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