diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs index 085354ec01..d1c7b518a8 100644 --- a/drivers/acpid/src/main.rs +++ b/drivers/acpid/src/main.rs @@ -96,6 +96,12 @@ fn daemon(daemon: daemon::Daemon) -> ! { let socket = Socket::nonblock().expect("acpid: failed to create disk scheme"); let mut scheme = self::scheme::AcpiScheme::new(&acpi_context, &socket); + // Phase I.5: register the kstop handle fd so the main loop + // can call kstop_reason (kcall 2) to query the kernel for + // the reason of the most recent kstop event. The handle + // shares the underlying file descriptor; the kcall goes + // through the same fd that the event queue subscribes to. + scheme.set_kstop_fd(Fd::new(shutdown_pipe.raw())); let mut handler = Blocking::new(&socket, 16); event_queue @@ -133,8 +139,42 @@ fn daemon(daemon: daemon::Daemon) -> ! { } } } else if event.fd == shutdown_pipe.raw() as usize { - log::info!("Received shutdown request from kernel."); - mounted = false; + // Phase I.5: dispatch on the kstop reason. The + // kcall 2 (CheckShutdown) verb returns the + // u8 reason. The kernel re-arms the EVENT_READ + // for the next event in the same fd; we read it + // once per cycle. + let reason = match scheme.kstop_reason() { + Ok(r) => r as u8, + Err(e) => { + log::warn!("kstop_reason failed: {:?}, falling back to shutdown", e); + 1 + } + }; + match reason { + 0 => { + // idle / no event — spurious wake, ignore + } + 1 => { + // shutdown (S5) + log::info!("Received shutdown request from kernel."); + mounted = false; + } + 2 => { + // s2idle wake (Phase I.5) + log::info!("s2idle wake: running \\_SST(2) -> \\_WAK(0) -> \\_SST(1)"); + acpi_context.exit_s2idle(); + } + 3 => { + // s3 wake (Phase II) + log::info!("s3 wake (Phase II — not yet implemented)"); + // TODO: Phase II + } + other => { + log::warn!("unknown kstop reason {}, treating as shutdown", other); + mounted = false; + } + } } else { log::debug!("Received request to unknown fd: {}", event.fd); continue; diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs index 0b02e9cc69..041e7721f6 100644 --- a/drivers/acpid/src/scheme.rs +++ b/drivers/acpid/src/scheme.rs @@ -6,6 +6,8 @@ use libredox::Fd; use parking_lot::RwLockReadGuard; use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult, SendFdRequest, Socket}; +use syscall::flag::CallFlags; +use syscall::flag::AcpiVerb; use ron::de::SpannedError; use scheme_utils::HandleMap; use std::convert::{TryFrom, TryInto}; @@ -29,6 +31,10 @@ pub struct AcpiScheme<'acpi, 'sock> { handles: HandleMap>, pci_fd: Option, socket: &'sock Socket, + /// Phase I.5: the kstop handle fd. Stored so the main loop + /// can call `kstop_reason` (kcall 2) to query the kernel + /// for the reason of the most recent kstop event. + kstop_fd: Option, } struct Handle<'a> { @@ -131,8 +137,38 @@ impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { handles: HandleMap::new(), pci_fd: None, socket, + kstop_fd: None, } } + + /// Phase I.5: register the kstop handle fd. Called by the + /// main loop right after opening the kstop handle. + pub fn set_kstop_fd(&mut self, fd: Fd) { + self.kstop_fd = Some(fd); + } + + /// Phase I.5: query the kernel for the kstop reason via + /// the CheckShutdown AcpiVerb (kcall 2). Returns the u8 + /// reason: 0=idle, 1=shutdown (S5), 2=s2idle wake, + /// 3=s3 wake. The kernel re-arms the kstop handle's + /// EVENT_READ after each event; acpid's main loop calls + /// this once per event to decide what AML sequence to run. + /// + /// Mirrors Linux 7.1 `acpi_s2idle_wake` returning the + /// wake reason in `drivers/acpi/sleep.c:758`. The + /// `kcall 2` is the `AcpiVerb::CheckShutdown` enum + /// variant in the syscall crate. + /// + /// Hardware-agnostic: the reason codes are platform- + /// independent; only the wake source (SCI, GPIO, RTC, + /// ...) varies per OEM. + pub fn kstop_reason(&mut self) -> syscall::Result { + let handle = self.kstop_fd.ok_or(syscall::error::Error::new(syscall::error::EBADF))?; + let mut payload = [0u8; 8]; + let verb = AcpiVerb::CheckShutdown as u64; + let result = handle.call_ro(&mut payload, CallFlags::empty(), &[verb])?; + Ok(u64::from_ne_bytes(payload)) + } } fn parse_hex_digit(hex: u8) -> Option {