xref: /dflybsd-src/sys/dev/virtual/virtio/pci/virtio_pci.c (revision b817dce2e30a257b86b253a698e63a418758d76d)
111447b59SVenkatesh Srinivas /*-
211447b59SVenkatesh Srinivas  * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
311447b59SVenkatesh Srinivas  * All rights reserved.
411447b59SVenkatesh Srinivas  *
511447b59SVenkatesh Srinivas  * Redistribution and use in source and binary forms, with or without
611447b59SVenkatesh Srinivas  * modification, are permitted provided that the following conditions
711447b59SVenkatesh Srinivas  * are met:
811447b59SVenkatesh Srinivas  * 1. Redistributions of source code must retain the above copyright
911447b59SVenkatesh Srinivas  *    notice unmodified, this list of conditions, and the following
1011447b59SVenkatesh Srinivas  *    disclaimer.
1111447b59SVenkatesh Srinivas  * 2. Redistributions in binary form must reproduce the above copyright
1211447b59SVenkatesh Srinivas  *    notice, this list of conditions and the following disclaimer in the
1311447b59SVenkatesh Srinivas  *    documentation and/or other materials provided with the distribution.
1411447b59SVenkatesh Srinivas  *
1511447b59SVenkatesh Srinivas  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1611447b59SVenkatesh Srinivas  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1711447b59SVenkatesh Srinivas  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1811447b59SVenkatesh Srinivas  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1911447b59SVenkatesh Srinivas  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2011447b59SVenkatesh Srinivas  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2111447b59SVenkatesh Srinivas  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2211447b59SVenkatesh Srinivas  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2311447b59SVenkatesh Srinivas  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2411447b59SVenkatesh Srinivas  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2511447b59SVenkatesh Srinivas  *
2611447b59SVenkatesh Srinivas  * $FreeBSD: src/sys/dev/virtio/pci/virtio_pci.c,v 1.3 2012/04/14 05:48:04 grehan Exp $
2711447b59SVenkatesh Srinivas  */
2811447b59SVenkatesh Srinivas 
2911447b59SVenkatesh Srinivas /* Driver for the VirtIO PCI interface. */
3011447b59SVenkatesh Srinivas 
3111447b59SVenkatesh Srinivas #include <sys/param.h>
3211447b59SVenkatesh Srinivas #include <sys/systm.h>
3311447b59SVenkatesh Srinivas #include <sys/bus.h>
3411447b59SVenkatesh Srinivas #include <sys/kernel.h>
3511447b59SVenkatesh Srinivas #include <sys/module.h>
3611447b59SVenkatesh Srinivas #include <sys/malloc.h>
372f1382caSVenkatesh Srinivas #include <sys/serialize.h>
3811447b59SVenkatesh Srinivas 
3911447b59SVenkatesh Srinivas #include <bus/pci/pcivar.h>
4011447b59SVenkatesh Srinivas #include <bus/pci/pcireg.h>
4111447b59SVenkatesh Srinivas 
4211447b59SVenkatesh Srinivas #include <sys/rman.h>
4311447b59SVenkatesh Srinivas 
44dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtio.h>
45dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtqueue.h>
4611447b59SVenkatesh Srinivas #include "virtio_pci.h"
47dcbcbc86SVenkatesh Srinivas #include "virtio_bus_if.h"
4811447b59SVenkatesh Srinivas 
492f2405bbSImre Vadász struct vqentry {
502f2405bbSImre Vadász 	int what;
519d96478cSImre Vadász 	struct virtqueue *vq;
529d96478cSImre Vadász 	driver_intr_t *handler;
539d96478cSImre Vadász 	void *arg;
542f2405bbSImre Vadász 	TAILQ_ENTRY(vqentry) entries;
552f2405bbSImre Vadász };
562f2405bbSImre Vadász 
572f2405bbSImre Vadász TAILQ_HEAD(vqirq_list, vqentry);
582f2405bbSImre Vadász 
5911447b59SVenkatesh Srinivas struct vtpci_softc {
6011447b59SVenkatesh Srinivas 	device_t			 vtpci_dev;
6111447b59SVenkatesh Srinivas 	struct resource			*vtpci_res;
6211447b59SVenkatesh Srinivas 	struct resource			*vtpci_msix_res;
6311447b59SVenkatesh Srinivas 	uint64_t			 vtpci_features;
6411447b59SVenkatesh Srinivas 	uint32_t			 vtpci_flags;
65c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSI		 0x0001
66c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSIX		 0x0010
6711447b59SVenkatesh Srinivas 
6811447b59SVenkatesh Srinivas 	device_t			 vtpci_child_dev;
6911447b59SVenkatesh Srinivas 	struct virtio_feature_desc	*vtpci_child_feat_desc;
7011447b59SVenkatesh Srinivas 
7111447b59SVenkatesh Srinivas 	/*
7211447b59SVenkatesh Srinivas 	 * Ideally, each virtqueue that the driver provides a callback for
7311447b59SVenkatesh Srinivas 	 * will receive its own MSIX vector. If there are not sufficient
7411447b59SVenkatesh Srinivas 	 * vectors available, we will then attempt to have all the VQs
7511447b59SVenkatesh Srinivas 	 * share one vector. Note that when using MSIX, the configuration
7611447b59SVenkatesh Srinivas 	 * changed notifications must be on their own vector.
7711447b59SVenkatesh Srinivas 	 *
7811447b59SVenkatesh Srinivas 	 * If MSIX is not available, we will attempt to have the whole
7911447b59SVenkatesh Srinivas 	 * device share one MSI vector, and then, finally, one legacy
8011447b59SVenkatesh Srinivas 	 * interrupt.
8111447b59SVenkatesh Srinivas 	 */
8211447b59SVenkatesh Srinivas 	int				 vtpci_nvqs;
8311447b59SVenkatesh Srinivas 	struct vtpci_virtqueue {
8411447b59SVenkatesh Srinivas 		struct virtqueue *vq;
8511447b59SVenkatesh Srinivas 
862f2405bbSImre Vadász 		/* Index into vtpci_intr_res[] below. -1 if no IRQ assigned. */
8711447b59SVenkatesh Srinivas 		int		  ires_idx;
8811447b59SVenkatesh Srinivas 	} vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
8911447b59SVenkatesh Srinivas 
9011447b59SVenkatesh Srinivas 	/*
9111447b59SVenkatesh Srinivas 	 * When using MSIX interrupts, the first element of vtpci_intr_res[]
9211447b59SVenkatesh Srinivas 	 * is always the configuration changed notifications. The remaining
9311447b59SVenkatesh Srinivas 	 * element(s) are used for the virtqueues.
9411447b59SVenkatesh Srinivas 	 *
9511447b59SVenkatesh Srinivas 	 * With MSI and legacy interrupts, only the first element of
9611447b59SVenkatesh Srinivas 	 * vtpci_intr_res[] is used.
9711447b59SVenkatesh Srinivas 	 */
9811447b59SVenkatesh Srinivas 	int				 vtpci_nintr_res;
99c8247d06SImre Vadász 	int				 vtpci_irq_flags;
10011447b59SVenkatesh Srinivas 	struct vtpci_intr_resource {
1012f2405bbSImre Vadász 		struct vtpci_softc *ires_sc;
10211447b59SVenkatesh Srinivas 		struct resource	*irq;
10311447b59SVenkatesh Srinivas 		int		 rid;
10411447b59SVenkatesh Srinivas 		void		*intrhand;
1052f2405bbSImre Vadász 		struct vqirq_list ls;
10611447b59SVenkatesh Srinivas 	} vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
1072f2405bbSImre Vadász 
1082f2405bbSImre Vadász 	int				 vtpci_config_irq;
10911447b59SVenkatesh Srinivas };
11011447b59SVenkatesh Srinivas 
11111447b59SVenkatesh Srinivas static int	vtpci_probe(device_t);
11211447b59SVenkatesh Srinivas static int	vtpci_attach(device_t);
11311447b59SVenkatesh Srinivas static int	vtpci_detach(device_t);
11411447b59SVenkatesh Srinivas static int	vtpci_suspend(device_t);
11511447b59SVenkatesh Srinivas static int	vtpci_resume(device_t);
11611447b59SVenkatesh Srinivas static int	vtpci_shutdown(device_t);
11711447b59SVenkatesh Srinivas static void	vtpci_driver_added(device_t, driver_t *);
11811447b59SVenkatesh Srinivas static void	vtpci_child_detached(device_t, device_t);
11911447b59SVenkatesh Srinivas static int	vtpci_read_ivar(device_t, device_t, int, uintptr_t *);
12011447b59SVenkatesh Srinivas static int	vtpci_write_ivar(device_t, device_t, int, uintptr_t);
12111447b59SVenkatesh Srinivas 
12211447b59SVenkatesh Srinivas static uint64_t	vtpci_negotiate_features(device_t, uint64_t);
12311447b59SVenkatesh Srinivas static int	vtpci_with_feature(device_t, uint64_t);
1242f2405bbSImre Vadász static int	vtpci_intr_count(device_t dev);
1252f2405bbSImre Vadász static int	vtpci_intr_alloc(device_t dev, int *cnt, int use_config,
1262f2405bbSImre Vadász 		    int *cpus);
1272f2405bbSImre Vadász static int	vtpci_intr_release(device_t dev);
12811447b59SVenkatesh Srinivas static int	vtpci_alloc_virtqueues(device_t, int, int,
12911447b59SVenkatesh Srinivas 		    struct vq_alloc_info *);
1302f2405bbSImre Vadász static int	vtpci_setup_intr(device_t, uint irq, lwkt_serialize_t);
1312f2405bbSImre Vadász static int	vtpci_teardown_intr(device_t, uint irq);
1329d96478cSImre Vadász static int	vtpci_bind_intr(device_t, uint, int, driver_intr_t, void *);
1332f2405bbSImre Vadász static int	vtpci_unbind_intr(device_t, int);
13411447b59SVenkatesh Srinivas static void	vtpci_stop(device_t);
13511447b59SVenkatesh Srinivas static int	vtpci_reinit(device_t, uint64_t);
13611447b59SVenkatesh Srinivas static void	vtpci_reinit_complete(device_t);
13711447b59SVenkatesh Srinivas static void	vtpci_notify_virtqueue(device_t, uint16_t);
13811447b59SVenkatesh Srinivas static uint8_t	vtpci_get_status(device_t);
13911447b59SVenkatesh Srinivas static void	vtpci_set_status(device_t, uint8_t);
14011447b59SVenkatesh Srinivas static void	vtpci_read_dev_config(device_t, bus_size_t, void *, int);
14111447b59SVenkatesh Srinivas static void	vtpci_write_dev_config(device_t, bus_size_t, void *, int);
14211447b59SVenkatesh Srinivas 
14311447b59SVenkatesh Srinivas static void	vtpci_describe_features(struct vtpci_softc *, const char *,
14411447b59SVenkatesh Srinivas 		    uint64_t);
14511447b59SVenkatesh Srinivas static void	vtpci_probe_and_attach_child(struct vtpci_softc *);
14611447b59SVenkatesh Srinivas 
14711447b59SVenkatesh Srinivas static int	vtpci_register_msix_vector(struct vtpci_softc *, int, int);
14811447b59SVenkatesh Srinivas 
14911447b59SVenkatesh Srinivas static void	vtpci_free_interrupts(struct vtpci_softc *);
15011447b59SVenkatesh Srinivas static void	vtpci_free_virtqueues(struct vtpci_softc *);
15111447b59SVenkatesh Srinivas static void	vtpci_release_child_resources(struct vtpci_softc *);
15211447b59SVenkatesh Srinivas static void	vtpci_reset(struct vtpci_softc *);
15311447b59SVenkatesh Srinivas 
154df2df2fcSImre Vadász static void	vtpci_legacy_intr(void *);
1552f2405bbSImre Vadász static void	vtpci_msix_intr(void *);
15611447b59SVenkatesh Srinivas 
15711447b59SVenkatesh Srinivas /*
15811447b59SVenkatesh Srinivas  * I/O port read/write wrappers.
15911447b59SVenkatesh Srinivas  */
16011447b59SVenkatesh Srinivas #define vtpci_read_config_1(sc, o)	bus_read_1((sc)->vtpci_res, (o))
16111447b59SVenkatesh Srinivas #define vtpci_read_config_2(sc, o)	bus_read_2((sc)->vtpci_res, (o))
16211447b59SVenkatesh Srinivas #define vtpci_read_config_4(sc, o)	bus_read_4((sc)->vtpci_res, (o))
16311447b59SVenkatesh Srinivas #define vtpci_write_config_1(sc, o, v)	bus_write_1((sc)->vtpci_res, (o), (v))
16411447b59SVenkatesh Srinivas #define vtpci_write_config_2(sc, o, v)	bus_write_2((sc)->vtpci_res, (o), (v))
16511447b59SVenkatesh Srinivas #define vtpci_write_config_4(sc, o, v)	bus_write_4((sc)->vtpci_res, (o), (v))
16611447b59SVenkatesh Srinivas 
16711447b59SVenkatesh Srinivas /* Tunables. */
16811447b59SVenkatesh Srinivas static int vtpci_disable_msix = 0;
16911447b59SVenkatesh Srinivas TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix);
17011447b59SVenkatesh Srinivas 
17111447b59SVenkatesh Srinivas static device_method_t vtpci_methods[] = {
17211447b59SVenkatesh Srinivas 	/* Device interface. */
17311447b59SVenkatesh Srinivas 	DEVMETHOD(device_probe,			  vtpci_probe),
17411447b59SVenkatesh Srinivas 	DEVMETHOD(device_attach,		  vtpci_attach),
17511447b59SVenkatesh Srinivas 	DEVMETHOD(device_detach,		  vtpci_detach),
17611447b59SVenkatesh Srinivas 	DEVMETHOD(device_suspend,		  vtpci_suspend),
17711447b59SVenkatesh Srinivas 	DEVMETHOD(device_resume,		  vtpci_resume),
17811447b59SVenkatesh Srinivas 	DEVMETHOD(device_shutdown,		  vtpci_shutdown),
17911447b59SVenkatesh Srinivas 
18011447b59SVenkatesh Srinivas 	/* Bus interface. */
18111447b59SVenkatesh Srinivas 	DEVMETHOD(bus_driver_added,		  vtpci_driver_added),
18211447b59SVenkatesh Srinivas 	DEVMETHOD(bus_child_detached,		  vtpci_child_detached),
18311447b59SVenkatesh Srinivas 	DEVMETHOD(bus_read_ivar,		  vtpci_read_ivar),
18411447b59SVenkatesh Srinivas 	DEVMETHOD(bus_write_ivar,		  vtpci_write_ivar),
18511447b59SVenkatesh Srinivas 
18611447b59SVenkatesh Srinivas 	/* VirtIO bus interface. */
18711447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_negotiate_features,  vtpci_negotiate_features),
18811447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_with_feature,	  vtpci_with_feature),
1892f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_count,	  vtpci_intr_count),
1902f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_alloc,	  vtpci_intr_alloc),
1912f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_release,	  vtpci_intr_release),
19211447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_alloc_virtqueues,	  vtpci_alloc_virtqueues),
19311447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_setup_intr,	  vtpci_setup_intr),
1942f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_teardown_intr,	  vtpci_teardown_intr),
1952f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_bind_intr,		  vtpci_bind_intr),
1962f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_unbind_intr,	  vtpci_unbind_intr),
19711447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_stop,		  vtpci_stop),
19811447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_reinit,		  vtpci_reinit),
19911447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_reinit_complete,	  vtpci_reinit_complete),
20011447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_notify_vq,		  vtpci_notify_virtqueue),
20111447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_read_device_config,  vtpci_read_dev_config),
20211447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
20311447b59SVenkatesh Srinivas 
204d3c9c58eSSascha Wildner 	DEVMETHOD_END
20511447b59SVenkatesh Srinivas };
20611447b59SVenkatesh Srinivas 
20711447b59SVenkatesh Srinivas static driver_t vtpci_driver = {
20811447b59SVenkatesh Srinivas 	"virtio_pci",
20911447b59SVenkatesh Srinivas 	vtpci_methods,
21011447b59SVenkatesh Srinivas 	sizeof(struct vtpci_softc)
21111447b59SVenkatesh Srinivas };
21211447b59SVenkatesh Srinivas 
21311447b59SVenkatesh Srinivas devclass_t vtpci_devclass;
21411447b59SVenkatesh Srinivas 
215dfc199f7SSascha Wildner DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, NULL, NULL);
21611447b59SVenkatesh Srinivas MODULE_VERSION(virtio_pci, 1);
21711447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, pci, 1, 1, 1);
21811447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1);
21911447b59SVenkatesh Srinivas 
22011447b59SVenkatesh Srinivas static int
22111447b59SVenkatesh Srinivas vtpci_probe(device_t dev)
22211447b59SVenkatesh Srinivas {
22311447b59SVenkatesh Srinivas 	char desc[36];
22411447b59SVenkatesh Srinivas 	const char *name;
22511447b59SVenkatesh Srinivas 
22611447b59SVenkatesh Srinivas 	if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
22711447b59SVenkatesh Srinivas 		return (ENXIO);
22811447b59SVenkatesh Srinivas 
22911447b59SVenkatesh Srinivas 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
23011447b59SVenkatesh Srinivas 	    pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX)
23111447b59SVenkatesh Srinivas 		return (ENXIO);
23211447b59SVenkatesh Srinivas 
23311447b59SVenkatesh Srinivas 	if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION)
23411447b59SVenkatesh Srinivas 		return (ENXIO);
23511447b59SVenkatesh Srinivas 
23611447b59SVenkatesh Srinivas 	name = virtio_device_name(pci_get_subdevice(dev));
23711447b59SVenkatesh Srinivas 	if (name == NULL)
23811447b59SVenkatesh Srinivas 		name = "Unknown";
23911447b59SVenkatesh Srinivas 
24011447b59SVenkatesh Srinivas 	ksnprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name);
24111447b59SVenkatesh Srinivas 	device_set_desc_copy(dev, desc);
24211447b59SVenkatesh Srinivas 
24311447b59SVenkatesh Srinivas 	return (BUS_PROBE_DEFAULT);
24411447b59SVenkatesh Srinivas }
24511447b59SVenkatesh Srinivas 
24611447b59SVenkatesh Srinivas static int
24711447b59SVenkatesh Srinivas vtpci_attach(device_t dev)
24811447b59SVenkatesh Srinivas {
24911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
25011447b59SVenkatesh Srinivas 	device_t child;
251c8247d06SImre Vadász 	int msix_cap, rid;
25211447b59SVenkatesh Srinivas 
25311447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
25411447b59SVenkatesh Srinivas 	sc->vtpci_dev = dev;
2552f2405bbSImre Vadász 	sc->vtpci_config_irq = -1;
25611447b59SVenkatesh Srinivas 
25711447b59SVenkatesh Srinivas 	pci_enable_busmaster(dev);
25811447b59SVenkatesh Srinivas 
25911447b59SVenkatesh Srinivas 	rid = PCIR_BAR(0);
26011447b59SVenkatesh Srinivas 	sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
26111447b59SVenkatesh Srinivas 	    RF_ACTIVE);
26211447b59SVenkatesh Srinivas 	if (sc->vtpci_res == NULL) {
26311447b59SVenkatesh Srinivas 		device_printf(dev, "cannot map I/O space\n");
26411447b59SVenkatesh Srinivas 		return (ENXIO);
26511447b59SVenkatesh Srinivas 	}
26611447b59SVenkatesh Srinivas 
267c8247d06SImre Vadász 	if (pci_find_extcap(dev, PCIY_MSIX, &msix_cap) == 0) {
268c8247d06SImre Vadász 		uint32_t val;
269c8247d06SImre Vadász 		val = pci_read_config(dev, msix_cap + PCIR_MSIX_TABLE, 4);
270c8247d06SImre Vadász 		rid = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
27111447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = bus_alloc_resource_any(dev,
27211447b59SVenkatesh Srinivas 		    SYS_RES_MEMORY, &rid, RF_ACTIVE);
27311447b59SVenkatesh Srinivas 	}
27411447b59SVenkatesh Srinivas 
27511447b59SVenkatesh Srinivas 	vtpci_reset(sc);
27611447b59SVenkatesh Srinivas 
27711447b59SVenkatesh Srinivas 	/* Tell the host we've noticed this device. */
27811447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
27911447b59SVenkatesh Srinivas 
28011447b59SVenkatesh Srinivas 	if ((child = device_add_child(dev, NULL, -1)) == NULL) {
28111447b59SVenkatesh Srinivas 		device_printf(dev, "cannot create child device\n");
28211447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
28311447b59SVenkatesh Srinivas 		vtpci_detach(dev);
28411447b59SVenkatesh Srinivas 		return (ENOMEM);
28511447b59SVenkatesh Srinivas 	}
28611447b59SVenkatesh Srinivas 
28711447b59SVenkatesh Srinivas 	sc->vtpci_child_dev = child;
28811447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
28911447b59SVenkatesh Srinivas 
29011447b59SVenkatesh Srinivas 	return (0);
29111447b59SVenkatesh Srinivas }
29211447b59SVenkatesh Srinivas 
29311447b59SVenkatesh Srinivas static int
29411447b59SVenkatesh Srinivas vtpci_detach(device_t dev)
29511447b59SVenkatesh Srinivas {
29611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
29711447b59SVenkatesh Srinivas 	device_t child;
29811447b59SVenkatesh Srinivas 	int error;
29911447b59SVenkatesh Srinivas 
30011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
30111447b59SVenkatesh Srinivas 
30211447b59SVenkatesh Srinivas 	if ((child = sc->vtpci_child_dev) != NULL) {
30311447b59SVenkatesh Srinivas 		error = device_delete_child(dev, child);
30411447b59SVenkatesh Srinivas 		if (error)
30511447b59SVenkatesh Srinivas 			return (error);
30611447b59SVenkatesh Srinivas 		sc->vtpci_child_dev = NULL;
30711447b59SVenkatesh Srinivas 	}
30811447b59SVenkatesh Srinivas 
30911447b59SVenkatesh Srinivas 	vtpci_reset(sc);
31011447b59SVenkatesh Srinivas 
31111447b59SVenkatesh Srinivas 	if (sc->vtpci_msix_res != NULL) {
312c8247d06SImre Vadász 		bus_release_resource(dev, SYS_RES_MEMORY,
313c8247d06SImre Vadász 		    rman_get_rid(sc->vtpci_msix_res), sc->vtpci_msix_res);
31411447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = NULL;
31511447b59SVenkatesh Srinivas 	}
31611447b59SVenkatesh Srinivas 
31711447b59SVenkatesh Srinivas 	if (sc->vtpci_res != NULL) {
31811447b59SVenkatesh Srinivas 		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
31911447b59SVenkatesh Srinivas 		    sc->vtpci_res);
32011447b59SVenkatesh Srinivas 		sc->vtpci_res = NULL;
32111447b59SVenkatesh Srinivas 	}
32211447b59SVenkatesh Srinivas 
32311447b59SVenkatesh Srinivas 	return (0);
32411447b59SVenkatesh Srinivas }
32511447b59SVenkatesh Srinivas 
32611447b59SVenkatesh Srinivas static int
32711447b59SVenkatesh Srinivas vtpci_suspend(device_t dev)
32811447b59SVenkatesh Srinivas {
32911447b59SVenkatesh Srinivas 
33011447b59SVenkatesh Srinivas 	return (bus_generic_suspend(dev));
33111447b59SVenkatesh Srinivas }
33211447b59SVenkatesh Srinivas 
33311447b59SVenkatesh Srinivas static int
33411447b59SVenkatesh Srinivas vtpci_resume(device_t dev)
33511447b59SVenkatesh Srinivas {
33611447b59SVenkatesh Srinivas 
33711447b59SVenkatesh Srinivas 	return (bus_generic_resume(dev));
33811447b59SVenkatesh Srinivas }
33911447b59SVenkatesh Srinivas 
34011447b59SVenkatesh Srinivas static int
34111447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev)
34211447b59SVenkatesh Srinivas {
34311447b59SVenkatesh Srinivas 
34411447b59SVenkatesh Srinivas 	(void) bus_generic_shutdown(dev);
34511447b59SVenkatesh Srinivas 	/* Forcibly stop the host device. */
34611447b59SVenkatesh Srinivas 	vtpci_stop(dev);
34711447b59SVenkatesh Srinivas 
34811447b59SVenkatesh Srinivas 	return (0);
34911447b59SVenkatesh Srinivas }
35011447b59SVenkatesh Srinivas 
35111447b59SVenkatesh Srinivas static void
35211447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver)
35311447b59SVenkatesh Srinivas {
35411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
35511447b59SVenkatesh Srinivas 
35611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
35711447b59SVenkatesh Srinivas 
35811447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
35911447b59SVenkatesh Srinivas }
36011447b59SVenkatesh Srinivas 
36111447b59SVenkatesh Srinivas static void
36211447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child)
36311447b59SVenkatesh Srinivas {
36411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
36511447b59SVenkatesh Srinivas 
36611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
36711447b59SVenkatesh Srinivas 
36811447b59SVenkatesh Srinivas 	vtpci_reset(sc);
36911447b59SVenkatesh Srinivas 	vtpci_release_child_resources(sc);
37011447b59SVenkatesh Srinivas }
37111447b59SVenkatesh Srinivas 
37211447b59SVenkatesh Srinivas static int
37311447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
37411447b59SVenkatesh Srinivas {
37511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
37611447b59SVenkatesh Srinivas 
37711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
37811447b59SVenkatesh Srinivas 
37911447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
38011447b59SVenkatesh Srinivas 		return (ENOENT);
38111447b59SVenkatesh Srinivas 
38211447b59SVenkatesh Srinivas 	switch (index) {
38311447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_DEVTYPE:
38411447b59SVenkatesh Srinivas 		*result = pci_get_subdevice(dev);
38511447b59SVenkatesh Srinivas 		break;
38611447b59SVenkatesh Srinivas 	default:
38711447b59SVenkatesh Srinivas 		return (ENOENT);
38811447b59SVenkatesh Srinivas 	}
38911447b59SVenkatesh Srinivas 
39011447b59SVenkatesh Srinivas 	return (0);
39111447b59SVenkatesh Srinivas }
39211447b59SVenkatesh Srinivas 
39311447b59SVenkatesh Srinivas static int
39411447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
39511447b59SVenkatesh Srinivas {
39611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
39711447b59SVenkatesh Srinivas 
39811447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
39911447b59SVenkatesh Srinivas 
40011447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
40111447b59SVenkatesh Srinivas 		return (ENOENT);
40211447b59SVenkatesh Srinivas 
40311447b59SVenkatesh Srinivas 	switch (index) {
40411447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_FEATURE_DESC:
40511447b59SVenkatesh Srinivas 		sc->vtpci_child_feat_desc = (void *) value;
40611447b59SVenkatesh Srinivas 		break;
40711447b59SVenkatesh Srinivas 	default:
40811447b59SVenkatesh Srinivas 		return (ENOENT);
40911447b59SVenkatesh Srinivas 	}
41011447b59SVenkatesh Srinivas 
41111447b59SVenkatesh Srinivas 	return (0);
41211447b59SVenkatesh Srinivas }
41311447b59SVenkatesh Srinivas 
41411447b59SVenkatesh Srinivas static uint64_t
41511447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features)
41611447b59SVenkatesh Srinivas {
41711447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
41811447b59SVenkatesh Srinivas 	uint64_t host_features, features;
41911447b59SVenkatesh Srinivas 
42011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
42111447b59SVenkatesh Srinivas 
42211447b59SVenkatesh Srinivas 	host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
42311447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "host", host_features);
42411447b59SVenkatesh Srinivas 
42511447b59SVenkatesh Srinivas 	/*
42611447b59SVenkatesh Srinivas 	 * Limit negotiated features to what the driver, virtqueue, and
42711447b59SVenkatesh Srinivas 	 * host all support.
42811447b59SVenkatesh Srinivas 	 */
42911447b59SVenkatesh Srinivas 	features = host_features & child_features;
43011447b59SVenkatesh Srinivas 	features = virtqueue_filter_features(features);
43111447b59SVenkatesh Srinivas 	sc->vtpci_features = features;
43211447b59SVenkatesh Srinivas 
43311447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "negotiated", features);
43411447b59SVenkatesh Srinivas 	vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
43511447b59SVenkatesh Srinivas 
43611447b59SVenkatesh Srinivas 	return (features);
43711447b59SVenkatesh Srinivas }
43811447b59SVenkatesh Srinivas 
43911447b59SVenkatesh Srinivas static int
44011447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature)
44111447b59SVenkatesh Srinivas {
44211447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
44311447b59SVenkatesh Srinivas 
44411447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
44511447b59SVenkatesh Srinivas 
44611447b59SVenkatesh Srinivas 	return ((sc->vtpci_features & feature) != 0);
44711447b59SVenkatesh Srinivas }
44811447b59SVenkatesh Srinivas 
44911447b59SVenkatesh Srinivas static int
4502f2405bbSImre Vadász vtpci_intr_count(device_t dev)
4512f2405bbSImre Vadász {
4522f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
4532f2405bbSImre Vadász 
4542f2405bbSImre Vadász 	if (vtpci_disable_msix != 0 || sc->vtpci_msix_res == NULL)
4552f2405bbSImre Vadász 		return 1;
4562f2405bbSImre Vadász 	else
4572f2405bbSImre Vadász 		return pci_msix_count(dev);
4582f2405bbSImre Vadász }
4592f2405bbSImre Vadász 
4602f2405bbSImre Vadász /* Will never return 0, with *cnt <= 0. */
4612f2405bbSImre Vadász static int
4622f2405bbSImre Vadász vtpci_intr_alloc(device_t dev, int *cnt, int use_config, int *cpus)
4632f2405bbSImre Vadász {
4642f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
4652f2405bbSImre Vadász 	int i;
4662f2405bbSImre Vadász 
4672f2405bbSImre Vadász 	if (sc->vtpci_nintr_res > 0)
4682f2405bbSImre Vadász 		return (EINVAL);
4692f2405bbSImre Vadász 
4702f2405bbSImre Vadász 	if (*cnt <= 0)
4712f2405bbSImre Vadász 		return (EINVAL);
4722f2405bbSImre Vadász 
4732f2405bbSImre Vadász 	if (vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
4742f2405bbSImre Vadász 		int nmsix = pci_msix_count(dev);
4752f2405bbSImre Vadász 		if (nmsix < *cnt)
4762f2405bbSImre Vadász 			*cnt = nmsix;
4772f2405bbSImre Vadász 	}
4782f2405bbSImre Vadász 
4792f2405bbSImre Vadász 	if ((*cnt > 1 || use_config == 0) &&
4802f2405bbSImre Vadász 	    vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
4812f2405bbSImre Vadász 		if (pci_setup_msix(dev) != 0) {
4822f2405bbSImre Vadász 			device_printf(dev, "pci_setup_msix failed\n");
4832f2405bbSImre Vadász 			/* Just fallthrough to legacy IRQ code instead. */
4842f2405bbSImre Vadász 		} else {
4852f2405bbSImre Vadász 			for (i = 0; i < *cnt; i++) {
4862f2405bbSImre Vadász 				int cpu, rid;
4872f2405bbSImre Vadász 
4882f2405bbSImre Vadász 				if (cpus != NULL && cpus[i] >= 0 &&
4892f2405bbSImre Vadász 				    cpus[i] < ncpus) {
4902f2405bbSImre Vadász 					cpu = cpus[i];
4912f2405bbSImre Vadász 				} else {
4922f2405bbSImre Vadász 					cpu = device_get_unit(dev) + i;
4932f2405bbSImre Vadász 					cpu %= ncpus;
4942f2405bbSImre Vadász 				}
4952f2405bbSImre Vadász 				if (pci_alloc_msix_vector(dev, i, &rid, cpu)
4962f2405bbSImre Vadász 				    != 0) {
4972f2405bbSImre Vadász 					if (i > 1 || (i == 1 && !use_config)) {
4982f2405bbSImre Vadász 						*cnt = i;
4992f2405bbSImre Vadász 						/* Got some MSI-X vectors. */
5002f2405bbSImre Vadász 						sc->vtpci_irq_flags = RF_ACTIVE;
5012f2405bbSImre Vadász 						sc->vtpci_flags |=
5022f2405bbSImre Vadász 						    VIRTIO_PCI_FLAG_MSIX;
5032f2405bbSImre Vadász 						goto finish;
5042f2405bbSImre Vadász 					}
5052f2405bbSImre Vadász 					/*
5062f2405bbSImre Vadász 					 * Allocate the legacy IRQ instead.
5072f2405bbSImre Vadász 					 */
5082f2405bbSImre Vadász 					if (i == 1) {
5092f2405bbSImre Vadász 						pci_release_msix_vector(dev, 0);
5102f2405bbSImre Vadász 					}
5112f2405bbSImre Vadász 					pci_teardown_msix(dev);
5122f2405bbSImre Vadász 					break;
5132f2405bbSImre Vadász 				}
5142f2405bbSImre Vadász 				sc->vtpci_intr_res[i].rid = rid;
5152f2405bbSImre Vadász 			}
5162f2405bbSImre Vadász 			/* Got all the MSI-X vectors we wanted. */
5172f2405bbSImre Vadász 			sc->vtpci_irq_flags = RF_ACTIVE;
5182f2405bbSImre Vadász 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
5192f2405bbSImre Vadász 			/* Successfully allocated all MSI-X vectors */
5202f2405bbSImre Vadász 			goto finish;
5212f2405bbSImre Vadász 		}
5222f2405bbSImre Vadász 	}
5232f2405bbSImre Vadász 
5242f2405bbSImre Vadász 	/* Legacy IRQ code: */
5252f2405bbSImre Vadász 	*cnt = 1;
5262f2405bbSImre Vadász 	/*
5272f2405bbSImre Vadász 	 * Use MSI interrupts if available. Otherwise, we fallback
5282f2405bbSImre Vadász 	 * to legacy interrupts.
5292f2405bbSImre Vadász 	 */
5302f2405bbSImre Vadász 	sc->vtpci_intr_res[0].rid = 0;
5312f2405bbSImre Vadász 	if (pci_alloc_1intr(sc->vtpci_dev, 1,
5322f2405bbSImre Vadász 	    &sc->vtpci_intr_res[0].rid,
5332f2405bbSImre Vadász 	    &sc->vtpci_irq_flags) == PCI_INTR_TYPE_MSI) {
5342f2405bbSImre Vadász 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
5352f2405bbSImre Vadász 	}
5362f2405bbSImre Vadász 
5372f2405bbSImre Vadász finish:
5382f2405bbSImre Vadász 	KKASSERT(!((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) != 0 &&
5392f2405bbSImre Vadász 		   (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) != 0));
5402f2405bbSImre Vadász 
5412f2405bbSImre Vadász 	sc->vtpci_nintr_res = *cnt;
5422f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
5432f2405bbSImre Vadász 		struct resource *irq;
5442f2405bbSImre Vadász 
5452f2405bbSImre Vadász 		TAILQ_INIT(&sc->vtpci_intr_res[i].ls);
5462f2405bbSImre Vadász 		sc->vtpci_intr_res[i].ires_sc = sc;
5472f2405bbSImre Vadász 		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
5482f2405bbSImre Vadász 		    &sc->vtpci_intr_res[i].rid, sc->vtpci_irq_flags);
5492f2405bbSImre Vadász 		if (irq == NULL)
5502f2405bbSImre Vadász 			return (ENXIO);
5512f2405bbSImre Vadász 		if (cpus != NULL)
5522f2405bbSImre Vadász 			cpus[i] = rman_get_cpuid(irq);
5532f2405bbSImre Vadász 
5542f2405bbSImre Vadász 		sc->vtpci_intr_res[i].irq = irq;
5552f2405bbSImre Vadász 	}
5562f2405bbSImre Vadász 
5572f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
5582f2405bbSImre Vadász 		device_printf(dev, "using %d MSI-X vectors\n", *cnt);
5592f2405bbSImre Vadász 		pci_enable_msix(dev);
5602f2405bbSImre Vadász 	}
5612f2405bbSImre Vadász 
5622f2405bbSImre Vadász 	return (0);
5632f2405bbSImre Vadász }
5642f2405bbSImre Vadász 
5652f2405bbSImre Vadász static int
5662f2405bbSImre Vadász vtpci_intr_release(device_t dev)
5672f2405bbSImre Vadász {
5682f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
5692f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
5702f2405bbSImre Vadász 	int i;
5712f2405bbSImre Vadász 
5722f2405bbSImre Vadász 	if (sc->vtpci_nintr_res == 0)
5732f2405bbSImre Vadász 		return (EINVAL);
5742f2405bbSImre Vadász 
5752f2405bbSImre Vadász 	/* XXX Make sure none of the interrupts is used at the moment. */
5762f2405bbSImre Vadász 
5772f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
5782f2405bbSImre Vadász 		ires = &sc->vtpci_intr_res[i];
5792f2405bbSImre Vadász 
5802f2405bbSImre Vadász 		KKASSERT(TAILQ_EMPTY(&ires->ls));
5812f2405bbSImre Vadász 		if (ires->irq != NULL) {
5822f2405bbSImre Vadász 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
5832f2405bbSImre Vadász 			    ires->irq);
5842f2405bbSImre Vadász 			ires->irq = NULL;
5852f2405bbSImre Vadász 		}
5862f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
5872f2405bbSImre Vadász 			pci_release_msix_vector(dev, ires->rid);
5882f2405bbSImre Vadász 		ires->rid = 0;
5892f2405bbSImre Vadász 	}
5902f2405bbSImre Vadász 	sc->vtpci_nintr_res = 0;
5912f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
5922f2405bbSImre Vadász 		pci_release_msi(dev);
5932f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
5942f2405bbSImre Vadász 	}
5952f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
5962f2405bbSImre Vadász 		pci_teardown_msix(dev);
5972f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
5982f2405bbSImre Vadász 	}
5992f2405bbSImre Vadász 	return (0);
6002f2405bbSImre Vadász }
6012f2405bbSImre Vadász 
6022f2405bbSImre Vadász static int
60311447b59SVenkatesh Srinivas vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
60411447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
60511447b59SVenkatesh Srinivas {
60611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
60711447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
60811447b59SVenkatesh Srinivas 	struct vq_alloc_info *info;
60911447b59SVenkatesh Srinivas 	int queue, error;
61011447b59SVenkatesh Srinivas 	uint16_t vq_size;
61111447b59SVenkatesh Srinivas 
61211447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
61311447b59SVenkatesh Srinivas 
61411447b59SVenkatesh Srinivas 	if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
61511447b59SVenkatesh Srinivas 	    nvqs > VIRTIO_MAX_VIRTQUEUES)
61611447b59SVenkatesh Srinivas 		return (EINVAL);
61711447b59SVenkatesh Srinivas 
61811447b59SVenkatesh Srinivas 	for (queue = 0; queue < nvqs; queue++) {
61911447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
62011447b59SVenkatesh Srinivas 		info = &vq_info[queue];
62111447b59SVenkatesh Srinivas 
6222f2405bbSImre Vadász 		vqx->ires_idx = -1;
62311447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
62411447b59SVenkatesh Srinivas 
62511447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
62611447b59SVenkatesh Srinivas 		error = virtqueue_alloc(dev, queue, vq_size,
62711447b59SVenkatesh Srinivas 		    VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
62811447b59SVenkatesh Srinivas 		if (error)
62911447b59SVenkatesh Srinivas 			return (error);
63011447b59SVenkatesh Srinivas 
63111447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
63211447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
63311447b59SVenkatesh Srinivas 
63411447b59SVenkatesh Srinivas 		*info->vqai_vq = vqx->vq;
63511447b59SVenkatesh Srinivas 		sc->vtpci_nvqs++;
63611447b59SVenkatesh Srinivas 	}
63711447b59SVenkatesh Srinivas 
63811447b59SVenkatesh Srinivas 	return (0);
63911447b59SVenkatesh Srinivas }
64011447b59SVenkatesh Srinivas 
6412f2405bbSImre Vadász /* XXX Add argument to specify the callback function here. */
64211447b59SVenkatesh Srinivas static int
6432f2405bbSImre Vadász vtpci_setup_intr(device_t dev, uint irq, lwkt_serialize_t slz)
64411447b59SVenkatesh Srinivas {
64511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
64611447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
6472f2405bbSImre Vadász 	int flags, error;
64811447b59SVenkatesh Srinivas 
64911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
65011447b59SVenkatesh Srinivas 	flags = INTR_MPSAFE;
6512f2405bbSImre Vadász 
6522f2405bbSImre Vadász 	if ((int)irq >= sc->vtpci_nintr_res)
6532f2405bbSImre Vadász 		return (EINVAL);
6542f2405bbSImre Vadász 	ires = &sc->vtpci_intr_res[irq];
65511447b59SVenkatesh Srinivas 
65611447b59SVenkatesh Srinivas 	if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
65711447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
658df2df2fcSImre Vadász 				       vtpci_legacy_intr,
6592f2405bbSImre Vadász 				       ires, &ires->intrhand, slz);
6602f2405bbSImre Vadász 	} else {
6612f2405bbSImre Vadász 		error = bus_setup_intr(dev, ires->irq, flags,
6622f2405bbSImre Vadász 				       vtpci_msix_intr,
6632f2405bbSImre Vadász 				       ires, &ires->intrhand, slz);
6642f2405bbSImre Vadász 	}
66511447b59SVenkatesh Srinivas 	return (error);
66611447b59SVenkatesh Srinivas }
66711447b59SVenkatesh Srinivas 
6682f2405bbSImre Vadász static int
6692f2405bbSImre Vadász vtpci_teardown_intr(device_t dev, uint irq)
6702f2405bbSImre Vadász {
6712f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
6722f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
67311447b59SVenkatesh Srinivas 
6742f2405bbSImre Vadász 	if ((int)irq >= sc->vtpci_nintr_res)
6752f2405bbSImre Vadász 		return (EINVAL);
67611447b59SVenkatesh Srinivas 
6772f2405bbSImre Vadász 	ires = &sc->vtpci_intr_res[irq];
6782f2405bbSImre Vadász 
6792f2405bbSImre Vadász 	if (ires->intrhand == NULL)
6802f2405bbSImre Vadász 		return (ENXIO);
6812f2405bbSImre Vadász 
6822f2405bbSImre Vadász 	bus_teardown_intr(dev, ires->irq, ires->intrhand);
6832f2405bbSImre Vadász 	ires->intrhand = NULL;
6842f2405bbSImre Vadász 	return (0);
68511447b59SVenkatesh Srinivas }
68611447b59SVenkatesh Srinivas 
6872f2405bbSImre Vadász static void
6889d96478cSImre Vadász vtpci_add_irqentry(struct vtpci_intr_resource *intr_res, int what,
6899d96478cSImre Vadász     driver_intr_t handler, void *arg)
6902f2405bbSImre Vadász {
6912f2405bbSImre Vadász 	struct vqentry *e;
69211447b59SVenkatesh Srinivas 
6932f2405bbSImre Vadász 	TAILQ_FOREACH(e, &intr_res->ls, entries) {
6942f2405bbSImre Vadász 		if (e->what == what)
6952f2405bbSImre Vadász 			return;
6962f2405bbSImre Vadász 	}
6979d96478cSImre Vadász 	e = kmalloc(sizeof(*e), M_DEVBUF, M_WAITOK | M_ZERO);
6982f2405bbSImre Vadász 	e->what = what;
6999d96478cSImre Vadász 	if (e->what == -1) {
7009d96478cSImre Vadász 		e->vq = NULL;
7019d96478cSImre Vadász 	} else {
7029d96478cSImre Vadász 		e->vq = intr_res->ires_sc->vtpci_vqx[e->what].vq;
7039d96478cSImre Vadász 	}
7049d96478cSImre Vadász 	e->handler = handler;
7059d96478cSImre Vadász 	e->arg = arg;
7062f2405bbSImre Vadász 	TAILQ_INSERT_TAIL(&intr_res->ls, e, entries);
7072f2405bbSImre Vadász }
7082f2405bbSImre Vadász 
7092f2405bbSImre Vadász static void
7102f2405bbSImre Vadász vtpci_del_irqentry(struct vtpci_intr_resource *intr_res, int what)
7112f2405bbSImre Vadász {
7122f2405bbSImre Vadász 	struct vqentry *e;
7132f2405bbSImre Vadász 
7142f2405bbSImre Vadász 	TAILQ_FOREACH(e, &intr_res->ls, entries) {
7152f2405bbSImre Vadász 		if (e->what == what)
7162f2405bbSImre Vadász 			break;
7172f2405bbSImre Vadász 	}
7182f2405bbSImre Vadász 	if (e != NULL) {
7192f2405bbSImre Vadász 		TAILQ_REMOVE(&intr_res->ls, e, entries);
7202f2405bbSImre Vadász 		kfree(e, M_DEVBUF);
7212f2405bbSImre Vadász 	}
7222f2405bbSImre Vadász }
7232f2405bbSImre Vadász 
7242f2405bbSImre Vadász /*
7252f2405bbSImre Vadász  * Config intr can be bound after intr_alloc, virtqueue intrs can be bound
7262f2405bbSImre Vadász  * after intr_alloc and alloc_virtqueues.
7272f2405bbSImre Vadász  */
7282f2405bbSImre Vadász static int
7299d96478cSImre Vadász vtpci_bind_intr(device_t dev, uint irq, int what,
7309d96478cSImre Vadász     driver_intr_t handler, void *arg)
7312f2405bbSImre Vadász {
7322f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
7332f2405bbSImre Vadász 	struct vtpci_virtqueue *vqx;
7342f2405bbSImre Vadász 	int error;
7352f2405bbSImre Vadász 
7362f2405bbSImre Vadász 	if (irq >= sc->vtpci_nintr_res)
7372f2405bbSImre Vadász 		return (EINVAL);
7382f2405bbSImre Vadász 
7392f2405bbSImre Vadász 	if (what == -1) {
7402f2405bbSImre Vadász 		if (sc->vtpci_config_irq != -1)
7412f2405bbSImre Vadász 			return (EINVAL);
7422f2405bbSImre Vadász 
7432f2405bbSImre Vadász 		sc->vtpci_config_irq = irq;
7442f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7452f2405bbSImre Vadász 			error = vtpci_register_msix_vector(sc,
7462f2405bbSImre Vadász 			    VIRTIO_MSI_CONFIG_VECTOR, irq);
74711447b59SVenkatesh Srinivas 			if (error)
74811447b59SVenkatesh Srinivas 				return (error);
74911447b59SVenkatesh Srinivas 		}
7502f2405bbSImre Vadász 		goto done;
7512f2405bbSImre Vadász 	}
75211447b59SVenkatesh Srinivas 
7532f2405bbSImre Vadász 	if (sc->vtpci_nvqs <= what || what < 0)
7542f2405bbSImre Vadász 		return (EINVAL);
7552f2405bbSImre Vadász 
7562f2405bbSImre Vadász 	vqx = &sc->vtpci_vqx[what];
7572f2405bbSImre Vadász 	if (vqx->ires_idx != -1)
7582f2405bbSImre Vadász 		return (EINVAL);
7592f2405bbSImre Vadász 
7602f2405bbSImre Vadász 	vqx->ires_idx = irq;
7612f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7622f2405bbSImre Vadász 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
7632f2405bbSImre Vadász 		error = vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR,
7642f2405bbSImre Vadász 		    irq);
7652f2405bbSImre Vadász 		if (error)
7662f2405bbSImre Vadász 			return (error);
7672f2405bbSImre Vadász 	}
7682f2405bbSImre Vadász done:
7699d96478cSImre Vadász 	vtpci_add_irqentry(&sc->vtpci_intr_res[irq], what, handler, arg);
7702f2405bbSImre Vadász 	return (0);
7712f2405bbSImre Vadász }
7722f2405bbSImre Vadász 
7732f2405bbSImre Vadász static int
7742f2405bbSImre Vadász vtpci_unbind_intr(device_t dev, int what)
7752f2405bbSImre Vadász {
7762f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
7772f2405bbSImre Vadász 	struct vtpci_virtqueue *vqx;
7782f2405bbSImre Vadász 	uint irq;
7792f2405bbSImre Vadász 
7802f2405bbSImre Vadász 	if (what == -1) {
7812f2405bbSImre Vadász 		if (sc->vtpci_config_irq == -1)
7822f2405bbSImre Vadász 			return (EINVAL);
7832f2405bbSImre Vadász 
7842f2405bbSImre Vadász 		irq = sc->vtpci_config_irq;
7852f2405bbSImre Vadász 		sc->vtpci_config_irq = -1;
7862f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7872f2405bbSImre Vadász 			vtpci_register_msix_vector(sc,
7882f2405bbSImre Vadász 			    VIRTIO_MSI_CONFIG_VECTOR, -1);
7892f2405bbSImre Vadász 		}
7902f2405bbSImre Vadász 		goto done;
7912f2405bbSImre Vadász 	}
7922f2405bbSImre Vadász 
7932f2405bbSImre Vadász 	if (sc->vtpci_nvqs <= what || what < 0)
7942f2405bbSImre Vadász 		return (EINVAL);
7952f2405bbSImre Vadász 
7962f2405bbSImre Vadász 	vqx = &sc->vtpci_vqx[what];
7972f2405bbSImre Vadász 	if (vqx->ires_idx == -1)
7982f2405bbSImre Vadász 		return (EINVAL);
7992f2405bbSImre Vadász 
8002f2405bbSImre Vadász 	irq = vqx->ires_idx;
8012f2405bbSImre Vadász 	vqx->ires_idx = -1;
8022f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
8032f2405bbSImre Vadász 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
8042f2405bbSImre Vadász 		vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR, -1);
8052f2405bbSImre Vadász 	}
8062f2405bbSImre Vadász done:
8072f2405bbSImre Vadász 	KKASSERT(irq >= 0 && irq < sc->vtpci_nintr_res);
8082f2405bbSImre Vadász 	vtpci_del_irqentry(&sc->vtpci_intr_res[irq], what);
80911447b59SVenkatesh Srinivas 	return (0);
81011447b59SVenkatesh Srinivas }
81111447b59SVenkatesh Srinivas 
81211447b59SVenkatesh Srinivas static void
81311447b59SVenkatesh Srinivas vtpci_stop(device_t dev)
81411447b59SVenkatesh Srinivas {
81511447b59SVenkatesh Srinivas 	vtpci_reset(device_get_softc(dev));
81611447b59SVenkatesh Srinivas }
81711447b59SVenkatesh Srinivas 
81811447b59SVenkatesh Srinivas static int
81911447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features)
82011447b59SVenkatesh Srinivas {
82111447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
82211447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
82311447b59SVenkatesh Srinivas 	struct virtqueue *vq;
82411447b59SVenkatesh Srinivas 	int queue, error;
82511447b59SVenkatesh Srinivas 	uint16_t vq_size;
82611447b59SVenkatesh Srinivas 
82711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
82811447b59SVenkatesh Srinivas 
82911447b59SVenkatesh Srinivas 	/*
83011447b59SVenkatesh Srinivas 	 * Redrive the device initialization. This is a bit of an abuse
83111447b59SVenkatesh Srinivas 	 * of the specification, but both VirtualBox and QEMU/KVM seem
83211447b59SVenkatesh Srinivas 	 * to play nice. We do not allow the host device to change from
83311447b59SVenkatesh Srinivas 	 * what was originally negotiated beyond what the guest driver
83411447b59SVenkatesh Srinivas 	 * changed (MSIX state should not change, number of virtqueues
83511447b59SVenkatesh Srinivas 	 * and their size remain the same, etc).
83611447b59SVenkatesh Srinivas 	 */
83711447b59SVenkatesh Srinivas 
83811447b59SVenkatesh Srinivas 	if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
83911447b59SVenkatesh Srinivas 		vtpci_stop(dev);
84011447b59SVenkatesh Srinivas 
84111447b59SVenkatesh Srinivas 	/*
84211447b59SVenkatesh Srinivas 	 * Quickly drive the status through ACK and DRIVER. The device
84311447b59SVenkatesh Srinivas 	 * does not become usable again until vtpci_reinit_complete().
84411447b59SVenkatesh Srinivas 	 */
84511447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
84611447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
84711447b59SVenkatesh Srinivas 
84811447b59SVenkatesh Srinivas 	vtpci_negotiate_features(dev, features);
84911447b59SVenkatesh Srinivas 
85011447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
851c8247d06SImre Vadász 		pci_enable_msix(dev);
85211447b59SVenkatesh Srinivas 		error = vtpci_register_msix_vector(sc,
85311447b59SVenkatesh Srinivas 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
85411447b59SVenkatesh Srinivas 		if (error)
85511447b59SVenkatesh Srinivas 			return (error);
85611447b59SVenkatesh Srinivas 	}
85711447b59SVenkatesh Srinivas 
85811447b59SVenkatesh Srinivas 	for (queue = 0; queue < sc->vtpci_nvqs; queue++) {
85911447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
86011447b59SVenkatesh Srinivas 		vq = vqx->vq;
86111447b59SVenkatesh Srinivas 
86211447b59SVenkatesh Srinivas 		KASSERT(vq != NULL, ("vq %d not allocated", queue));
86311447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
86411447b59SVenkatesh Srinivas 
86511447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
86611447b59SVenkatesh Srinivas 		error = virtqueue_reinit(vq, vq_size);
86711447b59SVenkatesh Srinivas 		if (error)
86811447b59SVenkatesh Srinivas 			return (error);
86911447b59SVenkatesh Srinivas 
87011447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
87111447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
87211447b59SVenkatesh Srinivas 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
87311447b59SVenkatesh Srinivas 			if (error)
87411447b59SVenkatesh Srinivas 				return (error);
87511447b59SVenkatesh Srinivas 		}
87611447b59SVenkatesh Srinivas 
87711447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
87811447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
87911447b59SVenkatesh Srinivas 	}
88011447b59SVenkatesh Srinivas 
88111447b59SVenkatesh Srinivas 	return (0);
88211447b59SVenkatesh Srinivas }
88311447b59SVenkatesh Srinivas 
88411447b59SVenkatesh Srinivas static void
88511447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev)
88611447b59SVenkatesh Srinivas {
88711447b59SVenkatesh Srinivas 
88811447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
88911447b59SVenkatesh Srinivas }
89011447b59SVenkatesh Srinivas 
89111447b59SVenkatesh Srinivas static void
89211447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue)
89311447b59SVenkatesh Srinivas {
89411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
89511447b59SVenkatesh Srinivas 
89611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
89711447b59SVenkatesh Srinivas 
89811447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
89911447b59SVenkatesh Srinivas }
90011447b59SVenkatesh Srinivas 
90111447b59SVenkatesh Srinivas static uint8_t
90211447b59SVenkatesh Srinivas vtpci_get_status(device_t dev)
90311447b59SVenkatesh Srinivas {
90411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
90511447b59SVenkatesh Srinivas 
90611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
90711447b59SVenkatesh Srinivas 
90811447b59SVenkatesh Srinivas 	return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
90911447b59SVenkatesh Srinivas }
91011447b59SVenkatesh Srinivas 
91111447b59SVenkatesh Srinivas static void
91211447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status)
91311447b59SVenkatesh Srinivas {
91411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
91511447b59SVenkatesh Srinivas 
91611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
91711447b59SVenkatesh Srinivas 
91811447b59SVenkatesh Srinivas 	if (status != VIRTIO_CONFIG_STATUS_RESET)
91911447b59SVenkatesh Srinivas 		status |= vtpci_get_status(dev);
92011447b59SVenkatesh Srinivas 
92111447b59SVenkatesh Srinivas 	vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
92211447b59SVenkatesh Srinivas }
92311447b59SVenkatesh Srinivas 
92411447b59SVenkatesh Srinivas static void
92511447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset,
92611447b59SVenkatesh Srinivas     void *dst, int length)
92711447b59SVenkatesh Srinivas {
92811447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
92911447b59SVenkatesh Srinivas 	bus_size_t off;
93011447b59SVenkatesh Srinivas 	uint8_t *d;
93111447b59SVenkatesh Srinivas 	int size;
93211447b59SVenkatesh Srinivas 
93311447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
93411447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
93511447b59SVenkatesh Srinivas 
93611447b59SVenkatesh Srinivas 	for (d = dst; length > 0; d += size, off += size, length -= size) {
93711447b59SVenkatesh Srinivas 		if (length >= 4) {
93811447b59SVenkatesh Srinivas 			size = 4;
93911447b59SVenkatesh Srinivas 			*(uint32_t *)d = vtpci_read_config_4(sc, off);
94011447b59SVenkatesh Srinivas 		} else if (length >= 2) {
94111447b59SVenkatesh Srinivas 			size = 2;
94211447b59SVenkatesh Srinivas 			*(uint16_t *)d = vtpci_read_config_2(sc, off);
94311447b59SVenkatesh Srinivas 		} else {
94411447b59SVenkatesh Srinivas 			size = 1;
94511447b59SVenkatesh Srinivas 			*d = vtpci_read_config_1(sc, off);
94611447b59SVenkatesh Srinivas 		}
94711447b59SVenkatesh Srinivas 	}
94811447b59SVenkatesh Srinivas }
94911447b59SVenkatesh Srinivas 
95011447b59SVenkatesh Srinivas static void
95111447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset,
95211447b59SVenkatesh Srinivas     void *src, int length)
95311447b59SVenkatesh Srinivas {
95411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
95511447b59SVenkatesh Srinivas 	bus_size_t off;
95611447b59SVenkatesh Srinivas 	uint8_t *s;
95711447b59SVenkatesh Srinivas 	int size;
95811447b59SVenkatesh Srinivas 
95911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
96011447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
96111447b59SVenkatesh Srinivas 
96211447b59SVenkatesh Srinivas 	for (s = src; length > 0; s += size, off += size, length -= size) {
96311447b59SVenkatesh Srinivas 		if (length >= 4) {
96411447b59SVenkatesh Srinivas 			size = 4;
96511447b59SVenkatesh Srinivas 			vtpci_write_config_4(sc, off, *(uint32_t *)s);
96611447b59SVenkatesh Srinivas 		} else if (length >= 2) {
96711447b59SVenkatesh Srinivas 			size = 2;
96811447b59SVenkatesh Srinivas 			vtpci_write_config_2(sc, off, *(uint16_t *)s);
96911447b59SVenkatesh Srinivas 		} else {
97011447b59SVenkatesh Srinivas 			size = 1;
97111447b59SVenkatesh Srinivas 			vtpci_write_config_1(sc, off, *s);
97211447b59SVenkatesh Srinivas 		}
97311447b59SVenkatesh Srinivas 	}
97411447b59SVenkatesh Srinivas }
97511447b59SVenkatesh Srinivas 
97611447b59SVenkatesh Srinivas static void
97711447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
97811447b59SVenkatesh Srinivas     uint64_t features)
97911447b59SVenkatesh Srinivas {
98011447b59SVenkatesh Srinivas 	device_t dev, child;
98111447b59SVenkatesh Srinivas 
98211447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
98311447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
98411447b59SVenkatesh Srinivas 
98511447b59SVenkatesh Srinivas 	if (device_is_attached(child) && bootverbose == 0)
98611447b59SVenkatesh Srinivas 		return;
98711447b59SVenkatesh Srinivas 
98811447b59SVenkatesh Srinivas 	virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
98911447b59SVenkatesh Srinivas }
99011447b59SVenkatesh Srinivas 
99111447b59SVenkatesh Srinivas static void
99211447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc)
99311447b59SVenkatesh Srinivas {
99411447b59SVenkatesh Srinivas 	device_t dev, child;
995b17fe9c5SImre Vadasz 	int error;
99611447b59SVenkatesh Srinivas 
99711447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
99811447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
99911447b59SVenkatesh Srinivas 
100011447b59SVenkatesh Srinivas 	if (child == NULL)
100111447b59SVenkatesh Srinivas 		return;
100211447b59SVenkatesh Srinivas 
100311447b59SVenkatesh Srinivas 	if (device_get_state(child) != DS_NOTPRESENT)
100411447b59SVenkatesh Srinivas 		return;
100511447b59SVenkatesh Srinivas 
100611447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
1007b17fe9c5SImre Vadasz 	error = device_probe_and_attach(child);
1008b17fe9c5SImre Vadasz 	if (error != 0 || device_get_state(child) == DS_NOTPRESENT) {
100911447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
101011447b59SVenkatesh Srinivas 		vtpci_reset(sc);
101111447b59SVenkatesh Srinivas 		vtpci_release_child_resources(sc);
101211447b59SVenkatesh Srinivas 
101311447b59SVenkatesh Srinivas 		/* Reset status for future attempt. */
101411447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
101511447b59SVenkatesh Srinivas 	} else
101611447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
101711447b59SVenkatesh Srinivas }
101811447b59SVenkatesh Srinivas 
101911447b59SVenkatesh Srinivas static int
102011447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
102111447b59SVenkatesh Srinivas {
102211447b59SVenkatesh Srinivas 	device_t dev;
102311447b59SVenkatesh Srinivas 	uint16_t vector;
102411447b59SVenkatesh Srinivas 
102511447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
102611447b59SVenkatesh Srinivas 
102711447b59SVenkatesh Srinivas 	if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
102811447b59SVenkatesh Srinivas 	    offset != VIRTIO_MSI_QUEUE_VECTOR)
102911447b59SVenkatesh Srinivas 		return (EINVAL);
103011447b59SVenkatesh Srinivas 
103111447b59SVenkatesh Srinivas 	if (res_idx != -1) {
103211447b59SVenkatesh Srinivas 		/* Map from rid to host vector. */
1033c8247d06SImre Vadász 		vector = res_idx;
1034c8247d06SImre Vadász 	} else {
103511447b59SVenkatesh Srinivas 		vector = VIRTIO_MSI_NO_VECTOR;
1036c8247d06SImre Vadász 	}
103711447b59SVenkatesh Srinivas 
103811447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, offset, vector);
103911447b59SVenkatesh Srinivas 
104011447b59SVenkatesh Srinivas 	if (vtpci_read_config_2(sc, offset) != vector) {
104111447b59SVenkatesh Srinivas 		device_printf(dev, "insufficient host resources for "
104211447b59SVenkatesh Srinivas 		    "MSIX interrupts\n");
104311447b59SVenkatesh Srinivas 		return (ENODEV);
104411447b59SVenkatesh Srinivas 	}
104511447b59SVenkatesh Srinivas 
104611447b59SVenkatesh Srinivas 	return (0);
104711447b59SVenkatesh Srinivas }
104811447b59SVenkatesh Srinivas 
104911447b59SVenkatesh Srinivas static void
105011447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc)
105111447b59SVenkatesh Srinivas {
1052c8247d06SImre Vadász 	device_t dev = sc->vtpci_dev;
105311447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
105411447b59SVenkatesh Srinivas 	int i;
105511447b59SVenkatesh Srinivas 
1056c8247d06SImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
105711447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[i];
105811447b59SVenkatesh Srinivas 
105911447b59SVenkatesh Srinivas 		if (ires->intrhand != NULL) {
106011447b59SVenkatesh Srinivas 			bus_teardown_intr(dev, ires->irq, ires->intrhand);
106111447b59SVenkatesh Srinivas 			ires->intrhand = NULL;
106211447b59SVenkatesh Srinivas 		}
106311447b59SVenkatesh Srinivas 		if (ires->irq != NULL) {
106411447b59SVenkatesh Srinivas 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
106511447b59SVenkatesh Srinivas 			    ires->irq);
106611447b59SVenkatesh Srinivas 			ires->irq = NULL;
106711447b59SVenkatesh Srinivas 		}
10682f2405bbSImre Vadász 	}
10692f2405bbSImre Vadász 
10702f2405bbSImre Vadász 	vtpci_unbind_intr(sc->vtpci_dev, -1);
10712f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nvqs; i++)
10722f2405bbSImre Vadász 		vtpci_unbind_intr(sc->vtpci_dev, i);
10732f2405bbSImre Vadász 
10742f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
10752f2405bbSImre Vadász 		ires = &sc->vtpci_intr_res[i];
10762f2405bbSImre Vadász 
1077c8247d06SImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
1078c8247d06SImre Vadász 			pci_release_msix_vector(dev, ires->rid);
1079c8247d06SImre Vadász 		ires->rid = 0;
108011447b59SVenkatesh Srinivas 	}
1081c8247d06SImre Vadász 	sc->vtpci_nintr_res = 0;
1082c8247d06SImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
1083c8247d06SImre Vadász 		pci_release_msi(dev);
1084c8247d06SImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
1085c8247d06SImre Vadász 	}
1086c8247d06SImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
1087c8247d06SImre Vadász 		pci_disable_msix(dev);
1088c8247d06SImre Vadász 		pci_teardown_msix(dev);
10892f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
1090c8247d06SImre Vadász 	}
1091c8247d06SImre Vadász 
109211447b59SVenkatesh Srinivas }
109311447b59SVenkatesh Srinivas 
109411447b59SVenkatesh Srinivas static void
109511447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc)
109611447b59SVenkatesh Srinivas {
109711447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
109811447b59SVenkatesh Srinivas 	int i;
109911447b59SVenkatesh Srinivas 
110011447b59SVenkatesh Srinivas 	sc->vtpci_nvqs = 0;
110111447b59SVenkatesh Srinivas 
110211447b59SVenkatesh Srinivas 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
110311447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
110411447b59SVenkatesh Srinivas 
110511447b59SVenkatesh Srinivas 		if (vqx->vq != NULL) {
110611447b59SVenkatesh Srinivas 			virtqueue_free(vqx->vq);
110711447b59SVenkatesh Srinivas 			vqx->vq = NULL;
110811447b59SVenkatesh Srinivas 		}
110911447b59SVenkatesh Srinivas 	}
111011447b59SVenkatesh Srinivas }
111111447b59SVenkatesh Srinivas 
111211447b59SVenkatesh Srinivas static void
111311447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc)
111411447b59SVenkatesh Srinivas {
111511447b59SVenkatesh Srinivas 	vtpci_free_interrupts(sc);
111611447b59SVenkatesh Srinivas 	vtpci_free_virtqueues(sc);
111711447b59SVenkatesh Srinivas }
111811447b59SVenkatesh Srinivas 
111911447b59SVenkatesh Srinivas static void
112011447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc)
112111447b59SVenkatesh Srinivas {
112211447b59SVenkatesh Srinivas 
112311447b59SVenkatesh Srinivas 	/*
112411447b59SVenkatesh Srinivas 	 * Setting the status to RESET sets the host device to
112511447b59SVenkatesh Srinivas 	 * the original, uninitialized state.
112611447b59SVenkatesh Srinivas 	 */
112711447b59SVenkatesh Srinivas 	vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
112811447b59SVenkatesh Srinivas }
112911447b59SVenkatesh Srinivas 
1130df2df2fcSImre Vadász static void
11312f2405bbSImre Vadász vtpci_legacy_intr(void *arg)
113211447b59SVenkatesh Srinivas {
11332f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
113411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
11352f2405bbSImre Vadász 	struct vqentry *e;
113611447b59SVenkatesh Srinivas 	uint8_t isr;
113711447b59SVenkatesh Srinivas 
11382f2405bbSImre Vadász 	ires = arg;
11392f2405bbSImre Vadász 	sc = ires->ires_sc;
114011447b59SVenkatesh Srinivas 
114111447b59SVenkatesh Srinivas 	/* Reading the ISR also clears it. */
114211447b59SVenkatesh Srinivas 	isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
114311447b59SVenkatesh Srinivas 
11442f2405bbSImre Vadász 	TAILQ_FOREACH(e, &ires->ls, entries) {
11459d96478cSImre Vadász 		/* XXX Allow for masking individual virtqueue handlers. */
11462f2405bbSImre Vadász 		if (e->what == -1) {
114711447b59SVenkatesh Srinivas 			if (isr & VIRTIO_PCI_ISR_CONFIG)
1148*b817dce2SImre Vadász 				e->handler(e->arg);
11499d96478cSImre Vadász 		} else if ((isr & VIRTIO_PCI_ISR_INTR) &&
11509d96478cSImre Vadász 		    virtqueue_pending(e->vq)) {
11519d96478cSImre Vadász 			e->handler(e->arg);
11522f2405bbSImre Vadász 		}
1153c8247d06SImre Vadász 	}
115411447b59SVenkatesh Srinivas }
115511447b59SVenkatesh Srinivas 
1156df2df2fcSImre Vadász static void
11572f2405bbSImre Vadász vtpci_msix_intr(void *arg)
115811447b59SVenkatesh Srinivas {
11592f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
116011447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
11612f2405bbSImre Vadász 	struct vqentry *e;
116211447b59SVenkatesh Srinivas 
11632f2405bbSImre Vadász 	ires = arg;
11642f2405bbSImre Vadász 	sc = ires->ires_sc;
11652f2405bbSImre Vadász 	TAILQ_FOREACH(e, &ires->ls, entries) {
11669d96478cSImre Vadász 		/* XXX Allow for masking individual virtqueue handlers. */
11672f2405bbSImre Vadász 		if (e->what == -1) {
1168*b817dce2SImre Vadász 			e->handler(e->arg);
11699d96478cSImre Vadász 		} else if (virtqueue_pending(e->vq)) {
11709d96478cSImre Vadász 			e->handler(e->arg);
117111447b59SVenkatesh Srinivas 		}
11722f2405bbSImre Vadász 	}
117311447b59SVenkatesh Srinivas }
1174