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 45 #include <machine/vmm.h> 46 #include <machine/vmm_dev.h> 47 48 #include "vmm_lapic.h" 49 #include "vmm_ktr.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 155 ppt = device_get_softc(dev); 156 157 iommu_remove_device(iommu_host_domain(), pci_get_rid(dev)); 158 num_pptdevs++; 159 TAILQ_INSERT_TAIL(&pptdev_list, ppt, next); 160 ppt->dev = dev; 161 162 if (bootverbose) 163 device_printf(dev, "attached\n"); 164 165 return (0); 166 } 167 168 static int 169 ppt_detach(device_t dev) 170 { 171 struct pptdev *ppt; 172 173 ppt = device_get_softc(dev); 174 175 if (ppt->vm != NULL) 176 return (EBUSY); 177 num_pptdevs--; 178 TAILQ_REMOVE(&pptdev_list, ppt, next); 179 pci_disable_busmaster(dev); 180 181 if (iommu_host_domain() != NULL) 182 iommu_add_device(iommu_host_domain(), pci_get_rid(dev)); 183 184 return (0); 185 } 186 187 static device_method_t ppt_methods[] = { 188 /* Device interface */ 189 DEVMETHOD(device_probe, ppt_probe), 190 DEVMETHOD(device_attach, ppt_attach), 191 DEVMETHOD(device_detach, ppt_detach), 192 {0, 0} 193 }; 194 195 DEFINE_CLASS_0(ppt, ppt_driver, ppt_methods, sizeof(struct pptdev)); 196 DRIVER_MODULE(ppt, pci, ppt_driver, NULL, NULL); 197 198 static int 199 ppt_find(struct vm *vm, int bus, int slot, int func, struct pptdev **pptp) 200 { 201 device_t dev; 202 struct pptdev *ppt; 203 int b, s, f; 204 205 TAILQ_FOREACH(ppt, &pptdev_list, next) { 206 dev = ppt->dev; 207 b = pci_get_bus(dev); 208 s = pci_get_slot(dev); 209 f = pci_get_function(dev); 210 if (bus == b && slot == s && func == f) 211 break; 212 } 213 214 if (ppt == NULL) 215 return (ENOENT); 216 if (ppt->vm != vm) /* Make sure we own this device */ 217 return (EBUSY); 218 *pptp = ppt; 219 return (0); 220 } 221 222 static void 223 ppt_unmap_all_mmio(struct vm *vm, struct pptdev *ppt) 224 { 225 int i; 226 struct pptseg *seg; 227 228 for (i = 0; i < MAX_MMIOSEGS; i++) { 229 seg = &ppt->mmio[i]; 230 if (seg->len == 0) 231 continue; 232 (void)vm_unmap_mmio(vm, seg->gpa, seg->len); 233 bzero(seg, sizeof(struct pptseg)); 234 } 235 } 236 237 static void 238 ppt_teardown_msi(struct pptdev *ppt) 239 { 240 int i, rid; 241 void *cookie; 242 struct resource *res; 243 244 if (ppt->msi.num_msgs == 0) 245 return; 246 247 for (i = 0; i < ppt->msi.num_msgs; i++) { 248 rid = ppt->msi.startrid + i; 249 res = ppt->msi.res[i]; 250 cookie = ppt->msi.cookie[i]; 251 252 if (cookie != NULL) 253 bus_teardown_intr(ppt->dev, res, cookie); 254 255 if (res != NULL) 256 bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 257 258 ppt->msi.res[i] = NULL; 259 ppt->msi.cookie[i] = NULL; 260 } 261 262 if (ppt->msi.startrid == 1) 263 pci_release_msi(ppt->dev); 264 265 ppt->msi.num_msgs = 0; 266 } 267 268 static void 269 ppt_teardown_msix_intr(struct pptdev *ppt, int idx) 270 { 271 int rid; 272 struct resource *res; 273 void *cookie; 274 275 rid = ppt->msix.startrid + idx; 276 res = ppt->msix.res[idx]; 277 cookie = ppt->msix.cookie[idx]; 278 279 if (cookie != NULL) 280 bus_teardown_intr(ppt->dev, res, cookie); 281 282 if (res != NULL) 283 bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 284 285 ppt->msix.res[idx] = NULL; 286 ppt->msix.cookie[idx] = NULL; 287 } 288 289 static void 290 ppt_teardown_msix(struct pptdev *ppt) 291 { 292 int i; 293 294 if (ppt->msix.num_msgs == 0) 295 return; 296 297 for (i = 0; i < ppt->msix.num_msgs; i++) 298 ppt_teardown_msix_intr(ppt, i); 299 300 free(ppt->msix.res, M_PPTMSIX); 301 free(ppt->msix.cookie, M_PPTMSIX); 302 free(ppt->msix.arg, M_PPTMSIX); 303 304 pci_release_msi(ppt->dev); 305 306 if (ppt->msix.msix_table_res) { 307 bus_release_resource(ppt->dev, SYS_RES_MEMORY, 308 ppt->msix.msix_table_rid, 309 ppt->msix.msix_table_res); 310 ppt->msix.msix_table_res = NULL; 311 ppt->msix.msix_table_rid = 0; 312 } 313 if (ppt->msix.msix_pba_res) { 314 bus_release_resource(ppt->dev, SYS_RES_MEMORY, 315 ppt->msix.msix_pba_rid, 316 ppt->msix.msix_pba_res); 317 ppt->msix.msix_pba_res = NULL; 318 ppt->msix.msix_pba_rid = 0; 319 } 320 321 ppt->msix.num_msgs = 0; 322 } 323 324 int 325 ppt_avail_devices(void) 326 { 327 328 return (num_pptdevs); 329 } 330 331 int 332 ppt_assigned_devices(struct vm *vm) 333 { 334 struct pptdev *ppt; 335 int num; 336 337 num = 0; 338 TAILQ_FOREACH(ppt, &pptdev_list, next) { 339 if (ppt->vm == vm) 340 num++; 341 } 342 return (num); 343 } 344 345 bool 346 ppt_is_mmio(struct vm *vm, vm_paddr_t gpa) 347 { 348 int i; 349 struct pptdev *ppt; 350 struct pptseg *seg; 351 352 TAILQ_FOREACH(ppt, &pptdev_list, next) { 353 if (ppt->vm != vm) 354 continue; 355 356 for (i = 0; i < MAX_MMIOSEGS; i++) { 357 seg = &ppt->mmio[i]; 358 if (seg->len == 0) 359 continue; 360 if (gpa >= seg->gpa && gpa < seg->gpa + seg->len) 361 return (true); 362 } 363 } 364 365 return (false); 366 } 367 368 static void 369 ppt_pci_reset(device_t dev) 370 { 371 372 if (pcie_flr(dev, 373 max(pcie_get_max_completion_timeout(dev) / 1000, 10), true)) 374 return; 375 376 pci_power_reset(dev); 377 } 378 379 int 380 ppt_assign_device(struct vm *vm, int bus, int slot, int func) 381 { 382 struct pptdev *ppt; 383 int error; 384 385 /* Passing NULL requires the device to be unowned. */ 386 error = ppt_find(NULL, bus, slot, func, &ppt); 387 if (error) 388 return (error); 389 390 pci_save_state(ppt->dev); 391 ppt_pci_reset(ppt->dev); 392 pci_restore_state(ppt->dev); 393 ppt->vm = vm; 394 iommu_add_device(vm_iommu_domain(vm), pci_get_rid(ppt->dev)); 395 return (0); 396 } 397 398 int 399 ppt_unassign_device(struct vm *vm, int bus, int slot, int func) 400 { 401 struct pptdev *ppt; 402 int error; 403 404 error = ppt_find(vm, bus, slot, func, &ppt); 405 if (error) 406 return (error); 407 408 pci_save_state(ppt->dev); 409 ppt_pci_reset(ppt->dev); 410 pci_restore_state(ppt->dev); 411 ppt_unmap_all_mmio(vm, ppt); 412 ppt_teardown_msi(ppt); 413 ppt_teardown_msix(ppt); 414 iommu_remove_device(vm_iommu_domain(vm), pci_get_rid(ppt->dev)); 415 ppt->vm = NULL; 416 return (0); 417 } 418 419 int 420 ppt_unassign_all(struct vm *vm) 421 { 422 struct pptdev *ppt; 423 int bus, slot, func; 424 device_t dev; 425 426 TAILQ_FOREACH(ppt, &pptdev_list, next) { 427 if (ppt->vm == vm) { 428 dev = ppt->dev; 429 bus = pci_get_bus(dev); 430 slot = pci_get_slot(dev); 431 func = pci_get_function(dev); 432 vm_unassign_pptdev(vm, bus, slot, func); 433 } 434 } 435 436 return (0); 437 } 438 439 static bool 440 ppt_valid_bar_mapping(struct pptdev *ppt, vm_paddr_t hpa, size_t len) 441 { 442 struct pci_map *pm; 443 pci_addr_t base, size; 444 445 for (pm = pci_first_bar(ppt->dev); pm != NULL; pm = pci_next_bar(pm)) { 446 if (!PCI_BAR_MEM(pm->pm_value)) 447 continue; 448 base = pm->pm_value & PCIM_BAR_MEM_BASE; 449 size = (pci_addr_t)1 << pm->pm_size; 450 if (hpa >= base && hpa + len <= base + size) 451 return (true); 452 } 453 return (false); 454 } 455 456 int 457 ppt_map_mmio(struct vm *vm, int bus, int slot, int func, 458 vm_paddr_t gpa, size_t len, vm_paddr_t hpa) 459 { 460 int i, error; 461 struct pptseg *seg; 462 struct pptdev *ppt; 463 464 if (len % PAGE_SIZE != 0 || len == 0 || gpa % PAGE_SIZE != 0 || 465 hpa % PAGE_SIZE != 0 || gpa + len < gpa || hpa + len < hpa) 466 return (EINVAL); 467 468 error = ppt_find(vm, bus, slot, func, &ppt); 469 if (error) 470 return (error); 471 472 if (!ppt_valid_bar_mapping(ppt, hpa, len)) 473 return (EINVAL); 474 475 for (i = 0; i < MAX_MMIOSEGS; i++) { 476 seg = &ppt->mmio[i]; 477 if (seg->len == 0) { 478 error = vm_map_mmio(vm, gpa, len, hpa); 479 if (error == 0) { 480 seg->gpa = gpa; 481 seg->len = len; 482 } 483 return (error); 484 } 485 } 486 return (ENOSPC); 487 } 488 489 int 490 ppt_unmap_mmio(struct vm *vm, int bus, int slot, int func, 491 vm_paddr_t gpa, size_t len) 492 { 493 int i, error; 494 struct pptseg *seg; 495 struct pptdev *ppt; 496 497 error = ppt_find(vm, bus, slot, func, &ppt); 498 if (error) 499 return (error); 500 501 for (i = 0; i < MAX_MMIOSEGS; i++) { 502 seg = &ppt->mmio[i]; 503 if (seg->gpa == gpa && seg->len == len) { 504 error = vm_unmap_mmio(vm, seg->gpa, seg->len); 505 if (error == 0) { 506 seg->gpa = 0; 507 seg->len = 0; 508 } 509 return (error); 510 } 511 } 512 return (ENOENT); 513 } 514 515 static int 516 pptintr(void *arg) 517 { 518 struct pptdev *ppt; 519 struct pptintr_arg *pptarg; 520 521 pptarg = arg; 522 ppt = pptarg->pptdev; 523 524 if (ppt->vm != NULL) 525 lapic_intr_msi(ppt->vm, pptarg->addr, pptarg->msg_data); 526 else { 527 /* 528 * XXX 529 * This is not expected to happen - panic? 530 */ 531 } 532 533 /* 534 * For legacy interrupts give other filters a chance in case 535 * the interrupt was not generated by the passthrough device. 536 */ 537 if (ppt->msi.startrid == 0) 538 return (FILTER_STRAY); 539 else 540 return (FILTER_HANDLED); 541 } 542 543 int 544 ppt_setup_msi(struct vm *vm, int bus, int slot, int func, 545 uint64_t addr, uint64_t msg, int numvec) 546 { 547 int i, rid, flags; 548 int msi_count, startrid, error, tmp; 549 struct pptdev *ppt; 550 551 if (numvec < 0 || numvec > MAX_MSIMSGS) 552 return (EINVAL); 553 554 error = ppt_find(vm, bus, slot, func, &ppt); 555 if (error) 556 return (error); 557 558 /* Reject attempts to enable MSI while MSI-X is active. */ 559 if (ppt->msix.num_msgs != 0 && numvec != 0) 560 return (EBUSY); 561 562 /* Free any allocated resources */ 563 ppt_teardown_msi(ppt); 564 565 if (numvec == 0) /* nothing more to do */ 566 return (0); 567 568 flags = RF_ACTIVE; 569 msi_count = pci_msi_count(ppt->dev); 570 if (msi_count == 0) { 571 startrid = 0; /* legacy interrupt */ 572 msi_count = 1; 573 flags |= RF_SHAREABLE; 574 } else 575 startrid = 1; /* MSI */ 576 577 /* 578 * The device must be capable of supporting the number of vectors 579 * the guest wants to allocate. 580 */ 581 if (numvec > msi_count) 582 return (EINVAL); 583 584 /* 585 * Make sure that we can allocate all the MSI vectors that are needed 586 * by the guest. 587 */ 588 if (startrid == 1) { 589 tmp = numvec; 590 error = pci_alloc_msi(ppt->dev, &tmp); 591 if (error) 592 return (error); 593 else if (tmp != numvec) { 594 pci_release_msi(ppt->dev); 595 return (ENOSPC); 596 } else { 597 /* success */ 598 } 599 } 600 601 ppt->msi.startrid = startrid; 602 603 /* 604 * Allocate the irq resource and attach it to the interrupt handler. 605 */ 606 for (i = 0; i < numvec; i++) { 607 ppt->msi.num_msgs = i + 1; 608 ppt->msi.cookie[i] = NULL; 609 610 rid = startrid + i; 611 ppt->msi.res[i] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 612 &rid, flags); 613 if (ppt->msi.res[i] == NULL) 614 break; 615 616 ppt->msi.arg[i].pptdev = ppt; 617 ppt->msi.arg[i].addr = addr; 618 ppt->msi.arg[i].msg_data = msg + i; 619 620 error = bus_setup_intr(ppt->dev, ppt->msi.res[i], 621 INTR_TYPE_NET | INTR_MPSAFE, 622 pptintr, NULL, &ppt->msi.arg[i], 623 &ppt->msi.cookie[i]); 624 if (error != 0) 625 break; 626 } 627 628 if (i < numvec) { 629 ppt_teardown_msi(ppt); 630 return (ENXIO); 631 } 632 633 return (0); 634 } 635 636 int 637 ppt_setup_msix(struct vm *vm, int bus, int slot, int func, 638 int idx, uint64_t addr, uint64_t msg, uint32_t vector_control) 639 { 640 struct pptdev *ppt; 641 struct pci_devinfo *dinfo; 642 int numvec, alloced, rid, error; 643 size_t res_size, cookie_size, arg_size; 644 645 error = ppt_find(vm, bus, slot, func, &ppt); 646 if (error) 647 return (error); 648 649 /* Reject attempts to enable MSI-X while MSI is active. */ 650 if (ppt->msi.num_msgs != 0) 651 return (EBUSY); 652 653 dinfo = device_get_ivars(ppt->dev); 654 if (!dinfo) 655 return (ENXIO); 656 657 /* 658 * First-time configuration: 659 * Allocate the MSI-X table 660 * Allocate the IRQ resources 661 * Set up some variables in ppt->msix 662 */ 663 if (ppt->msix.num_msgs == 0) { 664 numvec = pci_msix_count(ppt->dev); 665 if (numvec <= 0) 666 return (EINVAL); 667 668 ppt->msix.startrid = 1; 669 ppt->msix.num_msgs = numvec; 670 671 res_size = numvec * sizeof(ppt->msix.res[0]); 672 cookie_size = numvec * sizeof(ppt->msix.cookie[0]); 673 arg_size = numvec * sizeof(ppt->msix.arg[0]); 674 675 ppt->msix.res = malloc(res_size, M_PPTMSIX, M_WAITOK | M_ZERO); 676 ppt->msix.cookie = malloc(cookie_size, M_PPTMSIX, 677 M_WAITOK | M_ZERO); 678 ppt->msix.arg = malloc(arg_size, M_PPTMSIX, M_WAITOK | M_ZERO); 679 680 rid = dinfo->cfg.msix.msix_table_bar; 681 ppt->msix.msix_table_res = bus_alloc_resource_any(ppt->dev, 682 SYS_RES_MEMORY, &rid, RF_ACTIVE); 683 684 if (ppt->msix.msix_table_res == NULL) { 685 ppt_teardown_msix(ppt); 686 return (ENOSPC); 687 } 688 ppt->msix.msix_table_rid = rid; 689 690 if (dinfo->cfg.msix.msix_table_bar != 691 dinfo->cfg.msix.msix_pba_bar) { 692 rid = dinfo->cfg.msix.msix_pba_bar; 693 ppt->msix.msix_pba_res = bus_alloc_resource_any( 694 ppt->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 695 696 if (ppt->msix.msix_pba_res == NULL) { 697 ppt_teardown_msix(ppt); 698 return (ENOSPC); 699 } 700 ppt->msix.msix_pba_rid = rid; 701 } 702 703 alloced = numvec; 704 error = pci_alloc_msix(ppt->dev, &alloced); 705 if (error || alloced != numvec) { 706 ppt_teardown_msix(ppt); 707 return (error == 0 ? ENOSPC: error); 708 } 709 } 710 711 if ((vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 712 /* Tear down the IRQ if it's already set up */ 713 ppt_teardown_msix_intr(ppt, idx); 714 715 /* Allocate the IRQ resource */ 716 ppt->msix.cookie[idx] = NULL; 717 rid = ppt->msix.startrid + idx; 718 ppt->msix.res[idx] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 719 &rid, RF_ACTIVE); 720 if (ppt->msix.res[idx] == NULL) 721 return (ENXIO); 722 723 ppt->msix.arg[idx].pptdev = ppt; 724 ppt->msix.arg[idx].addr = addr; 725 ppt->msix.arg[idx].msg_data = msg; 726 727 /* Setup the MSI-X interrupt */ 728 error = bus_setup_intr(ppt->dev, ppt->msix.res[idx], 729 INTR_TYPE_NET | INTR_MPSAFE, 730 pptintr, NULL, &ppt->msix.arg[idx], 731 &ppt->msix.cookie[idx]); 732 733 if (error != 0) { 734 bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, ppt->msix.res[idx]); 735 ppt->msix.cookie[idx] = NULL; 736 ppt->msix.res[idx] = NULL; 737 return (ENXIO); 738 } 739 } else { 740 /* Masked, tear it down if it's already been set up */ 741 ppt_teardown_msix_intr(ppt, idx); 742 } 743 744 return (0); 745 } 746 747 int 748 ppt_disable_msix(struct vm *vm, int bus, int slot, int func) 749 { 750 struct pptdev *ppt; 751 int error; 752 753 error = ppt_find(vm, bus, slot, func, &ppt); 754 if (error) 755 return (error); 756 757 ppt_teardown_msix(ppt); 758 return (0); 759 } 760