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