// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. #[macro_use] extern crate error_chain; #[macro_use] extern crate log; #[macro_use] extern crate machine_manager; #[cfg(target_arch = "x86_64")] #[macro_use] extern crate vmm_sys_util; pub mod errors { error_chain! { links { AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); IntCtrl(devices::IntCtrlErrs::Error, devices::IntCtrlErrs::ErrorKind) #[cfg(target_arch = "aarch64")]; Legacy(devices::LegacyErrs::Error, devices::LegacyErrs::ErrorKind); MicroVm(super::micro_vm::errors::Error, super::micro_vm::errors::ErrorKind); StdVm(super::standard_vm::errors::Error, super::standard_vm::errors::ErrorKind); Util(util::errors::Error, util::errors::ErrorKind); Virtio(virtio::errors::Error, virtio::errors::ErrorKind); MachineManager(machine_manager::config::errors::Error, machine_manager::config::errors::ErrorKind); Hypervisor(hypervisor::errors::Error, hypervisor::errors::ErrorKind); } foreign_links { KvmIoctl(kvm_ioctls::Error); Io(std::io::Error); } errors { AddDevErr(dev: String) { display("Failed to add {} device.", dev) } LoadKernErr { display("Failed to load kernel.") } CrtMemSpaceErr { display("Failed to create memory address space") } CrtIoSpaceErr { display("Failed to create I/O address space") } RegMemRegionErr(base: u64, size: u64) { display("Failed to register region in memory space: base={},size={}", base, size) } InitEventFdErr(fd: String) { display("Failed to init eventfd {}.", fd) } RlzVirtioMmioErr { display("Failed to realize virtio mmio.") } #[cfg(target_arch = "x86_64")] CrtIrqchipErr { display("Failed to create irq chip.") } #[cfg(target_arch = "x86_64")] SetTssErr { display("Failed to set tss address.") } #[cfg(target_arch = "x86_64")] CrtPitErr { display("Failed to create PIT.") } #[cfg(target_arch = "aarch64")] GenFdtErr { display("Failed to generate FDT.") } #[cfg(target_arch = "aarch64")] WrtFdtErr(addr: u64, size: usize) { display("Failed to write FDT: addr={}, size={}", addr, size) } RegNotifierErr { display("Failed to register event notifier.") } StartVcpuErr(id: u8) { display("Failed to run vcpu{}.", id) } PauseVcpuErr(id: u8) { display("Failed to pause vcpu{}.", id) } ResumeVcpuErr(id: u8) { display("Failed to resume vcpu{}.", id) } DestroyVcpuErr(id: u8) { display("Failed to destroy vcpu{}.", id) } } } } mod micro_vm; mod standard_vm; pub use micro_vm::LightMachine; use pci::{PciBus, PciDevOps, PciHost, RootPort}; pub use standard_vm::StdMachine; use virtio::{ BlockState, RngState, VhostKern, VirtioConsoleState, VirtioDevice, VirtioMmioState, VirtioNetState, }; use std::os::unix::io::AsRawFd; use std::path::Path; use std::sync::{Arc, Barrier, Mutex, Weak}; #[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; use address_space::{create_host_mmaps, AddressSpace, KvmMemoryListener, Region}; use cpu::{ArchCPU, CPUBootConfig, CPUInterface, CPU}; use devices::legacy::FwCfgOps; #[cfg(target_arch = "aarch64")] use devices::InterruptController; use hypervisor::kvm::KVM_FDS; use kvm_ioctls::VcpuFd; use machine_manager::config::{ get_multi_function, get_pci_bdf, parse_balloon, parse_blk, parse_device_id, parse_net, parse_rng_dev, parse_root_port, parse_vfio, parse_virtconsole, parse_virtio_serial, parse_vsock, MachineMemConfig, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON, }; use machine_manager::event_loop::EventLoop; use machine_manager::machine::{KvmVmState, MachineInterface}; use migration::MigrationManager; use util::loop_context::{EventNotifier, NotifierCallback, NotifierOperation}; use util::seccomp::{BpfRule, SeccompOpt, SyscallFilter}; use vfio::{VfioDevice, VfioPciDevice}; use virtio::{balloon_allow_list, Balloon, Block, Console, Rng, VirtioMmioDevice, VirtioPciDevice}; use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use errors::{ErrorKind, Result, ResultExt}; use standard_vm::errors::Result as StdResult; pub trait MachineOps { /// Calculate the ranges of memory according to architecture. /// /// # Arguments /// /// * `mem_size` - memory size of VM. /// /// # Returns /// /// A array of ranges, it's element represents (start_addr, size). /// On x86_64, there is a gap ranged from (4G - 768M) to 4G, which will be skipped. fn arch_ram_ranges(&self, mem_size: u64) -> Vec<(u64, u64)>; fn load_boot_source(&self, fwcfg: Option<&Arc<Mutex<dyn FwCfgOps>>>) -> Result<CPUBootConfig>; /// Init I/O & memory address space and mmap guest memory. /// /// # Arguments /// /// * `mem_config` - Memory setting. /// * `sys_io` - IO address space required for x86_64. /// * `sys_mem` - Memory address space. fn init_memory( &self, mem_config: &MachineMemConfig, #[cfg(target_arch = "x86_64")] sys_io: &Arc<AddressSpace>, sys_mem: &Arc<AddressSpace>, is_migrate: bool, nr_cpus: u8, ) -> Result<()> { // KVM_CREATE_VM system call is invoked when KVM_FDS is used for the first time. The system // call registers some notifier functions in the KVM, which are frequently triggered when // doing memory prealloc.To avoid affecting memory prealloc performance, create_host_mmaps // needs to be invoked first. let mut mem_mappings = Vec::new(); if !is_migrate { let ram_ranges = self.arch_ram_ranges(mem_config.mem_size); mem_mappings = create_host_mmaps(&ram_ranges, &mem_config, nr_cpus) .chain_err(|| "Failed to mmap guest ram.")?; } sys_mem .register_listener(Arc::new(Mutex::new(KvmMemoryListener::new( KVM_FDS.load().fd.as_ref().unwrap().get_nr_memslots() as u32, )))) .chain_err(|| "Failed to register KVM listener for memory space.")?; #[cfg(target_arch = "x86_64")] sys_io .register_listener(Arc::new(Mutex::new(KvmIoListener::default()))) .chain_err(|| "Failed to register KVM listener for I/O address space.")?; if !is_migrate { for mmap in mem_mappings.iter() { let base = mmap.start_address().raw_value(); let size = mmap.size(); sys_mem .root() .add_subregion(Region::init_ram_region(mmap.clone()), base) .chain_err(|| ErrorKind::RegMemRegionErr(base, size))?; } } MigrationManager::register_memory_instance(sys_mem.clone()); Ok(()) } /// Init vcpu register with boot message. /// /// # Arguments /// /// * `vm` - `MachineInterface` to obtain functions cpu can use. /// * `nr_cpus` - The number of vcpus. /// * `fds` - File descriptors obtained by creating new Vcpu in KVM. /// * `boot_cfg` - Boot message generated by reading boot source to guest memory. fn init_vcpu( vm: Arc<Mutex<dyn MachineInterface + Send + Sync>>, nr_cpus: u8, fds: &[Arc<VcpuFd>], boot_cfg: &Option<CPUBootConfig>, ) -> Result<Vec<Arc<CPU>>> where Self: Sized, { let mut cpus = Vec::<Arc<CPU>>::new(); for vcpu_id in 0..nr_cpus { #[cfg(target_arch = "aarch64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id)); #[cfg(target_arch = "x86_64")] let arch_cpu = ArchCPU::new(u32::from(vcpu_id), u32::from(nr_cpus)); let cpu = Arc::new(CPU::new( fds[vcpu_id as usize].clone(), vcpu_id, Arc::new(Mutex::new(arch_cpu)), vm.clone(), )); cpus.push(cpu.clone()); MigrationManager::register_device_instance(cpu::ArchCPU::descriptor(), cpu, false); } if let Some(boot_config) = boot_cfg { for cpu_index in 0..nr_cpus as usize { cpus[cpu_index as usize] .realize(&boot_config) .chain_err(|| { format!( "Failed to realize arch cpu register for CPU {}/KVM", cpu_index ) })?; } } Ok(cpus) } /// Add interrupt controller. /// /// # Arguments /// /// * `vcpu_count` - The number of vcpu. fn init_interrupt_controller(&mut self, vcpu_count: u64) -> Result<()>; /// Add RTC device. fn add_rtc_device(&mut self, #[cfg(target_arch = "x86_64")] mem_size: u64) -> Result<()>; /// Add serial device. /// /// # Arguments /// /// * `config` - Device configuration. fn add_serial_device(&mut self, config: &SerialConfig) -> Result<()>; /// Add block device. /// /// # Arguments /// /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration args. fn add_virtio_mmio_block(&mut self, _vm_config: &mut VmConfig, _cfg_args: &str) -> Result<()> { bail!("Virtio mmio devices Not supported!"); } /// Add virtio mmio vsock device. /// /// # Arguments /// /// * `cfg_args` - Device configuration. fn add_virtio_vsock(&mut self, cfg_args: &str) -> Result<()> { let device_cfg = parse_vsock(cfg_args)?; let sys_mem = self.get_sys_mem().clone(); let vsock = Arc::new(Mutex::new(VhostKern::Vsock::new(&device_cfg, &sys_mem))); if cfg_args.contains("vhost-vsock-device") { let device = VirtioMmioDevice::new(&sys_mem, vsock.clone()); MigrationManager::register_device_instance_mutex( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, ); } else { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let virtio_pci_device = VirtioPciDevice::new( device_cfg.id, devfn, sys_mem, vsock.clone(), parent_bus, multi_func, ); virtio_pci_device .realize() .chain_err(|| "Failed to add virtio pci vsock device")?; } MigrationManager::register_device_instance_mutex( VhostKern::VsockState::descriptor(), vsock, ); Ok(()) } fn realize_virtio_mmio_device( &mut self, _dev: VirtioMmioDevice, ) -> Result<Arc<Mutex<VirtioMmioDevice>>> { bail!("Virtio mmio devices not supported"); } fn get_sys_mem(&mut self) -> &Arc<AddressSpace>; /// Add net device. /// /// # Arguments /// /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration args. fn add_virtio_mmio_net(&mut self, _vm_config: &mut VmConfig, _cfg_args: &str) -> Result<()> { bail!("Virtio mmio device Not supported!"); } fn add_virtio_balloon(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_balloon(vm_config, cfg_args)?; let sys_mem = self.get_sys_mem(); let balloon = Arc::new(Mutex::new(Balloon::new(&device_cfg, sys_mem.clone()))); Balloon::object_init(balloon.clone()); if cfg_args.contains("virtio-balloon-device") { let device = VirtioMmioDevice::new(sys_mem, balloon); self.realize_virtio_mmio_device(device)?; } else { let name = device_cfg.id; let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem().clone(); let virtio_pci_device = VirtioPciDevice::new(name, devfn, sys_mem, balloon, parent_bus, multi_func); virtio_pci_device .realize() .chain_err(|| "Failed to add virtio pci balloon device")?; } Ok(()) } /// Add console device. /// /// # Arguments /// /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration args. fn add_virtio_console(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_virtconsole(vm_config, cfg_args)?; let sys_mem = self.get_sys_mem(); let console = Arc::new(Mutex::new(Console::new(device_cfg.clone()))); if let Some(serial) = &vm_config.virtio_serial { if serial.pci_bdf.is_none() { let device = VirtioMmioDevice::new(sys_mem, console.clone()); MigrationManager::register_device_instance_mutex( VirtioMmioState::descriptor(), self.realize_virtio_mmio_device(device) .chain_err(|| ErrorKind::RlzVirtioMmioErr)?, ); } else { let name = device_cfg.id; let virtio_serial_info = if let Some(serial_info) = &vm_config.virtio_serial { serial_info } else { bail!("No virtio-serial-pci device configured for virtconsole"); }; // Reasonable, because for virtio-serial-pci device, the bdf has been checked. let bdf = virtio_serial_info.pci_bdf.clone().unwrap(); let multi_func = virtio_serial_info.multifunction; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem().clone(); let virtio_pci_device = VirtioPciDevice::new( name, devfn, sys_mem, console.clone(), parent_bus, multi_func, ); virtio_pci_device .realize() .chain_err(|| "Failed to add virtio pci console device")?; } } else { bail!("No virtio-serial-bus specified"); } MigrationManager::register_device_instance_mutex(VirtioConsoleState::descriptor(), console); Ok(()) } fn add_virtio_serial(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { parse_virtio_serial(vm_config, cfg_args)?; Ok(()) } /// Add virtio-rng device. /// /// # Arguments /// /// * `vm_config` - VM configuration. /// * `cfg_args` - Device configuration arguments. fn add_virtio_rng(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let device_cfg = parse_rng_dev(vm_config, cfg_args)?; let sys_mem = self.get_sys_mem(); let rng_dev = Arc::new(Mutex::new(Rng::new(device_cfg.clone()))); if cfg_args.contains("virtio-rng-device") { let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone()); self.realize_virtio_mmio_device(device) .chain_err(|| "Failed to add virtio mmio rng device")?; } else { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem().clone(); let vitio_pci_device = VirtioPciDevice::new( device_cfg.id, devfn, sys_mem, rng_dev.clone(), parent_bus, multi_func, ); vitio_pci_device .realize() .chain_err(|| "Failed to add pci rng device")?; } MigrationManager::register_device_instance_mutex(RngState::descriptor(), rng_dev); Ok(()) } fn get_pci_host(&mut self) -> StdResult<&Arc<Mutex<PciHost>>> { bail!("No pci host found"); } fn check_device_id_existed(&mut self, name: &str) -> Result<()> { // If there is no pci bus, skip the id check, such as micro vm. if let Ok(pci_host) = self.get_pci_host() { // Because device_del needs an id when removing a device, it's necessary to ensure that the id is unique. if name.is_empty() { bail!("Device id is empty"); } if PciBus::find_attached_bus(&pci_host.lock().unwrap().root_bus, name).is_some() { bail!("Device id {} existed", name); } } Ok(()) } fn add_virtio_pci_blk(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_blk(vm_config, cfg_args)?; let device = Arc::new(Mutex::new(Block::new(device_cfg.clone()))); self.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func)?; MigrationManager::register_device_instance_mutex(BlockState::descriptor(), device); self.reset_bus(&device_cfg.id)?; Ok(()) } fn add_virtio_pci_net(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let multi_func = get_multi_function(cfg_args)?; let device_cfg = parse_net(vm_config, cfg_args)?; let device: Arc<Mutex<dyn VirtioDevice>> = if device_cfg.vhost_type.is_some() { Arc::new(Mutex::new(VhostKern::Net::new( &device_cfg, self.get_sys_mem(), ))) } else { let device = Arc::new(Mutex::new(virtio::Net::new(device_cfg.clone()))); MigrationManager::register_device_instance_mutex( VirtioNetState::descriptor(), device.clone(), ); device }; self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func)?; self.reset_bus(&device_cfg.id)?; Ok(()) } fn create_vfio_pci_device( &mut self, id: &str, bdf: &PciBdf, host: &str, multifunc: bool, ) -> Result<()> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let path = format!("/sys/bus/pci/devices/{}", host); let device = VfioDevice::new(Path::new(&path), self.get_sys_mem()) .chain_err(|| "Failed to create vfio device.")?; let vfio_pci = VfioPciDevice::new( device, devfn, id.to_string(), parent_bus, multifunc, self.get_sys_mem().clone(), ); VfioPciDevice::realize(vfio_pci).chain_err(|| "Failed to realize vfio-pci device.")?; Ok(()) } fn add_vfio_device(&mut self, cfg_args: &str) -> Result<()> { let device_cfg: VfioConfig = parse_vfio(cfg_args)?; let bdf = get_pci_bdf(cfg_args)?; let multifunc = get_multi_function(cfg_args)?; self.create_vfio_pci_device(&device_cfg.id, &bdf, &device_cfg.host, multifunc)?; self.reset_bus(&device_cfg.id)?; Ok(()) } fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> StdResult<(u8, Weak<Mutex<PciBus>>)> { let pci_host = self.get_pci_host()?; let bus = pci_host.lock().unwrap().root_bus.clone(); let pci_bus = PciBus::find_bus_by_name(&bus, &bdf.bus); if pci_bus.is_none() { bail!("Parent bus :{} not found", &bdf.bus); } let parent_bus = Arc::downgrade(&pci_bus.unwrap()); let devfn = (bdf.addr.0 << 3) + bdf.addr.1; Ok((devfn, parent_bus)) } fn add_pci_root_port(&mut self, cfg_args: &str) -> Result<()> { let bdf = get_pci_bdf(cfg_args)?; let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let device_cfg = parse_root_port(cfg_args)?; let pci_host = self.get_pci_host()?; let bus = pci_host.lock().unwrap().root_bus.clone(); if PciBus::find_bus_by_name(&bus, &device_cfg.id).is_some() { bail!("ID {} already exists."); } let rootport = RootPort::new( device_cfg.id, devfn, device_cfg.port, parent_bus, device_cfg.multifunction, ); rootport .realize() .chain_err(|| "Failed to add pci root port")?; Ok(()) } fn add_virtio_pci_device( &mut self, id: &str, bdf: &PciBdf, device: Arc<Mutex<dyn VirtioDevice>>, multi_func: bool, ) -> Result<()> { let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?; let sys_mem = self.get_sys_mem(); let pcidev = VirtioPciDevice::new( id.to_string(), devfn, sys_mem.clone(), device, parent_bus, multi_func, ); pcidev .realize() .chain_err(|| "Failed to add virtio pci device")?; Ok(()) } /// Set the parent bus slot on when device attached fn reset_bus(&mut self, dev_id: &str) -> Result<()> { let pci_host = self.get_pci_host()?; let locked_pci_host = pci_host.lock().unwrap(); let bus = if let Some((bus, _)) = PciBus::find_attached_bus(&locked_pci_host.root_bus, &dev_id) { bus } else { bail!("Bus not found, dev id {}", dev_id); }; let locked_bus = bus.lock().unwrap(); if locked_bus.name == "pcie.0" { // No need to reset root bus return Ok(()); } let parent_bridge = if let Some(bridge) = locked_bus.parent_bridge.as_ref() { bridge } else { bail!("Parent bridge does not exist, dev id {}", dev_id); }; let dev = parent_bridge.upgrade().unwrap(); let locked_dev = dev.lock().unwrap(); let name = locked_dev.name(); drop(locked_dev); let mut devfn = None; let locked_bus = locked_pci_host.root_bus.lock().unwrap(); for (id, dev) in &locked_bus.devices { if dev.lock().unwrap().name() == name { devfn = Some(*id); break; } } drop(locked_bus); // It's safe to call devfn.unwrap(), because the bus exists. match locked_pci_host.find_device(0, devfn.unwrap()) { Some(dev) => dev .lock() .unwrap() .reset(false) .chain_err(|| "Failed to reset bus"), None => bail!("Failed to found device"), } } /// Init vm global config. /// /// # Arguments /// /// * `vm_config` - VM Configuration. fn init_global_config(&mut self, vm_config: &mut VmConfig) -> Result<()> { let fast_unplug = vm_config .global_config .get("pcie-root-port.fast-unplug") .map_or(false, |val| val == FAST_UNPLUG_ON); RootPort::set_fast_unplug_feature(fast_unplug); Ok(()) } /// Add peripheral devices. /// /// # Arguments /// /// * `vm_config` - VM Configuration. fn add_devices(&mut self, vm_config: &mut VmConfig) -> Result<()> { self.add_rtc_device( #[cfg(target_arch = "x86_64")] vm_config.machine_config.mem_config.mem_size, ) .chain_err(|| ErrorKind::AddDevErr("RTC".to_string()))?; let cloned_vm_config = vm_config.clone(); if let Some(serial) = cloned_vm_config.serial.as_ref() { self.add_serial_device(serial) .chain_err(|| ErrorKind::AddDevErr("serial".to_string()))?; } if let Some(pflashs) = cloned_vm_config.pflashs.as_ref() { self.add_pflash_device(pflashs) .chain_err(|| ErrorKind::AddDevErr("pflash".to_string()))?; } for dev in &cloned_vm_config.devices { let cfg_args = dev.1.as_str(); // Check whether the device id exists to ensure device uniqueness. let id = parse_device_id(cfg_args)?; self.check_device_id_existed(&id) .chain_err(|| format!("Failed to check device id: config {}", cfg_args))?; match dev.0.as_str() { "virtio-blk-device" => { self.add_virtio_mmio_block(vm_config, cfg_args)?; } "virtio-blk-pci" => { self.add_virtio_pci_blk(vm_config, cfg_args)?; } "virtio-net-device" => { self.add_virtio_mmio_net(vm_config, cfg_args)?; } "virtio-net-pci" => { self.add_virtio_pci_net(vm_config, cfg_args)?; } "pcie-root-port" => { self.add_pci_root_port(cfg_args)?; } "vhost-vsock-pci" | "vhost-vsock-device" => { self.add_virtio_vsock(cfg_args)?; } "virtio-balloon-device" | "virtio-balloon-pci" => { self.add_virtio_balloon(vm_config, cfg_args)?; } "virtio-serial-device" | "virtio-serial-pci" => { self.add_virtio_serial(vm_config, cfg_args)?; } "virtconsole" => { self.add_virtio_console(vm_config, cfg_args)?; } "virtio-rng-device" | "virtio-rng-pci" => { self.add_virtio_rng(vm_config, cfg_args)?; } "vfio-pci" => { self.add_vfio_device(cfg_args)?; } _ => { bail!("Unsupported device: {:?}", dev.0.as_str()); } } } Ok(()) } fn add_pflash_device(&mut self, _configs: &[PFlashConfig]) -> Result<()> { bail!("Pflash device is not supported!"); } /// Return the syscall whitelist for seccomp. fn syscall_whitelist(&self) -> Vec<BpfRule>; /// Register seccomp rules in syscall whitelist to seccomp. fn register_seccomp(&self, balloon_enable: bool) -> Result<()> { let mut seccomp_filter = SyscallFilter::new(SeccompOpt::Trap); let mut bpf_rules = self.syscall_whitelist(); if balloon_enable { balloon_allow_list(&mut bpf_rules); } for bpf_rule in &mut bpf_rules { seccomp_filter.push(bpf_rule); } seccomp_filter .realize() .chain_err(|| "Failed to init seccomp filter.")?; Ok(()) } /// Register event notifier for power button of mainboard. /// /// # Arguments /// /// * `power_button` - Eventfd of the power button. fn register_power_event(&self, power_button: &EventFd) -> Result<()> { let power_button = power_button.try_clone().unwrap(); let button_fd = power_button.as_raw_fd(); let power_button_handler: Arc<Mutex<Box<NotifierCallback>>> = Arc::new(Mutex::new(Box::new(move |_, _| { let _ret = power_button.read().unwrap(); None }))); let notifier = EventNotifier::new( NotifierOperation::AddShared, button_fd, None, EventSet::IN, vec![power_button_handler], ); EventLoop::update_event(vec![notifier], None).chain_err(|| ErrorKind::RegNotifierErr)?; Ok(()) } /// Realize the machine. /// /// # Arguments /// /// * `vm` - The machine structure. /// * `vm_config` - VM configuration. fn realize(vm: &Arc<Mutex<Self>>, vm_config: &mut VmConfig, is_migrate: bool) -> Result<()> where Self: Sized; /// Run `LightMachine` with `paused` flag. /// /// # Arguments /// /// * `paused` - Flag for `paused` when `LightMachine` starts to run. fn run(&self, paused: bool) -> Result<()>; /// Start machine as `Running` or `Paused` state. /// /// # Arguments /// /// * `paused` - After started, paused all vcpu or not. /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_start(paused: bool, cpus: &[Arc<CPU>], vm_state: &mut KvmVmState) -> Result<()> where Self: Sized, { let nr_vcpus = cpus.len(); let cpus_thread_barrier = Arc::new(Barrier::new((nr_vcpus + 1) as usize)); for cpu_index in 0..nr_vcpus { let cpu_thread_barrier = cpus_thread_barrier.clone(); let cpu = cpus[cpu_index as usize].clone(); CPU::start(cpu, cpu_thread_barrier, paused) .chain_err(|| format!("Failed to run vcpu{}", cpu_index))?; } if paused { *vm_state = KvmVmState::Paused; } else { *vm_state = KvmVmState::Running; } cpus_thread_barrier.wait(); Ok(()) } /// Pause VM as `Paused` state, sleepy all vcpu thread. /// /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_pause( cpus: &[Arc<CPU>], #[cfg(target_arch = "aarch64")] irq_chip: &Option<Arc<InterruptController>>, vm_state: &mut KvmVmState, ) -> Result<()> where Self: Sized, { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.pause() .chain_err(|| format!("Failed to pause vcpu{}", cpu_index))?; } #[cfg(target_arch = "aarch64")] irq_chip.as_ref().unwrap().stop(); *vm_state = KvmVmState::Paused; Ok(()) } /// Resume VM as `Running` state, awaken all vcpu thread. /// /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_resume(cpus: &[Arc<CPU>], vm_state: &mut KvmVmState) -> Result<()> where Self: Sized, { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.resume() .chain_err(|| format!("Failed to resume vcpu{}", cpu_index))?; } *vm_state = KvmVmState::Running; Ok(()) } /// Destroy VM as `Shutdown` state, destroy vcpu thread. /// /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. fn vm_destroy(cpus: &[Arc<CPU>], vm_state: &mut KvmVmState) -> Result<()> where Self: Sized, { for (cpu_index, cpu) in cpus.iter().enumerate() { cpu.destroy() .chain_err(|| format!("Failed to destroy vcpu{}", cpu_index))?; } *vm_state = KvmVmState::Shutdown; Ok(()) } /// Transfer VM state from `old` to `new`. /// /// # Arguments /// /// * `cpus` - Cpus vector restore cpu structure. /// * `vm_state` - Vm kvm vm state. /// * `old_state` - Old vm state want to leave. /// * `new_state` - New vm state want to transfer to. fn vm_state_transfer( cpus: &[Arc<CPU>], #[cfg(target_arch = "aarch64")] irq_chip: &Option<Arc<InterruptController>>, vm_state: &mut KvmVmState, old_state: KvmVmState, new_state: KvmVmState, ) -> Result<()> where Self: Sized, { use KvmVmState::*; if *vm_state != old_state { bail!("Vm lifecycle error: state check failed."); } match (old_state, new_state) { (Created, Running) => <Self as MachineOps>::vm_start(false, cpus, vm_state) .chain_err(|| "Failed to start vm.")?, (Running, Paused) => <Self as MachineOps>::vm_pause( cpus, #[cfg(target_arch = "aarch64")] irq_chip, vm_state, ) .chain_err(|| "Failed to pause vm.")?, (Paused, Running) => <Self as MachineOps>::vm_resume(cpus, vm_state) .chain_err(|| "Failed to resume vm.")?, (_, Shutdown) => { <Self as MachineOps>::vm_destroy(cpus, vm_state) .chain_err(|| "Failed to destroy vm.")?; } (_, _) => { bail!("Vm lifecycle error: this transform is illegal."); } } if *vm_state != new_state { bail!( "Vm lifecycle error: state '{:?} -> {:?}' transform failed.", old_state, new_state ); } Ok(()) } }