xref: /freebsd-src/sys/dev/virtio/mmio/virtio_mmio.c (revision 11a9117871e6037ae7b8011b243939322efce569)
1c141c5c6SRuslan Bukin /*-
2c141c5c6SRuslan Bukin  * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
34dbff239SAndrew Turner  * Copyright (c) 2014 The FreeBSD Foundation
4c141c5c6SRuslan Bukin  * All rights reserved.
5c141c5c6SRuslan Bukin  *
6c141c5c6SRuslan Bukin  * This software was developed by SRI International and the University of
7c141c5c6SRuslan Bukin  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
8c141c5c6SRuslan Bukin  * ("CTSRD"), as part of the DARPA CRASH research programme.
9c141c5c6SRuslan Bukin  *
104dbff239SAndrew Turner  * Portions of this software were developed by Andrew Turner
114dbff239SAndrew Turner  * under sponsorship from the FreeBSD Foundation.
124dbff239SAndrew Turner  *
13c141c5c6SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
14c141c5c6SRuslan Bukin  * modification, are permitted provided that the following conditions
15c141c5c6SRuslan Bukin  * are met:
16c141c5c6SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
17c141c5c6SRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
18c141c5c6SRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
19c141c5c6SRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
20c141c5c6SRuslan Bukin  *    documentation and/or other materials provided with the distribution.
21c141c5c6SRuslan Bukin  *
22c141c5c6SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23c141c5c6SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24c141c5c6SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25c141c5c6SRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26c141c5c6SRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27c141c5c6SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28c141c5c6SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29c141c5c6SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30c141c5c6SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31c141c5c6SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32c141c5c6SRuslan Bukin  * SUCH DAMAGE.
33c141c5c6SRuslan Bukin  */
34c141c5c6SRuslan Bukin 
35c141c5c6SRuslan Bukin /*
36c141c5c6SRuslan Bukin  * VirtIO MMIO interface.
37c141c5c6SRuslan Bukin  * This driver is heavily based on VirtIO PCI interface driver.
38c141c5c6SRuslan Bukin  */
39c141c5c6SRuslan Bukin 
40c141c5c6SRuslan Bukin #include <sys/param.h>
41c141c5c6SRuslan Bukin #include <sys/systm.h>
42c141c5c6SRuslan Bukin #include <sys/bus.h>
43c141c5c6SRuslan Bukin #include <sys/kernel.h>
44c141c5c6SRuslan Bukin #include <sys/module.h>
45c141c5c6SRuslan Bukin #include <sys/malloc.h>
46c141c5c6SRuslan Bukin #include <sys/rman.h>
47046096d5SJessica Clarke #include <sys/endian.h>
48c141c5c6SRuslan Bukin 
49c141c5c6SRuslan Bukin #include <machine/bus.h>
50c141c5c6SRuslan Bukin #include <machine/resource.h>
51c141c5c6SRuslan Bukin 
52c141c5c6SRuslan Bukin #include <dev/virtio/virtio.h>
53c141c5c6SRuslan Bukin #include <dev/virtio/virtqueue.h>
54c141c5c6SRuslan Bukin #include <dev/virtio/mmio/virtio_mmio.h>
55c141c5c6SRuslan Bukin 
56c141c5c6SRuslan Bukin #include "virtio_mmio_if.h"
57c141c5c6SRuslan Bukin #include "virtio_bus_if.h"
58c141c5c6SRuslan Bukin #include "virtio_if.h"
59c141c5c6SRuslan Bukin 
60c141c5c6SRuslan Bukin struct vtmmio_virtqueue {
61c141c5c6SRuslan Bukin 	struct virtqueue	*vtv_vq;
62c141c5c6SRuslan Bukin 	int			 vtv_no_intr;
63c141c5c6SRuslan Bukin };
64c141c5c6SRuslan Bukin 
65c141c5c6SRuslan Bukin static int	vtmmio_detach(device_t);
66c141c5c6SRuslan Bukin static int	vtmmio_suspend(device_t);
67c141c5c6SRuslan Bukin static int	vtmmio_resume(device_t);
68c141c5c6SRuslan Bukin static int	vtmmio_shutdown(device_t);
69c141c5c6SRuslan Bukin static void	vtmmio_driver_added(device_t, driver_t *);
70c141c5c6SRuslan Bukin static void	vtmmio_child_detached(device_t, device_t);
71c141c5c6SRuslan Bukin static int	vtmmio_read_ivar(device_t, device_t, int, uintptr_t *);
72c141c5c6SRuslan Bukin static int	vtmmio_write_ivar(device_t, device_t, int, uintptr_t);
73c141c5c6SRuslan Bukin static uint64_t	vtmmio_negotiate_features(device_t, uint64_t);
74926cedd9SBryan Venteicher static int	vtmmio_finalize_features(device_t);
75ccb576a8SMina Galić static bool	vtmmio_with_feature(device_t, uint64_t);
76046096d5SJessica Clarke static void	vtmmio_set_virtqueue(struct vtmmio_softc *sc,
77046096d5SJessica Clarke 		    struct virtqueue *vq, uint32_t size);
78180c0240SMina Galić static int	vtmmio_alloc_virtqueues(device_t, int,
79c141c5c6SRuslan Bukin 		    struct vq_alloc_info *);
80c141c5c6SRuslan Bukin static int	vtmmio_setup_intr(device_t, enum intr_type);
81c141c5c6SRuslan Bukin static void	vtmmio_stop(device_t);
82156b97faSRuslan Bukin static void	vtmmio_poll(device_t);
83c141c5c6SRuslan Bukin static int	vtmmio_reinit(device_t, uint64_t);
84c141c5c6SRuslan Bukin static void	vtmmio_reinit_complete(device_t);
859da9560cSBryan Venteicher static void	vtmmio_notify_virtqueue(device_t, uint16_t, bus_size_t);
86926cedd9SBryan Venteicher static int	vtmmio_config_generation(device_t);
87c141c5c6SRuslan Bukin static uint8_t	vtmmio_get_status(device_t);
88c141c5c6SRuslan Bukin static void	vtmmio_set_status(device_t, uint8_t);
89c141c5c6SRuslan Bukin static void	vtmmio_read_dev_config(device_t, bus_size_t, void *, int);
90926cedd9SBryan Venteicher static uint64_t	vtmmio_read_dev_config_8(struct vtmmio_softc *, bus_size_t);
916c4f9516SAlex Richardson static void	vtmmio_write_dev_config(device_t, bus_size_t, const void *, int);
92c141c5c6SRuslan Bukin static void	vtmmio_describe_features(struct vtmmio_softc *, const char *,
93c141c5c6SRuslan Bukin 		    uint64_t);
94c141c5c6SRuslan Bukin static void	vtmmio_probe_and_attach_child(struct vtmmio_softc *);
95c141c5c6SRuslan Bukin static int	vtmmio_reinit_virtqueue(struct vtmmio_softc *, int);
96c141c5c6SRuslan Bukin static void	vtmmio_free_interrupts(struct vtmmio_softc *);
97c141c5c6SRuslan Bukin static void	vtmmio_free_virtqueues(struct vtmmio_softc *);
98c141c5c6SRuslan Bukin static void	vtmmio_release_child_resources(struct vtmmio_softc *);
99c141c5c6SRuslan Bukin static void	vtmmio_reset(struct vtmmio_softc *);
100c141c5c6SRuslan Bukin static void	vtmmio_select_virtqueue(struct vtmmio_softc *, int);
101c141c5c6SRuslan Bukin static void	vtmmio_vq_intr(void *);
102c141c5c6SRuslan Bukin 
103c141c5c6SRuslan Bukin /*
104c141c5c6SRuslan Bukin  * I/O port read/write wrappers.
105c141c5c6SRuslan Bukin  */
106c141c5c6SRuslan Bukin #define vtmmio_write_config_1(sc, o, v)				\
1074dbff239SAndrew Turner do {								\
108116b8d2bSRuslan Bukin 	if (sc->platform != NULL)				\
109116b8d2bSRuslan Bukin 		VIRTIO_MMIO_PREWRITE(sc->platform, (o), (v));	\
110c141c5c6SRuslan Bukin 	bus_write_1((sc)->res[0], (o), (v)); 			\
1114dbff239SAndrew Turner 	if (sc->platform != NULL)				\
112a8098016SRuslan Bukin 		VIRTIO_MMIO_NOTE(sc->platform, (o), (v));	\
1134dbff239SAndrew Turner } while (0)
114c141c5c6SRuslan Bukin #define vtmmio_write_config_2(sc, o, v)				\
1154dbff239SAndrew Turner do {								\
116116b8d2bSRuslan Bukin 	if (sc->platform != NULL)				\
117116b8d2bSRuslan Bukin 		VIRTIO_MMIO_PREWRITE(sc->platform, (o), (v));	\
118c141c5c6SRuslan Bukin 	bus_write_2((sc)->res[0], (o), (v));			\
1194dbff239SAndrew Turner 	if (sc->platform != NULL)				\
120a8098016SRuslan Bukin 		VIRTIO_MMIO_NOTE(sc->platform, (o), (v));	\
1214dbff239SAndrew Turner } while (0)
122c141c5c6SRuslan Bukin #define vtmmio_write_config_4(sc, o, v)				\
1234dbff239SAndrew Turner do {								\
124116b8d2bSRuslan Bukin 	if (sc->platform != NULL)				\
125116b8d2bSRuslan Bukin 		VIRTIO_MMIO_PREWRITE(sc->platform, (o), (v));	\
126c141c5c6SRuslan Bukin 	bus_write_4((sc)->res[0], (o), (v));			\
1274dbff239SAndrew Turner 	if (sc->platform != NULL)				\
128a8098016SRuslan Bukin 		VIRTIO_MMIO_NOTE(sc->platform, (o), (v));	\
1294dbff239SAndrew Turner } while (0)
130c141c5c6SRuslan Bukin 
131c141c5c6SRuslan Bukin #define vtmmio_read_config_1(sc, o) \
132c141c5c6SRuslan Bukin 	bus_read_1((sc)->res[0], (o))
133c141c5c6SRuslan Bukin #define vtmmio_read_config_2(sc, o) \
134c141c5c6SRuslan Bukin 	bus_read_2((sc)->res[0], (o))
135c141c5c6SRuslan Bukin #define vtmmio_read_config_4(sc, o) \
136c141c5c6SRuslan Bukin 	bus_read_4((sc)->res[0], (o))
137c141c5c6SRuslan Bukin 
138c141c5c6SRuslan Bukin static device_method_t vtmmio_methods[] = {
139c141c5c6SRuslan Bukin 	/* Device interface. */
140c141c5c6SRuslan Bukin 	DEVMETHOD(device_attach,		  vtmmio_attach),
141c141c5c6SRuslan Bukin 	DEVMETHOD(device_detach,		  vtmmio_detach),
142c141c5c6SRuslan Bukin 	DEVMETHOD(device_suspend,		  vtmmio_suspend),
143c141c5c6SRuslan Bukin 	DEVMETHOD(device_resume,		  vtmmio_resume),
144c141c5c6SRuslan Bukin 	DEVMETHOD(device_shutdown,		  vtmmio_shutdown),
145c141c5c6SRuslan Bukin 
146c141c5c6SRuslan Bukin 	/* Bus interface. */
147c141c5c6SRuslan Bukin 	DEVMETHOD(bus_driver_added,		  vtmmio_driver_added),
148c141c5c6SRuslan Bukin 	DEVMETHOD(bus_child_detached,		  vtmmio_child_detached),
149ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_pnpinfo,		  virtio_child_pnpinfo),
150c141c5c6SRuslan Bukin 	DEVMETHOD(bus_read_ivar,		  vtmmio_read_ivar),
151c141c5c6SRuslan Bukin 	DEVMETHOD(bus_write_ivar,		  vtmmio_write_ivar),
152c141c5c6SRuslan Bukin 
153c141c5c6SRuslan Bukin 	/* VirtIO bus interface. */
154c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_negotiate_features,  vtmmio_negotiate_features),
155926cedd9SBryan Venteicher 	DEVMETHOD(virtio_bus_finalize_features,	  vtmmio_finalize_features),
156c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_with_feature,	  vtmmio_with_feature),
157c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_alloc_virtqueues,	  vtmmio_alloc_virtqueues),
158c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_setup_intr,	  vtmmio_setup_intr),
159c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_stop,		  vtmmio_stop),
160156b97faSRuslan Bukin 	DEVMETHOD(virtio_bus_poll,		  vtmmio_poll),
161c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_reinit,		  vtmmio_reinit),
162c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_reinit_complete,	  vtmmio_reinit_complete),
163c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_notify_vq,		  vtmmio_notify_virtqueue),
164926cedd9SBryan Venteicher 	DEVMETHOD(virtio_bus_config_generation,	  vtmmio_config_generation),
165c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_read_device_config,  vtmmio_read_dev_config),
166c141c5c6SRuslan Bukin 	DEVMETHOD(virtio_bus_write_device_config, vtmmio_write_dev_config),
167c141c5c6SRuslan Bukin 
168c141c5c6SRuslan Bukin 	DEVMETHOD_END
169c141c5c6SRuslan Bukin };
170c141c5c6SRuslan Bukin 
171a3609b82SAndrew Turner DEFINE_CLASS_0(virtio_mmio, vtmmio_driver, vtmmio_methods,
172a3609b82SAndrew Turner     sizeof(struct vtmmio_softc));
173c141c5c6SRuslan Bukin 
174c141c5c6SRuslan Bukin MODULE_VERSION(virtio_mmio, 1);
175c141c5c6SRuslan Bukin 
176be79a2c6SJessica Clarke int
177be79a2c6SJessica Clarke vtmmio_probe(device_t dev)
178be79a2c6SJessica Clarke {
179be79a2c6SJessica Clarke 	struct vtmmio_softc *sc;
180be79a2c6SJessica Clarke 	int rid;
181be79a2c6SJessica Clarke 	uint32_t magic, version;
182be79a2c6SJessica Clarke 
183be79a2c6SJessica Clarke 	sc = device_get_softc(dev);
184be79a2c6SJessica Clarke 
185be79a2c6SJessica Clarke 	rid = 0;
186be79a2c6SJessica Clarke 	sc->res[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
187be79a2c6SJessica Clarke 	    RF_ACTIVE);
188be79a2c6SJessica Clarke 	if (sc->res[0] == NULL) {
189be79a2c6SJessica Clarke 		device_printf(dev, "Cannot allocate memory window.\n");
190be79a2c6SJessica Clarke 		return (ENXIO);
191be79a2c6SJessica Clarke 	}
192be79a2c6SJessica Clarke 
193be79a2c6SJessica Clarke 	magic = vtmmio_read_config_4(sc, VIRTIO_MMIO_MAGIC_VALUE);
194be79a2c6SJessica Clarke 	if (magic != VIRTIO_MMIO_MAGIC_VIRT) {
195be79a2c6SJessica Clarke 		device_printf(dev, "Bad magic value %#x\n", magic);
196be79a2c6SJessica Clarke 		bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]);
197be79a2c6SJessica Clarke 		return (ENXIO);
198be79a2c6SJessica Clarke 	}
199be79a2c6SJessica Clarke 
200be79a2c6SJessica Clarke 	version = vtmmio_read_config_4(sc, VIRTIO_MMIO_VERSION);
201be79a2c6SJessica Clarke 	if (version < 1 || version > 2) {
202be79a2c6SJessica Clarke 		device_printf(dev, "Unsupported version: %#x\n", version);
203be79a2c6SJessica Clarke 		bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]);
204be79a2c6SJessica Clarke 		return (ENXIO);
205be79a2c6SJessica Clarke 	}
206be79a2c6SJessica Clarke 
207be79a2c6SJessica Clarke 	if (vtmmio_read_config_4(sc, VIRTIO_MMIO_DEVICE_ID) == 0) {
208be79a2c6SJessica Clarke 		bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]);
209be79a2c6SJessica Clarke 		return (ENXIO);
210be79a2c6SJessica Clarke 	}
211be79a2c6SJessica Clarke 
212be79a2c6SJessica Clarke 	bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]);
213be79a2c6SJessica Clarke 
214be79a2c6SJessica Clarke 	device_set_desc(dev, "VirtIO MMIO adapter");
215be79a2c6SJessica Clarke 	return (BUS_PROBE_DEFAULT);
216be79a2c6SJessica Clarke }
217be79a2c6SJessica Clarke 
218c141c5c6SRuslan Bukin static int
219c141c5c6SRuslan Bukin vtmmio_setup_intr(device_t dev, enum intr_type type)
220c141c5c6SRuslan Bukin {
221c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
222c141c5c6SRuslan Bukin 	int rid;
223c141c5c6SRuslan Bukin 	int err;
224c141c5c6SRuslan Bukin 
225c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
226c141c5c6SRuslan Bukin 
2274dbff239SAndrew Turner 	if (sc->platform != NULL) {
228c141c5c6SRuslan Bukin 		err = VIRTIO_MMIO_SETUP_INTR(sc->platform, sc->dev,
229c141c5c6SRuslan Bukin 					vtmmio_vq_intr, sc);
230c141c5c6SRuslan Bukin 		if (err == 0) {
231c141c5c6SRuslan Bukin 			/* Okay we have backend-specific interrupts */
232c141c5c6SRuslan Bukin 			return (0);
233c141c5c6SRuslan Bukin 		}
2344dbff239SAndrew Turner 	}
235c141c5c6SRuslan Bukin 
236c141c5c6SRuslan Bukin 	rid = 0;
237c141c5c6SRuslan Bukin 	sc->res[1] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
238c141c5c6SRuslan Bukin 		RF_ACTIVE);
239c141c5c6SRuslan Bukin 	if (!sc->res[1]) {
240c141c5c6SRuslan Bukin 		device_printf(dev, "Can't allocate interrupt\n");
241c141c5c6SRuslan Bukin 		return (ENXIO);
242c141c5c6SRuslan Bukin 	}
243c141c5c6SRuslan Bukin 
24455cd9324SKristof Provost 	if (bus_setup_intr(dev, sc->res[1], type | INTR_MPSAFE,
245c141c5c6SRuslan Bukin 		NULL, vtmmio_vq_intr, sc, &sc->ih)) {
246c141c5c6SRuslan Bukin 		device_printf(dev, "Can't setup the interrupt\n");
247c141c5c6SRuslan Bukin 		return (ENXIO);
248c141c5c6SRuslan Bukin 	}
249c141c5c6SRuslan Bukin 
250c141c5c6SRuslan Bukin 	return (0);
251c141c5c6SRuslan Bukin }
252c141c5c6SRuslan Bukin 
253a3609b82SAndrew Turner int
254c141c5c6SRuslan Bukin vtmmio_attach(device_t dev)
255c141c5c6SRuslan Bukin {
256c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
257c141c5c6SRuslan Bukin 	device_t child;
258c141c5c6SRuslan Bukin 	int rid;
259c141c5c6SRuslan Bukin 
260c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
261c141c5c6SRuslan Bukin 	sc->dev = dev;
262c141c5c6SRuslan Bukin 
263c141c5c6SRuslan Bukin 	rid = 0;
264c141c5c6SRuslan Bukin 	sc->res[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
265c141c5c6SRuslan Bukin 			RF_ACTIVE);
2660e72f2c5SJessica Clarke 	if (sc->res[0] == NULL) {
267c141c5c6SRuslan Bukin 		device_printf(dev, "Cannot allocate memory window.\n");
268c141c5c6SRuslan Bukin 		return (ENXIO);
269c141c5c6SRuslan Bukin 	}
270c141c5c6SRuslan Bukin 
271046096d5SJessica Clarke 	sc->vtmmio_version = vtmmio_read_config_4(sc, VIRTIO_MMIO_VERSION);
272046096d5SJessica Clarke 
273c141c5c6SRuslan Bukin 	vtmmio_reset(sc);
274c141c5c6SRuslan Bukin 
275c141c5c6SRuslan Bukin 	/* Tell the host we've noticed this device. */
276c141c5c6SRuslan Bukin 	vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
277c141c5c6SRuslan Bukin 
278c141c5c6SRuslan Bukin 	if ((child = device_add_child(dev, NULL, -1)) == NULL) {
279c141c5c6SRuslan Bukin 		device_printf(dev, "Cannot create child device.\n");
280c141c5c6SRuslan Bukin 		vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
281c141c5c6SRuslan Bukin 		vtmmio_detach(dev);
282c141c5c6SRuslan Bukin 		return (ENOMEM);
283c141c5c6SRuslan Bukin 	}
284c141c5c6SRuslan Bukin 
285c141c5c6SRuslan Bukin 	sc->vtmmio_child_dev = child;
286c141c5c6SRuslan Bukin 	vtmmio_probe_and_attach_child(sc);
287c141c5c6SRuslan Bukin 
288c141c5c6SRuslan Bukin 	return (0);
289c141c5c6SRuslan Bukin }
290c141c5c6SRuslan Bukin 
291c141c5c6SRuslan Bukin static int
292c141c5c6SRuslan Bukin vtmmio_detach(device_t dev)
293c141c5c6SRuslan Bukin {
294c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
295c141c5c6SRuslan Bukin 	int error;
296c141c5c6SRuslan Bukin 
297c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
298c141c5c6SRuslan Bukin 
299*11a91178SJohn Baldwin 	error = bus_generic_detach(dev);
300c141c5c6SRuslan Bukin 	if (error)
301c141c5c6SRuslan Bukin 		return (error);
302c141c5c6SRuslan Bukin 
303c141c5c6SRuslan Bukin 	vtmmio_reset(sc);
304c141c5c6SRuslan Bukin 
305c141c5c6SRuslan Bukin 	if (sc->res[0] != NULL) {
306c141c5c6SRuslan Bukin 		bus_release_resource(dev, SYS_RES_MEMORY, 0,
307c141c5c6SRuslan Bukin 		    sc->res[0]);
308c141c5c6SRuslan Bukin 		sc->res[0] = NULL;
309c141c5c6SRuslan Bukin 	}
310c141c5c6SRuslan Bukin 
311c141c5c6SRuslan Bukin 	return (0);
312c141c5c6SRuslan Bukin }
313c141c5c6SRuslan Bukin 
314c141c5c6SRuslan Bukin static int
315c141c5c6SRuslan Bukin vtmmio_suspend(device_t dev)
316c141c5c6SRuslan Bukin {
317c141c5c6SRuslan Bukin 
318c141c5c6SRuslan Bukin 	return (bus_generic_suspend(dev));
319c141c5c6SRuslan Bukin }
320c141c5c6SRuslan Bukin 
321c141c5c6SRuslan Bukin static int
322c141c5c6SRuslan Bukin vtmmio_resume(device_t dev)
323c141c5c6SRuslan Bukin {
324c141c5c6SRuslan Bukin 
325c141c5c6SRuslan Bukin 	return (bus_generic_resume(dev));
326c141c5c6SRuslan Bukin }
327c141c5c6SRuslan Bukin 
328c141c5c6SRuslan Bukin static int
329c141c5c6SRuslan Bukin vtmmio_shutdown(device_t dev)
330c141c5c6SRuslan Bukin {
331c141c5c6SRuslan Bukin 
332c141c5c6SRuslan Bukin 	(void) bus_generic_shutdown(dev);
333c141c5c6SRuslan Bukin 
334c141c5c6SRuslan Bukin 	/* Forcibly stop the host device. */
335c141c5c6SRuslan Bukin 	vtmmio_stop(dev);
336c141c5c6SRuslan Bukin 
337c141c5c6SRuslan Bukin 	return (0);
338c141c5c6SRuslan Bukin }
339c141c5c6SRuslan Bukin 
340c141c5c6SRuslan Bukin static void
341c141c5c6SRuslan Bukin vtmmio_driver_added(device_t dev, driver_t *driver)
342c141c5c6SRuslan Bukin {
343c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
344c141c5c6SRuslan Bukin 
345c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
346c141c5c6SRuslan Bukin 
347c141c5c6SRuslan Bukin 	vtmmio_probe_and_attach_child(sc);
348c141c5c6SRuslan Bukin }
349c141c5c6SRuslan Bukin 
350c141c5c6SRuslan Bukin static void
351c141c5c6SRuslan Bukin vtmmio_child_detached(device_t dev, device_t child)
352c141c5c6SRuslan Bukin {
353c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
354c141c5c6SRuslan Bukin 
355c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
356c141c5c6SRuslan Bukin 
357c141c5c6SRuslan Bukin 	vtmmio_reset(sc);
358c141c5c6SRuslan Bukin 	vtmmio_release_child_resources(sc);
359c141c5c6SRuslan Bukin }
360c141c5c6SRuslan Bukin 
361c141c5c6SRuslan Bukin static int
362c141c5c6SRuslan Bukin vtmmio_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
363c141c5c6SRuslan Bukin {
364c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
365c141c5c6SRuslan Bukin 
366c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
367c141c5c6SRuslan Bukin 
368c141c5c6SRuslan Bukin 	if (sc->vtmmio_child_dev != child)
369c141c5c6SRuslan Bukin 		return (ENOENT);
370c141c5c6SRuslan Bukin 
371c141c5c6SRuslan Bukin 	switch (index) {
372c141c5c6SRuslan Bukin 	case VIRTIO_IVAR_DEVTYPE:
373c141c5c6SRuslan Bukin 	case VIRTIO_IVAR_SUBDEVICE:
374c141c5c6SRuslan Bukin 		*result = vtmmio_read_config_4(sc, VIRTIO_MMIO_DEVICE_ID);
375c141c5c6SRuslan Bukin 		break;
376c141c5c6SRuslan Bukin 	case VIRTIO_IVAR_VENDOR:
377c141c5c6SRuslan Bukin 		*result = vtmmio_read_config_4(sc, VIRTIO_MMIO_VENDOR_ID);
378c141c5c6SRuslan Bukin 		break;
379dfca0a8bSConrad Meyer 	case VIRTIO_IVAR_SUBVENDOR:
380dfca0a8bSConrad Meyer 	case VIRTIO_IVAR_DEVICE:
381dfca0a8bSConrad Meyer 		/*
382dfca0a8bSConrad Meyer 		 * Dummy value for fields not present in this bus.  Used by
383ddfc9c4cSWarner Losh 		 * bus-agnostic virtio_child_pnpinfo.
384dfca0a8bSConrad Meyer 		 */
385dfca0a8bSConrad Meyer 		*result = 0;
386dfca0a8bSConrad Meyer 		break;
3879da9560cSBryan Venteicher 	case VIRTIO_IVAR_MODERN:
3889da9560cSBryan Venteicher 		/*
3899da9560cSBryan Venteicher 		 * There are several modern (aka MMIO v2) spec compliance
3909da9560cSBryan Venteicher 		 * issues with this driver, but keep the status quo.
3919da9560cSBryan Venteicher 		 */
3929da9560cSBryan Venteicher 		*result = sc->vtmmio_version > 1;
3939da9560cSBryan Venteicher 		break;
394c141c5c6SRuslan Bukin 	default:
395c141c5c6SRuslan Bukin 		return (ENOENT);
396c141c5c6SRuslan Bukin 	}
397c141c5c6SRuslan Bukin 
398c141c5c6SRuslan Bukin 	return (0);
399c141c5c6SRuslan Bukin }
400c141c5c6SRuslan Bukin 
401c141c5c6SRuslan Bukin static int
402c141c5c6SRuslan Bukin vtmmio_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
403c141c5c6SRuslan Bukin {
404c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
405c141c5c6SRuslan Bukin 
406c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
407c141c5c6SRuslan Bukin 
408c141c5c6SRuslan Bukin 	if (sc->vtmmio_child_dev != child)
409c141c5c6SRuslan Bukin 		return (ENOENT);
410c141c5c6SRuslan Bukin 
411c141c5c6SRuslan Bukin 	switch (index) {
412c141c5c6SRuslan Bukin 	case VIRTIO_IVAR_FEATURE_DESC:
413c141c5c6SRuslan Bukin 		sc->vtmmio_child_feat_desc = (void *) value;
414c141c5c6SRuslan Bukin 		break;
415c141c5c6SRuslan Bukin 	default:
416c141c5c6SRuslan Bukin 		return (ENOENT);
417c141c5c6SRuslan Bukin 	}
418c141c5c6SRuslan Bukin 
419c141c5c6SRuslan Bukin 	return (0);
420c141c5c6SRuslan Bukin }
421c141c5c6SRuslan Bukin 
422c141c5c6SRuslan Bukin static uint64_t
423c141c5c6SRuslan Bukin vtmmio_negotiate_features(device_t dev, uint64_t child_features)
424c141c5c6SRuslan Bukin {
425c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
426c141c5c6SRuslan Bukin 	uint64_t host_features, features;
427c141c5c6SRuslan Bukin 
428c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
429c141c5c6SRuslan Bukin 
4309da9560cSBryan Venteicher 	if (sc->vtmmio_version > 1) {
4319da9560cSBryan Venteicher 		child_features |= VIRTIO_F_VERSION_1;
4329da9560cSBryan Venteicher 	}
4339da9560cSBryan Venteicher 
43416ca3d0fSJessica Clarke 	vtmmio_write_config_4(sc, VIRTIO_MMIO_HOST_FEATURES_SEL, 1);
435c141c5c6SRuslan Bukin 	host_features = vtmmio_read_config_4(sc, VIRTIO_MMIO_HOST_FEATURES);
43616ca3d0fSJessica Clarke 	host_features <<= 32;
43716ca3d0fSJessica Clarke 
43816ca3d0fSJessica Clarke 	vtmmio_write_config_4(sc, VIRTIO_MMIO_HOST_FEATURES_SEL, 0);
43916ca3d0fSJessica Clarke 	host_features |= vtmmio_read_config_4(sc, VIRTIO_MMIO_HOST_FEATURES);
44016ca3d0fSJessica Clarke 
441c141c5c6SRuslan Bukin 	vtmmio_describe_features(sc, "host", host_features);
442c141c5c6SRuslan Bukin 
443c141c5c6SRuslan Bukin 	/*
444c141c5c6SRuslan Bukin 	 * Limit negotiated features to what the driver, virtqueue, and
445c141c5c6SRuslan Bukin 	 * host all support.
446c141c5c6SRuslan Bukin 	 */
447c141c5c6SRuslan Bukin 	features = host_features & child_features;
4489da9560cSBryan Venteicher 	features = virtio_filter_transport_features(features);
449c141c5c6SRuslan Bukin 	sc->vtmmio_features = features;
450c141c5c6SRuslan Bukin 
451c141c5c6SRuslan Bukin 	vtmmio_describe_features(sc, "negotiated", features);
45216ca3d0fSJessica Clarke 
45350a6b28aSJessica Clarke 	vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES_SEL, 1);
45416ca3d0fSJessica Clarke 	vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES, features >> 32);
45516ca3d0fSJessica Clarke 
45650a6b28aSJessica Clarke 	vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
457c141c5c6SRuslan Bukin 	vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES, features);
458c141c5c6SRuslan Bukin 
459c141c5c6SRuslan Bukin 	return (features);
460c141c5c6SRuslan Bukin }
461c141c5c6SRuslan Bukin 
462c141c5c6SRuslan Bukin static int
463926cedd9SBryan Venteicher vtmmio_finalize_features(device_t dev)
464926cedd9SBryan Venteicher {
465926cedd9SBryan Venteicher 	struct vtmmio_softc *sc;
466926cedd9SBryan Venteicher 	uint8_t status;
467926cedd9SBryan Venteicher 
468926cedd9SBryan Venteicher 	sc = device_get_softc(dev);
469926cedd9SBryan Venteicher 
470926cedd9SBryan Venteicher 	if (sc->vtmmio_version > 1) {
471926cedd9SBryan Venteicher 		/*
472926cedd9SBryan Venteicher 		 * Must re-read the status after setting it to verify the
473926cedd9SBryan Venteicher 		 * negotiated features were accepted by the device.
474926cedd9SBryan Venteicher 		 */
475926cedd9SBryan Venteicher 		vtmmio_set_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
476926cedd9SBryan Venteicher 
477926cedd9SBryan Venteicher 		status = vtmmio_get_status(dev);
478926cedd9SBryan Venteicher 		if ((status & VIRTIO_CONFIG_S_FEATURES_OK) == 0) {
479926cedd9SBryan Venteicher 			device_printf(dev, "desired features were not accepted\n");
480926cedd9SBryan Venteicher 			return (ENOTSUP);
481926cedd9SBryan Venteicher 		}
482926cedd9SBryan Venteicher 	}
483926cedd9SBryan Venteicher 
484926cedd9SBryan Venteicher 	return (0);
485926cedd9SBryan Venteicher }
486926cedd9SBryan Venteicher 
487ccb576a8SMina Galić static bool
488c141c5c6SRuslan Bukin vtmmio_with_feature(device_t dev, uint64_t feature)
489c141c5c6SRuslan Bukin {
490c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
491c141c5c6SRuslan Bukin 
492c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
493c141c5c6SRuslan Bukin 
494c141c5c6SRuslan Bukin 	return ((sc->vtmmio_features & feature) != 0);
495c141c5c6SRuslan Bukin }
496c141c5c6SRuslan Bukin 
497046096d5SJessica Clarke static void
498046096d5SJessica Clarke vtmmio_set_virtqueue(struct vtmmio_softc *sc, struct virtqueue *vq,
499046096d5SJessica Clarke     uint32_t size)
500046096d5SJessica Clarke {
501046096d5SJessica Clarke 	vm_paddr_t paddr;
502046096d5SJessica Clarke 
503046096d5SJessica Clarke 	vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size);
50485ad7f8dSJessica Clarke 
505046096d5SJessica Clarke 	if (sc->vtmmio_version == 1) {
506046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN,
507046096d5SJessica Clarke 		    VIRTIO_MMIO_VRING_ALIGN);
508046096d5SJessica Clarke 		paddr = virtqueue_paddr(vq);
509046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN,
510046096d5SJessica Clarke 		    paddr >> PAGE_SHIFT);
511046096d5SJessica Clarke 	} else {
512046096d5SJessica Clarke 		paddr = virtqueue_desc_paddr(vq);
513046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_LOW,
514046096d5SJessica Clarke 		    paddr);
515046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_HIGH,
516cfe6a221SJessica Clarke 		    ((uint64_t)paddr) >> 32);
517046096d5SJessica Clarke 
518046096d5SJessica Clarke 		paddr = virtqueue_avail_paddr(vq);
519046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_LOW,
520046096d5SJessica Clarke 		    paddr);
521046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_HIGH,
522cfe6a221SJessica Clarke 		    ((uint64_t)paddr) >> 32);
523046096d5SJessica Clarke 
524046096d5SJessica Clarke 		paddr = virtqueue_used_paddr(vq);
525046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_LOW,
526046096d5SJessica Clarke 		    paddr);
527046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_HIGH,
528cfe6a221SJessica Clarke 		    ((uint64_t)paddr) >> 32);
529046096d5SJessica Clarke 
530046096d5SJessica Clarke 		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 1);
531046096d5SJessica Clarke 	}
532046096d5SJessica Clarke }
533046096d5SJessica Clarke 
534c141c5c6SRuslan Bukin static int
535180c0240SMina Galić vtmmio_alloc_virtqueues(device_t dev, int nvqs,
536c141c5c6SRuslan Bukin     struct vq_alloc_info *vq_info)
537c141c5c6SRuslan Bukin {
538c141c5c6SRuslan Bukin 	struct vtmmio_virtqueue *vqx;
539c141c5c6SRuslan Bukin 	struct vq_alloc_info *info;
540c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
541c141c5c6SRuslan Bukin 	struct virtqueue *vq;
5424dbff239SAndrew Turner 	uint32_t size;
543c141c5c6SRuslan Bukin 	int idx, error;
544c141c5c6SRuslan Bukin 
545c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
546c141c5c6SRuslan Bukin 
547c141c5c6SRuslan Bukin 	if (sc->vtmmio_nvqs != 0)
548c141c5c6SRuslan Bukin 		return (EALREADY);
549c141c5c6SRuslan Bukin 	if (nvqs <= 0)
550c141c5c6SRuslan Bukin 		return (EINVAL);
551c141c5c6SRuslan Bukin 
552ac2fffa4SPedro F. Giffuni 	sc->vtmmio_vqs = malloc(nvqs * sizeof(struct vtmmio_virtqueue),
553c141c5c6SRuslan Bukin 	    M_DEVBUF, M_NOWAIT | M_ZERO);
554c141c5c6SRuslan Bukin 	if (sc->vtmmio_vqs == NULL)
555c141c5c6SRuslan Bukin 		return (ENOMEM);
556c141c5c6SRuslan Bukin 
5577377c1dfSAndrew Turner 	if (sc->vtmmio_version == 1) {
5581147faceSRuslan Bukin 		vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_PAGE_SIZE,
5591147faceSRuslan Bukin 		    (1 << PAGE_SHIFT));
5607377c1dfSAndrew Turner 	}
5617c5c5b97SAndrew Turner 
562c141c5c6SRuslan Bukin 	for (idx = 0; idx < nvqs; idx++) {
563c141c5c6SRuslan Bukin 		vqx = &sc->vtmmio_vqs[idx];
564c141c5c6SRuslan Bukin 		info = &vq_info[idx];
565c141c5c6SRuslan Bukin 
566c141c5c6SRuslan Bukin 		vtmmio_select_virtqueue(sc, idx);
5674dbff239SAndrew Turner 		size = vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX);
568c141c5c6SRuslan Bukin 
569c141c5c6SRuslan Bukin 		error = virtqueue_alloc(dev, idx, size,
5709da9560cSBryan Venteicher 		    VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_VRING_ALIGN,
5719da9560cSBryan Venteicher 		    ~(vm_paddr_t)0, info, &vq);
572c141c5c6SRuslan Bukin 		if (error) {
573c141c5c6SRuslan Bukin 			device_printf(dev,
574c141c5c6SRuslan Bukin 			    "cannot allocate virtqueue %d: %d\n",
575c141c5c6SRuslan Bukin 			    idx, error);
576c141c5c6SRuslan Bukin 			break;
577c141c5c6SRuslan Bukin 		}
5784dbff239SAndrew Turner 
579046096d5SJessica Clarke 		vtmmio_set_virtqueue(sc, vq, size);
580c141c5c6SRuslan Bukin 
581c141c5c6SRuslan Bukin 		vqx->vtv_vq = *info->vqai_vq = vq;
582c141c5c6SRuslan Bukin 		vqx->vtv_no_intr = info->vqai_intr == NULL;
583c141c5c6SRuslan Bukin 
584c141c5c6SRuslan Bukin 		sc->vtmmio_nvqs++;
585c141c5c6SRuslan Bukin 	}
586c141c5c6SRuslan Bukin 
587c141c5c6SRuslan Bukin 	if (error)
588c141c5c6SRuslan Bukin 		vtmmio_free_virtqueues(sc);
589c141c5c6SRuslan Bukin 
590c141c5c6SRuslan Bukin 	return (error);
591c141c5c6SRuslan Bukin }
592c141c5c6SRuslan Bukin 
593c141c5c6SRuslan Bukin static void
594c141c5c6SRuslan Bukin vtmmio_stop(device_t dev)
595c141c5c6SRuslan Bukin {
596c141c5c6SRuslan Bukin 
597c141c5c6SRuslan Bukin 	vtmmio_reset(device_get_softc(dev));
598c141c5c6SRuslan Bukin }
599c141c5c6SRuslan Bukin 
600156b97faSRuslan Bukin static void
601156b97faSRuslan Bukin vtmmio_poll(device_t dev)
602156b97faSRuslan Bukin {
603156b97faSRuslan Bukin 	struct vtmmio_softc *sc;
604156b97faSRuslan Bukin 
605156b97faSRuslan Bukin 	sc = device_get_softc(dev);
606156b97faSRuslan Bukin 
607156b97faSRuslan Bukin 	if (sc->platform != NULL)
608156b97faSRuslan Bukin 		VIRTIO_MMIO_POLL(sc->platform);
609156b97faSRuslan Bukin }
610156b97faSRuslan Bukin 
611c141c5c6SRuslan Bukin static int
612c141c5c6SRuslan Bukin vtmmio_reinit(device_t dev, uint64_t features)
613c141c5c6SRuslan Bukin {
614c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
615c141c5c6SRuslan Bukin 	int idx, error;
616c141c5c6SRuslan Bukin 
617c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
618c141c5c6SRuslan Bukin 
619c141c5c6SRuslan Bukin 	if (vtmmio_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
620c141c5c6SRuslan Bukin 		vtmmio_stop(dev);
621c141c5c6SRuslan Bukin 
622c141c5c6SRuslan Bukin 	/*
623c141c5c6SRuslan Bukin 	 * Quickly drive the status through ACK and DRIVER. The device
624c141c5c6SRuslan Bukin 	 * does not become usable again until vtmmio_reinit_complete().
625c141c5c6SRuslan Bukin 	 */
626c141c5c6SRuslan Bukin 	vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
627c141c5c6SRuslan Bukin 	vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
628c141c5c6SRuslan Bukin 
629926cedd9SBryan Venteicher 	/*
630926cedd9SBryan Venteicher 	 * TODO: Check that features are not added as to what was
631926cedd9SBryan Venteicher 	 * originally negotiated.
632926cedd9SBryan Venteicher 	 */
633c141c5c6SRuslan Bukin 	vtmmio_negotiate_features(dev, features);
634926cedd9SBryan Venteicher 	error = vtmmio_finalize_features(dev);
635926cedd9SBryan Venteicher 	if (error) {
636926cedd9SBryan Venteicher 		device_printf(dev, "cannot finalize features during reinit\n");
637926cedd9SBryan Venteicher 		return (error);
638926cedd9SBryan Venteicher 	}
639c141c5c6SRuslan Bukin 
6407377c1dfSAndrew Turner 	if (sc->vtmmio_version == 1) {
6411147faceSRuslan Bukin 		vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_PAGE_SIZE,
6421147faceSRuslan Bukin 		    (1 << PAGE_SHIFT));
6437377c1dfSAndrew Turner 	}
6441147faceSRuslan Bukin 
645c141c5c6SRuslan Bukin 	for (idx = 0; idx < sc->vtmmio_nvqs; idx++) {
646c141c5c6SRuslan Bukin 		error = vtmmio_reinit_virtqueue(sc, idx);
647c141c5c6SRuslan Bukin 		if (error)
648c141c5c6SRuslan Bukin 			return (error);
649c141c5c6SRuslan Bukin 	}
650c141c5c6SRuslan Bukin 
651c141c5c6SRuslan Bukin 	return (0);
652c141c5c6SRuslan Bukin }
653c141c5c6SRuslan Bukin 
654c141c5c6SRuslan Bukin static void
655c141c5c6SRuslan Bukin vtmmio_reinit_complete(device_t dev)
656c141c5c6SRuslan Bukin {
657c141c5c6SRuslan Bukin 
658c141c5c6SRuslan Bukin 	vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
659c141c5c6SRuslan Bukin }
660c141c5c6SRuslan Bukin 
661c141c5c6SRuslan Bukin static void
6629da9560cSBryan Venteicher vtmmio_notify_virtqueue(device_t dev, uint16_t queue, bus_size_t offset)
663c141c5c6SRuslan Bukin {
664c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
665c141c5c6SRuslan Bukin 
666c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
6679da9560cSBryan Venteicher 	MPASS(offset == VIRTIO_MMIO_QUEUE_NOTIFY);
668c141c5c6SRuslan Bukin 
6699da9560cSBryan Venteicher 	vtmmio_write_config_4(sc, offset, queue);
670c141c5c6SRuslan Bukin }
671c141c5c6SRuslan Bukin 
672926cedd9SBryan Venteicher static int
673926cedd9SBryan Venteicher vtmmio_config_generation(device_t dev)
674926cedd9SBryan Venteicher {
675926cedd9SBryan Venteicher 	struct vtmmio_softc *sc;
676926cedd9SBryan Venteicher 	uint32_t gen;
677926cedd9SBryan Venteicher 
678926cedd9SBryan Venteicher 	sc = device_get_softc(dev);
679926cedd9SBryan Venteicher 
680926cedd9SBryan Venteicher 	if (sc->vtmmio_version > 1)
681926cedd9SBryan Venteicher 		gen = vtmmio_read_config_4(sc, VIRTIO_MMIO_CONFIG_GENERATION);
682926cedd9SBryan Venteicher 	else
683926cedd9SBryan Venteicher 		gen = 0;
684926cedd9SBryan Venteicher 
685926cedd9SBryan Venteicher 	return (gen);
686926cedd9SBryan Venteicher }
687926cedd9SBryan Venteicher 
688c141c5c6SRuslan Bukin static uint8_t
689c141c5c6SRuslan Bukin vtmmio_get_status(device_t dev)
690c141c5c6SRuslan Bukin {
691c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
692c141c5c6SRuslan Bukin 
693c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
694c141c5c6SRuslan Bukin 
6954dbff239SAndrew Turner 	return (vtmmio_read_config_4(sc, VIRTIO_MMIO_STATUS));
696c141c5c6SRuslan Bukin }
697c141c5c6SRuslan Bukin 
698c141c5c6SRuslan Bukin static void
699c141c5c6SRuslan Bukin vtmmio_set_status(device_t dev, uint8_t status)
700c141c5c6SRuslan Bukin {
701c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
702c141c5c6SRuslan Bukin 
703c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
704c141c5c6SRuslan Bukin 
705c141c5c6SRuslan Bukin 	if (status != VIRTIO_CONFIG_STATUS_RESET)
706c141c5c6SRuslan Bukin 		status |= vtmmio_get_status(dev);
707c141c5c6SRuslan Bukin 
7084dbff239SAndrew Turner 	vtmmio_write_config_4(sc, VIRTIO_MMIO_STATUS, status);
709c141c5c6SRuslan Bukin }
710c141c5c6SRuslan Bukin 
711c141c5c6SRuslan Bukin static void
712c141c5c6SRuslan Bukin vtmmio_read_dev_config(device_t dev, bus_size_t offset,
713c141c5c6SRuslan Bukin     void *dst, int length)
714c141c5c6SRuslan Bukin {
715c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
716c141c5c6SRuslan Bukin 	bus_size_t off;
717c141c5c6SRuslan Bukin 	uint8_t *d;
718c141c5c6SRuslan Bukin 	int size;
719c141c5c6SRuslan Bukin 
720c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
721c141c5c6SRuslan Bukin 	off = VIRTIO_MMIO_CONFIG + offset;
722c141c5c6SRuslan Bukin 
723046096d5SJessica Clarke 	/*
724046096d5SJessica Clarke 	 * The non-legacy MMIO specification adds the following restriction:
725046096d5SJessica Clarke 	 *
726046096d5SJessica Clarke 	 *   4.2.2.2: For the device-specific configuration space, the driver
727046096d5SJessica Clarke 	 *   MUST use 8 bit wide accesses for 8 bit wide fields, 16 bit wide
728046096d5SJessica Clarke 	 *   and aligned accesses for 16 bit wide fields and 32 bit wide and
729046096d5SJessica Clarke 	 *   aligned accesses for 32 and 64 bit wide fields.
730046096d5SJessica Clarke 	 *
731046096d5SJessica Clarke 	 * The endianness also varies between non-legacy and legacy:
732046096d5SJessica Clarke 	 *
733046096d5SJessica Clarke 	 *   2.4: Note: The device configuration space uses the little-endian
734046096d5SJessica Clarke 	 *   format for multi-byte fields.
735046096d5SJessica Clarke 	 *
736046096d5SJessica Clarke 	 *   2.4.3: Note that for legacy interfaces, device configuration space
737046096d5SJessica Clarke 	 *   is generally the guest’s native endian, rather than PCI’s
738046096d5SJessica Clarke 	 *   little-endian. The correct endian-ness is documented for each
739046096d5SJessica Clarke 	 *   device.
740046096d5SJessica Clarke 	 */
741046096d5SJessica Clarke 	if (sc->vtmmio_version > 1) {
742046096d5SJessica Clarke 		switch (length) {
743046096d5SJessica Clarke 		case 1:
744046096d5SJessica Clarke 			*(uint8_t *)dst = vtmmio_read_config_1(sc, off);
745046096d5SJessica Clarke 			break;
746046096d5SJessica Clarke 		case 2:
747046096d5SJessica Clarke 			*(uint16_t *)dst =
748046096d5SJessica Clarke 			    le16toh(vtmmio_read_config_2(sc, off));
749046096d5SJessica Clarke 			break;
750046096d5SJessica Clarke 		case 4:
751046096d5SJessica Clarke 			*(uint32_t *)dst =
752046096d5SJessica Clarke 			    le32toh(vtmmio_read_config_4(sc, off));
753046096d5SJessica Clarke 			break;
754046096d5SJessica Clarke 		case 8:
755926cedd9SBryan Venteicher 			*(uint64_t *)dst = vtmmio_read_dev_config_8(sc, off);
756046096d5SJessica Clarke 			break;
757046096d5SJessica Clarke 		default:
758046096d5SJessica Clarke 			panic("%s: invalid length %d\n", __func__, length);
759046096d5SJessica Clarke 		}
760046096d5SJessica Clarke 
761046096d5SJessica Clarke 		return;
762046096d5SJessica Clarke 	}
763046096d5SJessica Clarke 
764c141c5c6SRuslan Bukin 	for (d = dst; length > 0; d += size, off += size, length -= size) {
7654dbff239SAndrew Turner #ifdef ALLOW_WORD_ALIGNED_ACCESS
766c141c5c6SRuslan Bukin 		if (length >= 4) {
767c141c5c6SRuslan Bukin 			size = 4;
768c141c5c6SRuslan Bukin 			*(uint32_t *)d = vtmmio_read_config_4(sc, off);
769c141c5c6SRuslan Bukin 		} else if (length >= 2) {
770c141c5c6SRuslan Bukin 			size = 2;
771c141c5c6SRuslan Bukin 			*(uint16_t *)d = vtmmio_read_config_2(sc, off);
7724dbff239SAndrew Turner 		} else
7734dbff239SAndrew Turner #endif
7744dbff239SAndrew Turner 		{
775c141c5c6SRuslan Bukin 			size = 1;
776c141c5c6SRuslan Bukin 			*d = vtmmio_read_config_1(sc, off);
777c141c5c6SRuslan Bukin 		}
778c141c5c6SRuslan Bukin 	}
779c141c5c6SRuslan Bukin }
780c141c5c6SRuslan Bukin 
781926cedd9SBryan Venteicher static uint64_t
782926cedd9SBryan Venteicher vtmmio_read_dev_config_8(struct vtmmio_softc *sc, bus_size_t off)
783926cedd9SBryan Venteicher {
784926cedd9SBryan Venteicher 	device_t dev;
785926cedd9SBryan Venteicher 	int gen;
786926cedd9SBryan Venteicher 	uint32_t val0, val1;
787926cedd9SBryan Venteicher 
788926cedd9SBryan Venteicher 	dev = sc->dev;
789926cedd9SBryan Venteicher 
790926cedd9SBryan Venteicher 	do {
791926cedd9SBryan Venteicher 		gen = vtmmio_config_generation(dev);
792926cedd9SBryan Venteicher 		val0 = le32toh(vtmmio_read_config_4(sc, off));
793926cedd9SBryan Venteicher 		val1 = le32toh(vtmmio_read_config_4(sc, off + 4));
794926cedd9SBryan Venteicher 	} while (gen != vtmmio_config_generation(dev));
795926cedd9SBryan Venteicher 
796926cedd9SBryan Venteicher 	return (((uint64_t) val1 << 32) | val0);
797926cedd9SBryan Venteicher }
798926cedd9SBryan Venteicher 
799c141c5c6SRuslan Bukin static void
800c141c5c6SRuslan Bukin vtmmio_write_dev_config(device_t dev, bus_size_t offset,
8016c4f9516SAlex Richardson     const void *src, int length)
802c141c5c6SRuslan Bukin {
803c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
804c141c5c6SRuslan Bukin 	bus_size_t off;
80590178705SAlex Richardson 	const uint8_t *s;
806c141c5c6SRuslan Bukin 	int size;
807c141c5c6SRuslan Bukin 
808c141c5c6SRuslan Bukin 	sc = device_get_softc(dev);
809c141c5c6SRuslan Bukin 	off = VIRTIO_MMIO_CONFIG + offset;
810c141c5c6SRuslan Bukin 
811046096d5SJessica Clarke 	/*
812046096d5SJessica Clarke 	 * The non-legacy MMIO specification adds size and alignment
813046096d5SJessica Clarke 	 * restrctions. It also changes the endianness from native-endian to
814046096d5SJessica Clarke 	 * little-endian. See vtmmio_read_dev_config.
815046096d5SJessica Clarke 	 */
816046096d5SJessica Clarke 	if (sc->vtmmio_version > 1) {
817046096d5SJessica Clarke 		switch (length) {
818046096d5SJessica Clarke 		case 1:
8196c4f9516SAlex Richardson 			vtmmio_write_config_1(sc, off, *(const uint8_t *)src);
820046096d5SJessica Clarke 			break;
821046096d5SJessica Clarke 		case 2:
822046096d5SJessica Clarke 			vtmmio_write_config_2(sc, off,
8236c4f9516SAlex Richardson 			    htole16(*(const uint16_t *)src));
824046096d5SJessica Clarke 			break;
825046096d5SJessica Clarke 		case 4:
826046096d5SJessica Clarke 			vtmmio_write_config_4(sc, off,
8276c4f9516SAlex Richardson 			    htole32(*(const uint32_t *)src));
828046096d5SJessica Clarke 			break;
829046096d5SJessica Clarke 		case 8:
830046096d5SJessica Clarke 			vtmmio_write_config_4(sc, off,
8316c4f9516SAlex Richardson 			    htole32(*(const uint64_t *)src));
832046096d5SJessica Clarke 			vtmmio_write_config_4(sc, off + 4,
8336c4f9516SAlex Richardson 			    htole32((*(const uint64_t *)src) >> 32));
834046096d5SJessica Clarke 			break;
835046096d5SJessica Clarke 		default:
836046096d5SJessica Clarke 			panic("%s: invalid length %d\n", __func__, length);
837046096d5SJessica Clarke 		}
838046096d5SJessica Clarke 
839046096d5SJessica Clarke 		return;
840046096d5SJessica Clarke 	}
841046096d5SJessica Clarke 
842c141c5c6SRuslan Bukin 	for (s = src; length > 0; s += size, off += size, length -= size) {
8434dbff239SAndrew Turner #ifdef ALLOW_WORD_ALIGNED_ACCESS
844c141c5c6SRuslan Bukin 		if (length >= 4) {
845c141c5c6SRuslan Bukin 			size = 4;
846c141c5c6SRuslan Bukin 			vtmmio_write_config_4(sc, off, *(uint32_t *)s);
847c141c5c6SRuslan Bukin 		} else if (length >= 2) {
848c141c5c6SRuslan Bukin 			size = 2;
849c141c5c6SRuslan Bukin 			vtmmio_write_config_2(sc, off, *(uint16_t *)s);
8504dbff239SAndrew Turner 		} else
8514dbff239SAndrew Turner #endif
8524dbff239SAndrew Turner 		{
853c141c5c6SRuslan Bukin 			size = 1;
854c141c5c6SRuslan Bukin 			vtmmio_write_config_1(sc, off, *s);
855c141c5c6SRuslan Bukin 		}
856c141c5c6SRuslan Bukin 	}
857c141c5c6SRuslan Bukin }
858c141c5c6SRuslan Bukin 
859c141c5c6SRuslan Bukin static void
860c141c5c6SRuslan Bukin vtmmio_describe_features(struct vtmmio_softc *sc, const char *msg,
861c141c5c6SRuslan Bukin     uint64_t features)
862c141c5c6SRuslan Bukin {
863c141c5c6SRuslan Bukin 	device_t dev, child;
864c141c5c6SRuslan Bukin 
865c141c5c6SRuslan Bukin 	dev = sc->dev;
866c141c5c6SRuslan Bukin 	child = sc->vtmmio_child_dev;
867c141c5c6SRuslan Bukin 
86861cefb9bSAlexander Motin 	if (device_is_attached(child) || bootverbose == 0)
869c141c5c6SRuslan Bukin 		return;
870c141c5c6SRuslan Bukin 
871c141c5c6SRuslan Bukin 	virtio_describe(dev, msg, features, sc->vtmmio_child_feat_desc);
872c141c5c6SRuslan Bukin }
873c141c5c6SRuslan Bukin 
874c141c5c6SRuslan Bukin static void
875c141c5c6SRuslan Bukin vtmmio_probe_and_attach_child(struct vtmmio_softc *sc)
876c141c5c6SRuslan Bukin {
877c141c5c6SRuslan Bukin 	device_t dev, child;
878c141c5c6SRuslan Bukin 
879c141c5c6SRuslan Bukin 	dev = sc->dev;
880c141c5c6SRuslan Bukin 	child = sc->vtmmio_child_dev;
881c141c5c6SRuslan Bukin 
882c141c5c6SRuslan Bukin 	if (child == NULL)
883c141c5c6SRuslan Bukin 		return;
884c141c5c6SRuslan Bukin 
885c141c5c6SRuslan Bukin 	if (device_get_state(child) != DS_NOTPRESENT) {
886c141c5c6SRuslan Bukin 		return;
887c141c5c6SRuslan Bukin 	}
888c141c5c6SRuslan Bukin 
889c141c5c6SRuslan Bukin 	if (device_probe(child) != 0) {
890c141c5c6SRuslan Bukin 		return;
891c141c5c6SRuslan Bukin 	}
892c141c5c6SRuslan Bukin 
893c141c5c6SRuslan Bukin 	vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
894c141c5c6SRuslan Bukin 	if (device_attach(child) != 0) {
895c141c5c6SRuslan Bukin 		vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
896c141c5c6SRuslan Bukin 		vtmmio_reset(sc);
897c141c5c6SRuslan Bukin 		vtmmio_release_child_resources(sc);
898c141c5c6SRuslan Bukin 		/* Reset status for future attempt. */
899c141c5c6SRuslan Bukin 		vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
900c141c5c6SRuslan Bukin 	} else {
901c141c5c6SRuslan Bukin 		vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
902c141c5c6SRuslan Bukin 		VIRTIO_ATTACH_COMPLETED(child);
903c141c5c6SRuslan Bukin 	}
904c141c5c6SRuslan Bukin }
905c141c5c6SRuslan Bukin 
906c141c5c6SRuslan Bukin static int
907c141c5c6SRuslan Bukin vtmmio_reinit_virtqueue(struct vtmmio_softc *sc, int idx)
908c141c5c6SRuslan Bukin {
909c141c5c6SRuslan Bukin 	struct vtmmio_virtqueue *vqx;
910c141c5c6SRuslan Bukin 	struct virtqueue *vq;
911c141c5c6SRuslan Bukin 	int error;
912c141c5c6SRuslan Bukin 	uint16_t size;
913c141c5c6SRuslan Bukin 
914c141c5c6SRuslan Bukin 	vqx = &sc->vtmmio_vqs[idx];
915c141c5c6SRuslan Bukin 	vq = vqx->vtv_vq;
916c141c5c6SRuslan Bukin 
917c141c5c6SRuslan Bukin 	KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx));
918c141c5c6SRuslan Bukin 
919c141c5c6SRuslan Bukin 	vtmmio_select_virtqueue(sc, idx);
9204dbff239SAndrew Turner 	size = vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX);
921c141c5c6SRuslan Bukin 
922c141c5c6SRuslan Bukin 	error = virtqueue_reinit(vq, size);
923c141c5c6SRuslan Bukin 	if (error)
924c141c5c6SRuslan Bukin 		return (error);
925c141c5c6SRuslan Bukin 
926046096d5SJessica Clarke 	vtmmio_set_virtqueue(sc, vq, size);
927c141c5c6SRuslan Bukin 
928c141c5c6SRuslan Bukin 	return (0);
929c141c5c6SRuslan Bukin }
930c141c5c6SRuslan Bukin 
931c141c5c6SRuslan Bukin static void
932c141c5c6SRuslan Bukin vtmmio_free_interrupts(struct vtmmio_softc *sc)
933c141c5c6SRuslan Bukin {
934c141c5c6SRuslan Bukin 
935c141c5c6SRuslan Bukin 	if (sc->ih != NULL)
936c141c5c6SRuslan Bukin 		bus_teardown_intr(sc->dev, sc->res[1], sc->ih);
937c141c5c6SRuslan Bukin 
938c141c5c6SRuslan Bukin 	if (sc->res[1] != NULL)
939c141c5c6SRuslan Bukin 		bus_release_resource(sc->dev, SYS_RES_IRQ, 0, sc->res[1]);
940c141c5c6SRuslan Bukin }
941c141c5c6SRuslan Bukin 
942c141c5c6SRuslan Bukin static void
943c141c5c6SRuslan Bukin vtmmio_free_virtqueues(struct vtmmio_softc *sc)
944c141c5c6SRuslan Bukin {
945c141c5c6SRuslan Bukin 	struct vtmmio_virtqueue *vqx;
946c141c5c6SRuslan Bukin 	int idx;
947c141c5c6SRuslan Bukin 
948c141c5c6SRuslan Bukin 	for (idx = 0; idx < sc->vtmmio_nvqs; idx++) {
949c141c5c6SRuslan Bukin 		vqx = &sc->vtmmio_vqs[idx];
950c141c5c6SRuslan Bukin 
951c141c5c6SRuslan Bukin 		vtmmio_select_virtqueue(sc, idx);
952926cedd9SBryan Venteicher 		if (sc->vtmmio_version > 1) {
953046096d5SJessica Clarke 			vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 0);
954926cedd9SBryan Venteicher 			vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_READY);
955926cedd9SBryan Venteicher 		} else
956926cedd9SBryan Venteicher 			vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0);
957c141c5c6SRuslan Bukin 
958c141c5c6SRuslan Bukin 		virtqueue_free(vqx->vtv_vq);
959c141c5c6SRuslan Bukin 		vqx->vtv_vq = NULL;
960c141c5c6SRuslan Bukin 	}
961c141c5c6SRuslan Bukin 
962c141c5c6SRuslan Bukin 	free(sc->vtmmio_vqs, M_DEVBUF);
963c141c5c6SRuslan Bukin 	sc->vtmmio_vqs = NULL;
964c141c5c6SRuslan Bukin 	sc->vtmmio_nvqs = 0;
965c141c5c6SRuslan Bukin }
966c141c5c6SRuslan Bukin 
967c141c5c6SRuslan Bukin static void
968c141c5c6SRuslan Bukin vtmmio_release_child_resources(struct vtmmio_softc *sc)
969c141c5c6SRuslan Bukin {
970c141c5c6SRuslan Bukin 
971c141c5c6SRuslan Bukin 	vtmmio_free_interrupts(sc);
972c141c5c6SRuslan Bukin 	vtmmio_free_virtqueues(sc);
973c141c5c6SRuslan Bukin }
974c141c5c6SRuslan Bukin 
975c141c5c6SRuslan Bukin static void
976c141c5c6SRuslan Bukin vtmmio_reset(struct vtmmio_softc *sc)
977c141c5c6SRuslan Bukin {
978c141c5c6SRuslan Bukin 
979c141c5c6SRuslan Bukin 	/*
980c141c5c6SRuslan Bukin 	 * Setting the status to RESET sets the host device to
981c141c5c6SRuslan Bukin 	 * the original, uninitialized state.
982c141c5c6SRuslan Bukin 	 */
983c141c5c6SRuslan Bukin 	vtmmio_set_status(sc->dev, VIRTIO_CONFIG_STATUS_RESET);
984c141c5c6SRuslan Bukin }
985c141c5c6SRuslan Bukin 
986c141c5c6SRuslan Bukin static void
987c141c5c6SRuslan Bukin vtmmio_select_virtqueue(struct vtmmio_softc *sc, int idx)
988c141c5c6SRuslan Bukin {
989c141c5c6SRuslan Bukin 
9904dbff239SAndrew Turner 	vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_SEL, idx);
991c141c5c6SRuslan Bukin }
992c141c5c6SRuslan Bukin 
993c141c5c6SRuslan Bukin static void
994c141c5c6SRuslan Bukin vtmmio_vq_intr(void *arg)
995c141c5c6SRuslan Bukin {
996c141c5c6SRuslan Bukin 	struct vtmmio_virtqueue *vqx;
997c141c5c6SRuslan Bukin 	struct vtmmio_softc *sc;
998c141c5c6SRuslan Bukin 	struct virtqueue *vq;
9994dbff239SAndrew Turner 	uint32_t status;
1000c141c5c6SRuslan Bukin 	int idx;
1001c141c5c6SRuslan Bukin 
1002c141c5c6SRuslan Bukin 	sc = arg;
1003c141c5c6SRuslan Bukin 
10044dbff239SAndrew Turner 	status = vtmmio_read_config_4(sc, VIRTIO_MMIO_INTERRUPT_STATUS);
10054dbff239SAndrew Turner 	vtmmio_write_config_4(sc, VIRTIO_MMIO_INTERRUPT_ACK, status);
10064dbff239SAndrew Turner 
10074dbff239SAndrew Turner 	/* The config changed */
10084dbff239SAndrew Turner 	if (status & VIRTIO_MMIO_INT_CONFIG)
10094dbff239SAndrew Turner 		if (sc->vtmmio_child_dev != NULL)
10104dbff239SAndrew Turner 			VIRTIO_CONFIG_CHANGE(sc->vtmmio_child_dev);
10114dbff239SAndrew Turner 
1012c141c5c6SRuslan Bukin 	/* Notify all virtqueues. */
10134dbff239SAndrew Turner 	if (status & VIRTIO_MMIO_INT_VRING) {
1014c141c5c6SRuslan Bukin 		for (idx = 0; idx < sc->vtmmio_nvqs; idx++) {
1015c141c5c6SRuslan Bukin 			vqx = &sc->vtmmio_vqs[idx];
10164dbff239SAndrew Turner 			if (vqx->vtv_no_intr == 0) {
1017c141c5c6SRuslan Bukin 				vq = vqx->vtv_vq;
1018c141c5c6SRuslan Bukin 				virtqueue_intr(vq);
10194dbff239SAndrew Turner 			}
10204dbff239SAndrew Turner 		}
10214dbff239SAndrew Turner 	}
1022c141c5c6SRuslan Bukin }
1023