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