diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 0000000..62d33ee
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,2 @@
+[build]
+target = "aarch64-unknown-none-softfloat"
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c91435c..8764187 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -44,16 +44,16 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
- toolchain: nightly-2024-12-25
+ toolchain: nightly-2025-05-20
- name: Build docs
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
run: |
cargo doc --no-deps --all-features
- printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
+ printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/aarch64-unknown-none-softfloat/doc/index.html
- name: Deploy to Github Pages
if: ${{ github.ref == env.default-branch }}
uses: JamesIves/github-pages-deploy-action@v4
with:
single-commit: true
branch: gh-pages
- folder: target/doc
+ folder: target/aarch64-unknown-none-softfloat/doc
diff --git a/Cargo.toml b/Cargo.toml
index 655fb6d..594b78b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,22 +1,19 @@
[package]
-edition = "2024"
-name = "arm_vcpu"
-version = "0.1.1"
authors = [
- "KeYang Hu ",
- "Mingxian Su ",
- "ShiMei Tang ",
- "DeBin Luo ",
- "周睿 "
+ "KeYang Hu ",
+ "Mingxian Su ",
+ "ShiMei Tang ",
+ "DeBin Luo ",
+ "周睿 ",
]
+categories = ["embedded", "no-std"]
description = "Aarch64 VCPU implementation for Arceos Hypervisor"
+edition = "2024"
+keywords = ["hypervisor", "aarch64", "vcpu"]
license = "MIT OR Apache-2.0"
+name = "arm_vcpu"
repository = "https://github.com/arceos-hypervisor/arm_vcpu"
-categories = ["embedded", "no-std"]
-keywords = ["hypervisor", "aarch64", "vcpu"]
-
-[features]
-4-level-ept = []
+version = "0.1.1"
[dependencies]
log = "0.4"
@@ -24,12 +21,12 @@ spin = "0.10"
aarch64-cpu = "10.0"
numeric-enum-macro = "0.2"
-tock-registers = "0.9"
axerrno = "0.1.0"
percpu = {version = "0.2.0", features = ["arm-el2"]}
axaddrspace = "0.1"
axdevice_base = "0.1.0"
-axvcpu = "0.1.0"
+axvcpu = {git="https://github.com/arceos-hypervisor/axvcpu.git", branch="next"}
+# axvcpu = "0.1.0"
axvisor_api = "0.1.0"
diff --git a/src/exception_utils.rs b/src/exception_utils.rs
index 0ae5e37..f2889ed 100644
--- a/src/exception_utils.rs
+++ b/src/exception_utils.rs
@@ -1,7 +1,6 @@
-use aarch64_cpu::registers::{ESR_EL2, FAR_EL2, PAR_EL1};
+use aarch64_cpu::registers::*;
use axaddrspace::GuestPhysAddr;
use axerrno::{AxResult, ax_err};
-use tock_registers::interfaces::*;
/// Retrieves the Exception Syndrome Register (ESR) value from EL2.
///
diff --git a/src/pcpu.rs b/src/pcpu.rs
index 296cbbb..1345623 100644
--- a/src/pcpu.rs
+++ b/src/pcpu.rs
@@ -3,7 +3,6 @@ use core::{cell::OnceCell, marker::PhantomData};
use aarch64_cpu::registers::*;
use axerrno::AxResult;
use axvcpu::{AxArchPerCpu, AxVCpuHal};
-use tock_registers::interfaces::ReadWriteable;
/// Per-CPU data. A pointer to this struct is loaded into TP when a CPU starts. This structure
#[repr(C)]
@@ -56,11 +55,7 @@ impl AxArchPerCpu for Aarch64PerCpu {
VBAR_EL2.set(exception_vector_base_vcpu as usize as _);
HCR_EL2.modify(
- HCR_EL2::VM::Enable
- + HCR_EL2::RW::EL1IsAarch64
- + HCR_EL2::IMO::EnableVirtualIRQ
- + HCR_EL2::FMO::EnableVirtualFIQ
- + HCR_EL2::TSC::EnableTrapEl1SmcToEl2,
+ HCR_EL2::VM::Enable + HCR_EL2::RW::EL1IsAarch64 + HCR_EL2::TSC::EnableTrapEl1SmcToEl2,
);
// Note that `ICH_HCR_EL2` is not the same as `HCR_EL2`.
@@ -88,4 +83,8 @@ impl AxArchPerCpu for Aarch64PerCpu {
HCR_EL2.set(HCR_EL2::VM::Disable.into());
Ok(())
}
+
+ fn max_guest_page_table_levels(&self) -> usize {
+ crate::vcpu::max_gpt_level(crate::vcpu::pa_bits())
+ }
}
diff --git a/src/vcpu.rs b/src/vcpu.rs
index 724463c..371b22c 100644
--- a/src/vcpu.rs
+++ b/src/vcpu.rs
@@ -167,60 +167,15 @@ impl Aarch64VCpu {
self.guest_system_regs.sctlr_el1 = 0x30C50830;
self.guest_system_regs.pmcr_el0 = 0;
- // use 3 level ept paging
- // - 4KiB granule (TG0)
- // - 39-bit address space (T0_SZ)
- // - start at level 1 (SL0)
- #[cfg(not(feature = "4-level-ept"))]
- {
- self.guest_system_regs.vtcr_el2 = (VTCR_EL2::PS::PA_40B_1TB
- + VTCR_EL2::TG0::Granule4KB
+ self.guest_system_regs.vtcr_el2 = probe_vtcr_support()
+ + (VTCR_EL2::TG0::Granule4KB
+ VTCR_EL2::SH0::Inner
+ VTCR_EL2::ORGN0::NormalWBRAWA
- + VTCR_EL2::IRGN0::NormalWBRAWA
- + VTCR_EL2::SL0.val(0b01)
- + VTCR_EL2::T0SZ.val(64 - 39))
- .into();
- }
-
- // use 4 level ept paging
- // - 4KiB granule (TG0)
- // - 48-bit address space (T0_SZ)
- // - start at level 0 (SL0)
- #[cfg(feature = "4-level-ept")]
- {
- // read PARange (bits 3:0)
- let parange = (ID_AA64MMFR0_EL1.get() & 0xF) as u8;
- // ARM Definition: 0x5 indicates 48 bits PA, 0x4 indicates 44 bits PA, and so on.
- if parange <= 0x4 {
- panic!(
- "CPU only supports {}-bit PA (< 44), \
- cannot enable 4-level EPT paging!",
- match parange {
- 0x0 => 32,
- 0x1 => 36,
- 0x2 => 40,
- 0x3 => 42,
- 0x4 => 44,
- _ => 48,
- }
- );
- }
- self.guest_system_regs.vtcr_el2 = (VTCR_EL2::PS::PA_48B_256TB
- + VTCR_EL2::TG0::Granule4KB
- + VTCR_EL2::SH0::Inner
- + VTCR_EL2::ORGN0::NormalWBRAWA
- + VTCR_EL2::IRGN0::NormalWBRAWA
- + VTCR_EL2::SL0.val(0b10) // 0b10 means start at level 0
- + VTCR_EL2::T0SZ.val(64 - 48))
- .into();
- }
+ + VTCR_EL2::IRGN0::NormalWBRAWA)
+ .value;
- let mut hcr_el2 = HCR_EL2::VM::Enable
- + HCR_EL2::RW::EL1IsAarch64
- + HCR_EL2::FMO::EnableVirtualFIQ
- + HCR_EL2::TSC::EnableTrapEl1SmcToEl2
- + HCR_EL2::RW::EL1IsAarch64;
+ let mut hcr_el2 =
+ HCR_EL2::VM::Enable + HCR_EL2::TSC::EnableTrapEl1SmcToEl2 + HCR_EL2::RW::EL1IsAarch64;
if !config.passthrough_interrupt {
// Set HCR_EL2.IMO will trap IRQs to EL2 while enabling virtual IRQs.
@@ -228,7 +183,7 @@ impl Aarch64VCpu {
// We must choose one of the two:
// - Enable virtual IRQs and trap physical IRQs to EL2.
// - Disable virtual IRQs and pass through physical IRQs to EL1.
- hcr_el2 += HCR_EL2::IMO::EnableVirtualIRQ;
+ hcr_el2 += HCR_EL2::IMO::EnableVirtualIRQ + HCR_EL2::FMO::EnableVirtualFIQ;
}
self.guest_system_regs.hcr_el2 = hcr_el2.into();
@@ -441,3 +396,54 @@ impl Aarch64VCpu {
}
}
}
+
+pub(crate) fn pa_bits() -> usize {
+ match ID_AA64MMFR0_EL1.read_as_enum(ID_AA64MMFR0_EL1::PARange) {
+ Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_32) => 32,
+ Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_36) => 36,
+ Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_40) => 40,
+ Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_42) => 42,
+ Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_44) => 44,
+ Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_48) => 48,
+ Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_52) => 52,
+ _ => 32,
+ }
+}
+
+#[allow(dead_code)]
+pub(crate) fn current_gpt_level() -> usize {
+ let t0sz = VTCR_EL2.read(VTCR_EL2::T0SZ) as usize;
+ match t0sz {
+ 16..=25 => 4,
+ 26..=35 => 3,
+ _ => 2,
+ }
+}
+
+pub(crate) fn max_gpt_level(pa_bits: usize) -> usize {
+ match pa_bits {
+ 44.. => 4,
+ _ => 3,
+ }
+}
+
+fn probe_vtcr_support() -> u64 {
+ let pa_bits = pa_bits();
+
+ let mut val = match max_gpt_level(pa_bits) {
+ 4 => VTCR_EL2::SL0::Granule4KBLevel0 + VTCR_EL2::T0SZ.val(64 - 48),
+ _ => VTCR_EL2::SL0::Granule4KBLevel1 + VTCR_EL2::T0SZ.val(64 - 39),
+ };
+
+ match pa_bits {
+ 52..=64 => val += VTCR_EL2::PS::PA_52B_4PB,
+ 48..=51 => val += VTCR_EL2::PS::PA_48B_256TB,
+ 44..=47 => val += VTCR_EL2::PS::PA_44B_16TB,
+ 42..=43 => val += VTCR_EL2::PS::PA_42B_4TB,
+ 40..=41 => val += VTCR_EL2::PS::PA_40B_1TB,
+ 36..=39 => val += VTCR_EL2::PS::PA_36B_64GB,
+ _ => val += VTCR_EL2::PS::PA_32B_4GB,
+ }
+
+ val.value
+}