From bee7cf0e9f0cae43d1871ac3f45e1d2ef054bfa9 Mon Sep 17 00:00:00 2001 From: Jake Correnti Date: Wed, 23 Apr 2025 16:01:51 -0400 Subject: [PATCH] hvf: Add API to verify Nested Virt is supported Add an API to check if the current system supports Nested Virt on macOS. Signed-off-by: Jake Correnti --- include/libkrun.h | 13 ++++++++++ src/hvf/src/lib.rs | 57 +++++++++++++++++++++++------------------- src/libkrun/src/lib.rs | 13 ++++++++++ 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/include/libkrun.h b/include/libkrun.h index b2c70a77e..a62a9c90e 100644 --- a/include/libkrun.h +++ b/include/libkrun.h @@ -560,6 +560,19 @@ int32_t krun_setgid(uint32_t ctx_id, gid_t gid); */ int32_t krun_set_nested_virt(uint32_t ctx_id, bool enabled); +/** + * Check the system if Nested Virtualization is supported + * + * Notes: + * This feature is only supported on macOS. + * + * Returns: + * - 1 : Success and Nested Virtualization is supported + * - 0 : Success and Nested Virtualization is not supported + * - <0: Failure + */ +int32_t krun_check_nested_virt(void); + /** * Specify whether to split IRQCHIP responsibilities between the host and the guest. * diff --git a/src/hvf/src/lib.rs b/src/hvf/src/lib.rs index fc435ead6..3088be5f8 100644 --- a/src/hvf/src/lib.rs +++ b/src/hvf/src/lib.rs @@ -9,6 +9,9 @@ #[allow(deref_nullptr)] pub mod bindings; +#[macro_use] +extern crate log; + use bindings::*; #[cfg(target_arch = "aarch64")] @@ -206,11 +209,27 @@ pub fn vcpu_set_vtimer_mask(vcpuid: u64, masked: bool) -> Result<(), Error> { } } -pub struct HvfNestedBindings { - hv_vm_config_get_el2_supported: - libloading::Symbol<'static, unsafe extern "C" fn(*mut bool) -> hv_return_t>, - hv_vm_config_set_el2_enabled: - libloading::Symbol<'static, unsafe extern "C" fn(hv_vm_config_t, bool) -> hv_return_t>, +/// Checks if Nested Virtualization is supported on the current system. Only +/// M3 or newer chips on macOS 15+ will satisfy the requirements. +pub fn check_nested_virt() -> Result { + type GetEL2Supported = + libloading::Symbol<'static, unsafe extern "C" fn(*mut bool) -> hv_return_t>; + + let get_el2_supported: Result = + unsafe { HVF.get(b"hv_vm_config_get_el2_supported") }; + if get_el2_supported.is_err() { + info!("cannot find hv_vm_config_get_el2_supported symbol"); + return Ok(false); + } + + let mut el2_supported: bool = false; + let ret = unsafe { (get_el2_supported.unwrap())(&mut el2_supported) }; + if ret != HV_SUCCESS { + error!("hv_vm_config_get_el2_supported failed: {:?}", ret); + return Err(Error::NestedCheck); + } + + Ok(el2_supported) } pub struct HvfVm {} @@ -225,30 +244,16 @@ static HVF: LazyLock = LazyLock::new(|| unsafe { impl HvfVm { pub fn new(nested_enabled: bool) -> Result { let config = unsafe { hv_vm_config_create() }; - if nested_enabled { - let bindings = unsafe { - HvfNestedBindings { - hv_vm_config_get_el2_supported: HVF - .get(b"hv_vm_config_get_el2_supported") - .map_err(Error::FindSymbol)?, - hv_vm_config_set_el2_enabled: HVF - .get(b"hv_vm_config_set_el2_enabled") - .map_err(Error::FindSymbol)?, - } + let set_el2_enabled: libloading::Symbol< + 'static, + unsafe extern "C" fn(hv_vm_config_t, bool) -> hv_return_t, + > = unsafe { + HVF.get(b"hv_vm_config_set_el2_enabled") + .map_err(Error::FindSymbol)? }; - let mut el2_supported: bool = false; - let ret = unsafe { (bindings.hv_vm_config_get_el2_supported)(&mut el2_supported) }; - if ret != HV_SUCCESS { - return Err(Error::NestedCheck); - } - - if !el2_supported { - return Err(Error::NestedCheck); - } - - let ret = unsafe { (bindings.hv_vm_config_set_el2_enabled)(config, true) }; + let ret = unsafe { (set_el2_enabled)(config, true) }; if ret != HV_SUCCESS { return Err(Error::EnableEL2); } diff --git a/src/libkrun/src/lib.rs b/src/libkrun/src/lib.rs index 0a0bce7c6..b49df9b4f 100644 --- a/src/libkrun/src/lib.rs +++ b/src/libkrun/src/lib.rs @@ -1075,6 +1075,19 @@ pub unsafe extern "C" fn krun_set_nested_virt(ctx_id: u32, enabled: bool) -> i32 } } +#[allow(clippy::missing_safety_doc)] +#[no_mangle] +pub unsafe extern "C" fn krun_check_nested_virt() -> i32 { + #[cfg(target_os = "macos")] + match hvf::check_nested_virt() { + Ok(supp) => supp as i32, + Err(_) => -libc::EINVAL, + } + + #[cfg(not(target_os = "macos"))] + -libc::EOPNOTSUPP +} + #[allow(clippy::missing_safety_doc)] #[no_mangle] pub extern "C" fn krun_split_irqchip(ctx_id: u32, enable: bool) -> i32 {