[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>
This commit is contained in:
copilot-swe-agent[bot]
2025-12-04 15:41:45 +00:00
parent e12ae42809
commit 81d0c2a75d
10 changed files with 239 additions and 33 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -5,10 +5,20 @@ menuconfig RT_USING_VIRTIO
if RT_USING_VIRTIO
choice RT_USING_VIRTIO_VERSION
prompt "VirtIO Version"
default RT_USING_VIRTIO10
default RT_USING_VIRTIO_LEGACY
config RT_USING_VIRTIO10
bool "VirtIO v1.0"
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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);