crosvm/crosvm/sys/linux/
device_helpers.rs

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
5use 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
82/// Tubes that service requests from devices.
83///
84/// Only includes those that happen to be handled together in the main `WaitContext` loop.
85pub enum TaggedControlTube {
86    /// Receives `DeviceControlRequest`.
87    Device(Tube),
88    /// Receives `FsMappingRequest`.
89    Fs(Tube),
90    /// Receives `VmRequest`.
91    Vm(Tube),
92    /// Receives `VmMemoryMappingRequest`.
93    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/// Tubes that service `VmMemoryRequest` requests from devices.
118#[derive(serde::Serialize, serde::Deserialize)]
119pub struct VmMemoryTube {
120    pub tube: Tube,
121    /// See devices::virtio::VirtioDevice.expose_shared_memory_region_with_viommu
122    pub expose_with_viommu: bool,
123    /// Whether the other end of the tube is in another separate process.
124    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
176/// A trait for spawning vhost-user device instances and jails from their configuration structure.
177// TODO: This isn't used for regular virtio devices anymore. Rename to VhostUserDeviceBuilder. Or,
178// dissolve it. There are too many different vhost-user builder traits.
179pub trait VirtioDeviceBuilder: Sized {
180    /// Base name of the device, as it will appear in logs.
181    const NAME: &'static str;
182
183    /// Create a device suitable for being run as a vhost-user instance.
184    fn create_vhost_user_device(
185        self,
186        _keep_rds: &mut Vec<RawDescriptor>,
187    ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>;
188
189    /// Create a jail that is suitable to run a device.
190    ///
191    /// The default implementation creates a simple jail with a seccomp policy derived from the
192    /// base name of the device.
193    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
205/// A one-shot configuration structure for implementing `VirtioDeviceBuilder`. We cannot do it on
206/// `DiskOption` directly because disk devices can be passed an optional control tube.
207pub struct DiskConfig<'a> {
208    /// Options for disk creation.
209    disk: &'a DiskOption,
210    /// Optional control tube for the device.
211    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                // ConnectionRefused => Might be a stale file the backend hasn't deleted yet.
264                // NotFound => Might be the backend hasn't bound the socket yet.
265                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        /* is_remote_backend= */ true,
317    )
318    .context("failed to set up vhost-user frontend")?;
319
320    Ok(VirtioDeviceStub {
321        dev: Box::new(dev),
322        // no sandbox here because virtqueue handling is exported to a different process.
323        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            /* render_node_only= */ false,
670            /* snapshot_scratch_path= */ None,
671        )?;
672        // Bind mount the wayland socket's directory into jail's root. This is necessary since
673        // each new wayland context must open() the socket. If the wayland socket is ever
674        // destroyed and remade in the same host directory, new connections will be possible
675        // without restarting the wayland device.
676        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        // `typ` is always a VideoDeviceType enabled
704        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, /* render_node_only= */ true)?;
724    }
725
726    #[cfg(target_arch = "x86_64")]
727    {
728        // Device nodes used by libdrm through minigbm in libvda on AMD devices.
729        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        // Required for loading dri or vulkan libraries loaded by minigbm on AMD devices.
735        jail_mount_bind_if_exists(&mut jail, &["/usr/lib64", "/usr/lib", "/usr/share/vulkan"])?;
736    }
737
738    // Device nodes required by libchrome which establishes Mojo connection in libvda.
739    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        // We want bind mounts from the parent namespaces to propagate into the fs device's
892        // namespace.
893        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    // TODO(chirantan): Use more than one worker once the kernel driver has been fixed to not panic
906    // when num_queues > 1.
907    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        // We want bind mounts from the parent namespaces to propagate into the 9p server's
934        // namespace.
935        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        //  The shared directory becomes the root of the device's file system.
944        let root = Path::new("/");
945        (Some(jail), root)
946    } else {
947        // There's no mount namespace so we tell the server to treat the source directory as the
948        // root.
949        (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    // Linux requires pmem region sizes to be 2 MiB aligned. Linux will fill any partial page
989    // at the end of an mmap'd file and won't write back beyond the actual file length, but if
990    // we just align the size of the file to 2 MiB then access beyond the last page of the
991    // mapped file will generate SIGBUS. So use a memory mapping arena that will provide
992    // padding up to 2 MiB.
993    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        // Conversion from u64 to usize may fail on 32bit system.
1008        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        // If the disk is not a multiple of the page size, the OS will fill the remaining part
1018        // of the page with zeroes. However, the anonymous mapping added below must start on a
1019        // page boundary, so round up the size before calculating the offset of the anon region.
1020        let disk_size = round_up_to_page_size(disk_size);
1021
1022        if arena_size > disk_size {
1023            // Add an anonymous region with the same protection as the disk mapping if the arena
1024            // size was aligned.
1025            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                // Allocate from the bottom up rather than top down to avoid exceeding PHYSMEM_END
1040                // with kaslr.
1041                // TODO: b/375506171: Find a proper fix.
1042                .top_down(false)
1043                .prefetchable(true)
1044                // Linux kernel requires pmem namespaces to be 128 MiB aligned.
1045                // cf. https://github.com/pmem/ndctl/issues/76
1046                .align(128 * 1024 * 1024), /* 128 MiB */
1047            )
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                /* read_only = */ pmem.ro,
1057                /* log_dirty_pages = */ 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                // 2MB alignment for DAX
1114                // cf. https://docs.pmem.io/persistent-memory/getting-started-guide/creating-development-environments/linux-environments/advanced-topics/i-o-alignment-considerations#verifying-io-alignment
1115                .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
1208/// For creating console virtio devices.
1209impl 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            // early reservation for pass-through PCI devices.
1314            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
1364/// Setup for devices with virtio-iommu
1365pub 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, // endpoint id
1373            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}