crosvm/crosvm/sys/
linux.rs

1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#[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 is the least likely to collide with the indices generated above for
342                    // the multi_touch options, which begin at 0.
343                    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 is the least likely to collide with the indices generated above for
366                    // the multi_touch options, which begin at 0.
367                    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        // Prepare a `VmMemoryClient` for pmem-ext2 device to send a request for mmap() and memory
449        // registeration.
450        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        // pvclock gets a tube for handling suspend/resume requests from the main thread.
478        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                // Host TSCs are not in sync. Log what mitigations are applied.
489                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            // SAFETY: This instruction have no side effect apart from storing the current timestamp
500            //         frequency into the specified register.
501            unsafe {
502                asm!("mrs {x}, cntfrq_el0",
503                    x = out(reg) x,
504                );
505            }
506            frequency = x;
507
508            // If unset, KVM defaults to an offset that is calculated from VM boot time. Explicitly
509            // set it to zero on boot. When updating the offset, we always set it to the total
510            // amount of time the VM has been suspended.
511            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            // Balloon gets a special socket so balloon requests can be forwarded
698            // from the main process.
699            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            // The initial balloon size is the total memory size minus the initial memory size.
721            total_memory_bytes - init_memory_bytes
722        } else {
723            // No --init-mem specified; start with balloon completely deflated.
724            0
725        };
726
727        // The balloon device also needs a tube to communicate back to the main process to
728        // handle remapping memory dynamically.
729        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    // Sort the devices to match a legacy ordering (the order affects PCI addresses etc). This
958    // allows us to move devices to the VirtioDeviceModule style without side effects.
959    //
960    // Doesn't provide a complete ordering, for example, all the input devices are aliased together
961    // and so the order between them will be determined by the cmdline processing code, which
962    // matches legacy behavior.
963    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    // Stores a set of PID of child processes that are suppose to exit cleanly.
1009    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            // SAFETY: trivially safe
1067            let res = unsafe { libc::getrlimit64(libc::RLIMIT_MEMLOCK, buf.as_mut_ptr()) };
1068            if res == 0 {
1069                // SAFETY: safe because getrlimit64 has returned success.
1070                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                    // SAFETY: trivially safe
1079                    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        // Create xhci controller.
1193        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        // Stub devices don't need jailing since they don't do anything.
1205        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            // OutOfSpace just means that this mapping is not in the MMIO regions at all, so don't
1249            // consider it an error.
1250            // TODO(b/222769529): Reserve this region in a global memory address space allocator
1251            // once we have that so nothing else can accidentally overlap with it.
1252            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            /* log_dirty_pages = */ 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")]
1270/// Collection of devices related to PCI hotplug.
1271struct HotPlugStub {
1272    /// Map from bus index to hotplug bus.
1273    hotplug_buses: BTreeMap<u8, Arc<Mutex<dyn HotPlugBus>>>,
1274    /// Bus ranges of devices for virtio-iommu.
1275    iommu_bus_ranges: Vec<RangeInclusive<u32>>,
1276    /// Map from bus index to PmeNotify devices.
1277    pme_notify_devs: BTreeMap<u8, Arc<Mutex<dyn PmeNotify>>>,
1278}
1279
1280#[cfg(target_arch = "x86_64")]
1281impl HotPlugStub {
1282    /// Constructs empty HotPlugStub.
1283    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")]
1293/// Creates PCIE root port with only virtual devices.
1294///
1295/// user doesn't specify host pcie root port which link to this virtual pcie rp,
1296/// find the empty bus and create a total virtual pcie rp
1297fn 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    // Create Pcie Root Port for non-root buses, each non-root bus device will be
1306    // connected behind a virtual pcie root port.
1307    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        // no ipc is used if the root port disables hotplug
1322        devices.push((pci_bridge, None));
1323    }
1324
1325    // Create Pcie Root Port for hot-plug
1326    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
1363/// For `vcpu_id`, return the pcpu that it's affined to. It's considered the "representative"
1364/// pcpu since there could be multiple pcpu's affined to a single vcpu, so arbitrarily return the
1365/// first pcpu we see. This "shouldn't" be an issue since ideally all the affined pcpu's have the
1366/// same capacity, frequency, etc.
1367fn get_representative_pcpu(vcpu_id: usize, vcpu_affinity: &Option<VcpuAffinity>) -> usize {
1368    match vcpu_affinity {
1369        // Default to pcpu 0 to preserve the intent to map all vcpu's to the same cluster of pcpu's.
1370        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
1379/// Given `vcpu_affinity` (vcpu->pcpu mapping) and `host_capacity` (pcpu->pcpu capacity mapping),
1380/// return a mapping of vcpu->pcpu's capacity.
1381fn 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
1398/// Given `vcpu_affinity` (vcpu->pcpu mapping) and `host_clusters` (cluster->pcpu mapping),
1399/// return a mapping of cluster->vcpu mapping.
1400fn 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    // Maps vcpu -> the corresponding vcpu's frequency.
1491    #[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    // if --enable-fw-cfg or --fw-cfg was given, we want to enable fw_cfg
1497    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                        // Check that the physical CPUs that the vCPU is affined to all share the
1549                        // same frequency domain.
1550                        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            // Find the highest maximum frequency over all host CPUs. The guest CPU IPC ratios will
1573            // be normalized by dividing by this value.
1574            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                // Assign parent crosvm process to top level cgroup
1603                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 subtree for domain
1619                    create_dir_all(&vcpu_domain_path)?;
1620
1621                    // Set vcpu_domain cgroup type as 'threaded' to get thread level granularity
1622                    // controls
1623                    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
1771// Replaces ranges in `guest_mem_layout` that overlap with ranges in `file_backed_mappings`.
1772// Returns the updated guest memory layout.
1773fn 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    // Create a set containing (start, end) pairs with exclusive end (end = start + size; the byte
1778    // at end is not included in the range).
1779    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    // Make sure the RAM mappings are a subset of the RAM memory layout.
1785    // For simplicity, we currently require each mapping to be fully contained within a single
1786    // region of the input layout.
1787    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        // Repeatedly split overlapping guest memory regions until no overlaps remain.
1802        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    // Build the final guest memory layout from the modified layout_set.
1836    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    // When sandboxing is enabled, we can MADV_REMOVE from the balloon process, otherwise, fallback
1867    // to using FALLOC_FL_PUNCH_HOLE.
1868    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        // Note that this isn't compatible with sandboxing. We could potentially fix that by
1875        // delaying the call until after the sandboxed devices are forked. However, the main use
1876        // for this is in conjunction with protected VMs, where most of the guest memory has been
1877        // unshared with the host. We'd need to be confident that the guest memory is unshared with
1878        // the host only after the `use_dontfork` call and those details will vary by hypervisor.
1879        // So, for now we keep things simple to be safe.
1880        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    // Check that the VM was actually created in protected mode as expected.
1915    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    // Check that the VM was actually created in protected mode as expected.
1974    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    // Check that the VM was actually created in protected mode as expected.
2036    // This check is only needed on aarch64. On x86_64, protected VM creation will fail
2037    // if protected mode is not supported.
2038    #[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    // Check that the VM was actually created in protected mode as expected.
2138    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
2154/// Choose a default hypervisor if no `--hypervisor` option was specified.
2155fn 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        // Printing something to the syslog before entering minijail so that libc's syslogger has a
2243        // chance to open files necessary for its operation, like `/etc/localtime`. After jailing,
2244        // access to those files will not be possible.
2245        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    // pci-hotplug is only implemented for x86_64 for now, attempting to use it on other platform
2257    // would crash.
2258    #[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    // hotplug_manager must be created before vm is started since it forks jail warden process.
2263    #[cfg(feature = "pci-hotplug")]
2264    // TODO(293801301): Remove unused_mut after aarch64 support
2265    #[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    // Masking signals is inherently dangerous, since this can persist across clones/execs. Do this
2282    // before any jailed devices have been spawned, so that we can catch any of them that fail very
2283    // quickly.
2284    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            // Setup a bind mount to the system D-Bus socket if the powerd monitor is used.
2315            #[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    // Hold on to the render server jail so it keeps running until we exit run_vm()
2356    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        &reg_evt_wrtube,
2390        &mut vfio_container_manager,
2391        &mut worker_process_pids,
2392    )?;
2393
2394    #[cfg(feature = "pci-hotplug")]
2395    // TODO(293801301): Remove unused_variables after aarch64 support
2396    #[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    // order the OpenFirmware device paths, in ascending order, by their boot_index
2437    open_firmware_device_paths.sort_by(|a, b| (a.1).cmp(&(b.1)));
2438
2439    // "/pci@iocf8/" is x86 specific and represents the root at the system bus port
2440    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    // the "bootorder" file is expected to end with a null terminator
2452    bootorder_fw_cfg_blob.push(0);
2453
2454    components.bootorder_fw_cfg_blob = bootorder_fw_cfg_blob;
2455
2456    // if the bootindex argument was given, we want to make sure that fw_cfg is enabled so the
2457    // "bootorder" file can be accessed by the guest.
2458    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        // early reservation for viommu.
2509        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    // KVM_CREATE_VCPU uses apic id for x86 and uses cpu id for others.
2529    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// Hotplug command is facing dead lock issue when it tries to acquire the lock
2682// for pci root in the vm control thread. Dead lock could happen when the vm
2683// control thread(Thread A namely) is handling the hotplug command and it tries
2684// to get the lock for pci root. However, the lock is already hold by another
2685// device in thread B, which is actively sending an vm control to be handled by
2686// thread A and waiting for response. However, thread A is blocked on acquiring
2687// the lock, so dead lock happens. In order to resolve this issue, we add this
2688// worker thread and push all work that locks pci root to this thread.
2689#[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                            // SAFETY:
2887                            // Safe because the descriptor is uniquely owned by `descriptor`.
2888                            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 all simbling downstream ports are empty, do not send hot unplug event for this
3105            // downstream port. Root port will send one plug out interrupt and remove all
3106            // the remaining devices
3107            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        // Some types of TBT device has a few empty downstream ports. The emulated bridges
3127        // of these ports won't be removed since no vfio device is connected to our emulated
3128        // bridges. So we explicitly check all simbling bridges of the removed bridge here,
3129        // and remove them if bridge has no child device connected.
3130        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    // During suspend also emulate sleepbtn, which allows to suspend VM (if running e.g. acpid and
3175    // reacts on sleep button events)
3176    if let Some(pm) = pm {
3177        pm.lock().slpbtn_evt();
3178    } else {
3179        error!("generating sleepbtn during suspend not supported");
3180    }
3181
3182    // Wait for notification about guest suspension, if not received after 15sec,
3183    // proceed anyway.
3184    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    // Now we ready to send response over the tube and communicate that VM suspend has finished
3197    if let Err(e) = tube.send(&response) {
3198        error!("failed to send VmResponse: {}", e);
3199    }
3200}
3201
3202#[cfg(feature = "pvclock")]
3203#[derive(Debug)]
3204/// The action requested by the pvclock device to perform on the main thread.
3205enum PvClockAction {
3206    #[cfg(target_arch = "aarch64")]
3207    /// Update the counter offset with VmAarch64::set_counter_offset.
3208    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                    // For non-AArch64 platforms this is handled by directly updating the offset in
3235                    // shared memory in the pvclock device worker.
3236                    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                // Suppress warnings.
3404                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                    // Update clock offset when pvclock is used.
3544                    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                    // Spawn s2idle wait thread.
3604                    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                    // For s2idle, omit the response since it will be sent by
3623                    // s2idle_wait thread when suspension actually happens.
3624                    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                    // Record the time after VCPUs are suspended to track suspension duration.
3630                    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                // Since all tubes of the same addr should
3781                // be an RC to the same tube, it doesn't
3782                // matter which one we get. But we do need
3783                // to check for a registration for the
3784                // current event, so can't break here.
3785                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)] // mut is required x86 only
3830    #[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    // A set of PID of child processes whose clean exit is expected and can be ignored.
3837    mut worker_process_pids: BTreeSet<Pid>,
3838    #[cfg(target_arch = "aarch64")] vcpu_domain_paths: BTreeMap<usize, PathBuf>,
3839) -> Result<ExitState> {
3840    // Split up `all_control_tubes`.
3841    #[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        // GDB needs a control socket to interrupt vcpus.
3900        let (gdb_host_tube, gdb_control_tube) = Tube::pair().context("failed to create tube")?;
3901        control_tubes.push(TaggedControlTube::Vm(gdb_host_tube));
3902        // Create a channel for GDB thread.
3903        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        (&reg_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        // Before starting VCPUs, in case we started with some capabilities, drop them all.
3968        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    // Create devices thread, and restore if a restore file exists.
3973    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    // Enable core scheduling before creating vCPUs so that the cookie will be
4004    // shared by all vCPU threads.
4005    // TODO(b/199312402): Avoid enabling core scheduling for the crosvm process
4006    // itself for even better performance. Only vCPUs need the feature.
4007    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    // The tasks file only exist on sysfs if CgroupV1 hierachies are enabled
4014    let vcpu_cgroup_tasks_file = match &cfg.vcpu_cgroup_path {
4015        None => None,
4016        Some(cgroup_path) => {
4017            // Move main process to cgroup_path
4018            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    // vCPU freq domains are currently only supported with CgroupsV2.
4032    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        // Sleep devices before creating vcpus.
4071        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        // Wait until a GDB client attaches
4090        run_mode = VmRunMode::Breakpoint;
4091    }
4092    // If we are restoring from a snapshot, then start suspended.
4093    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    // Architecture-specific code must supply a vcpu_init element for each VCPU.
4100    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    // Spawn GDB thread.
4212    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    // See comment on `VmRequest::execute`.
4272    let mut suspended_pvclock_state: Option<hypervisor::ClockState> = None;
4273
4274    // Restore VM (if applicable).
4275    // Must happen after the vCPU barrier to avoid deadlock.
4276    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            /* require_encrypted= */ false,
4286            &mut suspended_pvclock_state,
4287            &*linux.vm,
4288        )?;
4289        // Allow the vCPUs to start for real.
4290        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(&reg_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                    // Print all available siginfo structs, then exit the loop if child process has
4435                    // been exited except CLD_STOPPED and CLD_CONTINUED. the two should be ignored
4436                    // here since they are used by the vmm-swap feature.
4437                    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                        // TODO(kawasin): this is a temporary exception until device suspension.
4448                        #[cfg(feature = "swap")]
4449                        if siginfo.ssi_code == libc::CLD_STOPPED
4450                            || siginfo.ssi_code == libc::CLD_CONTINUED
4451                        {
4452                            continue;
4453                        }
4454
4455                        // Ignore clean exits of non-tracked child processes when running without
4456                        // sandboxing. The virtio gpu process launches a render server for
4457                        // pass-through graphics. Host GPU drivers have been observed to fork
4458                        // child processes that exit cleanly which should not be considered a
4459                        // crash. When running with sandboxing, this should be handled by the
4460                        // device's process handler.
4461                        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                        // Allow clean exits of a child process in `worker_process_pids`.
4471                        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    // After joining all vcpu threads, unregister the process-wide signal handler.
4626    if let Err(e) = vcpu::remove_vcpu_signal_handler() {
4627        error!("failed to remove vcpu thread signal handler: {:#}", e);
4628    }
4629
4630    // Stop the vmm-swap monitor process.
4631    #[cfg(feature = "swap")]
4632    drop(swap_controller);
4633
4634    // Stop pci root worker thread
4635    #[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    // At this point, the only remaining `Arc` references to the `Bus` objects should be the ones
4655    // inside `linux`. If the checks below fail, then some other thread is probably still running
4656    // and needs to be explicitly stopped before dropping `linux` to ensure devices actually get
4657    // cleaned up.
4658    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    // Explicitly drop the VM structure here to allow the devices to clean up before the
4674    // control sockets are closed when this function exits.
4675    mem::drop(linux);
4676
4677    // Shut down the VM memory handler thread. This must happen after the potential device worker
4678    // threads(including the vhost device request handler threads) exit, because device worker
4679    // threads can issue VM memory requests. Those device worker threads are supposed to stop after
4680    // the RunnableLinuxVm is dropped.
4681    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    // Shut down the IRQ handler thread after the devices are dropped.
4692    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    // Drop the hotplug manager to tell the warden process to exit before we try to join
4700    // the metrics thread.
4701    #[cfg(feature = "pci-hotplug")]
4702    mem::drop(hotplug_manager);
4703
4704    // All our children should have exited by now, so closing our fd should
4705    // terminate metrics. Then join so that everything gets flushed.
4706    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
4728/// Handles IRQs and requests from devices to add additional IRQ lines.
4729fn 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/// Commands to control the VM Memory handler thread.
4960#[derive(serde::Serialize, serde::Deserialize)]
4961pub enum VmMemoryHandlerRequest {
4962    /// No response is sent for this command.
4963    AddControlTubes(Vec<VmMemoryTube>),
4964    /// No response is sent for this command.
4965    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
5096/// When control tubes hang up, we want to make sure that we've fully drained
5097/// the underlying socket before removing it. This function also handles
5098/// removing closed sockets in such a way that avoids phantom events.
5099///
5100/// `tube_ids_to_remove` is the set of ids that we already know should
5101/// be removed (e.g. from getting a disconnect error on read).
5102fn 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    // It's possible more data is readable and buffered while the socket is hungup,
5114    // so don't delete the tube from the poll context until we're sure all the
5115    // data is read.
5116    // Below case covers a condition where we have received a hungup event and the tube is not
5117    // readable.
5118    // In case of readable tube, once all data is read, any attempt to read more data on hungup
5119    // tube should fail. On such failure, we get Disconnected error and ids gets added to
5120    // tube_ids_to_remove by the time we reach here.
5121    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        // Delete the socket from the `wait_ctx` synchronously. Otherwise, the kernel will do
5130        // this automatically when the FD inserted into the `wait_ctx` is closed after this
5131        // if-block, but this removal can be deferred unpredictably. In some instances where the
5132        // system is under heavy load, we can even get events returned by `wait_ctx` for an FD
5133        // that has already been closed. Because the token associated with that spurious event
5134        // now belongs to a different socket, the control loop will start to interact with
5135        // sockets that might not be ready to use. This can cause incorrect hangup detection or
5136        // blocking on a socket that will never be ready. See also: crbug.com/1019986
5137        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
5146/// Start and jail a vhost-user device according to its configuration and a vhost listener string.
5147///
5148/// The jailing business is nasty and potentially unsafe if done from the wrong context - do not
5149/// call outside of `start_devices`!
5150///
5151/// Returns the pid of the jailed device process.
5152fn 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    // Create a jail from the configuration. If the configuration is `None`, `create_jail` will also
5167    // return `None` so fall back to an empty (i.e. non-constrained) Minijail.
5168    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    // Create the device in the parent process, so the child does not need any privileges necessary
5176    // to do it (only runtime capabilities are required).
5177    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    // Executor must be created before jail in order to prevent the jailed process from creating
5186    // unrestricted io_urings.
5187    let ex = Executor::new().context("Failed to create an Executor")?;
5188    keep_rds.extend(ex.as_raw_descriptors());
5189
5190    // Deduplicate the FDs since minijail expects them to be unique.
5191    keep_rds.sort_unstable();
5192    keep_rds.dedup();
5193
5194    // SAFETY:
5195    // Safe because we are keeping all the descriptors needed for the child to function.
5196    match unsafe { jail.fork(Some(&keep_rds)).context("error while forking")? } {
5197        0 => {
5198            // In the child process.
5199
5200            // Free memory for the resources managed by the parent, without running drop() on them.
5201            // The parent will do it as we exit.
5202            let _ = std::mem::ManuallyDrop::new(parent_resources);
5203
5204            // Make sure the child process does not survive its parent.
5205            // SAFETY: trivially safe
5206            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            // Set the name for the thread.
5211            const MAX_LEN: usize = 15; // pthread_setname_np() limit on Linux
5212            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            // SAFETY:
5215            // Safe because we trimmed the name to 15 characters (and pthread_setname_np will return
5216            // an error if we don't anyway).
5217            let _ = unsafe { libc::pthread_setname_np(libc::pthread_self(), thread_name.as_ptr()) };
5218
5219            // Run the device loop and terminate the child process once it exits.
5220            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            // SAFETY: trivially safe
5228            unsafe { libc::exit(res) };
5229        }
5230        pid => {
5231            // In the parent process. We will drop the device and listener when exiting this method.
5232            // This is fine as ownership for both has been transferred to the child process and they
5233            // will keep living there. We just retain `parent_resources` for things we are supposed
5234            // to clean up ourselves.
5235
5236            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        // Unique name for the device, in the form `foomatic-0`.
5308        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    // Create control server socket
5344    let control_server_socket = opts.control_socket.map(|path| {
5345        UnlinkUnixSeqpacketListener(
5346            UnixSeqpacketListener::bind(path).expect("Could not bind socket"),
5347        )
5348    });
5349
5350    // Create serial devices.
5351    for (i, params) in opts.serial.iter().enumerate() {
5352        let serial_config = &params.device;
5353        add_device(i, serial_config, &params.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    // Create block devices.
5359    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(&params.device, tube);
5368        add_device(i, disk_config, &params.vhost, jail, &mut devices_jails)?;
5369    }
5370
5371    // Create vsock devices.
5372    for (i, params) in opts.vsock.iter().enumerate() {
5373        add_device(i, &params.device, &params.vhost, jail, &mut devices_jails)?;
5374    }
5375
5376    // Create network devices.
5377    #[cfg(feature = "net")]
5378    for (i, params) in opts.net.iter().enumerate() {
5379        add_device(i, &params.device, &params.vhost, jail, &mut devices_jails)?;
5380    }
5381
5382    // No device created, that's probably not intended - print the help in that case.
5383    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        // Start the control server in the parent process.
5395        std::thread::spawn(move || {
5396            start_vhost_user_control_server(control_server_socket, disk_host_tubes)
5397        });
5398    }
5399
5400    // Now wait for all device processes to return.
5401    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            // `wait_for_pid` will necessarily return a PID because we asked to it wait for one to
5421            // complete.
5422            Ok((None, _)) => unreachable!(),
5423        }
5424    }
5425
5426    info!("all device processes have exited");
5427
5428    Ok(())
5429}
5430
5431/// Setup crash reporting for a process. Each process MUST provide a unique `product_type` to avoid
5432/// making crash reports incomprehensible.
5433#[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    // Create a file-backed mapping parameters struct with the given `address` and `size` and other
5454    // parameters set to default values.
5455    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        // Base case: no file mappings; output layout should be identical.
5471        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        // File mapping that does not overlap guest memory.
5487        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        // File mapping at the start of the low address space region.
5501        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        // File mapping at the end of the low address space region.
5528        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        // File mapping fully contained within the middle of the low address space region.
5551        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        // File mapping at the start of the high address space region.
5579        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        // File mapping at the end of the high address space region.
5606        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        // File mapping fully contained within the middle of the high address space region.
5633        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        // File mapping overlapping two guest memory regions.
5661        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        // File mapping with different region purpose.
5675        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); // Fallback to vcpu_id on missing vCPU
5745
5746        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        // Assume PCPU 1 is offline or skipped.
5758        // VCPU 0 -> PCPU 0
5759        // VCPU 1 -> PCPU 2
5760        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        // PCPU 1 is missing (offline).
5769
5770        let vcpu_capacity = map_vcpu_capacity(vcpu_count, &vcpu_affinity, &host_capacity).unwrap();
5771
5772        // Verify lookup by VCPU ID
5773        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])); // in cluster 0
5787        affinity_map.insert(1, arch::CpuSet::new(vec![4])); // in cluster 1
5788        affinity_map.insert(2, arch::CpuSet::new(vec![1])); // in cluster 0
5789        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        // Cluster 0 should have vCPU 0 and 2
5795        assert!(vcpu_clusters[0].contains(&0));
5796        assert!(vcpu_clusters[0].contains(&2));
5797        assert!(!vcpu_clusters[0].contains(&1));
5798        // Cluster 1 should have vCPU 1
5799        assert!(vcpu_clusters[1].contains(&1));
5800        assert!(!vcpu_clusters[1].contains(&0));
5801        assert!(!vcpu_clusters[1].contains(&2));
5802    }
5803}