diff --git a/src/scheme/acpi.rs b/src/scheme/acpi.rs index 66cede11eb..fd95d4f89a 100644 --- a/src/scheme/acpi.rs +++ b/src/scheme/acpi.rs @@ -37,9 +37,44 @@ bitflags! { static RXSDT_DATA: Once> = Once::new(); -static KSTOP_FLAG: Mutex = Mutex::new(false); +/// Phase I: kstop reason codes. Read via the CheckShutdown +/// AcpiVerb (kcall 2). The reason tells acpid what AML +/// sequence to run. +/// +/// | Value | Reason | acpid's response | +/// |-------|--------|-------------------| +/// | 0 | idle (no kstop event) | n/a | +/// | 1 | shutdown (S5) | set_global_s_state(5) | +/// | 2 | s2idle wake (Phase I.5) | exit_s2idle() (\_SST(2)→\_WAK(0)→\_SST(1)) | +/// | 3 | s3 wake (Phase II) | wake S3 path | +static KSTOP_FLAG: Mutex = Mutex::new(0); static EXISTS_KSTOP_HANDLE: AtomicBool = AtomicBool::new(false); +/// Phase I.5: set the kstop reason. Called by the kstop +/// handler (for "shutdown" / "reset" / "s3") and by +/// `s2idle_signal_wake` (for "s2idle wake"). +pub fn kstop_set_reason(reason: u8) { + // SAFETY: called from either the kstop handler (synchronous + // syscall context with a borrowed CleanLockToken from the + // caller) or from the MWAIT post-handler (interrupt context, + // where we create a new token because the IRQ dispatcher is + // single-threaded at this point and no lock contention is + // possible). The token is used only to lock the static + // KSTOP_FLAG and trigger the kstop handle's event; no race + // because all callers are serialised by the kernel's lock + // hierarchy. + let mut token = unsafe { CleanLockToken::new() }; + *KSTOP_FLAG.lock(token.token()) = reason; + if EXISTS_KSTOP_HANDLE.load(Ordering::Relaxed) { + crate::event::trigger( + GlobalSchemes::Acpi.scheme_id(), + HandleBits::KSTOP_HANDLE.bits(), + EventFlags::EVENT_READ, + &mut token, + ); + } +} + /// Phase I: s2idle (Modern Standby / S0ix) coordination flag. /// Set by `s2idle_request_set` (called from the kstop handler /// when acpid writes "s2idle" to /scheme/sys/kstop). Read by @@ -83,20 +118,11 @@ pub fn s2idle_requested() -> bool { /// `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, - ); - } + kstop_set_reason(2); // Phase I.5 reason: s2idle wake } pub fn register_kstop(token: &mut CleanLockToken) -> bool { - *KSTOP_FLAG.lock(token.token()) = true; + *KSTOP_FLAG.lock(token.token()) = 1; // reason: shutdown (S5) if !EXISTS_KSTOP_HANDLE.load(Ordering::Relaxed) { error!("No userspace ACPI handler was notified when trying to shutdown. This is bad."); @@ -206,7 +232,11 @@ impl KernelScheme for AcpiScheme { if handle != HandleBits::KSTOP_HANDLE { return Err(Error::new(EINVAL)); } - Ok(usize::from(*KSTOP_FLAG.lock(token.token()))) + // Phase I.5: return the u8 reason, not the + // pre-Phase-I.5 bool. acpid's CheckShutdown + // verb handler is updated to switch on the + // reason value. + Ok(*KSTOP_FLAG.lock(token.token()) as usize) } } } diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs index 2ab386e98b..78f5a4cdf4 100644 --- a/src/scheme/sys/mod.rs +++ b/src/scheme/sys/mod.rs @@ -133,18 +133,25 @@ const FILES: &[(&str, Kind)] = &[ b"shutdown" => crate::stop::kstop(token), b"reset" => crate::stop::kreset(), b"emergency_reset" => crate::stop::emergency_reset(), - // Phase I: hardware-agnostic s2idle / Modern - // Standby. acpid writes "s2idle" or "s3" to - // /scheme/sys/kstop. The kernel does the - // platform-independent part (set the - // S2IDLE_REQUESTED flag, signal the idle - // path); acpid does the AML (\\_TTS, - // \\_PTS, \\_SI._SST) and FACS waking-vector - // write. See local/sources/kernel/src/ - // arch/x86_shared/stop.rs for the entry - // functions. + // Phase I.5: hardware-agnostic s2idle / Modern + // Standby. acpid writes "s2idle" to + // /scheme/sys/kstop. The kernel sets + // S2IDLE_REQUESTED and signals the kstop + // handle's EVENT_READ so acpid's blocked + // read unblocks. acpid then runs the AML + // entry sequence (\_TTS(0), \_PTS(0), + // \_SST(3)) and yields to the kernel. The + // kernel's idle path sees S2IDLE_REQUESTED + // and calls mwait_loop(). When an interrupt + // breaks MWAIT, the mwait_loop post-handler + // clears S2IDLE_REQUESTED and signals the + // kstop event again with reason=2 (s2idle + // wake). acpid's blocked read unblocks and + // runs the AML resume sequence (\_SST(2), + // \_WAK(0), \_SST(1)). b"s2idle" => { crate::stop::enter_s2idle(); + crate::scheme::acpi::kstop_set_reason(2); Ok(0) } b"s3" => crate::stop::enter_s3(token),