The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/amd64/vmm/io/ppt.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: e4e3ced874dca01bfb5e96492b2245b1


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.