Skip to content

Commit 26685f1

Browse files
committed
fix: initialize all segment registers and XSAVE state for MSHV/WHP compatibility
MSHV and WHP reject vCPU state with zeroed segment registers (ES, SS, FS, GS, LDT) and uninitialized XSAVE areas. Properly initialize all segment registers in standard_real_mode_defaults() and add reset_xsave() call after set_sregs() to ensure FPU state (FCW, MXCSR) is valid. Signed-off-by: danbugs <danilochiarlone@gmail.com>
1 parent 976483f commit 26685f1

2 files changed

Lines changed: 35 additions & 8 deletions

File tree

src/hyperlight_host/src/hypervisor/hyperlight_vm.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,12 @@ impl HyperlightVm {
439439
vm.set_sregs(&CommonSpecialRegisters::standard_real_mode_defaults())
440440
.map_err(VmError::Register)?;
441441

442+
// Initialize XSAVE state with proper FPU defaults (FCW, MXCSR).
443+
// MSHV (unlike KVM) does not provide sane defaults for the XSAVE
444+
// area on a freshly created vCPU, and will reject the VP state
445+
// on the first run if it is left uninitialized.
446+
vm.reset_xsave().map_err(VmError::Register)?;
447+
442448
#[cfg(any(kvm, mshv3))]
443449
let interrupt_handle: Arc<dyn InterruptHandleImpl> = Arc::new(LinuxInterruptHandle {
444450
state: AtomicU8::new(0),

src/hyperlight_host/src/hypervisor/regs/special_regs.rs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,34 +106,55 @@ impl CommonSpecialRegisters {
106106

107107
#[cfg(not(feature = "init-paging"))]
108108
pub(crate) fn standard_real_mode_defaults() -> Self {
109+
// In real mode, all data/code segment registers must have valid
110+
// limit, present, type_, and s fields. MSHV (unlike KVM) rejects
111+
// the vCPU state if any segment has limit=0 / present=0.
112+
let data_seg = CommonSegmentRegister {
113+
base: 0,
114+
selector: 0,
115+
limit: 0xFFFF,
116+
type_: 3, // data, read/write, accessed
117+
present: 1,
118+
s: 1, // non-system
119+
..Default::default()
120+
};
121+
109122
CommonSpecialRegisters {
110123
cs: CommonSegmentRegister {
111124
base: 0,
112125
selector: 0,
113126
limit: 0xFFFF,
114-
type_: 11,
127+
type_: 11, // code, readable, accessed
115128
present: 1,
116-
s: 1,
129+
s: 1, // non-system
117130
..Default::default()
118131
},
119-
ds: CommonSegmentRegister {
132+
ds: data_seg,
133+
es: data_seg,
134+
ss: data_seg,
135+
fs: data_seg,
136+
gs: data_seg,
137+
tr: CommonSegmentRegister {
120138
base: 0,
121139
selector: 0,
122140
limit: 0xFFFF,
123-
type_: 3,
141+
type_: 11, // 32-bit busy TSS
124142
present: 1,
125-
s: 1,
143+
s: 0, // system segment
126144
..Default::default()
127145
},
128-
tr: CommonSegmentRegister {
146+
ldt: CommonSegmentRegister {
129147
base: 0,
130148
selector: 0,
131149
limit: 0xFFFF,
132-
type_: 11,
150+
type_: 2, // LDT descriptor
133151
present: 1,
134-
s: 0,
152+
s: 0, // system segment
135153
..Default::default()
136154
},
155+
// CR0.ET (bit 4) is hardwired to 1 on modern x86 CPUs.
156+
// MSHV rejects the vCPU state if ET is not set.
157+
cr0: 1 << 4,
137158
..Default::default()
138159
}
139160
}

0 commit comments

Comments
 (0)