Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/mod.rs
5394 views
1
// Copyright 2017 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
//! Implements virtio devices, queues, and transport mechanisms.
6
7
mod async_utils;
8
#[cfg(feature = "balloon")]
9
mod balloon;
10
mod descriptor_chain;
11
mod descriptor_utils;
12
pub mod device_constants;
13
pub mod input;
14
mod interrupt;
15
mod iommu;
16
#[cfg(feature = "net")]
17
pub mod net;
18
#[cfg(feature = "pvclock")]
19
pub mod pvclock;
20
mod queue;
21
mod rng;
22
#[cfg(feature = "vtpm")]
23
mod tpm;
24
#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
25
mod video;
26
mod virtio_device;
27
mod virtio_mmio_device;
28
mod virtio_pci_common_config;
29
mod virtio_pci_device;
30
31
pub mod block;
32
pub mod console;
33
#[cfg(feature = "gpu")]
34
pub mod gpu;
35
#[cfg(all(unix, feature = "media"))]
36
pub mod media;
37
pub mod resource_bridge;
38
pub mod scsi;
39
#[cfg(feature = "audio")]
40
pub mod snd;
41
pub mod vhost;
42
pub mod vhost_user_backend;
43
pub mod vhost_user_frontend;
44
pub mod vsock;
45
46
pub use vmm_vhost::SharedMemoryRegion;
47
48
#[cfg(feature = "balloon")]
49
pub use self::balloon::Balloon;
50
#[cfg(feature = "balloon")]
51
pub use self::balloon::BalloonFeatures;
52
pub use self::block::BlockAsync;
53
pub use self::console::Console;
54
pub use self::descriptor_chain::DescriptorChain;
55
pub use self::descriptor_chain::DescriptorChainIter;
56
pub use self::descriptor_utils::create_descriptor_chain;
57
pub use self::descriptor_utils::DescriptorType;
58
pub use self::descriptor_utils::Reader;
59
pub use self::descriptor_utils::Writer;
60
#[cfg(feature = "gpu")]
61
pub use self::gpu::DisplayBackend;
62
#[cfg(feature = "gpu")]
63
pub use self::gpu::Gpu;
64
#[cfg(feature = "gpu")]
65
pub use self::gpu::GpuDisplayMode;
66
#[cfg(feature = "gpu")]
67
pub use self::gpu::GpuDisplayParameters;
68
#[cfg(feature = "gpu")]
69
pub use self::gpu::GpuMode;
70
#[cfg(feature = "gpu")]
71
pub use self::gpu::GpuMouseMode;
72
#[cfg(feature = "gpu")]
73
pub use self::gpu::GpuParameters;
74
#[cfg(feature = "gpu")]
75
pub use self::gpu::GpuWsi;
76
pub use self::interrupt::Interrupt;
77
pub use self::interrupt::InterruptSnapshot;
78
pub use self::iommu::ipc_memory_mapper;
79
pub use self::iommu::memory_mapper;
80
pub use self::iommu::Iommu;
81
pub use self::iommu::IommuError;
82
#[cfg(feature = "net")]
83
pub use self::net::Net;
84
#[cfg(feature = "net")]
85
pub use self::net::NetError;
86
#[cfg(feature = "net")]
87
pub use self::net::NetParameters;
88
#[cfg(feature = "net")]
89
pub use self::net::NetParametersMode;
90
pub use self::queue::split_descriptor_chain::Desc;
91
pub use self::queue::split_descriptor_chain::SplitDescriptorChain;
92
pub use self::queue::PeekedDescriptorChain;
93
pub use self::queue::Queue;
94
pub use self::queue::QueueConfig;
95
pub use self::rng::Rng;
96
pub use self::scsi::Controller as ScsiController;
97
pub use self::scsi::DiskConfig as ScsiDiskConfig;
98
#[cfg(feature = "vtpm")]
99
pub use self::tpm::Tpm;
100
#[cfg(feature = "vtpm")]
101
pub use self::tpm::TpmBackend;
102
pub use self::vhost_user_frontend::VhostUserFrontend;
103
#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
104
pub use self::video::VideoDevice;
105
pub use self::virtio_device::SharedMemoryMapper;
106
pub use self::virtio_device::SharedMemoryPrepareType;
107
pub use self::virtio_device::VirtioDevice;
108
pub use self::virtio_mmio_device::VirtioMmioDevice;
109
pub use self::virtio_pci_device::PciCapabilityType;
110
pub use self::virtio_pci_device::VirtioPciCap;
111
pub use self::virtio_pci_device::VirtioPciDevice;
112
pub use self::virtio_pci_device::VirtioPciShmCap;
113
#[cfg(feature = "pvclock")]
114
pub use self::DeviceType::Pvclock;
115
116
cfg_if::cfg_if! {
117
if #[cfg(any(target_os = "android", target_os = "linux"))] {
118
mod p9;
119
mod pmem;
120
121
pub mod wl;
122
pub mod fs;
123
124
pub use self::iommu::sys::linux::vfio_wrapper;
125
#[cfg(feature = "net")]
126
pub use self::net::VhostNetParameters;
127
#[cfg(feature = "net")]
128
pub use self::net::VHOST_NET_DEFAULT_PATH;
129
pub use self::p9::P9;
130
pub use self::pmem::Pmem;
131
pub use self::pmem::PmemConfig;
132
pub use self::pmem::MemSlotConfig;
133
#[cfg(feature = "audio")]
134
pub use self::snd::new_sound;
135
pub use self::wl::Wl;
136
} else if #[cfg(windows)] {
137
pub use self::vsock::Vsock;
138
} else {
139
compile_error!("Unsupported platform");
140
}
141
}
142
143
use std::cmp;
144
use std::convert::TryFrom;
145
146
use futures::channel::oneshot;
147
use hypervisor::ProtectionType;
148
use serde::Deserialize;
149
use serde::Serialize;
150
use virtio_sys::virtio_config::VIRTIO_F_ACCESS_PLATFORM;
151
use virtio_sys::virtio_config::VIRTIO_F_SUSPEND;
152
use virtio_sys::virtio_config::VIRTIO_F_VERSION_1;
153
use virtio_sys::virtio_ids;
154
use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
155
156
const DEVICE_RESET: u32 = 0x0;
157
158
const INTERRUPT_STATUS_USED_RING: u32 = 0x1;
159
const INTERRUPT_STATUS_CONFIG_CHANGED: u32 = 0x2;
160
161
const VIRTIO_MSI_NO_VECTOR: u16 = 0xffff;
162
163
#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
164
#[serde(rename_all = "kebab-case")]
165
#[repr(u32)]
166
pub enum DeviceType {
167
Net = virtio_ids::VIRTIO_ID_NET,
168
Block = virtio_ids::VIRTIO_ID_BLOCK,
169
Console = virtio_ids::VIRTIO_ID_CONSOLE,
170
Rng = virtio_ids::VIRTIO_ID_RNG,
171
Balloon = virtio_ids::VIRTIO_ID_BALLOON,
172
Scsi = virtio_ids::VIRTIO_ID_SCSI,
173
#[serde(rename = "9p")]
174
P9 = virtio_ids::VIRTIO_ID_9P,
175
Gpu = virtio_ids::VIRTIO_ID_GPU,
176
Input = virtio_ids::VIRTIO_ID_INPUT,
177
Vsock = virtio_ids::VIRTIO_ID_VSOCK,
178
Iommu = virtio_ids::VIRTIO_ID_IOMMU,
179
Sound = virtio_ids::VIRTIO_ID_SOUND,
180
Fs = virtio_ids::VIRTIO_ID_FS,
181
Pmem = virtio_ids::VIRTIO_ID_PMEM,
182
#[serde(rename = "mac80211-hwsim")]
183
Mac80211HwSim = virtio_ids::VIRTIO_ID_MAC80211_HWSIM,
184
VideoEncoder = virtio_ids::VIRTIO_ID_VIDEO_ENCODER,
185
VideoDecoder = virtio_ids::VIRTIO_ID_VIDEO_DECODER,
186
Scmi = virtio_ids::VIRTIO_ID_SCMI,
187
Wl = virtio_ids::VIRTIO_ID_WL,
188
Tpm = virtio_ids::VIRTIO_ID_TPM,
189
Pvclock = virtio_ids::VIRTIO_ID_PVCLOCK,
190
Media = virtio_ids::VIRTIO_ID_MEDIA,
191
}
192
193
impl DeviceType {
194
/// Returns the minimum number of queues that a device of the corresponding type must support.
195
///
196
/// Note that this does not mean a driver must activate these queues, only that they must be
197
/// implemented by a spec-compliant device.
198
pub fn min_queues(&self) -> usize {
199
match self {
200
DeviceType::Net => 3, // rx, tx (TODO: b/314353246: ctrl is optional)
201
DeviceType::Block => 1, // request queue
202
DeviceType::Console => 2, // receiveq, transmitq
203
DeviceType::Rng => 1, // request queue
204
DeviceType::Balloon => 2, // inflateq, deflateq
205
DeviceType::Scsi => 3, // controlq, eventq, request queue
206
DeviceType::P9 => 1, // request queue
207
DeviceType::Gpu => 2, // controlq, cursorq
208
DeviceType::Input => 2, // eventq, statusq
209
DeviceType::Vsock => 3, // rx, tx, event
210
DeviceType::Iommu => 2, // requestq, eventq
211
DeviceType::Sound => 4, // controlq, eventq, txq, rxq
212
DeviceType::Fs => 2, // hiprio, request queue
213
DeviceType::Pmem => 1, // request queue
214
DeviceType::Mac80211HwSim => 2, // tx, rx
215
DeviceType::VideoEncoder => 2, // cmdq, eventq
216
DeviceType::VideoDecoder => 2, // cmdq, eventq
217
DeviceType::Scmi => 2, // cmdq, eventq
218
DeviceType::Wl => 2, // in, out
219
DeviceType::Tpm => 1, // request queue
220
DeviceType::Pvclock => 1, // request queue
221
DeviceType::Media => 2, // commandq, eventq
222
}
223
}
224
}
225
226
/// Prints a string representation of the given virtio device type.
227
impl std::fmt::Display for DeviceType {
228
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
229
match &self {
230
DeviceType::Net => write!(f, "net"),
231
DeviceType::Block => write!(f, "block"),
232
DeviceType::Console => write!(f, "console"),
233
DeviceType::Rng => write!(f, "rng"),
234
DeviceType::Balloon => write!(f, "balloon"),
235
DeviceType::Scsi => write!(f, "scsi"),
236
DeviceType::P9 => write!(f, "9p"),
237
DeviceType::Input => write!(f, "input"),
238
DeviceType::Gpu => write!(f, "gpu"),
239
DeviceType::Vsock => write!(f, "vsock"),
240
DeviceType::Iommu => write!(f, "iommu"),
241
DeviceType::Sound => write!(f, "sound"),
242
DeviceType::Fs => write!(f, "fs"),
243
DeviceType::Pmem => write!(f, "pmem"),
244
DeviceType::Wl => write!(f, "wl"),
245
DeviceType::Tpm => write!(f, "tpm"),
246
DeviceType::Pvclock => write!(f, "pvclock"),
247
DeviceType::VideoDecoder => write!(f, "video-decoder"),
248
DeviceType::VideoEncoder => write!(f, "video-encoder"),
249
DeviceType::Mac80211HwSim => write!(f, "mac80211-hwsim"),
250
DeviceType::Scmi => write!(f, "scmi"),
251
DeviceType::Media => write!(f, "media"),
252
}
253
}
254
}
255
256
/// Copy virtio device configuration data from a subslice of `src` to a subslice of `dst`.
257
/// Unlike std::slice::copy_from_slice(), this function copies as much as possible within
258
/// the common subset of the two slices, truncating the requested range instead of
259
/// panicking if the slices do not match in size.
260
///
261
/// `dst_offset` and `src_offset` specify the starting indexes of the `dst` and `src`
262
/// slices, respectively; if either index is out of bounds, this function is a no-op
263
/// rather than panicking. This makes it safe to call with arbitrary user-controlled
264
/// inputs.
265
pub fn copy_config(dst: &mut [u8], dst_offset: u64, src: &[u8], src_offset: u64) {
266
if let Ok(dst_offset) = usize::try_from(dst_offset) {
267
if let Ok(src_offset) = usize::try_from(src_offset) {
268
if let Some(dst_slice) = dst.get_mut(dst_offset..) {
269
if let Some(src_slice) = src.get(src_offset..) {
270
let len = cmp::min(dst_slice.len(), src_slice.len());
271
let dst_subslice = &mut dst_slice[0..len];
272
let src_subslice = &src_slice[0..len];
273
dst_subslice.copy_from_slice(src_subslice);
274
}
275
}
276
}
277
}
278
}
279
280
/// Returns the set of reserved base features common to all virtio devices.
281
pub fn base_features(protection_type: ProtectionType) -> u64 {
282
let mut features: u64 =
283
1 << VIRTIO_F_VERSION_1 | 1 << VIRTIO_RING_F_EVENT_IDX | 1 << VIRTIO_F_SUSPEND;
284
285
if protection_type != ProtectionType::Unprotected {
286
features |= 1 << VIRTIO_F_ACCESS_PLATFORM;
287
}
288
289
features
290
}
291
292
/// Type of virtio transport.
293
///
294
/// The virtio protocol can be transported by several means, which affects a few things for device
295
/// creation - for instance, the seccomp policy we need to use when jailing the device.
296
pub enum VirtioDeviceType {
297
/// A regular (in-VMM) virtio device.
298
Regular,
299
/// Socket-backed vhost-user device.
300
VhostUser,
301
}
302
303
impl VirtioDeviceType {
304
/// Returns the seccomp policy file that we will want to load for device `base`, depending on
305
/// the virtio transport type.
306
pub fn seccomp_policy_file(&self, base: &str) -> String {
307
match self {
308
VirtioDeviceType::Regular => format!("{base}_device"),
309
VirtioDeviceType::VhostUser => format!("{base}_device_vhost_user"),
310
}
311
}
312
}
313
314
/// Creates a oneshot channel, returning the rx end and adding the tx end to the
315
/// provided `Vec`. Useful for creating oneshots that signal a virtqueue future
316
/// to stop processing and exit.
317
pub(crate) fn create_stop_oneshot(tx_vec: &mut Vec<oneshot::Sender<()>>) -> oneshot::Receiver<()> {
318
let (stop_tx, stop_rx) = futures::channel::oneshot::channel();
319
tx_vec.push(stop_tx);
320
stop_rx
321
}
322
323
/// When we request to stop the worker, this represents the terminal state
324
/// for the thread (if it exists).
325
pub(crate) enum StoppedWorker<Q> {
326
/// Worker stopped successfully & returned its queues.
327
WithQueues(Box<Q>),
328
329
/// Worker wasn't running when the stop was requested.
330
AlreadyStopped,
331
332
/// Worker was running but did not successfully return its queues. Something
333
/// has gone wrong (and will be in the error log). In the case of a device
334
/// reset this is fine since the next activation will replace the queues.
335
MissingQueues,
336
}
337
338