Phase I.5: complete the acpid <-> kernel s2idle wire. After
MWAIT returns from an interrupt (typically an SCI from
acpid), the kernel now:
1. Clears S2IDLE_REQUESTED (via s2idle_request_clear)
2. Sets KSTOP_FLAG and triggers EVENT_READ on the kstop
handle (via s2idle_signal_wake)
This is the kernel-side analog of Linux 7.1
`acpi_s2idle_wake` in `drivers/acpi/sleep.c:758`. The
existing irq_trigger in generic_irq has already routed the
SCI to acpid's listener (which opened /scheme/irq/{sci}
earlier in the boot sequence), so the AML interpretation
is done by acpid asynchronously.
The s2idle flow now:
1. acpid: enter_s2idle() (\_TTS(0), \_PTS(0), \_SST(3))
2. acpid: write 's2idle\n' to /scheme/sys/kstop
-> kernel sets S2IDLE_REQUESTED, returns
3. Kernel idle path: mwait_loop() at deepest C-state
4. SCI breaks MWAIT (any interrupt, not just SCI)
5. Kernel mwait_loop post-handler (this commit):
- s2idle_request_clear()
- s2idle_signal_wake() -> KSTOP_FLAG set, EVENT_READ
6. acpid main loop: wakes from kstop handle read
7. acpid: exit_s2idle() (\_SST(2), \_WAK(0), \_SST(1))
The KSTOP_FLAG set in step 5 also serves as a 'reason'
indicator — acpid's CheckShutdown verb (kcall 2) returns
the flag, so acpid can distinguish a kstop-shutdown event
from a kstop-s2idle-wake event by polling CheckShutdown
after waking.
Hardware-agnostic: the same flow works for any platform
with Modern Standby firmware (Dell, HP, Lenovo, LG Gram,
etc.). The s2idle is the universal mechanism for low-power
idle; only the wake source (SCI, GPIO, RTC, ...) varies
per OEM.
Phase I: hardware-agnostic sleep coordination. The sys
scheme's kstop handler now dispatches on additional string
arguments:
* 's2idle' — acpid requests Modern Standby / S0ix entry.
The kernel sets S2IDLE_REQUESTED in scheme/acpi.rs. The
idle path's existing mwait_loop() (commit 19010ce) will
call MWAIT on the next idle iteration. MWAIT breaks on
any interrupt (typically an SCI from acpid). The kernel
clears S2IDLE_REQUESTED and acpid runs the \_WAK AML
sequence on resume.
* 's3' — acpid requests Suspend-to-RAM. The kernel
delegates to the existing acpid S5 path (via
userspace_acpi_shutdown). Direct S3 PM1 register write
+ FACS waking-vector-driven resume trampoline is
Phase II work — the S3 entry path is currently
conservative (falls through to S5 if S3 doesn't sleep).
The S2IDLE_REQUESTED atomic in scheme/acpi.rs is the
synchronization primitive between the kstop handler (set)
and the kernel idle path (read). It mirrors Linux 7.1
s2idle_state == S2IDLE_STATE_ENTER in
kernel/power/suspend.c:91.
Hardware-agnostic: works on any platform with Modern
Standby firmware (Dell, HP, Lenovo, LG Gram, etc.) or
traditional S3 (systems that advertise \_S3 in AML). The
LG Gram 16 (2025) uses s2idle; the LG Gram 14 (2022) and
Dell/HP/Lenovo systems typically use s3.
Why not extend the syscall crate with new AcPiVerb
variants? The libredox 0.1.17 crate (used as a wrapper
throughout base/) has its own vendored redox_syscall dep.
Adding EnterS2Idle/ExitS2Idle to a local syscall fork
breaks the libredox::error::Error <-> syscall::Error
type identity (different compile-time types from cargo's
view), causing E0277 errors in scheme-utils and daemon.
Phase J (deferred) will fork libredox to also use the
local syscall fork. Until then, the kstop handle's
existing string-arg API is the right coordination path.
Phase A of the ACPI fork-sync plan (local/docs/ACPI-FORK-SYNC-STRATEGY-2026-06-30.md).
Restores the kernel to the upstream Redox OS kernel main branch state for
the ACPI subsystem:
- Cargo.toml: switch redox_syscall from 0.7.4 (two versions behind) to a
git ref of gitlab.redox-os.org/redox-os/syscall.git, matching the
upstream master dependency. The crates.io 0.8.1 release predates the
AcpiVerb enum that MR #613 / MR #275 introduced, so a crates.io pin
is insufficient.
- src/acpi/rsdp.rs: full rewrite to match upstream f49c7d99 (RSDP
validation + NonNull + fail-softly):
* signature check "RSD PTR "
* 20-byte base checksum
* full-length checksum for revision >= 2
* NonNull<u8> instead of *const u8
Fixes gap #1 from the 2026-06-30 ACPI assessment: the kernel was
accepting any pointer from the bootloader without validation.
- src/startup/mod.rs: acpi_rsdp() returns Option<NonNull<u8>> to match
the new Rsdp::get_rsdp signature.
- src/acpi/mod.rs: init() takes Option<NonNull<u8>>.
- src/scheme/acpi.rs: full rewrite to upstream MR #613 (Simplify acpi
scheme). Drops the /scheme/kernel.acpi/ filesystem surface in favor
of a single Fd::open + call() interface with AcpiVerb verbs:
* AcpiVerb::ReadRxsdt - returns the raw RXSDT bytes
* AcpiVerb::CheckShutdown - returns whether shutdown is pending
Uses HandleBits bitflags, atomic EXISTS_KSTOP_HANDLE, Mutex<L4> from
crate::sync::ordered. Replaces /scheme/kernel.acpi/rxsdt and
/scheme/kernel.acpi/kstop files.
- src/scheme/mod.rs: KernelScheme::kcall signature updated to take
fds: &[usize] instead of id: usize (matches upstream). kfpath now
has a default body returning EOPNOTSUPP (matches upstream).
- src/scheme/memory.rs, proc.rs, user.rs: kcall impls updated to
match new trait signature, using fds.first() to extract the single
handle for backward compat.
- src/scheme/proc.rs: kcall dispatch adds _ => Err(EINVAL) catch-all
for the new ProcSchemeVerb variants (RegsInt, RegsFloat, RegsEnv,
SchedAffinity, Start) that the gitlab syscall crate adds. These
verbs are not yet implemented in the proc scheme; the catch-all
returns EINVAL cleanly instead of failing to compile.
- src/syscall/fs.rs: SYS_CALL dispatcher now passes &[number] to
scheme.kcall() to match the new trait signature.
- Makefile: removed -Z json-target-spec flag (promoted to stable in
nightly 2026-04-01; the flag is unknown in our pinned toolchain).
Verified by `make` in local/sources/kernel/ with PATH including the
prefix cross-toolchain: kernel builds and links successfully.