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;
 

Reply via email to