From fe6bcd03769feddff24f50e386d8115d76a1470e Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Sat, 18 Apr 2026 12:21:39 +0100 Subject: [PATCH] virtio-devices: pci: Clear all config state on reset Per the virtio spec a device reset must return the device to its power-on state. The reset path was only zeroing queue_select. Add VirtioPciCommonConfig::reset() and call it from the transport's reset path so the configuration is cleared. Also relax the condition to allow the device to be reset at any time to match the spec. Signed-off-by: Rob Bradford --- .../src/transport/pci_common_config.rs | 47 +++++++++++++++++++ virtio-devices/src/transport/pci_device.rs | 30 +++++------- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/virtio-devices/src/transport/pci_common_config.rs b/virtio-devices/src/transport/pci_common_config.rs index 379622bd9..1ca86a220 100644 --- a/virtio-devices/src/transport/pci_common_config.rs +++ b/virtio-devices/src/transport/pci_common_config.rs @@ -160,6 +160,19 @@ impl VirtioPciCommonConfig { } } + /// Returns the common configuration to its power-on state. Per the virtio + /// spec a device reset must restore the values that a fresh driver would + /// observe. + pub fn reset(&mut self) { + self.driver_status.store(0, Ordering::Release); + self.device_feature_select = 0; + self.driver_feature_select = 0; + self.queue_select = 0; + self.msix_config + .store(VIRTQ_MSI_NO_VECTOR, Ordering::Release); + self.msix_queues.lock().unwrap().fill(VIRTQ_MSI_NO_VECTOR); + } + pub fn read(&mut self, offset: u64, data: &mut [u8], queues: &[Queue]) { assert!(data.len() <= 8); @@ -521,4 +534,38 @@ mod unit_tests { // Write queue_msix_vector — must not panic. regs.write(0x1a, &[0xAB, 0xCD], &mut queues); } + + #[test] + fn reset_returns_initial_state() { + let dev: Arc> = Arc::new(Mutex::new(DummyDevice(0))); + let mut regs = VirtioPciCommonConfig { + device: dev, + driver_status: Arc::new(AtomicU8::new(0x55)), + config_generation: 0xab, + device_feature_select: 1, + driver_feature_select: 1, + queue_select: 7, + msix_config: Arc::new(AtomicU16::new(3)), + msix_queues: Arc::new(Mutex::new(vec![1, 2, 3])), + }; + + regs.reset(); + + assert_eq!(regs.driver_status.load(Ordering::Acquire), 0); + assert_eq!(regs.config_generation, 0xab); // unchanged across reset + assert_eq!(regs.device_feature_select, 0); + assert_eq!(regs.driver_feature_select, 0); + assert_eq!(regs.queue_select, 0); + assert_eq!( + regs.msix_config.load(Ordering::Acquire), + VIRTQ_MSI_NO_VECTOR + ); + assert!( + regs.msix_queues + .lock() + .unwrap() + .iter() + .all(|v| *v == VIRTQ_MSI_NO_VECTOR) + ); + } } diff --git a/virtio-devices/src/transport/pci_device.rs b/virtio-devices/src/transport/pci_device.rs index 98abb0493..fe3cf7105 100644 --- a/virtio-devices/src/transport/pci_device.rs +++ b/virtio-devices/src/transport/pci_device.rs @@ -1245,24 +1245,20 @@ impl PciDevice for VirtioPciDevice { return Some(barrier); } - // Device has been reset by the driver - if self.device_activated.load(Ordering::SeqCst) && self.is_driver_init() { - let mut device = self.device.lock().unwrap(); - if let Some(virtio_interrupt) = device.reset() { - // Upon reset the device returns its interrupt EventFD - self.virtio_interrupt = Some(virtio_interrupt); - self.device_activated.store(false, Ordering::SeqCst); - - // Reset queue readiness (changes queue_enable), queue sizes - // and selected_queue as per spec for reset - self.queues.iter_mut().for_each(Queue::reset); - self.common_config.queue_select = 0; - } else { - error!("Attempt to reset device when not implemented in underlying device"); - self.common_config - .driver_status - .store(crate::DEVICE_FAILED as u8, Ordering::SeqCst); + // The driver requested a reset by writing 0 to device_status. Per the + // virtio spec this is permitted at any point in initialisation. + if self.is_driver_init() { + if self.device_activated.swap(false, Ordering::SeqCst) { + let mut device = self.device.lock().unwrap(); + if let Some(virtio_interrupt) = device.reset() { + // Upon reset the device returns its interrupt EventFD + self.virtio_interrupt = Some(virtio_interrupt); + } } + + // Reset queue readiness and the common configuration + self.queues.iter_mut().for_each(Queue::reset); + self.common_config.reset(); } None