@@ -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+ // MSR intercept can be unsafely disabled by the user in the config
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,17 @@ impl HyperlightVm {
172185 dropped : AtomicBool :: new ( false ) ,
173186 } ) ;
174187
188+ // get xsave header for debugging
189+ #[ cfg( target_os = "windows" ) ]
190+ let xsave = vm. xsave ( ) ?;
191+
192+ #[ cfg( target_os = "windows" ) ]
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+
175199 #[ cfg_attr( not( gdb) , allow( unused_mut) ) ]
176200 let mut ret = Self {
177201 vm,
@@ -185,6 +209,8 @@ impl HyperlightVm {
185209 mmap_regions : Vec :: new ( ) ,
186210 freed_slots : Vec :: new ( ) ,
187211
212+ #[ cfg( feature = "init-paging" ) ]
213+ pml4_addr : _pml4_addr,
188214 #[ cfg( gdb) ]
189215 gdb_conn,
190216 #[ cfg( gdb) ]
@@ -193,6 +219,8 @@ impl HyperlightVm {
193219 trace_info,
194220 #[ cfg( crashdump) ]
195221 rt_cfg,
222+ #[ cfg( target_os = "windows" ) ]
223+ xcomp_bv,
196224 } ;
197225
198226 // Send the interrupt handle to the GDB thread if debugging is enabled
@@ -332,9 +360,6 @@ impl HyperlightVm {
332360 } ;
333361 self . vm . set_regs ( & regs) ?;
334362
335- // reset fpu
336- self . vm . set_fpu ( & CommonFpu :: default ( ) ) ?;
337-
338363 self . run (
339364 mem_mgr,
340365 host_funcs,
@@ -487,6 +512,12 @@ impl HyperlightVm {
487512 }
488513 }
489514 }
515+ Ok ( HyperlightExit :: MsrRead ( msr_index) ) => {
516+ break Err ( HyperlightError :: MsrReadViolation ( msr_index) ) ;
517+ }
518+ Ok ( HyperlightExit :: MsrWrite { msr_index, value } ) => {
519+ break Err ( HyperlightError :: MsrWriteViolation ( msr_index, value) ) ;
520+ }
490521 Ok ( HyperlightExit :: Cancelled ( ) ) => {
491522 // If cancellation was not requested for this specific guest function call,
492523 // the vcpu was interrupted by a stale cancellation. This can occur when:
@@ -581,6 +612,42 @@ impl HyperlightVm {
581612 Ok ( ( ) )
582613 }
583614
615+ pub ( crate ) fn reset_vcpu ( & self ) -> Result < ( ) > {
616+ self . vm . set_regs ( & CommonRegisters :: default ( ) ) ?;
617+ self . vm . set_fpu ( & CommonFpu :: default ( ) ) ?;
618+ self . vm . set_debug_regs ( & CommonDebugRegs :: default ( ) ) ?;
619+
620+ #[ cfg( feature = "init-paging" ) ]
621+ self . vm
622+ . set_sregs ( & CommonSpecialRegisters :: standard_64bit_defaults (
623+ self . pml4_addr ,
624+ ) ) ?;
625+ #[ cfg( not( feature = "init-paging" ) ) ]
626+ self . vm
627+ . set_sregs ( & CommonSpecialRegisters :: standard_real_mode_defaults ( ) ) ?;
628+
629+ // Windows does not support just "zeroing" out the xsave area - we need to preserve the XCOMP_BV.
630+ #[ cfg( target_os = "windows" ) ]
631+ {
632+ // The XSAVE header starts at offset 512.
633+ // Bytes 7:0 of the header is XSTATE_BV.
634+ // Bytes 15:8 of the header is XCOMP_BV.
635+ // So XCOMP_BV starts at offset 512 + 8 = 520.
636+ // We are using a u32 array, so the index is 520 / 4 = 130.
637+ const XCOMP_BV_OFFSET_U32 : usize = 520 / std:: mem:: size_of :: < u32 > ( ) ;
638+
639+ let mut xsave = [ 0u32 ; 1024 ] ;
640+ xsave[ XCOMP_BV_OFFSET_U32 ] = self . xcomp_bv as u32 ;
641+ xsave[ XCOMP_BV_OFFSET_U32 + 1 ] = ( self . xcomp_bv >> 32 ) as u32 ;
642+ self . vm . set_xsave ( & xsave) ?;
643+ }
644+ #[ cfg( not( target_os = "windows" ) ) ]
645+ self . vm . set_xsave ( & [ 0 ; 1024 ] ) ?;
646+
647+ // MSRs don't need to be reset as they cannot be modified by guest (unless unsafely-allowed)
648+ Ok ( ( ) )
649+ }
650+
584651 // Handle a debug exit
585652 #[ cfg( gdb) ]
586653 fn handle_debug (
@@ -818,6 +885,158 @@ fn get_memory_access_violation<'a>(
818885 None
819886}
820887
888+ #[ cfg( test) ]
889+ mod tests {
890+ use super :: * ;
891+ use crate :: is_hypervisor_present;
892+ use crate :: mem:: layout:: SandboxMemoryLayout ;
893+ use crate :: mem:: mgr:: SandboxMemoryManager ;
894+ use crate :: mem:: ptr:: RawPtr ;
895+ use crate :: mem:: ptr_offset:: Offset ;
896+ use crate :: mem:: shared_mem:: { ExclusiveSharedMemory , SharedMemory } ;
897+ use crate :: sandbox:: SandboxConfiguration ;
898+ use crate :: sandbox:: host_funcs:: FunctionRegistry ;
899+ #[ cfg( feature = "mem_profile" ) ]
900+ use crate :: sandbox:: trace:: DummyUnwindInfo ;
901+ #[ cfg( feature = "mem_profile" ) ]
902+ use crate :: sandbox:: trace:: MemTraceInfo ;
903+ #[ cfg( crashdump) ]
904+ use crate :: sandbox:: uninitialized:: SandboxRuntimeConfig ;
905+ use std:: sync:: { Arc , Mutex } ;
906+
907+ #[ test]
908+ fn test_reset_vcpu ( ) -> Result < ( ) > {
909+ if !is_hypervisor_present ( ) {
910+ return Ok ( ( ) ) ;
911+ }
912+
913+ #[ cfg( target_os = "windows" ) ]
914+ return Ok ( ( ) ) ;
915+
916+ #[ cfg( not( target_os = "windows" ) ) ]
917+ {
918+ let mut code = Vec :: new ( ) ;
919+ // mov rax, 0x1111111111111111
920+ code. extend_from_slice ( & [ 0x48 , 0xb8 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 , 0x11 ] ) ;
921+ // mov rbx, 0x2222222222222222
922+ code. extend_from_slice ( & [ 0x48 , 0xbb , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 , 0x22 ] ) ;
923+ // mov rcx, 0x3333333333333333
924+ code. extend_from_slice ( & [ 0x48 , 0xb9 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 , 0x33 ] ) ;
925+
926+ // hlt
927+ code. extend_from_slice ( & [ 0xf4 ] ) ;
928+
929+ let config: SandboxConfiguration = Default :: default ( ) ;
930+ #[ cfg( crashdump) ]
931+ let rt_cfg: SandboxRuntimeConfig = Default :: default ( ) ;
932+ #[ cfg( feature = "mem_profile" ) ]
933+ let trace_info = MemTraceInfo :: new ( Arc :: new ( DummyUnwindInfo { } ) ) . unwrap ( ) ;
934+
935+ let layout = SandboxMemoryLayout :: new (
936+ config. clone ( ) ,
937+ code. len ( ) , // code size
938+ 0 , // stack
939+ 0 , // heap
940+ 0 , // init data
941+ None ,
942+ ) ?;
943+
944+ let mem_size = layout. get_memory_size ( ) ?;
945+ let eshm = ExclusiveSharedMemory :: new ( mem_size) ?;
946+
947+ let stack_cookie = [ 0u8 ; 16 ] ;
948+ let mem_mgr = SandboxMemoryManager :: new (
949+ layout. clone ( ) ,
950+ eshm,
951+ RawPtr :: from ( 0 ) ,
952+ Offset :: from ( 0 ) ,
953+ stack_cookie,
954+ ) ;
955+
956+ let ( mut mem_mgr_hshm, mut mem_mgr_gshm) = mem_mgr. build ( ) ;
957+
958+ // Set up shared memory (page tables)
959+ #[ cfg( feature = "init-paging" ) ]
960+ let rsp = {
961+ let mut regions = layout. get_memory_regions ( & mem_mgr_gshm. shared_mem ) ?;
962+ let mem_size = mem_mgr_gshm. shared_mem . mem_size ( ) as u64 ;
963+ mem_mgr_gshm. set_up_shared_memory ( mem_size, & mut regions) ?
964+ } ;
965+ #[ cfg( not( feature = "init-paging" ) ) ]
966+ let rsp = 0 ;
967+
968+ // Write code
969+ let code_offset = layout. get_guest_code_offset ( ) ;
970+ mem_mgr_hshm
971+ . shared_mem
972+ . copy_from_slice ( & code, code_offset) ?;
973+
974+ // Get regions for VM
975+ let regions = layout
976+ . get_memory_regions ( & mem_mgr_gshm. shared_mem ) ?
977+ . into_iter ( )
978+ . filter ( |r| r. guest_region . len ( ) > 0 )
979+ . collect ( ) ;
980+
981+ // Calculate pml4_addr
982+ #[ cfg( feature = "init-paging" ) ]
983+ let pml4_addr = SandboxMemoryLayout :: PML4_OFFSET as u64 ;
984+ #[ cfg( not( feature = "init-paging" ) ) ]
985+ let pml4_addr = 0 ;
986+
987+ // Entrypoint
988+ let entrypoint = layout. get_guest_code_address ( ) as u64 ;
989+
990+ let mut vm = HyperlightVm :: new (
991+ regions,
992+ pml4_addr,
993+ entrypoint,
994+ rsp,
995+ & config,
996+ #[ cfg( gdb) ]
997+ None ,
998+ #[ cfg( crashdump) ]
999+ rt_cfg,
1000+ #[ cfg( feature = "mem_profile" ) ]
1001+ trace_info,
1002+ ) ?;
1003+
1004+ let host_funcs = Arc :: new ( Mutex :: new ( FunctionRegistry :: default ( ) ) ) ;
1005+ #[ cfg( gdb) ]
1006+ let dbg_mem_access_fn = Arc :: new ( Mutex :: new ( mem_mgr_hshm. clone ( ) ) ) ;
1007+
1008+ // Run the VM
1009+ vm. initialise (
1010+ RawPtr :: from ( 0 ) , // peb_addr
1011+ 0 , // seed
1012+ 4096 , // page_size
1013+ & mut mem_mgr_hshm,
1014+ & host_funcs,
1015+ None ,
1016+ #[ cfg( gdb) ]
1017+ dbg_mem_access_fn. clone ( ) ,
1018+ ) ?;
1019+
1020+ // After run, check registers
1021+ let regs = vm. vm . regs ( ) ?;
1022+ assert_eq ! ( regs. rax, 0x1111111111111111 ) ;
1023+ assert_eq ! ( regs. rbx, 0x2222222222222222 ) ;
1024+ assert_eq ! ( regs. rcx, 0x3333333333333333 ) ;
1025+
1026+ // Reset vcpu
1027+ vm. reset_vcpu ( ) ?;
1028+
1029+ // Check registers again
1030+ let regs = vm. vm . regs ( ) ?;
1031+ assert_eq ! ( regs. rax, 0 ) ;
1032+ assert_eq ! ( regs. rbx, 0 ) ;
1033+ assert_eq ! ( regs. rcx, 0 ) ;
1034+ }
1035+
1036+ Ok ( ( ) )
1037+ }
1038+ }
1039+
8211040#[ cfg( gdb) ]
8221041mod debug {
8231042 use hyperlight_common:: mem:: PAGE_SIZE ;
0 commit comments