• 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::convert::TryFrom;
7 use std::fs::OpenOptions;
8 use std::ops::RangeInclusive;
9 use std::os::unix::net::UnixListener;
10 use std::os::unix::net::UnixStream;
11 use std::path::Path;
12 use std::path::PathBuf;
13 use std::str;
14 use std::sync::Arc;
15 
16 use anyhow::anyhow;
17 use anyhow::bail;
18 use anyhow::Context;
19 use anyhow::Result;
20 use arch::VirtioDeviceStub;
21 use base::ReadNotifier;
22 use base::*;
23 use devices::serial_device::SerialParameters;
24 use devices::serial_device::SerialType;
25 use devices::vfio::VfioCommonSetup;
26 use devices::vfio::VfioCommonTrait;
27 use devices::virtio;
28 use devices::virtio::block::block::DiskOption;
29 use devices::virtio::console::asynchronous::AsyncConsole;
30 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
31 use devices::virtio::device_constants::video::VideoBackendType;
32 use devices::virtio::device_constants::video::VideoDeviceType;
33 use devices::virtio::ipc_memory_mapper::create_ipc_mapper;
34 use devices::virtio::ipc_memory_mapper::CreateIpcMapperRet;
35 use devices::virtio::memory_mapper::BasicMemoryMapper;
36 use devices::virtio::memory_mapper::MemoryMapperTrait;
37 #[cfg(feature = "audio")]
38 use devices::virtio::snd::parameters::Parameters as SndParameters;
39 use devices::virtio::vfio_wrapper::VfioWrapper;
40 use devices::virtio::vhost::user::proxy::VirtioVhostUser;
41 use devices::virtio::vhost::user::vmm::VhostUserVirtioDevice;
42 use devices::virtio::vhost::user::VhostUserDevice;
43 use devices::virtio::vhost::user::VhostUserVsockDevice;
44 use devices::virtio::vsock::VsockConfig;
45 #[cfg(feature = "balloon")]
46 use devices::virtio::BalloonMode;
47 use devices::virtio::Console;
48 use devices::virtio::NetError;
49 use devices::virtio::NetParametersMode;
50 use devices::virtio::VirtioDevice;
51 use devices::virtio::VirtioDeviceType;
52 use devices::BusDeviceObj;
53 use devices::IommuDevType;
54 use devices::PciAddress;
55 use devices::PciDevice;
56 #[cfg(feature = "tpm")]
57 use devices::SoftwareTpm;
58 use devices::VfioDevice;
59 use devices::VfioDeviceType;
60 use devices::VfioPciDevice;
61 use devices::VfioPlatformDevice;
62 #[cfg(all(feature = "vtpm", target_arch = "x86_64"))]
63 use devices::VtpmProxy;
64 use hypervisor::ProtectionType;
65 use hypervisor::Vm;
66 use jail::*;
67 use minijail::Minijail;
68 use net_util::sys::unix::Tap;
69 use net_util::MacAddress;
70 use net_util::TapT;
71 use net_util::TapTCommon;
72 use resources::Alloc;
73 use resources::AllocOptions;
74 use resources::SystemAllocator;
75 use sync::Mutex;
76 use vm_memory::GuestAddress;
77 
78 use crate::crosvm::config::TouchDeviceOption;
79 use crate::crosvm::config::VhostUserFsOption;
80 use crate::crosvm::config::VhostUserOption;
81 use crate::crosvm::config::VvuOption;
82 
83 pub enum TaggedControlTube {
84     Fs(Tube),
85     Vm(Tube),
86     VmMemory {
87         tube: Tube,
88         /// See devices::virtio::VirtioDevice.expose_shared_memory_region_with_viommu
89         expose_with_viommu: bool,
90     },
91     VmMsync(Tube),
92 }
93 
94 impl AsRef<Tube> for TaggedControlTube {
as_ref(&self) -> &Tube95     fn as_ref(&self) -> &Tube {
96         use self::TaggedControlTube::*;
97         match &self {
98             Fs(tube) | Vm(tube) | VmMemory { tube, .. } | VmMsync(tube) => tube,
99         }
100     }
101 }
102 
103 impl AsRawDescriptor for TaggedControlTube {
as_raw_descriptor(&self) -> RawDescriptor104     fn as_raw_descriptor(&self) -> RawDescriptor {
105         self.as_ref().as_raw_descriptor()
106     }
107 }
108 
109 impl ReadNotifier for TaggedControlTube {
get_read_notifier(&self) -> &dyn AsRawDescriptor110     fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
111         self.as_ref().get_read_notifier()
112     }
113 }
114 
115 pub trait IntoUnixStream {
into_unix_stream(self) -> Result<UnixStream>116     fn into_unix_stream(self) -> Result<UnixStream>;
117 }
118 
119 impl<'a> IntoUnixStream for &'a Path {
into_unix_stream(self) -> Result<UnixStream>120     fn into_unix_stream(self) -> Result<UnixStream> {
121         if let Some(fd) = safe_descriptor_from_path(self).context("failed to open event device")? {
122             Ok(fd.into())
123         } else {
124             UnixStream::connect(self).context("failed to open event device")
125         }
126     }
127 }
128 
129 impl<'a> IntoUnixStream for &'a PathBuf {
into_unix_stream(self) -> Result<UnixStream>130     fn into_unix_stream(self) -> Result<UnixStream> {
131         self.as_path().into_unix_stream()
132     }
133 }
134 
135 impl IntoUnixStream for UnixStream {
into_unix_stream(self) -> Result<UnixStream>136     fn into_unix_stream(self) -> Result<UnixStream> {
137         Ok(self)
138     }
139 }
140 
141 pub type DeviceResult<T = VirtioDeviceStub> = Result<T>;
142 
143 /// A trait for spawning virtio device instances and jails from their configuration structure.
144 ///
145 /// Implementors become able to create virtio devices and jails following their own configuration.
146 /// This trait also provides a few convenience methods for e.g. creating a virtio device and jail
147 /// at once.
148 pub trait VirtioDeviceBuilder: Sized {
149     /// Base name of the device, as it will appear in logs.
150     const NAME: &'static str;
151 
152     /// Create a regular virtio device from the configuration and `protection_type` setting.
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>153     fn create_virtio_device(
154         self,
155         protection_type: ProtectionType,
156     ) -> anyhow::Result<Box<dyn VirtioDevice>>;
157 
158     /// Create a device suitable for being run as a vhost-user instance.
159     ///
160     /// It is ok to leave this method unimplemented if the device is not intended to be used with
161     /// vhost-user.
create_vhost_user_device( self, _keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDevice>>162     fn create_vhost_user_device(
163         self,
164         _keep_rds: &mut Vec<RawDescriptor>,
165     ) -> anyhow::Result<Box<dyn VhostUserDevice>> {
166         unimplemented!()
167     }
168 
169     /// Create a jail that is suitable to run a device.
170     ///
171     /// The default implementation creates a simple jail with a seccomp policy derived from the
172     /// base name of the device.
create_jail( &self, jail_config: &Option<JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>173     fn create_jail(
174         &self,
175         jail_config: &Option<JailConfig>,
176         virtio_transport: VirtioDeviceType,
177     ) -> anyhow::Result<Option<Minijail>> {
178         simple_jail(
179             jail_config,
180             &virtio_transport.seccomp_policy_file(Self::NAME),
181         )
182     }
183 
184     /// Helper method to return a `VirtioDeviceStub` filled using `create_virtio_device` and
185     /// `create_jail`.
186     ///
187     /// 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>, ) -> DeviceResult188     fn create_virtio_device_and_jail(
189         self,
190         protection_type: ProtectionType,
191         jail_config: &Option<JailConfig>,
192     ) -> DeviceResult {
193         let jail = self.create_jail(jail_config, VirtioDeviceType::Regular)?;
194         let dev = self.create_virtio_device(protection_type)?;
195         Ok(VirtioDeviceStub { dev, jail })
196     }
197 }
198 
199 /// A one-shot configuration structure for implementing `VirtioDeviceBuilder`. We cannot do it on
200 /// `DiskOption` directly because disk devices can be passed an optional control tube.
201 pub struct DiskConfig<'a> {
202     /// Options for disk creation.
203     disk: &'a DiskOption,
204     /// Optional control tube for the device.
205     device_tube: Option<Tube>,
206 }
207 
208 impl<'a> DiskConfig<'a> {
new(disk: &'a DiskOption, device_tube: Option<Tube>) -> Self209     pub fn new(disk: &'a DiskOption, device_tube: Option<Tube>) -> Self {
210         Self { disk, device_tube }
211     }
212 }
213 
214 impl<'a> VirtioDeviceBuilder for DiskConfig<'a> {
215     const NAME: &'static str = "block";
216 
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>217     fn create_virtio_device(
218         self,
219         protection_type: ProtectionType,
220     ) -> anyhow::Result<Box<dyn VirtioDevice>> {
221         info!(
222             "Trying to attach block device: {}",
223             self.disk.path.display(),
224         );
225         let disk_image = self.disk.open()?;
226 
227         Ok(Box::new(
228             virtio::BlockAsync::new(
229                 virtio::base_features(protection_type),
230                 disk_image,
231                 self.disk.read_only,
232                 self.disk.sparse,
233                 self.disk.block_size,
234                 self.disk.multiple_workers,
235                 self.disk.id,
236                 self.device_tube,
237                 None,
238                 self.disk.async_executor,
239                 None,
240             )
241             .context("failed to create block device")?,
242         ))
243     }
244 
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDevice>>245     fn create_vhost_user_device(
246         self,
247         keep_rds: &mut Vec<RawDescriptor>,
248     ) -> anyhow::Result<Box<dyn VhostUserDevice>> {
249         let disk = self.disk;
250         let disk_image = disk.open()?;
251         let block = Box::new(
252             virtio::BlockAsync::new(
253                 virtio::base_features(ProtectionType::Unprotected),
254                 disk_image,
255                 disk.read_only,
256                 disk.sparse,
257                 disk.block_size,
258                 false,
259                 disk.id,
260                 self.device_tube,
261                 None,
262                 disk.async_executor,
263                 None,
264             )
265             .context("failed to create block device")?,
266         );
267         keep_rds.extend(block.keep_rds());
268 
269         Ok(block)
270     }
271 }
272 
vhost_user_connection(path: &Path) -> Result<UnixStream>273 fn vhost_user_connection(path: &Path) -> Result<UnixStream> {
274     UnixStream::connect(path).with_context(|| {
275         format!(
276             "failed to connect to vhost-user socket path {}",
277             path.display()
278         )
279     })
280 }
281 
create_vhost_user_block_device( protection_type: ProtectionType, opt: &VhostUserOption, ) -> DeviceResult282 pub fn create_vhost_user_block_device(
283     protection_type: ProtectionType,
284     opt: &VhostUserOption,
285 ) -> DeviceResult {
286     let dev = VhostUserVirtioDevice::new_block(
287         virtio::base_features(protection_type),
288         vhost_user_connection(&opt.socket)?,
289     )
290     .context("failed to set up vhost-user block device")?;
291 
292     Ok(VirtioDeviceStub {
293         dev: Box::new(dev),
294         // no sandbox here because virtqueue handling is exported to a different process.
295         jail: None,
296     })
297 }
298 
create_vhost_user_console_device( protection_type: ProtectionType, opt: &VhostUserOption, ) -> DeviceResult299 pub fn create_vhost_user_console_device(
300     protection_type: ProtectionType,
301     opt: &VhostUserOption,
302 ) -> DeviceResult {
303     let dev = VhostUserVirtioDevice::new_console(
304         virtio::base_features(protection_type),
305         vhost_user_connection(&opt.socket)?,
306     )
307     .context("failed to set up vhost-user console device")?;
308 
309     Ok(VirtioDeviceStub {
310         dev: Box::new(dev),
311         // no sandbox here because virtqueue handling is exported to a different process.
312         jail: None,
313     })
314 }
315 
create_vhost_user_fs_device( protection_type: ProtectionType, option: &VhostUserFsOption, ) -> DeviceResult316 pub fn create_vhost_user_fs_device(
317     protection_type: ProtectionType,
318     option: &VhostUserFsOption,
319 ) -> DeviceResult {
320     let dev = VhostUserVirtioDevice::new_fs(
321         virtio::base_features(protection_type),
322         vhost_user_connection(&option.socket)?,
323         &option.tag,
324     )
325     .context("failed to set up vhost-user fs device")?;
326 
327     Ok(VirtioDeviceStub {
328         dev: Box::new(dev),
329         // no sandbox here because virtqueue handling is exported to a different process.
330         jail: None,
331     })
332 }
333 
create_vhost_user_mac80211_hwsim_device( protection_type: ProtectionType, opt: &VhostUserOption, ) -> DeviceResult334 pub fn create_vhost_user_mac80211_hwsim_device(
335     protection_type: ProtectionType,
336     opt: &VhostUserOption,
337 ) -> DeviceResult {
338     let dev = VhostUserVirtioDevice::new_mac80211_hwsim(
339         virtio::base_features(protection_type),
340         vhost_user_connection(&opt.socket)?,
341     )
342     .context("failed to set up vhost-user mac80211_hwsim device")?;
343 
344     Ok(VirtioDeviceStub {
345         dev: Box::new(dev),
346         // no sandbox here because virtqueue handling is exported to a different process.
347         jail: None,
348     })
349 }
350 
create_vhost_user_snd_device( protection_type: ProtectionType, option: &VhostUserOption, ) -> DeviceResult351 pub fn create_vhost_user_snd_device(
352     protection_type: ProtectionType,
353     option: &VhostUserOption,
354 ) -> DeviceResult {
355     let dev = VhostUserVirtioDevice::new_snd(
356         virtio::base_features(protection_type),
357         vhost_user_connection(&option.socket)?,
358     )
359     .context("failed to set up vhost-user snd device")?;
360 
361     Ok(VirtioDeviceStub {
362         dev: Box::new(dev),
363         // no sandbox here because virtqueue handling is exported to a different process.
364         jail: None,
365     })
366 }
367 
create_vhost_user_gpu_device( protection_type: ProtectionType, opt: &VhostUserOption, ) -> DeviceResult368 pub fn create_vhost_user_gpu_device(
369     protection_type: ProtectionType,
370     opt: &VhostUserOption,
371 ) -> DeviceResult {
372     // The crosvm gpu device expects us to connect the tube before it will accept a vhost-user
373     // connection.
374     let dev = VhostUserVirtioDevice::new_gpu(
375         virtio::base_features(protection_type),
376         vhost_user_connection(&opt.socket)?,
377     )
378     .context("failed to set up vhost-user gpu device")?;
379 
380     Ok(VirtioDeviceStub {
381         dev: Box::new(dev),
382         // no sandbox here because virtqueue handling is exported to a different process.
383         jail: None,
384     })
385 }
386 
create_vvu_proxy_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, opt: &VvuOption, tube: Tube, max_sibling_mem_size: u64, ) -> DeviceResult387 pub fn create_vvu_proxy_device(
388     protection_type: ProtectionType,
389     jail_config: &Option<JailConfig>,
390     opt: &VvuOption,
391     tube: Tube,
392     max_sibling_mem_size: u64,
393 ) -> DeviceResult {
394     let listener =
395         UnixListener::bind(&opt.socket).context("failed to bind listener for vvu proxy device")?;
396 
397     let dev = VirtioVhostUser::new(
398         virtio::base_features(protection_type),
399         listener,
400         tube,
401         opt.addr,
402         opt.uuid,
403         max_sibling_mem_size,
404     )
405     .context("failed to create VVU proxy device")?;
406 
407     Ok(VirtioDeviceStub {
408         dev: Box::new(dev),
409         jail: simple_jail(jail_config, "vvu_proxy_device")?,
410     })
411 }
412 
create_rng_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, ) -> DeviceResult413 pub fn create_rng_device(
414     protection_type: ProtectionType,
415     jail_config: &Option<JailConfig>,
416 ) -> DeviceResult {
417     let dev =
418         virtio::Rng::new(virtio::base_features(protection_type)).context("failed to set up rng")?;
419 
420     Ok(VirtioDeviceStub {
421         dev: Box::new(dev),
422         jail: simple_jail(jail_config, "rng_device")?,
423     })
424 }
425 
426 #[cfg(feature = "audio")]
create_virtio_snd_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, snd_params: SndParameters, ) -> DeviceResult427 pub fn create_virtio_snd_device(
428     protection_type: ProtectionType,
429     jail_config: &Option<JailConfig>,
430     snd_params: SndParameters,
431 ) -> DeviceResult {
432     let backend = snd_params.backend;
433     let dev = virtio::snd::common_backend::VirtioSnd::new(
434         virtio::base_features(protection_type),
435         snd_params,
436     )
437     .context("failed to create cras sound device")?;
438 
439     use virtio::snd::parameters::StreamSourceBackend as Backend;
440 
441     let policy = match backend {
442         Backend::NULL | Backend::FILE => "snd_null_device",
443         #[cfg(feature = "audio_cras")]
444         Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) => "snd_cras_device",
445         #[cfg(not(feature = "audio_cras"))]
446         _ => unreachable!(),
447     };
448 
449     let jail = if let Some(jail_config) = jail_config {
450         let mut config = SandboxConfig::new(jail_config, policy);
451         #[cfg(feature = "audio_cras")]
452         if backend == Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) {
453             config.bind_mounts = true;
454         }
455         // TODO(b/267574679): running as current_user may not be required for snd device.
456         config.run_as = RunAsUser::CurrentUser;
457         #[allow(unused_mut)]
458         let mut jail =
459             create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
460         #[cfg(feature = "audio_cras")]
461         if backend == Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) {
462             let run_cras_path = Path::new("/run/cras");
463             jail.mount_bind(run_cras_path, run_cras_path, true)?;
464         }
465         Some(jail)
466     } else {
467         None
468     };
469 
470     Ok(VirtioDeviceStub {
471         dev: Box::new(dev),
472         jail,
473     })
474 }
475 
476 #[cfg(feature = "tpm")]
create_software_tpm_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, ) -> DeviceResult477 pub fn create_software_tpm_device(
478     protection_type: ProtectionType,
479     jail_config: &Option<JailConfig>,
480 ) -> DeviceResult {
481     use std::ffi::CString;
482     use std::fs;
483     use std::process;
484 
485     let (jail, tpm_storage) = if let Some(jail_config) = jail_config {
486         let mut config = SandboxConfig::new(jail_config, "tpm_device");
487         config.bind_mounts = true;
488         let mut jail =
489             create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
490 
491         let pid = process::id();
492         let crosvm_uid = geteuid();
493         let crosvm_gid = getegid();
494         let tpm_pid_dir = format!("/run/vm/tpm.{}", pid);
495         let tpm_storage = PathBuf::from(&tpm_pid_dir);
496         fs::create_dir_all(&tpm_storage).with_context(|| {
497             format!("failed to create tpm storage dir {}", tpm_storage.display())
498         })?;
499         let tpm_pid_dir_c = CString::new(tpm_pid_dir).expect("no nul bytes");
500         chown(&tpm_pid_dir_c, crosvm_uid, crosvm_gid).context("failed to chown tpm storage")?;
501 
502         jail.mount_bind(&tpm_storage, &tpm_storage, true)?;
503 
504         (Some(jail), tpm_storage)
505     } else {
506         // Path used inside cros_sdk which does not have /run/vm.
507         (None, PathBuf::from("/tmp/tpm-simulator"))
508     };
509 
510     let backend = SoftwareTpm::new(tpm_storage).context("failed to create SoftwareTpm")?;
511     let dev = virtio::Tpm::new(Box::new(backend), virtio::base_features(protection_type));
512 
513     Ok(VirtioDeviceStub {
514         dev: Box::new(dev),
515         jail,
516     })
517 }
518 
519 #[cfg(all(feature = "vtpm", target_arch = "x86_64"))]
create_vtpm_proxy_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, ) -> DeviceResult520 pub fn create_vtpm_proxy_device(
521     protection_type: ProtectionType,
522     jail_config: &Option<JailConfig>,
523 ) -> DeviceResult {
524     let jail = if let Some(jail_config) = jail_config {
525         let mut config = SandboxConfig::new(jail_config, "vtpm_proxy_device");
526         config.bind_mounts = true;
527         let mut jail =
528             create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
529         let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
530         jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
531         Some(jail)
532     } else {
533         None
534     };
535 
536     let backend = VtpmProxy::new();
537     let dev = virtio::Tpm::new(Box::new(backend), virtio::base_features(protection_type));
538 
539     Ok(VirtioDeviceStub {
540         dev: Box::new(dev),
541         jail,
542     })
543 }
544 
create_single_touch_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, single_touch_spec: &TouchDeviceOption, idx: u32, ) -> DeviceResult545 pub fn create_single_touch_device(
546     protection_type: ProtectionType,
547     jail_config: &Option<JailConfig>,
548     single_touch_spec: &TouchDeviceOption,
549     idx: u32,
550 ) -> DeviceResult {
551     let socket = single_touch_spec
552         .get_path()
553         .into_unix_stream()
554         .context("failed configuring virtio single touch")?;
555 
556     let (width, height) = single_touch_spec.get_size();
557     let dev = virtio::new_single_touch(
558         idx,
559         socket,
560         width,
561         height,
562         virtio::base_features(protection_type),
563     )
564     .context("failed to set up input device")?;
565     Ok(VirtioDeviceStub {
566         dev: Box::new(dev),
567         jail: simple_jail(jail_config, "input_device")?,
568     })
569 }
570 
create_multi_touch_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, multi_touch_spec: &TouchDeviceOption, idx: u32, ) -> DeviceResult571 pub fn create_multi_touch_device(
572     protection_type: ProtectionType,
573     jail_config: &Option<JailConfig>,
574     multi_touch_spec: &TouchDeviceOption,
575     idx: u32,
576 ) -> DeviceResult {
577     let socket = multi_touch_spec
578         .get_path()
579         .into_unix_stream()
580         .context("failed configuring virtio multi touch")?;
581 
582     let (width, height) = multi_touch_spec.get_size();
583     let dev = virtio::new_multi_touch(
584         idx,
585         socket,
586         width,
587         height,
588         virtio::base_features(protection_type),
589     )
590     .context("failed to set up input device")?;
591 
592     Ok(VirtioDeviceStub {
593         dev: Box::new(dev),
594         jail: simple_jail(jail_config, "input_device")?,
595     })
596 }
597 
create_trackpad_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, trackpad_spec: &TouchDeviceOption, idx: u32, ) -> DeviceResult598 pub fn create_trackpad_device(
599     protection_type: ProtectionType,
600     jail_config: &Option<JailConfig>,
601     trackpad_spec: &TouchDeviceOption,
602     idx: u32,
603 ) -> DeviceResult {
604     let socket = trackpad_spec
605         .get_path()
606         .into_unix_stream()
607         .context("failed configuring virtio trackpad")?;
608 
609     let (width, height) = trackpad_spec.get_size();
610     let dev = virtio::new_trackpad(
611         idx,
612         socket,
613         width,
614         height,
615         virtio::base_features(protection_type),
616     )
617     .context("failed to set up input device")?;
618 
619     Ok(VirtioDeviceStub {
620         dev: Box::new(dev),
621         jail: simple_jail(jail_config, "input_device")?,
622     })
623 }
624 
create_mouse_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, mouse_socket: T, idx: u32, ) -> DeviceResult625 pub fn create_mouse_device<T: IntoUnixStream>(
626     protection_type: ProtectionType,
627     jail_config: &Option<JailConfig>,
628     mouse_socket: T,
629     idx: u32,
630 ) -> DeviceResult {
631     let socket = mouse_socket
632         .into_unix_stream()
633         .context("failed configuring virtio mouse")?;
634 
635     let dev = virtio::new_mouse(idx, socket, virtio::base_features(protection_type))
636         .context("failed to set up input device")?;
637 
638     Ok(VirtioDeviceStub {
639         dev: Box::new(dev),
640         jail: simple_jail(jail_config, "input_device")?,
641     })
642 }
643 
create_keyboard_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, keyboard_socket: T, idx: u32, ) -> DeviceResult644 pub fn create_keyboard_device<T: IntoUnixStream>(
645     protection_type: ProtectionType,
646     jail_config: &Option<JailConfig>,
647     keyboard_socket: T,
648     idx: u32,
649 ) -> DeviceResult {
650     let socket = keyboard_socket
651         .into_unix_stream()
652         .context("failed configuring virtio keyboard")?;
653 
654     let dev = virtio::new_keyboard(idx, socket, virtio::base_features(protection_type))
655         .context("failed to set up input device")?;
656 
657     Ok(VirtioDeviceStub {
658         dev: Box::new(dev),
659         jail: simple_jail(jail_config, "input_device")?,
660     })
661 }
662 
create_switches_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, switches_socket: T, idx: u32, ) -> DeviceResult663 pub fn create_switches_device<T: IntoUnixStream>(
664     protection_type: ProtectionType,
665     jail_config: &Option<JailConfig>,
666     switches_socket: T,
667     idx: u32,
668 ) -> DeviceResult {
669     let socket = switches_socket
670         .into_unix_stream()
671         .context("failed configuring virtio switches")?;
672 
673     let dev = virtio::new_switches(idx, socket, virtio::base_features(protection_type))
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_vinput_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, dev_path: &Path, ) -> DeviceResult682 pub fn create_vinput_device(
683     protection_type: ProtectionType,
684     jail_config: &Option<JailConfig>,
685     dev_path: &Path,
686 ) -> DeviceResult {
687     let dev_file = OpenOptions::new()
688         .read(true)
689         .write(true)
690         .open(dev_path)
691         .with_context(|| format!("failed to open vinput device {}", dev_path.display()))?;
692 
693     let dev = virtio::new_evdev(dev_file, virtio::base_features(protection_type))
694         .context("failed to set up input device")?;
695 
696     Ok(VirtioDeviceStub {
697         dev: Box::new(dev),
698         jail: simple_jail(jail_config, "input_device")?,
699     })
700 }
701 
702 #[cfg(feature = "balloon")]
create_balloon_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, mode: BalloonMode, tube: Tube, wss_tube: Option<Tube>, inflate_tube: Option<Tube>, init_balloon_size: u64, enabled_features: u64, registered_evt_q: Option<SendTube>, ) -> DeviceResult703 pub fn create_balloon_device(
704     protection_type: ProtectionType,
705     jail_config: &Option<JailConfig>,
706     mode: BalloonMode,
707     tube: Tube,
708     wss_tube: Option<Tube>,
709     inflate_tube: Option<Tube>,
710     init_balloon_size: u64,
711     enabled_features: u64,
712     registered_evt_q: Option<SendTube>,
713 ) -> DeviceResult {
714     let dev = virtio::Balloon::new(
715         virtio::base_features(protection_type),
716         tube,
717         wss_tube,
718         inflate_tube,
719         init_balloon_size,
720         mode,
721         enabled_features,
722         registered_evt_q,
723     )
724     .context("failed to create balloon")?;
725 
726     Ok(VirtioDeviceStub {
727         dev: Box::new(dev),
728         jail: simple_jail(jail_config, "balloon_device")?,
729     })
730 }
731 
732 /// Generic method for creating a network device. `create_device` is a closure that takes the virtio
733 /// features and number of queue pairs as parameters, and is responsible for creating the device
734 /// itself.
create_net_device<F, T>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, mut vq_pairs: u16, vcpu_count: usize, policy: &str, create_device: F, ) -> DeviceResult where F: FnOnce(u64, u16) -> Result<T>, T: VirtioDevice + 'static,735 pub fn create_net_device<F, T>(
736     protection_type: ProtectionType,
737     jail_config: &Option<JailConfig>,
738     mut vq_pairs: u16,
739     vcpu_count: usize,
740     policy: &str,
741     create_device: F,
742 ) -> DeviceResult
743 where
744     F: FnOnce(u64, u16) -> Result<T>,
745     T: VirtioDevice + 'static,
746 {
747     if vcpu_count < vq_pairs as usize {
748         warn!("the number of net vq pairs must not exceed the vcpu count, falling back to single queue mode");
749         vq_pairs = 1;
750     }
751     let features = virtio::base_features(protection_type);
752 
753     let dev = create_device(features, vq_pairs)?;
754 
755     Ok(VirtioDeviceStub {
756         dev: Box::new(dev) as Box<dyn VirtioDevice>,
757         jail: simple_jail(jail_config, policy)?,
758     })
759 }
760 
761 /// Create a new tap interface based on NetParametersMode.
create_tap_for_net_device( mode: &NetParametersMode, multi_vq: bool, ) -> DeviceResult<(Tap, Option<MacAddress>)>762 pub fn create_tap_for_net_device(
763     mode: &NetParametersMode,
764     multi_vq: bool,
765 ) -> DeviceResult<(Tap, Option<MacAddress>)> {
766     match mode {
767         NetParametersMode::TapName { tap_name, mac } => {
768             let tap = Tap::new_with_name(tap_name.as_bytes(), true, multi_vq)
769                 .map_err(NetError::TapOpen)?;
770             Ok((tap, *mac))
771         }
772         NetParametersMode::TapFd { tap_fd, mac } => {
773             // Safe because we ensure that we get a unique handle to the fd.
774             let tap = unsafe {
775                 Tap::from_raw_descriptor(
776                     validate_raw_descriptor(*tap_fd)
777                         .context("failed to validate tap descriptor")?,
778                 )
779                 .context("failed to create tap device")?
780             };
781             Ok((tap, *mac))
782         }
783         NetParametersMode::RawConfig {
784             host_ip,
785             netmask,
786             mac,
787         } => {
788             let tap = Tap::new(true, multi_vq).map_err(NetError::TapOpen)?;
789             tap.set_ip_addr(*host_ip).map_err(NetError::TapSetIp)?;
790             tap.set_netmask(*netmask).map_err(NetError::TapSetNetmask)?;
791             tap.set_mac_address(*mac)
792                 .map_err(NetError::TapSetMacAddress)?;
793             tap.enable().map_err(NetError::TapEnable)?;
794             Ok((tap, None))
795         }
796     }
797 }
798 
799 /// Returns a virtio network device created from a new TAP device.
create_virtio_net_device_from_tap<T: TapT + ReadNotifier + 'static>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, vq_pairs: u16, vcpu_count: usize, tap: T, mac: Option<MacAddress>, ) -> DeviceResult800 pub fn create_virtio_net_device_from_tap<T: TapT + ReadNotifier + 'static>(
801     protection_type: ProtectionType,
802     jail_config: &Option<JailConfig>,
803     vq_pairs: u16,
804     vcpu_count: usize,
805     tap: T,
806     mac: Option<MacAddress>,
807 ) -> DeviceResult {
808     create_net_device(
809         protection_type,
810         jail_config,
811         vq_pairs,
812         vcpu_count,
813         "net_device",
814         move |features, vq_pairs| {
815             virtio::Net::new(features, tap, vq_pairs, mac)
816                 .context("failed to set up virtio networking")
817         },
818     )
819 }
820 
821 /// Returns a virtio-vhost network device created from a new TAP device.
create_virtio_vhost_net_device_from_tap<T: TapT + 'static>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, vq_pairs: u16, vcpu_count: usize, vhost_net_device_path: PathBuf, tap: T, mac: Option<MacAddress>, ) -> DeviceResult822 pub fn create_virtio_vhost_net_device_from_tap<T: TapT + 'static>(
823     protection_type: ProtectionType,
824     jail_config: &Option<JailConfig>,
825     vq_pairs: u16,
826     vcpu_count: usize,
827     vhost_net_device_path: PathBuf,
828     tap: T,
829     mac: Option<MacAddress>,
830 ) -> DeviceResult {
831     create_net_device(
832         protection_type,
833         jail_config,
834         vq_pairs,
835         vcpu_count,
836         "vhost_net_device",
837         move |features, _vq_pairs| {
838             virtio::vhost::Net::<T, vhost::Net<T>>::new(&vhost_net_device_path, features, tap, mac)
839                 .context("failed to set up virtio-vhost networking")
840         },
841     )
842 }
843 
create_vhost_user_net_device( protection_type: ProtectionType, opt: &VhostUserOption, ) -> DeviceResult844 pub fn create_vhost_user_net_device(
845     protection_type: ProtectionType,
846     opt: &VhostUserOption,
847 ) -> DeviceResult {
848     let dev = VhostUserVirtioDevice::new_net(
849         virtio::base_features(protection_type),
850         vhost_user_connection(&opt.socket)?,
851     )
852     .context("failed to set up vhost-user net device")?;
853 
854     Ok(VirtioDeviceStub {
855         dev: Box::new(dev),
856         // no sandbox here because virtqueue handling is exported to a different process.
857         jail: None,
858     })
859 }
860 
create_vhost_user_vsock_device( protection_type: ProtectionType, opt: &VhostUserOption, ) -> DeviceResult861 pub fn create_vhost_user_vsock_device(
862     protection_type: ProtectionType,
863     opt: &VhostUserOption,
864 ) -> DeviceResult {
865     let dev = VhostUserVirtioDevice::new_vsock(
866         virtio::base_features(protection_type),
867         vhost_user_connection(&opt.socket)?,
868     )
869     .context("failed to set up vhost-user vsock device")?;
870 
871     Ok(VirtioDeviceStub {
872         dev: Box::new(dev),
873         // no sandbox here because virtqueue handling is exported to a different process.
874         jail: None,
875     })
876 }
877 
create_vhost_user_wl_device( protection_type: ProtectionType, opt: &VhostUserOption, ) -> DeviceResult878 pub fn create_vhost_user_wl_device(
879     protection_type: ProtectionType,
880     opt: &VhostUserOption,
881 ) -> DeviceResult {
882     // The crosvm wl device expects us to connect the tube before it will accept a vhost-user
883     // connection.
884     let dev = VhostUserVirtioDevice::new_wl(
885         virtio::base_features(protection_type),
886         vhost_user_connection(&opt.socket)?,
887     )
888     .context("failed to set up vhost-user wl device")?;
889 
890     Ok(VirtioDeviceStub {
891         dev: Box::new(dev),
892         // no sandbox here because virtqueue handling is exported to a different process.
893         jail: None,
894     })
895 }
896 
create_wayland_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, wayland_socket_paths: &BTreeMap<String, PathBuf>, resource_bridge: Option<Tube>, ) -> DeviceResult897 pub fn create_wayland_device(
898     protection_type: ProtectionType,
899     jail_config: &Option<JailConfig>,
900     wayland_socket_paths: &BTreeMap<String, PathBuf>,
901     resource_bridge: Option<Tube>,
902 ) -> DeviceResult {
903     let wayland_socket_dirs = wayland_socket_paths
904         .iter()
905         .map(|(_name, path)| path.parent())
906         .collect::<Option<Vec<_>>>()
907         .ok_or_else(|| anyhow!("wayland socket path has no parent or file name"))?;
908 
909     let features = virtio::base_features(protection_type);
910     let dev = virtio::Wl::new(features, wayland_socket_paths.clone(), resource_bridge)
911         .context("failed to create wayland device")?;
912 
913     let jail = if let Some(jail_config) = jail_config {
914         let mut config = SandboxConfig::new(jail_config, "wl_device");
915         config.bind_mounts = true;
916         let mut jail = create_gpu_minijail(&jail_config.pivot_root, &config)?;
917         // Bind mount the wayland socket's directory into jail's root. This is necessary since
918         // each new wayland context must open() the socket. If the wayland socket is ever
919         // destroyed and remade in the same host directory, new connections will be possible
920         // without restarting the wayland device.
921         for dir in &wayland_socket_dirs {
922             jail.mount_bind(dir, dir, true)?;
923         }
924 
925         Some(jail)
926     } else {
927         None
928     };
929 
930     Ok(VirtioDeviceStub {
931         dev: Box::new(dev),
932         jail,
933     })
934 }
935 
936 #[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, ) -> DeviceResult937 pub fn create_video_device(
938     backend: VideoBackendType,
939     protection_type: ProtectionType,
940     jail_config: &Option<JailConfig>,
941     typ: VideoDeviceType,
942     resource_bridge: Tube,
943 ) -> DeviceResult {
944     let jail = if let Some(jail_config) = jail_config {
945         match typ {
946             #[cfg(feature = "video-decoder")]
947             VideoDeviceType::Decoder => {}
948             #[cfg(feature = "video-encoder")]
949             VideoDeviceType::Encoder => {}
950             #[cfg(any(not(feature = "video-decoder"), not(feature = "video-encoder")))]
951             // `typ` is always a VideoDeviceType enabled
952             device_type => unreachable!("Not compiled with {:?} enabled", device_type),
953         };
954         let mut config = SandboxConfig::new(jail_config, "video_device");
955         config.bind_mounts = true;
956         let mut jail =
957             create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
958 
959         let need_drm_device = match backend {
960             #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
961             VideoBackendType::Libvda => true,
962             #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
963             VideoBackendType::LibvdaVd => true,
964             #[cfg(feature = "vaapi")]
965             VideoBackendType::Vaapi => true,
966             #[cfg(feature = "ffmpeg")]
967             VideoBackendType::Ffmpeg => false,
968         };
969 
970         if need_drm_device {
971             // follow the implementation at:
972             // https://chromium.googlesource.com/chromiumos/platform/minigbm/+/c06cc9cccb3cf3c7f9d2aec706c27c34cd6162a0/cros_gralloc/cros_gralloc_driver.cc#90
973             const DRM_NUM_NODES: u32 = 63;
974             const DRM_RENDER_NODE_START: u32 = 128;
975             for offset in 0..DRM_NUM_NODES {
976                 let path_str = format!("/dev/dri/renderD{}", DRM_RENDER_NODE_START + offset);
977                 let dev_dri_path = Path::new(&path_str);
978                 if !dev_dri_path.exists() {
979                     break;
980                 }
981                 jail.mount_bind(dev_dri_path, dev_dri_path, false)?;
982             }
983         }
984 
985         #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
986         {
987             // Device nodes used by libdrm through minigbm in libvda on AMD devices.
988             let sys_dev_char_path = Path::new("/sys/dev/char");
989             jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false)?;
990             let sys_devices_path = Path::new("/sys/devices");
991             jail.mount_bind(sys_devices_path, sys_devices_path, false)?;
992 
993             // Required for loading dri libraries loaded by minigbm on AMD devices.
994             jail_mount_bind_if_exists(&mut jail, &["/usr/lib64"])?;
995         }
996 
997         // Device nodes required by libchrome which establishes Mojo connection in libvda.
998         let dev_urandom_path = Path::new("/dev/urandom");
999         jail.mount_bind(dev_urandom_path, dev_urandom_path, false)?;
1000         let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
1001         jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
1002 
1003         Some(jail)
1004     } else {
1005         None
1006     };
1007 
1008     Ok(VirtioDeviceStub {
1009         dev: Box::new(devices::virtio::VideoDevice::new(
1010             virtio::base_features(protection_type),
1011             typ,
1012             backend,
1013             Some(resource_bridge),
1014         )),
1015         jail,
1016     })
1017 }
1018 
1019 #[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<()>1020 pub fn register_video_device(
1021     backend: VideoBackendType,
1022     devs: &mut Vec<VirtioDeviceStub>,
1023     video_tube: Tube,
1024     protection_type: ProtectionType,
1025     jail_config: &Option<JailConfig>,
1026     typ: VideoDeviceType,
1027 ) -> Result<()> {
1028     devs.push(create_video_device(
1029         backend,
1030         protection_type,
1031         jail_config,
1032         typ,
1033         video_tube,
1034     )?);
1035     Ok(())
1036 }
1037 
create_vhost_user_video_device( protection_type: ProtectionType, opt: &VhostUserOption, device_type: VideoDeviceType, ) -> DeviceResult1038 pub fn create_vhost_user_video_device(
1039     protection_type: ProtectionType,
1040     opt: &VhostUserOption,
1041     device_type: VideoDeviceType,
1042 ) -> DeviceResult {
1043     let dev = VhostUserVirtioDevice::new_video(
1044         virtio::base_features(protection_type),
1045         vhost_user_connection(&opt.socket)?,
1046         device_type,
1047     )
1048     .context("failed to set up vhost-user video device")?;
1049 
1050     Ok(VirtioDeviceStub {
1051         dev: Box::new(dev),
1052         // no sandbox here because virtqueue handling is exported to a different process.
1053         jail: None,
1054     })
1055 }
1056 
1057 impl VirtioDeviceBuilder for &VsockConfig {
1058     const NAME: &'static str = "vhost_vsock";
1059 
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>1060     fn create_virtio_device(
1061         self,
1062         protection_type: ProtectionType,
1063     ) -> anyhow::Result<Box<dyn VirtioDevice>> {
1064         let features = virtio::base_features(protection_type);
1065 
1066         let dev = virtio::vhost::Vsock::new(features, self)
1067             .context("failed to set up virtual socket device")?;
1068 
1069         Ok(Box::new(dev))
1070     }
1071 
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDevice>>1072     fn create_vhost_user_device(
1073         self,
1074         keep_rds: &mut Vec<RawDescriptor>,
1075     ) -> anyhow::Result<Box<dyn VhostUserDevice>> {
1076         let vsock_device = VhostUserVsockDevice::new(self.cid, &self.vhost_device)?;
1077 
1078         keep_rds.push(vsock_device.as_raw_descriptor());
1079 
1080         Ok(Box::new(vsock_device))
1081     }
1082 }
1083 
create_fs_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, uid_map: &str, gid_map: &str, src: &Path, tag: &str, fs_cfg: virtio::fs::passthrough::Config, device_tube: Tube, ) -> DeviceResult1084 pub fn create_fs_device(
1085     protection_type: ProtectionType,
1086     jail_config: &Option<JailConfig>,
1087     uid_map: &str,
1088     gid_map: &str,
1089     src: &Path,
1090     tag: &str,
1091     fs_cfg: virtio::fs::passthrough::Config,
1092     device_tube: Tube,
1093 ) -> DeviceResult {
1094     let max_open_files =
1095         base::get_max_open_files().context("failed to get max number of open files")?;
1096     let j = if let Some(jail_config) = jail_config {
1097         let mut config = SandboxConfig::new(jail_config, "fs_device");
1098         config.limit_caps = false;
1099         config.ugid_map = Some((uid_map, gid_map));
1100         // We want bind mounts from the parent namespaces to propagate into the fs device's
1101         // namespace.
1102         config.remount_mode = Some(libc::MS_SLAVE);
1103         create_sandbox_minijail(src, max_open_files, &config)?
1104     } else {
1105         create_base_minijail(src, max_open_files)?
1106     };
1107 
1108     let features = virtio::base_features(protection_type);
1109     // TODO(chirantan): Use more than one worker once the kernel driver has been fixed to not panic
1110     // when num_queues > 1.
1111     let dev = virtio::fs::Fs::new(features, tag, 1, fs_cfg, device_tube)
1112         .context("failed to create fs device")?;
1113 
1114     Ok(VirtioDeviceStub {
1115         dev: Box::new(dev),
1116         jail: Some(j),
1117     })
1118 }
1119 
create_9p_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, uid_map: &str, gid_map: &str, src: &Path, tag: &str, mut p9_cfg: p9::Config, ) -> DeviceResult1120 pub fn create_9p_device(
1121     protection_type: ProtectionType,
1122     jail_config: &Option<JailConfig>,
1123     uid_map: &str,
1124     gid_map: &str,
1125     src: &Path,
1126     tag: &str,
1127     mut p9_cfg: p9::Config,
1128 ) -> DeviceResult {
1129     let max_open_files =
1130         base::get_max_open_files().context("failed to get max number of open files")?;
1131     let (jail, root) = if let Some(jail_config) = jail_config {
1132         let mut config = SandboxConfig::new(jail_config, "9p_device");
1133         config.limit_caps = false;
1134         config.ugid_map = Some((uid_map, gid_map));
1135         // We want bind mounts from the parent namespaces to propagate into the 9p server's
1136         // namespace.
1137         config.remount_mode = Some(libc::MS_SLAVE);
1138         let jail = create_sandbox_minijail(src, max_open_files, &config)?;
1139 
1140         //  The shared directory becomes the root of the device's file system.
1141         let root = Path::new("/");
1142         (Some(jail), root)
1143     } else {
1144         // There's no mount namespace so we tell the server to treat the source directory as the
1145         // root.
1146         (None, src)
1147     };
1148 
1149     let features = virtio::base_features(protection_type);
1150     p9_cfg.root = root.into();
1151     let dev = virtio::P9::new(features, tag, p9_cfg).context("failed to create 9p device")?;
1152 
1153     Ok(VirtioDeviceStub {
1154         dev: Box::new(dev),
1155         jail,
1156     })
1157 }
1158 
create_pmem_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, vm: &mut impl Vm, resources: &mut SystemAllocator, disk: &DiskOption, index: usize, pmem_device_tube: Tube, ) -> DeviceResult1159 pub fn create_pmem_device(
1160     protection_type: ProtectionType,
1161     jail_config: &Option<JailConfig>,
1162     vm: &mut impl Vm,
1163     resources: &mut SystemAllocator,
1164     disk: &DiskOption,
1165     index: usize,
1166     pmem_device_tube: Tube,
1167 ) -> DeviceResult {
1168     let fd = open_file(
1169         &disk.path,
1170         OpenOptions::new().read(true).write(!disk.read_only),
1171     )
1172     .with_context(|| format!("failed to load disk image {}", disk.path.display()))?;
1173 
1174     let (disk_size, arena_size) = {
1175         let metadata = std::fs::metadata(&disk.path).with_context(|| {
1176             format!("failed to get disk image {} metadata", disk.path.display())
1177         })?;
1178         let disk_len = metadata.len();
1179         // Linux requires pmem region sizes to be 2 MiB aligned. Linux will fill any partial page
1180         // at the end of an mmap'd file and won't write back beyond the actual file length, but if
1181         // we just align the size of the file to 2 MiB then access beyond the last page of the
1182         // mapped file will generate SIGBUS. So use a memory mapping arena that will provide
1183         // padding up to 2 MiB.
1184         let alignment = 2 * 1024 * 1024;
1185         let align_adjust = if disk_len % alignment != 0 {
1186             alignment - (disk_len % alignment)
1187         } else {
1188             0
1189         };
1190         (
1191             disk_len,
1192             disk_len
1193                 .checked_add(align_adjust)
1194                 .ok_or_else(|| anyhow!("pmem device image too big"))?,
1195         )
1196     };
1197 
1198     let protection = {
1199         if disk.read_only {
1200             Protection::read()
1201         } else {
1202             Protection::read_write()
1203         }
1204     };
1205 
1206     let arena = {
1207         // Conversion from u64 to usize may fail on 32bit system.
1208         let arena_size = usize::try_from(arena_size).context("pmem device image too big")?;
1209         let disk_size = usize::try_from(disk_size).context("pmem device image too big")?;
1210 
1211         let mut arena =
1212             MemoryMappingArena::new(arena_size).context("failed to reserve pmem memory")?;
1213         arena
1214             .add_fd_offset_protection(0, disk_size, &fd, 0, protection)
1215             .context("failed to reserve pmem memory")?;
1216 
1217         // If the disk is not a multiple of the page size, the OS will fill the remaining part
1218         // of the page with zeroes. However, the anonymous mapping added below must start on a
1219         // page boundary, so round up the size before calculating the offset of the anon region.
1220         let disk_size = round_up_to_page_size(disk_size);
1221 
1222         if arena_size > disk_size {
1223             // Add an anonymous region with the same protection as the disk mapping if the arena
1224             // size was aligned.
1225             arena
1226                 .add_anon_protection(disk_size, arena_size - disk_size, protection)
1227                 .context("failed to reserve pmem padding")?;
1228         }
1229         arena
1230     };
1231 
1232     let mapping_address = resources
1233         .allocate_mmio(
1234             arena_size,
1235             Alloc::PmemDevice(index),
1236             format!("pmem_disk_image_{}", index),
1237             AllocOptions::new()
1238                 .top_down(true)
1239                 .prefetchable(true)
1240                 // Linux kernel requires pmem namespaces to be 128 MiB aligned.
1241                 .align(128 * 1024 * 1024), /* 128 MiB */
1242         )
1243         .context("failed to allocate memory for pmem device")?;
1244 
1245     let slot = vm
1246         .add_memory_region(
1247             GuestAddress(mapping_address),
1248             Box::new(arena),
1249             /* read_only = */ disk.read_only,
1250             /* log_dirty_pages = */ false,
1251         )
1252         .context("failed to add pmem device memory")?;
1253 
1254     let dev = virtio::Pmem::new(
1255         virtio::base_features(protection_type),
1256         fd,
1257         GuestAddress(mapping_address),
1258         slot,
1259         arena_size,
1260         Some(pmem_device_tube),
1261     )
1262     .context("failed to create pmem device")?;
1263 
1264     Ok(VirtioDeviceStub {
1265         dev: Box::new(dev) as Box<dyn VirtioDevice>,
1266         jail: simple_jail(jail_config, "pmem_device")?,
1267     })
1268 }
1269 
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, ) -> DeviceResult1270 pub fn create_iommu_device(
1271     protection_type: ProtectionType,
1272     jail_config: &Option<JailConfig>,
1273     iova_max_addr: u64,
1274     endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1275     hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
1276     translate_response_senders: Option<BTreeMap<u32, Tube>>,
1277     translate_request_rx: Option<Tube>,
1278     iommu_device_tube: Tube,
1279 ) -> DeviceResult {
1280     let dev = virtio::Iommu::new(
1281         virtio::base_features(protection_type),
1282         endpoints,
1283         iova_max_addr,
1284         hp_endpoints_ranges,
1285         translate_response_senders,
1286         translate_request_rx,
1287         Some(iommu_device_tube),
1288     )
1289     .context("failed to create IOMMU device")?;
1290 
1291     Ok(VirtioDeviceStub {
1292         dev: Box::new(dev),
1293         jail: simple_jail(jail_config, "iommu_device")?,
1294     })
1295 }
1296 
add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error>1297 fn add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error> {
1298     if let Some(path) = &param.path {
1299         if let SerialType::SystemSerialType = param.type_ {
1300             if let Some(parent) = path.as_path().parent() {
1301                 if parent.exists() {
1302                     info!("Bind mounting dir {}", parent.display());
1303                     jail.mount_bind(parent, parent, true)?;
1304                 }
1305             }
1306         }
1307     }
1308     Ok(())
1309 }
1310 
1311 /// For creating console virtio devices.
1312 impl VirtioDeviceBuilder for &SerialParameters {
1313     const NAME: &'static str = "serial";
1314 
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>1315     fn create_virtio_device(
1316         self,
1317         protection_type: ProtectionType,
1318     ) -> anyhow::Result<Box<dyn VirtioDevice>> {
1319         let mut keep_rds = Vec::new();
1320         let evt = Event::new().context("failed to create event")?;
1321 
1322         // TODO(b/238440998): Switch back to AsyncConsole in android.
1323         Ok(Box::new(
1324             self.create_serial_device::<Console>(protection_type, &evt, &mut keep_rds)
1325                 .context("failed to create console device")?,
1326         ))
1327     }
1328 
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDevice>>1329     fn create_vhost_user_device(
1330         self,
1331         keep_rds: &mut Vec<RawDescriptor>,
1332     ) -> anyhow::Result<Box<dyn VhostUserDevice>> {
1333         Ok(Box::new(virtio::vhost::user::create_vu_console_device(
1334             self, keep_rds,
1335         )?))
1336     }
1337 
create_jail( &self, jail_config: &Option<JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>1338     fn create_jail(
1339         &self,
1340         jail_config: &Option<JailConfig>,
1341         virtio_transport: VirtioDeviceType,
1342     ) -> anyhow::Result<Option<Minijail>> {
1343         if let Some(jail_config) = jail_config {
1344             let policy = virtio_transport.seccomp_policy_file("serial");
1345             let mut config = SandboxConfig::new(jail_config, &policy);
1346             config.bind_mounts = true;
1347             let mut jail =
1348                 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
1349             add_bind_mounts(self, &mut jail)
1350                 .context("failed to add bind mounts for console device")?;
1351             Ok(Some(jail))
1352         } else {
1353             Ok(None)
1354         }
1355     }
1356 }
1357 
1358 #[cfg(feature = "audio")]
create_sound_device( path: &Path, protection_type: ProtectionType, jail_config: &Option<JailConfig>, ) -> DeviceResult1359 pub fn create_sound_device(
1360     path: &Path,
1361     protection_type: ProtectionType,
1362     jail_config: &Option<JailConfig>,
1363 ) -> DeviceResult {
1364     let dev = virtio::new_sound(path, virtio::base_features(protection_type))
1365         .context("failed to create sound device")?;
1366 
1367     Ok(VirtioDeviceStub {
1368         dev: Box::new(dev),
1369         jail: simple_jail(jail_config, "vios_audio_device")?,
1370     })
1371 }
1372 
1373 #[allow(clippy::large_enum_variant)]
1374 pub enum VfioDeviceVariant {
1375     Pci(VfioPciDevice),
1376     Platform(VfioPlatformDevice),
1377 }
1378 
create_vfio_device( jail_config: &Option<JailConfig>, vm: &impl Vm, resources: &mut SystemAllocator, irq_control_tubes: &mut Vec<Tube>, control_tubes: &mut Vec<TaggedControlTube>, vfio_path: &Path, hotplug: bool, hotplug_bus: Option<u8>, guest_address: Option<PciAddress>, coiommu_endpoints: Option<&mut Vec<u16>>, iommu_dev: IommuDevType, #[cfg(feature = "direct")] is_intel_lpss: bool, ) -> DeviceResult<(VfioDeviceVariant, Option<Minijail>, Option<VfioWrapper>)>1379 pub fn create_vfio_device(
1380     jail_config: &Option<JailConfig>,
1381     vm: &impl Vm,
1382     resources: &mut SystemAllocator,
1383     irq_control_tubes: &mut Vec<Tube>,
1384     control_tubes: &mut Vec<TaggedControlTube>,
1385     vfio_path: &Path,
1386     hotplug: bool,
1387     hotplug_bus: Option<u8>,
1388     guest_address: Option<PciAddress>,
1389     coiommu_endpoints: Option<&mut Vec<u16>>,
1390     iommu_dev: IommuDevType,
1391     #[cfg(feature = "direct")] is_intel_lpss: bool,
1392 ) -> DeviceResult<(VfioDeviceVariant, Option<Minijail>, Option<VfioWrapper>)> {
1393     let vfio_container = VfioCommonSetup::vfio_get_container(iommu_dev, Some(vfio_path))
1394         .context("failed to get vfio container")?;
1395 
1396     let (vfio_host_tube_mem, vfio_device_tube_mem) =
1397         Tube::pair().context("failed to create tube")?;
1398     control_tubes.push(TaggedControlTube::VmMemory {
1399         tube: vfio_host_tube_mem,
1400         expose_with_viommu: false,
1401     });
1402 
1403     let (vfio_host_tube_vm, vfio_device_tube_vm) = Tube::pair().context("failed to create tube")?;
1404     control_tubes.push(TaggedControlTube::Vm(vfio_host_tube_vm));
1405 
1406     let vfio_device = VfioDevice::new_passthrough(
1407         &vfio_path,
1408         vm,
1409         vfio_container.clone(),
1410         iommu_dev != IommuDevType::NoIommu,
1411     )
1412     .context("failed to create vfio device")?;
1413 
1414     match vfio_device.device_type() {
1415         VfioDeviceType::Pci => {
1416             let (vfio_host_tube_msi, vfio_device_tube_msi) =
1417                 Tube::pair().context("failed to create tube")?;
1418             irq_control_tubes.push(vfio_host_tube_msi);
1419 
1420             let (vfio_host_tube_msix, vfio_device_tube_msix) =
1421                 Tube::pair().context("failed to create tube")?;
1422             irq_control_tubes.push(vfio_host_tube_msix);
1423 
1424             let mut vfio_pci_device = VfioPciDevice::new(
1425                 vfio_path,
1426                 vfio_device,
1427                 hotplug,
1428                 hotplug_bus,
1429                 guest_address,
1430                 vfio_device_tube_msi,
1431                 vfio_device_tube_msix,
1432                 vfio_device_tube_mem,
1433                 vfio_device_tube_vm,
1434                 #[cfg(feature = "direct")]
1435                 is_intel_lpss,
1436             )?;
1437             // early reservation for pass-through PCI devices.
1438             let endpoint_addr = vfio_pci_device
1439                 .allocate_address(resources)
1440                 .context("failed to allocate resources early for vfio pci dev")?;
1441 
1442             let viommu_mapper = match iommu_dev {
1443                 IommuDevType::NoIommu => None,
1444                 IommuDevType::VirtioIommu => {
1445                     Some(VfioWrapper::new(vfio_container, vm.get_memory().clone()))
1446                 }
1447                 IommuDevType::CoIommu => {
1448                     if let Some(endpoints) = coiommu_endpoints {
1449                         endpoints.push(endpoint_addr.to_u32() as u16);
1450                     } else {
1451                         bail!("Missed coiommu_endpoints vector to store the endpoint addr");
1452                     }
1453                     None
1454                 }
1455             };
1456 
1457             if hotplug {
1458                 Ok((VfioDeviceVariant::Pci(vfio_pci_device), None, viommu_mapper))
1459             } else {
1460                 Ok((
1461                     VfioDeviceVariant::Pci(vfio_pci_device),
1462                     simple_jail(jail_config, "vfio_device")?,
1463                     viommu_mapper,
1464                 ))
1465             }
1466         }
1467         VfioDeviceType::Platform => {
1468             if guest_address.is_some() {
1469                 bail!("guest-address is not supported for VFIO platform devices");
1470             }
1471 
1472             if hotplug {
1473                 bail!("hotplug is not supported for VFIO platform devices");
1474             }
1475 
1476             let vfio_plat_dev = VfioPlatformDevice::new(vfio_device, vfio_device_tube_mem);
1477 
1478             Ok((
1479                 VfioDeviceVariant::Platform(vfio_plat_dev),
1480                 simple_jail(jail_config, "vfio_platform_device")?,
1481                 None,
1482             ))
1483         }
1484     }
1485 }
1486 
1487 /// 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>)>1488 pub fn setup_virtio_access_platform(
1489     resources: &mut SystemAllocator,
1490     iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1491     devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)],
1492 ) -> DeviceResult<(Option<BTreeMap<u32, Tube>>, Option<Tube>)> {
1493     let mut translate_response_senders: Option<
1494         BTreeMap<
1495             u32, // endpoint id
1496             Tube,
1497         >,
1498     > = None;
1499     let mut tube_pair: Option<(Tube, Tube)> = None;
1500 
1501     for dev in devices.iter_mut() {
1502         if let Some(pci_dev) = dev.0.as_pci_device_mut() {
1503             if pci_dev.supports_iommu() {
1504                 let endpoint_id = pci_dev
1505                     .allocate_address(resources)
1506                     .context("failed to allocate resources for pci dev")?
1507                     .to_u32();
1508                 let mapper: Arc<Mutex<Box<dyn MemoryMapperTrait>>> =
1509                     Arc::new(Mutex::new(Box::new(BasicMemoryMapper::new(u64::MAX))));
1510                 let (request_tx, _request_rx) =
1511                     tube_pair.get_or_insert_with(|| Tube::pair().unwrap());
1512                 let CreateIpcMapperRet {
1513                     mapper: ipc_mapper,
1514                     response_tx,
1515                 } = create_ipc_mapper(
1516                     endpoint_id,
1517                     #[allow(deprecated)]
1518                     request_tx.try_clone()?,
1519                 );
1520                 translate_response_senders
1521                     .get_or_insert_with(BTreeMap::new)
1522                     .insert(endpoint_id, response_tx);
1523                 iommu_attached_endpoints.insert(endpoint_id, mapper);
1524                 pci_dev.set_iommu(ipc_mapper)?;
1525             }
1526         }
1527     }
1528 
1529     Ok((
1530         translate_response_senders,
1531         tube_pair.map(|(_request_tx, request_rx)| request_rx),
1532     ))
1533 }
1534