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 {