xref: /freebsd/sys/dev/virtio/pci/virtio_pci.c (revision 10b59a9b4add0320d52c15ce057dd697261e7dfc)
1*10b59a9bSPeter Grehan /*-
2*10b59a9bSPeter Grehan  * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
3*10b59a9bSPeter Grehan  * All rights reserved.
4*10b59a9bSPeter Grehan  *
5*10b59a9bSPeter Grehan  * Redistribution and use in source and binary forms, with or without
6*10b59a9bSPeter Grehan  * modification, are permitted provided that the following conditions
7*10b59a9bSPeter Grehan  * are met:
8*10b59a9bSPeter Grehan  * 1. Redistributions of source code must retain the above copyright
9*10b59a9bSPeter Grehan  *    notice unmodified, this list of conditions, and the following
10*10b59a9bSPeter Grehan  *    disclaimer.
11*10b59a9bSPeter Grehan  * 2. Redistributions in binary form must reproduce the above copyright
12*10b59a9bSPeter Grehan  *    notice, this list of conditions and the following disclaimer in the
13*10b59a9bSPeter Grehan  *    documentation and/or other materials provided with the distribution.
14*10b59a9bSPeter Grehan  *
15*10b59a9bSPeter Grehan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16*10b59a9bSPeter Grehan  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17*10b59a9bSPeter Grehan  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18*10b59a9bSPeter Grehan  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19*10b59a9bSPeter Grehan  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20*10b59a9bSPeter Grehan  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21*10b59a9bSPeter Grehan  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22*10b59a9bSPeter Grehan  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23*10b59a9bSPeter Grehan  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24*10b59a9bSPeter Grehan  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25*10b59a9bSPeter Grehan  */
26*10b59a9bSPeter Grehan 
27*10b59a9bSPeter Grehan /* Driver for the VirtIO PCI interface. */
28*10b59a9bSPeter Grehan 
29*10b59a9bSPeter Grehan #include <sys/cdefs.h>
30*10b59a9bSPeter Grehan __FBSDID("$FreeBSD$");
31*10b59a9bSPeter Grehan 
32*10b59a9bSPeter Grehan #include <sys/param.h>
33*10b59a9bSPeter Grehan #include <sys/systm.h>
34*10b59a9bSPeter Grehan #include <sys/bus.h>
35*10b59a9bSPeter Grehan #include <sys/kernel.h>
36*10b59a9bSPeter Grehan #include <sys/module.h>
37*10b59a9bSPeter Grehan #include <sys/malloc.h>
38*10b59a9bSPeter Grehan 
39*10b59a9bSPeter Grehan #include <machine/bus.h>
40*10b59a9bSPeter Grehan #include <machine/resource.h>
41*10b59a9bSPeter Grehan #include <sys/bus.h>
42*10b59a9bSPeter Grehan #include <sys/rman.h>
43*10b59a9bSPeter Grehan 
44*10b59a9bSPeter Grehan #include <dev/pci/pcivar.h>
45*10b59a9bSPeter Grehan #include <dev/pci/pcireg.h>
46*10b59a9bSPeter Grehan 
47*10b59a9bSPeter Grehan #include <dev/virtio/virtio.h>
48*10b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h>
49*10b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h>
50*10b59a9bSPeter Grehan 
51*10b59a9bSPeter Grehan #include "virtio_bus_if.h"
52*10b59a9bSPeter Grehan #include "virtio_if.h"
53*10b59a9bSPeter Grehan 
54*10b59a9bSPeter Grehan struct vtpci_softc {
55*10b59a9bSPeter Grehan 	device_t			 vtpci_dev;
56*10b59a9bSPeter Grehan 	struct resource			*vtpci_res;
57*10b59a9bSPeter Grehan 	struct resource			*vtpci_msix_res;
58*10b59a9bSPeter Grehan 	uint64_t			 vtpci_features;
59*10b59a9bSPeter Grehan 	uint32_t			 vtpci_flags;
60*10b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_NO_MSI		 0x0001
61*10b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_MSI		 0x0002
62*10b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_NO_MSIX		 0x0010
63*10b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_MSIX		 0x0020
64*10b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_SHARED_MSIX	 0x0040
65*10b59a9bSPeter Grehan 
66*10b59a9bSPeter Grehan 	device_t			 vtpci_child_dev;
67*10b59a9bSPeter Grehan 	struct virtio_feature_desc	*vtpci_child_feat_desc;
68*10b59a9bSPeter Grehan 
69*10b59a9bSPeter Grehan 	/*
70*10b59a9bSPeter Grehan 	 * Ideally, each virtqueue that the driver provides a callback for
71*10b59a9bSPeter Grehan 	 * will receive its own MSIX vector. If there are not sufficient
72*10b59a9bSPeter Grehan 	 * vectors available, we will then attempt to have all the VQs
73*10b59a9bSPeter Grehan 	 * share one vector. Note that when using MSIX, the configuration
74*10b59a9bSPeter Grehan 	 * changed notifications must be on their own vector.
75*10b59a9bSPeter Grehan 	 *
76*10b59a9bSPeter Grehan 	 * If MSIX is not available, we will attempt to have the whole
77*10b59a9bSPeter Grehan 	 * device share one MSI vector, and then, finally, one legacy
78*10b59a9bSPeter Grehan 	 * interrupt.
79*10b59a9bSPeter Grehan 	 */
80*10b59a9bSPeter Grehan 	int				 vtpci_nvqs;
81*10b59a9bSPeter Grehan 	struct vtpci_virtqueue {
82*10b59a9bSPeter Grehan 		struct virtqueue *vq;
83*10b59a9bSPeter Grehan 
84*10b59a9bSPeter Grehan 		/* Index into vtpci_intr_res[] below. Unused, then -1. */
85*10b59a9bSPeter Grehan 		int		  ires_idx;
86*10b59a9bSPeter Grehan 	} vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
87*10b59a9bSPeter Grehan 
88*10b59a9bSPeter Grehan 	/*
89*10b59a9bSPeter Grehan 	 * When using MSIX interrupts, the first element of vtpci_intr_res[]
90*10b59a9bSPeter Grehan 	 * is always the configuration changed notifications. The remaining
91*10b59a9bSPeter Grehan 	 * element(s) are used for the virtqueues.
92*10b59a9bSPeter Grehan 	 *
93*10b59a9bSPeter Grehan 	 * With MSI and legacy interrupts, only the first element of
94*10b59a9bSPeter Grehan 	 * vtpci_intr_res[] is used.
95*10b59a9bSPeter Grehan 	 */
96*10b59a9bSPeter Grehan 	int				 vtpci_nintr_res;
97*10b59a9bSPeter Grehan 	struct vtpci_intr_resource {
98*10b59a9bSPeter Grehan 		struct resource	*irq;
99*10b59a9bSPeter Grehan 		int		 rid;
100*10b59a9bSPeter Grehan 		void		*intrhand;
101*10b59a9bSPeter Grehan 	} vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
102*10b59a9bSPeter Grehan };
103*10b59a9bSPeter Grehan 
104*10b59a9bSPeter Grehan static int	vtpci_probe(device_t);
105*10b59a9bSPeter Grehan static int	vtpci_attach(device_t);
106*10b59a9bSPeter Grehan static int	vtpci_detach(device_t);
107*10b59a9bSPeter Grehan static int	vtpci_suspend(device_t);
108*10b59a9bSPeter Grehan static int	vtpci_resume(device_t);
109*10b59a9bSPeter Grehan static int	vtpci_shutdown(device_t);
110*10b59a9bSPeter Grehan static void	vtpci_driver_added(device_t, driver_t *);
111*10b59a9bSPeter Grehan static void	vtpci_child_detached(device_t, device_t);
112*10b59a9bSPeter Grehan static int	vtpci_read_ivar(device_t, device_t, int, uintptr_t *);
113*10b59a9bSPeter Grehan static int	vtpci_write_ivar(device_t, device_t, int, uintptr_t);
114*10b59a9bSPeter Grehan 
115*10b59a9bSPeter Grehan static uint64_t	vtpci_negotiate_features(device_t, uint64_t);
116*10b59a9bSPeter Grehan static int	vtpci_with_feature(device_t, uint64_t);
117*10b59a9bSPeter Grehan static int	vtpci_alloc_virtqueues(device_t, int, int,
118*10b59a9bSPeter Grehan 		    struct vq_alloc_info *);
119*10b59a9bSPeter Grehan static int	vtpci_setup_intr(device_t, enum intr_type);
120*10b59a9bSPeter Grehan static void	vtpci_stop(device_t);
121*10b59a9bSPeter Grehan static int	vtpci_reinit(device_t, uint64_t);
122*10b59a9bSPeter Grehan static void	vtpci_reinit_complete(device_t);
123*10b59a9bSPeter Grehan static void	vtpci_notify_virtqueue(device_t, uint16_t);
124*10b59a9bSPeter Grehan static uint8_t	vtpci_get_status(device_t);
125*10b59a9bSPeter Grehan static void	vtpci_set_status(device_t, uint8_t);
126*10b59a9bSPeter Grehan static void	vtpci_read_dev_config(device_t, bus_size_t, void *, int);
127*10b59a9bSPeter Grehan static void	vtpci_write_dev_config(device_t, bus_size_t, void *, int);
128*10b59a9bSPeter Grehan 
129*10b59a9bSPeter Grehan static void	vtpci_describe_features(struct vtpci_softc *, const char *,
130*10b59a9bSPeter Grehan 		    uint64_t);
131*10b59a9bSPeter Grehan static void	vtpci_probe_and_attach_child(struct vtpci_softc *);
132*10b59a9bSPeter Grehan 
133*10b59a9bSPeter Grehan static int	vtpci_alloc_interrupts(struct vtpci_softc *, int, int,
134*10b59a9bSPeter Grehan 		    struct vq_alloc_info *);
135*10b59a9bSPeter Grehan static int	vtpci_alloc_intr_resources(struct vtpci_softc *, int,
136*10b59a9bSPeter Grehan 		    struct vq_alloc_info *);
137*10b59a9bSPeter Grehan static int	vtpci_alloc_msi(struct vtpci_softc *);
138*10b59a9bSPeter Grehan static int	vtpci_alloc_msix(struct vtpci_softc *, int);
139*10b59a9bSPeter Grehan static int	vtpci_register_msix_vector(struct vtpci_softc *, int, int);
140*10b59a9bSPeter Grehan 
141*10b59a9bSPeter Grehan static void	vtpci_free_interrupts(struct vtpci_softc *);
142*10b59a9bSPeter Grehan static void	vtpci_free_virtqueues(struct vtpci_softc *);
143*10b59a9bSPeter Grehan static void	vtpci_release_child_resources(struct vtpci_softc *);
144*10b59a9bSPeter Grehan static void	vtpci_reset(struct vtpci_softc *);
145*10b59a9bSPeter Grehan 
146*10b59a9bSPeter Grehan static int	vtpci_legacy_intr(void *);
147*10b59a9bSPeter Grehan static int	vtpci_vq_shared_intr(void *);
148*10b59a9bSPeter Grehan static int	vtpci_vq_intr(void *);
149*10b59a9bSPeter Grehan static int	vtpci_config_intr(void *);
150*10b59a9bSPeter Grehan 
151*10b59a9bSPeter Grehan /*
152*10b59a9bSPeter Grehan  * I/O port read/write wrappers.
153*10b59a9bSPeter Grehan  */
154*10b59a9bSPeter Grehan #define vtpci_read_config_1(sc, o)	bus_read_1((sc)->vtpci_res, (o))
155*10b59a9bSPeter Grehan #define vtpci_read_config_2(sc, o)	bus_read_2((sc)->vtpci_res, (o))
156*10b59a9bSPeter Grehan #define vtpci_read_config_4(sc, o)	bus_read_4((sc)->vtpci_res, (o))
157*10b59a9bSPeter Grehan #define vtpci_write_config_1(sc, o, v)	bus_write_1((sc)->vtpci_res, (o), (v))
158*10b59a9bSPeter Grehan #define vtpci_write_config_2(sc, o, v)	bus_write_2((sc)->vtpci_res, (o), (v))
159*10b59a9bSPeter Grehan #define vtpci_write_config_4(sc, o, v)	bus_write_4((sc)->vtpci_res, (o), (v))
160*10b59a9bSPeter Grehan 
161*10b59a9bSPeter Grehan /* Tunables. */
162*10b59a9bSPeter Grehan static int vtpci_disable_msix = 0;
163*10b59a9bSPeter Grehan TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix);
164*10b59a9bSPeter Grehan 
165*10b59a9bSPeter Grehan static device_method_t vtpci_methods[] = {
166*10b59a9bSPeter Grehan 	/* Device interface. */
167*10b59a9bSPeter Grehan 	DEVMETHOD(device_probe,			  vtpci_probe),
168*10b59a9bSPeter Grehan 	DEVMETHOD(device_attach,		  vtpci_attach),
169*10b59a9bSPeter Grehan 	DEVMETHOD(device_detach,		  vtpci_detach),
170*10b59a9bSPeter Grehan 	DEVMETHOD(device_suspend,		  vtpci_suspend),
171*10b59a9bSPeter Grehan 	DEVMETHOD(device_resume,		  vtpci_resume),
172*10b59a9bSPeter Grehan 	DEVMETHOD(device_shutdown,		  vtpci_shutdown),
173*10b59a9bSPeter Grehan 
174*10b59a9bSPeter Grehan 	/* Bus interface. */
175*10b59a9bSPeter Grehan 	DEVMETHOD(bus_driver_added,		  vtpci_driver_added),
176*10b59a9bSPeter Grehan 	DEVMETHOD(bus_child_detached,		  vtpci_child_detached),
177*10b59a9bSPeter Grehan 	DEVMETHOD(bus_read_ivar,		  vtpci_read_ivar),
178*10b59a9bSPeter Grehan 	DEVMETHOD(bus_write_ivar,		  vtpci_write_ivar),
179*10b59a9bSPeter Grehan 
180*10b59a9bSPeter Grehan 	/* VirtIO bus interface. */
181*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_negotiate_features,  vtpci_negotiate_features),
182*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_with_feature,	  vtpci_with_feature),
183*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_alloc_virtqueues,	  vtpci_alloc_virtqueues),
184*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_setup_intr,	  vtpci_setup_intr),
185*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_stop,		  vtpci_stop),
186*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_reinit,		  vtpci_reinit),
187*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_reinit_complete,	  vtpci_reinit_complete),
188*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_notify_vq,		  vtpci_notify_virtqueue),
189*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_read_device_config,  vtpci_read_dev_config),
190*10b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
191*10b59a9bSPeter Grehan 
192*10b59a9bSPeter Grehan 	{ 0, 0 }
193*10b59a9bSPeter Grehan };
194*10b59a9bSPeter Grehan 
195*10b59a9bSPeter Grehan static driver_t vtpci_driver = {
196*10b59a9bSPeter Grehan 	"virtio_pci",
197*10b59a9bSPeter Grehan 	vtpci_methods,
198*10b59a9bSPeter Grehan 	sizeof(struct vtpci_softc)
199*10b59a9bSPeter Grehan };
200*10b59a9bSPeter Grehan 
201*10b59a9bSPeter Grehan devclass_t vtpci_devclass;
202*10b59a9bSPeter Grehan 
203*10b59a9bSPeter Grehan DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0);
204*10b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1);
205*10b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1);
206*10b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1);
207*10b59a9bSPeter Grehan 
208*10b59a9bSPeter Grehan static int
209*10b59a9bSPeter Grehan vtpci_probe(device_t dev)
210*10b59a9bSPeter Grehan {
211*10b59a9bSPeter Grehan 	char desc[36];
212*10b59a9bSPeter Grehan 	const char *name;
213*10b59a9bSPeter Grehan 
214*10b59a9bSPeter Grehan 	if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
215*10b59a9bSPeter Grehan 		return (ENXIO);
216*10b59a9bSPeter Grehan 
217*10b59a9bSPeter Grehan 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
218*10b59a9bSPeter Grehan 	    pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX)
219*10b59a9bSPeter Grehan 		return (ENXIO);
220*10b59a9bSPeter Grehan 
221*10b59a9bSPeter Grehan 	if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION)
222*10b59a9bSPeter Grehan 		return (ENXIO);
223*10b59a9bSPeter Grehan 
224*10b59a9bSPeter Grehan 	name = virtio_device_name(pci_get_subdevice(dev));
225*10b59a9bSPeter Grehan 	if (name == NULL)
226*10b59a9bSPeter Grehan 		name = "Unknown";
227*10b59a9bSPeter Grehan 
228*10b59a9bSPeter Grehan 	snprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name);
229*10b59a9bSPeter Grehan 	device_set_desc_copy(dev, desc);
230*10b59a9bSPeter Grehan 
231*10b59a9bSPeter Grehan 	return (BUS_PROBE_DEFAULT);
232*10b59a9bSPeter Grehan }
233*10b59a9bSPeter Grehan 
234*10b59a9bSPeter Grehan static int
235*10b59a9bSPeter Grehan vtpci_attach(device_t dev)
236*10b59a9bSPeter Grehan {
237*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
238*10b59a9bSPeter Grehan 	device_t child;
239*10b59a9bSPeter Grehan 	int rid;
240*10b59a9bSPeter Grehan 
241*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
242*10b59a9bSPeter Grehan 	sc->vtpci_dev = dev;
243*10b59a9bSPeter Grehan 
244*10b59a9bSPeter Grehan 	pci_enable_busmaster(dev);
245*10b59a9bSPeter Grehan 
246*10b59a9bSPeter Grehan 	rid = PCIR_BAR(0);
247*10b59a9bSPeter Grehan 	sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
248*10b59a9bSPeter Grehan 	    RF_ACTIVE);
249*10b59a9bSPeter Grehan 	if (sc->vtpci_res == NULL) {
250*10b59a9bSPeter Grehan 		device_printf(dev, "cannot map I/O space\n");
251*10b59a9bSPeter Grehan 		return (ENXIO);
252*10b59a9bSPeter Grehan 	}
253*10b59a9bSPeter Grehan 
254*10b59a9bSPeter Grehan 	if (pci_find_extcap(dev, PCIY_MSI, NULL) != 0)
255*10b59a9bSPeter Grehan 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSI;
256*10b59a9bSPeter Grehan 
257*10b59a9bSPeter Grehan 	if (pci_find_extcap(dev, PCIY_MSIX, NULL) == 0) {
258*10b59a9bSPeter Grehan 		rid = PCIR_BAR(1);
259*10b59a9bSPeter Grehan 		sc->vtpci_msix_res = bus_alloc_resource_any(dev,
260*10b59a9bSPeter Grehan 		    SYS_RES_MEMORY, &rid, RF_ACTIVE);
261*10b59a9bSPeter Grehan 	}
262*10b59a9bSPeter Grehan 
263*10b59a9bSPeter Grehan 	if (sc->vtpci_msix_res == NULL)
264*10b59a9bSPeter Grehan 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSIX;
265*10b59a9bSPeter Grehan 
266*10b59a9bSPeter Grehan 	vtpci_reset(sc);
267*10b59a9bSPeter Grehan 
268*10b59a9bSPeter Grehan 	/* Tell the host we've noticed this device. */
269*10b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
270*10b59a9bSPeter Grehan 
271*10b59a9bSPeter Grehan 	if ((child = device_add_child(dev, NULL, -1)) == NULL) {
272*10b59a9bSPeter Grehan 		device_printf(dev, "cannot create child device\n");
273*10b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
274*10b59a9bSPeter Grehan 		vtpci_detach(dev);
275*10b59a9bSPeter Grehan 		return (ENOMEM);
276*10b59a9bSPeter Grehan 	}
277*10b59a9bSPeter Grehan 
278*10b59a9bSPeter Grehan 	sc->vtpci_child_dev = child;
279*10b59a9bSPeter Grehan 	vtpci_probe_and_attach_child(sc);
280*10b59a9bSPeter Grehan 
281*10b59a9bSPeter Grehan 	return (0);
282*10b59a9bSPeter Grehan }
283*10b59a9bSPeter Grehan 
284*10b59a9bSPeter Grehan static int
285*10b59a9bSPeter Grehan vtpci_detach(device_t dev)
286*10b59a9bSPeter Grehan {
287*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
288*10b59a9bSPeter Grehan 	device_t child;
289*10b59a9bSPeter Grehan 	int error;
290*10b59a9bSPeter Grehan 
291*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
292*10b59a9bSPeter Grehan 
293*10b59a9bSPeter Grehan 	if ((child = sc->vtpci_child_dev) != NULL) {
294*10b59a9bSPeter Grehan 		error = device_delete_child(dev, child);
295*10b59a9bSPeter Grehan 		if (error)
296*10b59a9bSPeter Grehan 			return (error);
297*10b59a9bSPeter Grehan 		sc->vtpci_child_dev = NULL;
298*10b59a9bSPeter Grehan 	}
299*10b59a9bSPeter Grehan 
300*10b59a9bSPeter Grehan 	vtpci_reset(sc);
301*10b59a9bSPeter Grehan 
302*10b59a9bSPeter Grehan 	if (sc->vtpci_msix_res != NULL) {
303*10b59a9bSPeter Grehan 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1),
304*10b59a9bSPeter Grehan 		    sc->vtpci_msix_res);
305*10b59a9bSPeter Grehan 		sc->vtpci_msix_res = NULL;
306*10b59a9bSPeter Grehan 	}
307*10b59a9bSPeter Grehan 
308*10b59a9bSPeter Grehan 	if (sc->vtpci_res != NULL) {
309*10b59a9bSPeter Grehan 		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
310*10b59a9bSPeter Grehan 		    sc->vtpci_res);
311*10b59a9bSPeter Grehan 		sc->vtpci_res = NULL;
312*10b59a9bSPeter Grehan 	}
313*10b59a9bSPeter Grehan 
314*10b59a9bSPeter Grehan 	return (0);
315*10b59a9bSPeter Grehan }
316*10b59a9bSPeter Grehan 
317*10b59a9bSPeter Grehan static int
318*10b59a9bSPeter Grehan vtpci_suspend(device_t dev)
319*10b59a9bSPeter Grehan {
320*10b59a9bSPeter Grehan 
321*10b59a9bSPeter Grehan 	return (bus_generic_suspend(dev));
322*10b59a9bSPeter Grehan }
323*10b59a9bSPeter Grehan 
324*10b59a9bSPeter Grehan static int
325*10b59a9bSPeter Grehan vtpci_resume(device_t dev)
326*10b59a9bSPeter Grehan {
327*10b59a9bSPeter Grehan 
328*10b59a9bSPeter Grehan 	return (bus_generic_resume(dev));
329*10b59a9bSPeter Grehan }
330*10b59a9bSPeter Grehan 
331*10b59a9bSPeter Grehan static int
332*10b59a9bSPeter Grehan vtpci_shutdown(device_t dev)
333*10b59a9bSPeter Grehan {
334*10b59a9bSPeter Grehan 
335*10b59a9bSPeter Grehan 	(void) bus_generic_shutdown(dev);
336*10b59a9bSPeter Grehan 	/* Forcibly stop the host device. */
337*10b59a9bSPeter Grehan 	vtpci_stop(dev);
338*10b59a9bSPeter Grehan 
339*10b59a9bSPeter Grehan 	return (0);
340*10b59a9bSPeter Grehan }
341*10b59a9bSPeter Grehan 
342*10b59a9bSPeter Grehan static void
343*10b59a9bSPeter Grehan vtpci_driver_added(device_t dev, driver_t *driver)
344*10b59a9bSPeter Grehan {
345*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
346*10b59a9bSPeter Grehan 
347*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
348*10b59a9bSPeter Grehan 
349*10b59a9bSPeter Grehan 	vtpci_probe_and_attach_child(sc);
350*10b59a9bSPeter Grehan }
351*10b59a9bSPeter Grehan 
352*10b59a9bSPeter Grehan static void
353*10b59a9bSPeter Grehan vtpci_child_detached(device_t dev, device_t child)
354*10b59a9bSPeter Grehan {
355*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
356*10b59a9bSPeter Grehan 
357*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
358*10b59a9bSPeter Grehan 
359*10b59a9bSPeter Grehan 	vtpci_reset(sc);
360*10b59a9bSPeter Grehan 	vtpci_release_child_resources(sc);
361*10b59a9bSPeter Grehan }
362*10b59a9bSPeter Grehan 
363*10b59a9bSPeter Grehan static int
364*10b59a9bSPeter Grehan vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
365*10b59a9bSPeter Grehan {
366*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
367*10b59a9bSPeter Grehan 
368*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
369*10b59a9bSPeter Grehan 
370*10b59a9bSPeter Grehan 	if (sc->vtpci_child_dev != child)
371*10b59a9bSPeter Grehan 		return (ENOENT);
372*10b59a9bSPeter Grehan 
373*10b59a9bSPeter Grehan 	switch (index) {
374*10b59a9bSPeter Grehan 	case VIRTIO_IVAR_DEVTYPE:
375*10b59a9bSPeter Grehan 		*result = pci_get_subdevice(dev);
376*10b59a9bSPeter Grehan 		break;
377*10b59a9bSPeter Grehan 	default:
378*10b59a9bSPeter Grehan 		return (ENOENT);
379*10b59a9bSPeter Grehan 	}
380*10b59a9bSPeter Grehan 
381*10b59a9bSPeter Grehan 	return (0);
382*10b59a9bSPeter Grehan }
383*10b59a9bSPeter Grehan 
384*10b59a9bSPeter Grehan static int
385*10b59a9bSPeter Grehan vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
386*10b59a9bSPeter Grehan {
387*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
388*10b59a9bSPeter Grehan 
389*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
390*10b59a9bSPeter Grehan 
391*10b59a9bSPeter Grehan 	if (sc->vtpci_child_dev != child)
392*10b59a9bSPeter Grehan 		return (ENOENT);
393*10b59a9bSPeter Grehan 
394*10b59a9bSPeter Grehan 	switch (index) {
395*10b59a9bSPeter Grehan 	case VIRTIO_IVAR_FEATURE_DESC:
396*10b59a9bSPeter Grehan 		sc->vtpci_child_feat_desc = (void *) value;
397*10b59a9bSPeter Grehan 		break;
398*10b59a9bSPeter Grehan 	default:
399*10b59a9bSPeter Grehan 		return (ENOENT);
400*10b59a9bSPeter Grehan 	}
401*10b59a9bSPeter Grehan 
402*10b59a9bSPeter Grehan 	return (0);
403*10b59a9bSPeter Grehan }
404*10b59a9bSPeter Grehan 
405*10b59a9bSPeter Grehan static uint64_t
406*10b59a9bSPeter Grehan vtpci_negotiate_features(device_t dev, uint64_t child_features)
407*10b59a9bSPeter Grehan {
408*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
409*10b59a9bSPeter Grehan 	uint64_t host_features, features;
410*10b59a9bSPeter Grehan 
411*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
412*10b59a9bSPeter Grehan 
413*10b59a9bSPeter Grehan 	host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
414*10b59a9bSPeter Grehan 	vtpci_describe_features(sc, "host", host_features);
415*10b59a9bSPeter Grehan 
416*10b59a9bSPeter Grehan 	/*
417*10b59a9bSPeter Grehan 	 * Limit negotiated features to what the driver, virtqueue, and
418*10b59a9bSPeter Grehan 	 * host all support.
419*10b59a9bSPeter Grehan 	 */
420*10b59a9bSPeter Grehan 	features = host_features & child_features;
421*10b59a9bSPeter Grehan 	features = virtqueue_filter_features(features);
422*10b59a9bSPeter Grehan 	sc->vtpci_features = features;
423*10b59a9bSPeter Grehan 
424*10b59a9bSPeter Grehan 	vtpci_describe_features(sc, "negotiated", features);
425*10b59a9bSPeter Grehan 	vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
426*10b59a9bSPeter Grehan 
427*10b59a9bSPeter Grehan 	return (features);
428*10b59a9bSPeter Grehan }
429*10b59a9bSPeter Grehan 
430*10b59a9bSPeter Grehan static int
431*10b59a9bSPeter Grehan vtpci_with_feature(device_t dev, uint64_t feature)
432*10b59a9bSPeter Grehan {
433*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
434*10b59a9bSPeter Grehan 
435*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
436*10b59a9bSPeter Grehan 
437*10b59a9bSPeter Grehan 	return ((sc->vtpci_features & feature) != 0);
438*10b59a9bSPeter Grehan }
439*10b59a9bSPeter Grehan 
440*10b59a9bSPeter Grehan static int
441*10b59a9bSPeter Grehan vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
442*10b59a9bSPeter Grehan     struct vq_alloc_info *vq_info)
443*10b59a9bSPeter Grehan {
444*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
445*10b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
446*10b59a9bSPeter Grehan 	struct vq_alloc_info *info;
447*10b59a9bSPeter Grehan 	int queue, error;
448*10b59a9bSPeter Grehan 	uint16_t vq_size;
449*10b59a9bSPeter Grehan 
450*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
451*10b59a9bSPeter Grehan 
452*10b59a9bSPeter Grehan 	if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
453*10b59a9bSPeter Grehan 	    nvqs > VIRTIO_MAX_VIRTQUEUES)
454*10b59a9bSPeter Grehan 		return (EINVAL);
455*10b59a9bSPeter Grehan 
456*10b59a9bSPeter Grehan 	error = vtpci_alloc_interrupts(sc, flags, nvqs, vq_info);
457*10b59a9bSPeter Grehan 	if (error) {
458*10b59a9bSPeter Grehan 		device_printf(dev, "cannot allocate interrupts\n");
459*10b59a9bSPeter Grehan 		return (error);
460*10b59a9bSPeter Grehan 	}
461*10b59a9bSPeter Grehan 
462*10b59a9bSPeter Grehan 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
463*10b59a9bSPeter Grehan 		error = vtpci_register_msix_vector(sc,
464*10b59a9bSPeter Grehan 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
465*10b59a9bSPeter Grehan 		if (error)
466*10b59a9bSPeter Grehan 			return (error);
467*10b59a9bSPeter Grehan 	}
468*10b59a9bSPeter Grehan 
469*10b59a9bSPeter Grehan 	for (queue = 0; queue < nvqs; queue++) {
470*10b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[queue];
471*10b59a9bSPeter Grehan 		info = &vq_info[queue];
472*10b59a9bSPeter Grehan 
473*10b59a9bSPeter Grehan 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
474*10b59a9bSPeter Grehan 
475*10b59a9bSPeter Grehan 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
476*10b59a9bSPeter Grehan 		error = virtqueue_alloc(dev, queue, vq_size,
477*10b59a9bSPeter Grehan 		    VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
478*10b59a9bSPeter Grehan 		if (error)
479*10b59a9bSPeter Grehan 			return (error);
480*10b59a9bSPeter Grehan 
481*10b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
482*10b59a9bSPeter Grehan 			error = vtpci_register_msix_vector(sc,
483*10b59a9bSPeter Grehan 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
484*10b59a9bSPeter Grehan 			if (error)
485*10b59a9bSPeter Grehan 				return (error);
486*10b59a9bSPeter Grehan 		}
487*10b59a9bSPeter Grehan 
488*10b59a9bSPeter Grehan 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
489*10b59a9bSPeter Grehan 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
490*10b59a9bSPeter Grehan 
491*10b59a9bSPeter Grehan 		*info->vqai_vq = vqx->vq;
492*10b59a9bSPeter Grehan 		sc->vtpci_nvqs++;
493*10b59a9bSPeter Grehan 	}
494*10b59a9bSPeter Grehan 
495*10b59a9bSPeter Grehan 	return (0);
496*10b59a9bSPeter Grehan }
497*10b59a9bSPeter Grehan 
498*10b59a9bSPeter Grehan static int
499*10b59a9bSPeter Grehan vtpci_setup_intr(device_t dev, enum intr_type type)
500*10b59a9bSPeter Grehan {
501*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
502*10b59a9bSPeter Grehan 	struct vtpci_intr_resource *ires;
503*10b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
504*10b59a9bSPeter Grehan 	int i, flags, error;
505*10b59a9bSPeter Grehan 
506*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
507*10b59a9bSPeter Grehan 	flags = type | INTR_MPSAFE;
508*10b59a9bSPeter Grehan 	ires = &sc->vtpci_intr_res[0];
509*10b59a9bSPeter Grehan 
510*10b59a9bSPeter Grehan 	if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
511*10b59a9bSPeter Grehan 		error = bus_setup_intr(dev, ires->irq, flags,
512*10b59a9bSPeter Grehan 		    vtpci_legacy_intr, NULL, sc, &ires->intrhand);
513*10b59a9bSPeter Grehan 
514*10b59a9bSPeter Grehan 		return (error);
515*10b59a9bSPeter Grehan 	}
516*10b59a9bSPeter Grehan 
517*10b59a9bSPeter Grehan 	error = bus_setup_intr(dev, ires->irq, flags, vtpci_config_intr,
518*10b59a9bSPeter Grehan 	    NULL, sc, &ires->intrhand);
519*10b59a9bSPeter Grehan 	if (error)
520*10b59a9bSPeter Grehan 		return (error);
521*10b59a9bSPeter Grehan 
522*10b59a9bSPeter Grehan 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) {
523*10b59a9bSPeter Grehan 		ires = &sc->vtpci_intr_res[1];
524*10b59a9bSPeter Grehan 		error = bus_setup_intr(dev, ires->irq, flags,
525*10b59a9bSPeter Grehan 		    vtpci_vq_shared_intr, NULL, sc, &ires->intrhand);
526*10b59a9bSPeter Grehan 
527*10b59a9bSPeter Grehan 		return (error);
528*10b59a9bSPeter Grehan 	}
529*10b59a9bSPeter Grehan 
530*10b59a9bSPeter Grehan 	/* Setup an interrupt handler for each virtqueue. */
531*10b59a9bSPeter Grehan 	for (i = 0; i < sc->vtpci_nvqs; i++) {
532*10b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[i];
533*10b59a9bSPeter Grehan 		if (vqx->ires_idx < 1)
534*10b59a9bSPeter Grehan 			continue;
535*10b59a9bSPeter Grehan 
536*10b59a9bSPeter Grehan 		ires = &sc->vtpci_intr_res[vqx->ires_idx];
537*10b59a9bSPeter Grehan 		error = bus_setup_intr(dev, ires->irq, flags,
538*10b59a9bSPeter Grehan 		    vtpci_vq_intr, NULL, vqx->vq, &ires->intrhand);
539*10b59a9bSPeter Grehan 		if (error)
540*10b59a9bSPeter Grehan 			return (error);
541*10b59a9bSPeter Grehan 	}
542*10b59a9bSPeter Grehan 
543*10b59a9bSPeter Grehan 	return (0);
544*10b59a9bSPeter Grehan }
545*10b59a9bSPeter Grehan 
546*10b59a9bSPeter Grehan static void
547*10b59a9bSPeter Grehan vtpci_stop(device_t dev)
548*10b59a9bSPeter Grehan {
549*10b59a9bSPeter Grehan 
550*10b59a9bSPeter Grehan 	vtpci_reset(device_get_softc(dev));
551*10b59a9bSPeter Grehan }
552*10b59a9bSPeter Grehan 
553*10b59a9bSPeter Grehan static int
554*10b59a9bSPeter Grehan vtpci_reinit(device_t dev, uint64_t features)
555*10b59a9bSPeter Grehan {
556*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
557*10b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
558*10b59a9bSPeter Grehan 	struct virtqueue *vq;
559*10b59a9bSPeter Grehan 	int queue, error;
560*10b59a9bSPeter Grehan 	uint16_t vq_size;
561*10b59a9bSPeter Grehan 
562*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
563*10b59a9bSPeter Grehan 
564*10b59a9bSPeter Grehan 	/*
565*10b59a9bSPeter Grehan 	 * Redrive the device initialization. This is a bit of an abuse
566*10b59a9bSPeter Grehan 	 * of the specification, but both VirtualBox and QEMU/KVM seem
567*10b59a9bSPeter Grehan 	 * to play nice. We do not allow the host device to change from
568*10b59a9bSPeter Grehan 	 * what was originally negotiated beyond what the guest driver
569*10b59a9bSPeter Grehan 	 * changed (MSIX state should not change, number of virtqueues
570*10b59a9bSPeter Grehan 	 * and their size remain the same, etc).
571*10b59a9bSPeter Grehan 	 */
572*10b59a9bSPeter Grehan 
573*10b59a9bSPeter Grehan 	if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
574*10b59a9bSPeter Grehan 		vtpci_stop(dev);
575*10b59a9bSPeter Grehan 
576*10b59a9bSPeter Grehan 	/*
577*10b59a9bSPeter Grehan 	 * Quickly drive the status through ACK and DRIVER. The device
578*10b59a9bSPeter Grehan 	 * does not become usable again until vtpci_reinit_complete().
579*10b59a9bSPeter Grehan 	 */
580*10b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
581*10b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
582*10b59a9bSPeter Grehan 
583*10b59a9bSPeter Grehan 	vtpci_negotiate_features(dev, features);
584*10b59a9bSPeter Grehan 
585*10b59a9bSPeter Grehan 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
586*10b59a9bSPeter Grehan 		error = vtpci_register_msix_vector(sc,
587*10b59a9bSPeter Grehan 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
588*10b59a9bSPeter Grehan 		if (error)
589*10b59a9bSPeter Grehan 			return (error);
590*10b59a9bSPeter Grehan 	}
591*10b59a9bSPeter Grehan 
592*10b59a9bSPeter Grehan 	for (queue = 0; queue < sc->vtpci_nvqs; queue++) {
593*10b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[queue];
594*10b59a9bSPeter Grehan 		vq = vqx->vq;
595*10b59a9bSPeter Grehan 
596*10b59a9bSPeter Grehan 		KASSERT(vq != NULL, ("vq %d not allocated", queue));
597*10b59a9bSPeter Grehan 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
598*10b59a9bSPeter Grehan 
599*10b59a9bSPeter Grehan 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
600*10b59a9bSPeter Grehan 		error = virtqueue_reinit(vq, vq_size);
601*10b59a9bSPeter Grehan 		if (error)
602*10b59a9bSPeter Grehan 			return (error);
603*10b59a9bSPeter Grehan 
604*10b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
605*10b59a9bSPeter Grehan 			error = vtpci_register_msix_vector(sc,
606*10b59a9bSPeter Grehan 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
607*10b59a9bSPeter Grehan 			if (error)
608*10b59a9bSPeter Grehan 				return (error);
609*10b59a9bSPeter Grehan 		}
610*10b59a9bSPeter Grehan 
611*10b59a9bSPeter Grehan 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
612*10b59a9bSPeter Grehan 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
613*10b59a9bSPeter Grehan 	}
614*10b59a9bSPeter Grehan 
615*10b59a9bSPeter Grehan 	return (0);
616*10b59a9bSPeter Grehan }
617*10b59a9bSPeter Grehan 
618*10b59a9bSPeter Grehan static void
619*10b59a9bSPeter Grehan vtpci_reinit_complete(device_t dev)
620*10b59a9bSPeter Grehan {
621*10b59a9bSPeter Grehan 
622*10b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
623*10b59a9bSPeter Grehan }
624*10b59a9bSPeter Grehan 
625*10b59a9bSPeter Grehan static void
626*10b59a9bSPeter Grehan vtpci_notify_virtqueue(device_t dev, uint16_t queue)
627*10b59a9bSPeter Grehan {
628*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
629*10b59a9bSPeter Grehan 
630*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
631*10b59a9bSPeter Grehan 
632*10b59a9bSPeter Grehan 	vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
633*10b59a9bSPeter Grehan }
634*10b59a9bSPeter Grehan 
635*10b59a9bSPeter Grehan static uint8_t
636*10b59a9bSPeter Grehan vtpci_get_status(device_t dev)
637*10b59a9bSPeter Grehan {
638*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
639*10b59a9bSPeter Grehan 
640*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
641*10b59a9bSPeter Grehan 
642*10b59a9bSPeter Grehan 	return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
643*10b59a9bSPeter Grehan }
644*10b59a9bSPeter Grehan 
645*10b59a9bSPeter Grehan static void
646*10b59a9bSPeter Grehan vtpci_set_status(device_t dev, uint8_t status)
647*10b59a9bSPeter Grehan {
648*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
649*10b59a9bSPeter Grehan 
650*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
651*10b59a9bSPeter Grehan 
652*10b59a9bSPeter Grehan 	if (status != VIRTIO_CONFIG_STATUS_RESET)
653*10b59a9bSPeter Grehan 		status |= vtpci_get_status(dev);
654*10b59a9bSPeter Grehan 
655*10b59a9bSPeter Grehan 	vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
656*10b59a9bSPeter Grehan }
657*10b59a9bSPeter Grehan 
658*10b59a9bSPeter Grehan static void
659*10b59a9bSPeter Grehan vtpci_read_dev_config(device_t dev, bus_size_t offset,
660*10b59a9bSPeter Grehan     void *dst, int length)
661*10b59a9bSPeter Grehan {
662*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
663*10b59a9bSPeter Grehan 	bus_size_t off;
664*10b59a9bSPeter Grehan 	uint8_t *d;
665*10b59a9bSPeter Grehan 	int size;
666*10b59a9bSPeter Grehan 
667*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
668*10b59a9bSPeter Grehan 	off = VIRTIO_PCI_CONFIG(sc) + offset;
669*10b59a9bSPeter Grehan 
670*10b59a9bSPeter Grehan 	for (d = dst; length > 0; d += size, off += size, length -= size) {
671*10b59a9bSPeter Grehan 		if (length >= 4) {
672*10b59a9bSPeter Grehan 			size = 4;
673*10b59a9bSPeter Grehan 			*(uint32_t *)d = vtpci_read_config_4(sc, off);
674*10b59a9bSPeter Grehan 		} else if (length >= 2) {
675*10b59a9bSPeter Grehan 			size = 2;
676*10b59a9bSPeter Grehan 			*(uint16_t *)d = vtpci_read_config_2(sc, off);
677*10b59a9bSPeter Grehan 		} else {
678*10b59a9bSPeter Grehan 			size = 1;
679*10b59a9bSPeter Grehan 			*d = vtpci_read_config_1(sc, off);
680*10b59a9bSPeter Grehan 		}
681*10b59a9bSPeter Grehan 	}
682*10b59a9bSPeter Grehan }
683*10b59a9bSPeter Grehan 
684*10b59a9bSPeter Grehan static void
685*10b59a9bSPeter Grehan vtpci_write_dev_config(device_t dev, bus_size_t offset,
686*10b59a9bSPeter Grehan     void *src, int length)
687*10b59a9bSPeter Grehan {
688*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
689*10b59a9bSPeter Grehan 	bus_size_t off;
690*10b59a9bSPeter Grehan 	uint8_t *s;
691*10b59a9bSPeter Grehan 	int size;
692*10b59a9bSPeter Grehan 
693*10b59a9bSPeter Grehan 	sc = device_get_softc(dev);
694*10b59a9bSPeter Grehan 	off = VIRTIO_PCI_CONFIG(sc) + offset;
695*10b59a9bSPeter Grehan 
696*10b59a9bSPeter Grehan 	for (s = src; length > 0; s += size, off += size, length -= size) {
697*10b59a9bSPeter Grehan 		if (length >= 4) {
698*10b59a9bSPeter Grehan 			size = 4;
699*10b59a9bSPeter Grehan 			vtpci_write_config_4(sc, off, *(uint32_t *)s);
700*10b59a9bSPeter Grehan 		} else if (length >= 2) {
701*10b59a9bSPeter Grehan 			size = 2;
702*10b59a9bSPeter Grehan 			vtpci_write_config_2(sc, off, *(uint16_t *)s);
703*10b59a9bSPeter Grehan 		} else {
704*10b59a9bSPeter Grehan 			size = 1;
705*10b59a9bSPeter Grehan 			vtpci_write_config_1(sc, off, *s);
706*10b59a9bSPeter Grehan 		}
707*10b59a9bSPeter Grehan 	}
708*10b59a9bSPeter Grehan }
709*10b59a9bSPeter Grehan 
710*10b59a9bSPeter Grehan static void
711*10b59a9bSPeter Grehan vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
712*10b59a9bSPeter Grehan     uint64_t features)
713*10b59a9bSPeter Grehan {
714*10b59a9bSPeter Grehan 	device_t dev, child;
715*10b59a9bSPeter Grehan 
716*10b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
717*10b59a9bSPeter Grehan 	child = sc->vtpci_child_dev;
718*10b59a9bSPeter Grehan 
719*10b59a9bSPeter Grehan 	if (device_is_attached(child) && bootverbose == 0)
720*10b59a9bSPeter Grehan 		return;
721*10b59a9bSPeter Grehan 
722*10b59a9bSPeter Grehan 	virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
723*10b59a9bSPeter Grehan }
724*10b59a9bSPeter Grehan 
725*10b59a9bSPeter Grehan static void
726*10b59a9bSPeter Grehan vtpci_probe_and_attach_child(struct vtpci_softc *sc)
727*10b59a9bSPeter Grehan {
728*10b59a9bSPeter Grehan 	device_t dev, child;
729*10b59a9bSPeter Grehan 
730*10b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
731*10b59a9bSPeter Grehan 	child = sc->vtpci_child_dev;
732*10b59a9bSPeter Grehan 
733*10b59a9bSPeter Grehan 	if (child == NULL)
734*10b59a9bSPeter Grehan 		return;
735*10b59a9bSPeter Grehan 
736*10b59a9bSPeter Grehan 	if (device_get_state(child) != DS_NOTPRESENT)
737*10b59a9bSPeter Grehan 		return;
738*10b59a9bSPeter Grehan 
739*10b59a9bSPeter Grehan 	if (device_probe(child) != 0)
740*10b59a9bSPeter Grehan 		return;
741*10b59a9bSPeter Grehan 
742*10b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
743*10b59a9bSPeter Grehan 	if (device_attach(child) != 0) {
744*10b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
745*10b59a9bSPeter Grehan 		vtpci_reset(sc);
746*10b59a9bSPeter Grehan 		vtpci_release_child_resources(sc);
747*10b59a9bSPeter Grehan 
748*10b59a9bSPeter Grehan 		/* Reset status for future attempt. */
749*10b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
750*10b59a9bSPeter Grehan 	} else
751*10b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
752*10b59a9bSPeter Grehan }
753*10b59a9bSPeter Grehan 
754*10b59a9bSPeter Grehan static int
755*10b59a9bSPeter Grehan vtpci_alloc_interrupts(struct vtpci_softc *sc, int flags, int nvqs,
756*10b59a9bSPeter Grehan     struct vq_alloc_info *vq_info)
757*10b59a9bSPeter Grehan {
758*10b59a9bSPeter Grehan 	int i, nvectors, error;
759*10b59a9bSPeter Grehan 
760*10b59a9bSPeter Grehan 	/*
761*10b59a9bSPeter Grehan 	 * Only allocate a vector for virtqueues that are actually
762*10b59a9bSPeter Grehan 	 * expecting an interrupt.
763*10b59a9bSPeter Grehan 	 */
764*10b59a9bSPeter Grehan 	for (nvectors = 0, i = 0; i < nvqs; i++)
765*10b59a9bSPeter Grehan 		if (vq_info[i].vqai_intr != NULL)
766*10b59a9bSPeter Grehan 			nvectors++;
767*10b59a9bSPeter Grehan 
768*10b59a9bSPeter Grehan 	if (vtpci_disable_msix != 0 ||
769*10b59a9bSPeter Grehan 	    sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSIX ||
770*10b59a9bSPeter Grehan 	    flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX ||
771*10b59a9bSPeter Grehan 	    vtpci_alloc_msix(sc, nvectors) != 0) {
772*10b59a9bSPeter Grehan 		/*
773*10b59a9bSPeter Grehan 		 * Use MSI interrupts if available. Otherwise, we fallback
774*10b59a9bSPeter Grehan 		 * to legacy interrupts.
775*10b59a9bSPeter Grehan 		 */
776*10b59a9bSPeter Grehan 		if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSI) == 0 &&
777*10b59a9bSPeter Grehan 		    vtpci_alloc_msi(sc) == 0)
778*10b59a9bSPeter Grehan 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
779*10b59a9bSPeter Grehan 
780*10b59a9bSPeter Grehan 		sc->vtpci_nintr_res = 1;
781*10b59a9bSPeter Grehan 	}
782*10b59a9bSPeter Grehan 
783*10b59a9bSPeter Grehan 	error = vtpci_alloc_intr_resources(sc, nvqs, vq_info);
784*10b59a9bSPeter Grehan 
785*10b59a9bSPeter Grehan 	return (error);
786*10b59a9bSPeter Grehan }
787*10b59a9bSPeter Grehan 
788*10b59a9bSPeter Grehan static int
789*10b59a9bSPeter Grehan vtpci_alloc_intr_resources(struct vtpci_softc *sc, int nvqs,
790*10b59a9bSPeter Grehan     struct vq_alloc_info *vq_info)
791*10b59a9bSPeter Grehan {
792*10b59a9bSPeter Grehan 	device_t dev;
793*10b59a9bSPeter Grehan 	struct resource *irq;
794*10b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
795*10b59a9bSPeter Grehan 	int i, rid, flags, res_idx;
796*10b59a9bSPeter Grehan 
797*10b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
798*10b59a9bSPeter Grehan 	flags = RF_ACTIVE;
799*10b59a9bSPeter Grehan 
800*10b59a9bSPeter Grehan 	if ((sc->vtpci_flags &
801*10b59a9bSPeter Grehan 	    (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) == 0) {
802*10b59a9bSPeter Grehan 		rid = 0;
803*10b59a9bSPeter Grehan 		flags |= RF_SHAREABLE;
804*10b59a9bSPeter Grehan 	} else
805*10b59a9bSPeter Grehan 		rid = 1;
806*10b59a9bSPeter Grehan 
807*10b59a9bSPeter Grehan 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
808*10b59a9bSPeter Grehan 		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags);
809*10b59a9bSPeter Grehan 		if (irq == NULL)
810*10b59a9bSPeter Grehan 			return (ENXIO);
811*10b59a9bSPeter Grehan 
812*10b59a9bSPeter Grehan 		sc->vtpci_intr_res[i].irq = irq;
813*10b59a9bSPeter Grehan 		sc->vtpci_intr_res[i].rid = rid++;
814*10b59a9bSPeter Grehan 	}
815*10b59a9bSPeter Grehan 
816*10b59a9bSPeter Grehan 	/*
817*10b59a9bSPeter Grehan 	 * Map the virtqueue into the correct index in vq_intr_res[]. Note the
818*10b59a9bSPeter Grehan 	 * first index is reserved for configuration changes notifications.
819*10b59a9bSPeter Grehan 	 */
820*10b59a9bSPeter Grehan 	for (i = 0, res_idx = 1; i < nvqs; i++) {
821*10b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[i];
822*10b59a9bSPeter Grehan 
823*10b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
824*10b59a9bSPeter Grehan 			if (vq_info[i].vqai_intr == NULL)
825*10b59a9bSPeter Grehan 				vqx->ires_idx = -1;
826*10b59a9bSPeter Grehan 			else if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
827*10b59a9bSPeter Grehan 				vqx->ires_idx = res_idx;
828*10b59a9bSPeter Grehan 			else
829*10b59a9bSPeter Grehan 				vqx->ires_idx = res_idx++;
830*10b59a9bSPeter Grehan 		} else
831*10b59a9bSPeter Grehan 			vqx->ires_idx = -1;
832*10b59a9bSPeter Grehan 	}
833*10b59a9bSPeter Grehan 
834*10b59a9bSPeter Grehan 	return (0);
835*10b59a9bSPeter Grehan }
836*10b59a9bSPeter Grehan 
837*10b59a9bSPeter Grehan static int
838*10b59a9bSPeter Grehan vtpci_alloc_msi(struct vtpci_softc *sc)
839*10b59a9bSPeter Grehan {
840*10b59a9bSPeter Grehan 	device_t dev;
841*10b59a9bSPeter Grehan 	int nmsi, cnt;
842*10b59a9bSPeter Grehan 
843*10b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
844*10b59a9bSPeter Grehan 	nmsi = pci_msi_count(dev);
845*10b59a9bSPeter Grehan 
846*10b59a9bSPeter Grehan 	if (nmsi < 1)
847*10b59a9bSPeter Grehan 		return (1);
848*10b59a9bSPeter Grehan 
849*10b59a9bSPeter Grehan 	cnt = 1;
850*10b59a9bSPeter Grehan 	if (pci_alloc_msi(dev, &cnt) == 0 && cnt == 1)
851*10b59a9bSPeter Grehan 		return (0);
852*10b59a9bSPeter Grehan 
853*10b59a9bSPeter Grehan 	return (1);
854*10b59a9bSPeter Grehan }
855*10b59a9bSPeter Grehan 
856*10b59a9bSPeter Grehan static int
857*10b59a9bSPeter Grehan vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors)
858*10b59a9bSPeter Grehan {
859*10b59a9bSPeter Grehan 	device_t dev;
860*10b59a9bSPeter Grehan 	int nmsix, cnt, required;
861*10b59a9bSPeter Grehan 
862*10b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
863*10b59a9bSPeter Grehan 
864*10b59a9bSPeter Grehan 	nmsix = pci_msix_count(dev);
865*10b59a9bSPeter Grehan 	if (nmsix < 1)
866*10b59a9bSPeter Grehan 		return (1);
867*10b59a9bSPeter Grehan 
868*10b59a9bSPeter Grehan 	/* An additional vector is needed for the config changes. */
869*10b59a9bSPeter Grehan 	required = nvectors + 1;
870*10b59a9bSPeter Grehan 	if (nmsix >= required) {
871*10b59a9bSPeter Grehan 		cnt = required;
872*10b59a9bSPeter Grehan 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required)
873*10b59a9bSPeter Grehan 			goto out;
874*10b59a9bSPeter Grehan 
875*10b59a9bSPeter Grehan 		pci_release_msi(dev);
876*10b59a9bSPeter Grehan 	}
877*10b59a9bSPeter Grehan 
878*10b59a9bSPeter Grehan 	/* Attempt shared MSIX configuration. */
879*10b59a9bSPeter Grehan 	required = 2;
880*10b59a9bSPeter Grehan 	if (nmsix >= required) {
881*10b59a9bSPeter Grehan 		cnt = required;
882*10b59a9bSPeter Grehan 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) {
883*10b59a9bSPeter Grehan 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_SHARED_MSIX;
884*10b59a9bSPeter Grehan 			goto out;
885*10b59a9bSPeter Grehan 		}
886*10b59a9bSPeter Grehan 
887*10b59a9bSPeter Grehan 		pci_release_msi(dev);
888*10b59a9bSPeter Grehan 	}
889*10b59a9bSPeter Grehan 
890*10b59a9bSPeter Grehan 	return (1);
891*10b59a9bSPeter Grehan 
892*10b59a9bSPeter Grehan out:
893*10b59a9bSPeter Grehan 	sc->vtpci_nintr_res = required;
894*10b59a9bSPeter Grehan 	sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
895*10b59a9bSPeter Grehan 
896*10b59a9bSPeter Grehan 	if (bootverbose) {
897*10b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
898*10b59a9bSPeter Grehan 			device_printf(dev, "using shared virtqueue MSIX\n");
899*10b59a9bSPeter Grehan 		else
900*10b59a9bSPeter Grehan 			device_printf(dev, "using per virtqueue MSIX\n");
901*10b59a9bSPeter Grehan 	}
902*10b59a9bSPeter Grehan 
903*10b59a9bSPeter Grehan 	return (0);
904*10b59a9bSPeter Grehan }
905*10b59a9bSPeter Grehan 
906*10b59a9bSPeter Grehan static int
907*10b59a9bSPeter Grehan vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
908*10b59a9bSPeter Grehan {
909*10b59a9bSPeter Grehan 	device_t dev;
910*10b59a9bSPeter Grehan 	uint16_t vector;
911*10b59a9bSPeter Grehan 
912*10b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
913*10b59a9bSPeter Grehan 
914*10b59a9bSPeter Grehan 	if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
915*10b59a9bSPeter Grehan 	    offset != VIRTIO_MSI_QUEUE_VECTOR)
916*10b59a9bSPeter Grehan 		return (EINVAL);
917*10b59a9bSPeter Grehan 
918*10b59a9bSPeter Grehan 	if (res_idx != -1) {
919*10b59a9bSPeter Grehan 		/* Map from rid to host vector. */
920*10b59a9bSPeter Grehan 		vector = sc->vtpci_intr_res[res_idx].rid - 1;
921*10b59a9bSPeter Grehan 	} else
922*10b59a9bSPeter Grehan 		vector = VIRTIO_MSI_NO_VECTOR;
923*10b59a9bSPeter Grehan 
924*10b59a9bSPeter Grehan 	/* The first resource is special; make sure it is used correctly. */
925*10b59a9bSPeter Grehan 	if (res_idx == 0) {
926*10b59a9bSPeter Grehan 		KASSERT(vector == 0, ("unexpected config vector"));
927*10b59a9bSPeter Grehan 		KASSERT(offset == VIRTIO_MSI_CONFIG_VECTOR,
928*10b59a9bSPeter Grehan 		    ("unexpected config offset"));
929*10b59a9bSPeter Grehan 	}
930*10b59a9bSPeter Grehan 
931*10b59a9bSPeter Grehan 	vtpci_write_config_2(sc, offset, vector);
932*10b59a9bSPeter Grehan 
933*10b59a9bSPeter Grehan 	if (vtpci_read_config_2(sc, offset) != vector) {
934*10b59a9bSPeter Grehan 		device_printf(dev, "insufficient host resources for "
935*10b59a9bSPeter Grehan 		    "MSIX interrupts\n");
936*10b59a9bSPeter Grehan 		return (ENODEV);
937*10b59a9bSPeter Grehan 	}
938*10b59a9bSPeter Grehan 
939*10b59a9bSPeter Grehan 	return (0);
940*10b59a9bSPeter Grehan }
941*10b59a9bSPeter Grehan 
942*10b59a9bSPeter Grehan static void
943*10b59a9bSPeter Grehan vtpci_free_interrupts(struct vtpci_softc *sc)
944*10b59a9bSPeter Grehan {
945*10b59a9bSPeter Grehan 	device_t dev;
946*10b59a9bSPeter Grehan 	struct vtpci_intr_resource *ires;
947*10b59a9bSPeter Grehan 	int i;
948*10b59a9bSPeter Grehan 
949*10b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
950*10b59a9bSPeter Grehan 	sc->vtpci_nintr_res = 0;
951*10b59a9bSPeter Grehan 
952*10b59a9bSPeter Grehan 	if (sc->vtpci_flags & (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) {
953*10b59a9bSPeter Grehan 		pci_release_msi(dev);
954*10b59a9bSPeter Grehan 		sc->vtpci_flags &= ~(VIRTIO_PCI_FLAG_MSI |
955*10b59a9bSPeter Grehan 		    VIRTIO_PCI_FLAG_MSIX | VIRTIO_PCI_FLAG_SHARED_MSIX);
956*10b59a9bSPeter Grehan 	}
957*10b59a9bSPeter Grehan 
958*10b59a9bSPeter Grehan 	for (i = 0; i < 1 + VIRTIO_MAX_VIRTQUEUES; i++) {
959*10b59a9bSPeter Grehan 		ires = &sc->vtpci_intr_res[i];
960*10b59a9bSPeter Grehan 
961*10b59a9bSPeter Grehan 		if (ires->intrhand != NULL) {
962*10b59a9bSPeter Grehan 			bus_teardown_intr(dev, ires->irq, ires->intrhand);
963*10b59a9bSPeter Grehan 			ires->intrhand = NULL;
964*10b59a9bSPeter Grehan 		}
965*10b59a9bSPeter Grehan 
966*10b59a9bSPeter Grehan 		if (ires->irq != NULL) {
967*10b59a9bSPeter Grehan 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
968*10b59a9bSPeter Grehan 			    ires->irq);
969*10b59a9bSPeter Grehan 			ires->irq = NULL;
970*10b59a9bSPeter Grehan 		}
971*10b59a9bSPeter Grehan 
972*10b59a9bSPeter Grehan 		ires->rid = -1;
973*10b59a9bSPeter Grehan 	}
974*10b59a9bSPeter Grehan }
975*10b59a9bSPeter Grehan 
976*10b59a9bSPeter Grehan static void
977*10b59a9bSPeter Grehan vtpci_free_virtqueues(struct vtpci_softc *sc)
978*10b59a9bSPeter Grehan {
979*10b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
980*10b59a9bSPeter Grehan 	int i;
981*10b59a9bSPeter Grehan 
982*10b59a9bSPeter Grehan 	sc->vtpci_nvqs = 0;
983*10b59a9bSPeter Grehan 
984*10b59a9bSPeter Grehan 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
985*10b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[i];
986*10b59a9bSPeter Grehan 
987*10b59a9bSPeter Grehan 		if (vqx->vq != NULL) {
988*10b59a9bSPeter Grehan 			virtqueue_free(vqx->vq);
989*10b59a9bSPeter Grehan 			vqx->vq = NULL;
990*10b59a9bSPeter Grehan 		}
991*10b59a9bSPeter Grehan 	}
992*10b59a9bSPeter Grehan }
993*10b59a9bSPeter Grehan 
994*10b59a9bSPeter Grehan static void
995*10b59a9bSPeter Grehan vtpci_release_child_resources(struct vtpci_softc *sc)
996*10b59a9bSPeter Grehan {
997*10b59a9bSPeter Grehan 
998*10b59a9bSPeter Grehan 	vtpci_free_interrupts(sc);
999*10b59a9bSPeter Grehan 	vtpci_free_virtqueues(sc);
1000*10b59a9bSPeter Grehan }
1001*10b59a9bSPeter Grehan 
1002*10b59a9bSPeter Grehan static void
1003*10b59a9bSPeter Grehan vtpci_reset(struct vtpci_softc *sc)
1004*10b59a9bSPeter Grehan {
1005*10b59a9bSPeter Grehan 
1006*10b59a9bSPeter Grehan 	/*
1007*10b59a9bSPeter Grehan 	 * Setting the status to RESET sets the host device to
1008*10b59a9bSPeter Grehan 	 * the original, uninitialized state.
1009*10b59a9bSPeter Grehan 	 */
1010*10b59a9bSPeter Grehan 	vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
1011*10b59a9bSPeter Grehan }
1012*10b59a9bSPeter Grehan 
1013*10b59a9bSPeter Grehan static int
1014*10b59a9bSPeter Grehan vtpci_legacy_intr(void *xsc)
1015*10b59a9bSPeter Grehan {
1016*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
1017*10b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
1018*10b59a9bSPeter Grehan 	int i;
1019*10b59a9bSPeter Grehan 	uint8_t isr;
1020*10b59a9bSPeter Grehan 
1021*10b59a9bSPeter Grehan 	sc = xsc;
1022*10b59a9bSPeter Grehan 	vqx = &sc->vtpci_vqx[0];
1023*10b59a9bSPeter Grehan 
1024*10b59a9bSPeter Grehan 	/* Reading the ISR also clears it. */
1025*10b59a9bSPeter Grehan 	isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
1026*10b59a9bSPeter Grehan 
1027*10b59a9bSPeter Grehan 	if (isr & VIRTIO_PCI_ISR_CONFIG)
1028*10b59a9bSPeter Grehan 		vtpci_config_intr(sc);
1029*10b59a9bSPeter Grehan 
1030*10b59a9bSPeter Grehan 	if (isr & VIRTIO_PCI_ISR_INTR)
1031*10b59a9bSPeter Grehan 		for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
1032*10b59a9bSPeter Grehan 			virtqueue_intr(vqx->vq);
1033*10b59a9bSPeter Grehan 
1034*10b59a9bSPeter Grehan 	return (isr ? FILTER_HANDLED : FILTER_STRAY);
1035*10b59a9bSPeter Grehan }
1036*10b59a9bSPeter Grehan 
1037*10b59a9bSPeter Grehan static int
1038*10b59a9bSPeter Grehan vtpci_vq_shared_intr(void *xsc)
1039*10b59a9bSPeter Grehan {
1040*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
1041*10b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
1042*10b59a9bSPeter Grehan 	int i, rc;
1043*10b59a9bSPeter Grehan 
1044*10b59a9bSPeter Grehan 	rc = 0;
1045*10b59a9bSPeter Grehan 	sc = xsc;
1046*10b59a9bSPeter Grehan 	vqx = &sc->vtpci_vqx[0];
1047*10b59a9bSPeter Grehan 
1048*10b59a9bSPeter Grehan 	for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
1049*10b59a9bSPeter Grehan 		rc |= virtqueue_intr(vqx->vq);
1050*10b59a9bSPeter Grehan 
1051*10b59a9bSPeter Grehan 	return (rc ? FILTER_HANDLED : FILTER_STRAY);
1052*10b59a9bSPeter Grehan }
1053*10b59a9bSPeter Grehan 
1054*10b59a9bSPeter Grehan static int
1055*10b59a9bSPeter Grehan vtpci_vq_intr(void *xvq)
1056*10b59a9bSPeter Grehan {
1057*10b59a9bSPeter Grehan 	struct virtqueue *vq;
1058*10b59a9bSPeter Grehan 	int rc;
1059*10b59a9bSPeter Grehan 
1060*10b59a9bSPeter Grehan 	vq = xvq;
1061*10b59a9bSPeter Grehan 	rc = virtqueue_intr(vq);
1062*10b59a9bSPeter Grehan 
1063*10b59a9bSPeter Grehan 	return (rc ? FILTER_HANDLED : FILTER_STRAY);
1064*10b59a9bSPeter Grehan }
1065*10b59a9bSPeter Grehan 
1066*10b59a9bSPeter Grehan static int
1067*10b59a9bSPeter Grehan vtpci_config_intr(void *xsc)
1068*10b59a9bSPeter Grehan {
1069*10b59a9bSPeter Grehan 	struct vtpci_softc *sc;
1070*10b59a9bSPeter Grehan 	device_t child;
1071*10b59a9bSPeter Grehan 	int rc;
1072*10b59a9bSPeter Grehan 
1073*10b59a9bSPeter Grehan 	rc = 0;
1074*10b59a9bSPeter Grehan 	sc = xsc;
1075*10b59a9bSPeter Grehan 	child = sc->vtpci_child_dev;
1076*10b59a9bSPeter Grehan 
1077*10b59a9bSPeter Grehan 	if (child != NULL)
1078*10b59a9bSPeter Grehan 		rc = VIRTIO_CONFIG_CHANGE(child);
1079*10b59a9bSPeter Grehan 
1080*10b59a9bSPeter Grehan 	return (rc ? FILTER_HANDLED : FILTER_STRAY);
1081*10b59a9bSPeter Grehan }
1082