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::collections::BTreeSet;
7 use std::convert::TryFrom;
8 use std::fs;
9 use std::fs::File;
10 use std::fs::OpenOptions;
11 use std::io::ErrorKind;
12 use std::ops::RangeInclusive;
13 use std::os::unix::fs::FileTypeExt;
14 use std::os::unix::net::UnixStream;
15 use std::path::Path;
16 use std::path::PathBuf;
17 use std::str;
18 use std::sync::Arc;
19 use std::time::Duration;
20 use std::time::Instant;
21
22 use anyhow::anyhow;
23 use anyhow::bail;
24 use anyhow::Context;
25 use anyhow::Result;
26 use arch::VirtioDeviceStub;
27 use base::linux::MemfdSeals;
28 use base::sys::SharedMemoryLinux;
29 use base::ReadNotifier;
30 use base::*;
31 use devices::serial_device::SerialParameters;
32 use devices::serial_device::SerialType;
33 use devices::vfio::VfioContainerManager;
34 use devices::virtio;
35 use devices::virtio::block::DiskOption;
36 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
37 use devices::virtio::device_constants::video::VideoBackendType;
38 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
39 use devices::virtio::device_constants::video::VideoDeviceType;
40 use devices::virtio::ipc_memory_mapper::create_ipc_mapper;
41 use devices::virtio::ipc_memory_mapper::CreateIpcMapperRet;
42 use devices::virtio::memory_mapper::BasicMemoryMapper;
43 use devices::virtio::memory_mapper::MemoryMapperTrait;
44 #[cfg(feature = "pvclock")]
45 use devices::virtio::pvclock::PvClock;
46 use devices::virtio::scsi::ScsiOption;
47 #[cfg(feature = "audio")]
48 use devices::virtio::snd::parameters::Parameters as SndParameters;
49 use devices::virtio::vfio_wrapper::VfioWrapper;
50 #[cfg(feature = "net")]
51 use devices::virtio::vhost::user::NetBackend;
52 use devices::virtio::vhost::user::VhostUserDeviceBuilder;
53 use devices::virtio::vhost::user::VhostUserVsockDevice;
54 use devices::virtio::vsock::VsockConfig;
55 use devices::virtio::Console;
56 use devices::virtio::MemSlotConfig;
57 #[cfg(feature = "net")]
58 use devices::virtio::NetError;
59 #[cfg(feature = "net")]
60 use devices::virtio::NetParameters;
61 #[cfg(feature = "net")]
62 use devices::virtio::NetParametersMode;
63 use devices::virtio::PmemConfig;
64 use devices::virtio::VhostUserFrontend;
65 use devices::virtio::VirtioDevice;
66 use devices::virtio::VirtioDeviceType;
67 use devices::BusDeviceObj;
68 use devices::IommuDevType;
69 use devices::PciAddress;
70 use devices::PciDevice;
71 use devices::VfioDevice;
72 use devices::VfioDeviceType;
73 use devices::VfioPciDevice;
74 use devices::VfioPlatformDevice;
75 #[cfg(feature = "vtpm")]
76 use devices::VtpmProxy;
77 use hypervisor::MemCacheType;
78 use hypervisor::ProtectionType;
79 use hypervisor::Vm;
80 use jail::*;
81 use minijail::Minijail;
82 #[cfg(feature = "net")]
83 use net_util::sys::linux::Tap;
84 #[cfg(feature = "net")]
85 use net_util::MacAddress;
86 #[cfg(feature = "net")]
87 use net_util::TapTCommon;
88 use resources::Alloc;
89 use resources::AllocOptions;
90 use resources::SystemAllocator;
91 use sync::Mutex;
92 use vm_control::api::VmMemoryClient;
93 use vm_memory::GuestAddress;
94
95 use crate::crosvm::config::PmemOption;
96 use crate::crosvm::config::VhostUserFrontendOption;
97 use crate::crosvm::config::VhostUserFsOption;
98 use crate::crosvm::sys::config::PmemExt2Option;
99
100 /// All the tube types collected and passed to `run_control`.
101 ///
102 /// This mainly exists to simplify the device setup plumbing. We collect the tubes of all the
103 /// devices into one list using this enum and then separate them out in `run_control` to be handled
104 /// individually.
105 #[remain::sorted]
106 pub enum AnyControlTube {
107 DeviceControlTube(DeviceControlTube),
108 /// Receives `IrqHandlerRequest`.
109 IrqTube(Tube),
110 TaggedControlTube(TaggedControlTube),
111 VmMemoryTube(VmMemoryTube),
112 }
113
114 impl From<DeviceControlTube> for AnyControlTube {
from(value: DeviceControlTube) -> Self115 fn from(value: DeviceControlTube) -> Self {
116 AnyControlTube::DeviceControlTube(value)
117 }
118 }
119
120 impl From<TaggedControlTube> for AnyControlTube {
from(value: TaggedControlTube) -> Self121 fn from(value: TaggedControlTube) -> Self {
122 AnyControlTube::TaggedControlTube(value)
123 }
124 }
125
126 impl From<VmMemoryTube> for AnyControlTube {
from(value: VmMemoryTube) -> Self127 fn from(value: VmMemoryTube) -> Self {
128 AnyControlTube::VmMemoryTube(value)
129 }
130 }
131
132 /// Tubes that initiate requests to devices.
133 #[remain::sorted]
134 pub enum DeviceControlTube {
135 // See `BalloonTube`.
136 #[cfg(feature = "balloon")]
137 Balloon(Tube),
138 // Sends `DiskControlCommand`.
139 Disk(Tube),
140 // Sends `GpuControlCommand`.
141 #[cfg(feature = "gpu")]
142 Gpu(Tube),
143 // Sends `PvClockCommand`.
144 #[cfg(feature = "pvclock")]
145 PvClock(Tube),
146 }
147
148 /// Tubes that service requests from devices.
149 ///
150 /// Only includes those that happen to be handled together in the main `WaitContext` loop.
151 pub enum TaggedControlTube {
152 /// Receives `FsMappingRequest`.
153 Fs(Tube),
154 /// Receives `VmRequest`.
155 Vm(Tube),
156 /// Receives `VmMemoryMappingRequest`.
157 VmMsync(Tube),
158 }
159
160 impl AsRef<Tube> for TaggedControlTube {
as_ref(&self) -> &Tube161 fn as_ref(&self) -> &Tube {
162 use self::TaggedControlTube::*;
163 match &self {
164 Fs(tube) | Vm(tube) | VmMsync(tube) => tube,
165 }
166 }
167 }
168
169 impl AsRawDescriptor for TaggedControlTube {
as_raw_descriptor(&self) -> RawDescriptor170 fn as_raw_descriptor(&self) -> RawDescriptor {
171 self.as_ref().as_raw_descriptor()
172 }
173 }
174
175 impl ReadNotifier for TaggedControlTube {
get_read_notifier(&self) -> &dyn AsRawDescriptor176 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
177 self.as_ref().get_read_notifier()
178 }
179 }
180
181 /// Tubes that service `VmMemoryRequest` requests from devices.
182 #[derive(serde::Serialize, serde::Deserialize)]
183 pub struct VmMemoryTube {
184 pub tube: Tube,
185 /// See devices::virtio::VirtioDevice.expose_shared_memory_region_with_viommu
186 pub expose_with_viommu: bool,
187 }
188
189 impl AsRef<Tube> for VmMemoryTube {
as_ref(&self) -> &Tube190 fn as_ref(&self) -> &Tube {
191 &self.tube
192 }
193 }
194
195 impl AsRawDescriptor for VmMemoryTube {
as_raw_descriptor(&self) -> RawDescriptor196 fn as_raw_descriptor(&self) -> RawDescriptor {
197 self.as_ref().as_raw_descriptor()
198 }
199 }
200
201 impl ReadNotifier for VmMemoryTube {
get_read_notifier(&self) -> &dyn AsRawDescriptor202 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
203 self.as_ref().get_read_notifier()
204 }
205 }
206
207 pub trait IntoUnixStream {
into_unix_stream(self) -> Result<UnixStream>208 fn into_unix_stream(self) -> Result<UnixStream>;
209 }
210
211 impl<'a> IntoUnixStream for &'a Path {
into_unix_stream(self) -> Result<UnixStream>212 fn into_unix_stream(self) -> Result<UnixStream> {
213 if let Some(fd) = safe_descriptor_from_path(self)
214 .with_context(|| format!("failed to open event device '{}'", self.display()))?
215 {
216 Ok(fd.into())
217 } else {
218 UnixStream::connect(self)
219 .with_context(|| format!("failed to open event device '{}'", self.display()))
220 }
221 }
222 }
223
224 impl<'a> IntoUnixStream for &'a PathBuf {
into_unix_stream(self) -> Result<UnixStream>225 fn into_unix_stream(self) -> Result<UnixStream> {
226 self.as_path().into_unix_stream()
227 }
228 }
229
230 impl IntoUnixStream for UnixStream {
into_unix_stream(self) -> Result<UnixStream>231 fn into_unix_stream(self) -> Result<UnixStream> {
232 Ok(self)
233 }
234 }
235
236 pub type DeviceResult<T = VirtioDeviceStub> = Result<T>;
237
238 /// A trait for spawning virtio device instances and jails from their configuration structure.
239 ///
240 /// Implementors become able to create virtio devices and jails following their own configuration.
241 /// This trait also provides a few convenience methods for e.g. creating a virtio device and jail
242 /// at once.
243 pub trait VirtioDeviceBuilder: Sized {
244 /// Base name of the device, as it will appear in logs.
245 const NAME: &'static str;
246
247 /// Create a regular virtio device from the configuration and `protection_type` setting.
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>248 fn create_virtio_device(
249 self,
250 protection_type: ProtectionType,
251 ) -> anyhow::Result<Box<dyn VirtioDevice>>;
252
253 /// Create a device suitable for being run as a vhost-user instance.
254 ///
255 /// It is ok to leave this method unimplemented if the device is not intended to be used with
256 /// vhost-user.
create_vhost_user_device( self, _keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>257 fn create_vhost_user_device(
258 self,
259 _keep_rds: &mut Vec<RawDescriptor>,
260 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
261 unimplemented!()
262 }
263
264 /// Create a jail that is suitable to run a device.
265 ///
266 /// The default implementation creates a simple jail with a seccomp policy derived from the
267 /// base name of the device.
create_jail( &self, jail_config: &Option<JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>268 fn create_jail(
269 &self,
270 jail_config: &Option<JailConfig>,
271 virtio_transport: VirtioDeviceType,
272 ) -> anyhow::Result<Option<Minijail>> {
273 simple_jail(
274 jail_config,
275 &virtio_transport.seccomp_policy_file(Self::NAME),
276 )
277 }
278
279 /// Helper method to return a `VirtioDeviceStub` filled using `create_virtio_device` and
280 /// `create_jail`.
281 ///
282 /// 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>, ) -> DeviceResult283 fn create_virtio_device_and_jail(
284 self,
285 protection_type: ProtectionType,
286 jail_config: &Option<JailConfig>,
287 ) -> DeviceResult {
288 let jail = self.create_jail(jail_config, VirtioDeviceType::Regular)?;
289 let dev = self.create_virtio_device(protection_type)?;
290 Ok(VirtioDeviceStub { dev, jail })
291 }
292 }
293
294 /// A one-shot configuration structure for implementing `VirtioDeviceBuilder`. We cannot do it on
295 /// `DiskOption` directly because disk devices can be passed an optional control tube.
296 pub struct DiskConfig<'a> {
297 /// Options for disk creation.
298 disk: &'a DiskOption,
299 /// Optional control tube for the device.
300 device_tube: Option<Tube>,
301 }
302
303 impl<'a> DiskConfig<'a> {
new(disk: &'a DiskOption, device_tube: Option<Tube>) -> Self304 pub fn new(disk: &'a DiskOption, device_tube: Option<Tube>) -> Self {
305 Self { disk, device_tube }
306 }
307 }
308
309 impl<'a> VirtioDeviceBuilder for DiskConfig<'a> {
310 const NAME: &'static str = "block";
311
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>312 fn create_virtio_device(
313 self,
314 protection_type: ProtectionType,
315 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
316 info!(
317 "Trying to attach block device: {}",
318 self.disk.path.display(),
319 );
320 let disk_image = self.disk.open()?;
321 let base_features = virtio::base_features(protection_type);
322 Ok(Box::new(
323 virtio::BlockAsync::new(
324 base_features,
325 disk_image,
326 self.disk,
327 self.device_tube,
328 None,
329 None,
330 )
331 .context("failed to create block device")?,
332 ))
333 }
334
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>335 fn create_vhost_user_device(
336 self,
337 keep_rds: &mut Vec<RawDescriptor>,
338 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
339 let disk = self.disk;
340 let disk_image = disk.open()?;
341 let base_features = virtio::base_features(ProtectionType::Unprotected);
342
343 let block = Box::new(
344 virtio::BlockAsync::new(
345 base_features,
346 disk_image,
347 disk,
348 self.device_tube,
349 None,
350 None,
351 )
352 .context("failed to create block device")?,
353 );
354 keep_rds.extend(block.keep_rds());
355
356 Ok(block)
357 }
358 }
359
360 pub struct ScsiConfig<'a>(pub &'a [ScsiOption]);
361
362 impl<'a> VirtioDeviceBuilder for &'a ScsiConfig<'a> {
363 const NAME: &'static str = "scsi";
364
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>365 fn create_virtio_device(
366 self,
367 protection_type: ProtectionType,
368 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
369 let base_features = virtio::base_features(protection_type);
370 let disks = self
371 .0
372 .iter()
373 .map(|op| {
374 info!("Trying to attach a scsi device: {}", op.path.display());
375 let file = op.open()?;
376 Ok(virtio::ScsiDiskConfig {
377 file,
378 block_size: op.block_size,
379 read_only: op.read_only,
380 })
381 })
382 .collect::<anyhow::Result<_>>()?;
383 let controller = virtio::ScsiController::new(base_features, disks)
384 .context("failed to create a scsi controller")?;
385 Ok(Box::new(controller))
386 }
387 }
388
vhost_user_connection( path: &Path, connect_timeout_ms: Option<u64>, ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>>389 fn vhost_user_connection(
390 path: &Path,
391 connect_timeout_ms: Option<u64>,
392 ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>> {
393 let deadline = connect_timeout_ms.map(|t| Instant::now() + Duration::from_millis(t));
394 let mut first = true;
395 loop {
396 match UnixStream::connect(path) {
397 Ok(sock) => {
398 let connection = sock
399 .try_into()
400 .context("failed to construct Connection from UnixStream")?;
401 return Ok(connection);
402 }
403 Err(e) => {
404 // ConnectionRefused => Might be a stale file the backend hasn't deleted yet.
405 // NotFound => Might be the backend hasn't bound the socket yet.
406 if e.kind() == ErrorKind::ConnectionRefused || e.kind() == ErrorKind::NotFound {
407 if let Some(deadline) = deadline {
408 if first {
409 first = false;
410 warn!(
411 "vhost-user socket path {} not available. retrying up to {} ms",
412 path.display(),
413 connect_timeout_ms.unwrap()
414 );
415 }
416 if Instant::now() > deadline {
417 anyhow::bail!(
418 "timeout waiting for vhost-user socket path {}: final error: {e:#}",
419 path.display()
420 );
421 }
422 std::thread::sleep(Duration::from_millis(1));
423 continue;
424 }
425 }
426 return Err(e).with_context(|| {
427 format!(
428 "failed to connect to vhost-user socket path {}",
429 path.display()
430 )
431 });
432 }
433 }
434 }
435 }
436
is_socket(path: &PathBuf) -> bool437 fn is_socket(path: &PathBuf) -> bool {
438 match fs::metadata(path) {
439 Ok(metadata) => metadata.file_type().is_socket(),
440 Err(_) => false, // Assume not a socket if we can't get metadata
441 }
442 }
443
vhost_user_connection_from_socket_fd( fd: u32, ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>>444 fn vhost_user_connection_from_socket_fd(
445 fd: u32,
446 ) -> Result<vmm_vhost::Connection<vmm_vhost::FrontendReq>> {
447 let path = PathBuf::from(format!("/proc/self/fd/{}", fd));
448 if !is_socket(&path) {
449 anyhow::bail!("path {} is not socket", path.display());
450 }
451
452 let safe_fd = safe_descriptor_from_cmdline_fd(&(fd as i32))?;
453
454 safe_fd
455 .try_into()
456 .context("failed to create vhost-user connection from fd")
457 }
458
create_vhost_user_frontend( protection_type: ProtectionType, opt: &VhostUserFrontendOption, connect_timeout_ms: Option<u64>, ) -> DeviceResult459 pub fn create_vhost_user_frontend(
460 protection_type: ProtectionType,
461 opt: &VhostUserFrontendOption,
462 connect_timeout_ms: Option<u64>,
463 ) -> DeviceResult {
464 let dev = VhostUserFrontend::new(
465 opt.type_,
466 virtio::base_features(protection_type),
467 vhost_user_connection(&opt.socket, connect_timeout_ms)?,
468 opt.max_queue_size,
469 opt.pci_address,
470 )
471 .context("failed to set up vhost-user frontend")?;
472
473 Ok(VirtioDeviceStub {
474 dev: Box::new(dev),
475 // no sandbox here because virtqueue handling is exported to a different process.
476 jail: None,
477 })
478 }
479
create_vhost_user_fs_device( protection_type: ProtectionType, option: &VhostUserFsOption, ) -> DeviceResult480 pub fn create_vhost_user_fs_device(
481 protection_type: ProtectionType,
482 option: &VhostUserFsOption,
483 ) -> DeviceResult {
484 let connection = match (&option.socket_path, option.socket_fd) {
485 (Some(socket), None) => vhost_user_connection(socket, None)?,
486 (None, Some(fd)) => vhost_user_connection_from_socket_fd(fd)?,
487 (Some(_), Some(_)) => bail!("Cannot specify both a UDS path and a file descriptor"),
488 (None, None) => bail!("Must specify either a socket or a file descriptor"),
489 };
490 let dev = VhostUserFrontend::new_fs(
491 virtio::base_features(protection_type),
492 connection,
493 option.max_queue_size,
494 option.tag.as_deref(),
495 )
496 .context("failed to set up vhost-user fs device")?;
497
498 Ok(VirtioDeviceStub {
499 dev: Box::new(dev),
500 // no sandbox here because virtqueue handling is exported to a different process.
501 jail: None,
502 })
503 }
504
create_rng_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, ) -> DeviceResult505 pub fn create_rng_device(
506 protection_type: ProtectionType,
507 jail_config: &Option<JailConfig>,
508 ) -> DeviceResult {
509 let dev =
510 virtio::Rng::new(virtio::base_features(protection_type)).context("failed to set up rng")?;
511
512 Ok(VirtioDeviceStub {
513 dev: Box::new(dev),
514 jail: simple_jail(jail_config, "rng_device")?,
515 })
516 }
517
518 #[cfg(feature = "audio")]
create_virtio_snd_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, snd_params: SndParameters, ) -> DeviceResult519 pub fn create_virtio_snd_device(
520 protection_type: ProtectionType,
521 jail_config: &Option<JailConfig>,
522 snd_params: SndParameters,
523 ) -> DeviceResult {
524 let backend = snd_params.backend;
525 let dev = virtio::snd::common_backend::VirtioSnd::new(
526 virtio::base_features(protection_type),
527 snd_params,
528 )
529 .context("failed to create cras sound device")?;
530
531 use virtio::snd::parameters::StreamSourceBackend as Backend;
532
533 let policy = match backend {
534 Backend::NULL | Backend::FILE => "snd_null_device",
535 #[cfg(feature = "audio_aaudio")]
536 Backend::Sys(virtio::snd::sys::StreamSourceBackend::AAUDIO) => "snd_aaudio_device",
537 #[cfg(feature = "audio_cras")]
538 Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) => "snd_cras_device",
539 #[cfg(not(any(feature = "audio_cras", feature = "audio_aaudio")))]
540 _ => unreachable!(),
541 };
542
543 let jail = if let Some(jail_config) = jail_config {
544 let mut config = SandboxConfig::new(jail_config, policy);
545 #[cfg(feature = "audio_cras")]
546 if backend == Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) {
547 config.bind_mounts = true;
548 }
549 // TODO(b/267574679): running as current_user may not be required for snd device.
550 config.run_as = RunAsUser::CurrentUser;
551 #[allow(unused_mut)]
552 let mut jail =
553 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
554 #[cfg(feature = "audio_cras")]
555 if backend == Backend::Sys(virtio::snd::sys::StreamSourceBackend::CRAS) {
556 let run_cras_path = Path::new("/run/cras");
557 jail.mount_bind(run_cras_path, run_cras_path, true)?;
558 }
559 Some(jail)
560 } else {
561 None
562 };
563
564 Ok(VirtioDeviceStub {
565 dev: Box::new(dev),
566 jail,
567 })
568 }
569
570 #[cfg(feature = "vtpm")]
create_vtpm_proxy_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, ) -> DeviceResult571 pub fn create_vtpm_proxy_device(
572 protection_type: ProtectionType,
573 jail_config: &Option<JailConfig>,
574 ) -> DeviceResult {
575 let jail = if let Some(jail_config) = jail_config {
576 let mut config = SandboxConfig::new(jail_config, "vtpm_proxy_device");
577 config.bind_mounts = true;
578 let mut jail =
579 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
580 let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
581 jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
582 Some(jail)
583 } else {
584 None
585 };
586
587 let backend = VtpmProxy::new();
588 let dev = virtio::Tpm::new(Box::new(backend), virtio::base_features(protection_type));
589
590 Ok(VirtioDeviceStub {
591 dev: Box::new(dev),
592 jail,
593 })
594 }
595
create_single_touch_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, single_touch_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult596 pub fn create_single_touch_device<T: IntoUnixStream>(
597 protection_type: ProtectionType,
598 jail_config: &Option<JailConfig>,
599 single_touch_socket: T,
600 width: u32,
601 height: u32,
602 name: Option<&str>,
603 idx: u32,
604 ) -> DeviceResult {
605 let socket = single_touch_socket
606 .into_unix_stream()
607 .context("failed configuring virtio single touch")?;
608
609 let dev = virtio::input::new_single_touch(
610 idx,
611 socket,
612 width,
613 height,
614 name,
615 virtio::base_features(protection_type),
616 )
617 .context("failed to set up input device")?;
618 Ok(VirtioDeviceStub {
619 dev: Box::new(dev),
620 jail: simple_jail(jail_config, "input_device")?,
621 })
622 }
623
create_multi_touch_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, multi_touch_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult624 pub fn create_multi_touch_device<T: IntoUnixStream>(
625 protection_type: ProtectionType,
626 jail_config: &Option<JailConfig>,
627 multi_touch_socket: T,
628 width: u32,
629 height: u32,
630 name: Option<&str>,
631 idx: u32,
632 ) -> DeviceResult {
633 let socket = multi_touch_socket
634 .into_unix_stream()
635 .context("failed configuring virtio multi touch")?;
636
637 let dev = virtio::input::new_multi_touch(
638 idx,
639 socket,
640 width,
641 height,
642 name,
643 virtio::base_features(protection_type),
644 )
645 .context("failed to set up input device")?;
646
647 Ok(VirtioDeviceStub {
648 dev: Box::new(dev),
649 jail: simple_jail(jail_config, "input_device")?,
650 })
651 }
652
create_trackpad_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, trackpad_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult653 pub fn create_trackpad_device<T: IntoUnixStream>(
654 protection_type: ProtectionType,
655 jail_config: &Option<JailConfig>,
656 trackpad_socket: T,
657 width: u32,
658 height: u32,
659 name: Option<&str>,
660 idx: u32,
661 ) -> DeviceResult {
662 let socket = trackpad_socket
663 .into_unix_stream()
664 .context("failed configuring virtio trackpad")?;
665
666 let dev = virtio::input::new_trackpad(
667 idx,
668 socket,
669 width,
670 height,
671 name,
672 virtio::base_features(protection_type),
673 )
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_multitouch_trackpad_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, trackpad_socket: T, width: u32, height: u32, name: Option<&str>, idx: u32, ) -> DeviceResult682 pub fn create_multitouch_trackpad_device<T: IntoUnixStream>(
683 protection_type: ProtectionType,
684 jail_config: &Option<JailConfig>,
685 trackpad_socket: T,
686 width: u32,
687 height: u32,
688 name: Option<&str>,
689 idx: u32,
690 ) -> DeviceResult {
691 let socket = trackpad_socket
692 .into_unix_stream()
693 .context("failed configuring virtio trackpad")?;
694
695 let dev = virtio::input::new_multitouch_trackpad(
696 idx,
697 socket,
698 width,
699 height,
700 name,
701 virtio::base_features(protection_type),
702 )
703 .context("failed to set up input device")?;
704
705 Ok(VirtioDeviceStub {
706 dev: Box::new(dev),
707 jail: simple_jail(jail_config, "input_device")?,
708 })
709 }
710
create_mouse_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, mouse_socket: T, idx: u32, ) -> DeviceResult711 pub fn create_mouse_device<T: IntoUnixStream>(
712 protection_type: ProtectionType,
713 jail_config: &Option<JailConfig>,
714 mouse_socket: T,
715 idx: u32,
716 ) -> DeviceResult {
717 let socket = mouse_socket
718 .into_unix_stream()
719 .context("failed configuring virtio mouse")?;
720
721 let dev = virtio::input::new_mouse(idx, socket, virtio::base_features(protection_type))
722 .context("failed to set up input device")?;
723
724 Ok(VirtioDeviceStub {
725 dev: Box::new(dev),
726 jail: simple_jail(jail_config, "input_device")?,
727 })
728 }
729
create_keyboard_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, keyboard_socket: T, idx: u32, ) -> DeviceResult730 pub fn create_keyboard_device<T: IntoUnixStream>(
731 protection_type: ProtectionType,
732 jail_config: &Option<JailConfig>,
733 keyboard_socket: T,
734 idx: u32,
735 ) -> DeviceResult {
736 let socket = keyboard_socket
737 .into_unix_stream()
738 .context("failed configuring virtio keyboard")?;
739
740 let dev = virtio::input::new_keyboard(idx, socket, virtio::base_features(protection_type))
741 .context("failed to set up input device")?;
742
743 Ok(VirtioDeviceStub {
744 dev: Box::new(dev),
745 jail: simple_jail(jail_config, "input_device")?,
746 })
747 }
748
create_switches_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, switches_socket: T, idx: u32, ) -> DeviceResult749 pub fn create_switches_device<T: IntoUnixStream>(
750 protection_type: ProtectionType,
751 jail_config: &Option<JailConfig>,
752 switches_socket: T,
753 idx: u32,
754 ) -> DeviceResult {
755 let socket = switches_socket
756 .into_unix_stream()
757 .context("failed configuring virtio switches")?;
758
759 let dev = virtio::input::new_switches(idx, socket, virtio::base_features(protection_type))
760 .context("failed to set up input device")?;
761
762 Ok(VirtioDeviceStub {
763 dev: Box::new(dev),
764 jail: simple_jail(jail_config, "input_device")?,
765 })
766 }
767
create_rotary_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, rotary_socket: T, idx: u32, ) -> DeviceResult768 pub fn create_rotary_device<T: IntoUnixStream>(
769 protection_type: ProtectionType,
770 jail_config: &Option<JailConfig>,
771 rotary_socket: T,
772 idx: u32,
773 ) -> DeviceResult {
774 let socket = rotary_socket
775 .into_unix_stream()
776 .context("failed configuring virtio rotary")?;
777
778 let dev = virtio::input::new_rotary(idx, socket, virtio::base_features(protection_type))
779 .context("failed to set up input device")?;
780
781 Ok(VirtioDeviceStub {
782 dev: Box::new(dev),
783 jail: simple_jail(jail_config, "input_device")?,
784 })
785 }
786
create_vinput_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, dev_path: &Path, ) -> DeviceResult787 pub fn create_vinput_device(
788 protection_type: ProtectionType,
789 jail_config: &Option<JailConfig>,
790 dev_path: &Path,
791 ) -> DeviceResult {
792 let dev_file = OpenOptions::new()
793 .read(true)
794 .write(true)
795 .open(dev_path)
796 .with_context(|| format!("failed to open vinput device {}", dev_path.display()))?;
797
798 let dev = virtio::input::new_evdev(dev_file, virtio::base_features(protection_type))
799 .context("failed to set up input device")?;
800
801 Ok(VirtioDeviceStub {
802 dev: Box::new(dev),
803 jail: simple_jail(jail_config, "input_device")?,
804 })
805 }
806
create_custom_device<T: IntoUnixStream>( protection_type: ProtectionType, jail_config: &Option<JailConfig>, custom_device_socket: T, idx: u32, input_config_path: PathBuf, ) -> DeviceResult807 pub fn create_custom_device<T: IntoUnixStream>(
808 protection_type: ProtectionType,
809 jail_config: &Option<JailConfig>,
810 custom_device_socket: T,
811 idx: u32,
812 input_config_path: PathBuf,
813 ) -> DeviceResult {
814 let socket = custom_device_socket
815 .into_unix_stream()
816 .context("failed configuring custom virtio input device")?;
817
818 let dev = virtio::input::new_custom(
819 idx,
820 socket,
821 input_config_path,
822 virtio::base_features(protection_type),
823 )
824 .context("failed to set up input device")?;
825
826 Ok(VirtioDeviceStub {
827 dev: Box::new(dev),
828 jail: simple_jail(jail_config, "input_device")?,
829 })
830 }
831
832 #[cfg(feature = "balloon")]
create_balloon_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, tube: Tube, inflate_tube: Option<Tube>, init_balloon_size: u64, enabled_features: u64, #[cfg(feature = "registered_events")] registered_evt_q: Option<SendTube>, ws_num_bins: u8, ) -> DeviceResult833 pub fn create_balloon_device(
834 protection_type: ProtectionType,
835 jail_config: &Option<JailConfig>,
836 tube: Tube,
837 inflate_tube: Option<Tube>,
838 init_balloon_size: u64,
839 enabled_features: u64,
840 #[cfg(feature = "registered_events")] registered_evt_q: Option<SendTube>,
841 ws_num_bins: u8,
842 ) -> DeviceResult {
843 let dev = virtio::Balloon::new(
844 virtio::base_features(protection_type),
845 tube,
846 inflate_tube,
847 init_balloon_size,
848 enabled_features,
849 #[cfg(feature = "registered_events")]
850 registered_evt_q,
851 ws_num_bins,
852 )
853 .context("failed to create balloon")?;
854
855 Ok(VirtioDeviceStub {
856 dev: Box::new(dev),
857 jail: simple_jail(jail_config, "balloon_device")?,
858 })
859 }
860
861 #[cfg(feature = "pvclock")]
create_pvclock_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, tsc_frequency: u64, suspend_tube: Tube, ) -> DeviceResult862 pub fn create_pvclock_device(
863 protection_type: ProtectionType,
864 jail_config: &Option<JailConfig>,
865 tsc_frequency: u64,
866 suspend_tube: Tube,
867 ) -> DeviceResult {
868 let dev = PvClock::new(
869 virtio::base_features(protection_type),
870 tsc_frequency,
871 suspend_tube,
872 );
873
874 Ok(VirtioDeviceStub {
875 dev: Box::new(dev),
876 jail: simple_jail(jail_config, "pvclock_device")?,
877 })
878 }
879
880 #[cfg(feature = "net")]
881 impl VirtioDeviceBuilder for &NetParameters {
882 const NAME: &'static str = "net";
883
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>884 fn create_virtio_device(
885 self,
886 protection_type: ProtectionType,
887 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
888 let vq_pairs = self.vq_pairs.unwrap_or(1);
889 let multi_vq = vq_pairs > 1 && self.vhost_net.is_none();
890
891 let features = virtio::base_features(protection_type);
892 let (tap, mac) = create_tap_for_net_device(&self.mode, multi_vq)?;
893
894 Ok(if let Some(vhost_net) = &self.vhost_net {
895 Box::new(
896 virtio::vhost::Net::<_, vhost::Net<_>>::new(
897 &vhost_net.device,
898 features,
899 tap,
900 mac,
901 self.packed_queue,
902 self.pci_address,
903 )
904 .context("failed to set up virtio-vhost networking")?,
905 ) as Box<dyn VirtioDevice>
906 } else {
907 Box::new(
908 virtio::Net::new(
909 features,
910 tap,
911 vq_pairs,
912 mac,
913 self.packed_queue,
914 self.pci_address,
915 )
916 .context("failed to set up virtio networking")?,
917 ) as Box<dyn VirtioDevice>
918 })
919 }
920
create_jail( &self, jail_config: &Option<JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>921 fn create_jail(
922 &self,
923 jail_config: &Option<JailConfig>,
924 virtio_transport: VirtioDeviceType,
925 ) -> anyhow::Result<Option<Minijail>> {
926 let policy = if self.vhost_net.is_some() {
927 "vhost_net"
928 } else {
929 "net"
930 };
931
932 simple_jail(jail_config, &virtio_transport.seccomp_policy_file(policy))
933 }
934
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>935 fn create_vhost_user_device(
936 self,
937 keep_rds: &mut Vec<RawDescriptor>,
938 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
939 let vq_pairs = self.vq_pairs.unwrap_or(1);
940 let multi_vq = vq_pairs > 1 && self.vhost_net.is_none();
941 let (tap, _mac) = create_tap_for_net_device(&self.mode, multi_vq)?;
942
943 let backend = NetBackend::new(tap)?;
944
945 keep_rds.extend(backend.as_raw_descriptors());
946
947 Ok(Box::new(backend))
948 }
949 }
950
951 /// Create a new tap interface based on NetParametersMode.
952 #[cfg(feature = "net")]
create_tap_for_net_device( mode: &NetParametersMode, multi_vq: bool, ) -> DeviceResult<(Tap, Option<MacAddress>)>953 fn create_tap_for_net_device(
954 mode: &NetParametersMode,
955 multi_vq: bool,
956 ) -> DeviceResult<(Tap, Option<MacAddress>)> {
957 match mode {
958 NetParametersMode::TapName { tap_name, mac } => {
959 let tap = Tap::new_with_name(tap_name.as_bytes(), true, multi_vq)
960 .map_err(NetError::TapOpen)?;
961 Ok((tap, *mac))
962 }
963 NetParametersMode::TapFd { tap_fd, mac } => {
964 // SAFETY:
965 // Safe because we ensure that we get a unique handle to the fd.
966 let tap = unsafe {
967 Tap::from_raw_descriptor(
968 validate_raw_descriptor(*tap_fd)
969 .context("failed to validate tap descriptor")?,
970 )
971 .context("failed to create tap device")?
972 };
973 Ok((tap, *mac))
974 }
975 NetParametersMode::RawConfig {
976 host_ip,
977 netmask,
978 mac,
979 } => {
980 let tap = Tap::new(true, multi_vq).map_err(NetError::TapOpen)?;
981 tap.set_ip_addr(*host_ip).map_err(NetError::TapSetIp)?;
982 tap.set_netmask(*netmask).map_err(NetError::TapSetNetmask)?;
983 tap.set_mac_address(*mac)
984 .map_err(NetError::TapSetMacAddress)?;
985 tap.enable().map_err(NetError::TapEnable)?;
986 Ok((tap, None))
987 }
988 }
989 }
990
create_wayland_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, wayland_socket_paths: &BTreeMap<String, PathBuf>, resource_bridge: Option<Tube>, ) -> DeviceResult991 pub fn create_wayland_device(
992 protection_type: ProtectionType,
993 jail_config: &Option<JailConfig>,
994 wayland_socket_paths: &BTreeMap<String, PathBuf>,
995 resource_bridge: Option<Tube>,
996 ) -> DeviceResult {
997 let wayland_socket_dirs = wayland_socket_paths
998 .iter()
999 .map(|(_name, path)| path.parent())
1000 .collect::<Option<Vec<_>>>()
1001 .ok_or_else(|| anyhow!("wayland socket path has no parent or file name"))?;
1002
1003 let features = virtio::base_features(protection_type);
1004 let dev = virtio::Wl::new(features, wayland_socket_paths.clone(), resource_bridge)
1005 .context("failed to create wayland device")?;
1006
1007 let jail = if let Some(jail_config) = jail_config {
1008 let mut config = SandboxConfig::new(jail_config, "wl_device");
1009 config.bind_mounts = true;
1010 let mut jail = create_gpu_minijail(
1011 &jail_config.pivot_root,
1012 &config,
1013 /* render_node_only= */ false,
1014 )?;
1015 // Bind mount the wayland socket's directory into jail's root. This is necessary since
1016 // each new wayland context must open() the socket. If the wayland socket is ever
1017 // destroyed and remade in the same host directory, new connections will be possible
1018 // without restarting the wayland device.
1019 for dir in &wayland_socket_dirs {
1020 jail.mount(dir, dir, "", (libc::MS_BIND | libc::MS_REC) as usize)?;
1021 }
1022
1023 Some(jail)
1024 } else {
1025 None
1026 };
1027
1028 Ok(VirtioDeviceStub {
1029 dev: Box::new(dev),
1030 jail,
1031 })
1032 }
1033
1034 #[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, ) -> DeviceResult1035 pub fn create_video_device(
1036 backend: VideoBackendType,
1037 protection_type: ProtectionType,
1038 jail_config: &Option<JailConfig>,
1039 typ: VideoDeviceType,
1040 resource_bridge: Tube,
1041 ) -> DeviceResult {
1042 let jail = if let Some(jail_config) = jail_config {
1043 match typ {
1044 #[cfg(feature = "video-decoder")]
1045 VideoDeviceType::Decoder => {}
1046 #[cfg(feature = "video-encoder")]
1047 VideoDeviceType::Encoder => {}
1048 #[cfg(any(not(feature = "video-decoder"), not(feature = "video-encoder")))]
1049 // `typ` is always a VideoDeviceType enabled
1050 device_type => unreachable!("Not compiled with {:?} enabled", device_type),
1051 };
1052 let mut config = SandboxConfig::new(jail_config, "video_device");
1053 config.bind_mounts = true;
1054 let mut jail =
1055 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
1056
1057 let need_drm_device = match backend {
1058 #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
1059 VideoBackendType::Libvda => true,
1060 #[cfg(any(feature = "libvda", feature = "libvda-stub"))]
1061 VideoBackendType::LibvdaVd => true,
1062 #[cfg(feature = "vaapi")]
1063 VideoBackendType::Vaapi => true,
1064 #[cfg(feature = "ffmpeg")]
1065 VideoBackendType::Ffmpeg => false,
1066 };
1067
1068 if need_drm_device {
1069 jail_mount_bind_drm(&mut jail, /* render_node_only= */ true)?;
1070 }
1071
1072 #[cfg(target_arch = "x86_64")]
1073 {
1074 // Device nodes used by libdrm through minigbm in libvda on AMD devices.
1075 let sys_dev_char_path = Path::new("/sys/dev/char");
1076 jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false)?;
1077 let sys_devices_path = Path::new("/sys/devices");
1078 jail.mount_bind(sys_devices_path, sys_devices_path, false)?;
1079
1080 // Required for loading dri or vulkan libraries loaded by minigbm on AMD devices.
1081 jail_mount_bind_if_exists(&mut jail, &["/usr/lib64", "/usr/lib", "/usr/share/vulkan"])?;
1082 }
1083
1084 // Device nodes required by libchrome which establishes Mojo connection in libvda.
1085 let dev_urandom_path = Path::new("/dev/urandom");
1086 jail.mount_bind(dev_urandom_path, dev_urandom_path, false)?;
1087 let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
1088 jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
1089
1090 Some(jail)
1091 } else {
1092 None
1093 };
1094
1095 Ok(VirtioDeviceStub {
1096 dev: Box::new(devices::virtio::VideoDevice::new(
1097 virtio::base_features(protection_type),
1098 typ,
1099 backend,
1100 Some(resource_bridge),
1101 )),
1102 jail,
1103 })
1104 }
1105
1106 #[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<()>1107 pub fn register_video_device(
1108 backend: VideoBackendType,
1109 devs: &mut Vec<VirtioDeviceStub>,
1110 video_tube: Tube,
1111 protection_type: ProtectionType,
1112 jail_config: &Option<JailConfig>,
1113 typ: VideoDeviceType,
1114 ) -> Result<()> {
1115 devs.push(create_video_device(
1116 backend,
1117 protection_type,
1118 jail_config,
1119 typ,
1120 video_tube,
1121 )?);
1122 Ok(())
1123 }
1124
1125 #[cfg(feature = "media")]
create_simple_media_device(protection_type: ProtectionType) -> DeviceResult1126 pub fn create_simple_media_device(protection_type: ProtectionType) -> DeviceResult {
1127 use devices::virtio::media::create_virtio_media_simple_capture_device;
1128
1129 let features = virtio::base_features(protection_type);
1130 let dev = create_virtio_media_simple_capture_device(features);
1131
1132 Ok(VirtioDeviceStub { dev, jail: None })
1133 }
1134
1135 #[cfg(any(target_os = "android", target_os = "linux"))]
1136 #[cfg(feature = "media")]
create_v4l2_device<P: AsRef<Path>>( protection_type: ProtectionType, path: P, ) -> DeviceResult1137 pub fn create_v4l2_device<P: AsRef<Path>>(
1138 protection_type: ProtectionType,
1139 path: P,
1140 ) -> DeviceResult {
1141 use devices::virtio::media::create_virtio_media_v4l2_proxy_device;
1142
1143 let features = virtio::base_features(protection_type);
1144 let dev = create_virtio_media_v4l2_proxy_device(features, path)?;
1145
1146 Ok(VirtioDeviceStub { dev, jail: None })
1147 }
1148
1149 impl VirtioDeviceBuilder for &VsockConfig {
1150 const NAME: &'static str = "vhost_vsock";
1151
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>1152 fn create_virtio_device(
1153 self,
1154 protection_type: ProtectionType,
1155 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
1156 let features = virtio::base_features(protection_type);
1157
1158 let dev = virtio::vhost::Vsock::new(features, self)
1159 .context("failed to set up virtual socket device")?;
1160
1161 Ok(Box::new(dev))
1162 }
1163
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>1164 fn create_vhost_user_device(
1165 self,
1166 keep_rds: &mut Vec<RawDescriptor>,
1167 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
1168 let vsock_device = VhostUserVsockDevice::new(self.cid, &self.vhost_device)?;
1169
1170 keep_rds.push(vsock_device.as_raw_descriptor());
1171
1172 Ok(Box::new(vsock_device))
1173 }
1174 }
1175
1176 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
create_vhost_scmi_device( protected_vm: ProtectionType, jail_config: &Option<JailConfig>, vhost_scmi_dev_path: PathBuf, ) -> DeviceResult1177 pub fn create_vhost_scmi_device(
1178 protected_vm: ProtectionType,
1179 jail_config: &Option<JailConfig>,
1180 vhost_scmi_dev_path: PathBuf,
1181 ) -> DeviceResult {
1182 let features = virtio::base_features(protected_vm);
1183
1184 let dev = virtio::vhost::Scmi::new(&vhost_scmi_dev_path, features)
1185 .context("failed to set up vhost scmi device")?;
1186
1187 Ok(VirtioDeviceStub {
1188 dev: Box::new(dev),
1189 jail: simple_jail(jail_config, "vhost_scmi_device")?,
1190 })
1191 }
1192
create_fs_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, ugid: (Option<u32>, Option<u32>), uid_map: &str, gid_map: &str, src: &Path, tag: &str, fs_cfg: virtio::fs::Config, device_tube: Tube, ) -> DeviceResult1193 pub fn create_fs_device(
1194 protection_type: ProtectionType,
1195 jail_config: &Option<JailConfig>,
1196 ugid: (Option<u32>, Option<u32>),
1197 uid_map: &str,
1198 gid_map: &str,
1199 src: &Path,
1200 tag: &str,
1201 fs_cfg: virtio::fs::Config,
1202 device_tube: Tube,
1203 ) -> DeviceResult {
1204 let max_open_files = base::linux::max_open_files()
1205 .context("failed to get max number of open files")?
1206 .rlim_max;
1207 let j = if let Some(jail_config) = jail_config {
1208 let mut config = SandboxConfig::new(jail_config, "fs_device");
1209 config.limit_caps = false;
1210 config.ugid_map = Some((uid_map, gid_map));
1211 // We want bind mounts from the parent namespaces to propagate into the fs device's
1212 // namespace.
1213 config.remount_mode = Some(libc::MS_SLAVE);
1214 config.run_as = if ugid == (None, None) {
1215 RunAsUser::Unspecified
1216 } else {
1217 RunAsUser::Specified(ugid.0.unwrap_or(0), ugid.1.unwrap_or(0))
1218 };
1219 create_sandbox_minijail(src, max_open_files, &config)?
1220 } else {
1221 create_base_minijail(src, max_open_files)?
1222 };
1223
1224 let features = virtio::base_features(protection_type);
1225 // TODO(chirantan): Use more than one worker once the kernel driver has been fixed to not panic
1226 // when num_queues > 1.
1227 let dev = virtio::fs::Fs::new(features, tag, 1, fs_cfg, device_tube)
1228 .context("failed to create fs device")?;
1229
1230 Ok(VirtioDeviceStub {
1231 dev: Box::new(dev),
1232 jail: Some(j),
1233 })
1234 }
1235
create_9p_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, ugid: (Option<u32>, Option<u32>), uid_map: &str, gid_map: &str, src: &Path, tag: &str, mut p9_cfg: p9::Config, ) -> DeviceResult1236 pub fn create_9p_device(
1237 protection_type: ProtectionType,
1238 jail_config: &Option<JailConfig>,
1239 ugid: (Option<u32>, Option<u32>),
1240 uid_map: &str,
1241 gid_map: &str,
1242 src: &Path,
1243 tag: &str,
1244 mut p9_cfg: p9::Config,
1245 ) -> DeviceResult {
1246 let max_open_files = base::linux::max_open_files()
1247 .context("failed to get max number of open files")?
1248 .rlim_max;
1249 let (jail, root) = if let Some(jail_config) = jail_config {
1250 let mut config = SandboxConfig::new(jail_config, "9p_device");
1251 config.limit_caps = false;
1252 config.ugid_map = Some((uid_map, gid_map));
1253 // We want bind mounts from the parent namespaces to propagate into the 9p server's
1254 // namespace.
1255 config.remount_mode = Some(libc::MS_SLAVE);
1256 config.run_as = if ugid == (None, None) {
1257 RunAsUser::Unspecified
1258 } else {
1259 RunAsUser::Specified(ugid.0.unwrap_or(0), ugid.1.unwrap_or(0))
1260 };
1261 let jail = create_sandbox_minijail(src, max_open_files, &config)?;
1262
1263 // The shared directory becomes the root of the device's file system.
1264 let root = Path::new("/");
1265 (Some(jail), root)
1266 } else {
1267 // There's no mount namespace so we tell the server to treat the source directory as the
1268 // root.
1269 (None, src)
1270 };
1271
1272 let features = virtio::base_features(protection_type);
1273 p9_cfg.root = root.into();
1274 let dev = virtio::P9::new(features, tag, p9_cfg).context("failed to create 9p device")?;
1275
1276 Ok(VirtioDeviceStub {
1277 dev: Box::new(dev),
1278 jail,
1279 })
1280 }
1281
create_pmem_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, vm: &mut impl Vm, resources: &mut SystemAllocator, pmem: &PmemOption, index: usize, pmem_device_tube: Tube, ) -> DeviceResult1282 pub fn create_pmem_device(
1283 protection_type: ProtectionType,
1284 jail_config: &Option<JailConfig>,
1285 vm: &mut impl Vm,
1286 resources: &mut SystemAllocator,
1287 pmem: &PmemOption,
1288 index: usize,
1289 pmem_device_tube: Tube,
1290 ) -> DeviceResult {
1291 let (fd, disk_size) = match pmem.vma_size {
1292 None => {
1293 let disk_image =
1294 open_file_or_duplicate(&pmem.path, OpenOptions::new().read(true).write(!pmem.ro))
1295 .with_context(|| format!("failed to load disk image {}", pmem.path.display()))?;
1296 let metadata = std::fs::metadata(&pmem.path).with_context(|| {
1297 format!("failed to get disk image {} metadata", pmem.path.display())
1298 })?;
1299 (disk_image, metadata.len())
1300 }
1301 Some(size) => {
1302 let anon_file =
1303 create_anonymous_file(&pmem.path, size).context("failed to create anon file")?;
1304 (anon_file, size)
1305 }
1306 };
1307
1308 // Linux requires pmem region sizes to be 2 MiB aligned. Linux will fill any partial page
1309 // at the end of an mmap'd file and won't write back beyond the actual file length, but if
1310 // we just align the size of the file to 2 MiB then access beyond the last page of the
1311 // mapped file will generate SIGBUS. So use a memory mapping arena that will provide
1312 // padding up to 2 MiB.
1313 let alignment = 2 * 1024 * 1024;
1314 let arena_size = disk_size
1315 .checked_next_multiple_of(alignment)
1316 .ok_or_else(|| anyhow!("pmem device image too big"))?;
1317
1318 let protection = {
1319 if pmem.ro {
1320 Protection::read()
1321 } else {
1322 Protection::read_write()
1323 }
1324 };
1325
1326 let arena = {
1327 // Conversion from u64 to usize may fail on 32bit system.
1328 let arena_size = usize::try_from(arena_size).context("pmem device image too big")?;
1329 let disk_size = usize::try_from(disk_size).context("pmem device image too big")?;
1330
1331 let mut arena =
1332 MemoryMappingArena::new(arena_size).context("failed to reserve pmem memory")?;
1333 arena
1334 .add_fd_offset_protection(0, disk_size, &fd, 0, protection)
1335 .context("failed to reserve pmem memory")?;
1336
1337 // If the disk is not a multiple of the page size, the OS will fill the remaining part
1338 // of the page with zeroes. However, the anonymous mapping added below must start on a
1339 // page boundary, so round up the size before calculating the offset of the anon region.
1340 let disk_size = round_up_to_page_size(disk_size);
1341
1342 if arena_size > disk_size {
1343 // Add an anonymous region with the same protection as the disk mapping if the arena
1344 // size was aligned.
1345 arena
1346 .add_anon_protection(disk_size, arena_size - disk_size, protection)
1347 .context("failed to reserve pmem padding")?;
1348 }
1349 arena
1350 };
1351
1352 let mapping_address = GuestAddress(
1353 resources
1354 .allocate_mmio(
1355 arena_size,
1356 Alloc::PmemDevice(index),
1357 format!("pmem_disk_image_{}", index),
1358 AllocOptions::new()
1359 // Allocate from the bottom up rather than top down to avoid exceeding PHYSMEM_END
1360 // with kaslr.
1361 // TODO: b/375506171: Find a proper fix.
1362 .top_down(false)
1363 .prefetchable(true)
1364 // Linux kernel requires pmem namespaces to be 128 MiB aligned.
1365 // cf. https://github.com/pmem/ndctl/issues/76
1366 .align(128 * 1024 * 1024), /* 128 MiB */
1367 )
1368 .context("failed to allocate memory for pmem device")?,
1369 );
1370
1371 let mem_slot = MemSlotConfig::MemSlot {
1372 idx: vm
1373 .add_memory_region(
1374 mapping_address,
1375 Box::new(arena),
1376 /* read_only = */ pmem.ro,
1377 /* log_dirty_pages = */ false,
1378 MemCacheType::CacheCoherent,
1379 )
1380 .context("failed to add pmem device memory")?,
1381 };
1382
1383 let dev = virtio::Pmem::new(
1384 virtio::base_features(protection_type),
1385 PmemConfig {
1386 disk_image: Some(fd),
1387 mapping_address,
1388 mem_slot,
1389 mapping_size: arena_size,
1390 pmem_device_tube,
1391 swap_interval: pmem.swap_interval,
1392 mapping_writable: !pmem.ro,
1393 },
1394 )
1395 .context("failed to create pmem device")?;
1396
1397 Ok(VirtioDeviceStub {
1398 dev: Box::new(dev) as Box<dyn VirtioDevice>,
1399 jail: simple_jail(jail_config, "pmem_device")?,
1400 })
1401 }
1402
create_pmem_ext2_device( protection_type: ProtectionType, jail_config: &Option<JailConfig>, resources: &mut SystemAllocator, opts: &PmemExt2Option, index: usize, vm_memory_client: VmMemoryClient, pmem_device_tube: Tube, worker_process_pids: &mut BTreeSet<Pid>, ) -> DeviceResult1403 pub fn create_pmem_ext2_device(
1404 protection_type: ProtectionType,
1405 jail_config: &Option<JailConfig>,
1406 resources: &mut SystemAllocator,
1407 opts: &PmemExt2Option,
1408 index: usize,
1409 vm_memory_client: VmMemoryClient,
1410 pmem_device_tube: Tube,
1411 worker_process_pids: &mut BTreeSet<Pid>,
1412 ) -> DeviceResult {
1413 let mapping_size = opts.size as u64;
1414 let builder = ext2::Builder {
1415 inodes_per_group: opts.inodes_per_group,
1416 blocks_per_group: opts.blocks_per_group,
1417 size: mapping_size as u32,
1418 ..Default::default()
1419 };
1420
1421 let max_open_files = base::linux::max_open_files()
1422 .context("failed to get max number of open files")?
1423 .rlim_max;
1424 let mapping_address = GuestAddress(
1425 resources
1426 .allocate_mmio(
1427 mapping_size,
1428 Alloc::PmemDevice(index),
1429 format!("pmem_ext2_image_{}", index),
1430 AllocOptions::new()
1431 .top_down(true)
1432 .prefetchable(true)
1433 // 2MB alignment for DAX
1434 // cf. https://docs.pmem.io/persistent-memory/getting-started-guide/creating-development-environments/linux-environments/advanced-topics/i-o-alignment-considerations#verifying-io-alignment
1435 .align(2 * 1024 * 1024),
1436 )
1437 .context("failed to allocate memory for pmem device")?,
1438 );
1439
1440 let (mkfs_tube, mkfs_device_tube) = Tube::pair().context("failed to create tube")?;
1441
1442 let ext2_proc_pid = crate::crosvm::sys::linux::ext2::launch(
1443 mapping_address,
1444 vm_memory_client,
1445 mkfs_tube,
1446 &opts.path,
1447 &opts.ugid,
1448 (&opts.uid_map, &opts.gid_map),
1449 builder,
1450 jail_config,
1451 )
1452 .context("failed to spawn mkfs process")?;
1453
1454 worker_process_pids.insert(ext2_proc_pid);
1455
1456 let dev = virtio::Pmem::new(
1457 virtio::base_features(protection_type),
1458 PmemConfig {
1459 disk_image: None,
1460 mapping_address,
1461 mem_slot: MemSlotConfig::LazyInit {
1462 tube: mkfs_device_tube,
1463 },
1464 mapping_size,
1465 pmem_device_tube,
1466 swap_interval: None,
1467 mapping_writable: false,
1468 },
1469 )
1470 .context("failed to create pmem device")?;
1471
1472 let j = if let Some(jail_config) = jail_config {
1473 let mut config = SandboxConfig::new(jail_config, "pmem_device");
1474 config.limit_caps = false;
1475 create_sandbox_minijail(&opts.path, max_open_files, &config)?
1476 } else {
1477 create_base_minijail(&opts.path, max_open_files)?
1478 };
1479 Ok(VirtioDeviceStub {
1480 dev: Box::new(dev) as Box<dyn VirtioDevice>,
1481 jail: Some(j),
1482 })
1483 }
1484
create_anonymous_file<P: AsRef<Path>>(path: P, size: u64) -> Result<File>1485 pub fn create_anonymous_file<P: AsRef<Path>>(path: P, size: u64) -> Result<File> {
1486 let file_name = path
1487 .as_ref()
1488 .to_str()
1489 .ok_or_else(|| Error::new(libc::EINVAL))?;
1490 let mut shm = SharedMemory::new(file_name, size)?;
1491 let mut seals = MemfdSeals::new();
1492
1493 seals.set_shrink_seal();
1494 seals.set_grow_seal();
1495 seals.set_seal_seal();
1496 shm.add_seals(seals)?;
1497
1498 Ok(shm.descriptor.into())
1499 }
1500
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, ) -> DeviceResult1501 pub fn create_iommu_device(
1502 protection_type: ProtectionType,
1503 jail_config: &Option<JailConfig>,
1504 iova_max_addr: u64,
1505 endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1506 hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
1507 translate_response_senders: Option<BTreeMap<u32, Tube>>,
1508 translate_request_rx: Option<Tube>,
1509 iommu_device_tube: Tube,
1510 ) -> DeviceResult {
1511 let dev = virtio::Iommu::new(
1512 virtio::base_features(protection_type),
1513 endpoints,
1514 iova_max_addr,
1515 hp_endpoints_ranges,
1516 translate_response_senders,
1517 translate_request_rx,
1518 Some(iommu_device_tube),
1519 )
1520 .context("failed to create IOMMU device")?;
1521
1522 Ok(VirtioDeviceStub {
1523 dev: Box::new(dev),
1524 jail: simple_jail(jail_config, "iommu_device")?,
1525 })
1526 }
1527
add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error>1528 fn add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error> {
1529 if let Some(path) = ¶m.path {
1530 if let SerialType::SystemSerialType = param.type_ {
1531 if let Some(parent) = path.as_path().parent() {
1532 if parent.exists() {
1533 info!("Bind mounting dir {}", parent.display());
1534 jail.mount_bind(parent, parent, true)?;
1535 }
1536 }
1537 }
1538 }
1539 Ok(())
1540 }
1541
1542 /// For creating console virtio devices.
1543 impl VirtioDeviceBuilder for &SerialParameters {
1544 const NAME: &'static str = "serial";
1545
create_virtio_device( self, protection_type: ProtectionType, ) -> anyhow::Result<Box<dyn VirtioDevice>>1546 fn create_virtio_device(
1547 self,
1548 protection_type: ProtectionType,
1549 ) -> anyhow::Result<Box<dyn VirtioDevice>> {
1550 let mut keep_rds = Vec::new();
1551 let evt = Event::new().context("failed to create event")?;
1552
1553 Ok(Box::new(
1554 self.create_serial_device::<Console>(protection_type, &evt, &mut keep_rds)
1555 .context("failed to create console device")?,
1556 ))
1557 }
1558
create_vhost_user_device( self, keep_rds: &mut Vec<RawDescriptor>, ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>>1559 fn create_vhost_user_device(
1560 self,
1561 keep_rds: &mut Vec<RawDescriptor>,
1562 ) -> anyhow::Result<Box<dyn VhostUserDeviceBuilder>> {
1563 Ok(Box::new(virtio::vhost::user::create_vu_console_device(
1564 self, keep_rds,
1565 )?))
1566 }
1567
create_jail( &self, jail_config: &Option<JailConfig>, virtio_transport: VirtioDeviceType, ) -> anyhow::Result<Option<Minijail>>1568 fn create_jail(
1569 &self,
1570 jail_config: &Option<JailConfig>,
1571 virtio_transport: VirtioDeviceType,
1572 ) -> anyhow::Result<Option<Minijail>> {
1573 if let Some(jail_config) = jail_config {
1574 let policy = virtio_transport.seccomp_policy_file("serial");
1575 let mut config = SandboxConfig::new(jail_config, &policy);
1576 config.bind_mounts = true;
1577 let mut jail =
1578 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
1579 add_bind_mounts(self, &mut jail)
1580 .context("failed to add bind mounts for console device")?;
1581 Ok(Some(jail))
1582 } else {
1583 Ok(None)
1584 }
1585 }
1586 }
1587
1588 #[cfg(feature = "audio")]
create_sound_device( path: &Path, protection_type: ProtectionType, jail_config: &Option<JailConfig>, ) -> DeviceResult1589 pub fn create_sound_device(
1590 path: &Path,
1591 protection_type: ProtectionType,
1592 jail_config: &Option<JailConfig>,
1593 ) -> DeviceResult {
1594 let dev = virtio::new_sound(path, virtio::base_features(protection_type))
1595 .context("failed to create sound device")?;
1596
1597 Ok(VirtioDeviceStub {
1598 dev: Box::new(dev),
1599 jail: simple_jail(jail_config, "vios_audio_device")?,
1600 })
1601 }
1602
1603 #[allow(clippy::large_enum_variant)]
1604 pub enum VfioDeviceVariant {
1605 Pci(VfioPciDevice),
1606 Platform(VfioPlatformDevice),
1607 }
1608
create_vfio_device( jail_config: &Option<JailConfig>, vm: &impl Vm, resources: &mut SystemAllocator, add_control_tube: &mut impl FnMut(AnyControlTube), vfio_path: &Path, hotplug: bool, hotplug_bus: Option<u8>, guest_address: Option<PciAddress>, coiommu_endpoints: Option<&mut Vec<u16>>, iommu_dev: IommuDevType, dt_symbol: Option<String>, vfio_container_manager: &mut VfioContainerManager, ) -> DeviceResult<(VfioDeviceVariant, Option<Minijail>, Option<VfioWrapper>)>1609 pub fn create_vfio_device(
1610 jail_config: &Option<JailConfig>,
1611 vm: &impl Vm,
1612 resources: &mut SystemAllocator,
1613 add_control_tube: &mut impl FnMut(AnyControlTube),
1614 vfio_path: &Path,
1615 hotplug: bool,
1616 hotplug_bus: Option<u8>,
1617 guest_address: Option<PciAddress>,
1618 coiommu_endpoints: Option<&mut Vec<u16>>,
1619 iommu_dev: IommuDevType,
1620 dt_symbol: Option<String>,
1621 vfio_container_manager: &mut VfioContainerManager,
1622 ) -> DeviceResult<(VfioDeviceVariant, Option<Minijail>, Option<VfioWrapper>)> {
1623 let vfio_container = vfio_container_manager
1624 .get_container(iommu_dev, Some(vfio_path))
1625 .context("failed to get vfio container")?;
1626
1627 let (vfio_host_tube_mem, vfio_device_tube_mem) =
1628 Tube::pair().context("failed to create tube")?;
1629 add_control_tube(
1630 VmMemoryTube {
1631 tube: vfio_host_tube_mem,
1632 expose_with_viommu: false,
1633 }
1634 .into(),
1635 );
1636
1637 let (vfio_host_tube_vm, vfio_device_tube_vm) = Tube::pair().context("failed to create tube")?;
1638 add_control_tube(TaggedControlTube::Vm(vfio_host_tube_vm).into());
1639
1640 let vfio_device =
1641 VfioDevice::new_passthrough(&vfio_path, vm, vfio_container.clone(), iommu_dev, dt_symbol)
1642 .context("failed to create vfio device")?;
1643
1644 match vfio_device.device_type() {
1645 VfioDeviceType::Pci => {
1646 let (vfio_host_tube_msi, vfio_device_tube_msi) =
1647 Tube::pair().context("failed to create tube")?;
1648 add_control_tube(AnyControlTube::IrqTube(vfio_host_tube_msi));
1649
1650 let (vfio_host_tube_msix, vfio_device_tube_msix) =
1651 Tube::pair().context("failed to create tube")?;
1652 add_control_tube(AnyControlTube::IrqTube(vfio_host_tube_msix));
1653
1654 let mut vfio_pci_device = VfioPciDevice::new(
1655 vfio_path,
1656 vfio_device,
1657 hotplug,
1658 hotplug_bus,
1659 guest_address,
1660 vfio_device_tube_msi,
1661 vfio_device_tube_msix,
1662 VmMemoryClient::new(vfio_device_tube_mem),
1663 vfio_device_tube_vm,
1664 )?;
1665 // early reservation for pass-through PCI devices.
1666 let endpoint_addr = vfio_pci_device
1667 .allocate_address(resources)
1668 .context("failed to allocate resources early for vfio pci dev")?;
1669
1670 let viommu_mapper = match iommu_dev {
1671 IommuDevType::NoIommu | IommuDevType::PkvmPviommu => None,
1672 IommuDevType::VirtioIommu => {
1673 Some(VfioWrapper::new(vfio_container, vm.get_memory().clone()))
1674 }
1675 IommuDevType::CoIommu => {
1676 if let Some(endpoints) = coiommu_endpoints {
1677 endpoints.push(endpoint_addr.to_u32() as u16);
1678 } else {
1679 bail!("Missed coiommu_endpoints vector to store the endpoint addr");
1680 }
1681 None
1682 }
1683 };
1684
1685 if hotplug {
1686 Ok((VfioDeviceVariant::Pci(vfio_pci_device), None, viommu_mapper))
1687 } else {
1688 Ok((
1689 VfioDeviceVariant::Pci(vfio_pci_device),
1690 simple_jail(jail_config, "vfio_device")?,
1691 viommu_mapper,
1692 ))
1693 }
1694 }
1695 VfioDeviceType::Platform => {
1696 if guest_address.is_some() {
1697 bail!("guest-address is not supported for VFIO platform devices");
1698 }
1699
1700 if hotplug {
1701 bail!("hotplug is not supported for VFIO platform devices");
1702 }
1703
1704 let vfio_plat_dev =
1705 VfioPlatformDevice::new(vfio_device, VmMemoryClient::new(vfio_device_tube_mem));
1706
1707 Ok((
1708 VfioDeviceVariant::Platform(vfio_plat_dev),
1709 simple_jail(jail_config, "vfio_platform_device")?,
1710 None,
1711 ))
1712 }
1713 }
1714 }
1715
1716 /// 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>)>1717 pub fn setup_virtio_access_platform(
1718 resources: &mut SystemAllocator,
1719 iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1720 devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)],
1721 ) -> DeviceResult<(Option<BTreeMap<u32, Tube>>, Option<Tube>)> {
1722 let mut translate_response_senders: Option<
1723 BTreeMap<
1724 u32, // endpoint id
1725 Tube,
1726 >,
1727 > = None;
1728 let mut tube_pair: Option<(Tube, Tube)> = None;
1729
1730 for dev in devices.iter_mut() {
1731 if let Some(pci_dev) = dev.0.as_pci_device_mut() {
1732 if pci_dev.supports_iommu() {
1733 let endpoint_id = pci_dev
1734 .allocate_address(resources)
1735 .context("failed to allocate resources for pci dev")?
1736 .to_u32();
1737 let mapper: Arc<Mutex<Box<dyn MemoryMapperTrait>>> =
1738 Arc::new(Mutex::new(Box::new(BasicMemoryMapper::new(u64::MAX))));
1739 let (request_tx, _request_rx) =
1740 tube_pair.get_or_insert_with(|| Tube::pair().unwrap());
1741 let CreateIpcMapperRet {
1742 mapper: ipc_mapper,
1743 response_tx,
1744 } = create_ipc_mapper(
1745 endpoint_id,
1746 #[allow(deprecated)]
1747 request_tx.try_clone()?,
1748 );
1749 translate_response_senders
1750 .get_or_insert_with(BTreeMap::new)
1751 .insert(endpoint_id, response_tx);
1752 iommu_attached_endpoints.insert(endpoint_id, mapper);
1753 pci_dev.set_iommu(ipc_mapper)?;
1754 }
1755 }
1756 }
1757
1758 Ok((
1759 translate_response_senders,
1760 tube_pair.map(|(_request_tx, request_rx)| request_rx),
1761 ))
1762 }
1763