1366f6083SPeter Grehan /*- 2366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc. 3366f6083SPeter Grehan * All rights reserved. 4366f6083SPeter Grehan * 5366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without 6366f6083SPeter Grehan * modification, are permitted provided that the following conditions 7366f6083SPeter Grehan * are met: 8366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright 9366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer. 10366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 11366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the 12366f6083SPeter Grehan * documentation and/or other materials provided with the distribution. 13366f6083SPeter Grehan * 14366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24366f6083SPeter Grehan * SUCH DAMAGE. 25366f6083SPeter Grehan * 26366f6083SPeter Grehan * $FreeBSD$ 27366f6083SPeter Grehan */ 28366f6083SPeter Grehan 29366f6083SPeter Grehan #include <sys/cdefs.h> 30366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 31366f6083SPeter Grehan 32366f6083SPeter Grehan #include <sys/param.h> 33366f6083SPeter Grehan #include <sys/linker_set.h> 34028d9311SNeel Natu #include <sys/errno.h> 35366f6083SPeter Grehan 36366f6083SPeter Grehan #include <ctype.h> 373cbf3585SJohn Baldwin #include <pthread.h> 38366f6083SPeter Grehan #include <stdio.h> 39366f6083SPeter Grehan #include <stdlib.h> 40366f6083SPeter Grehan #include <string.h> 41366f6083SPeter Grehan #include <strings.h> 42366f6083SPeter Grehan #include <assert.h> 43028d9311SNeel Natu #include <stdbool.h> 44366f6083SPeter Grehan 45366f6083SPeter Grehan #include <machine/vmm.h> 46366f6083SPeter Grehan #include <vmmapi.h> 47366f6083SPeter Grehan 48e6c8bc29SJohn Baldwin #include "acpi.h" 49e285ef8dSPeter Grehan #include "bhyverun.h" 50366f6083SPeter Grehan #include "inout.h" 513cbf3585SJohn Baldwin #include "ioapic.h" 524d1e669cSPeter Grehan #include "mem.h" 53366f6083SPeter Grehan #include "pci_emul.h" 54e6c8bc29SJohn Baldwin #include "pci_lpc.h" 55366f6083SPeter Grehan 56366f6083SPeter Grehan #define CONF1_ADDR_PORT 0x0cf8 57366f6083SPeter Grehan #define CONF1_DATA_PORT 0x0cfc 58366f6083SPeter Grehan 5975543036SPeter Grehan #define CONF1_ENABLE 0x80000000ul 6075543036SPeter Grehan 61366f6083SPeter Grehan #define CFGWRITE(pi,off,val,b) \ 62366f6083SPeter Grehan do { \ 63366f6083SPeter Grehan if ((b) == 1) { \ 64366f6083SPeter Grehan pci_set_cfgdata8((pi),(off),(val)); \ 65366f6083SPeter Grehan } else if ((b) == 2) { \ 66366f6083SPeter Grehan pci_set_cfgdata16((pi),(off),(val)); \ 67366f6083SPeter Grehan } else { \ 68366f6083SPeter Grehan pci_set_cfgdata32((pi),(off),(val)); \ 69366f6083SPeter Grehan } \ 70366f6083SPeter Grehan } while (0) 71366f6083SPeter Grehan 7299d65389SNeel Natu #define MAXSLOTS (PCI_SLOTMAX + 1) 7399d65389SNeel Natu #define MAXFUNCS (PCI_FUNCMAX + 1) 74366f6083SPeter Grehan 753cbf3585SJohn Baldwin struct funcinfo { 763cbf3585SJohn Baldwin char *fi_name; 773cbf3585SJohn Baldwin char *fi_param; 783cbf3585SJohn Baldwin struct pci_devinst *fi_devi; 793cbf3585SJohn Baldwin }; 803cbf3585SJohn Baldwin 813cbf3585SJohn Baldwin struct intxinfo { 823cbf3585SJohn Baldwin int ii_count; 833cbf3585SJohn Baldwin int ii_ioapic_irq; 843cbf3585SJohn Baldwin }; 853cbf3585SJohn Baldwin 863cbf3585SJohn Baldwin struct slotinfo { 873cbf3585SJohn Baldwin struct intxinfo si_intpins[4]; 883cbf3585SJohn Baldwin struct funcinfo si_funcs[MAXFUNCS]; 893cbf3585SJohn Baldwin } pci_slotinfo[MAXSLOTS]; 90366f6083SPeter Grehan 91366f6083SPeter Grehan SET_DECLARE(pci_devemu_set, struct pci_devemu); 92366f6083SPeter Grehan 93366f6083SPeter Grehan static uint64_t pci_emul_iobase; 94366f6083SPeter Grehan static uint64_t pci_emul_membase32; 95366f6083SPeter Grehan static uint64_t pci_emul_membase64; 96366f6083SPeter Grehan 97366f6083SPeter Grehan #define PCI_EMUL_IOBASE 0x2000 98366f6083SPeter Grehan #define PCI_EMUL_IOLIMIT 0x10000 99366f6083SPeter Grehan 100366f6083SPeter Grehan #define PCI_EMUL_MEMLIMIT32 0xE0000000 /* 3.5GB */ 101366f6083SPeter Grehan 102366f6083SPeter Grehan #define PCI_EMUL_MEMBASE64 0xD000000000UL 103366f6083SPeter Grehan #define PCI_EMUL_MEMLIMIT64 0xFD00000000UL 104366f6083SPeter Grehan 105b05c77ffSNeel Natu static struct pci_devemu *pci_emul_finddev(char *name); 1063cbf3585SJohn Baldwin static void pci_lintr_update(struct pci_devinst *pi); 107b05c77ffSNeel Natu 108366f6083SPeter Grehan static int pci_emul_devices; 109e6c8bc29SJohn Baldwin static struct mem_range pci_mem_hole; 110366f6083SPeter Grehan 111366f6083SPeter Grehan /* 112366f6083SPeter Grehan * I/O access 113366f6083SPeter Grehan */ 114366f6083SPeter Grehan 115366f6083SPeter Grehan /* 116366f6083SPeter Grehan * Slot options are in the form: 117366f6083SPeter Grehan * 11899d65389SNeel Natu * <slot>[:<func>],<emul>[,<config>] 119366f6083SPeter Grehan * 120366f6083SPeter Grehan * slot is 0..31 12199d65389SNeel Natu * func is 0..7 122366f6083SPeter Grehan * emul is a string describing the type of PCI device e.g. virtio-net 123366f6083SPeter Grehan * config is an optional string, depending on the device, that can be 124366f6083SPeter Grehan * used for configuration. 125366f6083SPeter Grehan * Examples are: 126366f6083SPeter Grehan * 1,virtio-net,tap0 12799d65389SNeel Natu * 3:0,dummy 128366f6083SPeter Grehan */ 129366f6083SPeter Grehan static void 130366f6083SPeter Grehan pci_parse_slot_usage(char *aopt) 131366f6083SPeter Grehan { 132b05c77ffSNeel Natu 133b05c77ffSNeel Natu fprintf(stderr, "Invalid PCI slot info field \"%s\"\n", aopt); 134366f6083SPeter Grehan } 135366f6083SPeter Grehan 136b05c77ffSNeel Natu int 137d2bc4816SJohn Baldwin pci_parse_slot(char *opt) 138366f6083SPeter Grehan { 13999d65389SNeel Natu char *slot, *func, *emul, *config; 140366f6083SPeter Grehan char *str, *cpy; 141b05c77ffSNeel Natu int error, snum, fnum; 142366f6083SPeter Grehan 143b05c77ffSNeel Natu error = -1; 144366f6083SPeter Grehan str = cpy = strdup(opt); 14599d65389SNeel Natu 146366f6083SPeter Grehan slot = strsep(&str, ","); 14799d65389SNeel Natu func = NULL; 14834d244edSPeter Grehan if (strchr(slot, ':') != NULL) { 14934d244edSPeter Grehan func = cpy; 15034d244edSPeter Grehan (void) strsep(&func, ":"); 15199d65389SNeel Natu } 15299d65389SNeel Natu 153366f6083SPeter Grehan emul = strsep(&str, ","); 15434d244edSPeter Grehan config = str; 155366f6083SPeter Grehan 156366f6083SPeter Grehan if (emul == NULL) { 157b05c77ffSNeel Natu pci_parse_slot_usage(opt); 158b05c77ffSNeel Natu goto done; 159366f6083SPeter Grehan } 160366f6083SPeter Grehan 161366f6083SPeter Grehan snum = atoi(slot); 16299d65389SNeel Natu fnum = func ? atoi(func) : 0; 163b05c77ffSNeel Natu 16499d65389SNeel Natu if (snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) { 165b05c77ffSNeel Natu pci_parse_slot_usage(opt); 166b05c77ffSNeel Natu goto done; 167b05c77ffSNeel Natu } 168b05c77ffSNeel Natu 1693cbf3585SJohn Baldwin if (pci_slotinfo[snum].si_funcs[fnum].fi_name != NULL) { 170b05c77ffSNeel Natu fprintf(stderr, "pci slot %d:%d already occupied!\n", 171b05c77ffSNeel Natu snum, fnum); 172b05c77ffSNeel Natu goto done; 173b05c77ffSNeel Natu } 174b05c77ffSNeel Natu 175b05c77ffSNeel Natu if (pci_emul_finddev(emul) == NULL) { 176b05c77ffSNeel Natu fprintf(stderr, "pci slot %d:%d: unknown device \"%s\"\n", 177b05c77ffSNeel Natu snum, fnum, emul); 178b05c77ffSNeel Natu goto done; 179b05c77ffSNeel Natu } 180b05c77ffSNeel Natu 181b05c77ffSNeel Natu error = 0; 1823cbf3585SJohn Baldwin pci_slotinfo[snum].si_funcs[fnum].fi_name = emul; 1833cbf3585SJohn Baldwin pci_slotinfo[snum].si_funcs[fnum].fi_param = config; 184b05c77ffSNeel Natu 185b05c77ffSNeel Natu done: 186b05c77ffSNeel Natu if (error) 187b05c77ffSNeel Natu free(cpy); 188b05c77ffSNeel Natu 189b05c77ffSNeel Natu return (error); 190366f6083SPeter Grehan } 191366f6083SPeter Grehan 192366f6083SPeter Grehan static int 193c9b4e987SNeel Natu pci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) 194c9b4e987SNeel Natu { 195c9b4e987SNeel Natu 196c9b4e987SNeel Natu if (offset < pi->pi_msix.pba_offset) 197c9b4e987SNeel Natu return (0); 198c9b4e987SNeel Natu 199c9b4e987SNeel Natu if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 200c9b4e987SNeel Natu return (0); 201c9b4e987SNeel Natu } 202c9b4e987SNeel Natu 203c9b4e987SNeel Natu return (1); 204c9b4e987SNeel Natu } 205c9b4e987SNeel Natu 206c9b4e987SNeel Natu int 207c9b4e987SNeel Natu pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, 208c9b4e987SNeel Natu uint64_t value) 209c9b4e987SNeel Natu { 210c9b4e987SNeel Natu int msix_entry_offset; 211c9b4e987SNeel Natu int tab_index; 212c9b4e987SNeel Natu char *dest; 213c9b4e987SNeel Natu 214c9b4e987SNeel Natu /* support only 4 or 8 byte writes */ 215c9b4e987SNeel Natu if (size != 4 && size != 8) 216c9b4e987SNeel Natu return (-1); 217c9b4e987SNeel Natu 218c9b4e987SNeel Natu /* 219c9b4e987SNeel Natu * Return if table index is beyond what device supports 220c9b4e987SNeel Natu */ 221c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 222c9b4e987SNeel Natu if (tab_index >= pi->pi_msix.table_count) 223c9b4e987SNeel Natu return (-1); 224c9b4e987SNeel Natu 225c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 226c9b4e987SNeel Natu 227c9b4e987SNeel Natu /* support only aligned writes */ 228c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) 229c9b4e987SNeel Natu return (-1); 230c9b4e987SNeel Natu 231c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 232c9b4e987SNeel Natu dest += msix_entry_offset; 233c9b4e987SNeel Natu 234c9b4e987SNeel Natu if (size == 4) 235c9b4e987SNeel Natu *((uint32_t *)dest) = value; 236c9b4e987SNeel Natu else 237c9b4e987SNeel Natu *((uint64_t *)dest) = value; 238c9b4e987SNeel Natu 239c9b4e987SNeel Natu return (0); 240c9b4e987SNeel Natu } 241c9b4e987SNeel Natu 242c9b4e987SNeel Natu uint64_t 243c9b4e987SNeel Natu pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) 244c9b4e987SNeel Natu { 245c9b4e987SNeel Natu char *dest; 246c9b4e987SNeel Natu int msix_entry_offset; 247c9b4e987SNeel Natu int tab_index; 248c9b4e987SNeel Natu uint64_t retval = ~0; 249c9b4e987SNeel Natu 2506a52209fSNeel Natu /* 2516a52209fSNeel Natu * The PCI standard only allows 4 and 8 byte accesses to the MSI-X 2526a52209fSNeel Natu * table but we also allow 1 byte access to accomodate reads from 2536a52209fSNeel Natu * ddb. 2546a52209fSNeel Natu */ 2556a52209fSNeel Natu if (size != 1 && size != 4 && size != 8) 256c9b4e987SNeel Natu return (retval); 257c9b4e987SNeel Natu 258c9b4e987SNeel Natu msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 259c9b4e987SNeel Natu 260c9b4e987SNeel Natu /* support only aligned reads */ 261c9b4e987SNeel Natu if ((msix_entry_offset % size) != 0) { 262c9b4e987SNeel Natu return (retval); 263c9b4e987SNeel Natu } 264c9b4e987SNeel Natu 265c9b4e987SNeel Natu tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 266c9b4e987SNeel Natu 267c9b4e987SNeel Natu if (tab_index < pi->pi_msix.table_count) { 268c9b4e987SNeel Natu /* valid MSI-X Table access */ 269c9b4e987SNeel Natu dest = (char *)(pi->pi_msix.table + tab_index); 270c9b4e987SNeel Natu dest += msix_entry_offset; 271c9b4e987SNeel Natu 2726a52209fSNeel Natu if (size == 1) 2736a52209fSNeel Natu retval = *((uint8_t *)dest); 2746a52209fSNeel Natu else if (size == 4) 275c9b4e987SNeel Natu retval = *((uint32_t *)dest); 276c9b4e987SNeel Natu else 277c9b4e987SNeel Natu retval = *((uint64_t *)dest); 278c9b4e987SNeel Natu } else if (pci_valid_pba_offset(pi, offset)) { 279c9b4e987SNeel Natu /* return 0 for PBA access */ 280c9b4e987SNeel Natu retval = 0; 281c9b4e987SNeel Natu } 282c9b4e987SNeel Natu 283c9b4e987SNeel Natu return (retval); 284c9b4e987SNeel Natu } 285c9b4e987SNeel Natu 286aa12663fSNeel Natu int 287aa12663fSNeel Natu pci_msix_table_bar(struct pci_devinst *pi) 288aa12663fSNeel Natu { 289aa12663fSNeel Natu 290aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 291aa12663fSNeel Natu return (pi->pi_msix.table_bar); 292aa12663fSNeel Natu else 293aa12663fSNeel Natu return (-1); 294aa12663fSNeel Natu } 295aa12663fSNeel Natu 296aa12663fSNeel Natu int 297aa12663fSNeel Natu pci_msix_pba_bar(struct pci_devinst *pi) 298aa12663fSNeel Natu { 299aa12663fSNeel Natu 300aa12663fSNeel Natu if (pi->pi_msix.table != NULL) 301aa12663fSNeel Natu return (pi->pi_msix.pba_bar); 302aa12663fSNeel Natu else 303aa12663fSNeel Natu return (-1); 304aa12663fSNeel Natu } 305aa12663fSNeel Natu 306c9b4e987SNeel Natu static int 3074d1e669cSPeter Grehan pci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 308366f6083SPeter Grehan uint32_t *eax, void *arg) 309366f6083SPeter Grehan { 310366f6083SPeter Grehan struct pci_devinst *pdi = arg; 311366f6083SPeter Grehan struct pci_devemu *pe = pdi->pi_d; 3124d1e669cSPeter Grehan uint64_t offset; 3134d1e669cSPeter Grehan int i; 314366f6083SPeter Grehan 315366f6083SPeter Grehan for (i = 0; i <= PCI_BARMAX; i++) { 316366f6083SPeter Grehan if (pdi->pi_bar[i].type == PCIBAR_IO && 317366f6083SPeter Grehan port >= pdi->pi_bar[i].addr && 318c9b4e987SNeel Natu port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 319366f6083SPeter Grehan offset = port - pdi->pi_bar[i].addr; 320366f6083SPeter Grehan if (in) 3214d1e669cSPeter Grehan *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, 3224d1e669cSPeter Grehan offset, bytes); 323366f6083SPeter Grehan else 3244d1e669cSPeter Grehan (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, 3254d1e669cSPeter Grehan bytes, *eax); 326366f6083SPeter Grehan return (0); 327366f6083SPeter Grehan } 328366f6083SPeter Grehan } 329366f6083SPeter Grehan return (-1); 330366f6083SPeter Grehan } 331366f6083SPeter Grehan 332366f6083SPeter Grehan static int 3334d1e669cSPeter Grehan pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 3344d1e669cSPeter Grehan int size, uint64_t *val, void *arg1, long arg2) 3354d1e669cSPeter Grehan { 3364d1e669cSPeter Grehan struct pci_devinst *pdi = arg1; 3374d1e669cSPeter Grehan struct pci_devemu *pe = pdi->pi_d; 3384d1e669cSPeter Grehan uint64_t offset; 3394d1e669cSPeter Grehan int bidx = (int) arg2; 3404d1e669cSPeter Grehan 3414d1e669cSPeter Grehan assert(bidx <= PCI_BARMAX); 3424d1e669cSPeter Grehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 3434d1e669cSPeter Grehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 3444d1e669cSPeter Grehan assert(addr >= pdi->pi_bar[bidx].addr && 3454d1e669cSPeter Grehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 3464d1e669cSPeter Grehan 3474d1e669cSPeter Grehan offset = addr - pdi->pi_bar[bidx].addr; 3484d1e669cSPeter Grehan 3494d1e669cSPeter Grehan if (dir == MEM_F_WRITE) 3504d1e669cSPeter Grehan (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val); 3514d1e669cSPeter Grehan else 3524d1e669cSPeter Grehan *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size); 3534d1e669cSPeter Grehan 3544d1e669cSPeter Grehan return (0); 3554d1e669cSPeter Grehan } 3564d1e669cSPeter Grehan 3574d1e669cSPeter Grehan 3584d1e669cSPeter Grehan static int 359366f6083SPeter Grehan pci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 360366f6083SPeter Grehan uint64_t *addr) 361366f6083SPeter Grehan { 362366f6083SPeter Grehan uint64_t base; 363366f6083SPeter Grehan 364366f6083SPeter Grehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 365366f6083SPeter Grehan 366366f6083SPeter Grehan base = roundup2(*baseptr, size); 367366f6083SPeter Grehan 368366f6083SPeter Grehan if (base + size <= limit) { 369366f6083SPeter Grehan *addr = base; 370366f6083SPeter Grehan *baseptr = base + size; 371366f6083SPeter Grehan return (0); 372366f6083SPeter Grehan } else 373366f6083SPeter Grehan return (-1); 374366f6083SPeter Grehan } 375366f6083SPeter Grehan 376366f6083SPeter Grehan int 3774d1e669cSPeter Grehan pci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 3784d1e669cSPeter Grehan uint64_t size) 3794d1e669cSPeter Grehan { 3804d1e669cSPeter Grehan 3814d1e669cSPeter Grehan return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); 3824d1e669cSPeter Grehan } 3834d1e669cSPeter Grehan 384028d9311SNeel Natu /* 385028d9311SNeel Natu * Register (or unregister) the MMIO or I/O region associated with the BAR 386028d9311SNeel Natu * register 'idx' of an emulated pci device. 387028d9311SNeel Natu */ 388028d9311SNeel Natu static void 389028d9311SNeel Natu modify_bar_registration(struct pci_devinst *pi, int idx, int registration) 390028d9311SNeel Natu { 391028d9311SNeel Natu int error; 392028d9311SNeel Natu struct inout_port iop; 393028d9311SNeel Natu struct mem_range mr; 394028d9311SNeel Natu 395028d9311SNeel Natu switch (pi->pi_bar[idx].type) { 396028d9311SNeel Natu case PCIBAR_IO: 397028d9311SNeel Natu bzero(&iop, sizeof(struct inout_port)); 398028d9311SNeel Natu iop.name = pi->pi_name; 399028d9311SNeel Natu iop.port = pi->pi_bar[idx].addr; 400028d9311SNeel Natu iop.size = pi->pi_bar[idx].size; 401028d9311SNeel Natu if (registration) { 402028d9311SNeel Natu iop.flags = IOPORT_F_INOUT; 403028d9311SNeel Natu iop.handler = pci_emul_io_handler; 404028d9311SNeel Natu iop.arg = pi; 405028d9311SNeel Natu error = register_inout(&iop); 406028d9311SNeel Natu } else 407028d9311SNeel Natu error = unregister_inout(&iop); 408028d9311SNeel Natu break; 409028d9311SNeel Natu case PCIBAR_MEM32: 410028d9311SNeel Natu case PCIBAR_MEM64: 411028d9311SNeel Natu bzero(&mr, sizeof(struct mem_range)); 412028d9311SNeel Natu mr.name = pi->pi_name; 413028d9311SNeel Natu mr.base = pi->pi_bar[idx].addr; 414028d9311SNeel Natu mr.size = pi->pi_bar[idx].size; 415028d9311SNeel Natu if (registration) { 416028d9311SNeel Natu mr.flags = MEM_F_RW; 417028d9311SNeel Natu mr.handler = pci_emul_mem_handler; 418028d9311SNeel Natu mr.arg1 = pi; 419028d9311SNeel Natu mr.arg2 = idx; 420028d9311SNeel Natu error = register_mem(&mr); 421028d9311SNeel Natu } else 422028d9311SNeel Natu error = unregister_mem(&mr); 423028d9311SNeel Natu break; 424028d9311SNeel Natu default: 425028d9311SNeel Natu error = EINVAL; 426028d9311SNeel Natu break; 427028d9311SNeel Natu } 428028d9311SNeel Natu assert(error == 0); 429028d9311SNeel Natu } 430028d9311SNeel Natu 431028d9311SNeel Natu static void 432028d9311SNeel Natu unregister_bar(struct pci_devinst *pi, int idx) 433028d9311SNeel Natu { 434028d9311SNeel Natu 435028d9311SNeel Natu modify_bar_registration(pi, idx, 0); 436028d9311SNeel Natu } 437028d9311SNeel Natu 438028d9311SNeel Natu static void 439028d9311SNeel Natu register_bar(struct pci_devinst *pi, int idx) 440028d9311SNeel Natu { 441028d9311SNeel Natu 442028d9311SNeel Natu modify_bar_registration(pi, idx, 1); 443028d9311SNeel Natu } 444028d9311SNeel Natu 445028d9311SNeel Natu /* Are we decoding i/o port accesses for the emulated pci device? */ 446028d9311SNeel Natu static int 447028d9311SNeel Natu porten(struct pci_devinst *pi) 448028d9311SNeel Natu { 449028d9311SNeel Natu uint16_t cmd; 450028d9311SNeel Natu 451028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 452028d9311SNeel Natu 453028d9311SNeel Natu return (cmd & PCIM_CMD_PORTEN); 454028d9311SNeel Natu } 455028d9311SNeel Natu 456028d9311SNeel Natu /* Are we decoding memory accesses for the emulated pci device? */ 457028d9311SNeel Natu static int 458028d9311SNeel Natu memen(struct pci_devinst *pi) 459028d9311SNeel Natu { 460028d9311SNeel Natu uint16_t cmd; 461028d9311SNeel Natu 462028d9311SNeel Natu cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 463028d9311SNeel Natu 464028d9311SNeel Natu return (cmd & PCIM_CMD_MEMEN); 465028d9311SNeel Natu } 466028d9311SNeel Natu 467028d9311SNeel Natu /* 468028d9311SNeel Natu * Update the MMIO or I/O address that is decoded by the BAR register. 469028d9311SNeel Natu * 470028d9311SNeel Natu * If the pci device has enabled the address space decoding then intercept 471028d9311SNeel Natu * the address range decoded by the BAR register. 472028d9311SNeel Natu */ 473028d9311SNeel Natu static void 474028d9311SNeel Natu update_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) 475028d9311SNeel Natu { 476028d9311SNeel Natu int decode; 477028d9311SNeel Natu 478028d9311SNeel Natu if (pi->pi_bar[idx].type == PCIBAR_IO) 479028d9311SNeel Natu decode = porten(pi); 480028d9311SNeel Natu else 481028d9311SNeel Natu decode = memen(pi); 482028d9311SNeel Natu 483028d9311SNeel Natu if (decode) 484028d9311SNeel Natu unregister_bar(pi, idx); 485028d9311SNeel Natu 486028d9311SNeel Natu switch (type) { 487028d9311SNeel Natu case PCIBAR_IO: 488028d9311SNeel Natu case PCIBAR_MEM32: 489028d9311SNeel Natu pi->pi_bar[idx].addr = addr; 490028d9311SNeel Natu break; 491028d9311SNeel Natu case PCIBAR_MEM64: 492028d9311SNeel Natu pi->pi_bar[idx].addr &= ~0xffffffffUL; 493028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 494028d9311SNeel Natu break; 495028d9311SNeel Natu case PCIBAR_MEMHI64: 496028d9311SNeel Natu pi->pi_bar[idx].addr &= 0xffffffff; 497028d9311SNeel Natu pi->pi_bar[idx].addr |= addr; 498028d9311SNeel Natu break; 499028d9311SNeel Natu default: 500028d9311SNeel Natu assert(0); 501028d9311SNeel Natu } 502028d9311SNeel Natu 503028d9311SNeel Natu if (decode) 504028d9311SNeel Natu register_bar(pi, idx); 505028d9311SNeel Natu } 506028d9311SNeel Natu 5074d1e669cSPeter Grehan int 5084d1e669cSPeter Grehan pci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, 509366f6083SPeter Grehan enum pcibar_type type, uint64_t size) 510366f6083SPeter Grehan { 511028d9311SNeel Natu int error; 512366f6083SPeter Grehan uint64_t *baseptr, limit, addr, mask, lobits, bar; 513366f6083SPeter Grehan 514366f6083SPeter Grehan assert(idx >= 0 && idx <= PCI_BARMAX); 515366f6083SPeter Grehan 516366f6083SPeter Grehan if ((size & (size - 1)) != 0) 517366f6083SPeter Grehan size = 1UL << flsl(size); /* round up to a power of 2 */ 518366f6083SPeter Grehan 519028d9311SNeel Natu /* Enforce minimum BAR sizes required by the PCI standard */ 520028d9311SNeel Natu if (type == PCIBAR_IO) { 521028d9311SNeel Natu if (size < 4) 522028d9311SNeel Natu size = 4; 523028d9311SNeel Natu } else { 524028d9311SNeel Natu if (size < 16) 525028d9311SNeel Natu size = 16; 526028d9311SNeel Natu } 527028d9311SNeel Natu 528366f6083SPeter Grehan switch (type) { 529366f6083SPeter Grehan case PCIBAR_NONE: 530366f6083SPeter Grehan baseptr = NULL; 531366f6083SPeter Grehan addr = mask = lobits = 0; 532366f6083SPeter Grehan break; 533366f6083SPeter Grehan case PCIBAR_IO: 534366f6083SPeter Grehan baseptr = &pci_emul_iobase; 535366f6083SPeter Grehan limit = PCI_EMUL_IOLIMIT; 536366f6083SPeter Grehan mask = PCIM_BAR_IO_BASE; 537366f6083SPeter Grehan lobits = PCIM_BAR_IO_SPACE; 538366f6083SPeter Grehan break; 539366f6083SPeter Grehan case PCIBAR_MEM64: 540366f6083SPeter Grehan /* 541366f6083SPeter Grehan * XXX 542366f6083SPeter Grehan * Some drivers do not work well if the 64-bit BAR is allocated 543366f6083SPeter Grehan * above 4GB. Allow for this by allocating small requests under 544366f6083SPeter Grehan * 4GB unless then allocation size is larger than some arbitrary 545366f6083SPeter Grehan * number (32MB currently). 546366f6083SPeter Grehan */ 547366f6083SPeter Grehan if (size > 32 * 1024 * 1024) { 548366f6083SPeter Grehan /* 549366f6083SPeter Grehan * XXX special case for device requiring peer-peer DMA 550366f6083SPeter Grehan */ 551366f6083SPeter Grehan if (size == 0x100000000UL) 552366f6083SPeter Grehan baseptr = &hostbase; 553366f6083SPeter Grehan else 554366f6083SPeter Grehan baseptr = &pci_emul_membase64; 555366f6083SPeter Grehan limit = PCI_EMUL_MEMLIMIT64; 556366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 557366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 558366f6083SPeter Grehan PCIM_BAR_MEM_PREFETCH; 559366f6083SPeter Grehan break; 56025d4944eSNeel Natu } else { 56125d4944eSNeel Natu baseptr = &pci_emul_membase32; 56225d4944eSNeel Natu limit = PCI_EMUL_MEMLIMIT32; 56325d4944eSNeel Natu mask = PCIM_BAR_MEM_BASE; 56425d4944eSNeel Natu lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 565366f6083SPeter Grehan } 56625d4944eSNeel Natu break; 567366f6083SPeter Grehan case PCIBAR_MEM32: 568366f6083SPeter Grehan baseptr = &pci_emul_membase32; 569366f6083SPeter Grehan limit = PCI_EMUL_MEMLIMIT32; 570366f6083SPeter Grehan mask = PCIM_BAR_MEM_BASE; 571366f6083SPeter Grehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 572366f6083SPeter Grehan break; 573366f6083SPeter Grehan default: 574366f6083SPeter Grehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 575366f6083SPeter Grehan assert(0); 576366f6083SPeter Grehan } 577366f6083SPeter Grehan 578366f6083SPeter Grehan if (baseptr != NULL) { 579366f6083SPeter Grehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 580366f6083SPeter Grehan if (error != 0) 581366f6083SPeter Grehan return (error); 582366f6083SPeter Grehan } 583366f6083SPeter Grehan 584366f6083SPeter Grehan pdi->pi_bar[idx].type = type; 585366f6083SPeter Grehan pdi->pi_bar[idx].addr = addr; 586366f6083SPeter Grehan pdi->pi_bar[idx].size = size; 587366f6083SPeter Grehan 588366f6083SPeter Grehan /* Initialize the BAR register in config space */ 589366f6083SPeter Grehan bar = (addr & mask) | lobits; 590366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 591366f6083SPeter Grehan 592366f6083SPeter Grehan if (type == PCIBAR_MEM64) { 593366f6083SPeter Grehan assert(idx + 1 <= PCI_BARMAX); 594366f6083SPeter Grehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 595366f6083SPeter Grehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 596366f6083SPeter Grehan } 597366f6083SPeter Grehan 598028d9311SNeel Natu register_bar(pdi, idx); 599366f6083SPeter Grehan 600366f6083SPeter Grehan return (0); 601366f6083SPeter Grehan } 602366f6083SPeter Grehan 603366f6083SPeter Grehan #define CAP_START_OFFSET 0x40 604366f6083SPeter Grehan static int 605366f6083SPeter Grehan pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 606366f6083SPeter Grehan { 607366f6083SPeter Grehan int i, capoff, capid, reallen; 608366f6083SPeter Grehan uint16_t sts; 609366f6083SPeter Grehan 610366f6083SPeter Grehan static u_char endofcap[4] = { 611366f6083SPeter Grehan PCIY_RESERVED, 0, 0, 0 612366f6083SPeter Grehan }; 613366f6083SPeter Grehan 614366f6083SPeter Grehan assert(caplen > 0 && capdata[0] != PCIY_RESERVED); 615366f6083SPeter Grehan 616366f6083SPeter Grehan reallen = roundup2(caplen, 4); /* dword aligned */ 617366f6083SPeter Grehan 618366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 619366f6083SPeter Grehan if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 620366f6083SPeter Grehan capoff = CAP_START_OFFSET; 621366f6083SPeter Grehan pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 622366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 623366f6083SPeter Grehan } else { 624366f6083SPeter Grehan capoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR); 625366f6083SPeter Grehan while (1) { 626366f6083SPeter Grehan assert((capoff & 0x3) == 0); 627366f6083SPeter Grehan capid = pci_get_cfgdata8(pi, capoff); 628366f6083SPeter Grehan if (capid == PCIY_RESERVED) 629366f6083SPeter Grehan break; 630366f6083SPeter Grehan capoff = pci_get_cfgdata8(pi, capoff + 1); 631366f6083SPeter Grehan } 632366f6083SPeter Grehan } 633366f6083SPeter Grehan 634366f6083SPeter Grehan /* Check if we have enough space */ 635366f6083SPeter Grehan if (capoff + reallen + sizeof(endofcap) > PCI_REGMAX + 1) 636366f6083SPeter Grehan return (-1); 637366f6083SPeter Grehan 638366f6083SPeter Grehan /* Copy the capability */ 639366f6083SPeter Grehan for (i = 0; i < caplen; i++) 640366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 641366f6083SPeter Grehan 642366f6083SPeter Grehan /* Set the next capability pointer */ 643366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + 1, capoff + reallen); 644366f6083SPeter Grehan 645366f6083SPeter Grehan /* Copy of the reserved capability which serves as the end marker */ 646366f6083SPeter Grehan for (i = 0; i < sizeof(endofcap); i++) 647366f6083SPeter Grehan pci_set_cfgdata8(pi, capoff + reallen + i, endofcap[i]); 648366f6083SPeter Grehan 649366f6083SPeter Grehan return (0); 650366f6083SPeter Grehan } 651366f6083SPeter Grehan 652366f6083SPeter Grehan static struct pci_devemu * 653366f6083SPeter Grehan pci_emul_finddev(char *name) 654366f6083SPeter Grehan { 655366f6083SPeter Grehan struct pci_devemu **pdpp, *pdp; 656366f6083SPeter Grehan 657366f6083SPeter Grehan SET_FOREACH(pdpp, pci_devemu_set) { 658366f6083SPeter Grehan pdp = *pdpp; 659366f6083SPeter Grehan if (!strcmp(pdp->pe_emu, name)) { 660366f6083SPeter Grehan return (pdp); 661366f6083SPeter Grehan } 662366f6083SPeter Grehan } 663366f6083SPeter Grehan 664366f6083SPeter Grehan return (NULL); 665366f6083SPeter Grehan } 666366f6083SPeter Grehan 667a38e2a64SPeter Grehan static int 66899d65389SNeel Natu pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func, 66999d65389SNeel Natu char *params) 670366f6083SPeter Grehan { 671366f6083SPeter Grehan struct pci_devinst *pdi; 672a38e2a64SPeter Grehan int err; 673a38e2a64SPeter Grehan 674366f6083SPeter Grehan pdi = malloc(sizeof(struct pci_devinst)); 675366f6083SPeter Grehan bzero(pdi, sizeof(*pdi)); 676366f6083SPeter Grehan 677366f6083SPeter Grehan pdi->pi_vmctx = ctx; 678366f6083SPeter Grehan pdi->pi_bus = 0; 679366f6083SPeter Grehan pdi->pi_slot = slot; 68099d65389SNeel Natu pdi->pi_func = func; 6813cbf3585SJohn Baldwin pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 6823cbf3585SJohn Baldwin pdi->pi_lintr.pin = 0; 6833cbf3585SJohn Baldwin pdi->pi_lintr.state = IDLE; 6843cbf3585SJohn Baldwin pdi->pi_lintr.ioapic_irq = 0; 685366f6083SPeter Grehan pdi->pi_d = pde; 686366f6083SPeter Grehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 687366f6083SPeter Grehan 688366f6083SPeter Grehan /* Disable legacy interrupts */ 689366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 690366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 691366f6083SPeter Grehan 692366f6083SPeter Grehan pci_set_cfgdata8(pdi, PCIR_COMMAND, 693366f6083SPeter Grehan PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 694366f6083SPeter Grehan 695a38e2a64SPeter Grehan err = (*pde->pe_init)(ctx, pdi, params); 696a38e2a64SPeter Grehan if (err != 0) { 697366f6083SPeter Grehan free(pdi); 698366f6083SPeter Grehan } else { 699366f6083SPeter Grehan pci_emul_devices++; 7003cbf3585SJohn Baldwin pci_slotinfo[slot].si_funcs[func].fi_devi = pdi; 701366f6083SPeter Grehan } 702a38e2a64SPeter Grehan 703a38e2a64SPeter Grehan return (err); 704366f6083SPeter Grehan } 705366f6083SPeter Grehan 706366f6083SPeter Grehan void 707366f6083SPeter Grehan pci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 708366f6083SPeter Grehan { 709366f6083SPeter Grehan int mmc; 710366f6083SPeter Grehan 711366f6083SPeter Grehan CTASSERT(sizeof(struct msicap) == 14); 712366f6083SPeter Grehan 713366f6083SPeter Grehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 714366f6083SPeter Grehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 715366f6083SPeter Grehan mmc = ffs(msgnum) - 1; 716366f6083SPeter Grehan 717366f6083SPeter Grehan bzero(msicap, sizeof(struct msicap)); 718366f6083SPeter Grehan msicap->capid = PCIY_MSI; 719366f6083SPeter Grehan msicap->nextptr = nextptr; 720366f6083SPeter Grehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 721366f6083SPeter Grehan } 722366f6083SPeter Grehan 723366f6083SPeter Grehan int 724366f6083SPeter Grehan pci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 725366f6083SPeter Grehan { 726366f6083SPeter Grehan struct msicap msicap; 727366f6083SPeter Grehan 728366f6083SPeter Grehan pci_populate_msicap(&msicap, msgnum, 0); 729366f6083SPeter Grehan 730366f6083SPeter Grehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 731366f6083SPeter Grehan } 732366f6083SPeter Grehan 733c9b4e987SNeel Natu static void 734c9b4e987SNeel Natu pci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 735c9b4e987SNeel Natu uint32_t msix_tab_size, int nextptr) 736c9b4e987SNeel Natu { 737c9b4e987SNeel Natu CTASSERT(sizeof(struct msixcap) == 12); 738c9b4e987SNeel Natu 739c9b4e987SNeel Natu assert(msix_tab_size % 4096 == 0); 740c9b4e987SNeel Natu 741c9b4e987SNeel Natu bzero(msixcap, sizeof(struct msixcap)); 742c9b4e987SNeel Natu msixcap->capid = PCIY_MSIX; 743c9b4e987SNeel Natu msixcap->nextptr = nextptr; 744c9b4e987SNeel Natu 745c9b4e987SNeel Natu /* 746c9b4e987SNeel Natu * Message Control Register, all fields set to 747c9b4e987SNeel Natu * zero except for the Table Size. 748c9b4e987SNeel Natu * Note: Table size N is encoded as N-1 749c9b4e987SNeel Natu */ 750c9b4e987SNeel Natu msixcap->msgctrl = msgnum - 1; 751c9b4e987SNeel Natu 752c9b4e987SNeel Natu /* 753c9b4e987SNeel Natu * MSI-X BAR setup: 754c9b4e987SNeel Natu * - MSI-X table start at offset 0 755c9b4e987SNeel Natu * - PBA table starts at a 4K aligned offset after the MSI-X table 756c9b4e987SNeel Natu */ 757c9b4e987SNeel Natu msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 758c9b4e987SNeel Natu msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 759c9b4e987SNeel Natu } 760c9b4e987SNeel Natu 761c9b4e987SNeel Natu static void 762c9b4e987SNeel Natu pci_msix_table_init(struct pci_devinst *pi, int table_entries) 763c9b4e987SNeel Natu { 764c9b4e987SNeel Natu int i, table_size; 765c9b4e987SNeel Natu 766c9b4e987SNeel Natu assert(table_entries > 0); 767c9b4e987SNeel Natu assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 768c9b4e987SNeel Natu 769c9b4e987SNeel Natu table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 770c9b4e987SNeel Natu pi->pi_msix.table = malloc(table_size); 771c9b4e987SNeel Natu bzero(pi->pi_msix.table, table_size); 772c9b4e987SNeel Natu 773c9b4e987SNeel Natu /* set mask bit of vector control register */ 774c9b4e987SNeel Natu for (i = 0; i < table_entries; i++) 775c9b4e987SNeel Natu pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 776c9b4e987SNeel Natu } 777c9b4e987SNeel Natu 778c9b4e987SNeel Natu int 779c9b4e987SNeel Natu pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 780c9b4e987SNeel Natu { 781c9b4e987SNeel Natu uint16_t pba_index; 782c9b4e987SNeel Natu uint32_t tab_size; 783c9b4e987SNeel Natu struct msixcap msixcap; 784c9b4e987SNeel Natu 785c9b4e987SNeel Natu assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 786c9b4e987SNeel Natu assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 787c9b4e987SNeel Natu 788c9b4e987SNeel Natu tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 789c9b4e987SNeel Natu 790c9b4e987SNeel Natu /* Align table size to nearest 4K */ 791c9b4e987SNeel Natu tab_size = roundup2(tab_size, 4096); 792c9b4e987SNeel Natu 793c9b4e987SNeel Natu pi->pi_msix.table_bar = barnum; 794c9b4e987SNeel Natu pi->pi_msix.pba_bar = barnum; 795c9b4e987SNeel Natu pi->pi_msix.table_offset = 0; 796c9b4e987SNeel Natu pi->pi_msix.table_count = msgnum; 797c9b4e987SNeel Natu pi->pi_msix.pba_offset = tab_size; 798c9b4e987SNeel Natu 799c9b4e987SNeel Natu /* calculate the MMIO size required for MSI-X PBA */ 800c9b4e987SNeel Natu pba_index = (msgnum - 1) / (PBA_TABLE_ENTRY_SIZE * 8); 801c9b4e987SNeel Natu pi->pi_msix.pba_size = (pba_index + 1) * PBA_TABLE_ENTRY_SIZE; 802c9b4e987SNeel Natu 803c9b4e987SNeel Natu pci_msix_table_init(pi, msgnum); 804c9b4e987SNeel Natu 805c9b4e987SNeel Natu pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size, 0); 806c9b4e987SNeel Natu 807c9b4e987SNeel Natu /* allocate memory for MSI-X Table and PBA */ 808c9b4e987SNeel Natu pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 809c9b4e987SNeel Natu tab_size + pi->pi_msix.pba_size); 810c9b4e987SNeel Natu 811c9b4e987SNeel Natu return (pci_emul_add_capability(pi, (u_char *)&msixcap, 812c9b4e987SNeel Natu sizeof(msixcap))); 813c9b4e987SNeel Natu } 814c9b4e987SNeel Natu 815366f6083SPeter Grehan void 816cd942e0fSPeter Grehan msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 817cd942e0fSPeter Grehan int bytes, uint32_t val) 818cd942e0fSPeter Grehan { 819cd942e0fSPeter Grehan uint16_t msgctrl, rwmask; 820cd942e0fSPeter Grehan int off, table_bar; 821cd942e0fSPeter Grehan 822cd942e0fSPeter Grehan off = offset - capoff; 823cd942e0fSPeter Grehan table_bar = pi->pi_msix.table_bar; 824cd942e0fSPeter Grehan /* Message Control Register */ 825cd942e0fSPeter Grehan if (off == 2 && bytes == 2) { 826cd942e0fSPeter Grehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 827cd942e0fSPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 828cd942e0fSPeter Grehan msgctrl &= ~rwmask; 829cd942e0fSPeter Grehan msgctrl |= val & rwmask; 830cd942e0fSPeter Grehan val = msgctrl; 831cd942e0fSPeter Grehan 832cd942e0fSPeter Grehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 833c9b4e987SNeel Natu pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 8343cbf3585SJohn Baldwin pci_lintr_update(pi); 835cd942e0fSPeter Grehan } 836cd942e0fSPeter Grehan 837cd942e0fSPeter Grehan CFGWRITE(pi, offset, val, bytes); 838cd942e0fSPeter Grehan } 839cd942e0fSPeter Grehan 840cd942e0fSPeter Grehan void 841366f6083SPeter Grehan msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 842366f6083SPeter Grehan int bytes, uint32_t val) 843366f6083SPeter Grehan { 844366f6083SPeter Grehan uint16_t msgctrl, rwmask, msgdata, mme; 845366f6083SPeter Grehan uint32_t addrlo; 846366f6083SPeter Grehan 847366f6083SPeter Grehan /* 848366f6083SPeter Grehan * If guest is writing to the message control register make sure 849366f6083SPeter Grehan * we do not overwrite read-only fields. 850366f6083SPeter Grehan */ 851366f6083SPeter Grehan if ((offset - capoff) == 2 && bytes == 2) { 852366f6083SPeter Grehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 853366f6083SPeter Grehan msgctrl = pci_get_cfgdata16(pi, offset); 854366f6083SPeter Grehan msgctrl &= ~rwmask; 855366f6083SPeter Grehan msgctrl |= val & rwmask; 856366f6083SPeter Grehan val = msgctrl; 857366f6083SPeter Grehan 858366f6083SPeter Grehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 859366f6083SPeter Grehan if (msgctrl & PCIM_MSICTRL_64BIT) 860366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 861366f6083SPeter Grehan else 862366f6083SPeter Grehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 863366f6083SPeter Grehan 864366f6083SPeter Grehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 865366f6083SPeter Grehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 866366f6083SPeter Grehan if (pi->pi_msi.enabled) { 8674f8be175SNeel Natu pi->pi_msi.addr = addrlo; 8684f8be175SNeel Natu pi->pi_msi.msg_data = msgdata; 8694f8be175SNeel Natu pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 870366f6083SPeter Grehan } else { 8714f8be175SNeel Natu pi->pi_msi.maxmsgnum = 0; 872366f6083SPeter Grehan } 8733cbf3585SJohn Baldwin pci_lintr_update(pi); 874366f6083SPeter Grehan } 875366f6083SPeter Grehan 876366f6083SPeter Grehan CFGWRITE(pi, offset, val, bytes); 877366f6083SPeter Grehan } 878366f6083SPeter Grehan 87974f80b23SNeel Natu void 88074f80b23SNeel Natu pciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 88174f80b23SNeel Natu int bytes, uint32_t val) 88274f80b23SNeel Natu { 88374f80b23SNeel Natu 88474f80b23SNeel Natu /* XXX don't write to the readonly parts */ 88574f80b23SNeel Natu CFGWRITE(pi, offset, val, bytes); 88674f80b23SNeel Natu } 88774f80b23SNeel Natu 88874f80b23SNeel Natu #define PCIECAP_VERSION 0x2 88974f80b23SNeel Natu int 89074f80b23SNeel Natu pci_emul_add_pciecap(struct pci_devinst *pi, int type) 89174f80b23SNeel Natu { 89274f80b23SNeel Natu int err; 89374f80b23SNeel Natu struct pciecap pciecap; 89474f80b23SNeel Natu 89574f80b23SNeel Natu CTASSERT(sizeof(struct pciecap) == 60); 89674f80b23SNeel Natu 89774f80b23SNeel Natu if (type != PCIEM_TYPE_ROOT_PORT) 89874f80b23SNeel Natu return (-1); 89974f80b23SNeel Natu 90074f80b23SNeel Natu bzero(&pciecap, sizeof(pciecap)); 90174f80b23SNeel Natu 90274f80b23SNeel Natu pciecap.capid = PCIY_EXPRESS; 90374f80b23SNeel Natu pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT; 90474f80b23SNeel Natu pciecap.link_capabilities = 0x411; /* gen1, x1 */ 90574f80b23SNeel Natu pciecap.link_status = 0x11; /* gen1, x1 */ 90674f80b23SNeel Natu 90774f80b23SNeel Natu err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 90874f80b23SNeel Natu return (err); 90974f80b23SNeel Natu } 91074f80b23SNeel Natu 911366f6083SPeter Grehan /* 912366f6083SPeter Grehan * This function assumes that 'coff' is in the capabilities region of the 913366f6083SPeter Grehan * config space. 914366f6083SPeter Grehan */ 915366f6083SPeter Grehan static void 916366f6083SPeter Grehan pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) 917366f6083SPeter Grehan { 918366f6083SPeter Grehan int capid; 919366f6083SPeter Grehan uint8_t capoff, nextoff; 920366f6083SPeter Grehan 921366f6083SPeter Grehan /* Do not allow un-aligned writes */ 922366f6083SPeter Grehan if ((offset & (bytes - 1)) != 0) 923366f6083SPeter Grehan return; 924366f6083SPeter Grehan 925366f6083SPeter Grehan /* Find the capability that we want to update */ 926366f6083SPeter Grehan capoff = CAP_START_OFFSET; 927366f6083SPeter Grehan while (1) { 928366f6083SPeter Grehan capid = pci_get_cfgdata8(pi, capoff); 929366f6083SPeter Grehan if (capid == PCIY_RESERVED) 930366f6083SPeter Grehan break; 931366f6083SPeter Grehan 932366f6083SPeter Grehan nextoff = pci_get_cfgdata8(pi, capoff + 1); 933366f6083SPeter Grehan if (offset >= capoff && offset < nextoff) 934366f6083SPeter Grehan break; 935366f6083SPeter Grehan 936366f6083SPeter Grehan capoff = nextoff; 937366f6083SPeter Grehan } 938366f6083SPeter Grehan assert(offset >= capoff); 939366f6083SPeter Grehan 940366f6083SPeter Grehan /* 9412a8d400aSPeter Grehan * Capability ID and Next Capability Pointer are readonly. 9422a8d400aSPeter Grehan * However, some o/s's do 4-byte writes that include these. 9432a8d400aSPeter Grehan * For this case, trim the write back to 2 bytes and adjust 9442a8d400aSPeter Grehan * the data. 945366f6083SPeter Grehan */ 9462a8d400aSPeter Grehan if (offset == capoff || offset == capoff + 1) { 9472a8d400aSPeter Grehan if (offset == capoff && bytes == 4) { 9482a8d400aSPeter Grehan bytes = 2; 9492a8d400aSPeter Grehan offset += 2; 9502a8d400aSPeter Grehan val >>= 16; 9512a8d400aSPeter Grehan } else 952366f6083SPeter Grehan return; 9532a8d400aSPeter Grehan } 954366f6083SPeter Grehan 955366f6083SPeter Grehan switch (capid) { 956366f6083SPeter Grehan case PCIY_MSI: 957366f6083SPeter Grehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 958366f6083SPeter Grehan break; 959c9b4e987SNeel Natu case PCIY_MSIX: 960c9b4e987SNeel Natu msixcap_cfgwrite(pi, capoff, offset, bytes, val); 961c9b4e987SNeel Natu break; 96274f80b23SNeel Natu case PCIY_EXPRESS: 96374f80b23SNeel Natu pciecap_cfgwrite(pi, capoff, offset, bytes, val); 96474f80b23SNeel Natu break; 965366f6083SPeter Grehan default: 966366f6083SPeter Grehan break; 967366f6083SPeter Grehan } 968366f6083SPeter Grehan } 969366f6083SPeter Grehan 970366f6083SPeter Grehan static int 971366f6083SPeter Grehan pci_emul_iscap(struct pci_devinst *pi, int offset) 972366f6083SPeter Grehan { 973366f6083SPeter Grehan int found; 974366f6083SPeter Grehan uint16_t sts; 975366f6083SPeter Grehan uint8_t capid, lastoff; 976366f6083SPeter Grehan 977366f6083SPeter Grehan found = 0; 978366f6083SPeter Grehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 979366f6083SPeter Grehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 980366f6083SPeter Grehan lastoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR); 981366f6083SPeter Grehan while (1) { 982366f6083SPeter Grehan assert((lastoff & 0x3) == 0); 983366f6083SPeter Grehan capid = pci_get_cfgdata8(pi, lastoff); 984366f6083SPeter Grehan if (capid == PCIY_RESERVED) 985366f6083SPeter Grehan break; 986366f6083SPeter Grehan lastoff = pci_get_cfgdata8(pi, lastoff + 1); 987366f6083SPeter Grehan } 988366f6083SPeter Grehan if (offset >= CAP_START_OFFSET && offset <= lastoff) 989366f6083SPeter Grehan found = 1; 990366f6083SPeter Grehan } 991366f6083SPeter Grehan return (found); 992366f6083SPeter Grehan } 993366f6083SPeter Grehan 9940ab13648SPeter Grehan static int 9950ab13648SPeter Grehan pci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 9960ab13648SPeter Grehan int size, uint64_t *val, void *arg1, long arg2) 9970ab13648SPeter Grehan { 9980ab13648SPeter Grehan /* 9990ab13648SPeter Grehan * Ignore writes; return 0xff's for reads. The mem read code 10000ab13648SPeter Grehan * will take care of truncating to the correct size. 10010ab13648SPeter Grehan */ 10020ab13648SPeter Grehan if (dir == MEM_F_READ) { 10030ab13648SPeter Grehan *val = 0xffffffffffffffff; 10040ab13648SPeter Grehan } 10050ab13648SPeter Grehan 10060ab13648SPeter Grehan return (0); 10070ab13648SPeter Grehan } 10080ab13648SPeter Grehan 1009a38e2a64SPeter Grehan int 1010366f6083SPeter Grehan init_pci(struct vmctx *ctx) 1011366f6083SPeter Grehan { 1012366f6083SPeter Grehan struct pci_devemu *pde; 10133cbf3585SJohn Baldwin struct funcinfo *fi; 10149f08548dSNeel Natu size_t lowmem; 101599d65389SNeel Natu int slot, func; 10160ab13648SPeter Grehan int error; 1017366f6083SPeter Grehan 1018366f6083SPeter Grehan pci_emul_iobase = PCI_EMUL_IOBASE; 10199f08548dSNeel Natu pci_emul_membase32 = vm_get_lowmem_limit(ctx); 1020366f6083SPeter Grehan pci_emul_membase64 = PCI_EMUL_MEMBASE64; 1021366f6083SPeter Grehan 102299d65389SNeel Natu for (slot = 0; slot < MAXSLOTS; slot++) { 102399d65389SNeel Natu for (func = 0; func < MAXFUNCS; func++) { 10243cbf3585SJohn Baldwin fi = &pci_slotinfo[slot].si_funcs[func]; 10253cbf3585SJohn Baldwin if (fi->fi_name != NULL) { 10263cbf3585SJohn Baldwin pde = pci_emul_finddev(fi->fi_name); 1027b05c77ffSNeel Natu assert(pde != NULL); 1028a38e2a64SPeter Grehan error = pci_emul_init(ctx, pde, slot, func, 10293cbf3585SJohn Baldwin fi->fi_param); 1030a38e2a64SPeter Grehan if (error) 1031a38e2a64SPeter Grehan return (error); 1032366f6083SPeter Grehan } 1033366f6083SPeter Grehan } 1034366f6083SPeter Grehan } 10350038ee98SPeter Grehan 10360038ee98SPeter Grehan /* 10379f08548dSNeel Natu * The guest physical memory map looks like the following: 10389f08548dSNeel Natu * [0, lowmem) guest system memory 10399f08548dSNeel Natu * [lowmem, lowmem_limit) memory hole (may be absent) 10409f08548dSNeel Natu * [lowmem_limit, 4GB) PCI hole (32-bit BAR allocation) 10419f08548dSNeel Natu * [4GB, 4GB + highmem) 10429f08548dSNeel Natu * 10439f08548dSNeel Natu * Accesses to memory addresses that are not allocated to system 10449f08548dSNeel Natu * memory or PCI devices return 0xff's. 10450ab13648SPeter Grehan */ 1046318224bbSNeel Natu error = vm_get_memory_seg(ctx, 0, &lowmem, NULL); 10479f08548dSNeel Natu assert(error == 0); 10489f08548dSNeel Natu 1049e6c8bc29SJohn Baldwin memset(&pci_mem_hole, 0, sizeof(struct mem_range)); 1050e6c8bc29SJohn Baldwin pci_mem_hole.name = "PCI hole"; 1051e6c8bc29SJohn Baldwin pci_mem_hole.flags = MEM_F_RW; 1052e6c8bc29SJohn Baldwin pci_mem_hole.base = lowmem; 1053e6c8bc29SJohn Baldwin pci_mem_hole.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 1054e6c8bc29SJohn Baldwin pci_mem_hole.handler = pci_emul_fallback_handler; 10550ab13648SPeter Grehan 1056e6c8bc29SJohn Baldwin error = register_mem_fallback(&pci_mem_hole); 10570ab13648SPeter Grehan assert(error == 0); 1058a38e2a64SPeter Grehan 1059a38e2a64SPeter Grehan return (0); 1060366f6083SPeter Grehan } 1061366f6083SPeter Grehan 10623cbf3585SJohn Baldwin static void 10633cbf3585SJohn Baldwin pci_prt_entry(int slot, int pin, int ioapic_irq, void *arg) 10643cbf3585SJohn Baldwin { 10653cbf3585SJohn Baldwin int *count; 10663cbf3585SJohn Baldwin 10673cbf3585SJohn Baldwin count = arg; 10683cbf3585SJohn Baldwin dsdt_line(" Package (0x04)"); 10693cbf3585SJohn Baldwin dsdt_line(" {"); 10703cbf3585SJohn Baldwin dsdt_line(" 0x%X,", slot << 16 | 0xffff); 10713cbf3585SJohn Baldwin dsdt_line(" 0x%02X,", pin - 1); 10723cbf3585SJohn Baldwin dsdt_line(" Zero,"); 10733cbf3585SJohn Baldwin dsdt_line(" 0x%X", ioapic_irq); 10743cbf3585SJohn Baldwin dsdt_line(" }%s", *count == 1 ? "" : ","); 10753cbf3585SJohn Baldwin (*count)--; 10763cbf3585SJohn Baldwin } 10773cbf3585SJohn Baldwin 1078e6c8bc29SJohn Baldwin void 1079e6c8bc29SJohn Baldwin pci_write_dsdt(void) 1080e6c8bc29SJohn Baldwin { 1081e6c8bc29SJohn Baldwin struct pci_devinst *pi; 10823cbf3585SJohn Baldwin int count, slot, func; 1083e6c8bc29SJohn Baldwin 1084e6c8bc29SJohn Baldwin dsdt_indent(1); 1085e6c8bc29SJohn Baldwin dsdt_line("Scope (_SB)"); 1086e6c8bc29SJohn Baldwin dsdt_line("{"); 1087e6c8bc29SJohn Baldwin dsdt_line(" Device (PCI0)"); 1088e6c8bc29SJohn Baldwin dsdt_line(" {"); 1089e6c8bc29SJohn Baldwin dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1090e6c8bc29SJohn Baldwin dsdt_line(" Name (_ADR, Zero)"); 1091e6c8bc29SJohn Baldwin dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1092e6c8bc29SJohn Baldwin dsdt_line(" {"); 1093e6c8bc29SJohn Baldwin dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1094e6c8bc29SJohn Baldwin "MaxFixed, PosDecode,"); 1095e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1096e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Range Minimum"); 1097e6c8bc29SJohn Baldwin dsdt_line(" 0x00FF, // Range Maximum"); 1098e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1099e6c8bc29SJohn Baldwin dsdt_line(" 0x0100, // Length"); 1100e6c8bc29SJohn Baldwin dsdt_line(" ,, )"); 1101e6c8bc29SJohn Baldwin dsdt_indent(3); 1102e6c8bc29SJohn Baldwin dsdt_fixed_ioport(0xCF8, 8); 1103e6c8bc29SJohn Baldwin dsdt_unindent(3); 1104e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1105e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1106e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1107e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Range Minimum"); 1108e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF7, // Range Maximum"); 1109e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1110e6c8bc29SJohn Baldwin dsdt_line(" 0x0CF8, // Length"); 1111e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1112e6c8bc29SJohn Baldwin dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1113e6c8bc29SJohn Baldwin "PosDecode, EntireRange,"); 1114e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Granularity"); 1115e6c8bc29SJohn Baldwin dsdt_line(" 0x0D00, // Range Minimum"); 1116e6c8bc29SJohn Baldwin dsdt_line(" 0xFFFF, // Range Maximum"); 1117e6c8bc29SJohn Baldwin dsdt_line(" 0x0000, // Translation Offset"); 1118e6c8bc29SJohn Baldwin dsdt_line(" 0xF300, // Length"); 1119e6c8bc29SJohn Baldwin dsdt_line(" ,, , TypeStatic)"); 1120e6c8bc29SJohn Baldwin dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1121e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1122e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Granularity"); 1123e6c8bc29SJohn Baldwin dsdt_line(" 0x%08lX, // Range Minimum\n", 1124e6c8bc29SJohn Baldwin pci_mem_hole.base); 1125e6c8bc29SJohn Baldwin dsdt_line(" 0x%08X, // Range Maximum\n", 1126e6c8bc29SJohn Baldwin PCI_EMUL_MEMLIMIT32 - 1); 1127e6c8bc29SJohn Baldwin dsdt_line(" 0x00000000, // Translation Offset"); 1128e6c8bc29SJohn Baldwin dsdt_line(" 0x%08lX, // Length\n", 1129e6c8bc29SJohn Baldwin PCI_EMUL_MEMLIMIT32 - pci_mem_hole.base); 1130e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1131e6c8bc29SJohn Baldwin dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1132e6c8bc29SJohn Baldwin "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1133e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Granularity"); 1134e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Range Minimum\n", 1135e6c8bc29SJohn Baldwin PCI_EMUL_MEMBASE64); 1136e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Range Maximum\n", 1137e6c8bc29SJohn Baldwin PCI_EMUL_MEMLIMIT64 - 1); 1138e6c8bc29SJohn Baldwin dsdt_line(" 0x0000000000000000, // Translation Offset"); 1139e6c8bc29SJohn Baldwin dsdt_line(" 0x%016lX, // Length\n", 1140e6c8bc29SJohn Baldwin PCI_EMUL_MEMLIMIT64 - PCI_EMUL_MEMBASE64); 1141e6c8bc29SJohn Baldwin dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1142e6c8bc29SJohn Baldwin dsdt_line(" })"); 11433cbf3585SJohn Baldwin count = pci_count_lintr(); 11443cbf3585SJohn Baldwin if (count != 0) { 11453cbf3585SJohn Baldwin dsdt_indent(2); 11463cbf3585SJohn Baldwin dsdt_line("Name (_PRT, Package (0x%02X)", count); 11473cbf3585SJohn Baldwin dsdt_line("{"); 11483cbf3585SJohn Baldwin pci_walk_lintr(pci_prt_entry, &count); 11493cbf3585SJohn Baldwin dsdt_line("})"); 11503cbf3585SJohn Baldwin dsdt_unindent(2); 11513cbf3585SJohn Baldwin } 1152e6c8bc29SJohn Baldwin 1153e6c8bc29SJohn Baldwin dsdt_indent(2); 1154e6c8bc29SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 1155e6c8bc29SJohn Baldwin for (func = 0; func < MAXFUNCS; func++) { 11563cbf3585SJohn Baldwin pi = pci_slotinfo[slot].si_funcs[func].fi_devi; 1157e6c8bc29SJohn Baldwin if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1158e6c8bc29SJohn Baldwin pi->pi_d->pe_write_dsdt(pi); 1159e6c8bc29SJohn Baldwin } 1160e6c8bc29SJohn Baldwin } 1161e6c8bc29SJohn Baldwin dsdt_unindent(2); 1162e6c8bc29SJohn Baldwin 1163e6c8bc29SJohn Baldwin dsdt_line(" }"); 1164e6c8bc29SJohn Baldwin dsdt_line("}"); 1165e6c8bc29SJohn Baldwin dsdt_unindent(1); 1166e6c8bc29SJohn Baldwin } 1167e6c8bc29SJohn Baldwin 1168366f6083SPeter Grehan int 1169366f6083SPeter Grehan pci_msi_enabled(struct pci_devinst *pi) 1170366f6083SPeter Grehan { 1171366f6083SPeter Grehan return (pi->pi_msi.enabled); 1172366f6083SPeter Grehan } 1173366f6083SPeter Grehan 1174366f6083SPeter Grehan int 11754f8be175SNeel Natu pci_msi_maxmsgnum(struct pci_devinst *pi) 1176366f6083SPeter Grehan { 1177366f6083SPeter Grehan if (pi->pi_msi.enabled) 11784f8be175SNeel Natu return (pi->pi_msi.maxmsgnum); 1179366f6083SPeter Grehan else 1180366f6083SPeter Grehan return (0); 1181366f6083SPeter Grehan } 1182366f6083SPeter Grehan 1183c9b4e987SNeel Natu int 1184c9b4e987SNeel Natu pci_msix_enabled(struct pci_devinst *pi) 1185c9b4e987SNeel Natu { 1186c9b4e987SNeel Natu 1187c9b4e987SNeel Natu return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1188c9b4e987SNeel Natu } 1189c9b4e987SNeel Natu 1190c9b4e987SNeel Natu void 1191c9b4e987SNeel Natu pci_generate_msix(struct pci_devinst *pi, int index) 1192c9b4e987SNeel Natu { 1193c9b4e987SNeel Natu struct msix_table_entry *mte; 1194c9b4e987SNeel Natu 1195c9b4e987SNeel Natu if (!pci_msix_enabled(pi)) 1196c9b4e987SNeel Natu return; 1197c9b4e987SNeel Natu 1198c9b4e987SNeel Natu if (pi->pi_msix.function_mask) 1199c9b4e987SNeel Natu return; 1200c9b4e987SNeel Natu 1201c9b4e987SNeel Natu if (index >= pi->pi_msix.table_count) 1202c9b4e987SNeel Natu return; 1203c9b4e987SNeel Natu 1204c9b4e987SNeel Natu mte = &pi->pi_msix.table[index]; 1205c9b4e987SNeel Natu if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1206c9b4e987SNeel Natu /* XXX Set PBA bit if interrupt is disabled */ 12074f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1208c9b4e987SNeel Natu } 1209c9b4e987SNeel Natu } 1210c9b4e987SNeel Natu 1211366f6083SPeter Grehan void 12124f8be175SNeel Natu pci_generate_msi(struct pci_devinst *pi, int index) 1213366f6083SPeter Grehan { 1214366f6083SPeter Grehan 12154f8be175SNeel Natu if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 12164f8be175SNeel Natu vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 12174f8be175SNeel Natu pi->pi_msi.msg_data + index); 1218366f6083SPeter Grehan } 1219366f6083SPeter Grehan } 1220366f6083SPeter Grehan 12213cbf3585SJohn Baldwin static bool 12223cbf3585SJohn Baldwin pci_lintr_permitted(struct pci_devinst *pi) 12230038ee98SPeter Grehan { 12243cbf3585SJohn Baldwin uint16_t cmd; 12250038ee98SPeter Grehan 12263cbf3585SJohn Baldwin cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 12273cbf3585SJohn Baldwin return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 12283cbf3585SJohn Baldwin (cmd & PCIM_CMD_INTxDIS))); 12293cbf3585SJohn Baldwin } 12303cbf3585SJohn Baldwin 12313cbf3585SJohn Baldwin int 12323cbf3585SJohn Baldwin pci_lintr_request(struct pci_devinst *pi) 12333cbf3585SJohn Baldwin { 12343cbf3585SJohn Baldwin struct slotinfo *si; 12353cbf3585SJohn Baldwin int bestpin, bestcount, irq, pin; 12363cbf3585SJohn Baldwin 12373cbf3585SJohn Baldwin /* 12383cbf3585SJohn Baldwin * First, allocate a pin from our slot. 12393cbf3585SJohn Baldwin */ 12403cbf3585SJohn Baldwin si = &pci_slotinfo[pi->pi_slot]; 12413cbf3585SJohn Baldwin bestpin = 0; 12423cbf3585SJohn Baldwin bestcount = si->si_intpins[0].ii_count; 12433cbf3585SJohn Baldwin for (pin = 1; pin < 4; pin++) { 12443cbf3585SJohn Baldwin if (si->si_intpins[pin].ii_count < bestcount) { 12453cbf3585SJohn Baldwin bestpin = pin; 12463cbf3585SJohn Baldwin bestcount = si->si_intpins[pin].ii_count; 12473cbf3585SJohn Baldwin } 12483cbf3585SJohn Baldwin } 12493cbf3585SJohn Baldwin 12503cbf3585SJohn Baldwin /* 12513cbf3585SJohn Baldwin * Attempt to allocate an I/O APIC pin for this intpin. If 12523cbf3585SJohn Baldwin * 8259A support is added we will need a separate field to 12533cbf3585SJohn Baldwin * assign the intpin to an input pin on the PCI interrupt 12543cbf3585SJohn Baldwin * router. 12553cbf3585SJohn Baldwin */ 12563cbf3585SJohn Baldwin if (si->si_intpins[bestpin].ii_count == 0) { 12573cbf3585SJohn Baldwin irq = ioapic_pci_alloc_irq(); 1258ea7f1c8cSNeel Natu if (irq < 0) 1259ea7f1c8cSNeel Natu return (-1); 12603cbf3585SJohn Baldwin si->si_intpins[bestpin].ii_ioapic_irq = irq; 12613cbf3585SJohn Baldwin } else 12623cbf3585SJohn Baldwin irq = si->si_intpins[bestpin].ii_ioapic_irq; 12633cbf3585SJohn Baldwin si->si_intpins[bestpin].ii_count++; 1264ea7f1c8cSNeel Natu 12653cbf3585SJohn Baldwin pi->pi_lintr.pin = bestpin + 1; 12663cbf3585SJohn Baldwin pi->pi_lintr.ioapic_irq = irq; 1267ea7f1c8cSNeel Natu pci_set_cfgdata8(pi, PCIR_INTLINE, irq); 12683cbf3585SJohn Baldwin pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 12690038ee98SPeter Grehan return (0); 12700038ee98SPeter Grehan } 12710038ee98SPeter Grehan 12720038ee98SPeter Grehan void 12730038ee98SPeter Grehan pci_lintr_assert(struct pci_devinst *pi) 12740038ee98SPeter Grehan { 12750038ee98SPeter Grehan 12763cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1277ac7304a7SNeel Natu 12783cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 12793cbf3585SJohn Baldwin if (pi->pi_lintr.state == IDLE) { 12803cbf3585SJohn Baldwin if (pci_lintr_permitted(pi)) { 12813cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 12823cbf3585SJohn Baldwin vm_ioapic_assert_irq(pi->pi_vmctx, 12833cbf3585SJohn Baldwin pi->pi_lintr.ioapic_irq); 12843cbf3585SJohn Baldwin } else 12853cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 12860038ee98SPeter Grehan } 12873cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 1288ac7304a7SNeel Natu } 12890038ee98SPeter Grehan 12900038ee98SPeter Grehan void 12910038ee98SPeter Grehan pci_lintr_deassert(struct pci_devinst *pi) 12920038ee98SPeter Grehan { 12930038ee98SPeter Grehan 12943cbf3585SJohn Baldwin assert(pi->pi_lintr.pin > 0); 1295ac7304a7SNeel Natu 12963cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 12973cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED) { 12983cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 12993cbf3585SJohn Baldwin vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq); 13003cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING) 13013cbf3585SJohn Baldwin pi->pi_lintr.state = IDLE; 13023cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 13033cbf3585SJohn Baldwin } 13043cbf3585SJohn Baldwin 13053cbf3585SJohn Baldwin static void 13063cbf3585SJohn Baldwin pci_lintr_update(struct pci_devinst *pi) 13073cbf3585SJohn Baldwin { 13083cbf3585SJohn Baldwin 13093cbf3585SJohn Baldwin pthread_mutex_lock(&pi->pi_lintr.lock); 13103cbf3585SJohn Baldwin if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 13113cbf3585SJohn Baldwin vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq); 13123cbf3585SJohn Baldwin pi->pi_lintr.state = PENDING; 13133cbf3585SJohn Baldwin } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 13143cbf3585SJohn Baldwin pi->pi_lintr.state = ASSERTED; 13153cbf3585SJohn Baldwin vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq); 13163cbf3585SJohn Baldwin } 13173cbf3585SJohn Baldwin pthread_mutex_unlock(&pi->pi_lintr.lock); 13183cbf3585SJohn Baldwin } 13193cbf3585SJohn Baldwin 13203cbf3585SJohn Baldwin int 13213cbf3585SJohn Baldwin pci_count_lintr(void) 13223cbf3585SJohn Baldwin { 13233cbf3585SJohn Baldwin int count, slot, pin; 13243cbf3585SJohn Baldwin 13253cbf3585SJohn Baldwin count = 0; 13263cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 13273cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 13283cbf3585SJohn Baldwin if (pci_slotinfo[slot].si_intpins[pin].ii_count != 0) 13293cbf3585SJohn Baldwin count++; 13303cbf3585SJohn Baldwin } 13313cbf3585SJohn Baldwin } 13323cbf3585SJohn Baldwin return (count); 13333cbf3585SJohn Baldwin } 13343cbf3585SJohn Baldwin 13353cbf3585SJohn Baldwin void 13363cbf3585SJohn Baldwin pci_walk_lintr(pci_lintr_cb cb, void *arg) 13373cbf3585SJohn Baldwin { 13383cbf3585SJohn Baldwin struct intxinfo *ii; 13393cbf3585SJohn Baldwin int slot, pin; 13403cbf3585SJohn Baldwin 13413cbf3585SJohn Baldwin for (slot = 0; slot < MAXSLOTS; slot++) { 13423cbf3585SJohn Baldwin for (pin = 0; pin < 4; pin++) { 13433cbf3585SJohn Baldwin ii = &pci_slotinfo[slot].si_intpins[pin]; 13443cbf3585SJohn Baldwin if (ii->ii_count != 0) 13453cbf3585SJohn Baldwin cb(slot, pin + 1, ii->ii_ioapic_irq, arg); 13463cbf3585SJohn Baldwin } 13470038ee98SPeter Grehan } 1348ac7304a7SNeel Natu } 13490038ee98SPeter Grehan 135099d65389SNeel Natu /* 135199d65389SNeel Natu * Return 1 if the emulated device in 'slot' is a multi-function device. 135299d65389SNeel Natu * Return 0 otherwise. 135399d65389SNeel Natu */ 135499d65389SNeel Natu static int 135599d65389SNeel Natu pci_emul_is_mfdev(int slot) 135699d65389SNeel Natu { 135799d65389SNeel Natu int f, numfuncs; 13580038ee98SPeter Grehan 135999d65389SNeel Natu numfuncs = 0; 136099d65389SNeel Natu for (f = 0; f < MAXFUNCS; f++) { 13613cbf3585SJohn Baldwin if (pci_slotinfo[slot].si_funcs[f].fi_devi != NULL) { 136299d65389SNeel Natu numfuncs++; 136399d65389SNeel Natu } 136499d65389SNeel Natu } 136599d65389SNeel Natu return (numfuncs > 1); 136699d65389SNeel Natu } 136799d65389SNeel Natu 136899d65389SNeel Natu /* 136999d65389SNeel Natu * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 137099d65389SNeel Natu * whether or not is a multi-function being emulated in the pci 'slot'. 137199d65389SNeel Natu */ 137299d65389SNeel Natu static void 137399d65389SNeel Natu pci_emul_hdrtype_fixup(int slot, int off, int bytes, uint32_t *rv) 137499d65389SNeel Natu { 137599d65389SNeel Natu int mfdev; 137699d65389SNeel Natu 137799d65389SNeel Natu if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 137899d65389SNeel Natu mfdev = pci_emul_is_mfdev(slot); 137999d65389SNeel Natu switch (bytes) { 138099d65389SNeel Natu case 1: 138199d65389SNeel Natu case 2: 138299d65389SNeel Natu *rv &= ~PCIM_MFDEV; 138399d65389SNeel Natu if (mfdev) { 138499d65389SNeel Natu *rv |= PCIM_MFDEV; 138599d65389SNeel Natu } 138699d65389SNeel Natu break; 138799d65389SNeel Natu case 4: 138899d65389SNeel Natu *rv &= ~(PCIM_MFDEV << 16); 138999d65389SNeel Natu if (mfdev) { 139099d65389SNeel Natu *rv |= (PCIM_MFDEV << 16); 139199d65389SNeel Natu } 139299d65389SNeel Natu break; 139399d65389SNeel Natu } 139499d65389SNeel Natu } 139599d65389SNeel Natu } 13960038ee98SPeter Grehan 1397366f6083SPeter Grehan static int cfgbus, cfgslot, cfgfunc, cfgoff; 1398366f6083SPeter Grehan 1399366f6083SPeter Grehan static int 1400366f6083SPeter Grehan pci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1401366f6083SPeter Grehan uint32_t *eax, void *arg) 1402366f6083SPeter Grehan { 1403366f6083SPeter Grehan uint32_t x; 1404366f6083SPeter Grehan 140575543036SPeter Grehan if (bytes != 4) { 140675543036SPeter Grehan if (in) 140775543036SPeter Grehan *eax = (bytes == 2) ? 0xffff : 0xff; 140875543036SPeter Grehan return (0); 140975543036SPeter Grehan } 1410366f6083SPeter Grehan 141175543036SPeter Grehan if (in) { 141275543036SPeter Grehan x = (cfgbus << 16) | 141375543036SPeter Grehan (cfgslot << 11) | 141475543036SPeter Grehan (cfgfunc << 8) | 141575543036SPeter Grehan cfgoff; 141675543036SPeter Grehan *eax = x | CONF1_ENABLE; 141775543036SPeter Grehan } else { 1418366f6083SPeter Grehan x = *eax; 1419366f6083SPeter Grehan cfgoff = x & PCI_REGMAX; 1420366f6083SPeter Grehan cfgfunc = (x >> 8) & PCI_FUNCMAX; 1421366f6083SPeter Grehan cfgslot = (x >> 11) & PCI_SLOTMAX; 1422366f6083SPeter Grehan cfgbus = (x >> 16) & PCI_BUSMAX; 142375543036SPeter Grehan } 1424366f6083SPeter Grehan 1425366f6083SPeter Grehan return (0); 1426366f6083SPeter Grehan } 142775543036SPeter Grehan INOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 1428366f6083SPeter Grehan 1429028d9311SNeel Natu static uint32_t 1430028d9311SNeel Natu bits_changed(uint32_t old, uint32_t new, uint32_t mask) 1431028d9311SNeel Natu { 1432028d9311SNeel Natu 1433028d9311SNeel Natu return ((old ^ new) & mask); 1434028d9311SNeel Natu } 1435028d9311SNeel Natu 1436028d9311SNeel Natu static void 1437028d9311SNeel Natu pci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes) 1438028d9311SNeel Natu { 1439028d9311SNeel Natu int i; 1440028d9311SNeel Natu uint16_t old; 1441028d9311SNeel Natu 1442028d9311SNeel Natu /* 1443028d9311SNeel Natu * The command register is at an offset of 4 bytes and thus the 1444028d9311SNeel Natu * guest could write 1, 2 or 4 bytes starting at this offset. 1445028d9311SNeel Natu */ 1446028d9311SNeel Natu 1447028d9311SNeel Natu old = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 1448028d9311SNeel Natu CFGWRITE(pi, PCIR_COMMAND, new, bytes); /* update config */ 1449028d9311SNeel Natu new = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ 1450028d9311SNeel Natu 1451028d9311SNeel Natu /* 1452028d9311SNeel Natu * If the MMIO or I/O address space decoding has changed then 1453028d9311SNeel Natu * register/unregister all BARs that decode that address space. 1454028d9311SNeel Natu */ 1455c8afb9bcSNeel Natu for (i = 0; i <= PCI_BARMAX; i++) { 1456028d9311SNeel Natu switch (pi->pi_bar[i].type) { 1457028d9311SNeel Natu case PCIBAR_NONE: 1458028d9311SNeel Natu case PCIBAR_MEMHI64: 1459028d9311SNeel Natu break; 1460028d9311SNeel Natu case PCIBAR_IO: 1461028d9311SNeel Natu /* I/O address space decoding changed? */ 1462028d9311SNeel Natu if (bits_changed(old, new, PCIM_CMD_PORTEN)) { 1463028d9311SNeel Natu if (porten(pi)) 1464028d9311SNeel Natu register_bar(pi, i); 1465028d9311SNeel Natu else 1466028d9311SNeel Natu unregister_bar(pi, i); 1467028d9311SNeel Natu } 1468028d9311SNeel Natu break; 1469028d9311SNeel Natu case PCIBAR_MEM32: 1470028d9311SNeel Natu case PCIBAR_MEM64: 1471028d9311SNeel Natu /* MMIO address space decoding changed? */ 1472028d9311SNeel Natu if (bits_changed(old, new, PCIM_CMD_MEMEN)) { 1473028d9311SNeel Natu if (memen(pi)) 1474028d9311SNeel Natu register_bar(pi, i); 1475028d9311SNeel Natu else 1476028d9311SNeel Natu unregister_bar(pi, i); 1477028d9311SNeel Natu } 1478028d9311SNeel Natu break; 1479028d9311SNeel Natu default: 1480028d9311SNeel Natu assert(0); 1481028d9311SNeel Natu } 1482028d9311SNeel Natu } 14833cbf3585SJohn Baldwin 14843cbf3585SJohn Baldwin /* 14853cbf3585SJohn Baldwin * If INTx has been unmasked and is pending, assert the 14863cbf3585SJohn Baldwin * interrupt. 14873cbf3585SJohn Baldwin */ 14883cbf3585SJohn Baldwin pci_lintr_update(pi); 1489028d9311SNeel Natu } 1490028d9311SNeel Natu 1491366f6083SPeter Grehan static int 1492366f6083SPeter Grehan pci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1493366f6083SPeter Grehan uint32_t *eax, void *arg) 1494366f6083SPeter Grehan { 1495366f6083SPeter Grehan struct pci_devinst *pi; 1496366f6083SPeter Grehan struct pci_devemu *pe; 149799d65389SNeel Natu int coff, idx, needcfg; 1498028d9311SNeel Natu uint64_t addr, bar, mask; 1499366f6083SPeter Grehan 1500366f6083SPeter Grehan assert(bytes == 1 || bytes == 2 || bytes == 4); 1501366f6083SPeter Grehan 150290415e0bSNeel Natu if (cfgbus == 0) 15033cbf3585SJohn Baldwin pi = pci_slotinfo[cfgslot].si_funcs[cfgfunc].fi_devi; 150490415e0bSNeel Natu else 150590415e0bSNeel Natu pi = NULL; 150690415e0bSNeel Natu 1507366f6083SPeter Grehan coff = cfgoff + (port - CONF1_DATA_PORT); 1508366f6083SPeter Grehan 1509366f6083SPeter Grehan #if 0 1510366f6083SPeter Grehan printf("pcicfg-%s from 0x%0x of %d bytes (%d/%d/%d)\n\r", 1511366f6083SPeter Grehan in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc); 1512366f6083SPeter Grehan #endif 1513366f6083SPeter Grehan 151499d65389SNeel Natu /* 151599d65389SNeel Natu * Just return if there is no device at this cfgslot:cfgfunc or 151699d65389SNeel Natu * if the guest is doing an un-aligned access 151799d65389SNeel Natu */ 151899d65389SNeel Natu if (pi == NULL || (coff & (bytes - 1)) != 0) { 1519366f6083SPeter Grehan if (in) 1520366f6083SPeter Grehan *eax = 0xffffffff; 1521366f6083SPeter Grehan return (0); 1522366f6083SPeter Grehan } 1523366f6083SPeter Grehan 1524366f6083SPeter Grehan pe = pi->pi_d; 1525366f6083SPeter Grehan 1526366f6083SPeter Grehan /* 1527366f6083SPeter Grehan * Config read 1528366f6083SPeter Grehan */ 1529366f6083SPeter Grehan if (in) { 1530366f6083SPeter Grehan /* Let the device emulation override the default handler */ 153199d65389SNeel Natu if (pe->pe_cfgread != NULL) { 153299d65389SNeel Natu needcfg = pe->pe_cfgread(ctx, vcpu, pi, 153399d65389SNeel Natu coff, bytes, eax); 153499d65389SNeel Natu } else { 153599d65389SNeel Natu needcfg = 1; 153699d65389SNeel Natu } 1537366f6083SPeter Grehan 153899d65389SNeel Natu if (needcfg) { 1539366f6083SPeter Grehan if (bytes == 1) 1540366f6083SPeter Grehan *eax = pci_get_cfgdata8(pi, coff); 1541366f6083SPeter Grehan else if (bytes == 2) 1542366f6083SPeter Grehan *eax = pci_get_cfgdata16(pi, coff); 1543366f6083SPeter Grehan else 1544366f6083SPeter Grehan *eax = pci_get_cfgdata32(pi, coff); 154599d65389SNeel Natu } 154699d65389SNeel Natu 154799d65389SNeel Natu pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax); 1548366f6083SPeter Grehan } else { 1549366f6083SPeter Grehan /* Let the device emulation override the default handler */ 1550366f6083SPeter Grehan if (pe->pe_cfgwrite != NULL && 1551366f6083SPeter Grehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 1552366f6083SPeter Grehan return (0); 1553366f6083SPeter Grehan 1554366f6083SPeter Grehan /* 1555366f6083SPeter Grehan * Special handling for write to BAR registers 1556366f6083SPeter Grehan */ 1557366f6083SPeter Grehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { 1558366f6083SPeter Grehan /* 1559366f6083SPeter Grehan * Ignore writes to BAR registers that are not 1560366f6083SPeter Grehan * 4-byte aligned. 1561366f6083SPeter Grehan */ 1562366f6083SPeter Grehan if (bytes != 4 || (coff & 0x3) != 0) 1563366f6083SPeter Grehan return (0); 1564366f6083SPeter Grehan idx = (coff - PCIR_BAR(0)) / 4; 1565028d9311SNeel Natu mask = ~(pi->pi_bar[idx].size - 1); 1566366f6083SPeter Grehan switch (pi->pi_bar[idx].type) { 1567366f6083SPeter Grehan case PCIBAR_NONE: 1568028d9311SNeel Natu pi->pi_bar[idx].addr = bar = 0; 1569366f6083SPeter Grehan break; 1570366f6083SPeter Grehan case PCIBAR_IO: 1571028d9311SNeel Natu addr = *eax & mask; 1572028d9311SNeel Natu addr &= 0xffff; 1573028d9311SNeel Natu bar = addr | PCIM_BAR_IO_SPACE; 1574028d9311SNeel Natu /* 1575028d9311SNeel Natu * Register the new BAR value for interception 1576028d9311SNeel Natu */ 1577028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 1578028d9311SNeel Natu update_bar_address(pi, addr, idx, 1579028d9311SNeel Natu PCIBAR_IO); 1580028d9311SNeel Natu } 1581366f6083SPeter Grehan break; 1582366f6083SPeter Grehan case PCIBAR_MEM32: 1583028d9311SNeel Natu addr = bar = *eax & mask; 1584366f6083SPeter Grehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 1585028d9311SNeel Natu if (addr != pi->pi_bar[idx].addr) { 1586028d9311SNeel Natu update_bar_address(pi, addr, idx, 1587028d9311SNeel Natu PCIBAR_MEM32); 1588028d9311SNeel Natu } 1589366f6083SPeter Grehan break; 1590366f6083SPeter Grehan case PCIBAR_MEM64: 1591028d9311SNeel Natu addr = bar = *eax & mask; 1592366f6083SPeter Grehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 1593366f6083SPeter Grehan PCIM_BAR_MEM_PREFETCH; 1594028d9311SNeel Natu if (addr != (uint32_t)pi->pi_bar[idx].addr) { 1595028d9311SNeel Natu update_bar_address(pi, addr, idx, 1596028d9311SNeel Natu PCIBAR_MEM64); 1597028d9311SNeel Natu } 1598366f6083SPeter Grehan break; 1599366f6083SPeter Grehan case PCIBAR_MEMHI64: 1600366f6083SPeter Grehan mask = ~(pi->pi_bar[idx - 1].size - 1); 1601028d9311SNeel Natu addr = ((uint64_t)*eax << 32) & mask; 1602028d9311SNeel Natu bar = addr >> 32; 1603028d9311SNeel Natu if (bar != pi->pi_bar[idx - 1].addr >> 32) { 1604028d9311SNeel Natu update_bar_address(pi, addr, idx - 1, 1605028d9311SNeel Natu PCIBAR_MEMHI64); 1606028d9311SNeel Natu } 1607366f6083SPeter Grehan break; 1608366f6083SPeter Grehan default: 1609366f6083SPeter Grehan assert(0); 1610366f6083SPeter Grehan } 1611366f6083SPeter Grehan pci_set_cfgdata32(pi, coff, bar); 1612cd942e0fSPeter Grehan 1613366f6083SPeter Grehan } else if (pci_emul_iscap(pi, coff)) { 1614366f6083SPeter Grehan pci_emul_capwrite(pi, coff, bytes, *eax); 1615028d9311SNeel Natu } else if (coff == PCIR_COMMAND) { 1616028d9311SNeel Natu pci_emul_cmdwrite(pi, *eax, bytes); 1617366f6083SPeter Grehan } else { 1618366f6083SPeter Grehan CFGWRITE(pi, coff, *eax, bytes); 1619366f6083SPeter Grehan } 1620366f6083SPeter Grehan } 1621366f6083SPeter Grehan 1622366f6083SPeter Grehan return (0); 1623366f6083SPeter Grehan } 1624366f6083SPeter Grehan 1625366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 1626366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 1627366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 1628366f6083SPeter Grehan INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 1629366f6083SPeter Grehan 1630366f6083SPeter Grehan /* 1631366f6083SPeter Grehan * I/O ports to configure PCI IRQ routing. We ignore all writes to it. 1632366f6083SPeter Grehan */ 1633366f6083SPeter Grehan static int 1634366f6083SPeter Grehan pci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1635366f6083SPeter Grehan uint32_t *eax, void *arg) 1636366f6083SPeter Grehan { 1637366f6083SPeter Grehan assert(in == 0); 1638366f6083SPeter Grehan return (0); 1639366f6083SPeter Grehan } 1640366f6083SPeter Grehan INOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler); 1641366f6083SPeter Grehan INOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler); 1642e6c8bc29SJohn Baldwin SYSRES_IO(0xC00, 2); 1643366f6083SPeter Grehan 1644366f6083SPeter Grehan #define PCI_EMUL_TEST 1645366f6083SPeter Grehan #ifdef PCI_EMUL_TEST 1646366f6083SPeter Grehan /* 1647366f6083SPeter Grehan * Define a dummy test device 1648366f6083SPeter Grehan */ 16494d1e669cSPeter Grehan #define DIOSZ 20 16504d1e669cSPeter Grehan #define DMEMSZ 4096 1651366f6083SPeter Grehan struct pci_emul_dsoftc { 16524d1e669cSPeter Grehan uint8_t ioregs[DIOSZ]; 16534d1e669cSPeter Grehan uint8_t memregs[DMEMSZ]; 1654366f6083SPeter Grehan }; 1655366f6083SPeter Grehan 16564d1e669cSPeter Grehan #define PCI_EMUL_MSI_MSGS 4 16574d1e669cSPeter Grehan #define PCI_EMUL_MSIX_MSGS 16 1658366f6083SPeter Grehan 1659b67e81dbSJohn Baldwin static int 1660366f6083SPeter Grehan pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 1661366f6083SPeter Grehan { 1662366f6083SPeter Grehan int error; 1663366f6083SPeter Grehan struct pci_emul_dsoftc *sc; 1664366f6083SPeter Grehan 1665366f6083SPeter Grehan sc = malloc(sizeof(struct pci_emul_dsoftc)); 1666366f6083SPeter Grehan memset(sc, 0, sizeof(struct pci_emul_dsoftc)); 1667366f6083SPeter Grehan 1668366f6083SPeter Grehan pi->pi_arg = sc; 1669366f6083SPeter Grehan 1670366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 1671366f6083SPeter Grehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 1672366f6083SPeter Grehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 1673366f6083SPeter Grehan 16744d1e669cSPeter Grehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 1675366f6083SPeter Grehan assert(error == 0); 1676366f6083SPeter Grehan 16774d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 16784d1e669cSPeter Grehan assert(error == 0); 16794d1e669cSPeter Grehan 16804d1e669cSPeter Grehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 1681366f6083SPeter Grehan assert(error == 0); 1682366f6083SPeter Grehan 1683366f6083SPeter Grehan return (0); 1684366f6083SPeter Grehan } 1685366f6083SPeter Grehan 1686b67e81dbSJohn Baldwin static void 16874d1e669cSPeter Grehan pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 16884d1e669cSPeter Grehan uint64_t offset, int size, uint64_t value) 1689366f6083SPeter Grehan { 1690366f6083SPeter Grehan int i; 1691366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1692366f6083SPeter Grehan 16934d1e669cSPeter Grehan if (baridx == 0) { 16944d1e669cSPeter Grehan if (offset + size > DIOSZ) { 16954d1e669cSPeter Grehan printf("diow: iow too large, offset %ld size %d\n", 16964d1e669cSPeter Grehan offset, size); 1697366f6083SPeter Grehan return; 1698366f6083SPeter Grehan } 1699366f6083SPeter Grehan 1700366f6083SPeter Grehan if (size == 1) { 17014d1e669cSPeter Grehan sc->ioregs[offset] = value & 0xff; 1702366f6083SPeter Grehan } else if (size == 2) { 17034d1e669cSPeter Grehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 17044d1e669cSPeter Grehan } else if (size == 4) { 17054d1e669cSPeter Grehan *(uint32_t *)&sc->ioregs[offset] = value; 1706366f6083SPeter Grehan } else { 17074d1e669cSPeter Grehan printf("diow: iow unknown size %d\n", size); 1708366f6083SPeter Grehan } 1709366f6083SPeter Grehan 1710366f6083SPeter Grehan /* 1711366f6083SPeter Grehan * Special magic value to generate an interrupt 1712366f6083SPeter Grehan */ 1713366f6083SPeter Grehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 17144f8be175SNeel Natu pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 1715366f6083SPeter Grehan 1716366f6083SPeter Grehan if (value == 0xabcdef) { 17174f8be175SNeel Natu for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 1718366f6083SPeter Grehan pci_generate_msi(pi, i); 1719366f6083SPeter Grehan } 1720366f6083SPeter Grehan } 1721366f6083SPeter Grehan 17224d1e669cSPeter Grehan if (baridx == 1) { 17234d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 17244d1e669cSPeter Grehan printf("diow: memw too large, offset %ld size %d\n", 17254d1e669cSPeter Grehan offset, size); 17264d1e669cSPeter Grehan return; 17274d1e669cSPeter Grehan } 17284d1e669cSPeter Grehan 17294d1e669cSPeter Grehan if (size == 1) { 17304d1e669cSPeter Grehan sc->memregs[offset] = value; 17314d1e669cSPeter Grehan } else if (size == 2) { 17324d1e669cSPeter Grehan *(uint16_t *)&sc->memregs[offset] = value; 17334d1e669cSPeter Grehan } else if (size == 4) { 17344d1e669cSPeter Grehan *(uint32_t *)&sc->memregs[offset] = value; 17354d1e669cSPeter Grehan } else if (size == 8) { 17364d1e669cSPeter Grehan *(uint64_t *)&sc->memregs[offset] = value; 17374d1e669cSPeter Grehan } else { 17384d1e669cSPeter Grehan printf("diow: memw unknown size %d\n", size); 17394d1e669cSPeter Grehan } 17404d1e669cSPeter Grehan 17414d1e669cSPeter Grehan /* 17424d1e669cSPeter Grehan * magic interrupt ?? 17434d1e669cSPeter Grehan */ 17444d1e669cSPeter Grehan } 17454d1e669cSPeter Grehan 17464d1e669cSPeter Grehan if (baridx > 1) { 17474d1e669cSPeter Grehan printf("diow: unknown bar idx %d\n", baridx); 17484d1e669cSPeter Grehan } 17494d1e669cSPeter Grehan } 17504d1e669cSPeter Grehan 17514d1e669cSPeter Grehan static uint64_t 17524d1e669cSPeter Grehan pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 17534d1e669cSPeter Grehan uint64_t offset, int size) 1754366f6083SPeter Grehan { 1755366f6083SPeter Grehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1756366f6083SPeter Grehan uint32_t value; 1757366f6083SPeter Grehan 17584d1e669cSPeter Grehan if (baridx == 0) { 17594d1e669cSPeter Grehan if (offset + size > DIOSZ) { 17604d1e669cSPeter Grehan printf("dior: ior too large, offset %ld size %d\n", 17614d1e669cSPeter Grehan offset, size); 1762366f6083SPeter Grehan return (0); 1763366f6083SPeter Grehan } 1764366f6083SPeter Grehan 1765366f6083SPeter Grehan if (size == 1) { 17664d1e669cSPeter Grehan value = sc->ioregs[offset]; 1767366f6083SPeter Grehan } else if (size == 2) { 17684d1e669cSPeter Grehan value = *(uint16_t *) &sc->ioregs[offset]; 17694d1e669cSPeter Grehan } else if (size == 4) { 17704d1e669cSPeter Grehan value = *(uint32_t *) &sc->ioregs[offset]; 1771366f6083SPeter Grehan } else { 17724d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 17734d1e669cSPeter Grehan } 17744d1e669cSPeter Grehan } 17754d1e669cSPeter Grehan 17764d1e669cSPeter Grehan if (baridx == 1) { 17774d1e669cSPeter Grehan if (offset + size > DMEMSZ) { 17784d1e669cSPeter Grehan printf("dior: memr too large, offset %ld size %d\n", 17794d1e669cSPeter Grehan offset, size); 17804d1e669cSPeter Grehan return (0); 17814d1e669cSPeter Grehan } 17824d1e669cSPeter Grehan 17834d1e669cSPeter Grehan if (size == 1) { 17844d1e669cSPeter Grehan value = sc->memregs[offset]; 17854d1e669cSPeter Grehan } else if (size == 2) { 17864d1e669cSPeter Grehan value = *(uint16_t *) &sc->memregs[offset]; 17874d1e669cSPeter Grehan } else if (size == 4) { 17884d1e669cSPeter Grehan value = *(uint32_t *) &sc->memregs[offset]; 17894d1e669cSPeter Grehan } else if (size == 8) { 17904d1e669cSPeter Grehan value = *(uint64_t *) &sc->memregs[offset]; 17914d1e669cSPeter Grehan } else { 17924d1e669cSPeter Grehan printf("dior: ior unknown size %d\n", size); 17934d1e669cSPeter Grehan } 17944d1e669cSPeter Grehan } 17954d1e669cSPeter Grehan 17964d1e669cSPeter Grehan 17974d1e669cSPeter Grehan if (baridx > 1) { 17984d1e669cSPeter Grehan printf("dior: unknown bar idx %d\n", baridx); 17994d1e669cSPeter Grehan return (0); 1800366f6083SPeter Grehan } 1801366f6083SPeter Grehan 1802366f6083SPeter Grehan return (value); 1803366f6083SPeter Grehan } 1804366f6083SPeter Grehan 1805366f6083SPeter Grehan struct pci_devemu pci_dummy = { 1806366f6083SPeter Grehan .pe_emu = "dummy", 1807366f6083SPeter Grehan .pe_init = pci_emul_dinit, 18084d1e669cSPeter Grehan .pe_barwrite = pci_emul_diow, 18094d1e669cSPeter Grehan .pe_barread = pci_emul_dior 1810366f6083SPeter Grehan }; 1811366f6083SPeter Grehan PCI_EMUL_SET(pci_dummy); 1812366f6083SPeter Grehan 1813366f6083SPeter Grehan #endif /* PCI_EMUL_TEST */ 1814