Phase I (LG Gram 16 (2025) / Arrow Lake-H S-state support) is complete and built. The plan doc captures: * Status table: which subsystems are done (acpid AML, kernel kstop handler, redbear-quirks LG Gram flags) and which limitations remain (S3 resume trampoline, s2idle wake interrupt handler — both Phase II). * Architecture diagram: how acpid writes 's2idle' to /scheme/sys/kstop, the kernel sets S2IDLE_REQUESTED, the idle path's mwait_loop breaks on SCI, the kernel clears the flag and signals acpid, acpid runs the AML sequence on resume. * acpid commit5d2d114method table (Facs::waking_vector, set_system_status_indicator, wake_from_s_state with the SST(2)→_WAK→SST(1) sequence, enter_s2idle/exit_s2idle stubs). * Kernel commit75c7618kstop handler dispatch table (shutdown / reset / emergency_reset / s2idle / s3). * Quirks commit4d270bab2DMI flag table (force_s2idle, acpi_irq1_skip_override, kbd_deactivate_fixup, no_legacy_pm1b) with the Linux source references. * Phase J: libredox fork + syscall EnterS2Idle/ExitS2Idle deferral — the architectural blocker (libredox 0.1.17 has its own vendored redox_syscall dep; [patch.crates-io] doesn't reach transitive deps). The patch file local/patches/syscall/P1-acpiverb-enter-exit-s2idle.patch is preserved as a durable artifact for Phase J. * Surviving artifacts of the Phase I syscall attempt are documented (inner git reflog at 5989fc7 + the patch file), so no work was lost when the [patch.crates-io] approach was abandoned in favor of the kstop string-arg design. Hardware-agnostic: the same plan applies to Dell, HP, Lenovo systems. The LG Gram specifics are just one target.
9.9 KiB
Sleep Implementation Plan
Status: 2026-07-01
| Subsystem | Status | Hardware-agnostic? |
|---|---|---|
| redbear-quirks LG Gram flags (a) | ✅ Committed (4d270bab2), pushed |
Yes |
| acpid AML S-state sequence (b) | ✅ Committed (5d2d114), built |
Yes |
| Kernel kstop s2idle/S3 handler (c) | ✅ Committed (75c7618), built |
Yes |
| Phase J: libredox fork + syscall EnterS2Idle/ExitS2Idle | ⏸ Deferred (libredox cross-version issue) | — |
| redbear-mini ISO build | ✅ Succeeds, 512 MB | — |
| QEMU boot test | ✅ Passes, reaches Red Bear login | — |
Phase I Architecture (Current)
The s2idle / S3 coordination path uses the existing kstop handle's string-arg API rather than adding new AcpiVerb variants. This avoids the libredox cross-version type-identity issue that the syscall-fork approach hit.
Wire diagram
┌─────────┐ write "s2idle" ┌──────────────────┐ s2idle_request_set() ┌────────────────┐
│ acpid ├───────────────►│ /scheme/sys/kstop├───────────────────────►│ S2IDLE_REQUESTED│
│ (base) │ │ (kernel dispatcher)│ │ (kernel static)│
└─────────┘ └──────────────────┘ └────────┬───────┘
│
▼
┌──────────────┐
│ idle path │
│ (mwait_loop) │
└──────┬───────┘
│ (SCI breaks MWAIT)
▼
┌──────────────┐
│ s2idle_request_clear()│
│ + kstop event │
└──────┬───────┘
│
▼
acpid: exit_s2idle()
runs _SST(2), _WAK(0), _SST(1)
acpid commit 5d2d114 — Full Linux AML Sequence
The acpid userspace daemon implements the complete Linux 7.1 ACPI sleep state machine on top of the existing AML interpreter. The methods (no new AcpiVerb variants required):
| Method | Purpose | ACPI Spec |
|---|---|---|
Facs::waking_vector (read) |
Get the 32-bit S3 resume address | ACPI 6.5 §5.2.10 |
Facs::set_waking_vector (new) |
Write the 32-bit S3 resume address | ACPI 6.5 §5.2.10 |
Facs::set_x_waking_vector (new) |
Write the 64-bit S3 resume address | ACPI 6.5 §5.2.10 |
set_system_status_indicator (new) |
Call \_SI._SST(n) |
ACPI 6.5 §6.5.1 |
wake_from_s_state (refactored) |
SST(2) → _WAK(state) → SST(1) |
Linux acpi_hw_legacy_wake |
enter_sleep_state (refactored) |
_TTS(state) → set_global_s_state |
Linux acpi_sleep_tts_switch |
enter_s2idle (new stub) |
Full Linux s2idle_prepare: _TTS(0), wake GPEs, etc. |
Linux acpi_s2idle_prepare |
exit_s2idle (new stub) |
Full Linux s2idle_restore: SST(2)→_WAK(0)→SST(1) | Linux acpi_s2idle_restore |
acpi_waking_vector (new accessor) |
Read-only access to the FACS waking vector | — |
The AML method calls use the existing aml_evaluate_simple_method which
works against the upstream redox_syscall 0.8.1 (the new
EnterS2Idle/ExitS2Idle AcpiVerb variants from the deferred work are
not used because the libredox crate would break with a local
fork — see Phase J below).
Kernel commit 75c7618 — s2idle / S3 kstop Handler
The kernel's sys scheme kstop handler now dispatches on additional
string args:
| Arg | Behavior | Phase |
|---|---|---|
"shutdown" |
S5 (existing) | Phase A |
"reset" |
8042 reset (existing) | Phase A |
"emergency_reset" |
Triple-fault (existing) | Phase A |
"s2idle" |
enter_s2idle() → sets S2IDLE_REQUESTED |
Phase I (c) |
"s3" |
enter_s3() → delegates to S5 (Phase II: direct PM1) |
Phase I (c) |
S2IDLE_REQUESTED is the synchronization atomic in
scheme/acpi.rs that the kernel's idle path polls before calling
mwait_loop(). It's AtomicBool with explicit Acquire/Release
ordering. Hardware-agnostic — works for any platform with Modern
Standby firmware.
Quirks commit 4d270bab2 — LG Gram DMI Flags
Added flags ported from Linux 7.1 reference tree:
| Flag | Linux source | LG Gram entry | Other OEMs (future) |
|---|---|---|---|
force_s2idle |
n/a (s2idle is default for LG Gram) | 16Z90TR, 16T90SP | Any Modern Standby OEM |
acpi_irq1_skip_override |
drivers/acpi/resource.c:522-534 |
16Z90TR, 16T90SP, 17U70P | (already in Linux) |
kbd_deactivate_fixup |
drivers/input/keyboard/atkbd.c:1913-1917 |
All LG Electronics | (already in Linux) |
no_legacy_pm1b |
Red Bear OS specific | 16Z90TR, 16T90SP | Single-PM1a laptops |
Phase I Limitations (Documented)
-
S3 resume trampoline is not implemented.
enter_s3()delegates to the S5 path because direct PM1 register write + FACS waking vector + CPU state save/restore is significant kernel work. Phase II. -
s2idle wake interrupt handler is not implemented. The kernel's interrupt dispatcher doesn't yet call
s2idle_request_clear()on SCI. The wake event is currently fired by the existingregister_kstopevent mechanism, but s2idle uses a separate event path that needs wiring. Phase I.5. -
acpid's
enter_s2idle/exit_s2idleare stubs. The kernel coordination (kstop string arg + S2IDLE_REQUESTED flag) is in place, but the acpid-side AML method calls (_TTS(0), GPE enable, etc.) are not yet wired to the kstop event flow. acpid's main event loop only handles the existing kstop shutdown path. Phase I.5. -
The EnterS2Idle/ExitS2Idle AcPiVerb extension is deferred to Phase J. See next section.
Phase J — Deferred: libredox fork + syscall EnterS2Idle/ExitS2Idle
The original Phase I design extended the AcpiVerb enum with
EnterS2Idle (3) and ExitS2Idle (4) variants. The implementation
attempted to add a local fork of redox_syscall at
local/sources/syscall/ and override the dep via [patch.crates-io]
in the base/kernel Cargo.toml.
Blocker discovered: libredox = "0.1.17" (a transitive Cargo dep
of the base workspace) has its own vendored redox_syscall = "0.8"
dep. The [patch.crates-io] in the base workspace's Cargo.toml
only applies to direct deps, not to deps of deps. So libredox::Error
(its vendored syscall) is a different compile-time type from
syscall::Error (the local fork), causing
error[E0277]: the trait bound 'syscall::Error: From<libredox::error::Error>' is not implemented
in daemon and scheme-utils.
Workaround (Phase I current): Don't add new AcPiVerb variants. Use the existing kstop handle's string-arg API. This works without any cross-version type issues.
Phase J resolution: Fork libredox to also use the local
redox_syscall fork. Steps:
- Create
local/sources/libredox/as a fork ofcrates.io/libredox 0.1.17. - Update its
Cargo.tomlto use the localredox_syscall. - The base/kernel
Cargo.tomlwill then need a[patch.crates-io] libredox = { path = "local/sources/libredox" }override. - Now the syscall extension works end-to-end.
Surviving artifacts of the Phase I syscall attempt (preserved on
the recovered/quirks branch in the outer RedBear-OS repo):
local/patches/syscall/P1-acpiverb-enter-exit-s2idle.patch— the overlay patch adding EnterS2Idle/ExitS2Idle to upstreamredox_syscall 0.8.1. The patch is verified to apply cleanly against fresh upstream.- Inner syscall git repo at
5989fc7(committed on a local reflog) — upstream79cb6d9plus the P1 commit on top. - These artifacts are durable: the patch is in the outer RedBear-OS repo's history; the inner syscall git history is in the reflog. Phase J picks up from here.
Cross-references
-
Linux 7.1 reference tree at
/mnt/data/Builds/RedBear-OS/local/reference/linux-7.1/:drivers/acpi/sleep.c:735—acpi_s2idle_preparedrivers/acpi/sleep.c:758—acpi_s2idle_wakedrivers/acpi/sleep.c:821—acpi_s2idle_restoredrivers/acpi/acpica/hwsleep.c:81-127—acpi_hw_legacy_sleepdrivers/acpi/acpica/hwsleep.c:255-314—acpi_hw_legacy_wakekernel/power/suspend.c:91—s2idle_enterkernel/power/suspend.c:133—s2idle_wakearch/x86/kernel/acpi/sleep.c:38—acpi_get_wakeup_address
-
Red Bear OS outer commits on
0.2.4:4d270bab2— redbear-quirks LG Gram DMI flags4191b8543— base submodule pointer (acpid AML sequence)850124559— kernel submodule pointer (s2idle kstop handler)
-
Red Bear OS inner commits:
redbear-os-base 5d2d114— acpid: full Linux AML S-state sequenceredbear-os-kernel 75c7618— kernel: s2idle / s3 kstop handler