From 03f8840c776bacd2f53a14255aee366389a1a1e6 Mon Sep 17 00:00:00 2001 From: CMGS Date: Wed, 1 Apr 2026 08:38:58 +0000 Subject: [PATCH 1/3] efi: Implement reset_system for Cloud Hypervisor The ResetSystem runtime service was intentionally left as a no-op to force Linux to use ACPI for shutdown. However, Windows calls ResetSystem(EfiResetShutdown) as its final shutdown step and does not fall back to ACPI when the call returns. This causes the Cloud Hypervisor process to hang indefinitely after Windows shuts down. Write the appropriate value to Cloud Hypervisor's shutdown I/O port (0x600) based on the reset type: - EfiResetShutdown: SLP_TYP=5 + SLP_EN (triggers VM exit) - EfiResetCold/Warm: reboot value (triggers VM reset) Linux guests are unaffected as they use ACPI directly. Fixes: cloud-hypervisor/rust-hypervisor-firmware#422 Signed-off-by: CMGS --- src/efi/runtime_services.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/efi/runtime_services.rs b/src/efi/runtime_services.rs index fee334f1..6baddf54 100644 --- a/src/efi/runtime_services.rs +++ b/src/efi/runtime_services.rs @@ -196,8 +196,32 @@ pub extern "efiapi" fn get_next_high_mono_count(_: *mut u32) -> Status { Status::DEVICE_ERROR } -pub extern "efiapi" fn reset_system(_: ResetType, _: Status, _: usize, _: *mut c_void) { - // Don't do anything to force the kernel to use ACPI for shutdown and triple-fault for reset +pub extern "efiapi" fn reset_system(_reset_type: ResetType, _: Status, _: usize, _: *mut c_void) { + #[cfg(target_arch = "x86_64")] + { + // Cloud Hypervisor's AcpiShutdownDevice is at IO port 0x600. + const SHUTDOWN_PORT: u16 = 0x600; + const S5_SLEEP_VALUE: u8 = (5 << 2) | (1 << 5); // SLP_TYP=5, SLP_EN=1 + const REBOOT_VALUE: u8 = 1; + + let value = if _reset_type == efi::RESET_SHUTDOWN { + S5_SLEEP_VALUE + } else { + REBOOT_VALUE + }; + unsafe { + core::arch::asm!("out dx, al", in("dx") SHUTDOWN_PORT, in("al") value); + } + } + + loop { + #[cfg(target_arch = "x86_64")] + unsafe { + core::arch::asm!("hlt"); + } + #[cfg(not(target_arch = "x86_64"))] + core::hint::spin_loop(); + } } pub extern "efiapi" fn update_capsule( From 042c792a07a18ab330a9ad315730f5b297d0e737 Mon Sep 17 00:00:00 2001 From: CMGS Date: Thu, 2 Apr 2026 22:30:15 +0800 Subject: [PATCH 2/3] efi: Defer reset_system activation until after ExitBootServices EFI applications like shim call ResetSystem during early boot when MOK state import fails (set_variable returns UNSUPPORTED). The previous commit made reset_system write to Cloud Hypervisor's shutdown port and enter a halt loop, which caused VMs to either shut down (on CH) or hang (on QEMU) during boot. Split reset_system into two phases: - reset_system_boot: no-op during boot services (old behavior) - reset_system: activated after ExitBootServices with CH port I/O Also fix fixup_at_virtual to remap reset_system to its virtual address instead of not_available, so ResetSystem works after SetVirtualAddressMap (needed for Windows shutdown on CH). Signed-off-by: CMGS --- src/efi/boot_services.rs | 3 +++ src/efi/runtime_services.rs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/efi/boot_services.rs b/src/efi/boot_services.rs index 26a93ec5..6c1826a0 100644 --- a/src/efi/boot_services.rs +++ b/src/efi/boot_services.rs @@ -453,6 +453,9 @@ pub extern "efiapi" fn unload_image(_: Handle) -> Status { } pub extern "efiapi" fn exit_boot_services(_: Handle, _: usize) -> Status { + unsafe { + super::runtime_services::swap_reset_system(); + } Status::SUCCESS } diff --git a/src/efi/runtime_services.rs b/src/efi/runtime_services.rs index 6baddf54..c3290374 100644 --- a/src/efi/runtime_services.rs +++ b/src/efi/runtime_services.rs @@ -38,7 +38,7 @@ pub static mut RS: SyncUnsafeCell = get_next_variable_name, set_variable, get_next_high_mono_count, - reset_system, + reset_system: reset_system_boot, update_capsule, query_capsule_capabilities, query_variable_info, @@ -62,7 +62,11 @@ unsafe fn fixup_at_virtual(descriptors: &[MemoryDescriptor]) { rs.get_variable = transmute(ptr); rs.set_variable = transmute(ptr); rs.get_next_variable_name = transmute(ptr); - rs.reset_system = transmute(ptr); + let reset_ptr = ALLOCATOR + .borrow() + .convert_internal_pointer(descriptors, (reset_system as *const ()) as u64) + .unwrap(); + rs.reset_system = transmute(reset_ptr); rs.update_capsule = transmute(ptr); rs.query_capsule_capabilities = transmute(ptr); rs.query_variable_info = transmute(ptr); @@ -196,10 +200,24 @@ pub extern "efiapi" fn get_next_high_mono_count(_: *mut u32) -> Status { Status::DEVICE_ERROR } +// No-op used during boot services phase. EFI applications like shim may call +// ResetSystem during early boot (e.g. on MOK import failure). Returning here +// lets them continue, matching the original firmware behavior. +pub extern "efiapi" fn reset_system_boot( + _: ResetType, + _: Status, + _: usize, + _: *mut c_void, +) { +} + +// Activated after ExitBootServices via swap_reset_system(). Writes to Cloud +// Hypervisor's AcpiShutdownDevice at IO port 0x600 to perform the actual +// VM shutdown or reset. On non-CH platforms the port write is a no-op and +// the function falls through to a halt loop. pub extern "efiapi" fn reset_system(_reset_type: ResetType, _: Status, _: usize, _: *mut c_void) { #[cfg(target_arch = "x86_64")] { - // Cloud Hypervisor's AcpiShutdownDevice is at IO port 0x600. const SHUTDOWN_PORT: u16 = 0x600; const S5_SLEEP_VALUE: u8 = (5 << 2) | (1 << 5); // SLP_TYP=5, SLP_EN=1 const REBOOT_VALUE: u8 = 1; @@ -224,6 +242,14 @@ pub extern "efiapi" fn reset_system(_reset_type: ResetType, _: Status, _: usize, } } +/// Switch the runtime services table from the boot-phase no-op to the real +/// reset_system implementation. Called from exit_boot_services. +pub unsafe fn swap_reset_system() { + #[allow(static_mut_refs)] + let rs = RS.get_mut(); + rs.reset_system = reset_system; +} + pub extern "efiapi" fn update_capsule( _: *mut *mut CapsuleHeader, _: usize, From 8bef5666fe17ac6aebaa98dc78f55b3e8b2320a2 Mon Sep 17 00:00:00 2001 From: CMGS Date: Sat, 11 Apr 2026 12:57:26 +0800 Subject: [PATCH 3/3] efi: Fix reset_system_boot formatting Signed-off-by: CMGS --- src/efi/runtime_services.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/efi/runtime_services.rs b/src/efi/runtime_services.rs index c3290374..3346f65f 100644 --- a/src/efi/runtime_services.rs +++ b/src/efi/runtime_services.rs @@ -203,13 +203,7 @@ pub extern "efiapi" fn get_next_high_mono_count(_: *mut u32) -> Status { // No-op used during boot services phase. EFI applications like shim may call // ResetSystem during early boot (e.g. on MOK import failure). Returning here // lets them continue, matching the original firmware behavior. -pub extern "efiapi" fn reset_system_boot( - _: ResetType, - _: Status, - _: usize, - _: *mut c_void, -) { -} +pub extern "efiapi" fn reset_system_boot(_: ResetType, _: Status, _: usize, _: *mut c_void) {} // Activated after ExitBootServices via swap_reset_system(). Writes to Cloud // Hypervisor's AcpiShutdownDevice at IO port 0x600 to perform the actual