Skip to content

Fix: xstate load and restore now checks cpuid to avoid #GP or #UD on older CPUs #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 21, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 80 additions & 12 deletions src/vmx/vcpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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);
}
}
}
}
}

Expand Down Expand Up @@ -1008,17 +1082,11 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
}

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();
}
}

Expand Down
Loading