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
7mod 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 = "net")]
17pub mod net;
18#[cfg(feature = "pvclock")]
19pub mod pvclock;
20mod queue;
21mod rng;
22#[cfg(feature = "vtpm")]
23mod tpm;
24#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
25mod video;
26mod virtio_device;
27mod virtio_mmio_device;
28mod virtio_pci_common_config;
29mod virtio_pci_device;
30
31pub mod block;
32pub mod console;
33#[cfg(feature = "gpu")]
34pub mod gpu;
35#[cfg(all(unix, feature = "media"))]
36pub mod media;
37pub mod resource_bridge;
38pub mod scsi;
39#[cfg(feature = "audio")]
40pub mod snd;
41pub mod vhost;
42pub mod vhost_user_backend;
43pub mod vhost_user_frontend;
44pub mod vsock;
45
46#[cfg(feature = "balloon")]
47pub use self::balloon::Balloon;
48#[cfg(feature = "balloon")]
49pub use self::balloon::BalloonFeatures;
50pub use self::block::BlockAsync;
51pub use self::console::Console;
52pub use self::descriptor_chain::DescriptorChain;
53pub use self::descriptor_chain::DescriptorChainIter;
54pub use self::descriptor_utils::create_descriptor_chain;
55pub use self::descriptor_utils::DescriptorType;
56pub use self::descriptor_utils::Reader;
57pub use self::descriptor_utils::Writer;
58#[cfg(feature = "gpu")]
59pub use self::gpu::DisplayBackend;
60#[cfg(feature = "gpu")]
61pub use self::gpu::Gpu;
62#[cfg(feature = "gpu")]
63pub use self::gpu::GpuDisplayMode;
64#[cfg(feature = "gpu")]
65pub use self::gpu::GpuDisplayParameters;
66#[cfg(feature = "gpu")]
67pub use self::gpu::GpuMode;
68#[cfg(feature = "gpu")]
69pub use self::gpu::GpuMouseMode;
70#[cfg(feature = "gpu")]
71pub use self::gpu::GpuParameters;
72#[cfg(feature = "gpu")]
73pub use self::gpu::GpuWsi;
74pub use self::interrupt::Interrupt;
75pub use self::interrupt::InterruptSnapshot;
76pub use self::iommu::ipc_memory_mapper;
77pub use self::iommu::memory_mapper;
78pub use self::iommu::Iommu;
79pub use self::iommu::IommuError;
80#[cfg(feature = "net")]
81pub use self::net::Net;
82#[cfg(feature = "net")]
83pub use self::net::NetError;
84#[cfg(feature = "net")]
85pub use self::net::NetParameters;
86#[cfg(feature = "net")]
87pub use self::net::NetParametersMode;
88pub use self::queue::split_descriptor_chain::Desc;
89pub use self::queue::split_descriptor_chain::SplitDescriptorChain;
90pub use self::queue::PeekedDescriptorChain;
91pub use self::queue::Queue;
92pub use self::queue::QueueConfig;
93pub use self::rng::Rng;
94pub use self::scsi::Controller as ScsiController;
95pub use self::scsi::DiskConfig as ScsiDiskConfig;
96#[cfg(feature = "vtpm")]
97pub use self::tpm::Tpm;
98#[cfg(feature = "vtpm")]
99pub use self::tpm::TpmBackend;
100pub use self::vhost_user_frontend::VhostUserFrontend;
101#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
102pub use self::video::VideoDevice;
103pub use self::virtio_device::SharedMemoryMapper;
104pub use self::virtio_device::SharedMemoryPrepareType;
105pub use self::virtio_device::SharedMemoryRegion;
106pub use self::virtio_device::VirtioDevice;
107pub use self::virtio_mmio_device::VirtioMmioDevice;
108pub use self::virtio_pci_device::PciCapabilityType;
109pub use self::virtio_pci_device::VirtioPciCap;
110pub use self::virtio_pci_device::VirtioPciDevice;
111pub use self::virtio_pci_device::VirtioPciShmCap;
112#[cfg(feature = "pvclock")]
113pub use self::DeviceType::Pvclock;
114
115cfg_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
142use std::cmp;
143use std::convert::TryFrom;
144
145use futures::channel::oneshot;
146use hypervisor::ProtectionType;
147use serde::Deserialize;
148use serde::Serialize;
149use virtio_sys::virtio_config::VIRTIO_F_ACCESS_PLATFORM;
150use virtio_sys::virtio_config::VIRTIO_F_SUSPEND;
151use virtio_sys::virtio_config::VIRTIO_F_VERSION_1;
152use virtio_sys::virtio_ids;
153use virtio_sys::virtio_ring::VIRTIO_RING_F_EVENT_IDX;
154
155const DEVICE_RESET: u32 = 0x0;
156
157const INTERRUPT_STATUS_USED_RING: u32 = 0x1;
158const INTERRUPT_STATUS_CONFIG_CHANGED: u32 = 0x2;
159
160const 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)]
165pub 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
192impl 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.
197    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.
226impl std::fmt::Display for DeviceType {
227    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.
264pub 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.
280pub 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.
295pub enum VirtioDeviceType {
296    /// A regular (in-VMM) virtio device.
297    Regular,
298    /// Socket-backed vhost-user device.
299    VhostUser,
300}
301
302impl VirtioDeviceType {
303    /// Returns the seccomp policy file that we will want to load for device `base`, depending on
304    /// the virtio transport type.
305    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.
316pub(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).
324pub(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}