devices/virtio/
mod.rs

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