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$ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD$"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34221828Sgrehan 35221828Sgrehan#include <ctype.h> 36302705Sngie#include <errno.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 /* Number of msi messages must be a power of 2 between 1 and 32 */ 764221828Sgrehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 765221828Sgrehan mmc = ffs(msgnum) - 1; 766221828Sgrehan 767221828Sgrehan bzero(msicap, sizeof(struct msicap)); 768221828Sgrehan msicap->capid = PCIY_MSI; 769221828Sgrehan msicap->nextptr = nextptr; 770221828Sgrehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 771221828Sgrehan} 772221828Sgrehan 773221828Sgrehanint 774221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 775221828Sgrehan{ 776221828Sgrehan struct msicap msicap; 777221828Sgrehan 778221828Sgrehan pci_populate_msicap(&msicap, msgnum, 0); 779221828Sgrehan 780221828Sgrehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 781221828Sgrehan} 782221828Sgrehan 783246109Sneelstatic void 784246109Sneelpci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 785268887Sjhb uint32_t msix_tab_size) 786246109Sneel{ 787246109Sneel 788246109Sneel assert(msix_tab_size % 4096 == 0); 789246109Sneel 790246109Sneel bzero(msixcap, sizeof(struct msixcap)); 791246109Sneel msixcap->capid = PCIY_MSIX; 792246109Sneel 793246109Sneel /* 794246109Sneel * Message Control Register, all fields set to 795246109Sneel * zero except for the Table Size. 796246109Sneel * Note: Table size N is encoded as N-1 797246109Sneel */ 798246109Sneel msixcap->msgctrl = msgnum - 1; 799246109Sneel 800246109Sneel /* 801246109Sneel * MSI-X BAR setup: 802246109Sneel * - MSI-X table start at offset 0 803246109Sneel * - PBA table starts at a 4K aligned offset after the MSI-X table 804246109Sneel */ 805246109Sneel msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 806246109Sneel msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 807246109Sneel} 808246109Sneel 809246109Sneelstatic void 810246109Sneelpci_msix_table_init(struct pci_devinst *pi, int table_entries) 811246109Sneel{ 812246109Sneel int i, table_size; 813246109Sneel 814246109Sneel assert(table_entries > 0); 815246109Sneel assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 816246109Sneel 817246109Sneel table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 818268953Sjhb pi->pi_msix.table = calloc(1, table_size); 819246109Sneel 820246109Sneel /* set mask bit of vector control register */ 821246109Sneel for (i = 0; i < table_entries; i++) 822246109Sneel pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 823246109Sneel} 824246109Sneel 825246109Sneelint 826246109Sneelpci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 827246109Sneel{ 828246109Sneel uint32_t tab_size; 829246109Sneel struct msixcap msixcap; 830246109Sneel 831246109Sneel assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 832246109Sneel assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 833246109Sneel 834246109Sneel tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 835246109Sneel 836246109Sneel /* Align table size to nearest 4K */ 837246109Sneel tab_size = roundup2(tab_size, 4096); 838246109Sneel 839246109Sneel pi->pi_msix.table_bar = barnum; 840246109Sneel pi->pi_msix.pba_bar = barnum; 841246109Sneel pi->pi_msix.table_offset = 0; 842246109Sneel pi->pi_msix.table_count = msgnum; 843246109Sneel pi->pi_msix.pba_offset = tab_size; 844268887Sjhb pi->pi_msix.pba_size = PBA_SIZE(msgnum); 845246109Sneel 846246109Sneel pci_msix_table_init(pi, msgnum); 847246109Sneel 848268887Sjhb pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); 849246109Sneel 850246109Sneel /* allocate memory for MSI-X Table and PBA */ 851246109Sneel pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 852246109Sneel tab_size + pi->pi_msix.pba_size); 853246109Sneel 854246109Sneel return (pci_emul_add_capability(pi, (u_char *)&msixcap, 855246109Sneel sizeof(msixcap))); 856246109Sneel} 857246109Sneel 858221828Sgrehanvoid 859234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 860234761Sgrehan int bytes, uint32_t val) 861234761Sgrehan{ 862234761Sgrehan uint16_t msgctrl, rwmask; 863293408Saraujo int off; 864246109Sneel 865234761Sgrehan off = offset - capoff; 866234761Sgrehan /* Message Control Register */ 867234761Sgrehan if (off == 2 && bytes == 2) { 868234761Sgrehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 869234761Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 870234761Sgrehan msgctrl &= ~rwmask; 871234761Sgrehan msgctrl |= val & rwmask; 872234761Sgrehan val = msgctrl; 873234761Sgrehan 874234761Sgrehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 875246109Sneel pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 876267393Sjhb pci_lintr_update(pi); 877234761Sgrehan } 878234761Sgrehan 879234761Sgrehan CFGWRITE(pi, offset, val, bytes); 880234761Sgrehan} 881234761Sgrehan 882234761Sgrehanvoid 883221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 884221828Sgrehan int bytes, uint32_t val) 885221828Sgrehan{ 886221828Sgrehan uint16_t msgctrl, rwmask, msgdata, mme; 887221828Sgrehan uint32_t addrlo; 888221828Sgrehan 889221828Sgrehan /* 890221828Sgrehan * If guest is writing to the message control register make sure 891221828Sgrehan * we do not overwrite read-only fields. 892221828Sgrehan */ 893221828Sgrehan if ((offset - capoff) == 2 && bytes == 2) { 894221828Sgrehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 895221828Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 896221828Sgrehan msgctrl &= ~rwmask; 897221828Sgrehan msgctrl |= val & rwmask; 898221828Sgrehan val = msgctrl; 899221828Sgrehan 900221828Sgrehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 901221828Sgrehan if (msgctrl & PCIM_MSICTRL_64BIT) 902221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 903221828Sgrehan else 904221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 905221828Sgrehan 906221828Sgrehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 907221828Sgrehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 908221828Sgrehan if (pi->pi_msi.enabled) { 909262350Sjhb pi->pi_msi.addr = addrlo; 910262350Sjhb pi->pi_msi.msg_data = msgdata; 911262350Sjhb pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 912221828Sgrehan } else { 913262350Sjhb pi->pi_msi.maxmsgnum = 0; 914221828Sgrehan } 915267393Sjhb pci_lintr_update(pi); 916221828Sgrehan } 917221828Sgrehan 918221828Sgrehan CFGWRITE(pi, offset, val, bytes); 919221828Sgrehan} 920221828Sgrehan 921246846Sneelvoid 922246846Sneelpciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 923246846Sneel int bytes, uint32_t val) 924246846Sneel{ 925246846Sneel 926246846Sneel /* XXX don't write to the readonly parts */ 927246846Sneel CFGWRITE(pi, offset, val, bytes); 928246846Sneel} 929246846Sneel 930246846Sneel#define PCIECAP_VERSION 0x2 931246846Sneelint 932246846Sneelpci_emul_add_pciecap(struct pci_devinst *pi, int type) 933246846Sneel{ 934246846Sneel int err; 935246846Sneel struct pciecap pciecap; 936246846Sneel 937246846Sneel if (type != PCIEM_TYPE_ROOT_PORT) 938246846Sneel return (-1); 939246846Sneel 940246846Sneel bzero(&pciecap, sizeof(pciecap)); 941246846Sneel 942246846Sneel pciecap.capid = PCIY_EXPRESS; 943246846Sneel pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT; 944246846Sneel pciecap.link_capabilities = 0x411; /* gen1, x1 */ 945246846Sneel pciecap.link_status = 0x11; /* gen1, x1 */ 946246846Sneel 947246846Sneel err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 948246846Sneel return (err); 949246846Sneel} 950246846Sneel 951221828Sgrehan/* 952221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the 953221828Sgrehan * config space. 954221828Sgrehan */ 955221828Sgrehanstatic void 956221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) 957221828Sgrehan{ 958221828Sgrehan int capid; 959221828Sgrehan uint8_t capoff, nextoff; 960221828Sgrehan 961221828Sgrehan /* Do not allow un-aligned writes */ 962221828Sgrehan if ((offset & (bytes - 1)) != 0) 963221828Sgrehan return; 964221828Sgrehan 965221828Sgrehan /* Find the capability that we want to update */ 966221828Sgrehan capoff = CAP_START_OFFSET; 967221828Sgrehan while (1) { 968268887Sjhb nextoff = pci_get_cfgdata8(pi, capoff + 1); 969268887Sjhb if (nextoff == 0) 970221828Sgrehan break; 971221828Sgrehan if (offset >= capoff && offset < nextoff) 972221828Sgrehan break; 973221828Sgrehan 974221828Sgrehan capoff = nextoff; 975221828Sgrehan } 976221828Sgrehan assert(offset >= capoff); 977221828Sgrehan 978221828Sgrehan /* 979256248Sgrehan * Capability ID and Next Capability Pointer are readonly. 980256248Sgrehan * However, some o/s's do 4-byte writes that include these. 981256248Sgrehan * For this case, trim the write back to 2 bytes and adjust 982256248Sgrehan * the data. 983221828Sgrehan */ 984256248Sgrehan if (offset == capoff || offset == capoff + 1) { 985256248Sgrehan if (offset == capoff && bytes == 4) { 986256248Sgrehan bytes = 2; 987256248Sgrehan offset += 2; 988256248Sgrehan val >>= 16; 989256248Sgrehan } else 990256248Sgrehan return; 991256248Sgrehan } 992221828Sgrehan 993268887Sjhb capid = pci_get_cfgdata8(pi, capoff); 994221828Sgrehan switch (capid) { 995221828Sgrehan case PCIY_MSI: 996221828Sgrehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 997221828Sgrehan break; 998246109Sneel case PCIY_MSIX: 999246109Sneel msixcap_cfgwrite(pi, capoff, offset, bytes, val); 1000246109Sneel break; 1001246846Sneel case PCIY_EXPRESS: 1002246846Sneel pciecap_cfgwrite(pi, capoff, offset, bytes, val); 1003246846Sneel break; 1004221828Sgrehan default: 1005221828Sgrehan break; 1006221828Sgrehan } 1007221828Sgrehan} 1008221828Sgrehan 1009221828Sgrehanstatic int 1010221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset) 1011221828Sgrehan{ 1012221828Sgrehan uint16_t sts; 1013221828Sgrehan 1014221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 1015221828Sgrehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 1016268887Sjhb if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1017268887Sjhb return (1); 1018221828Sgrehan } 1019268887Sjhb return (0); 1020221828Sgrehan} 1021221828Sgrehan 1022247144Sgrehanstatic int 1023247144Sgrehanpci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1024247144Sgrehan int size, uint64_t *val, void *arg1, long arg2) 1025247144Sgrehan{ 1026247144Sgrehan /* 1027247144Sgrehan * Ignore writes; return 0xff's for reads. The mem read code 1028247144Sgrehan * will take care of truncating to the correct size. 1029247144Sgrehan */ 1030247144Sgrehan if (dir == MEM_F_READ) { 1031247144Sgrehan *val = 0xffffffffffffffff; 1032247144Sgrehan } 1033247144Sgrehan 1034247144Sgrehan return (0); 1035247144Sgrehan} 1036247144Sgrehan 1037270159Sgrehanstatic int 1038270159Sgrehanpci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1039270159Sgrehan int bytes, uint64_t *val, void *arg1, long arg2) 1040270159Sgrehan{ 1041270159Sgrehan int bus, slot, func, coff, in; 1042270159Sgrehan 1043270159Sgrehan coff = addr & 0xfff; 1044270159Sgrehan func = (addr >> 12) & 0x7; 1045270159Sgrehan slot = (addr >> 15) & 0x1f; 1046270159Sgrehan bus = (addr >> 20) & 0xff; 1047270159Sgrehan in = (dir == MEM_F_READ); 1048270159Sgrehan if (in) 1049270159Sgrehan *val = ~0UL; 1050270159Sgrehan pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); 1051270159Sgrehan return (0); 1052270159Sgrehan} 1053270159Sgrehan 1054270159Sgrehanuint64_t 1055270159Sgrehanpci_ecfg_base(void) 1056270159Sgrehan{ 1057270159Sgrehan 1058270159Sgrehan return (PCI_EMUL_ECFG_BASE); 1059270159Sgrehan} 1060270159Sgrehan 1061268887Sjhb#define BUSIO_ROUNDUP 32 1062268887Sjhb#define BUSMEM_ROUNDUP (1024 * 1024) 1063268887Sjhb 1064252682Sgrehanint 1065221828Sgrehaninit_pci(struct vmctx *ctx) 1066221828Sgrehan{ 1067270159Sgrehan struct mem_range mr; 1068221828Sgrehan struct pci_devemu *pde; 1069268887Sjhb struct businfo *bi; 1070268887Sjhb struct slotinfo *si; 1071267393Sjhb struct funcinfo *fi; 1072249572Sneel size_t lowmem; 1073268887Sjhb int bus, slot, func; 1074247144Sgrehan int error; 1075221828Sgrehan 1076221828Sgrehan pci_emul_iobase = PCI_EMUL_IOBASE; 1077249572Sneel pci_emul_membase32 = vm_get_lowmem_limit(ctx); 1078221828Sgrehan pci_emul_membase64 = PCI_EMUL_MEMBASE64; 1079221828Sgrehan 1080268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1081268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1082268887Sjhb continue; 1083268887Sjhb /* 1084268887Sjhb * Keep track of the i/o and memory resources allocated to 1085268887Sjhb * this bus. 1086268887Sjhb */ 1087268887Sjhb bi->iobase = pci_emul_iobase; 1088268887Sjhb bi->membase32 = pci_emul_membase32; 1089268887Sjhb bi->membase64 = pci_emul_membase64; 1090268887Sjhb 1091268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1092268887Sjhb si = &bi->slotinfo[slot]; 1093268887Sjhb for (func = 0; func < MAXFUNCS; func++) { 1094268887Sjhb fi = &si->si_funcs[func]; 1095268887Sjhb if (fi->fi_name == NULL) 1096268887Sjhb continue; 1097267393Sjhb pde = pci_emul_finddev(fi->fi_name); 1098249916Sneel assert(pde != NULL); 1099268887Sjhb error = pci_emul_init(ctx, pde, bus, slot, 1100268887Sjhb func, fi); 1101252682Sgrehan if (error) 1102252682Sgrehan return (error); 1103221828Sgrehan } 1104221828Sgrehan } 1105268887Sjhb 1106268887Sjhb /* 1107268887Sjhb * Add some slop to the I/O and memory resources decoded by 1108268887Sjhb * this bus to give a guest some flexibility if it wants to 1109268887Sjhb * reprogram the BARs. 1110268887Sjhb */ 1111268887Sjhb pci_emul_iobase += BUSIO_ROUNDUP; 1112268887Sjhb pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1113268887Sjhb bi->iolimit = pci_emul_iobase; 1114268887Sjhb 1115268887Sjhb pci_emul_membase32 += BUSMEM_ROUNDUP; 1116268887Sjhb pci_emul_membase32 = roundup2(pci_emul_membase32, 1117268887Sjhb BUSMEM_ROUNDUP); 1118268887Sjhb bi->memlimit32 = pci_emul_membase32; 1119268887Sjhb 1120268887Sjhb pci_emul_membase64 += BUSMEM_ROUNDUP; 1121268887Sjhb pci_emul_membase64 = roundup2(pci_emul_membase64, 1122268887Sjhb BUSMEM_ROUNDUP); 1123268887Sjhb bi->memlimit64 = pci_emul_membase64; 1124221828Sgrehan } 1125234938Sgrehan 1126234938Sgrehan /* 1127268972Sjhb * PCI backends are initialized before routing INTx interrupts 1128268972Sjhb * so that LPC devices are able to reserve ISA IRQs before 1129268972Sjhb * routing PIRQ pins. 1130268972Sjhb */ 1131268972Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1132268972Sjhb if ((bi = pci_businfo[bus]) == NULL) 1133268972Sjhb continue; 1134268972Sjhb 1135268972Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1136268972Sjhb si = &bi->slotinfo[slot]; 1137268972Sjhb for (func = 0; func < MAXFUNCS; func++) { 1138268972Sjhb fi = &si->si_funcs[func]; 1139268972Sjhb if (fi->fi_devi == NULL) 1140268972Sjhb continue; 1141268972Sjhb pci_lintr_route(fi->fi_devi); 1142268972Sjhb } 1143268972Sjhb } 1144268972Sjhb } 1145268972Sjhb lpc_pirq_routed(); 1146268972Sjhb 1147268972Sjhb /* 1148249572Sneel * The guest physical memory map looks like the following: 1149249572Sneel * [0, lowmem) guest system memory 1150249572Sneel * [lowmem, lowmem_limit) memory hole (may be absent) 1151270159Sgrehan * [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) 1152270159Sgrehan * [0xE0000000, 0xF0000000) PCI extended config window 1153270159Sgrehan * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware 1154249572Sneel * [4GB, 4GB + highmem) 1155270159Sgrehan */ 1156270159Sgrehan 1157270159Sgrehan /* 1158249572Sneel * Accesses to memory addresses that are not allocated to system 1159249572Sneel * memory or PCI devices return 0xff's. 1160247144Sgrehan */ 1161270074Sgrehan lowmem = vm_get_lowmem_size(ctx); 1162270159Sgrehan bzero(&mr, sizeof(struct mem_range)); 1163270159Sgrehan mr.name = "PCI hole"; 1164270159Sgrehan mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1165270159Sgrehan mr.base = lowmem; 1166270159Sgrehan mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 1167270159Sgrehan mr.handler = pci_emul_fallback_handler; 1168270159Sgrehan error = register_mem_fallback(&mr); 1169270159Sgrehan assert(error == 0); 1170249572Sneel 1171270159Sgrehan /* PCI extended config space */ 1172270159Sgrehan bzero(&mr, sizeof(struct mem_range)); 1173270159Sgrehan mr.name = "PCI ECFG"; 1174270159Sgrehan mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1175270159Sgrehan mr.base = PCI_EMUL_ECFG_BASE; 1176270159Sgrehan mr.size = PCI_EMUL_ECFG_SIZE; 1177270159Sgrehan mr.handler = pci_emul_ecfg_handler; 1178270159Sgrehan error = register_mem(&mr); 1179247144Sgrehan assert(error == 0); 1180252682Sgrehan 1181252682Sgrehan return (0); 1182221828Sgrehan} 1183221828Sgrehan 1184267393Sjhbstatic void 1185268972Sjhbpci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1186268972Sjhb void *arg) 1187267393Sjhb{ 1188267393Sjhb 1189268972Sjhb dsdt_line(" Package ()"); 1190267393Sjhb dsdt_line(" {"); 1191267393Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1192267393Sjhb dsdt_line(" 0x%02X,", pin - 1); 1193267393Sjhb dsdt_line(" Zero,"); 1194267393Sjhb dsdt_line(" 0x%X", ioapic_irq); 1195268972Sjhb dsdt_line(" },"); 1196267393Sjhb} 1197267393Sjhb 1198268972Sjhbstatic void 1199268972Sjhbpci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1200268972Sjhb void *arg) 1201268972Sjhb{ 1202268972Sjhb char *name; 1203268972Sjhb 1204268972Sjhb name = lpc_pirq_name(pirq_pin); 1205268972Sjhb if (name == NULL) 1206268972Sjhb return; 1207268972Sjhb dsdt_line(" Package ()"); 1208268972Sjhb dsdt_line(" {"); 1209268972Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1210268972Sjhb dsdt_line(" 0x%02X,", pin - 1); 1211268972Sjhb dsdt_line(" %s,", name); 1212268972Sjhb dsdt_line(" 0x00"); 1213268972Sjhb dsdt_line(" },"); 1214268972Sjhb free(name); 1215268972Sjhb} 1216268972Sjhb 1217268887Sjhb/* 1218268887Sjhb * A bhyve virtual machine has a flat PCI hierarchy with a root port 1219268887Sjhb * corresponding to each PCI bus. 1220268887Sjhb */ 1221268887Sjhbstatic void 1222268887Sjhbpci_bus_write_dsdt(int bus) 1223261265Sjhb{ 1224268887Sjhb struct businfo *bi; 1225268887Sjhb struct slotinfo *si; 1226261265Sjhb struct pci_devinst *pi; 1227268972Sjhb int count, func, slot; 1228261265Sjhb 1229268887Sjhb /* 1230268887Sjhb * If there are no devices on this 'bus' then just return. 1231268887Sjhb */ 1232268887Sjhb if ((bi = pci_businfo[bus]) == NULL) { 1233268887Sjhb /* 1234268887Sjhb * Bus 0 is special because it decodes the I/O ports used 1235268887Sjhb * for PCI config space access even if there are no devices 1236268887Sjhb * on it. 1237268887Sjhb */ 1238268887Sjhb if (bus != 0) 1239268887Sjhb return; 1240268887Sjhb } 1241268887Sjhb 1242268887Sjhb dsdt_line(" Device (PC%02X)", bus); 1243261265Sjhb dsdt_line(" {"); 1244261265Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1245261265Sjhb dsdt_line(" Name (_ADR, Zero)"); 1246268887Sjhb 1247268887Sjhb dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1248268887Sjhb dsdt_line(" {"); 1249268887Sjhb dsdt_line(" Return (0x%08X)", bus); 1250268887Sjhb dsdt_line(" }"); 1251261265Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1252261265Sjhb dsdt_line(" {"); 1253261265Sjhb dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1254261265Sjhb "MaxFixed, PosDecode,"); 1255261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1256268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bus); 1257268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", bus); 1258261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1259268887Sjhb dsdt_line(" 0x0001, // Length"); 1260261265Sjhb dsdt_line(" ,, )"); 1261268887Sjhb 1262268887Sjhb if (bus == 0) { 1263268887Sjhb dsdt_indent(3); 1264268887Sjhb dsdt_fixed_ioport(0xCF8, 8); 1265268887Sjhb dsdt_unindent(3); 1266268887Sjhb 1267268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1268268887Sjhb "PosDecode, EntireRange,"); 1269268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1270268887Sjhb dsdt_line(" 0x0000, // Range Minimum"); 1271268887Sjhb dsdt_line(" 0x0CF7, // Range Maximum"); 1272268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1273268887Sjhb dsdt_line(" 0x0CF8, // Length"); 1274268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1275268887Sjhb 1276268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1277268887Sjhb "PosDecode, EntireRange,"); 1278268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1279268887Sjhb dsdt_line(" 0x0D00, // Range Minimum"); 1280268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1281268887Sjhb PCI_EMUL_IOBASE - 1); 1282268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1283268887Sjhb dsdt_line(" 0x%04X, // Length", 1284268887Sjhb PCI_EMUL_IOBASE - 0x0D00); 1285268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1286268887Sjhb 1287268887Sjhb if (bi == NULL) { 1288268887Sjhb dsdt_line(" })"); 1289268887Sjhb goto done; 1290268887Sjhb } 1291268887Sjhb } 1292268887Sjhb assert(bi != NULL); 1293268887Sjhb 1294268887Sjhb /* i/o window */ 1295261265Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1296261265Sjhb "PosDecode, EntireRange,"); 1297261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1298268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1299268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1300268887Sjhb bi->iolimit - 1); 1301261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1302268887Sjhb dsdt_line(" 0x%04X, // Length", 1303268887Sjhb bi->iolimit - bi->iobase); 1304261265Sjhb dsdt_line(" ,, , TypeStatic)"); 1305268887Sjhb 1306268887Sjhb /* mmio window (32-bit) */ 1307261265Sjhb dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1308261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1309261265Sjhb dsdt_line(" 0x00000000, // Granularity"); 1310268887Sjhb dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1311261265Sjhb dsdt_line(" 0x%08X, // Range Maximum\n", 1312268887Sjhb bi->memlimit32 - 1); 1313261265Sjhb dsdt_line(" 0x00000000, // Translation Offset"); 1314268887Sjhb dsdt_line(" 0x%08X, // Length\n", 1315268887Sjhb bi->memlimit32 - bi->membase32); 1316261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1317268887Sjhb 1318268887Sjhb /* mmio window (64-bit) */ 1319261265Sjhb dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1320261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1321261265Sjhb dsdt_line(" 0x0000000000000000, // Granularity"); 1322268887Sjhb dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1323261265Sjhb dsdt_line(" 0x%016lX, // Range Maximum\n", 1324268887Sjhb bi->memlimit64 - 1); 1325261265Sjhb dsdt_line(" 0x0000000000000000, // Translation Offset"); 1326261265Sjhb dsdt_line(" 0x%016lX, // Length\n", 1327268887Sjhb bi->memlimit64 - bi->membase64); 1328261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1329261265Sjhb dsdt_line(" })"); 1330268887Sjhb 1331268887Sjhb count = pci_count_lintr(bus); 1332267393Sjhb if (count != 0) { 1333267393Sjhb dsdt_indent(2); 1334268972Sjhb dsdt_line("Name (PPRT, Package ()"); 1335267393Sjhb dsdt_line("{"); 1336268972Sjhb pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 1337268972Sjhb dsdt_line("})"); 1338268972Sjhb dsdt_line("Name (APRT, Package ()"); 1339268972Sjhb dsdt_line("{"); 1340268972Sjhb pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1341268972Sjhb dsdt_line("})"); 1342268972Sjhb dsdt_line("Method (_PRT, 0, NotSerialized)"); 1343268972Sjhb dsdt_line("{"); 1344268972Sjhb dsdt_line(" If (PICM)"); 1345268972Sjhb dsdt_line(" {"); 1346268972Sjhb dsdt_line(" Return (APRT)"); 1347268972Sjhb dsdt_line(" }"); 1348268972Sjhb dsdt_line(" Else"); 1349268972Sjhb dsdt_line(" {"); 1350268972Sjhb dsdt_line(" Return (PPRT)"); 1351268972Sjhb dsdt_line(" }"); 1352268972Sjhb dsdt_line("}"); 1353267393Sjhb dsdt_unindent(2); 1354267393Sjhb } 1355261265Sjhb 1356261265Sjhb dsdt_indent(2); 1357261265Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1358268887Sjhb si = &bi->slotinfo[slot]; 1359261265Sjhb for (func = 0; func < MAXFUNCS; func++) { 1360268887Sjhb pi = si->si_funcs[func].fi_devi; 1361261265Sjhb if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1362261265Sjhb pi->pi_d->pe_write_dsdt(pi); 1363261265Sjhb } 1364261265Sjhb } 1365261265Sjhb dsdt_unindent(2); 1366268887Sjhbdone: 1367261265Sjhb dsdt_line(" }"); 1368261265Sjhb} 1369261265Sjhb 1370268887Sjhbvoid 1371268887Sjhbpci_write_dsdt(void) 1372268887Sjhb{ 1373268887Sjhb int bus; 1374268887Sjhb 1375268972Sjhb dsdt_indent(1); 1376268972Sjhb dsdt_line("Name (PICM, 0x00)"); 1377268972Sjhb dsdt_line("Method (_PIC, 1, NotSerialized)"); 1378268972Sjhb dsdt_line("{"); 1379268972Sjhb dsdt_line(" Store (Arg0, PICM)"); 1380268972Sjhb dsdt_line("}"); 1381268972Sjhb dsdt_line(""); 1382268972Sjhb dsdt_line("Scope (_SB)"); 1383268972Sjhb dsdt_line("{"); 1384268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) 1385268887Sjhb pci_bus_write_dsdt(bus); 1386268972Sjhb dsdt_line("}"); 1387268972Sjhb dsdt_unindent(1); 1388268887Sjhb} 1389268887Sjhb 1390221828Sgrehanint 1391268887Sjhbpci_bus_configured(int bus) 1392268887Sjhb{ 1393268887Sjhb assert(bus >= 0 && bus < MAXBUSES); 1394268887Sjhb return (pci_businfo[bus] != NULL); 1395268887Sjhb} 1396268887Sjhb 1397268887Sjhbint 1398221828Sgrehanpci_msi_enabled(struct pci_devinst *pi) 1399221828Sgrehan{ 1400221828Sgrehan return (pi->pi_msi.enabled); 1401221828Sgrehan} 1402221828Sgrehan 1403221828Sgrehanint 1404262350Sjhbpci_msi_maxmsgnum(struct pci_devinst *pi) 1405221828Sgrehan{ 1406221828Sgrehan if (pi->pi_msi.enabled) 1407262350Sjhb return (pi->pi_msi.maxmsgnum); 1408221828Sgrehan else 1409221828Sgrehan return (0); 1410221828Sgrehan} 1411221828Sgrehan 1412246109Sneelint 1413246109Sneelpci_msix_enabled(struct pci_devinst *pi) 1414246109Sneel{ 1415246109Sneel 1416246109Sneel return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1417246109Sneel} 1418246109Sneel 1419221828Sgrehanvoid 1420246109Sneelpci_generate_msix(struct pci_devinst *pi, int index) 1421246109Sneel{ 1422246109Sneel struct msix_table_entry *mte; 1423246109Sneel 1424246109Sneel if (!pci_msix_enabled(pi)) 1425246109Sneel return; 1426246109Sneel 1427246109Sneel if (pi->pi_msix.function_mask) 1428246109Sneel return; 1429246109Sneel 1430246109Sneel if (index >= pi->pi_msix.table_count) 1431246109Sneel return; 1432246109Sneel 1433246109Sneel mte = &pi->pi_msix.table[index]; 1434246109Sneel if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1435246109Sneel /* XXX Set PBA bit if interrupt is disabled */ 1436262350Sjhb vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1437246109Sneel } 1438246109Sneel} 1439246109Sneel 1440246109Sneelvoid 1441262350Sjhbpci_generate_msi(struct pci_devinst *pi, int index) 1442221828Sgrehan{ 1443221828Sgrehan 1444262350Sjhb if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 1445262350Sjhb vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 1446262350Sjhb pi->pi_msi.msg_data + index); 1447221828Sgrehan } 1448221828Sgrehan} 1449221828Sgrehan 1450267393Sjhbstatic bool 1451267393Sjhbpci_lintr_permitted(struct pci_devinst *pi) 1452267393Sjhb{ 1453267393Sjhb uint16_t cmd; 1454267393Sjhb 1455267393Sjhb cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 1456267393Sjhb return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 1457267393Sjhb (cmd & PCIM_CMD_INTxDIS))); 1458267393Sjhb} 1459267393Sjhb 1460268972Sjhbvoid 1461267393Sjhbpci_lintr_request(struct pci_devinst *pi) 1462234938Sgrehan{ 1463268887Sjhb struct businfo *bi; 1464267393Sjhb struct slotinfo *si; 1465268972Sjhb int bestpin, bestcount, pin; 1466234938Sgrehan 1467268887Sjhb bi = pci_businfo[pi->pi_bus]; 1468268887Sjhb assert(bi != NULL); 1469268887Sjhb 1470267393Sjhb /* 1471268972Sjhb * Just allocate a pin from our slot. The pin will be 1472268972Sjhb * assigned IRQs later when interrupts are routed. 1473267393Sjhb */ 1474268887Sjhb si = &bi->slotinfo[pi->pi_slot]; 1475267393Sjhb bestpin = 0; 1476267393Sjhb bestcount = si->si_intpins[0].ii_count; 1477267393Sjhb for (pin = 1; pin < 4; pin++) { 1478267393Sjhb if (si->si_intpins[pin].ii_count < bestcount) { 1479267393Sjhb bestpin = pin; 1480267393Sjhb bestcount = si->si_intpins[pin].ii_count; 1481267393Sjhb } 1482267393Sjhb } 1483234938Sgrehan 1484267393Sjhb si->si_intpins[bestpin].ii_count++; 1485267393Sjhb pi->pi_lintr.pin = bestpin + 1; 1486267393Sjhb pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1487234938Sgrehan} 1488234938Sgrehan 1489268972Sjhbstatic void 1490268972Sjhbpci_lintr_route(struct pci_devinst *pi) 1491268972Sjhb{ 1492268972Sjhb struct businfo *bi; 1493268972Sjhb struct intxinfo *ii; 1494268972Sjhb 1495268972Sjhb if (pi->pi_lintr.pin == 0) 1496268972Sjhb return; 1497268972Sjhb 1498268972Sjhb bi = pci_businfo[pi->pi_bus]; 1499268972Sjhb assert(bi != NULL); 1500268972Sjhb ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1501268972Sjhb 1502268972Sjhb /* 1503268972Sjhb * Attempt to allocate an I/O APIC pin for this intpin if one 1504268972Sjhb * is not yet assigned. 1505268972Sjhb */ 1506268972Sjhb if (ii->ii_ioapic_irq == 0) 1507268972Sjhb ii->ii_ioapic_irq = ioapic_pci_alloc_irq(); 1508268972Sjhb assert(ii->ii_ioapic_irq > 0); 1509268972Sjhb 1510268972Sjhb /* 1511268972Sjhb * Attempt to allocate a PIRQ pin for this intpin if one is 1512268972Sjhb * not yet assigned. 1513268972Sjhb */ 1514268972Sjhb if (ii->ii_pirq_pin == 0) 1515268972Sjhb ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx); 1516268972Sjhb assert(ii->ii_pirq_pin > 0); 1517268972Sjhb 1518268972Sjhb pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1519268972Sjhb pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1520268972Sjhb pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 1521268972Sjhb} 1522268972Sjhb 1523234938Sgrehanvoid 1524234938Sgrehanpci_lintr_assert(struct pci_devinst *pi) 1525234938Sgrehan{ 1526234938Sgrehan 1527267393Sjhb assert(pi->pi_lintr.pin > 0); 1528261088Sjhb 1529267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1530267393Sjhb if (pi->pi_lintr.state == IDLE) { 1531267393Sjhb if (pci_lintr_permitted(pi)) { 1532267393Sjhb pi->pi_lintr.state = ASSERTED; 1533268972Sjhb pci_irq_assert(pi); 1534267393Sjhb } else 1535267393Sjhb pi->pi_lintr.state = PENDING; 1536261088Sjhb } 1537267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1538234938Sgrehan} 1539234938Sgrehan 1540234938Sgrehanvoid 1541234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi) 1542234938Sgrehan{ 1543234938Sgrehan 1544267393Sjhb assert(pi->pi_lintr.pin > 0); 1545261088Sjhb 1546267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1547267393Sjhb if (pi->pi_lintr.state == ASSERTED) { 1548267393Sjhb pi->pi_lintr.state = IDLE; 1549268972Sjhb pci_irq_deassert(pi); 1550267393Sjhb } else if (pi->pi_lintr.state == PENDING) 1551267393Sjhb pi->pi_lintr.state = IDLE; 1552267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1553267393Sjhb} 1554267393Sjhb 1555267393Sjhbstatic void 1556267393Sjhbpci_lintr_update(struct pci_devinst *pi) 1557267393Sjhb{ 1558267393Sjhb 1559267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1560267393Sjhb if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1561268972Sjhb pci_irq_deassert(pi); 1562267393Sjhb pi->pi_lintr.state = PENDING; 1563267393Sjhb } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 1564267393Sjhb pi->pi_lintr.state = ASSERTED; 1565268972Sjhb pci_irq_assert(pi); 1566261088Sjhb } 1567267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1568234938Sgrehan} 1569234938Sgrehan 1570267393Sjhbint 1571268887Sjhbpci_count_lintr(int bus) 1572267393Sjhb{ 1573267393Sjhb int count, slot, pin; 1574268887Sjhb struct slotinfo *slotinfo; 1575267393Sjhb 1576267393Sjhb count = 0; 1577268887Sjhb if (pci_businfo[bus] != NULL) { 1578268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1579268887Sjhb slotinfo = &pci_businfo[bus]->slotinfo[slot]; 1580268887Sjhb for (pin = 0; pin < 4; pin++) { 1581268887Sjhb if (slotinfo->si_intpins[pin].ii_count != 0) 1582268887Sjhb count++; 1583268887Sjhb } 1584267393Sjhb } 1585267393Sjhb } 1586267393Sjhb return (count); 1587267393Sjhb} 1588267393Sjhb 1589267393Sjhbvoid 1590268887Sjhbpci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 1591267393Sjhb{ 1592268887Sjhb struct businfo *bi; 1593268887Sjhb struct slotinfo *si; 1594267393Sjhb struct intxinfo *ii; 1595267393Sjhb int slot, pin; 1596267393Sjhb 1597268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1598268887Sjhb return; 1599268887Sjhb 1600267393Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1601268887Sjhb si = &bi->slotinfo[slot]; 1602267393Sjhb for (pin = 0; pin < 4; pin++) { 1603268887Sjhb ii = &si->si_intpins[pin]; 1604267393Sjhb if (ii->ii_count != 0) 1605268972Sjhb cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1606268972Sjhb ii->ii_ioapic_irq, arg); 1607267393Sjhb } 1608267393Sjhb } 1609267393Sjhb} 1610267393Sjhb 1611239085Sneel/* 1612239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device. 1613239085Sneel * Return 0 otherwise. 1614239085Sneel */ 1615239085Sneelstatic int 1616268887Sjhbpci_emul_is_mfdev(int bus, int slot) 1617239085Sneel{ 1618268887Sjhb struct businfo *bi; 1619268887Sjhb struct slotinfo *si; 1620239085Sneel int f, numfuncs; 1621234938Sgrehan 1622239085Sneel numfuncs = 0; 1623268887Sjhb if ((bi = pci_businfo[bus]) != NULL) { 1624268887Sjhb si = &bi->slotinfo[slot]; 1625268887Sjhb for (f = 0; f < MAXFUNCS; f++) { 1626268887Sjhb if (si->si_funcs[f].fi_devi != NULL) { 1627268887Sjhb numfuncs++; 1628268887Sjhb } 1629239085Sneel } 1630239085Sneel } 1631239085Sneel return (numfuncs > 1); 1632239085Sneel} 1633234938Sgrehan 1634239085Sneel/* 1635239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 1636239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'. 1637239085Sneel */ 1638239085Sneelstatic void 1639268887Sjhbpci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) 1640239085Sneel{ 1641239085Sneel int mfdev; 1642239085Sneel 1643239085Sneel if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 1644268887Sjhb mfdev = pci_emul_is_mfdev(bus, slot); 1645239085Sneel switch (bytes) { 1646239085Sneel case 1: 1647239085Sneel case 2: 1648239085Sneel *rv &= ~PCIM_MFDEV; 1649239085Sneel if (mfdev) { 1650239085Sneel *rv |= PCIM_MFDEV; 1651239085Sneel } 1652239085Sneel break; 1653239085Sneel case 4: 1654239085Sneel *rv &= ~(PCIM_MFDEV << 16); 1655239085Sneel if (mfdev) { 1656239085Sneel *rv |= (PCIM_MFDEV << 16); 1657239085Sneel } 1658239085Sneel break; 1659239085Sneel } 1660239085Sneel } 1661239085Sneel} 1662239085Sneel 1663284899Sneelstatic void 1664284899Sneelpci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) 1665249321Sneel{ 1666284899Sneel int i, rshift; 1667284899Sneel uint32_t cmd, cmd2, changed, old, readonly; 1668249321Sneel 1669284899Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 1670249321Sneel 1671249321Sneel /* 1672284899Sneel * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. 1673284899Sneel * 1674284899Sneel * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are 1675284899Sneel * 'write 1 to clear'. However these bits are not set to '1' by 1676284899Sneel * any device emulation so it is simpler to treat them as readonly. 1677249321Sneel */ 1678284899Sneel rshift = (coff & 0x3) * 8; 1679284899Sneel readonly = 0xFFFFF880 >> rshift; 1680249321Sneel 1681284899Sneel old = CFGREAD(pi, coff, bytes); 1682284899Sneel new &= ~readonly; 1683284899Sneel new |= (old & readonly); 1684284899Sneel CFGWRITE(pi, coff, new, bytes); /* update config */ 1685249321Sneel 1686284899Sneel cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ 1687284899Sneel changed = cmd ^ cmd2; 1688284899Sneel 1689249321Sneel /* 1690249321Sneel * If the MMIO or I/O address space decoding has changed then 1691249321Sneel * register/unregister all BARs that decode that address space. 1692249321Sneel */ 1693259301Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 1694249321Sneel switch (pi->pi_bar[i].type) { 1695249321Sneel case PCIBAR_NONE: 1696249321Sneel case PCIBAR_MEMHI64: 1697249321Sneel break; 1698249321Sneel case PCIBAR_IO: 1699249321Sneel /* I/O address space decoding changed? */ 1700284899Sneel if (changed & PCIM_CMD_PORTEN) { 1701249321Sneel if (porten(pi)) 1702249321Sneel register_bar(pi, i); 1703249321Sneel else 1704249321Sneel unregister_bar(pi, i); 1705249321Sneel } 1706249321Sneel break; 1707249321Sneel case PCIBAR_MEM32: 1708249321Sneel case PCIBAR_MEM64: 1709249321Sneel /* MMIO address space decoding changed? */ 1710284899Sneel if (changed & PCIM_CMD_MEMEN) { 1711249321Sneel if (memen(pi)) 1712249321Sneel register_bar(pi, i); 1713249321Sneel else 1714249321Sneel unregister_bar(pi, i); 1715249321Sneel } 1716249321Sneel break; 1717249321Sneel default: 1718249321Sneel assert(0); 1719249321Sneel } 1720249321Sneel } 1721267393Sjhb 1722267393Sjhb /* 1723267393Sjhb * If INTx has been unmasked and is pending, assert the 1724267393Sjhb * interrupt. 1725267393Sjhb */ 1726267393Sjhb pci_lintr_update(pi); 1727249321Sneel} 1728249321Sneel 1729270159Sgrehanstatic void 1730270159Sgrehanpci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, 1731270159Sgrehan int coff, int bytes, uint32_t *eax) 1732221828Sgrehan{ 1733268887Sjhb struct businfo *bi; 1734268887Sjhb struct slotinfo *si; 1735221828Sgrehan struct pci_devinst *pi; 1736221828Sgrehan struct pci_devemu *pe; 1737270159Sgrehan int idx, needcfg; 1738249321Sneel uint64_t addr, bar, mask; 1739221828Sgrehan 1740270159Sgrehan if ((bi = pci_businfo[bus]) != NULL) { 1741270159Sgrehan si = &bi->slotinfo[slot]; 1742270159Sgrehan pi = si->si_funcs[func].fi_devi; 1743268887Sjhb } else 1744242170Sneel pi = NULL; 1745242170Sneel 1746239085Sneel /* 1747270159Sgrehan * Just return if there is no device at this slot:func or if the 1748270159Sgrehan * the guest is doing an un-aligned access. 1749239085Sneel */ 1750270159Sgrehan if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || 1751270159Sgrehan (coff & (bytes - 1)) != 0) { 1752221828Sgrehan if (in) 1753221828Sgrehan *eax = 0xffffffff; 1754270159Sgrehan return; 1755221828Sgrehan } 1756221828Sgrehan 1757270159Sgrehan /* 1758270159Sgrehan * Ignore all writes beyond the standard config space and return all 1759270159Sgrehan * ones on reads. 1760270159Sgrehan */ 1761270159Sgrehan if (coff >= PCI_REGMAX + 1) { 1762270159Sgrehan if (in) { 1763270159Sgrehan *eax = 0xffffffff; 1764270159Sgrehan /* 1765270159Sgrehan * Extended capabilities begin at offset 256 in config 1766270159Sgrehan * space. Absence of extended capabilities is signaled 1767270159Sgrehan * with all 0s in the extended capability header at 1768270159Sgrehan * offset 256. 1769270159Sgrehan */ 1770270159Sgrehan if (coff <= PCI_REGMAX + 4) 1771270159Sgrehan *eax = 0x00000000; 1772270159Sgrehan } 1773270159Sgrehan return; 1774270159Sgrehan } 1775270159Sgrehan 1776221828Sgrehan pe = pi->pi_d; 1777221828Sgrehan 1778221828Sgrehan /* 1779221828Sgrehan * Config read 1780221828Sgrehan */ 1781221828Sgrehan if (in) { 1782221828Sgrehan /* Let the device emulation override the default handler */ 1783239085Sneel if (pe->pe_cfgread != NULL) { 1784270159Sgrehan needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, 1785270159Sgrehan eax); 1786239085Sneel } else { 1787239085Sneel needcfg = 1; 1788239085Sneel } 1789221828Sgrehan 1790284899Sneel if (needcfg) 1791284899Sneel *eax = CFGREAD(pi, coff, bytes); 1792239085Sneel 1793270159Sgrehan pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); 1794221828Sgrehan } else { 1795221828Sgrehan /* Let the device emulation override the default handler */ 1796221828Sgrehan if (pe->pe_cfgwrite != NULL && 1797221828Sgrehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 1798270159Sgrehan return; 1799221828Sgrehan 1800221828Sgrehan /* 1801221828Sgrehan * Special handling for write to BAR registers 1802221828Sgrehan */ 1803221828Sgrehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { 1804221828Sgrehan /* 1805221828Sgrehan * Ignore writes to BAR registers that are not 1806221828Sgrehan * 4-byte aligned. 1807221828Sgrehan */ 1808221828Sgrehan if (bytes != 4 || (coff & 0x3) != 0) 1809270159Sgrehan return; 1810221828Sgrehan idx = (coff - PCIR_BAR(0)) / 4; 1811249321Sneel mask = ~(pi->pi_bar[idx].size - 1); 1812221828Sgrehan switch (pi->pi_bar[idx].type) { 1813221828Sgrehan case PCIBAR_NONE: 1814249321Sneel pi->pi_bar[idx].addr = bar = 0; 1815221828Sgrehan break; 1816221828Sgrehan case PCIBAR_IO: 1817249321Sneel addr = *eax & mask; 1818249321Sneel addr &= 0xffff; 1819249321Sneel bar = addr | PCIM_BAR_IO_SPACE; 1820249321Sneel /* 1821249321Sneel * Register the new BAR value for interception 1822249321Sneel */ 1823249321Sneel if (addr != pi->pi_bar[idx].addr) { 1824249321Sneel update_bar_address(pi, addr, idx, 1825249321Sneel PCIBAR_IO); 1826249321Sneel } 1827221828Sgrehan break; 1828221828Sgrehan case PCIBAR_MEM32: 1829249321Sneel addr = bar = *eax & mask; 1830221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 1831249321Sneel if (addr != pi->pi_bar[idx].addr) { 1832249321Sneel update_bar_address(pi, addr, idx, 1833249321Sneel PCIBAR_MEM32); 1834249321Sneel } 1835221828Sgrehan break; 1836221828Sgrehan case PCIBAR_MEM64: 1837249321Sneel addr = bar = *eax & mask; 1838221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 1839221828Sgrehan PCIM_BAR_MEM_PREFETCH; 1840249321Sneel if (addr != (uint32_t)pi->pi_bar[idx].addr) { 1841249321Sneel update_bar_address(pi, addr, idx, 1842249321Sneel PCIBAR_MEM64); 1843249321Sneel } 1844221828Sgrehan break; 1845221828Sgrehan case PCIBAR_MEMHI64: 1846221828Sgrehan mask = ~(pi->pi_bar[idx - 1].size - 1); 1847249321Sneel addr = ((uint64_t)*eax << 32) & mask; 1848249321Sneel bar = addr >> 32; 1849249321Sneel if (bar != pi->pi_bar[idx - 1].addr >> 32) { 1850249321Sneel update_bar_address(pi, addr, idx - 1, 1851249321Sneel PCIBAR_MEMHI64); 1852249321Sneel } 1853221828Sgrehan break; 1854221828Sgrehan default: 1855221828Sgrehan assert(0); 1856221828Sgrehan } 1857221828Sgrehan pci_set_cfgdata32(pi, coff, bar); 1858234761Sgrehan 1859221828Sgrehan } else if (pci_emul_iscap(pi, coff)) { 1860221828Sgrehan pci_emul_capwrite(pi, coff, bytes, *eax); 1861284899Sneel } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { 1862284899Sneel pci_emul_cmdsts_write(pi, coff, *eax, bytes); 1863221828Sgrehan } else { 1864221828Sgrehan CFGWRITE(pi, coff, *eax, bytes); 1865221828Sgrehan } 1866221828Sgrehan } 1867270159Sgrehan} 1868221828Sgrehan 1869270159Sgrehanstatic int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 1870270159Sgrehan 1871270159Sgrehanstatic int 1872270159Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1873270159Sgrehan uint32_t *eax, void *arg) 1874270159Sgrehan{ 1875270159Sgrehan uint32_t x; 1876270159Sgrehan 1877270159Sgrehan if (bytes != 4) { 1878270159Sgrehan if (in) 1879270159Sgrehan *eax = (bytes == 2) ? 0xffff : 0xff; 1880270159Sgrehan return (0); 1881270159Sgrehan } 1882270159Sgrehan 1883270159Sgrehan if (in) { 1884270159Sgrehan x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; 1885270159Sgrehan if (cfgenable) 1886270159Sgrehan x |= CONF1_ENABLE; 1887270159Sgrehan *eax = x; 1888270159Sgrehan } else { 1889270159Sgrehan x = *eax; 1890270159Sgrehan cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 1891270159Sgrehan cfgoff = x & PCI_REGMAX; 1892270159Sgrehan cfgfunc = (x >> 8) & PCI_FUNCMAX; 1893270159Sgrehan cfgslot = (x >> 11) & PCI_SLOTMAX; 1894270159Sgrehan cfgbus = (x >> 16) & PCI_BUSMAX; 1895270159Sgrehan } 1896270159Sgrehan 1897221828Sgrehan return (0); 1898221828Sgrehan} 1899270159SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 1900221828Sgrehan 1901270159Sgrehanstatic int 1902270159Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1903270159Sgrehan uint32_t *eax, void *arg) 1904270159Sgrehan{ 1905270159Sgrehan int coff; 1906270159Sgrehan 1907270159Sgrehan assert(bytes == 1 || bytes == 2 || bytes == 4); 1908270159Sgrehan 1909270159Sgrehan coff = cfgoff + (port - CONF1_DATA_PORT); 1910270159Sgrehan if (cfgenable) { 1911270159Sgrehan pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, 1912270159Sgrehan eax); 1913270159Sgrehan } else { 1914270159Sgrehan /* Ignore accesses to cfgdata if not enabled by cfgaddr */ 1915270159Sgrehan if (in) 1916270159Sgrehan *eax = 0xffffffff; 1917270159Sgrehan } 1918270159Sgrehan return (0); 1919270159Sgrehan} 1920270159Sgrehan 1921221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 1922221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 1923221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 1924221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 1925221828Sgrehan 1926221828Sgrehan#define PCI_EMUL_TEST 1927221828Sgrehan#ifdef PCI_EMUL_TEST 1928221828Sgrehan/* 1929221828Sgrehan * Define a dummy test device 1930221828Sgrehan */ 1931268887Sjhb#define DIOSZ 8 1932241744Sgrehan#define DMEMSZ 4096 1933221828Sgrehanstruct pci_emul_dsoftc { 1934241744Sgrehan uint8_t ioregs[DIOSZ]; 1935284900Sneel uint8_t memregs[2][DMEMSZ]; 1936221828Sgrehan}; 1937221828Sgrehan 1938241744Sgrehan#define PCI_EMUL_MSI_MSGS 4 1939241744Sgrehan#define PCI_EMUL_MSIX_MSGS 16 1940221828Sgrehan 1941221942Sjhbstatic int 1942221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 1943221828Sgrehan{ 1944221828Sgrehan int error; 1945221828Sgrehan struct pci_emul_dsoftc *sc; 1946221828Sgrehan 1947268953Sjhb sc = calloc(1, sizeof(struct pci_emul_dsoftc)); 1948221828Sgrehan 1949221828Sgrehan pi->pi_arg = sc; 1950221828Sgrehan 1951221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 1952221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 1953221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 1954221828Sgrehan 1955241744Sgrehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 1956221828Sgrehan assert(error == 0); 1957221828Sgrehan 1958241744Sgrehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 1959221828Sgrehan assert(error == 0); 1960221828Sgrehan 1961241744Sgrehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 1962241744Sgrehan assert(error == 0); 1963241744Sgrehan 1964284900Sneel error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); 1965284900Sneel assert(error == 0); 1966284900Sneel 1967221828Sgrehan return (0); 1968221828Sgrehan} 1969221828Sgrehan 1970221942Sjhbstatic void 1971241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 1972241744Sgrehan uint64_t offset, int size, uint64_t value) 1973221828Sgrehan{ 1974221828Sgrehan int i; 1975221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1976221828Sgrehan 1977241744Sgrehan if (baridx == 0) { 1978241744Sgrehan if (offset + size > DIOSZ) { 1979241744Sgrehan printf("diow: iow too large, offset %ld size %d\n", 1980241744Sgrehan offset, size); 1981241744Sgrehan return; 1982241744Sgrehan } 1983221828Sgrehan 1984241744Sgrehan if (size == 1) { 1985241744Sgrehan sc->ioregs[offset] = value & 0xff; 1986241744Sgrehan } else if (size == 2) { 1987241744Sgrehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 1988241744Sgrehan } else if (size == 4) { 1989241744Sgrehan *(uint32_t *)&sc->ioregs[offset] = value; 1990241744Sgrehan } else { 1991241744Sgrehan printf("diow: iow unknown size %d\n", size); 1992241744Sgrehan } 1993241744Sgrehan 1994241744Sgrehan /* 1995241744Sgrehan * Special magic value to generate an interrupt 1996241744Sgrehan */ 1997241744Sgrehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 1998262350Sjhb pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 1999241744Sgrehan 2000241744Sgrehan if (value == 0xabcdef) { 2001262350Sjhb for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 2002241744Sgrehan pci_generate_msi(pi, i); 2003241744Sgrehan } 2004221828Sgrehan } 2005221828Sgrehan 2006284900Sneel if (baridx == 1 || baridx == 2) { 2007241744Sgrehan if (offset + size > DMEMSZ) { 2008241744Sgrehan printf("diow: memw too large, offset %ld size %d\n", 2009241744Sgrehan offset, size); 2010241744Sgrehan return; 2011241744Sgrehan } 2012221828Sgrehan 2013284900Sneel i = baridx - 1; /* 'memregs' index */ 2014284900Sneel 2015241744Sgrehan if (size == 1) { 2016284900Sneel sc->memregs[i][offset] = value; 2017241744Sgrehan } else if (size == 2) { 2018284900Sneel *(uint16_t *)&sc->memregs[i][offset] = value; 2019241744Sgrehan } else if (size == 4) { 2020284900Sneel *(uint32_t *)&sc->memregs[i][offset] = value; 2021241744Sgrehan } else if (size == 8) { 2022284900Sneel *(uint64_t *)&sc->memregs[i][offset] = value; 2023241744Sgrehan } else { 2024241744Sgrehan printf("diow: memw unknown size %d\n", size); 2025241744Sgrehan } 2026241744Sgrehan 2027241744Sgrehan /* 2028241744Sgrehan * magic interrupt ?? 2029241744Sgrehan */ 2030221828Sgrehan } 2031241744Sgrehan 2032284900Sneel if (baridx > 2) { 2033241744Sgrehan printf("diow: unknown bar idx %d\n", baridx); 2034241744Sgrehan } 2035221828Sgrehan} 2036221828Sgrehan 2037241744Sgrehanstatic uint64_t 2038241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 2039241744Sgrehan uint64_t offset, int size) 2040221828Sgrehan{ 2041221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2042221828Sgrehan uint32_t value; 2043284900Sneel int i; 2044221828Sgrehan 2045241744Sgrehan if (baridx == 0) { 2046241744Sgrehan if (offset + size > DIOSZ) { 2047241744Sgrehan printf("dior: ior too large, offset %ld size %d\n", 2048241744Sgrehan offset, size); 2049241744Sgrehan return (0); 2050241744Sgrehan } 2051241744Sgrehan 2052241744Sgrehan if (size == 1) { 2053241744Sgrehan value = sc->ioregs[offset]; 2054241744Sgrehan } else if (size == 2) { 2055241744Sgrehan value = *(uint16_t *) &sc->ioregs[offset]; 2056241744Sgrehan } else if (size == 4) { 2057241744Sgrehan value = *(uint32_t *) &sc->ioregs[offset]; 2058241744Sgrehan } else { 2059241744Sgrehan printf("dior: ior unknown size %d\n", size); 2060241744Sgrehan } 2061221828Sgrehan } 2062284900Sneel 2063284900Sneel if (baridx == 1 || baridx == 2) { 2064241744Sgrehan if (offset + size > DMEMSZ) { 2065241744Sgrehan printf("dior: memr too large, offset %ld size %d\n", 2066241744Sgrehan offset, size); 2067241744Sgrehan return (0); 2068241744Sgrehan } 2069284900Sneel 2070284900Sneel i = baridx - 1; /* 'memregs' index */ 2071284900Sneel 2072241744Sgrehan if (size == 1) { 2073284900Sneel value = sc->memregs[i][offset]; 2074241744Sgrehan } else if (size == 2) { 2075284900Sneel value = *(uint16_t *) &sc->memregs[i][offset]; 2076241744Sgrehan } else if (size == 4) { 2077284900Sneel value = *(uint32_t *) &sc->memregs[i][offset]; 2078241744Sgrehan } else if (size == 8) { 2079284900Sneel value = *(uint64_t *) &sc->memregs[i][offset]; 2080241744Sgrehan } else { 2081241744Sgrehan printf("dior: ior unknown size %d\n", size); 2082241744Sgrehan } 2083221828Sgrehan } 2084221828Sgrehan 2085241744Sgrehan 2086284900Sneel if (baridx > 2) { 2087241744Sgrehan printf("dior: unknown bar idx %d\n", baridx); 2088241744Sgrehan return (0); 2089241744Sgrehan } 2090241744Sgrehan 2091221828Sgrehan return (value); 2092221828Sgrehan} 2093221828Sgrehan 2094221828Sgrehanstruct pci_devemu pci_dummy = { 2095221828Sgrehan .pe_emu = "dummy", 2096221828Sgrehan .pe_init = pci_emul_dinit, 2097241744Sgrehan .pe_barwrite = pci_emul_diow, 2098241744Sgrehan .pe_barread = pci_emul_dior 2099221828Sgrehan}; 2100221828SgrehanPCI_EMUL_SET(pci_dummy); 2101221828Sgrehan 2102221828Sgrehan#endif /* PCI_EMUL_TEST */ 2103