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) = ¶m.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