@@ -41,9 +41,12 @@ use crate::hypervisor::gdb::{
4141#[ cfg( gdb) ]
4242use crate :: hypervisor:: gdb:: { DebugError , DebugMemoryAccessError } ;
4343use crate :: hypervisor:: regs:: {
44- CommonDebugRegs , CommonFpu , CommonRegisters , CommonSpecialRegisters ,
44+ CommonDebugRegs , CommonFpu , CommonRegisters , CommonSpecialRegisters , MsrEntry , MsrResetState ,
45+ core_reset_indices, is_mtrr_reset_index,
4546} ;
46- #[ cfg( not( gdb) ) ]
47+ #[ cfg( kvm) ]
48+ use crate :: hypervisor:: regs:: { MSR_KERNEL_GS_BASE , MSR_TSC } ;
49+ #[ cfg( any( not( gdb) , mshv3, target_os = "windows" ) ) ]
4750use crate :: hypervisor:: virtual_machine:: VirtualMachine ;
4851#[ cfg( kvm) ]
4952use crate :: hypervisor:: virtual_machine:: kvm:: KvmVm ;
@@ -69,6 +72,26 @@ use crate::sandbox::trace::MemTraceInfo;
6972#[ cfg( crashdump) ]
7073use crate :: sandbox:: uninitialized:: SandboxRuntimeConfig ;
7174
75+ #[ cfg( gdb) ]
76+ type BoxedVm = Box < dyn DebuggableVm > ;
77+ #[ cfg( not( gdb) ) ]
78+ type BoxedVm = Box < dyn VirtualMachine > ;
79+
80+ /// Determines the MTRR reset indices for an MSHV or WHP backend and validates
81+ /// its allow list. Both hosts lack an MSR filter, so the allow list adds reset
82+ /// state.
83+ #[ cfg( any( mshv3, target_os = "windows" ) ) ]
84+ fn determine_reset_msrs (
85+ vm : & dyn VirtualMachine ,
86+ allowed : & [ u32 ] ,
87+ ) -> std:: result:: Result < Vec < u32 > , VmError > {
88+ use crate :: hypervisor:: virtual_machine:: { mtrr_reset_indices, validate_allowed_msrs} ;
89+
90+ let required_mtrrs = mtrr_reset_indices ( vm) . map_err ( VmError :: CreateVm ) ?;
91+ validate_allowed_msrs ( vm, allowed) . map_err ( VmError :: CreateVm ) ?;
92+ Ok ( required_mtrrs)
93+ }
94+
7295impl HyperlightVm {
7396 /// Create a new HyperlightVm instance (will not run vm until calling `initialise`)
7497 #[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level = "Trace" ) ]
@@ -85,18 +108,27 @@ impl HyperlightVm {
85108 #[ cfg( crashdump) ] rt_cfg : SandboxRuntimeConfig ,
86109 #[ cfg( feature = "mem_profile" ) ] trace_info : MemTraceInfo ,
87110 ) -> std:: result:: Result < Self , CreateHyperlightVmError > {
88- #[ cfg( gdb) ]
89- type VmType = Box < dyn DebuggableVm > ;
90- #[ cfg( not( gdb) ) ]
91- type VmType = Box < dyn VirtualMachine > ;
92-
93- let vm: VmType = match get_available_hypervisor ( ) {
111+ let ( vm, required_mtrrs) : ( BoxedVm , Vec < u32 > ) = match get_available_hypervisor ( ) {
94112 #[ cfg( kvm) ]
95- Some ( HypervisorType :: Kvm ) => Box :: new ( KvmVm :: new ( ) . map_err ( VmError :: CreateVm ) ?) ,
113+ Some ( HypervisorType :: Kvm ) => {
114+ let kvm_vm = KvmVm :: new ( ) . map_err ( VmError :: CreateVm ) ?;
115+ kvm_vm
116+ . configure_msr_access ( config. get_allowed_msrs ( ) )
117+ . map_err ( VmError :: CreateVm ) ?;
118+ ( Box :: new ( kvm_vm) , Vec :: new ( ) )
119+ }
96120 #[ cfg( mshv3) ]
97- Some ( HypervisorType :: Mshv ) => Box :: new ( MshvVm :: new ( ) . map_err ( VmError :: CreateVm ) ?) ,
121+ Some ( HypervisorType :: Mshv ) => {
122+ let vm: BoxedVm = Box :: new ( MshvVm :: new ( ) . map_err ( VmError :: CreateVm ) ?) ;
123+ let required_mtrrs = determine_reset_msrs ( vm. as_ref ( ) , config. get_allowed_msrs ( ) ) ?;
124+ ( vm, required_mtrrs)
125+ }
98126 #[ cfg( target_os = "windows" ) ]
99- Some ( HypervisorType :: Whp ) => Box :: new ( WhpVm :: new ( ) . map_err ( VmError :: CreateVm ) ?) ,
127+ Some ( HypervisorType :: Whp ) => {
128+ let vm: BoxedVm = Box :: new ( WhpVm :: new ( ) . map_err ( VmError :: CreateVm ) ?) ;
129+ let required_mtrrs = determine_reset_msrs ( vm. as_ref ( ) , config. get_allowed_msrs ( ) ) ?;
130+ ( vm, required_mtrrs)
131+ }
100132 None => return Err ( CreateHyperlightVmError :: NoHypervisorFound ) ,
101133 } ;
102134
@@ -136,6 +168,10 @@ impl HyperlightVm {
136168 } ) ,
137169 } ) ;
138170
171+ // The MSRs reset on restore and captured in snapshots.
172+ let msr_reset =
173+ Self :: capture_msr_reset_state ( & vm, required_mtrrs, config. get_allowed_msrs ( ) ) ?;
174+
139175 let snapshot_slot = 0u32 ;
140176 let scratch_slot = 1u32 ;
141177 #[ cfg_attr( not( gdb) , allow( unused_mut) ) ]
@@ -168,6 +204,7 @@ impl HyperlightVm {
168204 trace_info,
169205 #[ cfg( crashdump) ]
170206 rt_cfg,
207+ msr_reset,
171208 } ;
172209
173210 ret. update_snapshot_mapping ( snapshot_mem) ?;
@@ -199,6 +236,50 @@ impl HyperlightVm {
199236 Ok ( ret)
200237 }
201238
239+ /// Determines this VM's MSR reset set: the MSRs reset on restore and
240+ /// captured in snapshots. `required_mtrrs` is the VCNT-driven MTRR set
241+ /// gathered at creation. `allowed` is the validated allow list.
242+ fn capture_msr_reset_state (
243+ vm : & BoxedVm ,
244+ required_mtrrs : Vec < u32 > ,
245+ allowed : & [ u32 ] ,
246+ ) -> std:: result:: Result < MsrResetState , VmError > {
247+ let core: Vec < u32 > = match get_available_hypervisor ( ) {
248+ #[ cfg( kvm) ]
249+ Some ( HypervisorType :: Kvm ) => vec ! [ MSR_KERNEL_GS_BASE , MSR_TSC ] , // GS_BASE is needed for correctness. TSC is to match mshv/whp behavior
250+ #[ cfg( mshv3) ]
251+ Some ( HypervisorType :: Mshv ) => Self :: probe_core_reset_indices ( vm) ,
252+ #[ cfg( target_os = "windows" ) ]
253+ Some ( HypervisorType :: Whp ) => Self :: probe_core_reset_indices ( vm) ,
254+ // The VM already exists, so a hypervisor was found. The `None`
255+ // arm in `new` returned before this point.
256+ None => unreachable ! ( ) ,
257+ } ;
258+ let mut indices: Vec < u32 > = core
259+ . into_iter ( )
260+ . chain ( required_mtrrs)
261+ . chain ( allowed. iter ( ) . copied ( ) )
262+ . collect ( ) ;
263+ indices. sort_unstable ( ) ;
264+ indices. dedup ( ) ;
265+ let baseline = vm. msrs ( & indices) . map_err ( VmError :: Register ) ?;
266+ let mut allowed = allowed. to_vec ( ) ;
267+ allowed. sort_unstable ( ) ;
268+ allowed. dedup ( ) ;
269+ Ok ( MsrResetState :: new ( baseline, allowed) )
270+ }
271+
272+ /// Core stateful MSRs a filterless host can read.
273+ ///
274+ /// MTRRs come from the VCNT-driven required set, so they are excluded.
275+ #[ cfg( any( mshv3, target_os = "windows" ) ) ]
276+ fn probe_core_reset_indices ( vm : & BoxedVm ) -> Vec < u32 > {
277+ core_reset_indices ( )
278+ . filter ( |i| !is_mtrr_reset_index ( * i) )
279+ . filter ( |i| vm. msrs ( & [ * i] ) . is_ok ( ) )
280+ . collect ( )
281+ }
282+
202283 /// Initialise the internally stored vCPU with the given PEB address and
203284 /// random number seed, then run it until a HLT instruction.
204285 #[ instrument( err( Debug ) , skip_all, parent = Span :: current( ) , level = "Trace" ) ]
@@ -270,6 +351,55 @@ impl HyperlightVm {
270351 Ok ( self . vm . sregs ( ) ?)
271352 }
272353
354+ /// Returns the current values of the MSR reset set.
355+ pub ( crate ) fn get_msr_reset_state ( & self ) -> Result < Vec < MsrEntry > , AccessPageTableError > {
356+ Ok ( self . vm . msrs ( & self . msr_reset . indices ( ) ) ?)
357+ }
358+
359+ /// Returns this VM's requested allow list, sorted and deduplicated.
360+ pub ( crate ) fn get_msr_allow_list ( & self ) -> Vec < u32 > {
361+ self . msr_reset . allowed ( ) . to_vec ( )
362+ }
363+
364+ /// Restores snapshot MSRs or the initialization baseline.
365+ pub ( crate ) fn restore_msrs (
366+ & mut self ,
367+ snap_msrs : Option < & Vec < MsrEntry > > ,
368+ snap_allowed : Option < & [ u32 ] > ,
369+ ) -> std:: result:: Result < ( ) , ResetVcpuError > {
370+ match snap_msrs {
371+ // A snapshot with no captured set predates MSR capture.
372+ // Reset to this VM's creation-time baseline.
373+ None => self . vm . set_msrs ( self . msr_reset . baseline ( ) ) ?,
374+ // Reset the MSRs to the snapshot's captured values. The snapshot's
375+ // allow list must be a subset of this VM's, and every captured
376+ // index must be in this reset set, so validation rejects a
377+ // mismatch first.
378+ Some ( msrs) => {
379+ let entries = self
380+ . msr_reset
381+ . validate_snapshot ( msrs, snap_allowed. unwrap_or ( & [ ] ) ) ?;
382+ self . vm . set_msrs ( & entries) ?;
383+ }
384+ }
385+ Ok ( ( ) )
386+ }
387+
388+ /// Reads arbitrary backend MSRs for tests.
389+ #[ cfg( all( test, mshv3, target_arch = "x86_64" ) ) ]
390+ pub ( crate ) fn capture_msrs_for_test (
391+ & self ,
392+ indices : & [ u32 ] ,
393+ ) -> std:: result:: Result < Vec < MsrEntry > , RegisterError > {
394+ self . vm . msrs ( indices)
395+ }
396+
397+ /// Attempts one backend MSR write for tests.
398+ #[ cfg( all( test, mshv3, target_arch = "x86_64" ) ) ]
399+ pub ( crate ) fn try_set_msr_for_test ( & self , index : u32 , value : u64 ) -> bool {
400+ self . vm . set_msrs ( & [ MsrEntry { index, value } ] ) . is_ok ( )
401+ }
402+
273403 /// Dispatch a call from the host to the guest using the given pointer
274404 /// to the dispatch function _in the guest's address space_.
275405 ///
0 commit comments