docs: add SLEEP-IMPLEMENTATION-PLAN.md (Phase I + Phase J deferral)
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.
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
# 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)
|
||||
|
||||
1. **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.
|
||||
|
||||
2. **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 existing
|
||||
`register_kstop` event mechanism, but s2idle uses a separate event
|
||||
path that needs wiring. Phase I.5.
|
||||
|
||||
3. **acpid's `enter_s2idle` / `exit_s2idle` are 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.
|
||||
|
||||
4. **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:
|
||||
1. Create `local/sources/libredox/` as a fork of `crates.io/libredox 0.1.17`.
|
||||
2. Update its `Cargo.toml` to use the local `redox_syscall`.
|
||||
3. The base/kernel `Cargo.toml` will then need a `[patch.crates-io]
|
||||
libredox = { path = "local/sources/libredox" }` override.
|
||||
4. 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 upstream
|
||||
`redox_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) — upstream `79cb6d9` plus 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_prepare`
|
||||
- `drivers/acpi/sleep.c:758` — `acpi_s2idle_wake`
|
||||
- `drivers/acpi/sleep.c:821` — `acpi_s2idle_restore`
|
||||
- `drivers/acpi/acpica/hwsleep.c:81-127` — `acpi_hw_legacy_sleep`
|
||||
- `drivers/acpi/acpica/hwsleep.c:255-314` — `acpi_hw_legacy_wake`
|
||||
- `kernel/power/suspend.c:91` — `s2idle_enter`
|
||||
- `kernel/power/suspend.c:133` — `s2idle_wake`
|
||||
- `arch/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 flags
|
||||
- `4191b8543` — 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 sequence
|
||||
- `redbear-os-kernel 75c7618` — kernel: s2idle / s3 kstop handler
|
||||
Reference in New Issue
Block a user