From fd0906e3dd208dcd7d9af8dc1b134adc6d782ed0 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Mon, 21 Apr 2025 12:22:02 +0800 Subject: [PATCH 1/5] fix: xstate load and restore now checks cpuid --- src/vmx/vcpu.rs | 92 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/src/vmx/vcpu.rs b/src/vmx/vcpu.rs index 5955a0f..da6e696 100644 --- a/src/vmx/vcpu.rs +++ b/src/vmx/vcpu.rs @@ -30,6 +30,9 @@ pub struct XState { guest_xcr0: u64, host_xss: u64, guest_xss: u64, + + xsave_available: bool, + xsaves_available: bool, } #[derive(PartialEq, Eq, Debug)] @@ -43,20 +46,91 @@ pub enum VmCpuMode { impl XState { /// Create a new [`XState`] instance with current host state fn new() -> Self { - let xcr0 = unsafe { xcr0_read().bits() }; - let xss = Msr::IA32_XSS.read(); + // Check if XSAVE is available + let xsave_available = Self::xsave_available(); + // Check if XSAVES and XRSTORS (as well as IA32_XSS) are available + let xsaves_available = if xsave_available { + Self::xsaves_available() + } else { + false + }; + + // Read XCR0 iff XSAVE is available + let xcr0 = if xsave_available { + unsafe { xcr0_read().bits() } + } else { + 0 + }; + // Read IA32_XSS iff XSAVES is available + let xss = if xsaves_available { + Msr::IA32_XSS.read() + } else { + 0 + }; Self { host_xcr0: xcr0, guest_xcr0: xcr0, host_xss: xss, guest_xss: xss, + xsave_available, + xsaves_available, } } - /// Enables extended processor state management instructions, including XGETBV and XSAVE. + /// Enable extended processor state management instructions, including XGETBV and XSAVE. pub fn enable_xsave() { - unsafe { Cr4::write(Cr4::read() | Cr4Flags::OSXSAVE) }; + if Self::xsave_available() { + unsafe { Cr4::write(Cr4::read() | Cr4Flags::OSXSAVE) }; + } + } + + /// Check if XSAVE is available on the current CPU. + pub fn xsave_available() -> bool { + let cpuid = CpuId::new(); + cpuid + .get_feature_info() + .map(|f| f.has_xsave()) + .unwrap_or(false) + } + + /// Check if XSAVES and XRSTORS (as well as IA32_XSS) are available on the current CPU. + pub fn xsaves_available() -> bool { + let cpuid = CpuId::new(); + cpuid + .get_extended_state_info() + .map(|f| f.has_xsaves_xrstors()) + .unwrap_or(false) + } + + /// Save the current host XCR0 and IA32_XSS values and load the guest values. + pub fn switch_to_guest(&mut self) { + unsafe { + if self.xsave_available { + self.host_xcr0 = xcr0_read().bits(); + xcr0_write(Xcr0::from_bits_unchecked(self.guest_xcr0)); + + if self.xsaves_available { + self.host_xss = Msr::IA32_XSS.read(); + Msr::IA32_XSS.write(self.guest_xss); + } + } + } + } + + /// Save the current guest XCR0 and IA32_XSS values and load the host values. + pub fn switch_to_host(&mut self) { + unsafe { + if self.xsave_available { + self.guest_xcr0 = xcr0_read().bits(); + xcr0_write(Xcr0::from_bits_unchecked(self.host_xcr0)); + + if self.xsaves_available { + self.guest_xss = Msr::IA32_XSS.read(); + Msr::IA32_XSS.write(self.host_xss); + } + } + } } } @@ -1008,17 +1082,11 @@ impl VmxVcpu { } fn load_guest_xstate(&mut self) { - unsafe { - xcr0_write(Xcr0::from_bits_unchecked(self.xstate.guest_xcr0)); - Msr::IA32_XSS.write(self.xstate.guest_xss); - } + self.xstate.switch_to_guest(); } fn load_host_xstate(&mut self) { - unsafe { - xcr0_write(Xcr0::from_bits_unchecked(self.xstate.host_xcr0)); - Msr::IA32_XSS.write(self.xstate.host_xss); - } + self.xstate.switch_to_host(); } } From 2a63d08eb2b320fc38c8a462b9928b7d6078dc46 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Mon, 21 Apr 2025 13:14:09 +0800 Subject: [PATCH 2/5] try suppress `unsafe_attr_outside_unsafe` for `#[naked]` --- src/vmx/vcpu.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vmx/vcpu.rs b/src/vmx/vcpu.rs index da6e696..3c858e3 100644 --- a/src/vmx/vcpu.rs +++ b/src/vmx/vcpu.rs @@ -812,6 +812,7 @@ macro_rules! vmx_entry_with { } impl VmxVcpu { + #[warn(unsafe_attr_outside_unsafe)] #[naked] /// Enter guest with vmlaunch. /// @@ -824,6 +825,7 @@ impl VmxVcpu { vmx_entry_with!("vmlaunch") } + #[warn(unsafe_attr_outside_unsafe)] #[naked] /// Enter guest with vmresume. /// @@ -832,6 +834,7 @@ impl VmxVcpu { vmx_entry_with!("vmresume") } + #[warn(unsafe_attr_outside_unsafe)] #[naked] /// Return after vm-exit. /// From 9564ae986640130fe2f54d93c87ab0a9c7a04075 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Mon, 21 Apr 2025 13:29:30 +0800 Subject: [PATCH 3/5] update nightly version --- .github/workflows/ci.yml | 2 +- src/vmx/vcpu.rs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20459a7..bac0c76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - rust-toolchain: [nightly-2024-12-25, nightly] + rust-toolchain: [nightly-2025-04-21, nightly] targets: [x86_64-unknown-none] steps: - uses: actions/checkout@v4 diff --git a/src/vmx/vcpu.rs b/src/vmx/vcpu.rs index 3c858e3..6a2f2f8 100644 --- a/src/vmx/vcpu.rs +++ b/src/vmx/vcpu.rs @@ -812,11 +812,10 @@ macro_rules! vmx_entry_with { } impl VmxVcpu { - #[warn(unsafe_attr_outside_unsafe)] - #[naked] + #[unsafe(naked)] /// Enter guest with vmlaunch. /// - /// `#[naked]` is essential here, without it the rust compiler will think `&mut self` is not used and won't give us correct %rdi. + /// `#[unsafe(naked)]` is essential here, without it the rust compiler will think `&mut self` is not used and won't give us correct %rdi. /// /// This function itself never returns, but [`Self::vmx_exit`] will do the return for this. /// @@ -825,8 +824,7 @@ impl VmxVcpu { vmx_entry_with!("vmlaunch") } - #[warn(unsafe_attr_outside_unsafe)] - #[naked] + #[unsafe(naked)] /// Enter guest with vmresume. /// /// See [`Self::vmx_launch`] for detail. @@ -834,8 +832,7 @@ impl VmxVcpu { vmx_entry_with!("vmresume") } - #[warn(unsafe_attr_outside_unsafe)] - #[naked] + #[unsafe(naked)] /// Return after vm-exit. /// /// The return value is a dummy value. From 79c46bbfc6d37db9e7bdcc96e99aa6e5856844d1 Mon Sep 17 00:00:00 2001 From: aarkegz Date: Mon, 21 Apr 2025 20:12:51 +0800 Subject: [PATCH 4/5] update ci.yml to match arceos and continue-on-error on nightly --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bac0c76..869c198 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - rust-toolchain: [nightly-2025-04-21, nightly] + rust-toolchain: [nightly-2024-12-25, nightly] targets: [x86_64-unknown-none] steps: - uses: actions/checkout@v4 @@ -20,12 +20,16 @@ jobs: - name: Check rust version run: rustc --version --verbose - name: Check code format + continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }} run: cargo fmt --all -- --check - name: Clippy + continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }} run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - name: Build + continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }} run: cargo build --target ${{ matrix.targets }} --all-features - name: Unit test + continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }} if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} run: cargo test --target ${{ matrix.targets }} -- --nocapture @@ -41,6 +45,8 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly-2024-12-25 - name: Build docs continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} run: | From d20186c4c1a33971c6c0953931ba28208a585d6b Mon Sep 17 00:00:00 2001 From: aarkegz Date: Mon, 21 Apr 2025 20:16:01 +0800 Subject: [PATCH 5/5] rollback `#[unsafe(naked)]` to `#[naked]` --- src/vmx/vcpu.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vmx/vcpu.rs b/src/vmx/vcpu.rs index 6a2f2f8..da6e696 100644 --- a/src/vmx/vcpu.rs +++ b/src/vmx/vcpu.rs @@ -812,10 +812,10 @@ macro_rules! vmx_entry_with { } impl VmxVcpu { - #[unsafe(naked)] + #[naked] /// Enter guest with vmlaunch. /// - /// `#[unsafe(naked)]` is essential here, without it the rust compiler will think `&mut self` is not used and won't give us correct %rdi. + /// `#[naked]` is essential here, without it the rust compiler will think `&mut self` is not used and won't give us correct %rdi. /// /// This function itself never returns, but [`Self::vmx_exit`] will do the return for this. /// @@ -824,7 +824,7 @@ impl VmxVcpu { vmx_entry_with!("vmlaunch") } - #[unsafe(naked)] + #[naked] /// Enter guest with vmresume. /// /// See [`Self::vmx_launch`] for detail. @@ -832,7 +832,7 @@ impl VmxVcpu { vmx_entry_with!("vmresume") } - #[unsafe(naked)] + #[naked] /// Return after vm-exit. /// /// The return value is a dummy value.