@@ -40,9 +40,9 @@ use crate::hypervisor::gdb::{
4040} ;
4141#[ cfg( gdb) ]
4242use crate :: hypervisor:: gdb:: { DebugError , DebugMemoryAccessError } ;
43- use crate :: hypervisor :: regs :: {
44- CommonDebugRegs , CommonFpu , CommonRegisters , CommonSpecialRegisters ,
45- } ;
43+ # [ cfg ( not ( target_os = "windows" ) ) ]
44+ use crate :: hypervisor :: regs :: CommonDebugRegs ;
45+ use crate :: hypervisor :: regs :: { CommonFpu , CommonRegisters , CommonSpecialRegisters } ;
4646#[ cfg( not( gdb) ) ]
4747use crate :: hypervisor:: virtual_machine:: VirtualMachine ;
4848#[ cfg( kvm) ]
@@ -329,27 +329,39 @@ impl HyperlightVm {
329329 }
330330
331331 /// Resets the following vCPU state:
332- /// - General purpose registers
333- /// - Debug registers
334- /// - XSAVE (includes FPU/SSE state with proper FCW and MXCSR defaults)
335- /// - Special registers (restored from snapshot, with CR3 updated to new page table location)
332+ /// - On Windows: calls WHvResetPartition (resets all per-VP state including
333+ /// GP registers, debug registers, XSAVE, MSRs, APIC, etc.)
334+ /// - On Linux: explicitly resets GP registers, debug registers, and XSAVE
335+ ///
336+ /// This does NOT restore special registers — call `restore_sregs` separately
337+ /// after memory mappings are established.
336338 // TODO: check if other state needs to be reset
337- pub ( crate ) fn reset_vcpu (
339+ pub ( crate ) fn reset_vm_state ( & mut self ) -> std:: result:: Result < ( ) , RegisterError > {
340+ #[ cfg( target_os = "windows" ) ]
341+ self . vm . reset_partition ( ) ?;
342+
343+ #[ cfg( not( target_os = "windows" ) ) ]
344+ {
345+ self . vm . set_regs ( & CommonRegisters {
346+ rflags : 1 << 1 , // Reserved bit always set
347+ ..Default :: default ( )
348+ } ) ?;
349+ self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
350+ self . vm . reset_xsave ( ) ?;
351+ }
352+
353+ Ok ( ( ) )
354+ }
355+
356+ /// Restores special registers from snapshot with CR3 updated to the
357+ /// new page table location.
358+ pub ( crate ) fn restore_sregs (
338359 & mut self ,
339360 cr3 : u64 ,
340361 sregs : & CommonSpecialRegisters ,
341362 ) -> std:: result:: Result < ( ) , RegisterError > {
342- self . vm . set_regs ( & CommonRegisters {
343- rflags : 1 << 1 , // Reserved bit always set
344- ..Default :: default ( )
345- } ) ?;
346- self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
347- self . vm . reset_xsave ( ) ?;
348-
349363 #[ cfg( not( feature = "nanvix-unstable" ) ) ]
350364 {
351- // Restore the full special registers from snapshot, but update CR3
352- // to point to the new (relocated) page tables
353365 let mut sregs = * sregs;
354366 sregs. cr3 = cr3;
355367 self . pending_tlb_flush = true ;
@@ -879,7 +891,9 @@ mod tests {
879891 use super :: * ;
880892 #[ cfg( kvm) ]
881893 use crate :: hypervisor:: regs:: FP_CONTROL_WORD_DEFAULT ;
882- use crate :: hypervisor:: regs:: { CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT } ;
894+ use crate :: hypervisor:: regs:: {
895+ CommonDebugRegs , CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT ,
896+ } ;
883897 use crate :: hypervisor:: virtual_machine:: VirtualMachine ;
884898 use crate :: mem:: layout:: SandboxMemoryLayout ;
885899 use crate :: mem:: memory_region:: { GuestMemoryRegion , MemoryRegionFlags } ;
@@ -906,7 +920,7 @@ mod tests {
906920 // Dirty State Builders - Create non-default vCPU state for testing reset
907921 // ==========================================================================
908922
909- /// Build dirty general purpose registers for testing reset_vcpu .
923+ /// Build dirty general purpose registers for testing reset .
910924 fn dirty_regs ( ) -> CommonRegisters {
911925 CommonRegisters {
912926 rax : 0x1111111111111111 ,
@@ -930,7 +944,7 @@ mod tests {
930944 }
931945 }
932946
933- /// Build dirty FPU state for testing reset_vcpu .
947+ /// Build dirty FPU state for testing reset .
934948 fn dirty_fpu ( ) -> CommonFpu {
935949 CommonFpu {
936950 fpr : [ [ 0xAB ; 16 ] ; 8 ] ,
@@ -945,7 +959,7 @@ mod tests {
945959 }
946960 }
947961
948- /// Build dirty special registers for testing reset_vcpu .
962+ /// Build dirty special registers for testing reset .
949963 /// Must be consistent for 64-bit long mode (CR0/CR4/EFER).
950964 fn dirty_sregs ( _pml4_addr : u64 ) -> CommonSpecialRegisters {
951965 let segment = CommonSegmentRegister {
@@ -1014,7 +1028,7 @@ mod tests {
10141028 }
10151029 }
10161030
1017- /// Build dirty debug registers for testing reset_vcpu .
1031+ /// Build dirty debug registers for testing reset .
10181032 ///
10191033 /// DR6 bit layout (Intel SDM / AMD APM):
10201034 /// Bits 0-3 (B0-B3): Breakpoint condition detected - software writable/clearable
@@ -1058,8 +1072,8 @@ mod tests {
10581072 }
10591073 }
10601074
1061- /// Returns default test values for reset_vcpu parameters.
1062- /// Uses standard 64-bit defaults since reset_vcpu now restores full sregs from snapshot.
1075+ /// Returns default test values for restore_sregs parameters.
1076+ /// Uses standard 64-bit defaults since restore_sregs restores full sregs from snapshot.
10631077 fn default_sregs ( ) -> CommonSpecialRegisters {
10641078 CommonSpecialRegisters :: standard_64bit_defaults ( 0 )
10651079 }
@@ -1183,9 +1197,18 @@ mod tests {
11831197 // Assertion Helpers - Verify vCPU state after reset
11841198 // ==========================================================================
11851199
1186- /// Assert that debug registers are in reset state.
1187- /// Reserved bits in DR6/DR7 are read-only (set by CPU), so we only check
1188- /// that writable bits are cleared to 0 and DR0-DR3 are zeroed.
1200+ /// Assert that debug registers are in architectural reset state.
1201+ ///
1202+ /// On Linux (KVM/MSHV): reset_vm_state explicitly zeroes debug registers.
1203+ ///
1204+ /// On Windows: WHvResetPartition resets to power-on defaults per
1205+ /// Intel SDM Vol. 3, Table 10-1:
1206+ /// DR0-DR3 = 0 (breakpoint addresses cleared)
1207+ /// DR6 = 0xFFFF0FF0 (reserved bits set, writable bits cleared)
1208+ /// DR7 = 0x00000400 (reserved bit 10 set, all enables cleared)
1209+ ///
1210+ /// Reserved bits in DR6/DR7 are read-only and CPU-dependent, so we only
1211+ /// verify that writable bits are cleared to 0 and DR0-DR3 are zeroed.
11891212 fn assert_debug_regs_reset ( vm : & dyn VirtualMachine ) {
11901213 let debug_regs = vm. debug_regs ( ) . unwrap ( ) ;
11911214 let expected = CommonDebugRegs {
@@ -1200,19 +1223,58 @@ mod tests {
12001223 }
12011224
12021225 /// Assert that general-purpose registers are in reset state.
1203- /// After reset, all registers should be zeroed except rflags which has
1204- /// reserved bit 1 always set.
1226+ ///
1227+ /// On Linux (KVM/MSHV): reset_vm_state explicitly zeroes all GP regs and sets
1228+ /// rflags = 0x2, so we verify all-zeros.
1229+ ///
1230+ /// On Windows: WHvResetPartition sets architectural power-on defaults
1231+ /// per Intel SDM Vol. 3, Table 10-1:
1232+ /// RIP = 0xFFF0 (reset vector)
1233+ /// RDX = CPUID signature (CPU-dependent stepping/model/family)
1234+ /// RFLAGS = 0x2 (only reserved bit 1 set)
1235+ /// All other GP regs = 0
1236+ /// These are overwritten by dispatch_call_from_host before guest execution,
1237+ /// but we still verify the power-on state is correct.
12051238 fn assert_regs_reset ( vm : & dyn VirtualMachine ) {
1239+ let regs = vm. regs ( ) . unwrap ( ) ;
1240+ #[ cfg( not( target_os = "windows" ) ) ]
12061241 assert_eq ! (
1207- vm . regs( ) . unwrap ( ) ,
1242+ regs,
12081243 CommonRegisters {
1209- rflags: 1 << 1 , // Reserved bit 1 is always set
1244+ rflags: 1 << 1 ,
12101245 ..Default :: default ( )
12111246 }
12121247 ) ;
1248+ #[ cfg( target_os = "windows" ) ]
1249+ {
1250+ // WHvResetPartition sets x86 power-on reset values
1251+ // (Intel SDM Vol. 3, Table 10-1)
1252+ let expected = CommonRegisters {
1253+ rip : 0xFFF0 , // Reset vector
1254+ rdx : regs. rdx , // CPUID signature (CPU-dependent)
1255+ rflags : 0x2 , // Reserved bit 1
1256+ ..Default :: default ( )
1257+ } ;
1258+ assert_ne ! (
1259+ regs. rdx, 0x4444444444444444 ,
1260+ "RDX should not retain dirty value"
1261+ ) ;
1262+ assert_eq ! ( regs, expected) ;
1263+ }
12131264 }
12141265
12151266 /// Assert that FPU state is in reset state.
1267+ ///
1268+ /// On Linux (KVM/MSHV): reset_vm_state calls reset_xsave which zeroes FPU state
1269+ /// and sets FCW/MXCSR to defaults.
1270+ ///
1271+ /// On Windows: WHvResetPartition resets to power-on defaults per
1272+ /// Intel SDM Vol. 3, Table 10-1 (FINIT-equivalent state):
1273+ /// FCW = 0x037F (all exceptions masked, precision=64-bit, round=nearest)
1274+ /// FSW = 0, FTW = 0 (all empty), FOP = 0, FIP = 0, FDP = 0
1275+ /// MXCSR = 0x1F80 (all SIMD exceptions masked, round=nearest)
1276+ /// ST0-ST7 = 0, XMM0-XMM15 = 0
1277+ ///
12161278 /// Handles hypervisor-specific quirks (KVM MXCSR, empty FPU registers).
12171279 fn assert_fpu_reset ( vm : & dyn VirtualMachine ) {
12181280 let fpu = vm. fpu ( ) . unwrap ( ) ;
@@ -1222,8 +1284,14 @@ mod tests {
12221284 assert_eq ! ( fpu, expected_fpu) ;
12231285 }
12241286
1225- /// Assert that special registers are in reset state.
1226- /// Handles hypervisor-specific differences in hidden descriptor cache fields.
1287+ /// Assert that special registers match the expected snapshot state.
1288+ ///
1289+ /// After reset, sregs are explicitly restored from the snapshot
1290+ /// (with CR3 updated). This verifies they match the expected 64-bit
1291+ /// long mode configuration from CommonSpecialRegisters::standard_64bit_defaults.
1292+ ///
1293+ /// Handles hypervisor-specific differences in hidden descriptor cache fields
1294+ /// (unusable, granularity, type_ for unused segments).
12271295 fn assert_sregs_reset ( vm : & dyn VirtualMachine , pml4_addr : u64 ) {
12281296 let defaults = CommonSpecialRegisters :: standard_64bit_defaults ( pml4_addr) ;
12291297 let sregs = vm. sregs ( ) . unwrap ( ) ;
@@ -1333,7 +1401,7 @@ mod tests {
13331401 dirtied_mask
13341402 }
13351403
1336- /// Dirty the legacy XSAVE region (bytes 0-511) for testing reset_vcpu .
1404+ /// Dirty the legacy XSAVE region (bytes 0-511) for testing reset .
13371405 /// This includes FPU/x87 state, SSE state, and reserved areas.
13381406 ///
13391407 /// Layout (from Intel SDM Table 13-1):
@@ -1621,7 +1689,8 @@ mod tests {
16211689 assert_eq ! ( got_sregs, expected_sregs) ;
16221690
16231691 // Reset the vCPU
1624- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1692+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1693+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
16251694
16261695 // Verify registers are reset to defaults
16271696 assert_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1686,7 +1755,7 @@ mod tests {
16861755 "xsave should be zeroed except for hypervisor-specific fields"
16871756 ) ;
16881757
1689- // Verify sregs are reset to defaults (CR3 is 0 as passed to reset_vcpu)
1758+ // Verify sregs are reset to defaults
16901759 assert_sregs_reset ( hyperlight_vm. vm . as_ref ( ) , 0 ) ;
16911760 }
16921761
@@ -1750,7 +1819,8 @@ mod tests {
17501819 assert_eq ! ( regs, expected_dirty) ;
17511820
17521821 // Reset vcpu
1753- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1822+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1823+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
17541824
17551825 // Check registers are reset to defaults
17561826 assert_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1874,7 +1944,8 @@ mod tests {
18741944 }
18751945
18761946 // Reset vcpu
1877- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1947+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1948+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
18781949
18791950 // Check FPU is reset to defaults
18801951 assert_fpu_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1925,7 +1996,8 @@ mod tests {
19251996 assert_eq ! ( debug_regs, expected_dirty) ;
19261997
19271998 // Reset vcpu
1928- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1999+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
2000+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
19292001
19302002 // Check debug registers are reset to default values
19312003 assert_debug_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1974,9 +2046,10 @@ mod tests {
19742046 assert_eq ! ( sregs, expected_dirty) ;
19752047
19762048 // Reset vcpu
1977- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
2049+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
2050+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
19782051
1979- // Check registers are reset to defaults (CR3 is 0 as passed to reset_vcpu)
2052+ // Check registers are reset to defaults
19802053 let sregs = hyperlight_vm. vm . sregs ( ) . unwrap ( ) ;
19812054 let mut expected_reset = CommonSpecialRegisters :: standard_64bit_defaults ( 0 ) ;
19822055 normalize_sregs_for_run_tests ( & mut expected_reset, & sregs) ;
@@ -2012,7 +2085,11 @@ mod tests {
20122085 let root_pt_addr = ctx. ctx . vm . get_root_pt ( ) . unwrap ( ) ;
20132086 let segment_state = ctx. ctx . vm . get_snapshot_sregs ( ) . unwrap ( ) ;
20142087
2015- ctx. ctx . vm . reset_vcpu ( root_pt_addr, & segment_state) . unwrap ( ) ;
2088+ ctx. ctx . vm . reset_vm_state ( ) . unwrap ( ) ;
2089+ ctx. ctx
2090+ . vm
2091+ . restore_sregs ( root_pt_addr, & segment_state)
2092+ . unwrap ( ) ;
20162093
20172094 // Re-run from entrypoint (flag=1 means guest skips dirty phase, just does FXSAVE)
20182095 // Use stack_top - 8 to match initialise()'s behavior (simulates call pushing return addr)
0 commit comments