@@ -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) ]
@@ -335,23 +335,37 @@ impl HyperlightVm {
335335 }
336336
337337 /// Resets the following vCPU state:
338- /// - General purpose registers
339- /// - Debug registers
340- /// - XSAVE (includes FPU/SSE state with proper FCW and MXCSR defaults)
341- /// - Special registers (restored from snapshot, with CR3 updated to new page table location)
338+ /// - On Windows: calls WHvResetPartition (resets all per-VP state including
339+ /// GP registers, debug registers, XSAVE, MSRs, APIC, etc.)
340+ /// - On Linux: explicitly resets GP registers, debug registers, and XSAVE
341+ ///
342+ /// This does NOT restore special registers (except on windows). Call `restore_sregs` separately
343+ /// after memory mappings are established.
342344 // TODO: check if other state needs to be reset
343- pub ( crate ) fn reset_vcpu (
345+ pub ( crate ) fn reset_vm_state ( & mut self ) -> std:: result:: Result < ( ) , RegisterError > {
346+ #[ cfg( target_os = "windows" ) ]
347+ self . vm . reset_partition ( ) ?;
348+
349+ #[ cfg( not( target_os = "windows" ) ) ]
350+ {
351+ self . vm . set_regs ( & CommonRegisters {
352+ rflags : 1 << 1 , // Reserved bit always set
353+ ..Default :: default ( )
354+ } ) ?;
355+ self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
356+ self . vm . reset_xsave ( ) ?;
357+ }
358+
359+ Ok ( ( ) )
360+ }
361+
362+ /// Restores special registers from snapshot with CR3 updated to the
363+ /// new page table location.
364+ pub ( crate ) fn restore_sregs (
344365 & mut self ,
345366 cr3 : u64 ,
346367 sregs : & CommonSpecialRegisters ,
347368 ) -> std:: result:: Result < ( ) , RegisterError > {
348- self . vm . set_regs ( & CommonRegisters {
349- rflags : 1 << 1 , // Reserved bit always set
350- ..Default :: default ( )
351- } ) ?;
352- self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
353- self . vm . reset_xsave ( ) ?;
354-
355369 #[ cfg( not( feature = "nanvix-unstable" ) ) ]
356370 {
357371 // Restore the full special registers from snapshot, but update CR3
@@ -885,7 +899,9 @@ mod tests {
885899 use super :: * ;
886900 #[ cfg( kvm) ]
887901 use crate :: hypervisor:: regs:: FP_CONTROL_WORD_DEFAULT ;
888- use crate :: hypervisor:: regs:: { CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT } ;
902+ use crate :: hypervisor:: regs:: {
903+ CommonDebugRegs , CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT ,
904+ } ;
889905 use crate :: hypervisor:: virtual_machine:: VirtualMachine ;
890906 use crate :: mem:: layout:: SandboxMemoryLayout ;
891907 use crate :: mem:: memory_region:: { GuestMemoryRegion , MemoryRegionFlags } ;
@@ -1206,16 +1222,44 @@ mod tests {
12061222 }
12071223
12081224 /// Assert that general-purpose registers are in reset state.
1209- /// After reset, all registers should be zeroed except rflags which has
1210- /// reserved bit 1 always set.
1225+ ///
1226+ /// On Linux (KVM/MSHV): reset_vm_state explicitly zeroes all GP regs and sets
1227+ /// rflags = 0x2, so we verify all-zeros.
1228+ ///
1229+ /// On Windows: WHvResetPartition sets architectural power-on defaults
1230+ /// per Intel SDM Vol. 3, Table 10-1:
1231+ /// RIP = 0xFFF0 (reset vector)
1232+ /// RDX = CPUID signature (CPU-dependent stepping/model/family)
1233+ /// RFLAGS = 0x2 (only reserved bit 1 set)
1234+ /// All other GP regs = 0
1235+ /// These are overwritten by dispatch_call_from_host before guest execution,
1236+ /// but we still verify the power-on state is correct.
12111237 fn assert_regs_reset ( vm : & dyn VirtualMachine ) {
1238+ let regs = vm. regs ( ) . unwrap ( ) ;
1239+ #[ cfg( not( target_os = "windows" ) ) ]
12121240 assert_eq ! (
1213- vm . regs( ) . unwrap ( ) ,
1241+ regs,
12141242 CommonRegisters {
1215- rflags: 1 << 1 , // Reserved bit 1 is always set
1243+ rflags: 1 << 1 ,
12161244 ..Default :: default ( )
12171245 }
12181246 ) ;
1247+ #[ cfg( target_os = "windows" ) ]
1248+ {
1249+ // WHvResetPartition sets x86 power-on reset values
1250+ // (Intel SDM Vol. 3, Table 10-1)
1251+ let expected = CommonRegisters {
1252+ rip : 0xFFF0 , // Reset vector
1253+ rdx : regs. rdx , // CPUID signature (CPU-dependent)
1254+ rflags : 0x2 , // Reserved bit 1
1255+ ..Default :: default ( )
1256+ } ;
1257+ assert_ne ! (
1258+ regs. rdx, 0x4444444444444444 ,
1259+ "RDX should not retain dirty value"
1260+ ) ;
1261+ assert_eq ! ( regs, expected) ;
1262+ }
12191263 }
12201264
12211265 /// Assert that FPU state is in reset state.
@@ -1629,7 +1673,8 @@ mod tests {
16291673 assert_eq ! ( got_sregs, expected_sregs) ;
16301674
16311675 // Reset the vCPU
1632- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1676+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1677+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
16331678
16341679 // Verify registers are reset to defaults
16351680 assert_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1694,7 +1739,7 @@ mod tests {
16941739 "xsave should be zeroed except for hypervisor-specific fields"
16951740 ) ;
16961741
1697- // Verify sregs are reset to defaults (CR3 is 0 as passed to reset_vcpu)
1742+ // Verify sregs are reset to defaults
16981743 assert_sregs_reset ( hyperlight_vm. vm . as_ref ( ) , 0 ) ;
16991744 }
17001745
@@ -1758,7 +1803,8 @@ mod tests {
17581803 assert_eq ! ( regs, expected_dirty) ;
17591804
17601805 // Reset vcpu
1761- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1806+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1807+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
17621808
17631809 // Check registers are reset to defaults
17641810 assert_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1882,7 +1928,8 @@ mod tests {
18821928 }
18831929
18841930 // Reset vcpu
1885- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1931+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1932+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
18861933
18871934 // Check FPU is reset to defaults
18881935 assert_fpu_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1933,7 +1980,8 @@ mod tests {
19331980 assert_eq ! ( debug_regs, expected_dirty) ;
19341981
19351982 // Reset vcpu
1936- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1983+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1984+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
19371985
19381986 // Check debug registers are reset to default values
19391987 assert_debug_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1982,9 +2030,10 @@ mod tests {
19822030 assert_eq ! ( sregs, expected_dirty) ;
19832031
19842032 // Reset vcpu
1985- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
2033+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
2034+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
19862035
1987- // Check registers are reset to defaults (CR3 is 0 as passed to reset_vcpu)
2036+ // Check registers are reset to defaults
19882037 let sregs = hyperlight_vm. vm . sregs ( ) . unwrap ( ) ;
19892038 let mut expected_reset = CommonSpecialRegisters :: standard_64bit_defaults ( 0 ) ;
19902039 normalize_sregs_for_run_tests ( & mut expected_reset, & sregs) ;
@@ -2020,7 +2069,11 @@ mod tests {
20202069 let root_pt_addr = ctx. ctx . vm . get_root_pt ( ) . unwrap ( ) ;
20212070 let segment_state = ctx. ctx . vm . get_snapshot_sregs ( ) . unwrap ( ) ;
20222071
2023- ctx. ctx . vm . reset_vcpu ( root_pt_addr, & segment_state) . unwrap ( ) ;
2072+ ctx. ctx . vm . reset_vm_state ( ) . unwrap ( ) ;
2073+ ctx. ctx
2074+ . vm
2075+ . restore_sregs ( root_pt_addr, & segment_state)
2076+ . unwrap ( ) ;
20242077
20252078 // Re-run from entrypoint (flag=1 means guest skips dirty phase, just does FXSAVE)
20262079 // Use stack_top - 8 to match initialise()'s behavior (simulates call pushing return addr)
0 commit comments