1+ use tracing:: { Span , instrument} ;
2+
13use crate :: Result ;
24use crate :: hypervisor:: regs:: { CommonFpu , CommonRegisters , CommonSpecialRegisters } ;
35use crate :: mem:: memory_region:: MemoryRegion ;
@@ -12,6 +14,77 @@ pub(crate) mod mshv;
1214#[ cfg( target_os = "windows" ) ]
1315pub ( crate ) mod whp;
1416
17+ use std:: sync:: OnceLock ;
18+
19+ static AVAILABLE_HYPERVISOR : OnceLock < Option < HypervisorType > > = OnceLock :: new ( ) ;
20+
21+ /// Returns which type of hypervisor is available, if any
22+ pub fn get_available_hypervisor ( ) -> & ' static Option < HypervisorType > {
23+ AVAILABLE_HYPERVISOR . get_or_init ( || {
24+ cfg_if:: cfg_if! {
25+ if #[ cfg( all( kvm, mshv3) ) ] {
26+ // If both features are enabled, we need to determine hypervisor at runtime.
27+ // Currently /dev/kvm and /dev/mshv cannot exist on the same machine, so the first one
28+ // that works is guaranteed to be correct.
29+ if mshv:: is_hypervisor_present( ) {
30+ Some ( HypervisorType :: Mshv )
31+ } else if kvm:: is_hypervisor_present( ) {
32+ Some ( HypervisorType :: Kvm )
33+ } else {
34+ None
35+ }
36+ } else if #[ cfg( kvm) ] {
37+ if kvm:: is_hypervisor_present( ) {
38+ Some ( HypervisorType :: Kvm )
39+ } else {
40+ None
41+ }
42+ } else if #[ cfg( mshv3) ] {
43+ if mshv:: is_hypervisor_present( ) {
44+ Some ( HypervisorType :: Mshv )
45+ } else {
46+ None
47+ }
48+ } else if #[ cfg( target_os = "windows" ) ] {
49+ if whp:: is_hypervisor_present( ) {
50+ Some ( HypervisorType :: Whp )
51+ } else {
52+ None
53+ }
54+ } else {
55+ None
56+ }
57+ }
58+ } )
59+ }
60+
61+ /// Returns `true` if a suitable hypervisor is available.
62+ /// If this returns `false`, no hypervisor-backed sandboxes can be created.
63+ #[ instrument( skip_all, parent = Span :: current( ) ) ]
64+ pub fn is_hypervisor_present ( ) -> bool {
65+ get_available_hypervisor ( ) . is_some ( )
66+ }
67+
68+ /// The hypervisor types available for the current platform
69+ #[ derive( PartialEq , Eq , Debug ) ]
70+ pub ( crate ) enum HypervisorType {
71+ #[ cfg( kvm) ]
72+ Kvm ,
73+
74+ #[ cfg( mshv3) ]
75+ Mshv ,
76+
77+ #[ cfg( target_os = "windows" ) ]
78+ Whp ,
79+ }
80+
81+ // Compiler error if no hypervisor type is available
82+ #[ cfg( not( any( kvm, mshv3, target_os = "windows" ) ) ) ]
83+ compile_error ! (
84+ "No hypervisor type is available for the current platform. Please enable either the `kvm` or `mshv3` cargo feature."
85+ ) ;
86+
87+ /// The various reasons a VM's vCPU can exit
1588pub ( crate ) enum VmExit {
1689 /// The vCPU has exited due to a debug event (usually breakpoint)
1790 #[ cfg( gdb) ]
@@ -88,3 +161,26 @@ pub(crate) trait Vm: Debug + Send {
88161 #[ cfg( target_os = "windows" ) ]
89162 fn complete_initial_memory_setup ( & mut self ) ;
90163}
164+
165+ #[ cfg( test) ]
166+ mod tests {
167+
168+ #[ test]
169+ // TODO: add support for testing on WHP
170+ #[ cfg( target_os = "linux" ) ]
171+ fn is_hypervisor_present ( ) {
172+ use std:: path:: Path ;
173+
174+ cfg_if:: cfg_if! {
175+ if #[ cfg( all( kvm, mshv3) ) ] {
176+ assert_eq!( Path :: new( "/dev/kvm" ) . exists( ) || Path :: new( "/dev/mshv" ) . exists( ) , super :: is_hypervisor_present( ) ) ;
177+ } else if #[ cfg( kvm) ] {
178+ assert_eq!( Path :: new( "/dev/kvm" ) . exists( ) , super :: is_hypervisor_present( ) ) ;
179+ } else if #[ cfg( mshv3) ] {
180+ assert_eq!( Path :: new( "/dev/mshv" ) . exists( ) , super :: is_hypervisor_present( ) ) ;
181+ } else {
182+ assert!( !super :: is_hypervisor_present( ) ) ;
183+ }
184+ }
185+ }
186+ }
0 commit comments