Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check that windows version is supported #110

Merged
merged 5 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ exclude = [
[workspace.package]
version = "0.1.0"
edition = "2021"
rust-version = "1.79.0"
rust-version = "1.80.0"
license = "Apache-2.0"
homepage = "https://github.com/hyperlight-dev/hyperlight"
repository = "https://github.com/hyperlight-dev/hyperlight"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ the [./src/tests/rust_guests](./src/tests/rust_guests) directory for Rust guests
You can run Hyperlight on:

- [Linux with KVM][kvm].
- [Windows with Windows Hypervisor Platform (WHP)][whp].
- Windows Subsystem for Linux 2 ([WSL2][wsl2]) with KVM.
- [Windows with Windows Hypervisor Platform (WHP).][whp] - Note that you need Windows 11 / Windows Server 2022 or later to use hyperlight, if you are running on earlier versions of Windows then you should consider using our devcontainer on [GitHub codepsaces]((https://codespaces.new/hyperlight-dev/hyperlight)) or WSL2.
- Windows Subsystem for Linux 2 (see instructions [here](https://learn.microsoft.com/en-us/windows/wsl/install) for Windows client and [here](https://learn.microsoft.com/en-us/windows/wsl/install-on-server) for Windows Server) with KVM.
- Azure Linux with mshv (note that you need mshv to be installed to use Hyperlight)

After having an environment with a hypervisor setup, running the example has the following pre-requisites:
Expand Down
1 change: 1 addition & 0 deletions src/hyperlight_host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ windows-sys = { version = "0.59", features = ["Win32"] }
windows-result = "0.2"
rust-embed = { version = "8.3.0", features = ["debug-embed", "include-exclude", "interpolate-folder-path"] }
sha256 = "1.4.0"
windows-version = "0.1"

[target.'cfg(unix)'.dependencies]
seccompiler = { version = "0.4.0", optional = true }
Expand Down
3 changes: 0 additions & 3 deletions src/hyperlight_host/src/hypervisor/hyperv_linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,6 @@ impl HypervLinuxDriver {
rsp_ptr: GuestPtr,
pml4_ptr: GuestPtr,
) -> Result<Self> {
if !is_hypervisor_present() {
log_then_return!("Hyper-V is not present on this system");
}
let mshv = Mshv::new()?;
let pr = Default::default();
let vm_fd = mshv.create_vm_with_config(&pr)?;
Expand Down
11 changes: 3 additions & 8 deletions src/hyperlight_host/src/hypervisor/hyperv_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,15 @@ use super::surrogate_process_manager::*;
use super::windows_hypervisor_platform::{VMPartition, VMProcessor};
use super::wrappers::WHvFPURegisters;
use super::{
windows_hypervisor_platform as whp, HyperlightExit, Hypervisor, VirtualCPU, CR0_AM, CR0_ET,
CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP, CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA,
EFER_LME, EFER_NX, EFER_SCE,
HyperlightExit, Hypervisor, VirtualCPU, CR0_AM, CR0_ET, CR0_MP, CR0_NE, CR0_PE, CR0_PG, CR0_WP,
CR4_OSFXSR, CR4_OSXMMEXCPT, CR4_PAE, EFER_LMA, EFER_LME, EFER_NX, EFER_SCE,
};
use crate::hypervisor::fpu::FP_CONTROL_WORD_DEFAULT;
use crate::hypervisor::hypervisor_handler::HypervisorHandler;
use crate::hypervisor::wrappers::WHvGeneralRegisters;
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
use crate::mem::ptr::{GuestPtr, RawPtr};
use crate::HyperlightError::{NoHypervisorFound, WindowsAPIError};
use crate::HyperlightError::WindowsAPIError;
use crate::{debug, log_then_return, new_error, Result};

/// A Hypervisor driver for HyperV-on-Windows.
Expand Down Expand Up @@ -75,10 +74,6 @@ impl HypervWindowsDriver {
entrypoint: u64,
rsp: u64,
) -> Result<Self> {
if !whp::is_hypervisor_present() {
log_then_return!(NoHypervisorFound());
}

// create and setup hypervisor partition
let mut partition = VMPartition::new(1)?;

Expand Down
24 changes: 23 additions & 1 deletion src/hyperlight_host/src/hypervisor/hypervisor_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,29 @@ impl HypervisorHandler {
HandlerMsg::Error(e) => Err(e),
HandlerMsg::FinishedHypervisorHandlerAction => Ok(()),
},
Err(_) => Err(HyperlightError::HypervisorHandlerMessageReceiveTimedout()),
Err(_) => {
// If we have timed out it may be that the handler thread returned an error before it sent a message, so rather than just timeout here
// we will try and get the join handle for the thread and if it has finished check to see if it returned an error
// if it did then we will return that error, otherwise we will return the timeout error
// we need to take ownership of the handle to join it
match self
.execution_variables
.join_handle
.try_lock()
.map_err(|_| HyperlightError::HypervisorHandlerMessageReceiveTimedout())?
.take_if(|handle| handle.is_finished())
{
Some(handle) => {
// If the thread has finished, we try to join it and return the error if it has one
let res = handle.join();
if res.as_ref().is_ok_and(|inner_res| inner_res.is_err()) {
return Err(res.unwrap().unwrap_err());
}
Err(HyperlightError::HypervisorHandlerMessageReceiveTimedout())
}
None => Err(HyperlightError::HypervisorHandlerMessageReceiveTimedout()),
}
}
}
}

Expand Down
3 changes: 0 additions & 3 deletions src/hyperlight_host/src/hypervisor/kvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,6 @@ impl KVMDriver {
entrypoint: u64,
rsp: u64,
) -> Result<Self> {
if !is_hypervisor_present() {
log_then_return!("KVM is not present");
};
let kvm = Kvm::new()?;

let vm_fd = kvm.create_vm_with_type(0)?;
Expand Down
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
28 changes: 28 additions & 0 deletions src/hyperlight_host/src/sandbox/uninitialized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ impl UninitializedSandbox {
) -> Result<Self> {
log_build_details();

// hyperlight is only supported on Windows 11 and Windows Server 2022 and later
#[cfg(target_os = "windows")]
check_windows_version()?;

// If the guest binary is a file make sure it exists
let guest_binary = match guest_binary {
GuestBinary::FilePath(binary_path) => {
Expand Down Expand Up @@ -303,6 +307,30 @@ impl UninitializedSandbox {
}
}
}
// Check to see if the current version of Windows is supported
// Hyperlight is only supported on Windows 11 and Windows Server 2022 and later
#[cfg(target_os = "windows")]
fn check_windows_version() -> Result<()> {
use windows_version::{is_server, OsVersion};
const WINDOWS_MAJOR: u32 = 10;
const WINDOWS_MINOR: u32 = 0;
const WINDOWS_PACK: u32 = 0;

// Windows Server 2022 has version numbers 10.0.20348 or greater
if is_server() {
if OsVersion::current() < OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 20348)
{
return Err(new_error!(
"Hyperlight Requires Windows Server 2022 or newer"
));
}
} else if OsVersion::current()
< OsVersion::new(WINDOWS_MAJOR, WINDOWS_MINOR, WINDOWS_PACK, 22000)
{
return Err(new_error!("Hyperlight Requires Windows 11 or newer"));
}
Ok(())
}

#[cfg(test)]
mod tests {
Expand Down
Loading