Просмотр исходного кода

[virtio] Add modern VirtIO (v1.2.0) support with backward compatibility

- Updated Kconfig to support both legacy and modern versions
- Added version field to virtio_device structure
- Implemented 64-bit feature negotiation for modern virtio
- Updated queue initialization for modern virtio (separate desc/driver/device areas)
- Added FEATURES_OK check for modern virtio
- Updated all device drivers (blk, net, console, gpu, input) to use new APIs
- Updated BSP drivers to accept both version 1 (legacy) and version 2 (modern)

Co-authored-by: BernardXiong <1241087+BernardXiong@users.noreply.github.com>
copilot-swe-agent[bot] 3 месяцев назад
Родитель
Сommit
81d0c2a75d

+ 6 - 1
bsp/qemu-virt64-aarch64/drivers/drv_virtio.c

@@ -74,12 +74,17 @@ int rt_virtio_devices_init(void)
         mmio_config = (struct virtio_mmio_config *)mmio_base;
 
         if (mmio_config->magic != VIRTIO_MAGIC_VALUE ||
-            mmio_config->version != RT_USING_VIRTIO_VERSION ||
             mmio_config->vendor_id != VIRTIO_VENDOR_ID)
         {
             continue;
         }
 
+        /* Support both legacy (0x1) and modern (0x2) versions */
+        if (mmio_config->version != 1 && mmio_config->version != 2)
+        {
+            continue;
+        }
+
         init_handler = virtio_device_init_handlers[mmio_config->device_id];
 
         if (init_handler != RT_NULL)

+ 6 - 1
bsp/qemu-virt64-riscv/driver/drv_virtio.c

@@ -80,12 +80,17 @@ int rt_virtio_devices_init(void)
         mmio_config = (struct virtio_mmio_config *)mmio_base;
 
         if (mmio_config->magic != VIRTIO_MAGIC_VALUE ||
-            mmio_config->version != RT_USING_VIRTIO_VERSION ||
             mmio_config->vendor_id != VIRTIO_VENDOR_ID)
         {
             continue;
         }
 
+        /* Support both legacy (0x1) and modern (0x2) versions */
+        if (mmio_config->version != 1 && mmio_config->version != 2)
+        {
+            continue;
+        }
+
         init_handler = virtio_device_init_handlers[mmio_config->device_id];
 
         if (init_handler != RT_NULL)

+ 14 - 4
components/drivers/virtio/Kconfig

@@ -5,10 +5,20 @@ menuconfig RT_USING_VIRTIO
     if RT_USING_VIRTIO
         choice RT_USING_VIRTIO_VERSION
             prompt "VirtIO Version"
-            default RT_USING_VIRTIO10
-
-            config RT_USING_VIRTIO10
-                bool "VirtIO v1.0"
+            default RT_USING_VIRTIO_LEGACY
+
+            config RT_USING_VIRTIO_LEGACY
+                bool "VirtIO Legacy (v0.95)"
+                help
+                    Support for VirtIO legacy interface (version 0x1).
+                    This is the older version compatible with most existing QEMU versions.
+
+            config RT_USING_VIRTIO_MODERN
+                bool "VirtIO Modern (v1.0+)"
+                help
+                    Support for VirtIO modern interface (version 0x2).
+                    This version supports VirtIO 1.0, 1.1, and 1.2 specifications.
+                    Requires QEMU 2.4+ or compatible hypervisor.
         endchoice
 
         config RT_USING_VIRTIO_MMIO_ALIGN

+ 122 - 6
components/drivers/virtio/virtio.c

@@ -38,7 +38,27 @@ void virtio_status_driver_ok(struct virtio_device *dev)
 {
     _virtio_dev_check(dev);
 
-    dev->mmio_config->status |= VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK;
+    if (dev->version == 1)
+    {
+        /* Legacy virtio */
+        dev->mmio_config->status |= VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK;
+    }
+    else
+    {
+        /* Modern virtio: set FEATURES_OK and verify it */
+        dev->mmio_config->status |= VIRTIO_STATUS_FEATURES_OK;
+
+        /* Verify that device accepted the features */
+        if (!(dev->mmio_config->status & VIRTIO_STATUS_FEATURES_OK))
+        {
+            /* Device doesn't support our feature subset */
+            dev->mmio_config->status |= VIRTIO_STATUS_FAILED;
+            return;
+        }
+
+        /* Now set DRIVER_OK */
+        dev->mmio_config->status |= VIRTIO_STATUS_DRIVER_OK;
+    }
 }
 
 void virtio_interrupt_ack(struct virtio_device *dev)
@@ -59,7 +79,66 @@ rt_bool_t virtio_has_feature(struct virtio_device *dev, rt_uint32_t feature_bit)
 {
     _virtio_dev_check(dev);
 
-    return !!(dev->mmio_config->device_features & (1UL << feature_bit));
+    if (dev->version == 1)
+    {
+        /* Legacy: 32-bit feature bits only */
+        return !!(dev->mmio_config->device_features & (1UL << feature_bit));
+    }
+    else
+    {
+        /* Modern: Use 64-bit feature access */
+        rt_uint64_t features = virtio_get_features(dev);
+        return !!(features & (1ULL << feature_bit));
+    }
+}
+
+rt_uint64_t virtio_get_features(struct virtio_device *dev)
+{
+    rt_uint64_t features = 0;
+
+    _virtio_dev_check(dev);
+
+    if (dev->version == 1)
+    {
+        /* Legacy: only lower 32 bits */
+        features = dev->mmio_config->device_features;
+    }
+    else
+    {
+        /* Modern: read both 32-bit halves */
+        dev->mmio_config->device_features_sel = 0;
+        features = dev->mmio_config->device_features;
+
+        dev->mmio_config->device_features_sel = 1;
+        features |= ((rt_uint64_t)dev->mmio_config->device_features) << 32;
+    }
+
+    return features;
+}
+
+void virtio_set_features(struct virtio_device *dev, rt_uint64_t features)
+{
+    _virtio_dev_check(dev);
+
+    if (dev->version == 1)
+    {
+        /* Legacy: only lower 32 bits */
+        dev->mmio_config->driver_features = (rt_uint32_t)features;
+    }
+    else
+    {
+        /* Modern: write both 32-bit halves */
+        dev->mmio_config->driver_features_sel = 0;
+        dev->mmio_config->driver_features = (rt_uint32_t)features;
+
+        dev->mmio_config->driver_features_sel = 1;
+        dev->mmio_config->driver_features = (rt_uint32_t)(features >> 32);
+    }
+}
+
+rt_bool_t virtio_has_feature_64(struct virtio_device *dev, rt_uint64_t features, rt_uint32_t feature_bit)
+{
+    return !!(features & (1ULL << feature_bit));
 }
 
 rt_err_t virtio_queues_alloc(struct virtio_device *dev, rt_size_t queues_num)
@@ -93,6 +172,7 @@ rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, r
     void *pages;
     rt_size_t pages_total_size;
     struct virtq *queue;
+    rt_uint64_t desc_addr, avail_addr, used_addr;
 
     _virtio_dev_check(dev);
 
@@ -123,18 +203,44 @@ rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, r
 
     rt_memset(pages, 0, pages_total_size);
 
-    dev->mmio_config->guest_page_size = VIRTIO_PAGE_SIZE;
+    /* Set queue selector */
     dev->mmio_config->queue_sel = queue_index;
     dev->mmio_config->queue_num = ring_size;
-    dev->mmio_config->queue_align = VIRTIO_PAGE_SIZE;
-    dev->mmio_config->queue_pfn = VIRTIO_VA2PA(pages) >> VIRTIO_PAGE_SHIFT;
 
+    /* Calculate queue area addresses */
     queue->num = ring_size;
     queue->desc = (struct virtq_desc *)((rt_ubase_t)pages);
     queue->avail = (struct virtq_avail *)(((rt_ubase_t)pages) + VIRTQ_DESC_TOTAL_SIZE(ring_size));
     queue->used = (struct virtq_used *)VIRTIO_PAGE_ALIGN(
             (rt_ubase_t)&queue->avail->ring[ring_size] + VIRTQ_AVAIL_RES_SIZE);
 
+    desc_addr = VIRTIO_VA2PA(queue->desc);
+    avail_addr = VIRTIO_VA2PA(queue->avail);
+    used_addr = VIRTIO_VA2PA(queue->used);
+
+    if (dev->version == 1)
+    {
+        /* Legacy virtio: use queue_pfn */
+        dev->mmio_config->guest_page_size = VIRTIO_PAGE_SIZE;
+        dev->mmio_config->queue_align = VIRTIO_PAGE_SIZE;
+        dev->mmio_config->queue_pfn = desc_addr >> VIRTIO_PAGE_SHIFT;
+    }
+    else
+    {
+        /* Modern virtio: use separate descriptor/driver/device registers */
+        dev->mmio_config->queue_desc_low = (rt_uint32_t)desc_addr;
+        dev->mmio_config->queue_desc_high = (rt_uint32_t)(desc_addr >> 32);
+
+        dev->mmio_config->queue_driver_low = (rt_uint32_t)avail_addr;
+        dev->mmio_config->queue_driver_high = (rt_uint32_t)(avail_addr >> 32);
+
+        dev->mmio_config->queue_device_low = (rt_uint32_t)used_addr;
+        dev->mmio_config->queue_device_high = (rt_uint32_t)(used_addr >> 32);
+
+        /* Enable the queue */
+        dev->mmio_config->queue_ready = 1;
+    }
+
     queue->used_idx = 0;
 
     /* All descriptors start out unused */
@@ -165,7 +271,17 @@ void virtio_queue_destroy(struct virtio_device *dev, rt_uint32_t queue_index)
     rt_free_align((void *)queue->desc);
 
     dev->mmio_config->queue_sel = queue_index;
-    dev->mmio_config->queue_pfn = RT_NULL;
+
+    if (dev->version == 1)
+    {
+        /* Legacy virtio */
+        dev->mmio_config->queue_pfn = 0;
+    }
+    else
+    {
+        /* Modern virtio */
+        dev->mmio_config->queue_ready = 0;
+    }
 
     queue->num = 0;
     queue->desc = RT_NULL;

+ 13 - 2
components/drivers/virtio/virtio.h

@@ -24,8 +24,13 @@
 #error "Please set RT_NAME_MAX to at lest 16"
 #endif
 
-#ifdef RT_USING_VIRTIO10
-#define RT_USING_VIRTIO_VERSION 0x1
+/* VirtIO version configuration */
+#ifdef RT_USING_VIRTIO_LEGACY
+#define RT_USING_VIRTIO_VERSION 0x1  /* Legacy interface */
+#elif defined(RT_USING_VIRTIO_MODERN)
+#define RT_USING_VIRTIO_VERSION 0x2  /* Modern interface (1.0+) */
+#else
+#define RT_USING_VIRTIO_VERSION 0x1  /* Default to legacy */
 #endif
 
 #include <virtio_mmio.h>
@@ -111,6 +116,7 @@ enum
 struct virtio_device
 {
     rt_uint32_t irq;
+    rt_uint32_t version;        /* VirtIO version from MMIO config */
 
     struct virtq *queues;
     rt_size_t queues_num;
@@ -136,6 +142,11 @@ void virtio_status_driver_ok(struct virtio_device *dev);
 void virtio_interrupt_ack(struct virtio_device *dev);
 rt_bool_t virtio_has_feature(struct virtio_device *dev, rt_uint32_t feature_bit);
 
+/* Modern VirtIO feature negotiation (64-bit features) */
+rt_uint64_t virtio_get_features(struct virtio_device *dev);
+void virtio_set_features(struct virtio_device *dev, rt_uint64_t features);
+rt_bool_t virtio_has_feature_64(struct virtio_device *dev, rt_uint64_t features, rt_uint32_t feature_bit);
+
 rt_err_t virtio_queues_alloc(struct virtio_device *dev, rt_size_t queues_num);
 void virtio_queues_free(struct virtio_device *dev);
 rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t ring_size);

+ 19 - 8
components/drivers/virtio/virtio_blk.c

@@ -178,6 +178,7 @@ rt_err_t rt_virtio_blk_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     char dev_name[RT_NAME_MAX];
     struct virtio_device *virtio_dev;
     struct virtio_blk_device *virtio_blk_dev;
+    rt_uint64_t device_features, driver_features;
 
     virtio_blk_dev = rt_malloc(sizeof(struct virtio_blk_device));
 
@@ -189,6 +190,7 @@ rt_err_t rt_virtio_blk_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_dev = &virtio_blk_dev->virtio_dev;
     virtio_dev->irq = irq;
     virtio_dev->mmio_base = mmio_base;
+    virtio_dev->version = virtio_dev->mmio_config->version;
 
     virtio_blk_dev->config = (struct virtio_blk_config *)virtio_dev->mmio_config->config;
 
@@ -200,14 +202,23 @@ rt_err_t rt_virtio_blk_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_status_acknowledge_driver(virtio_dev);
 
     /* Negotiate features */
-    virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
-            (1 << VIRTIO_BLK_F_RO) |
-            (1 << VIRTIO_BLK_F_MQ) |
-            (1 << VIRTIO_BLK_F_SCSI) |
-            (1 << VIRTIO_BLK_F_CONFIG_WCE) |
-            (1 << VIRTIO_F_ANY_LAYOUT) |
-            (1 << VIRTIO_F_RING_EVENT_IDX) |
-            (1 << VIRTIO_F_RING_INDIRECT_DESC));
+    device_features = virtio_get_features(virtio_dev);
+    driver_features = device_features & ~(
+            (1ULL << VIRTIO_BLK_F_RO) |
+            (1ULL << VIRTIO_BLK_F_MQ) |
+            (1ULL << VIRTIO_BLK_F_SCSI) |
+            (1ULL << VIRTIO_BLK_F_CONFIG_WCE) |
+            (1ULL << VIRTIO_F_ANY_LAYOUT) |
+            (1ULL << VIRTIO_F_RING_EVENT_IDX) |
+            (1ULL << VIRTIO_F_RING_INDIRECT_DESC));
+
+    /* For modern virtio, we must support VERSION_1 */
+    if (virtio_dev->version == 2)
+    {
+        driver_features |= (1ULL << VIRTIO_F_VERSION_1);
+    }
+
+    virtio_set_features(virtio_dev, driver_features);
 
     /* Tell device that feature negotiation is complete and we're completely ready */
     virtio_status_driver_ok(virtio_dev);

+ 15 - 3
components/drivers/virtio/virtio_console.c

@@ -674,6 +674,7 @@ rt_err_t rt_virtio_console_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     char dev_name[RT_NAME_MAX];
     struct virtio_device *virtio_dev;
     struct virtio_console_device *virtio_console_dev;
+    rt_uint64_t device_features, driver_features;
 
     RT_ASSERT(RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR > 0);
 
@@ -687,6 +688,7 @@ rt_err_t rt_virtio_console_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_dev = &virtio_console_dev->virtio_dev;
     virtio_dev->irq = irq;
     virtio_dev->mmio_base = mmio_base;
+    virtio_dev->version = virtio_dev->mmio_config->version;
 
     virtio_console_dev->config = (struct virtio_console_config *)virtio_dev->mmio_config->config;
 
@@ -697,9 +699,19 @@ rt_err_t rt_virtio_console_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_reset_device(virtio_dev);
     virtio_status_acknowledge_driver(virtio_dev);
 
-    virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
-            (1 << VIRTIO_F_RING_EVENT_IDX) |
-            (1 << VIRTIO_F_RING_INDIRECT_DESC));
+    /* Negotiate features */
+    device_features = virtio_get_features(virtio_dev);
+    driver_features = device_features & ~(
+            (1ULL << VIRTIO_F_RING_EVENT_IDX) |
+            (1ULL << VIRTIO_F_RING_INDIRECT_DESC));
+
+    /* For modern virtio, we must support VERSION_1 */
+    if (virtio_dev->version == 2)
+    {
+        driver_features |= (1ULL << VIRTIO_F_VERSION_1);
+    }
+
+    virtio_set_features(virtio_dev, driver_features);
 
     virtio_status_driver_ok(virtio_dev);
 

+ 15 - 3
components/drivers/virtio/virtio_gpu.c

@@ -850,6 +850,7 @@ rt_err_t rt_virtio_gpu_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     char dev_name[RT_NAME_MAX];
     struct virtio_device *virtio_dev;
     struct virtio_gpu_device *virtio_gpu_dev;
+    rt_uint64_t device_features, driver_features;
 
     virtio_gpu_dev = rt_malloc(sizeof(struct virtio_gpu_device));
 
@@ -861,6 +862,7 @@ rt_err_t rt_virtio_gpu_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_dev = &virtio_gpu_dev->virtio_dev;
     virtio_dev->irq = irq;
     virtio_dev->mmio_base = mmio_base;
+    virtio_dev->version = virtio_dev->mmio_config->version;
 
     virtio_gpu_dev->pmode_id = VIRTIO_GPU_INVALID_PMODE_ID;
     virtio_gpu_dev->display_resource_id = 0;
@@ -877,9 +879,19 @@ rt_err_t rt_virtio_gpu_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_reset_device(virtio_dev);
     virtio_status_acknowledge_driver(virtio_dev);
 
-    virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
-            (1 << VIRTIO_F_RING_EVENT_IDX) |
-            (1 << VIRTIO_F_RING_INDIRECT_DESC));
+    /* Negotiate features */
+    device_features = virtio_get_features(virtio_dev);
+    driver_features = device_features & ~(
+            (1ULL << VIRTIO_F_RING_EVENT_IDX) |
+            (1ULL << VIRTIO_F_RING_INDIRECT_DESC));
+
+    /* For modern virtio, we must support VERSION_1 */
+    if (virtio_dev->version == 2)
+    {
+        driver_features |= (1ULL << VIRTIO_F_VERSION_1);
+    }
+
+    virtio_set_features(virtio_dev, driver_features);
 
     virtio_status_driver_ok(virtio_dev);
 

+ 15 - 3
components/drivers/virtio/virtio_input.c

@@ -346,6 +346,7 @@ rt_err_t rt_virtio_input_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     char dev_name[RT_NAME_MAX];
     struct virtio_device *virtio_dev;
     struct virtio_input_device *virtio_input_dev;
+    rt_uint64_t device_features, driver_features;
 
     virtio_input_dev = rt_malloc(sizeof(struct virtio_input_device));
 
@@ -357,6 +358,7 @@ rt_err_t rt_virtio_input_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_dev = &virtio_input_dev->virtio_dev;
     virtio_dev->irq = irq;
     virtio_dev->mmio_base = mmio_base;
+    virtio_dev->version = virtio_dev->mmio_config->version;
 
     virtio_input_dev->config = (struct virtio_input_config *)virtio_dev->mmio_config->config;
     virtio_input_dev->bsct_handler = RT_NULL;
@@ -368,9 +370,19 @@ rt_err_t rt_virtio_input_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_reset_device(virtio_dev);
     virtio_status_acknowledge_driver(virtio_dev);
 
-    virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
-            (1 << VIRTIO_F_RING_EVENT_IDX) |
-            (1 << VIRTIO_F_RING_INDIRECT_DESC));
+    /* Negotiate features */
+    device_features = virtio_get_features(virtio_dev);
+    driver_features = device_features & ~(
+            (1ULL << VIRTIO_F_RING_EVENT_IDX) |
+            (1ULL << VIRTIO_F_RING_INDIRECT_DESC));
+
+    /* For modern virtio, we must support VERSION_1 */
+    if (virtio_dev->version == 2)
+    {
+        driver_features |= (1ULL << VIRTIO_F_VERSION_1);
+    }
+
+    virtio_set_features(virtio_dev, driver_features);
 
     virtio_status_driver_ok(virtio_dev);
 

+ 15 - 3
components/drivers/virtio/virtio_net.c

@@ -195,6 +195,7 @@ rt_err_t rt_virtio_net_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     char dev_name[RT_NAME_MAX];
     struct virtio_device *virtio_dev;
     struct virtio_net_device *virtio_net_dev;
+    rt_uint64_t device_features, driver_features;
 
     virtio_net_dev = rt_malloc(sizeof(struct virtio_net_device));
 
@@ -206,15 +207,26 @@ rt_err_t rt_virtio_net_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
     virtio_dev = &virtio_net_dev->virtio_dev;
     virtio_dev->irq = irq;
     virtio_dev->mmio_base = mmio_base;
+    virtio_dev->version = virtio_dev->mmio_config->version;
 
     virtio_net_dev->config = (struct virtio_net_config *)virtio_dev->mmio_config->config;
 
     virtio_reset_device(virtio_dev);
     virtio_status_acknowledge_driver(virtio_dev);
 
-    virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
-            (1 << VIRTIO_NET_F_CTRL_VQ) |
-            (1 << VIRTIO_F_RING_EVENT_IDX));
+    /* Negotiate features */
+    device_features = virtio_get_features(virtio_dev);
+    driver_features = device_features & ~(
+            (1ULL << VIRTIO_NET_F_CTRL_VQ) |
+            (1ULL << VIRTIO_F_RING_EVENT_IDX));
+
+    /* For modern virtio, we must support VERSION_1 */
+    if (virtio_dev->version == 2)
+    {
+        driver_features |= (1ULL << VIRTIO_F_VERSION_1);
+    }
+
+    virtio_set_features(virtio_dev, driver_features);
 
     virtio_status_driver_ok(virtio_dev);