1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD: releng/10.3/usr.sbin/bhyve/pci_emul.c 293408 2016-01-08 02:54:21Z araujo $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: releng/10.3/usr.sbin/bhyve/pci_emul.c 293408 2016-01-08 02:54:21Z araujo $"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34249321Sneel#include <sys/errno.h> 35221828Sgrehan 36221828Sgrehan#include <ctype.h> 37267393Sjhb#include <pthread.h> 38221828Sgrehan#include <stdio.h> 39221828Sgrehan#include <stdlib.h> 40221828Sgrehan#include <string.h> 41221828Sgrehan#include <strings.h> 42221828Sgrehan#include <assert.h> 43249321Sneel#include <stdbool.h> 44221828Sgrehan 45221828Sgrehan#include <machine/vmm.h> 46221828Sgrehan#include <vmmapi.h> 47221828Sgrehan 48261265Sjhb#include "acpi.h" 49244167Sgrehan#include "bhyverun.h" 50221828Sgrehan#include "inout.h" 51267393Sjhb#include "ioapic.h" 52241744Sgrehan#include "mem.h" 53221828Sgrehan#include "pci_emul.h" 54268972Sjhb#include "pci_irq.h" 55261265Sjhb#include "pci_lpc.h" 56221828Sgrehan 57221828Sgrehan#define CONF1_ADDR_PORT 0x0cf8 58221828Sgrehan#define CONF1_DATA_PORT 0x0cfc 59221828Sgrehan 60252331Sgrehan#define CONF1_ENABLE 0x80000000ul 61252331Sgrehan 62268887Sjhb#define MAXBUSES (PCI_BUSMAX + 1) 63239085Sneel#define MAXSLOTS (PCI_SLOTMAX + 1) 64239085Sneel#define MAXFUNCS (PCI_FUNCMAX + 1) 65221828Sgrehan 66267393Sjhbstruct funcinfo { 67267393Sjhb char *fi_name; 68267393Sjhb char *fi_param; 69267393Sjhb struct pci_devinst *fi_devi; 70267393Sjhb}; 71221828Sgrehan 72267393Sjhbstruct intxinfo { 73267393Sjhb int ii_count; 74268972Sjhb int ii_pirq_pin; 75267393Sjhb int ii_ioapic_irq; 76267393Sjhb}; 77267393Sjhb 78267393Sjhbstruct slotinfo { 79267393Sjhb struct intxinfo si_intpins[4]; 80267393Sjhb struct funcinfo si_funcs[MAXFUNCS]; 81268887Sjhb}; 82267393Sjhb 83268887Sjhbstruct businfo { 84268887Sjhb uint16_t iobase, iolimit; /* I/O window */ 85268887Sjhb uint32_t membase32, memlimit32; /* mmio window below 4GB */ 86268887Sjhb uint64_t membase64, memlimit64; /* mmio window above 4GB */ 87268887Sjhb struct slotinfo slotinfo[MAXSLOTS]; 88268887Sjhb}; 89268887Sjhb 90268887Sjhbstatic struct businfo *pci_businfo[MAXBUSES]; 91268887Sjhb 92221828SgrehanSET_DECLARE(pci_devemu_set, struct pci_devemu); 93221828Sgrehan 94221828Sgrehanstatic uint64_t pci_emul_iobase; 95221828Sgrehanstatic uint64_t pci_emul_membase32; 96221828Sgrehanstatic uint64_t pci_emul_membase64; 97221828Sgrehan 98221828Sgrehan#define PCI_EMUL_IOBASE 0x2000 99221828Sgrehan#define PCI_EMUL_IOLIMIT 0x10000 100221828Sgrehan 101270159Sgrehan#define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ 102270159Sgrehan#define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ 103270159SgrehanSYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); 104221828Sgrehan 105270159Sgrehan#define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE 106270159Sgrehan 107221828Sgrehan#define PCI_EMUL_MEMBASE64 0xD000000000UL 108221828Sgrehan#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL 109221828Sgrehan 110249916Sneelstatic struct pci_devemu *pci_emul_finddev(char *name); 111270159Sgrehanstatic void pci_lintr_route(struct pci_devinst *pi); 112270159Sgrehanstatic void pci_lintr_update(struct pci_devinst *pi); 113270159Sgrehanstatic void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, 114270159Sgrehan int func, int coff, int bytes, uint32_t *val); 115249916Sneel 116284899Sneelstatic __inline void 117284899SneelCFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) 118284899Sneel{ 119284899Sneel 120284899Sneel if (bytes == 1) 121284899Sneel pci_set_cfgdata8(pi, coff, val); 122284899Sneel else if (bytes == 2) 123284899Sneel pci_set_cfgdata16(pi, coff, val); 124284899Sneel else 125284899Sneel pci_set_cfgdata32(pi, coff, val); 126284899Sneel} 127284899Sneel 128284899Sneelstatic __inline uint32_t 129284899SneelCFGREAD(struct pci_devinst *pi, int coff, int bytes) 130284899Sneel{ 131284899Sneel 132284899Sneel if (bytes == 1) 133284899Sneel return (pci_get_cfgdata8(pi, coff)); 134284899Sneel else if (bytes == 2) 135284899Sneel return (pci_get_cfgdata16(pi, coff)); 136284899Sneel else 137284899Sneel return (pci_get_cfgdata32(pi, coff)); 138284899Sneel} 139284899Sneel 140221828Sgrehan/* 141221828Sgrehan * I/O access 142221828Sgrehan */ 143221828Sgrehan 144221828Sgrehan/* 145221828Sgrehan * Slot options are in the form: 146221828Sgrehan * 147268887Sjhb * <bus>:<slot>:<func>,<emul>[,<config>] 148239085Sneel * <slot>[:<func>],<emul>[,<config>] 149221828Sgrehan * 150221828Sgrehan * slot is 0..31 151239085Sneel * func is 0..7 152221828Sgrehan * emul is a string describing the type of PCI device e.g. virtio-net 153221828Sgrehan * config is an optional string, depending on the device, that can be 154221828Sgrehan * used for configuration. 155221828Sgrehan * Examples are: 156221828Sgrehan * 1,virtio-net,tap0 157239085Sneel * 3:0,dummy 158221828Sgrehan */ 159221828Sgrehanstatic void 160221828Sgrehanpci_parse_slot_usage(char *aopt) 161221828Sgrehan{ 162249916Sneel 163249916Sneel fprintf(stderr, "Invalid PCI slot info field \"%s\"\n", aopt); 164221828Sgrehan} 165221828Sgrehan 166249916Sneelint 167267341Sjhbpci_parse_slot(char *opt) 168221828Sgrehan{ 169268887Sjhb struct businfo *bi; 170268887Sjhb struct slotinfo *si; 171268887Sjhb char *emul, *config, *str, *cp; 172268887Sjhb int error, bnum, snum, fnum; 173221828Sgrehan 174249916Sneel error = -1; 175268887Sjhb str = strdup(opt); 176239085Sneel 177268887Sjhb emul = config = NULL; 178268887Sjhb if ((cp = strchr(str, ',')) != NULL) { 179268887Sjhb *cp = '\0'; 180268887Sjhb emul = cp + 1; 181268887Sjhb if ((cp = strchr(emul, ',')) != NULL) { 182268887Sjhb *cp = '\0'; 183268887Sjhb config = cp + 1; 184268887Sjhb } 185268887Sjhb } else { 186249916Sneel pci_parse_slot_usage(opt); 187249916Sneel goto done; 188221828Sgrehan } 189221828Sgrehan 190268887Sjhb /* <bus>:<slot>:<func> */ 191268887Sjhb if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { 192268887Sjhb bnum = 0; 193268887Sjhb /* <slot>:<func> */ 194268887Sjhb if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { 195268887Sjhb fnum = 0; 196268887Sjhb /* <slot> */ 197268887Sjhb if (sscanf(str, "%d", &snum) != 1) { 198268887Sjhb snum = -1; 199268887Sjhb } 200268887Sjhb } 201268887Sjhb } 202249916Sneel 203268887Sjhb if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || 204268887Sjhb fnum < 0 || fnum >= MAXFUNCS) { 205249916Sneel pci_parse_slot_usage(opt); 206249916Sneel goto done; 207221828Sgrehan } 208249916Sneel 209268887Sjhb if (pci_businfo[bnum] == NULL) 210268887Sjhb pci_businfo[bnum] = calloc(1, sizeof(struct businfo)); 211268887Sjhb 212268887Sjhb bi = pci_businfo[bnum]; 213268887Sjhb si = &bi->slotinfo[snum]; 214268887Sjhb 215268887Sjhb if (si->si_funcs[fnum].fi_name != NULL) { 216249916Sneel fprintf(stderr, "pci slot %d:%d already occupied!\n", 217249916Sneel snum, fnum); 218249916Sneel goto done; 219249916Sneel } 220249916Sneel 221249916Sneel if (pci_emul_finddev(emul) == NULL) { 222249916Sneel fprintf(stderr, "pci slot %d:%d: unknown device \"%s\"\n", 223249916Sneel snum, fnum, emul); 224249916Sneel goto done; 225249916Sneel } 226249916Sneel 227249916Sneel error = 0; 228268887Sjhb si->si_funcs[fnum].fi_name = emul; 229268887Sjhb si->si_funcs[fnum].fi_param = config; 230249916Sneel 231249916Sneeldone: 232249916Sneel if (error) 233268887Sjhb free(str); 234249916Sneel 235249916Sneel return (error); 236221828Sgrehan} 237221828Sgrehan 238221828Sgrehanstatic int 239246109Sneelpci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) 240246109Sneel{ 241246109Sneel 242246109Sneel if (offset < pi->pi_msix.pba_offset) 243246109Sneel return (0); 244246109Sneel 245246109Sneel if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 246246109Sneel return (0); 247246109Sneel } 248246109Sneel 249246109Sneel return (1); 250246109Sneel} 251246109Sneel 252246109Sneelint 253246109Sneelpci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, 254246109Sneel uint64_t value) 255246109Sneel{ 256246109Sneel int msix_entry_offset; 257246109Sneel int tab_index; 258246109Sneel char *dest; 259246109Sneel 260246109Sneel /* support only 4 or 8 byte writes */ 261246109Sneel if (size != 4 && size != 8) 262246109Sneel return (-1); 263246109Sneel 264246109Sneel /* 265246109Sneel * Return if table index is beyond what device supports 266246109Sneel */ 267246109Sneel tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 268246109Sneel if (tab_index >= pi->pi_msix.table_count) 269246109Sneel return (-1); 270246109Sneel 271246109Sneel msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 272246109Sneel 273246109Sneel /* support only aligned writes */ 274246109Sneel if ((msix_entry_offset % size) != 0) 275246109Sneel return (-1); 276246109Sneel 277246109Sneel dest = (char *)(pi->pi_msix.table + tab_index); 278246109Sneel dest += msix_entry_offset; 279246109Sneel 280246109Sneel if (size == 4) 281246109Sneel *((uint32_t *)dest) = value; 282246109Sneel else 283246109Sneel *((uint64_t *)dest) = value; 284246109Sneel 285246109Sneel return (0); 286246109Sneel} 287246109Sneel 288246109Sneeluint64_t 289246109Sneelpci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) 290246109Sneel{ 291246109Sneel char *dest; 292246109Sneel int msix_entry_offset; 293246109Sneel int tab_index; 294246109Sneel uint64_t retval = ~0; 295246109Sneel 296254965Sneel /* 297254965Sneel * The PCI standard only allows 4 and 8 byte accesses to the MSI-X 298293290Sbdrewery * table but we also allow 1 byte access to accommodate reads from 299254965Sneel * ddb. 300254965Sneel */ 301254965Sneel if (size != 1 && size != 4 && size != 8) 302246109Sneel return (retval); 303246109Sneel 304246109Sneel msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 305246109Sneel 306246109Sneel /* support only aligned reads */ 307246109Sneel if ((msix_entry_offset % size) != 0) { 308246109Sneel return (retval); 309246109Sneel } 310246109Sneel 311246109Sneel tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 312246109Sneel 313246109Sneel if (tab_index < pi->pi_msix.table_count) { 314246109Sneel /* valid MSI-X Table access */ 315246109Sneel dest = (char *)(pi->pi_msix.table + tab_index); 316246109Sneel dest += msix_entry_offset; 317246109Sneel 318254965Sneel if (size == 1) 319254965Sneel retval = *((uint8_t *)dest); 320254965Sneel else if (size == 4) 321246109Sneel retval = *((uint32_t *)dest); 322246109Sneel else 323246109Sneel retval = *((uint64_t *)dest); 324246109Sneel } else if (pci_valid_pba_offset(pi, offset)) { 325246109Sneel /* return 0 for PBA access */ 326246109Sneel retval = 0; 327246109Sneel } 328246109Sneel 329246109Sneel return (retval); 330246109Sneel} 331246109Sneel 332246190Sneelint 333246190Sneelpci_msix_table_bar(struct pci_devinst *pi) 334246190Sneel{ 335246190Sneel 336246190Sneel if (pi->pi_msix.table != NULL) 337246190Sneel return (pi->pi_msix.table_bar); 338246190Sneel else 339246190Sneel return (-1); 340246190Sneel} 341246190Sneel 342246190Sneelint 343246190Sneelpci_msix_pba_bar(struct pci_devinst *pi) 344246190Sneel{ 345246190Sneel 346246190Sneel if (pi->pi_msix.table != NULL) 347246190Sneel return (pi->pi_msix.pba_bar); 348246190Sneel else 349246190Sneel return (-1); 350246190Sneel} 351246190Sneel 352246109Sneelstatic int 353241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 354241744Sgrehan uint32_t *eax, void *arg) 355221828Sgrehan{ 356221828Sgrehan struct pci_devinst *pdi = arg; 357221828Sgrehan struct pci_devemu *pe = pdi->pi_d; 358241744Sgrehan uint64_t offset; 359241744Sgrehan int i; 360221828Sgrehan 361221828Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 362221828Sgrehan if (pdi->pi_bar[i].type == PCIBAR_IO && 363221828Sgrehan port >= pdi->pi_bar[i].addr && 364246109Sneel port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 365221828Sgrehan offset = port - pdi->pi_bar[i].addr; 366221828Sgrehan if (in) 367241744Sgrehan *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, 368241744Sgrehan offset, bytes); 369221828Sgrehan else 370241744Sgrehan (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, 371241744Sgrehan bytes, *eax); 372221828Sgrehan return (0); 373221828Sgrehan } 374221828Sgrehan } 375221828Sgrehan return (-1); 376221828Sgrehan} 377221828Sgrehan 378221828Sgrehanstatic int 379241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 380241744Sgrehan int size, uint64_t *val, void *arg1, long arg2) 381241744Sgrehan{ 382241744Sgrehan struct pci_devinst *pdi = arg1; 383241744Sgrehan struct pci_devemu *pe = pdi->pi_d; 384241744Sgrehan uint64_t offset; 385241744Sgrehan int bidx = (int) arg2; 386241744Sgrehan 387241744Sgrehan assert(bidx <= PCI_BARMAX); 388241744Sgrehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 389241744Sgrehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 390241744Sgrehan assert(addr >= pdi->pi_bar[bidx].addr && 391241744Sgrehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 392241744Sgrehan 393241744Sgrehan offset = addr - pdi->pi_bar[bidx].addr; 394241744Sgrehan 395268887Sjhb if (dir == MEM_F_WRITE) { 396268887Sjhb if (size == 8) { 397268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 398268887Sjhb 4, *val & 0xffffffff); 399268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 400268887Sjhb 4, *val >> 32); 401268887Sjhb } else { 402268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 403268887Sjhb size, *val); 404268887Sjhb } 405268887Sjhb } else { 406268887Sjhb if (size == 8) { 407268887Sjhb *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 408268887Sjhb offset, 4); 409268887Sjhb *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 410268887Sjhb offset + 4, 4) << 32; 411268887Sjhb } else { 412268887Sjhb *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 413268887Sjhb offset, size); 414268887Sjhb } 415268887Sjhb } 416241744Sgrehan 417241744Sgrehan return (0); 418241744Sgrehan} 419241744Sgrehan 420241744Sgrehan 421241744Sgrehanstatic int 422221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 423221828Sgrehan uint64_t *addr) 424221828Sgrehan{ 425221828Sgrehan uint64_t base; 426221828Sgrehan 427221828Sgrehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 428221828Sgrehan 429221828Sgrehan base = roundup2(*baseptr, size); 430221828Sgrehan 431221828Sgrehan if (base + size <= limit) { 432221828Sgrehan *addr = base; 433221828Sgrehan *baseptr = base + size; 434221828Sgrehan return (0); 435221828Sgrehan } else 436221828Sgrehan return (-1); 437221828Sgrehan} 438221828Sgrehan 439221828Sgrehanint 440241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 441241744Sgrehan uint64_t size) 442221828Sgrehan{ 443241744Sgrehan 444241744Sgrehan return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); 445241744Sgrehan} 446241744Sgrehan 447249321Sneel/* 448249321Sneel * Register (or unregister) the MMIO or I/O region associated with the BAR 449249321Sneel * register 'idx' of an emulated pci device. 450249321Sneel */ 451249321Sneelstatic void 452249321Sneelmodify_bar_registration(struct pci_devinst *pi, int idx, int registration) 453249321Sneel{ 454249321Sneel int error; 455249321Sneel struct inout_port iop; 456249321Sneel struct mem_range mr; 457249321Sneel 458249321Sneel switch (pi->pi_bar[idx].type) { 459249321Sneel case PCIBAR_IO: 460249321Sneel bzero(&iop, sizeof(struct inout_port)); 461249321Sneel iop.name = pi->pi_name; 462249321Sneel iop.port = pi->pi_bar[idx].addr; 463249321Sneel iop.size = pi->pi_bar[idx].size; 464249321Sneel if (registration) { 465249321Sneel iop.flags = IOPORT_F_INOUT; 466249321Sneel iop.handler = pci_emul_io_handler; 467249321Sneel iop.arg = pi; 468249321Sneel error = register_inout(&iop); 469249321Sneel } else 470249321Sneel error = unregister_inout(&iop); 471249321Sneel break; 472249321Sneel case PCIBAR_MEM32: 473249321Sneel case PCIBAR_MEM64: 474249321Sneel bzero(&mr, sizeof(struct mem_range)); 475249321Sneel mr.name = pi->pi_name; 476249321Sneel mr.base = pi->pi_bar[idx].addr; 477249321Sneel mr.size = pi->pi_bar[idx].size; 478249321Sneel if (registration) { 479249321Sneel mr.flags = MEM_F_RW; 480249321Sneel mr.handler = pci_emul_mem_handler; 481249321Sneel mr.arg1 = pi; 482249321Sneel mr.arg2 = idx; 483249321Sneel error = register_mem(&mr); 484249321Sneel } else 485249321Sneel error = unregister_mem(&mr); 486249321Sneel break; 487249321Sneel default: 488249321Sneel error = EINVAL; 489249321Sneel break; 490249321Sneel } 491249321Sneel assert(error == 0); 492249321Sneel} 493249321Sneel 494249321Sneelstatic void 495249321Sneelunregister_bar(struct pci_devinst *pi, int idx) 496249321Sneel{ 497249321Sneel 498249321Sneel modify_bar_registration(pi, idx, 0); 499249321Sneel} 500249321Sneel 501249321Sneelstatic void 502249321Sneelregister_bar(struct pci_devinst *pi, int idx) 503249321Sneel{ 504249321Sneel 505249321Sneel modify_bar_registration(pi, idx, 1); 506249321Sneel} 507249321Sneel 508249321Sneel/* Are we decoding i/o port accesses for the emulated pci device? */ 509249321Sneelstatic int 510249321Sneelporten(struct pci_devinst *pi) 511249321Sneel{ 512249321Sneel uint16_t cmd; 513249321Sneel 514249321Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 515249321Sneel 516249321Sneel return (cmd & PCIM_CMD_PORTEN); 517249321Sneel} 518249321Sneel 519249321Sneel/* Are we decoding memory accesses for the emulated pci device? */ 520249321Sneelstatic int 521249321Sneelmemen(struct pci_devinst *pi) 522249321Sneel{ 523249321Sneel uint16_t cmd; 524249321Sneel 525249321Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 526249321Sneel 527249321Sneel return (cmd & PCIM_CMD_MEMEN); 528249321Sneel} 529249321Sneel 530249321Sneel/* 531249321Sneel * Update the MMIO or I/O address that is decoded by the BAR register. 532249321Sneel * 533249321Sneel * If the pci device has enabled the address space decoding then intercept 534249321Sneel * the address range decoded by the BAR register. 535249321Sneel */ 536249321Sneelstatic void 537249321Sneelupdate_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) 538249321Sneel{ 539249321Sneel int decode; 540249321Sneel 541249321Sneel if (pi->pi_bar[idx].type == PCIBAR_IO) 542249321Sneel decode = porten(pi); 543249321Sneel else 544249321Sneel decode = memen(pi); 545249321Sneel 546249321Sneel if (decode) 547249321Sneel unregister_bar(pi, idx); 548249321Sneel 549249321Sneel switch (type) { 550249321Sneel case PCIBAR_IO: 551249321Sneel case PCIBAR_MEM32: 552249321Sneel pi->pi_bar[idx].addr = addr; 553249321Sneel break; 554249321Sneel case PCIBAR_MEM64: 555249321Sneel pi->pi_bar[idx].addr &= ~0xffffffffUL; 556249321Sneel pi->pi_bar[idx].addr |= addr; 557249321Sneel break; 558249321Sneel case PCIBAR_MEMHI64: 559249321Sneel pi->pi_bar[idx].addr &= 0xffffffff; 560249321Sneel pi->pi_bar[idx].addr |= addr; 561249321Sneel break; 562249321Sneel default: 563249321Sneel assert(0); 564249321Sneel } 565249321Sneel 566249321Sneel if (decode) 567249321Sneel register_bar(pi, idx); 568249321Sneel} 569249321Sneel 570241744Sgrehanint 571241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, 572241744Sgrehan enum pcibar_type type, uint64_t size) 573241744Sgrehan{ 574249321Sneel int error; 575221828Sgrehan uint64_t *baseptr, limit, addr, mask, lobits, bar; 576221828Sgrehan 577221828Sgrehan assert(idx >= 0 && idx <= PCI_BARMAX); 578221828Sgrehan 579221828Sgrehan if ((size & (size - 1)) != 0) 580221828Sgrehan size = 1UL << flsl(size); /* round up to a power of 2 */ 581221828Sgrehan 582249321Sneel /* Enforce minimum BAR sizes required by the PCI standard */ 583249321Sneel if (type == PCIBAR_IO) { 584249321Sneel if (size < 4) 585249321Sneel size = 4; 586249321Sneel } else { 587249321Sneel if (size < 16) 588249321Sneel size = 16; 589249321Sneel } 590249321Sneel 591221828Sgrehan switch (type) { 592221828Sgrehan case PCIBAR_NONE: 593221828Sgrehan baseptr = NULL; 594221828Sgrehan addr = mask = lobits = 0; 595221828Sgrehan break; 596221828Sgrehan case PCIBAR_IO: 597267341Sjhb baseptr = &pci_emul_iobase; 598221828Sgrehan limit = PCI_EMUL_IOLIMIT; 599221828Sgrehan mask = PCIM_BAR_IO_BASE; 600221828Sgrehan lobits = PCIM_BAR_IO_SPACE; 601221828Sgrehan break; 602221828Sgrehan case PCIBAR_MEM64: 603221828Sgrehan /* 604221828Sgrehan * XXX 605221828Sgrehan * Some drivers do not work well if the 64-bit BAR is allocated 606221828Sgrehan * above 4GB. Allow for this by allocating small requests under 607221828Sgrehan * 4GB unless then allocation size is larger than some arbitrary 608221828Sgrehan * number (32MB currently). 609221828Sgrehan */ 610221828Sgrehan if (size > 32 * 1024 * 1024) { 611221828Sgrehan /* 612221828Sgrehan * XXX special case for device requiring peer-peer DMA 613221828Sgrehan */ 614221828Sgrehan if (size == 0x100000000UL) 615221828Sgrehan baseptr = &hostbase; 616221828Sgrehan else 617221828Sgrehan baseptr = &pci_emul_membase64; 618221828Sgrehan limit = PCI_EMUL_MEMLIMIT64; 619221828Sgrehan mask = PCIM_BAR_MEM_BASE; 620221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 621221828Sgrehan PCIM_BAR_MEM_PREFETCH; 622221828Sgrehan break; 623239086Sneel } else { 624239086Sneel baseptr = &pci_emul_membase32; 625239086Sneel limit = PCI_EMUL_MEMLIMIT32; 626239086Sneel mask = PCIM_BAR_MEM_BASE; 627239086Sneel lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 628221828Sgrehan } 629239086Sneel break; 630221828Sgrehan case PCIBAR_MEM32: 631221828Sgrehan baseptr = &pci_emul_membase32; 632221828Sgrehan limit = PCI_EMUL_MEMLIMIT32; 633221828Sgrehan mask = PCIM_BAR_MEM_BASE; 634221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 635221828Sgrehan break; 636221828Sgrehan default: 637221828Sgrehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 638221828Sgrehan assert(0); 639221828Sgrehan } 640221828Sgrehan 641221828Sgrehan if (baseptr != NULL) { 642221828Sgrehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 643221828Sgrehan if (error != 0) 644221828Sgrehan return (error); 645221828Sgrehan } 646221828Sgrehan 647221828Sgrehan pdi->pi_bar[idx].type = type; 648221828Sgrehan pdi->pi_bar[idx].addr = addr; 649221828Sgrehan pdi->pi_bar[idx].size = size; 650221828Sgrehan 651221828Sgrehan /* Initialize the BAR register in config space */ 652221828Sgrehan bar = (addr & mask) | lobits; 653221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 654221828Sgrehan 655221828Sgrehan if (type == PCIBAR_MEM64) { 656221828Sgrehan assert(idx + 1 <= PCI_BARMAX); 657221828Sgrehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 658221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 659221828Sgrehan } 660221828Sgrehan 661249321Sneel register_bar(pdi, idx); 662221828Sgrehan 663221828Sgrehan return (0); 664221828Sgrehan} 665221828Sgrehan 666221828Sgrehan#define CAP_START_OFFSET 0x40 667221828Sgrehanstatic int 668221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 669221828Sgrehan{ 670268887Sjhb int i, capoff, reallen; 671221828Sgrehan uint16_t sts; 672221828Sgrehan 673268887Sjhb assert(caplen > 0); 674221828Sgrehan 675221828Sgrehan reallen = roundup2(caplen, 4); /* dword aligned */ 676221828Sgrehan 677221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 678268887Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) 679221828Sgrehan capoff = CAP_START_OFFSET; 680268887Sjhb else 681268887Sjhb capoff = pi->pi_capend + 1; 682221828Sgrehan 683221828Sgrehan /* Check if we have enough space */ 684268887Sjhb if (capoff + reallen > PCI_REGMAX + 1) 685221828Sgrehan return (-1); 686221828Sgrehan 687268887Sjhb /* Set the previous capability pointer */ 688268887Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 689268887Sjhb pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 690268887Sjhb pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 691268887Sjhb } else 692268887Sjhb pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); 693268887Sjhb 694221828Sgrehan /* Copy the capability */ 695221828Sgrehan for (i = 0; i < caplen; i++) 696221828Sgrehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 697221828Sgrehan 698221828Sgrehan /* Set the next capability pointer */ 699268887Sjhb pci_set_cfgdata8(pi, capoff + 1, 0); 700221828Sgrehan 701268887Sjhb pi->pi_prevcap = capoff; 702268887Sjhb pi->pi_capend = capoff + reallen - 1; 703221828Sgrehan return (0); 704221828Sgrehan} 705221828Sgrehan 706221828Sgrehanstatic struct pci_devemu * 707221828Sgrehanpci_emul_finddev(char *name) 708221828Sgrehan{ 709221828Sgrehan struct pci_devemu **pdpp, *pdp; 710221828Sgrehan 711221828Sgrehan SET_FOREACH(pdpp, pci_devemu_set) { 712221828Sgrehan pdp = *pdpp; 713221828Sgrehan if (!strcmp(pdp->pe_emu, name)) { 714221828Sgrehan return (pdp); 715221828Sgrehan } 716221828Sgrehan } 717221828Sgrehan 718221828Sgrehan return (NULL); 719221828Sgrehan} 720221828Sgrehan 721252682Sgrehanstatic int 722268887Sjhbpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, 723268887Sjhb int func, struct funcinfo *fi) 724221828Sgrehan{ 725221828Sgrehan struct pci_devinst *pdi; 726252682Sgrehan int err; 727252682Sgrehan 728268953Sjhb pdi = calloc(1, sizeof(struct pci_devinst)); 729221828Sgrehan 730221828Sgrehan pdi->pi_vmctx = ctx; 731268887Sjhb pdi->pi_bus = bus; 732221828Sgrehan pdi->pi_slot = slot; 733239085Sneel pdi->pi_func = func; 734267393Sjhb pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 735267393Sjhb pdi->pi_lintr.pin = 0; 736267393Sjhb pdi->pi_lintr.state = IDLE; 737268972Sjhb pdi->pi_lintr.pirq_pin = 0; 738267393Sjhb pdi->pi_lintr.ioapic_irq = 0; 739221828Sgrehan pdi->pi_d = pde; 740221828Sgrehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 741221828Sgrehan 742221828Sgrehan /* Disable legacy interrupts */ 743221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 744221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 745221828Sgrehan 746221828Sgrehan pci_set_cfgdata8(pdi, PCIR_COMMAND, 747221828Sgrehan PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 748221828Sgrehan 749268887Sjhb err = (*pde->pe_init)(ctx, pdi, fi->fi_param); 750268887Sjhb if (err == 0) 751268887Sjhb fi->fi_devi = pdi; 752268887Sjhb else 753221828Sgrehan free(pdi); 754252682Sgrehan 755252682Sgrehan return (err); 756221828Sgrehan} 757221828Sgrehan 758221828Sgrehanvoid 759221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 760221828Sgrehan{ 761221828Sgrehan int mmc; 762221828Sgrehan 763221828Sgrehan CTASSERT(sizeof(struct msicap) == 14); 764221828Sgrehan 765221828Sgrehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 766221828Sgrehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 767221828Sgrehan mmc = ffs(msgnum) - 1; 768221828Sgrehan 769221828Sgrehan bzero(msicap, sizeof(struct msicap)); 770221828Sgrehan msicap->capid = PCIY_MSI; 771221828Sgrehan msicap->nextptr = nextptr; 772221828Sgrehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 773221828Sgrehan} 774221828Sgrehan 775221828Sgrehanint 776221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 777221828Sgrehan{ 778221828Sgrehan struct msicap msicap; 779221828Sgrehan 780221828Sgrehan pci_populate_msicap(&msicap, msgnum, 0); 781221828Sgrehan 782221828Sgrehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 783221828Sgrehan} 784221828Sgrehan 785246109Sneelstatic void 786246109Sneelpci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 787268887Sjhb uint32_t msix_tab_size) 788246109Sneel{ 789246109Sneel CTASSERT(sizeof(struct msixcap) == 12); 790246109Sneel 791246109Sneel assert(msix_tab_size % 4096 == 0); 792246109Sneel 793246109Sneel bzero(msixcap, sizeof(struct msixcap)); 794246109Sneel msixcap->capid = PCIY_MSIX; 795246109Sneel 796246109Sneel /* 797246109Sneel * Message Control Register, all fields set to 798246109Sneel * zero except for the Table Size. 799246109Sneel * Note: Table size N is encoded as N-1 800246109Sneel */ 801246109Sneel msixcap->msgctrl = msgnum - 1; 802246109Sneel 803246109Sneel /* 804246109Sneel * MSI-X BAR setup: 805246109Sneel * - MSI-X table start at offset 0 806246109Sneel * - PBA table starts at a 4K aligned offset after the MSI-X table 807246109Sneel */ 808246109Sneel msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 809246109Sneel msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 810246109Sneel} 811246109Sneel 812246109Sneelstatic void 813246109Sneelpci_msix_table_init(struct pci_devinst *pi, int table_entries) 814246109Sneel{ 815246109Sneel int i, table_size; 816246109Sneel 817246109Sneel assert(table_entries > 0); 818246109Sneel assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 819246109Sneel 820246109Sneel table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 821268953Sjhb pi->pi_msix.table = calloc(1, table_size); 822246109Sneel 823246109Sneel /* set mask bit of vector control register */ 824246109Sneel for (i = 0; i < table_entries; i++) 825246109Sneel pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 826246109Sneel} 827246109Sneel 828246109Sneelint 829246109Sneelpci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 830246109Sneel{ 831246109Sneel uint32_t tab_size; 832246109Sneel struct msixcap msixcap; 833246109Sneel 834246109Sneel assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 835246109Sneel assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 836246109Sneel 837246109Sneel tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 838246109Sneel 839246109Sneel /* Align table size to nearest 4K */ 840246109Sneel tab_size = roundup2(tab_size, 4096); 841246109Sneel 842246109Sneel pi->pi_msix.table_bar = barnum; 843246109Sneel pi->pi_msix.pba_bar = barnum; 844246109Sneel pi->pi_msix.table_offset = 0; 845246109Sneel pi->pi_msix.table_count = msgnum; 846246109Sneel pi->pi_msix.pba_offset = tab_size; 847268887Sjhb pi->pi_msix.pba_size = PBA_SIZE(msgnum); 848246109Sneel 849246109Sneel pci_msix_table_init(pi, msgnum); 850246109Sneel 851268887Sjhb pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); 852246109Sneel 853246109Sneel /* allocate memory for MSI-X Table and PBA */ 854246109Sneel pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 855246109Sneel tab_size + pi->pi_msix.pba_size); 856246109Sneel 857246109Sneel return (pci_emul_add_capability(pi, (u_char *)&msixcap, 858246109Sneel sizeof(msixcap))); 859246109Sneel} 860246109Sneel 861221828Sgrehanvoid 862234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 863234761Sgrehan int bytes, uint32_t val) 864234761Sgrehan{ 865234761Sgrehan uint16_t msgctrl, rwmask; 866293408Saraujo int off; 867246109Sneel 868234761Sgrehan off = offset - capoff; 869234761Sgrehan /* Message Control Register */ 870234761Sgrehan if (off == 2 && bytes == 2) { 871234761Sgrehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 872234761Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 873234761Sgrehan msgctrl &= ~rwmask; 874234761Sgrehan msgctrl |= val & rwmask; 875234761Sgrehan val = msgctrl; 876234761Sgrehan 877234761Sgrehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 878246109Sneel pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 879267393Sjhb pci_lintr_update(pi); 880234761Sgrehan } 881234761Sgrehan 882234761Sgrehan CFGWRITE(pi, offset, val, bytes); 883234761Sgrehan} 884234761Sgrehan 885234761Sgrehanvoid 886221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 887221828Sgrehan int bytes, uint32_t val) 888221828Sgrehan{ 889221828Sgrehan uint16_t msgctrl, rwmask, msgdata, mme; 890221828Sgrehan uint32_t addrlo; 891221828Sgrehan 892221828Sgrehan /* 893221828Sgrehan * If guest is writing to the message control register make sure 894221828Sgrehan * we do not overwrite read-only fields. 895221828Sgrehan */ 896221828Sgrehan if ((offset - capoff) == 2 && bytes == 2) { 897221828Sgrehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 898221828Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 899221828Sgrehan msgctrl &= ~rwmask; 900221828Sgrehan msgctrl |= val & rwmask; 901221828Sgrehan val = msgctrl; 902221828Sgrehan 903221828Sgrehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 904221828Sgrehan if (msgctrl & PCIM_MSICTRL_64BIT) 905221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 906221828Sgrehan else 907221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 908221828Sgrehan 909221828Sgrehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 910221828Sgrehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 911221828Sgrehan if (pi->pi_msi.enabled) { 912262350Sjhb pi->pi_msi.addr = addrlo; 913262350Sjhb pi->pi_msi.msg_data = msgdata; 914262350Sjhb pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 915221828Sgrehan } else { 916262350Sjhb pi->pi_msi.maxmsgnum = 0; 917221828Sgrehan } 918267393Sjhb pci_lintr_update(pi); 919221828Sgrehan } 920221828Sgrehan 921221828Sgrehan CFGWRITE(pi, offset, val, bytes); 922221828Sgrehan} 923221828Sgrehan 924246846Sneelvoid 925246846Sneelpciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 926246846Sneel int bytes, uint32_t val) 927246846Sneel{ 928246846Sneel 929246846Sneel /* XXX don't write to the readonly parts */ 930246846Sneel CFGWRITE(pi, offset, val, bytes); 931246846Sneel} 932246846Sneel 933246846Sneel#define PCIECAP_VERSION 0x2 934246846Sneelint 935246846Sneelpci_emul_add_pciecap(struct pci_devinst *pi, int type) 936246846Sneel{ 937246846Sneel int err; 938246846Sneel struct pciecap pciecap; 939246846Sneel 940246846Sneel CTASSERT(sizeof(struct pciecap) == 60); 941246846Sneel 942246846Sneel if (type != PCIEM_TYPE_ROOT_PORT) 943246846Sneel return (-1); 944246846Sneel 945246846Sneel bzero(&pciecap, sizeof(pciecap)); 946246846Sneel 947246846Sneel pciecap.capid = PCIY_EXPRESS; 948246846Sneel pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT; 949246846Sneel pciecap.link_capabilities = 0x411; /* gen1, x1 */ 950246846Sneel pciecap.link_status = 0x11; /* gen1, x1 */ 951246846Sneel 952246846Sneel err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 953246846Sneel return (err); 954246846Sneel} 955246846Sneel 956221828Sgrehan/* 957221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the 958221828Sgrehan * config space. 959221828Sgrehan */ 960221828Sgrehanstatic void 961221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) 962221828Sgrehan{ 963221828Sgrehan int capid; 964221828Sgrehan uint8_t capoff, nextoff; 965221828Sgrehan 966221828Sgrehan /* Do not allow un-aligned writes */ 967221828Sgrehan if ((offset & (bytes - 1)) != 0) 968221828Sgrehan return; 969221828Sgrehan 970221828Sgrehan /* Find the capability that we want to update */ 971221828Sgrehan capoff = CAP_START_OFFSET; 972221828Sgrehan while (1) { 973268887Sjhb nextoff = pci_get_cfgdata8(pi, capoff + 1); 974268887Sjhb if (nextoff == 0) 975221828Sgrehan break; 976221828Sgrehan if (offset >= capoff && offset < nextoff) 977221828Sgrehan break; 978221828Sgrehan 979221828Sgrehan capoff = nextoff; 980221828Sgrehan } 981221828Sgrehan assert(offset >= capoff); 982221828Sgrehan 983221828Sgrehan /* 984256248Sgrehan * Capability ID and Next Capability Pointer are readonly. 985256248Sgrehan * However, some o/s's do 4-byte writes that include these. 986256248Sgrehan * For this case, trim the write back to 2 bytes and adjust 987256248Sgrehan * the data. 988221828Sgrehan */ 989256248Sgrehan if (offset == capoff || offset == capoff + 1) { 990256248Sgrehan if (offset == capoff && bytes == 4) { 991256248Sgrehan bytes = 2; 992256248Sgrehan offset += 2; 993256248Sgrehan val >>= 16; 994256248Sgrehan } else 995256248Sgrehan return; 996256248Sgrehan } 997221828Sgrehan 998268887Sjhb capid = pci_get_cfgdata8(pi, capoff); 999221828Sgrehan switch (capid) { 1000221828Sgrehan case PCIY_MSI: 1001221828Sgrehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 1002221828Sgrehan break; 1003246109Sneel case PCIY_MSIX: 1004246109Sneel msixcap_cfgwrite(pi, capoff, offset, bytes, val); 1005246109Sneel break; 1006246846Sneel case PCIY_EXPRESS: 1007246846Sneel pciecap_cfgwrite(pi, capoff, offset, bytes, val); 1008246846Sneel break; 1009221828Sgrehan default: 1010221828Sgrehan break; 1011221828Sgrehan } 1012221828Sgrehan} 1013221828Sgrehan 1014221828Sgrehanstatic int 1015221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset) 1016221828Sgrehan{ 1017221828Sgrehan uint16_t sts; 1018221828Sgrehan 1019221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 1020221828Sgrehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 1021268887Sjhb if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1022268887Sjhb return (1); 1023221828Sgrehan } 1024268887Sjhb return (0); 1025221828Sgrehan} 1026221828Sgrehan 1027247144Sgrehanstatic int 1028247144Sgrehanpci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1029247144Sgrehan int size, uint64_t *val, void *arg1, long arg2) 1030247144Sgrehan{ 1031247144Sgrehan /* 1032247144Sgrehan * Ignore writes; return 0xff's for reads. The mem read code 1033247144Sgrehan * will take care of truncating to the correct size. 1034247144Sgrehan */ 1035247144Sgrehan if (dir == MEM_F_READ) { 1036247144Sgrehan *val = 0xffffffffffffffff; 1037247144Sgrehan } 1038247144Sgrehan 1039247144Sgrehan return (0); 1040247144Sgrehan} 1041247144Sgrehan 1042270159Sgrehanstatic int 1043270159Sgrehanpci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1044270159Sgrehan int bytes, uint64_t *val, void *arg1, long arg2) 1045270159Sgrehan{ 1046270159Sgrehan int bus, slot, func, coff, in; 1047270159Sgrehan 1048270159Sgrehan coff = addr & 0xfff; 1049270159Sgrehan func = (addr >> 12) & 0x7; 1050270159Sgrehan slot = (addr >> 15) & 0x1f; 1051270159Sgrehan bus = (addr >> 20) & 0xff; 1052270159Sgrehan in = (dir == MEM_F_READ); 1053270159Sgrehan if (in) 1054270159Sgrehan *val = ~0UL; 1055270159Sgrehan pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); 1056270159Sgrehan return (0); 1057270159Sgrehan} 1058270159Sgrehan 1059270159Sgrehanuint64_t 1060270159Sgrehanpci_ecfg_base(void) 1061270159Sgrehan{ 1062270159Sgrehan 1063270159Sgrehan return (PCI_EMUL_ECFG_BASE); 1064270159Sgrehan} 1065270159Sgrehan 1066268887Sjhb#define BUSIO_ROUNDUP 32 1067268887Sjhb#define BUSMEM_ROUNDUP (1024 * 1024) 1068268887Sjhb 1069252682Sgrehanint 1070221828Sgrehaninit_pci(struct vmctx *ctx) 1071221828Sgrehan{ 1072270159Sgrehan struct mem_range mr; 1073221828Sgrehan struct pci_devemu *pde; 1074268887Sjhb struct businfo *bi; 1075268887Sjhb struct slotinfo *si; 1076267393Sjhb struct funcinfo *fi; 1077249572Sneel size_t lowmem; 1078268887Sjhb int bus, slot, func; 1079247144Sgrehan int error; 1080221828Sgrehan 1081221828Sgrehan pci_emul_iobase = PCI_EMUL_IOBASE; 1082249572Sneel pci_emul_membase32 = vm_get_lowmem_limit(ctx); 1083221828Sgrehan pci_emul_membase64 = PCI_EMUL_MEMBASE64; 1084221828Sgrehan 1085268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1086268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1087268887Sjhb continue; 1088268887Sjhb /* 1089268887Sjhb * Keep track of the i/o and memory resources allocated to 1090268887Sjhb * this bus. 1091268887Sjhb */ 1092268887Sjhb bi->iobase = pci_emul_iobase; 1093268887Sjhb bi->membase32 = pci_emul_membase32; 1094268887Sjhb bi->membase64 = pci_emul_membase64; 1095268887Sjhb 1096268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1097268887Sjhb si = &bi->slotinfo[slot]; 1098268887Sjhb for (func = 0; func < MAXFUNCS; func++) { 1099268887Sjhb fi = &si->si_funcs[func]; 1100268887Sjhb if (fi->fi_name == NULL) 1101268887Sjhb continue; 1102267393Sjhb pde = pci_emul_finddev(fi->fi_name); 1103249916Sneel assert(pde != NULL); 1104268887Sjhb error = pci_emul_init(ctx, pde, bus, slot, 1105268887Sjhb func, fi); 1106252682Sgrehan if (error) 1107252682Sgrehan return (error); 1108221828Sgrehan } 1109221828Sgrehan } 1110268887Sjhb 1111268887Sjhb /* 1112268887Sjhb * Add some slop to the I/O and memory resources decoded by 1113268887Sjhb * this bus to give a guest some flexibility if it wants to 1114268887Sjhb * reprogram the BARs. 1115268887Sjhb */ 1116268887Sjhb pci_emul_iobase += BUSIO_ROUNDUP; 1117268887Sjhb pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1118268887Sjhb bi->iolimit = pci_emul_iobase; 1119268887Sjhb 1120268887Sjhb pci_emul_membase32 += BUSMEM_ROUNDUP; 1121268887Sjhb pci_emul_membase32 = roundup2(pci_emul_membase32, 1122268887Sjhb BUSMEM_ROUNDUP); 1123268887Sjhb bi->memlimit32 = pci_emul_membase32; 1124268887Sjhb 1125268887Sjhb pci_emul_membase64 += BUSMEM_ROUNDUP; 1126268887Sjhb pci_emul_membase64 = roundup2(pci_emul_membase64, 1127268887Sjhb BUSMEM_ROUNDUP); 1128268887Sjhb bi->memlimit64 = pci_emul_membase64; 1129221828Sgrehan } 1130234938Sgrehan 1131234938Sgrehan /* 1132268972Sjhb * PCI backends are initialized before routing INTx interrupts 1133268972Sjhb * so that LPC devices are able to reserve ISA IRQs before 1134268972Sjhb * routing PIRQ pins. 1135268972Sjhb */ 1136268972Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1137268972Sjhb if ((bi = pci_businfo[bus]) == NULL) 1138268972Sjhb continue; 1139268972Sjhb 1140268972Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1141268972Sjhb si = &bi->slotinfo[slot]; 1142268972Sjhb for (func = 0; func < MAXFUNCS; func++) { 1143268972Sjhb fi = &si->si_funcs[func]; 1144268972Sjhb if (fi->fi_devi == NULL) 1145268972Sjhb continue; 1146268972Sjhb pci_lintr_route(fi->fi_devi); 1147268972Sjhb } 1148268972Sjhb } 1149268972Sjhb } 1150268972Sjhb lpc_pirq_routed(); 1151268972Sjhb 1152268972Sjhb /* 1153249572Sneel * The guest physical memory map looks like the following: 1154249572Sneel * [0, lowmem) guest system memory 1155249572Sneel * [lowmem, lowmem_limit) memory hole (may be absent) 1156270159Sgrehan * [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) 1157270159Sgrehan * [0xE0000000, 0xF0000000) PCI extended config window 1158270159Sgrehan * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware 1159249572Sneel * [4GB, 4GB + highmem) 1160270159Sgrehan */ 1161270159Sgrehan 1162270159Sgrehan /* 1163249572Sneel * Accesses to memory addresses that are not allocated to system 1164249572Sneel * memory or PCI devices return 0xff's. 1165247144Sgrehan */ 1166270074Sgrehan lowmem = vm_get_lowmem_size(ctx); 1167270159Sgrehan bzero(&mr, sizeof(struct mem_range)); 1168270159Sgrehan mr.name = "PCI hole"; 1169270159Sgrehan mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1170270159Sgrehan mr.base = lowmem; 1171270159Sgrehan mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 1172270159Sgrehan mr.handler = pci_emul_fallback_handler; 1173270159Sgrehan error = register_mem_fallback(&mr); 1174270159Sgrehan assert(error == 0); 1175249572Sneel 1176270159Sgrehan /* PCI extended config space */ 1177270159Sgrehan bzero(&mr, sizeof(struct mem_range)); 1178270159Sgrehan mr.name = "PCI ECFG"; 1179270159Sgrehan mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1180270159Sgrehan mr.base = PCI_EMUL_ECFG_BASE; 1181270159Sgrehan mr.size = PCI_EMUL_ECFG_SIZE; 1182270159Sgrehan mr.handler = pci_emul_ecfg_handler; 1183270159Sgrehan error = register_mem(&mr); 1184247144Sgrehan assert(error == 0); 1185252682Sgrehan 1186252682Sgrehan return (0); 1187221828Sgrehan} 1188221828Sgrehan 1189267393Sjhbstatic void 1190268972Sjhbpci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1191268972Sjhb void *arg) 1192267393Sjhb{ 1193267393Sjhb 1194268972Sjhb dsdt_line(" Package ()"); 1195267393Sjhb dsdt_line(" {"); 1196267393Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1197267393Sjhb dsdt_line(" 0x%02X,", pin - 1); 1198267393Sjhb dsdt_line(" Zero,"); 1199267393Sjhb dsdt_line(" 0x%X", ioapic_irq); 1200268972Sjhb dsdt_line(" },"); 1201267393Sjhb} 1202267393Sjhb 1203268972Sjhbstatic void 1204268972Sjhbpci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1205268972Sjhb void *arg) 1206268972Sjhb{ 1207268972Sjhb char *name; 1208268972Sjhb 1209268972Sjhb name = lpc_pirq_name(pirq_pin); 1210268972Sjhb if (name == NULL) 1211268972Sjhb return; 1212268972Sjhb dsdt_line(" Package ()"); 1213268972Sjhb dsdt_line(" {"); 1214268972Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1215268972Sjhb dsdt_line(" 0x%02X,", pin - 1); 1216268972Sjhb dsdt_line(" %s,", name); 1217268972Sjhb dsdt_line(" 0x00"); 1218268972Sjhb dsdt_line(" },"); 1219268972Sjhb free(name); 1220268972Sjhb} 1221268972Sjhb 1222268887Sjhb/* 1223268887Sjhb * A bhyve virtual machine has a flat PCI hierarchy with a root port 1224268887Sjhb * corresponding to each PCI bus. 1225268887Sjhb */ 1226268887Sjhbstatic void 1227268887Sjhbpci_bus_write_dsdt(int bus) 1228261265Sjhb{ 1229268887Sjhb struct businfo *bi; 1230268887Sjhb struct slotinfo *si; 1231261265Sjhb struct pci_devinst *pi; 1232268972Sjhb int count, func, slot; 1233261265Sjhb 1234268887Sjhb /* 1235268887Sjhb * If there are no devices on this 'bus' then just return. 1236268887Sjhb */ 1237268887Sjhb if ((bi = pci_businfo[bus]) == NULL) { 1238268887Sjhb /* 1239268887Sjhb * Bus 0 is special because it decodes the I/O ports used 1240268887Sjhb * for PCI config space access even if there are no devices 1241268887Sjhb * on it. 1242268887Sjhb */ 1243268887Sjhb if (bus != 0) 1244268887Sjhb return; 1245268887Sjhb } 1246268887Sjhb 1247268887Sjhb dsdt_line(" Device (PC%02X)", bus); 1248261265Sjhb dsdt_line(" {"); 1249261265Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1250261265Sjhb dsdt_line(" Name (_ADR, Zero)"); 1251268887Sjhb 1252268887Sjhb dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1253268887Sjhb dsdt_line(" {"); 1254268887Sjhb dsdt_line(" Return (0x%08X)", bus); 1255268887Sjhb dsdt_line(" }"); 1256261265Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1257261265Sjhb dsdt_line(" {"); 1258261265Sjhb dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1259261265Sjhb "MaxFixed, PosDecode,"); 1260261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1261268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bus); 1262268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", bus); 1263261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1264268887Sjhb dsdt_line(" 0x0001, // Length"); 1265261265Sjhb dsdt_line(" ,, )"); 1266268887Sjhb 1267268887Sjhb if (bus == 0) { 1268268887Sjhb dsdt_indent(3); 1269268887Sjhb dsdt_fixed_ioport(0xCF8, 8); 1270268887Sjhb dsdt_unindent(3); 1271268887Sjhb 1272268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1273268887Sjhb "PosDecode, EntireRange,"); 1274268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1275268887Sjhb dsdt_line(" 0x0000, // Range Minimum"); 1276268887Sjhb dsdt_line(" 0x0CF7, // Range Maximum"); 1277268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1278268887Sjhb dsdt_line(" 0x0CF8, // Length"); 1279268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1280268887Sjhb 1281268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1282268887Sjhb "PosDecode, EntireRange,"); 1283268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1284268887Sjhb dsdt_line(" 0x0D00, // Range Minimum"); 1285268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1286268887Sjhb PCI_EMUL_IOBASE - 1); 1287268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1288268887Sjhb dsdt_line(" 0x%04X, // Length", 1289268887Sjhb PCI_EMUL_IOBASE - 0x0D00); 1290268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1291268887Sjhb 1292268887Sjhb if (bi == NULL) { 1293268887Sjhb dsdt_line(" })"); 1294268887Sjhb goto done; 1295268887Sjhb } 1296268887Sjhb } 1297268887Sjhb assert(bi != NULL); 1298268887Sjhb 1299268887Sjhb /* i/o window */ 1300261265Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1301261265Sjhb "PosDecode, EntireRange,"); 1302261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1303268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1304268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1305268887Sjhb bi->iolimit - 1); 1306261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1307268887Sjhb dsdt_line(" 0x%04X, // Length", 1308268887Sjhb bi->iolimit - bi->iobase); 1309261265Sjhb dsdt_line(" ,, , TypeStatic)"); 1310268887Sjhb 1311268887Sjhb /* mmio window (32-bit) */ 1312261265Sjhb dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1313261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1314261265Sjhb dsdt_line(" 0x00000000, // Granularity"); 1315268887Sjhb dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1316261265Sjhb dsdt_line(" 0x%08X, // Range Maximum\n", 1317268887Sjhb bi->memlimit32 - 1); 1318261265Sjhb dsdt_line(" 0x00000000, // Translation Offset"); 1319268887Sjhb dsdt_line(" 0x%08X, // Length\n", 1320268887Sjhb bi->memlimit32 - bi->membase32); 1321261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1322268887Sjhb 1323268887Sjhb /* mmio window (64-bit) */ 1324261265Sjhb dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1325261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1326261265Sjhb dsdt_line(" 0x0000000000000000, // Granularity"); 1327268887Sjhb dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1328261265Sjhb dsdt_line(" 0x%016lX, // Range Maximum\n", 1329268887Sjhb bi->memlimit64 - 1); 1330261265Sjhb dsdt_line(" 0x0000000000000000, // Translation Offset"); 1331261265Sjhb dsdt_line(" 0x%016lX, // Length\n", 1332268887Sjhb bi->memlimit64 - bi->membase64); 1333261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1334261265Sjhb dsdt_line(" })"); 1335268887Sjhb 1336268887Sjhb count = pci_count_lintr(bus); 1337267393Sjhb if (count != 0) { 1338267393Sjhb dsdt_indent(2); 1339268972Sjhb dsdt_line("Name (PPRT, Package ()"); 1340267393Sjhb dsdt_line("{"); 1341268972Sjhb pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 1342268972Sjhb dsdt_line("})"); 1343268972Sjhb dsdt_line("Name (APRT, Package ()"); 1344268972Sjhb dsdt_line("{"); 1345268972Sjhb pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1346268972Sjhb dsdt_line("})"); 1347268972Sjhb dsdt_line("Method (_PRT, 0, NotSerialized)"); 1348268972Sjhb dsdt_line("{"); 1349268972Sjhb dsdt_line(" If (PICM)"); 1350268972Sjhb dsdt_line(" {"); 1351268972Sjhb dsdt_line(" Return (APRT)"); 1352268972Sjhb dsdt_line(" }"); 1353268972Sjhb dsdt_line(" Else"); 1354268972Sjhb dsdt_line(" {"); 1355268972Sjhb dsdt_line(" Return (PPRT)"); 1356268972Sjhb dsdt_line(" }"); 1357268972Sjhb dsdt_line("}"); 1358267393Sjhb dsdt_unindent(2); 1359267393Sjhb } 1360261265Sjhb 1361261265Sjhb dsdt_indent(2); 1362261265Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1363268887Sjhb si = &bi->slotinfo[slot]; 1364261265Sjhb for (func = 0; func < MAXFUNCS; func++) { 1365268887Sjhb pi = si->si_funcs[func].fi_devi; 1366261265Sjhb if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1367261265Sjhb pi->pi_d->pe_write_dsdt(pi); 1368261265Sjhb } 1369261265Sjhb } 1370261265Sjhb dsdt_unindent(2); 1371268887Sjhbdone: 1372261265Sjhb dsdt_line(" }"); 1373261265Sjhb} 1374261265Sjhb 1375268887Sjhbvoid 1376268887Sjhbpci_write_dsdt(void) 1377268887Sjhb{ 1378268887Sjhb int bus; 1379268887Sjhb 1380268972Sjhb dsdt_indent(1); 1381268972Sjhb dsdt_line("Name (PICM, 0x00)"); 1382268972Sjhb dsdt_line("Method (_PIC, 1, NotSerialized)"); 1383268972Sjhb dsdt_line("{"); 1384268972Sjhb dsdt_line(" Store (Arg0, PICM)"); 1385268972Sjhb dsdt_line("}"); 1386268972Sjhb dsdt_line(""); 1387268972Sjhb dsdt_line("Scope (_SB)"); 1388268972Sjhb dsdt_line("{"); 1389268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) 1390268887Sjhb pci_bus_write_dsdt(bus); 1391268972Sjhb dsdt_line("}"); 1392268972Sjhb dsdt_unindent(1); 1393268887Sjhb} 1394268887Sjhb 1395221828Sgrehanint 1396268887Sjhbpci_bus_configured(int bus) 1397268887Sjhb{ 1398268887Sjhb assert(bus >= 0 && bus < MAXBUSES); 1399268887Sjhb return (pci_businfo[bus] != NULL); 1400268887Sjhb} 1401268887Sjhb 1402268887Sjhbint 1403221828Sgrehanpci_msi_enabled(struct pci_devinst *pi) 1404221828Sgrehan{ 1405221828Sgrehan return (pi->pi_msi.enabled); 1406221828Sgrehan} 1407221828Sgrehan 1408221828Sgrehanint 1409262350Sjhbpci_msi_maxmsgnum(struct pci_devinst *pi) 1410221828Sgrehan{ 1411221828Sgrehan if (pi->pi_msi.enabled) 1412262350Sjhb return (pi->pi_msi.maxmsgnum); 1413221828Sgrehan else 1414221828Sgrehan return (0); 1415221828Sgrehan} 1416221828Sgrehan 1417246109Sneelint 1418246109Sneelpci_msix_enabled(struct pci_devinst *pi) 1419246109Sneel{ 1420246109Sneel 1421246109Sneel return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1422246109Sneel} 1423246109Sneel 1424221828Sgrehanvoid 1425246109Sneelpci_generate_msix(struct pci_devinst *pi, int index) 1426246109Sneel{ 1427246109Sneel struct msix_table_entry *mte; 1428246109Sneel 1429246109Sneel if (!pci_msix_enabled(pi)) 1430246109Sneel return; 1431246109Sneel 1432246109Sneel if (pi->pi_msix.function_mask) 1433246109Sneel return; 1434246109Sneel 1435246109Sneel if (index >= pi->pi_msix.table_count) 1436246109Sneel return; 1437246109Sneel 1438246109Sneel mte = &pi->pi_msix.table[index]; 1439246109Sneel if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1440246109Sneel /* XXX Set PBA bit if interrupt is disabled */ 1441262350Sjhb vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1442246109Sneel } 1443246109Sneel} 1444246109Sneel 1445246109Sneelvoid 1446262350Sjhbpci_generate_msi(struct pci_devinst *pi, int index) 1447221828Sgrehan{ 1448221828Sgrehan 1449262350Sjhb if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 1450262350Sjhb vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 1451262350Sjhb pi->pi_msi.msg_data + index); 1452221828Sgrehan } 1453221828Sgrehan} 1454221828Sgrehan 1455267393Sjhbstatic bool 1456267393Sjhbpci_lintr_permitted(struct pci_devinst *pi) 1457267393Sjhb{ 1458267393Sjhb uint16_t cmd; 1459267393Sjhb 1460267393Sjhb cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 1461267393Sjhb return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 1462267393Sjhb (cmd & PCIM_CMD_INTxDIS))); 1463267393Sjhb} 1464267393Sjhb 1465268972Sjhbvoid 1466267393Sjhbpci_lintr_request(struct pci_devinst *pi) 1467234938Sgrehan{ 1468268887Sjhb struct businfo *bi; 1469267393Sjhb struct slotinfo *si; 1470268972Sjhb int bestpin, bestcount, pin; 1471234938Sgrehan 1472268887Sjhb bi = pci_businfo[pi->pi_bus]; 1473268887Sjhb assert(bi != NULL); 1474268887Sjhb 1475267393Sjhb /* 1476268972Sjhb * Just allocate a pin from our slot. The pin will be 1477268972Sjhb * assigned IRQs later when interrupts are routed. 1478267393Sjhb */ 1479268887Sjhb si = &bi->slotinfo[pi->pi_slot]; 1480267393Sjhb bestpin = 0; 1481267393Sjhb bestcount = si->si_intpins[0].ii_count; 1482267393Sjhb for (pin = 1; pin < 4; pin++) { 1483267393Sjhb if (si->si_intpins[pin].ii_count < bestcount) { 1484267393Sjhb bestpin = pin; 1485267393Sjhb bestcount = si->si_intpins[pin].ii_count; 1486267393Sjhb } 1487267393Sjhb } 1488234938Sgrehan 1489267393Sjhb si->si_intpins[bestpin].ii_count++; 1490267393Sjhb pi->pi_lintr.pin = bestpin + 1; 1491267393Sjhb pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1492234938Sgrehan} 1493234938Sgrehan 1494268972Sjhbstatic void 1495268972Sjhbpci_lintr_route(struct pci_devinst *pi) 1496268972Sjhb{ 1497268972Sjhb struct businfo *bi; 1498268972Sjhb struct intxinfo *ii; 1499268972Sjhb 1500268972Sjhb if (pi->pi_lintr.pin == 0) 1501268972Sjhb return; 1502268972Sjhb 1503268972Sjhb bi = pci_businfo[pi->pi_bus]; 1504268972Sjhb assert(bi != NULL); 1505268972Sjhb ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1506268972Sjhb 1507268972Sjhb /* 1508268972Sjhb * Attempt to allocate an I/O APIC pin for this intpin if one 1509268972Sjhb * is not yet assigned. 1510268972Sjhb */ 1511268972Sjhb if (ii->ii_ioapic_irq == 0) 1512268972Sjhb ii->ii_ioapic_irq = ioapic_pci_alloc_irq(); 1513268972Sjhb assert(ii->ii_ioapic_irq > 0); 1514268972Sjhb 1515268972Sjhb /* 1516268972Sjhb * Attempt to allocate a PIRQ pin for this intpin if one is 1517268972Sjhb * not yet assigned. 1518268972Sjhb */ 1519268972Sjhb if (ii->ii_pirq_pin == 0) 1520268972Sjhb ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx); 1521268972Sjhb assert(ii->ii_pirq_pin > 0); 1522268972Sjhb 1523268972Sjhb pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1524268972Sjhb pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1525268972Sjhb pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 1526268972Sjhb} 1527268972Sjhb 1528234938Sgrehanvoid 1529234938Sgrehanpci_lintr_assert(struct pci_devinst *pi) 1530234938Sgrehan{ 1531234938Sgrehan 1532267393Sjhb assert(pi->pi_lintr.pin > 0); 1533261088Sjhb 1534267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1535267393Sjhb if (pi->pi_lintr.state == IDLE) { 1536267393Sjhb if (pci_lintr_permitted(pi)) { 1537267393Sjhb pi->pi_lintr.state = ASSERTED; 1538268972Sjhb pci_irq_assert(pi); 1539267393Sjhb } else 1540267393Sjhb pi->pi_lintr.state = PENDING; 1541261088Sjhb } 1542267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1543234938Sgrehan} 1544234938Sgrehan 1545234938Sgrehanvoid 1546234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi) 1547234938Sgrehan{ 1548234938Sgrehan 1549267393Sjhb assert(pi->pi_lintr.pin > 0); 1550261088Sjhb 1551267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1552267393Sjhb if (pi->pi_lintr.state == ASSERTED) { 1553267393Sjhb pi->pi_lintr.state = IDLE; 1554268972Sjhb pci_irq_deassert(pi); 1555267393Sjhb } else if (pi->pi_lintr.state == PENDING) 1556267393Sjhb pi->pi_lintr.state = IDLE; 1557267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1558267393Sjhb} 1559267393Sjhb 1560267393Sjhbstatic void 1561267393Sjhbpci_lintr_update(struct pci_devinst *pi) 1562267393Sjhb{ 1563267393Sjhb 1564267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1565267393Sjhb if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1566268972Sjhb pci_irq_deassert(pi); 1567267393Sjhb pi->pi_lintr.state = PENDING; 1568267393Sjhb } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 1569267393Sjhb pi->pi_lintr.state = ASSERTED; 1570268972Sjhb pci_irq_assert(pi); 1571261088Sjhb } 1572267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1573234938Sgrehan} 1574234938Sgrehan 1575267393Sjhbint 1576268887Sjhbpci_count_lintr(int bus) 1577267393Sjhb{ 1578267393Sjhb int count, slot, pin; 1579268887Sjhb struct slotinfo *slotinfo; 1580267393Sjhb 1581267393Sjhb count = 0; 1582268887Sjhb if (pci_businfo[bus] != NULL) { 1583268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1584268887Sjhb slotinfo = &pci_businfo[bus]->slotinfo[slot]; 1585268887Sjhb for (pin = 0; pin < 4; pin++) { 1586268887Sjhb if (slotinfo->si_intpins[pin].ii_count != 0) 1587268887Sjhb count++; 1588268887Sjhb } 1589267393Sjhb } 1590267393Sjhb } 1591267393Sjhb return (count); 1592267393Sjhb} 1593267393Sjhb 1594267393Sjhbvoid 1595268887Sjhbpci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 1596267393Sjhb{ 1597268887Sjhb struct businfo *bi; 1598268887Sjhb struct slotinfo *si; 1599267393Sjhb struct intxinfo *ii; 1600267393Sjhb int slot, pin; 1601267393Sjhb 1602268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1603268887Sjhb return; 1604268887Sjhb 1605267393Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1606268887Sjhb si = &bi->slotinfo[slot]; 1607267393Sjhb for (pin = 0; pin < 4; pin++) { 1608268887Sjhb ii = &si->si_intpins[pin]; 1609267393Sjhb if (ii->ii_count != 0) 1610268972Sjhb cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1611268972Sjhb ii->ii_ioapic_irq, arg); 1612267393Sjhb } 1613267393Sjhb } 1614267393Sjhb} 1615267393Sjhb 1616239085Sneel/* 1617239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device. 1618239085Sneel * Return 0 otherwise. 1619239085Sneel */ 1620239085Sneelstatic int 1621268887Sjhbpci_emul_is_mfdev(int bus, int slot) 1622239085Sneel{ 1623268887Sjhb struct businfo *bi; 1624268887Sjhb struct slotinfo *si; 1625239085Sneel int f, numfuncs; 1626234938Sgrehan 1627239085Sneel numfuncs = 0; 1628268887Sjhb if ((bi = pci_businfo[bus]) != NULL) { 1629268887Sjhb si = &bi->slotinfo[slot]; 1630268887Sjhb for (f = 0; f < MAXFUNCS; f++) { 1631268887Sjhb if (si->si_funcs[f].fi_devi != NULL) { 1632268887Sjhb numfuncs++; 1633268887Sjhb } 1634239085Sneel } 1635239085Sneel } 1636239085Sneel return (numfuncs > 1); 1637239085Sneel} 1638234938Sgrehan 1639239085Sneel/* 1640239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 1641239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'. 1642239085Sneel */ 1643239085Sneelstatic void 1644268887Sjhbpci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) 1645239085Sneel{ 1646239085Sneel int mfdev; 1647239085Sneel 1648239085Sneel if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 1649268887Sjhb mfdev = pci_emul_is_mfdev(bus, slot); 1650239085Sneel switch (bytes) { 1651239085Sneel case 1: 1652239085Sneel case 2: 1653239085Sneel *rv &= ~PCIM_MFDEV; 1654239085Sneel if (mfdev) { 1655239085Sneel *rv |= PCIM_MFDEV; 1656239085Sneel } 1657239085Sneel break; 1658239085Sneel case 4: 1659239085Sneel *rv &= ~(PCIM_MFDEV << 16); 1660239085Sneel if (mfdev) { 1661239085Sneel *rv |= (PCIM_MFDEV << 16); 1662239085Sneel } 1663239085Sneel break; 1664239085Sneel } 1665239085Sneel } 1666239085Sneel} 1667239085Sneel 1668284899Sneelstatic void 1669284899Sneelpci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) 1670249321Sneel{ 1671284899Sneel int i, rshift; 1672284899Sneel uint32_t cmd, cmd2, changed, old, readonly; 1673249321Sneel 1674284899Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 1675249321Sneel 1676249321Sneel /* 1677284899Sneel * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. 1678284899Sneel * 1679284899Sneel * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are 1680284899Sneel * 'write 1 to clear'. However these bits are not set to '1' by 1681284899Sneel * any device emulation so it is simpler to treat them as readonly. 1682249321Sneel */ 1683284899Sneel rshift = (coff & 0x3) * 8; 1684284899Sneel readonly = 0xFFFFF880 >> rshift; 1685249321Sneel 1686284899Sneel old = CFGREAD(pi, coff, bytes); 1687284899Sneel new &= ~readonly; 1688284899Sneel new |= (old & readonly); 1689284899Sneel CFGWRITE(pi, coff, new, bytes); /* update config */ 1690249321Sneel 1691284899Sneel cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ 1692284899Sneel changed = cmd ^ cmd2; 1693284899Sneel 1694249321Sneel /* 1695249321Sneel * If the MMIO or I/O address space decoding has changed then 1696249321Sneel * register/unregister all BARs that decode that address space. 1697249321Sneel */ 1698259301Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 1699249321Sneel switch (pi->pi_bar[i].type) { 1700249321Sneel case PCIBAR_NONE: 1701249321Sneel case PCIBAR_MEMHI64: 1702249321Sneel break; 1703249321Sneel case PCIBAR_IO: 1704249321Sneel /* I/O address space decoding changed? */ 1705284899Sneel if (changed & PCIM_CMD_PORTEN) { 1706249321Sneel if (porten(pi)) 1707249321Sneel register_bar(pi, i); 1708249321Sneel else 1709249321Sneel unregister_bar(pi, i); 1710249321Sneel } 1711249321Sneel break; 1712249321Sneel case PCIBAR_MEM32: 1713249321Sneel case PCIBAR_MEM64: 1714249321Sneel /* MMIO address space decoding changed? */ 1715284899Sneel if (changed & PCIM_CMD_MEMEN) { 1716249321Sneel if (memen(pi)) 1717249321Sneel register_bar(pi, i); 1718249321Sneel else 1719249321Sneel unregister_bar(pi, i); 1720249321Sneel } 1721249321Sneel break; 1722249321Sneel default: 1723249321Sneel assert(0); 1724249321Sneel } 1725249321Sneel } 1726267393Sjhb 1727267393Sjhb /* 1728267393Sjhb * If INTx has been unmasked and is pending, assert the 1729267393Sjhb * interrupt. 1730267393Sjhb */ 1731267393Sjhb pci_lintr_update(pi); 1732249321Sneel} 1733249321Sneel 1734270159Sgrehanstatic void 1735270159Sgrehanpci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, 1736270159Sgrehan int coff, int bytes, uint32_t *eax) 1737221828Sgrehan{ 1738268887Sjhb struct businfo *bi; 1739268887Sjhb struct slotinfo *si; 1740221828Sgrehan struct pci_devinst *pi; 1741221828Sgrehan struct pci_devemu *pe; 1742270159Sgrehan int idx, needcfg; 1743249321Sneel uint64_t addr, bar, mask; 1744221828Sgrehan 1745270159Sgrehan if ((bi = pci_businfo[bus]) != NULL) { 1746270159Sgrehan si = &bi->slotinfo[slot]; 1747270159Sgrehan pi = si->si_funcs[func].fi_devi; 1748268887Sjhb } else 1749242170Sneel pi = NULL; 1750242170Sneel 1751239085Sneel /* 1752270159Sgrehan * Just return if there is no device at this slot:func or if the 1753270159Sgrehan * the guest is doing an un-aligned access. 1754239085Sneel */ 1755270159Sgrehan if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || 1756270159Sgrehan (coff & (bytes - 1)) != 0) { 1757221828Sgrehan if (in) 1758221828Sgrehan *eax = 0xffffffff; 1759270159Sgrehan return; 1760221828Sgrehan } 1761221828Sgrehan 1762270159Sgrehan /* 1763270159Sgrehan * Ignore all writes beyond the standard config space and return all 1764270159Sgrehan * ones on reads. 1765270159Sgrehan */ 1766270159Sgrehan if (coff >= PCI_REGMAX + 1) { 1767270159Sgrehan if (in) { 1768270159Sgrehan *eax = 0xffffffff; 1769270159Sgrehan /* 1770270159Sgrehan * Extended capabilities begin at offset 256 in config 1771270159Sgrehan * space. Absence of extended capabilities is signaled 1772270159Sgrehan * with all 0s in the extended capability header at 1773270159Sgrehan * offset 256. 1774270159Sgrehan */ 1775270159Sgrehan if (coff <= PCI_REGMAX + 4) 1776270159Sgrehan *eax = 0x00000000; 1777270159Sgrehan } 1778270159Sgrehan return; 1779270159Sgrehan } 1780270159Sgrehan 1781221828Sgrehan pe = pi->pi_d; 1782221828Sgrehan 1783221828Sgrehan /* 1784221828Sgrehan * Config read 1785221828Sgrehan */ 1786221828Sgrehan if (in) { 1787221828Sgrehan /* Let the device emulation override the default handler */ 1788239085Sneel if (pe->pe_cfgread != NULL) { 1789270159Sgrehan needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, 1790270159Sgrehan eax); 1791239085Sneel } else { 1792239085Sneel needcfg = 1; 1793239085Sneel } 1794221828Sgrehan 1795284899Sneel if (needcfg) 1796284899Sneel *eax = CFGREAD(pi, coff, bytes); 1797239085Sneel 1798270159Sgrehan pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); 1799221828Sgrehan } else { 1800221828Sgrehan /* Let the device emulation override the default handler */ 1801221828Sgrehan if (pe->pe_cfgwrite != NULL && 1802221828Sgrehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 1803270159Sgrehan return; 1804221828Sgrehan 1805221828Sgrehan /* 1806221828Sgrehan * Special handling for write to BAR registers 1807221828Sgrehan */ 1808221828Sgrehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { 1809221828Sgrehan /* 1810221828Sgrehan * Ignore writes to BAR registers that are not 1811221828Sgrehan * 4-byte aligned. 1812221828Sgrehan */ 1813221828Sgrehan if (bytes != 4 || (coff & 0x3) != 0) 1814270159Sgrehan return; 1815221828Sgrehan idx = (coff - PCIR_BAR(0)) / 4; 1816249321Sneel mask = ~(pi->pi_bar[idx].size - 1); 1817221828Sgrehan switch (pi->pi_bar[idx].type) { 1818221828Sgrehan case PCIBAR_NONE: 1819249321Sneel pi->pi_bar[idx].addr = bar = 0; 1820221828Sgrehan break; 1821221828Sgrehan case PCIBAR_IO: 1822249321Sneel addr = *eax & mask; 1823249321Sneel addr &= 0xffff; 1824249321Sneel bar = addr | PCIM_BAR_IO_SPACE; 1825249321Sneel /* 1826249321Sneel * Register the new BAR value for interception 1827249321Sneel */ 1828249321Sneel if (addr != pi->pi_bar[idx].addr) { 1829249321Sneel update_bar_address(pi, addr, idx, 1830249321Sneel PCIBAR_IO); 1831249321Sneel } 1832221828Sgrehan break; 1833221828Sgrehan case PCIBAR_MEM32: 1834249321Sneel addr = bar = *eax & mask; 1835221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 1836249321Sneel if (addr != pi->pi_bar[idx].addr) { 1837249321Sneel update_bar_address(pi, addr, idx, 1838249321Sneel PCIBAR_MEM32); 1839249321Sneel } 1840221828Sgrehan break; 1841221828Sgrehan case PCIBAR_MEM64: 1842249321Sneel addr = bar = *eax & mask; 1843221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 1844221828Sgrehan PCIM_BAR_MEM_PREFETCH; 1845249321Sneel if (addr != (uint32_t)pi->pi_bar[idx].addr) { 1846249321Sneel update_bar_address(pi, addr, idx, 1847249321Sneel PCIBAR_MEM64); 1848249321Sneel } 1849221828Sgrehan break; 1850221828Sgrehan case PCIBAR_MEMHI64: 1851221828Sgrehan mask = ~(pi->pi_bar[idx - 1].size - 1); 1852249321Sneel addr = ((uint64_t)*eax << 32) & mask; 1853249321Sneel bar = addr >> 32; 1854249321Sneel if (bar != pi->pi_bar[idx - 1].addr >> 32) { 1855249321Sneel update_bar_address(pi, addr, idx - 1, 1856249321Sneel PCIBAR_MEMHI64); 1857249321Sneel } 1858221828Sgrehan break; 1859221828Sgrehan default: 1860221828Sgrehan assert(0); 1861221828Sgrehan } 1862221828Sgrehan pci_set_cfgdata32(pi, coff, bar); 1863234761Sgrehan 1864221828Sgrehan } else if (pci_emul_iscap(pi, coff)) { 1865221828Sgrehan pci_emul_capwrite(pi, coff, bytes, *eax); 1866284899Sneel } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { 1867284899Sneel pci_emul_cmdsts_write(pi, coff, *eax, bytes); 1868221828Sgrehan } else { 1869221828Sgrehan CFGWRITE(pi, coff, *eax, bytes); 1870221828Sgrehan } 1871221828Sgrehan } 1872270159Sgrehan} 1873221828Sgrehan 1874270159Sgrehanstatic int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 1875270159Sgrehan 1876270159Sgrehanstatic int 1877270159Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1878270159Sgrehan uint32_t *eax, void *arg) 1879270159Sgrehan{ 1880270159Sgrehan uint32_t x; 1881270159Sgrehan 1882270159Sgrehan if (bytes != 4) { 1883270159Sgrehan if (in) 1884270159Sgrehan *eax = (bytes == 2) ? 0xffff : 0xff; 1885270159Sgrehan return (0); 1886270159Sgrehan } 1887270159Sgrehan 1888270159Sgrehan if (in) { 1889270159Sgrehan x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; 1890270159Sgrehan if (cfgenable) 1891270159Sgrehan x |= CONF1_ENABLE; 1892270159Sgrehan *eax = x; 1893270159Sgrehan } else { 1894270159Sgrehan x = *eax; 1895270159Sgrehan cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 1896270159Sgrehan cfgoff = x & PCI_REGMAX; 1897270159Sgrehan cfgfunc = (x >> 8) & PCI_FUNCMAX; 1898270159Sgrehan cfgslot = (x >> 11) & PCI_SLOTMAX; 1899270159Sgrehan cfgbus = (x >> 16) & PCI_BUSMAX; 1900270159Sgrehan } 1901270159Sgrehan 1902221828Sgrehan return (0); 1903221828Sgrehan} 1904270159SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 1905221828Sgrehan 1906270159Sgrehanstatic int 1907270159Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1908270159Sgrehan uint32_t *eax, void *arg) 1909270159Sgrehan{ 1910270159Sgrehan int coff; 1911270159Sgrehan 1912270159Sgrehan assert(bytes == 1 || bytes == 2 || bytes == 4); 1913270159Sgrehan 1914270159Sgrehan coff = cfgoff + (port - CONF1_DATA_PORT); 1915270159Sgrehan if (cfgenable) { 1916270159Sgrehan pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, 1917270159Sgrehan eax); 1918270159Sgrehan } else { 1919270159Sgrehan /* Ignore accesses to cfgdata if not enabled by cfgaddr */ 1920270159Sgrehan if (in) 1921270159Sgrehan *eax = 0xffffffff; 1922270159Sgrehan } 1923270159Sgrehan return (0); 1924270159Sgrehan} 1925270159Sgrehan 1926221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 1927221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 1928221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 1929221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 1930221828Sgrehan 1931221828Sgrehan#define PCI_EMUL_TEST 1932221828Sgrehan#ifdef PCI_EMUL_TEST 1933221828Sgrehan/* 1934221828Sgrehan * Define a dummy test device 1935221828Sgrehan */ 1936268887Sjhb#define DIOSZ 8 1937241744Sgrehan#define DMEMSZ 4096 1938221828Sgrehanstruct pci_emul_dsoftc { 1939241744Sgrehan uint8_t ioregs[DIOSZ]; 1940284900Sneel uint8_t memregs[2][DMEMSZ]; 1941221828Sgrehan}; 1942221828Sgrehan 1943241744Sgrehan#define PCI_EMUL_MSI_MSGS 4 1944241744Sgrehan#define PCI_EMUL_MSIX_MSGS 16 1945221828Sgrehan 1946221942Sjhbstatic int 1947221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 1948221828Sgrehan{ 1949221828Sgrehan int error; 1950221828Sgrehan struct pci_emul_dsoftc *sc; 1951221828Sgrehan 1952268953Sjhb sc = calloc(1, sizeof(struct pci_emul_dsoftc)); 1953221828Sgrehan 1954221828Sgrehan pi->pi_arg = sc; 1955221828Sgrehan 1956221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 1957221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 1958221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 1959221828Sgrehan 1960241744Sgrehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 1961221828Sgrehan assert(error == 0); 1962221828Sgrehan 1963241744Sgrehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 1964221828Sgrehan assert(error == 0); 1965221828Sgrehan 1966241744Sgrehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 1967241744Sgrehan assert(error == 0); 1968241744Sgrehan 1969284900Sneel error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); 1970284900Sneel assert(error == 0); 1971284900Sneel 1972221828Sgrehan return (0); 1973221828Sgrehan} 1974221828Sgrehan 1975221942Sjhbstatic void 1976241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 1977241744Sgrehan uint64_t offset, int size, uint64_t value) 1978221828Sgrehan{ 1979221828Sgrehan int i; 1980221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1981221828Sgrehan 1982241744Sgrehan if (baridx == 0) { 1983241744Sgrehan if (offset + size > DIOSZ) { 1984241744Sgrehan printf("diow: iow too large, offset %ld size %d\n", 1985241744Sgrehan offset, size); 1986241744Sgrehan return; 1987241744Sgrehan } 1988221828Sgrehan 1989241744Sgrehan if (size == 1) { 1990241744Sgrehan sc->ioregs[offset] = value & 0xff; 1991241744Sgrehan } else if (size == 2) { 1992241744Sgrehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 1993241744Sgrehan } else if (size == 4) { 1994241744Sgrehan *(uint32_t *)&sc->ioregs[offset] = value; 1995241744Sgrehan } else { 1996241744Sgrehan printf("diow: iow unknown size %d\n", size); 1997241744Sgrehan } 1998241744Sgrehan 1999241744Sgrehan /* 2000241744Sgrehan * Special magic value to generate an interrupt 2001241744Sgrehan */ 2002241744Sgrehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 2003262350Sjhb pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 2004241744Sgrehan 2005241744Sgrehan if (value == 0xabcdef) { 2006262350Sjhb for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 2007241744Sgrehan pci_generate_msi(pi, i); 2008241744Sgrehan } 2009221828Sgrehan } 2010221828Sgrehan 2011284900Sneel if (baridx == 1 || baridx == 2) { 2012241744Sgrehan if (offset + size > DMEMSZ) { 2013241744Sgrehan printf("diow: memw too large, offset %ld size %d\n", 2014241744Sgrehan offset, size); 2015241744Sgrehan return; 2016241744Sgrehan } 2017221828Sgrehan 2018284900Sneel i = baridx - 1; /* 'memregs' index */ 2019284900Sneel 2020241744Sgrehan if (size == 1) { 2021284900Sneel sc->memregs[i][offset] = value; 2022241744Sgrehan } else if (size == 2) { 2023284900Sneel *(uint16_t *)&sc->memregs[i][offset] = value; 2024241744Sgrehan } else if (size == 4) { 2025284900Sneel *(uint32_t *)&sc->memregs[i][offset] = value; 2026241744Sgrehan } else if (size == 8) { 2027284900Sneel *(uint64_t *)&sc->memregs[i][offset] = value; 2028241744Sgrehan } else { 2029241744Sgrehan printf("diow: memw unknown size %d\n", size); 2030241744Sgrehan } 2031241744Sgrehan 2032241744Sgrehan /* 2033241744Sgrehan * magic interrupt ?? 2034241744Sgrehan */ 2035221828Sgrehan } 2036241744Sgrehan 2037284900Sneel if (baridx > 2) { 2038241744Sgrehan printf("diow: unknown bar idx %d\n", baridx); 2039241744Sgrehan } 2040221828Sgrehan} 2041221828Sgrehan 2042241744Sgrehanstatic uint64_t 2043241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 2044241744Sgrehan uint64_t offset, int size) 2045221828Sgrehan{ 2046221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2047221828Sgrehan uint32_t value; 2048284900Sneel int i; 2049221828Sgrehan 2050241744Sgrehan if (baridx == 0) { 2051241744Sgrehan if (offset + size > DIOSZ) { 2052241744Sgrehan printf("dior: ior too large, offset %ld size %d\n", 2053241744Sgrehan offset, size); 2054241744Sgrehan return (0); 2055241744Sgrehan } 2056241744Sgrehan 2057241744Sgrehan if (size == 1) { 2058241744Sgrehan value = sc->ioregs[offset]; 2059241744Sgrehan } else if (size == 2) { 2060241744Sgrehan value = *(uint16_t *) &sc->ioregs[offset]; 2061241744Sgrehan } else if (size == 4) { 2062241744Sgrehan value = *(uint32_t *) &sc->ioregs[offset]; 2063241744Sgrehan } else { 2064241744Sgrehan printf("dior: ior unknown size %d\n", size); 2065241744Sgrehan } 2066221828Sgrehan } 2067284900Sneel 2068284900Sneel if (baridx == 1 || baridx == 2) { 2069241744Sgrehan if (offset + size > DMEMSZ) { 2070241744Sgrehan printf("dior: memr too large, offset %ld size %d\n", 2071241744Sgrehan offset, size); 2072241744Sgrehan return (0); 2073241744Sgrehan } 2074284900Sneel 2075284900Sneel i = baridx - 1; /* 'memregs' index */ 2076284900Sneel 2077241744Sgrehan if (size == 1) { 2078284900Sneel value = sc->memregs[i][offset]; 2079241744Sgrehan } else if (size == 2) { 2080284900Sneel value = *(uint16_t *) &sc->memregs[i][offset]; 2081241744Sgrehan } else if (size == 4) { 2082284900Sneel value = *(uint32_t *) &sc->memregs[i][offset]; 2083241744Sgrehan } else if (size == 8) { 2084284900Sneel value = *(uint64_t *) &sc->memregs[i][offset]; 2085241744Sgrehan } else { 2086241744Sgrehan printf("dior: ior unknown size %d\n", size); 2087241744Sgrehan } 2088221828Sgrehan } 2089221828Sgrehan 2090241744Sgrehan 2091284900Sneel if (baridx > 2) { 2092241744Sgrehan printf("dior: unknown bar idx %d\n", baridx); 2093241744Sgrehan return (0); 2094241744Sgrehan } 2095241744Sgrehan 2096221828Sgrehan return (value); 2097221828Sgrehan} 2098221828Sgrehan 2099221828Sgrehanstruct pci_devemu pci_dummy = { 2100221828Sgrehan .pe_emu = "dummy", 2101221828Sgrehan .pe_init = pci_emul_dinit, 2102241744Sgrehan .pe_barwrite = pci_emul_diow, 2103241744Sgrehan .pe_barread = pci_emul_dior 2104221828Sgrehan}; 2105221828SgrehanPCI_EMUL_SET(pci_dummy); 2106221828Sgrehan 2107221828Sgrehan#endif /* PCI_EMUL_TEST */ 2108