Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions vm/devices/virtio/virtio/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::queue::QueueCore;
use crate::queue::QueueError;
use crate::queue::QueueParams;
use crate::queue::VirtioQueuePayload;
use crate::spec::VirtioDeviceFeatures;
use async_trait::async_trait;
use futures::FutureExt;
use futures::Stream;
Expand Down Expand Up @@ -234,7 +235,7 @@ pub struct VirtioQueue {

impl VirtioQueue {
pub fn new(
features: u64,
features: VirtioDeviceFeatures,
params: QueueParams,
mem: GuestMemory,
notify: Interrupt,
Expand Down Expand Up @@ -305,7 +306,7 @@ impl Stream for VirtioQueue {
enum VirtioQueueStateInner {
Initializing {
mem: GuestMemory,
features: u64,
features: VirtioDeviceFeatures,
params: QueueParams,
event: Event,
notify: Interrupt,
Expand Down Expand Up @@ -339,7 +340,7 @@ impl VirtioQueueWorker {
self,
name: impl Into<String>,
mem: GuestMemory,
features: u64,
features: VirtioDeviceFeatures,
queue_resources: QueueResources,
exit_event: event_listener::EventListener,
) -> TaskControl<VirtioQueueWorker, VirtioQueueState> {
Expand Down Expand Up @@ -424,7 +425,7 @@ impl AsyncRun<VirtioQueueState> for VirtioQueueWorker {
}

pub struct VirtioRunningState {
pub features: u64,
pub features: VirtioDeviceFeatures,
pub enabled_queues: Vec<bool>,
}

Expand Down Expand Up @@ -467,10 +468,10 @@ pub struct DeviceTraitsSharedMemory {
pub size: u64,
}

#[derive(Copy, Clone, Debug, Default)]
#[derive(Clone, Debug, Default)]
pub struct DeviceTraits {
pub device_id: u16,
pub device_features: u64,
pub device_features: VirtioDeviceFeatures,
pub max_queues: u16,
pub device_register_length: u32,
pub shared_memory: DeviceTraitsSharedMemory,
Expand Down Expand Up @@ -499,7 +500,7 @@ pub struct QueueResources {
}

pub struct Resources {
pub features: u64,
pub features: VirtioDeviceFeatures,
pub queues: Vec<QueueResources>,
pub shared_memory_region: Option<Arc<dyn MappedMemoryRegion>>,
pub shared_memory_size: u64,
Expand Down Expand Up @@ -541,7 +542,7 @@ impl<T: LegacyVirtioDevice> VirtioDevice for LegacyWrapper<T> {

fn enable(&mut self, resources: Resources) {
let running_state = VirtioRunningState {
features: resources.features,
features: resources.features.clone(),
enabled_queues: resources
.queues
.iter()
Expand All @@ -566,7 +567,7 @@ impl<T: LegacyVirtioDevice> VirtioDevice for LegacyWrapper<T> {
Some(worker.into_running_task(
"virtio-queue".to_string(),
self.mem.clone(),
resources.features,
resources.features.clone(),
queue_resources,
self.exit_event.listen(),
))
Expand Down
11 changes: 7 additions & 4 deletions vm/devices/virtio/virtio/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! Core virtio queue implementation, without any notification mechanisms, async
//! support, or other transport-specific details.

use crate::spec::VirtioDeviceFeatures;
use crate::spec::queue as spec;
use crate::spec::u16_le;
use guestmem::GuestMemory;
Expand Down Expand Up @@ -41,9 +42,11 @@ pub struct QueueParams {
}

impl QueueCore {
pub fn new(features: u64, mem: GuestMemory, params: QueueParams) -> Result<Self, QueueError> {
let use_ring_event_index = (features & crate::spec::VIRTIO_F_RING_EVENT_IDX as u64) != 0;

pub fn new(
features: VirtioDeviceFeatures,
mem: GuestMemory,
params: QueueParams,
) -> Result<Self, QueueError> {
let queue_avail = mem
.subrange(
params.avail_addr,
Expand Down Expand Up @@ -77,7 +80,7 @@ impl QueueCore {
queue_desc,
queue_avail,
queue_used,
use_ring_event_index,
use_ring_event_index: features.bank0().ring_event_idx(),
mem,
})
}
Expand Down
122 changes: 109 additions & 13 deletions vm/devices/virtio/virtio/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@

//! Constants defined by the virtio spec

use bitfield_struct::bitfield;
use inspect::Inspect;
use zerocopy::FromBytes;
use zerocopy::Immutable;
use zerocopy::IntoBytes;
use zerocopy::KnownLayout;

pub use packed_nums::*;

#[expect(non_camel_case_types)]
Expand All @@ -12,19 +19,108 @@ mod packed_nums {
pub type u64_le = zerocopy::U64<zerocopy::LittleEndian>;
}

// Device features - first bank
pub const VIRTIO_F_RING_INDIRECT_DESC: u32 = 0x10000000;
pub const VIRTIO_F_RING_EVENT_IDX: u32 = 0x20000000;
// Device features - second bank
pub const VIRTIO_F_VERSION_1: u32 = 1;

// Device status
pub const VIRTIO_ACKNOWLEDGE: u32 = 1;
pub const VIRTIO_DRIVER: u32 = 2;
pub const VIRTIO_DRIVER_OK: u32 = 4;
pub const VIRTIO_FEATURES_OK: u32 = 8;
// const VIRTIO_DEVICE_NEEDS_RESET: u32 = 0x40;
pub const VIRTIO_FAILED: u32 = 0x80;
#[bitfield(u32)]
#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
pub struct VirtioDeviceFeaturesBank0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these bank types be Copy too?

#[bits(24)]
pub device_specific: u32,
#[bits(4)]
_reserved1: u8,
pub ring_indirect_desc: bool, // VIRTIO_F_INDIRECT_DESC
pub ring_event_idx: bool, // VIRTIO_F_EVENT_IDX
#[bits(2)]
_reserved2: u8,
}

#[bitfield(u32)]
#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
pub struct VirtioDeviceFeaturesBank1 {
pub version_1: bool, // VIRTIO_F_VERSION_1
pub access_platform: bool, // VIRTIO_F_ACCESS_PLATFORM
pub ring_packed: bool, // VIRTIO_F_RING_PACKED
pub in_order: bool, // VIRTIO_F_IN_ORDER
pub order_platform: bool, // VIRTIO_F_ORDER_PLATFORM
pub sriov: bool, // VIRTIO_F_SR_IOV
pub notification_data: bool, // VIRTIO_F_NOTIFICATION_DATA
pub notif_config_data: bool, // VIRTIO_F_NOTIF_CONFIG_DATA
pub ring_reset: bool, // VIRTIO_F_RING_RESET
pub admin_vq: bool, // VIRTIO_F_ADMIN_VQ
pub device_specific_bit_42: bool,
pub suspend: bool, // VIRTIO_F_SUSPEND
#[bits(7)]
_reserved: u8,
#[bits(13)]
pub device_specific: u16,
}

#[derive(Debug, Clone)]
pub struct VirtioDeviceFeatures(Vec<u32>);
impl VirtioDeviceFeatures {
pub fn new() -> Self {
Self(Vec::with_capacity(2))
}

pub fn len(&self) -> usize {
self.0.len()
}

pub fn set_bank(&mut self, index: usize, val: u32) {
if self.0.len() <= index {
self.0.resize(index + 1, 0);
}
self.0[index] = val;
}
Comment on lines +67 to +72
Copy link

Copilot AI Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The resize operation could be inefficient for large indices. Consider adding bounds checking or documentation about expected index ranges to prevent excessive memory allocation.

Copilot uses AI. Check for mistakes.

pub fn with_bank(mut self, index: usize, val: u32) -> Self {
self.set_bank(index, val);
self
}

pub fn with_bank0(self, bank0: VirtioDeviceFeaturesBank0) -> Self {
self.with_bank(0, bank0.into_bits())
}

pub fn with_bank1(self, bank1: VirtioDeviceFeaturesBank1) -> Self {
self.with_bank(1, bank1.into_bits())
}

pub fn bank(&self, index: usize) -> u32 {
self.0.get(index).map_or(0, |x| *x)
}

pub fn bank0(&self) -> VirtioDeviceFeaturesBank0 {
VirtioDeviceFeaturesBank0::from_bits(self.bank(0))
}

pub fn bank1(&self) -> VirtioDeviceFeaturesBank1 {
VirtioDeviceFeaturesBank1::from_bits(self.bank(1))
}
}

impl Default for VirtioDeviceFeatures {
fn default() -> Self {
Self::new()
}
}

#[bitfield(u8)]
#[derive(IntoBytes, Immutable, KnownLayout, FromBytes, Inspect)]
pub struct VirtioDeviceStatus {
pub acknowledge: bool,
pub driver: bool,
pub driver_ok: bool,
pub features_ok: bool,
pub suspend: bool,
_reserved1: bool,
pub device_needs_reset: bool,
pub failed: bool,
}

impl VirtioDeviceStatus {
pub fn as_u32(&self) -> u32 {
self.into_bits() as u32
}
}

// ACPI interrupt status flags
pub const VIRTIO_MMIO_INTERRUPT_STATUS_USED_BUFFER: u32 = 1;
Expand Down
Loading