1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD$ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD$"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34249321Sneel#include <sys/errno.h> 35221828Sgrehan 36221828Sgrehan#include <ctype.h> 37267393Sjhb#include <pthread.h> 38221828Sgrehan#include <stdio.h> 39221828Sgrehan#include <stdlib.h> 40221828Sgrehan#include <string.h> 41221828Sgrehan#include <strings.h> 42221828Sgrehan#include <assert.h> 43249321Sneel#include <stdbool.h> 44221828Sgrehan 45221828Sgrehan#include <machine/vmm.h> 46221828Sgrehan#include <vmmapi.h> 47221828Sgrehan 48261265Sjhb#include "acpi.h" 49244167Sgrehan#include "bhyverun.h" 50221828Sgrehan#include "inout.h" 51267393Sjhb#include "ioapic.h" 52241744Sgrehan#include "mem.h" 53221828Sgrehan#include "pci_emul.h" 54268972Sjhb#include "pci_irq.h" 55261265Sjhb#include "pci_lpc.h" 56221828Sgrehan 57221828Sgrehan#define CONF1_ADDR_PORT 0x0cf8 58221828Sgrehan#define CONF1_DATA_PORT 0x0cfc 59221828Sgrehan 60252331Sgrehan#define CONF1_ENABLE 0x80000000ul 61252331Sgrehan 62221828Sgrehan#define CFGWRITE(pi,off,val,b) \ 63221828Sgrehando { \ 64221828Sgrehan if ((b) == 1) { \ 65221828Sgrehan pci_set_cfgdata8((pi),(off),(val)); \ 66221828Sgrehan } else if ((b) == 2) { \ 67221828Sgrehan pci_set_cfgdata16((pi),(off),(val)); \ 68221828Sgrehan } else { \ 69221828Sgrehan pci_set_cfgdata32((pi),(off),(val)); \ 70221828Sgrehan } \ 71221828Sgrehan} while (0) 72221828Sgrehan 73268887Sjhb#define MAXBUSES (PCI_BUSMAX + 1) 74239085Sneel#define MAXSLOTS (PCI_SLOTMAX + 1) 75239085Sneel#define MAXFUNCS (PCI_FUNCMAX + 1) 76221828Sgrehan 77267393Sjhbstruct funcinfo { 78267393Sjhb char *fi_name; 79267393Sjhb char *fi_param; 80267393Sjhb struct pci_devinst *fi_devi; 81267393Sjhb}; 82221828Sgrehan 83267393Sjhbstruct intxinfo { 84267393Sjhb int ii_count; 85268972Sjhb int ii_pirq_pin; 86267393Sjhb int ii_ioapic_irq; 87267393Sjhb}; 88267393Sjhb 89267393Sjhbstruct slotinfo { 90267393Sjhb struct intxinfo si_intpins[4]; 91267393Sjhb struct funcinfo si_funcs[MAXFUNCS]; 92268887Sjhb}; 93267393Sjhb 94268887Sjhbstruct businfo { 95268887Sjhb uint16_t iobase, iolimit; /* I/O window */ 96268887Sjhb uint32_t membase32, memlimit32; /* mmio window below 4GB */ 97268887Sjhb uint64_t membase64, memlimit64; /* mmio window above 4GB */ 98268887Sjhb struct slotinfo slotinfo[MAXSLOTS]; 99268887Sjhb}; 100268887Sjhb 101268887Sjhbstatic struct businfo *pci_businfo[MAXBUSES]; 102268887Sjhb 103221828SgrehanSET_DECLARE(pci_devemu_set, struct pci_devemu); 104221828Sgrehan 105221828Sgrehanstatic uint64_t pci_emul_iobase; 106221828Sgrehanstatic uint64_t pci_emul_membase32; 107221828Sgrehanstatic uint64_t pci_emul_membase64; 108221828Sgrehan 109221828Sgrehan#define PCI_EMUL_IOBASE 0x2000 110221828Sgrehan#define PCI_EMUL_IOLIMIT 0x10000 111221828Sgrehan 112270159Sgrehan#define PCI_EMUL_ECFG_BASE 0xE0000000 /* 3.5GB */ 113270159Sgrehan#define PCI_EMUL_ECFG_SIZE (MAXBUSES * 1024 * 1024) /* 1MB per bus */ 114270159SgrehanSYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE); 115221828Sgrehan 116270159Sgrehan#define PCI_EMUL_MEMLIMIT32 PCI_EMUL_ECFG_BASE 117270159Sgrehan 118221828Sgrehan#define PCI_EMUL_MEMBASE64 0xD000000000UL 119221828Sgrehan#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL 120221828Sgrehan 121249916Sneelstatic struct pci_devemu *pci_emul_finddev(char *name); 122270159Sgrehanstatic void pci_lintr_route(struct pci_devinst *pi); 123270159Sgrehanstatic void pci_lintr_update(struct pci_devinst *pi); 124270159Sgrehanstatic void pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, 125270159Sgrehan int func, int coff, int bytes, uint32_t *val); 126249916Sneel 127221828Sgrehan/* 128221828Sgrehan * I/O access 129221828Sgrehan */ 130221828Sgrehan 131221828Sgrehan/* 132221828Sgrehan * Slot options are in the form: 133221828Sgrehan * 134268887Sjhb * <bus>:<slot>:<func>,<emul>[,<config>] 135239085Sneel * <slot>[:<func>],<emul>[,<config>] 136221828Sgrehan * 137221828Sgrehan * slot is 0..31 138239085Sneel * func is 0..7 139221828Sgrehan * emul is a string describing the type of PCI device e.g. virtio-net 140221828Sgrehan * config is an optional string, depending on the device, that can be 141221828Sgrehan * used for configuration. 142221828Sgrehan * Examples are: 143221828Sgrehan * 1,virtio-net,tap0 144239085Sneel * 3:0,dummy 145221828Sgrehan */ 146221828Sgrehanstatic void 147221828Sgrehanpci_parse_slot_usage(char *aopt) 148221828Sgrehan{ 149249916Sneel 150249916Sneel fprintf(stderr, "Invalid PCI slot info field \"%s\"\n", aopt); 151221828Sgrehan} 152221828Sgrehan 153249916Sneelint 154267341Sjhbpci_parse_slot(char *opt) 155221828Sgrehan{ 156268887Sjhb struct businfo *bi; 157268887Sjhb struct slotinfo *si; 158268887Sjhb char *emul, *config, *str, *cp; 159268887Sjhb int error, bnum, snum, fnum; 160221828Sgrehan 161249916Sneel error = -1; 162268887Sjhb str = strdup(opt); 163239085Sneel 164268887Sjhb emul = config = NULL; 165268887Sjhb if ((cp = strchr(str, ',')) != NULL) { 166268887Sjhb *cp = '\0'; 167268887Sjhb emul = cp + 1; 168268887Sjhb if ((cp = strchr(emul, ',')) != NULL) { 169268887Sjhb *cp = '\0'; 170268887Sjhb config = cp + 1; 171268887Sjhb } 172268887Sjhb } else { 173249916Sneel pci_parse_slot_usage(opt); 174249916Sneel goto done; 175221828Sgrehan } 176221828Sgrehan 177268887Sjhb /* <bus>:<slot>:<func> */ 178268887Sjhb if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { 179268887Sjhb bnum = 0; 180268887Sjhb /* <slot>:<func> */ 181268887Sjhb if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { 182268887Sjhb fnum = 0; 183268887Sjhb /* <slot> */ 184268887Sjhb if (sscanf(str, "%d", &snum) != 1) { 185268887Sjhb snum = -1; 186268887Sjhb } 187268887Sjhb } 188268887Sjhb } 189249916Sneel 190268887Sjhb if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || 191268887Sjhb fnum < 0 || fnum >= MAXFUNCS) { 192249916Sneel pci_parse_slot_usage(opt); 193249916Sneel goto done; 194221828Sgrehan } 195249916Sneel 196268887Sjhb if (pci_businfo[bnum] == NULL) 197268887Sjhb pci_businfo[bnum] = calloc(1, sizeof(struct businfo)); 198268887Sjhb 199268887Sjhb bi = pci_businfo[bnum]; 200268887Sjhb si = &bi->slotinfo[snum]; 201268887Sjhb 202268887Sjhb if (si->si_funcs[fnum].fi_name != NULL) { 203249916Sneel fprintf(stderr, "pci slot %d:%d already occupied!\n", 204249916Sneel snum, fnum); 205249916Sneel goto done; 206249916Sneel } 207249916Sneel 208249916Sneel if (pci_emul_finddev(emul) == NULL) { 209249916Sneel fprintf(stderr, "pci slot %d:%d: unknown device \"%s\"\n", 210249916Sneel snum, fnum, emul); 211249916Sneel goto done; 212249916Sneel } 213249916Sneel 214249916Sneel error = 0; 215268887Sjhb si->si_funcs[fnum].fi_name = emul; 216268887Sjhb si->si_funcs[fnum].fi_param = config; 217249916Sneel 218249916Sneeldone: 219249916Sneel if (error) 220268887Sjhb free(str); 221249916Sneel 222249916Sneel return (error); 223221828Sgrehan} 224221828Sgrehan 225221828Sgrehanstatic int 226246109Sneelpci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) 227246109Sneel{ 228246109Sneel 229246109Sneel if (offset < pi->pi_msix.pba_offset) 230246109Sneel return (0); 231246109Sneel 232246109Sneel if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 233246109Sneel return (0); 234246109Sneel } 235246109Sneel 236246109Sneel return (1); 237246109Sneel} 238246109Sneel 239246109Sneelint 240246109Sneelpci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, 241246109Sneel uint64_t value) 242246109Sneel{ 243246109Sneel int msix_entry_offset; 244246109Sneel int tab_index; 245246109Sneel char *dest; 246246109Sneel 247246109Sneel /* support only 4 or 8 byte writes */ 248246109Sneel if (size != 4 && size != 8) 249246109Sneel return (-1); 250246109Sneel 251246109Sneel /* 252246109Sneel * Return if table index is beyond what device supports 253246109Sneel */ 254246109Sneel tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 255246109Sneel if (tab_index >= pi->pi_msix.table_count) 256246109Sneel return (-1); 257246109Sneel 258246109Sneel msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 259246109Sneel 260246109Sneel /* support only aligned writes */ 261246109Sneel if ((msix_entry_offset % size) != 0) 262246109Sneel return (-1); 263246109Sneel 264246109Sneel dest = (char *)(pi->pi_msix.table + tab_index); 265246109Sneel dest += msix_entry_offset; 266246109Sneel 267246109Sneel if (size == 4) 268246109Sneel *((uint32_t *)dest) = value; 269246109Sneel else 270246109Sneel *((uint64_t *)dest) = value; 271246109Sneel 272246109Sneel return (0); 273246109Sneel} 274246109Sneel 275246109Sneeluint64_t 276246109Sneelpci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) 277246109Sneel{ 278246109Sneel char *dest; 279246109Sneel int msix_entry_offset; 280246109Sneel int tab_index; 281246109Sneel uint64_t retval = ~0; 282246109Sneel 283254965Sneel /* 284254965Sneel * The PCI standard only allows 4 and 8 byte accesses to the MSI-X 285254965Sneel * table but we also allow 1 byte access to accomodate reads from 286254965Sneel * ddb. 287254965Sneel */ 288254965Sneel if (size != 1 && size != 4 && size != 8) 289246109Sneel return (retval); 290246109Sneel 291246109Sneel msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 292246109Sneel 293246109Sneel /* support only aligned reads */ 294246109Sneel if ((msix_entry_offset % size) != 0) { 295246109Sneel return (retval); 296246109Sneel } 297246109Sneel 298246109Sneel tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 299246109Sneel 300246109Sneel if (tab_index < pi->pi_msix.table_count) { 301246109Sneel /* valid MSI-X Table access */ 302246109Sneel dest = (char *)(pi->pi_msix.table + tab_index); 303246109Sneel dest += msix_entry_offset; 304246109Sneel 305254965Sneel if (size == 1) 306254965Sneel retval = *((uint8_t *)dest); 307254965Sneel else if (size == 4) 308246109Sneel retval = *((uint32_t *)dest); 309246109Sneel else 310246109Sneel retval = *((uint64_t *)dest); 311246109Sneel } else if (pci_valid_pba_offset(pi, offset)) { 312246109Sneel /* return 0 for PBA access */ 313246109Sneel retval = 0; 314246109Sneel } 315246109Sneel 316246109Sneel return (retval); 317246109Sneel} 318246109Sneel 319246190Sneelint 320246190Sneelpci_msix_table_bar(struct pci_devinst *pi) 321246190Sneel{ 322246190Sneel 323246190Sneel if (pi->pi_msix.table != NULL) 324246190Sneel return (pi->pi_msix.table_bar); 325246190Sneel else 326246190Sneel return (-1); 327246190Sneel} 328246190Sneel 329246190Sneelint 330246190Sneelpci_msix_pba_bar(struct pci_devinst *pi) 331246190Sneel{ 332246190Sneel 333246190Sneel if (pi->pi_msix.table != NULL) 334246190Sneel return (pi->pi_msix.pba_bar); 335246190Sneel else 336246190Sneel return (-1); 337246190Sneel} 338246190Sneel 339246109Sneelstatic int 340241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 341241744Sgrehan uint32_t *eax, void *arg) 342221828Sgrehan{ 343221828Sgrehan struct pci_devinst *pdi = arg; 344221828Sgrehan struct pci_devemu *pe = pdi->pi_d; 345241744Sgrehan uint64_t offset; 346241744Sgrehan int i; 347221828Sgrehan 348221828Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 349221828Sgrehan if (pdi->pi_bar[i].type == PCIBAR_IO && 350221828Sgrehan port >= pdi->pi_bar[i].addr && 351246109Sneel port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 352221828Sgrehan offset = port - pdi->pi_bar[i].addr; 353221828Sgrehan if (in) 354241744Sgrehan *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, 355241744Sgrehan offset, bytes); 356221828Sgrehan else 357241744Sgrehan (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, 358241744Sgrehan bytes, *eax); 359221828Sgrehan return (0); 360221828Sgrehan } 361221828Sgrehan } 362221828Sgrehan return (-1); 363221828Sgrehan} 364221828Sgrehan 365221828Sgrehanstatic int 366241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 367241744Sgrehan int size, uint64_t *val, void *arg1, long arg2) 368241744Sgrehan{ 369241744Sgrehan struct pci_devinst *pdi = arg1; 370241744Sgrehan struct pci_devemu *pe = pdi->pi_d; 371241744Sgrehan uint64_t offset; 372241744Sgrehan int bidx = (int) arg2; 373241744Sgrehan 374241744Sgrehan assert(bidx <= PCI_BARMAX); 375241744Sgrehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 376241744Sgrehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 377241744Sgrehan assert(addr >= pdi->pi_bar[bidx].addr && 378241744Sgrehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 379241744Sgrehan 380241744Sgrehan offset = addr - pdi->pi_bar[bidx].addr; 381241744Sgrehan 382268887Sjhb if (dir == MEM_F_WRITE) { 383268887Sjhb if (size == 8) { 384268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 385268887Sjhb 4, *val & 0xffffffff); 386268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 387268887Sjhb 4, *val >> 32); 388268887Sjhb } else { 389268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 390268887Sjhb size, *val); 391268887Sjhb } 392268887Sjhb } else { 393268887Sjhb if (size == 8) { 394268887Sjhb *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 395268887Sjhb offset, 4); 396268887Sjhb *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 397268887Sjhb offset + 4, 4) << 32; 398268887Sjhb } else { 399268887Sjhb *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 400268887Sjhb offset, size); 401268887Sjhb } 402268887Sjhb } 403241744Sgrehan 404241744Sgrehan return (0); 405241744Sgrehan} 406241744Sgrehan 407241744Sgrehan 408241744Sgrehanstatic int 409221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 410221828Sgrehan uint64_t *addr) 411221828Sgrehan{ 412221828Sgrehan uint64_t base; 413221828Sgrehan 414221828Sgrehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 415221828Sgrehan 416221828Sgrehan base = roundup2(*baseptr, size); 417221828Sgrehan 418221828Sgrehan if (base + size <= limit) { 419221828Sgrehan *addr = base; 420221828Sgrehan *baseptr = base + size; 421221828Sgrehan return (0); 422221828Sgrehan } else 423221828Sgrehan return (-1); 424221828Sgrehan} 425221828Sgrehan 426221828Sgrehanint 427241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 428241744Sgrehan uint64_t size) 429221828Sgrehan{ 430241744Sgrehan 431241744Sgrehan return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); 432241744Sgrehan} 433241744Sgrehan 434249321Sneel/* 435249321Sneel * Register (or unregister) the MMIO or I/O region associated with the BAR 436249321Sneel * register 'idx' of an emulated pci device. 437249321Sneel */ 438249321Sneelstatic void 439249321Sneelmodify_bar_registration(struct pci_devinst *pi, int idx, int registration) 440249321Sneel{ 441249321Sneel int error; 442249321Sneel struct inout_port iop; 443249321Sneel struct mem_range mr; 444249321Sneel 445249321Sneel switch (pi->pi_bar[idx].type) { 446249321Sneel case PCIBAR_IO: 447249321Sneel bzero(&iop, sizeof(struct inout_port)); 448249321Sneel iop.name = pi->pi_name; 449249321Sneel iop.port = pi->pi_bar[idx].addr; 450249321Sneel iop.size = pi->pi_bar[idx].size; 451249321Sneel if (registration) { 452249321Sneel iop.flags = IOPORT_F_INOUT; 453249321Sneel iop.handler = pci_emul_io_handler; 454249321Sneel iop.arg = pi; 455249321Sneel error = register_inout(&iop); 456249321Sneel } else 457249321Sneel error = unregister_inout(&iop); 458249321Sneel break; 459249321Sneel case PCIBAR_MEM32: 460249321Sneel case PCIBAR_MEM64: 461249321Sneel bzero(&mr, sizeof(struct mem_range)); 462249321Sneel mr.name = pi->pi_name; 463249321Sneel mr.base = pi->pi_bar[idx].addr; 464249321Sneel mr.size = pi->pi_bar[idx].size; 465249321Sneel if (registration) { 466249321Sneel mr.flags = MEM_F_RW; 467249321Sneel mr.handler = pci_emul_mem_handler; 468249321Sneel mr.arg1 = pi; 469249321Sneel mr.arg2 = idx; 470249321Sneel error = register_mem(&mr); 471249321Sneel } else 472249321Sneel error = unregister_mem(&mr); 473249321Sneel break; 474249321Sneel default: 475249321Sneel error = EINVAL; 476249321Sneel break; 477249321Sneel } 478249321Sneel assert(error == 0); 479249321Sneel} 480249321Sneel 481249321Sneelstatic void 482249321Sneelunregister_bar(struct pci_devinst *pi, int idx) 483249321Sneel{ 484249321Sneel 485249321Sneel modify_bar_registration(pi, idx, 0); 486249321Sneel} 487249321Sneel 488249321Sneelstatic void 489249321Sneelregister_bar(struct pci_devinst *pi, int idx) 490249321Sneel{ 491249321Sneel 492249321Sneel modify_bar_registration(pi, idx, 1); 493249321Sneel} 494249321Sneel 495249321Sneel/* Are we decoding i/o port accesses for the emulated pci device? */ 496249321Sneelstatic int 497249321Sneelporten(struct pci_devinst *pi) 498249321Sneel{ 499249321Sneel uint16_t cmd; 500249321Sneel 501249321Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 502249321Sneel 503249321Sneel return (cmd & PCIM_CMD_PORTEN); 504249321Sneel} 505249321Sneel 506249321Sneel/* Are we decoding memory accesses for the emulated pci device? */ 507249321Sneelstatic int 508249321Sneelmemen(struct pci_devinst *pi) 509249321Sneel{ 510249321Sneel uint16_t cmd; 511249321Sneel 512249321Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 513249321Sneel 514249321Sneel return (cmd & PCIM_CMD_MEMEN); 515249321Sneel} 516249321Sneel 517249321Sneel/* 518249321Sneel * Update the MMIO or I/O address that is decoded by the BAR register. 519249321Sneel * 520249321Sneel * If the pci device has enabled the address space decoding then intercept 521249321Sneel * the address range decoded by the BAR register. 522249321Sneel */ 523249321Sneelstatic void 524249321Sneelupdate_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) 525249321Sneel{ 526249321Sneel int decode; 527249321Sneel 528249321Sneel if (pi->pi_bar[idx].type == PCIBAR_IO) 529249321Sneel decode = porten(pi); 530249321Sneel else 531249321Sneel decode = memen(pi); 532249321Sneel 533249321Sneel if (decode) 534249321Sneel unregister_bar(pi, idx); 535249321Sneel 536249321Sneel switch (type) { 537249321Sneel case PCIBAR_IO: 538249321Sneel case PCIBAR_MEM32: 539249321Sneel pi->pi_bar[idx].addr = addr; 540249321Sneel break; 541249321Sneel case PCIBAR_MEM64: 542249321Sneel pi->pi_bar[idx].addr &= ~0xffffffffUL; 543249321Sneel pi->pi_bar[idx].addr |= addr; 544249321Sneel break; 545249321Sneel case PCIBAR_MEMHI64: 546249321Sneel pi->pi_bar[idx].addr &= 0xffffffff; 547249321Sneel pi->pi_bar[idx].addr |= addr; 548249321Sneel break; 549249321Sneel default: 550249321Sneel assert(0); 551249321Sneel } 552249321Sneel 553249321Sneel if (decode) 554249321Sneel register_bar(pi, idx); 555249321Sneel} 556249321Sneel 557241744Sgrehanint 558241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, 559241744Sgrehan enum pcibar_type type, uint64_t size) 560241744Sgrehan{ 561249321Sneel int error; 562221828Sgrehan uint64_t *baseptr, limit, addr, mask, lobits, bar; 563221828Sgrehan 564221828Sgrehan assert(idx >= 0 && idx <= PCI_BARMAX); 565221828Sgrehan 566221828Sgrehan if ((size & (size - 1)) != 0) 567221828Sgrehan size = 1UL << flsl(size); /* round up to a power of 2 */ 568221828Sgrehan 569249321Sneel /* Enforce minimum BAR sizes required by the PCI standard */ 570249321Sneel if (type == PCIBAR_IO) { 571249321Sneel if (size < 4) 572249321Sneel size = 4; 573249321Sneel } else { 574249321Sneel if (size < 16) 575249321Sneel size = 16; 576249321Sneel } 577249321Sneel 578221828Sgrehan switch (type) { 579221828Sgrehan case PCIBAR_NONE: 580221828Sgrehan baseptr = NULL; 581221828Sgrehan addr = mask = lobits = 0; 582221828Sgrehan break; 583221828Sgrehan case PCIBAR_IO: 584267341Sjhb baseptr = &pci_emul_iobase; 585221828Sgrehan limit = PCI_EMUL_IOLIMIT; 586221828Sgrehan mask = PCIM_BAR_IO_BASE; 587221828Sgrehan lobits = PCIM_BAR_IO_SPACE; 588221828Sgrehan break; 589221828Sgrehan case PCIBAR_MEM64: 590221828Sgrehan /* 591221828Sgrehan * XXX 592221828Sgrehan * Some drivers do not work well if the 64-bit BAR is allocated 593221828Sgrehan * above 4GB. Allow for this by allocating small requests under 594221828Sgrehan * 4GB unless then allocation size is larger than some arbitrary 595221828Sgrehan * number (32MB currently). 596221828Sgrehan */ 597221828Sgrehan if (size > 32 * 1024 * 1024) { 598221828Sgrehan /* 599221828Sgrehan * XXX special case for device requiring peer-peer DMA 600221828Sgrehan */ 601221828Sgrehan if (size == 0x100000000UL) 602221828Sgrehan baseptr = &hostbase; 603221828Sgrehan else 604221828Sgrehan baseptr = &pci_emul_membase64; 605221828Sgrehan limit = PCI_EMUL_MEMLIMIT64; 606221828Sgrehan mask = PCIM_BAR_MEM_BASE; 607221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 608221828Sgrehan PCIM_BAR_MEM_PREFETCH; 609221828Sgrehan break; 610239086Sneel } else { 611239086Sneel baseptr = &pci_emul_membase32; 612239086Sneel limit = PCI_EMUL_MEMLIMIT32; 613239086Sneel mask = PCIM_BAR_MEM_BASE; 614239086Sneel lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 615221828Sgrehan } 616239086Sneel break; 617221828Sgrehan case PCIBAR_MEM32: 618221828Sgrehan baseptr = &pci_emul_membase32; 619221828Sgrehan limit = PCI_EMUL_MEMLIMIT32; 620221828Sgrehan mask = PCIM_BAR_MEM_BASE; 621221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 622221828Sgrehan break; 623221828Sgrehan default: 624221828Sgrehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 625221828Sgrehan assert(0); 626221828Sgrehan } 627221828Sgrehan 628221828Sgrehan if (baseptr != NULL) { 629221828Sgrehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 630221828Sgrehan if (error != 0) 631221828Sgrehan return (error); 632221828Sgrehan } 633221828Sgrehan 634221828Sgrehan pdi->pi_bar[idx].type = type; 635221828Sgrehan pdi->pi_bar[idx].addr = addr; 636221828Sgrehan pdi->pi_bar[idx].size = size; 637221828Sgrehan 638221828Sgrehan /* Initialize the BAR register in config space */ 639221828Sgrehan bar = (addr & mask) | lobits; 640221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 641221828Sgrehan 642221828Sgrehan if (type == PCIBAR_MEM64) { 643221828Sgrehan assert(idx + 1 <= PCI_BARMAX); 644221828Sgrehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 645221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 646221828Sgrehan } 647221828Sgrehan 648249321Sneel register_bar(pdi, idx); 649221828Sgrehan 650221828Sgrehan return (0); 651221828Sgrehan} 652221828Sgrehan 653221828Sgrehan#define CAP_START_OFFSET 0x40 654221828Sgrehanstatic int 655221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 656221828Sgrehan{ 657268887Sjhb int i, capoff, reallen; 658221828Sgrehan uint16_t sts; 659221828Sgrehan 660268887Sjhb assert(caplen > 0); 661221828Sgrehan 662221828Sgrehan reallen = roundup2(caplen, 4); /* dword aligned */ 663221828Sgrehan 664221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 665268887Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) 666221828Sgrehan capoff = CAP_START_OFFSET; 667268887Sjhb else 668268887Sjhb capoff = pi->pi_capend + 1; 669221828Sgrehan 670221828Sgrehan /* Check if we have enough space */ 671268887Sjhb if (capoff + reallen > PCI_REGMAX + 1) 672221828Sgrehan return (-1); 673221828Sgrehan 674268887Sjhb /* Set the previous capability pointer */ 675268887Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 676268887Sjhb pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 677268887Sjhb pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 678268887Sjhb } else 679268887Sjhb pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); 680268887Sjhb 681221828Sgrehan /* Copy the capability */ 682221828Sgrehan for (i = 0; i < caplen; i++) 683221828Sgrehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 684221828Sgrehan 685221828Sgrehan /* Set the next capability pointer */ 686268887Sjhb pci_set_cfgdata8(pi, capoff + 1, 0); 687221828Sgrehan 688268887Sjhb pi->pi_prevcap = capoff; 689268887Sjhb pi->pi_capend = capoff + reallen - 1; 690221828Sgrehan return (0); 691221828Sgrehan} 692221828Sgrehan 693221828Sgrehanstatic struct pci_devemu * 694221828Sgrehanpci_emul_finddev(char *name) 695221828Sgrehan{ 696221828Sgrehan struct pci_devemu **pdpp, *pdp; 697221828Sgrehan 698221828Sgrehan SET_FOREACH(pdpp, pci_devemu_set) { 699221828Sgrehan pdp = *pdpp; 700221828Sgrehan if (!strcmp(pdp->pe_emu, name)) { 701221828Sgrehan return (pdp); 702221828Sgrehan } 703221828Sgrehan } 704221828Sgrehan 705221828Sgrehan return (NULL); 706221828Sgrehan} 707221828Sgrehan 708252682Sgrehanstatic int 709268887Sjhbpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, 710268887Sjhb int func, struct funcinfo *fi) 711221828Sgrehan{ 712221828Sgrehan struct pci_devinst *pdi; 713252682Sgrehan int err; 714252682Sgrehan 715268953Sjhb pdi = calloc(1, sizeof(struct pci_devinst)); 716221828Sgrehan 717221828Sgrehan pdi->pi_vmctx = ctx; 718268887Sjhb pdi->pi_bus = bus; 719221828Sgrehan pdi->pi_slot = slot; 720239085Sneel pdi->pi_func = func; 721267393Sjhb pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 722267393Sjhb pdi->pi_lintr.pin = 0; 723267393Sjhb pdi->pi_lintr.state = IDLE; 724268972Sjhb pdi->pi_lintr.pirq_pin = 0; 725267393Sjhb pdi->pi_lintr.ioapic_irq = 0; 726221828Sgrehan pdi->pi_d = pde; 727221828Sgrehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 728221828Sgrehan 729221828Sgrehan /* Disable legacy interrupts */ 730221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 731221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 732221828Sgrehan 733221828Sgrehan pci_set_cfgdata8(pdi, PCIR_COMMAND, 734221828Sgrehan PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 735221828Sgrehan 736268887Sjhb err = (*pde->pe_init)(ctx, pdi, fi->fi_param); 737268887Sjhb if (err == 0) 738268887Sjhb fi->fi_devi = pdi; 739268887Sjhb else 740221828Sgrehan free(pdi); 741252682Sgrehan 742252682Sgrehan return (err); 743221828Sgrehan} 744221828Sgrehan 745221828Sgrehanvoid 746221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 747221828Sgrehan{ 748221828Sgrehan int mmc; 749221828Sgrehan 750221828Sgrehan CTASSERT(sizeof(struct msicap) == 14); 751221828Sgrehan 752221828Sgrehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 753221828Sgrehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 754221828Sgrehan mmc = ffs(msgnum) - 1; 755221828Sgrehan 756221828Sgrehan bzero(msicap, sizeof(struct msicap)); 757221828Sgrehan msicap->capid = PCIY_MSI; 758221828Sgrehan msicap->nextptr = nextptr; 759221828Sgrehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 760221828Sgrehan} 761221828Sgrehan 762221828Sgrehanint 763221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 764221828Sgrehan{ 765221828Sgrehan struct msicap msicap; 766221828Sgrehan 767221828Sgrehan pci_populate_msicap(&msicap, msgnum, 0); 768221828Sgrehan 769221828Sgrehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 770221828Sgrehan} 771221828Sgrehan 772246109Sneelstatic void 773246109Sneelpci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 774268887Sjhb uint32_t msix_tab_size) 775246109Sneel{ 776246109Sneel CTASSERT(sizeof(struct msixcap) == 12); 777246109Sneel 778246109Sneel assert(msix_tab_size % 4096 == 0); 779246109Sneel 780246109Sneel bzero(msixcap, sizeof(struct msixcap)); 781246109Sneel msixcap->capid = PCIY_MSIX; 782246109Sneel 783246109Sneel /* 784246109Sneel * Message Control Register, all fields set to 785246109Sneel * zero except for the Table Size. 786246109Sneel * Note: Table size N is encoded as N-1 787246109Sneel */ 788246109Sneel msixcap->msgctrl = msgnum - 1; 789246109Sneel 790246109Sneel /* 791246109Sneel * MSI-X BAR setup: 792246109Sneel * - MSI-X table start at offset 0 793246109Sneel * - PBA table starts at a 4K aligned offset after the MSI-X table 794246109Sneel */ 795246109Sneel msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 796246109Sneel msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 797246109Sneel} 798246109Sneel 799246109Sneelstatic void 800246109Sneelpci_msix_table_init(struct pci_devinst *pi, int table_entries) 801246109Sneel{ 802246109Sneel int i, table_size; 803246109Sneel 804246109Sneel assert(table_entries > 0); 805246109Sneel assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 806246109Sneel 807246109Sneel table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 808268953Sjhb pi->pi_msix.table = calloc(1, table_size); 809246109Sneel 810246109Sneel /* set mask bit of vector control register */ 811246109Sneel for (i = 0; i < table_entries; i++) 812246109Sneel pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 813246109Sneel} 814246109Sneel 815246109Sneelint 816246109Sneelpci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 817246109Sneel{ 818246109Sneel uint32_t tab_size; 819246109Sneel struct msixcap msixcap; 820246109Sneel 821246109Sneel assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 822246109Sneel assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 823246109Sneel 824246109Sneel tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 825246109Sneel 826246109Sneel /* Align table size to nearest 4K */ 827246109Sneel tab_size = roundup2(tab_size, 4096); 828246109Sneel 829246109Sneel pi->pi_msix.table_bar = barnum; 830246109Sneel pi->pi_msix.pba_bar = barnum; 831246109Sneel pi->pi_msix.table_offset = 0; 832246109Sneel pi->pi_msix.table_count = msgnum; 833246109Sneel pi->pi_msix.pba_offset = tab_size; 834268887Sjhb pi->pi_msix.pba_size = PBA_SIZE(msgnum); 835246109Sneel 836246109Sneel pci_msix_table_init(pi, msgnum); 837246109Sneel 838268887Sjhb pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); 839246109Sneel 840246109Sneel /* allocate memory for MSI-X Table and PBA */ 841246109Sneel pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 842246109Sneel tab_size + pi->pi_msix.pba_size); 843246109Sneel 844246109Sneel return (pci_emul_add_capability(pi, (u_char *)&msixcap, 845246109Sneel sizeof(msixcap))); 846246109Sneel} 847246109Sneel 848221828Sgrehanvoid 849234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 850234761Sgrehan int bytes, uint32_t val) 851234761Sgrehan{ 852234761Sgrehan uint16_t msgctrl, rwmask; 853234761Sgrehan int off, table_bar; 854246109Sneel 855234761Sgrehan off = offset - capoff; 856234761Sgrehan table_bar = pi->pi_msix.table_bar; 857234761Sgrehan /* Message Control Register */ 858234761Sgrehan if (off == 2 && bytes == 2) { 859234761Sgrehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 860234761Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 861234761Sgrehan msgctrl &= ~rwmask; 862234761Sgrehan msgctrl |= val & rwmask; 863234761Sgrehan val = msgctrl; 864234761Sgrehan 865234761Sgrehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 866246109Sneel pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 867267393Sjhb pci_lintr_update(pi); 868234761Sgrehan } 869234761Sgrehan 870234761Sgrehan CFGWRITE(pi, offset, val, bytes); 871234761Sgrehan} 872234761Sgrehan 873234761Sgrehanvoid 874221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 875221828Sgrehan int bytes, uint32_t val) 876221828Sgrehan{ 877221828Sgrehan uint16_t msgctrl, rwmask, msgdata, mme; 878221828Sgrehan uint32_t addrlo; 879221828Sgrehan 880221828Sgrehan /* 881221828Sgrehan * If guest is writing to the message control register make sure 882221828Sgrehan * we do not overwrite read-only fields. 883221828Sgrehan */ 884221828Sgrehan if ((offset - capoff) == 2 && bytes == 2) { 885221828Sgrehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 886221828Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 887221828Sgrehan msgctrl &= ~rwmask; 888221828Sgrehan msgctrl |= val & rwmask; 889221828Sgrehan val = msgctrl; 890221828Sgrehan 891221828Sgrehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 892221828Sgrehan if (msgctrl & PCIM_MSICTRL_64BIT) 893221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 894221828Sgrehan else 895221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 896221828Sgrehan 897221828Sgrehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 898221828Sgrehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 899221828Sgrehan if (pi->pi_msi.enabled) { 900262350Sjhb pi->pi_msi.addr = addrlo; 901262350Sjhb pi->pi_msi.msg_data = msgdata; 902262350Sjhb pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 903221828Sgrehan } else { 904262350Sjhb pi->pi_msi.maxmsgnum = 0; 905221828Sgrehan } 906267393Sjhb pci_lintr_update(pi); 907221828Sgrehan } 908221828Sgrehan 909221828Sgrehan CFGWRITE(pi, offset, val, bytes); 910221828Sgrehan} 911221828Sgrehan 912246846Sneelvoid 913246846Sneelpciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 914246846Sneel int bytes, uint32_t val) 915246846Sneel{ 916246846Sneel 917246846Sneel /* XXX don't write to the readonly parts */ 918246846Sneel CFGWRITE(pi, offset, val, bytes); 919246846Sneel} 920246846Sneel 921246846Sneel#define PCIECAP_VERSION 0x2 922246846Sneelint 923246846Sneelpci_emul_add_pciecap(struct pci_devinst *pi, int type) 924246846Sneel{ 925246846Sneel int err; 926246846Sneel struct pciecap pciecap; 927246846Sneel 928246846Sneel CTASSERT(sizeof(struct pciecap) == 60); 929246846Sneel 930246846Sneel if (type != PCIEM_TYPE_ROOT_PORT) 931246846Sneel return (-1); 932246846Sneel 933246846Sneel bzero(&pciecap, sizeof(pciecap)); 934246846Sneel 935246846Sneel pciecap.capid = PCIY_EXPRESS; 936246846Sneel pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT; 937246846Sneel pciecap.link_capabilities = 0x411; /* gen1, x1 */ 938246846Sneel pciecap.link_status = 0x11; /* gen1, x1 */ 939246846Sneel 940246846Sneel err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 941246846Sneel return (err); 942246846Sneel} 943246846Sneel 944221828Sgrehan/* 945221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the 946221828Sgrehan * config space. 947221828Sgrehan */ 948221828Sgrehanstatic void 949221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) 950221828Sgrehan{ 951221828Sgrehan int capid; 952221828Sgrehan uint8_t capoff, nextoff; 953221828Sgrehan 954221828Sgrehan /* Do not allow un-aligned writes */ 955221828Sgrehan if ((offset & (bytes - 1)) != 0) 956221828Sgrehan return; 957221828Sgrehan 958221828Sgrehan /* Find the capability that we want to update */ 959221828Sgrehan capoff = CAP_START_OFFSET; 960221828Sgrehan while (1) { 961268887Sjhb nextoff = pci_get_cfgdata8(pi, capoff + 1); 962268887Sjhb if (nextoff == 0) 963221828Sgrehan break; 964221828Sgrehan if (offset >= capoff && offset < nextoff) 965221828Sgrehan break; 966221828Sgrehan 967221828Sgrehan capoff = nextoff; 968221828Sgrehan } 969221828Sgrehan assert(offset >= capoff); 970221828Sgrehan 971221828Sgrehan /* 972256248Sgrehan * Capability ID and Next Capability Pointer are readonly. 973256248Sgrehan * However, some o/s's do 4-byte writes that include these. 974256248Sgrehan * For this case, trim the write back to 2 bytes and adjust 975256248Sgrehan * the data. 976221828Sgrehan */ 977256248Sgrehan if (offset == capoff || offset == capoff + 1) { 978256248Sgrehan if (offset == capoff && bytes == 4) { 979256248Sgrehan bytes = 2; 980256248Sgrehan offset += 2; 981256248Sgrehan val >>= 16; 982256248Sgrehan } else 983256248Sgrehan return; 984256248Sgrehan } 985221828Sgrehan 986268887Sjhb capid = pci_get_cfgdata8(pi, capoff); 987221828Sgrehan switch (capid) { 988221828Sgrehan case PCIY_MSI: 989221828Sgrehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 990221828Sgrehan break; 991246109Sneel case PCIY_MSIX: 992246109Sneel msixcap_cfgwrite(pi, capoff, offset, bytes, val); 993246109Sneel break; 994246846Sneel case PCIY_EXPRESS: 995246846Sneel pciecap_cfgwrite(pi, capoff, offset, bytes, val); 996246846Sneel break; 997221828Sgrehan default: 998221828Sgrehan break; 999221828Sgrehan } 1000221828Sgrehan} 1001221828Sgrehan 1002221828Sgrehanstatic int 1003221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset) 1004221828Sgrehan{ 1005221828Sgrehan uint16_t sts; 1006221828Sgrehan 1007221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 1008221828Sgrehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 1009268887Sjhb if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1010268887Sjhb return (1); 1011221828Sgrehan } 1012268887Sjhb return (0); 1013221828Sgrehan} 1014221828Sgrehan 1015247144Sgrehanstatic int 1016247144Sgrehanpci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1017247144Sgrehan int size, uint64_t *val, void *arg1, long arg2) 1018247144Sgrehan{ 1019247144Sgrehan /* 1020247144Sgrehan * Ignore writes; return 0xff's for reads. The mem read code 1021247144Sgrehan * will take care of truncating to the correct size. 1022247144Sgrehan */ 1023247144Sgrehan if (dir == MEM_F_READ) { 1024247144Sgrehan *val = 0xffffffffffffffff; 1025247144Sgrehan } 1026247144Sgrehan 1027247144Sgrehan return (0); 1028247144Sgrehan} 1029247144Sgrehan 1030270159Sgrehanstatic int 1031270159Sgrehanpci_emul_ecfg_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1032270159Sgrehan int bytes, uint64_t *val, void *arg1, long arg2) 1033270159Sgrehan{ 1034270159Sgrehan int bus, slot, func, coff, in; 1035270159Sgrehan 1036270159Sgrehan coff = addr & 0xfff; 1037270159Sgrehan func = (addr >> 12) & 0x7; 1038270159Sgrehan slot = (addr >> 15) & 0x1f; 1039270159Sgrehan bus = (addr >> 20) & 0xff; 1040270159Sgrehan in = (dir == MEM_F_READ); 1041270159Sgrehan if (in) 1042270159Sgrehan *val = ~0UL; 1043270159Sgrehan pci_cfgrw(ctx, vcpu, in, bus, slot, func, coff, bytes, (uint32_t *)val); 1044270159Sgrehan return (0); 1045270159Sgrehan} 1046270159Sgrehan 1047270159Sgrehanuint64_t 1048270159Sgrehanpci_ecfg_base(void) 1049270159Sgrehan{ 1050270159Sgrehan 1051270159Sgrehan return (PCI_EMUL_ECFG_BASE); 1052270159Sgrehan} 1053270159Sgrehan 1054268887Sjhb#define BUSIO_ROUNDUP 32 1055268887Sjhb#define BUSMEM_ROUNDUP (1024 * 1024) 1056268887Sjhb 1057252682Sgrehanint 1058221828Sgrehaninit_pci(struct vmctx *ctx) 1059221828Sgrehan{ 1060270159Sgrehan struct mem_range mr; 1061221828Sgrehan struct pci_devemu *pde; 1062268887Sjhb struct businfo *bi; 1063268887Sjhb struct slotinfo *si; 1064267393Sjhb struct funcinfo *fi; 1065249572Sneel size_t lowmem; 1066268887Sjhb int bus, slot, func; 1067247144Sgrehan int error; 1068221828Sgrehan 1069221828Sgrehan pci_emul_iobase = PCI_EMUL_IOBASE; 1070249572Sneel pci_emul_membase32 = vm_get_lowmem_limit(ctx); 1071221828Sgrehan pci_emul_membase64 = PCI_EMUL_MEMBASE64; 1072221828Sgrehan 1073268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1074268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1075268887Sjhb continue; 1076268887Sjhb /* 1077268887Sjhb * Keep track of the i/o and memory resources allocated to 1078268887Sjhb * this bus. 1079268887Sjhb */ 1080268887Sjhb bi->iobase = pci_emul_iobase; 1081268887Sjhb bi->membase32 = pci_emul_membase32; 1082268887Sjhb bi->membase64 = pci_emul_membase64; 1083268887Sjhb 1084268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1085268887Sjhb si = &bi->slotinfo[slot]; 1086268887Sjhb for (func = 0; func < MAXFUNCS; func++) { 1087268887Sjhb fi = &si->si_funcs[func]; 1088268887Sjhb if (fi->fi_name == NULL) 1089268887Sjhb continue; 1090267393Sjhb pde = pci_emul_finddev(fi->fi_name); 1091249916Sneel assert(pde != NULL); 1092268887Sjhb error = pci_emul_init(ctx, pde, bus, slot, 1093268887Sjhb func, fi); 1094252682Sgrehan if (error) 1095252682Sgrehan return (error); 1096221828Sgrehan } 1097221828Sgrehan } 1098268887Sjhb 1099268887Sjhb /* 1100268887Sjhb * Add some slop to the I/O and memory resources decoded by 1101268887Sjhb * this bus to give a guest some flexibility if it wants to 1102268887Sjhb * reprogram the BARs. 1103268887Sjhb */ 1104268887Sjhb pci_emul_iobase += BUSIO_ROUNDUP; 1105268887Sjhb pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1106268887Sjhb bi->iolimit = pci_emul_iobase; 1107268887Sjhb 1108268887Sjhb pci_emul_membase32 += BUSMEM_ROUNDUP; 1109268887Sjhb pci_emul_membase32 = roundup2(pci_emul_membase32, 1110268887Sjhb BUSMEM_ROUNDUP); 1111268887Sjhb bi->memlimit32 = pci_emul_membase32; 1112268887Sjhb 1113268887Sjhb pci_emul_membase64 += BUSMEM_ROUNDUP; 1114268887Sjhb pci_emul_membase64 = roundup2(pci_emul_membase64, 1115268887Sjhb BUSMEM_ROUNDUP); 1116268887Sjhb bi->memlimit64 = pci_emul_membase64; 1117221828Sgrehan } 1118234938Sgrehan 1119234938Sgrehan /* 1120268972Sjhb * PCI backends are initialized before routing INTx interrupts 1121268972Sjhb * so that LPC devices are able to reserve ISA IRQs before 1122268972Sjhb * routing PIRQ pins. 1123268972Sjhb */ 1124268972Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1125268972Sjhb if ((bi = pci_businfo[bus]) == NULL) 1126268972Sjhb continue; 1127268972Sjhb 1128268972Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1129268972Sjhb si = &bi->slotinfo[slot]; 1130268972Sjhb for (func = 0; func < MAXFUNCS; func++) { 1131268972Sjhb fi = &si->si_funcs[func]; 1132268972Sjhb if (fi->fi_devi == NULL) 1133268972Sjhb continue; 1134268972Sjhb pci_lintr_route(fi->fi_devi); 1135268972Sjhb } 1136268972Sjhb } 1137268972Sjhb } 1138268972Sjhb lpc_pirq_routed(); 1139268972Sjhb 1140268972Sjhb /* 1141249572Sneel * The guest physical memory map looks like the following: 1142249572Sneel * [0, lowmem) guest system memory 1143249572Sneel * [lowmem, lowmem_limit) memory hole (may be absent) 1144270159Sgrehan * [lowmem_limit, 0xE0000000) PCI hole (32-bit BAR allocation) 1145270159Sgrehan * [0xE0000000, 0xF0000000) PCI extended config window 1146270159Sgrehan * [0xF0000000, 4GB) LAPIC, IOAPIC, HPET, firmware 1147249572Sneel * [4GB, 4GB + highmem) 1148270159Sgrehan */ 1149270159Sgrehan 1150270159Sgrehan /* 1151249572Sneel * Accesses to memory addresses that are not allocated to system 1152249572Sneel * memory or PCI devices return 0xff's. 1153247144Sgrehan */ 1154270074Sgrehan lowmem = vm_get_lowmem_size(ctx); 1155270159Sgrehan bzero(&mr, sizeof(struct mem_range)); 1156270159Sgrehan mr.name = "PCI hole"; 1157270159Sgrehan mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1158270159Sgrehan mr.base = lowmem; 1159270159Sgrehan mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 1160270159Sgrehan mr.handler = pci_emul_fallback_handler; 1161270159Sgrehan error = register_mem_fallback(&mr); 1162270159Sgrehan assert(error == 0); 1163249572Sneel 1164270159Sgrehan /* PCI extended config space */ 1165270159Sgrehan bzero(&mr, sizeof(struct mem_range)); 1166270159Sgrehan mr.name = "PCI ECFG"; 1167270159Sgrehan mr.flags = MEM_F_RW | MEM_F_IMMUTABLE; 1168270159Sgrehan mr.base = PCI_EMUL_ECFG_BASE; 1169270159Sgrehan mr.size = PCI_EMUL_ECFG_SIZE; 1170270159Sgrehan mr.handler = pci_emul_ecfg_handler; 1171270159Sgrehan error = register_mem(&mr); 1172247144Sgrehan assert(error == 0); 1173252682Sgrehan 1174252682Sgrehan return (0); 1175221828Sgrehan} 1176221828Sgrehan 1177267393Sjhbstatic void 1178268972Sjhbpci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1179268972Sjhb void *arg) 1180267393Sjhb{ 1181267393Sjhb 1182268972Sjhb dsdt_line(" Package ()"); 1183267393Sjhb dsdt_line(" {"); 1184267393Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1185267393Sjhb dsdt_line(" 0x%02X,", pin - 1); 1186267393Sjhb dsdt_line(" Zero,"); 1187267393Sjhb dsdt_line(" 0x%X", ioapic_irq); 1188268972Sjhb dsdt_line(" },"); 1189267393Sjhb} 1190267393Sjhb 1191268972Sjhbstatic void 1192268972Sjhbpci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1193268972Sjhb void *arg) 1194268972Sjhb{ 1195268972Sjhb char *name; 1196268972Sjhb 1197268972Sjhb name = lpc_pirq_name(pirq_pin); 1198268972Sjhb if (name == NULL) 1199268972Sjhb return; 1200268972Sjhb dsdt_line(" Package ()"); 1201268972Sjhb dsdt_line(" {"); 1202268972Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1203268972Sjhb dsdt_line(" 0x%02X,", pin - 1); 1204268972Sjhb dsdt_line(" %s,", name); 1205268972Sjhb dsdt_line(" 0x00"); 1206268972Sjhb dsdt_line(" },"); 1207268972Sjhb free(name); 1208268972Sjhb} 1209268972Sjhb 1210268887Sjhb/* 1211268887Sjhb * A bhyve virtual machine has a flat PCI hierarchy with a root port 1212268887Sjhb * corresponding to each PCI bus. 1213268887Sjhb */ 1214268887Sjhbstatic void 1215268887Sjhbpci_bus_write_dsdt(int bus) 1216261265Sjhb{ 1217268887Sjhb struct businfo *bi; 1218268887Sjhb struct slotinfo *si; 1219261265Sjhb struct pci_devinst *pi; 1220268972Sjhb int count, func, slot; 1221261265Sjhb 1222268887Sjhb /* 1223268887Sjhb * If there are no devices on this 'bus' then just return. 1224268887Sjhb */ 1225268887Sjhb if ((bi = pci_businfo[bus]) == NULL) { 1226268887Sjhb /* 1227268887Sjhb * Bus 0 is special because it decodes the I/O ports used 1228268887Sjhb * for PCI config space access even if there are no devices 1229268887Sjhb * on it. 1230268887Sjhb */ 1231268887Sjhb if (bus != 0) 1232268887Sjhb return; 1233268887Sjhb } 1234268887Sjhb 1235268887Sjhb dsdt_line(" Device (PC%02X)", bus); 1236261265Sjhb dsdt_line(" {"); 1237261265Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1238261265Sjhb dsdt_line(" Name (_ADR, Zero)"); 1239268887Sjhb 1240268887Sjhb dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1241268887Sjhb dsdt_line(" {"); 1242268887Sjhb dsdt_line(" Return (0x%08X)", bus); 1243268887Sjhb dsdt_line(" }"); 1244261265Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1245261265Sjhb dsdt_line(" {"); 1246261265Sjhb dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1247261265Sjhb "MaxFixed, PosDecode,"); 1248261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1249268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bus); 1250268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", bus); 1251261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1252268887Sjhb dsdt_line(" 0x0001, // Length"); 1253261265Sjhb dsdt_line(" ,, )"); 1254268887Sjhb 1255268887Sjhb if (bus == 0) { 1256268887Sjhb dsdt_indent(3); 1257268887Sjhb dsdt_fixed_ioport(0xCF8, 8); 1258268887Sjhb dsdt_unindent(3); 1259268887Sjhb 1260268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1261268887Sjhb "PosDecode, EntireRange,"); 1262268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1263268887Sjhb dsdt_line(" 0x0000, // Range Minimum"); 1264268887Sjhb dsdt_line(" 0x0CF7, // Range Maximum"); 1265268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1266268887Sjhb dsdt_line(" 0x0CF8, // Length"); 1267268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1268268887Sjhb 1269268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1270268887Sjhb "PosDecode, EntireRange,"); 1271268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1272268887Sjhb dsdt_line(" 0x0D00, // Range Minimum"); 1273268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1274268887Sjhb PCI_EMUL_IOBASE - 1); 1275268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1276268887Sjhb dsdt_line(" 0x%04X, // Length", 1277268887Sjhb PCI_EMUL_IOBASE - 0x0D00); 1278268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1279268887Sjhb 1280268887Sjhb if (bi == NULL) { 1281268887Sjhb dsdt_line(" })"); 1282268887Sjhb goto done; 1283268887Sjhb } 1284268887Sjhb } 1285268887Sjhb assert(bi != NULL); 1286268887Sjhb 1287268887Sjhb /* i/o window */ 1288261265Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1289261265Sjhb "PosDecode, EntireRange,"); 1290261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1291268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1292268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1293268887Sjhb bi->iolimit - 1); 1294261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1295268887Sjhb dsdt_line(" 0x%04X, // Length", 1296268887Sjhb bi->iolimit - bi->iobase); 1297261265Sjhb dsdt_line(" ,, , TypeStatic)"); 1298268887Sjhb 1299268887Sjhb /* mmio window (32-bit) */ 1300261265Sjhb dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1301261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1302261265Sjhb dsdt_line(" 0x00000000, // Granularity"); 1303268887Sjhb dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1304261265Sjhb dsdt_line(" 0x%08X, // Range Maximum\n", 1305268887Sjhb bi->memlimit32 - 1); 1306261265Sjhb dsdt_line(" 0x00000000, // Translation Offset"); 1307268887Sjhb dsdt_line(" 0x%08X, // Length\n", 1308268887Sjhb bi->memlimit32 - bi->membase32); 1309261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1310268887Sjhb 1311268887Sjhb /* mmio window (64-bit) */ 1312261265Sjhb dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1313261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1314261265Sjhb dsdt_line(" 0x0000000000000000, // Granularity"); 1315268887Sjhb dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1316261265Sjhb dsdt_line(" 0x%016lX, // Range Maximum\n", 1317268887Sjhb bi->memlimit64 - 1); 1318261265Sjhb dsdt_line(" 0x0000000000000000, // Translation Offset"); 1319261265Sjhb dsdt_line(" 0x%016lX, // Length\n", 1320268887Sjhb bi->memlimit64 - bi->membase64); 1321261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1322261265Sjhb dsdt_line(" })"); 1323268887Sjhb 1324268887Sjhb count = pci_count_lintr(bus); 1325267393Sjhb if (count != 0) { 1326267393Sjhb dsdt_indent(2); 1327268972Sjhb dsdt_line("Name (PPRT, Package ()"); 1328267393Sjhb dsdt_line("{"); 1329268972Sjhb pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 1330268972Sjhb dsdt_line("})"); 1331268972Sjhb dsdt_line("Name (APRT, Package ()"); 1332268972Sjhb dsdt_line("{"); 1333268972Sjhb pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1334268972Sjhb dsdt_line("})"); 1335268972Sjhb dsdt_line("Method (_PRT, 0, NotSerialized)"); 1336268972Sjhb dsdt_line("{"); 1337268972Sjhb dsdt_line(" If (PICM)"); 1338268972Sjhb dsdt_line(" {"); 1339268972Sjhb dsdt_line(" Return (APRT)"); 1340268972Sjhb dsdt_line(" }"); 1341268972Sjhb dsdt_line(" Else"); 1342268972Sjhb dsdt_line(" {"); 1343268972Sjhb dsdt_line(" Return (PPRT)"); 1344268972Sjhb dsdt_line(" }"); 1345268972Sjhb dsdt_line("}"); 1346267393Sjhb dsdt_unindent(2); 1347267393Sjhb } 1348261265Sjhb 1349261265Sjhb dsdt_indent(2); 1350261265Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1351268887Sjhb si = &bi->slotinfo[slot]; 1352261265Sjhb for (func = 0; func < MAXFUNCS; func++) { 1353268887Sjhb pi = si->si_funcs[func].fi_devi; 1354261265Sjhb if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1355261265Sjhb pi->pi_d->pe_write_dsdt(pi); 1356261265Sjhb } 1357261265Sjhb } 1358261265Sjhb dsdt_unindent(2); 1359268887Sjhbdone: 1360261265Sjhb dsdt_line(" }"); 1361261265Sjhb} 1362261265Sjhb 1363268887Sjhbvoid 1364268887Sjhbpci_write_dsdt(void) 1365268887Sjhb{ 1366268887Sjhb int bus; 1367268887Sjhb 1368268972Sjhb dsdt_indent(1); 1369268972Sjhb dsdt_line("Name (PICM, 0x00)"); 1370268972Sjhb dsdt_line("Method (_PIC, 1, NotSerialized)"); 1371268972Sjhb dsdt_line("{"); 1372268972Sjhb dsdt_line(" Store (Arg0, PICM)"); 1373268972Sjhb dsdt_line("}"); 1374268972Sjhb dsdt_line(""); 1375268972Sjhb dsdt_line("Scope (_SB)"); 1376268972Sjhb dsdt_line("{"); 1377268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) 1378268887Sjhb pci_bus_write_dsdt(bus); 1379268972Sjhb dsdt_line("}"); 1380268972Sjhb dsdt_unindent(1); 1381268887Sjhb} 1382268887Sjhb 1383221828Sgrehanint 1384268887Sjhbpci_bus_configured(int bus) 1385268887Sjhb{ 1386268887Sjhb assert(bus >= 0 && bus < MAXBUSES); 1387268887Sjhb return (pci_businfo[bus] != NULL); 1388268887Sjhb} 1389268887Sjhb 1390268887Sjhbint 1391221828Sgrehanpci_msi_enabled(struct pci_devinst *pi) 1392221828Sgrehan{ 1393221828Sgrehan return (pi->pi_msi.enabled); 1394221828Sgrehan} 1395221828Sgrehan 1396221828Sgrehanint 1397262350Sjhbpci_msi_maxmsgnum(struct pci_devinst *pi) 1398221828Sgrehan{ 1399221828Sgrehan if (pi->pi_msi.enabled) 1400262350Sjhb return (pi->pi_msi.maxmsgnum); 1401221828Sgrehan else 1402221828Sgrehan return (0); 1403221828Sgrehan} 1404221828Sgrehan 1405246109Sneelint 1406246109Sneelpci_msix_enabled(struct pci_devinst *pi) 1407246109Sneel{ 1408246109Sneel 1409246109Sneel return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1410246109Sneel} 1411246109Sneel 1412221828Sgrehanvoid 1413246109Sneelpci_generate_msix(struct pci_devinst *pi, int index) 1414246109Sneel{ 1415246109Sneel struct msix_table_entry *mte; 1416246109Sneel 1417246109Sneel if (!pci_msix_enabled(pi)) 1418246109Sneel return; 1419246109Sneel 1420246109Sneel if (pi->pi_msix.function_mask) 1421246109Sneel return; 1422246109Sneel 1423246109Sneel if (index >= pi->pi_msix.table_count) 1424246109Sneel return; 1425246109Sneel 1426246109Sneel mte = &pi->pi_msix.table[index]; 1427246109Sneel if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1428246109Sneel /* XXX Set PBA bit if interrupt is disabled */ 1429262350Sjhb vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1430246109Sneel } 1431246109Sneel} 1432246109Sneel 1433246109Sneelvoid 1434262350Sjhbpci_generate_msi(struct pci_devinst *pi, int index) 1435221828Sgrehan{ 1436221828Sgrehan 1437262350Sjhb if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 1438262350Sjhb vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 1439262350Sjhb pi->pi_msi.msg_data + index); 1440221828Sgrehan } 1441221828Sgrehan} 1442221828Sgrehan 1443267393Sjhbstatic bool 1444267393Sjhbpci_lintr_permitted(struct pci_devinst *pi) 1445267393Sjhb{ 1446267393Sjhb uint16_t cmd; 1447267393Sjhb 1448267393Sjhb cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 1449267393Sjhb return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 1450267393Sjhb (cmd & PCIM_CMD_INTxDIS))); 1451267393Sjhb} 1452267393Sjhb 1453268972Sjhbvoid 1454267393Sjhbpci_lintr_request(struct pci_devinst *pi) 1455234938Sgrehan{ 1456268887Sjhb struct businfo *bi; 1457267393Sjhb struct slotinfo *si; 1458268972Sjhb int bestpin, bestcount, pin; 1459234938Sgrehan 1460268887Sjhb bi = pci_businfo[pi->pi_bus]; 1461268887Sjhb assert(bi != NULL); 1462268887Sjhb 1463267393Sjhb /* 1464268972Sjhb * Just allocate a pin from our slot. The pin will be 1465268972Sjhb * assigned IRQs later when interrupts are routed. 1466267393Sjhb */ 1467268887Sjhb si = &bi->slotinfo[pi->pi_slot]; 1468267393Sjhb bestpin = 0; 1469267393Sjhb bestcount = si->si_intpins[0].ii_count; 1470267393Sjhb for (pin = 1; pin < 4; pin++) { 1471267393Sjhb if (si->si_intpins[pin].ii_count < bestcount) { 1472267393Sjhb bestpin = pin; 1473267393Sjhb bestcount = si->si_intpins[pin].ii_count; 1474267393Sjhb } 1475267393Sjhb } 1476234938Sgrehan 1477267393Sjhb si->si_intpins[bestpin].ii_count++; 1478267393Sjhb pi->pi_lintr.pin = bestpin + 1; 1479267393Sjhb pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1480234938Sgrehan} 1481234938Sgrehan 1482268972Sjhbstatic void 1483268972Sjhbpci_lintr_route(struct pci_devinst *pi) 1484268972Sjhb{ 1485268972Sjhb struct businfo *bi; 1486268972Sjhb struct intxinfo *ii; 1487268972Sjhb 1488268972Sjhb if (pi->pi_lintr.pin == 0) 1489268972Sjhb return; 1490268972Sjhb 1491268972Sjhb bi = pci_businfo[pi->pi_bus]; 1492268972Sjhb assert(bi != NULL); 1493268972Sjhb ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1494268972Sjhb 1495268972Sjhb /* 1496268972Sjhb * Attempt to allocate an I/O APIC pin for this intpin if one 1497268972Sjhb * is not yet assigned. 1498268972Sjhb */ 1499268972Sjhb if (ii->ii_ioapic_irq == 0) 1500268972Sjhb ii->ii_ioapic_irq = ioapic_pci_alloc_irq(); 1501268972Sjhb assert(ii->ii_ioapic_irq > 0); 1502268972Sjhb 1503268972Sjhb /* 1504268972Sjhb * Attempt to allocate a PIRQ pin for this intpin if one is 1505268972Sjhb * not yet assigned. 1506268972Sjhb */ 1507268972Sjhb if (ii->ii_pirq_pin == 0) 1508268972Sjhb ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx); 1509268972Sjhb assert(ii->ii_pirq_pin > 0); 1510268972Sjhb 1511268972Sjhb pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1512268972Sjhb pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1513268972Sjhb pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 1514268972Sjhb} 1515268972Sjhb 1516234938Sgrehanvoid 1517234938Sgrehanpci_lintr_assert(struct pci_devinst *pi) 1518234938Sgrehan{ 1519234938Sgrehan 1520267393Sjhb assert(pi->pi_lintr.pin > 0); 1521261088Sjhb 1522267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1523267393Sjhb if (pi->pi_lintr.state == IDLE) { 1524267393Sjhb if (pci_lintr_permitted(pi)) { 1525267393Sjhb pi->pi_lintr.state = ASSERTED; 1526268972Sjhb pci_irq_assert(pi); 1527267393Sjhb } else 1528267393Sjhb pi->pi_lintr.state = PENDING; 1529261088Sjhb } 1530267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1531234938Sgrehan} 1532234938Sgrehan 1533234938Sgrehanvoid 1534234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi) 1535234938Sgrehan{ 1536234938Sgrehan 1537267393Sjhb assert(pi->pi_lintr.pin > 0); 1538261088Sjhb 1539267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1540267393Sjhb if (pi->pi_lintr.state == ASSERTED) { 1541267393Sjhb pi->pi_lintr.state = IDLE; 1542268972Sjhb pci_irq_deassert(pi); 1543267393Sjhb } else if (pi->pi_lintr.state == PENDING) 1544267393Sjhb pi->pi_lintr.state = IDLE; 1545267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1546267393Sjhb} 1547267393Sjhb 1548267393Sjhbstatic void 1549267393Sjhbpci_lintr_update(struct pci_devinst *pi) 1550267393Sjhb{ 1551267393Sjhb 1552267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1553267393Sjhb if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1554268972Sjhb pci_irq_deassert(pi); 1555267393Sjhb pi->pi_lintr.state = PENDING; 1556267393Sjhb } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 1557267393Sjhb pi->pi_lintr.state = ASSERTED; 1558268972Sjhb pci_irq_assert(pi); 1559261088Sjhb } 1560267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1561234938Sgrehan} 1562234938Sgrehan 1563267393Sjhbint 1564268887Sjhbpci_count_lintr(int bus) 1565267393Sjhb{ 1566267393Sjhb int count, slot, pin; 1567268887Sjhb struct slotinfo *slotinfo; 1568267393Sjhb 1569267393Sjhb count = 0; 1570268887Sjhb if (pci_businfo[bus] != NULL) { 1571268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1572268887Sjhb slotinfo = &pci_businfo[bus]->slotinfo[slot]; 1573268887Sjhb for (pin = 0; pin < 4; pin++) { 1574268887Sjhb if (slotinfo->si_intpins[pin].ii_count != 0) 1575268887Sjhb count++; 1576268887Sjhb } 1577267393Sjhb } 1578267393Sjhb } 1579267393Sjhb return (count); 1580267393Sjhb} 1581267393Sjhb 1582267393Sjhbvoid 1583268887Sjhbpci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 1584267393Sjhb{ 1585268887Sjhb struct businfo *bi; 1586268887Sjhb struct slotinfo *si; 1587267393Sjhb struct intxinfo *ii; 1588267393Sjhb int slot, pin; 1589267393Sjhb 1590268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1591268887Sjhb return; 1592268887Sjhb 1593267393Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1594268887Sjhb si = &bi->slotinfo[slot]; 1595267393Sjhb for (pin = 0; pin < 4; pin++) { 1596268887Sjhb ii = &si->si_intpins[pin]; 1597267393Sjhb if (ii->ii_count != 0) 1598268972Sjhb cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1599268972Sjhb ii->ii_ioapic_irq, arg); 1600267393Sjhb } 1601267393Sjhb } 1602267393Sjhb} 1603267393Sjhb 1604239085Sneel/* 1605239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device. 1606239085Sneel * Return 0 otherwise. 1607239085Sneel */ 1608239085Sneelstatic int 1609268887Sjhbpci_emul_is_mfdev(int bus, int slot) 1610239085Sneel{ 1611268887Sjhb struct businfo *bi; 1612268887Sjhb struct slotinfo *si; 1613239085Sneel int f, numfuncs; 1614234938Sgrehan 1615239085Sneel numfuncs = 0; 1616268887Sjhb if ((bi = pci_businfo[bus]) != NULL) { 1617268887Sjhb si = &bi->slotinfo[slot]; 1618268887Sjhb for (f = 0; f < MAXFUNCS; f++) { 1619268887Sjhb if (si->si_funcs[f].fi_devi != NULL) { 1620268887Sjhb numfuncs++; 1621268887Sjhb } 1622239085Sneel } 1623239085Sneel } 1624239085Sneel return (numfuncs > 1); 1625239085Sneel} 1626234938Sgrehan 1627239085Sneel/* 1628239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 1629239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'. 1630239085Sneel */ 1631239085Sneelstatic void 1632268887Sjhbpci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) 1633239085Sneel{ 1634239085Sneel int mfdev; 1635239085Sneel 1636239085Sneel if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 1637268887Sjhb mfdev = pci_emul_is_mfdev(bus, slot); 1638239085Sneel switch (bytes) { 1639239085Sneel case 1: 1640239085Sneel case 2: 1641239085Sneel *rv &= ~PCIM_MFDEV; 1642239085Sneel if (mfdev) { 1643239085Sneel *rv |= PCIM_MFDEV; 1644239085Sneel } 1645239085Sneel break; 1646239085Sneel case 4: 1647239085Sneel *rv &= ~(PCIM_MFDEV << 16); 1648239085Sneel if (mfdev) { 1649239085Sneel *rv |= (PCIM_MFDEV << 16); 1650239085Sneel } 1651239085Sneel break; 1652239085Sneel } 1653239085Sneel } 1654239085Sneel} 1655239085Sneel 1656249321Sneelstatic uint32_t 1657249321Sneelbits_changed(uint32_t old, uint32_t new, uint32_t mask) 1658249321Sneel{ 1659249321Sneel 1660249321Sneel return ((old ^ new) & mask); 1661249321Sneel} 1662249321Sneel 1663249321Sneelstatic void 1664249321Sneelpci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes) 1665249321Sneel{ 1666249321Sneel int i; 1667249321Sneel uint16_t old; 1668249321Sneel 1669249321Sneel /* 1670249321Sneel * The command register is at an offset of 4 bytes and thus the 1671249321Sneel * guest could write 1, 2 or 4 bytes starting at this offset. 1672249321Sneel */ 1673249321Sneel 1674249321Sneel old = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 1675249321Sneel CFGWRITE(pi, PCIR_COMMAND, new, bytes); /* update config */ 1676249321Sneel new = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ 1677249321Sneel 1678249321Sneel /* 1679249321Sneel * If the MMIO or I/O address space decoding has changed then 1680249321Sneel * register/unregister all BARs that decode that address space. 1681249321Sneel */ 1682259301Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 1683249321Sneel switch (pi->pi_bar[i].type) { 1684249321Sneel case PCIBAR_NONE: 1685249321Sneel case PCIBAR_MEMHI64: 1686249321Sneel break; 1687249321Sneel case PCIBAR_IO: 1688249321Sneel /* I/O address space decoding changed? */ 1689249321Sneel if (bits_changed(old, new, PCIM_CMD_PORTEN)) { 1690249321Sneel if (porten(pi)) 1691249321Sneel register_bar(pi, i); 1692249321Sneel else 1693249321Sneel unregister_bar(pi, i); 1694249321Sneel } 1695249321Sneel break; 1696249321Sneel case PCIBAR_MEM32: 1697249321Sneel case PCIBAR_MEM64: 1698249321Sneel /* MMIO address space decoding changed? */ 1699249321Sneel if (bits_changed(old, new, PCIM_CMD_MEMEN)) { 1700249321Sneel if (memen(pi)) 1701249321Sneel register_bar(pi, i); 1702249321Sneel else 1703249321Sneel unregister_bar(pi, i); 1704249321Sneel } 1705249321Sneel break; 1706249321Sneel default: 1707249321Sneel assert(0); 1708249321Sneel } 1709249321Sneel } 1710267393Sjhb 1711267393Sjhb /* 1712267393Sjhb * If INTx has been unmasked and is pending, assert the 1713267393Sjhb * interrupt. 1714267393Sjhb */ 1715267393Sjhb pci_lintr_update(pi); 1716249321Sneel} 1717249321Sneel 1718270159Sgrehanstatic void 1719270159Sgrehanpci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func, 1720270159Sgrehan int coff, int bytes, uint32_t *eax) 1721221828Sgrehan{ 1722268887Sjhb struct businfo *bi; 1723268887Sjhb struct slotinfo *si; 1724221828Sgrehan struct pci_devinst *pi; 1725221828Sgrehan struct pci_devemu *pe; 1726270159Sgrehan int idx, needcfg; 1727249321Sneel uint64_t addr, bar, mask; 1728221828Sgrehan 1729270159Sgrehan if ((bi = pci_businfo[bus]) != NULL) { 1730270159Sgrehan si = &bi->slotinfo[slot]; 1731270159Sgrehan pi = si->si_funcs[func].fi_devi; 1732268887Sjhb } else 1733242170Sneel pi = NULL; 1734242170Sneel 1735239085Sneel /* 1736270159Sgrehan * Just return if there is no device at this slot:func or if the 1737270159Sgrehan * the guest is doing an un-aligned access. 1738239085Sneel */ 1739270159Sgrehan if (pi == NULL || (bytes != 1 && bytes != 2 && bytes != 4) || 1740270159Sgrehan (coff & (bytes - 1)) != 0) { 1741221828Sgrehan if (in) 1742221828Sgrehan *eax = 0xffffffff; 1743270159Sgrehan return; 1744221828Sgrehan } 1745221828Sgrehan 1746270159Sgrehan /* 1747270159Sgrehan * Ignore all writes beyond the standard config space and return all 1748270159Sgrehan * ones on reads. 1749270159Sgrehan */ 1750270159Sgrehan if (coff >= PCI_REGMAX + 1) { 1751270159Sgrehan if (in) { 1752270159Sgrehan *eax = 0xffffffff; 1753270159Sgrehan /* 1754270159Sgrehan * Extended capabilities begin at offset 256 in config 1755270159Sgrehan * space. Absence of extended capabilities is signaled 1756270159Sgrehan * with all 0s in the extended capability header at 1757270159Sgrehan * offset 256. 1758270159Sgrehan */ 1759270159Sgrehan if (coff <= PCI_REGMAX + 4) 1760270159Sgrehan *eax = 0x00000000; 1761270159Sgrehan } 1762270159Sgrehan return; 1763270159Sgrehan } 1764270159Sgrehan 1765221828Sgrehan pe = pi->pi_d; 1766221828Sgrehan 1767221828Sgrehan /* 1768221828Sgrehan * Config read 1769221828Sgrehan */ 1770221828Sgrehan if (in) { 1771221828Sgrehan /* Let the device emulation override the default handler */ 1772239085Sneel if (pe->pe_cfgread != NULL) { 1773270159Sgrehan needcfg = pe->pe_cfgread(ctx, vcpu, pi, coff, bytes, 1774270159Sgrehan eax); 1775239085Sneel } else { 1776239085Sneel needcfg = 1; 1777239085Sneel } 1778221828Sgrehan 1779239085Sneel if (needcfg) { 1780239085Sneel if (bytes == 1) 1781239085Sneel *eax = pci_get_cfgdata8(pi, coff); 1782239085Sneel else if (bytes == 2) 1783239085Sneel *eax = pci_get_cfgdata16(pi, coff); 1784239085Sneel else 1785239085Sneel *eax = pci_get_cfgdata32(pi, coff); 1786239085Sneel } 1787239085Sneel 1788270159Sgrehan pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax); 1789221828Sgrehan } else { 1790221828Sgrehan /* Let the device emulation override the default handler */ 1791221828Sgrehan if (pe->pe_cfgwrite != NULL && 1792221828Sgrehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 1793270159Sgrehan return; 1794221828Sgrehan 1795221828Sgrehan /* 1796221828Sgrehan * Special handling for write to BAR registers 1797221828Sgrehan */ 1798221828Sgrehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { 1799221828Sgrehan /* 1800221828Sgrehan * Ignore writes to BAR registers that are not 1801221828Sgrehan * 4-byte aligned. 1802221828Sgrehan */ 1803221828Sgrehan if (bytes != 4 || (coff & 0x3) != 0) 1804270159Sgrehan return; 1805221828Sgrehan idx = (coff - PCIR_BAR(0)) / 4; 1806249321Sneel mask = ~(pi->pi_bar[idx].size - 1); 1807221828Sgrehan switch (pi->pi_bar[idx].type) { 1808221828Sgrehan case PCIBAR_NONE: 1809249321Sneel pi->pi_bar[idx].addr = bar = 0; 1810221828Sgrehan break; 1811221828Sgrehan case PCIBAR_IO: 1812249321Sneel addr = *eax & mask; 1813249321Sneel addr &= 0xffff; 1814249321Sneel bar = addr | PCIM_BAR_IO_SPACE; 1815249321Sneel /* 1816249321Sneel * Register the new BAR value for interception 1817249321Sneel */ 1818249321Sneel if (addr != pi->pi_bar[idx].addr) { 1819249321Sneel update_bar_address(pi, addr, idx, 1820249321Sneel PCIBAR_IO); 1821249321Sneel } 1822221828Sgrehan break; 1823221828Sgrehan case PCIBAR_MEM32: 1824249321Sneel addr = bar = *eax & mask; 1825221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 1826249321Sneel if (addr != pi->pi_bar[idx].addr) { 1827249321Sneel update_bar_address(pi, addr, idx, 1828249321Sneel PCIBAR_MEM32); 1829249321Sneel } 1830221828Sgrehan break; 1831221828Sgrehan case PCIBAR_MEM64: 1832249321Sneel addr = bar = *eax & mask; 1833221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 1834221828Sgrehan PCIM_BAR_MEM_PREFETCH; 1835249321Sneel if (addr != (uint32_t)pi->pi_bar[idx].addr) { 1836249321Sneel update_bar_address(pi, addr, idx, 1837249321Sneel PCIBAR_MEM64); 1838249321Sneel } 1839221828Sgrehan break; 1840221828Sgrehan case PCIBAR_MEMHI64: 1841221828Sgrehan mask = ~(pi->pi_bar[idx - 1].size - 1); 1842249321Sneel addr = ((uint64_t)*eax << 32) & mask; 1843249321Sneel bar = addr >> 32; 1844249321Sneel if (bar != pi->pi_bar[idx - 1].addr >> 32) { 1845249321Sneel update_bar_address(pi, addr, idx - 1, 1846249321Sneel PCIBAR_MEMHI64); 1847249321Sneel } 1848221828Sgrehan break; 1849221828Sgrehan default: 1850221828Sgrehan assert(0); 1851221828Sgrehan } 1852221828Sgrehan pci_set_cfgdata32(pi, coff, bar); 1853234761Sgrehan 1854221828Sgrehan } else if (pci_emul_iscap(pi, coff)) { 1855221828Sgrehan pci_emul_capwrite(pi, coff, bytes, *eax); 1856249321Sneel } else if (coff == PCIR_COMMAND) { 1857249321Sneel pci_emul_cmdwrite(pi, *eax, bytes); 1858221828Sgrehan } else { 1859221828Sgrehan CFGWRITE(pi, coff, *eax, bytes); 1860221828Sgrehan } 1861221828Sgrehan } 1862270159Sgrehan} 1863221828Sgrehan 1864270159Sgrehanstatic int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 1865270159Sgrehan 1866270159Sgrehanstatic int 1867270159Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1868270159Sgrehan uint32_t *eax, void *arg) 1869270159Sgrehan{ 1870270159Sgrehan uint32_t x; 1871270159Sgrehan 1872270159Sgrehan if (bytes != 4) { 1873270159Sgrehan if (in) 1874270159Sgrehan *eax = (bytes == 2) ? 0xffff : 0xff; 1875270159Sgrehan return (0); 1876270159Sgrehan } 1877270159Sgrehan 1878270159Sgrehan if (in) { 1879270159Sgrehan x = (cfgbus << 16) | (cfgslot << 11) | (cfgfunc << 8) | cfgoff; 1880270159Sgrehan if (cfgenable) 1881270159Sgrehan x |= CONF1_ENABLE; 1882270159Sgrehan *eax = x; 1883270159Sgrehan } else { 1884270159Sgrehan x = *eax; 1885270159Sgrehan cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 1886270159Sgrehan cfgoff = x & PCI_REGMAX; 1887270159Sgrehan cfgfunc = (x >> 8) & PCI_FUNCMAX; 1888270159Sgrehan cfgslot = (x >> 11) & PCI_SLOTMAX; 1889270159Sgrehan cfgbus = (x >> 16) & PCI_BUSMAX; 1890270159Sgrehan } 1891270159Sgrehan 1892221828Sgrehan return (0); 1893221828Sgrehan} 1894270159SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 1895221828Sgrehan 1896270159Sgrehanstatic int 1897270159Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1898270159Sgrehan uint32_t *eax, void *arg) 1899270159Sgrehan{ 1900270159Sgrehan int coff; 1901270159Sgrehan 1902270159Sgrehan assert(bytes == 1 || bytes == 2 || bytes == 4); 1903270159Sgrehan 1904270159Sgrehan coff = cfgoff + (port - CONF1_DATA_PORT); 1905270159Sgrehan if (cfgenable) { 1906270159Sgrehan pci_cfgrw(ctx, vcpu, in, cfgbus, cfgslot, cfgfunc, coff, bytes, 1907270159Sgrehan eax); 1908270159Sgrehan } else { 1909270159Sgrehan /* Ignore accesses to cfgdata if not enabled by cfgaddr */ 1910270159Sgrehan if (in) 1911270159Sgrehan *eax = 0xffffffff; 1912270159Sgrehan } 1913270159Sgrehan return (0); 1914270159Sgrehan} 1915270159Sgrehan 1916221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 1917221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 1918221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 1919221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 1920221828Sgrehan 1921221828Sgrehan#define PCI_EMUL_TEST 1922221828Sgrehan#ifdef PCI_EMUL_TEST 1923221828Sgrehan/* 1924221828Sgrehan * Define a dummy test device 1925221828Sgrehan */ 1926268887Sjhb#define DIOSZ 8 1927241744Sgrehan#define DMEMSZ 4096 1928221828Sgrehanstruct pci_emul_dsoftc { 1929241744Sgrehan uint8_t ioregs[DIOSZ]; 1930241744Sgrehan uint8_t memregs[DMEMSZ]; 1931221828Sgrehan}; 1932221828Sgrehan 1933241744Sgrehan#define PCI_EMUL_MSI_MSGS 4 1934241744Sgrehan#define PCI_EMUL_MSIX_MSGS 16 1935221828Sgrehan 1936221942Sjhbstatic int 1937221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 1938221828Sgrehan{ 1939221828Sgrehan int error; 1940221828Sgrehan struct pci_emul_dsoftc *sc; 1941221828Sgrehan 1942268953Sjhb sc = calloc(1, sizeof(struct pci_emul_dsoftc)); 1943221828Sgrehan 1944221828Sgrehan pi->pi_arg = sc; 1945221828Sgrehan 1946221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 1947221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 1948221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 1949221828Sgrehan 1950241744Sgrehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 1951221828Sgrehan assert(error == 0); 1952221828Sgrehan 1953241744Sgrehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 1954221828Sgrehan assert(error == 0); 1955221828Sgrehan 1956241744Sgrehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 1957241744Sgrehan assert(error == 0); 1958241744Sgrehan 1959221828Sgrehan return (0); 1960221828Sgrehan} 1961221828Sgrehan 1962221942Sjhbstatic void 1963241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 1964241744Sgrehan uint64_t offset, int size, uint64_t value) 1965221828Sgrehan{ 1966221828Sgrehan int i; 1967221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1968221828Sgrehan 1969241744Sgrehan if (baridx == 0) { 1970241744Sgrehan if (offset + size > DIOSZ) { 1971241744Sgrehan printf("diow: iow too large, offset %ld size %d\n", 1972241744Sgrehan offset, size); 1973241744Sgrehan return; 1974241744Sgrehan } 1975221828Sgrehan 1976241744Sgrehan if (size == 1) { 1977241744Sgrehan sc->ioregs[offset] = value & 0xff; 1978241744Sgrehan } else if (size == 2) { 1979241744Sgrehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 1980241744Sgrehan } else if (size == 4) { 1981241744Sgrehan *(uint32_t *)&sc->ioregs[offset] = value; 1982241744Sgrehan } else { 1983241744Sgrehan printf("diow: iow unknown size %d\n", size); 1984241744Sgrehan } 1985241744Sgrehan 1986241744Sgrehan /* 1987241744Sgrehan * Special magic value to generate an interrupt 1988241744Sgrehan */ 1989241744Sgrehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 1990262350Sjhb pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 1991241744Sgrehan 1992241744Sgrehan if (value == 0xabcdef) { 1993262350Sjhb for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 1994241744Sgrehan pci_generate_msi(pi, i); 1995241744Sgrehan } 1996221828Sgrehan } 1997221828Sgrehan 1998241744Sgrehan if (baridx == 1) { 1999241744Sgrehan if (offset + size > DMEMSZ) { 2000241744Sgrehan printf("diow: memw too large, offset %ld size %d\n", 2001241744Sgrehan offset, size); 2002241744Sgrehan return; 2003241744Sgrehan } 2004221828Sgrehan 2005241744Sgrehan if (size == 1) { 2006241744Sgrehan sc->memregs[offset] = value; 2007241744Sgrehan } else if (size == 2) { 2008241744Sgrehan *(uint16_t *)&sc->memregs[offset] = value; 2009241744Sgrehan } else if (size == 4) { 2010241744Sgrehan *(uint32_t *)&sc->memregs[offset] = value; 2011241744Sgrehan } else if (size == 8) { 2012241744Sgrehan *(uint64_t *)&sc->memregs[offset] = value; 2013241744Sgrehan } else { 2014241744Sgrehan printf("diow: memw unknown size %d\n", size); 2015241744Sgrehan } 2016241744Sgrehan 2017241744Sgrehan /* 2018241744Sgrehan * magic interrupt ?? 2019241744Sgrehan */ 2020221828Sgrehan } 2021241744Sgrehan 2022241744Sgrehan if (baridx > 1) { 2023241744Sgrehan printf("diow: unknown bar idx %d\n", baridx); 2024241744Sgrehan } 2025221828Sgrehan} 2026221828Sgrehan 2027241744Sgrehanstatic uint64_t 2028241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 2029241744Sgrehan uint64_t offset, int size) 2030221828Sgrehan{ 2031221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 2032221828Sgrehan uint32_t value; 2033221828Sgrehan 2034241744Sgrehan if (baridx == 0) { 2035241744Sgrehan if (offset + size > DIOSZ) { 2036241744Sgrehan printf("dior: ior too large, offset %ld size %d\n", 2037241744Sgrehan offset, size); 2038241744Sgrehan return (0); 2039241744Sgrehan } 2040241744Sgrehan 2041241744Sgrehan if (size == 1) { 2042241744Sgrehan value = sc->ioregs[offset]; 2043241744Sgrehan } else if (size == 2) { 2044241744Sgrehan value = *(uint16_t *) &sc->ioregs[offset]; 2045241744Sgrehan } else if (size == 4) { 2046241744Sgrehan value = *(uint32_t *) &sc->ioregs[offset]; 2047241744Sgrehan } else { 2048241744Sgrehan printf("dior: ior unknown size %d\n", size); 2049241744Sgrehan } 2050221828Sgrehan } 2051221828Sgrehan 2052241744Sgrehan if (baridx == 1) { 2053241744Sgrehan if (offset + size > DMEMSZ) { 2054241744Sgrehan printf("dior: memr too large, offset %ld size %d\n", 2055241744Sgrehan offset, size); 2056241744Sgrehan return (0); 2057241744Sgrehan } 2058241744Sgrehan 2059241744Sgrehan if (size == 1) { 2060241744Sgrehan value = sc->memregs[offset]; 2061241744Sgrehan } else if (size == 2) { 2062241744Sgrehan value = *(uint16_t *) &sc->memregs[offset]; 2063241744Sgrehan } else if (size == 4) { 2064241744Sgrehan value = *(uint32_t *) &sc->memregs[offset]; 2065241744Sgrehan } else if (size == 8) { 2066241744Sgrehan value = *(uint64_t *) &sc->memregs[offset]; 2067241744Sgrehan } else { 2068241744Sgrehan printf("dior: ior unknown size %d\n", size); 2069241744Sgrehan } 2070221828Sgrehan } 2071221828Sgrehan 2072241744Sgrehan 2073241744Sgrehan if (baridx > 1) { 2074241744Sgrehan printf("dior: unknown bar idx %d\n", baridx); 2075241744Sgrehan return (0); 2076241744Sgrehan } 2077241744Sgrehan 2078221828Sgrehan return (value); 2079221828Sgrehan} 2080221828Sgrehan 2081221828Sgrehanstruct pci_devemu pci_dummy = { 2082221828Sgrehan .pe_emu = "dummy", 2083221828Sgrehan .pe_init = pci_emul_dinit, 2084241744Sgrehan .pe_barwrite = pci_emul_diow, 2085241744Sgrehan .pe_barread = pci_emul_dior 2086221828Sgrehan}; 2087221828SgrehanPCI_EMUL_SET(pci_dummy); 2088221828Sgrehan 2089221828Sgrehan#endif /* PCI_EMUL_TEST */ 2090