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