@@ -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,27 +335,39 @@ 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 — 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 {
357- // Restore the full special registers from snapshot, but update CR3
358- // to point to the new (relocated) page tables
359371 let mut sregs = * sregs;
360372 sregs. cr3 = cr3;
361373 self . pending_tlb_flush = true ;
@@ -885,7 +897,9 @@ mod tests {
885897 use super :: * ;
886898 #[ cfg( kvm) ]
887899 use crate :: hypervisor:: regs:: FP_CONTROL_WORD_DEFAULT ;
888- use crate :: hypervisor:: regs:: { CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT } ;
900+ use crate :: hypervisor:: regs:: {
901+ CommonDebugRegs , CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT ,
902+ } ;
889903 use crate :: hypervisor:: virtual_machine:: VirtualMachine ;
890904 use crate :: mem:: layout:: SandboxMemoryLayout ;
891905 use crate :: mem:: memory_region:: { GuestMemoryRegion , MemoryRegionFlags } ;
@@ -912,7 +926,7 @@ mod tests {
912926 // Dirty State Builders - Create non-default vCPU state for testing reset
913927 // ==========================================================================
914928
915- /// Build dirty general purpose registers for testing reset_vcpu .
929+ /// Build dirty general purpose registers for testing reset .
916930 fn dirty_regs ( ) -> CommonRegisters {
917931 CommonRegisters {
918932 rax : 0x1111111111111111 ,
@@ -936,7 +950,7 @@ mod tests {
936950 }
937951 }
938952
939- /// Build dirty FPU state for testing reset_vcpu .
953+ /// Build dirty FPU state for testing reset .
940954 fn dirty_fpu ( ) -> CommonFpu {
941955 CommonFpu {
942956 fpr : [ [ 0xAB ; 16 ] ; 8 ] ,
@@ -951,7 +965,7 @@ mod tests {
951965 }
952966 }
953967
954- /// Build dirty special registers for testing reset_vcpu .
968+ /// Build dirty special registers for testing reset .
955969 /// Must be consistent for 64-bit long mode (CR0/CR4/EFER).
956970 fn dirty_sregs ( _pml4_addr : u64 ) -> CommonSpecialRegisters {
957971 let segment = CommonSegmentRegister {
@@ -1020,7 +1034,7 @@ mod tests {
10201034 }
10211035 }
10221036
1023- /// Build dirty debug registers for testing reset_vcpu .
1037+ /// Build dirty debug registers for testing reset .
10241038 ///
10251039 /// DR6 bit layout (Intel SDM / AMD APM):
10261040 /// Bits 0-3 (B0-B3): Breakpoint condition detected - software writable/clearable
@@ -1064,8 +1078,8 @@ mod tests {
10641078 }
10651079 }
10661080
1067- /// Returns default test values for reset_vcpu parameters.
1068- /// Uses standard 64-bit defaults since reset_vcpu now restores full sregs from snapshot.
1081+ /// Returns default test values for restore_sregs parameters.
1082+ /// Uses standard 64-bit defaults since restore_sregs restores full sregs from snapshot.
10691083 fn default_sregs ( ) -> CommonSpecialRegisters {
10701084 CommonSpecialRegisters :: standard_64bit_defaults ( 0 )
10711085 }
@@ -1189,9 +1203,18 @@ mod tests {
11891203 // Assertion Helpers - Verify vCPU state after reset
11901204 // ==========================================================================
11911205
1192- /// Assert that debug registers are in reset state.
1193- /// Reserved bits in DR6/DR7 are read-only (set by CPU), so we only check
1194- /// that writable bits are cleared to 0 and DR0-DR3 are zeroed.
1206+ /// Assert that debug registers are in architectural reset state.
1207+ ///
1208+ /// On Linux (KVM/MSHV): reset_vm_state explicitly zeroes debug registers.
1209+ ///
1210+ /// On Windows: WHvResetPartition resets to power-on defaults per
1211+ /// Intel SDM Vol. 3, Table 10-1:
1212+ /// DR0-DR3 = 0 (breakpoint addresses cleared)
1213+ /// DR6 = 0xFFFF0FF0 (reserved bits set, writable bits cleared)
1214+ /// DR7 = 0x00000400 (reserved bit 10 set, all enables cleared)
1215+ ///
1216+ /// Reserved bits in DR6/DR7 are read-only and CPU-dependent, so we only
1217+ /// verify that writable bits are cleared to 0 and DR0-DR3 are zeroed.
11951218 fn assert_debug_regs_reset ( vm : & dyn VirtualMachine ) {
11961219 let debug_regs = vm. debug_regs ( ) . unwrap ( ) ;
11971220 let expected = CommonDebugRegs {
@@ -1206,19 +1229,58 @@ mod tests {
12061229 }
12071230
12081231 /// 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.
1232+ ///
1233+ /// On Linux (KVM/MSHV): reset_vm_state explicitly zeroes all GP regs and sets
1234+ /// rflags = 0x2, so we verify all-zeros.
1235+ ///
1236+ /// On Windows: WHvResetPartition sets architectural power-on defaults
1237+ /// per Intel SDM Vol. 3, Table 10-1:
1238+ /// RIP = 0xFFF0 (reset vector)
1239+ /// RDX = CPUID signature (CPU-dependent stepping/model/family)
1240+ /// RFLAGS = 0x2 (only reserved bit 1 set)
1241+ /// All other GP regs = 0
1242+ /// These are overwritten by dispatch_call_from_host before guest execution,
1243+ /// but we still verify the power-on state is correct.
12111244 fn assert_regs_reset ( vm : & dyn VirtualMachine ) {
1245+ let regs = vm. regs ( ) . unwrap ( ) ;
1246+ #[ cfg( not( target_os = "windows" ) ) ]
12121247 assert_eq ! (
1213- vm . regs( ) . unwrap ( ) ,
1248+ regs,
12141249 CommonRegisters {
1215- rflags: 1 << 1 , // Reserved bit 1 is always set
1250+ rflags: 1 << 1 ,
12161251 ..Default :: default ( )
12171252 }
12181253 ) ;
1254+ #[ cfg( target_os = "windows" ) ]
1255+ {
1256+ // WHvResetPartition sets x86 power-on reset values
1257+ // (Intel SDM Vol. 3, Table 10-1)
1258+ let expected = CommonRegisters {
1259+ rip : 0xFFF0 , // Reset vector
1260+ rdx : regs. rdx , // CPUID signature (CPU-dependent)
1261+ rflags : 0x2 , // Reserved bit 1
1262+ ..Default :: default ( )
1263+ } ;
1264+ assert_ne ! (
1265+ regs. rdx, 0x4444444444444444 ,
1266+ "RDX should not retain dirty value"
1267+ ) ;
1268+ assert_eq ! ( regs, expected) ;
1269+ }
12191270 }
12201271
12211272 /// Assert that FPU state is in reset state.
1273+ ///
1274+ /// On Linux (KVM/MSHV): reset_vm_state calls reset_xsave which zeroes FPU state
1275+ /// and sets FCW/MXCSR to defaults.
1276+ ///
1277+ /// On Windows: WHvResetPartition resets to power-on defaults per
1278+ /// Intel SDM Vol. 3, Table 10-1 (FINIT-equivalent state):
1279+ /// FCW = 0x037F (all exceptions masked, precision=64-bit, round=nearest)
1280+ /// FSW = 0, FTW = 0 (all empty), FOP = 0, FIP = 0, FDP = 0
1281+ /// MXCSR = 0x1F80 (all SIMD exceptions masked, round=nearest)
1282+ /// ST0-ST7 = 0, XMM0-XMM15 = 0
1283+ ///
12221284 /// Handles hypervisor-specific quirks (KVM MXCSR, empty FPU registers).
12231285 fn assert_fpu_reset ( vm : & dyn VirtualMachine ) {
12241286 let fpu = vm. fpu ( ) . unwrap ( ) ;
@@ -1228,8 +1290,14 @@ mod tests {
12281290 assert_eq ! ( fpu, expected_fpu) ;
12291291 }
12301292
1231- /// Assert that special registers are in reset state.
1232- /// Handles hypervisor-specific differences in hidden descriptor cache fields.
1293+ /// Assert that special registers match the expected snapshot state.
1294+ ///
1295+ /// After reset, sregs are explicitly restored from the snapshot
1296+ /// (with CR3 updated). This verifies they match the expected 64-bit
1297+ /// long mode configuration from CommonSpecialRegisters::standard_64bit_defaults.
1298+ ///
1299+ /// Handles hypervisor-specific differences in hidden descriptor cache fields
1300+ /// (unusable, granularity, type_ for unused segments).
12331301 fn assert_sregs_reset ( vm : & dyn VirtualMachine , pml4_addr : u64 ) {
12341302 let defaults = CommonSpecialRegisters :: standard_64bit_defaults ( pml4_addr) ;
12351303 let sregs = vm. sregs ( ) . unwrap ( ) ;
@@ -1339,7 +1407,7 @@ mod tests {
13391407 dirtied_mask
13401408 }
13411409
1342- /// Dirty the legacy XSAVE region (bytes 0-511) for testing reset_vcpu .
1410+ /// Dirty the legacy XSAVE region (bytes 0-511) for testing reset .
13431411 /// This includes FPU/x87 state, SSE state, and reserved areas.
13441412 ///
13451413 /// Layout (from Intel SDM Table 13-1):
@@ -1629,7 +1697,8 @@ mod tests {
16291697 assert_eq ! ( got_sregs, expected_sregs) ;
16301698
16311699 // Reset the vCPU
1632- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1700+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1701+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
16331702
16341703 // Verify registers are reset to defaults
16351704 assert_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1694,7 +1763,7 @@ mod tests {
16941763 "xsave should be zeroed except for hypervisor-specific fields"
16951764 ) ;
16961765
1697- // Verify sregs are reset to defaults (CR3 is 0 as passed to reset_vcpu)
1766+ // Verify sregs are reset to defaults
16981767 assert_sregs_reset ( hyperlight_vm. vm . as_ref ( ) , 0 ) ;
16991768 }
17001769
@@ -1758,7 +1827,8 @@ mod tests {
17581827 assert_eq ! ( regs, expected_dirty) ;
17591828
17601829 // Reset vcpu
1761- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1830+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1831+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
17621832
17631833 // Check registers are reset to defaults
17641834 assert_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1882,7 +1952,8 @@ mod tests {
18821952 }
18831953
18841954 // Reset vcpu
1885- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
1955+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
1956+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
18861957
18871958 // Check FPU is reset to defaults
18881959 assert_fpu_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1933,7 +2004,8 @@ mod tests {
19332004 assert_eq ! ( debug_regs, expected_dirty) ;
19342005
19352006 // Reset vcpu
1936- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
2007+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
2008+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
19372009
19382010 // Check debug registers are reset to default values
19392011 assert_debug_regs_reset ( hyperlight_vm. vm . as_ref ( ) ) ;
@@ -1982,9 +2054,10 @@ mod tests {
19822054 assert_eq ! ( sregs, expected_dirty) ;
19832055
19842056 // Reset vcpu
1985- hyperlight_vm. reset_vcpu ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
2057+ hyperlight_vm. reset_vm_state ( ) . unwrap ( ) ;
2058+ hyperlight_vm. restore_sregs ( 0 , & default_sregs ( ) ) . unwrap ( ) ;
19862059
1987- // Check registers are reset to defaults (CR3 is 0 as passed to reset_vcpu)
2060+ // Check registers are reset to defaults
19882061 let sregs = hyperlight_vm. vm . sregs ( ) . unwrap ( ) ;
19892062 let mut expected_reset = CommonSpecialRegisters :: standard_64bit_defaults ( 0 ) ;
19902063 normalize_sregs_for_run_tests ( & mut expected_reset, & sregs) ;
@@ -2020,7 +2093,11 @@ mod tests {
20202093 let root_pt_addr = ctx. ctx . vm . get_root_pt ( ) . unwrap ( ) ;
20212094 let segment_state = ctx. ctx . vm . get_snapshot_sregs ( ) . unwrap ( ) ;
20222095
2023- ctx. ctx . vm . reset_vcpu ( root_pt_addr, & segment_state) . unwrap ( ) ;
2096+ ctx. ctx . vm . reset_vm_state ( ) . unwrap ( ) ;
2097+ ctx. ctx
2098+ . vm
2099+ . restore_sregs ( root_pt_addr, & segment_state)
2100+ . unwrap ( ) ;
20242101
20252102 // Re-run from entrypoint (flag=1 means guest skips dirty phase, just does FXSAVE)
20262103 // Use stack_top - 8 to match initialise()'s behavior (simulates call pushing return addr)
0 commit comments