diff --git a/src/core/src/vmm/aarch64.rs b/src/core/src/vmm/aarch64.rs index 37ba570e6..b085befe8 100644 --- a/src/core/src/vmm/aarch64.rs +++ b/src/core/src/vmm/aarch64.rs @@ -1,20 +1,22 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use super::hv::{Cpu, CpuStates}; +use super::hv::{Cpu, CpuFeats, CpuStates}; use super::hw::RamMap; use super::MainCpuError; -pub fn setup_main_cpu(cpu: &mut impl Cpu, entry: usize, map: RamMap) -> Result<(), MainCpuError> { +pub fn setup_main_cpu( + cpu: &mut impl Cpu, + entry: usize, + map: RamMap, + feats: &CpuFeats, +) -> Result<(), MainCpuError> { // Check if CPU support VM page size. let mut states = cpu .states() .map_err(|e| MainCpuError::GetCpuStatesFailed(Box::new(e)))?; - let mmfr0 = states - .get_id_aa64mmfr0() - .map_err(|e| MainCpuError::GetIdAa64mmfr0Failed(Box::new(e)))?; match map.page_size.get() { 0x4000 => { - if ((mmfr0 & 0xF00000) >> 20) == 0 { + if !feats.tgran16 { return Err(MainCpuError::PageSizeNotSupported(map.page_size)); } } @@ -22,9 +24,7 @@ pub fn setup_main_cpu(cpu: &mut impl Cpu, entry: usize, map: RamMap) -> Result<( } // Check if CPU support at least 36 bits physical address. - let pa_range = mmfr0 & 0xF; - - if pa_range == 0 { + if feats.pa_range == 0 { return Err(MainCpuError::PhysicalAddressTooSmall); } diff --git a/src/core/src/vmm/hv/aarch64.rs b/src/core/src/vmm/hv/aarch64.rs new file mode 100644 index 000000000..c6cb493fc --- /dev/null +++ b/src/core/src/vmm/hv/aarch64.rs @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +/// Features available on a PE. +pub struct CpuFeats { + /// Indicates support for disabling context synchronizing exception entry and exit. + /// + /// - `false`: All exception entries and exits are context synchronization events. + /// - `true`: Non-context synchronizing exception entry and exit are supported. + pub feat_exs: bool, + /// Indicates support for 4KB memory translation granule size. + pub tgran4: bool, + /// Indicates support for 64KB memory translation granule size. + pub tgran64: bool, + /// Indicates support for 16KB memory translation granule size. + pub tgran16: bool, + /// Indicates support for mixed-endian configuration. + /// + /// - `false`: No mixed-endian support. The `SCTLR_ELx.EE` bits have a fixed value. See the + /// [`Self::big_end_el0`], for whether EL0 supports mixed-endian. + /// - `true`: Mixed-endian support. The `SCTLR_ELx.EE` and `SCTLR_EL1.E0E` bits can be + /// configured. + pub big_end: bool, + /// Indicates support for mixed-endian at EL0 only. + /// + /// - `false`: No mixed-endian support at EL0. The `SCTLR_EL1.E0E` bit has a fixed value. + /// - `true`: Mixed-endian support at EL0. The `SCTLR_EL1.E0E` bit can be configured. + pub big_end_el0: Option, + /// Indicates support for ASID 16 bits. + pub asid16: bool, + /// Physical Address range supported. + /// + /// - `0b0000`: 32 bits, 4GB. + /// - `0b0001`: 36 bits, 64GB. + /// - `0b0010`: 40 bits, 1TB. + /// - `0b0011`: 42 bits, 4TB. + /// - `0b0100`: 44 bits, 16TB. + /// - `0b0101`: 48 bits, 256TB. + pub pa_range: u8, +} diff --git a/src/core/src/vmm/hv/linux/mod.rs b/src/core/src/vmm/hv/linux/mod.rs index 8194fa5dc..db948a779 100644 --- a/src/core/src/vmm/hv/linux/mod.rs +++ b/src/core/src/vmm/hv/linux/mod.rs @@ -3,7 +3,7 @@ use self::ffi::{ kvm_check_version, kvm_create_vcpu, kvm_create_vm, kvm_get_vcpu_mmap_size, kvm_max_vcpus, kvm_set_user_memory_region, }; -use super::Hypervisor; +use super::{CpuFeats, Hypervisor}; use crate::vmm::hw::Ram; use crate::vmm::VmmError; use libc::{mmap, open, MAP_FAILED, MAP_PRIVATE, O_RDWR, PROT_READ, PROT_WRITE}; @@ -104,6 +104,10 @@ impl Hypervisor for Kvm { type Cpu<'a> = KvmCpu<'a>; type CpuErr = KvmCpuError; + fn cpu_features(&mut self) -> Result { + Ok(CpuFeats {}) + } + fn create_cpu(&self, id: usize) -> Result, Self::CpuErr> { use std::io::Error; diff --git a/src/core/src/vmm/hv/macos/cpu.rs b/src/core/src/vmm/hv/macos/cpu.rs index 8c4d5ca7b..95297e6cf 100644 --- a/src/core/src/vmm/hv/macos/cpu.rs +++ b/src/core/src/vmm/hv/macos/cpu.rs @@ -60,7 +60,7 @@ impl<'a> HfCpu<'a> { } #[cfg(target_arch = "aarch64")] - fn read_sys(&self, reg: hv_sys::hv_sys_reg_t) -> Result> { + pub fn read_sys(&self, reg: hv_sys::hv_sys_reg_t) -> Result> { use hv_sys::hv_vcpu_get_sys_reg; let mut v = 0; @@ -130,7 +130,6 @@ impl<'a> Cpu for HfCpu<'a> { fn states(&mut self) -> Result, Self::GetStatesErr> { Ok(HfStates { cpu: self, - id_aa64mmfr0: State::None, pstate: State::None, sctlr_el1: State::None, mair_el1: State::None, @@ -190,8 +189,6 @@ impl<'a> Drop for HfCpu<'a> { /// Implementation of [`Cpu::States`] for Hypervisor Framework. pub struct HfStates<'a, 'b> { cpu: &'a mut HfCpu<'b>, - #[cfg(target_arch = "aarch64")] - id_aa64mmfr0: State, #[cfg(target_arch = "x86_64")] rsp: State, #[cfg(target_arch = "x86_64")] @@ -237,25 +234,6 @@ pub struct HfStates<'a, 'b> { impl<'a, 'b> CpuStates for HfStates<'a, 'b> { type Err = StatesError; - #[cfg(target_arch = "aarch64")] - fn get_id_aa64mmfr0(&mut self) -> Result { - use hv_sys::hv_sys_reg_t_HV_SYS_REG_ID_AA64MMFR0_EL1 as HV_SYS_REG_ID_AA64MMFR0_EL1; - - let v = match self.id_aa64mmfr0 { - State::None => { - let v = self - .cpu - .read_sys(HV_SYS_REG_ID_AA64MMFR0_EL1) - .map_err(StatesError::ReadRegisterFailed)?; - self.id_aa64mmfr0 = State::Clean(v); - v - } - State::Clean(v) | State::Dirty(v) => v, - }; - - Ok(v) - } - #[cfg(target_arch = "x86_64")] fn set_rdi(&mut self, v: usize) { todo!() diff --git a/src/core/src/vmm/hv/macos/mod.rs b/src/core/src/vmm/hv/macos/mod.rs index 16fb9bdb9..594198f67 100644 --- a/src/core/src/vmm/hv/macos/mod.rs +++ b/src/core/src/vmm/hv/macos/mod.rs @@ -1,6 +1,7 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 use self::cpu::HfCpu; use self::vm::Vm; -use super::Hypervisor; +use super::{CpuFeats, Hypervisor}; use crate::vmm::hw::Ram; use crate::vmm::VmmError; use hv_sys::hv_vcpu_create; @@ -40,6 +41,85 @@ impl Hypervisor for Hf { type Cpu<'a> = HfCpu<'a>; type CpuErr = HfCpuError; + #[cfg(target_arch = "aarch64")] + fn cpu_features(&mut self) -> Result { + use hv_sys::hv_sys_reg_t_HV_SYS_REG_ID_AA64MMFR0_EL1 as HV_SYS_REG_ID_AA64MMFR0_EL1; + + // Load ID_AA64MMFR0_EL1. + let cpu = self.create_cpu(0)?; + let reg = cpu + .read_sys(HV_SYS_REG_ID_AA64MMFR0_EL1) + .map_err(HfCpuError::ReadMmfr0Failed)?; + + // FEAT_ExS. + let feat_exs = match (reg & 0xF00000000000) >> 44 { + 0b0000 => false, + 0b0001 => true, + _ => unreachable!(), + }; + + // TGran4. + let tgran4 = match (reg & 0xF0000000) >> 28 { + 0b0000 | 0b0001 => true, + 0b1111 => false, + _ => unreachable!(), + }; + + // TGran64. + let tgran64 = match (reg & 0xF000000) >> 24 { + 0b0000 => true, + 0b1111 => false, + _ => unreachable!(), + }; + + // TGran16. + let tgran16 = match (reg & 0xF00000) >> 20 { + 0b0000 => false, + 0b0001 | 0b0010 => true, + _ => unreachable!(), + }; + + // BigEnd. + let big_end = match (reg & 0xF00) >> 8 { + 0b0000 => false, + 0b0001 => true, + _ => unreachable!(), + }; + + // BigEndEL0. + let big_end_el0 = (big_end == false).then(|| match (reg & 0xF0000) >> 16 { + 0b0000 => false, + 0b0001 => true, + _ => unreachable!(), + }); + + // ASIDBits. + let asid16 = match (reg & 0xF0) >> 4 { + 0b0000 => false, + 0b0010 => true, + _ => unreachable!(), + }; + + // PARange. + let pa_range = (reg & 0xF).try_into().unwrap(); + + Ok(CpuFeats { + feat_exs, + tgran4, + tgran64, + tgran16, + big_end, + big_end_el0, + asid16, + pa_range, + }) + } + + #[cfg(target_arch = "x86_64")] + fn cpu_features(&mut self) -> Result { + Ok(CpuFeats {}) + } + fn create_cpu(&self, _: usize) -> Result, Self::CpuErr> { let mut instance = 0; @@ -74,4 +154,8 @@ impl Hypervisor for Hf { pub enum HfCpuError { #[error("couldn't create a vCPU ({0:#x})")] CreateVcpuFailed(NonZero), + + #[cfg(target_arch = "aarch64")] + #[error("couldn't read ID_AA64MMFR0_EL1 ({0:#x})")] + ReadMmfr0Failed(NonZero), } diff --git a/src/core/src/vmm/hv/mod.rs b/src/core/src/vmm/hv/mod.rs index ae5b1ec91..dbb699609 100644 --- a/src/core/src/vmm/hv/mod.rs +++ b/src/core/src/vmm/hv/mod.rs @@ -4,6 +4,11 @@ use super::VmmError; use std::error::Error; use std::sync::Arc; +pub use self::arch::*; + +#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")] +#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] +mod arch; #[cfg(target_os = "linux")] mod linux; #[cfg(target_os = "macos")] @@ -33,6 +38,8 @@ pub trait Hypervisor: Send + Sync { Self: 'a; type CpuErr: Error + Send + 'static; + fn cpu_features(&mut self) -> Result; + /// This method must be called by a thread that is going to drive the returned CPU. fn create_cpu(&self, id: usize) -> Result, Self::CpuErr>; } @@ -58,9 +65,6 @@ pub trait Cpu { pub trait CpuStates { type Err: Error + Send + 'static; - #[cfg(target_arch = "aarch64")] - fn get_id_aa64mmfr0(&mut self) -> Result; - #[cfg(target_arch = "x86_64")] fn set_rdi(&mut self, v: usize); diff --git a/src/core/src/vmm/hv/windows/mod.rs b/src/core/src/vmm/hv/windows/mod.rs index e75e0c175..3e64d168f 100644 --- a/src/core/src/vmm/hv/windows/mod.rs +++ b/src/core/src/vmm/hv/windows/mod.rs @@ -1,6 +1,6 @@ use self::cpu::WhpCpu; use self::partition::Partition; -use super::Hypervisor; +use super::{CpuFeats, Hypervisor}; use crate::vmm::hw::Ram; use crate::vmm::VmmError; use std::sync::Arc; @@ -39,6 +39,10 @@ impl Hypervisor for Whp { type Cpu<'a> = WhpCpu<'a>; type CpuErr = WhpCpuError; + fn cpu_features(&mut self) -> Result { + Ok(CpuFeats {}) + } + fn create_cpu(&self, id: usize) -> Result, Self::CpuErr> { let id = id.try_into().unwrap(); diff --git a/src/core/src/vmm/hv/x86_64.rs b/src/core/src/vmm/hv/x86_64.rs new file mode 100644 index 000000000..c792c6c56 --- /dev/null +++ b/src/core/src/vmm/hv/x86_64.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 + +/// Features available on a CPU. +pub struct CpuFeats {} diff --git a/src/core/src/vmm/mod.rs b/src/core/src/vmm/mod.rs index 2bf4f73e1..d71157c29 100644 --- a/src/core/src/vmm/mod.rs +++ b/src/core/src/vmm/mod.rs @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -use self::hv::{Cpu, CpuExit, CpuIo, Hypervisor}; +use self::hv::{Cpu, CpuExit, CpuFeats, CpuIo, Hypervisor}; use self::hw::{setup_devices, Device, DeviceContext, DeviceTree, Ram, RamBuilder, RamMap}; use self::kernel::{ Kernel, PT_DYNAMIC, PT_GNU_EH_FRAME, PT_GNU_RELRO, PT_GNU_STACK, PT_LOAD, PT_NOTE, PT_PHDR, @@ -393,7 +393,7 @@ pub unsafe extern "C" fn vmm_run( // Setup hypervisor. let ram = Arc::new(ram); - let hv = match self::hv::new(8, ram.clone()) { + let mut hv = match self::hv::new(8, ram.clone()) { Ok(v) => v, Err(e) => { *err = RustError::with_source("couldn't setup a hypervisor", e); @@ -401,6 +401,15 @@ pub unsafe extern "C" fn vmm_run( } }; + // Load CPU features. + let feats = match hv.cpu_features() { + Ok(v) => v, + Err(e) => { + *err = RustError::with_source("couldn't get available vCPU features", e); + return null_mut(); + } + }; + // Setup screen. let screen = match self::screen::Default::new(screen) { Ok(v) => v, @@ -416,6 +425,7 @@ pub unsafe extern "C" fn vmm_run( hv, ram, screen: screen.buffer().clone(), + feats, devices, shutdown: shutdown.clone(), }; @@ -483,7 +493,7 @@ fn main_cpu( } }; - if let Err(e) = self::arch::setup_main_cpu(&mut cpu, entry, map) { + if let Err(e) = self::arch::setup_main_cpu(&mut cpu, entry, map, &args.feats) { status.send(Err(e)).unwrap(); return; } @@ -656,6 +666,7 @@ struct CpuArgs { hv: H, ram: Arc, screen: Arc<::Buffer>, + feats: CpuFeats, devices: Arc, shutdown: Arc, } @@ -736,10 +747,6 @@ enum MainCpuError { #[error("couldn't get vCPU states")] GetCpuStatesFailed(#[source] Box), - #[cfg(target_arch = "aarch64")] - #[error("couldn't get ID_AA64MMFR0_EL1")] - GetIdAa64mmfr0Failed(#[source] Box), - #[cfg(target_arch = "aarch64")] #[error("vCPU does not support {0:#x} page size")] PageSizeNotSupported(NonZero), diff --git a/src/core/src/vmm/x86_64.rs b/src/core/src/vmm/x86_64.rs index e925b2edd..5fe8a32c1 100644 --- a/src/core/src/vmm/x86_64.rs +++ b/src/core/src/vmm/x86_64.rs @@ -1,8 +1,13 @@ -use super::hv::{Cpu, CpuStates}; +use super::hv::{Cpu, CpuFeats, CpuStates}; use super::hw::RamMap; use super::MainCpuError; -pub fn setup_main_cpu(cpu: &mut impl Cpu, entry: usize, map: RamMap) -> Result<(), MainCpuError> { +pub fn setup_main_cpu( + cpu: &mut impl Cpu, + entry: usize, + map: RamMap, + _: &CpuFeats, +) -> Result<(), MainCpuError> { // Set CR3 to page-map level-4 table. let mut states = cpu .states()