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