Skip to content

Commit ab5d194

Browse files
authored
Fix: xstate load and restore now checks cpuid to avoid #GP or #UD on older CPUs (#15)
* fix: xstate load and restore now checks cpuid * try suppress `unsafe_attr_outside_unsafe` for `#[naked]` * update nightly version * update ci.yml to match arceos and continue-on-error on nightly * rollback `#[unsafe(naked)]` to `#[naked]`
1 parent 5f6eaf4 commit ab5d194

File tree

2 files changed

+86
-12
lines changed

2 files changed

+86
-12
lines changed

.github/workflows/ci.yml

+6
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ jobs:
2020
- name: Check rust version
2121
run: rustc --version --verbose
2222
- name: Check code format
23+
continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
2324
run: cargo fmt --all -- --check
2425
- name: Clippy
26+
continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
2527
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
2628
- name: Build
29+
continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
2730
run: cargo build --target ${{ matrix.targets }} --all-features
2831
- name: Unit test
32+
continue-on-error: ${{ matrix.rust-toolchain == 'nightly' }}
2933
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
3034
run: cargo test --target ${{ matrix.targets }} -- --nocapture
3135

@@ -41,6 +45,8 @@ jobs:
4145
steps:
4246
- uses: actions/checkout@v4
4347
- uses: dtolnay/rust-toolchain@nightly
48+
with:
49+
toolchain: nightly-2024-12-25
4450
- name: Build docs
4551
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
4652
run: |

src/vmx/vcpu.rs

+80-12
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ pub struct XState {
3030
guest_xcr0: u64,
3131
host_xss: u64,
3232
guest_xss: u64,
33+
34+
xsave_available: bool,
35+
xsaves_available: bool,
3336
}
3437

3538
#[derive(PartialEq, Eq, Debug)]
@@ -43,20 +46,91 @@ pub enum VmCpuMode {
4346
impl XState {
4447
/// Create a new [`XState`] instance with current host state
4548
fn new() -> Self {
46-
let xcr0 = unsafe { xcr0_read().bits() };
47-
let xss = Msr::IA32_XSS.read();
49+
// Check if XSAVE is available
50+
let xsave_available = Self::xsave_available();
51+
// Check if XSAVES and XRSTORS (as well as IA32_XSS) are available
52+
let xsaves_available = if xsave_available {
53+
Self::xsaves_available()
54+
} else {
55+
false
56+
};
57+
58+
// Read XCR0 iff XSAVE is available
59+
let xcr0 = if xsave_available {
60+
unsafe { xcr0_read().bits() }
61+
} else {
62+
0
63+
};
64+
// Read IA32_XSS iff XSAVES is available
65+
let xss = if xsaves_available {
66+
Msr::IA32_XSS.read()
67+
} else {
68+
0
69+
};
4870

4971
Self {
5072
host_xcr0: xcr0,
5173
guest_xcr0: xcr0,
5274
host_xss: xss,
5375
guest_xss: xss,
76+
xsave_available,
77+
xsaves_available,
5478
}
5579
}
5680

57-
/// Enables extended processor state management instructions, including XGETBV and XSAVE.
81+
/// Enable extended processor state management instructions, including XGETBV and XSAVE.
5882
pub fn enable_xsave() {
59-
unsafe { Cr4::write(Cr4::read() | Cr4Flags::OSXSAVE) };
83+
if Self::xsave_available() {
84+
unsafe { Cr4::write(Cr4::read() | Cr4Flags::OSXSAVE) };
85+
}
86+
}
87+
88+
/// Check if XSAVE is available on the current CPU.
89+
pub fn xsave_available() -> bool {
90+
let cpuid = CpuId::new();
91+
cpuid
92+
.get_feature_info()
93+
.map(|f| f.has_xsave())
94+
.unwrap_or(false)
95+
}
96+
97+
/// Check if XSAVES and XRSTORS (as well as IA32_XSS) are available on the current CPU.
98+
pub fn xsaves_available() -> bool {
99+
let cpuid = CpuId::new();
100+
cpuid
101+
.get_extended_state_info()
102+
.map(|f| f.has_xsaves_xrstors())
103+
.unwrap_or(false)
104+
}
105+
106+
/// Save the current host XCR0 and IA32_XSS values and load the guest values.
107+
pub fn switch_to_guest(&mut self) {
108+
unsafe {
109+
if self.xsave_available {
110+
self.host_xcr0 = xcr0_read().bits();
111+
xcr0_write(Xcr0::from_bits_unchecked(self.guest_xcr0));
112+
113+
if self.xsaves_available {
114+
self.host_xss = Msr::IA32_XSS.read();
115+
Msr::IA32_XSS.write(self.guest_xss);
116+
}
117+
}
118+
}
119+
}
120+
121+
/// Save the current guest XCR0 and IA32_XSS values and load the host values.
122+
pub fn switch_to_host(&mut self) {
123+
unsafe {
124+
if self.xsave_available {
125+
self.guest_xcr0 = xcr0_read().bits();
126+
xcr0_write(Xcr0::from_bits_unchecked(self.host_xcr0));
127+
128+
if self.xsaves_available {
129+
self.guest_xss = Msr::IA32_XSS.read();
130+
Msr::IA32_XSS.write(self.host_xss);
131+
}
132+
}
133+
}
60134
}
61135
}
62136

@@ -1008,17 +1082,11 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
10081082
}
10091083

10101084
fn load_guest_xstate(&mut self) {
1011-
unsafe {
1012-
xcr0_write(Xcr0::from_bits_unchecked(self.xstate.guest_xcr0));
1013-
Msr::IA32_XSS.write(self.xstate.guest_xss);
1014-
}
1085+
self.xstate.switch_to_guest();
10151086
}
10161087

10171088
fn load_host_xstate(&mut self) {
1018-
unsafe {
1019-
xcr0_write(Xcr0::from_bits_unchecked(self.xstate.host_xcr0));
1020-
Msr::IA32_XSS.write(self.xstate.host_xss);
1021-
}
1089+
self.xstate.switch_to_host();
10221090
}
10231091
}
10241092

0 commit comments

Comments
 (0)