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