@@ -46,7 +46,7 @@ use crate::hypervisor::hyperv_linux::MshvVm;
4646use crate :: hypervisor:: hyperv_windows:: WhpVm ;
4747#[ cfg( kvm) ]
4848use crate :: hypervisor:: kvm:: KvmVm ;
49- use crate :: hypervisor:: regs:: CommonSpecialRegisters ;
49+ use crate :: hypervisor:: regs:: { CommonDebugRegs , CommonSpecialRegisters } ;
5050#[ cfg( target_os = "windows" ) ]
5151use crate :: hypervisor:: wrappers:: HandleWrapper ;
5252use crate :: hypervisor:: { HyperlightExit , InterruptHandle , InterruptHandleImpl , get_max_log_level} ;
@@ -86,6 +86,9 @@ pub(crate) struct HyperlightVm {
8686 next_slot : u32 , // Monotonically increasing slot number
8787 freed_slots : Vec < u32 > , // Reusable slots from unmapped regions
8888
89+ // pml4 saved to be able to restore it if needed
90+ #[ cfg( feature = "init-paging" ) ]
91+ pml4_addr : u64 ,
8992 #[ cfg( gdb) ]
9093 gdb_conn : Option < DebugCommChannel < DebugResponse , DebugMsg > > ,
9194 #[ cfg( gdb) ]
@@ -94,6 +97,11 @@ pub(crate) struct HyperlightVm {
9497 trace_info : MemTraceInfo ,
9598 #[ cfg( crashdump) ]
9699 rt_cfg : SandboxRuntimeConfig ,
100+ // Windows does not support just "zeroing" out the xsave area - we need to preserve the XCOMP_BV.
101+ // Since it varies by hardware, we can't just hardcode a value, so we save it here.
102+ // Also note that windows uses compacted format for xsave.
103+ #[ cfg( target_os = "windows" ) ]
104+ xcomp_bv : u64 ,
97105}
98106
99107impl HyperlightVm {
@@ -128,6 +136,11 @@ impl HyperlightVm {
128136 None => return Err ( NoHypervisorFound ( ) ) ,
129137 } ;
130138
139+ // Enable MSR intercepts unless explicitly allowed
140+ if !config. get_allow_msr ( ) {
141+ vm. enable_msr_intercept ( ) ?;
142+ }
143+
131144 for ( i, region) in mem_regions. iter ( ) . enumerate ( ) {
132145 // Safety: slots are unique and region points to valid memory since we created the regions
133146 unsafe { vm. map_memory ( ( i as u32 , region) ) ? } ;
@@ -172,6 +185,18 @@ impl HyperlightVm {
172185 dropped : AtomicBool :: new ( false ) ,
173186 } ) ;
174187
188+ // get xsave header for debugging
189+ #[ cfg( target_os = "windows" ) ]
190+ {
191+ let xsave = vm. xsave ( ) ?;
192+
193+ let xcomp_bv = if xsave. len ( ) >= 528 {
194+ u64:: from_le_bytes ( xsave[ 520 ..528 ] . try_into ( ) . unwrap ( ) )
195+ } else {
196+ return Err ( new_error ! ( "XSAVE buffer too small to contain XCOMP_BV" ) ) ;
197+ } ;
198+ }
199+
175200 #[ cfg_attr( not( gdb) , allow( unused_mut) ) ]
176201 let mut ret = Self {
177202 vm,
@@ -185,6 +210,8 @@ impl HyperlightVm {
185210 mmap_regions : Vec :: new ( ) ,
186211 freed_slots : Vec :: new ( ) ,
187212
213+ #[ cfg( feature = "init-paging" ) ]
214+ pml4_addr : _pml4_addr,
188215 #[ cfg( gdb) ]
189216 gdb_conn,
190217 #[ cfg( gdb) ]
@@ -193,6 +220,8 @@ impl HyperlightVm {
193220 trace_info,
194221 #[ cfg( crashdump) ]
195222 rt_cfg,
223+ #[ cfg( target_os = "windows" ) ]
224+ xcomp_bv,
196225 } ;
197226
198227 // Send the interrupt handle to the GDB thread if debugging is enabled
@@ -487,6 +516,12 @@ impl HyperlightVm {
487516 }
488517 }
489518 }
519+ Ok ( HyperlightExit :: MsrRead ( msr_index) ) => {
520+ break Err ( HyperlightError :: MsrReadViolation ( msr_index) ) ;
521+ }
522+ Ok ( HyperlightExit :: MsrWrite { msr_index, value } ) => {
523+ break Err ( HyperlightError :: MsrWriteViolation ( msr_index, value) ) ;
524+ }
490525 Ok ( HyperlightExit :: Cancelled ( ) ) => {
491526 // If cancellation was not requested for this specific guest function call,
492527 // the vcpu was interrupted by a stale cancellation. This can occur when:
@@ -581,6 +616,48 @@ impl HyperlightVm {
581616 Ok ( ( ) )
582617 }
583618
619+ // Resets the following vCPU state:
620+ // - General purpose registers
621+ // - FPU registers
622+ // - Debug registers
623+ // - Special registers
624+ // - XSAVE area
625+ pub ( crate ) fn reset_vcpu ( & self ) -> Result < ( ) > {
626+ self . vm . set_regs ( & CommonRegisters :: default ( ) ) ?;
627+ self . vm . set_fpu ( & CommonFpu :: default ( ) ) ?;
628+ self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
629+
630+ #[ cfg( feature = "init-paging" ) ]
631+ self . vm
632+ . set_sregs ( & CommonSpecialRegisters :: standard_64bit_defaults (
633+ self . pml4_addr ,
634+ ) ) ?;
635+ #[ cfg( not( feature = "init-paging" ) ) ]
636+ self . vm
637+ . set_sregs ( & CommonSpecialRegisters :: standard_real_mode_defaults ( ) ) ?;
638+
639+ // Windows does not support just "zeroing" out the xsave area - we need to preserve the XCOMP_BV.
640+ #[ cfg( target_os = "windows" ) ]
641+ {
642+ // The XSAVE header starts at offset 512.
643+ // Bytes 7:0 of the header is XSTATE_BV.
644+ // Bytes 15:8 of the header is XCOMP_BV.
645+ // So XCOMP_BV starts at offset 512 + 8 = 520.
646+ // We are using a u32 array, so the index is 520 / 4 = 130.
647+ const XCOMP_BV_OFFSET_U32 : usize = 520 / std:: mem:: size_of :: < u32 > ( ) ;
648+
649+ let mut xsave = [ 0u32 ; 1024 ] ;
650+ xsave[ XCOMP_BV_OFFSET_U32 ] = self . xcomp_bv as u32 ;
651+ xsave[ XCOMP_BV_OFFSET_U32 + 1 ] = ( self . xcomp_bv >> 32 ) as u32 ;
652+ self . vm . set_xsave ( & xsave) ?;
653+ }
654+ #[ cfg( not( target_os = "windows" ) ) ]
655+ self . vm . set_xsave ( & [ 0 ; 1024 ] ) ?;
656+
657+ // MSRs don't need to be reset as they cannot be modified by guest (unless unsafely-allowed)
658+ Ok ( ( ) )
659+ }
660+
584661 // Handle a debug exit
585662 #[ cfg( gdb) ]
586663 fn handle_debug (
@@ -818,6 +895,158 @@ fn get_memory_access_violation<'a>(
818895 None
819896}
820897
898+ #[ cfg( test) ]
899+ mod tests {
900+ use super :: * ;
901+ use crate :: is_hypervisor_present;
902+ use crate :: mem:: layout:: SandboxMemoryLayout ;
903+ use crate :: mem:: mgr:: SandboxMemoryManager ;
904+ use crate :: mem:: ptr:: RawPtr ;
905+ use crate :: mem:: ptr_offset:: Offset ;
906+ use crate :: mem:: shared_mem:: { ExclusiveSharedMemory , SharedMemory } ;
907+ use crate :: sandbox:: SandboxConfiguration ;
908+ use crate :: sandbox:: host_funcs:: FunctionRegistry ;
909+ #[ cfg( feature = "mem_profile" ) ]
910+ use crate :: sandbox:: trace:: DummyUnwindInfo ;
911+ #[ cfg( feature = "mem_profile" ) ]
912+ use crate :: sandbox:: trace:: MemTraceInfo ;
913+ #[ cfg( crashdump) ]
914+ use crate :: sandbox:: uninitialized:: SandboxRuntimeConfig ;
915+ use std:: sync:: { Arc , Mutex } ;
916+
917+ #[ test]
918+ fn test_reset_vcpu ( ) -> Result < ( ) > {
919+ if !is_hypervisor_present ( ) {
920+ return Ok ( ( ) ) ;
921+ }
922+
923+ #[ cfg( target_os = "windows" ) ]
924+ return Ok ( ( ) ) ;
925+
926+ #[ cfg( not( target_os = "windows" ) ) ]
927+ {
928+ let mut code = Vec :: new ( ) ;
929+ // mov rax, 0x1111111111111111
930+ code. extend_from_slice ( & [ 0x48 , 0xb8 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 ] ) ;
931+ // mov rbx, 0x2222222222222222
932+ code. extend_from_slice ( & [ 0x48 , 0xbb , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 ] ) ;
933+ // mov rcx, 0x3333333333333333
934+ code. extend_from_slice ( & [ 0x48 , 0xb9 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 ] ) ;
935+
936+ // hlt
937+ code. extend_from_slice ( & [ 0xf4 ] ) ;
938+
939+ let config: SandboxConfiguration = Default :: default ( ) ;
940+ #[ cfg( crashdump) ]
941+ let rt_cfg: SandboxRuntimeConfig = Default :: default ( ) ;
942+ #[ cfg( feature = "mem_profile" ) ]
943+ let trace_info = MemTraceInfo :: new ( Arc :: new ( DummyUnwindInfo { } ) ) . unwrap ( ) ;
944+
945+ let layout = SandboxMemoryLayout :: new (
946+ config. clone ( ) ,
947+ code. len ( ) , // code size
948+ 0 , // stack
949+ 0 , // heap
950+ 0 , // init data
951+ None ,
952+ ) ?;
953+
954+ let mem_size = layout. get_memory_size ( ) ?;
955+ let eshm = ExclusiveSharedMemory :: new ( mem_size) ?;
956+
957+ let stack_cookie = [ 0u8 ; 16 ] ;
958+ let mem_mgr = SandboxMemoryManager :: new (
959+ layout. clone ( ) ,
960+ eshm,
961+ RawPtr :: from ( 0 ) ,
962+ Offset :: from ( 0 ) ,
963+ stack_cookie,
964+ ) ;
965+
966+ let ( mut mem_mgr_hshm, mut mem_mgr_gshm) = mem_mgr. build ( ) ;
967+
968+ // Set up shared memory (page tables)
969+ #[ cfg( feature = "init-paging" ) ]
970+ let rsp = {
971+ let mut regions = layout. get_memory_regions ( & mem_mgr_gshm. shared_mem ) ?;
972+ let mem_size = mem_mgr_gshm. shared_mem . mem_size ( ) as u64 ;
973+ mem_mgr_gshm. set_up_shared_memory ( mem_size, & mut regions) ?
974+ } ;
975+ #[ cfg( not( feature = "init-paging" ) ) ]
976+ let rsp = 0 ;
977+
978+ // Write code
979+ let code_offset = layout. get_guest_code_offset ( ) ;
980+ mem_mgr_hshm
981+ . shared_mem
982+ . copy_from_slice ( & code, code_offset) ?;
983+
984+ // Get regions for VM
985+ let regions = layout
986+ . get_memory_regions ( & mem_mgr_gshm. shared_mem ) ?
987+ . into_iter ( )
988+ . filter ( |r| r. guest_region . len ( ) > 0 )
989+ . collect ( ) ;
990+
991+ // Calculate pml4_addr
992+ #[ cfg( feature = "init-paging" ) ]
993+ let pml4_addr = SandboxMemoryLayout :: PML4_OFFSET as u64 ;
994+ #[ cfg( not( feature = "init-paging" ) ) ]
995+ let pml4_addr = 0 ;
996+
997+ // Entrypoint
998+ let entrypoint = layout. get_guest_code_address ( ) as u64 ;
999+
1000+ let mut vm = HyperlightVm :: new (
1001+ regions,
1002+ pml4_addr,
1003+ entrypoint,
1004+ rsp,
1005+ & config,
1006+ #[ cfg( gdb) ]
1007+ None ,
1008+ #[ cfg( crashdump) ]
1009+ rt_cfg,
1010+ #[ cfg( feature = "mem_profile" ) ]
1011+ trace_info,
1012+ ) ?;
1013+
1014+ let host_funcs = Arc :: new ( Mutex :: new ( FunctionRegistry :: default ( ) ) ) ;
1015+ #[ cfg( gdb) ]
1016+ let dbg_mem_access_fn = Arc :: new ( Mutex :: new ( mem_mgr_hshm. clone ( ) ) ) ;
1017+
1018+ // Run the VM
1019+ vm. initialise (
1020+ RawPtr :: from ( 0 ) , // peb_addr
1021+ 0 , // seed
1022+ 4096 , // page_size
1023+ & mut mem_mgr_hshm,
1024+ & host_funcs,
1025+ None ,
1026+ #[ cfg( gdb) ]
1027+ dbg_mem_access_fn. clone ( ) ,
1028+ ) ?;
1029+
1030+ // After run, check registers
1031+ let regs = vm. vm . regs ( ) ?;
1032+ assert_eq ! ( regs. rax, 0x1111111111111111 ) ;
1033+ assert_eq ! ( regs. rbx, 0x2222222222222222 ) ;
1034+ assert_eq ! ( regs. rcx, 0x3333333333333333 ) ;
1035+
1036+ // Reset vcpu
1037+ vm. reset_vcpu ( ) ?;
1038+
1039+ // Check registers again
1040+ let regs = vm. vm . regs ( ) ?;
1041+ assert_eq ! ( regs. rax, 0 ) ;
1042+ assert_eq ! ( regs. rbx, 0 ) ;
1043+ assert_eq ! ( regs. rcx, 0 ) ;
1044+ }
1045+
1046+ Ok ( ( ) )
1047+ }
1048+ }
1049+
8211050#[ cfg( gdb) ]
8221051mod debug {
8231052 use hyperlight_common:: mem:: PAGE_SIZE ;
0 commit comments