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