diff --git a/arch/src/aarch64/mod.rs b/arch/src/aarch64/mod.rs index c1059b769..0ad0ae325 100644 --- a/arch/src/aarch64/mod.rs +++ b/arch/src/aarch64/mod.rs @@ -3,16 +3,26 @@ pub mod layout; -use memory_model::{GuestAddress, GuestMemory}; +use crate::RegionType; +use kvm_ioctls::*; +use vm_memory::{GuestAddress, GuestMemoryMmap, GuestUsize}; /// Stub function that needs to be implemented when aarch64 functionality is added. -pub fn arch_memory_regions(size: usize) -> Vec<(GuestAddress, usize, RegionType)> { - vec![(GuestAddress(0), size, RegionType::Ram)] +pub fn arch_memory_regions(size: GuestUsize) -> Vec<(GuestAddress, usize, RegionType)> { + vec![(GuestAddress(0), size as usize, RegionType::Ram)] +} + +#[derive(Debug, Copy, Clone)] +/// Specifies the entry point address where the guest must start +/// executing code. +pub struct EntryPoint { + /// Address in guest memory where the guest must start execution + pub entry_addr: GuestAddress, } /// Stub function that needs to be implemented when aarch64 functionality is added. pub fn configure_system( - _guest_mem: &GuestMemory, + _guest_mem: &GuestMemoryMmap, _cmdline_addr: GuestAddress, _cmdline_size: usize, _num_cpus: u8, @@ -25,3 +35,29 @@ pub fn configure_system( pub fn get_reserved_mem_addr() -> usize { 0 } + +pub fn get_host_cpu_phys_bits() -> u8 { + // The value returned here is used to determine the physical address space size + // for a VM (IPA size). + // In recent kernel versions, the maxium IPA size supported by the host can be + // known by querying cap KVM_CAP_ARM_VM_IPA_SIZE. And the IPA size for a + // guest can be configured smaller. + // But in Cloud-Hypervisor we simply use the maxium value for the VM. + // Reference https://lwn.net/Articles/766767/. + // + // The correct way to query KVM_CAP_ARM_VM_IPA_SIZE is via rust-vmm/kvm-ioctls, + // which wraps all IOCTL's and provides easy interface to user hypervisors. + // For now the cap hasn't been supported. A separate patch will be submitted to + // rust-vmm to add it. + // So a hardcoded value is used here as a temporary solution. + // It will be replace once rust-vmm/kvm-ioctls is ready. + // + 40 +} + +pub fn check_required_kvm_extensions(kvm: &Kvm) -> super::Result<()> { + if !kvm.check_extension(Cap::SignalMsi) { + return Err(super::Error::CapabilityMissing(Cap::SignalMsi)); + } + Ok(()) +} diff --git a/arch/src/lib.rs b/arch/src/lib.rs index e20550f65..34b817ee9 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -22,6 +22,7 @@ extern crate kvm_ioctls; extern crate linux_loader; extern crate vm_memory; +use kvm_ioctls::*; use std::result; #[derive(Debug)] @@ -47,6 +48,8 @@ pub enum Error { ModlistSetup(vm_memory::GuestMemoryError), /// RSDP Beyond Guest Memory RSDPPastRamEnd, + /// Capability missing + CapabilityMissing(Cap), } pub type Result = result::Result; @@ -73,8 +76,8 @@ pub mod aarch64; #[cfg(target_arch = "aarch64")] pub use aarch64::{ - arch_memory_regions, configure_system, get_reserved_mem_addr, layout::CMDLINE_MAX_SIZE, - layout::CMDLINE_START, + arch_memory_regions, check_required_kvm_extensions, configure_system, get_host_cpu_phys_bits, + get_reserved_mem_addr, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, EntryPoint, }; #[cfg(target_arch = "x86_64")] @@ -82,11 +85,13 @@ pub mod x86_64; #[cfg(target_arch = "x86_64")] pub use x86_64::{ - arch_memory_regions, configure_system, initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, - layout::CMDLINE_START, regs, BootProtocol, EntryPoint, + arch_memory_regions, check_required_kvm_extensions, configure_system, get_host_cpu_phys_bits, + initramfs_load_addr, layout, layout::CMDLINE_MAX_SIZE, layout::CMDLINE_START, regs, + BootProtocol, EntryPoint, }; /// Safe wrapper for `sysconf(_SC_PAGESIZE)`. +#[cfg(target_arch = "x86_64")] #[inline(always)] fn pagesize() -> usize { // Trivially safe diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index 5c8ce9a2f..ecd5c484a 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -13,9 +13,9 @@ pub mod layout; #[cfg(not(feature = "acpi"))] mod mptable; pub mod regs; - use crate::InitramfsConfig; use crate::RegionType; +use kvm_ioctls::*; use linux_loader::loader::bootparam::{boot_params, setup_header}; use linux_loader::loader::elf::start_info::{ hvm_memmap_table_entry, hvm_modlist_entry, hvm_start_info, @@ -459,6 +459,47 @@ pub fn initramfs_load_addr( Ok(aligned_addr) } +pub fn get_host_cpu_phys_bits() -> u8 { + use std::arch::x86_64; + unsafe { + let leaf = x86_64::__cpuid(0x8000_0000); + + // Detect and handle AMD SME (Secure Memory Encryption) properly. + // Some physical address bits may become reserved when the feature is enabled. + // See AMD64 Architecture Programmer's Manual Volume 2, Section 7.10.1 + let reduced = if leaf.eax >= 0x8000_001f + && leaf.ebx == 0x6874_7541 // Vendor ID: AuthenticAMD + && leaf.ecx == 0x444d_4163 + && leaf.edx == 0x6974_6e65 + && x86_64::__cpuid(0x8000_001f).eax & 0x1 != 0 + { + (x86_64::__cpuid(0x8000_001f).ebx >> 6) & 0x3f + } else { + 0 + }; + + if leaf.eax >= 0x8000_0008 { + let leaf = x86_64::__cpuid(0x8000_0008); + ((leaf.eax & 0xff) - reduced) as u8 + } else { + 36 + } + } +} + +pub fn check_required_kvm_extensions(kvm: &Kvm) -> super::Result<()> { + if !kvm.check_extension(Cap::SignalMsi) { + return Err(super::Error::CapabilityMissing(Cap::SignalMsi)); + } + if !kvm.check_extension(Cap::TscDeadlineTimer) { + return Err(super::Error::CapabilityMissing(Cap::TscDeadlineTimer)); + } + if !kvm.check_extension(Cap::SplitIrqchip) { + return Err(super::Error::CapabilityMissing(Cap::SplitIrqchip)); + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/net_util/src/tap.rs b/net_util/src/tap.rs index 8de8e9667..22b7423f8 100644 --- a/net_util/src/tap.rs +++ b/net_util/src/tap.rs @@ -192,7 +192,7 @@ impl Tap { unsafe { let ifru_hwaddr = ifreq.ifr_ifru.ifru_hwaddr.as_mut(); for (i, v) in addr.get_bytes().iter().enumerate() { - ifru_hwaddr.sa_data[i] = *v as i8; + ifru_hwaddr.sa_data[i] = *v as c_char; } } diff --git a/vhost_user_fs/src/seccomp.rs b/vhost_user_fs/src/seccomp.rs index cc2664b9e..3ce9446ec 100644 --- a/vhost_user_fs/src/seccomp.rs +++ b/vhost_user_fs/src/seccomp.rs @@ -39,10 +39,12 @@ fn vuf_filter(action: SeccompAction) -> Result { allow_syscall(libc::SYS_close), allow_syscall(libc::SYS_copy_file_range), allow_syscall(libc::SYS_dup), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_epoll_create), allow_syscall(libc::SYS_epoll_create1), allow_syscall(libc::SYS_epoll_ctl), allow_syscall(libc::SYS_epoll_pwait), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_epoll_wait), allow_syscall(libc::SYS_eventfd2), allow_syscall(libc::SYS_exit), @@ -59,10 +61,13 @@ fn vuf_filter(action: SeccompAction) -> Result { allow_syscall(libc::SYS_fremovexattr), allow_syscall(libc::SYS_fsetxattr), allow_syscall(libc::SYS_fstat), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_fstatfs), allow_syscall(libc::SYS_fsync), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_ftruncate), allow_syscall(libc::SYS_futex), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_getdents), allow_syscall(libc::SYS_getdents64), allow_syscall(libc::SYS_getegid), @@ -82,6 +87,7 @@ fn vuf_filter(action: SeccompAction) -> Result { allow_syscall(libc::SYS_mremap), allow_syscall(libc::SYS_munmap), allow_syscall(libc::SYS_newfstatat), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_open), allow_syscall(libc::SYS_openat), allow_syscall(libc::SYS_prctl), // TODO restrict to just PR_SET_NAME? @@ -109,9 +115,11 @@ fn vuf_filter(action: SeccompAction) -> Result { allow_syscall(libc::SYS_sigaltstack), allow_syscall(libc::SYS_statx), allow_syscall(libc::SYS_symlinkat), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_time), // Rarely needed, except on static builds allow_syscall(libc::SYS_tgkill), allow_syscall(libc::SYS_umask), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_unlink), allow_syscall(libc::SYS_unlinkat), allow_syscall(libc::SYS_unshare), diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 6365a88f2..23f720c10 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -21,36 +21,46 @@ use anyhow::anyhow; use arch::layout; use arch::EntryPoint; use devices::{ioapic, BusDevice}; +#[cfg(target_arch = "x86_64")] use kvm_bindings::{ kvm_fpu, kvm_lapic_state, kvm_mp_state, kvm_regs, kvm_sregs, kvm_vcpu_events, kvm_xcrs, kvm_xsave, CpuId, Msrs, }; use kvm_ioctls::*; +#[cfg(target_arch = "x86_64")] use libc::{c_void, siginfo_t}; use serde_derive::{Deserialize, Serialize}; -use std::cmp; +#[cfg(target_arch = "x86_64")] +use std::fmt; use std::os::unix::thread::JoinHandleExt; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Barrier, Mutex}; -use std::thread; -use std::{fmt, io, result}; -use vm_memory::{Address, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap}; +use std::{cmp, io, result, thread}; +#[cfg(target_arch = "x86_64")] +use vm_memory::{Address, GuestAddressSpace}; +use vm_memory::{GuestAddress, GuestMemoryAtomic, GuestMemoryMmap}; use vm_migration::{ Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable, Transportable, }; use vmm_sys_util::eventfd::EventFd; -use vmm_sys_util::signal::{register_signal_handler, SIGRTMIN}; +#[cfg(target_arch = "x86_64")] +use vmm_sys_util::signal::register_signal_handler; +use vmm_sys_util::signal::SIGRTMIN; // CPUID feature bits +#[cfg(target_arch = "x86_64")] const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit. +#[cfg(target_arch = "x86_64")] const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit. // Debug I/O port #[cfg(target_arch = "x86_64")] const DEBUG_IOPORT: u16 = 0x80; +#[cfg(target_arch = "x86_64")] const DEBUG_IOPORT_PREFIX: &str = "Debug I/O port"; +#[cfg(target_arch = "x86_64")] /// Debug I/O port, see: /// https://www.intel.com/content/www/us/en/support/articles/000005500/boards-and-kits.html /// @@ -63,7 +73,7 @@ pub enum DebugIoPortRange { Userspace, Custom, } - +#[cfg(target_arch = "x86_64")] impl DebugIoPortRange { fn from_u8(value: u8) -> DebugIoPortRange { match value { @@ -76,6 +86,7 @@ impl DebugIoPortRange { } } +#[cfg(target_arch = "x86_64")] impl fmt::Display for DebugIoPortRange { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -199,6 +210,7 @@ pub enum Error { } pub type Result = result::Result; +#[cfg(target_arch = "x86_64")] #[allow(dead_code)] #[derive(Copy, Clone)] enum CpuidReg { @@ -208,6 +220,7 @@ enum CpuidReg { EDX, } +#[cfg(target_arch = "x86_64")] pub struct CpuidPatch { pub function: u32, pub index: u32, @@ -218,6 +231,7 @@ pub struct CpuidPatch { pub edx_bit: Option, } +#[cfg(target_arch = "x86_64")] impl CpuidPatch { fn set_cpuid_reg( cpuid: &mut CpuId, @@ -311,12 +325,16 @@ struct InterruptSourceOverride { pub struct Vcpu { fd: VcpuFd, id: u8, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] io_bus: Arc, mmio_bus: Arc, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] ioapic: Option>>, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] vm_ts: std::time::Instant, } +#[cfg(target_arch = "x86_64")] #[derive(Clone, Serialize, Deserialize)] pub struct VcpuKvmState { msrs: Msrs, @@ -330,6 +348,10 @@ pub struct VcpuKvmState { mp_state: kvm_mp_state, } +#[cfg(target_arch = "aarch64")] +#[derive(Clone, Serialize, Deserialize)] +pub struct VcpuKvmState {} + impl Vcpu { /// Constructs a new VCPU for `vm`. /// @@ -357,6 +379,7 @@ impl Vcpu { }))) } + #[cfg(target_arch = "x86_64")] /// Configures a x86_64 specific vcpu and should be called once per vcpu from the vcpu's thread. /// /// # Arguments @@ -406,10 +429,12 @@ impl Vcpu { pub fn run(&self) -> Result { match self.fd.run() { Ok(run) => match run { + #[cfg(target_arch = "x86_64")] VcpuExit::IoIn(addr, data) => { self.io_bus.read(u64::from(addr), data); Ok(true) } + #[cfg(target_arch = "x86_64")] VcpuExit::IoOut(addr, data) => { if addr == DEBUG_IOPORT && data.len() == 1 { self.log_debug_ioport(data[0]); @@ -425,6 +450,7 @@ impl Vcpu { self.mmio_bus.write(addr as u64, data); Ok(true) } + #[cfg(target_arch = "x86_64")] VcpuExit::IoapicEoi(vector) => { if let Some(ioapic) = &self.ioapic { ioapic.lock().unwrap().end_of_interrupt(vector); @@ -451,6 +477,7 @@ impl Vcpu { } } + #[cfg(target_arch = "x86_64")] // Log debug io port codes. fn log_debug_ioport(&self, code: u8) { let ts = self.vm_ts.elapsed(); @@ -464,6 +491,7 @@ impl Vcpu { ); } + #[cfg(target_arch = "x86_64")] fn kvm_state(&self) -> Result { let mut msrs = arch::x86_64::regs::boot_msr_entries(); self.fd.get_msrs(&mut msrs).map_err(Error::VcpuGetMsrs)?; @@ -493,6 +521,7 @@ impl Vcpu { }) } + #[cfg(target_arch = "x86_64")] fn set_kvm_state(&mut self, state: &VcpuKvmState) -> Result<()> { self.fd.set_regs(&state.regs).map_err(Error::VcpuSetRegs)?; @@ -520,6 +549,16 @@ impl Vcpu { Ok(()) } + + #[cfg(target_arch = "aarch64")] + fn kvm_state(&self) -> Result { + unimplemented!(); + } + + #[cfg(target_arch = "aarch64")] + fn set_kvm_state(&mut self, _state: &VcpuKvmState) -> Result<()> { + Ok(()) + } } const VCPU_SNAPSHOT_ID: &str = "vcpu"; @@ -576,13 +615,19 @@ pub struct CpuManager { boot_vcpus: u8, max_vcpus: u8, io_bus: Arc, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] mmio_bus: Arc, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] ioapic: Option>>, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] vm_memory: GuestMemoryAtomic, + #[cfg(target_arch = "x86_64")] cpuid: CpuId, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] fd: Arc, vcpus_kill_signalled: Arc, vcpus_pause_signalled: Arc, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] reset_evt: EventFd, vcpu_states: Vec, selected_cpu: u8, @@ -709,7 +754,7 @@ impl CpuManager { config: &CpusConfig, device_manager: &Arc>, guest_memory: GuestMemoryAtomic, - kvm: &Kvm, + #[cfg_attr(target_arch = "aarch64", allow(unused_variables))] kvm: &Kvm, fd: Arc, reset_evt: EventFd, ) -> Result>> { @@ -717,6 +762,7 @@ impl CpuManager { vcpu_states.resize_with(usize::from(config.max_vcpus), VcpuState::default); let device_manager = device_manager.lock().unwrap(); + #[cfg(target_arch = "x86_64")] let cpuid = CpuManager::patch_cpuid(kvm)?; let cpu_manager = Arc::new(Mutex::new(CpuManager { boot_vcpus: config.boot_vcpus, @@ -725,6 +771,7 @@ impl CpuManager { mmio_bus: device_manager.mmio_bus().clone(), ioapic: device_manager.ioapic().clone(), vm_memory: guest_memory, + #[cfg(target_arch = "x86_64")] cpuid, fd, vcpus_kill_signalled: Arc::new(AtomicBool::new(false)), @@ -752,6 +799,7 @@ impl CpuManager { Ok(cpu_manager) } + #[cfg(target_arch = "x86_64")] fn patch_cpuid(kvm: &Kvm) -> Result { let mut cpuid_patches = Vec::new(); @@ -787,6 +835,20 @@ impl CpuManager { Ok(cpuid) } + #[cfg(target_arch = "aarch64")] + fn start_vcpu( + &mut self, + _cpu_id: u8, + _creation_ts: std::time::Instant, + _vcpu_thread_barrier: Arc, + _entry_point: Option, + _inserting: bool, + _snapshot: Option, + ) -> Result<()> { + unimplemented!(); + } + + #[cfg(target_arch = "x86_64")] fn start_vcpu( &mut self, cpu_id: u8, diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 06d825793..bee084977 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -24,6 +24,7 @@ use acpi_tables::{aml, aml::Aml}; use anyhow::anyhow; #[cfg(feature = "acpi")] use arch::layout; +#[cfg(target_arch = "x86_64")] use arch::layout::{APIC_START, IOAPIC_SIZE, IOAPIC_START}; use devices::{ioapic, BusDevice, HotPlugNotificationFlags}; use kvm_ioctls::*; @@ -75,6 +76,7 @@ const MMIO_LEN: u64 = 0x1000; #[cfg(feature = "pci_support")] const VFIO_DEVICE_NAME_PREFIX: &str = "_vfio"; +#[cfg(target_arch = "x86_64")] const IOAPIC_DEVICE_NAME: &str = "_ioapic"; const SERIAL_DEVICE_NAME_PREFIX: &str = "_serial"; @@ -662,6 +664,7 @@ pub struct DeviceManager { #[cfg(feature = "pci_support")] pci_bus: Option>>, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] // MSI Interrupt Manager msi_interrupt_manager: Arc>, @@ -999,6 +1002,12 @@ impl DeviceManager { Ok(()) } + #[cfg(target_arch = "aarch64")] + fn add_ioapic(&mut self) -> DeviceManagerResult>> { + unimplemented!(); + } + + #[cfg(target_arch = "x86_64")] fn add_ioapic(&mut self) -> DeviceManagerResult>> { let id = String::from(IOAPIC_DEVICE_NAME); diff --git a/vmm/src/memory_manager.rs b/vmm/src/memory_manager.rs index a75d82c04..07b06d2c9 100644 --- a/vmm/src/memory_manager.rs +++ b/vmm/src/memory_manager.rs @@ -8,8 +8,12 @@ use crate::MEMORY_MANAGER_SNAPSHOT_ID; #[cfg(feature = "acpi")] use acpi_tables::{aml, aml::Aml}; use anyhow::anyhow; -use arch::{layout, RegionType}; -use devices::{ioapic, BusDevice}; +#[cfg(target_arch = "x86_64")] +use arch::layout; +use arch::{get_host_cpu_phys_bits, RegionType}; +#[cfg(target_arch = "x86_64")] +use devices::ioapic; +use devices::BusDevice; use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_READONLY}; use kvm_ioctls::*; use std::convert::TryInto; @@ -21,7 +25,9 @@ use std::path::PathBuf; use std::result; use std::sync::{Arc, Mutex}; use url::Url; -use vm_allocator::{GsiApic, SystemAllocator}; +#[cfg(target_arch = "x86_64")] +use vm_allocator::GsiApic; +use vm_allocator::SystemAllocator; use vm_memory::guest_memory::FileOffset; use vm_memory::{ mmap::MmapRegionError, Address, Bytes, Error as MmapError, GuestAddress, GuestAddressSpace, @@ -33,6 +39,7 @@ use vm_migration::{ Transportable, }; +#[cfg(target_arch = "x86_64")] const X86_64_IRQ_BASE: u32 = 5; const HOTPLUG_COUNT: usize = 8; @@ -120,34 +127,6 @@ pub enum Error { InvalidAmountExternalBackingFiles, } -pub fn get_host_cpu_phys_bits() -> u8 { - use core::arch::x86_64; - unsafe { - let leaf = x86_64::__cpuid(0x8000_0000); - - // Detect and handle AMD SME (Secure Memory Encryption) properly. - // Some physical address bits may become reserved when the feature is enabled. - // See AMD64 Architecture Programmer's Manual Volume 2, Section 7.10.1 - let reduced = if leaf.eax >= 0x8000_001f - && leaf.ebx == 0x6874_7541 // Vendor ID: AuthenticAMD - && leaf.ecx == 0x444d_4163 - && leaf.edx == 0x6974_6e65 - && x86_64::__cpuid(0x8000_001f).eax & 0x1 != 0 - { - (x86_64::__cpuid(0x8000_001f).ebx >> 6) & 0x3f - } else { - 0 - }; - - if leaf.eax >= 0x8000_0008 { - let leaf = x86_64::__cpuid(0x8000_0008); - ((leaf.eax & 0xff) - reduced) as u8 - } else { - 36 - } - } -} - const ENABLE_FLAG: usize = 0; const INSERTING_FLAG: usize = 1; const REMOVING_FLAG: usize = 2; @@ -279,12 +258,8 @@ impl MemoryManager { GuestMemoryMmap::from_arc_regions(mem_regions).map_err(Error::GuestMemory)?; let end_of_device_area = GuestAddress((1 << get_host_cpu_phys_bits()) - 1); - let mem_end = guest_memory.last_addr(); - let mut start_of_device_area = if mem_end < arch::layout::MEM_32BIT_RESERVED_START { - arch::layout::RAM_64BIT_START - } else { - mem_end.unchecked_add(1) - }; + + let mut start_of_device_area = MemoryManager::start_addr(guest_memory.last_addr(), false); let mut virtiomem_region = None; let mut virtiomem_resize = None; @@ -319,6 +294,7 @@ impl MemoryManager { let mut hotplug_slots = Vec::with_capacity(HOTPLUG_COUNT); hotplug_slots.resize_with(HOTPLUG_COUNT, HotPlugState::default); + #[cfg(target_arch = "x86_64")] // Let's allocate 64 GiB of addressable MMIO space, starting at 0. let allocator = Arc::new(Mutex::new( SystemAllocator::new( @@ -336,6 +312,20 @@ impl MemoryManager { .ok_or(Error::CreateSystemAllocator)?, )); + #[cfg(target_arch = "aarch64")] + let allocator = Arc::new(Mutex::new( + SystemAllocator::new( + GuestAddress(0), + 0, + GuestAddress(0), + 0, + GuestAddress(0), + 0, + vec![], + ) + .ok_or(Error::CreateSystemAllocator)?, + )); + let memory_manager = Arc::new(Mutex::new(MemoryManager { guest_memory: guest_memory.clone(), next_kvm_memory_slot: 0, @@ -578,6 +568,30 @@ impl MemoryManager { Ok(()) } + // + // Calculate the start address of an area next to RAM. + // + // If the next area is device space, there is no gap. + // If the next area is hotplugged RAM, the start address needs to be aligned + // to 128MiB boundary, and a gap of 256MiB need to be set before it. + // On x86_64, it must also start at the 64bit start. + fn start_addr(mem_end: GuestAddress, with_gap: bool) -> GuestAddress { + let start_addr = if with_gap { + GuestAddress((mem_end.0 + 1 + (256 << 20)) & !((128 << 20) - 1)) + } else { + mem_end.unchecked_add(1) + }; + + #[cfg(target_arch = "x86_64")] + let start_addr = if mem_end < arch::layout::MEM_32BIT_RESERVED_START { + arch::layout::RAM_64BIT_START + } else { + start_addr + }; + + start_addr + } + fn hotplug_ram_region(&mut self, size: usize) -> Result, Error> { info!("Hotplugging new RAM: {}", size); @@ -591,14 +605,7 @@ impl MemoryManager { return Err(Error::InvalidSize); } - // Start address needs to be non-contiguous with last memory added (leaving a gap of 256MiB) - // and also aligned to 128MiB boundary. It must also start at the 64bit start. - let mem_end = self.guest_memory.memory().last_addr(); - let start_addr = if mem_end < arch::layout::MEM_32BIT_RESERVED_START { - arch::layout::RAM_64BIT_START - } else { - GuestAddress((mem_end.0 + 1 + (256 << 20)) & !((128 << 20) - 1)) - }; + let start_addr = MemoryManager::start_addr(self.guest_memory.memory().last_addr(), true); if start_addr.checked_add(size.try_into().unwrap()).unwrap() >= self.start_of_device_area() { diff --git a/vmm/src/seccomp_filters.rs b/vmm/src/seccomp_filters.rs index ddd554ff1..5eb994642 100644 --- a/vmm/src/seccomp_filters.rs +++ b/vmm/src/seccomp_filters.rs @@ -195,7 +195,9 @@ pub fn vmm_thread_filter() -> Result { Ok(SeccompFilter::new( vec![ allow_syscall(libc::SYS_accept4), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_access), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_arch_prctl), allow_syscall(libc::SYS_bind), allow_syscall(libc::SYS_brk), @@ -208,6 +210,7 @@ pub fn vmm_thread_filter() -> Result { allow_syscall(libc::SYS_epoll_create1), allow_syscall(libc::SYS_epoll_ctl), allow_syscall(libc::SYS_epoll_pwait), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_epoll_wait), allow_syscall(libc::SYS_eventfd2), allow_syscall(libc::SYS_execve), @@ -216,9 +219,11 @@ pub fn vmm_thread_filter() -> Result { allow_syscall(libc::SYS_fallocate), allow_syscall(libc::SYS_fcntl), allow_syscall(libc::SYS_fdatasync), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_fork), allow_syscall(libc::SYS_fstat), allow_syscall(libc::SYS_fsync), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_ftruncate), allow_syscall(libc::SYS_futex), allow_syscall(libc::SYS_getpid), @@ -236,6 +241,7 @@ pub fn vmm_thread_filter() -> Result { allow_syscall(libc::SYS_mremap), allow_syscall(libc::SYS_munmap), allow_syscall(libc::SYS_nanosleep), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_open), allow_syscall(libc::SYS_openat), allow_syscall(libc::SYS_pipe2), @@ -244,6 +250,7 @@ pub fn vmm_thread_filter() -> Result { allow_syscall(libc::SYS_prlimit64), allow_syscall(libc::SYS_pwrite64), allow_syscall(libc::SYS_read), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_readlink), allow_syscall(libc::SYS_recvfrom), allow_syscall(libc::SYS_recvmsg), @@ -264,10 +271,12 @@ pub fn vmm_thread_filter() -> Result { ], ), allow_syscall(libc::SYS_socketpair), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_stat), allow_syscall(libc::SYS_statx), allow_syscall(libc::SYS_tgkill), allow_syscall(libc::SYS_tkill), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_unlink), allow_syscall(libc::SYS_wait4), allow_syscall(libc::SYS_write), @@ -290,6 +299,7 @@ pub fn api_thread_filter() -> Result { allow_syscall(libc::SYS_epoll_create1), allow_syscall(libc::SYS_epoll_ctl), allow_syscall(libc::SYS_epoll_pwait), + #[cfg(target_arch = "x86_64")] allow_syscall(libc::SYS_epoll_wait), allow_syscall(libc::SYS_exit), allow_syscall(libc::SYS_futex), diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index e4494cedc..2e8292de1 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -35,24 +35,37 @@ use crate::memory_manager::{Error as MemoryManagerError, MemoryManager}; use crate::migration::{url_to_path, vm_config_from_snapshot, VM_SNAPSHOT_FILE}; use crate::{CPU_MANAGER_SNAPSHOT_ID, DEVICE_MANAGER_SNAPSHOT_ID, MEMORY_MANAGER_SNAPSHOT_ID}; use anyhow::anyhow; -use arch::{BootProtocol, EntryPoint}; -use devices::{ioapic, HotPlugNotificationFlags}; +#[cfg(target_arch = "x86_64")] +use arch::BootProtocol; +use arch::{check_required_kvm_extensions, EntryPoint}; +#[cfg(target_arch = "x86_64")] +use devices::ioapic; +use devices::HotPlugNotificationFlags; +#[cfg(target_arch = "x86_64")] use kvm_bindings::{kvm_enable_cap, kvm_userspace_memory_region, KVM_CAP_SPLIT_IRQCHIP}; use kvm_ioctls::*; +#[cfg(target_arch = "x86_64")] use linux_loader::cmdline::Cmdline; +#[cfg(target_arch = "x86_64")] use linux_loader::loader::elf::Error::InvalidElfMagicNumber; +#[cfg(target_arch = "x86_64")] use linux_loader::loader::KernelLoader; use signal_hook::{iterator::Signals, SIGINT, SIGTERM, SIGWINCH}; +#[cfg(target_arch = "x86_64")] use std::convert::TryInto; +#[cfg(target_arch = "x86_64")] use std::ffi::CString; use std::fs::{File, OpenOptions}; -use std::io::Write; -use std::io::{self, Seek, SeekFrom}; +use std::io::{self, Write}; +#[cfg(target_arch = "x86_64")] +use std::io::{Seek, SeekFrom}; +#[cfg(target_arch = "x86_64")] use std::ops::Deref; use std::path::PathBuf; use std::sync::{Arc, Mutex, RwLock}; use std::{result, str, thread}; use url::Url; +#[cfg(target_arch = "x86_64")] use vm_memory::{ Address, Bytes, GuestAddress, GuestAddressSpace, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, @@ -65,6 +78,7 @@ use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::terminal::Terminal; // 64 bit direct boot entry offset for bzImage +#[cfg(target_arch = "x86_64")] const KERNEL_64BIT_ENTRY_OFFSET: u64 = 0x200; /// Errors associated with VM management @@ -153,9 +167,6 @@ pub enum Error { /// Error from CPU handling CpuManager(cpu::Error), - /// Capability missing - CapabilityMissing(Cap), - /// Cannot pause devices PauseDevices(MigratableError), @@ -246,7 +257,9 @@ impl VmState { } pub struct Vm { + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] kernel: File, + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] initramfs: Option, threads: Vec>, device_manager: Arc>, @@ -262,18 +275,7 @@ impl Vm { fn kvm_new() -> Result<(Kvm, Arc)> { let kvm = Kvm::new().map_err(Error::KvmNew)?; - // Check required capabilities: - if !kvm.check_extension(Cap::SignalMsi) { - return Err(Error::CapabilityMissing(Cap::SignalMsi)); - } - - if !kvm.check_extension(Cap::TscDeadlineTimer) { - return Err(Error::CapabilityMissing(Cap::TscDeadlineTimer)); - } - - if !kvm.check_extension(Cap::SplitIrqchip) { - return Err(Error::CapabilityMissing(Cap::SplitIrqchip)); - } + check_required_kvm_extensions(&kvm).expect("Missing KVM capabilities"); let fd: VmFd; loop { @@ -295,16 +297,20 @@ impl Vm { let fd = Arc::new(fd); // Set TSS + #[cfg(target_arch = "x86_64")] fd.set_tss_address(arch::x86_64::layout::KVM_TSS_ADDRESS.raw_value() as usize) .map_err(Error::VmSetup)?; - // Create split irqchip - // Only the local APIC is emulated in kernel, both PICs and IOAPIC - // are not. - let mut cap: kvm_enable_cap = Default::default(); - cap.cap = KVM_CAP_SPLIT_IRQCHIP; - cap.args[0] = ioapic::NUM_IOAPIC_PINS as u64; - fd.enable_cap(&cap).map_err(Error::VmSetup)?; + #[cfg(target_arch = "x86_64")] + { + // Create split irqchip + // Only the local APIC is emulated in kernel, both PICs and IOAPIC + // are not. + let mut cap: kvm_enable_cap = Default::default(); + cap.cap = KVM_CAP_SPLIT_IRQCHIP; + cap.args[0] = ioapic::NUM_IOAPIC_PINS as u64; + fd.enable_cap(&cap).map_err(Error::VmSetup)?; + } Ok((kvm, fd)) } @@ -446,6 +452,7 @@ impl Vm { ) } + #[cfg(target_arch = "x86_64")] fn load_initramfs(&mut self, guest_mem: &GuestMemoryMmap) -> Result { let mut initramfs = self.initramfs.as_ref().unwrap(); let size: usize = initramfs @@ -468,6 +475,12 @@ impl Vm { Ok(arch::InitramfsConfig { address, size }) } + #[cfg(target_arch = "aarch64")] + fn load_kernel(&mut self) -> Result { + unimplemented!(); + } + + #[cfg(target_arch = "x86_64")] fn load_kernel(&mut self) -> Result { let mut cmdline = Cmdline::new(arch::CMDLINE_MAX_SIZE); cmdline @@ -1276,6 +1289,7 @@ impl Transportable for Vm { } impl Migratable for Vm {} +#[cfg(target_arch = "x86_64")] #[cfg(test)] mod tests { use super::*; @@ -1334,6 +1348,7 @@ mod tests { } } +#[cfg(target_arch = "x86_64")] #[allow(unused)] pub fn test_vm() { // This example based on https://lwn.net/Articles/658511/