kernel: kstop reason codes (Phase I.5 s2idle / s3 wire)
Phase I.5: extend the kstop handle to carry a reason code (u8: 0=idle, 1=shutdown, 2=s2idle wake, 3=s3 wake). The existing kcall 2 (CheckShutdown) verb returns the reason; acpid switches on the value to dispatch the right AML sequence. * 1 (shutdown): acpid runs \_TTS(5) + \_PTS(5) + \_SST then exits (existing behavior). * 2 (s2idle wake): acpid runs \_SST(2) + \_WAK(0) + \_SST(1) (new Phase I.5 behavior). * 3 (s3 wake): Phase II — not yet wired. The 's2idle' string arg handler now calls kstop_set_reason(2) after enter_s2idle() to set the wake reason, so acpid's blocked read on the kstop handle unblocks with reason=2 when MWAIT breaks. This is the dual-purpose wake signal. Hardware-agnostic: works for any platform with Modern Standby firmware (Dell, HP, Lenovo, LG Gram, etc.). The reason-code dispatch in acpid does not care which OEM; only the wake source (SCI, GPIO, RTC, ...) varies.
This commit is contained in:
+43
-13
@@ -37,9 +37,44 @@ bitflags! {
|
||||
|
||||
static RXSDT_DATA: Once<Box<[u8]>> = Once::new();
|
||||
|
||||
static KSTOP_FLAG: Mutex<L4, bool> = 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<L4, u8> = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+17
-10
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user