1*a4052f0fSdv /* $OpenBSD: pci.c,v 1.35 2024/10/02 17:05:56 dv Exp $ */ 215caf263Sreyk 3f3c0184aSmlarkin /* 4f3c0184aSmlarkin * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> 5f3c0184aSmlarkin * 6f3c0184aSmlarkin * Permission to use, copy, modify, and distribute this software for any 7f3c0184aSmlarkin * purpose with or without fee is hereby granted, provided that the above 8f3c0184aSmlarkin * copyright notice and this permission notice appear in all copies. 9f3c0184aSmlarkin * 10f3c0184aSmlarkin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11f3c0184aSmlarkin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12f3c0184aSmlarkin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13f3c0184aSmlarkin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14f3c0184aSmlarkin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15f3c0184aSmlarkin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16f3c0184aSmlarkin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17f3c0184aSmlarkin */ 18f3c0184aSmlarkin 19f3c0184aSmlarkin #include <sys/types.h> 2087302766Sderaadt 21f3c0184aSmlarkin #include <dev/pci/pcireg.h> 22f3c0184aSmlarkin #include <dev/pci/pcidevs.h> 23ba66f564Sdv #include <dev/vmm/vmm.h> 2487302766Sderaadt 2587302766Sderaadt #include <string.h> 26813e3047Spd #include <unistd.h> 276eb4c859Sdv 28f3c0184aSmlarkin #include "vmd.h" 29f3c0184aSmlarkin #include "pci.h" 30813e3047Spd #include "atomicio.h" 31f3c0184aSmlarkin 32f3c0184aSmlarkin struct pci pci; 33f3c0184aSmlarkin 34c4fd4c5bSdv extern struct vmd_vm current_vm; 35f3c0184aSmlarkin extern char *__progname; 36f3c0184aSmlarkin 37f3c0184aSmlarkin /* PIC IRQs, assigned to devices in order */ 38a2ad4f71Smlarkin const uint8_t pci_pic_irqs[PCI_MAX_PIC_IRQS] = {3, 5, 6, 7, 9, 10, 11, 12, 39a2ad4f71Smlarkin 14, 15}; 40f3c0184aSmlarkin 41f3c0184aSmlarkin /* 42f3c0184aSmlarkin * pci_add_bar 43f3c0184aSmlarkin * 44f3c0184aSmlarkin * Adds a BAR for the PCI device 'id'. On access, 'barfn' will be 45f3c0184aSmlarkin * called, and passed 'cookie' as an identifier. 46f3c0184aSmlarkin * 47f3c0184aSmlarkin * BARs are fixed size, meaning all I/O BARs requested have the 48f3c0184aSmlarkin * same size and all MMIO BARs have the same size. 49f3c0184aSmlarkin * 50f3c0184aSmlarkin * Parameters: 51f3c0184aSmlarkin * id: PCI device to add the BAR to (local count, eg if id == 4, 52f3c0184aSmlarkin * this BAR is to be added to the VM's 5th PCI device) 53f3c0184aSmlarkin * type: type of the BAR to add (PCI_MAPREG_TYPE_xxx) 54f3c0184aSmlarkin * barfn: callback function invoked on BAR access 55f3c0184aSmlarkin * cookie: cookie passed to barfn on access 56f3c0184aSmlarkin * 57f3c0184aSmlarkin * Returns 0 if the BAR was added successfully, 1 otherwise. 58f3c0184aSmlarkin */ 59f3c0184aSmlarkin int 60f3c0184aSmlarkin pci_add_bar(uint8_t id, uint32_t type, void *barfn, void *cookie) 61f3c0184aSmlarkin { 62f3c0184aSmlarkin uint8_t bar_reg_idx, bar_ct; 63f3c0184aSmlarkin 64f3c0184aSmlarkin /* Check id */ 65f3c0184aSmlarkin if (id >= pci.pci_dev_ct) 66f3c0184aSmlarkin return (1); 67f3c0184aSmlarkin 68f3c0184aSmlarkin /* Can only add PCI_MAX_BARS BARs to any device */ 69f3c0184aSmlarkin bar_ct = pci.pci_devices[id].pd_bar_ct; 70f3c0184aSmlarkin if (bar_ct >= PCI_MAX_BARS) 71f3c0184aSmlarkin return (1); 72f3c0184aSmlarkin 73f3c0184aSmlarkin /* Compute BAR address and add */ 74f3c0184aSmlarkin bar_reg_idx = (PCI_MAPREG_START + (bar_ct * 4)) / 4; 75f3c0184aSmlarkin if (type == PCI_MAPREG_TYPE_MEM) { 76*a4052f0fSdv if (pci.pci_next_mmio_bar >= PCI_MMIO_BAR_END) 77f3c0184aSmlarkin return (1); 78f3c0184aSmlarkin 79f3c0184aSmlarkin pci.pci_devices[id].pd_cfg_space[bar_reg_idx] = 80f3c0184aSmlarkin PCI_MAPREG_MEM_ADDR(pci.pci_next_mmio_bar); 81d489aa7eSdv pci.pci_next_mmio_bar += VM_PCI_MMIO_BAR_SIZE; 82f3c0184aSmlarkin pci.pci_devices[id].pd_barfunc[bar_ct] = barfn; 83f3c0184aSmlarkin pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie; 84f3c0184aSmlarkin pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_MMIO; 85d489aa7eSdv pci.pci_devices[id].pd_barsize[bar_ct] = VM_PCI_MMIO_BAR_SIZE; 86f3c0184aSmlarkin pci.pci_devices[id].pd_bar_ct++; 87c4fd4c5bSdv } 88c4fd4c5bSdv #ifdef __amd64__ 89c4fd4c5bSdv else if (type == PCI_MAPREG_TYPE_IO) { 90d489aa7eSdv if (pci.pci_next_io_bar >= VM_PCI_IO_BAR_END) 91f3c0184aSmlarkin return (1); 92f3c0184aSmlarkin 93f3c0184aSmlarkin pci.pci_devices[id].pd_cfg_space[bar_reg_idx] = 94f3c0184aSmlarkin PCI_MAPREG_IO_ADDR(pci.pci_next_io_bar) | 95f3c0184aSmlarkin PCI_MAPREG_TYPE_IO; 96d489aa7eSdv pci.pci_next_io_bar += VM_PCI_IO_BAR_SIZE; 97f3c0184aSmlarkin pci.pci_devices[id].pd_barfunc[bar_ct] = barfn; 98f3c0184aSmlarkin pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie; 991f5e00e0Sreyk DPRINTF("%s: adding pci bar cookie for dev %d bar %d = %p", 100f3c0184aSmlarkin __progname, id, bar_ct, cookie); 101f3c0184aSmlarkin pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_IO; 102d489aa7eSdv pci.pci_devices[id].pd_barsize[bar_ct] = VM_PCI_IO_BAR_SIZE; 103f3c0184aSmlarkin pci.pci_devices[id].pd_bar_ct++; 104f3c0184aSmlarkin } 105c4fd4c5bSdv #endif /* __amd64__ */ 106f3c0184aSmlarkin 107f3c0184aSmlarkin return (0); 108f3c0184aSmlarkin } 109f3c0184aSmlarkin 110813e3047Spd int 111813e3047Spd pci_set_bar_fn(uint8_t id, uint8_t bar_ct, void *barfn, void *cookie) 112813e3047Spd { 113813e3047Spd /* Check id */ 114813e3047Spd if (id >= pci.pci_dev_ct) 115813e3047Spd return (1); 116813e3047Spd 117813e3047Spd if (bar_ct >= PCI_MAX_BARS) 118813e3047Spd return (1); 119813e3047Spd 120813e3047Spd pci.pci_devices[id].pd_barfunc[bar_ct] = barfn; 121813e3047Spd pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie; 122813e3047Spd 123813e3047Spd return (0); 124813e3047Spd } 125813e3047Spd 126f3c0184aSmlarkin /* 1270eab2803Smlarkin * pci_get_dev_irq 1280eab2803Smlarkin * 1290eab2803Smlarkin * Returns the IRQ for the specified PCI device 1300eab2803Smlarkin * 1310eab2803Smlarkin * Parameters: 1320eab2803Smlarkin * id: PCI device id to return IRQ for 1330eab2803Smlarkin * 1340eab2803Smlarkin * Return values: 1350eab2803Smlarkin * The IRQ for the device, or 0xff if no device IRQ assigned 1360eab2803Smlarkin */ 1370eab2803Smlarkin uint8_t 1380eab2803Smlarkin pci_get_dev_irq(uint8_t id) 1390eab2803Smlarkin { 1400eab2803Smlarkin if (pci.pci_devices[id].pd_int) 1410eab2803Smlarkin return pci.pci_devices[id].pd_irq; 1420eab2803Smlarkin else 1430eab2803Smlarkin return 0xFF; 1440eab2803Smlarkin } 1450eab2803Smlarkin 1460eab2803Smlarkin /* 147f3c0184aSmlarkin * pci_add_device 148f3c0184aSmlarkin * 149f3c0184aSmlarkin * Adds a PCI device to the guest VM defined by the supplied parameters. 150f3c0184aSmlarkin * 151f3c0184aSmlarkin * Parameters: 152f3c0184aSmlarkin * id: the new PCI device ID (0 .. PCI_CONFIG_MAX_DEV) 153f3c0184aSmlarkin * vid: PCI VID of the new device 154f3c0184aSmlarkin * pid: PCI PID of the new device 155f3c0184aSmlarkin * class: PCI 'class' of the new device 156f3c0184aSmlarkin * subclass: PCI 'subclass' of the new device 157f3c0184aSmlarkin * subsys_vid: subsystem VID of the new device 158f3c0184aSmlarkin * subsys_id: subsystem ID of the new device 159f3c0184aSmlarkin * irq_needed: 1 if an IRQ should be assigned to this PCI device, 0 otherwise 160f3c0184aSmlarkin * csfunc: PCI config space callback function when the guest VM accesses 161f3c0184aSmlarkin * CS of this PCI device 162f3c0184aSmlarkin * 163f3c0184aSmlarkin * Return values: 164f3c0184aSmlarkin * 0: the PCI device was added successfully. The PCI device ID is in 'id'. 165f3c0184aSmlarkin * 1: the PCI device addition failed. 166f3c0184aSmlarkin */ 167f3c0184aSmlarkin int 168f3c0184aSmlarkin pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid, uint8_t class, 169f3c0184aSmlarkin uint8_t subclass, uint16_t subsys_vid, uint16_t subsys_id, 170f3c0184aSmlarkin uint8_t irq_needed, pci_cs_fn_t csfunc) 171f3c0184aSmlarkin { 172f3c0184aSmlarkin /* Exceeded max devices? */ 173f3c0184aSmlarkin if (pci.pci_dev_ct >= PCI_CONFIG_MAX_DEV) 174f3c0184aSmlarkin return (1); 175f3c0184aSmlarkin 176f3c0184aSmlarkin /* Exceeded max IRQs? */ 177f3c0184aSmlarkin /* XXX we could share IRQs ... */ 178f3c0184aSmlarkin if (pci.pci_next_pic_irq >= PCI_MAX_PIC_IRQS && irq_needed) 179f3c0184aSmlarkin return (1); 180f3c0184aSmlarkin 181f3c0184aSmlarkin *id = pci.pci_dev_ct; 182f3c0184aSmlarkin 183f3c0184aSmlarkin pci.pci_devices[*id].pd_vid = vid; 184f3c0184aSmlarkin pci.pci_devices[*id].pd_did = pid; 185f3c0184aSmlarkin pci.pci_devices[*id].pd_class = class; 186f3c0184aSmlarkin pci.pci_devices[*id].pd_subclass = subclass; 187f3c0184aSmlarkin pci.pci_devices[*id].pd_subsys_vid = subsys_vid; 188f3c0184aSmlarkin pci.pci_devices[*id].pd_subsys_id = subsys_id; 189f3c0184aSmlarkin 190f3c0184aSmlarkin pci.pci_devices[*id].pd_csfunc = csfunc; 191f3c0184aSmlarkin 192f3c0184aSmlarkin if (irq_needed) { 193f3c0184aSmlarkin pci.pci_devices[*id].pd_irq = 194f3c0184aSmlarkin pci_pic_irqs[pci.pci_next_pic_irq]; 195f3c0184aSmlarkin pci.pci_devices[*id].pd_int = 1; 196f3c0184aSmlarkin pci.pci_next_pic_irq++; 1971f5e00e0Sreyk DPRINTF("assigned irq %d to pci dev %d", 198f3c0184aSmlarkin pci.pci_devices[*id].pd_irq, *id); 199c4fd4c5bSdv intr_toggle_el(¤t_vm, pci.pci_devices[*id].pd_irq, 1); 200f3c0184aSmlarkin } 201f3c0184aSmlarkin 202f3c0184aSmlarkin pci.pci_dev_ct ++; 203f3c0184aSmlarkin 204f3c0184aSmlarkin return (0); 205f3c0184aSmlarkin } 206f3c0184aSmlarkin 207f3c0184aSmlarkin /* 208f3c0184aSmlarkin * pci_init 209f3c0184aSmlarkin * 210f3c0184aSmlarkin * Initializes the PCI subsystem for the VM by adding a PCI host bridge 211f3c0184aSmlarkin * as the first PCI device. 212f3c0184aSmlarkin */ 213f3c0184aSmlarkin void 214f3c0184aSmlarkin pci_init(void) 215f3c0184aSmlarkin { 216f3c0184aSmlarkin uint8_t id; 217f3c0184aSmlarkin 218cb0fa87cSmlarkin memset(&pci, 0, sizeof(pci)); 219*a4052f0fSdv pci.pci_next_mmio_bar = PCI_MMIO_BAR_BASE; 220c4fd4c5bSdv 221c4fd4c5bSdv #ifdef __amd64__ 222d489aa7eSdv pci.pci_next_io_bar = VM_PCI_IO_BAR_BASE; 223c4fd4c5bSdv #endif /* __amd64__ */ 224f3c0184aSmlarkin 225f3c0184aSmlarkin if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_PCHB, 226f3c0184aSmlarkin PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_HOST, 227f3c0184aSmlarkin PCI_VENDOR_OPENBSD, 0, 0, NULL)) { 2287da934edSreyk log_warnx("%s: can't add PCI host bridge", __progname); 229f3c0184aSmlarkin return; 230f3c0184aSmlarkin } 231f3c0184aSmlarkin } 232f3c0184aSmlarkin 233c4fd4c5bSdv #ifdef __amd64__ 234f3c0184aSmlarkin void 235f3c0184aSmlarkin pci_handle_address_reg(struct vm_run_params *vrp) 236f3c0184aSmlarkin { 23702ee787fSmlarkin struct vm_exit *vei = vrp->vrp_exit; 238f3c0184aSmlarkin 239f3c0184aSmlarkin /* 2406ee12970Smlarkin * vei_dir == VEI_DIR_OUT : out instruction 241f3c0184aSmlarkin * 242f3c0184aSmlarkin * The guest wrote to the address register. 243f3c0184aSmlarkin */ 2446ee12970Smlarkin if (vei->vei.vei_dir == VEI_DIR_OUT) { 245b966d91aSmlarkin get_input_data(vei, &pci.pci_addr_reg); 246f3c0184aSmlarkin } else { 247f3c0184aSmlarkin /* 2486ee12970Smlarkin * vei_dir == VEI_DIR_IN : in instruction 249f3c0184aSmlarkin * 250de6a9f31Smlarkin * The guest read the address register 251f3c0184aSmlarkin */ 252eef1411cSmlarkin set_return_data(vei, pci.pci_addr_reg); 253f3c0184aSmlarkin } 254f3c0184aSmlarkin } 255f3c0184aSmlarkin 256f3c0184aSmlarkin uint8_t 257f3c0184aSmlarkin pci_handle_io(struct vm_run_params *vrp) 258f3c0184aSmlarkin { 259f3c0184aSmlarkin int i, j, k, l; 260f3c0184aSmlarkin uint16_t reg, b_hi, b_lo; 261f3c0184aSmlarkin pci_iobar_fn_t fn; 26202ee787fSmlarkin struct vm_exit *vei = vrp->vrp_exit; 263de6a9f31Smlarkin uint8_t intr, dir; 264f3c0184aSmlarkin 265f3c0184aSmlarkin k = -1; 266f3c0184aSmlarkin l = -1; 267f3c0184aSmlarkin reg = vei->vei.vei_port; 268de6a9f31Smlarkin dir = vei->vei.vei_dir; 269f3c0184aSmlarkin intr = 0xFF; 270f3c0184aSmlarkin 271f3c0184aSmlarkin for (i = 0 ; i < pci.pci_dev_ct ; i++) { 272f3c0184aSmlarkin for (j = 0 ; j < pci.pci_devices[i].pd_bar_ct; j++) { 273f3c0184aSmlarkin b_lo = PCI_MAPREG_IO_ADDR(pci.pci_devices[i].pd_bar[j]); 274d489aa7eSdv b_hi = b_lo + VM_PCI_IO_BAR_SIZE; 275f3c0184aSmlarkin if (reg >= b_lo && reg < b_hi) { 276f3c0184aSmlarkin if (pci.pci_devices[i].pd_barfunc[j]) { 277f3c0184aSmlarkin k = j; 278f3c0184aSmlarkin l = i; 279f3c0184aSmlarkin } 280f3c0184aSmlarkin } 281f3c0184aSmlarkin } 282f3c0184aSmlarkin } 283f3c0184aSmlarkin 284f3c0184aSmlarkin if (k >= 0 && l >= 0) { 285f3c0184aSmlarkin fn = (pci_iobar_fn_t)pci.pci_devices[l].pd_barfunc[k]; 286f3c0184aSmlarkin if (fn(vei->vei.vei_dir, reg - 287f3c0184aSmlarkin PCI_MAPREG_IO_ADDR(pci.pci_devices[l].pd_bar[k]), 288f3c0184aSmlarkin &vei->vei.vei_data, &intr, 289eef1411cSmlarkin pci.pci_devices[l].pd_bar_cookie[k], 290eef1411cSmlarkin vei->vei.vei_size)) { 2917da934edSreyk log_warnx("%s: pci i/o access function failed", 292f3c0184aSmlarkin __progname); 293f3c0184aSmlarkin } 294f3c0184aSmlarkin } else { 2958e646ea0Smlarkin DPRINTF("%s: no pci i/o function for reg 0x%llx (dir=%d " 2968e646ea0Smlarkin "guest %%rip=0x%llx", __progname, (uint64_t)reg, dir, 2978e646ea0Smlarkin vei->vrs.vrs_gprs[VCPU_REGS_RIP]); 298de6a9f31Smlarkin /* Reads from undefined ports return 0xFF */ 299afd1d05cSmlarkin if (dir == VEI_DIR_IN) 300eef1411cSmlarkin set_return_data(vei, 0xFFFFFFFF); 301f3c0184aSmlarkin } 302f3c0184aSmlarkin 303f3c0184aSmlarkin if (intr != 0xFF) { 304f3c0184aSmlarkin intr = pci.pci_devices[l].pd_irq; 305f3c0184aSmlarkin } 306f3c0184aSmlarkin 307f3c0184aSmlarkin return (intr); 308f3c0184aSmlarkin } 309f3c0184aSmlarkin 310f3c0184aSmlarkin void 311f3c0184aSmlarkin pci_handle_data_reg(struct vm_run_params *vrp) 312f3c0184aSmlarkin { 31302ee787fSmlarkin struct vm_exit *vei = vrp->vrp_exit; 314eef1411cSmlarkin uint8_t b, d, f, o, baridx, ofs, sz; 315f3c0184aSmlarkin int ret; 316f3c0184aSmlarkin pci_cs_fn_t csfunc; 317f3c0184aSmlarkin 318f3c0184aSmlarkin /* abort if the address register is wack */ 319f3c0184aSmlarkin if (!(pci.pci_addr_reg & PCI_MODE1_ENABLE)) { 320f3c0184aSmlarkin /* if read, return FFs */ 3216ee12970Smlarkin if (vei->vei.vei_dir == VEI_DIR_IN) 322eef1411cSmlarkin set_return_data(vei, 0xFFFFFFFF); 3237da934edSreyk log_warnx("invalid address register during pci read: " 3247da934edSreyk "0x%llx", (uint64_t)pci.pci_addr_reg); 325f3c0184aSmlarkin return; 326f3c0184aSmlarkin } 327f3c0184aSmlarkin 328eef1411cSmlarkin /* I/Os to 0xCFC..0xCFF are permitted */ 329eef1411cSmlarkin ofs = vei->vei.vei_port - 0xCFC; 330eef1411cSmlarkin sz = vei->vei.vei_size; 331eef1411cSmlarkin 332f3c0184aSmlarkin b = (pci.pci_addr_reg >> 16) & 0xff; 333f3c0184aSmlarkin d = (pci.pci_addr_reg >> 11) & 0x1f; 334f3c0184aSmlarkin f = (pci.pci_addr_reg >> 8) & 0x7; 335f3c0184aSmlarkin o = (pci.pci_addr_reg & 0xfc); 336f3c0184aSmlarkin 337f3c0184aSmlarkin csfunc = pci.pci_devices[d].pd_csfunc; 338f3c0184aSmlarkin if (csfunc != NULL) { 339f3c0184aSmlarkin ret = csfunc(vei->vei.vei_dir, (o / 4), &vei->vei.vei_data); 340f3c0184aSmlarkin if (ret) 3417da934edSreyk log_warnx("cfg space access function failed for " 3427da934edSreyk "pci device %d", d); 343f3c0184aSmlarkin return; 344f3c0184aSmlarkin } 345f3c0184aSmlarkin 346f3c0184aSmlarkin /* No config space function, fallback to default simple r/w impl. */ 347f3c0184aSmlarkin 348eef1411cSmlarkin o += ofs; 349eef1411cSmlarkin 350f3c0184aSmlarkin /* 3516ee12970Smlarkin * vei_dir == VEI_DIR_OUT : out instruction 352f3c0184aSmlarkin * 353f3c0184aSmlarkin * The guest wrote to the config space location denoted by the current 354f3c0184aSmlarkin * value in the address register. 355f3c0184aSmlarkin */ 3566ee12970Smlarkin if (vei->vei.vei_dir == VEI_DIR_OUT) { 357f3c0184aSmlarkin if ((o >= 0x10 && o <= 0x24) && 358f3c0184aSmlarkin vei->vei.vei_data == 0xffffffff) { 359de6a9f31Smlarkin /* 360de6a9f31Smlarkin * Compute BAR index: 361afd1d05cSmlarkin * o = 0x10 -> baridx = 0 362afd1d05cSmlarkin * o = 0x14 -> baridx = 1 363afd1d05cSmlarkin * o = 0x18 -> baridx = 2 364afd1d05cSmlarkin * o = 0x1c -> baridx = 3 365afd1d05cSmlarkin * o = 0x20 -> baridx = 4 366afd1d05cSmlarkin * o = 0x24 -> baridx = 5 367de6a9f31Smlarkin */ 368afd1d05cSmlarkin baridx = (o / 4) - 4; 369afd1d05cSmlarkin if (baridx < pci.pci_devices[d].pd_bar_ct) 370f3c0184aSmlarkin vei->vei.vei_data = 0xfffff000; 371de6a9f31Smlarkin else 372de6a9f31Smlarkin vei->vei.vei_data = 0; 373f3c0184aSmlarkin } 374eef1411cSmlarkin 375afd1d05cSmlarkin /* IOBAR registers must have bit 0 set */ 376afd1d05cSmlarkin if (o >= 0x10 && o <= 0x24) { 377afd1d05cSmlarkin baridx = (o / 4) - 4; 378afd1d05cSmlarkin if (baridx < pci.pci_devices[d].pd_bar_ct && 379afd1d05cSmlarkin pci.pci_devices[d].pd_bartype[baridx] == 380afd1d05cSmlarkin PCI_BAR_TYPE_IO) 381afd1d05cSmlarkin vei->vei.vei_data |= 1; 382afd1d05cSmlarkin } 383afd1d05cSmlarkin 38481865e7dSmlarkin /* 38581865e7dSmlarkin * Discard writes to "option rom base address" as none of our 386afd1d05cSmlarkin * emulated devices have PCI option roms. Accept any other 387afd1d05cSmlarkin * writes and copy data to config space registers. 38881865e7dSmlarkin */ 38981865e7dSmlarkin if (o != PCI_EXROMADDR_0) 39081865e7dSmlarkin get_input_data(vei, 39181865e7dSmlarkin &pci.pci_devices[d].pd_cfg_space[o / 4]); 392f3c0184aSmlarkin } else { 393f3c0184aSmlarkin /* 3946ee12970Smlarkin * vei_dir == VEI_DIR_IN : in instruction 395f3c0184aSmlarkin * 396f3c0184aSmlarkin * The guest read from the config space location determined by 397f3c0184aSmlarkin * the current value in the address register. 398f3c0184aSmlarkin */ 399b966d91aSmlarkin if (d > pci.pci_dev_ct || b > 0 || f > 0) 400b966d91aSmlarkin set_return_data(vei, 0xFFFFFFFF); 401b966d91aSmlarkin else { 402b966d91aSmlarkin switch (sz) { 403b966d91aSmlarkin case 4: 404d2de69e7Sreyk set_return_data(vei, 405d2de69e7Sreyk pci.pci_devices[d].pd_cfg_space[o / 4]); 406b966d91aSmlarkin break; 407b966d91aSmlarkin case 2: 408b966d91aSmlarkin if (ofs == 0) 409d2de69e7Sreyk set_return_data(vei, pci.pci_devices[d]. 410d2de69e7Sreyk pd_cfg_space[o / 4]); 411b966d91aSmlarkin else 412d2de69e7Sreyk set_return_data(vei, pci.pci_devices[d]. 413d2de69e7Sreyk pd_cfg_space[o / 4] >> 16); 414b966d91aSmlarkin break; 415b966d91aSmlarkin case 1: 416d2de69e7Sreyk set_return_data(vei, pci.pci_devices[d]. 417d2de69e7Sreyk pd_cfg_space[o / 4] >> (ofs * 8)); 418b966d91aSmlarkin break; 419b966d91aSmlarkin } 420b966d91aSmlarkin } 421f3c0184aSmlarkin } 422f3c0184aSmlarkin } 423c4fd4c5bSdv #endif /* __amd64__ */ 424813e3047Spd 425813e3047Spd int 426813e3047Spd pci_dump(int fd) 427813e3047Spd { 428813e3047Spd log_debug("%s: sending pci", __func__); 429813e3047Spd if (atomicio(vwrite, fd, &pci, sizeof(pci)) != sizeof(pci)) { 430813e3047Spd log_warnx("%s: error writing pci to fd", __func__); 431813e3047Spd return (-1); 432813e3047Spd } 433813e3047Spd return (0); 434813e3047Spd } 435813e3047Spd 436813e3047Spd int 437813e3047Spd pci_restore(int fd) 438813e3047Spd { 439813e3047Spd log_debug("%s: receiving pci", __func__); 440813e3047Spd if (atomicio(read, fd, &pci, sizeof(pci)) != sizeof(pci)) { 441813e3047Spd log_warnx("%s: error reading pci from fd", __func__); 442813e3047Spd return (-1); 443813e3047Spd } 444813e3047Spd return (0); 445813e3047Spd } 4461e8eabf2Sdv 4471e8eabf2Sdv /* 4481e8eabf2Sdv * Find the first PCI device based on PCI Subsystem ID 4491e8eabf2Sdv * (e.g. PCI_PRODUCT_VIRTIO_BLOCK). 4501e8eabf2Sdv * 4511e8eabf2Sdv * Returns the PCI device id of the first matching device, if found. 4521e8eabf2Sdv * Otherwise, returns -1. 4531e8eabf2Sdv */ 4541e8eabf2Sdv int 4551e8eabf2Sdv pci_find_first_device(uint16_t subsys_id) 4561e8eabf2Sdv { 4571e8eabf2Sdv int i; 4581e8eabf2Sdv 4591e8eabf2Sdv for (i = 0; i < pci.pci_dev_ct; i++) 4601e8eabf2Sdv if (pci.pci_devices[i].pd_subsys_id == subsys_id) 4611e8eabf2Sdv return (i); 4621e8eabf2Sdv return (-1); 4631e8eabf2Sdv } 464