1c141c5c6SRuslan Bukin /*- 2c141c5c6SRuslan Bukin * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 34dbff239SAndrew Turner * Copyright (c) 2014 The FreeBSD Foundation 4c141c5c6SRuslan Bukin * All rights reserved. 5c141c5c6SRuslan Bukin * 6c141c5c6SRuslan Bukin * This software was developed by SRI International and the University of 7c141c5c6SRuslan Bukin * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 8c141c5c6SRuslan Bukin * ("CTSRD"), as part of the DARPA CRASH research programme. 9c141c5c6SRuslan Bukin * 104dbff239SAndrew Turner * Portions of this software were developed by Andrew Turner 114dbff239SAndrew Turner * under sponsorship from the FreeBSD Foundation. 124dbff239SAndrew Turner * 13c141c5c6SRuslan Bukin * Redistribution and use in source and binary forms, with or without 14c141c5c6SRuslan Bukin * modification, are permitted provided that the following conditions 15c141c5c6SRuslan Bukin * are met: 16c141c5c6SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 17c141c5c6SRuslan Bukin * notice, this list of conditions and the following disclaimer. 18c141c5c6SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 19c141c5c6SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 20c141c5c6SRuslan Bukin * documentation and/or other materials provided with the distribution. 21c141c5c6SRuslan Bukin * 22c141c5c6SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23c141c5c6SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24c141c5c6SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25c141c5c6SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26c141c5c6SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27c141c5c6SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28c141c5c6SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29c141c5c6SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30c141c5c6SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31c141c5c6SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32c141c5c6SRuslan Bukin * SUCH DAMAGE. 33c141c5c6SRuslan Bukin */ 34c141c5c6SRuslan Bukin 35c141c5c6SRuslan Bukin /* 36c141c5c6SRuslan Bukin * VirtIO MMIO interface. 37c141c5c6SRuslan Bukin * This driver is heavily based on VirtIO PCI interface driver. 38c141c5c6SRuslan Bukin */ 39c141c5c6SRuslan Bukin 40c141c5c6SRuslan Bukin #include <sys/param.h> 41c141c5c6SRuslan Bukin #include <sys/systm.h> 42c141c5c6SRuslan Bukin #include <sys/bus.h> 43c141c5c6SRuslan Bukin #include <sys/kernel.h> 44c141c5c6SRuslan Bukin #include <sys/module.h> 45c141c5c6SRuslan Bukin #include <sys/malloc.h> 46c141c5c6SRuslan Bukin #include <sys/rman.h> 47046096d5SJessica Clarke #include <sys/endian.h> 48c141c5c6SRuslan Bukin 49c141c5c6SRuslan Bukin #include <machine/bus.h> 50c141c5c6SRuslan Bukin #include <machine/resource.h> 51c141c5c6SRuslan Bukin 52c141c5c6SRuslan Bukin #include <dev/virtio/virtio.h> 53c141c5c6SRuslan Bukin #include <dev/virtio/virtqueue.h> 54c141c5c6SRuslan Bukin #include <dev/virtio/mmio/virtio_mmio.h> 55c141c5c6SRuslan Bukin 56c141c5c6SRuslan Bukin #include "virtio_mmio_if.h" 57c141c5c6SRuslan Bukin #include "virtio_bus_if.h" 58c141c5c6SRuslan Bukin #include "virtio_if.h" 59c141c5c6SRuslan Bukin 60c141c5c6SRuslan Bukin struct vtmmio_virtqueue { 61c141c5c6SRuslan Bukin struct virtqueue *vtv_vq; 62c141c5c6SRuslan Bukin int vtv_no_intr; 63c141c5c6SRuslan Bukin }; 64c141c5c6SRuslan Bukin 65c141c5c6SRuslan Bukin static int vtmmio_detach(device_t); 66c141c5c6SRuslan Bukin static int vtmmio_suspend(device_t); 67c141c5c6SRuslan Bukin static int vtmmio_resume(device_t); 68c141c5c6SRuslan Bukin static int vtmmio_shutdown(device_t); 69c141c5c6SRuslan Bukin static void vtmmio_driver_added(device_t, driver_t *); 70c141c5c6SRuslan Bukin static void vtmmio_child_detached(device_t, device_t); 71c141c5c6SRuslan Bukin static int vtmmio_read_ivar(device_t, device_t, int, uintptr_t *); 72c141c5c6SRuslan Bukin static int vtmmio_write_ivar(device_t, device_t, int, uintptr_t); 73c141c5c6SRuslan Bukin static uint64_t vtmmio_negotiate_features(device_t, uint64_t); 74926cedd9SBryan Venteicher static int vtmmio_finalize_features(device_t); 75ccb576a8SMina Galić static bool vtmmio_with_feature(device_t, uint64_t); 76046096d5SJessica Clarke static void vtmmio_set_virtqueue(struct vtmmio_softc *sc, 77046096d5SJessica Clarke struct virtqueue *vq, uint32_t size); 78180c0240SMina Galić static int vtmmio_alloc_virtqueues(device_t, int, 79c141c5c6SRuslan Bukin struct vq_alloc_info *); 80c141c5c6SRuslan Bukin static int vtmmio_setup_intr(device_t, enum intr_type); 81c141c5c6SRuslan Bukin static void vtmmio_stop(device_t); 82156b97faSRuslan Bukin static void vtmmio_poll(device_t); 83c141c5c6SRuslan Bukin static int vtmmio_reinit(device_t, uint64_t); 84c141c5c6SRuslan Bukin static void vtmmio_reinit_complete(device_t); 859da9560cSBryan Venteicher static void vtmmio_notify_virtqueue(device_t, uint16_t, bus_size_t); 86926cedd9SBryan Venteicher static int vtmmio_config_generation(device_t); 87c141c5c6SRuslan Bukin static uint8_t vtmmio_get_status(device_t); 88c141c5c6SRuslan Bukin static void vtmmio_set_status(device_t, uint8_t); 89c141c5c6SRuslan Bukin static void vtmmio_read_dev_config(device_t, bus_size_t, void *, int); 90926cedd9SBryan Venteicher static uint64_t vtmmio_read_dev_config_8(struct vtmmio_softc *, bus_size_t); 916c4f9516SAlex Richardson static void vtmmio_write_dev_config(device_t, bus_size_t, const void *, int); 92c141c5c6SRuslan Bukin static void vtmmio_describe_features(struct vtmmio_softc *, const char *, 93c141c5c6SRuslan Bukin uint64_t); 94c141c5c6SRuslan Bukin static void vtmmio_probe_and_attach_child(struct vtmmio_softc *); 95c141c5c6SRuslan Bukin static int vtmmio_reinit_virtqueue(struct vtmmio_softc *, int); 96c141c5c6SRuslan Bukin static void vtmmio_free_interrupts(struct vtmmio_softc *); 97c141c5c6SRuslan Bukin static void vtmmio_free_virtqueues(struct vtmmio_softc *); 98c141c5c6SRuslan Bukin static void vtmmio_release_child_resources(struct vtmmio_softc *); 99c141c5c6SRuslan Bukin static void vtmmio_reset(struct vtmmio_softc *); 100c141c5c6SRuslan Bukin static void vtmmio_select_virtqueue(struct vtmmio_softc *, int); 101c141c5c6SRuslan Bukin static void vtmmio_vq_intr(void *); 102c141c5c6SRuslan Bukin 103c141c5c6SRuslan Bukin /* 104c141c5c6SRuslan Bukin * I/O port read/write wrappers. 105c141c5c6SRuslan Bukin */ 106c141c5c6SRuslan Bukin #define vtmmio_write_config_1(sc, o, v) \ 1074dbff239SAndrew Turner do { \ 108116b8d2bSRuslan Bukin if (sc->platform != NULL) \ 109116b8d2bSRuslan Bukin VIRTIO_MMIO_PREWRITE(sc->platform, (o), (v)); \ 110c141c5c6SRuslan Bukin bus_write_1((sc)->res[0], (o), (v)); \ 1114dbff239SAndrew Turner if (sc->platform != NULL) \ 112a8098016SRuslan Bukin VIRTIO_MMIO_NOTE(sc->platform, (o), (v)); \ 1134dbff239SAndrew Turner } while (0) 114c141c5c6SRuslan Bukin #define vtmmio_write_config_2(sc, o, v) \ 1154dbff239SAndrew Turner do { \ 116116b8d2bSRuslan Bukin if (sc->platform != NULL) \ 117116b8d2bSRuslan Bukin VIRTIO_MMIO_PREWRITE(sc->platform, (o), (v)); \ 118c141c5c6SRuslan Bukin bus_write_2((sc)->res[0], (o), (v)); \ 1194dbff239SAndrew Turner if (sc->platform != NULL) \ 120a8098016SRuslan Bukin VIRTIO_MMIO_NOTE(sc->platform, (o), (v)); \ 1214dbff239SAndrew Turner } while (0) 122c141c5c6SRuslan Bukin #define vtmmio_write_config_4(sc, o, v) \ 1234dbff239SAndrew Turner do { \ 124116b8d2bSRuslan Bukin if (sc->platform != NULL) \ 125116b8d2bSRuslan Bukin VIRTIO_MMIO_PREWRITE(sc->platform, (o), (v)); \ 126c141c5c6SRuslan Bukin bus_write_4((sc)->res[0], (o), (v)); \ 1274dbff239SAndrew Turner if (sc->platform != NULL) \ 128a8098016SRuslan Bukin VIRTIO_MMIO_NOTE(sc->platform, (o), (v)); \ 1294dbff239SAndrew Turner } while (0) 130c141c5c6SRuslan Bukin 131c141c5c6SRuslan Bukin #define vtmmio_read_config_1(sc, o) \ 132c141c5c6SRuslan Bukin bus_read_1((sc)->res[0], (o)) 133c141c5c6SRuslan Bukin #define vtmmio_read_config_2(sc, o) \ 134c141c5c6SRuslan Bukin bus_read_2((sc)->res[0], (o)) 135c141c5c6SRuslan Bukin #define vtmmio_read_config_4(sc, o) \ 136c141c5c6SRuslan Bukin bus_read_4((sc)->res[0], (o)) 137c141c5c6SRuslan Bukin 138c141c5c6SRuslan Bukin static device_method_t vtmmio_methods[] = { 139c141c5c6SRuslan Bukin /* Device interface. */ 140c141c5c6SRuslan Bukin DEVMETHOD(device_attach, vtmmio_attach), 141c141c5c6SRuslan Bukin DEVMETHOD(device_detach, vtmmio_detach), 142c141c5c6SRuslan Bukin DEVMETHOD(device_suspend, vtmmio_suspend), 143c141c5c6SRuslan Bukin DEVMETHOD(device_resume, vtmmio_resume), 144c141c5c6SRuslan Bukin DEVMETHOD(device_shutdown, vtmmio_shutdown), 145c141c5c6SRuslan Bukin 146c141c5c6SRuslan Bukin /* Bus interface. */ 147c141c5c6SRuslan Bukin DEVMETHOD(bus_driver_added, vtmmio_driver_added), 148c141c5c6SRuslan Bukin DEVMETHOD(bus_child_detached, vtmmio_child_detached), 149ddfc9c4cSWarner Losh DEVMETHOD(bus_child_pnpinfo, virtio_child_pnpinfo), 150c141c5c6SRuslan Bukin DEVMETHOD(bus_read_ivar, vtmmio_read_ivar), 151c141c5c6SRuslan Bukin DEVMETHOD(bus_write_ivar, vtmmio_write_ivar), 152c141c5c6SRuslan Bukin 153c141c5c6SRuslan Bukin /* VirtIO bus interface. */ 154c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_negotiate_features, vtmmio_negotiate_features), 155926cedd9SBryan Venteicher DEVMETHOD(virtio_bus_finalize_features, vtmmio_finalize_features), 156c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_with_feature, vtmmio_with_feature), 157c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_alloc_virtqueues, vtmmio_alloc_virtqueues), 158c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_setup_intr, vtmmio_setup_intr), 159c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_stop, vtmmio_stop), 160156b97faSRuslan Bukin DEVMETHOD(virtio_bus_poll, vtmmio_poll), 161c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_reinit, vtmmio_reinit), 162c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_reinit_complete, vtmmio_reinit_complete), 163c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_notify_vq, vtmmio_notify_virtqueue), 164926cedd9SBryan Venteicher DEVMETHOD(virtio_bus_config_generation, vtmmio_config_generation), 165c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_read_device_config, vtmmio_read_dev_config), 166c141c5c6SRuslan Bukin DEVMETHOD(virtio_bus_write_device_config, vtmmio_write_dev_config), 167c141c5c6SRuslan Bukin 168c141c5c6SRuslan Bukin DEVMETHOD_END 169c141c5c6SRuslan Bukin }; 170c141c5c6SRuslan Bukin 171a3609b82SAndrew Turner DEFINE_CLASS_0(virtio_mmio, vtmmio_driver, vtmmio_methods, 172a3609b82SAndrew Turner sizeof(struct vtmmio_softc)); 173c141c5c6SRuslan Bukin 174c141c5c6SRuslan Bukin MODULE_VERSION(virtio_mmio, 1); 175c141c5c6SRuslan Bukin 176be79a2c6SJessica Clarke int 177be79a2c6SJessica Clarke vtmmio_probe(device_t dev) 178be79a2c6SJessica Clarke { 179be79a2c6SJessica Clarke struct vtmmio_softc *sc; 180be79a2c6SJessica Clarke int rid; 181be79a2c6SJessica Clarke uint32_t magic, version; 182be79a2c6SJessica Clarke 183be79a2c6SJessica Clarke sc = device_get_softc(dev); 184be79a2c6SJessica Clarke 185be79a2c6SJessica Clarke rid = 0; 186be79a2c6SJessica Clarke sc->res[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 187be79a2c6SJessica Clarke RF_ACTIVE); 188be79a2c6SJessica Clarke if (sc->res[0] == NULL) { 189be79a2c6SJessica Clarke device_printf(dev, "Cannot allocate memory window.\n"); 190be79a2c6SJessica Clarke return (ENXIO); 191be79a2c6SJessica Clarke } 192be79a2c6SJessica Clarke 193be79a2c6SJessica Clarke magic = vtmmio_read_config_4(sc, VIRTIO_MMIO_MAGIC_VALUE); 194be79a2c6SJessica Clarke if (magic != VIRTIO_MMIO_MAGIC_VIRT) { 195be79a2c6SJessica Clarke device_printf(dev, "Bad magic value %#x\n", magic); 196be79a2c6SJessica Clarke bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]); 197be79a2c6SJessica Clarke return (ENXIO); 198be79a2c6SJessica Clarke } 199be79a2c6SJessica Clarke 200be79a2c6SJessica Clarke version = vtmmio_read_config_4(sc, VIRTIO_MMIO_VERSION); 201be79a2c6SJessica Clarke if (version < 1 || version > 2) { 202be79a2c6SJessica Clarke device_printf(dev, "Unsupported version: %#x\n", version); 203be79a2c6SJessica Clarke bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]); 204be79a2c6SJessica Clarke return (ENXIO); 205be79a2c6SJessica Clarke } 206be79a2c6SJessica Clarke 207be79a2c6SJessica Clarke if (vtmmio_read_config_4(sc, VIRTIO_MMIO_DEVICE_ID) == 0) { 208be79a2c6SJessica Clarke bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]); 209be79a2c6SJessica Clarke return (ENXIO); 210be79a2c6SJessica Clarke } 211be79a2c6SJessica Clarke 212be79a2c6SJessica Clarke bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res[0]); 213be79a2c6SJessica Clarke 214be79a2c6SJessica Clarke device_set_desc(dev, "VirtIO MMIO adapter"); 215be79a2c6SJessica Clarke return (BUS_PROBE_DEFAULT); 216be79a2c6SJessica Clarke } 217be79a2c6SJessica Clarke 218c141c5c6SRuslan Bukin static int 219c141c5c6SRuslan Bukin vtmmio_setup_intr(device_t dev, enum intr_type type) 220c141c5c6SRuslan Bukin { 221c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 222c141c5c6SRuslan Bukin int rid; 223c141c5c6SRuslan Bukin int err; 224c141c5c6SRuslan Bukin 225c141c5c6SRuslan Bukin sc = device_get_softc(dev); 226c141c5c6SRuslan Bukin 2274dbff239SAndrew Turner if (sc->platform != NULL) { 228c141c5c6SRuslan Bukin err = VIRTIO_MMIO_SETUP_INTR(sc->platform, sc->dev, 229c141c5c6SRuslan Bukin vtmmio_vq_intr, sc); 230c141c5c6SRuslan Bukin if (err == 0) { 231c141c5c6SRuslan Bukin /* Okay we have backend-specific interrupts */ 232c141c5c6SRuslan Bukin return (0); 233c141c5c6SRuslan Bukin } 2344dbff239SAndrew Turner } 235c141c5c6SRuslan Bukin 236c141c5c6SRuslan Bukin rid = 0; 237c141c5c6SRuslan Bukin sc->res[1] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 238c141c5c6SRuslan Bukin RF_ACTIVE); 239c141c5c6SRuslan Bukin if (!sc->res[1]) { 240c141c5c6SRuslan Bukin device_printf(dev, "Can't allocate interrupt\n"); 241c141c5c6SRuslan Bukin return (ENXIO); 242c141c5c6SRuslan Bukin } 243c141c5c6SRuslan Bukin 24455cd9324SKristof Provost if (bus_setup_intr(dev, sc->res[1], type | INTR_MPSAFE, 245c141c5c6SRuslan Bukin NULL, vtmmio_vq_intr, sc, &sc->ih)) { 246c141c5c6SRuslan Bukin device_printf(dev, "Can't setup the interrupt\n"); 247c141c5c6SRuslan Bukin return (ENXIO); 248c141c5c6SRuslan Bukin } 249c141c5c6SRuslan Bukin 250c141c5c6SRuslan Bukin return (0); 251c141c5c6SRuslan Bukin } 252c141c5c6SRuslan Bukin 253a3609b82SAndrew Turner int 254c141c5c6SRuslan Bukin vtmmio_attach(device_t dev) 255c141c5c6SRuslan Bukin { 256c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 257c141c5c6SRuslan Bukin device_t child; 258c141c5c6SRuslan Bukin int rid; 259c141c5c6SRuslan Bukin 260c141c5c6SRuslan Bukin sc = device_get_softc(dev); 261c141c5c6SRuslan Bukin sc->dev = dev; 262c141c5c6SRuslan Bukin 263c141c5c6SRuslan Bukin rid = 0; 264c141c5c6SRuslan Bukin sc->res[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 265c141c5c6SRuslan Bukin RF_ACTIVE); 2660e72f2c5SJessica Clarke if (sc->res[0] == NULL) { 267c141c5c6SRuslan Bukin device_printf(dev, "Cannot allocate memory window.\n"); 268c141c5c6SRuslan Bukin return (ENXIO); 269c141c5c6SRuslan Bukin } 270c141c5c6SRuslan Bukin 271046096d5SJessica Clarke sc->vtmmio_version = vtmmio_read_config_4(sc, VIRTIO_MMIO_VERSION); 272046096d5SJessica Clarke 273c141c5c6SRuslan Bukin vtmmio_reset(sc); 274c141c5c6SRuslan Bukin 275c141c5c6SRuslan Bukin /* Tell the host we've noticed this device. */ 276c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 277c141c5c6SRuslan Bukin 278c141c5c6SRuslan Bukin if ((child = device_add_child(dev, NULL, -1)) == NULL) { 279c141c5c6SRuslan Bukin device_printf(dev, "Cannot create child device.\n"); 280c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 281c141c5c6SRuslan Bukin vtmmio_detach(dev); 282c141c5c6SRuslan Bukin return (ENOMEM); 283c141c5c6SRuslan Bukin } 284c141c5c6SRuslan Bukin 285c141c5c6SRuslan Bukin sc->vtmmio_child_dev = child; 286c141c5c6SRuslan Bukin vtmmio_probe_and_attach_child(sc); 287c141c5c6SRuslan Bukin 288c141c5c6SRuslan Bukin return (0); 289c141c5c6SRuslan Bukin } 290c141c5c6SRuslan Bukin 291c141c5c6SRuslan Bukin static int 292c141c5c6SRuslan Bukin vtmmio_detach(device_t dev) 293c141c5c6SRuslan Bukin { 294c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 295c141c5c6SRuslan Bukin int error; 296c141c5c6SRuslan Bukin 297c141c5c6SRuslan Bukin sc = device_get_softc(dev); 298c141c5c6SRuslan Bukin 299*11a91178SJohn Baldwin error = bus_generic_detach(dev); 300c141c5c6SRuslan Bukin if (error) 301c141c5c6SRuslan Bukin return (error); 302c141c5c6SRuslan Bukin 303c141c5c6SRuslan Bukin vtmmio_reset(sc); 304c141c5c6SRuslan Bukin 305c141c5c6SRuslan Bukin if (sc->res[0] != NULL) { 306c141c5c6SRuslan Bukin bus_release_resource(dev, SYS_RES_MEMORY, 0, 307c141c5c6SRuslan Bukin sc->res[0]); 308c141c5c6SRuslan Bukin sc->res[0] = NULL; 309c141c5c6SRuslan Bukin } 310c141c5c6SRuslan Bukin 311c141c5c6SRuslan Bukin return (0); 312c141c5c6SRuslan Bukin } 313c141c5c6SRuslan Bukin 314c141c5c6SRuslan Bukin static int 315c141c5c6SRuslan Bukin vtmmio_suspend(device_t dev) 316c141c5c6SRuslan Bukin { 317c141c5c6SRuslan Bukin 318c141c5c6SRuslan Bukin return (bus_generic_suspend(dev)); 319c141c5c6SRuslan Bukin } 320c141c5c6SRuslan Bukin 321c141c5c6SRuslan Bukin static int 322c141c5c6SRuslan Bukin vtmmio_resume(device_t dev) 323c141c5c6SRuslan Bukin { 324c141c5c6SRuslan Bukin 325c141c5c6SRuslan Bukin return (bus_generic_resume(dev)); 326c141c5c6SRuslan Bukin } 327c141c5c6SRuslan Bukin 328c141c5c6SRuslan Bukin static int 329c141c5c6SRuslan Bukin vtmmio_shutdown(device_t dev) 330c141c5c6SRuslan Bukin { 331c141c5c6SRuslan Bukin 332c141c5c6SRuslan Bukin (void) bus_generic_shutdown(dev); 333c141c5c6SRuslan Bukin 334c141c5c6SRuslan Bukin /* Forcibly stop the host device. */ 335c141c5c6SRuslan Bukin vtmmio_stop(dev); 336c141c5c6SRuslan Bukin 337c141c5c6SRuslan Bukin return (0); 338c141c5c6SRuslan Bukin } 339c141c5c6SRuslan Bukin 340c141c5c6SRuslan Bukin static void 341c141c5c6SRuslan Bukin vtmmio_driver_added(device_t dev, driver_t *driver) 342c141c5c6SRuslan Bukin { 343c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 344c141c5c6SRuslan Bukin 345c141c5c6SRuslan Bukin sc = device_get_softc(dev); 346c141c5c6SRuslan Bukin 347c141c5c6SRuslan Bukin vtmmio_probe_and_attach_child(sc); 348c141c5c6SRuslan Bukin } 349c141c5c6SRuslan Bukin 350c141c5c6SRuslan Bukin static void 351c141c5c6SRuslan Bukin vtmmio_child_detached(device_t dev, device_t child) 352c141c5c6SRuslan Bukin { 353c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 354c141c5c6SRuslan Bukin 355c141c5c6SRuslan Bukin sc = device_get_softc(dev); 356c141c5c6SRuslan Bukin 357c141c5c6SRuslan Bukin vtmmio_reset(sc); 358c141c5c6SRuslan Bukin vtmmio_release_child_resources(sc); 359c141c5c6SRuslan Bukin } 360c141c5c6SRuslan Bukin 361c141c5c6SRuslan Bukin static int 362c141c5c6SRuslan Bukin vtmmio_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 363c141c5c6SRuslan Bukin { 364c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 365c141c5c6SRuslan Bukin 366c141c5c6SRuslan Bukin sc = device_get_softc(dev); 367c141c5c6SRuslan Bukin 368c141c5c6SRuslan Bukin if (sc->vtmmio_child_dev != child) 369c141c5c6SRuslan Bukin return (ENOENT); 370c141c5c6SRuslan Bukin 371c141c5c6SRuslan Bukin switch (index) { 372c141c5c6SRuslan Bukin case VIRTIO_IVAR_DEVTYPE: 373c141c5c6SRuslan Bukin case VIRTIO_IVAR_SUBDEVICE: 374c141c5c6SRuslan Bukin *result = vtmmio_read_config_4(sc, VIRTIO_MMIO_DEVICE_ID); 375c141c5c6SRuslan Bukin break; 376c141c5c6SRuslan Bukin case VIRTIO_IVAR_VENDOR: 377c141c5c6SRuslan Bukin *result = vtmmio_read_config_4(sc, VIRTIO_MMIO_VENDOR_ID); 378c141c5c6SRuslan Bukin break; 379dfca0a8bSConrad Meyer case VIRTIO_IVAR_SUBVENDOR: 380dfca0a8bSConrad Meyer case VIRTIO_IVAR_DEVICE: 381dfca0a8bSConrad Meyer /* 382dfca0a8bSConrad Meyer * Dummy value for fields not present in this bus. Used by 383ddfc9c4cSWarner Losh * bus-agnostic virtio_child_pnpinfo. 384dfca0a8bSConrad Meyer */ 385dfca0a8bSConrad Meyer *result = 0; 386dfca0a8bSConrad Meyer break; 3879da9560cSBryan Venteicher case VIRTIO_IVAR_MODERN: 3889da9560cSBryan Venteicher /* 3899da9560cSBryan Venteicher * There are several modern (aka MMIO v2) spec compliance 3909da9560cSBryan Venteicher * issues with this driver, but keep the status quo. 3919da9560cSBryan Venteicher */ 3929da9560cSBryan Venteicher *result = sc->vtmmio_version > 1; 3939da9560cSBryan Venteicher break; 394c141c5c6SRuslan Bukin default: 395c141c5c6SRuslan Bukin return (ENOENT); 396c141c5c6SRuslan Bukin } 397c141c5c6SRuslan Bukin 398c141c5c6SRuslan Bukin return (0); 399c141c5c6SRuslan Bukin } 400c141c5c6SRuslan Bukin 401c141c5c6SRuslan Bukin static int 402c141c5c6SRuslan Bukin vtmmio_write_ivar(device_t dev, device_t child, int index, uintptr_t value) 403c141c5c6SRuslan Bukin { 404c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 405c141c5c6SRuslan Bukin 406c141c5c6SRuslan Bukin sc = device_get_softc(dev); 407c141c5c6SRuslan Bukin 408c141c5c6SRuslan Bukin if (sc->vtmmio_child_dev != child) 409c141c5c6SRuslan Bukin return (ENOENT); 410c141c5c6SRuslan Bukin 411c141c5c6SRuslan Bukin switch (index) { 412c141c5c6SRuslan Bukin case VIRTIO_IVAR_FEATURE_DESC: 413c141c5c6SRuslan Bukin sc->vtmmio_child_feat_desc = (void *) value; 414c141c5c6SRuslan Bukin break; 415c141c5c6SRuslan Bukin default: 416c141c5c6SRuslan Bukin return (ENOENT); 417c141c5c6SRuslan Bukin } 418c141c5c6SRuslan Bukin 419c141c5c6SRuslan Bukin return (0); 420c141c5c6SRuslan Bukin } 421c141c5c6SRuslan Bukin 422c141c5c6SRuslan Bukin static uint64_t 423c141c5c6SRuslan Bukin vtmmio_negotiate_features(device_t dev, uint64_t child_features) 424c141c5c6SRuslan Bukin { 425c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 426c141c5c6SRuslan Bukin uint64_t host_features, features; 427c141c5c6SRuslan Bukin 428c141c5c6SRuslan Bukin sc = device_get_softc(dev); 429c141c5c6SRuslan Bukin 4309da9560cSBryan Venteicher if (sc->vtmmio_version > 1) { 4319da9560cSBryan Venteicher child_features |= VIRTIO_F_VERSION_1; 4329da9560cSBryan Venteicher } 4339da9560cSBryan Venteicher 43416ca3d0fSJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_HOST_FEATURES_SEL, 1); 435c141c5c6SRuslan Bukin host_features = vtmmio_read_config_4(sc, VIRTIO_MMIO_HOST_FEATURES); 43616ca3d0fSJessica Clarke host_features <<= 32; 43716ca3d0fSJessica Clarke 43816ca3d0fSJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_HOST_FEATURES_SEL, 0); 43916ca3d0fSJessica Clarke host_features |= vtmmio_read_config_4(sc, VIRTIO_MMIO_HOST_FEATURES); 44016ca3d0fSJessica Clarke 441c141c5c6SRuslan Bukin vtmmio_describe_features(sc, "host", host_features); 442c141c5c6SRuslan Bukin 443c141c5c6SRuslan Bukin /* 444c141c5c6SRuslan Bukin * Limit negotiated features to what the driver, virtqueue, and 445c141c5c6SRuslan Bukin * host all support. 446c141c5c6SRuslan Bukin */ 447c141c5c6SRuslan Bukin features = host_features & child_features; 4489da9560cSBryan Venteicher features = virtio_filter_transport_features(features); 449c141c5c6SRuslan Bukin sc->vtmmio_features = features; 450c141c5c6SRuslan Bukin 451c141c5c6SRuslan Bukin vtmmio_describe_features(sc, "negotiated", features); 45216ca3d0fSJessica Clarke 45350a6b28aSJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES_SEL, 1); 45416ca3d0fSJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES, features >> 32); 45516ca3d0fSJessica Clarke 45650a6b28aSJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES_SEL, 0); 457c141c5c6SRuslan Bukin vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_FEATURES, features); 458c141c5c6SRuslan Bukin 459c141c5c6SRuslan Bukin return (features); 460c141c5c6SRuslan Bukin } 461c141c5c6SRuslan Bukin 462c141c5c6SRuslan Bukin static int 463926cedd9SBryan Venteicher vtmmio_finalize_features(device_t dev) 464926cedd9SBryan Venteicher { 465926cedd9SBryan Venteicher struct vtmmio_softc *sc; 466926cedd9SBryan Venteicher uint8_t status; 467926cedd9SBryan Venteicher 468926cedd9SBryan Venteicher sc = device_get_softc(dev); 469926cedd9SBryan Venteicher 470926cedd9SBryan Venteicher if (sc->vtmmio_version > 1) { 471926cedd9SBryan Venteicher /* 472926cedd9SBryan Venteicher * Must re-read the status after setting it to verify the 473926cedd9SBryan Venteicher * negotiated features were accepted by the device. 474926cedd9SBryan Venteicher */ 475926cedd9SBryan Venteicher vtmmio_set_status(dev, VIRTIO_CONFIG_S_FEATURES_OK); 476926cedd9SBryan Venteicher 477926cedd9SBryan Venteicher status = vtmmio_get_status(dev); 478926cedd9SBryan Venteicher if ((status & VIRTIO_CONFIG_S_FEATURES_OK) == 0) { 479926cedd9SBryan Venteicher device_printf(dev, "desired features were not accepted\n"); 480926cedd9SBryan Venteicher return (ENOTSUP); 481926cedd9SBryan Venteicher } 482926cedd9SBryan Venteicher } 483926cedd9SBryan Venteicher 484926cedd9SBryan Venteicher return (0); 485926cedd9SBryan Venteicher } 486926cedd9SBryan Venteicher 487ccb576a8SMina Galić static bool 488c141c5c6SRuslan Bukin vtmmio_with_feature(device_t dev, uint64_t feature) 489c141c5c6SRuslan Bukin { 490c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 491c141c5c6SRuslan Bukin 492c141c5c6SRuslan Bukin sc = device_get_softc(dev); 493c141c5c6SRuslan Bukin 494c141c5c6SRuslan Bukin return ((sc->vtmmio_features & feature) != 0); 495c141c5c6SRuslan Bukin } 496c141c5c6SRuslan Bukin 497046096d5SJessica Clarke static void 498046096d5SJessica Clarke vtmmio_set_virtqueue(struct vtmmio_softc *sc, struct virtqueue *vq, 499046096d5SJessica Clarke uint32_t size) 500046096d5SJessica Clarke { 501046096d5SJessica Clarke vm_paddr_t paddr; 502046096d5SJessica Clarke 503046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size); 50485ad7f8dSJessica Clarke 505046096d5SJessica Clarke if (sc->vtmmio_version == 1) { 506046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN, 507046096d5SJessica Clarke VIRTIO_MMIO_VRING_ALIGN); 508046096d5SJessica Clarke paddr = virtqueue_paddr(vq); 509046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 510046096d5SJessica Clarke paddr >> PAGE_SHIFT); 511046096d5SJessica Clarke } else { 512046096d5SJessica Clarke paddr = virtqueue_desc_paddr(vq); 513046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_LOW, 514046096d5SJessica Clarke paddr); 515046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_HIGH, 516cfe6a221SJessica Clarke ((uint64_t)paddr) >> 32); 517046096d5SJessica Clarke 518046096d5SJessica Clarke paddr = virtqueue_avail_paddr(vq); 519046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_LOW, 520046096d5SJessica Clarke paddr); 521046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_HIGH, 522cfe6a221SJessica Clarke ((uint64_t)paddr) >> 32); 523046096d5SJessica Clarke 524046096d5SJessica Clarke paddr = virtqueue_used_paddr(vq); 525046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_LOW, 526046096d5SJessica Clarke paddr); 527046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_HIGH, 528cfe6a221SJessica Clarke ((uint64_t)paddr) >> 32); 529046096d5SJessica Clarke 530046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 1); 531046096d5SJessica Clarke } 532046096d5SJessica Clarke } 533046096d5SJessica Clarke 534c141c5c6SRuslan Bukin static int 535180c0240SMina Galić vtmmio_alloc_virtqueues(device_t dev, int nvqs, 536c141c5c6SRuslan Bukin struct vq_alloc_info *vq_info) 537c141c5c6SRuslan Bukin { 538c141c5c6SRuslan Bukin struct vtmmio_virtqueue *vqx; 539c141c5c6SRuslan Bukin struct vq_alloc_info *info; 540c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 541c141c5c6SRuslan Bukin struct virtqueue *vq; 5424dbff239SAndrew Turner uint32_t size; 543c141c5c6SRuslan Bukin int idx, error; 544c141c5c6SRuslan Bukin 545c141c5c6SRuslan Bukin sc = device_get_softc(dev); 546c141c5c6SRuslan Bukin 547c141c5c6SRuslan Bukin if (sc->vtmmio_nvqs != 0) 548c141c5c6SRuslan Bukin return (EALREADY); 549c141c5c6SRuslan Bukin if (nvqs <= 0) 550c141c5c6SRuslan Bukin return (EINVAL); 551c141c5c6SRuslan Bukin 552ac2fffa4SPedro F. Giffuni sc->vtmmio_vqs = malloc(nvqs * sizeof(struct vtmmio_virtqueue), 553c141c5c6SRuslan Bukin M_DEVBUF, M_NOWAIT | M_ZERO); 554c141c5c6SRuslan Bukin if (sc->vtmmio_vqs == NULL) 555c141c5c6SRuslan Bukin return (ENOMEM); 556c141c5c6SRuslan Bukin 5577377c1dfSAndrew Turner if (sc->vtmmio_version == 1) { 5581147faceSRuslan Bukin vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_PAGE_SIZE, 5591147faceSRuslan Bukin (1 << PAGE_SHIFT)); 5607377c1dfSAndrew Turner } 5617c5c5b97SAndrew Turner 562c141c5c6SRuslan Bukin for (idx = 0; idx < nvqs; idx++) { 563c141c5c6SRuslan Bukin vqx = &sc->vtmmio_vqs[idx]; 564c141c5c6SRuslan Bukin info = &vq_info[idx]; 565c141c5c6SRuslan Bukin 566c141c5c6SRuslan Bukin vtmmio_select_virtqueue(sc, idx); 5674dbff239SAndrew Turner size = vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX); 568c141c5c6SRuslan Bukin 569c141c5c6SRuslan Bukin error = virtqueue_alloc(dev, idx, size, 5709da9560cSBryan Venteicher VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_VRING_ALIGN, 5719da9560cSBryan Venteicher ~(vm_paddr_t)0, info, &vq); 572c141c5c6SRuslan Bukin if (error) { 573c141c5c6SRuslan Bukin device_printf(dev, 574c141c5c6SRuslan Bukin "cannot allocate virtqueue %d: %d\n", 575c141c5c6SRuslan Bukin idx, error); 576c141c5c6SRuslan Bukin break; 577c141c5c6SRuslan Bukin } 5784dbff239SAndrew Turner 579046096d5SJessica Clarke vtmmio_set_virtqueue(sc, vq, size); 580c141c5c6SRuslan Bukin 581c141c5c6SRuslan Bukin vqx->vtv_vq = *info->vqai_vq = vq; 582c141c5c6SRuslan Bukin vqx->vtv_no_intr = info->vqai_intr == NULL; 583c141c5c6SRuslan Bukin 584c141c5c6SRuslan Bukin sc->vtmmio_nvqs++; 585c141c5c6SRuslan Bukin } 586c141c5c6SRuslan Bukin 587c141c5c6SRuslan Bukin if (error) 588c141c5c6SRuslan Bukin vtmmio_free_virtqueues(sc); 589c141c5c6SRuslan Bukin 590c141c5c6SRuslan Bukin return (error); 591c141c5c6SRuslan Bukin } 592c141c5c6SRuslan Bukin 593c141c5c6SRuslan Bukin static void 594c141c5c6SRuslan Bukin vtmmio_stop(device_t dev) 595c141c5c6SRuslan Bukin { 596c141c5c6SRuslan Bukin 597c141c5c6SRuslan Bukin vtmmio_reset(device_get_softc(dev)); 598c141c5c6SRuslan Bukin } 599c141c5c6SRuslan Bukin 600156b97faSRuslan Bukin static void 601156b97faSRuslan Bukin vtmmio_poll(device_t dev) 602156b97faSRuslan Bukin { 603156b97faSRuslan Bukin struct vtmmio_softc *sc; 604156b97faSRuslan Bukin 605156b97faSRuslan Bukin sc = device_get_softc(dev); 606156b97faSRuslan Bukin 607156b97faSRuslan Bukin if (sc->platform != NULL) 608156b97faSRuslan Bukin VIRTIO_MMIO_POLL(sc->platform); 609156b97faSRuslan Bukin } 610156b97faSRuslan Bukin 611c141c5c6SRuslan Bukin static int 612c141c5c6SRuslan Bukin vtmmio_reinit(device_t dev, uint64_t features) 613c141c5c6SRuslan Bukin { 614c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 615c141c5c6SRuslan Bukin int idx, error; 616c141c5c6SRuslan Bukin 617c141c5c6SRuslan Bukin sc = device_get_softc(dev); 618c141c5c6SRuslan Bukin 619c141c5c6SRuslan Bukin if (vtmmio_get_status(dev) != VIRTIO_CONFIG_STATUS_RESET) 620c141c5c6SRuslan Bukin vtmmio_stop(dev); 621c141c5c6SRuslan Bukin 622c141c5c6SRuslan Bukin /* 623c141c5c6SRuslan Bukin * Quickly drive the status through ACK and DRIVER. The device 624c141c5c6SRuslan Bukin * does not become usable again until vtmmio_reinit_complete(). 625c141c5c6SRuslan Bukin */ 626c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 627c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 628c141c5c6SRuslan Bukin 629926cedd9SBryan Venteicher /* 630926cedd9SBryan Venteicher * TODO: Check that features are not added as to what was 631926cedd9SBryan Venteicher * originally negotiated. 632926cedd9SBryan Venteicher */ 633c141c5c6SRuslan Bukin vtmmio_negotiate_features(dev, features); 634926cedd9SBryan Venteicher error = vtmmio_finalize_features(dev); 635926cedd9SBryan Venteicher if (error) { 636926cedd9SBryan Venteicher device_printf(dev, "cannot finalize features during reinit\n"); 637926cedd9SBryan Venteicher return (error); 638926cedd9SBryan Venteicher } 639c141c5c6SRuslan Bukin 6407377c1dfSAndrew Turner if (sc->vtmmio_version == 1) { 6411147faceSRuslan Bukin vtmmio_write_config_4(sc, VIRTIO_MMIO_GUEST_PAGE_SIZE, 6421147faceSRuslan Bukin (1 << PAGE_SHIFT)); 6437377c1dfSAndrew Turner } 6441147faceSRuslan Bukin 645c141c5c6SRuslan Bukin for (idx = 0; idx < sc->vtmmio_nvqs; idx++) { 646c141c5c6SRuslan Bukin error = vtmmio_reinit_virtqueue(sc, idx); 647c141c5c6SRuslan Bukin if (error) 648c141c5c6SRuslan Bukin return (error); 649c141c5c6SRuslan Bukin } 650c141c5c6SRuslan Bukin 651c141c5c6SRuslan Bukin return (0); 652c141c5c6SRuslan Bukin } 653c141c5c6SRuslan Bukin 654c141c5c6SRuslan Bukin static void 655c141c5c6SRuslan Bukin vtmmio_reinit_complete(device_t dev) 656c141c5c6SRuslan Bukin { 657c141c5c6SRuslan Bukin 658c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 659c141c5c6SRuslan Bukin } 660c141c5c6SRuslan Bukin 661c141c5c6SRuslan Bukin static void 6629da9560cSBryan Venteicher vtmmio_notify_virtqueue(device_t dev, uint16_t queue, bus_size_t offset) 663c141c5c6SRuslan Bukin { 664c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 665c141c5c6SRuslan Bukin 666c141c5c6SRuslan Bukin sc = device_get_softc(dev); 6679da9560cSBryan Venteicher MPASS(offset == VIRTIO_MMIO_QUEUE_NOTIFY); 668c141c5c6SRuslan Bukin 6699da9560cSBryan Venteicher vtmmio_write_config_4(sc, offset, queue); 670c141c5c6SRuslan Bukin } 671c141c5c6SRuslan Bukin 672926cedd9SBryan Venteicher static int 673926cedd9SBryan Venteicher vtmmio_config_generation(device_t dev) 674926cedd9SBryan Venteicher { 675926cedd9SBryan Venteicher struct vtmmio_softc *sc; 676926cedd9SBryan Venteicher uint32_t gen; 677926cedd9SBryan Venteicher 678926cedd9SBryan Venteicher sc = device_get_softc(dev); 679926cedd9SBryan Venteicher 680926cedd9SBryan Venteicher if (sc->vtmmio_version > 1) 681926cedd9SBryan Venteicher gen = vtmmio_read_config_4(sc, VIRTIO_MMIO_CONFIG_GENERATION); 682926cedd9SBryan Venteicher else 683926cedd9SBryan Venteicher gen = 0; 684926cedd9SBryan Venteicher 685926cedd9SBryan Venteicher return (gen); 686926cedd9SBryan Venteicher } 687926cedd9SBryan Venteicher 688c141c5c6SRuslan Bukin static uint8_t 689c141c5c6SRuslan Bukin vtmmio_get_status(device_t dev) 690c141c5c6SRuslan Bukin { 691c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 692c141c5c6SRuslan Bukin 693c141c5c6SRuslan Bukin sc = device_get_softc(dev); 694c141c5c6SRuslan Bukin 6954dbff239SAndrew Turner return (vtmmio_read_config_4(sc, VIRTIO_MMIO_STATUS)); 696c141c5c6SRuslan Bukin } 697c141c5c6SRuslan Bukin 698c141c5c6SRuslan Bukin static void 699c141c5c6SRuslan Bukin vtmmio_set_status(device_t dev, uint8_t status) 700c141c5c6SRuslan Bukin { 701c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 702c141c5c6SRuslan Bukin 703c141c5c6SRuslan Bukin sc = device_get_softc(dev); 704c141c5c6SRuslan Bukin 705c141c5c6SRuslan Bukin if (status != VIRTIO_CONFIG_STATUS_RESET) 706c141c5c6SRuslan Bukin status |= vtmmio_get_status(dev); 707c141c5c6SRuslan Bukin 7084dbff239SAndrew Turner vtmmio_write_config_4(sc, VIRTIO_MMIO_STATUS, status); 709c141c5c6SRuslan Bukin } 710c141c5c6SRuslan Bukin 711c141c5c6SRuslan Bukin static void 712c141c5c6SRuslan Bukin vtmmio_read_dev_config(device_t dev, bus_size_t offset, 713c141c5c6SRuslan Bukin void *dst, int length) 714c141c5c6SRuslan Bukin { 715c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 716c141c5c6SRuslan Bukin bus_size_t off; 717c141c5c6SRuslan Bukin uint8_t *d; 718c141c5c6SRuslan Bukin int size; 719c141c5c6SRuslan Bukin 720c141c5c6SRuslan Bukin sc = device_get_softc(dev); 721c141c5c6SRuslan Bukin off = VIRTIO_MMIO_CONFIG + offset; 722c141c5c6SRuslan Bukin 723046096d5SJessica Clarke /* 724046096d5SJessica Clarke * The non-legacy MMIO specification adds the following restriction: 725046096d5SJessica Clarke * 726046096d5SJessica Clarke * 4.2.2.2: For the device-specific configuration space, the driver 727046096d5SJessica Clarke * MUST use 8 bit wide accesses for 8 bit wide fields, 16 bit wide 728046096d5SJessica Clarke * and aligned accesses for 16 bit wide fields and 32 bit wide and 729046096d5SJessica Clarke * aligned accesses for 32 and 64 bit wide fields. 730046096d5SJessica Clarke * 731046096d5SJessica Clarke * The endianness also varies between non-legacy and legacy: 732046096d5SJessica Clarke * 733046096d5SJessica Clarke * 2.4: Note: The device configuration space uses the little-endian 734046096d5SJessica Clarke * format for multi-byte fields. 735046096d5SJessica Clarke * 736046096d5SJessica Clarke * 2.4.3: Note that for legacy interfaces, device configuration space 737046096d5SJessica Clarke * is generally the guest’s native endian, rather than PCI’s 738046096d5SJessica Clarke * little-endian. The correct endian-ness is documented for each 739046096d5SJessica Clarke * device. 740046096d5SJessica Clarke */ 741046096d5SJessica Clarke if (sc->vtmmio_version > 1) { 742046096d5SJessica Clarke switch (length) { 743046096d5SJessica Clarke case 1: 744046096d5SJessica Clarke *(uint8_t *)dst = vtmmio_read_config_1(sc, off); 745046096d5SJessica Clarke break; 746046096d5SJessica Clarke case 2: 747046096d5SJessica Clarke *(uint16_t *)dst = 748046096d5SJessica Clarke le16toh(vtmmio_read_config_2(sc, off)); 749046096d5SJessica Clarke break; 750046096d5SJessica Clarke case 4: 751046096d5SJessica Clarke *(uint32_t *)dst = 752046096d5SJessica Clarke le32toh(vtmmio_read_config_4(sc, off)); 753046096d5SJessica Clarke break; 754046096d5SJessica Clarke case 8: 755926cedd9SBryan Venteicher *(uint64_t *)dst = vtmmio_read_dev_config_8(sc, off); 756046096d5SJessica Clarke break; 757046096d5SJessica Clarke default: 758046096d5SJessica Clarke panic("%s: invalid length %d\n", __func__, length); 759046096d5SJessica Clarke } 760046096d5SJessica Clarke 761046096d5SJessica Clarke return; 762046096d5SJessica Clarke } 763046096d5SJessica Clarke 764c141c5c6SRuslan Bukin for (d = dst; length > 0; d += size, off += size, length -= size) { 7654dbff239SAndrew Turner #ifdef ALLOW_WORD_ALIGNED_ACCESS 766c141c5c6SRuslan Bukin if (length >= 4) { 767c141c5c6SRuslan Bukin size = 4; 768c141c5c6SRuslan Bukin *(uint32_t *)d = vtmmio_read_config_4(sc, off); 769c141c5c6SRuslan Bukin } else if (length >= 2) { 770c141c5c6SRuslan Bukin size = 2; 771c141c5c6SRuslan Bukin *(uint16_t *)d = vtmmio_read_config_2(sc, off); 7724dbff239SAndrew Turner } else 7734dbff239SAndrew Turner #endif 7744dbff239SAndrew Turner { 775c141c5c6SRuslan Bukin size = 1; 776c141c5c6SRuslan Bukin *d = vtmmio_read_config_1(sc, off); 777c141c5c6SRuslan Bukin } 778c141c5c6SRuslan Bukin } 779c141c5c6SRuslan Bukin } 780c141c5c6SRuslan Bukin 781926cedd9SBryan Venteicher static uint64_t 782926cedd9SBryan Venteicher vtmmio_read_dev_config_8(struct vtmmio_softc *sc, bus_size_t off) 783926cedd9SBryan Venteicher { 784926cedd9SBryan Venteicher device_t dev; 785926cedd9SBryan Venteicher int gen; 786926cedd9SBryan Venteicher uint32_t val0, val1; 787926cedd9SBryan Venteicher 788926cedd9SBryan Venteicher dev = sc->dev; 789926cedd9SBryan Venteicher 790926cedd9SBryan Venteicher do { 791926cedd9SBryan Venteicher gen = vtmmio_config_generation(dev); 792926cedd9SBryan Venteicher val0 = le32toh(vtmmio_read_config_4(sc, off)); 793926cedd9SBryan Venteicher val1 = le32toh(vtmmio_read_config_4(sc, off + 4)); 794926cedd9SBryan Venteicher } while (gen != vtmmio_config_generation(dev)); 795926cedd9SBryan Venteicher 796926cedd9SBryan Venteicher return (((uint64_t) val1 << 32) | val0); 797926cedd9SBryan Venteicher } 798926cedd9SBryan Venteicher 799c141c5c6SRuslan Bukin static void 800c141c5c6SRuslan Bukin vtmmio_write_dev_config(device_t dev, bus_size_t offset, 8016c4f9516SAlex Richardson const void *src, int length) 802c141c5c6SRuslan Bukin { 803c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 804c141c5c6SRuslan Bukin bus_size_t off; 80590178705SAlex Richardson const uint8_t *s; 806c141c5c6SRuslan Bukin int size; 807c141c5c6SRuslan Bukin 808c141c5c6SRuslan Bukin sc = device_get_softc(dev); 809c141c5c6SRuslan Bukin off = VIRTIO_MMIO_CONFIG + offset; 810c141c5c6SRuslan Bukin 811046096d5SJessica Clarke /* 812046096d5SJessica Clarke * The non-legacy MMIO specification adds size and alignment 813046096d5SJessica Clarke * restrctions. It also changes the endianness from native-endian to 814046096d5SJessica Clarke * little-endian. See vtmmio_read_dev_config. 815046096d5SJessica Clarke */ 816046096d5SJessica Clarke if (sc->vtmmio_version > 1) { 817046096d5SJessica Clarke switch (length) { 818046096d5SJessica Clarke case 1: 8196c4f9516SAlex Richardson vtmmio_write_config_1(sc, off, *(const uint8_t *)src); 820046096d5SJessica Clarke break; 821046096d5SJessica Clarke case 2: 822046096d5SJessica Clarke vtmmio_write_config_2(sc, off, 8236c4f9516SAlex Richardson htole16(*(const uint16_t *)src)); 824046096d5SJessica Clarke break; 825046096d5SJessica Clarke case 4: 826046096d5SJessica Clarke vtmmio_write_config_4(sc, off, 8276c4f9516SAlex Richardson htole32(*(const uint32_t *)src)); 828046096d5SJessica Clarke break; 829046096d5SJessica Clarke case 8: 830046096d5SJessica Clarke vtmmio_write_config_4(sc, off, 8316c4f9516SAlex Richardson htole32(*(const uint64_t *)src)); 832046096d5SJessica Clarke vtmmio_write_config_4(sc, off + 4, 8336c4f9516SAlex Richardson htole32((*(const uint64_t *)src) >> 32)); 834046096d5SJessica Clarke break; 835046096d5SJessica Clarke default: 836046096d5SJessica Clarke panic("%s: invalid length %d\n", __func__, length); 837046096d5SJessica Clarke } 838046096d5SJessica Clarke 839046096d5SJessica Clarke return; 840046096d5SJessica Clarke } 841046096d5SJessica Clarke 842c141c5c6SRuslan Bukin for (s = src; length > 0; s += size, off += size, length -= size) { 8434dbff239SAndrew Turner #ifdef ALLOW_WORD_ALIGNED_ACCESS 844c141c5c6SRuslan Bukin if (length >= 4) { 845c141c5c6SRuslan Bukin size = 4; 846c141c5c6SRuslan Bukin vtmmio_write_config_4(sc, off, *(uint32_t *)s); 847c141c5c6SRuslan Bukin } else if (length >= 2) { 848c141c5c6SRuslan Bukin size = 2; 849c141c5c6SRuslan Bukin vtmmio_write_config_2(sc, off, *(uint16_t *)s); 8504dbff239SAndrew Turner } else 8514dbff239SAndrew Turner #endif 8524dbff239SAndrew Turner { 853c141c5c6SRuslan Bukin size = 1; 854c141c5c6SRuslan Bukin vtmmio_write_config_1(sc, off, *s); 855c141c5c6SRuslan Bukin } 856c141c5c6SRuslan Bukin } 857c141c5c6SRuslan Bukin } 858c141c5c6SRuslan Bukin 859c141c5c6SRuslan Bukin static void 860c141c5c6SRuslan Bukin vtmmio_describe_features(struct vtmmio_softc *sc, const char *msg, 861c141c5c6SRuslan Bukin uint64_t features) 862c141c5c6SRuslan Bukin { 863c141c5c6SRuslan Bukin device_t dev, child; 864c141c5c6SRuslan Bukin 865c141c5c6SRuslan Bukin dev = sc->dev; 866c141c5c6SRuslan Bukin child = sc->vtmmio_child_dev; 867c141c5c6SRuslan Bukin 86861cefb9bSAlexander Motin if (device_is_attached(child) || bootverbose == 0) 869c141c5c6SRuslan Bukin return; 870c141c5c6SRuslan Bukin 871c141c5c6SRuslan Bukin virtio_describe(dev, msg, features, sc->vtmmio_child_feat_desc); 872c141c5c6SRuslan Bukin } 873c141c5c6SRuslan Bukin 874c141c5c6SRuslan Bukin static void 875c141c5c6SRuslan Bukin vtmmio_probe_and_attach_child(struct vtmmio_softc *sc) 876c141c5c6SRuslan Bukin { 877c141c5c6SRuslan Bukin device_t dev, child; 878c141c5c6SRuslan Bukin 879c141c5c6SRuslan Bukin dev = sc->dev; 880c141c5c6SRuslan Bukin child = sc->vtmmio_child_dev; 881c141c5c6SRuslan Bukin 882c141c5c6SRuslan Bukin if (child == NULL) 883c141c5c6SRuslan Bukin return; 884c141c5c6SRuslan Bukin 885c141c5c6SRuslan Bukin if (device_get_state(child) != DS_NOTPRESENT) { 886c141c5c6SRuslan Bukin return; 887c141c5c6SRuslan Bukin } 888c141c5c6SRuslan Bukin 889c141c5c6SRuslan Bukin if (device_probe(child) != 0) { 890c141c5c6SRuslan Bukin return; 891c141c5c6SRuslan Bukin } 892c141c5c6SRuslan Bukin 893c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER); 894c141c5c6SRuslan Bukin if (device_attach(child) != 0) { 895c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_FAILED); 896c141c5c6SRuslan Bukin vtmmio_reset(sc); 897c141c5c6SRuslan Bukin vtmmio_release_child_resources(sc); 898c141c5c6SRuslan Bukin /* Reset status for future attempt. */ 899c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_ACK); 900c141c5c6SRuslan Bukin } else { 901c141c5c6SRuslan Bukin vtmmio_set_status(dev, VIRTIO_CONFIG_STATUS_DRIVER_OK); 902c141c5c6SRuslan Bukin VIRTIO_ATTACH_COMPLETED(child); 903c141c5c6SRuslan Bukin } 904c141c5c6SRuslan Bukin } 905c141c5c6SRuslan Bukin 906c141c5c6SRuslan Bukin static int 907c141c5c6SRuslan Bukin vtmmio_reinit_virtqueue(struct vtmmio_softc *sc, int idx) 908c141c5c6SRuslan Bukin { 909c141c5c6SRuslan Bukin struct vtmmio_virtqueue *vqx; 910c141c5c6SRuslan Bukin struct virtqueue *vq; 911c141c5c6SRuslan Bukin int error; 912c141c5c6SRuslan Bukin uint16_t size; 913c141c5c6SRuslan Bukin 914c141c5c6SRuslan Bukin vqx = &sc->vtmmio_vqs[idx]; 915c141c5c6SRuslan Bukin vq = vqx->vtv_vq; 916c141c5c6SRuslan Bukin 917c141c5c6SRuslan Bukin KASSERT(vq != NULL, ("%s: vq %d not allocated", __func__, idx)); 918c141c5c6SRuslan Bukin 919c141c5c6SRuslan Bukin vtmmio_select_virtqueue(sc, idx); 9204dbff239SAndrew Turner size = vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX); 921c141c5c6SRuslan Bukin 922c141c5c6SRuslan Bukin error = virtqueue_reinit(vq, size); 923c141c5c6SRuslan Bukin if (error) 924c141c5c6SRuslan Bukin return (error); 925c141c5c6SRuslan Bukin 926046096d5SJessica Clarke vtmmio_set_virtqueue(sc, vq, size); 927c141c5c6SRuslan Bukin 928c141c5c6SRuslan Bukin return (0); 929c141c5c6SRuslan Bukin } 930c141c5c6SRuslan Bukin 931c141c5c6SRuslan Bukin static void 932c141c5c6SRuslan Bukin vtmmio_free_interrupts(struct vtmmio_softc *sc) 933c141c5c6SRuslan Bukin { 934c141c5c6SRuslan Bukin 935c141c5c6SRuslan Bukin if (sc->ih != NULL) 936c141c5c6SRuslan Bukin bus_teardown_intr(sc->dev, sc->res[1], sc->ih); 937c141c5c6SRuslan Bukin 938c141c5c6SRuslan Bukin if (sc->res[1] != NULL) 939c141c5c6SRuslan Bukin bus_release_resource(sc->dev, SYS_RES_IRQ, 0, sc->res[1]); 940c141c5c6SRuslan Bukin } 941c141c5c6SRuslan Bukin 942c141c5c6SRuslan Bukin static void 943c141c5c6SRuslan Bukin vtmmio_free_virtqueues(struct vtmmio_softc *sc) 944c141c5c6SRuslan Bukin { 945c141c5c6SRuslan Bukin struct vtmmio_virtqueue *vqx; 946c141c5c6SRuslan Bukin int idx; 947c141c5c6SRuslan Bukin 948c141c5c6SRuslan Bukin for (idx = 0; idx < sc->vtmmio_nvqs; idx++) { 949c141c5c6SRuslan Bukin vqx = &sc->vtmmio_vqs[idx]; 950c141c5c6SRuslan Bukin 951c141c5c6SRuslan Bukin vtmmio_select_virtqueue(sc, idx); 952926cedd9SBryan Venteicher if (sc->vtmmio_version > 1) { 953046096d5SJessica Clarke vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 0); 954926cedd9SBryan Venteicher vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_READY); 955926cedd9SBryan Venteicher } else 956926cedd9SBryan Venteicher vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0); 957c141c5c6SRuslan Bukin 958c141c5c6SRuslan Bukin virtqueue_free(vqx->vtv_vq); 959c141c5c6SRuslan Bukin vqx->vtv_vq = NULL; 960c141c5c6SRuslan Bukin } 961c141c5c6SRuslan Bukin 962c141c5c6SRuslan Bukin free(sc->vtmmio_vqs, M_DEVBUF); 963c141c5c6SRuslan Bukin sc->vtmmio_vqs = NULL; 964c141c5c6SRuslan Bukin sc->vtmmio_nvqs = 0; 965c141c5c6SRuslan Bukin } 966c141c5c6SRuslan Bukin 967c141c5c6SRuslan Bukin static void 968c141c5c6SRuslan Bukin vtmmio_release_child_resources(struct vtmmio_softc *sc) 969c141c5c6SRuslan Bukin { 970c141c5c6SRuslan Bukin 971c141c5c6SRuslan Bukin vtmmio_free_interrupts(sc); 972c141c5c6SRuslan Bukin vtmmio_free_virtqueues(sc); 973c141c5c6SRuslan Bukin } 974c141c5c6SRuslan Bukin 975c141c5c6SRuslan Bukin static void 976c141c5c6SRuslan Bukin vtmmio_reset(struct vtmmio_softc *sc) 977c141c5c6SRuslan Bukin { 978c141c5c6SRuslan Bukin 979c141c5c6SRuslan Bukin /* 980c141c5c6SRuslan Bukin * Setting the status to RESET sets the host device to 981c141c5c6SRuslan Bukin * the original, uninitialized state. 982c141c5c6SRuslan Bukin */ 983c141c5c6SRuslan Bukin vtmmio_set_status(sc->dev, VIRTIO_CONFIG_STATUS_RESET); 984c141c5c6SRuslan Bukin } 985c141c5c6SRuslan Bukin 986c141c5c6SRuslan Bukin static void 987c141c5c6SRuslan Bukin vtmmio_select_virtqueue(struct vtmmio_softc *sc, int idx) 988c141c5c6SRuslan Bukin { 989c141c5c6SRuslan Bukin 9904dbff239SAndrew Turner vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_SEL, idx); 991c141c5c6SRuslan Bukin } 992c141c5c6SRuslan Bukin 993c141c5c6SRuslan Bukin static void 994c141c5c6SRuslan Bukin vtmmio_vq_intr(void *arg) 995c141c5c6SRuslan Bukin { 996c141c5c6SRuslan Bukin struct vtmmio_virtqueue *vqx; 997c141c5c6SRuslan Bukin struct vtmmio_softc *sc; 998c141c5c6SRuslan Bukin struct virtqueue *vq; 9994dbff239SAndrew Turner uint32_t status; 1000c141c5c6SRuslan Bukin int idx; 1001c141c5c6SRuslan Bukin 1002c141c5c6SRuslan Bukin sc = arg; 1003c141c5c6SRuslan Bukin 10044dbff239SAndrew Turner status = vtmmio_read_config_4(sc, VIRTIO_MMIO_INTERRUPT_STATUS); 10054dbff239SAndrew Turner vtmmio_write_config_4(sc, VIRTIO_MMIO_INTERRUPT_ACK, status); 10064dbff239SAndrew Turner 10074dbff239SAndrew Turner /* The config changed */ 10084dbff239SAndrew Turner if (status & VIRTIO_MMIO_INT_CONFIG) 10094dbff239SAndrew Turner if (sc->vtmmio_child_dev != NULL) 10104dbff239SAndrew Turner VIRTIO_CONFIG_CHANGE(sc->vtmmio_child_dev); 10114dbff239SAndrew Turner 1012c141c5c6SRuslan Bukin /* Notify all virtqueues. */ 10134dbff239SAndrew Turner if (status & VIRTIO_MMIO_INT_VRING) { 1014c141c5c6SRuslan Bukin for (idx = 0; idx < sc->vtmmio_nvqs; idx++) { 1015c141c5c6SRuslan Bukin vqx = &sc->vtmmio_vqs[idx]; 10164dbff239SAndrew Turner if (vqx->vtv_no_intr == 0) { 1017c141c5c6SRuslan Bukin vq = vqx->vtv_vq; 1018c141c5c6SRuslan Bukin virtqueue_intr(vq); 10194dbff239SAndrew Turner } 10204dbff239SAndrew Turner } 10214dbff239SAndrew Turner } 1022c141c5c6SRuslan Bukin } 1023