Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 6 May 2020 23:28:51 +0000 (UTC)
From:      Jessica Clarke <jrtc27@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r360722 - head/sys/dev/virtio/mmio
Message-ID:  <202005062328.046NSp8x079151@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jrtc27
Date: Wed May  6 23:28:51 2020
New Revision: 360722
URL: https://svnweb.freebsd.org/changeset/base/360722

Log:
  virtio_mmio: Support non-transitional version 2 devices
  
  The non-legacy virtio MMIO specification drops the use of PFNs and
  replaces them with physical addresses. Whilst many implementations are
  so-called transitional devices, also implementing the legacy
  specification, TinyEMU[1] does not. Device-specific configuration
  registers have also changed to being little-endian, and must be accessed
  using a single aligned access for registers up to 32 bits, and two
  32-bit aligned accesses for 64-bit registers.
  
  [1] https://bellard.org/tinyemu/
  
  Reviewed by:	br, brooks (mentor)
  Approved by:	br, brooks (mentor)
  Differential Revision:	https://reviews.freebsd.org/D24681

Modified:
  head/sys/dev/virtio/mmio/virtio_mmio.c
  head/sys/dev/virtio/mmio/virtio_mmio.h

Modified: head/sys/dev/virtio/mmio/virtio_mmio.c
==============================================================================
--- head/sys/dev/virtio/mmio/virtio_mmio.c	Wed May  6 23:23:22 2020	(r360721)
+++ head/sys/dev/virtio/mmio/virtio_mmio.c	Wed May  6 23:28:51 2020	(r360722)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/module.h>
 #include <sys/malloc.h>
 #include <sys/rman.h>
+#include <sys/endian.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -76,6 +77,8 @@ static int	vtmmio_read_ivar(device_t, device_t, int, u
 static int	vtmmio_write_ivar(device_t, device_t, int, uintptr_t);
 static uint64_t	vtmmio_negotiate_features(device_t, uint64_t);
 static int	vtmmio_with_feature(device_t, uint64_t);
+static void	vtmmio_set_virtqueue(struct vtmmio_softc *sc,
+		    struct virtqueue *vq, uint32_t size);
 static int	vtmmio_alloc_virtqueues(device_t, int, int,
 		    struct vq_alloc_info *);
 static int	vtmmio_setup_intr(device_t, enum intr_type);
@@ -223,6 +226,16 @@ vtmmio_attach(device_t dev)
 		return (ENXIO);
 	}
 
+	sc->vtmmio_version = vtmmio_read_config_4(sc, VIRTIO_MMIO_VERSION);
+	if (sc->vtmmio_version < 1 || sc->vtmmio_version > 2) {
+		device_printf(dev, "Unsupported version: %x\n",
+		    sc->vtmmio_version);
+		bus_release_resource(dev, SYS_RES_MEMORY, 0,
+		    sc->res[0]);
+		sc->res[0] = NULL;
+		return (ENXIO);
+	}
+
 	vtmmio_reset(sc);
 
 	/* Tell the host we've noticed this device. */
@@ -404,6 +417,46 @@ vtmmio_with_feature(device_t dev, uint64_t feature)
 	return ((sc->vtmmio_features & feature) != 0);
 }
 
+static void
+vtmmio_set_virtqueue(struct vtmmio_softc *sc, struct virtqueue *vq,
+    uint32_t size)
+{
+	vm_paddr_t paddr;
+
+	vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size);
+#if 0
+	device_printf(dev, "virtqueue paddr 0x%08lx\n",
+	    (uint64_t)paddr);
+#endif
+	if (sc->vtmmio_version == 1) {
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN,
+		    VIRTIO_MMIO_VRING_ALIGN);
+		paddr = virtqueue_paddr(vq);
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN,
+		    paddr >> PAGE_SHIFT);
+	} else {
+		paddr = virtqueue_desc_paddr(vq);
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_LOW,
+		    paddr);
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_DESC_HIGH,
+		    paddr >> 32);
+
+		paddr = virtqueue_avail_paddr(vq);
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_LOW,
+		    paddr);
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_AVAIL_HIGH,
+		    paddr >> 32);
+
+		paddr = virtqueue_used_paddr(vq);
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_LOW,
+		    paddr);
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_USED_HIGH,
+		    paddr >> 32);
+
+		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 1);
+	}
+}
+
 static int
 vtmmio_alloc_virtqueues(device_t dev, int flags, int nvqs,
     struct vq_alloc_info *vq_info)
@@ -448,15 +501,7 @@ vtmmio_alloc_virtqueues(device_t dev, int flags, int n
 			break;
 		}
 
-		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size);
-		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN,
-		    VIRTIO_MMIO_VRING_ALIGN);
-#if 0
-		device_printf(dev, "virtqueue paddr 0x%08lx\n",
-		    (uint64_t)virtqueue_paddr(vq));
-#endif
-		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN,
-		    virtqueue_paddr(vq) >> PAGE_SHIFT);
+		vtmmio_set_virtqueue(sc, vq, size);
 
 		vqx->vtv_vq = *info->vqai_vq = vq;
 		vqx->vtv_no_intr = info->vqai_intr == NULL;
@@ -568,10 +613,54 @@ vtmmio_read_dev_config(device_t dev, bus_size_t offset
 	bus_size_t off;
 	uint8_t *d;
 	int size;
+	uint64_t low32, high32;
 
 	sc = device_get_softc(dev);
 	off = VIRTIO_MMIO_CONFIG + offset;
 
+	/*
+	 * The non-legacy MMIO specification adds the following restriction:
+	 *
+	 *   4.2.2.2: For the device-specific configuration space, the driver
+	 *   MUST use 8 bit wide accesses for 8 bit wide fields, 16 bit wide
+	 *   and aligned accesses for 16 bit wide fields and 32 bit wide and
+	 *   aligned accesses for 32 and 64 bit wide fields.
+	 *
+	 * The endianness also varies between non-legacy and legacy:
+	 *
+	 *   2.4: Note: The device configuration space uses the little-endian
+	 *   format for multi-byte fields.
+	 *
+	 *   2.4.3: Note that for legacy interfaces, device configuration space
+	 *   is generally the guest’s native endian, rather than PCI’s
+	 *   little-endian. The correct endian-ness is documented for each
+	 *   device.
+	 */
+	if (sc->vtmmio_version > 1) {
+		switch (length) {
+		case 1:
+			*(uint8_t *)dst = vtmmio_read_config_1(sc, off);
+			break;
+		case 2:
+			*(uint16_t *)dst =
+			    le16toh(vtmmio_read_config_2(sc, off));
+			break;
+		case 4:
+			*(uint32_t *)dst =
+			    le32toh(vtmmio_read_config_4(sc, off));
+			break;
+		case 8:
+			low32 = le32toh(vtmmio_read_config_4(sc, off));
+			high32 = le32toh(vtmmio_read_config_4(sc, off + 4));
+			*(uint64_t *)dst = (high32 << 32) | low32;
+			break;
+		default:
+			panic("%s: invalid length %d\n", __func__, length);
+		}
+
+		return;
+	}
+
 	for (d = dst; length > 0; d += size, off += size, length -= size) {
 #ifdef ALLOW_WORD_ALIGNED_ACCESS
 		if (length >= 4) {
@@ -601,6 +690,37 @@ vtmmio_write_dev_config(device_t dev, bus_size_t offse
 	sc = device_get_softc(dev);
 	off = VIRTIO_MMIO_CONFIG + offset;
 
+	/*
+	 * The non-legacy MMIO specification adds size and alignment
+	 * restrctions. It also changes the endianness from native-endian to
+	 * little-endian. See vtmmio_read_dev_config.
+	 */
+	if (sc->vtmmio_version > 1) {
+		switch (length) {
+		case 1:
+			vtmmio_write_config_1(sc, off, *(uint8_t *)src);
+			break;
+		case 2:
+			vtmmio_write_config_2(sc, off,
+			    htole16(*(uint16_t *)src));
+			break;
+		case 4:
+			vtmmio_write_config_4(sc, off,
+			    htole32(*(uint32_t *)src));
+			break;
+		case 8:
+			vtmmio_write_config_4(sc, off,
+			    htole32(*(uint64_t *)src));
+			vtmmio_write_config_4(sc, off + 4,
+			    htole32((*(uint64_t *)src) >> 32));
+			break;
+		default:
+			panic("%s: invalid length %d\n", __func__, length);
+		}
+
+		return;
+	}
+
 	for (s = src; length > 0; s += size, off += size, length -= size) {
 #ifdef ALLOW_WORD_ALIGNED_ACCESS
 		if (length >= 4) {
@@ -685,15 +805,7 @@ vtmmio_reinit_virtqueue(struct vtmmio_softc *sc, int i
 	if (error)
 		return (error);
 
-	vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_NUM, size);
-	vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_ALIGN,
-	    VIRTIO_MMIO_VRING_ALIGN);
-#if 0
-	device_printf(sc->dev, "virtqueue paddr 0x%08lx\n",
-	    (uint64_t)virtqueue_paddr(vq));
-#endif
-	vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN,
-	    virtqueue_paddr(vq) >> PAGE_SHIFT);
+	vtmmio_set_virtqueue(sc, vq, size);
 
 	return (0);
 }
@@ -719,7 +831,10 @@ vtmmio_free_virtqueues(struct vtmmio_softc *sc)
 		vqx = &sc->vtmmio_vqs[idx];
 
 		vtmmio_select_virtqueue(sc, idx);
-		vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0);
+		if (sc->vtmmio_version == 1)
+			vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_PFN, 0);
+		else
+			vtmmio_write_config_4(sc, VIRTIO_MMIO_QUEUE_READY, 0);
 
 		virtqueue_free(vqx->vtv_vq);
 		vqx->vtv_vq = NULL;

Modified: head/sys/dev/virtio/mmio/virtio_mmio.h
==============================================================================
--- head/sys/dev/virtio/mmio/virtio_mmio.h	Wed May  6 23:23:22 2020	(r360721)
+++ head/sys/dev/virtio/mmio/virtio_mmio.h	Wed May  6 23:28:51 2020	(r360722)
@@ -44,6 +44,7 @@ struct vtmmio_softc {
 
 	uint64_t			vtmmio_features;
 	uint32_t			vtmmio_flags;
+	uint32_t			vtmmio_version;
 
 	/* This "bus" will only ever have one child. */
 	device_t			vtmmio_child_dev;
@@ -64,16 +65,24 @@ int vtmmio_attach(device_t);
 #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_GUEST_PAGE_SIZE	0x028	/* version 1 only */
 #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_QUEUE_ALIGN		0x03c	/* version 1 only */
+#define	VIRTIO_MMIO_QUEUE_PFN		0x040	/* version 1 only */
+#define	VIRTIO_MMIO_QUEUE_READY		0x044	/* requires version 2 */
 #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_QUEUE_DESC_LOW	0x080	/* requires version 2 */
+#define	VIRTIO_MMIO_QUEUE_DESC_HIGH	0x084	/* requires version 2 */
+#define	VIRTIO_MMIO_QUEUE_AVAIL_LOW	0x090	/* requires version 2 */
+#define	VIRTIO_MMIO_QUEUE_AVAIL_HIGH	0x094	/* requires version 2 */
+#define	VIRTIO_MMIO_QUEUE_USED_LOW	0x0a0	/* requires version 2 */
+#define	VIRTIO_MMIO_QUEUE_USED_HIGH	0x0a4	/* requires version 2 */
+#define	VIRTIO_MMIO_CONFIG_GENERATION	0x100	/* requires version 2 */
 #define	VIRTIO_MMIO_CONFIG		0x100
 #define	VIRTIO_MMIO_INT_VRING		(1 << 0)
 #define	VIRTIO_MMIO_INT_CONFIG		(1 << 1)



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202005062328.046NSp8x079151>