From 0eb62b67e8d68d0cb87b0557744d5bea0dd1cc62 Mon Sep 17 00:00:00 2001 From: Asher Kariv Date: Mon, 30 Mar 2026 11:39:50 -0700 Subject: [PATCH 1/5] Add a missing reply in FUSE_DESTROY handler. The missing reply may have caused the crash in the guest, during unmount of a virtiofs mount, since the guest is waiting indefinitely for the fuse_destroy request reply, such as demonstrated by the captured stack below. Process: 1583 (parent: 1569) cmd: stat: 1583 (weston) Z 1569 1 1 58624 1 4228364 5042 0 0 0 12 2 0 0 20 0 2 0 16448 0 0 18446744073709551615 0 0 0 0 0 0 84480 0 2 0 0 0 17 6 0 0 0 0 0 0 0 0 0 0 0 0 9 tid: 1583 - weston tid: 1585 - weston [<0>] request_wait_answer+0x138/0x220 [<0>] fuse_simple_request+0x189/0x2d0 [<0>] fuse_send_destroy+0x66/0x70 [<0>] fuse_conn_destroy+0xae/0xc0 [<0>] virtio_kill_sb+0xc2/0x180 [<0>] deactivate_locked_super+0x39/0xb0 [<0>] deactivate_super+0x44/0x50 [<0>] cleanup_mnt+0xc3/0x160 [<0>] __cleanup_mnt+0x16/0x20 [<0>] task_work_run+0x65/0xa0 [<0>] do_exit+0x37a/0xb30 [<0>] do_group_exit+0x39/0x90 [<0>] get_signal+0xa9a/0xaa0 --- vm/devices/support/fs/fuse/src/session.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/vm/devices/support/fs/fuse/src/session.rs b/vm/devices/support/fs/fuse/src/session.rs index d3059bb65c..a78f25207b 100644 --- a/vm/devices/support/fs/fuse/src/session.rs +++ b/vm/devices/support/fs/fuse/src/session.rs @@ -321,6 +321,7 @@ impl Session { mapper.clear(); } self.destroy(); + sender.send_empty(request.unique())?; } FuseOperation::Ioctl { arg, data } => { let out = self.fs.ioctl(&request, arg, data)?; From b39337ead41e521e80df025b6689c5c43731a54f Mon Sep 17 00:00:00 2001 From: Asher Kariv Date: Mon, 27 Apr 2026 16:53:38 -0700 Subject: [PATCH 2/5] VirtioFS enhancements: Enable mmap for VirtioFS without DAX. Update the backend fuse protocol to version 7.39, and use FUSE_DIRECT_IO_ALLOW_MMAP to enable memory backed files with VirtioFS. Related work items: https://microsoft.visualstudio.com/OS/_workitems/edit/61002501 --- vm/devices/support/fs/fuse/src/protocol.rs | 30 +++++++++++++++++++--- vm/devices/support/fs/fuse/src/session.rs | 22 ++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/vm/devices/support/fs/fuse/src/protocol.rs b/vm/devices/support/fs/fuse/src/protocol.rs index a733244ab1..7ec16657c5 100644 --- a/vm/devices/support/fs/fuse/src/protocol.rs +++ b/vm/devices/support/fs/fuse/src/protocol.rs @@ -4,7 +4,7 @@ //! Defines the kernel interface of FUSE. //! //! This was derived from the official fuse.h from the Linux kernel sources. It represents -//! FUSE protocol version 7.31. +//! FUSE protocol version 7.39. //! //! For more details, see fuse.h. #![expect(unused_parens)] @@ -39,7 +39,7 @@ use zerocopy::KnownLayout; pub const FUSE_KERNEL_VERSION: u32 = 7; /** Minor version number of this interface */ -pub const FUSE_KERNEL_MINOR_VERSION: u32 = 31; +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 39; /** The node ID of the root inode */ pub const FUSE_ROOT_ID: u64 = 1; @@ -187,6 +187,11 @@ pub const FOPEN_STREAM: u32 = (1 << 4); * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request * FUSE_MAP_ALIGNMENT: map_alignment field is valid + * FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts + * FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc + * FUSE_SETXATTR_EXT: server supports extended struct fuse_setxattr_in + * FUSE_INIT_EXT: extended fuse_init_in request (flags2 field is valid) + * FUSE_INIT_RESERVED: reserved, do not use */ pub const FUSE_ASYNC_READ: u32 = (1 << 0); pub const FUSE_POSIX_LOCKS: u32 = (1 << 1); @@ -217,6 +222,24 @@ pub const FUSE_EXPLICIT_INVAL_DATA: u32 = (1 << 25); pub const FUSE_MAP_ALIGNMENT: u32 = (1 << 26); pub const FUSE_SUBMOUNTS: u32 = (1 << 27); pub const FUSE_HANDLE_KILLPRIV_V2: u32 = (1 << 28); +pub const FUSE_SETXATTR_EXT: u32 = (1 << 29); +pub const FUSE_INIT_EXT: u32 = (1 << 30); +pub const FUSE_INIT_RESERVED: u32 = (1 << 31); + +/** + * INIT flags2 (bits 32..63 of the combined flag space, shifted down by 32) + * + * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and mknod + * FUSE_HAS_INODE_DAX: use per inode DAX + * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, symlink and mknod + * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation + * FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode + */ +pub const FUSE_SECURITY_CTX_FLAG2: u32 = (1 << 0); +pub const FUSE_HAS_INODE_DAX_FLAG2: u32 = (1 << 1); +pub const FUSE_CREATE_SUPP_GROUP_FLAG2: u32 = (1 << 2); +pub const FUSE_HAS_EXPIRE_ONLY_FLAG2: u32 = (1 << 3); +pub const FUSE_DIRECT_IO_ALLOW_MMAP_FLAG2: u32 = (1 << 4); /** * CUSE INIT request/reply flags @@ -636,7 +659,8 @@ pub struct fuse_init_out { pub time_gran: u32, pub max_pages: u16, pub map_alignment: u16, - pub unused: [u32; 8], + pub flags2: u32, + pub unused: [u32; 7], } pub const CUSE_INIT_INFO_MAX: u32 = 4096; diff --git a/vm/devices/support/fs/fuse/src/session.rs b/vm/devices/support/fs/fuse/src/session.rs index a78f25207b..eeaf7854d8 100644 --- a/vm/devices/support/fs/fuse/src/session.rs +++ b/vm/devices/support/fs/fuse/src/session.rs @@ -23,7 +23,11 @@ const DEFAULT_FLAGS: u32 = FUSE_ASYNC_READ | FUSE_HANDLE_KILLPRIV | FUSE_ASYNC_DIO | FUSE_ATOMIC_O_TRUNC - | FUSE_BIG_WRITES; + | FUSE_BIG_WRITES + | FUSE_INIT_EXT; + +// Default flags2 to negotiate when FUSE_INIT_EXT is supported. +const DEFAULT_FLAGS2: u32 = FUSE_DIRECT_IO_ALLOW_MMAP_FLAG2; const DEFAULT_MAX_PAGES: u32 = 256; @@ -478,6 +482,14 @@ impl Session { info.max_readahead = init.max_readahead; info.capable = init.flags; info.want = DEFAULT_FLAGS & init.flags; + // Negotiate flags2 when the kernel supports extended init. + // We cannot read the kernel's flags2 from the request (the struct is + // kept at its legacy 16-byte size for backward compatibility), so we + // set our desired flags2 unconditionally and rely on the kernel to + // mask out any bits it does not support. + if init.flags & FUSE_INIT_EXT != 0 { + info.want2 = DEFAULT_FLAGS2; + } info.time_gran = 1; info.max_write = DEFAULT_MAX_PAGES * PAGE_SIZE; self.fs.init(&mut info); @@ -493,6 +505,10 @@ impl Session { out.max_write = info.max_write; out.time_gran = info.time_gran; out.max_pages = ((info.max_write - 1) / PAGE_SIZE - 1).try_into().unwrap(); + // Only report flags2 when extended init was negotiated. + if info.want & FUSE_INIT_EXT != 0 { + out.flags2 = info.want2; + } sender.send_arg(request.unique(), out)?; @@ -589,6 +605,8 @@ pub struct SessionInfo { pub max_readahead: u32, capable: u32, pub want: u32, + /// Extended flags (flags2) to negotiate when FUSE_INIT_EXT is active. + pub want2: u32, pub max_background: u16, pub congestion_threshold: u16, pub max_write: u32, @@ -772,7 +790,7 @@ mod tests { const LOOKUP_CALLED: u32 = 0x4; const INIT_REPLY: &[u8] = &[ - 80, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 31, 0, 0, 0, 0, 0, 2, 0, 41, + 80, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 39, 0, 0, 0, 0, 0, 2, 0, 41, 144, 12, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 0, 0, 0, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; From 3d9d54e3eb3318b03e853cab6fb9d94936f93c47 Mon Sep 17 00:00:00 2001 From: Asher Kariv Date: Fri, 8 May 2026 11:26:06 -0700 Subject: [PATCH 3/5] Virtio: allow specifying device features at creation. Add the ability to customize VirtIO device features based on host configuration. For example, this is required for environments utilizing SWIOTLB, where the device must advertise VIRTIO_F_ACCESS_PLATFORM to ensure proper memory access through the IOMMU/bounce buffers. --- vm/devices/virtio/virtio/src/tests.rs | 81 +++++++++++++++++++ .../virtio/virtio/src/transport/core.rs | 11 ++- .../virtio/virtio/src/transport/mmio.rs | 35 +++++++- vm/devices/virtio/virtio/src/transport/pci.rs | 35 +++++++- 4 files changed, 157 insertions(+), 5 deletions(-) diff --git a/vm/devices/virtio/virtio/src/tests.rs b/vm/devices/virtio/virtio/src/tests.rs index 58f38808b4..6729b3ae1b 100644 --- a/vm/devices/virtio/virtio/src/tests.rs +++ b/vm/devices/virtio/virtio/src/tests.rs @@ -1603,6 +1603,87 @@ async fn verify_chipset_config(driver: DefaultDriver) { assert_eq!(dev.read_u32(164), 0); } +#[async_test] +async fn verify_transport_features_access_platform_mmio(driver: DefaultDriver) { + let test_mem = VirtioTestMemoryAccess::new(); + let doorbell_registration: Arc = test_mem.clone(); + let mem = GuestMemory::new("test", test_mem); + let interrupt = LineInterrupt::detached(); + let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + + let mut dev = VirtioMmioDevice::new_with_transport_features( + Box::new(TestDevice::new( + &driver_source, + DeviceTraits { + device_id: VirtioDeviceType::CONSOLE, + device_features: VirtioDeviceFeatures::new() + .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(1, VIRTIO_F_RING_PACKED), + max_queues: 1, + device_register_length: 0, + ..Default::default() + }, + None, + )), + &driver_source.simple(), + mem.clone(), + interrupt, + Some(doorbell_registration), + 0, + 1, + VirtioDeviceFeatures::new() + .with_version_1(true) + .with_access_platform(true), + ) + .unwrap(); + + // device feature (bank 1) should include ACCESS_PLATFORM + dev.write_u32(20, 1); + assert_eq!( + dev.read_u32(16), + VIRTIO_F_VERSION_1 | VIRTIO_F_ACCESS_PLATFORM | VIRTIO_F_RING_PACKED + ); +} + +#[async_test] +async fn verify_transport_features_no_access_platform_mmio(driver: DefaultDriver) { + let test_mem = VirtioTestMemoryAccess::new(); + let doorbell_registration: Arc = test_mem.clone(); + let mem = GuestMemory::new("test", test_mem); + let interrupt = LineInterrupt::detached(); + let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + + let mut dev = VirtioMmioDevice::new( + Box::new(TestDevice::new( + &driver_source, + DeviceTraits { + device_id: VirtioDeviceType::CONSOLE, + device_features: VirtioDeviceFeatures::new() + .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(1, VIRTIO_F_RING_PACKED), + max_queues: 1, + device_register_length: 0, + ..Default::default() + }, + None, + )), + &driver_source.simple(), + mem.clone(), + interrupt, + Some(doorbell_registration), + 0, + 1, + ) + .unwrap(); + + // device feature (bank 1) should NOT include ACCESS_PLATFORM + dev.write_u32(20, 1); + assert_eq!( + dev.read_u32(16), + VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED + ); +} + #[async_test] async fn verify_pci_config(driver: DefaultDriver) { let mut pci_test_device = diff --git a/vm/devices/virtio/virtio/src/transport/core.rs b/vm/devices/virtio/virtio/src/transport/core.rs index a208965d1e..8842db54a9 100644 --- a/vm/devices/virtio/virtio/src/transport/core.rs +++ b/vm/devices/virtio/virtio/src/transport/core.rs @@ -96,12 +96,15 @@ pub(crate) struct VirtioTransportCore { } impl VirtioTransportCore { - /// Create a new transport core, spawning the device task. - pub fn new( + /// Create a new transport core, merging caller-supplied transport + /// features (e.g. `VIRTIO_F_VERSION_1`, `VIRTIO_F_ACCESS_PLATFORM`) + /// with the device-specific features. + pub(crate) fn new_with_transport_features( device: Box, driver: &impl Spawn, guest_memory: GuestMemory, doorbell_registration: Option>, + transport_features: VirtioDeviceFeatures, ) -> std::io::Result { let traits = device.traits(); let queues: Vec = (0..traits.max_queues) @@ -121,7 +124,9 @@ impl VirtioTransportCore { }) .collect::>>()?; - let device_feature = traits.device_features.with_version_1(true); + let device_feature = VirtioDeviceFeatures::from_bits( + traits.device_features.into_bits() | transport_features.into_bits(), + ); let supports_save_restore = device.supports_save_restore(); let (sender, receiver) = mesh::channel(); diff --git a/vm/devices/virtio/virtio/src/transport/mmio.rs b/vm/devices/virtio/virtio/src/transport/mmio.rs index fb05ecec66..1c033631c0 100644 --- a/vm/devices/virtio/virtio/src/transport/mmio.rs +++ b/vm/devices/virtio/virtio/src/transport/mmio.rs @@ -10,6 +10,7 @@ use crate::DynVirtioDevice; use crate::MAX_QUEUE_SIZE; use crate::spec::VIRTIO_MMIO_INTERRUPT_STATUS_CONFIG_CHANGE; use crate::spec::VIRTIO_MMIO_INTERRUPT_STATUS_USED_BUFFER; +use crate::spec::VirtioDeviceFeatures; use crate::spec::mmio::VirtioMmioRegister; use chipset_device::ChipsetDevice; use chipset_device::io::IoResult; @@ -105,6 +106,8 @@ impl fmt::Debug for VirtioMmioDevice { } impl VirtioMmioDevice { + /// Create a new MMIO transport with default transport features + /// (`VIRTIO_F_VERSION_1`). pub fn new( device: Box, driver: &impl Spawn, @@ -113,6 +116,30 @@ impl VirtioMmioDevice { doorbell_registration: Option>, mmio_gpa: u64, mmio_len: u64, + ) -> std::io::Result { + Self::new_with_transport_features( + device, + driver, + guest_memory, + interrupt, + doorbell_registration, + mmio_gpa, + mmio_len, + VirtioDeviceFeatures::new().with_version_1(true), + ) + } + + /// Create a new MMIO transport, merging caller-supplied transport + /// features with the device-specific features. + pub fn new_with_transport_features( + device: Box, + driver: &impl Spawn, + guest_memory: GuestMemory, + interrupt: LineInterrupt, + doorbell_registration: Option>, + mmio_gpa: u64, + mmio_len: u64, + transport_features: VirtioDeviceFeatures, ) -> std::io::Result { let traits = device.traits(); let interrupt_state = Arc::new(Mutex::new(InterruptState { @@ -120,7 +147,13 @@ impl VirtioMmioDevice { status: 0, })); - let core = VirtioTransportCore::new(device, driver, guest_memory, doorbell_registration)?; + let core = VirtioTransportCore::new_with_transport_features( + device, + driver, + guest_memory, + doorbell_registration, + transport_features, + )?; Ok(Self { core, diff --git a/vm/devices/virtio/virtio/src/transport/pci.rs b/vm/devices/virtio/virtio/src/transport/pci.rs index e8f58883b7..36302edc50 100644 --- a/vm/devices/virtio/virtio/src/transport/pci.rs +++ b/vm/devices/virtio/virtio/src/transport/pci.rs @@ -11,6 +11,7 @@ use super::task::defer_config_read; use super::task::defer_config_write; use crate::DynVirtioDevice; use crate::MAX_QUEUE_SIZE; +use crate::spec::VirtioDeviceFeatures; use crate::spec::pci::VIRTIO_PCI_COMMON_CFG_SIZE; use crate::spec::pci::VIRTIO_PCI_DEVICE_ID_BASE; use crate::spec::pci::VIRTIO_VENDOR_ID; @@ -145,7 +146,32 @@ pub struct VirtioPciDevice { } impl VirtioPciDevice { + /// Create a new PCI transport with default transport features + /// (`VIRTIO_F_VERSION_1`). pub fn new( + device: Box, + driver: &impl Spawn, + guest_memory: GuestMemory, + interrupt_model: PciInterruptModel<'_>, + doorbell_registration: Option>, + mmio_registration: &mut dyn RegisterMmioIntercept, + shared_mem_mapper: Option<&dyn MemoryMapper>, + ) -> io::Result { + Self::new_with_transport_features( + device, + driver, + guest_memory, + interrupt_model, + doorbell_registration, + mmio_registration, + shared_mem_mapper, + VirtioDeviceFeatures::new().with_version_1(true), + ) + } + + /// Create a new PCI transport, merging caller-supplied transport + /// features with the device-specific features. + pub fn new_with_transport_features( mut device: Box, driver: &impl Spawn, guest_memory: GuestMemory, @@ -153,6 +179,7 @@ impl VirtioPciDevice { doorbell_registration: Option>, mmio_registration: &mut dyn RegisterMmioIntercept, shared_mem_mapper: Option<&dyn MemoryMapper>, + transport_features: VirtioDeviceFeatures, ) -> io::Result { let traits = device.traits(); @@ -266,7 +293,13 @@ impl VirtioPciDevice { } }; - let core = VirtioTransportCore::new(device, driver, guest_memory, doorbell_registration)?; + let core = VirtioTransportCore::new_with_transport_features( + device, + driver, + guest_memory, + doorbell_registration, + transport_features, + )?; Ok(VirtioPciDevice { core, From dea4584009575d439fcd950e52d047926ed91b25 Mon Sep 17 00:00:00 2001 From: Asher Kariv Date: Fri, 8 May 2026 12:45:15 -0700 Subject: [PATCH 4/5] PR comments: enforce virtio device creation with at least mandatory feature VIRTIO_F_VERSION_1, and fix tests. --- vm/devices/virtio/virtio/src/tests.rs | 127 ++++++++++++++---- .../virtio/virtio/src/transport/core.rs | 4 + 2 files changed, 108 insertions(+), 23 deletions(-) diff --git a/vm/devices/virtio/virtio/src/tests.rs b/vm/devices/virtio/virtio/src/tests.rs index 6729b3ae1b..bb0a5f0156 100644 --- a/vm/devices/virtio/virtio/src/tests.rs +++ b/vm/devices/virtio/virtio/src/tests.rs @@ -61,10 +61,12 @@ use vmcore::vm_task::SingleDriverBackend; use vmcore::vm_task::VmTaskDriverSource; // Device features - first bank +const TEST_DEVICE_FEATURE_BIT: u32 = 2; // arbitrary device-specific feature for testing 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_ACCESS_PLATFORM: u32 = 2; const VIRTIO_F_RING_PACKED: u32 = 4; // Device status @@ -1399,7 +1401,7 @@ impl VirtioPciTestDevice { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: num_queues, device_register_length: 12, @@ -1452,7 +1454,7 @@ async fn verify_chipset_config(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 0, @@ -1479,7 +1481,7 @@ async fn verify_chipset_config(driver: DefaultDriver) { // device feature (bank 0) assert_eq!( dev.read_u32(16), - VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | 2 + VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT ); // device feature bank index assert_eq!(dev.read_u32(20), 0); @@ -1497,7 +1499,7 @@ async fn verify_chipset_config(driver: DefaultDriver) { dev.write_u32(32, 0xffffffff); assert_eq!( dev.read_u32(32), - VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | 2 + VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT ); // driver feature bank index assert_eq!(dev.read_u32(36), 0); @@ -1617,7 +1619,7 @@ async fn verify_transport_features_access_platform_mmio(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 0, @@ -1659,7 +1661,7 @@ async fn verify_transport_features_no_access_platform_mmio(driver: DefaultDriver DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 0, @@ -1684,6 +1686,85 @@ async fn verify_transport_features_no_access_platform_mmio(driver: DefaultDriver ); } +#[async_test] +async fn verify_transport_features_access_platform_pci(driver: DefaultDriver) { + let test_mem = VirtioTestMemoryAccess::new(); + let doorbell_registration: Arc = test_mem.clone(); + let msi_conn = MsiConnection::new(); + let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + + let mut dev = VirtioPciDevice::new_with_transport_features( + Box::new(TestDevice::new( + &driver_source, + DeviceTraits { + device_id: VirtioDeviceType::CONSOLE, + device_features: VirtioDeviceFeatures::new() + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(1, VIRTIO_F_RING_PACKED), + max_queues: 1, + device_register_length: 12, + ..Default::default() + }, + None, + )), + &driver_source.simple(), + GuestMemory::new("test", test_mem), + PciInterruptModel::Msix(msi_conn.target()), + Some(doorbell_registration), + &mut ExternallyManagedMmioIntercepts, + None, + VirtioDeviceFeatures::new() + .with_version_1(true) + .with_access_platform(true), + ) + .unwrap(); + + // device feature (bank 1) should include ACCESS_PLATFORM + dev.write_u32(0, 1); // select bank 1 + assert_eq!( + dev.read_u32(4), + VIRTIO_F_VERSION_1 | VIRTIO_F_ACCESS_PLATFORM | VIRTIO_F_RING_PACKED + ); +} + +#[async_test] +async fn verify_transport_features_no_access_platform_pci(driver: DefaultDriver) { + let test_mem = VirtioTestMemoryAccess::new(); + let doorbell_registration: Arc = test_mem.clone(); + let msi_conn = MsiConnection::new(); + let driver_source = VmTaskDriverSource::new(SingleDriverBackend::new(driver)); + + let mut dev = VirtioPciDevice::new( + Box::new(TestDevice::new( + &driver_source, + DeviceTraits { + device_id: VirtioDeviceType::CONSOLE, + device_features: VirtioDeviceFeatures::new() + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank(1, VIRTIO_F_RING_PACKED), + max_queues: 1, + device_register_length: 12, + ..Default::default() + }, + None, + )), + &driver_source.simple(), + GuestMemory::new("test", test_mem), + PciInterruptModel::Msix(msi_conn.target()), + Some(doorbell_registration), + &mut ExternallyManagedMmioIntercepts, + None, + ) + .unwrap(); + + // device feature (bank 1) should NOT include ACCESS_PLATFORM + dev.write_u32(0, 1); // select bank 1 + assert_eq!( + dev.read_u32(4), + VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED + ); +} + #[async_test] async fn verify_pci_config(driver: DefaultDriver) { let mut pci_test_device = @@ -1867,7 +1948,7 @@ async fn verify_pci_registers(driver: DefaultDriver) { // device feature (bank 0) assert_eq!( pci_test_device.read_u32(bar_address1 + 4), - VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | 2 + VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT ); // device feature (bank 1) pci_test_device.write_u32(bar_address1, 1); @@ -1889,7 +1970,7 @@ async fn verify_pci_registers(driver: DefaultDriver) { pci_test_device.write_u32(bar_address1 + 12, 0xffffffff); assert_eq!( pci_test_device.read_u32(bar_address1 + 12), - VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | 2 + VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT ); // driver feature (bank 1) pci_test_device.write_u32(bar_address1 + 8, 1); @@ -2696,7 +2777,7 @@ async fn verify_device_split_queue_simple(driver: DefaultDriver) { let test_mem = VirtioTestMemoryAccess::new(); let guest = VirtioTestGuest::new_split(&driver, &test_mem, 1, 2, true); let features = VirtioDeviceFeatures::new() - .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT) .with_bank(1, VIRTIO_F_VERSION_1); verify_device_queue_simple_inner(test_mem, guest, features).await; } @@ -2705,7 +2786,7 @@ async fn verify_device_packed_queue_simple(driver: DefaultDriver) { let test_mem = VirtioTestMemoryAccess::new(); let guest = VirtioTestGuest::new_packed(&driver, &test_mem, 1, 2, true); let features = VirtioDeviceFeatures::new() - .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT) .with_bank(1, VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED); verify_device_queue_simple_inner(test_mem, guest, features).await; } @@ -2797,7 +2878,7 @@ async fn verify_device_split_multi_queue(driver: DefaultDriver) { let test_mem = VirtioTestMemoryAccess::new(); let guest = VirtioTestGuest::new_split(&driver, &test_mem, num_queues, 2, true); let features = VirtioDeviceFeatures::new() - .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT) .with_bank(1, VIRTIO_F_VERSION_1); verify_device_multi_queue_inner(test_mem, guest, num_queues, features).await; } @@ -2807,7 +2888,7 @@ async fn verify_device_packed_multi_queue(driver: DefaultDriver) { let test_mem = VirtioTestMemoryAccess::new(); let guest = VirtioTestGuest::new_packed(&driver, &test_mem, num_queues, 2, true); let features = VirtioDeviceFeatures::new() - .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT) .with_bank(1, VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED); verify_device_multi_queue_inner(test_mem, guest, num_queues, features).await; } @@ -2877,7 +2958,7 @@ async fn verify_device_split_multi_queue_pci(driver: DefaultDriver) { let test_mem = VirtioTestMemoryAccess::new(); let guest = VirtioTestGuest::new_split(&driver, &test_mem, num_queues, 2, true); let features = VirtioDeviceFeatures::new() - .with_bank(0, VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(0, VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT) .with_bank(1, VIRTIO_F_VERSION_1); verify_device_multi_queue_pci_inner(test_mem, guest, num_queues, features).await; } @@ -2887,7 +2968,7 @@ async fn verify_device_packed_multi_queue_pci(driver: DefaultDriver) { let test_mem = VirtioTestMemoryAccess::new(); let guest = VirtioTestGuest::new_packed(&driver, &test_mem, num_queues, 2, true); let features = VirtioDeviceFeatures::new() - .with_bank(0, VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | 2) + .with_bank(0, VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT) .with_bank(1, VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED); verify_device_multi_queue_pci_inner(test_mem, guest, num_queues, features).await; } @@ -2903,7 +2984,7 @@ async fn verify_enable_failure_mmio_does_not_set_driver_ok(_driver: DefaultDrive traits: DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues: 1, device_register_length: 0, ..Default::default() @@ -2956,7 +3037,7 @@ async fn verify_enable_failure_pci_does_not_set_driver_ok(_driver: DefaultDriver traits: DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues: 1, device_register_length: 12, ..Default::default() @@ -3643,7 +3724,7 @@ impl PartialFailTestDevice { traits: DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues, device_register_length: 0, ..Default::default() @@ -4034,7 +4115,7 @@ async fn pci_intx_line_deasserted_on_reset(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues: 1, device_register_length: 12, ..Default::default() @@ -4215,7 +4296,7 @@ async fn mmio_save_restore_round_trip(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues: 1, device_register_length: 0, ..Default::default() @@ -4251,7 +4332,7 @@ async fn mmio_save_restore_round_trip(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues: 1, device_register_length: 0, ..Default::default() @@ -4292,7 +4373,7 @@ async fn pci_save_restore_incompatible_features(driver: DefaultDriver) { let mut dev = VirtioPciTestDevice::new(&driver, 1, &test_mem, None); // Negotiate features including the device-specific bit (bank 0, bit 1). let mut driver_features = guest.queue_features(); - driver_features.set_bank(0, driver_features.bank(0) | 2); + driver_features.set_bank(0, driver_features.bank(0) | TEST_DEVICE_FEATURE_BIT); guest.setup_pci_device(&mut dev, driver_features).await; dev.pci_device.stop().await; @@ -4465,7 +4546,7 @@ async fn mmio_restore_reinstalls_doorbells(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues: 1, device_register_length: 0, ..Default::default() @@ -4503,7 +4584,7 @@ async fn mmio_restore_reinstalls_doorbells(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, 2 | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), max_queues: 1, device_register_length: 0, ..Default::default() diff --git a/vm/devices/virtio/virtio/src/transport/core.rs b/vm/devices/virtio/virtio/src/transport/core.rs index 8842db54a9..894de957bf 100644 --- a/vm/devices/virtio/virtio/src/transport/core.rs +++ b/vm/devices/virtio/virtio/src/transport/core.rs @@ -124,6 +124,10 @@ impl VirtioTransportCore { }) .collect::>>()?; + // Always enforce VERSION_1 — both PCI-modern and MMIO v2 transports + // require it, and omitting it would be a caller bug. + let transport_features = transport_features.with_version_1(true); + let device_feature = VirtioDeviceFeatures::from_bits( traits.device_features.into_bits() | transport_features.into_bits(), ); From 1e8603c34110bff578b1346a5aaf58a96f10dd4b Mon Sep 17 00:00:00 2001 From: Asher Kariv Date: Fri, 8 May 2026 13:07:40 -0700 Subject: [PATCH 5/5] Run xtask fmt --- vm/devices/virtio/virtio/src/tests.rs | 105 ++++++++++++++++++-------- 1 file changed, 74 insertions(+), 31 deletions(-) diff --git a/vm/devices/virtio/virtio/src/tests.rs b/vm/devices/virtio/virtio/src/tests.rs index bb0a5f0156..aa9d5aecf0 100644 --- a/vm/devices/virtio/virtio/src/tests.rs +++ b/vm/devices/virtio/virtio/src/tests.rs @@ -1401,7 +1401,12 @@ impl VirtioPciTestDevice { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank( + 0, + TEST_DEVICE_FEATURE_BIT + | VIRTIO_F_RING_INDIRECT_DESC + | VIRTIO_F_RING_EVENT_IDX, + ) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: num_queues, device_register_length: 12, @@ -1454,7 +1459,12 @@ async fn verify_chipset_config(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank( + 0, + TEST_DEVICE_FEATURE_BIT + | VIRTIO_F_RING_INDIRECT_DESC + | VIRTIO_F_RING_EVENT_IDX, + ) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 0, @@ -1619,7 +1629,12 @@ async fn verify_transport_features_access_platform_mmio(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank( + 0, + TEST_DEVICE_FEATURE_BIT + | VIRTIO_F_RING_INDIRECT_DESC + | VIRTIO_F_RING_EVENT_IDX, + ) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 0, @@ -1661,7 +1676,12 @@ async fn verify_transport_features_no_access_platform_mmio(driver: DefaultDriver DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank( + 0, + TEST_DEVICE_FEATURE_BIT + | VIRTIO_F_RING_INDIRECT_DESC + | VIRTIO_F_RING_EVENT_IDX, + ) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 0, @@ -1680,10 +1700,7 @@ async fn verify_transport_features_no_access_platform_mmio(driver: DefaultDriver // device feature (bank 1) should NOT include ACCESS_PLATFORM dev.write_u32(20, 1); - assert_eq!( - dev.read_u32(16), - VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED - ); + assert_eq!(dev.read_u32(16), VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED); } #[async_test] @@ -1699,7 +1716,12 @@ async fn verify_transport_features_access_platform_pci(driver: DefaultDriver) { DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank( + 0, + TEST_DEVICE_FEATURE_BIT + | VIRTIO_F_RING_INDIRECT_DESC + | VIRTIO_F_RING_EVENT_IDX, + ) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 12, @@ -1740,7 +1762,12 @@ async fn verify_transport_features_no_access_platform_pci(driver: DefaultDriver) DeviceTraits { device_id: VirtioDeviceType::CONSOLE, device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX) + .with_bank( + 0, + TEST_DEVICE_FEATURE_BIT + | VIRTIO_F_RING_INDIRECT_DESC + | VIRTIO_F_RING_EVENT_IDX, + ) .with_bank(1, VIRTIO_F_RING_PACKED), max_queues: 1, device_register_length: 12, @@ -1759,10 +1786,7 @@ async fn verify_transport_features_no_access_platform_pci(driver: DefaultDriver) // device feature (bank 1) should NOT include ACCESS_PLATFORM dev.write_u32(0, 1); // select bank 1 - assert_eq!( - dev.read_u32(4), - VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED - ); + assert_eq!(dev.read_u32(4), VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED); } #[async_test] @@ -2968,7 +2992,10 @@ async fn verify_device_packed_multi_queue_pci(driver: DefaultDriver) { let test_mem = VirtioTestMemoryAccess::new(); let guest = VirtioTestGuest::new_packed(&driver, &test_mem, num_queues, 2, true); let features = VirtioDeviceFeatures::new() - .with_bank(0, VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT) + .with_bank( + 0, + VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX | TEST_DEVICE_FEATURE_BIT, + ) .with_bank(1, VIRTIO_F_VERSION_1 | VIRTIO_F_RING_PACKED); verify_device_multi_queue_pci_inner(test_mem, guest, num_queues, features).await; } @@ -2983,8 +3010,10 @@ async fn verify_enable_failure_mmio_does_not_set_driver_ok(_driver: DefaultDrive Box::new(FailingTestDevice { traits: DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues: 1, device_register_length: 0, ..Default::default() @@ -3036,8 +3065,10 @@ async fn verify_enable_failure_pci_does_not_set_driver_ok(_driver: DefaultDriver Box::new(FailingTestDevice { traits: DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues: 1, device_register_length: 12, ..Default::default() @@ -3723,8 +3754,10 @@ impl PartialFailTestDevice { Self { traits: DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues, device_register_length: 0, ..Default::default() @@ -4114,8 +4147,10 @@ async fn pci_intx_line_deasserted_on_reset(driver: DefaultDriver) { &driver_source, DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues: 1, device_register_length: 12, ..Default::default() @@ -4295,8 +4330,10 @@ async fn mmio_save_restore_round_trip(driver: DefaultDriver) { &driver_source, DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues: 1, device_register_length: 0, ..Default::default() @@ -4331,8 +4368,10 @@ async fn mmio_save_restore_round_trip(driver: DefaultDriver) { &driver_source, DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues: 1, device_register_length: 0, ..Default::default() @@ -4545,8 +4584,10 @@ async fn mmio_restore_reinstalls_doorbells(driver: DefaultDriver) { &driver_source, DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues: 1, device_register_length: 0, ..Default::default() @@ -4583,8 +4624,10 @@ async fn mmio_restore_reinstalls_doorbells(driver: DefaultDriver) { &driver_source, DeviceTraits { device_id: VirtioDeviceType::CONSOLE, - device_features: VirtioDeviceFeatures::new() - .with_bank(0, TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX), + device_features: VirtioDeviceFeatures::new().with_bank( + 0, + TEST_DEVICE_FEATURE_BIT | VIRTIO_F_RING_INDIRECT_DESC | VIRTIO_F_RING_EVENT_IDX, + ), max_queues: 1, device_register_length: 0, ..Default::default()