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 use std::collections::BTreeMap;
6 use std::collections::BTreeSet;
7 use std::convert::TryFrom;
8 use std::fs;
9 use std::fs::File;
10 use std::fs::OpenOptions;
11 use std::io::ErrorKind;
12 use std::ops::RangeInclusive;
13 use std::os::unix::fs::FileTypeExt;
14 use std::os::unix::net::UnixStream;
15 use std::path::Path;
16 use std::path::PathBuf;
17 use std::str;
18 use std::sync::Arc;
19 use std::time::Duration;
20 use std::time::Instant;
21
22 use anyhow::anyhow;
23 use anyhow::bail;
24 use anyhow::Context;
25 use anyhow::Result;
26 use arch::VirtioDeviceStub;
27 use base::linux::MemfdSeals;
28 use base::sys::SharedMemoryLinux;
29 use base::ReadNotifier;
30 use base::*;
31 use devices::serial_device::SerialParameters;
32 use devices::serial_device::SerialType;
33 use devices::vfio::VfioContainerManager;
34 use devices::virtio;
35 use devices::virtio::block::DiskOption;
36 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
37 use devices::virtio::device_constants::video::VideoBackendType;
38 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
39 use devices::virtio::device_constants::video::VideoDeviceType;
40 use devices::virtio::ipc_memory_mapper::create_ipc_mapper;
41 use devices::virtio::ipc_memory_mapper::CreateIpcMapperRet;
42 use devices::virtio::memory_mapper::BasicMemoryMapper;
43 use devices::virtio::memory_mapper::MemoryMapperTrait;
44 #[cfg(feature = "pvclock")]
45 use devices::virtio::pvclock::PvClock;
46 use devices::virtio::scsi::ScsiOption;
47 #[cfg(feature = "audio")]
48 use devices::virtio::snd::parameters::Parameters as SndParameters;
49 use devices::virtio::vfio_wrapper::VfioWrapper;
50 #[cfg(feature = "net")]
51 use devices::virtio::vhost::user::NetBackend;
52 use devices::virtio::vhost::user::VhostUserDeviceBuilder;
53 use devices::virtio::vhost::user::VhostUserVsockDevice;
54 use devices::virtio::vsock::VsockConfig;
55 use devices::virtio::Console;
56 use devices::virtio::MemSlotConfig;
57 #[cfg(feature = "net")]
58 use devices::virtio::NetError;
59 #[cfg(feature = "net")]
60 use devices::virtio::NetParameters;
61 #[cfg(feature = "net")]
62 use devices::virtio::NetParametersMode;
63 use devices::virtio::PmemConfig;
64 use devices::virtio::VhostUserFrontend;
65 use devices::virtio::VirtioDevice;
66 use devices::virtio::VirtioDeviceType;
67 use devices::BusDeviceObj;
68 use devices::IommuDevType;
69 use devices::PciAddress;
70 use devices::PciDevice;
71 use devices::VfioDevice;
72 use devices::VfioDeviceType;
73 use devices::VfioPciDevice;
74 use devices::VfioPlatformDevice;
75 #[cfg(feature = "vtpm")]
76 use devices::VtpmProxy;
77 use hypervisor::MemCacheType;
78 use hypervisor::ProtectionType;
79 use hypervisor::Vm;
80 use jail::*;
81 use minijail::Minijail;
82 #[cfg(feature = "net")]
83 use net_util::sys::linux::Tap;
84 #[cfg(feature = "net")]
85 use net_util::MacAddress;
86 #[cfg(feature = "net")]
87 use net_util::TapTCommon;
88 use resources::Alloc;
89 use resources::AllocOptions;
90 use resources::SystemAllocator;
91 use sync::Mutex;
92 use vm_control::api::VmMemoryClient;
93 use vm_memory::GuestAddress;
94
95 use crate::crosvm::config::PmemOption;
96 use crate::crosvm::config::VhostUserFrontendOption;
97 use crate::crosvm::config::VhostUserFsOption;
98 use crate::crosvm::sys::config::PmemExt2Option;
99
100 /// All the tube types collected and passed to `run_control`.
101 ///
102 /// This mainly exists to simplify the device setup plumbing. We collect the tubes of all the
103 /// devices into one list using this enum and then separate them out in `run_control` to be handled
104 /// individually.
105 #[remain::sorted]
106 pub enum AnyControlTube {
107 DeviceControlTube(DeviceControlTube),
108 /// Receives `IrqHandlerRequest`.
109 IrqTube(Tube),
110 TaggedControlTube(TaggedControlTube),
111 VmMemoryTube(VmMemoryTube),
112 }
113
114 impl From<DeviceControlTube> for AnyControlTube {
from(value: DeviceControlTube) -> Self115 fn from(value: DeviceControlTube) -> Self {
116 AnyControlTube::DeviceControlTube(value)
117 }
118 }
119
120 impl From<TaggedControlTube> for AnyControlTube {
from(value: TaggedControlTube) -> Self121 fn from(value: TaggedControlTube) -> Self {
122 AnyControlTube::TaggedControlTube(value)
123 }
124 }
125
126 impl From<VmMemoryTube> for AnyControlTube {
from(value: VmMemoryTube) -> Self127 fn from(value: VmMemoryTube) -> Self {
128 AnyControlTube::VmMemoryTube(value)
129 }
130 }
131
132 /// Tubes that initiate requests to devices.
133 #[remain::sorted]
134 pub enum DeviceControlTube {
135 // See `BalloonTube`.
136 #[cfg(feature = "balloon")]
137 Balloon(Tube),
138 // Sends `DiskControlCommand`.
139 Disk(Tube),
140 // Sends `GpuControlCommand`.
141 #[cfg(feature = "gpu")]
142 Gpu(Tube),
143 // Sends `PvClockCommand`.
144 #[cfg(feature = "pvclock")]
145 PvClock(Tube),
146 #[cfg(feature = "audio")]
147 Snd(Tube),
148 }
149
150 /// Tubes that service requests from devices.
151 ///
152 /// Only includes those that happen to be handled together in the main `WaitContext` loop.
153 pub enum TaggedControlTube {
154 /// Receives `FsMappingRequest`.
155 Fs(Tube),
156 /// Receives `VmRequest`.
157 Vm(Tube),
158 /// Receives `VmMemoryMappingRequest`.
159 VmMsync(Tube),
160 }
161
162 impl AsRef<Tube> for TaggedControlTube {
as_ref(&self) -> &Tube163 fn as_ref(&self) -> &Tube {
164 use self::TaggedControlTube::*;
165 match &self {
166 Fs(tube) | Vm(tube) | VmMsync(tube) => tube,
167 }
168 }
169 }
170
171 impl AsRawDescriptor for TaggedControlTube {
as_raw_descriptor(&self) -> RawDescriptor172 fn as_raw_descriptor(&self) -> RawDescriptor {
173 self.as_ref().as_raw_descriptor()
174 }
175 }
176
177 impl ReadNotifier for TaggedControlTube {
get_read_notifier(&self) -> &dyn AsRawDescriptor178 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
179 self.as_ref().get_read_notifier()
180 }
181 }
182
183 /// Tubes that service `VmMemoryRequest` requests from devices.
184 #[derive(serde::Serialize, serde::Deserialize)]
185 pub struct VmMemoryTube {
186 pub tube: Tube,
187 /// See devices::virtio::VirtioDevice.expose_shared_memory_region_with_viommu
188 pub expose_with_viommu: bool,
189 }
190
191 impl AsRef<Tube> for VmMemoryTube {
as_ref(&self) -> &Tube192 fn as_ref(&self) -> &Tube {
193 &self.tube
194 }
195 }
196
197 impl AsRawDescriptor for VmMemoryTube {
as_raw_descriptor(&self) -> RawDescriptor198 fn as_raw_descriptor(&self) -> RawDescriptor {
199 self.as_ref().as_raw_descriptor()
200 }
201 }
202
203 impl ReadNotifier for VmMemoryTube {
get_read_notifier(&self) -> &dyn AsRawDescriptor204 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
205 self.as_ref().get_read_notifier()
206 }
207 }
208
209 pub trait IntoUnixStream {
into_unix_stream(self) -> Result<UnixStream>210 fn into_unix_stream(self) -> Result<UnixStream>;
211 }
212
213 impl IntoUnixStream for &Path {
into_unix_stream(self) -> Result<UnixStream>214 fn into_unix_stream(self) -> Result<UnixStream> {
215 if let Some(fd) = safe_descriptor_from_path(self)
216 .with_context(|| format!("failed to open event device '{}'", self.display()))?
217 {
218 Ok(fd.into())
219 } else {
220 UnixStream::connect(self)
221 .with_context(|| format!("failed to open event device '{}'", self.display()))
222 }
223 }
224 }
225
226 impl IntoUnixStream for &PathBuf {
into_unix_stream(self) -> Result<UnixStream>227 fn into_unix_stream(self) -> Result<UnixStream> {
228 self.as_path().into_unix_stream()
229 }
230 }
231
232 impl IntoUnixStream for UnixStream {
into_unix_stream(self) -> Result<UnixStream>233 fn into_unix_stream(self) -> Result<UnixStream> {
234 Ok(self)
235 }
236 }
237
238 pub type DeviceResult<T = VirtioDeviceStub> = Result<T>;
239
240 /// A trait for spawning virtio device instances and jails from their configuration structure.
241 ///
242 /// Implementors become able to create virtio devices and jails following their own configuration.
243 /// This trait also provides a few convenience methods for e.g. creating a virtio device and jail
244 /// at once.
245 pub trait VirtioDeviceBuilder: Sized {
246 /// Base name of the device, as it will appear in logs.
247 const NAME: &'static str;
248
249 /// Create a regular virtio device from the configuration and `protection_type` setting.
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>250 fn create_virtio_device(
251 self,
252 protection_type: ProtectionType,
253 ) -> anyhow::Result<Box<dyn VirtioDevice>>;
254
255 /// Create a device suitable for being run as a vhost-user instance.
256 ///
257 /// It is ok to leave this method unimplemented if the device is not intended to be used with
258 /// vhost-user.
create_vhost_user_device( self, _keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>259 fn create_vhost_user_device(
260 self,
261 _keep_rds: &mut Vec<RawDescriptor>,
262 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
263 unimplemented!()
264 }
265
266 /// Create a jail that is suitable to run a device.
267 ///
268 /// The default implementation creates a simple jail with a seccomp policy derived from the
269 /// base name of the device.
create_jail( &self, jail_config: Option<&JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>270 fn create_jail(
271 &self,
272 jail_config: Option<&JailConfig>,
273 virtio_transport: VirtioDeviceType,
274 ) -> anyhow::Result<Option<Minijail>> {
275 simple_jail(
276 jail_config,
277 &virtio_transport.seccomp_policy_file(Self::NAME),
278 )
279 }
280
281 /// Helper method to return a `VirtioDeviceStub` filled using `create_virtio_device` and
282 /// `create_jail`.
283 ///
284 /// This helper should cover the needs of most devices when run as regular virtio devices.
create_virtio_device_and_jail( self, protection_type: ProtectionType, jail_config: Option<&JailConfig>, ) -> DeviceResult285 fn create_virtio_device_and_jail(
286 self,
287 protection_type: ProtectionType,
288 jail_config: Option<&JailConfig>,
289 ) -> DeviceResult {
290 let jail = self.create_jail(jail_config, VirtioDeviceType::Regular)?;
291 let dev = self.create_virtio_device(protection_type)?;
292 Ok(VirtioDeviceStub { dev, jail })
293 }
294 }
295
296 /// A one-shot configuration structure for implementing `VirtioDeviceBuilder`. We cannot do it on
297 /// `DiskOption` directly because disk devices can be passed an optional control tube.
298 pub struct DiskConfig<'a> {
299 /// Options for disk creation.
300 disk: &'a DiskOption,
301 /// Optional control tube for the device.
302 device_tube: Option<Tube>,
303 }
304
305 impl<'a> DiskConfig<'a> {
new(disk: &'a DiskOption, device_tube: Option<Tube>) -> Self306 pub fn new(disk: &'a DiskOption, device_tube: Option<Tube>) -> Self {
307 Self { disk, device_tube }
308 }
309 }
310
311 impl VirtioDeviceBuilder for DiskConfig<'_> {
312 const NAME: &'static str = "block";
313
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>314 fn create_virtio_device(
315 self,
316 protection_type: ProtectionType,
317 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
318 info!(
319 "Trying to attach block device: {}",
320 self.disk.path.display(),
321 );
322 let disk_image = self.disk.open()?;
323 let base_features = virtio::base_features(protection_type);
324 Ok(Box::new(
325 virtio::BlockAsync::new(
326 base_features,
327 disk_image,
328 self.disk,
329 self.device_tube,
330 None,
331 None,
332 )
333 .context("failed to create block device")?,
334 ))
335 }
336
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>337 fn create_vhost_user_device(
338 self,
339 keep_rds: &mut Vec<RawDescriptor>,
340 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
341 let disk = self.disk;
342 let disk_image = disk.open()?;
343 let base_features = virtio::base_features(ProtectionType::Unprotected);
344
345 let block = Box::new(
346 virtio::BlockAsync::new(
347 base_features,
348 disk_image,
349 disk,
350 self.device_tube,
351 None,
352 None,
353 )
354 .context("failed to create block device")?,
355 );
356 keep_rds.extend(block.keep_rds());
357
358 Ok(block)
359 }
360 }
361
362 pub struct ScsiConfig<'a>(pub &'a [ScsiOption]);
363
364 impl<'a> VirtioDeviceBuilder for &'a ScsiConfig<'a> {
365 const NAME: &'static str = "scsi";
366
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>367 fn create_virtio_device(
368 self,
369 protection_type: ProtectionType,
370 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
371 let base_features = virtio::base_features(protection_type);
372 let disks = self
373 .0
374 .iter()
375 .map(|op| {
376 info!("Trying to attach a scsi device: {}", op.path.display());
377 let file = op.open()?;
378 Ok(virtio::ScsiDiskConfig {
379 file,
380 block_size: op.block_size,
381 read_only: op.read_only,
382 })
383 })
384 .collect::<anyhow::Result<_>>()?;
385 let controller = virtio::ScsiController::new(base_features, disks)
386 .context("failed to create a scsi controller")?;
387 Ok(Box::new(controller))
388 }
389 }
390
vhost_user_connection( path: &Path, connect_timeout_ms: Option<u64>, ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>>391 fn vhost_user_connection(
392 path: &Path,
393 connect_timeout_ms: Option<u64>,
394 ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>> {
395 let deadline = connect_timeout_ms.map(|t| Instant::now() + Duration::from_millis(t));
396 let mut first = true;
397 loop {
398 match UnixStream::connect(path) {
399 Ok(sock) => {
400 let connection = sock
401 .try_into()
402 .context("failed to construct Connection from UnixStream")?;
403 return Ok(connection);
404 }
405 Err(e) => {
406 // ConnectionRefused => Might be a stale file the backend hasn't deleted yet.
407 // NotFound => Might be the backend hasn't bound the socket yet.
408 if e.kind() == ErrorKind::ConnectionRefused || e.kind() == ErrorKind::NotFound {
409 if let Some(deadline) = deadline {
410 if first {
411 first = false;
412 warn!(
413 "vhost-user socket path {} not available. retrying up to {} ms",
414 path.display(),
415 connect_timeout_ms.unwrap()
416 );
417 }
418 if Instant::now() > deadline {
419 anyhow::bail!(
420 "timeout waiting for vhost-user socket path {}: final error: {e:#}",
421 path.display()
422 );
423 }
424 std::thread::sleep(Duration::from_millis(1));
425 continue;
426 }
427 }
428 return Err(e).with_context(|| {
429 format!(
430 "failed to connect to vhost-user socket path {}",
431 path.display()
432 )
433 });
434 }
435 }
436 }
437 }
438
is_socket(path: &PathBuf) -> bool439 fn is_socket(path: &PathBuf) -> bool {
440 match fs::metadata(path) {
441 Ok(metadata) => metadata.file_type().is_socket(),
442 Err(_) => false, // Assume not a socket if we can't get metadata
443 }
444 }
445
vhost_user_connection_from_socket_fd( fd: u32, ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>>446 fn vhost_user_connection_from_socket_fd(
447 fd: u32,
448 ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>> {
449 let path = PathBuf::from(format!("/proc/self/fd/{}", fd));
450 if !is_socket(&path) {
451 anyhow::bail!("path {} is not socket", path.display());
452 }
453
454 let safe_fd = safe_descriptor_from_cmdline_fd(&(fd as i32))?;
455
456 safe_fd
457 .try_into()
458 .context("failed to create vhost-user connection from fd")
459 }
460
create_vhost_user_frontend( protection_type: ProtectionType, opt: &VhostUserFrontendOption, connect_timeout_ms: Option<u64>, ) -> DeviceResult461 pub fn create_vhost_user_frontend(
462 protection_type: ProtectionType,
463 opt: &VhostUserFrontendOption,
464 connect_timeout_ms: Option<u64>,
465 ) -> DeviceResult {
466 let connection = if let Some(socket_fd) = safe_descriptor_from_path(&opt.socket)? {
467 socket_fd
468 .try_into()
469 .context("failed to create vhost-user connection from fd")?
470 } else {
471 vhost_user_connection(&opt.socket, connect_timeout_ms)?
472 };
473 let dev = VhostUserFrontend::new(
474 opt.type_,
475 virtio::base_features(protection_type),
476 connection,
477 opt.max_queue_size,
478 opt.pci_address,
479 )
480 .context("failed to set up vhost-user frontend")?;
481
482 Ok(VirtioDeviceStub {
483 dev: Box::new(dev),
484 // no sandbox here because virtqueue handling is exported to a different process.
485 jail: None,
486 })
487 }
488
create_vhost_user_fs_device( protection_type: ProtectionType, option: &VhostUserFsOption, ) -> DeviceResult489 pub fn create_vhost_user_fs_device(
490 protection_type: ProtectionType,
491 option: &VhostUserFsOption,
492 ) -> DeviceResult {
493 let connection = match (&option.socket_path, option.socket_fd) {
494 (Some(socket), None) => vhost_user_connection(socket, None)?,
495 (None, Some(fd)) => vhost_user_connection_from_socket_fd(fd)?,
496 (Some(_), Some(_)) => bail!("Cannot specify both a UDS path and a file descriptor"),
497 (None, None) => bail!("Must specify either a socket or a file descriptor"),
498 };
499 let dev = VhostUserFrontend::new_fs(
500 virtio::base_features(protection_type),
501 connection,
502 option.max_queue_size,
503 option.tag.as_deref(),
504 )
505 .context("failed to set up vhost-user fs device")?;
506
507 Ok(VirtioDeviceStub {
508 dev: Box::new(dev),
509 // no sandbox here because virtqueue handling is exported to a different process.
510 jail: None,
511 })
512 }
513
create_rng_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, ) -> DeviceResult514 pub fn create_rng_device(
515 protection_type: ProtectionType,
516 jail_config: Option<&JailConfig>,
517 ) -> DeviceResult {
518 let dev =
519 virtio::Rng::new(virtio::base_features(protection_type)).context("failed to set up rng")?;
520
521 Ok(VirtioDeviceStub {
522 dev: Box::new(dev),
523 jail: simple_jail(jail_config, "rng_device")?,
524 })
525 }
526
527 #[cfg(feature = "audio")]
create_virtio_snd_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, snd_params: SndParameters, snd_device_tube: Tube, ) -> DeviceResult528 pub fn create_virtio_snd_device(
529 protection_type: ProtectionType,
530 jail_config: Option<&JailConfig>,
531 snd_params: SndParameters,
532 snd_device_tube: Tube,
533 ) -> DeviceResult {
534 let backend = snd_params.backend;
535 let dev = virtio::snd::common_backend::VirtioSnd::new(
536 virtio::base_features(protection_type),
537 snd_params,
538 snd_device_tube,
539 )
540 .context("failed to create cras sound device")?;
541
542 use virtio::snd::parameters::StreamSourceBackend as Backend;
543
544 let policy = match backend {
545 Backend::NULL | Backend::FILE => "snd_null_device",
546 #[cfg(feature = "audio_aaudio")]
547 Backend::Sys(virtio::snd::sys::StreamSourceBackend::AAUDIO) => "snd_aaudio_device",
548 #[cfg(feature = "audio_cras")]
549 Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) => "snd_cras_device",
550 #[cfg(not(any(feature = "audio_cras", feature = "audio_aaudio")))]
551 _ => unreachable!(),
552 };
553
554 let jail = if let Some(jail_config) = jail_config {
555 let mut config = SandboxConfig::new(jail_config, policy);
556 #[cfg(feature = "audio_cras")]
557 if backend == Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) {
558 config.bind_mounts = true;
559 }
560 // TODO(b/267574679): running as current_user may not be required for snd device.
561 config.run_as = RunAsUser::CurrentUser;
562 #[allow(unused_mut)]
563 let mut jail =
564 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
565 #[cfg(feature = "audio_cras")]
566 if backend == Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) {
567 let run_cras_path = Path::new("/run/cras");
568 jail.mount_bind(run_cras_path, run_cras_path, true)?;
569 }
570 Some(jail)
571 } else {
572 None
573 };
574
575 Ok(VirtioDeviceStub {
576 dev: Box::new(dev),
577 jail,
578 })
579 }
580
581 #[cfg(feature = "vtpm")]
create_vtpm_proxy_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, ) -> DeviceResult582 pub fn create_vtpm_proxy_device(
583 protection_type: ProtectionType,
584 jail_config: Option<&JailConfig>,
585 ) -> DeviceResult {
586 let jail = if let Some(jail_config) = jail_config {
587 let mut config = SandboxConfig::new(jail_config, "vtpm_proxy_device");
588 config.bind_mounts = true;
589 let mut jail =
590 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
591 let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
592 jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
593 Some(jail)
594 } else {
595 None
596 };
597
598 let backend = VtpmProxy::new();
599 let dev = virtio::Tpm::new(Box::new(backend), virtio::base_features(protection_type));
600
601 Ok(VirtioDeviceStub {
602 dev: Box::new(dev),
603 jail,
604 })
605 }
606
create_single_touch_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, single_touch_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult607 pub fn create_single_touch_device<T: IntoUnixStream>(
608 protection_type: ProtectionType,
609 jail_config: Option<&JailConfig>,
610 single_touch_socket: T,
611 width: u32,
612 height: u32,
613 name: Option<&str>,
614 idx: u32,
615 ) -> DeviceResult {
616 let socket = single_touch_socket
617 .into_unix_stream()
618 .context("failed configuring virtio single touch")?;
619
620 let dev = virtio::input::new_single_touch(
621 idx,
622 socket,
623 width,
624 height,
625 name,
626 virtio::base_features(protection_type),
627 )
628 .context("failed to set up input device")?;
629 Ok(VirtioDeviceStub {
630 dev: Box::new(dev),
631 jail: simple_jail(jail_config, "input_device")?,
632 })
633 }
634
create_multi_touch_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, multi_touch_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult635 pub fn create_multi_touch_device<T: IntoUnixStream>(
636 protection_type: ProtectionType,
637 jail_config: Option<&JailConfig>,
638 multi_touch_socket: T,
639 width: u32,
640 height: u32,
641 name: Option<&str>,
642 idx: u32,
643 ) -> DeviceResult {
644 let socket = multi_touch_socket
645 .into_unix_stream()
646 .context("failed configuring virtio multi touch")?;
647
648 let dev = virtio::input::new_multi_touch(
649 idx,
650 socket,
651 width,
652 height,
653 name,
654 virtio::base_features(protection_type),
655 )
656 .context("failed to set up input device")?;
657
658 Ok(VirtioDeviceStub {
659 dev: Box::new(dev),
660 jail: simple_jail(jail_config, "input_device")?,
661 })
662 }
663
create_trackpad_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, trackpad_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult664 pub fn create_trackpad_device<T: IntoUnixStream>(
665 protection_type: ProtectionType,
666 jail_config: Option<&JailConfig>,
667 trackpad_socket: T,
668 width: u32,
669 height: u32,
670 name: Option<&str>,
671 idx: u32,
672 ) -> DeviceResult {
673 let socket = trackpad_socket
674 .into_unix_stream()
675 .context("failed configuring virtio trackpad")?;
676
677 let dev = virtio::input::new_trackpad(
678 idx,
679 socket,
680 width,
681 height,
682 name,
683 virtio::base_features(protection_type),
684 )
685 .context("failed to set up input device")?;
686
687 Ok(VirtioDeviceStub {
688 dev: Box::new(dev),
689 jail: simple_jail(jail_config, "input_device")?,
690 })
691 }
692
create_multitouch_trackpad_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, trackpad_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult693 pub fn create_multitouch_trackpad_device<T: IntoUnixStream>(
694 protection_type: ProtectionType,
695 jail_config: Option<&JailConfig>,
696 trackpad_socket: T,
697 width: u32,
698 height: u32,
699 name: Option<&str>,
700 idx: u32,
701 ) -> DeviceResult {
702 let socket = trackpad_socket
703 .into_unix_stream()
704 .context("failed configuring virtio trackpad")?;
705
706 let dev = virtio::input::new_multitouch_trackpad(
707 idx,
708 socket,
709 width,
710 height,
711 name,
712 virtio::base_features(protection_type),
713 )
714 .context("failed to set up input device")?;
715
716 Ok(VirtioDeviceStub {
717 dev: Box::new(dev),
718 jail: simple_jail(jail_config, "input_device")?,
719 })
720 }
721
create_mouse_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, mouse_socket: T, idx: u32, ) -> DeviceResult722 pub fn create_mouse_device<T: IntoUnixStream>(
723 protection_type: ProtectionType,
724 jail_config: Option<&JailConfig>,
725 mouse_socket: T,
726 idx: u32,
727 ) -> DeviceResult {
728 let socket = mouse_socket
729 .into_unix_stream()
730 .context("failed configuring virtio mouse")?;
731
732 let dev = virtio::input::new_mouse(idx, socket, virtio::base_features(protection_type))
733 .context("failed to set up input device")?;
734
735 Ok(VirtioDeviceStub {
736 dev: Box::new(dev),
737 jail: simple_jail(jail_config, "input_device")?,
738 })
739 }
740
create_keyboard_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, keyboard_socket: T, idx: u32, ) -> DeviceResult741 pub fn create_keyboard_device<T: IntoUnixStream>(
742 protection_type: ProtectionType,
743 jail_config: Option<&JailConfig>,
744 keyboard_socket: T,
745 idx: u32,
746 ) -> DeviceResult {
747 let socket = keyboard_socket
748 .into_unix_stream()
749 .context("failed configuring virtio keyboard")?;
750
751 let dev = virtio::input::new_keyboard(idx, socket, virtio::base_features(protection_type))
752 .context("failed to set up input device")?;
753
754 Ok(VirtioDeviceStub {
755 dev: Box::new(dev),
756 jail: simple_jail(jail_config, "input_device")?,
757 })
758 }
759
create_switches_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, switches_socket: T, idx: u32, ) -> DeviceResult760 pub fn create_switches_device<T: IntoUnixStream>(
761 protection_type: ProtectionType,
762 jail_config: Option<&JailConfig>,
763 switches_socket: T,
764 idx: u32,
765 ) -> DeviceResult {
766 let socket = switches_socket
767 .into_unix_stream()
768 .context("failed configuring virtio switches")?;
769
770 let dev = virtio::input::new_switches(idx, socket, virtio::base_features(protection_type))
771 .context("failed to set up input device")?;
772
773 Ok(VirtioDeviceStub {
774 dev: Box::new(dev),
775 jail: simple_jail(jail_config, "input_device")?,
776 })
777 }
778
create_rotary_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, rotary_socket: T, idx: u32, ) -> DeviceResult779 pub fn create_rotary_device<T: IntoUnixStream>(
780 protection_type: ProtectionType,
781 jail_config: Option<&JailConfig>,
782 rotary_socket: T,
783 idx: u32,
784 ) -> DeviceResult {
785 let socket = rotary_socket
786 .into_unix_stream()
787 .context("failed configuring virtio rotary")?;
788
789 let dev = virtio::input::new_rotary(idx, socket, virtio::base_features(protection_type))
790 .context("failed to set up input device")?;
791
792 Ok(VirtioDeviceStub {
793 dev: Box::new(dev),
794 jail: simple_jail(jail_config, "input_device")?,
795 })
796 }
797
create_vinput_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, dev_path: &Path, ) -> DeviceResult798 pub fn create_vinput_device(
799 protection_type: ProtectionType,
800 jail_config: Option<&JailConfig>,
801 dev_path: &Path,
802 ) -> DeviceResult {
803 let dev_file = OpenOptions::new()
804 .read(true)
805 .write(true)
806 .open(dev_path)
807 .with_context(|| format!("failed to open vinput device {}", dev_path.display()))?;
808
809 let dev = virtio::input::new_evdev(dev_file, virtio::base_features(protection_type))
810 .context("failed to set up input device")?;
811
812 Ok(VirtioDeviceStub {
813 dev: Box::new(dev),
814 jail: simple_jail(jail_config, "input_device")?,
815 })
816 }
817
create_custom_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: Option<&JailConfig>, custom_device_socket: T, idx: u32, input_config_path: PathBuf, ) -> DeviceResult818 pub fn create_custom_device<T: IntoUnixStream>(
819 protection_type: ProtectionType,
820 jail_config: Option<&JailConfig>,
821 custom_device_socket: T,
822 idx: u32,
823 input_config_path: PathBuf,
824 ) -> DeviceResult {
825 let socket = custom_device_socket
826 .into_unix_stream()
827 .context("failed configuring custom virtio input device")?;
828
829 let dev = virtio::input::new_custom(
830 idx,
831 socket,
832 input_config_path,
833 virtio::base_features(protection_type),
834 )
835 .context("failed to set up input device")?;
836
837 Ok(VirtioDeviceStub {
838 dev: Box::new(dev),
839 jail: simple_jail(jail_config, "input_device")?,
840 })
841 }
842
843 #[cfg(feature = "balloon")]
create_balloon_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, tube: Tube, inflate_tube: Option<Tube>, init_balloon_size: u64, vm_memory_client: VmMemoryClient, enabled_features: u64, #[cfg(feature = "registered_events")] registered_evt_q: Option<SendTube>, ws_num_bins: u8, ) -> DeviceResult844 pub fn create_balloon_device(
845 protection_type: ProtectionType,
846 jail_config: Option<&JailConfig>,
847 tube: Tube,
848 inflate_tube: Option<Tube>,
849 init_balloon_size: u64,
850 vm_memory_client: VmMemoryClient,
851 enabled_features: u64,
852 #[cfg(feature = "registered_events")] registered_evt_q: Option<SendTube>,
853 ws_num_bins: u8,
854 ) -> DeviceResult {
855 let dev = virtio::Balloon::new(
856 virtio::base_features(protection_type),
857 tube,
858 vm_memory_client,
859 inflate_tube,
860 init_balloon_size,
861 enabled_features,
862 #[cfg(feature = "registered_events")]
863 registered_evt_q,
864 ws_num_bins,
865 )
866 .context("failed to create balloon")?;
867
868 Ok(VirtioDeviceStub {
869 dev: Box::new(dev),
870 jail: simple_jail(jail_config, "balloon_device")?,
871 })
872 }
873
874 #[cfg(feature = "pvclock")]
create_pvclock_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, tsc_frequency: u64, suspend_tube: Tube, ) -> DeviceResult875 pub fn create_pvclock_device(
876 protection_type: ProtectionType,
877 jail_config: Option<&JailConfig>,
878 tsc_frequency: u64,
879 suspend_tube: Tube,
880 ) -> DeviceResult {
881 let dev = PvClock::new(
882 virtio::base_features(protection_type),
883 tsc_frequency,
884 suspend_tube,
885 );
886
887 Ok(VirtioDeviceStub {
888 dev: Box::new(dev),
889 jail: simple_jail(jail_config, "pvclock_device")?,
890 })
891 }
892
893 #[cfg(feature = "net")]
894 impl VirtioDeviceBuilder for &NetParameters {
895 const NAME: &'static str = "net";
896
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>897 fn create_virtio_device(
898 self,
899 protection_type: ProtectionType,
900 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
901 let vq_pairs = self.vq_pairs.unwrap_or(1);
902 let multi_vq = vq_pairs > 1 && self.vhost_net.is_none();
903
904 let features = virtio::base_features(protection_type);
905 let (tap, mac) = create_tap_for_net_device(&self.mode, multi_vq)?;
906
907 Ok(if let Some(vhost_net) = &self.vhost_net {
908 Box::new(
909 virtio::vhost::Net::<_, vhost::Net<_>>::new(
910 &vhost_net.device,
911 features,
912 tap,
913 mac,
914 self.packed_queue,
915 self.pci_address,
916 )
917 .context("failed to set up virtio-vhost networking")?,
918 ) as Box<dyn VirtioDevice>
919 } else {
920 Box::new(
921 virtio::Net::new(
922 features,
923 tap,
924 vq_pairs,
925 mac,
926 self.packed_queue,
927 self.pci_address,
928 )
929 .context("failed to set up virtio networking")?,
930 ) as Box<dyn VirtioDevice>
931 })
932 }
933
create_jail( &self, jail_config: Option<&JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>934 fn create_jail(
935 &self,
936 jail_config: Option<&JailConfig>,
937 virtio_transport: VirtioDeviceType,
938 ) -> anyhow::Result<Option<Minijail>> {
939 let policy = if self.vhost_net.is_some() {
940 "vhost_net"
941 } else {
942 "net"
943 };
944
945 simple_jail(jail_config, &virtio_transport.seccomp_policy_file(policy))
946 }
947
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>948 fn create_vhost_user_device(
949 self,
950 keep_rds: &mut Vec<RawDescriptor>,
951 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
952 let vq_pairs = self.vq_pairs.unwrap_or(1);
953 let multi_vq = vq_pairs > 1 && self.vhost_net.is_none();
954 let (tap, _mac) = create_tap_for_net_device(&self.mode, multi_vq)?;
955
956 let backend = NetBackend::new(tap)?;
957
958 keep_rds.extend(backend.as_raw_descriptors());
959
960 Ok(Box::new(backend))
961 }
962 }
963
964 /// Create a new tap interface based on NetParametersMode.
965 #[cfg(feature = "net")]
create_tap_for_net_device( mode: &NetParametersMode, multi_vq: bool, ) -> DeviceResult<(Tap, Option<MacAddress>)>966 fn create_tap_for_net_device(
967 mode: &NetParametersMode,
968 multi_vq: bool,
969 ) -> DeviceResult<(Tap, Option<MacAddress>)> {
970 match mode {
971 NetParametersMode::TapName { tap_name, mac } => {
972 let tap = Tap::new_with_name(tap_name.as_bytes(), true, multi_vq)
973 .map_err(NetError::TapOpen)?;
974 Ok((tap, *mac))
975 }
976 NetParametersMode::TapFd { tap_fd, mac } => {
977 // SAFETY:
978 // Safe because we ensure that we get a unique handle to the fd.
979 let tap = unsafe {
980 Tap::from_raw_descriptor(
981 validate_raw_descriptor(*tap_fd)
982 .context("failed to validate tap descriptor")?,
983 )
984 .context("failed to create tap device")?
985 };
986 Ok((tap, *mac))
987 }
988 NetParametersMode::RawConfig {
989 host_ip,
990 netmask,
991 mac,
992 } => {
993 let tap = Tap::new(true, multi_vq).map_err(NetError::TapOpen)?;
994 tap.set_ip_addr(*host_ip).map_err(NetError::TapSetIp)?;
995 tap.set_netmask(*netmask).map_err(NetError::TapSetNetmask)?;
996 tap.set_mac_address(*mac)
997 .map_err(NetError::TapSetMacAddress)?;
998 tap.enable().map_err(NetError::TapEnable)?;
999 Ok((tap, None))
1000 }
1001 }
1002 }
1003
create_wayland_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, wayland_socket_paths: &BTreeMap<String, PathBuf>, resource_bridge: Option<Tube>, ) -> DeviceResult1004 pub fn create_wayland_device(
1005 protection_type: ProtectionType,
1006 jail_config: Option<&JailConfig>,
1007 wayland_socket_paths: &BTreeMap<String, PathBuf>,
1008 resource_bridge: Option<Tube>,
1009 ) -> DeviceResult {
1010 let wayland_socket_dirs = wayland_socket_paths
1011 .iter()
1012 .map(|(_name, path)| path.parent())
1013 .collect::<Option<Vec<_>>>()
1014 .ok_or_else(|| anyhow!("wayland socket path has no parent or file name"))?;
1015
1016 let features = virtio::base_features(protection_type);
1017 let dev = virtio::Wl::new(features, wayland_socket_paths.clone(), resource_bridge)
1018 .context("failed to create wayland device")?;
1019
1020 let jail = if let Some(jail_config) = jail_config {
1021 let mut config = SandboxConfig::new(jail_config, "wl_device");
1022 config.bind_mounts = true;
1023 let mut jail = create_gpu_minijail(
1024 &jail_config.pivot_root,
1025 &config,
1026 /* render_node_only= */ false,
1027 /* snapshot_scratch_path= */ None,
1028 )?;
1029 // Bind mount the wayland socket's directory into jail's root. This is necessary since
1030 // each new wayland context must open() the socket. If the wayland socket is ever
1031 // destroyed and remade in the same host directory, new connections will be possible
1032 // without restarting the wayland device.
1033 for dir in &wayland_socket_dirs {
1034 jail.mount(dir, dir, "", (libc::MS_BIND | libc::MS_REC) as usize)?;
1035 }
1036
1037 Some(jail)
1038 } else {
1039 None
1040 };
1041
1042 Ok(VirtioDeviceStub {
1043 dev: Box::new(dev),
1044 jail,
1045 })
1046 }
1047
1048 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
create_video_device_jail( backend: VideoBackendType, jail_config: &JailConfig, typ: VideoDeviceType, ) -> Result<Minijail>1049 fn create_video_device_jail(
1050 backend: VideoBackendType,
1051 jail_config: &JailConfig,
1052 typ: VideoDeviceType,
1053 ) -> Result<Minijail> {
1054 match typ {
1055 #[cfg(feature = "video-decoder")]
1056 VideoDeviceType::Decoder => {}
1057 #[cfg(feature = "video-encoder")]
1058 VideoDeviceType::Encoder => {}
1059 #[cfg(any(not(feature = "video-decoder"), not(feature = "video-encoder")))]
1060 // `typ` is always a VideoDeviceType enabled
1061 device_type => unreachable!("Not compiled with {:?} enabled", device_type),
1062 };
1063 let mut config = SandboxConfig::new(jail_config, "video_device");
1064 config.bind_mounts = true;
1065 let mut jail =
1066 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
1067
1068 let need_drm_device = match backend {
1069 #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
1070 VideoBackendType::Libvda => true,
1071 #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
1072 VideoBackendType::LibvdaVd => true,
1073 #[cfg(feature = "vaapi")]
1074 VideoBackendType::Vaapi => true,
1075 #[cfg(feature = "ffmpeg")]
1076 VideoBackendType::Ffmpeg => false,
1077 };
1078
1079 if need_drm_device {
1080 jail_mount_bind_drm(&mut jail, /* render_node_only= */ true)?;
1081 }
1082
1083 #[cfg(target_arch = "x86_64")]
1084 {
1085 // Device nodes used by libdrm through minigbm in libvda on AMD devices.
1086 let sys_dev_char_path = Path::new("/sys/dev/char");
1087 jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false)?;
1088 let sys_devices_path = Path::new("/sys/devices");
1089 jail.mount_bind(sys_devices_path, sys_devices_path, false)?;
1090
1091 // Required for loading dri or vulkan libraries loaded by minigbm on AMD devices.
1092 jail_mount_bind_if_exists(&mut jail, &["/usr/lib64", "/usr/lib", "/usr/share/vulkan"])?;
1093 }
1094
1095 // Device nodes required by libchrome which establishes Mojo connection in libvda.
1096 let dev_urandom_path = Path::new("/dev/urandom");
1097 jail.mount_bind(dev_urandom_path, dev_urandom_path, false)?;
1098 let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
1099 jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
1100
1101 Ok(jail)
1102 }
1103
1104 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
create_video_device( backend: VideoBackendType, protection_type: ProtectionType, jail_config: Option<&JailConfig>, typ: VideoDeviceType, resource_bridge: Tube, ) -> DeviceResult1105 pub fn create_video_device(
1106 backend: VideoBackendType,
1107 protection_type: ProtectionType,
1108 jail_config: Option<&JailConfig>,
1109 typ: VideoDeviceType,
1110 resource_bridge: Tube,
1111 ) -> DeviceResult {
1112 let jail = if let Some(jail_config) = jail_config {
1113 Some(create_video_device_jail(backend, jail_config, typ)?)
1114 } else {
1115 None
1116 };
1117
1118 Ok(VirtioDeviceStub {
1119 dev: Box::new(devices::virtio::VideoDevice::new(
1120 virtio::base_features(protection_type),
1121 typ,
1122 backend,
1123 Some(resource_bridge),
1124 )),
1125 jail,
1126 })
1127 }
1128
1129 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
register_video_device( backend: VideoBackendType, devs: &mut Vec<VirtioDeviceStub>, video_tube: Tube, protection_type: ProtectionType, jail_config: Option<&JailConfig>, typ: VideoDeviceType, ) -> Result<()>1130 pub fn register_video_device(
1131 backend: VideoBackendType,
1132 devs: &mut Vec<VirtioDeviceStub>,
1133 video_tube: Tube,
1134 protection_type: ProtectionType,
1135 jail_config: Option<&JailConfig>,
1136 typ: VideoDeviceType,
1137 ) -> Result<()> {
1138 devs.push(create_video_device(
1139 backend,
1140 protection_type,
1141 jail_config,
1142 typ,
1143 video_tube,
1144 )?);
1145 Ok(())
1146 }
1147
1148 #[cfg(feature = "media")]
create_simple_media_device(protection_type: ProtectionType) -> DeviceResult1149 pub fn create_simple_media_device(protection_type: ProtectionType) -> DeviceResult {
1150 use devices::virtio::media::create_virtio_media_simple_capture_device;
1151
1152 let features = virtio::base_features(protection_type);
1153 let dev = create_virtio_media_simple_capture_device(features);
1154
1155 Ok(VirtioDeviceStub { dev, jail: None })
1156 }
1157
1158 #[cfg(any(target_os = "android", target_os = "linux"))]
1159 #[cfg(feature = "media")]
create_v4l2_device<P: AsRef<Path>>( protection_type: ProtectionType, path: P, ) -> DeviceResult1160 pub fn create_v4l2_device<P: AsRef<Path>>(
1161 protection_type: ProtectionType,
1162 path: P,
1163 ) -> DeviceResult {
1164 use devices::virtio::media::create_virtio_media_v4l2_proxy_device;
1165
1166 let features = virtio::base_features(protection_type);
1167 let dev = create_virtio_media_v4l2_proxy_device(features, path)?;
1168
1169 Ok(VirtioDeviceStub { dev, jail: None })
1170 }
1171
1172 #[cfg(all(feature = "media", feature = "video-decoder"))]
create_virtio_media_adapter( protection_type: ProtectionType, jail_config: Option<&JailConfig>, tube: Tube, backend: VideoBackendType, ) -> DeviceResult1173 pub fn create_virtio_media_adapter(
1174 protection_type: ProtectionType,
1175 jail_config: Option<&JailConfig>,
1176 tube: Tube,
1177 backend: VideoBackendType,
1178 ) -> DeviceResult {
1179 use devices::virtio::media::create_virtio_media_decoder_adapter_device;
1180
1181 let jail = if let Some(jail_config) = jail_config {
1182 Some(create_video_device_jail(
1183 backend,
1184 jail_config,
1185 VideoDeviceType::Decoder,
1186 )?)
1187 } else {
1188 None
1189 };
1190
1191 let features = virtio::base_features(protection_type);
1192 let dev = create_virtio_media_decoder_adapter_device(features, tube, backend)?;
1193
1194 Ok(VirtioDeviceStub { dev, jail })
1195 }
1196
1197 impl VirtioDeviceBuilder for &VsockConfig {
1198 const NAME: &'static str = "vhost_vsock";
1199
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>1200 fn create_virtio_device(
1201 self,
1202 protection_type: ProtectionType,
1203 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
1204 let features = virtio::base_features(protection_type);
1205
1206 let dev = virtio::vhost::Vsock::new(features, self)
1207 .context("failed to set up virtual socket device")?;
1208
1209 Ok(Box::new(dev))
1210 }
1211
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>1212 fn create_vhost_user_device(
1213 self,
1214 keep_rds: &mut Vec<RawDescriptor>,
1215 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
1216 let vsock_device = VhostUserVsockDevice::new(self.cid, &self.vhost_device)?;
1217
1218 keep_rds.push(vsock_device.as_raw_descriptor());
1219
1220 Ok(Box::new(vsock_device))
1221 }
1222 }
1223
1224 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
create_vhost_scmi_device( protected_vm: ProtectionType, jail_config: Option<&JailConfig>, vhost_scmi_dev_path: PathBuf, ) -> DeviceResult1225 pub fn create_vhost_scmi_device(
1226 protected_vm: ProtectionType,
1227 jail_config: Option<&JailConfig>,
1228 vhost_scmi_dev_path: PathBuf,
1229 ) -> DeviceResult {
1230 let features = virtio::base_features(protected_vm);
1231
1232 let dev = virtio::vhost::Scmi::new(&vhost_scmi_dev_path, features)
1233 .context("failed to set up vhost scmi device")?;
1234
1235 Ok(VirtioDeviceStub {
1236 dev: Box::new(dev),
1237 jail: simple_jail(jail_config, "vhost_scmi_device")?,
1238 })
1239 }
1240
create_fs_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, ugid: (Option<u32>, Option<u32>), uid_map: &str, gid_map: &str, src: &Path, tag: &str, fs_cfg: virtio::fs::Config, device_tube: Tube, ) -> DeviceResult1241 pub fn create_fs_device(
1242 protection_type: ProtectionType,
1243 jail_config: Option<&JailConfig>,
1244 ugid: (Option<u32>, Option<u32>),
1245 uid_map: &str,
1246 gid_map: &str,
1247 src: &Path,
1248 tag: &str,
1249 fs_cfg: virtio::fs::Config,
1250 device_tube: Tube,
1251 ) -> DeviceResult {
1252 let max_open_files = base::linux::max_open_files()
1253 .context("failed to get max number of open files")?
1254 .rlim_max;
1255 let j = if let Some(jail_config) = jail_config {
1256 let mut config = SandboxConfig::new(jail_config, "fs_device");
1257 config.limit_caps = false;
1258 config.ugid_map = Some((uid_map, gid_map));
1259 // We want bind mounts from the parent namespaces to propagate into the fs device's
1260 // namespace.
1261 config.remount_mode = Some(libc::MS_SLAVE);
1262 config.run_as = if ugid == (None, None) {
1263 RunAsUser::Unspecified
1264 } else {
1265 RunAsUser::Specified(ugid.0.unwrap_or(0), ugid.1.unwrap_or(0))
1266 };
1267 create_sandbox_minijail(src, max_open_files, &config)?
1268 } else {
1269 create_base_minijail(src, max_open_files)?
1270 };
1271
1272 let features = virtio::base_features(protection_type);
1273 // TODO(chirantan): Use more than one worker once the kernel driver has been fixed to not panic
1274 // when num_queues > 1.
1275 let dev = virtio::fs::Fs::new(features, tag, 1, fs_cfg, device_tube)
1276 .context("failed to create fs device")?;
1277
1278 Ok(VirtioDeviceStub {
1279 dev: Box::new(dev),
1280 jail: Some(j),
1281 })
1282 }
1283
create_9p_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, ugid: (Option<u32>, Option<u32>), uid_map: &str, gid_map: &str, src: &Path, tag: &str, mut p9_cfg: p9::Config, ) -> DeviceResult1284 pub fn create_9p_device(
1285 protection_type: ProtectionType,
1286 jail_config: Option<&JailConfig>,
1287 ugid: (Option<u32>, Option<u32>),
1288 uid_map: &str,
1289 gid_map: &str,
1290 src: &Path,
1291 tag: &str,
1292 mut p9_cfg: p9::Config,
1293 ) -> DeviceResult {
1294 let max_open_files = base::linux::max_open_files()
1295 .context("failed to get max number of open files")?
1296 .rlim_max;
1297 let (jail, root) = if let Some(jail_config) = jail_config {
1298 let mut config = SandboxConfig::new(jail_config, "9p_device");
1299 config.limit_caps = false;
1300 config.ugid_map = Some((uid_map, gid_map));
1301 // We want bind mounts from the parent namespaces to propagate into the 9p server's
1302 // namespace.
1303 config.remount_mode = Some(libc::MS_SLAVE);
1304 config.run_as = if ugid == (None, None) {
1305 RunAsUser::Unspecified
1306 } else {
1307 RunAsUser::Specified(ugid.0.unwrap_or(0), ugid.1.unwrap_or(0))
1308 };
1309 let jail = create_sandbox_minijail(src, max_open_files, &config)?;
1310
1311 // The shared directory becomes the root of the device's file system.
1312 let root = Path::new("/");
1313 (Some(jail), root)
1314 } else {
1315 // There's no mount namespace so we tell the server to treat the source directory as the
1316 // root.
1317 (None, src)
1318 };
1319
1320 let features = virtio::base_features(protection_type);
1321 p9_cfg.root = root.into();
1322 let dev = virtio::P9::new(features, tag, p9_cfg).context("failed to create 9p device")?;
1323
1324 Ok(VirtioDeviceStub {
1325 dev: Box::new(dev),
1326 jail,
1327 })
1328 }
1329
create_pmem_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, vm: &mut impl Vm, resources: &mut SystemAllocator, pmem: &PmemOption, index: usize, pmem_device_tube: Tube, ) -> DeviceResult1330 pub fn create_pmem_device(
1331 protection_type: ProtectionType,
1332 jail_config: Option<&JailConfig>,
1333 vm: &mut impl Vm,
1334 resources: &mut SystemAllocator,
1335 pmem: &PmemOption,
1336 index: usize,
1337 pmem_device_tube: Tube,
1338 ) -> DeviceResult {
1339 let (fd, disk_size) = match pmem.vma_size {
1340 None => {
1341 let disk_image =
1342 open_file_or_duplicate(&pmem.path, OpenOptions::new().read(true).write(!pmem.ro))
1343 .with_context(|| format!("failed to load disk image {}", pmem.path.display()))?;
1344 let metadata = std::fs::metadata(&pmem.path).with_context(|| {
1345 format!("failed to get disk image {} metadata", pmem.path.display())
1346 })?;
1347 (disk_image, metadata.len())
1348 }
1349 Some(size) => {
1350 let anon_file =
1351 create_anonymous_file(&pmem.path, size).context("failed to create anon file")?;
1352 (anon_file, size)
1353 }
1354 };
1355
1356 // Linux requires pmem region sizes to be 2 MiB aligned. Linux will fill any partial page
1357 // at the end of an mmap'd file and won't write back beyond the actual file length, but if
1358 // we just align the size of the file to 2 MiB then access beyond the last page of the
1359 // mapped file will generate SIGBUS. So use a memory mapping arena that will provide
1360 // padding up to 2 MiB.
1361 let alignment = 2 * 1024 * 1024;
1362 let arena_size = disk_size
1363 .checked_next_multiple_of(alignment)
1364 .ok_or_else(|| anyhow!("pmem device image too big"))?;
1365
1366 let protection = {
1367 if pmem.ro {
1368 Protection::read()
1369 } else {
1370 Protection::read_write()
1371 }
1372 };
1373
1374 let arena = {
1375 // Conversion from u64 to usize may fail on 32bit system.
1376 let arena_size = usize::try_from(arena_size).context("pmem device image too big")?;
1377 let disk_size = usize::try_from(disk_size).context("pmem device image too big")?;
1378
1379 let mut arena =
1380 MemoryMappingArena::new(arena_size).context("failed to reserve pmem memory")?;
1381 arena
1382 .add_fd_offset_protection(0, disk_size, &fd, 0, protection)
1383 .context("failed to reserve pmem memory")?;
1384
1385 // If the disk is not a multiple of the page size, the OS will fill the remaining part
1386 // of the page with zeroes. However, the anonymous mapping added below must start on a
1387 // page boundary, so round up the size before calculating the offset of the anon region.
1388 let disk_size = round_up_to_page_size(disk_size);
1389
1390 if arena_size > disk_size {
1391 // Add an anonymous region with the same protection as the disk mapping if the arena
1392 // size was aligned.
1393 arena
1394 .add_anon_protection(disk_size, arena_size - disk_size, protection)
1395 .context("failed to reserve pmem padding")?;
1396 }
1397 arena
1398 };
1399
1400 let mapping_address = GuestAddress(
1401 resources
1402 .allocate_mmio(
1403 arena_size,
1404 Alloc::PmemDevice(index),
1405 format!("pmem_disk_image_{}", index),
1406 AllocOptions::new()
1407 // Allocate from the bottom up rather than top down to avoid exceeding PHYSMEM_END
1408 // with kaslr.
1409 // TODO: b/375506171: Find a proper fix.
1410 .top_down(false)
1411 .prefetchable(true)
1412 // Linux kernel requires pmem namespaces to be 128 MiB aligned.
1413 // cf. https://github.com/pmem/ndctl/issues/76
1414 .align(128 * 1024 * 1024), /* 128 MiB */
1415 )
1416 .context("failed to allocate memory for pmem device")?,
1417 );
1418
1419 let mem_slot = MemSlotConfig::MemSlot {
1420 idx: vm
1421 .add_memory_region(
1422 mapping_address,
1423 Box::new(arena),
1424 /* read_only = */ pmem.ro,
1425 /* log_dirty_pages = */ false,
1426 MemCacheType::CacheCoherent,
1427 )
1428 .context("failed to add pmem device memory")?,
1429 };
1430
1431 let dev = virtio::Pmem::new(
1432 virtio::base_features(protection_type),
1433 PmemConfig {
1434 disk_image: Some(fd),
1435 mapping_address,
1436 mem_slot,
1437 mapping_size: arena_size,
1438 pmem_device_tube,
1439 swap_interval: pmem.swap_interval,
1440 mapping_writable: !pmem.ro,
1441 },
1442 )
1443 .context("failed to create pmem device")?;
1444
1445 Ok(VirtioDeviceStub {
1446 dev: Box::new(dev) as Box<dyn VirtioDevice>,
1447 jail: simple_jail(jail_config, "pmem_device")?,
1448 })
1449 }
1450
create_pmem_ext2_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, resources: &mut SystemAllocator, opts: &PmemExt2Option, index: usize, vm_memory_client: VmMemoryClient, pmem_device_tube: Tube, worker_process_pids: &mut BTreeSet<Pid>, ) -> DeviceResult1451 pub fn create_pmem_ext2_device(
1452 protection_type: ProtectionType,
1453 jail_config: Option<&JailConfig>,
1454 resources: &mut SystemAllocator,
1455 opts: &PmemExt2Option,
1456 index: usize,
1457 vm_memory_client: VmMemoryClient,
1458 pmem_device_tube: Tube,
1459 worker_process_pids: &mut BTreeSet<Pid>,
1460 ) -> DeviceResult {
1461 let mapping_size = opts.size as u64;
1462 let builder = ext2::Builder {
1463 inodes_per_group: opts.inodes_per_group,
1464 blocks_per_group: opts.blocks_per_group,
1465 size: mapping_size as u32,
1466 ..Default::default()
1467 };
1468
1469 let max_open_files = base::linux::max_open_files()
1470 .context("failed to get max number of open files")?
1471 .rlim_max;
1472 let mapping_address = GuestAddress(
1473 resources
1474 .allocate_mmio(
1475 mapping_size,
1476 Alloc::PmemDevice(index),
1477 format!("pmem_ext2_image_{}", index),
1478 AllocOptions::new()
1479 .top_down(true)
1480 .prefetchable(true)
1481 // 2MB alignment for DAX
1482 // cf. https://docs.pmem.io/persistent-memory/getting-started-guide/creating-development-environments/linux-environments/advanced-topics/i-o-alignment-considerations#verifying-io-alignment
1483 .align(2 * 1024 * 1024),
1484 )
1485 .context("failed to allocate memory for pmem device")?,
1486 );
1487
1488 let (mkfs_tube, mkfs_device_tube) = Tube::pair().context("failed to create tube")?;
1489
1490 let ext2_proc_pid = crate::crosvm::sys::linux::ext2::launch(
1491 mapping_address,
1492 vm_memory_client,
1493 mkfs_tube,
1494 &opts.path,
1495 &opts.ugid,
1496 (&opts.uid_map, &opts.gid_map),
1497 builder,
1498 jail_config,
1499 )
1500 .context("failed to spawn mkfs process")?;
1501
1502 worker_process_pids.insert(ext2_proc_pid);
1503
1504 let dev = virtio::Pmem::new(
1505 virtio::base_features(protection_type),
1506 PmemConfig {
1507 disk_image: None,
1508 mapping_address,
1509 mem_slot: MemSlotConfig::LazyInit {
1510 tube: mkfs_device_tube,
1511 },
1512 mapping_size,
1513 pmem_device_tube,
1514 swap_interval: None,
1515 mapping_writable: false,
1516 },
1517 )
1518 .context("failed to create pmem device")?;
1519
1520 let j = if let Some(jail_config) = jail_config {
1521 let mut config = SandboxConfig::new(jail_config, "pmem_device");
1522 config.limit_caps = false;
1523 create_sandbox_minijail(&opts.path, max_open_files, &config)?
1524 } else {
1525 create_base_minijail(&opts.path, max_open_files)?
1526 };
1527 Ok(VirtioDeviceStub {
1528 dev: Box::new(dev) as Box<dyn VirtioDevice>,
1529 jail: Some(j),
1530 })
1531 }
1532
create_anonymous_file<P: AsRef<Path>>(path: P, size: u64) -> Result<File>1533 pub fn create_anonymous_file<P: AsRef<Path>>(path: P, size: u64) -> Result<File> {
1534 let file_name = path
1535 .as_ref()
1536 .to_str()
1537 .ok_or_else(|| Error::new(libc::EINVAL))?;
1538 let mut shm = SharedMemory::new(file_name, size)?;
1539 let mut seals = MemfdSeals::new();
1540
1541 seals.set_shrink_seal();
1542 seals.set_grow_seal();
1543 seals.set_seal_seal();
1544 shm.add_seals(seals)?;
1545
1546 Ok(shm.descriptor.into())
1547 }
1548
create_iommu_device( protection_type: ProtectionType, jail_config: Option<&JailConfig>, iova_max_addr: u64, endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>, hp_endpoints_ranges: Vec<RangeInclusive<u32>>, translate_response_senders: Option<BTreeMap<u32, Tube>>, translate_request_rx: Option<Tube>, iommu_device_tube: Tube, ) -> DeviceResult1549 pub fn create_iommu_device(
1550 protection_type: ProtectionType,
1551 jail_config: Option<&JailConfig>,
1552 iova_max_addr: u64,
1553 endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1554 hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
1555 translate_response_senders: Option<BTreeMap<u32, Tube>>,
1556 translate_request_rx: Option<Tube>,
1557 iommu_device_tube: Tube,
1558 ) -> DeviceResult {
1559 let dev = virtio::Iommu::new(
1560 virtio::base_features(protection_type),
1561 endpoints,
1562 iova_max_addr,
1563 hp_endpoints_ranges,
1564 translate_response_senders,
1565 translate_request_rx,
1566 Some(iommu_device_tube),
1567 )
1568 .context("failed to create IOMMU device")?;
1569
1570 Ok(VirtioDeviceStub {
1571 dev: Box::new(dev),
1572 jail: simple_jail(jail_config, "iommu_device")?,
1573 })
1574 }
1575
add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error>1576 fn add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error> {
1577 if let Some(path) = ¶m.path {
1578 if let SerialType::SystemSerialType = param.type_ {
1579 if let Some(parent) = path.as_path().parent() {
1580 if parent.exists() {
1581 info!("Bind mounting dir {}", parent.display());
1582 jail.mount_bind(parent, parent, true)?;
1583 }
1584 }
1585 }
1586 }
1587 Ok(())
1588 }
1589
1590 /// For creating console virtio devices.
1591 impl VirtioDeviceBuilder for &SerialParameters {
1592 const NAME: &'static str = "serial";
1593
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>1594 fn create_virtio_device(
1595 self,
1596 protection_type: ProtectionType,
1597 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
1598 let mut keep_rds = Vec::new();
1599 let evt = Event::new().context("failed to create event")?;
1600
1601 Ok(Box::new(
1602 self.create_serial_device::<Console>(protection_type, &evt, &mut keep_rds)
1603 .context("failed to create console device")?,
1604 ))
1605 }
1606
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>1607 fn create_vhost_user_device(
1608 self,
1609 keep_rds: &mut Vec<RawDescriptor>,
1610 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
1611 Ok(Box::new(virtio::vhost::user::create_vu_console_device(
1612 self, keep_rds,
1613 )?))
1614 }
1615
create_jail( &self, jail_config: Option<&JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>1616 fn create_jail(
1617 &self,
1618 jail_config: Option<&JailConfig>,
1619 virtio_transport: VirtioDeviceType,
1620 ) -> anyhow::Result<Option<Minijail>> {
1621 if let Some(jail_config) = jail_config {
1622 let policy = virtio_transport.seccomp_policy_file("serial");
1623 let mut config = SandboxConfig::new(jail_config, &policy);
1624 config.bind_mounts = true;
1625 let mut jail =
1626 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
1627 add_bind_mounts(self, &mut jail)
1628 .context("failed to add bind mounts for console device")?;
1629 Ok(Some(jail))
1630 } else {
1631 Ok(None)
1632 }
1633 }
1634 }
1635
1636 #[cfg(feature = "audio")]
create_sound_device( path: &Path, protection_type: ProtectionType, jail_config: Option<&JailConfig>, ) -> DeviceResult1637 pub fn create_sound_device(
1638 path: &Path,
1639 protection_type: ProtectionType,
1640 jail_config: Option<&JailConfig>,
1641 ) -> DeviceResult {
1642 let dev = virtio::new_sound(path, virtio::base_features(protection_type))
1643 .context("failed to create sound device")?;
1644
1645 Ok(VirtioDeviceStub {
1646 dev: Box::new(dev),
1647 jail: simple_jail(jail_config, "vios_audio_device")?,
1648 })
1649 }
1650
1651 #[allow(clippy::large_enum_variant)]
1652 pub enum VfioDeviceVariant {
1653 Pci(VfioPciDevice),
1654 Platform(VfioPlatformDevice),
1655 }
1656
create_vfio_device( jail_config: Option<&JailConfig>, vm: &impl Vm, resources: &mut SystemAllocator, add_control_tube: &mut impl FnMut(AnyControlTube), vfio_path: &Path, hotplug: bool, hotplug_bus: Option<u8>, guest_address: Option<PciAddress>, coiommu_endpoints: Option<&mut Vec<u16>>, iommu_dev: IommuDevType, dt_symbol: Option<String>, vfio_container_manager: &mut VfioContainerManager, ) -> DeviceResult<(VfioDeviceVariant, Option<Minijail>, Option<VfioWrapper>)>1657 pub fn create_vfio_device(
1658 jail_config: Option<&JailConfig>,
1659 vm: &impl Vm,
1660 resources: &mut SystemAllocator,
1661 add_control_tube: &mut impl FnMut(AnyControlTube),
1662 vfio_path: &Path,
1663 hotplug: bool,
1664 hotplug_bus: Option<u8>,
1665 guest_address: Option<PciAddress>,
1666 coiommu_endpoints: Option<&mut Vec<u16>>,
1667 iommu_dev: IommuDevType,
1668 dt_symbol: Option<String>,
1669 vfio_container_manager: &mut VfioContainerManager,
1670 ) -> DeviceResult<(VfioDeviceVariant, Option<Minijail>, Option<VfioWrapper>)> {
1671 let vfio_container = vfio_container_manager
1672 .get_container(iommu_dev, Some(vfio_path))
1673 .context("failed to get vfio container")?;
1674
1675 let (vfio_host_tube_mem, vfio_device_tube_mem) =
1676 Tube::pair().context("failed to create tube")?;
1677 add_control_tube(
1678 VmMemoryTube {
1679 tube: vfio_host_tube_mem,
1680 expose_with_viommu: false,
1681 }
1682 .into(),
1683 );
1684
1685 let (vfio_host_tube_vm, vfio_device_tube_vm) = Tube::pair().context("failed to create tube")?;
1686 add_control_tube(TaggedControlTube::Vm(vfio_host_tube_vm).into());
1687
1688 let vfio_device =
1689 VfioDevice::new_passthrough(&vfio_path, vm, vfio_container.clone(), iommu_dev, dt_symbol)
1690 .context("failed to create vfio device")?;
1691
1692 match vfio_device.device_type() {
1693 VfioDeviceType::Pci => {
1694 let (vfio_host_tube_msi, vfio_device_tube_msi) =
1695 Tube::pair().context("failed to create tube")?;
1696 add_control_tube(AnyControlTube::IrqTube(vfio_host_tube_msi));
1697
1698 let (vfio_host_tube_msix, vfio_device_tube_msix) =
1699 Tube::pair().context("failed to create tube")?;
1700 add_control_tube(AnyControlTube::IrqTube(vfio_host_tube_msix));
1701
1702 let mut vfio_pci_device = VfioPciDevice::new(
1703 vfio_path,
1704 vfio_device,
1705 hotplug,
1706 hotplug_bus,
1707 guest_address,
1708 vfio_device_tube_msi,
1709 vfio_device_tube_msix,
1710 VmMemoryClient::new(vfio_device_tube_mem),
1711 vfio_device_tube_vm,
1712 )?;
1713 // early reservation for pass-through PCI devices.
1714 let endpoint_addr = vfio_pci_device
1715 .allocate_address(resources)
1716 .context("failed to allocate resources early for vfio pci dev")?;
1717
1718 let viommu_mapper = match iommu_dev {
1719 IommuDevType::NoIommu | IommuDevType::PkvmPviommu => None,
1720 IommuDevType::VirtioIommu => {
1721 Some(VfioWrapper::new(vfio_container, vm.get_memory().clone()))
1722 }
1723 IommuDevType::CoIommu => {
1724 if let Some(endpoints) = coiommu_endpoints {
1725 endpoints.push(endpoint_addr.to_u32() as u16);
1726 } else {
1727 bail!("Missed coiommu_endpoints vector to store the endpoint addr");
1728 }
1729 None
1730 }
1731 };
1732
1733 if hotplug {
1734 Ok((VfioDeviceVariant::Pci(vfio_pci_device), None, viommu_mapper))
1735 } else {
1736 Ok((
1737 VfioDeviceVariant::Pci(vfio_pci_device),
1738 simple_jail(jail_config, "vfio_device")?,
1739 viommu_mapper,
1740 ))
1741 }
1742 }
1743 VfioDeviceType::Platform => {
1744 if guest_address.is_some() {
1745 bail!("guest-address is not supported for VFIO platform devices");
1746 }
1747
1748 if hotplug {
1749 bail!("hotplug is not supported for VFIO platform devices");
1750 }
1751
1752 let vfio_plat_dev =
1753 VfioPlatformDevice::new(vfio_device, VmMemoryClient::new(vfio_device_tube_mem));
1754
1755 Ok((
1756 VfioDeviceVariant::Platform(vfio_plat_dev),
1757 simple_jail(jail_config, "vfio_platform_device")?,
1758 None,
1759 ))
1760 }
1761 }
1762 }
1763
1764 /// Setup for devices with virtio-iommu
setup_virtio_access_platform( resources: &mut SystemAllocator, iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>, devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)], ) -> DeviceResult<(Option<BTreeMap<u32, Tube>>, Option<Tube>)>1765 pub fn setup_virtio_access_platform(
1766 resources: &mut SystemAllocator,
1767 iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1768 devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)],
1769 ) -> DeviceResult<(Option<BTreeMap<u32, Tube>>, Option<Tube>)> {
1770 let mut translate_response_senders: Option<
1771 BTreeMap<
1772 u32, // endpoint id
1773 Tube,
1774 >,
1775 > = None;
1776 let mut tube_pair: Option<(Tube, Tube)> = None;
1777
1778 for dev in devices.iter_mut() {
1779 if let Some(pci_dev) = dev.0.as_pci_device_mut() {
1780 if pci_dev.supports_iommu() {
1781 let endpoint_id = pci_dev
1782 .allocate_address(resources)
1783 .context("failed to allocate resources for pci dev")?
1784 .to_u32();
1785 let mapper: Arc<Mutex<Box<dyn MemoryMapperTrait>>> =
1786 Arc::new(Mutex::new(Box::new(BasicMemoryMapper::new(u64::MAX))));
1787 let (request_tx, _request_rx) =
1788 tube_pair.get_or_insert_with(|| Tube::pair().unwrap());
1789 let CreateIpcMapperRet {
1790 mapper: ipc_mapper,
1791 response_tx,
1792 } = create_ipc_mapper(
1793 endpoint_id,
1794 #[allow(deprecated)]
1795 request_tx.try_clone()?,
1796 );
1797 translate_response_senders
1798 .get_or_insert_with(BTreeMap::new)
1799 .insert(endpoint_id, response_tx);
1800 iommu_attached_endpoints.insert(endpoint_id, mapper);
1801 pci_dev.set_iommu(ipc_mapper)?;
1802 }
1803 }
1804 }
1805
1806 Ok((
1807 translate_response_senders,
1808 tube_pair.map(|(_request_tx, request_rx)| request_rx),
1809 ))
1810 }
1811