@@ -17,13 +17,16 @@ limitations under the License.
1717use core:: ffi:: c_void;
1818
1919use tracing:: { instrument, Span } ;
20- use windows:: Win32 :: Foundation :: HANDLE ;
20+ use windows:: core:: s;
21+ use windows:: Win32 :: Foundation :: { FreeLibrary , HANDLE } ;
2122use windows:: Win32 :: System :: Hypervisor :: * ;
23+ use windows:: Win32 :: System :: LibraryLoader :: * ;
24+ use windows_result:: HRESULT ;
2225
2326use super :: wrappers:: HandleWrapper ;
2427use crate :: hypervisor:: wrappers:: { WHvFPURegisters , WHvGeneralRegisters , WHvSpecialRegisters } ;
2528use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags } ;
26- use crate :: Result ;
29+ use crate :: { new_error , Result } ;
2730
2831// We need to pass in a primitive array of register names/values
2932// to WHvSetVirtualProcessorRegisters and rust needs to know array size
@@ -90,8 +93,18 @@ impl VMPartition {
9093 process_handle : HandleWrapper ,
9194 ) -> Result < ( ) > {
9295 let process_handle: HANDLE = process_handle. into ( ) ;
96+ // The function pointer to WHvMapGpaRange2 is resolved dynamically to allow us to detect
97+ // when we are running on older versions of windows that do not support this API and
98+ // return a more informative error message, rather than failing with an error about a missing entrypoint
99+ let whvmapgparange2_func = unsafe {
100+ match try_load_whv_map_gpa_range2 ( ) {
101+ Ok ( func) => func,
102+ Err ( e) => return Err ( new_error ! ( "Cant find API: {}" , e) ) ,
103+ }
104+ } ;
105+
93106 regions. iter ( ) . try_for_each ( |region| unsafe {
94- WHvMapGpaRange2 (
107+ let res = whvmapgparange2_func (
95108 self . 0 ,
96109 process_handle,
97110 region. host_region . start as * const c_void ,
@@ -109,12 +122,59 @@ impl VMPartition {
109122 _ => panic ! ( "Invalid flag" ) ,
110123 } )
111124 . fold ( WHvMapGpaRangeFlagNone , |acc, flag| acc | flag) , // collect using bitwise OR,
112- )
125+ ) ;
126+ if res. is_err ( ) {
127+ return Err ( new_error ! ( "Call to WHvMapGpaRange2 failed" ) ) ;
128+ }
129+ Ok ( ( ) )
113130 } ) ?;
114131 Ok ( ( ) )
115132 }
116133}
117134
135+ // This function dynmically loads the WHvMapGpaRange2 function from the winhvplatform.dll
136+ // WHvMapGpaRange2 only available on Windows 11 or Windows Server 2022 and later
137+ // we do things this way to allow a user trying to load hyperlight on an older version of windows to
138+ // get an error message saying that hyperlight requires a newer version of windows, rather than just failing
139+ // with an error about a missing entrypoint
140+ // This function should always succeed since before we get here we have already checked that the hypervisor is present and
141+ // that we are on a supported version of windows.
142+ type WHvMapGpaRange2Func = unsafe extern "cdecl" fn (
143+ WHV_PARTITION_HANDLE ,
144+ HANDLE ,
145+ * const c_void ,
146+ u64 ,
147+ u64 ,
148+ WHV_MAP_GPA_RANGE_FLAGS ,
149+ ) -> HRESULT ;
150+
151+ pub unsafe fn try_load_whv_map_gpa_range2 ( ) -> Result < WHvMapGpaRange2Func > {
152+ let library = unsafe {
153+ LoadLibraryExA (
154+ s ! ( "winhvplatform.dll" ) ,
155+ None ,
156+ LOAD_LIBRARY_SEARCH_DEFAULT_DIRS ,
157+ )
158+ } ;
159+
160+ if let Err ( e) = library {
161+ return Err ( new_error ! ( "{}" , e) ) ;
162+ }
163+
164+ let library = library. unwrap ( ) ;
165+
166+ let address = unsafe { GetProcAddress ( library, s ! ( "WHvMapGpaRange2" ) ) } ;
167+
168+ if address. is_none ( ) {
169+ unsafe { FreeLibrary ( library) ? } ;
170+ return Err ( new_error ! (
171+ "Failed to find WHvMapGpaRange2 in winhvplatform.dll"
172+ ) ) ;
173+ }
174+
175+ unsafe { Ok ( std:: mem:: transmute_copy ( & address) ) }
176+ }
177+
118178impl Drop for VMPartition {
119179 #[ instrument( skip_all, parent = Span :: current( ) , level= "Trace" ) ]
120180 fn drop ( & mut self ) {
0 commit comments