1use std::collections::BTreeMap;
6use std::collections::BTreeSet;
7use std::convert::TryFrom;
8use std::fs::File;
9use std::fs::OpenOptions;
10use std::io::ErrorKind;
11use std::ops::RangeInclusive;
12use std::os::unix::net::UnixStream;
13use std::path::Path;
14use std::path::PathBuf;
15use std::str;
16use std::sync::Arc;
17use std::time::Duration;
18use std::time::Instant;
19
20use anyhow::anyhow;
21use anyhow::bail;
22use anyhow::Context;
23use anyhow::Result;
24use arch::VirtioDeviceStub;
25use base::linux::MemfdSeals;
26use base::sys::SharedMemoryLinux;
27use base::*;
28use device_virtio_block::DiskOption;
29#[cfg(feature = "net")]
30use device_virtio_net::create_tap_for_net_device;
31#[cfg(feature = "net")]
32use device_virtio_net::NetBackend;
33#[cfg(feature = "net")]
34use device_virtio_net::NetParameters;
35use device_virtio_vsock::VsockConfig;
36use devices::serial_device::SerialParameters;
37use devices::vfio::VfioContainerManager;
38use devices::virtio;
39#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
40use devices::virtio::device_constants::video::VideoBackendType;
41#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
42use devices::virtio::device_constants::video::VideoDeviceType;
43use devices::virtio::ipc_memory_mapper::create_ipc_mapper;
44use devices::virtio::ipc_memory_mapper::CreateIpcMapperRet;
45use devices::virtio::memory_mapper::BasicMemoryMapper;
46use devices::virtio::memory_mapper::MemoryMapperTrait;
47#[cfg(feature = "pvclock")]
48use devices::virtio::pvclock::PvClock;
49use devices::virtio::vfio_wrapper::VfioWrapper;
50use devices::virtio::vhost_user_backend::VhostUserDeviceBuilder;
51use devices::virtio::vhost_user_backend::VhostUserVsockDevice;
52use devices::virtio::MemSlotConfig;
53use devices::virtio::PmemConfig;
54use devices::virtio::VhostUserFrontend;
55use devices::virtio::VirtioDevice;
56use devices::virtio::VirtioDeviceType;
57use devices::BusDeviceObj;
58use devices::IommuDevType;
59use devices::PciAddress;
60use devices::PciDevice;
61use devices::VfioDevice;
62use devices::VfioDeviceType;
63use devices::VfioPciDevice;
64use devices::VfioPlatformDevice;
65use hypervisor::MemCacheType;
66use hypervisor::ProtectionType;
67use hypervisor::Vm;
68use jail::*;
69use minijail::Minijail;
70use resources::Alloc;
71use resources::AllocOptions;
72use resources::SystemAllocator;
73use sync::Mutex;
74use vm_control::api::VmMemoryClient;
75use vm_control::AnyControlTube;
76use vm_memory::GuestAddress;
77
78use crate::crosvm::config::PmemOption;
79use crate::crosvm::config::VhostUserFrontendOption;
80use crate::crosvm::sys::config::PmemExt2Option;
81
82pub enum TaggedControlTube {
86 Device(Tube),
88 Fs(Tube),
90 Vm(Tube),
92 VmMsync(Tube),
94}
95
96impl AsRef<Tube> for TaggedControlTube {
97 fn as_ref(&self) -> &Tube {
98 use self::TaggedControlTube::*;
99 match &self {
100 Device(tube) | Fs(tube) | Vm(tube) | VmMsync(tube) => tube,
101 }
102 }
103}
104
105impl AsRawDescriptor for TaggedControlTube {
106 fn as_raw_descriptor(&self) -> RawDescriptor {
107 self.as_ref().as_raw_descriptor()
108 }
109}
110
111impl ReadNotifier for TaggedControlTube {
112 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
113 self.as_ref().get_read_notifier()
114 }
115}
116
117#[derive(serde::Serialize, serde::Deserialize)]
119pub struct VmMemoryTube {
120 pub tube: Tube,
121 pub expose_with_viommu: bool,
123 pub remote_peer: bool,
125}
126
127impl AsRef<Tube> for VmMemoryTube {
128 fn as_ref(&self) -> &Tube {
129 &self.tube
130 }
131}
132
133impl AsRawDescriptor for VmMemoryTube {
134 fn as_raw_descriptor(&self) -> RawDescriptor {
135 self.as_ref().as_raw_descriptor()
136 }
137}
138
139impl ReadNotifier for VmMemoryTube {
140 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
141 self.as_ref().get_read_notifier()
142 }
143}
144
145pub trait IntoUnixStream {
146 fn into_unix_stream(self) -> Result<UnixStream>;
147}
148
149impl IntoUnixStream for &Path {
150 fn into_unix_stream(self) -> Result<UnixStream> {
151 if let Some(fd) = safe_descriptor_from_path(self)
152 .with_context(|| format!("failed to open event device '{}'", self.display()))?
153 {
154 Ok(fd.into())
155 } else {
156 UnixStream::connect(self)
157 .with_context(|| format!("failed to open event device '{}'", self.display()))
158 }
159 }
160}
161
162impl IntoUnixStream for &PathBuf {
163 fn into_unix_stream(self) -> Result<UnixStream> {
164 self.as_path().into_unix_stream()
165 }
166}
167
168impl IntoUnixStream for UnixStream {
169 fn into_unix_stream(self) -> Result<UnixStream> {
170 Ok(self)
171 }
172}
173
174pub type DeviceResult<T = VirtioDeviceStub> = Result<T>;
175
176pub trait VirtioDeviceBuilder: Sized {
180 const NAME: &'static str;
182
183 fn create_vhost_user_device(
185 self,
186 _keep_rds: &mut Vec<RawDescriptor>,
187 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>;
188
189 fn create_jail(
194 &self,
195 jail_config: Option<&JailConfig>,
196 virtio_transport: VirtioDeviceType,
197 ) -> anyhow::Result<Option<Minijail>> {
198 simple_jail(
199 jail_config,
200 &virtio_transport.seccomp_policy_file(Self::NAME),
201 )
202 }
203}
204
205pub struct DiskConfig<'a> {
208 disk: &'a DiskOption,
210 device_tube: Option<Tube>,
212}
213
214impl<'a> DiskConfig<'a> {
215 pub fn new(disk: &'a DiskOption, device_tube: Option<Tube>) -> Self {
216 Self { disk, device_tube }
217 }
218}
219
220impl VirtioDeviceBuilder for DiskConfig<'_> {
221 const NAME: &'static str = "block";
222
223 fn create_vhost_user_device(
224 self,
225 keep_rds: &mut Vec<RawDescriptor>,
226 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
227 let disk = self.disk;
228 let disk_image = disk.open()?;
229 let base_features = virtio::base_features(ProtectionType::Unprotected);
230
231 let block = Box::new(
232 device_virtio_block::BlockAsync::new(
233 base_features,
234 disk_image,
235 disk,
236 self.device_tube,
237 None,
238 None,
239 )
240 .context("failed to create block device")?,
241 );
242 keep_rds.extend(block.keep_rds());
243
244 Ok(block)
245 }
246}
247
248fn vhost_user_connection(
249 path: &Path,
250 connect_timeout_ms: Option<u64>,
251) -> Result<vmm_vhost::Connection> {
252 let deadline = connect_timeout_ms.map(|t| Instant::now() + Duration::from_millis(t));
253 let mut first = true;
254 loop {
255 match UnixStream::connect(path) {
256 Ok(sock) => {
257 let connection = sock
258 .try_into()
259 .context("failed to construct Connection from UnixStream")?;
260 return Ok(connection);
261 }
262 Err(e) => {
263 if e.kind() == ErrorKind::ConnectionRefused || e.kind() == ErrorKind::NotFound {
266 if let Some(deadline) = deadline {
267 if first {
268 first = false;
269 warn!(
270 "vhost-user socket path {} not available. retrying up to {} ms",
271 path.display(),
272 connect_timeout_ms.unwrap()
273 );
274 }
275 if Instant::now() > deadline {
276 anyhow::bail!(
277 "timeout waiting for vhost-user socket path {}: final error: {e:#}",
278 path.display()
279 );
280 }
281 std::thread::sleep(Duration::from_millis(1));
282 continue;
283 }
284 }
285 return Err(e).with_context(|| {
286 format!(
287 "failed to connect to vhost-user socket path {}",
288 path.display()
289 )
290 });
291 }
292 }
293 }
294}
295
296pub fn create_vhost_user_frontend(
297 protection_type: ProtectionType,
298 opt: &VhostUserFrontendOption,
299 connect_timeout_ms: Option<u64>,
300 vm_evt_wrtube: base::SendTube,
301) -> DeviceResult {
302 let connection = if let Some(socket_fd) = safe_descriptor_from_path(&opt.socket)? {
303 socket_fd
304 .try_into()
305 .context("failed to create vhost-user connection from fd")?
306 } else {
307 vhost_user_connection(&opt.socket, connect_timeout_ms)?
308 };
309 let dev = VhostUserFrontend::new(
310 opt.type_,
311 virtio::base_features(protection_type),
312 connection,
313 vm_evt_wrtube,
314 opt.max_queue_size,
315 opt.pci_address,
316 true,
317 )
318 .context("failed to set up vhost-user frontend")?;
319
320 Ok(VirtioDeviceStub {
321 dev: Box::new(dev),
322 jail: None,
324 })
325}
326
327pub fn create_single_touch_device<T: IntoUnixStream>(
328 protection_type: ProtectionType,
329 jail_config: Option<&JailConfig>,
330 single_touch_socket: T,
331 width: u32,
332 height: u32,
333 name: Option<&str>,
334 idx: u32,
335) -> DeviceResult {
336 let socket = single_touch_socket
337 .into_unix_stream()
338 .context("failed configuring virtio single touch")?;
339
340 let dev = virtio::input::new_single_touch(
341 idx,
342 socket,
343 width,
344 height,
345 name,
346 virtio::base_features(protection_type),
347 )
348 .context("failed to set up input device")?;
349 Ok(VirtioDeviceStub {
350 dev: Box::new(dev),
351 jail: simple_jail(jail_config, "input_device")?,
352 })
353}
354
355pub fn create_multi_touch_device<T: IntoUnixStream>(
356 protection_type: ProtectionType,
357 jail_config: Option<&JailConfig>,
358 multi_touch_socket: T,
359 width: u32,
360 height: u32,
361 name: Option<&str>,
362 idx: u32,
363) -> DeviceResult {
364 let socket = multi_touch_socket
365 .into_unix_stream()
366 .context("failed configuring virtio multi touch")?;
367
368 let dev = virtio::input::new_multi_touch(
369 idx,
370 socket,
371 width,
372 height,
373 name,
374 virtio::base_features(protection_type),
375 )
376 .context("failed to set up input device")?;
377
378 Ok(VirtioDeviceStub {
379 dev: Box::new(dev),
380 jail: simple_jail(jail_config, "input_device")?,
381 })
382}
383
384pub fn create_trackpad_device<T: IntoUnixStream>(
385 protection_type: ProtectionType,
386 jail_config: Option<&JailConfig>,
387 trackpad_socket: T,
388 width: u32,
389 height: u32,
390 name: Option<&str>,
391 idx: u32,
392) -> DeviceResult {
393 let socket = trackpad_socket
394 .into_unix_stream()
395 .context("failed configuring virtio trackpad")?;
396
397 let dev = virtio::input::new_trackpad(
398 idx,
399 socket,
400 width,
401 height,
402 name,
403 virtio::base_features(protection_type),
404 )
405 .context("failed to set up input device")?;
406
407 Ok(VirtioDeviceStub {
408 dev: Box::new(dev),
409 jail: simple_jail(jail_config, "input_device")?,
410 })
411}
412
413pub fn create_multitouch_trackpad_device<T: IntoUnixStream>(
414 protection_type: ProtectionType,
415 jail_config: Option<&JailConfig>,
416 trackpad_socket: T,
417 width: u32,
418 height: u32,
419 name: Option<&str>,
420 idx: u32,
421) -> DeviceResult {
422 let socket = trackpad_socket
423 .into_unix_stream()
424 .context("failed configuring virtio trackpad")?;
425
426 let dev = virtio::input::new_multitouch_trackpad(
427 idx,
428 socket,
429 width,
430 height,
431 name,
432 virtio::base_features(protection_type),
433 )
434 .context("failed to set up input device")?;
435
436 Ok(VirtioDeviceStub {
437 dev: Box::new(dev),
438 jail: simple_jail(jail_config, "input_device")?,
439 })
440}
441
442pub fn create_mouse_device<T: IntoUnixStream>(
443 protection_type: ProtectionType,
444 jail_config: Option<&JailConfig>,
445 mouse_socket: T,
446 idx: u32,
447) -> DeviceResult {
448 let socket = mouse_socket
449 .into_unix_stream()
450 .context("failed configuring virtio mouse")?;
451
452 let dev = virtio::input::new_mouse(idx, socket, virtio::base_features(protection_type))
453 .context("failed to set up input device")?;
454
455 Ok(VirtioDeviceStub {
456 dev: Box::new(dev),
457 jail: simple_jail(jail_config, "input_device")?,
458 })
459}
460
461pub fn create_keyboard_device<T: IntoUnixStream>(
462 protection_type: ProtectionType,
463 jail_config: Option<&JailConfig>,
464 keyboard_socket: T,
465 idx: u32,
466) -> DeviceResult {
467 let socket = keyboard_socket
468 .into_unix_stream()
469 .context("failed configuring virtio keyboard")?;
470
471 let dev = virtio::input::new_keyboard(idx, socket, virtio::base_features(protection_type))
472 .context("failed to set up input device")?;
473
474 Ok(VirtioDeviceStub {
475 dev: Box::new(dev),
476 jail: simple_jail(jail_config, "input_device")?,
477 })
478}
479
480pub fn create_switches_device<T: IntoUnixStream>(
481 protection_type: ProtectionType,
482 jail_config: Option<&JailConfig>,
483 switches_socket: T,
484 idx: u32,
485) -> DeviceResult {
486 let socket = switches_socket
487 .into_unix_stream()
488 .context("failed configuring virtio switches")?;
489
490 let dev = virtio::input::new_switches(idx, socket, virtio::base_features(protection_type))
491 .context("failed to set up input device")?;
492
493 Ok(VirtioDeviceStub {
494 dev: Box::new(dev),
495 jail: simple_jail(jail_config, "input_device")?,
496 })
497}
498
499pub fn create_rotary_device<T: IntoUnixStream>(
500 protection_type: ProtectionType,
501 jail_config: Option<&JailConfig>,
502 rotary_socket: T,
503 idx: u32,
504) -> DeviceResult {
505 let socket = rotary_socket
506 .into_unix_stream()
507 .context("failed configuring virtio rotary")?;
508
509 let dev = virtio::input::new_rotary(idx, socket, virtio::base_features(protection_type))
510 .context("failed to set up input device")?;
511
512 Ok(VirtioDeviceStub {
513 dev: Box::new(dev),
514 jail: simple_jail(jail_config, "input_device")?,
515 })
516}
517
518pub fn create_vinput_device(
519 protection_type: ProtectionType,
520 jail_config: Option<&JailConfig>,
521 dev_path: &Path,
522) -> DeviceResult {
523 let dev_file = OpenOptions::new()
524 .read(true)
525 .write(true)
526 .open(dev_path)
527 .with_context(|| format!("failed to open vinput device {}", dev_path.display()))?;
528
529 let dev = virtio::input::new_evdev(dev_file, virtio::base_features(protection_type))
530 .context("failed to set up input device")?;
531
532 Ok(VirtioDeviceStub {
533 dev: Box::new(dev),
534 jail: simple_jail(jail_config, "input_device")?,
535 })
536}
537
538pub fn create_custom_device<T: IntoUnixStream>(
539 protection_type: ProtectionType,
540 jail_config: Option<&JailConfig>,
541 custom_device_socket: T,
542 idx: u32,
543 input_config_path: PathBuf,
544) -> DeviceResult {
545 let socket = custom_device_socket
546 .into_unix_stream()
547 .context("failed configuring custom virtio input device")?;
548
549 let dev = virtio::input::new_custom(
550 idx,
551 socket,
552 input_config_path,
553 virtio::base_features(protection_type),
554 )
555 .context("failed to set up input device")?;
556
557 Ok(VirtioDeviceStub {
558 dev: Box::new(dev),
559 jail: simple_jail(jail_config, "input_device")?,
560 })
561}
562
563#[cfg(feature = "balloon")]
564pub fn create_balloon_device(
565 protection_type: ProtectionType,
566 jail_config: Option<&JailConfig>,
567 tube: Tube,
568 inflate_tube: Option<Tube>,
569 init_balloon_size: u64,
570 vm_memory_client: VmMemoryClient,
571 enabled_features: u64,
572 #[cfg(feature = "registered_events")] registered_evt_q: Option<SendTube>,
573 ws_num_bins: u8,
574) -> DeviceResult {
575 let dev = virtio::Balloon::new(
576 virtio::base_features(protection_type),
577 tube,
578 vm_memory_client,
579 inflate_tube,
580 init_balloon_size,
581 enabled_features,
582 #[cfg(feature = "registered_events")]
583 registered_evt_q,
584 ws_num_bins,
585 )
586 .context("failed to create balloon")?;
587
588 Ok(VirtioDeviceStub {
589 dev: Box::new(dev),
590 jail: simple_jail(jail_config, "balloon_device")?,
591 })
592}
593
594#[cfg(feature = "pvclock")]
595pub fn create_pvclock_device(
596 protection_type: ProtectionType,
597 jail_config: Option<&JailConfig>,
598 tsc_frequency: u64,
599 suspend_tube: Tube,
600) -> DeviceResult {
601 let dev = PvClock::new(
602 virtio::base_features(protection_type),
603 tsc_frequency,
604 suspend_tube,
605 );
606
607 Ok(VirtioDeviceStub {
608 dev: Box::new(dev),
609 jail: simple_jail(jail_config, "pvclock_device")?,
610 })
611}
612
613#[cfg(feature = "net")]
614impl VirtioDeviceBuilder for &NetParameters {
615 const NAME: &'static str = "net";
616
617 fn create_jail(
618 &self,
619 jail_config: Option<&JailConfig>,
620 virtio_transport: VirtioDeviceType,
621 ) -> anyhow::Result<Option<Minijail>> {
622 let policy = if self.vhost_net.is_some() {
623 "vhost_net"
624 } else {
625 "net"
626 };
627 simple_jail(jail_config, &virtio_transport.seccomp_policy_file(policy))
628 }
629
630 fn create_vhost_user_device(
631 self,
632 keep_rds: &mut Vec<RawDescriptor>,
633 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
634 let vq_pairs = self.vq_pairs.unwrap_or(1);
635 let multi_vq = vq_pairs > 1 && self.vhost_net.is_none();
636 let (tap, _mac) = create_tap_for_net_device(&self.mode, multi_vq)?;
637
638 let backend = NetBackend::new(tap, self.mrg_rxbuf)?;
639
640 keep_rds.extend(backend.as_raw_descriptors());
641
642 Ok(Box::new(backend))
643 }
644}
645
646#[cfg(feature = "virtio_wl")]
647pub fn create_wayland_device(
648 protection_type: ProtectionType,
649 jail_config: Option<&JailConfig>,
650 wayland_socket_paths: &BTreeMap<String, PathBuf>,
651 resource_bridge: Option<Tube>,
652) -> DeviceResult {
653 let wayland_socket_dirs = wayland_socket_paths
654 .values()
655 .map(|path| path.parent())
656 .collect::<Option<Vec<_>>>()
657 .ok_or_else(|| anyhow!("wayland socket path has no parent or file name"))?;
658
659 let features = virtio::base_features(protection_type);
660 let dev = virtio::Wl::new(features, wayland_socket_paths.clone(), resource_bridge)
661 .context("failed to create wayland device")?;
662
663 let jail = if let Some(jail_config) = jail_config {
664 let mut config = SandboxConfig::new(jail_config, "wl_device");
665 config.bind_mounts = true;
666 let mut jail = create_gpu_minijail(
667 &jail_config.pivot_root,
668 &config,
669 false,
670 None,
671 )?;
672 for dir in &wayland_socket_dirs {
677 jail.mount(dir, dir, "", (libc::MS_BIND | libc::MS_REC) as usize)?;
678 }
679
680 Some(jail)
681 } else {
682 None
683 };
684
685 Ok(VirtioDeviceStub {
686 dev: Box::new(dev),
687 jail,
688 })
689}
690
691#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
692fn create_video_device_jail(
693 backend: VideoBackendType,
694 jail_config: &JailConfig,
695 typ: VideoDeviceType,
696) -> Result<Minijail> {
697 match typ {
698 #[cfg(feature = "video-decoder")]
699 VideoDeviceType::Decoder => {}
700 #[cfg(feature = "video-encoder")]
701 VideoDeviceType::Encoder => {}
702 #[cfg(any(not(feature = "video-decoder"), not(feature = "video-encoder")))]
703 device_type => unreachable!("Not compiled with {:?} enabled", device_type),
705 };
706 let mut config = SandboxConfig::new(jail_config, "video_device");
707 config.bind_mounts = true;
708 let mut jail =
709 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
710
711 let need_drm_device = match backend {
712 #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
713 VideoBackendType::Libvda => true,
714 #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
715 VideoBackendType::LibvdaVd => true,
716 #[cfg(feature = "vaapi")]
717 VideoBackendType::Vaapi => true,
718 #[cfg(feature = "ffmpeg")]
719 VideoBackendType::Ffmpeg => false,
720 };
721
722 if need_drm_device {
723 jail_mount_bind_drm(&mut jail, true)?;
724 }
725
726 #[cfg(target_arch = "x86_64")]
727 {
728 let sys_dev_char_path = Path::new("/sys/dev/char");
730 jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false)?;
731 let sys_devices_path = Path::new("/sys/devices");
732 jail.mount_bind(sys_devices_path, sys_devices_path, false)?;
733
734 jail_mount_bind_if_exists(&mut jail, &["/usr/lib64", "/usr/lib", "/usr/share/vulkan"])?;
736 }
737
738 let dev_urandom_path = Path::new("/dev/urandom");
740 jail.mount_bind(dev_urandom_path, dev_urandom_path, false)?;
741 let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
742 jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
743
744 Ok(jail)
745}
746
747#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
748pub fn create_video_device(
749 backend: VideoBackendType,
750 protection_type: ProtectionType,
751 jail_config: Option<&JailConfig>,
752 typ: VideoDeviceType,
753 resource_bridge: Tube,
754) -> DeviceResult {
755 let jail = if let Some(jail_config) = jail_config {
756 Some(create_video_device_jail(backend, jail_config, typ)?)
757 } else {
758 None
759 };
760
761 Ok(VirtioDeviceStub {
762 dev: Box::new(devices::virtio::VideoDevice::new(
763 virtio::base_features(protection_type),
764 typ,
765 backend,
766 Some(resource_bridge),
767 )),
768 jail,
769 })
770}
771
772#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
773pub fn register_video_device(
774 backend: VideoBackendType,
775 devs: &mut Vec<(&'static str, VirtioDeviceStub)>,
776 video_tube: Tube,
777 protection_type: ProtectionType,
778 jail_config: Option<&JailConfig>,
779 typ: VideoDeviceType,
780) -> Result<()> {
781 devs.push((
782 "video",
783 create_video_device(backend, protection_type, jail_config, typ, video_tube)?,
784 ));
785 Ok(())
786}
787
788#[cfg(feature = "media")]
789pub fn create_simple_media_device(protection_type: ProtectionType) -> DeviceResult {
790 use devices::virtio::media::create_virtio_media_simple_capture_device;
791
792 let features = virtio::base_features(protection_type);
793 let dev = create_virtio_media_simple_capture_device(features);
794
795 Ok(VirtioDeviceStub { dev, jail: None })
796}
797
798#[cfg(any(target_os = "android", target_os = "linux"))]
799#[cfg(feature = "media")]
800pub fn create_v4l2_device<P: AsRef<Path>>(
801 protection_type: ProtectionType,
802 path: P,
803) -> DeviceResult {
804 use devices::virtio::media::create_virtio_media_v4l2_proxy_device;
805
806 let features = virtio::base_features(protection_type);
807 let dev = create_virtio_media_v4l2_proxy_device(features, path)?;
808
809 Ok(VirtioDeviceStub { dev, jail: None })
810}
811
812#[cfg(all(feature = "media", feature = "video-decoder"))]
813pub fn create_virtio_media_adapter(
814 protection_type: ProtectionType,
815 jail_config: Option<&JailConfig>,
816 tube: Tube,
817 backend: VideoBackendType,
818) -> DeviceResult {
819 use devices::virtio::media::create_virtio_media_decoder_adapter_device;
820
821 let jail = if let Some(jail_config) = jail_config {
822 Some(create_video_device_jail(
823 backend,
824 jail_config,
825 VideoDeviceType::Decoder,
826 )?)
827 } else {
828 None
829 };
830
831 let features = virtio::base_features(protection_type);
832 let dev = create_virtio_media_decoder_adapter_device(features, tube, backend)?;
833
834 Ok(VirtioDeviceStub { dev, jail })
835}
836
837impl VirtioDeviceBuilder for &VsockConfig {
838 const NAME: &'static str = "vhost_vsock";
839
840 fn create_vhost_user_device(
841 self,
842 keep_rds: &mut Vec<RawDescriptor>,
843 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
844 if self.max_queue_sizes.is_some() {
845 bail!("vhost-user vsock doesn't support max-queue-sizes option");
846 }
847
848 let vsock_device = VhostUserVsockDevice::new(self.cid, &self.vhost_device)?;
849
850 keep_rds.push(vsock_device.as_raw_descriptor());
851
852 Ok(Box::new(vsock_device))
853 }
854}
855
856#[cfg(target_arch = "aarch64")]
857pub fn create_vhost_scmi_device(
858 protected_vm: ProtectionType,
859 jail_config: Option<&JailConfig>,
860 vhost_scmi_dev_path: PathBuf,
861) -> DeviceResult {
862 let features = virtio::base_features(protected_vm);
863
864 let dev = virtio::vhost::Scmi::new(&vhost_scmi_dev_path, features)
865 .context("failed to set up vhost scmi device")?;
866
867 Ok(VirtioDeviceStub {
868 dev: Box::new(dev),
869 jail: simple_jail(jail_config, "vhost_scmi_device")?,
870 })
871}
872
873pub fn create_fs_device(
874 protection_type: ProtectionType,
875 jail_config: Option<&JailConfig>,
876 ugid: (Option<u32>, Option<u32>),
877 uid_map: &str,
878 gid_map: &str,
879 src: &Path,
880 tag: &str,
881 fs_cfg: virtio::fs::Config,
882 device_tube: Tube,
883) -> DeviceResult {
884 let max_open_files = base::linux::max_open_files()
885 .context("failed to get max number of open files")?
886 .rlim_max;
887 let j = if let Some(jail_config) = jail_config {
888 let mut config = SandboxConfig::new(jail_config, "fs_device");
889 config.limit_caps = false;
890 config.ugid_map = Some((uid_map, gid_map));
891 config.remount_mode = Some(libc::MS_SLAVE);
894 config.run_as = if ugid == (None, None) {
895 RunAsUser::Unspecified
896 } else {
897 RunAsUser::Specified(ugid.0.unwrap_or(0), ugid.1.unwrap_or(0))
898 };
899 create_sandbox_minijail(src, max_open_files, &config)?
900 } else {
901 create_base_minijail(src, max_open_files)?
902 };
903
904 let features = virtio::base_features(protection_type);
905 let dev = virtio::fs::Fs::new(features, tag, 1, fs_cfg, device_tube)
908 .context("failed to create fs device")?;
909
910 Ok(VirtioDeviceStub {
911 dev: Box::new(dev),
912 jail: Some(j),
913 })
914}
915
916pub fn create_9p_device(
917 protection_type: ProtectionType,
918 jail_config: Option<&JailConfig>,
919 ugid: (Option<u32>, Option<u32>),
920 uid_map: &str,
921 gid_map: &str,
922 src: &Path,
923 tag: &str,
924 mut p9_cfg: p9::Config,
925) -> DeviceResult {
926 let max_open_files = base::linux::max_open_files()
927 .context("failed to get max number of open files")?
928 .rlim_max;
929 let (jail, root) = if let Some(jail_config) = jail_config {
930 let mut config = SandboxConfig::new(jail_config, "9p_device");
931 config.limit_caps = false;
932 config.ugid_map = Some((uid_map, gid_map));
933 config.remount_mode = Some(libc::MS_SLAVE);
936 config.run_as = if ugid == (None, None) {
937 RunAsUser::Unspecified
938 } else {
939 RunAsUser::Specified(ugid.0.unwrap_or(0), ugid.1.unwrap_or(0))
940 };
941 let jail = create_sandbox_minijail(src, max_open_files, &config)?;
942
943 let root = Path::new("/");
945 (Some(jail), root)
946 } else {
947 (None, src)
950 };
951
952 let features = virtio::base_features(protection_type);
953 p9_cfg.root = root.into();
954 let dev = virtio::P9::new(features, tag, p9_cfg).context("failed to create 9p device")?;
955
956 Ok(VirtioDeviceStub {
957 dev: Box::new(dev),
958 jail,
959 })
960}
961
962pub fn create_pmem_device(
963 protection_type: ProtectionType,
964 jail_config: Option<&JailConfig>,
965 vm: &dyn Vm,
966 resources: &mut SystemAllocator,
967 pmem: &PmemOption,
968 index: usize,
969 pmem_device_tube: Tube,
970) -> DeviceResult {
971 let (fd, disk_size) = match pmem.vma_size {
972 None => {
973 let disk_image =
974 open_file_or_duplicate(&pmem.path, OpenOptions::new().read(true).write(!pmem.ro))
975 .with_context(|| format!("failed to load disk image {}", pmem.path.display()))?;
976 let metadata = std::fs::metadata(&pmem.path).with_context(|| {
977 format!("failed to get disk image {} metadata", pmem.path.display())
978 })?;
979 (disk_image, metadata.len())
980 }
981 Some(size) => {
982 let anon_file =
983 create_anonymous_file(&pmem.path, size).context("failed to create anon file")?;
984 (anon_file, size)
985 }
986 };
987
988 let alignment = 2 * 1024 * 1024;
994 let arena_size = disk_size
995 .checked_next_multiple_of(alignment)
996 .ok_or_else(|| anyhow!("pmem device image too big"))?;
997
998 let protection = {
999 if pmem.ro {
1000 Protection::read()
1001 } else {
1002 Protection::read_write()
1003 }
1004 };
1005
1006 let arena = {
1007 let arena_size = usize::try_from(arena_size).context("pmem device image too big")?;
1009 let disk_size = usize::try_from(disk_size).context("pmem device image too big")?;
1010
1011 let mut arena =
1012 MemoryMappingArena::new(arena_size).context("failed to reserve pmem memory")?;
1013 arena
1014 .add_fd_offset_protection(0, disk_size, &fd, 0, protection)
1015 .context("failed to reserve pmem memory")?;
1016
1017 let disk_size = round_up_to_page_size(disk_size);
1021
1022 if arena_size > disk_size {
1023 arena
1026 .add_anon_protection(disk_size, arena_size - disk_size, protection)
1027 .context("failed to reserve pmem padding")?;
1028 }
1029 arena
1030 };
1031
1032 let mapping_address = GuestAddress(
1033 resources
1034 .allocate_mmio(
1035 arena_size,
1036 Alloc::PmemDevice(index),
1037 format!("pmem_disk_image_{index}"),
1038 AllocOptions::new()
1039 .top_down(false)
1043 .prefetchable(true)
1044 .align(128 * 1024 * 1024), )
1048 .context("failed to allocate memory for pmem device")?,
1049 );
1050
1051 let mem_slot = MemSlotConfig::MemSlot {
1052 idx: vm
1053 .add_memory_region(
1054 mapping_address,
1055 Box::new(arena),
1056 pmem.ro,
1057 false,
1058 MemCacheType::CacheCoherent,
1059 )
1060 .context("failed to add pmem device memory")?,
1061 };
1062
1063 let dev = virtio::Pmem::new(
1064 virtio::base_features(protection_type),
1065 PmemConfig {
1066 disk_image: Some(fd),
1067 mapping_address,
1068 mem_slot,
1069 mapping_size: arena_size,
1070 pmem_device_tube,
1071 swap_interval: pmem.swap_interval,
1072 mapping_writable: !pmem.ro,
1073 },
1074 )
1075 .context("failed to create pmem device")?;
1076
1077 Ok(VirtioDeviceStub {
1078 dev: Box::new(dev) as Box<dyn VirtioDevice>,
1079 jail: simple_jail(jail_config, "pmem_device")?,
1080 })
1081}
1082
1083pub fn create_pmem_ext2_device(
1084 protection_type: ProtectionType,
1085 jail_config: Option<&JailConfig>,
1086 resources: &mut SystemAllocator,
1087 opts: &PmemExt2Option,
1088 index: usize,
1089 vm_memory_client: VmMemoryClient,
1090 pmem_device_tube: Tube,
1091 worker_process_pids: &mut BTreeSet<Pid>,
1092) -> DeviceResult {
1093 let mapping_size = opts.size as u64;
1094 let builder = ext2::Builder {
1095 inodes_per_group: opts.inodes_per_group,
1096 blocks_per_group: opts.blocks_per_group,
1097 size: mapping_size as u32,
1098 ..Default::default()
1099 };
1100
1101 let max_open_files = base::linux::max_open_files()
1102 .context("failed to get max number of open files")?
1103 .rlim_max;
1104 let mapping_address = GuestAddress(
1105 resources
1106 .allocate_mmio(
1107 mapping_size,
1108 Alloc::PmemDevice(index),
1109 format!("pmem_ext2_image_{index}"),
1110 AllocOptions::new()
1111 .top_down(true)
1112 .prefetchable(true)
1113 .align(2 * 1024 * 1024),
1116 )
1117 .context("failed to allocate memory for pmem device")?,
1118 );
1119
1120 let (mkfs_tube, mkfs_device_tube) = Tube::pair().context("failed to create tube")?;
1121
1122 let ext2_proc_pid = crate::crosvm::sys::linux::ext2::launch(
1123 mapping_address,
1124 vm_memory_client,
1125 mkfs_tube,
1126 &opts.path,
1127 &opts.ugid,
1128 (&opts.uid_map, &opts.gid_map),
1129 builder,
1130 jail_config,
1131 )
1132 .context("failed to spawn mkfs process")?;
1133
1134 worker_process_pids.insert(ext2_proc_pid);
1135
1136 let dev = virtio::Pmem::new(
1137 virtio::base_features(protection_type),
1138 PmemConfig {
1139 disk_image: None,
1140 mapping_address,
1141 mem_slot: MemSlotConfig::LazyInit {
1142 tube: mkfs_device_tube,
1143 },
1144 mapping_size,
1145 pmem_device_tube,
1146 swap_interval: None,
1147 mapping_writable: false,
1148 },
1149 )
1150 .context("failed to create pmem device")?;
1151
1152 let j = if let Some(jail_config) = jail_config {
1153 let mut config = SandboxConfig::new(jail_config, "pmem_device");
1154 config.limit_caps = false;
1155 create_sandbox_minijail(&opts.path, max_open_files, &config)?
1156 } else {
1157 create_base_minijail(&opts.path, max_open_files)?
1158 };
1159 Ok(VirtioDeviceStub {
1160 dev: Box::new(dev) as Box<dyn VirtioDevice>,
1161 jail: Some(j),
1162 })
1163}
1164
1165pub fn create_anonymous_file<P: AsRef<Path>>(path: P, size: u64) -> Result<File> {
1166 let file_name = path
1167 .as_ref()
1168 .to_str()
1169 .ok_or_else(|| Error::new(libc::EINVAL))?;
1170 let mut shm = SharedMemory::new(file_name, size)?;
1171 let mut seals = MemfdSeals::new();
1172
1173 seals.set_shrink_seal();
1174 seals.set_grow_seal();
1175 seals.set_seal_seal();
1176 shm.add_seals(seals)?;
1177
1178 Ok(shm.descriptor.into())
1179}
1180
1181pub fn create_iommu_device(
1182 protection_type: ProtectionType,
1183 jail_config: Option<&JailConfig>,
1184 iova_max_addr: u64,
1185 endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1186 hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
1187 translate_response_senders: Option<BTreeMap<u32, Tube>>,
1188 translate_request_rx: Option<Tube>,
1189 iommu_device_tube: Tube,
1190) -> DeviceResult {
1191 let dev = virtio::Iommu::new(
1192 virtio::base_features(protection_type),
1193 endpoints,
1194 iova_max_addr,
1195 hp_endpoints_ranges,
1196 translate_response_senders,
1197 translate_request_rx,
1198 Some(iommu_device_tube),
1199 )
1200 .context("failed to create IOMMU device")?;
1201
1202 Ok(VirtioDeviceStub {
1203 dev: Box::new(dev),
1204 jail: simple_jail(jail_config, "iommu_device")?,
1205 })
1206}
1207
1208impl VirtioDeviceBuilder for &SerialParameters {
1210 const NAME: &'static str = "serial";
1211
1212 fn create_vhost_user_device(
1213 self,
1214 keep_rds: &mut Vec<RawDescriptor>,
1215 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
1216 Ok(Box::new(
1217 device_virtio_console::vhost_user::create_vu_console_device(self, keep_rds)?,
1218 ))
1219 }
1220
1221 fn create_jail(
1222 &self,
1223 jail_config: Option<&JailConfig>,
1224 virtio_transport: VirtioDeviceType,
1225 ) -> anyhow::Result<Option<Minijail>> {
1226 if let Some(jail_config) = jail_config {
1227 device_virtio_console::create_jail(
1228 self,
1229 jail_config,
1230 virtio_transport.seccomp_policy_file("serial").as_str(),
1231 )
1232 } else {
1233 Ok(None)
1234 }
1235 }
1236}
1237
1238#[cfg(feature = "audio")]
1239pub fn create_sound_device(
1240 path: &Path,
1241 protection_type: ProtectionType,
1242 jail_config: Option<&JailConfig>,
1243) -> DeviceResult {
1244 let dev = device_virtio_snd::new_sound(path, virtio::base_features(protection_type))
1245 .context("failed to create sound device")?;
1246
1247 Ok(VirtioDeviceStub {
1248 dev: Box::new(dev),
1249 jail: simple_jail(jail_config, "vios_audio_device")?,
1250 })
1251}
1252
1253#[allow(clippy::large_enum_variant)]
1254pub enum VfioDeviceVariant {
1255 Pci(VfioPciDevice),
1256 Platform(VfioPlatformDevice),
1257}
1258
1259pub fn create_vfio_device(
1260 jail_config: Option<&JailConfig>,
1261 vm: &dyn Vm,
1262 resources: &mut SystemAllocator,
1263 add_control_tube: &mut impl FnMut(AnyControlTube),
1264 vfio_path: &Path,
1265 hotplug: bool,
1266 hotplug_bus: Option<u8>,
1267 guest_address: Option<PciAddress>,
1268 coiommu_endpoints: Option<&mut Vec<u16>>,
1269 iommu_dev: IommuDevType,
1270 dt_symbol: Option<String>,
1271 vfio_container_manager: &mut VfioContainerManager,
1272) -> DeviceResult<(VfioDeviceVariant, Option<Minijail>, Option<VfioWrapper>)> {
1273 let vfio_container = vfio_container_manager
1274 .get_container(iommu_dev, Some(vfio_path))
1275 .context("failed to get vfio container")?;
1276
1277 let (vfio_host_tube_mem, vfio_device_tube_mem) =
1278 Tube::pair().context("failed to create tube")?;
1279 add_control_tube(AnyControlTube::VmMemoryTube {
1280 tube: vfio_host_tube_mem,
1281 expose_with_viommu: false,
1282 remote_peer: jail_config.is_some(),
1283 });
1284
1285 let (vfio_host_tube_vm, vfio_device_tube_vm) = Tube::pair().context("failed to create tube")?;
1286 add_control_tube(AnyControlTube::Device(vfio_host_tube_vm));
1287
1288 let vfio_device =
1289 VfioDevice::new_passthrough(&vfio_path, vm, vfio_container.clone(), iommu_dev, dt_symbol)
1290 .context("failed to create vfio device")?;
1291
1292 match vfio_device.device_type() {
1293 VfioDeviceType::Pci => {
1294 let (vfio_host_tube_msi, vfio_device_tube_msi) =
1295 Tube::pair().context("failed to create tube")?;
1296 add_control_tube(AnyControlTube::IrqTube(vfio_host_tube_msi));
1297
1298 let (vfio_host_tube_msix, vfio_device_tube_msix) =
1299 Tube::pair().context("failed to create tube")?;
1300 add_control_tube(AnyControlTube::IrqTube(vfio_host_tube_msix));
1301
1302 let mut vfio_pci_device = VfioPciDevice::new(
1303 vfio_path,
1304 vfio_device,
1305 hotplug,
1306 hotplug_bus,
1307 guest_address,
1308 vfio_device_tube_msi,
1309 vfio_device_tube_msix,
1310 VmMemoryClient::new(vfio_device_tube_mem),
1311 vfio_device_tube_vm,
1312 )?;
1313 let endpoint_addr = vfio_pci_device
1315 .allocate_address(resources)
1316 .context("failed to allocate resources early for vfio pci dev")?;
1317
1318 let viommu_mapper = match iommu_dev {
1319 IommuDevType::NoIommu | IommuDevType::PkvmPviommu => None,
1320 IommuDevType::VirtioIommu => {
1321 Some(VfioWrapper::new(vfio_container, vm.get_memory().clone()))
1322 }
1323 IommuDevType::CoIommu => {
1324 if let Some(endpoints) = coiommu_endpoints {
1325 endpoints.push(endpoint_addr.to_u32() as u16);
1326 } else {
1327 bail!("Missed coiommu_endpoints vector to store the endpoint addr");
1328 }
1329 None
1330 }
1331 };
1332
1333 if hotplug {
1334 Ok((VfioDeviceVariant::Pci(vfio_pci_device), None, viommu_mapper))
1335 } else {
1336 Ok((
1337 VfioDeviceVariant::Pci(vfio_pci_device),
1338 simple_jail(jail_config, "vfio_device")?,
1339 viommu_mapper,
1340 ))
1341 }
1342 }
1343 VfioDeviceType::Platform => {
1344 if guest_address.is_some() {
1345 bail!("guest-address is not supported for VFIO platform devices");
1346 }
1347
1348 if hotplug {
1349 bail!("hotplug is not supported for VFIO platform devices");
1350 }
1351
1352 let vfio_plat_dev =
1353 VfioPlatformDevice::new(vfio_device, VmMemoryClient::new(vfio_device_tube_mem));
1354
1355 Ok((
1356 VfioDeviceVariant::Platform(vfio_plat_dev),
1357 simple_jail(jail_config, "vfio_platform_device")?,
1358 None,
1359 ))
1360 }
1361 }
1362}
1363
1364pub fn setup_virtio_access_platform(
1366 resources: &mut SystemAllocator,
1367 iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1368 devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)],
1369) -> DeviceResult<(Option<BTreeMap<u32, Tube>>, Option<Tube>)> {
1370 let mut translate_response_senders: Option<
1371 BTreeMap<
1372 u32, Tube,
1374 >,
1375 > = None;
1376 let mut tube_pair: Option<(Tube, Tube)> = None;
1377
1378 for dev in devices.iter_mut() {
1379 if let Some(pci_dev) = dev.0.as_pci_device_mut() {
1380 if pci_dev.supports_iommu() {
1381 let endpoint_id = pci_dev
1382 .allocate_address(resources)
1383 .context("failed to allocate resources for pci dev")?
1384 .to_u32();
1385 let mapper: Arc<Mutex<Box<dyn MemoryMapperTrait>>> =
1386 Arc::new(Mutex::new(Box::new(BasicMemoryMapper::new(u64::MAX))));
1387 let (request_tx, _request_rx) =
1388 tube_pair.get_or_insert_with(|| Tube::pair().unwrap());
1389 let CreateIpcMapperRet {
1390 mapper: ipc_mapper,
1391 response_tx,
1392 } = create_ipc_mapper(
1393 endpoint_id,
1394 #[allow(deprecated)]
1395 request_tx.try_clone()?,
1396 );
1397 translate_response_senders
1398 .get_or_insert_with(BTreeMap::new)
1399 .insert(endpoint_id, response_tx);
1400 iommu_attached_endpoints.insert(endpoint_id, mapper);
1401 pci_dev.set_iommu(ipc_mapper)?;
1402 }
1403 }
1404 }
1405
1406 Ok((
1407 translate_response_senders,
1408 tube_pair.map(|(_request_tx, request_rx)| request_rx),
1409 ))
1410}