diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 0d2d5788..8a227d78 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -7,3 +7,4 @@ pub mod gdt; pub mod layout; pub mod paging; pub mod sse; +pub mod vmx; diff --git a/src/arch/x86_64/vmx.rs b/src/arch/x86_64/vmx.rs new file mode 100644 index 00000000..56ff9b9b --- /dev/null +++ b/src/arch/x86_64/vmx.rs @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 + +use core::arch::x86_64::__cpuid; +use x86_64::registers::model_specific::Msr; + +const IA32_FEATURE_CONTROL: u32 = 0x3a; +const FEATURE_CONTROL_LOCKED: u64 = 1 << 0; +const FEATURE_CONTROL_VMXON_OUTSIDE_SMX: u64 = 1 << 2; +const CPUID_1_ECX_VMX: u32 = 1 << 5; + +// A guest hypervisor (Hyper-V / WSL2) refuses VMXON unless firmware locked IA32_FEATURE_CONTROL with VMXON enabled; real BIOS does this at boot, so we must too or nested virt is unusable despite VMX in CPUID. +pub fn enable_feature_control() { + let has_vmx = unsafe { __cpuid(1) }.ecx & CPUID_1_ECX_VMX != 0; + if !has_vmx { + return; + } + let mut msr = Msr::new(IA32_FEATURE_CONTROL); + let current = unsafe { msr.read() }; + if current & FEATURE_CONTROL_LOCKED == 0 { + unsafe { msr.write(current | FEATURE_CONTROL_VMXON_OUTSIDE_SMX | FEATURE_CONTROL_LOCKED) }; + } +} diff --git a/src/main.rs b/src/main.rs index 5928863d..8a6ee198 100644 --- a/src/main.rs +++ b/src/main.rs @@ -185,6 +185,7 @@ pub extern "C" fn rust64_start(#[cfg(not(feature = "coreboot"))] pvh_info: &pvh: arch::x86_64::sse::enable_sse(); arch::x86_64::paging::setup(); + arch::x86_64::vmx::enable_feature_control(); #[cfg(feature = "coreboot")] let info = &coreboot::StartInfo::default();