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 fee334f1..3346f65f 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,8 +200,48 @@ 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 +// 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")] + { + 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(); + } +} + +/// 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(