# Red Bear OS — CachyOS-Class Boot Experience Implementation Plan **Version:** 1.0 · 2026-06-11 · Branch: `0.2.3` **Status:** Canonical plan for boot visual quality, display handoff, and boot speed **Depends on:** existing `redox-drm`, `inputd`, `vesad`, `fbbootlogd`, `fbcond`, `bootloader` **Supersedes:** boot-comfort fragments in `CONSOLE-TO-KDE-DESKTOP-PLAN.md` (boot pipeline layer only) --- ## 0. Architecture Decision **The Linux model is correct: once DRM driver becomes available, it realizes handoff automatically.** No daemon-side config awareness. No polling. No inter-daemon handshakes. When `redox-drm` registers `scheme:drm/card0`, the display path switches through the existing `inputd` ESTALE mechanism. Init orchestrates the lifecycle — staging the splash, detecting DRM, withdrawing the earlyfb, forwarding traffic to the new path. ### Target Pipeline (Post-Plan) ``` UEFI GOP framebuffer (bootloader paints Red Bear logo) → kernel boots, passes FB env vars to init → init starts vesad (20_vesad.service) ← registers display.vesa (earlyfb) → init starts redbear-bootanim (20_bootanim.service) ← paints splash on earlyfb → init starts fbbootlogd (quiet mode, hidden behind splash) → init starts fbcond (VT 2, behind splash) → redox-drm loads (04_drivers.target), registers scheme:drm/card0 → inputd signals ESTALE on all display.* handles → 50_drm-handoff.service runs ← atomic swap: vesad → DRM • bootanim re-parents onto DRM FB (memcpy, no redraw) • fbbootlogd/fbcond reconnect to DRM • vesad releases bootloader FB, exits → SDDM/KWin start (08_userland.target) → bootanim fades out as greeter paints Visible result: black → red bear logo + spinner → silent handoff → SDDM fade-in No log text unless user presses Esc. No flicker. No blank screen. ``` ### Linux Mechanism Mapping | CachyOS / Linux | Red Bear equivalent | |---|---| | `simpledrm` (kernel) | `vesad` earlyfb + bootanim mmap | | `Plymouth` (userspace splash) | `redbear-bootanim` (Rust, per AGENTS.md "system-critical must be Rust") | | Plymouth two-step (pre-DRM → post-DRM) | bootanim `Surface::Vesad` → `Surface::Drm` state machine | | `drm_aperture_remove_conflicting_framebuffers()` | init-managed via `50_drm-handoff.service` + `98_release_vesad.service` | | `CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER` | bootanim holds firmware FB visible until DRM handoff completes | | Plymouth Esc-to-reveal | bootanim SIGUSR2 → fbbootlogd reconnects, paints log overlay | | Plymouth fade-out on greeter ready | bootanim SIGTERM → 200ms fade → exit | --- ## 1. Current State Assessment ### What Exists | Component | Location | Scheme | Status | |---|---|---|---| | Bootloader | `local/sources/bootloader/` | UEFI GOP text menu | Text-only, no logo/splash | | Kernel debug display | `local/sources/kernel/src/devices/graphical_debug/` | `scheme:debug` | Immediately overwrites bootloader FB | | vesad | `local/sources/base/drivers/graphics/vesad/` | `display.vesa` | ✅ Registers earlyfb. No handoff code. Stays alive. | | fbbootlogd | `local/sources/base/drivers/graphics/fbbootlogd/` | `fbbootlog` | ✅ Overwrites FB with log text immediately. Has handoff path. VT 1. | | fbcond | `local/sources/base/drivers/graphics/fbcond/` | `fbcon` | ✅ Text console VTs. Handoff with 4-retry limit. VT 2+. | | inputd | `local/sources/base/drivers/inputd/` | `scheme:input` | ✅ Display/input multiplexer. Signals ESTALE on handoff. | | redox-drm | `local/recipes/gpu/redox-drm/source/` | `scheme:drm` | 🚧 Registers DRM. Calls inputd/handle/ to announce itself. | | virtio-gpud | `local/sources/base/drivers/graphics/virtio-gpud/` | `display.virtio-gpu` | ⚠️ Legacy, uses old GraphicsScheme API | | ihdgd | `local/sources/base/drivers/graphics/ihdgd/` | `display.ihdg.*` | ⚠️ Legacy Intel driver | | Branding assets | `local/Assets/images/` | n/a | PNGs exist, NOT integrated anywhere | ### What's Missing (Gap Analysis) | # | Gap | Impact | |---|-----|--------| | 1 | No boot splash/logo | User sees raw kernel/init log text from the first millisecond | | 2 | fbbootlogd overwrites bootloader FB immediately | Any bootloader-painted pixels are destroyed within milliseconds | | 3 | No smooth display handoff | vesad stays alive, doesn't release FB memory, no coordinated transition | | 4 | No "quiet boot" mode | Kernel/init log is always shown, no way to hide it behind splash | | 5 | Boot is slow (4 barrier syncs before SDDM) | 00→02→04→06→08 target chain; each waits for all services | | 6 | No progress indicator | No animated spinner or progress bar during boot | | 7 | No bootloader branding | UEFI bootloader shows text mode selection menu only | | 8 | vesad doesn't release FB on DRM handoff | Bootloader FB stays mapped, wasting ~8MB memory | | 9 | `29_activate_console` is a mess | Overridden to no-op in legacy-base, then overridden again in mini. 200ms sleep hack. | | 10 | fbcond gives up after 4 handoff retries | If DRM is slow (firmware load), console silently stops | | 11 | Legacy virtio-gpud/ihdgd may conflict | Could race with redox-drm for display scheme | ### Init Service Order (Current) ``` INITFS STAGE: 00_runtime.target → 10_inputd → 20_vesad → 20_fbbootlogd → 20_fbcond → 40_drivers.target → 50_rootfs → 90_initfs.target → switch_root ROOTFS STAGE: 00_base.target → 02_early_hw.target → 04_drivers.target → 06_services.target → 08_userland.target → 29_activate_console → 30_console (getty 2) → login For redbear-full: Same + 12_sddm → kwin_wayland → KDE Plasma ``` --- ## 2. Phased Implementation Plan ### PHASE 1 — Branding Infrastructure **Goal:** Single source of truth for Red Bear visual assets with deterministic conversion. **Effort:** 1–4 hours **Files:** | Path | Type | Purpose | |---|---|---| | `local/Assets/scripts/render-assets.sh` | script | PNG → BMP/RAW conversion via `imagemagick` (host-side) | | `local/Assets/MANIFEST.sha256` | text | Deterministic checksums for all generated assets | | `local/recipes/system/redbear-assets/recipe.toml` | recipe (Rule 1) | Stages assets to `/usr/share/redbear/assets/` | | `local/sources/redbear-assets/` | source (Rule 1) | Trivial install crate | | `local/docs/BOOT-BRANDING-SPEC.md` | doc | Resolution policy, color profile, animation budget | **Generated assets (from existing PNGs):** | Asset | Format | Resolution | Consumer | |---|---|---|---| | `bootlogo-1080p.bmp` | 32-bit BGRA BMP | 1920×1080 | Bootloader UEFI `Blt()` | | `bootlogo-720p.bmp` | 32-bit BGRA BMP | 1280×720 | Bootloader fallback | | `bootlogo-tiny.bmp` | 32-bit BGRA BMP | 640×480 | VESA-only firmware | | `splash-1080p.raw` | Raw BGRA scanout | 1920×1080 | bootanim direct mmap | | `splash-1080p.anim.json` | JSON | n/a | Animation timeline | **Verification:** - `render-assets.sh` produces all assets, byte-identical across rebuilds - `redbear-assets` recipe stages them into sysroot --- ### PHASE 2 — `redbear-bootanim`: Plymouth Equivalent **Goal:** Rust userspace daemon that owns the framebuffer from vesad registration until greeter focus, rendering the Red Bear brand consistently across both earlyfb and DRM. **Effort:** 1–2 days **Files:** | Path | Type | Purpose | |---|---|---| | `local/sources/redbear-bootanim/` | source (Rule 1) | Bootanim daemon source | | `local/sources/redbear-bootanim/src/main.rs` | Rust | Daemon entry, signal handlers | | `local/sources/redbear-bootanim/src/surface.rs` | Rust | Surface abstraction over vesad earlyfb + DRM | | `local/sources/redbear-bootanim/src/anim.rs` | Rust | Animation loop (logo + spinner + progress) | | `local/sources/redbear-bootanim/src/progress.rs` | Rust | Unix datagram socket for progress updates from init | | `local/recipes/system/redbear-bootanim/recipe.toml` | recipe (Rule 1) | Depends on redbear-assets, inputd | | `config/redbear-bootanim.toml` | config fragment | 20_bootanim.service + 50_drm-handoff + 98_release_vesad | **Service wiring:** ```toml # 20_bootanim.service — runs on earlyfb, transitions to DRM [[files]] path = "/etc/init.d/20_bootanim.service" data = """ [unit] description = "Red Bear boot animation (splash)" requires_weak = ["10_inputd.service", "20_vesad.service"] [service] cmd = "/usr/bin/redbear-bootanim" args = ["--surface=vesad", "--vt=1"] type = "simple" respawn = false """ ``` **Behavior:** | State | Surface | Renders | Input | |---|---|---|---| | `Surface::Vesad` | mmap'd bootloader FB | Logo + spinner + progress | Pass-through to fbcond | | `Surface::Drm` | `/scheme/drm/card0` | Same pixels (memcpy, no redraw) | Pass-through | | `Reveal` (SIGUSR2/Esc) | Both | Translucent log overlay on splash | Log scrollback | | `Exit` (SIGTERM) | n/a | 200ms fade to black, exit | n/a | **Key design property:** Handoff is a memcpy, not a redraw. bootanim holds a cached `Box<[u32]>` of the last frame (~8MB). On handoff, it copies this to the DRM FB. Both surfaces end up pixel-identical — zero flicker. **Verification:** - `redbear-mini`: logo appears in UEFI FB, continues through init, transitions to fbbootlogd - `redbear-full`: logo → smooth DRM handoff → SDDM fade-in (no blank gap >1 frame) - Esc reveals log; Esc again hides it --- ### PHASE 3 — Atomic DRM Handoff (Linux `drm_aperture` Equivalent) **Goal:** One-shot helper that orchestrates vesad → DRM transition in a single transaction. **Effort:** 4–8 hours **Files:** | Path | Type | Purpose | |---|---|---| | `local/sources/redbear-bootanim/src/bin/handoff.rs` | Rust | Handoff orchestrator binary | | `local/sources/redbear-bootanim/src/bin/release_fb.rs` | Rust | Sends RELEASE_EARLYFB to vesad | **Handoff sequence (in `handoff.rs`):** ``` 1. Send PREPARE_HANDOFF to bootanim → bootanim flushes scanout, snapshots frame, pauses animation 2. bootanim opens /scheme/drm/card0, performs ModeSetCrtc + first present 3. bootanim returns HANDOFF_READY 4. Send RELEASE_EARLYFB to vesad → vesad munmaps bootloader FB, signals ESTALE, exits 5. Send POST_HANDOFF to bootanim → bootanim resumes animation on DRM surface exclusively 6. Send REBIND_DISPLAY drm to inputd → promotes DRM to primary, ESTALE to remaining consumers 7. Exit 0 ``` **Why a separate binary:** Init can enforce ordering and timeout. If handoff hangs, init moves on — user still gets a working system (stuck splash, compositor paints over it). **Timeout/fallback:** If `redox-drm` doesn't register within 30s, handoff helper falls back to keeping splash on vesad, shows "GPU driver did not load" overlay. **Linux mapping:** | Linux | Red Bear | |---|---| | `drm_aperture_remove_conflicting_framebuffers()` | Init via `handoff.rs` (driver doesn't do implicit aperture management) | | `CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER` | bootanim holds firmware FB visible until handoff step 4 | | Plymouth `show-splash` / `hide-splash` | bootanim exit + sessiond Seat transition signal | > **See also:** `local/docs/boot-logs/REDBEAR-MINI-BOOT-PS2D-INPUTD-LOG-FIX.md` > for the 2026-06-30 fix that added `inputd: scheme:input registered` and > `ps2d: registered producer handle, listening on serio/0 (keyboard) and > serio/1 (mouse)` startup log lines. The handoff binary's step 6 > (`REBIND_DISPLAY drm to inputd`) now has companion observability — > operators can confirm `inputd` is alive before the handoff completes. **Verification:** - `redbear-full` QEMU: screen never black for >1 frame during handoff - Disable redox-drm: fallback message appears, user can still log in via getty - Kill bootanim mid-handoff: handoff helper detects and recovers --- ### PHASE 4 — Quiet Boot (Log Suppression Behind Splash) **Goal:** Normal boot shows only splash. Kernel/init log hidden unless user presses Esc or boot fails. **Effort:** 1 day **Files to modify:** | Path | Change | |---|---| | `local/sources/base/drivers/graphics/fbbootlogd/src/main.rs` | Add `--quiet` flag (don't open display, write to logd only) | | `local/sources/base/drivers/graphics/fbbootlogd/src/scheme.rs` | Quiet mode: no display painting until SIGUSR2 | | `local/sources/base/drivers/inputd/src/main.rs` | Separate "log sink" consumer role from "display" consumer | | `config/redbear-full.toml` | fbbootlogd args `["--quiet"]` | | `config/redbear-mini.toml` | fbbootlogd args `[]` (no quiet — text target shows log) | | `local/docs/QUIET-BOOT-SPEC.md` | Kernel cmdline `redbear_quiet=0|1`, key bindings, failure modes | **Reveal key:** Esc (configurable in `/etc/redbear/bootanim.toml`) → bootanim sends SIGUSR2 to fbbootlogd → fbbootlogd connects to display, paints log. Esc again → disconnects, clears overlay. **Force-reveal conditions (always show log, no quiet):** - Kernel panic - `redox-drm` register timeout - Init restart loop > 2 times - `redbear_quiet=0` kernel cmdline **Verification:** - `redbear-full`: no log text during normal boot. Esc reveals, Esc hides. - `redbear-mini`: log always visible (no quiet). - Daemon crash during boot: log auto-reveals for 5s. --- ### PHASE 5 — Boot Speed: Flatten the Stage Graph **Goal:** Parallelize display path with hardware enumeration. Remove the 200ms sleep hack. **Effort:** 1–2 days **Current chain (4 barrier syncs):** ``` 00_base → 02_early_hw → 04_drivers → 06_services → 08_userland → SDDM ``` **Proposed chain (parallel branches):** ``` 00_base.target (10_inputd is the ONLY hard dep) ├─ [branch A — display] [branch B — hardware] │ 10_bootanim 50_rootfs │ 20_vesad 02_early_hw.target │ 20_fbbootlogd 04_drivers.target │ 20_fbcond redox-drm, xhcid, e1000d, ... │ 06_services.target │ dbus, sessiond, dhcpd │ └──────────────┬───────────────────┘ │ 08_userland.target 12_sddm (requires 50_drm-handoff, not 04_drivers.target) 29_activate_console (no sleep — waits on handoff FD) 30_console (getty 2) ``` **Key changes:** - Display services and driver services run in parallel - `29_activate_console` uses FD-barrier instead of `sleep 0.2` (the FD-handoff pattern from existing pcid patches) - SDDM requires `50_drm-handoff.service`, not `04_drivers.target` - fbcond retry limit removed — handoff helper retries DRM internally with exponential backoff (30s budget) **Benchmark targets:** | Metric | QEMU target | Bare-metal target | |---|---|---| | kernel_entry → bootanim started | < 300ms | < 200ms | | bootanim → SDDM visible | < 2.0s | < 4.0s | | kernel_entry → SDDM painted | < 5.0s | < 7.0s | | Regression threshold | >10% fails CI | >10% fails CI | **Verification:** - `measure-boot-stages.sh` produces CSV of stage timestamps - QEMU video recording: splash from start to SDDM, no black gap - `redbear-mini` unchanged (speedup is redbear-full specific) --- ### PHASE 6 — Bootloader Branding & Live Progress **Goal:** Red Bear logo visible from UEFI handoff. Branded boot menu with auto-boot countdown. **Effort:** 1–2 days **Files to add/modify:** | Path | Change | |---|---| | `local/sources/bootloader/src/os/uefi/boot_logo.rs` | New module: `Blt()` bootlogo BMP at native resolution | | `local/sources/bootloader/src/os/uefi/display.rs` | Extend Output to support `Blt()` with 32-bit BGRA | | `local/sources/bootloader/src/os/uefi/video_mode.rs` | Prefer largest available mode, paint bootlogo | | `local/sources/bootloader/src/main.rs` | Add `--quiet` (default on), `--menu-timeout=3` config | | `local/sources/bootloader/mk/uefi.mk` | Embed BMPs at compile time via `include_bytes!` | | `recipes/core/bootloader/recipe.toml` | Add redbear-assets as dependency | | `local/docs/BOOTLOADER-BRANDING-SPEC.md` | Menu layout, timeout, key bindings, text fallback | **Bootloader progress bar:** - Logo + thin progress bar at bottom (0% at start) - Bar fills to 10% when kernel is read from disk - Bar fills to 100% when kernel entry is reached - Same logo persists through kernel → init transition (no visible gap) **Fallback:** If UEFI GOP doesn't support `Blt()`, bootloader falls back to text mode. Splash from Phase 2 still works. **Verification:** - `redbear-full` ISO in QEMU: red bear logo in UEFI FB, 3s menu, smooth transition to kernel FB - Bare metal AMD + Intel: same behavior - Firmware without Blt(): text fallback works --- ### PHASE 7 — Early Graphical Greeter **Goal:** Something graphical appears before full SDDM/KWin is ready (~2s splash → ~3s minimal greeter). **Effort:** 1–2 days **Files:** | Path | Type | Purpose | |---|---|---| | `local/recipes/wayland/redbear-compositor/source/src/bin/mini.rs` | Rust | Minimal Wayland greeter (user selector on black bg) | | `config/redbear-greeter-services.toml` | config | `11_mini-greeter.service` between handoff and SDDM | **The mini greeter:** - Tiny Wayland compositor (few hundred lines Rust) - Shows single user selector per configured user - Owns the `wl_display` before KWin - On user selection: calls `org.freedesktop.login1.Manager.SwitchToUser(uid)`, exits - Init then starts `12_sddm` which inherits the Wayland display **Verification:** - `redbear-full`: splash → mini greeter (~500ms) → user selection → KWin/Plasma - Total time < 7s on QEMU - `redbear-mini`: unchanged --- ### PHASE 8 — Clean FB Resource Management **Goal:** vesad releases bootloader FB on handoff. Memory accounting is auditable. **Effort:** 4–8 hours **Files to modify:** | Path | Change | |---|---| | `local/sources/base/drivers/graphics/vesad/src/main.rs` | On RELEASE_EARLYFB: munmap FB, close FD, log freed bytes, exit 0 | | `local/sources/base/drivers/graphics/vesad/src/scheme.rs` | Track FB lifetime in `Resource` struct | | `local/sources/base/drivers/inputd/src/main.rs` | On handoff: query vesad resource, log freed bytes, 30s kill watchdog | | `config/redbear-bootanim.toml` | Add vesad-release-timeout watchdog service | | `local/docs/FB-RESOURCE-LIFECYCLE.md` | Full lifecycle diagram with byte counts | **FB lifecycle:** ``` Bootloader → vesad mmap (8MB) → redox-drm allocates DRM FB (8MB) → handoff: both mapped briefly (16MB) → release vesad → only DRM (8MB) ``` **Verification:** - `/var/log/logd` shows FB byte counts through lifecycle - Watchdog kills vesad if release hangs >30s - `redbear-mini`: vesad stays alive (no DRM, no release) --- ## 3. Dependency Graph ``` Phase 1 (branding assets) ← everything downstream │ Phase 2 (bootanim daemon) ← needs Phase 1 assets │ Phase 3 (atomic handoff) ← needs Phase 2 state machine │ Phase 4 (quiet boot) ← independent, parallelizable │ Phase 5 (boot speed graph) ← needs Phase 3 (handoff is the barrier) │ Phase 6 (bootloader branding) ← independent, parallelizable │ Phase 7 (mini greeter) ← needs Phase 3 + Phase 5 │ Phase 8 (FB resource mgmt) ← needs Phase 3 (release step) Critical path: 1 → 2 → 3 → 5 → 7 Parallelizable: 4, 6, 8 ``` --- ## 4. Effort Summary | Phase | Effort | Risk | Rollback | |---|---|---|---| | 1. Branding assets | 1–4 h | Trivial (host-side imagemagick) | Delete recipe + config | | 2. bootanim daemon | 1–2 d | Handoff correctness is subtle | Disable service; log/console still works | | 3. Atomic handoff | 4–8 h | Low (thin orchestrator) | Fallback to vesad if handoff fails | | 4. Quiet boot | 1 d | Reveal key must work pre-fbcond | Per-config opt-in; mini unchanged | | 5. Boot speed | 1–2 d | Invasive stage graph restructure | Revert config; one git checkout | | 6. Bootloader branding | 1–2 d | UEFI Blt() varies by firmware | Text mode fallback preserved | | 7. Mini greeter | 1–2 d | New UI; keyboard handling | Opt-in per config; SDDM still works | | 8. FB resource mgmt | 4–8 h | Force-killing vesad could break consumers | Disable watchdog service | **Total: ~7–10 working days** for a single engineer to land all 8 phases. **First visible improvement:** Phase 1 + Phase 2 (~2 days) → bootloader logo + splash on earlyfb. **Full CachyOS-class experience:** All 8 phases. --- ## 5. Watch-Outs 1. **Bootloader `Blt()` is firmware-dependent.** Test on ≥2 bare-metal firmwares + QEMU OVMF. If GOP doesn't support `Blt()`, text fallback kicks in. 2. **Resolution mismatch on handoff.** If DRM mode differs from vesad earlyfb, bootanim resamples the cached frame (Lanczos). Worst case: Intel i915 at 1366×768 panel + 1920×1080 DRM mode. 3. **Init FD-handoff semantics** assumed by Phase 5 (`pass_fds = [3]`) must be verified in init source before restructuring the boot graph. 4. **No patches in `local/patches/`.** All changes are direct edits in `local/sources//` (Rule 1) or tracked config fragments. 5. **Actual source paths:** `local/sources/base/drivers/graphics//`, not `local/sources/base/src/daemon/`. Verify before editing. 6. **KWin QML gate:** If full Plasma can't boot, Phase 7's mini greeter is the graceful degradation. Working graphical session without Plasma is better than stuck boot. 7. **Legacy virtio-gpud/ihdgd conflict:** Verify `config/redbear-full.toml` excludes these. If they ship alongside redox-drm, they'll race for the display scheme. --- ## 6. Immediate Next Steps (Blocking Issues) Before starting Phase 1, fix these existing issues that block a clean boot: 1. **Init stops at thermald** — why console services (29-31) never start. Need runtime debug output from init. 2. **`29_activate_console.service` no-op** — redbear-legacy-base.toml overrides to `cmd = "true"`. VT 2 never activated. 3. **Remove temporary debug code** from init main.rs (INIT_LOG_LEVEL=DEBUG, debug_log function). 4. **Fix `00_acpid.service` reference** — `00_driver-manager.service` references non-existent `00_acpid.service` (should be `30_acpid.service`).