diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20459a7..869c198 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: | 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(); } }