ppt.c revision 234761
1210312Sjmallett/*- 2210312Sjmallett * Copyright (c) 2011 NetApp, Inc. 3210312Sjmallett * All rights reserved. 4210312Sjmallett * 5210312Sjmallett * Redistribution and use in source and binary forms, with or without 6210312Sjmallett * modification, are permitted provided that the following conditions 7210312Sjmallett * are met: 8210312Sjmallett * 1. Redistributions of source code must retain the above copyright 9210312Sjmallett * notice, this list of conditions and the following disclaimer. 10210312Sjmallett * 2. Redistributions in binary form must reproduce the above copyright 11210312Sjmallett * notice, this list of conditions and the following disclaimer in the 12210312Sjmallett * documentation and/or other materials provided with the distribution. 13210312Sjmallett * 14210312Sjmallett * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15210312Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16210312Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17210312Sjmallett * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18210312Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19210312Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20210312Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21210312Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22210312Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23210312Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24210312Sjmallett * SUCH DAMAGE. 25210312Sjmallett * 26210312Sjmallett * $FreeBSD$ 27210312Sjmallett */ 28210312Sjmallett 29210312Sjmallett#include <sys/cdefs.h> 30210312Sjmallett__FBSDID("$FreeBSD$"); 31210312Sjmallett 32210312Sjmallett#include <sys/param.h> 33210312Sjmallett#include <sys/systm.h> 34210312Sjmallett#include <sys/kernel.h> 35210312Sjmallett#include <sys/malloc.h> 36210312Sjmallett#include <sys/module.h> 37210312Sjmallett#include <sys/bus.h> 38210312Sjmallett#include <sys/pciio.h> 39210312Sjmallett#include <sys/rman.h> 40210312Sjmallett#include <sys/smp.h> 41210312Sjmallett 42210312Sjmallett#include <dev/pci/pcivar.h> 43210312Sjmallett#include <dev/pci/pcireg.h> 44210312Sjmallett 45210312Sjmallett#include <machine/resource.h> 46210312Sjmallett 47210312Sjmallett#include <machine/vmm.h> 48210312Sjmallett#include <machine/vmm_dev.h> 49210312Sjmallett 50210312Sjmallett#include "vmm_lapic.h" 51210312Sjmallett#include "vmm_ktr.h" 52210312Sjmallett 53210312Sjmallett#include "iommu.h" 54210312Sjmallett#include "ppt.h" 55210312Sjmallett 56210312Sjmallett#define MAX_PPTDEVS (sizeof(pptdevs) / sizeof(pptdevs[0])) 57210312Sjmallett#define MAX_MMIOSEGS (PCIR_MAX_BAR_0 + 1) 58210312Sjmallett#define MAX_MSIMSGS 32 59210312Sjmallett 60210312SjmallettMALLOC_DEFINE(M_PPTMSIX, "pptmsix", "Passthru MSI-X resources"); 61210312Sjmallett 62210312Sjmallettstruct pptintr_arg { /* pptintr(pptintr_arg) */ 63210312Sjmallett struct pptdev *pptdev; 64210312Sjmallett int vec; 65210312Sjmallett int vcpu; 66210312Sjmallett}; 67210312Sjmallett 68210312Sjmallettstatic struct pptdev { 69210312Sjmallett device_t dev; 70210312Sjmallett struct vm *vm; /* owner of this device */ 71210312Sjmallett struct vm_memory_segment mmio[MAX_MMIOSEGS]; 72210312Sjmallett struct { 73210312Sjmallett int num_msgs; /* guest state */ 74210312Sjmallett int vector; 75210312Sjmallett int vcpu; 76210312Sjmallett 77210312Sjmallett int startrid; /* host state */ 78210312Sjmallett struct resource *res[MAX_MSIMSGS]; 79210312Sjmallett void *cookie[MAX_MSIMSGS]; 80210312Sjmallett struct pptintr_arg arg[MAX_MSIMSGS]; 81210312Sjmallett } msi; 82210312Sjmallett 83210312Sjmallett struct { 84210312Sjmallett int num_msgs; 85210312Sjmallett int startrid; 86210312Sjmallett int msix_table_rid; 87210312Sjmallett struct resource *msix_table_res; 88210312Sjmallett struct resource **res; 89210312Sjmallett void **cookie; 90210312Sjmallett struct pptintr_arg *arg; 91210312Sjmallett } msix; 92210312Sjmallett} pptdevs[32]; 93210312Sjmallett 94210312Sjmallettstatic int num_pptdevs; 95210312Sjmallett 96210312Sjmallettstatic int 97210312Sjmallettppt_probe(device_t dev) 98210312Sjmallett{ 99210312Sjmallett int bus, slot, func; 100210312Sjmallett struct pci_devinfo *dinfo; 101210312Sjmallett 102210312Sjmallett dinfo = (struct pci_devinfo *)device_get_ivars(dev); 103210312Sjmallett 104210312Sjmallett bus = pci_get_bus(dev); 105210312Sjmallett slot = pci_get_slot(dev); 106210312Sjmallett func = pci_get_function(dev); 107210312Sjmallett 108210312Sjmallett /* 109210312Sjmallett * To qualify as a pci passthrough device a device must: 110210312Sjmallett * - be allowed by administrator to be used in this role 111210312Sjmallett * - be an endpoint device 112210312Sjmallett */ 113210312Sjmallett if (vmm_is_pptdev(bus, slot, func) && 114210312Sjmallett (dinfo->cfg.hdrtype & PCIM_HDRTYPE) == PCIM_HDRTYPE_NORMAL) 115210312Sjmallett return (0); 116210312Sjmallett else 117210312Sjmallett return (ENXIO); 118210312Sjmallett} 119210312Sjmallett 120210312Sjmallettstatic int 121210312Sjmallettppt_attach(device_t dev) 122210312Sjmallett{ 123210312Sjmallett int n; 124210312Sjmallett 125210312Sjmallett if (num_pptdevs >= MAX_PPTDEVS) { 126210312Sjmallett printf("ppt_attach: maximum number of pci passthrough devices " 127210312Sjmallett "exceeded\n"); 128210312Sjmallett return (ENXIO); 129210312Sjmallett } 130210312Sjmallett 131210312Sjmallett n = num_pptdevs++; 132210312Sjmallett pptdevs[n].dev = dev; 133210312Sjmallett 134210312Sjmallett if (bootverbose) 135210312Sjmallett device_printf(dev, "attached\n"); 136210312Sjmallett 137210312Sjmallett return (0); 138210312Sjmallett} 139210312Sjmallett 140210312Sjmallettstatic int 141210312Sjmallettppt_detach(device_t dev) 142210312Sjmallett{ 143210312Sjmallett /* 144210312Sjmallett * XXX check whether there are any pci passthrough devices assigned 145210312Sjmallett * to guests before we allow this driver to detach. 146210312Sjmallett */ 147210312Sjmallett 148210312Sjmallett return (0); 149210312Sjmallett} 150210312Sjmallett 151210312Sjmallettstatic device_method_t ppt_methods[] = { 152210312Sjmallett /* Device interface */ 153210312Sjmallett DEVMETHOD(device_probe, ppt_probe), 154210312Sjmallett DEVMETHOD(device_attach, ppt_attach), 155210312Sjmallett DEVMETHOD(device_detach, ppt_detach), 156210312Sjmallett {0, 0} 157210312Sjmallett}; 158210312Sjmallett 159210312Sjmallettstatic devclass_t ppt_devclass; 160210312SjmallettDEFINE_CLASS_0(ppt, ppt_driver, ppt_methods, 0); 161210312SjmallettDRIVER_MODULE(ppt, pci, ppt_driver, ppt_devclass, NULL, NULL); 162210312Sjmallett 163210312Sjmallettstatic struct pptdev * 164210312Sjmallettppt_find(int bus, int slot, int func) 165210312Sjmallett{ 166210312Sjmallett device_t dev; 167210312Sjmallett int i, b, s, f; 168210312Sjmallett 169210312Sjmallett for (i = 0; i < num_pptdevs; i++) { 170210312Sjmallett dev = pptdevs[i].dev; 171210312Sjmallett b = pci_get_bus(dev); 172210312Sjmallett s = pci_get_slot(dev); 173210312Sjmallett f = pci_get_function(dev); 174210312Sjmallett if (bus == b && slot == s && func == f) 175210312Sjmallett return (&pptdevs[i]); 176210312Sjmallett } 177210312Sjmallett return (NULL); 178210312Sjmallett} 179210312Sjmallett 180210312Sjmallettstatic void 181210312Sjmallettppt_unmap_mmio(struct vm *vm, struct pptdev *ppt) 182210312Sjmallett{ 183210312Sjmallett int i; 184210312Sjmallett struct vm_memory_segment *seg; 185210312Sjmallett 186210312Sjmallett for (i = 0; i < MAX_MMIOSEGS; i++) { 187210312Sjmallett seg = &ppt->mmio[i]; 188210312Sjmallett if (seg->len == 0) 189210312Sjmallett continue; 190210312Sjmallett (void)vm_unmap_mmio(vm, seg->gpa, seg->len); 191210312Sjmallett bzero(seg, sizeof(struct vm_memory_segment)); 192210312Sjmallett } 193210312Sjmallett} 194210312Sjmallett 195210312Sjmallettstatic void 196210312Sjmallettppt_teardown_msi(struct pptdev *ppt) 197210312Sjmallett{ 198210312Sjmallett int i, rid; 199210312Sjmallett void *cookie; 200210312Sjmallett struct resource *res; 201210312Sjmallett 202210312Sjmallett if (ppt->msi.num_msgs == 0) 203210312Sjmallett return; 204210312Sjmallett 205210312Sjmallett for (i = 0; i < ppt->msi.num_msgs; i++) { 206210312Sjmallett rid = ppt->msi.startrid + i; 207210312Sjmallett res = ppt->msi.res[i]; 208210312Sjmallett cookie = ppt->msi.cookie[i]; 209210312Sjmallett 210210312Sjmallett if (cookie != NULL) 211210312Sjmallett bus_teardown_intr(ppt->dev, res, cookie); 212210312Sjmallett 213210312Sjmallett if (res != NULL) 214210312Sjmallett bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 215210312Sjmallett 216210312Sjmallett ppt->msi.res[i] = NULL; 217210312Sjmallett ppt->msi.cookie[i] = NULL; 218210312Sjmallett } 219210312Sjmallett 220210312Sjmallett if (ppt->msi.startrid == 1) 221210312Sjmallett pci_release_msi(ppt->dev); 222210312Sjmallett 223210312Sjmallett ppt->msi.num_msgs = 0; 224210312Sjmallett} 225210312Sjmallett 226210312Sjmallettstatic void 227210312Sjmallettppt_teardown_msix_intr(struct pptdev *ppt, int idx) 228210312Sjmallett{ 229210312Sjmallett int rid; 230210312Sjmallett struct resource *res; 231210312Sjmallett void *cookie; 232210312Sjmallett 233210312Sjmallett rid = ppt->msix.startrid + idx; 234210312Sjmallett res = ppt->msix.res[idx]; 235210312Sjmallett cookie = ppt->msix.cookie[idx]; 236210312Sjmallett 237210312Sjmallett if (cookie != NULL) 238210312Sjmallett bus_teardown_intr(ppt->dev, res, cookie); 239210312Sjmallett 240210312Sjmallett if (res != NULL) 241210312Sjmallett bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 242210312Sjmallett 243210312Sjmallett ppt->msix.res[idx] = NULL; 244210312Sjmallett ppt->msix.cookie[idx] = NULL; 245210312Sjmallett} 246210312Sjmallett 247210312Sjmallettstatic void 248210312Sjmallettppt_teardown_msix(struct pptdev *ppt) 249210312Sjmallett{ 250210312Sjmallett int i, error; 251210312Sjmallett 252210312Sjmallett if (ppt->msix.num_msgs == 0) 253210312Sjmallett return; 254210312Sjmallett 255210312Sjmallett for (i = 0; i < ppt->msix.num_msgs; i++) 256210312Sjmallett ppt_teardown_msix_intr(ppt, i); 257210312Sjmallett 258210312Sjmallett if (ppt->msix.msix_table_res) { 259210312Sjmallett bus_release_resource(ppt->dev, SYS_RES_MEMORY, 260210312Sjmallett ppt->msix.msix_table_rid, 261210312Sjmallett ppt->msix.msix_table_res); 262210312Sjmallett ppt->msix.msix_table_res = NULL; 263210312Sjmallett ppt->msix.msix_table_rid = 0; 264210312Sjmallett } 265210312Sjmallett 266210312Sjmallett free(ppt->msix.res, M_PPTMSIX); 267210312Sjmallett free(ppt->msix.cookie, M_PPTMSIX); 268210312Sjmallett free(ppt->msix.arg, M_PPTMSIX); 269210312Sjmallett 270210312Sjmallett error = pci_release_msi(ppt->dev); 271210312Sjmallett if (error) 272210312Sjmallett printf("ppt_teardown_msix: Failed to release MSI-X resources (error %i)\n", error); 273210312Sjmallett 274210312Sjmallett ppt->msix.num_msgs = 0; 275210312Sjmallett} 276210312Sjmallett 277210312Sjmallettint 278210312Sjmallettppt_assign_device(struct vm *vm, int bus, int slot, int func) 279210312Sjmallett{ 280210312Sjmallett struct pptdev *ppt; 281210312Sjmallett 282210312Sjmallett ppt = ppt_find(bus, slot, func); 283210312Sjmallett if (ppt != NULL) { 284210312Sjmallett /* 285210312Sjmallett * If this device is owned by a different VM then we 286210312Sjmallett * cannot change its owner. 287210312Sjmallett */ 288210312Sjmallett if (ppt->vm != NULL && ppt->vm != vm) 289210312Sjmallett return (EBUSY); 290210312Sjmallett 291210312Sjmallett ppt->vm = vm; 292210312Sjmallett iommu_add_device(vm_iommu_domain(vm), bus, slot, func); 293210312Sjmallett return (0); 294210312Sjmallett } 295210312Sjmallett return (ENOENT); 296210312Sjmallett} 297210312Sjmallett 298210312Sjmallettint 299210312Sjmallettppt_unassign_device(struct vm *vm, int bus, int slot, int func) 300210312Sjmallett{ 301210312Sjmallett struct pptdev *ppt; 302210312Sjmallett 303210312Sjmallett ppt = ppt_find(bus, slot, func); 304210312Sjmallett if (ppt != NULL) { 305210312Sjmallett /* 306210312Sjmallett * If this device is not owned by this 'vm' then bail out. 307210312Sjmallett */ 308210312Sjmallett if (ppt->vm != vm) 309210312Sjmallett return (EBUSY); 310210312Sjmallett ppt_unmap_mmio(vm, ppt); 311210312Sjmallett ppt_teardown_msi(ppt); 312210312Sjmallett ppt_teardown_msix(ppt); 313210312Sjmallett iommu_remove_device(vm_iommu_domain(vm), bus, slot, func); 314210312Sjmallett ppt->vm = NULL; 315210312Sjmallett return (0); 316210312Sjmallett } 317210312Sjmallett return (ENOENT); 318210312Sjmallett} 319210312Sjmallett 320210312Sjmallettint 321210312Sjmallettppt_unassign_all(struct vm *vm) 322210312Sjmallett{ 323210312Sjmallett int i, bus, slot, func; 324210312Sjmallett device_t dev; 325210312Sjmallett 326210312Sjmallett for (i = 0; i < num_pptdevs; i++) { 327210312Sjmallett if (pptdevs[i].vm == vm) { 328210312Sjmallett dev = pptdevs[i].dev; 329210312Sjmallett bus = pci_get_bus(dev); 330210312Sjmallett slot = pci_get_slot(dev); 331210312Sjmallett func = pci_get_function(dev); 332210312Sjmallett ppt_unassign_device(vm, bus, slot, func); 333210312Sjmallett } 334210312Sjmallett } 335210312Sjmallett 336210312Sjmallett return (0); 337210312Sjmallett} 338217620Sgonzo 339210312Sjmallettint 340210312Sjmallettppt_map_mmio(struct vm *vm, int bus, int slot, int func, 341210312Sjmallett vm_paddr_t gpa, size_t len, vm_paddr_t hpa) 342210312Sjmallett{ 343210312Sjmallett int i, error; 344210312Sjmallett struct vm_memory_segment *seg; 345210312Sjmallett struct pptdev *ppt; 346210312Sjmallett 347210312Sjmallett ppt = ppt_find(bus, slot, func); 348210312Sjmallett if (ppt != NULL) { 349210312Sjmallett if (ppt->vm != vm) 350210312Sjmallett return (EBUSY); 351210312Sjmallett 352210312Sjmallett for (i = 0; i < MAX_MMIOSEGS; i++) { 353210312Sjmallett seg = &ppt->mmio[i]; 354210312Sjmallett if (seg->len == 0) { 355210312Sjmallett error = vm_map_mmio(vm, gpa, len, hpa); 356210312Sjmallett if (error == 0) { 357210312Sjmallett seg->gpa = gpa; 358210312Sjmallett seg->len = len; 359210312Sjmallett seg->hpa = hpa; 360210312Sjmallett } 361210312Sjmallett return (error); 362210312Sjmallett } 363210312Sjmallett } 364210312Sjmallett return (ENOSPC); 365210312Sjmallett } 366210312Sjmallett return (ENOENT); 367210312Sjmallett} 368210312Sjmallett 369210312Sjmallettstatic int 370210312Sjmallettpptintr(void *arg) 371210312Sjmallett{ 372210312Sjmallett int vec; 373210312Sjmallett struct pptdev *ppt; 374210312Sjmallett struct pptintr_arg *pptarg; 375210312Sjmallett 376210312Sjmallett pptarg = arg; 377210312Sjmallett ppt = pptarg->pptdev; 378210312Sjmallett vec = pptarg->vec; 379210312Sjmallett 380210312Sjmallett if (ppt->vm != NULL) 381210312Sjmallett (void) lapic_set_intr(ppt->vm, pptarg->vcpu, vec); 382210312Sjmallett else { 383210312Sjmallett /* 384210312Sjmallett * XXX 385210312Sjmallett * This is not expected to happen - panic? 386210312Sjmallett */ 387210312Sjmallett } 388210312Sjmallett 389210312Sjmallett /* 390210312Sjmallett * For legacy interrupts give other filters a chance in case 391210312Sjmallett * the interrupt was not generated by the passthrough device. 392210312Sjmallett */ 393210312Sjmallett if (ppt->msi.startrid == 0) 394210312Sjmallett return (FILTER_STRAY); 395210312Sjmallett else 396210312Sjmallett return (FILTER_HANDLED); 397217620Sgonzo} 398210312Sjmallett 399210312Sjmallett/* 400210312Sjmallett * XXX 401210312Sjmallett * When we try to free the MSI resource the kernel will bind the thread to 402210312Sjmallett * the host cpu was originally handling the MSI. The function freeing the 403210312Sjmallett * MSI vector (apic_free_vector()) will panic the kernel if the thread 404210312Sjmallett * is already bound to a cpu. 405210312Sjmallett * 406210312Sjmallett * So, we temporarily unbind the vcpu thread before freeing the MSI resource. 407210312Sjmallett */ 408210312Sjmallettstatic void 409210312SjmallettPPT_TEARDOWN_MSI(struct vm *vm, int vcpu, struct pptdev *ppt) 410210312Sjmallett{ 411210312Sjmallett int pincpu = -1; 412210312Sjmallett 413210312Sjmallett vm_get_pinning(vm, vcpu, &pincpu); 414210312Sjmallett 415210312Sjmallett if (pincpu >= 0) 416210312Sjmallett vm_set_pinning(vm, vcpu, -1); 417210312Sjmallett 418210312Sjmallett ppt_teardown_msi(ppt); 419210312Sjmallett 420210312Sjmallett if (pincpu >= 0) 421210312Sjmallett vm_set_pinning(vm, vcpu, pincpu); 422210312Sjmallett} 423210312Sjmallett 424210312Sjmallettint 425210312Sjmallettppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func, 426210312Sjmallett int destcpu, int vector, int numvec) 427210312Sjmallett{ 428210312Sjmallett int i, rid, flags; 429210312Sjmallett int msi_count, startrid, error, tmp; 430210312Sjmallett struct pptdev *ppt; 431210312Sjmallett 432210312Sjmallett if ((destcpu >= VM_MAXCPU || destcpu < 0) || 433210312Sjmallett (vector < 0 || vector > 255) || 434210312Sjmallett (numvec < 0 || numvec > MAX_MSIMSGS)) 435210312Sjmallett return (EINVAL); 436210312Sjmallett 437210312Sjmallett ppt = ppt_find(bus, slot, func); 438210312Sjmallett if (ppt == NULL) 439210312Sjmallett return (ENOENT); 440210312Sjmallett if (ppt->vm != vm) /* Make sure we own this device */ 441210312Sjmallett return (EBUSY); 442210312Sjmallett 443210312Sjmallett /* Free any allocated resources */ 444210312Sjmallett PPT_TEARDOWN_MSI(vm, vcpu, ppt); 445210312Sjmallett 446210312Sjmallett if (numvec == 0) /* nothing more to do */ 447210312Sjmallett return (0); 448210312Sjmallett 449210312Sjmallett flags = RF_ACTIVE; 450210312Sjmallett msi_count = pci_msi_count(ppt->dev); 451210312Sjmallett if (msi_count == 0) { 452210312Sjmallett startrid = 0; /* legacy interrupt */ 453210312Sjmallett msi_count = 1; 454210312Sjmallett flags |= RF_SHAREABLE; 455210312Sjmallett } else 456210312Sjmallett startrid = 1; /* MSI */ 457217620Sgonzo 458210312Sjmallett /* 459210312Sjmallett * The device must be capable of supporting the number of vectors 460210312Sjmallett * the guest wants to allocate. 461210312Sjmallett */ 462210312Sjmallett if (numvec > msi_count) 463210312Sjmallett return (EINVAL); 464210312Sjmallett 465210312Sjmallett /* 466210312Sjmallett * Make sure that we can allocate all the MSI vectors that are needed 467210312Sjmallett * by the guest. 468210312Sjmallett */ 469210312Sjmallett if (startrid == 1) { 470210312Sjmallett tmp = numvec; 471210312Sjmallett error = pci_alloc_msi(ppt->dev, &tmp); 472210312Sjmallett if (error) 473210312Sjmallett return (error); 474210312Sjmallett else if (tmp != numvec) { 475210312Sjmallett pci_release_msi(ppt->dev); 476210312Sjmallett return (ENOSPC); 477210312Sjmallett } else { 478210312Sjmallett /* success */ 479210312Sjmallett } 480210312Sjmallett } 481210312Sjmallett 482210312Sjmallett ppt->msi.vector = vector; 483210312Sjmallett ppt->msi.vcpu = destcpu; 484210312Sjmallett ppt->msi.startrid = startrid; 485210312Sjmallett 486210312Sjmallett /* 487210312Sjmallett * Allocate the irq resource and attach it to the interrupt handler. 488210312Sjmallett */ 489210312Sjmallett for (i = 0; i < numvec; i++) { 490210312Sjmallett ppt->msi.num_msgs = i + 1; 491210312Sjmallett ppt->msi.cookie[i] = NULL; 492210312Sjmallett 493210312Sjmallett rid = startrid + i; 494210312Sjmallett ppt->msi.res[i] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 495210312Sjmallett &rid, flags); 496210312Sjmallett if (ppt->msi.res[i] == NULL) 497210312Sjmallett break; 498210312Sjmallett 499210312Sjmallett ppt->msi.arg[i].pptdev = ppt; 500210312Sjmallett ppt->msi.arg[i].vec = vector + i; 501210312Sjmallett 502210312Sjmallett error = bus_setup_intr(ppt->dev, ppt->msi.res[i], 503210312Sjmallett INTR_TYPE_NET | INTR_MPSAFE, 504210312Sjmallett pptintr, NULL, &ppt->msi.arg[i], 505210312Sjmallett &ppt->msi.cookie[i]); 506210312Sjmallett if (error != 0) 507210312Sjmallett break; 508210312Sjmallett } 509210312Sjmallett 510210312Sjmallett if (i < numvec) { 511210312Sjmallett PPT_TEARDOWN_MSI(vm, vcpu, ppt); 512210312Sjmallett return (ENXIO); 513210312Sjmallett } 514210312Sjmallett 515210312Sjmallett return (0); 516210312Sjmallett} 517210312Sjmallett 518210312Sjmallettint 519210312Sjmallettppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, 520210312Sjmallett int idx, uint32_t msg, uint32_t vector_control, uint64_t addr) 521210312Sjmallett{ 522210312Sjmallett struct pptdev *ppt; 523210312Sjmallett struct pci_devinfo *dinfo; 524210312Sjmallett int numvec, vector_count, rid, error; 525210312Sjmallett size_t res_size, cookie_size, arg_size; 526217620Sgonzo 527210312Sjmallett ppt = ppt_find(bus, slot, func); 528210312Sjmallett if (ppt == NULL) 529210312Sjmallett return (ENOENT); 530210312Sjmallett if (ppt->vm != vm) /* Make sure we own this device */ 531210312Sjmallett return (EBUSY); 532210312Sjmallett 533210312Sjmallett dinfo = device_get_ivars(ppt->dev); 534210312Sjmallett if (!dinfo) 535210312Sjmallett return (ENXIO); 536210312Sjmallett 537210312Sjmallett /* 538210312Sjmallett * First-time configuration: 539210312Sjmallett * Allocate the MSI-X table 540210312Sjmallett * Allocate the IRQ resources 541210312Sjmallett * Set up some variables in ppt->msix 542210312Sjmallett */ 543210312Sjmallett if (!ppt->msix.msix_table_res) { 544210312Sjmallett ppt->msix.res = NULL; 545210312Sjmallett ppt->msix.cookie = NULL; 546210312Sjmallett ppt->msix.arg = NULL; 547210312Sjmallett 548210312Sjmallett rid = dinfo->cfg.msix.msix_table_bar; 549210312Sjmallett ppt->msix.msix_table_res = bus_alloc_resource_any(ppt->dev, SYS_RES_MEMORY, 550210312Sjmallett &rid, RF_ACTIVE); 551210312Sjmallett if (ppt->msix.msix_table_res == NULL) 552210312Sjmallett return (ENOSPC); 553210312Sjmallett 554210312Sjmallett ppt->msix.msix_table_rid = rid; 555210312Sjmallett 556210312Sjmallett vector_count = numvec = pci_msix_count(ppt->dev); 557210312Sjmallett 558210312Sjmallett error = pci_alloc_msix(ppt->dev, &numvec); 559210312Sjmallett if (error) 560210312Sjmallett return (error); 561210312Sjmallett else if (vector_count != numvec) { 562210312Sjmallett pci_release_msi(ppt->dev); 563210312Sjmallett return (ENOSPC); 564210312Sjmallett } 565210312Sjmallett 566210312Sjmallett ppt->msix.num_msgs = numvec; 567210312Sjmallett 568210312Sjmallett ppt->msix.startrid = 1; 569210312Sjmallett 570210312Sjmallett res_size = numvec * sizeof(ppt->msix.res[0]); 571210312Sjmallett cookie_size = numvec * sizeof(ppt->msix.cookie[0]); 572210312Sjmallett arg_size = numvec * sizeof(ppt->msix.arg[0]); 573210312Sjmallett 574210312Sjmallett ppt->msix.res = malloc(res_size, M_PPTMSIX, M_WAITOK); 575210312Sjmallett ppt->msix.cookie = malloc(cookie_size, M_PPTMSIX, M_WAITOK); 576210312Sjmallett ppt->msix.arg = malloc(arg_size, M_PPTMSIX, M_WAITOK); 577210312Sjmallett if (ppt->msix.res == NULL || ppt->msix.cookie == NULL || 578210312Sjmallett ppt->msix.arg == NULL) { 579210312Sjmallett ppt_teardown_msix(ppt); 580210312Sjmallett return (ENOSPC); 581210312Sjmallett } 582210312Sjmallett bzero(ppt->msix.res, res_size); 583210312Sjmallett bzero(ppt->msix.cookie, cookie_size); 584210312Sjmallett bzero(ppt->msix.arg, arg_size); 585210312Sjmallett } 586210312Sjmallett 587210312Sjmallett if ((vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 588210312Sjmallett /* Tear down the IRQ if it's already set up */ 589210312Sjmallett ppt_teardown_msix_intr(ppt, idx); 590210312Sjmallett 591210312Sjmallett /* Allocate the IRQ resource */ 592210312Sjmallett ppt->msix.cookie[idx] = NULL; 593210312Sjmallett rid = ppt->msix.startrid + idx; 594210312Sjmallett ppt->msix.res[idx] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 595210312Sjmallett &rid, RF_ACTIVE); 596210312Sjmallett if (ppt->msix.res[idx] == NULL) 597210312Sjmallett return (ENXIO); 598210312Sjmallett 599217620Sgonzo ppt->msix.arg[idx].pptdev = ppt; 600210312Sjmallett ppt->msix.arg[idx].vec = msg; 601210312Sjmallett ppt->msix.arg[idx].vcpu = (addr >> 12) & 0xFF; 602210312Sjmallett 603210312Sjmallett /* Setup the MSI-X interrupt */ 604210312Sjmallett error = bus_setup_intr(ppt->dev, ppt->msix.res[idx], 605210312Sjmallett INTR_TYPE_NET | INTR_MPSAFE, 606210312Sjmallett pptintr, NULL, &ppt->msix.arg[idx], 607210312Sjmallett &ppt->msix.cookie[idx]); 608210312Sjmallett 609210312Sjmallett if (error != 0) { 610210312Sjmallett bus_teardown_intr(ppt->dev, ppt->msix.res[idx], ppt->msix.cookie[idx]); 611210312Sjmallett bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, ppt->msix.res[idx]); 612210312Sjmallett ppt->msix.cookie[idx] = NULL; 613210312Sjmallett ppt->msix.res[idx] = NULL; 614210312Sjmallett return (ENXIO); 615210312Sjmallett } 616210312Sjmallett } else { 617210312Sjmallett /* Masked, tear it down if it's already been set up */ 618210312Sjmallett ppt_teardown_msix_intr(ppt, idx); 619210312Sjmallett } 620210312Sjmallett 621210312Sjmallett return (0); 622210312Sjmallett} 623210312Sjmallett 624210312Sjmallett