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