Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements Hypervisor::cpu_features for Mac M1 #968

Merged
merged 1 commit into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions src/core/src/vmm/aarch64.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
// 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));
}
}
_ => todo!(),
}

// 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);
}

Expand Down
39 changes: 39 additions & 0 deletions src/core/src/vmm/hv/aarch64.rs
Original file line number Diff line number Diff line change
@@ -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<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,
}
6 changes: 5 additions & 1 deletion src/core/src/vmm/hv/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -104,6 +104,10 @@ impl Hypervisor for Kvm {
type Cpu<'a> = KvmCpu<'a>;
type CpuErr = KvmCpuError;

fn cpu_features(&mut self) -> Result<CpuFeats, Self::CpuErr> {
Ok(CpuFeats {})
}

fn create_cpu(&self, id: usize) -> Result<Self::Cpu<'_>, Self::CpuErr> {
use std::io::Error;

Expand Down
24 changes: 1 addition & 23 deletions src/core/src/vmm/hv/macos/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl<'a> HfCpu<'a> {
}

#[cfg(target_arch = "aarch64")]
fn read_sys(&self, reg: hv_sys::hv_sys_reg_t) -> Result<u64, NonZero<hv_sys::hv_return_t>> {
pub fn read_sys(&self, reg: hv_sys::hv_sys_reg_t) -> Result<u64, NonZero<hv_sys::hv_return_t>> {
use hv_sys::hv_vcpu_get_sys_reg;

let mut v = 0;
Expand Down Expand Up @@ -130,7 +130,6 @@ impl<'a> Cpu for HfCpu<'a> {
fn states(&mut self) -> Result<Self::States<'_>, Self::GetStatesErr> {
Ok(HfStates {
cpu: self,
id_aa64mmfr0: State::None,
pstate: State::None,
sctlr_el1: State::None,
mair_el1: State::None,
Expand Down Expand Up @@ -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<u64>,
#[cfg(target_arch = "x86_64")]
rsp: State<usize>,
#[cfg(target_arch = "x86_64")]
Expand Down Expand Up @@ -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<u64, Self::Err> {
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!()
Expand Down
86 changes: 85 additions & 1 deletion src/core/src/vmm/hv/macos/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<CpuFeats, Self::CpuErr> {
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<CpuFeats, Self::CpuErr> {
Ok(CpuFeats {})
}

fn create_cpu(&self, _: usize) -> Result<Self::Cpu<'_>, Self::CpuErr> {
let mut instance = 0;

Expand Down Expand Up @@ -74,4 +154,8 @@ impl Hypervisor for Hf {
pub enum HfCpuError {
#[error("couldn't create a vCPU ({0:#x})")]
CreateVcpuFailed(NonZero<c_int>),

#[cfg(target_arch = "aarch64")]
#[error("couldn't read ID_AA64MMFR0_EL1 ({0:#x})")]
ReadMmfr0Failed(NonZero<hv_sys::hv_return_t>),
}
10 changes: 7 additions & 3 deletions src/core/src/vmm/hv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -33,6 +38,8 @@ pub trait Hypervisor: Send + Sync {
Self: 'a;
type CpuErr: Error + Send + 'static;

fn cpu_features(&mut self) -> Result<CpuFeats, Self::CpuErr>;

/// This method must be called by a thread that is going to drive the returned CPU.
fn create_cpu(&self, id: usize) -> Result<Self::Cpu<'_>, Self::CpuErr>;
}
Expand All @@ -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<u64, Self::Err>;

#[cfg(target_arch = "x86_64")]
fn set_rdi(&mut self, v: usize);

Expand Down
6 changes: 5 additions & 1 deletion src/core/src/vmm/hv/windows/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -39,6 +39,10 @@ impl Hypervisor for Whp {
type Cpu<'a> = WhpCpu<'a>;
type CpuErr = WhpCpuError;

fn cpu_features(&mut self) -> Result<CpuFeats, Self::CpuErr> {
Ok(CpuFeats {})
}

fn create_cpu(&self, id: usize) -> Result<Self::Cpu<'_>, Self::CpuErr> {
let id = id.try_into().unwrap();

Expand Down
4 changes: 4 additions & 0 deletions src/core/src/vmm/hv/x86_64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

/// Features available on a CPU.
pub struct CpuFeats {}
21 changes: 14 additions & 7 deletions src/core/src/vmm/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -393,14 +393,23 @@ 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);
return null_mut();
}
};

// 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,
Expand All @@ -416,6 +425,7 @@ pub unsafe extern "C" fn vmm_run(
hv,
ram,
screen: screen.buffer().clone(),
feats,
devices,
shutdown: shutdown.clone(),
};
Expand Down Expand Up @@ -483,7 +493,7 @@ fn main_cpu<H: Hypervisor>(
}
};

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;
}
Expand Down Expand Up @@ -656,6 +666,7 @@ struct CpuArgs<H: Hypervisor> {
hv: H,
ram: Arc<Ram>,
screen: Arc<<self::screen::Default as Screen>::Buffer>,
feats: CpuFeats,
devices: Arc<DeviceTree>,
shutdown: Arc<AtomicBool>,
}
Expand Down Expand Up @@ -736,10 +747,6 @@ enum MainCpuError {
#[error("couldn't get vCPU states")]
GetCpuStatesFailed(#[source] Box<dyn Error + Send>),

#[cfg(target_arch = "aarch64")]
#[error("couldn't get ID_AA64MMFR0_EL1")]
GetIdAa64mmfr0Failed(#[source] Box<dyn Error + Send>),

#[cfg(target_arch = "aarch64")]
#[error("vCPU does not support {0:#x} page size")]
PageSizeNotSupported(NonZero<usize>),
Expand Down
9 changes: 7 additions & 2 deletions src/core/src/vmm/x86_64.rs
Original file line number Diff line number Diff line change
@@ -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()
Expand Down