From 8d9f9e552f0cb2f92df5abf174c8bdbb7b44d0d1 Mon Sep 17 00:00:00 2001 From: vasilito Date: Wed, 1 Jul 2026 07:10:28 +0300 Subject: [PATCH] kernel: s2idle MWAIT wake signal (Phase I.5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase I.5: complete the acpid <-> kernel s2idle wire. After MWAIT returns from an interrupt (typically an SCI from acpid), the kernel now: 1. Clears S2IDLE_REQUESTED (via s2idle_request_clear) 2. Sets KSTOP_FLAG and triggers EVENT_READ on the kstop handle (via s2idle_signal_wake) This is the kernel-side analog of Linux 7.1 `acpi_s2idle_wake` in `drivers/acpi/sleep.c:758`. The existing irq_trigger in generic_irq has already routed the SCI to acpid's listener (which opened /scheme/irq/{sci} earlier in the boot sequence), so the AML interpretation is done by acpid asynchronously. The s2idle flow now: 1. acpid: enter_s2idle() (\_TTS(0), \_PTS(0), \_SST(3)) 2. acpid: write 's2idle\n' to /scheme/sys/kstop -> kernel sets S2IDLE_REQUESTED, returns 3. Kernel idle path: mwait_loop() at deepest C-state 4. SCI breaks MWAIT (any interrupt, not just SCI) 5. Kernel mwait_loop post-handler (this commit): - s2idle_request_clear() - s2idle_signal_wake() -> KSTOP_FLAG set, EVENT_READ 6. acpid main loop: wakes from kstop handle read 7. acpid: exit_s2idle() (\_SST(2), \_WAK(0), \_SST(1)) The KSTOP_FLAG set in step 5 also serves as a 'reason' indicator — acpid's CheckShutdown verb (kcall 2) returns the flag, so acpid can distinguish a kstop-shutdown event from a kstop-s2idle-wake event by polling CheckShutdown after waking. Hardware-agnostic: the same flow works for any platform with Modern Standby firmware (Dell, HP, Lenovo, LG Gram, etc.). The s2idle is the universal mechanism for low-power idle; only the wake source (SCI, GPIO, RTC, ...) varies per OEM. --- src/arch/x86_shared/interrupt/mod.rs | 13 +++++++++++++ src/scheme/acpi.rs | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/arch/x86_shared/interrupt/mod.rs b/src/arch/x86_shared/interrupt/mod.rs index 794d5354b6..ad1ba0367f 100644 --- a/src/arch/x86_shared/interrupt/mod.rs +++ b/src/arch/x86_shared/interrupt/mod.rs @@ -107,6 +107,19 @@ pub unsafe fn mwait_loop(eax_hint: u32, ecx_hint: u32) { ); #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] let _ = (eax_hint, ecx_hint); + + // MWAIT returns when an interrupt fires (ecx=0 means + // "break on any interrupt"). The interrupt dispatcher + // has already run and returned by the time we get here. + // If the CPU was in s2idle (S2IDLE_REQUESTED was set by + // the kstop handler), we now know an SCI or other wake + // event has occurred. Clear the s2idle flag and trigger + // the kstop handle's EVENT_READ so acpid's main loop + // wakes up and runs the _WAK AML sequence. + if crate::scheme::acpi::s2idle_requested() { + crate::scheme::acpi::s2idle_request_clear(); + crate::scheme::acpi::s2idle_signal_wake(); + } } /// Probe MWAIT support and enter the deepest available C-state diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index 4ca307e818..66cede11eb 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -73,6 +73,28 @@ pub fn s2idle_requested() -> bool { S2IDLE_REQUESTED.load(Ordering::Acquire) } +/// Phase I: signal acpid that s2idle MWAIT was broken by an +/// interrupt. Called from `mwait_loop` after MWAIT returns. +/// Triggers the kstop handle's EVENT_READ so acpid's main loop +/// wakes and runs the \_SST(2) → \_WAK(0) → \_SST(1) AML +/// sequence on resume. +/// +/// Mirrors Linux 7.1 `acpi_s2idle_wake` in +/// `drivers/acpi/sleep.c:758` — the kernel clears +/// s2idle_state and signals the userspace ACPI driver. +pub fn s2idle_signal_wake() { + let mut token = CleanLockToken::new(); + *KSTOP_FLAG.lock(token.token()) = true; + if EXISTS_KSTOP_HANDLE.load(Ordering::Relaxed) { + crate::event::trigger( + GlobalSchemes::Acpi.scheme_id(), + HandleBits::KSTOP_HANDLE.bits(), + EventFlags::EVENT_READ, + &mut token, + ); + } +} + pub fn register_kstop(token: &mut CleanLockToken) -> bool { *KSTOP_FLAG.lock(token.token()) = true;