1/* $OpenBSD: pci.c,v 1.31 2023/02/06 20:33:34 dv Exp $ */ 2 3/* 4 * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20 21#include <dev/pci/pcireg.h> 22#include <dev/pci/pcidevs.h> 23#include <dev/pv/virtioreg.h> 24#include <machine/vmmvar.h> 25 26#include <string.h> 27#include <unistd.h> 28 29#include "vmd.h" 30#include "pci.h" 31#include "vmm.h" 32#include "i8259.h" 33#include "atomicio.h" 34 35struct pci pci; 36 37extern char *__progname; 38 39/* PIC IRQs, assigned to devices in order */ 40const uint8_t pci_pic_irqs[PCI_MAX_PIC_IRQS] = {3, 5, 6, 7, 9, 10, 11, 12, 41 14, 15}; 42 43/* 44 * pci_add_bar 45 * 46 * Adds a BAR for the PCI device 'id'. On access, 'barfn' will be 47 * called, and passed 'cookie' as an identifier. 48 * 49 * BARs are fixed size, meaning all I/O BARs requested have the 50 * same size and all MMIO BARs have the same size. 51 * 52 * Parameters: 53 * id: PCI device to add the BAR to (local count, eg if id == 4, 54 * this BAR is to be added to the VM's 5th PCI device) 55 * type: type of the BAR to add (PCI_MAPREG_TYPE_xxx) 56 * barfn: callback function invoked on BAR access 57 * cookie: cookie passed to barfn on access 58 * 59 * Returns 0 if the BAR was added successfully, 1 otherwise. 60 */ 61int 62pci_add_bar(uint8_t id, uint32_t type, void *barfn, void *cookie) 63{ 64 uint8_t bar_reg_idx, bar_ct; 65 66 /* Check id */ 67 if (id >= pci.pci_dev_ct) 68 return (1); 69 70 /* Can only add PCI_MAX_BARS BARs to any device */ 71 bar_ct = pci.pci_devices[id].pd_bar_ct; 72 if (bar_ct >= PCI_MAX_BARS) 73 return (1); 74 75 /* Compute BAR address and add */ 76 bar_reg_idx = (PCI_MAPREG_START + (bar_ct * 4)) / 4; 77 if (type == PCI_MAPREG_TYPE_MEM) { 78 if (pci.pci_next_mmio_bar >= VMM_PCI_MMIO_BAR_END) 79 return (1); 80 81 pci.pci_devices[id].pd_cfg_space[bar_reg_idx] = 82 PCI_MAPREG_MEM_ADDR(pci.pci_next_mmio_bar); 83 pci.pci_next_mmio_bar += VM_PCI_MMIO_BAR_SIZE; 84 pci.pci_devices[id].pd_barfunc[bar_ct] = barfn; 85 pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie; 86 pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_MMIO; 87 pci.pci_devices[id].pd_barsize[bar_ct] = VM_PCI_MMIO_BAR_SIZE; 88 pci.pci_devices[id].pd_bar_ct++; 89 } else if (type == PCI_MAPREG_TYPE_IO) { 90 if (pci.pci_next_io_bar >= VM_PCI_IO_BAR_END) 91 return (1); 92 93 pci.pci_devices[id].pd_cfg_space[bar_reg_idx] = 94 PCI_MAPREG_IO_ADDR(pci.pci_next_io_bar) | 95 PCI_MAPREG_TYPE_IO; 96 pci.pci_next_io_bar += VM_PCI_IO_BAR_SIZE; 97 pci.pci_devices[id].pd_barfunc[bar_ct] = barfn; 98 pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie; 99 DPRINTF("%s: adding pci bar cookie for dev %d bar %d = %p", 100 __progname, id, bar_ct, cookie); 101 pci.pci_devices[id].pd_bartype[bar_ct] = PCI_BAR_TYPE_IO; 102 pci.pci_devices[id].pd_barsize[bar_ct] = VM_PCI_IO_BAR_SIZE; 103 pci.pci_devices[id].pd_bar_ct++; 104 } 105 106 return (0); 107} 108 109int 110pci_set_bar_fn(uint8_t id, uint8_t bar_ct, void *barfn, void *cookie) 111{ 112 /* Check id */ 113 if (id >= pci.pci_dev_ct) 114 return (1); 115 116 if (bar_ct >= PCI_MAX_BARS) 117 return (1); 118 119 pci.pci_devices[id].pd_barfunc[bar_ct] = barfn; 120 pci.pci_devices[id].pd_bar_cookie[bar_ct] = cookie; 121 122 return (0); 123} 124 125/* 126 * pci_get_dev_irq 127 * 128 * Returns the IRQ for the specified PCI device 129 * 130 * Parameters: 131 * id: PCI device id to return IRQ for 132 * 133 * Return values: 134 * The IRQ for the device, or 0xff if no device IRQ assigned 135 */ 136uint8_t 137pci_get_dev_irq(uint8_t id) 138{ 139 if (pci.pci_devices[id].pd_int) 140 return pci.pci_devices[id].pd_irq; 141 else 142 return 0xFF; 143} 144 145/* 146 * pci_add_device 147 * 148 * Adds a PCI device to the guest VM defined by the supplied parameters. 149 * 150 * Parameters: 151 * id: the new PCI device ID (0 .. PCI_CONFIG_MAX_DEV) 152 * vid: PCI VID of the new device 153 * pid: PCI PID of the new device 154 * class: PCI 'class' of the new device 155 * subclass: PCI 'subclass' of the new device 156 * subsys_vid: subsystem VID of the new device 157 * subsys_id: subsystem ID of the new device 158 * irq_needed: 1 if an IRQ should be assigned to this PCI device, 0 otherwise 159 * csfunc: PCI config space callback function when the guest VM accesses 160 * CS of this PCI device 161 * 162 * Return values: 163 * 0: the PCI device was added successfully. The PCI device ID is in 'id'. 164 * 1: the PCI device addition failed. 165 */ 166int 167pci_add_device(uint8_t *id, uint16_t vid, uint16_t pid, uint8_t class, 168 uint8_t subclass, uint16_t subsys_vid, uint16_t subsys_id, 169 uint8_t irq_needed, pci_cs_fn_t csfunc) 170{ 171 /* Exceeded max devices? */ 172 if (pci.pci_dev_ct >= PCI_CONFIG_MAX_DEV) 173 return (1); 174 175 /* Exceeded max IRQs? */ 176 /* XXX we could share IRQs ... */ 177 if (pci.pci_next_pic_irq >= PCI_MAX_PIC_IRQS && irq_needed) 178 return (1); 179 180 *id = pci.pci_dev_ct; 181 182 pci.pci_devices[*id].pd_vid = vid; 183 pci.pci_devices[*id].pd_did = pid; 184 pci.pci_devices[*id].pd_class = class; 185 pci.pci_devices[*id].pd_subclass = subclass; 186 pci.pci_devices[*id].pd_subsys_vid = subsys_vid; 187 pci.pci_devices[*id].pd_subsys_id = subsys_id; 188 189 pci.pci_devices[*id].pd_csfunc = csfunc; 190 191 if (irq_needed) { 192 pci.pci_devices[*id].pd_irq = 193 pci_pic_irqs[pci.pci_next_pic_irq]; 194 pci.pci_devices[*id].pd_int = 1; 195 pci.pci_next_pic_irq++; 196 DPRINTF("assigned irq %d to pci dev %d", 197 pci.pci_devices[*id].pd_irq, *id); 198 pic_set_elcr(pci.pci_devices[*id].pd_irq, 1); 199 } 200 201 pci.pci_dev_ct ++; 202 203 return (0); 204} 205 206/* 207 * pci_init 208 * 209 * Initializes the PCI subsystem for the VM by adding a PCI host bridge 210 * as the first PCI device. 211 */ 212void 213pci_init(void) 214{ 215 uint8_t id; 216 217 memset(&pci, 0, sizeof(pci)); 218 pci.pci_next_mmio_bar = VMM_PCI_MMIO_BAR_BASE; 219 pci.pci_next_io_bar = VM_PCI_IO_BAR_BASE; 220 221 if (pci_add_device(&id, PCI_VENDOR_OPENBSD, PCI_PRODUCT_OPENBSD_PCHB, 222 PCI_CLASS_BRIDGE, PCI_SUBCLASS_BRIDGE_HOST, 223 PCI_VENDOR_OPENBSD, 0, 0, NULL)) { 224 log_warnx("%s: can't add PCI host bridge", __progname); 225 return; 226 } 227} 228 229void 230pci_handle_address_reg(struct vm_run_params *vrp) 231{ 232 struct vm_exit *vei = vrp->vrp_exit; 233 234 /* 235 * vei_dir == VEI_DIR_OUT : out instruction 236 * 237 * The guest wrote to the address register. 238 */ 239 if (vei->vei.vei_dir == VEI_DIR_OUT) { 240 get_input_data(vei, &pci.pci_addr_reg); 241 } else { 242 /* 243 * vei_dir == VEI_DIR_IN : in instruction 244 * 245 * The guest read the address register 246 */ 247 set_return_data(vei, pci.pci_addr_reg); 248 } 249} 250 251uint8_t 252pci_handle_io(struct vm_run_params *vrp) 253{ 254 int i, j, k, l; 255 uint16_t reg, b_hi, b_lo; 256 pci_iobar_fn_t fn; 257 struct vm_exit *vei = vrp->vrp_exit; 258 uint8_t intr, dir; 259 260 k = -1; 261 l = -1; 262 reg = vei->vei.vei_port; 263 dir = vei->vei.vei_dir; 264 intr = 0xFF; 265 266 for (i = 0 ; i < pci.pci_dev_ct ; i++) { 267 for (j = 0 ; j < pci.pci_devices[i].pd_bar_ct; j++) { 268 b_lo = PCI_MAPREG_IO_ADDR(pci.pci_devices[i].pd_bar[j]); 269 b_hi = b_lo + VM_PCI_IO_BAR_SIZE; 270 if (reg >= b_lo && reg < b_hi) { 271 if (pci.pci_devices[i].pd_barfunc[j]) { 272 k = j; 273 l = i; 274 } 275 } 276 } 277 } 278 279 if (k >= 0 && l >= 0) { 280 fn = (pci_iobar_fn_t)pci.pci_devices[l].pd_barfunc[k]; 281 if (fn(vei->vei.vei_dir, reg - 282 PCI_MAPREG_IO_ADDR(pci.pci_devices[l].pd_bar[k]), 283 &vei->vei.vei_data, &intr, 284 pci.pci_devices[l].pd_bar_cookie[k], 285 vei->vei.vei_size)) { 286 log_warnx("%s: pci i/o access function failed", 287 __progname); 288 } 289 } else { 290 DPRINTF("%s: no pci i/o function for reg 0x%llx (dir=%d " 291 "guest %%rip=0x%llx", __progname, (uint64_t)reg, dir, 292 vei->vrs.vrs_gprs[VCPU_REGS_RIP]); 293 /* Reads from undefined ports return 0xFF */ 294 if (dir == VEI_DIR_IN) 295 set_return_data(vei, 0xFFFFFFFF); 296 } 297 298 if (intr != 0xFF) { 299 intr = pci.pci_devices[l].pd_irq; 300 } 301 302 return (intr); 303} 304 305void 306pci_handle_data_reg(struct vm_run_params *vrp) 307{ 308 struct vm_exit *vei = vrp->vrp_exit; 309 uint8_t b, d, f, o, baridx, ofs, sz; 310 int ret; 311 pci_cs_fn_t csfunc; 312 313 /* abort if the address register is wack */ 314 if (!(pci.pci_addr_reg & PCI_MODE1_ENABLE)) { 315 /* if read, return FFs */ 316 if (vei->vei.vei_dir == VEI_DIR_IN) 317 set_return_data(vei, 0xFFFFFFFF); 318 log_warnx("invalid address register during pci read: " 319 "0x%llx", (uint64_t)pci.pci_addr_reg); 320 return; 321 } 322 323 /* I/Os to 0xCFC..0xCFF are permitted */ 324 ofs = vei->vei.vei_port - 0xCFC; 325 sz = vei->vei.vei_size; 326 327 b = (pci.pci_addr_reg >> 16) & 0xff; 328 d = (pci.pci_addr_reg >> 11) & 0x1f; 329 f = (pci.pci_addr_reg >> 8) & 0x7; 330 o = (pci.pci_addr_reg & 0xfc); 331 332 csfunc = pci.pci_devices[d].pd_csfunc; 333 if (csfunc != NULL) { 334 ret = csfunc(vei->vei.vei_dir, (o / 4), &vei->vei.vei_data); 335 if (ret) 336 log_warnx("cfg space access function failed for " 337 "pci device %d", d); 338 return; 339 } 340 341 /* No config space function, fallback to default simple r/w impl. */ 342 343 o += ofs; 344 345 /* 346 * vei_dir == VEI_DIR_OUT : out instruction 347 * 348 * The guest wrote to the config space location denoted by the current 349 * value in the address register. 350 */ 351 if (vei->vei.vei_dir == VEI_DIR_OUT) { 352 if ((o >= 0x10 && o <= 0x24) && 353 vei->vei.vei_data == 0xffffffff) { 354 /* 355 * Compute BAR index: 356 * o = 0x10 -> baridx = 0 357 * o = 0x14 -> baridx = 1 358 * o = 0x18 -> baridx = 2 359 * o = 0x1c -> baridx = 3 360 * o = 0x20 -> baridx = 4 361 * o = 0x24 -> baridx = 5 362 */ 363 baridx = (o / 4) - 4; 364 if (baridx < pci.pci_devices[d].pd_bar_ct) 365 vei->vei.vei_data = 0xfffff000; 366 else 367 vei->vei.vei_data = 0; 368 } 369 370 /* IOBAR registers must have bit 0 set */ 371 if (o >= 0x10 && o <= 0x24) { 372 baridx = (o / 4) - 4; 373 if (baridx < pci.pci_devices[d].pd_bar_ct && 374 pci.pci_devices[d].pd_bartype[baridx] == 375 PCI_BAR_TYPE_IO) 376 vei->vei.vei_data |= 1; 377 } 378 379 /* 380 * Discard writes to "option rom base address" as none of our 381 * emulated devices have PCI option roms. Accept any other 382 * writes and copy data to config space registers. 383 */ 384 if (o != PCI_EXROMADDR_0) 385 get_input_data(vei, 386 &pci.pci_devices[d].pd_cfg_space[o / 4]); 387 } else { 388 /* 389 * vei_dir == VEI_DIR_IN : in instruction 390 * 391 * The guest read from the config space location determined by 392 * the current value in the address register. 393 */ 394 if (d > pci.pci_dev_ct || b > 0 || f > 0) 395 set_return_data(vei, 0xFFFFFFFF); 396 else { 397 switch (sz) { 398 case 4: 399 set_return_data(vei, 400 pci.pci_devices[d].pd_cfg_space[o / 4]); 401 break; 402 case 2: 403 if (ofs == 0) 404 set_return_data(vei, pci.pci_devices[d]. 405 pd_cfg_space[o / 4]); 406 else 407 set_return_data(vei, pci.pci_devices[d]. 408 pd_cfg_space[o / 4] >> 16); 409 break; 410 case 1: 411 set_return_data(vei, pci.pci_devices[d]. 412 pd_cfg_space[o / 4] >> (ofs * 8)); 413 break; 414 } 415 } 416 } 417} 418 419int 420pci_dump(int fd) 421{ 422 log_debug("%s: sending pci", __func__); 423 if (atomicio(vwrite, fd, &pci, sizeof(pci)) != sizeof(pci)) { 424 log_warnx("%s: error writing pci to fd", __func__); 425 return (-1); 426 } 427 return (0); 428} 429 430int 431pci_restore(int fd) 432{ 433 log_debug("%s: receiving pci", __func__); 434 if (atomicio(read, fd, &pci, sizeof(pci)) != sizeof(pci)) { 435 log_warnx("%s: error reading pci from fd", __func__); 436 return (-1); 437 } 438 return (0); 439} 440 441/* 442 * Find the first PCI device based on PCI Subsystem ID 443 * (e.g. PCI_PRODUCT_VIRTIO_BLOCK). 444 * 445 * Returns the PCI device id of the first matching device, if found. 446 * Otherwise, returns -1. 447 */ 448int 449pci_find_first_device(uint16_t subsys_id) 450{ 451 int i; 452 453 for (i = 0; i < pci.pci_dev_ct; i++) 454 if (pci.pci_devices[i].pd_subsys_id == subsys_id) 455 return (i); 456 return (-1); 457} 458