111447b59SVenkatesh Srinivas /*- 211447b59SVenkatesh Srinivas * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org> 311447b59SVenkatesh Srinivas * All rights reserved. 411447b59SVenkatesh Srinivas * 511447b59SVenkatesh Srinivas * Redistribution and use in source and binary forms, with or without 611447b59SVenkatesh Srinivas * modification, are permitted provided that the following conditions 711447b59SVenkatesh Srinivas * are met: 811447b59SVenkatesh Srinivas * 1. Redistributions of source code must retain the above copyright 911447b59SVenkatesh Srinivas * notice unmodified, this list of conditions, and the following 1011447b59SVenkatesh Srinivas * disclaimer. 1111447b59SVenkatesh Srinivas * 2. Redistributions in binary form must reproduce the above copyright 1211447b59SVenkatesh Srinivas * notice, this list of conditions and the following disclaimer in the 1311447b59SVenkatesh Srinivas * documentation and/or other materials provided with the distribution. 1411447b59SVenkatesh Srinivas * 1511447b59SVenkatesh Srinivas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1611447b59SVenkatesh Srinivas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1711447b59SVenkatesh Srinivas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1811447b59SVenkatesh Srinivas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1911447b59SVenkatesh Srinivas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2011447b59SVenkatesh Srinivas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2111447b59SVenkatesh Srinivas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2211447b59SVenkatesh Srinivas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2311447b59SVenkatesh Srinivas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2411447b59SVenkatesh Srinivas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2511447b59SVenkatesh Srinivas * 2611447b59SVenkatesh Srinivas * $FreeBSD: src/sys/dev/virtio/pci/virtio_pci.c,v 1.3 2012/04/14 05:48:04 grehan Exp $ 2711447b59SVenkatesh Srinivas */ 2811447b59SVenkatesh Srinivas 2911447b59SVenkatesh Srinivas /* Driver for the VirtIO PCI interface. */ 3011447b59SVenkatesh Srinivas 3111447b59SVenkatesh Srinivas #include <sys/param.h> 3211447b59SVenkatesh Srinivas #include <sys/systm.h> 3311447b59SVenkatesh Srinivas #include <sys/bus.h> 3411447b59SVenkatesh Srinivas #include <sys/kernel.h> 3511447b59SVenkatesh Srinivas #include <sys/module.h> 3611447b59SVenkatesh Srinivas #include <sys/malloc.h> 372f1382caSVenkatesh Srinivas #include <sys/serialize.h> 3811447b59SVenkatesh Srinivas 3911447b59SVenkatesh Srinivas #include <bus/pci/pcivar.h> 4011447b59SVenkatesh Srinivas #include <bus/pci/pcireg.h> 4111447b59SVenkatesh Srinivas 4211447b59SVenkatesh Srinivas #include <sys/rman.h> 4311447b59SVenkatesh Srinivas 44dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtio.h> 45dcbcbc86SVenkatesh Srinivas #include <dev/virtual/virtio/virtio/virtqueue.h> 4611447b59SVenkatesh Srinivas #include "virtio_pci.h" 47dcbcbc86SVenkatesh Srinivas #include "virtio_bus_if.h" 4811447b59SVenkatesh Srinivas 492f2405bbSImre Vadász struct vqentry { 502f2405bbSImre Vadász int what; 519d96478cSImre Vadász struct virtqueue *vq; 529d96478cSImre Vadász driver_intr_t *handler; 539d96478cSImre Vadász void *arg; 542f2405bbSImre Vadász TAILQ_ENTRY(vqentry) entries; 552f2405bbSImre Vadász }; 562f2405bbSImre Vadász 572f2405bbSImre Vadász TAILQ_HEAD(vqirq_list, vqentry); 582f2405bbSImre Vadász 5911447b59SVenkatesh Srinivas struct vtpci_softc { 6011447b59SVenkatesh Srinivas device_t vtpci_dev; 6111447b59SVenkatesh Srinivas struct resource *vtpci_res; 6211447b59SVenkatesh Srinivas struct resource *vtpci_msix_res; 6311447b59SVenkatesh Srinivas uint64_t vtpci_features; 6411447b59SVenkatesh Srinivas uint32_t vtpci_flags; 65c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSI 0x0001 66c8247d06SImre Vadász #define VIRTIO_PCI_FLAG_MSIX 0x0010 6711447b59SVenkatesh Srinivas 6811447b59SVenkatesh Srinivas device_t vtpci_child_dev; 6911447b59SVenkatesh Srinivas struct virtio_feature_desc *vtpci_child_feat_desc; 7011447b59SVenkatesh Srinivas 7111447b59SVenkatesh Srinivas /* 7211447b59SVenkatesh Srinivas * Ideally, each virtqueue that the driver provides a callback for 7311447b59SVenkatesh Srinivas * will receive its own MSIX vector. If there are not sufficient 7411447b59SVenkatesh Srinivas * vectors available, we will then attempt to have all the VQs 7511447b59SVenkatesh Srinivas * share one vector. Note that when using MSIX, the configuration 7611447b59SVenkatesh Srinivas * changed notifications must be on their own vector. 7711447b59SVenkatesh Srinivas * 7811447b59SVenkatesh Srinivas * If MSIX is not available, we will attempt to have the whole 7911447b59SVenkatesh Srinivas * device share one MSI vector, and then, finally, one legacy 8011447b59SVenkatesh Srinivas * interrupt. 8111447b59SVenkatesh Srinivas */ 8211447b59SVenkatesh Srinivas int vtpci_nvqs; 8311447b59SVenkatesh Srinivas struct vtpci_virtqueue { 8411447b59SVenkatesh Srinivas struct virtqueue *vq; 8511447b59SVenkatesh Srinivas 862f2405bbSImre Vadász /* Index into vtpci_intr_res[] below. -1 if no IRQ assigned. */ 8711447b59SVenkatesh Srinivas int ires_idx; 8811447b59SVenkatesh Srinivas } vtpci_vqx[VIRTIO_MAX_VIRTQUEUES]; 8911447b59SVenkatesh Srinivas 9011447b59SVenkatesh Srinivas /* 9111447b59SVenkatesh Srinivas * When using MSIX interrupts, the first element of vtpci_intr_res[] 9211447b59SVenkatesh Srinivas * is always the configuration changed notifications. The remaining 9311447b59SVenkatesh Srinivas * element(s) are used for the virtqueues. 9411447b59SVenkatesh Srinivas * 9511447b59SVenkatesh Srinivas * With MSI and legacy interrupts, only the first element of 9611447b59SVenkatesh Srinivas * vtpci_intr_res[] is used. 9711447b59SVenkatesh Srinivas */ 9811447b59SVenkatesh Srinivas int vtpci_nintr_res; 99c8247d06SImre Vadász int vtpci_irq_flags; 10011447b59SVenkatesh Srinivas struct vtpci_intr_resource { 1012f2405bbSImre Vadász struct vtpci_softc *ires_sc; 10211447b59SVenkatesh Srinivas struct resource *irq; 10311447b59SVenkatesh Srinivas int rid; 10411447b59SVenkatesh Srinivas void *intrhand; 1052f2405bbSImre Vadász struct vqirq_list ls; 10611447b59SVenkatesh Srinivas } vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES]; 1072f2405bbSImre Vadász 1082f2405bbSImre Vadász int vtpci_config_irq; 10911447b59SVenkatesh Srinivas }; 11011447b59SVenkatesh Srinivas 11111447b59SVenkatesh Srinivas static int vtpci_probe(device_t); 11211447b59SVenkatesh Srinivas static int vtpci_attach(device_t); 11311447b59SVenkatesh Srinivas static int vtpci_detach(device_t); 11411447b59SVenkatesh Srinivas static int vtpci_suspend(device_t); 11511447b59SVenkatesh Srinivas static int vtpci_resume(device_t); 11611447b59SVenkatesh Srinivas static int vtpci_shutdown(device_t); 11711447b59SVenkatesh Srinivas static void vtpci_driver_added(device_t, driver_t *); 11811447b59SVenkatesh Srinivas static void vtpci_child_detached(device_t, device_t); 11911447b59SVenkatesh Srinivas static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *); 12011447b59SVenkatesh Srinivas static int vtpci_write_ivar(device_t, device_t, int, uintptr_t); 12111447b59SVenkatesh Srinivas 12211447b59SVenkatesh Srinivas static uint64_t vtpci_negotiate_features(device_t, uint64_t); 12311447b59SVenkatesh Srinivas static int vtpci_with_feature(device_t, uint64_t); 1242f2405bbSImre Vadász static int vtpci_intr_count(device_t dev); 1252f2405bbSImre Vadász static int vtpci_intr_alloc(device_t dev, int *cnt, int use_config, 1262f2405bbSImre Vadász int *cpus); 1272f2405bbSImre Vadász static int vtpci_intr_release(device_t dev); 12811447b59SVenkatesh Srinivas static int vtpci_alloc_virtqueues(device_t, int, int, 12911447b59SVenkatesh Srinivas struct vq_alloc_info *); 1302f2405bbSImre Vadász static int vtpci_setup_intr(device_t, uint irq, lwkt_serialize_t); 1312f2405bbSImre Vadász static int vtpci_teardown_intr(device_t, uint irq); 1329d96478cSImre Vadász static int vtpci_bind_intr(device_t, uint, int, driver_intr_t, void *); 1332f2405bbSImre Vadász static int vtpci_unbind_intr(device_t, int); 13411447b59SVenkatesh Srinivas static void vtpci_stop(device_t); 13511447b59SVenkatesh Srinivas static int vtpci_reinit(device_t, uint64_t); 13611447b59SVenkatesh Srinivas static void vtpci_reinit_complete(device_t); 13711447b59SVenkatesh Srinivas static void vtpci_notify_virtqueue(device_t, uint16_t); 13811447b59SVenkatesh Srinivas static uint8_t vtpci_get_status(device_t); 13911447b59SVenkatesh Srinivas static void vtpci_set_status(device_t, uint8_t); 14011447b59SVenkatesh Srinivas static void vtpci_read_dev_config(device_t, bus_size_t, void *, int); 14111447b59SVenkatesh Srinivas static void vtpci_write_dev_config(device_t, bus_size_t, void *, int); 14211447b59SVenkatesh Srinivas 14311447b59SVenkatesh Srinivas static void vtpci_describe_features(struct vtpci_softc *, const char *, 14411447b59SVenkatesh Srinivas uint64_t); 14511447b59SVenkatesh Srinivas static void vtpci_probe_and_attach_child(struct vtpci_softc *); 14611447b59SVenkatesh Srinivas 14711447b59SVenkatesh Srinivas static int vtpci_register_msix_vector(struct vtpci_softc *, int, int); 14811447b59SVenkatesh Srinivas 14911447b59SVenkatesh Srinivas static void vtpci_free_interrupts(struct vtpci_softc *); 15011447b59SVenkatesh Srinivas static void vtpci_free_virtqueues(struct vtpci_softc *); 15111447b59SVenkatesh Srinivas static void vtpci_release_child_resources(struct vtpci_softc *); 15211447b59SVenkatesh Srinivas static void vtpci_reset(struct vtpci_softc *); 15311447b59SVenkatesh Srinivas 154df2df2fcSImre Vadász static void vtpci_legacy_intr(void *); 1552f2405bbSImre Vadász static void vtpci_msix_intr(void *); 15611447b59SVenkatesh Srinivas 15711447b59SVenkatesh Srinivas /* 15811447b59SVenkatesh Srinivas * I/O port read/write wrappers. 15911447b59SVenkatesh Srinivas */ 16011447b59SVenkatesh Srinivas #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o)) 16111447b59SVenkatesh Srinivas #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o)) 16211447b59SVenkatesh Srinivas #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o)) 16311447b59SVenkatesh Srinivas #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v)) 16411447b59SVenkatesh Srinivas #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v)) 16511447b59SVenkatesh Srinivas #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v)) 16611447b59SVenkatesh Srinivas 16711447b59SVenkatesh Srinivas /* Tunables. */ 16811447b59SVenkatesh Srinivas static int vtpci_disable_msix = 0; 16911447b59SVenkatesh Srinivas TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 17011447b59SVenkatesh Srinivas 17111447b59SVenkatesh Srinivas static device_method_t vtpci_methods[] = { 17211447b59SVenkatesh Srinivas /* Device interface. */ 17311447b59SVenkatesh Srinivas DEVMETHOD(device_probe, vtpci_probe), 17411447b59SVenkatesh Srinivas DEVMETHOD(device_attach, vtpci_attach), 17511447b59SVenkatesh Srinivas DEVMETHOD(device_detach, vtpci_detach), 17611447b59SVenkatesh Srinivas DEVMETHOD(device_suspend, vtpci_suspend), 17711447b59SVenkatesh Srinivas DEVMETHOD(device_resume, vtpci_resume), 17811447b59SVenkatesh Srinivas DEVMETHOD(device_shutdown, vtpci_shutdown), 17911447b59SVenkatesh Srinivas 18011447b59SVenkatesh Srinivas /* Bus interface. */ 18111447b59SVenkatesh Srinivas DEVMETHOD(bus_driver_added, vtpci_driver_added), 18211447b59SVenkatesh Srinivas DEVMETHOD(bus_child_detached, vtpci_child_detached), 18311447b59SVenkatesh Srinivas DEVMETHOD(bus_read_ivar, vtpci_read_ivar), 18411447b59SVenkatesh Srinivas DEVMETHOD(bus_write_ivar, vtpci_write_ivar), 18511447b59SVenkatesh Srinivas 18611447b59SVenkatesh Srinivas /* VirtIO bus interface. */ 18711447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features), 18811447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature), 1892f2405bbSImre Vadász DEVMETHOD(virtio_bus_intr_count, vtpci_intr_count), 1902f2405bbSImre Vadász DEVMETHOD(virtio_bus_intr_alloc, vtpci_intr_alloc), 1912f2405bbSImre Vadász DEVMETHOD(virtio_bus_intr_release, vtpci_intr_release), 19211447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues), 19311447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr), 1942f2405bbSImre Vadász DEVMETHOD(virtio_bus_teardown_intr, vtpci_teardown_intr), 1952f2405bbSImre Vadász DEVMETHOD(virtio_bus_bind_intr, vtpci_bind_intr), 1962f2405bbSImre Vadász DEVMETHOD(virtio_bus_unbind_intr, vtpci_unbind_intr), 19711447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_stop, vtpci_stop), 19811447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit, vtpci_reinit), 19911447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete), 20011447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), 20111447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), 20211447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), 20311447b59SVenkatesh Srinivas 204d3c9c58eSSascha Wildner DEVMETHOD_END 20511447b59SVenkatesh Srinivas }; 20611447b59SVenkatesh Srinivas 20711447b59SVenkatesh Srinivas static driver_t vtpci_driver = { 20811447b59SVenkatesh Srinivas "virtio_pci", 20911447b59SVenkatesh Srinivas vtpci_methods, 21011447b59SVenkatesh Srinivas sizeof(struct vtpci_softc) 21111447b59SVenkatesh Srinivas }; 21211447b59SVenkatesh Srinivas 21311447b59SVenkatesh Srinivas devclass_t vtpci_devclass; 21411447b59SVenkatesh Srinivas 215dfc199f7SSascha Wildner DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, NULL, NULL); 21611447b59SVenkatesh Srinivas MODULE_VERSION(virtio_pci, 1); 21711447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 21811447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 21911447b59SVenkatesh Srinivas 22011447b59SVenkatesh Srinivas static int 22111447b59SVenkatesh Srinivas vtpci_probe(device_t dev) 22211447b59SVenkatesh Srinivas { 22311447b59SVenkatesh Srinivas char desc[36]; 22411447b59SVenkatesh Srinivas const char *name; 22511447b59SVenkatesh Srinivas 22611447b59SVenkatesh Srinivas if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID) 22711447b59SVenkatesh Srinivas return (ENXIO); 22811447b59SVenkatesh Srinivas 22911447b59SVenkatesh Srinivas if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN || 23011447b59SVenkatesh Srinivas pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX) 23111447b59SVenkatesh Srinivas return (ENXIO); 23211447b59SVenkatesh Srinivas 23311447b59SVenkatesh Srinivas if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION) 23411447b59SVenkatesh Srinivas return (ENXIO); 23511447b59SVenkatesh Srinivas 23611447b59SVenkatesh Srinivas name = virtio_device_name(pci_get_subdevice(dev)); 23711447b59SVenkatesh Srinivas if (name == NULL) 23811447b59SVenkatesh Srinivas name = "Unknown"; 23911447b59SVenkatesh Srinivas 24011447b59SVenkatesh Srinivas ksnprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name); 24111447b59SVenkatesh Srinivas device_set_desc_copy(dev, desc); 24211447b59SVenkatesh Srinivas 24311447b59SVenkatesh Srinivas return (BUS_PROBE_DEFAULT); 24411447b59SVenkatesh Srinivas } 24511447b59SVenkatesh Srinivas 24611447b59SVenkatesh Srinivas static int 24711447b59SVenkatesh Srinivas vtpci_attach(device_t dev) 24811447b59SVenkatesh Srinivas { 24911447b59SVenkatesh Srinivas struct vtpci_softc *sc; 25011447b59SVenkatesh Srinivas device_t child; 251c8247d06SImre Vadász int msix_cap, rid; 25211447b59SVenkatesh Srinivas 25311447b59SVenkatesh Srinivas sc = device_get_softc(dev); 25411447b59SVenkatesh Srinivas sc->vtpci_dev = dev; 2552f2405bbSImre Vadász sc->vtpci_config_irq = -1; 25611447b59SVenkatesh Srinivas 25711447b59SVenkatesh Srinivas pci_enable_busmaster(dev); 25811447b59SVenkatesh Srinivas 25911447b59SVenkatesh Srinivas rid = PCIR_BAR(0); 26011447b59SVenkatesh Srinivas sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 26111447b59SVenkatesh Srinivas RF_ACTIVE); 26211447b59SVenkatesh Srinivas if (sc->vtpci_res == NULL) { 26311447b59SVenkatesh Srinivas device_printf(dev, "cannot map I/O space\n"); 26411447b59SVenkatesh Srinivas return (ENXIO); 26511447b59SVenkatesh Srinivas } 26611447b59SVenkatesh Srinivas 267c8247d06SImre Vadász if (pci_find_extcap(dev, PCIY_MSIX, &msix_cap) == 0) { 268c8247d06SImre Vadász uint32_t val; 269c8247d06SImre Vadász val = pci_read_config(dev, msix_cap + PCIR_MSIX_TABLE, 4); 270c8247d06SImre Vadász rid = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); 27111447b59SVenkatesh Srinivas sc->vtpci_msix_res = bus_alloc_resource_any(dev, 27211447b59SVenkatesh Srinivas SYS_RES_MEMORY, &rid, RF_ACTIVE); 27311447b59SVenkatesh Srinivas } 27411447b59SVenkatesh Srinivas 27511447b59SVenkatesh Srinivas vtpci_reset(sc); 27611447b59SVenkatesh Srinivas 27711447b59SVenkatesh Srinivas /* Tell the host we've noticed this device. */ 27811447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 27911447b59SVenkatesh Srinivas 28011447b59SVenkatesh Srinivas if ((child = device_add_child(dev, NULL, -1)) == NULL) { 28111447b59SVenkatesh Srinivas device_printf(dev, "cannot create child device\n"); 28211447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 28311447b59SVenkatesh Srinivas vtpci_detach(dev); 28411447b59SVenkatesh Srinivas return (ENOMEM); 28511447b59SVenkatesh Srinivas } 28611447b59SVenkatesh Srinivas 28711447b59SVenkatesh Srinivas sc->vtpci_child_dev = child; 28811447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc); 28911447b59SVenkatesh Srinivas 29011447b59SVenkatesh Srinivas return (0); 29111447b59SVenkatesh Srinivas } 29211447b59SVenkatesh Srinivas 29311447b59SVenkatesh Srinivas static int 29411447b59SVenkatesh Srinivas vtpci_detach(device_t dev) 29511447b59SVenkatesh Srinivas { 29611447b59SVenkatesh Srinivas struct vtpci_softc *sc; 29711447b59SVenkatesh Srinivas device_t child; 29811447b59SVenkatesh Srinivas int error; 29911447b59SVenkatesh Srinivas 30011447b59SVenkatesh Srinivas sc = device_get_softc(dev); 30111447b59SVenkatesh Srinivas 30211447b59SVenkatesh Srinivas if ((child = sc->vtpci_child_dev) != NULL) { 30311447b59SVenkatesh Srinivas error = device_delete_child(dev, child); 30411447b59SVenkatesh Srinivas if (error) 30511447b59SVenkatesh Srinivas return (error); 30611447b59SVenkatesh Srinivas sc->vtpci_child_dev = NULL; 30711447b59SVenkatesh Srinivas } 30811447b59SVenkatesh Srinivas 30911447b59SVenkatesh Srinivas vtpci_reset(sc); 31011447b59SVenkatesh Srinivas 31111447b59SVenkatesh Srinivas if (sc->vtpci_msix_res != NULL) { 312c8247d06SImre Vadász bus_release_resource(dev, SYS_RES_MEMORY, 313c8247d06SImre Vadász rman_get_rid(sc->vtpci_msix_res), sc->vtpci_msix_res); 31411447b59SVenkatesh Srinivas sc->vtpci_msix_res = NULL; 31511447b59SVenkatesh Srinivas } 31611447b59SVenkatesh Srinivas 31711447b59SVenkatesh Srinivas if (sc->vtpci_res != NULL) { 31811447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), 31911447b59SVenkatesh Srinivas sc->vtpci_res); 32011447b59SVenkatesh Srinivas sc->vtpci_res = NULL; 32111447b59SVenkatesh Srinivas } 32211447b59SVenkatesh Srinivas 32311447b59SVenkatesh Srinivas return (0); 32411447b59SVenkatesh Srinivas } 32511447b59SVenkatesh Srinivas 32611447b59SVenkatesh Srinivas static int 32711447b59SVenkatesh Srinivas vtpci_suspend(device_t dev) 32811447b59SVenkatesh Srinivas { 32911447b59SVenkatesh Srinivas 33011447b59SVenkatesh Srinivas return (bus_generic_suspend(dev)); 33111447b59SVenkatesh Srinivas } 33211447b59SVenkatesh Srinivas 33311447b59SVenkatesh Srinivas static int 33411447b59SVenkatesh Srinivas vtpci_resume(device_t dev) 33511447b59SVenkatesh Srinivas { 33611447b59SVenkatesh Srinivas 33711447b59SVenkatesh Srinivas return (bus_generic_resume(dev)); 33811447b59SVenkatesh Srinivas } 33911447b59SVenkatesh Srinivas 34011447b59SVenkatesh Srinivas static int 34111447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev) 34211447b59SVenkatesh Srinivas { 34311447b59SVenkatesh Srinivas 34411447b59SVenkatesh Srinivas (void) bus_generic_shutdown(dev); 34511447b59SVenkatesh Srinivas /* Forcibly stop the host device. */ 34611447b59SVenkatesh Srinivas vtpci_stop(dev); 34711447b59SVenkatesh Srinivas 34811447b59SVenkatesh Srinivas return (0); 34911447b59SVenkatesh Srinivas } 35011447b59SVenkatesh Srinivas 35111447b59SVenkatesh Srinivas static void 35211447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver) 35311447b59SVenkatesh Srinivas { 35411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 35511447b59SVenkatesh Srinivas 35611447b59SVenkatesh Srinivas sc = device_get_softc(dev); 35711447b59SVenkatesh Srinivas 35811447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc); 35911447b59SVenkatesh Srinivas } 36011447b59SVenkatesh Srinivas 36111447b59SVenkatesh Srinivas static void 36211447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child) 36311447b59SVenkatesh Srinivas { 36411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 36511447b59SVenkatesh Srinivas 36611447b59SVenkatesh Srinivas sc = device_get_softc(dev); 36711447b59SVenkatesh Srinivas 36811447b59SVenkatesh Srinivas vtpci_reset(sc); 36911447b59SVenkatesh Srinivas vtpci_release_child_resources(sc); 37011447b59SVenkatesh Srinivas } 37111447b59SVenkatesh Srinivas 37211447b59SVenkatesh Srinivas static int 37311447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 37411447b59SVenkatesh Srinivas { 37511447b59SVenkatesh Srinivas struct vtpci_softc *sc; 37611447b59SVenkatesh Srinivas 37711447b59SVenkatesh Srinivas sc = device_get_softc(dev); 37811447b59SVenkatesh Srinivas 37911447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child) 38011447b59SVenkatesh Srinivas return (ENOENT); 38111447b59SVenkatesh Srinivas 38211447b59SVenkatesh Srinivas switch (index) { 38311447b59SVenkatesh Srinivas case VIRTIO_IVAR_DEVTYPE: 38411447b59SVenkatesh Srinivas *result = pci_get_subdevice(dev); 38511447b59SVenkatesh Srinivas break; 38611447b59SVenkatesh Srinivas default: 38711447b59SVenkatesh Srinivas return (ENOENT); 38811447b59SVenkatesh Srinivas } 38911447b59SVenkatesh Srinivas 39011447b59SVenkatesh Srinivas return (0); 39111447b59SVenkatesh Srinivas } 39211447b59SVenkatesh Srinivas 39311447b59SVenkatesh Srinivas static int 39411447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 39511447b59SVenkatesh Srinivas { 39611447b59SVenkatesh Srinivas struct vtpci_softc *sc; 39711447b59SVenkatesh Srinivas 39811447b59SVenkatesh Srinivas sc = device_get_softc(dev); 39911447b59SVenkatesh Srinivas 40011447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child) 40111447b59SVenkatesh Srinivas return (ENOENT); 40211447b59SVenkatesh Srinivas 40311447b59SVenkatesh Srinivas switch (index) { 40411447b59SVenkatesh Srinivas case VIRTIO_IVAR_FEATURE_DESC: 40511447b59SVenkatesh Srinivas sc->vtpci_child_feat_desc = (void *) value; 40611447b59SVenkatesh Srinivas break; 40711447b59SVenkatesh Srinivas default: 40811447b59SVenkatesh Srinivas return (ENOENT); 40911447b59SVenkatesh Srinivas } 41011447b59SVenkatesh Srinivas 41111447b59SVenkatesh Srinivas return (0); 41211447b59SVenkatesh Srinivas } 41311447b59SVenkatesh Srinivas 41411447b59SVenkatesh Srinivas static uint64_t 41511447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features) 41611447b59SVenkatesh Srinivas { 41711447b59SVenkatesh Srinivas struct vtpci_softc *sc; 41811447b59SVenkatesh Srinivas uint64_t host_features, features; 41911447b59SVenkatesh Srinivas 42011447b59SVenkatesh Srinivas sc = device_get_softc(dev); 42111447b59SVenkatesh Srinivas 42211447b59SVenkatesh Srinivas host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES); 42311447b59SVenkatesh Srinivas vtpci_describe_features(sc, "host", host_features); 42411447b59SVenkatesh Srinivas 42511447b59SVenkatesh Srinivas /* 42611447b59SVenkatesh Srinivas * Limit negotiated features to what the driver, virtqueue, and 42711447b59SVenkatesh Srinivas * host all support. 42811447b59SVenkatesh Srinivas */ 42911447b59SVenkatesh Srinivas features = host_features & child_features; 43011447b59SVenkatesh Srinivas features = virtqueue_filter_features(features); 43111447b59SVenkatesh Srinivas sc->vtpci_features = features; 43211447b59SVenkatesh Srinivas 43311447b59SVenkatesh Srinivas vtpci_describe_features(sc, "negotiated", features); 43411447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features); 43511447b59SVenkatesh Srinivas 43611447b59SVenkatesh Srinivas return (features); 43711447b59SVenkatesh Srinivas } 43811447b59SVenkatesh Srinivas 43911447b59SVenkatesh Srinivas static int 44011447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature) 44111447b59SVenkatesh Srinivas { 44211447b59SVenkatesh Srinivas struct vtpci_softc *sc; 44311447b59SVenkatesh Srinivas 44411447b59SVenkatesh Srinivas sc = device_get_softc(dev); 44511447b59SVenkatesh Srinivas 44611447b59SVenkatesh Srinivas return ((sc->vtpci_features & feature) != 0); 44711447b59SVenkatesh Srinivas } 44811447b59SVenkatesh Srinivas 44911447b59SVenkatesh Srinivas static int 4502f2405bbSImre Vadász vtpci_intr_count(device_t dev) 4512f2405bbSImre Vadász { 4522f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev); 4532f2405bbSImre Vadász 4542f2405bbSImre Vadász if (vtpci_disable_msix != 0 || sc->vtpci_msix_res == NULL) 4552f2405bbSImre Vadász return 1; 4562f2405bbSImre Vadász else 4572f2405bbSImre Vadász return pci_msix_count(dev); 4582f2405bbSImre Vadász } 4592f2405bbSImre Vadász 4602f2405bbSImre Vadász /* Will never return 0, with *cnt <= 0. */ 4612f2405bbSImre Vadász static int 4622f2405bbSImre Vadász vtpci_intr_alloc(device_t dev, int *cnt, int use_config, int *cpus) 4632f2405bbSImre Vadász { 4642f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev); 4652f2405bbSImre Vadász int i; 4662f2405bbSImre Vadász 4672f2405bbSImre Vadász if (sc->vtpci_nintr_res > 0) 4682f2405bbSImre Vadász return (EINVAL); 4692f2405bbSImre Vadász 4702f2405bbSImre Vadász if (*cnt <= 0) 4712f2405bbSImre Vadász return (EINVAL); 4722f2405bbSImre Vadász 4732f2405bbSImre Vadász if (vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) { 4742f2405bbSImre Vadász int nmsix = pci_msix_count(dev); 4752f2405bbSImre Vadász if (nmsix < *cnt) 4762f2405bbSImre Vadász *cnt = nmsix; 4772f2405bbSImre Vadász } 4782f2405bbSImre Vadász 4792f2405bbSImre Vadász if ((*cnt > 1 || use_config == 0) && 4802f2405bbSImre Vadász vtpci_disable_msix == 0 && sc->vtpci_msix_res != NULL) { 4812f2405bbSImre Vadász if (pci_setup_msix(dev) != 0) { 4822f2405bbSImre Vadász device_printf(dev, "pci_setup_msix failed\n"); 4832f2405bbSImre Vadász /* Just fallthrough to legacy IRQ code instead. */ 4842f2405bbSImre Vadász } else { 4852f2405bbSImre Vadász for (i = 0; i < *cnt; i++) { 4862f2405bbSImre Vadász int cpu, rid; 4872f2405bbSImre Vadász 4882f2405bbSImre Vadász if (cpus != NULL && cpus[i] >= 0 && 4892f2405bbSImre Vadász cpus[i] < ncpus) { 4902f2405bbSImre Vadász cpu = cpus[i]; 4912f2405bbSImre Vadász } else { 4922f2405bbSImre Vadász cpu = device_get_unit(dev) + i; 4932f2405bbSImre Vadász cpu %= ncpus; 4942f2405bbSImre Vadász } 4952f2405bbSImre Vadász if (pci_alloc_msix_vector(dev, i, &rid, cpu) 4962f2405bbSImre Vadász != 0) { 4972f2405bbSImre Vadász if (i > 1 || (i == 1 && !use_config)) { 4982f2405bbSImre Vadász *cnt = i; 4992f2405bbSImre Vadász /* Got some MSI-X vectors. */ 5002f2405bbSImre Vadász sc->vtpci_irq_flags = RF_ACTIVE; 5012f2405bbSImre Vadász sc->vtpci_flags |= 5022f2405bbSImre Vadász VIRTIO_PCI_FLAG_MSIX; 5032f2405bbSImre Vadász goto finish; 5042f2405bbSImre Vadász } 5052f2405bbSImre Vadász /* 5062f2405bbSImre Vadász * Allocate the legacy IRQ instead. 5072f2405bbSImre Vadász */ 5082f2405bbSImre Vadász if (i == 1) { 5092f2405bbSImre Vadász pci_release_msix_vector(dev, 0); 5102f2405bbSImre Vadász } 5112f2405bbSImre Vadász pci_teardown_msix(dev); 5122f2405bbSImre Vadász break; 5132f2405bbSImre Vadász } 5142f2405bbSImre Vadász sc->vtpci_intr_res[i].rid = rid; 5152f2405bbSImre Vadász } 5162f2405bbSImre Vadász /* Got all the MSI-X vectors we wanted. */ 5172f2405bbSImre Vadász sc->vtpci_irq_flags = RF_ACTIVE; 5182f2405bbSImre Vadász sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX; 5192f2405bbSImre Vadász /* Successfully allocated all MSI-X vectors */ 5202f2405bbSImre Vadász goto finish; 5212f2405bbSImre Vadász } 5222f2405bbSImre Vadász } 5232f2405bbSImre Vadász 5242f2405bbSImre Vadász /* Legacy IRQ code: */ 5252f2405bbSImre Vadász *cnt = 1; 5262f2405bbSImre Vadász /* 5272f2405bbSImre Vadász * Use MSI interrupts if available. Otherwise, we fallback 5282f2405bbSImre Vadász * to legacy interrupts. 5292f2405bbSImre Vadász */ 5302f2405bbSImre Vadász sc->vtpci_intr_res[0].rid = 0; 5312f2405bbSImre Vadász if (pci_alloc_1intr(sc->vtpci_dev, 1, 5322f2405bbSImre Vadász &sc->vtpci_intr_res[0].rid, 5332f2405bbSImre Vadász &sc->vtpci_irq_flags) == PCI_INTR_TYPE_MSI) { 5342f2405bbSImre Vadász sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI; 5352f2405bbSImre Vadász } 5362f2405bbSImre Vadász 5372f2405bbSImre Vadász finish: 5382f2405bbSImre Vadász KKASSERT(!((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) != 0 && 5392f2405bbSImre Vadász (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) != 0)); 5402f2405bbSImre Vadász 5412f2405bbSImre Vadász sc->vtpci_nintr_res = *cnt; 5422f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) { 5432f2405bbSImre Vadász struct resource *irq; 5442f2405bbSImre Vadász 5452f2405bbSImre Vadász TAILQ_INIT(&sc->vtpci_intr_res[i].ls); 5462f2405bbSImre Vadász sc->vtpci_intr_res[i].ires_sc = sc; 5472f2405bbSImre Vadász irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 5482f2405bbSImre Vadász &sc->vtpci_intr_res[i].rid, sc->vtpci_irq_flags); 5492f2405bbSImre Vadász if (irq == NULL) 5502f2405bbSImre Vadász return (ENXIO); 5512f2405bbSImre Vadász if (cpus != NULL) 5522f2405bbSImre Vadász cpus[i] = rman_get_cpuid(irq); 5532f2405bbSImre Vadász 5542f2405bbSImre Vadász sc->vtpci_intr_res[i].irq = irq; 5552f2405bbSImre Vadász } 5562f2405bbSImre Vadász 5572f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 5582f2405bbSImre Vadász device_printf(dev, "using %d MSI-X vectors\n", *cnt); 5592f2405bbSImre Vadász pci_enable_msix(dev); 5602f2405bbSImre Vadász } 5612f2405bbSImre Vadász 5622f2405bbSImre Vadász return (0); 5632f2405bbSImre Vadász } 5642f2405bbSImre Vadász 5652f2405bbSImre Vadász static int 5662f2405bbSImre Vadász vtpci_intr_release(device_t dev) 5672f2405bbSImre Vadász { 5682f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev); 5692f2405bbSImre Vadász struct vtpci_intr_resource *ires; 5702f2405bbSImre Vadász int i; 5712f2405bbSImre Vadász 5722f2405bbSImre Vadász if (sc->vtpci_nintr_res == 0) 5732f2405bbSImre Vadász return (EINVAL); 5742f2405bbSImre Vadász 5752f2405bbSImre Vadász /* XXX Make sure none of the interrupts is used at the moment. */ 5762f2405bbSImre Vadász 5772f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) { 5782f2405bbSImre Vadász ires = &sc->vtpci_intr_res[i]; 5792f2405bbSImre Vadász 5802f2405bbSImre Vadász KKASSERT(TAILQ_EMPTY(&ires->ls)); 5812f2405bbSImre Vadász if (ires->irq != NULL) { 5822f2405bbSImre Vadász bus_release_resource(dev, SYS_RES_IRQ, ires->rid, 5832f2405bbSImre Vadász ires->irq); 5842f2405bbSImre Vadász ires->irq = NULL; 5852f2405bbSImre Vadász } 5862f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) 5872f2405bbSImre Vadász pci_release_msix_vector(dev, ires->rid); 5882f2405bbSImre Vadász ires->rid = 0; 5892f2405bbSImre Vadász } 5902f2405bbSImre Vadász sc->vtpci_nintr_res = 0; 5912f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) { 5922f2405bbSImre Vadász pci_release_msi(dev); 5932f2405bbSImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI; 5942f2405bbSImre Vadász } 5952f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 5962f2405bbSImre Vadász pci_teardown_msix(dev); 5972f2405bbSImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX; 5982f2405bbSImre Vadász } 5992f2405bbSImre Vadász return (0); 6002f2405bbSImre Vadász } 6012f2405bbSImre Vadász 6022f2405bbSImre Vadász static int 60311447b59SVenkatesh Srinivas vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, 60411447b59SVenkatesh Srinivas struct vq_alloc_info *vq_info) 60511447b59SVenkatesh Srinivas { 60611447b59SVenkatesh Srinivas struct vtpci_softc *sc; 60711447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 60811447b59SVenkatesh Srinivas struct vq_alloc_info *info; 60911447b59SVenkatesh Srinivas int queue, error; 61011447b59SVenkatesh Srinivas uint16_t vq_size; 61111447b59SVenkatesh Srinivas 61211447b59SVenkatesh Srinivas sc = device_get_softc(dev); 61311447b59SVenkatesh Srinivas 61411447b59SVenkatesh Srinivas if (sc->vtpci_nvqs != 0 || nvqs <= 0 || 61511447b59SVenkatesh Srinivas nvqs > VIRTIO_MAX_VIRTQUEUES) 61611447b59SVenkatesh Srinivas return (EINVAL); 61711447b59SVenkatesh Srinivas 61811447b59SVenkatesh Srinivas for (queue = 0; queue < nvqs; queue++) { 61911447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue]; 62011447b59SVenkatesh Srinivas info = &vq_info[queue]; 62111447b59SVenkatesh Srinivas 6222f2405bbSImre Vadász vqx->ires_idx = -1; 62311447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 62411447b59SVenkatesh Srinivas 62511447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 62611447b59SVenkatesh Srinivas error = virtqueue_alloc(dev, queue, vq_size, 62711447b59SVenkatesh Srinivas VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq); 62811447b59SVenkatesh Srinivas if (error) 62911447b59SVenkatesh Srinivas return (error); 63011447b59SVenkatesh Srinivas 63111447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 63211447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 63311447b59SVenkatesh Srinivas 63411447b59SVenkatesh Srinivas *info->vqai_vq = vqx->vq; 63511447b59SVenkatesh Srinivas sc->vtpci_nvqs++; 63611447b59SVenkatesh Srinivas } 63711447b59SVenkatesh Srinivas 63811447b59SVenkatesh Srinivas return (0); 63911447b59SVenkatesh Srinivas } 64011447b59SVenkatesh Srinivas 6412f2405bbSImre Vadász /* XXX Add argument to specify the callback function here. */ 64211447b59SVenkatesh Srinivas static int 6432f2405bbSImre Vadász vtpci_setup_intr(device_t dev, uint irq, lwkt_serialize_t slz) 64411447b59SVenkatesh Srinivas { 64511447b59SVenkatesh Srinivas struct vtpci_softc *sc; 64611447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires; 6472f2405bbSImre Vadász int flags, error; 64811447b59SVenkatesh Srinivas 64911447b59SVenkatesh Srinivas sc = device_get_softc(dev); 65011447b59SVenkatesh Srinivas flags = INTR_MPSAFE; 6512f2405bbSImre Vadász 6522f2405bbSImre Vadász if ((int)irq >= sc->vtpci_nintr_res) 6532f2405bbSImre Vadász return (EINVAL); 6542f2405bbSImre Vadász ires = &sc->vtpci_intr_res[irq]; 65511447b59SVenkatesh Srinivas 65611447b59SVenkatesh Srinivas if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) { 65711447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 658df2df2fcSImre Vadász vtpci_legacy_intr, 6592f2405bbSImre Vadász ires, &ires->intrhand, slz); 6602f2405bbSImre Vadász } else { 6612f2405bbSImre Vadász error = bus_setup_intr(dev, ires->irq, flags, 6622f2405bbSImre Vadász vtpci_msix_intr, 6632f2405bbSImre Vadász ires, &ires->intrhand, slz); 6642f2405bbSImre Vadász } 66511447b59SVenkatesh Srinivas return (error); 66611447b59SVenkatesh Srinivas } 66711447b59SVenkatesh Srinivas 6682f2405bbSImre Vadász static int 6692f2405bbSImre Vadász vtpci_teardown_intr(device_t dev, uint irq) 6702f2405bbSImre Vadász { 6712f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev); 6722f2405bbSImre Vadász struct vtpci_intr_resource *ires; 67311447b59SVenkatesh Srinivas 6742f2405bbSImre Vadász if ((int)irq >= sc->vtpci_nintr_res) 6752f2405bbSImre Vadász return (EINVAL); 67611447b59SVenkatesh Srinivas 6772f2405bbSImre Vadász ires = &sc->vtpci_intr_res[irq]; 6782f2405bbSImre Vadász 6792f2405bbSImre Vadász if (ires->intrhand == NULL) 6802f2405bbSImre Vadász return (ENXIO); 6812f2405bbSImre Vadász 6822f2405bbSImre Vadász bus_teardown_intr(dev, ires->irq, ires->intrhand); 6832f2405bbSImre Vadász ires->intrhand = NULL; 6842f2405bbSImre Vadász return (0); 68511447b59SVenkatesh Srinivas } 68611447b59SVenkatesh Srinivas 6872f2405bbSImre Vadász static void 6889d96478cSImre Vadász vtpci_add_irqentry(struct vtpci_intr_resource *intr_res, int what, 6899d96478cSImre Vadász driver_intr_t handler, void *arg) 6902f2405bbSImre Vadász { 6912f2405bbSImre Vadász struct vqentry *e; 69211447b59SVenkatesh Srinivas 6932f2405bbSImre Vadász TAILQ_FOREACH(e, &intr_res->ls, entries) { 6942f2405bbSImre Vadász if (e->what == what) 6952f2405bbSImre Vadász return; 6962f2405bbSImre Vadász } 6979d96478cSImre Vadász e = kmalloc(sizeof(*e), M_DEVBUF, M_WAITOK | M_ZERO); 6982f2405bbSImre Vadász e->what = what; 6999d96478cSImre Vadász if (e->what == -1) { 7009d96478cSImre Vadász e->vq = NULL; 7019d96478cSImre Vadász } else { 7029d96478cSImre Vadász e->vq = intr_res->ires_sc->vtpci_vqx[e->what].vq; 7039d96478cSImre Vadász } 7049d96478cSImre Vadász e->handler = handler; 7059d96478cSImre Vadász e->arg = arg; 7062f2405bbSImre Vadász TAILQ_INSERT_TAIL(&intr_res->ls, e, entries); 7072f2405bbSImre Vadász } 7082f2405bbSImre Vadász 7092f2405bbSImre Vadász static void 7102f2405bbSImre Vadász vtpci_del_irqentry(struct vtpci_intr_resource *intr_res, int what) 7112f2405bbSImre Vadász { 7122f2405bbSImre Vadász struct vqentry *e; 7132f2405bbSImre Vadász 7142f2405bbSImre Vadász TAILQ_FOREACH(e, &intr_res->ls, entries) { 7152f2405bbSImre Vadász if (e->what == what) 7162f2405bbSImre Vadász break; 7172f2405bbSImre Vadász } 7182f2405bbSImre Vadász if (e != NULL) { 7192f2405bbSImre Vadász TAILQ_REMOVE(&intr_res->ls, e, entries); 7202f2405bbSImre Vadász kfree(e, M_DEVBUF); 7212f2405bbSImre Vadász } 7222f2405bbSImre Vadász } 7232f2405bbSImre Vadász 7242f2405bbSImre Vadász /* 7252f2405bbSImre Vadász * Config intr can be bound after intr_alloc, virtqueue intrs can be bound 7262f2405bbSImre Vadász * after intr_alloc and alloc_virtqueues. 7272f2405bbSImre Vadász */ 7282f2405bbSImre Vadász static int 7299d96478cSImre Vadász vtpci_bind_intr(device_t dev, uint irq, int what, 7309d96478cSImre Vadász driver_intr_t handler, void *arg) 7312f2405bbSImre Vadász { 7322f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev); 7332f2405bbSImre Vadász struct vtpci_virtqueue *vqx; 7342f2405bbSImre Vadász int error; 7352f2405bbSImre Vadász 7362f2405bbSImre Vadász if (irq >= sc->vtpci_nintr_res) 7372f2405bbSImre Vadász return (EINVAL); 7382f2405bbSImre Vadász 7392f2405bbSImre Vadász if (what == -1) { 7402f2405bbSImre Vadász if (sc->vtpci_config_irq != -1) 7412f2405bbSImre Vadász return (EINVAL); 7422f2405bbSImre Vadász 7432f2405bbSImre Vadász sc->vtpci_config_irq = irq; 7442f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 7452f2405bbSImre Vadász error = vtpci_register_msix_vector(sc, 7462f2405bbSImre Vadász VIRTIO_MSI_CONFIG_VECTOR, irq); 74711447b59SVenkatesh Srinivas if (error) 74811447b59SVenkatesh Srinivas return (error); 74911447b59SVenkatesh Srinivas } 7502f2405bbSImre Vadász goto done; 7512f2405bbSImre Vadász } 75211447b59SVenkatesh Srinivas 7532f2405bbSImre Vadász if (sc->vtpci_nvqs <= what || what < 0) 7542f2405bbSImre Vadász return (EINVAL); 7552f2405bbSImre Vadász 7562f2405bbSImre Vadász vqx = &sc->vtpci_vqx[what]; 7572f2405bbSImre Vadász if (vqx->ires_idx != -1) 7582f2405bbSImre Vadász return (EINVAL); 7592f2405bbSImre Vadász 7602f2405bbSImre Vadász vqx->ires_idx = irq; 7612f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 7622f2405bbSImre Vadász vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what); 7632f2405bbSImre Vadász error = vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR, 7642f2405bbSImre Vadász irq); 7652f2405bbSImre Vadász if (error) 7662f2405bbSImre Vadász return (error); 7672f2405bbSImre Vadász } 7682f2405bbSImre Vadász done: 7699d96478cSImre Vadász vtpci_add_irqentry(&sc->vtpci_intr_res[irq], what, handler, arg); 7702f2405bbSImre Vadász return (0); 7712f2405bbSImre Vadász } 7722f2405bbSImre Vadász 7732f2405bbSImre Vadász static int 7742f2405bbSImre Vadász vtpci_unbind_intr(device_t dev, int what) 7752f2405bbSImre Vadász { 7762f2405bbSImre Vadász struct vtpci_softc *sc = device_get_softc(dev); 7772f2405bbSImre Vadász struct vtpci_virtqueue *vqx; 7782f2405bbSImre Vadász uint irq; 7792f2405bbSImre Vadász 7802f2405bbSImre Vadász if (what == -1) { 7812f2405bbSImre Vadász if (sc->vtpci_config_irq == -1) 7822f2405bbSImre Vadász return (EINVAL); 7832f2405bbSImre Vadász 7842f2405bbSImre Vadász irq = sc->vtpci_config_irq; 7852f2405bbSImre Vadász sc->vtpci_config_irq = -1; 7862f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 7872f2405bbSImre Vadász vtpci_register_msix_vector(sc, 7882f2405bbSImre Vadász VIRTIO_MSI_CONFIG_VECTOR, -1); 7892f2405bbSImre Vadász } 7902f2405bbSImre Vadász goto done; 7912f2405bbSImre Vadász } 7922f2405bbSImre Vadász 7932f2405bbSImre Vadász if (sc->vtpci_nvqs <= what || what < 0) 7942f2405bbSImre Vadász return (EINVAL); 7952f2405bbSImre Vadász 7962f2405bbSImre Vadász vqx = &sc->vtpci_vqx[what]; 7972f2405bbSImre Vadász if (vqx->ires_idx == -1) 7982f2405bbSImre Vadász return (EINVAL); 7992f2405bbSImre Vadász 8002f2405bbSImre Vadász irq = vqx->ires_idx; 8012f2405bbSImre Vadász vqx->ires_idx = -1; 8022f2405bbSImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 8032f2405bbSImre Vadász vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, what); 8042f2405bbSImre Vadász vtpci_register_msix_vector(sc, VIRTIO_MSI_QUEUE_VECTOR, -1); 8052f2405bbSImre Vadász } 8062f2405bbSImre Vadász done: 8072f2405bbSImre Vadász KKASSERT(irq >= 0 && irq < sc->vtpci_nintr_res); 8082f2405bbSImre Vadász vtpci_del_irqentry(&sc->vtpci_intr_res[irq], what); 80911447b59SVenkatesh Srinivas return (0); 81011447b59SVenkatesh Srinivas } 81111447b59SVenkatesh Srinivas 81211447b59SVenkatesh Srinivas static void 81311447b59SVenkatesh Srinivas vtpci_stop(device_t dev) 81411447b59SVenkatesh Srinivas { 81511447b59SVenkatesh Srinivas vtpci_reset(device_get_softc(dev)); 81611447b59SVenkatesh Srinivas } 81711447b59SVenkatesh Srinivas 81811447b59SVenkatesh Srinivas static int 81911447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features) 82011447b59SVenkatesh Srinivas { 82111447b59SVenkatesh Srinivas struct vtpci_softc *sc; 82211447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 82311447b59SVenkatesh Srinivas struct virtqueue *vq; 82411447b59SVenkatesh Srinivas int queue, error; 82511447b59SVenkatesh Srinivas uint16_t vq_size; 82611447b59SVenkatesh Srinivas 82711447b59SVenkatesh Srinivas sc = device_get_softc(dev); 82811447b59SVenkatesh Srinivas 82911447b59SVenkatesh Srinivas /* 83011447b59SVenkatesh Srinivas * Redrive the device initialization. This is a bit of an abuse 83111447b59SVenkatesh Srinivas * of the specification, but both VirtualBox and QEMU/KVM seem 83211447b59SVenkatesh Srinivas * to play nice. We do not allow the host device to change from 83311447b59SVenkatesh Srinivas * what was originally negotiated beyond what the guest driver 83411447b59SVenkatesh Srinivas * changed (MSIX state should not change, number of virtqueues 83511447b59SVenkatesh Srinivas * and their size remain the same, etc). 83611447b59SVenkatesh Srinivas */ 83711447b59SVenkatesh Srinivas 83811447b59SVenkatesh Srinivas if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 83911447b59SVenkatesh Srinivas vtpci_stop(dev); 84011447b59SVenkatesh Srinivas 84111447b59SVenkatesh Srinivas /* 84211447b59SVenkatesh Srinivas * Quickly drive the status through ACK and DRIVER. The device 84311447b59SVenkatesh Srinivas * does not become usable again until vtpci_reinit_complete(). 84411447b59SVenkatesh Srinivas */ 84511447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 84611447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 84711447b59SVenkatesh Srinivas 84811447b59SVenkatesh Srinivas vtpci_negotiate_features(dev, features); 84911447b59SVenkatesh Srinivas 85011447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 851c8247d06SImre Vadász pci_enable_msix(dev); 85211447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 85311447b59SVenkatesh Srinivas VIRTIO_MSI_CONFIG_VECTOR, 0); 85411447b59SVenkatesh Srinivas if (error) 85511447b59SVenkatesh Srinivas return (error); 85611447b59SVenkatesh Srinivas } 85711447b59SVenkatesh Srinivas 85811447b59SVenkatesh Srinivas for (queue = 0; queue < sc->vtpci_nvqs; queue++) { 85911447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue]; 86011447b59SVenkatesh Srinivas vq = vqx->vq; 86111447b59SVenkatesh Srinivas 86211447b59SVenkatesh Srinivas KASSERT(vq != NULL, ("vq %d not allocated", queue)); 86311447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 86411447b59SVenkatesh Srinivas 86511447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 86611447b59SVenkatesh Srinivas error = virtqueue_reinit(vq, vq_size); 86711447b59SVenkatesh Srinivas if (error) 86811447b59SVenkatesh Srinivas return (error); 86911447b59SVenkatesh Srinivas 87011447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 87111447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 87211447b59SVenkatesh Srinivas VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx); 87311447b59SVenkatesh Srinivas if (error) 87411447b59SVenkatesh Srinivas return (error); 87511447b59SVenkatesh Srinivas } 87611447b59SVenkatesh Srinivas 87711447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 87811447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 87911447b59SVenkatesh Srinivas } 88011447b59SVenkatesh Srinivas 88111447b59SVenkatesh Srinivas return (0); 88211447b59SVenkatesh Srinivas } 88311447b59SVenkatesh Srinivas 88411447b59SVenkatesh Srinivas static void 88511447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev) 88611447b59SVenkatesh Srinivas { 88711447b59SVenkatesh Srinivas 88811447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 88911447b59SVenkatesh Srinivas } 89011447b59SVenkatesh Srinivas 89111447b59SVenkatesh Srinivas static void 89211447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue) 89311447b59SVenkatesh Srinivas { 89411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 89511447b59SVenkatesh Srinivas 89611447b59SVenkatesh Srinivas sc = device_get_softc(dev); 89711447b59SVenkatesh Srinivas 89811447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue); 89911447b59SVenkatesh Srinivas } 90011447b59SVenkatesh Srinivas 90111447b59SVenkatesh Srinivas static uint8_t 90211447b59SVenkatesh Srinivas vtpci_get_status(device_t dev) 90311447b59SVenkatesh Srinivas { 90411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 90511447b59SVenkatesh Srinivas 90611447b59SVenkatesh Srinivas sc = device_get_softc(dev); 90711447b59SVenkatesh Srinivas 90811447b59SVenkatesh Srinivas return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS)); 90911447b59SVenkatesh Srinivas } 91011447b59SVenkatesh Srinivas 91111447b59SVenkatesh Srinivas static void 91211447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status) 91311447b59SVenkatesh Srinivas { 91411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 91511447b59SVenkatesh Srinivas 91611447b59SVenkatesh Srinivas sc = device_get_softc(dev); 91711447b59SVenkatesh Srinivas 91811447b59SVenkatesh Srinivas if (status != VIRTIO_CONFIG_STATUS_RESET) 91911447b59SVenkatesh Srinivas status |= vtpci_get_status(dev); 92011447b59SVenkatesh Srinivas 92111447b59SVenkatesh Srinivas vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status); 92211447b59SVenkatesh Srinivas } 92311447b59SVenkatesh Srinivas 92411447b59SVenkatesh Srinivas static void 92511447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset, 92611447b59SVenkatesh Srinivas void *dst, int length) 92711447b59SVenkatesh Srinivas { 92811447b59SVenkatesh Srinivas struct vtpci_softc *sc; 92911447b59SVenkatesh Srinivas bus_size_t off; 93011447b59SVenkatesh Srinivas uint8_t *d; 93111447b59SVenkatesh Srinivas int size; 93211447b59SVenkatesh Srinivas 93311447b59SVenkatesh Srinivas sc = device_get_softc(dev); 93411447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset; 93511447b59SVenkatesh Srinivas 93611447b59SVenkatesh Srinivas for (d = dst; length > 0; d += size, off += size, length -= size) { 93711447b59SVenkatesh Srinivas if (length >= 4) { 93811447b59SVenkatesh Srinivas size = 4; 93911447b59SVenkatesh Srinivas *(uint32_t *)d = vtpci_read_config_4(sc, off); 94011447b59SVenkatesh Srinivas } else if (length >= 2) { 94111447b59SVenkatesh Srinivas size = 2; 94211447b59SVenkatesh Srinivas *(uint16_t *)d = vtpci_read_config_2(sc, off); 94311447b59SVenkatesh Srinivas } else { 94411447b59SVenkatesh Srinivas size = 1; 94511447b59SVenkatesh Srinivas *d = vtpci_read_config_1(sc, off); 94611447b59SVenkatesh Srinivas } 94711447b59SVenkatesh Srinivas } 94811447b59SVenkatesh Srinivas } 94911447b59SVenkatesh Srinivas 95011447b59SVenkatesh Srinivas static void 95111447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset, 95211447b59SVenkatesh Srinivas void *src, int length) 95311447b59SVenkatesh Srinivas { 95411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 95511447b59SVenkatesh Srinivas bus_size_t off; 95611447b59SVenkatesh Srinivas uint8_t *s; 95711447b59SVenkatesh Srinivas int size; 95811447b59SVenkatesh Srinivas 95911447b59SVenkatesh Srinivas sc = device_get_softc(dev); 96011447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset; 96111447b59SVenkatesh Srinivas 96211447b59SVenkatesh Srinivas for (s = src; length > 0; s += size, off += size, length -= size) { 96311447b59SVenkatesh Srinivas if (length >= 4) { 96411447b59SVenkatesh Srinivas size = 4; 96511447b59SVenkatesh Srinivas vtpci_write_config_4(sc, off, *(uint32_t *)s); 96611447b59SVenkatesh Srinivas } else if (length >= 2) { 96711447b59SVenkatesh Srinivas size = 2; 96811447b59SVenkatesh Srinivas vtpci_write_config_2(sc, off, *(uint16_t *)s); 96911447b59SVenkatesh Srinivas } else { 97011447b59SVenkatesh Srinivas size = 1; 97111447b59SVenkatesh Srinivas vtpci_write_config_1(sc, off, *s); 97211447b59SVenkatesh Srinivas } 97311447b59SVenkatesh Srinivas } 97411447b59SVenkatesh Srinivas } 97511447b59SVenkatesh Srinivas 97611447b59SVenkatesh Srinivas static void 97711447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg, 97811447b59SVenkatesh Srinivas uint64_t features) 97911447b59SVenkatesh Srinivas { 98011447b59SVenkatesh Srinivas device_t dev, child; 98111447b59SVenkatesh Srinivas 98211447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 98311447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 98411447b59SVenkatesh Srinivas 98511447b59SVenkatesh Srinivas if (device_is_attached(child) && bootverbose == 0) 98611447b59SVenkatesh Srinivas return; 98711447b59SVenkatesh Srinivas 98811447b59SVenkatesh Srinivas virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc); 98911447b59SVenkatesh Srinivas } 99011447b59SVenkatesh Srinivas 99111447b59SVenkatesh Srinivas static void 99211447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc) 99311447b59SVenkatesh Srinivas { 99411447b59SVenkatesh Srinivas device_t dev, child; 995b17fe9c5SImre Vadasz int error; 99611447b59SVenkatesh Srinivas 99711447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 99811447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 99911447b59SVenkatesh Srinivas 100011447b59SVenkatesh Srinivas if (child == NULL) 100111447b59SVenkatesh Srinivas return; 100211447b59SVenkatesh Srinivas 100311447b59SVenkatesh Srinivas if (device_get_state(child) != DS_NOTPRESENT) 100411447b59SVenkatesh Srinivas return; 100511447b59SVenkatesh Srinivas 100611447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 1007b17fe9c5SImre Vadasz error = device_probe_and_attach(child); 1008b17fe9c5SImre Vadasz if (error != 0 || device_get_state(child) == DS_NOTPRESENT) { 100911447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 101011447b59SVenkatesh Srinivas vtpci_reset(sc); 101111447b59SVenkatesh Srinivas vtpci_release_child_resources(sc); 101211447b59SVenkatesh Srinivas 101311447b59SVenkatesh Srinivas /* Reset status for future attempt. */ 101411447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 101511447b59SVenkatesh Srinivas } else 101611447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 101711447b59SVenkatesh Srinivas } 101811447b59SVenkatesh Srinivas 101911447b59SVenkatesh Srinivas static int 102011447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx) 102111447b59SVenkatesh Srinivas { 102211447b59SVenkatesh Srinivas device_t dev; 102311447b59SVenkatesh Srinivas uint16_t vector; 102411447b59SVenkatesh Srinivas 102511447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 102611447b59SVenkatesh Srinivas 102711447b59SVenkatesh Srinivas if (offset != VIRTIO_MSI_CONFIG_VECTOR && 102811447b59SVenkatesh Srinivas offset != VIRTIO_MSI_QUEUE_VECTOR) 102911447b59SVenkatesh Srinivas return (EINVAL); 103011447b59SVenkatesh Srinivas 103111447b59SVenkatesh Srinivas if (res_idx != -1) { 103211447b59SVenkatesh Srinivas /* Map from rid to host vector. */ 1033c8247d06SImre Vadász vector = res_idx; 1034c8247d06SImre Vadász } else { 103511447b59SVenkatesh Srinivas vector = VIRTIO_MSI_NO_VECTOR; 1036c8247d06SImre Vadász } 103711447b59SVenkatesh Srinivas 103811447b59SVenkatesh Srinivas vtpci_write_config_2(sc, offset, vector); 103911447b59SVenkatesh Srinivas 104011447b59SVenkatesh Srinivas if (vtpci_read_config_2(sc, offset) != vector) { 104111447b59SVenkatesh Srinivas device_printf(dev, "insufficient host resources for " 104211447b59SVenkatesh Srinivas "MSIX interrupts\n"); 104311447b59SVenkatesh Srinivas return (ENODEV); 104411447b59SVenkatesh Srinivas } 104511447b59SVenkatesh Srinivas 104611447b59SVenkatesh Srinivas return (0); 104711447b59SVenkatesh Srinivas } 104811447b59SVenkatesh Srinivas 104911447b59SVenkatesh Srinivas static void 105011447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc) 105111447b59SVenkatesh Srinivas { 1052c8247d06SImre Vadász device_t dev = sc->vtpci_dev; 105311447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires; 105411447b59SVenkatesh Srinivas int i; 105511447b59SVenkatesh Srinivas 1056c8247d06SImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) { 105711447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[i]; 105811447b59SVenkatesh Srinivas 105911447b59SVenkatesh Srinivas if (ires->intrhand != NULL) { 106011447b59SVenkatesh Srinivas bus_teardown_intr(dev, ires->irq, ires->intrhand); 106111447b59SVenkatesh Srinivas ires->intrhand = NULL; 106211447b59SVenkatesh Srinivas } 106311447b59SVenkatesh Srinivas if (ires->irq != NULL) { 106411447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IRQ, ires->rid, 106511447b59SVenkatesh Srinivas ires->irq); 106611447b59SVenkatesh Srinivas ires->irq = NULL; 106711447b59SVenkatesh Srinivas } 10682f2405bbSImre Vadász } 10692f2405bbSImre Vadász 10702f2405bbSImre Vadász vtpci_unbind_intr(sc->vtpci_dev, -1); 10712f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nvqs; i++) 10722f2405bbSImre Vadász vtpci_unbind_intr(sc->vtpci_dev, i); 10732f2405bbSImre Vadász 10742f2405bbSImre Vadász for (i = 0; i < sc->vtpci_nintr_res; i++) { 10752f2405bbSImre Vadász ires = &sc->vtpci_intr_res[i]; 10762f2405bbSImre Vadász 1077c8247d06SImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) 1078c8247d06SImre Vadász pci_release_msix_vector(dev, ires->rid); 1079c8247d06SImre Vadász ires->rid = 0; 108011447b59SVenkatesh Srinivas } 1081c8247d06SImre Vadász sc->vtpci_nintr_res = 0; 1082c8247d06SImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSI) { 1083c8247d06SImre Vadász pci_release_msi(dev); 1084c8247d06SImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSI; 1085c8247d06SImre Vadász } 1086c8247d06SImre Vadász if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 1087c8247d06SImre Vadász pci_disable_msix(dev); 1088c8247d06SImre Vadász pci_teardown_msix(dev); 10892f2405bbSImre Vadász sc->vtpci_flags &= ~VIRTIO_PCI_FLAG_MSIX; 1090c8247d06SImre Vadász } 1091c8247d06SImre Vadász 109211447b59SVenkatesh Srinivas } 109311447b59SVenkatesh Srinivas 109411447b59SVenkatesh Srinivas static void 109511447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc) 109611447b59SVenkatesh Srinivas { 109711447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 109811447b59SVenkatesh Srinivas int i; 109911447b59SVenkatesh Srinivas 110011447b59SVenkatesh Srinivas sc->vtpci_nvqs = 0; 110111447b59SVenkatesh Srinivas 110211447b59SVenkatesh Srinivas for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) { 110311447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i]; 110411447b59SVenkatesh Srinivas 110511447b59SVenkatesh Srinivas if (vqx->vq != NULL) { 110611447b59SVenkatesh Srinivas virtqueue_free(vqx->vq); 110711447b59SVenkatesh Srinivas vqx->vq = NULL; 110811447b59SVenkatesh Srinivas } 110911447b59SVenkatesh Srinivas } 111011447b59SVenkatesh Srinivas } 111111447b59SVenkatesh Srinivas 111211447b59SVenkatesh Srinivas static void 111311447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc) 111411447b59SVenkatesh Srinivas { 111511447b59SVenkatesh Srinivas vtpci_free_interrupts(sc); 111611447b59SVenkatesh Srinivas vtpci_free_virtqueues(sc); 111711447b59SVenkatesh Srinivas } 111811447b59SVenkatesh Srinivas 111911447b59SVenkatesh Srinivas static void 112011447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc) 112111447b59SVenkatesh Srinivas { 112211447b59SVenkatesh Srinivas 112311447b59SVenkatesh Srinivas /* 112411447b59SVenkatesh Srinivas * Setting the status to RESET sets the host device to 112511447b59SVenkatesh Srinivas * the original, uninitialized state. 112611447b59SVenkatesh Srinivas */ 112711447b59SVenkatesh Srinivas vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET); 112811447b59SVenkatesh Srinivas } 112911447b59SVenkatesh Srinivas 1130df2df2fcSImre Vadász static void 11312f2405bbSImre Vadász vtpci_legacy_intr(void *arg) 113211447b59SVenkatesh Srinivas { 11332f2405bbSImre Vadász struct vtpci_intr_resource *ires; 113411447b59SVenkatesh Srinivas struct vtpci_softc *sc; 11352f2405bbSImre Vadász struct vqentry *e; 113611447b59SVenkatesh Srinivas uint8_t isr; 113711447b59SVenkatesh Srinivas 11382f2405bbSImre Vadász ires = arg; 11392f2405bbSImre Vadász sc = ires->ires_sc; 114011447b59SVenkatesh Srinivas 114111447b59SVenkatesh Srinivas /* Reading the ISR also clears it. */ 114211447b59SVenkatesh Srinivas isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR); 114311447b59SVenkatesh Srinivas 11442f2405bbSImre Vadász TAILQ_FOREACH(e, &ires->ls, entries) { 11459d96478cSImre Vadász /* XXX Allow for masking individual virtqueue handlers. */ 11462f2405bbSImre Vadász if (e->what == -1) { 114711447b59SVenkatesh Srinivas if (isr & VIRTIO_PCI_ISR_CONFIG) 1148*b817dce2SImre Vadász e->handler(e->arg); 11499d96478cSImre Vadász } else if ((isr & VIRTIO_PCI_ISR_INTR) && 11509d96478cSImre Vadász virtqueue_pending(e->vq)) { 11519d96478cSImre Vadász e->handler(e->arg); 11522f2405bbSImre Vadász } 1153c8247d06SImre Vadász } 115411447b59SVenkatesh Srinivas } 115511447b59SVenkatesh Srinivas 1156df2df2fcSImre Vadász static void 11572f2405bbSImre Vadász vtpci_msix_intr(void *arg) 115811447b59SVenkatesh Srinivas { 11592f2405bbSImre Vadász struct vtpci_intr_resource *ires; 116011447b59SVenkatesh Srinivas struct vtpci_softc *sc; 11612f2405bbSImre Vadász struct vqentry *e; 116211447b59SVenkatesh Srinivas 11632f2405bbSImre Vadász ires = arg; 11642f2405bbSImre Vadász sc = ires->ires_sc; 11652f2405bbSImre Vadász TAILQ_FOREACH(e, &ires->ls, entries) { 11669d96478cSImre Vadász /* XXX Allow for masking individual virtqueue handlers. */ 11672f2405bbSImre Vadász if (e->what == -1) { 1168*b817dce2SImre Vadász e->handler(e->arg); 11699d96478cSImre Vadász } else if (virtqueue_pending(e->vq)) { 11709d96478cSImre Vadász e->handler(e->arg); 117111447b59SVenkatesh Srinivas } 11722f2405bbSImre Vadász } 117311447b59SVenkatesh Srinivas } 1174