1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011 NetApp, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/module.h> 34 #include <sys/bus.h> 35 #include <sys/pciio.h> 36 #include <sys/rman.h> 37 #include <sys/smp.h> 38 #include <sys/sysctl.h> 39 40 #include <dev/pci/pcivar.h> 41 #include <dev/pci/pcireg.h> 42 43 #include <machine/resource.h> 44 #include <machine/vmm.h> 45 #include <machine/vmm_dev.h> 46 47 #include <dev/vmm/vmm_ktr.h> 48 49 #include "vmm_lapic.h" 50 51 #include "iommu.h" 52 #include "ppt.h" 53 54 /* XXX locking */ 55 56 #define MAX_MSIMSGS 32 57 58 /* 59 * If the MSI-X table is located in the middle of a BAR then that MMIO 60 * region gets split into two segments - one segment above the MSI-X table 61 * and the other segment below the MSI-X table - with a hole in place of 62 * the MSI-X table so accesses to it can be trapped and emulated. 63 * 64 * So, allocate a MMIO segment for each BAR register + 1 additional segment. 65 */ 66 #define MAX_MMIOSEGS ((PCIR_MAX_BAR_0 + 1) + 1) 67 68 MALLOC_DEFINE(M_PPTMSIX, "pptmsix", "Passthru MSI-X resources"); 69 70 struct pptintr_arg { /* pptintr(pptintr_arg) */ 71 struct pptdev *pptdev; 72 uint64_t addr; 73 uint64_t msg_data; 74 }; 75 76 struct pptseg { 77 vm_paddr_t gpa; 78 size_t len; 79 int wired; 80 }; 81 82 struct pptdev { 83 device_t dev; 84 struct vm *vm; /* owner of this device */ 85 TAILQ_ENTRY(pptdev) next; 86 struct pptseg mmio[MAX_MMIOSEGS]; 87 struct { 88 int num_msgs; /* guest state */ 89 90 int startrid; /* host state */ 91 struct resource *res[MAX_MSIMSGS]; 92 void *cookie[MAX_MSIMSGS]; 93 struct pptintr_arg arg[MAX_MSIMSGS]; 94 } msi; 95 96 struct { 97 int num_msgs; 98 int startrid; 99 int msix_table_rid; 100 int msix_pba_rid; 101 struct resource *msix_table_res; 102 struct resource *msix_pba_res; 103 struct resource **res; 104 void **cookie; 105 struct pptintr_arg *arg; 106 } msix; 107 }; 108 109 SYSCTL_DECL(_hw_vmm); 110 SYSCTL_NODE(_hw_vmm, OID_AUTO, ppt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 111 "bhyve passthru devices"); 112 113 static int num_pptdevs; 114 SYSCTL_INT(_hw_vmm_ppt, OID_AUTO, devices, CTLFLAG_RD, &num_pptdevs, 0, 115 "number of pci passthru devices"); 116 117 static TAILQ_HEAD(, pptdev) pptdev_list = TAILQ_HEAD_INITIALIZER(pptdev_list); 118 119 static int 120 ppt_probe(device_t dev) 121 { 122 int bus, slot, func; 123 struct pci_devinfo *dinfo; 124 125 dinfo = (struct pci_devinfo *)device_get_ivars(dev); 126 127 bus = pci_get_bus(dev); 128 slot = pci_get_slot(dev); 129 func = pci_get_function(dev); 130 131 /* 132 * To qualify as a pci passthrough device a device must: 133 * - be allowed by administrator to be used in this role 134 * - be an endpoint device 135 */ 136 if ((dinfo->cfg.hdrtype & PCIM_HDRTYPE) != PCIM_HDRTYPE_NORMAL) 137 return (ENXIO); 138 else if (vmm_is_pptdev(bus, slot, func)) 139 return (0); 140 else 141 /* 142 * Returning BUS_PROBE_NOWILDCARD here matches devices that the 143 * SR-IOV infrastructure specified as "ppt" passthrough devices. 144 * All normal devices that did not have "ppt" specified as their 145 * driver will not be matched by this. 146 */ 147 return (BUS_PROBE_NOWILDCARD); 148 } 149 150 static int 151 ppt_attach(device_t dev) 152 { 153 struct pptdev *ppt; 154 uint16_t cmd, cmd1; 155 int error; 156 157 ppt = device_get_softc(dev); 158 159 cmd1 = cmd = pci_read_config(dev, PCIR_COMMAND, 2); 160 cmd &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 161 pci_write_config(dev, PCIR_COMMAND, cmd, 2); 162 error = iommu_remove_device(iommu_host_domain(), dev, pci_get_rid(dev)); 163 if (error != 0) { 164 pci_write_config(dev, PCIR_COMMAND, cmd1, 2); 165 return (error); 166 } 167 num_pptdevs++; 168 TAILQ_INSERT_TAIL(&pptdev_list, ppt, next); 169 ppt->dev = dev; 170 171 if (bootverbose) 172 device_printf(dev, "attached\n"); 173 174 return (0); 175 } 176 177 static int 178 ppt_detach(device_t dev) 179 { 180 struct pptdev *ppt; 181 int error; 182 183 ppt = device_get_softc(dev); 184 185 if (ppt->vm != NULL) 186 return (EBUSY); 187 if (iommu_host_domain() != NULL) { 188 error = iommu_add_device(iommu_host_domain(), dev, 189 pci_get_rid(dev)); 190 } else { 191 error = 0; 192 } 193 if (error != 0) 194 return (error); 195 num_pptdevs--; 196 TAILQ_REMOVE(&pptdev_list, ppt, next); 197 198 return (0); 199 } 200 201 static device_method_t ppt_methods[] = { 202 /* Device interface */ 203 DEVMETHOD(device_probe, ppt_probe), 204 DEVMETHOD(device_attach, ppt_attach), 205 DEVMETHOD(device_detach, ppt_detach), 206 {0, 0} 207 }; 208 209 DEFINE_CLASS_0(ppt, ppt_driver, ppt_methods, sizeof(struct pptdev)); 210 DRIVER_MODULE(ppt, pci, ppt_driver, NULL, NULL); 211 212 static int 213 ppt_find(struct vm *vm, int bus, int slot, int func, struct pptdev **pptp) 214 { 215 device_t dev; 216 struct pptdev *ppt; 217 int b, s, f; 218 219 TAILQ_FOREACH(ppt, &pptdev_list, next) { 220 dev = ppt->dev; 221 b = pci_get_bus(dev); 222 s = pci_get_slot(dev); 223 f = pci_get_function(dev); 224 if (bus == b && slot == s && func == f) 225 break; 226 } 227 228 if (ppt == NULL) 229 return (ENOENT); 230 if (ppt->vm != vm) /* Make sure we own this device */ 231 return (EBUSY); 232 *pptp = ppt; 233 return (0); 234 } 235 236 static void 237 ppt_unmap_all_mmio(struct vm *vm, struct pptdev *ppt) 238 { 239 int i; 240 struct pptseg *seg; 241 242 for (i = 0; i < MAX_MMIOSEGS; i++) { 243 seg = &ppt->mmio[i]; 244 if (seg->len == 0) 245 continue; 246 (void)vm_unmap_mmio(vm, seg->gpa, seg->len); 247 bzero(seg, sizeof(struct pptseg)); 248 } 249 } 250 251 static void 252 ppt_teardown_msi(struct pptdev *ppt) 253 { 254 int i, rid; 255 void *cookie; 256 struct resource *res; 257 258 if (ppt->msi.num_msgs == 0) 259 return; 260 261 for (i = 0; i < ppt->msi.num_msgs; i++) { 262 rid = ppt->msi.startrid + i; 263 res = ppt->msi.res[i]; 264 cookie = ppt->msi.cookie[i]; 265 266 if (cookie != NULL) 267 bus_teardown_intr(ppt->dev, res, cookie); 268 269 if (res != NULL) 270 bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 271 272 ppt->msi.res[i] = NULL; 273 ppt->msi.cookie[i] = NULL; 274 } 275 276 if (ppt->msi.startrid == 1) 277 pci_release_msi(ppt->dev); 278 279 ppt->msi.num_msgs = 0; 280 } 281 282 static void 283 ppt_teardown_msix_intr(struct pptdev *ppt, int idx) 284 { 285 int rid; 286 struct resource *res; 287 void *cookie; 288 289 rid = ppt->msix.startrid + idx; 290 res = ppt->msix.res[idx]; 291 cookie = ppt->msix.cookie[idx]; 292 293 if (cookie != NULL) 294 bus_teardown_intr(ppt->dev, res, cookie); 295 296 if (res != NULL) 297 bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 298 299 ppt->msix.res[idx] = NULL; 300 ppt->msix.cookie[idx] = NULL; 301 } 302 303 static void 304 ppt_teardown_msix(struct pptdev *ppt) 305 { 306 int i; 307 308 if (ppt->msix.num_msgs == 0) 309 return; 310 311 for (i = 0; i < ppt->msix.num_msgs; i++) 312 ppt_teardown_msix_intr(ppt, i); 313 314 free(ppt->msix.res, M_PPTMSIX); 315 free(ppt->msix.cookie, M_PPTMSIX); 316 free(ppt->msix.arg, M_PPTMSIX); 317 318 pci_release_msi(ppt->dev); 319 320 if (ppt->msix.msix_table_res) { 321 bus_release_resource(ppt->dev, SYS_RES_MEMORY, 322 ppt->msix.msix_table_rid, 323 ppt->msix.msix_table_res); 324 ppt->msix.msix_table_res = NULL; 325 ppt->msix.msix_table_rid = 0; 326 } 327 if (ppt->msix.msix_pba_res) { 328 bus_release_resource(ppt->dev, SYS_RES_MEMORY, 329 ppt->msix.msix_pba_rid, 330 ppt->msix.msix_pba_res); 331 ppt->msix.msix_pba_res = NULL; 332 ppt->msix.msix_pba_rid = 0; 333 } 334 335 ppt->msix.num_msgs = 0; 336 } 337 338 int 339 ppt_avail_devices(void) 340 { 341 342 return (num_pptdevs); 343 } 344 345 int 346 ppt_assigned_devices(struct vm *vm) 347 { 348 struct pptdev *ppt; 349 int num; 350 351 num = 0; 352 TAILQ_FOREACH(ppt, &pptdev_list, next) { 353 if (ppt->vm == vm) 354 num++; 355 } 356 return (num); 357 } 358 359 bool 360 ppt_is_mmio(struct vm *vm, vm_paddr_t gpa) 361 { 362 int i; 363 struct pptdev *ppt; 364 struct pptseg *seg; 365 366 TAILQ_FOREACH(ppt, &pptdev_list, next) { 367 if (ppt->vm != vm) 368 continue; 369 370 for (i = 0; i < MAX_MMIOSEGS; i++) { 371 seg = &ppt->mmio[i]; 372 if (seg->len == 0) 373 continue; 374 if (gpa >= seg->gpa && gpa < seg->gpa + seg->len) 375 return (true); 376 } 377 } 378 379 return (false); 380 } 381 382 static void 383 ppt_pci_reset(device_t dev) 384 { 385 386 if (pcie_flr(dev, 387 max(pcie_get_max_completion_timeout(dev) / 1000, 10), true)) 388 return; 389 390 pci_power_reset(dev); 391 } 392 393 static uint16_t 394 ppt_bar_enables(struct pptdev *ppt) 395 { 396 struct pci_map *pm; 397 uint16_t cmd; 398 399 cmd = 0; 400 for (pm = pci_first_bar(ppt->dev); pm != NULL; pm = pci_next_bar(pm)) { 401 if (PCI_BAR_IO(pm->pm_value)) 402 cmd |= PCIM_CMD_PORTEN; 403 if (PCI_BAR_MEM(pm->pm_value)) 404 cmd |= PCIM_CMD_MEMEN; 405 } 406 return (cmd); 407 } 408 409 int 410 ppt_assign_device(struct vm *vm, int bus, int slot, int func) 411 { 412 struct pptdev *ppt; 413 int error; 414 uint16_t cmd; 415 416 /* Passing NULL requires the device to be unowned. */ 417 error = ppt_find(NULL, bus, slot, func, &ppt); 418 if (error) 419 return (error); 420 421 pci_save_state(ppt->dev); 422 ppt_pci_reset(ppt->dev); 423 pci_restore_state(ppt->dev); 424 error = iommu_add_device(vm_iommu_domain(vm), ppt->dev, 425 pci_get_rid(ppt->dev)); 426 if (error != 0) 427 return (error); 428 ppt->vm = vm; 429 cmd = pci_read_config(ppt->dev, PCIR_COMMAND, 2); 430 cmd |= PCIM_CMD_BUSMASTEREN | ppt_bar_enables(ppt); 431 pci_write_config(ppt->dev, PCIR_COMMAND, cmd, 2); 432 return (0); 433 } 434 435 int 436 ppt_unassign_device(struct vm *vm, int bus, int slot, int func) 437 { 438 struct pptdev *ppt; 439 int error; 440 uint16_t cmd; 441 442 error = ppt_find(vm, bus, slot, func, &ppt); 443 if (error) 444 return (error); 445 446 cmd = pci_read_config(ppt->dev, PCIR_COMMAND, 2); 447 cmd &= ~(PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); 448 pci_write_config(ppt->dev, PCIR_COMMAND, cmd, 2); 449 pci_save_state(ppt->dev); 450 ppt_pci_reset(ppt->dev); 451 pci_restore_state(ppt->dev); 452 ppt_unmap_all_mmio(vm, ppt); 453 ppt_teardown_msi(ppt); 454 ppt_teardown_msix(ppt); 455 error = iommu_remove_device(vm_iommu_domain(vm), ppt->dev, 456 pci_get_rid(ppt->dev)); 457 ppt->vm = NULL; 458 return (error); 459 } 460 461 int 462 ppt_unassign_all(struct vm *vm) 463 { 464 struct pptdev *ppt; 465 int bus, slot, func; 466 device_t dev; 467 468 TAILQ_FOREACH(ppt, &pptdev_list, next) { 469 if (ppt->vm == vm) { 470 dev = ppt->dev; 471 bus = pci_get_bus(dev); 472 slot = pci_get_slot(dev); 473 func = pci_get_function(dev); 474 vm_unassign_pptdev(vm, bus, slot, func); 475 } 476 } 477 478 return (0); 479 } 480 481 static bool 482 ppt_valid_bar_mapping(struct pptdev *ppt, vm_paddr_t hpa, size_t len) 483 { 484 struct pci_map *pm; 485 pci_addr_t base, size; 486 487 for (pm = pci_first_bar(ppt->dev); pm != NULL; pm = pci_next_bar(pm)) { 488 if (!PCI_BAR_MEM(pm->pm_value)) 489 continue; 490 base = pm->pm_value & PCIM_BAR_MEM_BASE; 491 size = (pci_addr_t)1 << pm->pm_size; 492 if (hpa >= base && hpa + len <= base + size) 493 return (true); 494 } 495 return (false); 496 } 497 498 int 499 ppt_map_mmio(struct vm *vm, int bus, int slot, int func, 500 vm_paddr_t gpa, size_t len, vm_paddr_t hpa) 501 { 502 int i, error; 503 struct pptseg *seg; 504 struct pptdev *ppt; 505 506 if (len % PAGE_SIZE != 0 || len == 0 || gpa % PAGE_SIZE != 0 || 507 hpa % PAGE_SIZE != 0 || gpa + len < gpa || hpa + len < hpa) 508 return (EINVAL); 509 510 error = ppt_find(vm, bus, slot, func, &ppt); 511 if (error) 512 return (error); 513 514 if (!ppt_valid_bar_mapping(ppt, hpa, len)) 515 return (EINVAL); 516 517 for (i = 0; i < MAX_MMIOSEGS; i++) { 518 seg = &ppt->mmio[i]; 519 if (seg->len == 0) { 520 error = vm_map_mmio(vm, gpa, len, hpa); 521 if (error == 0) { 522 seg->gpa = gpa; 523 seg->len = len; 524 } 525 return (error); 526 } 527 } 528 return (ENOSPC); 529 } 530 531 int 532 ppt_unmap_mmio(struct vm *vm, int bus, int slot, int func, 533 vm_paddr_t gpa, size_t len) 534 { 535 int i, error; 536 struct pptseg *seg; 537 struct pptdev *ppt; 538 539 error = ppt_find(vm, bus, slot, func, &ppt); 540 if (error) 541 return (error); 542 543 for (i = 0; i < MAX_MMIOSEGS; i++) { 544 seg = &ppt->mmio[i]; 545 if (seg->gpa == gpa && seg->len == len) { 546 error = vm_unmap_mmio(vm, seg->gpa, seg->len); 547 if (error == 0) { 548 seg->gpa = 0; 549 seg->len = 0; 550 } 551 return (error); 552 } 553 } 554 return (ENOENT); 555 } 556 557 static int 558 pptintr(void *arg) 559 { 560 struct pptdev *ppt; 561 struct pptintr_arg *pptarg; 562 563 pptarg = arg; 564 ppt = pptarg->pptdev; 565 566 if (ppt->vm != NULL) 567 lapic_intr_msi(ppt->vm, pptarg->addr, pptarg->msg_data); 568 else { 569 /* 570 * XXX 571 * This is not expected to happen - panic? 572 */ 573 } 574 575 /* 576 * For legacy interrupts give other filters a chance in case 577 * the interrupt was not generated by the passthrough device. 578 */ 579 if (ppt->msi.startrid == 0) 580 return (FILTER_STRAY); 581 else 582 return (FILTER_HANDLED); 583 } 584 585 int 586 ppt_setup_msi(struct vm *vm, int bus, int slot, int func, 587 uint64_t addr, uint64_t msg, int numvec) 588 { 589 int i, rid, flags; 590 int msi_count, startrid, error, tmp; 591 struct pptdev *ppt; 592 593 if (numvec < 0 || numvec > MAX_MSIMSGS) 594 return (EINVAL); 595 596 error = ppt_find(vm, bus, slot, func, &ppt); 597 if (error) 598 return (error); 599 600 /* Reject attempts to enable MSI while MSI-X is active. */ 601 if (ppt->msix.num_msgs != 0 && numvec != 0) 602 return (EBUSY); 603 604 /* Free any allocated resources */ 605 ppt_teardown_msi(ppt); 606 607 if (numvec == 0) /* nothing more to do */ 608 return (0); 609 610 flags = RF_ACTIVE; 611 msi_count = pci_msi_count(ppt->dev); 612 if (msi_count == 0) { 613 startrid = 0; /* legacy interrupt */ 614 msi_count = 1; 615 flags |= RF_SHAREABLE; 616 } else 617 startrid = 1; /* MSI */ 618 619 /* 620 * The device must be capable of supporting the number of vectors 621 * the guest wants to allocate. 622 */ 623 if (numvec > msi_count) 624 return (EINVAL); 625 626 /* 627 * Make sure that we can allocate all the MSI vectors that are needed 628 * by the guest. 629 */ 630 if (startrid == 1) { 631 tmp = numvec; 632 error = pci_alloc_msi(ppt->dev, &tmp); 633 if (error) 634 return (error); 635 else if (tmp != numvec) { 636 pci_release_msi(ppt->dev); 637 return (ENOSPC); 638 } else { 639 /* success */ 640 } 641 } 642 643 ppt->msi.startrid = startrid; 644 645 /* 646 * Allocate the irq resource and attach it to the interrupt handler. 647 */ 648 for (i = 0; i < numvec; i++) { 649 ppt->msi.num_msgs = i + 1; 650 ppt->msi.cookie[i] = NULL; 651 652 rid = startrid + i; 653 ppt->msi.res[i] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 654 &rid, flags); 655 if (ppt->msi.res[i] == NULL) 656 break; 657 658 ppt->msi.arg[i].pptdev = ppt; 659 ppt->msi.arg[i].addr = addr; 660 ppt->msi.arg[i].msg_data = msg + i; 661 662 error = bus_setup_intr(ppt->dev, ppt->msi.res[i], 663 INTR_TYPE_NET | INTR_MPSAFE, 664 pptintr, NULL, &ppt->msi.arg[i], 665 &ppt->msi.cookie[i]); 666 if (error != 0) 667 break; 668 } 669 670 if (i < numvec) { 671 ppt_teardown_msi(ppt); 672 return (ENXIO); 673 } 674 675 return (0); 676 } 677 678 int 679 ppt_setup_msix(struct vm *vm, int bus, int slot, int func, 680 int idx, uint64_t addr, uint64_t msg, uint32_t vector_control) 681 { 682 struct pptdev *ppt; 683 struct pci_devinfo *dinfo; 684 int numvec, alloced, rid, error; 685 size_t res_size, cookie_size, arg_size; 686 687 error = ppt_find(vm, bus, slot, func, &ppt); 688 if (error) 689 return (error); 690 691 /* Reject attempts to enable MSI-X while MSI is active. */ 692 if (ppt->msi.num_msgs != 0) 693 return (EBUSY); 694 695 dinfo = device_get_ivars(ppt->dev); 696 if (!dinfo) 697 return (ENXIO); 698 699 /* 700 * First-time configuration: 701 * Allocate the MSI-X table 702 * Allocate the IRQ resources 703 * Set up some variables in ppt->msix 704 */ 705 if (ppt->msix.num_msgs == 0) { 706 numvec = pci_msix_count(ppt->dev); 707 if (numvec <= 0) 708 return (EINVAL); 709 710 ppt->msix.startrid = 1; 711 ppt->msix.num_msgs = numvec; 712 713 res_size = numvec * sizeof(ppt->msix.res[0]); 714 cookie_size = numvec * sizeof(ppt->msix.cookie[0]); 715 arg_size = numvec * sizeof(ppt->msix.arg[0]); 716 717 ppt->msix.res = malloc(res_size, M_PPTMSIX, M_WAITOK | M_ZERO); 718 ppt->msix.cookie = malloc(cookie_size, M_PPTMSIX, 719 M_WAITOK | M_ZERO); 720 ppt->msix.arg = malloc(arg_size, M_PPTMSIX, M_WAITOK | M_ZERO); 721 722 rid = dinfo->cfg.msix.msix_table_bar; 723 ppt->msix.msix_table_res = bus_alloc_resource_any(ppt->dev, 724 SYS_RES_MEMORY, &rid, RF_ACTIVE); 725 726 if (ppt->msix.msix_table_res == NULL) { 727 ppt_teardown_msix(ppt); 728 return (ENOSPC); 729 } 730 ppt->msix.msix_table_rid = rid; 731 732 if (dinfo->cfg.msix.msix_table_bar != 733 dinfo->cfg.msix.msix_pba_bar) { 734 rid = dinfo->cfg.msix.msix_pba_bar; 735 ppt->msix.msix_pba_res = bus_alloc_resource_any( 736 ppt->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 737 738 if (ppt->msix.msix_pba_res == NULL) { 739 ppt_teardown_msix(ppt); 740 return (ENOSPC); 741 } 742 ppt->msix.msix_pba_rid = rid; 743 } 744 745 alloced = numvec; 746 error = pci_alloc_msix(ppt->dev, &alloced); 747 if (error || alloced != numvec) { 748 ppt_teardown_msix(ppt); 749 return (error == 0 ? ENOSPC: error); 750 } 751 } 752 753 if (idx >= ppt->msix.num_msgs) 754 return (EINVAL); 755 756 if ((vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 757 /* Tear down the IRQ if it's already set up */ 758 ppt_teardown_msix_intr(ppt, idx); 759 760 /* Allocate the IRQ resource */ 761 ppt->msix.cookie[idx] = NULL; 762 rid = ppt->msix.startrid + idx; 763 ppt->msix.res[idx] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 764 &rid, RF_ACTIVE); 765 if (ppt->msix.res[idx] == NULL) 766 return (ENXIO); 767 768 ppt->msix.arg[idx].pptdev = ppt; 769 ppt->msix.arg[idx].addr = addr; 770 ppt->msix.arg[idx].msg_data = msg; 771 772 /* Setup the MSI-X interrupt */ 773 error = bus_setup_intr(ppt->dev, ppt->msix.res[idx], 774 INTR_TYPE_NET | INTR_MPSAFE, 775 pptintr, NULL, &ppt->msix.arg[idx], 776 &ppt->msix.cookie[idx]); 777 778 if (error != 0) { 779 bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, ppt->msix.res[idx]); 780 ppt->msix.cookie[idx] = NULL; 781 ppt->msix.res[idx] = NULL; 782 return (ENXIO); 783 } 784 } else { 785 /* Masked, tear it down if it's already been set up */ 786 ppt_teardown_msix_intr(ppt, idx); 787 } 788 789 return (0); 790 } 791 792 int 793 ppt_disable_msix(struct vm *vm, int bus, int slot, int func) 794 { 795 struct pptdev *ppt; 796 int error; 797 798 error = ppt_find(vm, bus, slot, func, &ppt); 799 if (error) 800 return (error); 801 802 ppt_teardown_msix(ppt); 803 return (0); 804 } 805