xref: /aosp_15_r20/external/crosvm/devices/src/virtio/virtio_device.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2018 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::BTreeMap;
6 
7 #[cfg(target_arch = "x86_64")]
8 use acpi_tables::sdt::SDT;
9 use anyhow::anyhow;
10 use anyhow::Result;
11 use base::Protection;
12 use base::RawDescriptor;
13 use hypervisor::MemCacheType;
14 use vm_control::VmMemorySource;
15 use vm_memory::GuestAddress;
16 use vm_memory::GuestMemory;
17 
18 use super::*;
19 use crate::pci::MsixStatus;
20 use crate::pci::PciAddress;
21 use crate::pci::PciBarConfiguration;
22 use crate::pci::PciBarIndex;
23 use crate::pci::PciCapability;
24 
25 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
26 pub enum VirtioTransportType {
27     Pci,
28     Mmio,
29 }
30 
31 /// Type of Virtio device memory mapping to use.
32 pub enum SharedMemoryPrepareType {
33     /// On first attempted mapping, the entire SharedMemoryRegion is configured with declared
34     /// MemCacheType.
35     SingleMappingOnFirst(MemCacheType),
36     /// No mapping preparation is performed. each mapping is handled individually
37     DynamicPerMapping,
38 }
39 
40 #[derive(Clone)]
41 pub struct SharedMemoryRegion {
42     /// The id of the shared memory region. A device may have multiple regions, but each
43     /// must have a unique id. The meaning of a particular region is device-specific.
44     pub id: u8,
45     pub length: u64,
46 }
47 
48 /// Trait for mapping memory into the device's shared memory region.
49 pub trait SharedMemoryMapper: Send {
50     /// Maps the given |source| into the shared memory region at |offset|.
add_mapping( &mut self, source: VmMemorySource, offset: u64, prot: Protection, cache: MemCacheType, ) -> Result<()>51     fn add_mapping(
52         &mut self,
53         source: VmMemorySource,
54         offset: u64,
55         prot: Protection,
56         cache: MemCacheType,
57     ) -> Result<()>;
58 
59     /// Removes the mapping beginning at |offset|.
remove_mapping(&mut self, offset: u64) -> Result<()>60     fn remove_mapping(&mut self, offset: u64) -> Result<()>;
61 
as_raw_descriptor(&self) -> Option<RawDescriptor>62     fn as_raw_descriptor(&self) -> Option<RawDescriptor> {
63         None
64     }
65 }
66 
67 /// Trait for virtio devices to be driven by a virtio transport.
68 ///
69 /// The lifecycle of a virtio device is to be moved to a virtio transport, which will then query the
70 /// device. Once the guest driver has configured the device, `VirtioDevice::activate` will be called
71 /// and all the events, memory, and queues for device operation will be moved into the device.
72 /// Optionally, a virtio device can implement device reset in which it returns said resources and
73 /// resets its internal.
74 ///
75 /// Virtio device state machine
76 /// ```none
77 ///                           restore (inactive)
78 ///       ----------------------------------------------------
79 ///       |                                                  |
80 ///       |                                                  V
81 ///       |                       ------------         --------------
82 /// ------------- restore(active) |  asleep  |         |   asleep   |   // States in this row
83 /// |asleep(new)|---------------> | (active) |         | (inactive) |   // can be snapshotted
84 /// -------------                 ------------         --------------
85 ///    ^       |                     ^    |              ^      |
86 ///    |       |                     |    |              |      |
87 ///  sleep    wake                sleep  wake         sleep   wake
88 ///    |       |                     |    |              |      |
89 ///    |       V                     |    V              |      V
90 ///  ------------     activate     ----------  reset   ------------
91 ///  |    new   | ---------------> | active | ------>  | inactive |
92 ///  ------------                  ---------- <------  ------------
93 ///                                           activate
94 /// ```
95 pub trait VirtioDevice: Send {
96     /// Returns a label suitable for debug output.
debug_label(&self) -> String97     fn debug_label(&self) -> String {
98         format!("virtio-{}", self.device_type())
99     }
100 
101     /// A vector of device-specific file descriptors that must be kept open
102     /// after jailing. Must be called before the process is jailed.
keep_rds(&self) -> Vec<RawDescriptor>103     fn keep_rds(&self) -> Vec<RawDescriptor>;
104 
105     /// The virtio device type.
device_type(&self) -> DeviceType106     fn device_type(&self) -> DeviceType;
107 
108     /// The maximum size of each queue that this device supports.
queue_max_sizes(&self) -> &[u16]109     fn queue_max_sizes(&self) -> &[u16];
110 
111     /// The number of interrupts used by this device.
num_interrupts(&self) -> usize112     fn num_interrupts(&self) -> usize {
113         self.queue_max_sizes().len()
114     }
115 
116     /// The set of feature bits that this device supports in addition to the base features.
features(&self) -> u64117     fn features(&self) -> u64 {
118         0
119     }
120 
121     /// Acknowledges that this set of features should be enabled.
ack_features(&mut self, value: u64)122     fn ack_features(&mut self, value: u64) {
123         let _ = value;
124     }
125 
126     /// Reads this device configuration space at `offset`.
read_config(&self, offset: u64, data: &mut [u8])127     fn read_config(&self, offset: u64, data: &mut [u8]) {
128         let _ = offset;
129         let _ = data;
130     }
131 
132     /// Writes to this device configuration space at `offset`.
write_config(&mut self, offset: u64, data: &[u8])133     fn write_config(&mut self, offset: u64, data: &[u8]) {
134         let _ = offset;
135         let _ = data;
136     }
137 
138     /// Activates this device for real usage.
activate( &mut self, mem: GuestMemory, interrupt: Interrupt, queues: BTreeMap<usize, Queue>, ) -> Result<()>139     fn activate(
140         &mut self,
141         mem: GuestMemory,
142         interrupt: Interrupt,
143         queues: BTreeMap<usize, Queue>,
144     ) -> Result<()>;
145 
146     /// Optionally deactivates this device. If the reset method is
147     /// not able to reset the virtio device, or the virtio device model doesn't
148     /// implement the reset method, an `Err` value is returned to indicate
149     /// the reset is not successful. Otherwise `Ok(())` should be returned.
reset(&mut self) -> Result<()>150     fn reset(&mut self) -> Result<()> {
151         Err(anyhow!("reset not implemented for {}", self.debug_label()))
152     }
153 
154     /// Returns any additional BAR configuration required by the device.
get_device_bars(&mut self, _address: PciAddress) -> Vec<PciBarConfiguration>155     fn get_device_bars(&mut self, _address: PciAddress) -> Vec<PciBarConfiguration> {
156         Vec::new()
157     }
158 
159     /// Returns any additional capabiltiies required by the device.
get_device_caps(&self) -> Vec<Box<dyn PciCapability>>160     fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {
161         Vec::new()
162     }
163 
164     /// Invoked when the device is sandboxed.
on_device_sandboxed(&mut self)165     fn on_device_sandboxed(&mut self) {}
166 
control_notify(&self, _behavior: MsixStatus)167     fn control_notify(&self, _behavior: MsixStatus) {}
168 
169     #[cfg(target_arch = "x86_64")]
generate_acpi( &mut self, _pci_address: &Option<PciAddress>, sdts: Vec<SDT>, ) -> Option<Vec<SDT>>170     fn generate_acpi(
171         &mut self,
172         _pci_address: &Option<PciAddress>,
173         sdts: Vec<SDT>,
174     ) -> Option<Vec<SDT>> {
175         Some(sdts)
176     }
177 
178     /// Reads from a BAR region mapped in to the device.
179     /// * `addr` - The guest address inside the BAR.
180     /// * `data` - Filled with the data from `addr`.
read_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &mut [u8])181     fn read_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &mut [u8]) {}
182 
183     /// Writes to a BAR region mapped in to the device.
184     /// * `addr` - The guest address inside the BAR.
185     /// * `data` - The data to write.
write_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &[u8])186     fn write_bar(&mut self, _bar_index: PciBarIndex, _offset: u64, _data: &[u8]) {}
187 
188     /// Returns the PCI address where the device will be allocated.
189     /// Returns `None` if any address is good for the device.
pci_address(&self) -> Option<PciAddress>190     fn pci_address(&self) -> Option<PciAddress> {
191         None
192     }
193 
194     /// Returns the Virtio transport type: PCI (default for crosvm) or MMIO.
transport_type(&self) -> VirtioTransportType195     fn transport_type(&self) -> VirtioTransportType {
196         VirtioTransportType::Pci
197     }
198 
199     /// Returns the device's shared memory region if present.
get_shared_memory_region(&self) -> Option<SharedMemoryRegion>200     fn get_shared_memory_region(&self) -> Option<SharedMemoryRegion> {
201         None
202     }
203 
204     /// If true, VFIO passthrough devices can access descriptors mapped into
205     /// this region by mapping the corresponding addresses from this device's
206     /// PCI bar into their IO address space with virtio-iommu.
207     ///
208     /// NOTE: Not all vm_control::VmMemorySource types are supported.
209     /// NOTE: Not yet compatible with PrepareSharedMemoryRegion (aka fixed mapping).
expose_shmem_descriptors_with_viommu(&self) -> bool210     fn expose_shmem_descriptors_with_viommu(&self) -> bool {
211         false
212     }
213 
214     /// Provides the trait object used to map files into the device's shared
215     /// memory region.
216     ///
217     /// If `get_shared_memory_region` returns `Some`, then this will be called
218     /// before `activate`.
set_shared_memory_mapper(&mut self, _mapper: Box<dyn SharedMemoryMapper>)219     fn set_shared_memory_mapper(&mut self, _mapper: Box<dyn SharedMemoryMapper>) {}
220 
221     /// Provides the base address of the shared memory region, if one is present. Will
222     /// be called before `activate`.
223     ///
224     /// NOTE: Mappings in shared memory regions should be accessed via offset, rather
225     /// than via raw guest physical address. This function is only provided so
226     /// devices can remain backwards compatible with older drivers.
set_shared_memory_region_base(&mut self, _addr: GuestAddress)227     fn set_shared_memory_region_base(&mut self, _addr: GuestAddress) {}
228 
229     /// Queries the implementation whether a single prepared hypervisor memory mapping with explicit
230     /// caching type should be setup lazily on first mapping request, or whether to dynamically
231     /// setup a hypervisor mapping with every request's caching type.
get_shared_memory_prepare_type(&mut self) -> SharedMemoryPrepareType232     fn get_shared_memory_prepare_type(&mut self) -> SharedMemoryPrepareType {
233         // default to lazy-prepare of a single memslot with explicit caching type
234         SharedMemoryPrepareType::SingleMappingOnFirst(MemCacheType::CacheCoherent)
235     }
236 
237     /// Pause all processing.
238     ///
239     /// Gives up the queues so that a higher layer can potentially snapshot them. The
240     /// implementations should also drop the `Interrupt` and queues `Event`s that were given along
241     /// with the queues originally.
242     ///
243     /// Unlike `Suspendable::sleep`, this is not idempotent. Attempting to sleep while already
244     /// asleep is an error.
virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>>245     fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
246         anyhow::bail!("virtio_sleep not implemented for {}", self.debug_label());
247     }
248 
249     /// Resume all processing.
250     ///
251     /// If the device's queues are active, then the queues and associated data will is included.
252     ///
253     /// Unlike `Suspendable::wake`, this is not idempotent. Attempting to wake while already awake
254     /// is an error.
virtio_wake( &mut self, _queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, ) -> anyhow::Result<()>255     fn virtio_wake(
256         &mut self,
257         _queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
258     ) -> anyhow::Result<()> {
259         anyhow::bail!("virtio_wake not implemented for {}", self.debug_label());
260     }
261 
262     /// Snapshot current state. Device must be asleep.
virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value>263     fn virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
264         anyhow::bail!("virtio_snapshot not implemented for {}", self.debug_label());
265     }
266 
267     /// Restore device state from a snapshot.
268     /// TODO(b/280607404): Vhost user will need fds passed to the device process.
virtio_restore(&mut self, _data: serde_json::Value) -> anyhow::Result<()>269     fn virtio_restore(&mut self, _data: serde_json::Value) -> anyhow::Result<()> {
270         anyhow::bail!("virtio_restore not implemented for {}", self.debug_label());
271     }
272 
273     // Returns a tuple consisting of the non-arch specific part of the OpenFirmware path,
274     // represented as bytes, and the boot index of a device. The non-arch specific part of path for
275     // a virtio-blk device, for example, would consist of everything after the first '/' below:
276     // pci@i0cf8/scsi@6[,3]/disk@0,0
277     //    ^           ^  ^       ^ ^
278     //    |           |  |       fixed
279     //    |           | (PCI function related to disk (optional))
280     // (x86 specf  (PCI slot holding disk)
281     //  root at sys
282     //  bus port)
bootorder_fw_cfg(&self, _pci_address: u8) -> Option<(Vec<u8>, usize)>283     fn bootorder_fw_cfg(&self, _pci_address: u8) -> Option<(Vec<u8>, usize)> {
284         None
285     }
286 }
287 
288 // General tests that should pass on all suspendables.
289 // Do implement device-specific tests to validate the functionality of the device.
290 // Those tests are not a replacement for regular tests. Only an extension specific to the trait's
291 // basic functionality.
292 /// `name` is the name of the test grouping. Can be anything unique within the same crate.
293 /// `dev` is a block that returns a created virtio device.
294 /// ``num_queues` is the number of queues to be created.
295 /// `modfun` is the function name of the function that would modify the device. The function call
296 /// should modify the device so that a snapshot taken after the function call would be different
297 /// from a snapshot taken before the function call.
298 #[macro_export]
299 macro_rules! suspendable_virtio_tests {
300     ($name:ident, $dev: expr, $num_queues:literal, $modfun:expr) => {
301         mod $name {
302             use $crate::virtio::QueueConfig;
303 
304             use super::*;
305 
306             fn memory() -> GuestMemory {
307                 GuestMemory::new(&[(GuestAddress(0u64), 4 * 1024 * 1024)])
308                     .expect("Creating guest memory failed.")
309             }
310 
311             fn interrupt() -> Interrupt {
312                 Interrupt::new_for_test()
313             }
314 
315             fn create_queues(
316                 num_queues: usize,
317                 queue_size: u16,
318                 mem: &GuestMemory,
319                 interrupt: Interrupt,
320             ) -> BTreeMap<usize, Queue> {
321                 let mut queues = BTreeMap::new();
322                 for i in 0..num_queues {
323                     // activate with queues of an arbitrary size.
324                     let mut queue = QueueConfig::new(queue_size, 0);
325                     queue.set_ready(true);
326                     let queue = queue
327                         .activate(mem, base::Event::new().unwrap(), interrupt.clone())
328                         .expect("QueueConfig::activate");
329                     queues.insert(i, queue);
330                 }
331                 queues
332             }
333 
334             #[test]
335             fn test_unactivated_sleep_snapshot_wake() {
336                 let (_ctx, mut device) = $dev();
337                 let sleep_result = device.virtio_sleep().expect("failed to sleep");
338                 assert!(sleep_result.is_none());
339                 device.virtio_snapshot().expect("failed to snapshot");
340                 device.virtio_wake(None).expect("failed to wake");
341             }
342 
343             #[test]
344             fn test_sleep_snapshot_wake() {
345                 let (_ctx, mut device) = $dev();
346                 let mem = memory();
347                 let interrupt = interrupt();
348                 let queues = create_queues(
349                     $num_queues,
350                     device
351                         .queue_max_sizes()
352                         .first()
353                         .cloned()
354                         .expect("missing queue size"),
355                     &mem,
356                     interrupt.clone(),
357                 );
358                 device
359                     .activate(mem.clone(), interrupt.clone(), queues)
360                     .expect("failed to activate");
361                 let sleep_result = device
362                     .virtio_sleep()
363                     .expect("failed to sleep")
364                     .expect("missing queues while sleeping");
365                 device.virtio_snapshot().expect("failed to snapshot");
366                 device
367                     .virtio_wake(Some((mem.clone(), interrupt.clone(), sleep_result)))
368                     .expect("failed to wake");
369             }
370 
371             #[test]
372             fn test_suspend_mod_restore() {
373                 let (mut context, mut device) = $dev();
374                 let mem = memory();
375                 let interrupt = interrupt();
376                 let queues = create_queues(
377                     $num_queues,
378                     device
379                         .queue_max_sizes()
380                         .first()
381                         .cloned()
382                         .expect("missing queue size"),
383                     &mem,
384                     interrupt.clone(),
385                 );
386                 device
387                     .activate(mem.clone(), interrupt.clone(), queues)
388                     .expect("failed to activate");
389                 let sleep_result = device
390                     .virtio_sleep()
391                     .expect("failed to sleep")
392                     .expect("missing queues while sleeping");
393                 // Modify device before snapshotting.
394                 $modfun(&mut context, &mut device);
395                 let snap = device
396                     .virtio_snapshot()
397                     .expect("failed to take initial snapshot");
398                 device
399                     .virtio_wake(Some((mem.clone(), interrupt.clone(), sleep_result)))
400                     .expect("failed to wake");
401 
402                 // Create a new device to restore the previously taken snapshot
403                 let (_ctx2, mut device) = $dev();
404                 // Sleep the device before restore
405                 assert!(device.virtio_sleep().expect("failed to sleep").is_none());
406                 device
407                     .virtio_restore(snap.clone())
408                     .expect("failed to restore");
409                 let snap2 = device
410                     .virtio_snapshot()
411                     .expect("failed to take snapshot after mod");
412                 assert_eq!(snap, snap2);
413             }
414         }
415     };
416 }
417