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