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 <[email protected]>
* 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;