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