111447b59SVenkatesh Srinivas /*- 211447b59SVenkatesh Srinivas * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org> 311447b59SVenkatesh Srinivas * All rights reserved. 411447b59SVenkatesh Srinivas * 511447b59SVenkatesh Srinivas * Redistribution and use in source and binary forms, with or without 611447b59SVenkatesh Srinivas * modification, are permitted provided that the following conditions 711447b59SVenkatesh Srinivas * are met: 811447b59SVenkatesh Srinivas * 1. Redistributions of source code must retain the above copyright 911447b59SVenkatesh Srinivas * notice unmodified, this list of conditions, and the following 1011447b59SVenkatesh Srinivas * disclaimer. 1111447b59SVenkatesh Srinivas * 2. Redistributions in binary form must reproduce the above copyright 1211447b59SVenkatesh Srinivas * notice, this list of conditions and the following disclaimer in the 1311447b59SVenkatesh Srinivas * documentation and/or other materials provided with the distribution. 1411447b59SVenkatesh Srinivas * 1511447b59SVenkatesh Srinivas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1611447b59SVenkatesh Srinivas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1711447b59SVenkatesh Srinivas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1811447b59SVenkatesh Srinivas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1911447b59SVenkatesh Srinivas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2011447b59SVenkatesh Srinivas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2111447b59SVenkatesh Srinivas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2211447b59SVenkatesh Srinivas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2311447b59SVenkatesh Srinivas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2411447b59SVenkatesh Srinivas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2511447b59SVenkatesh Srinivas * 2611447b59SVenkatesh Srinivas * $FreeBSD: src/sys/dev/virtio/pci/virtio_pci.c,v 1.3 2012/04/14 05:48:04 grehan Exp $ 2711447b59SVenkatesh Srinivas */ 2811447b59SVenkatesh Srinivas 2911447b59SVenkatesh Srinivas /* Driver for the VirtIO PCI interface. */ 3011447b59SVenkatesh Srinivas 3111447b59SVenkatesh Srinivas #include <sys/param.h> 3211447b59SVenkatesh Srinivas #include <sys/systm.h> 3311447b59SVenkatesh Srinivas #include <sys/bus.h> 3411447b59SVenkatesh Srinivas #include <sys/kernel.h> 3511447b59SVenkatesh Srinivas #include <sys/module.h> 3611447b59SVenkatesh Srinivas #include <sys/malloc.h> 372f1382caSVenkatesh Srinivas #include <sys/serialize.h> 3811447b59SVenkatesh Srinivas 3911447b59SVenkatesh Srinivas #include <bus/pci/pcivar.h> 4011447b59SVenkatesh Srinivas #include <bus/pci/pcireg.h> 4111447b59SVenkatesh Srinivas 4211447b59SVenkatesh Srinivas #include <sys/rman.h> 4311447b59SVenkatesh Srinivas 44dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtio.h> 45dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtqueue.h> 4611447b59SVenkatesh Srinivas #include "virtio_pci.h" 4711447b59SVenkatesh Srinivas #include "virtio_if.h" 48dcbcbc86SVenkatesh Srinivas #include "virtio_bus_if.h" 4911447b59SVenkatesh Srinivas 5011447b59SVenkatesh Srinivas struct vtpci_softc { 5111447b59SVenkatesh Srinivas device_t vtpci_dev; 5211447b59SVenkatesh Srinivas struct resource *vtpci_res; 5311447b59SVenkatesh Srinivas struct resource *vtpci_msix_res; 5411447b59SVenkatesh Srinivas uint64_t vtpci_features; 5511447b59SVenkatesh Srinivas uint32_t vtpci_flags; 5611447b59SVenkatesh Srinivas int vtpci_irq_type; 5711447b59SVenkatesh Srinivas int vtpci_irq_rid; 5811447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_NO_MSI 0x0001 5911447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_MSI 0x0002 6011447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_NO_MSIX 0x0010 6111447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_MSIX 0x0020 6211447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_SHARED_MSIX 0x0040 6311447b59SVenkatesh Srinivas 6411447b59SVenkatesh Srinivas device_t vtpci_child_dev; 6511447b59SVenkatesh Srinivas struct virtio_feature_desc *vtpci_child_feat_desc; 6611447b59SVenkatesh Srinivas 6711447b59SVenkatesh Srinivas /* 6811447b59SVenkatesh Srinivas * Ideally, each virtqueue that the driver provides a callback for 6911447b59SVenkatesh Srinivas * will receive its own MSIX vector. If there are not sufficient 7011447b59SVenkatesh Srinivas * vectors available, we will then attempt to have all the VQs 7111447b59SVenkatesh Srinivas * share one vector. Note that when using MSIX, the configuration 7211447b59SVenkatesh Srinivas * changed notifications must be on their own vector. 7311447b59SVenkatesh Srinivas * 7411447b59SVenkatesh Srinivas * If MSIX is not available, we will attempt to have the whole 7511447b59SVenkatesh Srinivas * device share one MSI vector, and then, finally, one legacy 7611447b59SVenkatesh Srinivas * interrupt. 7711447b59SVenkatesh Srinivas */ 7811447b59SVenkatesh Srinivas int vtpci_nvqs; 7911447b59SVenkatesh Srinivas struct vtpci_virtqueue { 8011447b59SVenkatesh Srinivas struct virtqueue *vq; 8111447b59SVenkatesh Srinivas 8211447b59SVenkatesh Srinivas /* Index into vtpci_intr_res[] below. Unused, then -1. */ 8311447b59SVenkatesh Srinivas int ires_idx; 8411447b59SVenkatesh Srinivas } vtpci_vqx[VIRTIO_MAX_VIRTQUEUES]; 8511447b59SVenkatesh Srinivas 8611447b59SVenkatesh Srinivas /* 8711447b59SVenkatesh Srinivas * When using MSIX interrupts, the first element of vtpci_intr_res[] 8811447b59SVenkatesh Srinivas * is always the configuration changed notifications. The remaining 8911447b59SVenkatesh Srinivas * element(s) are used for the virtqueues. 9011447b59SVenkatesh Srinivas * 9111447b59SVenkatesh Srinivas * With MSI and legacy interrupts, only the first element of 9211447b59SVenkatesh Srinivas * vtpci_intr_res[] is used. 9311447b59SVenkatesh Srinivas */ 9411447b59SVenkatesh Srinivas int vtpci_nintr_res; 9511447b59SVenkatesh Srinivas struct vtpci_intr_resource { 9611447b59SVenkatesh Srinivas struct resource *irq; 9711447b59SVenkatesh Srinivas int rid; 9811447b59SVenkatesh Srinivas void *intrhand; 9911447b59SVenkatesh Srinivas } vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES]; 10011447b59SVenkatesh Srinivas }; 10111447b59SVenkatesh Srinivas 10211447b59SVenkatesh Srinivas static int vtpci_probe(device_t); 10311447b59SVenkatesh Srinivas static int vtpci_attach(device_t); 10411447b59SVenkatesh Srinivas static int vtpci_detach(device_t); 10511447b59SVenkatesh Srinivas static int vtpci_suspend(device_t); 10611447b59SVenkatesh Srinivas static int vtpci_resume(device_t); 10711447b59SVenkatesh Srinivas static int vtpci_shutdown(device_t); 10811447b59SVenkatesh Srinivas static void vtpci_driver_added(device_t, driver_t *); 10911447b59SVenkatesh Srinivas static void vtpci_child_detached(device_t, device_t); 11011447b59SVenkatesh Srinivas static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *); 11111447b59SVenkatesh Srinivas static int vtpci_write_ivar(device_t, device_t, int, uintptr_t); 11211447b59SVenkatesh Srinivas 11311447b59SVenkatesh Srinivas static uint64_t vtpci_negotiate_features(device_t, uint64_t); 11411447b59SVenkatesh Srinivas static int vtpci_with_feature(device_t, uint64_t); 11511447b59SVenkatesh Srinivas static int vtpci_alloc_virtqueues(device_t, int, int, 11611447b59SVenkatesh Srinivas struct vq_alloc_info *); 1172f1382caSVenkatesh Srinivas static int vtpci_setup_intr(device_t, lwkt_serialize_t); 11811447b59SVenkatesh Srinivas static void vtpci_stop(device_t); 11911447b59SVenkatesh Srinivas static int vtpci_reinit(device_t, uint64_t); 12011447b59SVenkatesh Srinivas static void vtpci_reinit_complete(device_t); 12111447b59SVenkatesh Srinivas static void vtpci_notify_virtqueue(device_t, uint16_t); 12211447b59SVenkatesh Srinivas static uint8_t vtpci_get_status(device_t); 12311447b59SVenkatesh Srinivas static void vtpci_set_status(device_t, uint8_t); 12411447b59SVenkatesh Srinivas static void vtpci_read_dev_config(device_t, bus_size_t, void *, int); 12511447b59SVenkatesh Srinivas static void vtpci_write_dev_config(device_t, bus_size_t, void *, int); 12611447b59SVenkatesh Srinivas 12711447b59SVenkatesh Srinivas static void vtpci_describe_features(struct vtpci_softc *, const char *, 12811447b59SVenkatesh Srinivas uint64_t); 12911447b59SVenkatesh Srinivas static void vtpci_probe_and_attach_child(struct vtpci_softc *); 13011447b59SVenkatesh Srinivas 13111447b59SVenkatesh Srinivas static int vtpci_alloc_interrupts(struct vtpci_softc *, int, int, 13211447b59SVenkatesh Srinivas struct vq_alloc_info *); 13311447b59SVenkatesh Srinivas static int vtpci_alloc_intr_resources(struct vtpci_softc *, int, 13411447b59SVenkatesh Srinivas struct vq_alloc_info *); 13511447b59SVenkatesh Srinivas static int vtpci_alloc_msi(struct vtpci_softc *); 13611447b59SVenkatesh Srinivas static int vtpci_alloc_msix(struct vtpci_softc *, int); 13711447b59SVenkatesh Srinivas static int vtpci_register_msix_vector(struct vtpci_softc *, int, int); 13811447b59SVenkatesh Srinivas 13911447b59SVenkatesh Srinivas static void vtpci_free_interrupts(struct vtpci_softc *); 14011447b59SVenkatesh Srinivas static void vtpci_free_virtqueues(struct vtpci_softc *); 14111447b59SVenkatesh Srinivas static void vtpci_release_child_resources(struct vtpci_softc *); 14211447b59SVenkatesh Srinivas static void vtpci_reset(struct vtpci_softc *); 14311447b59SVenkatesh Srinivas 14411447b59SVenkatesh Srinivas static int vtpci_legacy_intr(void *); 14511447b59SVenkatesh Srinivas static int vtpci_vq_shared_intr(void *); 14611447b59SVenkatesh Srinivas static int vtpci_vq_intr(void *); 14711447b59SVenkatesh Srinivas static int vtpci_config_intr(void *); 14811447b59SVenkatesh Srinivas 14911447b59SVenkatesh Srinivas /* 15011447b59SVenkatesh Srinivas * I/O port read/write wrappers. 15111447b59SVenkatesh Srinivas */ 15211447b59SVenkatesh Srinivas #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o)) 15311447b59SVenkatesh Srinivas #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o)) 15411447b59SVenkatesh Srinivas #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o)) 15511447b59SVenkatesh Srinivas #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v)) 15611447b59SVenkatesh Srinivas #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v)) 15711447b59SVenkatesh Srinivas #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v)) 15811447b59SVenkatesh Srinivas 15911447b59SVenkatesh Srinivas /* Tunables. */ 16011447b59SVenkatesh Srinivas static int vtpci_disable_msix = 0; 16111447b59SVenkatesh Srinivas TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 16211447b59SVenkatesh Srinivas 16311447b59SVenkatesh Srinivas static device_method_t vtpci_methods[] = { 16411447b59SVenkatesh Srinivas /* Device interface. */ 16511447b59SVenkatesh Srinivas DEVMETHOD(device_probe, vtpci_probe), 16611447b59SVenkatesh Srinivas DEVMETHOD(device_attach, vtpci_attach), 16711447b59SVenkatesh Srinivas DEVMETHOD(device_detach, vtpci_detach), 16811447b59SVenkatesh Srinivas DEVMETHOD(device_suspend, vtpci_suspend), 16911447b59SVenkatesh Srinivas DEVMETHOD(device_resume, vtpci_resume), 17011447b59SVenkatesh Srinivas DEVMETHOD(device_shutdown, vtpci_shutdown), 17111447b59SVenkatesh Srinivas 17211447b59SVenkatesh Srinivas /* Bus interface. */ 17311447b59SVenkatesh Srinivas DEVMETHOD(bus_driver_added, vtpci_driver_added), 17411447b59SVenkatesh Srinivas DEVMETHOD(bus_child_detached, vtpci_child_detached), 17511447b59SVenkatesh Srinivas DEVMETHOD(bus_read_ivar, vtpci_read_ivar), 17611447b59SVenkatesh Srinivas DEVMETHOD(bus_write_ivar, vtpci_write_ivar), 17711447b59SVenkatesh Srinivas 17811447b59SVenkatesh Srinivas /* VirtIO bus interface. */ 17911447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features), 18011447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature), 18111447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues), 18211447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr), 18311447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_stop, vtpci_stop), 18411447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit, vtpci_reinit), 18511447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete), 18611447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), 18711447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), 18811447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), 18911447b59SVenkatesh Srinivas 190d3c9c58eSSascha Wildner DEVMETHOD_END 19111447b59SVenkatesh Srinivas }; 19211447b59SVenkatesh Srinivas 19311447b59SVenkatesh Srinivas static driver_t vtpci_driver = { 19411447b59SVenkatesh Srinivas "virtio_pci", 19511447b59SVenkatesh Srinivas vtpci_methods, 19611447b59SVenkatesh Srinivas sizeof(struct vtpci_softc) 19711447b59SVenkatesh Srinivas }; 19811447b59SVenkatesh Srinivas 19911447b59SVenkatesh Srinivas devclass_t vtpci_devclass; 20011447b59SVenkatesh Srinivas 201dfc199f7SSascha Wildner DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, NULL, NULL); 20211447b59SVenkatesh Srinivas MODULE_VERSION(virtio_pci, 1); 20311447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 20411447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 20511447b59SVenkatesh Srinivas 20611447b59SVenkatesh Srinivas static int 20711447b59SVenkatesh Srinivas vtpci_probe(device_t dev) 20811447b59SVenkatesh Srinivas { 20911447b59SVenkatesh Srinivas char desc[36]; 21011447b59SVenkatesh Srinivas const char *name; 21111447b59SVenkatesh Srinivas 21211447b59SVenkatesh Srinivas if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID) 21311447b59SVenkatesh Srinivas return (ENXIO); 21411447b59SVenkatesh Srinivas 21511447b59SVenkatesh Srinivas if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN || 21611447b59SVenkatesh Srinivas pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX) 21711447b59SVenkatesh Srinivas return (ENXIO); 21811447b59SVenkatesh Srinivas 21911447b59SVenkatesh Srinivas if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION) 22011447b59SVenkatesh Srinivas return (ENXIO); 22111447b59SVenkatesh Srinivas 22211447b59SVenkatesh Srinivas name = virtio_device_name(pci_get_subdevice(dev)); 22311447b59SVenkatesh Srinivas if (name == NULL) 22411447b59SVenkatesh Srinivas name = "Unknown"; 22511447b59SVenkatesh Srinivas 22611447b59SVenkatesh Srinivas ksnprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name); 22711447b59SVenkatesh Srinivas device_set_desc_copy(dev, desc); 22811447b59SVenkatesh Srinivas 22911447b59SVenkatesh Srinivas return (BUS_PROBE_DEFAULT); 23011447b59SVenkatesh Srinivas } 23111447b59SVenkatesh Srinivas 23211447b59SVenkatesh Srinivas static int 23311447b59SVenkatesh Srinivas vtpci_attach(device_t dev) 23411447b59SVenkatesh Srinivas { 23511447b59SVenkatesh Srinivas struct vtpci_softc *sc; 23611447b59SVenkatesh Srinivas device_t child; 23711447b59SVenkatesh Srinivas int rid; 23811447b59SVenkatesh Srinivas 23911447b59SVenkatesh Srinivas sc = device_get_softc(dev); 24011447b59SVenkatesh Srinivas sc->vtpci_dev = dev; 24111447b59SVenkatesh Srinivas 24211447b59SVenkatesh Srinivas pci_enable_busmaster(dev); 24311447b59SVenkatesh Srinivas 24411447b59SVenkatesh Srinivas rid = PCIR_BAR(0); 24511447b59SVenkatesh Srinivas sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 24611447b59SVenkatesh Srinivas RF_ACTIVE); 24711447b59SVenkatesh Srinivas if (sc->vtpci_res == NULL) { 24811447b59SVenkatesh Srinivas device_printf(dev, "cannot map I/O space\n"); 24911447b59SVenkatesh Srinivas return (ENXIO); 25011447b59SVenkatesh Srinivas } 25111447b59SVenkatesh Srinivas 25211447b59SVenkatesh Srinivas if (pci_find_extcap(dev, PCIY_MSI, NULL) != 0) 25311447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSI; 25411447b59SVenkatesh Srinivas /* XXX(vsrinivas): Check out how to get MSI-X */ 2555d0ea777SSascha Wildner #ifdef OLD_MSI 25611447b59SVenkatesh Srinivas if (pci_find_extcap(dev, PCIY_MSIX, NULL) == 0) { 25711447b59SVenkatesh Srinivas rid = PCIR_BAR(1); 25811447b59SVenkatesh Srinivas sc->vtpci_msix_res = bus_alloc_resource_any(dev, 25911447b59SVenkatesh Srinivas SYS_RES_MEMORY, &rid, RF_ACTIVE); 26011447b59SVenkatesh Srinivas } 26111447b59SVenkatesh Srinivas #endif 26211447b59SVenkatesh Srinivas if (sc->vtpci_msix_res == NULL) 26311447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSIX; 26411447b59SVenkatesh Srinivas 26511447b59SVenkatesh Srinivas vtpci_reset(sc); 26611447b59SVenkatesh Srinivas 26711447b59SVenkatesh Srinivas /* Tell the host we've noticed this device. */ 26811447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 26911447b59SVenkatesh Srinivas 27011447b59SVenkatesh Srinivas if ((child = device_add_child(dev, NULL, -1)) == NULL) { 27111447b59SVenkatesh Srinivas device_printf(dev, "cannot create child device\n"); 27211447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 27311447b59SVenkatesh Srinivas vtpci_detach(dev); 27411447b59SVenkatesh Srinivas return (ENOMEM); 27511447b59SVenkatesh Srinivas } 27611447b59SVenkatesh Srinivas 27711447b59SVenkatesh Srinivas sc->vtpci_child_dev = child; 27811447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc); 27911447b59SVenkatesh Srinivas 28011447b59SVenkatesh Srinivas return (0); 28111447b59SVenkatesh Srinivas } 28211447b59SVenkatesh Srinivas 28311447b59SVenkatesh Srinivas static int 28411447b59SVenkatesh Srinivas vtpci_detach(device_t dev) 28511447b59SVenkatesh Srinivas { 28611447b59SVenkatesh Srinivas struct vtpci_softc *sc; 28711447b59SVenkatesh Srinivas device_t child; 28811447b59SVenkatesh Srinivas int error; 28911447b59SVenkatesh Srinivas 29011447b59SVenkatesh Srinivas sc = device_get_softc(dev); 29111447b59SVenkatesh Srinivas 29211447b59SVenkatesh Srinivas if ((child = sc->vtpci_child_dev) != NULL) { 29311447b59SVenkatesh Srinivas error = device_delete_child(dev, child); 29411447b59SVenkatesh Srinivas if (error) 29511447b59SVenkatesh Srinivas return (error); 29611447b59SVenkatesh Srinivas sc->vtpci_child_dev = NULL; 29711447b59SVenkatesh Srinivas } 29811447b59SVenkatesh Srinivas 29911447b59SVenkatesh Srinivas vtpci_reset(sc); 30011447b59SVenkatesh Srinivas 30111447b59SVenkatesh Srinivas if (sc->vtpci_msix_res != NULL) { 30211447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), 30311447b59SVenkatesh Srinivas sc->vtpci_msix_res); 30411447b59SVenkatesh Srinivas sc->vtpci_msix_res = NULL; 30511447b59SVenkatesh Srinivas } 30611447b59SVenkatesh Srinivas 30711447b59SVenkatesh Srinivas if (sc->vtpci_res != NULL) { 30811447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), 30911447b59SVenkatesh Srinivas sc->vtpci_res); 31011447b59SVenkatesh Srinivas sc->vtpci_res = NULL; 31111447b59SVenkatesh Srinivas } 31211447b59SVenkatesh Srinivas 31311447b59SVenkatesh Srinivas return (0); 31411447b59SVenkatesh Srinivas } 31511447b59SVenkatesh Srinivas 31611447b59SVenkatesh Srinivas static int 31711447b59SVenkatesh Srinivas vtpci_suspend(device_t dev) 31811447b59SVenkatesh Srinivas { 31911447b59SVenkatesh Srinivas 32011447b59SVenkatesh Srinivas return (bus_generic_suspend(dev)); 32111447b59SVenkatesh Srinivas } 32211447b59SVenkatesh Srinivas 32311447b59SVenkatesh Srinivas static int 32411447b59SVenkatesh Srinivas vtpci_resume(device_t dev) 32511447b59SVenkatesh Srinivas { 32611447b59SVenkatesh Srinivas 32711447b59SVenkatesh Srinivas return (bus_generic_resume(dev)); 32811447b59SVenkatesh Srinivas } 32911447b59SVenkatesh Srinivas 33011447b59SVenkatesh Srinivas static int 33111447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev) 33211447b59SVenkatesh Srinivas { 33311447b59SVenkatesh Srinivas 33411447b59SVenkatesh Srinivas (void) bus_generic_shutdown(dev); 33511447b59SVenkatesh Srinivas /* Forcibly stop the host device. */ 33611447b59SVenkatesh Srinivas vtpci_stop(dev); 33711447b59SVenkatesh Srinivas 33811447b59SVenkatesh Srinivas return (0); 33911447b59SVenkatesh Srinivas } 34011447b59SVenkatesh Srinivas 34111447b59SVenkatesh Srinivas static void 34211447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver) 34311447b59SVenkatesh Srinivas { 34411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 34511447b59SVenkatesh Srinivas 34611447b59SVenkatesh Srinivas sc = device_get_softc(dev); 34711447b59SVenkatesh Srinivas 34811447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc); 34911447b59SVenkatesh Srinivas } 35011447b59SVenkatesh Srinivas 35111447b59SVenkatesh Srinivas static void 35211447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child) 35311447b59SVenkatesh Srinivas { 35411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 35511447b59SVenkatesh Srinivas 35611447b59SVenkatesh Srinivas sc = device_get_softc(dev); 35711447b59SVenkatesh Srinivas 35811447b59SVenkatesh Srinivas vtpci_reset(sc); 35911447b59SVenkatesh Srinivas vtpci_release_child_resources(sc); 36011447b59SVenkatesh Srinivas } 36111447b59SVenkatesh Srinivas 36211447b59SVenkatesh Srinivas static int 36311447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 36411447b59SVenkatesh Srinivas { 36511447b59SVenkatesh Srinivas struct vtpci_softc *sc; 36611447b59SVenkatesh Srinivas 36711447b59SVenkatesh Srinivas sc = device_get_softc(dev); 36811447b59SVenkatesh Srinivas 36911447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child) 37011447b59SVenkatesh Srinivas return (ENOENT); 37111447b59SVenkatesh Srinivas 37211447b59SVenkatesh Srinivas switch (index) { 37311447b59SVenkatesh Srinivas case VIRTIO_IVAR_DEVTYPE: 37411447b59SVenkatesh Srinivas *result = pci_get_subdevice(dev); 37511447b59SVenkatesh Srinivas break; 37611447b59SVenkatesh Srinivas default: 37711447b59SVenkatesh Srinivas return (ENOENT); 37811447b59SVenkatesh Srinivas } 37911447b59SVenkatesh Srinivas 38011447b59SVenkatesh Srinivas return (0); 38111447b59SVenkatesh Srinivas } 38211447b59SVenkatesh Srinivas 38311447b59SVenkatesh Srinivas static int 38411447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 38511447b59SVenkatesh Srinivas { 38611447b59SVenkatesh Srinivas struct vtpci_softc *sc; 38711447b59SVenkatesh Srinivas 38811447b59SVenkatesh Srinivas sc = device_get_softc(dev); 38911447b59SVenkatesh Srinivas 39011447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child) 39111447b59SVenkatesh Srinivas return (ENOENT); 39211447b59SVenkatesh Srinivas 39311447b59SVenkatesh Srinivas switch (index) { 39411447b59SVenkatesh Srinivas case VIRTIO_IVAR_FEATURE_DESC: 39511447b59SVenkatesh Srinivas sc->vtpci_child_feat_desc = (void *) value; 39611447b59SVenkatesh Srinivas break; 39711447b59SVenkatesh Srinivas default: 39811447b59SVenkatesh Srinivas return (ENOENT); 39911447b59SVenkatesh Srinivas } 40011447b59SVenkatesh Srinivas 40111447b59SVenkatesh Srinivas return (0); 40211447b59SVenkatesh Srinivas } 40311447b59SVenkatesh Srinivas 40411447b59SVenkatesh Srinivas static uint64_t 40511447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features) 40611447b59SVenkatesh Srinivas { 40711447b59SVenkatesh Srinivas struct vtpci_softc *sc; 40811447b59SVenkatesh Srinivas uint64_t host_features, features; 40911447b59SVenkatesh Srinivas 41011447b59SVenkatesh Srinivas sc = device_get_softc(dev); 41111447b59SVenkatesh Srinivas 41211447b59SVenkatesh Srinivas host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES); 41311447b59SVenkatesh Srinivas vtpci_describe_features(sc, "host", host_features); 41411447b59SVenkatesh Srinivas 41511447b59SVenkatesh Srinivas /* 41611447b59SVenkatesh Srinivas * Limit negotiated features to what the driver, virtqueue, and 41711447b59SVenkatesh Srinivas * host all support. 41811447b59SVenkatesh Srinivas */ 41911447b59SVenkatesh Srinivas features = host_features & child_features; 42011447b59SVenkatesh Srinivas features = virtqueue_filter_features(features); 42111447b59SVenkatesh Srinivas sc->vtpci_features = features; 42211447b59SVenkatesh Srinivas 42311447b59SVenkatesh Srinivas vtpci_describe_features(sc, "negotiated", features); 42411447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features); 42511447b59SVenkatesh Srinivas 42611447b59SVenkatesh Srinivas return (features); 42711447b59SVenkatesh Srinivas } 42811447b59SVenkatesh Srinivas 42911447b59SVenkatesh Srinivas static int 43011447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature) 43111447b59SVenkatesh Srinivas { 43211447b59SVenkatesh Srinivas struct vtpci_softc *sc; 43311447b59SVenkatesh Srinivas 43411447b59SVenkatesh Srinivas sc = device_get_softc(dev); 43511447b59SVenkatesh Srinivas 43611447b59SVenkatesh Srinivas return ((sc->vtpci_features & feature) != 0); 43711447b59SVenkatesh Srinivas } 43811447b59SVenkatesh Srinivas 43911447b59SVenkatesh Srinivas static int 44011447b59SVenkatesh Srinivas vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, 44111447b59SVenkatesh Srinivas struct vq_alloc_info *vq_info) 44211447b59SVenkatesh Srinivas { 44311447b59SVenkatesh Srinivas struct vtpci_softc *sc; 44411447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 44511447b59SVenkatesh Srinivas struct vq_alloc_info *info; 44611447b59SVenkatesh Srinivas int queue, error; 44711447b59SVenkatesh Srinivas uint16_t vq_size; 44811447b59SVenkatesh Srinivas 44911447b59SVenkatesh Srinivas sc = device_get_softc(dev); 45011447b59SVenkatesh Srinivas 45111447b59SVenkatesh Srinivas if (sc->vtpci_nvqs != 0 || nvqs <= 0 || 45211447b59SVenkatesh Srinivas nvqs > VIRTIO_MAX_VIRTQUEUES) 45311447b59SVenkatesh Srinivas return (EINVAL); 45411447b59SVenkatesh Srinivas 45511447b59SVenkatesh Srinivas error = vtpci_alloc_interrupts(sc, flags, nvqs, vq_info); 45611447b59SVenkatesh Srinivas if (error) { 45711447b59SVenkatesh Srinivas device_printf(dev, "cannot allocate interrupts\n"); 45811447b59SVenkatesh Srinivas return (error); 45911447b59SVenkatesh Srinivas } 46011447b59SVenkatesh Srinivas 46111447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 46211447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 46311447b59SVenkatesh Srinivas VIRTIO_MSI_CONFIG_VECTOR, 0); 46411447b59SVenkatesh Srinivas if (error) 46511447b59SVenkatesh Srinivas return (error); 46611447b59SVenkatesh Srinivas } 46711447b59SVenkatesh Srinivas 46811447b59SVenkatesh Srinivas for (queue = 0; queue < nvqs; queue++) { 46911447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue]; 47011447b59SVenkatesh Srinivas info = &vq_info[queue]; 47111447b59SVenkatesh Srinivas 47211447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 47311447b59SVenkatesh Srinivas 47411447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 47511447b59SVenkatesh Srinivas error = virtqueue_alloc(dev, queue, vq_size, 47611447b59SVenkatesh Srinivas VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq); 47711447b59SVenkatesh Srinivas if (error) 47811447b59SVenkatesh Srinivas return (error); 47911447b59SVenkatesh Srinivas 48011447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 48111447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 48211447b59SVenkatesh Srinivas VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx); 48311447b59SVenkatesh Srinivas if (error) 48411447b59SVenkatesh Srinivas return (error); 48511447b59SVenkatesh Srinivas } 48611447b59SVenkatesh Srinivas 48711447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 48811447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 48911447b59SVenkatesh Srinivas 49011447b59SVenkatesh Srinivas *info->vqai_vq = vqx->vq; 49111447b59SVenkatesh Srinivas sc->vtpci_nvqs++; 49211447b59SVenkatesh Srinivas } 49311447b59SVenkatesh Srinivas 49411447b59SVenkatesh Srinivas return (0); 49511447b59SVenkatesh Srinivas } 49611447b59SVenkatesh Srinivas 49711447b59SVenkatesh Srinivas static int 4982f1382caSVenkatesh Srinivas vtpci_setup_intr(device_t dev, lwkt_serialize_t slz) 49911447b59SVenkatesh Srinivas { 50011447b59SVenkatesh Srinivas struct vtpci_softc *sc; 50111447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires; 50211447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 50311447b59SVenkatesh Srinivas int i, flags, error; 50411447b59SVenkatesh Srinivas 50511447b59SVenkatesh Srinivas sc = device_get_softc(dev); 50611447b59SVenkatesh Srinivas flags = INTR_MPSAFE; 50711447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[0]; 50811447b59SVenkatesh Srinivas 50911447b59SVenkatesh Srinivas if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) { 51011447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 5112f1382caSVenkatesh Srinivas (driver_intr_t *) vtpci_legacy_intr, 5122f1382caSVenkatesh Srinivas sc, &ires->intrhand, slz); 51311447b59SVenkatesh Srinivas return (error); 51411447b59SVenkatesh Srinivas } 51511447b59SVenkatesh Srinivas 5162f1382caSVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 5172f1382caSVenkatesh Srinivas (driver_intr_t *) vtpci_config_intr, 5182f1382caSVenkatesh Srinivas sc, &ires->intrhand, slz); 51911447b59SVenkatesh Srinivas if (error) 52011447b59SVenkatesh Srinivas return (error); 52111447b59SVenkatesh Srinivas 52211447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) { 52311447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[1]; 52411447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 5252f1382caSVenkatesh Srinivas (driver_intr_t *) vtpci_vq_shared_intr, 5262f1382caSVenkatesh Srinivas sc, &ires->intrhand, slz); 52711447b59SVenkatesh Srinivas 52811447b59SVenkatesh Srinivas return (error); 52911447b59SVenkatesh Srinivas } 53011447b59SVenkatesh Srinivas 53111447b59SVenkatesh Srinivas /* Setup an interrupt handler for each virtqueue. */ 53211447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nvqs; i++) { 53311447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i]; 53411447b59SVenkatesh Srinivas if (vqx->ires_idx < 1) 53511447b59SVenkatesh Srinivas continue; 53611447b59SVenkatesh Srinivas 53711447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[vqx->ires_idx]; 53811447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 5392f1382caSVenkatesh Srinivas (driver_intr_t *) vtpci_vq_intr, 5402f1382caSVenkatesh Srinivas vqx->vq, &ires->intrhand, slz); 54111447b59SVenkatesh Srinivas if (error) 54211447b59SVenkatesh Srinivas return (error); 54311447b59SVenkatesh Srinivas } 54411447b59SVenkatesh Srinivas 54511447b59SVenkatesh Srinivas return (0); 54611447b59SVenkatesh Srinivas } 54711447b59SVenkatesh Srinivas 54811447b59SVenkatesh Srinivas static void 54911447b59SVenkatesh Srinivas vtpci_stop(device_t dev) 55011447b59SVenkatesh Srinivas { 55111447b59SVenkatesh Srinivas vtpci_reset(device_get_softc(dev)); 55211447b59SVenkatesh Srinivas } 55311447b59SVenkatesh Srinivas 55411447b59SVenkatesh Srinivas static int 55511447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features) 55611447b59SVenkatesh Srinivas { 55711447b59SVenkatesh Srinivas struct vtpci_softc *sc; 55811447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 55911447b59SVenkatesh Srinivas struct virtqueue *vq; 56011447b59SVenkatesh Srinivas int queue, error; 56111447b59SVenkatesh Srinivas uint16_t vq_size; 56211447b59SVenkatesh Srinivas 56311447b59SVenkatesh Srinivas sc = device_get_softc(dev); 56411447b59SVenkatesh Srinivas 56511447b59SVenkatesh Srinivas /* 56611447b59SVenkatesh Srinivas * Redrive the device initialization. This is a bit of an abuse 56711447b59SVenkatesh Srinivas * of the specification, but both VirtualBox and QEMU/KVM seem 56811447b59SVenkatesh Srinivas * to play nice. We do not allow the host device to change from 56911447b59SVenkatesh Srinivas * what was originally negotiated beyond what the guest driver 57011447b59SVenkatesh Srinivas * changed (MSIX state should not change, number of virtqueues 57111447b59SVenkatesh Srinivas * and their size remain the same, etc). 57211447b59SVenkatesh Srinivas */ 57311447b59SVenkatesh Srinivas 57411447b59SVenkatesh Srinivas if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 57511447b59SVenkatesh Srinivas vtpci_stop(dev); 57611447b59SVenkatesh Srinivas 57711447b59SVenkatesh Srinivas /* 57811447b59SVenkatesh Srinivas * Quickly drive the status through ACK and DRIVER. The device 57911447b59SVenkatesh Srinivas * does not become usable again until vtpci_reinit_complete(). 58011447b59SVenkatesh Srinivas */ 58111447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 58211447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 58311447b59SVenkatesh Srinivas 58411447b59SVenkatesh Srinivas vtpci_negotiate_features(dev, features); 58511447b59SVenkatesh Srinivas 58611447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 58711447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 58811447b59SVenkatesh Srinivas VIRTIO_MSI_CONFIG_VECTOR, 0); 58911447b59SVenkatesh Srinivas if (error) 59011447b59SVenkatesh Srinivas return (error); 59111447b59SVenkatesh Srinivas } 59211447b59SVenkatesh Srinivas 59311447b59SVenkatesh Srinivas for (queue = 0; queue < sc->vtpci_nvqs; queue++) { 59411447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue]; 59511447b59SVenkatesh Srinivas vq = vqx->vq; 59611447b59SVenkatesh Srinivas 59711447b59SVenkatesh Srinivas KASSERT(vq != NULL, ("vq %d not allocated", queue)); 59811447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 59911447b59SVenkatesh Srinivas 60011447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 60111447b59SVenkatesh Srinivas error = virtqueue_reinit(vq, vq_size); 60211447b59SVenkatesh Srinivas if (error) 60311447b59SVenkatesh Srinivas return (error); 60411447b59SVenkatesh Srinivas 60511447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 60611447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 60711447b59SVenkatesh Srinivas VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx); 60811447b59SVenkatesh Srinivas if (error) 60911447b59SVenkatesh Srinivas return (error); 61011447b59SVenkatesh Srinivas } 61111447b59SVenkatesh Srinivas 61211447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 61311447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 61411447b59SVenkatesh Srinivas } 61511447b59SVenkatesh Srinivas 61611447b59SVenkatesh Srinivas return (0); 61711447b59SVenkatesh Srinivas } 61811447b59SVenkatesh Srinivas 61911447b59SVenkatesh Srinivas static void 62011447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev) 62111447b59SVenkatesh Srinivas { 62211447b59SVenkatesh Srinivas 62311447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 62411447b59SVenkatesh Srinivas } 62511447b59SVenkatesh Srinivas 62611447b59SVenkatesh Srinivas static void 62711447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue) 62811447b59SVenkatesh Srinivas { 62911447b59SVenkatesh Srinivas struct vtpci_softc *sc; 63011447b59SVenkatesh Srinivas 63111447b59SVenkatesh Srinivas sc = device_get_softc(dev); 63211447b59SVenkatesh Srinivas 63311447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue); 63411447b59SVenkatesh Srinivas } 63511447b59SVenkatesh Srinivas 63611447b59SVenkatesh Srinivas static uint8_t 63711447b59SVenkatesh Srinivas vtpci_get_status(device_t dev) 63811447b59SVenkatesh Srinivas { 63911447b59SVenkatesh Srinivas struct vtpci_softc *sc; 64011447b59SVenkatesh Srinivas 64111447b59SVenkatesh Srinivas sc = device_get_softc(dev); 64211447b59SVenkatesh Srinivas 64311447b59SVenkatesh Srinivas return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS)); 64411447b59SVenkatesh Srinivas } 64511447b59SVenkatesh Srinivas 64611447b59SVenkatesh Srinivas static void 64711447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status) 64811447b59SVenkatesh Srinivas { 64911447b59SVenkatesh Srinivas struct vtpci_softc *sc; 65011447b59SVenkatesh Srinivas 65111447b59SVenkatesh Srinivas sc = device_get_softc(dev); 65211447b59SVenkatesh Srinivas 65311447b59SVenkatesh Srinivas if (status != VIRTIO_CONFIG_STATUS_RESET) 65411447b59SVenkatesh Srinivas status |= vtpci_get_status(dev); 65511447b59SVenkatesh Srinivas 65611447b59SVenkatesh Srinivas vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status); 65711447b59SVenkatesh Srinivas } 65811447b59SVenkatesh Srinivas 65911447b59SVenkatesh Srinivas static void 66011447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset, 66111447b59SVenkatesh Srinivas void *dst, int length) 66211447b59SVenkatesh Srinivas { 66311447b59SVenkatesh Srinivas struct vtpci_softc *sc; 66411447b59SVenkatesh Srinivas bus_size_t off; 66511447b59SVenkatesh Srinivas uint8_t *d; 66611447b59SVenkatesh Srinivas int size; 66711447b59SVenkatesh Srinivas 66811447b59SVenkatesh Srinivas sc = device_get_softc(dev); 66911447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset; 67011447b59SVenkatesh Srinivas 67111447b59SVenkatesh Srinivas for (d = dst; length > 0; d += size, off += size, length -= size) { 67211447b59SVenkatesh Srinivas if (length >= 4) { 67311447b59SVenkatesh Srinivas size = 4; 67411447b59SVenkatesh Srinivas *(uint32_t *)d = vtpci_read_config_4(sc, off); 67511447b59SVenkatesh Srinivas } else if (length >= 2) { 67611447b59SVenkatesh Srinivas size = 2; 67711447b59SVenkatesh Srinivas *(uint16_t *)d = vtpci_read_config_2(sc, off); 67811447b59SVenkatesh Srinivas } else { 67911447b59SVenkatesh Srinivas size = 1; 68011447b59SVenkatesh Srinivas *d = vtpci_read_config_1(sc, off); 68111447b59SVenkatesh Srinivas } 68211447b59SVenkatesh Srinivas } 68311447b59SVenkatesh Srinivas } 68411447b59SVenkatesh Srinivas 68511447b59SVenkatesh Srinivas static void 68611447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset, 68711447b59SVenkatesh Srinivas void *src, int length) 68811447b59SVenkatesh Srinivas { 68911447b59SVenkatesh Srinivas struct vtpci_softc *sc; 69011447b59SVenkatesh Srinivas bus_size_t off; 69111447b59SVenkatesh Srinivas uint8_t *s; 69211447b59SVenkatesh Srinivas int size; 69311447b59SVenkatesh Srinivas 69411447b59SVenkatesh Srinivas sc = device_get_softc(dev); 69511447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset; 69611447b59SVenkatesh Srinivas 69711447b59SVenkatesh Srinivas for (s = src; length > 0; s += size, off += size, length -= size) { 69811447b59SVenkatesh Srinivas if (length >= 4) { 69911447b59SVenkatesh Srinivas size = 4; 70011447b59SVenkatesh Srinivas vtpci_write_config_4(sc, off, *(uint32_t *)s); 70111447b59SVenkatesh Srinivas } else if (length >= 2) { 70211447b59SVenkatesh Srinivas size = 2; 70311447b59SVenkatesh Srinivas vtpci_write_config_2(sc, off, *(uint16_t *)s); 70411447b59SVenkatesh Srinivas } else { 70511447b59SVenkatesh Srinivas size = 1; 70611447b59SVenkatesh Srinivas vtpci_write_config_1(sc, off, *s); 70711447b59SVenkatesh Srinivas } 70811447b59SVenkatesh Srinivas } 70911447b59SVenkatesh Srinivas } 71011447b59SVenkatesh Srinivas 71111447b59SVenkatesh Srinivas static void 71211447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg, 71311447b59SVenkatesh Srinivas uint64_t features) 71411447b59SVenkatesh Srinivas { 71511447b59SVenkatesh Srinivas device_t dev, child; 71611447b59SVenkatesh Srinivas 71711447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 71811447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 71911447b59SVenkatesh Srinivas 72011447b59SVenkatesh Srinivas if (device_is_attached(child) && bootverbose == 0) 72111447b59SVenkatesh Srinivas return; 72211447b59SVenkatesh Srinivas 72311447b59SVenkatesh Srinivas virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc); 72411447b59SVenkatesh Srinivas } 72511447b59SVenkatesh Srinivas 72611447b59SVenkatesh Srinivas static void 72711447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc) 72811447b59SVenkatesh Srinivas { 72911447b59SVenkatesh Srinivas device_t dev, child; 730*b17fe9c5SImre Vadasz int error; 73111447b59SVenkatesh Srinivas 73211447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 73311447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 73411447b59SVenkatesh Srinivas 73511447b59SVenkatesh Srinivas if (child == NULL) 73611447b59SVenkatesh Srinivas return; 73711447b59SVenkatesh Srinivas 73811447b59SVenkatesh Srinivas if (device_get_state(child) != DS_NOTPRESENT) 73911447b59SVenkatesh Srinivas return; 74011447b59SVenkatesh Srinivas 74111447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 742*b17fe9c5SImre Vadasz error = device_probe_and_attach(child); 743*b17fe9c5SImre Vadasz if (error != 0 || device_get_state(child) == DS_NOTPRESENT) { 74411447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 74511447b59SVenkatesh Srinivas vtpci_reset(sc); 74611447b59SVenkatesh Srinivas vtpci_release_child_resources(sc); 74711447b59SVenkatesh Srinivas 74811447b59SVenkatesh Srinivas /* Reset status for future attempt. */ 74911447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 75011447b59SVenkatesh Srinivas } else 75111447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 75211447b59SVenkatesh Srinivas } 75311447b59SVenkatesh Srinivas 75411447b59SVenkatesh Srinivas static int 75511447b59SVenkatesh Srinivas vtpci_alloc_interrupts(struct vtpci_softc *sc, int flags, int nvqs, 75611447b59SVenkatesh Srinivas struct vq_alloc_info *vq_info) 75711447b59SVenkatesh Srinivas { 75811447b59SVenkatesh Srinivas int i, nvectors, error; 75911447b59SVenkatesh Srinivas 76011447b59SVenkatesh Srinivas /* 76111447b59SVenkatesh Srinivas * Only allocate a vector for virtqueues that are actually 76211447b59SVenkatesh Srinivas * expecting an interrupt. 76311447b59SVenkatesh Srinivas */ 76411447b59SVenkatesh Srinivas for (nvectors = 0, i = 0; i < nvqs; i++) 76511447b59SVenkatesh Srinivas if (vq_info[i].vqai_intr != NULL) 76611447b59SVenkatesh Srinivas nvectors++; 76711447b59SVenkatesh Srinivas 76811447b59SVenkatesh Srinivas if (vtpci_disable_msix != 0 || 76911447b59SVenkatesh Srinivas sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSIX || 77011447b59SVenkatesh Srinivas flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX || 77111447b59SVenkatesh Srinivas vtpci_alloc_msix(sc, nvectors) != 0) { 77211447b59SVenkatesh Srinivas /* 77311447b59SVenkatesh Srinivas * Use MSI interrupts if available. Otherwise, we fallback 77411447b59SVenkatesh Srinivas * to legacy interrupts. 77511447b59SVenkatesh Srinivas */ 77611447b59SVenkatesh Srinivas if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSI) == 0 && 77711447b59SVenkatesh Srinivas vtpci_alloc_msi(sc) == 0) 77811447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI; 77911447b59SVenkatesh Srinivas 78011447b59SVenkatesh Srinivas sc->vtpci_nintr_res = 1; 78111447b59SVenkatesh Srinivas } 78211447b59SVenkatesh Srinivas 78311447b59SVenkatesh Srinivas error = vtpci_alloc_intr_resources(sc, nvqs, vq_info); 78411447b59SVenkatesh Srinivas 78511447b59SVenkatesh Srinivas return (error); 78611447b59SVenkatesh Srinivas } 78711447b59SVenkatesh Srinivas 78811447b59SVenkatesh Srinivas static int 78911447b59SVenkatesh Srinivas vtpci_alloc_intr_resources(struct vtpci_softc *sc, int nvqs, 79011447b59SVenkatesh Srinivas struct vq_alloc_info *vq_info) 79111447b59SVenkatesh Srinivas { 79211447b59SVenkatesh Srinivas device_t dev; 79311447b59SVenkatesh Srinivas struct resource *irq; 79411447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 79511447b59SVenkatesh Srinivas int i, rid, flags, res_idx; 79611447b59SVenkatesh Srinivas 79711447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 79811447b59SVenkatesh Srinivas flags = RF_ACTIVE; 79911447b59SVenkatesh Srinivas 80011447b59SVenkatesh Srinivas if ((sc->vtpci_flags & 80111447b59SVenkatesh Srinivas (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) == 0) { 80211447b59SVenkatesh Srinivas rid = 0; 80311447b59SVenkatesh Srinivas flags |= RF_SHAREABLE; 80411447b59SVenkatesh Srinivas } else 80511447b59SVenkatesh Srinivas rid = 1; 80611447b59SVenkatesh Srinivas 80711447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nintr_res; i++) { 80811447b59SVenkatesh Srinivas irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags); 80911447b59SVenkatesh Srinivas if (irq == NULL) 81011447b59SVenkatesh Srinivas return (ENXIO); 81111447b59SVenkatesh Srinivas 81211447b59SVenkatesh Srinivas sc->vtpci_intr_res[i].irq = irq; 81311447b59SVenkatesh Srinivas sc->vtpci_intr_res[i].rid = rid++; 81411447b59SVenkatesh Srinivas } 81511447b59SVenkatesh Srinivas 81611447b59SVenkatesh Srinivas /* 81711447b59SVenkatesh Srinivas * Map the virtqueue into the correct index in vq_intr_res[]. Note the 81811447b59SVenkatesh Srinivas * first index is reserved for configuration changes notifications. 81911447b59SVenkatesh Srinivas */ 82011447b59SVenkatesh Srinivas for (i = 0, res_idx = 1; i < nvqs; i++) { 82111447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i]; 82211447b59SVenkatesh Srinivas 82311447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 82411447b59SVenkatesh Srinivas if (vq_info[i].vqai_intr == NULL) 82511447b59SVenkatesh Srinivas vqx->ires_idx = -1; 82611447b59SVenkatesh Srinivas else if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) 82711447b59SVenkatesh Srinivas vqx->ires_idx = res_idx; 82811447b59SVenkatesh Srinivas else 82911447b59SVenkatesh Srinivas vqx->ires_idx = res_idx++; 83011447b59SVenkatesh Srinivas } else 83111447b59SVenkatesh Srinivas vqx->ires_idx = -1; 83211447b59SVenkatesh Srinivas } 83311447b59SVenkatesh Srinivas 83411447b59SVenkatesh Srinivas return (0); 83511447b59SVenkatesh Srinivas } 83611447b59SVenkatesh Srinivas 83711447b59SVenkatesh Srinivas static int 83811447b59SVenkatesh Srinivas vtpci_alloc_msi(struct vtpci_softc *sc) 83911447b59SVenkatesh Srinivas { 84011447b59SVenkatesh Srinivas device_t dev; 8419aa0ab36SSascha Wildner int nmsi; 84211447b59SVenkatesh Srinivas u_int irq_flags; 84311447b59SVenkatesh Srinivas 84411447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 84511447b59SVenkatesh Srinivas nmsi = pci_msi_count(dev); 84611447b59SVenkatesh Srinivas 84711447b59SVenkatesh Srinivas if (nmsi < 1) 84811447b59SVenkatesh Srinivas return (1); 84911447b59SVenkatesh Srinivas 85011447b59SVenkatesh Srinivas sc->vtpci_irq_rid = 0; 85111447b59SVenkatesh Srinivas sc->vtpci_irq_type = pci_alloc_1intr(dev, 1, 85211447b59SVenkatesh Srinivas &sc->vtpci_irq_rid, &irq_flags); 85311447b59SVenkatesh Srinivas 85411447b59SVenkatesh Srinivas 85511447b59SVenkatesh Srinivas return (1); 85611447b59SVenkatesh Srinivas } 85711447b59SVenkatesh Srinivas 85811447b59SVenkatesh Srinivas static int 85911447b59SVenkatesh Srinivas vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors) 86011447b59SVenkatesh Srinivas { 86111447b59SVenkatesh Srinivas /* XXX(vsrinivas): Huh? Is this how MSI-X works?*/ 86211447b59SVenkatesh Srinivas /* XXX(vsrinivas): All of this was disabled... */ 86311447b59SVenkatesh Srinivas #ifdef OLD_MSI 86411447b59SVenkatesh Srinivas device_t dev; 86511447b59SVenkatesh Srinivas int nmsix, cnt, required; 86611447b59SVenkatesh Srinivas 86711447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 86811447b59SVenkatesh Srinivas 86911447b59SVenkatesh Srinivas nmsix = pci_msix_count(dev); 87011447b59SVenkatesh Srinivas if (nmsix < 1) 87111447b59SVenkatesh Srinivas return (1); 87211447b59SVenkatesh Srinivas 87311447b59SVenkatesh Srinivas /* An additional vector is needed for the config changes. */ 87411447b59SVenkatesh Srinivas required = nvectors + 1; 87511447b59SVenkatesh Srinivas if (nmsix >= required) { 87611447b59SVenkatesh Srinivas cnt = required; 87711447b59SVenkatesh Srinivas if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) 87811447b59SVenkatesh Srinivas goto out; 87911447b59SVenkatesh Srinivas 88011447b59SVenkatesh Srinivas pci_release_msi(dev); 88111447b59SVenkatesh Srinivas } 88211447b59SVenkatesh Srinivas 88311447b59SVenkatesh Srinivas /* Attempt shared MSIX configuration. */ 88411447b59SVenkatesh Srinivas required = 2; 88511447b59SVenkatesh Srinivas if (nmsix >= required) { 88611447b59SVenkatesh Srinivas cnt = required; 88711447b59SVenkatesh Srinivas if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 88811447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_SHARED_MSIX; 88911447b59SVenkatesh Srinivas goto out; 89011447b59SVenkatesh Srinivas } 89111447b59SVenkatesh Srinivas 89211447b59SVenkatesh Srinivas pci_release_msi(dev); 89311447b59SVenkatesh Srinivas } 89411447b59SVenkatesh Srinivas 89511447b59SVenkatesh Srinivas return (1); 89611447b59SVenkatesh Srinivas 89711447b59SVenkatesh Srinivas out: 89811447b59SVenkatesh Srinivas sc->vtpci_nintr_res = required; 89911447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX; 90011447b59SVenkatesh Srinivas 90111447b59SVenkatesh Srinivas if (bootverbose) { 90211447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) 90311447b59SVenkatesh Srinivas device_printf(dev, "using shared virtqueue MSIX\n"); 90411447b59SVenkatesh Srinivas else 90511447b59SVenkatesh Srinivas device_printf(dev, "using per virtqueue MSIX\n"); 90611447b59SVenkatesh Srinivas } 90711447b59SVenkatesh Srinivas #endif 90811447b59SVenkatesh Srinivas return (0); 90911447b59SVenkatesh Srinivas } 91011447b59SVenkatesh Srinivas 91111447b59SVenkatesh Srinivas static int 91211447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx) 91311447b59SVenkatesh Srinivas { 91411447b59SVenkatesh Srinivas device_t dev; 91511447b59SVenkatesh Srinivas uint16_t vector; 91611447b59SVenkatesh Srinivas 91711447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 91811447b59SVenkatesh Srinivas 91911447b59SVenkatesh Srinivas if (offset != VIRTIO_MSI_CONFIG_VECTOR && 92011447b59SVenkatesh Srinivas offset != VIRTIO_MSI_QUEUE_VECTOR) 92111447b59SVenkatesh Srinivas return (EINVAL); 92211447b59SVenkatesh Srinivas 92311447b59SVenkatesh Srinivas if (res_idx != -1) { 92411447b59SVenkatesh Srinivas /* Map from rid to host vector. */ 92511447b59SVenkatesh Srinivas vector = sc->vtpci_intr_res[res_idx].rid - 1; 92611447b59SVenkatesh Srinivas } else 92711447b59SVenkatesh Srinivas vector = VIRTIO_MSI_NO_VECTOR; 92811447b59SVenkatesh Srinivas 92911447b59SVenkatesh Srinivas /* The first resource is special; make sure it is used correctly. */ 93011447b59SVenkatesh Srinivas if (res_idx == 0) { 93111447b59SVenkatesh Srinivas KASSERT(vector == 0, ("unexpected config vector")); 93211447b59SVenkatesh Srinivas KASSERT(offset == VIRTIO_MSI_CONFIG_VECTOR, 93311447b59SVenkatesh Srinivas ("unexpected config offset")); 93411447b59SVenkatesh Srinivas } 93511447b59SVenkatesh Srinivas 93611447b59SVenkatesh Srinivas vtpci_write_config_2(sc, offset, vector); 93711447b59SVenkatesh Srinivas 93811447b59SVenkatesh Srinivas if (vtpci_read_config_2(sc, offset) != vector) { 93911447b59SVenkatesh Srinivas device_printf(dev, "insufficient host resources for " 94011447b59SVenkatesh Srinivas "MSIX interrupts\n"); 94111447b59SVenkatesh Srinivas return (ENODEV); 94211447b59SVenkatesh Srinivas } 94311447b59SVenkatesh Srinivas 94411447b59SVenkatesh Srinivas return (0); 94511447b59SVenkatesh Srinivas } 94611447b59SVenkatesh Srinivas 94711447b59SVenkatesh Srinivas static void 94811447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc) 94911447b59SVenkatesh Srinivas { 95011447b59SVenkatesh Srinivas device_t dev; 95111447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires; 95211447b59SVenkatesh Srinivas int i; 95311447b59SVenkatesh Srinivas 95411447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 95511447b59SVenkatesh Srinivas sc->vtpci_nintr_res = 0; 95611447b59SVenkatesh Srinivas 95711447b59SVenkatesh Srinivas if (sc->vtpci_flags & (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) { 95811447b59SVenkatesh Srinivas pci_release_msi(dev); 95911447b59SVenkatesh Srinivas sc->vtpci_flags &= ~(VIRTIO_PCI_FLAG_MSI | 96011447b59SVenkatesh Srinivas VIRTIO_PCI_FLAG_MSIX | VIRTIO_PCI_FLAG_SHARED_MSIX); 96111447b59SVenkatesh Srinivas } 96211447b59SVenkatesh Srinivas 96311447b59SVenkatesh Srinivas for (i = 0; i < 1 + VIRTIO_MAX_VIRTQUEUES; i++) { 96411447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[i]; 96511447b59SVenkatesh Srinivas 96611447b59SVenkatesh Srinivas if (ires->intrhand != NULL) { 96711447b59SVenkatesh Srinivas bus_teardown_intr(dev, ires->irq, ires->intrhand); 96811447b59SVenkatesh Srinivas ires->intrhand = NULL; 96911447b59SVenkatesh Srinivas } 97011447b59SVenkatesh Srinivas 97111447b59SVenkatesh Srinivas if (ires->irq != NULL) { 97211447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IRQ, ires->rid, 97311447b59SVenkatesh Srinivas ires->irq); 97411447b59SVenkatesh Srinivas ires->irq = NULL; 97511447b59SVenkatesh Srinivas } 97611447b59SVenkatesh Srinivas 97711447b59SVenkatesh Srinivas ires->rid = -1; 97811447b59SVenkatesh Srinivas } 97911447b59SVenkatesh Srinivas } 98011447b59SVenkatesh Srinivas 98111447b59SVenkatesh Srinivas static void 98211447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc) 98311447b59SVenkatesh Srinivas { 98411447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 98511447b59SVenkatesh Srinivas int i; 98611447b59SVenkatesh Srinivas 98711447b59SVenkatesh Srinivas sc->vtpci_nvqs = 0; 98811447b59SVenkatesh Srinivas 98911447b59SVenkatesh Srinivas for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) { 99011447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i]; 99111447b59SVenkatesh Srinivas 99211447b59SVenkatesh Srinivas if (vqx->vq != NULL) { 99311447b59SVenkatesh Srinivas virtqueue_free(vqx->vq); 99411447b59SVenkatesh Srinivas vqx->vq = NULL; 99511447b59SVenkatesh Srinivas } 99611447b59SVenkatesh Srinivas } 99711447b59SVenkatesh Srinivas } 99811447b59SVenkatesh Srinivas 99911447b59SVenkatesh Srinivas static void 100011447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc) 100111447b59SVenkatesh Srinivas { 100211447b59SVenkatesh Srinivas 100311447b59SVenkatesh Srinivas vtpci_free_interrupts(sc); 100411447b59SVenkatesh Srinivas vtpci_free_virtqueues(sc); 100511447b59SVenkatesh Srinivas } 100611447b59SVenkatesh Srinivas 100711447b59SVenkatesh Srinivas static void 100811447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc) 100911447b59SVenkatesh Srinivas { 101011447b59SVenkatesh Srinivas 101111447b59SVenkatesh Srinivas /* 101211447b59SVenkatesh Srinivas * Setting the status to RESET sets the host device to 101311447b59SVenkatesh Srinivas * the original, uninitialized state. 101411447b59SVenkatesh Srinivas */ 101511447b59SVenkatesh Srinivas vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET); 101611447b59SVenkatesh Srinivas } 101711447b59SVenkatesh Srinivas 101811447b59SVenkatesh Srinivas static int 101911447b59SVenkatesh Srinivas vtpci_legacy_intr(void *xsc) 102011447b59SVenkatesh Srinivas { 102111447b59SVenkatesh Srinivas struct vtpci_softc *sc; 102211447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 102311447b59SVenkatesh Srinivas int i; 102411447b59SVenkatesh Srinivas uint8_t isr; 102511447b59SVenkatesh Srinivas 102611447b59SVenkatesh Srinivas sc = xsc; 102711447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[0]; 102811447b59SVenkatesh Srinivas 102911447b59SVenkatesh Srinivas /* Reading the ISR also clears it. */ 103011447b59SVenkatesh Srinivas isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR); 103111447b59SVenkatesh Srinivas 103211447b59SVenkatesh Srinivas if (isr & VIRTIO_PCI_ISR_CONFIG) 103311447b59SVenkatesh Srinivas vtpci_config_intr(sc); 103411447b59SVenkatesh Srinivas 103511447b59SVenkatesh Srinivas if (isr & VIRTIO_PCI_ISR_INTR) 103611447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) 103711447b59SVenkatesh Srinivas virtqueue_intr(vqx->vq); 103811447b59SVenkatesh Srinivas 103911447b59SVenkatesh Srinivas return isr; 104011447b59SVenkatesh Srinivas } 104111447b59SVenkatesh Srinivas 104211447b59SVenkatesh Srinivas static int 104311447b59SVenkatesh Srinivas vtpci_vq_shared_intr(void *xsc) 104411447b59SVenkatesh Srinivas { 104511447b59SVenkatesh Srinivas struct vtpci_softc *sc; 104611447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 104711447b59SVenkatesh Srinivas int i, rc; 104811447b59SVenkatesh Srinivas 104911447b59SVenkatesh Srinivas rc = 0; 105011447b59SVenkatesh Srinivas sc = xsc; 105111447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[0]; 105211447b59SVenkatesh Srinivas 105311447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) 105411447b59SVenkatesh Srinivas rc |= virtqueue_intr(vqx->vq); 105511447b59SVenkatesh Srinivas 105611447b59SVenkatesh Srinivas return rc; 105711447b59SVenkatesh Srinivas } 105811447b59SVenkatesh Srinivas 105911447b59SVenkatesh Srinivas static int 106011447b59SVenkatesh Srinivas vtpci_vq_intr(void *xvq) 106111447b59SVenkatesh Srinivas { 106211447b59SVenkatesh Srinivas struct virtqueue *vq; 106311447b59SVenkatesh Srinivas int rc; 106411447b59SVenkatesh Srinivas 106511447b59SVenkatesh Srinivas vq = xvq; 106611447b59SVenkatesh Srinivas rc = virtqueue_intr(vq); 106711447b59SVenkatesh Srinivas 106811447b59SVenkatesh Srinivas return rc; 106911447b59SVenkatesh Srinivas } 107011447b59SVenkatesh Srinivas 107111447b59SVenkatesh Srinivas static int 107211447b59SVenkatesh Srinivas vtpci_config_intr(void *xsc) 107311447b59SVenkatesh Srinivas { 107411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 107511447b59SVenkatesh Srinivas device_t child; 107611447b59SVenkatesh Srinivas int rc; 107711447b59SVenkatesh Srinivas 107811447b59SVenkatesh Srinivas rc = 0; 107911447b59SVenkatesh Srinivas sc = xsc; 108011447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 108111447b59SVenkatesh Srinivas 108211447b59SVenkatesh Srinivas if (child != NULL) 108311447b59SVenkatesh Srinivas rc = VIRTIO_CONFIG_CHANGE(child); 108411447b59SVenkatesh Srinivas 108511447b59SVenkatesh Srinivas return rc; 108611447b59SVenkatesh Srinivas } 1087