Skip to content

Commit afbd1af

Browse files
committed
Dynamically resolve WHvMapGpaRange2 function
Resolve the WHvMapGpaRange2 function dynamically so that we can load on earlier versions of windows that are not supported Signed-off-by: Simon Davies <[email protected]>
1 parent 4732e71 commit afbd1af

File tree

1 file changed

+64
-4
lines changed

1 file changed

+64
-4
lines changed

src/hyperlight_host/src/hypervisor/windows_hypervisor_platform.rs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ limitations under the License.
1717
use core::ffi::c_void;
1818

1919
use tracing::{instrument, Span};
20-
use windows::Win32::Foundation::HANDLE;
20+
use windows::core::s;
21+
use windows::Win32::Foundation::{FreeLibrary, HANDLE};
2122
use windows::Win32::System::Hypervisor::*;
23+
use windows::Win32::System::LibraryLoader::*;
24+
use windows_result::HRESULT;
2225

2326
use super::wrappers::HandleWrapper;
2427
use crate::hypervisor::wrappers::{WHvFPURegisters, WHvGeneralRegisters, WHvSpecialRegisters};
2528
use 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+
118178
impl Drop for VMPartition {
119179
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
120180
fn drop(&mut self) {

0 commit comments

Comments
 (0)