Loads ID_AA64MMFR0_EL1 from HV_FEATURE_REG_ID_AA64MMFR0_EL1 (#978)
Some checks failed
Development Build / Build (push) Failing after 0s
Development Build / Update PRs (push) Failing after 0s
Housekeep / Housekeep (push) Failing after 0s

This commit is contained in:
Putta Khunchalee 2024-09-11 03:45:32 +07:00 committed by GitHub
parent 2c7fa2d06d
commit c41cedf00d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 217 additions and 109 deletions

View File

@ -16,7 +16,7 @@ pub fn setup_main_cpu(
match map.page_size.get() {
0x4000 => {
if !feats.tgran16 {
if feats.mmfr0.t_gran16() == 0b0000 {
return Err(MainCpuError::PageSizeNotSupported(map.page_size));
}
}
@ -24,7 +24,7 @@ pub fn setup_main_cpu(
}
// Check if CPU support at least 36 bits physical address.
if feats.pa_range == 0 {
if feats.mmfr0.pa_range() == 0 {
return Err(MainCpuError::PhysicalAddressTooSmall);
}

View File

@ -3,40 +3,8 @@ use bitfield_struct::bitfield;
/// 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<bool>,
/// 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,
/// Raw value of `ID_AA64MMFR0_EL1`.
pub mmfr0: Mmfr0,
}
/// Represents a value of `PSTATE`.
@ -53,3 +21,170 @@ pub struct Pstate {
#[bits(22)]
__: u32,
}
/// Represents a value of `ID_AA64MMFR0_EL1`.
#[bitfield(u64)]
pub struct Mmfr0 {
/// 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.
/// - `0b0110`: *When FEAT_LPA is implemented or FEAT_LPA2 is implemented:* 52 bits, 4PB.
/// - `0b0111`: *When FEAT_D128 is implemented:* 56 bits, 64PB.
///
/// All other values are reserved.
#[bits(4)]
pub pa_range: u8,
/// Number of ASID bits.
///
/// - `0b0000`: 8 bits.
/// - `0b0010`: 16 bits.
///
/// All other values are reserved.
#[bits(4)]
pub asid_bits: u8,
/// Indicates support for mixed-endian configuration.
///
/// - `0b0000`: No mixed-endian support. The SCTLR_ELx.EE bits have a fixed value. See the
/// `big_end_el0` field, for whether EL0 supports mixed-endian.
/// - `0b0001`: Mixed-endian support. The SCTLR_ELx.EE and SCTLR_EL1.E0E bits can be configured.
///
/// All other values are reserved.
#[bits(4)]
pub big_end: u8,
/// Indicates support for a distinction between Secure and Non-secure Memory.
///
/// - `0b0000`: Does not support a distinction between Secure and Non-secure Memory.
/// - `0b0001`: Does support a distinction between Secure and Non-secure Memory.
///
/// All other values are reserved.
///
/// If EL3 is implemented, the value `0b0000` is not permitted.
#[bits(4)]
pub sns_mem: u8,
/// Indicates support for mixed-endian at EL0 only.
///
/// - `0b0000`: No mixed-endian support at EL0. The SCTLR_EL1.E0E bit has a fixed value.
/// - `0b0001`: Mixed-endian support at EL0. The SCTLR_EL1.E0E bit can be configured.
///
/// All other values are reserved.
///
/// This field is invalid and is `RES0` if `big_end` is not `0b0000`.
#[bits(4)]
pub big_end_el0: u8,
/// Indicates support for 16KB memory translation granule size.
///
/// - `0b0000`: 16KB granule not supported.
/// - `0b0001`: 16KB granule supported.
/// - `0b0010`: *When FEAT_LPA2 is implemented:* 16KB granule supports 52-bit input addresses
/// and can describe 52-bit output addresses.
///
/// All other values are reserved.
#[bits(4)]
pub t_gran16: u8,
/// Indicates support for 64KB memory translation granule size.
///
/// - `0b0000`: 64KB granule supported.
/// - `0b1111`: 64KB granule not supported.
///
/// All other values are reserved.
#[bits(4)]
pub t_gran64: u8,
/// Indicates support for 4KB memory translation granule size.
///
/// - `0b0000`: 4KB granule supported.
/// - `0b0001`: *When FEAT_LPA2 is implemented:* 4KB granule supports 52-bit input addresses and
/// can describe 52-bit output addresses.
/// - `0b1111`: 4KB granule not supported.
///
/// All other values are reserved.
#[bits(4)]
pub t_gran4: u8,
/// Indicates support for 16KB memory granule size at stage 2.
///
/// - `0b0000`: Support for 16KB granule at stage 2 is identified in the `t_gran16` field.
/// - `0b0001`: 16KB granule not supported at stage 2.
/// - `0b0010`: 16KB granule supported at stage 2.
/// - `0b0011`: *When FEAT_LPA2 is implemented:* 16KB granule at stage 2 supports 52-bit input
/// addresses and can describe 52-bit output addresses.
///
/// All other values are reserved.
#[bits(4)]
pub t_gran16_2: u8,
/// Indicates support for 64KB memory granule size at stage 2.
///
/// - `0b0000`: Support for 64KB granule at stage 2 is identified in the `t_gran64` field.
/// - `0b0001`: 64KB granule not supported at stage 2.
/// - `0b0010`: 64KB granule supported at stage 2.
///
/// All other values are reserved.
#[bits(4)]
pub t_gran64_2: u8,
/// Indicates support for 4KB memory granule size at stage 2.
///
/// - `0b0000`: Support for 4KB granule at stage 2 is identified in the `t_gran4` field.
/// - `0b0001`: 4KB granule not supported at stage 2.
/// - `0b0010`: 4KB granule supported at stage 2.
/// - `0b0011`: *When FEAT_LPA2 is implemented:* 4KB granule at stage 2 supports 52-bit input
/// addresses and can describe 52-bit output addresses.
///
/// All other values are reserved.
#[bits(4)]
pub t_gran4_2: u8,
/// Indicates support for disabling context synchronizing exception entry and exit.
///
/// - `0b0000`: All exception entries and exits are context synchronization events.
/// - `0b0001`: Non-context synchronizing exception entry and exit are supported.
///
/// All other values are reserved.
///
/// FEAT_ExS implements the functionality identified by the value `0b0001`.
#[bits(4)]
pub exs: u8,
__: u8,
/// Indicates presence of the Fine-Grained Trap controls.
///
/// - `0b0000`: Fine-grained trap controls are not implemented.
/// - `0b0001`: Fine-grained trap controls are implemented. Supports:
/// - If EL2 is implemented, the HAFGRTR_EL2, HDFGRTR_EL2, HDFGWTR_EL2, HFGRTR_EL2, HFGITR_EL2
/// and HFGWTR_EL2 registers, and their associated traps.
/// - If EL2 is implemented, MDCR_EL2.TDCC.
/// - If EL3 is implemented, MDCR_EL3.TDCC.
/// - If both EL2 and EL3 are implemented, SCR_EL3.FGTEn.
/// - `0b0010`: As `0b0001`, and also includes support for:
/// - If EL2 is implemented, the HDFGRTR2_EL2, HDFGWTR2_EL2, HFGITR2_EL2, HFGRTR2_EL2, and
/// HFGWTR2_EL2 registers, and their associated traps.
/// - If both EL2 and EL3 are implemented, SCR_EL3.FGTEn2.
///
/// All other values are reserved.
///
/// FEAT_FGT implements the functionality identified by the value `0b0001`.
///
/// FEAT_FGT2 implements the functionality identified by the value `0b0010`.
///
/// From Armv8.6, the value `0b0000` is not permitted.
///
/// From Armv8.9, the value `0b0001` is not permitted.
#[bits(4)]
pub fgt: u8,
/// Indicates presence of Enhanced Counter Virtualization.
///
/// - `0b0000`: Enhanced Counter Virtualization is not implemented.
/// - `0b0001`: Enhanced Counter Virtualization is implemented. Supports
/// CNTHCTL_EL2.{EL1TVT, EL1TVCT, EL1NVPCT, EL1NVVCT, EVNTIS}, CNTKCTL_EL1.EVNTIS,
/// CNTPCTSS_EL0 counter views, and CNTVCTSS_EL0 counter views. Extends the PMSCR_EL1.PCT,
/// PMSCR_EL2.PCT, TRFCR_EL1.TS, and TRFCR_EL2.TS fields.
/// - `0b0010`: As `0b0001`, and also includes support for CNTHCTL_EL2.ECV and CNTPOFF_EL2.
///
/// All other values are reserved.
///
/// FEAT_ECV implements the functionality identified by the values `0b0001` and `0b0010`
///
/// From Armv8.6, the only permitted values are `0b0001` and `0b0010`.
#[bits(4)]
pub ecv: u8,
}

View File

@ -3,8 +3,7 @@ use self::cpu::HfCpu;
use super::{CpuFeats, Hypervisor};
use crate::vmm::ram::Ram;
use crate::vmm::VmmError;
use hv_sys::{hv_vcpu_create, hv_vm_create, hv_vm_destroy, hv_vm_map};
use std::ffi::c_int;
use hv_sys::{hv_return_t, hv_vcpu_create, hv_vm_create, hv_vm_destroy, hv_vm_map};
use std::num::NonZero;
use thiserror::Error;
@ -18,10 +17,10 @@ pub fn new(_: usize, ram: Ram) -> Result<impl Hypervisor, VmmError> {
}
/// Implementation of [`Hypervisor`] using Hypervisor Framework.
///
/// Fields in this struct need to drop in a correct order.
struct Hvf {
ram: Ram,
#[cfg(target_arch = "aarch64")]
cpu_config: hv_sys::hv_vcpu_config_t,
}
impl Hvf {
@ -33,7 +32,11 @@ impl Hvf {
let ret = unsafe { hv_vm_create(0) };
let hv = match NonZero::new(ret) {
Some(ret) => return Err(VmmError::CreateVmFailed(ret)),
None => Self { ram },
None => Self {
ram,
#[cfg(target_arch = "aarch64")]
cpu_config: unsafe { hv_sys::hv_vcpu_config_create() },
},
};
// Set RAM.
@ -46,10 +49,33 @@ impl Hvf {
None => Ok(hv),
}
}
#[cfg(target_arch = "aarch64")]
fn read_feature_reg(
&mut self,
reg: hv_sys::hv_feature_reg_t,
) -> Result<u64, NonZero<hv_return_t>> {
use hv_sys::hv_vcpu_config_get_feature_reg;
let mut val = 0;
let ret = unsafe { hv_vcpu_config_get_feature_reg(self.cpu_config, reg, &mut val) };
match NonZero::new(ret) {
Some(e) => Err(e),
None => Ok(val),
}
}
}
impl Drop for Hvf {
fn drop(&mut self) {
// Free CPU config.
#[cfg(target_arch = "aarch64")]
unsafe {
os_release(self.cpu_config.cast())
};
// Destroy VM.
let status = unsafe { hv_vm_destroy() };
if status != 0 {
@ -64,75 +90,14 @@ impl Hypervisor for Hvf {
#[cfg(target_arch = "aarch64")]
fn cpu_features(&mut self) -> Result<CpuFeats, Self::CpuErr> {
use hv_sys::hv_sys_reg_t_HV_SYS_REG_ID_AA64MMFR0_EL1 as HV_SYS_REG_ID_AA64MMFR0_EL1;
use hv_sys::hv_feature_reg_t_HV_FEATURE_REG_ID_AA64MMFR0_EL1 as HV_FEATURE_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)
let mmfr0 = self
.read_feature_reg(HV_FEATURE_REG_ID_AA64MMFR0_EL1)
.map_err(HvfCpuError::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,
mmfr0: mmfr0.into(),
})
}
@ -153,7 +118,7 @@ impl Hypervisor for Hvf {
fn create_cpu(&self, _: usize) -> Result<Self::Cpu<'_>, Self::CpuErr> {
let mut instance = 0;
let mut exit = std::ptr::null_mut();
let ret = unsafe { hv_vcpu_create(&mut instance, &mut exit, std::ptr::null_mut()) };
let ret = unsafe { hv_vcpu_create(&mut instance, &mut exit, self.cpu_config) };
match NonZero::new(ret) {
Some(e) => Err(HvfCpuError::CreateVcpuFailed(e)),
@ -173,13 +138,21 @@ impl Hypervisor for Hvf {
}
}
unsafe impl Send for Hvf {}
unsafe impl Sync for Hvf {}
/// Implementation of [`Hypervisor::CpuErr`].
#[derive(Debug, Error)]
pub enum HvfCpuError {
#[error("couldn't create a vCPU ({0:#x})")]
CreateVcpuFailed(NonZero<c_int>),
CreateVcpuFailed(NonZero<hv_return_t>),
#[cfg(target_arch = "aarch64")]
#[error("couldn't read ID_AA64MMFR0_EL1 ({0:#x})")]
ReadMmfr0Failed(NonZero<hv_sys::hv_return_t>),
ReadMmfr0Failed(NonZero<hv_return_t>),
}
#[cfg(target_arch = "aarch64")]
extern "C" {
fn os_release(object: *mut std::ffi::c_void);
}