Module Name: src Committed By: thorpej Date: Tue Jan 2 07:24:50 UTC 2024
Modified Files: src/sys/dev/virtio: virtio_mmio.c virtio_mmiovar.h Log Message: - The VirtIO 1.0 spec says that the MMIO interface uses little-endian registers. For some VMs, at least, this appears to be independent of the config/struct byte-order. Detect this and handle it. - Add support for the "v2" MMIO personality. To generate a diff of this commit: cvs rdiff -u -r1.11 -r1.12 src/sys/dev/virtio/virtio_mmio.c cvs rdiff -u -r1.5 -r1.6 src/sys/dev/virtio/virtio_mmiovar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/virtio/virtio_mmio.c diff -u src/sys/dev/virtio/virtio_mmio.c:1.11 src/sys/dev/virtio/virtio_mmio.c:1.12 --- src/sys/dev/virtio/virtio_mmio.c:1.11 Fri Jul 7 07:19:36 2023 +++ src/sys/dev/virtio/virtio_mmio.c Tue Jan 2 07:24:50 2024 @@ -1,6 +1,35 @@ -/* $NetBSD: virtio_mmio.c,v 1.11 2023/07/07 07:19:36 rin Exp $ */ +/* $NetBSD: virtio_mmio.c,v 1.12 2024/01/02 07:24:50 thorpej Exp $ */ /* $OpenBSD: virtio_mmio.c,v 1.2 2017/02/24 17:12:31 patrick Exp $ */ +/*- + * Copyright (c) 2024 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + /* * Copyright (c) 2014 Patrick Wildt <patr...@blueri.se> * Copyright (c) 2012 Stefan Fritsch. @@ -29,7 +58,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: virtio_mmio.c,v 1.11 2023/07/07 07:19:36 rin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: virtio_mmio.c,v 1.12 2024/01/02 07:24:50 thorpej Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -46,20 +75,28 @@ __KERNEL_RCSID(0, "$NetBSD: virtio_mmio. #define VIRTIO_MMIO_VERSION 0x004 #define VIRTIO_MMIO_DEVICE_ID 0x008 #define VIRTIO_MMIO_VENDOR_ID 0x00c -#define VIRTIO_MMIO_HOST_FEATURES 0x010 -#define VIRTIO_MMIO_HOST_FEATURES_SEL 0x014 -#define VIRTIO_MMIO_GUEST_FEATURES 0x020 -#define VIRTIO_MMIO_GUEST_FEATURES_SEL 0x024 -#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028 +#define VIRTIO_MMIO_DEVICE_FEATURES 0x010 /* "HostFeatures" in v1 */ +#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 /* "HostFeaturesSel" in v1 */ +#define VIRTIO_MMIO_DRIVER_FEATURES 0x020 /* "GuestFeatures" in v1 */ +#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 /* "GuestFeaturesSel" in v1 */ +#define VIRTIO_MMIO_V1_GUEST_PAGE_SIZE 0x028 #define VIRTIO_MMIO_QUEUE_SEL 0x030 #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 #define VIRTIO_MMIO_QUEUE_NUM 0x038 -#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c -#define VIRTIO_MMIO_QUEUE_PFN 0x040 +#define VIRTIO_MMIO_V1_QUEUE_ALIGN 0x03c +#define VIRTIO_MMIO_V1_QUEUE_PFN 0x040 +#define VIRTIO_MMIO_QUEUE_READY 0x044 #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 #define VIRTIO_MMIO_INTERRUPT_ACK 0x064 #define VIRTIO_MMIO_STATUS 0x070 +#define VIRTIO_MMIO_V2_QUEUE_DESC_LOW 0x080 +#define VIRTIO_MMIO_V2_QUEUE_DESC_HIGH 0x084 +#define VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW 0x090 +#define VIRTIO_MMIO_V2_QUEUE_AVAIL_HIGH 0x094 +#define VIRTIO_MMIO_V2_QUEUE_USED_LOW 0x0a0 +#define VIRTIO_MMIO_V2_QUEUE_USED_HIGH 0x0a4 +#define VIRTIO_MMIO_V2_CONFIG_GEN 0x0fc #define VIRTIO_MMIO_CONFIG 0x100 #define VIRTIO_MMIO_INT_VRING (1 << 0) @@ -85,17 +122,59 @@ __KERNEL_RCSID(0, "$NetBSD: virtio_mmio. static void virtio_mmio_kick(struct virtio_softc *, uint16_t); static uint16_t virtio_mmio_read_queue_size(struct virtio_softc *, uint16_t); -static void virtio_mmio_setup_queue(struct virtio_softc *, uint16_t, uint64_t); +static void virtio_mmio_v1_setup_queue(struct virtio_softc *, uint16_t, uint64_t); +static void virtio_mmio_v2_setup_queue(struct virtio_softc *, uint16_t, uint64_t); static void virtio_mmio_set_status(struct virtio_softc *, int); static void virtio_mmio_negotiate_features(struct virtio_softc *, uint64_t); static int virtio_mmio_alloc_interrupts(struct virtio_softc *); static void virtio_mmio_free_interrupts(struct virtio_softc *); static int virtio_mmio_setup_interrupts(struct virtio_softc *, int); -static const struct virtio_ops virtio_mmio_ops = { +static uint32_t +virtio_mmio_reg_read(struct virtio_mmio_softc *sc, bus_addr_t reg) +{ + uint32_t val; + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg); + if (sc->sc_le_regs) { + val = le32toh(val); + } + return val; +} + +static void +virtio_mmio_reg_write(struct virtio_mmio_softc *sc, bus_addr_t reg, + uint32_t val) +{ + if (sc->sc_le_regs) { + val = htole32(val); + } + bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val); +} + +static void +virtio_mmio_v2_set_addr(struct virtio_mmio_softc *sc, bus_addr_t reg, + uint64_t addr) +{ + virtio_mmio_reg_write(sc, reg, BUS_ADDR_LO32(addr)); + virtio_mmio_reg_write(sc, reg + 4, BUS_ADDR_HI32(addr)); +} + +static const struct virtio_ops virtio_mmio_v1_ops = { .kick = virtio_mmio_kick, .read_queue_size = virtio_mmio_read_queue_size, - .setup_queue = virtio_mmio_setup_queue, + .setup_queue = virtio_mmio_v1_setup_queue, + .set_status = virtio_mmio_set_status, + .neg_features = virtio_mmio_negotiate_features, + .alloc_interrupts = virtio_mmio_alloc_interrupts, + .free_interrupts = virtio_mmio_free_interrupts, + .setup_interrupts = virtio_mmio_setup_interrupts, +}; + +static const struct virtio_ops virtio_mmio_v2_ops = { + .kick = virtio_mmio_kick, + .read_queue_size = virtio_mmio_read_queue_size, + .setup_queue = virtio_mmio_v2_setup_queue, .set_status = virtio_mmio_set_status, .neg_features = virtio_mmio_negotiate_features, .alloc_interrupts = virtio_mmio_alloc_interrupts, @@ -107,36 +186,61 @@ static uint16_t virtio_mmio_read_queue_size(struct virtio_softc *vsc, uint16_t idx) { struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_SEL, idx); - return bus_space_read_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_QUEUE_NUM_MAX); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx); + return virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX); } static void -virtio_mmio_setup_queue(struct virtio_softc *vsc, uint16_t idx, uint64_t addr) +virtio_mmio_v1_setup_queue(struct virtio_softc *vsc, uint16_t idx, + uint64_t addr) { struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_SEL, idx); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NUM, - bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NUM_MAX)); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_ALIGN, + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM, + virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX)); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_ALIGN, VIRTIO_PAGE_SIZE); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_PFN, + virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_PFN, addr / VIRTIO_PAGE_SIZE); } static void +virtio_mmio_v2_setup_queue(struct virtio_softc *vsc, uint16_t idx, + uint64_t addr) +{ + struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; + struct virtqueue *vq = &vsc->sc_vqs[idx]; + KASSERT(vq->vq_index == idx); + + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx); + if (addr == 0) { + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 0); + virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW, 0); + virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW, 0); + virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW, 0); + } else { + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM, + virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX)); + virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW, + addr); + virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW, + addr + vq->vq_availoffset); + virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW, + addr + vq->vq_usedoffset); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 1); + } +} + +static void virtio_mmio_set_status(struct virtio_softc *vsc, int status) { struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; int old = 0; if (status != 0) - old = bus_space_read_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_STATUS); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_STATUS, - status|old); + old = virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_STATUS, status|old); } bool @@ -144,11 +248,13 @@ virtio_mmio_common_probe_present(struct { uint32_t magic; + /* XXX */ magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_MAGIC_VALUE); return (magic == VIRTIO_MMIO_MAGIC); } + void virtio_mmio_common_attach(struct virtio_mmio_softc *sc) { @@ -159,32 +265,44 @@ virtio_mmio_common_attach(struct virtio_ magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_MAGIC_VALUE); if (magic != VIRTIO_MMIO_MAGIC) { - aprint_error_dev(vsc->sc_dev, - "wrong magic value 0x%08x; giving up\n", magic); - return; + if (magic == le32toh(VIRTIO_MMIO_MAGIC)) { + sc->sc_le_regs = true; + } else { + aprint_error_dev(vsc->sc_dev, + "wrong magic value 0x%08x; giving up\n", magic); + return; + } } + vsc->sc_bus_endian = READ_ENDIAN; + vsc->sc_struct_endian = STRUCT_ENDIAN; - ver = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_VERSION); - if (ver != 1) { + ver = virtio_mmio_reg_read(sc, VIRTIO_MMIO_VERSION); + switch (ver) { + case 1: + /* we could use PAGE_SIZE, but virtio(4) assumes 4KiB for now */ + virtio_mmio_reg_write(sc, + VIRTIO_MMIO_V1_GUEST_PAGE_SIZE, VIRTIO_PAGE_SIZE); + vsc->sc_ops = &virtio_mmio_v1_ops; + break; + + case 2: + vsc->sc_ops = &virtio_mmio_v2_ops; + break; + + default: aprint_error_dev(vsc->sc_dev, - "unknown version 0x%02x; giving up\n", ver); + "unknown version 0x%08x; giving up\n", ver); return; } + aprint_normal_dev(self, "VirtIO-MMIO v%d\n", ver); - id = bus_space_read_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_DEVICE_ID); - - /* we could use PAGE_SIZE, but virtio(4) assumes 4KiB for now */ - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_GUEST_PAGE_SIZE, - VIRTIO_PAGE_SIZE); - - /* no device connected. */ - if (id == 0) + id = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_ID); + if (id == 0) { + /* no device connected. */ return; + } virtio_print_device_type(self, id, ver); - vsc->sc_ops = &virtio_mmio_ops; - vsc->sc_bus_endian = READ_ENDIAN; - vsc->sc_struct_endian = STRUCT_ENDIAN; /* set up our device config tag */ vsc->sc_devcfg_iosize = sc->sc_iosize - VIRTIO_MMIO_CONFIG; @@ -232,20 +350,16 @@ virtio_mmio_common_detach(struct virtio_ */ static void virtio_mmio_negotiate_features(struct virtio_softc *vsc, uint64_t - guest_features) + driver_features) { struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; uint32_t r; - bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_HOST_FEATURES_SEL, 0); - r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_HOST_FEATURES); - r &= guest_features; - bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_GUEST_FEATURES_SEL, 0); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_GUEST_FEATURES, r); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0); + r = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES); + r &= driver_features; + virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES, r); vsc->sc_active_features = r; } @@ -261,10 +375,8 @@ virtio_mmio_intr(void *arg) int isr, r = 0; /* check and ack the interrupt */ - isr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_INTERRUPT_STATUS); - bus_space_write_4(sc->sc_iot, sc->sc_ioh, - VIRTIO_MMIO_INTERRUPT_ACK, isr); + isr = virtio_mmio_reg_read(sc, VIRTIO_MMIO_INTERRUPT_STATUS); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_INTERRUPT_ACK, isr); if ((isr & VIRTIO_MMIO_INT_CONFIG) && (vsc->sc_config_change != NULL)) r = (vsc->sc_config_change)(vsc); @@ -283,8 +395,7 @@ static void virtio_mmio_kick(struct virtio_softc *vsc, uint16_t idx) { struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; - bus_space_write_4(sc->sc_iot, sc->sc_ioh, VIRTIO_MMIO_QUEUE_NOTIFY, - idx); + virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NOTIFY, idx); } static int Index: src/sys/dev/virtio/virtio_mmiovar.h diff -u src/sys/dev/virtio/virtio_mmiovar.h:1.5 src/sys/dev/virtio/virtio_mmiovar.h:1.6 --- src/sys/dev/virtio/virtio_mmiovar.h:1.5 Fri Oct 22 02:57:23 2021 +++ src/sys/dev/virtio/virtio_mmiovar.h Tue Jan 2 07:24:50 2024 @@ -1,4 +1,4 @@ -/* $NetBSD: virtio_mmiovar.h,v 1.5 2021/10/22 02:57:23 yamaguchi Exp $ */ +/* $NetBSD: virtio_mmiovar.h,v 1.6 2024/01/02 07:24:50 thorpej Exp $ */ /* * Copyright (c) 2018 Jonathan A. Kollasch * All rights reserved. @@ -36,6 +36,7 @@ struct virtio_mmio_softc { bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; bus_size_t sc_iosize; + bool sc_le_regs; void *sc_ih;