svn commit: r360722 - head/sys/dev/virtio/mmio
Jessica Clarke
jrtc27 at FreeBSD.org
Wed May 6 23:28:52 UTC 2020
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)
More information about the svn-src-head
mailing list