@@ -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) ]
@@ -330,22 +330,29 @@ impl HyperlightVm {
330330 }
331331
332332 /// Resets the following vCPU state:
333- /// - General purpose registers
334- /// - Debug registers
335- /// - XSAVE (includes FPU/SSE state with proper FCW and MXCSR defaults)
336- /// - Special registers (restored from snapshot, with CR3 updated to new page table location)
333+ /// - On Windows: calls WHvResetPartition (resets all per-VP state including
334+ /// GP registers, debug registers, XSAVE, MSRs, APIC, etc.)
335+ /// - On Linux: explicitly resets GP registers, debug registers, and XSAVE
336+ /// - On all platforms: restores special registers from snapshot with CR3
337+ /// updated to new page table location
337338 // TODO: check if other state needs to be reset
338339 pub ( crate ) fn reset_vcpu (
339340 & mut self ,
340341 cr3 : u64 ,
341342 sregs : & CommonSpecialRegisters ,
342343 ) -> std:: result:: Result < ( ) , RegisterError > {
343- self . vm . set_regs ( & CommonRegisters {
344- rflags : 1 << 1 , // Reserved bit always set
345- ..Default :: default ( )
346- } ) ?;
347- self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
348- self . vm . reset_xsave ( ) ?;
344+ #[ cfg( target_os = "windows" ) ]
345+ self . vm . reset_partition ( ) ?;
346+
347+ #[ cfg( not( target_os = "windows" ) ) ]
348+ {
349+ self . vm . set_regs ( & CommonRegisters {
350+ rflags : 1 << 1 , // Reserved bit always set
351+ ..Default :: default ( )
352+ } ) ?;
353+ self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
354+ self . vm . reset_xsave ( ) ?;
355+ }
349356
350357 #[ cfg( not( feature = "nanvix-unstable" ) ) ]
351358 {
@@ -880,7 +887,9 @@ mod tests {
880887 use super :: * ;
881888 #[ cfg( kvm) ]
882889 use crate :: hypervisor:: regs:: FP_CONTROL_WORD_DEFAULT ;
883- use crate :: hypervisor:: regs:: { CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT } ;
890+ use crate :: hypervisor:: regs:: {
891+ CommonDebugRegs , CommonSegmentRegister , CommonTableRegister , MXCSR_DEFAULT ,
892+ } ;
884893 use crate :: hypervisor:: virtual_machine:: VirtualMachine ;
885894 use crate :: mem:: layout:: SandboxMemoryLayout ;
886895 use crate :: mem:: memory_region:: { GuestMemoryRegion , MemoryRegionFlags } ;
@@ -1184,9 +1193,18 @@ mod tests {
11841193 // Assertion Helpers - Verify vCPU state after reset
11851194 // ==========================================================================
11861195
1187- /// Assert that debug registers are in reset state.
1188- /// Reserved bits in DR6/DR7 are read-only (set by CPU), so we only check
1189- /// that writable bits are cleared to 0 and DR0-DR3 are zeroed.
1196+ /// Assert that debug registers are in architectural reset state.
1197+ ///
1198+ /// On Linux (KVM/MSHV): reset_vcpu explicitly zeroes debug registers.
1199+ ///
1200+ /// On Windows: WHvResetPartition resets to power-on defaults per
1201+ /// Intel SDM Vol. 3, Table 10-1:
1202+ /// DR0-DR3 = 0 (breakpoint addresses cleared)
1203+ /// DR6 = 0xFFFF0FF0 (reserved bits set, writable bits cleared)
1204+ /// DR7 = 0x00000400 (reserved bit 10 set, all enables cleared)
1205+ ///
1206+ /// Reserved bits in DR6/DR7 are read-only and CPU-dependent, so we only
1207+ /// verify that writable bits are cleared to 0 and DR0-DR3 are zeroed.
11901208 fn assert_debug_regs_reset ( vm : & dyn VirtualMachine ) {
11911209 let debug_regs = vm. debug_regs ( ) . unwrap ( ) ;
11921210 let expected = CommonDebugRegs {
@@ -1201,19 +1219,58 @@ mod tests {
12011219 }
12021220
12031221 /// Assert that general-purpose registers are in reset state.
1204- /// After reset, all registers should be zeroed except rflags which has
1205- /// reserved bit 1 always set.
1222+ ///
1223+ /// On Linux (KVM/MSHV): reset_vcpu explicitly zeroes all GP regs and sets
1224+ /// rflags = 0x2, so we verify all-zeros.
1225+ ///
1226+ /// On Windows: WHvResetPartition sets architectural power-on defaults
1227+ /// per Intel SDM Vol. 3, Table 10-1:
1228+ /// RIP = 0xFFF0 (reset vector)
1229+ /// RDX = CPUID signature (CPU-dependent stepping/model/family)
1230+ /// RFLAGS = 0x2 (only reserved bit 1 set)
1231+ /// All other GP regs = 0
1232+ /// These are overwritten by dispatch_call_from_host before guest execution,
1233+ /// but we still verify the power-on state is correct.
12061234 fn assert_regs_reset ( vm : & dyn VirtualMachine ) {
1235+ let regs = vm. regs ( ) . unwrap ( ) ;
1236+ #[ cfg( not( target_os = "windows" ) ) ]
12071237 assert_eq ! (
1208- vm . regs( ) . unwrap ( ) ,
1238+ regs,
12091239 CommonRegisters {
1210- rflags: 1 << 1 , // Reserved bit 1 is always set
1240+ rflags: 1 << 1 ,
12111241 ..Default :: default ( )
12121242 }
12131243 ) ;
1244+ #[ cfg( target_os = "windows" ) ]
1245+ {
1246+ // WHvResetPartition sets x86 power-on reset values
1247+ // (Intel SDM Vol. 3, Table 10-1)
1248+ let expected = CommonRegisters {
1249+ rip : 0xFFF0 , // Reset vector
1250+ rdx : regs. rdx , // CPUID signature (CPU-dependent)
1251+ rflags : 0x2 , // Reserved bit 1
1252+ ..Default :: default ( )
1253+ } ;
1254+ assert_ne ! (
1255+ regs. rdx, 0x4444444444444444 ,
1256+ "RDX should not retain dirty value"
1257+ ) ;
1258+ assert_eq ! ( regs, expected) ;
1259+ }
12141260 }
12151261
12161262 /// Assert that FPU state is in reset state.
1263+ ///
1264+ /// On Linux (KVM/MSHV): reset_vcpu calls reset_xsave which zeroes FPU state
1265+ /// and sets FCW/MXCSR to defaults.
1266+ ///
1267+ /// On Windows: WHvResetPartition resets to power-on defaults per
1268+ /// Intel SDM Vol. 3, Table 10-1 (FINIT-equivalent state):
1269+ /// FCW = 0x037F (all exceptions masked, precision=64-bit, round=nearest)
1270+ /// FSW = 0, FTW = 0 (all empty), FOP = 0, FIP = 0, FDP = 0
1271+ /// MXCSR = 0x1F80 (all SIMD exceptions masked, round=nearest)
1272+ /// ST0-ST7 = 0, XMM0-XMM15 = 0
1273+ ///
12171274 /// Handles hypervisor-specific quirks (KVM MXCSR, empty FPU registers).
12181275 fn assert_fpu_reset ( vm : & dyn VirtualMachine ) {
12191276 let fpu = vm. fpu ( ) . unwrap ( ) ;
@@ -1223,8 +1280,14 @@ mod tests {
12231280 assert_eq ! ( fpu, expected_fpu) ;
12241281 }
12251282
1226- /// Assert that special registers are in reset state.
1227- /// Handles hypervisor-specific differences in hidden descriptor cache fields.
1283+ /// Assert that special registers match the expected snapshot state.
1284+ ///
1285+ /// After reset_vcpu, sregs are explicitly restored from the snapshot
1286+ /// (with CR3 updated). This verifies they match the expected 64-bit
1287+ /// long mode configuration from CommonSpecialRegisters::standard_64bit_defaults.
1288+ ///
1289+ /// Handles hypervisor-specific differences in hidden descriptor cache fields
1290+ /// (unusable, granularity, type_ for unused segments).
12281291 fn assert_sregs_reset ( vm : & dyn VirtualMachine , pml4_addr : u64 ) {
12291292 let defaults = CommonSpecialRegisters :: standard_64bit_defaults ( pml4_addr) ;
12301293 let sregs = vm. sregs ( ) . unwrap ( ) ;
0 commit comments