pci_emul.c revision 268972
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: stable/10/usr.sbin/bhyve/pci_emul.c 268972 2014-07-22 03:14:37Z jhb $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: stable/10/usr.sbin/bhyve/pci_emul.c 268972 2014-07-22 03:14:37Z jhb $"); 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 112268887Sjhb#define PCI_EMUL_MEMLIMIT32 0xE0000000 /* 3.5GB */ 113221828Sgrehan 114221828Sgrehan#define PCI_EMUL_MEMBASE64 0xD000000000UL 115221828Sgrehan#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL 116221828Sgrehan 117249916Sneelstatic struct pci_devemu *pci_emul_finddev(char *name); 118268972Sjhbstatic void pci_lintr_route(struct pci_devinst *pi); 119267393Sjhbstatic void pci_lintr_update(struct pci_devinst *pi); 120249916Sneel 121261265Sjhbstatic struct mem_range pci_mem_hole; 122221828Sgrehan 123221828Sgrehan/* 124221828Sgrehan * I/O access 125221828Sgrehan */ 126221828Sgrehan 127221828Sgrehan/* 128221828Sgrehan * Slot options are in the form: 129221828Sgrehan * 130268887Sjhb * <bus>:<slot>:<func>,<emul>[,<config>] 131239085Sneel * <slot>[:<func>],<emul>[,<config>] 132221828Sgrehan * 133221828Sgrehan * slot is 0..31 134239085Sneel * func is 0..7 135221828Sgrehan * emul is a string describing the type of PCI device e.g. virtio-net 136221828Sgrehan * config is an optional string, depending on the device, that can be 137221828Sgrehan * used for configuration. 138221828Sgrehan * Examples are: 139221828Sgrehan * 1,virtio-net,tap0 140239085Sneel * 3:0,dummy 141221828Sgrehan */ 142221828Sgrehanstatic void 143221828Sgrehanpci_parse_slot_usage(char *aopt) 144221828Sgrehan{ 145249916Sneel 146249916Sneel fprintf(stderr, "Invalid PCI slot info field \"%s\"\n", aopt); 147221828Sgrehan} 148221828Sgrehan 149249916Sneelint 150267341Sjhbpci_parse_slot(char *opt) 151221828Sgrehan{ 152268887Sjhb struct businfo *bi; 153268887Sjhb struct slotinfo *si; 154268887Sjhb char *emul, *config, *str, *cp; 155268887Sjhb int error, bnum, snum, fnum; 156221828Sgrehan 157249916Sneel error = -1; 158268887Sjhb str = strdup(opt); 159239085Sneel 160268887Sjhb emul = config = NULL; 161268887Sjhb if ((cp = strchr(str, ',')) != NULL) { 162268887Sjhb *cp = '\0'; 163268887Sjhb emul = cp + 1; 164268887Sjhb if ((cp = strchr(emul, ',')) != NULL) { 165268887Sjhb *cp = '\0'; 166268887Sjhb config = cp + 1; 167268887Sjhb } 168268887Sjhb } else { 169249916Sneel pci_parse_slot_usage(opt); 170249916Sneel goto done; 171221828Sgrehan } 172221828Sgrehan 173268887Sjhb /* <bus>:<slot>:<func> */ 174268887Sjhb if (sscanf(str, "%d:%d:%d", &bnum, &snum, &fnum) != 3) { 175268887Sjhb bnum = 0; 176268887Sjhb /* <slot>:<func> */ 177268887Sjhb if (sscanf(str, "%d:%d", &snum, &fnum) != 2) { 178268887Sjhb fnum = 0; 179268887Sjhb /* <slot> */ 180268887Sjhb if (sscanf(str, "%d", &snum) != 1) { 181268887Sjhb snum = -1; 182268887Sjhb } 183268887Sjhb } 184268887Sjhb } 185249916Sneel 186268887Sjhb if (bnum < 0 || bnum >= MAXBUSES || snum < 0 || snum >= MAXSLOTS || 187268887Sjhb fnum < 0 || fnum >= MAXFUNCS) { 188249916Sneel pci_parse_slot_usage(opt); 189249916Sneel goto done; 190221828Sgrehan } 191249916Sneel 192268887Sjhb if (pci_businfo[bnum] == NULL) 193268887Sjhb pci_businfo[bnum] = calloc(1, sizeof(struct businfo)); 194268887Sjhb 195268887Sjhb bi = pci_businfo[bnum]; 196268887Sjhb si = &bi->slotinfo[snum]; 197268887Sjhb 198268887Sjhb if (si->si_funcs[fnum].fi_name != NULL) { 199249916Sneel fprintf(stderr, "pci slot %d:%d already occupied!\n", 200249916Sneel snum, fnum); 201249916Sneel goto done; 202249916Sneel } 203249916Sneel 204249916Sneel if (pci_emul_finddev(emul) == NULL) { 205249916Sneel fprintf(stderr, "pci slot %d:%d: unknown device \"%s\"\n", 206249916Sneel snum, fnum, emul); 207249916Sneel goto done; 208249916Sneel } 209249916Sneel 210249916Sneel error = 0; 211268887Sjhb si->si_funcs[fnum].fi_name = emul; 212268887Sjhb si->si_funcs[fnum].fi_param = config; 213249916Sneel 214249916Sneeldone: 215249916Sneel if (error) 216268887Sjhb free(str); 217249916Sneel 218249916Sneel return (error); 219221828Sgrehan} 220221828Sgrehan 221221828Sgrehanstatic int 222246109Sneelpci_valid_pba_offset(struct pci_devinst *pi, uint64_t offset) 223246109Sneel{ 224246109Sneel 225246109Sneel if (offset < pi->pi_msix.pba_offset) 226246109Sneel return (0); 227246109Sneel 228246109Sneel if (offset >= pi->pi_msix.pba_offset + pi->pi_msix.pba_size) { 229246109Sneel return (0); 230246109Sneel } 231246109Sneel 232246109Sneel return (1); 233246109Sneel} 234246109Sneel 235246109Sneelint 236246109Sneelpci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size, 237246109Sneel uint64_t value) 238246109Sneel{ 239246109Sneel int msix_entry_offset; 240246109Sneel int tab_index; 241246109Sneel char *dest; 242246109Sneel 243246109Sneel /* support only 4 or 8 byte writes */ 244246109Sneel if (size != 4 && size != 8) 245246109Sneel return (-1); 246246109Sneel 247246109Sneel /* 248246109Sneel * Return if table index is beyond what device supports 249246109Sneel */ 250246109Sneel tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 251246109Sneel if (tab_index >= pi->pi_msix.table_count) 252246109Sneel return (-1); 253246109Sneel 254246109Sneel msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 255246109Sneel 256246109Sneel /* support only aligned writes */ 257246109Sneel if ((msix_entry_offset % size) != 0) 258246109Sneel return (-1); 259246109Sneel 260246109Sneel dest = (char *)(pi->pi_msix.table + tab_index); 261246109Sneel dest += msix_entry_offset; 262246109Sneel 263246109Sneel if (size == 4) 264246109Sneel *((uint32_t *)dest) = value; 265246109Sneel else 266246109Sneel *((uint64_t *)dest) = value; 267246109Sneel 268246109Sneel return (0); 269246109Sneel} 270246109Sneel 271246109Sneeluint64_t 272246109Sneelpci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size) 273246109Sneel{ 274246109Sneel char *dest; 275246109Sneel int msix_entry_offset; 276246109Sneel int tab_index; 277246109Sneel uint64_t retval = ~0; 278246109Sneel 279254965Sneel /* 280254965Sneel * The PCI standard only allows 4 and 8 byte accesses to the MSI-X 281254965Sneel * table but we also allow 1 byte access to accomodate reads from 282254965Sneel * ddb. 283254965Sneel */ 284254965Sneel if (size != 1 && size != 4 && size != 8) 285246109Sneel return (retval); 286246109Sneel 287246109Sneel msix_entry_offset = offset % MSIX_TABLE_ENTRY_SIZE; 288246109Sneel 289246109Sneel /* support only aligned reads */ 290246109Sneel if ((msix_entry_offset % size) != 0) { 291246109Sneel return (retval); 292246109Sneel } 293246109Sneel 294246109Sneel tab_index = offset / MSIX_TABLE_ENTRY_SIZE; 295246109Sneel 296246109Sneel if (tab_index < pi->pi_msix.table_count) { 297246109Sneel /* valid MSI-X Table access */ 298246109Sneel dest = (char *)(pi->pi_msix.table + tab_index); 299246109Sneel dest += msix_entry_offset; 300246109Sneel 301254965Sneel if (size == 1) 302254965Sneel retval = *((uint8_t *)dest); 303254965Sneel else if (size == 4) 304246109Sneel retval = *((uint32_t *)dest); 305246109Sneel else 306246109Sneel retval = *((uint64_t *)dest); 307246109Sneel } else if (pci_valid_pba_offset(pi, offset)) { 308246109Sneel /* return 0 for PBA access */ 309246109Sneel retval = 0; 310246109Sneel } 311246109Sneel 312246109Sneel return (retval); 313246109Sneel} 314246109Sneel 315246190Sneelint 316246190Sneelpci_msix_table_bar(struct pci_devinst *pi) 317246190Sneel{ 318246190Sneel 319246190Sneel if (pi->pi_msix.table != NULL) 320246190Sneel return (pi->pi_msix.table_bar); 321246190Sneel else 322246190Sneel return (-1); 323246190Sneel} 324246190Sneel 325246190Sneelint 326246190Sneelpci_msix_pba_bar(struct pci_devinst *pi) 327246190Sneel{ 328246190Sneel 329246190Sneel if (pi->pi_msix.table != NULL) 330246190Sneel return (pi->pi_msix.pba_bar); 331246190Sneel else 332246190Sneel return (-1); 333246190Sneel} 334246190Sneel 335246109Sneelstatic int 336241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 337241744Sgrehan uint32_t *eax, void *arg) 338221828Sgrehan{ 339221828Sgrehan struct pci_devinst *pdi = arg; 340221828Sgrehan struct pci_devemu *pe = pdi->pi_d; 341241744Sgrehan uint64_t offset; 342241744Sgrehan int i; 343221828Sgrehan 344221828Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 345221828Sgrehan if (pdi->pi_bar[i].type == PCIBAR_IO && 346221828Sgrehan port >= pdi->pi_bar[i].addr && 347246109Sneel port + bytes <= pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 348221828Sgrehan offset = port - pdi->pi_bar[i].addr; 349221828Sgrehan if (in) 350241744Sgrehan *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, 351241744Sgrehan offset, bytes); 352221828Sgrehan else 353241744Sgrehan (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, 354241744Sgrehan bytes, *eax); 355221828Sgrehan return (0); 356221828Sgrehan } 357221828Sgrehan } 358221828Sgrehan return (-1); 359221828Sgrehan} 360221828Sgrehan 361221828Sgrehanstatic int 362241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 363241744Sgrehan int size, uint64_t *val, void *arg1, long arg2) 364241744Sgrehan{ 365241744Sgrehan struct pci_devinst *pdi = arg1; 366241744Sgrehan struct pci_devemu *pe = pdi->pi_d; 367241744Sgrehan uint64_t offset; 368241744Sgrehan int bidx = (int) arg2; 369241744Sgrehan 370241744Sgrehan assert(bidx <= PCI_BARMAX); 371241744Sgrehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 372241744Sgrehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 373241744Sgrehan assert(addr >= pdi->pi_bar[bidx].addr && 374241744Sgrehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 375241744Sgrehan 376241744Sgrehan offset = addr - pdi->pi_bar[bidx].addr; 377241744Sgrehan 378268887Sjhb if (dir == MEM_F_WRITE) { 379268887Sjhb if (size == 8) { 380268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 381268887Sjhb 4, *val & 0xffffffff); 382268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset + 4, 383268887Sjhb 4, *val >> 32); 384268887Sjhb } else { 385268887Sjhb (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, 386268887Sjhb size, *val); 387268887Sjhb } 388268887Sjhb } else { 389268887Sjhb if (size == 8) { 390268887Sjhb *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 391268887Sjhb offset, 4); 392268887Sjhb *val |= (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 393268887Sjhb offset + 4, 4) << 32; 394268887Sjhb } else { 395268887Sjhb *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, 396268887Sjhb offset, size); 397268887Sjhb } 398268887Sjhb } 399241744Sgrehan 400241744Sgrehan return (0); 401241744Sgrehan} 402241744Sgrehan 403241744Sgrehan 404241744Sgrehanstatic int 405221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 406221828Sgrehan uint64_t *addr) 407221828Sgrehan{ 408221828Sgrehan uint64_t base; 409221828Sgrehan 410221828Sgrehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 411221828Sgrehan 412221828Sgrehan base = roundup2(*baseptr, size); 413221828Sgrehan 414221828Sgrehan if (base + size <= limit) { 415221828Sgrehan *addr = base; 416221828Sgrehan *baseptr = base + size; 417221828Sgrehan return (0); 418221828Sgrehan } else 419221828Sgrehan return (-1); 420221828Sgrehan} 421221828Sgrehan 422221828Sgrehanint 423241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 424241744Sgrehan uint64_t size) 425221828Sgrehan{ 426241744Sgrehan 427241744Sgrehan return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); 428241744Sgrehan} 429241744Sgrehan 430249321Sneel/* 431249321Sneel * Register (or unregister) the MMIO or I/O region associated with the BAR 432249321Sneel * register 'idx' of an emulated pci device. 433249321Sneel */ 434249321Sneelstatic void 435249321Sneelmodify_bar_registration(struct pci_devinst *pi, int idx, int registration) 436249321Sneel{ 437249321Sneel int error; 438249321Sneel struct inout_port iop; 439249321Sneel struct mem_range mr; 440249321Sneel 441249321Sneel switch (pi->pi_bar[idx].type) { 442249321Sneel case PCIBAR_IO: 443249321Sneel bzero(&iop, sizeof(struct inout_port)); 444249321Sneel iop.name = pi->pi_name; 445249321Sneel iop.port = pi->pi_bar[idx].addr; 446249321Sneel iop.size = pi->pi_bar[idx].size; 447249321Sneel if (registration) { 448249321Sneel iop.flags = IOPORT_F_INOUT; 449249321Sneel iop.handler = pci_emul_io_handler; 450249321Sneel iop.arg = pi; 451249321Sneel error = register_inout(&iop); 452249321Sneel } else 453249321Sneel error = unregister_inout(&iop); 454249321Sneel break; 455249321Sneel case PCIBAR_MEM32: 456249321Sneel case PCIBAR_MEM64: 457249321Sneel bzero(&mr, sizeof(struct mem_range)); 458249321Sneel mr.name = pi->pi_name; 459249321Sneel mr.base = pi->pi_bar[idx].addr; 460249321Sneel mr.size = pi->pi_bar[idx].size; 461249321Sneel if (registration) { 462249321Sneel mr.flags = MEM_F_RW; 463249321Sneel mr.handler = pci_emul_mem_handler; 464249321Sneel mr.arg1 = pi; 465249321Sneel mr.arg2 = idx; 466249321Sneel error = register_mem(&mr); 467249321Sneel } else 468249321Sneel error = unregister_mem(&mr); 469249321Sneel break; 470249321Sneel default: 471249321Sneel error = EINVAL; 472249321Sneel break; 473249321Sneel } 474249321Sneel assert(error == 0); 475249321Sneel} 476249321Sneel 477249321Sneelstatic void 478249321Sneelunregister_bar(struct pci_devinst *pi, int idx) 479249321Sneel{ 480249321Sneel 481249321Sneel modify_bar_registration(pi, idx, 0); 482249321Sneel} 483249321Sneel 484249321Sneelstatic void 485249321Sneelregister_bar(struct pci_devinst *pi, int idx) 486249321Sneel{ 487249321Sneel 488249321Sneel modify_bar_registration(pi, idx, 1); 489249321Sneel} 490249321Sneel 491249321Sneel/* Are we decoding i/o port accesses for the emulated pci device? */ 492249321Sneelstatic int 493249321Sneelporten(struct pci_devinst *pi) 494249321Sneel{ 495249321Sneel uint16_t cmd; 496249321Sneel 497249321Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 498249321Sneel 499249321Sneel return (cmd & PCIM_CMD_PORTEN); 500249321Sneel} 501249321Sneel 502249321Sneel/* Are we decoding memory accesses for the emulated pci device? */ 503249321Sneelstatic int 504249321Sneelmemen(struct pci_devinst *pi) 505249321Sneel{ 506249321Sneel uint16_t cmd; 507249321Sneel 508249321Sneel cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 509249321Sneel 510249321Sneel return (cmd & PCIM_CMD_MEMEN); 511249321Sneel} 512249321Sneel 513249321Sneel/* 514249321Sneel * Update the MMIO or I/O address that is decoded by the BAR register. 515249321Sneel * 516249321Sneel * If the pci device has enabled the address space decoding then intercept 517249321Sneel * the address range decoded by the BAR register. 518249321Sneel */ 519249321Sneelstatic void 520249321Sneelupdate_bar_address(struct pci_devinst *pi, uint64_t addr, int idx, int type) 521249321Sneel{ 522249321Sneel int decode; 523249321Sneel 524249321Sneel if (pi->pi_bar[idx].type == PCIBAR_IO) 525249321Sneel decode = porten(pi); 526249321Sneel else 527249321Sneel decode = memen(pi); 528249321Sneel 529249321Sneel if (decode) 530249321Sneel unregister_bar(pi, idx); 531249321Sneel 532249321Sneel switch (type) { 533249321Sneel case PCIBAR_IO: 534249321Sneel case PCIBAR_MEM32: 535249321Sneel pi->pi_bar[idx].addr = addr; 536249321Sneel break; 537249321Sneel case PCIBAR_MEM64: 538249321Sneel pi->pi_bar[idx].addr &= ~0xffffffffUL; 539249321Sneel pi->pi_bar[idx].addr |= addr; 540249321Sneel break; 541249321Sneel case PCIBAR_MEMHI64: 542249321Sneel pi->pi_bar[idx].addr &= 0xffffffff; 543249321Sneel pi->pi_bar[idx].addr |= addr; 544249321Sneel break; 545249321Sneel default: 546249321Sneel assert(0); 547249321Sneel } 548249321Sneel 549249321Sneel if (decode) 550249321Sneel register_bar(pi, idx); 551249321Sneel} 552249321Sneel 553241744Sgrehanint 554241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, 555241744Sgrehan enum pcibar_type type, uint64_t size) 556241744Sgrehan{ 557249321Sneel int error; 558221828Sgrehan uint64_t *baseptr, limit, addr, mask, lobits, bar; 559221828Sgrehan 560221828Sgrehan assert(idx >= 0 && idx <= PCI_BARMAX); 561221828Sgrehan 562221828Sgrehan if ((size & (size - 1)) != 0) 563221828Sgrehan size = 1UL << flsl(size); /* round up to a power of 2 */ 564221828Sgrehan 565249321Sneel /* Enforce minimum BAR sizes required by the PCI standard */ 566249321Sneel if (type == PCIBAR_IO) { 567249321Sneel if (size < 4) 568249321Sneel size = 4; 569249321Sneel } else { 570249321Sneel if (size < 16) 571249321Sneel size = 16; 572249321Sneel } 573249321Sneel 574221828Sgrehan switch (type) { 575221828Sgrehan case PCIBAR_NONE: 576221828Sgrehan baseptr = NULL; 577221828Sgrehan addr = mask = lobits = 0; 578221828Sgrehan break; 579221828Sgrehan case PCIBAR_IO: 580267341Sjhb baseptr = &pci_emul_iobase; 581221828Sgrehan limit = PCI_EMUL_IOLIMIT; 582221828Sgrehan mask = PCIM_BAR_IO_BASE; 583221828Sgrehan lobits = PCIM_BAR_IO_SPACE; 584221828Sgrehan break; 585221828Sgrehan case PCIBAR_MEM64: 586221828Sgrehan /* 587221828Sgrehan * XXX 588221828Sgrehan * Some drivers do not work well if the 64-bit BAR is allocated 589221828Sgrehan * above 4GB. Allow for this by allocating small requests under 590221828Sgrehan * 4GB unless then allocation size is larger than some arbitrary 591221828Sgrehan * number (32MB currently). 592221828Sgrehan */ 593221828Sgrehan if (size > 32 * 1024 * 1024) { 594221828Sgrehan /* 595221828Sgrehan * XXX special case for device requiring peer-peer DMA 596221828Sgrehan */ 597221828Sgrehan if (size == 0x100000000UL) 598221828Sgrehan baseptr = &hostbase; 599221828Sgrehan else 600221828Sgrehan baseptr = &pci_emul_membase64; 601221828Sgrehan limit = PCI_EMUL_MEMLIMIT64; 602221828Sgrehan mask = PCIM_BAR_MEM_BASE; 603221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 604221828Sgrehan PCIM_BAR_MEM_PREFETCH; 605221828Sgrehan break; 606239086Sneel } else { 607239086Sneel baseptr = &pci_emul_membase32; 608239086Sneel limit = PCI_EMUL_MEMLIMIT32; 609239086Sneel mask = PCIM_BAR_MEM_BASE; 610239086Sneel lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 611221828Sgrehan } 612239086Sneel break; 613221828Sgrehan case PCIBAR_MEM32: 614221828Sgrehan baseptr = &pci_emul_membase32; 615221828Sgrehan limit = PCI_EMUL_MEMLIMIT32; 616221828Sgrehan mask = PCIM_BAR_MEM_BASE; 617221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 618221828Sgrehan break; 619221828Sgrehan default: 620221828Sgrehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 621221828Sgrehan assert(0); 622221828Sgrehan } 623221828Sgrehan 624221828Sgrehan if (baseptr != NULL) { 625221828Sgrehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 626221828Sgrehan if (error != 0) 627221828Sgrehan return (error); 628221828Sgrehan } 629221828Sgrehan 630221828Sgrehan pdi->pi_bar[idx].type = type; 631221828Sgrehan pdi->pi_bar[idx].addr = addr; 632221828Sgrehan pdi->pi_bar[idx].size = size; 633221828Sgrehan 634221828Sgrehan /* Initialize the BAR register in config space */ 635221828Sgrehan bar = (addr & mask) | lobits; 636221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 637221828Sgrehan 638221828Sgrehan if (type == PCIBAR_MEM64) { 639221828Sgrehan assert(idx + 1 <= PCI_BARMAX); 640221828Sgrehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 641221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 642221828Sgrehan } 643221828Sgrehan 644249321Sneel register_bar(pdi, idx); 645221828Sgrehan 646221828Sgrehan return (0); 647221828Sgrehan} 648221828Sgrehan 649221828Sgrehan#define CAP_START_OFFSET 0x40 650221828Sgrehanstatic int 651221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 652221828Sgrehan{ 653268887Sjhb int i, capoff, reallen; 654221828Sgrehan uint16_t sts; 655221828Sgrehan 656268887Sjhb assert(caplen > 0); 657221828Sgrehan 658221828Sgrehan reallen = roundup2(caplen, 4); /* dword aligned */ 659221828Sgrehan 660221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 661268887Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) 662221828Sgrehan capoff = CAP_START_OFFSET; 663268887Sjhb else 664268887Sjhb capoff = pi->pi_capend + 1; 665221828Sgrehan 666221828Sgrehan /* Check if we have enough space */ 667268887Sjhb if (capoff + reallen > PCI_REGMAX + 1) 668221828Sgrehan return (-1); 669221828Sgrehan 670268887Sjhb /* Set the previous capability pointer */ 671268887Sjhb if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 672268887Sjhb pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 673268887Sjhb pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 674268887Sjhb } else 675268887Sjhb pci_set_cfgdata8(pi, pi->pi_prevcap + 1, capoff); 676268887Sjhb 677221828Sgrehan /* Copy the capability */ 678221828Sgrehan for (i = 0; i < caplen; i++) 679221828Sgrehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 680221828Sgrehan 681221828Sgrehan /* Set the next capability pointer */ 682268887Sjhb pci_set_cfgdata8(pi, capoff + 1, 0); 683221828Sgrehan 684268887Sjhb pi->pi_prevcap = capoff; 685268887Sjhb pi->pi_capend = capoff + reallen - 1; 686221828Sgrehan return (0); 687221828Sgrehan} 688221828Sgrehan 689221828Sgrehanstatic struct pci_devemu * 690221828Sgrehanpci_emul_finddev(char *name) 691221828Sgrehan{ 692221828Sgrehan struct pci_devemu **pdpp, *pdp; 693221828Sgrehan 694221828Sgrehan SET_FOREACH(pdpp, pci_devemu_set) { 695221828Sgrehan pdp = *pdpp; 696221828Sgrehan if (!strcmp(pdp->pe_emu, name)) { 697221828Sgrehan return (pdp); 698221828Sgrehan } 699221828Sgrehan } 700221828Sgrehan 701221828Sgrehan return (NULL); 702221828Sgrehan} 703221828Sgrehan 704252682Sgrehanstatic int 705268887Sjhbpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot, 706268887Sjhb int func, struct funcinfo *fi) 707221828Sgrehan{ 708221828Sgrehan struct pci_devinst *pdi; 709252682Sgrehan int err; 710252682Sgrehan 711268953Sjhb pdi = calloc(1, sizeof(struct pci_devinst)); 712221828Sgrehan 713221828Sgrehan pdi->pi_vmctx = ctx; 714268887Sjhb pdi->pi_bus = bus; 715221828Sgrehan pdi->pi_slot = slot; 716239085Sneel pdi->pi_func = func; 717267393Sjhb pthread_mutex_init(&pdi->pi_lintr.lock, NULL); 718267393Sjhb pdi->pi_lintr.pin = 0; 719267393Sjhb pdi->pi_lintr.state = IDLE; 720268972Sjhb pdi->pi_lintr.pirq_pin = 0; 721267393Sjhb pdi->pi_lintr.ioapic_irq = 0; 722221828Sgrehan pdi->pi_d = pde; 723221828Sgrehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 724221828Sgrehan 725221828Sgrehan /* Disable legacy interrupts */ 726221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 727221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 728221828Sgrehan 729221828Sgrehan pci_set_cfgdata8(pdi, PCIR_COMMAND, 730221828Sgrehan PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 731221828Sgrehan 732268887Sjhb err = (*pde->pe_init)(ctx, pdi, fi->fi_param); 733268887Sjhb if (err == 0) 734268887Sjhb fi->fi_devi = pdi; 735268887Sjhb else 736221828Sgrehan free(pdi); 737252682Sgrehan 738252682Sgrehan return (err); 739221828Sgrehan} 740221828Sgrehan 741221828Sgrehanvoid 742221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 743221828Sgrehan{ 744221828Sgrehan int mmc; 745221828Sgrehan 746221828Sgrehan CTASSERT(sizeof(struct msicap) == 14); 747221828Sgrehan 748221828Sgrehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 749221828Sgrehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 750221828Sgrehan mmc = ffs(msgnum) - 1; 751221828Sgrehan 752221828Sgrehan bzero(msicap, sizeof(struct msicap)); 753221828Sgrehan msicap->capid = PCIY_MSI; 754221828Sgrehan msicap->nextptr = nextptr; 755221828Sgrehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 756221828Sgrehan} 757221828Sgrehan 758221828Sgrehanint 759221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 760221828Sgrehan{ 761221828Sgrehan struct msicap msicap; 762221828Sgrehan 763221828Sgrehan pci_populate_msicap(&msicap, msgnum, 0); 764221828Sgrehan 765221828Sgrehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 766221828Sgrehan} 767221828Sgrehan 768246109Sneelstatic void 769246109Sneelpci_populate_msixcap(struct msixcap *msixcap, int msgnum, int barnum, 770268887Sjhb uint32_t msix_tab_size) 771246109Sneel{ 772246109Sneel CTASSERT(sizeof(struct msixcap) == 12); 773246109Sneel 774246109Sneel assert(msix_tab_size % 4096 == 0); 775246109Sneel 776246109Sneel bzero(msixcap, sizeof(struct msixcap)); 777246109Sneel msixcap->capid = PCIY_MSIX; 778246109Sneel 779246109Sneel /* 780246109Sneel * Message Control Register, all fields set to 781246109Sneel * zero except for the Table Size. 782246109Sneel * Note: Table size N is encoded as N-1 783246109Sneel */ 784246109Sneel msixcap->msgctrl = msgnum - 1; 785246109Sneel 786246109Sneel /* 787246109Sneel * MSI-X BAR setup: 788246109Sneel * - MSI-X table start at offset 0 789246109Sneel * - PBA table starts at a 4K aligned offset after the MSI-X table 790246109Sneel */ 791246109Sneel msixcap->table_info = barnum & PCIM_MSIX_BIR_MASK; 792246109Sneel msixcap->pba_info = msix_tab_size | (barnum & PCIM_MSIX_BIR_MASK); 793246109Sneel} 794246109Sneel 795246109Sneelstatic void 796246109Sneelpci_msix_table_init(struct pci_devinst *pi, int table_entries) 797246109Sneel{ 798246109Sneel int i, table_size; 799246109Sneel 800246109Sneel assert(table_entries > 0); 801246109Sneel assert(table_entries <= MAX_MSIX_TABLE_ENTRIES); 802246109Sneel 803246109Sneel table_size = table_entries * MSIX_TABLE_ENTRY_SIZE; 804268953Sjhb pi->pi_msix.table = calloc(1, table_size); 805246109Sneel 806246109Sneel /* set mask bit of vector control register */ 807246109Sneel for (i = 0; i < table_entries; i++) 808246109Sneel pi->pi_msix.table[i].vector_control |= PCIM_MSIX_VCTRL_MASK; 809246109Sneel} 810246109Sneel 811246109Sneelint 812246109Sneelpci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum) 813246109Sneel{ 814246109Sneel uint32_t tab_size; 815246109Sneel struct msixcap msixcap; 816246109Sneel 817246109Sneel assert(msgnum >= 1 && msgnum <= MAX_MSIX_TABLE_ENTRIES); 818246109Sneel assert(barnum >= 0 && barnum <= PCIR_MAX_BAR_0); 819246109Sneel 820246109Sneel tab_size = msgnum * MSIX_TABLE_ENTRY_SIZE; 821246109Sneel 822246109Sneel /* Align table size to nearest 4K */ 823246109Sneel tab_size = roundup2(tab_size, 4096); 824246109Sneel 825246109Sneel pi->pi_msix.table_bar = barnum; 826246109Sneel pi->pi_msix.pba_bar = barnum; 827246109Sneel pi->pi_msix.table_offset = 0; 828246109Sneel pi->pi_msix.table_count = msgnum; 829246109Sneel pi->pi_msix.pba_offset = tab_size; 830268887Sjhb pi->pi_msix.pba_size = PBA_SIZE(msgnum); 831246109Sneel 832246109Sneel pci_msix_table_init(pi, msgnum); 833246109Sneel 834268887Sjhb pci_populate_msixcap(&msixcap, msgnum, barnum, tab_size); 835246109Sneel 836246109Sneel /* allocate memory for MSI-X Table and PBA */ 837246109Sneel pci_emul_alloc_bar(pi, barnum, PCIBAR_MEM32, 838246109Sneel tab_size + pi->pi_msix.pba_size); 839246109Sneel 840246109Sneel return (pci_emul_add_capability(pi, (u_char *)&msixcap, 841246109Sneel sizeof(msixcap))); 842246109Sneel} 843246109Sneel 844221828Sgrehanvoid 845234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 846234761Sgrehan int bytes, uint32_t val) 847234761Sgrehan{ 848234761Sgrehan uint16_t msgctrl, rwmask; 849234761Sgrehan int off, table_bar; 850246109Sneel 851234761Sgrehan off = offset - capoff; 852234761Sgrehan table_bar = pi->pi_msix.table_bar; 853234761Sgrehan /* Message Control Register */ 854234761Sgrehan if (off == 2 && bytes == 2) { 855234761Sgrehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 856234761Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 857234761Sgrehan msgctrl &= ~rwmask; 858234761Sgrehan msgctrl |= val & rwmask; 859234761Sgrehan val = msgctrl; 860234761Sgrehan 861234761Sgrehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 862246109Sneel pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK; 863267393Sjhb pci_lintr_update(pi); 864234761Sgrehan } 865234761Sgrehan 866234761Sgrehan CFGWRITE(pi, offset, val, bytes); 867234761Sgrehan} 868234761Sgrehan 869234761Sgrehanvoid 870221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 871221828Sgrehan int bytes, uint32_t val) 872221828Sgrehan{ 873221828Sgrehan uint16_t msgctrl, rwmask, msgdata, mme; 874221828Sgrehan uint32_t addrlo; 875221828Sgrehan 876221828Sgrehan /* 877221828Sgrehan * If guest is writing to the message control register make sure 878221828Sgrehan * we do not overwrite read-only fields. 879221828Sgrehan */ 880221828Sgrehan if ((offset - capoff) == 2 && bytes == 2) { 881221828Sgrehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 882221828Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 883221828Sgrehan msgctrl &= ~rwmask; 884221828Sgrehan msgctrl |= val & rwmask; 885221828Sgrehan val = msgctrl; 886221828Sgrehan 887221828Sgrehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 888221828Sgrehan if (msgctrl & PCIM_MSICTRL_64BIT) 889221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 890221828Sgrehan else 891221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 892221828Sgrehan 893221828Sgrehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 894221828Sgrehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 895221828Sgrehan if (pi->pi_msi.enabled) { 896262350Sjhb pi->pi_msi.addr = addrlo; 897262350Sjhb pi->pi_msi.msg_data = msgdata; 898262350Sjhb pi->pi_msi.maxmsgnum = 1 << (mme >> 4); 899221828Sgrehan } else { 900262350Sjhb pi->pi_msi.maxmsgnum = 0; 901221828Sgrehan } 902267393Sjhb pci_lintr_update(pi); 903221828Sgrehan } 904221828Sgrehan 905221828Sgrehan CFGWRITE(pi, offset, val, bytes); 906221828Sgrehan} 907221828Sgrehan 908246846Sneelvoid 909246846Sneelpciecap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 910246846Sneel int bytes, uint32_t val) 911246846Sneel{ 912246846Sneel 913246846Sneel /* XXX don't write to the readonly parts */ 914246846Sneel CFGWRITE(pi, offset, val, bytes); 915246846Sneel} 916246846Sneel 917246846Sneel#define PCIECAP_VERSION 0x2 918246846Sneelint 919246846Sneelpci_emul_add_pciecap(struct pci_devinst *pi, int type) 920246846Sneel{ 921246846Sneel int err; 922246846Sneel struct pciecap pciecap; 923246846Sneel 924246846Sneel CTASSERT(sizeof(struct pciecap) == 60); 925246846Sneel 926246846Sneel if (type != PCIEM_TYPE_ROOT_PORT) 927246846Sneel return (-1); 928246846Sneel 929246846Sneel bzero(&pciecap, sizeof(pciecap)); 930246846Sneel 931246846Sneel pciecap.capid = PCIY_EXPRESS; 932246846Sneel pciecap.pcie_capabilities = PCIECAP_VERSION | PCIEM_TYPE_ROOT_PORT; 933246846Sneel pciecap.link_capabilities = 0x411; /* gen1, x1 */ 934246846Sneel pciecap.link_status = 0x11; /* gen1, x1 */ 935246846Sneel 936246846Sneel err = pci_emul_add_capability(pi, (u_char *)&pciecap, sizeof(pciecap)); 937246846Sneel return (err); 938246846Sneel} 939246846Sneel 940221828Sgrehan/* 941221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the 942221828Sgrehan * config space. 943221828Sgrehan */ 944221828Sgrehanstatic void 945221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) 946221828Sgrehan{ 947221828Sgrehan int capid; 948221828Sgrehan uint8_t capoff, nextoff; 949221828Sgrehan 950221828Sgrehan /* Do not allow un-aligned writes */ 951221828Sgrehan if ((offset & (bytes - 1)) != 0) 952221828Sgrehan return; 953221828Sgrehan 954221828Sgrehan /* Find the capability that we want to update */ 955221828Sgrehan capoff = CAP_START_OFFSET; 956221828Sgrehan while (1) { 957268887Sjhb nextoff = pci_get_cfgdata8(pi, capoff + 1); 958268887Sjhb if (nextoff == 0) 959221828Sgrehan break; 960221828Sgrehan if (offset >= capoff && offset < nextoff) 961221828Sgrehan break; 962221828Sgrehan 963221828Sgrehan capoff = nextoff; 964221828Sgrehan } 965221828Sgrehan assert(offset >= capoff); 966221828Sgrehan 967221828Sgrehan /* 968256248Sgrehan * Capability ID and Next Capability Pointer are readonly. 969256248Sgrehan * However, some o/s's do 4-byte writes that include these. 970256248Sgrehan * For this case, trim the write back to 2 bytes and adjust 971256248Sgrehan * the data. 972221828Sgrehan */ 973256248Sgrehan if (offset == capoff || offset == capoff + 1) { 974256248Sgrehan if (offset == capoff && bytes == 4) { 975256248Sgrehan bytes = 2; 976256248Sgrehan offset += 2; 977256248Sgrehan val >>= 16; 978256248Sgrehan } else 979256248Sgrehan return; 980256248Sgrehan } 981221828Sgrehan 982268887Sjhb capid = pci_get_cfgdata8(pi, capoff); 983221828Sgrehan switch (capid) { 984221828Sgrehan case PCIY_MSI: 985221828Sgrehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 986221828Sgrehan break; 987246109Sneel case PCIY_MSIX: 988246109Sneel msixcap_cfgwrite(pi, capoff, offset, bytes, val); 989246109Sneel break; 990246846Sneel case PCIY_EXPRESS: 991246846Sneel pciecap_cfgwrite(pi, capoff, offset, bytes, val); 992246846Sneel break; 993221828Sgrehan default: 994221828Sgrehan break; 995221828Sgrehan } 996221828Sgrehan} 997221828Sgrehan 998221828Sgrehanstatic int 999221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset) 1000221828Sgrehan{ 1001221828Sgrehan uint16_t sts; 1002221828Sgrehan 1003221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 1004221828Sgrehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 1005268887Sjhb if (offset >= CAP_START_OFFSET && offset <= pi->pi_capend) 1006268887Sjhb return (1); 1007221828Sgrehan } 1008268887Sjhb return (0); 1009221828Sgrehan} 1010221828Sgrehan 1011247144Sgrehanstatic int 1012247144Sgrehanpci_emul_fallback_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 1013247144Sgrehan int size, uint64_t *val, void *arg1, long arg2) 1014247144Sgrehan{ 1015247144Sgrehan /* 1016247144Sgrehan * Ignore writes; return 0xff's for reads. The mem read code 1017247144Sgrehan * will take care of truncating to the correct size. 1018247144Sgrehan */ 1019247144Sgrehan if (dir == MEM_F_READ) { 1020247144Sgrehan *val = 0xffffffffffffffff; 1021247144Sgrehan } 1022247144Sgrehan 1023247144Sgrehan return (0); 1024247144Sgrehan} 1025247144Sgrehan 1026268887Sjhb#define BUSIO_ROUNDUP 32 1027268887Sjhb#define BUSMEM_ROUNDUP (1024 * 1024) 1028268887Sjhb 1029252682Sgrehanint 1030221828Sgrehaninit_pci(struct vmctx *ctx) 1031221828Sgrehan{ 1032221828Sgrehan struct pci_devemu *pde; 1033268887Sjhb struct businfo *bi; 1034268887Sjhb struct slotinfo *si; 1035267393Sjhb struct funcinfo *fi; 1036249572Sneel size_t lowmem; 1037268887Sjhb int bus, slot, func; 1038247144Sgrehan int error; 1039221828Sgrehan 1040221828Sgrehan pci_emul_iobase = PCI_EMUL_IOBASE; 1041249572Sneel pci_emul_membase32 = vm_get_lowmem_limit(ctx); 1042221828Sgrehan pci_emul_membase64 = PCI_EMUL_MEMBASE64; 1043221828Sgrehan 1044268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1045268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1046268887Sjhb continue; 1047268887Sjhb /* 1048268887Sjhb * Keep track of the i/o and memory resources allocated to 1049268887Sjhb * this bus. 1050268887Sjhb */ 1051268887Sjhb bi->iobase = pci_emul_iobase; 1052268887Sjhb bi->membase32 = pci_emul_membase32; 1053268887Sjhb bi->membase64 = pci_emul_membase64; 1054268887Sjhb 1055268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1056268887Sjhb si = &bi->slotinfo[slot]; 1057268887Sjhb for (func = 0; func < MAXFUNCS; func++) { 1058268887Sjhb fi = &si->si_funcs[func]; 1059268887Sjhb if (fi->fi_name == NULL) 1060268887Sjhb continue; 1061267393Sjhb pde = pci_emul_finddev(fi->fi_name); 1062249916Sneel assert(pde != NULL); 1063268887Sjhb error = pci_emul_init(ctx, pde, bus, slot, 1064268887Sjhb func, fi); 1065252682Sgrehan if (error) 1066252682Sgrehan return (error); 1067221828Sgrehan } 1068221828Sgrehan } 1069268887Sjhb 1070268887Sjhb /* 1071268887Sjhb * Add some slop to the I/O and memory resources decoded by 1072268887Sjhb * this bus to give a guest some flexibility if it wants to 1073268887Sjhb * reprogram the BARs. 1074268887Sjhb */ 1075268887Sjhb pci_emul_iobase += BUSIO_ROUNDUP; 1076268887Sjhb pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP); 1077268887Sjhb bi->iolimit = pci_emul_iobase; 1078268887Sjhb 1079268887Sjhb pci_emul_membase32 += BUSMEM_ROUNDUP; 1080268887Sjhb pci_emul_membase32 = roundup2(pci_emul_membase32, 1081268887Sjhb BUSMEM_ROUNDUP); 1082268887Sjhb bi->memlimit32 = pci_emul_membase32; 1083268887Sjhb 1084268887Sjhb pci_emul_membase64 += BUSMEM_ROUNDUP; 1085268887Sjhb pci_emul_membase64 = roundup2(pci_emul_membase64, 1086268887Sjhb BUSMEM_ROUNDUP); 1087268887Sjhb bi->memlimit64 = pci_emul_membase64; 1088221828Sgrehan } 1089234938Sgrehan 1090234938Sgrehan /* 1091268972Sjhb * PCI backends are initialized before routing INTx interrupts 1092268972Sjhb * so that LPC devices are able to reserve ISA IRQs before 1093268972Sjhb * routing PIRQ pins. 1094268972Sjhb */ 1095268972Sjhb for (bus = 0; bus < MAXBUSES; bus++) { 1096268972Sjhb if ((bi = pci_businfo[bus]) == NULL) 1097268972Sjhb continue; 1098268972Sjhb 1099268972Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1100268972Sjhb si = &bi->slotinfo[slot]; 1101268972Sjhb for (func = 0; func < MAXFUNCS; func++) { 1102268972Sjhb fi = &si->si_funcs[func]; 1103268972Sjhb if (fi->fi_devi == NULL) 1104268972Sjhb continue; 1105268972Sjhb pci_lintr_route(fi->fi_devi); 1106268972Sjhb } 1107268972Sjhb } 1108268972Sjhb } 1109268972Sjhb lpc_pirq_routed(); 1110268972Sjhb 1111268972Sjhb /* 1112249572Sneel * The guest physical memory map looks like the following: 1113249572Sneel * [0, lowmem) guest system memory 1114249572Sneel * [lowmem, lowmem_limit) memory hole (may be absent) 1115249572Sneel * [lowmem_limit, 4GB) PCI hole (32-bit BAR allocation) 1116249572Sneel * [4GB, 4GB + highmem) 1117249572Sneel * 1118249572Sneel * Accesses to memory addresses that are not allocated to system 1119249572Sneel * memory or PCI devices return 0xff's. 1120247144Sgrehan */ 1121256072Sneel error = vm_get_memory_seg(ctx, 0, &lowmem, NULL); 1122249572Sneel assert(error == 0); 1123249572Sneel 1124261265Sjhb memset(&pci_mem_hole, 0, sizeof(struct mem_range)); 1125261265Sjhb pci_mem_hole.name = "PCI hole"; 1126261265Sjhb pci_mem_hole.flags = MEM_F_RW; 1127261265Sjhb pci_mem_hole.base = lowmem; 1128261265Sjhb pci_mem_hole.size = (4ULL * 1024 * 1024 * 1024) - lowmem; 1129261265Sjhb pci_mem_hole.handler = pci_emul_fallback_handler; 1130247144Sgrehan 1131261265Sjhb error = register_mem_fallback(&pci_mem_hole); 1132247144Sgrehan assert(error == 0); 1133252682Sgrehan 1134252682Sgrehan return (0); 1135221828Sgrehan} 1136221828Sgrehan 1137267393Sjhbstatic void 1138268972Sjhbpci_apic_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1139268972Sjhb void *arg) 1140267393Sjhb{ 1141267393Sjhb 1142268972Sjhb dsdt_line(" Package ()"); 1143267393Sjhb dsdt_line(" {"); 1144267393Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1145267393Sjhb dsdt_line(" 0x%02X,", pin - 1); 1146267393Sjhb dsdt_line(" Zero,"); 1147267393Sjhb dsdt_line(" 0x%X", ioapic_irq); 1148268972Sjhb dsdt_line(" },"); 1149267393Sjhb} 1150267393Sjhb 1151268972Sjhbstatic void 1152268972Sjhbpci_pirq_prt_entry(int bus, int slot, int pin, int pirq_pin, int ioapic_irq, 1153268972Sjhb void *arg) 1154268972Sjhb{ 1155268972Sjhb char *name; 1156268972Sjhb 1157268972Sjhb name = lpc_pirq_name(pirq_pin); 1158268972Sjhb if (name == NULL) 1159268972Sjhb return; 1160268972Sjhb dsdt_line(" Package ()"); 1161268972Sjhb dsdt_line(" {"); 1162268972Sjhb dsdt_line(" 0x%X,", slot << 16 | 0xffff); 1163268972Sjhb dsdt_line(" 0x%02X,", pin - 1); 1164268972Sjhb dsdt_line(" %s,", name); 1165268972Sjhb dsdt_line(" 0x00"); 1166268972Sjhb dsdt_line(" },"); 1167268972Sjhb free(name); 1168268972Sjhb} 1169268972Sjhb 1170268887Sjhb/* 1171268887Sjhb * A bhyve virtual machine has a flat PCI hierarchy with a root port 1172268887Sjhb * corresponding to each PCI bus. 1173268887Sjhb */ 1174268887Sjhbstatic void 1175268887Sjhbpci_bus_write_dsdt(int bus) 1176261265Sjhb{ 1177268887Sjhb struct businfo *bi; 1178268887Sjhb struct slotinfo *si; 1179261265Sjhb struct pci_devinst *pi; 1180268972Sjhb int count, func, slot; 1181261265Sjhb 1182268887Sjhb /* 1183268887Sjhb * If there are no devices on this 'bus' then just return. 1184268887Sjhb */ 1185268887Sjhb if ((bi = pci_businfo[bus]) == NULL) { 1186268887Sjhb /* 1187268887Sjhb * Bus 0 is special because it decodes the I/O ports used 1188268887Sjhb * for PCI config space access even if there are no devices 1189268887Sjhb * on it. 1190268887Sjhb */ 1191268887Sjhb if (bus != 0) 1192268887Sjhb return; 1193268887Sjhb } 1194268887Sjhb 1195268887Sjhb dsdt_line(" Device (PC%02X)", bus); 1196261265Sjhb dsdt_line(" {"); 1197261265Sjhb dsdt_line(" Name (_HID, EisaId (\"PNP0A03\"))"); 1198261265Sjhb dsdt_line(" Name (_ADR, Zero)"); 1199268887Sjhb 1200268887Sjhb dsdt_line(" Method (_BBN, 0, NotSerialized)"); 1201268887Sjhb dsdt_line(" {"); 1202268887Sjhb dsdt_line(" Return (0x%08X)", bus); 1203268887Sjhb dsdt_line(" }"); 1204261265Sjhb dsdt_line(" Name (_CRS, ResourceTemplate ()"); 1205261265Sjhb dsdt_line(" {"); 1206261265Sjhb dsdt_line(" WordBusNumber (ResourceProducer, MinFixed, " 1207261265Sjhb "MaxFixed, PosDecode,"); 1208261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1209268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bus); 1210268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", bus); 1211261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1212268887Sjhb dsdt_line(" 0x0001, // Length"); 1213261265Sjhb dsdt_line(" ,, )"); 1214268887Sjhb 1215268887Sjhb if (bus == 0) { 1216268887Sjhb dsdt_indent(3); 1217268887Sjhb dsdt_fixed_ioport(0xCF8, 8); 1218268887Sjhb dsdt_unindent(3); 1219268887Sjhb 1220268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1221268887Sjhb "PosDecode, EntireRange,"); 1222268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1223268887Sjhb dsdt_line(" 0x0000, // Range Minimum"); 1224268887Sjhb dsdt_line(" 0x0CF7, // Range Maximum"); 1225268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1226268887Sjhb dsdt_line(" 0x0CF8, // Length"); 1227268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1228268887Sjhb 1229268887Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1230268887Sjhb "PosDecode, EntireRange,"); 1231268887Sjhb dsdt_line(" 0x0000, // Granularity"); 1232268887Sjhb dsdt_line(" 0x0D00, // Range Minimum"); 1233268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1234268887Sjhb PCI_EMUL_IOBASE - 1); 1235268887Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1236268887Sjhb dsdt_line(" 0x%04X, // Length", 1237268887Sjhb PCI_EMUL_IOBASE - 0x0D00); 1238268887Sjhb dsdt_line(" ,, , TypeStatic)"); 1239268887Sjhb 1240268887Sjhb if (bi == NULL) { 1241268887Sjhb dsdt_line(" })"); 1242268887Sjhb goto done; 1243268887Sjhb } 1244268887Sjhb } 1245268887Sjhb assert(bi != NULL); 1246268887Sjhb 1247268887Sjhb /* i/o window */ 1248261265Sjhb dsdt_line(" WordIO (ResourceProducer, MinFixed, MaxFixed, " 1249261265Sjhb "PosDecode, EntireRange,"); 1250261265Sjhb dsdt_line(" 0x0000, // Granularity"); 1251268887Sjhb dsdt_line(" 0x%04X, // Range Minimum", bi->iobase); 1252268887Sjhb dsdt_line(" 0x%04X, // Range Maximum", 1253268887Sjhb bi->iolimit - 1); 1254261265Sjhb dsdt_line(" 0x0000, // Translation Offset"); 1255268887Sjhb dsdt_line(" 0x%04X, // Length", 1256268887Sjhb bi->iolimit - bi->iobase); 1257261265Sjhb dsdt_line(" ,, , TypeStatic)"); 1258268887Sjhb 1259268887Sjhb /* mmio window (32-bit) */ 1260261265Sjhb dsdt_line(" DWordMemory (ResourceProducer, PosDecode, " 1261261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1262261265Sjhb dsdt_line(" 0x00000000, // Granularity"); 1263268887Sjhb dsdt_line(" 0x%08X, // Range Minimum\n", bi->membase32); 1264261265Sjhb dsdt_line(" 0x%08X, // Range Maximum\n", 1265268887Sjhb bi->memlimit32 - 1); 1266261265Sjhb dsdt_line(" 0x00000000, // Translation Offset"); 1267268887Sjhb dsdt_line(" 0x%08X, // Length\n", 1268268887Sjhb bi->memlimit32 - bi->membase32); 1269261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1270268887Sjhb 1271268887Sjhb /* mmio window (64-bit) */ 1272261265Sjhb dsdt_line(" QWordMemory (ResourceProducer, PosDecode, " 1273261265Sjhb "MinFixed, MaxFixed, NonCacheable, ReadWrite,"); 1274261265Sjhb dsdt_line(" 0x0000000000000000, // Granularity"); 1275268887Sjhb dsdt_line(" 0x%016lX, // Range Minimum\n", bi->membase64); 1276261265Sjhb dsdt_line(" 0x%016lX, // Range Maximum\n", 1277268887Sjhb bi->memlimit64 - 1); 1278261265Sjhb dsdt_line(" 0x0000000000000000, // Translation Offset"); 1279261265Sjhb dsdt_line(" 0x%016lX, // Length\n", 1280268887Sjhb bi->memlimit64 - bi->membase64); 1281261265Sjhb dsdt_line(" ,, , AddressRangeMemory, TypeStatic)"); 1282261265Sjhb dsdt_line(" })"); 1283268887Sjhb 1284268887Sjhb count = pci_count_lintr(bus); 1285267393Sjhb if (count != 0) { 1286267393Sjhb dsdt_indent(2); 1287268972Sjhb dsdt_line("Name (PPRT, Package ()"); 1288267393Sjhb dsdt_line("{"); 1289268972Sjhb pci_walk_lintr(bus, pci_pirq_prt_entry, NULL); 1290268972Sjhb dsdt_line("})"); 1291268972Sjhb dsdt_line("Name (APRT, Package ()"); 1292268972Sjhb dsdt_line("{"); 1293268972Sjhb pci_walk_lintr(bus, pci_apic_prt_entry, NULL); 1294268972Sjhb dsdt_line("})"); 1295268972Sjhb dsdt_line("Method (_PRT, 0, NotSerialized)"); 1296268972Sjhb dsdt_line("{"); 1297268972Sjhb dsdt_line(" If (PICM)"); 1298268972Sjhb dsdt_line(" {"); 1299268972Sjhb dsdt_line(" Return (APRT)"); 1300268972Sjhb dsdt_line(" }"); 1301268972Sjhb dsdt_line(" Else"); 1302268972Sjhb dsdt_line(" {"); 1303268972Sjhb dsdt_line(" Return (PPRT)"); 1304268972Sjhb dsdt_line(" }"); 1305268972Sjhb dsdt_line("}"); 1306267393Sjhb dsdt_unindent(2); 1307267393Sjhb } 1308261265Sjhb 1309261265Sjhb dsdt_indent(2); 1310261265Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1311268887Sjhb si = &bi->slotinfo[slot]; 1312261265Sjhb for (func = 0; func < MAXFUNCS; func++) { 1313268887Sjhb pi = si->si_funcs[func].fi_devi; 1314261265Sjhb if (pi != NULL && pi->pi_d->pe_write_dsdt != NULL) 1315261265Sjhb pi->pi_d->pe_write_dsdt(pi); 1316261265Sjhb } 1317261265Sjhb } 1318261265Sjhb dsdt_unindent(2); 1319268887Sjhbdone: 1320261265Sjhb dsdt_line(" }"); 1321261265Sjhb} 1322261265Sjhb 1323268887Sjhbvoid 1324268887Sjhbpci_write_dsdt(void) 1325268887Sjhb{ 1326268887Sjhb int bus; 1327268887Sjhb 1328268972Sjhb dsdt_indent(1); 1329268972Sjhb dsdt_line("Name (PICM, 0x00)"); 1330268972Sjhb dsdt_line("Method (_PIC, 1, NotSerialized)"); 1331268972Sjhb dsdt_line("{"); 1332268972Sjhb dsdt_line(" Store (Arg0, PICM)"); 1333268972Sjhb dsdt_line("}"); 1334268972Sjhb dsdt_line(""); 1335268972Sjhb dsdt_line("Scope (_SB)"); 1336268972Sjhb dsdt_line("{"); 1337268887Sjhb for (bus = 0; bus < MAXBUSES; bus++) 1338268887Sjhb pci_bus_write_dsdt(bus); 1339268972Sjhb dsdt_line("}"); 1340268972Sjhb dsdt_unindent(1); 1341268887Sjhb} 1342268887Sjhb 1343221828Sgrehanint 1344268887Sjhbpci_bus_configured(int bus) 1345268887Sjhb{ 1346268887Sjhb assert(bus >= 0 && bus < MAXBUSES); 1347268887Sjhb return (pci_businfo[bus] != NULL); 1348268887Sjhb} 1349268887Sjhb 1350268887Sjhbint 1351221828Sgrehanpci_msi_enabled(struct pci_devinst *pi) 1352221828Sgrehan{ 1353221828Sgrehan return (pi->pi_msi.enabled); 1354221828Sgrehan} 1355221828Sgrehan 1356221828Sgrehanint 1357262350Sjhbpci_msi_maxmsgnum(struct pci_devinst *pi) 1358221828Sgrehan{ 1359221828Sgrehan if (pi->pi_msi.enabled) 1360262350Sjhb return (pi->pi_msi.maxmsgnum); 1361221828Sgrehan else 1362221828Sgrehan return (0); 1363221828Sgrehan} 1364221828Sgrehan 1365246109Sneelint 1366246109Sneelpci_msix_enabled(struct pci_devinst *pi) 1367246109Sneel{ 1368246109Sneel 1369246109Sneel return (pi->pi_msix.enabled && !pi->pi_msi.enabled); 1370246109Sneel} 1371246109Sneel 1372221828Sgrehanvoid 1373246109Sneelpci_generate_msix(struct pci_devinst *pi, int index) 1374246109Sneel{ 1375246109Sneel struct msix_table_entry *mte; 1376246109Sneel 1377246109Sneel if (!pci_msix_enabled(pi)) 1378246109Sneel return; 1379246109Sneel 1380246109Sneel if (pi->pi_msix.function_mask) 1381246109Sneel return; 1382246109Sneel 1383246109Sneel if (index >= pi->pi_msix.table_count) 1384246109Sneel return; 1385246109Sneel 1386246109Sneel mte = &pi->pi_msix.table[index]; 1387246109Sneel if ((mte->vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 1388246109Sneel /* XXX Set PBA bit if interrupt is disabled */ 1389262350Sjhb vm_lapic_msi(pi->pi_vmctx, mte->addr, mte->msg_data); 1390246109Sneel } 1391246109Sneel} 1392246109Sneel 1393246109Sneelvoid 1394262350Sjhbpci_generate_msi(struct pci_devinst *pi, int index) 1395221828Sgrehan{ 1396221828Sgrehan 1397262350Sjhb if (pci_msi_enabled(pi) && index < pci_msi_maxmsgnum(pi)) { 1398262350Sjhb vm_lapic_msi(pi->pi_vmctx, pi->pi_msi.addr, 1399262350Sjhb pi->pi_msi.msg_data + index); 1400221828Sgrehan } 1401221828Sgrehan} 1402221828Sgrehan 1403267393Sjhbstatic bool 1404267393Sjhbpci_lintr_permitted(struct pci_devinst *pi) 1405267393Sjhb{ 1406267393Sjhb uint16_t cmd; 1407267393Sjhb 1408267393Sjhb cmd = pci_get_cfgdata16(pi, PCIR_COMMAND); 1409267393Sjhb return (!(pi->pi_msi.enabled || pi->pi_msix.enabled || 1410267393Sjhb (cmd & PCIM_CMD_INTxDIS))); 1411267393Sjhb} 1412267393Sjhb 1413268972Sjhbvoid 1414267393Sjhbpci_lintr_request(struct pci_devinst *pi) 1415234938Sgrehan{ 1416268887Sjhb struct businfo *bi; 1417267393Sjhb struct slotinfo *si; 1418268972Sjhb int bestpin, bestcount, pin; 1419234938Sgrehan 1420268887Sjhb bi = pci_businfo[pi->pi_bus]; 1421268887Sjhb assert(bi != NULL); 1422268887Sjhb 1423267393Sjhb /* 1424268972Sjhb * Just allocate a pin from our slot. The pin will be 1425268972Sjhb * assigned IRQs later when interrupts are routed. 1426267393Sjhb */ 1427268887Sjhb si = &bi->slotinfo[pi->pi_slot]; 1428267393Sjhb bestpin = 0; 1429267393Sjhb bestcount = si->si_intpins[0].ii_count; 1430267393Sjhb for (pin = 1; pin < 4; pin++) { 1431267393Sjhb if (si->si_intpins[pin].ii_count < bestcount) { 1432267393Sjhb bestpin = pin; 1433267393Sjhb bestcount = si->si_intpins[pin].ii_count; 1434267393Sjhb } 1435267393Sjhb } 1436234938Sgrehan 1437267393Sjhb si->si_intpins[bestpin].ii_count++; 1438267393Sjhb pi->pi_lintr.pin = bestpin + 1; 1439267393Sjhb pci_set_cfgdata8(pi, PCIR_INTPIN, bestpin + 1); 1440234938Sgrehan} 1441234938Sgrehan 1442268972Sjhbstatic void 1443268972Sjhbpci_lintr_route(struct pci_devinst *pi) 1444268972Sjhb{ 1445268972Sjhb struct businfo *bi; 1446268972Sjhb struct intxinfo *ii; 1447268972Sjhb 1448268972Sjhb if (pi->pi_lintr.pin == 0) 1449268972Sjhb return; 1450268972Sjhb 1451268972Sjhb bi = pci_businfo[pi->pi_bus]; 1452268972Sjhb assert(bi != NULL); 1453268972Sjhb ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1]; 1454268972Sjhb 1455268972Sjhb /* 1456268972Sjhb * Attempt to allocate an I/O APIC pin for this intpin if one 1457268972Sjhb * is not yet assigned. 1458268972Sjhb */ 1459268972Sjhb if (ii->ii_ioapic_irq == 0) 1460268972Sjhb ii->ii_ioapic_irq = ioapic_pci_alloc_irq(); 1461268972Sjhb assert(ii->ii_ioapic_irq > 0); 1462268972Sjhb 1463268972Sjhb /* 1464268972Sjhb * Attempt to allocate a PIRQ pin for this intpin if one is 1465268972Sjhb * not yet assigned. 1466268972Sjhb */ 1467268972Sjhb if (ii->ii_pirq_pin == 0) 1468268972Sjhb ii->ii_pirq_pin = pirq_alloc_pin(pi->pi_vmctx); 1469268972Sjhb assert(ii->ii_pirq_pin > 0); 1470268972Sjhb 1471268972Sjhb pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq; 1472268972Sjhb pi->pi_lintr.pirq_pin = ii->ii_pirq_pin; 1473268972Sjhb pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin)); 1474268972Sjhb} 1475268972Sjhb 1476234938Sgrehanvoid 1477234938Sgrehanpci_lintr_assert(struct pci_devinst *pi) 1478234938Sgrehan{ 1479234938Sgrehan 1480267393Sjhb assert(pi->pi_lintr.pin > 0); 1481261088Sjhb 1482267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1483267393Sjhb if (pi->pi_lintr.state == IDLE) { 1484267393Sjhb if (pci_lintr_permitted(pi)) { 1485267393Sjhb pi->pi_lintr.state = ASSERTED; 1486268972Sjhb pci_irq_assert(pi); 1487267393Sjhb } else 1488267393Sjhb pi->pi_lintr.state = PENDING; 1489261088Sjhb } 1490267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1491234938Sgrehan} 1492234938Sgrehan 1493234938Sgrehanvoid 1494234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi) 1495234938Sgrehan{ 1496234938Sgrehan 1497267393Sjhb assert(pi->pi_lintr.pin > 0); 1498261088Sjhb 1499267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1500267393Sjhb if (pi->pi_lintr.state == ASSERTED) { 1501267393Sjhb pi->pi_lintr.state = IDLE; 1502268972Sjhb pci_irq_deassert(pi); 1503267393Sjhb } else if (pi->pi_lintr.state == PENDING) 1504267393Sjhb pi->pi_lintr.state = IDLE; 1505267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1506267393Sjhb} 1507267393Sjhb 1508267393Sjhbstatic void 1509267393Sjhbpci_lintr_update(struct pci_devinst *pi) 1510267393Sjhb{ 1511267393Sjhb 1512267393Sjhb pthread_mutex_lock(&pi->pi_lintr.lock); 1513267393Sjhb if (pi->pi_lintr.state == ASSERTED && !pci_lintr_permitted(pi)) { 1514268972Sjhb pci_irq_deassert(pi); 1515267393Sjhb pi->pi_lintr.state = PENDING; 1516267393Sjhb } else if (pi->pi_lintr.state == PENDING && pci_lintr_permitted(pi)) { 1517267393Sjhb pi->pi_lintr.state = ASSERTED; 1518268972Sjhb pci_irq_assert(pi); 1519261088Sjhb } 1520267393Sjhb pthread_mutex_unlock(&pi->pi_lintr.lock); 1521234938Sgrehan} 1522234938Sgrehan 1523267393Sjhbint 1524268887Sjhbpci_count_lintr(int bus) 1525267393Sjhb{ 1526267393Sjhb int count, slot, pin; 1527268887Sjhb struct slotinfo *slotinfo; 1528267393Sjhb 1529267393Sjhb count = 0; 1530268887Sjhb if (pci_businfo[bus] != NULL) { 1531268887Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1532268887Sjhb slotinfo = &pci_businfo[bus]->slotinfo[slot]; 1533268887Sjhb for (pin = 0; pin < 4; pin++) { 1534268887Sjhb if (slotinfo->si_intpins[pin].ii_count != 0) 1535268887Sjhb count++; 1536268887Sjhb } 1537267393Sjhb } 1538267393Sjhb } 1539267393Sjhb return (count); 1540267393Sjhb} 1541267393Sjhb 1542267393Sjhbvoid 1543268887Sjhbpci_walk_lintr(int bus, pci_lintr_cb cb, void *arg) 1544267393Sjhb{ 1545268887Sjhb struct businfo *bi; 1546268887Sjhb struct slotinfo *si; 1547267393Sjhb struct intxinfo *ii; 1548267393Sjhb int slot, pin; 1549267393Sjhb 1550268887Sjhb if ((bi = pci_businfo[bus]) == NULL) 1551268887Sjhb return; 1552268887Sjhb 1553267393Sjhb for (slot = 0; slot < MAXSLOTS; slot++) { 1554268887Sjhb si = &bi->slotinfo[slot]; 1555267393Sjhb for (pin = 0; pin < 4; pin++) { 1556268887Sjhb ii = &si->si_intpins[pin]; 1557267393Sjhb if (ii->ii_count != 0) 1558268972Sjhb cb(bus, slot, pin + 1, ii->ii_pirq_pin, 1559268972Sjhb ii->ii_ioapic_irq, arg); 1560267393Sjhb } 1561267393Sjhb } 1562267393Sjhb} 1563267393Sjhb 1564239085Sneel/* 1565239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device. 1566239085Sneel * Return 0 otherwise. 1567239085Sneel */ 1568239085Sneelstatic int 1569268887Sjhbpci_emul_is_mfdev(int bus, int slot) 1570239085Sneel{ 1571268887Sjhb struct businfo *bi; 1572268887Sjhb struct slotinfo *si; 1573239085Sneel int f, numfuncs; 1574234938Sgrehan 1575239085Sneel numfuncs = 0; 1576268887Sjhb if ((bi = pci_businfo[bus]) != NULL) { 1577268887Sjhb si = &bi->slotinfo[slot]; 1578268887Sjhb for (f = 0; f < MAXFUNCS; f++) { 1579268887Sjhb if (si->si_funcs[f].fi_devi != NULL) { 1580268887Sjhb numfuncs++; 1581268887Sjhb } 1582239085Sneel } 1583239085Sneel } 1584239085Sneel return (numfuncs > 1); 1585239085Sneel} 1586234938Sgrehan 1587239085Sneel/* 1588239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 1589239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'. 1590239085Sneel */ 1591239085Sneelstatic void 1592268887Sjhbpci_emul_hdrtype_fixup(int bus, int slot, int off, int bytes, uint32_t *rv) 1593239085Sneel{ 1594239085Sneel int mfdev; 1595239085Sneel 1596239085Sneel if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 1597268887Sjhb mfdev = pci_emul_is_mfdev(bus, slot); 1598239085Sneel switch (bytes) { 1599239085Sneel case 1: 1600239085Sneel case 2: 1601239085Sneel *rv &= ~PCIM_MFDEV; 1602239085Sneel if (mfdev) { 1603239085Sneel *rv |= PCIM_MFDEV; 1604239085Sneel } 1605239085Sneel break; 1606239085Sneel case 4: 1607239085Sneel *rv &= ~(PCIM_MFDEV << 16); 1608239085Sneel if (mfdev) { 1609239085Sneel *rv |= (PCIM_MFDEV << 16); 1610239085Sneel } 1611239085Sneel break; 1612239085Sneel } 1613239085Sneel } 1614239085Sneel} 1615239085Sneel 1616268887Sjhbstatic int cfgenable, cfgbus, cfgslot, cfgfunc, cfgoff; 1617221828Sgrehan 1618221828Sgrehanstatic int 1619221828Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1620221828Sgrehan uint32_t *eax, void *arg) 1621221828Sgrehan{ 1622221828Sgrehan uint32_t x; 1623221828Sgrehan 1624252331Sgrehan if (bytes != 4) { 1625252331Sgrehan if (in) 1626252331Sgrehan *eax = (bytes == 2) ? 0xffff : 0xff; 1627252331Sgrehan return (0); 1628252331Sgrehan } 1629221828Sgrehan 1630252331Sgrehan if (in) { 1631252331Sgrehan x = (cfgbus << 16) | 1632252331Sgrehan (cfgslot << 11) | 1633252331Sgrehan (cfgfunc << 8) | 1634252331Sgrehan cfgoff; 1635268887Sjhb if (cfgenable) 1636268887Sjhb x |= CONF1_ENABLE; 1637268887Sjhb *eax = x; 1638252331Sgrehan } else { 1639252331Sgrehan x = *eax; 1640268887Sjhb cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; 1641252331Sgrehan cfgoff = x & PCI_REGMAX; 1642252331Sgrehan cfgfunc = (x >> 8) & PCI_FUNCMAX; 1643252331Sgrehan cfgslot = (x >> 11) & PCI_SLOTMAX; 1644252331Sgrehan cfgbus = (x >> 16) & PCI_BUSMAX; 1645252331Sgrehan } 1646221828Sgrehan 1647221828Sgrehan return (0); 1648221828Sgrehan} 1649252331SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_INOUT, pci_emul_cfgaddr); 1650221828Sgrehan 1651249321Sneelstatic uint32_t 1652249321Sneelbits_changed(uint32_t old, uint32_t new, uint32_t mask) 1653249321Sneel{ 1654249321Sneel 1655249321Sneel return ((old ^ new) & mask); 1656249321Sneel} 1657249321Sneel 1658249321Sneelstatic void 1659249321Sneelpci_emul_cmdwrite(struct pci_devinst *pi, uint32_t new, int bytes) 1660249321Sneel{ 1661249321Sneel int i; 1662249321Sneel uint16_t old; 1663249321Sneel 1664249321Sneel /* 1665249321Sneel * The command register is at an offset of 4 bytes and thus the 1666249321Sneel * guest could write 1, 2 or 4 bytes starting at this offset. 1667249321Sneel */ 1668249321Sneel 1669249321Sneel old = pci_get_cfgdata16(pi, PCIR_COMMAND); /* stash old value */ 1670249321Sneel CFGWRITE(pi, PCIR_COMMAND, new, bytes); /* update config */ 1671249321Sneel new = pci_get_cfgdata16(pi, PCIR_COMMAND); /* get updated value */ 1672249321Sneel 1673249321Sneel /* 1674249321Sneel * If the MMIO or I/O address space decoding has changed then 1675249321Sneel * register/unregister all BARs that decode that address space. 1676249321Sneel */ 1677259301Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 1678249321Sneel switch (pi->pi_bar[i].type) { 1679249321Sneel case PCIBAR_NONE: 1680249321Sneel case PCIBAR_MEMHI64: 1681249321Sneel break; 1682249321Sneel case PCIBAR_IO: 1683249321Sneel /* I/O address space decoding changed? */ 1684249321Sneel if (bits_changed(old, new, PCIM_CMD_PORTEN)) { 1685249321Sneel if (porten(pi)) 1686249321Sneel register_bar(pi, i); 1687249321Sneel else 1688249321Sneel unregister_bar(pi, i); 1689249321Sneel } 1690249321Sneel break; 1691249321Sneel case PCIBAR_MEM32: 1692249321Sneel case PCIBAR_MEM64: 1693249321Sneel /* MMIO address space decoding changed? */ 1694249321Sneel if (bits_changed(old, new, PCIM_CMD_MEMEN)) { 1695249321Sneel if (memen(pi)) 1696249321Sneel register_bar(pi, i); 1697249321Sneel else 1698249321Sneel unregister_bar(pi, i); 1699249321Sneel } 1700249321Sneel break; 1701249321Sneel default: 1702249321Sneel assert(0); 1703249321Sneel } 1704249321Sneel } 1705267393Sjhb 1706267393Sjhb /* 1707267393Sjhb * If INTx has been unmasked and is pending, assert the 1708267393Sjhb * interrupt. 1709267393Sjhb */ 1710267393Sjhb pci_lintr_update(pi); 1711249321Sneel} 1712249321Sneel 1713221828Sgrehanstatic int 1714221828Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 1715221828Sgrehan uint32_t *eax, void *arg) 1716221828Sgrehan{ 1717268887Sjhb struct businfo *bi; 1718268887Sjhb struct slotinfo *si; 1719221828Sgrehan struct pci_devinst *pi; 1720221828Sgrehan struct pci_devemu *pe; 1721239085Sneel int coff, idx, needcfg; 1722249321Sneel uint64_t addr, bar, mask; 1723221828Sgrehan 1724221828Sgrehan assert(bytes == 1 || bytes == 2 || bytes == 4); 1725268887Sjhb 1726268887Sjhb if ((bi = pci_businfo[cfgbus]) != NULL) { 1727268887Sjhb si = &bi->slotinfo[cfgslot]; 1728268887Sjhb pi = si->si_funcs[cfgfunc].fi_devi; 1729268887Sjhb } else 1730242170Sneel pi = NULL; 1731242170Sneel 1732221828Sgrehan coff = cfgoff + (port - CONF1_DATA_PORT); 1733221828Sgrehan 1734221828Sgrehan#if 0 1735221828Sgrehan printf("pcicfg-%s from 0x%0x of %d bytes (%d/%d/%d)\n\r", 1736221828Sgrehan in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc); 1737221828Sgrehan#endif 1738221828Sgrehan 1739239085Sneel /* 1740268887Sjhb * Just return if there is no device at this cfgslot:cfgfunc, 1741268887Sjhb * if the guest is doing an un-aligned access, or if the config 1742268887Sjhb * address word isn't enabled. 1743239085Sneel */ 1744268887Sjhb if (!cfgenable || pi == NULL || (coff & (bytes - 1)) != 0) { 1745221828Sgrehan if (in) 1746221828Sgrehan *eax = 0xffffffff; 1747221828Sgrehan return (0); 1748221828Sgrehan } 1749221828Sgrehan 1750221828Sgrehan pe = pi->pi_d; 1751221828Sgrehan 1752221828Sgrehan /* 1753221828Sgrehan * Config read 1754221828Sgrehan */ 1755221828Sgrehan if (in) { 1756221828Sgrehan /* Let the device emulation override the default handler */ 1757239085Sneel if (pe->pe_cfgread != NULL) { 1758239085Sneel needcfg = pe->pe_cfgread(ctx, vcpu, pi, 1759239085Sneel coff, bytes, eax); 1760239085Sneel } else { 1761239085Sneel needcfg = 1; 1762239085Sneel } 1763221828Sgrehan 1764239085Sneel if (needcfg) { 1765239085Sneel if (bytes == 1) 1766239085Sneel *eax = pci_get_cfgdata8(pi, coff); 1767239085Sneel else if (bytes == 2) 1768239085Sneel *eax = pci_get_cfgdata16(pi, coff); 1769239085Sneel else 1770239085Sneel *eax = pci_get_cfgdata32(pi, coff); 1771239085Sneel } 1772239085Sneel 1773268887Sjhb pci_emul_hdrtype_fixup(cfgbus, cfgslot, coff, bytes, eax); 1774221828Sgrehan } else { 1775221828Sgrehan /* Let the device emulation override the default handler */ 1776221828Sgrehan if (pe->pe_cfgwrite != NULL && 1777221828Sgrehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 1778221828Sgrehan return (0); 1779221828Sgrehan 1780221828Sgrehan /* 1781221828Sgrehan * Special handling for write to BAR registers 1782221828Sgrehan */ 1783221828Sgrehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { 1784221828Sgrehan /* 1785221828Sgrehan * Ignore writes to BAR registers that are not 1786221828Sgrehan * 4-byte aligned. 1787221828Sgrehan */ 1788221828Sgrehan if (bytes != 4 || (coff & 0x3) != 0) 1789221828Sgrehan return (0); 1790221828Sgrehan idx = (coff - PCIR_BAR(0)) / 4; 1791249321Sneel mask = ~(pi->pi_bar[idx].size - 1); 1792221828Sgrehan switch (pi->pi_bar[idx].type) { 1793221828Sgrehan case PCIBAR_NONE: 1794249321Sneel pi->pi_bar[idx].addr = bar = 0; 1795221828Sgrehan break; 1796221828Sgrehan case PCIBAR_IO: 1797249321Sneel addr = *eax & mask; 1798249321Sneel addr &= 0xffff; 1799249321Sneel bar = addr | PCIM_BAR_IO_SPACE; 1800249321Sneel /* 1801249321Sneel * Register the new BAR value for interception 1802249321Sneel */ 1803249321Sneel if (addr != pi->pi_bar[idx].addr) { 1804249321Sneel update_bar_address(pi, addr, idx, 1805249321Sneel PCIBAR_IO); 1806249321Sneel } 1807221828Sgrehan break; 1808221828Sgrehan case PCIBAR_MEM32: 1809249321Sneel addr = bar = *eax & mask; 1810221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 1811249321Sneel if (addr != pi->pi_bar[idx].addr) { 1812249321Sneel update_bar_address(pi, addr, idx, 1813249321Sneel PCIBAR_MEM32); 1814249321Sneel } 1815221828Sgrehan break; 1816221828Sgrehan case PCIBAR_MEM64: 1817249321Sneel addr = bar = *eax & mask; 1818221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 1819221828Sgrehan PCIM_BAR_MEM_PREFETCH; 1820249321Sneel if (addr != (uint32_t)pi->pi_bar[idx].addr) { 1821249321Sneel update_bar_address(pi, addr, idx, 1822249321Sneel PCIBAR_MEM64); 1823249321Sneel } 1824221828Sgrehan break; 1825221828Sgrehan case PCIBAR_MEMHI64: 1826221828Sgrehan mask = ~(pi->pi_bar[idx - 1].size - 1); 1827249321Sneel addr = ((uint64_t)*eax << 32) & mask; 1828249321Sneel bar = addr >> 32; 1829249321Sneel if (bar != pi->pi_bar[idx - 1].addr >> 32) { 1830249321Sneel update_bar_address(pi, addr, idx - 1, 1831249321Sneel PCIBAR_MEMHI64); 1832249321Sneel } 1833221828Sgrehan break; 1834221828Sgrehan default: 1835221828Sgrehan assert(0); 1836221828Sgrehan } 1837221828Sgrehan pci_set_cfgdata32(pi, coff, bar); 1838234761Sgrehan 1839221828Sgrehan } else if (pci_emul_iscap(pi, coff)) { 1840221828Sgrehan pci_emul_capwrite(pi, coff, bytes, *eax); 1841249321Sneel } else if (coff == PCIR_COMMAND) { 1842249321Sneel pci_emul_cmdwrite(pi, *eax, bytes); 1843221828Sgrehan } else { 1844221828Sgrehan CFGWRITE(pi, coff, *eax, bytes); 1845221828Sgrehan } 1846221828Sgrehan } 1847221828Sgrehan 1848221828Sgrehan return (0); 1849221828Sgrehan} 1850221828Sgrehan 1851221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 1852221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 1853221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 1854221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 1855221828Sgrehan 1856221828Sgrehan#define PCI_EMUL_TEST 1857221828Sgrehan#ifdef PCI_EMUL_TEST 1858221828Sgrehan/* 1859221828Sgrehan * Define a dummy test device 1860221828Sgrehan */ 1861268887Sjhb#define DIOSZ 8 1862241744Sgrehan#define DMEMSZ 4096 1863221828Sgrehanstruct pci_emul_dsoftc { 1864241744Sgrehan uint8_t ioregs[DIOSZ]; 1865241744Sgrehan uint8_t memregs[DMEMSZ]; 1866221828Sgrehan}; 1867221828Sgrehan 1868241744Sgrehan#define PCI_EMUL_MSI_MSGS 4 1869241744Sgrehan#define PCI_EMUL_MSIX_MSGS 16 1870221828Sgrehan 1871221942Sjhbstatic int 1872221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 1873221828Sgrehan{ 1874221828Sgrehan int error; 1875221828Sgrehan struct pci_emul_dsoftc *sc; 1876221828Sgrehan 1877268953Sjhb sc = calloc(1, sizeof(struct pci_emul_dsoftc)); 1878221828Sgrehan 1879221828Sgrehan pi->pi_arg = sc; 1880221828Sgrehan 1881221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 1882221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 1883221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 1884221828Sgrehan 1885241744Sgrehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 1886221828Sgrehan assert(error == 0); 1887221828Sgrehan 1888241744Sgrehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 1889221828Sgrehan assert(error == 0); 1890221828Sgrehan 1891241744Sgrehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 1892241744Sgrehan assert(error == 0); 1893241744Sgrehan 1894221828Sgrehan return (0); 1895221828Sgrehan} 1896221828Sgrehan 1897221942Sjhbstatic void 1898241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 1899241744Sgrehan uint64_t offset, int size, uint64_t value) 1900221828Sgrehan{ 1901221828Sgrehan int i; 1902221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1903221828Sgrehan 1904241744Sgrehan if (baridx == 0) { 1905241744Sgrehan if (offset + size > DIOSZ) { 1906241744Sgrehan printf("diow: iow too large, offset %ld size %d\n", 1907241744Sgrehan offset, size); 1908241744Sgrehan return; 1909241744Sgrehan } 1910221828Sgrehan 1911241744Sgrehan if (size == 1) { 1912241744Sgrehan sc->ioregs[offset] = value & 0xff; 1913241744Sgrehan } else if (size == 2) { 1914241744Sgrehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 1915241744Sgrehan } else if (size == 4) { 1916241744Sgrehan *(uint32_t *)&sc->ioregs[offset] = value; 1917241744Sgrehan } else { 1918241744Sgrehan printf("diow: iow unknown size %d\n", size); 1919241744Sgrehan } 1920241744Sgrehan 1921241744Sgrehan /* 1922241744Sgrehan * Special magic value to generate an interrupt 1923241744Sgrehan */ 1924241744Sgrehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 1925262350Sjhb pci_generate_msi(pi, value % pci_msi_maxmsgnum(pi)); 1926241744Sgrehan 1927241744Sgrehan if (value == 0xabcdef) { 1928262350Sjhb for (i = 0; i < pci_msi_maxmsgnum(pi); i++) 1929241744Sgrehan pci_generate_msi(pi, i); 1930241744Sgrehan } 1931221828Sgrehan } 1932221828Sgrehan 1933241744Sgrehan if (baridx == 1) { 1934241744Sgrehan if (offset + size > DMEMSZ) { 1935241744Sgrehan printf("diow: memw too large, offset %ld size %d\n", 1936241744Sgrehan offset, size); 1937241744Sgrehan return; 1938241744Sgrehan } 1939221828Sgrehan 1940241744Sgrehan if (size == 1) { 1941241744Sgrehan sc->memregs[offset] = value; 1942241744Sgrehan } else if (size == 2) { 1943241744Sgrehan *(uint16_t *)&sc->memregs[offset] = value; 1944241744Sgrehan } else if (size == 4) { 1945241744Sgrehan *(uint32_t *)&sc->memregs[offset] = value; 1946241744Sgrehan } else if (size == 8) { 1947241744Sgrehan *(uint64_t *)&sc->memregs[offset] = value; 1948241744Sgrehan } else { 1949241744Sgrehan printf("diow: memw unknown size %d\n", size); 1950241744Sgrehan } 1951241744Sgrehan 1952241744Sgrehan /* 1953241744Sgrehan * magic interrupt ?? 1954241744Sgrehan */ 1955221828Sgrehan } 1956241744Sgrehan 1957241744Sgrehan if (baridx > 1) { 1958241744Sgrehan printf("diow: unknown bar idx %d\n", baridx); 1959241744Sgrehan } 1960221828Sgrehan} 1961221828Sgrehan 1962241744Sgrehanstatic uint64_t 1963241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 1964241744Sgrehan uint64_t offset, int size) 1965221828Sgrehan{ 1966221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1967221828Sgrehan uint32_t value; 1968221828Sgrehan 1969241744Sgrehan if (baridx == 0) { 1970241744Sgrehan if (offset + size > DIOSZ) { 1971241744Sgrehan printf("dior: ior too large, offset %ld size %d\n", 1972241744Sgrehan offset, size); 1973241744Sgrehan return (0); 1974241744Sgrehan } 1975241744Sgrehan 1976241744Sgrehan if (size == 1) { 1977241744Sgrehan value = sc->ioregs[offset]; 1978241744Sgrehan } else if (size == 2) { 1979241744Sgrehan value = *(uint16_t *) &sc->ioregs[offset]; 1980241744Sgrehan } else if (size == 4) { 1981241744Sgrehan value = *(uint32_t *) &sc->ioregs[offset]; 1982241744Sgrehan } else { 1983241744Sgrehan printf("dior: ior unknown size %d\n", size); 1984241744Sgrehan } 1985221828Sgrehan } 1986221828Sgrehan 1987241744Sgrehan if (baridx == 1) { 1988241744Sgrehan if (offset + size > DMEMSZ) { 1989241744Sgrehan printf("dior: memr too large, offset %ld size %d\n", 1990241744Sgrehan offset, size); 1991241744Sgrehan return (0); 1992241744Sgrehan } 1993241744Sgrehan 1994241744Sgrehan if (size == 1) { 1995241744Sgrehan value = sc->memregs[offset]; 1996241744Sgrehan } else if (size == 2) { 1997241744Sgrehan value = *(uint16_t *) &sc->memregs[offset]; 1998241744Sgrehan } else if (size == 4) { 1999241744Sgrehan value = *(uint32_t *) &sc->memregs[offset]; 2000241744Sgrehan } else if (size == 8) { 2001241744Sgrehan value = *(uint64_t *) &sc->memregs[offset]; 2002241744Sgrehan } else { 2003241744Sgrehan printf("dior: ior unknown size %d\n", size); 2004241744Sgrehan } 2005221828Sgrehan } 2006221828Sgrehan 2007241744Sgrehan 2008241744Sgrehan if (baridx > 1) { 2009241744Sgrehan printf("dior: unknown bar idx %d\n", baridx); 2010241744Sgrehan return (0); 2011241744Sgrehan } 2012241744Sgrehan 2013221828Sgrehan return (value); 2014221828Sgrehan} 2015221828Sgrehan 2016221828Sgrehanstruct pci_devemu pci_dummy = { 2017221828Sgrehan .pe_emu = "dummy", 2018221828Sgrehan .pe_init = pci_emul_dinit, 2019241744Sgrehan .pe_barwrite = pci_emul_diow, 2020241744Sgrehan .pe_barread = pci_emul_dior 2021221828Sgrehan}; 2022221828SgrehanPCI_EMUL_SET(pci_dummy); 2023221828Sgrehan 2024221828Sgrehan#endif /* PCI_EMUL_TEST */ 2025