110b59a9bSPeter Grehan /*- 2abd6790cSBryan Venteicher * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org> 310b59a9bSPeter Grehan * All rights reserved. 410b59a9bSPeter Grehan * 510b59a9bSPeter Grehan * Redistribution and use in source and binary forms, with or without 610b59a9bSPeter Grehan * modification, are permitted provided that the following conditions 710b59a9bSPeter Grehan * are met: 810b59a9bSPeter Grehan * 1. Redistributions of source code must retain the above copyright 910b59a9bSPeter Grehan * notice unmodified, this list of conditions, and the following 1010b59a9bSPeter Grehan * disclaimer. 1110b59a9bSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 1210b59a9bSPeter Grehan * notice, this list of conditions and the following disclaimer in the 1310b59a9bSPeter Grehan * documentation and/or other materials provided with the distribution. 1410b59a9bSPeter Grehan * 1510b59a9bSPeter Grehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1610b59a9bSPeter Grehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1710b59a9bSPeter Grehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1810b59a9bSPeter Grehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1910b59a9bSPeter Grehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2010b59a9bSPeter Grehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2110b59a9bSPeter Grehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2210b59a9bSPeter Grehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2310b59a9bSPeter Grehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2410b59a9bSPeter Grehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2510b59a9bSPeter Grehan */ 2610b59a9bSPeter Grehan 2710b59a9bSPeter Grehan /* Driver for the VirtIO PCI interface. */ 2810b59a9bSPeter Grehan 2910b59a9bSPeter Grehan #include <sys/cdefs.h> 3010b59a9bSPeter Grehan __FBSDID("$FreeBSD$"); 3110b59a9bSPeter Grehan 3210b59a9bSPeter Grehan #include <sys/param.h> 3310b59a9bSPeter Grehan #include <sys/systm.h> 3410b59a9bSPeter Grehan #include <sys/bus.h> 3510b59a9bSPeter Grehan #include <sys/kernel.h> 3610b59a9bSPeter Grehan #include <sys/module.h> 3710b59a9bSPeter Grehan #include <sys/malloc.h> 3810b59a9bSPeter Grehan 3910b59a9bSPeter Grehan #include <machine/bus.h> 4010b59a9bSPeter Grehan #include <machine/resource.h> 4110b59a9bSPeter Grehan #include <sys/bus.h> 4210b59a9bSPeter Grehan #include <sys/rman.h> 4310b59a9bSPeter Grehan 4410b59a9bSPeter Grehan #include <dev/pci/pcivar.h> 4510b59a9bSPeter Grehan #include <dev/pci/pcireg.h> 4610b59a9bSPeter Grehan 4710b59a9bSPeter Grehan #include <dev/virtio/virtio.h> 4810b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h> 4910b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h> 5010b59a9bSPeter Grehan 5110b59a9bSPeter Grehan #include "virtio_bus_if.h" 5210b59a9bSPeter Grehan #include "virtio_if.h" 5310b59a9bSPeter Grehan 54*62a69c41SBryan Venteicher struct vtpci_interrupt { 55*62a69c41SBryan Venteicher struct resource *vti_irq; 56*62a69c41SBryan Venteicher int vti_rid; 57*62a69c41SBryan Venteicher void *vti_handler; 58*62a69c41SBryan Venteicher }; 59*62a69c41SBryan Venteicher 60*62a69c41SBryan Venteicher struct vtpci_virtqueue { 61*62a69c41SBryan Venteicher struct virtqueue *vtv_vq; 62*62a69c41SBryan Venteicher int vtv_no_intr; 63*62a69c41SBryan Venteicher }; 64*62a69c41SBryan Venteicher 6510b59a9bSPeter Grehan struct vtpci_softc { 6610b59a9bSPeter Grehan device_t vtpci_dev; 6710b59a9bSPeter Grehan struct resource *vtpci_res; 6810b59a9bSPeter Grehan struct resource *vtpci_msix_res; 6910b59a9bSPeter Grehan uint64_t vtpci_features; 7010b59a9bSPeter Grehan uint32_t vtpci_flags; 71310dacd0SPeter Grehan #define VTPCI_FLAG_NO_MSI 0x0001 72310dacd0SPeter Grehan #define VTPCI_FLAG_NO_MSIX 0x0002 73310dacd0SPeter Grehan #define VTPCI_FLAG_LEGACY 0x1000 74310dacd0SPeter Grehan #define VTPCI_FLAG_MSI 0x2000 75310dacd0SPeter Grehan #define VTPCI_FLAG_MSIX 0x4000 76310dacd0SPeter Grehan #define VTPCI_FLAG_SHARED_MSIX 0x8000 77310dacd0SPeter Grehan #define VTPCI_FLAG_ITYPE_MASK 0xF000 7810b59a9bSPeter Grehan 79310dacd0SPeter Grehan /* This "bus" will only ever have one child. */ 8010b59a9bSPeter Grehan device_t vtpci_child_dev; 8110b59a9bSPeter Grehan struct virtio_feature_desc *vtpci_child_feat_desc; 8210b59a9bSPeter Grehan 8310b59a9bSPeter Grehan int vtpci_nvqs; 84*62a69c41SBryan Venteicher struct vtpci_virtqueue *vtpci_vqs; 8510b59a9bSPeter Grehan 8610b59a9bSPeter Grehan /* 87*62a69c41SBryan Venteicher * Ideally, each virtqueue that the driver provides a callback for will 88*62a69c41SBryan Venteicher * receive its own MSIX vector. If there are not sufficient vectors 89*62a69c41SBryan Venteicher * available, then attempt to have all the VQs share one vector. For 90*62a69c41SBryan Venteicher * MSIX, the configuration changed notifications must be on their own 91*62a69c41SBryan Venteicher * vector. 9210b59a9bSPeter Grehan * 93*62a69c41SBryan Venteicher * If MSIX is not available, we will attempt to have the whole device 94*62a69c41SBryan Venteicher * share one MSI vector, and then, finally, one legacy interrupt. 9510b59a9bSPeter Grehan */ 96*62a69c41SBryan Venteicher struct vtpci_interrupt vtpci_device_interrupt; 97*62a69c41SBryan Venteicher struct vtpci_interrupt *vtpci_msix_vq_interrupts; 98*62a69c41SBryan Venteicher int vtpci_nmsix_resources; 9910b59a9bSPeter Grehan }; 10010b59a9bSPeter Grehan 10110b59a9bSPeter Grehan static int vtpci_probe(device_t); 10210b59a9bSPeter Grehan static int vtpci_attach(device_t); 10310b59a9bSPeter Grehan static int vtpci_detach(device_t); 10410b59a9bSPeter Grehan static int vtpci_suspend(device_t); 10510b59a9bSPeter Grehan static int vtpci_resume(device_t); 10610b59a9bSPeter Grehan static int vtpci_shutdown(device_t); 10710b59a9bSPeter Grehan static void vtpci_driver_added(device_t, driver_t *); 10810b59a9bSPeter Grehan static void vtpci_child_detached(device_t, device_t); 10910b59a9bSPeter Grehan static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *); 11010b59a9bSPeter Grehan static int vtpci_write_ivar(device_t, device_t, int, uintptr_t); 11110b59a9bSPeter Grehan 11210b59a9bSPeter Grehan static uint64_t vtpci_negotiate_features(device_t, uint64_t); 11310b59a9bSPeter Grehan static int vtpci_with_feature(device_t, uint64_t); 11410b59a9bSPeter Grehan static int vtpci_alloc_virtqueues(device_t, int, int, 11510b59a9bSPeter Grehan struct vq_alloc_info *); 11610b59a9bSPeter Grehan static int vtpci_setup_intr(device_t, enum intr_type); 11710b59a9bSPeter Grehan static void vtpci_stop(device_t); 11810b59a9bSPeter Grehan static int vtpci_reinit(device_t, uint64_t); 11910b59a9bSPeter Grehan static void vtpci_reinit_complete(device_t); 12010b59a9bSPeter Grehan static void vtpci_notify_virtqueue(device_t, uint16_t); 12110b59a9bSPeter Grehan static uint8_t vtpci_get_status(device_t); 12210b59a9bSPeter Grehan static void vtpci_set_status(device_t, uint8_t); 12310b59a9bSPeter Grehan static void vtpci_read_dev_config(device_t, bus_size_t, void *, int); 12410b59a9bSPeter Grehan static void vtpci_write_dev_config(device_t, bus_size_t, void *, int); 12510b59a9bSPeter Grehan 12610b59a9bSPeter Grehan static void vtpci_describe_features(struct vtpci_softc *, const char *, 12710b59a9bSPeter Grehan uint64_t); 12810b59a9bSPeter Grehan static void vtpci_probe_and_attach_child(struct vtpci_softc *); 12910b59a9bSPeter Grehan 13010b59a9bSPeter Grehan static int vtpci_alloc_msix(struct vtpci_softc *, int); 131310dacd0SPeter Grehan static int vtpci_alloc_msi(struct vtpci_softc *); 132310dacd0SPeter Grehan static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *); 133310dacd0SPeter Grehan static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *); 134310dacd0SPeter Grehan static int vtpci_alloc_intr_msi(struct vtpci_softc *); 135310dacd0SPeter Grehan static int vtpci_alloc_intr_legacy(struct vtpci_softc *); 136*62a69c41SBryan Venteicher static int vtpci_alloc_interrupt(struct vtpci_softc *, int, int, 137*62a69c41SBryan Venteicher struct vtpci_interrupt *); 138310dacd0SPeter Grehan static int vtpci_alloc_intr_resources(struct vtpci_softc *); 139310dacd0SPeter Grehan 140310dacd0SPeter Grehan static int vtpci_setup_legacy_interrupt(struct vtpci_softc *, 141310dacd0SPeter Grehan enum intr_type); 142*62a69c41SBryan Venteicher static int vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *, 143*62a69c41SBryan Venteicher enum intr_type); 144310dacd0SPeter Grehan static int vtpci_setup_msix_interrupts(struct vtpci_softc *, 145310dacd0SPeter Grehan enum intr_type); 146310dacd0SPeter Grehan static int vtpci_setup_interrupts(struct vtpci_softc *, enum intr_type); 147310dacd0SPeter Grehan 148*62a69c41SBryan Venteicher static int vtpci_register_msix_vector(struct vtpci_softc *, int, 149*62a69c41SBryan Venteicher struct vtpci_interrupt *); 150310dacd0SPeter Grehan static int vtpci_set_host_msix_vectors(struct vtpci_softc *); 151310dacd0SPeter Grehan static int vtpci_reinit_virtqueue(struct vtpci_softc *, int); 15210b59a9bSPeter Grehan 153*62a69c41SBryan Venteicher static void vtpci_free_interrupt(struct vtpci_softc *, 154*62a69c41SBryan Venteicher struct vtpci_interrupt *); 15510b59a9bSPeter Grehan static void vtpci_free_interrupts(struct vtpci_softc *); 15610b59a9bSPeter Grehan static void vtpci_free_virtqueues(struct vtpci_softc *); 15710b59a9bSPeter Grehan static void vtpci_release_child_resources(struct vtpci_softc *); 158*62a69c41SBryan Venteicher static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *); 15910b59a9bSPeter Grehan static void vtpci_reset(struct vtpci_softc *); 16010b59a9bSPeter Grehan 161310dacd0SPeter Grehan static void vtpci_select_virtqueue(struct vtpci_softc *, int); 162310dacd0SPeter Grehan 1636632efe4SBryan Venteicher static void vtpci_legacy_intr(void *); 1646632efe4SBryan Venteicher static int vtpci_vq_shared_intr_filter(void *); 1656632efe4SBryan Venteicher static void vtpci_vq_shared_intr(void *); 1666632efe4SBryan Venteicher static int vtpci_vq_intr_filter(void *); 1676632efe4SBryan Venteicher static void vtpci_vq_intr(void *); 1686632efe4SBryan Venteicher static void vtpci_config_intr(void *); 16910b59a9bSPeter Grehan 170310dacd0SPeter Grehan #define vtpci_setup_msi_interrupt vtpci_setup_legacy_interrupt 171310dacd0SPeter Grehan 17210b59a9bSPeter Grehan /* 17310b59a9bSPeter Grehan * I/O port read/write wrappers. 17410b59a9bSPeter Grehan */ 17510b59a9bSPeter Grehan #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o)) 17610b59a9bSPeter Grehan #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o)) 17710b59a9bSPeter Grehan #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o)) 17810b59a9bSPeter Grehan #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v)) 17910b59a9bSPeter Grehan #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v)) 18010b59a9bSPeter Grehan #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v)) 18110b59a9bSPeter Grehan 18210b59a9bSPeter Grehan /* Tunables. */ 18310b59a9bSPeter Grehan static int vtpci_disable_msix = 0; 18410b59a9bSPeter Grehan TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 18510b59a9bSPeter Grehan 18610b59a9bSPeter Grehan static device_method_t vtpci_methods[] = { 18710b59a9bSPeter Grehan /* Device interface. */ 18810b59a9bSPeter Grehan DEVMETHOD(device_probe, vtpci_probe), 18910b59a9bSPeter Grehan DEVMETHOD(device_attach, vtpci_attach), 19010b59a9bSPeter Grehan DEVMETHOD(device_detach, vtpci_detach), 19110b59a9bSPeter Grehan DEVMETHOD(device_suspend, vtpci_suspend), 19210b59a9bSPeter Grehan DEVMETHOD(device_resume, vtpci_resume), 19310b59a9bSPeter Grehan DEVMETHOD(device_shutdown, vtpci_shutdown), 19410b59a9bSPeter Grehan 19510b59a9bSPeter Grehan /* Bus interface. */ 19610b59a9bSPeter Grehan DEVMETHOD(bus_driver_added, vtpci_driver_added), 19710b59a9bSPeter Grehan DEVMETHOD(bus_child_detached, vtpci_child_detached), 19810b59a9bSPeter Grehan DEVMETHOD(bus_read_ivar, vtpci_read_ivar), 19910b59a9bSPeter Grehan DEVMETHOD(bus_write_ivar, vtpci_write_ivar), 20010b59a9bSPeter Grehan 20110b59a9bSPeter Grehan /* VirtIO bus interface. */ 20210b59a9bSPeter Grehan DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features), 20310b59a9bSPeter Grehan DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature), 20410b59a9bSPeter Grehan DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues), 20510b59a9bSPeter Grehan DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr), 20610b59a9bSPeter Grehan DEVMETHOD(virtio_bus_stop, vtpci_stop), 20710b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit, vtpci_reinit), 20810b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete), 20910b59a9bSPeter Grehan DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), 21010b59a9bSPeter Grehan DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), 21110b59a9bSPeter Grehan DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), 21210b59a9bSPeter Grehan 213b8a58707SPeter Grehan DEVMETHOD_END 21410b59a9bSPeter Grehan }; 21510b59a9bSPeter Grehan 21610b59a9bSPeter Grehan static driver_t vtpci_driver = { 21710b59a9bSPeter Grehan "virtio_pci", 21810b59a9bSPeter Grehan vtpci_methods, 21910b59a9bSPeter Grehan sizeof(struct vtpci_softc) 22010b59a9bSPeter Grehan }; 22110b59a9bSPeter Grehan 22210b59a9bSPeter Grehan devclass_t vtpci_devclass; 22310b59a9bSPeter Grehan 22410b59a9bSPeter Grehan DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0); 22510b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1); 22610b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 22710b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 22810b59a9bSPeter Grehan 22910b59a9bSPeter Grehan static int 23010b59a9bSPeter Grehan vtpci_probe(device_t dev) 23110b59a9bSPeter Grehan { 23210b59a9bSPeter Grehan char desc[36]; 23310b59a9bSPeter Grehan const char *name; 23410b59a9bSPeter Grehan 23510b59a9bSPeter Grehan if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID) 23610b59a9bSPeter Grehan return (ENXIO); 23710b59a9bSPeter Grehan 23810b59a9bSPeter Grehan if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN || 23910b59a9bSPeter Grehan pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX) 24010b59a9bSPeter Grehan return (ENXIO); 24110b59a9bSPeter Grehan 24210b59a9bSPeter Grehan if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION) 24310b59a9bSPeter Grehan return (ENXIO); 24410b59a9bSPeter Grehan 24510b59a9bSPeter Grehan name = virtio_device_name(pci_get_subdevice(dev)); 24610b59a9bSPeter Grehan if (name == NULL) 24710b59a9bSPeter Grehan name = "Unknown"; 24810b59a9bSPeter Grehan 24910b59a9bSPeter Grehan snprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name); 25010b59a9bSPeter Grehan device_set_desc_copy(dev, desc); 25110b59a9bSPeter Grehan 25210b59a9bSPeter Grehan return (BUS_PROBE_DEFAULT); 25310b59a9bSPeter Grehan } 25410b59a9bSPeter Grehan 25510b59a9bSPeter Grehan static int 25610b59a9bSPeter Grehan vtpci_attach(device_t dev) 25710b59a9bSPeter Grehan { 25810b59a9bSPeter Grehan struct vtpci_softc *sc; 25910b59a9bSPeter Grehan device_t child; 26010b59a9bSPeter Grehan int rid; 26110b59a9bSPeter Grehan 26210b59a9bSPeter Grehan sc = device_get_softc(dev); 26310b59a9bSPeter Grehan sc->vtpci_dev = dev; 26410b59a9bSPeter Grehan 26510b59a9bSPeter Grehan pci_enable_busmaster(dev); 26610b59a9bSPeter Grehan 26710b59a9bSPeter Grehan rid = PCIR_BAR(0); 26810b59a9bSPeter Grehan sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 26910b59a9bSPeter Grehan RF_ACTIVE); 27010b59a9bSPeter Grehan if (sc->vtpci_res == NULL) { 27110b59a9bSPeter Grehan device_printf(dev, "cannot map I/O space\n"); 27210b59a9bSPeter Grehan return (ENXIO); 27310b59a9bSPeter Grehan } 27410b59a9bSPeter Grehan 275cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSI, NULL) != 0) 276310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_NO_MSI; 27710b59a9bSPeter Grehan 278cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSIX, NULL) == 0) { 27910b59a9bSPeter Grehan rid = PCIR_BAR(1); 28010b59a9bSPeter Grehan sc->vtpci_msix_res = bus_alloc_resource_any(dev, 28110b59a9bSPeter Grehan SYS_RES_MEMORY, &rid, RF_ACTIVE); 28210b59a9bSPeter Grehan } 28310b59a9bSPeter Grehan 28410b59a9bSPeter Grehan if (sc->vtpci_msix_res == NULL) 285310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_NO_MSIX; 28610b59a9bSPeter Grehan 28710b59a9bSPeter Grehan vtpci_reset(sc); 28810b59a9bSPeter Grehan 28910b59a9bSPeter Grehan /* Tell the host we've noticed this device. */ 29010b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 29110b59a9bSPeter Grehan 29210b59a9bSPeter Grehan if ((child = device_add_child(dev, NULL, -1)) == NULL) { 29310b59a9bSPeter Grehan device_printf(dev, "cannot create child device\n"); 29410b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 29510b59a9bSPeter Grehan vtpci_detach(dev); 29610b59a9bSPeter Grehan return (ENOMEM); 29710b59a9bSPeter Grehan } 29810b59a9bSPeter Grehan 29910b59a9bSPeter Grehan sc->vtpci_child_dev = child; 30010b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 30110b59a9bSPeter Grehan 30210b59a9bSPeter Grehan return (0); 30310b59a9bSPeter Grehan } 30410b59a9bSPeter Grehan 30510b59a9bSPeter Grehan static int 30610b59a9bSPeter Grehan vtpci_detach(device_t dev) 30710b59a9bSPeter Grehan { 30810b59a9bSPeter Grehan struct vtpci_softc *sc; 30910b59a9bSPeter Grehan device_t child; 31010b59a9bSPeter Grehan int error; 31110b59a9bSPeter Grehan 31210b59a9bSPeter Grehan sc = device_get_softc(dev); 31310b59a9bSPeter Grehan 31410b59a9bSPeter Grehan if ((child = sc->vtpci_child_dev) != NULL) { 31510b59a9bSPeter Grehan error = device_delete_child(dev, child); 31610b59a9bSPeter Grehan if (error) 31710b59a9bSPeter Grehan return (error); 31810b59a9bSPeter Grehan sc->vtpci_child_dev = NULL; 31910b59a9bSPeter Grehan } 32010b59a9bSPeter Grehan 32110b59a9bSPeter Grehan vtpci_reset(sc); 32210b59a9bSPeter Grehan 32310b59a9bSPeter Grehan if (sc->vtpci_msix_res != NULL) { 32410b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), 32510b59a9bSPeter Grehan sc->vtpci_msix_res); 32610b59a9bSPeter Grehan sc->vtpci_msix_res = NULL; 32710b59a9bSPeter Grehan } 32810b59a9bSPeter Grehan 32910b59a9bSPeter Grehan if (sc->vtpci_res != NULL) { 33010b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), 33110b59a9bSPeter Grehan sc->vtpci_res); 33210b59a9bSPeter Grehan sc->vtpci_res = NULL; 33310b59a9bSPeter Grehan } 33410b59a9bSPeter Grehan 33510b59a9bSPeter Grehan return (0); 33610b59a9bSPeter Grehan } 33710b59a9bSPeter Grehan 33810b59a9bSPeter Grehan static int 33910b59a9bSPeter Grehan vtpci_suspend(device_t dev) 34010b59a9bSPeter Grehan { 34110b59a9bSPeter Grehan 34210b59a9bSPeter Grehan return (bus_generic_suspend(dev)); 34310b59a9bSPeter Grehan } 34410b59a9bSPeter Grehan 34510b59a9bSPeter Grehan static int 34610b59a9bSPeter Grehan vtpci_resume(device_t dev) 34710b59a9bSPeter Grehan { 34810b59a9bSPeter Grehan 34910b59a9bSPeter Grehan return (bus_generic_resume(dev)); 35010b59a9bSPeter Grehan } 35110b59a9bSPeter Grehan 35210b59a9bSPeter Grehan static int 35310b59a9bSPeter Grehan vtpci_shutdown(device_t dev) 35410b59a9bSPeter Grehan { 35510b59a9bSPeter Grehan 35610b59a9bSPeter Grehan (void) bus_generic_shutdown(dev); 35710b59a9bSPeter Grehan /* Forcibly stop the host device. */ 35810b59a9bSPeter Grehan vtpci_stop(dev); 35910b59a9bSPeter Grehan 36010b59a9bSPeter Grehan return (0); 36110b59a9bSPeter Grehan } 36210b59a9bSPeter Grehan 36310b59a9bSPeter Grehan static void 36410b59a9bSPeter Grehan vtpci_driver_added(device_t dev, driver_t *driver) 36510b59a9bSPeter Grehan { 36610b59a9bSPeter Grehan struct vtpci_softc *sc; 36710b59a9bSPeter Grehan 36810b59a9bSPeter Grehan sc = device_get_softc(dev); 36910b59a9bSPeter Grehan 37010b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 37110b59a9bSPeter Grehan } 37210b59a9bSPeter Grehan 37310b59a9bSPeter Grehan static void 37410b59a9bSPeter Grehan vtpci_child_detached(device_t dev, device_t child) 37510b59a9bSPeter Grehan { 37610b59a9bSPeter Grehan struct vtpci_softc *sc; 37710b59a9bSPeter Grehan 37810b59a9bSPeter Grehan sc = device_get_softc(dev); 37910b59a9bSPeter Grehan 38010b59a9bSPeter Grehan vtpci_reset(sc); 38110b59a9bSPeter Grehan vtpci_release_child_resources(sc); 38210b59a9bSPeter Grehan } 38310b59a9bSPeter Grehan 38410b59a9bSPeter Grehan static int 38510b59a9bSPeter Grehan vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 38610b59a9bSPeter Grehan { 38710b59a9bSPeter Grehan struct vtpci_softc *sc; 38810b59a9bSPeter Grehan 38910b59a9bSPeter Grehan sc = device_get_softc(dev); 39010b59a9bSPeter Grehan 39110b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 39210b59a9bSPeter Grehan return (ENOENT); 39310b59a9bSPeter Grehan 39410b59a9bSPeter Grehan switch (index) { 39510b59a9bSPeter Grehan case VIRTIO_IVAR_DEVTYPE: 396310dacd0SPeter Grehan case VIRTIO_IVAR_SUBDEVICE: 397310dacd0SPeter Grehan *result = pci_get_subdevice(dev); 398310dacd0SPeter Grehan break; 399310dacd0SPeter Grehan case VIRTIO_IVAR_VENDOR: 400310dacd0SPeter Grehan *result = pci_get_vendor(dev); 401310dacd0SPeter Grehan break; 402310dacd0SPeter Grehan case VIRTIO_IVAR_DEVICE: 403310dacd0SPeter Grehan *result = pci_get_device(dev); 404310dacd0SPeter Grehan break; 405310dacd0SPeter Grehan case VIRTIO_IVAR_SUBVENDOR: 40610b59a9bSPeter Grehan *result = pci_get_subdevice(dev); 40710b59a9bSPeter Grehan break; 40810b59a9bSPeter Grehan default: 40910b59a9bSPeter Grehan return (ENOENT); 41010b59a9bSPeter Grehan } 41110b59a9bSPeter Grehan 41210b59a9bSPeter Grehan return (0); 41310b59a9bSPeter Grehan } 41410b59a9bSPeter Grehan 41510b59a9bSPeter Grehan static int 41610b59a9bSPeter Grehan vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 41710b59a9bSPeter Grehan { 41810b59a9bSPeter Grehan struct vtpci_softc *sc; 41910b59a9bSPeter Grehan 42010b59a9bSPeter Grehan sc = device_get_softc(dev); 42110b59a9bSPeter Grehan 42210b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 42310b59a9bSPeter Grehan return (ENOENT); 42410b59a9bSPeter Grehan 42510b59a9bSPeter Grehan switch (index) { 42610b59a9bSPeter Grehan case VIRTIO_IVAR_FEATURE_DESC: 42710b59a9bSPeter Grehan sc->vtpci_child_feat_desc = (void *) value; 42810b59a9bSPeter Grehan break; 42910b59a9bSPeter Grehan default: 43010b59a9bSPeter Grehan return (ENOENT); 43110b59a9bSPeter Grehan } 43210b59a9bSPeter Grehan 43310b59a9bSPeter Grehan return (0); 43410b59a9bSPeter Grehan } 43510b59a9bSPeter Grehan 43610b59a9bSPeter Grehan static uint64_t 43710b59a9bSPeter Grehan vtpci_negotiate_features(device_t dev, uint64_t child_features) 43810b59a9bSPeter Grehan { 43910b59a9bSPeter Grehan struct vtpci_softc *sc; 44010b59a9bSPeter Grehan uint64_t host_features, features; 44110b59a9bSPeter Grehan 44210b59a9bSPeter Grehan sc = device_get_softc(dev); 44310b59a9bSPeter Grehan 44410b59a9bSPeter Grehan host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES); 44510b59a9bSPeter Grehan vtpci_describe_features(sc, "host", host_features); 44610b59a9bSPeter Grehan 44710b59a9bSPeter Grehan /* 44810b59a9bSPeter Grehan * Limit negotiated features to what the driver, virtqueue, and 44910b59a9bSPeter Grehan * host all support. 45010b59a9bSPeter Grehan */ 45110b59a9bSPeter Grehan features = host_features & child_features; 45210b59a9bSPeter Grehan features = virtqueue_filter_features(features); 45310b59a9bSPeter Grehan sc->vtpci_features = features; 45410b59a9bSPeter Grehan 45510b59a9bSPeter Grehan vtpci_describe_features(sc, "negotiated", features); 45610b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features); 45710b59a9bSPeter Grehan 45810b59a9bSPeter Grehan return (features); 45910b59a9bSPeter Grehan } 46010b59a9bSPeter Grehan 46110b59a9bSPeter Grehan static int 46210b59a9bSPeter Grehan vtpci_with_feature(device_t dev, uint64_t feature) 46310b59a9bSPeter Grehan { 46410b59a9bSPeter Grehan struct vtpci_softc *sc; 46510b59a9bSPeter Grehan 46610b59a9bSPeter Grehan sc = device_get_softc(dev); 46710b59a9bSPeter Grehan 46810b59a9bSPeter Grehan return ((sc->vtpci_features & feature) != 0); 46910b59a9bSPeter Grehan } 47010b59a9bSPeter Grehan 47110b59a9bSPeter Grehan static int 47210b59a9bSPeter Grehan vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, 47310b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 47410b59a9bSPeter Grehan { 47510b59a9bSPeter Grehan struct vtpci_softc *sc; 476310dacd0SPeter Grehan struct virtqueue *vq; 47710b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 47810b59a9bSPeter Grehan struct vq_alloc_info *info; 479310dacd0SPeter Grehan int idx, error; 480310dacd0SPeter Grehan uint16_t size; 48110b59a9bSPeter Grehan 48210b59a9bSPeter Grehan sc = device_get_softc(dev); 48310b59a9bSPeter Grehan 484310dacd0SPeter Grehan if (sc->vtpci_nvqs != 0) 485310dacd0SPeter Grehan return (EALREADY); 486*62a69c41SBryan Venteicher if (nvqs <= 0) 48710b59a9bSPeter Grehan return (EINVAL); 48810b59a9bSPeter Grehan 489*62a69c41SBryan Venteicher sc->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue), 490*62a69c41SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 491*62a69c41SBryan Venteicher if (sc->vtpci_vqs == NULL) 492*62a69c41SBryan Venteicher return (ENOMEM); 493*62a69c41SBryan Venteicher 494310dacd0SPeter Grehan for (idx = 0; idx < nvqs; idx++) { 495*62a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 496310dacd0SPeter Grehan info = &vq_info[idx]; 497310dacd0SPeter Grehan 498310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 499310dacd0SPeter Grehan size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 500310dacd0SPeter Grehan 501310dacd0SPeter Grehan error = virtqueue_alloc(dev, idx, size, VIRTIO_PCI_VRING_ALIGN, 502310dacd0SPeter Grehan 0xFFFFFFFFUL, info, &vq); 50310b59a9bSPeter Grehan if (error) { 504310dacd0SPeter Grehan device_printf(dev, 505310dacd0SPeter Grehan "cannot allocate virtqueue %d: %d\n", idx, error); 506310dacd0SPeter Grehan break; 50710b59a9bSPeter Grehan } 50810b59a9bSPeter Grehan 50910b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 510310dacd0SPeter Grehan virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 51110b59a9bSPeter Grehan 512*62a69c41SBryan Venteicher vqx->vtv_vq = *info->vqai_vq = vq; 513*62a69c41SBryan Venteicher vqx->vtv_no_intr = info->vqai_intr == NULL; 514310dacd0SPeter Grehan 51510b59a9bSPeter Grehan sc->vtpci_nvqs++; 51610b59a9bSPeter Grehan } 51710b59a9bSPeter Grehan 518*62a69c41SBryan Venteicher if (error) 519*62a69c41SBryan Venteicher vtpci_free_virtqueues(sc); 520*62a69c41SBryan Venteicher 521310dacd0SPeter Grehan return (error); 52210b59a9bSPeter Grehan } 52310b59a9bSPeter Grehan 52410b59a9bSPeter Grehan static int 52510b59a9bSPeter Grehan vtpci_setup_intr(device_t dev, enum intr_type type) 52610b59a9bSPeter Grehan { 52710b59a9bSPeter Grehan struct vtpci_softc *sc; 528310dacd0SPeter Grehan int attempt, error; 52910b59a9bSPeter Grehan 53010b59a9bSPeter Grehan sc = device_get_softc(dev); 53110b59a9bSPeter Grehan 532310dacd0SPeter Grehan for (attempt = 0; attempt < 5; attempt++) { 533310dacd0SPeter Grehan /* 534310dacd0SPeter Grehan * Start with the most desirable interrupt configuration and 535310dacd0SPeter Grehan * fallback towards less desirable ones. 536310dacd0SPeter Grehan */ 537310dacd0SPeter Grehan switch (attempt) { 538310dacd0SPeter Grehan case 0: 539310dacd0SPeter Grehan error = vtpci_alloc_intr_msix_pervq(sc); 540310dacd0SPeter Grehan break; 541310dacd0SPeter Grehan case 1: 542310dacd0SPeter Grehan error = vtpci_alloc_intr_msix_shared(sc); 543310dacd0SPeter Grehan break; 544310dacd0SPeter Grehan case 2: 545310dacd0SPeter Grehan error = vtpci_alloc_intr_msi(sc); 546310dacd0SPeter Grehan break; 547310dacd0SPeter Grehan case 3: 548310dacd0SPeter Grehan error = vtpci_alloc_intr_legacy(sc); 549310dacd0SPeter Grehan break; 550310dacd0SPeter Grehan default: 551310dacd0SPeter Grehan device_printf(dev, 552310dacd0SPeter Grehan "exhausted all interrupt allocation attempts\n"); 553310dacd0SPeter Grehan return (ENXIO); 55410b59a9bSPeter Grehan } 55510b59a9bSPeter Grehan 556310dacd0SPeter Grehan if (error == 0 && vtpci_setup_interrupts(sc, type) == 0) 557310dacd0SPeter Grehan break; 55810b59a9bSPeter Grehan 559310dacd0SPeter Grehan vtpci_cleanup_setup_intr_attempt(sc); 56010b59a9bSPeter Grehan } 56110b59a9bSPeter Grehan 562310dacd0SPeter Grehan if (bootverbose) { 563310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 564310dacd0SPeter Grehan device_printf(dev, "using legacy interrupt\n"); 565310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_MSI) 566310dacd0SPeter Grehan device_printf(dev, "using MSI interrupt\n"); 567310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) 568310dacd0SPeter Grehan device_printf(dev, "using shared MSIX interrupts\n"); 569310dacd0SPeter Grehan else 570310dacd0SPeter Grehan device_printf(dev, "using per VQ MSIX interrupts\n"); 57110b59a9bSPeter Grehan } 57210b59a9bSPeter Grehan 57310b59a9bSPeter Grehan return (0); 57410b59a9bSPeter Grehan } 57510b59a9bSPeter Grehan 57610b59a9bSPeter Grehan static void 57710b59a9bSPeter Grehan vtpci_stop(device_t dev) 57810b59a9bSPeter Grehan { 57910b59a9bSPeter Grehan 58010b59a9bSPeter Grehan vtpci_reset(device_get_softc(dev)); 58110b59a9bSPeter Grehan } 58210b59a9bSPeter Grehan 58310b59a9bSPeter Grehan static int 58410b59a9bSPeter Grehan vtpci_reinit(device_t dev, uint64_t features) 58510b59a9bSPeter Grehan { 58610b59a9bSPeter Grehan struct vtpci_softc *sc; 587310dacd0SPeter Grehan int idx, error; 58810b59a9bSPeter Grehan 58910b59a9bSPeter Grehan sc = device_get_softc(dev); 59010b59a9bSPeter Grehan 59110b59a9bSPeter Grehan /* 592310dacd0SPeter Grehan * Redrive the device initialization. This is a bit of an abuse of 593310dacd0SPeter Grehan * the specification, but VirtualBox, QEMU/KVM, and BHyVe seem to 594310dacd0SPeter Grehan * play nice. 595310dacd0SPeter Grehan * 596310dacd0SPeter Grehan * We do not allow the host device to change from what was originally 597310dacd0SPeter Grehan * negotiated beyond what the guest driver changed. MSIX state should 598310dacd0SPeter Grehan * not change, number of virtqueues and their size remain the same, etc. 599310dacd0SPeter Grehan * This will need to be rethought when we want to support migration. 60010b59a9bSPeter Grehan */ 60110b59a9bSPeter Grehan 60210b59a9bSPeter Grehan if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 60310b59a9bSPeter Grehan vtpci_stop(dev); 60410b59a9bSPeter Grehan 60510b59a9bSPeter Grehan /* 60610b59a9bSPeter Grehan * Quickly drive the status through ACK and DRIVER. The device 60710b59a9bSPeter Grehan * does not become usable again until vtpci_reinit_complete(). 60810b59a9bSPeter Grehan */ 60910b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 61010b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 61110b59a9bSPeter Grehan 61210b59a9bSPeter Grehan vtpci_negotiate_features(dev, features); 61310b59a9bSPeter Grehan 614310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 615310dacd0SPeter Grehan error = vtpci_reinit_virtqueue(sc, idx); 61610b59a9bSPeter Grehan if (error) 61710b59a9bSPeter Grehan return (error); 61810b59a9bSPeter Grehan } 61910b59a9bSPeter Grehan 620310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_MSIX) { 621310dacd0SPeter Grehan error = vtpci_set_host_msix_vectors(sc); 62210b59a9bSPeter Grehan if (error) 62310b59a9bSPeter Grehan return (error); 62410b59a9bSPeter Grehan } 62510b59a9bSPeter Grehan 62610b59a9bSPeter Grehan return (0); 62710b59a9bSPeter Grehan } 62810b59a9bSPeter Grehan 62910b59a9bSPeter Grehan static void 63010b59a9bSPeter Grehan vtpci_reinit_complete(device_t dev) 63110b59a9bSPeter Grehan { 63210b59a9bSPeter Grehan 63310b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 63410b59a9bSPeter Grehan } 63510b59a9bSPeter Grehan 63610b59a9bSPeter Grehan static void 63710b59a9bSPeter Grehan vtpci_notify_virtqueue(device_t dev, uint16_t queue) 63810b59a9bSPeter Grehan { 63910b59a9bSPeter Grehan struct vtpci_softc *sc; 64010b59a9bSPeter Grehan 64110b59a9bSPeter Grehan sc = device_get_softc(dev); 64210b59a9bSPeter Grehan 64310b59a9bSPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue); 64410b59a9bSPeter Grehan } 64510b59a9bSPeter Grehan 64610b59a9bSPeter Grehan static uint8_t 64710b59a9bSPeter Grehan vtpci_get_status(device_t dev) 64810b59a9bSPeter Grehan { 64910b59a9bSPeter Grehan struct vtpci_softc *sc; 65010b59a9bSPeter Grehan 65110b59a9bSPeter Grehan sc = device_get_softc(dev); 65210b59a9bSPeter Grehan 65310b59a9bSPeter Grehan return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS)); 65410b59a9bSPeter Grehan } 65510b59a9bSPeter Grehan 65610b59a9bSPeter Grehan static void 65710b59a9bSPeter Grehan vtpci_set_status(device_t dev, uint8_t status) 65810b59a9bSPeter Grehan { 65910b59a9bSPeter Grehan struct vtpci_softc *sc; 66010b59a9bSPeter Grehan 66110b59a9bSPeter Grehan sc = device_get_softc(dev); 66210b59a9bSPeter Grehan 66310b59a9bSPeter Grehan if (status != VIRTIO_CONFIG_STATUS_RESET) 66410b59a9bSPeter Grehan status |= vtpci_get_status(dev); 66510b59a9bSPeter Grehan 66610b59a9bSPeter Grehan vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status); 66710b59a9bSPeter Grehan } 66810b59a9bSPeter Grehan 66910b59a9bSPeter Grehan static void 67010b59a9bSPeter Grehan vtpci_read_dev_config(device_t dev, bus_size_t offset, 67110b59a9bSPeter Grehan void *dst, int length) 67210b59a9bSPeter Grehan { 67310b59a9bSPeter Grehan struct vtpci_softc *sc; 67410b59a9bSPeter Grehan bus_size_t off; 67510b59a9bSPeter Grehan uint8_t *d; 67610b59a9bSPeter Grehan int size; 67710b59a9bSPeter Grehan 67810b59a9bSPeter Grehan sc = device_get_softc(dev); 67910b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 68010b59a9bSPeter Grehan 68110b59a9bSPeter Grehan for (d = dst; length > 0; d += size, off += size, length -= size) { 68210b59a9bSPeter Grehan if (length >= 4) { 68310b59a9bSPeter Grehan size = 4; 68410b59a9bSPeter Grehan *(uint32_t *)d = vtpci_read_config_4(sc, off); 68510b59a9bSPeter Grehan } else if (length >= 2) { 68610b59a9bSPeter Grehan size = 2; 68710b59a9bSPeter Grehan *(uint16_t *)d = vtpci_read_config_2(sc, off); 68810b59a9bSPeter Grehan } else { 68910b59a9bSPeter Grehan size = 1; 69010b59a9bSPeter Grehan *d = vtpci_read_config_1(sc, off); 69110b59a9bSPeter Grehan } 69210b59a9bSPeter Grehan } 69310b59a9bSPeter Grehan } 69410b59a9bSPeter Grehan 69510b59a9bSPeter Grehan static void 69610b59a9bSPeter Grehan vtpci_write_dev_config(device_t dev, bus_size_t offset, 69710b59a9bSPeter Grehan void *src, int length) 69810b59a9bSPeter Grehan { 69910b59a9bSPeter Grehan struct vtpci_softc *sc; 70010b59a9bSPeter Grehan bus_size_t off; 70110b59a9bSPeter Grehan uint8_t *s; 70210b59a9bSPeter Grehan int size; 70310b59a9bSPeter Grehan 70410b59a9bSPeter Grehan sc = device_get_softc(dev); 70510b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 70610b59a9bSPeter Grehan 70710b59a9bSPeter Grehan for (s = src; length > 0; s += size, off += size, length -= size) { 70810b59a9bSPeter Grehan if (length >= 4) { 70910b59a9bSPeter Grehan size = 4; 71010b59a9bSPeter Grehan vtpci_write_config_4(sc, off, *(uint32_t *)s); 71110b59a9bSPeter Grehan } else if (length >= 2) { 71210b59a9bSPeter Grehan size = 2; 71310b59a9bSPeter Grehan vtpci_write_config_2(sc, off, *(uint16_t *)s); 71410b59a9bSPeter Grehan } else { 71510b59a9bSPeter Grehan size = 1; 71610b59a9bSPeter Grehan vtpci_write_config_1(sc, off, *s); 71710b59a9bSPeter Grehan } 71810b59a9bSPeter Grehan } 71910b59a9bSPeter Grehan } 72010b59a9bSPeter Grehan 72110b59a9bSPeter Grehan static void 72210b59a9bSPeter Grehan vtpci_describe_features(struct vtpci_softc *sc, const char *msg, 72310b59a9bSPeter Grehan uint64_t features) 72410b59a9bSPeter Grehan { 72510b59a9bSPeter Grehan device_t dev, child; 72610b59a9bSPeter Grehan 72710b59a9bSPeter Grehan dev = sc->vtpci_dev; 72810b59a9bSPeter Grehan child = sc->vtpci_child_dev; 72910b59a9bSPeter Grehan 73010b59a9bSPeter Grehan if (device_is_attached(child) && bootverbose == 0) 73110b59a9bSPeter Grehan return; 73210b59a9bSPeter Grehan 73310b59a9bSPeter Grehan virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc); 73410b59a9bSPeter Grehan } 73510b59a9bSPeter Grehan 73610b59a9bSPeter Grehan static void 73710b59a9bSPeter Grehan vtpci_probe_and_attach_child(struct vtpci_softc *sc) 73810b59a9bSPeter Grehan { 73910b59a9bSPeter Grehan device_t dev, child; 74010b59a9bSPeter Grehan 74110b59a9bSPeter Grehan dev = sc->vtpci_dev; 74210b59a9bSPeter Grehan child = sc->vtpci_child_dev; 74310b59a9bSPeter Grehan 74410b59a9bSPeter Grehan if (child == NULL) 74510b59a9bSPeter Grehan return; 74610b59a9bSPeter Grehan 74710b59a9bSPeter Grehan if (device_get_state(child) != DS_NOTPRESENT) 74810b59a9bSPeter Grehan return; 74910b59a9bSPeter Grehan 75010b59a9bSPeter Grehan if (device_probe(child) != 0) 75110b59a9bSPeter Grehan return; 75210b59a9bSPeter Grehan 75310b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 75410b59a9bSPeter Grehan if (device_attach(child) != 0) { 75510b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 75610b59a9bSPeter Grehan vtpci_reset(sc); 75710b59a9bSPeter Grehan vtpci_release_child_resources(sc); 75810b59a9bSPeter Grehan /* Reset status for future attempt. */ 75910b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 76010b59a9bSPeter Grehan } else 76110b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 76210b59a9bSPeter Grehan } 76310b59a9bSPeter Grehan 76410b59a9bSPeter Grehan static int 765310dacd0SPeter Grehan vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors) 76610b59a9bSPeter Grehan { 767310dacd0SPeter Grehan device_t dev; 768310dacd0SPeter Grehan int nmsix, cnt, required; 76910b59a9bSPeter Grehan 770310dacd0SPeter Grehan dev = sc->vtpci_dev; 77110b59a9bSPeter Grehan 772310dacd0SPeter Grehan /* Allocate an additional vector for the config changes. */ 773310dacd0SPeter Grehan required = nvectors + 1; 77410b59a9bSPeter Grehan 775310dacd0SPeter Grehan nmsix = pci_msix_count(dev); 776310dacd0SPeter Grehan if (nmsix < required) 777310dacd0SPeter Grehan return (1); 778310dacd0SPeter Grehan 779310dacd0SPeter Grehan cnt = required; 780310dacd0SPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 781*62a69c41SBryan Venteicher sc->vtpci_nmsix_resources = required; 782310dacd0SPeter Grehan return (0); 78310b59a9bSPeter Grehan } 78410b59a9bSPeter Grehan 785310dacd0SPeter Grehan pci_release_msi(dev); 78610b59a9bSPeter Grehan 787310dacd0SPeter Grehan return (1); 78810b59a9bSPeter Grehan } 78910b59a9bSPeter Grehan 79010b59a9bSPeter Grehan static int 791310dacd0SPeter Grehan vtpci_alloc_msi(struct vtpci_softc *sc) 792310dacd0SPeter Grehan { 793310dacd0SPeter Grehan device_t dev; 794310dacd0SPeter Grehan int nmsi, cnt, required; 795310dacd0SPeter Grehan 796310dacd0SPeter Grehan dev = sc->vtpci_dev; 797310dacd0SPeter Grehan required = 1; 798310dacd0SPeter Grehan 799310dacd0SPeter Grehan nmsi = pci_msi_count(dev); 800310dacd0SPeter Grehan if (nmsi < required) 801310dacd0SPeter Grehan return (1); 802310dacd0SPeter Grehan 803310dacd0SPeter Grehan cnt = required; 804*62a69c41SBryan Venteicher if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) 805310dacd0SPeter Grehan return (0); 806310dacd0SPeter Grehan 807310dacd0SPeter Grehan pci_release_msi(dev); 808310dacd0SPeter Grehan 809310dacd0SPeter Grehan return (1); 810310dacd0SPeter Grehan } 811310dacd0SPeter Grehan 812310dacd0SPeter Grehan static int 813310dacd0SPeter Grehan vtpci_alloc_intr_msix_pervq(struct vtpci_softc *sc) 814310dacd0SPeter Grehan { 815310dacd0SPeter Grehan int i, nvectors, error; 816310dacd0SPeter Grehan 817310dacd0SPeter Grehan if (vtpci_disable_msix != 0 || 818310dacd0SPeter Grehan sc->vtpci_flags & VTPCI_FLAG_NO_MSIX) 819310dacd0SPeter Grehan return (ENOTSUP); 820310dacd0SPeter Grehan 821310dacd0SPeter Grehan for (nvectors = 0, i = 0; i < sc->vtpci_nvqs; i++) { 822*62a69c41SBryan Venteicher if (sc->vtpci_vqs[i].vtv_no_intr == 0) 823310dacd0SPeter Grehan nvectors++; 824310dacd0SPeter Grehan } 825310dacd0SPeter Grehan 826310dacd0SPeter Grehan error = vtpci_alloc_msix(sc, nvectors); 827310dacd0SPeter Grehan if (error) 828310dacd0SPeter Grehan return (error); 829310dacd0SPeter Grehan 830310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSIX; 831310dacd0SPeter Grehan 832310dacd0SPeter Grehan return (0); 833310dacd0SPeter Grehan } 834310dacd0SPeter Grehan 835310dacd0SPeter Grehan static int 836310dacd0SPeter Grehan vtpci_alloc_intr_msix_shared(struct vtpci_softc *sc) 837310dacd0SPeter Grehan { 838310dacd0SPeter Grehan int error; 839310dacd0SPeter Grehan 840310dacd0SPeter Grehan if (vtpci_disable_msix != 0 || 841310dacd0SPeter Grehan sc->vtpci_flags & VTPCI_FLAG_NO_MSIX) 842310dacd0SPeter Grehan return (ENOTSUP); 843310dacd0SPeter Grehan 844310dacd0SPeter Grehan error = vtpci_alloc_msix(sc, 1); 845310dacd0SPeter Grehan if (error) 846310dacd0SPeter Grehan return (error); 847310dacd0SPeter Grehan 848310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSIX | VTPCI_FLAG_SHARED_MSIX; 849310dacd0SPeter Grehan 850310dacd0SPeter Grehan return (0); 851310dacd0SPeter Grehan } 852310dacd0SPeter Grehan 853310dacd0SPeter Grehan static int 854310dacd0SPeter Grehan vtpci_alloc_intr_msi(struct vtpci_softc *sc) 855310dacd0SPeter Grehan { 856310dacd0SPeter Grehan int error; 857310dacd0SPeter Grehan 858310dacd0SPeter Grehan /* Only BHyVe supports MSI. */ 859310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_NO_MSI) 860310dacd0SPeter Grehan return (ENOTSUP); 861310dacd0SPeter Grehan 862310dacd0SPeter Grehan error = vtpci_alloc_msi(sc); 863310dacd0SPeter Grehan if (error) 864310dacd0SPeter Grehan return (error); 865310dacd0SPeter Grehan 866310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSI; 867310dacd0SPeter Grehan 868310dacd0SPeter Grehan return (0); 869310dacd0SPeter Grehan } 870310dacd0SPeter Grehan 871310dacd0SPeter Grehan static int 872310dacd0SPeter Grehan vtpci_alloc_intr_legacy(struct vtpci_softc *sc) 873310dacd0SPeter Grehan { 874310dacd0SPeter Grehan 875310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_LEGACY; 876*62a69c41SBryan Venteicher 877*62a69c41SBryan Venteicher return (0); 878*62a69c41SBryan Venteicher } 879*62a69c41SBryan Venteicher 880*62a69c41SBryan Venteicher static int 881*62a69c41SBryan Venteicher vtpci_alloc_interrupt(struct vtpci_softc *sc, int rid, int flags, 882*62a69c41SBryan Venteicher struct vtpci_interrupt *intr) 883*62a69c41SBryan Venteicher { 884*62a69c41SBryan Venteicher struct resource *irq; 885*62a69c41SBryan Venteicher 886*62a69c41SBryan Venteicher irq = bus_alloc_resource_any(sc->vtpci_dev, SYS_RES_IRQ, &rid, flags); 887*62a69c41SBryan Venteicher if (irq == NULL) 888*62a69c41SBryan Venteicher return (ENXIO); 889*62a69c41SBryan Venteicher 890*62a69c41SBryan Venteicher intr->vti_irq = irq; 891*62a69c41SBryan Venteicher intr->vti_rid = rid; 892310dacd0SPeter Grehan 893310dacd0SPeter Grehan return (0); 894310dacd0SPeter Grehan } 895310dacd0SPeter Grehan 896310dacd0SPeter Grehan static int 897310dacd0SPeter Grehan vtpci_alloc_intr_resources(struct vtpci_softc *sc) 89810b59a9bSPeter Grehan { 899*62a69c41SBryan Venteicher struct vtpci_interrupt *intr; 900*62a69c41SBryan Venteicher int i, rid, flags, nvq_intrs, error; 90110b59a9bSPeter Grehan 90210b59a9bSPeter Grehan rid = 0; 903310dacd0SPeter Grehan flags = RF_ACTIVE; 90410b59a9bSPeter Grehan 905*62a69c41SBryan Venteicher if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 906*62a69c41SBryan Venteicher flags |= RF_SHAREABLE; 907*62a69c41SBryan Venteicher else 908*62a69c41SBryan Venteicher rid = 1; 90910b59a9bSPeter Grehan 91010b59a9bSPeter Grehan /* 911*62a69c41SBryan Venteicher * For legacy and MSI interrupts, this single resource handles all 912*62a69c41SBryan Venteicher * interrupts. For MSIX, this resource is used for the configuration 913*62a69c41SBryan Venteicher * changed interrupt. 91410b59a9bSPeter Grehan */ 915*62a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 916*62a69c41SBryan Venteicher error = vtpci_alloc_interrupt(sc, rid, flags, intr); 917*62a69c41SBryan Venteicher if (error || sc->vtpci_flags & (VTPCI_FLAG_LEGACY | VTPCI_FLAG_MSI)) 918*62a69c41SBryan Venteicher return (error); 91910b59a9bSPeter Grehan 920*62a69c41SBryan Venteicher /* Subtract one for the configuration changed interrupt. */ 921*62a69c41SBryan Venteicher nvq_intrs = sc->vtpci_nmsix_resources - 1; 922*62a69c41SBryan Venteicher 923*62a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts = malloc(nvq_intrs * 924*62a69c41SBryan Venteicher sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO); 925*62a69c41SBryan Venteicher if (sc->vtpci_msix_vq_interrupts == NULL) 926*62a69c41SBryan Venteicher return (ENOMEM); 927*62a69c41SBryan Venteicher 928*62a69c41SBryan Venteicher for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) { 929*62a69c41SBryan Venteicher error = vtpci_alloc_interrupt(sc, rid, flags, intr); 930*62a69c41SBryan Venteicher if (error) 931*62a69c41SBryan Venteicher return (error); 93210b59a9bSPeter Grehan } 93310b59a9bSPeter Grehan 93410b59a9bSPeter Grehan return (0); 93510b59a9bSPeter Grehan } 93610b59a9bSPeter Grehan 93710b59a9bSPeter Grehan static int 938310dacd0SPeter Grehan vtpci_setup_legacy_interrupt(struct vtpci_softc *sc, enum intr_type type) 93910b59a9bSPeter Grehan { 940*62a69c41SBryan Venteicher struct vtpci_interrupt *intr; 941310dacd0SPeter Grehan int error; 94210b59a9bSPeter Grehan 943*62a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 944*62a69c41SBryan Venteicher error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, NULL, 945*62a69c41SBryan Venteicher vtpci_legacy_intr, sc, &intr->vti_handler); 94610b59a9bSPeter Grehan 947310dacd0SPeter Grehan return (error); 94810b59a9bSPeter Grehan } 94910b59a9bSPeter Grehan 95010b59a9bSPeter Grehan static int 951*62a69c41SBryan Venteicher vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *sc, enum intr_type type) 952*62a69c41SBryan Venteicher { 953*62a69c41SBryan Venteicher struct vtpci_virtqueue *vqx; 954*62a69c41SBryan Venteicher struct vtpci_interrupt *intr; 955*62a69c41SBryan Venteicher int i, error; 956*62a69c41SBryan Venteicher 957*62a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 958*62a69c41SBryan Venteicher 959*62a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++) { 960*62a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[i]; 961*62a69c41SBryan Venteicher 962*62a69c41SBryan Venteicher if (vqx->vtv_no_intr) 963*62a69c41SBryan Venteicher continue; 964*62a69c41SBryan Venteicher 965*62a69c41SBryan Venteicher error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, 966*62a69c41SBryan Venteicher vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq, 967*62a69c41SBryan Venteicher &intr->vti_handler); 968*62a69c41SBryan Venteicher if (error) 969*62a69c41SBryan Venteicher return (error); 970*62a69c41SBryan Venteicher 971*62a69c41SBryan Venteicher intr++; 972*62a69c41SBryan Venteicher } 973*62a69c41SBryan Venteicher 974*62a69c41SBryan Venteicher return (0); 975*62a69c41SBryan Venteicher } 976*62a69c41SBryan Venteicher 977*62a69c41SBryan Venteicher static int 978310dacd0SPeter Grehan vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type) 97910b59a9bSPeter Grehan { 98010b59a9bSPeter Grehan device_t dev; 981*62a69c41SBryan Venteicher struct vtpci_interrupt *intr; 982*62a69c41SBryan Venteicher int error; 98310b59a9bSPeter Grehan 98410b59a9bSPeter Grehan dev = sc->vtpci_dev; 985*62a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 98610b59a9bSPeter Grehan 987*62a69c41SBryan Venteicher error = bus_setup_intr(dev, intr->vti_irq, type, NULL, 988*62a69c41SBryan Venteicher vtpci_config_intr, sc, &intr->vti_handler); 989310dacd0SPeter Grehan if (error) 990310dacd0SPeter Grehan return (error); 99110b59a9bSPeter Grehan 992310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) { 993*62a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 994*62a69c41SBryan Venteicher error = bus_setup_intr(dev, intr->vti_irq, type, 9956632efe4SBryan Venteicher vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, sc, 996*62a69c41SBryan Venteicher &intr->vti_handler); 997*62a69c41SBryan Venteicher } else 998*62a69c41SBryan Venteicher error = vtpci_setup_pervq_msix_interrupts(sc, type); 999310dacd0SPeter Grehan 1000*62a69c41SBryan Venteicher return (error ? error : vtpci_set_host_msix_vectors(sc)); 100110b59a9bSPeter Grehan } 100210b59a9bSPeter Grehan 100310b59a9bSPeter Grehan static int 1004310dacd0SPeter Grehan vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type) 1005310dacd0SPeter Grehan { 1006310dacd0SPeter Grehan int error; 1007310dacd0SPeter Grehan 1008310dacd0SPeter Grehan type |= INTR_MPSAFE; 1009310dacd0SPeter Grehan KASSERT(sc->vtpci_flags & VTPCI_FLAG_ITYPE_MASK, 1010*62a69c41SBryan Venteicher ("%s: no interrupt type selected %#x", __func__, sc->vtpci_flags)); 1011310dacd0SPeter Grehan 1012310dacd0SPeter Grehan error = vtpci_alloc_intr_resources(sc); 1013310dacd0SPeter Grehan if (error) 1014310dacd0SPeter Grehan return (error); 1015310dacd0SPeter Grehan 1016310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 1017310dacd0SPeter Grehan error = vtpci_setup_legacy_interrupt(sc, type); 1018310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_MSI) 1019310dacd0SPeter Grehan error = vtpci_setup_msi_interrupt(sc, type); 1020310dacd0SPeter Grehan else 1021310dacd0SPeter Grehan error = vtpci_setup_msix_interrupts(sc, type); 1022310dacd0SPeter Grehan 1023310dacd0SPeter Grehan return (error); 1024310dacd0SPeter Grehan } 1025310dacd0SPeter Grehan 1026310dacd0SPeter Grehan static int 1027*62a69c41SBryan Venteicher vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, 1028*62a69c41SBryan Venteicher struct vtpci_interrupt *intr) 102910b59a9bSPeter Grehan { 103010b59a9bSPeter Grehan device_t dev; 1031*62a69c41SBryan Venteicher uint16_t vector; 103210b59a9bSPeter Grehan 103310b59a9bSPeter Grehan dev = sc->vtpci_dev; 103410b59a9bSPeter Grehan 1035*62a69c41SBryan Venteicher if (intr != NULL) { 1036310dacd0SPeter Grehan /* Map from guest rid to host vector. */ 1037*62a69c41SBryan Venteicher vector = intr->vti_rid - 1; 103810b59a9bSPeter Grehan } else 103910b59a9bSPeter Grehan vector = VIRTIO_MSI_NO_VECTOR; 104010b59a9bSPeter Grehan 104110b59a9bSPeter Grehan vtpci_write_config_2(sc, offset, vector); 104210b59a9bSPeter Grehan 1043310dacd0SPeter Grehan /* Read vector to determine if the host had sufficient resources. */ 1044*62a69c41SBryan Venteicher if (vtpci_read_config_2(sc, offset) != vector) { 1045310dacd0SPeter Grehan device_printf(dev, 1046310dacd0SPeter Grehan "insufficient host resources for MSIX interrupts\n"); 104710b59a9bSPeter Grehan return (ENODEV); 104810b59a9bSPeter Grehan } 104910b59a9bSPeter Grehan 105010b59a9bSPeter Grehan return (0); 105110b59a9bSPeter Grehan } 105210b59a9bSPeter Grehan 1053310dacd0SPeter Grehan static int 1054310dacd0SPeter Grehan vtpci_set_host_msix_vectors(struct vtpci_softc *sc) 1055310dacd0SPeter Grehan { 1056*62a69c41SBryan Venteicher struct vtpci_interrupt *intr, *tintr; 1057*62a69c41SBryan Venteicher int idx, offset, error; 1058310dacd0SPeter Grehan 1059*62a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 1060*62a69c41SBryan Venteicher offset = VIRTIO_MSI_CONFIG_VECTOR; 1061*62a69c41SBryan Venteicher 1062*62a69c41SBryan Venteicher error = vtpci_register_msix_vector(sc, offset, intr); 1063310dacd0SPeter Grehan if (error) 1064310dacd0SPeter Grehan return (error); 1065310dacd0SPeter Grehan 1066*62a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 1067*62a69c41SBryan Venteicher offset = VIRTIO_MSI_QUEUE_VECTOR; 1068*62a69c41SBryan Venteicher 1069310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 1070310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 1071*62a69c41SBryan Venteicher 1072*62a69c41SBryan Venteicher if (sc->vtpci_vqs[idx].vtv_no_intr) 1073*62a69c41SBryan Venteicher tintr = NULL; 1074*62a69c41SBryan Venteicher else 1075*62a69c41SBryan Venteicher tintr = intr; 1076*62a69c41SBryan Venteicher 1077*62a69c41SBryan Venteicher error = vtpci_register_msix_vector(sc, offset, tintr); 1078310dacd0SPeter Grehan if (error) 1079*62a69c41SBryan Venteicher break; 1080*62a69c41SBryan Venteicher 1081*62a69c41SBryan Venteicher /* 1082*62a69c41SBryan Venteicher * For shared MSIX, all the virtqueues share the first 1083*62a69c41SBryan Venteicher * interrupt. 1084*62a69c41SBryan Venteicher */ 1085*62a69c41SBryan Venteicher if ((sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0) 1086*62a69c41SBryan Venteicher intr++; 1087310dacd0SPeter Grehan } 1088310dacd0SPeter Grehan 1089*62a69c41SBryan Venteicher return (error); 1090310dacd0SPeter Grehan } 1091310dacd0SPeter Grehan 1092310dacd0SPeter Grehan static int 1093310dacd0SPeter Grehan vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx) 1094310dacd0SPeter Grehan { 1095310dacd0SPeter Grehan struct vtpci_virtqueue *vqx; 1096310dacd0SPeter Grehan struct virtqueue *vq; 1097310dacd0SPeter Grehan int error; 1098310dacd0SPeter Grehan uint16_t size; 1099310dacd0SPeter Grehan 1100*62a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 1101*62a69c41SBryan Venteicher vq = vqx->vtv_vq; 1102310dacd0SPeter Grehan 1103*62a69c41SBryan Venteicher KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx)); 1104310dacd0SPeter Grehan 1105310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 1106310dacd0SPeter Grehan size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 1107310dacd0SPeter Grehan 1108310dacd0SPeter Grehan error = virtqueue_reinit(vq, size); 1109310dacd0SPeter Grehan if (error) 1110310dacd0SPeter Grehan return (error); 1111310dacd0SPeter Grehan 1112310dacd0SPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 1113310dacd0SPeter Grehan virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 1114310dacd0SPeter Grehan 1115310dacd0SPeter Grehan return (0); 1116310dacd0SPeter Grehan } 1117310dacd0SPeter Grehan 111810b59a9bSPeter Grehan static void 1119*62a69c41SBryan Venteicher vtpci_free_interrupt(struct vtpci_softc *sc, struct vtpci_interrupt *intr) 112010b59a9bSPeter Grehan { 112110b59a9bSPeter Grehan device_t dev; 112210b59a9bSPeter Grehan 112310b59a9bSPeter Grehan dev = sc->vtpci_dev; 112410b59a9bSPeter Grehan 1125*62a69c41SBryan Venteicher if (intr->vti_handler != NULL) { 1126*62a69c41SBryan Venteicher bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler); 1127*62a69c41SBryan Venteicher intr->vti_handler = NULL; 112810b59a9bSPeter Grehan } 112910b59a9bSPeter Grehan 1130*62a69c41SBryan Venteicher if (intr->vti_irq != NULL) { 1131*62a69c41SBryan Venteicher bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid, 1132*62a69c41SBryan Venteicher intr->vti_irq); 1133*62a69c41SBryan Venteicher intr->vti_irq = NULL; 1134*62a69c41SBryan Venteicher intr->vti_rid = -1; 1135*62a69c41SBryan Venteicher } 113610b59a9bSPeter Grehan } 113710b59a9bSPeter Grehan 1138*62a69c41SBryan Venteicher static void 1139*62a69c41SBryan Venteicher vtpci_free_interrupts(struct vtpci_softc *sc) 1140*62a69c41SBryan Venteicher { 1141*62a69c41SBryan Venteicher struct vtpci_interrupt *intr; 1142*62a69c41SBryan Venteicher int i, nvq_intrs; 1143*62a69c41SBryan Venteicher 1144*62a69c41SBryan Venteicher vtpci_free_interrupt(sc, &sc->vtpci_device_interrupt); 1145*62a69c41SBryan Venteicher 1146*62a69c41SBryan Venteicher if (sc->vtpci_nmsix_resources != 0) { 1147*62a69c41SBryan Venteicher nvq_intrs = sc->vtpci_nmsix_resources - 1; 1148*62a69c41SBryan Venteicher sc->vtpci_nmsix_resources = 0; 1149*62a69c41SBryan Venteicher 1150*62a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 1151*62a69c41SBryan Venteicher if (intr != NULL) { 1152*62a69c41SBryan Venteicher for (i = 0; i < nvq_intrs; i++, intr++) 1153*62a69c41SBryan Venteicher vtpci_free_interrupt(sc, intr); 1154*62a69c41SBryan Venteicher 1155*62a69c41SBryan Venteicher free(sc->vtpci_msix_vq_interrupts, M_DEVBUF); 1156*62a69c41SBryan Venteicher sc->vtpci_msix_vq_interrupts = NULL; 1157*62a69c41SBryan Venteicher } 115810b59a9bSPeter Grehan } 1159310dacd0SPeter Grehan 1160310dacd0SPeter Grehan if (sc->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX)) 1161*62a69c41SBryan Venteicher pci_release_msi(sc->vtpci_dev); 1162310dacd0SPeter Grehan 1163310dacd0SPeter Grehan sc->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK; 116410b59a9bSPeter Grehan } 116510b59a9bSPeter Grehan 116610b59a9bSPeter Grehan static void 116710b59a9bSPeter Grehan vtpci_free_virtqueues(struct vtpci_softc *sc) 116810b59a9bSPeter Grehan { 116910b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 1170*62a69c41SBryan Venteicher int idx; 117110b59a9bSPeter Grehan 1172*62a69c41SBryan Venteicher for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 1173*62a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 117410b59a9bSPeter Grehan 1175*62a69c41SBryan Venteicher vtpci_select_virtqueue(sc, idx); 1176*62a69c41SBryan Venteicher vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 0); 1177*62a69c41SBryan Venteicher 1178*62a69c41SBryan Venteicher virtqueue_free(vqx->vtv_vq); 1179*62a69c41SBryan Venteicher vqx->vtv_vq = NULL; 118010b59a9bSPeter Grehan } 1181310dacd0SPeter Grehan 1182*62a69c41SBryan Venteicher free(sc->vtpci_vqs, M_DEVBUF); 1183*62a69c41SBryan Venteicher sc->vtpci_vqs = NULL; 1184310dacd0SPeter Grehan sc->vtpci_nvqs = 0; 118510b59a9bSPeter Grehan } 1186310dacd0SPeter Grehan 1187310dacd0SPeter Grehan static void 1188*62a69c41SBryan Venteicher vtpci_release_child_resources(struct vtpci_softc *sc) 1189*62a69c41SBryan Venteicher { 1190*62a69c41SBryan Venteicher 1191*62a69c41SBryan Venteicher vtpci_free_interrupts(sc); 1192*62a69c41SBryan Venteicher vtpci_free_virtqueues(sc); 1193*62a69c41SBryan Venteicher } 1194*62a69c41SBryan Venteicher 1195*62a69c41SBryan Venteicher static void 1196310dacd0SPeter Grehan vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc) 1197310dacd0SPeter Grehan { 1198310dacd0SPeter Grehan int idx; 1199310dacd0SPeter Grehan 1200310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_MSIX) { 1201310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_MSI_CONFIG_VECTOR, 1202310dacd0SPeter Grehan VIRTIO_MSI_NO_VECTOR); 1203310dacd0SPeter Grehan 1204310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 1205310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 1206310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_MSI_QUEUE_VECTOR, 1207310dacd0SPeter Grehan VIRTIO_MSI_NO_VECTOR); 1208310dacd0SPeter Grehan } 1209310dacd0SPeter Grehan } 1210310dacd0SPeter Grehan 1211310dacd0SPeter Grehan vtpci_free_interrupts(sc); 121210b59a9bSPeter Grehan } 121310b59a9bSPeter Grehan 121410b59a9bSPeter Grehan static void 121510b59a9bSPeter Grehan vtpci_reset(struct vtpci_softc *sc) 121610b59a9bSPeter Grehan { 121710b59a9bSPeter Grehan 121810b59a9bSPeter Grehan /* 121910b59a9bSPeter Grehan * Setting the status to RESET sets the host device to 122010b59a9bSPeter Grehan * the original, uninitialized state. 122110b59a9bSPeter Grehan */ 122210b59a9bSPeter Grehan vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET); 122310b59a9bSPeter Grehan } 122410b59a9bSPeter Grehan 1225310dacd0SPeter Grehan static void 1226310dacd0SPeter Grehan vtpci_select_virtqueue(struct vtpci_softc *sc, int idx) 1227310dacd0SPeter Grehan { 1228310dacd0SPeter Grehan 1229310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, idx); 1230310dacd0SPeter Grehan } 1231310dacd0SPeter Grehan 12326632efe4SBryan Venteicher static void 123310b59a9bSPeter Grehan vtpci_legacy_intr(void *xsc) 123410b59a9bSPeter Grehan { 123510b59a9bSPeter Grehan struct vtpci_softc *sc; 123610b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 123710b59a9bSPeter Grehan int i; 123810b59a9bSPeter Grehan uint8_t isr; 123910b59a9bSPeter Grehan 124010b59a9bSPeter Grehan sc = xsc; 1241*62a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 124210b59a9bSPeter Grehan 124310b59a9bSPeter Grehan /* Reading the ISR also clears it. */ 124410b59a9bSPeter Grehan isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR); 124510b59a9bSPeter Grehan 124610b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_CONFIG) 124710b59a9bSPeter Grehan vtpci_config_intr(sc); 124810b59a9bSPeter Grehan 12496632efe4SBryan Venteicher if (isr & VIRTIO_PCI_ISR_INTR) { 1250*62a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 1251*62a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 1252*62a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 1253*62a69c41SBryan Venteicher } 12546632efe4SBryan Venteicher } 125510b59a9bSPeter Grehan } 125610b59a9bSPeter Grehan 125710b59a9bSPeter Grehan static int 12586632efe4SBryan Venteicher vtpci_vq_shared_intr_filter(void *xsc) 125910b59a9bSPeter Grehan { 126010b59a9bSPeter Grehan struct vtpci_softc *sc; 126110b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 126210b59a9bSPeter Grehan int i, rc; 126310b59a9bSPeter Grehan 126410b59a9bSPeter Grehan rc = 0; 126510b59a9bSPeter Grehan sc = xsc; 1266*62a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 126710b59a9bSPeter Grehan 1268*62a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 1269*62a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 1270*62a69c41SBryan Venteicher rc |= virtqueue_intr_filter(vqx->vtv_vq); 1271*62a69c41SBryan Venteicher } 127210b59a9bSPeter Grehan 12736632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 12746632efe4SBryan Venteicher } 12756632efe4SBryan Venteicher 12766632efe4SBryan Venteicher static void 12776632efe4SBryan Venteicher vtpci_vq_shared_intr(void *xsc) 12786632efe4SBryan Venteicher { 12796632efe4SBryan Venteicher struct vtpci_softc *sc; 12806632efe4SBryan Venteicher struct vtpci_virtqueue *vqx; 12816632efe4SBryan Venteicher int i; 12826632efe4SBryan Venteicher 12836632efe4SBryan Venteicher sc = xsc; 1284*62a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 12856632efe4SBryan Venteicher 1286*62a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 1287*62a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 1288*62a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 1289*62a69c41SBryan Venteicher } 129010b59a9bSPeter Grehan } 129110b59a9bSPeter Grehan 129210b59a9bSPeter Grehan static int 12936632efe4SBryan Venteicher vtpci_vq_intr_filter(void *xvq) 129410b59a9bSPeter Grehan { 129510b59a9bSPeter Grehan struct virtqueue *vq; 129610b59a9bSPeter Grehan int rc; 129710b59a9bSPeter Grehan 129810b59a9bSPeter Grehan vq = xvq; 12996632efe4SBryan Venteicher rc = virtqueue_intr_filter(vq); 130010b59a9bSPeter Grehan 13016632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 130210b59a9bSPeter Grehan } 130310b59a9bSPeter Grehan 13046632efe4SBryan Venteicher static void 13056632efe4SBryan Venteicher vtpci_vq_intr(void *xvq) 13066632efe4SBryan Venteicher { 13076632efe4SBryan Venteicher struct virtqueue *vq; 13086632efe4SBryan Venteicher 13096632efe4SBryan Venteicher vq = xvq; 13106632efe4SBryan Venteicher virtqueue_intr(vq); 13116632efe4SBryan Venteicher } 13126632efe4SBryan Venteicher 13136632efe4SBryan Venteicher static void 131410b59a9bSPeter Grehan vtpci_config_intr(void *xsc) 131510b59a9bSPeter Grehan { 131610b59a9bSPeter Grehan struct vtpci_softc *sc; 131710b59a9bSPeter Grehan device_t child; 131810b59a9bSPeter Grehan 131910b59a9bSPeter Grehan sc = xsc; 132010b59a9bSPeter Grehan child = sc->vtpci_child_dev; 132110b59a9bSPeter Grehan 132210b59a9bSPeter Grehan if (child != NULL) 13236632efe4SBryan Venteicher VIRTIO_CONFIG_CHANGE(child); 132410b59a9bSPeter Grehan } 1325