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