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) = ¶m.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