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