From dcd70a12554eaee7caaa449b519c3976de30e6f0 Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Wed, 1 Jul 2026 16:32:33 +0300 Subject: [PATCH] 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. --- drivers/acpid/src/main.rs | 11 ++++++++--- drivers/acpid/src/scheme.rs | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs index d1c7b518a8..6aa29ccf97 100644 --- a/drivers/acpid/src/main.rs +++ b/drivers/acpid/src/main.rs @@ -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); diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs index da9180948a..5407e38395 100644 --- a/drivers/acpid/src/scheme.rs +++ b/drivers/acpid/src/scheme.rs @@ -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 {