summaryrefslogtreecommitdiffstats
path: root/rsnix/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--rsnix/src/lib.rs1
-rw-r--r--rsnix/src/power.rs48
-rw-r--r--rsnix/src/uefi.rs34
3 files changed, 81 insertions, 2 deletions
diff --git a/rsnix/src/lib.rs b/rsnix/src/lib.rs
index d2dacf0..8c69b35 100644
--- a/rsnix/src/lib.rs
+++ b/rsnix/src/lib.rs
@@ -7,6 +7,7 @@ pub mod gop;
7pub mod interrupts; 7pub mod interrupts;
8pub mod memory; 8pub mod memory;
9pub mod paging; 9pub mod paging;
10pub mod power;
10pub mod ps2; 11pub mod ps2;
11pub mod serial; 12pub mod serial;
12pub mod uefi; 13pub mod uefi;
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}
diff --git a/rsnix/src/uefi.rs b/rsnix/src/uefi.rs
index 619739e..b1b5e48 100644
--- a/rsnix/src/uefi.rs
+++ b/rsnix/src/uefi.rs
@@ -86,12 +86,42 @@ pub struct SimpleTextInput {
86} 86}
87 87
88// --------------------------------------------------------------------------- 88// ---------------------------------------------------------------------------
89// Runtime services (stub — we don't use them) 89// Runtime services
90// --------------------------------------------------------------------------- 90// ---------------------------------------------------------------------------
91
92/// `EFI_RESET_SYSTEM` — ResetType, ResetStatus, DataSize, ResetData.
93/// ResetType: 0 cold, 1 warm, 2 shutdown, 3 platform-specific.
94pub type EfiResetSystem =
95 unsafe extern "efiapi" fn(u32, u64, usize, *const u16) -> Status;
96
97pub const RESET_COLD: u32 = 0;
98#[allow(dead_code)]
99pub const RESET_WARM: u32 = 1;
100pub const RESET_SHUTDOWN: u32 = 2;
101#[allow(dead_code)]
102pub const RESET_PLATFORM_SPECIFIC: u32 = 3;
103
104/// EFI_RUNTIME_SERVICES — function pointers in spec order after the header.
105/// We only use ResetSystem; the rest stay opaque. The table remains
106/// callable after ExitBootServices because we never call SetVirtualAddressMap,
107/// so the pointers keep working with physical (identity) addresses.
91#[repr(C)] 108#[repr(C)]
92pub struct RuntimeServices { 109pub struct RuntimeServices {
93 _hdr: TableHeader, 110 _hdr: TableHeader,
94 _reserved: [*mut c_void; 30], 111 _get_time: *mut c_void, // 0
112 _set_time: *mut c_void, // 1
113 _get_wakeup_time: *mut c_void, // 2
114 _set_wakeup_time: *mut c_void, // 3
115 _set_virtual_address_map: *mut c_void, // 4
116 _convert_pointer: *mut c_void, // 5
117 _get_variable: *mut c_void, // 6
118 _get_next_variable_name: *mut c_void, // 7
119 _set_variable: *mut c_void, // 8
120 _get_next_high_monotonic_count: *mut c_void, // 9
121 pub reset_system: EfiResetSystem, // 10
122 _update_capsule: *mut c_void, // 11
123 _query_capsule_capabilities: *mut c_void, // 12
124 _query_variable_info: *mut c_void, // 13
95} 125}
96 126
97// --------------------------------------------------------------------------- 127// ---------------------------------------------------------------------------