Skip to content

x86_64: lock IA32_FEATURE_CONTROL with VMXON enabled at boot#1

Open
tonicmuroq wants to merge 1 commit into
mainfrom
fix/feature-control-vmx
Open

x86_64: lock IA32_FEATURE_CONTROL with VMXON enabled at boot#1
tonicmuroq wants to merge 1 commit into
mainfrom
fix/feature-control-vmx

Conversation

@tonicmuroq

@tonicmuroq tonicmuroq commented Jun 17, 2026

Copy link
Copy Markdown

Problem

Windows guests under Cloud Hypervisor cannot run Hyper-V / WSL2 (and therefore Docker Desktop), failing with Virtualization support not detected. Inside the guest:

  • VMMonitorModeExtensions = True, SecondLevelAddressTranslation = True (VMX + EPT are exposed in CPUID)
  • but VirtualizationFirmwareEnabled = False
  • and on enabling Hyper-V, the System log shows Microsoft-Windows-Hyper-V-Hypervisor Event 41: "Hypervisor launch failed; Either VMX not present or not enabled in BIOS."

Root cause

IA32_FEATURE_CONTROL (MSR 0x3A) is owned by firmware. Real BIOS/UEFI locks it at boot with the VMXON-outside-SMX bit set; a guest hypervisor reads it and refuses VMXON if the lock bit is clear. rust-hypervisor-firmware never wrote this MSR, so even though the VMX feature bit is present in CPUID, the guest concludes virtualization is "not enabled in firmware" and nested virt is unusable.

Fix

In rust64_start (x86_64), after paging setup and before handing off to the OS, do what real firmware does: when CPUID reports VMX, lock IA32_FEATURE_CONTROL with VMXON-outside-SMX enabled (0x5).

  • Idempotent: skipped if the lock bit is already set.
  • Safe on AMD / nested-off: CPUID.1:ECX[5] (VMX) is Intel-specific, so has_vmx is false and the function is a no-op — AMD and non-nested guests are untouched.

Test

Built (x86_64-unknown-none, pinned nightly) and deployed as CLOUDHV.fd to a Cloud Hypervisor host, fresh cold-boot of a Windows 11 guest with the stock cloud-hypervisor binary:

  • VirtualizationFirmwareEnabled flips False → True
  • the cold boot itself is unaffected (no regression to the boot path)

⚠️ Open issue — end-to-end nested virt is NOT yet confirmed (likely still broken)

This change fixes the firmware-reported state, but it does not yet yield a working nested hypervisor, and may make the failure mode worse.

After the firmware flips VirtualizationFirmwareEnabled to True, I enabled VirtualMachinePlatform + Microsoft-Hyper-V-All and set hypervisorlaunchtype auto, then rebooted the guest. The guest did not come back:

  • cloud-hypervisor process stays at ~130–150% CPU (spinning)
  • the COW overlay disk is frozen — zero writes for 13+ minutes after the reboot was issued
  • cocoon-agent never reconnected

This is the same hang signature as a guest stuck in early boot. The likely reading: with the firmware flag now set, Windows' hvix64 gets past the firmware check (no more Event 41) and proceeds to the actual VMXON, then hangs there — i.e. the underlying nested VMX does not actually function in this host (this is a doubly-nested case: GCE nested-virt L0→L1, Cloud Hypervisor L1→L2 Windows, and Windows Hyper-V would need L2→L3). So this MSR fix appears necessary but not sufficient, and it converts a graceful "not enabled in firmware" failure into a hard hang.

Do not treat this as enabling Docker/WSL2 on Windows guests until the post-VMXON hang is understood. Verification of whether L2→L3 nesting is even supported on the host should land before relying on this.

Targets origin (cocoonstack), not upstream.

Guest hypervisors (Windows Hyper-V, WSL2) check IA32_FEATURE_CONTROL at boot and refuse VMXON unless firmware has set the lock bit with VMXON-outside-SMX enabled — otherwise they report "virtualization not enabled in firmware". rust-hypervisor-firmware never touched this MSR, so a Windows guest saw VMX in CPUID (VMMonitorModeExtensions=True) but VirtualizationFirmwareEnabled=False, and Hyper-V launch failed with Event 41 "Either VMX not present or not enabled in BIOS". Nested virtualization was therefore unusable.

Set the MSR the way real firmware does: when CPUID reports VMX, lock IA32_FEATURE_CONTROL with VMXON-outside-SMX enabled, before handing control to the OS. Skipped when the lock bit is already set (idempotent) and when VMX is absent (AMD / nested off), so AMD and non-nested guests are unaffected.

Verified on a Cloud Hypervisor Windows 11 guest: VirtualizationFirmwareEnabled flips False to True and the guest boots normally.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant