110b59a9bSPeter Grehan /*- 2*718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*718cf2ccSPedro F. Giffuni * 4abd6790cSBryan Venteicher * Copyright (c) 2011, Bryan Venteicher <bryanv@FreeBSD.org> 510b59a9bSPeter Grehan * All rights reserved. 610b59a9bSPeter Grehan * 710b59a9bSPeter Grehan * Redistribution and use in source and binary forms, with or without 810b59a9bSPeter Grehan * modification, are permitted provided that the following conditions 910b59a9bSPeter Grehan * are met: 1010b59a9bSPeter Grehan * 1. Redistributions of source code must retain the above copyright 1110b59a9bSPeter Grehan * notice unmodified, this list of conditions, and the following 1210b59a9bSPeter Grehan * disclaimer. 1310b59a9bSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 1410b59a9bSPeter Grehan * notice, this list of conditions and the following disclaimer in the 1510b59a9bSPeter Grehan * documentation and/or other materials provided with the distribution. 1610b59a9bSPeter Grehan * 1710b59a9bSPeter Grehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1810b59a9bSPeter Grehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1910b59a9bSPeter Grehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2010b59a9bSPeter Grehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2110b59a9bSPeter Grehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2210b59a9bSPeter Grehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2310b59a9bSPeter Grehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2410b59a9bSPeter Grehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2510b59a9bSPeter Grehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2610b59a9bSPeter Grehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2710b59a9bSPeter Grehan */ 2810b59a9bSPeter Grehan 2910b59a9bSPeter Grehan /* Driver for the VirtIO PCI interface. */ 3010b59a9bSPeter Grehan 3110b59a9bSPeter Grehan #include <sys/cdefs.h> 3210b59a9bSPeter Grehan __FBSDID("$FreeBSD$"); 3310b59a9bSPeter Grehan 3410b59a9bSPeter Grehan #include <sys/param.h> 3510b59a9bSPeter Grehan #include <sys/systm.h> 3610b59a9bSPeter Grehan #include <sys/bus.h> 3710b59a9bSPeter Grehan #include <sys/kernel.h> 3810b59a9bSPeter Grehan #include <sys/module.h> 3910b59a9bSPeter Grehan #include <sys/malloc.h> 4010b59a9bSPeter Grehan 4110b59a9bSPeter Grehan #include <machine/bus.h> 4210b59a9bSPeter Grehan #include <machine/resource.h> 4310b59a9bSPeter Grehan #include <sys/bus.h> 4410b59a9bSPeter Grehan #include <sys/rman.h> 4510b59a9bSPeter Grehan 4610b59a9bSPeter Grehan #include <dev/pci/pcivar.h> 4710b59a9bSPeter Grehan #include <dev/pci/pcireg.h> 4810b59a9bSPeter Grehan 4910b59a9bSPeter Grehan #include <dev/virtio/virtio.h> 5010b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h> 5110b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h> 5210b59a9bSPeter Grehan 5310b59a9bSPeter Grehan #include "virtio_bus_if.h" 5410b59a9bSPeter Grehan #include "virtio_if.h" 5510b59a9bSPeter Grehan 5662a69c41SBryan Venteicher struct vtpci_interrupt { 5762a69c41SBryan Venteicher struct resource *vti_irq; 5862a69c41SBryan Venteicher int vti_rid; 5962a69c41SBryan Venteicher void *vti_handler; 6062a69c41SBryan Venteicher }; 6162a69c41SBryan Venteicher 6262a69c41SBryan Venteicher struct vtpci_virtqueue { 6362a69c41SBryan Venteicher struct virtqueue *vtv_vq; 6462a69c41SBryan Venteicher int vtv_no_intr; 6562a69c41SBryan Venteicher }; 6662a69c41SBryan Venteicher 6710b59a9bSPeter Grehan struct vtpci_softc { 6810b59a9bSPeter Grehan device_t vtpci_dev; 6910b59a9bSPeter Grehan struct resource *vtpci_res; 7010b59a9bSPeter Grehan struct resource *vtpci_msix_res; 7110b59a9bSPeter Grehan uint64_t vtpci_features; 7210b59a9bSPeter Grehan uint32_t vtpci_flags; 73310dacd0SPeter Grehan #define VTPCI_FLAG_NO_MSI 0x0001 74310dacd0SPeter Grehan #define VTPCI_FLAG_NO_MSIX 0x0002 75310dacd0SPeter Grehan #define VTPCI_FLAG_LEGACY 0x1000 76310dacd0SPeter Grehan #define VTPCI_FLAG_MSI 0x2000 77310dacd0SPeter Grehan #define VTPCI_FLAG_MSIX 0x4000 78310dacd0SPeter Grehan #define VTPCI_FLAG_SHARED_MSIX 0x8000 79310dacd0SPeter Grehan #define VTPCI_FLAG_ITYPE_MASK 0xF000 8010b59a9bSPeter Grehan 81310dacd0SPeter Grehan /* This "bus" will only ever have one child. */ 8210b59a9bSPeter Grehan device_t vtpci_child_dev; 8310b59a9bSPeter Grehan struct virtio_feature_desc *vtpci_child_feat_desc; 8410b59a9bSPeter Grehan 8510b59a9bSPeter Grehan int vtpci_nvqs; 8662a69c41SBryan Venteicher struct vtpci_virtqueue *vtpci_vqs; 8710b59a9bSPeter Grehan 8810b59a9bSPeter Grehan /* 8962a69c41SBryan Venteicher * Ideally, each virtqueue that the driver provides a callback for will 9062a69c41SBryan Venteicher * receive its own MSIX vector. If there are not sufficient vectors 9162a69c41SBryan Venteicher * available, then attempt to have all the VQs share one vector. For 9262a69c41SBryan Venteicher * MSIX, the configuration changed notifications must be on their own 9362a69c41SBryan Venteicher * vector. 9410b59a9bSPeter Grehan * 9562a69c41SBryan Venteicher * If MSIX is not available, we will attempt to have the whole device 9662a69c41SBryan Venteicher * share one MSI vector, and then, finally, one legacy interrupt. 9710b59a9bSPeter Grehan */ 9862a69c41SBryan Venteicher struct vtpci_interrupt vtpci_device_interrupt; 9962a69c41SBryan Venteicher struct vtpci_interrupt *vtpci_msix_vq_interrupts; 10062a69c41SBryan Venteicher int vtpci_nmsix_resources; 10110b59a9bSPeter Grehan }; 10210b59a9bSPeter Grehan 10310b59a9bSPeter Grehan static int vtpci_probe(device_t); 10410b59a9bSPeter Grehan static int vtpci_attach(device_t); 10510b59a9bSPeter Grehan static int vtpci_detach(device_t); 10610b59a9bSPeter Grehan static int vtpci_suspend(device_t); 10710b59a9bSPeter Grehan static int vtpci_resume(device_t); 10810b59a9bSPeter Grehan static int vtpci_shutdown(device_t); 10910b59a9bSPeter Grehan static void vtpci_driver_added(device_t, driver_t *); 11010b59a9bSPeter Grehan static void vtpci_child_detached(device_t, device_t); 11110b59a9bSPeter Grehan static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *); 11210b59a9bSPeter Grehan static int vtpci_write_ivar(device_t, device_t, int, uintptr_t); 11310b59a9bSPeter Grehan 11410b59a9bSPeter Grehan static uint64_t vtpci_negotiate_features(device_t, uint64_t); 11510b59a9bSPeter Grehan static int vtpci_with_feature(device_t, uint64_t); 11610b59a9bSPeter Grehan static int vtpci_alloc_virtqueues(device_t, int, int, 11710b59a9bSPeter Grehan struct vq_alloc_info *); 11810b59a9bSPeter Grehan static int vtpci_setup_intr(device_t, enum intr_type); 11910b59a9bSPeter Grehan static void vtpci_stop(device_t); 12010b59a9bSPeter Grehan static int vtpci_reinit(device_t, uint64_t); 12110b59a9bSPeter Grehan static void vtpci_reinit_complete(device_t); 12210b59a9bSPeter Grehan static void vtpci_notify_virtqueue(device_t, uint16_t); 12310b59a9bSPeter Grehan static uint8_t vtpci_get_status(device_t); 12410b59a9bSPeter Grehan static void vtpci_set_status(device_t, uint8_t); 12510b59a9bSPeter Grehan static void vtpci_read_dev_config(device_t, bus_size_t, void *, int); 12610b59a9bSPeter Grehan static void vtpci_write_dev_config(device_t, bus_size_t, void *, int); 12710b59a9bSPeter Grehan 12810b59a9bSPeter Grehan static void vtpci_describe_features(struct vtpci_softc *, const char *, 12910b59a9bSPeter Grehan uint64_t); 13010b59a9bSPeter Grehan static void vtpci_probe_and_attach_child(struct vtpci_softc *); 13110b59a9bSPeter Grehan 13210b59a9bSPeter Grehan static int vtpci_alloc_msix(struct vtpci_softc *, int); 133310dacd0SPeter Grehan static int vtpci_alloc_msi(struct vtpci_softc *); 134310dacd0SPeter Grehan static int vtpci_alloc_intr_msix_pervq(struct vtpci_softc *); 135310dacd0SPeter Grehan static int vtpci_alloc_intr_msix_shared(struct vtpci_softc *); 136310dacd0SPeter Grehan static int vtpci_alloc_intr_msi(struct vtpci_softc *); 137310dacd0SPeter Grehan static int vtpci_alloc_intr_legacy(struct vtpci_softc *); 13862a69c41SBryan Venteicher static int vtpci_alloc_interrupt(struct vtpci_softc *, int, int, 13962a69c41SBryan Venteicher struct vtpci_interrupt *); 140310dacd0SPeter Grehan static int vtpci_alloc_intr_resources(struct vtpci_softc *); 141310dacd0SPeter Grehan 142310dacd0SPeter Grehan static int vtpci_setup_legacy_interrupt(struct vtpci_softc *, 143310dacd0SPeter Grehan enum intr_type); 14462a69c41SBryan Venteicher static int vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *, 14562a69c41SBryan Venteicher enum intr_type); 146310dacd0SPeter Grehan static int vtpci_setup_msix_interrupts(struct vtpci_softc *, 147310dacd0SPeter Grehan enum intr_type); 148310dacd0SPeter Grehan static int vtpci_setup_interrupts(struct vtpci_softc *, enum intr_type); 149310dacd0SPeter Grehan 15062a69c41SBryan Venteicher static int vtpci_register_msix_vector(struct vtpci_softc *, int, 15162a69c41SBryan Venteicher struct vtpci_interrupt *); 152310dacd0SPeter Grehan static int vtpci_set_host_msix_vectors(struct vtpci_softc *); 153310dacd0SPeter Grehan static int vtpci_reinit_virtqueue(struct vtpci_softc *, int); 15410b59a9bSPeter Grehan 15562a69c41SBryan Venteicher static void vtpci_free_interrupt(struct vtpci_softc *, 15662a69c41SBryan Venteicher struct vtpci_interrupt *); 15710b59a9bSPeter Grehan static void vtpci_free_interrupts(struct vtpci_softc *); 15810b59a9bSPeter Grehan static void vtpci_free_virtqueues(struct vtpci_softc *); 15910b59a9bSPeter Grehan static void vtpci_release_child_resources(struct vtpci_softc *); 16062a69c41SBryan Venteicher static void vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *); 16110b59a9bSPeter Grehan static void vtpci_reset(struct vtpci_softc *); 16210b59a9bSPeter Grehan 163310dacd0SPeter Grehan static void vtpci_select_virtqueue(struct vtpci_softc *, int); 164310dacd0SPeter Grehan 1656632efe4SBryan Venteicher static void vtpci_legacy_intr(void *); 1666632efe4SBryan Venteicher static int vtpci_vq_shared_intr_filter(void *); 1676632efe4SBryan Venteicher static void vtpci_vq_shared_intr(void *); 1686632efe4SBryan Venteicher static int vtpci_vq_intr_filter(void *); 1696632efe4SBryan Venteicher static void vtpci_vq_intr(void *); 1706632efe4SBryan Venteicher static void vtpci_config_intr(void *); 17110b59a9bSPeter Grehan 172310dacd0SPeter Grehan #define vtpci_setup_msi_interrupt vtpci_setup_legacy_interrupt 173310dacd0SPeter Grehan 174e026de11SBryan Venteicher #define VIRTIO_PCI_CONFIG(_sc) \ 175e026de11SBryan Venteicher VIRTIO_PCI_CONFIG_OFF((((_sc)->vtpci_flags & VTPCI_FLAG_MSIX)) != 0) 176e026de11SBryan Venteicher 17710b59a9bSPeter Grehan /* 17810b59a9bSPeter Grehan * I/O port read/write wrappers. 17910b59a9bSPeter Grehan */ 18010b59a9bSPeter Grehan #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o)) 18110b59a9bSPeter Grehan #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o)) 18210b59a9bSPeter Grehan #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o)) 18310b59a9bSPeter Grehan #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v)) 18410b59a9bSPeter Grehan #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v)) 18510b59a9bSPeter Grehan #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v)) 18610b59a9bSPeter Grehan 18710b59a9bSPeter Grehan /* Tunables. */ 18810b59a9bSPeter Grehan static int vtpci_disable_msix = 0; 18910b59a9bSPeter Grehan TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 19010b59a9bSPeter Grehan 19110b59a9bSPeter Grehan static device_method_t vtpci_methods[] = { 19210b59a9bSPeter Grehan /* Device interface. */ 19310b59a9bSPeter Grehan DEVMETHOD(device_probe, vtpci_probe), 19410b59a9bSPeter Grehan DEVMETHOD(device_attach, vtpci_attach), 19510b59a9bSPeter Grehan DEVMETHOD(device_detach, vtpci_detach), 19610b59a9bSPeter Grehan DEVMETHOD(device_suspend, vtpci_suspend), 19710b59a9bSPeter Grehan DEVMETHOD(device_resume, vtpci_resume), 19810b59a9bSPeter Grehan DEVMETHOD(device_shutdown, vtpci_shutdown), 19910b59a9bSPeter Grehan 20010b59a9bSPeter Grehan /* Bus interface. */ 20110b59a9bSPeter Grehan DEVMETHOD(bus_driver_added, vtpci_driver_added), 20210b59a9bSPeter Grehan DEVMETHOD(bus_child_detached, vtpci_child_detached), 20310b59a9bSPeter Grehan DEVMETHOD(bus_read_ivar, vtpci_read_ivar), 20410b59a9bSPeter Grehan DEVMETHOD(bus_write_ivar, vtpci_write_ivar), 20510b59a9bSPeter Grehan 20610b59a9bSPeter Grehan /* VirtIO bus interface. */ 20710b59a9bSPeter Grehan DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features), 20810b59a9bSPeter Grehan DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature), 20910b59a9bSPeter Grehan DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues), 21010b59a9bSPeter Grehan DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr), 21110b59a9bSPeter Grehan DEVMETHOD(virtio_bus_stop, vtpci_stop), 21210b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit, vtpci_reinit), 21310b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete), 21410b59a9bSPeter Grehan DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), 21510b59a9bSPeter Grehan DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), 21610b59a9bSPeter Grehan DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), 21710b59a9bSPeter Grehan 218b8a58707SPeter Grehan DEVMETHOD_END 21910b59a9bSPeter Grehan }; 22010b59a9bSPeter Grehan 22110b59a9bSPeter Grehan static driver_t vtpci_driver = { 22210b59a9bSPeter Grehan "virtio_pci", 22310b59a9bSPeter Grehan vtpci_methods, 22410b59a9bSPeter Grehan sizeof(struct vtpci_softc) 22510b59a9bSPeter Grehan }; 22610b59a9bSPeter Grehan 22710b59a9bSPeter Grehan devclass_t vtpci_devclass; 22810b59a9bSPeter Grehan 22910b59a9bSPeter Grehan DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0); 23010b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1); 23110b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 23210b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 23310b59a9bSPeter Grehan 23410b59a9bSPeter Grehan static int 23510b59a9bSPeter Grehan vtpci_probe(device_t dev) 23610b59a9bSPeter Grehan { 23710b59a9bSPeter Grehan char desc[36]; 23810b59a9bSPeter Grehan const char *name; 23910b59a9bSPeter Grehan 24010b59a9bSPeter Grehan if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID) 24110b59a9bSPeter Grehan return (ENXIO); 24210b59a9bSPeter Grehan 24310b59a9bSPeter Grehan if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN || 24410b59a9bSPeter Grehan pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX) 24510b59a9bSPeter Grehan return (ENXIO); 24610b59a9bSPeter Grehan 24710b59a9bSPeter Grehan if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION) 24810b59a9bSPeter Grehan return (ENXIO); 24910b59a9bSPeter Grehan 25010b59a9bSPeter Grehan name = virtio_device_name(pci_get_subdevice(dev)); 25110b59a9bSPeter Grehan if (name == NULL) 25210b59a9bSPeter Grehan name = "Unknown"; 25310b59a9bSPeter Grehan 25410b59a9bSPeter Grehan snprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name); 25510b59a9bSPeter Grehan device_set_desc_copy(dev, desc); 25610b59a9bSPeter Grehan 25710b59a9bSPeter Grehan return (BUS_PROBE_DEFAULT); 25810b59a9bSPeter Grehan } 25910b59a9bSPeter Grehan 26010b59a9bSPeter Grehan static int 26110b59a9bSPeter Grehan vtpci_attach(device_t dev) 26210b59a9bSPeter Grehan { 26310b59a9bSPeter Grehan struct vtpci_softc *sc; 26410b59a9bSPeter Grehan device_t child; 26510b59a9bSPeter Grehan int rid; 26610b59a9bSPeter Grehan 26710b59a9bSPeter Grehan sc = device_get_softc(dev); 26810b59a9bSPeter Grehan sc->vtpci_dev = dev; 26910b59a9bSPeter Grehan 27010b59a9bSPeter Grehan pci_enable_busmaster(dev); 27110b59a9bSPeter Grehan 27210b59a9bSPeter Grehan rid = PCIR_BAR(0); 27310b59a9bSPeter Grehan sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 27410b59a9bSPeter Grehan RF_ACTIVE); 27510b59a9bSPeter Grehan if (sc->vtpci_res == NULL) { 27610b59a9bSPeter Grehan device_printf(dev, "cannot map I/O space\n"); 27710b59a9bSPeter Grehan return (ENXIO); 27810b59a9bSPeter Grehan } 27910b59a9bSPeter Grehan 280cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSI, NULL) != 0) 281310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_NO_MSI; 28210b59a9bSPeter Grehan 283cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSIX, NULL) == 0) { 28410b59a9bSPeter Grehan rid = PCIR_BAR(1); 28510b59a9bSPeter Grehan sc->vtpci_msix_res = bus_alloc_resource_any(dev, 28610b59a9bSPeter Grehan SYS_RES_MEMORY, &rid, RF_ACTIVE); 28710b59a9bSPeter Grehan } 28810b59a9bSPeter Grehan 28910b59a9bSPeter Grehan if (sc->vtpci_msix_res == NULL) 290310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_NO_MSIX; 29110b59a9bSPeter Grehan 29210b59a9bSPeter Grehan vtpci_reset(sc); 29310b59a9bSPeter Grehan 29410b59a9bSPeter Grehan /* Tell the host we've noticed this device. */ 29510b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 29610b59a9bSPeter Grehan 29710b59a9bSPeter Grehan if ((child = device_add_child(dev, NULL, -1)) == NULL) { 29810b59a9bSPeter Grehan device_printf(dev, "cannot create child device\n"); 29910b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 30010b59a9bSPeter Grehan vtpci_detach(dev); 30110b59a9bSPeter Grehan return (ENOMEM); 30210b59a9bSPeter Grehan } 30310b59a9bSPeter Grehan 30410b59a9bSPeter Grehan sc->vtpci_child_dev = child; 30510b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 30610b59a9bSPeter Grehan 30710b59a9bSPeter Grehan return (0); 30810b59a9bSPeter Grehan } 30910b59a9bSPeter Grehan 31010b59a9bSPeter Grehan static int 31110b59a9bSPeter Grehan vtpci_detach(device_t dev) 31210b59a9bSPeter Grehan { 31310b59a9bSPeter Grehan struct vtpci_softc *sc; 31410b59a9bSPeter Grehan device_t child; 31510b59a9bSPeter Grehan int error; 31610b59a9bSPeter Grehan 31710b59a9bSPeter Grehan sc = device_get_softc(dev); 31810b59a9bSPeter Grehan 31910b59a9bSPeter Grehan if ((child = sc->vtpci_child_dev) != NULL) { 32010b59a9bSPeter Grehan error = device_delete_child(dev, child); 32110b59a9bSPeter Grehan if (error) 32210b59a9bSPeter Grehan return (error); 32310b59a9bSPeter Grehan sc->vtpci_child_dev = NULL; 32410b59a9bSPeter Grehan } 32510b59a9bSPeter Grehan 32610b59a9bSPeter Grehan vtpci_reset(sc); 32710b59a9bSPeter Grehan 32810b59a9bSPeter Grehan if (sc->vtpci_msix_res != NULL) { 32910b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), 33010b59a9bSPeter Grehan sc->vtpci_msix_res); 33110b59a9bSPeter Grehan sc->vtpci_msix_res = NULL; 33210b59a9bSPeter Grehan } 33310b59a9bSPeter Grehan 33410b59a9bSPeter Grehan if (sc->vtpci_res != NULL) { 33510b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), 33610b59a9bSPeter Grehan sc->vtpci_res); 33710b59a9bSPeter Grehan sc->vtpci_res = NULL; 33810b59a9bSPeter Grehan } 33910b59a9bSPeter Grehan 34010b59a9bSPeter Grehan return (0); 34110b59a9bSPeter Grehan } 34210b59a9bSPeter Grehan 34310b59a9bSPeter Grehan static int 34410b59a9bSPeter Grehan vtpci_suspend(device_t dev) 34510b59a9bSPeter Grehan { 34610b59a9bSPeter Grehan 34710b59a9bSPeter Grehan return (bus_generic_suspend(dev)); 34810b59a9bSPeter Grehan } 34910b59a9bSPeter Grehan 35010b59a9bSPeter Grehan static int 35110b59a9bSPeter Grehan vtpci_resume(device_t dev) 35210b59a9bSPeter Grehan { 35310b59a9bSPeter Grehan 35410b59a9bSPeter Grehan return (bus_generic_resume(dev)); 35510b59a9bSPeter Grehan } 35610b59a9bSPeter Grehan 35710b59a9bSPeter Grehan static int 35810b59a9bSPeter Grehan vtpci_shutdown(device_t dev) 35910b59a9bSPeter Grehan { 36010b59a9bSPeter Grehan 36110b59a9bSPeter Grehan (void) bus_generic_shutdown(dev); 36210b59a9bSPeter Grehan /* Forcibly stop the host device. */ 36310b59a9bSPeter Grehan vtpci_stop(dev); 36410b59a9bSPeter Grehan 36510b59a9bSPeter Grehan return (0); 36610b59a9bSPeter Grehan } 36710b59a9bSPeter Grehan 36810b59a9bSPeter Grehan static void 36910b59a9bSPeter Grehan vtpci_driver_added(device_t dev, driver_t *driver) 37010b59a9bSPeter Grehan { 37110b59a9bSPeter Grehan struct vtpci_softc *sc; 37210b59a9bSPeter Grehan 37310b59a9bSPeter Grehan sc = device_get_softc(dev); 37410b59a9bSPeter Grehan 37510b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 37610b59a9bSPeter Grehan } 37710b59a9bSPeter Grehan 37810b59a9bSPeter Grehan static void 37910b59a9bSPeter Grehan vtpci_child_detached(device_t dev, device_t child) 38010b59a9bSPeter Grehan { 38110b59a9bSPeter Grehan struct vtpci_softc *sc; 38210b59a9bSPeter Grehan 38310b59a9bSPeter Grehan sc = device_get_softc(dev); 38410b59a9bSPeter Grehan 38510b59a9bSPeter Grehan vtpci_reset(sc); 38610b59a9bSPeter Grehan vtpci_release_child_resources(sc); 38710b59a9bSPeter Grehan } 38810b59a9bSPeter Grehan 38910b59a9bSPeter Grehan static int 39010b59a9bSPeter Grehan vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 39110b59a9bSPeter Grehan { 39210b59a9bSPeter Grehan struct vtpci_softc *sc; 39310b59a9bSPeter Grehan 39410b59a9bSPeter Grehan sc = device_get_softc(dev); 39510b59a9bSPeter Grehan 39610b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 39710b59a9bSPeter Grehan return (ENOENT); 39810b59a9bSPeter Grehan 39910b59a9bSPeter Grehan switch (index) { 40010b59a9bSPeter Grehan case VIRTIO_IVAR_DEVTYPE: 401310dacd0SPeter Grehan case VIRTIO_IVAR_SUBDEVICE: 402310dacd0SPeter Grehan *result = pci_get_subdevice(dev); 403310dacd0SPeter Grehan break; 404310dacd0SPeter Grehan case VIRTIO_IVAR_VENDOR: 405310dacd0SPeter Grehan *result = pci_get_vendor(dev); 406310dacd0SPeter Grehan break; 407310dacd0SPeter Grehan case VIRTIO_IVAR_DEVICE: 408310dacd0SPeter Grehan *result = pci_get_device(dev); 409310dacd0SPeter Grehan break; 410310dacd0SPeter Grehan case VIRTIO_IVAR_SUBVENDOR: 41110b59a9bSPeter Grehan *result = pci_get_subdevice(dev); 41210b59a9bSPeter Grehan break; 41310b59a9bSPeter Grehan default: 41410b59a9bSPeter Grehan return (ENOENT); 41510b59a9bSPeter Grehan } 41610b59a9bSPeter Grehan 41710b59a9bSPeter Grehan return (0); 41810b59a9bSPeter Grehan } 41910b59a9bSPeter Grehan 42010b59a9bSPeter Grehan static int 42110b59a9bSPeter Grehan vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 42210b59a9bSPeter Grehan { 42310b59a9bSPeter Grehan struct vtpci_softc *sc; 42410b59a9bSPeter Grehan 42510b59a9bSPeter Grehan sc = device_get_softc(dev); 42610b59a9bSPeter Grehan 42710b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 42810b59a9bSPeter Grehan return (ENOENT); 42910b59a9bSPeter Grehan 43010b59a9bSPeter Grehan switch (index) { 43110b59a9bSPeter Grehan case VIRTIO_IVAR_FEATURE_DESC: 43210b59a9bSPeter Grehan sc->vtpci_child_feat_desc = (void *) value; 43310b59a9bSPeter Grehan break; 43410b59a9bSPeter Grehan default: 43510b59a9bSPeter Grehan return (ENOENT); 43610b59a9bSPeter Grehan } 43710b59a9bSPeter Grehan 43810b59a9bSPeter Grehan return (0); 43910b59a9bSPeter Grehan } 44010b59a9bSPeter Grehan 44110b59a9bSPeter Grehan static uint64_t 44210b59a9bSPeter Grehan vtpci_negotiate_features(device_t dev, uint64_t child_features) 44310b59a9bSPeter Grehan { 44410b59a9bSPeter Grehan struct vtpci_softc *sc; 44510b59a9bSPeter Grehan uint64_t host_features, features; 44610b59a9bSPeter Grehan 44710b59a9bSPeter Grehan sc = device_get_softc(dev); 44810b59a9bSPeter Grehan 44910b59a9bSPeter Grehan host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES); 45010b59a9bSPeter Grehan vtpci_describe_features(sc, "host", host_features); 45110b59a9bSPeter Grehan 45210b59a9bSPeter Grehan /* 45310b59a9bSPeter Grehan * Limit negotiated features to what the driver, virtqueue, and 45410b59a9bSPeter Grehan * host all support. 45510b59a9bSPeter Grehan */ 45610b59a9bSPeter Grehan features = host_features & child_features; 45710b59a9bSPeter Grehan features = virtqueue_filter_features(features); 45810b59a9bSPeter Grehan sc->vtpci_features = features; 45910b59a9bSPeter Grehan 46010b59a9bSPeter Grehan vtpci_describe_features(sc, "negotiated", features); 46110b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features); 46210b59a9bSPeter Grehan 46310b59a9bSPeter Grehan return (features); 46410b59a9bSPeter Grehan } 46510b59a9bSPeter Grehan 46610b59a9bSPeter Grehan static int 46710b59a9bSPeter Grehan vtpci_with_feature(device_t dev, uint64_t feature) 46810b59a9bSPeter Grehan { 46910b59a9bSPeter Grehan struct vtpci_softc *sc; 47010b59a9bSPeter Grehan 47110b59a9bSPeter Grehan sc = device_get_softc(dev); 47210b59a9bSPeter Grehan 47310b59a9bSPeter Grehan return ((sc->vtpci_features & feature) != 0); 47410b59a9bSPeter Grehan } 47510b59a9bSPeter Grehan 47610b59a9bSPeter Grehan static int 47710b59a9bSPeter Grehan vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, 47810b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 47910b59a9bSPeter Grehan { 48010b59a9bSPeter Grehan struct vtpci_softc *sc; 481310dacd0SPeter Grehan struct virtqueue *vq; 48210b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 48310b59a9bSPeter Grehan struct vq_alloc_info *info; 484310dacd0SPeter Grehan int idx, error; 485310dacd0SPeter Grehan uint16_t size; 48610b59a9bSPeter Grehan 48710b59a9bSPeter Grehan sc = device_get_softc(dev); 48810b59a9bSPeter Grehan 489310dacd0SPeter Grehan if (sc->vtpci_nvqs != 0) 490310dacd0SPeter Grehan return (EALREADY); 49162a69c41SBryan Venteicher if (nvqs <= 0) 49210b59a9bSPeter Grehan return (EINVAL); 49310b59a9bSPeter Grehan 49462a69c41SBryan Venteicher sc->vtpci_vqs = malloc(nvqs * sizeof(struct vtpci_virtqueue), 49562a69c41SBryan Venteicher M_DEVBUF, M_NOWAIT | M_ZERO); 49662a69c41SBryan Venteicher if (sc->vtpci_vqs == NULL) 49762a69c41SBryan Venteicher return (ENOMEM); 49862a69c41SBryan Venteicher 499310dacd0SPeter Grehan for (idx = 0; idx < nvqs; idx++) { 50062a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 501310dacd0SPeter Grehan info = &vq_info[idx]; 502310dacd0SPeter Grehan 503310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 504310dacd0SPeter Grehan size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 505310dacd0SPeter Grehan 506310dacd0SPeter Grehan error = virtqueue_alloc(dev, idx, size, VIRTIO_PCI_VRING_ALIGN, 507310dacd0SPeter Grehan 0xFFFFFFFFUL, info, &vq); 50810b59a9bSPeter Grehan if (error) { 509310dacd0SPeter Grehan device_printf(dev, 510310dacd0SPeter Grehan "cannot allocate virtqueue %d: %d\n", idx, error); 511310dacd0SPeter Grehan break; 51210b59a9bSPeter Grehan } 51310b59a9bSPeter Grehan 51410b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 515310dacd0SPeter Grehan virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 51610b59a9bSPeter Grehan 51762a69c41SBryan Venteicher vqx->vtv_vq = *info->vqai_vq = vq; 51862a69c41SBryan Venteicher vqx->vtv_no_intr = info->vqai_intr == NULL; 519310dacd0SPeter Grehan 52010b59a9bSPeter Grehan sc->vtpci_nvqs++; 52110b59a9bSPeter Grehan } 52210b59a9bSPeter Grehan 52362a69c41SBryan Venteicher if (error) 52462a69c41SBryan Venteicher vtpci_free_virtqueues(sc); 52562a69c41SBryan Venteicher 526310dacd0SPeter Grehan return (error); 52710b59a9bSPeter Grehan } 52810b59a9bSPeter Grehan 52910b59a9bSPeter Grehan static int 53010b59a9bSPeter Grehan vtpci_setup_intr(device_t dev, enum intr_type type) 53110b59a9bSPeter Grehan { 53210b59a9bSPeter Grehan struct vtpci_softc *sc; 533310dacd0SPeter Grehan int attempt, error; 53410b59a9bSPeter Grehan 53510b59a9bSPeter Grehan sc = device_get_softc(dev); 53610b59a9bSPeter Grehan 537310dacd0SPeter Grehan for (attempt = 0; attempt < 5; attempt++) { 538310dacd0SPeter Grehan /* 539310dacd0SPeter Grehan * Start with the most desirable interrupt configuration and 540310dacd0SPeter Grehan * fallback towards less desirable ones. 541310dacd0SPeter Grehan */ 542310dacd0SPeter Grehan switch (attempt) { 543310dacd0SPeter Grehan case 0: 544310dacd0SPeter Grehan error = vtpci_alloc_intr_msix_pervq(sc); 545310dacd0SPeter Grehan break; 546310dacd0SPeter Grehan case 1: 547310dacd0SPeter Grehan error = vtpci_alloc_intr_msix_shared(sc); 548310dacd0SPeter Grehan break; 549310dacd0SPeter Grehan case 2: 550310dacd0SPeter Grehan error = vtpci_alloc_intr_msi(sc); 551310dacd0SPeter Grehan break; 552310dacd0SPeter Grehan case 3: 553310dacd0SPeter Grehan error = vtpci_alloc_intr_legacy(sc); 554310dacd0SPeter Grehan break; 555310dacd0SPeter Grehan default: 556310dacd0SPeter Grehan device_printf(dev, 557310dacd0SPeter Grehan "exhausted all interrupt allocation attempts\n"); 558310dacd0SPeter Grehan return (ENXIO); 55910b59a9bSPeter Grehan } 56010b59a9bSPeter Grehan 561310dacd0SPeter Grehan if (error == 0 && vtpci_setup_interrupts(sc, type) == 0) 562310dacd0SPeter Grehan break; 56310b59a9bSPeter Grehan 564310dacd0SPeter Grehan vtpci_cleanup_setup_intr_attempt(sc); 56510b59a9bSPeter Grehan } 56610b59a9bSPeter Grehan 567310dacd0SPeter Grehan if (bootverbose) { 568310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 569310dacd0SPeter Grehan device_printf(dev, "using legacy interrupt\n"); 570310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_MSI) 571310dacd0SPeter Grehan device_printf(dev, "using MSI interrupt\n"); 572310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) 573310dacd0SPeter Grehan device_printf(dev, "using shared MSIX interrupts\n"); 574310dacd0SPeter Grehan else 575310dacd0SPeter Grehan device_printf(dev, "using per VQ MSIX interrupts\n"); 57610b59a9bSPeter Grehan } 57710b59a9bSPeter Grehan 57810b59a9bSPeter Grehan return (0); 57910b59a9bSPeter Grehan } 58010b59a9bSPeter Grehan 58110b59a9bSPeter Grehan static void 58210b59a9bSPeter Grehan vtpci_stop(device_t dev) 58310b59a9bSPeter Grehan { 58410b59a9bSPeter Grehan 58510b59a9bSPeter Grehan vtpci_reset(device_get_softc(dev)); 58610b59a9bSPeter Grehan } 58710b59a9bSPeter Grehan 58810b59a9bSPeter Grehan static int 58910b59a9bSPeter Grehan vtpci_reinit(device_t dev, uint64_t features) 59010b59a9bSPeter Grehan { 59110b59a9bSPeter Grehan struct vtpci_softc *sc; 592310dacd0SPeter Grehan int idx, error; 59310b59a9bSPeter Grehan 59410b59a9bSPeter Grehan sc = device_get_softc(dev); 59510b59a9bSPeter Grehan 59610b59a9bSPeter Grehan /* 597310dacd0SPeter Grehan * Redrive the device initialization. This is a bit of an abuse of 598310dacd0SPeter Grehan * the specification, but VirtualBox, QEMU/KVM, and BHyVe seem to 599310dacd0SPeter Grehan * play nice. 600310dacd0SPeter Grehan * 601310dacd0SPeter Grehan * We do not allow the host device to change from what was originally 602310dacd0SPeter Grehan * negotiated beyond what the guest driver changed. MSIX state should 603310dacd0SPeter Grehan * not change, number of virtqueues and their size remain the same, etc. 604310dacd0SPeter Grehan * This will need to be rethought when we want to support migration. 60510b59a9bSPeter Grehan */ 60610b59a9bSPeter Grehan 60710b59a9bSPeter Grehan if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 60810b59a9bSPeter Grehan vtpci_stop(dev); 60910b59a9bSPeter Grehan 61010b59a9bSPeter Grehan /* 61110b59a9bSPeter Grehan * Quickly drive the status through ACK and DRIVER. The device 61210b59a9bSPeter Grehan * does not become usable again until vtpci_reinit_complete(). 61310b59a9bSPeter Grehan */ 61410b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 61510b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 61610b59a9bSPeter Grehan 61710b59a9bSPeter Grehan vtpci_negotiate_features(dev, features); 61810b59a9bSPeter Grehan 619310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 620310dacd0SPeter Grehan error = vtpci_reinit_virtqueue(sc, idx); 62110b59a9bSPeter Grehan if (error) 62210b59a9bSPeter Grehan return (error); 62310b59a9bSPeter Grehan } 62410b59a9bSPeter Grehan 625310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_MSIX) { 626310dacd0SPeter Grehan error = vtpci_set_host_msix_vectors(sc); 62710b59a9bSPeter Grehan if (error) 62810b59a9bSPeter Grehan return (error); 62910b59a9bSPeter Grehan } 63010b59a9bSPeter Grehan 63110b59a9bSPeter Grehan return (0); 63210b59a9bSPeter Grehan } 63310b59a9bSPeter Grehan 63410b59a9bSPeter Grehan static void 63510b59a9bSPeter Grehan vtpci_reinit_complete(device_t dev) 63610b59a9bSPeter Grehan { 63710b59a9bSPeter Grehan 63810b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 63910b59a9bSPeter Grehan } 64010b59a9bSPeter Grehan 64110b59a9bSPeter Grehan static void 64210b59a9bSPeter Grehan vtpci_notify_virtqueue(device_t dev, uint16_t queue) 64310b59a9bSPeter Grehan { 64410b59a9bSPeter Grehan struct vtpci_softc *sc; 64510b59a9bSPeter Grehan 64610b59a9bSPeter Grehan sc = device_get_softc(dev); 64710b59a9bSPeter Grehan 64810b59a9bSPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue); 64910b59a9bSPeter Grehan } 65010b59a9bSPeter Grehan 65110b59a9bSPeter Grehan static uint8_t 65210b59a9bSPeter Grehan vtpci_get_status(device_t dev) 65310b59a9bSPeter Grehan { 65410b59a9bSPeter Grehan struct vtpci_softc *sc; 65510b59a9bSPeter Grehan 65610b59a9bSPeter Grehan sc = device_get_softc(dev); 65710b59a9bSPeter Grehan 65810b59a9bSPeter Grehan return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS)); 65910b59a9bSPeter Grehan } 66010b59a9bSPeter Grehan 66110b59a9bSPeter Grehan static void 66210b59a9bSPeter Grehan vtpci_set_status(device_t dev, uint8_t status) 66310b59a9bSPeter Grehan { 66410b59a9bSPeter Grehan struct vtpci_softc *sc; 66510b59a9bSPeter Grehan 66610b59a9bSPeter Grehan sc = device_get_softc(dev); 66710b59a9bSPeter Grehan 66810b59a9bSPeter Grehan if (status != VIRTIO_CONFIG_STATUS_RESET) 66910b59a9bSPeter Grehan status |= vtpci_get_status(dev); 67010b59a9bSPeter Grehan 67110b59a9bSPeter Grehan vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status); 67210b59a9bSPeter Grehan } 67310b59a9bSPeter Grehan 67410b59a9bSPeter Grehan static void 67510b59a9bSPeter Grehan vtpci_read_dev_config(device_t dev, bus_size_t offset, 67610b59a9bSPeter Grehan void *dst, int length) 67710b59a9bSPeter Grehan { 67810b59a9bSPeter Grehan struct vtpci_softc *sc; 67910b59a9bSPeter Grehan bus_size_t off; 68010b59a9bSPeter Grehan uint8_t *d; 68110b59a9bSPeter Grehan int size; 68210b59a9bSPeter Grehan 68310b59a9bSPeter Grehan sc = device_get_softc(dev); 68410b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 68510b59a9bSPeter Grehan 68610b59a9bSPeter Grehan for (d = dst; length > 0; d += size, off += size, length -= size) { 68710b59a9bSPeter Grehan if (length >= 4) { 68810b59a9bSPeter Grehan size = 4; 68910b59a9bSPeter Grehan *(uint32_t *)d = vtpci_read_config_4(sc, off); 69010b59a9bSPeter Grehan } else if (length >= 2) { 69110b59a9bSPeter Grehan size = 2; 69210b59a9bSPeter Grehan *(uint16_t *)d = vtpci_read_config_2(sc, off); 69310b59a9bSPeter Grehan } else { 69410b59a9bSPeter Grehan size = 1; 69510b59a9bSPeter Grehan *d = vtpci_read_config_1(sc, off); 69610b59a9bSPeter Grehan } 69710b59a9bSPeter Grehan } 69810b59a9bSPeter Grehan } 69910b59a9bSPeter Grehan 70010b59a9bSPeter Grehan static void 70110b59a9bSPeter Grehan vtpci_write_dev_config(device_t dev, bus_size_t offset, 70210b59a9bSPeter Grehan void *src, int length) 70310b59a9bSPeter Grehan { 70410b59a9bSPeter Grehan struct vtpci_softc *sc; 70510b59a9bSPeter Grehan bus_size_t off; 70610b59a9bSPeter Grehan uint8_t *s; 70710b59a9bSPeter Grehan int size; 70810b59a9bSPeter Grehan 70910b59a9bSPeter Grehan sc = device_get_softc(dev); 71010b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 71110b59a9bSPeter Grehan 71210b59a9bSPeter Grehan for (s = src; length > 0; s += size, off += size, length -= size) { 71310b59a9bSPeter Grehan if (length >= 4) { 71410b59a9bSPeter Grehan size = 4; 71510b59a9bSPeter Grehan vtpci_write_config_4(sc, off, *(uint32_t *)s); 71610b59a9bSPeter Grehan } else if (length >= 2) { 71710b59a9bSPeter Grehan size = 2; 71810b59a9bSPeter Grehan vtpci_write_config_2(sc, off, *(uint16_t *)s); 71910b59a9bSPeter Grehan } else { 72010b59a9bSPeter Grehan size = 1; 72110b59a9bSPeter Grehan vtpci_write_config_1(sc, off, *s); 72210b59a9bSPeter Grehan } 72310b59a9bSPeter Grehan } 72410b59a9bSPeter Grehan } 72510b59a9bSPeter Grehan 72610b59a9bSPeter Grehan static void 72710b59a9bSPeter Grehan vtpci_describe_features(struct vtpci_softc *sc, const char *msg, 72810b59a9bSPeter Grehan uint64_t features) 72910b59a9bSPeter Grehan { 73010b59a9bSPeter Grehan device_t dev, child; 73110b59a9bSPeter Grehan 73210b59a9bSPeter Grehan dev = sc->vtpci_dev; 73310b59a9bSPeter Grehan child = sc->vtpci_child_dev; 73410b59a9bSPeter Grehan 73561cefb9bSAlexander Motin if (device_is_attached(child) || bootverbose == 0) 73610b59a9bSPeter Grehan return; 73710b59a9bSPeter Grehan 73810b59a9bSPeter Grehan virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc); 73910b59a9bSPeter Grehan } 74010b59a9bSPeter Grehan 74110b59a9bSPeter Grehan static void 74210b59a9bSPeter Grehan vtpci_probe_and_attach_child(struct vtpci_softc *sc) 74310b59a9bSPeter Grehan { 74410b59a9bSPeter Grehan device_t dev, child; 74510b59a9bSPeter Grehan 74610b59a9bSPeter Grehan dev = sc->vtpci_dev; 74710b59a9bSPeter Grehan child = sc->vtpci_child_dev; 74810b59a9bSPeter Grehan 74910b59a9bSPeter Grehan if (child == NULL) 75010b59a9bSPeter Grehan return; 75110b59a9bSPeter Grehan 75210b59a9bSPeter Grehan if (device_get_state(child) != DS_NOTPRESENT) 75310b59a9bSPeter Grehan return; 75410b59a9bSPeter Grehan 75510b59a9bSPeter Grehan if (device_probe(child) != 0) 75610b59a9bSPeter Grehan return; 75710b59a9bSPeter Grehan 75810b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 75910b59a9bSPeter Grehan if (device_attach(child) != 0) { 76010b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 76110b59a9bSPeter Grehan vtpci_reset(sc); 76210b59a9bSPeter Grehan vtpci_release_child_resources(sc); 76310b59a9bSPeter Grehan /* Reset status for future attempt. */ 76410b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 76549a4385dSBryan Venteicher } else { 76610b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 76749a4385dSBryan Venteicher VIRTIO_ATTACH_COMPLETED(child); 76849a4385dSBryan Venteicher } 76910b59a9bSPeter Grehan } 77010b59a9bSPeter Grehan 77110b59a9bSPeter Grehan static int 772310dacd0SPeter Grehan vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors) 77310b59a9bSPeter Grehan { 774310dacd0SPeter Grehan device_t dev; 775310dacd0SPeter Grehan int nmsix, cnt, required; 77610b59a9bSPeter Grehan 777310dacd0SPeter Grehan dev = sc->vtpci_dev; 77810b59a9bSPeter Grehan 779310dacd0SPeter Grehan /* Allocate an additional vector for the config changes. */ 780310dacd0SPeter Grehan required = nvectors + 1; 78110b59a9bSPeter Grehan 782310dacd0SPeter Grehan nmsix = pci_msix_count(dev); 783310dacd0SPeter Grehan if (nmsix < required) 784310dacd0SPeter Grehan return (1); 785310dacd0SPeter Grehan 786310dacd0SPeter Grehan cnt = required; 787310dacd0SPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 78862a69c41SBryan Venteicher sc->vtpci_nmsix_resources = required; 789310dacd0SPeter Grehan return (0); 79010b59a9bSPeter Grehan } 79110b59a9bSPeter Grehan 792310dacd0SPeter Grehan pci_release_msi(dev); 79310b59a9bSPeter Grehan 794310dacd0SPeter Grehan return (1); 79510b59a9bSPeter Grehan } 79610b59a9bSPeter Grehan 79710b59a9bSPeter Grehan static int 798310dacd0SPeter Grehan vtpci_alloc_msi(struct vtpci_softc *sc) 799310dacd0SPeter Grehan { 800310dacd0SPeter Grehan device_t dev; 801310dacd0SPeter Grehan int nmsi, cnt, required; 802310dacd0SPeter Grehan 803310dacd0SPeter Grehan dev = sc->vtpci_dev; 804310dacd0SPeter Grehan required = 1; 805310dacd0SPeter Grehan 806310dacd0SPeter Grehan nmsi = pci_msi_count(dev); 807310dacd0SPeter Grehan if (nmsi < required) 808310dacd0SPeter Grehan return (1); 809310dacd0SPeter Grehan 810310dacd0SPeter Grehan cnt = required; 81162a69c41SBryan Venteicher if (pci_alloc_msi(dev, &cnt) == 0 && cnt >= required) 812310dacd0SPeter Grehan return (0); 813310dacd0SPeter Grehan 814310dacd0SPeter Grehan pci_release_msi(dev); 815310dacd0SPeter Grehan 816310dacd0SPeter Grehan return (1); 817310dacd0SPeter Grehan } 818310dacd0SPeter Grehan 819310dacd0SPeter Grehan static int 820310dacd0SPeter Grehan vtpci_alloc_intr_msix_pervq(struct vtpci_softc *sc) 821310dacd0SPeter Grehan { 822310dacd0SPeter Grehan int i, nvectors, error; 823310dacd0SPeter Grehan 824310dacd0SPeter Grehan if (vtpci_disable_msix != 0 || 825310dacd0SPeter Grehan sc->vtpci_flags & VTPCI_FLAG_NO_MSIX) 826310dacd0SPeter Grehan return (ENOTSUP); 827310dacd0SPeter Grehan 828310dacd0SPeter Grehan for (nvectors = 0, i = 0; i < sc->vtpci_nvqs; i++) { 82962a69c41SBryan Venteicher if (sc->vtpci_vqs[i].vtv_no_intr == 0) 830310dacd0SPeter Grehan nvectors++; 831310dacd0SPeter Grehan } 832310dacd0SPeter Grehan 833310dacd0SPeter Grehan error = vtpci_alloc_msix(sc, nvectors); 834310dacd0SPeter Grehan if (error) 835310dacd0SPeter Grehan return (error); 836310dacd0SPeter Grehan 837310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSIX; 838310dacd0SPeter Grehan 839310dacd0SPeter Grehan return (0); 840310dacd0SPeter Grehan } 841310dacd0SPeter Grehan 842310dacd0SPeter Grehan static int 843310dacd0SPeter Grehan vtpci_alloc_intr_msix_shared(struct vtpci_softc *sc) 844310dacd0SPeter Grehan { 845310dacd0SPeter Grehan int error; 846310dacd0SPeter Grehan 847310dacd0SPeter Grehan if (vtpci_disable_msix != 0 || 848310dacd0SPeter Grehan sc->vtpci_flags & VTPCI_FLAG_NO_MSIX) 849310dacd0SPeter Grehan return (ENOTSUP); 850310dacd0SPeter Grehan 851310dacd0SPeter Grehan error = vtpci_alloc_msix(sc, 1); 852310dacd0SPeter Grehan if (error) 853310dacd0SPeter Grehan return (error); 854310dacd0SPeter Grehan 855310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSIX | VTPCI_FLAG_SHARED_MSIX; 856310dacd0SPeter Grehan 857310dacd0SPeter Grehan return (0); 858310dacd0SPeter Grehan } 859310dacd0SPeter Grehan 860310dacd0SPeter Grehan static int 861310dacd0SPeter Grehan vtpci_alloc_intr_msi(struct vtpci_softc *sc) 862310dacd0SPeter Grehan { 863310dacd0SPeter Grehan int error; 864310dacd0SPeter Grehan 865310dacd0SPeter Grehan /* Only BHyVe supports MSI. */ 866310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_NO_MSI) 867310dacd0SPeter Grehan return (ENOTSUP); 868310dacd0SPeter Grehan 869310dacd0SPeter Grehan error = vtpci_alloc_msi(sc); 870310dacd0SPeter Grehan if (error) 871310dacd0SPeter Grehan return (error); 872310dacd0SPeter Grehan 873310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_MSI; 874310dacd0SPeter Grehan 875310dacd0SPeter Grehan return (0); 876310dacd0SPeter Grehan } 877310dacd0SPeter Grehan 878310dacd0SPeter Grehan static int 879310dacd0SPeter Grehan vtpci_alloc_intr_legacy(struct vtpci_softc *sc) 880310dacd0SPeter Grehan { 881310dacd0SPeter Grehan 882310dacd0SPeter Grehan sc->vtpci_flags |= VTPCI_FLAG_LEGACY; 88362a69c41SBryan Venteicher 88462a69c41SBryan Venteicher return (0); 88562a69c41SBryan Venteicher } 88662a69c41SBryan Venteicher 88762a69c41SBryan Venteicher static int 88862a69c41SBryan Venteicher vtpci_alloc_interrupt(struct vtpci_softc *sc, int rid, int flags, 88962a69c41SBryan Venteicher struct vtpci_interrupt *intr) 89062a69c41SBryan Venteicher { 89162a69c41SBryan Venteicher struct resource *irq; 89262a69c41SBryan Venteicher 89362a69c41SBryan Venteicher irq = bus_alloc_resource_any(sc->vtpci_dev, SYS_RES_IRQ, &rid, flags); 89462a69c41SBryan Venteicher if (irq == NULL) 89562a69c41SBryan Venteicher return (ENXIO); 89662a69c41SBryan Venteicher 89762a69c41SBryan Venteicher intr->vti_irq = irq; 89862a69c41SBryan Venteicher intr->vti_rid = rid; 899310dacd0SPeter Grehan 900310dacd0SPeter Grehan return (0); 901310dacd0SPeter Grehan } 902310dacd0SPeter Grehan 903310dacd0SPeter Grehan static int 904310dacd0SPeter Grehan vtpci_alloc_intr_resources(struct vtpci_softc *sc) 90510b59a9bSPeter Grehan { 90662a69c41SBryan Venteicher struct vtpci_interrupt *intr; 90762a69c41SBryan Venteicher int i, rid, flags, nvq_intrs, error; 90810b59a9bSPeter Grehan 90910b59a9bSPeter Grehan rid = 0; 910310dacd0SPeter Grehan flags = RF_ACTIVE; 91110b59a9bSPeter Grehan 91262a69c41SBryan Venteicher if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 91362a69c41SBryan Venteicher flags |= RF_SHAREABLE; 91462a69c41SBryan Venteicher else 91562a69c41SBryan Venteicher rid = 1; 91610b59a9bSPeter Grehan 91710b59a9bSPeter Grehan /* 91862a69c41SBryan Venteicher * For legacy and MSI interrupts, this single resource handles all 91962a69c41SBryan Venteicher * interrupts. For MSIX, this resource is used for the configuration 92062a69c41SBryan Venteicher * changed interrupt. 92110b59a9bSPeter Grehan */ 92262a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 92362a69c41SBryan Venteicher error = vtpci_alloc_interrupt(sc, rid, flags, intr); 92462a69c41SBryan Venteicher if (error || sc->vtpci_flags & (VTPCI_FLAG_LEGACY | VTPCI_FLAG_MSI)) 92562a69c41SBryan Venteicher return (error); 92610b59a9bSPeter Grehan 92762a69c41SBryan Venteicher /* Subtract one for the configuration changed interrupt. */ 92862a69c41SBryan Venteicher nvq_intrs = sc->vtpci_nmsix_resources - 1; 92962a69c41SBryan Venteicher 93062a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts = malloc(nvq_intrs * 93162a69c41SBryan Venteicher sizeof(struct vtpci_interrupt), M_DEVBUF, M_NOWAIT | M_ZERO); 93262a69c41SBryan Venteicher if (sc->vtpci_msix_vq_interrupts == NULL) 93362a69c41SBryan Venteicher return (ENOMEM); 93462a69c41SBryan Venteicher 93562a69c41SBryan Venteicher for (i = 0, rid++; i < nvq_intrs; i++, rid++, intr++) { 93662a69c41SBryan Venteicher error = vtpci_alloc_interrupt(sc, rid, flags, intr); 93762a69c41SBryan Venteicher if (error) 93862a69c41SBryan Venteicher return (error); 93910b59a9bSPeter Grehan } 94010b59a9bSPeter Grehan 94110b59a9bSPeter Grehan return (0); 94210b59a9bSPeter Grehan } 94310b59a9bSPeter Grehan 94410b59a9bSPeter Grehan static int 945310dacd0SPeter Grehan vtpci_setup_legacy_interrupt(struct vtpci_softc *sc, enum intr_type type) 94610b59a9bSPeter Grehan { 94762a69c41SBryan Venteicher struct vtpci_interrupt *intr; 948310dacd0SPeter Grehan int error; 94910b59a9bSPeter Grehan 95062a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 95162a69c41SBryan Venteicher error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, NULL, 95262a69c41SBryan Venteicher vtpci_legacy_intr, sc, &intr->vti_handler); 95310b59a9bSPeter Grehan 954310dacd0SPeter Grehan return (error); 95510b59a9bSPeter Grehan } 95610b59a9bSPeter Grehan 95710b59a9bSPeter Grehan static int 95862a69c41SBryan Venteicher vtpci_setup_pervq_msix_interrupts(struct vtpci_softc *sc, enum intr_type type) 95962a69c41SBryan Venteicher { 96062a69c41SBryan Venteicher struct vtpci_virtqueue *vqx; 96162a69c41SBryan Venteicher struct vtpci_interrupt *intr; 96262a69c41SBryan Venteicher int i, error; 96362a69c41SBryan Venteicher 96462a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 96562a69c41SBryan Venteicher 96662a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++) { 96762a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[i]; 96862a69c41SBryan Venteicher 96962a69c41SBryan Venteicher if (vqx->vtv_no_intr) 97062a69c41SBryan Venteicher continue; 97162a69c41SBryan Venteicher 97262a69c41SBryan Venteicher error = bus_setup_intr(sc->vtpci_dev, intr->vti_irq, type, 97362a69c41SBryan Venteicher vtpci_vq_intr_filter, vtpci_vq_intr, vqx->vtv_vq, 97462a69c41SBryan Venteicher &intr->vti_handler); 97562a69c41SBryan Venteicher if (error) 97662a69c41SBryan Venteicher return (error); 97762a69c41SBryan Venteicher 97862a69c41SBryan Venteicher intr++; 97962a69c41SBryan Venteicher } 98062a69c41SBryan Venteicher 98162a69c41SBryan Venteicher return (0); 98262a69c41SBryan Venteicher } 98362a69c41SBryan Venteicher 98462a69c41SBryan Venteicher static int 985310dacd0SPeter Grehan vtpci_setup_msix_interrupts(struct vtpci_softc *sc, enum intr_type type) 98610b59a9bSPeter Grehan { 98710b59a9bSPeter Grehan device_t dev; 98862a69c41SBryan Venteicher struct vtpci_interrupt *intr; 98962a69c41SBryan Venteicher int error; 99010b59a9bSPeter Grehan 99110b59a9bSPeter Grehan dev = sc->vtpci_dev; 99262a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 99310b59a9bSPeter Grehan 99462a69c41SBryan Venteicher error = bus_setup_intr(dev, intr->vti_irq, type, NULL, 99562a69c41SBryan Venteicher vtpci_config_intr, sc, &intr->vti_handler); 996310dacd0SPeter Grehan if (error) 997310dacd0SPeter Grehan return (error); 99810b59a9bSPeter Grehan 999310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) { 100062a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 100162a69c41SBryan Venteicher error = bus_setup_intr(dev, intr->vti_irq, type, 10026632efe4SBryan Venteicher vtpci_vq_shared_intr_filter, vtpci_vq_shared_intr, sc, 100362a69c41SBryan Venteicher &intr->vti_handler); 100462a69c41SBryan Venteicher } else 100562a69c41SBryan Venteicher error = vtpci_setup_pervq_msix_interrupts(sc, type); 1006310dacd0SPeter Grehan 100762a69c41SBryan Venteicher return (error ? error : vtpci_set_host_msix_vectors(sc)); 100810b59a9bSPeter Grehan } 100910b59a9bSPeter Grehan 101010b59a9bSPeter Grehan static int 1011310dacd0SPeter Grehan vtpci_setup_interrupts(struct vtpci_softc *sc, enum intr_type type) 1012310dacd0SPeter Grehan { 1013310dacd0SPeter Grehan int error; 1014310dacd0SPeter Grehan 1015310dacd0SPeter Grehan type |= INTR_MPSAFE; 1016310dacd0SPeter Grehan KASSERT(sc->vtpci_flags & VTPCI_FLAG_ITYPE_MASK, 101762a69c41SBryan Venteicher ("%s: no interrupt type selected %#x", __func__, sc->vtpci_flags)); 1018310dacd0SPeter Grehan 1019310dacd0SPeter Grehan error = vtpci_alloc_intr_resources(sc); 1020310dacd0SPeter Grehan if (error) 1021310dacd0SPeter Grehan return (error); 1022310dacd0SPeter Grehan 1023310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_LEGACY) 1024310dacd0SPeter Grehan error = vtpci_setup_legacy_interrupt(sc, type); 1025310dacd0SPeter Grehan else if (sc->vtpci_flags & VTPCI_FLAG_MSI) 1026310dacd0SPeter Grehan error = vtpci_setup_msi_interrupt(sc, type); 1027310dacd0SPeter Grehan else 1028310dacd0SPeter Grehan error = vtpci_setup_msix_interrupts(sc, type); 1029310dacd0SPeter Grehan 1030310dacd0SPeter Grehan return (error); 1031310dacd0SPeter Grehan } 1032310dacd0SPeter Grehan 1033310dacd0SPeter Grehan static int 103462a69c41SBryan Venteicher vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, 103562a69c41SBryan Venteicher struct vtpci_interrupt *intr) 103610b59a9bSPeter Grehan { 103710b59a9bSPeter Grehan device_t dev; 103862a69c41SBryan Venteicher uint16_t vector; 103910b59a9bSPeter Grehan 104010b59a9bSPeter Grehan dev = sc->vtpci_dev; 104110b59a9bSPeter Grehan 104262a69c41SBryan Venteicher if (intr != NULL) { 1043310dacd0SPeter Grehan /* Map from guest rid to host vector. */ 104462a69c41SBryan Venteicher vector = intr->vti_rid - 1; 104510b59a9bSPeter Grehan } else 104610b59a9bSPeter Grehan vector = VIRTIO_MSI_NO_VECTOR; 104710b59a9bSPeter Grehan 104810b59a9bSPeter Grehan vtpci_write_config_2(sc, offset, vector); 104910b59a9bSPeter Grehan 1050310dacd0SPeter Grehan /* Read vector to determine if the host had sufficient resources. */ 105162a69c41SBryan Venteicher if (vtpci_read_config_2(sc, offset) != vector) { 1052310dacd0SPeter Grehan device_printf(dev, 1053310dacd0SPeter Grehan "insufficient host resources for MSIX interrupts\n"); 105410b59a9bSPeter Grehan return (ENODEV); 105510b59a9bSPeter Grehan } 105610b59a9bSPeter Grehan 105710b59a9bSPeter Grehan return (0); 105810b59a9bSPeter Grehan } 105910b59a9bSPeter Grehan 1060310dacd0SPeter Grehan static int 1061310dacd0SPeter Grehan vtpci_set_host_msix_vectors(struct vtpci_softc *sc) 1062310dacd0SPeter Grehan { 106362a69c41SBryan Venteicher struct vtpci_interrupt *intr, *tintr; 106462a69c41SBryan Venteicher int idx, offset, error; 1065310dacd0SPeter Grehan 106662a69c41SBryan Venteicher intr = &sc->vtpci_device_interrupt; 106762a69c41SBryan Venteicher offset = VIRTIO_MSI_CONFIG_VECTOR; 106862a69c41SBryan Venteicher 106962a69c41SBryan Venteicher error = vtpci_register_msix_vector(sc, offset, intr); 1070310dacd0SPeter Grehan if (error) 1071310dacd0SPeter Grehan return (error); 1072310dacd0SPeter Grehan 107362a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 107462a69c41SBryan Venteicher offset = VIRTIO_MSI_QUEUE_VECTOR; 107562a69c41SBryan Venteicher 1076310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 1077310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 107862a69c41SBryan Venteicher 107962a69c41SBryan Venteicher if (sc->vtpci_vqs[idx].vtv_no_intr) 108062a69c41SBryan Venteicher tintr = NULL; 108162a69c41SBryan Venteicher else 108262a69c41SBryan Venteicher tintr = intr; 108362a69c41SBryan Venteicher 108462a69c41SBryan Venteicher error = vtpci_register_msix_vector(sc, offset, tintr); 1085310dacd0SPeter Grehan if (error) 108662a69c41SBryan Venteicher break; 108762a69c41SBryan Venteicher 108862a69c41SBryan Venteicher /* 108962a69c41SBryan Venteicher * For shared MSIX, all the virtqueues share the first 109062a69c41SBryan Venteicher * interrupt. 109162a69c41SBryan Venteicher */ 109218ec6fbcSAndriy Gapon if (!sc->vtpci_vqs[idx].vtv_no_intr && 109318ec6fbcSAndriy Gapon (sc->vtpci_flags & VTPCI_FLAG_SHARED_MSIX) == 0) 109462a69c41SBryan Venteicher intr++; 1095310dacd0SPeter Grehan } 1096310dacd0SPeter Grehan 109762a69c41SBryan Venteicher return (error); 1098310dacd0SPeter Grehan } 1099310dacd0SPeter Grehan 1100310dacd0SPeter Grehan static int 1101310dacd0SPeter Grehan vtpci_reinit_virtqueue(struct vtpci_softc *sc, int idx) 1102310dacd0SPeter Grehan { 1103310dacd0SPeter Grehan struct vtpci_virtqueue *vqx; 1104310dacd0SPeter Grehan struct virtqueue *vq; 1105310dacd0SPeter Grehan int error; 1106310dacd0SPeter Grehan uint16_t size; 1107310dacd0SPeter Grehan 110862a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 110962a69c41SBryan Venteicher vq = vqx->vtv_vq; 1110310dacd0SPeter Grehan 111162a69c41SBryan Venteicher KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx)); 1112310dacd0SPeter Grehan 1113310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 1114310dacd0SPeter Grehan size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 1115310dacd0SPeter Grehan 1116310dacd0SPeter Grehan error = virtqueue_reinit(vq, size); 1117310dacd0SPeter Grehan if (error) 1118310dacd0SPeter Grehan return (error); 1119310dacd0SPeter Grehan 1120310dacd0SPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 1121310dacd0SPeter Grehan virtqueue_paddr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 1122310dacd0SPeter Grehan 1123310dacd0SPeter Grehan return (0); 1124310dacd0SPeter Grehan } 1125310dacd0SPeter Grehan 112610b59a9bSPeter Grehan static void 112762a69c41SBryan Venteicher vtpci_free_interrupt(struct vtpci_softc *sc, struct vtpci_interrupt *intr) 112810b59a9bSPeter Grehan { 112910b59a9bSPeter Grehan device_t dev; 113010b59a9bSPeter Grehan 113110b59a9bSPeter Grehan dev = sc->vtpci_dev; 113210b59a9bSPeter Grehan 113362a69c41SBryan Venteicher if (intr->vti_handler != NULL) { 113462a69c41SBryan Venteicher bus_teardown_intr(dev, intr->vti_irq, intr->vti_handler); 113562a69c41SBryan Venteicher intr->vti_handler = NULL; 113610b59a9bSPeter Grehan } 113710b59a9bSPeter Grehan 113862a69c41SBryan Venteicher if (intr->vti_irq != NULL) { 113962a69c41SBryan Venteicher bus_release_resource(dev, SYS_RES_IRQ, intr->vti_rid, 114062a69c41SBryan Venteicher intr->vti_irq); 114162a69c41SBryan Venteicher intr->vti_irq = NULL; 114262a69c41SBryan Venteicher intr->vti_rid = -1; 114362a69c41SBryan Venteicher } 114410b59a9bSPeter Grehan } 114510b59a9bSPeter Grehan 114662a69c41SBryan Venteicher static void 114762a69c41SBryan Venteicher vtpci_free_interrupts(struct vtpci_softc *sc) 114862a69c41SBryan Venteicher { 114962a69c41SBryan Venteicher struct vtpci_interrupt *intr; 115062a69c41SBryan Venteicher int i, nvq_intrs; 115162a69c41SBryan Venteicher 115262a69c41SBryan Venteicher vtpci_free_interrupt(sc, &sc->vtpci_device_interrupt); 115362a69c41SBryan Venteicher 115462a69c41SBryan Venteicher if (sc->vtpci_nmsix_resources != 0) { 115562a69c41SBryan Venteicher nvq_intrs = sc->vtpci_nmsix_resources - 1; 115662a69c41SBryan Venteicher sc->vtpci_nmsix_resources = 0; 115762a69c41SBryan Venteicher 115862a69c41SBryan Venteicher intr = sc->vtpci_msix_vq_interrupts; 115962a69c41SBryan Venteicher if (intr != NULL) { 116062a69c41SBryan Venteicher for (i = 0; i < nvq_intrs; i++, intr++) 116162a69c41SBryan Venteicher vtpci_free_interrupt(sc, intr); 116262a69c41SBryan Venteicher 116362a69c41SBryan Venteicher free(sc->vtpci_msix_vq_interrupts, M_DEVBUF); 116462a69c41SBryan Venteicher sc->vtpci_msix_vq_interrupts = NULL; 116562a69c41SBryan Venteicher } 116610b59a9bSPeter Grehan } 1167310dacd0SPeter Grehan 1168310dacd0SPeter Grehan if (sc->vtpci_flags & (VTPCI_FLAG_MSI | VTPCI_FLAG_MSIX)) 116962a69c41SBryan Venteicher pci_release_msi(sc->vtpci_dev); 1170310dacd0SPeter Grehan 1171310dacd0SPeter Grehan sc->vtpci_flags &= ~VTPCI_FLAG_ITYPE_MASK; 117210b59a9bSPeter Grehan } 117310b59a9bSPeter Grehan 117410b59a9bSPeter Grehan static void 117510b59a9bSPeter Grehan vtpci_free_virtqueues(struct vtpci_softc *sc) 117610b59a9bSPeter Grehan { 117710b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 117862a69c41SBryan Venteicher int idx; 117910b59a9bSPeter Grehan 118062a69c41SBryan Venteicher for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 118162a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[idx]; 118210b59a9bSPeter Grehan 118362a69c41SBryan Venteicher vtpci_select_virtqueue(sc, idx); 118462a69c41SBryan Venteicher vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 0); 118562a69c41SBryan Venteicher 118662a69c41SBryan Venteicher virtqueue_free(vqx->vtv_vq); 118762a69c41SBryan Venteicher vqx->vtv_vq = NULL; 118810b59a9bSPeter Grehan } 1189310dacd0SPeter Grehan 119062a69c41SBryan Venteicher free(sc->vtpci_vqs, M_DEVBUF); 119162a69c41SBryan Venteicher sc->vtpci_vqs = NULL; 1192310dacd0SPeter Grehan sc->vtpci_nvqs = 0; 119310b59a9bSPeter Grehan } 1194310dacd0SPeter Grehan 1195310dacd0SPeter Grehan static void 119662a69c41SBryan Venteicher vtpci_release_child_resources(struct vtpci_softc *sc) 119762a69c41SBryan Venteicher { 119862a69c41SBryan Venteicher 119962a69c41SBryan Venteicher vtpci_free_interrupts(sc); 120062a69c41SBryan Venteicher vtpci_free_virtqueues(sc); 120162a69c41SBryan Venteicher } 120262a69c41SBryan Venteicher 120362a69c41SBryan Venteicher static void 1204310dacd0SPeter Grehan vtpci_cleanup_setup_intr_attempt(struct vtpci_softc *sc) 1205310dacd0SPeter Grehan { 1206310dacd0SPeter Grehan int idx; 1207310dacd0SPeter Grehan 1208310dacd0SPeter Grehan if (sc->vtpci_flags & VTPCI_FLAG_MSIX) { 1209310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_MSI_CONFIG_VECTOR, 1210310dacd0SPeter Grehan VIRTIO_MSI_NO_VECTOR); 1211310dacd0SPeter Grehan 1212310dacd0SPeter Grehan for (idx = 0; idx < sc->vtpci_nvqs; idx++) { 1213310dacd0SPeter Grehan vtpci_select_virtqueue(sc, idx); 1214310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_MSI_QUEUE_VECTOR, 1215310dacd0SPeter Grehan VIRTIO_MSI_NO_VECTOR); 1216310dacd0SPeter Grehan } 1217310dacd0SPeter Grehan } 1218310dacd0SPeter Grehan 1219310dacd0SPeter Grehan vtpci_free_interrupts(sc); 122010b59a9bSPeter Grehan } 122110b59a9bSPeter Grehan 122210b59a9bSPeter Grehan static void 122310b59a9bSPeter Grehan vtpci_reset(struct vtpci_softc *sc) 122410b59a9bSPeter Grehan { 122510b59a9bSPeter Grehan 122610b59a9bSPeter Grehan /* 122710b59a9bSPeter Grehan * Setting the status to RESET sets the host device to 122810b59a9bSPeter Grehan * the original, uninitialized state. 122910b59a9bSPeter Grehan */ 123010b59a9bSPeter Grehan vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET); 123110b59a9bSPeter Grehan } 123210b59a9bSPeter Grehan 1233310dacd0SPeter Grehan static void 1234310dacd0SPeter Grehan vtpci_select_virtqueue(struct vtpci_softc *sc, int idx) 1235310dacd0SPeter Grehan { 1236310dacd0SPeter Grehan 1237310dacd0SPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, idx); 1238310dacd0SPeter Grehan } 1239310dacd0SPeter Grehan 12406632efe4SBryan Venteicher static void 124110b59a9bSPeter Grehan vtpci_legacy_intr(void *xsc) 124210b59a9bSPeter Grehan { 124310b59a9bSPeter Grehan struct vtpci_softc *sc; 124410b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 124510b59a9bSPeter Grehan int i; 124610b59a9bSPeter Grehan uint8_t isr; 124710b59a9bSPeter Grehan 124810b59a9bSPeter Grehan sc = xsc; 124962a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 125010b59a9bSPeter Grehan 125110b59a9bSPeter Grehan /* Reading the ISR also clears it. */ 125210b59a9bSPeter Grehan isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR); 125310b59a9bSPeter Grehan 125410b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_CONFIG) 125510b59a9bSPeter Grehan vtpci_config_intr(sc); 125610b59a9bSPeter Grehan 12576632efe4SBryan Venteicher if (isr & VIRTIO_PCI_ISR_INTR) { 125862a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 125962a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 126062a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 126162a69c41SBryan Venteicher } 12626632efe4SBryan Venteicher } 126310b59a9bSPeter Grehan } 126410b59a9bSPeter Grehan 126510b59a9bSPeter Grehan static int 12666632efe4SBryan Venteicher vtpci_vq_shared_intr_filter(void *xsc) 126710b59a9bSPeter Grehan { 126810b59a9bSPeter Grehan struct vtpci_softc *sc; 126910b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 127010b59a9bSPeter Grehan int i, rc; 127110b59a9bSPeter Grehan 127210b59a9bSPeter Grehan rc = 0; 127310b59a9bSPeter Grehan sc = xsc; 127462a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 127510b59a9bSPeter Grehan 127662a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 127762a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 127862a69c41SBryan Venteicher rc |= virtqueue_intr_filter(vqx->vtv_vq); 127962a69c41SBryan Venteicher } 128010b59a9bSPeter Grehan 12816632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 12826632efe4SBryan Venteicher } 12836632efe4SBryan Venteicher 12846632efe4SBryan Venteicher static void 12856632efe4SBryan Venteicher vtpci_vq_shared_intr(void *xsc) 12866632efe4SBryan Venteicher { 12876632efe4SBryan Venteicher struct vtpci_softc *sc; 12886632efe4SBryan Venteicher struct vtpci_virtqueue *vqx; 12896632efe4SBryan Venteicher int i; 12906632efe4SBryan Venteicher 12916632efe4SBryan Venteicher sc = xsc; 129262a69c41SBryan Venteicher vqx = &sc->vtpci_vqs[0]; 12936632efe4SBryan Venteicher 129462a69c41SBryan Venteicher for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) { 129562a69c41SBryan Venteicher if (vqx->vtv_no_intr == 0) 129662a69c41SBryan Venteicher virtqueue_intr(vqx->vtv_vq); 129762a69c41SBryan Venteicher } 129810b59a9bSPeter Grehan } 129910b59a9bSPeter Grehan 130010b59a9bSPeter Grehan static int 13016632efe4SBryan Venteicher vtpci_vq_intr_filter(void *xvq) 130210b59a9bSPeter Grehan { 130310b59a9bSPeter Grehan struct virtqueue *vq; 130410b59a9bSPeter Grehan int rc; 130510b59a9bSPeter Grehan 130610b59a9bSPeter Grehan vq = xvq; 13076632efe4SBryan Venteicher rc = virtqueue_intr_filter(vq); 130810b59a9bSPeter Grehan 13096632efe4SBryan Venteicher return (rc ? FILTER_SCHEDULE_THREAD : FILTER_STRAY); 131010b59a9bSPeter Grehan } 131110b59a9bSPeter Grehan 13126632efe4SBryan Venteicher static void 13136632efe4SBryan Venteicher vtpci_vq_intr(void *xvq) 13146632efe4SBryan Venteicher { 13156632efe4SBryan Venteicher struct virtqueue *vq; 13166632efe4SBryan Venteicher 13176632efe4SBryan Venteicher vq = xvq; 13186632efe4SBryan Venteicher virtqueue_intr(vq); 13196632efe4SBryan Venteicher } 13206632efe4SBryan Venteicher 13216632efe4SBryan Venteicher static void 132210b59a9bSPeter Grehan vtpci_config_intr(void *xsc) 132310b59a9bSPeter Grehan { 132410b59a9bSPeter Grehan struct vtpci_softc *sc; 132510b59a9bSPeter Grehan device_t child; 132610b59a9bSPeter Grehan 132710b59a9bSPeter Grehan sc = xsc; 132810b59a9bSPeter Grehan child = sc->vtpci_child_dev; 132910b59a9bSPeter Grehan 133010b59a9bSPeter Grehan if (child != NULL) 13316632efe4SBryan Venteicher VIRTIO_CONFIG_CHANGE(child); 133210b59a9bSPeter Grehan } 1333