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> 489a732166SBryan Venteicher #include <dev/virtio/virtio_config.h> 4910b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h> 5010b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h> 5110b59a9bSPeter Grehan 5210b59a9bSPeter Grehan #include "virtio_bus_if.h" 5310b59a9bSPeter Grehan #include "virtio_if.h" 5410b59a9bSPeter Grehan 5562a69c41SBryan Venteicher struct vtpci_interrupt { 5662a69c41SBryan Venteicher struct resource *vti_irq; 5762a69c41SBryan Venteicher int vti_rid; 5862a69c41SBryan Venteicher void *vti_handler; 5962a69c41SBryan Venteicher }; 6062a69c41SBryan Venteicher 6162a69c41SBryan Venteicher struct vtpci_virtqueue { 6262a69c41SBryan Venteicher struct virtqueue *vtv_vq; 6362a69c41SBryan Venteicher int vtv_no_intr; 6462a69c41SBryan Venteicher }; 6562a69c41SBryan Venteicher 6610b59a9bSPeter Grehan struct vtpci_softc { 6710b59a9bSPeter Grehan device_t vtpci_dev; 6810b59a9bSPeter Grehan struct resource *vtpci_res; 6910b59a9bSPeter Grehan struct resource *vtpci_msix_res; 7010b59a9bSPeter Grehan uint64_t vtpci_features; 7110b59a9bSPeter Grehan uint32_t vtpci_flags; 72310dacd0SPeter Grehan #define VTPCI_FLAG_NO_MSI 0x0001 73310dacd0SPeter Grehan #define VTPCI_FLAG_NO_MSIX 0x0002 74310dacd0SPeter Grehan #define VTPCI_FLAG_LEGACY 0x1000 75310dacd0SPeter Grehan #define VTPCI_FLAG_MSI 0x2000 76310dacd0SPeter Grehan #define VTPCI_FLAG_MSIX 0x4000 77310dacd0SPeter Grehan #define VTPCI_FLAG_SHARED_MSIX 0x8000 78310dacd0SPeter Grehan #define VTPCI_FLAG_ITYPE_MASK 0xF000 7910b59a9bSPeter Grehan 80310dacd0SPeter Grehan /* This "bus" will only ever have one child. */ 8110b59a9bSPeter Grehan device_t vtpci_child_dev; 8210b59a9bSPeter Grehan struct virtio_feature_desc *vtpci_child_feat_desc; 8310b59a9bSPeter Grehan 8410b59a9bSPeter Grehan int vtpci_nvqs; 8562a69c41SBryan Venteicher struct vtpci_virtqueue *vtpci_vqs; 8610b59a9bSPeter Grehan 8710b59a9bSPeter Grehan /* 8862a69c41SBryan Venteicher * Ideally, each virtqueue that the driver provides a callback for will 8962a69c41SBryan Venteicher * receive its own MSIX vector. If there are not sufficient vectors 9062a69c41SBryan Venteicher * available, then attempt to have all the VQs share one vector. For 9162a69c41SBryan Venteicher * MSIX, the configuration changed notifications must be on their own 9262a69c41SBryan Venteicher * vector. 9310b59a9bSPeter Grehan * 9462a69c41SBryan Venteicher * If MSIX is not available, we will attempt to have the whole device 9562a69c41SBryan Venteicher * share one MSI vector, and then, finally, one legacy interrupt. 9610b59a9bSPeter Grehan */ 9762a69c41SBryan Venteicher struct vtpci_interrupt vtpci_device_interrupt; 9862a69c41SBryan Venteicher struct vtpci_interrupt *vtpci_msix_vq_interrupts; 9962a69c41SBryan Venteicher int vtpci_nmsix_resources; 10010b59a9bSPeter Grehan }; 10110b59a9bSPeter Grehan 10210b59a9bSPeter Grehan static int vtpci_probe(device_t); 10310b59a9bSPeter Grehan static int vtpci_attach(device_t); 10410b59a9bSPeter Grehan static int vtpci_detach(device_t); 10510b59a9bSPeter Grehan static int vtpci_suspend(device_t); 10610b59a9bSPeter Grehan static int vtpci_resume(device_t); 10710b59a9bSPeter Grehan static int vtpci_shutdown(device_t); 10810b59a9bSPeter Grehan static void vtpci_driver_added(device_t, driver_t *); 10910b59a9bSPeter Grehan static void vtpci_child_detached(device_t, device_t); 11010b59a9bSPeter Grehan static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *); 11110b59a9bSPeter Grehan static int vtpci_write_ivar(device_t, device_t, int, uintptr_t); 11210b59a9bSPeter Grehan 11310b59a9bSPeter Grehan static uint64_t vtpci_negotiate_features(device_t, uint64_t); 11410b59a9bSPeter Grehan static int vtpci_with_feature(device_t, uint64_t); 11510b59a9bSPeter Grehan static int vtpci_alloc_virtqueues(device_t, int, int, 11610b59a9bSPeter Grehan struct vq_alloc_info *); 11710b59a9bSPeter Grehan static int vtpci_setup_intr(device_t, enum intr_type); 11810b59a9bSPeter Grehan static void vtpci_stop(device_t); 11910b59a9bSPeter Grehan static int vtpci_reinit(device_t, uint64_t); 12010b59a9bSPeter Grehan static void vtpci_reinit_complete(device_t); 12110b59a9bSPeter Grehan static void vtpci_notify_virtqueue(device_t, uint16_t); 12210b59a9bSPeter Grehan static uint8_t vtpci_get_status(device_t); 12310b59a9bSPeter Grehan static void vtpci_set_status(device_t, uint8_t); 12410b59a9bSPeter Grehan static void vtpci_read_dev_config(device_t, bus_size_t, void *, int); 12510b59a9bSPeter Grehan static void vtpci_write_dev_config(device_t, bus_size_t, void *, int); 12610b59a9bSPeter Grehan 12710b59a9bSPeter Grehan static void vtpci_describe_features(struct vtpci_softc *, const char *, 12810b59a9bSPeter Grehan uint64_t); 12910b59a9bSPeter Grehan static void vtpci_probe_and_attach_child(struct vtpci_softc *); 13010b59a9bSPeter Grehan 13110b59a9bSPeter Grehan static int vtpci_alloc_msix(struct vtpci_softc *, int); 132310dacd0SPeter Grehan static int vtpci_alloc_msi(struct vtpci_softc *); 133310dacd0SPeter Grehan static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *); 134310dacd0SPeter Grehan static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *); 135310dacd0SPeter Grehan static int vtpci_alloc_intr_msi(struct vtpci_softc *); 136310dacd0SPeter Grehan static int vtpci_alloc_intr_legacy(struct vtpci_softc *); 13762a69c41SBryan Venteicher static int vtpci_alloc_interrupt(struct vtpci_softc *, int, int, 13862a69c41SBryan Venteicher struct vtpci_interrupt *); 139310dacd0SPeter Grehan static int vtpci_alloc_intr_resources(struct vtpci_softc *); 140310dacd0SPeter Grehan 141310dacd0SPeter Grehan static int vtpci_setup_legacy_interrupt(struct vtpci_softc *, 142310dacd0SPeter Grehan enum intr_type); 14362a69c41SBryan Venteicher static int vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *, 14462a69c41SBryan Venteicher enum intr_type); 145310dacd0SPeter Grehan static int vtpci_setup_msix_interrupts(struct vtpci_softc *, 146310dacd0SPeter Grehan enum intr_type); 147310dacd0SPeter Grehan static int vtpci_setup_interrupts(struct vtpci_softc *, enum intr_type); 148310dacd0SPeter Grehan 14962a69c41SBryan Venteicher static int vtpci_register_msix_vector(struct vtpci_softc *, int, 15062a69c41SBryan Venteicher struct vtpci_interrupt *); 151310dacd0SPeter Grehan static int vtpci_set_host_msix_vectors(struct vtpci_softc *); 152310dacd0SPeter Grehan static int vtpci_reinit_virtqueue(struct vtpci_softc *, int); 15310b59a9bSPeter Grehan 15462a69c41SBryan Venteicher static void vtpci_free_interrupt(struct vtpci_softc *, 15562a69c41SBryan Venteicher struct vtpci_interrupt *); 15610b59a9bSPeter Grehan static void vtpci_free_interrupts(struct vtpci_softc *); 15710b59a9bSPeter Grehan static void vtpci_free_virtqueues(struct vtpci_softc *); 15810b59a9bSPeter Grehan static void vtpci_release_child_resources(struct vtpci_softc *); 15962a69c41SBryan Venteicher static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *); 16010b59a9bSPeter Grehan static void vtpci_reset(struct vtpci_softc *); 16110b59a9bSPeter Grehan 162310dacd0SPeter Grehan static void vtpci_select_virtqueue(struct vtpci_softc *, int); 163310dacd0SPeter Grehan 1646632efe4SBryan Venteicher static void vtpci_legacy_intr(void *); 1656632efe4SBryan Venteicher static int vtpci_vq_shared_intr_filter(void *); 1666632efe4SBryan Venteicher static void vtpci_vq_shared_intr(void *); 1676632efe4SBryan Venteicher static int vtpci_vq_intr_filter(void *); 1686632efe4SBryan Venteicher static void vtpci_vq_intr(void *); 1696632efe4SBryan Venteicher static void vtpci_config_intr(void *); 17010b59a9bSPeter Grehan 171310dacd0SPeter Grehan #define vtpci_setup_msi_interrupt vtpci_setup_legacy_interrupt 172310dacd0SPeter Grehan 173*e026de11SBryan Venteicher #define VIRTIO_PCI_CONFIG(_sc) \ 174*e026de11SBryan Venteicher VIRTIO_PCI_CONFIG_OFF((((_sc)->vtpci_flags & VTPCI_FLAG_MSIX)) != 0) 175*e026de11SBryan Venteicher 17610b59a9bSPeter Grehan /* 17710b59a9bSPeter Grehan * I/O port read/write wrappers. 17810b59a9bSPeter Grehan */ 17910b59a9bSPeter Grehan #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o)) 18010b59a9bSPeter Grehan #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o)) 18110b59a9bSPeter Grehan #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o)) 18210b59a9bSPeter Grehan #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v)) 18310b59a9bSPeter Grehan #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v)) 18410b59a9bSPeter Grehan #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v)) 18510b59a9bSPeter Grehan 18610b59a9bSPeter Grehan /* Tunables. */ 18710b59a9bSPeter Grehan static int vtpci_disable_msix = 0; 18810b59a9bSPeter Grehan TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 18910b59a9bSPeter Grehan 19010b59a9bSPeter Grehan static device_method_t vtpci_methods[] = { 19110b59a9bSPeter Grehan /* Device interface. */ 19210b59a9bSPeter Grehan DEVMETHOD(device_probe, vtpci_probe), 19310b59a9bSPeter Grehan DEVMETHOD(device_attach, vtpci_attach), 19410b59a9bSPeter Grehan DEVMETHOD(device_detach, vtpci_detach), 19510b59a9bSPeter Grehan DEVMETHOD(device_suspend, vtpci_suspend), 19610b59a9bSPeter Grehan DEVMETHOD(device_resume, vtpci_resume), 19710b59a9bSPeter Grehan DEVMETHOD(device_shutdown, vtpci_shutdown), 19810b59a9bSPeter Grehan 19910b59a9bSPeter Grehan /* Bus interface. */ 20010b59a9bSPeter Grehan DEVMETHOD(bus_driver_added, vtpci_driver_added), 20110b59a9bSPeter Grehan DEVMETHOD(bus_child_detached, vtpci_child_detached), 20210b59a9bSPeter Grehan DEVMETHOD(bus_read_ivar, vtpci_read_ivar), 20310b59a9bSPeter Grehan DEVMETHOD(bus_write_ivar, vtpci_write_ivar), 20410b59a9bSPeter Grehan 20510b59a9bSPeter Grehan /* VirtIO bus interface. */ 20610b59a9bSPeter Grehan DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features), 20710b59a9bSPeter Grehan DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature), 20810b59a9bSPeter Grehan DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues), 20910b59a9bSPeter Grehan DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr), 21010b59a9bSPeter Grehan DEVMETHOD(virtio_bus_stop, vtpci_stop), 21110b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit, vtpci_reinit), 21210b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete), 21310b59a9bSPeter Grehan DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), 21410b59a9bSPeter Grehan DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), 21510b59a9bSPeter Grehan DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), 21610b59a9bSPeter Grehan 217b8a58707SPeter Grehan DEVMETHOD_END 21810b59a9bSPeter Grehan }; 21910b59a9bSPeter Grehan 22010b59a9bSPeter Grehan static driver_t vtpci_driver = { 22110b59a9bSPeter Grehan "virtio_pci", 22210b59a9bSPeter Grehan vtpci_methods, 22310b59a9bSPeter Grehan sizeof(struct vtpci_softc) 22410b59a9bSPeter Grehan }; 22510b59a9bSPeter Grehan 22610b59a9bSPeter Grehan devclass_t vtpci_devclass; 22710b59a9bSPeter Grehan 22810b59a9bSPeter Grehan DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0); 22910b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1); 23010b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 23110b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 23210b59a9bSPeter Grehan 23310b59a9bSPeter Grehan static int 23410b59a9bSPeter Grehan vtpci_probe(device_t dev) 23510b59a9bSPeter Grehan { 23610b59a9bSPeter Grehan char desc[36]; 23710b59a9bSPeter Grehan const char *name; 23810b59a9bSPeter Grehan 23910b59a9bSPeter Grehan if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID) 24010b59a9bSPeter Grehan return (ENXIO); 24110b59a9bSPeter Grehan 24210b59a9bSPeter Grehan if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN || 24310b59a9bSPeter Grehan pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX) 24410b59a9bSPeter Grehan return (ENXIO); 24510b59a9bSPeter Grehan 24610b59a9bSPeter Grehan if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION) 24710b59a9bSPeter Grehan return (ENXIO); 24810b59a9bSPeter Grehan 24910b59a9bSPeter Grehan name = virtio_device_name(pci_get_subdevice(dev)); 25010b59a9bSPeter Grehan if (name == NULL) 25110b59a9bSPeter Grehan name = "Unknown"; 25210b59a9bSPeter Grehan 25310b59a9bSPeter Grehan snprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name); 25410b59a9bSPeter Grehan device_set_desc_copy(dev, desc); 25510b59a9bSPeter Grehan 25610b59a9bSPeter Grehan return (BUS_PROBE_DEFAULT); 25710b59a9bSPeter Grehan } 25810b59a9bSPeter Grehan 25910b59a9bSPeter Grehan static int 26010b59a9bSPeter Grehan vtpci_attach(device_t dev) 26110b59a9bSPeter Grehan { 26210b59a9bSPeter Grehan struct vtpci_softc *sc; 26310b59a9bSPeter Grehan device_t child; 26410b59a9bSPeter Grehan int rid; 26510b59a9bSPeter Grehan 26610b59a9bSPeter Grehan sc = device_get_softc(dev); 26710b59a9bSPeter Grehan sc->vtpci_dev = dev; 26810b59a9bSPeter Grehan 26910b59a9bSPeter Grehan pci_enable_busmaster(dev); 27010b59a9bSPeter Grehan 27110b59a9bSPeter Grehan rid = PCIR_BAR(0); 27210b59a9bSPeter Grehan sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 27310b59a9bSPeter Grehan RF_ACTIVE); 27410b59a9bSPeter Grehan if (sc->vtpci_res == NULL) { 27510b59a9bSPeter Grehan device_printf(dev, "cannot map I/O space\n"); 27610b59a9bSPeter Grehan return (ENXIO); 27710b59a9bSPeter Grehan } 27810b59a9bSPeter Grehan 279cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSI, NULL) != 0) 280310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_NO_MSI; 28110b59a9bSPeter Grehan 282cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSIX, NULL) == 0) { 28310b59a9bSPeter Grehan rid = PCIR_BAR(1); 28410b59a9bSPeter Grehan sc->vtpci_msix_res = bus_alloc_resource_any(dev, 28510b59a9bSPeter Grehan SYS_RES_MEMORY, &rid, RF_ACTIVE); 28610b59a9bSPeter Grehan } 28710b59a9bSPeter Grehan 28810b59a9bSPeter Grehan if (sc->vtpci_msix_res == NULL) 289310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_NO_MSIX; 29010b59a9bSPeter Grehan 29110b59a9bSPeter Grehan vtpci_reset(sc); 29210b59a9bSPeter Grehan 29310b59a9bSPeter Grehan /* Tell the host we've noticed this device. */ 29410b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 29510b59a9bSPeter Grehan 29610b59a9bSPeter Grehan if ((child = device_add_child(dev, NULL, -1)) == NULL) { 29710b59a9bSPeter Grehan device_printf(dev, "cannot create child device\n"); 29810b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 29910b59a9bSPeter Grehan vtpci_detach(dev); 30010b59a9bSPeter Grehan return (ENOMEM); 30110b59a9bSPeter Grehan } 30210b59a9bSPeter Grehan 30310b59a9bSPeter Grehan sc->vtpci_child_dev = child; 30410b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 30510b59a9bSPeter Grehan 30610b59a9bSPeter Grehan return (0); 30710b59a9bSPeter Grehan } 30810b59a9bSPeter Grehan 30910b59a9bSPeter Grehan static int 31010b59a9bSPeter Grehan vtpci_detach(device_t dev) 31110b59a9bSPeter Grehan { 31210b59a9bSPeter Grehan struct vtpci_softc *sc; 31310b59a9bSPeter Grehan device_t child; 31410b59a9bSPeter Grehan int error; 31510b59a9bSPeter Grehan 31610b59a9bSPeter Grehan sc = device_get_softc(dev); 31710b59a9bSPeter Grehan 31810b59a9bSPeter Grehan if ((child = sc->vtpci_child_dev) != NULL) { 31910b59a9bSPeter Grehan error = device_delete_child(dev, child); 32010b59a9bSPeter Grehan if (error) 32110b59a9bSPeter Grehan return (error); 32210b59a9bSPeter Grehan sc->vtpci_child_dev = NULL; 32310b59a9bSPeter Grehan } 32410b59a9bSPeter Grehan 32510b59a9bSPeter Grehan vtpci_reset(sc); 32610b59a9bSPeter Grehan 32710b59a9bSPeter Grehan if (sc->vtpci_msix_res != NULL) { 32810b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), 32910b59a9bSPeter Grehan sc->vtpci_msix_res); 33010b59a9bSPeter Grehan sc->vtpci_msix_res = NULL; 33110b59a9bSPeter Grehan } 33210b59a9bSPeter Grehan 33310b59a9bSPeter Grehan if (sc->vtpci_res != NULL) { 33410b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), 33510b59a9bSPeter Grehan sc->vtpci_res); 33610b59a9bSPeter Grehan sc->vtpci_res = NULL; 33710b59a9bSPeter Grehan } 33810b59a9bSPeter Grehan 33910b59a9bSPeter Grehan return (0); 34010b59a9bSPeter Grehan } 34110b59a9bSPeter Grehan 34210b59a9bSPeter Grehan static int 34310b59a9bSPeter Grehan vtpci_suspend(device_t dev) 34410b59a9bSPeter Grehan { 34510b59a9bSPeter Grehan 34610b59a9bSPeter Grehan return (bus_generic_suspend(dev)); 34710b59a9bSPeter Grehan } 34810b59a9bSPeter Grehan 34910b59a9bSPeter Grehan static int 35010b59a9bSPeter Grehan vtpci_resume(device_t dev) 35110b59a9bSPeter Grehan { 35210b59a9bSPeter Grehan 35310b59a9bSPeter Grehan return (bus_generic_resume(dev)); 35410b59a9bSPeter Grehan } 35510b59a9bSPeter Grehan 35610b59a9bSPeter Grehan static int 35710b59a9bSPeter Grehan vtpci_shutdown(device_t dev) 35810b59a9bSPeter Grehan { 35910b59a9bSPeter Grehan 36010b59a9bSPeter Grehan (void) bus_generic_shutdown(dev); 36110b59a9bSPeter Grehan /* Forcibly stop the host device. */ 36210b59a9bSPeter Grehan vtpci_stop(dev); 36310b59a9bSPeter Grehan 36410b59a9bSPeter Grehan return (0); 36510b59a9bSPeter Grehan } 36610b59a9bSPeter Grehan 36710b59a9bSPeter Grehan static void 36810b59a9bSPeter Grehan vtpci_driver_added(device_t dev, driver_t *driver) 36910b59a9bSPeter Grehan { 37010b59a9bSPeter Grehan struct vtpci_softc *sc; 37110b59a9bSPeter Grehan 37210b59a9bSPeter Grehan sc = device_get_softc(dev); 37310b59a9bSPeter Grehan 37410b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 37510b59a9bSPeter Grehan } 37610b59a9bSPeter Grehan 37710b59a9bSPeter Grehan static void 37810b59a9bSPeter Grehan vtpci_child_detached(device_t dev, device_t child) 37910b59a9bSPeter Grehan { 38010b59a9bSPeter Grehan struct vtpci_softc *sc; 38110b59a9bSPeter Grehan 38210b59a9bSPeter Grehan sc = device_get_softc(dev); 38310b59a9bSPeter Grehan 38410b59a9bSPeter Grehan vtpci_reset(sc); 38510b59a9bSPeter Grehan vtpci_release_child_resources(sc); 38610b59a9bSPeter Grehan } 38710b59a9bSPeter Grehan 38810b59a9bSPeter Grehan static int 38910b59a9bSPeter Grehan vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 39010b59a9bSPeter Grehan { 39110b59a9bSPeter Grehan struct vtpci_softc *sc; 39210b59a9bSPeter Grehan 39310b59a9bSPeter Grehan sc = device_get_softc(dev); 39410b59a9bSPeter Grehan 39510b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 39610b59a9bSPeter Grehan return (ENOENT); 39710b59a9bSPeter Grehan 39810b59a9bSPeter Grehan switch (index) { 39910b59a9bSPeter Grehan case VIRTIO_IVAR_DEVTYPE: 400310dacd0SPeter Grehan case VIRTIO_IVAR_SUBDEVICE: 401310dacd0SPeter Grehan *result = pci_get_subdevice(dev); 402310dacd0SPeter Grehan break; 403310dacd0SPeter Grehan case VIRTIO_IVAR_VENDOR: 404310dacd0SPeter Grehan *result = pci_get_vendor(dev); 405310dacd0SPeter Grehan break; 406310dacd0SPeter Grehan case VIRTIO_IVAR_DEVICE: 407310dacd0SPeter Grehan *result = pci_get_device(dev); 408310dacd0SPeter Grehan break; 409310dacd0SPeter Grehan case VIRTIO_IVAR_SUBVENDOR: 41010b59a9bSPeter Grehan *result = pci_get_subdevice(dev); 41110b59a9bSPeter Grehan break; 41210b59a9bSPeter Grehan default: 41310b59a9bSPeter Grehan return (ENOENT); 41410b59a9bSPeter Grehan } 41510b59a9bSPeter Grehan 41610b59a9bSPeter Grehan return (0); 41710b59a9bSPeter Grehan } 41810b59a9bSPeter Grehan 41910b59a9bSPeter Grehan static int 42010b59a9bSPeter Grehan vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 42110b59a9bSPeter Grehan { 42210b59a9bSPeter Grehan struct vtpci_softc *sc; 42310b59a9bSPeter Grehan 42410b59a9bSPeter Grehan sc = device_get_softc(dev); 42510b59a9bSPeter Grehan 42610b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 42710b59a9bSPeter Grehan return (ENOENT); 42810b59a9bSPeter Grehan 42910b59a9bSPeter Grehan switch (index) { 43010b59a9bSPeter Grehan case VIRTIO_IVAR_FEATURE_DESC: 43110b59a9bSPeter Grehan sc->vtpci_child_feat_desc = (void *) value; 43210b59a9bSPeter Grehan break; 43310b59a9bSPeter Grehan default: 43410b59a9bSPeter Grehan return (ENOENT); 43510b59a9bSPeter Grehan } 43610b59a9bSPeter Grehan 43710b59a9bSPeter Grehan return (0); 43810b59a9bSPeter Grehan } 43910b59a9bSPeter Grehan 44010b59a9bSPeter Grehan static uint64_t 44110b59a9bSPeter Grehan vtpci_negotiate_features(device_t dev, uint64_t child_features) 44210b59a9bSPeter Grehan { 44310b59a9bSPeter Grehan struct vtpci_softc *sc; 44410b59a9bSPeter Grehan uint64_t host_features, features; 44510b59a9bSPeter Grehan 44610b59a9bSPeter Grehan sc = device_get_softc(dev); 44710b59a9bSPeter Grehan 44810b59a9bSPeter Grehan host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES); 44910b59a9bSPeter Grehan vtpci_describe_features(sc, "host", host_features); 45010b59a9bSPeter Grehan 45110b59a9bSPeter Grehan /* 45210b59a9bSPeter Grehan * Limit negotiated features to what the driver, virtqueue, and 45310b59a9bSPeter Grehan * host all support. 45410b59a9bSPeter Grehan */ 45510b59a9bSPeter Grehan features = host_features & child_features; 45610b59a9bSPeter Grehan features = virtqueue_filter_features(features); 45710b59a9bSPeter Grehan sc->vtpci_features = features; 45810b59a9bSPeter Grehan 45910b59a9bSPeter Grehan vtpci_describe_features(sc, "negotiated", features); 46010b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features); 46110b59a9bSPeter Grehan 46210b59a9bSPeter Grehan return (features); 46310b59a9bSPeter Grehan } 46410b59a9bSPeter Grehan 46510b59a9bSPeter Grehan static int 46610b59a9bSPeter Grehan vtpci_with_feature(device_t dev, uint64_t feature) 46710b59a9bSPeter Grehan { 46810b59a9bSPeter Grehan struct vtpci_softc *sc; 46910b59a9bSPeter Grehan 47010b59a9bSPeter Grehan sc = device_get_softc(dev); 47110b59a9bSPeter Grehan 47210b59a9bSPeter Grehan return ((sc->vtpci_features & feature) != 0); 47310b59a9bSPeter Grehan } 47410b59a9bSPeter Grehan 47510b59a9bSPeter Grehan static int 47610b59a9bSPeter Grehan vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, 47710b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 47810b59a9bSPeter Grehan { 47910b59a9bSPeter Grehan struct vtpci_softc *sc; 480310dacd0SPeter Grehan struct virtqueue *vq; 48110b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 48210b59a9bSPeter Grehan struct vq_alloc_info *info; 483310dacd0SPeter Grehan int idx, error; 484310dacd0SPeter Grehan uint16_t size; 48510b59a9bSPeter Grehan 48610b59a9bSPeter Grehan sc = device_get_softc(dev); 48710b59a9bSPeter Grehan 488310dacd0SPeter Grehan if (sc->vtpci_nvqs != 0) 489310dacd0SPeter Grehan return (EALREADY); 49062a69c41SBryan Venteicher if (nvqs <= 0) 49110b59a9bSPeter Grehan return (EINVAL); 49210b59a9bSPeter Grehan 49362a69c41SBryan Venteicher sc->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue), 49462a69c41SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 49562a69c41SBryan Venteicher if (sc->vtpci_vqs == NULL) 49662a69c41SBryan Venteicher return (ENOMEM); 49762a69c41SBryan Venteicher 498310dacd0SPeter Grehan for (idx = 0; idx < nvqs; idx++) { 49962a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 500310dacd0SPeter Grehan info = &vq_info[idx]; 501310dacd0SPeter Grehan 502310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 503310dacd0SPeter Grehan size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 504310dacd0SPeter Grehan 505310dacd0SPeter Grehan error = virtqueue_alloc(dev, idx, size, VIRTIO_PCI_VRING_ALIGN, 506310dacd0SPeter Grehan 0xFFFFFFFFUL, info, &vq); 50710b59a9bSPeter Grehan if (error) { 508310dacd0SPeter Grehan device_printf(dev, 509310dacd0SPeter Grehan "cannot allocate virtqueue %d: %d\n", idx, error); 510310dacd0SPeter Grehan break; 51110b59a9bSPeter Grehan } 51210b59a9bSPeter Grehan 51310b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 514310dacd0SPeter Grehan virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 51510b59a9bSPeter Grehan 51662a69c41SBryan Venteicher vqx->vtv_vq = *info->vqai_vq = vq; 51762a69c41SBryan Venteicher vqx->vtv_no_intr = info->vqai_intr == NULL; 518310dacd0SPeter Grehan 51910b59a9bSPeter Grehan sc->vtpci_nvqs++; 52010b59a9bSPeter Grehan } 52110b59a9bSPeter Grehan 52262a69c41SBryan Venteicher if (error) 52362a69c41SBryan Venteicher vtpci_free_virtqueues(sc); 52462a69c41SBryan Venteicher 525310dacd0SPeter Grehan return (error); 52610b59a9bSPeter Grehan } 52710b59a9bSPeter Grehan 52810b59a9bSPeter Grehan static int 52910b59a9bSPeter Grehan vtpci_setup_intr(device_t dev, enum intr_type type) 53010b59a9bSPeter Grehan { 53110b59a9bSPeter Grehan struct vtpci_softc *sc; 532310dacd0SPeter Grehan int attempt, error; 53310b59a9bSPeter Grehan 53410b59a9bSPeter Grehan sc = device_get_softc(dev); 53510b59a9bSPeter Grehan 536310dacd0SPeter Grehan for (attempt = 0; attempt < 5; attempt++) { 537310dacd0SPeter Grehan /* 538310dacd0SPeter Grehan * Start with the most desirable interrupt configuration and 539310dacd0SPeter Grehan * fallback towards less desirable ones. 540310dacd0SPeter Grehan */ 541310dacd0SPeter Grehan switch (attempt) { 542310dacd0SPeter Grehan case 0: 543310dacd0SPeter Grehan error = vtpci_alloc_intr_msix_pervq(sc); 544310dacd0SPeter Grehan break; 545310dacd0SPeter Grehan case 1: 546310dacd0SPeter Grehan error = vtpci_alloc_intr_msix_shared(sc); 547310dacd0SPeter Grehan break; 548310dacd0SPeter Grehan case 2: 549310dacd0SPeter Grehan error = vtpci_alloc_intr_msi(sc); 550310dacd0SPeter Grehan break; 551310dacd0SPeter Grehan case 3: 552310dacd0SPeter Grehan error = vtpci_alloc_intr_legacy(sc); 553310dacd0SPeter Grehan break; 554310dacd0SPeter Grehan default: 555310dacd0SPeter Grehan device_printf(dev, 556310dacd0SPeter Grehan "exhausted all interrupt allocation attempts\n"); 557310dacd0SPeter Grehan return (ENXIO); 55810b59a9bSPeter Grehan } 55910b59a9bSPeter Grehan 560310dacd0SPeter Grehan if (error == 0 && vtpci_setup_interrupts(sc, type) == 0) 561310dacd0SPeter Grehan break; 56210b59a9bSPeter Grehan 563310dacd0SPeter Grehan vtpci_cleanup_setup_intr_attempt(sc); 56410b59a9bSPeter Grehan } 56510b59a9bSPeter Grehan 566310dacd0SPeter Grehan if (bootverbose) { 567310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 568310dacd0SPeter Grehan device_printf(dev, "using legacy interrupt\n"); 569310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_MSI) 570310dacd0SPeter Grehan device_printf(dev, "using MSI interrupt\n"); 571310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) 572310dacd0SPeter Grehan device_printf(dev, "using shared MSIX interrupts\n"); 573310dacd0SPeter Grehan else 574310dacd0SPeter Grehan device_printf(dev, "using per VQ MSIX interrupts\n"); 57510b59a9bSPeter Grehan } 57610b59a9bSPeter Grehan 57710b59a9bSPeter Grehan return (0); 57810b59a9bSPeter Grehan } 57910b59a9bSPeter Grehan 58010b59a9bSPeter Grehan static void 58110b59a9bSPeter Grehan vtpci_stop(device_t dev) 58210b59a9bSPeter Grehan { 58310b59a9bSPeter Grehan 58410b59a9bSPeter Grehan vtpci_reset(device_get_softc(dev)); 58510b59a9bSPeter Grehan } 58610b59a9bSPeter Grehan 58710b59a9bSPeter Grehan static int 58810b59a9bSPeter Grehan vtpci_reinit(device_t dev, uint64_t features) 58910b59a9bSPeter Grehan { 59010b59a9bSPeter Grehan struct vtpci_softc *sc; 591310dacd0SPeter Grehan int idx, error; 59210b59a9bSPeter Grehan 59310b59a9bSPeter Grehan sc = device_get_softc(dev); 59410b59a9bSPeter Grehan 59510b59a9bSPeter Grehan /* 596310dacd0SPeter Grehan * Redrive the device initialization. This is a bit of an abuse of 597310dacd0SPeter Grehan * the specification, but VirtualBox, QEMU/KVM, and BHyVe seem to 598310dacd0SPeter Grehan * play nice. 599310dacd0SPeter Grehan * 600310dacd0SPeter Grehan * We do not allow the host device to change from what was originally 601310dacd0SPeter Grehan * negotiated beyond what the guest driver changed. MSIX state should 602310dacd0SPeter Grehan * not change, number of virtqueues and their size remain the same, etc. 603310dacd0SPeter Grehan * This will need to be rethought when we want to support migration. 60410b59a9bSPeter Grehan */ 60510b59a9bSPeter Grehan 60610b59a9bSPeter Grehan if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 60710b59a9bSPeter Grehan vtpci_stop(dev); 60810b59a9bSPeter Grehan 60910b59a9bSPeter Grehan /* 61010b59a9bSPeter Grehan * Quickly drive the status through ACK and DRIVER. The device 61110b59a9bSPeter Grehan * does not become usable again until vtpci_reinit_complete(). 61210b59a9bSPeter Grehan */ 61310b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 61410b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 61510b59a9bSPeter Grehan 61610b59a9bSPeter Grehan vtpci_negotiate_features(dev, features); 61710b59a9bSPeter Grehan 618310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 619310dacd0SPeter Grehan error = vtpci_reinit_virtqueue(sc, idx); 62010b59a9bSPeter Grehan if (error) 62110b59a9bSPeter Grehan return (error); 62210b59a9bSPeter Grehan } 62310b59a9bSPeter Grehan 624310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_MSIX) { 625310dacd0SPeter Grehan error = vtpci_set_host_msix_vectors(sc); 62610b59a9bSPeter Grehan if (error) 62710b59a9bSPeter Grehan return (error); 62810b59a9bSPeter Grehan } 62910b59a9bSPeter Grehan 63010b59a9bSPeter Grehan return (0); 63110b59a9bSPeter Grehan } 63210b59a9bSPeter Grehan 63310b59a9bSPeter Grehan static void 63410b59a9bSPeter Grehan vtpci_reinit_complete(device_t dev) 63510b59a9bSPeter Grehan { 63610b59a9bSPeter Grehan 63710b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 63810b59a9bSPeter Grehan } 63910b59a9bSPeter Grehan 64010b59a9bSPeter Grehan static void 64110b59a9bSPeter Grehan vtpci_notify_virtqueue(device_t dev, uint16_t queue) 64210b59a9bSPeter Grehan { 64310b59a9bSPeter Grehan struct vtpci_softc *sc; 64410b59a9bSPeter Grehan 64510b59a9bSPeter Grehan sc = device_get_softc(dev); 64610b59a9bSPeter Grehan 64710b59a9bSPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue); 64810b59a9bSPeter Grehan } 64910b59a9bSPeter Grehan 65010b59a9bSPeter Grehan static uint8_t 65110b59a9bSPeter Grehan vtpci_get_status(device_t dev) 65210b59a9bSPeter Grehan { 65310b59a9bSPeter Grehan struct vtpci_softc *sc; 65410b59a9bSPeter Grehan 65510b59a9bSPeter Grehan sc = device_get_softc(dev); 65610b59a9bSPeter Grehan 65710b59a9bSPeter Grehan return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS)); 65810b59a9bSPeter Grehan } 65910b59a9bSPeter Grehan 66010b59a9bSPeter Grehan static void 66110b59a9bSPeter Grehan vtpci_set_status(device_t dev, uint8_t status) 66210b59a9bSPeter Grehan { 66310b59a9bSPeter Grehan struct vtpci_softc *sc; 66410b59a9bSPeter Grehan 66510b59a9bSPeter Grehan sc = device_get_softc(dev); 66610b59a9bSPeter Grehan 66710b59a9bSPeter Grehan if (status != VIRTIO_CONFIG_STATUS_RESET) 66810b59a9bSPeter Grehan status |= vtpci_get_status(dev); 66910b59a9bSPeter Grehan 67010b59a9bSPeter Grehan vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status); 67110b59a9bSPeter Grehan } 67210b59a9bSPeter Grehan 67310b59a9bSPeter Grehan static void 67410b59a9bSPeter Grehan vtpci_read_dev_config(device_t dev, bus_size_t offset, 67510b59a9bSPeter Grehan void *dst, int length) 67610b59a9bSPeter Grehan { 67710b59a9bSPeter Grehan struct vtpci_softc *sc; 67810b59a9bSPeter Grehan bus_size_t off; 67910b59a9bSPeter Grehan uint8_t *d; 68010b59a9bSPeter Grehan int size; 68110b59a9bSPeter Grehan 68210b59a9bSPeter Grehan sc = device_get_softc(dev); 68310b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 68410b59a9bSPeter Grehan 68510b59a9bSPeter Grehan for (d = dst; length > 0; d += size, off += size, length -= size) { 68610b59a9bSPeter Grehan if (length >= 4) { 68710b59a9bSPeter Grehan size = 4; 68810b59a9bSPeter Grehan *(uint32_t *)d = vtpci_read_config_4(sc, off); 68910b59a9bSPeter Grehan } else if (length >= 2) { 69010b59a9bSPeter Grehan size = 2; 69110b59a9bSPeter Grehan *(uint16_t *)d = vtpci_read_config_2(sc, off); 69210b59a9bSPeter Grehan } else { 69310b59a9bSPeter Grehan size = 1; 69410b59a9bSPeter Grehan *d = vtpci_read_config_1(sc, off); 69510b59a9bSPeter Grehan } 69610b59a9bSPeter Grehan } 69710b59a9bSPeter Grehan } 69810b59a9bSPeter Grehan 69910b59a9bSPeter Grehan static void 70010b59a9bSPeter Grehan vtpci_write_dev_config(device_t dev, bus_size_t offset, 70110b59a9bSPeter Grehan void *src, int length) 70210b59a9bSPeter Grehan { 70310b59a9bSPeter Grehan struct vtpci_softc *sc; 70410b59a9bSPeter Grehan bus_size_t off; 70510b59a9bSPeter Grehan uint8_t *s; 70610b59a9bSPeter Grehan int size; 70710b59a9bSPeter Grehan 70810b59a9bSPeter Grehan sc = device_get_softc(dev); 70910b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 71010b59a9bSPeter Grehan 71110b59a9bSPeter Grehan for (s = src; length > 0; s += size, off += size, length -= size) { 71210b59a9bSPeter Grehan if (length >= 4) { 71310b59a9bSPeter Grehan size = 4; 71410b59a9bSPeter Grehan vtpci_write_config_4(sc, off, *(uint32_t *)s); 71510b59a9bSPeter Grehan } else if (length >= 2) { 71610b59a9bSPeter Grehan size = 2; 71710b59a9bSPeter Grehan vtpci_write_config_2(sc, off, *(uint16_t *)s); 71810b59a9bSPeter Grehan } else { 71910b59a9bSPeter Grehan size = 1; 72010b59a9bSPeter Grehan vtpci_write_config_1(sc, off, *s); 72110b59a9bSPeter Grehan } 72210b59a9bSPeter Grehan } 72310b59a9bSPeter Grehan } 72410b59a9bSPeter Grehan 72510b59a9bSPeter Grehan static void 72610b59a9bSPeter Grehan vtpci_describe_features(struct vtpci_softc *sc, const char *msg, 72710b59a9bSPeter Grehan uint64_t features) 72810b59a9bSPeter Grehan { 72910b59a9bSPeter Grehan device_t dev, child; 73010b59a9bSPeter Grehan 73110b59a9bSPeter Grehan dev = sc->vtpci_dev; 73210b59a9bSPeter Grehan child = sc->vtpci_child_dev; 73310b59a9bSPeter Grehan 73410b59a9bSPeter Grehan if (device_is_attached(child) && bootverbose == 0) 73510b59a9bSPeter Grehan return; 73610b59a9bSPeter Grehan 73710b59a9bSPeter Grehan virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc); 73810b59a9bSPeter Grehan } 73910b59a9bSPeter Grehan 74010b59a9bSPeter Grehan static void 74110b59a9bSPeter Grehan vtpci_probe_and_attach_child(struct vtpci_softc *sc) 74210b59a9bSPeter Grehan { 74310b59a9bSPeter Grehan device_t dev, child; 74410b59a9bSPeter Grehan 74510b59a9bSPeter Grehan dev = sc->vtpci_dev; 74610b59a9bSPeter Grehan child = sc->vtpci_child_dev; 74710b59a9bSPeter Grehan 74810b59a9bSPeter Grehan if (child == NULL) 74910b59a9bSPeter Grehan return; 75010b59a9bSPeter Grehan 75110b59a9bSPeter Grehan if (device_get_state(child) != DS_NOTPRESENT) 75210b59a9bSPeter Grehan return; 75310b59a9bSPeter Grehan 75410b59a9bSPeter Grehan if (device_probe(child) != 0) 75510b59a9bSPeter Grehan return; 75610b59a9bSPeter Grehan 75710b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 75810b59a9bSPeter Grehan if (device_attach(child) != 0) { 75910b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 76010b59a9bSPeter Grehan vtpci_reset(sc); 76110b59a9bSPeter Grehan vtpci_release_child_resources(sc); 76210b59a9bSPeter Grehan /* Reset status for future attempt. */ 76310b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 76449a4385dSBryan Venteicher } else { 76510b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 76649a4385dSBryan Venteicher VIRTIO_ATTACH_COMPLETED(child); 76749a4385dSBryan Venteicher } 76810b59a9bSPeter Grehan } 76910b59a9bSPeter Grehan 77010b59a9bSPeter Grehan static int 771310dacd0SPeter Grehan vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors) 77210b59a9bSPeter Grehan { 773310dacd0SPeter Grehan device_t dev; 774310dacd0SPeter Grehan int nmsix, cnt, required; 77510b59a9bSPeter Grehan 776310dacd0SPeter Grehan dev = sc->vtpci_dev; 77710b59a9bSPeter Grehan 778310dacd0SPeter Grehan /* Allocate an additional vector for the config changes. */ 779310dacd0SPeter Grehan required = nvectors + 1; 78010b59a9bSPeter Grehan 781310dacd0SPeter Grehan nmsix = pci_msix_count(dev); 782310dacd0SPeter Grehan if (nmsix < required) 783310dacd0SPeter Grehan return (1); 784310dacd0SPeter Grehan 785310dacd0SPeter Grehan cnt = required; 786310dacd0SPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 78762a69c41SBryan Venteicher sc->vtpci_nmsix_resources = required; 788310dacd0SPeter Grehan return (0); 78910b59a9bSPeter Grehan } 79010b59a9bSPeter Grehan 791310dacd0SPeter Grehan pci_release_msi(dev); 79210b59a9bSPeter Grehan 793310dacd0SPeter Grehan return (1); 79410b59a9bSPeter Grehan } 79510b59a9bSPeter Grehan 79610b59a9bSPeter Grehan static int 797310dacd0SPeter Grehan vtpci_alloc_msi(struct vtpci_softc *sc) 798310dacd0SPeter Grehan { 799310dacd0SPeter Grehan device_t dev; 800310dacd0SPeter Grehan int nmsi, cnt, required; 801310dacd0SPeter Grehan 802310dacd0SPeter Grehan dev = sc->vtpci_dev; 803310dacd0SPeter Grehan required = 1; 804310dacd0SPeter Grehan 805310dacd0SPeter Grehan nmsi = pci_msi_count(dev); 806310dacd0SPeter Grehan if (nmsi < required) 807310dacd0SPeter Grehan return (1); 808310dacd0SPeter Grehan 809310dacd0SPeter Grehan cnt = required; 81062a69c41SBryan Venteicher if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) 811310dacd0SPeter Grehan return (0); 812310dacd0SPeter Grehan 813310dacd0SPeter Grehan pci_release_msi(dev); 814310dacd0SPeter Grehan 815310dacd0SPeter Grehan return (1); 816310dacd0SPeter Grehan } 817310dacd0SPeter Grehan 818310dacd0SPeter Grehan static int 819310dacd0SPeter Grehan vtpci_alloc_intr_msix_pervq(struct vtpci_softc *sc) 820310dacd0SPeter Grehan { 821310dacd0SPeter Grehan int i, nvectors, error; 822310dacd0SPeter Grehan 823310dacd0SPeter Grehan if (vtpci_disable_msix != 0 || 824310dacd0SPeter Grehan sc->vtpci_flags & VTPCI_FLAG_NO_MSIX) 825310dacd0SPeter Grehan return (ENOTSUP); 826310dacd0SPeter Grehan 827310dacd0SPeter Grehan for (nvectors = 0, i = 0; i < sc->vtpci_nvqs; i++) { 82862a69c41SBryan Venteicher if (sc->vtpci_vqs[i].vtv_no_intr == 0) 829310dacd0SPeter Grehan nvectors++; 830310dacd0SPeter Grehan } 831310dacd0SPeter Grehan 832310dacd0SPeter Grehan error = vtpci_alloc_msix(sc, nvectors); 833310dacd0SPeter Grehan if (error) 834310dacd0SPeter Grehan return (error); 835310dacd0SPeter Grehan 836310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSIX; 837310dacd0SPeter Grehan 838310dacd0SPeter Grehan return (0); 839310dacd0SPeter Grehan } 840310dacd0SPeter Grehan 841310dacd0SPeter Grehan static int 842310dacd0SPeter Grehan vtpci_alloc_intr_msix_shared(struct vtpci_softc *sc) 843310dacd0SPeter Grehan { 844310dacd0SPeter Grehan int error; 845310dacd0SPeter Grehan 846310dacd0SPeter Grehan if (vtpci_disable_msix != 0 || 847310dacd0SPeter Grehan sc->vtpci_flags & VTPCI_FLAG_NO_MSIX) 848310dacd0SPeter Grehan return (ENOTSUP); 849310dacd0SPeter Grehan 850310dacd0SPeter Grehan error = vtpci_alloc_msix(sc, 1); 851310dacd0SPeter Grehan if (error) 852310dacd0SPeter Grehan return (error); 853310dacd0SPeter Grehan 854310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSIX | VTPCI_FLAG_SHARED_MSIX; 855310dacd0SPeter Grehan 856310dacd0SPeter Grehan return (0); 857310dacd0SPeter Grehan } 858310dacd0SPeter Grehan 859310dacd0SPeter Grehan static int 860310dacd0SPeter Grehan vtpci_alloc_intr_msi(struct vtpci_softc *sc) 861310dacd0SPeter Grehan { 862310dacd0SPeter Grehan int error; 863310dacd0SPeter Grehan 864310dacd0SPeter Grehan /* Only BHyVe supports MSI. */ 865310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_NO_MSI) 866310dacd0SPeter Grehan return (ENOTSUP); 867310dacd0SPeter Grehan 868310dacd0SPeter Grehan error = vtpci_alloc_msi(sc); 869310dacd0SPeter Grehan if (error) 870310dacd0SPeter Grehan return (error); 871310dacd0SPeter Grehan 872310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSI; 873310dacd0SPeter Grehan 874310dacd0SPeter Grehan return (0); 875310dacd0SPeter Grehan } 876310dacd0SPeter Grehan 877310dacd0SPeter Grehan static int 878310dacd0SPeter Grehan vtpci_alloc_intr_legacy(struct vtpci_softc *sc) 879310dacd0SPeter Grehan { 880310dacd0SPeter Grehan 881310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_LEGACY; 88262a69c41SBryan Venteicher 88362a69c41SBryan Venteicher return (0); 88462a69c41SBryan Venteicher } 88562a69c41SBryan Venteicher 88662a69c41SBryan Venteicher static int 88762a69c41SBryan Venteicher vtpci_alloc_interrupt(struct vtpci_softc *sc, int rid, int flags, 88862a69c41SBryan Venteicher struct vtpci_interrupt *intr) 88962a69c41SBryan Venteicher { 89062a69c41SBryan Venteicher struct resource *irq; 89162a69c41SBryan Venteicher 89262a69c41SBryan Venteicher irq = bus_alloc_resource_any(sc->vtpci_dev, SYS_RES_IRQ, &rid, flags); 89362a69c41SBryan Venteicher if (irq == NULL) 89462a69c41SBryan Venteicher return (ENXIO); 89562a69c41SBryan Venteicher 89662a69c41SBryan Venteicher intr->vti_irq = irq; 89762a69c41SBryan Venteicher intr->vti_rid = rid; 898310dacd0SPeter Grehan 899310dacd0SPeter Grehan return (0); 900310dacd0SPeter Grehan } 901310dacd0SPeter Grehan 902310dacd0SPeter Grehan static int 903310dacd0SPeter Grehan vtpci_alloc_intr_resources(struct vtpci_softc *sc) 90410b59a9bSPeter Grehan { 90562a69c41SBryan Venteicher struct vtpci_interrupt *intr; 90662a69c41SBryan Venteicher int i, rid, flags, nvq_intrs, error; 90710b59a9bSPeter Grehan 90810b59a9bSPeter Grehan rid = 0; 909310dacd0SPeter Grehan flags = RF_ACTIVE; 91010b59a9bSPeter Grehan 91162a69c41SBryan Venteicher if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 91262a69c41SBryan Venteicher flags |= RF_SHAREABLE; 91362a69c41SBryan Venteicher else 91462a69c41SBryan Venteicher rid = 1; 91510b59a9bSPeter Grehan 91610b59a9bSPeter Grehan /* 91762a69c41SBryan Venteicher * For legacy and MSI interrupts, this single resource handles all 91862a69c41SBryan Venteicher * interrupts. For MSIX, this resource is used for the configuration 91962a69c41SBryan Venteicher * changed interrupt. 92010b59a9bSPeter Grehan */ 92162a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 92262a69c41SBryan Venteicher error = vtpci_alloc_interrupt(sc, rid, flags, intr); 92362a69c41SBryan Venteicher if (error || sc->vtpci_flags & (VTPCI_FLAG_LEGACY | VTPCI_FLAG_MSI)) 92462a69c41SBryan Venteicher return (error); 92510b59a9bSPeter Grehan 92662a69c41SBryan Venteicher /* Subtract one for the configuration changed interrupt. */ 92762a69c41SBryan Venteicher nvq_intrs = sc->vtpci_nmsix_resources - 1; 92862a69c41SBryan Venteicher 92962a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts = malloc(nvq_intrs * 93062a69c41SBryan Venteicher sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO); 93162a69c41SBryan Venteicher if (sc->vtpci_msix_vq_interrupts == NULL) 93262a69c41SBryan Venteicher return (ENOMEM); 93362a69c41SBryan Venteicher 93462a69c41SBryan Venteicher for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) { 93562a69c41SBryan Venteicher error = vtpci_alloc_interrupt(sc, rid, flags, intr); 93662a69c41SBryan Venteicher if (error) 93762a69c41SBryan Venteicher return (error); 93810b59a9bSPeter Grehan } 93910b59a9bSPeter Grehan 94010b59a9bSPeter Grehan return (0); 94110b59a9bSPeter Grehan } 94210b59a9bSPeter Grehan 94310b59a9bSPeter Grehan static int 944310dacd0SPeter Grehan vtpci_setup_legacy_interrupt(struct vtpci_softc *sc, enum intr_type type) 94510b59a9bSPeter Grehan { 94662a69c41SBryan Venteicher struct vtpci_interrupt *intr; 947310dacd0SPeter Grehan int error; 94810b59a9bSPeter Grehan 94962a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 95062a69c41SBryan Venteicher error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, NULL, 95162a69c41SBryan Venteicher vtpci_legacy_intr, sc, &intr->vti_handler); 95210b59a9bSPeter Grehan 953310dacd0SPeter Grehan return (error); 95410b59a9bSPeter Grehan } 95510b59a9bSPeter Grehan 95610b59a9bSPeter Grehan static int 95762a69c41SBryan Venteicher vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *sc, enum intr_type type) 95862a69c41SBryan Venteicher { 95962a69c41SBryan Venteicher struct vtpci_virtqueue *vqx; 96062a69c41SBryan Venteicher struct vtpci_interrupt *intr; 96162a69c41SBryan Venteicher int i, error; 96262a69c41SBryan Venteicher 96362a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 96462a69c41SBryan Venteicher 96562a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++) { 96662a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[i]; 96762a69c41SBryan Venteicher 96862a69c41SBryan Venteicher if (vqx->vtv_no_intr) 96962a69c41SBryan Venteicher continue; 97062a69c41SBryan Venteicher 97162a69c41SBryan Venteicher error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, 97262a69c41SBryan Venteicher vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq, 97362a69c41SBryan Venteicher &intr->vti_handler); 97462a69c41SBryan Venteicher if (error) 97562a69c41SBryan Venteicher return (error); 97662a69c41SBryan Venteicher 97762a69c41SBryan Venteicher intr++; 97862a69c41SBryan Venteicher } 97962a69c41SBryan Venteicher 98062a69c41SBryan Venteicher return (0); 98162a69c41SBryan Venteicher } 98262a69c41SBryan Venteicher 98362a69c41SBryan Venteicher static int 984310dacd0SPeter Grehan vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type) 98510b59a9bSPeter Grehan { 98610b59a9bSPeter Grehan device_t dev; 98762a69c41SBryan Venteicher struct vtpci_interrupt *intr; 98862a69c41SBryan Venteicher int error; 98910b59a9bSPeter Grehan 99010b59a9bSPeter Grehan dev = sc->vtpci_dev; 99162a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 99210b59a9bSPeter Grehan 99362a69c41SBryan Venteicher error = bus_setup_intr(dev, intr->vti_irq, type, NULL, 99462a69c41SBryan Venteicher vtpci_config_intr, sc, &intr->vti_handler); 995310dacd0SPeter Grehan if (error) 996310dacd0SPeter Grehan return (error); 99710b59a9bSPeter Grehan 998310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) { 99962a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 100062a69c41SBryan Venteicher error = bus_setup_intr(dev, intr->vti_irq, type, 10016632efe4SBryan Venteicher vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, sc, 100262a69c41SBryan Venteicher &intr->vti_handler); 100362a69c41SBryan Venteicher } else 100462a69c41SBryan Venteicher error = vtpci_setup_pervq_msix_interrupts(sc, type); 1005310dacd0SPeter Grehan 100662a69c41SBryan Venteicher return (error ? error : vtpci_set_host_msix_vectors(sc)); 100710b59a9bSPeter Grehan } 100810b59a9bSPeter Grehan 100910b59a9bSPeter Grehan static int 1010310dacd0SPeter Grehan vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type) 1011310dacd0SPeter Grehan { 1012310dacd0SPeter Grehan int error; 1013310dacd0SPeter Grehan 1014310dacd0SPeter Grehan type |= INTR_MPSAFE; 1015310dacd0SPeter Grehan KASSERT(sc->vtpci_flags & VTPCI_FLAG_ITYPE_MASK, 101662a69c41SBryan Venteicher ("%s: no interrupt type selected %#x", __func__, sc->vtpci_flags)); 1017310dacd0SPeter Grehan 1018310dacd0SPeter Grehan error = vtpci_alloc_intr_resources(sc); 1019310dacd0SPeter Grehan if (error) 1020310dacd0SPeter Grehan return (error); 1021310dacd0SPeter Grehan 1022310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 1023310dacd0SPeter Grehan error = vtpci_setup_legacy_interrupt(sc, type); 1024310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_MSI) 1025310dacd0SPeter Grehan error = vtpci_setup_msi_interrupt(sc, type); 1026310dacd0SPeter Grehan else 1027310dacd0SPeter Grehan error = vtpci_setup_msix_interrupts(sc, type); 1028310dacd0SPeter Grehan 1029310dacd0SPeter Grehan return (error); 1030310dacd0SPeter Grehan } 1031310dacd0SPeter Grehan 1032310dacd0SPeter Grehan static int 103362a69c41SBryan Venteicher vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, 103462a69c41SBryan Venteicher struct vtpci_interrupt *intr) 103510b59a9bSPeter Grehan { 103610b59a9bSPeter Grehan device_t dev; 103762a69c41SBryan Venteicher uint16_t vector; 103810b59a9bSPeter Grehan 103910b59a9bSPeter Grehan dev = sc->vtpci_dev; 104010b59a9bSPeter Grehan 104162a69c41SBryan Venteicher if (intr != NULL) { 1042310dacd0SPeter Grehan /* Map from guest rid to host vector. */ 104362a69c41SBryan Venteicher vector = intr->vti_rid - 1; 104410b59a9bSPeter Grehan } else 104510b59a9bSPeter Grehan vector = VIRTIO_MSI_NO_VECTOR; 104610b59a9bSPeter Grehan 104710b59a9bSPeter Grehan vtpci_write_config_2(sc, offset, vector); 104810b59a9bSPeter Grehan 1049310dacd0SPeter Grehan /* Read vector to determine if the host had sufficient resources. */ 105062a69c41SBryan Venteicher if (vtpci_read_config_2(sc, offset) != vector) { 1051310dacd0SPeter Grehan device_printf(dev, 1052310dacd0SPeter Grehan "insufficient host resources for MSIX interrupts\n"); 105310b59a9bSPeter Grehan return (ENODEV); 105410b59a9bSPeter Grehan } 105510b59a9bSPeter Grehan 105610b59a9bSPeter Grehan return (0); 105710b59a9bSPeter Grehan } 105810b59a9bSPeter Grehan 1059310dacd0SPeter Grehan static int 1060310dacd0SPeter Grehan vtpci_set_host_msix_vectors(struct vtpci_softc *sc) 1061310dacd0SPeter Grehan { 106262a69c41SBryan Venteicher struct vtpci_interrupt *intr, *tintr; 106362a69c41SBryan Venteicher int idx, offset, error; 1064310dacd0SPeter Grehan 106562a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 106662a69c41SBryan Venteicher offset = VIRTIO_MSI_CONFIG_VECTOR; 106762a69c41SBryan Venteicher 106862a69c41SBryan Venteicher error = vtpci_register_msix_vector(sc, offset, intr); 1069310dacd0SPeter Grehan if (error) 1070310dacd0SPeter Grehan return (error); 1071310dacd0SPeter Grehan 107262a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 107362a69c41SBryan Venteicher offset = VIRTIO_MSI_QUEUE_VECTOR; 107462a69c41SBryan Venteicher 1075310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 1076310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 107762a69c41SBryan Venteicher 107862a69c41SBryan Venteicher if (sc->vtpci_vqs[idx].vtv_no_intr) 107962a69c41SBryan Venteicher tintr = NULL; 108062a69c41SBryan Venteicher else 108162a69c41SBryan Venteicher tintr = intr; 108262a69c41SBryan Venteicher 108362a69c41SBryan Venteicher error = vtpci_register_msix_vector(sc, offset, tintr); 1084310dacd0SPeter Grehan if (error) 108562a69c41SBryan Venteicher break; 108662a69c41SBryan Venteicher 108762a69c41SBryan Venteicher /* 108862a69c41SBryan Venteicher * For shared MSIX, all the virtqueues share the first 108962a69c41SBryan Venteicher * interrupt. 109062a69c41SBryan Venteicher */ 109162a69c41SBryan Venteicher if ((sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0) 109262a69c41SBryan Venteicher intr++; 1093310dacd0SPeter Grehan } 1094310dacd0SPeter Grehan 109562a69c41SBryan Venteicher return (error); 1096310dacd0SPeter Grehan } 1097310dacd0SPeter Grehan 1098310dacd0SPeter Grehan static int 1099310dacd0SPeter Grehan vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx) 1100310dacd0SPeter Grehan { 1101310dacd0SPeter Grehan struct vtpci_virtqueue *vqx; 1102310dacd0SPeter Grehan struct virtqueue *vq; 1103310dacd0SPeter Grehan int error; 1104310dacd0SPeter Grehan uint16_t size; 1105310dacd0SPeter Grehan 110662a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 110762a69c41SBryan Venteicher vq = vqx->vtv_vq; 1108310dacd0SPeter Grehan 110962a69c41SBryan Venteicher KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx)); 1110310dacd0SPeter Grehan 1111310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 1112310dacd0SPeter Grehan size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 1113310dacd0SPeter Grehan 1114310dacd0SPeter Grehan error = virtqueue_reinit(vq, size); 1115310dacd0SPeter Grehan if (error) 1116310dacd0SPeter Grehan return (error); 1117310dacd0SPeter Grehan 1118310dacd0SPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 1119310dacd0SPeter Grehan virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 1120310dacd0SPeter Grehan 1121310dacd0SPeter Grehan return (0); 1122310dacd0SPeter Grehan } 1123310dacd0SPeter Grehan 112410b59a9bSPeter Grehan static void 112562a69c41SBryan Venteicher vtpci_free_interrupt(struct vtpci_softc *sc, struct vtpci_interrupt *intr) 112610b59a9bSPeter Grehan { 112710b59a9bSPeter Grehan device_t dev; 112810b59a9bSPeter Grehan 112910b59a9bSPeter Grehan dev = sc->vtpci_dev; 113010b59a9bSPeter Grehan 113162a69c41SBryan Venteicher if (intr->vti_handler != NULL) { 113262a69c41SBryan Venteicher bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler); 113362a69c41SBryan Venteicher intr->vti_handler = NULL; 113410b59a9bSPeter Grehan } 113510b59a9bSPeter Grehan 113662a69c41SBryan Venteicher if (intr->vti_irq != NULL) { 113762a69c41SBryan Venteicher bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid, 113862a69c41SBryan Venteicher intr->vti_irq); 113962a69c41SBryan Venteicher intr->vti_irq = NULL; 114062a69c41SBryan Venteicher intr->vti_rid = -1; 114162a69c41SBryan Venteicher } 114210b59a9bSPeter Grehan } 114310b59a9bSPeter Grehan 114462a69c41SBryan Venteicher static void 114562a69c41SBryan Venteicher vtpci_free_interrupts(struct vtpci_softc *sc) 114662a69c41SBryan Venteicher { 114762a69c41SBryan Venteicher struct vtpci_interrupt *intr; 114862a69c41SBryan Venteicher int i, nvq_intrs; 114962a69c41SBryan Venteicher 115062a69c41SBryan Venteicher vtpci_free_interrupt(sc, &sc->vtpci_device_interrupt); 115162a69c41SBryan Venteicher 115262a69c41SBryan Venteicher if (sc->vtpci_nmsix_resources != 0) { 115362a69c41SBryan Venteicher nvq_intrs = sc->vtpci_nmsix_resources - 1; 115462a69c41SBryan Venteicher sc->vtpci_nmsix_resources = 0; 115562a69c41SBryan Venteicher 115662a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 115762a69c41SBryan Venteicher if (intr != NULL) { 115862a69c41SBryan Venteicher for (i = 0; i < nvq_intrs; i++, intr++) 115962a69c41SBryan Venteicher vtpci_free_interrupt(sc, intr); 116062a69c41SBryan Venteicher 116162a69c41SBryan Venteicher free(sc->vtpci_msix_vq_interrupts, M_DEVBUF); 116262a69c41SBryan Venteicher sc->vtpci_msix_vq_interrupts = NULL; 116362a69c41SBryan Venteicher } 116410b59a9bSPeter Grehan } 1165310dacd0SPeter Grehan 1166310dacd0SPeter Grehan if (sc->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX)) 116762a69c41SBryan Venteicher pci_release_msi(sc->vtpci_dev); 1168310dacd0SPeter Grehan 1169310dacd0SPeter Grehan sc->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK; 117010b59a9bSPeter Grehan } 117110b59a9bSPeter Grehan 117210b59a9bSPeter Grehan static void 117310b59a9bSPeter Grehan vtpci_free_virtqueues(struct vtpci_softc *sc) 117410b59a9bSPeter Grehan { 117510b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 117662a69c41SBryan Venteicher int idx; 117710b59a9bSPeter Grehan 117862a69c41SBryan Venteicher for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 117962a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 118010b59a9bSPeter Grehan 118162a69c41SBryan Venteicher vtpci_select_virtqueue(sc, idx); 118262a69c41SBryan Venteicher vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 0); 118362a69c41SBryan Venteicher 118462a69c41SBryan Venteicher virtqueue_free(vqx->vtv_vq); 118562a69c41SBryan Venteicher vqx->vtv_vq = NULL; 118610b59a9bSPeter Grehan } 1187310dacd0SPeter Grehan 118862a69c41SBryan Venteicher free(sc->vtpci_vqs, M_DEVBUF); 118962a69c41SBryan Venteicher sc->vtpci_vqs = NULL; 1190310dacd0SPeter Grehan sc->vtpci_nvqs = 0; 119110b59a9bSPeter Grehan } 1192310dacd0SPeter Grehan 1193310dacd0SPeter Grehan static void 119462a69c41SBryan Venteicher vtpci_release_child_resources(struct vtpci_softc *sc) 119562a69c41SBryan Venteicher { 119662a69c41SBryan Venteicher 119762a69c41SBryan Venteicher vtpci_free_interrupts(sc); 119862a69c41SBryan Venteicher vtpci_free_virtqueues(sc); 119962a69c41SBryan Venteicher } 120062a69c41SBryan Venteicher 120162a69c41SBryan Venteicher static void 1202310dacd0SPeter Grehan vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc) 1203310dacd0SPeter Grehan { 1204310dacd0SPeter Grehan int idx; 1205310dacd0SPeter Grehan 1206310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_MSIX) { 1207310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_MSI_CONFIG_VECTOR, 1208310dacd0SPeter Grehan VIRTIO_MSI_NO_VECTOR); 1209310dacd0SPeter Grehan 1210310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 1211310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 1212310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_MSI_QUEUE_VECTOR, 1213310dacd0SPeter Grehan VIRTIO_MSI_NO_VECTOR); 1214310dacd0SPeter Grehan } 1215310dacd0SPeter Grehan } 1216310dacd0SPeter Grehan 1217310dacd0SPeter Grehan vtpci_free_interrupts(sc); 121810b59a9bSPeter Grehan } 121910b59a9bSPeter Grehan 122010b59a9bSPeter Grehan static void 122110b59a9bSPeter Grehan vtpci_reset(struct vtpci_softc *sc) 122210b59a9bSPeter Grehan { 122310b59a9bSPeter Grehan 122410b59a9bSPeter Grehan /* 122510b59a9bSPeter Grehan * Setting the status to RESET sets the host device to 122610b59a9bSPeter Grehan * the original, uninitialized state. 122710b59a9bSPeter Grehan */ 122810b59a9bSPeter Grehan vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET); 122910b59a9bSPeter Grehan } 123010b59a9bSPeter Grehan 1231310dacd0SPeter Grehan static void 1232310dacd0SPeter Grehan vtpci_select_virtqueue(struct vtpci_softc *sc, int idx) 1233310dacd0SPeter Grehan { 1234310dacd0SPeter Grehan 1235310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, idx); 1236310dacd0SPeter Grehan } 1237310dacd0SPeter Grehan 12386632efe4SBryan Venteicher static void 123910b59a9bSPeter Grehan vtpci_legacy_intr(void *xsc) 124010b59a9bSPeter Grehan { 124110b59a9bSPeter Grehan struct vtpci_softc *sc; 124210b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 124310b59a9bSPeter Grehan int i; 124410b59a9bSPeter Grehan uint8_t isr; 124510b59a9bSPeter Grehan 124610b59a9bSPeter Grehan sc = xsc; 124762a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 124810b59a9bSPeter Grehan 124910b59a9bSPeter Grehan /* Reading the ISR also clears it. */ 125010b59a9bSPeter Grehan isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR); 125110b59a9bSPeter Grehan 125210b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_CONFIG) 125310b59a9bSPeter Grehan vtpci_config_intr(sc); 125410b59a9bSPeter Grehan 12556632efe4SBryan Venteicher if (isr & VIRTIO_PCI_ISR_INTR) { 125662a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 125762a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 125862a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 125962a69c41SBryan Venteicher } 12606632efe4SBryan Venteicher } 126110b59a9bSPeter Grehan } 126210b59a9bSPeter Grehan 126310b59a9bSPeter Grehan static int 12646632efe4SBryan Venteicher vtpci_vq_shared_intr_filter(void *xsc) 126510b59a9bSPeter Grehan { 126610b59a9bSPeter Grehan struct vtpci_softc *sc; 126710b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 126810b59a9bSPeter Grehan int i, rc; 126910b59a9bSPeter Grehan 127010b59a9bSPeter Grehan rc = 0; 127110b59a9bSPeter Grehan sc = xsc; 127262a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 127310b59a9bSPeter Grehan 127462a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 127562a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 127662a69c41SBryan Venteicher rc |= virtqueue_intr_filter(vqx->vtv_vq); 127762a69c41SBryan Venteicher } 127810b59a9bSPeter Grehan 12796632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 12806632efe4SBryan Venteicher } 12816632efe4SBryan Venteicher 12826632efe4SBryan Venteicher static void 12836632efe4SBryan Venteicher vtpci_vq_shared_intr(void *xsc) 12846632efe4SBryan Venteicher { 12856632efe4SBryan Venteicher struct vtpci_softc *sc; 12866632efe4SBryan Venteicher struct vtpci_virtqueue *vqx; 12876632efe4SBryan Venteicher int i; 12886632efe4SBryan Venteicher 12896632efe4SBryan Venteicher sc = xsc; 129062a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 12916632efe4SBryan Venteicher 129262a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 129362a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 129462a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 129562a69c41SBryan Venteicher } 129610b59a9bSPeter Grehan } 129710b59a9bSPeter Grehan 129810b59a9bSPeter Grehan static int 12996632efe4SBryan Venteicher vtpci_vq_intr_filter(void *xvq) 130010b59a9bSPeter Grehan { 130110b59a9bSPeter Grehan struct virtqueue *vq; 130210b59a9bSPeter Grehan int rc; 130310b59a9bSPeter Grehan 130410b59a9bSPeter Grehan vq = xvq; 13056632efe4SBryan Venteicher rc = virtqueue_intr_filter(vq); 130610b59a9bSPeter Grehan 13076632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 130810b59a9bSPeter Grehan } 130910b59a9bSPeter Grehan 13106632efe4SBryan Venteicher static void 13116632efe4SBryan Venteicher vtpci_vq_intr(void *xvq) 13126632efe4SBryan Venteicher { 13136632efe4SBryan Venteicher struct virtqueue *vq; 13146632efe4SBryan Venteicher 13156632efe4SBryan Venteicher vq = xvq; 13166632efe4SBryan Venteicher virtqueue_intr(vq); 13176632efe4SBryan Venteicher } 13186632efe4SBryan Venteicher 13196632efe4SBryan Venteicher static void 132010b59a9bSPeter Grehan vtpci_config_intr(void *xsc) 132110b59a9bSPeter Grehan { 132210b59a9bSPeter Grehan struct vtpci_softc *sc; 132310b59a9bSPeter Grehan device_t child; 132410b59a9bSPeter Grehan 132510b59a9bSPeter Grehan sc = xsc; 132610b59a9bSPeter Grehan child = sc->vtpci_child_dev; 132710b59a9bSPeter Grehan 132810b59a9bSPeter Grehan if (child != NULL) 13296632efe4SBryan Venteicher VIRTIO_CONFIG_CHANGE(child); 133010b59a9bSPeter Grehan } 1331