// --------------------------------------------------------------------------- // Power — halt loop and ACPI shutdown via the UEFI ResetSystem runtime // service. // // The firmware's runtime-services table is stashed before ExitBootServices // (`init`). Because we never call SetVirtualAddressMap, the service // pointers keep working with physical (identity) addresses after EBS, so // poweroff() can ask the firmware to shut the machine down (ACPI S5) — on // QEMU this makes the VM exit, on real hardware the power drops. // --------------------------------------------------------------------------- use crate::uefi::{EFI_SUCCESS, RESET_SHUTDOWN, RuntimeServices}; static mut RUNTIME_SERVICES: *mut RuntimeServices = core::ptr::null_mut(); /// Stash the runtime-services table pointer (call in Phase 1, pre-EBS). pub fn init(rt: *mut RuntimeServices) { unsafe { RUNTIME_SERVICES = rt; } } /// Halt the CPU forever (interrupts off, hlt loop). pub fn halt() -> ! { loop { unsafe { core::arch::asm!("cli; hlt", options(nomem, nostack)); } } } /// Shut the machine down through `ResetSystem(EfiResetShutdown)`. Falls /// back to `halt()` if no table was saved or the call failed. pub fn poweroff() -> ! { unsafe { let rt = RUNTIME_SERVICES; if rt.is_null() { halt(); } let reset = (*rt).reset_system; if reset as usize == 0 { halt(); } (reset)(RESET_SHUTDOWN, EFI_SUCCESS, 0, core::ptr::null()); // ResetSystem only returns on failure. halt() } }