acpid: Phase II.X.W S3 wake handling + kstop_enter_s3 helper

Phase II.X.W: extend the acpid main loop to handle the
kstop reason 3 (S3 wake) with the standard AML sequence
\\_SST(2) -> \\_WAK(3) -> \\_SST(1).

Also adds \`kstop_enter_s3()\` to AcpiScheme: writes the
kernel's S3 resume trampoline address to FACS via the new
SetS3WakingVector AcPiVerb (verb 5). A zero payload is a
sentinel for 'use the kernel's default trampoline address'.

The acpid's enter_sleep_state for S3 will:
1. Do the AML prep (\\_TTS(3), \\_PTS(3), \\_SST(3)) - the
   existing set_global_s_state path.
2. Call kstop_enter_s3(0) to write the trampoline
   address to FACS.
3. Write 's3' to /scheme/sys/kstop to trigger the
   kernel's S3 entry path.

Hardware-agnostic: works on any x86_64 system with
standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14).
On Modern Standby-only systems (LG Gram 16 (2025)), the
kernel never enters S3 so the S3 wake path is never
executed.
This commit is contained in:
Red Bear OS
2026-07-01 16:32:33 +03:00
parent aadf55bfca
commit dcd70a1255
2 changed files with 27 additions and 3 deletions
+8 -3
View File
@@ -166,9 +166,14 @@ fn daemon(daemon: daemon::Daemon) -> ! {
acpi_context.exit_s2idle();
}
3 => {
// s3 wake (Phase II)
log::info!("s3 wake (Phase II — not yet implemented)");
// TODO: Phase II
// s3 wake (Phase II.X.W)
// Run the standard S3 resume AML sequence:
// \_SST(2) -> \_WAK(3) -> \_SST(1). The kernel
// trampoline at s3_resume::s3_trampoline
// has already restored the kernel state. The
// acpid's job is the AML wake sequence.
log::info!("s3 wake: running \\_SST(2) -> \\_WAK(3) -> \\_SST(1)");
acpi_context.wake_from_sleep_state(3);
}
other => {
log::warn!("unknown kstop reason {}, treating as shutdown", other);
+19
View File
@@ -195,6 +195,25 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> {
handle.call_wo(&[], CallFlags::empty(), &[verb])?;
Ok(())
}
/// Phase II.X.W: write the kernel's S3 resume
/// trampoline address to FACS.xfirmware_waking_vector so
/// the platform firmware jumps to it on S3 wake.
///
/// `trampoline_addr` is the address of the kernel's
/// `s3_resume::s3_trampoline` function. The kernel
/// writes this to FACS via the `SetS3WakingVector`
/// AcPiVerb (verb 5).
pub fn kstop_enter_s3(&self, trampoline_addr: u64) -> syscall::Result<()> {
let handle = self.kstop_fd.ok_or(syscall::error::Error::new(syscall::error::EBADF))?;
let verb = AcpiVerb::SetS3WakingVector as u64;
// Payload: 8-byte little-endian u64 (the trampoline
// address). The kernel's `SetS3WakingVector` handler
// requires the payload to be exactly 8 bytes.
let payload = trampoline_addr.to_ne_bytes();
handle.call_wo(&payload, CallFlags::empty(), &[verb])?;
Ok(())
}
}
fn parse_hex_digit(hex: u8) -> Option<u8> {