diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-31 16:01:46 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-31 16:01:46 +0200 |
| commit | ba667148af04963f5d70c647511ea1fad1add21d (patch) | |
| tree | 06f8faf9128d4d1fc48bbe90b5f50cf65a2e9022 /rsnix | |
| parent | docs: CONTEXT.md — paging adoption, heap growth, shell additions (diff) | |
| download | rs-dos-ba667148af04963f5d70c647511ea1fad1add21d.tar.gz rs-dos-ba667148af04963f5d70c647511ea1fad1add21d.zip | |
power: proper shutdown via UEFI ResetSystem (ACPI S5)
- uefi.rs: full EFI_RUNTIME_SERVICES layout with reset_system at index 10
(spec order); EfiResetSystem type + RESET_* constants
- power.rs: stash the runtime-services table pre-EBS; poweroff() calls
ResetSystem(EfiResetShutdown) — still callable after ExitBootServices
because we never call SetVirtualAddressMap, so the pointers keep
working with physical addresses; falls back to the hlt loop
- shell: 'poweroff' and 'exit' shut the machine down (QEMU exits),
'halt' keeps the plain hlt loop
Diffstat (limited to 'rsnix')
| -rw-r--r-- | rsnix/src/lib.rs | 1 | ||||
| -rw-r--r-- | rsnix/src/power.rs | 48 | ||||
| -rw-r--r-- | rsnix/src/uefi.rs | 34 |
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; | |||
| 7 | pub mod interrupts; | 7 | pub mod interrupts; |
| 8 | pub mod memory; | 8 | pub mod memory; |
| 9 | pub mod paging; | 9 | pub mod paging; |
| 10 | pub mod power; | ||
| 10 | pub mod ps2; | 11 | pub mod ps2; |
| 11 | pub mod serial; | 12 | pub mod serial; |
| 12 | pub mod uefi; | 13 | pub 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 | |||
| 12 | use crate::uefi::{RuntimeServices, EFI_SUCCESS, RESET_SHUTDOWN}; | ||
| 13 | |||
| 14 | static mut RUNTIME_SERVICES: *mut RuntimeServices = core::ptr::null_mut(); | ||
| 15 | |||
| 16 | /// Stash the runtime-services table pointer (call in Phase 1, pre-EBS). | ||
| 17 | pub fn init(rt: *mut RuntimeServices) { | ||
| 18 | unsafe { | ||
| 19 | RUNTIME_SERVICES = rt; | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | /// Halt the CPU forever (interrupts off, hlt loop). | ||
| 24 | pub 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. | ||
| 34 | pub 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. | ||
| 94 | pub type EfiResetSystem = | ||
| 95 | unsafe extern "efiapi" fn(u32, u64, usize, *const u16) -> Status; | ||
| 96 | |||
| 97 | pub const RESET_COLD: u32 = 0; | ||
| 98 | #[allow(dead_code)] | ||
| 99 | pub const RESET_WARM: u32 = 1; | ||
| 100 | pub const RESET_SHUTDOWN: u32 = 2; | ||
| 101 | #[allow(dead_code)] | ||
| 102 | pub 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)] |
| 92 | pub struct RuntimeServices { | 109 | pub 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 | // --------------------------------------------------------------------------- |
