Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/virtio_device.rs
5394 views
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
/// Trait for mapping memory into the device's shared memory region.
35
pub 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
/// ```
81
pub 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]
270
macro_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
}
389
390