1#[cfg(target_os = "android")]
6mod android;
7pub mod cmdline;
8pub mod config;
9mod device_helpers;
10pub(crate) mod ext2;
11#[cfg(feature = "gpu")]
12pub(crate) mod gpu;
13#[cfg(feature = "pci-hotplug")]
14pub(crate) mod jail_warden;
15#[cfg(feature = "pci-hotplug")]
16pub(crate) mod pci_hotplug_helpers;
17#[cfg(feature = "pci-hotplug")]
18pub(crate) mod pci_hotplug_manager;
19mod vcpu;
20
21#[cfg(all(feature = "pvclock", target_arch = "aarch64"))]
22use std::arch::asm;
23use std::cmp::max;
24use std::collections::BTreeMap;
25use std::collections::BTreeSet;
26#[cfg(feature = "registered_events")]
27use std::collections::HashMap;
28#[cfg(feature = "registered_events")]
29use std::collections::HashSet;
30use std::convert::TryInto;
31use std::ffi::CString;
32#[cfg(target_arch = "aarch64")]
33use std::fs::create_dir_all;
34use std::fs::File;
35use std::fs::OpenOptions;
36#[cfg(feature = "registered_events")]
37use std::hash::Hash;
38use std::io::stdin;
39use std::iter;
40use std::mem;
41#[cfg(target_arch = "x86_64")]
42use std::ops::RangeInclusive;
43use std::os::unix::process::ExitStatusExt;
44use std::path::Path;
45#[cfg(target_arch = "aarch64")]
46use std::path::PathBuf;
47#[cfg(target_arch = "aarch64")]
48use std::process;
49#[cfg(feature = "registered_events")]
50use std::rc::Rc;
51use std::sync::mpsc;
52use std::sync::Arc;
53use std::sync::Barrier;
54use std::thread::JoinHandle;
55
56#[cfg(target_arch = "aarch64")]
57use aarch64::AArch64 as Arch;
58use acpi_tables::sdt::SDT;
59use anyhow::anyhow;
60use anyhow::bail;
61use anyhow::Context;
62use anyhow::Result;
63use arch::DtbOverlay;
64use arch::IrqChipArch;
65use arch::LinuxArch;
66use arch::RunnableLinuxVm;
67use arch::VcpuAffinity;
68use arch::VirtioDeviceStub;
69use arch::VmArch;
70use arch::VmComponents;
71use arch::VmImage;
72use arch::DEFAULT_CPU_CAPACITY;
73use argh::FromArgs;
74use base::ReadNotifier;
75#[cfg(feature = "balloon")]
76use base::UnixSeqpacket;
77use base::UnixSeqpacketListener;
78use base::UnlinkUnixSeqpacketListener;
79use base::*;
80use cros_async::Executor;
81use device_helpers::*;
82use devices::create_devices_worker_thread;
83use devices::serial_device::SerialHardware;
84#[cfg(all(feature = "pvclock", target_arch = "x86_64"))]
85use devices::tsc::get_tsc_sync_mitigations;
86use devices::vfio::VfioContainerManager;
87#[cfg(feature = "gpu")]
88use devices::virtio;
89#[cfg(any(feature = "video-decoder", feature = "video-encoder"))]
90use devices::virtio::device_constants::video::VideoDeviceType;
91#[cfg(feature = "gpu")]
92use devices::virtio::gpu::EventDevice;
93#[cfg(target_arch = "x86_64")]
94use devices::virtio::memory_mapper::MemoryMapper;
95use devices::virtio::memory_mapper::MemoryMapperTrait;
96use devices::virtio::vhost_user_backend::VhostUserConnectionTrait;
97use devices::virtio::vhost_user_backend::VhostUserListener;
98#[cfg(feature = "balloon")]
99use devices::virtio::BalloonFeatures;
100#[cfg(feature = "pci-hotplug")]
101use devices::virtio::NetParameters;
102#[cfg(feature = "pci-hotplug")]
103use devices::virtio::NetParametersMode;
104use devices::virtio::VirtioDevice;
105use devices::virtio::VirtioDeviceType;
106use devices::Bus;
107use devices::BusDeviceObj;
108use devices::BusType;
109use devices::CoIommuDev;
110#[cfg(feature = "usb")]
111use devices::DeviceProvider;
112#[cfg(target_arch = "x86_64")]
113use devices::HotPlugBus;
114#[cfg(target_arch = "x86_64")]
115use devices::HotPlugKey;
116use devices::IommuDevType;
117use devices::IrqEventIndex;
118use devices::IrqEventSource;
119#[cfg(feature = "pci-hotplug")]
120use devices::NetResourceCarrier;
121#[cfg(target_arch = "x86_64")]
122use devices::PciAddress;
123#[cfg(target_arch = "x86_64")]
124use devices::PciBridge;
125use devices::PciDevice;
126#[cfg(target_arch = "x86_64")]
127use devices::PciMmioMapper;
128#[cfg(target_arch = "x86_64")]
129use devices::PciRoot;
130#[cfg(target_arch = "x86_64")]
131use devices::PciRootCommand;
132#[cfg(target_arch = "x86_64")]
133use devices::PcieDownstreamPort;
134#[cfg(target_arch = "x86_64")]
135use devices::PcieHostPort;
136#[cfg(target_arch = "x86_64")]
137use devices::PcieRootPort;
138#[cfg(target_arch = "x86_64")]
139use devices::PcieUpstreamPort;
140use devices::PvPanicCode;
141use devices::PvPanicPciDevice;
142#[cfg(feature = "pci-hotplug")]
143use devices::ResourceCarrier;
144use devices::StubPciDevice;
145use devices::VirtioDeviceArgs;
146use devices::VirtioDeviceModule;
147use devices::VirtioPciDevice;
148#[cfg(feature = "usb")]
149use devices::XhciController;
150#[cfg(feature = "gpu")]
151use gpu::*;
152#[cfg(target_arch = "riscv64")]
153use hypervisor::CpuConfigRiscv64;
154#[cfg(target_arch = "x86_64")]
155use hypervisor::CpuConfigX86_64;
156use hypervisor::Hypervisor;
157use hypervisor::HypervisorCap;
158use hypervisor::MemCacheType;
159use hypervisor::ProtectionType;
160use hypervisor::Vm;
161use hypervisor::VmCap;
162use jail::*;
163#[cfg(feature = "pci-hotplug")]
164use jail_warden::JailWarden;
165#[cfg(feature = "pci-hotplug")]
166use jail_warden::JailWardenImpl;
167#[cfg(feature = "pci-hotplug")]
168use jail_warden::PermissiveJailWarden;
169use libc;
170use metrics::MetricsController;
171use minijail::Minijail;
172#[cfg(feature = "pci-hotplug")]
173use pci_hotplug_manager::PciHotPlugManager;
174use resources::AddressRange;
175use resources::Alloc;
176use resources::SystemAllocator;
177#[cfg(target_arch = "riscv64")]
178use riscv64::Riscv64 as Arch;
179#[cfg(feature = "gpu")]
180use rutabaga_gfx::RutabagaGralloc;
181#[cfg(feature = "gpu")]
182use rutabaga_gfx::RutabagaGrallocBackendFlags;
183use smallvec::SmallVec;
184#[cfg(feature = "swap")]
185use swap::SwapController;
186use sync::Condvar;
187use sync::Mutex;
188use vm_control::api::VmMemoryClient;
189use vm_control::*;
190use vm_memory::FileBackedMappingParameters;
191use vm_memory::GuestAddress;
192use vm_memory::GuestMemory;
193use vm_memory::MemoryPolicy;
194use vm_memory::MemoryRegionOptions;
195#[cfg(target_arch = "x86_64")]
196use x86_64::X8664arch as Arch;
197
198use crate::crosvm::config::Config;
199use crate::crosvm::config::Executable;
200use crate::crosvm::config::HypervisorKind;
201use crate::crosvm::config::InputDeviceOption;
202use crate::crosvm::config::IrqChipKind;
203use crate::crosvm::config::DEFAULT_TOUCH_DEVICE_HEIGHT;
204use crate::crosvm::config::DEFAULT_TOUCH_DEVICE_WIDTH;
205#[cfg(feature = "gdb")]
206use crate::crosvm::gdb::gdb_thread;
207#[cfg(feature = "gdb")]
208use crate::crosvm::gdb::GdbStub;
209#[cfg(target_arch = "x86_64")]
210use crate::crosvm::ratelimit::Ratelimit;
211use crate::crosvm::sys::cmdline::DevicesCommand;
212use crate::crosvm::sys::config::SharedDir;
213use crate::crosvm::sys::config::SharedDirKind;
214use crate::crosvm::sys::platform::vcpu::VcpuPidTid;
215
216const KVM_PATH: &str = "/dev/kvm";
217#[cfg(all(target_arch = "aarch64", feature = "geniezone"))]
218const GENIEZONE_PATH: &str = "/dev/gzvm";
219#[cfg(all(target_arch = "aarch64", feature = "gunyah"))]
220static GUNYAH_PATH: &str = "/dev/gunyah";
221#[cfg(target_arch = "aarch64")]
222#[cfg(feature = "halla")]
223const HALLA_PATH: &str = "/dev/halla";
224
225fn create_virtio_devices(
226 cfg: &Config,
227 vm: &dyn VmArch,
228 resources: &mut SystemAllocator,
229 add_control_tube: &mut impl FnMut(AnyControlTube),
230 #[cfg_attr(not(feature = "gpu"), allow(unused_variables))] vm_evt_wrtube: &SendTube,
231 #[cfg(feature = "balloon")] balloon_inflate_tube: Option<Tube>,
232 worker_process_pids: &mut BTreeSet<Pid>,
233 #[cfg(feature = "gpu")] render_server_fd: Option<SafeDescriptor>,
234 #[cfg(feature = "gpu")] has_vfio_gfx_device: bool,
235 #[cfg(feature = "registered_events")] registered_evt_q: &SendTube,
236) -> DeviceResult<Vec<VirtioDeviceStub>> {
237 let mut devs = Vec::new();
238
239 #[cfg(any(feature = "gpu", feature = "video-decoder", feature = "video-encoder"))]
240 let mut resource_bridges = Vec::<Tube>::new();
241
242 if !cfg.wayland_socket_paths.is_empty() {
243 #[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
244 let mut wl_resource_bridge = None::<Tube>;
245
246 #[cfg(feature = "gpu")]
247 {
248 if cfg.gpu_parameters.is_some() {
249 let (wl_socket, gpu_socket) = Tube::pair().context("failed to create tube")?;
250 resource_bridges.push(gpu_socket);
251 wl_resource_bridge = Some(wl_socket);
252 }
253 }
254
255 devs.push((
256 "wayland",
257 create_wayland_device(
258 cfg.protection_type,
259 cfg.jail_config.as_ref(),
260 &cfg.wayland_socket_paths,
261 wl_resource_bridge,
262 )?,
263 ));
264 }
265
266 #[cfg(all(feature = "media", feature = "video-decoder"))]
267 let media_adapter_cfg = cfg
268 .media_decoder
269 .iter()
270 .map(|config| {
271 let (video_tube, gpu_tube) =
272 Tube::pair().expect("failed to create tube for media adapter");
273 resource_bridges.push(gpu_tube);
274 (video_tube, config.backend)
275 })
276 .collect::<Vec<_>>();
277
278 #[cfg(feature = "video-decoder")]
279 let video_dec_cfg = cfg
280 .video_dec
281 .iter()
282 .map(|config| {
283 let (video_tube, gpu_tube) =
284 Tube::pair().expect("failed to create tube for video decoder");
285 resource_bridges.push(gpu_tube);
286 (video_tube, config.backend)
287 })
288 .collect::<Vec<_>>();
289
290 #[cfg(feature = "video-encoder")]
291 let video_enc_cfg = cfg
292 .video_enc
293 .iter()
294 .map(|config| {
295 let (video_tube, gpu_tube) =
296 Tube::pair().expect("failed to create tube for video encoder");
297 resource_bridges.push(gpu_tube);
298 (video_tube, config.backend)
299 })
300 .collect::<Vec<_>>();
301
302 #[cfg(feature = "gpu")]
303 {
304 if let Some(gpu_parameters) = &cfg.gpu_parameters {
305 let mut event_devices = Vec::new();
306 if cfg.display_window_mouse {
307 let display_param = if gpu_parameters.display_params.is_empty() {
308 Default::default()
309 } else {
310 gpu_parameters.display_params[0].clone()
311 };
312 let (gpu_display_w, gpu_display_h) = display_param.get_virtual_display_size();
313
314 let (event_device_socket, virtio_dev_socket) =
315 StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Byte)
316 .context("failed to create socket")?;
317 let mut multi_touch_width = gpu_display_w;
318 let mut multi_touch_height = gpu_display_h;
319 let mut multi_touch_name = None;
320 for input in &cfg.virtio_input {
321 if let InputDeviceOption::MultiTouch {
322 width,
323 height,
324 name,
325 ..
326 } = input
327 {
328 if let Some(width) = width {
329 multi_touch_width = *width;
330 }
331 if let Some(height) = height {
332 multi_touch_height = *height;
333 }
334 if let Some(name) = name {
335 multi_touch_name = Some(name.as_str());
336 }
337 break;
338 }
339 }
340 let dev = virtio::input::new_multi_touch(
341 u32::MAX,
344 virtio_dev_socket,
345 multi_touch_width,
346 multi_touch_height,
347 multi_touch_name,
348 virtio::base_features(cfg.protection_type),
349 )
350 .context("failed to set up mouse device")?;
351 devs.push((
352 "multi_touch",
353 VirtioDeviceStub {
354 dev: Box::new(dev),
355 jail: simple_jail(cfg.jail_config.as_ref(), "input_device")?,
356 },
357 ));
358 event_devices.push(EventDevice::touchscreen(event_device_socket));
359 }
360 if cfg.display_window_keyboard {
361 let (event_device_socket, virtio_dev_socket) =
362 StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Byte)
363 .context("failed to create socket")?;
364 let dev = virtio::input::new_keyboard(
365 u32::MAX,
368 virtio_dev_socket,
369 virtio::base_features(cfg.protection_type),
370 )
371 .context("failed to set up keyboard device")?;
372 devs.push((
373 "window_keyboard",
374 VirtioDeviceStub {
375 dev: Box::new(dev),
376 jail: simple_jail(cfg.jail_config.as_ref(), "input_device")?,
377 },
378 ));
379 event_devices.push(EventDevice::keyboard(event_device_socket));
380 }
381
382 let (gpu_control_host_tube, gpu_control_device_tube) =
383 Tube::pair().context("failed to create gpu tube")?;
384 add_control_tube(AnyControlTube::Gpu(gpu_control_host_tube));
385 devs.push((
386 "gpu",
387 create_gpu_device(
388 cfg,
389 vm_evt_wrtube,
390 gpu_control_device_tube,
391 resource_bridges,
392 render_server_fd,
393 has_vfio_gfx_device,
394 event_devices,
395 )?,
396 ));
397 }
398 }
399
400 for (_, param) in cfg
401 .serial_parameters
402 .iter()
403 .filter(|(_k, v)| v.hardware == SerialHardware::VirtioConsole)
404 {
405 let dev =
406 param.create_virtio_device_and_jail(cfg.protection_type, cfg.jail_config.as_ref())?;
407 devs.push(("console", dev));
408 }
409
410 for disk in &cfg.disks {
411 let (disk_host_tube, disk_device_tube) = Tube::pair().context("failed to create tube")?;
412 add_control_tube(AnyControlTube::Disk(disk_host_tube));
413 let disk_config = DiskConfig::new(disk, Some(disk_device_tube));
414 devs.push((
415 "disk",
416 disk_config
417 .create_virtio_device_and_jail(cfg.protection_type, cfg.jail_config.as_ref())?,
418 ));
419 }
420
421 if !cfg.scsis.is_empty() {
422 let scsi_config = ScsiConfig(&cfg.scsis);
423 devs.push((
424 "scsi",
425 scsi_config
426 .create_virtio_device_and_jail(cfg.protection_type, cfg.jail_config.as_ref())?,
427 ));
428 }
429
430 for (index, pmem_disk) in cfg.pmems.iter().enumerate() {
431 let (pmem_host_tube, pmem_device_tube) = Tube::pair().context("failed to create tube")?;
432 add_control_tube(AnyControlTube::VmMsync(pmem_host_tube));
433 devs.push((
434 "pmem",
435 create_pmem_device(
436 cfg.protection_type,
437 cfg.jail_config.as_ref(),
438 vm,
439 resources,
440 pmem_disk,
441 index,
442 pmem_device_tube,
443 )?,
444 ));
445 }
446
447 for (index, pmem_ext2) in cfg.pmem_ext2.iter().enumerate() {
448 let (pmem_ext2_host_tube, pmem_ext2_device_tube) =
451 Tube::pair().context("failed to create tube")?;
452 let vm_memory_client = VmMemoryClient::new(pmem_ext2_device_tube);
453 add_control_tube(AnyControlTube::VmMemoryTube {
454 tube: pmem_ext2_host_tube,
455 expose_with_viommu: false,
456 remote_peer: cfg.jail_config.is_some(),
457 });
458 let (pmem_host_tube, pmem_device_tube) = Tube::pair().context("failed to create tube")?;
459 add_control_tube(AnyControlTube::VmMsync(pmem_host_tube));
460 devs.push((
461 "pmem_ext2",
462 create_pmem_ext2_device(
463 cfg.protection_type,
464 cfg.jail_config.as_ref(),
465 resources,
466 pmem_ext2,
467 index,
468 vm_memory_client,
469 pmem_device_tube,
470 worker_process_pids,
471 )?,
472 ));
473 }
474
475 #[cfg(feature = "pvclock")]
476 if cfg.pvclock {
477 let (host_suspend_tube, suspend_tube) = Tube::pair().context("failed to create tube")?;
479 add_control_tube(AnyControlTube::PvClock(host_suspend_tube));
480
481 let frequency: u64;
482 #[cfg(target_arch = "x86_64")]
483 {
484 let tsc_state = devices::tsc::tsc_state()?;
485 let tsc_sync_mitigations =
486 get_tsc_sync_mitigations(&tsc_state, cfg.vcpu_count.unwrap_or(1));
487 if tsc_state.core_grouping.size() > 1 {
488 warn!(
490 "Host TSCs are not in sync, applying the following mitigations: {:?}",
491 tsc_sync_mitigations
492 );
493 }
494 frequency = tsc_state.frequency;
495 }
496 #[cfg(target_arch = "aarch64")]
497 {
498 let mut x: u64;
499 unsafe {
502 asm!("mrs {x}, cntfrq_el0",
503 x = out(reg) x,
504 );
505 }
506 frequency = x;
507
508 vm.set_counter_offset(0)
512 .context("failed to set up pvclock")?;
513 }
514 let dev = create_pvclock_device(
515 cfg.protection_type,
516 cfg.jail_config.as_ref(),
517 frequency,
518 suspend_tube,
519 )?;
520 devs.push(("pvclock", dev));
521 info!("virtio-pvclock is enabled for this vm");
522 }
523
524 let mut keyboard_idx = 0;
525 let mut mouse_idx = 0;
526 let mut rotary_idx = 0;
527 let mut switches_idx = 0;
528 let mut multi_touch_idx = 0;
529 let mut single_touch_idx = 0;
530 let mut trackpad_idx = 0;
531 let mut multi_touch_trackpad_idx = 0;
532 let mut custom_idx = 0;
533 for input in &cfg.virtio_input {
534 let input_dev = match input {
535 InputDeviceOption::Evdev { path } => create_vinput_device(
536 cfg.protection_type,
537 cfg.jail_config.as_ref(),
538 path.as_path(),
539 )?,
540 InputDeviceOption::Keyboard { path } => {
541 let dev = create_keyboard_device(
542 cfg.protection_type,
543 cfg.jail_config.as_ref(),
544 path.as_path(),
545 keyboard_idx,
546 )?;
547 keyboard_idx += 1;
548 dev
549 }
550 InputDeviceOption::Mouse { path } => {
551 let dev = create_mouse_device(
552 cfg.protection_type,
553 cfg.jail_config.as_ref(),
554 path.as_path(),
555 mouse_idx,
556 )?;
557 mouse_idx += 1;
558 dev
559 }
560 InputDeviceOption::MultiTouch {
561 path,
562 width,
563 height,
564 name,
565 } => {
566 let mut width = *width;
567 let mut height = *height;
568 if multi_touch_idx == 0 {
569 if width.is_none() {
570 width = cfg.display_input_width;
571 }
572 if height.is_none() {
573 height = cfg.display_input_height;
574 }
575 }
576 let dev = create_multi_touch_device(
577 cfg.protection_type,
578 cfg.jail_config.as_ref(),
579 path.as_path(),
580 width.unwrap_or(DEFAULT_TOUCH_DEVICE_WIDTH),
581 height.unwrap_or(DEFAULT_TOUCH_DEVICE_HEIGHT),
582 name.as_deref(),
583 multi_touch_idx,
584 )?;
585 multi_touch_idx += 1;
586 dev
587 }
588 InputDeviceOption::Rotary { path } => {
589 let dev = create_rotary_device(
590 cfg.protection_type,
591 cfg.jail_config.as_ref(),
592 path.as_path(),
593 rotary_idx,
594 )?;
595 rotary_idx += 1;
596 dev
597 }
598 InputDeviceOption::SingleTouch {
599 path,
600 width,
601 height,
602 name,
603 } => {
604 let mut width = *width;
605 let mut height = *height;
606 if single_touch_idx == 0 {
607 if width.is_none() {
608 width = cfg.display_input_width;
609 }
610 if height.is_none() {
611 height = cfg.display_input_height;
612 }
613 }
614 let dev = create_single_touch_device(
615 cfg.protection_type,
616 cfg.jail_config.as_ref(),
617 path.as_path(),
618 width.unwrap_or(DEFAULT_TOUCH_DEVICE_WIDTH),
619 height.unwrap_or(DEFAULT_TOUCH_DEVICE_HEIGHT),
620 name.as_deref(),
621 single_touch_idx,
622 )?;
623 single_touch_idx += 1;
624 dev
625 }
626 InputDeviceOption::Switches { path } => {
627 let dev = create_switches_device(
628 cfg.protection_type,
629 cfg.jail_config.as_ref(),
630 path.as_path(),
631 switches_idx,
632 )?;
633 switches_idx += 1;
634 dev
635 }
636 InputDeviceOption::Trackpad {
637 path,
638 width,
639 height,
640 name,
641 } => {
642 let dev = create_trackpad_device(
643 cfg.protection_type,
644 cfg.jail_config.as_ref(),
645 path.as_path(),
646 width.unwrap_or(DEFAULT_TOUCH_DEVICE_WIDTH),
647 height.unwrap_or(DEFAULT_TOUCH_DEVICE_HEIGHT),
648 name.as_deref(),
649 trackpad_idx,
650 )?;
651 trackpad_idx += 1;
652 dev
653 }
654 InputDeviceOption::MultiTouchTrackpad {
655 path,
656 width,
657 height,
658 name,
659 } => {
660 let dev = create_multitouch_trackpad_device(
661 cfg.protection_type,
662 cfg.jail_config.as_ref(),
663 path.as_path(),
664 width.unwrap_or(DEFAULT_TOUCH_DEVICE_WIDTH),
665 height.unwrap_or(DEFAULT_TOUCH_DEVICE_HEIGHT),
666 name.as_deref(),
667 multi_touch_trackpad_idx,
668 )?;
669 multi_touch_trackpad_idx += 1;
670 dev
671 }
672 InputDeviceOption::Custom { path, config_path } => {
673 let dev = create_custom_device(
674 cfg.protection_type,
675 cfg.jail_config.as_ref(),
676 path.as_path(),
677 custom_idx,
678 config_path.clone(),
679 )?;
680 custom_idx += 1;
681 dev
682 }
683 };
684 devs.push(("input", input_dev));
685 }
686
687 #[cfg(feature = "balloon")]
688 if cfg.balloon {
689 let balloon_device_tube = if let Some(ref path) = cfg.balloon_control {
690 Tube::try_from(UnixSeqpacket::connect(path).with_context(|| {
691 format!(
692 "failed to connect to balloon control socket {}",
693 path.display(),
694 )
695 })?)?
696 } else {
697 let (host, device) = Tube::pair().context("failed to create tube")?;
700 add_control_tube(AnyControlTube::Balloon(host));
701 device
702 };
703
704 let balloon_features = (cfg.balloon_page_reporting as u64)
705 << BalloonFeatures::PageReporting as u64
706 | (cfg.balloon_ws_reporting as u64) << BalloonFeatures::WSReporting as u64;
707
708 let init_balloon_size = if let Some(init_memory) = cfg.init_memory {
709 let init_memory_bytes = init_memory.saturating_mul(1024 * 1024);
710 let total_memory_bytes = vm.get_memory().memory_size();
711
712 if init_memory_bytes > total_memory_bytes {
713 bail!(
714 "initial memory {} cannot be greater than total memory {}",
715 init_memory,
716 total_memory_bytes / (1024 * 1024),
717 );
718 }
719
720 total_memory_bytes - init_memory_bytes
722 } else {
723 0
725 };
726
727 let (dynamic_mapping_host_tube, dynamic_mapping_device_tube) =
730 Tube::pair().context("failed to create tube")?;
731 add_control_tube(AnyControlTube::VmMemoryTube {
732 tube: dynamic_mapping_host_tube,
733 expose_with_viommu: false,
734 remote_peer: cfg.jail_config.is_some(),
735 });
736
737 devs.push((
738 "balloon",
739 create_balloon_device(
740 cfg.protection_type,
741 cfg.jail_config.as_ref(),
742 balloon_device_tube,
743 balloon_inflate_tube,
744 init_balloon_size,
745 VmMemoryClient::new(dynamic_mapping_device_tube),
746 balloon_features,
747 #[cfg(feature = "registered_events")]
748 Some(
749 registered_evt_q
750 .try_clone()
751 .context("failed to clone registered_evt_q tube")?,
752 ),
753 cfg.balloon_ws_num_bins,
754 )?,
755 ));
756 }
757
758 #[cfg(feature = "net")]
759 for opt in &cfg.net {
760 let dev =
761 opt.create_virtio_device_and_jail(cfg.protection_type, cfg.jail_config.as_ref())?;
762 devs.push(("net", dev));
763 }
764
765 #[cfg(feature = "audio")]
766 {
767 for (card_index, virtio_snd) in cfg.virtio_snds.iter().enumerate() {
768 let (snd_host_tube, snd_device_tube) =
769 Tube::pair().context("failed to create tube for snd")?;
770 add_control_tube(AnyControlTube::Snd(snd_host_tube));
771 let mut snd_params = virtio_snd.clone();
772 snd_params.card_index = card_index;
773 devs.push((
774 "snd",
775 create_virtio_snd_device(
776 cfg.protection_type,
777 cfg.jail_config.as_ref(),
778 snd_params,
779 snd_device_tube,
780 )?,
781 ));
782 }
783 }
784
785 #[cfg(any(target_os = "android", target_os = "linux"))]
786 #[cfg(feature = "media")]
787 {
788 for v4l2_device in &cfg.v4l2_proxy {
789 devs.push((
790 "v4l2",
791 create_v4l2_device(cfg.protection_type, v4l2_device)?,
792 ));
793 }
794 }
795
796 #[cfg(feature = "media")]
797 if cfg.simple_media_device {
798 devs.push(("media", create_simple_media_device(cfg.protection_type)?));
799 }
800
801 #[cfg(all(feature = "media", feature = "video-decoder"))]
802 {
803 for (tube, backend) in media_adapter_cfg {
804 devs.push((
805 "media_adapter",
806 create_virtio_media_adapter(
807 cfg.protection_type,
808 cfg.jail_config.as_ref(),
809 tube,
810 backend,
811 )?,
812 ));
813 }
814 }
815
816 #[cfg(feature = "video-decoder")]
817 {
818 for (tube, backend) in video_dec_cfg {
819 register_video_device(
820 backend,
821 &mut devs,
822 tube,
823 cfg.protection_type,
824 cfg.jail_config.as_ref(),
825 VideoDeviceType::Decoder,
826 )?;
827 }
828 }
829
830 #[cfg(feature = "video-encoder")]
831 {
832 for (tube, backend) in video_enc_cfg {
833 register_video_device(
834 backend,
835 &mut devs,
836 tube,
837 cfg.protection_type,
838 cfg.jail_config.as_ref(),
839 VideoDeviceType::Encoder,
840 )?;
841 }
842 }
843
844 if let Some(vsock_config) = &cfg.vsock {
845 devs.push((
846 "vsock",
847 vsock_config
848 .create_virtio_device_and_jail(cfg.protection_type, cfg.jail_config.as_ref())?,
849 ));
850 }
851
852 #[cfg(target_arch = "aarch64")]
853 {
854 if cfg.vhost_scmi {
855 devs.push((
856 "vhost_scmi",
857 create_vhost_scmi_device(
858 cfg.protection_type,
859 cfg.jail_config.as_ref(),
860 cfg.vhost_scmi_device.clone(),
861 )?,
862 ));
863 }
864 }
865
866 for shared_dir in &cfg.shared_dirs {
867 let SharedDir {
868 src,
869 tag,
870 kind,
871 ugid,
872 uid_map,
873 gid_map,
874 fs_cfg,
875 p9_cfg,
876 } = shared_dir;
877
878 match kind {
879 SharedDirKind::FS => {
880 let (host_tube, device_tube) = Tube::pair().context("failed to create tube")?;
881 add_control_tube(AnyControlTube::Fs(host_tube));
882
883 devs.push((
884 "fs",
885 create_fs_device(
886 cfg.protection_type,
887 cfg.jail_config.as_ref(),
888 *ugid,
889 uid_map,
890 gid_map,
891 src,
892 tag,
893 fs_cfg.clone(),
894 device_tube,
895 )?,
896 ));
897 }
898 SharedDirKind::P9 => devs.push((
899 "9p",
900 create_9p_device(
901 cfg.protection_type,
902 cfg.jail_config.as_ref(),
903 *ugid,
904 uid_map,
905 gid_map,
906 src,
907 tag,
908 p9_cfg.clone(),
909 )?,
910 )),
911 };
912 }
913
914 #[cfg(feature = "audio")]
915 if let Some(path) = &cfg.sound {
916 devs.push((
917 "sound",
918 create_sound_device(path, cfg.protection_type, cfg.jail_config.as_ref())?,
919 ));
920 }
921
922 for virtio_device_module in &cfg.virtio_device_modules {
923 let mut args = VirtioDeviceArgs {
924 vm: vm as &dyn Vm,
925 resources,
926 add_control_tube,
927 protection_type: cfg.protection_type,
928 };
929 let dev = virtio_device_module
930 .create(&mut args)
931 .context("failed to create virtio device")?;
932 let jail = if let Some(jail_config) = cfg.jail_config.as_ref() {
933 virtio_device_module
934 .create_jail(jail_config)
935 .context("failed to create jail")?
936 } else {
937 None
938 };
939 devs.push((
940 virtio_device_module.sort_name(),
941 VirtioDeviceStub { dev, jail },
942 ));
943 }
944
945 for opt in &cfg.vhost_user {
946 devs.push((
947 "vhost_user",
948 create_vhost_user_frontend(
949 cfg.protection_type,
950 opt,
951 cfg.vhost_user_connect_timeout_ms,
952 vm_evt_wrtube.try_clone()?,
953 )?,
954 ));
955 }
956
957 let device_order = [
964 "wayland",
965 "multi_touch",
966 "window_keyboard",
967 "gpu",
968 "console",
969 "disk",
970 "scsi",
971 "pmem",
972 "pmem_ext2",
973 "rng",
974 "pvclock",
975 "vtpm",
976 "input",
977 "balloon",
978 "net",
979 "snd",
980 "v4l2",
981 "media",
982 "media_adapter",
983 "video",
984 "vsock",
985 "vhost_scmi",
986 "fs",
987 "9p",
988 "sound",
989 "vhost_user",
990 ];
991 devs.sort_by_key(|(name, _)| device_order.iter().position(|s| s == name).unwrap_or(9999));
992
993 Ok(devs.into_iter().map(|(_, dev)| dev).collect())
994}
995
996fn create_devices(
997 cfg: &Config,
998 vm: &dyn VmArch,
999 resources: &mut SystemAllocator,
1000 add_control_tube: &mut impl FnMut(AnyControlTube),
1001 vm_evt_wrtube: &SendTube,
1002 iommu_attached_endpoints: &mut BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>>,
1003 #[cfg(feature = "usb")] usb_provider: DeviceProvider,
1004 #[cfg(feature = "gpu")] render_server_fd: Option<SafeDescriptor>,
1005 iova_max_addr: &mut Option<u64>,
1006 #[cfg(feature = "registered_events")] registered_evt_q: &SendTube,
1007 vfio_container_manager: &mut VfioContainerManager,
1008 worker_process_pids: &mut BTreeSet<Pid>,
1010) -> DeviceResult<Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>> {
1011 let mut devices: Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)> = Vec::new();
1012 #[cfg(feature = "balloon")]
1013 let mut balloon_inflate_tube: Option<Tube> = None;
1014 #[cfg(feature = "gpu")]
1015 let mut has_vfio_gfx_device = false;
1016 if !cfg.vfio.is_empty() {
1017 let mut coiommu_attached_endpoints = Vec::new();
1018
1019 for vfio_dev in &cfg.vfio {
1020 let (dev, jail, viommu_mapper) = create_vfio_device(
1021 cfg.jail_config.as_ref(),
1022 vm,
1023 resources,
1024 add_control_tube,
1025 &vfio_dev.path,
1026 false,
1027 None,
1028 vfio_dev.guest_address,
1029 Some(&mut coiommu_attached_endpoints),
1030 vfio_dev.iommu,
1031 vfio_dev.dt_symbol.clone(),
1032 vfio_container_manager,
1033 )?;
1034 match dev {
1035 VfioDeviceVariant::Pci(vfio_pci_device) => {
1036 *iova_max_addr = Some(max(
1037 vfio_pci_device.get_max_iova(),
1038 iova_max_addr.unwrap_or(0),
1039 ));
1040
1041 #[cfg(feature = "gpu")]
1042 if vfio_pci_device.is_gfx() {
1043 has_vfio_gfx_device = true;
1044 }
1045
1046 if let Some(viommu_mapper) = viommu_mapper {
1047 iommu_attached_endpoints.insert(
1048 vfio_pci_device
1049 .pci_address()
1050 .context("not initialized")?
1051 .to_u32(),
1052 Arc::new(Mutex::new(Box::new(viommu_mapper))),
1053 );
1054 }
1055
1056 devices.push((Box::new(vfio_pci_device), jail));
1057 }
1058 VfioDeviceVariant::Platform(vfio_plat_dev) => {
1059 devices.push((Box::new(vfio_plat_dev), jail));
1060 }
1061 }
1062 }
1063
1064 if !coiommu_attached_endpoints.is_empty() || !iommu_attached_endpoints.is_empty() {
1065 let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
1066 let res = unsafe { libc::getrlimit64(libc::RLIMIT_MEMLOCK, buf.as_mut_ptr()) };
1068 if res == 0 {
1069 let limit = unsafe { buf.assume_init() };
1071 let rlim_new = limit.rlim_cur.saturating_add(vm.get_memory().memory_size());
1072 let rlim_max = max(limit.rlim_max, rlim_new);
1073 if limit.rlim_cur < rlim_new {
1074 let limit_arg = libc::rlimit64 {
1075 rlim_cur: rlim_new,
1076 rlim_max,
1077 };
1078 let res = unsafe { libc::setrlimit64(libc::RLIMIT_MEMLOCK, &limit_arg) };
1080 if res != 0 {
1081 bail!("Set rlimit failed");
1082 }
1083 }
1084 } else {
1085 bail!("Get rlimit failed");
1086 }
1087 }
1088 #[cfg(feature = "balloon")]
1089 let coiommu_tube: Option<Tube>;
1090 #[cfg(not(feature = "balloon"))]
1091 let coiommu_tube: Option<Tube> = None;
1092 if !coiommu_attached_endpoints.is_empty() {
1093 let vfio_container = vfio_container_manager
1094 .get_container(IommuDevType::CoIommu, None as Option<&Path>)
1095 .context("failed to get vfio container")?;
1096 let (coiommu_host_tube, coiommu_device_tube) =
1097 Tube::pair().context("failed to create coiommu tube")?;
1098 add_control_tube(AnyControlTube::VmMemoryTube {
1099 tube: coiommu_host_tube,
1100 expose_with_viommu: false,
1101 remote_peer: cfg.jail_config.is_some(),
1102 });
1103 let vcpu_count = cfg.vcpu_count.unwrap_or(1) as u64;
1104 #[cfg(feature = "balloon")]
1105 match Tube::pair() {
1106 Ok((x, y)) => {
1107 coiommu_tube = Some(x);
1108 balloon_inflate_tube = Some(y);
1109 }
1110 Err(x) => return Err(x).context("failed to create coiommu tube"),
1111 }
1112 let dev = CoIommuDev::new(
1113 vm.get_memory().clone(),
1114 vfio_container,
1115 VmMemoryClient::new(coiommu_device_tube),
1116 coiommu_tube,
1117 coiommu_attached_endpoints,
1118 vcpu_count,
1119 cfg.coiommu_param.unwrap_or_default(),
1120 )
1121 .context("failed to create coiommu device")?;
1122
1123 devices.push((
1124 Box::new(dev),
1125 simple_jail(cfg.jail_config.as_ref(), "coiommu_device")?,
1126 ));
1127 }
1128 }
1129
1130 let stubs = create_virtio_devices(
1131 cfg,
1132 vm,
1133 resources,
1134 add_control_tube,
1135 vm_evt_wrtube,
1136 #[cfg(feature = "balloon")]
1137 balloon_inflate_tube,
1138 worker_process_pids,
1139 #[cfg(feature = "gpu")]
1140 render_server_fd,
1141 #[cfg(feature = "gpu")]
1142 has_vfio_gfx_device,
1143 #[cfg(feature = "registered_events")]
1144 registered_evt_q,
1145 )?;
1146
1147 for stub in stubs {
1148 let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
1149 add_control_tube(AnyControlTube::IrqTube(msi_host_tube));
1150
1151 let shared_memory_tube = if stub.dev.get_shared_memory_region().is_some() {
1152 let (host_tube, device_tube) =
1153 Tube::pair().context("failed to create shared memory tube")?;
1154 add_control_tube(AnyControlTube::VmMemoryTube {
1155 tube: host_tube,
1156 expose_with_viommu: stub.dev.expose_shmem_descriptors_with_viommu(),
1157 remote_peer: stub.jail.is_some(),
1158 });
1159 Some(device_tube)
1160 } else {
1161 None
1162 };
1163
1164 let (ioevent_host_tube, ioevent_device_tube) =
1165 Tube::pair().context("failed to create ioevent tube")?;
1166 add_control_tube(AnyControlTube::VmMemoryTube {
1167 tube: ioevent_host_tube,
1168 expose_with_viommu: false,
1169 remote_peer: stub.jail.is_some(),
1170 });
1171
1172 let (host_tube, device_tube) =
1173 Tube::pair().context("failed to create device control tube")?;
1174 add_control_tube(AnyControlTube::Vm(host_tube));
1175
1176 let dev = VirtioPciDevice::new(
1177 vm.get_memory().clone(),
1178 stub.dev,
1179 msi_device_tube,
1180 cfg.disable_virtio_intx,
1181 shared_memory_tube.map(VmMemoryClient::new),
1182 VmMemoryClient::new(ioevent_device_tube),
1183 device_tube,
1184 )
1185 .context("failed to create virtio pci dev")?;
1186
1187 devices.push((Box::new(dev) as Box<dyn BusDeviceObj>, stub.jail));
1188 }
1189
1190 #[cfg(feature = "usb")]
1191 if cfg.usb {
1192 let usb_controller = Box::new(XhciController::new(
1194 vm.get_memory().clone(),
1195 Box::new(usb_provider),
1196 ));
1197 devices.push((
1198 usb_controller,
1199 simple_jail(cfg.jail_config.as_ref(), "xhci_device")?,
1200 ));
1201 }
1202
1203 for params in &cfg.stub_pci_devices {
1204 devices.push((Box::new(StubPciDevice::new(params)), None));
1206 }
1207
1208 devices.push((
1209 Box::new(PvPanicPciDevice::new(vm_evt_wrtube.try_clone()?)),
1210 None,
1211 ));
1212
1213 Ok(devices)
1214}
1215
1216fn create_mmio_file_backed_mappings(
1217 cfg: &Config,
1218 vm: &dyn Vm,
1219 resources: &mut SystemAllocator,
1220) -> Result<()> {
1221 for mapping in &cfg.file_backed_mappings_mmio {
1222 let file = mapping
1223 .open()
1224 .context("failed to open file for file-backed mapping")?;
1225 let prot = if mapping.writable {
1226 Protection::read_write()
1227 } else {
1228 Protection::read()
1229 };
1230 let size = mapping
1231 .size
1232 .try_into()
1233 .context("Invalid size for file-backed mapping")?;
1234 let memory_mapping = MemoryMappingBuilder::new(size)
1235 .from_file(&file)
1236 .offset(mapping.offset)
1237 .protection(prot)
1238 .build()
1239 .context("failed to map backing file for file-backed mapping")?;
1240
1241 let mapping_range = AddressRange::from_start_and_size(mapping.address, mapping.size)
1242 .context("failed to convert to AddressRange")?;
1243 match resources.mmio_allocator_any().allocate_at(
1244 mapping_range,
1245 Alloc::FileBacked(mapping.address),
1246 "file-backed mapping".to_owned(),
1247 ) {
1248 Ok(()) | Err(resources::Error::OutOfSpace) => {}
1253 e => e.context("failed to allocate guest address for file-backed mapping")?,
1254 }
1255
1256 vm.add_memory_region(
1257 GuestAddress(mapping.address),
1258 Box::new(memory_mapping),
1259 !mapping.writable,
1260 false,
1261 MemCacheType::CacheCoherent,
1262 )
1263 .context("failed to configure file-backed mapping")?;
1264 }
1265
1266 Ok(())
1267}
1268
1269#[cfg(target_arch = "x86_64")]
1270struct HotPlugStub {
1272 hotplug_buses: BTreeMap<u8, Arc<Mutex<dyn HotPlugBus>>>,
1274 iommu_bus_ranges: Vec<RangeInclusive<u32>>,
1276 pme_notify_devs: BTreeMap<u8, Arc<Mutex<dyn PmeNotify>>>,
1278}
1279
1280#[cfg(target_arch = "x86_64")]
1281impl HotPlugStub {
1282 fn new() -> Self {
1284 Self {
1285 hotplug_buses: BTreeMap::new(),
1286 iommu_bus_ranges: Vec::new(),
1287 pme_notify_devs: BTreeMap::new(),
1288 }
1289 }
1290}
1291
1292#[cfg(target_arch = "x86_64")]
1293fn create_pure_virtual_pcie_root_port(
1298 sys_allocator: &mut SystemAllocator,
1299 add_control_tube: &mut impl FnMut(AnyControlTube),
1300 devices: &mut Vec<(Box<dyn BusDeviceObj>, Option<Minijail>)>,
1301 hp_bus_count: u8,
1302) -> Result<HotPlugStub> {
1303 let mut hp_sec_buses = Vec::new();
1304 let mut hp_stub = HotPlugStub::new();
1305 for i in 1..255 {
1308 if sys_allocator.pci_bus_empty(i) {
1309 if hp_sec_buses.len() < hp_bus_count.into() {
1310 hp_sec_buses.push(i);
1311 }
1312 continue;
1313 }
1314 let pcie_root_port = Arc::new(Mutex::new(PcieRootPort::new(i, false)));
1315 hp_stub
1316 .pme_notify_devs
1317 .insert(i, pcie_root_port.clone() as Arc<Mutex<dyn PmeNotify>>);
1318 let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
1319 add_control_tube(AnyControlTube::IrqTube(msi_host_tube));
1320 let pci_bridge = Box::new(PciBridge::new(pcie_root_port.clone(), msi_device_tube));
1321 devices.push((pci_bridge, None));
1323 }
1324
1325 if hp_sec_buses.len() < hp_bus_count.into() {
1327 return Err(anyhow!("no more addresses are available"));
1328 }
1329
1330 for hp_sec_bus in hp_sec_buses {
1331 let pcie_root_port = Arc::new(Mutex::new(PcieRootPort::new(hp_sec_bus, true)));
1332 hp_stub.pme_notify_devs.insert(
1333 hp_sec_bus,
1334 pcie_root_port.clone() as Arc<Mutex<dyn PmeNotify>>,
1335 );
1336 let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
1337 add_control_tube(AnyControlTube::IrqTube(msi_host_tube));
1338 let pci_bridge = Box::new(PciBridge::new(pcie_root_port.clone(), msi_device_tube));
1339
1340 hp_stub.iommu_bus_ranges.push(RangeInclusive::new(
1341 PciAddress {
1342 bus: pci_bridge.get_secondary_num(),
1343 dev: 0,
1344 func: 0,
1345 }
1346 .to_u32(),
1347 PciAddress {
1348 bus: pci_bridge.get_subordinate_num(),
1349 dev: 32,
1350 func: 8,
1351 }
1352 .to_u32(),
1353 ));
1354
1355 devices.push((pci_bridge, None));
1356 hp_stub
1357 .hotplug_buses
1358 .insert(hp_sec_bus, pcie_root_port as Arc<Mutex<dyn HotPlugBus>>);
1359 }
1360 Ok(hp_stub)
1361}
1362
1363fn get_representative_pcpu(vcpu_id: usize, vcpu_affinity: &Option<VcpuAffinity>) -> usize {
1368 match vcpu_affinity {
1369 Some(VcpuAffinity::Global(s)) => s.iter().next().copied().unwrap_or(0),
1371 Some(VcpuAffinity::PerVcpu(m)) => match m.get(&vcpu_id) {
1372 Some(s) => s.iter().next().copied().unwrap_or(vcpu_id),
1373 None => vcpu_id,
1374 },
1375 None => vcpu_id,
1376 }
1377}
1378
1379fn map_vcpu_capacity(
1382 vcpu_count: usize,
1383 vcpu_affinity: &Option<VcpuAffinity>,
1384 host_capacity: &BTreeMap<usize, u32>,
1385) -> anyhow::Result<BTreeMap<usize, u32>> {
1386 let mut mapped_capacity = BTreeMap::new();
1387 for vcpu_id in 0..vcpu_count {
1388 let pcpu_id = get_representative_pcpu(vcpu_id, vcpu_affinity);
1389 let capacity = host_capacity
1390 .get(&pcpu_id)
1391 .copied()
1392 .unwrap_or(DEFAULT_CPU_CAPACITY);
1393 mapped_capacity.insert(vcpu_id, capacity);
1394 }
1395 Ok(mapped_capacity)
1396}
1397
1398fn map_vcpu_clusters(
1401 vcpu_count: usize,
1402 vcpu_affinity: &Option<VcpuAffinity>,
1403 host_clusters: Vec<arch::CpuSet>,
1404) -> anyhow::Result<Vec<arch::CpuSet>> {
1405 let mut pcpu_to_cluster = std::collections::BTreeMap::new();
1406 for (cluster_idx, cluster) in host_clusters.iter().enumerate() {
1407 for pcpu_id in cluster.iter() {
1408 pcpu_to_cluster.insert(*pcpu_id, cluster_idx);
1409 }
1410 }
1411
1412 let mut vcpu_clusters_sets: Vec<std::collections::BTreeSet<usize>> =
1413 vec![std::collections::BTreeSet::new(); host_clusters.len()];
1414
1415 for vcpu_id in 0..vcpu_count {
1416 let pcpu_id = get_representative_pcpu(vcpu_id, vcpu_affinity);
1417
1418 if let Some(&cluster_idx) = pcpu_to_cluster.get(&pcpu_id) {
1419 vcpu_clusters_sets[cluster_idx].insert(vcpu_id);
1420 }
1421 }
1422
1423 Ok(vcpu_clusters_sets
1424 .into_iter()
1425 .filter(|s| !s.is_empty())
1426 .map(arch::CpuSet::new)
1427 .collect())
1428}
1429
1430fn setup_vm_components(cfg: &Config) -> Result<VmComponents> {
1431 let initrd_image = if let Some(initrd_path) = &cfg.initrd_path {
1432 Some(
1433 open_file_or_duplicate(initrd_path, OpenOptions::new().read(true))
1434 .with_context(|| format!("failed to open initrd {}", initrd_path.display()))?,
1435 )
1436 } else {
1437 None
1438 };
1439 let pvm_fw_image = if let Some(pvm_fw_path) = &cfg.pvm_fw {
1440 Some(
1441 open_file_or_duplicate(pvm_fw_path, OpenOptions::new().read(true))
1442 .with_context(|| format!("failed to open pvm_fw {}", pvm_fw_path.display()))?,
1443 )
1444 } else {
1445 None
1446 };
1447
1448 let vm_image = match cfg.executable_path {
1449 Some(Executable::Kernel(ref kernel_path)) => VmImage::Kernel(
1450 open_file_or_duplicate(kernel_path, OpenOptions::new().read(true)).with_context(
1451 || format!("failed to open kernel image {}", kernel_path.display()),
1452 )?,
1453 ),
1454 Some(Executable::Bios(ref bios_path)) => VmImage::Bios(
1455 open_file_or_duplicate(bios_path, OpenOptions::new().read(true))
1456 .with_context(|| format!("failed to open bios {}", bios_path.display()))?,
1457 ),
1458 _ => panic!("Did not receive a bios or kernel, should be impossible."),
1459 };
1460
1461 let swiotlb = if let Some(size) = cfg.swiotlb {
1462 Some(
1463 size.checked_mul(1024 * 1024)
1464 .ok_or_else(|| anyhow!("requested swiotlb size too large"))?,
1465 )
1466 } else if matches!(cfg.protection_type, ProtectionType::Unprotected) {
1467 None
1468 } else {
1469 Some(64 * 1024 * 1024)
1470 };
1471
1472 let (pflash_image, pflash_block_size) = if let Some(pflash_parameters) = &cfg.pflash_parameters
1473 {
1474 (
1475 Some(
1476 open_file_or_duplicate(
1477 &pflash_parameters.path,
1478 OpenOptions::new().read(true).write(true),
1479 )
1480 .with_context(|| {
1481 format!("failed to open pflash {}", pflash_parameters.path.display())
1482 })?,
1483 ),
1484 pflash_parameters.block_size,
1485 )
1486 } else {
1487 (None, 0)
1488 };
1489
1490 #[allow(unused_mut)]
1492 let mut vcpu_frequencies: BTreeMap<usize, Vec<u32>> = BTreeMap::new();
1493 #[cfg(target_arch = "aarch64")]
1494 let mut normalized_cpu_ipc_ratios = BTreeMap::new();
1495
1496 let fw_cfg_enable = cfg.enable_fw_cfg || !cfg.fw_cfg_parameters.is_empty();
1498 let (vcpu_clusters, vcpu_capacity) = if cfg.host_cpu_topology {
1499 let host_capacity = Arch::get_host_cpu_capacity()?;
1500 let mapped_capacity = map_vcpu_capacity(
1501 cfg.vcpu_count.unwrap_or(1),
1502 &cfg.vcpu_affinity,
1503 &host_capacity,
1504 )?;
1505
1506 let host_clusters = Arch::get_host_cpu_clusters()?;
1507 let mapped_clusters = map_vcpu_clusters(
1508 cfg.vcpu_count.unwrap_or(1),
1509 &cfg.vcpu_affinity,
1510 host_clusters,
1511 )?;
1512
1513 (mapped_clusters, mapped_capacity)
1514 } else {
1515 (cfg.cpu_clusters.clone(), cfg.cpu_capacity.clone())
1516 };
1517
1518 #[cfg(target_arch = "aarch64")]
1519 let cpu_ipc_ratio = if cfg.host_cpu_topology {
1520 &vcpu_capacity
1521 } else {
1522 &cfg.cpu_ipc_ratio
1523 };
1524
1525 #[cfg(target_arch = "aarch64")]
1526 let mut vcpu_domain_paths = BTreeMap::new();
1527 #[cfg(target_arch = "aarch64")]
1528 let mut vcpu_domains = BTreeMap::new();
1529
1530 #[cfg(target_arch = "aarch64")]
1531 if cfg.virt_cpufreq || cfg.virt_cpufreq_v2 {
1532 if !cfg.cpu_frequencies_khz.is_empty() {
1533 vcpu_frequencies = cfg.cpu_frequencies_khz.clone();
1534 } else {
1535 match Arch::get_host_cpu_frequencies_khz() {
1536 Ok(host_cpu_frequencies) => {
1537 for vcpu_id in 0..cfg.vcpu_count.unwrap_or(1) {
1538 let vcpu_affinity = match cfg.vcpu_affinity.clone() {
1539 Some(VcpuAffinity::Global(v)) => v,
1540 Some(VcpuAffinity::PerVcpu(mut m)) => {
1541 m.remove(&vcpu_id).unwrap_or_default()
1542 }
1543 None => {
1544 panic!("There must be some vcpu_affinity setting with VirtCpufreq enabled!")
1545 }
1546 };
1547
1548 if let Some(freq_domain) = host_cpu_frequencies.get(&vcpu_affinity[0]) {
1551 for cpu in vcpu_affinity.iter() {
1552 if let Some(frequencies) = host_cpu_frequencies.get(cpu) {
1553 if frequencies != freq_domain {
1554 panic!("Affined CPUs do not share a frequency domain!");
1555 }
1556 }
1557 }
1558 vcpu_frequencies.insert(vcpu_id, freq_domain.clone());
1559 } else {
1560 panic!("No frequency domain for vcpu:{vcpu_id}");
1561 }
1562 }
1563 }
1564 Err(e) => {
1565 warn!("Unable to get host cpu frequencies {:#}", e);
1566 }
1567 }
1568 }
1569
1570 if !vcpu_frequencies.is_empty() {
1571 let host_max_freqs = Arch::get_host_cpu_max_freq_khz()?;
1572 let host_max_freq = host_max_freqs.values().copied().max().unwrap_or_default();
1575
1576 normalized_cpu_ipc_ratios = normalize_cpu_ipc_ratios(
1577 vcpu_frequencies.iter().map(|(vcpu_id, frequencies)| {
1578 (
1579 *vcpu_id,
1580 frequencies.iter().copied().max().unwrap_or_default(),
1581 )
1582 }),
1583 host_max_freq,
1584 |vcpu_id| {
1585 cpu_ipc_ratio
1586 .get(&vcpu_id)
1587 .copied()
1588 .unwrap_or(DEFAULT_CPU_CAPACITY)
1589 },
1590 )?;
1591
1592 if !cfg.cpu_freq_domains.is_empty() {
1593 let cgroup_path = cfg
1594 .vcpu_cgroup_path
1595 .clone()
1596 .context("cpu_freq_domains requires vcpu_cgroup_path")?;
1597
1598 if !cgroup_path.join("cgroup.controllers").exists() {
1599 panic!("CGroupsV2 must be enabled for cpu freq domain support!");
1600 }
1601
1602 let cgroup_procs_path = cgroup_path.join("cgroup.procs");
1604 std::fs::write(
1605 cgroup_procs_path.clone(),
1606 process::id().to_string().as_bytes(),
1607 )
1608 .with_context(|| {
1609 format!(
1610 "failed to create vcpu-cgroup-path {}",
1611 cgroup_procs_path.display(),
1612 )
1613 })?;
1614
1615 for (freq_domain_idx, cpus) in cfg.cpu_freq_domains.iter().enumerate() {
1616 let vcpu_domain_path =
1617 cgroup_path.join(format!("vcpu-domain{freq_domain_idx}"));
1618 create_dir_all(&vcpu_domain_path)?;
1620
1621 let cgroup_type_path = cgroup_path.join(vcpu_domain_path.join("cgroup.type"));
1624 std::fs::write(cgroup_type_path.clone(), b"threaded").with_context(|| {
1625 format!(
1626 "failed to create vcpu-cgroup-path {}",
1627 cgroup_type_path.display(),
1628 )
1629 })?;
1630 for core_idx in cpus.iter() {
1631 vcpu_domain_paths.insert(*core_idx, vcpu_domain_path.clone());
1632 vcpu_domains.insert(*core_idx, freq_domain_idx as u32);
1633 }
1634 }
1635 }
1636 }
1637 }
1638
1639 let vcpu_count = cfg.vcpu_count.unwrap_or(1);
1640 let vcpu_properties = arch::derive_vcpu_properties(
1641 vcpu_count,
1642 &vcpu_capacity,
1643 &cfg.dynamic_power_coefficient,
1644 &vcpu_frequencies,
1645 #[cfg(all(
1646 target_arch = "aarch64",
1647 any(target_os = "android", target_os = "linux")
1648 ))]
1649 &normalized_cpu_ipc_ratios,
1650 #[cfg(all(
1651 target_arch = "aarch64",
1652 any(target_os = "android", target_os = "linux")
1653 ))]
1654 &vcpu_domains,
1655 #[cfg(all(
1656 target_arch = "aarch64",
1657 any(target_os = "android", target_os = "linux")
1658 ))]
1659 &vcpu_domain_paths,
1660 );
1661
1662 Ok(VmComponents {
1663 #[cfg(target_arch = "x86_64")]
1664 break_linux_pci_config_io: cfg.break_linux_pci_config_io,
1665 memory_size: cfg
1666 .memory
1667 .unwrap_or(256)
1668 .checked_mul(1024 * 1024)
1669 .ok_or_else(|| anyhow!("requested memory size too large"))?,
1670 swiotlb,
1671 fw_cfg_enable,
1672 bootorder_fw_cfg_blob: Vec::new(),
1673 vcpu_properties,
1674 vcpu_affinity: cfg.vcpu_affinity.clone(),
1675 fw_cfg_parameters: cfg.fw_cfg_parameters.clone(),
1676 vcpu_clusters,
1677 dev_pm: cfg.dev_pm,
1678 no_smt: cfg.no_smt,
1679 hugepages: cfg.hugepages,
1680 hv_cfg: hypervisor::Config {
1681 #[cfg(target_arch = "aarch64")]
1682 mte: cfg.mte,
1683 protection_type: cfg.protection_type,
1684 #[cfg(all(target_os = "android", target_arch = "aarch64"))]
1685 ffa: cfg.ffa.map(|g| g.auto).unwrap_or(false),
1686 force_disable_readonly_mem: cfg.force_disable_readonly_mem,
1687 },
1688 vm_image,
1689 android_fstab: cfg
1690 .android_fstab
1691 .as_ref()
1692 .map(|x| {
1693 File::open(x)
1694 .with_context(|| format!("failed to open android fstab file {}", x.display()))
1695 })
1696 .map_or(Ok(None), |v| v.map(Some))?,
1697 pstore: cfg.pstore.clone(),
1698 pflash_block_size,
1699 pflash_image,
1700 initrd_image,
1701 extra_kernel_params: cfg.params.clone(),
1702 acpi_sdts: cfg
1703 .acpi_tables
1704 .iter()
1705 .map(|path| {
1706 SDT::from_file(path)
1707 .with_context(|| format!("failed to open ACPI file {}", path.display()))
1708 })
1709 .collect::<Result<Vec<SDT>>>()?,
1710 rt_cpus: cfg.rt_cpus.clone(),
1711 delay_rt: cfg.delay_rt,
1712 no_i8042: cfg.no_i8042,
1713 no_rtc: cfg.no_rtc,
1714 #[cfg(target_arch = "x86_64")]
1715 smbios: cfg.smbios.clone(),
1716 host_cpu_topology: cfg.host_cpu_topology,
1717 itmt: cfg.itmt,
1718 #[cfg(target_arch = "x86_64")]
1719 force_s2idle: cfg.force_s2idle,
1720 pvm_fw: pvm_fw_image,
1721 pci_config: cfg.pci_config,
1722 boot_cpu: cfg.boot_cpu,
1723 vfio_platform_pm: cfg.vfio_platform_pm,
1724 #[cfg(target_arch = "aarch64")]
1725 virt_cpufreq_v2: cfg.virt_cpufreq_v2,
1726 smccc_trng: cfg.smccc_trng,
1727 #[cfg(target_arch = "aarch64")]
1728 sve_config: cfg.sve.unwrap_or_default(),
1729 #[cfg(target_arch = "aarch64")]
1730 nested: cfg.nested.mode,
1731 })
1732}
1733
1734#[cfg(target_arch = "aarch64")]
1735fn normalize_cpu_ipc_ratios(
1736 max_frequency_per_cpu: impl Iterator<Item = (usize, u32)>,
1737 host_max_freq: u32,
1738 cpu_ipc_ratio: impl Fn(usize) -> u32,
1739) -> Result<BTreeMap<usize, u32>> {
1740 if host_max_freq == 0 {
1741 return Err(anyhow!("invalid host_max_freq 0"));
1742 }
1743
1744 let host_max_freq = u64::from(host_max_freq);
1745 let mut normalized_cpu_ipc_ratios = BTreeMap::new();
1746 for (cpu_id, max_freq) in max_frequency_per_cpu {
1747 let ipc_ratio = u64::from(cpu_ipc_ratio(cpu_id));
1748 let max_freq = u64::from(max_freq);
1749
1750 let normalized_cpu_ipc_ratio = (ipc_ratio * max_freq) / host_max_freq;
1751
1752 normalized_cpu_ipc_ratios.insert(
1753 cpu_id,
1754 u32::try_from(normalized_cpu_ipc_ratio)
1755 .context("normalized CPU IPC ratio out of u32 range")?,
1756 );
1757 }
1758
1759 Ok(normalized_cpu_ipc_ratios)
1760}
1761
1762#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1763pub enum ExitState {
1764 Reset,
1765 Stop,
1766 Crash,
1767 GuestPanic,
1768 WatchdogReset,
1769}
1770
1771fn punch_holes_in_guest_mem_layout_for_mappings(
1774 guest_mem_layout: Vec<(GuestAddress, u64, MemoryRegionOptions)>,
1775 file_backed_mappings_ram: &[FileBackedMappingParameters],
1776) -> Result<Vec<(GuestAddress, u64, MemoryRegionOptions)>> {
1777 let mut layout_set = BTreeSet::new();
1780 for (addr, size, options) in &guest_mem_layout {
1781 layout_set.insert((addr.offset(), addr.offset() + size, options.clone()));
1782 }
1783
1784 for mapping in file_backed_mappings_ram {
1788 anyhow::ensure!(
1789 layout_set
1790 .iter()
1791 .any(|(addr, size, _)| *addr <= mapping.address
1792 && mapping.address + mapping.size <= *addr + *size),
1793 "RAM file-backed-mapping must be a subset of a RAM region"
1794 );
1795 }
1796
1797 for mapping in file_backed_mappings_ram.iter().cloned() {
1798 let mapping_start = mapping.address;
1799 let mapping_end = mapping_start + mapping.size;
1800 let mut purpose = None;
1801 while let Some((range_start, range_end, options)) = layout_set
1803 .iter()
1804 .find(|&&(range_start, range_end, _)| {
1805 mapping_start < range_end && mapping_end > range_start
1806 })
1807 .cloned()
1808 {
1809 let purpose = *purpose.get_or_insert(options.purpose);
1810 anyhow::ensure!(
1811 options.purpose == purpose,
1812 "RAM file-backed-mapping cannot span regions with different purposes: {:?} vs {:?}",
1813 options.purpose,
1814 purpose
1815 );
1816
1817 layout_set.remove(&(range_start, range_end, options.clone()));
1818
1819 if range_start < mapping_start {
1820 layout_set.insert((range_start, mapping_start, options.clone()));
1821 }
1822 if range_end > mapping_end {
1823 layout_set.insert((mapping_end, range_end, options));
1824 }
1825 }
1826 layout_set.insert((
1827 mapping_start,
1828 mapping_end,
1829 MemoryRegionOptions::new()
1830 .purpose(purpose.unwrap())
1831 .file_backed(mapping),
1832 ));
1833 }
1834
1835 Ok(layout_set
1837 .into_iter()
1838 .map(|(start, end, options)| (GuestAddress(start), end - start, options))
1839 .collect())
1840}
1841
1842fn create_guest_memory(
1843 cfg: &Config,
1844 components: &VmComponents,
1845 arch_memory_layout: &<Arch as LinuxArch>::ArchMemoryLayout,
1846 hypervisor: &impl Hypervisor,
1847) -> Result<GuestMemory> {
1848 let guest_mem_layout = Arch::guest_memory_layout(components, arch_memory_layout, hypervisor)
1849 .context("failed to create guest memory layout")?;
1850
1851 let guest_mem_layout = punch_holes_in_guest_mem_layout_for_mappings(
1852 guest_mem_layout,
1853 &cfg.file_backed_mappings_ram,
1854 )?;
1855
1856 let mut guest_mem = GuestMemory::new_with_options(&guest_mem_layout)
1857 .context("failed to create guest memory")?;
1858 let mut mem_policy = MemoryPolicy::empty();
1859 if components.hugepages {
1860 mem_policy |= MemoryPolicy::USE_HUGEPAGES;
1861 }
1862
1863 if cfg.lock_guest_memory {
1864 mem_policy |= MemoryPolicy::LOCK_GUEST_MEMORY;
1865 }
1866 if cfg.jail_config.is_none() {
1869 mem_policy |= MemoryPolicy::USE_PUNCHHOLE_LOCKED;
1870 }
1871 guest_mem.set_memory_policy(mem_policy);
1872
1873 if cfg.unmap_guest_memory_on_fork {
1874 guest_mem.use_dontfork().context("use_dontfork failed")?;
1881 }
1882
1883 Ok(guest_mem)
1884}
1885
1886#[cfg(all(target_arch = "aarch64", feature = "geniezone"))]
1887fn run_gz(device_path: Option<&Path>, cfg: Config, components: VmComponents) -> Result<ExitState> {
1888 use devices::GeniezoneKernelIrqChip;
1889 use hypervisor::geniezone::Geniezone;
1890 use hypervisor::geniezone::GeniezoneVm;
1891
1892 let device_path = device_path.unwrap_or(Path::new(GENIEZONE_PATH));
1893 let gzvm = Geniezone::new_with_path(device_path)
1894 .with_context(|| format!("failed to open GenieZone device {}", device_path.display()))?;
1895
1896 let arch_memory_layout =
1897 Arch::arch_memory_layout(&components).context("failed to create arch memory layout")?;
1898 let guest_mem = create_guest_memory(&cfg, &components, &arch_memory_layout, &gzvm)?;
1899
1900 #[cfg(feature = "swap")]
1901 let swap_controller = if let Some(swap_dir) = cfg.swap_dir.as_ref() {
1902 Some(
1903 SwapController::launch(guest_mem.clone(), swap_dir, cfg.jail_config.as_ref())
1904 .context("launch vmm-swap monitor process")?,
1905 )
1906 } else {
1907 None
1908 };
1909
1910 let vm = Arc::new(
1911 GeniezoneVm::new(&gzvm, guest_mem, components.hv_cfg).context("failed to create vm")?,
1912 );
1913
1914 if cfg.protection_type.isolates_memory() && !vm.check_capability(VmCap::Protected) {
1916 bail!("Failed to create protected VM");
1917 }
1918
1919 let ioapic_host_tube;
1920 let irq_chip = match cfg.irq_chip.unwrap_or_default() {
1921 IrqChipKind::Split => bail!("Geniezone does not support split irqchip mode"),
1922 IrqChipKind::Userspace => bail!("Geniezone does not support userspace irqchip mode"),
1923 IrqChipKind::Kernel { allow_vgic_its: _ } => {
1924 ioapic_host_tube = None;
1925 GeniezoneKernelIrqChip::new(vm.clone(), components.vcpu_properties.len())
1926 .context("failed to create IRQ chip")?
1927 }
1928 };
1929
1930 run_vm(
1931 cfg,
1932 components,
1933 &arch_memory_layout,
1934 vm,
1935 Arc::new(irq_chip),
1936 ioapic_host_tube,
1937 #[cfg(feature = "swap")]
1938 swap_controller,
1939 )
1940}
1941
1942#[cfg(all(target_arch = "aarch64", feature = "halla"))]
1943fn run_halla(
1944 device_path: Option<&Path>,
1945 cfg: Config,
1946 components: VmComponents,
1947) -> Result<ExitState> {
1948 use devices::HallaKernelIrqChip;
1949 use hypervisor::halla::Halla;
1950 use hypervisor::halla::HallaVm;
1951
1952 let device_path = device_path.unwrap_or(Path::new(HALLA_PATH));
1953 let hvm = Halla::new_with_path(device_path)
1954 .with_context(|| format!("failed to open Halla device {}", device_path.display()))?;
1955
1956 let arch_memory_layout =
1957 Arch::arch_memory_layout(&components).context("failed to create arch memory layout")?;
1958 let guest_mem = create_guest_memory(&cfg, &components, &arch_memory_layout, &hvm)?;
1959
1960 #[cfg(feature = "swap")]
1961 let swap_controller = if let Some(swap_dir) = cfg.swap_dir.as_ref() {
1962 Some(
1963 SwapController::launch(guest_mem.clone(), swap_dir, cfg.jail_config.as_ref())
1964 .context("launch vmm-swap monitor process")?,
1965 )
1966 } else {
1967 None
1968 };
1969
1970 let vm =
1971 Arc::new(HallaVm::new(&hvm, guest_mem, components.hv_cfg).context("failed to create vm")?);
1972
1973 if cfg.protection_type.isolates_memory() && !vm.check_capability(VmCap::Protected) {
1975 bail!("Failed to create protected VM");
1976 }
1977
1978 let ioapic_host_tube;
1979 let irq_chip = match cfg.irq_chip.unwrap_or_default() {
1980 IrqChipKind::Split => bail!("Halla does not support split irqchip mode"),
1981 IrqChipKind::Userspace => bail!("Halla does not support userspace irqchip mode"),
1982 IrqChipKind::Kernel { allow_vgic_its: _ } => {
1983 ioapic_host_tube = None;
1984 HallaKernelIrqChip::new(vm.clone(), components.vcpu_properties.len())
1985 .context("failed to create IRQ chip")?
1986 }
1987 };
1988
1989 run_vm(
1990 cfg,
1991 components,
1992 &arch_memory_layout,
1993 vm,
1994 Arc::new(irq_chip),
1995 ioapic_host_tube,
1996 #[cfg(feature = "swap")]
1997 swap_controller,
1998 )
1999}
2000
2001fn run_kvm(device_path: Option<&Path>, cfg: Config, components: VmComponents) -> Result<ExitState> {
2002 use devices::KvmKernelIrqChip;
2003 #[cfg(target_arch = "x86_64")]
2004 use devices::KvmSplitIrqChip;
2005 use hypervisor::kvm::Kvm;
2006 use hypervisor::kvm::KvmVm;
2007
2008 let device_path = device_path.unwrap_or(Path::new(KVM_PATH));
2009 let kvm = Kvm::new_with_path(device_path)
2010 .with_context(|| format!("failed to open KVM device {}", device_path.display()))?;
2011
2012 let arch_memory_layout =
2013 Arch::arch_memory_layout(&components).context("failed to create arch memory layout")?;
2014 let guest_mem = create_guest_memory(&cfg, &components, &arch_memory_layout, &kvm)?;
2015
2016 #[cfg(feature = "swap")]
2017 let swap_controller = if let Some(swap_dir) = cfg.swap_dir.as_ref() {
2018 Some(
2019 SwapController::launch(guest_mem.clone(), swap_dir, cfg.jail_config.as_ref())
2020 .context("launch vmm-swap monitor process")?,
2021 )
2022 } else {
2023 None
2024 };
2025
2026 let vm =
2027 Arc::new(KvmVm::new(&kvm, guest_mem, components.hv_cfg).context("failed to create vm")?);
2028
2029 #[cfg(target_arch = "x86_64")]
2030 if cfg.itmt {
2031 vm.set_platform_info_read_access(false)
2032 .context("failed to disable MSR_PLATFORM_INFO read access")?;
2033 }
2034
2035 #[cfg(not(target_arch = "x86_64"))]
2039 if cfg.protection_type.isolates_memory() && !vm.check_capability(VmCap::Protected) {
2040 bail!("Failed to create protected VM");
2041 }
2042
2043 let ioapic_host_tube;
2044 let irq_chip: Arc<dyn IrqChipArch> = match cfg.irq_chip.unwrap_or_default() {
2045 IrqChipKind::Userspace => {
2046 bail!("KVM userspace irqchip mode not implemented");
2047 }
2048 IrqChipKind::Split => {
2049 #[cfg(not(target_arch = "x86_64"))]
2050 bail!("KVM split irqchip mode only supported on x86 processors");
2051 #[cfg(target_arch = "x86_64")]
2052 {
2053 let (host_tube, ioapic_device_tube) =
2054 Tube::pair().context("failed to create tube")?;
2055 ioapic_host_tube = Some(host_tube);
2056 Arc::new(
2057 KvmSplitIrqChip::new(
2058 vm.clone(),
2059 components.vcpu_properties.len(),
2060 ioapic_device_tube,
2061 Some(24),
2062 )
2063 .context("failed to create IRQ chip")?,
2064 )
2065 }
2066 }
2067 IrqChipKind::Kernel {
2068 #[cfg(target_arch = "aarch64")]
2069 allow_vgic_its,
2070 } => {
2071 ioapic_host_tube = None;
2072 Arc::new(
2073 KvmKernelIrqChip::new(
2074 vm.clone(),
2075 components.vcpu_properties.len(),
2076 #[cfg(target_arch = "aarch64")]
2077 allow_vgic_its,
2078 )
2079 .context("failed to create IRQ chip")?,
2080 )
2081 }
2082 };
2083
2084 run_vm(
2085 cfg,
2086 components,
2087 &arch_memory_layout,
2088 vm,
2089 irq_chip,
2090 ioapic_host_tube,
2091 #[cfg(feature = "swap")]
2092 swap_controller,
2093 )
2094}
2095
2096#[cfg(all(target_arch = "aarch64", feature = "gunyah"))]
2097fn run_gunyah(
2098 device_path: Option<&Path>,
2099 qcom_trusted_vm_id: Option<u16>,
2100 qcom_trusted_vm_pas_id: Option<u32>,
2101 cfg: Config,
2102 components: VmComponents,
2103) -> Result<ExitState> {
2104 use devices::GunyahIrqChip;
2105 use hypervisor::gunyah::Gunyah;
2106 use hypervisor::gunyah::GunyahVm;
2107
2108 let device_path = device_path.unwrap_or(Path::new(GUNYAH_PATH));
2109 let gunyah = Gunyah::new_with_path(device_path)
2110 .with_context(|| format!("failed to open Gunyah device {}", device_path.display()))?;
2111
2112 let arch_memory_layout =
2113 Arch::arch_memory_layout(&components).context("failed to create arch memory layout")?;
2114 let guest_mem = create_guest_memory(&cfg, &components, &arch_memory_layout, &gunyah)?;
2115
2116 #[cfg(feature = "swap")]
2117 let swap_controller = if let Some(swap_dir) = cfg.swap_dir.as_ref() {
2118 Some(
2119 SwapController::launch(guest_mem.clone(), swap_dir, cfg.jail_config.as_ref())
2120 .context("launch vmm-swap monitor process")?,
2121 )
2122 } else {
2123 None
2124 };
2125
2126 let vm = Arc::new(
2127 GunyahVm::new(
2128 &gunyah,
2129 qcom_trusted_vm_id,
2130 qcom_trusted_vm_pas_id,
2131 guest_mem,
2132 components.hv_cfg,
2133 )
2134 .context("failed to create vm")?,
2135 );
2136
2137 if cfg.protection_type.isolates_memory() && !vm.check_capability(VmCap::Protected) {
2139 bail!("Failed to create protected VM");
2140 }
2141
2142 run_vm(
2143 cfg,
2144 components,
2145 &arch_memory_layout,
2146 vm.clone(),
2147 Arc::new(GunyahIrqChip::new(vm)?),
2148 None,
2149 #[cfg(feature = "swap")]
2150 swap_controller,
2151 )
2152}
2153
2154fn get_default_hypervisor() -> Option<HypervisorKind> {
2156 let kvm_path = Path::new(KVM_PATH);
2157 if kvm_path.exists() {
2158 return Some(HypervisorKind::Kvm {
2159 device: Some(kvm_path.to_path_buf()),
2160 });
2161 }
2162
2163 #[cfg(all(target_arch = "aarch64", feature = "geniezone"))]
2164 {
2165 let gz_path = Path::new(GENIEZONE_PATH);
2166 if gz_path.exists() {
2167 return Some(HypervisorKind::Geniezone {
2168 device: Some(gz_path.to_path_buf()),
2169 });
2170 }
2171 }
2172
2173 #[cfg(target_arch = "aarch64")]
2174 #[cfg(feature = "halla")]
2175 {
2176 let halla_path = Path::new(HALLA_PATH);
2177 if halla_path.exists() {
2178 return Some(HypervisorKind::Halla {
2179 device: Some(halla_path.to_path_buf()),
2180 });
2181 }
2182 }
2183
2184 #[cfg(all(unix, target_arch = "aarch64", feature = "gunyah"))]
2185 {
2186 let gunyah_path = Path::new(GUNYAH_PATH);
2187 if gunyah_path.exists() {
2188 return Some(HypervisorKind::Gunyah {
2189 device: Some(gunyah_path.to_path_buf()),
2190 qcom_trusted_vm_id: None,
2191 qcom_trusted_vm_pas_id: None,
2192 });
2193 }
2194 }
2195
2196 None
2197}
2198
2199pub fn run_config(cfg: Config) -> Result<ExitState> {
2200 let components = setup_vm_components(&cfg)?;
2201
2202 let hypervisor = cfg
2203 .hypervisor
2204 .clone()
2205 .or_else(get_default_hypervisor)
2206 .context("no enabled hypervisor")?;
2207
2208 debug!("creating hypervisor: {:?}", hypervisor);
2209
2210 match hypervisor {
2211 HypervisorKind::Kvm { device } => run_kvm(device.as_deref(), cfg, components),
2212 #[cfg(all(target_arch = "aarch64", feature = "geniezone"))]
2213 HypervisorKind::Geniezone { device } => run_gz(device.as_deref(), cfg, components),
2214 #[cfg(target_arch = "aarch64")]
2215 #[cfg(feature = "halla")]
2216 HypervisorKind::Halla { device } => run_halla(device.as_deref(), cfg, components),
2217 #[cfg(all(unix, target_arch = "aarch64", feature = "gunyah"))]
2218 HypervisorKind::Gunyah {
2219 device,
2220 qcom_trusted_vm_id,
2221 qcom_trusted_vm_pas_id,
2222 } => run_gunyah(
2223 device.as_deref(),
2224 qcom_trusted_vm_id,
2225 qcom_trusted_vm_pas_id,
2226 cfg,
2227 components,
2228 ),
2229 }
2230}
2231
2232fn run_vm(
2233 cfg: Config,
2234 #[allow(unused_mut)] mut components: VmComponents,
2235 arch_memory_layout: &<Arch as LinuxArch>::ArchMemoryLayout,
2236 vm: Arc<dyn VmArch>,
2237 irq_chip: Arc<dyn IrqChipArch>,
2238 ioapic_host_tube: Option<Tube>,
2239 #[cfg(feature = "swap")] mut swap_controller: Option<SwapController>,
2240) -> Result<ExitState> {
2241 if cfg.jail_config.is_some() {
2242 info!("crosvm entering multiprocess mode");
2246 }
2247
2248 let (metrics_send, metrics_recv) = Tube::directional_pair().context("metrics tube")?;
2249 metrics::initialize(metrics_send);
2250
2251 #[cfg(all(feature = "pci-hotplug", feature = "swap"))]
2252 let swap_device_helper = match &swap_controller {
2253 Some(swap_controller) => Some(swap_controller.create_device_helper()?),
2254 None => None,
2255 };
2256 #[cfg(all(feature = "pci-hotplug", not(target_arch = "x86_64")))]
2259 if cfg.pci_hotplug_slots.is_some() {
2260 bail!("pci-hotplug is not implemented for non x86_64 architecture");
2261 }
2262 #[cfg(feature = "pci-hotplug")]
2264 #[allow(unused_mut)]
2266 let mut hotplug_manager = if cfg.pci_hotplug_slots.is_some() {
2267 Some(PciHotPlugManager::new(
2268 vm.get_memory().clone(),
2269 &cfg,
2270 #[cfg(feature = "swap")]
2271 swap_device_helper,
2272 )?)
2273 } else {
2274 None
2275 };
2276
2277 #[cfg(feature = "usb")]
2278 let (usb_control_tube, usb_provider) =
2279 DeviceProvider::new().context("failed to create usb provider")?;
2280
2281 let sigchld_fd = SignalFd::new(libc::SIGCHLD).context("failed to create signalfd")?;
2285
2286 let control_server_socket = match &cfg.socket_path {
2287 Some(path) => Some(UnlinkUnixSeqpacketListener(
2288 UnixSeqpacketListener::bind(path).context("failed to create control server")?,
2289 )),
2290 None => None,
2291 };
2292
2293 let mut all_control_tubes = Vec::new();
2294 let mut add_control_tube = |t| all_control_tubes.push(t);
2295
2296 if let Some(ioapic_host_tube) = ioapic_host_tube {
2297 add_control_tube(AnyControlTube::IrqTube(ioapic_host_tube));
2298 }
2299
2300 let battery = if cfg.battery_config.is_some() {
2301 #[cfg_attr(
2302 not(feature = "power-monitor-powerd"),
2303 allow(clippy::manual_map, clippy::needless_match, unused_mut)
2304 )]
2305 let jail = if let Some(jail_config) = cfg.jail_config.as_ref() {
2306 let mut config = SandboxConfig::new(jail_config, "battery");
2307 #[cfg(feature = "power-monitor-powerd")]
2308 {
2309 config.bind_mounts = true;
2310 }
2311 let mut jail =
2312 create_sandbox_minijail(&jail_config.pivot_root, MAX_OPEN_FILES_DEFAULT, &config)?;
2313
2314 #[cfg(feature = "power-monitor-powerd")]
2316 {
2317 let system_bus_socket_path = Path::new("/run/dbus/system_bus_socket");
2318 jail.mount_bind(system_bus_socket_path, system_bus_socket_path, true)?;
2319 }
2320 Some(jail)
2321 } else {
2322 None
2323 };
2324 (cfg.battery_config.as_ref().map(|c| c.type_), jail)
2325 } else {
2326 (cfg.battery_config.as_ref().map(|c| c.type_), None)
2327 };
2328
2329 let (vm_evt_wrtube, vm_evt_rdtube) =
2330 Tube::directional_pair().context("failed to create vm event tube")?;
2331
2332 let pstore_size = components.pstore.as_ref().map(|pstore| pstore.size as u64);
2333 let mut sys_allocator = SystemAllocator::new(
2334 Arch::get_system_allocator_config(&*vm, arch_memory_layout),
2335 pstore_size,
2336 &cfg.mmio_address_ranges,
2337 )
2338 .context("failed to create system allocator")?;
2339
2340 let ramoops_region = match &components.pstore {
2341 Some(pstore) => Some(
2342 arch::pstore::create_memory_region(
2343 &*vm,
2344 sys_allocator.reserved_region().unwrap(),
2345 pstore,
2346 )
2347 .context("failed to allocate pstore region")?,
2348 ),
2349 None => None,
2350 };
2351
2352 create_mmio_file_backed_mappings(&cfg, &*vm, &mut sys_allocator)?;
2353
2354 #[cfg(feature = "gpu")]
2355 let (_render_server_jail, render_server_fd) =
2357 if let Some(parameters) = &cfg.gpu_render_server_parameters {
2358 let (jail, fd) = start_gpu_render_server(&cfg, parameters)?;
2359 (Some(ScopedMinijail(jail)), Some(fd))
2360 } else {
2361 (None, None)
2362 };
2363
2364 let mut iommu_attached_endpoints: BTreeMap<u32, Arc<Mutex<Box<dyn MemoryMapperTrait>>>> =
2365 BTreeMap::new();
2366 let mut iova_max_addr: Option<u64> = None;
2367
2368 let mut vfio_container_manager = VfioContainerManager::new();
2369
2370 #[cfg(feature = "registered_events")]
2371 let (reg_evt_wrtube, reg_evt_rdtube) =
2372 Tube::directional_pair().context("failed to create registered event tube")?;
2373
2374 let mut worker_process_pids = BTreeSet::new();
2375
2376 let mut devices = create_devices(
2377 &cfg,
2378 &*vm,
2379 &mut sys_allocator,
2380 &mut add_control_tube,
2381 &vm_evt_wrtube,
2382 &mut iommu_attached_endpoints,
2383 #[cfg(feature = "usb")]
2384 usb_provider,
2385 #[cfg(feature = "gpu")]
2386 render_server_fd,
2387 &mut iova_max_addr,
2388 #[cfg(feature = "registered_events")]
2389 ®_evt_wrtube,
2390 &mut vfio_container_manager,
2391 &mut worker_process_pids,
2392 )?;
2393
2394 #[cfg(feature = "pci-hotplug")]
2395 #[allow(unused_variables)]
2397 let pci_hotplug_slots = cfg.pci_hotplug_slots;
2398 #[cfg(not(feature = "pci-hotplug"))]
2399 #[allow(unused_variables)]
2400 let pci_hotplug_slots: Option<u8> = None;
2401 #[cfg(target_arch = "x86_64")]
2402 let hp_stub = create_pure_virtual_pcie_root_port(
2403 &mut sys_allocator,
2404 &mut add_control_tube,
2405 &mut devices,
2406 pci_hotplug_slots.unwrap_or(1),
2407 )?;
2408
2409 arch::assign_pci_addresses(&mut devices, &mut sys_allocator)?;
2410
2411 let pci_devices: Vec<&dyn PciDevice> = devices
2412 .iter()
2413 .filter_map(|d| (d.0).as_pci_device())
2414 .collect();
2415
2416 let virtio_devices: Vec<(&dyn VirtioDevice, devices::PciAddress)> = pci_devices
2417 .into_iter()
2418 .flat_map(|s| {
2419 if let Some(virtio_pci_device) = s.as_virtio_pci_device() {
2420 std::iter::zip(
2421 Some(virtio_pci_device.virtio_device()),
2422 virtio_pci_device.pci_address(),
2423 )
2424 .next()
2425 } else {
2426 None
2427 }
2428 })
2429 .collect();
2430
2431 let mut open_firmware_device_paths: Vec<(Vec<u8>, usize)> = virtio_devices
2432 .iter()
2433 .flat_map(|s| (s.0).bootorder_fw_cfg(s.1.dev))
2434 .collect();
2435
2436 open_firmware_device_paths.sort_by(|a, b| (a.1).cmp(&(b.1)));
2438
2439 let mut bootorder_fw_cfg_blob =
2441 open_firmware_device_paths
2442 .into_iter()
2443 .fold(Vec::new(), |a, b| {
2444 a.into_iter()
2445 .chain("/pci@i0cf8/".as_bytes().iter().copied())
2446 .chain(b.0)
2447 .chain("\n".as_bytes().iter().copied())
2448 .collect()
2449 });
2450
2451 bootorder_fw_cfg_blob.push(0);
2453
2454 components.bootorder_fw_cfg_blob = bootorder_fw_cfg_blob;
2455
2456 components.fw_cfg_enable |= components.bootorder_fw_cfg_blob.len() > 1;
2459
2460 let (translate_response_senders, request_rx) = setup_virtio_access_platform(
2461 &mut sys_allocator,
2462 &mut iommu_attached_endpoints,
2463 &mut devices,
2464 )?;
2465
2466 #[cfg(target_arch = "x86_64")]
2467 let iommu_bus_ranges = hp_stub.iommu_bus_ranges;
2468 #[cfg(not(target_arch = "x86_64"))]
2469 let iommu_bus_ranges = Vec::new();
2470
2471 let iommu_host_tube = if !iommu_attached_endpoints.is_empty()
2472 || (cfg.vfio_isolate_hotplug && !iommu_bus_ranges.is_empty())
2473 {
2474 let (iommu_host_tube, iommu_device_tube) = Tube::pair().context("failed to create tube")?;
2475 let iommu_dev = create_iommu_device(
2476 cfg.protection_type,
2477 cfg.jail_config.as_ref(),
2478 iova_max_addr.unwrap_or(u64::MAX),
2479 iommu_attached_endpoints,
2480 iommu_bus_ranges,
2481 translate_response_senders,
2482 request_rx,
2483 iommu_device_tube,
2484 )?;
2485
2486 let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
2487 add_control_tube(AnyControlTube::IrqTube(msi_host_tube));
2488 let (ioevent_host_tube, ioevent_device_tube) =
2489 Tube::pair().context("failed to create ioevent tube")?;
2490 add_control_tube(AnyControlTube::VmMemoryTube {
2491 tube: ioevent_host_tube,
2492 expose_with_viommu: false,
2493 remote_peer: iommu_dev.jail.is_some(),
2494 });
2495 let (host_tube, device_tube) =
2496 Tube::pair().context("failed to create device control tube")?;
2497 add_control_tube(AnyControlTube::Vm(host_tube));
2498 let mut dev = VirtioPciDevice::new(
2499 vm.get_memory().clone(),
2500 iommu_dev.dev,
2501 msi_device_tube,
2502 cfg.disable_virtio_intx,
2503 None,
2504 VmMemoryClient::new(ioevent_device_tube),
2505 device_tube,
2506 )
2507 .context("failed to create virtio pci dev")?;
2508 dev.allocate_address(&mut sys_allocator)
2510 .context("failed to allocate resources early for virtio pci dev")?;
2511 let dev = Box::new(dev);
2512 devices.push((dev, iommu_dev.jail));
2513 Some(iommu_host_tube)
2514 } else {
2515 None
2516 };
2517
2518 #[cfg(target_arch = "x86_64")]
2519 for device in devices
2520 .iter_mut()
2521 .filter_map(|(dev, _)| dev.as_pci_device_mut())
2522 {
2523 device
2524 .generate_acpi(&mut components.acpi_sdts)
2525 .with_context(|| format!("generate_acpi failed for {}", device.debug_label()))?;
2526 }
2527
2528 let mut vcpu_ids = Vec::new();
2530
2531 let guest_suspended_cvar = if cfg.force_s2idle {
2532 Some(Arc::new((Mutex::new(false), Condvar::new())))
2533 } else {
2534 None
2535 };
2536
2537 let dt_overlays = cfg
2538 .device_tree_overlay
2539 .iter()
2540 .map(|o| {
2541 Ok(DtbOverlay {
2542 file: open_file_or_duplicate(o.path.as_path(), OpenOptions::new().read(true))
2543 .with_context(|| {
2544 format!("failed to open device tree overlay {}", o.path.display())
2545 })?,
2546 symbol_allowlist: o.select_symbols.clone().map(|v| v.into_iter().collect()),
2547 })
2548 })
2549 .collect::<Result<Vec<DtbOverlay>>>()?;
2550
2551 #[cfg(target_arch = "aarch64")]
2552 let vcpu_domain_paths: BTreeMap<usize, PathBuf> = components
2553 .vcpu_properties
2554 .iter()
2555 .filter_map(|(id, props)| {
2556 props
2557 .vcpu_domain_path
2558 .as_ref()
2559 .map(|path| (*id, path.clone()))
2560 })
2561 .collect();
2562
2563 let mut linux = Arch::build_vm(
2564 components,
2565 arch_memory_layout,
2566 &vm_evt_wrtube,
2567 &mut sys_allocator,
2568 &cfg.serial_parameters,
2569 simple_jail(cfg.jail_config.as_ref(), "serial_device")?,
2570 battery,
2571 vm,
2572 ramoops_region,
2573 devices,
2574 irq_chip.clone(),
2575 &mut vcpu_ids,
2576 cfg.dump_device_tree_blob.clone(),
2577 simple_jail(cfg.jail_config.as_ref(), "serial_device")?,
2578 #[cfg(target_arch = "x86_64")]
2579 simple_jail(cfg.jail_config.as_ref(), "block_device")?,
2580 #[cfg(target_arch = "x86_64")]
2581 simple_jail(cfg.jail_config.as_ref(), "fw_cfg_device")?,
2582 #[cfg(feature = "swap")]
2583 &mut swap_controller,
2584 guest_suspended_cvar.clone(),
2585 dt_overlays,
2586 cfg.fdt_position,
2587 cfg.no_pmu,
2588 )
2589 .context("the architecture failed to build the vm")?;
2590
2591 for tube in linux.vm_request_tubes.drain(..) {
2592 add_control_tube(AnyControlTube::Vm(tube));
2593 }
2594
2595 #[cfg(target_arch = "x86_64")]
2596 let (hp_control_tube, hp_worker_tube) = mpsc::channel();
2597 #[cfg(all(feature = "pci-hotplug", target_arch = "x86_64"))]
2598 if let Some(hotplug_manager) = &mut hotplug_manager {
2599 hotplug_manager.set_rootbus_controller(hp_control_tube.clone())?;
2600 }
2601 #[cfg(target_arch = "x86_64")]
2602 let hp_thread = {
2603 for (bus_num, hp_bus) in hp_stub.hotplug_buses.into_iter() {
2604 #[cfg(feature = "pci-hotplug")]
2605 if let Some(hotplug_manager) = &mut hotplug_manager {
2606 hotplug_manager.add_port(hp_bus)?;
2607 } else {
2608 linux.hotplug_bus.insert(bus_num, hp_bus);
2609 }
2610 #[cfg(not(feature = "pci-hotplug"))]
2611 linux.hotplug_bus.insert(bus_num, hp_bus);
2612 }
2613
2614 if let Some(pm) = &linux.pm {
2615 for (bus, notify_dev) in hp_stub.pme_notify_devs.into_iter() {
2616 pm.lock().register_pme_notify_dev(bus, notify_dev);
2617 }
2618 }
2619
2620 let (hp_vm_mem_host_tube, hp_vm_mem_worker_tube) =
2621 Tube::pair().context("failed to create tube")?;
2622 add_control_tube(AnyControlTube::VmMemoryTube {
2623 tube: hp_vm_mem_host_tube,
2624 expose_with_viommu: false,
2625 remote_peer: false,
2626 });
2627
2628 let supports_readonly_mapping = linux.vm.as_ref().supports_readonly_mapping();
2629 let pci_root = linux.root_config.clone();
2630 std::thread::Builder::new()
2631 .name("pci_root".to_string())
2632 .spawn(move || {
2633 start_pci_root_worker(
2634 supports_readonly_mapping,
2635 pci_root,
2636 hp_worker_tube,
2637 hp_vm_mem_worker_tube,
2638 )
2639 })?
2640 };
2641
2642 #[cfg(feature = "gpu")]
2643 let flags = RutabagaGrallocBackendFlags::new().disable_vulkano();
2644 #[cfg(feature = "gpu")]
2645 let gralloc = RutabagaGralloc::new(flags).context("failed to create gralloc")?;
2646
2647 run_control(
2648 linux,
2649 sys_allocator,
2650 cfg,
2651 control_server_socket,
2652 all_control_tubes,
2653 #[cfg(feature = "usb")]
2654 usb_control_tube,
2655 vm_evt_rdtube,
2656 vm_evt_wrtube,
2657 sigchld_fd,
2658 #[cfg(feature = "gpu")]
2659 gralloc,
2660 vcpu_ids,
2661 iommu_host_tube,
2662 #[cfg(target_arch = "x86_64")]
2663 hp_control_tube,
2664 #[cfg(target_arch = "x86_64")]
2665 hp_thread,
2666 #[cfg(feature = "pci-hotplug")]
2667 hotplug_manager,
2668 #[cfg(feature = "swap")]
2669 swap_controller,
2670 #[cfg(feature = "registered_events")]
2671 reg_evt_rdtube,
2672 guest_suspended_cvar,
2673 metrics_recv,
2674 vfio_container_manager,
2675 worker_process_pids,
2676 #[cfg(target_arch = "aarch64")]
2677 vcpu_domain_paths,
2678 )
2679}
2680
2681#[cfg(target_arch = "x86_64")]
2690fn start_pci_root_worker(
2691 supports_readonly_mapping: bool,
2692 pci_root: Arc<Mutex<PciRoot>>,
2693 hp_device_tube: mpsc::Receiver<PciRootCommand>,
2694 vm_control_tube: Tube,
2695) {
2696 struct PciMmioMapperTube {
2697 supports_readonly_mapping: bool,
2698 vm_control_tube: Tube,
2699 registered_regions: BTreeMap<u32, VmMemoryRegionId>,
2700 next_id: u32,
2701 }
2702
2703 impl PciMmioMapper for PciMmioMapperTube {
2704 fn supports_readonly_mapping(&self) -> bool {
2705 self.supports_readonly_mapping
2706 }
2707
2708 fn add_mapping(&mut self, addr: GuestAddress, shmem: &SharedMemory) -> anyhow::Result<u32> {
2709 let shmem = shmem
2710 .try_clone()
2711 .context("failed to create new SharedMemory")?;
2712 self.vm_control_tube
2713 .send(&VmMemoryRequest::RegisterMemory {
2714 source: VmMemorySource::SharedMemory(shmem),
2715 dest: VmMemoryDestination::GuestPhysicalAddress(addr.0),
2716 prot: Protection::read(),
2717 cache: MemCacheType::CacheCoherent,
2718 })
2719 .context("failed to send request")?;
2720 match self.vm_control_tube.recv::<VmMemoryResponse>() {
2721 Ok(VmMemoryResponse::RegisterMemory { region_id, .. }) => {
2722 let cur_id = self.next_id;
2723 self.registered_regions.insert(cur_id, region_id);
2724 self.next_id += 1;
2725 Ok(cur_id)
2726 }
2727 res => bail!("Bad response: {:?}", res),
2728 }
2729 }
2730 }
2731
2732 let mut mapper = PciMmioMapperTube {
2733 supports_readonly_mapping,
2734 vm_control_tube,
2735 registered_regions: BTreeMap::new(),
2736 next_id: 0,
2737 };
2738
2739 loop {
2740 match hp_device_tube.recv() {
2741 Ok(cmd) => match cmd {
2742 PciRootCommand::Add(addr, device) => {
2743 if let Err(e) = pci_root.lock().add_device(addr, device, &mut mapper) {
2744 error!("failed to add hotplugged device to PCI root port: {}", e);
2745 }
2746 }
2747 PciRootCommand::AddBridge(pci_bus) => {
2748 if let Err(e) = pci_root.lock().add_bridge(pci_bus) {
2749 error!("failed to add hotplugged bridge to PCI root port: {}", e);
2750 }
2751 }
2752 PciRootCommand::Remove(addr) => {
2753 pci_root.lock().remove_device(addr);
2754 }
2755 PciRootCommand::Kill => break,
2756 },
2757 Err(e) => {
2758 error!("Error: pci root worker channel closed: {}", e);
2759 break;
2760 }
2761 }
2762 }
2763}
2764
2765#[cfg(target_arch = "x86_64")]
2766fn get_hp_bus(
2767 linux: &RunnableLinuxVm,
2768 host_addr: PciAddress,
2769) -> Result<Arc<Mutex<dyn HotPlugBus>>> {
2770 for (_, hp_bus) in linux.hotplug_bus.iter() {
2771 if hp_bus.lock().is_match(host_addr).is_some() {
2772 return Ok(hp_bus.clone());
2773 }
2774 }
2775 Err(anyhow!("Failed to find a suitable hotplug bus"))
2776}
2777
2778#[cfg(target_arch = "x86_64")]
2779fn add_hotplug_device(
2780 linux: &mut RunnableLinuxVm,
2781 sys_allocator: &mut SystemAllocator,
2782 cfg: &Config,
2783 add_control_tube: &mut impl FnMut(AnyControlTube),
2784 hp_control_tube: &mpsc::Sender<PciRootCommand>,
2785 iommu_host_tube: Option<&Tube>,
2786 device: &HotPlugDeviceInfo,
2787 #[cfg(feature = "swap")] swap_controller: &mut Option<SwapController>,
2788 vfio_container_manager: &mut VfioContainerManager,
2789) -> Result<()> {
2790 let host_addr = PciAddress::from_path(&device.path)
2791 .context("failed to parse hotplug device's PCI address")?;
2792 let hp_bus = get_hp_bus(linux, host_addr)?;
2793
2794 let (hotplug_key, pci_address) = match device.device_type {
2795 HotPlugDeviceType::UpstreamPort | HotPlugDeviceType::DownstreamPort => {
2796 let (vm_host_tube, vm_device_tube) = Tube::pair().context("failed to create tube")?;
2797 add_control_tube(AnyControlTube::Vm(vm_host_tube));
2798 let (msi_host_tube, msi_device_tube) = Tube::pair().context("failed to create tube")?;
2799 add_control_tube(AnyControlTube::IrqTube(msi_host_tube));
2800 let pcie_host = PcieHostPort::new(device.path.as_path(), vm_device_tube)?;
2801 let (hotplug_key, pci_bridge) = match device.device_type {
2802 HotPlugDeviceType::UpstreamPort => {
2803 let hotplug_key = HotPlugKey::HostUpstreamPort { host_addr };
2804 let pcie_upstream_port = Arc::new(Mutex::new(PcieUpstreamPort::new_from_host(
2805 pcie_host, true,
2806 )?));
2807 let pci_bridge =
2808 Box::new(PciBridge::new(pcie_upstream_port.clone(), msi_device_tube));
2809 linux
2810 .hotplug_bus
2811 .insert(pci_bridge.get_secondary_num(), pcie_upstream_port);
2812 (hotplug_key, pci_bridge)
2813 }
2814 HotPlugDeviceType::DownstreamPort => {
2815 let hotplug_key = HotPlugKey::HostDownstreamPort { host_addr };
2816 let pcie_downstream_port = Arc::new(Mutex::new(
2817 PcieDownstreamPort::new_from_host(pcie_host, true)?,
2818 ));
2819 let pci_bridge = Box::new(PciBridge::new(
2820 pcie_downstream_port.clone(),
2821 msi_device_tube,
2822 ));
2823 linux
2824 .hotplug_bus
2825 .insert(pci_bridge.get_secondary_num(), pcie_downstream_port);
2826 (hotplug_key, pci_bridge)
2827 }
2828 _ => {
2829 bail!("Impossible to reach here")
2830 }
2831 };
2832 let pci_address = Arch::register_pci_device(
2833 linux,
2834 pci_bridge,
2835 None,
2836 sys_allocator,
2837 hp_control_tube,
2838 #[cfg(feature = "swap")]
2839 swap_controller,
2840 )?;
2841
2842 (hotplug_key, pci_address)
2843 }
2844 HotPlugDeviceType::EndPoint => {
2845 let hotplug_key = HotPlugKey::HostVfio { host_addr };
2846 let (vfio_device, jail, viommu_mapper) = create_vfio_device(
2847 cfg.jail_config.as_ref(),
2848 &*linux.vm,
2849 sys_allocator,
2850 add_control_tube,
2851 &device.path,
2852 true,
2853 None,
2854 None,
2855 None,
2856 if iommu_host_tube.is_some() {
2857 IommuDevType::VirtioIommu
2858 } else {
2859 IommuDevType::NoIommu
2860 },
2861 None,
2862 vfio_container_manager,
2863 )?;
2864 let vfio_pci_device = match vfio_device {
2865 VfioDeviceVariant::Pci(pci) => Box::new(pci),
2866 VfioDeviceVariant::Platform(_) => bail!("vfio platform hotplug not supported"),
2867 };
2868 let pci_address = Arch::register_pci_device(
2869 linux,
2870 vfio_pci_device,
2871 jail,
2872 sys_allocator,
2873 hp_control_tube,
2874 #[cfg(feature = "swap")]
2875 swap_controller,
2876 )?;
2877 if let Some(iommu_host_tube) = iommu_host_tube {
2878 let endpoint_addr = pci_address.to_u32();
2879 let vfio_wrapper = viommu_mapper.context("expected mapper")?;
2880 let descriptor = vfio_wrapper.clone_as_raw_descriptor()?;
2881 let request =
2882 VirtioIOMMURequest::VfioCommand(VirtioIOMMUVfioCommand::VfioDeviceAdd {
2883 endpoint_addr,
2884 wrapper_id: vfio_wrapper.id(),
2885 container: {
2886 unsafe { File::from_raw_descriptor(descriptor) }
2889 },
2890 });
2891 match virtio_iommu_request(iommu_host_tube, &request)
2892 .map_err(|_| VirtioIOMMUVfioError::SocketFailed)?
2893 {
2894 VirtioIOMMUResponse::VfioResponse(VirtioIOMMUVfioResult::Ok) => (),
2895 resp => bail!("Unexpected message response: {:?}", resp),
2896 }
2897 }
2898
2899 (hotplug_key, pci_address)
2900 }
2901 };
2902 hp_bus.lock().add_hotplug_device(hotplug_key, pci_address);
2903 if device.hp_interrupt {
2904 hp_bus.lock().hot_plug(pci_address)?;
2905 }
2906 Ok(())
2907}
2908
2909#[cfg(feature = "pci-hotplug")]
2910fn add_hotplug_net(
2911 linux: &mut RunnableLinuxVm,
2912 sys_allocator: &mut SystemAllocator,
2913 add_control_tube: &mut impl FnMut(AnyControlTube),
2914 hotplug_manager: &mut PciHotPlugManager,
2915 net_param: NetParameters,
2916) -> Result<u8> {
2917 let (msi_host_tube, msi_device_tube) = Tube::pair().context("create tube")?;
2918 add_control_tube(AnyControlTube::IrqTube(msi_host_tube));
2919 let (ioevent_host_tube, ioevent_device_tube) = Tube::pair().context("create tube")?;
2920 let ioevent_vm_memory_client = VmMemoryClient::new(ioevent_device_tube);
2921 add_control_tube(AnyControlTube::VmMemoryTube {
2922 tube: ioevent_host_tube,
2923 expose_with_viommu: false,
2924 remote_peer: true,
2925 });
2926 let (vm_control_host_tube, vm_control_device_tube) = Tube::pair().context("create tube")?;
2927 add_control_tube(AnyControlTube::Vm(vm_control_host_tube));
2928 let net_carrier_device = NetResourceCarrier::new(
2929 net_param,
2930 msi_device_tube,
2931 ioevent_vm_memory_client,
2932 vm_control_device_tube,
2933 );
2934 hotplug_manager.hotplug_device(
2935 vec![ResourceCarrier::VirtioNet(net_carrier_device)],
2936 linux,
2937 sys_allocator,
2938 )
2939}
2940
2941#[cfg(feature = "pci-hotplug")]
2942fn handle_hotplug_net_command(
2943 net_cmd: NetControlCommand,
2944 linux: &mut RunnableLinuxVm,
2945 sys_allocator: &mut SystemAllocator,
2946 add_control_tube: &mut impl FnMut(AnyControlTube),
2947 hotplug_manager: &mut PciHotPlugManager,
2948) -> VmResponse {
2949 match net_cmd {
2950 NetControlCommand::AddTap(tap_name) => handle_hotplug_net_add(
2951 linux,
2952 sys_allocator,
2953 add_control_tube,
2954 hotplug_manager,
2955 &tap_name,
2956 ),
2957 NetControlCommand::RemoveTap(bus) => {
2958 handle_hotplug_net_remove(linux, sys_allocator, hotplug_manager, bus)
2959 }
2960 }
2961}
2962
2963#[cfg(feature = "pci-hotplug")]
2964fn handle_hotplug_net_add(
2965 linux: &mut RunnableLinuxVm,
2966 sys_allocator: &mut SystemAllocator,
2967 add_control_tube: &mut impl FnMut(AnyControlTube),
2968 hotplug_manager: &mut PciHotPlugManager,
2969 tap_name: &str,
2970) -> VmResponse {
2971 let net_param_mode = NetParametersMode::TapName {
2972 tap_name: tap_name.to_owned(),
2973 mac: None,
2974 };
2975 let net_param = NetParameters {
2976 mode: net_param_mode,
2977 vhost_net: None,
2978 vq_pairs: None,
2979 packed_queue: false,
2980 pci_address: None,
2981 mrg_rxbuf: false,
2982 };
2983 let ret = add_hotplug_net(
2984 linux,
2985 sys_allocator,
2986 add_control_tube,
2987 hotplug_manager,
2988 net_param,
2989 );
2990
2991 match ret {
2992 Ok(pci_bus) => VmResponse::PciHotPlugResponse { bus: pci_bus },
2993 Err(e) => VmResponse::ErrString(format!("{e:?}")),
2994 }
2995}
2996
2997#[cfg(feature = "pci-hotplug")]
2998fn handle_hotplug_net_remove(
2999 linux: &mut RunnableLinuxVm,
3000 sys_allocator: &mut SystemAllocator,
3001 hotplug_manager: &mut PciHotPlugManager,
3002 bus: u8,
3003) -> VmResponse {
3004 match hotplug_manager.remove_hotplug_device(bus, linux, sys_allocator) {
3005 Ok(_) => VmResponse::Ok,
3006 Err(e) => VmResponse::ErrString(format!("{e:?}")),
3007 }
3008}
3009
3010#[cfg(target_arch = "x86_64")]
3011fn remove_hotplug_bridge(
3012 linux: &RunnableLinuxVm,
3013 sys_allocator: &mut SystemAllocator,
3014 buses_to_remove: &mut Vec<u8>,
3015 hotplug_key: HotPlugKey,
3016 child_bus: u8,
3017) -> Result<()> {
3018 for (bus_num, hp_bus) in linux.hotplug_bus.iter() {
3019 let mut hp_bus_lock = hp_bus.lock();
3020 if let Some(pci_addr) = hp_bus_lock.get_hotplug_device(hotplug_key) {
3021 sys_allocator.release_pci(pci_addr);
3022 hp_bus_lock.hot_unplug(pci_addr)?;
3023 buses_to_remove.push(child_bus);
3024 if hp_bus_lock.is_empty() {
3025 if let Some(hotplug_key) = hp_bus_lock.get_hotplug_key() {
3026 remove_hotplug_bridge(
3027 linux,
3028 sys_allocator,
3029 buses_to_remove,
3030 hotplug_key,
3031 *bus_num,
3032 )?;
3033 }
3034 }
3035 return Ok(());
3036 }
3037 }
3038
3039 Err(anyhow!(
3040 "Can not find device {:?} on hotplug buses",
3041 hotplug_key
3042 ))
3043}
3044
3045#[cfg(target_arch = "x86_64")]
3046fn remove_hotplug_device(
3047 linux: &mut RunnableLinuxVm,
3048 sys_allocator: &mut SystemAllocator,
3049 iommu_host_tube: Option<&Tube>,
3050 device: &HotPlugDeviceInfo,
3051) -> Result<()> {
3052 let host_addr = PciAddress::from_path(&device.path)?;
3053 let hotplug_key = match device.device_type {
3054 HotPlugDeviceType::UpstreamPort => HotPlugKey::HostUpstreamPort { host_addr },
3055 HotPlugDeviceType::DownstreamPort => HotPlugKey::HostDownstreamPort { host_addr },
3056 HotPlugDeviceType::EndPoint => HotPlugKey::HostVfio { host_addr },
3057 };
3058
3059 let hp_bus = linux
3060 .hotplug_bus
3061 .iter()
3062 .find(|(_, hp_bus)| {
3063 let hp_bus = hp_bus.lock();
3064 hp_bus.get_hotplug_device(hotplug_key).is_some()
3065 })
3066 .map(|(bus_num, hp_bus)| (*bus_num, hp_bus.clone()));
3067
3068 if let Some((bus_num, hp_bus)) = hp_bus {
3069 let mut buses_to_remove = Vec::new();
3070 let mut removed_key = None;
3071 let mut hp_bus_lock = hp_bus.lock();
3072 if let Some(pci_addr) = hp_bus_lock.get_hotplug_device(hotplug_key) {
3073 if let Some(iommu_host_tube) = iommu_host_tube {
3074 let request =
3075 VirtioIOMMURequest::VfioCommand(VirtioIOMMUVfioCommand::VfioDeviceDel {
3076 endpoint_addr: pci_addr.to_u32(),
3077 });
3078 match virtio_iommu_request(iommu_host_tube, &request)
3079 .map_err(|_| VirtioIOMMUVfioError::SocketFailed)?
3080 {
3081 VirtioIOMMUResponse::VfioResponse(VirtioIOMMUVfioResult::Ok) => (),
3082 resp => bail!("Unexpected message response: {:?}", resp),
3083 }
3084 }
3085 let mut empty_simbling = true;
3086 if let Some(HotPlugKey::HostDownstreamPort { host_addr }) =
3087 hp_bus_lock.get_hotplug_key()
3088 {
3089 let addr_alias = host_addr;
3090 for (simbling_bus_num, hp_bus) in linux.hotplug_bus.iter() {
3091 if *simbling_bus_num != bus_num {
3092 let hp_bus_lock = hp_bus.lock();
3093 let hotplug_key = hp_bus_lock.get_hotplug_key();
3094 if let Some(HotPlugKey::HostDownstreamPort { host_addr }) = hotplug_key {
3095 if addr_alias.bus == host_addr.bus && !hp_bus_lock.is_empty() {
3096 empty_simbling = false;
3097 break;
3098 }
3099 }
3100 }
3101 }
3102 }
3103
3104 if !empty_simbling {
3108 hp_bus_lock.hot_unplug(pci_addr)?;
3109 }
3110
3111 sys_allocator.release_pci(pci_addr);
3112 if empty_simbling || hp_bus_lock.is_empty() {
3113 if let Some(hotplug_key) = hp_bus_lock.get_hotplug_key() {
3114 removed_key = Some(hotplug_key);
3115 remove_hotplug_bridge(
3116 linux,
3117 sys_allocator,
3118 &mut buses_to_remove,
3119 hotplug_key,
3120 bus_num,
3121 )?;
3122 }
3123 }
3124 }
3125
3126 if let Some(HotPlugKey::HostDownstreamPort { host_addr }) = removed_key {
3131 let addr_alias = host_addr;
3132 for (simbling_bus_num, hp_bus) in linux.hotplug_bus.iter() {
3133 if *simbling_bus_num != bus_num {
3134 let hp_bus_lock = hp_bus.lock();
3135 let hotplug_key = hp_bus_lock.get_hotplug_key();
3136 if let Some(HotPlugKey::HostDownstreamPort { host_addr }) = hotplug_key {
3137 if addr_alias.bus == host_addr.bus && hp_bus_lock.is_empty() {
3138 remove_hotplug_bridge(
3139 linux,
3140 sys_allocator,
3141 &mut buses_to_remove,
3142 hotplug_key.unwrap(),
3143 *simbling_bus_num,
3144 )?;
3145 }
3146 }
3147 }
3148 }
3149 }
3150 for bus in buses_to_remove.iter() {
3151 linux.hotplug_bus.remove(bus);
3152 }
3153 return Ok(());
3154 }
3155
3156 Err(anyhow!(
3157 "Can not find device {:?} on hotplug buses",
3158 hotplug_key
3159 ))
3160}
3161
3162pub fn trigger_vm_suspend_and_wait_for_entry(
3163 guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>,
3164 tube: &SendTube,
3165 response: vm_control::VmResponse,
3166 suspend_tube: Arc<Mutex<SendTube>>,
3167 pm: Option<Arc<Mutex<dyn PmResource + Send>>>,
3168) {
3169 let (lock, cvar) = &*guest_suspended_cvar;
3170 let mut guest_suspended = lock.lock();
3171
3172 *guest_suspended = false;
3173
3174 if let Some(pm) = pm {
3177 pm.lock().slpbtn_evt();
3178 } else {
3179 error!("generating sleepbtn during suspend not supported");
3180 }
3181
3182 let result = cvar.wait_timeout(guest_suspended, std::time::Duration::from_secs(15));
3185 guest_suspended = result.0;
3186
3187 if result.1.timed_out() {
3188 warn!("Guest suspension timeout - proceeding anyway");
3189 } else if *guest_suspended {
3190 info!("Guest suspended");
3191 }
3192
3193 if let Err(e) = suspend_tube.lock().send(&true) {
3194 error!("failed to trigger suspend event: {}", e);
3195 }
3196 if let Err(e) = tube.send(&response) {
3198 error!("failed to send VmResponse: {}", e);
3199 }
3200}
3201
3202#[cfg(feature = "pvclock")]
3203#[derive(Debug)]
3204enum PvClockAction {
3206 #[cfg(target_arch = "aarch64")]
3207 SetCounterOffset(u64),
3209}
3210
3211#[cfg(feature = "pvclock")]
3212fn send_pvclock_cmd(tube: &Tube, command: PvClockCommand) -> Result<Option<PvClockAction>> {
3213 tube.send(&command)
3214 .with_context(|| format!("failed to send pvclock command {command:?}"))?;
3215 let resp = tube
3216 .recv::<PvClockCommandResponse>()
3217 .context("failed to receive pvclock command response")?;
3218 match resp {
3219 PvClockCommandResponse::Err(e) => {
3220 bail!("pvclock encountered error on {:?}: {}", command, e);
3221 }
3222 PvClockCommandResponse::DeviceInactive => {
3223 warn!("Tried to send {command:?} but pvclock device was inactive");
3224 Ok(None)
3225 }
3226 PvClockCommandResponse::Resumed {
3227 total_suspended_ticks,
3228 } => {
3229 info!("{command:?} completed with {total_suspended_ticks} total_suspended_ticks");
3230 cfg_if::cfg_if! {
3231 if #[cfg(target_arch = "aarch64")] {
3232 Ok(Some(PvClockAction::SetCounterOffset(total_suspended_ticks)))
3233 } else {
3234 Ok(None)
3237 }
3238 }
3239 }
3240 PvClockCommandResponse::Ok => {
3241 info!("{command:?} completed with {resp:?}");
3242 Ok(None)
3243 }
3244 }
3245}
3246
3247#[cfg(target_arch = "x86_64")]
3248fn handle_hotplug_command(
3249 linux: &mut RunnableLinuxVm,
3250 sys_allocator: &mut SystemAllocator,
3251 cfg: &Config,
3252 add_control_tube: &mut impl FnMut(AnyControlTube),
3253 hp_control_tube: &mpsc::Sender<PciRootCommand>,
3254 iommu_host_tube: Option<&Tube>,
3255 device: &HotPlugDeviceInfo,
3256 add: bool,
3257 #[cfg(feature = "swap")] swap_controller: &mut Option<SwapController>,
3258 vfio_container_manager: &mut VfioContainerManager,
3259) -> VmResponse {
3260 let iommu_host_tube = if cfg.vfio_isolate_hotplug {
3261 iommu_host_tube
3262 } else {
3263 None
3264 };
3265
3266 let ret = if add {
3267 add_hotplug_device(
3268 linux,
3269 sys_allocator,
3270 cfg,
3271 add_control_tube,
3272 hp_control_tube,
3273 iommu_host_tube,
3274 device,
3275 #[cfg(feature = "swap")]
3276 swap_controller,
3277 vfio_container_manager,
3278 )
3279 } else {
3280 remove_hotplug_device(linux, sys_allocator, iommu_host_tube, device)
3281 };
3282
3283 match ret {
3284 Ok(()) => VmResponse::Ok,
3285 Err(e) => {
3286 error!("handle_hotplug_command failure: {}", e);
3287 VmResponse::Err(base::Error::new(libc::EINVAL))
3288 }
3289 }
3290}
3291
3292struct ControlLoopState<'a> {
3293 linux: &'a mut RunnableLinuxVm,
3294 cfg: &'a Config,
3295 sys_allocator: &'a Arc<Mutex<SystemAllocator>>,
3296 control_tubes: &'a BTreeMap<usize, TaggedControlTube>,
3297 disk_host_tubes: &'a [Tube],
3298 #[cfg(feature = "audio")]
3299 snd_host_tubes: &'a [Tube],
3300 #[cfg(feature = "gpu")]
3301 gpu_control_tube: Option<&'a Tube>,
3302 #[cfg(feature = "usb")]
3303 usb_control_tube: &'a Tube,
3304 #[cfg(target_arch = "x86_64")]
3305 iommu_host_tube: &'a Option<Arc<Mutex<Tube>>>,
3306 #[cfg(target_arch = "x86_64")]
3307 hp_control_tube: &'a mpsc::Sender<PciRootCommand>,
3308 guest_suspended_cvar: &'a Option<Arc<(Mutex<bool>, Condvar)>>,
3309 #[cfg(feature = "pci-hotplug")]
3310 hotplug_manager: &'a mut Option<PciHotPlugManager>,
3311 #[cfg(feature = "swap")]
3312 swap_controller: &'a mut Option<SwapController>,
3313 vcpu_handles: &'a [(JoinHandle<()>, mpsc::Sender<vm_control::VcpuControl>)],
3314 #[cfg(feature = "balloon")]
3315 balloon_tube: Option<&'a mut BalloonTube>,
3316 device_ctrl_tube: &'a Tube,
3317 irq_handler_control: &'a Tube,
3318 #[cfg(any(target_arch = "x86_64", feature = "pci-hotplug"))]
3319 vm_memory_handler_control: &'a Tube,
3320 #[cfg(feature = "registered_events")]
3321 registered_evt_tubes: &'a mut HashMap<RegisteredEvent, HashSet<AddressedProtoTube>>,
3322 #[cfg(feature = "pvclock")]
3323 pvclock_host_tube: Option<Arc<Tube>>,
3324 vfio_container_manager: &'a mut VfioContainerManager,
3325 suspended_pvclock_state: &'a mut Option<hypervisor::ClockState>,
3326 vcpus_pid_tid: &'a BTreeMap<usize, (u32, u32)>,
3327 vm_memory_control_client: &'a VmMemoryClient,
3328}
3329
3330struct VmRequestResult {
3331 response: Option<VmResponse>,
3332 exit: bool,
3333}
3334
3335impl VmRequestResult {
3336 fn new(response: Option<VmResponse>, exit: bool) -> Self {
3337 VmRequestResult { response, exit }
3338 }
3339}
3340
3341fn process_vm_request(
3342 state: &mut ControlLoopState,
3343 id: usize,
3344 tube: &Tube,
3345 request: VmRequest,
3346 #[cfg_attr(
3347 not(any(target_arch = "x86_64", feature = "pci-hotplug")),
3348 allow(unused_variables, clippy::ptr_arg)
3349 )]
3350 add_tubes: &mut Vec<TaggedControlTube>,
3351) -> Result<VmRequestResult> {
3352 #[cfg(any(target_arch = "x86_64", feature = "pci-hotplug"))]
3353 let mut add_irq_control_tubes = Vec::new();
3354 #[cfg(any(target_arch = "x86_64", feature = "pci-hotplug"))]
3355 let mut add_vm_memory_control_tubes = Vec::new();
3356
3357 #[cfg(any(target_arch = "x86_64", feature = "pci-hotplug"))]
3358 let mut add_control_tube = |t| match t {
3359 AnyControlTube::Balloon(_) => panic!("balloon tube hotplug not supported"),
3360 AnyControlTube::Disk(_) => panic!("disk tube hotplug not supported"),
3361 AnyControlTube::Fs(t) => add_tubes.push(TaggedControlTube::Fs(t)),
3362 AnyControlTube::Gpu(_) => panic!("gpu tube hotplug not supported"),
3363 AnyControlTube::IrqTube(t) => add_irq_control_tubes.push(t),
3364 AnyControlTube::PvClock(_) => panic!("pv-clock tube hotplug not supported"),
3365 AnyControlTube::Snd(_) => panic!("snd tube hotplug not supported"),
3366 AnyControlTube::Vm(t) => add_tubes.push(TaggedControlTube::Vm(t)),
3367 AnyControlTube::VmMemoryTube {
3368 tube,
3369 expose_with_viommu,
3370 remote_peer,
3371 } => add_vm_memory_control_tubes.push(VmMemoryTube {
3372 tube,
3373 expose_with_viommu,
3374 remote_peer,
3375 }),
3376 AnyControlTube::VmMsync(t) => add_tubes.push(TaggedControlTube::VmMsync(t)),
3377 };
3378
3379 let response = match request {
3380 VmRequest::Exit => {
3381 return Ok(VmRequestResult::new(Some(VmResponse::Ok), true));
3382 }
3383 VmRequest::HotPlugVfioCommand { device, add } => {
3384 #[cfg(target_arch = "x86_64")]
3385 {
3386 handle_hotplug_command(
3387 state.linux,
3388 &mut state.sys_allocator.lock(),
3389 state.cfg,
3390 &mut add_control_tube,
3391 state.hp_control_tube,
3392 state.iommu_host_tube.as_ref().map(|t| t.lock()).as_deref(),
3393 &device,
3394 add,
3395 #[cfg(feature = "swap")]
3396 state.swap_controller,
3397 state.vfio_container_manager,
3398 )
3399 }
3400
3401 #[cfg(not(target_arch = "x86_64"))]
3402 {
3403 let _ = (device, add);
3405 let _ = &state.vfio_container_manager;
3406 VmResponse::Ok
3407 }
3408 }
3409 #[cfg(feature = "pci-hotplug")]
3410 VmRequest::HotPlugNetCommand(net_cmd) => {
3411 if let Some(hotplug_manager) = state.hotplug_manager.as_mut() {
3412 handle_hotplug_net_command(
3413 net_cmd,
3414 state.linux,
3415 &mut state.sys_allocator.lock(),
3416 &mut add_control_tube,
3417 hotplug_manager,
3418 )
3419 } else {
3420 VmResponse::ErrString("PCI hotplug is not enabled.".to_owned())
3421 }
3422 }
3423 #[cfg(feature = "registered_events")]
3424 VmRequest::RegisterListener { socket_addr, event } => {
3425 let (registered_tube, already_registered) =
3426 find_registered_tube(state.registered_evt_tubes, &socket_addr, event);
3427
3428 if !already_registered {
3429 let addr_tube = make_addr_tube_from_maybe_existing(registered_tube, socket_addr)?;
3430
3431 if let Some(tubes) = state.registered_evt_tubes.get_mut(&event) {
3432 tubes.insert(addr_tube);
3433 } else {
3434 state
3435 .registered_evt_tubes
3436 .insert(event, vec![addr_tube].into_iter().collect());
3437 }
3438 }
3439 VmResponse::Ok
3440 }
3441 #[cfg(feature = "registered_events")]
3442 VmRequest::UnregisterListener { socket_addr, event } => {
3443 if let Some(tubes) = state.registered_evt_tubes.get_mut(&event) {
3444 tubes.retain(|t| t.socket_addr != socket_addr);
3445 }
3446 state
3447 .registered_evt_tubes
3448 .retain(|_, tubes| !tubes.is_empty());
3449 VmResponse::Ok
3450 }
3451 #[cfg(feature = "registered_events")]
3452 VmRequest::Unregister { socket_addr } => {
3453 for (_, tubes) in state.registered_evt_tubes.iter_mut() {
3454 tubes.retain(|t| t.socket_addr != socket_addr);
3455 }
3456 state
3457 .registered_evt_tubes
3458 .retain(|_, tubes| !tubes.is_empty());
3459 VmResponse::Ok
3460 }
3461 #[cfg(feature = "balloon")]
3462 VmRequest::BalloonCommand(cmd) => {
3463 if let Some(tube) = state.balloon_tube.as_mut() {
3464 let Some((r, key)) = tube.send_cmd(cmd, Some(id)) else {
3465 return Ok(VmRequestResult::new(None, false));
3466 };
3467 if key != id {
3468 let Some(TaggedControlTube::Vm(tube)) = state.control_tubes.get(&key) else {
3469 return Ok(VmRequestResult::new(None, false));
3470 };
3471 if let Err(e) = tube.send(&r) {
3472 error!("failed to send VmResponse: {}", e);
3473 }
3474 return Ok(VmRequestResult::new(None, false));
3475 }
3476 r
3477 } else {
3478 VmResponse::Err(base::Error::new(libc::ENOTSUP))
3479 }
3480 }
3481 VmRequest::VcpuPidTid => VmResponse::VcpuPidTidResponse {
3482 pid_tid_map: state.vcpus_pid_tid.clone(),
3483 },
3484 VmRequest::Throttle(vcpu, cycles) => {
3485 vcpu::kick_vcpu(
3486 &state.vcpu_handles.get(vcpu),
3487 &*state.linux.irq_chip,
3488 VcpuControl::Throttle(cycles),
3489 );
3490 return Ok(VmRequestResult::new(None, false));
3491 }
3492 VmRequest::RegisterMemory {
3493 fd,
3494 offset,
3495 range_start,
3496 range_end,
3497 cache_coherent,
3498 } => {
3499 if range_start >= range_end {
3500 error!("range_start >= range_end");
3501 return Ok(VmRequestResult::new(
3502 Some(VmResponse::Err(base::Error::new(libc::EINVAL))),
3503 false,
3504 ));
3505 }
3506 let source = VmMemorySource::Descriptor {
3507 descriptor: fd,
3508 offset,
3509 size: range_end - range_start,
3510 };
3511 let dest = VmMemoryDestination::GuestPhysicalAddress(range_start);
3512 let cache_type = if cache_coherent {
3513 MemCacheType::CacheCoherent
3514 } else {
3515 MemCacheType::CacheNonCoherent
3516 };
3517 match state.vm_memory_control_client.register_memory(
3518 source,
3519 dest,
3520 Protection::read_write(),
3521 cache_type,
3522 ) {
3523 Ok(region_id) => VmResponse::RegisterMemory2 {
3524 region_id: region_id.0 .0,
3525 },
3526 Err(e) => VmResponse::ErrString(format!("register memory failed: {e:?}")),
3527 }
3528 }
3529 VmRequest::UnregisterMemory { region_id } => {
3530 let mem_region_id = VmMemoryRegionId(GuestAddress(region_id));
3531 match state
3532 .vm_memory_control_client
3533 .unregister_memory(mem_region_id)
3534 {
3535 Ok(_) => VmResponse::Ok,
3536 Err(e) => VmResponse::ErrString(format!("unregister memory failed: {e:?}")),
3537 }
3538 }
3539 _ => {
3540 if !state.cfg.force_s2idle {
3541 #[cfg(feature = "pvclock")]
3542 if let Some(ref pvclock_host_tube) = state.pvclock_host_tube {
3543 if let VmRequest::ResumeVcpus = request {
3545 let cmd = PvClockCommand::Resume;
3546 match send_pvclock_cmd(pvclock_host_tube, cmd.clone()) {
3547 Ok(action) => {
3548 info!("{:?} command successfully processed", cmd);
3549 if let Some(action) = action {
3550 match action {
3551 #[cfg(target_arch = "aarch64")]
3552 PvClockAction::SetCounterOffset(offset) => {
3553 state.linux.vm.set_counter_offset(offset)?;
3554 }
3555 }
3556 }
3557 }
3558 Err(e) => error!("{:?} command failed: {:#}", cmd, e),
3559 };
3560 }
3561 }
3562 }
3563 let kick_all_vcpus = |msg| {
3564 if let VcpuControl::RunState(VmRunMode::Running) = msg {
3565 for dev in &state.linux.resume_notify_devices {
3566 dev.lock().resume_imminent();
3567 }
3568 }
3569 vcpu::kick_all_vcpus(state.vcpu_handles, &*state.linux.irq_chip, msg);
3570 };
3571 let response = request.execute(
3572 &*state.linux.vm,
3573 state.disk_host_tubes,
3574 #[cfg(feature = "audio")]
3575 state.snd_host_tubes,
3576 #[cfg(not(feature = "audio"))]
3577 &[],
3578 &mut state.linux.pm,
3579 #[cfg(feature = "gpu")]
3580 state.gpu_control_tube,
3581 #[cfg(not(feature = "gpu"))]
3582 None,
3583 #[cfg(feature = "usb")]
3584 Some(state.usb_control_tube),
3585 #[cfg(not(feature = "usb"))]
3586 None,
3587 &mut state.linux.bat_control,
3588 kick_all_vcpus,
3589 |index, msg| {
3590 vcpu::kick_vcpu(&state.vcpu_handles.get(index), &*state.linux.irq_chip, msg)
3591 },
3592 state.cfg.force_s2idle,
3593 #[cfg(feature = "swap")]
3594 state.swap_controller.as_ref(),
3595 state.device_ctrl_tube,
3596 state.vcpu_handles.len(),
3597 state.irq_handler_control,
3598 || state.linux.irq_chip.snapshot(state.linux.vcpu_count),
3599 state.suspended_pvclock_state,
3600 );
3601 if state.cfg.force_s2idle {
3602 if let VmRequest::SuspendVcpus = request {
3603 let send_tube = tube.try_clone_send_tube().unwrap();
3605 let suspend_tube = state.linux.suspend_tube.0.clone();
3606 let guest_suspended_cvar = state.guest_suspended_cvar.clone();
3607 let pm = state.linux.pm.clone();
3608
3609 std::thread::Builder::new()
3610 .name("s2idle_wait".to_owned())
3611 .spawn(move || {
3612 trigger_vm_suspend_and_wait_for_entry(
3613 guest_suspended_cvar.unwrap(),
3614 &send_tube,
3615 response,
3616 suspend_tube,
3617 pm,
3618 )
3619 })
3620 .context("failed to spawn s2idle_wait thread")?;
3621
3622 return Ok(VmRequestResult::new(None, false));
3625 }
3626 } else {
3627 #[cfg(feature = "pvclock")]
3628 if let Some(ref pvclock_host_tube) = state.pvclock_host_tube {
3629 if let VmRequest::SuspendVcpus = request {
3631 let cmd = PvClockCommand::Suspend;
3632 match send_pvclock_cmd(pvclock_host_tube, cmd.clone()) {
3633 Ok(action) => {
3634 info!("{:?} command successfully processed", cmd);
3635 if let Some(action) = action {
3636 error!("Unexpected action {:?} requested for suspend", action);
3637 }
3638 }
3639 Err(e) => error!("{:?} command failed: {:#}", cmd, e),
3640 };
3641 }
3642 }
3643 }
3644 response
3645 }
3646 };
3647
3648 cfg_if::cfg_if! {
3649 if #[cfg(any(target_arch = "x86_64", feature = "pci-hotplug"))] {
3650 if !add_irq_control_tubes.is_empty() {
3651 state
3652 .irq_handler_control
3653 .send(&IrqHandlerRequest::AddIrqControlTubes(
3654 add_irq_control_tubes,
3655 ))?;
3656 }
3657 if !add_vm_memory_control_tubes.is_empty() {
3658 state
3659 .vm_memory_handler_control
3660 .send(&VmMemoryHandlerRequest::AddControlTubes(
3661 add_vm_memory_control_tubes,
3662 ))?;
3663 }
3664 }
3665 }
3666
3667 Ok(VmRequestResult::new(Some(response), false))
3668}
3669
3670fn process_vm_control_event(
3671 state: &mut ControlLoopState,
3672 id: usize,
3673 socket: &TaggedControlTube,
3674) -> Result<(bool, Vec<usize>, Vec<TaggedControlTube>)> {
3675 let mut vm_control_ids_to_remove = Vec::new();
3676 let mut add_tubes = Vec::new();
3677 match socket {
3678 TaggedControlTube::Vm(tube) => match tube.recv::<VmRequest>() {
3679 Ok(request) => {
3680 let res = process_vm_request(state, id, tube, request, &mut add_tubes)?;
3681
3682 if let Some(response) = res.response {
3683 if let Err(e) = tube.send(&response) {
3684 error!("failed to send VmResponse: {}", e);
3685 }
3686 }
3687
3688 if res.exit {
3689 return Ok((true, Vec::new(), Vec::new()));
3690 }
3691 }
3692 Err(e) => {
3693 if let TubeError::Disconnected = e {
3694 vm_control_ids_to_remove.push(id);
3695 } else {
3696 error!("failed to recv VmRequest: {}", e);
3697 }
3698 }
3699 },
3700 TaggedControlTube::VmMsync(tube) => match tube.recv::<VmMemoryMappingRequest>() {
3701 Ok(request) => {
3702 let response = request.execute(&*state.linux.vm);
3703 if let Err(e) = tube.send(&response) {
3704 error!("failed to send VmMsyncResponse: {}", e);
3705 }
3706 }
3707 Err(e) => {
3708 if let TubeError::Disconnected = e {
3709 vm_control_ids_to_remove.push(id);
3710 } else {
3711 error!("failed to recv VmMsyncRequest: {}", e);
3712 }
3713 }
3714 },
3715 TaggedControlTube::Fs(tube) => match tube.recv::<FsMappingRequest>() {
3716 Ok(request) => {
3717 let response = request.execute(&*state.linux.vm, &mut state.sys_allocator.lock());
3718 if let Err(e) = tube.send(&response) {
3719 error!("failed to send VmResponse: {}", e);
3720 }
3721 }
3722 Err(e) => {
3723 if let TubeError::Disconnected = e {
3724 vm_control_ids_to_remove.push(id);
3725 } else {
3726 error!("failed to recv VmResponse: {}", e);
3727 }
3728 }
3729 },
3730 }
3731
3732 Ok((false, vm_control_ids_to_remove, add_tubes))
3733}
3734
3735#[cfg(feature = "registered_events")]
3736struct AddressedProtoTube {
3737 tube: Rc<ProtoTube>,
3738 socket_addr: String,
3739}
3740
3741#[cfg(feature = "registered_events")]
3742impl PartialEq for AddressedProtoTube {
3743 fn eq(&self, other: &Self) -> bool {
3744 self.socket_addr == other.socket_addr
3745 }
3746}
3747
3748#[cfg(feature = "registered_events")]
3749impl Eq for AddressedProtoTube {}
3750
3751#[cfg(feature = "registered_events")]
3752impl Hash for AddressedProtoTube {
3753 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
3754 self.socket_addr.hash(state);
3755 }
3756}
3757
3758#[cfg(feature = "registered_events")]
3759impl AddressedProtoTube {
3760 pub fn send<M: protobuf::Message>(&self, msg: &M) -> Result<(), base::TubeError> {
3761 self.tube.send_proto(msg)
3762 }
3763}
3764
3765#[cfg(feature = "registered_events")]
3766fn find_registered_tube<'a>(
3767 registered_tubes: &'a HashMap<RegisteredEvent, HashSet<AddressedProtoTube>>,
3768 socket_addr: &str,
3769 event: RegisteredEvent,
3770) -> (Option<&'a Rc<ProtoTube>>, bool) {
3771 let mut registered_tube: Option<&Rc<ProtoTube>> = None;
3772 let mut already_registered = false;
3773 'outer: for (evt, addr_tubes) in registered_tubes {
3774 for addr_tube in addr_tubes {
3775 if addr_tube.socket_addr == socket_addr {
3776 if *evt == event {
3777 already_registered = true;
3778 break 'outer;
3779 }
3780 registered_tube = Some(&addr_tube.tube);
3786 }
3787 }
3788 }
3789 (registered_tube, already_registered)
3790}
3791
3792#[cfg(feature = "registered_events")]
3793fn make_addr_tube_from_maybe_existing(
3794 tube: Option<&Rc<ProtoTube>>,
3795 addr: String,
3796) -> Result<AddressedProtoTube> {
3797 if let Some(registered_tube) = tube {
3798 Ok(AddressedProtoTube {
3799 tube: registered_tube.clone(),
3800 socket_addr: addr,
3801 })
3802 } else {
3803 let sock = UnixSeqpacket::connect(addr.clone())
3804 .with_context(|| format!("failed to connect to registered listening socket {addr}"))?;
3805 let tube = ProtoTube::from(Tube::try_from(sock)?);
3806 Ok(AddressedProtoTube {
3807 tube: Rc::new(tube),
3808 socket_addr: addr,
3809 })
3810 }
3811}
3812
3813fn run_control(
3814 mut linux: RunnableLinuxVm,
3815 sys_allocator: SystemAllocator,
3816 cfg: Config,
3817 control_server_socket: Option<UnlinkUnixSeqpacketListener>,
3818 all_control_tubes: Vec<AnyControlTube>,
3819 #[cfg(feature = "usb")] usb_control_tube: Tube,
3820 vm_evt_rdtube: RecvTube,
3821 vm_evt_wrtube: SendTube,
3822 sigchld_fd: SignalFd,
3823 #[cfg(feature = "gpu")] gralloc: RutabagaGralloc,
3824 vcpu_ids: Vec<usize>,
3825 iommu_host_tube: Option<Tube>,
3826 #[cfg(target_arch = "x86_64")] hp_control_tube: mpsc::Sender<PciRootCommand>,
3827 #[cfg(target_arch = "x86_64")] hp_thread: std::thread::JoinHandle<()>,
3828 #[cfg(feature = "pci-hotplug")] mut hotplug_manager: Option<PciHotPlugManager>,
3829 #[allow(unused_mut)] #[cfg(feature = "swap")]
3831 mut swap_controller: Option<SwapController>,
3832 #[cfg(feature = "registered_events")] reg_evt_rdtube: RecvTube,
3833 guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
3834 metrics_tube: RecvTube,
3835 mut vfio_container_manager: VfioContainerManager,
3836 mut worker_process_pids: BTreeSet<Pid>,
3838 #[cfg(target_arch = "aarch64")] vcpu_domain_paths: BTreeMap<usize, PathBuf>,
3839) -> Result<ExitState> {
3840 #[cfg(feature = "balloon")]
3842 let mut balloon_host_tube = None;
3843 let mut disk_host_tubes = Vec::new();
3844 #[cfg(feature = "gpu")]
3845 let mut gpu_control_tube = None;
3846 #[cfg(feature = "pvclock")]
3847 let mut pvclock_host_tube = None;
3848 #[cfg(feature = "audio")]
3849 let mut snd_host_tubes = Vec::new();
3850 let mut irq_control_tubes = Vec::new();
3851 let mut vm_memory_control_tubes = Vec::new();
3852 let mut control_tubes = Vec::new();
3853 for t in all_control_tubes {
3854 match t {
3855 #[cfg(feature = "balloon")]
3856 AnyControlTube::Balloon(t) => {
3857 assert!(balloon_host_tube.is_none());
3858 balloon_host_tube = Some(t)
3859 }
3860 #[cfg(not(feature = "balloon"))]
3861 AnyControlTube::Balloon(_) => unreachable!(),
3862 AnyControlTube::Disk(t) => disk_host_tubes.push(t),
3863 AnyControlTube::Fs(t) => control_tubes.push(TaggedControlTube::Fs(t)),
3864 #[cfg(feature = "gpu")]
3865 AnyControlTube::Gpu(t) => {
3866 assert!(gpu_control_tube.is_none());
3867 gpu_control_tube = Some(t)
3868 }
3869 #[cfg(not(feature = "gpu"))]
3870 AnyControlTube::Gpu(_) => unreachable!(),
3871 AnyControlTube::IrqTube(t) => irq_control_tubes.push(t),
3872 #[cfg(feature = "pvclock")]
3873 AnyControlTube::PvClock(t) => {
3874 assert!(pvclock_host_tube.is_none());
3875 pvclock_host_tube = Some(Arc::new(t))
3876 }
3877 #[cfg(not(feature = "pvclock"))]
3878 AnyControlTube::PvClock(_) => unreachable!(),
3879 #[cfg(feature = "audio")]
3880 AnyControlTube::Snd(t) => snd_host_tubes.push(t),
3881 #[cfg(not(feature = "audio"))]
3882 AnyControlTube::Snd(_) => unreachable!(),
3883 AnyControlTube::Vm(t) => control_tubes.push(TaggedControlTube::Vm(t)),
3884 AnyControlTube::VmMemoryTube {
3885 tube,
3886 expose_with_viommu,
3887 remote_peer,
3888 } => vm_memory_control_tubes.push(VmMemoryTube {
3889 tube,
3890 expose_with_viommu,
3891 remote_peer,
3892 }),
3893 AnyControlTube::VmMsync(t) => control_tubes.push(TaggedControlTube::VmMsync(t)),
3894 }
3895 }
3896
3897 #[cfg(feature = "gdb")]
3898 let (to_gdb_channel, gdb) = if let Some(port) = cfg.gdb {
3899 let (gdb_host_tube, gdb_control_tube) = Tube::pair().context("failed to create tube")?;
3901 control_tubes.push(TaggedControlTube::Vm(gdb_host_tube));
3902 let (to_gdb_channel, from_vcpu_channel) = mpsc::channel();
3904 (
3905 Some(to_gdb_channel),
3906 Some((port, gdb_control_tube, from_vcpu_channel)),
3907 )
3908 } else {
3909 (None, None)
3910 };
3911
3912 #[derive(EventToken)]
3913 enum Token {
3914 VmEvent,
3915 Suspend,
3916 ChildSignal,
3917 VmControlServer,
3918 VmControl {
3919 id: usize,
3920 },
3921 #[cfg(feature = "registered_events")]
3922 RegisteredEvent,
3923 #[cfg(feature = "balloon")]
3924 BalloonTube,
3925 }
3926 stdin()
3927 .set_raw_mode()
3928 .expect("failed to set terminal raw mode");
3929
3930 let sys_allocator_mutex = Arc::new(Mutex::new(sys_allocator));
3931 let iommu_host_tube = iommu_host_tube.map(|t| Arc::new(Mutex::new(t)));
3932
3933 let wait_ctx = WaitContext::build_with(&[
3934 (&linux.suspend_tube.1, Token::Suspend),
3935 (&sigchld_fd, Token::ChildSignal),
3936 (&vm_evt_rdtube, Token::VmEvent),
3937 #[cfg(feature = "registered_events")]
3938 (®_evt_rdtube, Token::RegisteredEvent),
3939 ])
3940 .context("failed to build wait context")?;
3941
3942 if let Some(socket_server) = &control_server_socket {
3943 wait_ctx
3944 .add(socket_server, Token::VmControlServer)
3945 .context("failed to add descriptor to wait context")?;
3946 }
3947 let mut control_tubes = BTreeMap::from_iter(control_tubes.into_iter().enumerate());
3948 let mut next_control_id = control_tubes.len();
3949 for (id, socket) in control_tubes.iter() {
3950 wait_ctx
3951 .add(socket.as_ref(), Token::VmControl { id: *id })
3952 .context("failed to add descriptor to wait context")?;
3953 }
3954
3955 #[cfg(feature = "balloon")]
3956 let mut balloon_tube = balloon_host_tube
3957 .map(|tube| -> Result<BalloonTube> {
3958 wait_ctx
3959 .add(&tube, Token::BalloonTube)
3960 .context("failed to add descriptor to wait context")?;
3961 Ok(BalloonTube::new(tube))
3962 })
3963 .transpose()
3964 .context("failed to create balloon tube")?;
3965
3966 if cfg.jail_config.is_some() {
3967 drop_capabilities().context("failed to drop process capabilities")?;
3969 }
3970
3971 let (device_ctrl_tube, device_ctrl_resp) = Tube::pair().context("failed to create tube")?;
3972 linux.devices_thread = match create_devices_worker_thread(
3974 linux.io_bus.clone(),
3975 linux.mmio_bus.clone(),
3976 device_ctrl_resp,
3977 ) {
3978 Ok(join_handle) => Some(join_handle),
3979 Err(e) => {
3980 return Err(anyhow!("Failed to start devices thread: {}", e));
3981 }
3982 };
3983
3984 let mut vcpu_handles = Vec::with_capacity(linux.vcpu_count);
3985 let vcpu_thread_barrier = Arc::new(Barrier::new(linux.vcpu_count + 1));
3986
3987 if !linux
3988 .vm
3989 .get_hypervisor()
3990 .check_capability(HypervisorCap::ImmediateExit)
3991 {
3992 return Err(anyhow!(
3993 "missing required hypervisor capability ImmediateExit"
3994 ));
3995 }
3996
3997 vcpu::setup_vcpu_signal_handler()?;
3998
3999 let vcpus: Vec<Option<_>> = match linux.vcpus.take() {
4000 Some(vec) => vec.into_iter().map(Some).collect(),
4001 None => iter::repeat_with(|| None).take(linux.vcpu_count).collect(),
4002 };
4003 if cfg.core_scheduling && cfg.per_vm_core_scheduling {
4008 if let Err(e) = enable_core_scheduling() {
4009 error!("Failed to enable core scheduling: {}", e);
4010 }
4011 }
4012
4013 let vcpu_cgroup_tasks_file = match &cfg.vcpu_cgroup_path {
4015 None => None,
4016 Some(cgroup_path) => {
4017 match File::create(cgroup_path.join("tasks")) {
4019 Ok(file) => Some(file),
4020 Err(_) => {
4021 info!(
4022 "Unable to open tasks file in cgroup: {}, trying CgroupV2",
4023 cgroup_path.display()
4024 );
4025 None
4026 }
4027 }
4028 }
4029 };
4030
4031 let mut vcpu_cgroup_v2_files: std::collections::BTreeMap<usize, File> = BTreeMap::new();
4033 #[cfg(target_arch = "aarch64")]
4034 for (vcpu_id, vcpu_domain_path) in vcpu_domain_paths.iter() {
4035 let vcpu_cgroup_v2_file = File::create(vcpu_domain_path.join("cgroup.threads"))
4036 .with_context(|| {
4037 format!(
4038 "failed to create vcpu-cgroup-path {}",
4039 vcpu_domain_path.join("cgroup.threads").display(),
4040 )
4041 })?;
4042 vcpu_cgroup_v2_files.insert(*vcpu_id, vcpu_cgroup_v2_file);
4043 }
4044
4045 #[cfg(target_arch = "x86_64")]
4046 let bus_lock_ratelimit_ctrl: Arc<Mutex<Ratelimit>> = Arc::new(Mutex::new(Ratelimit::new()));
4047 #[cfg(target_arch = "x86_64")]
4048 if cfg.bus_lock_ratelimit > 0 {
4049 let bus_lock_ratelimit = cfg.bus_lock_ratelimit;
4050 if linux.vm.check_capability(VmCap::BusLockDetect) {
4051 info!("Hypervisor support bus lock detect");
4052 linux
4053 .vm
4054 .enable_capability(VmCap::BusLockDetect, 0)
4055 .expect("kvm: Failed to enable bus lock detection cap");
4056 info!("Hypervisor enabled bus lock detect");
4057 bus_lock_ratelimit_ctrl
4058 .lock()
4059 .ratelimit_set_speed(bus_lock_ratelimit);
4060 } else {
4061 bail!("Kvm: bus lock detection unsuported");
4062 }
4063 }
4064
4065 #[cfg(target_os = "android")]
4066 android::set_process_profiles(&cfg.task_profiles)?;
4067
4068 #[allow(unused_mut)]
4069 let mut run_mode = if cfg.suspended {
4070 device_ctrl_tube
4072 .send(&DeviceControlCommand::SleepDevices)
4073 .context("send command to devices control socket")?;
4074 match device_ctrl_tube
4075 .recv()
4076 .context("receive from devices control socket")?
4077 {
4078 VmResponse::Ok => (),
4079 resp => bail!("device sleep failed: {}", resp),
4080 }
4081 VmRunMode::Suspending
4082 } else if cfg.suspended_vcpus {
4083 VmRunMode::Suspending
4084 } else {
4085 VmRunMode::Running
4086 };
4087 #[cfg(feature = "gdb")]
4088 if to_gdb_channel.is_some() {
4089 run_mode = VmRunMode::Breakpoint;
4091 }
4092 let (run_mode, post_restore_run_mode) = if cfg.restore_path.is_some() {
4094 (VmRunMode::Suspending, run_mode)
4095 } else {
4096 (run_mode, run_mode)
4097 };
4098
4099 assert_eq!(vcpus.len(), linux.vcpu_init.len());
4101
4102 let (vcpu_pid_tid_sender, vcpu_pid_tid_receiver) = mpsc::channel();
4103 for ((cpu_id, vcpu), vcpu_init) in vcpus.into_iter().enumerate().zip(linux.vcpu_init.drain(..))
4104 {
4105 let vcpu_cgroup_file: Option<File>;
4106 if let Some(cgroup_file) = &vcpu_cgroup_tasks_file {
4107 vcpu_cgroup_file = Some(cgroup_file.try_clone().unwrap())
4108 } else if !cfg.cpu_freq_domains.is_empty() {
4109 vcpu_cgroup_file = Some(
4110 (vcpu_cgroup_v2_files.remove(&cpu_id).unwrap())
4111 .try_clone()
4112 .unwrap(),
4113 )
4114 } else {
4115 vcpu_cgroup_file = None
4116 };
4117
4118 let (to_vcpu_channel, from_main_channel) = mpsc::channel();
4119 let vcpu_affinity = match &linux.vcpu_affinity {
4120 Some(VcpuAffinity::Global(v)) => v.clone(),
4121 Some(VcpuAffinity::PerVcpu(m)) => m.get(&cpu_id).cloned().unwrap_or_default(),
4122 None => Default::default(),
4123 };
4124
4125 #[cfg(target_arch = "x86_64")]
4126 let vcpu_hybrid_type = if !cfg.vcpu_hybrid_type.is_empty() {
4127 Some(*cfg.vcpu_hybrid_type.get(&cpu_id).unwrap())
4128 } else {
4129 None
4130 };
4131
4132 #[cfg(target_arch = "x86_64")]
4133 let cpu_config = Some(CpuConfigX86_64::new(
4134 cfg.force_calibrated_tsc_leaf,
4135 cfg.host_cpu_topology,
4136 cfg.enable_hwp,
4137 cfg.no_smt,
4138 cfg.itmt,
4139 vcpu_hybrid_type,
4140 cfg.nested.mode,
4141 ));
4142 #[cfg(target_arch = "x86_64")]
4143 let bus_lock_ratelimit_ctrl = Arc::clone(&bus_lock_ratelimit_ctrl);
4144
4145 #[cfg(target_arch = "aarch64")]
4146 let cpu_config = None;
4147
4148 #[cfg(target_arch = "riscv64")]
4149 let cpu_config = Some(CpuConfigRiscv64::new(vcpu_init.fdt_address));
4150
4151 let handle = vcpu::run_vcpu(
4152 cpu_id,
4153 vcpu_ids[cpu_id],
4154 vcpu,
4155 vcpu_init,
4156 linux.vm.clone(),
4157 linux.irq_chip.clone(),
4158 linux.vcpu_count,
4159 linux.rt_cpus.contains(&cpu_id),
4160 vcpu_affinity,
4161 linux.delay_rt,
4162 vcpu_thread_barrier.clone(),
4163 (*linux.io_bus).clone(),
4164 (*linux.mmio_bus).clone(),
4165 (*linux.hypercall_bus).clone(),
4166 vm_evt_wrtube
4167 .try_clone()
4168 .context("failed to clone vm event tube")?,
4169 from_main_channel,
4170 #[cfg(feature = "gdb")]
4171 to_gdb_channel.clone(),
4172 cfg.core_scheduling,
4173 cfg.per_vm_core_scheduling,
4174 cpu_config,
4175 match vcpu_cgroup_file {
4176 None => None,
4177 Some(ref f) => Some(
4178 f.try_clone()
4179 .context("failed to clone vcpu cgroup tasks file")?,
4180 ),
4181 },
4182 #[cfg(target_arch = "x86_64")]
4183 bus_lock_ratelimit_ctrl,
4184 run_mode,
4185 cfg.boost_uclamp,
4186 vcpu_pid_tid_sender.clone(),
4187 )?;
4188 vcpu_handles.push((handle, to_vcpu_channel));
4189 }
4190
4191 let mut vcpus_pid_tid = BTreeMap::new();
4192 for _ in 0..vcpu_handles.len() {
4193 let vcpu_pid_tid: VcpuPidTid = vcpu_pid_tid_receiver
4194 .recv()
4195 .context("failed receiving vcpu pid/tid")?;
4196 if vcpus_pid_tid
4197 .insert(
4198 vcpu_pid_tid.vcpu_id,
4199 (vcpu_pid_tid.process_id, vcpu_pid_tid.thread_id),
4200 )
4201 .is_some()
4202 {
4203 return Err(anyhow!(
4204 "Vcpu {} returned more than 1 PID and TID",
4205 vcpu_pid_tid.vcpu_id
4206 ));
4207 }
4208 }
4209
4210 #[cfg(feature = "gdb")]
4211 if let Some((gdb_port_num, gdb_control_tube, from_vcpu_channel)) = gdb {
4213 let to_vcpu_channels = vcpu_handles
4214 .iter()
4215 .map(|(_handle, channel)| channel.clone())
4216 .collect();
4217 let target = GdbStub::new(gdb_control_tube, to_vcpu_channels, from_vcpu_channel);
4218 std::thread::Builder::new()
4219 .name("gdb".to_owned())
4220 .spawn(move || gdb_thread(target, gdb_port_num))
4221 .context("failed to spawn GDB thread")?;
4222 };
4223
4224 let (irq_handler_control, irq_handler_control_for_thread) = Tube::pair()?;
4225 let sys_allocator_for_thread = sys_allocator_mutex.clone();
4226 let irq_chip_for_thread = linux.irq_chip.clone();
4227 let irq_handler_thread = std::thread::Builder::new()
4228 .name("irq_handler_thread".into())
4229 .spawn(move || {
4230 irq_handler_thread(
4231 irq_control_tubes,
4232 irq_chip_for_thread,
4233 sys_allocator_for_thread,
4234 irq_handler_control_for_thread,
4235 )
4236 })
4237 .unwrap();
4238
4239 let (vm_memory_control_tube1, vm_memory_control_tube_2) = Tube::pair()?;
4240 vm_memory_control_tubes.push(VmMemoryTube {
4241 tube: vm_memory_control_tube1,
4242 expose_with_viommu: false,
4243 remote_peer: false,
4244 });
4245 let vm_memory_control_client = VmMemoryClient::new(vm_memory_control_tube_2);
4246 let (vm_memory_handler_control, vm_memory_handler_control_for_thread) = Tube::pair()?;
4247 let vm_memory_handler_thread = std::thread::Builder::new()
4248 .name("vm_memory_handler_thread".into())
4249 .spawn({
4250 let vm = linux.vm.clone();
4251 let sys_allocator_mutex = sys_allocator_mutex.clone();
4252 let iommu_client = iommu_host_tube
4253 .as_ref()
4254 .map(|t| VmMemoryRequestIommuClient::new(t.clone()));
4255 move || {
4256 vm_memory_handler_thread(
4257 vm_memory_control_tubes,
4258 vm,
4259 sys_allocator_mutex,
4260 #[cfg(feature = "gpu")]
4261 gralloc,
4262 iommu_client,
4263 vm_memory_handler_control_for_thread,
4264 )
4265 }
4266 })
4267 .unwrap();
4268
4269 vcpu_thread_barrier.wait();
4270
4271 let mut suspended_pvclock_state: Option<hypervisor::ClockState> = None;
4273
4274 if let Some(path) = &cfg.restore_path {
4277 vm_control::do_restore(
4278 path,
4279 |msg| vcpu::kick_all_vcpus(&vcpu_handles, &*linux.irq_chip, msg),
4280 |msg, index| vcpu::kick_vcpu(&vcpu_handles.get(index), &*linux.irq_chip, msg),
4281 &irq_handler_control,
4282 &device_ctrl_tube,
4283 linux.vcpu_count,
4284 |image| linux.irq_chip.restore(image, linux.vcpu_count),
4285 false,
4286 &mut suspended_pvclock_state,
4287 &*linux.vm,
4288 )?;
4289 vcpu::kick_all_vcpus(
4291 &vcpu_handles,
4292 &*linux.irq_chip,
4293 VcpuControl::RunState(post_restore_run_mode),
4294 )
4295 }
4296
4297 #[cfg(feature = "swap")]
4298 if let Some(swap_controller) = &swap_controller {
4299 swap_controller
4300 .on_static_devices_setup_complete()
4301 .context("static device setup complete")?;
4302 }
4303
4304 let metrics_thread = if metrics::is_initialized() {
4305 Some(
4306 std::thread::Builder::new()
4307 .name("metrics_thread".into())
4308 .spawn(move || {
4309 if let Err(e) = MetricsController::new(vec![metrics_tube]).run() {
4310 error!("Metrics controller error: {:?}", e);
4311 }
4312 })
4313 .context("metrics thread failed")?,
4314 )
4315 } else {
4316 None
4317 };
4318
4319 let mut exit_state = ExitState::Stop;
4320 let mut pvpanic_code = PvPanicCode::Unknown;
4321 #[cfg(feature = "registered_events")]
4322 let mut registered_evt_tubes: HashMap<RegisteredEvent, HashSet<AddressedProtoTube>> =
4323 HashMap::new();
4324
4325 'wait: loop {
4326 let events = {
4327 match wait_ctx.wait() {
4328 Ok(v) => v,
4329 Err(e) => {
4330 error!("failed to poll: {}", e);
4331 break;
4332 }
4333 }
4334 };
4335
4336 let mut vm_control_ids_to_remove = Vec::new();
4337 for event in events.iter().filter(|e| e.is_readable) {
4338 match event.token {
4339 #[cfg(feature = "registered_events")]
4340 Token::RegisteredEvent => match reg_evt_rdtube.recv::<RegisteredEventWithData>() {
4341 Ok(reg_evt) => {
4342 let evt = reg_evt.into_event();
4343 let mut tubes_to_remove: Vec<String> = Vec::new();
4344 if let Some(tubes) = registered_evt_tubes.get_mut(&evt) {
4345 for tube in tubes.iter() {
4346 if let Err(e) = tube.send(®_evt.into_proto()) {
4347 warn!(
4348 "failed to send registered event {:?} to {}, removing from \
4349 registrations: {}",
4350 reg_evt, tube.socket_addr, e
4351 );
4352 tubes_to_remove.push(tube.socket_addr.clone());
4353 }
4354 }
4355 }
4356 for tube_addr in tubes_to_remove {
4357 for tubes in registered_evt_tubes.values_mut() {
4358 tubes.retain(|t| t.socket_addr != tube_addr);
4359 }
4360 }
4361 registered_evt_tubes.retain(|_, tubes| !tubes.is_empty());
4362 }
4363 Err(e) => {
4364 warn!("failed to recv RegisteredEvent: {}", e);
4365 }
4366 },
4367 Token::VmEvent => {
4368 let mut break_to_wait: bool = true;
4369 match vm_evt_rdtube.recv::<VmEventType>() {
4370 Ok(vm_event) => match vm_event {
4371 VmEventType::Exit => {
4372 info!("vcpu requested shutdown");
4373 exit_state = ExitState::Stop;
4374 }
4375 VmEventType::Reset => {
4376 info!("vcpu requested reset");
4377 exit_state = ExitState::Reset;
4378 }
4379 VmEventType::Crash => {
4380 info!("vcpu crashed");
4381 exit_state = ExitState::Crash;
4382 }
4383 VmEventType::GuestPanic => {
4384 info!("guest panic event");
4385 exit_state = ExitState::GuestPanic;
4386 }
4387 VmEventType::DeviceCrashed => {
4388 info!("device crashed");
4389 exit_state = ExitState::Crash;
4390 }
4391 VmEventType::Panic(panic_code) => {
4392 pvpanic_code = PvPanicCode::from_u8(panic_code);
4393 info!("Guest reported panic [Code: {}]", pvpanic_code);
4394 break_to_wait = false;
4395 }
4396 VmEventType::WatchdogReset => {
4397 info!("vcpu stall detected");
4398 exit_state = ExitState::WatchdogReset;
4399 }
4400 },
4401 Err(e) => {
4402 warn!("failed to recv VmEvent: {}", e);
4403 }
4404 }
4405 if break_to_wait {
4406 if pvpanic_code == PvPanicCode::Panicked {
4407 exit_state = ExitState::GuestPanic;
4408 }
4409 break 'wait;
4410 }
4411 }
4412 Token::Suspend => match linux.suspend_tube.1.recv::<bool>() {
4413 Ok(is_suspend_request) => {
4414 let mode = if is_suspend_request {
4415 VmRunMode::Suspending
4416 } else {
4417 for dev in &linux.resume_notify_devices {
4418 dev.lock().resume_imminent();
4419 }
4420 VmRunMode::Running
4421 };
4422 info!("VM requested {}", mode);
4423 vcpu::kick_all_vcpus(
4424 &vcpu_handles,
4425 &*linux.irq_chip,
4426 VcpuControl::RunState(mode),
4427 );
4428 }
4429 Err(err) => {
4430 warn!("Failed to read suspend tube {:?}", err);
4431 }
4432 },
4433 Token::ChildSignal => {
4434 let mut do_exit = false;
4438 while let Some(siginfo) =
4439 sigchld_fd.read().context("failed to read signalfd")?
4440 {
4441 let pid = siginfo.ssi_pid;
4442 let pid_label = match linux.pid_debug_label_map.get(&pid) {
4443 Some(label) => format!("{label} (pid {pid})"),
4444 None => format!("pid {pid}"),
4445 };
4446
4447 #[cfg(feature = "swap")]
4449 if siginfo.ssi_code == libc::CLD_STOPPED
4450 || siginfo.ssi_code == libc::CLD_CONTINUED
4451 {
4452 continue;
4453 }
4454
4455 if cfg.jail_config.is_none()
4462 && !linux.pid_debug_label_map.contains_key(&pid)
4463 && siginfo.ssi_signo == libc::SIGCHLD as u32
4464 && siginfo.ssi_code == libc::CLD_EXITED
4465 && siginfo.ssi_status == 0
4466 {
4467 continue;
4468 }
4469
4470 if siginfo.ssi_signo == libc::SIGCHLD as u32
4472 && siginfo.ssi_code == libc::CLD_EXITED
4473 && siginfo.ssi_status == 0
4474 && worker_process_pids.remove(&(pid as Pid))
4475 {
4476 info!("child {pid} exited successfully");
4477 continue;
4478 }
4479
4480 if siginfo.ssi_signo == libc::SIGCHLD as u32
4481 && (siginfo.ssi_code == libc::CLD_KILLED
4482 || siginfo.ssi_code == libc::CLD_DUMPED)
4483 {
4484 error!(
4485 "child {} killed by signal {} ({})",
4486 pid_label,
4487 siginfo.ssi_status,
4488 base::signal::Signal::try_from(siginfo.ssi_status)
4489 .map(|s| s.to_string())
4490 .unwrap_or("unknown".to_string()),
4491 );
4492 } else {
4493 error!(
4494 "child {} exited: signo {}, status {}, code {}",
4495 pid_label, siginfo.ssi_signo, siginfo.ssi_status, siginfo.ssi_code
4496 );
4497 }
4498 do_exit = true;
4499 }
4500 if do_exit {
4501 exit_state = ExitState::Crash;
4502 break 'wait;
4503 }
4504 }
4505 Token::VmControlServer => {
4506 if let Some(socket_server) = &control_server_socket {
4507 match socket_server.accept() {
4508 Ok(socket) => {
4509 let id = next_control_id;
4510 next_control_id += 1;
4511 wait_ctx
4512 .add(&socket, Token::VmControl { id })
4513 .context("failed to add descriptor to wait context")?;
4514 control_tubes
4515 .insert(id, TaggedControlTube::Vm(Tube::try_from(socket)?));
4516 }
4517 Err(e) => error!("failed to accept socket: {}", e),
4518 }
4519 }
4520 }
4521 Token::VmControl { id } => {
4522 if let Some(socket) = control_tubes.get(&id) {
4523 let mut state = ControlLoopState {
4524 linux: &mut linux,
4525 cfg: &cfg,
4526 sys_allocator: &sys_allocator_mutex,
4527 control_tubes: &control_tubes,
4528 disk_host_tubes: &disk_host_tubes[..],
4529 #[cfg(feature = "audio")]
4530 snd_host_tubes: &snd_host_tubes[..],
4531 #[cfg(feature = "gpu")]
4532 gpu_control_tube: gpu_control_tube.as_ref(),
4533 #[cfg(feature = "usb")]
4534 usb_control_tube: &usb_control_tube,
4535 #[cfg(target_arch = "x86_64")]
4536 iommu_host_tube: &iommu_host_tube,
4537 #[cfg(target_arch = "x86_64")]
4538 hp_control_tube: &hp_control_tube,
4539 guest_suspended_cvar: &guest_suspended_cvar,
4540 #[cfg(feature = "pci-hotplug")]
4541 hotplug_manager: &mut hotplug_manager,
4542 #[cfg(feature = "swap")]
4543 swap_controller: &mut swap_controller,
4544 vcpu_handles: &vcpu_handles,
4545 #[cfg(feature = "balloon")]
4546 balloon_tube: balloon_tube.as_mut(),
4547 device_ctrl_tube: &device_ctrl_tube,
4548 irq_handler_control: &irq_handler_control,
4549 #[cfg(any(target_arch = "x86_64", feature = "pci-hotplug"))]
4550 vm_memory_handler_control: &vm_memory_handler_control,
4551 #[cfg(feature = "registered_events")]
4552 registered_evt_tubes: &mut registered_evt_tubes,
4553 #[cfg(feature = "pvclock")]
4554 pvclock_host_tube: pvclock_host_tube.clone(),
4555 vfio_container_manager: &mut vfio_container_manager,
4556 suspended_pvclock_state: &mut suspended_pvclock_state,
4557 vcpus_pid_tid: &vcpus_pid_tid,
4558 vm_memory_control_client: &vm_memory_control_client,
4559 };
4560 let (exit_requested, mut ids_to_remove, add_tubes) =
4561 process_vm_control_event(&mut state, id, socket)?;
4562 if exit_requested {
4563 break 'wait;
4564 }
4565 vm_control_ids_to_remove.append(&mut ids_to_remove);
4566 for socket in add_tubes {
4567 let id = next_control_id;
4568 next_control_id += 1;
4569 wait_ctx
4570 .add(socket.as_ref(), Token::VmControl { id })
4571 .context(
4572 "failed to add hotplug vfio-pci descriptor to wait context",
4573 )?;
4574 control_tubes.insert(id, socket);
4575 }
4576 }
4577 }
4578 #[cfg(feature = "balloon")]
4579 Token::BalloonTube => {
4580 match balloon_tube.as_mut().expect("missing balloon tube").recv() {
4581 Ok(resp) => {
4582 for (resp, idx) in resp {
4583 if let Some(TaggedControlTube::Vm(tube)) = control_tubes.get(&idx) {
4584 if let Err(e) = tube.send(&resp) {
4585 error!("failed to send VmResponse: {}", e);
4586 }
4587 } else {
4588 error!("Bad tube index {}", idx);
4589 }
4590 }
4591 }
4592 Err(err) => {
4593 error!("Error processing balloon tube {:?}", err)
4594 }
4595 }
4596 }
4597 }
4598 }
4599
4600 remove_hungup_and_drained_tubes(
4601 &events,
4602 &wait_ctx,
4603 &mut control_tubes,
4604 vm_control_ids_to_remove,
4605 |token: &Token| {
4606 if let Token::VmControl { id } = token {
4607 return Some(*id);
4608 }
4609 None
4610 },
4611 )?;
4612 }
4613
4614 vcpu::kick_all_vcpus(
4615 &vcpu_handles,
4616 &*linux.irq_chip,
4617 VcpuControl::RunState(VmRunMode::Exiting),
4618 );
4619 for (handle, _) in vcpu_handles {
4620 if let Err(e) = handle.join() {
4621 error!("failed to join vcpu thread: {:?}", e);
4622 }
4623 }
4624
4625 if let Err(e) = vcpu::remove_vcpu_signal_handler() {
4627 error!("failed to remove vcpu thread signal handler: {:#}", e);
4628 }
4629
4630 #[cfg(feature = "swap")]
4632 drop(swap_controller);
4633
4634 #[cfg(target_arch = "x86_64")]
4636 {
4637 let _ = hp_control_tube.send(PciRootCommand::Kill);
4638 if let Err(e) = hp_thread.join() {
4639 error!("failed to join hotplug thread: {:?}", e);
4640 }
4641 }
4642
4643 if linux.devices_thread.is_some() {
4644 if let Err(e) = device_ctrl_tube.send(&DeviceControlCommand::Exit) {
4645 error!("failed to stop device control loop: {}", e);
4646 };
4647 if let Some(thread) = linux.devices_thread.take() {
4648 if let Err(e) = thread.join() {
4649 error!("failed to exit devices thread: {:?}", e);
4650 }
4651 }
4652 }
4653
4654 match Arc::try_unwrap(std::mem::replace(
4659 &mut linux.mmio_bus,
4660 Arc::new(Bus::new(BusType::Mmio)),
4661 )) {
4662 Ok(_) => {}
4663 Err(_) => panic!("internal error: mmio_bus had more than one reference at shutdown"),
4664 }
4665 match Arc::try_unwrap(std::mem::replace(
4666 &mut linux.io_bus,
4667 Arc::new(Bus::new(BusType::Io)),
4668 )) {
4669 Ok(_) => {}
4670 Err(_) => panic!("internal error: io_bus had more than one reference at shutdown"),
4671 }
4672
4673 mem::drop(linux);
4676
4677 if let Err(e) = vm_memory_handler_control.send(&VmMemoryHandlerRequest::Exit) {
4682 error!(
4683 "failed to request exit from VM Memory handler thread: {}",
4684 e
4685 );
4686 }
4687 if let Err(e) = vm_memory_handler_thread.join() {
4688 error!("failed to exit VM Memory handler thread: {:?}", e);
4689 }
4690
4691 if let Err(e) = irq_handler_control.send(&IrqHandlerRequest::Exit) {
4693 error!("failed to request exit from IRQ handler thread: {}", e);
4694 }
4695 if let Err(e) = irq_handler_thread.join() {
4696 error!("failed to exit irq handler thread: {:?}", e);
4697 }
4698
4699 #[cfg(feature = "pci-hotplug")]
4702 mem::drop(hotplug_manager);
4703
4704 metrics::get_destructor().cleanup();
4707 if let Some(metrics_thread) = metrics_thread {
4708 if let Err(e) = metrics_thread.join() {
4709 error!("failed to exit irq handler thread: {:?}", e);
4710 }
4711 }
4712
4713 stdin()
4714 .set_canon_mode()
4715 .expect("failed to restore canonical mode for terminal");
4716
4717 Ok(exit_state)
4718}
4719
4720#[derive(EventToken)]
4721enum IrqHandlerToken {
4722 IrqFd { index: IrqEventIndex },
4723 VmIrq { id: usize },
4724 DelayedIrqFd,
4725 HandlerControl,
4726}
4727
4728fn irq_handler_thread(
4730 irq_control_tubes: Vec<Tube>,
4731 irq_chip: Arc<dyn IrqChipArch>,
4732 sys_allocator_mutex: Arc<Mutex<SystemAllocator>>,
4733 handler_control: Tube,
4734) -> anyhow::Result<()> {
4735 let wait_ctx = WaitContext::build_with(&[(
4736 handler_control.get_read_notifier(),
4737 IrqHandlerToken::HandlerControl,
4738 )])
4739 .context("failed to build wait context")?;
4740
4741 if let Some(delayed_ioapic_irq_trigger) = irq_chip.irq_delayed_event_token()? {
4742 wait_ctx
4743 .add(&delayed_ioapic_irq_trigger, IrqHandlerToken::DelayedIrqFd)
4744 .context("failed to add descriptor to wait context")?;
4745 }
4746
4747 let mut irq_event_tokens = irq_chip
4748 .irq_event_tokens()
4749 .context("failed get event tokens from irqchip")?;
4750
4751 for (index, _gsi, evt) in irq_event_tokens.iter() {
4752 wait_ctx
4753 .add(evt, IrqHandlerToken::IrqFd { index: *index })
4754 .context("failed to add irq chip event tokens to wait context")?;
4755 }
4756
4757 let mut irq_control_tubes = BTreeMap::from_iter(irq_control_tubes.into_iter().enumerate());
4758 let mut next_control_id = irq_control_tubes.len();
4759 for (id, socket) in irq_control_tubes.iter() {
4760 wait_ctx
4761 .add(
4762 socket.get_read_notifier(),
4763 IrqHandlerToken::VmIrq { id: *id },
4764 )
4765 .context("irq control tubes to wait context")?;
4766 }
4767
4768 'wait: loop {
4769 let events = {
4770 match wait_ctx.wait() {
4771 Ok(v) => v,
4772 Err(e) => {
4773 error!("failed to poll: {}", e);
4774 break 'wait;
4775 }
4776 }
4777 };
4778 let token_count = events.len();
4779 let mut vm_irq_tubes_to_remove = Vec::new();
4780 let mut notify_control_on_iteration_end = false;
4781
4782 for event in events.iter().filter(|e| e.is_readable) {
4783 match event.token {
4784 IrqHandlerToken::HandlerControl => {
4785 match handler_control.recv::<IrqHandlerRequest>() {
4786 Ok(request) => {
4787 match request {
4788 IrqHandlerRequest::Exit => break 'wait,
4789 IrqHandlerRequest::AddIrqControlTubes(tubes) => {
4790 for socket in tubes {
4791 let id = next_control_id;
4792 next_control_id += 1;
4793 wait_ctx
4794 .add(
4795 socket.get_read_notifier(),
4796 IrqHandlerToken::VmIrq { id },
4797 )
4798 .context("failed to add new IRQ control Tube to wait context")?;
4799 irq_control_tubes.insert(id, socket);
4800 }
4801 }
4802 IrqHandlerRequest::RefreshIrqEventTokens => {
4803 for (_index, _gsi, evt) in irq_event_tokens.iter() {
4804 wait_ctx.delete(evt).context(
4805 "failed to remove irq chip event \
4806 token from wait context",
4807 )?;
4808 }
4809
4810 irq_event_tokens = irq_chip
4811 .irq_event_tokens()
4812 .context("failed get event tokens from irqchip")?;
4813 for (index, _gsi, evt) in irq_event_tokens.iter() {
4814 wait_ctx
4815 .add(evt, IrqHandlerToken::IrqFd { index: *index })
4816 .context(
4817 "failed to add irq chip event \
4818 tokens to wait context",
4819 )?;
4820 }
4821
4822 if let Err(e) = handler_control
4823 .send(&IrqHandlerResponse::IrqEventTokenRefreshComplete)
4824 {
4825 error!(
4826 "failed to notify IRQ event token refresh \
4827 was completed: {}",
4828 e
4829 );
4830 }
4831 }
4832 IrqHandlerRequest::WakeAndNotifyIteration => {
4833 notify_control_on_iteration_end = true;
4834 }
4835 }
4836 }
4837 Err(e) => {
4838 if let TubeError::Disconnected = e {
4839 panic!("irq handler control tube disconnected.");
4840 } else {
4841 error!("failed to recv IrqHandlerRequest: {}", e);
4842 }
4843 }
4844 }
4845 }
4846 IrqHandlerToken::VmIrq { id } => {
4847 if let Some(tube) = irq_control_tubes.get(&id) {
4848 handle_irq_tube_request(
4849 &sys_allocator_mutex,
4850 &*irq_chip,
4851 &mut vm_irq_tubes_to_remove,
4852 &wait_ctx,
4853 tube,
4854 id,
4855 );
4856 }
4857 }
4858 IrqHandlerToken::IrqFd { index } => {
4859 if let Err(e) = irq_chip.service_irq_event(index) {
4860 error!("failed to signal irq {}: {}", index, e);
4861 }
4862 }
4863 IrqHandlerToken::DelayedIrqFd => {
4864 if let Err(e) = irq_chip.process_delayed_irq_events() {
4865 warn!("can't deliver delayed irqs: {}", e);
4866 }
4867 }
4868 }
4869 }
4870
4871 if notify_control_on_iteration_end {
4872 if let Err(e) = handler_control.send(&IrqHandlerResponse::HandlerIterationComplete(
4873 token_count - 1,
4874 )) {
4875 error!(
4876 "failed to notify on iteration completion (snapshotting may fail): {}",
4877 e
4878 );
4879 }
4880 }
4881
4882 remove_hungup_and_drained_tubes(
4883 &events,
4884 &wait_ctx,
4885 &mut irq_control_tubes,
4886 vm_irq_tubes_to_remove,
4887 |token: &IrqHandlerToken| {
4888 if let IrqHandlerToken::VmIrq { id } = token {
4889 return Some(*id);
4890 }
4891 None
4892 },
4893 )?;
4894 if events.iter().any(|e| {
4895 e.is_hungup && !e.is_readable && matches!(e.token, IrqHandlerToken::HandlerControl)
4896 }) {
4897 error!("IRQ handler control hung up but did not request an exit.");
4898 break 'wait;
4899 }
4900 }
4901 Ok(())
4902}
4903
4904fn handle_irq_tube_request(
4905 sys_allocator_mutex: &Arc<Mutex<SystemAllocator>>,
4906 irq_chip: &dyn IrqChipArch,
4907 vm_irq_tubes_to_remove: &mut Vec<usize>,
4908 wait_ctx: &WaitContext<IrqHandlerToken>,
4909 tube: &Tube,
4910 tube_index: usize,
4911) {
4912 match tube.recv::<VmIrqRequest>() {
4913 Ok(request) => {
4914 let response = {
4915 request.execute(
4916 |setup| match setup {
4917 IrqSetup::Event(irq, ev, device_id, queue_id, device_name) => {
4918 let irq_evt = devices::IrqEdgeEvent::from_event(ev.try_clone()?);
4919 let source = IrqEventSource {
4920 device_id,
4921 queue_id,
4922 device_name,
4923 };
4924 if let Some(event_index) =
4925 irq_chip.register_edge_irq_event(irq, &irq_evt, source)?
4926 {
4927 if let Err(e) =
4928 wait_ctx.add(ev, IrqHandlerToken::IrqFd { index: event_index })
4929 {
4930 warn!("failed to add IrqFd to poll context: {}", e);
4931 return Err(e);
4932 }
4933 }
4934 Ok(())
4935 }
4936 IrqSetup::Route(route) => irq_chip.route_irq(route),
4937 IrqSetup::UnRegister(irq, ev) => {
4938 let irq_evt = devices::IrqEdgeEvent::from_event(ev.try_clone()?);
4939 irq_chip.unregister_edge_irq_event(irq, &irq_evt)
4940 }
4941 },
4942 &mut sys_allocator_mutex.lock(),
4943 )
4944 };
4945 if let Err(e) = tube.send(&response) {
4946 error!("failed to send VmIrqResponse: {}", e);
4947 }
4948 }
4949 Err(e) => {
4950 if let TubeError::Disconnected = e {
4951 vm_irq_tubes_to_remove.push(tube_index);
4952 } else {
4953 error!("failed to recv VmIrqRequest: {}", e);
4954 }
4955 }
4956 }
4957}
4958
4959#[derive(serde::Serialize, serde::Deserialize)]
4961pub enum VmMemoryHandlerRequest {
4962 AddControlTubes(Vec<VmMemoryTube>),
4964 Exit,
4966}
4967
4968fn vm_memory_handler_thread(
4969 control_tubes: Vec<VmMemoryTube>,
4970 vm: Arc<dyn Vm>,
4971 sys_allocator_mutex: Arc<Mutex<SystemAllocator>>,
4972 #[cfg(feature = "gpu")] mut gralloc: RutabagaGralloc,
4973 mut iommu_client: Option<VmMemoryRequestIommuClient>,
4974 handler_control: Tube,
4975) -> anyhow::Result<()> {
4976 #[derive(EventToken)]
4977 enum Token {
4978 VmControl { id: usize },
4979 HandlerControl,
4980 }
4981
4982 let wait_ctx =
4983 WaitContext::build_with(&[(handler_control.get_read_notifier(), Token::HandlerControl)])
4984 .context("failed to build wait context")?;
4985 let mut control_tubes = BTreeMap::from_iter(control_tubes.into_iter().enumerate());
4986 let mut next_control_id = control_tubes.len();
4987 for (id, socket) in control_tubes.iter() {
4988 wait_ctx
4989 .add(socket.as_ref(), Token::VmControl { id: *id })
4990 .context("failed to add descriptor to wait context")?;
4991 }
4992
4993 let mut region_state: VmMemoryRegionState = Default::default();
4994
4995 'wait: loop {
4996 let events = {
4997 match wait_ctx.wait() {
4998 Ok(v) => v,
4999 Err(e) => {
5000 error!("failed to poll: {}", e);
5001 break;
5002 }
5003 }
5004 };
5005
5006 let mut vm_control_ids_to_remove = Vec::new();
5007 for event in events.iter().filter(|e| e.is_readable) {
5008 match event.token {
5009 Token::HandlerControl => match handler_control.recv::<VmMemoryHandlerRequest>() {
5010 Ok(request) => match request {
5011 VmMemoryHandlerRequest::Exit => break 'wait,
5012 VmMemoryHandlerRequest::AddControlTubes(tubes) => {
5013 for socket in tubes {
5014 let id = next_control_id;
5015 next_control_id += 1;
5016 wait_ctx
5017 .add(socket.get_read_notifier(), Token::VmControl { id })
5018 .context(
5019 "failed to add new vm memory control Tube to wait context",
5020 )?;
5021 control_tubes.insert(id, socket);
5022 }
5023 }
5024 },
5025 Err(e) => {
5026 if let TubeError::Disconnected = e {
5027 panic!("vm memory control tube disconnected.");
5028 } else {
5029 error!("failed to recv VmMemoryHandlerRequest: {}", e);
5030 }
5031 }
5032 },
5033 Token::VmControl { id } => {
5034 if let Some(VmMemoryTube {
5035 tube,
5036 expose_with_viommu,
5037 remote_peer,
5038 }) = control_tubes.get(&id)
5039 {
5040 match tube.recv::<VmMemoryRequest>() {
5041 Ok(request) => {
5042 let response = request.execute(
5043 tube,
5044 &*vm,
5045 &mut sys_allocator_mutex.lock(),
5046 #[cfg(feature = "gpu")]
5047 &mut gralloc,
5048 if *expose_with_viommu {
5049 iommu_client.as_mut()
5050 } else {
5051 None
5052 },
5053 &mut region_state,
5054 *remote_peer,
5055 );
5056 if let Err(e) = tube.send(&response) {
5057 error!("failed to send VmMemoryControlResponse: {}", e);
5058 }
5059 }
5060 Err(e) => {
5061 if let TubeError::Disconnected = e {
5062 vm_control_ids_to_remove.push(id);
5063 } else {
5064 error!("failed to recv VmMemoryControlRequest: {}", e);
5065 }
5066 }
5067 }
5068 }
5069 }
5070 }
5071 }
5072
5073 remove_hungup_and_drained_tubes(
5074 &events,
5075 &wait_ctx,
5076 &mut control_tubes,
5077 vm_control_ids_to_remove,
5078 |token: &Token| {
5079 if let Token::VmControl { id } = token {
5080 return Some(*id);
5081 }
5082 None
5083 },
5084 )?;
5085 if events
5086 .iter()
5087 .any(|e| e.is_hungup && !e.is_readable && matches!(e.token, Token::HandlerControl))
5088 {
5089 error!("vm memory handler control hung up but did not request an exit.");
5090 break 'wait;
5091 }
5092 }
5093 Ok(())
5094}
5095
5096fn remove_hungup_and_drained_tubes<T, U>(
5103 events: &SmallVec<[TriggeredEvent<T>; 16]>,
5104 wait_ctx: &WaitContext<T>,
5105 tubes: &mut BTreeMap<usize, U>,
5106 mut tube_ids_to_remove: Vec<usize>,
5107 get_tube_id: fn(token: &T) -> Option<usize>,
5108) -> anyhow::Result<()>
5109where
5110 T: EventToken,
5111 U: ReadNotifier,
5112{
5113 for event in events.iter().filter(|e| e.is_hungup && !e.is_readable) {
5122 if let Some(id) = get_tube_id(&event.token) {
5123 tube_ids_to_remove.push(id);
5124 }
5125 }
5126
5127 tube_ids_to_remove.dedup();
5128 for id in tube_ids_to_remove {
5129 if let Some(socket) = tubes.remove(&id) {
5138 wait_ctx
5139 .delete(socket.get_read_notifier())
5140 .context("failed to remove descriptor from wait context")?;
5141 }
5142 }
5143 Ok(())
5144}
5145
5146fn jail_and_start_vu_device<T: VirtioDeviceBuilder>(
5153 jail_config: Option<&JailConfig>,
5154 params: T,
5155 vhost: &str,
5156 name: &str,
5157) -> anyhow::Result<(libc::pid_t, Option<Box<dyn std::any::Any>>)> {
5158 let mut keep_rds = Vec::new();
5159
5160 base::syslog::push_descriptors(&mut keep_rds);
5161 cros_tracing::push_descriptors!(&mut keep_rds);
5162 metrics::push_descriptors(&mut keep_rds);
5163
5164 let jail_type = VirtioDeviceType::VhostUser;
5165
5166 let jail = params
5169 .create_jail(jail_config, jail_type)
5170 .with_context(|| format!("failed to create jail for {name}"))?
5171 .ok_or(())
5172 .or_else(|_| Minijail::new())
5173 .with_context(|| format!("failed to create empty jail for {name}"))?;
5174
5175 let device = params
5178 .create_vhost_user_device(&mut keep_rds)
5179 .context("failed to create vhost-user device")?;
5180 let mut listener =
5181 VhostUserListener::new(vhost).context("failed to create the vhost listener")?;
5182 keep_rds.push(listener.as_raw_descriptor());
5183 let parent_resources = listener.take_parent_process_resources();
5184
5185 let ex = Executor::new().context("Failed to create an Executor")?;
5188 keep_rds.extend(ex.as_raw_descriptors());
5189
5190 keep_rds.sort_unstable();
5192 keep_rds.dedup();
5193
5194 match unsafe { jail.fork(Some(&keep_rds)).context("error while forking")? } {
5197 0 => {
5198 let _ = std::mem::ManuallyDrop::new(parent_resources);
5203
5204 if unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL) } < 0 {
5207 panic!("call to prctl(PR_SET_DEATHSIG, SIGKILL) failed. Aborting child process.");
5208 }
5209
5210 const MAX_LEN: usize = 15; let debug_label_trimmed = &name.as_bytes()[..std::cmp::min(MAX_LEN, name.len())];
5213 let thread_name = CString::new(debug_label_trimmed).unwrap();
5214 let _ = unsafe { libc::pthread_setname_np(libc::pthread_self(), thread_name.as_ptr()) };
5218
5219 let res = match listener.run_device(ex, device) {
5221 Ok(()) => 0,
5222 Err(e) => {
5223 error!("error while running device {}: {:#}", name, e);
5224 1
5225 }
5226 };
5227 unsafe { libc::exit(res) };
5229 }
5230 pid => {
5231 info!("process for device {} (PID {}) started", &name, pid);
5237 #[cfg(feature = "seccomp_trace")]
5238 debug!(
5239 "seccomp_trace {{\"event\": \"minijail_fork\", \"pid\": {}, \"name\": \"{}\", \"jail_addr\": \"0x{:x}\"}}",
5240 pid,
5241 &name,
5242 read_jail_addr(&jail)
5243 );
5244 Ok((pid, parent_resources))
5245 }
5246 }
5247}
5248
5249fn process_vhost_user_control_request(tube: Tube, disk_host_tubes: &[Tube]) -> Result<()> {
5250 let command = tube
5251 .recv::<VmRequest>()
5252 .context("failed to receive VmRequest")?;
5253 let resp = match command {
5254 VmRequest::DiskCommand {
5255 disk_index,
5256 ref command,
5257 } => match &disk_host_tubes.get(disk_index) {
5258 Some(tube) => handle_disk_command(command, tube),
5259 None => VmResponse::Err(base::Error::new(libc::ENODEV)),
5260 },
5261 request => {
5262 error!(
5263 "Request {:?} currently not supported in vhost user backend",
5264 request
5265 );
5266 VmResponse::Err(base::Error::new(libc::EPERM))
5267 }
5268 };
5269
5270 tube.send(&resp).context("failed to send VmResponse")?;
5271 Ok(())
5272}
5273
5274fn start_vhost_user_control_server(
5275 control_server_socket: UnlinkUnixSeqpacketListener,
5276 disk_host_tubes: Vec<Tube>,
5277) {
5278 info!("Start vhost-user control server");
5279 loop {
5280 match control_server_socket.accept() {
5281 Ok(socket) => {
5282 let tube = match Tube::try_from(socket) {
5283 Ok(tube) => tube,
5284 Err(e) => {
5285 error!("failed to open tube: {:#}", e);
5286 return;
5287 }
5288 };
5289 if let Err(e) = process_vhost_user_control_request(tube, &disk_host_tubes) {
5290 error!("failed to process control request: {:#}", e);
5291 }
5292 }
5293 Err(e) => {
5294 error!("failed to establish connection: {}", e);
5295 }
5296 }
5297 }
5298}
5299
5300pub fn start_devices(opts: DevicesCommand) -> anyhow::Result<()> {
5301 if let Some(async_executor) = opts.async_executor {
5302 Executor::set_default_executor_kind(async_executor)
5303 .context("Failed to set the default async executor")?;
5304 }
5305
5306 struct DeviceJailInfo {
5307 name: String,
5309 _drop_resources: Option<Box<dyn std::any::Any>>,
5310 }
5311
5312 fn add_device<T: VirtioDeviceBuilder>(
5313 i: usize,
5314 device_params: T,
5315 vhost: &str,
5316 jail_config: Option<&JailConfig>,
5317 devices_jails: &mut BTreeMap<libc::pid_t, DeviceJailInfo>,
5318 ) -> anyhow::Result<()> {
5319 let name = format!("{}-{}", T::NAME, i);
5320
5321 let (pid, _drop_resources) =
5322 jail_and_start_vu_device::<T>(jail_config, device_params, vhost, &name)?;
5323
5324 devices_jails.insert(
5325 pid,
5326 DeviceJailInfo {
5327 name,
5328 _drop_resources,
5329 },
5330 );
5331
5332 Ok(())
5333 }
5334
5335 let mut devices_jails: BTreeMap<libc::pid_t, DeviceJailInfo> = BTreeMap::new();
5336
5337 let jail = if opts.disable_sandbox {
5338 None
5339 } else {
5340 Some(&opts.jail)
5341 };
5342
5343 let control_server_socket = opts.control_socket.map(|path| {
5345 UnlinkUnixSeqpacketListener(
5346 UnixSeqpacketListener::bind(path).expect("Could not bind socket"),
5347 )
5348 });
5349
5350 for (i, params) in opts.serial.iter().enumerate() {
5352 let serial_config = ¶ms.device;
5353 add_device(i, serial_config, ¶ms.vhost, jail, &mut devices_jails)?;
5354 }
5355
5356 let mut disk_host_tubes = Vec::new();
5357 let control_socket_exists = control_server_socket.is_some();
5358 for (i, params) in opts.block.iter().enumerate() {
5360 let tube = if control_socket_exists {
5361 let (host_tube, device_tube) = Tube::pair().context("failed to create tube")?;
5362 disk_host_tubes.push(host_tube);
5363 Some(device_tube)
5364 } else {
5365 None
5366 };
5367 let disk_config = DiskConfig::new(¶ms.device, tube);
5368 add_device(i, disk_config, ¶ms.vhost, jail, &mut devices_jails)?;
5369 }
5370
5371 for (i, params) in opts.vsock.iter().enumerate() {
5373 add_device(i, ¶ms.device, ¶ms.vhost, jail, &mut devices_jails)?;
5374 }
5375
5376 #[cfg(feature = "net")]
5378 for (i, params) in opts.net.iter().enumerate() {
5379 add_device(i, ¶ms.device, ¶ms.vhost, jail, &mut devices_jails)?;
5380 }
5381
5382 if devices_jails.is_empty() {
5384 let err = DevicesCommand::from_args(
5385 &[&std::env::args().next().unwrap_or(String::from("crosvm"))],
5386 &["--help"],
5387 )
5388 .unwrap_err();
5389 println!("{}", err.output);
5390 return Ok(());
5391 }
5392
5393 if let Some(control_server_socket) = control_server_socket {
5394 std::thread::spawn(move || {
5396 start_vhost_user_control_server(control_server_socket, disk_host_tubes)
5397 });
5398 }
5399
5400 while !devices_jails.is_empty() {
5402 match base::linux::wait_for_pid(-1, 0) {
5403 Err(e) => panic!("error waiting for child process to complete: {e:#}"),
5404 Ok((Some(pid), wait_status)) => match devices_jails.remove_entry(&pid) {
5405 Some((_, info)) => {
5406 if let Some(status) = wait_status.code() {
5407 info!(
5408 "process for device {} (PID {}) exited with code {}",
5409 &info.name, pid, status
5410 );
5411 } else if let Some(signal) = wait_status.signal() {
5412 warn!(
5413 "process for device {} (PID {}) has been killed by signal {:?}",
5414 &info.name, pid, signal,
5415 );
5416 }
5417 }
5418 None => error!("pid {} is not one of our device processes", pid),
5419 },
5420 Ok((None, _)) => unreachable!(),
5423 }
5424 }
5425
5426 info!("all device processes have exited");
5427
5428 Ok(())
5429}
5430
5431#[cfg(feature = "crash-report")]
5434pub fn setup_emulator_crash_reporting(_cfg: &Config) -> anyhow::Result<String> {
5435 crash_report::setup_crash_reporting(crash_report::CrashReportAttributes {
5436 product_type: "emulator".to_owned(),
5437 pipe_name: None,
5438 report_uuid: None,
5439 product_name: None,
5440 product_version: None,
5441 })
5442}
5443
5444#[cfg(test)]
5445mod tests {
5446 use std::path::PathBuf;
5447
5448 use arch::CpuSet;
5449 use vm_memory::MemoryRegionPurpose;
5450
5451 use super::*;
5452
5453 fn test_file_backed_mapping(address: u64, size: u64) -> FileBackedMappingParameters {
5456 FileBackedMappingParameters {
5457 address,
5458 size,
5459 path: PathBuf::new(),
5460 offset: 0,
5461 writable: false,
5462 sync: false,
5463 align: false,
5464 ram: true,
5465 }
5466 }
5467
5468 #[test]
5469 fn guest_mem_file_backed_mappings_overlap() {
5470 assert_eq!(
5472 punch_holes_in_guest_mem_layout_for_mappings(
5473 vec![
5474 (GuestAddress(0), 0xD000_0000, Default::default()),
5475 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5476 ],
5477 &[]
5478 )
5479 .unwrap(),
5480 vec![
5481 (GuestAddress(0), 0xD000_0000, Default::default()),
5482 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5483 ],
5484 );
5485
5486 assert_eq!(
5488 punch_holes_in_guest_mem_layout_for_mappings(
5489 vec![
5490 (GuestAddress(0), 0xD000_0000, Default::default()),
5491 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5492 ],
5493 &[test_file_backed_mapping(0xD000_0000, 0x1000)]
5494 )
5495 .unwrap_err()
5496 .to_string(),
5497 "RAM file-backed-mapping must be a subset of a RAM region",
5498 );
5499
5500 assert_eq!(
5502 punch_holes_in_guest_mem_layout_for_mappings(
5503 vec![
5504 (GuestAddress(0), 0xD000_0000, Default::default()),
5505 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5506 ],
5507 &[test_file_backed_mapping(0, 0x2000)]
5508 )
5509 .unwrap(),
5510 vec![
5511 (
5512 GuestAddress(0),
5513 0x2000,
5514 MemoryRegionOptions::new()
5515 .purpose(MemoryRegionPurpose::GuestMemoryRegion)
5516 .file_backed(test_file_backed_mapping(0, 0x2000)),
5517 ),
5518 (
5519 GuestAddress(0x2000),
5520 0xD000_0000 - 0x2000,
5521 Default::default()
5522 ),
5523 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5524 ],
5525 );
5526
5527 assert_eq!(
5529 punch_holes_in_guest_mem_layout_for_mappings(
5530 vec![
5531 (GuestAddress(0), 0xD000_0000, Default::default()),
5532 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5533 ],
5534 &[test_file_backed_mapping(0xD000_0000 - 0x2000, 0x2000)]
5535 )
5536 .unwrap(),
5537 vec![
5538 (GuestAddress(0), 0xD000_0000 - 0x2000, Default::default()),
5539 (
5540 GuestAddress(0xD000_0000 - 0x2000),
5541 0x2000,
5542 MemoryRegionOptions::new()
5543 .purpose(MemoryRegionPurpose::GuestMemoryRegion)
5544 .file_backed(test_file_backed_mapping(0xD000_0000 - 0x2000, 0x2000)),
5545 ),
5546 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5547 ],
5548 );
5549
5550 assert_eq!(
5552 punch_holes_in_guest_mem_layout_for_mappings(
5553 vec![
5554 (GuestAddress(0), 0xD000_0000, Default::default()),
5555 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5556 ],
5557 &[test_file_backed_mapping(0x1000, 0x2000)]
5558 )
5559 .unwrap(),
5560 vec![
5561 (GuestAddress(0), 0x1000, Default::default()),
5562 (
5563 GuestAddress(0x1000),
5564 0x2000,
5565 MemoryRegionOptions::new()
5566 .purpose(MemoryRegionPurpose::GuestMemoryRegion)
5567 .file_backed(test_file_backed_mapping(0x1000, 0x2000)),
5568 ),
5569 (
5570 GuestAddress(0x3000),
5571 0xD000_0000 - 0x3000,
5572 Default::default()
5573 ),
5574 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5575 ],
5576 );
5577
5578 assert_eq!(
5580 punch_holes_in_guest_mem_layout_for_mappings(
5581 vec![
5582 (GuestAddress(0), 0xD000_0000, Default::default()),
5583 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5584 ],
5585 &[test_file_backed_mapping(0x1_0000_0000, 0x2000)]
5586 )
5587 .unwrap(),
5588 vec![
5589 (GuestAddress(0), 0xD000_0000, Default::default()),
5590 (
5591 GuestAddress(0x1_0000_0000),
5592 0x2000,
5593 MemoryRegionOptions::new()
5594 .purpose(MemoryRegionPurpose::GuestMemoryRegion)
5595 .file_backed(test_file_backed_mapping(0x1_0000_0000, 0x2000)),
5596 ),
5597 (
5598 GuestAddress(0x1_0000_2000),
5599 0x8_0000 - 0x2000,
5600 Default::default()
5601 ),
5602 ],
5603 );
5604
5605 assert_eq!(
5607 punch_holes_in_guest_mem_layout_for_mappings(
5608 vec![
5609 (GuestAddress(0), 0xD000_0000, Default::default()),
5610 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5611 ],
5612 &[test_file_backed_mapping(0x1_0008_0000 - 0x2000, 0x2000)]
5613 )
5614 .unwrap(),
5615 vec![
5616 (GuestAddress(0), 0xD000_0000, Default::default()),
5617 (
5618 GuestAddress(0x1_0000_0000),
5619 0x8_0000 - 0x2000,
5620 Default::default()
5621 ),
5622 (
5623 GuestAddress(0x1_0008_0000 - 0x2000),
5624 0x2000,
5625 MemoryRegionOptions::new()
5626 .purpose(MemoryRegionPurpose::GuestMemoryRegion)
5627 .file_backed(test_file_backed_mapping(0x1_0008_0000 - 0x2000, 0x2000)),
5628 ),
5629 ],
5630 );
5631
5632 assert_eq!(
5634 punch_holes_in_guest_mem_layout_for_mappings(
5635 vec![
5636 (GuestAddress(0), 0xD000_0000, Default::default()),
5637 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5638 ],
5639 &[test_file_backed_mapping(0x1_0000_1000, 0x2000)]
5640 )
5641 .unwrap(),
5642 vec![
5643 (GuestAddress(0), 0xD000_0000, Default::default()),
5644 (GuestAddress(0x1_0000_0000), 0x1000, Default::default()),
5645 (
5646 GuestAddress(0x1_0000_1000),
5647 0x2000,
5648 MemoryRegionOptions::new()
5649 .purpose(MemoryRegionPurpose::GuestMemoryRegion)
5650 .file_backed(test_file_backed_mapping(0x1_0000_1000, 0x2000)),
5651 ),
5652 (
5653 GuestAddress(0x1_0000_3000),
5654 0x8_0000 - 0x3000,
5655 Default::default()
5656 ),
5657 ],
5658 );
5659
5660 assert_eq!(
5662 punch_holes_in_guest_mem_layout_for_mappings(
5663 vec![
5664 (GuestAddress(0), 0xD000_0000, Default::default()),
5665 (GuestAddress(0x1_0000_0000), 0x8_0000, Default::default()),
5666 ],
5667 &[test_file_backed_mapping(0xA000_0000, 0x60002000)]
5668 )
5669 .unwrap_err()
5670 .to_string(),
5671 "RAM file-backed-mapping must be a subset of a RAM region",
5672 );
5673
5674 assert_eq!(
5676 punch_holes_in_guest_mem_layout_for_mappings(
5677 vec![
5678 (GuestAddress(0x0000), 0x2000, Default::default()),
5679 (
5680 GuestAddress(0x2000),
5681 0x2000,
5682 MemoryRegionOptions::new().purpose(MemoryRegionPurpose::Bios)
5683 ),
5684 ],
5685 &[test_file_backed_mapping(0x2000, 0x2000)]
5686 )
5687 .unwrap(),
5688 vec![
5689 (GuestAddress(0x0000), 0x2000, Default::default()),
5690 (
5691 GuestAddress(0x2000),
5692 0x2000,
5693 MemoryRegionOptions::new()
5694 .purpose(MemoryRegionPurpose::Bios)
5695 .file_backed(test_file_backed_mapping(0x2000, 0x2000)),
5696 ),
5697 ],
5698 );
5699 }
5700
5701 #[cfg(target_arch = "aarch64")]
5702 #[test]
5703 fn normalized_cpu_ipc_ratios_simple() {
5704 let host_max_freq = 5000000;
5705 let mut cpu_frequencies = BTreeMap::new();
5706 cpu_frequencies.insert(0, vec![100000, 200000, 500000]);
5707 cpu_frequencies.insert(1, vec![50000, 75000, 200000]);
5708
5709 let mut cpu_ipc_ratio = BTreeMap::new();
5710 cpu_ipc_ratio.insert(0, 1024);
5711 cpu_ipc_ratio.insert(1, 512);
5712
5713 let normalized_cpu_ipc_ratios = normalize_cpu_ipc_ratios(
5714 cpu_frequencies.iter().map(|(cpu_id, frequencies)| {
5715 (
5716 *cpu_id,
5717 frequencies.iter().copied().max().unwrap_or_default(),
5718 )
5719 }),
5720 host_max_freq,
5721 |cpu_id| {
5722 cpu_ipc_ratio
5723 .get(&cpu_id)
5724 .copied()
5725 .unwrap_or(DEFAULT_CPU_CAPACITY)
5726 },
5727 )
5728 .expect("normalize_cpu_ipc_ratios failed");
5729
5730 let ratios: Vec<(usize, u32)> = normalized_cpu_ipc_ratios.into_iter().collect();
5731 assert_eq!(ratios, vec![(0, 102), (1, 20)]);
5732 }
5733
5734 #[test]
5735 fn test_get_representative_pcpu() {
5736 use std::collections::BTreeMap;
5737 let mut affinity_map = BTreeMap::new();
5738 affinity_map.insert(0, arch::CpuSet::new(vec![4, 5]));
5739 affinity_map.insert(1, arch::CpuSet::new(vec![6]));
5740 let vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
5741
5742 assert_eq!(get_representative_pcpu(0, &vcpu_affinity), 4);
5743 assert_eq!(get_representative_pcpu(1, &vcpu_affinity), 6);
5744 assert_eq!(get_representative_pcpu(2, &vcpu_affinity), 2); let global_affinity = Some(VcpuAffinity::Global(arch::CpuSet::new(vec![7, 8])));
5747 assert_eq!(get_representative_pcpu(0, &global_affinity), 7);
5748 assert_eq!(get_representative_pcpu(1, &global_affinity), 7);
5749
5750 assert_eq!(get_representative_pcpu(0, &None), 0);
5751 assert_eq!(get_representative_pcpu(1, &None), 1);
5752 }
5753
5754 #[test]
5755 fn test_map_vcpu_capacity() {
5756 let vcpu_count = 2;
5757 let mut affinity_map = BTreeMap::new();
5761 affinity_map.insert(0, CpuSet::new(vec![0]));
5762 affinity_map.insert(1, CpuSet::new(vec![2]));
5763 let vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
5764
5765 let mut host_capacity = BTreeMap::new();
5766 host_capacity.insert(0, 512);
5767 host_capacity.insert(2, 1024);
5768 let vcpu_capacity = map_vcpu_capacity(vcpu_count, &vcpu_affinity, &host_capacity).unwrap();
5771
5772 assert_eq!(*vcpu_capacity.get(&0).unwrap(), 512);
5774 assert_eq!(*vcpu_capacity.get(&1).unwrap(), 1024);
5775 }
5776
5777 #[test]
5778 fn test_map_vcpu_clusters() {
5779 use std::collections::BTreeMap;
5780 let host_clusters = vec![
5781 arch::CpuSet::new(vec![0, 1, 2, 3]),
5782 arch::CpuSet::new(vec![4, 5, 6, 7]),
5783 ];
5784
5785 let mut affinity_map = BTreeMap::new();
5786 affinity_map.insert(0, arch::CpuSet::new(vec![0])); affinity_map.insert(1, arch::CpuSet::new(vec![4])); affinity_map.insert(2, arch::CpuSet::new(vec![1])); let vcpu_affinity = Some(VcpuAffinity::PerVcpu(affinity_map));
5790
5791 let vcpu_clusters = map_vcpu_clusters(3, &vcpu_affinity, host_clusters.clone()).unwrap();
5792
5793 assert_eq!(vcpu_clusters.len(), 2);
5794 assert!(vcpu_clusters[0].contains(&0));
5796 assert!(vcpu_clusters[0].contains(&2));
5797 assert!(!vcpu_clusters[0].contains(&1));
5798 assert!(vcpu_clusters[1].contains(&1));
5800 assert!(!vcpu_clusters[1].contains(&0));
5801 assert!(!vcpu_clusters[1].contains(&2));
5802 }
5803}