xref: /dflybsd-src/sys/dev/virtual/virtio/pci/virtio_pci.c (revision 92d9e66d550e56fad63067121682475b11638f09)
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);
128099c4d8eSImre Vadász static int	vtpci_alloc_virtqueues(device_t, int, struct vq_alloc_info *);
1292f2405bbSImre Vadász static int	vtpci_setup_intr(device_t, uint irq, lwkt_serialize_t);
1302f2405bbSImre Vadász static int	vtpci_teardown_intr(device_t, uint irq);
1319d96478cSImre Vadász static int	vtpci_bind_intr(device_t, uint, int, driver_intr_t, void *);
1322f2405bbSImre Vadász static int	vtpci_unbind_intr(device_t, int);
13311447b59SVenkatesh Srinivas static void	vtpci_stop(device_t);
13411447b59SVenkatesh Srinivas static int	vtpci_reinit(device_t, uint64_t);
13511447b59SVenkatesh Srinivas static void	vtpci_reinit_complete(device_t);
13611447b59SVenkatesh Srinivas static void	vtpci_notify_virtqueue(device_t, uint16_t);
13711447b59SVenkatesh Srinivas static uint8_t	vtpci_get_status(device_t);
13811447b59SVenkatesh Srinivas static void	vtpci_set_status(device_t, uint8_t);
13911447b59SVenkatesh Srinivas static void	vtpci_read_dev_config(device_t, bus_size_t, void *, int);
14011447b59SVenkatesh Srinivas static void	vtpci_write_dev_config(device_t, bus_size_t, void *, int);
14111447b59SVenkatesh Srinivas 
14211447b59SVenkatesh Srinivas static void	vtpci_describe_features(struct vtpci_softc *, const char *,
14311447b59SVenkatesh Srinivas 		    uint64_t);
14411447b59SVenkatesh Srinivas static void	vtpci_probe_and_attach_child(struct vtpci_softc *);
14511447b59SVenkatesh Srinivas 
14611447b59SVenkatesh Srinivas static int	vtpci_register_msix_vector(struct vtpci_softc *, int, int);
14711447b59SVenkatesh Srinivas 
14811447b59SVenkatesh Srinivas static void	vtpci_free_interrupts(struct vtpci_softc *);
14911447b59SVenkatesh Srinivas static void	vtpci_free_virtqueues(struct vtpci_softc *);
15011447b59SVenkatesh Srinivas static void	vtpci_release_child_resources(struct vtpci_softc *);
15111447b59SVenkatesh Srinivas static void	vtpci_reset(struct vtpci_softc *);
15211447b59SVenkatesh Srinivas 
153df2df2fcSImre Vadász static void	vtpci_legacy_intr(void *);
1542f2405bbSImre Vadász static void	vtpci_msix_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),
1882f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_count,	  vtpci_intr_count),
1892f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_intr_alloc,	  vtpci_intr_alloc),
1902f2405bbSImre 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),
1932f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_teardown_intr,	  vtpci_teardown_intr),
1942f2405bbSImre Vadász 	DEVMETHOD(virtio_bus_bind_intr,		  vtpci_bind_intr),
1952f2405bbSImre 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
vtpci_probe(device_t dev)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
vtpci_attach(device_t dev)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;
2542f2405bbSImre 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
vtpci_detach(device_t dev)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
vtpci_suspend(device_t dev)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
vtpci_resume(device_t dev)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
vtpci_shutdown(device_t dev)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
vtpci_driver_added(device_t dev,driver_t * driver)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
vtpci_child_detached(device_t dev,device_t child)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
vtpci_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)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
vtpci_write_ivar(device_t dev,device_t child,int index,uintptr_t value)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
vtpci_negotiate_features(device_t dev,uint64_t child_features)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
vtpci_with_feature(device_t dev,uint64_t feature)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
vtpci_intr_count(device_t dev)4492f2405bbSImre Vadász vtpci_intr_count(device_t dev)
4502f2405bbSImre Vadász {
4512f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
4522f2405bbSImre Vadász 
4532f2405bbSImre Vadász 	if (vtpci_disable_msix != 0 || sc->vtpci_msix_res == NULL)
4542f2405bbSImre Vadász 		return 1;
4552f2405bbSImre Vadász 	else
4562f2405bbSImre Vadász 		return pci_msix_count(dev);
4572f2405bbSImre Vadász }
4582f2405bbSImre Vadász 
4592f2405bbSImre Vadász /* Will never return 0, with *cnt <= 0. */
4602f2405bbSImre Vadász static int
vtpci_intr_alloc(device_t dev,int * cnt,int use_config,int * cpus)4612f2405bbSImre Vadász vtpci_intr_alloc(device_t dev, int *cnt, int use_config, int *cpus)
4622f2405bbSImre Vadász {
4632f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
4642f2405bbSImre Vadász 	int i;
4652f2405bbSImre Vadász 
4662f2405bbSImre Vadász 	if (sc->vtpci_nintr_res > 0)
4672f2405bbSImre Vadász 		return (EINVAL);
4682f2405bbSImre Vadász 
4692f2405bbSImre Vadász 	if (*cnt <= 0)
4702f2405bbSImre Vadász 		return (EINVAL);
4712f2405bbSImre Vadász 
4722f2405bbSImre Vadász 	if (vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
4732f2405bbSImre Vadász 		int nmsix = pci_msix_count(dev);
4742f2405bbSImre Vadász 		if (nmsix < *cnt)
4752f2405bbSImre Vadász 			*cnt = nmsix;
4762f2405bbSImre Vadász 	}
4772f2405bbSImre Vadász 
4782f2405bbSImre Vadász 	if ((*cnt > 1 || use_config == 0) &&
4792f2405bbSImre Vadász 	    vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) {
4802f2405bbSImre Vadász 		if (pci_setup_msix(dev) != 0) {
4812f2405bbSImre Vadász 			device_printf(dev, "pci_setup_msix failed\n");
4822f2405bbSImre Vadász 			/* Just fallthrough to legacy IRQ code instead. */
4832f2405bbSImre Vadász 		} else {
4842f2405bbSImre Vadász 			for (i = 0; i < *cnt; i++) {
4852f2405bbSImre Vadász 				int cpu, rid;
4862f2405bbSImre Vadász 
4872f2405bbSImre Vadász 				if (cpus != NULL && cpus[i] >= 0 &&
4882f2405bbSImre Vadász 				    cpus[i] < ncpus) {
4892f2405bbSImre Vadász 					cpu = cpus[i];
4902f2405bbSImre Vadász 				} else {
4912f2405bbSImre Vadász 					cpu = device_get_unit(dev) + i;
4922f2405bbSImre Vadász 					cpu %= ncpus;
4932f2405bbSImre Vadász 				}
4942f2405bbSImre Vadász 				if (pci_alloc_msix_vector(dev, i, &rid, cpu)
4952f2405bbSImre Vadász 				    != 0) {
4962f2405bbSImre Vadász 					if (i > 1 || (i == 1 && !use_config)) {
4972f2405bbSImre Vadász 						*cnt = i;
4982f2405bbSImre Vadász 						/* Got some MSI-X vectors. */
4992f2405bbSImre Vadász 						sc->vtpci_irq_flags = RF_ACTIVE;
5002f2405bbSImre Vadász 						sc->vtpci_flags |=
5012f2405bbSImre Vadász 						    VIRTIO_PCI_FLAG_MSIX;
5022f2405bbSImre Vadász 						goto finish;
5032f2405bbSImre Vadász 					}
5042f2405bbSImre Vadász 					/*
5052f2405bbSImre Vadász 					 * Allocate the legacy IRQ instead.
5062f2405bbSImre Vadász 					 */
5072f2405bbSImre Vadász 					if (i == 1) {
5082f2405bbSImre Vadász 						pci_release_msix_vector(dev, 0);
5092f2405bbSImre Vadász 					}
5102f2405bbSImre Vadász 					pci_teardown_msix(dev);
5112f2405bbSImre Vadász 					break;
5122f2405bbSImre Vadász 				}
5132f2405bbSImre Vadász 				sc->vtpci_intr_res[i].rid = rid;
5142f2405bbSImre Vadász 			}
5152f2405bbSImre Vadász 			/* Got all the MSI-X vectors we wanted. */
5162f2405bbSImre Vadász 			sc->vtpci_irq_flags = RF_ACTIVE;
5172f2405bbSImre Vadász 			sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX;
5182f2405bbSImre Vadász 			/* Successfully allocated all MSI-X vectors */
5192f2405bbSImre Vadász 			goto finish;
5202f2405bbSImre Vadász 		}
5212f2405bbSImre Vadász 	}
5222f2405bbSImre Vadász 
5232f2405bbSImre Vadász 	/* Legacy IRQ code: */
5242f2405bbSImre Vadász 	*cnt = 1;
5252f2405bbSImre Vadász 	/*
5262f2405bbSImre Vadász 	 * Use MSI interrupts if available. Otherwise, we fallback
5272f2405bbSImre Vadász 	 * to legacy interrupts.
5282f2405bbSImre Vadász 	 */
5292f2405bbSImre Vadász 	sc->vtpci_intr_res[0].rid = 0;
5302f2405bbSImre Vadász 	if (pci_alloc_1intr(sc->vtpci_dev, 1,
5312f2405bbSImre Vadász 	    &sc->vtpci_intr_res[0].rid,
5322f2405bbSImre Vadász 	    &sc->vtpci_irq_flags) == PCI_INTR_TYPE_MSI) {
5332f2405bbSImre Vadász 		sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI;
5342f2405bbSImre Vadász 	}
5352f2405bbSImre Vadász 
5362f2405bbSImre Vadász finish:
5372f2405bbSImre Vadász 	KKASSERT(!((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) != 0 &&
5382f2405bbSImre Vadász 		   (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) != 0));
5392f2405bbSImre Vadász 
5402f2405bbSImre Vadász 	sc->vtpci_nintr_res = *cnt;
5412f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
5422f2405bbSImre Vadász 		struct resource *irq;
5432f2405bbSImre Vadász 
5442f2405bbSImre Vadász 		TAILQ_INIT(&sc->vtpci_intr_res[i].ls);
5452f2405bbSImre Vadász 		sc->vtpci_intr_res[i].ires_sc = sc;
5462f2405bbSImre Vadász 		irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
5472f2405bbSImre Vadász 		    &sc->vtpci_intr_res[i].rid, sc->vtpci_irq_flags);
5482f2405bbSImre Vadász 		if (irq == NULL)
5492f2405bbSImre Vadász 			return (ENXIO);
5502f2405bbSImre Vadász 		if (cpus != NULL)
5512f2405bbSImre Vadász 			cpus[i] = rman_get_cpuid(irq);
5522f2405bbSImre Vadász 
5532f2405bbSImre Vadász 		sc->vtpci_intr_res[i].irq = irq;
5542f2405bbSImre Vadász 	}
5552f2405bbSImre Vadász 
5562f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
5572f2405bbSImre Vadász 		device_printf(dev, "using %d MSI-X vectors\n", *cnt);
5582f2405bbSImre Vadász 		pci_enable_msix(dev);
5592f2405bbSImre Vadász 	}
5602f2405bbSImre Vadász 
5612f2405bbSImre Vadász 	return (0);
5622f2405bbSImre Vadász }
5632f2405bbSImre Vadász 
5642f2405bbSImre Vadász static int
vtpci_intr_release(device_t dev)5652f2405bbSImre Vadász vtpci_intr_release(device_t dev)
5662f2405bbSImre Vadász {
5672f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
5682f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
5692f2405bbSImre Vadász 	int i;
5702f2405bbSImre Vadász 
5712f2405bbSImre Vadász 	if (sc->vtpci_nintr_res == 0)
5722f2405bbSImre Vadász 		return (EINVAL);
5732f2405bbSImre Vadász 
5742f2405bbSImre Vadász 	/* XXX Make sure none of the interrupts is used at the moment. */
5752f2405bbSImre Vadász 
5762f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
5772f2405bbSImre Vadász 		ires = &sc->vtpci_intr_res[i];
5782f2405bbSImre Vadász 
5792f2405bbSImre Vadász 		KKASSERT(TAILQ_EMPTY(&ires->ls));
5802f2405bbSImre Vadász 		if (ires->irq != NULL) {
5812f2405bbSImre Vadász 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
5822f2405bbSImre Vadász 			    ires->irq);
5832f2405bbSImre Vadász 			ires->irq = NULL;
5842f2405bbSImre Vadász 		}
5852f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
5862f2405bbSImre Vadász 			pci_release_msix_vector(dev, ires->rid);
5872f2405bbSImre Vadász 		ires->rid = 0;
5882f2405bbSImre Vadász 	}
5892f2405bbSImre Vadász 	sc->vtpci_nintr_res = 0;
5902f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
5912f2405bbSImre Vadász 		pci_release_msi(dev);
5922f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
5932f2405bbSImre Vadász 	}
5942f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
5952f2405bbSImre Vadász 		pci_teardown_msix(dev);
5962f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
5972f2405bbSImre Vadász 	}
5982f2405bbSImre Vadász 	return (0);
5992f2405bbSImre Vadász }
6002f2405bbSImre Vadász 
6012f2405bbSImre Vadász static int
vtpci_alloc_virtqueues(device_t dev,int nvqs,struct vq_alloc_info * vq_info)602099c4d8eSImre Vadász vtpci_alloc_virtqueues(device_t dev, int nvqs, struct vq_alloc_info *vq_info)
60311447b59SVenkatesh Srinivas {
60411447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
60511447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
60611447b59SVenkatesh Srinivas 	struct vq_alloc_info *info;
60711447b59SVenkatesh Srinivas 	int queue, error;
60811447b59SVenkatesh Srinivas 	uint16_t vq_size;
60911447b59SVenkatesh Srinivas 
61011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
61111447b59SVenkatesh Srinivas 
61211447b59SVenkatesh Srinivas 	if (sc->vtpci_nvqs != 0 || nvqs <= 0 ||
61311447b59SVenkatesh Srinivas 	    nvqs > VIRTIO_MAX_VIRTQUEUES)
61411447b59SVenkatesh Srinivas 		return (EINVAL);
61511447b59SVenkatesh Srinivas 
61611447b59SVenkatesh Srinivas 	for (queue = 0; queue < nvqs; queue++) {
61711447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[queue];
61811447b59SVenkatesh Srinivas 		info = &vq_info[queue];
61911447b59SVenkatesh Srinivas 
6202f2405bbSImre Vadász 		vqx->ires_idx = -1;
62111447b59SVenkatesh Srinivas 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue);
62211447b59SVenkatesh Srinivas 
62311447b59SVenkatesh Srinivas 		vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM);
62411447b59SVenkatesh Srinivas 		error = virtqueue_alloc(dev, queue, vq_size,
62511447b59SVenkatesh Srinivas 		    VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq);
62611447b59SVenkatesh Srinivas 		if (error)
62711447b59SVenkatesh Srinivas 			return (error);
62811447b59SVenkatesh Srinivas 
62911447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
63011447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
63111447b59SVenkatesh Srinivas 
63211447b59SVenkatesh Srinivas 		*info->vqai_vq = vqx->vq;
63311447b59SVenkatesh Srinivas 		sc->vtpci_nvqs++;
63411447b59SVenkatesh Srinivas 	}
63511447b59SVenkatesh Srinivas 
63611447b59SVenkatesh Srinivas 	return (0);
63711447b59SVenkatesh Srinivas }
63811447b59SVenkatesh Srinivas 
6392f2405bbSImre Vadász /* XXX Add argument to specify the callback function here. */
64011447b59SVenkatesh Srinivas static int
vtpci_setup_intr(device_t dev,uint irq,lwkt_serialize_t slz)6412f2405bbSImre Vadász vtpci_setup_intr(device_t dev, uint irq, lwkt_serialize_t slz)
64211447b59SVenkatesh Srinivas {
64311447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
64411447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
6452f2405bbSImre Vadász 	int flags, error;
64611447b59SVenkatesh Srinivas 
64711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
64811447b59SVenkatesh Srinivas 	flags = INTR_MPSAFE;
6492f2405bbSImre Vadász 
6502f2405bbSImre Vadász 	if ((int)irq >= sc->vtpci_nintr_res)
6512f2405bbSImre Vadász 		return (EINVAL);
6522f2405bbSImre Vadász 	ires = &sc->vtpci_intr_res[irq];
65311447b59SVenkatesh Srinivas 
65411447b59SVenkatesh Srinivas 	if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) {
65511447b59SVenkatesh Srinivas 		error = bus_setup_intr(dev, ires->irq, flags,
656df2df2fcSImre Vadász 				       vtpci_legacy_intr,
6572f2405bbSImre Vadász 				       ires, &ires->intrhand, slz);
6582f2405bbSImre Vadász 	} else {
6592f2405bbSImre Vadász 		error = bus_setup_intr(dev, ires->irq, flags,
6602f2405bbSImre Vadász 				       vtpci_msix_intr,
6612f2405bbSImre Vadász 				       ires, &ires->intrhand, slz);
6622f2405bbSImre Vadász 	}
66311447b59SVenkatesh Srinivas 	return (error);
66411447b59SVenkatesh Srinivas }
66511447b59SVenkatesh Srinivas 
6662f2405bbSImre Vadász static int
vtpci_teardown_intr(device_t dev,uint irq)6672f2405bbSImre Vadász vtpci_teardown_intr(device_t dev, uint irq)
6682f2405bbSImre Vadász {
6692f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
6702f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
67111447b59SVenkatesh Srinivas 
6722f2405bbSImre Vadász 	if ((int)irq >= sc->vtpci_nintr_res)
6732f2405bbSImre Vadász 		return (EINVAL);
67411447b59SVenkatesh Srinivas 
6752f2405bbSImre Vadász 	ires = &sc->vtpci_intr_res[irq];
6762f2405bbSImre Vadász 
6772f2405bbSImre Vadász 	if (ires->intrhand == NULL)
6782f2405bbSImre Vadász 		return (ENXIO);
6792f2405bbSImre Vadász 
6802f2405bbSImre Vadász 	bus_teardown_intr(dev, ires->irq, ires->intrhand);
6812f2405bbSImre Vadász 	ires->intrhand = NULL;
6822f2405bbSImre Vadász 	return (0);
68311447b59SVenkatesh Srinivas }
68411447b59SVenkatesh Srinivas 
6852f2405bbSImre Vadász static void
vtpci_add_irqentry(struct vtpci_intr_resource * intr_res,int what,driver_intr_t handler,void * arg)6869d96478cSImre Vadász vtpci_add_irqentry(struct vtpci_intr_resource *intr_res, int what,
6879d96478cSImre Vadász     driver_intr_t handler, void *arg)
6882f2405bbSImre Vadász {
6892f2405bbSImre Vadász 	struct vqentry *e;
69011447b59SVenkatesh Srinivas 
6912f2405bbSImre Vadász 	TAILQ_FOREACH(e, &intr_res->ls, entries) {
6922f2405bbSImre Vadász 		if (e->what == what)
6932f2405bbSImre Vadász 			return;
6942f2405bbSImre Vadász 	}
6959d96478cSImre Vadász 	e = kmalloc(sizeof(*e), M_DEVBUF, M_WAITOK | M_ZERO);
6962f2405bbSImre Vadász 	e->what = what;
6979d96478cSImre Vadász 	if (e->what == -1) {
6989d96478cSImre Vadász 		e->vq = NULL;
6999d96478cSImre Vadász 	} else {
7009d96478cSImre Vadász 		e->vq = intr_res->ires_sc->vtpci_vqx[e->what].vq;
7019d96478cSImre Vadász 	}
7029d96478cSImre Vadász 	e->handler = handler;
7039d96478cSImre Vadász 	e->arg = arg;
7042f2405bbSImre Vadász 	TAILQ_INSERT_TAIL(&intr_res->ls, e, entries);
7052f2405bbSImre Vadász }
7062f2405bbSImre Vadász 
7072f2405bbSImre Vadász static void
vtpci_del_irqentry(struct vtpci_intr_resource * intr_res,int what)7082f2405bbSImre Vadász vtpci_del_irqentry(struct vtpci_intr_resource *intr_res, int what)
7092f2405bbSImre Vadász {
7102f2405bbSImre Vadász 	struct vqentry *e;
7112f2405bbSImre Vadász 
7122f2405bbSImre Vadász 	TAILQ_FOREACH(e, &intr_res->ls, entries) {
7132f2405bbSImre Vadász 		if (e->what == what)
7142f2405bbSImre Vadász 			break;
7152f2405bbSImre Vadász 	}
7162f2405bbSImre Vadász 	if (e != NULL) {
7172f2405bbSImre Vadász 		TAILQ_REMOVE(&intr_res->ls, e, entries);
7182f2405bbSImre Vadász 		kfree(e, M_DEVBUF);
7192f2405bbSImre Vadász 	}
7202f2405bbSImre Vadász }
7212f2405bbSImre Vadász 
7222f2405bbSImre Vadász /*
7232f2405bbSImre Vadász  * Config intr can be bound after intr_alloc, virtqueue intrs can be bound
7242f2405bbSImre Vadász  * after intr_alloc and alloc_virtqueues.
7252f2405bbSImre Vadász  */
7262f2405bbSImre Vadász static int
vtpci_bind_intr(device_t dev,uint irq,int what,driver_intr_t handler,void * arg)7279d96478cSImre Vadász vtpci_bind_intr(device_t dev, uint irq, int what,
7289d96478cSImre Vadász     driver_intr_t handler, void *arg)
7292f2405bbSImre Vadász {
7302f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
7312f2405bbSImre Vadász 	struct vtpci_virtqueue *vqx;
7322f2405bbSImre Vadász 	int error;
7332f2405bbSImre Vadász 
7342f2405bbSImre Vadász 	if (irq >= sc->vtpci_nintr_res)
7352f2405bbSImre Vadász 		return (EINVAL);
7362f2405bbSImre Vadász 
7372f2405bbSImre Vadász 	if (what == -1) {
7382f2405bbSImre Vadász 		if (sc->vtpci_config_irq != -1)
7392f2405bbSImre Vadász 			return (EINVAL);
7402f2405bbSImre Vadász 
7412f2405bbSImre Vadász 		sc->vtpci_config_irq = irq;
7422f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7432f2405bbSImre Vadász 			error = vtpci_register_msix_vector(sc,
7442f2405bbSImre Vadász 			    VIRTIO_MSI_CONFIG_VECTOR, irq);
74511447b59SVenkatesh Srinivas 			if (error)
74611447b59SVenkatesh Srinivas 				return (error);
74711447b59SVenkatesh Srinivas 		}
7482f2405bbSImre Vadász 		goto done;
7492f2405bbSImre Vadász 	}
75011447b59SVenkatesh Srinivas 
7512f2405bbSImre Vadász 	if (sc->vtpci_nvqs <= what || what < 0)
7522f2405bbSImre Vadász 		return (EINVAL);
7532f2405bbSImre Vadász 
7542f2405bbSImre Vadász 	vqx = &sc->vtpci_vqx[what];
7552f2405bbSImre Vadász 	if (vqx->ires_idx != -1)
7562f2405bbSImre Vadász 		return (EINVAL);
7572f2405bbSImre Vadász 
7582f2405bbSImre Vadász 	vqx->ires_idx = irq;
7592f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7602f2405bbSImre Vadász 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
7612f2405bbSImre Vadász 		error = vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR,
7622f2405bbSImre Vadász 		    irq);
7632f2405bbSImre Vadász 		if (error)
7642f2405bbSImre Vadász 			return (error);
7652f2405bbSImre Vadász 	}
7662f2405bbSImre Vadász done:
7679d96478cSImre Vadász 	vtpci_add_irqentry(&sc->vtpci_intr_res[irq], what, handler, arg);
7682f2405bbSImre Vadász 	return (0);
7692f2405bbSImre Vadász }
7702f2405bbSImre Vadász 
7712f2405bbSImre Vadász static int
vtpci_unbind_intr(device_t dev,int what)7722f2405bbSImre Vadász vtpci_unbind_intr(device_t dev, int what)
7732f2405bbSImre Vadász {
7742f2405bbSImre Vadász 	struct vtpci_softc *sc = device_get_softc(dev);
7752f2405bbSImre Vadász 	struct vtpci_virtqueue *vqx;
7762f2405bbSImre Vadász 	uint irq;
7772f2405bbSImre Vadász 
7782f2405bbSImre Vadász 	if (what == -1) {
7792f2405bbSImre Vadász 		if (sc->vtpci_config_irq == -1)
7802f2405bbSImre Vadász 			return (EINVAL);
7812f2405bbSImre Vadász 
7822f2405bbSImre Vadász 		irq = sc->vtpci_config_irq;
7832f2405bbSImre Vadász 		sc->vtpci_config_irq = -1;
7842f2405bbSImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
7852f2405bbSImre Vadász 			vtpci_register_msix_vector(sc,
7862f2405bbSImre Vadász 			    VIRTIO_MSI_CONFIG_VECTOR, -1);
7872f2405bbSImre Vadász 		}
7882f2405bbSImre Vadász 		goto done;
7892f2405bbSImre Vadász 	}
7902f2405bbSImre Vadász 
7912f2405bbSImre Vadász 	if (sc->vtpci_nvqs <= what || what < 0)
7922f2405bbSImre Vadász 		return (EINVAL);
7932f2405bbSImre Vadász 
7942f2405bbSImre Vadász 	vqx = &sc->vtpci_vqx[what];
7952f2405bbSImre Vadász 	if (vqx->ires_idx == -1)
7962f2405bbSImre Vadász 		return (EINVAL);
7972f2405bbSImre Vadász 
7982f2405bbSImre Vadász 	irq = vqx->ires_idx;
7992f2405bbSImre Vadász 	vqx->ires_idx = -1;
8002f2405bbSImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
8012f2405bbSImre Vadász 		vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what);
8022f2405bbSImre Vadász 		vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR, -1);
8032f2405bbSImre Vadász 	}
8042f2405bbSImre Vadász done:
8052f2405bbSImre Vadász 	KKASSERT(irq >= 0 && irq < sc->vtpci_nintr_res);
8062f2405bbSImre Vadász 	vtpci_del_irqentry(&sc->vtpci_intr_res[irq], what);
80711447b59SVenkatesh Srinivas 	return (0);
80811447b59SVenkatesh Srinivas }
80911447b59SVenkatesh Srinivas 
81011447b59SVenkatesh Srinivas static void
vtpci_stop(device_t dev)81111447b59SVenkatesh Srinivas vtpci_stop(device_t dev)
81211447b59SVenkatesh Srinivas {
81311447b59SVenkatesh Srinivas 	vtpci_reset(device_get_softc(dev));
81411447b59SVenkatesh Srinivas }
81511447b59SVenkatesh Srinivas 
81611447b59SVenkatesh Srinivas static int
vtpci_reinit(device_t dev,uint64_t features)81711447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features)
81811447b59SVenkatesh Srinivas {
81911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
82011447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
82111447b59SVenkatesh Srinivas 	struct virtqueue *vq;
82211447b59SVenkatesh Srinivas 	int queue, error;
82311447b59SVenkatesh Srinivas 	uint16_t vq_size;
82411447b59SVenkatesh Srinivas 
82511447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
82611447b59SVenkatesh Srinivas 
82711447b59SVenkatesh Srinivas 	/*
82811447b59SVenkatesh Srinivas 	 * Redrive the device initialization. This is a bit of an abuse
82911447b59SVenkatesh Srinivas 	 * of the specification, but both VirtualBox and QEMU/KVM seem
83011447b59SVenkatesh Srinivas 	 * to play nice. We do not allow the host device to change from
83111447b59SVenkatesh Srinivas 	 * what was originally negotiated beyond what the guest driver
83211447b59SVenkatesh Srinivas 	 * changed (MSIX state should not change, number of virtqueues
83311447b59SVenkatesh Srinivas 	 * and their size remain the same, etc).
83411447b59SVenkatesh Srinivas 	 */
83511447b59SVenkatesh Srinivas 
83611447b59SVenkatesh Srinivas 	if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET)
83711447b59SVenkatesh Srinivas 		vtpci_stop(dev);
83811447b59SVenkatesh Srinivas 
83911447b59SVenkatesh Srinivas 	/*
84011447b59SVenkatesh Srinivas 	 * Quickly drive the status through ACK and DRIVER. The device
84111447b59SVenkatesh Srinivas 	 * does not become usable again until vtpci_reinit_complete().
84211447b59SVenkatesh Srinivas 	 */
84311447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
84411447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
84511447b59SVenkatesh Srinivas 
84611447b59SVenkatesh Srinivas 	vtpci_negotiate_features(dev, features);
84711447b59SVenkatesh Srinivas 
84811447b59SVenkatesh Srinivas 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
849c8247d06SImre Vadász 		pci_enable_msix(dev);
850*92d9e66dSImre Vadász 		if (sc->vtpci_config_irq != -1) {
85111447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
852*92d9e66dSImre Vadász 			    VIRTIO_MSI_CONFIG_VECTOR, sc->vtpci_config_irq);
85311447b59SVenkatesh Srinivas 			if (error)
85411447b59SVenkatesh Srinivas 				return (error);
85511447b59SVenkatesh Srinivas 		}
856*92d9e66dSImre Vadász 	}
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 
870*92d9e66dSImre Vadász 		if (vqx->ires_idx != -1 &&
871*92d9e66dSImre Vadász 		    (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)) {
87211447b59SVenkatesh Srinivas 			error = vtpci_register_msix_vector(sc,
87311447b59SVenkatesh Srinivas 			    VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx);
87411447b59SVenkatesh Srinivas 			if (error)
87511447b59SVenkatesh Srinivas 				return (error);
87611447b59SVenkatesh Srinivas 		}
87711447b59SVenkatesh Srinivas 
87811447b59SVenkatesh Srinivas 		vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN,
87911447b59SVenkatesh Srinivas 		    virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT);
88011447b59SVenkatesh Srinivas 	}
88111447b59SVenkatesh Srinivas 
88211447b59SVenkatesh Srinivas 	return (0);
88311447b59SVenkatesh Srinivas }
88411447b59SVenkatesh Srinivas 
88511447b59SVenkatesh Srinivas static void
vtpci_reinit_complete(device_t dev)88611447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev)
88711447b59SVenkatesh Srinivas {
88811447b59SVenkatesh Srinivas 
88911447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
89011447b59SVenkatesh Srinivas }
89111447b59SVenkatesh Srinivas 
89211447b59SVenkatesh Srinivas static void
vtpci_notify_virtqueue(device_t dev,uint16_t queue)89311447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue)
89411447b59SVenkatesh Srinivas {
89511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
89611447b59SVenkatesh Srinivas 
89711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
89811447b59SVenkatesh Srinivas 
89911447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue);
90011447b59SVenkatesh Srinivas }
90111447b59SVenkatesh Srinivas 
90211447b59SVenkatesh Srinivas static uint8_t
vtpci_get_status(device_t dev)90311447b59SVenkatesh Srinivas vtpci_get_status(device_t dev)
90411447b59SVenkatesh Srinivas {
90511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
90611447b59SVenkatesh Srinivas 
90711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
90811447b59SVenkatesh Srinivas 
90911447b59SVenkatesh Srinivas 	return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS));
91011447b59SVenkatesh Srinivas }
91111447b59SVenkatesh Srinivas 
91211447b59SVenkatesh Srinivas static void
vtpci_set_status(device_t dev,uint8_t status)91311447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status)
91411447b59SVenkatesh Srinivas {
91511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
91611447b59SVenkatesh Srinivas 
91711447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
91811447b59SVenkatesh Srinivas 
91911447b59SVenkatesh Srinivas 	if (status != VIRTIO_CONFIG_STATUS_RESET)
92011447b59SVenkatesh Srinivas 		status |= vtpci_get_status(dev);
92111447b59SVenkatesh Srinivas 
92211447b59SVenkatesh Srinivas 	vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status);
92311447b59SVenkatesh Srinivas }
92411447b59SVenkatesh Srinivas 
92511447b59SVenkatesh Srinivas static void
vtpci_read_dev_config(device_t dev,bus_size_t offset,void * dst,int length)92611447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset,
92711447b59SVenkatesh Srinivas     void *dst, int length)
92811447b59SVenkatesh Srinivas {
92911447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
93011447b59SVenkatesh Srinivas 	bus_size_t off;
93111447b59SVenkatesh Srinivas 	uint8_t *d;
93211447b59SVenkatesh Srinivas 	int size;
93311447b59SVenkatesh Srinivas 
93411447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
93511447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
93611447b59SVenkatesh Srinivas 
93711447b59SVenkatesh Srinivas 	for (d = dst; length > 0; d += size, off += size, length -= size) {
93811447b59SVenkatesh Srinivas 		if (length >= 4) {
93911447b59SVenkatesh Srinivas 			size = 4;
94011447b59SVenkatesh Srinivas 			*(uint32_t *)d = vtpci_read_config_4(sc, off);
94111447b59SVenkatesh Srinivas 		} else if (length >= 2) {
94211447b59SVenkatesh Srinivas 			size = 2;
94311447b59SVenkatesh Srinivas 			*(uint16_t *)d = vtpci_read_config_2(sc, off);
94411447b59SVenkatesh Srinivas 		} else {
94511447b59SVenkatesh Srinivas 			size = 1;
94611447b59SVenkatesh Srinivas 			*d = vtpci_read_config_1(sc, off);
94711447b59SVenkatesh Srinivas 		}
94811447b59SVenkatesh Srinivas 	}
94911447b59SVenkatesh Srinivas }
95011447b59SVenkatesh Srinivas 
95111447b59SVenkatesh Srinivas static void
vtpci_write_dev_config(device_t dev,bus_size_t offset,void * src,int length)95211447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset,
95311447b59SVenkatesh Srinivas     void *src, int length)
95411447b59SVenkatesh Srinivas {
95511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
95611447b59SVenkatesh Srinivas 	bus_size_t off;
95711447b59SVenkatesh Srinivas 	uint8_t *s;
95811447b59SVenkatesh Srinivas 	int size;
95911447b59SVenkatesh Srinivas 
96011447b59SVenkatesh Srinivas 	sc = device_get_softc(dev);
96111447b59SVenkatesh Srinivas 	off = VIRTIO_PCI_CONFIG(sc) + offset;
96211447b59SVenkatesh Srinivas 
96311447b59SVenkatesh Srinivas 	for (s = src; length > 0; s += size, off += size, length -= size) {
96411447b59SVenkatesh Srinivas 		if (length >= 4) {
96511447b59SVenkatesh Srinivas 			size = 4;
96611447b59SVenkatesh Srinivas 			vtpci_write_config_4(sc, off, *(uint32_t *)s);
96711447b59SVenkatesh Srinivas 		} else if (length >= 2) {
96811447b59SVenkatesh Srinivas 			size = 2;
96911447b59SVenkatesh Srinivas 			vtpci_write_config_2(sc, off, *(uint16_t *)s);
97011447b59SVenkatesh Srinivas 		} else {
97111447b59SVenkatesh Srinivas 			size = 1;
97211447b59SVenkatesh Srinivas 			vtpci_write_config_1(sc, off, *s);
97311447b59SVenkatesh Srinivas 		}
97411447b59SVenkatesh Srinivas 	}
97511447b59SVenkatesh Srinivas }
97611447b59SVenkatesh Srinivas 
97711447b59SVenkatesh Srinivas static void
vtpci_describe_features(struct vtpci_softc * sc,const char * msg,uint64_t features)97811447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg,
97911447b59SVenkatesh Srinivas     uint64_t features)
98011447b59SVenkatesh Srinivas {
98111447b59SVenkatesh Srinivas 	device_t dev, child;
98211447b59SVenkatesh Srinivas 
98311447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
98411447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
98511447b59SVenkatesh Srinivas 
98611447b59SVenkatesh Srinivas 	if (device_is_attached(child) && bootverbose == 0)
98711447b59SVenkatesh Srinivas 		return;
98811447b59SVenkatesh Srinivas 
98911447b59SVenkatesh Srinivas 	virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc);
99011447b59SVenkatesh Srinivas }
99111447b59SVenkatesh Srinivas 
99211447b59SVenkatesh Srinivas static void
vtpci_probe_and_attach_child(struct vtpci_softc * sc)99311447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc)
99411447b59SVenkatesh Srinivas {
99511447b59SVenkatesh Srinivas 	device_t dev, child;
996b17fe9c5SImre Vadasz 	int error;
99711447b59SVenkatesh Srinivas 
99811447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
99911447b59SVenkatesh Srinivas 	child = sc->vtpci_child_dev;
100011447b59SVenkatesh Srinivas 
100111447b59SVenkatesh Srinivas 	if (child == NULL)
100211447b59SVenkatesh Srinivas 		return;
100311447b59SVenkatesh Srinivas 
100411447b59SVenkatesh Srinivas 	if (device_get_state(child) != DS_NOTPRESENT)
100511447b59SVenkatesh Srinivas 		return;
100611447b59SVenkatesh Srinivas 
100711447b59SVenkatesh Srinivas 	vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER);
1008b17fe9c5SImre Vadasz 	error = device_probe_and_attach(child);
1009b17fe9c5SImre Vadasz 	if (error != 0 || device_get_state(child) == DS_NOTPRESENT) {
101011447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED);
101111447b59SVenkatesh Srinivas 		vtpci_reset(sc);
101211447b59SVenkatesh Srinivas 		vtpci_release_child_resources(sc);
101311447b59SVenkatesh Srinivas 
101411447b59SVenkatesh Srinivas 		/* Reset status for future attempt. */
101511447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK);
101611447b59SVenkatesh Srinivas 	} else
101711447b59SVenkatesh Srinivas 		vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK);
101811447b59SVenkatesh Srinivas }
101911447b59SVenkatesh Srinivas 
102011447b59SVenkatesh Srinivas static int
vtpci_register_msix_vector(struct vtpci_softc * sc,int offset,int res_idx)102111447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx)
102211447b59SVenkatesh Srinivas {
102311447b59SVenkatesh Srinivas 	device_t dev;
102411447b59SVenkatesh Srinivas 	uint16_t vector;
102511447b59SVenkatesh Srinivas 
102611447b59SVenkatesh Srinivas 	dev = sc->vtpci_dev;
102711447b59SVenkatesh Srinivas 
102811447b59SVenkatesh Srinivas 	if (offset != VIRTIO_MSI_CONFIG_VECTOR &&
102911447b59SVenkatesh Srinivas 	    offset != VIRTIO_MSI_QUEUE_VECTOR)
103011447b59SVenkatesh Srinivas 		return (EINVAL);
103111447b59SVenkatesh Srinivas 
103211447b59SVenkatesh Srinivas 	if (res_idx != -1) {
103311447b59SVenkatesh Srinivas 		/* Map from rid to host vector. */
1034c8247d06SImre Vadász 		vector = res_idx;
1035c8247d06SImre Vadász 	} else {
103611447b59SVenkatesh Srinivas 		vector = VIRTIO_MSI_NO_VECTOR;
1037c8247d06SImre Vadász 	}
103811447b59SVenkatesh Srinivas 
103911447b59SVenkatesh Srinivas 	vtpci_write_config_2(sc, offset, vector);
104011447b59SVenkatesh Srinivas 
104111447b59SVenkatesh Srinivas 	if (vtpci_read_config_2(sc, offset) != vector) {
104211447b59SVenkatesh Srinivas 		device_printf(dev, "insufficient host resources for "
104311447b59SVenkatesh Srinivas 		    "MSIX interrupts\n");
104411447b59SVenkatesh Srinivas 		return (ENODEV);
104511447b59SVenkatesh Srinivas 	}
104611447b59SVenkatesh Srinivas 
104711447b59SVenkatesh Srinivas 	return (0);
104811447b59SVenkatesh Srinivas }
104911447b59SVenkatesh Srinivas 
105011447b59SVenkatesh Srinivas static void
vtpci_free_interrupts(struct vtpci_softc * sc)105111447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc)
105211447b59SVenkatesh Srinivas {
1053c8247d06SImre Vadász 	device_t dev = sc->vtpci_dev;
105411447b59SVenkatesh Srinivas 	struct vtpci_intr_resource *ires;
105511447b59SVenkatesh Srinivas 	int i;
105611447b59SVenkatesh Srinivas 
1057c8247d06SImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
105811447b59SVenkatesh Srinivas 		ires = &sc->vtpci_intr_res[i];
105911447b59SVenkatesh Srinivas 
106011447b59SVenkatesh Srinivas 		if (ires->intrhand != NULL) {
106111447b59SVenkatesh Srinivas 			bus_teardown_intr(dev, ires->irq, ires->intrhand);
106211447b59SVenkatesh Srinivas 			ires->intrhand = NULL;
106311447b59SVenkatesh Srinivas 		}
106411447b59SVenkatesh Srinivas 		if (ires->irq != NULL) {
106511447b59SVenkatesh Srinivas 			bus_release_resource(dev, SYS_RES_IRQ, ires->rid,
106611447b59SVenkatesh Srinivas 			    ires->irq);
106711447b59SVenkatesh Srinivas 			ires->irq = NULL;
106811447b59SVenkatesh Srinivas 		}
10692f2405bbSImre Vadász 	}
10702f2405bbSImre Vadász 
10712f2405bbSImre Vadász 	vtpci_unbind_intr(sc->vtpci_dev, -1);
10722f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nvqs; i++)
10732f2405bbSImre Vadász 		vtpci_unbind_intr(sc->vtpci_dev, i);
10742f2405bbSImre Vadász 
10752f2405bbSImre Vadász 	for (i = 0; i < sc->vtpci_nintr_res; i++) {
10762f2405bbSImre Vadász 		ires = &sc->vtpci_intr_res[i];
10772f2405bbSImre Vadász 
1078c8247d06SImre Vadász 		if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX)
1079c8247d06SImre Vadász 			pci_release_msix_vector(dev, ires->rid);
1080c8247d06SImre Vadász 		ires->rid = 0;
108111447b59SVenkatesh Srinivas 	}
1082c8247d06SImre Vadász 	sc->vtpci_nintr_res = 0;
1083c8247d06SImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) {
1084c8247d06SImre Vadász 		pci_release_msi(dev);
1085c8247d06SImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI;
1086c8247d06SImre Vadász 	}
1087c8247d06SImre Vadász 	if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) {
1088c8247d06SImre Vadász 		pci_disable_msix(dev);
1089c8247d06SImre Vadász 		pci_teardown_msix(dev);
10902f2405bbSImre Vadász 		sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX;
1091c8247d06SImre Vadász 	}
1092c8247d06SImre Vadász 
109311447b59SVenkatesh Srinivas }
109411447b59SVenkatesh Srinivas 
109511447b59SVenkatesh Srinivas static void
vtpci_free_virtqueues(struct vtpci_softc * sc)109611447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc)
109711447b59SVenkatesh Srinivas {
109811447b59SVenkatesh Srinivas 	struct vtpci_virtqueue *vqx;
109911447b59SVenkatesh Srinivas 	int i;
110011447b59SVenkatesh Srinivas 
110111447b59SVenkatesh Srinivas 	sc->vtpci_nvqs = 0;
110211447b59SVenkatesh Srinivas 
110311447b59SVenkatesh Srinivas 	for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) {
110411447b59SVenkatesh Srinivas 		vqx = &sc->vtpci_vqx[i];
110511447b59SVenkatesh Srinivas 
110611447b59SVenkatesh Srinivas 		if (vqx->vq != NULL) {
110711447b59SVenkatesh Srinivas 			virtqueue_free(vqx->vq);
110811447b59SVenkatesh Srinivas 			vqx->vq = NULL;
110911447b59SVenkatesh Srinivas 		}
111011447b59SVenkatesh Srinivas 	}
111111447b59SVenkatesh Srinivas }
111211447b59SVenkatesh Srinivas 
111311447b59SVenkatesh Srinivas static void
vtpci_release_child_resources(struct vtpci_softc * sc)111411447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc)
111511447b59SVenkatesh Srinivas {
111611447b59SVenkatesh Srinivas 	vtpci_free_interrupts(sc);
111711447b59SVenkatesh Srinivas 	vtpci_free_virtqueues(sc);
111811447b59SVenkatesh Srinivas }
111911447b59SVenkatesh Srinivas 
112011447b59SVenkatesh Srinivas static void
vtpci_reset(struct vtpci_softc * sc)112111447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc)
112211447b59SVenkatesh Srinivas {
112311447b59SVenkatesh Srinivas 
112411447b59SVenkatesh Srinivas 	/*
112511447b59SVenkatesh Srinivas 	 * Setting the status to RESET sets the host device to
112611447b59SVenkatesh Srinivas 	 * the original, uninitialized state.
112711447b59SVenkatesh Srinivas 	 */
112811447b59SVenkatesh Srinivas 	vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET);
112911447b59SVenkatesh Srinivas }
113011447b59SVenkatesh Srinivas 
1131df2df2fcSImre Vadász static void
vtpci_legacy_intr(void * arg)11322f2405bbSImre Vadász vtpci_legacy_intr(void *arg)
113311447b59SVenkatesh Srinivas {
11342f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
113511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
11362f2405bbSImre Vadász 	struct vqentry *e;
113711447b59SVenkatesh Srinivas 	uint8_t isr;
113811447b59SVenkatesh Srinivas 
11392f2405bbSImre Vadász 	ires = arg;
11402f2405bbSImre Vadász 	sc = ires->ires_sc;
114111447b59SVenkatesh Srinivas 
114211447b59SVenkatesh Srinivas 	/* Reading the ISR also clears it. */
114311447b59SVenkatesh Srinivas 	isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR);
114411447b59SVenkatesh Srinivas 
11452f2405bbSImre Vadász 	TAILQ_FOREACH(e, &ires->ls, entries) {
1146f852cf82SImre Vadász 		/*
1147f852cf82SImre Vadász 		 * The lwkt_serialize_handler_call API doesn't seem to fit
1148f852cf82SImre Vadász 		 * properly here. Instead move the virtqueue pending check
1149f852cf82SImre Vadász 		 * into the driver, who can then properly implement masking
1150f852cf82SImre Vadász 		 * of the handler itself.
1151f852cf82SImre Vadász 		 */
11522f2405bbSImre Vadász 		if (e->what == -1) {
115311447b59SVenkatesh Srinivas 			if (isr & VIRTIO_PCI_ISR_CONFIG)
1154b817dce2SImre Vadász 				e->handler(e->arg);
1155f852cf82SImre Vadász 		} else if (isr & VIRTIO_PCI_ISR_INTR) {
11569d96478cSImre Vadász 			e->handler(e->arg);
11572f2405bbSImre Vadász 		}
1158c8247d06SImre Vadász 	}
115911447b59SVenkatesh Srinivas }
116011447b59SVenkatesh Srinivas 
1161df2df2fcSImre Vadász static void
vtpci_msix_intr(void * arg)11622f2405bbSImre Vadász vtpci_msix_intr(void *arg)
116311447b59SVenkatesh Srinivas {
11642f2405bbSImre Vadász 	struct vtpci_intr_resource *ires;
116511447b59SVenkatesh Srinivas 	struct vtpci_softc *sc;
11662f2405bbSImre Vadász 	struct vqentry *e;
116711447b59SVenkatesh Srinivas 
11682f2405bbSImre Vadász 	ires = arg;
11692f2405bbSImre Vadász 	sc = ires->ires_sc;
11702f2405bbSImre Vadász 	TAILQ_FOREACH(e, &ires->ls, entries) {
1171f852cf82SImre Vadász 		/*
1172f852cf82SImre Vadász 		 * The lwkt_serialize_handler_call API doesn't seem to fit
1173f852cf82SImre Vadász 		 * properly here. Instead move the virtqueue pending check
1174f852cf82SImre Vadász 		 * into the driver, who can then properly implement masking
1175f852cf82SImre Vadász 		 * of the handler itself.
1176f852cf82SImre Vadász 		 */
1177b817dce2SImre Vadász 		e->handler(e->arg);
11782f2405bbSImre Vadász 	}
117911447b59SVenkatesh Srinivas }
1180