1*11447b59SVenkatesh Srinivas /*- 2*11447b59SVenkatesh Srinivas * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org> 3*11447b59SVenkatesh Srinivas * All rights reserved. 4*11447b59SVenkatesh Srinivas * 5*11447b59SVenkatesh Srinivas * Redistribution and use in source and binary forms, with or without 6*11447b59SVenkatesh Srinivas * modification, are permitted provided that the following conditions 7*11447b59SVenkatesh Srinivas * are met: 8*11447b59SVenkatesh Srinivas * 1. Redistributions of source code must retain the above copyright 9*11447b59SVenkatesh Srinivas * notice unmodified, this list of conditions, and the following 10*11447b59SVenkatesh Srinivas * disclaimer. 11*11447b59SVenkatesh Srinivas * 2. Redistributions in binary form must reproduce the above copyright 12*11447b59SVenkatesh Srinivas * notice, this list of conditions and the following disclaimer in the 13*11447b59SVenkatesh Srinivas * documentation and/or other materials provided with the distribution. 14*11447b59SVenkatesh Srinivas * 15*11447b59SVenkatesh Srinivas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16*11447b59SVenkatesh Srinivas * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17*11447b59SVenkatesh Srinivas * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18*11447b59SVenkatesh Srinivas * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19*11447b59SVenkatesh Srinivas * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20*11447b59SVenkatesh Srinivas * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21*11447b59SVenkatesh Srinivas * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22*11447b59SVenkatesh Srinivas * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23*11447b59SVenkatesh Srinivas * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24*11447b59SVenkatesh Srinivas * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25*11447b59SVenkatesh Srinivas * 26*11447b59SVenkatesh Srinivas * $FreeBSD: src/sys/dev/virtio/pci/virtio_pci.c,v 1.3 2012/04/14 05:48:04 grehan Exp $ 27*11447b59SVenkatesh Srinivas */ 28*11447b59SVenkatesh Srinivas 29*11447b59SVenkatesh Srinivas /* Driver for the VirtIO PCI interface. */ 30*11447b59SVenkatesh Srinivas 31*11447b59SVenkatesh Srinivas #include <sys/cdefs.h> 32*11447b59SVenkatesh Srinivas 33*11447b59SVenkatesh Srinivas #include <sys/param.h> 34*11447b59SVenkatesh Srinivas #include <sys/systm.h> 35*11447b59SVenkatesh Srinivas #include <sys/bus.h> 36*11447b59SVenkatesh Srinivas #include <sys/kernel.h> 37*11447b59SVenkatesh Srinivas #include <sys/module.h> 38*11447b59SVenkatesh Srinivas #include <sys/malloc.h> 39*11447b59SVenkatesh Srinivas 40*11447b59SVenkatesh Srinivas #include <bus/pci/pcivar.h> 41*11447b59SVenkatesh Srinivas #include <bus/pci/pcireg.h> 42*11447b59SVenkatesh Srinivas 43*11447b59SVenkatesh Srinivas #include <sys/bus.h> 44*11447b59SVenkatesh Srinivas #include <sys/param.h> 45*11447b59SVenkatesh Srinivas #include <sys/rman.h> 46*11447b59SVenkatesh Srinivas 47*11447b59SVenkatesh Srinivas 48*11447b59SVenkatesh Srinivas #include <virtio/virtio.h> 49*11447b59SVenkatesh Srinivas #include <virtio/virtqueue.h> 50*11447b59SVenkatesh Srinivas #include "virtio_pci.h" 51*11447b59SVenkatesh Srinivas 52*11447b59SVenkatesh Srinivas #include "virtio_bus_if.h" 53*11447b59SVenkatesh Srinivas #include "virtio_if.h" 54*11447b59SVenkatesh Srinivas 55*11447b59SVenkatesh Srinivas struct vtpci_softc { 56*11447b59SVenkatesh Srinivas device_t vtpci_dev; 57*11447b59SVenkatesh Srinivas struct resource *vtpci_res; 58*11447b59SVenkatesh Srinivas struct resource *vtpci_msix_res; 59*11447b59SVenkatesh Srinivas uint64_t vtpci_features; 60*11447b59SVenkatesh Srinivas uint32_t vtpci_flags; 61*11447b59SVenkatesh Srinivas int vtpci_irq_type; 62*11447b59SVenkatesh Srinivas int vtpci_irq_rid; 63*11447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_NO_MSI 0x0001 64*11447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_MSI 0x0002 65*11447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_NO_MSIX 0x0010 66*11447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_MSIX 0x0020 67*11447b59SVenkatesh Srinivas #define VIRTIO_PCI_FLAG_SHARED_MSIX 0x0040 68*11447b59SVenkatesh Srinivas 69*11447b59SVenkatesh Srinivas device_t vtpci_child_dev; 70*11447b59SVenkatesh Srinivas struct virtio_feature_desc *vtpci_child_feat_desc; 71*11447b59SVenkatesh Srinivas 72*11447b59SVenkatesh Srinivas /* 73*11447b59SVenkatesh Srinivas * Ideally, each virtqueue that the driver provides a callback for 74*11447b59SVenkatesh Srinivas * will receive its own MSIX vector. If there are not sufficient 75*11447b59SVenkatesh Srinivas * vectors available, we will then attempt to have all the VQs 76*11447b59SVenkatesh Srinivas * share one vector. Note that when using MSIX, the configuration 77*11447b59SVenkatesh Srinivas * changed notifications must be on their own vector. 78*11447b59SVenkatesh Srinivas * 79*11447b59SVenkatesh Srinivas * If MSIX is not available, we will attempt to have the whole 80*11447b59SVenkatesh Srinivas * device share one MSI vector, and then, finally, one legacy 81*11447b59SVenkatesh Srinivas * interrupt. 82*11447b59SVenkatesh Srinivas */ 83*11447b59SVenkatesh Srinivas int vtpci_nvqs; 84*11447b59SVenkatesh Srinivas struct vtpci_virtqueue { 85*11447b59SVenkatesh Srinivas struct virtqueue *vq; 86*11447b59SVenkatesh Srinivas 87*11447b59SVenkatesh Srinivas /* Index into vtpci_intr_res[] below. Unused, then -1. */ 88*11447b59SVenkatesh Srinivas int ires_idx; 89*11447b59SVenkatesh Srinivas } vtpci_vqx[VIRTIO_MAX_VIRTQUEUES]; 90*11447b59SVenkatesh Srinivas 91*11447b59SVenkatesh Srinivas /* 92*11447b59SVenkatesh Srinivas * When using MSIX interrupts, the first element of vtpci_intr_res[] 93*11447b59SVenkatesh Srinivas * is always the configuration changed notifications. The remaining 94*11447b59SVenkatesh Srinivas * element(s) are used for the virtqueues. 95*11447b59SVenkatesh Srinivas * 96*11447b59SVenkatesh Srinivas * With MSI and legacy interrupts, only the first element of 97*11447b59SVenkatesh Srinivas * vtpci_intr_res[] is used. 98*11447b59SVenkatesh Srinivas */ 99*11447b59SVenkatesh Srinivas int vtpci_nintr_res; 100*11447b59SVenkatesh Srinivas struct vtpci_intr_resource { 101*11447b59SVenkatesh Srinivas struct resource *irq; 102*11447b59SVenkatesh Srinivas int rid; 103*11447b59SVenkatesh Srinivas void *intrhand; 104*11447b59SVenkatesh Srinivas } vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES]; 105*11447b59SVenkatesh Srinivas }; 106*11447b59SVenkatesh Srinivas 107*11447b59SVenkatesh Srinivas static int vtpci_probe(device_t); 108*11447b59SVenkatesh Srinivas static int vtpci_attach(device_t); 109*11447b59SVenkatesh Srinivas static int vtpci_detach(device_t); 110*11447b59SVenkatesh Srinivas static int vtpci_suspend(device_t); 111*11447b59SVenkatesh Srinivas static int vtpci_resume(device_t); 112*11447b59SVenkatesh Srinivas static int vtpci_shutdown(device_t); 113*11447b59SVenkatesh Srinivas static void vtpci_driver_added(device_t, driver_t *); 114*11447b59SVenkatesh Srinivas static void vtpci_child_detached(device_t, device_t); 115*11447b59SVenkatesh Srinivas static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *); 116*11447b59SVenkatesh Srinivas static int vtpci_write_ivar(device_t, device_t, int, uintptr_t); 117*11447b59SVenkatesh Srinivas 118*11447b59SVenkatesh Srinivas static uint64_t vtpci_negotiate_features(device_t, uint64_t); 119*11447b59SVenkatesh Srinivas static int vtpci_with_feature(device_t, uint64_t); 120*11447b59SVenkatesh Srinivas static int vtpci_alloc_virtqueues(device_t, int, int, 121*11447b59SVenkatesh Srinivas struct vq_alloc_info *); 122*11447b59SVenkatesh Srinivas static int vtpci_setup_intr(device_t); 123*11447b59SVenkatesh Srinivas static void vtpci_stop(device_t); 124*11447b59SVenkatesh Srinivas static int vtpci_reinit(device_t, uint64_t); 125*11447b59SVenkatesh Srinivas static void vtpci_reinit_complete(device_t); 126*11447b59SVenkatesh Srinivas static void vtpci_notify_virtqueue(device_t, uint16_t); 127*11447b59SVenkatesh Srinivas static uint8_t vtpci_get_status(device_t); 128*11447b59SVenkatesh Srinivas static void vtpci_set_status(device_t, uint8_t); 129*11447b59SVenkatesh Srinivas static void vtpci_read_dev_config(device_t, bus_size_t, void *, int); 130*11447b59SVenkatesh Srinivas static void vtpci_write_dev_config(device_t, bus_size_t, void *, int); 131*11447b59SVenkatesh Srinivas 132*11447b59SVenkatesh Srinivas static void vtpci_describe_features(struct vtpci_softc *, const char *, 133*11447b59SVenkatesh Srinivas uint64_t); 134*11447b59SVenkatesh Srinivas static void vtpci_probe_and_attach_child(struct vtpci_softc *); 135*11447b59SVenkatesh Srinivas 136*11447b59SVenkatesh Srinivas static int vtpci_alloc_interrupts(struct vtpci_softc *, int, int, 137*11447b59SVenkatesh Srinivas struct vq_alloc_info *); 138*11447b59SVenkatesh Srinivas static int vtpci_alloc_intr_resources(struct vtpci_softc *, int, 139*11447b59SVenkatesh Srinivas struct vq_alloc_info *); 140*11447b59SVenkatesh Srinivas static int vtpci_alloc_msi(struct vtpci_softc *); 141*11447b59SVenkatesh Srinivas static int vtpci_alloc_msix(struct vtpci_softc *, int); 142*11447b59SVenkatesh Srinivas static int vtpci_register_msix_vector(struct vtpci_softc *, int, int); 143*11447b59SVenkatesh Srinivas 144*11447b59SVenkatesh Srinivas static void vtpci_free_interrupts(struct vtpci_softc *); 145*11447b59SVenkatesh Srinivas static void vtpci_free_virtqueues(struct vtpci_softc *); 146*11447b59SVenkatesh Srinivas static void vtpci_release_child_resources(struct vtpci_softc *); 147*11447b59SVenkatesh Srinivas static void vtpci_reset(struct vtpci_softc *); 148*11447b59SVenkatesh Srinivas 149*11447b59SVenkatesh Srinivas static int vtpci_legacy_intr(void *); 150*11447b59SVenkatesh Srinivas static int vtpci_vq_shared_intr(void *); 151*11447b59SVenkatesh Srinivas static int vtpci_vq_intr(void *); 152*11447b59SVenkatesh Srinivas static int vtpci_config_intr(void *); 153*11447b59SVenkatesh Srinivas 154*11447b59SVenkatesh Srinivas /* 155*11447b59SVenkatesh Srinivas * I/O port read/write wrappers. 156*11447b59SVenkatesh Srinivas */ 157*11447b59SVenkatesh Srinivas #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o)) 158*11447b59SVenkatesh Srinivas #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o)) 159*11447b59SVenkatesh Srinivas #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o)) 160*11447b59SVenkatesh Srinivas #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v)) 161*11447b59SVenkatesh Srinivas #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v)) 162*11447b59SVenkatesh Srinivas #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v)) 163*11447b59SVenkatesh Srinivas 164*11447b59SVenkatesh Srinivas /* Tunables. */ 165*11447b59SVenkatesh Srinivas static int vtpci_disable_msix = 0; 166*11447b59SVenkatesh Srinivas TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 167*11447b59SVenkatesh Srinivas 168*11447b59SVenkatesh Srinivas static device_method_t vtpci_methods[] = { 169*11447b59SVenkatesh Srinivas /* Device interface. */ 170*11447b59SVenkatesh Srinivas DEVMETHOD(device_probe, vtpci_probe), 171*11447b59SVenkatesh Srinivas DEVMETHOD(device_attach, vtpci_attach), 172*11447b59SVenkatesh Srinivas DEVMETHOD(device_detach, vtpci_detach), 173*11447b59SVenkatesh Srinivas DEVMETHOD(device_suspend, vtpci_suspend), 174*11447b59SVenkatesh Srinivas DEVMETHOD(device_resume, vtpci_resume), 175*11447b59SVenkatesh Srinivas DEVMETHOD(device_shutdown, vtpci_shutdown), 176*11447b59SVenkatesh Srinivas 177*11447b59SVenkatesh Srinivas /* Bus interface. */ 178*11447b59SVenkatesh Srinivas DEVMETHOD(bus_driver_added, vtpci_driver_added), 179*11447b59SVenkatesh Srinivas DEVMETHOD(bus_child_detached, vtpci_child_detached), 180*11447b59SVenkatesh Srinivas DEVMETHOD(bus_read_ivar, vtpci_read_ivar), 181*11447b59SVenkatesh Srinivas DEVMETHOD(bus_write_ivar, vtpci_write_ivar), 182*11447b59SVenkatesh Srinivas 183*11447b59SVenkatesh Srinivas /* VirtIO bus interface. */ 184*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features), 185*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature), 186*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues), 187*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr), 188*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_stop, vtpci_stop), 189*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit, vtpci_reinit), 190*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete), 191*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), 192*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), 193*11447b59SVenkatesh Srinivas DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), 194*11447b59SVenkatesh Srinivas 195*11447b59SVenkatesh Srinivas { 0, 0 } 196*11447b59SVenkatesh Srinivas }; 197*11447b59SVenkatesh Srinivas 198*11447b59SVenkatesh Srinivas static driver_t vtpci_driver = { 199*11447b59SVenkatesh Srinivas "virtio_pci", 200*11447b59SVenkatesh Srinivas vtpci_methods, 201*11447b59SVenkatesh Srinivas sizeof(struct vtpci_softc) 202*11447b59SVenkatesh Srinivas }; 203*11447b59SVenkatesh Srinivas 204*11447b59SVenkatesh Srinivas devclass_t vtpci_devclass; 205*11447b59SVenkatesh Srinivas 206*11447b59SVenkatesh Srinivas DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0); 207*11447b59SVenkatesh Srinivas MODULE_VERSION(virtio_pci, 1); 208*11447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 209*11447b59SVenkatesh Srinivas MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 210*11447b59SVenkatesh Srinivas 211*11447b59SVenkatesh Srinivas static int 212*11447b59SVenkatesh Srinivas vtpci_probe(device_t dev) 213*11447b59SVenkatesh Srinivas { 214*11447b59SVenkatesh Srinivas char desc[36]; 215*11447b59SVenkatesh Srinivas const char *name; 216*11447b59SVenkatesh Srinivas 217*11447b59SVenkatesh Srinivas if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID) 218*11447b59SVenkatesh Srinivas return (ENXIO); 219*11447b59SVenkatesh Srinivas 220*11447b59SVenkatesh Srinivas if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN || 221*11447b59SVenkatesh Srinivas pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX) 222*11447b59SVenkatesh Srinivas return (ENXIO); 223*11447b59SVenkatesh Srinivas 224*11447b59SVenkatesh Srinivas if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION) 225*11447b59SVenkatesh Srinivas return (ENXIO); 226*11447b59SVenkatesh Srinivas 227*11447b59SVenkatesh Srinivas name = virtio_device_name(pci_get_subdevice(dev)); 228*11447b59SVenkatesh Srinivas if (name == NULL) 229*11447b59SVenkatesh Srinivas name = "Unknown"; 230*11447b59SVenkatesh Srinivas 231*11447b59SVenkatesh Srinivas ksnprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name); 232*11447b59SVenkatesh Srinivas device_set_desc_copy(dev, desc); 233*11447b59SVenkatesh Srinivas 234*11447b59SVenkatesh Srinivas return (BUS_PROBE_DEFAULT); 235*11447b59SVenkatesh Srinivas } 236*11447b59SVenkatesh Srinivas 237*11447b59SVenkatesh Srinivas static int 238*11447b59SVenkatesh Srinivas vtpci_attach(device_t dev) 239*11447b59SVenkatesh Srinivas { 240*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 241*11447b59SVenkatesh Srinivas device_t child; 242*11447b59SVenkatesh Srinivas int rid; 243*11447b59SVenkatesh Srinivas 244*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 245*11447b59SVenkatesh Srinivas sc->vtpci_dev = dev; 246*11447b59SVenkatesh Srinivas 247*11447b59SVenkatesh Srinivas pci_enable_busmaster(dev); 248*11447b59SVenkatesh Srinivas 249*11447b59SVenkatesh Srinivas rid = PCIR_BAR(0); 250*11447b59SVenkatesh Srinivas sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 251*11447b59SVenkatesh Srinivas RF_ACTIVE); 252*11447b59SVenkatesh Srinivas if (sc->vtpci_res == NULL) { 253*11447b59SVenkatesh Srinivas device_printf(dev, "cannot map I/O space\n"); 254*11447b59SVenkatesh Srinivas return (ENXIO); 255*11447b59SVenkatesh Srinivas } 256*11447b59SVenkatesh Srinivas 257*11447b59SVenkatesh Srinivas if (pci_find_extcap(dev, PCIY_MSI, NULL) != 0) 258*11447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSI; 259*11447b59SVenkatesh Srinivas /* XXX(vsrinivas): Check out how to get MSI-X */ 260*11447b59SVenkatesh Srinivas #if OLD_MSI 261*11447b59SVenkatesh Srinivas if (pci_find_extcap(dev, PCIY_MSIX, NULL) == 0) { 262*11447b59SVenkatesh Srinivas rid = PCIR_BAR(1); 263*11447b59SVenkatesh Srinivas sc->vtpci_msix_res = bus_alloc_resource_any(dev, 264*11447b59SVenkatesh Srinivas SYS_RES_MEMORY, &rid, RF_ACTIVE); 265*11447b59SVenkatesh Srinivas } 266*11447b59SVenkatesh Srinivas #endif 267*11447b59SVenkatesh Srinivas if (sc->vtpci_msix_res == NULL) 268*11447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSIX; 269*11447b59SVenkatesh Srinivas 270*11447b59SVenkatesh Srinivas vtpci_reset(sc); 271*11447b59SVenkatesh Srinivas 272*11447b59SVenkatesh Srinivas /* Tell the host we've noticed this device. */ 273*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 274*11447b59SVenkatesh Srinivas 275*11447b59SVenkatesh Srinivas if ((child = device_add_child(dev, NULL, -1)) == NULL) { 276*11447b59SVenkatesh Srinivas device_printf(dev, "cannot create child device\n"); 277*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 278*11447b59SVenkatesh Srinivas vtpci_detach(dev); 279*11447b59SVenkatesh Srinivas return (ENOMEM); 280*11447b59SVenkatesh Srinivas } 281*11447b59SVenkatesh Srinivas 282*11447b59SVenkatesh Srinivas sc->vtpci_child_dev = child; 283*11447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc); 284*11447b59SVenkatesh Srinivas 285*11447b59SVenkatesh Srinivas return (0); 286*11447b59SVenkatesh Srinivas } 287*11447b59SVenkatesh Srinivas 288*11447b59SVenkatesh Srinivas static int 289*11447b59SVenkatesh Srinivas vtpci_detach(device_t dev) 290*11447b59SVenkatesh Srinivas { 291*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 292*11447b59SVenkatesh Srinivas device_t child; 293*11447b59SVenkatesh Srinivas int error; 294*11447b59SVenkatesh Srinivas 295*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 296*11447b59SVenkatesh Srinivas 297*11447b59SVenkatesh Srinivas if ((child = sc->vtpci_child_dev) != NULL) { 298*11447b59SVenkatesh Srinivas error = device_delete_child(dev, child); 299*11447b59SVenkatesh Srinivas if (error) 300*11447b59SVenkatesh Srinivas return (error); 301*11447b59SVenkatesh Srinivas sc->vtpci_child_dev = NULL; 302*11447b59SVenkatesh Srinivas } 303*11447b59SVenkatesh Srinivas 304*11447b59SVenkatesh Srinivas vtpci_reset(sc); 305*11447b59SVenkatesh Srinivas 306*11447b59SVenkatesh Srinivas if (sc->vtpci_msix_res != NULL) { 307*11447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), 308*11447b59SVenkatesh Srinivas sc->vtpci_msix_res); 309*11447b59SVenkatesh Srinivas sc->vtpci_msix_res = NULL; 310*11447b59SVenkatesh Srinivas } 311*11447b59SVenkatesh Srinivas 312*11447b59SVenkatesh Srinivas if (sc->vtpci_res != NULL) { 313*11447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), 314*11447b59SVenkatesh Srinivas sc->vtpci_res); 315*11447b59SVenkatesh Srinivas sc->vtpci_res = NULL; 316*11447b59SVenkatesh Srinivas } 317*11447b59SVenkatesh Srinivas 318*11447b59SVenkatesh Srinivas return (0); 319*11447b59SVenkatesh Srinivas } 320*11447b59SVenkatesh Srinivas 321*11447b59SVenkatesh Srinivas static int 322*11447b59SVenkatesh Srinivas vtpci_suspend(device_t dev) 323*11447b59SVenkatesh Srinivas { 324*11447b59SVenkatesh Srinivas 325*11447b59SVenkatesh Srinivas return (bus_generic_suspend(dev)); 326*11447b59SVenkatesh Srinivas } 327*11447b59SVenkatesh Srinivas 328*11447b59SVenkatesh Srinivas static int 329*11447b59SVenkatesh Srinivas vtpci_resume(device_t dev) 330*11447b59SVenkatesh Srinivas { 331*11447b59SVenkatesh Srinivas 332*11447b59SVenkatesh Srinivas return (bus_generic_resume(dev)); 333*11447b59SVenkatesh Srinivas } 334*11447b59SVenkatesh Srinivas 335*11447b59SVenkatesh Srinivas static int 336*11447b59SVenkatesh Srinivas vtpci_shutdown(device_t dev) 337*11447b59SVenkatesh Srinivas { 338*11447b59SVenkatesh Srinivas 339*11447b59SVenkatesh Srinivas (void) bus_generic_shutdown(dev); 340*11447b59SVenkatesh Srinivas /* Forcibly stop the host device. */ 341*11447b59SVenkatesh Srinivas vtpci_stop(dev); 342*11447b59SVenkatesh Srinivas 343*11447b59SVenkatesh Srinivas return (0); 344*11447b59SVenkatesh Srinivas } 345*11447b59SVenkatesh Srinivas 346*11447b59SVenkatesh Srinivas static void 347*11447b59SVenkatesh Srinivas vtpci_driver_added(device_t dev, driver_t *driver) 348*11447b59SVenkatesh Srinivas { 349*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 350*11447b59SVenkatesh Srinivas 351*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 352*11447b59SVenkatesh Srinivas 353*11447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(sc); 354*11447b59SVenkatesh Srinivas } 355*11447b59SVenkatesh Srinivas 356*11447b59SVenkatesh Srinivas static void 357*11447b59SVenkatesh Srinivas vtpci_child_detached(device_t dev, device_t child) 358*11447b59SVenkatesh Srinivas { 359*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 360*11447b59SVenkatesh Srinivas 361*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 362*11447b59SVenkatesh Srinivas 363*11447b59SVenkatesh Srinivas vtpci_reset(sc); 364*11447b59SVenkatesh Srinivas vtpci_release_child_resources(sc); 365*11447b59SVenkatesh Srinivas } 366*11447b59SVenkatesh Srinivas 367*11447b59SVenkatesh Srinivas static int 368*11447b59SVenkatesh Srinivas vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 369*11447b59SVenkatesh Srinivas { 370*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 371*11447b59SVenkatesh Srinivas 372*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 373*11447b59SVenkatesh Srinivas 374*11447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child) 375*11447b59SVenkatesh Srinivas return (ENOENT); 376*11447b59SVenkatesh Srinivas 377*11447b59SVenkatesh Srinivas switch (index) { 378*11447b59SVenkatesh Srinivas case VIRTIO_IVAR_DEVTYPE: 379*11447b59SVenkatesh Srinivas *result = pci_get_subdevice(dev); 380*11447b59SVenkatesh Srinivas break; 381*11447b59SVenkatesh Srinivas default: 382*11447b59SVenkatesh Srinivas return (ENOENT); 383*11447b59SVenkatesh Srinivas } 384*11447b59SVenkatesh Srinivas 385*11447b59SVenkatesh Srinivas return (0); 386*11447b59SVenkatesh Srinivas } 387*11447b59SVenkatesh Srinivas 388*11447b59SVenkatesh Srinivas static int 389*11447b59SVenkatesh Srinivas vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 390*11447b59SVenkatesh Srinivas { 391*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 392*11447b59SVenkatesh Srinivas 393*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 394*11447b59SVenkatesh Srinivas 395*11447b59SVenkatesh Srinivas if (sc->vtpci_child_dev != child) 396*11447b59SVenkatesh Srinivas return (ENOENT); 397*11447b59SVenkatesh Srinivas 398*11447b59SVenkatesh Srinivas switch (index) { 399*11447b59SVenkatesh Srinivas case VIRTIO_IVAR_FEATURE_DESC: 400*11447b59SVenkatesh Srinivas sc->vtpci_child_feat_desc = (void *) value; 401*11447b59SVenkatesh Srinivas break; 402*11447b59SVenkatesh Srinivas default: 403*11447b59SVenkatesh Srinivas return (ENOENT); 404*11447b59SVenkatesh Srinivas } 405*11447b59SVenkatesh Srinivas 406*11447b59SVenkatesh Srinivas return (0); 407*11447b59SVenkatesh Srinivas } 408*11447b59SVenkatesh Srinivas 409*11447b59SVenkatesh Srinivas static uint64_t 410*11447b59SVenkatesh Srinivas vtpci_negotiate_features(device_t dev, uint64_t child_features) 411*11447b59SVenkatesh Srinivas { 412*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 413*11447b59SVenkatesh Srinivas uint64_t host_features, features; 414*11447b59SVenkatesh Srinivas 415*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 416*11447b59SVenkatesh Srinivas 417*11447b59SVenkatesh Srinivas host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES); 418*11447b59SVenkatesh Srinivas vtpci_describe_features(sc, "host", host_features); 419*11447b59SVenkatesh Srinivas 420*11447b59SVenkatesh Srinivas /* 421*11447b59SVenkatesh Srinivas * Limit negotiated features to what the driver, virtqueue, and 422*11447b59SVenkatesh Srinivas * host all support. 423*11447b59SVenkatesh Srinivas */ 424*11447b59SVenkatesh Srinivas features = host_features & child_features; 425*11447b59SVenkatesh Srinivas features = virtqueue_filter_features(features); 426*11447b59SVenkatesh Srinivas sc->vtpci_features = features; 427*11447b59SVenkatesh Srinivas 428*11447b59SVenkatesh Srinivas vtpci_describe_features(sc, "negotiated", features); 429*11447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features); 430*11447b59SVenkatesh Srinivas 431*11447b59SVenkatesh Srinivas return (features); 432*11447b59SVenkatesh Srinivas } 433*11447b59SVenkatesh Srinivas 434*11447b59SVenkatesh Srinivas static int 435*11447b59SVenkatesh Srinivas vtpci_with_feature(device_t dev, uint64_t feature) 436*11447b59SVenkatesh Srinivas { 437*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 438*11447b59SVenkatesh Srinivas 439*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 440*11447b59SVenkatesh Srinivas 441*11447b59SVenkatesh Srinivas return ((sc->vtpci_features & feature) != 0); 442*11447b59SVenkatesh Srinivas } 443*11447b59SVenkatesh Srinivas 444*11447b59SVenkatesh Srinivas static int 445*11447b59SVenkatesh Srinivas vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, 446*11447b59SVenkatesh Srinivas struct vq_alloc_info *vq_info) 447*11447b59SVenkatesh Srinivas { 448*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 449*11447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 450*11447b59SVenkatesh Srinivas struct vq_alloc_info *info; 451*11447b59SVenkatesh Srinivas int queue, error; 452*11447b59SVenkatesh Srinivas uint16_t vq_size; 453*11447b59SVenkatesh Srinivas 454*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 455*11447b59SVenkatesh Srinivas 456*11447b59SVenkatesh Srinivas if (sc->vtpci_nvqs != 0 || nvqs <= 0 || 457*11447b59SVenkatesh Srinivas nvqs > VIRTIO_MAX_VIRTQUEUES) 458*11447b59SVenkatesh Srinivas return (EINVAL); 459*11447b59SVenkatesh Srinivas 460*11447b59SVenkatesh Srinivas error = vtpci_alloc_interrupts(sc, flags, nvqs, vq_info); 461*11447b59SVenkatesh Srinivas if (error) { 462*11447b59SVenkatesh Srinivas device_printf(dev, "cannot allocate interrupts\n"); 463*11447b59SVenkatesh Srinivas return (error); 464*11447b59SVenkatesh Srinivas } 465*11447b59SVenkatesh Srinivas 466*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 467*11447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 468*11447b59SVenkatesh Srinivas VIRTIO_MSI_CONFIG_VECTOR, 0); 469*11447b59SVenkatesh Srinivas if (error) 470*11447b59SVenkatesh Srinivas return (error); 471*11447b59SVenkatesh Srinivas } 472*11447b59SVenkatesh Srinivas 473*11447b59SVenkatesh Srinivas for (queue = 0; queue < nvqs; queue++) { 474*11447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue]; 475*11447b59SVenkatesh Srinivas info = &vq_info[queue]; 476*11447b59SVenkatesh Srinivas 477*11447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 478*11447b59SVenkatesh Srinivas 479*11447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 480*11447b59SVenkatesh Srinivas error = virtqueue_alloc(dev, queue, vq_size, 481*11447b59SVenkatesh Srinivas VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq); 482*11447b59SVenkatesh Srinivas if (error) 483*11447b59SVenkatesh Srinivas return (error); 484*11447b59SVenkatesh Srinivas 485*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 486*11447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 487*11447b59SVenkatesh Srinivas VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx); 488*11447b59SVenkatesh Srinivas if (error) 489*11447b59SVenkatesh Srinivas return (error); 490*11447b59SVenkatesh Srinivas } 491*11447b59SVenkatesh Srinivas 492*11447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 493*11447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 494*11447b59SVenkatesh Srinivas 495*11447b59SVenkatesh Srinivas *info->vqai_vq = vqx->vq; 496*11447b59SVenkatesh Srinivas sc->vtpci_nvqs++; 497*11447b59SVenkatesh Srinivas } 498*11447b59SVenkatesh Srinivas 499*11447b59SVenkatesh Srinivas return (0); 500*11447b59SVenkatesh Srinivas } 501*11447b59SVenkatesh Srinivas 502*11447b59SVenkatesh Srinivas static int 503*11447b59SVenkatesh Srinivas vtpci_setup_intr(device_t dev) 504*11447b59SVenkatesh Srinivas { 505*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 506*11447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires; 507*11447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 508*11447b59SVenkatesh Srinivas int i, flags, error; 509*11447b59SVenkatesh Srinivas 510*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 511*11447b59SVenkatesh Srinivas flags = INTR_MPSAFE; 512*11447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[0]; 513*11447b59SVenkatesh Srinivas 514*11447b59SVenkatesh Srinivas if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) { 515*11447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 516*11447b59SVenkatesh Srinivas (driver_intr_t *) vtpci_legacy_intr, sc, &ires->intrhand, NULL); 517*11447b59SVenkatesh Srinivas 518*11447b59SVenkatesh Srinivas return (error); 519*11447b59SVenkatesh Srinivas } 520*11447b59SVenkatesh Srinivas 521*11447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags,(driver_intr_t *) vtpci_config_intr, 522*11447b59SVenkatesh Srinivas sc, &ires->intrhand, NULL); 523*11447b59SVenkatesh Srinivas if (error) 524*11447b59SVenkatesh Srinivas return (error); 525*11447b59SVenkatesh Srinivas 526*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) { 527*11447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[1]; 528*11447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 529*11447b59SVenkatesh Srinivas (driver_intr_t *) vtpci_vq_shared_intr, sc, &ires->intrhand, NULL); 530*11447b59SVenkatesh Srinivas 531*11447b59SVenkatesh Srinivas return (error); 532*11447b59SVenkatesh Srinivas } 533*11447b59SVenkatesh Srinivas 534*11447b59SVenkatesh Srinivas /* Setup an interrupt handler for each virtqueue. */ 535*11447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nvqs; i++) { 536*11447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i]; 537*11447b59SVenkatesh Srinivas if (vqx->ires_idx < 1) 538*11447b59SVenkatesh Srinivas continue; 539*11447b59SVenkatesh Srinivas 540*11447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[vqx->ires_idx]; 541*11447b59SVenkatesh Srinivas error = bus_setup_intr(dev, ires->irq, flags, 542*11447b59SVenkatesh Srinivas (driver_intr_t *) vtpci_vq_intr, vqx->vq, &ires->intrhand, NULL); 543*11447b59SVenkatesh Srinivas if (error) 544*11447b59SVenkatesh Srinivas return (error); 545*11447b59SVenkatesh Srinivas } 546*11447b59SVenkatesh Srinivas 547*11447b59SVenkatesh Srinivas return (0); 548*11447b59SVenkatesh Srinivas } 549*11447b59SVenkatesh Srinivas 550*11447b59SVenkatesh Srinivas static void 551*11447b59SVenkatesh Srinivas vtpci_stop(device_t dev) 552*11447b59SVenkatesh Srinivas { 553*11447b59SVenkatesh Srinivas 554*11447b59SVenkatesh Srinivas vtpci_reset(device_get_softc(dev)); 555*11447b59SVenkatesh Srinivas } 556*11447b59SVenkatesh Srinivas 557*11447b59SVenkatesh Srinivas static int 558*11447b59SVenkatesh Srinivas vtpci_reinit(device_t dev, uint64_t features) 559*11447b59SVenkatesh Srinivas { 560*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 561*11447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 562*11447b59SVenkatesh Srinivas struct virtqueue *vq; 563*11447b59SVenkatesh Srinivas int queue, error; 564*11447b59SVenkatesh Srinivas uint16_t vq_size; 565*11447b59SVenkatesh Srinivas 566*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 567*11447b59SVenkatesh Srinivas 568*11447b59SVenkatesh Srinivas /* 569*11447b59SVenkatesh Srinivas * Redrive the device initialization. This is a bit of an abuse 570*11447b59SVenkatesh Srinivas * of the specification, but both VirtualBox and QEMU/KVM seem 571*11447b59SVenkatesh Srinivas * to play nice. We do not allow the host device to change from 572*11447b59SVenkatesh Srinivas * what was originally negotiated beyond what the guest driver 573*11447b59SVenkatesh Srinivas * changed (MSIX state should not change, number of virtqueues 574*11447b59SVenkatesh Srinivas * and their size remain the same, etc). 575*11447b59SVenkatesh Srinivas */ 576*11447b59SVenkatesh Srinivas 577*11447b59SVenkatesh Srinivas if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 578*11447b59SVenkatesh Srinivas vtpci_stop(dev); 579*11447b59SVenkatesh Srinivas 580*11447b59SVenkatesh Srinivas /* 581*11447b59SVenkatesh Srinivas * Quickly drive the status through ACK and DRIVER. The device 582*11447b59SVenkatesh Srinivas * does not become usable again until vtpci_reinit_complete(). 583*11447b59SVenkatesh Srinivas */ 584*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 585*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 586*11447b59SVenkatesh Srinivas 587*11447b59SVenkatesh Srinivas vtpci_negotiate_features(dev, features); 588*11447b59SVenkatesh Srinivas 589*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 590*11447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 591*11447b59SVenkatesh Srinivas VIRTIO_MSI_CONFIG_VECTOR, 0); 592*11447b59SVenkatesh Srinivas if (error) 593*11447b59SVenkatesh Srinivas return (error); 594*11447b59SVenkatesh Srinivas } 595*11447b59SVenkatesh Srinivas 596*11447b59SVenkatesh Srinivas for (queue = 0; queue < sc->vtpci_nvqs; queue++) { 597*11447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[queue]; 598*11447b59SVenkatesh Srinivas vq = vqx->vq; 599*11447b59SVenkatesh Srinivas 600*11447b59SVenkatesh Srinivas KASSERT(vq != NULL, ("vq %d not allocated", queue)); 601*11447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 602*11447b59SVenkatesh Srinivas 603*11447b59SVenkatesh Srinivas vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 604*11447b59SVenkatesh Srinivas error = virtqueue_reinit(vq, vq_size); 605*11447b59SVenkatesh Srinivas if (error) 606*11447b59SVenkatesh Srinivas return (error); 607*11447b59SVenkatesh Srinivas 608*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 609*11447b59SVenkatesh Srinivas error = vtpci_register_msix_vector(sc, 610*11447b59SVenkatesh Srinivas VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx); 611*11447b59SVenkatesh Srinivas if (error) 612*11447b59SVenkatesh Srinivas return (error); 613*11447b59SVenkatesh Srinivas } 614*11447b59SVenkatesh Srinivas 615*11447b59SVenkatesh Srinivas vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 616*11447b59SVenkatesh Srinivas virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 617*11447b59SVenkatesh Srinivas } 618*11447b59SVenkatesh Srinivas 619*11447b59SVenkatesh Srinivas return (0); 620*11447b59SVenkatesh Srinivas } 621*11447b59SVenkatesh Srinivas 622*11447b59SVenkatesh Srinivas static void 623*11447b59SVenkatesh Srinivas vtpci_reinit_complete(device_t dev) 624*11447b59SVenkatesh Srinivas { 625*11447b59SVenkatesh Srinivas 626*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 627*11447b59SVenkatesh Srinivas } 628*11447b59SVenkatesh Srinivas 629*11447b59SVenkatesh Srinivas static void 630*11447b59SVenkatesh Srinivas vtpci_notify_virtqueue(device_t dev, uint16_t queue) 631*11447b59SVenkatesh Srinivas { 632*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 633*11447b59SVenkatesh Srinivas 634*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 635*11447b59SVenkatesh Srinivas 636*11447b59SVenkatesh Srinivas vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue); 637*11447b59SVenkatesh Srinivas } 638*11447b59SVenkatesh Srinivas 639*11447b59SVenkatesh Srinivas static uint8_t 640*11447b59SVenkatesh Srinivas vtpci_get_status(device_t dev) 641*11447b59SVenkatesh Srinivas { 642*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 643*11447b59SVenkatesh Srinivas 644*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 645*11447b59SVenkatesh Srinivas 646*11447b59SVenkatesh Srinivas return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS)); 647*11447b59SVenkatesh Srinivas } 648*11447b59SVenkatesh Srinivas 649*11447b59SVenkatesh Srinivas static void 650*11447b59SVenkatesh Srinivas vtpci_set_status(device_t dev, uint8_t status) 651*11447b59SVenkatesh Srinivas { 652*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 653*11447b59SVenkatesh Srinivas 654*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 655*11447b59SVenkatesh Srinivas 656*11447b59SVenkatesh Srinivas if (status != VIRTIO_CONFIG_STATUS_RESET) 657*11447b59SVenkatesh Srinivas status |= vtpci_get_status(dev); 658*11447b59SVenkatesh Srinivas 659*11447b59SVenkatesh Srinivas vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status); 660*11447b59SVenkatesh Srinivas } 661*11447b59SVenkatesh Srinivas 662*11447b59SVenkatesh Srinivas static void 663*11447b59SVenkatesh Srinivas vtpci_read_dev_config(device_t dev, bus_size_t offset, 664*11447b59SVenkatesh Srinivas void *dst, int length) 665*11447b59SVenkatesh Srinivas { 666*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 667*11447b59SVenkatesh Srinivas bus_size_t off; 668*11447b59SVenkatesh Srinivas uint8_t *d; 669*11447b59SVenkatesh Srinivas int size; 670*11447b59SVenkatesh Srinivas 671*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 672*11447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset; 673*11447b59SVenkatesh Srinivas 674*11447b59SVenkatesh Srinivas for (d = dst; length > 0; d += size, off += size, length -= size) { 675*11447b59SVenkatesh Srinivas if (length >= 4) { 676*11447b59SVenkatesh Srinivas size = 4; 677*11447b59SVenkatesh Srinivas *(uint32_t *)d = vtpci_read_config_4(sc, off); 678*11447b59SVenkatesh Srinivas } else if (length >= 2) { 679*11447b59SVenkatesh Srinivas size = 2; 680*11447b59SVenkatesh Srinivas *(uint16_t *)d = vtpci_read_config_2(sc, off); 681*11447b59SVenkatesh Srinivas } else { 682*11447b59SVenkatesh Srinivas size = 1; 683*11447b59SVenkatesh Srinivas *d = vtpci_read_config_1(sc, off); 684*11447b59SVenkatesh Srinivas } 685*11447b59SVenkatesh Srinivas } 686*11447b59SVenkatesh Srinivas } 687*11447b59SVenkatesh Srinivas 688*11447b59SVenkatesh Srinivas static void 689*11447b59SVenkatesh Srinivas vtpci_write_dev_config(device_t dev, bus_size_t offset, 690*11447b59SVenkatesh Srinivas void *src, int length) 691*11447b59SVenkatesh Srinivas { 692*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 693*11447b59SVenkatesh Srinivas bus_size_t off; 694*11447b59SVenkatesh Srinivas uint8_t *s; 695*11447b59SVenkatesh Srinivas int size; 696*11447b59SVenkatesh Srinivas 697*11447b59SVenkatesh Srinivas sc = device_get_softc(dev); 698*11447b59SVenkatesh Srinivas off = VIRTIO_PCI_CONFIG(sc) + offset; 699*11447b59SVenkatesh Srinivas 700*11447b59SVenkatesh Srinivas for (s = src; length > 0; s += size, off += size, length -= size) { 701*11447b59SVenkatesh Srinivas if (length >= 4) { 702*11447b59SVenkatesh Srinivas size = 4; 703*11447b59SVenkatesh Srinivas vtpci_write_config_4(sc, off, *(uint32_t *)s); 704*11447b59SVenkatesh Srinivas } else if (length >= 2) { 705*11447b59SVenkatesh Srinivas size = 2; 706*11447b59SVenkatesh Srinivas vtpci_write_config_2(sc, off, *(uint16_t *)s); 707*11447b59SVenkatesh Srinivas } else { 708*11447b59SVenkatesh Srinivas size = 1; 709*11447b59SVenkatesh Srinivas vtpci_write_config_1(sc, off, *s); 710*11447b59SVenkatesh Srinivas } 711*11447b59SVenkatesh Srinivas } 712*11447b59SVenkatesh Srinivas } 713*11447b59SVenkatesh Srinivas 714*11447b59SVenkatesh Srinivas static void 715*11447b59SVenkatesh Srinivas vtpci_describe_features(struct vtpci_softc *sc, const char *msg, 716*11447b59SVenkatesh Srinivas uint64_t features) 717*11447b59SVenkatesh Srinivas { 718*11447b59SVenkatesh Srinivas device_t dev, child; 719*11447b59SVenkatesh Srinivas 720*11447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 721*11447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 722*11447b59SVenkatesh Srinivas 723*11447b59SVenkatesh Srinivas if (device_is_attached(child) && bootverbose == 0) 724*11447b59SVenkatesh Srinivas return; 725*11447b59SVenkatesh Srinivas 726*11447b59SVenkatesh Srinivas virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc); 727*11447b59SVenkatesh Srinivas } 728*11447b59SVenkatesh Srinivas 729*11447b59SVenkatesh Srinivas static void 730*11447b59SVenkatesh Srinivas vtpci_probe_and_attach_child(struct vtpci_softc *sc) 731*11447b59SVenkatesh Srinivas { 732*11447b59SVenkatesh Srinivas device_t dev, child; 733*11447b59SVenkatesh Srinivas 734*11447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 735*11447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 736*11447b59SVenkatesh Srinivas 737*11447b59SVenkatesh Srinivas if (child == NULL) 738*11447b59SVenkatesh Srinivas return; 739*11447b59SVenkatesh Srinivas 740*11447b59SVenkatesh Srinivas if (device_get_state(child) != DS_NOTPRESENT) 741*11447b59SVenkatesh Srinivas return; 742*11447b59SVenkatesh Srinivas 743*11447b59SVenkatesh Srinivas if (device_probe_child(dev, child) != 0) 744*11447b59SVenkatesh Srinivas return; 745*11447b59SVenkatesh Srinivas 746*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 747*11447b59SVenkatesh Srinivas if (DEVICE_ATTACH(child) != 0) { 748*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 749*11447b59SVenkatesh Srinivas vtpci_reset(sc); 750*11447b59SVenkatesh Srinivas vtpci_release_child_resources(sc); 751*11447b59SVenkatesh Srinivas 752*11447b59SVenkatesh Srinivas /* Reset status for future attempt. */ 753*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 754*11447b59SVenkatesh Srinivas } else 755*11447b59SVenkatesh Srinivas vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 756*11447b59SVenkatesh Srinivas } 757*11447b59SVenkatesh Srinivas 758*11447b59SVenkatesh Srinivas static int 759*11447b59SVenkatesh Srinivas vtpci_alloc_interrupts(struct vtpci_softc *sc, int flags, int nvqs, 760*11447b59SVenkatesh Srinivas struct vq_alloc_info *vq_info) 761*11447b59SVenkatesh Srinivas { 762*11447b59SVenkatesh Srinivas int i, nvectors, error; 763*11447b59SVenkatesh Srinivas 764*11447b59SVenkatesh Srinivas /* 765*11447b59SVenkatesh Srinivas * Only allocate a vector for virtqueues that are actually 766*11447b59SVenkatesh Srinivas * expecting an interrupt. 767*11447b59SVenkatesh Srinivas */ 768*11447b59SVenkatesh Srinivas for (nvectors = 0, i = 0; i < nvqs; i++) 769*11447b59SVenkatesh Srinivas if (vq_info[i].vqai_intr != NULL) 770*11447b59SVenkatesh Srinivas nvectors++; 771*11447b59SVenkatesh Srinivas 772*11447b59SVenkatesh Srinivas if (vtpci_disable_msix != 0 || 773*11447b59SVenkatesh Srinivas sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSIX || 774*11447b59SVenkatesh Srinivas flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX || 775*11447b59SVenkatesh Srinivas vtpci_alloc_msix(sc, nvectors) != 0) { 776*11447b59SVenkatesh Srinivas /* 777*11447b59SVenkatesh Srinivas * Use MSI interrupts if available. Otherwise, we fallback 778*11447b59SVenkatesh Srinivas * to legacy interrupts. 779*11447b59SVenkatesh Srinivas */ 780*11447b59SVenkatesh Srinivas if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSI) == 0 && 781*11447b59SVenkatesh Srinivas vtpci_alloc_msi(sc) == 0) 782*11447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI; 783*11447b59SVenkatesh Srinivas 784*11447b59SVenkatesh Srinivas sc->vtpci_nintr_res = 1; 785*11447b59SVenkatesh Srinivas } 786*11447b59SVenkatesh Srinivas 787*11447b59SVenkatesh Srinivas error = vtpci_alloc_intr_resources(sc, nvqs, vq_info); 788*11447b59SVenkatesh Srinivas 789*11447b59SVenkatesh Srinivas return (error); 790*11447b59SVenkatesh Srinivas } 791*11447b59SVenkatesh Srinivas 792*11447b59SVenkatesh Srinivas static int 793*11447b59SVenkatesh Srinivas vtpci_alloc_intr_resources(struct vtpci_softc *sc, int nvqs, 794*11447b59SVenkatesh Srinivas struct vq_alloc_info *vq_info) 795*11447b59SVenkatesh Srinivas { 796*11447b59SVenkatesh Srinivas device_t dev; 797*11447b59SVenkatesh Srinivas struct resource *irq; 798*11447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 799*11447b59SVenkatesh Srinivas int i, rid, flags, res_idx; 800*11447b59SVenkatesh Srinivas 801*11447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 802*11447b59SVenkatesh Srinivas flags = RF_ACTIVE; 803*11447b59SVenkatesh Srinivas 804*11447b59SVenkatesh Srinivas if ((sc->vtpci_flags & 805*11447b59SVenkatesh Srinivas (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) == 0) { 806*11447b59SVenkatesh Srinivas rid = 0; 807*11447b59SVenkatesh Srinivas flags |= RF_SHAREABLE; 808*11447b59SVenkatesh Srinivas } else 809*11447b59SVenkatesh Srinivas rid = 1; 810*11447b59SVenkatesh Srinivas 811*11447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nintr_res; i++) { 812*11447b59SVenkatesh Srinivas irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags); 813*11447b59SVenkatesh Srinivas if (irq == NULL) 814*11447b59SVenkatesh Srinivas return (ENXIO); 815*11447b59SVenkatesh Srinivas 816*11447b59SVenkatesh Srinivas sc->vtpci_intr_res[i].irq = irq; 817*11447b59SVenkatesh Srinivas sc->vtpci_intr_res[i].rid = rid++; 818*11447b59SVenkatesh Srinivas } 819*11447b59SVenkatesh Srinivas 820*11447b59SVenkatesh Srinivas /* 821*11447b59SVenkatesh Srinivas * Map the virtqueue into the correct index in vq_intr_res[]. Note the 822*11447b59SVenkatesh Srinivas * first index is reserved for configuration changes notifications. 823*11447b59SVenkatesh Srinivas */ 824*11447b59SVenkatesh Srinivas for (i = 0, res_idx = 1; i < nvqs; i++) { 825*11447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i]; 826*11447b59SVenkatesh Srinivas 827*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 828*11447b59SVenkatesh Srinivas if (vq_info[i].vqai_intr == NULL) 829*11447b59SVenkatesh Srinivas vqx->ires_idx = -1; 830*11447b59SVenkatesh Srinivas else if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) 831*11447b59SVenkatesh Srinivas vqx->ires_idx = res_idx; 832*11447b59SVenkatesh Srinivas else 833*11447b59SVenkatesh Srinivas vqx->ires_idx = res_idx++; 834*11447b59SVenkatesh Srinivas } else 835*11447b59SVenkatesh Srinivas vqx->ires_idx = -1; 836*11447b59SVenkatesh Srinivas } 837*11447b59SVenkatesh Srinivas 838*11447b59SVenkatesh Srinivas return (0); 839*11447b59SVenkatesh Srinivas } 840*11447b59SVenkatesh Srinivas 841*11447b59SVenkatesh Srinivas static int 842*11447b59SVenkatesh Srinivas vtpci_alloc_msi(struct vtpci_softc *sc) 843*11447b59SVenkatesh Srinivas { 844*11447b59SVenkatesh Srinivas device_t dev; 845*11447b59SVenkatesh Srinivas int nmsi, cnt; 846*11447b59SVenkatesh Srinivas u_int irq_flags; 847*11447b59SVenkatesh Srinivas 848*11447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 849*11447b59SVenkatesh Srinivas nmsi = pci_msi_count(dev); 850*11447b59SVenkatesh Srinivas 851*11447b59SVenkatesh Srinivas if (nmsi < 1) 852*11447b59SVenkatesh Srinivas return (1); 853*11447b59SVenkatesh Srinivas 854*11447b59SVenkatesh Srinivas cnt = 1; 855*11447b59SVenkatesh Srinivas 856*11447b59SVenkatesh Srinivas sc->vtpci_irq_rid = 0; 857*11447b59SVenkatesh Srinivas sc->vtpci_irq_type = pci_alloc_1intr(dev, 1, 858*11447b59SVenkatesh Srinivas &sc->vtpci_irq_rid, &irq_flags); 859*11447b59SVenkatesh Srinivas 860*11447b59SVenkatesh Srinivas 861*11447b59SVenkatesh Srinivas return (1); 862*11447b59SVenkatesh Srinivas } 863*11447b59SVenkatesh Srinivas 864*11447b59SVenkatesh Srinivas static int 865*11447b59SVenkatesh Srinivas vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors) 866*11447b59SVenkatesh Srinivas { 867*11447b59SVenkatesh Srinivas /* XXX(vsrinivas): Huh? Is this how MSI-X works?*/ 868*11447b59SVenkatesh Srinivas /* XXX(vsrinivas): All of this was disabled... */ 869*11447b59SVenkatesh Srinivas #ifdef OLD_MSI 870*11447b59SVenkatesh Srinivas device_t dev; 871*11447b59SVenkatesh Srinivas int nmsix, cnt, required; 872*11447b59SVenkatesh Srinivas 873*11447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 874*11447b59SVenkatesh Srinivas 875*11447b59SVenkatesh Srinivas nmsix = pci_msix_count(dev); 876*11447b59SVenkatesh Srinivas if (nmsix < 1) 877*11447b59SVenkatesh Srinivas return (1); 878*11447b59SVenkatesh Srinivas 879*11447b59SVenkatesh Srinivas /* An additional vector is needed for the config changes. */ 880*11447b59SVenkatesh Srinivas required = nvectors + 1; 881*11447b59SVenkatesh Srinivas if (nmsix >= required) { 882*11447b59SVenkatesh Srinivas cnt = required; 883*11447b59SVenkatesh Srinivas if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) 884*11447b59SVenkatesh Srinivas goto out; 885*11447b59SVenkatesh Srinivas 886*11447b59SVenkatesh Srinivas pci_release_msi(dev); 887*11447b59SVenkatesh Srinivas } 888*11447b59SVenkatesh Srinivas 889*11447b59SVenkatesh Srinivas /* Attempt shared MSIX configuration. */ 890*11447b59SVenkatesh Srinivas required = 2; 891*11447b59SVenkatesh Srinivas if (nmsix >= required) { 892*11447b59SVenkatesh Srinivas cnt = required; 893*11447b59SVenkatesh Srinivas if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 894*11447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_SHARED_MSIX; 895*11447b59SVenkatesh Srinivas goto out; 896*11447b59SVenkatesh Srinivas } 897*11447b59SVenkatesh Srinivas 898*11447b59SVenkatesh Srinivas pci_release_msi(dev); 899*11447b59SVenkatesh Srinivas } 900*11447b59SVenkatesh Srinivas 901*11447b59SVenkatesh Srinivas return (1); 902*11447b59SVenkatesh Srinivas 903*11447b59SVenkatesh Srinivas out: 904*11447b59SVenkatesh Srinivas sc->vtpci_nintr_res = required; 905*11447b59SVenkatesh Srinivas sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX; 906*11447b59SVenkatesh Srinivas 907*11447b59SVenkatesh Srinivas if (bootverbose) { 908*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) 909*11447b59SVenkatesh Srinivas device_printf(dev, "using shared virtqueue MSIX\n"); 910*11447b59SVenkatesh Srinivas else 911*11447b59SVenkatesh Srinivas device_printf(dev, "using per virtqueue MSIX\n"); 912*11447b59SVenkatesh Srinivas } 913*11447b59SVenkatesh Srinivas #endif 914*11447b59SVenkatesh Srinivas return (0); 915*11447b59SVenkatesh Srinivas } 916*11447b59SVenkatesh Srinivas 917*11447b59SVenkatesh Srinivas static int 918*11447b59SVenkatesh Srinivas vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx) 919*11447b59SVenkatesh Srinivas { 920*11447b59SVenkatesh Srinivas device_t dev; 921*11447b59SVenkatesh Srinivas uint16_t vector; 922*11447b59SVenkatesh Srinivas 923*11447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 924*11447b59SVenkatesh Srinivas 925*11447b59SVenkatesh Srinivas if (offset != VIRTIO_MSI_CONFIG_VECTOR && 926*11447b59SVenkatesh Srinivas offset != VIRTIO_MSI_QUEUE_VECTOR) 927*11447b59SVenkatesh Srinivas return (EINVAL); 928*11447b59SVenkatesh Srinivas 929*11447b59SVenkatesh Srinivas if (res_idx != -1) { 930*11447b59SVenkatesh Srinivas /* Map from rid to host vector. */ 931*11447b59SVenkatesh Srinivas vector = sc->vtpci_intr_res[res_idx].rid - 1; 932*11447b59SVenkatesh Srinivas } else 933*11447b59SVenkatesh Srinivas vector = VIRTIO_MSI_NO_VECTOR; 934*11447b59SVenkatesh Srinivas 935*11447b59SVenkatesh Srinivas /* The first resource is special; make sure it is used correctly. */ 936*11447b59SVenkatesh Srinivas if (res_idx == 0) { 937*11447b59SVenkatesh Srinivas KASSERT(vector == 0, ("unexpected config vector")); 938*11447b59SVenkatesh Srinivas KASSERT(offset == VIRTIO_MSI_CONFIG_VECTOR, 939*11447b59SVenkatesh Srinivas ("unexpected config offset")); 940*11447b59SVenkatesh Srinivas } 941*11447b59SVenkatesh Srinivas 942*11447b59SVenkatesh Srinivas vtpci_write_config_2(sc, offset, vector); 943*11447b59SVenkatesh Srinivas 944*11447b59SVenkatesh Srinivas if (vtpci_read_config_2(sc, offset) != vector) { 945*11447b59SVenkatesh Srinivas device_printf(dev, "insufficient host resources for " 946*11447b59SVenkatesh Srinivas "MSIX interrupts\n"); 947*11447b59SVenkatesh Srinivas return (ENODEV); 948*11447b59SVenkatesh Srinivas } 949*11447b59SVenkatesh Srinivas 950*11447b59SVenkatesh Srinivas return (0); 951*11447b59SVenkatesh Srinivas } 952*11447b59SVenkatesh Srinivas 953*11447b59SVenkatesh Srinivas static void 954*11447b59SVenkatesh Srinivas vtpci_free_interrupts(struct vtpci_softc *sc) 955*11447b59SVenkatesh Srinivas { 956*11447b59SVenkatesh Srinivas device_t dev; 957*11447b59SVenkatesh Srinivas struct vtpci_intr_resource *ires; 958*11447b59SVenkatesh Srinivas int i; 959*11447b59SVenkatesh Srinivas 960*11447b59SVenkatesh Srinivas dev = sc->vtpci_dev; 961*11447b59SVenkatesh Srinivas sc->vtpci_nintr_res = 0; 962*11447b59SVenkatesh Srinivas 963*11447b59SVenkatesh Srinivas if (sc->vtpci_flags & (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) { 964*11447b59SVenkatesh Srinivas pci_release_msi(dev); 965*11447b59SVenkatesh Srinivas sc->vtpci_flags &= ~(VIRTIO_PCI_FLAG_MSI | 966*11447b59SVenkatesh Srinivas VIRTIO_PCI_FLAG_MSIX | VIRTIO_PCI_FLAG_SHARED_MSIX); 967*11447b59SVenkatesh Srinivas } 968*11447b59SVenkatesh Srinivas 969*11447b59SVenkatesh Srinivas for (i = 0; i < 1 + VIRTIO_MAX_VIRTQUEUES; i++) { 970*11447b59SVenkatesh Srinivas ires = &sc->vtpci_intr_res[i]; 971*11447b59SVenkatesh Srinivas 972*11447b59SVenkatesh Srinivas if (ires->intrhand != NULL) { 973*11447b59SVenkatesh Srinivas bus_teardown_intr(dev, ires->irq, ires->intrhand); 974*11447b59SVenkatesh Srinivas ires->intrhand = NULL; 975*11447b59SVenkatesh Srinivas } 976*11447b59SVenkatesh Srinivas 977*11447b59SVenkatesh Srinivas if (ires->irq != NULL) { 978*11447b59SVenkatesh Srinivas bus_release_resource(dev, SYS_RES_IRQ, ires->rid, 979*11447b59SVenkatesh Srinivas ires->irq); 980*11447b59SVenkatesh Srinivas ires->irq = NULL; 981*11447b59SVenkatesh Srinivas } 982*11447b59SVenkatesh Srinivas 983*11447b59SVenkatesh Srinivas ires->rid = -1; 984*11447b59SVenkatesh Srinivas } 985*11447b59SVenkatesh Srinivas } 986*11447b59SVenkatesh Srinivas 987*11447b59SVenkatesh Srinivas static void 988*11447b59SVenkatesh Srinivas vtpci_free_virtqueues(struct vtpci_softc *sc) 989*11447b59SVenkatesh Srinivas { 990*11447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 991*11447b59SVenkatesh Srinivas int i; 992*11447b59SVenkatesh Srinivas 993*11447b59SVenkatesh Srinivas sc->vtpci_nvqs = 0; 994*11447b59SVenkatesh Srinivas 995*11447b59SVenkatesh Srinivas for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) { 996*11447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[i]; 997*11447b59SVenkatesh Srinivas 998*11447b59SVenkatesh Srinivas if (vqx->vq != NULL) { 999*11447b59SVenkatesh Srinivas virtqueue_free(vqx->vq); 1000*11447b59SVenkatesh Srinivas vqx->vq = NULL; 1001*11447b59SVenkatesh Srinivas } 1002*11447b59SVenkatesh Srinivas } 1003*11447b59SVenkatesh Srinivas } 1004*11447b59SVenkatesh Srinivas 1005*11447b59SVenkatesh Srinivas static void 1006*11447b59SVenkatesh Srinivas vtpci_release_child_resources(struct vtpci_softc *sc) 1007*11447b59SVenkatesh Srinivas { 1008*11447b59SVenkatesh Srinivas 1009*11447b59SVenkatesh Srinivas vtpci_free_interrupts(sc); 1010*11447b59SVenkatesh Srinivas vtpci_free_virtqueues(sc); 1011*11447b59SVenkatesh Srinivas } 1012*11447b59SVenkatesh Srinivas 1013*11447b59SVenkatesh Srinivas static void 1014*11447b59SVenkatesh Srinivas vtpci_reset(struct vtpci_softc *sc) 1015*11447b59SVenkatesh Srinivas { 1016*11447b59SVenkatesh Srinivas 1017*11447b59SVenkatesh Srinivas /* 1018*11447b59SVenkatesh Srinivas * Setting the status to RESET sets the host device to 1019*11447b59SVenkatesh Srinivas * the original, uninitialized state. 1020*11447b59SVenkatesh Srinivas */ 1021*11447b59SVenkatesh Srinivas vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET); 1022*11447b59SVenkatesh Srinivas } 1023*11447b59SVenkatesh Srinivas 1024*11447b59SVenkatesh Srinivas static int 1025*11447b59SVenkatesh Srinivas vtpci_legacy_intr(void *xsc) 1026*11447b59SVenkatesh Srinivas { 1027*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 1028*11447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 1029*11447b59SVenkatesh Srinivas int i; 1030*11447b59SVenkatesh Srinivas uint8_t isr; 1031*11447b59SVenkatesh Srinivas 1032*11447b59SVenkatesh Srinivas sc = xsc; 1033*11447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[0]; 1034*11447b59SVenkatesh Srinivas 1035*11447b59SVenkatesh Srinivas /* Reading the ISR also clears it. */ 1036*11447b59SVenkatesh Srinivas isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR); 1037*11447b59SVenkatesh Srinivas 1038*11447b59SVenkatesh Srinivas if (isr & VIRTIO_PCI_ISR_CONFIG) 1039*11447b59SVenkatesh Srinivas vtpci_config_intr(sc); 1040*11447b59SVenkatesh Srinivas 1041*11447b59SVenkatesh Srinivas if (isr & VIRTIO_PCI_ISR_INTR) 1042*11447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) 1043*11447b59SVenkatesh Srinivas virtqueue_intr(vqx->vq); 1044*11447b59SVenkatesh Srinivas 1045*11447b59SVenkatesh Srinivas return isr; 1046*11447b59SVenkatesh Srinivas } 1047*11447b59SVenkatesh Srinivas 1048*11447b59SVenkatesh Srinivas static int 1049*11447b59SVenkatesh Srinivas vtpci_vq_shared_intr(void *xsc) 1050*11447b59SVenkatesh Srinivas { 1051*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 1052*11447b59SVenkatesh Srinivas struct vtpci_virtqueue *vqx; 1053*11447b59SVenkatesh Srinivas int i, rc; 1054*11447b59SVenkatesh Srinivas 1055*11447b59SVenkatesh Srinivas rc = 0; 1056*11447b59SVenkatesh Srinivas sc = xsc; 1057*11447b59SVenkatesh Srinivas vqx = &sc->vtpci_vqx[0]; 1058*11447b59SVenkatesh Srinivas 1059*11447b59SVenkatesh Srinivas for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) 1060*11447b59SVenkatesh Srinivas rc |= virtqueue_intr(vqx->vq); 1061*11447b59SVenkatesh Srinivas 1062*11447b59SVenkatesh Srinivas return rc; 1063*11447b59SVenkatesh Srinivas } 1064*11447b59SVenkatesh Srinivas 1065*11447b59SVenkatesh Srinivas static int 1066*11447b59SVenkatesh Srinivas vtpci_vq_intr(void *xvq) 1067*11447b59SVenkatesh Srinivas { 1068*11447b59SVenkatesh Srinivas struct virtqueue *vq; 1069*11447b59SVenkatesh Srinivas int rc; 1070*11447b59SVenkatesh Srinivas 1071*11447b59SVenkatesh Srinivas vq = xvq; 1072*11447b59SVenkatesh Srinivas rc = virtqueue_intr(vq); 1073*11447b59SVenkatesh Srinivas 1074*11447b59SVenkatesh Srinivas return rc; 1075*11447b59SVenkatesh Srinivas } 1076*11447b59SVenkatesh Srinivas 1077*11447b59SVenkatesh Srinivas static int 1078*11447b59SVenkatesh Srinivas vtpci_config_intr(void *xsc) 1079*11447b59SVenkatesh Srinivas { 1080*11447b59SVenkatesh Srinivas struct vtpci_softc *sc; 1081*11447b59SVenkatesh Srinivas device_t child; 1082*11447b59SVenkatesh Srinivas int rc; 1083*11447b59SVenkatesh Srinivas 1084*11447b59SVenkatesh Srinivas rc = 0; 1085*11447b59SVenkatesh Srinivas sc = xsc; 1086*11447b59SVenkatesh Srinivas child = sc->vtpci_child_dev; 1087*11447b59SVenkatesh Srinivas 1088*11447b59SVenkatesh Srinivas if (child != NULL) 1089*11447b59SVenkatesh Srinivas rc = VIRTIO_CONFIG_CHANGE(child); 1090*11447b59SVenkatesh Srinivas 1091*11447b59SVenkatesh Srinivas return rc; 1092*11447b59SVenkatesh Srinivas } 1093