Skip to content

Commit ea63830

Browse files
committed
Reset vcpu state
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 48dc640 commit ea63830

File tree

10 files changed

+1282
-143
lines changed

10 files changed

+1282
-143
lines changed

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 960 additions & 1 deletion
Large diffs are not rendered by default.

src/hyperlight_host/src/hypervisor/regs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
mod debug_regs;
1718
mod fpu;
1819
mod special_regs;
1920
mod standard_regs;
2021

2122
#[cfg(target_os = "windows")]
2223
use std::collections::HashSet;
2324

25+
pub(crate) use debug_regs::*;
2426
pub(crate) use fpu::*;
2527
pub(crate) use special_regs::*;
2628
pub(crate) use standard_regs::*;

src/hyperlight_host/src/hypervisor/regs/fpu.rs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,11 @@ pub(crate) struct CommonFpu {
3636
pub fcw: u16,
3737
pub fsw: u16,
3838
pub ftwx: u8,
39-
pub pad1: u8,
4039
pub last_opcode: u16,
4140
pub last_ip: u64,
4241
pub last_dp: u64,
4342
pub xmm: [[u8; 16]; 16],
4443
pub mxcsr: u32,
45-
pub pad2: u32,
4644
}
4745

4846
impl Default for CommonFpu {
@@ -52,13 +50,11 @@ impl Default for CommonFpu {
5250
fcw: FP_CONTROL_WORD_DEFAULT,
5351
fsw: 0,
5452
ftwx: 0,
55-
pad1: 0,
5653
last_opcode: 0,
5754
last_ip: 0,
5855
last_dp: 0,
5956
xmm: [[0u8; 16]; 16],
6057
mxcsr: MXCSR_DEFAULT,
61-
pad2: 0,
6258
}
6359
}
6460
}
@@ -71,13 +67,13 @@ impl From<&CommonFpu> for kvm_fpu {
7167
fcw: common_fpu.fcw,
7268
fsw: common_fpu.fsw,
7369
ftwx: common_fpu.ftwx,
74-
pad1: common_fpu.pad1,
70+
pad1: 0,
7571
last_opcode: common_fpu.last_opcode,
7672
last_ip: common_fpu.last_ip,
7773
last_dp: common_fpu.last_dp,
7874
xmm: common_fpu.xmm,
7975
mxcsr: common_fpu.mxcsr,
80-
pad2: common_fpu.pad2,
76+
pad2: 0,
8177
}
8278
}
8379
}
@@ -90,13 +86,13 @@ impl From<&CommonFpu> for FloatingPointUnit {
9086
fcw: common_fpu.fcw,
9187
fsw: common_fpu.fsw,
9288
ftwx: common_fpu.ftwx,
93-
pad1: common_fpu.pad1,
89+
pad1: 0,
9490
last_opcode: common_fpu.last_opcode,
9591
last_ip: common_fpu.last_ip,
9692
last_dp: common_fpu.last_dp,
9793
xmm: common_fpu.xmm,
9894
mxcsr: common_fpu.mxcsr,
99-
pad2: common_fpu.pad2,
95+
pad2: 0,
10096
}
10197
}
10298
}
@@ -109,13 +105,11 @@ impl From<&kvm_fpu> for CommonFpu {
109105
fcw: kvm_fpu.fcw,
110106
fsw: kvm_fpu.fsw,
111107
ftwx: kvm_fpu.ftwx,
112-
pad1: kvm_fpu.pad1,
113108
last_opcode: kvm_fpu.last_opcode,
114109
last_ip: kvm_fpu.last_ip,
115110
last_dp: kvm_fpu.last_dp,
116111
xmm: kvm_fpu.xmm,
117112
mxcsr: kvm_fpu.mxcsr,
118-
pad2: kvm_fpu.pad2,
119113
}
120114
}
121115
}
@@ -128,13 +122,11 @@ impl From<&FloatingPointUnit> for CommonFpu {
128122
fcw: mshv_fpu.fcw,
129123
fsw: mshv_fpu.fsw,
130124
ftwx: mshv_fpu.ftwx,
131-
pad1: mshv_fpu.pad1,
132125
last_opcode: mshv_fpu.last_opcode,
133126
last_ip: mshv_fpu.last_ip,
134127
last_dp: mshv_fpu.last_dp,
135128
xmm: mshv_fpu.xmm,
136129
mxcsr: mshv_fpu.mxcsr,
137-
pad2: mshv_fpu.pad2,
138130
}
139131
}
140132
}
@@ -174,7 +166,7 @@ impl From<&CommonFpu> for [(WHV_REGISTER_NAME, Align16<WHV_REGISTER_VALUE>); WHP
174166
FpControl: fpu.fcw,
175167
FpStatus: fpu.fsw,
176168
FpTag: fpu.ftwx,
177-
Reserved: fpu.pad1,
169+
Reserved: 0,
178170
LastFpOp: fpu.last_opcode,
179171
Anonymous: WHV_X64_FP_CONTROL_STATUS_REGISTER_0_0 {
180172
LastFpRip: fpu.last_ip,
@@ -293,7 +285,6 @@ impl TryFrom<&[(WHV_REGISTER_NAME, Align16<WHV_REGISTER_VALUE>)]> for CommonFpu
293285
fpu.fcw = control.FpControl;
294286
fpu.fsw = control.FpStatus;
295287
fpu.ftwx = control.FpTag;
296-
fpu.pad1 = control.Reserved;
297288
fpu.last_opcode = control.LastFpOp;
298289
fpu.last_ip = unsafe { control.Anonymous.LastFpRip };
299290
}
@@ -355,7 +346,6 @@ mod tests {
355346
fcw: 0x1234,
356347
fsw: 0x5678,
357348
ftwx: 0x9a,
358-
pad1: 0xbc,
359349
last_opcode: 0xdef0,
360350
last_ip: 0xdeadbeefcafebabe,
361351
last_dp: 0xabad1deaf00dbabe,
@@ -365,7 +355,6 @@ mod tests {
365355
[22u8; 16], [23u8; 16],
366356
],
367357
mxcsr: 0x1f80,
368-
pad2: 0,
369358
}
370359
}
371360

src/hyperlight_host/src/hypervisor/virtual_machine/kvm.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,19 @@ use std::sync::LazyLock;
1818

1919
#[cfg(gdb)]
2020
use kvm_bindings::kvm_guest_debug;
21-
use kvm_bindings::{kvm_fpu, kvm_regs, kvm_sregs, kvm_userspace_memory_region};
21+
use kvm_bindings::{
22+
kvm_debugregs, kvm_fpu, kvm_regs, kvm_sregs, kvm_userspace_memory_region, kvm_xsave,
23+
};
2224
use kvm_ioctls::Cap::UserMemory;
2325
use kvm_ioctls::{Kvm, VcpuExit, VcpuFd, VmFd};
2426
use tracing::{Span, instrument};
2527

2628
#[cfg(gdb)]
2729
use crate::hypervisor::gdb::DebuggableVm;
28-
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
30+
use crate::hypervisor::regs::{
31+
CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters, FP_CONTROL_WORD_DEFAULT,
32+
MXCSR_DEFAULT,
33+
};
2934
use crate::hypervisor::virtual_machine::{VirtualMachine, VmExit};
3035
use crate::mem::memory_region::MemoryRegion;
3136
use crate::{Result, new_error};
@@ -140,12 +145,16 @@ impl VirtualMachine for KvmVm {
140145
}
141146

142147
fn fpu(&self) -> Result<CommonFpu> {
148+
// Note: On KVM this ignores MXCSR.
149+
// See https://github.com/torvalds/linux/blob/d358e5254674b70f34c847715ca509e46eb81e6f/arch/x86/kvm/x86.c#L12554-L12599
143150
let kvm_fpu = self.vcpu_fd.get_fpu()?;
144151
Ok((&kvm_fpu).into())
145152
}
146153

147154
fn set_fpu(&self, fpu: &CommonFpu) -> Result<()> {
148155
let kvm_fpu: kvm_fpu = fpu.into();
156+
// Note: On KVM this ignores MXCSR.
157+
// See https://github.com/torvalds/linux/blob/d358e5254674b70f34c847715ca509e46eb81e6f/arch/x86/kvm/x86.c#L12554-L12599
149158
self.vcpu_fd.set_fpu(&kvm_fpu)?;
150159
Ok(())
151160
}
@@ -161,7 +170,17 @@ impl VirtualMachine for KvmVm {
161170
Ok(())
162171
}
163172

164-
#[cfg(crashdump)]
173+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
174+
let kvm_debug_regs = self.vcpu_fd.get_debug_regs()?;
175+
Ok(kvm_debug_regs.into())
176+
}
177+
178+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
179+
let kvm_debug_regs: kvm_debugregs = drs.into();
180+
self.vcpu_fd.set_debug_regs(&kvm_debug_regs)?;
181+
Ok(())
182+
}
183+
165184
fn xsave(&self) -> Result<Vec<u8>> {
166185
let xsave = self.vcpu_fd.get_xsave()?;
167186
Ok(xsave
@@ -170,6 +189,35 @@ impl VirtualMachine for KvmVm {
170189
.flat_map(u32::to_le_bytes)
171190
.collect())
172191
}
192+
193+
fn reset_xsave(&self) -> Result<()> {
194+
let mut xsave = kvm_xsave::default(); // default is zeroed 4KB buffer with no FAM
195+
196+
// XSAVE area layout from Intel SDM Vol. 1 Section 13.4.1:
197+
// - Bytes 0-1: FCW (x87 FPU Control Word)
198+
// - Bytes 24-27: MXCSR
199+
xsave.region[0] = FP_CONTROL_WORD_DEFAULT as u32;
200+
xsave.region[6] = MXCSR_DEFAULT;
201+
202+
// SAFETY: No dynamic features enabled, 4KB is sufficient
203+
unsafe { self.vcpu_fd.set_xsave(&xsave)? };
204+
205+
Ok(())
206+
}
207+
208+
#[cfg(test)]
209+
#[cfg(feature = "init-paging")]
210+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
211+
let xsave = kvm_xsave {
212+
region: *xsave,
213+
..Default::default() // zeroed 4KB buffer with no FAM
214+
};
215+
// Safety: Safe because we only copy 4096 bytes
216+
// and have not enabled any dynamic xsave features
217+
unsafe { self.vcpu_fd.set_xsave(&xsave)? };
218+
219+
Ok(())
220+
}
173221
}
174222

175223
#[cfg(gdb)]

src/hyperlight_host/src/hypervisor/virtual_machine/mod.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ use std::sync::OnceLock;
2020
use tracing::{Span, instrument};
2121

2222
use crate::Result;
23-
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
23+
use crate::hypervisor::regs::{
24+
CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters,
25+
};
2426
use crate::mem::memory_region::MemoryRegion;
2527

2628
/// KVM (Kernel-based Virtual Machine) functionality (linux)
@@ -83,7 +85,7 @@ pub fn is_hypervisor_present() -> bool {
8385
}
8486

8587
/// The hypervisor types available for the current platform
86-
#[derive(PartialEq, Eq, Debug)]
88+
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
8789
pub(crate) enum HypervisorType {
8890
#[cfg(kvm)]
8991
Kvm,
@@ -165,9 +167,21 @@ pub(crate) trait VirtualMachine: Debug + Send {
165167
/// Set special regs
166168
fn set_sregs(&self, sregs: &CommonSpecialRegisters) -> Result<()>;
167169

168-
/// xsave
169-
#[cfg(crashdump)]
170+
/// Get xsave
171+
#[allow(dead_code)]
170172
fn xsave(&self) -> Result<Vec<u8>>;
173+
/// Reset xsave to default state
174+
fn reset_xsave(&self) -> Result<()>;
175+
/// Set xsave - only used for tests
176+
#[cfg(test)]
177+
#[cfg(feature = "init-paging")]
178+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()>;
179+
180+
/// Get the debug registers of the vCPU
181+
#[allow(dead_code)]
182+
fn debug_regs(&self) -> Result<CommonDebugRegs>;
183+
/// Set the debug registers of the vCPU
184+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()>;
171185

172186
/// Get partition handle
173187
#[cfg(target_os = "windows")]

src/hyperlight_host/src/hypervisor/virtual_machine/mshv.rs

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::sync::LazyLock;
2121
#[cfg(gdb)]
2222
use mshv_bindings::{DebugRegisters, hv_message_type_HVMSG_X64_EXCEPTION_INTERCEPT};
2323
use mshv_bindings::{
24-
FloatingPointUnit, SpecialRegisters, StandardRegisters, hv_message_type,
24+
FloatingPointUnit, SpecialRegisters, StandardRegisters, XSave, hv_message_type,
2525
hv_message_type_HVMSG_GPA_INTERCEPT, hv_message_type_HVMSG_UNMAPPED_GPA,
2626
hv_message_type_HVMSG_X64_HALT, hv_message_type_HVMSG_X64_IO_PORT_INTERCEPT,
2727
hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES,
@@ -33,7 +33,10 @@ use tracing::{Span, instrument};
3333

3434
#[cfg(gdb)]
3535
use crate::hypervisor::gdb::DebuggableVm;
36-
use crate::hypervisor::regs::{CommonFpu, CommonRegisters, CommonSpecialRegisters};
36+
use crate::hypervisor::regs::{
37+
CommonDebugRegs, CommonFpu, CommonRegisters, CommonSpecialRegisters, FP_CONTROL_WORD_DEFAULT,
38+
MXCSR_DEFAULT,
39+
};
3740
use crate::hypervisor::virtual_machine::{VirtualMachine, VmExit};
3841
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
3942
use crate::{Result, new_error};
@@ -209,11 +212,60 @@ impl VirtualMachine for MshvVm {
209212
Ok(())
210213
}
211214

212-
#[cfg(crashdump)]
215+
fn debug_regs(&self) -> Result<CommonDebugRegs> {
216+
let debug_regs = self.vcpu_fd.get_debug_regs()?;
217+
Ok(debug_regs.into())
218+
}
219+
220+
fn set_debug_regs(&self, drs: &CommonDebugRegs) -> Result<()> {
221+
let mshv_debug_regs = drs.into();
222+
self.vcpu_fd.set_debug_regs(&mshv_debug_regs)?;
223+
Ok(())
224+
}
225+
213226
fn xsave(&self) -> Result<Vec<u8>> {
214227
let xsave = self.vcpu_fd.get_xsave()?;
215228
Ok(xsave.buffer.to_vec())
216229
}
230+
231+
fn reset_xsave(&self) -> Result<()> {
232+
let current_xsave = self.vcpu_fd.get_xsave()?;
233+
if current_xsave.buffer.len() < 576 {
234+
// Minimum: 512 legacy + 64 header
235+
return Err(new_error!(
236+
"Unexpected xsave length {}",
237+
current_xsave.buffer.len()
238+
));
239+
}
240+
241+
let mut buf = XSave::default(); // default is zeroed 4KB buffer
242+
243+
// Copy XCOMP_BV (offset 520-527) - preserves feature mask + compacted bit
244+
buf.buffer[520..528].copy_from_slice(&current_xsave.buffer[520..528]);
245+
246+
// XSAVE area layout from Intel SDM Vol. 1 Section 13.4.1:
247+
// - Bytes 0-1: FCW (x87 FPU Control Word)
248+
// - Bytes 24-27: MXCSR
249+
buf.buffer[0..2].copy_from_slice(&FP_CONTROL_WORD_DEFAULT.to_le_bytes());
250+
buf.buffer[24..28].copy_from_slice(&MXCSR_DEFAULT.to_le_bytes());
251+
252+
self.vcpu_fd.set_xsave(&buf)?;
253+
Ok(())
254+
}
255+
256+
#[cfg(test)]
257+
#[cfg(feature = "init-paging")]
258+
fn set_xsave(&self, xsave: &[u32; 1024]) -> Result<()> {
259+
let mut buf = XSave::default();
260+
// Safety: all valid u32 values are 4 valid u8 values
261+
let (prefix, bytes, suffix) = unsafe { xsave.align_to() };
262+
if !prefix.is_empty() || !suffix.is_empty() {
263+
return Err(new_error!("Invalid xsave buffer alignment"));
264+
}
265+
buf.buffer.copy_from_slice(bytes);
266+
self.vcpu_fd.set_xsave(&buf)?;
267+
Ok(())
268+
}
217269
}
218270

219271
#[cfg(gdb)]

0 commit comments

Comments
 (0)