1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openeuler-stratovirt

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
lib.rs 80 КБ
Копировать Редактировать Исходные данные Просмотреть построчно История
zhanghan64 Отправлено год назад 68d3d6c
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273
// 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.
#[cfg(target_arch = "aarch64")]
pub mod aarch64;
pub mod error;
pub mod standard_common;
#[cfg(target_arch = "x86_64")]
pub mod x86_64;
mod micro_common;
pub use crate::error::MachineError;
pub use micro_common::LightMachine;
pub use standard_common::StdMachine;
use std::collections::{BTreeMap, HashMap};
use std::fs::{remove_file, File};
use std::net::TcpListener;
use std::ops::Deref;
use std::os::unix::net::UnixListener;
use std::path::Path;
use std::sync::{Arc, Barrier, Condvar, Mutex, RwLock, Weak};
#[cfg(feature = "windows_emu_pid")]
use std::time::Duration;
use anyhow::{anyhow, bail, Context, Result};
use clap::Parser;
use log::warn;
#[cfg(feature = "windows_emu_pid")]
use vmm_sys_util::eventfd::EventFd;
#[cfg(all(target_env = "ohos", feature = "ohui_srv"))]
use address_space::FileBackend;
use address_space::{create_backend_mem, create_default_mem, AddressSpace, GuestAddress, Region};
#[cfg(target_arch = "aarch64")]
use cpu::CPUFeatures;
use cpu::{ArchCPU, CPUBootConfig, CPUHypervisorOps, CPUInterface, CPUTopology, CpuTopology, CPU};
use devices::legacy::FwCfgOps;
#[cfg(feature = "pvpanic")]
use devices::misc::pvpanic::PvPanicPci;
#[cfg(feature = "scream")]
use devices::misc::scream::{Scream, ScreamConfig};
#[cfg(feature = "demo_device")]
use devices::pci::demo_device::DemoDev;
use devices::pci::{PciBus, PciDevOps, PciHost, RootPort};
use devices::smbios::smbios_table::{build_smbios_ep30, SmbiosTable};
use devices::smbios::{SMBIOS_ANCHOR_FILE, SMBIOS_TABLE_FILE};
use devices::sysbus::{SysBus, SysBusDevOps, SysBusDevType};
#[cfg(feature = "usb_camera")]
use devices::usb::camera::{UsbCamera, UsbCameraConfig};
use devices::usb::keyboard::{UsbKeyboard, UsbKeyboardConfig};
use devices::usb::tablet::{UsbTablet, UsbTabletConfig};
#[cfg(feature = "usb_host")]
use devices::usb::usbhost::{UsbHost, UsbHostConfig};
use devices::usb::xhci::xhci_pci::{XhciConfig, XhciPciDevice};
use devices::usb::{storage::UsbStorage, UsbDevice};
#[cfg(target_arch = "aarch64")]
use devices::InterruptController;
use devices::ScsiDisk::{ScsiDevice, SCSI_TYPE_DISK, SCSI_TYPE_ROM};
use hypervisor::{kvm::KvmHypervisor, HypervisorOps};
#[cfg(feature = "usb_camera")]
use machine_manager::config::get_cameradev_by_id;
#[cfg(feature = "demo_device")]
use machine_manager::config::parse_demo_dev;
#[cfg(feature = "virtio_gpu")]
use machine_manager::config::parse_gpu;
#[cfg(feature = "pvpanic")]
use machine_manager::config::parse_pvpanic;
use machine_manager::config::parse_usb_storage;
use machine_manager::config::{
complete_numa_node, get_multi_function, get_pci_bdf, parse_blk, parse_device_id,
parse_device_type, parse_fs, parse_net, parse_numa_distance, parse_numa_mem, parse_rng_dev,
parse_root_port, parse_scsi_controller, parse_scsi_device, parse_vfio, parse_vhost_user_blk,
parse_virtio_serial, parse_virtserialport, parse_vsock, str_slip_to_clap, BootIndexInfo,
BootSource, DriveFile, Incoming, MachineMemConfig, MigrateMode, NumaConfig, NumaDistance,
NumaNode, NumaNodes, PFlashConfig, PciBdf, SerialConfig, VfioConfig, VmConfig, FAST_UNPLUG_ON,
MAX_VIRTIO_QUEUE,
};
use machine_manager::event_loop::EventLoop;
use machine_manager::machine::{MachineInterface, VmState};
use migration::{MigrateOps, MigrationManager};
#[cfg(feature = "windows_emu_pid")]
use ui::console::{get_run_stage, VmRunningStage};
use util::file::{clear_file, lock_file, unlock_file};
use util::{
arg_parser,
seccomp::{BpfRule, SeccompOpt, SyscallFilter},
};
use vfio::{VfioDevice, VfioPciDevice, KVM_DEVICE_FD};
#[cfg(feature = "virtio_gpu")]
use virtio::Gpu;
#[cfg(all(target_env = "ohos", feature = "ohui_srv"))]
use virtio::VirtioDeviceQuirk;
use virtio::{
balloon_allow_list, find_port_by_nr, get_max_nr, vhost, Balloon, BalloonConfig, Block,
BlockState, Rng, RngState,
ScsiCntlr::{scsi_cntlr_create_scsi_bus, ScsiCntlr},
Serial, SerialPort, VhostKern, VhostUser, VirtioDevice, VirtioMmioDevice, VirtioMmioState,
VirtioNetState, VirtioPciDevice, VirtioSerialState, VIRTIO_TYPE_CONSOLE,
};
/// Machine structure include base members.
pub struct MachineBase {
/// `vCPU` topology, support sockets, cores, threads.
cpu_topo: CpuTopology,
/// `vCPU` devices.
cpus: Vec<Arc<CPU>>,
/// Interrupt controller device.
#[cfg(target_arch = "aarch64")]
irq_chip: Option<Arc<InterruptController>>,
/// Memory address space.
sys_mem: Arc<AddressSpace>,
// IO address space.
#[cfg(target_arch = "x86_64")]
sys_io: Arc<AddressSpace>,
/// System bus.
sysbus: SysBus,
/// VM running state.
vm_state: Arc<(Mutex<VmState>, Condvar)>,
/// Vm boot_source config.
boot_source: Arc<Mutex<BootSource>>,
/// All configuration information of virtual machine.
vm_config: Arc<Mutex<VmConfig>>,
/// List of guest NUMA nodes information.
numa_nodes: Option<NumaNodes>,
/// Drive backend files.
drive_files: Arc<Mutex<HashMap<String, DriveFile>>>,
/// FwCfg device.
fwcfg_dev: Option<Arc<Mutex<dyn FwCfgOps>>>,
/// machine all backend memory region tree
machine_ram: Arc<Region>,
/// machine hypervisor.
hypervisor: Arc<Mutex<dyn HypervisorOps>>,
/// migrate hypervisor.
migration_hypervisor: Arc<Mutex<dyn MigrateOps>>,
}
impl MachineBase {
pub fn new(
vm_config: &VmConfig,
free_irqs: (i32, i32),
mmio_region: (u64, u64),
) -> Result<Self> {
let cpu_topo = CpuTopology::new(
vm_config.machine_config.nr_cpus,
vm_config.machine_config.nr_sockets,
vm_config.machine_config.nr_dies,
vm_config.machine_config.nr_clusters,
vm_config.machine_config.nr_cores,
vm_config.machine_config.nr_threads,
vm_config.machine_config.max_cpus,
);
let machine_ram = Arc::new(Region::init_container_region(
u64::max_value(),
"MachineRam",
));
let sys_mem = AddressSpace::new(
Region::init_container_region(u64::max_value(), "SysMem"),
"sys_mem",
Some(machine_ram.clone()),
)
.with_context(|| MachineError::CrtIoSpaceErr)?;
#[cfg(target_arch = "x86_64")]
let sys_io = AddressSpace::new(
Region::init_container_region(1 << 16, "SysIo"),
"SysIo",
None,
)
.with_context(|| MachineError::CrtIoSpaceErr)?;
let sysbus = SysBus::new(
#[cfg(target_arch = "x86_64")]
&sys_io,
&sys_mem,
free_irqs,
mmio_region,
);
let hypervisor = Arc::new(Mutex::new(KvmHypervisor::new()?));
Ok(MachineBase {
cpu_topo,
cpus: Vec::new(),
#[cfg(target_arch = "aarch64")]
irq_chip: None,
sys_mem,
#[cfg(target_arch = "x86_64")]
sys_io,
sysbus,
vm_state: Arc::new((Mutex::new(VmState::Created), Condvar::new())),
boot_source: Arc::new(Mutex::new(vm_config.clone().boot_source)),
vm_config: Arc::new(Mutex::new(vm_config.clone())),
numa_nodes: None,
drive_files: Arc::new(Mutex::new(vm_config.init_drive_files()?)),
fwcfg_dev: None,
machine_ram,
hypervisor: hypervisor.clone(),
migration_hypervisor: hypervisor,
})
}
#[cfg(target_arch = "x86_64")]
fn pio_in(&self, addr: u64, mut data: &mut [u8]) -> bool {
// The function pit_calibrate_tsc() in kernel gets stuck if data read from
// io-port 0x61 is not 0x20.
// This problem only happens before Linux version 4.18 (fixed by 368a540e0)
if addr == 0x61 {
data[0] = 0x20;
return true;
}
if addr == 0x64 {
// UEFI will read PS2 Keyboard's Status register 0x64 to detect if
// this device is present.
data[0] = 0xFF;
}
let length = data.len() as u64;
self.sys_io
.read(&mut data, GuestAddress(addr), length)
.is_ok()
}
#[cfg(target_arch = "x86_64")]
fn pio_out(&self, addr: u64, mut data: &[u8]) -> bool {
use crate::x86_64::ich9_lpc::SLEEP_CTRL_OFFSET;
let count = data.len() as u64;
if addr == SLEEP_CTRL_OFFSET as u64 {
if let Err(e) = self.cpus[0].pause() {
log::error!("Fail to pause bsp, {:?}", e);
}
}
self.sys_io
.write(&mut data, GuestAddress(addr), count)
.is_ok()
}
fn mmio_read(&self, addr: u64, mut data: &mut [u8]) -> bool {
let length = data.len() as u64;
self.sys_mem
.read(&mut data, GuestAddress(addr), length)
.is_ok()
}
fn mmio_write(&self, addr: u64, mut data: &[u8]) -> bool {
let count = data.len() as u64;
self.sys_mem
.write(&mut data, GuestAddress(addr), count)
.is_ok()
}
}
macro_rules! create_device_add_matches {
( $command:expr; $controller: expr;
$(($($driver_name:tt)|+, $function_name:tt, $($arg:tt),*)),*;
$(#[cfg($($features: tt)*)]
($driver_name1:tt, $function_name1:tt, $($arg1:tt),*)),*
) => {
match $command {
$(
$($driver_name)|+ => {
$controller.$function_name($($arg),*)?;
},
)*
$(
#[cfg($($features)*)]
$driver_name1 => {
$controller.$function_name1($($arg1),*)?;
},
)*
_ => bail!("Unsupported device: {:?}", $command),
}
};
}
pub trait MachineOps {
fn machine_base(&self) -> &MachineBase;
fn machine_base_mut(&mut self) -> &mut MachineBase;
fn build_smbios(
&self,
fw_cfg: &Arc<Mutex<dyn FwCfgOps>>,
mem_array: Vec<(u64, u64)>,
) -> Result<()> {
let vm_config = self.get_vm_config();
let vmcfg_lock = vm_config.lock().unwrap();
let mut smbios = SmbiosTable::new();
let table = smbios.build_smbios_tables(
vmcfg_lock.smbios.clone(),
&vmcfg_lock.machine_config,
mem_array,
);
let ep = build_smbios_ep30(table.len() as u32);
let mut locked_fw_cfg = fw_cfg.lock().unwrap();
locked_fw_cfg
.add_file_entry(SMBIOS_TABLE_FILE, table)
.with_context(|| "Failed to add smbios table file entry")?;
locked_fw_cfg
.add_file_entry(SMBIOS_ANCHOR_FILE, ep)
.with_context(|| "Failed to add smbios anchor file entry")?;
Ok(())
}
#[cfg(target_arch = "x86_64")]
fn load_boot_source(&self, fwcfg: Option<&Arc<Mutex<dyn FwCfgOps>>>) -> Result<CPUBootConfig>;
#[cfg(target_arch = "aarch64")]
fn load_boot_source(
&self,
fwcfg: Option<&Arc<Mutex<dyn FwCfgOps>>>,
mem_start: u64,
) -> Result<CPUBootConfig> {
use boot_loader::{load_linux, BootLoaderConfig};
let mut boot_source = self.machine_base().boot_source.lock().unwrap();
let initrd = boot_source.initrd.as_ref().map(|b| b.initrd_file.clone());
let bootloader_config = BootLoaderConfig {
kernel: boot_source.kernel_file.clone(),
initrd,
mem_start,
};
let layout = load_linux(&bootloader_config, &self.machine_base().sys_mem, fwcfg)
.with_context(|| MachineError::LoadKernErr)?;
if let Some(rd) = &mut boot_source.initrd {
rd.initrd_addr = layout.initrd_start;
rd.initrd_size = layout.initrd_size;
}
Ok(CPUBootConfig {
fdt_addr: layout.dtb_start,
boot_pc: layout.boot_pc,
})
}
#[cfg(target_arch = "aarch64")]
fn load_cpu_features(&self, vmcfg: &VmConfig) -> Result<CPUFeatures> {
Ok((&vmcfg.machine_config.cpu_config).into())
}
/// Init memory of vm to architecture.
///
/// # Arguments
///
/// * `mem_size` - memory size of VM.
fn init_machine_ram(&self, sys_mem: &Arc<AddressSpace>, mem_size: u64) -> Result<()>;
fn create_machine_ram(&self, mem_config: &MachineMemConfig, thread_num: u8) -> Result<()> {
let root = self.get_vm_ram();
let numa_nodes = self.get_numa_nodes();
if numa_nodes.is_none() || mem_config.mem_zones.is_none() {
let default_mem = create_default_mem(mem_config, thread_num)?;
root.add_subregion_not_update(default_mem, 0_u64)?;
return Ok(());
}
let zones = mem_config.mem_zones.as_ref().unwrap();
let mut offset = 0_u64;
for (_, node) in numa_nodes.as_ref().unwrap().iter().enumerate() {
for zone in zones.iter() {
if zone.id.eq(&node.1.mem_dev) {
let ram = create_backend_mem(zone, thread_num)?;
root.add_subregion_not_update(ram, offset)?;
offset += zone.size;
break;
}
}
}
Ok(())
}
/// 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,
sys_mem: &Arc<AddressSpace>,
nr_cpus: u8,
) -> Result<()> {
let migrate_info = self.get_migrate_info();
if migrate_info.0 != MigrateMode::File {
self.create_machine_ram(mem_config, nr_cpus)?;
}
if migrate_info.0 != MigrateMode::File {
self.init_machine_ram(sys_mem, mem_config.mem_size)?;
}
MigrationManager::register_memory_instance(sys_mem.clone());
Ok(())
}
fn mem_show(&self) {
self.machine_base().sys_mem.memspace_show();
#[cfg(target_arch = "x86_64")]
self.machine_base().sys_io.memspace_show();
self.get_vm_ram().mtree(0_u32);
}
/// Create vcpu for virtual machine.
///
/// # Arguments
///
/// * `vcpu_id` - The id number of vcpu.
/// * `vm` - `MachineInterface` to obtain functions cpu can use.
/// * `max_cpus` - max cpu number of virtual machine.
fn create_vcpu(
vcpu_id: u8,
vm: Arc<Mutex<dyn MachineInterface + Send + Sync>>,
hypervisor: Arc<Mutex<dyn HypervisorOps>>,
#[cfg(target_arch = "x86_64")] max_cpus: u8,
) -> Result<Arc<CPU>>
where
Self: Sized,
{
let locked_hypervisor = hypervisor.lock().unwrap();
let hypervisor_cpu: Arc<dyn CPUHypervisorOps> =
locked_hypervisor.create_hypervisor_cpu(vcpu_id)?;
#[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(max_cpus));
let cpu = Arc::new(CPU::new(
hypervisor_cpu,
vcpu_id,
Arc::new(Mutex::new(arch_cpu)),
vm.clone(),
));
Ok(cpu)
}
/// Init vcpu register with boot message.
///
/// # Arguments
///
/// * `vm` - `MachineInterface` to obtain functions cpu can use.
/// * `nr_cpus` - The number of vcpus.
/// * `max_cpus` - The max number of vcpus.
/// * `boot_cfg` - Boot message generated by reading boot source to guest memory.
fn init_vcpu(
vm: Arc<Mutex<dyn MachineInterface + Send + Sync>>,
hypervisor: Arc<Mutex<dyn HypervisorOps>>,
nr_cpus: u8,
#[cfg(target_arch = "x86_64")] max_cpus: u8,
topology: &CPUTopology,
boot_cfg: &CPUBootConfig,
#[cfg(target_arch = "aarch64")] vcpu_cfg: &CPUFeatures,
) -> Result<Vec<Arc<CPU>>>
where
Self: Sized,
{
let mut cpus = Vec::<Arc<CPU>>::new();
for vcpu_id in 0..nr_cpus {
let cpu = Self::create_vcpu(
vcpu_id,
vm.clone(),
hypervisor.clone(),
#[cfg(target_arch = "x86_64")]
max_cpus,
)?;
cpus.push(cpu.clone());
MigrationManager::register_cpu_instance(cpu::ArchCPU::descriptor(), cpu, vcpu_id);
}
for (cpu_index, cpu) in cpus.iter().enumerate() {
cpu.realize(
boot_cfg,
topology,
#[cfg(target_arch = "aarch64")]
vcpu_cfg,
)
.with_context(|| {
format!(
"Failed to realize arch cpu register/features for CPU {}",
cpu_index
)
})?;
}
Ok(cpus)
}
/// Must be called after the CPUs have been realized and GIC has been created.
///
/// # Arguments
///
/// * `CPUFeatures` - The features of vcpu.
#[cfg(target_arch = "aarch64")]
fn cpu_post_init(&self, vcpu_cfg: &CPUFeatures) -> Result<()> {
if vcpu_cfg.pmu {
for cpu in self.machine_base().cpus.iter() {
cpu.hypervisor_cpu.init_pmu()?;
}
}
Ok(())
}
/// 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<()> {
Ok(())
}
/// Add Generic event device.
fn add_ged_device(&mut self) -> Result<()> {
Ok(())
}
/// 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)));
match parse_device_type(cfg_args)?.as_str() {
"vhost-vsock-device" => {
let device = VirtioMmioDevice::new(&sys_mem, vsock.clone());
MigrationManager::register_device_instance(
VirtioMmioState::descriptor(),
self.realize_virtio_mmio_device(device)
.with_context(|| MachineError::RlzVirtioMmioErr)?,
&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 mut virtio_pci_device = VirtioPciDevice::new(
device_cfg.id.clone(),
devfn,
sys_mem,
vsock.clone(),
parent_bus,
multi_func,
);
virtio_pci_device.enable_need_irqfd();
virtio_pci_device
.realize()
.with_context(|| "Failed to add virtio pci vsock device")?;
}
}
MigrationManager::register_device_instance(
VhostKern::VsockState::descriptor(),
vsock,
&device_cfg.id,
);
Ok(())
}
fn realize_virtio_mmio_device(
&mut self,
_dev: VirtioMmioDevice,
) -> Result<Arc<Mutex<VirtioMmioDevice>>> {
bail!("Virtio mmio devices not supported");
}
fn get_cpu_topo(&self) -> &CpuTopology {
&self.machine_base().cpu_topo
}
fn get_cpus(&self) -> &Vec<Arc<CPU>> {
&self.machine_base().cpus
}
fn get_sys_mem(&mut self) -> &Arc<AddressSpace> {
&self.machine_base().sys_mem
}
fn get_vm_config(&self) -> Arc<Mutex<VmConfig>> {
self.machine_base().vm_config.clone()
}
fn get_vm_state(&self) -> &Arc<(Mutex<VmState>, Condvar)> {
&self.machine_base().vm_state
}
fn get_vm_ram(&self) -> &Arc<Region> {
&self.machine_base().machine_ram
}
fn get_numa_nodes(&self) -> &Option<NumaNodes> {
&self.machine_base().numa_nodes
}
fn get_hypervisor(&self) -> Arc<Mutex<dyn HypervisorOps>> {
self.machine_base().hypervisor.clone()
}
/// Get migration mode and path from VM config. There are four modes in total:
/// Tcp, Unix, File and Unknown.
fn get_migrate_info(&self) -> Incoming {
if let Some((mode, path)) = self.get_vm_config().lock().unwrap().incoming.as_ref() {
return (*mode, path.to_string());
}
(MigrateMode::Unknown, String::new())
}
/// 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<()> {
if vm_config.dev_name.get("balloon").is_some() {
bail!("Only one balloon device is supported for each vm.");
}
let config = BalloonConfig::try_parse_from(str_slip_to_clap(cfg_args))?;
vm_config.dev_name.insert("balloon".to_string(), 1);
let sys_mem = self.get_sys_mem();
let balloon = Arc::new(Mutex::new(Balloon::new(config.clone(), sys_mem.clone())));
Balloon::object_init(balloon.clone());
match parse_device_type(cfg_args)?.as_str() {
"virtio-balloon-device" => {
if config.addr.is_some() || config.bus.is_some() || config.multifunction.is_some() {
bail!("virtio balloon device config is error!");
}
let device = VirtioMmioDevice::new(sys_mem, balloon);
self.realize_virtio_mmio_device(device)?;
}
_ => {
if config.addr.is_none() || config.bus.is_none() {
bail!("virtio balloon pci config is error!");
}
let bdf = PciBdf {
bus: config.bus.unwrap(),
addr: config.addr.unwrap(),
};
let multi_func = config.multifunction.unwrap_or_default();
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(
config.id, devfn, sys_mem, balloon, parent_bus, multi_func,
);
virtio_pci_device
.realize()
.with_context(|| "Failed to add virtio pci balloon device")?;
}
}
Ok(())
}
/// Add virtio serial device.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `cfg_args` - Device configuration args.
fn add_virtio_serial(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let serial_cfg = parse_virtio_serial(vm_config, cfg_args)?;
let sys_mem = self.get_sys_mem().clone();
let serial = Arc::new(Mutex::new(Serial::new(serial_cfg.clone())));
match parse_device_type(cfg_args)?.as_str() {
"virtio-serial-device" => {
let device = VirtioMmioDevice::new(&sys_mem, serial.clone());
MigrationManager::register_device_instance(
VirtioMmioState::descriptor(),
self.realize_virtio_mmio_device(device)
.with_context(|| MachineError::RlzVirtioMmioErr)?,
&serial_cfg.id,
);
}
_ => {
let bdf = serial_cfg.pci_bdf.unwrap();
let multi_func = serial_cfg.multifunction;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let virtio_pci_device = VirtioPciDevice::new(
serial_cfg.id.clone(),
devfn,
sys_mem,
serial.clone(),
parent_bus,
multi_func,
);
virtio_pci_device
.realize()
.with_context(|| "Failed to add virtio pci serial device")?;
}
}
MigrationManager::register_device_instance(
VirtioSerialState::descriptor(),
serial,
&serial_cfg.id,
);
Ok(())
}
/// Add virtio serial port.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `cfg_args` - Device configuration args.
fn add_virtio_serial_port(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let serial_cfg = vm_config
.virtio_serial
.as_ref()
.with_context(|| "No virtio serial device specified")?;
let mut virtio_device = None;
if serial_cfg.pci_bdf.is_none() {
// Micro_vm.
for dev in self.get_sys_bus().devices.iter() {
let locked_busdev = dev.lock().unwrap();
if locked_busdev.sysbusdev_base().dev_type == SysBusDevType::VirtioMmio {
let virtio_mmio_dev = locked_busdev
.as_any()
.downcast_ref::<VirtioMmioDevice>()
.unwrap();
if virtio_mmio_dev.device.lock().unwrap().device_type() == VIRTIO_TYPE_CONSOLE {
virtio_device = Some(virtio_mmio_dev.device.clone());
break;
}
}
}
} else {
// Standard_vm.
let pci_dev = self
.get_pci_dev_by_id_and_type(vm_config, Some(&serial_cfg.id), "virtio-serial-pci")
.with_context(|| {
format!(
"Can not find virtio serial pci device {} from pci bus",
serial_cfg.id
)
})?;
let locked_pcidev = pci_dev.lock().unwrap();
let virtio_pcidev = locked_pcidev
.as_any()
.downcast_ref::<VirtioPciDevice>()
.unwrap();
virtio_device = Some(virtio_pcidev.get_virtio_device().clone());
}
let virtio_dev = virtio_device.with_context(|| "No virtio serial device found")?;
let mut virtio_dev_h = virtio_dev.lock().unwrap();
let serial = virtio_dev_h.as_any_mut().downcast_mut::<Serial>().unwrap();
let is_console = matches!(parse_device_type(cfg_args)?.as_str(), "virtconsole");
let free_port0 = find_port_by_nr(&serial.ports, 0).is_none();
// Note: port 0 is reserved for a virtconsole.
let free_nr = get_max_nr(&serial.ports) + 1;
let serialport_cfg =
parse_virtserialport(vm_config, cfg_args, is_console, free_nr, free_port0)?;
if serialport_cfg.nr >= serial.max_nr_ports {
bail!(
"virtio serial port nr {} should be less than virtio serial's max_nr_ports {}",
serialport_cfg.nr,
serial.max_nr_ports
);
}
if find_port_by_nr(&serial.ports, serialport_cfg.nr).is_some() {
bail!("Repetitive virtio serial port nr {}.", serialport_cfg.nr,);
}
let mut serial_port = SerialPort::new(serialport_cfg);
let port = Arc::new(Mutex::new(serial_port.clone()));
serial_port.realize()?;
if !is_console {
serial_port.chardev.lock().unwrap().set_device(port.clone());
}
serial.ports.lock().unwrap().push(port);
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())));
match parse_device_type(cfg_args)?.as_str() {
"virtio-rng-device" => {
let device = VirtioMmioDevice::new(sys_mem, rng_dev.clone());
self.realize_virtio_mmio_device(device)
.with_context(|| "Failed to add virtio mmio rng device")?;
}
_ => {
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.clone(),
devfn,
sys_mem,
rng_dev.clone(),
parent_bus,
multi_func,
);
vitio_pci_device
.realize()
.with_context(|| "Failed to add pci rng device")?;
}
}
MigrationManager::register_device_instance(RngState::descriptor(), rng_dev, &device_cfg.id);
Ok(())
}
fn get_pci_host(&mut self) -> Result<&Arc<Mutex<PciHost>>> {
bail!("No pci host found");
}
/// Add virtioFs device.
///
/// # Arguments
///
/// * 'vm_config' - VM configuration.
/// * 'cfg_args' - Device configuration arguments.
fn add_virtio_fs(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let dev_cfg = parse_fs(vm_config, cfg_args)?;
let id_clone = dev_cfg.id.clone();
let sys_mem = self.get_sys_mem().clone();
if !vm_config.machine_config.mem_config.mem_share {
bail!("When configuring the vhost-user-fs-device or vhost-user-fs-pci device, the memory must be shared.");
}
match parse_device_type(cfg_args)?.as_str() {
"vhost-user-fs-device" => {
let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone())));
let virtio_mmio_device = VirtioMmioDevice::new(&sys_mem, device);
self.realize_virtio_mmio_device(virtio_mmio_device)
.with_context(|| "Failed to add vhost user fs device")?;
}
_ => {
let device = Arc::new(Mutex::new(vhost::user::Fs::new(dev_cfg, sys_mem.clone())));
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 mut vitio_pci_device =
VirtioPciDevice::new(id_clone, devfn, sys_mem, device, parent_bus, multi_func);
vitio_pci_device.enable_need_irqfd();
vitio_pci_device
.realize()
.with_context(|| "Failed to add pci fs device")?;
}
}
Ok(())
}
fn get_sys_bus(&mut self) -> &SysBus {
&self.machine_base().sysbus
}
fn get_fwcfg_dev(&mut self) -> Option<Arc<Mutex<dyn FwCfgOps>>> {
self.machine_base().fwcfg_dev.clone()
}
fn get_boot_order_list(&self) -> Option<Arc<Mutex<Vec<BootIndexInfo>>>> {
None
}
fn reset_all_devices(&mut self) -> Result<()> {
let sysbus = self.get_sys_bus();
for dev in sysbus.devices.iter() {
dev.lock()
.unwrap()
.reset()
.with_context(|| "Fail to reset sysbus device")?;
}
if let Ok(pci_host) = self.get_pci_host() {
pci_host
.lock()
.unwrap()
.reset()
.with_context(|| "Fail to reset pci host")?;
}
Ok(())
}
fn check_id_existed_in_xhci(&mut self, id: &str) -> Result<bool> {
let vm_config = self.get_vm_config();
let locked_vmconfig = vm_config.lock().unwrap();
let parent_dev = self
.get_pci_dev_by_id_and_type(&locked_vmconfig, None, "nec-usb-xhci")
.with_context(|| "Can not find parent device from pci bus")?;
let locked_parent_dev = parent_dev.lock().unwrap();
let xhci_pci = locked_parent_dev
.as_any()
.downcast_ref::<XhciPciDevice>()
.with_context(|| "PciDevOps can not downcast to XhciPciDevice")?;
let mut locked_xhci = xhci_pci.xhci.lock().unwrap();
let port = locked_xhci.find_usb_port_by_id(id);
Ok(port.is_some())
}
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);
}
if self.check_id_existed_in_xhci(name).unwrap_or_default() {
bail!("Device id {} existed in xhci", name);
}
}
Ok(())
}
fn reset_fwcfg_boot_order(&mut self) -> Result<()> {
// SAFETY: unwrap is safe because stand machine always make sure it not return null.
let boot_order_vec = self.get_boot_order_list().unwrap();
let mut locked_boot_order_vec = boot_order_vec.lock().unwrap().clone();
if locked_boot_order_vec.is_empty() {
return Ok(());
}
locked_boot_order_vec.sort_by(|x, y| x.boot_index.cmp(&y.boot_index));
let mut fwcfg_boot_order_string = String::new();
for item in &locked_boot_order_vec {
fwcfg_boot_order_string.push_str(&item.dev_path);
fwcfg_boot_order_string.push('\n');
}
fwcfg_boot_order_string.push('\0');
let fwcfg = self.get_fwcfg_dev();
if fwcfg.is_none() {
warn!("Direct kernel boot mode don't support set boot order");
return Ok(());
}
fwcfg
.unwrap()
.lock()
.unwrap()
.modify_file_entry("bootorder", fwcfg_boot_order_string.as_bytes().to_vec())
.with_context(|| "Fail to add bootorder entry for standard VM.")?;
Ok(())
}
/// Check the boot index of device is duplicated or not.
///
/// # Arguments
///
/// * `bootindex` - The boot index of the device.
fn check_bootindex(&mut self, boot_index: u8) -> Result<()> {
// SAFETY: Unwrap is safe because StdMachine will overwrite this function,
// which ensure boot_order_list is not None.
let boot_order_list = self.get_boot_order_list().unwrap();
if boot_order_list
.lock()
.unwrap()
.iter()
.any(|item| item.boot_index == boot_index)
{
bail!("Failed to add duplicated bootindex {}.", boot_index);
}
Ok(())
}
/// Add boot index of device.
///
/// # Arguments
///
/// * `bootindex` - The boot index of the device.
/// * `dev_path` - The firmware device path of the device.
/// * `dev_id` - The id of the device.
fn add_bootindex_devices(&mut self, boot_index: u8, dev_path: &str, dev_id: &str) {
// SAFETY: Unwrap is safe because StdMachine will overwrite this function,
// which ensure boot_order_list is not None.
let boot_order_list = self.get_boot_order_list().unwrap();
boot_order_list.lock().unwrap().push(BootIndexInfo {
boot_index,
id: dev_id.to_string(),
dev_path: dev_path.to_string(),
});
}
/// Delete boot index of device.
///
/// # Arguments
///
/// * `dev_id` - The id of the device.
fn del_bootindex_devices(&self, dev_id: &str) {
// Unwrap is safe because StdMachine will overwrite this function,
// which ensure boot_order_list is not None.
let boot_order_list = self.get_boot_order_list().unwrap();
let mut locked_boot_order_list = boot_order_list.lock().unwrap();
locked_boot_order_list.retain(|item| item.id != dev_id);
}
#[cfg(feature = "pvpanic")]
fn add_pvpanic(&mut self, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let device_cfg = parse_pvpanic(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let pcidev = PvPanicPci::new(&device_cfg, devfn, parent_bus);
pcidev
.realize()
.with_context(|| "Failed to realize pvpanic device")?;
Ok(())
}
fn add_virtio_pci_blk(
&mut self,
vm_config: &mut VmConfig,
cfg_args: &str,
hotplug: bool,
) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num(
0,
vm_config.machine_config.nr_cpus,
MAX_VIRTIO_QUEUE,
));
let device_cfg = parse_blk(vm_config, cfg_args, queues_auto)?;
if let Some(bootindex) = device_cfg.boot_index {
self.check_bootindex(bootindex)
.with_context(|| "Fail to add virtio pci blk device for invalid bootindex")?;
}
let device = Arc::new(Mutex::new(Block::new(
device_cfg.clone(),
self.get_drive_files(),
)));
let pci_dev = self
.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false)
.with_context(|| "Failed to add virtio pci device")?;
if let Some(bootindex) = device_cfg.boot_index {
// Eg: OpenFirmware device path(virtio-blk disk):
// /pci@i0cf8/scsi@6[,3]/disk@0,0
// | | | | |
// | | | | |
// | | | fixed 0.
// | PCI slot,[function] holding disk.
// PCI root as system bus port.
if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() {
self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id);
}
}
MigrationManager::register_device_instance(
BlockState::descriptor(),
device,
&device_cfg.id,
);
if !hotplug {
self.reset_bus(&device_cfg.id)?;
}
Ok(())
}
fn add_virtio_pci_scsi(
&mut self,
vm_config: &mut VmConfig,
cfg_args: &str,
hotplug: bool,
) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num(
0,
vm_config.machine_config.nr_cpus,
MAX_VIRTIO_QUEUE,
));
let device_cfg = parse_scsi_controller(cfg_args, queues_auto)?;
let device = Arc::new(Mutex::new(ScsiCntlr::new(device_cfg.clone())));
let bus_name = format!("{}.0", device_cfg.id);
scsi_cntlr_create_scsi_bus(&bus_name, &device)?;
let pci_dev = self
.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, false)
.with_context(|| "Failed to add virtio scsi controller")?;
if !hotplug {
self.reset_bus(&device_cfg.id)?;
}
device.lock().unwrap().config.boot_prefix = pci_dev.lock().unwrap().get_dev_path();
Ok(())
}
fn add_scsi_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let device_cfg = parse_scsi_device(vm_config, cfg_args)?;
let scsi_type = match parse_device_type(cfg_args)?.as_str() {
"scsi-hd" => SCSI_TYPE_DISK,
_ => SCSI_TYPE_ROM,
};
if let Some(bootindex) = device_cfg.boot_index {
self.check_bootindex(bootindex)
.with_context(|| "Failed to add scsi device for invalid bootindex")?;
}
let device = Arc::new(Mutex::new(ScsiDevice::new(
device_cfg.clone(),
scsi_type,
self.get_drive_files(),
)));
let pci_dev = self
.get_pci_dev_by_id_and_type(vm_config, Some(&device_cfg.cntlr), "virtio-scsi-pci")
.with_context(|| {
format!(
"Can not find scsi controller from pci bus {}",
device_cfg.cntlr
)
})?;
let locked_pcidev = pci_dev.lock().unwrap();
let virtio_pcidev = locked_pcidev
.as_any()
.downcast_ref::<VirtioPciDevice>()
.unwrap();
let virtio_device = virtio_pcidev.get_virtio_device().lock().unwrap();
let cntlr = virtio_device.as_any().downcast_ref::<ScsiCntlr>().unwrap();
let bus = cntlr.bus.as_ref().unwrap();
if bus
.lock()
.unwrap()
.devices
.contains_key(&(device_cfg.target, device_cfg.lun))
{
bail!("Wrong! Two scsi devices have the same scsi-id and lun");
}
let iothread = cntlr.config.iothread.clone();
device.lock().unwrap().realize(iothread)?;
bus.lock()
.unwrap()
.devices
.insert((device_cfg.target, device_cfg.lun), device.clone());
device.lock().unwrap().parent_bus = Arc::downgrade(bus);
if let Some(bootindex) = device_cfg.boot_index {
// Eg: OpenFirmware device path(virtio-scsi disk):
// /pci@i0cf8/scsi@7[,3]/channel@0/disk@2,3
// | | | | | |
// | | | | target,lun.
// | | | channel(unused, fixed 0).
// | PCI slot,[function] holding SCSI controller.
// PCI root as system bus port.
let prefix = cntlr.config.boot_prefix.as_ref().unwrap();
let dev_path =
format! {"{}/channel@0/disk@{:x},{:x}", prefix, device_cfg.target, device_cfg.lun};
self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id);
}
Ok(())
}
fn add_virtio_pci_net(
&mut self,
vm_config: &mut VmConfig,
cfg_args: &str,
hotplug: bool,
) -> 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 mut need_irqfd = false;
let device: Arc<Mutex<dyn VirtioDevice>> = if device_cfg.vhost_type.is_some() {
need_irqfd = true;
if device_cfg.vhost_type == Some(String::from("vhost-kernel")) {
Arc::new(Mutex::new(VhostKern::Net::new(
&device_cfg,
self.get_sys_mem(),
)))
} else {
Arc::new(Mutex::new(VhostUser::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(
VirtioNetState::descriptor(),
device.clone(),
&device_cfg.id,
);
device
};
self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, need_irqfd)?;
if !hotplug {
self.reset_bus(&device_cfg.id)?;
}
Ok(())
}
fn add_vhost_user_blk_pci(
&mut self,
vm_config: &mut VmConfig,
cfg_args: &str,
hotplug: bool,
) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let queues_auto = Some(VirtioPciDevice::virtio_pci_auto_queues_num(
0,
vm_config.machine_config.nr_cpus,
MAX_VIRTIO_QUEUE,
));
let device_cfg = parse_vhost_user_blk(vm_config, cfg_args, queues_auto)?;
let device: Arc<Mutex<dyn VirtioDevice>> = Arc::new(Mutex::new(VhostUser::Block::new(
&device_cfg,
self.get_sys_mem(),
)));
let pci_dev = self
.add_virtio_pci_device(&device_cfg.id, &bdf, device.clone(), multi_func, true)
.with_context(|| {
format!(
"Failed to add virtio pci device, device id: {}",
&device_cfg.id
)
})?;
if let Some(bootindex) = device_cfg.boot_index {
if let Some(dev_path) = pci_dev.lock().unwrap().get_dev_path() {
self.add_bootindex_devices(bootindex, &dev_path, &device_cfg.id);
}
}
if !hotplug {
self.reset_bus(&device_cfg.id)?;
}
Ok(())
}
fn add_vhost_user_blk_device(
&mut self,
vm_config: &mut VmConfig,
cfg_args: &str,
) -> Result<()> {
let device_cfg = parse_vhost_user_blk(vm_config, cfg_args, None)?;
let device: Arc<Mutex<dyn VirtioDevice>> = Arc::new(Mutex::new(VhostUser::Block::new(
&device_cfg,
self.get_sys_mem(),
)));
let virtio_mmio_device = VirtioMmioDevice::new(self.get_sys_mem(), device);
self.realize_virtio_mmio_device(virtio_mmio_device)
.with_context(|| "Failed to add vhost user block device")?;
Ok(())
}
fn create_vfio_pci_device(
&mut self,
id: &str,
bdf: &PciBdf,
host: &str,
sysfsdev: &str,
multifunc: bool,
) -> Result<()> {
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?;
let path = if !host.is_empty() {
format!("/sys/bus/pci/devices/{}", host)
} else {
sysfsdev.to_string()
};
let device = VfioDevice::new(Path::new(&path), self.get_sys_mem())
.with_context(|| "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).with_context(|| "Failed to realize vfio-pci device.")?;
Ok(())
}
fn add_vfio_device(&mut self, cfg_args: &str, hotplug: bool) -> Result<()> {
let hypervisor = self.get_hypervisor();
let locked_hypervisor = hypervisor.lock().unwrap();
*KVM_DEVICE_FD.lock().unwrap() = locked_hypervisor.create_vfio_device();
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,
&device_cfg.sysfsdev,
multifunc,
)?;
if !hotplug {
self.reset_bus(&device_cfg.id)?;
}
Ok(())
}
#[cfg(all(target_env = "ohos", feature = "ohui_srv"))]
fn update_ohui_srv(&mut self, _passthru: bool) {}
#[cfg(all(target_env = "ohos", feature = "ohui_srv"))]
fn get_ohui_fb(&self) -> Option<FileBackend> {
None
}
#[cfg(feature = "virtio_gpu")]
fn add_virtio_pci_gpu(&mut self, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let multi_func = get_multi_function(cfg_args)?;
let device_cfg = parse_gpu(cfg_args)?;
let device = Arc::new(Mutex::new(Gpu::new(device_cfg.clone())));
#[cfg(all(target_env = "ohos", feature = "ohui_srv"))]
if device.lock().unwrap().device_quirk() == Some(VirtioDeviceQuirk::VirtioGpuEnableBar0)
&& self.get_ohui_fb().is_some()
{
self.update_ohui_srv(true);
device.lock().unwrap().set_bar0_fb(self.get_ohui_fb());
}
self.add_virtio_pci_device(&device_cfg.id, &bdf, device, multi_func, false)?;
Ok(())
}
fn get_devfn_and_parent_bus(&mut self, bdf: &PciBdf) -> Result<(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.", &device_cfg.id);
}
let rootport = RootPort::new(
device_cfg.id,
devfn,
device_cfg.port,
parent_bus,
device_cfg.multifunction,
);
rootport
.realize()
.with_context(|| "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,
need_irqfd: bool,
) -> Result<Arc<Mutex<dyn PciDevOps>>> {
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(bdf)?;
let sys_mem = self.get_sys_mem();
let mut pcidev = VirtioPciDevice::new(
id.to_string(),
devfn,
sys_mem.clone(),
device,
parent_bus,
multi_func,
);
if need_irqfd {
pcidev.enable_need_irqfd();
}
let clone_pcidev = Arc::new(Mutex::new(pcidev.clone()));
pcidev
.realize()
.with_context(|| "Failed to add virtio pci device")?;
Ok(clone_pcidev)
}
/// 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 = PciBus::find_attached_bus(&locked_pci_host.root_bus, dev_id)
.with_context(|| format!("Bus not found, dev id {}", dev_id))?
.0;
let locked_bus = bus.lock().unwrap();
if locked_bus.name == "pcie.0" {
// No need to reset root bus
return Ok(());
}
let parent_bridge = locked_bus
.parent_bridge
.as_ref()
.with_context(|| format!("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)
.with_context(|| "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 numa nodes information to standard machine.
///
/// # Arguments
///
/// * `vm_config` - VM Configuration.
fn add_numa_nodes(&mut self, vm_config: &mut VmConfig) -> Result<Option<NumaNodes>> {
if vm_config.numa_nodes.is_empty() {
return Ok(None);
}
let mut numa_nodes: NumaNodes = BTreeMap::new();
vm_config.numa_nodes.sort_by(|p, n| n.0.cmp(&p.0));
for numa in vm_config.numa_nodes.iter() {
match numa.0.as_str() {
"node" => {
let numa_config: NumaConfig = parse_numa_mem(numa.1.as_str())?;
if numa_nodes.contains_key(&numa_config.numa_id) {
bail!("Numa node id is repeated {}", numa_config.numa_id);
}
let mut numa_node = NumaNode {
cpus: numa_config.cpus,
mem_dev: numa_config.mem_dev.clone(),
..Default::default()
};
numa_node.size = vm_config
.object
.mem_object
.remove(&numa_config.mem_dev)
.map(|mem_conf| mem_conf.size)
.with_context(|| {
format!(
"Object for memory-backend {} config not found",
numa_config.mem_dev
)
})?;
numa_nodes.insert(numa_config.numa_id, numa_node);
}
"dist" => {
let dist: (u32, NumaDistance) = parse_numa_distance(numa.1.as_str())?;
if !numa_nodes.contains_key(&dist.0) {
bail!("Numa node id is not found {}", dist.0);
}
if !numa_nodes.contains_key(&dist.1.destination) {
bail!("Numa node id is not found {}", dist.1.destination);
}
if let Some(n) = numa_nodes.get_mut(&dist.0) {
if n.distances.contains_key(&dist.1.destination) {
bail!(
"Numa destination info {} repeat settings",
dist.1.destination
);
}
n.distances.insert(dist.1.destination, dist.1.distance);
}
}
_ => {
bail!("Unsupported args for NUMA node: {}", numa.0.as_str());
}
}
}
// Complete user parameters if necessary.
complete_numa_node(
&mut numa_nodes,
vm_config.machine_config.nr_cpus,
vm_config.machine_config.mem_config.mem_size,
)?;
Ok(Some(numa_nodes))
}
/// Add usb xhci controller.
///
/// # Arguments
///
/// * `cfg_args` - XHCI Configuration.
fn add_usb_xhci(&mut self, cfg_args: &str) -> Result<()> {
let device_cfg = XhciConfig::try_parse_from(str_slip_to_clap(cfg_args))?;
let bdf = PciBdf::new(device_cfg.bus.clone(), device_cfg.addr);
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let pcidev = XhciPciDevice::new(&device_cfg, devfn, parent_bus, self.get_sys_mem());
pcidev
.realize()
.with_context(|| "Failed to realize usb xhci device")?;
Ok(())
}
/// Add scream sound based on ivshmem.
///
/// # Arguments
///
/// * `cfg_args` - scream configuration.
#[cfg(feature = "scream")]
fn add_ivshmem_scream(
&mut self,
vm_config: &mut VmConfig,
cfg_args: &str,
token_id: Option<Arc<RwLock<u64>>>,
) -> Result<()> {
let config = ScreamConfig::try_parse_from(str_slip_to_clap(cfg_args))?;
let bdf = PciBdf {
bus: config.bus.clone(),
addr: config.addr,
};
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let mem_cfg = vm_config
.object
.mem_object
.remove(&config.memdev)
.with_context(|| {
format!(
"Object for memory-backend-ram {} config not found",
config.memdev
)
})?;
if !mem_cfg.share {
bail!("Object for share config is not on");
}
let scream = Scream::new(mem_cfg.size, config, token_id);
scream
.realize(devfn, parent_bus)
.with_context(|| "Failed to realize scream device")
}
/// Get the corresponding device from the PCI bus based on the device id and device type name.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `id` - Device id.
/// * `dev_type` - Device type name.
fn get_pci_dev_by_id_and_type(
&mut self,
vm_config: &VmConfig,
id: Option<&str>,
dev_type: &str,
) -> Option<Arc<Mutex<dyn PciDevOps>>> {
let (id_check, id_str) = if id.is_some() {
(true, format! {"id={}", id.unwrap()})
} else {
(false, "".to_string())
};
for dev in &vm_config.devices {
if dev.0.as_str() != dev_type || id_check && !dev.1.contains(&id_str) {
continue;
}
let cfg_args = dev.1.as_str();
let bdf = get_pci_bdf(cfg_args).ok()?;
let devfn = (bdf.addr.0 << 3) + bdf.addr.1;
let pci_host = self.get_pci_host().ok()?;
let root_bus = pci_host.lock().unwrap().root_bus.clone();
if let Some(pci_bus) = PciBus::find_bus_by_name(&root_bus, &bdf.bus) {
return pci_bus.lock().unwrap().get_device(0, devfn);
} else {
return None;
}
}
None
}
/// Attach usb device to xhci controller.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `usb_dev` - Usb device.
fn attach_usb_to_xhci_controller(
&mut self,
vm_config: &mut VmConfig,
usb_dev: Arc<Mutex<dyn UsbDevice>>,
) -> Result<()> {
let parent_dev = self
.get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci")
.with_context(|| "Can not find parent device from pci bus")?;
let locked_parent_dev = parent_dev.lock().unwrap();
let xhci_pci = locked_parent_dev
.as_any()
.downcast_ref::<XhciPciDevice>()
.with_context(|| "PciDevOps can not downcast to XhciPciDevice")?;
xhci_pci.attach_device(&(usb_dev))?;
Ok(())
}
/// Detach usb device from xhci controller.
///
/// # Arguments
///
/// * `vm_config` - VM configuration.
/// * `id` - id of the usb device.
fn detach_usb_from_xhci_controller(
&mut self,
vm_config: &mut VmConfig,
id: String,
) -> Result<()> {
let parent_dev = self
.get_pci_dev_by_id_and_type(vm_config, None, "nec-usb-xhci")
.with_context(|| "Can not find parent device from pci bus")?;
let locked_parent_dev = parent_dev.lock().unwrap();
let xhci_pci = locked_parent_dev
.as_any()
.downcast_ref::<XhciPciDevice>()
.with_context(|| "PciDevOps can not downcast to XhciPciDevice")?;
xhci_pci.detach_device(id)?;
Ok(())
}
/// Add usb device.
///
/// # Arguments
///
/// * `driver` - USB device class.
/// * `cfg_args` - USB device Configuration.
fn add_usb_device(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let usb_device = match parse_device_type(cfg_args)?.as_str() {
"usb-kbd" => {
let config = UsbKeyboardConfig::try_parse_from(str_slip_to_clap(cfg_args))?;
let keyboard = UsbKeyboard::new(config);
keyboard
.realize()
.with_context(|| "Failed to realize usb keyboard device")?
}
"usb-tablet" => {
let config = UsbTabletConfig::try_parse_from(str_slip_to_clap(cfg_args))?;
let tablet = UsbTablet::new(config);
tablet
.realize()
.with_context(|| "Failed to realize usb tablet device")?
}
#[cfg(feature = "usb_camera")]
"usb-camera" => {
let config = UsbCameraConfig::try_parse_from(str_slip_to_clap(cfg_args))?;
let cameradev = get_cameradev_by_id(vm_config, config.cameradev.clone())
.with_context(|| {
format!(
"no cameradev found with id {:?} for usb-camera",
config.cameradev
)
})?;
let camera = UsbCamera::new(config, cameradev)?;
camera
.realize()
.with_context(|| "Failed to realize usb camera device")?
}
"usb-storage" => {
let device_cfg = parse_usb_storage(vm_config, cfg_args)?;
let storage = UsbStorage::new(device_cfg, self.get_drive_files());
storage
.realize()
.with_context(|| "Failed to realize usb storage device")?
}
#[cfg(feature = "usb_host")]
"usb-host" => {
let config = UsbHostConfig::try_parse_from(str_slip_to_clap(cfg_args))?;
let usbhost = UsbHost::new(config)?;
usbhost
.realize()
.with_context(|| "Failed to realize usb host device")?
}
_ => bail!("Unknown usb device classes."),
};
self.attach_usb_to_xhci_controller(vm_config, usb_device)?;
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,
)
.with_context(|| MachineError::AddDevErr("RTC".to_string()))?;
self.add_ged_device()
.with_context(|| MachineError::AddDevErr("Ged".to_string()))?;
let cloned_vm_config = vm_config.clone();
if let Some(serial) = cloned_vm_config.serial.as_ref() {
self.add_serial_device(serial)
.with_context(|| MachineError::AddDevErr("serial".to_string()))?;
}
if let Some(pflashs) = cloned_vm_config.pflashs.as_ref() {
self.add_pflash_device(pflashs)
.with_context(|| MachineError::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)
.with_context(|| format!("Failed to check device id: config {}", cfg_args))?;
#[cfg(feature = "scream")]
let token_id = self.get_token_id();
create_device_add_matches!(
dev.0.as_str(); self;
("virtio-blk-device", add_virtio_mmio_block, vm_config, cfg_args),
("virtio-blk-pci", add_virtio_pci_blk, vm_config, cfg_args, false),
("virtio-scsi-pci", add_virtio_pci_scsi, vm_config, cfg_args, false),
("scsi-hd" | "scsi-cd", add_scsi_device, vm_config, cfg_args),
("virtio-net-device", add_virtio_mmio_net, vm_config, cfg_args),
("virtio-net-pci", add_virtio_pci_net, vm_config, cfg_args, false),
("pcie-root-port", add_pci_root_port, cfg_args),
("vhost-vsock-pci" | "vhost-vsock-device", add_virtio_vsock, cfg_args),
("virtio-balloon-device" | "virtio-balloon-pci", add_virtio_balloon, vm_config, cfg_args),
("virtio-serial-device" | "virtio-serial-pci", add_virtio_serial, vm_config, cfg_args),
("virtconsole" | "virtserialport", add_virtio_serial_port, vm_config, cfg_args),
("virtio-rng-device" | "virtio-rng-pci", add_virtio_rng, vm_config, cfg_args),
("vfio-pci", add_vfio_device, cfg_args, false),
("vhost-user-blk-device",add_vhost_user_blk_device, vm_config, cfg_args),
("vhost-user-blk-pci",add_vhost_user_blk_pci, vm_config, cfg_args, false),
("vhost-user-fs-pci" | "vhost-user-fs-device", add_virtio_fs, vm_config, cfg_args),
("nec-usb-xhci", add_usb_xhci, cfg_args),
("usb-kbd" | "usb-storage" | "usb-tablet" | "usb-camera" | "usb-host", add_usb_device, vm_config, cfg_args);
#[cfg(feature = "virtio_gpu")]
("virtio-gpu-pci", add_virtio_pci_gpu, cfg_args),
#[cfg(feature = "ramfb")]
("ramfb", add_ramfb, cfg_args),
#[cfg(feature = "demo_device")]
("pcie-demo-dev", add_demo_dev, vm_config, cfg_args),
#[cfg(feature = "scream")]
("ivshmem-scream", add_ivshmem_scream, vm_config, cfg_args, token_id),
#[cfg(feature = "pvpanic")]
("pvpanic", add_pvpanic, cfg_args)
);
}
Ok(())
}
fn get_token_id(&self) -> Option<Arc<RwLock<u64>>> {
None
}
fn add_pflash_device(&mut self, _configs: &[PFlashConfig]) -> Result<()> {
bail!("Pflash device is not supported!");
}
fn add_ramfb(&mut self, _cfg_args: &str) -> Result<()> {
bail!("ramfb device is not supported!");
}
fn display_init(&mut self, _vm_config: &mut VmConfig) -> Result<()> {
bail!("Display is not supported.");
}
#[cfg(feature = "demo_device")]
fn add_demo_dev(&mut self, vm_config: &mut VmConfig, cfg_args: &str) -> Result<()> {
let bdf = get_pci_bdf(cfg_args)?;
let (devfn, parent_bus) = self.get_devfn_and_parent_bus(&bdf)?;
let demo_cfg = parse_demo_dev(vm_config, cfg_args.to_string())
.with_context(|| "failed to parse cmdline for demo dev.")?;
let sys_mem = self.get_sys_mem().clone();
let demo_dev = DemoDev::new(demo_cfg, devfn, sys_mem, parent_bus);
demo_dev.realize()
}
/// 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);
}
if let Ok(cov_enable) = std::env::var("STRATOVIRT_COV") {
if cov_enable.eq("on") {
coverage_allow_list(&mut bpf_rules);
}
}
for bpf_rule in &mut bpf_rules {
seccomp_filter.push(bpf_rule);
}
seccomp_filter
.realize()
.with_context(|| "Failed to init seccomp filter.")?;
Ok(())
}
/// Get the drive backend files.
fn get_drive_files(&self) -> Arc<Mutex<HashMap<String, DriveFile>>> {
self.machine_base().drive_files.clone()
}
/// Fetch a cloned file from drive backend files.
fn fetch_drive_file(&self, path: &str) -> Result<File> {
let files = self.get_drive_files();
let drive_files = files.lock().unwrap();
VmConfig::fetch_drive_file(&drive_files, path)
}
/// Register a new drive backend file.
fn register_drive_file(
&self,
id: &str,
path: &str,
read_only: bool,
direct: bool,
) -> Result<()> {
let files = self.get_drive_files();
let mut drive_files = files.lock().unwrap();
VmConfig::add_drive_file(&mut drive_files, id, path, read_only, direct)?;
// Lock the added file if VM is running.
let drive_file = drive_files.get_mut(path).unwrap();
let vm_state = self.get_vm_state().deref().0.lock().unwrap();
if *vm_state == VmState::Running && !drive_file.locked {
if let Err(e) = lock_file(&drive_file.file, path, read_only) {
VmConfig::remove_drive_file(&mut drive_files, path)?;
return Err(e);
}
drive_file.locked = true;
}
Ok(())
}
/// Unregister a drive backend file.
fn unregister_drive_file(&self, path: &str) -> Result<()> {
let files = self.get_drive_files();
let mut drive_files = files.lock().unwrap();
VmConfig::remove_drive_file(&mut drive_files, path)
}
/// Active drive backend files. i.e., Apply lock.
fn active_drive_files(&self) -> Result<()> {
for drive_file in self.get_drive_files().lock().unwrap().values_mut() {
if drive_file.locked {
continue;
}
lock_file(&drive_file.file, &drive_file.path, drive_file.read_only)?;
drive_file.locked = true;
}
Ok(())
}
/// Deactive drive backend files. i.e., Release lock.
fn deactive_drive_files(&self) -> Result<()> {
for drive_file in self.get_drive_files().lock().unwrap().values_mut() {
if !drive_file.locked {
continue;
}
unlock_file(&drive_file.file, &drive_file.path)?;
drive_file.locked = false;
}
Ok(())
}
/// Realize the machine.
///
/// # Arguments
///
/// * `vm` - The machine structure.
/// * `vm_config` - VM configuration.
fn realize(vm: &Arc<Mutex<Self>>, vm_config: &mut VmConfig) -> 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<()> {
self.vm_start(
paused,
&self.machine_base().cpus,
&mut self.machine_base().vm_state.0.lock().unwrap(),
)
}
/// 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 state.
fn vm_start(&self, paused: bool, cpus: &[Arc<CPU>], vm_state: &mut VmState) -> Result<()> {
if !paused {
EventLoop::get_ctx(None).unwrap().enable_clock();
self.active_drive_files()?;
}
let nr_vcpus = cpus.len();
let cpus_thread_barrier = Arc::new(Barrier::new(nr_vcpus + 1));
for (cpu_index, cpu) in cpus.iter().enumerate() {
let cpu_thread_barrier = cpus_thread_barrier.clone();
if let Err(e) = CPU::start(cpu.clone(), cpu_thread_barrier, paused) {
self.deactive_drive_files()?;
return Err(anyhow!("Failed to run vcpu{}, {:?}", cpu_index, e));
}
}
if paused {
*vm_state = VmState::Paused;
} else {
*vm_state = VmState::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 state.
fn vm_pause(
&self,
cpus: &[Arc<CPU>],
#[cfg(target_arch = "aarch64")] irq_chip: &Option<Arc<InterruptController>>,
vm_state: &mut VmState,
) -> Result<()> {
EventLoop::get_ctx(None).unwrap().disable_clock();
self.deactive_drive_files()?;
for (cpu_index, cpu) in cpus.iter().enumerate() {
if let Err(e) = cpu.pause() {
self.active_drive_files()?;
return Err(anyhow!("Failed to pause vcpu{}, {:?}", cpu_index, e));
}
}
#[cfg(target_arch = "aarch64")]
// SAFETY: ARM architecture must have interrupt controllers in user mode.
irq_chip.as_ref().unwrap().stop();
*vm_state = VmState::Paused;
Ok(())
}
/// Resume VM as `Running` state, awaken all vcpu thread.
///
/// # Arguments
///
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm state.
fn vm_resume(&self, cpus: &[Arc<CPU>], vm_state: &mut VmState) -> Result<()> {
EventLoop::get_ctx(None).unwrap().enable_clock();
self.active_drive_files()?;
for (cpu_index, cpu) in cpus.iter().enumerate() {
if let Err(e) = cpu.resume() {
self.deactive_drive_files()?;
return Err(anyhow!("Failed to resume vcpu{}, {:?}", cpu_index, e));
}
}
*vm_state = VmState::Running;
Ok(())
}
/// Destroy VM as `Shutdown` state, destroy vcpu thread.
///
/// # Arguments
///
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm state.
fn vm_destroy(&self, cpus: &[Arc<CPU>], vm_state: &mut VmState) -> Result<()> {
for (cpu_index, cpu) in cpus.iter().enumerate() {
cpu.destroy()
.with_context(|| format!("Failed to destroy vcpu{}", cpu_index))?;
}
*vm_state = VmState::Shutdown;
Ok(())
}
/// Transfer VM state from `old` to `new`.
///
/// # Arguments
///
/// * `cpus` - Cpus vector restore cpu structure.
/// * `vm_state` - Vm state.
/// * `old_state` - Old vm state want to leave.
/// * `new_state` - New vm state want to transfer to.
fn vm_state_transfer(
&self,
cpus: &[Arc<CPU>],
#[cfg(target_arch = "aarch64")] irq_chip: &Option<Arc<InterruptController>>,
vm_state: &mut VmState,
old_state: VmState,
new_state: VmState,
) -> Result<()> {
use VmState::*;
if *vm_state != old_state {
bail!("Vm lifecycle error: state check failed.");
}
match (old_state, new_state) {
(Created, Running) => self
.vm_start(false, cpus, vm_state)
.with_context(|| "Failed to start vm.")?,
(Running, Paused) => self
.vm_pause(
cpus,
#[cfg(target_arch = "aarch64")]
irq_chip,
vm_state,
)
.with_context(|| "Failed to pause vm.")?,
(Paused, Running) => self
.vm_resume(cpus, vm_state)
.with_context(|| "Failed to resume vm.")?,
(_, Shutdown) => self
.vm_destroy(cpus, vm_state)
.with_context(|| "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(())
}
}
/// Normal run or resume virtual machine from migration/snapshot.
///
/// # Arguments
///
/// * `vm` - virtual machine that implement `MachineOps`.
/// * `cmd_args` - Command arguments from user.
pub fn vm_run(
vm: &Arc<Mutex<dyn MachineOps + Send + Sync>>,
cmd_args: &arg_parser::ArgMatches,
) -> Result<()> {
let migrate = vm.lock().unwrap().get_migrate_info();
if migrate.0 == MigrateMode::Unknown {
vm.lock()
.unwrap()
.run(cmd_args.is_present("freeze_cpu"))
.with_context(|| "Failed to start VM.")?;
} else {
start_incoming_migration(vm).with_context(|| "Failed to start migration.")?;
}
Ok(())
}
/// Start incoming migration from destination.
fn start_incoming_migration(vm: &Arc<Mutex<dyn MachineOps + Send + Sync>>) -> Result<()> {
let (mode, path) = vm.lock().unwrap().get_migrate_info();
match mode {
MigrateMode::File => {
MigrationManager::restore_snapshot(&path)
.with_context(|| "Failed to restore snapshot")?;
vm.lock()
.unwrap()
.run(false)
.with_context(|| "Failed to start VM.")?;
}
MigrateMode::Unix => {
clear_file(path.clone())?;
let listener = UnixListener::bind(&path)?;
let (mut sock, _) = listener.accept()?;
remove_file(&path)?;
MigrationManager::recv_migration(&mut sock)
.with_context(|| "Failed to receive migration with unix mode")?;
vm.lock()
.unwrap()
.run(false)
.with_context(|| "Failed to start VM.")?;
MigrationManager::finish_migration(&mut sock)
.with_context(|| "Failed to finish migraton.")?;
}
MigrateMode::Tcp => {
let listener = TcpListener::bind(&path)?;
let mut sock = listener.accept().map(|(stream, _)| stream)?;
MigrationManager::recv_migration(&mut sock)
.with_context(|| "Failed to receive migration with tcp mode")?;
vm.lock()
.unwrap()
.run(false)
.with_context(|| "Failed to start VM.")?;
MigrationManager::finish_migration(&mut sock)
.with_context(|| "Failed to finish migraton.")?;
}
MigrateMode::Unknown => {
bail!("Unknown migration mode");
}
}
// End the migration and reset the mode.
let locked_vm = vm.lock().unwrap();
let vm_config = locked_vm.get_vm_config();
if let Some((mode, _)) = vm_config.lock().unwrap().incoming.as_mut() {
*mode = MigrateMode::Unknown;
}
Ok(())
}
fn coverage_allow_list(syscall_allow_list: &mut Vec<BpfRule>) {
syscall_allow_list.extend(vec![
BpfRule::new(libc::SYS_fcntl),
BpfRule::new(libc::SYS_ftruncate),
])
}
#[cfg(feature = "windows_emu_pid")]
fn check_windows_emu_pid(
pid_path: String,
powerdown_req: Arc<EventFd>,
shutdown_req: Arc<EventFd>,
) {
let mut check_delay = Duration::from_millis(4000);
if !Path::new(&pid_path).exists() {
log::info!("Detect windows emu exited, let VM exits now");
if get_run_stage() == VmRunningStage::Os {
if let Err(e) = powerdown_req.write(1) {
log::error!("Failed to send powerdown request after emu exits: {:?}", e);
}
} else if let Err(e) = shutdown_req.write(1) {
log::error!("Failed to send shutdown request after emu exits: {:?}", e);
}
// Continue checking to prevent exit failed.
check_delay = Duration::from_millis(1000);
}
let check_emu_alive = Box::new(move || {
check_windows_emu_pid(
pid_path.clone(),
powerdown_req.clone(),
shutdown_req.clone(),
);
});
EventLoop::get_ctx(None)
.unwrap()
.timer_add(check_emu_alive, check_delay);
}

Комментарий ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/openeuler-stratovirt.git
git@gitlife.ru:oschina-mirror/openeuler-stratovirt.git
oschina-mirror
openeuler-stratovirt
openeuler-stratovirt
v2.4.0