xref: /freebsd/sys/dev/virtio/pci/virtio_pci.c (revision cda6c6abf98e866e9fb49138711f2bb66c598a27)
110b59a9bSPeter Grehan /*-
210b59a9bSPeter Grehan  * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
310b59a9bSPeter Grehan  * All rights reserved.
410b59a9bSPeter Grehan  *
510b59a9bSPeter Grehan  * Redistribution and use in source and binary forms, with or without
610b59a9bSPeter Grehan  * modification, are permitted provided that the following conditions
710b59a9bSPeter Grehan  * are met:
810b59a9bSPeter Grehan  * 1. Redistributions of source code must retain the above copyright
910b59a9bSPeter Grehan  *    notice unmodified, this list of conditions, and the following
1010b59a9bSPeter Grehan  *    disclaimer.
1110b59a9bSPeter Grehan  * 2. Redistributions in binary form must reproduce the above copyright
1210b59a9bSPeter Grehan  *    notice, this list of conditions and the following disclaimer in the
1310b59a9bSPeter Grehan  *    documentation and/or other materials provided with the distribution.
1410b59a9bSPeter Grehan  *
1510b59a9bSPeter Grehan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1610b59a9bSPeter Grehan  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1710b59a9bSPeter Grehan  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1810b59a9bSPeter Grehan  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1910b59a9bSPeter Grehan  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2010b59a9bSPeter Grehan  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2110b59a9bSPeter Grehan  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2210b59a9bSPeter Grehan  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2310b59a9bSPeter Grehan  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2410b59a9bSPeter Grehan  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2510b59a9bSPeter Grehan  */
2610b59a9bSPeter Grehan 
2710b59a9bSPeter Grehan /* Driver for the VirtIO PCI interface. */
2810b59a9bSPeter Grehan 
2910b59a9bSPeter Grehan #include <sys/cdefs.h>
3010b59a9bSPeter Grehan __FBSDID("$FreeBSD$");
3110b59a9bSPeter Grehan 
3210b59a9bSPeter Grehan #include <sys/param.h>
3310b59a9bSPeter Grehan #include <sys/systm.h>
3410b59a9bSPeter Grehan #include <sys/bus.h>
3510b59a9bSPeter Grehan #include <sys/kernel.h>
3610b59a9bSPeter Grehan #include <sys/module.h>
3710b59a9bSPeter Grehan #include <sys/malloc.h>
3810b59a9bSPeter Grehan 
3910b59a9bSPeter Grehan #include <machine/bus.h>
4010b59a9bSPeter Grehan #include <machine/resource.h>
4110b59a9bSPeter Grehan #include <sys/bus.h>
4210b59a9bSPeter Grehan #include <sys/rman.h>
4310b59a9bSPeter Grehan 
4410b59a9bSPeter Grehan #include <dev/pci/pcivar.h>
4510b59a9bSPeter Grehan #include <dev/pci/pcireg.h>
4610b59a9bSPeter Grehan 
4710b59a9bSPeter Grehan #include <dev/virtio/virtio.h>
4810b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h>
4910b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h>
5010b59a9bSPeter Grehan 
5110b59a9bSPeter Grehan #include "virtio_bus_if.h"
5210b59a9bSPeter Grehan #include "virtio_if.h"
5310b59a9bSPeter Grehan 
5410b59a9bSPeter Grehan struct vtpci_softc {
5510b59a9bSPeter Grehan 	device_t			 vtpci_dev;
5610b59a9bSPeter Grehan 	struct resource			*vtpci_res;
5710b59a9bSPeter Grehan 	struct resource			*vtpci_msix_res;
5810b59a9bSPeter Grehan 	uint64_t			 vtpci_features;
5910b59a9bSPeter Grehan 	uint32_t			 vtpci_flags;
6010b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_NO_MSI		 0x0001
6110b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_MSI		 0x0002
6210b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_NO_MSIX		 0x0010
6310b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_MSIX		 0x0020
6410b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_SHARED_MSIX	 0x0040
6510b59a9bSPeter Grehan 
6610b59a9bSPeter Grehan 	device_t			 vtpci_child_dev;
6710b59a9bSPeter Grehan 	struct virtio_feature_desc	*vtpci_child_feat_desc;
6810b59a9bSPeter Grehan 
6910b59a9bSPeter Grehan 	/*
7010b59a9bSPeter Grehan 	 * Ideally, each virtqueue that the driver provides a callback for
7110b59a9bSPeter Grehan 	 * will receive its own MSIX vector. If there are not sufficient
7210b59a9bSPeter Grehan 	 * vectors available, we will then attempt to have all the VQs
7310b59a9bSPeter Grehan 	 * share one vector. Note that when using MSIX, the configuration
7410b59a9bSPeter Grehan 	 * changed notifications must be on their own vector.
7510b59a9bSPeter Grehan 	 *
7610b59a9bSPeter Grehan 	 * If MSIX is not available, we will attempt to have the whole
7710b59a9bSPeter Grehan 	 * device share one MSI vector, and then, finally, one legacy
7810b59a9bSPeter Grehan 	 * interrupt.
7910b59a9bSPeter Grehan 	 */
8010b59a9bSPeter Grehan 	int				 vtpci_nvqs;
8110b59a9bSPeter Grehan 	struct vtpci_virtqueue {
8210b59a9bSPeter Grehan 		struct virtqueue *vq;
8310b59a9bSPeter Grehan 
8410b59a9bSPeter Grehan 		/* Index into vtpci_intr_res[] below. Unused, then -1. */
8510b59a9bSPeter Grehan 		int		  ires_idx;
8610b59a9bSPeter Grehan 	} vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
8710b59a9bSPeter Grehan 
8810b59a9bSPeter Grehan 	/*
8910b59a9bSPeter Grehan 	 * When using MSIX interrupts, the first element of vtpci_intr_res[]
9010b59a9bSPeter Grehan 	 * is always the configuration changed notifications. The remaining
9110b59a9bSPeter Grehan 	 * element(s) are used for the virtqueues.
9210b59a9bSPeter Grehan 	 *
9310b59a9bSPeter Grehan 	 * With MSI and legacy interrupts, only the first element of
9410b59a9bSPeter Grehan 	 * vtpci_intr_res[] is used.
9510b59a9bSPeter Grehan 	 */
9610b59a9bSPeter Grehan 	int				 vtpci_nintr_res;
9710b59a9bSPeter Grehan 	struct vtpci_intr_resource {
9810b59a9bSPeter Grehan 		struct resource	*irq;
9910b59a9bSPeter Grehan 		int		 rid;
10010b59a9bSPeter Grehan 		void		*intrhand;
10110b59a9bSPeter Grehan 	} vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
10210b59a9bSPeter Grehan };
10310b59a9bSPeter Grehan 
10410b59a9bSPeter Grehan static int	vtpci_probe(device_t);
10510b59a9bSPeter Grehan static int	vtpci_attach(device_t);
10610b59a9bSPeter Grehan static int	vtpci_detach(device_t);
10710b59a9bSPeter Grehan static int	vtpci_suspend(device_t);
10810b59a9bSPeter Grehan static int	vtpci_resume(device_t);
10910b59a9bSPeter Grehan static int	vtpci_shutdown(device_t);
11010b59a9bSPeter Grehan static void	vtpci_driver_added(device_t, driver_t *);
11110b59a9bSPeter Grehan static void	vtpci_child_detached(device_t, device_t);
11210b59a9bSPeter Grehan static int	vtpci_read_ivar(device_t, device_t, int, uintptr_t *);
11310b59a9bSPeter Grehan static int	vtpci_write_ivar(device_t, device_t, int, uintptr_t);
11410b59a9bSPeter Grehan 
11510b59a9bSPeter Grehan static uint64_t	vtpci_negotiate_features(device_t, uint64_t);
11610b59a9bSPeter Grehan static int	vtpci_with_feature(device_t, uint64_t);
11710b59a9bSPeter Grehan static int	vtpci_alloc_virtqueues(device_t, int, int,
11810b59a9bSPeter Grehan 		    struct vq_alloc_info *);
11910b59a9bSPeter Grehan static int	vtpci_setup_intr(device_t, enum intr_type);
12010b59a9bSPeter Grehan static void	vtpci_stop(device_t);
12110b59a9bSPeter Grehan static int	vtpci_reinit(device_t, uint64_t);
12210b59a9bSPeter Grehan static void	vtpci_reinit_complete(device_t);
12310b59a9bSPeter Grehan static void	vtpci_notify_virtqueue(device_t, uint16_t);
12410b59a9bSPeter Grehan static uint8_t	vtpci_get_status(device_t);
12510b59a9bSPeter Grehan static void	vtpci_set_status(device_t, uint8_t);
12610b59a9bSPeter Grehan static void	vtpci_read_dev_config(device_t, bus_size_t, void *, int);
12710b59a9bSPeter Grehan static void	vtpci_write_dev_config(device_t, bus_size_t, void *, int);
12810b59a9bSPeter Grehan 
12910b59a9bSPeter Grehan static void	vtpci_describe_features(struct vtpci_softc *, const char *,
13010b59a9bSPeter Grehan 		    uint64_t);
13110b59a9bSPeter Grehan static void	vtpci_probe_and_attach_child(struct vtpci_softc *);
13210b59a9bSPeter Grehan 
13310b59a9bSPeter Grehan static int	vtpci_alloc_interrupts(struct vtpci_softc *, int, int,
13410b59a9bSPeter Grehan 		    struct vq_alloc_info *);
13510b59a9bSPeter Grehan static int	vtpci_alloc_intr_resources(struct vtpci_softc *, int,
13610b59a9bSPeter Grehan 		    struct vq_alloc_info *);
13710b59a9bSPeter Grehan static int	vtpci_alloc_msi(struct vtpci_softc *);
13810b59a9bSPeter Grehan static int	vtpci_alloc_msix(struct vtpci_softc *, int);
13910b59a9bSPeter Grehan static int	vtpci_register_msix_vector(struct vtpci_softc *, int, int);
14010b59a9bSPeter Grehan 
14110b59a9bSPeter Grehan static void	vtpci_free_interrupts(struct vtpci_softc *);
14210b59a9bSPeter Grehan static void	vtpci_free_virtqueues(struct vtpci_softc *);
14310b59a9bSPeter Grehan static void	vtpci_release_child_resources(struct vtpci_softc *);
14410b59a9bSPeter Grehan static void	vtpci_reset(struct vtpci_softc *);
14510b59a9bSPeter Grehan 
14610b59a9bSPeter Grehan static int	vtpci_legacy_intr(void *);
14710b59a9bSPeter Grehan static int	vtpci_vq_shared_intr(void *);
14810b59a9bSPeter Grehan static int	vtpci_vq_intr(void *);
14910b59a9bSPeter Grehan static int	vtpci_config_intr(void *);
15010b59a9bSPeter Grehan 
15110b59a9bSPeter Grehan /*
15210b59a9bSPeter Grehan  * I/O port read/write wrappers.
15310b59a9bSPeter Grehan  */
15410b59a9bSPeter Grehan #define vtpci_read_config_1(sc, o)	bus_read_1((sc)->vtpci_res, (o))
15510b59a9bSPeter Grehan #define vtpci_read_config_2(sc, o)	bus_read_2((sc)->vtpci_res, (o))
15610b59a9bSPeter Grehan #define vtpci_read_config_4(sc, o)	bus_read_4((sc)->vtpci_res, (o))
15710b59a9bSPeter Grehan #define vtpci_write_config_1(sc, o, v)	bus_write_1((sc)->vtpci_res, (o), (v))
15810b59a9bSPeter Grehan #define vtpci_write_config_2(sc, o, v)	bus_write_2((sc)->vtpci_res, (o), (v))
15910b59a9bSPeter Grehan #define vtpci_write_config_4(sc, o, v)	bus_write_4((sc)->vtpci_res, (o), (v))
16010b59a9bSPeter Grehan 
16110b59a9bSPeter Grehan /* Tunables. */
16210b59a9bSPeter Grehan static int vtpci_disable_msix = 0;
16310b59a9bSPeter Grehan TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix);
16410b59a9bSPeter Grehan 
16510b59a9bSPeter Grehan static device_method_t vtpci_methods[] = {
16610b59a9bSPeter Grehan 	/* Device interface. */
16710b59a9bSPeter Grehan 	DEVMETHOD(device_probe,			  vtpci_probe),
16810b59a9bSPeter Grehan 	DEVMETHOD(device_attach,		  vtpci_attach),
16910b59a9bSPeter Grehan 	DEVMETHOD(device_detach,		  vtpci_detach),
17010b59a9bSPeter Grehan 	DEVMETHOD(device_suspend,		  vtpci_suspend),
17110b59a9bSPeter Grehan 	DEVMETHOD(device_resume,		  vtpci_resume),
17210b59a9bSPeter Grehan 	DEVMETHOD(device_shutdown,		  vtpci_shutdown),
17310b59a9bSPeter Grehan 
17410b59a9bSPeter Grehan 	/* Bus interface. */
17510b59a9bSPeter Grehan 	DEVMETHOD(bus_driver_added,		  vtpci_driver_added),
17610b59a9bSPeter Grehan 	DEVMETHOD(bus_child_detached,		  vtpci_child_detached),
17710b59a9bSPeter Grehan 	DEVMETHOD(bus_read_ivar,		  vtpci_read_ivar),
17810b59a9bSPeter Grehan 	DEVMETHOD(bus_write_ivar,		  vtpci_write_ivar),
17910b59a9bSPeter Grehan 
18010b59a9bSPeter Grehan 	/* VirtIO bus interface. */
18110b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_negotiate_features,  vtpci_negotiate_features),
18210b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_with_feature,	  vtpci_with_feature),
18310b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_alloc_virtqueues,	  vtpci_alloc_virtqueues),
18410b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_setup_intr,	  vtpci_setup_intr),
18510b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_stop,		  vtpci_stop),
18610b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_reinit,		  vtpci_reinit),
18710b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_reinit_complete,	  vtpci_reinit_complete),
18810b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_notify_vq,		  vtpci_notify_virtqueue),
18910b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_read_device_config,  vtpci_read_dev_config),
19010b59a9bSPeter Grehan 	DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
19110b59a9bSPeter Grehan 
19210b59a9bSPeter Grehan 	{ 0, 0 }
19310b59a9bSPeter Grehan };
19410b59a9bSPeter Grehan 
19510b59a9bSPeter Grehan static driver_t vtpci_driver = {
19610b59a9bSPeter Grehan 	"virtio_pci",
19710b59a9bSPeter Grehan 	vtpci_methods,
19810b59a9bSPeter Grehan 	sizeof(struct vtpci_softc)
19910b59a9bSPeter Grehan };
20010b59a9bSPeter Grehan 
20110b59a9bSPeter Grehan devclass_t vtpci_devclass;
20210b59a9bSPeter Grehan 
20310b59a9bSPeter Grehan DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0);
20410b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1);
20510b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1);
20610b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1);
20710b59a9bSPeter Grehan 
20810b59a9bSPeter Grehan static int
20910b59a9bSPeter Grehan vtpci_probe(device_t dev)
21010b59a9bSPeter Grehan {
21110b59a9bSPeter Grehan 	char desc[36];
21210b59a9bSPeter Grehan 	const char *name;
21310b59a9bSPeter Grehan 
21410b59a9bSPeter Grehan 	if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
21510b59a9bSPeter Grehan 		return (ENXIO);
21610b59a9bSPeter Grehan 
21710b59a9bSPeter Grehan 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
21810b59a9bSPeter Grehan 	    pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX)
21910b59a9bSPeter Grehan 		return (ENXIO);
22010b59a9bSPeter Grehan 
22110b59a9bSPeter Grehan 	if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION)
22210b59a9bSPeter Grehan 		return (ENXIO);
22310b59a9bSPeter Grehan 
22410b59a9bSPeter Grehan 	name = virtio_device_name(pci_get_subdevice(dev));
22510b59a9bSPeter Grehan 	if (name == NULL)
22610b59a9bSPeter Grehan 		name = "Unknown";
22710b59a9bSPeter Grehan 
22810b59a9bSPeter Grehan 	snprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name);
22910b59a9bSPeter Grehan 	device_set_desc_copy(dev, desc);
23010b59a9bSPeter Grehan 
23110b59a9bSPeter Grehan 	return (BUS_PROBE_DEFAULT);
23210b59a9bSPeter Grehan }
23310b59a9bSPeter Grehan 
23410b59a9bSPeter Grehan static int
23510b59a9bSPeter Grehan vtpci_attach(device_t dev)
23610b59a9bSPeter Grehan {
23710b59a9bSPeter Grehan 	struct vtpci_softc *sc;
23810b59a9bSPeter Grehan 	device_t child;
23910b59a9bSPeter Grehan 	int rid;
24010b59a9bSPeter Grehan 
24110b59a9bSPeter Grehan 	sc = device_get_softc(dev);
24210b59a9bSPeter Grehan 	sc->vtpci_dev = dev;
24310b59a9bSPeter Grehan 
24410b59a9bSPeter Grehan 	pci_enable_busmaster(dev);
24510b59a9bSPeter Grehan 
24610b59a9bSPeter Grehan 	rid = PCIR_BAR(0);
24710b59a9bSPeter Grehan 	sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
24810b59a9bSPeter Grehan 	    RF_ACTIVE);
24910b59a9bSPeter Grehan 	if (sc->vtpci_res == NULL) {
25010b59a9bSPeter Grehan 		device_printf(dev, "cannot map I/O space\n");
25110b59a9bSPeter Grehan 		return (ENXIO);
25210b59a9bSPeter Grehan 	}
25310b59a9bSPeter Grehan 
254*cda6c6abSJohn Baldwin 	if (pci_find_cap(dev, PCIY_MSI, NULL) != 0)
25510b59a9bSPeter Grehan 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSI;
25610b59a9bSPeter Grehan 
257*cda6c6abSJohn Baldwin 	if (pci_find_cap(dev, PCIY_MSIX, NULL) == 0) {
25810b59a9bSPeter Grehan 		rid = PCIR_BAR(1);
25910b59a9bSPeter Grehan 		sc->vtpci_msix_res = bus_alloc_resource_any(dev,
26010b59a9bSPeter Grehan 		    SYS_RES_MEMORY, &rid, RF_ACTIVE);
26110b59a9bSPeter Grehan 	}
26210b59a9bSPeter Grehan 
26310b59a9bSPeter Grehan 	if (sc->vtpci_msix_res == NULL)
26410b59a9bSPeter Grehan 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSIX;
26510b59a9bSPeter Grehan 
26610b59a9bSPeter Grehan 	vtpci_reset(sc);
26710b59a9bSPeter Grehan 
26810b59a9bSPeter Grehan 	/* Tell the host we've noticed this device. */
26910b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
27010b59a9bSPeter Grehan 
27110b59a9bSPeter Grehan 	if ((child = device_add_child(dev, NULL, -1)) == NULL) {
27210b59a9bSPeter Grehan 		device_printf(dev, "cannot create child device\n");
27310b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
27410b59a9bSPeter Grehan 		vtpci_detach(dev);
27510b59a9bSPeter Grehan 		return (ENOMEM);
27610b59a9bSPeter Grehan 	}
27710b59a9bSPeter Grehan 
27810b59a9bSPeter Grehan 	sc->vtpci_child_dev = child;
27910b59a9bSPeter Grehan 	vtpci_probe_and_attach_child(sc);
28010b59a9bSPeter Grehan 
28110b59a9bSPeter Grehan 	return (0);
28210b59a9bSPeter Grehan }
28310b59a9bSPeter Grehan 
28410b59a9bSPeter Grehan static int
28510b59a9bSPeter Grehan vtpci_detach(device_t dev)
28610b59a9bSPeter Grehan {
28710b59a9bSPeter Grehan 	struct vtpci_softc *sc;
28810b59a9bSPeter Grehan 	device_t child;
28910b59a9bSPeter Grehan 	int error;
29010b59a9bSPeter Grehan 
29110b59a9bSPeter Grehan 	sc = device_get_softc(dev);
29210b59a9bSPeter Grehan 
29310b59a9bSPeter Grehan 	if ((child = sc->vtpci_child_dev) != NULL) {
29410b59a9bSPeter Grehan 		error = device_delete_child(dev, child);
29510b59a9bSPeter Grehan 		if (error)
29610b59a9bSPeter Grehan 			return (error);
29710b59a9bSPeter Grehan 		sc->vtpci_child_dev = NULL;
29810b59a9bSPeter Grehan 	}
29910b59a9bSPeter Grehan 
30010b59a9bSPeter Grehan 	vtpci_reset(sc);
30110b59a9bSPeter Grehan 
30210b59a9bSPeter Grehan 	if (sc->vtpci_msix_res != NULL) {
30310b59a9bSPeter Grehan 		bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1),
30410b59a9bSPeter Grehan 		    sc->vtpci_msix_res);
30510b59a9bSPeter Grehan 		sc->vtpci_msix_res = NULL;
30610b59a9bSPeter Grehan 	}
30710b59a9bSPeter Grehan 
30810b59a9bSPeter Grehan 	if (sc->vtpci_res != NULL) {
30910b59a9bSPeter Grehan 		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
31010b59a9bSPeter Grehan 		    sc->vtpci_res);
31110b59a9bSPeter Grehan 		sc->vtpci_res = NULL;
31210b59a9bSPeter Grehan 	}
31310b59a9bSPeter Grehan 
31410b59a9bSPeter Grehan 	return (0);
31510b59a9bSPeter Grehan }
31610b59a9bSPeter Grehan 
31710b59a9bSPeter Grehan static int
31810b59a9bSPeter Grehan vtpci_suspend(device_t dev)
31910b59a9bSPeter Grehan {
32010b59a9bSPeter Grehan 
32110b59a9bSPeter Grehan 	return (bus_generic_suspend(dev));
32210b59a9bSPeter Grehan }
32310b59a9bSPeter Grehan 
32410b59a9bSPeter Grehan static int
32510b59a9bSPeter Grehan vtpci_resume(device_t dev)
32610b59a9bSPeter Grehan {
32710b59a9bSPeter Grehan 
32810b59a9bSPeter Grehan 	return (bus_generic_resume(dev));
32910b59a9bSPeter Grehan }
33010b59a9bSPeter Grehan 
33110b59a9bSPeter Grehan static int
33210b59a9bSPeter Grehan vtpci_shutdown(device_t dev)
33310b59a9bSPeter Grehan {
33410b59a9bSPeter Grehan 
33510b59a9bSPeter Grehan 	(void) bus_generic_shutdown(dev);
33610b59a9bSPeter Grehan 	/* Forcibly stop the host device. */
33710b59a9bSPeter Grehan 	vtpci_stop(dev);
33810b59a9bSPeter Grehan 
33910b59a9bSPeter Grehan 	return (0);
34010b59a9bSPeter Grehan }
34110b59a9bSPeter Grehan 
34210b59a9bSPeter Grehan static void
34310b59a9bSPeter Grehan vtpci_driver_added(device_t dev, driver_t *driver)
34410b59a9bSPeter Grehan {
34510b59a9bSPeter Grehan 	struct vtpci_softc *sc;
34610b59a9bSPeter Grehan 
34710b59a9bSPeter Grehan 	sc = device_get_softc(dev);
34810b59a9bSPeter Grehan 
34910b59a9bSPeter Grehan 	vtpci_probe_and_attach_child(sc);
35010b59a9bSPeter Grehan }
35110b59a9bSPeter Grehan 
35210b59a9bSPeter Grehan static void
35310b59a9bSPeter Grehan vtpci_child_detached(device_t dev, device_t child)
35410b59a9bSPeter Grehan {
35510b59a9bSPeter Grehan 	struct vtpci_softc *sc;
35610b59a9bSPeter Grehan 
35710b59a9bSPeter Grehan 	sc = device_get_softc(dev);
35810b59a9bSPeter Grehan 
35910b59a9bSPeter Grehan 	vtpci_reset(sc);
36010b59a9bSPeter Grehan 	vtpci_release_child_resources(sc);
36110b59a9bSPeter Grehan }
36210b59a9bSPeter Grehan 
36310b59a9bSPeter Grehan static int
36410b59a9bSPeter Grehan vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
36510b59a9bSPeter Grehan {
36610b59a9bSPeter Grehan 	struct vtpci_softc *sc;
36710b59a9bSPeter Grehan 
36810b59a9bSPeter Grehan 	sc = device_get_softc(dev);
36910b59a9bSPeter Grehan 
37010b59a9bSPeter Grehan 	if (sc->vtpci_child_dev != child)
37110b59a9bSPeter Grehan 		return (ENOENT);
37210b59a9bSPeter Grehan 
37310b59a9bSPeter Grehan 	switch (index) {
37410b59a9bSPeter Grehan 	case VIRTIO_IVAR_DEVTYPE:
37510b59a9bSPeter Grehan 		*result = pci_get_subdevice(dev);
37610b59a9bSPeter Grehan 		break;
37710b59a9bSPeter Grehan 	default:
37810b59a9bSPeter Grehan 		return (ENOENT);
37910b59a9bSPeter Grehan 	}
38010b59a9bSPeter Grehan 
38110b59a9bSPeter Grehan 	return (0);
38210b59a9bSPeter Grehan }
38310b59a9bSPeter Grehan 
38410b59a9bSPeter Grehan static int
38510b59a9bSPeter Grehan vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
38610b59a9bSPeter Grehan {
38710b59a9bSPeter Grehan 	struct vtpci_softc *sc;
38810b59a9bSPeter Grehan 
38910b59a9bSPeter Grehan 	sc = device_get_softc(dev);
39010b59a9bSPeter Grehan 
39110b59a9bSPeter Grehan 	if (sc->vtpci_child_dev != child)
39210b59a9bSPeter Grehan 		return (ENOENT);
39310b59a9bSPeter Grehan 
39410b59a9bSPeter Grehan 	switch (index) {
39510b59a9bSPeter Grehan 	case VIRTIO_IVAR_FEATURE_DESC:
39610b59a9bSPeter Grehan 		sc->vtpci_child_feat_desc = (void *) value;
39710b59a9bSPeter Grehan 		break;
39810b59a9bSPeter Grehan 	default:
39910b59a9bSPeter Grehan 		return (ENOENT);
40010b59a9bSPeter Grehan 	}
40110b59a9bSPeter Grehan 
40210b59a9bSPeter Grehan 	return (0);
40310b59a9bSPeter Grehan }
40410b59a9bSPeter Grehan 
40510b59a9bSPeter Grehan static uint64_t
40610b59a9bSPeter Grehan vtpci_negotiate_features(device_t dev, uint64_t child_features)
40710b59a9bSPeter Grehan {
40810b59a9bSPeter Grehan 	struct vtpci_softc *sc;
40910b59a9bSPeter Grehan 	uint64_t host_features, features;
41010b59a9bSPeter Grehan 
41110b59a9bSPeter Grehan 	sc = device_get_softc(dev);
41210b59a9bSPeter Grehan 
41310b59a9bSPeter Grehan 	host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
41410b59a9bSPeter Grehan 	vtpci_describe_features(sc, "host", host_features);
41510b59a9bSPeter Grehan 
41610b59a9bSPeter Grehan 	/*
41710b59a9bSPeter Grehan 	 * Limit negotiated features to what the driver, virtqueue, and
41810b59a9bSPeter Grehan 	 * host all support.
41910b59a9bSPeter Grehan 	 */
42010b59a9bSPeter Grehan 	features = host_features & child_features;
42110b59a9bSPeter Grehan 	features = virtqueue_filter_features(features);
42210b59a9bSPeter Grehan 	sc->vtpci_features = features;
42310b59a9bSPeter Grehan 
42410b59a9bSPeter Grehan 	vtpci_describe_features(sc, "negotiated", features);
42510b59a9bSPeter Grehan 	vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
42610b59a9bSPeter Grehan 
42710b59a9bSPeter Grehan 	return (features);
42810b59a9bSPeter Grehan }
42910b59a9bSPeter Grehan 
43010b59a9bSPeter Grehan static int
43110b59a9bSPeter Grehan vtpci_with_feature(device_t dev, uint64_t feature)
43210b59a9bSPeter Grehan {
43310b59a9bSPeter Grehan 	struct vtpci_softc *sc;
43410b59a9bSPeter Grehan 
43510b59a9bSPeter Grehan 	sc = device_get_softc(dev);
43610b59a9bSPeter Grehan 
43710b59a9bSPeter Grehan 	return ((sc->vtpci_features & feature) != 0);
43810b59a9bSPeter Grehan }
43910b59a9bSPeter Grehan 
44010b59a9bSPeter Grehan static int
44110b59a9bSPeter Grehan vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
44210b59a9bSPeter Grehan     struct vq_alloc_info *vq_info)
44310b59a9bSPeter Grehan {
44410b59a9bSPeter Grehan 	struct vtpci_softc *sc;
44510b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
44610b59a9bSPeter Grehan 	struct vq_alloc_info *info;
44710b59a9bSPeter Grehan 	int queue, error;
44810b59a9bSPeter Grehan 	uint16_t vq_size;
44910b59a9bSPeter Grehan 
45010b59a9bSPeter Grehan 	sc = device_get_softc(dev);
45110b59a9bSPeter Grehan 
45210b59a9bSPeter Grehan 	if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
45310b59a9bSPeter Grehan 	    nvqs > VIRTIO_MAX_VIRTQUEUES)
45410b59a9bSPeter Grehan 		return (EINVAL);
45510b59a9bSPeter Grehan 
45610b59a9bSPeter Grehan 	error = vtpci_alloc_interrupts(sc, flags, nvqs, vq_info);
45710b59a9bSPeter Grehan 	if (error) {
45810b59a9bSPeter Grehan 		device_printf(dev, "cannot allocate interrupts\n");
45910b59a9bSPeter Grehan 		return (error);
46010b59a9bSPeter Grehan 	}
46110b59a9bSPeter Grehan 
46210b59a9bSPeter Grehan 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
46310b59a9bSPeter Grehan 		error = vtpci_register_msix_vector(sc,
46410b59a9bSPeter Grehan 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
46510b59a9bSPeter Grehan 		if (error)
46610b59a9bSPeter Grehan 			return (error);
46710b59a9bSPeter Grehan 	}
46810b59a9bSPeter Grehan 
46910b59a9bSPeter Grehan 	for (queue = 0; queue < nvqs; queue++) {
47010b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[queue];
47110b59a9bSPeter Grehan 		info = &vq_info[queue];
47210b59a9bSPeter Grehan 
47310b59a9bSPeter Grehan 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
47410b59a9bSPeter Grehan 
47510b59a9bSPeter Grehan 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
47610b59a9bSPeter Grehan 		error = virtqueue_alloc(dev, queue, vq_size,
47710b59a9bSPeter Grehan 		    VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
47810b59a9bSPeter Grehan 		if (error)
47910b59a9bSPeter Grehan 			return (error);
48010b59a9bSPeter Grehan 
48110b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
48210b59a9bSPeter Grehan 			error = vtpci_register_msix_vector(sc,
48310b59a9bSPeter Grehan 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
48410b59a9bSPeter Grehan 			if (error)
48510b59a9bSPeter Grehan 				return (error);
48610b59a9bSPeter Grehan 		}
48710b59a9bSPeter Grehan 
48810b59a9bSPeter Grehan 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
48910b59a9bSPeter Grehan 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
49010b59a9bSPeter Grehan 
49110b59a9bSPeter Grehan 		*info->vqai_vq = vqx->vq;
49210b59a9bSPeter Grehan 		sc->vtpci_nvqs++;
49310b59a9bSPeter Grehan 	}
49410b59a9bSPeter Grehan 
49510b59a9bSPeter Grehan 	return (0);
49610b59a9bSPeter Grehan }
49710b59a9bSPeter Grehan 
49810b59a9bSPeter Grehan static int
49910b59a9bSPeter Grehan vtpci_setup_intr(device_t dev, enum intr_type type)
50010b59a9bSPeter Grehan {
50110b59a9bSPeter Grehan 	struct vtpci_softc *sc;
50210b59a9bSPeter Grehan 	struct vtpci_intr_resource *ires;
50310b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
50410b59a9bSPeter Grehan 	int i, flags, error;
50510b59a9bSPeter Grehan 
50610b59a9bSPeter Grehan 	sc = device_get_softc(dev);
50710b59a9bSPeter Grehan 	flags = type | INTR_MPSAFE;
50810b59a9bSPeter Grehan 	ires = &sc->vtpci_intr_res[0];
50910b59a9bSPeter Grehan 
51010b59a9bSPeter Grehan 	if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
51110b59a9bSPeter Grehan 		error = bus_setup_intr(dev, ires->irq, flags,
51210b59a9bSPeter Grehan 		    vtpci_legacy_intr, NULL, sc, &ires->intrhand);
51310b59a9bSPeter Grehan 
51410b59a9bSPeter Grehan 		return (error);
51510b59a9bSPeter Grehan 	}
51610b59a9bSPeter Grehan 
51710b59a9bSPeter Grehan 	error = bus_setup_intr(dev, ires->irq, flags, vtpci_config_intr,
51810b59a9bSPeter Grehan 	    NULL, sc, &ires->intrhand);
51910b59a9bSPeter Grehan 	if (error)
52010b59a9bSPeter Grehan 		return (error);
52110b59a9bSPeter Grehan 
52210b59a9bSPeter Grehan 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) {
52310b59a9bSPeter Grehan 		ires = &sc->vtpci_intr_res[1];
52410b59a9bSPeter Grehan 		error = bus_setup_intr(dev, ires->irq, flags,
52510b59a9bSPeter Grehan 		    vtpci_vq_shared_intr, NULL, sc, &ires->intrhand);
52610b59a9bSPeter Grehan 
52710b59a9bSPeter Grehan 		return (error);
52810b59a9bSPeter Grehan 	}
52910b59a9bSPeter Grehan 
53010b59a9bSPeter Grehan 	/* Setup an interrupt handler for each virtqueue. */
53110b59a9bSPeter Grehan 	for (i = 0; i < sc->vtpci_nvqs; i++) {
53210b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[i];
53310b59a9bSPeter Grehan 		if (vqx->ires_idx < 1)
53410b59a9bSPeter Grehan 			continue;
53510b59a9bSPeter Grehan 
53610b59a9bSPeter Grehan 		ires = &sc->vtpci_intr_res[vqx->ires_idx];
53710b59a9bSPeter Grehan 		error = bus_setup_intr(dev, ires->irq, flags,
53810b59a9bSPeter Grehan 		    vtpci_vq_intr, NULL, vqx->vq, &ires->intrhand);
53910b59a9bSPeter Grehan 		if (error)
54010b59a9bSPeter Grehan 			return (error);
54110b59a9bSPeter Grehan 	}
54210b59a9bSPeter Grehan 
54310b59a9bSPeter Grehan 	return (0);
54410b59a9bSPeter Grehan }
54510b59a9bSPeter Grehan 
54610b59a9bSPeter Grehan static void
54710b59a9bSPeter Grehan vtpci_stop(device_t dev)
54810b59a9bSPeter Grehan {
54910b59a9bSPeter Grehan 
55010b59a9bSPeter Grehan 	vtpci_reset(device_get_softc(dev));
55110b59a9bSPeter Grehan }
55210b59a9bSPeter Grehan 
55310b59a9bSPeter Grehan static int
55410b59a9bSPeter Grehan vtpci_reinit(device_t dev, uint64_t features)
55510b59a9bSPeter Grehan {
55610b59a9bSPeter Grehan 	struct vtpci_softc *sc;
55710b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
55810b59a9bSPeter Grehan 	struct virtqueue *vq;
55910b59a9bSPeter Grehan 	int queue, error;
56010b59a9bSPeter Grehan 	uint16_t vq_size;
56110b59a9bSPeter Grehan 
56210b59a9bSPeter Grehan 	sc = device_get_softc(dev);
56310b59a9bSPeter Grehan 
56410b59a9bSPeter Grehan 	/*
56510b59a9bSPeter Grehan 	 * Redrive the device initialization. This is a bit of an abuse
56610b59a9bSPeter Grehan 	 * of the specification, but both VirtualBox and QEMU/KVM seem
56710b59a9bSPeter Grehan 	 * to play nice. We do not allow the host device to change from
56810b59a9bSPeter Grehan 	 * what was originally negotiated beyond what the guest driver
56910b59a9bSPeter Grehan 	 * changed (MSIX state should not change, number of virtqueues
57010b59a9bSPeter Grehan 	 * and their size remain the same, etc).
57110b59a9bSPeter Grehan 	 */
57210b59a9bSPeter Grehan 
57310b59a9bSPeter Grehan 	if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
57410b59a9bSPeter Grehan 		vtpci_stop(dev);
57510b59a9bSPeter Grehan 
57610b59a9bSPeter Grehan 	/*
57710b59a9bSPeter Grehan 	 * Quickly drive the status through ACK and DRIVER. The device
57810b59a9bSPeter Grehan 	 * does not become usable again until vtpci_reinit_complete().
57910b59a9bSPeter Grehan 	 */
58010b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
58110b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
58210b59a9bSPeter Grehan 
58310b59a9bSPeter Grehan 	vtpci_negotiate_features(dev, features);
58410b59a9bSPeter Grehan 
58510b59a9bSPeter Grehan 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
58610b59a9bSPeter Grehan 		error = vtpci_register_msix_vector(sc,
58710b59a9bSPeter Grehan 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
58810b59a9bSPeter Grehan 		if (error)
58910b59a9bSPeter Grehan 			return (error);
59010b59a9bSPeter Grehan 	}
59110b59a9bSPeter Grehan 
59210b59a9bSPeter Grehan 	for (queue = 0; queue < sc->vtpci_nvqs; queue++) {
59310b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[queue];
59410b59a9bSPeter Grehan 		vq = vqx->vq;
59510b59a9bSPeter Grehan 
59610b59a9bSPeter Grehan 		KASSERT(vq != NULL, ("vq %d not allocated", queue));
59710b59a9bSPeter Grehan 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
59810b59a9bSPeter Grehan 
59910b59a9bSPeter Grehan 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
60010b59a9bSPeter Grehan 		error = virtqueue_reinit(vq, vq_size);
60110b59a9bSPeter Grehan 		if (error)
60210b59a9bSPeter Grehan 			return (error);
60310b59a9bSPeter Grehan 
60410b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
60510b59a9bSPeter Grehan 			error = vtpci_register_msix_vector(sc,
60610b59a9bSPeter Grehan 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
60710b59a9bSPeter Grehan 			if (error)
60810b59a9bSPeter Grehan 				return (error);
60910b59a9bSPeter Grehan 		}
61010b59a9bSPeter Grehan 
61110b59a9bSPeter Grehan 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
61210b59a9bSPeter Grehan 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
61310b59a9bSPeter Grehan 	}
61410b59a9bSPeter Grehan 
61510b59a9bSPeter Grehan 	return (0);
61610b59a9bSPeter Grehan }
61710b59a9bSPeter Grehan 
61810b59a9bSPeter Grehan static void
61910b59a9bSPeter Grehan vtpci_reinit_complete(device_t dev)
62010b59a9bSPeter Grehan {
62110b59a9bSPeter Grehan 
62210b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
62310b59a9bSPeter Grehan }
62410b59a9bSPeter Grehan 
62510b59a9bSPeter Grehan static void
62610b59a9bSPeter Grehan vtpci_notify_virtqueue(device_t dev, uint16_t queue)
62710b59a9bSPeter Grehan {
62810b59a9bSPeter Grehan 	struct vtpci_softc *sc;
62910b59a9bSPeter Grehan 
63010b59a9bSPeter Grehan 	sc = device_get_softc(dev);
63110b59a9bSPeter Grehan 
63210b59a9bSPeter Grehan 	vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
63310b59a9bSPeter Grehan }
63410b59a9bSPeter Grehan 
63510b59a9bSPeter Grehan static uint8_t
63610b59a9bSPeter Grehan vtpci_get_status(device_t dev)
63710b59a9bSPeter Grehan {
63810b59a9bSPeter Grehan 	struct vtpci_softc *sc;
63910b59a9bSPeter Grehan 
64010b59a9bSPeter Grehan 	sc = device_get_softc(dev);
64110b59a9bSPeter Grehan 
64210b59a9bSPeter Grehan 	return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
64310b59a9bSPeter Grehan }
64410b59a9bSPeter Grehan 
64510b59a9bSPeter Grehan static void
64610b59a9bSPeter Grehan vtpci_set_status(device_t dev, uint8_t status)
64710b59a9bSPeter Grehan {
64810b59a9bSPeter Grehan 	struct vtpci_softc *sc;
64910b59a9bSPeter Grehan 
65010b59a9bSPeter Grehan 	sc = device_get_softc(dev);
65110b59a9bSPeter Grehan 
65210b59a9bSPeter Grehan 	if (status != VIRTIO_CONFIG_STATUS_RESET)
65310b59a9bSPeter Grehan 		status |= vtpci_get_status(dev);
65410b59a9bSPeter Grehan 
65510b59a9bSPeter Grehan 	vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
65610b59a9bSPeter Grehan }
65710b59a9bSPeter Grehan 
65810b59a9bSPeter Grehan static void
65910b59a9bSPeter Grehan vtpci_read_dev_config(device_t dev, bus_size_t offset,
66010b59a9bSPeter Grehan     void *dst, int length)
66110b59a9bSPeter Grehan {
66210b59a9bSPeter Grehan 	struct vtpci_softc *sc;
66310b59a9bSPeter Grehan 	bus_size_t off;
66410b59a9bSPeter Grehan 	uint8_t *d;
66510b59a9bSPeter Grehan 	int size;
66610b59a9bSPeter Grehan 
66710b59a9bSPeter Grehan 	sc = device_get_softc(dev);
66810b59a9bSPeter Grehan 	off = VIRTIO_PCI_CONFIG(sc) + offset;
66910b59a9bSPeter Grehan 
67010b59a9bSPeter Grehan 	for (d = dst; length > 0; d += size, off += size, length -= size) {
67110b59a9bSPeter Grehan 		if (length >= 4) {
67210b59a9bSPeter Grehan 			size = 4;
67310b59a9bSPeter Grehan 			*(uint32_t *)d = vtpci_read_config_4(sc, off);
67410b59a9bSPeter Grehan 		} else if (length >= 2) {
67510b59a9bSPeter Grehan 			size = 2;
67610b59a9bSPeter Grehan 			*(uint16_t *)d = vtpci_read_config_2(sc, off);
67710b59a9bSPeter Grehan 		} else {
67810b59a9bSPeter Grehan 			size = 1;
67910b59a9bSPeter Grehan 			*d = vtpci_read_config_1(sc, off);
68010b59a9bSPeter Grehan 		}
68110b59a9bSPeter Grehan 	}
68210b59a9bSPeter Grehan }
68310b59a9bSPeter Grehan 
68410b59a9bSPeter Grehan static void
68510b59a9bSPeter Grehan vtpci_write_dev_config(device_t dev, bus_size_t offset,
68610b59a9bSPeter Grehan     void *src, int length)
68710b59a9bSPeter Grehan {
68810b59a9bSPeter Grehan 	struct vtpci_softc *sc;
68910b59a9bSPeter Grehan 	bus_size_t off;
69010b59a9bSPeter Grehan 	uint8_t *s;
69110b59a9bSPeter Grehan 	int size;
69210b59a9bSPeter Grehan 
69310b59a9bSPeter Grehan 	sc = device_get_softc(dev);
69410b59a9bSPeter Grehan 	off = VIRTIO_PCI_CONFIG(sc) + offset;
69510b59a9bSPeter Grehan 
69610b59a9bSPeter Grehan 	for (s = src; length > 0; s += size, off += size, length -= size) {
69710b59a9bSPeter Grehan 		if (length >= 4) {
69810b59a9bSPeter Grehan 			size = 4;
69910b59a9bSPeter Grehan 			vtpci_write_config_4(sc, off, *(uint32_t *)s);
70010b59a9bSPeter Grehan 		} else if (length >= 2) {
70110b59a9bSPeter Grehan 			size = 2;
70210b59a9bSPeter Grehan 			vtpci_write_config_2(sc, off, *(uint16_t *)s);
70310b59a9bSPeter Grehan 		} else {
70410b59a9bSPeter Grehan 			size = 1;
70510b59a9bSPeter Grehan 			vtpci_write_config_1(sc, off, *s);
70610b59a9bSPeter Grehan 		}
70710b59a9bSPeter Grehan 	}
70810b59a9bSPeter Grehan }
70910b59a9bSPeter Grehan 
71010b59a9bSPeter Grehan static void
71110b59a9bSPeter Grehan vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
71210b59a9bSPeter Grehan     uint64_t features)
71310b59a9bSPeter Grehan {
71410b59a9bSPeter Grehan 	device_t dev, child;
71510b59a9bSPeter Grehan 
71610b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
71710b59a9bSPeter Grehan 	child = sc->vtpci_child_dev;
71810b59a9bSPeter Grehan 
71910b59a9bSPeter Grehan 	if (device_is_attached(child) && bootverbose == 0)
72010b59a9bSPeter Grehan 		return;
72110b59a9bSPeter Grehan 
72210b59a9bSPeter Grehan 	virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
72310b59a9bSPeter Grehan }
72410b59a9bSPeter Grehan 
72510b59a9bSPeter Grehan static void
72610b59a9bSPeter Grehan vtpci_probe_and_attach_child(struct vtpci_softc *sc)
72710b59a9bSPeter Grehan {
72810b59a9bSPeter Grehan 	device_t dev, child;
72910b59a9bSPeter Grehan 
73010b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
73110b59a9bSPeter Grehan 	child = sc->vtpci_child_dev;
73210b59a9bSPeter Grehan 
73310b59a9bSPeter Grehan 	if (child == NULL)
73410b59a9bSPeter Grehan 		return;
73510b59a9bSPeter Grehan 
73610b59a9bSPeter Grehan 	if (device_get_state(child) != DS_NOTPRESENT)
73710b59a9bSPeter Grehan 		return;
73810b59a9bSPeter Grehan 
73910b59a9bSPeter Grehan 	if (device_probe(child) != 0)
74010b59a9bSPeter Grehan 		return;
74110b59a9bSPeter Grehan 
74210b59a9bSPeter Grehan 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
74310b59a9bSPeter Grehan 	if (device_attach(child) != 0) {
74410b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
74510b59a9bSPeter Grehan 		vtpci_reset(sc);
74610b59a9bSPeter Grehan 		vtpci_release_child_resources(sc);
74710b59a9bSPeter Grehan 
74810b59a9bSPeter Grehan 		/* Reset status for future attempt. */
74910b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
75010b59a9bSPeter Grehan 	} else
75110b59a9bSPeter Grehan 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
75210b59a9bSPeter Grehan }
75310b59a9bSPeter Grehan 
75410b59a9bSPeter Grehan static int
75510b59a9bSPeter Grehan vtpci_alloc_interrupts(struct vtpci_softc *sc, int flags, int nvqs,
75610b59a9bSPeter Grehan     struct vq_alloc_info *vq_info)
75710b59a9bSPeter Grehan {
75810b59a9bSPeter Grehan 	int i, nvectors, error;
75910b59a9bSPeter Grehan 
76010b59a9bSPeter Grehan 	/*
76110b59a9bSPeter Grehan 	 * Only allocate a vector for virtqueues that are actually
76210b59a9bSPeter Grehan 	 * expecting an interrupt.
76310b59a9bSPeter Grehan 	 */
76410b59a9bSPeter Grehan 	for (nvectors = 0, i = 0; i < nvqs; i++)
76510b59a9bSPeter Grehan 		if (vq_info[i].vqai_intr != NULL)
76610b59a9bSPeter Grehan 			nvectors++;
76710b59a9bSPeter Grehan 
76810b59a9bSPeter Grehan 	if (vtpci_disable_msix != 0 ||
76910b59a9bSPeter Grehan 	    sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSIX ||
77010b59a9bSPeter Grehan 	    flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX ||
77110b59a9bSPeter Grehan 	    vtpci_alloc_msix(sc, nvectors) != 0) {
77210b59a9bSPeter Grehan 		/*
77310b59a9bSPeter Grehan 		 * Use MSI interrupts if available. Otherwise, we fallback
77410b59a9bSPeter Grehan 		 * to legacy interrupts.
77510b59a9bSPeter Grehan 		 */
77610b59a9bSPeter Grehan 		if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSI) == 0 &&
77710b59a9bSPeter Grehan 		    vtpci_alloc_msi(sc) == 0)
77810b59a9bSPeter Grehan 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
77910b59a9bSPeter Grehan 
78010b59a9bSPeter Grehan 		sc->vtpci_nintr_res = 1;
78110b59a9bSPeter Grehan 	}
78210b59a9bSPeter Grehan 
78310b59a9bSPeter Grehan 	error = vtpci_alloc_intr_resources(sc, nvqs, vq_info);
78410b59a9bSPeter Grehan 
78510b59a9bSPeter Grehan 	return (error);
78610b59a9bSPeter Grehan }
78710b59a9bSPeter Grehan 
78810b59a9bSPeter Grehan static int
78910b59a9bSPeter Grehan vtpci_alloc_intr_resources(struct vtpci_softc *sc, int nvqs,
79010b59a9bSPeter Grehan     struct vq_alloc_info *vq_info)
79110b59a9bSPeter Grehan {
79210b59a9bSPeter Grehan 	device_t dev;
79310b59a9bSPeter Grehan 	struct resource *irq;
79410b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
79510b59a9bSPeter Grehan 	int i, rid, flags, res_idx;
79610b59a9bSPeter Grehan 
79710b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
79810b59a9bSPeter Grehan 	flags = RF_ACTIVE;
79910b59a9bSPeter Grehan 
80010b59a9bSPeter Grehan 	if ((sc->vtpci_flags &
80110b59a9bSPeter Grehan 	    (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) == 0) {
80210b59a9bSPeter Grehan 		rid = 0;
80310b59a9bSPeter Grehan 		flags |= RF_SHAREABLE;
80410b59a9bSPeter Grehan 	} else
80510b59a9bSPeter Grehan 		rid = 1;
80610b59a9bSPeter Grehan 
80710b59a9bSPeter Grehan 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
80810b59a9bSPeter Grehan 		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags);
80910b59a9bSPeter Grehan 		if (irq == NULL)
81010b59a9bSPeter Grehan 			return (ENXIO);
81110b59a9bSPeter Grehan 
81210b59a9bSPeter Grehan 		sc->vtpci_intr_res[i].irq = irq;
81310b59a9bSPeter Grehan 		sc->vtpci_intr_res[i].rid = rid++;
81410b59a9bSPeter Grehan 	}
81510b59a9bSPeter Grehan 
81610b59a9bSPeter Grehan 	/*
81710b59a9bSPeter Grehan 	 * Map the virtqueue into the correct index in vq_intr_res[]. Note the
81810b59a9bSPeter Grehan 	 * first index is reserved for configuration changes notifications.
81910b59a9bSPeter Grehan 	 */
82010b59a9bSPeter Grehan 	for (i = 0, res_idx = 1; i < nvqs; i++) {
82110b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[i];
82210b59a9bSPeter Grehan 
82310b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
82410b59a9bSPeter Grehan 			if (vq_info[i].vqai_intr == NULL)
82510b59a9bSPeter Grehan 				vqx->ires_idx = -1;
82610b59a9bSPeter Grehan 			else if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
82710b59a9bSPeter Grehan 				vqx->ires_idx = res_idx;
82810b59a9bSPeter Grehan 			else
82910b59a9bSPeter Grehan 				vqx->ires_idx = res_idx++;
83010b59a9bSPeter Grehan 		} else
83110b59a9bSPeter Grehan 			vqx->ires_idx = -1;
83210b59a9bSPeter Grehan 	}
83310b59a9bSPeter Grehan 
83410b59a9bSPeter Grehan 	return (0);
83510b59a9bSPeter Grehan }
83610b59a9bSPeter Grehan 
83710b59a9bSPeter Grehan static int
83810b59a9bSPeter Grehan vtpci_alloc_msi(struct vtpci_softc *sc)
83910b59a9bSPeter Grehan {
84010b59a9bSPeter Grehan 	device_t dev;
84110b59a9bSPeter Grehan 	int nmsi, cnt;
84210b59a9bSPeter Grehan 
84310b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
84410b59a9bSPeter Grehan 	nmsi = pci_msi_count(dev);
84510b59a9bSPeter Grehan 
84610b59a9bSPeter Grehan 	if (nmsi < 1)
84710b59a9bSPeter Grehan 		return (1);
84810b59a9bSPeter Grehan 
84910b59a9bSPeter Grehan 	cnt = 1;
85010b59a9bSPeter Grehan 	if (pci_alloc_msi(dev, &cnt) == 0 && cnt == 1)
85110b59a9bSPeter Grehan 		return (0);
85210b59a9bSPeter Grehan 
85310b59a9bSPeter Grehan 	return (1);
85410b59a9bSPeter Grehan }
85510b59a9bSPeter Grehan 
85610b59a9bSPeter Grehan static int
85710b59a9bSPeter Grehan vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors)
85810b59a9bSPeter Grehan {
85910b59a9bSPeter Grehan 	device_t dev;
86010b59a9bSPeter Grehan 	int nmsix, cnt, required;
86110b59a9bSPeter Grehan 
86210b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
86310b59a9bSPeter Grehan 
86410b59a9bSPeter Grehan 	nmsix = pci_msix_count(dev);
86510b59a9bSPeter Grehan 	if (nmsix < 1)
86610b59a9bSPeter Grehan 		return (1);
86710b59a9bSPeter Grehan 
86810b59a9bSPeter Grehan 	/* An additional vector is needed for the config changes. */
86910b59a9bSPeter Grehan 	required = nvectors + 1;
87010b59a9bSPeter Grehan 	if (nmsix >= required) {
87110b59a9bSPeter Grehan 		cnt = required;
87210b59a9bSPeter Grehan 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required)
87310b59a9bSPeter Grehan 			goto out;
87410b59a9bSPeter Grehan 
87510b59a9bSPeter Grehan 		pci_release_msi(dev);
87610b59a9bSPeter Grehan 	}
87710b59a9bSPeter Grehan 
87810b59a9bSPeter Grehan 	/* Attempt shared MSIX configuration. */
87910b59a9bSPeter Grehan 	required = 2;
88010b59a9bSPeter Grehan 	if (nmsix >= required) {
88110b59a9bSPeter Grehan 		cnt = required;
88210b59a9bSPeter Grehan 		if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) {
88310b59a9bSPeter Grehan 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_SHARED_MSIX;
88410b59a9bSPeter Grehan 			goto out;
88510b59a9bSPeter Grehan 		}
88610b59a9bSPeter Grehan 
88710b59a9bSPeter Grehan 		pci_release_msi(dev);
88810b59a9bSPeter Grehan 	}
88910b59a9bSPeter Grehan 
89010b59a9bSPeter Grehan 	return (1);
89110b59a9bSPeter Grehan 
89210b59a9bSPeter Grehan out:
89310b59a9bSPeter Grehan 	sc->vtpci_nintr_res = required;
89410b59a9bSPeter Grehan 	sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
89510b59a9bSPeter Grehan 
89610b59a9bSPeter Grehan 	if (bootverbose) {
89710b59a9bSPeter Grehan 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX)
89810b59a9bSPeter Grehan 			device_printf(dev, "using shared virtqueue MSIX\n");
89910b59a9bSPeter Grehan 		else
90010b59a9bSPeter Grehan 			device_printf(dev, "using per virtqueue MSIX\n");
90110b59a9bSPeter Grehan 	}
90210b59a9bSPeter Grehan 
90310b59a9bSPeter Grehan 	return (0);
90410b59a9bSPeter Grehan }
90510b59a9bSPeter Grehan 
90610b59a9bSPeter Grehan static int
90710b59a9bSPeter Grehan vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
90810b59a9bSPeter Grehan {
90910b59a9bSPeter Grehan 	device_t dev;
91010b59a9bSPeter Grehan 	uint16_t vector;
91110b59a9bSPeter Grehan 
91210b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
91310b59a9bSPeter Grehan 
91410b59a9bSPeter Grehan 	if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
91510b59a9bSPeter Grehan 	    offset != VIRTIO_MSI_QUEUE_VECTOR)
91610b59a9bSPeter Grehan 		return (EINVAL);
91710b59a9bSPeter Grehan 
91810b59a9bSPeter Grehan 	if (res_idx != -1) {
91910b59a9bSPeter Grehan 		/* Map from rid to host vector. */
92010b59a9bSPeter Grehan 		vector = sc->vtpci_intr_res[res_idx].rid - 1;
92110b59a9bSPeter Grehan 	} else
92210b59a9bSPeter Grehan 		vector = VIRTIO_MSI_NO_VECTOR;
92310b59a9bSPeter Grehan 
92410b59a9bSPeter Grehan 	/* The first resource is special; make sure it is used correctly. */
92510b59a9bSPeter Grehan 	if (res_idx == 0) {
92610b59a9bSPeter Grehan 		KASSERT(vector == 0, ("unexpected config vector"));
92710b59a9bSPeter Grehan 		KASSERT(offset == VIRTIO_MSI_CONFIG_VECTOR,
92810b59a9bSPeter Grehan 		    ("unexpected config offset"));
92910b59a9bSPeter Grehan 	}
93010b59a9bSPeter Grehan 
93110b59a9bSPeter Grehan 	vtpci_write_config_2(sc, offset, vector);
93210b59a9bSPeter Grehan 
93310b59a9bSPeter Grehan 	if (vtpci_read_config_2(sc, offset) != vector) {
93410b59a9bSPeter Grehan 		device_printf(dev, "insufficient host resources for "
93510b59a9bSPeter Grehan 		    "MSIX interrupts\n");
93610b59a9bSPeter Grehan 		return (ENODEV);
93710b59a9bSPeter Grehan 	}
93810b59a9bSPeter Grehan 
93910b59a9bSPeter Grehan 	return (0);
94010b59a9bSPeter Grehan }
94110b59a9bSPeter Grehan 
94210b59a9bSPeter Grehan static void
94310b59a9bSPeter Grehan vtpci_free_interrupts(struct vtpci_softc *sc)
94410b59a9bSPeter Grehan {
94510b59a9bSPeter Grehan 	device_t dev;
94610b59a9bSPeter Grehan 	struct vtpci_intr_resource *ires;
94710b59a9bSPeter Grehan 	int i;
94810b59a9bSPeter Grehan 
94910b59a9bSPeter Grehan 	dev = sc->vtpci_dev;
95010b59a9bSPeter Grehan 	sc->vtpci_nintr_res = 0;
95110b59a9bSPeter Grehan 
95210b59a9bSPeter Grehan 	if (sc->vtpci_flags & (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) {
95310b59a9bSPeter Grehan 		pci_release_msi(dev);
95410b59a9bSPeter Grehan 		sc->vtpci_flags &= ~(VIRTIO_PCI_FLAG_MSI |
95510b59a9bSPeter Grehan 		    VIRTIO_PCI_FLAG_MSIX | VIRTIO_PCI_FLAG_SHARED_MSIX);
95610b59a9bSPeter Grehan 	}
95710b59a9bSPeter Grehan 
95810b59a9bSPeter Grehan 	for (i = 0; i < 1 + VIRTIO_MAX_VIRTQUEUES; i++) {
95910b59a9bSPeter Grehan 		ires = &sc->vtpci_intr_res[i];
96010b59a9bSPeter Grehan 
96110b59a9bSPeter Grehan 		if (ires->intrhand != NULL) {
96210b59a9bSPeter Grehan 			bus_teardown_intr(dev, ires->irq, ires->intrhand);
96310b59a9bSPeter Grehan 			ires->intrhand = NULL;
96410b59a9bSPeter Grehan 		}
96510b59a9bSPeter Grehan 
96610b59a9bSPeter Grehan 		if (ires->irq != NULL) {
96710b59a9bSPeter Grehan 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
96810b59a9bSPeter Grehan 			    ires->irq);
96910b59a9bSPeter Grehan 			ires->irq = NULL;
97010b59a9bSPeter Grehan 		}
97110b59a9bSPeter Grehan 
97210b59a9bSPeter Grehan 		ires->rid = -1;
97310b59a9bSPeter Grehan 	}
97410b59a9bSPeter Grehan }
97510b59a9bSPeter Grehan 
97610b59a9bSPeter Grehan static void
97710b59a9bSPeter Grehan vtpci_free_virtqueues(struct vtpci_softc *sc)
97810b59a9bSPeter Grehan {
97910b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
98010b59a9bSPeter Grehan 	int i;
98110b59a9bSPeter Grehan 
98210b59a9bSPeter Grehan 	sc->vtpci_nvqs = 0;
98310b59a9bSPeter Grehan 
98410b59a9bSPeter Grehan 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
98510b59a9bSPeter Grehan 		vqx = &sc->vtpci_vqx[i];
98610b59a9bSPeter Grehan 
98710b59a9bSPeter Grehan 		if (vqx->vq != NULL) {
98810b59a9bSPeter Grehan 			virtqueue_free(vqx->vq);
98910b59a9bSPeter Grehan 			vqx->vq = NULL;
99010b59a9bSPeter Grehan 		}
99110b59a9bSPeter Grehan 	}
99210b59a9bSPeter Grehan }
99310b59a9bSPeter Grehan 
99410b59a9bSPeter Grehan static void
99510b59a9bSPeter Grehan vtpci_release_child_resources(struct vtpci_softc *sc)
99610b59a9bSPeter Grehan {
99710b59a9bSPeter Grehan 
99810b59a9bSPeter Grehan 	vtpci_free_interrupts(sc);
99910b59a9bSPeter Grehan 	vtpci_free_virtqueues(sc);
100010b59a9bSPeter Grehan }
100110b59a9bSPeter Grehan 
100210b59a9bSPeter Grehan static void
100310b59a9bSPeter Grehan vtpci_reset(struct vtpci_softc *sc)
100410b59a9bSPeter Grehan {
100510b59a9bSPeter Grehan 
100610b59a9bSPeter Grehan 	/*
100710b59a9bSPeter Grehan 	 * Setting the status to RESET sets the host device to
100810b59a9bSPeter Grehan 	 * the original, uninitialized state.
100910b59a9bSPeter Grehan 	 */
101010b59a9bSPeter Grehan 	vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
101110b59a9bSPeter Grehan }
101210b59a9bSPeter Grehan 
101310b59a9bSPeter Grehan static int
101410b59a9bSPeter Grehan vtpci_legacy_intr(void *xsc)
101510b59a9bSPeter Grehan {
101610b59a9bSPeter Grehan 	struct vtpci_softc *sc;
101710b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
101810b59a9bSPeter Grehan 	int i;
101910b59a9bSPeter Grehan 	uint8_t isr;
102010b59a9bSPeter Grehan 
102110b59a9bSPeter Grehan 	sc = xsc;
102210b59a9bSPeter Grehan 	vqx = &sc->vtpci_vqx[0];
102310b59a9bSPeter Grehan 
102410b59a9bSPeter Grehan 	/* Reading the ISR also clears it. */
102510b59a9bSPeter Grehan 	isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
102610b59a9bSPeter Grehan 
102710b59a9bSPeter Grehan 	if (isr & VIRTIO_PCI_ISR_CONFIG)
102810b59a9bSPeter Grehan 		vtpci_config_intr(sc);
102910b59a9bSPeter Grehan 
103010b59a9bSPeter Grehan 	if (isr & VIRTIO_PCI_ISR_INTR)
103110b59a9bSPeter Grehan 		for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
103210b59a9bSPeter Grehan 			virtqueue_intr(vqx->vq);
103310b59a9bSPeter Grehan 
103410b59a9bSPeter Grehan 	return (isr ? FILTER_HANDLED : FILTER_STRAY);
103510b59a9bSPeter Grehan }
103610b59a9bSPeter Grehan 
103710b59a9bSPeter Grehan static int
103810b59a9bSPeter Grehan vtpci_vq_shared_intr(void *xsc)
103910b59a9bSPeter Grehan {
104010b59a9bSPeter Grehan 	struct vtpci_softc *sc;
104110b59a9bSPeter Grehan 	struct vtpci_virtqueue *vqx;
104210b59a9bSPeter Grehan 	int i, rc;
104310b59a9bSPeter Grehan 
104410b59a9bSPeter Grehan 	rc = 0;
104510b59a9bSPeter Grehan 	sc = xsc;
104610b59a9bSPeter Grehan 	vqx = &sc->vtpci_vqx[0];
104710b59a9bSPeter Grehan 
104810b59a9bSPeter Grehan 	for (i = 0; i < sc->vtpci_nvqs; i++, vqx++)
104910b59a9bSPeter Grehan 		rc |= virtqueue_intr(vqx->vq);
105010b59a9bSPeter Grehan 
105110b59a9bSPeter Grehan 	return (rc ? FILTER_HANDLED : FILTER_STRAY);
105210b59a9bSPeter Grehan }
105310b59a9bSPeter Grehan 
105410b59a9bSPeter Grehan static int
105510b59a9bSPeter Grehan vtpci_vq_intr(void *xvq)
105610b59a9bSPeter Grehan {
105710b59a9bSPeter Grehan 	struct virtqueue *vq;
105810b59a9bSPeter Grehan 	int rc;
105910b59a9bSPeter Grehan 
106010b59a9bSPeter Grehan 	vq = xvq;
106110b59a9bSPeter Grehan 	rc = virtqueue_intr(vq);
106210b59a9bSPeter Grehan 
106310b59a9bSPeter Grehan 	return (rc ? FILTER_HANDLED : FILTER_STRAY);
106410b59a9bSPeter Grehan }
106510b59a9bSPeter Grehan 
106610b59a9bSPeter Grehan static int
106710b59a9bSPeter Grehan vtpci_config_intr(void *xsc)
106810b59a9bSPeter Grehan {
106910b59a9bSPeter Grehan 	struct vtpci_softc *sc;
107010b59a9bSPeter Grehan 	device_t child;
107110b59a9bSPeter Grehan 	int rc;
107210b59a9bSPeter Grehan 
107310b59a9bSPeter Grehan 	rc = 0;
107410b59a9bSPeter Grehan 	sc = xsc;
107510b59a9bSPeter Grehan 	child = sc->vtpci_child_dev;
107610b59a9bSPeter Grehan 
107710b59a9bSPeter Grehan 	if (child != NULL)
107810b59a9bSPeter Grehan 		rc = VIRTIO_CONFIG_CHANGE(child);
107910b59a9bSPeter Grehan 
108010b59a9bSPeter Grehan 	return (rc ? FILTER_HANDLED : FILTER_STRAY);
108110b59a9bSPeter Grehan }
1082