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