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/check.yml b/.github/workflows/check.yml index 330fa15..a226604 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -63,4 +63,4 @@ jobs: - name: Build documentation env: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs - run: cargo doc --no-deps --target ${{ matrix.target }} --all-features + run: cargo doc --no-deps --target ${{ matrix.target }} --all-features \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 882ba93..9938e2c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -87,13 +87,30 @@ jobs: env: RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs run: | + # Build documentation cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + + # Auto-detect documentation directory + # Check if doc exists in target/doc or target/*/doc + if [ -d "target/doc" ]; then + DOC_DIR="target/doc" + else + # Find doc directory under target/*/doc pattern + DOC_DIR=$(find target -type d -name doc -path "target/*/doc" | head -n 1) + if [ -z "$DOC_DIR" ]; then + echo "Error: Could not find documentation directory" + exit 1 + fi + fi + + echo "Documentation found in: $DOC_DIR" + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > "${DOC_DIR}/index.html" + echo "DOC_DIR=${DOC_DIR}" >> $GITHUB_ENV - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: - path: target/doc + path: ${{ env.DOC_DIR }} deploy: name: Deploy to GitHub Pages @@ -106,4 +123,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e857b4..48c0f6c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -157,4 +157,4 @@ jobs: run: cargo publish --dry-run - name: Publish to crates.io - run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dc3b293..920eef1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,4 +47,4 @@ jobs: # - name: Run doc tests # run: cargo test --target ${{ matrix.target }} --doc - name: Run tests - run: echo "Tests are skipped!" + run: echo "Tests are skipped!" \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 1701f43..91b8f39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] edition = "2024" name = "arm_vcpu" -version = "0.2.0" +version = "0.2.2" authors = [ "KeYang Hu ", "Mingxian Su ", @@ -15,21 +15,17 @@ repository = "https://github.com/arceos-hypervisor/arm_vcpu" categories = ["embedded", "no-std"] keywords = ["hypervisor", "aarch64", "vcpu"] -[features] -4-level-ept = [] - [dependencies] log = "0.4" 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"]} +percpu = { version = "0.2.3-preview.1", features = ["arm-el2"] } -axaddrspace = "0.1" -axdevice_base = "0.1.0" -axvcpu = "0.1.0" +axaddrspace = "0.1.5" +axdevice_base = "0.2.1" +axvcpu = "0.2" axvisor_api = "0.1.0" diff --git a/src/exception_utils.rs b/src/exception_utils.rs index 449f727..f2889ed 100644 --- a/src/exception_utils.rs +++ b/src/exception_utils.rs @@ -1,21 +1,6 @@ -// Copyright 2025 The Axvisor Team -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -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 b495a2f..ece3076 100644 --- a/src/pcpu.rs +++ b/src/pcpu.rs @@ -17,7 +17,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)] @@ -70,11 +69,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 0f5e554..4abefae 100644 --- a/src/vcpu.rs +++ b/src/vcpu.rs @@ -181,60 +181,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. @@ -242,7 +197,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(); @@ -455,3 +410,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 +}