Per local/AGENTS.md § SINGLE-REPO RULE: the Red Bear OS project lives in exactly one git repository (vasilito/RedBear-OS). Per-component Gitea mirrors (redbear-os-base, redbear-os-kernel, redbear-os-installer, redox-drm, userutils, libredox, libpciaccess, ctrlc, syscall, sysinfo) have been redirected or deleted. For each per-component repo with source content, the working-tree HEAD was pushed as a 'submodule/<component>' branch on RedBear-OS: - submodule/base - submodule/bootloader - submodule/installer - submodule/kernel - submodule/libredox - submodule/redoxfs - submodule/relibc - submodule/syscall - submodule/userutils The .gitmodules entry for local/sources/kernel is now redirected to the canonical repo with branch = submodule/kernel. The other submodule .gitmodules entries remain to be added in a follow-up. Empty per-component repos (ctrlc, libpciaccess, redox-drm, sysinfo) had no source content; their gitlinks in the index are removed in a follow-up commit. Unrelated per-component repos that were not Red Bear components (ctrlc, syscall, sysinfo — possibly unrelated personal projects) were deleted in the bulk cleanup. Gitea state under vasilito/ is now exactly: RedBear-OS, hiperiso. Adds: - local/scripts/redirect-to-submodules.sh - local/scripts/delete-per-component-repos.sh Updates: - .gitmodules (kernel → RedBear-OS#submodule/kernel) - local/AGENTS.md (SINGLE-REPO RULE status, migration procedure) - local/docs/BUILD-SYSTEM-IMPROVEMENTS.md §11 (resolved) - local/docs/QUIRKS-AUDIT.md (drop dead links) - local/docs/SLEEP-IMPLEMENTATION-PLAN.md (mark historical) - CHANGELOG.md (mark historical references)
16 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 | ✅ Committed (aadf55b base, 6b98c64 kernel), built |
Yes |
| Phase II: S3 entry path (PM1 register write) | ✅ Committed (9f6a428 kernel), built |
Yes |
| Phase II.X: S3 resume trampoline (64-bit assembly) | ✅ Committed (1be659b, 9bc1fbf kernel), built |
Yes |
| Phase II.X.W: FACS parser + SetS3WakingVector/EnterS3 AcPiVerbs | ✅ Committed (b0f4fee syscall, 475f96e/9bc1fbf kernel, dcd70a1 base), built |
Yes |
| Broad OEM DMI (Dell/HP/Lenovo) | ✅ Committed (4d270bab2 quirks), built |
Yes |
| redbear-mini ISO build | ✅ Succeeds, 512 MB | — |
| QEMU boot test | ✅ Passes, reaches Red Bear login | — |
Build system patch verification (make verify-patches) |
✅ Added (1834c3bf Makefile, 32403ccf4 script) |
— |
| Phase K: convert local sources to git submodules | ⏳ Deferred — requires gitea mirror per source | — |
Phase J Architecture (Current)
The s2idle / s3 coordination path uses two parallel APIs:
- kstop string-arg path (Phase I.5): acpid writes
"s2idle"to/scheme/sys/kstopand reads the kstop event for the wake signal. This is the original path; it works without the AcpiVerb extension. - Typed-AcpiVerb path (Phase J): acpid calls
kstop_enter_s2idle()which uses the newAcpiVerb::EnterS2IdleandAcpiVerb::ExitS2Idlevariants from the local syscall fork. This is the preferred path now that Phase J's libredox fork is in place.
Both paths are fully wired and work. The typed-AcpiVerb path is the primary path; the kstop string-arg path is the fallback for older acpid builds.
Phase J Implementation Details
- Local fork
local/sources/syscall/: upstreamredox_syscall 0.8.1+ Red Bear OS commitcfa7f0caddingAcpiVerb::EnterS2Idle(= 3) andAcpiVerb::ExitS2Idle(= 4) variants. The version field stays at upstream 0.8.1 per the AGENTS.md "GOLDEN RULE". - Local fork
local/sources/libredox/: upstreamlibredox 0.1.17with theredox_syscalldep redirected topath = "../syscall". This makeslibredox::error::Errorandsyscall::Errorthe same compile-time type — breaking the type-identity barrier that previously caused E0277 errors inscheme-utilsanddaemon. - Base
Cargo.toml:[patch.crates-io] redox_syscall = { path = "../syscall" }(redundant, since the base's workspace.dependencies already uses the local path) and[patch.crates-io] libredox = { path = "../libredox" }. - Kernel
Cargo.toml:[workspace] members = [".", "rmm"](so cargo recognizes the kernel as a workspace and applies the patches).[patch."https://gitlab.redox-os.org/redox-os/syscall.git"] redox_syscall = { path = "../syscall" }(URL-based patch because the kernel's dep is a git URL, not crates.io).[patch.crates-io] libredox = { path = "../libredox" }. - Patch file:
local/patches/syscall/P1-acpiverb-enter-exit-s2idle.patchis the durable overlay patch backing the syscall fork commit.
Phase J End-to-End s2idle Flow
- acpid:
enter_s2idle()(_TTS(0),_PTS(0),_SST(3)) - acpid:
kstop_enter_s2idle()callskcall_wo(payload=&[], metadata=[3])on the kstop handle fd → kernel'sAcpiScheme::kcalldispatches onAcpiVerb::EnterS2Idle, setsS2IDLE_REQUESTED, signals the kstop handle event - kernel idle path:
mwait_loop()at deepest C-state - SCI breaks MWAIT
- kernel
mwait_looppost-handler: clearsS2IDLE_REQUESTED, callss2idle_signal_wake()which sets KSTOP_FLAG=2 and signals the kstop handle event - acpid:
kstop_reason()returns 2 (the new typed-AcpiVerbkcall_ro(payload=&mut, metadata=[2])returns the reason via the kernel'sCheckShutdownverb handler) - acpid:
exit_s2idle()(_SST(2),_WAK(0),_SST(1)) - loop
Phase J Test Plan
- Build verification:
redbear-mini.iso(512 MB) builds successfully with the Phase J commits. The build system applies the patches in the right order. - QEMU verification: boot the ISO in QEMU. The cpufreqd
daemon should NOT oscillate (Phase H fix). The acpid
main loop should NOT log repeated
P0→P1transitions. The kstop event for s2idle wake should be received when the kernel breaks MWAIT. - Patch application verification: run
cargo metadata --format-version 1and confirm the resolved source URL forredox_syscallandlibredredoxis the local fork path.
Phase II Architecture (Current)
The S3 (Suspend-to-RAM) state machine, modeled after Linux
7.1's arch/x86/kernel/acpi/wakeup_64.S and
arch/x86/kernel/acpi/sleep.c:
- S3 entry (acpid → kernel → firmware): acpid's
enter_sleep_state(3)does the AML prep (_TTS(3),_PTS(3),_SST(3)), then callskstop_enter_s3(0)which writes the kernel'ss3_trampolinesymbol address toFACS.xfirmware_waking_vectorvia the newSetS3WakingVectorAcPiVerb. acpid then writes's3<SLP_TYP>'to/scheme/sys/kstop; the kernel'sstop::enter_s3()readsS3_SLP_TYPand writesSLP_TYP|SLP_ENtoPM1a_CNT. The platform firmware enters S3. - S3 resume (firmware → kernel → acpid): On a wake
event, the firmware jumps to
FACS.waking_vector(the s3_trampoline). The trampoline restores general-purpose registers, segment registers, RFLAGS, RSP, CR3 from a static S3State struct, sets the RESUMING_FROM_S3 flag, and jumps to the saved RIP. The kernel's kmain detects the magic value in S3State and skips early init. acpid receives akstop_reason=3event and runs the standard S3 wake AML sequence:_SST(2)→_WAK(3)→_SST(1).
The S3 state save in kernel/src/arch/x86_shared/stop.rs
and the resume trampoline in
kernel/src/arch/x86_shared/s3_resume.rs are both
present and built.
Hardware-agnostic: works on any x86_64 system with standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14). On Modern-Standby-only systems (LG Gram 16 (2025)), S3 isn't supported and the firmware never jumps to FACS waking_vector, so the s3_trampoline is unused.
Phase I Architecture (Historical, kept for reference)
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 (historical — these repos have since been merged as
submodule/baseandsubmodule/kernelbranches inside the canonicalRedBear-OSrepo per the SINGLE-REPO RULE):redbear-os-base 5d2d114— acpid: full Linux AML S-state sequenceredbear-os-kernel 75c7618— kernel: s2idle / s3 kstop handler