Skip to content

Commit

Permalink
Dynamically resolve WHvMapGpaRange2 function
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
simongdavies committed Dec 17, 2024
1 parent 63ceac0 commit 9839135
Showing 1 changed file with 64 additions and 4 deletions.
68 changes: 64 additions & 4 deletions src/hyperlight_host/src/hypervisor/windows_hypervisor_platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ limitations under the License.
use core::ffi::c_void;

use tracing::{instrument, Span};
use windows::Win32::Foundation::HANDLE;
use windows::core::s;
use windows::Win32::Foundation::{FreeLibrary, HANDLE};
use windows::Win32::System::Hypervisor::*;
use windows::Win32::System::LibraryLoader::*;
use windows_result::HRESULT;

use super::wrappers::HandleWrapper;
use crate::hypervisor::wrappers::{WHvFPURegisters, WHvGeneralRegisters, WHvSpecialRegisters};
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
use crate::Result;
use crate::{new_error, Result};

// We need to pass in a primitive array of register names/values
// to WHvSetVirtualProcessorRegisters and rust needs to know array size
Expand Down Expand Up @@ -90,8 +93,18 @@ impl VMPartition {
process_handle: HandleWrapper,
) -> Result<()> {
let process_handle: HANDLE = process_handle.into();
// The function pointer to WHvMapGpaRange2 is resolved dynamically to allow us to detect
// when we are running on older versions of windows that do not support this API and
// return a more informative error message, rather than failing with an error about a missing entrypoint
let whvmapgparange2_func = unsafe {
match try_load_whv_map_gpa_range2() {
Ok(func) => func,
Err(e) => return Err(new_error!("Cant find API: {}", e)),
}
};

regions.iter().try_for_each(|region| unsafe {
WHvMapGpaRange2(
let res = whvmapgparange2_func(
self.0,
process_handle,
region.host_region.start as *const c_void,
Expand All @@ -109,12 +122,59 @@ impl VMPartition {
_ => panic!("Invalid flag"),
})
.fold(WHvMapGpaRangeFlagNone, |acc, flag| acc | flag), // collect using bitwise OR,
)
);
if res.is_err() {
return Err(new_error!("Call to WHvMapGpaRange2 failed"));
}
Ok(())
})?;
Ok(())
}
}

// This function dynamically loads the WHvMapGpaRange2 function from the winhvplatform.dll
// WHvMapGpaRange2 only available on Windows 11 or Windows Server 2022 and later
// we do things this way to allow a user trying to load hyperlight on an older version of windows to
// get an error message saying that hyperlight requires a newer version of windows, rather than just failing
// with an error about a missing entrypoint
// This function should always succeed since before we get here we have already checked that the hypervisor is present and
// that we are on a supported version of windows.
type WHvMapGpaRange2Func = unsafe extern "cdecl" fn(
WHV_PARTITION_HANDLE,
HANDLE,
*const c_void,
u64,
u64,
WHV_MAP_GPA_RANGE_FLAGS,
) -> HRESULT;

pub unsafe fn try_load_whv_map_gpa_range2() -> Result<WHvMapGpaRange2Func> {
let library = unsafe {
LoadLibraryExA(
s!("winhvplatform.dll"),
None,
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS,
)
};

if let Err(e) = library {
return Err(new_error!("{}", e));
}

let library = library.unwrap();

let address = unsafe { GetProcAddress(library, s!("WHvMapGpaRange2")) };

if address.is_none() {
unsafe { FreeLibrary(library)? };
return Err(new_error!(
"Failed to find WHvMapGpaRange2 in winhvplatform.dll"
));
}

unsafe { Ok(std::mem::transmute_copy(&address)) }
}

impl Drop for VMPartition {
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
fn drop(&mut self) {
Expand Down

0 comments on commit 9839135

Please sign in to comment.