110b59a9bSPeter Grehan /*- 210b59a9bSPeter Grehan * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org> 310b59a9bSPeter Grehan * All rights reserved. 410b59a9bSPeter Grehan * 510b59a9bSPeter Grehan * Redistribution and use in source and binary forms, with or without 610b59a9bSPeter Grehan * modification, are permitted provided that the following conditions 710b59a9bSPeter Grehan * are met: 810b59a9bSPeter Grehan * 1. Redistributions of source code must retain the above copyright 910b59a9bSPeter Grehan * notice unmodified, this list of conditions, and the following 1010b59a9bSPeter Grehan * disclaimer. 1110b59a9bSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 1210b59a9bSPeter Grehan * notice, this list of conditions and the following disclaimer in the 1310b59a9bSPeter Grehan * documentation and/or other materials provided with the distribution. 1410b59a9bSPeter Grehan * 1510b59a9bSPeter Grehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1610b59a9bSPeter Grehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1710b59a9bSPeter Grehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1810b59a9bSPeter Grehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1910b59a9bSPeter Grehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2010b59a9bSPeter Grehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2110b59a9bSPeter Grehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2210b59a9bSPeter Grehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2310b59a9bSPeter Grehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2410b59a9bSPeter Grehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2510b59a9bSPeter Grehan */ 2610b59a9bSPeter Grehan 2710b59a9bSPeter Grehan /* Driver for the VirtIO PCI interface. */ 2810b59a9bSPeter Grehan 2910b59a9bSPeter Grehan #include <sys/cdefs.h> 3010b59a9bSPeter Grehan __FBSDID("$FreeBSD$"); 3110b59a9bSPeter Grehan 3210b59a9bSPeter Grehan #include <sys/param.h> 3310b59a9bSPeter Grehan #include <sys/systm.h> 3410b59a9bSPeter Grehan #include <sys/bus.h> 3510b59a9bSPeter Grehan #include <sys/kernel.h> 3610b59a9bSPeter Grehan #include <sys/module.h> 3710b59a9bSPeter Grehan #include <sys/malloc.h> 3810b59a9bSPeter Grehan 3910b59a9bSPeter Grehan #include <machine/bus.h> 4010b59a9bSPeter Grehan #include <machine/resource.h> 4110b59a9bSPeter Grehan #include <sys/bus.h> 4210b59a9bSPeter Grehan #include <sys/rman.h> 4310b59a9bSPeter Grehan 4410b59a9bSPeter Grehan #include <dev/pci/pcivar.h> 4510b59a9bSPeter Grehan #include <dev/pci/pcireg.h> 4610b59a9bSPeter Grehan 4710b59a9bSPeter Grehan #include <dev/virtio/virtio.h> 4810b59a9bSPeter Grehan #include <dev/virtio/virtqueue.h> 4910b59a9bSPeter Grehan #include <dev/virtio/pci/virtio_pci.h> 5010b59a9bSPeter Grehan 5110b59a9bSPeter Grehan #include "virtio_bus_if.h" 5210b59a9bSPeter Grehan #include "virtio_if.h" 5310b59a9bSPeter Grehan 5410b59a9bSPeter Grehan struct vtpci_softc { 5510b59a9bSPeter Grehan device_t vtpci_dev; 5610b59a9bSPeter Grehan struct resource *vtpci_res; 5710b59a9bSPeter Grehan struct resource *vtpci_msix_res; 5810b59a9bSPeter Grehan uint64_t vtpci_features; 5910b59a9bSPeter Grehan uint32_t vtpci_flags; 6010b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_NO_MSI 0x0001 6110b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_MSI 0x0002 6210b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_NO_MSIX 0x0010 6310b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_MSIX 0x0020 6410b59a9bSPeter Grehan #define VIRTIO_PCI_FLAG_SHARED_MSIX 0x0040 6510b59a9bSPeter Grehan 6610b59a9bSPeter Grehan device_t vtpci_child_dev; 6710b59a9bSPeter Grehan struct virtio_feature_desc *vtpci_child_feat_desc; 6810b59a9bSPeter Grehan 6910b59a9bSPeter Grehan /* 7010b59a9bSPeter Grehan * Ideally, each virtqueue that the driver provides a callback for 7110b59a9bSPeter Grehan * will receive its own MSIX vector. If there are not sufficient 7210b59a9bSPeter Grehan * vectors available, we will then attempt to have all the VQs 7310b59a9bSPeter Grehan * share one vector. Note that when using MSIX, the configuration 7410b59a9bSPeter Grehan * changed notifications must be on their own vector. 7510b59a9bSPeter Grehan * 7610b59a9bSPeter Grehan * If MSIX is not available, we will attempt to have the whole 7710b59a9bSPeter Grehan * device share one MSI vector, and then, finally, one legacy 7810b59a9bSPeter Grehan * interrupt. 7910b59a9bSPeter Grehan */ 8010b59a9bSPeter Grehan int vtpci_nvqs; 8110b59a9bSPeter Grehan struct vtpci_virtqueue { 8210b59a9bSPeter Grehan struct virtqueue *vq; 8310b59a9bSPeter Grehan 8410b59a9bSPeter Grehan /* Index into vtpci_intr_res[] below. Unused, then -1. */ 8510b59a9bSPeter Grehan int ires_idx; 8610b59a9bSPeter Grehan } vtpci_vqx[VIRTIO_MAX_VIRTQUEUES]; 8710b59a9bSPeter Grehan 8810b59a9bSPeter Grehan /* 8910b59a9bSPeter Grehan * When using MSIX interrupts, the first element of vtpci_intr_res[] 9010b59a9bSPeter Grehan * is always the configuration changed notifications. The remaining 9110b59a9bSPeter Grehan * element(s) are used for the virtqueues. 9210b59a9bSPeter Grehan * 9310b59a9bSPeter Grehan * With MSI and legacy interrupts, only the first element of 9410b59a9bSPeter Grehan * vtpci_intr_res[] is used. 9510b59a9bSPeter Grehan */ 9610b59a9bSPeter Grehan int vtpci_nintr_res; 9710b59a9bSPeter Grehan struct vtpci_intr_resource { 9810b59a9bSPeter Grehan struct resource *irq; 9910b59a9bSPeter Grehan int rid; 10010b59a9bSPeter Grehan void *intrhand; 10110b59a9bSPeter Grehan } vtpci_intr_res[1 + VIRTIO_MAX_VIRTQUEUES]; 10210b59a9bSPeter Grehan }; 10310b59a9bSPeter Grehan 10410b59a9bSPeter Grehan static int vtpci_probe(device_t); 10510b59a9bSPeter Grehan static int vtpci_attach(device_t); 10610b59a9bSPeter Grehan static int vtpci_detach(device_t); 10710b59a9bSPeter Grehan static int vtpci_suspend(device_t); 10810b59a9bSPeter Grehan static int vtpci_resume(device_t); 10910b59a9bSPeter Grehan static int vtpci_shutdown(device_t); 11010b59a9bSPeter Grehan static void vtpci_driver_added(device_t, driver_t *); 11110b59a9bSPeter Grehan static void vtpci_child_detached(device_t, device_t); 11210b59a9bSPeter Grehan static int vtpci_read_ivar(device_t, device_t, int, uintptr_t *); 11310b59a9bSPeter Grehan static int vtpci_write_ivar(device_t, device_t, int, uintptr_t); 11410b59a9bSPeter Grehan 11510b59a9bSPeter Grehan static uint64_t vtpci_negotiate_features(device_t, uint64_t); 11610b59a9bSPeter Grehan static int vtpci_with_feature(device_t, uint64_t); 11710b59a9bSPeter Grehan static int vtpci_alloc_virtqueues(device_t, int, int, 11810b59a9bSPeter Grehan struct vq_alloc_info *); 11910b59a9bSPeter Grehan static int vtpci_setup_intr(device_t, enum intr_type); 12010b59a9bSPeter Grehan static void vtpci_stop(device_t); 12110b59a9bSPeter Grehan static int vtpci_reinit(device_t, uint64_t); 12210b59a9bSPeter Grehan static void vtpci_reinit_complete(device_t); 12310b59a9bSPeter Grehan static void vtpci_notify_virtqueue(device_t, uint16_t); 12410b59a9bSPeter Grehan static uint8_t vtpci_get_status(device_t); 12510b59a9bSPeter Grehan static void vtpci_set_status(device_t, uint8_t); 12610b59a9bSPeter Grehan static void vtpci_read_dev_config(device_t, bus_size_t, void *, int); 12710b59a9bSPeter Grehan static void vtpci_write_dev_config(device_t, bus_size_t, void *, int); 12810b59a9bSPeter Grehan 12910b59a9bSPeter Grehan static void vtpci_describe_features(struct vtpci_softc *, const char *, 13010b59a9bSPeter Grehan uint64_t); 13110b59a9bSPeter Grehan static void vtpci_probe_and_attach_child(struct vtpci_softc *); 13210b59a9bSPeter Grehan 13310b59a9bSPeter Grehan static int vtpci_alloc_interrupts(struct vtpci_softc *, int, int, 13410b59a9bSPeter Grehan struct vq_alloc_info *); 13510b59a9bSPeter Grehan static int vtpci_alloc_intr_resources(struct vtpci_softc *, int, 13610b59a9bSPeter Grehan struct vq_alloc_info *); 13710b59a9bSPeter Grehan static int vtpci_alloc_msi(struct vtpci_softc *); 13810b59a9bSPeter Grehan static int vtpci_alloc_msix(struct vtpci_softc *, int); 13910b59a9bSPeter Grehan static int vtpci_register_msix_vector(struct vtpci_softc *, int, int); 14010b59a9bSPeter Grehan 14110b59a9bSPeter Grehan static void vtpci_free_interrupts(struct vtpci_softc *); 14210b59a9bSPeter Grehan static void vtpci_free_virtqueues(struct vtpci_softc *); 14310b59a9bSPeter Grehan static void vtpci_release_child_resources(struct vtpci_softc *); 14410b59a9bSPeter Grehan static void vtpci_reset(struct vtpci_softc *); 14510b59a9bSPeter Grehan 14610b59a9bSPeter Grehan static int vtpci_legacy_intr(void *); 14710b59a9bSPeter Grehan static int vtpci_vq_shared_intr(void *); 14810b59a9bSPeter Grehan static int vtpci_vq_intr(void *); 14910b59a9bSPeter Grehan static int vtpci_config_intr(void *); 15010b59a9bSPeter Grehan 15110b59a9bSPeter Grehan /* 15210b59a9bSPeter Grehan * I/O port read/write wrappers. 15310b59a9bSPeter Grehan */ 15410b59a9bSPeter Grehan #define vtpci_read_config_1(sc, o) bus_read_1((sc)->vtpci_res, (o)) 15510b59a9bSPeter Grehan #define vtpci_read_config_2(sc, o) bus_read_2((sc)->vtpci_res, (o)) 15610b59a9bSPeter Grehan #define vtpci_read_config_4(sc, o) bus_read_4((sc)->vtpci_res, (o)) 15710b59a9bSPeter Grehan #define vtpci_write_config_1(sc, o, v) bus_write_1((sc)->vtpci_res, (o), (v)) 15810b59a9bSPeter Grehan #define vtpci_write_config_2(sc, o, v) bus_write_2((sc)->vtpci_res, (o), (v)) 15910b59a9bSPeter Grehan #define vtpci_write_config_4(sc, o, v) bus_write_4((sc)->vtpci_res, (o), (v)) 16010b59a9bSPeter Grehan 16110b59a9bSPeter Grehan /* Tunables. */ 16210b59a9bSPeter Grehan static int vtpci_disable_msix = 0; 16310b59a9bSPeter Grehan TUNABLE_INT("hw.virtio.pci.disable_msix", &vtpci_disable_msix); 16410b59a9bSPeter Grehan 16510b59a9bSPeter Grehan static device_method_t vtpci_methods[] = { 16610b59a9bSPeter Grehan /* Device interface. */ 16710b59a9bSPeter Grehan DEVMETHOD(device_probe, vtpci_probe), 16810b59a9bSPeter Grehan DEVMETHOD(device_attach, vtpci_attach), 16910b59a9bSPeter Grehan DEVMETHOD(device_detach, vtpci_detach), 17010b59a9bSPeter Grehan DEVMETHOD(device_suspend, vtpci_suspend), 17110b59a9bSPeter Grehan DEVMETHOD(device_resume, vtpci_resume), 17210b59a9bSPeter Grehan DEVMETHOD(device_shutdown, vtpci_shutdown), 17310b59a9bSPeter Grehan 17410b59a9bSPeter Grehan /* Bus interface. */ 17510b59a9bSPeter Grehan DEVMETHOD(bus_driver_added, vtpci_driver_added), 17610b59a9bSPeter Grehan DEVMETHOD(bus_child_detached, vtpci_child_detached), 17710b59a9bSPeter Grehan DEVMETHOD(bus_read_ivar, vtpci_read_ivar), 17810b59a9bSPeter Grehan DEVMETHOD(bus_write_ivar, vtpci_write_ivar), 17910b59a9bSPeter Grehan 18010b59a9bSPeter Grehan /* VirtIO bus interface. */ 18110b59a9bSPeter Grehan DEVMETHOD(virtio_bus_negotiate_features, vtpci_negotiate_features), 18210b59a9bSPeter Grehan DEVMETHOD(virtio_bus_with_feature, vtpci_with_feature), 18310b59a9bSPeter Grehan DEVMETHOD(virtio_bus_alloc_virtqueues, vtpci_alloc_virtqueues), 18410b59a9bSPeter Grehan DEVMETHOD(virtio_bus_setup_intr, vtpci_setup_intr), 18510b59a9bSPeter Grehan DEVMETHOD(virtio_bus_stop, vtpci_stop), 18610b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit, vtpci_reinit), 18710b59a9bSPeter Grehan DEVMETHOD(virtio_bus_reinit_complete, vtpci_reinit_complete), 18810b59a9bSPeter Grehan DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), 18910b59a9bSPeter Grehan DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), 19010b59a9bSPeter Grehan DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), 19110b59a9bSPeter Grehan 19210b59a9bSPeter Grehan { 0, 0 } 19310b59a9bSPeter Grehan }; 19410b59a9bSPeter Grehan 19510b59a9bSPeter Grehan static driver_t vtpci_driver = { 19610b59a9bSPeter Grehan "virtio_pci", 19710b59a9bSPeter Grehan vtpci_methods, 19810b59a9bSPeter Grehan sizeof(struct vtpci_softc) 19910b59a9bSPeter Grehan }; 20010b59a9bSPeter Grehan 20110b59a9bSPeter Grehan devclass_t vtpci_devclass; 20210b59a9bSPeter Grehan 20310b59a9bSPeter Grehan DRIVER_MODULE(virtio_pci, pci, vtpci_driver, vtpci_devclass, 0, 0); 20410b59a9bSPeter Grehan MODULE_VERSION(virtio_pci, 1); 20510b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, pci, 1, 1, 1); 20610b59a9bSPeter Grehan MODULE_DEPEND(virtio_pci, virtio, 1, 1, 1); 20710b59a9bSPeter Grehan 20810b59a9bSPeter Grehan static int 20910b59a9bSPeter Grehan vtpci_probe(device_t dev) 21010b59a9bSPeter Grehan { 21110b59a9bSPeter Grehan char desc[36]; 21210b59a9bSPeter Grehan const char *name; 21310b59a9bSPeter Grehan 21410b59a9bSPeter Grehan if (pci_get_vendor(dev) != VIRTIO_PCI_VENDORID) 21510b59a9bSPeter Grehan return (ENXIO); 21610b59a9bSPeter Grehan 21710b59a9bSPeter Grehan if (pci_get_device(dev) < VIRTIO_PCI_DEVICEID_MIN || 21810b59a9bSPeter Grehan pci_get_device(dev) > VIRTIO_PCI_DEVICEID_MAX) 21910b59a9bSPeter Grehan return (ENXIO); 22010b59a9bSPeter Grehan 22110b59a9bSPeter Grehan if (pci_get_revid(dev) != VIRTIO_PCI_ABI_VERSION) 22210b59a9bSPeter Grehan return (ENXIO); 22310b59a9bSPeter Grehan 22410b59a9bSPeter Grehan name = virtio_device_name(pci_get_subdevice(dev)); 22510b59a9bSPeter Grehan if (name == NULL) 22610b59a9bSPeter Grehan name = "Unknown"; 22710b59a9bSPeter Grehan 22810b59a9bSPeter Grehan snprintf(desc, sizeof(desc), "VirtIO PCI %s adapter", name); 22910b59a9bSPeter Grehan device_set_desc_copy(dev, desc); 23010b59a9bSPeter Grehan 23110b59a9bSPeter Grehan return (BUS_PROBE_DEFAULT); 23210b59a9bSPeter Grehan } 23310b59a9bSPeter Grehan 23410b59a9bSPeter Grehan static int 23510b59a9bSPeter Grehan vtpci_attach(device_t dev) 23610b59a9bSPeter Grehan { 23710b59a9bSPeter Grehan struct vtpci_softc *sc; 23810b59a9bSPeter Grehan device_t child; 23910b59a9bSPeter Grehan int rid; 24010b59a9bSPeter Grehan 24110b59a9bSPeter Grehan sc = device_get_softc(dev); 24210b59a9bSPeter Grehan sc->vtpci_dev = dev; 24310b59a9bSPeter Grehan 24410b59a9bSPeter Grehan pci_enable_busmaster(dev); 24510b59a9bSPeter Grehan 24610b59a9bSPeter Grehan rid = PCIR_BAR(0); 24710b59a9bSPeter Grehan sc->vtpci_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 24810b59a9bSPeter Grehan RF_ACTIVE); 24910b59a9bSPeter Grehan if (sc->vtpci_res == NULL) { 25010b59a9bSPeter Grehan device_printf(dev, "cannot map I/O space\n"); 25110b59a9bSPeter Grehan return (ENXIO); 25210b59a9bSPeter Grehan } 25310b59a9bSPeter Grehan 254*cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSI, NULL) != 0) 25510b59a9bSPeter Grehan sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSI; 25610b59a9bSPeter Grehan 257*cda6c6abSJohn Baldwin if (pci_find_cap(dev, PCIY_MSIX, NULL) == 0) { 25810b59a9bSPeter Grehan rid = PCIR_BAR(1); 25910b59a9bSPeter Grehan sc->vtpci_msix_res = bus_alloc_resource_any(dev, 26010b59a9bSPeter Grehan SYS_RES_MEMORY, &rid, RF_ACTIVE); 26110b59a9bSPeter Grehan } 26210b59a9bSPeter Grehan 26310b59a9bSPeter Grehan if (sc->vtpci_msix_res == NULL) 26410b59a9bSPeter Grehan sc->vtpci_flags |= VIRTIO_PCI_FLAG_NO_MSIX; 26510b59a9bSPeter Grehan 26610b59a9bSPeter Grehan vtpci_reset(sc); 26710b59a9bSPeter Grehan 26810b59a9bSPeter Grehan /* Tell the host we've noticed this device. */ 26910b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 27010b59a9bSPeter Grehan 27110b59a9bSPeter Grehan if ((child = device_add_child(dev, NULL, -1)) == NULL) { 27210b59a9bSPeter Grehan device_printf(dev, "cannot create child device\n"); 27310b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 27410b59a9bSPeter Grehan vtpci_detach(dev); 27510b59a9bSPeter Grehan return (ENOMEM); 27610b59a9bSPeter Grehan } 27710b59a9bSPeter Grehan 27810b59a9bSPeter Grehan sc->vtpci_child_dev = child; 27910b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 28010b59a9bSPeter Grehan 28110b59a9bSPeter Grehan return (0); 28210b59a9bSPeter Grehan } 28310b59a9bSPeter Grehan 28410b59a9bSPeter Grehan static int 28510b59a9bSPeter Grehan vtpci_detach(device_t dev) 28610b59a9bSPeter Grehan { 28710b59a9bSPeter Grehan struct vtpci_softc *sc; 28810b59a9bSPeter Grehan device_t child; 28910b59a9bSPeter Grehan int error; 29010b59a9bSPeter Grehan 29110b59a9bSPeter Grehan sc = device_get_softc(dev); 29210b59a9bSPeter Grehan 29310b59a9bSPeter Grehan if ((child = sc->vtpci_child_dev) != NULL) { 29410b59a9bSPeter Grehan error = device_delete_child(dev, child); 29510b59a9bSPeter Grehan if (error) 29610b59a9bSPeter Grehan return (error); 29710b59a9bSPeter Grehan sc->vtpci_child_dev = NULL; 29810b59a9bSPeter Grehan } 29910b59a9bSPeter Grehan 30010b59a9bSPeter Grehan vtpci_reset(sc); 30110b59a9bSPeter Grehan 30210b59a9bSPeter Grehan if (sc->vtpci_msix_res != NULL) { 30310b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(1), 30410b59a9bSPeter Grehan sc->vtpci_msix_res); 30510b59a9bSPeter Grehan sc->vtpci_msix_res = NULL; 30610b59a9bSPeter Grehan } 30710b59a9bSPeter Grehan 30810b59a9bSPeter Grehan if (sc->vtpci_res != NULL) { 30910b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), 31010b59a9bSPeter Grehan sc->vtpci_res); 31110b59a9bSPeter Grehan sc->vtpci_res = NULL; 31210b59a9bSPeter Grehan } 31310b59a9bSPeter Grehan 31410b59a9bSPeter Grehan return (0); 31510b59a9bSPeter Grehan } 31610b59a9bSPeter Grehan 31710b59a9bSPeter Grehan static int 31810b59a9bSPeter Grehan vtpci_suspend(device_t dev) 31910b59a9bSPeter Grehan { 32010b59a9bSPeter Grehan 32110b59a9bSPeter Grehan return (bus_generic_suspend(dev)); 32210b59a9bSPeter Grehan } 32310b59a9bSPeter Grehan 32410b59a9bSPeter Grehan static int 32510b59a9bSPeter Grehan vtpci_resume(device_t dev) 32610b59a9bSPeter Grehan { 32710b59a9bSPeter Grehan 32810b59a9bSPeter Grehan return (bus_generic_resume(dev)); 32910b59a9bSPeter Grehan } 33010b59a9bSPeter Grehan 33110b59a9bSPeter Grehan static int 33210b59a9bSPeter Grehan vtpci_shutdown(device_t dev) 33310b59a9bSPeter Grehan { 33410b59a9bSPeter Grehan 33510b59a9bSPeter Grehan (void) bus_generic_shutdown(dev); 33610b59a9bSPeter Grehan /* Forcibly stop the host device. */ 33710b59a9bSPeter Grehan vtpci_stop(dev); 33810b59a9bSPeter Grehan 33910b59a9bSPeter Grehan return (0); 34010b59a9bSPeter Grehan } 34110b59a9bSPeter Grehan 34210b59a9bSPeter Grehan static void 34310b59a9bSPeter Grehan vtpci_driver_added(device_t dev, driver_t *driver) 34410b59a9bSPeter Grehan { 34510b59a9bSPeter Grehan struct vtpci_softc *sc; 34610b59a9bSPeter Grehan 34710b59a9bSPeter Grehan sc = device_get_softc(dev); 34810b59a9bSPeter Grehan 34910b59a9bSPeter Grehan vtpci_probe_and_attach_child(sc); 35010b59a9bSPeter Grehan } 35110b59a9bSPeter Grehan 35210b59a9bSPeter Grehan static void 35310b59a9bSPeter Grehan vtpci_child_detached(device_t dev, device_t child) 35410b59a9bSPeter Grehan { 35510b59a9bSPeter Grehan struct vtpci_softc *sc; 35610b59a9bSPeter Grehan 35710b59a9bSPeter Grehan sc = device_get_softc(dev); 35810b59a9bSPeter Grehan 35910b59a9bSPeter Grehan vtpci_reset(sc); 36010b59a9bSPeter Grehan vtpci_release_child_resources(sc); 36110b59a9bSPeter Grehan } 36210b59a9bSPeter Grehan 36310b59a9bSPeter Grehan static int 36410b59a9bSPeter Grehan vtpci_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 36510b59a9bSPeter Grehan { 36610b59a9bSPeter Grehan struct vtpci_softc *sc; 36710b59a9bSPeter Grehan 36810b59a9bSPeter Grehan sc = device_get_softc(dev); 36910b59a9bSPeter Grehan 37010b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 37110b59a9bSPeter Grehan return (ENOENT); 37210b59a9bSPeter Grehan 37310b59a9bSPeter Grehan switch (index) { 37410b59a9bSPeter Grehan case VIRTIO_IVAR_DEVTYPE: 37510b59a9bSPeter Grehan *result = pci_get_subdevice(dev); 37610b59a9bSPeter Grehan break; 37710b59a9bSPeter Grehan default: 37810b59a9bSPeter Grehan return (ENOENT); 37910b59a9bSPeter Grehan } 38010b59a9bSPeter Grehan 38110b59a9bSPeter Grehan return (0); 38210b59a9bSPeter Grehan } 38310b59a9bSPeter Grehan 38410b59a9bSPeter Grehan static int 38510b59a9bSPeter Grehan vtpci_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 38610b59a9bSPeter Grehan { 38710b59a9bSPeter Grehan struct vtpci_softc *sc; 38810b59a9bSPeter Grehan 38910b59a9bSPeter Grehan sc = device_get_softc(dev); 39010b59a9bSPeter Grehan 39110b59a9bSPeter Grehan if (sc->vtpci_child_dev != child) 39210b59a9bSPeter Grehan return (ENOENT); 39310b59a9bSPeter Grehan 39410b59a9bSPeter Grehan switch (index) { 39510b59a9bSPeter Grehan case VIRTIO_IVAR_FEATURE_DESC: 39610b59a9bSPeter Grehan sc->vtpci_child_feat_desc = (void *) value; 39710b59a9bSPeter Grehan break; 39810b59a9bSPeter Grehan default: 39910b59a9bSPeter Grehan return (ENOENT); 40010b59a9bSPeter Grehan } 40110b59a9bSPeter Grehan 40210b59a9bSPeter Grehan return (0); 40310b59a9bSPeter Grehan } 40410b59a9bSPeter Grehan 40510b59a9bSPeter Grehan static uint64_t 40610b59a9bSPeter Grehan vtpci_negotiate_features(device_t dev, uint64_t child_features) 40710b59a9bSPeter Grehan { 40810b59a9bSPeter Grehan struct vtpci_softc *sc; 40910b59a9bSPeter Grehan uint64_t host_features, features; 41010b59a9bSPeter Grehan 41110b59a9bSPeter Grehan sc = device_get_softc(dev); 41210b59a9bSPeter Grehan 41310b59a9bSPeter Grehan host_features = vtpci_read_config_4(sc, VIRTIO_PCI_HOST_FEATURES); 41410b59a9bSPeter Grehan vtpci_describe_features(sc, "host", host_features); 41510b59a9bSPeter Grehan 41610b59a9bSPeter Grehan /* 41710b59a9bSPeter Grehan * Limit negotiated features to what the driver, virtqueue, and 41810b59a9bSPeter Grehan * host all support. 41910b59a9bSPeter Grehan */ 42010b59a9bSPeter Grehan features = host_features & child_features; 42110b59a9bSPeter Grehan features = virtqueue_filter_features(features); 42210b59a9bSPeter Grehan sc->vtpci_features = features; 42310b59a9bSPeter Grehan 42410b59a9bSPeter Grehan vtpci_describe_features(sc, "negotiated", features); 42510b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_GUEST_FEATURES, features); 42610b59a9bSPeter Grehan 42710b59a9bSPeter Grehan return (features); 42810b59a9bSPeter Grehan } 42910b59a9bSPeter Grehan 43010b59a9bSPeter Grehan static int 43110b59a9bSPeter Grehan vtpci_with_feature(device_t dev, uint64_t feature) 43210b59a9bSPeter Grehan { 43310b59a9bSPeter Grehan struct vtpci_softc *sc; 43410b59a9bSPeter Grehan 43510b59a9bSPeter Grehan sc = device_get_softc(dev); 43610b59a9bSPeter Grehan 43710b59a9bSPeter Grehan return ((sc->vtpci_features & feature) != 0); 43810b59a9bSPeter Grehan } 43910b59a9bSPeter Grehan 44010b59a9bSPeter Grehan static int 44110b59a9bSPeter Grehan vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, 44210b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 44310b59a9bSPeter Grehan { 44410b59a9bSPeter Grehan struct vtpci_softc *sc; 44510b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 44610b59a9bSPeter Grehan struct vq_alloc_info *info; 44710b59a9bSPeter Grehan int queue, error; 44810b59a9bSPeter Grehan uint16_t vq_size; 44910b59a9bSPeter Grehan 45010b59a9bSPeter Grehan sc = device_get_softc(dev); 45110b59a9bSPeter Grehan 45210b59a9bSPeter Grehan if (sc->vtpci_nvqs != 0 || nvqs <= 0 || 45310b59a9bSPeter Grehan nvqs > VIRTIO_MAX_VIRTQUEUES) 45410b59a9bSPeter Grehan return (EINVAL); 45510b59a9bSPeter Grehan 45610b59a9bSPeter Grehan error = vtpci_alloc_interrupts(sc, flags, nvqs, vq_info); 45710b59a9bSPeter Grehan if (error) { 45810b59a9bSPeter Grehan device_printf(dev, "cannot allocate interrupts\n"); 45910b59a9bSPeter Grehan return (error); 46010b59a9bSPeter Grehan } 46110b59a9bSPeter Grehan 46210b59a9bSPeter Grehan if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 46310b59a9bSPeter Grehan error = vtpci_register_msix_vector(sc, 46410b59a9bSPeter Grehan VIRTIO_MSI_CONFIG_VECTOR, 0); 46510b59a9bSPeter Grehan if (error) 46610b59a9bSPeter Grehan return (error); 46710b59a9bSPeter Grehan } 46810b59a9bSPeter Grehan 46910b59a9bSPeter Grehan for (queue = 0; queue < nvqs; queue++) { 47010b59a9bSPeter Grehan vqx = &sc->vtpci_vqx[queue]; 47110b59a9bSPeter Grehan info = &vq_info[queue]; 47210b59a9bSPeter Grehan 47310b59a9bSPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 47410b59a9bSPeter Grehan 47510b59a9bSPeter Grehan vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 47610b59a9bSPeter Grehan error = virtqueue_alloc(dev, queue, vq_size, 47710b59a9bSPeter Grehan VIRTIO_PCI_VRING_ALIGN, 0xFFFFFFFFUL, info, &vqx->vq); 47810b59a9bSPeter Grehan if (error) 47910b59a9bSPeter Grehan return (error); 48010b59a9bSPeter Grehan 48110b59a9bSPeter Grehan if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 48210b59a9bSPeter Grehan error = vtpci_register_msix_vector(sc, 48310b59a9bSPeter Grehan VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx); 48410b59a9bSPeter Grehan if (error) 48510b59a9bSPeter Grehan return (error); 48610b59a9bSPeter Grehan } 48710b59a9bSPeter Grehan 48810b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 48910b59a9bSPeter Grehan virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 49010b59a9bSPeter Grehan 49110b59a9bSPeter Grehan *info->vqai_vq = vqx->vq; 49210b59a9bSPeter Grehan sc->vtpci_nvqs++; 49310b59a9bSPeter Grehan } 49410b59a9bSPeter Grehan 49510b59a9bSPeter Grehan return (0); 49610b59a9bSPeter Grehan } 49710b59a9bSPeter Grehan 49810b59a9bSPeter Grehan static int 49910b59a9bSPeter Grehan vtpci_setup_intr(device_t dev, enum intr_type type) 50010b59a9bSPeter Grehan { 50110b59a9bSPeter Grehan struct vtpci_softc *sc; 50210b59a9bSPeter Grehan struct vtpci_intr_resource *ires; 50310b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 50410b59a9bSPeter Grehan int i, flags, error; 50510b59a9bSPeter Grehan 50610b59a9bSPeter Grehan sc = device_get_softc(dev); 50710b59a9bSPeter Grehan flags = type | INTR_MPSAFE; 50810b59a9bSPeter Grehan ires = &sc->vtpci_intr_res[0]; 50910b59a9bSPeter Grehan 51010b59a9bSPeter Grehan if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) == 0) { 51110b59a9bSPeter Grehan error = bus_setup_intr(dev, ires->irq, flags, 51210b59a9bSPeter Grehan vtpci_legacy_intr, NULL, sc, &ires->intrhand); 51310b59a9bSPeter Grehan 51410b59a9bSPeter Grehan return (error); 51510b59a9bSPeter Grehan } 51610b59a9bSPeter Grehan 51710b59a9bSPeter Grehan error = bus_setup_intr(dev, ires->irq, flags, vtpci_config_intr, 51810b59a9bSPeter Grehan NULL, sc, &ires->intrhand); 51910b59a9bSPeter Grehan if (error) 52010b59a9bSPeter Grehan return (error); 52110b59a9bSPeter Grehan 52210b59a9bSPeter Grehan if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) { 52310b59a9bSPeter Grehan ires = &sc->vtpci_intr_res[1]; 52410b59a9bSPeter Grehan error = bus_setup_intr(dev, ires->irq, flags, 52510b59a9bSPeter Grehan vtpci_vq_shared_intr, NULL, sc, &ires->intrhand); 52610b59a9bSPeter Grehan 52710b59a9bSPeter Grehan return (error); 52810b59a9bSPeter Grehan } 52910b59a9bSPeter Grehan 53010b59a9bSPeter Grehan /* Setup an interrupt handler for each virtqueue. */ 53110b59a9bSPeter Grehan for (i = 0; i < sc->vtpci_nvqs; i++) { 53210b59a9bSPeter Grehan vqx = &sc->vtpci_vqx[i]; 53310b59a9bSPeter Grehan if (vqx->ires_idx < 1) 53410b59a9bSPeter Grehan continue; 53510b59a9bSPeter Grehan 53610b59a9bSPeter Grehan ires = &sc->vtpci_intr_res[vqx->ires_idx]; 53710b59a9bSPeter Grehan error = bus_setup_intr(dev, ires->irq, flags, 53810b59a9bSPeter Grehan vtpci_vq_intr, NULL, vqx->vq, &ires->intrhand); 53910b59a9bSPeter Grehan if (error) 54010b59a9bSPeter Grehan return (error); 54110b59a9bSPeter Grehan } 54210b59a9bSPeter Grehan 54310b59a9bSPeter Grehan return (0); 54410b59a9bSPeter Grehan } 54510b59a9bSPeter Grehan 54610b59a9bSPeter Grehan static void 54710b59a9bSPeter Grehan vtpci_stop(device_t dev) 54810b59a9bSPeter Grehan { 54910b59a9bSPeter Grehan 55010b59a9bSPeter Grehan vtpci_reset(device_get_softc(dev)); 55110b59a9bSPeter Grehan } 55210b59a9bSPeter Grehan 55310b59a9bSPeter Grehan static int 55410b59a9bSPeter Grehan vtpci_reinit(device_t dev, uint64_t features) 55510b59a9bSPeter Grehan { 55610b59a9bSPeter Grehan struct vtpci_softc *sc; 55710b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 55810b59a9bSPeter Grehan struct virtqueue *vq; 55910b59a9bSPeter Grehan int queue, error; 56010b59a9bSPeter Grehan uint16_t vq_size; 56110b59a9bSPeter Grehan 56210b59a9bSPeter Grehan sc = device_get_softc(dev); 56310b59a9bSPeter Grehan 56410b59a9bSPeter Grehan /* 56510b59a9bSPeter Grehan * Redrive the device initialization. This is a bit of an abuse 56610b59a9bSPeter Grehan * of the specification, but both VirtualBox and QEMU/KVM seem 56710b59a9bSPeter Grehan * to play nice. We do not allow the host device to change from 56810b59a9bSPeter Grehan * what was originally negotiated beyond what the guest driver 56910b59a9bSPeter Grehan * changed (MSIX state should not change, number of virtqueues 57010b59a9bSPeter Grehan * and their size remain the same, etc). 57110b59a9bSPeter Grehan */ 57210b59a9bSPeter Grehan 57310b59a9bSPeter Grehan if (vtpci_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 57410b59a9bSPeter Grehan vtpci_stop(dev); 57510b59a9bSPeter Grehan 57610b59a9bSPeter Grehan /* 57710b59a9bSPeter Grehan * Quickly drive the status through ACK and DRIVER. The device 57810b59a9bSPeter Grehan * does not become usable again until vtpci_reinit_complete(). 57910b59a9bSPeter Grehan */ 58010b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 58110b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 58210b59a9bSPeter Grehan 58310b59a9bSPeter Grehan vtpci_negotiate_features(dev, features); 58410b59a9bSPeter Grehan 58510b59a9bSPeter Grehan if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 58610b59a9bSPeter Grehan error = vtpci_register_msix_vector(sc, 58710b59a9bSPeter Grehan VIRTIO_MSI_CONFIG_VECTOR, 0); 58810b59a9bSPeter Grehan if (error) 58910b59a9bSPeter Grehan return (error); 59010b59a9bSPeter Grehan } 59110b59a9bSPeter Grehan 59210b59a9bSPeter Grehan for (queue = 0; queue < sc->vtpci_nvqs; queue++) { 59310b59a9bSPeter Grehan vqx = &sc->vtpci_vqx[queue]; 59410b59a9bSPeter Grehan vq = vqx->vq; 59510b59a9bSPeter Grehan 59610b59a9bSPeter Grehan KASSERT(vq != NULL, ("vq %d not allocated", queue)); 59710b59a9bSPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_SEL, queue); 59810b59a9bSPeter Grehan 59910b59a9bSPeter Grehan vq_size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); 60010b59a9bSPeter Grehan error = virtqueue_reinit(vq, vq_size); 60110b59a9bSPeter Grehan if (error) 60210b59a9bSPeter Grehan return (error); 60310b59a9bSPeter Grehan 60410b59a9bSPeter Grehan if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 60510b59a9bSPeter Grehan error = vtpci_register_msix_vector(sc, 60610b59a9bSPeter Grehan VIRTIO_MSI_QUEUE_VECTOR, vqx->ires_idx); 60710b59a9bSPeter Grehan if (error) 60810b59a9bSPeter Grehan return (error); 60910b59a9bSPeter Grehan } 61010b59a9bSPeter Grehan 61110b59a9bSPeter Grehan vtpci_write_config_4(sc, VIRTIO_PCI_QUEUE_PFN, 61210b59a9bSPeter Grehan virtqueue_paddr(vqx->vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT); 61310b59a9bSPeter Grehan } 61410b59a9bSPeter Grehan 61510b59a9bSPeter Grehan return (0); 61610b59a9bSPeter Grehan } 61710b59a9bSPeter Grehan 61810b59a9bSPeter Grehan static void 61910b59a9bSPeter Grehan vtpci_reinit_complete(device_t dev) 62010b59a9bSPeter Grehan { 62110b59a9bSPeter Grehan 62210b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 62310b59a9bSPeter Grehan } 62410b59a9bSPeter Grehan 62510b59a9bSPeter Grehan static void 62610b59a9bSPeter Grehan vtpci_notify_virtqueue(device_t dev, uint16_t queue) 62710b59a9bSPeter Grehan { 62810b59a9bSPeter Grehan struct vtpci_softc *sc; 62910b59a9bSPeter Grehan 63010b59a9bSPeter Grehan sc = device_get_softc(dev); 63110b59a9bSPeter Grehan 63210b59a9bSPeter Grehan vtpci_write_config_2(sc, VIRTIO_PCI_QUEUE_NOTIFY, queue); 63310b59a9bSPeter Grehan } 63410b59a9bSPeter Grehan 63510b59a9bSPeter Grehan static uint8_t 63610b59a9bSPeter Grehan vtpci_get_status(device_t dev) 63710b59a9bSPeter Grehan { 63810b59a9bSPeter Grehan struct vtpci_softc *sc; 63910b59a9bSPeter Grehan 64010b59a9bSPeter Grehan sc = device_get_softc(dev); 64110b59a9bSPeter Grehan 64210b59a9bSPeter Grehan return (vtpci_read_config_1(sc, VIRTIO_PCI_STATUS)); 64310b59a9bSPeter Grehan } 64410b59a9bSPeter Grehan 64510b59a9bSPeter Grehan static void 64610b59a9bSPeter Grehan vtpci_set_status(device_t dev, uint8_t status) 64710b59a9bSPeter Grehan { 64810b59a9bSPeter Grehan struct vtpci_softc *sc; 64910b59a9bSPeter Grehan 65010b59a9bSPeter Grehan sc = device_get_softc(dev); 65110b59a9bSPeter Grehan 65210b59a9bSPeter Grehan if (status != VIRTIO_CONFIG_STATUS_RESET) 65310b59a9bSPeter Grehan status |= vtpci_get_status(dev); 65410b59a9bSPeter Grehan 65510b59a9bSPeter Grehan vtpci_write_config_1(sc, VIRTIO_PCI_STATUS, status); 65610b59a9bSPeter Grehan } 65710b59a9bSPeter Grehan 65810b59a9bSPeter Grehan static void 65910b59a9bSPeter Grehan vtpci_read_dev_config(device_t dev, bus_size_t offset, 66010b59a9bSPeter Grehan void *dst, int length) 66110b59a9bSPeter Grehan { 66210b59a9bSPeter Grehan struct vtpci_softc *sc; 66310b59a9bSPeter Grehan bus_size_t off; 66410b59a9bSPeter Grehan uint8_t *d; 66510b59a9bSPeter Grehan int size; 66610b59a9bSPeter Grehan 66710b59a9bSPeter Grehan sc = device_get_softc(dev); 66810b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 66910b59a9bSPeter Grehan 67010b59a9bSPeter Grehan for (d = dst; length > 0; d += size, off += size, length -= size) { 67110b59a9bSPeter Grehan if (length >= 4) { 67210b59a9bSPeter Grehan size = 4; 67310b59a9bSPeter Grehan *(uint32_t *)d = vtpci_read_config_4(sc, off); 67410b59a9bSPeter Grehan } else if (length >= 2) { 67510b59a9bSPeter Grehan size = 2; 67610b59a9bSPeter Grehan *(uint16_t *)d = vtpci_read_config_2(sc, off); 67710b59a9bSPeter Grehan } else { 67810b59a9bSPeter Grehan size = 1; 67910b59a9bSPeter Grehan *d = vtpci_read_config_1(sc, off); 68010b59a9bSPeter Grehan } 68110b59a9bSPeter Grehan } 68210b59a9bSPeter Grehan } 68310b59a9bSPeter Grehan 68410b59a9bSPeter Grehan static void 68510b59a9bSPeter Grehan vtpci_write_dev_config(device_t dev, bus_size_t offset, 68610b59a9bSPeter Grehan void *src, int length) 68710b59a9bSPeter Grehan { 68810b59a9bSPeter Grehan struct vtpci_softc *sc; 68910b59a9bSPeter Grehan bus_size_t off; 69010b59a9bSPeter Grehan uint8_t *s; 69110b59a9bSPeter Grehan int size; 69210b59a9bSPeter Grehan 69310b59a9bSPeter Grehan sc = device_get_softc(dev); 69410b59a9bSPeter Grehan off = VIRTIO_PCI_CONFIG(sc) + offset; 69510b59a9bSPeter Grehan 69610b59a9bSPeter Grehan for (s = src; length > 0; s += size, off += size, length -= size) { 69710b59a9bSPeter Grehan if (length >= 4) { 69810b59a9bSPeter Grehan size = 4; 69910b59a9bSPeter Grehan vtpci_write_config_4(sc, off, *(uint32_t *)s); 70010b59a9bSPeter Grehan } else if (length >= 2) { 70110b59a9bSPeter Grehan size = 2; 70210b59a9bSPeter Grehan vtpci_write_config_2(sc, off, *(uint16_t *)s); 70310b59a9bSPeter Grehan } else { 70410b59a9bSPeter Grehan size = 1; 70510b59a9bSPeter Grehan vtpci_write_config_1(sc, off, *s); 70610b59a9bSPeter Grehan } 70710b59a9bSPeter Grehan } 70810b59a9bSPeter Grehan } 70910b59a9bSPeter Grehan 71010b59a9bSPeter Grehan static void 71110b59a9bSPeter Grehan vtpci_describe_features(struct vtpci_softc *sc, const char *msg, 71210b59a9bSPeter Grehan uint64_t features) 71310b59a9bSPeter Grehan { 71410b59a9bSPeter Grehan device_t dev, child; 71510b59a9bSPeter Grehan 71610b59a9bSPeter Grehan dev = sc->vtpci_dev; 71710b59a9bSPeter Grehan child = sc->vtpci_child_dev; 71810b59a9bSPeter Grehan 71910b59a9bSPeter Grehan if (device_is_attached(child) && bootverbose == 0) 72010b59a9bSPeter Grehan return; 72110b59a9bSPeter Grehan 72210b59a9bSPeter Grehan virtio_describe(dev, msg, features, sc->vtpci_child_feat_desc); 72310b59a9bSPeter Grehan } 72410b59a9bSPeter Grehan 72510b59a9bSPeter Grehan static void 72610b59a9bSPeter Grehan vtpci_probe_and_attach_child(struct vtpci_softc *sc) 72710b59a9bSPeter Grehan { 72810b59a9bSPeter Grehan device_t dev, child; 72910b59a9bSPeter Grehan 73010b59a9bSPeter Grehan dev = sc->vtpci_dev; 73110b59a9bSPeter Grehan child = sc->vtpci_child_dev; 73210b59a9bSPeter Grehan 73310b59a9bSPeter Grehan if (child == NULL) 73410b59a9bSPeter Grehan return; 73510b59a9bSPeter Grehan 73610b59a9bSPeter Grehan if (device_get_state(child) != DS_NOTPRESENT) 73710b59a9bSPeter Grehan return; 73810b59a9bSPeter Grehan 73910b59a9bSPeter Grehan if (device_probe(child) != 0) 74010b59a9bSPeter Grehan return; 74110b59a9bSPeter Grehan 74210b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 74310b59a9bSPeter Grehan if (device_attach(child) != 0) { 74410b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 74510b59a9bSPeter Grehan vtpci_reset(sc); 74610b59a9bSPeter Grehan vtpci_release_child_resources(sc); 74710b59a9bSPeter Grehan 74810b59a9bSPeter Grehan /* Reset status for future attempt. */ 74910b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 75010b59a9bSPeter Grehan } else 75110b59a9bSPeter Grehan vtpci_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 75210b59a9bSPeter Grehan } 75310b59a9bSPeter Grehan 75410b59a9bSPeter Grehan static int 75510b59a9bSPeter Grehan vtpci_alloc_interrupts(struct vtpci_softc *sc, int flags, int nvqs, 75610b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 75710b59a9bSPeter Grehan { 75810b59a9bSPeter Grehan int i, nvectors, error; 75910b59a9bSPeter Grehan 76010b59a9bSPeter Grehan /* 76110b59a9bSPeter Grehan * Only allocate a vector for virtqueues that are actually 76210b59a9bSPeter Grehan * expecting an interrupt. 76310b59a9bSPeter Grehan */ 76410b59a9bSPeter Grehan for (nvectors = 0, i = 0; i < nvqs; i++) 76510b59a9bSPeter Grehan if (vq_info[i].vqai_intr != NULL) 76610b59a9bSPeter Grehan nvectors++; 76710b59a9bSPeter Grehan 76810b59a9bSPeter Grehan if (vtpci_disable_msix != 0 || 76910b59a9bSPeter Grehan sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSIX || 77010b59a9bSPeter Grehan flags & VIRTIO_ALLOC_VQS_DISABLE_MSIX || 77110b59a9bSPeter Grehan vtpci_alloc_msix(sc, nvectors) != 0) { 77210b59a9bSPeter Grehan /* 77310b59a9bSPeter Grehan * Use MSI interrupts if available. Otherwise, we fallback 77410b59a9bSPeter Grehan * to legacy interrupts. 77510b59a9bSPeter Grehan */ 77610b59a9bSPeter Grehan if ((sc->vtpci_flags & VIRTIO_PCI_FLAG_NO_MSI) == 0 && 77710b59a9bSPeter Grehan vtpci_alloc_msi(sc) == 0) 77810b59a9bSPeter Grehan sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSI; 77910b59a9bSPeter Grehan 78010b59a9bSPeter Grehan sc->vtpci_nintr_res = 1; 78110b59a9bSPeter Grehan } 78210b59a9bSPeter Grehan 78310b59a9bSPeter Grehan error = vtpci_alloc_intr_resources(sc, nvqs, vq_info); 78410b59a9bSPeter Grehan 78510b59a9bSPeter Grehan return (error); 78610b59a9bSPeter Grehan } 78710b59a9bSPeter Grehan 78810b59a9bSPeter Grehan static int 78910b59a9bSPeter Grehan vtpci_alloc_intr_resources(struct vtpci_softc *sc, int nvqs, 79010b59a9bSPeter Grehan struct vq_alloc_info *vq_info) 79110b59a9bSPeter Grehan { 79210b59a9bSPeter Grehan device_t dev; 79310b59a9bSPeter Grehan struct resource *irq; 79410b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 79510b59a9bSPeter Grehan int i, rid, flags, res_idx; 79610b59a9bSPeter Grehan 79710b59a9bSPeter Grehan dev = sc->vtpci_dev; 79810b59a9bSPeter Grehan flags = RF_ACTIVE; 79910b59a9bSPeter Grehan 80010b59a9bSPeter Grehan if ((sc->vtpci_flags & 80110b59a9bSPeter Grehan (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) == 0) { 80210b59a9bSPeter Grehan rid = 0; 80310b59a9bSPeter Grehan flags |= RF_SHAREABLE; 80410b59a9bSPeter Grehan } else 80510b59a9bSPeter Grehan rid = 1; 80610b59a9bSPeter Grehan 80710b59a9bSPeter Grehan for (i = 0; i < sc->vtpci_nintr_res; i++) { 80810b59a9bSPeter Grehan irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, flags); 80910b59a9bSPeter Grehan if (irq == NULL) 81010b59a9bSPeter Grehan return (ENXIO); 81110b59a9bSPeter Grehan 81210b59a9bSPeter Grehan sc->vtpci_intr_res[i].irq = irq; 81310b59a9bSPeter Grehan sc->vtpci_intr_res[i].rid = rid++; 81410b59a9bSPeter Grehan } 81510b59a9bSPeter Grehan 81610b59a9bSPeter Grehan /* 81710b59a9bSPeter Grehan * Map the virtqueue into the correct index in vq_intr_res[]. Note the 81810b59a9bSPeter Grehan * first index is reserved for configuration changes notifications. 81910b59a9bSPeter Grehan */ 82010b59a9bSPeter Grehan for (i = 0, res_idx = 1; i < nvqs; i++) { 82110b59a9bSPeter Grehan vqx = &sc->vtpci_vqx[i]; 82210b59a9bSPeter Grehan 82310b59a9bSPeter Grehan if (sc->vtpci_flags & VIRTIO_PCI_FLAG_MSIX) { 82410b59a9bSPeter Grehan if (vq_info[i].vqai_intr == NULL) 82510b59a9bSPeter Grehan vqx->ires_idx = -1; 82610b59a9bSPeter Grehan else if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) 82710b59a9bSPeter Grehan vqx->ires_idx = res_idx; 82810b59a9bSPeter Grehan else 82910b59a9bSPeter Grehan vqx->ires_idx = res_idx++; 83010b59a9bSPeter Grehan } else 83110b59a9bSPeter Grehan vqx->ires_idx = -1; 83210b59a9bSPeter Grehan } 83310b59a9bSPeter Grehan 83410b59a9bSPeter Grehan return (0); 83510b59a9bSPeter Grehan } 83610b59a9bSPeter Grehan 83710b59a9bSPeter Grehan static int 83810b59a9bSPeter Grehan vtpci_alloc_msi(struct vtpci_softc *sc) 83910b59a9bSPeter Grehan { 84010b59a9bSPeter Grehan device_t dev; 84110b59a9bSPeter Grehan int nmsi, cnt; 84210b59a9bSPeter Grehan 84310b59a9bSPeter Grehan dev = sc->vtpci_dev; 84410b59a9bSPeter Grehan nmsi = pci_msi_count(dev); 84510b59a9bSPeter Grehan 84610b59a9bSPeter Grehan if (nmsi < 1) 84710b59a9bSPeter Grehan return (1); 84810b59a9bSPeter Grehan 84910b59a9bSPeter Grehan cnt = 1; 85010b59a9bSPeter Grehan if (pci_alloc_msi(dev, &cnt) == 0 && cnt == 1) 85110b59a9bSPeter Grehan return (0); 85210b59a9bSPeter Grehan 85310b59a9bSPeter Grehan return (1); 85410b59a9bSPeter Grehan } 85510b59a9bSPeter Grehan 85610b59a9bSPeter Grehan static int 85710b59a9bSPeter Grehan vtpci_alloc_msix(struct vtpci_softc *sc, int nvectors) 85810b59a9bSPeter Grehan { 85910b59a9bSPeter Grehan device_t dev; 86010b59a9bSPeter Grehan int nmsix, cnt, required; 86110b59a9bSPeter Grehan 86210b59a9bSPeter Grehan dev = sc->vtpci_dev; 86310b59a9bSPeter Grehan 86410b59a9bSPeter Grehan nmsix = pci_msix_count(dev); 86510b59a9bSPeter Grehan if (nmsix < 1) 86610b59a9bSPeter Grehan return (1); 86710b59a9bSPeter Grehan 86810b59a9bSPeter Grehan /* An additional vector is needed for the config changes. */ 86910b59a9bSPeter Grehan required = nvectors + 1; 87010b59a9bSPeter Grehan if (nmsix >= required) { 87110b59a9bSPeter Grehan cnt = required; 87210b59a9bSPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) 87310b59a9bSPeter Grehan goto out; 87410b59a9bSPeter Grehan 87510b59a9bSPeter Grehan pci_release_msi(dev); 87610b59a9bSPeter Grehan } 87710b59a9bSPeter Grehan 87810b59a9bSPeter Grehan /* Attempt shared MSIX configuration. */ 87910b59a9bSPeter Grehan required = 2; 88010b59a9bSPeter Grehan if (nmsix >= required) { 88110b59a9bSPeter Grehan cnt = required; 88210b59a9bSPeter Grehan if (pci_alloc_msix(dev, &cnt) == 0 && cnt >= required) { 88310b59a9bSPeter Grehan sc->vtpci_flags |= VIRTIO_PCI_FLAG_SHARED_MSIX; 88410b59a9bSPeter Grehan goto out; 88510b59a9bSPeter Grehan } 88610b59a9bSPeter Grehan 88710b59a9bSPeter Grehan pci_release_msi(dev); 88810b59a9bSPeter Grehan } 88910b59a9bSPeter Grehan 89010b59a9bSPeter Grehan return (1); 89110b59a9bSPeter Grehan 89210b59a9bSPeter Grehan out: 89310b59a9bSPeter Grehan sc->vtpci_nintr_res = required; 89410b59a9bSPeter Grehan sc->vtpci_flags |= VIRTIO_PCI_FLAG_MSIX; 89510b59a9bSPeter Grehan 89610b59a9bSPeter Grehan if (bootverbose) { 89710b59a9bSPeter Grehan if (sc->vtpci_flags & VIRTIO_PCI_FLAG_SHARED_MSIX) 89810b59a9bSPeter Grehan device_printf(dev, "using shared virtqueue MSIX\n"); 89910b59a9bSPeter Grehan else 90010b59a9bSPeter Grehan device_printf(dev, "using per virtqueue MSIX\n"); 90110b59a9bSPeter Grehan } 90210b59a9bSPeter Grehan 90310b59a9bSPeter Grehan return (0); 90410b59a9bSPeter Grehan } 90510b59a9bSPeter Grehan 90610b59a9bSPeter Grehan static int 90710b59a9bSPeter Grehan vtpci_register_msix_vector(struct vtpci_softc *sc, int offset, int res_idx) 90810b59a9bSPeter Grehan { 90910b59a9bSPeter Grehan device_t dev; 91010b59a9bSPeter Grehan uint16_t vector; 91110b59a9bSPeter Grehan 91210b59a9bSPeter Grehan dev = sc->vtpci_dev; 91310b59a9bSPeter Grehan 91410b59a9bSPeter Grehan if (offset != VIRTIO_MSI_CONFIG_VECTOR && 91510b59a9bSPeter Grehan offset != VIRTIO_MSI_QUEUE_VECTOR) 91610b59a9bSPeter Grehan return (EINVAL); 91710b59a9bSPeter Grehan 91810b59a9bSPeter Grehan if (res_idx != -1) { 91910b59a9bSPeter Grehan /* Map from rid to host vector. */ 92010b59a9bSPeter Grehan vector = sc->vtpci_intr_res[res_idx].rid - 1; 92110b59a9bSPeter Grehan } else 92210b59a9bSPeter Grehan vector = VIRTIO_MSI_NO_VECTOR; 92310b59a9bSPeter Grehan 92410b59a9bSPeter Grehan /* The first resource is special; make sure it is used correctly. */ 92510b59a9bSPeter Grehan if (res_idx == 0) { 92610b59a9bSPeter Grehan KASSERT(vector == 0, ("unexpected config vector")); 92710b59a9bSPeter Grehan KASSERT(offset == VIRTIO_MSI_CONFIG_VECTOR, 92810b59a9bSPeter Grehan ("unexpected config offset")); 92910b59a9bSPeter Grehan } 93010b59a9bSPeter Grehan 93110b59a9bSPeter Grehan vtpci_write_config_2(sc, offset, vector); 93210b59a9bSPeter Grehan 93310b59a9bSPeter Grehan if (vtpci_read_config_2(sc, offset) != vector) { 93410b59a9bSPeter Grehan device_printf(dev, "insufficient host resources for " 93510b59a9bSPeter Grehan "MSIX interrupts\n"); 93610b59a9bSPeter Grehan return (ENODEV); 93710b59a9bSPeter Grehan } 93810b59a9bSPeter Grehan 93910b59a9bSPeter Grehan return (0); 94010b59a9bSPeter Grehan } 94110b59a9bSPeter Grehan 94210b59a9bSPeter Grehan static void 94310b59a9bSPeter Grehan vtpci_free_interrupts(struct vtpci_softc *sc) 94410b59a9bSPeter Grehan { 94510b59a9bSPeter Grehan device_t dev; 94610b59a9bSPeter Grehan struct vtpci_intr_resource *ires; 94710b59a9bSPeter Grehan int i; 94810b59a9bSPeter Grehan 94910b59a9bSPeter Grehan dev = sc->vtpci_dev; 95010b59a9bSPeter Grehan sc->vtpci_nintr_res = 0; 95110b59a9bSPeter Grehan 95210b59a9bSPeter Grehan if (sc->vtpci_flags & (VIRTIO_PCI_FLAG_MSI | VIRTIO_PCI_FLAG_MSIX)) { 95310b59a9bSPeter Grehan pci_release_msi(dev); 95410b59a9bSPeter Grehan sc->vtpci_flags &= ~(VIRTIO_PCI_FLAG_MSI | 95510b59a9bSPeter Grehan VIRTIO_PCI_FLAG_MSIX | VIRTIO_PCI_FLAG_SHARED_MSIX); 95610b59a9bSPeter Grehan } 95710b59a9bSPeter Grehan 95810b59a9bSPeter Grehan for (i = 0; i < 1 + VIRTIO_MAX_VIRTQUEUES; i++) { 95910b59a9bSPeter Grehan ires = &sc->vtpci_intr_res[i]; 96010b59a9bSPeter Grehan 96110b59a9bSPeter Grehan if (ires->intrhand != NULL) { 96210b59a9bSPeter Grehan bus_teardown_intr(dev, ires->irq, ires->intrhand); 96310b59a9bSPeter Grehan ires->intrhand = NULL; 96410b59a9bSPeter Grehan } 96510b59a9bSPeter Grehan 96610b59a9bSPeter Grehan if (ires->irq != NULL) { 96710b59a9bSPeter Grehan bus_release_resource(dev, SYS_RES_IRQ, ires->rid, 96810b59a9bSPeter Grehan ires->irq); 96910b59a9bSPeter Grehan ires->irq = NULL; 97010b59a9bSPeter Grehan } 97110b59a9bSPeter Grehan 97210b59a9bSPeter Grehan ires->rid = -1; 97310b59a9bSPeter Grehan } 97410b59a9bSPeter Grehan } 97510b59a9bSPeter Grehan 97610b59a9bSPeter Grehan static void 97710b59a9bSPeter Grehan vtpci_free_virtqueues(struct vtpci_softc *sc) 97810b59a9bSPeter Grehan { 97910b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 98010b59a9bSPeter Grehan int i; 98110b59a9bSPeter Grehan 98210b59a9bSPeter Grehan sc->vtpci_nvqs = 0; 98310b59a9bSPeter Grehan 98410b59a9bSPeter Grehan for (i = 0; i < VIRTIO_MAX_VIRTQUEUES; i++) { 98510b59a9bSPeter Grehan vqx = &sc->vtpci_vqx[i]; 98610b59a9bSPeter Grehan 98710b59a9bSPeter Grehan if (vqx->vq != NULL) { 98810b59a9bSPeter Grehan virtqueue_free(vqx->vq); 98910b59a9bSPeter Grehan vqx->vq = NULL; 99010b59a9bSPeter Grehan } 99110b59a9bSPeter Grehan } 99210b59a9bSPeter Grehan } 99310b59a9bSPeter Grehan 99410b59a9bSPeter Grehan static void 99510b59a9bSPeter Grehan vtpci_release_child_resources(struct vtpci_softc *sc) 99610b59a9bSPeter Grehan { 99710b59a9bSPeter Grehan 99810b59a9bSPeter Grehan vtpci_free_interrupts(sc); 99910b59a9bSPeter Grehan vtpci_free_virtqueues(sc); 100010b59a9bSPeter Grehan } 100110b59a9bSPeter Grehan 100210b59a9bSPeter Grehan static void 100310b59a9bSPeter Grehan vtpci_reset(struct vtpci_softc *sc) 100410b59a9bSPeter Grehan { 100510b59a9bSPeter Grehan 100610b59a9bSPeter Grehan /* 100710b59a9bSPeter Grehan * Setting the status to RESET sets the host device to 100810b59a9bSPeter Grehan * the original, uninitialized state. 100910b59a9bSPeter Grehan */ 101010b59a9bSPeter Grehan vtpci_set_status(sc->vtpci_dev, VIRTIO_CONFIG_STATUS_RESET); 101110b59a9bSPeter Grehan } 101210b59a9bSPeter Grehan 101310b59a9bSPeter Grehan static int 101410b59a9bSPeter Grehan vtpci_legacy_intr(void *xsc) 101510b59a9bSPeter Grehan { 101610b59a9bSPeter Grehan struct vtpci_softc *sc; 101710b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 101810b59a9bSPeter Grehan int i; 101910b59a9bSPeter Grehan uint8_t isr; 102010b59a9bSPeter Grehan 102110b59a9bSPeter Grehan sc = xsc; 102210b59a9bSPeter Grehan vqx = &sc->vtpci_vqx[0]; 102310b59a9bSPeter Grehan 102410b59a9bSPeter Grehan /* Reading the ISR also clears it. */ 102510b59a9bSPeter Grehan isr = vtpci_read_config_1(sc, VIRTIO_PCI_ISR); 102610b59a9bSPeter Grehan 102710b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_CONFIG) 102810b59a9bSPeter Grehan vtpci_config_intr(sc); 102910b59a9bSPeter Grehan 103010b59a9bSPeter Grehan if (isr & VIRTIO_PCI_ISR_INTR) 103110b59a9bSPeter Grehan for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) 103210b59a9bSPeter Grehan virtqueue_intr(vqx->vq); 103310b59a9bSPeter Grehan 103410b59a9bSPeter Grehan return (isr ? FILTER_HANDLED : FILTER_STRAY); 103510b59a9bSPeter Grehan } 103610b59a9bSPeter Grehan 103710b59a9bSPeter Grehan static int 103810b59a9bSPeter Grehan vtpci_vq_shared_intr(void *xsc) 103910b59a9bSPeter Grehan { 104010b59a9bSPeter Grehan struct vtpci_softc *sc; 104110b59a9bSPeter Grehan struct vtpci_virtqueue *vqx; 104210b59a9bSPeter Grehan int i, rc; 104310b59a9bSPeter Grehan 104410b59a9bSPeter Grehan rc = 0; 104510b59a9bSPeter Grehan sc = xsc; 104610b59a9bSPeter Grehan vqx = &sc->vtpci_vqx[0]; 104710b59a9bSPeter Grehan 104810b59a9bSPeter Grehan for (i = 0; i < sc->vtpci_nvqs; i++, vqx++) 104910b59a9bSPeter Grehan rc |= virtqueue_intr(vqx->vq); 105010b59a9bSPeter Grehan 105110b59a9bSPeter Grehan return (rc ? FILTER_HANDLED : FILTER_STRAY); 105210b59a9bSPeter Grehan } 105310b59a9bSPeter Grehan 105410b59a9bSPeter Grehan static int 105510b59a9bSPeter Grehan vtpci_vq_intr(void *xvq) 105610b59a9bSPeter Grehan { 105710b59a9bSPeter Grehan struct virtqueue *vq; 105810b59a9bSPeter Grehan int rc; 105910b59a9bSPeter Grehan 106010b59a9bSPeter Grehan vq = xvq; 106110b59a9bSPeter Grehan rc = virtqueue_intr(vq); 106210b59a9bSPeter Grehan 106310b59a9bSPeter Grehan return (rc ? FILTER_HANDLED : FILTER_STRAY); 106410b59a9bSPeter Grehan } 106510b59a9bSPeter Grehan 106610b59a9bSPeter Grehan static int 106710b59a9bSPeter Grehan vtpci_config_intr(void *xsc) 106810b59a9bSPeter Grehan { 106910b59a9bSPeter Grehan struct vtpci_softc *sc; 107010b59a9bSPeter Grehan device_t child; 107110b59a9bSPeter Grehan int rc; 107210b59a9bSPeter Grehan 107310b59a9bSPeter Grehan rc = 0; 107410b59a9bSPeter Grehan sc = xsc; 107510b59a9bSPeter Grehan child = sc->vtpci_child_dev; 107610b59a9bSPeter Grehan 107710b59a9bSPeter Grehan if (child != NULL) 107810b59a9bSPeter Grehan rc = VIRTIO_CONFIG_CHANGE(child); 107910b59a9bSPeter Grehan 108010b59a9bSPeter Grehan return (rc ? FILTER_HANDLED : FILTER_STRAY); 108110b59a9bSPeter Grehan } 1082