• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::collections::BTreeMap;
6 use std::convert::TryFrom;
7 use std::fs::{File, OpenOptions};
8 use std::net::Ipv4Addr;
9 use std::ops::RangeInclusive;
10 use std::os::unix::net::UnixListener;
11 use std::os::unix::{net::UnixStream, prelude::OpenOptionsExt};
12 use std::path::{Path, PathBuf};
13 use std::str;
14 use std::sync::Arc;
15 
16 use crate::{
17     Config, DiskOption, TouchDeviceOption, VhostUserFsOption, VhostUserOption, VhostUserWlOption,
18     VvuOption,
19 };
20 use anyhow::{anyhow, bail, Context, Result};
21 use arch::{self, VirtioDeviceStub};
22 use base::*;
23 use devices::serial_device::{SerialParameters, SerialType};
24 use devices::vfio::{VfioCommonSetup, VfioCommonTrait};
25 use devices::virtio::ipc_memory_mapper::{create_ipc_mapper, CreateIpcMapperRet};
26 use devices::virtio::memory_mapper::{BasicMemoryMapper, MemoryMapperTrait};
27 #[cfg(feature = "audio_cras")]
28 use devices::virtio::snd::cras_backend::Parameters as CrasSndParameters;
29 use devices::virtio::vfio_wrapper::VfioWrapper;
30 use devices::virtio::vhost::user::proxy::VirtioVhostUser;
31 #[cfg(feature = "audio")]
32 use devices::virtio::vhost::user::vmm::Snd as VhostUserSnd;
33 use devices::virtio::vhost::user::vmm::{
34     Block as VhostUserBlock, Console as VhostUserConsole, Fs as VhostUserFs,
35     Mac80211Hwsim as VhostUserMac80211Hwsim, Net as VhostUserNet, Vsock as VhostUserVsock,
36     Wl as VhostUserWl,
37 };
38 use devices::virtio::vhost::vsock::VhostVsockConfig;
39 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
40 use devices::virtio::VideoBackendType;
41 use devices::virtio::{self, BalloonMode, Console, VirtioDevice};
42 use devices::IommuDevType;
43 #[cfg(feature = "tpm")]
44 use devices::SoftwareTpm;
45 use devices::{
46     self, BusDeviceObj, PciAddress, PciDevice, VfioDevice, VfioPciDevice, VfioPlatformDevice,
47 };
48 use hypervisor::Vm;
49 use minijail::{self, Minijail};
50 use net_util::{MacAddress, Tap, TapT};
51 use resources::{Alloc, MmioType, SystemAllocator};
52 use sync::Mutex;
53 use vm_memory::GuestAddress;
54 
55 use super::jail_helpers::*;
56 
57 pub enum TaggedControlTube {
58     Fs(Tube),
59     Vm(Tube),
60     VmMemory(Tube),
61     VmIrq(Tube),
62     VmMsync(Tube),
63 }
64 
65 impl AsRef<Tube> for TaggedControlTube {
as_ref(&self) -> &Tube66     fn as_ref(&self) -> &Tube {
67         use self::TaggedControlTube::*;
68         match &self {
69             Fs(tube) | Vm(tube) | VmMemory(tube) | VmIrq(tube) | VmMsync(tube) => tube,
70         }
71     }
72 }
73 
74 impl AsRawDescriptor for TaggedControlTube {
as_raw_descriptor(&self) -> RawDescriptor75     fn as_raw_descriptor(&self) -> RawDescriptor {
76         self.as_ref().as_raw_descriptor()
77     }
78 }
79 
80 pub trait IntoUnixStream {
into_unix_stream(self) -> Result<UnixStream>81     fn into_unix_stream(self) -> Result<UnixStream>;
82 }
83 
84 impl<'a> IntoUnixStream for &'a Path {
into_unix_stream(self) -> Result<UnixStream>85     fn into_unix_stream(self) -> Result<UnixStream> {
86         if let Some(fd) = safe_descriptor_from_path(self).context("failed to open event device")? {
87             Ok(fd.into())
88         } else {
89             UnixStream::connect(self).context("failed to open event device")
90         }
91     }
92 }
93 
94 impl<'a> IntoUnixStream for &'a PathBuf {
into_unix_stream(self) -> Result<UnixStream>95     fn into_unix_stream(self) -> Result<UnixStream> {
96         self.as_path().into_unix_stream()
97     }
98 }
99 
100 impl IntoUnixStream for UnixStream {
into_unix_stream(self) -> Result<UnixStream>101     fn into_unix_stream(self) -> Result<UnixStream> {
102         Ok(self)
103     }
104 }
105 
106 pub type DeviceResult<T = VirtioDeviceStub> = Result<T>;
107 
create_block_device( cfg: &Config, disk: &DiskOption, disk_device_tube: Tube, ) -> DeviceResult108 pub fn create_block_device(
109     cfg: &Config,
110     disk: &DiskOption,
111     disk_device_tube: Tube,
112 ) -> DeviceResult {
113     let mut options = OpenOptions::new();
114     options.read(true).write(!disk.read_only);
115 
116     #[cfg(unix)]
117     if disk.o_direct {
118         options.custom_flags(libc::O_DIRECT);
119     }
120 
121     let raw_image: File = open_file(&disk.path, &options)
122         .with_context(|| format!("failed to load disk image {}", disk.path.display()))?;
123     // Lock the disk image to prevent other crosvm instances from using it.
124     let lock_op = if disk.read_only {
125         FlockOperation::LockShared
126     } else {
127         FlockOperation::LockExclusive
128     };
129     flock(&raw_image, lock_op, true).context("failed to lock disk image")?;
130 
131     info!("Trying to attach block device: {}", disk.path.display());
132     let dev = if disk::async_ok(&raw_image).context("failed to check disk async_ok")? {
133         let async_file = disk::create_async_disk_file(raw_image)
134             .context("failed to create async virtual disk")?;
135         Box::new(
136             virtio::BlockAsync::new(
137                 virtio::base_features(cfg.protected_vm),
138                 async_file,
139                 disk.read_only,
140                 disk.sparse,
141                 disk.block_size,
142                 disk.id,
143                 Some(disk_device_tube),
144             )
145             .context("failed to create block device")?,
146         ) as Box<dyn VirtioDevice>
147     } else {
148         let disk_file = disk::create_disk_file(raw_image, disk::MAX_NESTING_DEPTH, &disk.path)
149             .context("failed to create virtual disk")?;
150         Box::new(
151             virtio::Block::new(
152                 virtio::base_features(cfg.protected_vm),
153                 disk_file,
154                 disk.read_only,
155                 disk.sparse,
156                 disk.block_size,
157                 disk.id,
158                 Some(disk_device_tube),
159             )
160             .context("failed to create block device")?,
161         ) as Box<dyn VirtioDevice>
162     };
163 
164     Ok(VirtioDeviceStub {
165         dev,
166         jail: simple_jail(&cfg.jail_config, "block_device")?,
167     })
168 }
169 
create_vhost_user_block_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult170 pub fn create_vhost_user_block_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult {
171     let dev = VhostUserBlock::new(virtio::base_features(cfg.protected_vm), &opt.socket)
172         .context("failed to set up vhost-user block device")?;
173 
174     Ok(VirtioDeviceStub {
175         dev: Box::new(dev),
176         // no sandbox here because virtqueue handling is exported to a different process.
177         jail: None,
178     })
179 }
180 
create_vhost_user_console_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult181 pub fn create_vhost_user_console_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult {
182     let dev = VhostUserConsole::new(virtio::base_features(cfg.protected_vm), &opt.socket)
183         .context("failed to set up vhost-user console device")?;
184 
185     Ok(VirtioDeviceStub {
186         dev: Box::new(dev),
187         // no sandbox here because virtqueue handling is exported to a different process.
188         jail: None,
189     })
190 }
191 
create_vhost_user_fs_device(cfg: &Config, option: &VhostUserFsOption) -> DeviceResult192 pub fn create_vhost_user_fs_device(cfg: &Config, option: &VhostUserFsOption) -> DeviceResult {
193     let dev = VhostUserFs::new(
194         virtio::base_features(cfg.protected_vm),
195         &option.socket,
196         &option.tag,
197     )
198     .context("failed to set up vhost-user fs device")?;
199 
200     Ok(VirtioDeviceStub {
201         dev: Box::new(dev),
202         // no sandbox here because virtqueue handling is exported to a different process.
203         jail: None,
204     })
205 }
206 
create_vhost_user_mac80211_hwsim_device( cfg: &Config, opt: &VhostUserOption, ) -> DeviceResult207 pub fn create_vhost_user_mac80211_hwsim_device(
208     cfg: &Config,
209     opt: &VhostUserOption,
210 ) -> DeviceResult {
211     let dev = VhostUserMac80211Hwsim::new(virtio::base_features(cfg.protected_vm), &opt.socket)
212         .context("failed to set up vhost-user mac80211_hwsim device")?;
213 
214     Ok(VirtioDeviceStub {
215         dev: Box::new(dev),
216         // no sandbox here because virtqueue handling is exported to a different process.
217         jail: None,
218     })
219 }
220 
221 #[cfg(feature = "audio")]
create_vhost_user_snd_device(cfg: &Config, option: &VhostUserOption) -> DeviceResult222 pub fn create_vhost_user_snd_device(cfg: &Config, option: &VhostUserOption) -> DeviceResult {
223     let dev = VhostUserSnd::new(virtio::base_features(cfg.protected_vm), &option.socket)
224         .context("failed to set up vhost-user snd device")?;
225 
226     Ok(VirtioDeviceStub {
227         dev: Box::new(dev),
228         // no sandbox here because virtqueue handling is exported to a different process.
229         jail: None,
230     })
231 }
232 
create_vvu_proxy_device( cfg: &Config, opt: &VvuOption, tube: Tube, max_sibling_mem_size: u64, ) -> DeviceResult233 pub fn create_vvu_proxy_device(
234     cfg: &Config,
235     opt: &VvuOption,
236     tube: Tube,
237     max_sibling_mem_size: u64,
238 ) -> DeviceResult {
239     let listener = UnixListener::bind(&opt.socket).map_err(|e| {
240         error!("failed to bind listener for vvu proxy device: {}", e);
241         e
242     })?;
243 
244     let dev = VirtioVhostUser::new(
245         virtio::base_features(cfg.protected_vm),
246         listener,
247         tube,
248         opt.addr,
249         opt.uuid,
250         max_sibling_mem_size,
251     )
252     .context("failed to create VVU proxy device")?;
253 
254     Ok(VirtioDeviceStub {
255         dev: Box::new(dev),
256         jail: simple_jail(&cfg.jail_config, "vvu_proxy_device")?,
257     })
258 }
259 
create_rng_device(cfg: &Config) -> DeviceResult260 pub fn create_rng_device(cfg: &Config) -> DeviceResult {
261     let dev = virtio::Rng::new(virtio::base_features(cfg.protected_vm))
262         .context("failed to set up rng")?;
263 
264     Ok(VirtioDeviceStub {
265         dev: Box::new(dev),
266         jail: simple_jail(&cfg.jail_config, "rng_device")?,
267     })
268 }
269 
270 #[cfg(feature = "audio_cras")]
create_cras_snd_device(cfg: &Config, cras_snd: CrasSndParameters) -> DeviceResult271 pub fn create_cras_snd_device(cfg: &Config, cras_snd: CrasSndParameters) -> DeviceResult {
272     let dev = virtio::snd::cras_backend::VirtioSndCras::new(
273         virtio::base_features(cfg.protected_vm),
274         cras_snd,
275     )
276     .context("failed to create cras sound device")?;
277 
278     let jail = match simple_jail(&cfg.jail_config, "cras_snd_device")? {
279         Some(mut jail) => {
280             // Create a tmpfs in the device's root directory for cras_snd_device.
281             // The size is 20*1024, or 20 KB.
282             jail.mount_with_data(
283                 Path::new("none"),
284                 Path::new("/"),
285                 "tmpfs",
286                 (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
287                 "size=20480",
288             )?;
289 
290             let run_cras_path = Path::new("/run/cras");
291             jail.mount_bind(run_cras_path, run_cras_path, true)?;
292 
293             add_current_user_to_jail(&mut jail)?;
294 
295             Some(jail)
296         }
297         None => None,
298     };
299 
300     Ok(VirtioDeviceStub {
301         dev: Box::new(dev),
302         jail,
303     })
304 }
305 
306 #[cfg(feature = "tpm")]
create_software_tpm_device(cfg: &Config) -> DeviceResult307 pub fn create_software_tpm_device(cfg: &Config) -> DeviceResult {
308     use std::ffi::CString;
309     use std::fs;
310     use std::process;
311 
312     let tpm_storage: PathBuf;
313     let mut tpm_jail = simple_jail(&cfg.jail_config, "tpm_device")?;
314 
315     match &mut tpm_jail {
316         Some(jail) => {
317             // Create a tmpfs in the device's root directory for tpm
318             // simulator storage. The size is 20*1024, or 20 KB.
319             jail.mount_with_data(
320                 Path::new("none"),
321                 Path::new("/"),
322                 "tmpfs",
323                 (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
324                 "size=20480",
325             )?;
326 
327             let crosvm_ids = add_current_user_to_jail(jail)?;
328 
329             let pid = process::id();
330             let tpm_pid_dir = format!("/run/vm/tpm.{}", pid);
331             tpm_storage = Path::new(&tpm_pid_dir).to_owned();
332             fs::create_dir_all(&tpm_storage).with_context(|| {
333                 format!("failed to create tpm storage dir {}", tpm_storage.display())
334             })?;
335             let tpm_pid_dir_c = CString::new(tpm_pid_dir).expect("no nul bytes");
336             chown(&tpm_pid_dir_c, crosvm_ids.uid, crosvm_ids.gid)
337                 .context("failed to chown tpm storage")?;
338 
339             jail.mount_bind(&tpm_storage, &tpm_storage, true)?;
340         }
341         None => {
342             // Path used inside cros_sdk which does not have /run/vm.
343             tpm_storage = Path::new("/tmp/tpm-simulator").to_owned();
344         }
345     }
346 
347     let backend = SoftwareTpm::new(tpm_storage).context("failed to create SoftwareTpm")?;
348     let dev = virtio::Tpm::new(Arc::new(Mutex::new(backend)));
349 
350     Ok(VirtioDeviceStub {
351         dev: Box::new(dev),
352         jail: tpm_jail,
353     })
354 }
355 
create_single_touch_device( cfg: &Config, single_touch_spec: &TouchDeviceOption, idx: u32, ) -> DeviceResult356 pub fn create_single_touch_device(
357     cfg: &Config,
358     single_touch_spec: &TouchDeviceOption,
359     idx: u32,
360 ) -> DeviceResult {
361     let socket = single_touch_spec
362         .get_path()
363         .into_unix_stream()
364         .map_err(|e| {
365             error!("failed configuring virtio single touch: {:?}", e);
366             e
367         })?;
368 
369     let (width, height) = single_touch_spec.get_size();
370     let dev = virtio::new_single_touch(
371         idx,
372         socket,
373         width,
374         height,
375         virtio::base_features(cfg.protected_vm),
376     )
377     .context("failed to set up input device")?;
378     Ok(VirtioDeviceStub {
379         dev: Box::new(dev),
380         jail: simple_jail(&cfg.jail_config, "input_device")?,
381     })
382 }
383 
create_multi_touch_device( cfg: &Config, multi_touch_spec: &TouchDeviceOption, idx: u32, ) -> DeviceResult384 pub fn create_multi_touch_device(
385     cfg: &Config,
386     multi_touch_spec: &TouchDeviceOption,
387     idx: u32,
388 ) -> DeviceResult {
389     let socket = multi_touch_spec
390         .get_path()
391         .into_unix_stream()
392         .map_err(|e| {
393             error!("failed configuring virtio multi touch: {:?}", e);
394             e
395         })?;
396 
397     let (width, height) = multi_touch_spec.get_size();
398     let dev = virtio::new_multi_touch(
399         idx,
400         socket,
401         width,
402         height,
403         virtio::base_features(cfg.protected_vm),
404     )
405     .context("failed to set up input device")?;
406 
407     Ok(VirtioDeviceStub {
408         dev: Box::new(dev),
409         jail: simple_jail(&cfg.jail_config, "input_device")?,
410     })
411 }
412 
create_trackpad_device( cfg: &Config, trackpad_spec: &TouchDeviceOption, idx: u32, ) -> DeviceResult413 pub fn create_trackpad_device(
414     cfg: &Config,
415     trackpad_spec: &TouchDeviceOption,
416     idx: u32,
417 ) -> DeviceResult {
418     let socket = trackpad_spec.get_path().into_unix_stream().map_err(|e| {
419         error!("failed configuring virtio trackpad: {:#}", e);
420         e
421     })?;
422 
423     let (width, height) = trackpad_spec.get_size();
424     let dev = virtio::new_trackpad(
425         idx,
426         socket,
427         width,
428         height,
429         virtio::base_features(cfg.protected_vm),
430     )
431     .context("failed to set up input device")?;
432 
433     Ok(VirtioDeviceStub {
434         dev: Box::new(dev),
435         jail: simple_jail(&cfg.jail_config, "input_device")?,
436     })
437 }
438 
create_mouse_device<T: IntoUnixStream>( cfg: &Config, mouse_socket: T, idx: u32, ) -> DeviceResult439 pub fn create_mouse_device<T: IntoUnixStream>(
440     cfg: &Config,
441     mouse_socket: T,
442     idx: u32,
443 ) -> DeviceResult {
444     let socket = mouse_socket.into_unix_stream().map_err(|e| {
445         error!("failed configuring virtio mouse: {:#}", e);
446         e
447     })?;
448 
449     let dev = virtio::new_mouse(idx, socket, virtio::base_features(cfg.protected_vm))
450         .context("failed to set up input device")?;
451 
452     Ok(VirtioDeviceStub {
453         dev: Box::new(dev),
454         jail: simple_jail(&cfg.jail_config, "input_device")?,
455     })
456 }
457 
create_keyboard_device<T: IntoUnixStream>( cfg: &Config, keyboard_socket: T, idx: u32, ) -> DeviceResult458 pub fn create_keyboard_device<T: IntoUnixStream>(
459     cfg: &Config,
460     keyboard_socket: T,
461     idx: u32,
462 ) -> DeviceResult {
463     let socket = keyboard_socket.into_unix_stream().map_err(|e| {
464         error!("failed configuring virtio keyboard: {:#}", e);
465         e
466     })?;
467 
468     let dev = virtio::new_keyboard(idx, socket, virtio::base_features(cfg.protected_vm))
469         .context("failed to set up input device")?;
470 
471     Ok(VirtioDeviceStub {
472         dev: Box::new(dev),
473         jail: simple_jail(&cfg.jail_config, "input_device")?,
474     })
475 }
476 
create_switches_device<T: IntoUnixStream>( cfg: &Config, switches_socket: T, idx: u32, ) -> DeviceResult477 pub fn create_switches_device<T: IntoUnixStream>(
478     cfg: &Config,
479     switches_socket: T,
480     idx: u32,
481 ) -> DeviceResult {
482     let socket = switches_socket.into_unix_stream().map_err(|e| {
483         error!("failed configuring virtio switches: {:#}", e);
484         e
485     })?;
486 
487     let dev = virtio::new_switches(idx, socket, virtio::base_features(cfg.protected_vm))
488         .context("failed to set up input device")?;
489 
490     Ok(VirtioDeviceStub {
491         dev: Box::new(dev),
492         jail: simple_jail(&cfg.jail_config, "input_device")?,
493     })
494 }
495 
create_vinput_device(cfg: &Config, dev_path: &Path) -> DeviceResult496 pub fn create_vinput_device(cfg: &Config, dev_path: &Path) -> DeviceResult {
497     let dev_file = OpenOptions::new()
498         .read(true)
499         .write(true)
500         .open(dev_path)
501         .with_context(|| format!("failed to open vinput device {}", dev_path.display()))?;
502 
503     let dev = virtio::new_evdev(dev_file, virtio::base_features(cfg.protected_vm))
504         .context("failed to set up input device")?;
505 
506     Ok(VirtioDeviceStub {
507         dev: Box::new(dev),
508         jail: simple_jail(&cfg.jail_config, "input_device")?,
509     })
510 }
511 
create_balloon_device( cfg: &Config, tube: Tube, inflate_tube: Option<Tube>, init_balloon_size: u64, ) -> DeviceResult512 pub fn create_balloon_device(
513     cfg: &Config,
514     tube: Tube,
515     inflate_tube: Option<Tube>,
516     init_balloon_size: u64,
517 ) -> DeviceResult {
518     let dev = virtio::Balloon::new(
519         virtio::base_features(cfg.protected_vm),
520         tube,
521         inflate_tube,
522         init_balloon_size,
523         if cfg.strict_balloon {
524             BalloonMode::Strict
525         } else {
526             BalloonMode::Relaxed
527         },
528     )
529     .context("failed to create balloon")?;
530 
531     Ok(VirtioDeviceStub {
532         dev: Box::new(dev),
533         jail: simple_jail(&cfg.jail_config, "balloon_device")?,
534     })
535 }
536 
537 /// Generic method for creating a network device. `create_device` is a closure that takes the virtio
538 /// features and number of queue pairs as parameters, and is responsible for creating the device
539 /// itself.
create_net_device<F, T>(cfg: &Config, policy: &str, create_device: F) -> DeviceResult where F: Fn(u64, u16) -> Result<T>, T: VirtioDevice + 'static,540 pub fn create_net_device<F, T>(cfg: &Config, policy: &str, create_device: F) -> DeviceResult
541 where
542     F: Fn(u64, u16) -> Result<T>,
543     T: VirtioDevice + 'static,
544 {
545     let mut vq_pairs = cfg.net_vq_pairs.unwrap_or(1);
546     let vcpu_count = cfg.vcpu_count.unwrap_or(1);
547     if vcpu_count < vq_pairs as usize {
548         warn!("the number of net vq pairs must not exceed the vcpu count, falling back to single queue mode");
549         vq_pairs = 1;
550     }
551     let features = virtio::base_features(cfg.protected_vm);
552 
553     let dev = create_device(features, vq_pairs)?;
554 
555     Ok(VirtioDeviceStub {
556         dev: Box::new(dev) as Box<dyn VirtioDevice>,
557         jail: simple_jail(&cfg.jail_config, policy)?,
558     })
559 }
560 
561 /// Returns a network device created from a new TAP interface configured with `host_ip`, `netmask`,
562 /// and `mac_address`.
create_net_device_from_config( cfg: &Config, host_ip: Ipv4Addr, netmask: Ipv4Addr, mac_address: MacAddress, ) -> DeviceResult563 pub fn create_net_device_from_config(
564     cfg: &Config,
565     host_ip: Ipv4Addr,
566     netmask: Ipv4Addr,
567     mac_address: MacAddress,
568 ) -> DeviceResult {
569     let policy = if cfg.vhost_net {
570         "vhost_net_device"
571     } else {
572         "net_device"
573     };
574 
575     if cfg.vhost_net {
576         create_net_device(cfg, policy, |features, _vq_pairs| {
577             virtio::vhost::Net::<Tap, vhost::Net<Tap>>::new(
578                 &cfg.vhost_net_device_path,
579                 features,
580                 host_ip,
581                 netmask,
582                 mac_address,
583             )
584             .context("failed to set up vhost networking")
585         })
586     } else {
587         create_net_device(cfg, policy, |features, vq_pairs| {
588             virtio::Net::<Tap>::new(features, host_ip, netmask, mac_address, vq_pairs)
589                 .context("failed to create virtio network device")
590         })
591     }
592 }
593 
594 /// Returns a network device from a file descriptor to a configured TAP interface.
create_tap_net_device_from_fd(cfg: &Config, tap_fd: RawDescriptor) -> DeviceResult595 pub fn create_tap_net_device_from_fd(cfg: &Config, tap_fd: RawDescriptor) -> DeviceResult {
596     create_net_device(cfg, "net_device", |features, vq_pairs| {
597         // Safe because we ensure that we get a unique handle to the fd.
598         let tap = unsafe {
599             Tap::from_raw_descriptor(
600                 validate_raw_descriptor(tap_fd).context("failed to validate tap descriptor")?,
601             )
602             .context("failed to create tap device")?
603         };
604 
605         virtio::Net::from(features, tap, vq_pairs).context("failed to create tap net device")
606     })
607 }
608 
609 /// Returns a network device created by opening the persistent, configured TAP interface `tap_name`.
create_tap_net_device_from_name(cfg: &Config, tap_name: &[u8]) -> DeviceResult610 pub fn create_tap_net_device_from_name(cfg: &Config, tap_name: &[u8]) -> DeviceResult {
611     create_net_device(cfg, "net_device", |features, vq_pairs| {
612         virtio::Net::<Tap>::new_from_name(features, tap_name, vq_pairs)
613             .context("failed to create configured virtio network device")
614     })
615 }
616 
create_vhost_user_net_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult617 pub fn create_vhost_user_net_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult {
618     let dev = VhostUserNet::new(virtio::base_features(cfg.protected_vm), &opt.socket)
619         .context("failed to set up vhost-user net device")?;
620 
621     Ok(VirtioDeviceStub {
622         dev: Box::new(dev),
623         // no sandbox here because virtqueue handling is exported to a different process.
624         jail: None,
625     })
626 }
627 
create_vhost_user_vsock_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult628 pub fn create_vhost_user_vsock_device(cfg: &Config, opt: &VhostUserOption) -> DeviceResult {
629     let dev = VhostUserVsock::new(virtio::base_features(cfg.protected_vm), &opt.socket)
630         .context("failed to set up vhost-user vsock device")?;
631 
632     Ok(VirtioDeviceStub {
633         dev: Box::new(dev),
634         // no sandbox here because virtqueue handling is exported to a different process.
635         jail: None,
636     })
637 }
638 
create_vhost_user_wl_device(cfg: &Config, opt: &VhostUserWlOption) -> DeviceResult639 pub fn create_vhost_user_wl_device(cfg: &Config, opt: &VhostUserWlOption) -> DeviceResult {
640     // The crosvm wl device expects us to connect the tube before it will accept a vhost-user
641     // connection.
642     let dev = VhostUserWl::new(virtio::base_features(cfg.protected_vm), &opt.socket)
643         .context("failed to set up vhost-user wl device")?;
644 
645     Ok(VirtioDeviceStub {
646         dev: Box::new(dev),
647         // no sandbox here because virtqueue handling is exported to a different process.
648         jail: None,
649     })
650 }
651 
create_wayland_device( cfg: &Config, control_tube: Tube, resource_bridge: Option<Tube>, ) -> DeviceResult652 pub fn create_wayland_device(
653     cfg: &Config,
654     control_tube: Tube,
655     resource_bridge: Option<Tube>,
656 ) -> DeviceResult {
657     let wayland_socket_dirs = cfg
658         .wayland_socket_paths
659         .iter()
660         .map(|(_name, path)| path.parent())
661         .collect::<Option<Vec<_>>>()
662         .ok_or_else(|| anyhow!("wayland socket path has no parent or file name"))?;
663 
664     let features = virtio::base_features(cfg.protected_vm);
665     let dev = virtio::Wl::new(
666         features,
667         cfg.wayland_socket_paths.clone(),
668         control_tube,
669         resource_bridge,
670     )
671     .context("failed to create wayland device")?;
672 
673     let jail = match simple_jail(&cfg.jail_config, "wl_device")? {
674         Some(mut jail) => {
675             // Create a tmpfs in the device's root directory so that we can bind mount the wayland
676             // socket directory into it. The size=67108864 is size=64*1024*1024 or size=64MB.
677             jail.mount_with_data(
678                 Path::new("none"),
679                 Path::new("/"),
680                 "tmpfs",
681                 (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
682                 "size=67108864",
683             )?;
684 
685             // Bind mount the wayland socket's directory into jail's root. This is necessary since
686             // each new wayland context must open() the socket. If the wayland socket is ever
687             // destroyed and remade in the same host directory, new connections will be possible
688             // without restarting the wayland device.
689             for dir in &wayland_socket_dirs {
690                 jail.mount_bind(dir, dir, true)?;
691             }
692             add_current_user_to_jail(&mut jail)?;
693 
694             Some(jail)
695         }
696         None => None,
697     };
698 
699     Ok(VirtioDeviceStub {
700         dev: Box::new(dev),
701         jail,
702     })
703 }
704 
705 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
create_video_device( backend: VideoBackendType, cfg: &Config, typ: devices::virtio::VideoDeviceType, resource_bridge: Tube, ) -> DeviceResult706 pub fn create_video_device(
707     backend: VideoBackendType,
708     cfg: &Config,
709     typ: devices::virtio::VideoDeviceType,
710     resource_bridge: Tube,
711 ) -> DeviceResult {
712     let jail = match simple_jail(&cfg.jail_config, "video_device")? {
713         Some(mut jail) => {
714             match typ {
715                 #[cfg(feature = "video-decoder")]
716                 devices::virtio::VideoDeviceType::Decoder => add_current_user_to_jail(&mut jail)?,
717                 #[cfg(feature = "video-encoder")]
718                 devices::virtio::VideoDeviceType::Encoder => add_current_user_to_jail(&mut jail)?,
719             };
720 
721             // Create a tmpfs in the device's root directory so that we can bind mount files.
722             jail.mount_with_data(
723                 Path::new("none"),
724                 Path::new("/"),
725                 "tmpfs",
726                 (libc::MS_NOSUID | libc::MS_NODEV | libc::MS_NOEXEC) as usize,
727                 "size=67108864",
728             )?;
729 
730             #[cfg(feature = "libvda")]
731             // Render node for libvda.
732             if backend == VideoBackendType::Libvda || backend == VideoBackendType::LibvdaVd {
733                 // follow the implementation at:
734                 // https://chromium.googlesource.com/chromiumos/platform/minigbm/+/c06cc9cccb3cf3c7f9d2aec706c27c34cd6162a0/cros_gralloc/cros_gralloc_driver.cc#90
735                 const DRM_NUM_NODES: u32 = 63;
736                 const DRM_RENDER_NODE_START: u32 = 128;
737                 for offset in 0..DRM_NUM_NODES {
738                     let path_str = format!("/dev/dri/renderD{}", DRM_RENDER_NODE_START + offset);
739                     let dev_dri_path = Path::new(&path_str);
740                     if !dev_dri_path.exists() {
741                         break;
742                     }
743                     jail.mount_bind(dev_dri_path, dev_dri_path, false)?;
744                 }
745             }
746 
747             #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
748             {
749                 // Device nodes used by libdrm through minigbm in libvda on AMD devices.
750                 let sys_dev_char_path = Path::new("/sys/dev/char");
751                 jail.mount_bind(sys_dev_char_path, sys_dev_char_path, false)?;
752                 let sys_devices_path = Path::new("/sys/devices");
753                 jail.mount_bind(sys_devices_path, sys_devices_path, false)?;
754 
755                 // Required for loading dri libraries loaded by minigbm on AMD devices.
756                 jail_mount_bind_if_exists(&mut jail, &["/usr/lib64"])?;
757             }
758 
759             // Device nodes required by libchrome which establishes Mojo connection in libvda.
760             let dev_urandom_path = Path::new("/dev/urandom");
761             jail.mount_bind(dev_urandom_path, dev_urandom_path, false)?;
762             let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
763             jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
764 
765             Some(jail)
766         }
767         None => None,
768     };
769 
770     Ok(VirtioDeviceStub {
771         dev: Box::new(devices::virtio::VideoDevice::new(
772             virtio::base_features(cfg.protected_vm),
773             typ,
774             backend,
775             Some(resource_bridge),
776         )),
777         jail,
778     })
779 }
780 
781 #[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
register_video_device( backend: VideoBackendType, devs: &mut Vec<VirtioDeviceStub>, video_tube: Tube, cfg: &Config, typ: devices::virtio::VideoDeviceType, ) -> Result<()>782 pub fn register_video_device(
783     backend: VideoBackendType,
784     devs: &mut Vec<VirtioDeviceStub>,
785     video_tube: Tube,
786     cfg: &Config,
787     typ: devices::virtio::VideoDeviceType,
788 ) -> Result<()> {
789     devs.push(create_video_device(backend, cfg, typ, video_tube)?);
790     Ok(())
791 }
792 
create_vhost_vsock_device(cfg: &Config, vhost_config: &VhostVsockConfig) -> DeviceResult793 pub fn create_vhost_vsock_device(cfg: &Config, vhost_config: &VhostVsockConfig) -> DeviceResult {
794     let features = virtio::base_features(cfg.protected_vm);
795 
796     let dev = virtio::vhost::Vsock::new(features, vhost_config)
797         .context("failed to set up virtual socket device")?;
798 
799     Ok(VirtioDeviceStub {
800         dev: Box::new(dev),
801         jail: simple_jail(&cfg.jail_config, "vhost_vsock_device")?,
802     })
803 }
804 
create_fs_device( cfg: &Config, uid_map: &str, gid_map: &str, src: &Path, tag: &str, fs_cfg: virtio::fs::passthrough::Config, device_tube: Tube, ) -> DeviceResult805 pub fn create_fs_device(
806     cfg: &Config,
807     uid_map: &str,
808     gid_map: &str,
809     src: &Path,
810     tag: &str,
811     fs_cfg: virtio::fs::passthrough::Config,
812     device_tube: Tube,
813 ) -> DeviceResult {
814     let max_open_files =
815         base::get_max_open_files().context("failed to get max number of open files")?;
816     let j = if let Some(jail_config) = &cfg.jail_config {
817         let seccomp_policy = jail_config.seccomp_policy_dir.join("fs_device");
818         let config = SandboxConfig {
819             limit_caps: false,
820             uid_map: Some(uid_map),
821             gid_map: Some(gid_map),
822             log_failures: jail_config.seccomp_log_failures,
823             seccomp_policy: &seccomp_policy,
824             // We want bind mounts from the parent namespaces to propagate into the fs device's
825             // namespace.
826             remount_mode: Some(libc::MS_SLAVE),
827         };
828         create_base_minijail(src, Some(max_open_files), Some(&config))?
829     } else {
830         create_base_minijail(src, Some(max_open_files), None)?
831     };
832 
833     let features = virtio::base_features(cfg.protected_vm);
834     // TODO(chirantan): Use more than one worker once the kernel driver has been fixed to not panic
835     // when num_queues > 1.
836     let dev = virtio::fs::Fs::new(features, tag, 1, fs_cfg, device_tube)
837         .context("failed to create fs device")?;
838 
839     Ok(VirtioDeviceStub {
840         dev: Box::new(dev),
841         jail: Some(j),
842     })
843 }
844 
create_9p_device( cfg: &Config, uid_map: &str, gid_map: &str, src: &Path, tag: &str, mut p9_cfg: p9::Config, ) -> DeviceResult845 pub fn create_9p_device(
846     cfg: &Config,
847     uid_map: &str,
848     gid_map: &str,
849     src: &Path,
850     tag: &str,
851     mut p9_cfg: p9::Config,
852 ) -> DeviceResult {
853     let max_open_files =
854         base::get_max_open_files().context("failed to get max number of open files")?;
855     let (jail, root) = if let Some(jail_config) = &cfg.jail_config {
856         let seccomp_policy = jail_config.seccomp_policy_dir.join("9p_device");
857         let config = SandboxConfig {
858             limit_caps: false,
859             uid_map: Some(uid_map),
860             gid_map: Some(gid_map),
861             log_failures: jail_config.seccomp_log_failures,
862             seccomp_policy: &seccomp_policy,
863             // We want bind mounts from the parent namespaces to propagate into the 9p server's
864             // namespace.
865             remount_mode: Some(libc::MS_SLAVE),
866         };
867 
868         let jail = create_base_minijail(src, Some(max_open_files), Some(&config))?;
869 
870         //  The shared directory becomes the root of the device's file system.
871         let root = Path::new("/");
872         (Some(jail), root)
873     } else {
874         // There's no mount namespace so we tell the server to treat the source directory as the
875         // root.
876         (None, src)
877     };
878 
879     let features = virtio::base_features(cfg.protected_vm);
880     p9_cfg.root = root.into();
881     let dev = virtio::P9::new(features, tag, p9_cfg).context("failed to create 9p device")?;
882 
883     Ok(VirtioDeviceStub {
884         dev: Box::new(dev),
885         jail,
886     })
887 }
888 
create_pmem_device( cfg: &Config, vm: &mut impl Vm, resources: &mut SystemAllocator, disk: &DiskOption, index: usize, pmem_device_tube: Tube, ) -> DeviceResult889 pub fn create_pmem_device(
890     cfg: &Config,
891     vm: &mut impl Vm,
892     resources: &mut SystemAllocator,
893     disk: &DiskOption,
894     index: usize,
895     pmem_device_tube: Tube,
896 ) -> DeviceResult {
897     let fd = open_file(
898         &disk.path,
899         OpenOptions::new().read(true).write(!disk.read_only),
900     )
901     .with_context(|| format!("failed to load disk image {}", disk.path.display()))?;
902 
903     let (disk_size, arena_size) = {
904         let metadata = std::fs::metadata(&disk.path).with_context(|| {
905             format!("failed to get disk image {} metadata", disk.path.display())
906         })?;
907         let disk_len = metadata.len();
908         // Linux requires pmem region sizes to be 2 MiB aligned. Linux will fill any partial page
909         // at the end of an mmap'd file and won't write back beyond the actual file length, but if
910         // we just align the size of the file to 2 MiB then access beyond the last page of the
911         // mapped file will generate SIGBUS. So use a memory mapping arena that will provide
912         // padding up to 2 MiB.
913         let alignment = 2 * 1024 * 1024;
914         let align_adjust = if disk_len % alignment != 0 {
915             alignment - (disk_len % alignment)
916         } else {
917             0
918         };
919         (
920             disk_len,
921             disk_len
922                 .checked_add(align_adjust)
923                 .ok_or_else(|| anyhow!("pmem device image too big"))?,
924         )
925     };
926 
927     let protection = {
928         if disk.read_only {
929             Protection::read()
930         } else {
931             Protection::read_write()
932         }
933     };
934 
935     let arena = {
936         // Conversion from u64 to usize may fail on 32bit system.
937         let arena_size = usize::try_from(arena_size).context("pmem device image too big")?;
938         let disk_size = usize::try_from(disk_size).context("pmem device image too big")?;
939 
940         let mut arena =
941             MemoryMappingArena::new(arena_size).context("failed to reserve pmem memory")?;
942         arena
943             .add_fd_offset_protection(0, disk_size, &fd, 0, protection)
944             .context("failed to reserve pmem memory")?;
945 
946         // If the disk is not a multiple of the page size, the OS will fill the remaining part
947         // of the page with zeroes. However, the anonymous mapping added below must start on a
948         // page boundary, so round up the size before calculating the offset of the anon region.
949         let disk_size = round_up_to_page_size(disk_size);
950 
951         if arena_size > disk_size {
952             // Add an anonymous region with the same protection as the disk mapping if the arena
953             // size was aligned.
954             arena
955                 .add_anon_protection(disk_size, arena_size - disk_size, protection)
956                 .context("failed to reserve pmem padding")?;
957         }
958         arena
959     };
960 
961     let mapping_address = resources
962         .mmio_allocator(MmioType::High)
963         .reverse_allocate_with_align(
964             arena_size,
965             Alloc::PmemDevice(index),
966             format!("pmem_disk_image_{}", index),
967             // Linux kernel requires pmem namespaces to be 128 MiB aligned.
968             128 * 1024 * 1024, /* 128 MiB */
969         )
970         .context("failed to allocate memory for pmem device")?;
971 
972     let slot = vm
973         .add_memory_region(
974             GuestAddress(mapping_address),
975             Box::new(arena),
976             /* read_only = */ disk.read_only,
977             /* log_dirty_pages = */ false,
978         )
979         .context("failed to add pmem device memory")?;
980 
981     let dev = virtio::Pmem::new(
982         virtio::base_features(cfg.protected_vm),
983         fd,
984         GuestAddress(mapping_address),
985         slot,
986         arena_size,
987         Some(pmem_device_tube),
988     )
989     .context("failed to create pmem device")?;
990 
991     Ok(VirtioDeviceStub {
992         dev: Box::new(dev) as Box<dyn VirtioDevice>,
993         jail: simple_jail(&cfg.jail_config, "pmem_device")?,
994     })
995 }
996 
create_iommu_device( cfg: &Config, phys_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, ) -> DeviceResult997 pub fn create_iommu_device(
998     cfg: &Config,
999     phys_max_addr: u64,
1000     endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1001     hp_endpoints_ranges: Vec<RangeInclusive<u32>>,
1002     translate_response_senders: Option<BTreeMap<u32, Tube>>,
1003     translate_request_rx: Option<Tube>,
1004     iommu_device_tube: Tube,
1005 ) -> DeviceResult {
1006     let dev = virtio::Iommu::new(
1007         virtio::base_features(cfg.protected_vm),
1008         endpoints,
1009         phys_max_addr,
1010         hp_endpoints_ranges,
1011         translate_response_senders,
1012         translate_request_rx,
1013         Some(iommu_device_tube),
1014     )
1015     .context("failed to create IOMMU device")?;
1016 
1017     Ok(VirtioDeviceStub {
1018         dev: Box::new(dev),
1019         jail: simple_jail(&cfg.jail_config, "iommu_device")?,
1020     })
1021 }
1022 
add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error>1023 fn add_bind_mounts(param: &SerialParameters, jail: &mut Minijail) -> Result<(), minijail::Error> {
1024     if let Some(path) = &param.path {
1025         if let SerialType::SystemSerialType = param.type_ {
1026             if let Some(parent) = path.as_path().parent() {
1027                 if parent.exists() {
1028                     info!("Bind mounting dir {}", parent.display());
1029                     jail.mount_bind(parent, parent, true)?;
1030                 }
1031             }
1032         }
1033     }
1034     Ok(())
1035 }
1036 
create_console_device(cfg: &Config, param: &SerialParameters) -> DeviceResult1037 pub fn create_console_device(cfg: &Config, param: &SerialParameters) -> DeviceResult {
1038     let mut keep_rds = Vec::new();
1039     let evt = Event::new().context("failed to create event")?;
1040     let dev = param
1041         .create_serial_device::<Console>(cfg.protected_vm, &evt, &mut keep_rds)
1042         .context("failed to create console device")?;
1043 
1044     let jail = match simple_jail(&cfg.jail_config, "serial")? {
1045         Some(mut jail) => {
1046             // Create a tmpfs in the device's root directory so that we can bind mount the
1047             // log socket directory into it.
1048             // The size=67108864 is size=64*1024*1024 or size=64MB.
1049             jail.mount_with_data(
1050                 Path::new("none"),
1051                 Path::new("/"),
1052                 "tmpfs",
1053                 (libc::MS_NODEV | libc::MS_NOEXEC | libc::MS_NOSUID) as usize,
1054                 "size=67108864",
1055             )?;
1056             add_current_user_to_jail(&mut jail)?;
1057             let res = add_bind_mounts(param, &mut jail);
1058             if res.is_err() {
1059                 error!("failed to add bind mounts for console device");
1060             }
1061             Some(jail)
1062         }
1063         None => None,
1064     };
1065 
1066     Ok(VirtioDeviceStub {
1067         dev: Box::new(dev),
1068         jail, // TODO(dverkamp): use a separate policy for console?
1069     })
1070 }
1071 
1072 #[cfg(feature = "audio")]
create_sound_device(path: &Path, cfg: &Config) -> DeviceResult1073 pub fn create_sound_device(path: &Path, cfg: &Config) -> DeviceResult {
1074     let dev = virtio::new_sound(path, virtio::base_features(cfg.protected_vm))
1075         .context("failed to create sound device")?;
1076 
1077     Ok(VirtioDeviceStub {
1078         dev: Box::new(dev),
1079         jail: simple_jail(&cfg.jail_config, "vios_audio_device")?,
1080     })
1081 }
1082 
create_vfio_device( cfg: &Config, vm: &impl Vm, resources: &mut SystemAllocator, control_tubes: &mut Vec<TaggedControlTube>, vfio_path: &Path, bus_num: Option<u8>, guest_address: Option<PciAddress>, iommu_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>, coiommu_endpoints: Option<&mut Vec<u16>>, iommu_dev: IommuDevType, ) -> DeviceResult<(Box<VfioPciDevice>, Option<Minijail>)>1083 pub fn create_vfio_device(
1084     cfg: &Config,
1085     vm: &impl Vm,
1086     resources: &mut SystemAllocator,
1087     control_tubes: &mut Vec<TaggedControlTube>,
1088     vfio_path: &Path,
1089     bus_num: Option<u8>,
1090     guest_address: Option<PciAddress>,
1091     iommu_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1092     coiommu_endpoints: Option<&mut Vec<u16>>,
1093     iommu_dev: IommuDevType,
1094 ) -> DeviceResult<(Box<VfioPciDevice>, Option<Minijail>)> {
1095     let vfio_container = VfioCommonSetup::vfio_get_container(iommu_dev, Some(vfio_path))
1096         .context("failed to get vfio container")?;
1097 
1098     // create MSI, MSI-X, and Mem request sockets for each vfio device
1099     let (vfio_host_tube_msi, vfio_device_tube_msi) =
1100         Tube::pair().context("failed to create tube")?;
1101     control_tubes.push(TaggedControlTube::VmIrq(vfio_host_tube_msi));
1102 
1103     let (vfio_host_tube_msix, vfio_device_tube_msix) =
1104         Tube::pair().context("failed to create tube")?;
1105     control_tubes.push(TaggedControlTube::VmIrq(vfio_host_tube_msix));
1106 
1107     let (vfio_host_tube_mem, vfio_device_tube_mem) =
1108         Tube::pair().context("failed to create tube")?;
1109     control_tubes.push(TaggedControlTube::VmMemory(vfio_host_tube_mem));
1110 
1111     let hotplug = bus_num.is_some();
1112     let vfio_device_tube_vm = if hotplug {
1113         let (vfio_host_tube_vm, device_tube_vm) = Tube::pair().context("failed to create tube")?;
1114         control_tubes.push(TaggedControlTube::Vm(vfio_host_tube_vm));
1115         Some(device_tube_vm)
1116     } else {
1117         None
1118     };
1119 
1120     let vfio_device = VfioDevice::new_passthrough(
1121         &vfio_path,
1122         vm,
1123         vfio_container.clone(),
1124         iommu_dev != IommuDevType::NoIommu,
1125     )
1126     .context("failed to create vfio device")?;
1127     let mut vfio_pci_device = Box::new(VfioPciDevice::new(
1128         #[cfg(feature = "direct")]
1129         vfio_path,
1130         vfio_device,
1131         bus_num,
1132         guest_address,
1133         vfio_device_tube_msi,
1134         vfio_device_tube_msix,
1135         vfio_device_tube_mem,
1136         vfio_device_tube_vm,
1137     ));
1138     // early reservation for pass-through PCI devices.
1139     let endpoint_addr = vfio_pci_device
1140         .allocate_address(resources)
1141         .context("failed to allocate resources early for vfio pci dev")?;
1142 
1143     match iommu_dev {
1144         IommuDevType::NoIommu => {}
1145         IommuDevType::VirtioIommu => {
1146             iommu_endpoints.insert(
1147                 endpoint_addr.to_u32(),
1148                 Arc::new(Mutex::new(Box::new(VfioWrapper::new(
1149                     vfio_container,
1150                     vm.get_memory().clone(),
1151                 )))),
1152             );
1153         }
1154         IommuDevType::CoIommu => {
1155             if let Some(endpoints) = coiommu_endpoints {
1156                 endpoints.push(endpoint_addr.to_u32() as u16);
1157             } else {
1158                 bail!("Missed coiommu_endpoints vector to store the endpoint addr");
1159             }
1160         }
1161     }
1162 
1163     if hotplug {
1164         Ok((vfio_pci_device, None))
1165     } else {
1166         Ok((
1167             vfio_pci_device,
1168             simple_jail(&cfg.jail_config, "vfio_device")?,
1169         ))
1170     }
1171 }
1172 
create_vfio_platform_device( cfg: &Config, vm: &impl Vm, _resources: &mut SystemAllocator, control_tubes: &mut Vec<TaggedControlTube>, vfio_path: &Path, _endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>, iommu_dev: IommuDevType, ) -> DeviceResult<(VfioPlatformDevice, Option<Minijail>)>1173 pub fn create_vfio_platform_device(
1174     cfg: &Config,
1175     vm: &impl Vm,
1176     _resources: &mut SystemAllocator,
1177     control_tubes: &mut Vec<TaggedControlTube>,
1178     vfio_path: &Path,
1179     _endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1180     iommu_dev: IommuDevType,
1181 ) -> DeviceResult<(VfioPlatformDevice, Option<Minijail>)> {
1182     let vfio_container = VfioCommonSetup::vfio_get_container(iommu_dev, Some(vfio_path))
1183         .context("Failed to create vfio device")?;
1184 
1185     let (vfio_host_tube_mem, vfio_device_tube_mem) =
1186         Tube::pair().context("failed to create tube")?;
1187     control_tubes.push(TaggedControlTube::VmMemory(vfio_host_tube_mem));
1188 
1189     let vfio_device = VfioDevice::new_passthrough(
1190         &vfio_path,
1191         vm,
1192         vfio_container,
1193         iommu_dev != IommuDevType::NoIommu,
1194     )
1195     .context("Failed to create vfio device")?;
1196     let vfio_plat_dev = VfioPlatformDevice::new(vfio_device, vfio_device_tube_mem);
1197 
1198     Ok((
1199         vfio_plat_dev,
1200         simple_jail(&cfg.jail_config, "vfio_platform_device")?,
1201     ))
1202 }
1203 
1204 /// Setup for devices with VIRTIO_F_ACCESS_PLATFORM
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>)>1205 pub fn setup_virtio_access_platform(
1206     resources: &mut SystemAllocator,
1207     iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1208     devices: &mut [(Box<dyn BusDeviceObj>, Option<Minijail>)],
1209 ) -> DeviceResult<(Option<BTreeMap<u32, Tube>>, Option<Tube>)> {
1210     let mut translate_response_senders: Option<
1211         BTreeMap<
1212             u32, // endpoint id
1213             Tube,
1214         >,
1215     > = None;
1216     let mut tube_pair: Option<(Tube, Tube)> = None;
1217 
1218     for dev in devices.iter_mut() {
1219         if let Some(pci_dev) = dev.0.as_pci_device_mut() {
1220             if pci_dev.supports_iommu() {
1221                 let endpoint_id = pci_dev
1222                     .allocate_address(resources)
1223                     .context("failed to allocate resources for pci dev")?
1224                     .to_u32();
1225                 let mapper: Arc<Mutex<Box<dyn MemoryMapperTrait>>> =
1226                     Arc::new(Mutex::new(Box::new(BasicMemoryMapper::new(u64::MAX))));
1227                 let (request_tx, _request_rx) =
1228                     tube_pair.get_or_insert_with(|| Tube::pair().unwrap());
1229                 let CreateIpcMapperRet {
1230                     mapper: ipc_mapper,
1231                     response_tx,
1232                 } = create_ipc_mapper(
1233                     endpoint_id,
1234                     #[allow(deprecated)]
1235                     request_tx.try_clone()?,
1236                 );
1237                 translate_response_senders
1238                     .get_or_insert_with(BTreeMap::new)
1239                     .insert(endpoint_id, response_tx);
1240                 iommu_attached_endpoints.insert(endpoint_id, mapper);
1241                 pci_dev.set_iommu(ipc_mapper)?;
1242             }
1243         }
1244     }
1245 
1246     Ok((
1247         translate_response_senders,
1248         tube_pair.map(|(_request_tx, request_rx)| request_rx),
1249     ))
1250 }
1251