• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) = &param.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