diff --git a/vm/devices/virtio/virtio/src/common.rs b/vm/devices/virtio/virtio/src/common.rs index 2d02e2df53..ddc9cdee17 100644 --- a/vm/devices/virtio/virtio/src/common.rs +++ b/vm/devices/virtio/virtio/src/common.rs @@ -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; @@ -234,7 +235,7 @@ pub struct VirtioQueue { impl VirtioQueue { pub fn new( - features: u64, + features: VirtioDeviceFeatures, params: QueueParams, mem: GuestMemory, notify: Interrupt, @@ -305,7 +306,7 @@ impl Stream for VirtioQueue { enum VirtioQueueStateInner { Initializing { mem: GuestMemory, - features: u64, + features: VirtioDeviceFeatures, params: QueueParams, event: Event, notify: Interrupt, @@ -339,7 +340,7 @@ impl VirtioQueueWorker { self, name: impl Into, mem: GuestMemory, - features: u64, + features: VirtioDeviceFeatures, queue_resources: QueueResources, exit_event: event_listener::EventListener, ) -> TaskControl { @@ -424,7 +425,7 @@ impl AsyncRun for VirtioQueueWorker { } pub struct VirtioRunningState { - pub features: u64, + pub features: VirtioDeviceFeatures, pub enabled_queues: Vec, } @@ -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, @@ -499,7 +500,7 @@ pub struct QueueResources { } pub struct Resources { - pub features: u64, + pub features: VirtioDeviceFeatures, pub queues: Vec, pub shared_memory_region: Option>, pub shared_memory_size: u64, @@ -541,7 +542,7 @@ impl VirtioDevice for LegacyWrapper { fn enable(&mut self, resources: Resources) { let running_state = VirtioRunningState { - features: resources.features, + features: resources.features.clone(), enabled_queues: resources .queues .iter() @@ -566,7 +567,7 @@ impl VirtioDevice for LegacyWrapper { Some(worker.into_running_task( "virtio-queue".to_string(), self.mem.clone(), - resources.features, + resources.features.clone(), queue_resources, self.exit_event.listen(), )) diff --git a/vm/devices/virtio/virtio/src/queue.rs b/vm/devices/virtio/virtio/src/queue.rs index 941a8ed3dd..66a8139fdb 100644 --- a/vm/devices/virtio/virtio/src/queue.rs +++ b/vm/devices/virtio/virtio/src/queue.rs @@ -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; @@ -41,9 +42,11 @@ pub struct QueueParams { } impl QueueCore { - pub fn new(features: u64, mem: GuestMemory, params: QueueParams) -> Result { - 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 { let queue_avail = mem .subrange( params.avail_addr, @@ -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, }) } diff --git a/vm/devices/virtio/virtio/src/spec.rs b/vm/devices/virtio/virtio/src/spec.rs index cf686dea84..9f40bc9270 100644 --- a/vm/devices/virtio/virtio/src/spec.rs +++ b/vm/devices/virtio/virtio/src/spec.rs @@ -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)] @@ -12,19 +19,108 @@ mod packed_nums { pub type u64_le = zerocopy::U64; } -// 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 { + #[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); +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; + } + + 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; diff --git a/vm/devices/virtio/virtio/src/tests.rs b/vm/devices/virtio/virtio/src/tests.rs index 18cb26955f..010bb9f1c1 100644 --- a/vm/devices/virtio/virtio/src/tests.rs +++ b/vm/devices/virtio/virtio/src/tests.rs @@ -54,6 +54,21 @@ use vmcore::line_interrupt::test_helpers::TestLineInterruptTarget; use vmcore::vm_task::SingleDriverBackend; use vmcore::vm_task::VmTaskDriverSource; +// Device features - first bank +const VIRTIO_F_RING_INDIRECT_DESC: u32 = 0x10000000; +const VIRTIO_F_RING_EVENT_IDX: u32 = 0x20000000; +// Device features - second bank +const VIRTIO_F_VERSION_1: u32 = 1; +const _VIRTIO_F_RING_PACKED: u32 = 4; + +// Device status +const VIRTIO_ACKNOWLEDGE: u32 = 1; +const VIRTIO_DRIVER: u32 = 2; +const VIRTIO_DRIVER_OK: u32 = 4; +const VIRTIO_FEATURES_OK: u32 = 8; +const _VIRTIO_DEVICE_NEEDS_RESET: u32 = 0x40; +const _VIRTIO_FAILED: u32 = 0x80; + async fn must_recv_in_timeout( recv: &mut mesh::Receiver, timeout: Duration, @@ -309,12 +324,15 @@ impl VirtioTestGuest { .collect::>() } - fn queue_features(&self) -> u64 { - if self.use_ring_event_index { - VIRTIO_F_RING_EVENT_IDX as u64 - } else { - 0 - } + fn queue_features(&self) -> VirtioDeviceFeatures { + VirtioDeviceFeatures::new().with_bank( + 0, + if self.use_ring_event_index { + VIRTIO_F_RING_EVENT_IDX + } else { + 0 + }, + ) } fn queue_params(&self, i: u16) -> QueueParams { @@ -347,13 +365,17 @@ impl VirtioTestGuest { self.get_queue_base_address(index) + 0x4000 } - fn setup_chipset_device(&self, dev: &mut VirtioMmioDevice, driver_features: u64) { + fn setup_chipset_device( + &self, + dev: &mut VirtioMmioDevice, + driver_features: VirtioDeviceFeatures, + ) { dev.write_u32(112, VIRTIO_ACKNOWLEDGE); dev.write_u32(112, VIRTIO_DRIVER); dev.write_u32(36, 0); - dev.write_u32(32, driver_features as u32); + dev.write_u32(32, driver_features.bank(0)); dev.write_u32(36, 1); - dev.write_u32(32, (driver_features >> 32) as u32); + dev.write_u32(32, driver_features.bank(1)); dev.write_u32(112, VIRTIO_FEATURES_OK); for i in 0..self.num_queues { let queue_index = i; @@ -375,7 +397,11 @@ impl VirtioTestGuest { assert_eq!(dev.read_u32(0xfc), 2); } - fn setup_pci_device(&self, dev: &mut VirtioPciTestDevice, driver_features: u64) { + fn setup_pci_device( + &self, + dev: &mut VirtioPciTestDevice, + driver_features: VirtioDeviceFeatures, + ) { let bar_address1: u64 = 0x10000000000; dev.pci_device .pci_cfg_write(0x14, (bar_address1 >> 32) as u32) @@ -410,9 +436,9 @@ impl VirtioTestGuest { .mmio_write(bar_address1 + 20, &device_status.to_le_bytes()) .unwrap(); dev.write_u32(bar_address1 + 8, 0); - dev.write_u32(bar_address1 + 12, driver_features as u32); + dev.write_u32(bar_address1 + 12, driver_features.bank(0)); dev.write_u32(bar_address1 + 8, 1); - dev.write_u32(bar_address1 + 12, (driver_features >> 32) as u32); + dev.write_u32(bar_address1 + 12, driver_features.bank(1)); device_status = VIRTIO_FEATURES_OK as u8; dev.pci_device .mmio_write(bar_address1 + 20, &device_status.to_le_bytes()) @@ -755,7 +781,7 @@ impl TestDevice { impl LegacyVirtioDevice for TestDevice { fn traits(&self) -> DeviceTraits { - self.traits + self.traits.clone() } fn read_registers_u32(&self, _offset: u16) -> u32 { @@ -813,7 +839,7 @@ impl VirtioPciTestDevice { TestDevice::new( DeviceTraits { device_id: 3, - device_features: 2, + device_features: VirtioDeviceFeatures::new().with_bank(0, 2), max_queues: num_queues, device_register_length: 12, ..Default::default() @@ -864,7 +890,7 @@ async fn verify_chipset_config(driver: DefaultDriver) { TestDevice::new( DeviceTraits { device_id: 3, - device_features: 2, + device_features: VirtioDeviceFeatures::new().with_bank(0, 2), max_queues: 1, device_register_length: 0, ..Default::default() @@ -1579,7 +1605,9 @@ async fn verify_device_queue_simple(driver: DefaultDriver) { let doorbell_registration: Arc = test_mem.clone(); let mut guest = VirtioTestGuest::new(&driver, &test_mem, 1, 2, true); let mem = guest.mem(); - let features = ((VIRTIO_F_VERSION_1 as u64) << 32) | VIRTIO_F_RING_EVENT_IDX as u64 | 2; + let features = VirtioDeviceFeatures::new() + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(1, VIRTIO_F_VERSION_1); let target = TestLineInterruptTarget::new_arc(); let interrupt = LineInterrupt::new_with_target("test", target.clone(), 0); let base_addr = guest.get_queue_descriptor_backing_memory_address(0); @@ -1595,7 +1623,7 @@ async fn verify_device_queue_simple(driver: DefaultDriver) { TestDevice::new( DeviceTraits { device_id: 3, - device_features: features, + device_features: features.clone(), max_queues: 1, device_register_length: 0, ..Default::default() @@ -1644,7 +1672,9 @@ async fn verify_device_multi_queue(driver: DefaultDriver) { let doorbell_registration: Arc = test_mem.clone(); let mut guest = VirtioTestGuest::new(&driver, &test_mem, num_queues, 2, true); let mem = guest.mem(); - let features = ((VIRTIO_F_VERSION_1 as u64) << 32) | VIRTIO_F_RING_EVENT_IDX as u64 | 2; + let features = VirtioDeviceFeatures::new() + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(1, VIRTIO_F_VERSION_1); let target = TestLineInterruptTarget::new_arc(); let interrupt = LineInterrupt::new_with_target("test", target.clone(), 0); let base_addr: Vec<_> = (0..num_queues) @@ -1662,7 +1692,7 @@ async fn verify_device_multi_queue(driver: DefaultDriver) { TestDevice::new( DeviceTraits { device_id: 3, - device_features: features, + device_features: features.clone(), max_queues: num_queues + 1, device_register_length: 0, ..Default::default() @@ -1720,7 +1750,9 @@ async fn verify_device_multi_queue_pci(driver: DefaultDriver) { let num_queues = 5; let test_mem = VirtioTestMemoryAccess::new(); let mut guest = VirtioTestGuest::new(&driver, &test_mem, num_queues, 2, true); - let features = ((VIRTIO_F_VERSION_1 as u64) << 32) | VIRTIO_F_RING_EVENT_IDX as u64 | 2; + let features = VirtioDeviceFeatures::new() + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(1, VIRTIO_F_VERSION_1); let base_addr: Vec<_> = (0..num_queues) .map(|i| guest.get_queue_descriptor_backing_memory_address(i)) .collect(); diff --git a/vm/devices/virtio/virtio/src/transport/mmio.rs b/vm/devices/virtio/virtio/src/transport/mmio.rs index 6bb6e9392d..307e228288 100644 --- a/vm/devices/virtio/virtio/src/transport/mmio.rs +++ b/vm/devices/virtio/virtio/src/transport/mmio.rs @@ -35,14 +35,14 @@ pub struct VirtioMmioDevice { device: Box, device_id: u32, vendor_id: u32, - device_feature: [u32; 2], + device_feature: VirtioDeviceFeatures, device_feature_select: u32, - driver_feature: [u32; 2], + driver_feature: VirtioDeviceFeatures, driver_feature_select: u32, queue_select: u32, events: Vec, queues: Vec, - device_status: u32, + device_status: VirtioDeviceStatus, config_generation: u32, doorbells: VirtioDoorbells, interrupt_state: Arc>, @@ -100,24 +100,29 @@ impl VirtioMmioDevice { status: 0, })); + let mut device_feature = traits.device_features.clone(); + device_feature.set_bank( + 0, + device_feature + .bank0() + .with_ring_event_idx(true) + .with_ring_indirect_desc(true) + .into_bits(), + ); + device_feature.set_bank(1, device_feature.bank1().with_version_1(true).into_bits()); Self { fixed_mmio_region: ("virtio-chipset", mmio_gpa..=(mmio_gpa + mmio_len - 1)), device, device_id: traits.device_id as u32, vendor_id: 0x1af4, - device_feature: [ - traits.device_features as u32 - | VIRTIO_F_RING_EVENT_IDX - | VIRTIO_F_RING_INDIRECT_DESC, - (traits.device_features >> 32) as u32 | VIRTIO_F_VERSION_1, - ], + device_feature, device_feature_select: 0, - driver_feature: [0; 2], + driver_feature: VirtioDeviceFeatures::new(), driver_feature_select: 0, queue_select: 0, events, queues, - device_status: 0, + device_status: VirtioDeviceStatus::new(), config_generation: 0, doorbells: VirtioDoorbells::new(doorbell_registration), interrupt_state, @@ -126,7 +131,7 @@ impl VirtioMmioDevice { fn update_config_generation(&mut self) { self.config_generation = self.config_generation.wrapping_add(1); - if self.device_status & VIRTIO_DRIVER_OK != 0 { + if self.device_status.driver_ok() { self.interrupt_state .lock() .update(true, VIRTIO_MMIO_INTERRUPT_STATUS_CONFIG_CHANGE); @@ -156,11 +161,7 @@ impl VirtioMmioDevice { // Device feature bank 16 => { let feature_select = self.device_feature_select as usize; - if feature_select < self.device_feature.len() { - self.device_feature[feature_select] - } else { - 0 - } + self.device_feature.bank(feature_select) } // Device feature bank index 20 => self.device_feature_select, @@ -170,11 +171,7 @@ impl VirtioMmioDevice { // Driver feature bank 32 => { let feature_select = self.driver_feature_select as usize; - if feature_select < self.driver_feature.len() { - self.driver_feature[feature_select] - } else { - 0 - } + self.driver_feature.bank(feature_select) } // Driver feature bank index 36 => self.driver_feature_select, @@ -233,8 +230,7 @@ impl VirtioMmioDevice { // 8-byte padding // // Device status - 112 => self.device_status, - // + 112 => self.device_status.as_u32(), // 12-byte padding // // Queue descriptor table address (low part) @@ -307,16 +303,19 @@ impl VirtioMmioDevice { let offset = (address & 0xfff) as u16; assert!(offset & 3 == 0); let queue_select = self.queue_select as usize; - let queues_locked = self.device_status & VIRTIO_DRIVER_OK != 0; - let features_locked = queues_locked || self.device_status & VIRTIO_FEATURES_OK != 0; + let queues_locked = self.device_status.driver_ok(); + let features_locked = queues_locked || self.device_status.features_ok(); match offset { // Device feature bank index 20 => self.device_feature_select = val, // Driver feature bank 32 => { let bank = self.driver_feature_select as usize; - if !features_locked && bank < self.driver_feature.len() { - self.driver_feature[bank] = val & self.device_feature[bank]; + if features_locked || bank >= self.device_feature.len() { + // Update is not persisted. + } else { + self.driver_feature + .set_bank(bank, val & self.device_feature.bank(bank)); } } // Driver feature bank index @@ -355,8 +354,8 @@ impl VirtioMmioDevice { // Device status 112 => { if val == 0 { - let started = (self.device_status & VIRTIO_DRIVER_OK) != 0; - self.device_status = 0; + let started = self.device_status.driver_ok(); + self.device_status = VirtioDeviceStatus::new(); self.config_generation = 0; if started { self.doorbells.clear(); @@ -365,17 +364,23 @@ impl VirtioMmioDevice { self.interrupt_state.lock().update(false, !0); } - self.device_status |= val & (VIRTIO_ACKNOWLEDGE | VIRTIO_DRIVER | VIRTIO_FAILED); + let new_status = VirtioDeviceStatus::from(val as u8); + if new_status.acknowledge() { + self.device_status.set_acknowledge(true); + } + if new_status.driver() { + self.device_status.set_driver(true); + } + if new_status.failed() { + self.device_status.set_failed(true); + } - if self.device_status & VIRTIO_FEATURES_OK == 0 && val & VIRTIO_FEATURES_OK != 0 { - self.device_status |= VIRTIO_FEATURES_OK; + if !self.device_status.features_ok() && new_status.features_ok() { + self.device_status.set_features_ok(true); self.update_config_generation(); } - if self.device_status & VIRTIO_DRIVER_OK == 0 && val & VIRTIO_DRIVER_OK != 0 { - let features = - ((self.driver_feature[1] as u64) << 32) | self.driver_feature[0] as u64; - + if !self.device_status.driver_ok() && new_status.driver_ok() { let notification_address = (address & !0xfff) + 80; for i in 0..self.events.len() { self.doorbells.add( @@ -405,13 +410,13 @@ impl VirtioMmioDevice { .collect(); self.device.enable(Resources { - features, + features: self.driver_feature.clone(), queues, shared_memory_region: None, shared_memory_size: 0, }); - self.device_status |= VIRTIO_DRIVER_OK; + self.device_status.set_driver_ok(true); self.update_config_generation(); } } diff --git a/vm/devices/virtio/virtio/src/transport/pci.rs b/vm/devices/virtio/virtio/src/transport/pci.rs index 635f897e0d..a5ac9a163a 100644 --- a/vm/devices/virtio/virtio/src/transport/pci.rs +++ b/vm/devices/virtio/virtio/src/transport/pci.rs @@ -65,11 +65,11 @@ pub struct VirtioPciDevice { #[inspect(skip)] device: Box, #[inspect(skip)] - device_feature: [u32; 2], + device_feature: VirtioDeviceFeatures, #[inspect(hex)] device_feature_select: u32, #[inspect(skip)] - driver_feature: [u32; 2], + driver_feature: VirtioDeviceFeatures, #[inspect(hex)] driver_feature_select: u32, msix_config_vector: u16, @@ -83,7 +83,7 @@ pub struct VirtioPciDevice { #[inspect(skip)] interrupt_status: Arc>, #[inspect(hex)] - device_status: u32, + device_status: VirtioDeviceStatus, config_generation: u32, config_space: ConfigSpaceType0Emulator, @@ -210,16 +210,21 @@ impl VirtioPciDevice { } }; + let mut device_feature = traits.device_features.clone(); + device_feature.set_bank( + 0, + device_feature + .bank0() + .with_ring_event_idx(true) + .with_ring_indirect_desc(true) + .into_bits(), + ); + device_feature.set_bank(1, device_feature.bank1().with_version_1(true).into_bits()); Ok(VirtioPciDevice { device, - device_feature: [ - (traits.device_features & 0xffffffff) as u32 - | VIRTIO_F_RING_EVENT_IDX - | VIRTIO_F_RING_INDIRECT_DESC, - (traits.device_features >> 32) as u32 | VIRTIO_F_VERSION_1, - ], + device_feature, device_feature_select: 0, - driver_feature: [0; 2], + driver_feature: VirtioDeviceFeatures::new(), driver_feature_select: 0, msix_config_vector: 0, queue_select: 0, @@ -227,7 +232,7 @@ impl VirtioPciDevice { queues, msix_vectors, interrupt_status: Arc::new(Mutex::new(0)), - device_status: 0, + device_status: VirtioDeviceStatus::new(), config_generation: 0, interrupt_kind, config_space, @@ -239,7 +244,7 @@ impl VirtioPciDevice { fn update_config_generation(&mut self) { self.config_generation = self.config_generation.wrapping_add(1); - if self.device_status & VIRTIO_DRIVER_OK != 0 { + if self.device_status.driver_ok() { *self.interrupt_status.lock() |= 2; match &self.interrupt_kind { InterruptKind::Msix(msix) => { @@ -261,25 +266,19 @@ impl VirtioPciDevice { // Device feature bank 4 => { let feature_select = self.device_feature_select as usize; - if feature_select < self.device_feature.len() { - self.device_feature[feature_select] - } else { - 0 - } + self.device_feature.bank(feature_select) } // Driver feature bank index 8 => self.driver_feature_select, // Driver feature bank 12 => { let feature_select = self.driver_feature_select as usize; - if feature_select < self.driver_feature.len() { - self.driver_feature[feature_select] - } else { - 0 - } + self.driver_feature.bank(feature_select) } 16 => (self.queues.len() as u32) << 16 | self.msix_config_vector as u32, - 20 => self.queue_select << 24 | self.config_generation << 8 | self.device_status, + 20 => { + self.queue_select << 24 | self.config_generation << 8 | self.device_status.as_u32() + } 24 => { let size = if queue_select < self.queues.len() { self.queues[queue_select].size @@ -377,8 +376,8 @@ impl VirtioPciDevice { fn write_u32(&mut self, address: u64, offset: u16, val: u32) { assert!(offset & 3 == 0); - let queues_locked = self.device_status & VIRTIO_DRIVER_OK != 0; - let features_locked = queues_locked || self.device_status & VIRTIO_FEATURES_OK != 0; + let queues_locked = self.device_status.driver_ok(); + let features_locked = queues_locked || self.device_status.features_ok(); let queue_select = self.queue_select as usize; match offset { // Device feature bank index @@ -388,8 +387,11 @@ impl VirtioPciDevice { // Driver feature bank 12 => { let bank = self.driver_feature_select as usize; - if !features_locked && bank < self.driver_feature.len() { - self.driver_feature[bank] = val & self.device_feature[bank]; + if features_locked || bank >= self.device_feature.len() { + // Update is not persisted. + } else { + self.driver_feature + .set_bank(bank, val & self.device_feature.bank(bank)); } } 16 => self.msix_config_vector = val as u16, @@ -398,8 +400,8 @@ impl VirtioPciDevice { self.queue_select = val >> 16; let val = val & 0xff; if val == 0 { - let started = (self.device_status & VIRTIO_DRIVER_OK) != 0; - self.device_status = 0; + let started = self.device_status.driver_ok(); + self.device_status = VirtioDeviceStatus::new(); self.config_generation = 0; if started { self.doorbells.clear(); @@ -408,17 +410,23 @@ impl VirtioPciDevice { *self.interrupt_status.lock() = 0; } - self.device_status |= val & (VIRTIO_ACKNOWLEDGE | VIRTIO_DRIVER | VIRTIO_FAILED); + let new_status = VirtioDeviceStatus::from(val as u8); + if new_status.acknowledge() { + self.device_status.set_acknowledge(true); + } + if new_status.driver() { + self.device_status.set_driver(true); + } + if new_status.failed() { + self.device_status.set_failed(true); + } - if self.device_status & VIRTIO_FEATURES_OK == 0 && val & VIRTIO_FEATURES_OK != 0 { - self.device_status |= VIRTIO_FEATURES_OK; + if !self.device_status.features_ok() && new_status.features_ok() { + self.device_status.set_features_ok(true); self.update_config_generation(); } - if self.device_status & VIRTIO_DRIVER_OK == 0 && val & VIRTIO_DRIVER_OK != 0 { - let features = - ((self.driver_feature[1] as u64) << 32) | self.driver_feature[0] as u64; - + if !self.device_status.driver_ok() && new_status.driver_ok() { let notification_address = (address & !0xfff) + 56; for i in 0..self.events.len() { self.doorbells.add( @@ -462,13 +470,13 @@ impl VirtioPciDevice { .collect(); self.device.enable(Resources { - features, + features: self.driver_feature.clone(), queues, shared_memory_region: self.shared_memory_region.clone(), shared_memory_size: self.shared_memory_size, }); - self.device_status |= VIRTIO_DRIVER_OK; + self.device_status.set_driver_ok(true); self.update_config_generation(); } } diff --git a/vm/devices/virtio/virtio_net/src/lib.rs b/vm/devices/virtio/virtio_net/src/lib.rs index 60bcdd8b93..38c86bd9fd 100644 --- a/vm/devices/virtio/virtio_net/src/lib.rs +++ b/vm/devices/virtio/virtio_net/src/lib.rs @@ -43,6 +43,7 @@ use virtio::Resources; use virtio::VirtioDevice; use virtio::VirtioQueue; use virtio::VirtioQueueCallbackWork; +use virtio::spec::VirtioDeviceFeatures; use vmcore::vm_task::VmTaskDriver; use vmcore::vm_task::VmTaskDriverSource; use zerocopy::FromBytes; @@ -51,9 +52,9 @@ use zerocopy::IntoBytes; use zerocopy::KnownLayout; // These correspond to VIRTIO_NET_F_ flags. -#[bitfield(u64)] +#[bitfield(u32)] #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] -struct NetworkFeatures { +struct NetworkFeaturesBank0 { pub csum: bool, pub guest_csum: bool, pub ctrl_guest_offloads: bool, @@ -78,20 +79,38 @@ struct NetworkFeatures { pub guest_announce: bool, pub mq: bool, pub ctrl_mac_addr: bool, - #[bits(29)] - _reserved4: u64, - pub notf_coal: bool, - pub guest_uso4: bool, - pub guest_uso6: bool, - pub host_uso: bool, - pub hash_report: bool, - _reserved5: bool, - pub guest_hdrlen: bool, - pub rss: bool, - pub rsc_ext: bool, - pub standby: bool, - pub speed_duplex: bool, -} + #[bits(8)] + _unavailable: u8, +} +// #[bitfield(u32)] +// #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] +// struct NetworkFeaturesBank1 { +// pub device_stats: bool, // VIRTIO_NET_F_DEVICE_STATS(50) +// pub hash_tunnel: bool, +// pub vq_notf_coal: bool, +// pub notf_coal: bool, +// pub guest_uso4: bool, +// pub guest_uso6: bool, +// pub host_uso: bool, +// pub hash_report: bool, +// _reserved: bool, +// pub guest_hdrlen: bool, +// pub rss: bool, +// pub rsc_ext: bool, +// pub standby: bool, +// pub speed_duplex: bool, +// } +// #[bitfield(u32)] +// #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] +// struct NetworkFeaturesBank2 { +// pub rss_context: bool // VIRTIO_NET_F_RSS_CONTEXT(64) +// pub guest_udp_tunnel_gso: bool, +// pub guest_udp_tunnel_gso_csum: bool, +// pub host_udp_tunnel_gso: bool, +// pub host_udp_tunnel_gso_csum: bool, +// pub out_net_header: bool, +// pub ipsec: bool, +// } // These correspond to VIRTIO_NET_S_ flags. #[bitfield(u16)] @@ -209,7 +228,8 @@ impl VirtioDevice for Device { // TODO: Add network features based on endpoint capabilities (NetworkFeatures::VIRTIO_NET_F_*) DeviceTraits { device_id: 1, - device_features: NetworkFeatures::new().with_mac(true).into(), + device_features: VirtioDeviceFeatures::new() + .with_bank(0, NetworkFeaturesBank0::new().with_mac(true).into_bits()), max_queues: 2 * self.registers.max_virtqueue_pairs, device_register_length: size_of::() as u32, shared_memory: DeviceTraitsSharedMemory { id: 0, size: 0 }, @@ -258,7 +278,7 @@ impl VirtioDevice for Device { continue; } let rx_queue = VirtioQueue::new( - resources.features, + resources.features.clone(), rx_resources.params, self.memory.clone(), rx_resources.notify, @@ -282,7 +302,7 @@ impl VirtioDevice for Device { continue; } let tx_queue = VirtioQueue::new( - resources.features, + resources.features.clone(), tx_resources.params, self.memory.clone(), tx_resources.notify, diff --git a/vm/devices/virtio/virtio_p9/src/lib.rs b/vm/devices/virtio/virtio_p9/src/lib.rs index 1310f5787f..150560b19c 100644 --- a/vm/devices/virtio/virtio_p9/src/lib.rs +++ b/vm/devices/virtio/virtio_p9/src/lib.rs @@ -16,10 +16,11 @@ use virtio::LegacyVirtioDevice; use virtio::VirtioQueueCallbackWork; use virtio::VirtioQueueWorkerContext; use virtio::VirtioState; +use virtio::spec::VirtioDeviceFeatures; const VIRTIO_DEVICE_TYPE_9P_TRANSPORT: u16 = 9; -const VIRTIO_9P_F_MOUNT_TAG: u64 = 1; +const VIRTIO_9P_F_MOUNT_TAG: u32 = 1; pub struct VirtioPlan9Device { fs: Arc, @@ -56,7 +57,7 @@ impl LegacyVirtioDevice for VirtioPlan9Device { fn traits(&self) -> DeviceTraits { DeviceTraits { device_id: VIRTIO_DEVICE_TYPE_9P_TRANSPORT, - device_features: VIRTIO_9P_F_MOUNT_TAG, + device_features: VirtioDeviceFeatures::new().with_bank(0, VIRTIO_9P_F_MOUNT_TAG), max_queues: 1, device_register_length: self.tag.len() as u32, ..Default::default() diff --git a/vm/devices/virtio/virtio_pmem/src/lib.rs b/vm/devices/virtio/virtio_pmem/src/lib.rs index f3b54f0285..91a62918b6 100644 --- a/vm/devices/virtio/virtio_pmem/src/lib.rs +++ b/vm/devices/virtio/virtio_pmem/src/lib.rs @@ -21,6 +21,7 @@ use virtio::VirtioQueueCallbackWork; use virtio::VirtioQueueState; use virtio::VirtioQueueWorker; use virtio::VirtioQueueWorkerContext; +use virtio::spec::VirtioDeviceFeatures; use vmcore::vm_task::VmTaskDriver; use vmcore::vm_task::VmTaskDriverSource; @@ -69,7 +70,7 @@ impl VirtioDevice for Device { fn traits(&self) -> DeviceTraits { DeviceTraits { device_id: 27, - device_features: 0, + device_features: VirtioDeviceFeatures::new(), max_queues: 1, device_register_length: size_of::() as u32, shared_memory: DeviceTraitsSharedMemory { diff --git a/vm/devices/virtio/virtio_serial/src/lib.rs b/vm/devices/virtio/virtio_serial/src/lib.rs index 51df50755c..4ff7eec36f 100644 --- a/vm/devices/virtio/virtio_serial/src/lib.rs +++ b/vm/devices/virtio/virtio_serial/src/lib.rs @@ -18,12 +18,13 @@ use virtio::LegacyVirtioDevice; use virtio::VirtioQueueCallbackWork; use virtio::VirtioQueueWorkerContext; use virtio::VirtioState; +use virtio::spec::VirtioDeviceFeatures; const VIRTIO_DEVICE_TYPE_CONSOLE: u16 = 3; -// const VIRTIO_CONSOLE_F_SIZE: u64 = 1; -const VIRTIO_CONSOLE_F_MULTIPORT: u64 = 2; -// const VIRTIO_CONSOLE_F_EMERG_WRITE: u64 = 4; +// const VIRTIO_CONSOLE_F_SIZE: u32 = 1; +const VIRTIO_CONSOLE_F_MULTIPORT: u32 = 2; +// const VIRTIO_CONSOLE_F_EMERG_WRITE: u32 = 4; const VIRTIO_CONSOLE_DEVICE_READY: u16 = 0; const VIRTIO_CONSOLE_DEVICE_ADD: u16 = 1; @@ -517,11 +518,14 @@ impl SerialIo { impl LegacyVirtioDevice for VirtioSerialDevice { fn traits(&self) -> DeviceTraits { let queue_size = 2 + 2 * self.config.max_ports; - let features = if self.config.max_ports > 1 { - VIRTIO_CONSOLE_F_MULTIPORT - } else { - 0 - }; + let features = VirtioDeviceFeatures::new().with_bank( + 0, + if self.config.max_ports > 1 { + VIRTIO_CONSOLE_F_MULTIPORT + } else { + 0 + }, + ); DeviceTraits { device_id: VIRTIO_DEVICE_TYPE_CONSOLE, device_features: features, @@ -566,7 +570,7 @@ impl LegacyVirtioDevice for VirtioSerialDevice { match state { // if multi-port is set, start the control port thread VirtioState::Running(run_state) => { - if run_state.features & VIRTIO_CONSOLE_F_MULTIPORT != 0 { + if run_state.features.bank(0) & VIRTIO_CONSOLE_F_MULTIPORT != 0 { let enabled_queues = run_state.enabled_queues.clone(); if run_state.enabled_queues[2] && run_state.enabled_queues[3] { // on ready callback, asynchronously register the available ports with the guest. diff --git a/vm/devices/virtio/virtiofs/src/virtio.rs b/vm/devices/virtio/virtiofs/src/virtio.rs index a609d7e0cf..1954cd8cd0 100644 --- a/vm/devices/virtio/virtiofs/src/virtio.rs +++ b/vm/devices/virtio/virtiofs/src/virtio.rs @@ -21,6 +21,7 @@ use virtio::VirtioQueueCallbackWork; use virtio::VirtioQueueState; use virtio::VirtioQueueWorker; use virtio::VirtioQueueWorkerContext; +use virtio::spec::VirtioDeviceFeatures; use vmcore::vm_task::VmTaskDriver; use vmcore::vm_task::VmTaskDriverSource; use zerocopy::Immutable; @@ -97,7 +98,7 @@ impl VirtioDevice for VirtioFsDevice { fn traits(&self) -> DeviceTraits { DeviceTraits { device_id: VIRTIO_DEVICE_TYPE_FS, - device_features: 0, + device_features: VirtioDeviceFeatures::new(), max_queues: 2, device_register_length: self.config.as_bytes().len() as u32, shared_memory: DeviceTraitsSharedMemory { @@ -144,7 +145,7 @@ impl VirtioDevice for VirtioFsDevice { Some(worker.into_running_task( "virtiofs-virtio-queue".to_string(), self.memory.clone(), - resources.features, + resources.features.clone(), queue_resources, self.exit_event.listen(), ))