1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD: releng/11.0/usr.sbin/bhyve/pci_emul.c 302373 2016-07-06 16:02:15Z ngie $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: releng/11.0/usr.sbin/bhyve/pci_emul.c 302373 2016-07-06 16:02:15Z ngie $"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34221828Sgrehan 35221828Sgrehan#include <ctype.h> 36302373Sngie#include <errno.h> 37261268Sjhb#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 48260206Sjhb#include "acpi.h" 49244167Sgrehan#include "bhyverun.h" 50221828Sgrehan#include "inout.h" 51261268Sjhb#include "ioapic.h" 52241744Sgrehan#include "mem.h" 53221828Sgrehan#include "pci_emul.h" 54266125Sjhb#include "pci_irq.h" 55260206Sjhb#include "pci_lpc.h" 56221828Sgrehan 57221828Sgrehan#define CONF1_ADDR_PORT 0x0cf8 58221828Sgrehan#define CONF1_DATA_PORT 0x0cfc 59221828Sgrehan 60252331Sgrehan#define CONF1_ENABLE 0x80000000ul 61252331Sgrehan 62261904Sneel#define MAXBUSES (PCI_BUSMAX + 1) 63239085Sneel#define MAXSLOTS (PCI_SLOTMAX + 1) 64239085Sneel#define MAXFUNCS (PCI_FUNCMAX + 1) 65221828Sgrehan 66261268Sjhbstruct funcinfo { 67261268Sjhb char *fi_name; 68261268Sjhb char *fi_param; 69261268Sjhb struct pci_devinst *fi_devi; 70261268Sjhb}; 71221828Sgrehan 72261268Sjhbstruct intxinfo { 73261268Sjhb int ii_count; 74266125Sjhb int ii_pirq_pin; 75261268Sjhb int ii_ioapic_irq; 76261268Sjhb}; 77261268Sjhb 78261268Sjhbstruct slotinfo { 79261268Sjhb struct intxinfo si_intpins[4]; 80261268Sjhb struct funcinfo si_funcs[MAXFUNCS]; 81261904Sneel}; 82261268Sjhb 83261904Sneelstruct businfo { 84261904Sneel uint16_t iobase, iolimit; /* I/O window */ 85261904Sneel uint32_t membase32, memlimit32; /* mmio window below 4GB */ 86261904Sneel uint64_t membase64, memlimit64; /* mmio window above 4GB */ 87261904Sneel struct slotinfo slotinfo[MAXSLOTS]; 88261904Sneel}; 89261904Sneel 90261904Sneelstatic struct businfo *pci_businfo[MAXBUSES]; 91261904Sneel 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 101269700Sneel#define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ 102269700Sneel#define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ 103269700SneelSYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); 104221828Sgrehan 105269700Sneel#define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE 106269700Sneel 107221828Sgrehan#define PCI_EMUL_MEMBASE64 0xD000000000UL 108221828Sgrehan#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL 109221828Sgrehan 110249916Sneelstatic struct pci_devemu *pci_emul_finddev(char *name); 111269700Sneelstatic void pci_lintr_route(struct pci_devinst *pi); 112269700Sneelstatic void pci_lintr_update(struct pci_devinst *pi); 113269700Sneelstatic void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, 114269700Sneel int func, int coff, int bytes, uint32_t *val); 115249916Sneel 116281946Sneelstatic __inline void 117281946SneelCFGWRITE(struct pci_devinst *pi, int coff, uint32_t val, int bytes) 118281946Sneel{ 119281946Sneel 120281946Sneel if (bytes == 1) 121281946Sneel pci_set_cfgdata8(pi, coff, val); 122281946Sneel else if (bytes == 2) 123281946Sneel pci_set_cfgdata16(pi, coff, val); 124281946Sneel else 125281946Sneel pci_set_cfgdata32(pi, coff, val); 126281946Sneel} 127281946Sneel 128281946Sneelstatic __inline uint32_t 129281946SneelCFGREAD(struct pci_devinst *pi, int coff, int bytes) 130281946Sneel{ 131281946Sneel 132281946Sneel if (bytes == 1) 133281946Sneel return (pci_get_cfgdata8(pi, coff)); 134281946Sneel else if (bytes == 2) 135281946Sneel return (pci_get_cfgdata16(pi, coff)); 136281946Sneel else 137281946Sneel return (pci_get_cfgdata32(pi, coff)); 138281946Sneel} 139281946Sneel 140221828Sgrehan/* 141221828Sgrehan * I/O access 142221828Sgrehan */ 143221828Sgrehan 144221828Sgrehan/* 145221828Sgrehan * Slot options are in the form: 146221828Sgrehan * 147261904Sneel * <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 167261217Sjhbpci_parse_slot(char *opt) 168221828Sgrehan{ 169261904Sneel struct businfo *bi; 170261904Sneel struct slotinfo *si; 171261904Sneel char *emul, *config, *str, *cp; 172261904Sneel int error, bnum, snum, fnum; 173221828Sgrehan 174249916Sneel error = -1; 175261904Sneel str = strdup(opt); 176239085Sneel 177261904Sneel emul = config = NULL; 178261904Sneel if ((cp = strchr(str, ',')) != NULL) { 179261904Sneel *cp = '\0'; 180261904Sneel emul = cp + 1; 181261904Sneel if ((cp = strchr(emul, ',')) != NULL) { 182261904Sneel *cp = '\0'; 183261904Sneel config = cp + 1; 184261904Sneel } 185261904Sneel } else { 186249916Sneel pci_parse_slot_usage(opt); 187249916Sneel goto done; 188221828Sgrehan } 189221828Sgrehan 190261904Sneel /* <bus>:<slot>:<func> */ 191261904Sneel if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { 192261904Sneel bnum = 0; 193261904Sneel /* <slot>:<func> */ 194261904Sneel if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { 195261904Sneel fnum = 0; 196261904Sneel /* <slot> */ 197261904Sneel if (sscanf(str, "%d", &snum) != 1) { 198261904Sneel snum = -1; 199261904Sneel } 200261904Sneel } 201261904Sneel } 202249916Sneel 203261904Sneel if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || 204261904Sneel fnum < 0 || fnum >= MAXFUNCS) { 205249916Sneel pci_parse_slot_usage(opt); 206249916Sneel goto done; 207221828Sgrehan } 208249916Sneel 209261904Sneel if (pci_businfo[bnum] == NULL) 210261904Sneel pci_businfo[bnum] = calloc(1, sizeof(struct businfo)); 211261904Sneel 212261904Sneel bi = pci_businfo[bnum]; 213261904Sneel si = &bi->slotinfo[snum]; 214261904Sneel 215261904Sneel 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; 228261904Sneel si->si_funcs[fnum].fi_name = emul; 229261904Sneel si->si_funcs[fnum].fi_param = config; 230249916Sneel 231249916Sneeldone: 232249916Sneel if (error) 233261904Sneel 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 298289677Seadler * 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 395267169Stychon if (dir == MEM_F_WRITE) { 396267294Stychon if (size == 8) { 397267169Stychon (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 398267169Stychon 4, *val & 0xffffffff); 399267169Stychon (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 400267169Stychon 4, *val >> 32); 401267169Stychon } else { 402267169Stychon (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 403267169Stychon size, *val); 404267169Stychon } 405267169Stychon } else { 406267294Stychon if (size == 8) { 407267169Stychon *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 408267169Stychon offset, 4); 409267169Stychon *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 410267169Stychon offset + 4, 4) << 32; 411267169Stychon } else { 412267169Stychon *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 413267169Stychon offset, size); 414267169Stychon } 415267169Stychon } 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: 597261217Sjhb 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{ 670262143Sjhb int i, capoff, reallen; 671221828Sgrehan uint16_t sts; 672221828Sgrehan 673262143Sjhb assert(caplen > 0); 674221828Sgrehan 675221828Sgrehan reallen = roundup2(caplen, 4); /* dword aligned */ 676221828Sgrehan 677221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 678262143Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) 679221828Sgrehan capoff = CAP_START_OFFSET; 680262143Sjhb else 681262143Sjhb capoff = pi->pi_capend + 1; 682221828Sgrehan 683221828Sgrehan /* Check if we have enough space */ 684262143Sjhb if (capoff + reallen > PCI_REGMAX + 1) 685221828Sgrehan return (-1); 686221828Sgrehan 687262143Sjhb /* Set the previous capability pointer */ 688262143Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 689262143Sjhb pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 690262143Sjhb pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 691262143Sjhb } else 692262143Sjhb pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); 693262143Sjhb 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 */ 699262143Sjhb pci_set_cfgdata8(pi, capoff + 1, 0); 700221828Sgrehan 701262143Sjhb pi->pi_prevcap = capoff; 702262143Sjhb 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 722261904Sneelpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, 723261904Sneel int func, struct funcinfo *fi) 724221828Sgrehan{ 725221828Sgrehan struct pci_devinst *pdi; 726252682Sgrehan int err; 727252682Sgrehan 728264770Sdelphij pdi = calloc(1, sizeof(struct pci_devinst)); 729221828Sgrehan 730221828Sgrehan pdi->pi_vmctx = ctx; 731261904Sneel pdi->pi_bus = bus; 732221828Sgrehan pdi->pi_slot = slot; 733239085Sneel pdi->pi_func = func; 734261268Sjhb pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 735261268Sjhb pdi->pi_lintr.pin = 0; 736261268Sjhb pdi->pi_lintr.state = IDLE; 737266125Sjhb pdi->pi_lintr.pirq_pin = 0; 738261268Sjhb 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 749261904Sneel err = (*pde->pe_init)(ctx, pdi, fi->fi_param); 750261904Sneel if (err == 0) 751261904Sneel fi->fi_devi = pdi; 752261904Sneel 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, 785262143Sjhb 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; 818264770Sdelphij 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; 844262184Sneel pi->pi_msix.pba_size = PBA_SIZE(msgnum); 845246109Sneel 846246109Sneel pci_msix_table_init(pi, msgnum); 847246109Sneel 848262143Sjhb 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; 863292970Saraujo 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; 876261268Sjhb 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) { 909259482Sneel pi->pi_msi.addr = addrlo; 910259482Sneel pi->pi_msi.msg_data = msgdata; 911259482Sneel pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 912221828Sgrehan } else { 913259482Sneel pi->pi_msi.maxmsgnum = 0; 914221828Sgrehan } 915261268Sjhb 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) { 968262143Sjhb nextoff = pci_get_cfgdata8(pi, capoff + 1); 969262143Sjhb 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 993262143Sjhb 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) { 1016262143Sjhb if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1017262143Sjhb return (1); 1018221828Sgrehan } 1019262143Sjhb 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 1037269700Sneelstatic int 1038269700Sneelpci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1039269700Sneel int bytes, uint64_t *val, void *arg1, long arg2) 1040269700Sneel{ 1041269700Sneel int bus, slot, func, coff, in; 1042269700Sneel 1043269700Sneel coff = addr & 0xfff; 1044269700Sneel func = (addr >> 12) & 0x7; 1045269700Sneel slot = (addr >> 15) & 0x1f; 1046269700Sneel bus = (addr >> 20) & 0xff; 1047269700Sneel in = (dir == MEM_F_READ); 1048269700Sneel if (in) 1049269700Sneel *val = ~0UL; 1050269700Sneel pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); 1051269700Sneel return (0); 1052269700Sneel} 1053269700Sneel 1054269700Sneeluint64_t 1055269700Sneelpci_ecfg_base(void) 1056269700Sneel{ 1057269700Sneel 1058269700Sneel return (PCI_EMUL_ECFG_BASE); 1059269700Sneel} 1060269700Sneel 1061261904Sneel#define BUSIO_ROUNDUP 32 1062261904Sneel#define BUSMEM_ROUNDUP (1024 * 1024) 1063261904Sneel 1064252682Sgrehanint 1065221828Sgrehaninit_pci(struct vmctx *ctx) 1066221828Sgrehan{ 1067269700Sneel struct mem_range mr; 1068221828Sgrehan struct pci_devemu *pde; 1069261904Sneel struct businfo *bi; 1070261904Sneel struct slotinfo *si; 1071261268Sjhb struct funcinfo *fi; 1072249572Sneel size_t lowmem; 1073261904Sneel 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 1080261904Sneel for (bus = 0; bus < MAXBUSES; bus++) { 1081261904Sneel if ((bi = pci_businfo[bus]) == NULL) 1082261904Sneel continue; 1083261904Sneel /* 1084261904Sneel * Keep track of the i/o and memory resources allocated to 1085261904Sneel * this bus. 1086261904Sneel */ 1087261904Sneel bi->iobase = pci_emul_iobase; 1088261904Sneel bi->membase32 = pci_emul_membase32; 1089261904Sneel bi->membase64 = pci_emul_membase64; 1090261904Sneel 1091261904Sneel for (slot = 0; slot < MAXSLOTS; slot++) { 1092261904Sneel si = &bi->slotinfo[slot]; 1093261904Sneel for (func = 0; func < MAXFUNCS; func++) { 1094261904Sneel fi = &si->si_funcs[func]; 1095261904Sneel if (fi->fi_name == NULL) 1096261904Sneel continue; 1097261268Sjhb pde = pci_emul_finddev(fi->fi_name); 1098249916Sneel assert(pde != NULL); 1099261904Sneel error = pci_emul_init(ctx, pde, bus, slot, 1100261904Sneel func, fi); 1101252682Sgrehan if (error) 1102252682Sgrehan return (error); 1103221828Sgrehan } 1104221828Sgrehan } 1105261904Sneel 1106261904Sneel /* 1107261904Sneel * Add some slop to the I/O and memory resources decoded by 1108261904Sneel * this bus to give a guest some flexibility if it wants to 1109261904Sneel * reprogram the BARs. 1110261904Sneel */ 1111261904Sneel pci_emul_iobase += BUSIO_ROUNDUP; 1112261904Sneel pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1113261904Sneel bi->iolimit = pci_emul_iobase; 1114261904Sneel 1115261904Sneel pci_emul_membase32 += BUSMEM_ROUNDUP; 1116261904Sneel pci_emul_membase32 = roundup2(pci_emul_membase32, 1117261904Sneel BUSMEM_ROUNDUP); 1118261904Sneel bi->memlimit32 = pci_emul_membase32; 1119261904Sneel 1120261904Sneel pci_emul_membase64 += BUSMEM_ROUNDUP; 1121261904Sneel pci_emul_membase64 = roundup2(pci_emul_membase64, 1122261904Sneel BUSMEM_ROUNDUP); 1123261904Sneel bi->memlimit64 = pci_emul_membase64; 1124221828Sgrehan } 1125234938Sgrehan 1126234938Sgrehan /* 1127266125Sjhb * PCI backends are initialized before routing INTx interrupts 1128266125Sjhb * so that LPC devices are able to reserve ISA IRQs before 1129266125Sjhb * routing PIRQ pins. 1130266125Sjhb */ 1131266125Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1132266125Sjhb if ((bi = pci_businfo[bus]) == NULL) 1133266125Sjhb continue; 1134266125Sjhb 1135266125Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1136266125Sjhb si = &bi->slotinfo[slot]; 1137266125Sjhb for (func = 0; func < MAXFUNCS; func++) { 1138266125Sjhb fi = &si->si_funcs[func]; 1139266125Sjhb if (fi->fi_devi == NULL) 1140266125Sjhb continue; 1141266125Sjhb pci_lintr_route(fi->fi_devi); 1142266125Sjhb } 1143266125Sjhb } 1144266125Sjhb } 1145266125Sjhb lpc_pirq_routed(); 1146266125Sjhb 1147266125Sjhb /* 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) 1151269700Sneel * [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) 1152269700Sneel * [0xE0000000, 0xF0000000) PCI extended config window 1153269700Sneel * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware 1154249572Sneel * [4GB, 4GB + highmem) 1155269700Sneel */ 1156269700Sneel 1157269700Sneel /* 1158249572Sneel * Accesses to memory addresses that are not allocated to system 1159249572Sneel * memory or PCI devices return 0xff's. 1160247144Sgrehan */ 1161267811Sneel lowmem = vm_get_lowmem_size(ctx); 1162269700Sneel bzero(&mr, sizeof(struct mem_range)); 1163269700Sneel mr.name = "PCI hole"; 1164269700Sneel mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1165269700Sneel mr.base = lowmem; 1166269700Sneel mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 1167269700Sneel mr.handler = pci_emul_fallback_handler; 1168269700Sneel error = register_mem_fallback(&mr); 1169269700Sneel assert(error == 0); 1170249572Sneel 1171269700Sneel /* PCI extended config space */ 1172269700Sneel bzero(&mr, sizeof(struct mem_range)); 1173269700Sneel mr.name = "PCI ECFG"; 1174269700Sneel mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1175269700Sneel mr.base = PCI_EMUL_ECFG_BASE; 1176269700Sneel mr.size = PCI_EMUL_ECFG_SIZE; 1177269700Sneel mr.handler = pci_emul_ecfg_handler; 1178269700Sneel error = register_mem(&mr); 1179247144Sgrehan assert(error == 0); 1180252682Sgrehan 1181252682Sgrehan return (0); 1182221828Sgrehan} 1183221828Sgrehan 1184261268Sjhbstatic void 1185266125Sjhbpci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1186266125Sjhb void *arg) 1187261268Sjhb{ 1188261268Sjhb 1189266125Sjhb dsdt_line(" Package ()"); 1190261268Sjhb dsdt_line(" {"); 1191261268Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1192261268Sjhb dsdt_line(" 0x%02X,", pin - 1); 1193261268Sjhb dsdt_line(" Zero,"); 1194261268Sjhb dsdt_line(" 0x%X", ioapic_irq); 1195266125Sjhb dsdt_line(" },"); 1196261268Sjhb} 1197261268Sjhb 1198266125Sjhbstatic void 1199266125Sjhbpci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1200266125Sjhb void *arg) 1201266125Sjhb{ 1202266125Sjhb char *name; 1203266125Sjhb 1204266125Sjhb name = lpc_pirq_name(pirq_pin); 1205266125Sjhb if (name == NULL) 1206266125Sjhb return; 1207266125Sjhb dsdt_line(" Package ()"); 1208266125Sjhb dsdt_line(" {"); 1209266125Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1210266125Sjhb dsdt_line(" 0x%02X,", pin - 1); 1211266125Sjhb dsdt_line(" %s,", name); 1212266125Sjhb dsdt_line(" 0x00"); 1213266125Sjhb dsdt_line(" },"); 1214266125Sjhb free(name); 1215266125Sjhb} 1216266125Sjhb 1217261904Sneel/* 1218261904Sneel * A bhyve virtual machine has a flat PCI hierarchy with a root port 1219261904Sneel * corresponding to each PCI bus. 1220261904Sneel */ 1221261904Sneelstatic void 1222261904Sneelpci_bus_write_dsdt(int bus) 1223260206Sjhb{ 1224261904Sneel struct businfo *bi; 1225261904Sneel struct slotinfo *si; 1226260206Sjhb struct pci_devinst *pi; 1227266125Sjhb int count, func, slot; 1228260206Sjhb 1229261904Sneel /* 1230261904Sneel * If there are no devices on this 'bus' then just return. 1231261904Sneel */ 1232261904Sneel if ((bi = pci_businfo[bus]) == NULL) { 1233261904Sneel /* 1234261904Sneel * Bus 0 is special because it decodes the I/O ports used 1235261904Sneel * for PCI config space access even if there are no devices 1236261904Sneel * on it. 1237261904Sneel */ 1238261904Sneel if (bus != 0) 1239261904Sneel return; 1240261904Sneel } 1241261904Sneel 1242261904Sneel dsdt_line(" Device (PC%02X)", bus); 1243260206Sjhb dsdt_line(" {"); 1244260206Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1245260206Sjhb dsdt_line(" Name (_ADR, Zero)"); 1246261904Sneel 1247261904Sneel dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1248261904Sneel dsdt_line(" {"); 1249261904Sneel dsdt_line(" Return (0x%08X)", bus); 1250261904Sneel dsdt_line(" }"); 1251260206Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1252260206Sjhb dsdt_line(" {"); 1253260206Sjhb dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1254260206Sjhb "MaxFixed, PosDecode,"); 1255260206Sjhb dsdt_line(" 0x0000, // Granularity"); 1256261904Sneel dsdt_line(" 0x%04X, // Range Minimum", bus); 1257261904Sneel dsdt_line(" 0x%04X, // Range Maximum", bus); 1258260206Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1259261904Sneel dsdt_line(" 0x0001, // Length"); 1260260206Sjhb dsdt_line(" ,, )"); 1261261904Sneel 1262261904Sneel if (bus == 0) { 1263261904Sneel dsdt_indent(3); 1264261904Sneel dsdt_fixed_ioport(0xCF8, 8); 1265261904Sneel dsdt_unindent(3); 1266261904Sneel 1267261904Sneel dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1268261904Sneel "PosDecode, EntireRange,"); 1269261904Sneel dsdt_line(" 0x0000, // Granularity"); 1270261904Sneel dsdt_line(" 0x0000, // Range Minimum"); 1271261904Sneel dsdt_line(" 0x0CF7, // Range Maximum"); 1272261904Sneel dsdt_line(" 0x0000, // Translation Offset"); 1273261904Sneel dsdt_line(" 0x0CF8, // Length"); 1274261904Sneel dsdt_line(" ,, , TypeStatic)"); 1275261904Sneel 1276261904Sneel dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1277261904Sneel "PosDecode, EntireRange,"); 1278261904Sneel dsdt_line(" 0x0000, // Granularity"); 1279261904Sneel dsdt_line(" 0x0D00, // Range Minimum"); 1280261904Sneel dsdt_line(" 0x%04X, // Range Maximum", 1281261904Sneel PCI_EMUL_IOBASE - 1); 1282261904Sneel dsdt_line(" 0x0000, // Translation Offset"); 1283261904Sneel dsdt_line(" 0x%04X, // Length", 1284261904Sneel PCI_EMUL_IOBASE - 0x0D00); 1285261904Sneel dsdt_line(" ,, , TypeStatic)"); 1286261904Sneel 1287261904Sneel if (bi == NULL) { 1288261904Sneel dsdt_line(" })"); 1289261904Sneel goto done; 1290261904Sneel } 1291261904Sneel } 1292261904Sneel assert(bi != NULL); 1293261904Sneel 1294261904Sneel /* i/o window */ 1295260206Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1296260206Sjhb "PosDecode, EntireRange,"); 1297260206Sjhb dsdt_line(" 0x0000, // Granularity"); 1298261904Sneel dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1299261904Sneel dsdt_line(" 0x%04X, // Range Maximum", 1300261904Sneel bi->iolimit - 1); 1301260206Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1302261904Sneel dsdt_line(" 0x%04X, // Length", 1303261904Sneel bi->iolimit - bi->iobase); 1304260206Sjhb dsdt_line(" ,, , TypeStatic)"); 1305261904Sneel 1306261904Sneel /* mmio window (32-bit) */ 1307260206Sjhb dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1308260206Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1309260206Sjhb dsdt_line(" 0x00000000, // Granularity"); 1310261904Sneel dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1311260206Sjhb dsdt_line(" 0x%08X, // Range Maximum\n", 1312261904Sneel bi->memlimit32 - 1); 1313260206Sjhb dsdt_line(" 0x00000000, // Translation Offset"); 1314261904Sneel dsdt_line(" 0x%08X, // Length\n", 1315261904Sneel bi->memlimit32 - bi->membase32); 1316260206Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1317261904Sneel 1318261904Sneel /* mmio window (64-bit) */ 1319260206Sjhb dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1320260206Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1321260206Sjhb dsdt_line(" 0x0000000000000000, // Granularity"); 1322261904Sneel dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1323260206Sjhb dsdt_line(" 0x%016lX, // Range Maximum\n", 1324261904Sneel bi->memlimit64 - 1); 1325260206Sjhb dsdt_line(" 0x0000000000000000, // Translation Offset"); 1326260206Sjhb dsdt_line(" 0x%016lX, // Length\n", 1327261904Sneel bi->memlimit64 - bi->membase64); 1328260206Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1329260206Sjhb dsdt_line(" })"); 1330261904Sneel 1331261904Sneel count = pci_count_lintr(bus); 1332261268Sjhb if (count != 0) { 1333261268Sjhb dsdt_indent(2); 1334266125Sjhb dsdt_line("Name (PPRT, Package ()"); 1335261268Sjhb dsdt_line("{"); 1336266125Sjhb pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 1337266125Sjhb dsdt_line("})"); 1338266125Sjhb dsdt_line("Name (APRT, Package ()"); 1339266125Sjhb dsdt_line("{"); 1340266125Sjhb pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1341266125Sjhb dsdt_line("})"); 1342266125Sjhb dsdt_line("Method (_PRT, 0, NotSerialized)"); 1343266125Sjhb dsdt_line("{"); 1344266125Sjhb dsdt_line(" If (PICM)"); 1345266125Sjhb dsdt_line(" {"); 1346266125Sjhb dsdt_line(" Return (APRT)"); 1347266125Sjhb dsdt_line(" }"); 1348266125Sjhb dsdt_line(" Else"); 1349266125Sjhb dsdt_line(" {"); 1350266125Sjhb dsdt_line(" Return (PPRT)"); 1351266125Sjhb dsdt_line(" }"); 1352266125Sjhb dsdt_line("}"); 1353261268Sjhb dsdt_unindent(2); 1354261268Sjhb } 1355260206Sjhb 1356260206Sjhb dsdt_indent(2); 1357260206Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1358261904Sneel si = &bi->slotinfo[slot]; 1359260206Sjhb for (func = 0; func < MAXFUNCS; func++) { 1360261904Sneel pi = si->si_funcs[func].fi_devi; 1361260206Sjhb if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1362260206Sjhb pi->pi_d->pe_write_dsdt(pi); 1363260206Sjhb } 1364260206Sjhb } 1365260206Sjhb dsdt_unindent(2); 1366261904Sneeldone: 1367260206Sjhb dsdt_line(" }"); 1368260206Sjhb} 1369260206Sjhb 1370261904Sneelvoid 1371261904Sneelpci_write_dsdt(void) 1372261904Sneel{ 1373261904Sneel int bus; 1374261904Sneel 1375266125Sjhb dsdt_indent(1); 1376266125Sjhb dsdt_line("Name (PICM, 0x00)"); 1377266125Sjhb dsdt_line("Method (_PIC, 1, NotSerialized)"); 1378266125Sjhb dsdt_line("{"); 1379266125Sjhb dsdt_line(" Store (Arg0, PICM)"); 1380266125Sjhb dsdt_line("}"); 1381266125Sjhb dsdt_line(""); 1382266125Sjhb dsdt_line("Scope (_SB)"); 1383266125Sjhb dsdt_line("{"); 1384261904Sneel for (bus = 0; bus < MAXBUSES; bus++) 1385261904Sneel pci_bus_write_dsdt(bus); 1386266125Sjhb dsdt_line("}"); 1387266125Sjhb dsdt_unindent(1); 1388261904Sneel} 1389261904Sneel 1390221828Sgrehanint 1391265211Sneelpci_bus_configured(int bus) 1392265211Sneel{ 1393265211Sneel assert(bus >= 0 && bus < MAXBUSES); 1394265211Sneel return (pci_businfo[bus] != NULL); 1395265211Sneel} 1396265211Sneel 1397265211Sneelint 1398221828Sgrehanpci_msi_enabled(struct pci_devinst *pi) 1399221828Sgrehan{ 1400221828Sgrehan return (pi->pi_msi.enabled); 1401221828Sgrehan} 1402221828Sgrehan 1403221828Sgrehanint 1404259482Sneelpci_msi_maxmsgnum(struct pci_devinst *pi) 1405221828Sgrehan{ 1406221828Sgrehan if (pi->pi_msi.enabled) 1407259482Sneel 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 */ 1436259482Sneel vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1437246109Sneel } 1438246109Sneel} 1439246109Sneel 1440246109Sneelvoid 1441259482Sneelpci_generate_msi(struct pci_devinst *pi, int index) 1442221828Sgrehan{ 1443221828Sgrehan 1444259482Sneel if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 1445259482Sneel vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 1446259482Sneel pi->pi_msi.msg_data + index); 1447221828Sgrehan } 1448221828Sgrehan} 1449221828Sgrehan 1450261268Sjhbstatic bool 1451261268Sjhbpci_lintr_permitted(struct pci_devinst *pi) 1452261268Sjhb{ 1453261268Sjhb uint16_t cmd; 1454261268Sjhb 1455261268Sjhb cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 1456261268Sjhb return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 1457261268Sjhb (cmd & PCIM_CMD_INTxDIS))); 1458261268Sjhb} 1459261268Sjhb 1460266125Sjhbvoid 1461261268Sjhbpci_lintr_request(struct pci_devinst *pi) 1462234938Sgrehan{ 1463261904Sneel struct businfo *bi; 1464261268Sjhb struct slotinfo *si; 1465266125Sjhb int bestpin, bestcount, pin; 1466234938Sgrehan 1467261904Sneel bi = pci_businfo[pi->pi_bus]; 1468261904Sneel assert(bi != NULL); 1469261904Sneel 1470261268Sjhb /* 1471266125Sjhb * Just allocate a pin from our slot. The pin will be 1472266125Sjhb * assigned IRQs later when interrupts are routed. 1473261268Sjhb */ 1474261904Sneel si = &bi->slotinfo[pi->pi_slot]; 1475261268Sjhb bestpin = 0; 1476261268Sjhb bestcount = si->si_intpins[0].ii_count; 1477261268Sjhb for (pin = 1; pin < 4; pin++) { 1478261268Sjhb if (si->si_intpins[pin].ii_count < bestcount) { 1479261268Sjhb bestpin = pin; 1480261268Sjhb bestcount = si->si_intpins[pin].ii_count; 1481261268Sjhb } 1482261268Sjhb } 1483234938Sgrehan 1484261268Sjhb si->si_intpins[bestpin].ii_count++; 1485261268Sjhb pi->pi_lintr.pin = bestpin + 1; 1486261268Sjhb pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1487234938Sgrehan} 1488234938Sgrehan 1489266125Sjhbstatic void 1490266125Sjhbpci_lintr_route(struct pci_devinst *pi) 1491266125Sjhb{ 1492266125Sjhb struct businfo *bi; 1493266125Sjhb struct intxinfo *ii; 1494266125Sjhb 1495266125Sjhb if (pi->pi_lintr.pin == 0) 1496266125Sjhb return; 1497266125Sjhb 1498266125Sjhb bi = pci_businfo[pi->pi_bus]; 1499266125Sjhb assert(bi != NULL); 1500266125Sjhb ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1501266125Sjhb 1502266125Sjhb /* 1503266125Sjhb * Attempt to allocate an I/O APIC pin for this intpin if one 1504266125Sjhb * is not yet assigned. 1505266125Sjhb */ 1506266125Sjhb if (ii->ii_ioapic_irq == 0) 1507266125Sjhb ii->ii_ioapic_irq = ioapic_pci_alloc_irq(); 1508266125Sjhb assert(ii->ii_ioapic_irq > 0); 1509266125Sjhb 1510266125Sjhb /* 1511266125Sjhb * Attempt to allocate a PIRQ pin for this intpin if one is 1512266125Sjhb * not yet assigned. 1513266125Sjhb */ 1514266125Sjhb if (ii->ii_pirq_pin == 0) 1515266125Sjhb ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx); 1516266125Sjhb assert(ii->ii_pirq_pin > 0); 1517266125Sjhb 1518266125Sjhb pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1519266125Sjhb pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1520266125Sjhb pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 1521266125Sjhb} 1522266125Sjhb 1523234938Sgrehanvoid 1524234938Sgrehanpci_lintr_assert(struct pci_devinst *pi) 1525234938Sgrehan{ 1526234938Sgrehan 1527261268Sjhb assert(pi->pi_lintr.pin > 0); 1528258494Sneel 1529261268Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1530261268Sjhb if (pi->pi_lintr.state == IDLE) { 1531261268Sjhb if (pci_lintr_permitted(pi)) { 1532261268Sjhb pi->pi_lintr.state = ASSERTED; 1533266125Sjhb pci_irq_assert(pi); 1534261268Sjhb } else 1535261268Sjhb pi->pi_lintr.state = PENDING; 1536258494Sneel } 1537261268Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1538234938Sgrehan} 1539234938Sgrehan 1540234938Sgrehanvoid 1541234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi) 1542234938Sgrehan{ 1543234938Sgrehan 1544261268Sjhb assert(pi->pi_lintr.pin > 0); 1545258494Sneel 1546261268Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1547261268Sjhb if (pi->pi_lintr.state == ASSERTED) { 1548261268Sjhb pi->pi_lintr.state = IDLE; 1549266125Sjhb pci_irq_deassert(pi); 1550261268Sjhb } else if (pi->pi_lintr.state == PENDING) 1551261268Sjhb pi->pi_lintr.state = IDLE; 1552261268Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1553261268Sjhb} 1554261268Sjhb 1555261268Sjhbstatic void 1556261268Sjhbpci_lintr_update(struct pci_devinst *pi) 1557261268Sjhb{ 1558261268Sjhb 1559261268Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1560261268Sjhb if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1561266125Sjhb pci_irq_deassert(pi); 1562261268Sjhb pi->pi_lintr.state = PENDING; 1563261268Sjhb } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 1564261268Sjhb pi->pi_lintr.state = ASSERTED; 1565266125Sjhb pci_irq_assert(pi); 1566258494Sneel } 1567261268Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1568234938Sgrehan} 1569234938Sgrehan 1570261268Sjhbint 1571261904Sneelpci_count_lintr(int bus) 1572261268Sjhb{ 1573261268Sjhb int count, slot, pin; 1574261904Sneel struct slotinfo *slotinfo; 1575261268Sjhb 1576261268Sjhb count = 0; 1577261904Sneel if (pci_businfo[bus] != NULL) { 1578261904Sneel for (slot = 0; slot < MAXSLOTS; slot++) { 1579261904Sneel slotinfo = &pci_businfo[bus]->slotinfo[slot]; 1580261904Sneel for (pin = 0; pin < 4; pin++) { 1581261904Sneel if (slotinfo->si_intpins[pin].ii_count != 0) 1582261904Sneel count++; 1583261904Sneel } 1584261268Sjhb } 1585261268Sjhb } 1586261268Sjhb return (count); 1587261268Sjhb} 1588261268Sjhb 1589261268Sjhbvoid 1590261904Sneelpci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 1591261268Sjhb{ 1592261904Sneel struct businfo *bi; 1593261904Sneel struct slotinfo *si; 1594261268Sjhb struct intxinfo *ii; 1595261268Sjhb int slot, pin; 1596261268Sjhb 1597261904Sneel if ((bi = pci_businfo[bus]) == NULL) 1598261904Sneel return; 1599261904Sneel 1600261268Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1601261904Sneel si = &bi->slotinfo[slot]; 1602261268Sjhb for (pin = 0; pin < 4; pin++) { 1603261904Sneel ii = &si->si_intpins[pin]; 1604261268Sjhb if (ii->ii_count != 0) 1605266125Sjhb cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1606266125Sjhb ii->ii_ioapic_irq, arg); 1607261268Sjhb } 1608261268Sjhb } 1609261268Sjhb} 1610261268Sjhb 1611239085Sneel/* 1612239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device. 1613239085Sneel * Return 0 otherwise. 1614239085Sneel */ 1615239085Sneelstatic int 1616261904Sneelpci_emul_is_mfdev(int bus, int slot) 1617239085Sneel{ 1618261904Sneel struct businfo *bi; 1619261904Sneel struct slotinfo *si; 1620239085Sneel int f, numfuncs; 1621234938Sgrehan 1622239085Sneel numfuncs = 0; 1623261904Sneel if ((bi = pci_businfo[bus]) != NULL) { 1624261904Sneel si = &bi->slotinfo[slot]; 1625261904Sneel for (f = 0; f < MAXFUNCS; f++) { 1626261904Sneel if (si->si_funcs[f].fi_devi != NULL) { 1627261904Sneel numfuncs++; 1628261904Sneel } 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 1639261904Sneelpci_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) { 1644261904Sneel 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 1663281946Sneelstatic void 1664281946Sneelpci_emul_cmdsts_write(struct pci_devinst *pi, int coff, uint32_t new, int bytes) 1665249321Sneel{ 1666281946Sneel int i, rshift; 1667281946Sneel uint32_t cmd, cmd2, changed, old, readonly; 1668249321Sneel 1669281946Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 1670249321Sneel 1671249321Sneel /* 1672281946Sneel * From PCI Local Bus Specification 3.0 sections 6.2.2 and 6.2.3. 1673281946Sneel * 1674281946Sneel * XXX Bits 8, 11, 12, 13, 14 and 15 in the status register are 1675281946Sneel * 'write 1 to clear'. However these bits are not set to '1' by 1676281946Sneel * any device emulation so it is simpler to treat them as readonly. 1677249321Sneel */ 1678281946Sneel rshift = (coff & 0x3) * 8; 1679281946Sneel readonly = 0xFFFFF880 >> rshift; 1680249321Sneel 1681281946Sneel old = CFGREAD(pi, coff, bytes); 1682281946Sneel new &= ~readonly; 1683281946Sneel new |= (old & readonly); 1684281946Sneel CFGWRITE(pi, coff, new, bytes); /* update config */ 1685249321Sneel 1686281946Sneel cmd2 = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ 1687281946Sneel changed = cmd ^ cmd2; 1688281946Sneel 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 */ 1693257767Sneel 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? */ 1700281946Sneel 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? */ 1710281946Sneel 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 } 1721261268Sjhb 1722261268Sjhb /* 1723261268Sjhb * If INTx has been unmasked and is pending, assert the 1724261268Sjhb * interrupt. 1725261268Sjhb */ 1726261268Sjhb pci_lintr_update(pi); 1727249321Sneel} 1728249321Sneel 1729269700Sneelstatic void 1730269700Sneelpci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, 1731269700Sneel int coff, int bytes, uint32_t *eax) 1732221828Sgrehan{ 1733261904Sneel struct businfo *bi; 1734261904Sneel struct slotinfo *si; 1735221828Sgrehan struct pci_devinst *pi; 1736221828Sgrehan struct pci_devemu *pe; 1737269700Sneel int idx, needcfg; 1738249321Sneel uint64_t addr, bar, mask; 1739221828Sgrehan 1740269700Sneel if ((bi = pci_businfo[bus]) != NULL) { 1741269700Sneel si = &bi->slotinfo[slot]; 1742269700Sneel pi = si->si_funcs[func].fi_devi; 1743261904Sneel } else 1744242170Sneel pi = NULL; 1745242170Sneel 1746239085Sneel /* 1747269700Sneel * Just return if there is no device at this slot:func or if the 1748269700Sneel * the guest is doing an un-aligned access. 1749239085Sneel */ 1750269700Sneel if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || 1751269700Sneel (coff & (bytes - 1)) != 0) { 1752221828Sgrehan if (in) 1753221828Sgrehan *eax = 0xffffffff; 1754269700Sneel return; 1755221828Sgrehan } 1756221828Sgrehan 1757269700Sneel /* 1758269700Sneel * Ignore all writes beyond the standard config space and return all 1759269700Sneel * ones on reads. 1760269700Sneel */ 1761269700Sneel if (coff >= PCI_REGMAX + 1) { 1762269700Sneel if (in) { 1763269700Sneel *eax = 0xffffffff; 1764269700Sneel /* 1765269700Sneel * Extended capabilities begin at offset 256 in config 1766269700Sneel * space. Absence of extended capabilities is signaled 1767269700Sneel * with all 0s in the extended capability header at 1768269700Sneel * offset 256. 1769269700Sneel */ 1770269700Sneel if (coff <= PCI_REGMAX + 4) 1771269700Sneel *eax = 0x00000000; 1772269700Sneel } 1773269700Sneel return; 1774269700Sneel } 1775269700Sneel 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) { 1784269700Sneel needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, 1785269700Sneel eax); 1786239085Sneel } else { 1787239085Sneel needcfg = 1; 1788239085Sneel } 1789221828Sgrehan 1790281946Sneel if (needcfg) 1791281946Sneel *eax = CFGREAD(pi, coff, bytes); 1792239085Sneel 1793269700Sneel 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) 1798269700Sneel 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) 1809269700Sneel 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); 1861281946Sneel } else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) { 1862281946Sneel pci_emul_cmdsts_write(pi, coff, *eax, bytes); 1863221828Sgrehan } else { 1864221828Sgrehan CFGWRITE(pi, coff, *eax, bytes); 1865221828Sgrehan } 1866221828Sgrehan } 1867269700Sneel} 1868221828Sgrehan 1869269700Sneelstatic int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 1870269700Sneel 1871269700Sneelstatic int 1872269700Sneelpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1873269700Sneel uint32_t *eax, void *arg) 1874269700Sneel{ 1875269700Sneel uint32_t x; 1876269700Sneel 1877269700Sneel if (bytes != 4) { 1878269700Sneel if (in) 1879269700Sneel *eax = (bytes == 2) ? 0xffff : 0xff; 1880269700Sneel return (0); 1881269700Sneel } 1882269700Sneel 1883269700Sneel if (in) { 1884269700Sneel x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; 1885269700Sneel if (cfgenable) 1886269700Sneel x |= CONF1_ENABLE; 1887269700Sneel *eax = x; 1888269700Sneel } else { 1889269700Sneel x = *eax; 1890269700Sneel cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 1891269700Sneel cfgoff = x & PCI_REGMAX; 1892269700Sneel cfgfunc = (x >> 8) & PCI_FUNCMAX; 1893269700Sneel cfgslot = (x >> 11) & PCI_SLOTMAX; 1894269700Sneel cfgbus = (x >> 16) & PCI_BUSMAX; 1895269700Sneel } 1896269700Sneel 1897221828Sgrehan return (0); 1898221828Sgrehan} 1899269700SneelINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 1900221828Sgrehan 1901269700Sneelstatic int 1902269700Sneelpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1903269700Sneel uint32_t *eax, void *arg) 1904269700Sneel{ 1905269700Sneel int coff; 1906269700Sneel 1907269700Sneel assert(bytes == 1 || bytes == 2 || bytes == 4); 1908269700Sneel 1909269700Sneel coff = cfgoff + (port - CONF1_DATA_PORT); 1910269700Sneel if (cfgenable) { 1911269700Sneel pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, 1912269700Sneel eax); 1913269700Sneel } else { 1914269700Sneel /* Ignore accesses to cfgdata if not enabled by cfgaddr */ 1915269700Sneel if (in) 1916269700Sneel *eax = 0xffffffff; 1917269700Sneel } 1918269700Sneel return (0); 1919269700Sneel} 1920269700Sneel 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 */ 1931261904Sneel#define DIOSZ 8 1932241744Sgrehan#define DMEMSZ 4096 1933221828Sgrehanstruct pci_emul_dsoftc { 1934241744Sgrehan uint8_t ioregs[DIOSZ]; 1935282335Sneel 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 1947264770Sdelphij 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 1964282335Sneel error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ); 1965282335Sneel assert(error == 0); 1966282335Sneel 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)) 1998259482Sneel pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 1999241744Sgrehan 2000241744Sgrehan if (value == 0xabcdef) { 2001259482Sneel for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 2002241744Sgrehan pci_generate_msi(pi, i); 2003241744Sgrehan } 2004221828Sgrehan } 2005221828Sgrehan 2006282335Sneel 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 2013282335Sneel i = baridx - 1; /* 'memregs' index */ 2014282335Sneel 2015241744Sgrehan if (size == 1) { 2016282335Sneel sc->memregs[i][offset] = value; 2017241744Sgrehan } else if (size == 2) { 2018282335Sneel *(uint16_t *)&sc->memregs[i][offset] = value; 2019241744Sgrehan } else if (size == 4) { 2020282335Sneel *(uint32_t *)&sc->memregs[i][offset] = value; 2021241744Sgrehan } else if (size == 8) { 2022282335Sneel *(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 2032299676Spfg if (baridx > 2 || baridx < 0) { 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; 2043282335Sneel 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 2052297472Spfg value = 0; 2053241744Sgrehan if (size == 1) { 2054241744Sgrehan value = sc->ioregs[offset]; 2055241744Sgrehan } else if (size == 2) { 2056241744Sgrehan value = *(uint16_t *) &sc->ioregs[offset]; 2057241744Sgrehan } else if (size == 4) { 2058241744Sgrehan value = *(uint32_t *) &sc->ioregs[offset]; 2059241744Sgrehan } else { 2060241744Sgrehan printf("dior: ior unknown size %d\n", size); 2061241744Sgrehan } 2062221828Sgrehan } 2063282335Sneel 2064282335Sneel if (baridx == 1 || baridx == 2) { 2065241744Sgrehan if (offset + size > DMEMSZ) { 2066241744Sgrehan printf("dior: memr too large, offset %ld size %d\n", 2067241744Sgrehan offset, size); 2068241744Sgrehan return (0); 2069241744Sgrehan } 2070282335Sneel 2071282335Sneel i = baridx - 1; /* 'memregs' index */ 2072282335Sneel 2073241744Sgrehan if (size == 1) { 2074282335Sneel value = sc->memregs[i][offset]; 2075241744Sgrehan } else if (size == 2) { 2076282335Sneel value = *(uint16_t *) &sc->memregs[i][offset]; 2077241744Sgrehan } else if (size == 4) { 2078282335Sneel value = *(uint32_t *) &sc->memregs[i][offset]; 2079241744Sgrehan } else if (size == 8) { 2080282335Sneel value = *(uint64_t *) &sc->memregs[i][offset]; 2081241744Sgrehan } else { 2082241744Sgrehan printf("dior: ior unknown size %d\n", size); 2083241744Sgrehan } 2084221828Sgrehan } 2085221828Sgrehan 2086241744Sgrehan 2087299676Spfg if (baridx > 2 || baridx < 0) { 2088241744Sgrehan printf("dior: unknown bar idx %d\n", baridx); 2089241744Sgrehan return (0); 2090241744Sgrehan } 2091241744Sgrehan 2092221828Sgrehan return (value); 2093221828Sgrehan} 2094221828Sgrehan 2095221828Sgrehanstruct pci_devemu pci_dummy = { 2096221828Sgrehan .pe_emu = "dummy", 2097221828Sgrehan .pe_init = pci_emul_dinit, 2098241744Sgrehan .pe_barwrite = pci_emul_diow, 2099241744Sgrehan .pe_barread = pci_emul_dior 2100221828Sgrehan}; 2101221828SgrehanPCI_EMUL_SET(pci_dummy); 2102221828Sgrehan 2103221828Sgrehan#endif /* PCI_EMUL_TEST */ 2104