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