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)
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to