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