1366f6083SPeter Grehan /*- 2366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc. 3366f6083SPeter Grehan * All rights reserved. 4366f6083SPeter Grehan * 5366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without 6366f6083SPeter Grehan * modification, are permitted provided that the following conditions 7366f6083SPeter Grehan * are met: 8366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright 9366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer. 10366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 11366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the 12366f6083SPeter Grehan * documentation and/or other materials provided with the distribution. 13366f6083SPeter Grehan * 14366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24366f6083SPeter Grehan * SUCH DAMAGE. 25366f6083SPeter Grehan * 26366f6083SPeter Grehan * $FreeBSD$ 27366f6083SPeter Grehan */ 28366f6083SPeter Grehan 29366f6083SPeter Grehan #include <sys/cdefs.h> 30366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 31366f6083SPeter Grehan 32366f6083SPeter Grehan #include <sys/param.h> 33366f6083SPeter Grehan #include <sys/systm.h> 34366f6083SPeter Grehan #include <sys/kernel.h> 35*cd942e0fSPeter Grehan #include <sys/malloc.h> 36366f6083SPeter Grehan #include <sys/module.h> 37366f6083SPeter Grehan #include <sys/bus.h> 38366f6083SPeter Grehan #include <sys/pciio.h> 39366f6083SPeter Grehan #include <sys/rman.h> 40a5615c90SPeter Grehan #include <sys/smp.h> 41366f6083SPeter Grehan 42366f6083SPeter Grehan #include <dev/pci/pcivar.h> 43366f6083SPeter Grehan #include <dev/pci/pcireg.h> 44366f6083SPeter Grehan 45366f6083SPeter Grehan #include <machine/resource.h> 46366f6083SPeter Grehan 47366f6083SPeter Grehan #include <machine/vmm.h> 48366f6083SPeter Grehan #include <machine/vmm_dev.h> 49366f6083SPeter Grehan 50366f6083SPeter Grehan #include "vmm_lapic.h" 51366f6083SPeter Grehan #include "vmm_ktr.h" 52366f6083SPeter Grehan 53366f6083SPeter Grehan #include "iommu.h" 54366f6083SPeter Grehan #include "ppt.h" 55366f6083SPeter Grehan 56366f6083SPeter Grehan #define MAX_PPTDEVS (sizeof(pptdevs) / sizeof(pptdevs[0])) 57366f6083SPeter Grehan #define MAX_MMIOSEGS (PCIR_MAX_BAR_0 + 1) 58366f6083SPeter Grehan #define MAX_MSIMSGS 32 59366f6083SPeter Grehan 60*cd942e0fSPeter Grehan MALLOC_DEFINE(M_PPTMSIX, "pptmsix", "Passthru MSI-X resources"); 61*cd942e0fSPeter Grehan 62366f6083SPeter Grehan struct pptintr_arg { /* pptintr(pptintr_arg) */ 63366f6083SPeter Grehan struct pptdev *pptdev; 64*cd942e0fSPeter Grehan int vec; 65*cd942e0fSPeter Grehan int vcpu; 66366f6083SPeter Grehan }; 67366f6083SPeter Grehan 68366f6083SPeter Grehan static struct pptdev { 69366f6083SPeter Grehan device_t dev; 70366f6083SPeter Grehan struct vm *vm; /* owner of this device */ 71366f6083SPeter Grehan struct vm_memory_segment mmio[MAX_MMIOSEGS]; 72366f6083SPeter Grehan struct { 73366f6083SPeter Grehan int num_msgs; /* guest state */ 74366f6083SPeter Grehan int vector; 75366f6083SPeter Grehan int vcpu; 76366f6083SPeter Grehan 77366f6083SPeter Grehan int startrid; /* host state */ 78366f6083SPeter Grehan struct resource *res[MAX_MSIMSGS]; 79366f6083SPeter Grehan void *cookie[MAX_MSIMSGS]; 80366f6083SPeter Grehan struct pptintr_arg arg[MAX_MSIMSGS]; 81366f6083SPeter Grehan } msi; 82*cd942e0fSPeter Grehan 83*cd942e0fSPeter Grehan struct { 84*cd942e0fSPeter Grehan int num_msgs; 85*cd942e0fSPeter Grehan int startrid; 86*cd942e0fSPeter Grehan int msix_table_rid; 87*cd942e0fSPeter Grehan struct resource *msix_table_res; 88*cd942e0fSPeter Grehan struct resource **res; 89*cd942e0fSPeter Grehan void **cookie; 90*cd942e0fSPeter Grehan struct pptintr_arg *arg; 91*cd942e0fSPeter Grehan } msix; 92366f6083SPeter Grehan } pptdevs[32]; 93366f6083SPeter Grehan 94366f6083SPeter Grehan static int num_pptdevs; 95366f6083SPeter Grehan 96366f6083SPeter Grehan static int 97366f6083SPeter Grehan ppt_probe(device_t dev) 98366f6083SPeter Grehan { 99366f6083SPeter Grehan int bus, slot, func; 100366f6083SPeter Grehan struct pci_devinfo *dinfo; 101366f6083SPeter Grehan 102366f6083SPeter Grehan dinfo = (struct pci_devinfo *)device_get_ivars(dev); 103366f6083SPeter Grehan 104366f6083SPeter Grehan bus = pci_get_bus(dev); 105366f6083SPeter Grehan slot = pci_get_slot(dev); 106366f6083SPeter Grehan func = pci_get_function(dev); 107366f6083SPeter Grehan 108366f6083SPeter Grehan /* 109366f6083SPeter Grehan * To qualify as a pci passthrough device a device must: 110366f6083SPeter Grehan * - be allowed by administrator to be used in this role 111366f6083SPeter Grehan * - be an endpoint device 112366f6083SPeter Grehan */ 113366f6083SPeter Grehan if (vmm_is_pptdev(bus, slot, func) && 114366f6083SPeter Grehan (dinfo->cfg.hdrtype & PCIM_HDRTYPE) == PCIM_HDRTYPE_NORMAL) 115366f6083SPeter Grehan return (0); 116366f6083SPeter Grehan else 117366f6083SPeter Grehan return (ENXIO); 118366f6083SPeter Grehan } 119366f6083SPeter Grehan 120366f6083SPeter Grehan static int 121366f6083SPeter Grehan ppt_attach(device_t dev) 122366f6083SPeter Grehan { 123366f6083SPeter Grehan int n; 124366f6083SPeter Grehan 125366f6083SPeter Grehan if (num_pptdevs >= MAX_PPTDEVS) { 126366f6083SPeter Grehan printf("ppt_attach: maximum number of pci passthrough devices " 127366f6083SPeter Grehan "exceeded\n"); 128366f6083SPeter Grehan return (ENXIO); 129366f6083SPeter Grehan } 130366f6083SPeter Grehan 131366f6083SPeter Grehan n = num_pptdevs++; 132366f6083SPeter Grehan pptdevs[n].dev = dev; 133366f6083SPeter Grehan 134366f6083SPeter Grehan if (bootverbose) 135366f6083SPeter Grehan device_printf(dev, "attached\n"); 136366f6083SPeter Grehan 137366f6083SPeter Grehan return (0); 138366f6083SPeter Grehan } 139366f6083SPeter Grehan 140366f6083SPeter Grehan static int 141366f6083SPeter Grehan ppt_detach(device_t dev) 142366f6083SPeter Grehan { 143366f6083SPeter Grehan /* 144366f6083SPeter Grehan * XXX check whether there are any pci passthrough devices assigned 145366f6083SPeter Grehan * to guests before we allow this driver to detach. 146366f6083SPeter Grehan */ 147366f6083SPeter Grehan 148366f6083SPeter Grehan return (0); 149366f6083SPeter Grehan } 150366f6083SPeter Grehan 151366f6083SPeter Grehan static device_method_t ppt_methods[] = { 152366f6083SPeter Grehan /* Device interface */ 153366f6083SPeter Grehan DEVMETHOD(device_probe, ppt_probe), 154366f6083SPeter Grehan DEVMETHOD(device_attach, ppt_attach), 155366f6083SPeter Grehan DEVMETHOD(device_detach, ppt_detach), 156366f6083SPeter Grehan {0, 0} 157366f6083SPeter Grehan }; 158366f6083SPeter Grehan 159366f6083SPeter Grehan static devclass_t ppt_devclass; 160366f6083SPeter Grehan DEFINE_CLASS_0(ppt, ppt_driver, ppt_methods, 0); 161366f6083SPeter Grehan DRIVER_MODULE(ppt, pci, ppt_driver, ppt_devclass, NULL, NULL); 162366f6083SPeter Grehan 163366f6083SPeter Grehan static struct pptdev * 164366f6083SPeter Grehan ppt_find(int bus, int slot, int func) 165366f6083SPeter Grehan { 166366f6083SPeter Grehan device_t dev; 167366f6083SPeter Grehan int i, b, s, f; 168366f6083SPeter Grehan 169366f6083SPeter Grehan for (i = 0; i < num_pptdevs; i++) { 170366f6083SPeter Grehan dev = pptdevs[i].dev; 171366f6083SPeter Grehan b = pci_get_bus(dev); 172366f6083SPeter Grehan s = pci_get_slot(dev); 173366f6083SPeter Grehan f = pci_get_function(dev); 174366f6083SPeter Grehan if (bus == b && slot == s && func == f) 175366f6083SPeter Grehan return (&pptdevs[i]); 176366f6083SPeter Grehan } 177366f6083SPeter Grehan return (NULL); 178366f6083SPeter Grehan } 179366f6083SPeter Grehan 180366f6083SPeter Grehan static void 181366f6083SPeter Grehan ppt_unmap_mmio(struct vm *vm, struct pptdev *ppt) 182366f6083SPeter Grehan { 183366f6083SPeter Grehan int i; 184366f6083SPeter Grehan struct vm_memory_segment *seg; 185366f6083SPeter Grehan 186366f6083SPeter Grehan for (i = 0; i < MAX_MMIOSEGS; i++) { 187366f6083SPeter Grehan seg = &ppt->mmio[i]; 188366f6083SPeter Grehan if (seg->len == 0) 189366f6083SPeter Grehan continue; 190366f6083SPeter Grehan (void)vm_unmap_mmio(vm, seg->gpa, seg->len); 191366f6083SPeter Grehan bzero(seg, sizeof(struct vm_memory_segment)); 192366f6083SPeter Grehan } 193366f6083SPeter Grehan } 194366f6083SPeter Grehan 195366f6083SPeter Grehan static void 196366f6083SPeter Grehan ppt_teardown_msi(struct pptdev *ppt) 197366f6083SPeter Grehan { 198366f6083SPeter Grehan int i, rid; 199366f6083SPeter Grehan void *cookie; 200366f6083SPeter Grehan struct resource *res; 201366f6083SPeter Grehan 202366f6083SPeter Grehan if (ppt->msi.num_msgs == 0) 203366f6083SPeter Grehan return; 204366f6083SPeter Grehan 205366f6083SPeter Grehan for (i = 0; i < ppt->msi.num_msgs; i++) { 206366f6083SPeter Grehan rid = ppt->msi.startrid + i; 207366f6083SPeter Grehan res = ppt->msi.res[i]; 208366f6083SPeter Grehan cookie = ppt->msi.cookie[i]; 209366f6083SPeter Grehan 210366f6083SPeter Grehan if (cookie != NULL) 211366f6083SPeter Grehan bus_teardown_intr(ppt->dev, res, cookie); 212366f6083SPeter Grehan 213366f6083SPeter Grehan if (res != NULL) 214366f6083SPeter Grehan bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 215366f6083SPeter Grehan 216366f6083SPeter Grehan ppt->msi.res[i] = NULL; 217366f6083SPeter Grehan ppt->msi.cookie[i] = NULL; 218366f6083SPeter Grehan } 219366f6083SPeter Grehan 220366f6083SPeter Grehan if (ppt->msi.startrid == 1) 221366f6083SPeter Grehan pci_release_msi(ppt->dev); 222366f6083SPeter Grehan 223366f6083SPeter Grehan ppt->msi.num_msgs = 0; 224366f6083SPeter Grehan } 225366f6083SPeter Grehan 226*cd942e0fSPeter Grehan static void 227*cd942e0fSPeter Grehan ppt_teardown_msix_intr(struct pptdev *ppt, int idx) 228*cd942e0fSPeter Grehan { 229*cd942e0fSPeter Grehan int rid; 230*cd942e0fSPeter Grehan struct resource *res; 231*cd942e0fSPeter Grehan void *cookie; 232*cd942e0fSPeter Grehan 233*cd942e0fSPeter Grehan rid = ppt->msix.startrid + idx; 234*cd942e0fSPeter Grehan res = ppt->msix.res[idx]; 235*cd942e0fSPeter Grehan cookie = ppt->msix.cookie[idx]; 236*cd942e0fSPeter Grehan 237*cd942e0fSPeter Grehan if (cookie != NULL) 238*cd942e0fSPeter Grehan bus_teardown_intr(ppt->dev, res, cookie); 239*cd942e0fSPeter Grehan 240*cd942e0fSPeter Grehan if (res != NULL) 241*cd942e0fSPeter Grehan bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, res); 242*cd942e0fSPeter Grehan 243*cd942e0fSPeter Grehan ppt->msix.res[idx] = NULL; 244*cd942e0fSPeter Grehan ppt->msix.cookie[idx] = NULL; 245*cd942e0fSPeter Grehan } 246*cd942e0fSPeter Grehan 247*cd942e0fSPeter Grehan static void 248*cd942e0fSPeter Grehan ppt_teardown_msix(struct pptdev *ppt) 249*cd942e0fSPeter Grehan { 250*cd942e0fSPeter Grehan int i, error; 251*cd942e0fSPeter Grehan 252*cd942e0fSPeter Grehan if (ppt->msix.num_msgs == 0) 253*cd942e0fSPeter Grehan return; 254*cd942e0fSPeter Grehan 255*cd942e0fSPeter Grehan for (i = 0; i < ppt->msix.num_msgs; i++) 256*cd942e0fSPeter Grehan ppt_teardown_msix_intr(ppt, i); 257*cd942e0fSPeter Grehan 258*cd942e0fSPeter Grehan if (ppt->msix.msix_table_res) { 259*cd942e0fSPeter Grehan bus_release_resource(ppt->dev, SYS_RES_MEMORY, 260*cd942e0fSPeter Grehan ppt->msix.msix_table_rid, 261*cd942e0fSPeter Grehan ppt->msix.msix_table_res); 262*cd942e0fSPeter Grehan ppt->msix.msix_table_res = NULL; 263*cd942e0fSPeter Grehan ppt->msix.msix_table_rid = 0; 264*cd942e0fSPeter Grehan } 265*cd942e0fSPeter Grehan 266*cd942e0fSPeter Grehan free(ppt->msix.res, M_PPTMSIX); 267*cd942e0fSPeter Grehan free(ppt->msix.cookie, M_PPTMSIX); 268*cd942e0fSPeter Grehan free(ppt->msix.arg, M_PPTMSIX); 269*cd942e0fSPeter Grehan 270*cd942e0fSPeter Grehan error = pci_release_msi(ppt->dev); 271*cd942e0fSPeter Grehan if (error) 272*cd942e0fSPeter Grehan printf("ppt_teardown_msix: Failed to release MSI-X resources (error %i)\n", error); 273*cd942e0fSPeter Grehan 274*cd942e0fSPeter Grehan ppt->msix.num_msgs = 0; 275*cd942e0fSPeter Grehan } 276*cd942e0fSPeter Grehan 277366f6083SPeter Grehan int 278366f6083SPeter Grehan ppt_assign_device(struct vm *vm, int bus, int slot, int func) 279366f6083SPeter Grehan { 280366f6083SPeter Grehan struct pptdev *ppt; 281366f6083SPeter Grehan 282366f6083SPeter Grehan ppt = ppt_find(bus, slot, func); 283366f6083SPeter Grehan if (ppt != NULL) { 284366f6083SPeter Grehan /* 285366f6083SPeter Grehan * If this device is owned by a different VM then we 286366f6083SPeter Grehan * cannot change its owner. 287366f6083SPeter Grehan */ 288366f6083SPeter Grehan if (ppt->vm != NULL && ppt->vm != vm) 289366f6083SPeter Grehan return (EBUSY); 290366f6083SPeter Grehan 291366f6083SPeter Grehan ppt->vm = vm; 292366f6083SPeter Grehan iommu_add_device(vm_iommu_domain(vm), bus, slot, func); 293366f6083SPeter Grehan return (0); 294366f6083SPeter Grehan } 295366f6083SPeter Grehan return (ENOENT); 296366f6083SPeter Grehan } 297366f6083SPeter Grehan 298366f6083SPeter Grehan int 299366f6083SPeter Grehan ppt_unassign_device(struct vm *vm, int bus, int slot, int func) 300366f6083SPeter Grehan { 301366f6083SPeter Grehan struct pptdev *ppt; 302366f6083SPeter Grehan 303366f6083SPeter Grehan ppt = ppt_find(bus, slot, func); 304366f6083SPeter Grehan if (ppt != NULL) { 305366f6083SPeter Grehan /* 306366f6083SPeter Grehan * If this device is not owned by this 'vm' then bail out. 307366f6083SPeter Grehan */ 308366f6083SPeter Grehan if (ppt->vm != vm) 309366f6083SPeter Grehan return (EBUSY); 310366f6083SPeter Grehan ppt_unmap_mmio(vm, ppt); 311366f6083SPeter Grehan ppt_teardown_msi(ppt); 312*cd942e0fSPeter Grehan ppt_teardown_msix(ppt); 313366f6083SPeter Grehan iommu_remove_device(vm_iommu_domain(vm), bus, slot, func); 314366f6083SPeter Grehan ppt->vm = NULL; 315366f6083SPeter Grehan return (0); 316366f6083SPeter Grehan } 317366f6083SPeter Grehan return (ENOENT); 318366f6083SPeter Grehan } 319366f6083SPeter Grehan 320366f6083SPeter Grehan int 321366f6083SPeter Grehan ppt_unassign_all(struct vm *vm) 322366f6083SPeter Grehan { 323366f6083SPeter Grehan int i, bus, slot, func; 324366f6083SPeter Grehan device_t dev; 325366f6083SPeter Grehan 326366f6083SPeter Grehan for (i = 0; i < num_pptdevs; i++) { 327366f6083SPeter Grehan if (pptdevs[i].vm == vm) { 328366f6083SPeter Grehan dev = pptdevs[i].dev; 329366f6083SPeter Grehan bus = pci_get_bus(dev); 330366f6083SPeter Grehan slot = pci_get_slot(dev); 331366f6083SPeter Grehan func = pci_get_function(dev); 332366f6083SPeter Grehan ppt_unassign_device(vm, bus, slot, func); 333366f6083SPeter Grehan } 334366f6083SPeter Grehan } 335366f6083SPeter Grehan 336366f6083SPeter Grehan return (0); 337366f6083SPeter Grehan } 338366f6083SPeter Grehan 339366f6083SPeter Grehan int 340366f6083SPeter Grehan ppt_map_mmio(struct vm *vm, int bus, int slot, int func, 341366f6083SPeter Grehan vm_paddr_t gpa, size_t len, vm_paddr_t hpa) 342366f6083SPeter Grehan { 343366f6083SPeter Grehan int i, error; 344366f6083SPeter Grehan struct vm_memory_segment *seg; 345366f6083SPeter Grehan struct pptdev *ppt; 346366f6083SPeter Grehan 347366f6083SPeter Grehan ppt = ppt_find(bus, slot, func); 348366f6083SPeter Grehan if (ppt != NULL) { 349366f6083SPeter Grehan if (ppt->vm != vm) 350366f6083SPeter Grehan return (EBUSY); 351366f6083SPeter Grehan 352366f6083SPeter Grehan for (i = 0; i < MAX_MMIOSEGS; i++) { 353366f6083SPeter Grehan seg = &ppt->mmio[i]; 354366f6083SPeter Grehan if (seg->len == 0) { 355366f6083SPeter Grehan error = vm_map_mmio(vm, gpa, len, hpa); 356366f6083SPeter Grehan if (error == 0) { 357366f6083SPeter Grehan seg->gpa = gpa; 358366f6083SPeter Grehan seg->len = len; 359366f6083SPeter Grehan seg->hpa = hpa; 360366f6083SPeter Grehan } 361366f6083SPeter Grehan return (error); 362366f6083SPeter Grehan } 363366f6083SPeter Grehan } 364366f6083SPeter Grehan return (ENOSPC); 365366f6083SPeter Grehan } 366366f6083SPeter Grehan return (ENOENT); 367366f6083SPeter Grehan } 368366f6083SPeter Grehan 369366f6083SPeter Grehan static int 370366f6083SPeter Grehan pptintr(void *arg) 371366f6083SPeter Grehan { 372366f6083SPeter Grehan int vec; 373366f6083SPeter Grehan struct pptdev *ppt; 374366f6083SPeter Grehan struct pptintr_arg *pptarg; 375366f6083SPeter Grehan 376366f6083SPeter Grehan pptarg = arg; 377366f6083SPeter Grehan ppt = pptarg->pptdev; 378*cd942e0fSPeter Grehan vec = pptarg->vec; 379366f6083SPeter Grehan 380366f6083SPeter Grehan if (ppt->vm != NULL) 381*cd942e0fSPeter Grehan (void) lapic_set_intr(ppt->vm, pptarg->vcpu, vec); 382366f6083SPeter Grehan else { 383366f6083SPeter Grehan /* 384366f6083SPeter Grehan * XXX 385366f6083SPeter Grehan * This is not expected to happen - panic? 386366f6083SPeter Grehan */ 387366f6083SPeter Grehan } 388366f6083SPeter Grehan 389366f6083SPeter Grehan /* 390366f6083SPeter Grehan * For legacy interrupts give other filters a chance in case 391366f6083SPeter Grehan * the interrupt was not generated by the passthrough device. 392366f6083SPeter Grehan */ 393366f6083SPeter Grehan if (ppt->msi.startrid == 0) 394366f6083SPeter Grehan return (FILTER_STRAY); 395366f6083SPeter Grehan else 396366f6083SPeter Grehan return (FILTER_HANDLED); 397366f6083SPeter Grehan } 398366f6083SPeter Grehan 399366f6083SPeter Grehan /* 400366f6083SPeter Grehan * XXX 401366f6083SPeter Grehan * When we try to free the MSI resource the kernel will bind the thread to 402366f6083SPeter Grehan * the host cpu was originally handling the MSI. The function freeing the 403366f6083SPeter Grehan * MSI vector (apic_free_vector()) will panic the kernel if the thread 404366f6083SPeter Grehan * is already bound to a cpu. 405366f6083SPeter Grehan * 406366f6083SPeter Grehan * So, we temporarily unbind the vcpu thread before freeing the MSI resource. 407366f6083SPeter Grehan */ 408366f6083SPeter Grehan static void 409366f6083SPeter Grehan PPT_TEARDOWN_MSI(struct vm *vm, int vcpu, struct pptdev *ppt) 410366f6083SPeter Grehan { 411366f6083SPeter Grehan int pincpu = -1; 412366f6083SPeter Grehan 413366f6083SPeter Grehan vm_get_pinning(vm, vcpu, &pincpu); 414366f6083SPeter Grehan 415366f6083SPeter Grehan if (pincpu >= 0) 416366f6083SPeter Grehan vm_set_pinning(vm, vcpu, -1); 417366f6083SPeter Grehan 418366f6083SPeter Grehan ppt_teardown_msi(ppt); 419366f6083SPeter Grehan 420366f6083SPeter Grehan if (pincpu >= 0) 421366f6083SPeter Grehan vm_set_pinning(vm, vcpu, pincpu); 422366f6083SPeter Grehan } 423366f6083SPeter Grehan 424366f6083SPeter Grehan int 425366f6083SPeter Grehan ppt_setup_msi(struct vm *vm, int vcpu, int bus, int slot, int func, 426366f6083SPeter Grehan int destcpu, int vector, int numvec) 427366f6083SPeter Grehan { 428366f6083SPeter Grehan int i, rid, flags; 429366f6083SPeter Grehan int msi_count, startrid, error, tmp; 430366f6083SPeter Grehan struct pptdev *ppt; 431366f6083SPeter Grehan 432366f6083SPeter Grehan if ((destcpu >= VM_MAXCPU || destcpu < 0) || 433366f6083SPeter Grehan (vector < 0 || vector > 255) || 434366f6083SPeter Grehan (numvec < 0 || numvec > MAX_MSIMSGS)) 435366f6083SPeter Grehan return (EINVAL); 436366f6083SPeter Grehan 437366f6083SPeter Grehan ppt = ppt_find(bus, slot, func); 438366f6083SPeter Grehan if (ppt == NULL) 439366f6083SPeter Grehan return (ENOENT); 440366f6083SPeter Grehan if (ppt->vm != vm) /* Make sure we own this device */ 441366f6083SPeter Grehan return (EBUSY); 442366f6083SPeter Grehan 443366f6083SPeter Grehan /* Free any allocated resources */ 444366f6083SPeter Grehan PPT_TEARDOWN_MSI(vm, vcpu, ppt); 445366f6083SPeter Grehan 446366f6083SPeter Grehan if (numvec == 0) /* nothing more to do */ 447366f6083SPeter Grehan return (0); 448366f6083SPeter Grehan 449366f6083SPeter Grehan flags = RF_ACTIVE; 450366f6083SPeter Grehan msi_count = pci_msi_count(ppt->dev); 451366f6083SPeter Grehan if (msi_count == 0) { 452366f6083SPeter Grehan startrid = 0; /* legacy interrupt */ 453366f6083SPeter Grehan msi_count = 1; 454366f6083SPeter Grehan flags |= RF_SHAREABLE; 455366f6083SPeter Grehan } else 456366f6083SPeter Grehan startrid = 1; /* MSI */ 457366f6083SPeter Grehan 458366f6083SPeter Grehan /* 459366f6083SPeter Grehan * The device must be capable of supporting the number of vectors 460366f6083SPeter Grehan * the guest wants to allocate. 461366f6083SPeter Grehan */ 462366f6083SPeter Grehan if (numvec > msi_count) 463366f6083SPeter Grehan return (EINVAL); 464366f6083SPeter Grehan 465366f6083SPeter Grehan /* 466366f6083SPeter Grehan * Make sure that we can allocate all the MSI vectors that are needed 467366f6083SPeter Grehan * by the guest. 468366f6083SPeter Grehan */ 469366f6083SPeter Grehan if (startrid == 1) { 470366f6083SPeter Grehan tmp = numvec; 471366f6083SPeter Grehan error = pci_alloc_msi(ppt->dev, &tmp); 472366f6083SPeter Grehan if (error) 473366f6083SPeter Grehan return (error); 474366f6083SPeter Grehan else if (tmp != numvec) { 475366f6083SPeter Grehan pci_release_msi(ppt->dev); 476366f6083SPeter Grehan return (ENOSPC); 477366f6083SPeter Grehan } else { 478366f6083SPeter Grehan /* success */ 479366f6083SPeter Grehan } 480366f6083SPeter Grehan } 481366f6083SPeter Grehan 482366f6083SPeter Grehan ppt->msi.vector = vector; 483366f6083SPeter Grehan ppt->msi.vcpu = destcpu; 484366f6083SPeter Grehan ppt->msi.startrid = startrid; 485366f6083SPeter Grehan 486366f6083SPeter Grehan /* 487366f6083SPeter Grehan * Allocate the irq resource and attach it to the interrupt handler. 488366f6083SPeter Grehan */ 489366f6083SPeter Grehan for (i = 0; i < numvec; i++) { 490366f6083SPeter Grehan ppt->msi.num_msgs = i + 1; 491366f6083SPeter Grehan ppt->msi.cookie[i] = NULL; 492366f6083SPeter Grehan 493366f6083SPeter Grehan rid = startrid + i; 494366f6083SPeter Grehan ppt->msi.res[i] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 495366f6083SPeter Grehan &rid, flags); 496366f6083SPeter Grehan if (ppt->msi.res[i] == NULL) 497366f6083SPeter Grehan break; 498366f6083SPeter Grehan 499366f6083SPeter Grehan ppt->msi.arg[i].pptdev = ppt; 500*cd942e0fSPeter Grehan ppt->msi.arg[i].vec = vector + i; 501366f6083SPeter Grehan 502366f6083SPeter Grehan error = bus_setup_intr(ppt->dev, ppt->msi.res[i], 50334a6b2d6SJohn Baldwin INTR_TYPE_NET | INTR_MPSAFE, 504366f6083SPeter Grehan pptintr, NULL, &ppt->msi.arg[i], 505366f6083SPeter Grehan &ppt->msi.cookie[i]); 506366f6083SPeter Grehan if (error != 0) 507366f6083SPeter Grehan break; 508366f6083SPeter Grehan } 509366f6083SPeter Grehan 510366f6083SPeter Grehan if (i < numvec) { 511366f6083SPeter Grehan PPT_TEARDOWN_MSI(vm, vcpu, ppt); 512366f6083SPeter Grehan return (ENXIO); 513366f6083SPeter Grehan } 514366f6083SPeter Grehan 515366f6083SPeter Grehan return (0); 516366f6083SPeter Grehan } 517*cd942e0fSPeter Grehan 518*cd942e0fSPeter Grehan int 519*cd942e0fSPeter Grehan ppt_setup_msix(struct vm *vm, int vcpu, int bus, int slot, int func, 520*cd942e0fSPeter Grehan int idx, uint32_t msg, uint32_t vector_control, uint64_t addr) 521*cd942e0fSPeter Grehan { 522*cd942e0fSPeter Grehan struct pptdev *ppt; 523*cd942e0fSPeter Grehan struct pci_devinfo *dinfo; 524*cd942e0fSPeter Grehan int numvec, vector_count, rid, error; 525*cd942e0fSPeter Grehan size_t res_size, cookie_size, arg_size; 526*cd942e0fSPeter Grehan 527*cd942e0fSPeter Grehan ppt = ppt_find(bus, slot, func); 528*cd942e0fSPeter Grehan if (ppt == NULL) 529*cd942e0fSPeter Grehan return (ENOENT); 530*cd942e0fSPeter Grehan if (ppt->vm != vm) /* Make sure we own this device */ 531*cd942e0fSPeter Grehan return (EBUSY); 532*cd942e0fSPeter Grehan 533*cd942e0fSPeter Grehan dinfo = device_get_ivars(ppt->dev); 534*cd942e0fSPeter Grehan if (!dinfo) 535*cd942e0fSPeter Grehan return (ENXIO); 536*cd942e0fSPeter Grehan 537*cd942e0fSPeter Grehan /* 538*cd942e0fSPeter Grehan * First-time configuration: 539*cd942e0fSPeter Grehan * Allocate the MSI-X table 540*cd942e0fSPeter Grehan * Allocate the IRQ resources 541*cd942e0fSPeter Grehan * Set up some variables in ppt->msix 542*cd942e0fSPeter Grehan */ 543*cd942e0fSPeter Grehan if (!ppt->msix.msix_table_res) { 544*cd942e0fSPeter Grehan ppt->msix.res = NULL; 545*cd942e0fSPeter Grehan ppt->msix.cookie = NULL; 546*cd942e0fSPeter Grehan ppt->msix.arg = NULL; 547*cd942e0fSPeter Grehan 548*cd942e0fSPeter Grehan rid = dinfo->cfg.msix.msix_table_bar; 549*cd942e0fSPeter Grehan ppt->msix.msix_table_res = bus_alloc_resource_any(ppt->dev, SYS_RES_MEMORY, 550*cd942e0fSPeter Grehan &rid, RF_ACTIVE); 551*cd942e0fSPeter Grehan if (ppt->msix.msix_table_res == NULL) 552*cd942e0fSPeter Grehan return (ENOSPC); 553*cd942e0fSPeter Grehan 554*cd942e0fSPeter Grehan ppt->msix.msix_table_rid = rid; 555*cd942e0fSPeter Grehan 556*cd942e0fSPeter Grehan vector_count = numvec = pci_msix_count(ppt->dev); 557*cd942e0fSPeter Grehan 558*cd942e0fSPeter Grehan error = pci_alloc_msix(ppt->dev, &numvec); 559*cd942e0fSPeter Grehan if (error) 560*cd942e0fSPeter Grehan return (error); 561*cd942e0fSPeter Grehan else if (vector_count != numvec) { 562*cd942e0fSPeter Grehan pci_release_msi(ppt->dev); 563*cd942e0fSPeter Grehan return (ENOSPC); 564*cd942e0fSPeter Grehan } 565*cd942e0fSPeter Grehan 566*cd942e0fSPeter Grehan ppt->msix.num_msgs = numvec; 567*cd942e0fSPeter Grehan 568*cd942e0fSPeter Grehan ppt->msix.startrid = 1; 569*cd942e0fSPeter Grehan 570*cd942e0fSPeter Grehan res_size = numvec * sizeof(ppt->msix.res[0]); 571*cd942e0fSPeter Grehan cookie_size = numvec * sizeof(ppt->msix.cookie[0]); 572*cd942e0fSPeter Grehan arg_size = numvec * sizeof(ppt->msix.arg[0]); 573*cd942e0fSPeter Grehan 574*cd942e0fSPeter Grehan ppt->msix.res = malloc(res_size, M_PPTMSIX, M_WAITOK); 575*cd942e0fSPeter Grehan ppt->msix.cookie = malloc(cookie_size, M_PPTMSIX, M_WAITOK); 576*cd942e0fSPeter Grehan ppt->msix.arg = malloc(arg_size, M_PPTMSIX, M_WAITOK); 577*cd942e0fSPeter Grehan if (ppt->msix.res == NULL || ppt->msix.cookie == NULL || 578*cd942e0fSPeter Grehan ppt->msix.arg == NULL) { 579*cd942e0fSPeter Grehan ppt_teardown_msix(ppt); 580*cd942e0fSPeter Grehan return (ENOSPC); 581*cd942e0fSPeter Grehan } 582*cd942e0fSPeter Grehan bzero(ppt->msix.res, res_size); 583*cd942e0fSPeter Grehan bzero(ppt->msix.cookie, cookie_size); 584*cd942e0fSPeter Grehan bzero(ppt->msix.arg, arg_size); 585*cd942e0fSPeter Grehan } 586*cd942e0fSPeter Grehan 587*cd942e0fSPeter Grehan if ((vector_control & PCIM_MSIX_VCTRL_MASK) == 0) { 588*cd942e0fSPeter Grehan /* Tear down the IRQ if it's already set up */ 589*cd942e0fSPeter Grehan ppt_teardown_msix_intr(ppt, idx); 590*cd942e0fSPeter Grehan 591*cd942e0fSPeter Grehan /* Allocate the IRQ resource */ 592*cd942e0fSPeter Grehan ppt->msix.cookie[idx] = NULL; 593*cd942e0fSPeter Grehan rid = ppt->msix.startrid + idx; 594*cd942e0fSPeter Grehan ppt->msix.res[idx] = bus_alloc_resource_any(ppt->dev, SYS_RES_IRQ, 595*cd942e0fSPeter Grehan &rid, RF_ACTIVE); 596*cd942e0fSPeter Grehan if (ppt->msix.res[idx] == NULL) 597*cd942e0fSPeter Grehan return (ENXIO); 598*cd942e0fSPeter Grehan 599*cd942e0fSPeter Grehan ppt->msix.arg[idx].pptdev = ppt; 600*cd942e0fSPeter Grehan ppt->msix.arg[idx].vec = msg; 601*cd942e0fSPeter Grehan ppt->msix.arg[idx].vcpu = (addr >> 12) & 0xFF; 602*cd942e0fSPeter Grehan 603*cd942e0fSPeter Grehan /* Setup the MSI-X interrupt */ 604*cd942e0fSPeter Grehan error = bus_setup_intr(ppt->dev, ppt->msix.res[idx], 605*cd942e0fSPeter Grehan INTR_TYPE_NET | INTR_MPSAFE, 606*cd942e0fSPeter Grehan pptintr, NULL, &ppt->msix.arg[idx], 607*cd942e0fSPeter Grehan &ppt->msix.cookie[idx]); 608*cd942e0fSPeter Grehan 609*cd942e0fSPeter Grehan if (error != 0) { 610*cd942e0fSPeter Grehan bus_teardown_intr(ppt->dev, ppt->msix.res[idx], ppt->msix.cookie[idx]); 611*cd942e0fSPeter Grehan bus_release_resource(ppt->dev, SYS_RES_IRQ, rid, ppt->msix.res[idx]); 612*cd942e0fSPeter Grehan ppt->msix.cookie[idx] = NULL; 613*cd942e0fSPeter Grehan ppt->msix.res[idx] = NULL; 614*cd942e0fSPeter Grehan return (ENXIO); 615*cd942e0fSPeter Grehan } 616*cd942e0fSPeter Grehan } else { 617*cd942e0fSPeter Grehan /* Masked, tear it down if it's already been set up */ 618*cd942e0fSPeter Grehan ppt_teardown_msix_intr(ppt, idx); 619*cd942e0fSPeter Grehan } 620*cd942e0fSPeter Grehan 621*cd942e0fSPeter Grehan return (0); 622*cd942e0fSPeter Grehan } 623*cd942e0fSPeter Grehan 624