pci_emul.c revision 242170
1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD$ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD$"); 31221828Sgrehan 32221828Sgrehan#include <sys/param.h> 33221828Sgrehan#include <sys/linker_set.h> 34221828Sgrehan 35221828Sgrehan#include <ctype.h> 36221828Sgrehan#include <stdio.h> 37221828Sgrehan#include <stdlib.h> 38221828Sgrehan#include <string.h> 39221828Sgrehan#include <strings.h> 40221828Sgrehan#include <assert.h> 41221828Sgrehan 42221828Sgrehan#include <machine/vmm.h> 43221828Sgrehan#include <vmmapi.h> 44221828Sgrehan 45221828Sgrehan#include "fbsdrun.h" 46221828Sgrehan#include "inout.h" 47241744Sgrehan#include "mem.h" 48242131Sgrehan#include "mptbl.h" 49221828Sgrehan#include "pci_emul.h" 50239045Sneel#include "ioapic.h" 51221828Sgrehan 52221828Sgrehan#define CONF1_ADDR_PORT 0x0cf8 53221828Sgrehan#define CONF1_DATA_PORT 0x0cfc 54221828Sgrehan 55221828Sgrehan#define CFGWRITE(pi,off,val,b) \ 56221828Sgrehando { \ 57221828Sgrehan if ((b) == 1) { \ 58221828Sgrehan pci_set_cfgdata8((pi),(off),(val)); \ 59221828Sgrehan } else if ((b) == 2) { \ 60221828Sgrehan pci_set_cfgdata16((pi),(off),(val)); \ 61221828Sgrehan } else { \ 62221828Sgrehan pci_set_cfgdata32((pi),(off),(val)); \ 63221828Sgrehan } \ 64221828Sgrehan} while (0) 65221828Sgrehan 66239085Sneel#define MAXSLOTS (PCI_SLOTMAX + 1) 67239085Sneel#define MAXFUNCS (PCI_FUNCMAX + 1) 68221828Sgrehan 69221828Sgrehanstatic struct slotinfo { 70234938Sgrehan char *si_name; 71234938Sgrehan char *si_param; 72221828Sgrehan struct pci_devinst *si_devi; 73234938Sgrehan int si_legacy; 74239085Sneel} pci_slotinfo[MAXSLOTS][MAXFUNCS]; 75221828Sgrehan 76221828Sgrehan/* 77234938Sgrehan * Used to keep track of legacy interrupt owners/requestors 78234938Sgrehan */ 79234938Sgrehan#define NLIRQ 16 80234938Sgrehan 81234938Sgrehanstatic struct lirqinfo { 82234938Sgrehan int li_generic; 83234938Sgrehan int li_acount; 84234938Sgrehan struct pci_devinst *li_owner; /* XXX should be a list */ 85234938Sgrehan} lirq[NLIRQ]; 86234938Sgrehan 87221828SgrehanSET_DECLARE(pci_devemu_set, struct pci_devemu); 88221828Sgrehan 89221828Sgrehanstatic uint64_t pci_emul_iobase; 90221828Sgrehanstatic uint64_t pci_emul_membase32; 91221828Sgrehanstatic uint64_t pci_emul_membase64; 92221828Sgrehan 93221828Sgrehan#define PCI_EMUL_IOBASE 0x2000 94221828Sgrehan#define PCI_EMUL_IOLIMIT 0x10000 95221828Sgrehan 96221828Sgrehan#define PCI_EMUL_MEMBASE32 (lomem_sz) 97221828Sgrehan#define PCI_EMUL_MEMLIMIT32 0xE0000000 /* 3.5GB */ 98221828Sgrehan 99221828Sgrehan#define PCI_EMUL_MEMBASE64 0xD000000000UL 100221828Sgrehan#define PCI_EMUL_MEMLIMIT64 0xFD00000000UL 101221828Sgrehan 102221828Sgrehanstatic int pci_emul_devices; 103221828Sgrehan 104221828Sgrehan/* 105221828Sgrehan * I/O access 106221828Sgrehan */ 107221828Sgrehan 108221828Sgrehan/* 109221828Sgrehan * Slot options are in the form: 110221828Sgrehan * 111239085Sneel * <slot>[:<func>],<emul>[,<config>] 112221828Sgrehan * 113221828Sgrehan * slot is 0..31 114239085Sneel * func is 0..7 115221828Sgrehan * emul is a string describing the type of PCI device e.g. virtio-net 116221828Sgrehan * config is an optional string, depending on the device, that can be 117221828Sgrehan * used for configuration. 118221828Sgrehan * Examples are: 119221828Sgrehan * 1,virtio-net,tap0 120239085Sneel * 3:0,dummy 121221828Sgrehan */ 122221828Sgrehanstatic void 123221828Sgrehanpci_parse_slot_usage(char *aopt) 124221828Sgrehan{ 125221828Sgrehan printf("Invalid PCI slot info field \"%s\"\n", aopt); 126221828Sgrehan free(aopt); 127221828Sgrehan} 128221828Sgrehan 129221828Sgrehanvoid 130234938Sgrehanpci_parse_slot(char *opt, int legacy) 131221828Sgrehan{ 132239085Sneel char *slot, *func, *emul, *config; 133221828Sgrehan char *str, *cpy; 134239085Sneel int snum, fnum; 135221828Sgrehan 136221828Sgrehan str = cpy = strdup(opt); 137239085Sneel 138221828Sgrehan config = NULL; 139221828Sgrehan 140239085Sneel if (strchr(str, ':') != NULL) { 141239085Sneel slot = strsep(&str, ":"); 142239085Sneel func = strsep(&str, ","); 143239085Sneel } else { 144239085Sneel slot = strsep(&str, ","); 145239085Sneel func = NULL; 146239085Sneel } 147239085Sneel 148221828Sgrehan emul = strsep(&str, ","); 149221828Sgrehan if (str != NULL) { 150221828Sgrehan config = strsep(&str, ","); 151221828Sgrehan } 152221828Sgrehan 153221828Sgrehan if (emul == NULL) { 154221828Sgrehan pci_parse_slot_usage(cpy); 155221828Sgrehan return; 156221828Sgrehan } 157221828Sgrehan 158221828Sgrehan snum = atoi(slot); 159239085Sneel fnum = func ? atoi(func) : 0; 160239085Sneel if (snum < 0 || snum >= MAXSLOTS || fnum < 0 || fnum >= MAXFUNCS) { 161221828Sgrehan pci_parse_slot_usage(cpy); 162221828Sgrehan } else { 163239085Sneel pci_slotinfo[snum][fnum].si_name = emul; 164239085Sneel pci_slotinfo[snum][fnum].si_param = config; 165239085Sneel pci_slotinfo[snum][fnum].si_legacy = legacy; 166221828Sgrehan } 167221828Sgrehan} 168221828Sgrehan 169221828Sgrehanstatic int 170241744Sgrehanpci_emul_io_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 171241744Sgrehan uint32_t *eax, void *arg) 172221828Sgrehan{ 173221828Sgrehan struct pci_devinst *pdi = arg; 174221828Sgrehan struct pci_devemu *pe = pdi->pi_d; 175241744Sgrehan uint64_t offset; 176241744Sgrehan int i; 177221828Sgrehan 178221828Sgrehan for (i = 0; i <= PCI_BARMAX; i++) { 179221828Sgrehan if (pdi->pi_bar[i].type == PCIBAR_IO && 180221828Sgrehan port >= pdi->pi_bar[i].addr && 181241744Sgrehan port + bytes <= 182241744Sgrehan pdi->pi_bar[i].addr + pdi->pi_bar[i].size) { 183221828Sgrehan offset = port - pdi->pi_bar[i].addr; 184221828Sgrehan if (in) 185241744Sgrehan *eax = (*pe->pe_barread)(ctx, vcpu, pdi, i, 186241744Sgrehan offset, bytes); 187221828Sgrehan else 188241744Sgrehan (*pe->pe_barwrite)(ctx, vcpu, pdi, i, offset, 189241744Sgrehan bytes, *eax); 190221828Sgrehan return (0); 191221828Sgrehan } 192221828Sgrehan } 193221828Sgrehan return (-1); 194221828Sgrehan} 195221828Sgrehan 196221828Sgrehanstatic int 197241744Sgrehanpci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 198241744Sgrehan int size, uint64_t *val, void *arg1, long arg2) 199241744Sgrehan{ 200241744Sgrehan struct pci_devinst *pdi = arg1; 201241744Sgrehan struct pci_devemu *pe = pdi->pi_d; 202241744Sgrehan uint64_t offset; 203241744Sgrehan int bidx = (int) arg2; 204241744Sgrehan 205241744Sgrehan assert(bidx <= PCI_BARMAX); 206241744Sgrehan assert(pdi->pi_bar[bidx].type == PCIBAR_MEM32 || 207241744Sgrehan pdi->pi_bar[bidx].type == PCIBAR_MEM64); 208241744Sgrehan assert(addr >= pdi->pi_bar[bidx].addr && 209241744Sgrehan addr + size <= pdi->pi_bar[bidx].addr + pdi->pi_bar[bidx].size); 210241744Sgrehan 211241744Sgrehan offset = addr - pdi->pi_bar[bidx].addr; 212241744Sgrehan 213241744Sgrehan if (dir == MEM_F_WRITE) 214241744Sgrehan (*pe->pe_barwrite)(ctx, vcpu, pdi, bidx, offset, size, *val); 215241744Sgrehan else 216241744Sgrehan *val = (*pe->pe_barread)(ctx, vcpu, pdi, bidx, offset, size); 217241744Sgrehan 218241744Sgrehan return (0); 219241744Sgrehan} 220241744Sgrehan 221241744Sgrehan 222241744Sgrehanstatic int 223221828Sgrehanpci_emul_alloc_resource(uint64_t *baseptr, uint64_t limit, uint64_t size, 224221828Sgrehan uint64_t *addr) 225221828Sgrehan{ 226221828Sgrehan uint64_t base; 227221828Sgrehan 228221828Sgrehan assert((size & (size - 1)) == 0); /* must be a power of 2 */ 229221828Sgrehan 230221828Sgrehan base = roundup2(*baseptr, size); 231221828Sgrehan 232221828Sgrehan if (base + size <= limit) { 233221828Sgrehan *addr = base; 234221828Sgrehan *baseptr = base + size; 235221828Sgrehan return (0); 236221828Sgrehan } else 237221828Sgrehan return (-1); 238221828Sgrehan} 239221828Sgrehan 240221828Sgrehanint 241241744Sgrehanpci_emul_alloc_bar(struct pci_devinst *pdi, int idx, enum pcibar_type type, 242241744Sgrehan uint64_t size) 243221828Sgrehan{ 244241744Sgrehan 245241744Sgrehan return (pci_emul_alloc_pbar(pdi, idx, 0, type, size)); 246241744Sgrehan} 247241744Sgrehan 248241744Sgrehanint 249241744Sgrehanpci_emul_alloc_pbar(struct pci_devinst *pdi, int idx, uint64_t hostbase, 250241744Sgrehan enum pcibar_type type, uint64_t size) 251241744Sgrehan{ 252221828Sgrehan int i, error; 253221828Sgrehan uint64_t *baseptr, limit, addr, mask, lobits, bar; 254221828Sgrehan struct inout_port iop; 255241744Sgrehan struct mem_range memp; 256221828Sgrehan 257221828Sgrehan assert(idx >= 0 && idx <= PCI_BARMAX); 258221828Sgrehan 259221828Sgrehan if ((size & (size - 1)) != 0) 260221828Sgrehan size = 1UL << flsl(size); /* round up to a power of 2 */ 261221828Sgrehan 262221828Sgrehan switch (type) { 263221828Sgrehan case PCIBAR_NONE: 264221828Sgrehan baseptr = NULL; 265221828Sgrehan addr = mask = lobits = 0; 266221828Sgrehan break; 267221828Sgrehan case PCIBAR_IO: 268239085Sneel if (hostbase && 269239085Sneel pci_slotinfo[pdi->pi_slot][pdi->pi_func].si_legacy) { 270234938Sgrehan assert(hostbase < PCI_EMUL_IOBASE); 271234938Sgrehan baseptr = &hostbase; 272234938Sgrehan } else { 273234938Sgrehan baseptr = &pci_emul_iobase; 274234938Sgrehan } 275221828Sgrehan limit = PCI_EMUL_IOLIMIT; 276221828Sgrehan mask = PCIM_BAR_IO_BASE; 277221828Sgrehan lobits = PCIM_BAR_IO_SPACE; 278221828Sgrehan break; 279221828Sgrehan case PCIBAR_MEM64: 280221828Sgrehan /* 281221828Sgrehan * XXX 282221828Sgrehan * Some drivers do not work well if the 64-bit BAR is allocated 283221828Sgrehan * above 4GB. Allow for this by allocating small requests under 284221828Sgrehan * 4GB unless then allocation size is larger than some arbitrary 285221828Sgrehan * number (32MB currently). 286221828Sgrehan */ 287221828Sgrehan if (size > 32 * 1024 * 1024) { 288221828Sgrehan /* 289221828Sgrehan * XXX special case for device requiring peer-peer DMA 290221828Sgrehan */ 291221828Sgrehan if (size == 0x100000000UL) 292221828Sgrehan baseptr = &hostbase; 293221828Sgrehan else 294221828Sgrehan baseptr = &pci_emul_membase64; 295221828Sgrehan limit = PCI_EMUL_MEMLIMIT64; 296221828Sgrehan mask = PCIM_BAR_MEM_BASE; 297221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 298221828Sgrehan PCIM_BAR_MEM_PREFETCH; 299221828Sgrehan break; 300239086Sneel } else { 301239086Sneel baseptr = &pci_emul_membase32; 302239086Sneel limit = PCI_EMUL_MEMLIMIT32; 303239086Sneel mask = PCIM_BAR_MEM_BASE; 304239086Sneel lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64; 305221828Sgrehan } 306239086Sneel break; 307221828Sgrehan case PCIBAR_MEM32: 308221828Sgrehan baseptr = &pci_emul_membase32; 309221828Sgrehan limit = PCI_EMUL_MEMLIMIT32; 310221828Sgrehan mask = PCIM_BAR_MEM_BASE; 311221828Sgrehan lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 312221828Sgrehan break; 313221828Sgrehan default: 314221828Sgrehan printf("pci_emul_alloc_base: invalid bar type %d\n", type); 315221828Sgrehan assert(0); 316221828Sgrehan } 317221828Sgrehan 318221828Sgrehan if (baseptr != NULL) { 319221828Sgrehan error = pci_emul_alloc_resource(baseptr, limit, size, &addr); 320221828Sgrehan if (error != 0) 321221828Sgrehan return (error); 322221828Sgrehan } 323221828Sgrehan 324221828Sgrehan pdi->pi_bar[idx].type = type; 325221828Sgrehan pdi->pi_bar[idx].addr = addr; 326221828Sgrehan pdi->pi_bar[idx].size = size; 327221828Sgrehan 328221828Sgrehan /* Initialize the BAR register in config space */ 329221828Sgrehan bar = (addr & mask) | lobits; 330221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar); 331221828Sgrehan 332221828Sgrehan if (type == PCIBAR_MEM64) { 333221828Sgrehan assert(idx + 1 <= PCI_BARMAX); 334221828Sgrehan pdi->pi_bar[idx + 1].type = PCIBAR_MEMHI64; 335221828Sgrehan pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32); 336221828Sgrehan } 337221828Sgrehan 338221828Sgrehan /* add a handler to intercept accesses to the I/O bar */ 339221828Sgrehan if (type == PCIBAR_IO) { 340221828Sgrehan iop.name = pdi->pi_name; 341221828Sgrehan iop.flags = IOPORT_F_INOUT; 342241744Sgrehan iop.handler = pci_emul_io_handler; 343221828Sgrehan iop.arg = pdi; 344221828Sgrehan 345221828Sgrehan for (i = 0; i < size; i++) { 346221828Sgrehan iop.port = addr + i; 347221828Sgrehan register_inout(&iop); 348221828Sgrehan } 349241744Sgrehan } else if (type == PCIBAR_MEM32 || type == PCIBAR_MEM64) { 350241744Sgrehan /* add memory bar intercept handler */ 351241744Sgrehan memp.name = pdi->pi_name; 352241744Sgrehan memp.flags = MEM_F_RW; 353241744Sgrehan memp.base = addr; 354241744Sgrehan memp.size = size; 355241744Sgrehan memp.handler = pci_emul_mem_handler; 356241744Sgrehan memp.arg1 = pdi; 357241744Sgrehan memp.arg2 = idx; 358241744Sgrehan 359241744Sgrehan error = register_mem(&memp); 360241744Sgrehan assert(error == 0); 361221828Sgrehan } 362221828Sgrehan 363221828Sgrehan return (0); 364221828Sgrehan} 365221828Sgrehan 366221828Sgrehan#define CAP_START_OFFSET 0x40 367221828Sgrehanstatic int 368221828Sgrehanpci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) 369221828Sgrehan{ 370221828Sgrehan int i, capoff, capid, reallen; 371221828Sgrehan uint16_t sts; 372221828Sgrehan 373221828Sgrehan static u_char endofcap[4] = { 374221828Sgrehan PCIY_RESERVED, 0, 0, 0 375221828Sgrehan }; 376221828Sgrehan 377221828Sgrehan assert(caplen > 0 && capdata[0] != PCIY_RESERVED); 378221828Sgrehan 379221828Sgrehan reallen = roundup2(caplen, 4); /* dword aligned */ 380221828Sgrehan 381221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 382221828Sgrehan if ((sts & PCIM_STATUS_CAPPRESENT) == 0) { 383221828Sgrehan capoff = CAP_START_OFFSET; 384221828Sgrehan pci_set_cfgdata8(pi, PCIR_CAP_PTR, capoff); 385221828Sgrehan pci_set_cfgdata16(pi, PCIR_STATUS, sts|PCIM_STATUS_CAPPRESENT); 386221828Sgrehan } else { 387221828Sgrehan capoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR); 388221828Sgrehan while (1) { 389221828Sgrehan assert((capoff & 0x3) == 0); 390221828Sgrehan capid = pci_get_cfgdata8(pi, capoff); 391221828Sgrehan if (capid == PCIY_RESERVED) 392221828Sgrehan break; 393221828Sgrehan capoff = pci_get_cfgdata8(pi, capoff + 1); 394221828Sgrehan } 395221828Sgrehan } 396221828Sgrehan 397221828Sgrehan /* Check if we have enough space */ 398221828Sgrehan if (capoff + reallen + sizeof(endofcap) > PCI_REGMAX + 1) 399221828Sgrehan return (-1); 400221828Sgrehan 401221828Sgrehan /* Copy the capability */ 402221828Sgrehan for (i = 0; i < caplen; i++) 403221828Sgrehan pci_set_cfgdata8(pi, capoff + i, capdata[i]); 404221828Sgrehan 405221828Sgrehan /* Set the next capability pointer */ 406221828Sgrehan pci_set_cfgdata8(pi, capoff + 1, capoff + reallen); 407221828Sgrehan 408221828Sgrehan /* Copy of the reserved capability which serves as the end marker */ 409221828Sgrehan for (i = 0; i < sizeof(endofcap); i++) 410221828Sgrehan pci_set_cfgdata8(pi, capoff + reallen + i, endofcap[i]); 411221828Sgrehan 412221828Sgrehan return (0); 413221828Sgrehan} 414221828Sgrehan 415221828Sgrehanstatic struct pci_devemu * 416221828Sgrehanpci_emul_finddev(char *name) 417221828Sgrehan{ 418221828Sgrehan struct pci_devemu **pdpp, *pdp; 419221828Sgrehan 420221828Sgrehan SET_FOREACH(pdpp, pci_devemu_set) { 421221828Sgrehan pdp = *pdpp; 422221828Sgrehan if (!strcmp(pdp->pe_emu, name)) { 423221828Sgrehan return (pdp); 424221828Sgrehan } 425221828Sgrehan } 426221828Sgrehan 427221828Sgrehan return (NULL); 428221828Sgrehan} 429221828Sgrehan 430221828Sgrehanstatic void 431239085Sneelpci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int slot, int func, 432239085Sneel char *params) 433221828Sgrehan{ 434221828Sgrehan struct pci_devinst *pdi; 435221828Sgrehan pdi = malloc(sizeof(struct pci_devinst)); 436221828Sgrehan bzero(pdi, sizeof(*pdi)); 437221828Sgrehan 438221828Sgrehan pdi->pi_vmctx = ctx; 439221828Sgrehan pdi->pi_bus = 0; 440221828Sgrehan pdi->pi_slot = slot; 441239085Sneel pdi->pi_func = func; 442221828Sgrehan pdi->pi_d = pde; 443221828Sgrehan snprintf(pdi->pi_name, PI_NAMESZ, "%s-pci-%d", pde->pe_emu, slot); 444221828Sgrehan 445221828Sgrehan /* Disable legacy interrupts */ 446221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTLINE, 255); 447221828Sgrehan pci_set_cfgdata8(pdi, PCIR_INTPIN, 0); 448221828Sgrehan 449221828Sgrehan pci_set_cfgdata8(pdi, PCIR_COMMAND, 450221828Sgrehan PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 451221828Sgrehan 452221828Sgrehan if ((*pde->pe_init)(ctx, pdi, params) != 0) { 453221828Sgrehan free(pdi); 454221828Sgrehan } else { 455221828Sgrehan pci_emul_devices++; 456239085Sneel pci_slotinfo[slot][func].si_devi = pdi; 457221828Sgrehan } 458221828Sgrehan} 459221828Sgrehan 460221828Sgrehanvoid 461221828Sgrehanpci_populate_msicap(struct msicap *msicap, int msgnum, int nextptr) 462221828Sgrehan{ 463221828Sgrehan int mmc; 464221828Sgrehan 465221828Sgrehan CTASSERT(sizeof(struct msicap) == 14); 466221828Sgrehan 467221828Sgrehan /* Number of msi messages must be a power of 2 between 1 and 32 */ 468221828Sgrehan assert((msgnum & (msgnum - 1)) == 0 && msgnum >= 1 && msgnum <= 32); 469221828Sgrehan mmc = ffs(msgnum) - 1; 470221828Sgrehan 471221828Sgrehan bzero(msicap, sizeof(struct msicap)); 472221828Sgrehan msicap->capid = PCIY_MSI; 473221828Sgrehan msicap->nextptr = nextptr; 474221828Sgrehan msicap->msgctrl = PCIM_MSICTRL_64BIT | (mmc << 1); 475221828Sgrehan} 476221828Sgrehan 477221828Sgrehanint 478221828Sgrehanpci_emul_add_msicap(struct pci_devinst *pi, int msgnum) 479221828Sgrehan{ 480221828Sgrehan struct msicap msicap; 481221828Sgrehan 482221828Sgrehan pci_populate_msicap(&msicap, msgnum, 0); 483221828Sgrehan 484221828Sgrehan return (pci_emul_add_capability(pi, (u_char *)&msicap, sizeof(msicap))); 485221828Sgrehan} 486221828Sgrehan 487221828Sgrehanvoid 488234761Sgrehanmsixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 489234761Sgrehan int bytes, uint32_t val) 490234761Sgrehan{ 491234761Sgrehan uint16_t msgctrl, rwmask; 492234761Sgrehan int off, table_bar; 493234761Sgrehan 494234761Sgrehan off = offset - capoff; 495234761Sgrehan table_bar = pi->pi_msix.table_bar; 496234761Sgrehan /* Message Control Register */ 497234761Sgrehan if (off == 2 && bytes == 2) { 498234761Sgrehan rwmask = PCIM_MSIXCTRL_MSIX_ENABLE | PCIM_MSIXCTRL_FUNCTION_MASK; 499234761Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 500234761Sgrehan msgctrl &= ~rwmask; 501234761Sgrehan msgctrl |= val & rwmask; 502234761Sgrehan val = msgctrl; 503234761Sgrehan 504234761Sgrehan pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE; 505234761Sgrehan } 506234761Sgrehan 507234761Sgrehan CFGWRITE(pi, offset, val, bytes); 508234761Sgrehan} 509234761Sgrehan 510234761Sgrehanvoid 511221828Sgrehanmsicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset, 512221828Sgrehan int bytes, uint32_t val) 513221828Sgrehan{ 514221828Sgrehan uint16_t msgctrl, rwmask, msgdata, mme; 515221828Sgrehan uint32_t addrlo; 516221828Sgrehan 517221828Sgrehan /* 518221828Sgrehan * If guest is writing to the message control register make sure 519221828Sgrehan * we do not overwrite read-only fields. 520221828Sgrehan */ 521221828Sgrehan if ((offset - capoff) == 2 && bytes == 2) { 522221828Sgrehan rwmask = PCIM_MSICTRL_MME_MASK | PCIM_MSICTRL_MSI_ENABLE; 523221828Sgrehan msgctrl = pci_get_cfgdata16(pi, offset); 524221828Sgrehan msgctrl &= ~rwmask; 525221828Sgrehan msgctrl |= val & rwmask; 526221828Sgrehan val = msgctrl; 527221828Sgrehan 528221828Sgrehan addrlo = pci_get_cfgdata32(pi, capoff + 4); 529221828Sgrehan if (msgctrl & PCIM_MSICTRL_64BIT) 530221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 12); 531221828Sgrehan else 532221828Sgrehan msgdata = pci_get_cfgdata16(pi, capoff + 8); 533221828Sgrehan 534221828Sgrehan /* 535221828Sgrehan * XXX check delivery mode, destination mode etc 536221828Sgrehan */ 537221828Sgrehan mme = msgctrl & PCIM_MSICTRL_MME_MASK; 538221828Sgrehan pi->pi_msi.enabled = msgctrl & PCIM_MSICTRL_MSI_ENABLE ? 1 : 0; 539221828Sgrehan if (pi->pi_msi.enabled) { 540221828Sgrehan pi->pi_msi.cpu = (addrlo >> 12) & 0xff; 541221828Sgrehan pi->pi_msi.vector = msgdata & 0xff; 542221828Sgrehan pi->pi_msi.msgnum = 1 << (mme >> 4); 543221828Sgrehan } else { 544221828Sgrehan pi->pi_msi.cpu = 0; 545221828Sgrehan pi->pi_msi.vector = 0; 546221828Sgrehan pi->pi_msi.msgnum = 0; 547221828Sgrehan } 548221828Sgrehan } 549221828Sgrehan 550221828Sgrehan CFGWRITE(pi, offset, val, bytes); 551221828Sgrehan} 552221828Sgrehan 553221828Sgrehan/* 554221828Sgrehan * This function assumes that 'coff' is in the capabilities region of the 555221828Sgrehan * config space. 556221828Sgrehan */ 557221828Sgrehanstatic void 558221828Sgrehanpci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) 559221828Sgrehan{ 560221828Sgrehan int capid; 561221828Sgrehan uint8_t capoff, nextoff; 562221828Sgrehan 563221828Sgrehan /* Do not allow un-aligned writes */ 564221828Sgrehan if ((offset & (bytes - 1)) != 0) 565221828Sgrehan return; 566221828Sgrehan 567221828Sgrehan /* Find the capability that we want to update */ 568221828Sgrehan capoff = CAP_START_OFFSET; 569221828Sgrehan while (1) { 570221828Sgrehan capid = pci_get_cfgdata8(pi, capoff); 571221828Sgrehan if (capid == PCIY_RESERVED) 572221828Sgrehan break; 573221828Sgrehan 574221828Sgrehan nextoff = pci_get_cfgdata8(pi, capoff + 1); 575221828Sgrehan if (offset >= capoff && offset < nextoff) 576221828Sgrehan break; 577221828Sgrehan 578221828Sgrehan capoff = nextoff; 579221828Sgrehan } 580221828Sgrehan assert(offset >= capoff); 581221828Sgrehan 582221828Sgrehan /* 583221828Sgrehan * Capability ID and Next Capability Pointer are readonly 584221828Sgrehan */ 585221828Sgrehan if (offset == capoff || offset == capoff + 1) 586221828Sgrehan return; 587221828Sgrehan 588221828Sgrehan switch (capid) { 589221828Sgrehan case PCIY_MSI: 590221828Sgrehan msicap_cfgwrite(pi, capoff, offset, bytes, val); 591221828Sgrehan break; 592221828Sgrehan default: 593221828Sgrehan break; 594221828Sgrehan } 595221828Sgrehan} 596221828Sgrehan 597221828Sgrehanstatic int 598221828Sgrehanpci_emul_iscap(struct pci_devinst *pi, int offset) 599221828Sgrehan{ 600221828Sgrehan int found; 601221828Sgrehan uint16_t sts; 602221828Sgrehan uint8_t capid, lastoff; 603221828Sgrehan 604221828Sgrehan found = 0; 605221828Sgrehan sts = pci_get_cfgdata16(pi, PCIR_STATUS); 606221828Sgrehan if ((sts & PCIM_STATUS_CAPPRESENT) != 0) { 607221828Sgrehan lastoff = pci_get_cfgdata8(pi, PCIR_CAP_PTR); 608221828Sgrehan while (1) { 609221828Sgrehan assert((lastoff & 0x3) == 0); 610221828Sgrehan capid = pci_get_cfgdata8(pi, lastoff); 611221828Sgrehan if (capid == PCIY_RESERVED) 612221828Sgrehan break; 613221828Sgrehan lastoff = pci_get_cfgdata8(pi, lastoff + 1); 614221828Sgrehan } 615221828Sgrehan if (offset >= CAP_START_OFFSET && offset <= lastoff) 616221828Sgrehan found = 1; 617221828Sgrehan } 618221828Sgrehan return (found); 619221828Sgrehan} 620221828Sgrehan 621221828Sgrehanvoid 622221828Sgrehaninit_pci(struct vmctx *ctx) 623221828Sgrehan{ 624221828Sgrehan struct pci_devemu *pde; 625221828Sgrehan struct slotinfo *si; 626239085Sneel int slot, func; 627221828Sgrehan 628221828Sgrehan pci_emul_iobase = PCI_EMUL_IOBASE; 629221828Sgrehan pci_emul_membase32 = PCI_EMUL_MEMBASE32; 630221828Sgrehan pci_emul_membase64 = PCI_EMUL_MEMBASE64; 631221828Sgrehan 632239085Sneel for (slot = 0; slot < MAXSLOTS; slot++) { 633239085Sneel for (func = 0; func < MAXFUNCS; func++) { 634239085Sneel si = &pci_slotinfo[slot][func]; 635239085Sneel if (si->si_name != NULL) { 636239085Sneel pde = pci_emul_finddev(si->si_name); 637239085Sneel if (pde != NULL) { 638239085Sneel pci_emul_init(ctx, pde, slot, func, 639239085Sneel si->si_param); 640239085Sneel } 641221828Sgrehan } 642221828Sgrehan } 643221828Sgrehan } 644234938Sgrehan 645234938Sgrehan /* 646234938Sgrehan * Allow ISA IRQs 5,10,11,12, and 15 to be available for 647234938Sgrehan * generic use 648234938Sgrehan */ 649234938Sgrehan lirq[5].li_generic = 1; 650234938Sgrehan lirq[10].li_generic = 1; 651234938Sgrehan lirq[11].li_generic = 1; 652234938Sgrehan lirq[12].li_generic = 1; 653234938Sgrehan lirq[15].li_generic = 1; 654221828Sgrehan} 655221828Sgrehan 656221828Sgrehanint 657221828Sgrehanpci_msi_enabled(struct pci_devinst *pi) 658221828Sgrehan{ 659221828Sgrehan return (pi->pi_msi.enabled); 660221828Sgrehan} 661221828Sgrehan 662221828Sgrehanint 663221828Sgrehanpci_msi_msgnum(struct pci_devinst *pi) 664221828Sgrehan{ 665221828Sgrehan if (pi->pi_msi.enabled) 666221828Sgrehan return (pi->pi_msi.msgnum); 667221828Sgrehan else 668221828Sgrehan return (0); 669221828Sgrehan} 670221828Sgrehan 671221828Sgrehanvoid 672221828Sgrehanpci_generate_msi(struct pci_devinst *pi, int msg) 673221828Sgrehan{ 674221828Sgrehan 675221828Sgrehan if (pci_msi_enabled(pi) && msg < pci_msi_msgnum(pi)) { 676221828Sgrehan vm_lapic_irq(pi->pi_vmctx, 677221828Sgrehan pi->pi_msi.cpu, 678221828Sgrehan pi->pi_msi.vector + msg); 679221828Sgrehan } 680221828Sgrehan} 681221828Sgrehan 682234938Sgrehanint 683234938Sgrehanpci_is_legacy(struct pci_devinst *pi) 684234938Sgrehan{ 685234938Sgrehan 686239085Sneel return (pci_slotinfo[pi->pi_slot][pi->pi_func].si_legacy); 687234938Sgrehan} 688234938Sgrehan 689234938Sgrehanstatic int 690234938Sgrehanpci_lintr_alloc(struct pci_devinst *pi, int vec) 691234938Sgrehan{ 692234938Sgrehan int i; 693234938Sgrehan 694234938Sgrehan assert(vec < NLIRQ); 695234938Sgrehan 696234938Sgrehan if (vec == -1) { 697234938Sgrehan for (i = 0; i < NLIRQ; i++) { 698234938Sgrehan if (lirq[i].li_generic && 699234938Sgrehan lirq[i].li_owner == NULL) { 700234938Sgrehan vec = i; 701234938Sgrehan break; 702234938Sgrehan } 703234938Sgrehan } 704234938Sgrehan } else { 705239029Sneel if (lirq[vec].li_owner != NULL) { 706234938Sgrehan vec = -1; 707234938Sgrehan } 708234938Sgrehan } 709234938Sgrehan assert(vec != -1); 710234938Sgrehan 711234938Sgrehan lirq[vec].li_owner = pi; 712234938Sgrehan pi->pi_lintr_pin = vec; 713234938Sgrehan 714234938Sgrehan return (vec); 715234938Sgrehan} 716234938Sgrehan 717234938Sgrehanint 718234938Sgrehanpci_lintr_request(struct pci_devinst *pi, int vec) 719234938Sgrehan{ 720234938Sgrehan 721234938Sgrehan vec = pci_lintr_alloc(pi, vec); 722234938Sgrehan pci_set_cfgdata8(pi, PCIR_INTLINE, vec); 723234938Sgrehan pci_set_cfgdata8(pi, PCIR_INTPIN, 1); 724234938Sgrehan return (0); 725234938Sgrehan} 726234938Sgrehan 727234938Sgrehanvoid 728234938Sgrehanpci_lintr_assert(struct pci_devinst *pi) 729234938Sgrehan{ 730234938Sgrehan 731234938Sgrehan assert(pi->pi_lintr_pin); 732239045Sneel ioapic_assert_pin(pi->pi_vmctx, pi->pi_lintr_pin); 733234938Sgrehan} 734234938Sgrehan 735234938Sgrehanvoid 736234938Sgrehanpci_lintr_deassert(struct pci_devinst *pi) 737234938Sgrehan{ 738234938Sgrehan 739234938Sgrehan assert(pi->pi_lintr_pin); 740239045Sneel ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin); 741234938Sgrehan} 742234938Sgrehan 743239085Sneel/* 744239085Sneel * Return 1 if the emulated device in 'slot' is a multi-function device. 745239085Sneel * Return 0 otherwise. 746239085Sneel */ 747239085Sneelstatic int 748239085Sneelpci_emul_is_mfdev(int slot) 749239085Sneel{ 750239085Sneel int f, numfuncs; 751234938Sgrehan 752239085Sneel numfuncs = 0; 753239085Sneel for (f = 0; f < MAXFUNCS; f++) { 754239085Sneel if (pci_slotinfo[slot][f].si_devi != NULL) { 755239085Sneel numfuncs++; 756239085Sneel } 757239085Sneel } 758239085Sneel return (numfuncs > 1); 759239085Sneel} 760234938Sgrehan 761239085Sneel/* 762239085Sneel * Ensure that the PCIM_MFDEV bit is properly set (or unset) depending on 763239085Sneel * whether or not is a multi-function being emulated in the pci 'slot'. 764239085Sneel */ 765239085Sneelstatic void 766239085Sneelpci_emul_hdrtype_fixup(int slot, int off, int bytes, uint32_t *rv) 767239085Sneel{ 768239085Sneel int mfdev; 769239085Sneel 770239085Sneel if (off <= PCIR_HDRTYPE && off + bytes > PCIR_HDRTYPE) { 771239085Sneel mfdev = pci_emul_is_mfdev(slot); 772239085Sneel switch (bytes) { 773239085Sneel case 1: 774239085Sneel case 2: 775239085Sneel *rv &= ~PCIM_MFDEV; 776239085Sneel if (mfdev) { 777239085Sneel *rv |= PCIM_MFDEV; 778239085Sneel } 779239085Sneel break; 780239085Sneel case 4: 781239085Sneel *rv &= ~(PCIM_MFDEV << 16); 782239085Sneel if (mfdev) { 783239085Sneel *rv |= (PCIM_MFDEV << 16); 784239085Sneel } 785239085Sneel break; 786239085Sneel } 787239085Sneel } 788239085Sneel} 789239085Sneel 790221828Sgrehanstatic int cfgbus, cfgslot, cfgfunc, cfgoff; 791221828Sgrehan 792221828Sgrehanstatic int 793221828Sgrehanpci_emul_cfgaddr(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 794221828Sgrehan uint32_t *eax, void *arg) 795221828Sgrehan{ 796221828Sgrehan uint32_t x; 797221828Sgrehan 798221828Sgrehan assert(!in); 799221828Sgrehan 800221828Sgrehan if (bytes != 4) 801221828Sgrehan return (-1); 802221828Sgrehan 803221828Sgrehan x = *eax; 804221828Sgrehan cfgoff = x & PCI_REGMAX; 805221828Sgrehan cfgfunc = (x >> 8) & PCI_FUNCMAX; 806221828Sgrehan cfgslot = (x >> 11) & PCI_SLOTMAX; 807221828Sgrehan cfgbus = (x >> 16) & PCI_BUSMAX; 808221828Sgrehan 809221828Sgrehan return (0); 810221828Sgrehan} 811221828SgrehanINOUT_PORT(pci_cfgaddr, CONF1_ADDR_PORT, IOPORT_F_OUT, pci_emul_cfgaddr); 812221828Sgrehan 813221828Sgrehanstatic int 814221828Sgrehanpci_emul_cfgdata(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 815221828Sgrehan uint32_t *eax, void *arg) 816221828Sgrehan{ 817221828Sgrehan struct pci_devinst *pi; 818221828Sgrehan struct pci_devemu *pe; 819239085Sneel int coff, idx, needcfg; 820221828Sgrehan uint64_t mask, bar; 821221828Sgrehan 822221828Sgrehan assert(bytes == 1 || bytes == 2 || bytes == 4); 823221828Sgrehan 824242170Sneel if (cfgbus == 0) 825242170Sneel pi = pci_slotinfo[cfgslot][cfgfunc].si_devi; 826242170Sneel else 827242170Sneel pi = NULL; 828242170Sneel 829221828Sgrehan coff = cfgoff + (port - CONF1_DATA_PORT); 830221828Sgrehan 831221828Sgrehan#if 0 832221828Sgrehan printf("pcicfg-%s from 0x%0x of %d bytes (%d/%d/%d)\n\r", 833221828Sgrehan in ? "read" : "write", coff, bytes, cfgbus, cfgslot, cfgfunc); 834221828Sgrehan#endif 835221828Sgrehan 836239085Sneel /* 837239085Sneel * Just return if there is no device at this cfgslot:cfgfunc or 838239085Sneel * if the guest is doing an un-aligned access 839239085Sneel */ 840239085Sneel if (pi == NULL || (coff & (bytes - 1)) != 0) { 841221828Sgrehan if (in) 842221828Sgrehan *eax = 0xffffffff; 843221828Sgrehan return (0); 844221828Sgrehan } 845221828Sgrehan 846221828Sgrehan pe = pi->pi_d; 847221828Sgrehan 848221828Sgrehan /* 849221828Sgrehan * Config read 850221828Sgrehan */ 851221828Sgrehan if (in) { 852221828Sgrehan /* Let the device emulation override the default handler */ 853239085Sneel if (pe->pe_cfgread != NULL) { 854239085Sneel needcfg = pe->pe_cfgread(ctx, vcpu, pi, 855239085Sneel coff, bytes, eax); 856239085Sneel } else { 857239085Sneel needcfg = 1; 858239085Sneel } 859221828Sgrehan 860239085Sneel if (needcfg) { 861239085Sneel if (bytes == 1) 862239085Sneel *eax = pci_get_cfgdata8(pi, coff); 863239085Sneel else if (bytes == 2) 864239085Sneel *eax = pci_get_cfgdata16(pi, coff); 865239085Sneel else 866239085Sneel *eax = pci_get_cfgdata32(pi, coff); 867239085Sneel } 868239085Sneel 869239085Sneel pci_emul_hdrtype_fixup(cfgslot, coff, bytes, eax); 870221828Sgrehan } else { 871221828Sgrehan /* Let the device emulation override the default handler */ 872221828Sgrehan if (pe->pe_cfgwrite != NULL && 873221828Sgrehan (*pe->pe_cfgwrite)(ctx, vcpu, pi, coff, bytes, *eax) == 0) 874221828Sgrehan return (0); 875221828Sgrehan 876221828Sgrehan /* 877221828Sgrehan * Special handling for write to BAR registers 878221828Sgrehan */ 879221828Sgrehan if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) { 880221828Sgrehan /* 881221828Sgrehan * Ignore writes to BAR registers that are not 882221828Sgrehan * 4-byte aligned. 883221828Sgrehan */ 884221828Sgrehan if (bytes != 4 || (coff & 0x3) != 0) 885221828Sgrehan return (0); 886221828Sgrehan idx = (coff - PCIR_BAR(0)) / 4; 887221828Sgrehan switch (pi->pi_bar[idx].type) { 888221828Sgrehan case PCIBAR_NONE: 889221828Sgrehan bar = 0; 890221828Sgrehan break; 891221828Sgrehan case PCIBAR_IO: 892221828Sgrehan mask = ~(pi->pi_bar[idx].size - 1); 893221828Sgrehan mask &= PCIM_BAR_IO_BASE; 894221828Sgrehan bar = (*eax & mask) | PCIM_BAR_IO_SPACE; 895221828Sgrehan break; 896221828Sgrehan case PCIBAR_MEM32: 897221828Sgrehan mask = ~(pi->pi_bar[idx].size - 1); 898221828Sgrehan mask &= PCIM_BAR_MEM_BASE; 899221828Sgrehan bar = *eax & mask; 900221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32; 901221828Sgrehan break; 902221828Sgrehan case PCIBAR_MEM64: 903221828Sgrehan mask = ~(pi->pi_bar[idx].size - 1); 904221828Sgrehan mask &= PCIM_BAR_MEM_BASE; 905221828Sgrehan bar = *eax & mask; 906221828Sgrehan bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 | 907221828Sgrehan PCIM_BAR_MEM_PREFETCH; 908221828Sgrehan break; 909221828Sgrehan case PCIBAR_MEMHI64: 910221828Sgrehan mask = ~(pi->pi_bar[idx - 1].size - 1); 911221828Sgrehan mask &= PCIM_BAR_MEM_BASE; 912221828Sgrehan bar = ((uint64_t)*eax << 32) & mask; 913221828Sgrehan bar = bar >> 32; 914221828Sgrehan break; 915221828Sgrehan default: 916221828Sgrehan assert(0); 917221828Sgrehan } 918221828Sgrehan pci_set_cfgdata32(pi, coff, bar); 919234761Sgrehan 920221828Sgrehan } else if (pci_emul_iscap(pi, coff)) { 921221828Sgrehan pci_emul_capwrite(pi, coff, bytes, *eax); 922221828Sgrehan } else { 923221828Sgrehan CFGWRITE(pi, coff, *eax, bytes); 924221828Sgrehan } 925221828Sgrehan } 926221828Sgrehan 927221828Sgrehan return (0); 928221828Sgrehan} 929221828Sgrehan 930221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+0, IOPORT_F_INOUT, pci_emul_cfgdata); 931221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+1, IOPORT_F_INOUT, pci_emul_cfgdata); 932221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+2, IOPORT_F_INOUT, pci_emul_cfgdata); 933221828SgrehanINOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata); 934221828Sgrehan 935221828Sgrehan/* 936221828Sgrehan * I/O ports to configure PCI IRQ routing. We ignore all writes to it. 937221828Sgrehan */ 938221828Sgrehanstatic int 939221828Sgrehanpci_irq_port_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, 940221828Sgrehan uint32_t *eax, void *arg) 941221828Sgrehan{ 942221828Sgrehan assert(in == 0); 943221828Sgrehan return (0); 944221828Sgrehan} 945221828SgrehanINOUT_PORT(pci_irq, 0xC00, IOPORT_F_OUT, pci_irq_port_handler); 946221828SgrehanINOUT_PORT(pci_irq, 0xC01, IOPORT_F_OUT, pci_irq_port_handler); 947221828Sgrehan 948221828Sgrehan#define PCI_EMUL_TEST 949221828Sgrehan#ifdef PCI_EMUL_TEST 950221828Sgrehan/* 951221828Sgrehan * Define a dummy test device 952221828Sgrehan */ 953241744Sgrehan#define DIOSZ 20 954241744Sgrehan#define DMEMSZ 4096 955221828Sgrehanstruct pci_emul_dsoftc { 956241744Sgrehan uint8_t ioregs[DIOSZ]; 957241744Sgrehan uint8_t memregs[DMEMSZ]; 958221828Sgrehan}; 959221828Sgrehan 960241744Sgrehan#define PCI_EMUL_MSI_MSGS 4 961241744Sgrehan#define PCI_EMUL_MSIX_MSGS 16 962221828Sgrehan 963221942Sjhbstatic int 964221828Sgrehanpci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 965221828Sgrehan{ 966221828Sgrehan int error; 967221828Sgrehan struct pci_emul_dsoftc *sc; 968221828Sgrehan 969221828Sgrehan sc = malloc(sizeof(struct pci_emul_dsoftc)); 970221828Sgrehan memset(sc, 0, sizeof(struct pci_emul_dsoftc)); 971221828Sgrehan 972221828Sgrehan pi->pi_arg = sc; 973221828Sgrehan 974221828Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, 0x0001); 975221828Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, 0x10DD); 976221828Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, 0x02); 977221828Sgrehan 978241744Sgrehan error = pci_emul_add_msicap(pi, PCI_EMUL_MSI_MSGS); 979221828Sgrehan assert(error == 0); 980221828Sgrehan 981241744Sgrehan error = pci_emul_alloc_bar(pi, 0, PCIBAR_IO, DIOSZ); 982221828Sgrehan assert(error == 0); 983221828Sgrehan 984241744Sgrehan error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ); 985241744Sgrehan assert(error == 0); 986241744Sgrehan 987221828Sgrehan return (0); 988221828Sgrehan} 989221828Sgrehan 990221942Sjhbstatic void 991241744Sgrehanpci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 992241744Sgrehan uint64_t offset, int size, uint64_t value) 993221828Sgrehan{ 994221828Sgrehan int i; 995221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 996221828Sgrehan 997241744Sgrehan if (baridx == 0) { 998241744Sgrehan if (offset + size > DIOSZ) { 999241744Sgrehan printf("diow: iow too large, offset %ld size %d\n", 1000241744Sgrehan offset, size); 1001241744Sgrehan return; 1002241744Sgrehan } 1003221828Sgrehan 1004241744Sgrehan if (size == 1) { 1005241744Sgrehan sc->ioregs[offset] = value & 0xff; 1006241744Sgrehan } else if (size == 2) { 1007241744Sgrehan *(uint16_t *)&sc->ioregs[offset] = value & 0xffff; 1008241744Sgrehan } else if (size == 4) { 1009241744Sgrehan *(uint32_t *)&sc->ioregs[offset] = value; 1010241744Sgrehan } else { 1011241744Sgrehan printf("diow: iow unknown size %d\n", size); 1012241744Sgrehan } 1013241744Sgrehan 1014241744Sgrehan /* 1015241744Sgrehan * Special magic value to generate an interrupt 1016241744Sgrehan */ 1017241744Sgrehan if (offset == 4 && size == 4 && pci_msi_enabled(pi)) 1018241744Sgrehan pci_generate_msi(pi, value % pci_msi_msgnum(pi)); 1019241744Sgrehan 1020241744Sgrehan if (value == 0xabcdef) { 1021241744Sgrehan for (i = 0; i < pci_msi_msgnum(pi); i++) 1022241744Sgrehan pci_generate_msi(pi, i); 1023241744Sgrehan } 1024221828Sgrehan } 1025221828Sgrehan 1026241744Sgrehan if (baridx == 1) { 1027241744Sgrehan if (offset + size > DMEMSZ) { 1028241744Sgrehan printf("diow: memw too large, offset %ld size %d\n", 1029241744Sgrehan offset, size); 1030241744Sgrehan return; 1031241744Sgrehan } 1032221828Sgrehan 1033241744Sgrehan if (size == 1) { 1034241744Sgrehan sc->memregs[offset] = value; 1035241744Sgrehan } else if (size == 2) { 1036241744Sgrehan *(uint16_t *)&sc->memregs[offset] = value; 1037241744Sgrehan } else if (size == 4) { 1038241744Sgrehan *(uint32_t *)&sc->memregs[offset] = value; 1039241744Sgrehan } else if (size == 8) { 1040241744Sgrehan *(uint64_t *)&sc->memregs[offset] = value; 1041241744Sgrehan } else { 1042241744Sgrehan printf("diow: memw unknown size %d\n", size); 1043241744Sgrehan } 1044241744Sgrehan 1045241744Sgrehan /* 1046241744Sgrehan * magic interrupt ?? 1047241744Sgrehan */ 1048221828Sgrehan } 1049241744Sgrehan 1050241744Sgrehan if (baridx > 1) { 1051241744Sgrehan printf("diow: unknown bar idx %d\n", baridx); 1052241744Sgrehan } 1053221828Sgrehan} 1054221828Sgrehan 1055241744Sgrehanstatic uint64_t 1056241744Sgrehanpci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, 1057241744Sgrehan uint64_t offset, int size) 1058221828Sgrehan{ 1059221828Sgrehan struct pci_emul_dsoftc *sc = pi->pi_arg; 1060221828Sgrehan uint32_t value; 1061221828Sgrehan 1062241744Sgrehan if (baridx == 0) { 1063241744Sgrehan if (offset + size > DIOSZ) { 1064241744Sgrehan printf("dior: ior too large, offset %ld size %d\n", 1065241744Sgrehan offset, size); 1066241744Sgrehan return (0); 1067241744Sgrehan } 1068241744Sgrehan 1069241744Sgrehan if (size == 1) { 1070241744Sgrehan value = sc->ioregs[offset]; 1071241744Sgrehan } else if (size == 2) { 1072241744Sgrehan value = *(uint16_t *) &sc->ioregs[offset]; 1073241744Sgrehan } else if (size == 4) { 1074241744Sgrehan value = *(uint32_t *) &sc->ioregs[offset]; 1075241744Sgrehan } else { 1076241744Sgrehan printf("dior: ior unknown size %d\n", size); 1077241744Sgrehan } 1078221828Sgrehan } 1079221828Sgrehan 1080241744Sgrehan if (baridx == 1) { 1081241744Sgrehan if (offset + size > DMEMSZ) { 1082241744Sgrehan printf("dior: memr too large, offset %ld size %d\n", 1083241744Sgrehan offset, size); 1084241744Sgrehan return (0); 1085241744Sgrehan } 1086241744Sgrehan 1087241744Sgrehan if (size == 1) { 1088241744Sgrehan value = sc->memregs[offset]; 1089241744Sgrehan } else if (size == 2) { 1090241744Sgrehan value = *(uint16_t *) &sc->memregs[offset]; 1091241744Sgrehan } else if (size == 4) { 1092241744Sgrehan value = *(uint32_t *) &sc->memregs[offset]; 1093241744Sgrehan } else if (size == 8) { 1094241744Sgrehan value = *(uint64_t *) &sc->memregs[offset]; 1095241744Sgrehan } else { 1096241744Sgrehan printf("dior: ior unknown size %d\n", size); 1097241744Sgrehan } 1098221828Sgrehan } 1099221828Sgrehan 1100241744Sgrehan 1101241744Sgrehan if (baridx > 1) { 1102241744Sgrehan printf("dior: unknown bar idx %d\n", baridx); 1103241744Sgrehan return (0); 1104241744Sgrehan } 1105241744Sgrehan 1106221828Sgrehan return (value); 1107221828Sgrehan} 1108221828Sgrehan 1109221828Sgrehanstruct pci_devemu pci_dummy = { 1110221828Sgrehan .pe_emu = "dummy", 1111221828Sgrehan .pe_init = pci_emul_dinit, 1112241744Sgrehan .pe_barwrite = pci_emul_diow, 1113241744Sgrehan .pe_barread = pci_emul_dior 1114221828Sgrehan}; 1115221828SgrehanPCI_EMUL_SET(pci_dummy); 1116221828Sgrehan 1117221828Sgrehan#endif /* PCI_EMUL_TEST */ 1118