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