xref: /freebsd/sys/dev/virtio/pci/virtio_pci_modern.c (revision fdafd315)
19da9560cSBryan Venteicher /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
39da9560cSBryan Venteicher  *
49da9560cSBryan Venteicher  * Copyright (c) 2017, Bryan Venteicher <bryanv@FreeBSD.org>
59da9560cSBryan Venteicher  * All rights reserved.
69da9560cSBryan Venteicher  *
79da9560cSBryan Venteicher  * Redistribution and use in source and binary forms, with or without
89da9560cSBryan Venteicher  * modification, are permitted provided that the following conditions
99da9560cSBryan Venteicher  * are met:
109da9560cSBryan Venteicher  * 1. Redistributions of source code must retain the above copyright
119da9560cSBryan Venteicher  *    notice unmodified, this list of conditions, and the following
129da9560cSBryan Venteicher  *    disclaimer.
139da9560cSBryan Venteicher  * 2. Redistributions in binary form must reproduce the above copyright
149da9560cSBryan Venteicher  *    notice, this list of conditions and the following disclaimer in the
159da9560cSBryan Venteicher  *    documentation and/or other materials provided with the distribution.
169da9560cSBryan Venteicher  *
179da9560cSBryan Venteicher  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
189da9560cSBryan Venteicher  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
199da9560cSBryan Venteicher  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
209da9560cSBryan Venteicher  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
219da9560cSBryan Venteicher  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
229da9560cSBryan Venteicher  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
239da9560cSBryan Venteicher  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
249da9560cSBryan Venteicher  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
259da9560cSBryan Venteicher  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
269da9560cSBryan Venteicher  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
279da9560cSBryan Venteicher  */
289da9560cSBryan Venteicher 
299da9560cSBryan Venteicher /* Driver for the modern VirtIO PCI interface. */
309da9560cSBryan Venteicher 
319da9560cSBryan Venteicher #include <sys/param.h>
329da9560cSBryan Venteicher #include <sys/systm.h>
339da9560cSBryan Venteicher #include <sys/bus.h>
349da9560cSBryan Venteicher #include <sys/lock.h>
359da9560cSBryan Venteicher #include <sys/kernel.h>
369da9560cSBryan Venteicher #include <sys/module.h>
379da9560cSBryan Venteicher 
389da9560cSBryan Venteicher #include <machine/bus.h>
399da9560cSBryan Venteicher #include <machine/cpu.h>
409da9560cSBryan Venteicher #include <machine/resource.h>
419da9560cSBryan Venteicher #include <sys/bus.h>
429da9560cSBryan Venteicher #include <sys/rman.h>
439da9560cSBryan Venteicher 
449da9560cSBryan Venteicher #include <dev/pci/pcivar.h>
459da9560cSBryan Venteicher #include <dev/pci/pcireg.h>
469da9560cSBryan Venteicher 
479da9560cSBryan Venteicher #include <dev/virtio/virtio.h>
489da9560cSBryan Venteicher #include <dev/virtio/virtqueue.h>
499da9560cSBryan Venteicher #include <dev/virtio/pci/virtio_pci.h>
509da9560cSBryan Venteicher #include <dev/virtio/pci/virtio_pci_modern_var.h>
519da9560cSBryan Venteicher 
529da9560cSBryan Venteicher #include "virtio_bus_if.h"
539da9560cSBryan Venteicher #include "virtio_pci_if.h"
549da9560cSBryan Venteicher #include "virtio_if.h"
559da9560cSBryan Venteicher 
569da9560cSBryan Venteicher struct vtpci_modern_resource_map {
579da9560cSBryan Venteicher 	struct resource_map	vtrm_map;
589da9560cSBryan Venteicher 	int			vtrm_cap_offset;
599da9560cSBryan Venteicher 	int			vtrm_bar;
609da9560cSBryan Venteicher 	int			vtrm_offset;
619da9560cSBryan Venteicher 	int			vtrm_length;
629da9560cSBryan Venteicher 	int			vtrm_type;	/* SYS_RES_{MEMORY, IOPORT} */
639da9560cSBryan Venteicher };
649da9560cSBryan Venteicher 
659da9560cSBryan Venteicher struct vtpci_modern_bar_resource {
669da9560cSBryan Venteicher 	struct resource		*vtbr_res;
679da9560cSBryan Venteicher 	int			 vtbr_type;
689da9560cSBryan Venteicher };
699da9560cSBryan Venteicher 
709da9560cSBryan Venteicher struct vtpci_modern_softc {
719da9560cSBryan Venteicher 	device_t			 vtpci_dev;
729da9560cSBryan Venteicher 	struct vtpci_common		 vtpci_common;
739da9560cSBryan Venteicher 	uint32_t			 vtpci_notify_offset_multiplier;
749da9560cSBryan Venteicher 	uint16_t			 vtpci_devid;
759da9560cSBryan Venteicher 	int				 vtpci_msix_bar;
769da9560cSBryan Venteicher 	struct resource			*vtpci_msix_res;
779da9560cSBryan Venteicher 
789da9560cSBryan Venteicher 	struct vtpci_modern_resource_map vtpci_common_res_map;
799da9560cSBryan Venteicher 	struct vtpci_modern_resource_map vtpci_notify_res_map;
809da9560cSBryan Venteicher 	struct vtpci_modern_resource_map vtpci_isr_res_map;
819da9560cSBryan Venteicher 	struct vtpci_modern_resource_map vtpci_device_res_map;
829da9560cSBryan Venteicher 
839da9560cSBryan Venteicher #define VTPCI_MODERN_MAX_BARS		6
849da9560cSBryan Venteicher 	struct vtpci_modern_bar_resource vtpci_bar_res[VTPCI_MODERN_MAX_BARS];
859da9560cSBryan Venteicher };
869da9560cSBryan Venteicher 
879da9560cSBryan Venteicher static int	vtpci_modern_probe(device_t);
889da9560cSBryan Venteicher static int	vtpci_modern_attach(device_t);
899da9560cSBryan Venteicher static int	vtpci_modern_detach(device_t);
909da9560cSBryan Venteicher static int	vtpci_modern_suspend(device_t);
919da9560cSBryan Venteicher static int	vtpci_modern_resume(device_t);
929da9560cSBryan Venteicher static int	vtpci_modern_shutdown(device_t);
939da9560cSBryan Venteicher 
949da9560cSBryan Venteicher static void	vtpci_modern_driver_added(device_t, driver_t *);
959da9560cSBryan Venteicher static void	vtpci_modern_child_detached(device_t, device_t);
969da9560cSBryan Venteicher static int	vtpci_modern_read_ivar(device_t, device_t, int, uintptr_t *);
979da9560cSBryan Venteicher static int	vtpci_modern_write_ivar(device_t, device_t, int, uintptr_t);
989da9560cSBryan Venteicher 
999da9560cSBryan Venteicher static uint8_t	vtpci_modern_read_isr(device_t);
1009da9560cSBryan Venteicher static uint16_t	vtpci_modern_get_vq_size(device_t, int);
1019da9560cSBryan Venteicher static bus_size_t vtpci_modern_get_vq_notify_off(device_t, int);
1029da9560cSBryan Venteicher static void	vtpci_modern_set_vq(device_t, struct virtqueue *);
1039da9560cSBryan Venteicher static void	vtpci_modern_disable_vq(device_t, int);
1049da9560cSBryan Venteicher static int	vtpci_modern_register_msix(struct vtpci_modern_softc *, int,
1059da9560cSBryan Venteicher 		    struct vtpci_interrupt *);
1069da9560cSBryan Venteicher static int	vtpci_modern_register_cfg_msix(device_t,
1079da9560cSBryan Venteicher 		    struct vtpci_interrupt *);
1089da9560cSBryan Venteicher static int	vtpci_modern_register_vq_msix(device_t, int idx,
1099da9560cSBryan Venteicher 		    struct vtpci_interrupt *);
1109da9560cSBryan Venteicher 
1119da9560cSBryan Venteicher static uint64_t	vtpci_modern_negotiate_features(device_t, uint64_t);
1129da9560cSBryan Venteicher static int	vtpci_modern_finalize_features(device_t);
113ccb576a8SMina Galić static bool	vtpci_modern_with_feature(device_t, uint64_t);
114180c0240SMina Galić static int	vtpci_modern_alloc_virtqueues(device_t, int,
1159da9560cSBryan Venteicher 		    struct vq_alloc_info *);
1169da9560cSBryan Venteicher static int	vtpci_modern_setup_interrupts(device_t, enum intr_type);
1179da9560cSBryan Venteicher static void	vtpci_modern_stop(device_t);
1189da9560cSBryan Venteicher static int	vtpci_modern_reinit(device_t, uint64_t);
1199da9560cSBryan Venteicher static void	vtpci_modern_reinit_complete(device_t);
1209da9560cSBryan Venteicher static void	vtpci_modern_notify_vq(device_t, uint16_t, bus_size_t);
1219da9560cSBryan Venteicher static int	vtpci_modern_config_generation(device_t);
1229da9560cSBryan Venteicher static void	vtpci_modern_read_dev_config(device_t, bus_size_t, void *, int);
1236c4f9516SAlex Richardson static void	vtpci_modern_write_dev_config(device_t, bus_size_t, const void *, int);
1249da9560cSBryan Venteicher 
1259da9560cSBryan Venteicher static int	vtpci_modern_probe_configs(device_t);
1269da9560cSBryan Venteicher static int	vtpci_modern_find_cap(device_t, uint8_t, int *);
1279da9560cSBryan Venteicher static int	vtpci_modern_map_configs(struct vtpci_modern_softc *);
1289da9560cSBryan Venteicher static void	vtpci_modern_unmap_configs(struct vtpci_modern_softc *);
1299da9560cSBryan Venteicher static int	vtpci_modern_find_cap_resource(struct vtpci_modern_softc *,
1309da9560cSBryan Venteicher 		     uint8_t, int, int, struct vtpci_modern_resource_map *);
1319da9560cSBryan Venteicher static int	vtpci_modern_bar_type(struct vtpci_modern_softc *, int);
1329da9560cSBryan Venteicher static struct resource *vtpci_modern_get_bar_resource(
1339da9560cSBryan Venteicher 		    struct vtpci_modern_softc *, int, int);
1349da9560cSBryan Venteicher static struct resource *vtpci_modern_alloc_bar_resource(
1359da9560cSBryan Venteicher 		    struct vtpci_modern_softc *, int, int);
1369da9560cSBryan Venteicher static void	vtpci_modern_free_bar_resources(struct vtpci_modern_softc *);
1379da9560cSBryan Venteicher static int	vtpci_modern_alloc_resource_map(struct vtpci_modern_softc *,
1389da9560cSBryan Venteicher 		    struct vtpci_modern_resource_map *);
1399da9560cSBryan Venteicher static void	vtpci_modern_free_resource_map(struct vtpci_modern_softc *,
1409da9560cSBryan Venteicher 		    struct vtpci_modern_resource_map *);
1419da9560cSBryan Venteicher static void	vtpci_modern_alloc_msix_resource(struct vtpci_modern_softc *);
1429da9560cSBryan Venteicher static void	vtpci_modern_free_msix_resource(struct vtpci_modern_softc *);
1439da9560cSBryan Venteicher 
1449da9560cSBryan Venteicher static void	vtpci_modern_probe_and_attach_child(struct vtpci_modern_softc *);
1459da9560cSBryan Venteicher 
1469da9560cSBryan Venteicher static uint64_t vtpci_modern_read_features(struct vtpci_modern_softc *);
1479da9560cSBryan Venteicher static void	vtpci_modern_write_features(struct vtpci_modern_softc *,
1489da9560cSBryan Venteicher 		    uint64_t);
1499da9560cSBryan Venteicher static void	vtpci_modern_select_virtqueue(struct vtpci_modern_softc *, int);
1509da9560cSBryan Venteicher static uint8_t	vtpci_modern_get_status(struct vtpci_modern_softc *);
1519da9560cSBryan Venteicher static void	vtpci_modern_set_status(struct vtpci_modern_softc *, uint8_t);
1529da9560cSBryan Venteicher static void	vtpci_modern_reset(struct vtpci_modern_softc *);
1539da9560cSBryan Venteicher static void	vtpci_modern_enable_virtqueues(struct vtpci_modern_softc *);
1549da9560cSBryan Venteicher 
1559da9560cSBryan Venteicher static uint8_t	vtpci_modern_read_common_1(struct vtpci_modern_softc *,
1569da9560cSBryan Venteicher 		    bus_size_t);
1579da9560cSBryan Venteicher static uint16_t vtpci_modern_read_common_2(struct vtpci_modern_softc *,
1589da9560cSBryan Venteicher 		    bus_size_t);
1599da9560cSBryan Venteicher static uint32_t vtpci_modern_read_common_4(struct vtpci_modern_softc *,
1609da9560cSBryan Venteicher 		    bus_size_t);
1619da9560cSBryan Venteicher static void	vtpci_modern_write_common_1(struct vtpci_modern_softc *,
1629da9560cSBryan Venteicher 		     bus_size_t, uint8_t);
1639da9560cSBryan Venteicher static void	vtpci_modern_write_common_2(struct vtpci_modern_softc *,
1649da9560cSBryan Venteicher 		     bus_size_t, uint16_t);
1659da9560cSBryan Venteicher static void	vtpci_modern_write_common_4(struct vtpci_modern_softc *,
1669da9560cSBryan Venteicher 		    bus_size_t, uint32_t);
1679da9560cSBryan Venteicher static void	vtpci_modern_write_common_8(struct vtpci_modern_softc *,
1689da9560cSBryan Venteicher 		    bus_size_t, uint64_t);
1699da9560cSBryan Venteicher static void	vtpci_modern_write_notify_2(struct vtpci_modern_softc *,
1709da9560cSBryan Venteicher 		    bus_size_t, uint16_t);
1719da9560cSBryan Venteicher static uint8_t  vtpci_modern_read_isr_1(struct vtpci_modern_softc *,
1729da9560cSBryan Venteicher 		    bus_size_t);
1739da9560cSBryan Venteicher static uint8_t	vtpci_modern_read_device_1(struct vtpci_modern_softc *,
1749da9560cSBryan Venteicher 		    bus_size_t);
1759da9560cSBryan Venteicher static uint16_t vtpci_modern_read_device_2(struct vtpci_modern_softc *,
1769da9560cSBryan Venteicher 		    bus_size_t);
1779da9560cSBryan Venteicher static uint32_t vtpci_modern_read_device_4(struct vtpci_modern_softc *,
1789da9560cSBryan Venteicher 		    bus_size_t);
1799da9560cSBryan Venteicher static uint64_t vtpci_modern_read_device_8(struct vtpci_modern_softc *,
1809da9560cSBryan Venteicher 		    bus_size_t);
1819da9560cSBryan Venteicher static void	vtpci_modern_write_device_1(struct vtpci_modern_softc *,
1829da9560cSBryan Venteicher 		    bus_size_t, uint8_t);
1839da9560cSBryan Venteicher static void	vtpci_modern_write_device_2(struct vtpci_modern_softc *,
1849da9560cSBryan Venteicher 		    bus_size_t, uint16_t);
1859da9560cSBryan Venteicher static void	vtpci_modern_write_device_4(struct vtpci_modern_softc *,
1869da9560cSBryan Venteicher 		    bus_size_t, uint32_t);
1879da9560cSBryan Venteicher static void	vtpci_modern_write_device_8(struct vtpci_modern_softc *,
1889da9560cSBryan Venteicher 		    bus_size_t, uint64_t);
1899da9560cSBryan Venteicher 
1909da9560cSBryan Venteicher /* Tunables. */
1919da9560cSBryan Venteicher static int vtpci_modern_transitional = 0;
1929da9560cSBryan Venteicher TUNABLE_INT("hw.virtio.pci.transitional", &vtpci_modern_transitional);
1939da9560cSBryan Venteicher 
1949da9560cSBryan Venteicher static device_method_t vtpci_modern_methods[] = {
1959da9560cSBryan Venteicher 	/* Device interface. */
1969da9560cSBryan Venteicher 	DEVMETHOD(device_probe,			vtpci_modern_probe),
1979da9560cSBryan Venteicher 	DEVMETHOD(device_attach,		vtpci_modern_attach),
1989da9560cSBryan Venteicher 	DEVMETHOD(device_detach,		vtpci_modern_detach),
1999da9560cSBryan Venteicher 	DEVMETHOD(device_suspend,		vtpci_modern_suspend),
2009da9560cSBryan Venteicher 	DEVMETHOD(device_resume,		vtpci_modern_resume),
2019da9560cSBryan Venteicher 	DEVMETHOD(device_shutdown,		vtpci_modern_shutdown),
2029da9560cSBryan Venteicher 
2039da9560cSBryan Venteicher 	/* Bus interface. */
2049da9560cSBryan Venteicher 	DEVMETHOD(bus_driver_added,		vtpci_modern_driver_added),
2059da9560cSBryan Venteicher 	DEVMETHOD(bus_child_detached,		vtpci_modern_child_detached),
206ddfc9c4cSWarner Losh 	DEVMETHOD(bus_child_pnpinfo,		virtio_child_pnpinfo),
2079da9560cSBryan Venteicher 	DEVMETHOD(bus_read_ivar,		vtpci_modern_read_ivar),
2089da9560cSBryan Venteicher 	DEVMETHOD(bus_write_ivar,		vtpci_modern_write_ivar),
2099da9560cSBryan Venteicher 
2109da9560cSBryan Venteicher 	/* VirtIO PCI interface. */
2119da9560cSBryan Venteicher 	DEVMETHOD(virtio_pci_read_isr,		 vtpci_modern_read_isr),
2129da9560cSBryan Venteicher 	DEVMETHOD(virtio_pci_get_vq_size,	 vtpci_modern_get_vq_size),
2139da9560cSBryan Venteicher 	DEVMETHOD(virtio_pci_get_vq_notify_off,	 vtpci_modern_get_vq_notify_off),
2149da9560cSBryan Venteicher 	DEVMETHOD(virtio_pci_set_vq,		 vtpci_modern_set_vq),
2159da9560cSBryan Venteicher 	DEVMETHOD(virtio_pci_disable_vq,	 vtpci_modern_disable_vq),
2169da9560cSBryan Venteicher 	DEVMETHOD(virtio_pci_register_cfg_msix,	 vtpci_modern_register_cfg_msix),
2179da9560cSBryan Venteicher 	DEVMETHOD(virtio_pci_register_vq_msix,	 vtpci_modern_register_vq_msix),
2189da9560cSBryan Venteicher 
2199da9560cSBryan Venteicher 	/* VirtIO bus interface. */
2209da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_negotiate_features,  vtpci_modern_negotiate_features),
2219da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_finalize_features,	  vtpci_modern_finalize_features),
2229da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_with_feature,	  vtpci_modern_with_feature),
2239da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_alloc_virtqueues,	  vtpci_modern_alloc_virtqueues),
2249da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_setup_intr,	  vtpci_modern_setup_interrupts),
2259da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_stop,		  vtpci_modern_stop),
2269da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_reinit,		  vtpci_modern_reinit),
2279da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_reinit_complete,	  vtpci_modern_reinit_complete),
2289da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_notify_vq,		  vtpci_modern_notify_vq),
2299da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_config_generation,	  vtpci_modern_config_generation),
2309da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_read_device_config,  vtpci_modern_read_dev_config),
2319da9560cSBryan Venteicher 	DEVMETHOD(virtio_bus_write_device_config, vtpci_modern_write_dev_config),
2329da9560cSBryan Venteicher 
2339da9560cSBryan Venteicher 	DEVMETHOD_END
2349da9560cSBryan Venteicher };
2359da9560cSBryan Venteicher 
2369da9560cSBryan Venteicher static driver_t vtpci_modern_driver = {
2379da9560cSBryan Venteicher 	.name = "virtio_pci",
2389da9560cSBryan Venteicher 	.methods = vtpci_modern_methods,
2399da9560cSBryan Venteicher 	.size = sizeof(struct vtpci_modern_softc)
2409da9560cSBryan Venteicher };
2419da9560cSBryan Venteicher 
2425c4c96d3SJohn Baldwin DRIVER_MODULE(virtio_pci_modern, pci, vtpci_modern_driver, 0, 0);
2439da9560cSBryan Venteicher 
2449da9560cSBryan Venteicher static int
vtpci_modern_probe(device_t dev)2459da9560cSBryan Venteicher vtpci_modern_probe(device_t dev)
2469da9560cSBryan Venteicher {
2479da9560cSBryan Venteicher 	char desc[64];
2489da9560cSBryan Venteicher 	const char *name;
2499da9560cSBryan Venteicher 	uint16_t devid;
2509da9560cSBryan Venteicher 
2519da9560cSBryan Venteicher 	if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID)
2529da9560cSBryan Venteicher 		return (ENXIO);
2539da9560cSBryan Venteicher 
2549da9560cSBryan Venteicher 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN ||
2559da9560cSBryan Venteicher 	    pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MODERN_MAX)
2569da9560cSBryan Venteicher 		return (ENXIO);
2579da9560cSBryan Venteicher 
2589da9560cSBryan Venteicher 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MODERN_MIN) {
2599da9560cSBryan Venteicher 		if (!vtpci_modern_transitional)
2609da9560cSBryan Venteicher 			return (ENXIO);
2619da9560cSBryan Venteicher 		devid = pci_get_subdevice(dev);
2629da9560cSBryan Venteicher 	} else
2639da9560cSBryan Venteicher 		devid = pci_get_device(dev) - VIRTIO_PCI_DEVICEID_MODERN_MIN;
2649da9560cSBryan Venteicher 
2659da9560cSBryan Venteicher 	if (vtpci_modern_probe_configs(dev) != 0)
2669da9560cSBryan Venteicher 		return (ENXIO);
2679da9560cSBryan Venteicher 
2689da9560cSBryan Venteicher 	name = virtio_device_name(devid);
2699da9560cSBryan Venteicher 	if (name == NULL)
2709da9560cSBryan Venteicher 		name = "Unknown";
2719da9560cSBryan Venteicher 
2729da9560cSBryan Venteicher 	snprintf(desc, sizeof(desc), "VirtIO PCI (modern) %s adapter", name);
2739da9560cSBryan Venteicher 	device_set_desc_copy(dev, desc);
2749da9560cSBryan Venteicher 
2759da9560cSBryan Venteicher 	return (BUS_PROBE_DEFAULT);
2769da9560cSBryan Venteicher }
2779da9560cSBryan Venteicher 
2789da9560cSBryan Venteicher static int
vtpci_modern_attach(device_t dev)2799da9560cSBryan Venteicher vtpci_modern_attach(device_t dev)
2809da9560cSBryan Venteicher {
2819da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
2829da9560cSBryan Venteicher 	int error;
2839da9560cSBryan Venteicher 
2849da9560cSBryan Venteicher 	sc = device_get_softc(dev);
2859da9560cSBryan Venteicher 	sc->vtpci_dev = dev;
2869da9560cSBryan Venteicher 	vtpci_init(&sc->vtpci_common, dev, true);
2879da9560cSBryan Venteicher 
2889da9560cSBryan Venteicher 	if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MODERN_MIN)
2899da9560cSBryan Venteicher 		sc->vtpci_devid = pci_get_subdevice(dev);
2909da9560cSBryan Venteicher 	else
2919da9560cSBryan Venteicher 		sc->vtpci_devid = pci_get_device(dev) -
2929da9560cSBryan Venteicher 		    VIRTIO_PCI_DEVICEID_MODERN_MIN;
2939da9560cSBryan Venteicher 
2949da9560cSBryan Venteicher 	error = vtpci_modern_map_configs(sc);
2959da9560cSBryan Venteicher 	if (error) {
2969da9560cSBryan Venteicher 		device_printf(dev, "cannot map configs\n");
2979da9560cSBryan Venteicher 		vtpci_modern_unmap_configs(sc);
2989da9560cSBryan Venteicher 		return (error);
2999da9560cSBryan Venteicher 	}
3009da9560cSBryan Venteicher 
3019da9560cSBryan Venteicher 	vtpci_modern_reset(sc);
3029da9560cSBryan Venteicher 
3039da9560cSBryan Venteicher 	/* Tell the host we've noticed this device. */
3049da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_ACK);
3059da9560cSBryan Venteicher 
3069da9560cSBryan Venteicher 	error = vtpci_add_child(&sc->vtpci_common);
3079da9560cSBryan Venteicher 	if (error)
3089da9560cSBryan Venteicher 		goto fail;
3099da9560cSBryan Venteicher 
3109da9560cSBryan Venteicher 	vtpci_modern_probe_and_attach_child(sc);
3119da9560cSBryan Venteicher 
3129da9560cSBryan Venteicher 	return (0);
3139da9560cSBryan Venteicher 
3149da9560cSBryan Venteicher fail:
3159da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_FAILED);
3169da9560cSBryan Venteicher 	vtpci_modern_detach(dev);
3179da9560cSBryan Venteicher 
3189da9560cSBryan Venteicher 	return (error);
3199da9560cSBryan Venteicher }
3209da9560cSBryan Venteicher 
3219da9560cSBryan Venteicher static int
vtpci_modern_detach(device_t dev)3229da9560cSBryan Venteicher vtpci_modern_detach(device_t dev)
3239da9560cSBryan Venteicher {
3249da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
3259da9560cSBryan Venteicher 	int error;
3269da9560cSBryan Venteicher 
3279da9560cSBryan Venteicher 	sc = device_get_softc(dev);
3289da9560cSBryan Venteicher 
3299da9560cSBryan Venteicher 	error = vtpci_delete_child(&sc->vtpci_common);
3309da9560cSBryan Venteicher 	if (error)
3319da9560cSBryan Venteicher 		return (error);
3329da9560cSBryan Venteicher 
3339da9560cSBryan Venteicher 	vtpci_modern_reset(sc);
3349da9560cSBryan Venteicher 	vtpci_modern_unmap_configs(sc);
3359da9560cSBryan Venteicher 
3369da9560cSBryan Venteicher 	return (0);
3379da9560cSBryan Venteicher }
3389da9560cSBryan Venteicher 
3399da9560cSBryan Venteicher static int
vtpci_modern_suspend(device_t dev)3409da9560cSBryan Venteicher vtpci_modern_suspend(device_t dev)
3419da9560cSBryan Venteicher {
3429da9560cSBryan Venteicher 	return (bus_generic_suspend(dev));
3439da9560cSBryan Venteicher }
3449da9560cSBryan Venteicher 
3459da9560cSBryan Venteicher static int
vtpci_modern_resume(device_t dev)3469da9560cSBryan Venteicher vtpci_modern_resume(device_t dev)
3479da9560cSBryan Venteicher {
3489da9560cSBryan Venteicher 	return (bus_generic_resume(dev));
3499da9560cSBryan Venteicher }
3509da9560cSBryan Venteicher 
3519da9560cSBryan Venteicher static int
vtpci_modern_shutdown(device_t dev)3529da9560cSBryan Venteicher vtpci_modern_shutdown(device_t dev)
3539da9560cSBryan Venteicher {
3549da9560cSBryan Venteicher 	(void) bus_generic_shutdown(dev);
3559da9560cSBryan Venteicher 	/* Forcibly stop the host device. */
3569da9560cSBryan Venteicher 	vtpci_modern_stop(dev);
3579da9560cSBryan Venteicher 
3589da9560cSBryan Venteicher 	return (0);
3599da9560cSBryan Venteicher }
3609da9560cSBryan Venteicher 
3619da9560cSBryan Venteicher static void
vtpci_modern_driver_added(device_t dev,driver_t * driver)3629da9560cSBryan Venteicher vtpci_modern_driver_added(device_t dev, driver_t *driver)
3639da9560cSBryan Venteicher {
3649da9560cSBryan Venteicher 	vtpci_modern_probe_and_attach_child(device_get_softc(dev));
3659da9560cSBryan Venteicher }
3669da9560cSBryan Venteicher 
3679da9560cSBryan Venteicher static void
vtpci_modern_child_detached(device_t dev,device_t child)3689da9560cSBryan Venteicher vtpci_modern_child_detached(device_t dev, device_t child)
3699da9560cSBryan Venteicher {
3709da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
3719da9560cSBryan Venteicher 
3729da9560cSBryan Venteicher 	sc = device_get_softc(dev);
3739da9560cSBryan Venteicher 
3749da9560cSBryan Venteicher 	vtpci_modern_reset(sc);
3759da9560cSBryan Venteicher 	vtpci_child_detached(&sc->vtpci_common);
3769da9560cSBryan Venteicher 
3779da9560cSBryan Venteicher 	/* After the reset, retell the host we've noticed this device. */
3789da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_ACK);
3799da9560cSBryan Venteicher }
3809da9560cSBryan Venteicher 
3819da9560cSBryan Venteicher static int
vtpci_modern_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)3829da9560cSBryan Venteicher vtpci_modern_read_ivar(device_t dev, device_t child, int index,
3839da9560cSBryan Venteicher     uintptr_t *result)
3849da9560cSBryan Venteicher {
3859da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
3869da9560cSBryan Venteicher 	struct vtpci_common *cn;
3879da9560cSBryan Venteicher 
3889da9560cSBryan Venteicher 	sc = device_get_softc(dev);
3899da9560cSBryan Venteicher 	cn = &sc->vtpci_common;
3909da9560cSBryan Venteicher 
3919da9560cSBryan Venteicher 	if (vtpci_child_device(cn) != child)
3929da9560cSBryan Venteicher 		return (ENOENT);
3939da9560cSBryan Venteicher 
3949da9560cSBryan Venteicher 	switch (index) {
3959da9560cSBryan Venteicher 	case VIRTIO_IVAR_DEVTYPE:
3969da9560cSBryan Venteicher 		*result = sc->vtpci_devid;
3979da9560cSBryan Venteicher 		break;
3989da9560cSBryan Venteicher 	default:
3999da9560cSBryan Venteicher 		return (vtpci_read_ivar(cn, index, result));
4009da9560cSBryan Venteicher 	}
4019da9560cSBryan Venteicher 
4029da9560cSBryan Venteicher 	return (0);
4039da9560cSBryan Venteicher }
4049da9560cSBryan Venteicher 
4059da9560cSBryan Venteicher static int
vtpci_modern_write_ivar(device_t dev,device_t child,int index,uintptr_t value)4069da9560cSBryan Venteicher vtpci_modern_write_ivar(device_t dev, device_t child, int index,
4079da9560cSBryan Venteicher     uintptr_t value)
4089da9560cSBryan Venteicher {
4099da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
4109da9560cSBryan Venteicher 	struct vtpci_common *cn;
4119da9560cSBryan Venteicher 
4129da9560cSBryan Venteicher 	sc = device_get_softc(dev);
4139da9560cSBryan Venteicher 	cn = &sc->vtpci_common;
4149da9560cSBryan Venteicher 
4159da9560cSBryan Venteicher 	if (vtpci_child_device(cn) != child)
4169da9560cSBryan Venteicher 		return (ENOENT);
4179da9560cSBryan Venteicher 
4189da9560cSBryan Venteicher 	switch (index) {
4199da9560cSBryan Venteicher 	default:
4209da9560cSBryan Venteicher 		return (vtpci_write_ivar(cn, index, value));
4219da9560cSBryan Venteicher 	}
4229da9560cSBryan Venteicher 
4239da9560cSBryan Venteicher 	return (0);
4249da9560cSBryan Venteicher }
4259da9560cSBryan Venteicher 
4269da9560cSBryan Venteicher static uint64_t
vtpci_modern_negotiate_features(device_t dev,uint64_t child_features)4279da9560cSBryan Venteicher vtpci_modern_negotiate_features(device_t dev, uint64_t child_features)
4289da9560cSBryan Venteicher {
4299da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
4309da9560cSBryan Venteicher 	uint64_t host_features, features;
4319da9560cSBryan Venteicher 
4329da9560cSBryan Venteicher 	sc = device_get_softc(dev);
4339da9560cSBryan Venteicher 	host_features = vtpci_modern_read_features(sc);
4349da9560cSBryan Venteicher 
4359da9560cSBryan Venteicher 	/*
4369da9560cSBryan Venteicher 	 * Since the driver was added as a child of the modern PCI bus,
4379da9560cSBryan Venteicher 	 * always add the V1 flag.
4389da9560cSBryan Venteicher 	 */
4399da9560cSBryan Venteicher 	child_features |= VIRTIO_F_VERSION_1;
4409da9560cSBryan Venteicher 
4419da9560cSBryan Venteicher 	features = vtpci_negotiate_features(&sc->vtpci_common,
4429da9560cSBryan Venteicher 	    child_features, host_features);
4439da9560cSBryan Venteicher 	vtpci_modern_write_features(sc, features);
4449da9560cSBryan Venteicher 
4459da9560cSBryan Venteicher 	return (features);
4469da9560cSBryan Venteicher }
4479da9560cSBryan Venteicher 
4489da9560cSBryan Venteicher static int
vtpci_modern_finalize_features(device_t dev)4499da9560cSBryan Venteicher vtpci_modern_finalize_features(device_t dev)
4509da9560cSBryan Venteicher {
4519da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
4529da9560cSBryan Venteicher 	uint8_t status;
4539da9560cSBryan Venteicher 
4549da9560cSBryan Venteicher 	sc = device_get_softc(dev);
4559da9560cSBryan Venteicher 
4569da9560cSBryan Venteicher 	/*
4579da9560cSBryan Venteicher 	 * Must re-read the status after setting it to verify the negotiated
4589da9560cSBryan Venteicher 	 * features were accepted by the device.
4599da9560cSBryan Venteicher 	 */
4609da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_S_FEATURES_OK);
4619da9560cSBryan Venteicher 
4629da9560cSBryan Venteicher 	status = vtpci_modern_get_status(sc);
4639da9560cSBryan Venteicher 	if ((status & VIRTIO_CONFIG_S_FEATURES_OK) == 0) {
4649da9560cSBryan Venteicher 		device_printf(dev, "desired features were not accepted\n");
4659da9560cSBryan Venteicher 		return (ENOTSUP);
4669da9560cSBryan Venteicher 	}
4679da9560cSBryan Venteicher 
4689da9560cSBryan Venteicher 	return (0);
4699da9560cSBryan Venteicher }
4709da9560cSBryan Venteicher 
471ccb576a8SMina Galić static bool
vtpci_modern_with_feature(device_t dev,uint64_t feature)4729da9560cSBryan Venteicher vtpci_modern_with_feature(device_t dev, uint64_t feature)
4739da9560cSBryan Venteicher {
4749da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
4759da9560cSBryan Venteicher 
4769da9560cSBryan Venteicher 	sc = device_get_softc(dev);
4779da9560cSBryan Venteicher 
4789da9560cSBryan Venteicher 	return (vtpci_with_feature(&sc->vtpci_common, feature));
4799da9560cSBryan Venteicher }
4809da9560cSBryan Venteicher 
4819da9560cSBryan Venteicher static uint64_t
vtpci_modern_read_features(struct vtpci_modern_softc * sc)4829da9560cSBryan Venteicher vtpci_modern_read_features(struct vtpci_modern_softc *sc)
4839da9560cSBryan Venteicher {
4849da9560cSBryan Venteicher 	uint32_t features0, features1;
4859da9560cSBryan Venteicher 
4869da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, VIRTIO_PCI_COMMON_DFSELECT, 0);
4879da9560cSBryan Venteicher 	features0 = vtpci_modern_read_common_4(sc, VIRTIO_PCI_COMMON_DF);
4889da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, VIRTIO_PCI_COMMON_DFSELECT, 1);
4899da9560cSBryan Venteicher 	features1 = vtpci_modern_read_common_4(sc, VIRTIO_PCI_COMMON_DF);
4909da9560cSBryan Venteicher 
4919da9560cSBryan Venteicher 	return (((uint64_t) features1 << 32) | features0);
4929da9560cSBryan Venteicher }
4939da9560cSBryan Venteicher 
4949da9560cSBryan Venteicher static void
vtpci_modern_write_features(struct vtpci_modern_softc * sc,uint64_t features)4959da9560cSBryan Venteicher vtpci_modern_write_features(struct vtpci_modern_softc *sc, uint64_t features)
4969da9560cSBryan Venteicher {
4979da9560cSBryan Venteicher 	uint32_t features0, features1;
4989da9560cSBryan Venteicher 
4999da9560cSBryan Venteicher 	features0 = features;
5009da9560cSBryan Venteicher 	features1 = features >> 32;
5019da9560cSBryan Venteicher 
5029da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, VIRTIO_PCI_COMMON_GFSELECT, 0);
5039da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, VIRTIO_PCI_COMMON_GF, features0);
5049da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, VIRTIO_PCI_COMMON_GFSELECT, 1);
5059da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, VIRTIO_PCI_COMMON_GF, features1);
5069da9560cSBryan Venteicher }
5079da9560cSBryan Venteicher 
5089da9560cSBryan Venteicher static int
vtpci_modern_alloc_virtqueues(device_t dev,int nvqs,struct vq_alloc_info * vq_info)509180c0240SMina Galić vtpci_modern_alloc_virtqueues(device_t dev, int nvqs,
5109da9560cSBryan Venteicher     struct vq_alloc_info *vq_info)
5119da9560cSBryan Venteicher {
5129da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
5139da9560cSBryan Venteicher 	struct vtpci_common *cn;
5149da9560cSBryan Venteicher 	uint16_t max_nvqs;
5159da9560cSBryan Venteicher 
5169da9560cSBryan Venteicher 	sc = device_get_softc(dev);
5179da9560cSBryan Venteicher 	cn = &sc->vtpci_common;
5189da9560cSBryan Venteicher 
5199da9560cSBryan Venteicher 	max_nvqs = vtpci_modern_read_common_2(sc, VIRTIO_PCI_COMMON_NUMQ);
5209da9560cSBryan Venteicher 	if (nvqs > max_nvqs) {
5219da9560cSBryan Venteicher 		device_printf(sc->vtpci_dev, "requested virtqueue count %d "
5229da9560cSBryan Venteicher 		    "exceeds max %d\n", nvqs, max_nvqs);
5239da9560cSBryan Venteicher 		return (E2BIG);
5249da9560cSBryan Venteicher 	}
5259da9560cSBryan Venteicher 
526180c0240SMina Galić 	return (vtpci_alloc_virtqueues(cn, nvqs, vq_info));
5279da9560cSBryan Venteicher }
5289da9560cSBryan Venteicher 
5299da9560cSBryan Venteicher static int
vtpci_modern_setup_interrupts(device_t dev,enum intr_type type)5309da9560cSBryan Venteicher vtpci_modern_setup_interrupts(device_t dev, enum intr_type type)
5319da9560cSBryan Venteicher {
5329da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
5339da9560cSBryan Venteicher 	int error;
5349da9560cSBryan Venteicher 
5359da9560cSBryan Venteicher 	sc = device_get_softc(dev);
5369da9560cSBryan Venteicher 
5379da9560cSBryan Venteicher 	error = vtpci_setup_interrupts(&sc->vtpci_common, type);
5389da9560cSBryan Venteicher 	if (error == 0)
5399da9560cSBryan Venteicher 		vtpci_modern_enable_virtqueues(sc);
5409da9560cSBryan Venteicher 
5419da9560cSBryan Venteicher 	return (error);
5429da9560cSBryan Venteicher }
5439da9560cSBryan Venteicher 
5449da9560cSBryan Venteicher static void
vtpci_modern_stop(device_t dev)5459da9560cSBryan Venteicher vtpci_modern_stop(device_t dev)
5469da9560cSBryan Venteicher {
5479da9560cSBryan Venteicher 	vtpci_modern_reset(device_get_softc(dev));
5489da9560cSBryan Venteicher }
5499da9560cSBryan Venteicher 
5509da9560cSBryan Venteicher static int
vtpci_modern_reinit(device_t dev,uint64_t features)5519da9560cSBryan Venteicher vtpci_modern_reinit(device_t dev, uint64_t features)
5529da9560cSBryan Venteicher {
5539da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
5549da9560cSBryan Venteicher 	struct vtpci_common *cn;
5559da9560cSBryan Venteicher 	int error;
5569da9560cSBryan Venteicher 
5579da9560cSBryan Venteicher 	sc = device_get_softc(dev);
5589da9560cSBryan Venteicher 	cn = &sc->vtpci_common;
5599da9560cSBryan Venteicher 
5609da9560cSBryan Venteicher 	/*
5619da9560cSBryan Venteicher 	 * Redrive the device initialization. This is a bit of an abuse of
5629da9560cSBryan Venteicher 	 * the specification, but VirtualBox, QEMU/KVM, and BHyVe seem to
5639da9560cSBryan Venteicher 	 * play nice.
5649da9560cSBryan Venteicher 	 *
5659da9560cSBryan Venteicher 	 * We do not allow the host device to change from what was originally
5669da9560cSBryan Venteicher 	 * negotiated beyond what the guest driver changed. MSIX state should
5679da9560cSBryan Venteicher 	 * not change, number of virtqueues and their size remain the same, etc.
5689da9560cSBryan Venteicher 	 * This will need to be rethought when we want to support migration.
5699da9560cSBryan Venteicher 	 */
5709da9560cSBryan Venteicher 
5719da9560cSBryan Venteicher 	if (vtpci_modern_get_status(sc) != VIRTIO_CONFIG_STATUS_RESET)
5729da9560cSBryan Venteicher 		vtpci_modern_stop(dev);
5739da9560cSBryan Venteicher 
5749da9560cSBryan Venteicher 	/*
5759da9560cSBryan Venteicher 	 * Quickly drive the status through ACK and DRIVER. The device does
5769da9560cSBryan Venteicher 	 * not become usable again until DRIVER_OK in reinit complete.
5779da9560cSBryan Venteicher 	 */
5789da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_ACK);
5799da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_DRIVER);
5809da9560cSBryan Venteicher 
5819da9560cSBryan Venteicher 	/*
5829da9560cSBryan Venteicher 	 * TODO: Check that features are not added as to what was
5839da9560cSBryan Venteicher 	 * originally negotiated.
5849da9560cSBryan Venteicher 	 */
5859da9560cSBryan Venteicher 	vtpci_modern_negotiate_features(dev, features);
5869da9560cSBryan Venteicher 	error = vtpci_modern_finalize_features(dev);
5879da9560cSBryan Venteicher 	if (error) {
5889da9560cSBryan Venteicher 		device_printf(dev, "cannot finalize features during reinit\n");
5899da9560cSBryan Venteicher 		return (error);
5909da9560cSBryan Venteicher 	}
5919da9560cSBryan Venteicher 
5929da9560cSBryan Venteicher 	error = vtpci_reinit(cn);
5939da9560cSBryan Venteicher 	if (error)
5949da9560cSBryan Venteicher 		return (error);
5959da9560cSBryan Venteicher 
5969da9560cSBryan Venteicher 	return (0);
5979da9560cSBryan Venteicher }
5989da9560cSBryan Venteicher 
5999da9560cSBryan Venteicher static void
vtpci_modern_reinit_complete(device_t dev)6009da9560cSBryan Venteicher vtpci_modern_reinit_complete(device_t dev)
6019da9560cSBryan Venteicher {
6029da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
6039da9560cSBryan Venteicher 
6049da9560cSBryan Venteicher 	sc = device_get_softc(dev);
6059da9560cSBryan Venteicher 
6069da9560cSBryan Venteicher 	vtpci_modern_enable_virtqueues(sc);
6079da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_DRIVER_OK);
6089da9560cSBryan Venteicher }
6099da9560cSBryan Venteicher 
6109da9560cSBryan Venteicher static void
vtpci_modern_notify_vq(device_t dev,uint16_t queue,bus_size_t offset)6119da9560cSBryan Venteicher vtpci_modern_notify_vq(device_t dev, uint16_t queue, bus_size_t offset)
6129da9560cSBryan Venteicher {
6139da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
6149da9560cSBryan Venteicher 
6159da9560cSBryan Venteicher 	sc = device_get_softc(dev);
6169da9560cSBryan Venteicher 
6179da9560cSBryan Venteicher 	vtpci_modern_write_notify_2(sc, offset, queue);
6189da9560cSBryan Venteicher }
6199da9560cSBryan Venteicher 
6209da9560cSBryan Venteicher static uint8_t
vtpci_modern_get_status(struct vtpci_modern_softc * sc)6219da9560cSBryan Venteicher vtpci_modern_get_status(struct vtpci_modern_softc *sc)
6229da9560cSBryan Venteicher {
6239da9560cSBryan Venteicher 	return (vtpci_modern_read_common_1(sc, VIRTIO_PCI_COMMON_STATUS));
6249da9560cSBryan Venteicher }
6259da9560cSBryan Venteicher 
6269da9560cSBryan Venteicher static void
vtpci_modern_set_status(struct vtpci_modern_softc * sc,uint8_t status)6279da9560cSBryan Venteicher vtpci_modern_set_status(struct vtpci_modern_softc *sc, uint8_t status)
6289da9560cSBryan Venteicher {
6299da9560cSBryan Venteicher 	if (status != VIRTIO_CONFIG_STATUS_RESET)
6309da9560cSBryan Venteicher 		status |= vtpci_modern_get_status(sc);
6319da9560cSBryan Venteicher 
6329da9560cSBryan Venteicher 	vtpci_modern_write_common_1(sc, VIRTIO_PCI_COMMON_STATUS, status);
6339da9560cSBryan Venteicher }
6349da9560cSBryan Venteicher 
6359da9560cSBryan Venteicher static int
vtpci_modern_config_generation(device_t dev)6369da9560cSBryan Venteicher vtpci_modern_config_generation(device_t dev)
6379da9560cSBryan Venteicher {
6389da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
6399da9560cSBryan Venteicher 	uint8_t gen;
6409da9560cSBryan Venteicher 
6419da9560cSBryan Venteicher 	sc = device_get_softc(dev);
6429da9560cSBryan Venteicher 	gen = vtpci_modern_read_common_1(sc, VIRTIO_PCI_COMMON_CFGGENERATION);
6439da9560cSBryan Venteicher 
6449da9560cSBryan Venteicher 	return (gen);
6459da9560cSBryan Venteicher }
6469da9560cSBryan Venteicher 
6479da9560cSBryan Venteicher static void
vtpci_modern_read_dev_config(device_t dev,bus_size_t offset,void * dst,int length)6489da9560cSBryan Venteicher vtpci_modern_read_dev_config(device_t dev, bus_size_t offset, void *dst,
6499da9560cSBryan Venteicher     int length)
6509da9560cSBryan Venteicher {
6519da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
6529da9560cSBryan Venteicher 
6539da9560cSBryan Venteicher 	sc = device_get_softc(dev);
6549da9560cSBryan Venteicher 
6559da9560cSBryan Venteicher 	if (sc->vtpci_device_res_map.vtrm_map.r_size == 0) {
6569da9560cSBryan Venteicher 		panic("%s: attempt to read dev config but not present",
6579da9560cSBryan Venteicher 		    __func__);
6589da9560cSBryan Venteicher 	}
6599da9560cSBryan Venteicher 
6609da9560cSBryan Venteicher 	switch (length) {
6619da9560cSBryan Venteicher 	case 1:
6629da9560cSBryan Venteicher 		*(uint8_t *) dst = vtpci_modern_read_device_1(sc, offset);
6639da9560cSBryan Venteicher 		break;
6649da9560cSBryan Venteicher 	case 2:
6659da9560cSBryan Venteicher 		*(uint16_t *) dst = virtio_htog16(true,
6669da9560cSBryan Venteicher 		    vtpci_modern_read_device_2(sc, offset));
6679da9560cSBryan Venteicher 		break;
6689da9560cSBryan Venteicher 	case 4:
6699da9560cSBryan Venteicher 		*(uint32_t *) dst = virtio_htog32(true,
6709da9560cSBryan Venteicher 		    vtpci_modern_read_device_4(sc, offset));
6719da9560cSBryan Venteicher 		break;
6729da9560cSBryan Venteicher 	case 8:
6739da9560cSBryan Venteicher 		*(uint64_t *) dst = virtio_htog64(true,
6749da9560cSBryan Venteicher 		    vtpci_modern_read_device_8(sc, offset));
6759da9560cSBryan Venteicher 		break;
6769da9560cSBryan Venteicher 	default:
6779da9560cSBryan Venteicher 		panic("%s: device %s invalid device read length %d offset %d",
6789da9560cSBryan Venteicher 		    __func__, device_get_nameunit(dev), length, (int) offset);
6799da9560cSBryan Venteicher 	}
6809da9560cSBryan Venteicher }
6819da9560cSBryan Venteicher 
6829da9560cSBryan Venteicher static void
vtpci_modern_write_dev_config(device_t dev,bus_size_t offset,const void * src,int length)6836c4f9516SAlex Richardson vtpci_modern_write_dev_config(device_t dev, bus_size_t offset, const void *src,
6849da9560cSBryan Venteicher     int length)
6859da9560cSBryan Venteicher {
6869da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
6879da9560cSBryan Venteicher 
6889da9560cSBryan Venteicher 	sc = device_get_softc(dev);
6899da9560cSBryan Venteicher 
6909da9560cSBryan Venteicher 	if (sc->vtpci_device_res_map.vtrm_map.r_size == 0) {
6919da9560cSBryan Venteicher 		panic("%s: attempt to write dev config but not present",
6929da9560cSBryan Venteicher 		    __func__);
6939da9560cSBryan Venteicher 	}
6949da9560cSBryan Venteicher 
6959da9560cSBryan Venteicher 	switch (length) {
6969da9560cSBryan Venteicher 	case 1:
6976c4f9516SAlex Richardson 		vtpci_modern_write_device_1(sc, offset, *(const uint8_t *) src);
6989da9560cSBryan Venteicher 		break;
6999da9560cSBryan Venteicher 	case 2: {
7006c4f9516SAlex Richardson 		uint16_t val = virtio_gtoh16(true, *(const uint16_t *) src);
7019da9560cSBryan Venteicher 		vtpci_modern_write_device_2(sc, offset, val);
7029da9560cSBryan Venteicher 		break;
7039da9560cSBryan Venteicher 	}
7049da9560cSBryan Venteicher 	case 4: {
7056c4f9516SAlex Richardson 		uint32_t val = virtio_gtoh32(true, *(const uint32_t *) src);
7069da9560cSBryan Venteicher 		vtpci_modern_write_device_4(sc, offset, val);
7079da9560cSBryan Venteicher 		break;
7089da9560cSBryan Venteicher 	}
7099da9560cSBryan Venteicher 	case 8: {
7106c4f9516SAlex Richardson 		uint64_t val = virtio_gtoh64(true, *(const uint64_t *) src);
7119da9560cSBryan Venteicher 		vtpci_modern_write_device_8(sc, offset, val);
7129da9560cSBryan Venteicher 		break;
7139da9560cSBryan Venteicher 	}
7149da9560cSBryan Venteicher 	default:
7159da9560cSBryan Venteicher 		panic("%s: device %s invalid device write length %d offset %d",
7169da9560cSBryan Venteicher 		    __func__, device_get_nameunit(dev), length, (int) offset);
7179da9560cSBryan Venteicher 	}
7189da9560cSBryan Venteicher }
7199da9560cSBryan Venteicher 
7209da9560cSBryan Venteicher static int
vtpci_modern_probe_configs(device_t dev)7219da9560cSBryan Venteicher vtpci_modern_probe_configs(device_t dev)
7229da9560cSBryan Venteicher {
7239da9560cSBryan Venteicher 	int error;
7249da9560cSBryan Venteicher 
7259da9560cSBryan Venteicher 	/*
7269da9560cSBryan Venteicher 	 * These config capabilities must be present. The DEVICE_CFG
7279da9560cSBryan Venteicher 	 * capability is only present if the device requires it.
7289da9560cSBryan Venteicher 	 */
7299da9560cSBryan Venteicher 
7309da9560cSBryan Venteicher 	error = vtpci_modern_find_cap(dev, VIRTIO_PCI_CAP_COMMON_CFG, NULL);
7319da9560cSBryan Venteicher 	if (error) {
7329da9560cSBryan Venteicher 		device_printf(dev, "cannot find COMMON_CFG capability\n");
7339da9560cSBryan Venteicher 		return (error);
7349da9560cSBryan Venteicher 	}
7359da9560cSBryan Venteicher 
7369da9560cSBryan Venteicher 	error = vtpci_modern_find_cap(dev, VIRTIO_PCI_CAP_NOTIFY_CFG, NULL);
7379da9560cSBryan Venteicher 	if (error) {
7389da9560cSBryan Venteicher 		device_printf(dev, "cannot find NOTIFY_CFG capability\n");
7399da9560cSBryan Venteicher 		return (error);
7409da9560cSBryan Venteicher 	}
7419da9560cSBryan Venteicher 
7429da9560cSBryan Venteicher 	error = vtpci_modern_find_cap(dev, VIRTIO_PCI_CAP_ISR_CFG, NULL);
7439da9560cSBryan Venteicher 	if (error) {
7449da9560cSBryan Venteicher 		device_printf(dev, "cannot find ISR_CFG capability\n");
7459da9560cSBryan Venteicher 		return (error);
7469da9560cSBryan Venteicher 	}
7479da9560cSBryan Venteicher 
7489da9560cSBryan Venteicher 	return (0);
7499da9560cSBryan Venteicher }
7509da9560cSBryan Venteicher 
7519da9560cSBryan Venteicher static int
vtpci_modern_find_cap(device_t dev,uint8_t cfg_type,int * cap_offset)7529da9560cSBryan Venteicher vtpci_modern_find_cap(device_t dev, uint8_t cfg_type, int *cap_offset)
7539da9560cSBryan Venteicher {
7549da9560cSBryan Venteicher 	uint32_t type, bar;
7559da9560cSBryan Venteicher 	int capreg, error;
7569da9560cSBryan Venteicher 
7579da9560cSBryan Venteicher 	for (error = pci_find_cap(dev, PCIY_VENDOR, &capreg);
7589da9560cSBryan Venteicher 	     error == 0;
7599da9560cSBryan Venteicher 	     error = pci_find_next_cap(dev, PCIY_VENDOR, capreg, &capreg)) {
7609da9560cSBryan Venteicher 
7619da9560cSBryan Venteicher 		type = pci_read_config(dev, capreg +
7629da9560cSBryan Venteicher 		    offsetof(struct virtio_pci_cap, cfg_type), 1);
7639da9560cSBryan Venteicher 		bar = pci_read_config(dev, capreg +
7649da9560cSBryan Venteicher 		    offsetof(struct virtio_pci_cap, bar), 1);
7659da9560cSBryan Venteicher 
7669da9560cSBryan Venteicher 		/* Must ignore reserved BARs. */
7679da9560cSBryan Venteicher 		if (bar >= VTPCI_MODERN_MAX_BARS)
7689da9560cSBryan Venteicher 			continue;
7699da9560cSBryan Venteicher 
7709da9560cSBryan Venteicher 		if (type == cfg_type) {
7719da9560cSBryan Venteicher 			if (cap_offset != NULL)
7729da9560cSBryan Venteicher 				*cap_offset = capreg;
7739da9560cSBryan Venteicher 			break;
7749da9560cSBryan Venteicher 		}
7759da9560cSBryan Venteicher 	}
7769da9560cSBryan Venteicher 
7779da9560cSBryan Venteicher 	return (error);
7789da9560cSBryan Venteicher }
7799da9560cSBryan Venteicher 
7809da9560cSBryan Venteicher static int
vtpci_modern_map_common_config(struct vtpci_modern_softc * sc)7819da9560cSBryan Venteicher vtpci_modern_map_common_config(struct vtpci_modern_softc *sc)
7829da9560cSBryan Venteicher {
7839da9560cSBryan Venteicher 	device_t dev;
7849da9560cSBryan Venteicher 	int error;
7859da9560cSBryan Venteicher 
7869da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
7879da9560cSBryan Venteicher 
7889da9560cSBryan Venteicher 	error = vtpci_modern_find_cap_resource(sc, VIRTIO_PCI_CAP_COMMON_CFG,
7899da9560cSBryan Venteicher 	    sizeof(struct virtio_pci_common_cfg), 4, &sc->vtpci_common_res_map);
7909da9560cSBryan Venteicher 	if (error) {
7919da9560cSBryan Venteicher 		device_printf(dev, "cannot find cap COMMON_CFG resource\n");
7929da9560cSBryan Venteicher 		return (error);
7939da9560cSBryan Venteicher 	}
7949da9560cSBryan Venteicher 
7959da9560cSBryan Venteicher 	error = vtpci_modern_alloc_resource_map(sc, &sc->vtpci_common_res_map);
7969da9560cSBryan Venteicher 	if (error) {
7979da9560cSBryan Venteicher 		device_printf(dev, "cannot alloc resource for COMMON_CFG\n");
7989da9560cSBryan Venteicher 		return (error);
7999da9560cSBryan Venteicher 	}
8009da9560cSBryan Venteicher 
8019da9560cSBryan Venteicher 	return (0);
8029da9560cSBryan Venteicher }
8039da9560cSBryan Venteicher 
8049da9560cSBryan Venteicher static int
vtpci_modern_map_notify_config(struct vtpci_modern_softc * sc)8059da9560cSBryan Venteicher vtpci_modern_map_notify_config(struct vtpci_modern_softc *sc)
8069da9560cSBryan Venteicher {
8079da9560cSBryan Venteicher 	device_t dev;
8089da9560cSBryan Venteicher 	int cap_offset, error;
8099da9560cSBryan Venteicher 
8109da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
8119da9560cSBryan Venteicher 
8129da9560cSBryan Venteicher 	error = vtpci_modern_find_cap_resource(sc, VIRTIO_PCI_CAP_NOTIFY_CFG,
8139da9560cSBryan Venteicher 	    -1, 2, &sc->vtpci_notify_res_map);
8149da9560cSBryan Venteicher 	if (error) {
8159da9560cSBryan Venteicher 		device_printf(dev, "cannot find cap NOTIFY_CFG resource\n");
8169da9560cSBryan Venteicher 		return (error);
8179da9560cSBryan Venteicher 	}
8189da9560cSBryan Venteicher 
8199da9560cSBryan Venteicher 	cap_offset = sc->vtpci_notify_res_map.vtrm_cap_offset;
8209da9560cSBryan Venteicher 
8219da9560cSBryan Venteicher 	sc->vtpci_notify_offset_multiplier = pci_read_config(dev, cap_offset +
8229da9560cSBryan Venteicher 	    offsetof(struct virtio_pci_notify_cap, notify_off_multiplier), 4);
8239da9560cSBryan Venteicher 
8249da9560cSBryan Venteicher 	error = vtpci_modern_alloc_resource_map(sc, &sc->vtpci_notify_res_map);
8259da9560cSBryan Venteicher 	if (error) {
8269da9560cSBryan Venteicher 		device_printf(dev, "cannot alloc resource for NOTIFY_CFG\n");
8279da9560cSBryan Venteicher 		return (error);
8289da9560cSBryan Venteicher 	}
8299da9560cSBryan Venteicher 
8309da9560cSBryan Venteicher 	return (0);
8319da9560cSBryan Venteicher }
8329da9560cSBryan Venteicher 
8339da9560cSBryan Venteicher static int
vtpci_modern_map_isr_config(struct vtpci_modern_softc * sc)8349da9560cSBryan Venteicher vtpci_modern_map_isr_config(struct vtpci_modern_softc *sc)
8359da9560cSBryan Venteicher {
8369da9560cSBryan Venteicher 	device_t dev;
8379da9560cSBryan Venteicher 	int error;
8389da9560cSBryan Venteicher 
8399da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
8409da9560cSBryan Venteicher 
8419da9560cSBryan Venteicher 	error = vtpci_modern_find_cap_resource(sc, VIRTIO_PCI_CAP_ISR_CFG,
8429da9560cSBryan Venteicher 	    sizeof(uint8_t), 1, &sc->vtpci_isr_res_map);
8439da9560cSBryan Venteicher 	if (error) {
8449da9560cSBryan Venteicher 		device_printf(dev, "cannot find cap ISR_CFG resource\n");
8459da9560cSBryan Venteicher 		return (error);
8469da9560cSBryan Venteicher 	}
8479da9560cSBryan Venteicher 
8489da9560cSBryan Venteicher 	error = vtpci_modern_alloc_resource_map(sc, &sc->vtpci_isr_res_map);
8499da9560cSBryan Venteicher 	if (error) {
8509da9560cSBryan Venteicher 		device_printf(dev, "cannot alloc resource for ISR_CFG\n");
8519da9560cSBryan Venteicher 		return (error);
8529da9560cSBryan Venteicher 	}
8539da9560cSBryan Venteicher 
8549da9560cSBryan Venteicher 	return (0);
8559da9560cSBryan Venteicher }
8569da9560cSBryan Venteicher 
8579da9560cSBryan Venteicher static int
vtpci_modern_map_device_config(struct vtpci_modern_softc * sc)8589da9560cSBryan Venteicher vtpci_modern_map_device_config(struct vtpci_modern_softc *sc)
8599da9560cSBryan Venteicher {
8609da9560cSBryan Venteicher 	device_t dev;
8619da9560cSBryan Venteicher 	int error;
8629da9560cSBryan Venteicher 
8639da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
8649da9560cSBryan Venteicher 
8659da9560cSBryan Venteicher 	error = vtpci_modern_find_cap_resource(sc, VIRTIO_PCI_CAP_DEVICE_CFG,
8669da9560cSBryan Venteicher 	    -1, 4, &sc->vtpci_device_res_map);
8679da9560cSBryan Venteicher 	if (error == ENOENT) {
8689da9560cSBryan Venteicher 		/* Device configuration is optional depending on device. */
8699da9560cSBryan Venteicher 		return (0);
8709da9560cSBryan Venteicher 	} else if (error) {
8719da9560cSBryan Venteicher 		device_printf(dev, "cannot find cap DEVICE_CFG resource\n");
8729da9560cSBryan Venteicher 		return (error);
8739da9560cSBryan Venteicher 	}
8749da9560cSBryan Venteicher 
8759da9560cSBryan Venteicher 	error = vtpci_modern_alloc_resource_map(sc, &sc->vtpci_device_res_map);
8769da9560cSBryan Venteicher 	if (error) {
8779da9560cSBryan Venteicher 		device_printf(dev, "cannot alloc resource for DEVICE_CFG\n");
8789da9560cSBryan Venteicher 		return (error);
8799da9560cSBryan Venteicher 	}
8809da9560cSBryan Venteicher 
8819da9560cSBryan Venteicher 	return (0);
8829da9560cSBryan Venteicher }
8839da9560cSBryan Venteicher 
8849da9560cSBryan Venteicher static int
vtpci_modern_map_configs(struct vtpci_modern_softc * sc)8859da9560cSBryan Venteicher vtpci_modern_map_configs(struct vtpci_modern_softc *sc)
8869da9560cSBryan Venteicher {
8879da9560cSBryan Venteicher 	int error;
8889da9560cSBryan Venteicher 
8899da9560cSBryan Venteicher 	error = vtpci_modern_map_common_config(sc);
8909da9560cSBryan Venteicher 	if (error)
8919da9560cSBryan Venteicher 		return (error);
8929da9560cSBryan Venteicher 
8939da9560cSBryan Venteicher 	error = vtpci_modern_map_notify_config(sc);
8949da9560cSBryan Venteicher 	if (error)
8959da9560cSBryan Venteicher 		return (error);
8969da9560cSBryan Venteicher 
8979da9560cSBryan Venteicher 	error = vtpci_modern_map_isr_config(sc);
8989da9560cSBryan Venteicher 	if (error)
8999da9560cSBryan Venteicher 		return (error);
9009da9560cSBryan Venteicher 
9019da9560cSBryan Venteicher 	error = vtpci_modern_map_device_config(sc);
9029da9560cSBryan Venteicher 	if (error)
9039da9560cSBryan Venteicher 		return (error);
9049da9560cSBryan Venteicher 
9059da9560cSBryan Venteicher 	vtpci_modern_alloc_msix_resource(sc);
9069da9560cSBryan Venteicher 
9079da9560cSBryan Venteicher 	return (0);
9089da9560cSBryan Venteicher }
9099da9560cSBryan Venteicher 
9109da9560cSBryan Venteicher static void
vtpci_modern_unmap_configs(struct vtpci_modern_softc * sc)9119da9560cSBryan Venteicher vtpci_modern_unmap_configs(struct vtpci_modern_softc *sc)
9129da9560cSBryan Venteicher {
9139da9560cSBryan Venteicher 
9149da9560cSBryan Venteicher 	vtpci_modern_free_resource_map(sc, &sc->vtpci_common_res_map);
9159da9560cSBryan Venteicher 	vtpci_modern_free_resource_map(sc, &sc->vtpci_notify_res_map);
9169da9560cSBryan Venteicher 	vtpci_modern_free_resource_map(sc, &sc->vtpci_isr_res_map);
9179da9560cSBryan Venteicher 	vtpci_modern_free_resource_map(sc, &sc->vtpci_device_res_map);
9189da9560cSBryan Venteicher 
9199da9560cSBryan Venteicher 	vtpci_modern_free_bar_resources(sc);
9209da9560cSBryan Venteicher 	vtpci_modern_free_msix_resource(sc);
9219da9560cSBryan Venteicher 
9229da9560cSBryan Venteicher 	sc->vtpci_notify_offset_multiplier = 0;
9239da9560cSBryan Venteicher }
9249da9560cSBryan Venteicher 
9259da9560cSBryan Venteicher static int
vtpci_modern_find_cap_resource(struct vtpci_modern_softc * sc,uint8_t cfg_type,int min_size,int alignment,struct vtpci_modern_resource_map * res)9269da9560cSBryan Venteicher vtpci_modern_find_cap_resource(struct vtpci_modern_softc *sc, uint8_t cfg_type,
9279da9560cSBryan Venteicher     int min_size, int alignment, struct vtpci_modern_resource_map *res)
9289da9560cSBryan Venteicher {
9299da9560cSBryan Venteicher 	device_t dev;
9309da9560cSBryan Venteicher 	int cap_offset, offset, length, error;
9319da9560cSBryan Venteicher 	uint8_t bar, cap_length;
9329da9560cSBryan Venteicher 
9339da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
9349da9560cSBryan Venteicher 
9359da9560cSBryan Venteicher 	error = vtpci_modern_find_cap(dev, cfg_type, &cap_offset);
9369da9560cSBryan Venteicher 	if (error)
9379da9560cSBryan Venteicher 		return (error);
9389da9560cSBryan Venteicher 
9399da9560cSBryan Venteicher 	cap_length = pci_read_config(dev,
9409da9560cSBryan Venteicher 	    cap_offset + offsetof(struct virtio_pci_cap, cap_len), 1);
9419da9560cSBryan Venteicher 
9429da9560cSBryan Venteicher 	if (cap_length < sizeof(struct virtio_pci_cap)) {
9439da9560cSBryan Venteicher 		device_printf(dev, "cap %u length %d less than expected\n",
9449da9560cSBryan Venteicher 		    cfg_type, cap_length);
9459da9560cSBryan Venteicher 		return (ENXIO);
9469da9560cSBryan Venteicher 	}
9479da9560cSBryan Venteicher 
9489da9560cSBryan Venteicher 	bar = pci_read_config(dev,
9499da9560cSBryan Venteicher 	    cap_offset + offsetof(struct virtio_pci_cap, bar), 1);
9509da9560cSBryan Venteicher 	offset = pci_read_config(dev,
9519da9560cSBryan Venteicher 	    cap_offset + offsetof(struct virtio_pci_cap, offset), 4);
9529da9560cSBryan Venteicher 	length = pci_read_config(dev,
9539da9560cSBryan Venteicher 	    cap_offset + offsetof(struct virtio_pci_cap, length), 4);
9549da9560cSBryan Venteicher 
9559da9560cSBryan Venteicher 	if (min_size != -1 && length < min_size) {
9569da9560cSBryan Venteicher 		device_printf(dev, "cap %u struct length %d less than min %d\n",
9579da9560cSBryan Venteicher 		    cfg_type, length, min_size);
9589da9560cSBryan Venteicher 		return (ENXIO);
9599da9560cSBryan Venteicher 	}
9609da9560cSBryan Venteicher 
9619da9560cSBryan Venteicher 	if (offset % alignment) {
9629da9560cSBryan Venteicher 		device_printf(dev, "cap %u struct offset %d not aligned to %d\n",
9639da9560cSBryan Venteicher 		    cfg_type, offset, alignment);
9649da9560cSBryan Venteicher 		return (ENXIO);
9659da9560cSBryan Venteicher 	}
9669da9560cSBryan Venteicher 
9679da9560cSBryan Venteicher 	/* BMV: TODO Can we determine the size of the BAR here? */
9689da9560cSBryan Venteicher 
9699da9560cSBryan Venteicher 	res->vtrm_cap_offset = cap_offset;
9709da9560cSBryan Venteicher 	res->vtrm_bar = bar;
9719da9560cSBryan Venteicher 	res->vtrm_offset = offset;
9729da9560cSBryan Venteicher 	res->vtrm_length = length;
9739da9560cSBryan Venteicher 	res->vtrm_type = vtpci_modern_bar_type(sc, bar);
9749da9560cSBryan Venteicher 
9759da9560cSBryan Venteicher 	return (0);
9769da9560cSBryan Venteicher }
9779da9560cSBryan Venteicher 
9789da9560cSBryan Venteicher static int
vtpci_modern_bar_type(struct vtpci_modern_softc * sc,int bar)9799da9560cSBryan Venteicher vtpci_modern_bar_type(struct vtpci_modern_softc *sc, int bar)
9809da9560cSBryan Venteicher {
9819da9560cSBryan Venteicher 	uint32_t val;
9829da9560cSBryan Venteicher 
9839da9560cSBryan Venteicher 	/*
9849da9560cSBryan Venteicher 	 * The BAR described by a config capability may be either an IOPORT or
9859da9560cSBryan Venteicher 	 * MEM, but we must know the type when calling bus_alloc_resource().
9869da9560cSBryan Venteicher 	 */
9879da9560cSBryan Venteicher 	val = pci_read_config(sc->vtpci_dev, PCIR_BAR(bar), 4);
9889da9560cSBryan Venteicher 	if (PCI_BAR_IO(val))
9899da9560cSBryan Venteicher 		return (SYS_RES_IOPORT);
9909da9560cSBryan Venteicher 	else
9919da9560cSBryan Venteicher 		return (SYS_RES_MEMORY);
9929da9560cSBryan Venteicher }
9939da9560cSBryan Venteicher 
9949da9560cSBryan Venteicher static struct resource *
vtpci_modern_get_bar_resource(struct vtpci_modern_softc * sc,int bar,int type)9959da9560cSBryan Venteicher vtpci_modern_get_bar_resource(struct vtpci_modern_softc *sc, int bar, int type)
9969da9560cSBryan Venteicher {
9979da9560cSBryan Venteicher 	struct resource *res;
9989da9560cSBryan Venteicher 
9999da9560cSBryan Venteicher 	MPASS(bar >= 0 && bar < VTPCI_MODERN_MAX_BARS);
10009da9560cSBryan Venteicher 	res = sc->vtpci_bar_res[bar].vtbr_res;
10019da9560cSBryan Venteicher 	MPASS(res == NULL || sc->vtpci_bar_res[bar].vtbr_type == type);
10029da9560cSBryan Venteicher 
10039da9560cSBryan Venteicher 	return (res);
10049da9560cSBryan Venteicher }
10059da9560cSBryan Venteicher 
10069da9560cSBryan Venteicher static struct resource *
vtpci_modern_alloc_bar_resource(struct vtpci_modern_softc * sc,int bar,int type)10079da9560cSBryan Venteicher vtpci_modern_alloc_bar_resource(struct vtpci_modern_softc *sc, int bar,
10089da9560cSBryan Venteicher     int type)
10099da9560cSBryan Venteicher {
10109da9560cSBryan Venteicher 	struct resource *res;
10119da9560cSBryan Venteicher 	int rid;
10129da9560cSBryan Venteicher 
10139da9560cSBryan Venteicher 	MPASS(bar >= 0 && bar < VTPCI_MODERN_MAX_BARS);
10149da9560cSBryan Venteicher 	MPASS(type == SYS_RES_MEMORY || type == SYS_RES_IOPORT);
10159da9560cSBryan Venteicher 
10169da9560cSBryan Venteicher 	res = sc->vtpci_bar_res[bar].vtbr_res;
10179da9560cSBryan Venteicher 	if (res != NULL) {
10189da9560cSBryan Venteicher 		MPASS(sc->vtpci_bar_res[bar].vtbr_type == type);
10199da9560cSBryan Venteicher 		return (res);
10209da9560cSBryan Venteicher 	}
10219da9560cSBryan Venteicher 
10229da9560cSBryan Venteicher 	rid = PCIR_BAR(bar);
10239da9560cSBryan Venteicher 	res = bus_alloc_resource_any(sc->vtpci_dev, type, &rid,
10249da9560cSBryan Venteicher 	    RF_ACTIVE | RF_UNMAPPED);
10259da9560cSBryan Venteicher 	if (res != NULL) {
10269da9560cSBryan Venteicher 		sc->vtpci_bar_res[bar].vtbr_res = res;
10279da9560cSBryan Venteicher 		sc->vtpci_bar_res[bar].vtbr_type = type;
10289da9560cSBryan Venteicher 	}
10299da9560cSBryan Venteicher 
10309da9560cSBryan Venteicher 	return (res);
10319da9560cSBryan Venteicher }
10329da9560cSBryan Venteicher 
10339da9560cSBryan Venteicher static void
vtpci_modern_free_bar_resources(struct vtpci_modern_softc * sc)10349da9560cSBryan Venteicher vtpci_modern_free_bar_resources(struct vtpci_modern_softc *sc)
10359da9560cSBryan Venteicher {
10369da9560cSBryan Venteicher 	device_t dev;
10379da9560cSBryan Venteicher 	struct resource *res;
10389da9560cSBryan Venteicher 	int bar, rid, type;
10399da9560cSBryan Venteicher 
10409da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
10419da9560cSBryan Venteicher 
10429da9560cSBryan Venteicher 	for (bar = 0; bar < VTPCI_MODERN_MAX_BARS; bar++) {
10439da9560cSBryan Venteicher 		res = sc->vtpci_bar_res[bar].vtbr_res;
10449da9560cSBryan Venteicher 		type = sc->vtpci_bar_res[bar].vtbr_type;
10459da9560cSBryan Venteicher 
10469da9560cSBryan Venteicher 		if (res != NULL) {
10479da9560cSBryan Venteicher 			rid = PCIR_BAR(bar);
10489da9560cSBryan Venteicher 			bus_release_resource(dev, type, rid, res);
10499da9560cSBryan Venteicher 			sc->vtpci_bar_res[bar].vtbr_res = NULL;
10509da9560cSBryan Venteicher 			sc->vtpci_bar_res[bar].vtbr_type = 0;
10519da9560cSBryan Venteicher 		}
10529da9560cSBryan Venteicher 	}
10539da9560cSBryan Venteicher }
10549da9560cSBryan Venteicher 
10559da9560cSBryan Venteicher static int
vtpci_modern_alloc_resource_map(struct vtpci_modern_softc * sc,struct vtpci_modern_resource_map * map)10569da9560cSBryan Venteicher vtpci_modern_alloc_resource_map(struct vtpci_modern_softc *sc,
10579da9560cSBryan Venteicher     struct vtpci_modern_resource_map *map)
10589da9560cSBryan Venteicher {
10599da9560cSBryan Venteicher 	struct resource_map_request req;
10609da9560cSBryan Venteicher 	struct resource *res;
10619da9560cSBryan Venteicher 	int type;
10629da9560cSBryan Venteicher 
10639da9560cSBryan Venteicher 	type = map->vtrm_type;
10649da9560cSBryan Venteicher 
10659da9560cSBryan Venteicher 	res = vtpci_modern_alloc_bar_resource(sc, map->vtrm_bar, type);
10669da9560cSBryan Venteicher 	if (res == NULL)
10679da9560cSBryan Venteicher 		return (ENXIO);
10689da9560cSBryan Venteicher 
10699da9560cSBryan Venteicher 	resource_init_map_request(&req);
10709da9560cSBryan Venteicher 	req.offset = map->vtrm_offset;
10719da9560cSBryan Venteicher 	req.length = map->vtrm_length;
10729da9560cSBryan Venteicher 
10739da9560cSBryan Venteicher 	return (bus_map_resource(sc->vtpci_dev, type, res, &req,
10749da9560cSBryan Venteicher 	    &map->vtrm_map));
10759da9560cSBryan Venteicher }
10769da9560cSBryan Venteicher 
10779da9560cSBryan Venteicher static void
vtpci_modern_free_resource_map(struct vtpci_modern_softc * sc,struct vtpci_modern_resource_map * map)10789da9560cSBryan Venteicher vtpci_modern_free_resource_map(struct vtpci_modern_softc *sc,
10799da9560cSBryan Venteicher     struct vtpci_modern_resource_map *map)
10809da9560cSBryan Venteicher {
10819da9560cSBryan Venteicher 	struct resource *res;
10829da9560cSBryan Venteicher 	int type;
10839da9560cSBryan Venteicher 
10849da9560cSBryan Venteicher 	type = map->vtrm_type;
10859da9560cSBryan Venteicher 	res = vtpci_modern_get_bar_resource(sc, map->vtrm_bar, type);
10869da9560cSBryan Venteicher 
10879da9560cSBryan Venteicher 	if (res != NULL && map->vtrm_map.r_size != 0) {
10889da9560cSBryan Venteicher 		bus_unmap_resource(sc->vtpci_dev, type, res, &map->vtrm_map);
10899da9560cSBryan Venteicher 		bzero(map, sizeof(struct vtpci_modern_resource_map));
10909da9560cSBryan Venteicher 	}
10919da9560cSBryan Venteicher }
10929da9560cSBryan Venteicher 
10939da9560cSBryan Venteicher static void
vtpci_modern_alloc_msix_resource(struct vtpci_modern_softc * sc)10949da9560cSBryan Venteicher vtpci_modern_alloc_msix_resource(struct vtpci_modern_softc *sc)
10959da9560cSBryan Venteicher {
10969da9560cSBryan Venteicher 	device_t dev;
10979da9560cSBryan Venteicher 	int bar;
10989da9560cSBryan Venteicher 
10999da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
11009da9560cSBryan Venteicher 
11019da9560cSBryan Venteicher 	if (!vtpci_is_msix_available(&sc->vtpci_common) ||
11029da9560cSBryan Venteicher 	    (bar = pci_msix_table_bar(dev)) == -1)
11039da9560cSBryan Venteicher 		return;
11049da9560cSBryan Venteicher 
11059da9560cSBryan Venteicher 	/* TODO: Can this BAR be in the 0-5 range? */
11069da9560cSBryan Venteicher 	sc->vtpci_msix_bar = bar;
11079da9560cSBryan Venteicher 	if ((sc->vtpci_msix_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
11089da9560cSBryan Venteicher 	    &bar, RF_ACTIVE)) == NULL)
11099da9560cSBryan Venteicher 		device_printf(dev, "Unable to map MSIX table\n");
11109da9560cSBryan Venteicher }
11119da9560cSBryan Venteicher 
11129da9560cSBryan Venteicher static void
vtpci_modern_free_msix_resource(struct vtpci_modern_softc * sc)11139da9560cSBryan Venteicher vtpci_modern_free_msix_resource(struct vtpci_modern_softc *sc)
11149da9560cSBryan Venteicher {
11159da9560cSBryan Venteicher 	device_t dev;
11169da9560cSBryan Venteicher 
11179da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
11189da9560cSBryan Venteicher 
11199da9560cSBryan Venteicher 	if (sc->vtpci_msix_res != NULL) {
11209da9560cSBryan Venteicher 		bus_release_resource(dev, SYS_RES_MEMORY, sc->vtpci_msix_bar,
11219da9560cSBryan Venteicher 		    sc->vtpci_msix_res);
11229da9560cSBryan Venteicher 		sc->vtpci_msix_bar = 0;
11239da9560cSBryan Venteicher 		sc->vtpci_msix_res = NULL;
11249da9560cSBryan Venteicher 	}
11259da9560cSBryan Venteicher }
11269da9560cSBryan Venteicher 
11279da9560cSBryan Venteicher static void
vtpci_modern_probe_and_attach_child(struct vtpci_modern_softc * sc)11289da9560cSBryan Venteicher vtpci_modern_probe_and_attach_child(struct vtpci_modern_softc *sc)
11299da9560cSBryan Venteicher {
11309da9560cSBryan Venteicher 	device_t dev, child;
11319da9560cSBryan Venteicher 
11329da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
11339da9560cSBryan Venteicher 	child = vtpci_child_device(&sc->vtpci_common);
11349da9560cSBryan Venteicher 
11359da9560cSBryan Venteicher 	if (child == NULL || device_get_state(child) != DS_NOTPRESENT)
11369da9560cSBryan Venteicher 		return;
11379da9560cSBryan Venteicher 
11389da9560cSBryan Venteicher 	if (device_probe(child) != 0)
11399da9560cSBryan Venteicher 		return;
11409da9560cSBryan Venteicher 
11419da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_DRIVER);
11429da9560cSBryan Venteicher 
11439da9560cSBryan Venteicher 	if (device_attach(child) != 0) {
11449da9560cSBryan Venteicher 		vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_FAILED);
11459da9560cSBryan Venteicher 		/* Reset state for later attempt. */
11469da9560cSBryan Venteicher 		vtpci_modern_child_detached(dev, child);
11479da9560cSBryan Venteicher 	} else {
11489da9560cSBryan Venteicher 		vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_DRIVER_OK);
11499da9560cSBryan Venteicher 		VIRTIO_ATTACH_COMPLETED(child);
11509da9560cSBryan Venteicher 	}
11519da9560cSBryan Venteicher }
11529da9560cSBryan Venteicher 
11539da9560cSBryan Venteicher static int
vtpci_modern_register_msix(struct vtpci_modern_softc * sc,int offset,struct vtpci_interrupt * intr)11549da9560cSBryan Venteicher vtpci_modern_register_msix(struct vtpci_modern_softc *sc, int offset,
11559da9560cSBryan Venteicher     struct vtpci_interrupt *intr)
11569da9560cSBryan Venteicher {
11579da9560cSBryan Venteicher 	uint16_t vector;
11589da9560cSBryan Venteicher 
11599da9560cSBryan Venteicher 	if (intr != NULL) {
11609da9560cSBryan Venteicher 		/* Map from guest rid to host vector. */
11619da9560cSBryan Venteicher 		vector = intr->vti_rid - 1;
11629da9560cSBryan Venteicher 	} else
11639da9560cSBryan Venteicher 		vector = VIRTIO_MSI_NO_VECTOR;
11649da9560cSBryan Venteicher 
11659da9560cSBryan Venteicher 	vtpci_modern_write_common_2(sc, offset, vector);
11669da9560cSBryan Venteicher 	return (vtpci_modern_read_common_2(sc, offset) == vector ? 0 : ENODEV);
11679da9560cSBryan Venteicher }
11689da9560cSBryan Venteicher 
11699da9560cSBryan Venteicher static int
vtpci_modern_register_cfg_msix(device_t dev,struct vtpci_interrupt * intr)11709da9560cSBryan Venteicher vtpci_modern_register_cfg_msix(device_t dev, struct vtpci_interrupt *intr)
11719da9560cSBryan Venteicher {
11729da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
11739da9560cSBryan Venteicher 	int error;
11749da9560cSBryan Venteicher 
11759da9560cSBryan Venteicher 	sc = device_get_softc(dev);
11769da9560cSBryan Venteicher 
11779da9560cSBryan Venteicher 	error = vtpci_modern_register_msix(sc, VIRTIO_PCI_COMMON_MSIX, intr);
11789da9560cSBryan Venteicher 	if (error) {
11799da9560cSBryan Venteicher 		device_printf(dev,
11809da9560cSBryan Venteicher 		    "unable to register config MSIX interrupt\n");
11819da9560cSBryan Venteicher 		return (error);
11829da9560cSBryan Venteicher 	}
11839da9560cSBryan Venteicher 
11849da9560cSBryan Venteicher 	return (0);
11859da9560cSBryan Venteicher }
11869da9560cSBryan Venteicher 
11879da9560cSBryan Venteicher static int
vtpci_modern_register_vq_msix(device_t dev,int idx,struct vtpci_interrupt * intr)11889da9560cSBryan Venteicher vtpci_modern_register_vq_msix(device_t dev, int idx,
11899da9560cSBryan Venteicher     struct vtpci_interrupt *intr)
11909da9560cSBryan Venteicher {
11919da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
11929da9560cSBryan Venteicher 	int error;
11939da9560cSBryan Venteicher 
11949da9560cSBryan Venteicher 	sc = device_get_softc(dev);
11959da9560cSBryan Venteicher 
11969da9560cSBryan Venteicher 	vtpci_modern_select_virtqueue(sc, idx);
11979da9560cSBryan Venteicher 	error = vtpci_modern_register_msix(sc, VIRTIO_PCI_COMMON_Q_MSIX, intr);
11989da9560cSBryan Venteicher 	if (error) {
11999da9560cSBryan Venteicher 		device_printf(dev,
12009da9560cSBryan Venteicher 		    "unable to register virtqueue MSIX interrupt\n");
12019da9560cSBryan Venteicher 		return (error);
12029da9560cSBryan Venteicher 	}
12039da9560cSBryan Venteicher 
12049da9560cSBryan Venteicher 	return (0);
12059da9560cSBryan Venteicher }
12069da9560cSBryan Venteicher 
12079da9560cSBryan Venteicher static void
vtpci_modern_reset(struct vtpci_modern_softc * sc)12089da9560cSBryan Venteicher vtpci_modern_reset(struct vtpci_modern_softc *sc)
12099da9560cSBryan Venteicher {
12109da9560cSBryan Venteicher 	/*
12119da9560cSBryan Venteicher 	 * Setting the status to RESET sets the host device to the
12129da9560cSBryan Venteicher 	 * original, uninitialized state. Must poll the status until
12139da9560cSBryan Venteicher 	 * the reset is complete.
12149da9560cSBryan Venteicher 	 */
12159da9560cSBryan Venteicher 	vtpci_modern_set_status(sc, VIRTIO_CONFIG_STATUS_RESET);
12169da9560cSBryan Venteicher 
12179da9560cSBryan Venteicher 	while (vtpci_modern_get_status(sc) != VIRTIO_CONFIG_STATUS_RESET)
12189da9560cSBryan Venteicher 		cpu_spinwait();
12199da9560cSBryan Venteicher }
12209da9560cSBryan Venteicher 
12219da9560cSBryan Venteicher static void
vtpci_modern_select_virtqueue(struct vtpci_modern_softc * sc,int idx)12229da9560cSBryan Venteicher vtpci_modern_select_virtqueue(struct vtpci_modern_softc *sc, int idx)
12239da9560cSBryan Venteicher {
12249da9560cSBryan Venteicher 	vtpci_modern_write_common_2(sc, VIRTIO_PCI_COMMON_Q_SELECT, idx);
12259da9560cSBryan Venteicher }
12269da9560cSBryan Venteicher 
12279da9560cSBryan Venteicher static uint8_t
vtpci_modern_read_isr(device_t dev)12289da9560cSBryan Venteicher vtpci_modern_read_isr(device_t dev)
12299da9560cSBryan Venteicher {
12309da9560cSBryan Venteicher 	return (vtpci_modern_read_isr_1(device_get_softc(dev), 0));
12319da9560cSBryan Venteicher }
12329da9560cSBryan Venteicher 
12339da9560cSBryan Venteicher static uint16_t
vtpci_modern_get_vq_size(device_t dev,int idx)12349da9560cSBryan Venteicher vtpci_modern_get_vq_size(device_t dev, int idx)
12359da9560cSBryan Venteicher {
12369da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
12379da9560cSBryan Venteicher 
12389da9560cSBryan Venteicher 	sc = device_get_softc(dev);
12399da9560cSBryan Venteicher 
12409da9560cSBryan Venteicher 	vtpci_modern_select_virtqueue(sc, idx);
12419da9560cSBryan Venteicher 	return (vtpci_modern_read_common_2(sc, VIRTIO_PCI_COMMON_Q_SIZE));
12429da9560cSBryan Venteicher }
12439da9560cSBryan Venteicher 
12449da9560cSBryan Venteicher static bus_size_t
vtpci_modern_get_vq_notify_off(device_t dev,int idx)12459da9560cSBryan Venteicher vtpci_modern_get_vq_notify_off(device_t dev, int idx)
12469da9560cSBryan Venteicher {
12479da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
12489da9560cSBryan Venteicher 	uint16_t q_notify_off;
12499da9560cSBryan Venteicher 
12509da9560cSBryan Venteicher 	sc = device_get_softc(dev);
12519da9560cSBryan Venteicher 
12529da9560cSBryan Venteicher 	vtpci_modern_select_virtqueue(sc, idx);
12539da9560cSBryan Venteicher 	q_notify_off = vtpci_modern_read_common_2(sc, VIRTIO_PCI_COMMON_Q_NOFF);
12549da9560cSBryan Venteicher 
12559da9560cSBryan Venteicher 	return (q_notify_off * sc->vtpci_notify_offset_multiplier);
12569da9560cSBryan Venteicher }
12579da9560cSBryan Venteicher 
12589da9560cSBryan Venteicher static void
vtpci_modern_set_vq(device_t dev,struct virtqueue * vq)12599da9560cSBryan Venteicher vtpci_modern_set_vq(device_t dev, struct virtqueue *vq)
12609da9560cSBryan Venteicher {
12619da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
12629da9560cSBryan Venteicher 
12639da9560cSBryan Venteicher 	sc = device_get_softc(dev);
12649da9560cSBryan Venteicher 
12659da9560cSBryan Venteicher 	vtpci_modern_select_virtqueue(sc, virtqueue_index(vq));
12669da9560cSBryan Venteicher 
12679da9560cSBryan Venteicher 	/* BMV: Currently we never adjust the device's proposed VQ size. */
12689da9560cSBryan Venteicher 	vtpci_modern_write_common_2(sc,
12699da9560cSBryan Venteicher 	    VIRTIO_PCI_COMMON_Q_SIZE, virtqueue_size(vq));
12709da9560cSBryan Venteicher 
12719da9560cSBryan Venteicher 	vtpci_modern_write_common_8(sc,
12729da9560cSBryan Venteicher 	    VIRTIO_PCI_COMMON_Q_DESCLO, virtqueue_desc_paddr(vq));
12739da9560cSBryan Venteicher 	vtpci_modern_write_common_8(sc,
12749da9560cSBryan Venteicher 	    VIRTIO_PCI_COMMON_Q_AVAILLO, virtqueue_avail_paddr(vq));
12759da9560cSBryan Venteicher         vtpci_modern_write_common_8(sc,
12769da9560cSBryan Venteicher 	    VIRTIO_PCI_COMMON_Q_USEDLO, virtqueue_used_paddr(vq));
12779da9560cSBryan Venteicher }
12789da9560cSBryan Venteicher 
12799da9560cSBryan Venteicher static void
vtpci_modern_disable_vq(device_t dev,int idx)12809da9560cSBryan Venteicher vtpci_modern_disable_vq(device_t dev, int idx)
12819da9560cSBryan Venteicher {
12829da9560cSBryan Venteicher 	struct vtpci_modern_softc *sc;
12839da9560cSBryan Venteicher 
12849da9560cSBryan Venteicher 	sc = device_get_softc(dev);
12859da9560cSBryan Venteicher 
12869da9560cSBryan Venteicher 	vtpci_modern_select_virtqueue(sc, idx);
12879da9560cSBryan Venteicher 	vtpci_modern_write_common_8(sc, VIRTIO_PCI_COMMON_Q_DESCLO, 0ULL);
12889da9560cSBryan Venteicher 	vtpci_modern_write_common_8(sc, VIRTIO_PCI_COMMON_Q_AVAILLO, 0ULL);
12899da9560cSBryan Venteicher         vtpci_modern_write_common_8(sc, VIRTIO_PCI_COMMON_Q_USEDLO, 0ULL);
12909da9560cSBryan Venteicher }
12919da9560cSBryan Venteicher 
12929da9560cSBryan Venteicher static void
vtpci_modern_enable_virtqueues(struct vtpci_modern_softc * sc)12939da9560cSBryan Venteicher vtpci_modern_enable_virtqueues(struct vtpci_modern_softc *sc)
12949da9560cSBryan Venteicher {
12959da9560cSBryan Venteicher 	int idx;
12969da9560cSBryan Venteicher 
12979da9560cSBryan Venteicher 	for (idx = 0; idx < sc->vtpci_common.vtpci_nvqs; idx++) {
12989da9560cSBryan Venteicher 		vtpci_modern_select_virtqueue(sc, idx);
12999da9560cSBryan Venteicher 		vtpci_modern_write_common_2(sc, VIRTIO_PCI_COMMON_Q_ENABLE, 1);
13009da9560cSBryan Venteicher 	}
13019da9560cSBryan Venteicher }
13029da9560cSBryan Venteicher 
13039da9560cSBryan Venteicher static uint8_t
vtpci_modern_read_common_1(struct vtpci_modern_softc * sc,bus_size_t off)13049da9560cSBryan Venteicher vtpci_modern_read_common_1(struct vtpci_modern_softc *sc, bus_size_t off)
13059da9560cSBryan Venteicher {
13069da9560cSBryan Venteicher 	return (bus_read_1(&sc->vtpci_common_res_map.vtrm_map, off));
13079da9560cSBryan Venteicher }
13089da9560cSBryan Venteicher 
13099da9560cSBryan Venteicher static uint16_t
vtpci_modern_read_common_2(struct vtpci_modern_softc * sc,bus_size_t off)13109da9560cSBryan Venteicher vtpci_modern_read_common_2(struct vtpci_modern_softc *sc, bus_size_t off)
13119da9560cSBryan Venteicher {
1312fb53b42eSAlfredo Dal'Ava Junior 	return virtio_htog16(true,
1313fb53b42eSAlfredo Dal'Ava Junior 			bus_read_2(&sc->vtpci_common_res_map.vtrm_map, off));
13149da9560cSBryan Venteicher }
13159da9560cSBryan Venteicher 
13169da9560cSBryan Venteicher static uint32_t
vtpci_modern_read_common_4(struct vtpci_modern_softc * sc,bus_size_t off)13179da9560cSBryan Venteicher vtpci_modern_read_common_4(struct vtpci_modern_softc *sc, bus_size_t off)
13189da9560cSBryan Venteicher {
1319fb53b42eSAlfredo Dal'Ava Junior 	return virtio_htog32(true,
1320fb53b42eSAlfredo Dal'Ava Junior 			bus_read_4(&sc->vtpci_common_res_map.vtrm_map, off));
13219da9560cSBryan Venteicher }
13229da9560cSBryan Venteicher 
13239da9560cSBryan Venteicher static void
vtpci_modern_write_common_1(struct vtpci_modern_softc * sc,bus_size_t off,uint8_t val)13249da9560cSBryan Venteicher vtpci_modern_write_common_1(struct vtpci_modern_softc *sc, bus_size_t off,
13259da9560cSBryan Venteicher     uint8_t val)
13269da9560cSBryan Venteicher {
13279da9560cSBryan Venteicher 	bus_write_1(&sc->vtpci_common_res_map.vtrm_map, off, val);
13289da9560cSBryan Venteicher }
13299da9560cSBryan Venteicher 
13309da9560cSBryan Venteicher static void
vtpci_modern_write_common_2(struct vtpci_modern_softc * sc,bus_size_t off,uint16_t val)13319da9560cSBryan Venteicher vtpci_modern_write_common_2(struct vtpci_modern_softc *sc, bus_size_t off,
13329da9560cSBryan Venteicher     uint16_t val)
13339da9560cSBryan Venteicher {
1334fb53b42eSAlfredo Dal'Ava Junior 	bus_write_2(&sc->vtpci_common_res_map.vtrm_map,
1335fb53b42eSAlfredo Dal'Ava Junior 			off, virtio_gtoh16(true, val));
13369da9560cSBryan Venteicher }
13379da9560cSBryan Venteicher 
13389da9560cSBryan Venteicher static void
vtpci_modern_write_common_4(struct vtpci_modern_softc * sc,bus_size_t off,uint32_t val)13399da9560cSBryan Venteicher vtpci_modern_write_common_4(struct vtpci_modern_softc *sc, bus_size_t off,
13409da9560cSBryan Venteicher     uint32_t val)
13419da9560cSBryan Venteicher {
1342fb53b42eSAlfredo Dal'Ava Junior 	bus_write_4(&sc->vtpci_common_res_map.vtrm_map,
1343fb53b42eSAlfredo Dal'Ava Junior 			off, virtio_gtoh32(true, val));
13449da9560cSBryan Venteicher }
13459da9560cSBryan Venteicher 
13469da9560cSBryan Venteicher static void
vtpci_modern_write_common_8(struct vtpci_modern_softc * sc,bus_size_t off,uint64_t val)13479da9560cSBryan Venteicher vtpci_modern_write_common_8(struct vtpci_modern_softc *sc, bus_size_t off,
13489da9560cSBryan Venteicher     uint64_t val)
13499da9560cSBryan Venteicher {
13509da9560cSBryan Venteicher 	uint32_t val0, val1;
13519da9560cSBryan Venteicher 
13529da9560cSBryan Venteicher 	val0 = (uint32_t) val;
13539da9560cSBryan Venteicher 	val1 = val >> 32;
13549da9560cSBryan Venteicher 
13559da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, off, val0);
13569da9560cSBryan Venteicher 	vtpci_modern_write_common_4(sc, off + 4, val1);
13579da9560cSBryan Venteicher }
13589da9560cSBryan Venteicher 
13599da9560cSBryan Venteicher static void
vtpci_modern_write_notify_2(struct vtpci_modern_softc * sc,bus_size_t off,uint16_t val)13609da9560cSBryan Venteicher vtpci_modern_write_notify_2(struct vtpci_modern_softc *sc, bus_size_t off,
13619da9560cSBryan Venteicher     uint16_t val)
13629da9560cSBryan Venteicher {
13639da9560cSBryan Venteicher 	bus_write_2(&sc->vtpci_notify_res_map.vtrm_map, off, val);
13649da9560cSBryan Venteicher }
13659da9560cSBryan Venteicher 
13669da9560cSBryan Venteicher static uint8_t
vtpci_modern_read_isr_1(struct vtpci_modern_softc * sc,bus_size_t off)13679da9560cSBryan Venteicher vtpci_modern_read_isr_1(struct vtpci_modern_softc *sc, bus_size_t off)
13689da9560cSBryan Venteicher {
13699da9560cSBryan Venteicher 	return (bus_read_1(&sc->vtpci_isr_res_map.vtrm_map, off));
13709da9560cSBryan Venteicher }
13719da9560cSBryan Venteicher 
13729da9560cSBryan Venteicher static uint8_t
vtpci_modern_read_device_1(struct vtpci_modern_softc * sc,bus_size_t off)13739da9560cSBryan Venteicher vtpci_modern_read_device_1(struct vtpci_modern_softc *sc, bus_size_t off)
13749da9560cSBryan Venteicher {
13759da9560cSBryan Venteicher 	return (bus_read_1(&sc->vtpci_device_res_map.vtrm_map, off));
13769da9560cSBryan Venteicher }
13779da9560cSBryan Venteicher 
13789da9560cSBryan Venteicher static uint16_t
vtpci_modern_read_device_2(struct vtpci_modern_softc * sc,bus_size_t off)13799da9560cSBryan Venteicher vtpci_modern_read_device_2(struct vtpci_modern_softc *sc, bus_size_t off)
13809da9560cSBryan Venteicher {
13819da9560cSBryan Venteicher 	return (bus_read_2(&sc->vtpci_device_res_map.vtrm_map, off));
13829da9560cSBryan Venteicher }
13839da9560cSBryan Venteicher 
13849da9560cSBryan Venteicher static uint32_t
vtpci_modern_read_device_4(struct vtpci_modern_softc * sc,bus_size_t off)13859da9560cSBryan Venteicher vtpci_modern_read_device_4(struct vtpci_modern_softc *sc, bus_size_t off)
13869da9560cSBryan Venteicher {
13879da9560cSBryan Venteicher 	return (bus_read_4(&sc->vtpci_device_res_map.vtrm_map, off));
13889da9560cSBryan Venteicher }
13899da9560cSBryan Venteicher 
13909da9560cSBryan Venteicher static uint64_t
vtpci_modern_read_device_8(struct vtpci_modern_softc * sc,bus_size_t off)13919da9560cSBryan Venteicher vtpci_modern_read_device_8(struct vtpci_modern_softc *sc, bus_size_t off)
13929da9560cSBryan Venteicher {
13939da9560cSBryan Venteicher 	device_t dev;
13949da9560cSBryan Venteicher 	int gen;
13959da9560cSBryan Venteicher 	uint32_t val0, val1;
13969da9560cSBryan Venteicher 
13979da9560cSBryan Venteicher 	dev = sc->vtpci_dev;
13989da9560cSBryan Venteicher 
13999da9560cSBryan Venteicher 	/*
14009da9560cSBryan Venteicher 	 * Treat the 64-bit field as two 32-bit fields. Use the generation
14019da9560cSBryan Venteicher 	 * to ensure a consistent read.
14029da9560cSBryan Venteicher 	 */
14039da9560cSBryan Venteicher 	do {
14049da9560cSBryan Venteicher 		gen = vtpci_modern_config_generation(dev);
14059da9560cSBryan Venteicher 		val0 = vtpci_modern_read_device_4(sc, off);
14069da9560cSBryan Venteicher 		val1 = vtpci_modern_read_device_4(sc, off + 4);
14079da9560cSBryan Venteicher 	} while (gen != vtpci_modern_config_generation(dev));
14089da9560cSBryan Venteicher 
14099da9560cSBryan Venteicher 	return (((uint64_t) val1 << 32) | val0);
14109da9560cSBryan Venteicher }
14119da9560cSBryan Venteicher 
14129da9560cSBryan Venteicher static void
vtpci_modern_write_device_1(struct vtpci_modern_softc * sc,bus_size_t off,uint8_t val)14139da9560cSBryan Venteicher vtpci_modern_write_device_1(struct vtpci_modern_softc *sc, bus_size_t off,
14149da9560cSBryan Venteicher     uint8_t val)
14159da9560cSBryan Venteicher {
14169da9560cSBryan Venteicher 	bus_write_1(&sc->vtpci_device_res_map.vtrm_map, off, val);
14179da9560cSBryan Venteicher }
14189da9560cSBryan Venteicher 
14199da9560cSBryan Venteicher static void
vtpci_modern_write_device_2(struct vtpci_modern_softc * sc,bus_size_t off,uint16_t val)14209da9560cSBryan Venteicher vtpci_modern_write_device_2(struct vtpci_modern_softc *sc, bus_size_t off,
14219da9560cSBryan Venteicher     uint16_t val)
14229da9560cSBryan Venteicher {
14239da9560cSBryan Venteicher 	bus_write_2(&sc->vtpci_device_res_map.vtrm_map, off, val);
14249da9560cSBryan Venteicher }
14259da9560cSBryan Venteicher 
14269da9560cSBryan Venteicher static void
vtpci_modern_write_device_4(struct vtpci_modern_softc * sc,bus_size_t off,uint32_t val)14279da9560cSBryan Venteicher vtpci_modern_write_device_4(struct vtpci_modern_softc *sc, bus_size_t off,
14289da9560cSBryan Venteicher     uint32_t val)
14299da9560cSBryan Venteicher {
14309da9560cSBryan Venteicher 	bus_write_4(&sc->vtpci_device_res_map.vtrm_map, off, val);
14319da9560cSBryan Venteicher }
14329da9560cSBryan Venteicher 
14339da9560cSBryan Venteicher static void
vtpci_modern_write_device_8(struct vtpci_modern_softc * sc,bus_size_t off,uint64_t val)14349da9560cSBryan Venteicher vtpci_modern_write_device_8(struct vtpci_modern_softc *sc, bus_size_t off,
14359da9560cSBryan Venteicher     uint64_t val)
14369da9560cSBryan Venteicher {
14379da9560cSBryan Venteicher 	uint32_t val0, val1;
14389da9560cSBryan Venteicher 
14399da9560cSBryan Venteicher 	val0 = (uint32_t) val;
14409da9560cSBryan Venteicher 	val1 = val >> 32;
14419da9560cSBryan Venteicher 
14429da9560cSBryan Venteicher 	vtpci_modern_write_device_4(sc, off, val0);
14439da9560cSBryan Venteicher 	vtpci_modern_write_device_4(sc, off + 4, val1);
14449da9560cSBryan Venteicher }
1445