summaryrefslogtreecommitdiffstats
path: root/rsnix/src/power.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src/power.rs')
-rw-r--r--rsnix/src/power.rs48
1 files changed, 48 insertions, 0 deletions
diff --git a/rsnix/src/power.rs b/rsnix/src/power.rs
new file mode 100644
index 0000000..cb5d296
--- /dev/null
+++ b/rsnix/src/power.rs
@@ -0,0 +1,48 @@
1// ---------------------------------------------------------------------------
2// Power — halt loop and ACPI shutdown via the UEFI ResetSystem runtime
3// service.
4//
5// The firmware's runtime-services table is stashed before ExitBootServices
6// (`init`). Because we never call SetVirtualAddressMap, the service
7// pointers keep working with physical (identity) addresses after EBS, so
8// poweroff() can ask the firmware to shut the machine down (ACPI S5) — on
9// QEMU this makes the VM exit, on real hardware the power drops.
10// ---------------------------------------------------------------------------
11
12use crate::uefi::{RuntimeServices, EFI_SUCCESS, RESET_SHUTDOWN};
13
14static mut RUNTIME_SERVICES: *mut RuntimeServices = core::ptr::null_mut();
15
16/// Stash the runtime-services table pointer (call in Phase 1, pre-EBS).
17pub fn init(rt: *mut RuntimeServices) {
18 unsafe {
19 RUNTIME_SERVICES = rt;
20 }
21}
22
23/// Halt the CPU forever (interrupts off, hlt loop).
24pub fn halt() -> ! {
25 loop {
26 unsafe {
27 core::arch::asm!("cli; hlt", options(nomem, nostack));
28 }
29 }
30}
31
32/// Shut the machine down through `ResetSystem(EfiResetShutdown)`. Falls
33/// back to `halt()` if no table was saved or the call failed.
34pub fn poweroff() -> ! {
35 unsafe {
36 let rt = RUNTIME_SERVICES;
37 if rt.is_null() {
38 halt();
39 }
40 let reset = (*rt).reset_system;
41 if reset as usize == 0 {
42 halt();
43 }
44 (reset)(RESET_SHUTDOWN, EFI_SUCCESS, 0, core::ptr::null());
45 // ResetSystem only returns on failure.
46 halt()
47 }
48}