xref: /dflybsd-src/sys/dev/virtual/virtio/pci/virtio_pci.c (revision 2f2405bb182023ff0ff0fb5a4d9fc82a5231807c)
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"
4711447b59SVenkatesh Srinivas #include "virtio_if.h"
48dcbcbc86SVenkatesh Srinivas #include "virtio_bus_if.h"
4911447b59SVenkatesh Srinivas 
50*2f2405bbSImre Vadász struct vqentry {
51*2f2405bbSImre Vadász 	int what;
52*2f2405bbSImre Vadász 	TAILQ_ENTRY(vqentry) entries;
53*2f2405bbSImre Vadász };
54*2f2405bbSImre Vadász 
55*2f2405bbSImre Vadász TAILQ_HEAD(vqirq_list, vqentry);
56*2f2405bbSImre Vadász 
5711447b59SVenkatesh Srinivas struct vtpci_softc {
5811447b59SVenkatesh Srinivas 	device_t			 vtpci_dev;
5911447b59SVenkatesh Srinivas 	struct resource			*vtpci_res;
6011447b59SVenkatesh Srinivas 	struct resource			*vtpci_msix_res;
6111447b59SVenkatesh Srinivas 	uint64_t			 vtpci_features;
6211447b59SVenkatesh Srinivas 	uint32_t			 vtpci_flags;
63c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSI		 0x0001
64c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSIX		 0x0010
6511447b59SVenkatesh Srinivas 
6611447b59SVenkatesh Srinivas 	device_t			 vtpci_child_dev;
6711447b59SVenkatesh Srinivas 	struct virtio_feature_desc	*vtpci_child_feat_desc;
6811447b59SVenkatesh Srinivas 
6911447b59SVenkatesh Srinivas 	/*
7011447b59SVenkatesh Srinivas 	 * Ideally, each virtqueue that the driver provides a callback for
7111447b59SVenkatesh Srinivas 	 * will receive its own MSIX vector. If there are not sufficient
7211447b59SVenkatesh Srinivas 	 * vectors available, we will then attempt to have all the VQs
7311447b59SVenkatesh Srinivas 	 * share one vector. Note that when using MSIX, the configuration
7411447b59SVenkatesh Srinivas 	 * changed notifications must be on their own vector.
7511447b59SVenkatesh Srinivas 	 *
7611447b59SVenkatesh Srinivas 	 * If MSIX is not available, we will attempt to have the whole
7711447b59SVenkatesh Srinivas 	 * device share one MSI vector, and then, finally, one legacy
7811447b59SVenkatesh Srinivas 	 * interrupt.
7911447b59SVenkatesh Srinivas 	 */
8011447b59SVenkatesh Srinivas 	int				 vtpci_nvqs;
8111447b59SVenkatesh Srinivas 	struct vtpci_virtqueue {
8211447b59SVenkatesh Srinivas 		struct virtqueue *vq;
8311447b59SVenkatesh Srinivas 
84*2f2405bbSImre Vadász 		/* Index into vtpci_intr_res[] below. -1 if no IRQ assigned. */
8511447b59SVenkatesh Srinivas 		int		  ires_idx;
8611447b59SVenkatesh Srinivas 	} vtpci_vqx[VIRTIO_MAX_VIRTQUEUES];
8711447b59SVenkatesh Srinivas 
8811447b59SVenkatesh Srinivas 	/*
8911447b59SVenkatesh Srinivas 	 * When using MSIX interrupts, the first element of vtpci_intr_res[]
9011447b59SVenkatesh Srinivas 	 * is always the configuration changed notifications. The remaining
9111447b59SVenkatesh Srinivas 	 * element(s) are used for the virtqueues.
9211447b59SVenkatesh Srinivas 	 *
9311447b59SVenkatesh Srinivas 	 * With MSI and legacy interrupts, only the first element of
9411447b59SVenkatesh Srinivas 	 * vtpci_intr_res[] is used.
9511447b59SVenkatesh Srinivas 	 */
9611447b59SVenkatesh Srinivas 	int				 vtpci_nintr_res;
97c8247d06SImre Vadász 	int				 vtpci_irq_flags;
9811447b59SVenkatesh Srinivas 	struct vtpci_intr_resource {
99*2f2405bbSImre Vadász 		struct vtpci_softc *ires_sc;
10011447b59SVenkatesh Srinivas 		struct resource	*irq;
10111447b59SVenkatesh Srinivas 		int		 rid;
10211447b59SVenkatesh Srinivas 		void		*intrhand;
103*2f2405bbSImre Vadász 		struct vqirq_list ls;
10411447b59SVenkatesh Srinivas 	} vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES];
105*2f2405bbSImre Vadász 
106*2f2405bbSImre Vadász 	int				 vtpci_config_irq;
10711447b59SVenkatesh Srinivas };
10811447b59SVenkatesh Srinivas 
10911447b59SVenkatesh Srinivas static int	vtpci_probe(device_t);
11011447b59SVenkatesh Srinivas static int	vtpci_attach(device_t);
11111447b59SVenkatesh Srinivas static int	vtpci_detach(device_t);
11211447b59SVenkatesh Srinivas static int	vtpci_suspend(device_t);
11311447b59SVenkatesh Srinivas static int	vtpci_resume(device_t);
11411447b59SVenkatesh Srinivas static int	vtpci_shutdown(device_t);
11511447b59SVenkatesh Srinivas static void	vtpci_driver_added(device_t, driver_t *);
11611447b59SVenkatesh Srinivas static void	vtpci_child_detached(device_t, device_t);
11711447b59SVenkatesh Srinivas static int	vtpci_read_ivar(device_t, device_t, int, uintptr_t *);
11811447b59SVenkatesh Srinivas static int	vtpci_write_ivar(device_t, device_t, int, uintptr_t);
11911447b59SVenkatesh Srinivas 
12011447b59SVenkatesh Srinivas static uint64_t	vtpci_negotiate_features(device_t, uint64_t);
12111447b59SVenkatesh Srinivas static int	vtpci_with_feature(device_t, uint64_t);
122*2f2405bbSImre Vadász static int	vtpci_intr_count(device_t dev);
123*2f2405bbSImre Vadász static int	vtpci_intr_alloc(device_t dev, int *cnt, int use_config,
124*2f2405bbSImre Vadász 		    int *cpus);
125*2f2405bbSImre Vadász static int	vtpci_intr_release(device_t dev);
12611447b59SVenkatesh Srinivas static int	vtpci_alloc_virtqueues(device_t, int, int,
12711447b59SVenkatesh Srinivas 		    struct vq_alloc_info *);
128*2f2405bbSImre Vadász static int	vtpci_setup_intr(device_t, uint irq, lwkt_serialize_t);
129*2f2405bbSImre Vadász static int	vtpci_teardown_intr(device_t, uint irq);
130*2f2405bbSImre Vadász static int	vtpci_bind_intr(device_t, uint, int);
131*2f2405bbSImre Vadász static int	vtpci_unbind_intr(device_t, int);
13211447b59SVenkatesh Srinivas static void	vtpci_stop(device_t);
13311447b59SVenkatesh Srinivas static int	vtpci_reinit(device_t, uint64_t);
13411447b59SVenkatesh Srinivas static void	vtpci_reinit_complete(device_t);
13511447b59SVenkatesh Srinivas static void	vtpci_notify_virtqueue(device_t, uint16_t);
13611447b59SVenkatesh Srinivas static uint8_t	vtpci_get_status(device_t);
13711447b59SVenkatesh Srinivas static void	vtpci_set_status(device_t, uint8_t);
13811447b59SVenkatesh Srinivas static void	vtpci_read_dev_config(device_t, bus_size_t, void *, int);
13911447b59SVenkatesh Srinivas static void	vtpci_write_dev_config(device_t, bus_size_t, void *, int);
14011447b59SVenkatesh Srinivas 
14111447b59SVenkatesh Srinivas static void	vtpci_describe_features(struct vtpci_softc *, const char *,
14211447b59SVenkatesh Srinivas 		    uint64_t);
14311447b59SVenkatesh Srinivas static void	vtpci_probe_and_attach_child(struct vtpci_softc *);
14411447b59SVenkatesh Srinivas 
14511447b59SVenkatesh Srinivas static int	vtpci_register_msix_vector(struct vtpci_softc *, int, int);
14611447b59SVenkatesh Srinivas 
14711447b59SVenkatesh Srinivas static void	vtpci_free_interrupts(struct vtpci_softc *);
14811447b59SVenkatesh Srinivas static void	vtpci_free_virtqueues(struct vtpci_softc *);
14911447b59SVenkatesh Srinivas static void	vtpci_release_child_resources(struct vtpci_softc *);
15011447b59SVenkatesh Srinivas static void	vtpci_reset(struct vtpci_softc *);
15111447b59SVenkatesh Srinivas 
152df2df2fcSImre Vadász static void	vtpci_legacy_intr(void *);
153*2f2405bbSImre Vadász static void	vtpci_msix_intr(void *);
154df2df2fcSImre Vadász static void	vtpci_config_intr(void *);
15511447b59SVenkatesh Srinivas 
15611447b59SVenkatesh Srinivas /*
15711447b59SVenkatesh Srinivas  * I/O port read/write wrappers.
15811447b59SVenkatesh Srinivas  */
15911447b59SVenkatesh Srinivas #define vtpci_read_config_1(sc, o)	bus_read_1((sc)->vtpci_res, (o))
16011447b59SVenkatesh Srinivas #define vtpci_read_config_2(sc, o)	bus_read_2((sc)->vtpci_res, (o))
16111447b59SVenkatesh Srinivas #define vtpci_read_config_4(sc, o)	bus_read_4((sc)->vtpci_res, (o))
16211447b59SVenkatesh Srinivas #define vtpci_write_config_1(sc, o, v)	bus_write_1((sc)->vtpci_res, (o), (v))
16311447b59SVenkatesh Srinivas #define vtpci_write_config_2(sc, o, v)	bus_write_2((sc)->vtpci_res, (o), (v))
16411447b59SVenkatesh Srinivas #define vtpci_write_config_4(sc, o, v)	bus_write_4((sc)->vtpci_res, (o), (v))
16511447b59SVenkatesh Srinivas 
16611447b59SVenkatesh Srinivas /* Tunables. */
16711447b59SVenkatesh Srinivas static int vtpci_disable_msix = 0;
16811447b59SVenkatesh Srinivas TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix);
16911447b59SVenkatesh Srinivas 
17011447b59SVenkatesh Srinivas static device_method_t vtpci_methods[] = {
17111447b59SVenkatesh Srinivas 	/* Device interface. */
17211447b59SVenkatesh Srinivas 	DEVMETHOD(device_probe,			  vtpci_probe),
17311447b59SVenkatesh Srinivas 	DEVMETHOD(device_attach,		  vtpci_attach),
17411447b59SVenkatesh Srinivas 	DEVMETHOD(device_detach,		  vtpci_detach),
17511447b59SVenkatesh Srinivas 	DEVMETHOD(device_suspend,		  vtpci_suspend),
17611447b59SVenkatesh Srinivas 	DEVMETHOD(device_resume,		  vtpci_resume),
17711447b59SVenkatesh Srinivas 	DEVMETHOD(device_shutdown,		  vtpci_shutdown),
17811447b59SVenkatesh Srinivas 
17911447b59SVenkatesh Srinivas 	/* Bus interface. */
18011447b59SVenkatesh Srinivas 	DEVMETHOD(bus_driver_added,		  vtpci_driver_added),
18111447b59SVenkatesh Srinivas 	DEVMETHOD(bus_child_detached,		  vtpci_child_detached),
18211447b59SVenkatesh Srinivas 	DEVMETHOD(bus_read_ivar,		  vtpci_read_ivar),
18311447b59SVenkatesh Srinivas 	DEVMETHOD(bus_write_ivar,		  vtpci_write_ivar),
18411447b59SVenkatesh Srinivas 
18511447b59SVenkatesh Srinivas 	/* VirtIO bus interface. */
18611447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_negotiate_features,  vtpci_negotiate_features),
18711447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_with_feature,	  vtpci_with_feature),
188*2f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_count,	  vtpci_intr_count),
189*2f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_alloc,	  vtpci_intr_alloc),
190*2f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_release,	  vtpci_intr_release),
19111447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_alloc_virtqueues,	  vtpci_alloc_virtqueues),
19211447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_setup_intr,	  vtpci_setup_intr),
193*2f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_teardown_intr,	  vtpci_teardown_intr),
194*2f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_bind_intr,		  vtpci_bind_intr),
195*2f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_unbind_intr,	  vtpci_unbind_intr),
19611447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_stop,		  vtpci_stop),
19711447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_reinit,		  vtpci_reinit),
19811447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_reinit_complete,	  vtpci_reinit_complete),
19911447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_notify_vq,		  vtpci_notify_virtqueue),
20011447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_read_device_config,  vtpci_read_dev_config),
20111447b59SVenkatesh Srinivas 	DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config),
20211447b59SVenkatesh Srinivas 
203d3c9c58eSSascha Wildner 	DEVMETHOD_END
20411447b59SVenkatesh Srinivas };
20511447b59SVenkatesh Srinivas 
20611447b59SVenkatesh Srinivas static driver_t vtpci_driver = {
20711447b59SVenkatesh Srinivas 	"virtio_pci",
20811447b59SVenkatesh Srinivas 	vtpci_methods,
20911447b59SVenkatesh Srinivas 	sizeof(struct vtpci_softc)
21011447b59SVenkatesh Srinivas };
21111447b59SVenkatesh Srinivas 
21211447b59SVenkatesh Srinivas devclass_t vtpci_devclass;
21311447b59SVenkatesh Srinivas 
214dfc199f7SSascha Wildner DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, NULL, NULL);
21511447b59SVenkatesh Srinivas MODULE_VERSION(virtio_pci, 1);
21611447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, pci, 1, 1, 1);
21711447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1);
21811447b59SVenkatesh Srinivas 
21911447b59SVenkatesh Srinivas static int
22011447b59SVenkatesh Srinivas vtpci_probe(device_t dev)
22111447b59SVenkatesh Srinivas {
22211447b59SVenkatesh Srinivas 	char desc[36];
22311447b59SVenkatesh Srinivas 	const char *name;
22411447b59SVenkatesh Srinivas 
22511447b59SVenkatesh Srinivas 	if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
22611447b59SVenkatesh Srinivas 		return (ENXIO);
22711447b59SVenkatesh Srinivas 
22811447b59SVenkatesh Srinivas 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
22911447b59SVenkatesh Srinivas 	    pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX)
23011447b59SVenkatesh Srinivas 		return (ENXIO);
23111447b59SVenkatesh Srinivas 
23211447b59SVenkatesh Srinivas 	if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION)
23311447b59SVenkatesh Srinivas 		return (ENXIO);
23411447b59SVenkatesh Srinivas 
23511447b59SVenkatesh Srinivas 	name = virtio_device_name(pci_get_subdevice(dev));
23611447b59SVenkatesh Srinivas 	if (name == NULL)
23711447b59SVenkatesh Srinivas 		name = "Unknown";
23811447b59SVenkatesh Srinivas 
23911447b59SVenkatesh Srinivas 	ksnprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name);
24011447b59SVenkatesh Srinivas 	device_set_desc_copy(dev, desc);
24111447b59SVenkatesh Srinivas 
24211447b59SVenkatesh Srinivas 	return (BUS_PROBE_DEFAULT);
24311447b59SVenkatesh Srinivas }
24411447b59SVenkatesh Srinivas 
24511447b59SVenkatesh Srinivas static int
24611447b59SVenkatesh Srinivas vtpci_attach(device_t dev)
24711447b59SVenkatesh Srinivas {
24811447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
24911447b59SVenkatesh Srinivas 	device_t child;
250c8247d06SImre Vadász 	int msix_cap, rid;
25111447b59SVenkatesh Srinivas 
25211447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
25311447b59SVenkatesh Srinivas 	sc->vtpci_dev = dev;
254*2f2405bbSImre Vadász 	sc->vtpci_config_irq = -1;
25511447b59SVenkatesh Srinivas 
25611447b59SVenkatesh Srinivas 	pci_enable_busmaster(dev);
25711447b59SVenkatesh Srinivas 
25811447b59SVenkatesh Srinivas 	rid = PCIR_BAR(0);
25911447b59SVenkatesh Srinivas 	sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
26011447b59SVenkatesh Srinivas 	    RF_ACTIVE);
26111447b59SVenkatesh Srinivas 	if (sc->vtpci_res == NULL) {
26211447b59SVenkatesh Srinivas 		device_printf(dev, "cannot map I/O space\n");
26311447b59SVenkatesh Srinivas 		return (ENXIO);
26411447b59SVenkatesh Srinivas 	}
26511447b59SVenkatesh Srinivas 
266c8247d06SImre Vadász 	if (pci_find_extcap(dev, PCIY_MSIX, &msix_cap) == 0) {
267c8247d06SImre Vadász 		uint32_t val;
268c8247d06SImre Vadász 		val = pci_read_config(dev, msix_cap + PCIR_MSIX_TABLE, 4);
269c8247d06SImre Vadász 		rid = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
27011447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = bus_alloc_resource_any(dev,
27111447b59SVenkatesh Srinivas 		    SYS_RES_MEMORY, &rid, RF_ACTIVE);
27211447b59SVenkatesh Srinivas 	}
27311447b59SVenkatesh Srinivas 
27411447b59SVenkatesh Srinivas 	vtpci_reset(sc);
27511447b59SVenkatesh Srinivas 
27611447b59SVenkatesh Srinivas 	/* Tell the host we've noticed this device. */
27711447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
27811447b59SVenkatesh Srinivas 
27911447b59SVenkatesh Srinivas 	if ((child = device_add_child(dev, NULL, -1)) == NULL) {
28011447b59SVenkatesh Srinivas 		device_printf(dev, "cannot create child device\n");
28111447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
28211447b59SVenkatesh Srinivas 		vtpci_detach(dev);
28311447b59SVenkatesh Srinivas 		return (ENOMEM);
28411447b59SVenkatesh Srinivas 	}
28511447b59SVenkatesh Srinivas 
28611447b59SVenkatesh Srinivas 	sc->vtpci_child_dev = child;
28711447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
28811447b59SVenkatesh Srinivas 
28911447b59SVenkatesh Srinivas 	return (0);
29011447b59SVenkatesh Srinivas }
29111447b59SVenkatesh Srinivas 
29211447b59SVenkatesh Srinivas static int
29311447b59SVenkatesh Srinivas vtpci_detach(device_t dev)
29411447b59SVenkatesh Srinivas {
29511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
29611447b59SVenkatesh Srinivas 	device_t child;
29711447b59SVenkatesh Srinivas 	int error;
29811447b59SVenkatesh Srinivas 
29911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
30011447b59SVenkatesh Srinivas 
30111447b59SVenkatesh Srinivas 	if ((child = sc->vtpci_child_dev) != NULL) {
30211447b59SVenkatesh Srinivas 		error = device_delete_child(dev, child);
30311447b59SVenkatesh Srinivas 		if (error)
30411447b59SVenkatesh Srinivas 			return (error);
30511447b59SVenkatesh Srinivas 		sc->vtpci_child_dev = NULL;
30611447b59SVenkatesh Srinivas 	}
30711447b59SVenkatesh Srinivas 
30811447b59SVenkatesh Srinivas 	vtpci_reset(sc);
30911447b59SVenkatesh Srinivas 
31011447b59SVenkatesh Srinivas 	if (sc->vtpci_msix_res != NULL) {
311c8247d06SImre Vadász 		bus_release_resource(dev, SYS_RES_MEMORY,
312c8247d06SImre Vadász 		    rman_get_rid(sc->vtpci_msix_res), sc->vtpci_msix_res);
31311447b59SVenkatesh Srinivas 		sc->vtpci_msix_res = NULL;
31411447b59SVenkatesh Srinivas 	}
31511447b59SVenkatesh Srinivas 
31611447b59SVenkatesh Srinivas 	if (sc->vtpci_res != NULL) {
31711447b59SVenkatesh Srinivas 		bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
31811447b59SVenkatesh Srinivas 		    sc->vtpci_res);
31911447b59SVenkatesh Srinivas 		sc->vtpci_res = NULL;
32011447b59SVenkatesh Srinivas 	}
32111447b59SVenkatesh Srinivas 
32211447b59SVenkatesh Srinivas 	return (0);
32311447b59SVenkatesh Srinivas }
32411447b59SVenkatesh Srinivas 
32511447b59SVenkatesh Srinivas static int
32611447b59SVenkatesh Srinivas vtpci_suspend(device_t dev)
32711447b59SVenkatesh Srinivas {
32811447b59SVenkatesh Srinivas 
32911447b59SVenkatesh Srinivas 	return (bus_generic_suspend(dev));
33011447b59SVenkatesh Srinivas }
33111447b59SVenkatesh Srinivas 
33211447b59SVenkatesh Srinivas static int
33311447b59SVenkatesh Srinivas vtpci_resume(device_t dev)
33411447b59SVenkatesh Srinivas {
33511447b59SVenkatesh Srinivas 
33611447b59SVenkatesh Srinivas 	return (bus_generic_resume(dev));
33711447b59SVenkatesh Srinivas }
33811447b59SVenkatesh Srinivas 
33911447b59SVenkatesh Srinivas static int
34011447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev)
34111447b59SVenkatesh Srinivas {
34211447b59SVenkatesh Srinivas 
34311447b59SVenkatesh Srinivas 	(void) bus_generic_shutdown(dev);
34411447b59SVenkatesh Srinivas 	/* Forcibly stop the host device. */
34511447b59SVenkatesh Srinivas 	vtpci_stop(dev);
34611447b59SVenkatesh Srinivas 
34711447b59SVenkatesh Srinivas 	return (0);
34811447b59SVenkatesh Srinivas }
34911447b59SVenkatesh Srinivas 
35011447b59SVenkatesh Srinivas static void
35111447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver)
35211447b59SVenkatesh Srinivas {
35311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
35411447b59SVenkatesh Srinivas 
35511447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
35611447b59SVenkatesh Srinivas 
35711447b59SVenkatesh Srinivas 	vtpci_probe_and_attach_child(sc);
35811447b59SVenkatesh Srinivas }
35911447b59SVenkatesh Srinivas 
36011447b59SVenkatesh Srinivas static void
36111447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child)
36211447b59SVenkatesh Srinivas {
36311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
36411447b59SVenkatesh Srinivas 
36511447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
36611447b59SVenkatesh Srinivas 
36711447b59SVenkatesh Srinivas 	vtpci_reset(sc);
36811447b59SVenkatesh Srinivas 	vtpci_release_child_resources(sc);
36911447b59SVenkatesh Srinivas }
37011447b59SVenkatesh Srinivas 
37111447b59SVenkatesh Srinivas static int
37211447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
37311447b59SVenkatesh Srinivas {
37411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
37511447b59SVenkatesh Srinivas 
37611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
37711447b59SVenkatesh Srinivas 
37811447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
37911447b59SVenkatesh Srinivas 		return (ENOENT);
38011447b59SVenkatesh Srinivas 
38111447b59SVenkatesh Srinivas 	switch (index) {
38211447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_DEVTYPE:
38311447b59SVenkatesh Srinivas 		*result = pci_get_subdevice(dev);
38411447b59SVenkatesh Srinivas 		break;
38511447b59SVenkatesh Srinivas 	default:
38611447b59SVenkatesh Srinivas 		return (ENOENT);
38711447b59SVenkatesh Srinivas 	}
38811447b59SVenkatesh Srinivas 
38911447b59SVenkatesh Srinivas 	return (0);
39011447b59SVenkatesh Srinivas }
39111447b59SVenkatesh Srinivas 
39211447b59SVenkatesh Srinivas static int
39311447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
39411447b59SVenkatesh Srinivas {
39511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
39611447b59SVenkatesh Srinivas 
39711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
39811447b59SVenkatesh Srinivas 
39911447b59SVenkatesh Srinivas 	if (sc->vtpci_child_dev != child)
40011447b59SVenkatesh Srinivas 		return (ENOENT);
40111447b59SVenkatesh Srinivas 
40211447b59SVenkatesh Srinivas 	switch (index) {
40311447b59SVenkatesh Srinivas 	case VIRTIO_IVAR_FEATURE_DESC:
40411447b59SVenkatesh Srinivas 		sc->vtpci_child_feat_desc = (void *) value;
40511447b59SVenkatesh Srinivas 		break;
40611447b59SVenkatesh Srinivas 	default:
40711447b59SVenkatesh Srinivas 		return (ENOENT);
40811447b59SVenkatesh Srinivas 	}
40911447b59SVenkatesh Srinivas 
41011447b59SVenkatesh Srinivas 	return (0);
41111447b59SVenkatesh Srinivas }
41211447b59SVenkatesh Srinivas 
41311447b59SVenkatesh Srinivas static uint64_t
41411447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features)
41511447b59SVenkatesh Srinivas {
41611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
41711447b59SVenkatesh Srinivas 	uint64_t host_features, features;
41811447b59SVenkatesh Srinivas 
41911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
42011447b59SVenkatesh Srinivas 
42111447b59SVenkatesh Srinivas 	host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES);
42211447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "host", host_features);
42311447b59SVenkatesh Srinivas 
42411447b59SVenkatesh Srinivas 	/*
42511447b59SVenkatesh Srinivas 	 * Limit negotiated features to what the driver, virtqueue, and
42611447b59SVenkatesh Srinivas 	 * host all support.
42711447b59SVenkatesh Srinivas 	 */
42811447b59SVenkatesh Srinivas 	features = host_features & child_features;
42911447b59SVenkatesh Srinivas 	features = virtqueue_filter_features(features);
43011447b59SVenkatesh Srinivas 	sc->vtpci_features = features;
43111447b59SVenkatesh Srinivas 
43211447b59SVenkatesh Srinivas 	vtpci_describe_features(sc, "negotiated", features);
43311447b59SVenkatesh Srinivas 	vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features);
43411447b59SVenkatesh Srinivas 
43511447b59SVenkatesh Srinivas 	return (features);
43611447b59SVenkatesh Srinivas }
43711447b59SVenkatesh Srinivas 
43811447b59SVenkatesh Srinivas static int
43911447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature)
44011447b59SVenkatesh Srinivas {
44111447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
44211447b59SVenkatesh Srinivas 
44311447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
44411447b59SVenkatesh Srinivas 
44511447b59SVenkatesh Srinivas 	return ((sc->vtpci_features & feature) != 0);
44611447b59SVenkatesh Srinivas }
44711447b59SVenkatesh Srinivas 
44811447b59SVenkatesh Srinivas static int
449*2f2405bbSImre Vadász vtpci_intr_count(device_t dev)
450*2f2405bbSImre Vadász {
451*2f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
452*2f2405bbSImre Vadász 
453*2f2405bbSImre Vadász 	if (vtpci_disable_msix != 0 || sc->vtpci_msix_res == NULL)
454*2f2405bbSImre Vadász 		return 1;
455*2f2405bbSImre Vadász 	else
456*2f2405bbSImre Vadász 		return pci_msix_count(dev);
457*2f2405bbSImre Vadász }
458*2f2405bbSImre Vadász 
459*2f2405bbSImre Vadász /* Will never return 0, with *cnt <= 0. */
460*2f2405bbSImre Vadász static int
461*2f2405bbSImre Vadász vtpci_intr_alloc(device_t dev, int *cnt, int use_config, int *cpus)
462*2f2405bbSImre Vadász {
463*2f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
464*2f2405bbSImre Vadász 	int i;
465*2f2405bbSImre Vadász 
466*2f2405bbSImre Vadász 	if (sc->vtpci_nintr_res > 0)
467*2f2405bbSImre Vadász 		return (EINVAL);
468*2f2405bbSImre Vadász 
469*2f2405bbSImre Vadász 	if (*cnt <= 0)
470*2f2405bbSImre Vadász 		return (EINVAL);
471*2f2405bbSImre Vadász 
472*2f2405bbSImre Vadász 	if (vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
473*2f2405bbSImre Vadász 		int nmsix = pci_msix_count(dev);
474*2f2405bbSImre Vadász 		if (nmsix < *cnt)
475*2f2405bbSImre Vadász 			*cnt = nmsix;
476*2f2405bbSImre Vadász 	}
477*2f2405bbSImre Vadász 
478*2f2405bbSImre Vadász 	if ((*cnt > 1 || use_config == 0) &&
479*2f2405bbSImre Vadász 	    vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
480*2f2405bbSImre Vadász 		if (pci_setup_msix(dev) != 0) {
481*2f2405bbSImre Vadász 			device_printf(dev, "pci_setup_msix failed\n");
482*2f2405bbSImre Vadász 			/* Just fallthrough to legacy IRQ code instead. */
483*2f2405bbSImre Vadász 		} else {
484*2f2405bbSImre Vadász 			for (i = 0; i < *cnt; i++) {
485*2f2405bbSImre Vadász 				int cpu, rid;
486*2f2405bbSImre Vadász 
487*2f2405bbSImre Vadász 				if (cpus != NULL && cpus[i] >= 0 &&
488*2f2405bbSImre Vadász 				    cpus[i] < ncpus) {
489*2f2405bbSImre Vadász 					cpu = cpus[i];
490*2f2405bbSImre Vadász 				} else {
491*2f2405bbSImre Vadász 					cpu = device_get_unit(dev) + i;
492*2f2405bbSImre Vadász 					cpu %= ncpus;
493*2f2405bbSImre Vadász 				}
494*2f2405bbSImre Vadász 				if (pci_alloc_msix_vector(dev, i, &rid, cpu)
495*2f2405bbSImre Vadász 				    != 0) {
496*2f2405bbSImre Vadász 					if (i > 1 || (i == 1 && !use_config)) {
497*2f2405bbSImre Vadász 						*cnt = i;
498*2f2405bbSImre Vadász 						/* Got some MSI-X vectors. */
499*2f2405bbSImre Vadász 						sc->vtpci_irq_flags = RF_ACTIVE;
500*2f2405bbSImre Vadász 						sc->vtpci_flags |=
501*2f2405bbSImre Vadász 						    VIRTIO_PCI_FLAG_MSIX;
502*2f2405bbSImre Vadász 						goto finish;
503*2f2405bbSImre Vadász 					}
504*2f2405bbSImre Vadász 					/*
505*2f2405bbSImre Vadász 					 * Allocate the legacy IRQ instead.
506*2f2405bbSImre Vadász 					 */
507*2f2405bbSImre Vadász 					if (i == 1) {
508*2f2405bbSImre Vadász 						pci_release_msix_vector(dev, 0);
509*2f2405bbSImre Vadász 					}
510*2f2405bbSImre Vadász 					pci_teardown_msix(dev);
511*2f2405bbSImre Vadász 					break;
512*2f2405bbSImre Vadász 				}
513*2f2405bbSImre Vadász 				sc->vtpci_intr_res[i].rid = rid;
514*2f2405bbSImre Vadász 			}
515*2f2405bbSImre Vadász 			/* Got all the MSI-X vectors we wanted. */
516*2f2405bbSImre Vadász 			sc->vtpci_irq_flags = RF_ACTIVE;
517*2f2405bbSImre Vadász 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
518*2f2405bbSImre Vadász 			/* Successfully allocated all MSI-X vectors */
519*2f2405bbSImre Vadász 			goto finish;
520*2f2405bbSImre Vadász 		}
521*2f2405bbSImre Vadász 	}
522*2f2405bbSImre Vadász 
523*2f2405bbSImre Vadász 	/* Legacy IRQ code: */
524*2f2405bbSImre Vadász 	*cnt = 1;
525*2f2405bbSImre Vadász 	/*
526*2f2405bbSImre Vadász 	 * Use MSI interrupts if available. Otherwise, we fallback
527*2f2405bbSImre Vadász 	 * to legacy interrupts.
528*2f2405bbSImre Vadász 	 */
529*2f2405bbSImre Vadász 	sc->vtpci_intr_res[0].rid = 0;
530*2f2405bbSImre Vadász 	if (pci_alloc_1intr(sc->vtpci_dev, 1,
531*2f2405bbSImre Vadász 	    &sc->vtpci_intr_res[0].rid,
532*2f2405bbSImre Vadász 	    &sc->vtpci_irq_flags) == PCI_INTR_TYPE_MSI) {
533*2f2405bbSImre Vadász 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
534*2f2405bbSImre Vadász 	}
535*2f2405bbSImre Vadász 
536*2f2405bbSImre Vadász finish:
537*2f2405bbSImre Vadász 	KKASSERT(!((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) != 0 &&
538*2f2405bbSImre Vadász 		   (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) != 0));
539*2f2405bbSImre Vadász 
540*2f2405bbSImre Vadász 	sc->vtpci_nintr_res = *cnt;
541*2f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
542*2f2405bbSImre Vadász 		struct resource *irq;
543*2f2405bbSImre Vadász 
544*2f2405bbSImre Vadász 		TAILQ_INIT(&sc->vtpci_intr_res[i].ls);
545*2f2405bbSImre Vadász 		sc->vtpci_intr_res[i].ires_sc = sc;
546*2f2405bbSImre Vadász 		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
547*2f2405bbSImre Vadász 		    &sc->vtpci_intr_res[i].rid, sc->vtpci_irq_flags);
548*2f2405bbSImre Vadász 		if (irq == NULL)
549*2f2405bbSImre Vadász 			return (ENXIO);
550*2f2405bbSImre Vadász 		if (cpus != NULL)
551*2f2405bbSImre Vadász 			cpus[i] = rman_get_cpuid(irq);
552*2f2405bbSImre Vadász 
553*2f2405bbSImre Vadász 		sc->vtpci_intr_res[i].irq = irq;
554*2f2405bbSImre Vadász 	}
555*2f2405bbSImre Vadász 
556*2f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
557*2f2405bbSImre Vadász 		device_printf(dev, "using %d MSI-X vectors\n", *cnt);
558*2f2405bbSImre Vadász 		pci_enable_msix(dev);
559*2f2405bbSImre Vadász 	}
560*2f2405bbSImre Vadász 
561*2f2405bbSImre Vadász 	return (0);
562*2f2405bbSImre Vadász }
563*2f2405bbSImre Vadász 
564*2f2405bbSImre Vadász static int
565*2f2405bbSImre Vadász vtpci_intr_release(device_t dev)
566*2f2405bbSImre Vadász {
567*2f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
568*2f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
569*2f2405bbSImre Vadász 	int i;
570*2f2405bbSImre Vadász 
571*2f2405bbSImre Vadász 	if (sc->vtpci_nintr_res == 0)
572*2f2405bbSImre Vadász 		return (EINVAL);
573*2f2405bbSImre Vadász 
574*2f2405bbSImre Vadász 	/* XXX Make sure none of the interrupts is used at the moment. */
575*2f2405bbSImre Vadász 
576*2f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
577*2f2405bbSImre Vadász 		ires = &sc->vtpci_intr_res[i];
578*2f2405bbSImre Vadász 
579*2f2405bbSImre Vadász 		KKASSERT(TAILQ_EMPTY(&ires->ls));
580*2f2405bbSImre Vadász 		if (ires->irq != NULL) {
581*2f2405bbSImre Vadász 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
582*2f2405bbSImre Vadász 			    ires->irq);
583*2f2405bbSImre Vadász 			ires->irq = NULL;
584*2f2405bbSImre Vadász 		}
585*2f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
586*2f2405bbSImre Vadász 			pci_release_msix_vector(dev, ires->rid);
587*2f2405bbSImre Vadász 		ires->rid = 0;
588*2f2405bbSImre Vadász 	}
589*2f2405bbSImre Vadász 	sc->vtpci_nintr_res = 0;
590*2f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
591*2f2405bbSImre Vadász 		pci_release_msi(dev);
592*2f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
593*2f2405bbSImre Vadász 	}
594*2f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
595*2f2405bbSImre Vadász 		pci_teardown_msix(dev);
596*2f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
597*2f2405bbSImre Vadász 	}
598*2f2405bbSImre Vadász 	return (0);
599*2f2405bbSImre Vadász }
600*2f2405bbSImre Vadász 
601*2f2405bbSImre Vadász static int
60211447b59SVenkatesh Srinivas vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs,
60311447b59SVenkatesh Srinivas     struct vq_alloc_info *vq_info)
60411447b59SVenkatesh Srinivas {
60511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
60611447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
60711447b59SVenkatesh Srinivas 	struct vq_alloc_info *info;
60811447b59SVenkatesh Srinivas 	int queue, error;
60911447b59SVenkatesh Srinivas 	uint16_t vq_size;
61011447b59SVenkatesh Srinivas 
61111447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
61211447b59SVenkatesh Srinivas 
61311447b59SVenkatesh Srinivas 	if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
61411447b59SVenkatesh Srinivas 	    nvqs > VIRTIO_MAX_VIRTQUEUES)
61511447b59SVenkatesh Srinivas 		return (EINVAL);
61611447b59SVenkatesh Srinivas 
61711447b59SVenkatesh Srinivas 	for (queue = 0; queue < nvqs; queue++) {
61811447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
61911447b59SVenkatesh Srinivas 		info = &vq_info[queue];
62011447b59SVenkatesh Srinivas 
621*2f2405bbSImre Vadász 		vqx->ires_idx = -1;
62211447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
62311447b59SVenkatesh Srinivas 
62411447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
62511447b59SVenkatesh Srinivas 		error = virtqueue_alloc(dev, queue, vq_size,
62611447b59SVenkatesh Srinivas 		    VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
62711447b59SVenkatesh Srinivas 		if (error)
62811447b59SVenkatesh Srinivas 			return (error);
62911447b59SVenkatesh Srinivas 
63011447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
63111447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
63211447b59SVenkatesh Srinivas 
63311447b59SVenkatesh Srinivas 		*info->vqai_vq = vqx->vq;
63411447b59SVenkatesh Srinivas 		sc->vtpci_nvqs++;
63511447b59SVenkatesh Srinivas 	}
63611447b59SVenkatesh Srinivas 
63711447b59SVenkatesh Srinivas 	return (0);
63811447b59SVenkatesh Srinivas }
63911447b59SVenkatesh Srinivas 
640*2f2405bbSImre Vadász /* XXX Add argument to specify the callback function here. */
64111447b59SVenkatesh Srinivas static int
642*2f2405bbSImre Vadász vtpci_setup_intr(device_t dev, uint irq, lwkt_serialize_t slz)
64311447b59SVenkatesh Srinivas {
64411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
64511447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
646*2f2405bbSImre Vadász 	int flags, error;
64711447b59SVenkatesh Srinivas 
64811447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
64911447b59SVenkatesh Srinivas 	flags = INTR_MPSAFE;
650*2f2405bbSImre Vadász 
651*2f2405bbSImre Vadász 	if ((int)irq >= sc->vtpci_nintr_res)
652*2f2405bbSImre Vadász 		return (EINVAL);
653*2f2405bbSImre Vadász 	ires = &sc->vtpci_intr_res[irq];
65411447b59SVenkatesh Srinivas 
65511447b59SVenkatesh Srinivas 	if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
65611447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
657df2df2fcSImre Vadász 				       vtpci_legacy_intr,
658*2f2405bbSImre Vadász 				       ires, &ires->intrhand, slz);
659*2f2405bbSImre Vadász 	} else {
660*2f2405bbSImre Vadász 		error = bus_setup_intr(dev, ires->irq, flags,
661*2f2405bbSImre Vadász 				       vtpci_msix_intr,
662*2f2405bbSImre Vadász 				       ires, &ires->intrhand, slz);
663*2f2405bbSImre Vadász 	}
66411447b59SVenkatesh Srinivas 	return (error);
66511447b59SVenkatesh Srinivas }
66611447b59SVenkatesh Srinivas 
667*2f2405bbSImre Vadász static int
668*2f2405bbSImre Vadász vtpci_teardown_intr(device_t dev, uint irq)
669*2f2405bbSImre Vadász {
670*2f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
671*2f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
67211447b59SVenkatesh Srinivas 
673*2f2405bbSImre Vadász 	if ((int)irq >= sc->vtpci_nintr_res)
674*2f2405bbSImre Vadász 		return (EINVAL);
67511447b59SVenkatesh Srinivas 
676*2f2405bbSImre Vadász 	ires = &sc->vtpci_intr_res[irq];
677*2f2405bbSImre Vadász 
678*2f2405bbSImre Vadász 	if (ires->intrhand == NULL)
679*2f2405bbSImre Vadász 		return (ENXIO);
680*2f2405bbSImre Vadász 
681*2f2405bbSImre Vadász 	bus_teardown_intr(dev, ires->irq, ires->intrhand);
682*2f2405bbSImre Vadász 	ires->intrhand = NULL;
683*2f2405bbSImre Vadász 	return (0);
68411447b59SVenkatesh Srinivas }
68511447b59SVenkatesh Srinivas 
686*2f2405bbSImre Vadász static void
687*2f2405bbSImre Vadász vtpci_add_irqentry(struct vtpci_intr_resource *intr_res, int what)
688*2f2405bbSImre Vadász {
689*2f2405bbSImre Vadász 	struct vqentry *e;
69011447b59SVenkatesh Srinivas 
691*2f2405bbSImre Vadász 	TAILQ_FOREACH(e, &intr_res->ls, entries) {
692*2f2405bbSImre Vadász 		if (e->what == what)
693*2f2405bbSImre Vadász 			return;
694*2f2405bbSImre Vadász 	}
695*2f2405bbSImre Vadász 	e = kmalloc(sizeof(*e), M_DEVBUF, M_WAITOK);
696*2f2405bbSImre Vadász 	e->what = what;
697*2f2405bbSImre Vadász 	TAILQ_INSERT_TAIL(&intr_res->ls, e, entries);
698*2f2405bbSImre Vadász }
699*2f2405bbSImre Vadász 
700*2f2405bbSImre Vadász static void
701*2f2405bbSImre Vadász vtpci_del_irqentry(struct vtpci_intr_resource *intr_res, int what)
702*2f2405bbSImre Vadász {
703*2f2405bbSImre Vadász 	struct vqentry *e;
704*2f2405bbSImre Vadász 
705*2f2405bbSImre Vadász 	TAILQ_FOREACH(e, &intr_res->ls, entries) {
706*2f2405bbSImre Vadász 		if (e->what == what)
707*2f2405bbSImre Vadász 			break;
708*2f2405bbSImre Vadász 	}
709*2f2405bbSImre Vadász 	if (e != NULL) {
710*2f2405bbSImre Vadász 		TAILQ_REMOVE(&intr_res->ls, e, entries);
711*2f2405bbSImre Vadász 		kfree(e, M_DEVBUF);
712*2f2405bbSImre Vadász 	}
713*2f2405bbSImre Vadász }
714*2f2405bbSImre Vadász 
715*2f2405bbSImre Vadász /*
716*2f2405bbSImre Vadász  * Config intr can be bound after intr_alloc, virtqueue intrs can be bound
717*2f2405bbSImre Vadász  * after intr_alloc and alloc_virtqueues.
718*2f2405bbSImre Vadász  */
719*2f2405bbSImre Vadász static int
720*2f2405bbSImre Vadász vtpci_bind_intr(device_t dev, uint irq, int what)
721*2f2405bbSImre Vadász {
722*2f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
723*2f2405bbSImre Vadász 	struct vtpci_virtqueue *vqx;
724*2f2405bbSImre Vadász 	int error;
725*2f2405bbSImre Vadász 
726*2f2405bbSImre Vadász 	if (irq >= sc->vtpci_nintr_res)
727*2f2405bbSImre Vadász 		return (EINVAL);
728*2f2405bbSImre Vadász 
729*2f2405bbSImre Vadász 	if (what == -1) {
730*2f2405bbSImre Vadász 		if (sc->vtpci_config_irq != -1)
731*2f2405bbSImre Vadász 			return (EINVAL);
732*2f2405bbSImre Vadász 
733*2f2405bbSImre Vadász 		sc->vtpci_config_irq = irq;
734*2f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
735*2f2405bbSImre Vadász 			error = vtpci_register_msix_vector(sc,
736*2f2405bbSImre Vadász 			    VIRTIO_MSI_CONFIG_VECTOR, irq);
73711447b59SVenkatesh Srinivas 			if (error)
73811447b59SVenkatesh Srinivas 				return (error);
73911447b59SVenkatesh Srinivas 		}
740*2f2405bbSImre Vadász 		goto done;
741*2f2405bbSImre Vadász 	}
74211447b59SVenkatesh Srinivas 
743*2f2405bbSImre Vadász 	if (sc->vtpci_nvqs <= what || what < 0)
744*2f2405bbSImre Vadász 		return (EINVAL);
745*2f2405bbSImre Vadász 
746*2f2405bbSImre Vadász 	vqx = &sc->vtpci_vqx[what];
747*2f2405bbSImre Vadász 	if (vqx->ires_idx != -1)
748*2f2405bbSImre Vadász 		return (EINVAL);
749*2f2405bbSImre Vadász 
750*2f2405bbSImre Vadász 	vqx->ires_idx = irq;
751*2f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
752*2f2405bbSImre Vadász 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
753*2f2405bbSImre Vadász 		error = vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR,
754*2f2405bbSImre Vadász 		    irq);
755*2f2405bbSImre Vadász 		if (error)
756*2f2405bbSImre Vadász 			return (error);
757*2f2405bbSImre Vadász 	}
758*2f2405bbSImre Vadász done:
759*2f2405bbSImre Vadász 	vtpci_add_irqentry(&sc->vtpci_intr_res[irq], what);
760*2f2405bbSImre Vadász 	return (0);
761*2f2405bbSImre Vadász }
762*2f2405bbSImre Vadász 
763*2f2405bbSImre Vadász static int
764*2f2405bbSImre Vadász vtpci_unbind_intr(device_t dev, int what)
765*2f2405bbSImre Vadász {
766*2f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
767*2f2405bbSImre Vadász 	struct vtpci_virtqueue *vqx;
768*2f2405bbSImre Vadász 	uint irq;
769*2f2405bbSImre Vadász 
770*2f2405bbSImre Vadász 	if (what == -1) {
771*2f2405bbSImre Vadász 		if (sc->vtpci_config_irq == -1)
772*2f2405bbSImre Vadász 			return (EINVAL);
773*2f2405bbSImre Vadász 
774*2f2405bbSImre Vadász 		irq = sc->vtpci_config_irq;
775*2f2405bbSImre Vadász 		sc->vtpci_config_irq = -1;
776*2f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
777*2f2405bbSImre Vadász 			vtpci_register_msix_vector(sc,
778*2f2405bbSImre Vadász 			    VIRTIO_MSI_CONFIG_VECTOR, -1);
779*2f2405bbSImre Vadász 		}
780*2f2405bbSImre Vadász 		goto done;
781*2f2405bbSImre Vadász 	}
782*2f2405bbSImre Vadász 
783*2f2405bbSImre Vadász 	if (sc->vtpci_nvqs <= what || what < 0)
784*2f2405bbSImre Vadász 		return (EINVAL);
785*2f2405bbSImre Vadász 
786*2f2405bbSImre Vadász 	vqx = &sc->vtpci_vqx[what];
787*2f2405bbSImre Vadász 	if (vqx->ires_idx == -1)
788*2f2405bbSImre Vadász 		return (EINVAL);
789*2f2405bbSImre Vadász 
790*2f2405bbSImre Vadász 	irq = vqx->ires_idx;
791*2f2405bbSImre Vadász 	vqx->ires_idx = -1;
792*2f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
793*2f2405bbSImre Vadász 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
794*2f2405bbSImre Vadász 		vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR, -1);
795*2f2405bbSImre Vadász 	}
796*2f2405bbSImre Vadász done:
797*2f2405bbSImre Vadász 	KKASSERT(irq >= 0 && irq < sc->vtpci_nintr_res);
798*2f2405bbSImre Vadász 	vtpci_del_irqentry(&sc->vtpci_intr_res[irq], what);
79911447b59SVenkatesh Srinivas 	return (0);
80011447b59SVenkatesh Srinivas }
80111447b59SVenkatesh Srinivas 
80211447b59SVenkatesh Srinivas static void
80311447b59SVenkatesh Srinivas vtpci_stop(device_t dev)
80411447b59SVenkatesh Srinivas {
80511447b59SVenkatesh Srinivas 	vtpci_reset(device_get_softc(dev));
80611447b59SVenkatesh Srinivas }
80711447b59SVenkatesh Srinivas 
80811447b59SVenkatesh Srinivas static int
80911447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features)
81011447b59SVenkatesh Srinivas {
81111447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
81211447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
81311447b59SVenkatesh Srinivas 	struct virtqueue *vq;
81411447b59SVenkatesh Srinivas 	int queue, error;
81511447b59SVenkatesh Srinivas 	uint16_t vq_size;
81611447b59SVenkatesh Srinivas 
81711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
81811447b59SVenkatesh Srinivas 
81911447b59SVenkatesh Srinivas 	/*
82011447b59SVenkatesh Srinivas 	 * Redrive the device initialization. This is a bit of an abuse
82111447b59SVenkatesh Srinivas 	 * of the specification, but both VirtualBox and QEMU/KVM seem
82211447b59SVenkatesh Srinivas 	 * to play nice. We do not allow the host device to change from
82311447b59SVenkatesh Srinivas 	 * what was originally negotiated beyond what the guest driver
82411447b59SVenkatesh Srinivas 	 * changed (MSIX state should not change, number of virtqueues
82511447b59SVenkatesh Srinivas 	 * and their size remain the same, etc).
82611447b59SVenkatesh Srinivas 	 */
82711447b59SVenkatesh Srinivas 
82811447b59SVenkatesh Srinivas 	if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
82911447b59SVenkatesh Srinivas 		vtpci_stop(dev);
83011447b59SVenkatesh Srinivas 
83111447b59SVenkatesh Srinivas 	/*
83211447b59SVenkatesh Srinivas 	 * Quickly drive the status through ACK and DRIVER. The device
83311447b59SVenkatesh Srinivas 	 * does not become usable again until vtpci_reinit_complete().
83411447b59SVenkatesh Srinivas 	 */
83511447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
83611447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
83711447b59SVenkatesh Srinivas 
83811447b59SVenkatesh Srinivas 	vtpci_negotiate_features(dev, features);
83911447b59SVenkatesh Srinivas 
84011447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
841c8247d06SImre Vadász 		pci_enable_msix(dev);
84211447b59SVenkatesh Srinivas 		error = vtpci_register_msix_vector(sc,
84311447b59SVenkatesh Srinivas 		    VIRTIO_MSI_CONFIG_VECTOR, 0);
84411447b59SVenkatesh Srinivas 		if (error)
84511447b59SVenkatesh Srinivas 			return (error);
84611447b59SVenkatesh Srinivas 	}
84711447b59SVenkatesh Srinivas 
84811447b59SVenkatesh Srinivas 	for (queue = 0; queue < sc->vtpci_nvqs; queue++) {
84911447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
85011447b59SVenkatesh Srinivas 		vq = vqx->vq;
85111447b59SVenkatesh Srinivas 
85211447b59SVenkatesh Srinivas 		KASSERT(vq != NULL, ("vq %d not allocated", queue));
85311447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
85411447b59SVenkatesh Srinivas 
85511447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
85611447b59SVenkatesh Srinivas 		error = virtqueue_reinit(vq, vq_size);
85711447b59SVenkatesh Srinivas 		if (error)
85811447b59SVenkatesh Srinivas 			return (error);
85911447b59SVenkatesh Srinivas 
86011447b59SVenkatesh Srinivas 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
86111447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
86211447b59SVenkatesh Srinivas 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
86311447b59SVenkatesh Srinivas 			if (error)
86411447b59SVenkatesh Srinivas 				return (error);
86511447b59SVenkatesh Srinivas 		}
86611447b59SVenkatesh Srinivas 
86711447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
86811447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
86911447b59SVenkatesh Srinivas 	}
87011447b59SVenkatesh Srinivas 
87111447b59SVenkatesh Srinivas 	return (0);
87211447b59SVenkatesh Srinivas }
87311447b59SVenkatesh Srinivas 
87411447b59SVenkatesh Srinivas static void
87511447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev)
87611447b59SVenkatesh Srinivas {
87711447b59SVenkatesh Srinivas 
87811447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
87911447b59SVenkatesh Srinivas }
88011447b59SVenkatesh Srinivas 
88111447b59SVenkatesh Srinivas static void
88211447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue)
88311447b59SVenkatesh Srinivas {
88411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
88511447b59SVenkatesh Srinivas 
88611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
88711447b59SVenkatesh Srinivas 
88811447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
88911447b59SVenkatesh Srinivas }
89011447b59SVenkatesh Srinivas 
89111447b59SVenkatesh Srinivas static uint8_t
89211447b59SVenkatesh Srinivas vtpci_get_status(device_t dev)
89311447b59SVenkatesh Srinivas {
89411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
89511447b59SVenkatesh Srinivas 
89611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
89711447b59SVenkatesh Srinivas 
89811447b59SVenkatesh Srinivas 	return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
89911447b59SVenkatesh Srinivas }
90011447b59SVenkatesh Srinivas 
90111447b59SVenkatesh Srinivas static void
90211447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status)
90311447b59SVenkatesh Srinivas {
90411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
90511447b59SVenkatesh Srinivas 
90611447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
90711447b59SVenkatesh Srinivas 
90811447b59SVenkatesh Srinivas 	if (status != VIRTIO_CONFIG_STATUS_RESET)
90911447b59SVenkatesh Srinivas 		status |= vtpci_get_status(dev);
91011447b59SVenkatesh Srinivas 
91111447b59SVenkatesh Srinivas 	vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
91211447b59SVenkatesh Srinivas }
91311447b59SVenkatesh Srinivas 
91411447b59SVenkatesh Srinivas static void
91511447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset,
91611447b59SVenkatesh Srinivas     void *dst, int length)
91711447b59SVenkatesh Srinivas {
91811447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
91911447b59SVenkatesh Srinivas 	bus_size_t off;
92011447b59SVenkatesh Srinivas 	uint8_t *d;
92111447b59SVenkatesh Srinivas 	int size;
92211447b59SVenkatesh Srinivas 
92311447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
92411447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
92511447b59SVenkatesh Srinivas 
92611447b59SVenkatesh Srinivas 	for (d = dst; length > 0; d += size, off += size, length -= size) {
92711447b59SVenkatesh Srinivas 		if (length >= 4) {
92811447b59SVenkatesh Srinivas 			size = 4;
92911447b59SVenkatesh Srinivas 			*(uint32_t *)d = vtpci_read_config_4(sc, off);
93011447b59SVenkatesh Srinivas 		} else if (length >= 2) {
93111447b59SVenkatesh Srinivas 			size = 2;
93211447b59SVenkatesh Srinivas 			*(uint16_t *)d = vtpci_read_config_2(sc, off);
93311447b59SVenkatesh Srinivas 		} else {
93411447b59SVenkatesh Srinivas 			size = 1;
93511447b59SVenkatesh Srinivas 			*d = vtpci_read_config_1(sc, off);
93611447b59SVenkatesh Srinivas 		}
93711447b59SVenkatesh Srinivas 	}
93811447b59SVenkatesh Srinivas }
93911447b59SVenkatesh Srinivas 
94011447b59SVenkatesh Srinivas static void
94111447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset,
94211447b59SVenkatesh Srinivas     void *src, int length)
94311447b59SVenkatesh Srinivas {
94411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
94511447b59SVenkatesh Srinivas 	bus_size_t off;
94611447b59SVenkatesh Srinivas 	uint8_t *s;
94711447b59SVenkatesh Srinivas 	int size;
94811447b59SVenkatesh Srinivas 
94911447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
95011447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
95111447b59SVenkatesh Srinivas 
95211447b59SVenkatesh Srinivas 	for (s = src; length > 0; s += size, off += size, length -= size) {
95311447b59SVenkatesh Srinivas 		if (length >= 4) {
95411447b59SVenkatesh Srinivas 			size = 4;
95511447b59SVenkatesh Srinivas 			vtpci_write_config_4(sc, off, *(uint32_t *)s);
95611447b59SVenkatesh Srinivas 		} else if (length >= 2) {
95711447b59SVenkatesh Srinivas 			size = 2;
95811447b59SVenkatesh Srinivas 			vtpci_write_config_2(sc, off, *(uint16_t *)s);
95911447b59SVenkatesh Srinivas 		} else {
96011447b59SVenkatesh Srinivas 			size = 1;
96111447b59SVenkatesh Srinivas 			vtpci_write_config_1(sc, off, *s);
96211447b59SVenkatesh Srinivas 		}
96311447b59SVenkatesh Srinivas 	}
96411447b59SVenkatesh Srinivas }
96511447b59SVenkatesh Srinivas 
96611447b59SVenkatesh Srinivas static void
96711447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
96811447b59SVenkatesh Srinivas     uint64_t features)
96911447b59SVenkatesh Srinivas {
97011447b59SVenkatesh Srinivas 	device_t dev, child;
97111447b59SVenkatesh Srinivas 
97211447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
97311447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
97411447b59SVenkatesh Srinivas 
97511447b59SVenkatesh Srinivas 	if (device_is_attached(child) && bootverbose == 0)
97611447b59SVenkatesh Srinivas 		return;
97711447b59SVenkatesh Srinivas 
97811447b59SVenkatesh Srinivas 	virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
97911447b59SVenkatesh Srinivas }
98011447b59SVenkatesh Srinivas 
98111447b59SVenkatesh Srinivas static void
98211447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc)
98311447b59SVenkatesh Srinivas {
98411447b59SVenkatesh Srinivas 	device_t dev, child;
985b17fe9c5SImre Vadasz 	int error;
98611447b59SVenkatesh Srinivas 
98711447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
98811447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
98911447b59SVenkatesh Srinivas 
99011447b59SVenkatesh Srinivas 	if (child == NULL)
99111447b59SVenkatesh Srinivas 		return;
99211447b59SVenkatesh Srinivas 
99311447b59SVenkatesh Srinivas 	if (device_get_state(child) != DS_NOTPRESENT)
99411447b59SVenkatesh Srinivas 		return;
99511447b59SVenkatesh Srinivas 
99611447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
997b17fe9c5SImre Vadasz 	error = device_probe_and_attach(child);
998b17fe9c5SImre Vadasz 	if (error != 0 || device_get_state(child) == DS_NOTPRESENT) {
99911447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
100011447b59SVenkatesh Srinivas 		vtpci_reset(sc);
100111447b59SVenkatesh Srinivas 		vtpci_release_child_resources(sc);
100211447b59SVenkatesh Srinivas 
100311447b59SVenkatesh Srinivas 		/* Reset status for future attempt. */
100411447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
100511447b59SVenkatesh Srinivas 	} else
100611447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
100711447b59SVenkatesh Srinivas }
100811447b59SVenkatesh Srinivas 
100911447b59SVenkatesh Srinivas static int
101011447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
101111447b59SVenkatesh Srinivas {
101211447b59SVenkatesh Srinivas 	device_t dev;
101311447b59SVenkatesh Srinivas 	uint16_t vector;
101411447b59SVenkatesh Srinivas 
101511447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
101611447b59SVenkatesh Srinivas 
101711447b59SVenkatesh Srinivas 	if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
101811447b59SVenkatesh Srinivas 	    offset != VIRTIO_MSI_QUEUE_VECTOR)
101911447b59SVenkatesh Srinivas 		return (EINVAL);
102011447b59SVenkatesh Srinivas 
102111447b59SVenkatesh Srinivas 	if (res_idx != -1) {
102211447b59SVenkatesh Srinivas 		/* Map from rid to host vector. */
1023c8247d06SImre Vadász 		vector = res_idx;
1024c8247d06SImre Vadász 	} else {
102511447b59SVenkatesh Srinivas 		vector = VIRTIO_MSI_NO_VECTOR;
1026c8247d06SImre Vadász 	}
102711447b59SVenkatesh Srinivas 
102811447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, offset, vector);
102911447b59SVenkatesh Srinivas 
103011447b59SVenkatesh Srinivas 	if (vtpci_read_config_2(sc, offset) != vector) {
103111447b59SVenkatesh Srinivas 		device_printf(dev, "insufficient host resources for "
103211447b59SVenkatesh Srinivas 		    "MSIX interrupts\n");
103311447b59SVenkatesh Srinivas 		return (ENODEV);
103411447b59SVenkatesh Srinivas 	}
103511447b59SVenkatesh Srinivas 
103611447b59SVenkatesh Srinivas 	return (0);
103711447b59SVenkatesh Srinivas }
103811447b59SVenkatesh Srinivas 
103911447b59SVenkatesh Srinivas static void
104011447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc)
104111447b59SVenkatesh Srinivas {
1042c8247d06SImre Vadász 	device_t dev = sc->vtpci_dev;
104311447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
104411447b59SVenkatesh Srinivas 	int i;
104511447b59SVenkatesh Srinivas 
1046c8247d06SImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
104711447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[i];
104811447b59SVenkatesh Srinivas 
104911447b59SVenkatesh Srinivas 		if (ires->intrhand != NULL) {
105011447b59SVenkatesh Srinivas 			bus_teardown_intr(dev, ires->irq, ires->intrhand);
105111447b59SVenkatesh Srinivas 			ires->intrhand = NULL;
105211447b59SVenkatesh Srinivas 		}
105311447b59SVenkatesh Srinivas 		if (ires->irq != NULL) {
105411447b59SVenkatesh Srinivas 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
105511447b59SVenkatesh Srinivas 			    ires->irq);
105611447b59SVenkatesh Srinivas 			ires->irq = NULL;
105711447b59SVenkatesh Srinivas 		}
1058*2f2405bbSImre Vadász 	}
1059*2f2405bbSImre Vadász 
1060*2f2405bbSImre Vadász 	vtpci_unbind_intr(sc->vtpci_dev, -1);
1061*2f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nvqs; i++)
1062*2f2405bbSImre Vadász 		vtpci_unbind_intr(sc->vtpci_dev, i);
1063*2f2405bbSImre Vadász 
1064*2f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
1065*2f2405bbSImre Vadász 		ires = &sc->vtpci_intr_res[i];
1066*2f2405bbSImre Vadász 
1067c8247d06SImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
1068c8247d06SImre Vadász 			pci_release_msix_vector(dev, ires->rid);
1069c8247d06SImre Vadász 		ires->rid = 0;
107011447b59SVenkatesh Srinivas 	}
1071c8247d06SImre Vadász 	sc->vtpci_nintr_res = 0;
1072c8247d06SImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
1073c8247d06SImre Vadász 		pci_release_msi(dev);
1074c8247d06SImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
1075c8247d06SImre Vadász 	}
1076c8247d06SImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
1077c8247d06SImre Vadász 		pci_disable_msix(dev);
1078c8247d06SImre Vadász 		pci_teardown_msix(dev);
1079*2f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
1080c8247d06SImre Vadász 	}
1081c8247d06SImre Vadász 
108211447b59SVenkatesh Srinivas }
108311447b59SVenkatesh Srinivas 
108411447b59SVenkatesh Srinivas static void
108511447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc)
108611447b59SVenkatesh Srinivas {
108711447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
108811447b59SVenkatesh Srinivas 	int i;
108911447b59SVenkatesh Srinivas 
109011447b59SVenkatesh Srinivas 	sc->vtpci_nvqs = 0;
109111447b59SVenkatesh Srinivas 
109211447b59SVenkatesh Srinivas 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
109311447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
109411447b59SVenkatesh Srinivas 
109511447b59SVenkatesh Srinivas 		if (vqx->vq != NULL) {
109611447b59SVenkatesh Srinivas 			virtqueue_free(vqx->vq);
109711447b59SVenkatesh Srinivas 			vqx->vq = NULL;
109811447b59SVenkatesh Srinivas 		}
109911447b59SVenkatesh Srinivas 	}
110011447b59SVenkatesh Srinivas }
110111447b59SVenkatesh Srinivas 
110211447b59SVenkatesh Srinivas static void
110311447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc)
110411447b59SVenkatesh Srinivas {
110511447b59SVenkatesh Srinivas 	vtpci_free_interrupts(sc);
110611447b59SVenkatesh Srinivas 	vtpci_free_virtqueues(sc);
110711447b59SVenkatesh Srinivas }
110811447b59SVenkatesh Srinivas 
110911447b59SVenkatesh Srinivas static void
111011447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc)
111111447b59SVenkatesh Srinivas {
111211447b59SVenkatesh Srinivas 
111311447b59SVenkatesh Srinivas 	/*
111411447b59SVenkatesh Srinivas 	 * Setting the status to RESET sets the host device to
111511447b59SVenkatesh Srinivas 	 * the original, uninitialized state.
111611447b59SVenkatesh Srinivas 	 */
111711447b59SVenkatesh Srinivas 	vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
111811447b59SVenkatesh Srinivas }
111911447b59SVenkatesh Srinivas 
1120df2df2fcSImre Vadász static void
1121*2f2405bbSImre Vadász vtpci_legacy_intr(void *arg)
112211447b59SVenkatesh Srinivas {
1123*2f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
112411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
1125*2f2405bbSImre Vadász 	struct vqentry *e;
112611447b59SVenkatesh Srinivas 	uint8_t isr;
112711447b59SVenkatesh Srinivas 
1128*2f2405bbSImre Vadász 	ires = arg;
1129*2f2405bbSImre Vadász 	sc = ires->ires_sc;
113011447b59SVenkatesh Srinivas 
113111447b59SVenkatesh Srinivas 	/* Reading the ISR also clears it. */
113211447b59SVenkatesh Srinivas 	isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
113311447b59SVenkatesh Srinivas 
1134*2f2405bbSImre Vadász 	TAILQ_FOREACH(e, &ires->ls, entries) {
1135*2f2405bbSImre Vadász 		if (e->what == -1) {
113611447b59SVenkatesh Srinivas 			if (isr & VIRTIO_PCI_ISR_CONFIG)
113711447b59SVenkatesh Srinivas 				vtpci_config_intr(sc);
1138*2f2405bbSImre Vadász 		} else if (e->what >= 0 && e->what < sc->vtpci_nvqs) {
1139*2f2405bbSImre Vadász 			if (isr & VIRTIO_PCI_ISR_INTR)
1140*2f2405bbSImre Vadász 				virtqueue_intr(sc->vtpci_vqx[e->what].vq);
1141*2f2405bbSImre Vadász 		}
1142c8247d06SImre Vadász 	}
114311447b59SVenkatesh Srinivas }
114411447b59SVenkatesh Srinivas 
1145df2df2fcSImre Vadász static void
1146*2f2405bbSImre Vadász vtpci_msix_intr(void *arg)
114711447b59SVenkatesh Srinivas {
1148*2f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
114911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
1150*2f2405bbSImre Vadász 	struct vqentry *e;
115111447b59SVenkatesh Srinivas 
1152*2f2405bbSImre Vadász 	ires = arg;
1153*2f2405bbSImre Vadász 	sc = ires->ires_sc;
1154*2f2405bbSImre Vadász 	TAILQ_FOREACH(e, &ires->ls, entries) {
1155*2f2405bbSImre Vadász 		if (e->what == -1) {
1156*2f2405bbSImre Vadász 			vtpci_config_intr(sc);
1157*2f2405bbSImre Vadász 		} else if (e->what >= 0 && e->what < sc->vtpci_nvqs) {
1158*2f2405bbSImre Vadász 			virtqueue_intr(sc->vtpci_vqx[e->what].vq);
115911447b59SVenkatesh Srinivas 		}
1160*2f2405bbSImre Vadász 	}
116111447b59SVenkatesh Srinivas }
116211447b59SVenkatesh Srinivas 
1163df2df2fcSImre Vadász static void
116411447b59SVenkatesh Srinivas vtpci_config_intr(void *xsc)
116511447b59SVenkatesh Srinivas {
116611447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
116711447b59SVenkatesh Srinivas 	device_t child;
116811447b59SVenkatesh Srinivas 
116911447b59SVenkatesh Srinivas 	sc = xsc;
117011447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
117111447b59SVenkatesh Srinivas 
117211447b59SVenkatesh Srinivas 	if (child != NULL)
1173df2df2fcSImre Vadász 		VIRTIO_CONFIG_CHANGE(child);
117411447b59SVenkatesh Srinivas }
1175