From aa978f1a2d49afe97b2090089ce9affbfa80d60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Mon, 8 Sep 2025 15:41:21 +0800 Subject: [PATCH 1/7] fix: irq settings refactor: add page level detect and remove feature --- .cargo/config.toml | 2 + Cargo.toml | 24 +++++------- src/exception_utils.rs | 3 +- src/pcpu.rs | 7 +--- src/vcpu.rs | 89 ++++++++++++++++++------------------------ 5 files changed, 51 insertions(+), 74 deletions(-) create mode 100644 .cargo/config.toml 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/Cargo.toml b/Cargo.toml index 655fb6d..4d0b023 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,7 +21,6 @@ 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"]} 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..7e88aff 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`. diff --git a/src/vcpu.rs b/src/vcpu.rs index 724463c..5e0b22c 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(); - } + + VTCR_EL2::IRGN0::NormalWBRAWA) + .value; - // 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(); - } - - 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,33 @@ impl Aarch64VCpu { } } } + +fn probe_vtcr_support() -> u64 { + let pa_bits = 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, + }; + + let mut val = match pa_bits { + 44.. => 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 +} From c6eaba4fc0a7941b42ed2ade356a5f595baeddd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Mon, 8 Sep 2025 15:48:10 +0800 Subject: [PATCH 2/7] fix: ci --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From b1f5da90e18101348da85ee74e32fc7bc87af026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Tue, 9 Sep 2025 11:16:57 +0800 Subject: [PATCH 3/7] feat: add max gpt level api --- Cargo.toml | 3 ++- src/lib.rs | 1 + src/pcpu.rs | 6 +++++- src/vcpu.rs | 33 +++++++++++++++++++++++++++------ 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4d0b023..594b78b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,5 +27,6 @@ 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/lib.rs b/src/lib.rs index 7328d2a..c30954c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,3 +29,4 @@ pub fn has_hardware_support() -> bool { // Current just return true by default. true } + diff --git a/src/pcpu.rs b/src/pcpu.rs index 7e88aff..5960732 100644 --- a/src/pcpu.rs +++ b/src/pcpu.rs @@ -23,7 +23,7 @@ static ORI_EXCEPTION_VECTOR_BASE: usize = 0; #[percpu::def_percpu] pub static IRQ_HANDLER: OnceCell<&(dyn Fn() + Send + Sync)> = OnceCell::new(); -unsafe extern "C" { +unsafe extern { fn exception_vector_base_vcpu(); } @@ -83,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 5e0b22c..213e5d5 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -215,7 +215,7 @@ impl Aarch64VCpu { /// When a VM-Exit happens when guest's vCpu is running, /// the control flow will be redirected to this function through `return_run_guest`. #[unsafe(naked)] - unsafe extern "C" fn run_guest(&mut self) -> usize { + unsafe extern fn run_guest(&mut self) -> usize { // Fixes: https://github.com/arceos-hypervisor/arm_vcpu/issues/22 // // The original issue seems to be caused by an unexpected compiler optimization that takes @@ -397,8 +397,8 @@ impl Aarch64VCpu { } } -fn probe_vtcr_support() -> u64 { - let pa_bits = match ID_AA64MMFR0_EL1.read_as_enum(ID_AA64MMFR0_EL1::PARange) { +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, @@ -407,10 +407,31 @@ fn probe_vtcr_support() -> u64 { 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 pa_bits { - 44.. => VTCR_EL2::SL0::Granule4KBLevel0 + VTCR_EL2::T0SZ.val(64 - 48), + 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), }; From 07f37ba6bc4745980d23518df7a72a4c95cddc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Tue, 9 Sep 2025 11:59:46 +0800 Subject: [PATCH 4/7] fmt code --- src/exception.rs | 2 +- src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/exception.rs b/src/exception.rs index c7357da..5363805 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -340,7 +340,7 @@ fn current_el_sync_handler(tf: &mut TrapFrame) { /// invoked as part of the low-level hypervisor or VM exit handling routines. #[unsafe(naked)] #[unsafe(no_mangle)] -unsafe extern "C" fn vmexit_trampoline() -> ! { +unsafe extern fn vmexit_trampoline() -> ! { core::arch::naked_asm!( // Curretly `sp` points to the base address of `Aarch64VCpu.ctx`, which stores guest's `TrapFrame`. "add x9, sp, 34 * 8", // Skip the exception frame. diff --git a/src/lib.rs b/src/lib.rs index c30954c..7328d2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,4 +29,3 @@ pub fn has_hardware_support() -> bool { // Current just return true by default. true } - From 52b1ed3a919ac9cb447200d14496ed8ecafc5d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Tue, 9 Sep 2025 12:00:59 +0800 Subject: [PATCH 5/7] fix: change function signature of run_guest to use "C" calling convention --- src/vcpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vcpu.rs b/src/vcpu.rs index 213e5d5..371b22c 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -215,7 +215,7 @@ impl Aarch64VCpu { /// When a VM-Exit happens when guest's vCpu is running, /// the control flow will be redirected to this function through `return_run_guest`. #[unsafe(naked)] - unsafe extern fn run_guest(&mut self) -> usize { + unsafe extern "C" fn run_guest(&mut self) -> usize { // Fixes: https://github.com/arceos-hypervisor/arm_vcpu/issues/22 // // The original issue seems to be caused by an unexpected compiler optimization that takes From 980a003da1c11470fce5c3f51147b6e171eb25fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Tue, 9 Sep 2025 12:01:30 +0800 Subject: [PATCH 6/7] fix: specify calling convention for external function declaration --- src/pcpu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pcpu.rs b/src/pcpu.rs index 5960732..1345623 100644 --- a/src/pcpu.rs +++ b/src/pcpu.rs @@ -23,7 +23,7 @@ static ORI_EXCEPTION_VECTOR_BASE: usize = 0; #[percpu::def_percpu] pub static IRQ_HANDLER: OnceCell<&(dyn Fn() + Send + Sync)> = OnceCell::new(); -unsafe extern { +unsafe extern "C" { fn exception_vector_base_vcpu(); } From b24cc3635c049302ab8d58d3b54007bb5a053a96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E7=9D=BF?= Date: Tue, 9 Sep 2025 12:02:38 +0800 Subject: [PATCH 7/7] fix: specify "C" calling convention for vmexit_trampoline function --- src/exception.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/exception.rs b/src/exception.rs index 5363805..c7357da 100644 --- a/src/exception.rs +++ b/src/exception.rs @@ -340,7 +340,7 @@ fn current_el_sync_handler(tf: &mut TrapFrame) { /// invoked as part of the low-level hypervisor or VM exit handling routines. #[unsafe(naked)] #[unsafe(no_mangle)] -unsafe extern fn vmexit_trampoline() -> ! { +unsafe extern "C" fn vmexit_trampoline() -> ! { core::arch::naked_asm!( // Curretly `sp` points to the base address of `Aarch64VCpu.ctx`, which stores guest's `TrapFrame`. "add x9, sp, 34 * 8", // Skip the exception frame.