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 +}