From 0fd12118c74b8d3d35a92e331e56df369f8abac7 Mon Sep 17 00:00:00 2001 From: changyuanl Date: Fri, 28 Aug 2026 16:17:58 -0700 Subject: [PATCH] fix(virtio): reset queue registers on device reset Assisted-by: Antigravity:Gemini-3.7-Flash Signed-off-by: Changyuan Lyu --- alioth/src/virtio/pci.rs | 6 +- alioth/src/virtio/pci_test.rs | 103 +++++++++++++++++++++++++++++++++- 2 files changed, 107 insertions(+), 2 deletions(-) diff --git a/alioth/src/virtio/pci.rs b/alioth/src/virtio/pci.rs index 648ac110..46c17655 100644 --- a/alioth/src/virtio/pci.rs +++ b/alioth/src/virtio/pci.rs @@ -41,7 +41,7 @@ use crate::pci::{self, Pci, PciBar}; use crate::sync::notifier::Notifier; use crate::utils::{get_atomic_high32, get_atomic_low32, set_atomic_high32, set_atomic_low32}; use crate::virtio::dev::{Register, StartParam, VirtioDevice, WakeEvent}; -use crate::virtio::queue::QueueReg; +use crate::virtio::queue::{QUEUE_SIZE_MAX, QueueReg}; use crate::virtio::{DevStatus, DeviceId, IrqSender, Result, VirtioFeature, error}; use crate::{consts, impl_mmio_for_zerocopy, mem}; @@ -226,6 +226,10 @@ where } self.irq_sender.msix_table.reset(); for q in self.queues.iter() { + q.size.store(QUEUE_SIZE_MAX, Ordering::Release); + q.desc.store(0, Ordering::Release); + q.driver.store(0, Ordering::Release); + q.device.store(0, Ordering::Release); q.enabled.store(false, Ordering::Release); } } diff --git a/alioth/src/virtio/pci_test.rs b/alioth/src/virtio/pci_test.rs index 84aa4799..21b2f801 100644 --- a/alioth/src/virtio/pci_test.rs +++ b/alioth/src/virtio/pci_test.rs @@ -29,7 +29,7 @@ use crate::virtio::pci::{ PciIrqSender, VIRTIO_MSI_NO_VECTOR, VirtioCommonCfg, VirtioPciMsixVector, VirtioPciRegister, VirtioPciRegisterMmio, }; -use crate::virtio::queue::QueueReg; +use crate::virtio::queue::{QUEUE_SIZE_MAX, QueueReg}; use crate::virtio::tests::FakeIoeventFd; use crate::virtio::{DevStatus, VirtioFeature}; @@ -785,6 +785,107 @@ fn test_device_status_reset_without_driver_ok() { ); } +#[test] +fn test_device_reset_queue_registers() { + let queues = Arc::new([ + QueueReg { + size: AtomicU16::new(QUEUE_SIZE_MAX), + ..Default::default() + }, + QueueReg { + size: AtomicU16::new(QUEUE_SIZE_MAX), + ..Default::default() + }, + ]); + let (mmio, event_rx) = create_test_mmio(queues.clone()); + + // Initialize MSI-X table with 2 Entry slots + *mmio.irq_sender.msix_table.entries.write() = vec![ + MsixTableMmioEntry::Entry(MsixTableEntry::default()), + MsixTableMmioEntry::Entry(MsixTableEntry::default()), + ] + .into_boxed_slice(); + + for (offset, size, val) in [ + // Configure queue 0 + (VirtioCommonCfg::OFFSET_QUEUE_SELECT, 2, 0), + (VirtioCommonCfg::OFFSET_QUEUE_SIZE, 2, 128), + (VirtioCommonCfg::OFFSET_QUEUE_DESC_LO, 4, 0x1000), + (VirtioCommonCfg::OFFSET_QUEUE_DESC_HI, 4, 0x1111), + (VirtioCommonCfg::OFFSET_QUEUE_DRIVER_LO, 4, 0x2000), + (VirtioCommonCfg::OFFSET_QUEUE_DRIVER_HI, 4, 0x2222), + (VirtioCommonCfg::OFFSET_QUEUE_DEVICE_LO, 4, 0x3000), + (VirtioCommonCfg::OFFSET_QUEUE_DEVICE_HI, 4, 0x3333), + (VirtioCommonCfg::OFFSET_QUEUE_MSIX_VECTOR, 2, 0), + (VirtioCommonCfg::OFFSET_QUEUE_ENABLE, 2, 1), + // Configure queue 1 + (VirtioCommonCfg::OFFSET_QUEUE_SELECT, 2, 1), + (VirtioCommonCfg::OFFSET_QUEUE_SIZE, 2, 128), + (VirtioCommonCfg::OFFSET_QUEUE_DESC_LO, 4, 0x4000), + (VirtioCommonCfg::OFFSET_QUEUE_DESC_HI, 4, 0x4444), + (VirtioCommonCfg::OFFSET_QUEUE_DRIVER_LO, 4, 0x5000), + (VirtioCommonCfg::OFFSET_QUEUE_DRIVER_HI, 4, 0x5555), + (VirtioCommonCfg::OFFSET_QUEUE_DEVICE_LO, 4, 0x6000), + (VirtioCommonCfg::OFFSET_QUEUE_DEVICE_HI, 4, 0x6666), + (VirtioCommonCfg::OFFSET_QUEUE_MSIX_VECTOR, 2, 1), + (VirtioCommonCfg::OFFSET_QUEUE_ENABLE, 2, 1), + ] { + assert_matches!(mmio.write(offset as u64, size, val), Ok(Action::None)); + } + + // Set device status to DRIVER_OK + assert_matches!( + mmio.write( + VirtioCommonCfg::OFFSET_DEVICE_STATUS as u64, + 1, + (DevStatus::ACK | DevStatus::DRIVER | DevStatus::FEATURES_OK | DevStatus::DRIVER_OK) + .bits() as u64 + ), + Ok(Action::None) + ); + assert_matches!(event_rx.try_recv(), Ok(WakeEvent::Start { .. })); + + // Reset device: write status = 0 + assert_matches!( + mmio.write(VirtioCommonCfg::OFFSET_DEVICE_STATUS as u64, 1, 0), + Ok(Action::None) + ); + assert_matches!(event_rx.try_recv(), Ok(WakeEvent::Reset)); + assert!(event_rx.is_empty()); + + // Verify all queue registers are reset + for (q_index, _) in queues.iter().enumerate() { + assert_matches!( + mmio.write( + VirtioCommonCfg::OFFSET_QUEUE_SELECT as u64, + 2, + q_index as u64 + ), + Ok(Action::None) + ); + for (offset, size, expected) in [ + (VirtioCommonCfg::OFFSET_QUEUE_SIZE, 2, QUEUE_SIZE_MAX as u64), + (VirtioCommonCfg::OFFSET_QUEUE_DESC_LO, 4, 0), + (VirtioCommonCfg::OFFSET_QUEUE_DESC_HI, 4, 0), + (VirtioCommonCfg::OFFSET_QUEUE_DRIVER_LO, 4, 0), + (VirtioCommonCfg::OFFSET_QUEUE_DRIVER_HI, 4, 0), + (VirtioCommonCfg::OFFSET_QUEUE_DEVICE_LO, 4, 0), + (VirtioCommonCfg::OFFSET_QUEUE_DEVICE_HI, 4, 0), + ( + VirtioCommonCfg::OFFSET_QUEUE_MSIX_VECTOR, + 2, + VIRTIO_MSI_NO_VECTOR as u64, + ), + (VirtioCommonCfg::OFFSET_QUEUE_ENABLE, 2, 0), + ] { + assert_matches!( + mmio.read(offset as u64, size), + Ok(val) if val == expected + ); + } + } +} + #[rstest] #[case(VirtioCommonCfg::OFFSET_CONFIG_MSIX_VECTOR, None)] #[case(VirtioCommonCfg::OFFSET_QUEUE_MSIX_VECTOR, Some(0))]