vmm/devices/virtio/
device.rs

1// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5// Use of this source code is governed by a BSD-style license that can be
6// found in the THIRD-PARTY file.
7
8use std::fmt;
9use std::sync::Arc;
10use std::sync::atomic::AtomicU32;
11
12use vmm_sys_util::eventfd::EventFd;
13
14use super::ActivateError;
15use super::queue::{Queue, QueueError};
16use super::transport::VirtioInterrupt;
17use crate::devices::virtio::AsAny;
18use crate::devices::virtio::block::virtio::io::cow_io::CowFileEngine;
19use crate::logger::warn;
20use crate::vstate::memory::GuestMemoryMmap;
21
22/// State of an active VirtIO device
23#[derive(Debug, Clone)]
24pub struct ActiveState {
25    pub mem: GuestMemoryMmap,
26    pub interrupt: Arc<dyn VirtioInterrupt>,
27}
28
29/// Enum that indicates if a VirtioDevice is inactive or has been activated
30/// and memory attached to it.
31#[derive(Debug)]
32pub enum DeviceState {
33    Inactive,
34    Activated(ActiveState),
35}
36
37impl DeviceState {
38    /// Checks if the device is activated.
39    pub fn is_activated(&self) -> bool {
40        match self {
41            DeviceState::Inactive => false,
42            DeviceState::Activated(_) => true,
43        }
44    }
45
46    /// Gets the memory and interrupt attached to the device if it is activated.
47    pub fn active_state(&self) -> Option<&ActiveState> {
48        match self {
49            DeviceState::Activated(state) => Some(state),
50            DeviceState::Inactive => None,
51        }
52    }
53}
54
55/// Trait for virtio devices to be driven by a virtio transport.
56///
57/// The lifecycle of a virtio device is to be moved to a virtio transport, which will then query the
58/// device. The virtio devices needs to create queues, events and event fds for interrupts and
59/// expose them to the transport via get_queues/get_queue_events/get_interrupt/get_interrupt_status
60/// fns.
61pub trait VirtioDevice: AsAny + Send {
62    /// Get the available features offered by device.
63    fn avail_features(&self) -> u64;
64
65    /// Get acknowledged features of the driver.
66    fn acked_features(&self) -> u64;
67
68    /// Set acknowledged features of the driver.
69    /// This function must maintain the following invariant:
70    /// - self.avail_features() & self.acked_features() = self.get_acked_features()
71    fn set_acked_features(&mut self, acked_features: u64);
72
73    /// Check if virtio device has negotiated given feature.
74    fn has_feature(&self, feature: u64) -> bool {
75        (self.acked_features() & (1 << feature)) != 0
76    }
77
78    /// The virtio device type (as a constant of the struct).
79    fn const_device_type() -> u32
80    where
81        Self: Sized;
82
83    /// The virtio device type.
84    ///
85    /// It should be the same as returned by Self::const_device_type().
86    fn device_type(&self) -> u32;
87
88    /// Returns the device queues.
89    fn queues(&self) -> &[Queue];
90
91    /// Returns a mutable reference to the device queues.
92    fn queues_mut(&mut self) -> &mut [Queue];
93
94    /// Returns the device queues event fds.
95    fn queue_events(&self) -> &[EventFd];
96
97    /// Returns the current device interrupt status.
98    fn interrupt_status(&self) -> Arc<AtomicU32> {
99        self.interrupt_trigger().status()
100    }
101
102    fn interrupt_trigger(&self) -> &dyn VirtioInterrupt;
103
104    /// The set of feature bits shifted by `page * 32`.
105    fn avail_features_by_page(&self, page: u32) -> u32 {
106        let avail_features = self.avail_features();
107        match page {
108            // Get the lower 32-bits of the features bitfield.
109            0 => (avail_features & 0xFFFFFFFF) as u32,
110            // Get the upper 32-bits of the features bitfield.
111            1 => (avail_features >> 32) as u32,
112            _ => {
113                warn!("Received request for unknown features page.");
114                0u32
115            }
116        }
117    }
118
119    /// Acknowledges that this set of features should be enabled.
120    fn ack_features_by_page(&mut self, page: u32, value: u32) {
121        let mut v = match page {
122            0 => u64::from(value),
123            1 => u64::from(value) << 32,
124            _ => {
125                warn!("Cannot acknowledge unknown features page: {}", page);
126                0u64
127            }
128        };
129
130        // Check if the guest is ACK'ing a feature that we didn't claim to have.
131        let avail_features = self.avail_features();
132        let unrequested_features = v & !avail_features;
133        if unrequested_features != 0 {
134            warn!("Received acknowledge request for unknown feature: {:#x}", v);
135            // Don't count these features as acked.
136            v &= !unrequested_features;
137        }
138        self.set_acked_features(self.acked_features() | v);
139    }
140
141    /// Reads this device configuration space at `offset`.
142    fn read_config(&self, offset: u64, data: &mut [u8]);
143
144    /// Writes to this device configuration space at `offset`.
145    fn write_config(&mut self, offset: u64, data: &[u8]);
146
147    /// Performs the formal activation for a device, which can be verified also with `is_activated`.
148    fn activate(
149        &mut self,
150        mem: GuestMemoryMmap,
151        interrupt: Arc<dyn VirtioInterrupt>,
152    ) -> Result<(), ActivateError>;
153
154    /// Checks if the resources of this device are activated.
155    fn is_activated(&self) -> bool;
156
157    /// Optionally deactivates this device and returns ownership of the guest memory map, interrupt
158    /// event, and queue events.
159    fn reset(&mut self) -> Option<(Arc<dyn VirtioInterrupt>, Vec<EventFd>)> {
160        None
161    }
162
163    /// Mark pages used by queues as dirty.
164    fn mark_queue_memory_dirty(&mut self, mem: &GuestMemoryMmap) -> Result<(), QueueError> {
165        for queue in self.queues_mut() {
166            queue.initialize(mem)?
167        }
168        Ok(())
169    }
170
171    /// Kick the device, as if it had received external events.
172    fn kick(&mut self) {}
173
174    /// Returns a COW file engine if the device exposes one (NYX extension).
175    fn as_cow_file_engine(&self) -> Option<&CowFileEngine> {
176        None
177    }
178
179    /// Handle a queue notification from an MMIO write (NYX extension).
180    fn nyx_handle_queue_event(&mut self, _queue_index: u16) {}
181}
182
183impl fmt::Debug for dyn VirtioDevice {
184    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185        write!(f, "VirtioDevice type {}", self.device_type())
186    }
187}
188
189/// Utility to define both const_device_type and device_type with a u32 constant
190#[macro_export]
191macro_rules! impl_device_type {
192    ($const_type:expr) => {
193        fn const_device_type() -> u32 {
194            $const_type
195        }
196
197        fn device_type(&self) -> u32 {
198            Self::const_device_type()
199        }
200    };
201}
202
203#[cfg(test)]
204pub(crate) mod tests {
205    use super::*;
206
207    #[derive(Debug)]
208    struct MockVirtioDevice {
209        avail_features: u64,
210        acked_features: u64,
211    }
212
213    impl VirtioDevice for MockVirtioDevice {
214        impl_device_type!(0);
215
216        fn avail_features(&self) -> u64 {
217            self.avail_features
218        }
219
220        fn acked_features(&self) -> u64 {
221            self.acked_features
222        }
223
224        fn set_acked_features(&mut self, acked_features: u64) {
225            self.acked_features = acked_features
226        }
227
228        fn queues(&self) -> &[Queue] {
229            todo!()
230        }
231
232        fn queues_mut(&mut self) -> &mut [Queue] {
233            todo!()
234        }
235
236        fn queue_events(&self) -> &[EventFd] {
237            todo!()
238        }
239
240        fn interrupt_trigger(&self) -> &dyn VirtioInterrupt {
241            todo!()
242        }
243
244        fn read_config(&self, _offset: u64, _data: &mut [u8]) {
245            todo!()
246        }
247
248        fn write_config(&mut self, _offset: u64, _data: &[u8]) {
249            todo!()
250        }
251
252        fn activate(
253            &mut self,
254            _mem: GuestMemoryMmap,
255            _interrupt: Arc<dyn VirtioInterrupt>,
256        ) -> Result<(), ActivateError> {
257            todo!()
258        }
259
260        fn is_activated(&self) -> bool {
261            todo!()
262        }
263    }
264
265    #[test]
266    fn test_has_feature() {
267        let mut device = MockVirtioDevice {
268            avail_features: 0,
269            acked_features: 0,
270        };
271
272        let mock_feature_1 = 1u64;
273        assert!(!device.has_feature(mock_feature_1));
274        device.acked_features = 1 << mock_feature_1;
275        assert!(device.has_feature(mock_feature_1));
276
277        let mock_feature_2 = 2u64;
278        assert!(!device.has_feature(mock_feature_2));
279        device.acked_features = (1 << mock_feature_1) | (1 << mock_feature_2);
280        assert!(device.has_feature(mock_feature_1));
281        assert!(device.has_feature(mock_feature_2));
282    }
283
284    #[test]
285    fn test_features() {
286        let features: u64 = 0x11223344_55667788;
287
288        let mut device = MockVirtioDevice {
289            avail_features: features,
290            acked_features: 0,
291        };
292
293        assert_eq!(
294            device.avail_features_by_page(0),
295            (features & 0xFFFFFFFF) as u32,
296        );
297        assert_eq!(device.avail_features_by_page(1), (features >> 32) as u32);
298        for i in 2..10 {
299            assert_eq!(device.avail_features_by_page(i), 0u32);
300        }
301
302        for i in 0..10 {
303            device.ack_features_by_page(i, u32::MAX);
304        }
305
306        assert_eq!(device.acked_features, features);
307    }
308}