3 Commits

Author SHA1 Message Date
vasilito cdb5544fd1 quirks: add ACPI IRQ1, kbd_deactivate, no_legacy_pm1b flags for LG Gram 16 (2025)
Phase I (a): redbear-quirks enrichment. Each flag is ported
from the Linux 7.1 reference tree and applied to LG Gram 16
(2025) 16Z90TR and 16T90SP (2026 Panther Lake). The flags are
generic and applicable to other OEMs too:

* acpi_irq1_skip_override — Linux drivers/acpi/resource.c
  irq1_level_low_skip_override[] (lines 522-534). Without this
  the ACPI core rewrites the DSDT's ActiveLow to ActiveHigh
  and the i8042 keyboard IRQ stops firing on LG Gram.

* kbd_deactivate_fixup — Linux drivers/input/keyboard/atkbd.c
  line 1913-1917. Prevents spurious keyboard ACK / dropped
  keys on LG hardware.

* no_legacy_pm1b — Red Bear OS specific. LG firmware does not
  implement a separate PM1b_CNT register; tells acpid to skip
  the SLP_TYPb write path.

Also adds a 17U70P entry (Linux matches this on board_name)
and a catch-all LG Electronics entry (Linux atkbd.c matches on
sys_vendor only, not product_name).

Hardware-agnostic: the same flags apply to Dell, HP, Lenovo
laptops with similar firmware quirks. Future Phase I work
will add DMI matches for those vendors based on the Linux
quirk tables.

Also updates local/sources/kernel submodule pointer to
7a38664 (Phase I [patch.crates-io] for redox_syscall).
2026-07-01 04:55:22 +03:00
vasilito 4d4f67a1b4 patches/syscall: add P1-acpiverb-enter-exit-s2idle.patch (Phase I)
Phase I: hardware-agnostic s2idle / Modern Standby support.

The patch adds two new AcpiVerb enum variants to upstream
redox-os/syscall 0.8.1:

* EnterS2Idle (= 3) — acpid requests the kernel enter s2idle.
* ExitS2Idle  (= 4) — acpid signals s2idle exit.

The kernel-side wire is in local/sources/kernel/src/scheme/acpi.rs
(see kernel master @ 7a38664 for the [patch.crates-io] update that
points Cargo at local/sources/syscall).

The patch is durable in the outer RedBear-OS repo so that:
* The upstream base is at local/sources/syscall/ (own git repo,
  no version change from 0.8.1).
* When upstream updates, the inner repo is rebased on
  upstream/master and this patch is re-applied to the new
  upstream HEAD.
* The same patch is also applied to local/sources/syscall/
  in the inner git history (commit cfa7f0c), so the local fork
  has the same content the build system uses.

Hardware-agnostic: works for any platform with Modern Standby
firmware (Dell, HP, Lenovo, LG Gram, etc.), not just LG Gram.
2026-07-01 04:53:55 +03:00
vasilito 137f3e79a2 add blake3 hashes to 57 recipes, fix m4 URL typo (1.14.21 -> 1.4.21)
All local recipes with tar= source now have blake3 content hashes for
cache verification. m4 recipe URL had version typo causing 404.
2026-07-01 01:05:29 +03:00
4607 changed files with 7887 additions and 1269612 deletions
+2 -16
View File
@@ -13,12 +13,8 @@
# Nested recipe debris from prior build-system layouts (4.2GB+ of duplicates)
recipes/recipes/
# Fetched source trees in mainline recipes AND in specific local/ build-cache
# recipes (those whose source/ is a transient working copy re-fetched by the
# build system from the recipe's `git` URL). The durable code for these is
# recipe.toml + local/patches/. — DO NOT add a blanket `local/recipes/**/source`
# rule here: ~150 Red Bear recipes have durable source code under
# `local/recipes/<name>/source/` (the fork model).
# Fetched source trees in mainline recipes (not our code in local/)
# Matches recipes/<category>/<name>/source/ but NOT local/recipes/*/source/
recipes/**/source
recipes/**/source.tmp
recipes/**/source-new
@@ -26,10 +22,6 @@ recipes/**/source-old
recipes/**/source.tar
recipes/**/source.tar.tmp
recipes/**/source.pre-preservation-test/
local/recipes/archives/uutils-tar/source
local/recipes/dev/ninja-build/source
local/recipes/kde/sddm/source
local/recipes/kde/sddm/source-pristine
# Build artifacts — target/ dirs are everywhere
target
@@ -39,12 +31,6 @@ wget-log
# Vendor source trees (fetched, not our code)
**/amdgpu-source/
# External reference trees (read-only consultation sources). The Linux
# reference tree (local/reference/linux-7.1) is currently kept locally
# but is gitignored by size; seL4 reference is an empty placeholder.
local/reference/linux-*/
local/reference/seL4/
# Compiled objects
*.o
*.so
+2 -34
View File
@@ -1,36 +1,4 @@
[submodule "local/sources/base"]
path = local/sources/base
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/base
[submodule "local/sources/bootloader"]
path = local/sources/bootloader
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/bootloader
[submodule "local/sources/installer"]
path = local/sources/installer
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/installer
[submodule "local/sources/kernel"]
path = local/sources/kernel
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/kernel
[submodule "local/sources/libredox"]
path = local/sources/libredox
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/libredox
[submodule "local/sources/redoxfs"]
path = local/sources/redoxfs
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/redoxfs
[submodule "local/sources/relibc"]
path = local/sources/relibc
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/relibc
[submodule "local/sources/syscall"]
path = local/sources/syscall
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/syscall
[submodule "local/sources/userutils"]
path = local/sources/userutils
url = https://gitea.redbearos.org/vasilito/RedBear-OS.git
branch = submodule/userutils
url = https://gitea.redbearos.org/vasilito/redbear-os-kernel.git
branch = master
+24 -78
View File
@@ -113,25 +113,23 @@ echo 'PODMAN_BUILD?=1' > .config # Podman container build
# archive and ignores the local forks. Remove it if present:
grep -v REDBEAR_RELEASE .config > .config.tmp && mv .config.tmp .config
# Build Red Bear OS — CANONICAL build command is build-redbear.sh
# Build Red Bear OS
# Supported compile targets:
# redbear-full desktop/graphics target (live ISO)
# redbear-mini text-only console/recovery target (live ISO)
# redbear-full desktop/graphics target (harddrive.img or live ISO)
# redbear-mini text-only console/recovery target (harddrive.img or live ISO)
# redbear-grub text-only with GRUB boot manager (live ISO)
# Desktop/graphics target: redbear-full
# Text-only targets: redbear-mini, redbear-grub
./local/scripts/build-redbear.sh redbear-mini # Recommended (dev mode, offline)
./local/scripts/build-redbear.sh redbear-mini # Recommended (dev mode, local forks)
./local/scripts/build-redbear.sh --upstream redbear-mini # With online fetch (fast iteration)
./local/scripts/build-redbear.sh redbear-full # Desktop/graphics target
./local/scripts/build-redbear.sh redbear-grub # Text-only + GRUB
./local/scripts/build-redbear.sh --no-cache redbear-mini # Force clean rebuild
make all CONFIG_NAME=redbear-mini # Text-only target → harddrive.img
make all CONFIG_NAME=redbear-full # Desktop/graphics target → harddrive.img
make live CONFIG_NAME=redbear-full # Full desktop live ISO
make live CONFIG_NAME=redbear-mini # Text-only mini live ISO
make live CONFIG_NAME=redbear-grub # Text-only mini live ISO with GRUB
CI=1 make all CONFIG_NAME=redbear-mini # CI mode (disables TUI, for non-interactive)
# build-redbear.sh handles: .config parsing, prefix staleness detection,
# local-over-WIP policy, version sync, pre-cooking critical packages,
# source fingerprint tracking, and ultimately calls `make live`.
# Output: build/<arch>/<config>.iso
# For local fork development:
# IMPORTANT: For local fork development, use:
# export REDBEAR_ALLOW_PROTECTED_FETCH=1 # base/kernel/relibc are protected recipes
# ./local/scripts/build-redbear.sh --upstream redbear-mini
# Without --upstream, the build is in offline mode and re-extracts from
@@ -140,6 +138,7 @@ grep -v REDBEAR_RELEASE .config > .config.tmp && mv .config.tmp .config
# Run
make qemu # Boot in QEMU
make qemu QEMUFLAGS="-m 4G" # With more RAM
make live # Build live ISO for real bare metal
# QEMU with network access (required for testing):
qemu-system-x86_64 -cdrom build/x86_64/redbear-mini.iso \
@@ -421,69 +420,18 @@ KDE recipes, and all recipes carrying Red Bear patches (Qt, DRM, Mesa, Wayland,
glib, etc.). Any recipe with a `redox.patch` or local patches is a candidate for
protection — add it when adding patches.
### Local Fork Priority and Version Sync (MANDATORY)
**Rule**: If a recipe exists in `local/sources/<component>/` (a local fork), it
**always takes precedence** over the upstream version in `recipes/<component>/`.
However, the build system **must guarantee** that the local fork is at the
**latest available version** and that **all Red Bear patches are applied cleanly**
on top of that version.
**Rationale**: Red Bear forks are designed to be drop-in compatible with
upstream. Transitive dependencies from crates.io may pull in newer versions
of shared crates (e.g. `redox_syscall`, `redox-scheme`). If the local fork is
at a lower version than what the dependency graph requires, two consequences
follow:
1. **Lockfile collision**: Cargo sees `redox_syscall v0.8.1 (local/sources/syscall)`
and `redox_syscall v0.8.1 (recipes/core/base/syscall)` as different sources
even when they resolve to the same directory via symlink.
2. **Type mismatch**: Transitive deps built against `redox_syscall 0.9.x` (from
crates.io) produce types incompatible with our local `0.8.x` fork,
causing `error[E0308]: mismatched types` at link/compile time.
**Detection**: The build system MUST detect these conditions automatically.
On every build, the cookbook MUST:
1. Compare the version in `local/sources/<component>/Cargo.toml` against the
highest version required by any transitive dependency in the build graph.
2. Compare the version in `local/sources/<component>/Cargo.toml` against the
upstream `recipes/<component>/source/Cargo.toml` (if available).
3. If the local version is lower than either:
a. Record the required version in a manifest (`local/sources/<component>/.required-version`)
b. Emit a clear actionable error: `LOCAL FORK OUTDATED: local/sources/<component> is at vX.Y.Z but vA.B.C is required by <dep>`
c. Do NOT silently proceed with a broken build.
**Remediation (automatic, when invoked)**: The `local/scripts/bump-fork.sh`
script (or equivalent `repo bump-fork <component>` command) MUST:
1. Fetch the upstream source at the required version.
2. Apply all Red Bear patches from `local/patches/<component>/` using the
atomic patch application mechanism (see "Atomic Patch Application" below).
3. Update the version field in the local fork's `Cargo.toml`.
4. Commit the result to the `submodule/<component>` branch in the RedBear-OS repo.
5. Rebuild the affected packages.
**Symlink aliasing**: `recipes/<component>/` MUST be a symlink to
`../../../local/sources/<component>/` when a local fork exists. This ensures
backward compatibility for recipes that reference the `recipes/` path while
still preferring the local fork as the source of truth.
**Implementation status**: Detection is partially implemented via Cargo's
own lockfile collision errors. Full automatic detection and remediation
requires changes to `src/cook/fetch.rs` (version comparison logic) and the
addition of `local/scripts/bump-fork.sh`. Until fully automated, the manual
process is:
1. `cd local/sources/<component>`
2. Edit `Cargo.toml` version field to match upstream
3. `git pull` or fetch upstream at the new tag
4. Reapply all `local/patches/<component>/*.patch` (see AGENTS.md "Atomic Patch Application")
5. Commit to `submodule/<component>` branch
### Offline-First By Default
Red Bear OS is a **fork with frozen sources**. The cookbook tool defaults to
`COOKBOOK_OFFLINE=true` (changed from upstream Redox's `false`). Builds use archived
sources from `sources/redbear-0.1.0/` — no network access during compilation.
To allow online fetching for non-protected development recipes, use the `--upstream` flag:
To allow online fetching for non-protected development recipes:
```bash
COOKBOOK_OFFLINE=false make all CONFIG_NAME=redbear-full
```
Or use the `--upstream` flag of `build-redbear.sh`:
```bash
./local/scripts/build-redbear.sh --upstream redbear-mini
```
@@ -566,7 +514,7 @@ After ANY change to patches or `recipe.toml`:
3. Fetch: `repo --allow-protected fetch <recipe>`
4. Build: `repo cook <recipe>`
5. Verify no `FAILED`, `[ATOMIC] patch application rolled back`, or `.rej` files
6. Full image: `./local/scripts/build-redbear.sh <target>`
6. Full image: `make all CONFIG_NAME=<target>`
The `repo validate-patches` command (added 2026-05) performs a dry-run patch
application against clean upstream source in a temporary staging directory.
@@ -746,7 +694,7 @@ All custom work goes in `local/` — see `local/AGENTS.md` for fork model usage.
- Build requires Linux x86_64 host, 8GB+ RAM, 20GB+ disk
- QEMU used for testing (make qemu). VirtualBox also supported
- The `repo` binary (cookbook CLI) may crash with TUI in non-interactive environments — use `CI=1`
- 9 git submodules (core component forks on `submodule/<component>` branches). NO new branches and NO new submodules — see `local/AGENTS.md` § BRANCH AND SUBMODULE POLICY
- No git submodules — external repos managed via recipe source URLs and repo manifests
- Historical integration report removed (2026-04-16); see `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` for current state
### Stale Prefix Detection
@@ -759,12 +707,10 @@ base) gains new commits, the prefix must be rebuilt:
touch relibc && make prefix # Rebuild relibc in the cross-toolchain
```
`build-redbear.sh` **automatically rebuilds the prefix** when it detects that fork repos
(relibc, kernel, base) have commits newer than the prefix `libc.a`. This happens before any
recipe build begins. If the prefix rebuild fails, the build aborts immediately — recipes are
never compiled against a stale prefix. **A stale prefix is the #1 cause of "undefined reference"
link errors** after fork changes (e.g., adding `__freadahead` to relibc's `ext.rs` without
rebuilding the prefix).
`build-redbear.sh` includes a preflight check that warns when fork commits are newer than
prefix artifacts. **A stale prefix is the #1 cause of "undefined reference" link errors**
after fork changes (e.g., adding `__freadahead` to relibc's `ext.rs` without rebuilding the
prefix).
### relibc Header Circular Includes (Fixed)
+3 -287
View File
@@ -6,288 +6,6 @@ When a commit changes the visible system surface, supported hardware, build flow
or major documentation status, add a short note here and keep the README "What's New" section in
sync with the newest highlights.
## 2026-07-01 — Phase I/II complete: full s2idle + S3 entry + LG Gram DMI
### Phase I.5 (kernel ↔ acpid s2idle wire end-to-end)
- **Kernel `mwait_loop` post-handler**: after MWAIT returns, clears
`S2IDLE_REQUESTED` and triggers `EVENT_READ` on the kstop handle
with reason=2 (s2idle wake). Mirrors Linux 7.1
`acpi_s2idle_wake` in `drivers/acpi/sleep.c:758`.
- **Kernel `kstop` reason codes** (Phase I.5): `KSTOP_FLAG` is now
a `u8` with 0=idle, 1=shutdown (S5), 2=s2idle wake, 3=s3 wake.
`kstop_set_reason()` and the `CheckShutdown` AcpiVerb return the
reason. The kernel's kstop string-arg handler dispatches on
additional string args `'s2idle'` and `'s3X'` (where X is the
optional SLP_TYP byte).
- **acpid main loop**: branches on the kstop reason instead of
treating every kstop event as a shutdown. Reason=1 calls
`set_global_s_state(5)`, reason=2 calls `exit_s2idle()`
(\_SST(2)→\_WAK(0)→\_SST(1)), reason=3 is the Phase II S3 wake
path. `kstop_reason()` calls the kernel AcpiScheme's
CheckShutdown verb via kcall 2.
- **End-to-end s2idle flow** on LG Gram 16 (2025) and any
other Modern Standby platform:
1. acpid: `enter_s2idle()` (\_TTS(0), \_PTS(0), \_SST(3))
2. acpid: write `'s2idle'` to /scheme/sys/kstop
3. kernel kstop handler: sets S2IDLE_REQUESTED, returns
4. kernel idle path: `mwait_loop()` at deepest C-state
5. SCI breaks MWAIT
6. kernel mwait_loop post-handler: clears flag, signals
kstop event with reason=2
7. acpid: `kstop_reason()` returns 2
8. acpid: `exit_s2idle()` (\_SST(2)→\_WAK(0)→\_SST(1))
9. loop
### Phase II (S3 entry path)
- **Kernel FADT parser** (`acpi/fadt.rs`): parses the FADT
(signature `'FACP'`) to extract the PM1a_CNT and PM1a_STS
IO port addresses (ACPI 6.5 §5.2.9 / Table 5.6). 32-bit
General-Purpose Event Register Block 0 Addresses.
- **Kernel S3 entry** (`arch/x86_shared/stop.rs::enter_s3`):
hardware-agnostic S3 entry path mirroring Linux 7.1
`acpi_hw_legacy_sleep` in
`drivers/acpi/acpica/hwsleep.c:81-127`. Sequence:
1. clear WAK_STS (bit 15 of PM1a_STS)
2. flush CPU caches (wbinvd)
3. write SLP_TYP to PM1a_CNT
4. write SLP_TYP|SLP_EN to PM1a_CNT (split-write for
hardware compat)
5. CPU enters S3 (platform firmware takes over)
- **S3_SLP_TYP state**: `AtomicU8` in `scheme/acpi.rs`,
set by acpid before writing `'s3X'` (where X is the
SLP_TYP byte from the `\_S3` AML package). Default
SLP_TYP=5 (standard for x86 systems). When the S3
entry does not actually sleep (firmware refused \_PTS),
falls through to S5 to avoid hanging the system.
- **Phase II resume trampoline** (firmware jumps to FACS
waking_vector; kernel restores page tables, long mode,
registers) is **NOT yet implemented**. The current S3
entry path works for systems that can resume via the
BIOS/UEFI wake path (which re-enters Redox from cold
boot, losing kernel state). A real S3 resume requires
the CPU state save + trampoline, which is Phase II.X
(deferred).
- **Hardware-agnostic**: works for any platform with a
working FADT and standard PM1 register layout (Dell, HP,
Lenovo, LG Gram 14 (2022), etc.). Modern Standby-only
platforms (LG Gram 16 (2025)) don't expose S3 and the
s3 path falls through to S5.
### Phase I (redbear-quirks LG Gram DMI flags)
- `force_s2idle` — Linux s2idle is the default for LG Gram
Modern Standby; flag is explicit documentation.
- `acpi_irq1_skip_override` — Linux `drivers/acpi/resource.c`
`irq1_level_low_skip_override[]` (lines 522-534). Without
this the ACPI core rewrites the DSDT's ActiveLow to
ActiveHigh and the i8042 keyboard IRQ stops firing.
- `kbd_deactivate_fixup` — Linux `drivers/input/keyboard/atkbd.c`
line 1913-1917. Prevents spurious keyboard ACK / dropped
keys.
- `no_legacy_pm1b` — Red Bear OS specific. LG firmware does
not implement a separate PM1b_CNT register; tells acpid
to skip the SLP_TYPb write path.
### Phase J (deferred: libredox fork + syscall extension)
- The `AcpiVerb::EnterS2Idle` and `ExitS2Idle` extensions
for the syscall crate are written as a durable overlay
patch at `local/patches/syscall/P1-acpiverb-enter-exit-s2idle.patch`.
Applied to a local fork of `redox_syscall` at
`local/sources/syscall/`. NOT yet wired into the
base/kernel `Cargo.toml` `[patch.crates-io]` because
`libredox = "0.1.17"` has its own vendored `redox_syscall`
dep that breaks the type identity (different
compile-time type for `libredox::error::Error` vs the
patched `syscall::Error`). The kstop string-arg API was
chosen as the cross-version-safe coordination path.
### Build artifacts
- `build/x86_64/redbear-mini.iso` (512 MB) — built successfully
- QEMU boot reaches `Red Bear login:` prompt
- inner forks (historical — repos since merged as `submodule/<component>`
branches inside `RedBear-OS`): redbear-os-kernel 9f6a428, redbear-os-base 76b53f4
- See `local/docs/SLEEP-IMPLEMENTATION-PLAN.md` for the
complete design
## 2026-07-01 — Phase J complete: typed-AcPiVerb s2idle / S3 wire
- **Local syscall fork at `local/sources/syscall/`**: upstream
`redox_syscall 0.8.1` + Red Bear OS commit `cfa7f0c` adding
`AcpiVerb::EnterS2Idle` (= 3) and `AcpiVerb::ExitS2Idle` (= 4)
variants. The version field stays at upstream 0.8.1 per the
AGENTS.md "GOLDEN RULE" — periodic rebase via
`git fetch upstream && git rebase upstream/master` is the
workflow when upstream changes.
- **Local libredox fork at `local/sources/libredox/`**: upstream
`libredox 0.1.17` with the `redox_syscall` dep redirected
to `path = "../syscall"`. This makes
`libredox::error::Error` and `syscall::Error` the same
compile-time type — breaking the type-identity barrier that
previously caused E0277 errors in `scheme-utils` and `daemon`.
- **base `Cargo.toml`**: `[patch.crates-io] libredox = { path = "../libredox" }`
wires the local libredox fork. The existing
`[patch.crates-io] redox_syscall = { path = "../syscall" }`
is redundant (the base's workspace.dependencies already uses
the local path).
- **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" }`.
- **Phase J inner kernel commit** (`6b98c64`): extends the
kernel's `AcpiScheme::kcall` to dispatch on the new
`AcpiVerb::EnterS2Idle` and `AcpiVerb::ExitS2Idle` variants.
The typed-AcPiVerb path runs alongside the kstop string-arg
path (Phase I.5); both are functional.
- **Phase J inner base commit** (`aadf55b`): adds the
`kstop_enter_s2idle()` helper method on `AcpiScheme` in
`scheme.rs` that wraps the typed-AcPiVerb kcall. The acpid
main loop can call this directly.
- **Patch file**: `local/patches/syscall/P1-acpiverb-enter-exit-s2idle.patch`
is the durable overlay patch backing the syscall fork
commit. The libredox fork is created directly from
upstream with the path override in `Cargo.toml.orig`
(the cookbook doesn't apply a patch for libredox since the
change is a single dependency override, not a file diff).
- **Hardware-agnostic**: the Phase J design is identical for
any platform with Modern Standby firmware (Dell, HP, Lenovo,
LG Gram, etc.).
- **Build verification**: `redbear-mini.iso` (512 MB) builds
successfully with all Phase J commits. The patch system
works end-to-end.
## 2026-07-01 — Phase II.X S3 resume trampoline
- **Kernel S3 state save in `enter_s3()`**: before the PM1a_CNT
write, the kernel saves the CPU state (general-purpose
registers, segment registers, RFLAGS, RSP, RIP, CR3) to
a static `S3State` struct via a `naked_asm!` block. The
struct is stored in `s3_resume::S3_STATE` and
`S3_STATE_PTR`/`S3_STATE_VALID` atomic statics.
- **Kernel S3 resume trampoline** (`s3_resume::s3_trampoline`):
a 64-bit `naked_asm!` block that runs when the platform
firmware jumps to FACS.waking_vector on S3 wake. Mirrors
Linux 7.1 `arch/x86/kernel/acpi/wakeup_64.S`:
- Checks the magic value (0x123456789abcdef0) in
S3_STATE.saved_magic. If zero (cold boot), halts.
- Restores segment registers to __KERNEL_DS.
- Restores CR3 (page table base).
- Restores RSP, RFLAGS, 13 general-purpose registers.
- Sets the RESUMING_FROM_S3 flag.
- Pushes saved RIP onto the stack and uses `ret`.
- **Kernel exposes `s3_resume_address()`** that acpid writes
to FACS.waking_vector via the kernel AcpiScheme.
- **Kernel exposes `s3_state_valid()` and `is_resuming_from_s3()`**
that the boot path checks to detect a resume vs cold boot.
- **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 the FACS
waking_vector, so this trampoline is unused.
- **Build**: redbear-mini.iso (512 MB) builds successfully.
The S3 resume path is verified to compile and be present
in the ISO. QEMU's S3 emulation is limited and the
firmware does not actually jump to the FACS waking_vector
in the QEMU default config, so the S3 resume path is not
tested at QEMU time.
- **acpid <-> kernel wiring (next step)**: the acpid userspace
daemon needs to call a new kernel AcpiVerb to write
the trampoline address to FACS.waking_vector before
\_PTS(3). This is a separate Phase II.X.W commit.
## 2026-07-01 — Phase II.X.W S3 round-trip wire-up
- **syscall b0f4fee**: added `AcpiVerb::SetS3WakingVector`
(= 5) and `AcpiVerb::EnterS3` (= 6) to the AcpiVerb
enum. Hardware-agnostic: works on any x86_64
system with standard ACPI S3 support (Dell, HP, Lenovo,
LG Gram 14).
- **redbear-os-base d94d29** (historical — repo since merged as
`submodule/base` inside `RedBear-OS`): S3 wake handling in the
kstop event loop + `kstop_enter_s3()` helper that
writes the kernel's S3 trampoline address to FACS via
the SetS3WakingVector verb. Calls
`wake_from_sleep_state(3)` on S3 wake.
- **redbear-os-kernel 9bc1fbf**:
- **Comprehensive FACS parser** matching Linux 7.1's
`struct acpi_table_facs` from `include/acpi/actbl.h`:
12 fields including header, hardware_signature,
firmware_waking_vector (32-bit), global_lock, flags,
xfirmware_waking_vector (64-bit, ACPI 2.0+), version,
reserved[3], ospm_flags (ACPI 4.0+), reserved1[24].
- **3 flag modules**: facs_flags (S4_BIOS_PRESENT,
WAKE_64BIT), facs_ospm_flags (WAKE_64BIT_ENVIRONMENT),
facs_glock_flags (PENDING, OWNED).
- **FADT.x_firmware_ctrl + firmware_ctrl accessors**:
FADT offsets 140 and 36.
- **Sdt.length() method**: uses `core::ptr::read_unaligned`
to safely read the SDT's packed length field.
- **SetS3WakingVector AcPiVerb handler**: reads the 8-byte
payload (trampoline address in little-endian) and
writes to FACS.xfirmware_waking_vector. A zero payload
is a sentinel for "use the kernel's default
trampoline address" (s3_trampoline symbol).
- **acpi init** (src/acpi/mod.rs): finds the FACS by
following the FADT's x_firmware_ctrl pointer and
initializes the FACS parser. Logs a warning if FACS
is not found.
- **Full S3 round-trip flow** is now wired:
1. acpid: enter_sleep_state(3) does the AML prep
(`_TTS(3)`, `_PTS(3)`, `_SST(3)`)
2. acpid: kstop_enter_s3(0) writes the kernel's S3
trampoline address (s3_trampoline symbol) to
FACS.xfirmware_waking_vector
3. acpid: writes 's3' to /scheme/sys/kstop with the
SLP_TYP byte
4. kernel: stop::enter_s3 reads S3_SLP_TYP, writes
SLP_TYP|SLP_EN to PM1a_CNT
5. firmware: enters S3
6. ... on wake ... firmware jumps to FACS.waking_vector
7. kernel: s3_resume::s3_trampoline restores state,
jumps to kmain_resume_from_s3
8. acpid: receives kstop reason=3, runs
wake_from_sleep_state(3) (`_SST(2)` -> `_WAK(3)` ->
`_SST(1)`)
## 2026-07-01 — Build system: explicit patch verification
The user requested "build system must report complete when
upstream have our patches applied". This session adds the
explicit verification tools:
- **`local/scripts/check-cargo-patches.sh`** (Improvement C):
For each local source's `Cargo.toml`, scans for
`[patch.crates-io]` and `[patch."<URL>"]` sections,
resolves the patch via `cargo metadata`, and verifies
that the resolved source URL (or manifest_path for
path-deps) matches the expected local fork path. Returns
non-zero on any unresolved patch. Wraps `cargo metadata`
in a 30s timeout to handle large workspaces (relibc,
base). Explicitly skips relibc (its `[patch]` is only the
cc-rs git branch override, no `path` patches).
- **`make verify-patches`**: runs the above script. The
current kernel Phase J patch
([patch."<URL>"] redox_syscall) is verified
end-to-end.
- **`make verify-file-patches`**: runs
`local/scripts/check-unwired-patches.sh --strict` which
verifies all file-level .patch files in `local/patches/`
are referenced by at least one recipe.toml `patches = [...]`
entry.
- **`make verify-all`**: runs both. The comprehensive
Phase J end-to-end verification. Returns non-zero on any
failure (CI-friendly).
The cookbook itself already logs `[SUMMARY] All N patches
validated successfully` for file-level patches. These new
Makefile targets make the verification part of the standard
build workflow.
## 2026-07-01 — cpufreqd oscillation fixed (kernel MSR scheme + VM detection)
### Kernel fix: `sys` scheme path-strip ENOENT bug (kernel fork commit `c231262`)
@@ -661,10 +379,8 @@ versioning"):**
`local/AGENTS.md`. Documented the canonical server (gitea.redbearos.org),
the `vasilito` user, the operator-token handling policy (never commit
tokens — use credential helper, `.netrc`, or `$REDBEAR_GITEA_TOKEN`),
the repo map (historical at time of writing — `vasilito/redbear-os-base`,
`vasilito/redbear-os-kernel`, `vasilito/redbear-os-relibc`; since merged
as `submodule/<component>` branches inside `RedBear-OS` per the
SINGLE-REPO RULE in `local/AGENTS.md`),
the repo map (`vasilito/RedBear-OS`, `vasilito/redbear-os-base`,
`vasilito/redbear-os-kernel`, `vasilito/redbear-os-relibc`),
clone/remote-setup recipes, the cookbook auth path, push runbook,
Gitea API quick reference, and a full operator runbook including
credential recovery.
@@ -711,7 +427,7 @@ versioning"):**
### Intel GPU Driver Expansion
- Gen8-Gen12 supported: Skylake, Kaby Lake, Coffee Lake, Cannon Lake, Ice Lake, Tiger Lake, Alder Lake, DG2, Meteor Lake, Arrow Lake, Lunar Lake, Battlemage
- 200+ device IDs from Linux 7.1 i915 reference
- 200+ device IDs from Linux 7.0 i915 reference
- Gen4-Gen7 recognized with clear unsupported messages
- Display fixes: pipe count, page flip, EDID skeleton
Generated
+11 -137
View File
@@ -590,16 +590,6 @@ dependencies = [
"libc",
]
[[package]]
name = "libredox"
version = "0.1.18+rb0.3.0"
dependencies = [
"bitflags",
"libc",
"plain",
"redox_syscall",
]
[[package]]
name = "line-clipping"
version = "0.3.5"
@@ -747,12 +737,6 @@ dependencies = [
"toml",
]
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "portable-atomic"
version = "1.13.1"
@@ -872,7 +856,7 @@ dependencies = [
[[package]]
name = "redbear_cookbook"
version = "0.2.5"
version = "0.1.0"
dependencies = [
"ansi-to-tui",
"anyhow",
@@ -910,25 +894,15 @@ dependencies = [
[[package]]
name = "redox_installer"
version = "0.2.42+rb0.3.0"
version = "0.2.42"
dependencies = [
"anyhow",
"arg_parser",
"libc",
"libredox 0.1.18+rb0.3.0",
"ring",
"serde",
"serde_derive",
"toml",
]
[[package]]
name = "redox_syscall"
version = "0.9.0+rb0.3.0"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.5.2"
@@ -936,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"libredox 0.1.10",
"libredox",
"thiserror",
]
@@ -979,21 +953,6 @@ version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.16",
"libc",
"spin",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
@@ -1121,12 +1080,6 @@ version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "static_assertions"
version = "1.1.0"
@@ -1319,12 +1272,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "version_check"
version = "0.9.5"
@@ -1408,16 +1355,7 @@ version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
"windows-targets",
]
[[package]]
@@ -1435,29 +1373,13 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
@@ -1466,90 +1388,42 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.14"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "redbear_cookbook"
version = "0.2.5"
version = "0.1.0"
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
edition = "2024"
default-run = "repo"
-15
View File
@@ -44,21 +44,6 @@ cache-save-essential:
cache-verify:
@bash $(CACHE_RESTORE) --verify
# Phase J / Improvement C: verify that all Cargo [patch]
# sections in the local sources' Cargo.toml files resolve
# to the expected local fork paths. Run after `make all`
# to confirm Phase J end-to-end. Returns non-zero exit on
# any unresolved patch.
verify-patches:
@bash local/scripts/check-cargo-patches.sh
# Phase I: also run the file-level patch check
verify-file-patches:
@bash local/scripts/check-unwired-patches.sh --strict
# Verify everything: file-level patches + Cargo [patch] sections
verify-all: verify-file-patches verify-patches
cache-list:
@bash $(CACHE_SAVE) --list
+64 -155
View File
@@ -5,7 +5,7 @@
<h1 align="center">Red Bear OS</h1>
<p align="center">
<strong>A microkernel operating system written in Rust derived from <a href="https://www.redox-os.org">Redox OS</a>, built for bare metal.</strong>
<strong>A microkernel operating system written in Rust, derived from <a href="https://www.redox-os.org">Redox OS</a></strong>
</p>
<p align="center">
@@ -18,64 +18,40 @@
## What is Red Bear OS?
Red Bear OS is a general-purpose, Unix-like operating system with a **microkernel architecture**,
written entirely in **Rust**. It is a full fork of Redox OS (baseline 0.1.0), actively developed
on branch `0.2.5` with hardware enablement, multiple filesystems, a native greeter and login
system, and a KDE Plasma desktop path.
We aim to stay close to upstream Redox — diverging only where necessary to add missing
functionality, fix bugs, or support new hardware. The build system itself is under constant
active development alongside the OS.
It ships with several **first-in-class Rust-native tools** found nowhere else in the OS world:
- **cub** — an AUR-inspired package manager with pacman-style CLI (`-S`/`-Q`/`-R`) and a ratatui
TUI that converts Arch Linux PKGBUILDs into Red Bear recipes on the fly
- **tlc** (Twilight Commander) — a pure-Rust reimplementation of Midnight Commander; dual-panel
file manager, built-in editor and viewer, 8 color themes, 1204 unit tests, zero unsafe code
- **redbear-power** — interactive ratatui TUI for live CPU frequency, governor, and thermal
monitoring with on-the-fly P-state control
These are joined by dozens of `redbear-*` system utilities — `redbear-netctl` (network control),
`redbear-info` (hardware diagnostics), `redbear-acmd` (admin CLI), `redbear-mtr`,
`redbear-nmap`, `redbear-btctl`, and many more — all written in Rust, all built from source
alongside the OS.
Red Bear OS is a general-purpose, Unix-like operating system with a **microkernel architecture**, written in **Rust**. It is a full fork of Redox OS, frozen at release 0.1.0, with added hardware support, filesystem drivers, and a KDE Plasma desktop path.
**Goals**:
- **AMD & Intel parity** — equal-priority bare-metal support for both platforms
- **AMD & Intel parity** — first-class support for both platforms on bare metal
- **KDE Plasma desktop** — Wayland-based desktop environment via the KWin compositor
- **Hardware GPU acceleration** — AMD (amdgpu) and Intel GPU drivers via `redox-drm`
- **cub package ecosystem** — AUR → recipe.toml pipeline giving access to thousands of packages
- **First-class subsystems** — USB, WiFi, Bluetooth, ext4, FAT, GRUB, D-Bus (none optional)
- **Power management** — CPU frequency scaling, thermal monitoring, RAPL, sleep states
- **Offline-first, reproducible builds** — BLAKE3-verified source archives with content-hash caching
---
- **Hardware GPU acceleration** — AMD GPU (amdgpu) and Intel GPU drivers via `redox-drm`
- **Modern subsystems** — USB, WiFi, Bluetooth, ext4, GRUB, D-Bus
- **Offline-first builds** — reproducible from archived, BLAKE3-verified sources
## Our Git Server
Red Bear OS lives on a self-hosted Gitea instance at **https://gitea.redbearos.org**.
This is the canonical home no GitHub, GitLab, or Codeberg mirror is authoritative.
There is exactly **one repository**: all component sources (kernel, relibc, drivers,
system utilities) live here as submodule branches or tracked trees in `local/sources/`.
This is the canonical home for the fork — there is no GitHub / GitLab / Codeberg
mirror that is authoritative.
| Field | Value |
|----------|------------------------------------------------------|
| Host | `https://gitea.redbearos.org` |
| User | `vasilito` |
| Token | *(session-only — never stored in repo)* |
| Web UI | `https://gitea.redbearos.org/vasilito` |
| Main repo| `https://gitea.redbearos.org/vasilito/RedBear-OS` |
> Authentication tokens are per-session credentials — never stored in the repo.
> See [`local/AGENTS.md` § Our Git Server](./local/AGENTS.md) for the full operator runbook.
---
> **Token policy.** The `vasilito` token is a per-session credential and **must
> never** be committed to any tracked file. Use `git credential.helper` (store /
> cache / libsecret), `~/.netrc`, or `$REDBEAR_GITEA_TOKEN` env var. See
> [`local/AGENTS.md` § Our Git Server](./local/AGENTS.md) for the full operator
> runbook, mirror list, API reference, and recovery procedure.
## Quick Start
### Prerequisites
Linux x86_64 host with Rust nightly, QEMU, nasm, and standard build tools.
Linux x86_64 host with Rust nightly, QEMU, nasm, and standard build tools.
See the [Redox Build Guide](https://doc.redox-os.org/book/podman-build.html) for full setup.
### Build & Run
@@ -85,162 +61,95 @@ See the [Redox Build Guide](https://doc.redox-os.org/book/podman-build.html) for
git clone https://gitea.redbearos.org/vasilito/RedBear-OS.git
cd RedBear-OS
# Authenticated clone — supply token via env var
# Authenticated clone (one-off) — supply token via env var, not literal here
git clone https://vasilito:${REDBEAR_GITEA_TOKEN}@gitea.redbearos.org/vasilito/RedBear-OS.git
# Canonical build entry point
# Recommended: use the Red Bear wrapper
./local/scripts/build-redbear.sh redbear-mini # Text-only target
./local/scripts/build-redbear.sh redbear-full # Desktop-capable target
# Boot in QEMU
# Boot in QEMU with the resulting image
make qemu
```
> `local/scripts/build-redbear.sh` is the **only supported build entry point**. It handles
> `.config` parsing, prefix staleness detection, protected-recipe authorization, pre-cooking
> critical packages, and source fingerprint tracking. Direct `make` invocations bypass these
> gates. See [`AGENTS.md` § Build Commands](./AGENTS.md) for details.
> **Build script:** `local/scripts/build-redbear.sh` is the canonical entry point. Bare
> `make all` works but bypasses the `.config` checking and `REDBEAR_ALLOW_PROTECTED_FETCH=1`
> gates that `build-redbear.sh` enforces. See `AGENTS.md` § Build Commands for full details.
### Public Scripts
| Script | Purpose |
|--------|---------|
| `local/scripts/build-redbear.sh` | **Canonical** build wrapper for redbear-mini/full/grub |
| `scripts/run.sh` | Build and run in QEMU (`-b` to build, `-c <config>` for target) |
| `scripts/build-iso.sh` | Build a live ISO for bare-metal boot |
| `scripts/build-all-isos.sh` | Build all live ISO targets |
| `scripts/network-boot.sh` | PXE network boot helper |
| `scripts/dual-boot.sh` | Dual-boot installation helper |
### Config Targets
| Target | Type | Description |
|--------|------|-------------|
| `redbear-full` | Desktop-capable | GPU drivers + Wayland compositor + Qt 6.11.1 + KF6 6.27.0 + KWin + SDDM + greeter + D-Bus |
| `redbear-mini` | Console | Text-only recovery / install target with tlc, cub, and redbear-* utilities |
| `redbear-full` | Desktop | Wayland + KDE + GPU drivers + D-Bus services |
| `redbear-mini` | Console | Text-only recovery / install target |
| `redbear-grub` | Console | Text-only with GRUB boot manager |
---
## Current Status
Red Bear OS **boots to a login prompt** in QEMU with working wired networking, D-Bus system bus,
hardware detection daemons, and three filesystem backends (RedoxFS, ext4, FAT). The ISO builds
successfully on branch `0.2.5`. Graphics packages are frozen at latest upstream stable
(Qt 6.11.1, KF6 6.27.0, Plasma 6.7.2, SDDM 0.21.0, Mesa 24.0.8).
Red Bear OS **boots to a login prompt** in QEMU with working wired networking, D-Bus system bus, hardware detection daemons, and filesystem support (RedoxFS, ext4, FAT).
| Area | Status |
|------|--------|
| Boot (ACPI, x2APIC, SMP) | ✅ Bare-metal proven — Ryzen Threadripper 128-thread verified |
| Boot (ACPI/x2APIC/SMP) | ✅ Bare-metal proven |
| Userspace drivers (PCI, storage, net) | ✅ Working in QEMU |
| Filesystems — RedoxFS, ext4, FAT | ✅ Scheme daemons + mkfs/fsck tools |
| D-Bus system bus + services | ✅ Working — login1, PolicyKit, UDisks, UPower |
| **cub** package manager | 🟡 17-module Rust workspace; AUR → recipe pipeline; 70+ tests |
| **tlc** file manager | 🟡 113 .rs files, 46k+ lines; 986 tests; 8 skins; VFS archives |
| IRQ / PCI / MSI-X / IOMMU | 🟡 QEMU-proven; hardware validation open |
| POSIX gaps (relibc) | 🟡 ~85% coverage; ~38 active patches |
| DRM/KMS display drivers | 🟡 AMD + Intel + virtio-gpu compile; HW validation open |
| Mesa — llvmpipe + virgl | 🟡 Builds (`virtio_gpu_dri.so`, 17.4 MB); virgl EGL runtime probe open |
| SDDM display manager + Greeter/Login | 🟡 Wired in `redbear-full`; graphical login blocked by Qt6 Wayland crash |
| Qt 6.11.1 (Core, Gui, DBus, Wayland) | 🟡 Builds successfully; Wayland `null+8` crash blocks runtime |
| KF6 Frameworks — 40/40 | 🟡 All frameworks build; KWin cooks successfully |
| Wayland compositor | 🟡 Bounded proof; blocked by Qt6 Wayland protocol crash |
| KDE Plasma / KWin | 🔴 Blocked by Qt6 Wayland crash in `wl_proxy_add_listener` |
| WiFi (Intel iwlwifi) | 🟡 VFIO/passthrough bounded runtime validation framework exists |
| USB / Bluetooth | 🔴 USB maturity work in progress; Bluetooth controller path planned |
**Where help is most wanted:**
Qt6 Wayland protocol crash — the #1 blocker for graphical desktop · AMD/Intel GPU hardware validation on bare metal · USB controller maturity · WiFi native control plane · cub AUR pipeline hardening · package maintainers for the growing recipe catalog · tlc VFS remote backends and archive support
---
| D-Bus system bus + services | ✅ Working (login1, PolicyKit, UDisks, UPower) |
| ext4 / FAT filesystems | ✅ Compiles, installer-wired |
| POSIX gaps (relibc) | 🚧 Bounded Wayland-facing support |
| DRM/KMS display drivers | 🚧 AMD + Intel compile; HW validation pending |
| Wayland compositor | 🚧 Bounded proof; Qt6/KF6 clients crash at init |
| KDE Plasma desktop | 🔄 In progress (Qt6/KF6 compile; KWin/QML blocked) |
| WiFi / Bluetooth | 📋 Planned (architected, implementation pending) |
## How It Works
Red Bear OS uses a **userspace driver model** — all drivers run as unprivileged daemons
communicating through the kernel's scheme-based IPC.
Red Bear OS uses a **userspace driver model** — all drivers run as unprivileged daemons:
```
┌─────────────────────────────────────────────────────────────────┐
│ KERNEL (microkernel) │
│ schemes: memory · irq · event · pipe · debug │
└──────────────────────────┬──────────────────────────────────────┘
┌─────────────────────┼─────────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ pcid │ │ e1000d xhcid │ │ vesad redox-drm │
│ PCI enum │ │ Intel USB 3.0 │ │ fbdev GPU manager │
└──────────┘ └──────────────────┘ └──────────────────────┘
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ ext4d │ │ ps2d evdevd │ │ thermald cpufreqd │
│ fatd │ │ KB+mouse input │ │ thermal CPU freq │
└──────────┘ └──────────────────┘ └──────────────────────┘
┌──────────┐ ┌──────────────────┐ ┌──────────────────────┐
│ iommu │ │ acpid │ │ dbus-daemon │
│ DMA map │ │ power mgmt │ │ system + session │
└──────────┘ └──────────────────┘ └──────────────────────┘
Kernel (microkernel)
└── schemes: memory, irq, event, pipe, debug
└── Driver daemons (userspace)
├── pcid → PCI enumeration
├── e1000d → Intel ethernet
├── xhcid → USB controller
└── vesad → Display framebuffer
```
The kernel provides minimal services: memory, interrupts, and IPC. Everything else —
filesystems, networking, graphics, input, power management, D-Bus — runs in userspace.
Hardware quirks are handled by a data-driven system in `redox-driver-sys` with compiled-in
tables, TOML runtime configuration, and DMI matching.
---
## Engineering Standards
Red Bear OS operates under strict discipline. Full policies: [`local/AGENTS.md`](./local/AGENTS.md).
| Rule | |
|------|---|
| **Never delete to "fix" a build** | If a package breaks, fix the root cause. Never remove, ignore, or comment out a package, service, or config to make a build pass. |
| **Zero stubs** | No fake headers, `#ifdef` no-ops, or "make it compile" shortcuts. Missing functionality must be implemented properly in the right component. |
| **Single repository** | All component sources live here — no per-component repos. 9 `submodule/<component>` branches. |
| **Local fork model** | Core components (kernel, relibc, base, etc.) maintained as local forks in `local/sources/` with immutability guarantees. |
| **Adapt to upstream** | Red Bear adapts to upstream API/ABI changes — never pins, downgrades, or holds back a dependency. |
| **Free/libre only** | No proprietary, source-unavailable, or redistributability-restricted dependencies. MIT licensed. |
---
The kernel provides minimal services (memory, interrupts, IPC). Everything else — filesystems, networking, graphics, input — runs in userspace.
## Documentation
- [Desktop Path Plan](local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md) — Canonical plan v6.0: kernel → DRM → Mesa → Wayland → KDE
- [Implementation Plan](docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md) — Roadmap and execution model
- [cub Package Manager](local/docs/CUB-PACKAGE-MANAGER.md) — AUR → recipe pipeline, CLI reference, architecture
- [tlc File Manager](local/recipes/tui/tlc/README.md) — Pure-Rust Midnight Commander replacement
- [D-Bus Integration](local/docs/DBUS-INTEGRATION-PLAN.md) — Session bus architecture
- [IRQ & Low-Level Controllers](local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md) — IRQ delivery, MSI/MSI-X, IOMMU
- [Greeter & Login](local/docs/GREETER-LOGIN-IMPLEMENTATION-PLAN.md) — Native greeter, auth daemon, session launch
- [DRM Modernization](local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md) — DRM/KMS display and render maturity
- [USB Plan](local/docs/USB-IMPLEMENTATION-PLAN.md) — USB stack design and implementation
- [WiFi Plan](local/docs/WIFI-IMPLEMENTATION-PLAN.md) — Wireless architecture and driver plan
- [Bluetooth Plan](local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md) — Bluetooth stack design
- [Build Cache](local/docs/BUILD-CACHE-PLAN.md) — Content-hash (BLAKE3) build cache system
- [Build System Hardening](local/docs/BUILD-SYSTEM-HARDENING-PLAN.md) — Collision detection, init service validation
- [Quirks System](local/docs/QUIRKS-SYSTEM.md) — Hardware quirks infrastructure
- [Documentation Index](docs/README.md) — Full doc map
---
- [Implementation Plan](docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md) — roadmap and execution model
- [Desktop Path Plan](local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md) — kernel → DRM → Mesa → Wayland → KDE
- [D-Bus Integration](local/docs/DBUS-INTEGRATION-PLAN.md) — session bus architecture
- [USB Plan](local/docs/USB-IMPLEMENTATION-PLAN.md) — USB stack design
- [WiFi Plan](local/docs/WIFI-IMPLEMENTATION-PLAN.md) — wireless architecture
- [Bluetooth Plan](local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md) — BT stack design
- [Documentation Index](docs/README.md) — full doc map
## Contributing
Red Bear OS is a **full fork** of Redox OS. Upstream sources are frozen and archived; all
custom work lives in `local/` and survives every build operation.
Red Bear OS uses a **full fork** model. Upstream Redox sources are frozen and archived. All custom work lives in `local/`:
```
local/
├── sources/ # Local forks of core components (kernel, relibc, base, bootloader, …)
├── recipes/ # Custom packages — drivers, GPU stack, system daemons, branding
├── patches/ # Durable changes to upstream source trees
├── docs/ # Integration and planning documentation
── scripts/ # Build, test, validation, and release tooling
├── recipes/ # Custom packages (drivers, GPU, system)
── docs/ # Integration and planning docs
└── scripts/ # Build, test, and release tooling
```
### We're Looking For
| Role | What you'd work on |
|------|--------------------|
| **Package maintainers** | Port and maintain AUR packages through cub's pipeline; write and test recipe.toml files for Red Bear OS; improve the PKGBUILD → recipe conversion |
| **Driver developers** | AMD/Intel GPU drivers, USB controller maturity, WiFi native control plane, Bluetooth |
| **Graphics stack engineers** | Qt6 Wayland crash fix (the #1 desktop blocker), Mesa virgl runtime, KWin Wayland compositor |
| **Systems/Rust engineers** | Kernel syscalls, relibc POSIX gaps, filesystem daemons, D-Bus services, hardware quirks |
| **TUI/app developers** | tlc feature completion, cub TUI polish, redbear-power enhancements, new redbear-* utilities |
Contributions are welcome with or without AI assistance — we care about **quality**, not how
the code was produced. Pick an area from the status table above, check the relevant plan doc,
and dive in.
---
We welcome contributions made with or without AI assistance — we care about **quality**, not how the code was produced.
## License
+2 -8
View File
@@ -34,10 +34,7 @@ path = "/etc/init.d/30_console.service"
data = """
[unit]
description = "Console terminals"
requires_weak = [
"29_activate_console.service",
"00_ptyd.service",
]
requires_weak = ["29_activate_console.service"]
[service]
cmd = "getty"
@@ -50,10 +47,7 @@ path = "/etc/init.d/31_debug_console.service"
data = """
[unit]
description = "Debug console"
requires_weak = [
"29_activate_console.service",
"00_ptyd.service",
]
requires_weak = ["29_activate_console.service"]
[service]
cmd = "getty"
+1 -1
View File
@@ -519,7 +519,7 @@ requires_weak = [
[service]
cmd = "pcid-spawner"
type = "oneshot_async"
type = "oneshot"
"""
# Firmware fallback chain configs
+12 -83
View File
@@ -56,10 +56,8 @@ thermald = {}
redbear-power = {}
hwrngd = {}
redbear-acmd = {}
redbear-ftdi = {}
redbear-ecmd = {}
redbear-usbaudiod = {}
redbear-usb-hotplugd = {}
driver-params = {}
# ── PCI device database (critical for PCI driver matching) ──
@@ -92,8 +90,8 @@ iommu = {}
# ── Standard CLI tools (from server profile) ──
bash = {}
bottom = {}
curl = {}
diffutils = {}
#curl = {} # suppressed: nghttp2 dependency chain fails; curl not needed for boot/recovery
diffutils = "ignore" # suppressed: relibc/gnulib header gaps; not needed for boot/recovery or greeter proof
findutils = {}
uutils-tar = {}
bison = {}
@@ -173,21 +171,6 @@ cmd = "audiod"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/02_usb_hotplug.service"
data = """
[unit]
description = "USB device hotplug daemon (auto-spawns class drivers)"
requires_weak = [
"00_base.target",
"00_pcid-spawner.service",
]
[service]
cmd = "redbear-usb-hotplugd"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/02_serial_probe.service"
data = """
@@ -207,7 +190,7 @@ type = "oneshot"
path = "/etc/init.d/00_gpiod.service"
data = """
[unit]
description = "GPIO controller registry"
description = "GPIO controller registry (non-blocking on live-mini)"
requires_weak = [
"00_base.target",
]
@@ -221,7 +204,7 @@ type = { scheme = "gpio" }
path = "/etc/init.d/00_i2cd.service"
data = """
[unit]
description = "I2C adapter registry"
description = "I2C adapter registry (non-blocking on live-mini)"
requires_weak = [
"00_base.target",
]
@@ -235,7 +218,7 @@ type = { scheme = "i2c" }
path = "/etc/init.d/00_i2c-dw-acpi.service"
data = """
[unit]
description = "DesignWare ACPI I2C controller"
description = "DesignWare ACPI I2C controller (non-blocking)"
requires_weak = [
"00_i2cd.service",
]
@@ -249,7 +232,7 @@ type = "oneshot_async"
path = "/etc/init.d/00_intel-gpiod.service"
data = """
[unit]
description = "Intel ACPI GPIO registrar"
description = "Intel ACPI GPIO registrar (non-blocking)"
requires_weak = [
"00_gpiod.service",
"00_i2cd.service",
@@ -264,7 +247,7 @@ type = "oneshot_async"
path = "/etc/init.d/00_i2c-gpio-expanderd.service"
data = """
[unit]
description = "I2C GPIO expander companion bridge"
description = "I2C GPIO expander companion bridge (non-blocking on live-mini)"
requires_weak = [
"00_i2cd.service",
"00_gpiod.service",
@@ -279,7 +262,7 @@ type = "oneshot_async"
path = "/etc/init.d/00_i2c-hidd.service"
data = """
[unit]
description = "ACPI I2C HID bring-up daemon"
description = "ACPI I2C HID bring-up daemon (non-blocking)"
requires_weak = [
"00_i2cd.service",
"00_i2c-dw-acpi.service",
@@ -296,7 +279,7 @@ type = "oneshot_async"
path = "/etc/init.d/00_ucsid.service"
data = """
[unit]
description = "USB-C UCSI topology detector"
description = "USB-C UCSI topology detector (non-blocking on live-mini)"
requires_weak = [
"00_base.target",
"00_i2cd.service",
@@ -509,7 +492,7 @@ requires_weak = ["00_base.target"]
[service]
cmd = "inputd"
args = ["-A", "2"]
type = "oneshot_async"
type = "oneshot"
"""
[[files]]
@@ -529,10 +512,7 @@ path = "/etc/init.d/30_console.service"
data = """
[unit]
description = "Console terminals"
requires_weak = [
"29_activate_console.service",
"00_ptyd.service",
]
requires_weak = ["29_activate_console.service"]
[service]
cmd = "getty"
@@ -545,61 +525,10 @@ path = "/etc/init.d/31_debug_console.service"
data = """
[unit]
description = "Debug console"
requires_weak = [
"29_activate_console.service",
"00_ptyd.service",
]
requires_weak = ["29_activate_console.service"]
[service]
cmd = "getty"
args = ["/scheme/debug/no-preserve", "-J"]
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/00_pcid-spawner.service"
data = """
[unit]
description = "PCI driver spawner (non-blocking on live-mini)"
[service]
cmd = "pcid-spawner"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/00_ipcd.service"
data = """
[unit]
description = "Inter-process communication daemon (non-blocking on live-mini)"
[service]
cmd = "ipcd"
type = "oneshot_async"
"""
[[files]]
path = "/etc/init.d/13_ftdi-probe.service"
data = """
[unit]
description = "FTDI USB serial probe (non-blocking on redbear-mini)"
requires_weak = [
"00_base.target",
]
[service]
cmd = "echo"
args = ["RB_FTDI_PROBE_OK"]
type = "oneshot"
"""
[[files]]
path = "/etc/init.d/00_ptyd.service"
data = """
[unit]
description = "Pseudo-terminal daemon (non-blocking on live-mini)"
[service]
cmd = "ptyd"
type = "notify"
"""
-2
View File
@@ -116,7 +116,6 @@ data = """
description = "Console terminals"
requires_weak = [
"29_activate_console.service",
"00_ptyd.service",
]
[service]
@@ -148,7 +147,6 @@ data = """
description = "Debug console"
requires_weak = [
"29_activate_console.service",
"00_ptyd.service",
]
[service]
+18 -10
View File
@@ -121,12 +121,15 @@ desktop-capable target.
These produce images such as `build/x86_64/harddrive.img` or `build/x86_64/redbear-mini.iso`.
### Bare `make all` (Not Supported)
### Bare `make all` (Legacy / Advanced)
Direct `make` invocations bypass the policy gates (`.config` checking,
`REDBEAR_ALLOW_PROTECTED_FETCH=1`, prefix staleness detection, local-over-WIP
enforcement, pre-cooking, source fingerprint tracking) that `build-redbear.sh`
enforces. **Always use `build-redbear.sh`.**
```bash
make all CONFIG_NAME=redbear-mini
```
Bare `make all` works but bypasses the policy gates (`.config` checking,
`REDBEAR_ALLOW_PROTECTED_FETCH=1`, etc.) that `build-redbear.sh` enforces. Prefer the wrapper
unless you specifically need to bypass those gates.
### Export External Toolchain
@@ -149,20 +152,25 @@ make export-toolchain TARGET=x86_64-unknown-redox \
### Build with Specific Config
```bash
# Canonical Red Bear wrapper (produces live ISO):
# Preferred Red Bear wrapper:
./local/scripts/build-redbear.sh redbear-mini
./local/scripts/build-redbear.sh redbear-full
./local/scripts/build-redbear.sh redbear-grub
# Direct make is still valid when needed:
make all CONFIG_NAME=redbear-full
```
For tracked Red Bear work, prefer these three compile targets over older historical names.
### Live ISO
### Build a Live ISO
`build-redbear.sh` already produces a live ISO (it calls `make live` internally).
Output: `build/<arch>/<config>.iso`
```bash
make live CONFIG_NAME=redbear-full
# Produces: build/x86_64/redbear-live.iso
```
Live `.iso` outputs are for real bare-metal boot, install, recovery, and demo workflows.
Live `.iso` outputs are for real bare-metal boot, install, recovery, and demo workflows. They are not the VM/QEMU execution surface; for virtualization, use `make qemu` and the `harddrive.img` path instead.
### Rebuild After Changes
+3 -3
View File
@@ -100,7 +100,7 @@ This summary is only a quick orientation layer. For canonical current-state deta
- and the active subsystem plans under `local/docs/` for detailed current workstreams.
- **Compile targets**: the supported compile targets are `redbear-mini`, `redbear-full`, and `redbear-grub`
- **Live ISO policy**: live `.iso` outputs (`build-redbear.sh`) are for real bare-metal boot/install/recovery workflows, not the VM/QEMU execution surface.
- **Live ISO policy**: live `.iso` outputs (`make live`) are for real bare-metal boot/install/recovery workflows, not the VM/QEMU execution surface.
- **Wayland**: libwayland + wayland-protocols built. A bounded greeter/compositor-backed login proof now passes, but broader compositor/runtime stability remains incomplete.
- **Qt6**: qtbase 6.11.0 (Core+Gui+Widgets+DBus+Wayland), qtdeclarative, qtsvg, qtwayland ALL BUILT
- **D-Bus**: 1.16.2 built for Redox. Qt6DBus enabled.
@@ -134,8 +134,8 @@ cargo install just cbindgen
# 3. Configure for native build (no Podman)
echo 'PODMAN_BUILD?=0' > .config
# 4. Build (canonical command — produces live ISO)
./local/scripts/build-redbear.sh redbear-mini
# 4. Build (downloads cross-toolchain, then compiles)
make all
# 5. Run in QEMU
make qemu
+49 -567
View File
@@ -56,285 +56,6 @@ This is the only canonical home for our fork — there is no GitHub / GitLab / C
mirror that is treated as authoritative. All Red Bear custom work, including local
recipe sources that have no upstream, lives here.
### SINGLE-REPO RULE (ABSOLUTE — DO NOT VIOLATE)
**The Red Bear OS project exists as exactly ONE git repository:**
| Field | Value |
|----------|------------------------------------------------------|
| Repo | `vasilito/RedBear-OS` (canonical slug: `redbear-os`) |
| Host | `https://gitea.redbearos.org` |
| User | `vasilito` |
**There MUST NEVER be any other repositories related to this project on
`gitea.redbearos.org`.** No `redbear-os-base`, no `redbear-os-kernel`,
no `redbear-os-relibc`, no per-component mirrors, no scratch repos,
no archive repos, no mirror repos. Nothing.
Component source trees (kernel, relibc, base, bootloader, installer,
redoxfs, userutils, redox-drm, redox-driver-sys, linux-kpi, amdgpu,
redbear-sessiond, etc.) are **NOT separate repositories**. They live
inside `RedBear-OS` either as:
- **Submodules** — pinned to a specific commit, each on its own branch
inside this same `RedBear-OS` repo (`submodule/<component>` branches),
OR
- **Tracked trees under `local/sources/<component>/`** — full source
snapshots committed directly to the `RedBear-OS` repo as ordinary
files, versioned by Red Bear commits (not by external git history),
OR
- **Local recipes under `local/recipes/<category>/<name>/source/`** —
original Red Bear projects (tlc, redbear-*, cub, etc.) with no upstream
outside this repo.
**WE DO NOT CREATE NEW REPOSITORIES.** If a component needs a Red Bear
fork and does not already have a `submodule/<component>` branch, adding
one is an **operator-only** decision. Agents **MUST NOT** create new
branches or new repos on their own.
Operators and agents **MUST NOT**:
- create a new repository on `gitea.redbearos.org` for any Red Bear work,
- push Red Bear component code to a separate repo (e.g. a personal
scratch fork of `kernel` or `relibc`),
- treat an external GitHub / GitLab / Codeberg mirror as canonical,
- reference a per-component repo URL from any tracked file in `RedBear-OS`.
If a recipe's `[source]` section currently points at a separate repo
URL (e.g. `https://gitea.redbearos.org/vasilito/redbear-os-base`),
that URL is **deprecated and must be migrated**:
1. Push the component's source tree to the existing `submodule/<component>`
branch inside `RedBear-OS`.
2. Update the submodule's local `.git/config` origin to
`https://gitea.redbearos.org/vasilito/RedBear-OS.git`.
3. Replace the recipe's `git = "..."` URL with the in-repo submodule
path, and ensure `.gitmodules` references `branch = submodule/<component>`.
4. Delete the per-component repository on Gitea.
5. Update `local/AGENTS.md` and any other references.
**Enforcement.** The cookbook's fetch/validation path treats any
component source fetched from outside `RedBear-OS` as a misconfiguration.
Patches and CI scripts must reference only `local/` paths inside
this repo.
**Why this rule exists.** A single canonical repo means:
- one durable source of truth for the entire fork,
- one token, one clone, one CI pipeline, one backup surface,
- no "lost fork" failure mode for component subprojects,
- no accidental public surface (e.g. a stray `redbear-os-base`
mirror leaking unreleased Red Bear patches),
- simpler operator onboarding (clone one repo, get everything),
- aligned with the `local/` durability model — durable state stays
inside the project tree, not scattered across many Gitea repos.
### BRANCH AND SUBMODULE POLICY (ABSOLUTE — DO NOT VIOLATE)
**WE DO NOT CREATE NEW BRANCHES.** All work happens on existing branches.
Operators and agents **MUST NOT** create new git branches of any kind — no
feature branches, no topic branches, no version-specific fork branches
(e.g. `redoxfs-0.9.0-rb1`), no scratch branches. Ever.
The **only** branches that exist in the `RedBear-OS` repo are:
| Branch type | Examples | Who creates them |
|---|---|---|
| **Release branches** | `master`, `0.2.0`, `0.2.1`, …, `0.2.5`, … | **Operator only**, one per Red Bear OS release cycle. |
| **Submodule branches** | `submodule/base`, `submodule/kernel`, `submodule/relibc`, … (9 total) | **Operator only**, one per forked upstream component. |
| **Recovery branches** | `recovered/quirks` | **Operator only**, for emergency recovery work. |
Agents **MUST NOT**:
- `git checkout -b <anything>` — no new branches, no exceptions.
- `git push origin HEAD:refs/heads/<new-branch>` — no pushing new branches.
- create a branch named after a version (e.g. `redoxfs-0.9.0-rb1`) — this is
a **policy violation**. Version metadata lives in `Cargo.toml` version
fields (as `+rbN` build metadata), not in branch names.
- create a branch named `submodule/<new-component>` unless the operator has
explicitly decided to fork a new upstream component.
**If you need to work on a change, commit to the branch you are already on.**
Release work goes on the current release branch (e.g. `0.2.5`). Fork changes
go on the appropriate `submodule/<component>` branch. There is no
justification for a new branch — use a commit, a patch file, or a tracked
tree instead.
#### Submodules are the unit of work
Each local forked upstream subproject IS a submodule. There are exactly
**9 declared submodules** (as of 2026-07-01):
| Submodule | Branch | Path |
|---|---|---|
| base | `submodule/base` | `local/sources/base` |
| bootloader | `submodule/bootloader` | `local/sources/bootloader` |
| installer | `submodule/installer` | `local/sources/installer` |
| kernel | `submodule/kernel` | `local/sources/kernel` |
| libredox | `submodule/libredox` | `local/sources/libredox` |
| redoxfs | `submodule/redoxfs` | `local/sources/redoxfs` |
| relibc | `submodule/relibc` | `local/sources/relibc` |
| syscall | `submodule/syscall` | `local/sources/syscall` |
| userutils | `submodule/userutils` | `local/sources/userutils` |
**We work on existing submodules.** If a component needs Red Bear changes:
1. **Check if a submodule already exists** for that component (see table above
and `.gitmodules`). If yes, work on that submodule's branch.
2. **If no submodule exists**, the component lives as a **tracked tree**
under `local/sources/<name>/` (e.g. `redox-scheme`) or as a **local
recipe** under `local/recipes/<name>/source/`. Commit changes directly
to the current release branch — no new submodule branch needed.
3. **Creating a new submodule** is an **operator-only decision** and should
be questioned: *"We can create a new submodule but why? We just work on
existing submodules."* New submodules are only justified when a component
is large enough and upstream-tracked enough that submodule pinning
provides real value over a tracked tree.
#### Operator override — agents MAY create submodules when really necessary (2026-07-07)
> **Authorization:** Operator explicitly granted agents permission to create
> new submodules on 2026-07-07 ("agents CAN create submodules, document this —
> but only when really necessary"). Without an explicit necessity case, the
> default preference remains "work on existing submodules first".
>
> **What "really necessary" means (default-closed exception).** A new
> submodule is justified only when **all** of the following are true:
>
> 1. **Upstream-tracked.** The component has its own upstream commit history
> that Red Bear needs to track, rebase against, or import from.
> 2. **Large.** The component is big enough that a tracked tree would
> meaningfully bloat the parent repo on every clone (rough heuristic:
> >10MB of source and growing).
> 3. **Pinning has real value.** Submodule pinning (specific commit) provides
> a correct-dependency guarantee that a tracked tree or local recipe
> cannot — e.g. the component must be at the same commit across all
> consumers, or its build depends on the parent's commit graph.
> 4. **No smaller option.** A local recipe (`local/recipes/<cat>/<name>/source/`)
> or a tracked tree (`local/sources/<name>/`) cannot serve equivalently.
>
> If any of (1)(4) fails, **do not** create a new submodule. Use a tracked
> tree or local recipe instead.
>
> **Scope:** When justified, agents may now create a new
> `submodule/<component>` branch on `RedBear-OS` and add an entry to
> `.gitmodules`. The default preference remains "work on existing submodules
> first" — adding a new one is the exception, not the baseline.
>
> **Pre-create checklist (must be in the agent's commit message).** Before
> `git submodule add`:
>
> - **Operator instruction cited.** Quote the operator's words exactly that
> justify this submodule.
> - **Necessity test passed.** For each of (1)(4) above, a one-line answer
> with file/path evidence.
> - **Alternatives rejected.** Concrete reason a tracked tree or local recipe
> is not equivalent.
> - **Inventory impact.** How the 9-declared-submodule table above changes
> (becomes 10 declared submodules, new `<submodule>` row added).
> - **Operator review notice.** A line saying "operators should review this
> `.gitmodules` change before merge to a release branch".
>
> **What this does NOT change:**
>
> - The 9-declared-submodule table above is still the canonical inventory
> until a real necessity case lands.
> - Tracked trees under `local/sources/<name>/` and local recipes under
> `local/recipes/<name>/source/` are still the preferred home for new
> Red Bear code unless pinning matters.
> - All other single-repo, branch, fork, and durability rules in this file
> remain absolute.
> - The override still records an operator decision; it is not a relaxation
> of accountability.
>
> **Log requirement:** Every new submodule added by an agent under this
> override must commit a short note next to the `.gitmodules` change giving
> the operator instruction, the date, the necessity-test evidence, and the
> alternatives-rejected reasoning. This keeps the override auditable.
>
> **Current status of the override:** **Not yet exercised.** No new submodule
> has been created under this override as of 2026-07-07. All Red Bear USB and
> PCI subsystems continue to live as local recipes under `local/recipes/`,
> which satisfies the "work on existing patterns first" preference.
#### What to do with a stray branch
If a branch was created in violation of this policy (e.g.
`redox-scheme-0.11.2-rb1`):
1. Cherry-pick or merge any useful commits back onto the correct branch
(the release branch for tracked trees, or the `submodule/<component>`
branch for declared submodules).
2. Delete the stray branch: `git branch -D <stray-branch>`.
3. Delete it on the remote if it was pushed: `git push origin --delete <stray-branch>`.
4. Document the cleanup in the commit message.
### Migration status (as of 2026-07-01)
**Migration complete.** Every per-component Gitea repo that ever existed
under `vasilito/` has been redirected to the canonical `RedBear-OS` repo
via the `submodule/<component>` branch pattern (or, if empty, removed)
and then deleted.
**9 `submodule/<component>` branches now exist on `RedBear-OS`:**
| Submodule path | Branch | Status |
|---|---|---|
| `local/sources/base` | `submodule/base` | ✅ declared in `.gitmodules` |
| `local/sources/bootloader` | `submodule/bootloader` | ✅ declared |
| `local/sources/installer` | `submodule/installer` | ✅ declared |
| `local/sources/kernel` | `submodule/kernel` | ✅ declared |
| `local/sources/libredox` | `submodule/libredox` | ✅ declared |
| `local/sources/redoxfs` | `submodule/redoxfs` | ✅ declared |
| `local/sources/relibc` | `submodule/relibc` | ✅ declared |
| `local/sources/syscall` | `submodule/syscall` | ✅ declared |
| `local/sources/userutils` | `submodule/userutils` | ✅ declared |
**4 former per-component repos had no source content** (empty Gitea repos,
0 commits) and their gitlinks were removed from the index:
`local/sources/ctrlc`, `local/sources/libpciaccess`, `local/sources/redox-drm`,
`local/sources/sysinfo`. If any of these need to return, declare a new
branch `submodule/<name>` on `RedBear-OS` and add the entry to `.gitmodules`.
**Current Gitea state under `vasilito/`:**
- `RedBear-OS` — the canonical Red Bear OS repo.
- `hiperiso` — unrelated personal project (kept per operator request).
That is **all**. No other repos.
### Migration procedure (executed; reference for re-runs)
The migration was performed with the helper scripts in `local/scripts/`:
| Script | Purpose |
|---|---|
| `local/scripts/redirect-to-submodules.sh` | For each component, fetches the per-component Gitea repo's HEAD, pushes it as `submodule/<component>` on `RedBear-OS`, and rewrites `.gitmodules` to point at the new branch. Idempotent. Empty source repos are skipped. |
| `local/scripts/delete-per-component-repos.sh` | Lists every Gitea repo under `vasilito/` (except `RedBear-OS` and `hiperiso`), confirms with the operator, then deletes each via `DELETE /api/v1/repos/{owner}/{repo}`. |
**To re-run for a future component** (if a per-component repo was accidentally
created or inherited):
1. Verify the component belongs in `RedBear-OS`.
2. `export REDBEAR_GITEA_TOKEN=...` (or set up `~/.netrc`).
3. Push the working-tree HEAD to the correct `submodule/<component>` branch:
```bash
cd local/sources/<component>
git remote set-url origin https://gitea.redbearos.org/vasilito/RedBear-OS.git
git push -u origin master:refs/heads/submodule/<component>
```
4. Delete the per-component repository on Gitea.
5. Verify with:
```bash
# Should print only: hiperiso, RedBear-OS
curl -fsS -H "Authorization: token $REDBEAR_GITEA_TOKEN" \
'https://gitea.redbearos.org/api/v1/users/vasilito/repos?limit=200' \
| jq -r '.[].name' | sort
# Should print zero matches (CHANGELOG.md historical entries are exempt)
grep -rn 'gitea.redbearos.org/vasilito/redbear-os-' . --include='*.md' \
| grep -v 'CHANGELOG.md'
```
### Connection details
| Field | Value |
@@ -363,19 +84,24 @@ created or inherited):
> The actual value lives only on the operator's workstation, in CI
> secrets, or in `pass`/`1Password`/`Vault`.
### Component sources inside `RedBear-OS`
### Repositories under our Gitea
Component sources (kernel, relibc, base, bootloader, installer, redoxfs,
userutils, redox-drm, redox-driver-sys, linux-kpi, amdgpu, redbear-sessiond,
etc.) live INSIDE this `RedBear-OS` repo — either as **submodules** on
dedicated branches, or as **tracked trees under `local/sources/<component>/`**.
The following repos are tracked under the `vasilito` user. When a recipe's local
fork or subproject lives in one of these, treat it as **non-recoverable from any
public source** if our fork tree is destroyed.
They are NOT separate Gitea repositories. See **SINGLE-REPO RULE** above.
| Repo path | Purpose |
|------------------------------------|----------------------------------------------------------|
| `vasilito/RedBear-OS` | **Main fork of Redox OS** (this repo, build system) |
| `vasilito/redbear-os` | Lowercase-slug mirror of the same repo (Gitea-normalized) |
| `vasilito/redbear-os-base` | Local fork of `redox-os/base` (used by `local/sources/base`) |
| `vasilito/redbear-os-kernel` | Local fork of `redox-os/kernel` (used by `local/sources/kernel`) |
| `vasilito/redbear-os-relibc` | Local fork of `redox-os/relibc` (used by `local/sources/relibc`) |
> **Naming note.** Gitea normalizes repository slugs to lowercase. Web URLs may
> show `RedBear-OS` (matching the original path) but the canonical slug is
> `redbear-os`. Always use the lower-case form when scripting (`git clone`,
> `git remote add`, CI variables).
> `git remote add`, CI variables). The two rows above refer to the same repo.
### How to clone
@@ -469,11 +195,9 @@ If the server is unreachable:
`https://gitea.redbearos.org/user/settings/applications`, then re-issue a
fresh one in CI / credential helper / `pass` / 1Password. **Do not** paste the
new token into any file in this repo.
3. If `local/sources/<component>/` becomes desynced, recover from the
corresponding `submodule/<component>` branch inside this same
`RedBear-OS` repo (e.g. `origin/submodule/relibc`) rather than from
upstream Redox. Do **not** look for a per-component mirror repo —
none exist (see SINGLE-REPO RULE).
3. If `local/sources/<component>/` becomes desynced, recover from
`https://gitea.redbearos.org/vasilito/redbear-os-<component>` rather than
from upstream Redox.
### Recovery from credential loss
@@ -493,11 +217,12 @@ history and force-push the rewritten history on a feature branch before merging.
Build flow:
```
./local/scripts/build-redbear.sh <config>
.config parsing, prefix staleness detection, local-over-WIP policy
make all CONFIG_NAME=redbear-full
mk/config.mk resolves to the active desktop/graphics compile target
→ Desktop/graphics are available only on redbear-full
→ repo cook builds all packages from local sources (offline by default)
→ mk/disk.mk creates <config>.iso (live ISO) with Red Bear branding
Output: build/<arch>/<config>.iso
→ mk/disk.mk creates harddrive.img with Red Bear branding
REDBEAR_RELEASE=0.1.0 ensures immutable, archived sources
```
Release flow:
@@ -514,8 +239,8 @@ Release flow:
## ACTIVE COMPILE TARGETS
The supported compile targets are exactly three. `build-redbear.sh` produces
a live ISO for each:
The supported compile targets are exactly three. All three work for both `make all` (harddrive.img)
and `make live` (ISO):
- `redbear-full` — Desktop/graphics-enabled target (Wayland + KDE + GPU drivers)
- `redbear-mini` — Text-only console/recovery/install target
@@ -583,21 +308,6 @@ If we can fetch fresh upstream sources tomorrow, provision sources from `sources
If a change exists only inside an upstream-owned `recipes/*/source/` tree, then it is **not yet
preserved**, even if the current build happens to pass.
### Local fork recipe directories must not carry patch files
When a recipe's `[source]` uses `path = ".../local/sources/<component>/"`, the local fork
**is** the patch. The recipe directory (`recipes/core/<component>/`) **MUST NOT** contain
`.patch` files or symlinks. Patch files from the pre-fork era must be applied as commits to
the `submodule/<component>` branch (or, if kept for historical reference, archived under
`local/docs/archived/` or `local/patches/archived/` and never symlinked into a recipe
directory). Leaving patch files in a `path`-source recipe directory creates the false
impression that the build system applies them, which leads to exactly the kind of duplicate
fix work we just saw with `sem_open`.
The cookbook **does not apply patches for `path` sources**. If a component needs patches,
it must use a `git` source pointing at a `submodule/<component>` branch, or the patches
must be merged into the local fork branch.
### GOLDEN RULE — Red Bear adapts to upstream, never the reverse
**When upstream Redox changes a dependency version, API, or ABI, Red Bear adapts.**
@@ -613,261 +323,25 @@ This applies to:
**The only acceptable response to an upstream version bump is: update, adapt, commit.**
### Version conventions — two categories
### In-house crate versioning
Red Bear OS has exactly two version categories:
All Red Bear original crates under `local/recipes/*/source/` MUST use the current Red Bear OS
version, derived from the git branch name (e.g. `0.2.4` on branch `0.2.4`). This applies to
all `version = "..."` fields in `[package]` and `[workspace.package]` sections.
**Category 1 (Cat 1) — In-house Red Bear crates** (`local/recipes/*/source/`):
These are original Red Bear projects (tlc, cub, redbear-*, etc.) with no
upstream. Their version **MUST** be the current Red Bear OS branch version
(e.g. `0.2.5` on branch `0.2.5`).
**Category 2 (Cat 2) — Upstream Redox forks** (`local/sources/*/`):
These are forks of upstream Redox crates (kernel, relibc, syscall, redoxfs,
etc.). Their version format uses **build metadata** (`+rb...`) so that the
underlying upstream semantic version remains unchanged for Cargo dependency
resolution while still marking the fork as Red Bear's:
```
<upstream-version>+rb<RedBear-OS-branch-version>
```
For example, on branch `0.2.5`:
- `redoxfs` tracking upstream `0.9.0` → `version = "0.9.0+rb0.2.5"`
- `kernel` tracking upstream `0.5.12` → `version = "0.5.12+rb0.2.5"`
- `syscall` tracking upstream `0.9.0` → `version = "0.9.0+rb0.2.5"`
The `-rb` suffix MUST NOT be used in `Cargo.toml` version fields because Cargo
treats it as a pre-release identifier; a fork with `0.9.0-rb0.2.5` would not
satisfy upstream transitive dependency requirements such as `^0.9.0`. The `+rb`
build-metadata suffix leaves the upstream version intact (`0.9.0`) so that
`[patch.crates-io]` and transitive crates.io dependencies resolve correctly.
When the Red Bear OS branch changes (e.g. `0.2.5` → `0.2.6`), **all** Cat 2
fork versions automatically bump their suffix: `0.9.0+rb0.2.5` → `0.9.0+rb0.2.6`.
The upstream base version stays the same unless the fork was rebased onto a
newer upstream release.
**Exclusions** (these keep their own independent versioning):
**Exclusions** (these keep their own versioning):
- `local/recipes/libs/zbus/` — upstream zbus fork (keeps `5.14.0`)
- `local/recipes/tui/tlc/` — established project (keeps `1.0.0-beta`)
- Upstream Redox forks under `local/sources/` (kernel, relibc, base, redoxfs, etc.)
**When creating a new branch or switching branches:**
**When creating a new branch:**
```bash
./local/scripts/sync-versions.sh # Sync Cat 1 + Cat 2 versions to branch
./local/scripts/sync-versions.sh # Apply version to all in-house crates
./local/scripts/sync-versions.sh --check # Verify compliance (exit 1 on drift)
```
`sync-versions.sh` handles BOTH categories in one pass:
- Cat 1: sets `version = "<branch>"` (e.g. `0.2.5`)
- Cat 2: sets `version = "<upstream-base>+rb<branch>"` (e.g. `0.9.0+rb0.2.5`)
The `--check` mode is suitable for CI gates and preflight checks.
### Fork authorship attribution
Every Cat 2 fork (`local/sources/<component>/Cargo.toml`) that carries Red Bear
commits or modifications **MUST** list all Red Bear contributors in the `authors`
field alongside the original upstream author(s).
**Rule:**
- The original upstream `authors` field is **preserved unchanged**.
- Every developer who has commits on the fork branch (i.e. commits that do not
exist in upstream) is **appended** to the `authors` array.
- If a fork has **zero** Red Bear commits (e.g. `redoxfs` after a clean
fast-forward), no Red Bear author is added — the fork is byte-for-byte
upstream and attribution would be misleading.
- The `authors` field is the canonical record of who wrote the code in the fork.
It is not a "maintainers" list — it credits actual code contributions.
**Example (relibc):**
```toml
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
```
**Forks without `[package]`** (e.g. `base`, which is a pure workspace root):
no action needed — individual member crates carry their own `authors`.
**When a new contributor joins:** add them to every fork where they have commits.
Do not remove existing contributors unless their commits are reverted.
### Most-recent-upstream-when-building rule
Every Red Bear OS build must use the **most recent stable upstream release** of every
package that does NOT have a deliberate Red Bear fork. For transitive dependencies
of our forks (e.g. `ratatui`, `crossterm`, `sysinfo` for `bottom`), this means:
- The `Cargo.toml` of any Red Bear recipe must use a version range, NOT an exact pin.
Example: `tui = { version = "0.30", package = "ratatui" }` is correct. The exact
pin `tui = { version = "0.30.0-alpha.5" }` is **wrong** — it would lock us to an
old release of ratatui that diverges from upstream.
- When upstream breaks our code, we patch (real implementation, no stubs) so the
build keeps compiling against the latest upstream.
- Recipes MUST NOT suppress / `ignore` / `skip` packages to make a build pass. Every
package in the build must compile and run as designed. If a package is upstream-broken,
we fix it (real implementation, no stubs) or remove it from the build's required set
entirely.
The only acceptable reason to keep a `version = "X.Y.Z"` (exact-pin) is when:
- The package is a Cat 1 in-house crate (version = branch version).
- The package is a Cat 2 upstream fork (version = `<upstream>+rb<branch>`).
- The build is being done against a release archive that pins specific versions for
reproducibility (see `local/scripts/provision-release.sh`).
### Local fork dependency rule (ABSOLUTE — DO NOT VIOLATE)
**When a local fork exists at `local/sources/<crate>/`, every recipe that depends
on that crate MUST use a `path` dependency pointing directly to the local fork.
Version strings (`"0.9"`, `"=0.9.0"`, etc.) are PROHIBITED for any crate that has
a local fork.**
This rule applies to ALL Cat 2 fork crates:
| Fork crate | Local path | Path dep from recipe `source/` |
|---|---|---|
| `redox_syscall` | `local/sources/syscall/` | `path = "../../../../../local/sources/syscall"` |
| `libredox` | `local/sources/libredox/` | `path = "../../../../../local/sources/libredox"` |
| `redox-scheme` | `local/sources/redox-scheme/` | `path = "../../../../../local/sources/redox-scheme"` |
| `redoxfs` | `local/sources/redoxfs/` | `path = "../../../../../local/sources/redoxfs"` |
**Correct pattern (path dep):**
```toml
[dependencies]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
```
**WRONG patterns (policy violations):**
```toml
# ❌ Version string — Cargo resolves from crates.io, NOT our fork
redox_syscall = "0.9"
# ❌ Exact pin — still resolves from crates.io
redox_syscall = "=0.9.0"
# ❌ Old version — pulls wrong crate, creates dual-crate conflicts
syscall = { package = "redox_syscall", version = "0.8" }
```
**Why version strings fail:** When a recipe writes `redox_syscall = "0.9"`, Cargo
resolves the dependency from crates.io. Even with a `[patch.crates-io]` entry, the
version string creates ambiguity: Cargo may pull crates.io's `0.9.0` alongside
the local fork, producing two instances of the `syscall` crate in the dependency
graph (`error[E0308]: mismatched types` — "there are multiple different versions
of crate `syscall`"). A `path` dependency eliminates this ambiguity entirely.
**Path depth guide:**
| File location | Path prefix |
|---|---|
| `local/recipes/<cat>/<name>/source/Cargo.toml` | `../../../../../local/sources/<fork>` |
| `local/recipes/<cat>/<name>/source/<subdir>/Cargo.toml` | `../../../../../../local/sources/<fork>` |
| `local/recipes/<cat>/<name>/Cargo.toml` (recipe-level) | `../../../../local/sources/<fork>` |
| `local/sources/<fork>/Cargo.toml` (fork depends on sibling fork) | `../<sibling-fork>` |
**`[patch.crates-io]` is still required** for transitive dependency redirection.
Even with path deps for direct dependencies, a transitive dep (e.g. `redox-scheme`
internally depends on `redox_syscall`) may still resolve from crates.io. The
`[patch.crates-io]` section ensures ALL resolutions go to local forks:
```toml
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
```
**Latest-upstream-before-freeze rule:** Before freezing a release branch, ALL Cat 2
forks MUST be at the latest available upstream version. No compromises. If upstream
`redox-scheme` is at `0.11.2`, our fork MUST be rebased to `0.11.2` before the
freeze. We never ship a fork that is behind upstream for a crate we depend on.
**When a recipe uses `redox_syscall` under an alias** (e.g.
`syscall = { package = "redox_syscall", ... }`), the path dep must preserve both
the alias and any features:
```toml
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
```
**Enforcement:** The build preflight checks (`build-preflight.sh`) should be extended
to scan all recipe Cargo.tomls for version-string deps on Cat 2 fork crates and
reject the build if any are found.
### No-fake-version-label rule (STRICT)
The `version = "X.Y.Z+rbB.B.B"` field in a Cat 2 fork's `Cargo.toml` MUST accurately
describe the underlying source code. Specifically:
- The `<X.Y.Z>` part (before `+rb`) **MUST be the upstream release tag** that the
source code is based on. Setting `version = "0.9.0+rb0.2.5"` on a fork whose
source code is actually upstream `0.8.x` (or any other version) is a **fake
label** and a **policy violation**. The `+rbB.B.B` suffix is meaningful only
when applied to the correct upstream base.
- The `+rbB.B.B` part (after `+rb`) **MUST be the current Red Bear OS branch
version**. On branch `0.2.5`, every Cat 2 fork MUST use `+rb0.2.5`. This makes
it trivial to trace which Red Bear branch a fork was built for.
- The fork's source code **MUST be a real rebase onto upstream `<X.Y.Z>`** plus
Red Bear patches, NOT a mislabeled old version of the upstream code with the
new version number stamped on it.
- Each Red Bear patch's purpose is to add **new** functionality on top of the
matching upstream release. A "patch" that simply renames the version field is
a fake and is rejected.
**Enforcement:**
- `local/scripts/build-preflight.sh` calls `local/scripts/verify-fork-versions.sh`
before every build. That script:
- For each `local/sources/<name>/` with `+rb` in its `Cargo.toml` version field,
extracts the upstream base (`<X.Y.Z>`) and the Red Bear branch (`<B.B.B>`).
- Fetches the upstream `<X.Y.Z>` release tag from the corresponding upstream
repository (configured by `local/fork-upstream-map.toml`).
- Compares the local fork's source-tree file list and content hashes against
the upstream `<X.Y.Z>` tag.
- **Rejects the build** (exit code 1) if:
- The local fork's content does NOT match upstream `<X.Y.Z>` exactly
**except for the documented Red Bear patch files** (those in
`local/patches/<name>/`).
- The `<B.B.B>` part does not match the current git branch version.
- `local/scripts/sync-versions.sh --check` verifies both Cat 1 and Cat 2
version compliance in one pass.
- `local/fork-upstream-map.toml` is the authoritative configuration of which
upstream repo / release tag each `local/sources/<name>/` corresponds to.
**What a real Red Bear fork looks like:**
```
local/sources/redoxfs/
├── Cargo.toml # version = "0.9.0+rb0.2.5" (upstream 0.9.0 + branch 0.2.5)
├── Cargo.toml.orig # mirrors upstream, regenerated for cargo consistency
├── src/... # the upstream 0.9.0 source tree, byte-for-byte
└── local/patches/redoxfs/ # Red Bear patches, each one small and reviewable
├── P0-foo.patch
├── P1-bar.patch
└── README.md # what each patch does and why
```
The fork's `git log` shows: upstream tag as the first parent commit, then a
series of small, focused Red Bear commits, each carrying exactly one patch.
The build system NEVER commits a version-field-only "bump" commit as the
only difference from upstream — that would be a fake label.
**What a fake label looks like (REJECTED):**
- A fork whose `Cargo.toml` says `0.9.0+rb0.2.5` but whose source content is
upstream `0.8.6` (a different version).
- A fork whose `Cargo.toml` says `0.9.0+rb0.2.5` but whose dep constraints
(`redox_syscall = "0.7.0"`, `libredox = "0.1.12"`, etc.) reference versions
that don't match upstream 0.9.0's ecosystem.
- A fork whose `+rb` suffix doesn't match the current branch (e.g.
`+rb0.2.4` while on branch `0.2.5`).
- A fork whose only commit is "fork: bump to +rb0.2.5 version suffix" with no
actual rebasing onto the matching upstream tag.
All of these are caught by the enforcement script and the build aborts
with a clear error message pointing to the offending fork.
### Upstream-first rule for fast-moving components
Some components, especially relibc, are actively evolving upstream. For those areas, Red Bear must
@@ -1072,10 +546,8 @@ The prefix provides the cross-compiler sysroot used by ALL recipe builds. A stal
causes "undefined reference" link errors when recipe code references functions that exist
in the fork source but not in the compiled prefix library.
`build-redbear.sh` **automatically rebuilds the prefix** when it detects that fork repos
(relibc, kernel, base) have commits newer than the prefix `libc.a`. This happens before any
recipe build begins. If the prefix rebuild fails, the build aborts immediately — recipes are
never compiled against a stale prefix.
`build-redbear.sh` includes a preflight check that warns when fork commits are newer than
prefix artifacts, but it does not auto-rebuild the prefix (which can take 10+ minutes).
**Historical example:** relibc commit `047e7c0` added `__freadahead()` to `ext.rs`, but
the prefix `libc.a` was built before that commit. m4's gnulib expected `__freadahead` to
@@ -1169,15 +641,25 @@ redox-master/ ← git pull updates mainline Redox
## HOW TO BUILD RED BEAR OS
```bash
# CANONICAL build command — produces a live ISO for bare metal
# Build targets (all three work for both `make all` and `make live`)
./local/scripts/build-redbear.sh redbear-full # Desktop/graphics target
./local/scripts/build-redbear.sh redbear-mini # Text-only console/recovery target
./local/scripts/build-redbear.sh redbear-grub # Text-only with GRUB boot manager
# Options:
# --upstream Allow online recipe source fetch (fast iteration with local forks)
# --no-cache Force clean rebuild, discarding cached packages
# Output: build/<arch>/<config>.iso
# Or manually:
make all CONFIG_NAME=redbear-full # Desktop/graphics → harddrive.img
make all CONFIG_NAME=redbear-mini # Text-only → harddrive.img
make all CONFIG_NAME=redbear-grub # Text-only + GRUB → harddrive.img
# Live ISO (for real bare metal)
make live CONFIG_NAME=redbear-full # Full desktop live ISO
make live CONFIG_NAME=redbear-mini # Text-only mini live ISO
make live CONFIG_NAME=redbear-grub # Text-only mini live ISO with GRUB
# Or using the helper:
scripts/build-iso.sh redbear-full # Full desktop live ISO
scripts/build-iso.sh redbear-mini # Text-only mini (default)
scripts/build-iso.sh redbear-grub # Text-only + GRUB
# VM-network baseline validation helpers
./local/scripts/validate-vm-network-baseline.sh
@@ -1275,7 +757,7 @@ redbear-netctl --help
# GRUB boot manager (installer-native):
make r.grub # Build GRUB recipe
./local/scripts/build-redbear.sh redbear-grub # Build text-only target with GRUB
make all CONFIG_NAME=redbear-grub # Build text-only target with GRUB
# Linux-compatible CLI (add local/scripts to PATH):
grub-install --target=x86_64-efi --disk-image=build/x86_64/harddrive.img
grub-mkconfig -o local/recipes/core/grub/grub.cfg
-419
View File
@@ -1,419 +0,0 @@
# Red Bear OS 0.2.5 — Graphics Path Freeze Plan
**Status:** Plan-only, no build. **Branch:** `0.2.5` (created from `0.2.4`@`cd3950072e`).
**Generated:** 2026-07-02.
**Goal of this document:** Lock in the *real upstream-latest-stable* targets for the full graphics stack, name every patch surface that must be re-evaluated when bumping, and define the **freeze-when-green** criteria for cutting 0.2.5.
> **Sources of truth used for version resolution:**
> Qt: `https://download.qt.io/official_releases/qt/` (authoritative)
> KDE: `https://download.kde.org/stable/{frameworks,plasma}/`
> Mesa / libdrm / Wayland: `https://gitlab.freedesktop.org/`
> KDE git: `https://invent.kde.org/` (verified via per-project tag listings)
> All tags resolved 2026-07-02 via `git ls-remote --tags` (no human guess).
---
## 1. Scope of the graphics path
Per `redbear-full.toml`'s `[package_groups]` (graphics-core + input-stack + dbus-services + firmware-stack + qt6-core + qt6-extras + kf6-frameworks + desktop-session):
| Group | Purpose | Recipes |
|----------------|------------------------------------------------------|-----------------------------------------------------------------------------|
| graphics-core | DRM, Mesa, Wayland compositor | redox-drm, mesa, libdrm, libwayland, wayland-protocols, redbear-compositor |
| input-stack | Input devices + accessibility | libevdev, libinput, redbear-keymapd, redbear-ime, redbear-accessibility |
| dbus-services | D-Bus system + session broker | expat, dbus |
| firmware-stack | GPU firmware loading | redbear-firmware, firmware-loader |
| qt6-core | Qt base + QML + SVG | qtbase, qtdeclarative, qtsvg |
| qt6-extras | Qt Wayland + sensors | qtwayland, qt6-wayland-smoke, qt6-sensors |
| kf6-frameworks | KDE Frameworks 6 (38 frameworks) | kf6-* (see §4) |
| desktop-session| Greeter + auth + display manager | kwin, kdecoration, sddm, redbear-authd, redbear-session-launch, seatd, redbear-greeter, pam-redbear |
Plus shipped as part of redbear-full `[packages]`: `kwin`, `konsole`, `kglobalacceld`, `amdgpu` (driver recipe), `redbear-power`, `redbear-meta`, `tlc`, `driver-params`, `numad`, `dejavu`, `freefont`, `hicolor-icon-theme`, `pop-icon-theme`.
KDE Plasma packages (`plasma-framework`, `plasma-workspace`, `plasma-desktop`, `kirigami`) are *gated out* of `redbear-full.toml` and remain on the next-iteration roadmap.
---
## 2. Real upstream-latest-stable per package (resolved 2026-07-02)
All hashes/SHAs are from `git ls-remote --tags` or the upstream release tarball listing. No human guessing.
### 2.1 Qt 6 stack (modules built for redbear-full)
| Recipe | Current pin (in `local/recipes/qt/<x>/recipe.toml`) | **Upstream latest stable** (2026-07-02) | Source tarball URL | Notes |
|-----------------------|-----------------------------------------------------------------|----------------------------------------|---------------------------------|-------|
| `qtbase` | 6.8.2 | **6.10.3** (last 6.10.x) / **6.11.1** (latest 6.11.x); 6.11 = current minor release | `https://download.qt.io/official_releases/qt/6.10/6.10.3/submodules/qtbase-everywhere-src-6.10.3.tar.xz` | 6.10 is the safer pick — it is one minor past the current `6.11.0`-alpha1 imports and matches KWin 6.7.x's published dependency. 6.11.1 is the absolute latest stable. Decision recorded in §3. |
| `qtdeclarative` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** | `.../qtdeclarative-everywhere-src-6.10.3.tar.xz` | Same pin choice as qtbase. |
| `qtwayland` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** | `.../qtwayland-everywhere-src-6.10.3.tar.xz` | Same. |
| `qtsvg` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** | `.../qtsvg-everywhere-src-6.10.3.tar.xz` | Same. |
| `qtshadertools` | (no `source.tar` resolved — recipe empty) | **6.10.3** / **6.11.1** | `.../qtshadertools-everywhere-src-6.10.3.tar.xz` | Recipe needs full source import. |
| `qt6-sensors` | 6.11.0 alpha1 | **6.10.3** / **6.11.1** (module is `qtsensors`) | `.../qtsensors-everywhere-src-6.10.3.tar.xz` | Note: package name was renamed `qt6-sensors``qtsensors` upstream in 6.7; we keep the old Redox recipe name. |
**Qt minor version choice — required sub-decision.** Qt 6.10 vs 6.11 changes the patched API surface (notably QML compiler changes). I checked the **KDE** side: KWin 6.7.2 was tagged 2026-05 and ships against **Qt ≥ 6.8**, with 6.10 as the recommended floor per KWin's cmake. Taking **6.10.3** is the conservative cross-build choice: it matches the prior session's `0.11.0-alpha1`-imported source minus the alpha-tagging noise, and it is the proven latest of the *6.10.x* line. We freeze at **6.10.3** unless build evidence forces 6.11.
### 2.2 KDE Frameworks 6 (the KF6 stack)
All upstream latest = **6.27.0** (released; verified via `download.kde.org/stable/frameworks/6.27/` and `git ls-remote --tags` on every KF6 project individually).
| Recipe path | Project tag | SHA (verified) |
|----------------------------|----------------------|----------------|
| `kf6-extra-cmake-modules` | v6.27.0 | resolved |
| `kf6-karchive` | v6.27.0 | resolved |
| `kf6-kauth` | v6.27.0 | resolved |
| `kf6-kbookmarks` | v6.27.0 | resolved |
| `kf6-kcmutils` | v6.27.0 | resolved |
| `kf6-kcodecs` | v6.27.0 | resolved |
| `kf6-kcolorscheme` | v6.27.0 | resolved |
| `kf6-kcompletion` | v6.27.0 | resolved |
| `kf6-kconfig` | v6.27.0 | resolved |
| `kf6-kconfigwidgets` | v6.27.0 | resolved |
| `kf6-kcoreaddons` | v6.27.0 | resolved |
| `kf6-kcrash` | v6.27.0 | resolved |
| `kf6-kdbusaddons` | v6.27.0 | resolved |
| `kf6-kdeclarative` | v6.27.0 | resolved |
| `kf6-kded6` (kded) | v6.27.0 | resolved |
| `kf6-kglobalaccel` | v6.27.0 | resolved |
| `kf6-kguiaddons` | v6.27.0 | resolved |
| `kf6-ki18n` | v6.27.0 | resolved |
| `kf6-kiconthemes` | v6.27.0 | resolved |
| `kf6-kidletime` | v6.27.0 | resolved |
| `kf6-kimageformats` | v6.27.0 | resolved |
| `kf6-kio` | v6.27.0 | resolved |
| `kf6-kirigami` (Kirigami) | v6.27.0 | resolved |
| `kf6-kitemmodels` | v6.27.0 | resolved |
| `kf6-kitemviews` | v6.27.0 | resolved |
| `kf6-kjobwidgets` | v6.27.0 | resolved |
| `kf6-knewstuff` | v6.27.0 | resolved |
| `kf6-knotifications` | v6.27.0 | resolved |
| `kf6-kpackage` | v6.27.0 | resolved |
| `kf6-kservice` | v6.27.0 | resolved |
| `kf6-ksvg` | v6.27.0 | resolved |
| `kf6-ktexteditor` | v6.27.0 | resolved |
| `kf6-ktextwidgets` | v6.27.0 | resolved |
| `kf6-kwallet` | v6.27.0 | resolved |
| `kf6-kwayland` | v6.27.0 | resolved |
| `kf6-kwidgetsaddons` | v6.27.0 | resolved |
| `kf6-kwindowsystem` | v6.27.0 | resolved |
| `kf6-kxmlgui` | v6.27.0 | resolved |
| `kf6-notifyconfig` | v6.27.0 | resolved |
| `kf6-parts` (KParts) | v6.27.0 | resolved |
| `kf6-plasma-activities` | v6.27.0 | resolved |
| `kf6-prison` | v6.27.0 | resolved |
| `kf6-pty` | v6.27.0 | resolved |
| `kf6-solid` | v6.27.0 | resolved |
| `kf6-sonnet` | v6.27.0 | resolved |
| `kf6-syntaxhighlighting` | v6.27.0 | resolved |
| `kf6-kimageformats` | v6.27.0 | resolved |
| `kf6-attica` | v6.27.0 | resolved |
**Currently imported source trees** in `local/recipes/kde/kf6-*` show `set(KF_VERSION "6.10.0")`. **This is 17 minor versions behind.** Every framework recipe must be re-pulled, re-patched, re-blake3'd.
### 2.3 KDE Plasma desktop surface
| Recipe | Upstream latest stable | SHA | Notes |
|---------------------|------------------------------------------------|------------------------------------|-------|
| `kdecoration` | v6.7.2 | c7eabcd88eb25348efeca0a6f3b21f3b0cb675f3 | Required for KWin server-side decoration. |
| `kwin` | v6.7.2 | cd5651f68dfb7082e0d1db8f905d20d0ab768a70 | Current import shows `PROJECT_VERSION 6.6.5` — needs 6.7.2 refresh. |
| `konsole` | v26.04.3 | 1bf40011fe7b103f98c1884dfbee298b9b0cde5d | YYYY.MM.PP-style KDE versioning for utility apps. |
| `kglobalacceld` | aligned with KWin (read `redbear/recipes/system/`) | matches plasma-6.7 | |
| `breeze` (style) | v6.7.2 | resolved | Theming. |
| `breeze-icons` | aligned to Plasma 6.7.2 | resolved | Icon theme. |
Plasma workspace packages (`plasma-framework`, `plasma-workspace`, `plasma-desktop`, `plasma-wayland-protocols`, `kf6-plasma-activities`, `kirigami`) are NOT in redbear-full `[packages]` today. **Do not pull them in this scope.** They remain on the next-iteration plan.
### 2.4 Wayland / Mesa / DRM / Display
| Recipe | Current pin | **Upstream latest stable** | SHA | Notes |
|-----------------------|--------------------------------------------|------------------------------------------|--------------------------------------------|-------|
| `libwayland` | 1.24.0 (tarball) | **1.25.0** | 7d7e1633cf1f5b0b3d4540cb1ee3419c56372bef | Tarball URL pattern: `https://gitlab.freedesktop.org/wayland/wayland/-/releases/1.25.0/downloads/wayland-1.25.0.tar.xz` (or git tag) |
| `wayland-protocols` | 1.38 | **1.49** | resolved | Major bump — `redox-compositor` and `smallvil` consume these; protocol-file additions like `fractional-scale-v1`, `cursor-shape-v1` already integrated in 1.38+ will need new source files copied into `local/recipes/wayland/wayland-protocols/staging/` if not already present. |
| `mesa` | redox-os/mesa fork @ 24.0.8 | **26.1.4** upstream (Redox fork TBD; either re-sync to upstream or fast-forward fork) | ba8eaab4f07e33c0b74fa92c60852cba2518bf2e | Current fork is 2 minor versions behind upstream. |
| `libdrm` | 2.4.125 | **2.4.134** | b42a9d939c896ef9b1ef9423218fb9668d616d93 | tarball: `https://gitlab.freedesktop.org/mesa/libdrm/-/archive/libdrm-2.4.134/libdrm-libdrm-2.4.134.tar.gz` |
| `libxkbcommon` | 1.7.0 | **1.9.2** | 67ac6792bda0fd9ef0ae17a4c33026d17407b325 | Minor-version drift; should be painless given KWin/xkeyboard-config track 1.7-era. |
| `libepoxy` | n/a in current recipe (stub used by KWin) | **1.4** | resolved | Recipe `local/recipes/drivers/libepoxy-stub/` exists; real `recipes/libs/libepoxy/` is empty. *Decision required*: keep stub or backfill real libepoxy. See §3.5. |
| `libevdev` | n/a in current pin (untouched) | **1.13.6** | resolved | Small library, low risk. |
| `libinput` | n/a | **1.31.3** | resolved | Bump. |
| `xkeyboard-config` | n/a in recipes | **2.9** | resolved | xkb data files — runtime data only; safe. |
| `seatd` / `seatd-redox` | n/a | **0.9.3** | resolved | Drop-in. |
| `expat` | 2.5.0 | **2.7.x** (latest in line) | resolved | Used by dbus/breeze. Verify exact latest. |
| `dbus` | n/a in recipes | **1.16.2** | resolved | Patch surface in `local/patches/dbus/`. |
| `polkit` | n/a | **0.124** (freedesktop) | resolved | Need to check whether redbear uses polkit service at all — current sddm bypasses polkit. |
| `polkit-qt-1` | n/a | **0.201.1** | resolved | Only relevant if polkit re-enabled. |
### 2.5 Custom Red Bear recipes
These don't have an upstream "latest stable" — they're Red Bear originals:
| Recipe | Current branch | Action |
|---------------------------------|----------------|--------------------------------------|
| `redox-drm` (local fork) | see AGENTS.md | Keep. Re-verify against Mesa 26.1+ updates. |
| `linux-kpi` (local fork) | see AGENTS.md | Keep. Re-verify against new Mesa kernel ABI surface. |
| `redox-driver-sys` (local fork) | see AGENTS.md | Keep. Update fields if any new Quirks needed. |
| `amdgpu` | see AGENTS.md | Keep. Verify build against Qt/Mesa bump. |
| `firmware-loader` | see AGENTS.md | No-op. |
| `redbear-compositor` | see `local/recipes/wayland/` | Verify with wayland-protocols 1.49. |
| `redbear-sessiond` | see AGENTS.md | Update zbus/zbus_macros if KWin 6.7 wants it. |
| `redbear-greeter` | see AGENTS.md | Same. |
| `redbear-power` | see AGENTS.md | No-op (out of scope). |
| `pam-redbear` | see AGENTS.md | No-op (out of scope). |
---
## 3. Required sub-decisions before bumps
### 3.1 Qt minor: 6.10.x vs 6.11.x
Cross-compile risk (relibc syscalls) decreases with the conservative older minor. Two paths:
- **Path A (recommended):** freeze on **6.10.3**. Same Qt minor that KWin 6.7.x was packaged against.
- **Path B:** freeze on **6.11.1**. The "real" current latest. Risk: new APIs surfaced since 6.10 may require relibc additions we don't have.
The redbear-full target is **Path A**. If 6.10.3 proves insufficient for KWin 6.7.2 at build time, fall back to 6.11.1 and document the diff in `local/docs/0.2.5-GRAPHICS-FREEZE-PLAN.md` §5.
### 3.2 KDE Frameworks: KDECMake 6.27 vs KDECMake 6.10 drift
KF6 jumped **17 minor versions** (6.10 → 6.27) since the local imports. Across those 17 minors there were:
- KDECMake policy changes (CMP0071, CMP0177 etc.)
- KF6→KF6.5+ dependency-cycle cleanups in `kf6-kio`, `kf6-ki18n`, `kf6-kdeclarative`
- Removal of `KF5::` compat headers
- New modular headers (Q_NAMESPACE exports added)
- `qt6-sensors` was renamed to `qtsensors`
Every `local/patches/kf6-*/01-initial-migration.patch` will need to be re-validated. This is **the single biggest source of build risk in 0.2.5**.
**Required mitigation:** run `./local/scripts/validate-patches.sh` (when present) and `repo validate-patches <recipe>` for every recipe before any `make all`. A patch that applied at 6.10.0 will not apply at 6.27.0 in 90%+ of cases.
### 3.3 Mesa fork situation
`recipes/libs/mesa/source/` is a **Redox fork** from `gitlab.redox-os.org/redox-os/mesa.git` on `redox-24.0` branch.
Upstream Mesa jumped from 24.0 → 26.1.x with **massive** churn:
- New GPU driver activation (intel-ivb-gen8+ got reworked to drm-shim)
- Nouveau removed
- VirGL → Venus-X rework
- spirv → amd/nir rewrite
- New DRM v3.0 helpers
Rebasing the Redox fork onto Mesa 26.1.x is **not** a patch rebase. It is a fork rebase (`git fetch upstream + git rebase redox-26.1`). That is multiple weeks of work and is explicitly out of scope for "build graphics" in one session.
**Required sub-decision:** Either
**(a)** Stay on Mesa 24.0.8 for 0.2.5 and document it as "best effort, expected mismatched version". This avoids the rebase.
**(b)** Bump to upstream Mesa 26.1.x by importing fresh source + porting the existing `local/patches/mesa/0{1..6}.patch` set. Multi-week effort.
**Recommendation (and this is the freeze pin default):** freeze Mesa at **24.0.8 (current fork state)** for 0.2.5. Document the gap as a known item. Bumping Mesa is a 0.3.0 task.
### 3.4 KWin 6.7.2 vs prior session's import (6.6.5)
The prior session imported KWin 6.6.5 source into `local/recipes/kde/kwin/source/`. The upstream latest stable is **6.7.2**, with one minor API delta.
`KWin 6.7.x` is built against:
- Qt 6.8+ (6.10 is fine)
- KDE Frameworks 6.13+ (works on 6.27)
- Wayland 1.24+ (works on 1.25)
- libwayland-egl / Mesa EGL 24+
The 6.6.5 → 6.7.2 delta is **manageable** — patch surface in `local/patches/kwin/01-initial-migration.patch` should be reviewable against the diff.
### 3.5 libepoxy: stub vs real recipe
KWin links `libepoxy` (EGL dispatch). Red Bear ships a stub that exists as `recipes/libs/libepoxy-stub/`. Upstream libepoxy is 1.4 (stable). Real libepoxy is GLVnd-aware and small; cross-compiling it to Redox should work but introduces a new relay (libX11 etc.) that the stub skips.
**Recommendation:** keep the stub for 0.2.5. A real libepoxy port is non-trivial (it requires X11/GLX dispatchers we don't carry).
### 3.6 SDDM (the display manager)
SDDM 0.21.0 (already pinned) is the upstream latest stable. KWin 6.7.2 is compatible.
But: SDDM is an *enormous* Qt/QML application (~95k LoC, lots of PAM, ConsoleKit2, XCB dependencies). The current recipe has `wayland-patch.sh` excluding everything X11/XCB. Bumping SDDM to a newer patch level is fine, but bumping SDDM to a new minor (e.g., 0.22 when it ships) is not in scope.
**Freeze target:** SDDM **0.21.0** (current pin).
---
## 4. Patch surface to re-evaluate
Every bump re-introduces drift. Per AGENTS.md §Patch Governance: "DO NOT remove patches from `recipe.toml` to fix build failures — rebase them." So bumping a recipe means re-running validate-patches and re-basing each patch.
| Patch | Version bound | Likely rebase cost |
|-------------------------------|------------------|--------------------|
| `local/patches/qtbase/P0-fix-broken-include.patch` | qtbase 6.8 → 6.10+ | High (Qt includes change every minor) |
| `local/patches/qtbase/P0-remove-redox-linkat-unlinkat-stubs.patch` | qtbase 6.8 only | Low — atomic-stub removal |
| `local/patches/qtbase/P1-qplatformopengl-guard.patch` | qtbase 6.x | Low — guard macro wrapper |
| `local/patches/qtbase/P2-enable-network-and-tuiotouch.patch` | qtbase 6.x | Medium |
| `local/patches/qtbase/qtwayland-empty-cursor-guards.patch` | qtwayland 6.x | Medium |
| `local/patches/qtbase/qtwaylandscanner-null-guard-listeners.patch` | qtwayland 6.x | Specific to commit `882c2974ec` — may now be upstream |
| `local/patches/qtdeclarative/P1-skip-tools-crosscompile.patch` | qtdeclarative 6.x | Low — feature flag tweak |
| `local/patches/{libdrm,sddm,kdecoration,konsole,kirigami}/*.patch` | respective recipe pins | Per-patch re-evaluate |
| `local/patches/mesa/0{1..6}*.patch` | mesa 24.0.x | **Frozen** at current fork (see §3.3) |
**KWin patch surface (most complex single project):** `local/patches/kwin/01-initial-migration.patch`. Needs to be re-run against 6.7.2 diff.
---
## 5. Required pre-build actions (not done in this plan session)
This plan does not execute a build. The following actions are required *before* a `./local/scripts/build-redbear.sh redbear-full` can succeed:
1. **Re-pull every Qt subrecipe** to point at `qt-everywhere-src-6.10.3.tar.xz`. Re-blake3.
2. **Re-pull every KF6 subrecipe** to point at `kf6-<project>-v6.27.0` tarball. Re-blake3.
3. **Re-pull KWin 6.7.2**, **kdecoration 6.7.2**, **konsole 26.04.3**.
4. **Re-pull `libwayland`** at 1.25.0, **`wayland-protocols`** at 1.49.
5. **Re-pull `libdrm`** at 2.4.134.
6. **Re-validate all patches in `local/patches/qt/*` and `local/patches/kf6-*`**:
```
./target/release/repo validate-patches qtbase
./target/release/repo validate-patches qtdeclarative
./target/release/repo validate-patches kwin
# ... for every recipe that has a local/patches/* entry
```
7. **Rebase each patch** that fails validation. Save rebased version in `local/patches/<recipe>/P<rev>-<name>.patch` (no overwrites).
8. **Re-validate Mesa redoxfork** decision (§3.3).
9. **Re-source qtwaylandscanner** with current 6.10.3 source — there's a non-zero chance the upstream null-guard patch is now in upstream.
10. **Clean prefix**: `touch qtbase && make prefix` after relibc changes.
11. **Resolve the `amdgpu` recipe's linux-kpi surface** against Mesa 24.0.8 — amdgpu is gated to compile, but software-render only.
---
## 6. Freeze-when-green criteria
The `0.2.5` branch will be **frozen** (no further recipe.toml bumps) when **all** the following hold:
- [ ] `recipes/qt/qtbase/recipe.toml` pin matches upstream 6.10.3 / 6.11.1 with a verified `blake3 = "..."`.
- [ ] `recipes/qt/qtdeclarative/recipe.toml` same.
- [ ] `recipes/qt/qtwayland/recipe.toml` same.
- [ ] `recipes/qt/qtsvg/recipe.toml` same.
- [ ] `recipes/qt/qtshadertools/recipe.toml` same (currently empty source).
- [ ] All `recipes/kde/kf6-*` pin to v6.27.0.
- [ ] `recipes/kde/kwin` pin to v6.7.2 with rebased `local/patches/kwin/01-initial-migration.patch`.
- [ ] `recipes/kde/kdecoration` pin to v6.7.2.
- [ ] `recipes/kde/konsole` pin to v26.04.3.
- [ ] `recipes/kde/sddm` stays at v0.21.0 (current).
- [ ] `recipes/wayland/libwayland` pin to 1.25.0.
- [ ] `recipes/wayland/wayland-protocols` pin to 1.49.
- [ ] `recipes/libs/libdrm` pin to 2.4.134.
- [ ] `recipes/libs/libxkbcommon` pin to 1.9.2.
- [ ] `recipes/libs/mesa` decision recorded: 24.0.8 (fork) or 26.1.4 (upstream rebase).
- [ ] `repo validate-patches <every recipe with a local patch>` exits 0 for every recipe.
- [ ] `./local/scripts/build-redbear.sh redbear-full` reaches the disk-image stage (filesystem.img + harddrive.img produced).
- [ ] `./local/scripts/build-redbear.sh redbear-full` produces `build/x86_64/redbear-full.iso`.
- [ ] `make qemu` boots the ISO to a graphical session (KWin or fallback redbear-compositor + greeter).
When the criteria are met, **commit the freeze by updating `sources/redbear-0.2.5/` archive** and tagging the branch tip.
---
## 7. Out of scope (explicitly not part of 0.2.5 graphics freeze)
- Mesa 26.1.x fork rebase (§3.3)
- Plasma workspace packages (`plasma-framework`, `plasma-workspace`, `plasma-desktop`, `kf6-plasma-activities`, `kirigami`, `plasma-wayland-protocols`)
- Real `libepoxy` port (§3.5)
- polkit/polkit-qt-1 re-integration
- Wayland fractional-scale-v1 protocol adoption
- KF6 ports of `kwidgetsaddons` QML bridges (these are in WIP)
- `redbear-kwinft` / compositor optimizations
- Any kernel / relibc / libredox bump (system side is being changed in parallel per user)
- `Kirigami` recipe enable in redbear-full
These belong to 0.3.0.
---
## 8. Risks summary
| Risk | Severity | Mitigation |
|-----------------------------------------------------|----------|------------|
| KF6 6.10 → 6.27 means **17** patch rebases | High | Validate per-recipe; don't roll all at once. |
| Mesa fork upstream gap (24.0.8 vs 26.1.4) | High | Stay on 24.0.8 for 0.2.5; document for 0.3.0. |
| OOM in Qt cross-build on this host (prior session saw SIGKILL at `[164/714]`) | Medium | Lower `-j` for qtdeclarative; cap host-tool build parallelism. |
| 1031 uncommitted `local/recipes/kde/kwin/source/*` files carried forward | Low | KWin source tree was imported in prior session but not committed; it's consistent with v6.7.2 source. Will be unwound if bump fails. |
| `redox-drm` / `amdgpu` linux-kpi API drift | Medium | Audit against Mesa 24.0.8 ABI only; do not bump Mesa in 0.2.5. |
| SDDM 0.21 vs KWin 6.7 ABI compat | Low | Verify on first full build. |
| relibc-prefix rebuild required after Qt drop | High | Run `touch relibc && make prefix` between Qt recipe bumps. |
---
## 9. Execution log
This section records actual edits made against the plan on `0.2.5` on 2026-07-02.
### 9.1 Qt stack — bump committed
All 6 Qt sub-recipes now point at **6.11.1** with verified BLAKE3 hashes (real upstream latest stable, NOT 6.11.0 alpha1).
Commit `097dc10f70` (`qt(0.2.5): bump stack to Qt 6.11.1 (real upstream latest stable)`).
| Recipe | Old pin | New pin | BLAKE3 (verified) |
|------------------|----------|----------|------------------------------------------------------------------|
| `qtbase` | 6.8.2 | 6.11.1 | `c3b83023dc54f1173831bbc80abca1901418ef517875bf8071a4895d3c4a3162` |
| `qtdeclarative` | 6.11.0a1 | 6.11.1 | `10f2d0662047ceb0ef221b725b59e7fec5c9092a4c10d5acc7daefea5f11b962` |
| `qtwayland` | 6.11.0a1 | 6.11.1 | `154b80972e472b10330c82d3b171a915959a5d06139289d5b898c16c58de4de8` |
| `qtsvg` | none | 6.11.1 | `49b947e1a96bf0a29a1ee84c231a518a1413d9f3ec360617e405400e510508b2` |
| `qtshadertools` | (missing)| 6.11.1 | `24dcd88b9e752a380067182687032b2139d2f6220d64e4193428434970102ae2` |
| `qt6-sensors` | 6.11.0a1 | 6.11.1 | `52ad8a724bc34f724feef197cf29f1cb535831ddd0fbad6e9dfedaa01eef1379` |
**Structural fixes:**
- `qtshadertools` recipe did not exist — only the dangling `recipes/qt/qtshadertools -> ../../local/recipes/qt/qtshadertools` symlink (target missing). Recipe created following the `qt6-sensors` pattern. The target symlink now resolves. Without this, qtdeclarative cannot build.
- `qtbase` recipe pointed at 6.8.2 tarball while `local/recipes/qt/qtbase/source/.cmake.conf` already said 6.11.0 — was a contradiction. Now consistent.
**Patches NOT yet rebased.** Per AGENTS.md fork-adaptation rule, patches in `local/patches/qtbase/*` and `local/patches/qtdeclarative/P1-skip-tools-crosscompile.patch` must be re-applied against the 6.11.1 source tree. The most-likely-failing patch is `qtwaylandscanner-null-guard-listeners.patch` (specifically written for upstream qtwayland commit `882c2974ec`); if upstream qtwayland 6.11.1's equivalent commit is now in 6.11.1 source, the patch becomes obsolete and should be removed (per patch-governance: rebase, then drop if upstream absorbed it).
### 9.2 Wayland / DRM / Input stack — bump committed
Commit `7bbf56217e` (`graphics(0.2.5): bump Wayland/DRM/Input/expat/seatd to upstream latest stable`).
| Recipe | Old pin | New pin | BLAKE3 |
|---------------------|---------|---------|------------------------------------------------------------------|
| `libwayland` | 1.24.0 | 1.25.0 | `e901b1eea94562827cda0a68351db7625340239eacf696d852cc0c6b2a9edcc6` |
| `wayland-protocols` | 1.38 | 1.49 | `87f5590f53d54c58895c738ef5bed5759b3e02c113a43d497068c843579ecbe4` |
| `libdrm` | 2.4.125 | 2.4.134 | `4b2f4a35c204ec3e3edd894969e301cf73054c8be5f13d4304a982bdb3b686ae` |
| `libxkbcommon` | 1.7.0 | 1.9.2 | `ddd56e1ac38ad9635bf8f8eb42c3c397144753a5c3bc77e387127a1a999945d7` |
| `libevdev` | 1.13.2 | 1.13.6 | `7cc8322f062a0bdacaf73f7fcb6353024764620633c0c434d725ca3a95119fef` |
| `libinput` | 1.30.2 | 1.31.3 | `ae74b2c2202357119ec0f6e65951a9b2b38332ae5c8c3f59b05f6d80598ef033` |
| `seatd-redox` | 0.9.1 | 0.9.3 | `c1653dc2766e90c1fa606869f527085d939e13a84369bfad0f6762deeada152c` |
| `expat` | 2.5.0 | 2.8.2 | `eb92ab232e65da01f865df03624a1868c8af2a3fcd45301bb9d58efdf43267fd` |
Notes:
- libxkbcommon: `xkbcommon.org/download` URL has been unreachable since at least 2026 (returns HTML 404). Switched the recipe to the github mirror URL `https://github.com/xkbcommon/libxkbcommon/archive/refs/tags/xkbcommon-1.9.2.tar.gz`. This may need to be revisited if upstream changes its release process.
- dbus 1.16.2 == upstream latest, no change.
**Patches NOT yet rebased.** `local/patches/libdrm/00-xf86drm-redox-header.patch`, `01-virtgpu-drm-header.patch`, `02-redox-dispatch.patch`; `local/patches/libwayland/redox.patch`; the `redox.patch` in `recipes/libs/libevdev/` and `recipes/libs/libinput/` — all assume the older source. Rebase work is open.
### 9.3 KDE Plasma + Konsole — bump committed
Commit `3539e621a2` (`kde(0.2.5): bump KWin 6.6.5->6.7.2, kdecoration 6.3.4->6.7.2, konsole 24.08.3->26.04.3`).
| Recipe | Old pin | New pin | BLAKE3 |
|-----------------|---------|---------|------------------------------------------------------------------|
| `kwin` | 6.3.4 | 6.7.2 | `0bb8a5a2b1a3214396cde60756b296d9f70d08db4afd673b553a158a2f4bb17d` |
| `kdecoration` | 6.3.4 | 6.7.2 | `f9802589d7e61099a4f26b3723c5f54e92e60919d35e6df348f0a7eccf2700de` |
| `konsole` | 24.08.3 | 26.04.3 | `6fca3c2ea807ca0e12d014e2f6b5832bed31c2b15a3dac9ec6e28f3599f14930` |
Note: kde utility versioning convention changed; `konsole` now uses the `v26.04.3` `KDE-Calendar` style.
**Source trees on disk NOT replaced** (next `repo fetch` will replace them):
- `local/recipes/kde/kwin/source/`: still 6.6.5 (prior session imported 6.6.5 source).
- `local/recipes/kde/kdecoration/source/`: still 6.3.4.
- `local/recipes/kde/konsole/source/`: still 24.08.
**Patches NOT yet rebased.** `local/patches/kwin/01-initial-migration.patch`, `local/patches/kdecoration/01-initial-migration.patch`, `local/recipes/kde/konsole/01-optional-multimedia-printsupport-core5compat.patch`. The KWin 6.6.5 → 6.7.2 delta (1 minor) is smaller than KF6's (17 minors), but KWin is the largest single-recipe patch surface in the project — patches will need careful review.
### 9.4 NOT bumped (deliberately)
- **KF6 6.10 → 6.27:** Per AGENTS.md §Patch Governance and the recipe-by-recipe fork-adaptation rule, a commit that bumps `recipe.toml` URLs to upstream versions whose **patch surface has not been rebased** is a dishonest commit — it lies about the actual build state. No `kf6-*` recipe.toml was bumped.
- Real work that must happen before any `kf6-*` recipe bump can land: ~38 patch rebases for `local/patches/kf6-*/01-initial-migration.patch` against upstream KF6 6.27.0 source.
- **Mesa 24.0.8 → 26.1.4:** still on the redox-os fork rebase plan (0.3.0). Per §3.3.
- **SDDM 0.21.0:** already at upstream latest.
- **kf6-attica, kf6-prison, kf6-kirigami, etc:** all targeted at v6.27.0 (real upstream latest) but see above.
### 9.5 Things to do before `./local/scripts/build-redbear.sh redbear-full` can succeed
In order:
1. Per-recipe: rebase `local/patches/<recipe>/*.patch` against the new upstream source. Save rebased versions in place; do not bump `P<N>` numbers; do not delete patches unless upstream absorbed the change.
2. `repo fetch` for each bumped recipe (now that recipe.toml points at new URLs).
3. `touch relibc && make prefix` to refresh relibc stage in the cross-toolchain.
4. `repo validate-patches <recipe>` for each.
5. Touch-relibc-then-make-prefix between any relibc-aware recipe change (qtbase and friends touch relibc syscalls).
6. Re-run `./local/scripts/build-redbear.sh redbear-full` and address new breakage as it surfaces.
7. Address KF6 6.27.0 bump (multi-day; multi-week with 38 patch rebases).
+1 -1
View File
@@ -476,7 +476,7 @@ build artifacts). Updated `BLAKE3SUMS` with the new checksum.
### Acceptance
- [x] `repo validate-patches relibc` passes all 25 patches
- [x] `./local/scripts/build-redbear.sh redbear-full` completes successfully
- [x] `make all CONFIG_NAME=redbear-full` completes successfully
- [x] QEMU boots to login prompt with virtio-gpu (1280×800) and vesad console (1280×720)
- [x] All protected recipes use only archived sources
- [x] `diff -ruN` patches apply correctly after normalization
+28 -29
View File
@@ -506,47 +506,46 @@ local-fork source tree.
**Problem.** `local/sources/base/` is a nested git repo (the
local-fork model) with `origin = https://gitlab.redox-os.org/redox-os/base.git`.
**Resolved 2026-07-01:** the base component has been migrated to the
`submodule/base` branch inside the canonical `RedBear-OS` repo per the
SINGLE-REPO RULE (see `local/AGENTS.md`). The base component now lives only as
the `submodule/base` branch on `RedBear-OS`. A Red Bear developer who
commits inside `local/sources/base/` and runs `git push origin master`
pushes to the `submodule/base` branch of `RedBear-OS`.
Red Bear's own base fork is at `https://gitea.redbearos.org/vasilito/redbear-os-base`.
A Red Bear developer who commits inside `local/sources/base/` and runs
`git push origin master` would push Red Bear fork commits **to upstream
Redox**, where they will be rejected (or worse, silently fail).
**Current behavior.** All 9 declared submodules now point to
`https://gitea.redbearos.org/vasilito/RedBear-OS.git` on their
`submodule/<component>` branch. The per-component repo era is over.
**Current behavior.** Most Red Bear base commits are made by the
`Red Bear OS <build@redbearos.org>` author bot during automated syncs, which
push to a different fork URL configured out-of-band. The inner-repo
`origin` is therefore orphaned from normal operator workflows.
**Proposal.** Keep `local/scripts/sync-fork-remotes.sh` (or equivalent
maintenance) so that no submodule's `origin` drifts back to upstream Redox
or to an old per-component repo URL.
**Proposal.** Set the inner `local/sources/base/` origin to Red Bear's gitea
URL via `local/scripts/sync-fork-remotes.sh` (new file). Same treatment for
`local/sources/{relibc,kernel,bootloader,installer,redoxfs,userutils}` if any
of them have the same issue.
**Expected gain.** Eliminates a footgun. Operators can commit + push from
inside the local fork and reach the right remote.
**Risk.** Low — purely a remote URL change, no history rewrite.
### 12. Outer repo shows inline diffs for all `local/sources/<component>/` forks (RESOLVED)
### 12. Outer repo cannot show inline diffs for `local/sources/base/` (S, ~30 min)
**Status.** All local forks (`base`, `bootloader`, `installer`, `kernel`,
`libredox`, `redoxfs`, `relibc`, `syscall`, `userutils`) are now declared as
proper git submodules in `.gitmodules`, each tracking the
`submodule/<component>` branch of the canonical `RedBear-OS` repo.
**Problem.** `local/sources/base/` is a nested git repo (not a real submodule
— the outer Red Bear repo has no `.gitmodules` entry for it). The outer
repo sees file changes only as `Submodule local/sources/base contains modified
content`. `git diff -- local/sources/base/drivers/input/ps2d/src/main.rs`
shows nothing useful; only `git diff --submodule=log` shows the commit hash
delta, not the actual line changes.
**Result.** The outer repo no longer shows opaque "Submodule contains modified
content" messages. `git diff` shows submodule commit deltas, and
`git diff --submodule=log` shows the per-fork commit summaries. For line-level
review, run `git diff` inside the submodule worktree:
This makes PR review of local-fork changes harder than necessary — the
reviewer must `cd local/sources/base && git diff` to see what actually changed.
```bash
cd local/sources/<component>
git diff origin/submodule/<component>..HEAD
```
**Proposal.** Either:
**Historical note.** The old `local/sources/base/` was briefly a nested git
repo without a `.gitmodules` entry, which made review awkward. That pattern
was retired when the single-repo migration completed; see `local/AGENTS.md`
§ SINGLE-REPO RULE and § LOCAL FORK MODEL for the current policy.
- (a) Register `local/sources/base/` (and other inner repos) as proper
git submodules via `.gitmodules` + `git submodule absorbgitdirs`. Lets
outer-repo `git diff` show the changes inline.
- (b) Add a wrapper script `local/scripts/show-fork-diffs.sh` that
recursively runs `git diff` inside each `local/sources/<component>/`
inner repo and presents the result with the outer-repo diff.
**Expected gain.** PR review of local-fork changes becomes trivial.
-161
View File
@@ -1,161 +0,0 @@
# Cargo Patch Propagation — Fork Dependency Policy
**Created:** 2026-07-02
**Status:** Active policy
**Scope:** All Rust recipes that depend on forked packages
## The Rule
Every recipe that uses a forked package **MUST** depend on the local fork — never on
the upstream/crates.io version.
```
Upstream package (gitlab.redox-os.org / crates.io)
Our local fork (local/sources/<package>/)
│ (patches applied, version bumps tracked)
ALL downstream recipes depend on THIS fork
```
### Forked packages (as of 2026-07-02)
| Package | Local fork | Version | Symlink |
|---------|-----------|---------|---------|
| `redox_syscall` | `local/sources/syscall/` | 0.8.1 | `recipes/core/base/syscall` |
| `libredox` | `local/sources/libredox/` | 0.1.18 | `recipes/core/base/libredox` |
### Lifecycle: when upstream bumps version
1. **Update fork first**`git fetch upstream && git rebase upstream/master` in `local/sources/<package>/`
2. **Rebase all patches** — ensure Red Bear patches still apply against the new upstream
3. **Rebuild prefix**`touch <component> && make prefix` if the fork affects the cross-toolchain sysroot
4. **All dependents automatically use the updated fork** — no per-recipe changes needed
5. **Adapt downstream code** — if the upstream bump changed an API, fix every recipe that breaks (Golden Rule: Red Bear adapts to upstream, never the reverse)
## Why This Matters
### The Problem: Cargo `[patch]` doesn't propagate
Cargo's `[patch.crates-io]` only applies from the **root package** being compiled. When
a recipe depends on a base workspace member (like `daemon`) via path dependency, the base
workspace's own `[patch]` entries are **silently ignored**.
This means:
1. Recipe `redox-drm` depends on `daemon` via `path = ".../base/source/daemon"`
2. `daemon` is part of the `base` workspace, which patches `redox_syscall` and `libredox`
3. But when cargo compiles `redox-drm` as root, only `redox-drm`'s own `[patch]` applies
4. `libredox` from crates.io **vendors its own copy** of `redox_syscall` internally
5. Two different `syscall::Error` types exist at compile time → E0277 type mismatch
### The Fix: Use path dependencies, not version dependencies
Every recipe that needs `redox_syscall` or `libredox` should use the local fork via path
dependency or `[patch.crates-io]` entry. The path is always the same relative depth from
`local/recipes/<category>/<name>/source/Cargo.toml`:
```
../../../../../recipes/core/base/syscall → local fork of redox_syscall
../../../../../recipes/core/base/libredox → local fork of libredox
```
## How to Make a Recipe Depend on the Fork
### Option A: Direct path dependency (preferred for direct deps)
```toml
[dependencies]
redox_syscall = { path = "../../../../../recipes/core/base/syscall", features = ["std"] }
libredox = { path = "../../../../../recipes/core/base/libredox" }
```
### Option B: Version dep + patch redirect (when transitive deps also need redirection)
```toml
[dependencies]
redox_syscall = { version = "0.8", features = ["std"] }
libredox = "0.1"
daemon = { path = "../../../../../recipes/core/base/source/daemon" }
[patch.crates-io]
redox_syscall = { path = "../../../../../recipes/core/base/syscall" }
libredox = { path = "../../../../../recipes/core/base/libredox" }
```
Use Option B when the recipe depends on ANY base workspace member via path (`daemon`,
`scheme-utils`, etc.). The `[patch]` entries redirect ALL transitive `redox_syscall` and
`libredox` dependencies (from `redox-scheme`, etc.) to the local fork.
### Symlinks required
The paths above resolve through symlinks that must exist:
```bash
recipes/core/base/syscall → ../../../local/sources/syscall
recipes/core/base/libredox → ../../../local/sources/libredox
```
These symlinks bridge the base workspace's relative path resolution. Without them, the
base workspace's `path = "../syscall"` resolves to a non-existent directory.
## Validation
Run the validation gate to check all recipes:
```bash
./target/release/repo validate-cargo-deps
```
This scans every `Cargo.toml` in `recipes/` and `local/recipes/`, detects:
- Path dependencies on base workspace members without matching `[patch]` entries
- Version dependencies on forked packages that should use the local fork
- Missing or broken symlinks
Exit code 0 = all OK, exit code 1 = warnings found.
## Recipes Currently Violating This Policy
As of 2026-07-02, the following recipes pull `redox_syscall` or `libredox` from crates.io
instead of the local fork. These are policy violations that should be fixed incrementally:
### `redox_syscall` from crates.io (25 recipes)
Recipes using version 0.8 (straightforward conversion to fork):
- `drivers/ehcid`, `drivers/linux-kpi`, `drivers/redbear-btusb`
- `drivers/redox-driver-sys`
- `system/driver-manager`, `system/evdevd`, `system/firmware-loader`
- `system/hwrngd`, `system/iommu`
- `system/redbear-btctl`, `system/redbear-hwutils`
- `system/redbear-sessiond`, `system/redbear-traceroute`
- `system/redbear-wifictl`, `system/thermald`, `system/udev-shim`
- `tui/tlc`
Recipes using version 0.7 (need API adaptation to 0.8):
- `drivers/virtio-inputd`, `system/devfsd`, `system/diskd`
Recipes using version 0.4 (need significant API adaptation to 0.8):
- `system/redbear-accessibility`, `system/redbear-ime`, `system/redbear-keymapd`
- `gpu/redox-drm` (uses `syscall04` for legacy PCI config — separate concern)
### `libredox` from crates.io (21 recipes)
All recipes using `libredox = "0.1"` or `libredox = "0.1.x"` from crates.io should
switch to the local fork at `local/sources/libredox/` (version 0.1.18).
## Migration Plan
1. **Phase 1 (done):** Fix `redox-drm` — the only recipe with a path dep on `daemon`
2. **Phase 2:** Convert all 0.8.x recipes to use the local fork (straightforward)
3. **Phase 3:** Adapt 0.7.x recipes to 0.8.x API and switch to fork
4. **Phase 4:** Adapt 0.4.x recipes to 0.8.x API and switch to fork
Each conversion is: change `version = "0.x"` to `path = "..."`, cook, fix any API breaks.
## Related
- `local/AGENTS.md` § "GOLDEN RULE — Red Bear adapts to upstream, never the reverse"
- `local/AGENTS.md` § "LOCAL FORK MODEL (CORE COMPONENTS)"
- `local/sources/base/Cargo.toml` lines 133-157 — the base workspace `[patch]` section
that ONLY applies within the base workspace
@@ -3,7 +3,7 @@
**Date**: 2026-05-04
**Updated**: 2026-05-04 (MSI T1.1T2.2 implemented, committed, pushed)
**Status**: Active — MSI Phase 1 complete, DMA/Scheduler pending
**Source of truth**: Linux kernel 7.1 (local/reference/linux-7.1/)
**Source of truth**: Linux kernel 7.0 (local/reference/linux-7.0/)
## 1. Problem Statement
@@ -195,7 +195,7 @@ Validation and acceptance
- the AMD C backend still logs linux-kpi quirk-informed IRQ expectations, but firmware gating is no longer duplicated there.
- the PCI quirk extractor foundation has been upgraded so future reviewed GPU quirk imports can rely on explicit handler-body evidence instead of handler-name guessing.
**What A1 does not mean yet:** reviewed Linux 7.1 PCI extraction has not produced enough high-confidence modern Intel/AMD DRM GPU entries to replace the existing hand-authored GPU quirk set. Additional DRM-focused mining and review are still required before quirk-table expansion claims, and Intel-side quirk expansion remains deferred until the Intel runtime policy surface can consume those flags honestly.
**What A1 does not mean yet:** reviewed Linux 7.0 PCI extraction has not produced enough high-confidence modern Intel/AMD DRM GPU entries to replace the existing hand-authored GPU quirk set. Additional DRM-focused mining and review are still required before quirk-table expansion claims, and Intel-side quirk expansion remains deferred until the Intel runtime policy surface can consume those flags honestly.
**Current PCI ID naming policy:** human-readable PCI vendor/device naming now comes from the shipped
canonical `pciids` database, while DRM quirk policy remains on the reviewed Red Bear/Linux-backed
+2 -2
View File
@@ -2,7 +2,7 @@
**Date**: 2026-05-04
**Status**: Authoritative — supersedes CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md, COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md, and HARDWARE-VALIDATION-MATRIX.md
**Source of truth**: Linux kernel 7.1 (`local/reference/linux-7.1/`)
**Source of truth**: Linux kernel 7.0 (`local/reference/linux-7.0/`)
---
@@ -176,7 +176,7 @@ Stream.rs exists (387 lines). NOT runtime-validated.
| **D2.4: Streaming DMA** | `dma_map_single`/`dma_unmap_single` in linux-kpi. Allocates temp buffer, copies data, maps through IOMMU. | ~120 | P1 |
| **D2.5: SWIOTLB** | Bounce buffer allocation for DMA-limited devices. Linux ref: `kernel/dma/swiotlb.c`. | ~200 | P2 |
**Linux Reference Summary (from `local/reference/linux-7.1/`):**
**Linux Reference Summary (from `local/reference/linux-7.0/`):**
| Linux API | Purpose | Red Bear Equivalent |
|---|---|---|
+1 -1
View File
@@ -710,7 +710,7 @@ local/patches/relibc/P4-initgroups.patch
|-------|---------|
| Kernel compiles | `make r.kernel` |
| relibc compiles | `make r.relibc` |
| Full OS builds | `./local/scripts/build-redbear.sh redbear-full` |
| Full OS builds | `make all CONFIG_NAME=redbear-full` |
### 8.2 Runtime Evidence
@@ -1,763 +0,0 @@
# Red Bear OS — Multi-Threading Comprehensive Assessment and Implementation Plan
**Date:** 2026-07-02 (initial assessment); 2026-07-02 (Phase 0c patch recovery complete)
**Scope:** Full-stack multi-threading audit: hardware/SMP, kernel scheduler, kernel futex, kernel syscall ABI, relibc pthreads, userspace threading correctness and performance
**Status:** Authoritative — supersedes `archived/KERNEL-SCHEDULER-MULTITHREAD-IMPROVEMENT-PLAN.md` and `archived/SCHEDULER-REVIEW-FINAL.md` for all threading matters
**Validation levels:** `builds``enumerates``usable``validated``hardware-validated`
---
## UPDATE — Phase 0c Patch Recovery (2026-07-02)
The assessment in §1 below was accurate as of the time of writing. The Phase 0c
patch recovery was then executed in the same session, landing the following
commits on the local kernel fork (`local/sources/kernel/`):
| Commit | Effect |
|--------|--------|
| `ed3f0e1` | **P6-futex-sharding**: replaces single global `Mutex<L1, FutexList>` with 64-shard hash table |
| `5fb42fc` | **RUN_QUEUE_COUNT pre-flight**: defines `pub const RUN_QUEUE_COUNT: usize = 40;` (was missing from patch chain) |
| `cbf051e` | **P7-cache-affine-context (manual)**: surgically inserts `SchedPolicy` enum, `SCHED_PRIORITY_LEVELS`, helper functions, and 9 new Context fields (last_cpu, sched_policy, sched_rt_priority, sched_rr_ticks_consumed, sched_static_prio, sched_rr_quantum, vruntime, futex_pi_*, PhysicalAddress import) |
| `f7652fc` | **P5-context-mod-sched + P8-percpu-sched + P8-percpu-wiring**: PerCpuSched struct with SyncUnsafeCell-wrapped per-CPU run queues, get_percpu_block helper, full per-CPU scheduler wiring in switch.rs (pick_next_from_queues, pick_next_from_global_queues, select_next_context) |
| `7fc8bbf` | **P8-initial-placement + P9-numa-topology + P9-proc-lock-ordering**: least-loaded-CPU spawn, NUMA topology hints, proc scheme lock order fix |
| `327c150` | **`set_sched_policy` + `set_sched_other_prio`**: missing Context methods called by proc scheme handles |
| `e8ec916` | **fadt usize/u32 type mismatch fix**: changes FADT_MIN_SIZE constants to `u32` to match `Sdt::length()` |
| `4789d54` | **SchedPolicy/Name/Priority proc scheme handles**: adds `/proc/<tid>/{name, sched-policy, priority}` paths and read/write handlers |
**Upstream check (bg_27f3578a, 2026-07-02):** verified that `gitlab.redox-os.org/redox-os/kernel`
master (commit `aa7e7d2f44ba7cd9d1b007d37db139b345d46b8a`) has **NONE** of these features. The
local fork is the sole implementation. No upstream cherry-picks are available.
**Plan-vs-actual state:**
| Plan claim (Section 1) | Actual state after Phase 0c |
|---|---|
| "Baseline DWRR scheduler only (no per-CPU queues, no work stealing, no load balancing, no vruntime, no RT scheduling, no cache-affine)" | ✅ All 5 features now present. Per-CPU `PerCpuSched` with `run_queues`, `steal_work()`, `migrate_one_context()`, `maybe_balance_queues()`, `vruntime` CFS-style weighting, `last_cpu` cache-affine vruntime bonus, `SchedPolicy::Fifo`/`RoundRobin` RT scanning in `pick_next_from_queues` |
| "Baseline futex only (WAIT/WAIT64/WAKE — no sharding, no PI, no REQUEUE, no robust, no WAKE_OP, no BITSET)" | 🟡 **Sharding done** (64-shard hash). REQUEUE/PI/robust/WAKE_OP/BITSET still missing. |
| "relibc `sched_*` are all `todo!()`, `pthread_setschedparam` is a no-op, robust mutexes are `todo_skip!`, PI is absent" | 🟡 relibc fork only has the `pthread_cond_signal` POSIX fix so far. `sched_*`, robust, PI still pending in relibc. |
| "❌ Missing from relibc: CPU affinity API, Thread naming" | 🟡 **Kernel side done**`/proc/<tid>/{sched-policy, name, priority}` handles. relibc pthread_setname_np / pthread_setaffinity_np / sched_setscheduler still pending. |
| "cargo check has 1 pre-existing error" | ✅ **Fixed**`cargo check` now exits 0 with 0 errors. |
**Phase 0c status: kernel side complete (all 8 of 8 applicable kernel P5P9 patches
re-applied or made obsolete by the existing refactored scheduler). Remaining work
is in the relibc fork (Phase 0e) and the futex-REQUEUE/PI/robust work (Phase 1).**
The detailed analysis in §1–§9 below is preserved as historical record. The status
column "🚧 Missing" in §1 should be re-read as "now present in the local kernel
fork, pending relibc userspace wiring."
---
---
## 1. Executive Summary
### The Critical Finding — Lost Threading Work
The P5P9 scheduler and futex enhancement work (documented as "complete" in the archived
plans) was **lost during the local fork migration** (2026-06). The local forks at
`local/sources/kernel/` and `local/sources/relibc/` were created from **upstream Redox
baselines** that did NOT include the Red Bear enhancement patches. The patches exist in
`local/patches/kernel/` and `local/patches/relibc/` but are **not wired into the recipes**
(both `recipe.toml` files use `path = "..."` with no `patches = [...]` list).
**Impact:** The running kernel has:
- Baseline DWRR scheduler only (no per-CPU queues, no work stealing, no load balancing, no vruntime, no RT scheduling, no cache-affine)
- Baseline futex only (WAIT/WAIT64/WAKE — no sharding, no PI, no REQUEUE, no robust, no WAKE_OP, no BITSET)
- relibc `sched_*` are all `todo!()`, `pthread_setschedparam` is a no-op, robust mutexes are `todo_skip!`, PI is absent
**Recovery:** 13 of 18 kernel P5P9 patches apply cleanly to the current fork. 5 fail due to
patch-chain dependencies (they expect earlier patches applied first). The bulk of the work is
recoverable by re-applying patches to the forks and committing them.
### What Actually Works Today
| Layer | Status | Detail |
|-------|--------|--------|
| **SMP boot** | ✅ Solid | INIT→SIPI sequence correct, per-CPU PCR via GS_BASE, x2APIC support |
| **Context switching** | ✅ Solid | FPU/SIMD/AVX state save via XSAVE, FSBASE/GSBASE swap (FSGSBASE or MSR), correct callee-saved register save |
| **TLB shootdown protocol** | ✅ Correct | AtomicBool flag + IPI + ack counter with `fence(SeqCst)` race prevention |
| **Basic thread lifecycle** | ✅ Functional | pthread_create/join/detach/exit through proc scheme + redox_rt clone |
| **Basic synchronization** | ✅ Functional | Futex-backed mutex, condvar, rwlock, barrier, spinlock, once |
| **TLS** | ✅ Functional | ELF PT_TLS + pthread_key_create/getspecific/setspecific |
| **Per-CPU data** | ✅ Functional | PercpuBlock via GS_BASE, all per-CPU state accessible |
| **Signal delivery** | ✅ Functional | Shared-memory Sigcontrol pages, per-thread masks, trampoline |
| **Scheduler algorithm** | 🚧 Basic DWRR | 40 priority levels, geometric weights, cooperative preemption (3-tick quantum) |
| **Futex operations** | 🚧 Basic only | WAIT/WAIT64/WAKE with single global mutex |
| **SMP load balancing** | ❌ Missing | No work stealing, no migration, contexts stuck on birth CPU |
| **RT scheduling** | ❌ Missing | No SCHED_FIFO/SCHED_RR, no kernel policy dispatch |
| **Futex REQUEUE** | ❌ Missing | Condvar broadcast causes thundering herd |
| **Robust mutexes** | ❌ Missing | Thread death while holding mutex → permanent deadlock |
| **PI futexes** | ❌ Missing | No priority inheritance → priority inversion risk |
| **CPU affinity API** | ❌ Missing from relibc | Kernel supports sched_affinity field but no userspace API |
| **Thread naming** | ❌ Missing from relibc | Kernel supports name field but no userspace API |
| **Per-page TLB flush** | ❌ Missing | `invalidate_all()` = full CR3 reload on every shootdown |
| **NUMA awareness** | ❌ Missing | No SRAT/SLIT, no proximity domains, flat memory model |
| **IRQ balancing** | ❌ Missing | All legacy IRQs hardwired to BSP |
---
## 2. Layer-by-Layer Assessment
### 2.1 Hardware / SMP Layer
**Files:** `src/acpi/madt/arch/x86.rs`, `src/arch/x86_shared/start.rs`,
`src/arch/x86_shared/device/local_apic.rs`, `src/arch/x86_shared/device/ioapic.rs`,
`src/arch/x86_shared/ipi.rs`, `src/arch/x86_shared/interrupt/ipi.rs`, `src/percpu.rs`,
`src/arch/x86_shared/gdt.rs`
**Verdict: Functional foundation, performance gaps.**
| Component | Status | Detail |
|-----------|--------|--------|
| AP boot (INIT/SIPI) | ✅ validated | Correct trampoline at 0x8000, per-AP PCR/IDT/stack allocation |
| x2APIC mode | ✅ builds | Detected via CPUID, MSR-based access, APIC ID detection |
| Per-CPU PCR via GS_BASE | ✅ validated | `PercpuBlock::current()` reads from PCR, SWAPGS protocol correct |
| IPI send/receive | ✅ functional | 5 IPI kinds (Wakeup/Tlb/Switch/Pit/Profile), broadcast + unicast |
| TLB shootdown | ✅ correct | AtomicBool + IPI + ack with `fence(SeqCst)` race prevention |
| TLB granularity | ❌ coarse | Full CR3 reload (`mov cr3, cr3`) on every shootdown — no INVLPG |
| TLB broadcast | 🚧 sequential | Iterates CPUs individually, doesn't use ICR "all excluding self" shorthand |
| IRQ routing | ❌ BSP-only | Legacy I/O APIC entries hardcode `dest: bsp_apic_id` |
| NUMA | ❌ absent | No SRAT/SLIT, no proximity domains |
| SMT/HT topology | ❌ absent | No cache hierarchy, no hyperthread awareness |
| Idle loop | ✅ functional | MWAIT with deepest C-state or HLT fallback |
| W^X for trampoline | 🚧 minor | Trampoline page briefly W+X, unmapped after AP boot |
### 2.2 Kernel Scheduler Layer
**Files:** `src/context/switch.rs`, `src/context/mod.rs`, `src/context/context.rs`,
`src/context/timeout.rs`
**Verdict: Correct but primitive — DWRR only, no SMP balancing, no RT classes.**
**Algorithm:** Deficit Weighted Round Robin (DWRR)
- 40 priority levels, each a `VecDeque<WeakContextRef>`
- Geometric weights: `SCHED_PRIO_TO_WEIGHT[i] ≈ 1.25^i` (88761 → 15)
- Per-CPU `balance` accumulator drives dequeue decisions
- Quantum: 3 PIT ticks (~12.2ms) per scheduling round
- Cooperative preemption: `preempt_locks > 0` disables preemption
**Global locks:**
- `RUN_CONTEXTS: Mutex<L1, RunContextData>` — all 40 priority queues under one L1 lock
- `IDLE_CONTEXTS: Mutex<L2, VecDeque<WeakContextRef>>` — sleeping contexts
- `CONTEXT_SWITCH_LOCK: AtomicBool` — global CAS spinlock serializing all context switches
**What's missing (all was in lost P5P9 work):**
| Gap | Lost Patch | Recoverable? |
|-----|-----------|-------------|
| Per-CPU run queues (eliminate global L1) | P6-percpu-runqueues, P8-percpu-sched, P8-percpu-wiring | ✅ applies cleanly |
| Work stealing | P8-work-stealing | ❌ needs rebase (depends on per-CPU wiring) |
| Initial placement (least-loaded CPU) | P8-initial-placement | ✅ applies cleanly |
| Load balancing | P8-load-balance (absorbed) | needs verification |
| Vruntime tracking + min-vruntime selection | P6-vruntime-switch | ✅ applies cleanly |
| SchedPolicy enum (FIFO/RR/Other) | P5-sched-rt-policy | ✅ applies cleanly |
| RT scheduling dispatch | P5-sched-rt-policy | ✅ applies cleanly |
| Cache-affine scheduling | P7-cache-affine-switch | ✅ applies cleanly |
| NUMA topology hints | P9-numa-topology | ✅ applies cleanly |
### 2.3 Kernel Futex Layer
**File:** `src/syscall/futex.rs`
**Verdict: Baseline only — critical operations missing for desktop workloads.**
| Operation | Status | Impact of Absence |
|-----------|--------|-------------------|
| `FUTEX_WAIT` (32-bit) | ✅ | — |
| `FUTEX_WAIT64` (64-bit) | ✅ | — |
| `FUTEX_WAKE` | ✅ | — |
| `FUTEX_REQUEUE` | ❌ returns EINVAL | `pthread_cond_broadcast` wakes ALL waiters (thundering herd) |
| `FUTEX_CMP_REQUEUE` | ❌ not defined | Same + atomicity gap |
| `FUTEX_WAKE_OP` | ❌ not defined | glibc mutex fast path unavailable |
| `FUTEX_WAIT_BITSET` | ❌ not defined | `pselect`/`ppoll` optimization unavailable |
| `FUTEX_WAKE_BITSET` | ❌ not defined | Targeted wake unavailable |
| `FUTEX_LOCK_PI` / `UNLOCK_PI` | ❌ not defined | Priority inversion unprotected |
| Robust futex list | ❌ not defined | Thread death → permanent deadlock |
| Futex sharding (per-futex lock) | ❌ single global L1 mutex | All futex ops on all CPUs contend on one lock |
| Process-private futexes | ❌ global table | Unnecessary cross-process visibility |
**Architecture:**
```
static FUTEXES: Mutex<L1, FutexList> // single global lock
type FutexList = HashMap<PhysicalAddress, Vec<FutexEntry>>
```
Physical address is the key (enables cross-address-space futex via MAP_SHARED).
Virtual address + Weak<AddrSpaceWrapper> used for CoW disambiguation.
**Recoverable work (lost patches):**
| Feature | Lost Patch | Applies? |
|---------|-----------|----------|
| 64-shard hash table | P6-futex-sharding | ✅ cleanly |
| FUTEX_REQUEUE + CMP_REQUEUE | P8-futex-requeue | ❌ needs rebase |
| PI futex (LOCK_PI/UNLOCK_PI/TRYLOCK_PI) | P8-futex-pi | ❌ needs rebase |
| PI CAS fix | P9-futex-pi-cas-fix | ❌ needs rebase |
| Robust futex list | P8-futex-robust | ❌ needs rebase |
The 4 failing patches likely fail because they depend on sharding (P6-futex-sharding) being
applied first. Apply in order: P6-sharding → P8-requeue → P8-pi → P8-robust → P9-pi-cas-fix.
### 2.4 Kernel Syscall ABI Layer
**Files:** `src/syscall/mod.rs`, `src/syscall/futex.rs`, `src/syscall/time.rs`,
`src/syscall/process.rs`, `local/sources/syscall/src/number.rs`, `src/scheme/proc.rs`
**Verdict: Minimal surface — most threading done via proc scheme, not syscalls.**
The kernel defines only ~35 syscall numbers. Threading-relevant ones:
| Syscall | Status | Notes |
|---------|--------|-------|
| `SYS_FUTEX` (240) | ✅ partial | WAIT/WAIT64/WAKE only |
| `SYS_YIELD` (158) | ✅ | `context::switch()` + signal handler |
| `SYS_FMAP` (900) | ✅ | Anonymous + file-backed mmap |
| `SYS_FUNMAP` (92) | ✅ | munmap |
| `SYS_MPROTECT` (125) | ✅ | |
| `SYS_MREMAP` (155) | ✅ | |
| `SYS_NANOSLEEP` (162) | ✅ | EINTR-aware |
| `SYS_CLOCK_GETTIME` (265) | ✅ partial | REALTIME + MONOTONIC only |
**Threading done via proc scheme (not syscalls):**
| Operation | Mechanism |
|-----------|-----------|
| Thread/process creation | `proc:` scheme: open "new-context", share addr_space + files via kdup |
| waitpid | `proc:` scheme: `EVENT_READ` on context fd |
| getpid/gettid | `proc:` scheme: read "attrs" handle |
| kill/tkill | `proc:` scheme: `ForceKill` / `Interrupt` ContextVerb |
| CPU affinity | `proc:` scheme: write "sched-affinity" handle |
| Priority | `proc:` scheme: write "attrs" prio field |
| Signal setup | `proc:` scheme: write "sighandler" + shared Sigcontrol pages |
| TLS base (FSBASE) | `proc:` scheme: write "regs/env" EnvRegisters |
**Completely missing syscalls (no number, no handler):**
`clone`, `fork`, `vfork`, `waitpid`, `wait4`, `kill`, `tkill`, `tgkill`, `arch_prctl`,
`set_thread_area`, `set_tid_address`, `set_robust_list`, `get_robust_list`,
`sched_setaffinity`, `sched_getaffinity`, `sched_setscheduler`, `sched_getparam`,
`sigaction`, `sigprocmask`, `sigpending`, `sigsuspend`, `sigtimedwait`,
`timer_create`, `timer_settime`, `timer_delete`, `timerfd_create`,
`getrusage`, `setrlimit`, `getrlimit`, `times`
### 2.5 relibc Pthread Layer
**Files:** `src/pthread/mod.rs`, `src/sync/*.rs`, `src/header/pthread/*.rs`,
`src/header/sched/mod.rs`, `src/ld_so/tcb.rs`, `src/platform/redox/mod.rs`
**Verdict: Core pthreads solid, scheduling/robust/PI absent, several POSIX gaps.**
#### Fully Working (futex-backed)
| API Group | Backend | Notes |
|-----------|---------|-------|
| `pthread_create/join/detach/exit` | redox_rt clone + Waitval | Stack via mmap, TLS via Tcb::new() |
| `pthread_cancel/setcancelstate/testcancel` | SIGRT_RLCT_CANCEL (33) | Deferred cancellation only |
| `pthread_mutex_*` (normal/recursive/errorcheck) | AtomicU32 CAS + futex_wait/wake | 3-state: unlocked/locked/waiters |
| `pthread_cond_*` | Two-counter futex design | CLOCK_REALTIME only (monotonic = stub) |
| `pthread_rwlock_*` | AtomicU32 + futex | Reader count + WAITING_WR bit |
| `pthread_barrier_*` | Mutex + Cond | gen_id wrapping counter |
| `pthread_spin_*` | AtomicI32 CAS | No futex, pure spinning |
| `pthread_once` | 3-state futex (UNINIT→INITING→INIT) | |
| `pthread_key_create/getspecific/setspecific/delete` | BTreeMap global + thread_local values | Destructor iteration per POSIX |
| `pthread_sigmask` | Delegates to sigprocmask | |
| `pthread_kill` | redox_rt::rlct_kill | |
| `pthread_atfork` | Thread-local LinkedList hooks | |
| ELF TLS (`__thread` / `#[thread_local]`) | PT_TLS + Tcb | Static + dynamic DTV for dlopen |
| `pthread_attr_*` (getters/setters) | RlctAttr struct | |
#### Stubs / No-ops / Missing
| API | Status | Root Cause |
|-----|--------|------------|
| `sched_get_priority_max/min` | `todo!()` | Kernel has no scheduling policy API |
| `sched_getparam/setparam` | `todo!()` | Same |
| `sched_setscheduler` | `todo!()` | Same |
| `sched_rr_get_interval` | `todo!()` | Same |
| `pthread_setschedparam` | No-op (returns Ok) | Kernel ignores policy |
| `pthread_setschedprio` | No-op (returns Ok) | Kernel ignores priority change |
| `pthread_getschedparam` | `todo!()` | |
| `pthread_getcpuclockid` | ENOENT | No per-thread CPU clock |
| `pthread_mutex_consistent` | `todo_skip!` | Robust mutex not implemented |
| `pthread_mutex_getprioceiling` | `todo_skip!` | Priority ceiling not implemented |
| `pthread_mutex_setprioceiling` | `todo_skip!` | Same |
| `pthread_mutexattr_setprotocol` (PRIO_INHERIT) | Accepted, no-op | PI futex missing |
| `pthread_mutexattr_setrobust` (ROBUST) | Accepted, no-op | Robust futex missing |
| `pthread_cond_init` CLOCK_MONOTONIC | `todo_skip!` | |
| `pthread_cond_signal` | Calls broadcast (wakes ALL) | Missing FUTEX_REQUEUE optimization |
| `pthread_setaffinity_np` | Not defined | |
| `pthread_getaffinity_np` | Not defined | |
| `pthread_setname_np` | Not defined | |
| `pthread_getname_np` | Not defined | |
| `pthread_setcanceltype` | Always returns DEFERRED | ASYNC not tracked |
| Guard pages | Attribute stored, not mapped | No PROT_NONE page before stack |
| PTHREAD_KEYS_MAX limit | Not checked | |
---
## 3. Gap Classification
### 3.1 Correctness Gaps (Must Fix — Silent Data Corruption or Deadlock)
| # | Gap | Impact | Fix Location |
|---|-----|--------|-------------|
| C1 | **No robust mutexes** | Thread death while holding mutex → permanent deadlock for all waiters | Kernel: robust futex list + relibc: pthread_mutex_consistent |
| C2 | **No PI futexes** | Priority inversion: low-prio thread blocks high-prio thread indefinitely | Kernel: FUTEX_LOCK_PI/UNLOCK_PI + relibc: mutexattr_setprotocol |
| C3 | **`pthread_cond_signal` wakes ALL** | Correctness: wastes CPU. Performance: thundering herd on every signal | relibc: use true wake(1) — may need FUTEX_REQUEUE |
| C4 | **`fork()` not thread-safe** | `pthread_atfork` hooks exist but child inherits locked mutexes | relibc: implement atfork child handlers properly |
### 3.2 Performance Gaps (Must Fix for Desktop Responsiveness)
| # | Gap | Impact | Fix Location |
|---|-----|--------|-------------|
| P1 | **No SMP load balancing** | Cores sit idle while others are overloaded | Kernel: work stealing + initial placement |
| P2 | **No futex sharding** | Single global L1 mutex for ALL futex ops on ALL CPUs | Kernel: 64-shard hash table |
| P3 | **No FUTEX_REQUEUE** | `pthread_cond_broadcast` wakes all → thundering herd | Kernel: REQUEUE + CMP_REQUEUE |
| P4 | **Full TLB flush on every shootdown** | Per-page mprotect/munmap flushes entire TLB on all cores | Kernel: INVLPG-based selective flush |
| P5 | **Global context switch lock** | Serialization bottleneck beyond ~8 cores | Kernel: per-CPU context switch (needs per-CPU run queues) |
| P6 | **All IRQs to BSP** | CPU 0 handles all interrupts, cache thrash, latency | Kernel: IRQ steering in I/O APIC + MSI/MSI-X dest field |
| P7 | **No RT scheduling** | Audio/compositor threads can't get priority | Kernel: SchedPolicy + RT dispatch + relibc: sched_setscheduler |
### 3.3 POSIX Completeness Gaps (Must Fix for Application Compatibility)
| # | Gap | Impact | Fix Location |
|---|-----|--------|-------------|
| X1 | `sched_*` all `todo!()` | Applications calling sched_setscheduler panic | relibc: implement via proc scheme |
| X2 | `pthread_setschedparam` no-op | Apps can't change thread priority | relibc: wire to proc scheme prio write |
| X3 | `pthread_setaffinity_np` missing | Apps can't pin threads to CPUs | relibc: implement via proc scheme affinity write |
| X4 | `pthread_setname_np` missing | Debugging harder (no thread names in /proc) | relibc: implement via proc scheme name write |
| X5 | `pthread_getcpuclockid` ENOENT | Per-thread profiling impossible | relibc + kernel: expose cpu_time via clock |
| X6 | Guard pages not mapped | Stack overflow → silent corruption, no SIGSEGV | relibc: mmap PROT_NONE guard page in pthread_create |
| X7 | `pthread_cond_init` monotonic stub | CLOCK_MONOTONIC condvars use REALTIME (affected by wall clock jumps) | relibc: implement monotonic condvar |
---
## 4. Implementation Plan
### Phase 0: Patch Recovery — Re-Apply Lost Threading Work (Week 12)
**Goal:** Recover the P5P9 work that was lost during the local fork migration.
**This is the highest-priority phase — it restores ~6 months of work with minimal new code.**
#### 0.1 — Re-apply kernel scheduler patches to local fork
Apply in dependency order to `local/sources/kernel/`:
| Order | Patch | Status | Action |
|-------|-------|--------|--------|
| 1 | P6-futex-sharding | ✅ applies | Commit directly |
| 2 | P6-percpu-runqueues | ✅ applies | Commit directly |
| 3 | P8-percpu-sched | ✅ applies | Commit directly |
| 4 | P8-percpu-wiring | ✅ applies | Commit directly |
| 5 | P8-initial-placement | ✅ applies | Commit directly |
| 6 | P5-sched-rt-policy | ✅ applies | Commit directly |
| 7 | P5-context-mod-sched | ✅ applies | Commit directly |
| 8 | P6-vruntime-switch | ✅ applies | Commit directly |
| 9 | P7-cache-affine-switch | ✅ applies | Commit directly |
| 10 | P9-numa-topology | ✅ applies | Commit directly |
| 11 | P9-proc-lock-ordering | ✅ applies | Commit directly |
| 12 | P8-work-stealing | ❌ needs rebase | Rebase against 111, then apply |
| 13 | P8-futex-requeue | ❌ needs rebase | Rebase against P6-sharding (#1), then apply |
| 14 | P8-futex-pi | ❌ needs rebase | Rebase against #13, then apply |
| 15 | P8-futex-robust | ❌ needs rebase | Rebase against #14, then apply |
| 16 | P9-futex-pi-cas-fix | ❌ needs rebase | Rebase against #14, then apply |
| 17 | P7-scheduler-improvements | ❌ needs rebase | Rebase against 111, then apply |
**Verification after each patch:**
```bash
cd local/sources/kernel
cargo check # must pass
```
#### 0.2 — Re-apply relibc threading patches to local fork
Apply to `local/sources/relibc/`:
| Patch | Action |
|-------|--------|
| P3-threads.patch | ✅ applies — commit |
| P3-barrier-smp-futex (from absorbed/) | Verify already in fork; if not, apply |
| P3-pthread-signal-races (from absorbed/) | Verify already in fork |
| P3-pthread-yield (from absorbed/) | Verify already in fork |
| P5-robust-mutexes (from absorbed/) | Verify; re-apply if missing |
| P5-robust-mutex-enotrec-fix (from absorbed/) | Same |
| P5-sched-api (from absorbed/) | Same |
| P7-pthread-affinity (from absorbed/) | Same |
| P7-pthread-setname (from absorbed/) | Same |
| P7-setpriority (from absorbed/) | Same |
| P9-spin-and-barrier (from absorbed/) | Same |
| P9-spin-fix (from absorbed/) | Same |
| P3-semaphore-comprehensive | ✅ applies |
**Verification:**
```bash
cd local/sources/relibc
make all # must pass
touch relibc && make prefix # rebuild prefix with new libc
```
#### 0.3 — Build and smoke test
```bash
export REDBEAR_ALLOW_PROTECTED_FETCH=1
./local/scripts/build-redbear.sh --upstream redbear-mini
make qemu # verify boot + basic operation
```
**Success criteria:** redbear-mini boots, multi-threaded daemons (pcid, xhcid) start, no kernel panic.
---
### Phase 1: Futex Completeness (Week 24)
**Goal:** Close the futex operation gaps that affect correctness and performance.
**Depends on:** Phase 0 complete (sharding applied first).
#### 1.1 — FUTEX_REQUEUE + FUTEX_CMP_REQUEUE
**Kernel:** `src/syscall/futex.rs`
- Add `FUTEX_REQUEUE` and `FUTEX_CMP_REQUEUE` to the futex dispatcher
- Implement: move up to `val` waiters from addr1 → addr2, optionally compare `*addr1 == val2`
- Requires locking TWO shards (acquire both in deterministic order to avoid deadlock)
**relibc:** `src/sync/cond.rs`
- Change `pthread_cond_broadcast` to use `FUTEX_REQUEUE` (move waiters from condvar futex to mutex futex)
- Change `pthread_cond_signal` to wake exactly 1 (not all)
**Impact:** Eliminates thundering herd on every `pthread_cond_broadcast`. Major win for Qt event loop, KWin compositor, Mesa worker threads.
#### 1.2 — PI Futexes (FUTEX_LOCK_PI / FUTEX_UNLOCK_PI / FUTEX_TRYLOCK_PI)
**Kernel:** `src/syscall/futex.rs`
- Add `PiState` tracking per futex: owner context + waiter list with priorities
- On `LOCK_PI` block: boost owner's priority to waiter's priority
- On `UNLOCK_PI`: restore original priority, wake highest-priority waiter
- Requires kernel RT scheduling (Phase 0.1 #67: P5-sched-rt-policy)
**relibc:** `src/sync/pthread_mutex.rs`
- Implement `PTHREAD_PRIO_INHERIT` protocol path using PI futex
- Replace `todo_skip!` in `pthread_mutex_consistent` with real implementation
#### 1.3 — Robust Futex List
**Kernel:** `src/syscall/futex.rs` + `src/context/context.rs`
- Add `robust_list_head: Option<usize>` to `Context` struct
- Implement `set_robust_list` / `get_robust_list` via proc scheme or syscall
- On thread exit (`exit_this_context`): walk robust list, set `FUTEX_OWNER_DIED` bit, wake one waiter with `EOWNERDEAD`
**relibc:** `src/sync/pthread_mutex.rs`
- Implement robust list registration in `pthread_mutex_lock`
- Implement `pthread_mutex_consistent`: clear `EOWNERDEAD` state
- Replace `todo_skip!` with real implementation
#### 1.4 — FUTEX_WAKE_OP
**Kernel:** `src/syscall/futex.rs`
- Implement atomic op + wake: perform op on addr2, then wake up to `val` waiters on addr1
- Operations: set, add, or, andn, xor, with comparison condition
**Impact:** glibc mutex fast path optimization. Not critical for relibc but helps ported glibc-linked binaries.
---
### Phase 2: SMP Scheduling Quality (Week 36)
**Goal:** Make multi-core actually distribute work.
**Depends on:** Phase 0 complete (per-CPU queues applied).
#### 2.1 — Work stealing (recover + fix)
**Kernel:** `src/context/switch.rs`
- On `select_next_context()` empty local queue: steal from victim CPU
- Pick victim by round-robin, steal highest-priority runnable context
- Limit steal batch size (12 contexts per steal attempt)
- Send `IpiKind::Wakeup` to target CPU if stealing woke it from idle
**Recovery:** P8-work-stealing needs rebase against per-CPU wiring.
#### 2.2 — Load balancing (recover + verify)
**Kernel:** `src/context/switch.rs`
- Periodic balance trigger (every N ticks or when queue depth difference > threshold)
- Migrate contexts from overloaded CPU to most-idle CPU
- Respect `sched_affinity` mask during migration
**Recovery:** P8-load-balance is in absorbed/ — verify it's in the fork after Phase 0.
#### 2.3 — Reschedule IPI
**Kernel:** `src/arch/x86_shared/ipi.rs` + `src/context/switch.rs`
- When waking a context on a different CPU, send `IpiKind::Switch` to that CPU
- Currently the Switch IPI exists but is not used by the scheduler
#### 2.4 — Per-page TLB flush (INVLPG)
**Kernel:** `rmm/src/arch/x86_64.rs` + `src/context/memory.rs`
- Add `invalidate_page(addr)` using `invlpg` instruction
- Modify `Flusher` to track individual pages and use INVLPG when ≤ N pages affected
- Fall back to CR3 reload only for large-scale invalidations
**Impact:** Every `mprotect`/`mmap`/`munmap` on a multi-threaded process currently flushes the ENTIRE TLB on every core. This is one of the most impactful single fixes.
#### 2.5 — TLB broadcast optimization
**Kernel:** `src/percpu.rs`
- Replace per-CPU sequential `shootdown_tlb_ipi(Some(id))` loop with ICR "all excluding self" (destination shorthand 0b11)
- Single IPI + global ack counter instead of N individual IPIs + N ack counters
---
### Phase 3: RT Scheduling (Week 46)
**Goal:** Allow applications to request real-time scheduling for latency-sensitive threads.
**Depends on:** Phase 0 (SchedPolicy applied) + Phase 2 (per-CPU queues).
#### 3.1 — Kernel RT scheduling dispatch
**Kernel:** `src/context/switch.rs` (from P5-sched-rt-policy — recovered in Phase 0)
- `select_next_context()` passes:
1. SCHED_FIFO contexts (highest RT priority first, no preemption within same prio)
2. SCHED_RR contexts (highest RT priority first, round-robin within same prio)
3. SCHED_OTHER contexts (existing DWRR/vruntime)
- SCHED_RR quantum: configurable per-context (default 100ms)
#### 3.2 — relibc sched_* API completion
**relibc:** `src/header/sched/mod.rs`
Replace ALL `todo!()` stubs:
| Function | Implementation |
|----------|---------------|
| `sched_getscheduler(pid)` | Read policy from proc scheme attrs |
| `sched_setscheduler(pid, policy, param)` | Write policy + RT priority via proc scheme |
| `sched_getparam(pid, param)` | Read RT priority from proc scheme |
| `sched_setparam(pid, param)` | Write RT priority via proc scheme |
| `sched_get_priority_max(policy)` | Return 99 for FIFO/RR, 0 for OTHER |
| `sched_get_priority_min(policy)` | Return 1 for FIFO/RR, 0 for OTHER |
| `sched_rr_get_interval(pid, tp)` | Return SCHED_RR quantum (100ms default) |
#### 3.3 — pthread_setschedparam wiring
**relibc:** `src/pthread/mod.rs`
- Replace `set_sched_param` no-op with real proc scheme call
- Replace `set_sched_priority` no-op with real proc scheme call
---
### Phase 4: POSIX Pthread Completeness (Week 58)
**Goal:** Close remaining POSIX gaps that block application compatibility.
**Depends on:** Phase 0 + Phase 3 (for sched API).
#### 4.1 — pthread_setaffinity_np / pthread_getaffinity_np
**relibc:** `src/header/pthread/mod.rs` + `src/header/sched/mod.rs`
- Implement using proc scheme "sched-affinity" write/read
- Define `cpu_set_t` type and `CPU_SET/CPU_CLR/CPU_ZERO/CPU_ISSET` macros
#### 4.2 — pthread_setname_np / pthread_getname_np
**relibc:** `src/header/pthread/mod.rs`
- Implement using proc scheme name write/read (kernel already supports 32-char name field)
#### 4.3 — pthread_cond_init CLOCK_MONOTONIC
**relibc:** `src/sync/cond.rs`
- Replace `todo_skip!` with real monotonic clock support
- Store clock choice in cond struct, use `CLOCK_MONOTONIC` for deadline calculations
#### 4.4 — Guard pages
**relibc:** `src/pthread/mod.rs`
- In `pthread_create`, when allocating stack via mmap:
- Map `[stack_base, stack_base + guard_size)` with `PROT_NONE`
- Map `[stack_base + guard_size, stack_base + guard_size + stack_size)` with `PROT_READ | PROT_WRITE`
- On thread exit, munmap both regions
#### 4.5 — pthread_getcpuclockid
**relibc:** `src/header/pthread/mod.rs`
- Return `CLOCK_THREAD_CPUTIME_ID` (requires kernel support — add clock to `clock_gettime`)
**Kernel:** `src/syscall/time.rs`
- Add `CLOCK_THREAD_CPUTIME_ID` → read `context.cpu_time`
#### 4.6 — PTHREAD_KEYS_MAX enforcement
**relibc:** `src/header/pthread/tls.rs`
- Check `NEXTKEY` against `PTHREAD_KEYS_MAX` (1024) before allocating
---
### Phase 5: IRQ Steering and NUMA (Week 812)
**Goal:** Distribute interrupt load and respect memory locality.
**Depends on:** Phase 2 (per-CPU infrastructure).
#### 5.1 — IRQ steering
**Kernel:** `src/arch/x86_shared/device/ioapic.rs` + `src/arch/x86_shared/idt.rs`
- Change I/O APIC redirection `dest` from `bsp_apic_id` to round-robin or RSS hash
- Add per-CPU legacy IRQ handlers in IDT (not just BSP)
- For MSI/MSI-X: set destination CPU in Message Address register
#### 5.2 — NUMA topology discovery
**Kernel:** `src/acpi/` (from P9-numa-topology — recovered in Phase 0)
- Parse SRAT (Static Resource Affinity Table) for proximity domains
- Parse SLIT (System Locality Distance Information Table) for inter-node distances
- Store `NumaTopology` in kernel for O(1) scheduling lookups
#### 5.3 — NUMA-aware memory allocation
**Kernel:** `src/memory/` + frame allocator
- Track frame NUMA node in `Frame` or `PageInfo`
- On allocation, prefer frames from requesting CPU's NUMA node
- Fallback to remote node when local node is exhausted
---
## 5. Dependency Chain
```
Phase 0 (Patch Recovery) ← BLOCKING FOR ALL OTHERS
├──► Phase 1 (Futex Completeness)
│ │
│ ├──► 1.1 REQUEUE ──► condvar performance
│ ├──► 1.2 PI ──► priority inversion fix (needs Phase 3.1)
│ ├──► 1.3 Robust ──► deadlock prevention
│ └──► 1.4 WAKE_OP ──► glibc compat
├──► Phase 2 (SMP Scheduling)
│ │
│ ├──► 2.1 Work stealing ──► core utilization
│ ├──► 2.2 Load balancing ──► fair distribution
│ ├──► 2.3 Reschedule IPI ──→ cross-CPU wakeup
│ ├──► 2.4 Per-page TLB ──► mmap/mprotect performance
│ └──► 2.5 TLB broadcast ──► IPI efficiency
├──► Phase 3 (RT Scheduling)
│ │
│ ├──► 3.1 Kernel RT dispatch (from Phase 0)
│ ├──► 3.2 relibc sched_* API ──► POSIX compat
│ └──► 3.3 pthread_setschedparam ──► app priority control
├──► Phase 4 (POSIX Pthread Completeness)
│ │
│ ├──► 4.1 Affinity API ──► CPU pinning
│ ├──► 4.2 Thread naming ──► debuggability
│ ├──► 4.3 Monotonic condvar ──► clock correctness
│ ├──► 4.4 Guard pages ──► stack overflow detection
│ ├──► 4.5 CPU clock ──► per-thread profiling
│ └──► 4.6 Keys max ──► resource limit
└──► Phase 5 (IRQ + NUMA)
├──► 5.1 IRQ steering ──► interrupt distribution
├──► 5.2 NUMA topology ──► (from Phase 0)
└──► 5.3 NUMA allocator ──► memory locality
```
**Parallel work possible:**
- Phase 1 + Phase 2 + Phase 3 can run in parallel after Phase 0
- Phase 4 items are independent of each other
- Phase 5 depends on Phase 2 but not on Phase 1/3/4
---
## 6. Validation Plan
### 6.1 Build Evidence
| Check | Command |
|-------|---------|
| Kernel compiles | `make r.kernel` |
| relibc compiles | `make r.relibc` |
| Prefix rebuilt | `touch relibc kernel && make prefix` |
| Full OS builds | `./local/scripts/build-redbear.sh redbear-mini` |
### 6.2 Runtime Evidence (QEMU)
| Test | Verification |
|------|-------------|
| Multi-threaded boot | `make qemu QEMUFLAGS="-smp 4"` — all 4 CPUs active |
| pthread smoke test | Guest: compile + run simple pthread_create/join/mutex test |
| Work stealing | Guest: spawn 8 threads on 4-CPU QEMU, verify all CPUs utilized |
| Futex REQUEUE | Guest: condvar broadcast benchmark — waiters wake in ≤2 batches, not N |
| PI futex | Guest: priority inversion test — high-prio thread unblocked within 1 tick |
| Robust mutex | Guest: kill thread holding mutex, verify EOWNERDEAD recovery |
| RT scheduling | Guest: SCHED_FIFO thread preempts SCHED_OTHER within 100μs |
| CPU affinity | Guest: pin thread to CPU 1, verify it never runs on CPU 0 |
| Thread naming | Guest: `cat /scheme/proc/*/name` shows set names |
| Guard pages | Guest: overflow stack, verify SIGSEGV (not silent corruption) |
| TLB efficiency | Guest: mprotect benchmark — compare TLB miss rate before/after |
### 6.3 Validation Scripts (to create)
```bash
local/scripts/test-threading-qemu.sh # Comprehensive threading smoke test
local/scripts/test-futex-requeue-qemu.sh # REQUEUE-specific test
local/scripts/test-futex-pi-qemu.sh # PI futex test
local/scripts/test-futex-robust-qemu.sh # Robust mutex test
local/scripts/test-sched-rt-qemu.sh # RT scheduling latency test
local/scripts/test-sched-balance-qemu.sh # Load balancing on multi-vCPU
local/scripts/test-threading-baremetal.sh # Bare metal multi-threaded stress
```
---
## 7. Estimated Effort
| Phase | Duration | New Code | Recovery | Dependencies |
|-------|----------|----------|----------|-------------|
| Phase 0: Patch Recovery | 12 weeks | Minimal (rebase 5 patches) | 13 patches apply directly | None |
| Phase 1: Futex Completeness | 23 weeks | REQUEUE impl + WAKE_OP | PI/robust from P8 patches | Phase 0 |
| Phase 2: SMP Scheduling | 34 weeks | TLB INVLPG + broadcast opt | Work stealing from P8 | Phase 0 |
| Phase 3: RT Scheduling | 12 weeks | relibc sched_* API | RT dispatch from P5 | Phase 0 |
| Phase 4: POSIX Pthread | 23 weeks | Affinity/naming/guard/clock | Partial from P7 patches | Phase 0, 3 |
| Phase 5: IRQ + NUMA | 34 weeks | IRQ steering + NUMA allocator | NUMA topology from P9 | Phase 0, 2 |
**Total:** 1218 weeks with 12 developers. Phase 0 alone recovers the majority of the value in 12 weeks.
---
## 8. Integration with Existing Plans
| Plan | Relationship |
|------|-------------|
| `CONSOLE-TO-KDE-DESKTOP-PLAN.md` | **Consumer** — Phase 3 (KWin) needs PI futex + RT scheduling; Phase 2 (compositor) needs work stealing |
| `IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` | **Sibling** — IRQ steering (Phase 5.1) belongs to both plans |
| `DRM-MODERNIZATION-EXECUTION-PLAN.md` | **Consumer** — GPU worker threads benefit from load balancing + affinity |
| `IMPLEMENTATION-MASTER-PLAN.md` | **Parent** — this plan covers the kernel threading substrate |
| `CPU-DMA-IRQ-MSI-SCHEDULER-FIX-PLAN.md` | **Sibling** — overlaps on scheduler/IRQ delivery |
---
## 9. Bottom Line
The Red Bear OS threading stack is **functional for basic single-threaded and lightly-threaded
workloads**. The SMP boot, context switching, TLB shootdown, and basic futex operations are
correct.
The **critical problem** is that 6 months of threading enhancement work (P5P9 patches) was
lost during the local fork migration. This work exists as patch files that apply cleanly to
the current fork — **Phase 0 (Patch Recovery) is the single highest-ROI action**.
After Phase 0, the remaining gaps are:
1. **Futex REQUEUE/PI/robust** — for condvar performance and deadlock prevention
2. **SMP work stealing + load balancing** — for multi-core utilization
3. **RT scheduling** — for audio/compositor thread priority
4. **POSIX pthread completeness** — for application compatibility
5. **IRQ steering + NUMA** — for multi-socket performance
The **desktop-critical path** (KWin responsiveness) requires Phases 03. The
**server-critical path** (multi-socket, NUMA) adds Phase 5. Phase 4 (POSIX completeness)
benefits all paths but is not desktop-blocking.
-959
View File
@@ -1,959 +0,0 @@
# Red Bear OS Networking and TCP/IP Stack Improvement Plan
## Purpose
This document assesses the current network and TCP/IP stack in Red Bear OS for completeness
and quality, cross-references it against the Linux 7.1 reference implementation
(`local/reference/linux-7.1/`) and upstream Redox OS activity (20242026), and defines the
next improvement plan in execution order.
This is the **canonical current implementation plan** for the userspace TCP/IP stack
(`netstack` / `smolnetd`), the Ethernet driver family, the POSIX socket surface in relibc,
network configuration (`netcfg`, `dhcpd`, `redbear-netctl`), and the runtime properties of
the scheme-based packet path.
When another document discusses the smolnetd daemon, `scheme:tcp` / `scheme:udp` /
`scheme:icmp` / `scheme:ip` / `scheme:netcfg`, the `driver-network` trait, the Ethernet
driver family (`e1000d`, `ixgbed`, `rtl8139d`, `rtl8168d`, `virtio-netd`), or TCP/IP
performance and protocol coverage, prefer this file for:
- the current robustness judgment,
- the current implementation order,
- the current validation/proof expectations,
- and the current language for build-visible vs runtime-proven vs hardware-validated claims.
It is grounded in the current repository state, especially:
- `local/sources/base/netstack/` (the TCP/IP daemon sources)
- `local/sources/base/drivers/net/` (Ethernet driver family + `driver-network` library)
- `local/sources/base/dhcpd/` (DHCP client)
- `local/sources/relibc/src/header/sys_socket/` and `local/sources/relibc/src/platform/redox/socket.rs`
- `local/sources/libredox/src/lib.rs` (SocketCall enum)
- `local/sources/kernel/src/scheme/{mod,user}.rs` (scheme dispatch)
- `local/recipes/drivers/redbear-iwlwifi/` (Intel Wi-Fi transport)
- `config/redbear-{netctl,wifi-experimental,bluetooth-experimental,mini,full}.toml`
Companion plans that share authority over networking-adjacent surfaces:
- `local/docs/WIFI-IMPLEMENTATION-PLAN.md` — canonical for the Wi-Fi control plane, Intel
transport, `redbear-iwlwifi`, `linux-kpi` wireless layer, and `redbear-wifictl`.
- `local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` — canonical for the PCI
interrupt plumbing, MSI/MSI-X delivery, and IOMMU work that NIC drivers depend on.
- `local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md` — canonical for the Bluetooth stack.
This plan covers the **wired TCP/IP stack and the socket surface**. Where Wi-Fi intersects
with the IP datapath (for example, hooking a Wi-Fi link-layer into the `Router`), the
Wi-Fi plan owns the wireless-specific detail and this plan owns the integration contract.
## Validation States
| State | Meaning |
|---|---|
| **builds** | Compiles in-tree |
| **host-tested** | Tests pass on Linux host with synthesized fixtures |
| **qemu-proven** | Behavior confirmed in QEMU with a virtio-net or e1000 NIC |
| **validated** | Behavior confirmed on real bare-metal hardware with evidence |
| **experimental** | Available for bring-up, not support-promised |
| **missing** | No in-tree implementation |
## Current State
### Architecture at a Glance
Red Bear OS, like upstream Redox, has **zero in-kernel networking**. The kernel provides
only the scheme dispatch surface (`scheme/user.rs` proxies `open`/`read`/`write`/`dup`/
`call` to userspace daemons) and the global `event:` scheme for I/O readiness notification.
All TCP/IP logic lives in the userspace `netstack` daemon, built on **smoltcp 0.12.0**.
```
Application (POSIX socket, libstd TcpStream, curl, ssh, ...)
│ relibc sys_socket: socket(), connect(), send(), recv(), ...
│ → open("/scheme/tcp", ...) → dup(fd, "1.2.3.4:80")
Kernel scheme router (kernel/src/scheme/user.rs)
│ forwards scheme calls to the registered daemon via SQE/CQE IPC
netstack / smolnetd (local/sources/base/netstack/src/main.rs)
│ registers: tcp, udp, icmp, ip, netcfg
│ owns: smoltcp::iface::Interface + SocketSet + Router + DeviceList
Router (netstack/src/router/mod.rs) — implements smoltcp::phy::Device
│ poll(): drains EthernetLink.recv() into rx_buffer
│ dispatch(): RouteTable longest-prefix lookup → EthernetLink.send()
EthernetLink (netstack/src/link/ethernet.rs)
│ ARP cache (60s TTL), frame encode/decode, neighbor discovery
│ synchronous read/write of raw Ethernet frames via the network scheme fd
Ethernet driver daemon (e1000d, rtl8168d, ixgbed, rtl8139d, virtio-netd)
│ implements driver-network::NetworkAdapter
│ exposes scheme:network.<driver>_<bus> with raw frame read/write + /mac
Hardware NIC (PCI MMIO + IRQ + DMA rings)
```
### Status Matrix
| Area | State | Detail |
|---|---|---|
| TCP/IP daemon (`netstack`/`smolnetd`) | **builds, qemu-proven** | Single-threaded event loop over `EventQueue`. Owns smoltcp 0.12.0 `Interface` + `SocketSet`. Registers `tcp`, `udp`, `icmp`, `ip`, `netcfg` schemes. Binary aliased as both `netstack` and `smolnetd` for compatibility. |
| smoltcp version | **builds** | 0.12.0 with features: `std`, `medium-ethernet`, `medium-ip`, `proto-ipv4`, `socket-raw`, `socket-icmp`, `socket-udp`, `socket-tcp`, `socket-tcp-cubic`, `iface-max-addr-count-8`. **IPv6 NOT enabled.** |
| TCP congestion control | **builds** | CUBIC only (smoltcp `socket-tcp-cubic` feature). No BBR, no DCTCP, no per-socket selection. |
| TCP feature surface | **partial** | Window scaling, MSS negotiation, RTT estimation, exponential backoff retransmission, keep-alive, Nagle, delayed ACKs (all from smoltcp). **Missing: SACK, TCP timestamps, ECN, TCP Fast Open, MPTCP, urgent pointer.** |
| UDP sockets | **builds, qemu-proven** | `scheme:udp`. Peek-and-filter model for connected UDP. Ephemeral port range 4915265535. |
| ICMP | **builds** | `scheme:icmp`, root-only. |
| Raw IP | **builds** | `scheme:ip`, root-only. Wraps smoltcp `RawSocket`. |
| ARP | **builds** | Hand-implemented in `link/ethernet.rs`. Request/reply, neighbor cache, 60s TTL, 3 retries with 1s silence. IPv4-only (no NDP for IPv6). |
| Routing | **builds, qemu-proven** | `router/route_table.rs` — longest-prefix-match over a `Vec<Rule>`. Static routes only, configured via `scheme:netcfg/route/{add,del,list}`. No FIB trie, no policy routing, no multipath, no route daemon. |
| Network configuration (`netcfg`) | **builds** | Filesystem-like config scheme: `/resolv/nameserver`, `/route/{list,add,del}`, per-interface `ip`, `mac`, `gateway`. Backed by `BTreeMap` nodes with read/write callbacks. |
| DHCP client (`dhcpd`) | **builds, qemu-proven** | Standalone daemon. Reads/writes `scheme:netcfg` to apply leases. Init service `10_dhcpd.service` runs `dhcpd -f`. |
| Profile orchestration (`redbear-netctl`) | **builds, host-tested** | Arch-netctl-style profiles in `/etc/netctl/`. Wired/wireless, DHCP/static/bounded IP modes. Init service `12_netctl.service` applies the active profile after `smolnetd`+`dhcpd`. |
| DNS resolver | **builds** | In-process inside `netstack` via `dns-parser` crate. `scheme:netcfg/resolv/nameserver` for the recursive resolver address. Quad9 (9.9.9.9) fallback. |
| Ethernet drivers | **builds, qemu-proven** | 5 drivers: `e1000d` (Intel 8254x), `ixgbed` (Intel 10GbE 82599), `rtl8139d`, `rtl8168d` (Realtek), `virtio-netd`. All implement `driver-network::NetworkAdapter`. |
| Driver packet I/O model | **builds** | Synchronous file read/write of raw Ethernet frames. `NetworkAdapter::{read_packet, write_packet}`. IRQ-driven via `scheme:irq/{}` + `EventQueue`. No NAPI, no batched polling, no interrupt mitigation. |
| Wi-Fi IP datapath | **missing** | `redbear-iwlwifi` is control-plane/transport only (see `WIFI-IMPLEMENTATION-PLAN.md`). No 802.11 frame-to-EthernetLink bridge into `netstack`. |
| POSIX socket API (relibc) | **builds, qemu-proven** | `socket()`, `bind()`, `connect()`, `listen()`, `accept()`, `send()`, `recv()`, `sendto()`, `recvfrom()`, `sendmsg()`, `recvmsg()`, `getsockopt()`, `setsockopt()`, `shutdown()`, `socketpair()`, `getsockname()`, `getpeername()`. `AF_INET` and `AF_UNIX` only. **No `AF_INET6`.** |
| Socket multiplexing | **builds, qemu-proven** | Kernel `scheme:event` (analogous to epoll). `EventQueue` in daemons. `O_NONBLOCK` + `EAGAIN`/`EWOULDBLOCK` semantics on sockets. No `epoll_pwait2`, no io_uring equivalent. |
| Bulk FD passing | **partial** | Upstream relibc gained bulk FD passing for `recvmsg`/`sendmsg` in early 2026 (commits `94b0cfc68`, `5e61f17a3`, `1978c1aa4`). **Sync status with our local relibc fork needs verification** — see Upstream Sync. |
| Firewall / packet filtering | **missing** | No netfilter, iptables, nftables, or BPF equivalent. No hooks in the IP path. |
| Connection tracking | **missing** | No conntrack. No NAT. |
| IPv6 | **missing** | smoltcp `proto-ipv6` feature not enabled. `link/ethernet.rs` is IPv4-only (no NDP). relibc socket backend has no `AF_INET6` branch. `route_table.rs` is `IpAddress`-generic but no IPv6 routes are configured. |
| Performance offloads | **missing** | No GRO, no GSO, no TSO, no checksum offload, no zero-copy send/recv, no `MSG_ZEROCOPY`, no `sendfile`, no batched packet submission. |
| Multi-NIC | **partial** | `Router` supports a `DeviceList` and dispatches per-route, but `main.rs:get_network_adapter()` picks the **first** adapter and warns on multiple (FIXME comment). True multi-homing not wired. |
| Virtual network devices | **missing** | No bridge, VLAN, bond, tunnel, VXLAN, tun/tap scheme daemons. |
| Traffic control (qdisc) | **missing** | No shaping, policing, QoS, or scheduling discipline. |
| Network namespaces | **partial** | Scheme namespaces exist (via `setrens`), used by `smolnetd` to enter the null namespace. Not exposed as a general network isolation primitive. |
| Hardware validation (bare metal) | **qemu-proven only** | NIC drivers build and pass QEMU smoke tests. No bare-metal NIC throughput, packet loss, or long-connection validation evidence. |
### Packet Flow Detail (Receive Path)
```
1. NIC raises IRQ → driver's EventQueue wakes
2. Driver::handle_irq() → drains NIC RX ring → packet into driver-side buffer
3. NetworkScheme<T> posts fevent(EVENT_READ) on the network scheme fd
4. smolnetd's EventQueue wakes on EventSource::Network
5. smolnetd::on_network_scheme_event() → EthernetLink.recv(now)
6. EthernetLink reads raw frame via network_file.read(), parses EthernetRepr
7. If IPv4: returns payload to Router.poll() which enqueues into rx_buffer
8. If ARP: processes locally (neighbor cache update, may send reply)
9. smolnetd calls smoltcp Interface.poll(now, &mut Router, &mut SocketSet)
10. smoltcp processes IP/TCP/UDP state machines, fills socket buffers
11. Socket handlers post fevent on tcp/udp scheme fds → applications wake
12. Application calls read(fd) → kernel forwards to TcpScheme::read_buf()
13. TcpScheme::read_buf() → smoltcp TcpSocket::recv_slice()
```
### Packet Flow Detail (Transmit Path)
```
1. Application calls write(fd, data) → kernel forwards to TcpScheme::write_buf()
2. TcpScheme::write_buf() → smoltcp TcpSocket::send_slice()
3. smolnetd event loop calls smoltcp Interface.poll(now, &mut Router, &mut SocketSet)
4. smoltcp decides to transmit → Router.transmit(now) returns TxToken
5. smoltcp builds IP packet into TxToken buffer → Router.tx_buffer.enqueue()
6. smolnetd calls Router.dispatch(now)
7. Router.dispatch() parses Ipv4Packet, looks up RouteTable rule
8. If source address mismatch: rewrites src + refills checksum
9. Calls EthernetLink.send(next_hop, packet, now)
10. EthernetLink.send(): ARP neighbor cache lookup
- Hit: builds Ethernet frame, writes to network_file
- Miss: enqueues into waiting_packets, sends ARP request
11. Driver receives write() on network scheme → pushes to NIC TX ring → DMA → wire
```
## Gap Analysis vs Linux 7.1
Linux 7.1 (`local/reference/linux-7.1/`) is the reference for production-grade networking.
The table below maps every major Linux networking subsystem to its Red Bear equivalent and
identifies the gap.
| Linux 7.1 Subsystem | Linux Location | Red Bear Equivalent | Gap Severity |
|---|---|---|---|
| **In-kernel TCP/IP** | `net/ipv4/`, `net/ipv6/` (~80k LoC) | Userspace `netstack` daemon (smoltcp) | Architectural difference, not a gap per se. Microkernel design. |
| **`sk_buff` packet metadata** | `include/linux/skbuff.h` (5,462 LoC) | Raw `[u8]` buffers, no metadata struct | **High** — no zero-copy across layers, no per-packet metadata propagation. |
| **NAPI (interrupt→poll)** | `include/linux/netdevice.h:381`, `napi_schedule()` | IRQ-per-packet + `EventQueue` wake | **High** — interrupt storm risk under load. No batched polling. |
| **GRO / GSO / TSO** | `net/core/gro.c`, `net/core/gso.c`, NIC driver offload | None | **High** — every packet traverses the full stack individually. |
| **BIG TCP (512KB GSO/GRO)** | `net/ipv6/ip6_output.c`, `net/ipv4/ip_output.c` | None | Medium — relevant only at 100GbE+, not near-term. |
| **Pluggable congestion control** | `include/net/tcp.h:1325` `struct tcp_congestion_ops`, `net/ipv4/tcp_cubic.c`, `tcp_bbr.c`, `tcp_dctcp.c` | smoltcp `socket-tcp-cubic` feature (CUBIC only) | **High** — no BBR for WAN throughput, no DCTCP for datacenter, no per-socket selection. |
| **SACK (Selective ACK)** | `net/ipv4/tcp_input.c` | Not in smoltcp | Medium — hurts lossy links. |
| **TCP timestamps** | `net/ipv4/tcp_output.c` | Not in smoltcp | Low — RTT estimation works without them but less precisely. |
| **ECN (Explicit Congestion Notification)** | `net/ipv4/tcp_input.c` | Not in smoltcp | Medium — datacenter relevance. |
| **MSG_ZEROCOPY send** | `net/ipv4/tcp.c:1140-1326`, `net/core/skbuff.c:1719-1806` | None | **High** — every send copies user→kernel→daemon. |
| **sendfile / splice** | `net/core/skbuff.c:3257` `skb_splice_bits()` | None | Medium — relevant for static file servers. |
| **io_uring zero-copy receive** | `io_uring/zcrx.c`, `IORING_OP_RECV_ZC` (`include/uapi/linux/io_uring.h:314`) | None | Low (long-term) — Linux 7.x reaches 187 Gbps with this; not near-term for Red Bear. |
| **epoll** | `fs/eventpoll.c` (~3,000 LoC), `ep_poll_callback()` | Kernel `scheme:event` + `EventQueue` | Medium — functionally equivalent but less optimized; no ready-list integration with socket wakeup path. |
| **XDP / AF_XDP** | `net/xdp/xsk.c`, BPF programs in driver context | None | Low (long-term) — line-rate BPF filtering. |
| **eBPF everywhere** | `net/core/filter.c`, `bpf_tcp_ca.c`, XDP, TC, socket filter | None | Medium — no programmable packet processing. |
| **Netfilter (5 hooks)** | `include/uapi/linux/netfilter.h:43`, `struct nf_hook_ops` | None | **High** — no firewall, no NAT, no packet interception layer. |
| **Conntrack** | `net/netfilter/nf_conntrack_core.c` (2,790 LoC), `struct nf_conn` | None | **High** — no stateful firewall, no NAT. |
| **nftables / iptables** | `net/netfilter/nf_tables_api.c`, `xt_*.c` | None | High — no user-facing firewall CLI. |
| **FIB (LC-trie routing)** | `include/net/ip_fib.h`, `net/ipv4/fib_trie.c`, `fib_semantics.c` | `router/route_table.rs``Vec<Rule>` linear scan | Medium — fine for tens of routes; inadequate for full BGP table. |
| **Policy routing / multipath** | `net/ipv4/fib_rules.c`, `fib_select_multipath()` | None | Low — niche outside routers. |
| **IPv6 (full stack)** | `net/ipv6/` (40+ .c files), built-in only in 7.1 | Not enabled (smoltcp `proto-ipv6` off, no NDP, no `AF_INET6`) | **Critical** — IPv6 is mandatory for modern networking. |
| **cfg80211 / mac80211 / nl80211** | `include/net/cfg80211.h` (10,995 LoC), `net/mac80211/` (60+ files), `include/uapi/linux/nl80211.h` | `linux-kpi` wireless shim (Rust), `redbear-iwlwifi` transport (C), `redbear-wifictl` scheme | See `WIFI-IMPLEMENTATION-PLAN.md`. **Control-plane only; no IP datapath bridge yet.** |
| **`net_device` + `net_device_ops`** | `include/linux/netdevice.h:2139` (5,737 LoC), ~50 ops | `driver-network::NetworkAdapter` trait (4 methods) | **High** — no features negotiation, no offload reporting, no statistics, no queue selection. |
| **Multi-queue / RSS / RPS / RFS / XPS** | `net/core/dev.c`, per-CPU queues | None | Medium — relevant for >1Gbps. |
| **`page_pool` memory recycling** | `net/core/page_pool.c` | None | Medium — allocator overhead on RX. |
| **Cacheline-aware structs** | `include/linux/netdevice.h` `_cacheline_group` annotations | None | Low — micro-optimization. |
| **Network namespaces** | 67 namespace-aware subdirs under `net/` | Scheme namespaces (partial) | Medium — relevant for container story. |
| **Bridge / VLAN / bond / tunnel** | `net/bridge/`, `net/8021q/`, `net/bonding/`, `net/ipv4/ip_tunnel.c` | None | Medium — virtualization/networking appliance relevance. |
| **Traffic control (qdisc)** | `net/sched/` (100+ files), `include/net/sch_generic.h` | None | Low — QoS is niche for a desktop OS. |
| **MPTCP** | `net/mptcp/` | Not in smoltcp | Low — niche. |
| **Hardware offloads (checksum, TSO, LRO, TLS, IPsec)** | NIC driver `features`, `include/linux/netdevice.h` NETIF_F_* | None | Medium — drivers don't even advertise capabilities. |
| **Routing daemons (BGP/OSPF/RIP)** | Userspace (FRRouting, bird) | None | Low — not a kernel/daemon gap. |
| **Drop reasons / observability** | `include/net/dropreason.h`, `skb->drop_reason` | Logging only | Low — debugging aid. |
### Severity Summary
- **Critical (blocks modern use):** IPv6, firewall/NAT (netfilter equivalent).
- **High (major performance or functionality gap):** `sk_buff`-equivalent metadata, NAPI-style polling, GRO/GSO/TSO, MSG_ZEROCOPY, pluggable congestion control, multi-NIC.
- **Medium (matters for specific workloads):** SACK, ECN, FIB scaling, multi-queue, virtual devices, observability.
- **Low (long-tail polish):** TCP timestamps, policy routing, MPTCP, io_uring ZC, qdisc.
## Upstream Redox Sync Opportunities (20242026)
Red Bear OS is forked from a frozen Redox snapshot. Upstream Redox has been actively evolving
its networking stack. The following upstream commits should be evaluated for import into the
Red Bear local forks. **Per the Golden Rule (Red Bear adapts to upstream, never the reverse),
these are imports to track and apply, not workarounds.**
### Already in Red Bear (verified)
- ✅ smoltcp 0.12.0 with `socket-tcp-cubic` (upstream `b92be2e7d`, `d7c128684`, 2025-03-09)
- ✅ Named network adapters via `scheme:network.<driver>_<bus>` (upstream `f9b3170f0`, 2024-02-28)
-`netstack` merged into `base` recipe (upstream `c06e5b14e`, 2025-03-10)
- ✅ MAC address fetched from network scheme (upstream `674f5b6d7`, 2024-02-28)
-`dhcpd` moved into `base` (upstream `b68e5a685`, 2026-04-11)
- ✅ New scheme format, no legacy paths (upstream `bafdb3b66`, 2024-07-11)
### To Evaluate for Import
| Upstream Commit | Date | Description | Red Bear Action |
|---|---|---|---|
| `redox-os/drivers@ad9305bf9` | 2024-02-28 | Unified network drivers under `net/` with shared scheme | Verify our driver layout matches. |
| `redox-os/drivers@921c6b07f` | 2024-12-26 | `driver-network` migrated to `redox-scheme` crate | **Import** — modernizes the scheme protocol. |
| `redox-os/drivers@0f24975ff` | 2025-11-29 | PCI interrupts rework: dedup vector handling for RTL drivers | **Import** — IRQ quality (see IRQ plan). |
| `redox-os/drivers@dd41c4f13` | 2025-11-23 | Net: scheme created and daemon ready before device init | **Import** — fixes race on early opens. |
| `redox-os/drivers@407533201` | 2025-09-24 | Add ThinkPad T60 ethernet to `e1000d` PCI IDs | **Import** — broader hardware. |
| `redox-os/relibc@94b0cfc68` | 2026-02-08 | Reimplement `recvmsg`/`sendmsg` using bulk FD passing | **Verify + import** — check our relibc fork has this; critical for `SCM_RIGHTS`. |
| `redox-os/relibc@5e61f17a3` | 2026-02-08 | `MSG_CMSG_CLOEXEC` handling in `recvmsg` | **Import** alongside the above. |
| `redox-os/relibc@1978c1aa4` | 2026-03-25 | Full `recvmsg` implementation | **Import**. |
| `redox-os/relibc@cab002146` | 2026-02-28 | Move protocols into `libredox` | **Major refactor — evaluate carefully.** Affects SocketCall enum location. |
| `redox-os/relibc@443145fde` | 2025-11-07 | Use `recvmsg`/`sendmsg` when `recvfrom`/`sendto` has flags | **Import**. |
| `redox-os/relibc@9eaa9e82b` | 2025-11-08 | Pass through all `SOL_SOCKET` options, fix `getsockopt` option_len | **Import** — fixes option handling bugs. |
| `redox-os/relibc@6a455159a` | 2026-01-22 | Fix `getaddrinfo` infinite loop hang | **Critical import** — known crash. |
| `redox-os/relibc@726a0fb1a` | 2025-09-15 | `getaddrinfo` NULL nodename, `AI_PASSIVE`, `AI_NUMERICHOST` | **Import**. |
| `redox-os/relibc@5334455a2` | 2025-08-20 | `getnameinfo` + `getaddrinfo` loopback | **Import**. |
| `redox-os/relibc@d44010170` | 2025-07-18 | UDS `bind`/`connect` with RedoxFS integration | **Import** — fixes Unix domain sockets. |
| `redox-os/relibc@6dd10a1f1` | 2025-08-01 | Fix `connect` on Redox | **Import**. |
| `redox-os/relibc@9c6701802` | 2026-01-07 | Add `sys_socket` tests | **Import** — regression coverage. |
| `redox-os/syscall@7a1409a91` | 2026-05-27 | Multiple FDs variant for `call` and `std_fs_call` | **Import** — enables bulk FD passing. |
| `redox-os/syscall@178461f6f` | 2025-12-27 | Remove remnants of old packet format | **Import** — cleanup. |
| `redox-os/kernel@c089667ad` | 2025-12-13 | Remove legacy packet user schemes | **Import** — kernel side of the above. |
| `redox-os/kernel@6c3d5d28c` | 2026-07-01 | Move FD allocation logic into userspace | **Major refactor — evaluate carefully.** Most recent kernel change. |
| `redox-os/kernel@08ea1da2f` | 2025-07-05 | Trigger read event for user schemes on fd close | **Import** — fixes socket cleanup races. |
| `redox-os/kernel@4ff82ad8b` | 2025-11-27 | Fix user scheme deadlocks in `call_extended_inner` | **Critical import** — fixes hangs under load. |
| `redox-os/kernel@99ff55ee1` | 2026-02-08 | Demux results as soon as received from user scheme | **Import** — latency improvement. |
| `redox-os/redox@85b62fd85` | 2026-03-01 | Support networking in all configs | **Reference** — verify our configs cover networking in all targets. |
| `redox-os/redox@193e81897` | 2026-02-17 | ~115 WIP networking recipes (HTTP, FTP, SSH, VPN, P2P) | **Cherry-pick** — many of these are now relevant for `cub`. |
| `redox-os/netutils@6df5af955` | 2024-11-27 | Basic `ifconfig` command | **Reference**`redbear-netstat` covers this, but compare APIs. |
| `redox-os/netutils@62f96a4b5` | 2026-05-22 | Fix `nc` listener exiting on stdin EOF | **Import if we ship nc.** |
### Upstream Gaps Red Bear Fills Independently
These exist in Red Bear but **not** in upstream Redox:
- Wi-Fi driver support (`redbear-iwlwifi`, `linux-kpi` wireless layer, `redbear-wifictl`)
- `redbear-netctl` profile orchestration (Arch netctl-style)
- `redbear-netstat`, `redbear-mtr`, `redbear-nmap`, `redbear-traceroute`
- AMD GPU networking offload path (via `redox-drm`)
## Improvement Plan
The plan is organized into six phases. Each phase has a clear success criterion, dependencies,
and validation target. Phases are ordered by **dependency** (early phases unblock later ones)
and by **impact** (critical gaps first).
### Phase 0: Upstream Sync and Foundation Hardening
**Goal:** Bring the Red Bear local forks to current upstream networking state before adding
new features. This avoids building on stale foundations.
**Duration:** 23 weeks.
**Workstreams:**
0.1 **relibc socket fixes.** Apply the upstream commits listed in "To Evaluate for Import"
that touch relibc's `sys_socket`, `getaddrinfo`, `getnameinfo`, `recvmsg`/`sendmsg`,
`connect`, `bind`, and `SOL_SOCKET` option pass-through. **Priority: the `getaddrinfo`
infinite loop fix (`6a455159a`).** Verify against the `sys_socket` test suite.
0.2 **Kernel scheme dispatch fixes.** Apply `4ff82ad8b` (deadlock fix), `08ea1da2f`
(close event), `99ff55ee1` (latency). These are direct kernel-fork patches.
0.3 **Driver-side modernization.** Apply `921c6b07f` (`driver-network``redox-scheme`),
`dd41c4f13` (scheme-before-device-init race fix), `0f24975ff` (PCI interrupts rework).
Test each driver in QEMU after applying.
0.4 **Config audit.** Verify all `redbear-*.toml` targets that should have networking
actually pull in `redbear-netctl`, `smolnetd`, `dhcpd`, and a NIC driver. Reference
upstream `85b62fd85`.
0.5 **Bulk FD passing verification.** Check whether the local relibc fork already has
commits `94b0cfc68` and `1978c1aa4`. If not, import them. Validate `SCM_RIGHTS`
works end-to-end with a test daemon.
**Success Criteria:**
- All listed upstream commits either applied or documented as "intentionally skipped" with
a reason.
- `redbear-mini` boots in QEMU, acquires DHCP, and `curl http://example.com/` succeeds.
- `redbear-netstat` shows the TCP connection in `ESTABLISHED` state.
- `sys_socket` tests pass on the relibc fork.
**Validation target:** qemu-proven.
---
### Phase 1: Multi-NIC and Driver Feature Surface
**Goal:** Remove the single-NIC FIXME and give the stack visibility into driver capabilities.
**Duration:** 34 weeks.
**Dependencies:** Phase 0 complete.
**Workstreams:**
1.1 **Multi-NIC in `netstack`.** Replace `get_network_adapter()` (which picks the first NIC
and warns) with proper multi-interface support. The `Router` already has a `DeviceList`;
the gap is in `main.rs` initialization and in `EthernetLink` instantiation per adapter.
Reference Linux's `net_device` list semantics. Each NIC gets its own `EthernetLink` in
the `DeviceList`, with its own ARP cache and MAC.
1.2 **`NetworkAdapter` trait expansion.** The current trait is 4 methods:
```rust
pub trait NetworkAdapter {
fn mac_address(&mut self) -> [u8; 6];
fn available_for_read(&mut self) -> usize;
fn read_packet(&mut self, buf: &mut [u8]) -> Result<Option<usize>>;
fn write_packet(&mut self, buf: &[u8]) -> Result<usize>;
}
```
Expand to expose capabilities and statistics, modeled on Linux's
`struct net_device_ops` and `ethtool_ops`:
```rust
pub trait NetworkAdapter {
fn mac_address(&mut self) -> [u8; 6];
fn available_for_read(&mut self) -> usize;
fn read_packet(&mut self, buf: &mut [u8]) -> Result<Option<usize>>;
fn write_packet(&mut self, buf: &[u8]) -> Result<usize>;
// New: capability negotiation
fn capabilities(&self) -> NicCapabilities; // MTU, offloads, max queues
fn mtu(&self) -> usize { 1500 }
fn link_state(&self) -> LinkState; // Up / Down / Unknown
fn statistics(&self) -> NicStatistics; // rx/tx bytes, packets, errors, drops
}
```
Wire `NicCapabilities` into `netstack` so `Router` knows the real MTU (currently
hardcoded to 1486 in `router/mod.rs:42`).
1.3 **`scheme:netcfg` interface enumeration.** Expose `netcfg/ifaces` listing all
`DeviceList` entries with their MAC, IP, link state, statistics, and MTU. This gives
`redbear-netstat` and `redbear-netctl` a proper interface table.
1.4 **Per-driver statistics.** Each driver (`e1000d`, `rtl8168d`, etc.) should expose
RX/TX byte/packet/error counters through the new `statistics()` method. Reference
Linux's `ndo_get_stats64`.
**Success Criteria:**
- `redbear-netstat -i` lists all NICs present (in QEMU: just `virtio-net` or `e1000`; on
bare metal: all detected NICs).
- A second NIC can be brought up with a static IP via `redbear-netctl` and routes traffic.
- `Router::MTU` is no longer a hardcoded constant — it derives from the active NIC's
reported MTU.
- Driver statistics are non-zero after a ping flood.
**Validation target:** qemu-proven for multi-NIC (two virtio-net interfaces); host-tested
for the trait expansion (unit tests on the new trait).
---
### Phase 2: IPv6
**Goal:** Bring the stack to modern IP standards. IPv6 is no longer optional for a
general-purpose OS.
**Duration:** 46 weeks.
**Dependencies:** Phase 1.1 (multi-NIC) helpful but not strictly required. Phase 0
(relibc freshness) required.
**Workstreams:**
2.1 **Enable smoltcp IPv6.** In `netstack/Cargo.toml`, add `"proto-ipv6"` to the smoltcp
features list. Audit the build for new warnings/errors — smoltcp's IPv6 surface is
large. Reference: smoltcp `wire` module already has full IPv6 parsing.
2.2 **`link/ethernet.rs` IPv6 support.** Currently the link layer is IPv4-only: the ARP
cache uses `Ipv4Address`, `process_arp` only handles `ArpRepr::EthernetIpv4`, and
`send_to` only handles `IpAddress::Ipv4`. Add NDP (Neighbor Discovery Protocol,
RFC 4861) for IPv6 neighbor discovery, mirroring the ARP logic. Use smoltcp's
`wire::{Icmpv6Repr, Icmpv6Packet, NdiscRepr}` types.
2.3 **`router/route_table.rs` IPv6.** The `Rule` struct already uses `IpCidr` and
`IpAddress`, which are version-generic. The gap is in `dispatch()` (which only does
`Ipv4Packet::new_checked`) and in `set_src_addr` checksum refill. Add an IPv6 branch.
IPv6 has no header checksum (unlike IPv4), so the checksum logic is simpler — only
transport-layer pseudo-header checksums need attention.
2.4 **relibc `AF_INET6`.** In `local/sources/relibc/src/platform/redox/socket.rs`, add a
branch for `AF_INET6` that opens `/scheme/tcp` and `/scheme/udp` with IPv6-encoded
addresses. The scheme path encoding `dup(fd, "[2001:db8::1]:443")` should work once
`netstack` parses IPv6 endpoints (smoltcp's `parse_endpoint` already does).
2.5 **`netcfg` IPv6 surfaces.** Add `netcfg/resolv/nameserver6`, per-interface IPv6
address configuration, and IPv6 route add/del. Reference: Linux `addrconf.c`
(SLAAC) and `ndisc.c` (router advertisements).
2.6 **DHCPv6.** Either extend `dhcpd` or add a sibling `dhcpv6d` daemon. DHCPv6 (RFC 8415)
is a separate protocol from DHCPv4 — UDP port 546 (client) / 547 (server), different
message format. Alternatively, implement SLAAC (RFC 4862) as the default IPv6
autoconfiguration method.
2.7 **`redbear-netctl` IPv6 profiles.** Extend the profile format to support `IP6=dhcp`,
`IP6=slaac`, `IP6=static`, `Address6=`, `Gateway6=`, `DNS6=`.
2.8 **Validation.** Boot `redbear-mini` in QEMU with `qemu-system-x86_64 ... -netdev
user,id=n0,ipv6=on -device virtio-net,netdev=n0`. Verify `ping6 ::1`, `curl -6
https://ipv6.google.com`, and a dual-stack SSH connection.
**Success Criteria:**
- `socket(AF_INET6, SOCK_STREAM, 0)` returns a valid fd.
- `ping6 <router-advertised-address>` succeeds.
- A dual-stack TCP server accepts both IPv4 and IPv6 connections.
- `redbear-netstat -r` shows both IPv4 and IPv6 routes.
**Validation target:** qemu-proven for basic IPv6; host-tested for NDP and DHCPv6
protocol correctness.
**Risk:** This is the largest single workstream. The `link/ethernet.rs` IPv6 work is
non-trivial because NDP is more complex than ARP (solicitation-node multicast, router
discovery, prefix information options). Consider implementing NDP as a separate
`link/ndp.rs` module rather than complicating `ethernet.rs`.
---
### Phase 3: TCP Performance and Congestion Control
**Goal:** Bring TCP throughput and latency into the same order of magnitude as Linux for
common workloads.
**Duration:** 68 weeks.
**Dependencies:** Phase 1 (capabilities surface) for any hardware offload wiring.
**Workstreams:**
3.1 **Buffer pool audit.** `netstack/src/buffer_pool.rs` exists but its usage should be
audited. TCP socket buffers are currently `vec![0; 0xffff]` (64KB) per socket in
`scheme/tcp.rs:80-83`. For high-bandwidth connections, this is a throughput cap.
Make the buffer size configurable via `setsockopt(SO_SNDBUF/SO_RCVBUF)` (the scheme
already has stubs at `tcp.rs:14-15` but `set_setting` returns `Ok(0)`). Reference
Linux's `tcp_rmem`/`tcp_wmem` sysctl defaults.
3.2 **Pluggable congestion control interface.** smoltcp 0.12 has `socket-tcp-cubic` as a
feature flag but no runtime selection. Two options:
**Option A (recommended):** Add `setsockopt(TCP_CONGESTION, "cubic")` support to the
TCP scheme, backed by a `congestion_control` field on `SocketFile`. Since smoltcp
compiles CC at build time via features, this requires either (a) compiling both
Reno and CUBIC and switching at runtime (if smoltcp supports it), or (b) forking
smoltcp to expose a `CongestionController` trait object. Check whether smoltcp 0.12's
`socket-tcp-cubic` is additive to the default Reno controller.
**Option B:** Contribute a BBR-style controller upstream to smoltcp and enable it
via a Red Bear patch. This is higher-effort but higher-impact.
3.3 **SACK (Selective Acknowledgment).** smoltcp does not implement SACK (RFC 2018).
On lossy links (wireless, WAN), SACK dramatically improves throughput by allowing the
receiver to acknowledge non-contiguous blocks. This requires either (a) upstreaming
SACK to smoltcp, or (b) carrying a Red Bear patch against smoltcp. Given the Golden
Rule (prefer upstream), the path is: contribute to smoltcp, carry a local patch
until merged. Reference: Linux `net/ipv4/tcp_sack.c`.
3.4 **Receive batching (NAPI-equivalent).** The current receive path processes one packet
per `EventSource::Network` event. Under load, this means one scheme-IPC round-trip
per packet. Add a drain loop in `on_network_scheme_event()` that reads packets until
`available_for_read()` returns 0 (or a budget cap). This mirrors Linux's NAPI
`budget` parameter. Reference: Linux `napi_complete_done()` and the `budget` pattern
in `include/linux/netdevice.h`.
3.5 **Transmit batching.** Similarly, `Router::dispatch()` already drains `tx_buffer`
in a loop, but each `dev.send()` is a synchronous write to the network scheme fd.
Batch multiple frames into a single write if the driver supports it (requires the
`capabilities()` expansion from Phase 1.2).
3.6 **`MSG_ZEROCOPY` investigation.** True zero-copy requires shared memory between the
application and `netstack`. This is architecturally significant in a microkernel —
it means the scheme IPC must support passing buffer references, not just data copies.
This is a research workstream: evaluate whether the kernel's `scheme:memory` can be
used to establish a shared ring buffer between an application and `netstack`.
Reference: Linux `msg_zerocopy_alloc()` and the `ubuf_info` mechanism, and the
Redox "ring buffers" NLnet-funded project mentioned in the Development Priorities
2025/26 document. **Do not implement in this phase — produce a design doc.**
**Success Criteria:**
- `iperf3` (port the recipe) between two QEMU VMs shows ≥100 Mbps on virtio-net
(current baseline unknown — establish it first).
- `setsockopt(SO_RCVBUF, ...)` affects the actual receive buffer size (verify with
`getsockopt`).
- SACK enabled connections recover from induced packet loss (use `tc netem` in a
Linux-to-RedBear bridge, or a QEMU packet loss model).
- A design doc for zero-copy exists at `local/docs/NETWORK-ZEROCOPY-DESIGN.md`.
**Validation target:** qemu-proven for throughput and buffer sizing; host-tested for
SACK protocol correctness.
---
### Phase 4: Firewall and Packet Filtering
**Goal:** Provide a firewall and NAT capability, respecting the microkernel
everything-is-a-scheme design.
**Duration:** 610 weeks.
**Dependencies:** Phase 1 (interface awareness). Phase 2 (IPv6) for dual-stack filtering.
**Workstreams:**
4.1 **Architecture decision.** In Linux, netfilter hooks live inside the kernel IP path.
In Red Bear, the IP path is in `netstack`'s `Router` and `EthernetLink`. The
microkernel-aligned approach is to add **filter hooks in `netstack`'s `Router`**
that consult a `scheme:firewall` (or `scheme:nf`) daemon. This keeps policy in
userspace and matches Redox's design philosophy. Two designs to evaluate:
**Design A (in-process):** Add a `FirewallEngine` inside `netstack` that reads rules
from `scheme:netcfg/firewall/{rules,chains}`. Simpler, faster (no IPC per packet),
but couples policy to the stack daemon.
**Design B (out-of-process):** `netstack` calls out to a `firewalld` daemon via
scheme IPC for each packet. Cleaner separation but adds latency per packet.
Mitigated by a rule cache in `netstack` with invalidation via fevent.
**Recommendation:** Design A for the hot path (rule lookup), with rules managed by a
separate `scheme:firewall` daemon that writes to `netcfg/firewall/*`. This matches
how `route` and `resolv` already work (config via `netcfg`, enforced in `netstack`).
4.2 **Hook points.** Define 5 hook points mirroring Linux netfilter, adapted to the
`Router`/`EthernetLink` boundary:
- `PRE_ROUTING` — after `EthernetLink::recv()`, before `Router` enqueue.
- `LOCAL_IN` — after `Router` decides packet is for this host.
- `FORWARD` — after `Router` decides packet is to be forwarded (requires IP forwarding
support, currently missing — see Phase 6).
- `LOCAL_OUT` — after `Router::dispatch()` dequeues, before `EthernetLink::send()`.
- `POST_ROUTING` — just before `EthernetLink::send()`.
4.3 **Rule format.** Define a TOML or filesystem-based rule format under
`netcfg/firewall/`. Example:
```toml
# /etc/firewall/rules.toml (or via scheme:netcfg/firewall/rules)
[[chain]]
name = "input"
[[chain.rule]]
action = "accept"
proto = "tcp"
dest_port = 22
source = "192.168.0.0/16"
[[chain.rule]]
action = "drop"
proto = "tcp"
dest_port = 22
```
4.4 **Connection tracking (conntrack).** For stateful rules (`ACCEPT ESTABLISHED,
RELATED`), implement a connection tracker inside `netstack` (or a separate daemon).
Track `(src_ip, src_port, dst_ip, dst_port, proto, state)` tuples with timeouts.
Reference: Linux `struct nf_conn` (`include/net/netfilter/nf_conntrack.h:74`).
4.5 **NAT.** Build on conntrack. Source NAT (masquerade) rewrites `src_addr` in
`Router::dispatch()` and records the mapping in conntrack. Destination NAT (port
forwarding) rewrites `dst_addr` in `PRE_ROUTING`. This requires the checksum refill
logic that already exists for IPv4 src rewrites — extend it to handle dst rewrites
and (eventually) port rewrites in the transport pseudo-header.
4.6 **`redbear-firewall` CLI.** A `redbear-firewall` tool (or extend `redbear-netctl`)
that reads/writes `scheme:netcfg/firewall/*` and provides `iptables`-like or
`nft`-like ergonomics. Reference: Linux `iptables(8)` and `nft(8)`.
4.7 **Validation.** Tests: (a) drop all inbound TCP except 22, verify only SSH connects;
(b) masquerade a Red Bear VM behind another Red Bear VM's NAT, verify outbound
connections work and inbound connections to the inner VM fail without port forward;
(c) port-forward 8080 to an inner VM's 80, verify a curl to the gateway:8080 reaches
the inner VM.
**Success Criteria:**
- A basic ruleset (accept loopback, accept established, accept SSH, drop rest) can be
loaded via `redbear-firewall` and is enforced.
- SNAT masquerade works for outbound traffic from a NAT'd subnet.
- DNAT port-forwarding works for at least one TCP port.
- `redbear-netstat -t` shows conntrack entries for active connections.
**Validation target:** qemu-proven for all three test scenarios; host-tested for
conntrack state machine correctness.
---
### Phase 5: Driver Coverage and Hardware Validation
**Goal:** Expand the Ethernet driver family to cover common bare-metal hardware and
validate the stack on real NICs.
**Duration:** Ongoing (parallel with Phases 24).
**Dependencies:** Phase 0 (driver modernization). Phase 1 (capabilities surface).
**Workstreams:**
5.1 **Import upstream `alxd`.** Upstream Redox has an `alxd` driver (Atheros L2 Fast
Ethernet) that Red Bear does not. Import it. Reference: `redox-os/drivers/net/alxd/`.
5.2 **Common consumer NIC coverage.** The current family misses several common consumer
chipsets. Prioritize by deployment frequency:
- **`igb` / `igc`** (Intel I219/I225 on modern laptops — NUC, ThinkPad, Dell) —
**High priority.** The `e1000d` PCI ID list (`10ec:1004, 100e, 100f, 109a, 1503`)
does not cover the I219/I225 (PCI IDs `8086:15d8`, `8086:15f2`, etc.). Either
extend `e1000d` or add a new `igbd` driver.
- **`r8125`** (Realtek 2.5GbE on modern motherboards) — Medium priority.
`rtl8168d` covers 8168/8169 but not 8125 (`10ec:8125`).
- **`e1000e`** (Intel I219/I218 Gigabit) — the PCI IDs `8086:1502, 1503, 1559`
overlap with `e1000d` but the register interface differs. Verify coverage.
5.3 **Bare-metal validation harness.** Establish a bare-metal test bench with:
- An Intel NIC (e1000 or igb) and a Realtek NIC (rtl8168) on the same machine.
- A Linux host on the other end of the link for `iperf3`, `tcpdump`, and packet
injection.
- Test matrix: boot, DHCP acquire, ping, iperf3 TCP throughput, iperf3 UDP packet
loss, long-running TCP connection (1 hour), rapid connection churn (1000
connects/sec).
5.4 **Wi-Fi IP datapath bridge.** Once `redbear-iwlwifi` reaches runtime validation
(per `WIFI-IMPLEMENTATION-PLAN.md`), add the datapath bridge: a Wi-Fi link-layer
adapter that implements `driver-network::NetworkAdapter` and feeds 802.11 frames
(after 802.11-to-Ethernet header conversion) into `netstack`'s `DeviceList`. This
makes Wi-Fi a first-class `Router` device. Coordinate with the Wi-Fi plan.
**Success Criteria:**
- `alxd` builds and is packaged.
- At least one modern Intel NIC (I219 or I225) is supported and validated.
- Bare-metal iperf3 throughput is documented for at least one NIC.
- (Stretch) Wi-Fi TCP connection works end-to-end through `netstack`.
**Validation target:** validated for at least one bare-metal NIC.
---
### Phase 6: Forwarding, Virtual Devices, and Advanced Features
**Goal:** Bring the stack to feature parity with a general-purpose Linux networking
appliance for the features that matter.
**Duration:** 816 weeks (parallel, long-term).
**Dependencies:** Phases 2 (IPv6), 4 (firewall/conntrack for NAT foundation).
**Workstreams:**
6.1 **IP forwarding.** When `netstack` receives a packet whose destination is not a local
IP, it currently drops it (no `FORWARD` hook in the `Router`). Add a forwarding path:
look up the destination in the route table, and if the next-hop is via a different
interface than the ingress interface, re-queue the packet for that interface. Decrement
the IPv4 TTL (or IPv6 Hop Limit) and drop at 0. Reference: Linux `ip_forward()` in
`net/ipv4/ip_forward.c`. Gate behind a `netcfg/forwarding` enable flag.
6.2 **Bridge scheme daemon.** A `bridged` userspace daemon that implements an L2 learning
bridge (MAC learning table, STP optionally). Exposes `scheme:bridge/<name>` and
connects multiple `scheme:network/*` devices. The bridge itself registers as a
`scheme:network/bridge0` so `netstack` treats it like any other NIC. Reference:
Linux `net/bridge/br_device.c` and `struct net_bridge`.
6.3 **VLAN support.** Add 802.1Q VLAN tag parsing in `EthernetLink` (or a separate
`VlanLink` wrapper). Expose `scheme:network/<iface>.<vlan>` virtual devices. Reference:
Linux `net/8021q/`.
6.4 **Bonding / link aggregation.** A `bondd` daemon implementing LACP (802.3ad) or
active-backup. Higher-effort; defer until multi-NIC (Phase 1) is solid.
6.5 **TUN/TAP scheme daemon.** A `tund`/`tapd` that exposes a virtual interface to
userspace — read/write packets from an application. Useful for VPN daemons (WireGuard,
OpenVPN). Reference: Linux `drivers/net/tun.c`.
6.6 **Routing daemon support.** Once the FIB/route table supports sufficient scale, port
a routing daemon (bird or FRRouting) so Red Bear can participate in dynamic routing.
This is userspace-only work; no `netstack` changes needed beyond performance.
6.7 **`sk_buff`-equivalent metadata struct.** Introduce a `Packet` struct in `netstack`
that carries metadata (ingress interface, VLAN tag, receive timestamp, mark/label for
firewall) alongside the raw bytes. This replaces the current `[u8]`-only model and
enables future features (QoS marking, policy routing on metadata, observability).
Reference: Linux `struct sk_buff` (`include/linux/skbuff.h`). **This is a significant
internal refactor and should be staged carefully.**
**Success Criteria:**
- A Red Bear VM routes packets between two subnets at >100 Mbps.
- A bridge connects two VMs on the same L2 segment.
- VLAN-tagged traffic is correctly classified.
- (Stretch) WireGuard establishes a tunnel between two Red Bear VMs.
**Validation target:** qemu-proven for forwarding, bridge, VLAN; experimental for bonding
and TUN/TAP.
## Cross-Cutting Concerns
### Observability
Throughout all phases, maintain and extend the `redbear-netstat` tool to surface new
state:
- Phase 1: per-interface statistics table.
- Phase 2: IPv6 neighbor cache (`ndp -a` equivalent), IPv6 routes.
- Phase 3: TCP socket details (cwnd, rtt, retransmits — from smoltcp socket introspection).
- Phase 4: conntrack table (`conntrack -L` equivalent), firewall rule hit counters.
- Phase 6: bridge MAC table, VLAN mappings.
Add a `redbear-tcpdump` or `scheme:netcfg/pcap` capability for packet capture. A
userspace packet capture daemon that taps the `PRE_ROUTING` hook (Phase 4) and writes
pcap files would be invaluable for debugging.
### Testing
- **Host-tested:** smoltcp already has extensive upstream tests. Add Red Bear-specific
tests for: NDP state machine, firewall rule evaluation, conntrack state transitions,
NAT mapping correctness.
- **QEMU-tested:** Establish a standard QEMU networking test image with two NICs,
configurable network topology, and a test runner that boots Red Bear, runs a series
of network commands, and checks output. Reference: Linux's `tools/testing/selftests/net/`.
- **Bare-metal validated:** The Phase 5 harness. Document results in
`local/docs/networking-validation-log.md`.
### Documentation
This plan should be updated when:
- A phase is started or completed (update the status matrix).
- An upstream commit is imported or intentionally skipped (update the sync table).
- A design decision is made (e.g., the zero-copy design doc from Phase 3.6).
- A validation milestone is reached.
## Validation Evidence Requirements
Per the project's evidence policy, claims in this plan are not complete without:
| Claim Type | Required Evidence |
|---|---|
| "builds" | `repo cook recipes/core/base` succeeds; `netstack` binary present in stage. |
| "qemu-proven" | QEMU boot log showing the daemon start, the scheme registration, and the test command succeeding (e.g., `curl -v` output). |
| "host-tested" | `cargo test` output from the relevant crate (relibc, netstack, redbear-netctl). |
| "validated" | Bare-metal test report: hardware model, NIC model, kernel/driver versions, test commands, and results. Stored in `local/docs/networking-validation-log.md`. |
## Risk Register
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| smoltcp 0.12 IPv6 surface incomplete | Medium | High (blocks Phase 2) | Spike first: enable `proto-ipv6`, audit compilation errors and missing APIs before committing to Phase 2. |
| SACK requires smoltcp fork | High | Medium (Phase 3.3) | Engage upstream smoltcp maintainers early. Carry a local patch as fallback. |
| Zero-copy incompatible with scheme IPC | Medium | High (Phase 3.6) | Treat as research only in Phase 3. Do not block other Phase 3 work on it. |
| Firewall in-process vs out-of-process debate stalls Phase 4 | Medium | High | Decision criterion: rule-lookup latency. Benchmark Design A vs Design B with a 1000-rule ruleset before committing. |
| Bare-metal NIC unavailable for Phase 5 | Medium | Medium | Prioritize QEMU validation. Document bare-metal as stretch. |
| Upstream relibc refactor (`cab002146` "move protocols into libredox") conflicts with local patches | Medium | High | Evaluate early in Phase 0. If the refactor is large, defer to a dedicated sync sprint rather than mixing with feature work. |
## Execution Order Summary
```
Phase 0 (upstream sync, 2-3w) ──┬── Phase 1 (multi-NIC, capabilities, 3-4w) ──┬── Phase 3 (TCP perf, CC, 6-8w)
│ │
│ ├── Phase 5 (drivers, HW validation, ongoing)
│ │
└── Phase 2 (IPv6, 4-6w) ──────────────────────┤
└── Phase 4 (firewall, NAT, 6-10w)
└── Phase 6 (forwarding, virtual devices, 8-16w)
```
**Critical path:** Phase 0 → Phase 1 → Phase 3 (TCP perf is the highest user-visible impact).
**Parallel track:** Phase 2 (IPv6) can proceed alongside Phase 3 once Phase 0 is done.
**Enabling track:** Phase 4 (firewall) unblocks Phase 6 (forwarding/NAT).
**Long tail:** Phase 5 (hardware validation) and Phase 6 (virtual devices) run continuously.
## Appendix A: File Inventory
### TCP/IP Stack Core (`local/sources/base/netstack/`)
| File | Purpose |
|---|---|
| `src/main.rs` | Daemon entry: event loop, scheme fd setup, `Smolnetd::new()` |
| `src/scheme/mod.rs` | `Smolnetd` struct: owns `Interface`, `SocketSet`, `Router`, all scheme handlers |
| `src/scheme/socket.rs` | Generic `SocketScheme<SocketT>`: open/dup/read/write/fcntl/fevent |
| `src/scheme/tcp.rs` | `TcpScheme` over smoltcp `TcpSocket`. Port range 4915265535. |
| `src/scheme/udp.rs` | `UdpScheme` over smoltcp `UdpSocket`. Connected-UDP peek filter. |
| `src/scheme/icmp.rs` | `IcmpScheme` over smoltcp `IcmpSocket`. Root-only. |
| `src/scheme/ip.rs` | `IpScheme` over smoltcp `RawSocket`. Root-only. |
| `src/scheme/netcfg/mod.rs` | `NetCfgScheme`: filesystem-like config (routes, DNS, interfaces). |
| `src/scheme/netcfg/nodes.rs` | `cfg_node!` macro for declaring config tree nodes. |
| `src/scheme/netcfg/notifier.rs` | fevent notification on config change. |
| `src/link/mod.rs` | `LinkDevice` trait + `DeviceList` container. |
| `src/link/ethernet.rs` | `EthernetLink`: ARP, neighbor cache (60s TTL), frame I/O. **IPv4-only.** |
| `src/link/loopback.rs` | Loopback `LinkDevice`. |
| `src/router/mod.rs` | `Router`: smoltcp `phy::Device` impl. RX/TX packet buffers. MTU=1486. |
| `src/router/route_table.rs` | `RouteTable`: `Vec<Rule>` longest-prefix-match. |
| `src/buffer_pool.rs` | Buffer pool (audit usage). |
| `src/port_set.rs` | Ephemeral port allocation (4915265535). |
| `src/error.rs` | Error types. |
| `src/logger.rs` | `redox_log` integration. Logger name: `"smolnetd"`. |
| `Cargo.toml` | smoltcp 0.12.0 features, redox-scheme/daemon/event deps. |
### Driver Family (`local/sources/base/drivers/net/`)
| File | Purpose |
|---|---|
| `driver-network/src/lib.rs` | `NetworkAdapter` trait (4 methods) + `NetworkScheme<T>` wrapper. |
| `e1000d/src/{main,device}.rs` | Intel 8254x. PCI BAR0 MMIO. IRQ-driven. |
| `e1000d/config.toml` | PCI IDs: 8086:1004, 100e, 100f, 109a, 1503. |
| `ixgbed/src/{main,device}.rs` | Intel 82599 10GbE. |
| `rtl8139d/src/{main,device}.rs` | Realtek RTL8139. |
| `rtl8168d/src/{main,device}.rs` | Realtek RTL8168/8169. |
| `virtio-netd/src/{main,scheme}.rs` | VirtIO net. DMA-backed rx/tx queues. |
### Socket Layer (`local/sources/relibc/`)
| File | Purpose |
|---|---|
| `src/header/sys_socket/mod.rs` | POSIX socket API: socket/bind/connect/listen/accept/send/recv/... |
| `src/header/sys_socket/constants.rs` | SOCK_STREAM, SOCK_DGRAM, AF_INET, SOL_SOCKET, SO_* constants. |
| `src/platform/redox/socket.rs` | Redox backend: AF_INET→`/scheme/tcp`, dup-based connect/bind. |
| `src/header/netdb/mod.rs` | getaddrinfo, getnameinfo, gethostbyname, h_errno. |
### Protocol Layer (`local/sources/libredox/`)
| File | Purpose |
|---|---|
| `src/lib.rs:909` | `SocketCall` enum: Bind/Connect/SetSockOpt/GetSockOpt/SendMsg/RecvMsg/Unbind/GetToken/GetPeerName/Shutdown. |
### Kernel Scheme Dispatch (`local/sources/kernel/`)
| File | Purpose |
|---|---|
| `src/scheme/mod.rs` | Scheme registry: global schemes + UserScheme dispatch. |
| `src/scheme/user.rs` | `UserScheme`: forwards open/read/write/dup/call to userspace daemons. |
### Configuration
| File | Purpose |
|---|---|
| `config/redbear-netctl.toml` | Netctl profile examples (wired-DHCP, wired-static, wifi-dhcp, wifi-open). Init service `12_netctl.service`. |
| `config/redbear-wifi-experimental.toml` | Wi-Fi experimental target (includes `redbear-iwlwifi`). |
| `config/redbear-mini.toml` | Mini target: includes `redbear-netctl`, `redbear-netstat`, `redbear-wifictl`, `smolnetd` service. |
| `config/redbear-full.toml` | Full desktop target. |
| `/etc/init.d/10_smolnetd.service` | smolnetd (notify type). Requires `00_pcid-spawner`. |
| `/etc/init.d/10_dhcpd.service` | dhcpd (oneshot_async). Requires `10_smolnetd`. |
### Custom Red Bear Networking Tools (`local/recipes/system/`)
| Package | Purpose |
|---|---|
| `redbear-netctl` | Network profile orchestration (Arch netctl-style). |
| `redbear-netctl-console` | TUI frontend for `redbear-netctl`. |
| `redbear-netstat` | Socket/interface/routing statistics. |
| `redbear-wifictl` | Wi-Fi control daemon + scheme. |
| `redbear-mtr` | MTR (traceroute + ping). |
| `redbear-nmap` | Port scanner. |
| `redbear-traceroute` | Traceroute. |
## Appendix B: Linux 7.1 Reference File Map
For cross-referencing during implementation, the key Linux 7.1 source locations:
| Subsystem | Linux Path |
|---|---|
| Socket layer | `net/socket.c` (3,822 LoC) |
| TCP | `net/ipv4/tcp.c` (5,382 LoC), `tcp_input.c`, `tcp_output.c` |
| UDP | `net/ipv4/udp.c` (3,892 LoC) |
| IP input/output | `net/ipv4/ip_input.c`, `ip_output.c` |
| IPv6 | `net/ipv6/` (40+ files), always built-in in 7.1 |
| FIB routing | `net/ipv4/fib_trie.c`, `fib_semantics.c`, `include/net/ip_fib.h` |
| Netfilter hooks | `include/uapi/linux/netfilter.h:43`, `struct nf_hook_ops` |
| Conntrack | `net/netfilter/nf_conntrack_core.c`, `include/net/netfilter/nf_conntrack.h:74` |
| nftables | `net/netfilter/nf_tables_api.c`, 57+ `nft_*.c` |
| Congestion control | `include/net/tcp.h:1325` `struct tcp_congestion_ops`, `tcp_cubic.c`, `tcp_bbr.c` |
| NAPI | `include/linux/netdevice.h:381` `struct napi_struct` |
| GRO/GSO | `net/core/gro.c`, `net/core/gso.c` |
| Zero-copy send | `net/ipv4/tcp.c:1140`, `net/core/skbuff.c:1719` |
| `sk_buff` | `include/linux/skbuff.h` (5,462 LoC) |
| `net_device` | `include/linux/netdevice.h:2139` (5,737 LoC) |
| `net_device_ops` | `include/linux/netdevice.h:1443` |
| Wireless (cfg80211) | `include/net/cfg80211.h` (10,995 LoC), `net/wireless/core.c` |
| Wireless (mac80211) | `net/mac80211/` (60+ files) |
| nl80211 | `include/uapi/linux/nl80211.h` (9,080 LoC) |
| epoll | `fs/eventpoll.c` (~3,000 LoC) |
| io_uring networking | `io_uring/net.c`, `io_uring/zcrx.c`, `IORING_OP_RECV_ZC` |
## Appendix C: Upstream Redox Networking Activity (20242026)
Selected high-relevance commits from `redox-os/*` repositories. Full list maintained in the
sync tracking issue.
### Kernel
- `6c3d5d28c` (2026-07-01) Move FD allocation logic into userspace
- `c089667ad` (2025-12-13) Remove legacy packet user schemes
- `4ff82ad8b` (2025-11-27) Fix user scheme deadlocks in `call_extended_inner`
- `08ea1da2f` (2025-07-05) Trigger read event for user schemes on fd close
- `99ff55ee1` (2026-02-08) Demux results as soon as received from user scheme
### Netstack (now in `base`)
- `b92be2e7d` (2025-03-09) Update to smoltcp 0.12
- `d7c128684` (2025-03-09) Use CUBIC as TCP congestion controller
- `640e54897` (2024-09-04) Use 0.0.0.0 as default IP (DHCP fix)
- `49abe218a` (2024-11-26) ARP fix: respond only to device's broadcast/unicast
- `8e2d02232` (2024-03-18) Switch to libredox and redox-event
- `bafdb3b66` (2024-07-11) New scheme format, no legacy paths
- `f9b3170f0` (2024-02-28) Named network adapters
- `c06e5b14e` (2025-03-10) Netstack moved to base repo
### Drivers
- `ad9305bf9` (2024-02-28) Unify network drivers under `net/`
- `921c6b07f` (2024-12-26) driver-network migrated to redox-scheme
- `dd41c4f13` (2025-11-23) Net: scheme created before device init
- `0f24975ff` (2025-11-29) PCI interrupts rework5 (RTL dedup)
- `407533201` (2025-09-24) Add ThinkPad T60 ethernet to e1000d
### relibc
- `94b0cfc68` (2026-02-08) Reimplement recvmsg/sendmsg with bulk FD passing
- `1978c1aa4` (2026-03-25) Full recvmsg implementation
- `cab002146` (2026-02-28) Move protocols into libredox
- `6a455159a` (2026-01-22) Fix getaddrinfo infinite loop hang
- `726a0fb1a` (2025-09-15) getaddrinfo NULL nodename, AI_PASSIVE
- `5334455a2` (2025-08-20) getnameinfo + getaddrinfo loopback
- `9eaa9e82b` (2025-11-08) Pass through all SOL_SOCKET options
- `d44010170` (2025-07-18) UDS bind/connect with RedoxFS
- `9c6701802` (2026-01-07) Add sys_socket tests
### syscall
- `7a1409a91` (2026-05-27) Multiple FDs variant for call (bulk FD passing)
- `178461f6f` (2025-12-27) Remove remnants of old packet format
### Build/config
- `85b62fd85` (2026-03-01) Support networking in all configs
- `193e81897` (2026-02-17) ~115 WIP networking recipes
- `b68e5a685` (2026-04-11) Move dhcpd from netutils to base
---
**Document version:** 1.0 (2026-07-07)
**Authority:** Canonical for TCP/IP stack, socket surface, Ethernet drivers, and network
configuration. See companion plans for Wi-Fi, IRQ, and Bluetooth.
+1 -1
View File
@@ -78,7 +78,7 @@ After ANY change to the patches list or patch files:
2. Full rebuild: `REDBEAR_ALLOW_PROTECTED_FETCH=1 CI=1 make r.base`
3. Verify NO "FAILED" or "rejects" in output
4. Verify all expected binaries in stage: `ls stage/usr/bin/ stage/usr/lib/drivers/`
5. Full image build: `./local/scripts/build-redbear.sh redbear-full`
5. Full image build: `CI=1 make all CONFIG_NAME=redbear-full`
## Known Issues
+5 -6
View File
@@ -135,12 +135,11 @@ Two separate DMI systems exist in the source tree:
These are **incompatible at runtime** — the acpid scheme must serve DMI data
in *both* the flat-file and the per-field-subpath form. If acpid only
serves one, the other system is inert. The
`local/sources/base/drivers/hwd/src/main.rs` hwd daemon runs `acpid`
and the underlying `local/sources/base/drivers/acpid/src/scheme.rs`
[`local/sources/base/drivers/hwd/src/main.rs`](https://gitea.redbearos.org/vasilito/base)
hwd daemon runs `acpid` and the underlying
[`local/sources/base/drivers/acpid/src/scheme.rs`](https://gitea.redbearos.org/vasilito/base)
defines the DMI surface — check what it actually serves before assuming
both work. (The `base` source tree lives as the `submodule/base`
branch inside the canonical `RedBear-OS` repo per the SINGLE-REPO
RULE in `local/AGENTS.md`.)
both work.
## Confirmed live TOML coverage
@@ -158,7 +157,7 @@ list.
| `20-usb.toml` | 147 USB controller entries (logically mirrors `usb_table.rs`) |
| `30-net.toml` | Network controllers (Realtek + Broadcom) |
| `30-storage.toml` | (file does not exist — see `40-storage.toml`) |
| `40-storage.toml` | 214 USB mass-storage quirks (mined from Linux 7.1 `unusual_devs.h`) |
| `40-storage.toml` | 214 USB mass-storage quirks (mined from Linux 7.0 `unusual_devs.h`) |
| `50-system.toml` | System-level / BIOS quirks |
| `60-i2c-hid.toml` | I2C HID recovery quirks |
| `70-ucsi.toml` | Type-C / UCSI quirks |
+1 -1
View File
@@ -220,7 +220,7 @@ description = "SND1 Storage"
flags = ["ignore_residue"]
```
The full 214-entry table lives in `quirks.d/30-storage.toml`, mined from Linux 7.1's
The full 214-entry table lives in `quirks.d/30-storage.toml`, mined from Linux 7.0's
`drivers/usb/storage/unusual_devs.h`.
Available C quirk flag macros (defined in `linux/pci.h`):
+54 -233
View File
@@ -1,15 +1,14 @@
# Building Rich Red Bear Ratatui Apps — Patterns Guide
**Created:** 2026-06-20
**Last updated:** 2026-07-06 (added §15–§18: braille graphs, modal dialogs, key audit, redbear-power v1.44 status)
**Last updated:** 2026-06-20 (added §13 ratatui 0.30 best-practices update)
**Source:** Extracted from TLC (Twilight Commander) production codebase — 46k+ lines of pure Rust ratatui
**Audience:** Developers porting or building TUI apps for Red Bear OS
**ratatui version:** 0.29 baseline (TLC), 0.30 for new apps
**ratatui version:** 0.29 baseline (TLC), 0.30 update notes added §13
**Cross-references:**
- `local/recipes/system/redbear-power/` (production ratatui 0.30 consumer, 13,091 LoC, 27 modules, 217 tests)
- `local/recipes/system/redbear-power/` (production ratatui 0.30 consumer)
- `local/recipes/tui/tlc/` (46k+ LoC TUI file manager, ratatui 0.29)
- `local/docs/redbear-power-improvement-plan.md` (improvement roadmap)
- `local/docs/bottom-vs-redbear-power-assessment.md` (bottom comparison assessment)
- `local/docs/redbear-power-improvement-plan.md` (Phase 2 roadmap derived from this doc)
---
@@ -755,7 +754,7 @@ fn main() -> Result<()> {
---
## Summary: 13 Rules for Red Bear Ratatui Apps
## Summary: 10 Rules for Red Bear Ratatui Apps
1. **Theme-driven colors** — every render path takes `&Theme`, never hardcodes colors
2. **Poll-based event loop**`rustix::event::poll` with 100ms timeout for animations
@@ -767,9 +766,6 @@ fn main() -> Result<()> {
8. **Shared theme crate**`redbear-tui-theme` for brand consistency
9. **No platform gates**`cfg(unix)` only, same binary on Linux + Redox
10. **Test with TestBackend** — snapshot-style UI tests + thorough unit tests
11. **Braille graphs via Canvas**`Marker::Braille` + `RingHistory::display_max()` for stable y-axis
12. **Modal dialogs with Cell**`Cell<usize>` for interior mutability; `Widget for &Dialog`
13. **Audit key bindings**`grep Key::Char` + `sort | uniq -c | sort -rn` after every change
---
@@ -1094,33 +1090,45 @@ Use the canonical pattern from §1 (poll + sleep).
| `Box<dyn Widget>` | Yes | Yes | **`Box<dyn WidgetRef>`** (unstable) |
| `frame.buffer_mut()` | Yes | Yes | Stable |
| Modular crates | Single crate | Split (3-4 crates) | More granular split |
### 13.14 redbear-power Current Status (v1.44+, 2026-07-06)
### 13.14 redbear-power Specific Findings
A comprehensive audit and improvement cycle (16 passes) against bottom v0.11.2
brought redbear-power from v1.20 (6,360 LoC, 21 modules, 76 tests) to
**v1.44+ (13,091 LoC, 27 modules, 217 tests)**. All items below are
implemented and tested.
A targeted audit of `local/recipes/system/redbear-power/` (v1.20, 6360 LoC
across 21 modules, 76 unit tests) produced these actionable findings:
| Area | Status | Detail |
|------|--------|--------|
| Braille time-series graphs | ✅ v1.44 | `BrailleGraph` widget using ratatui's `Canvas` + `Marker::Braille`; 5 graphs across 4 tabs (CPU%, Temp°C, PkgW, Net KiB/s, Disk KiB/s); y-axis labels with reserved margin |
| RingHistory buffer | ✅ v1.44 | O(1) ring buffer with `display_max()` rounding to nice values (1,2,5,10,20,50...); prevents y-axis jitter |
| Theme system | ✅ v1.44 | `Theme` struct with `dark()`, `light()`, `high_contrast()` presets; `--theme` CLI flag with validation; wired into `panel_border()`, `BrailleGraph`, header governor, cursor highlight |
| Widget expansion | ✅ v1.44 | `e` key toggles full-screen tab rendering; hint bar at bottom |
| Data freeze | ✅ v1.44 | `f` key pauses data updates; `[FROZEN]` indicator in keybar |
| Process kill dialog | ✅ v1.44 | `k` key (Process tab) opens signal selection modal; `Cell<usize>` for interior mutability (avoids borrow conflicts); auto-closes 2s after signal sent; 6 unit tests |
| Panic hook | ✅ v1.44 | `panic::set_hook` restores terminal on panic |
| TTY check | ✅ v1.44 | `IsTerminal` check before raw mode; clear error message |
| MSR cache | ✅ v1.44 | Per-CPU failure cache avoids repeated doomed opens; auto-clears every ~30s |
| Skip-refresh guard | ✅ v1.44 | Adaptive throttle: if refresh >200ms, skip next cycle |
| Collector fix | ✅ v1.44 | Removed `Barrier` from per-CPU collector; threads start work immediately |
| Key shadowing audit | ✅ v1.44 | 4 bugs found and fixed: `g` (governor vs. move-to-top), `T` (tab-cycle vs. tree-toggle), `f` (freeze vs. process-filter), `f` duplicate removed |
| `--once` graph output | ✅ v1.44 | `--once` renders 3 braille graphs + all text panels |
| LTO release | ✅ v1.44 | `lto = true, opt-level = 3, codegen-units = 1` → 4.1 MB binary (-33%) |
| Integration tests | ✅ v1.44 | 8 new tests: CPU detection, graph population, process count, governor available, temp source, expand, freeze, skip-refresh |
| All previous v1.0v1.21 features | ✅ | Mouse, config, tabs, D-Bus, Linux fallbacks, meminfo, DMI, battery, sensors, network, storage, process list, CPU%, throughput, sort, filter, PID detail, SMART, benchmarks, HWP, cpuid, scheduler stats, CPU affinity, per-thread IO |
| Severity | Finding | Fix |
|----------|---------|-----|
| **bug** | `render_prochot_alert` always passes freshly-constructed `Instant::now()`, so the pulse never toggles | Use `Frame::count()` (§13.3) |
| minor | `centered_rect` hand-rolled | Use `Rect::centered` (§13.6) |
| minor | `Layout::default().split(...)` returns chunks | Use `area.layout(&Layout)` (§13.5) |
| cosmetic | `Style::default().fg(...)` chains | Use Stylize shorthand (§13.4) |
| cosmetic | `Theme` not centralized — colors scattered | Centralize as §12 (`redbear-tui-theme`) |
| minor | Input poll (250-2000ms) blocks snappy response | Decouple refresh from input (§1 ratatui audit §8) |
| cosmetic | Duplicate comment in `snapshot()` | Trivial cleanup |
| feature | No mouse support | Implemented in v1.1 (§13.16) |
| feature | No config file | Implemented in v1.2 (`config.rs` module) |
| feature | No multi-view tabs (single Per-CPU view only) | Implemented in v1.2 (`Tabs` widget + `TabId` enum) |
| feature | No D-Bus export for headless clients | Implemented in v1.1 (`dbus.rs` module + zbus 5) |
| feature | No Linux-host fallbacks (hardcoded `/scheme/sys/...` paths) | Implemented in v1.3 (`platform.rs` runtime probe + per-module fallbacks) |
| feature | No memory or OS info display | Implemented in v1.4 (`meminfo.rs` module + `mem_bar_line` helper) |
| feature | No Motherboard / DMI tab | Implemented in v1.5 (`dmi.rs` module + `TabId::Motherboard`) |
| feature | No Battery tab | Implemented in v1.6 (`battery.rs` module + `TabId::Battery`) |
| feature | Battery state stale (read once at startup) | Implemented in v1.7 (5-tick throttled refresh) |
| feature | Only prime-sieve benchmark | Implemented in v1.8 (FFT + AES + single-core toggle, 5 unit tests) |
| feature | No Sensors tab | Implemented in v1.9 (`sensor.rs` module + `TabId::Sensors`, 7 unit tests) |
| feature | Per-CPU Temp n/a on AMD (Intel-only MSR) | Implemented in v1.10 (`SensorInfo::pkg_temp_c` fallback to k10temp/coretemp/zenpower) |
| feature | No Network tab | Implemented in v1.11 (`network.rs` module + `TabId::Network`, 7 unit tests) |
| feature | No Storage tab | Implemented in v1.12 (`storage.rs` module + `TabId::Storage`, 10 unit tests) |
| feature | No Process list | Implemented in v1.13 (`process.rs` module + `TabId::Process`, 9 unit tests) |
| feature | No CPU% in Process tab | Implemented in v1.14 (`ProcInfo::read_with_cpu_pct` + 4 unit tests) |
| feature | No disk throughput in Storage tab | Implemented in v1.15 (`StorageInfo::read_with_throughput` + 3 unit tests) |
| feature | No network throughput in Network tab | Implemented in v1.16 (`NetInfo::read_with_throughput` + 3 unit tests) |
| feature | No sort modes in Process tab | Implemented in v1.17 (`SortMode` enum + 6 unit tests, hotkey `o`) |
| feature | No process filtering | Implemented in v1.18 (`App.process_filter` + hotkey `f` + 4 unit tests) |
| feature | No PID detail view | Implemented in v1.19 (`pid_detail.rs` module + Enter/Esc handling + 7 unit tests) |
| feature | No SMART disk health data | Implemented in v1.20 (`smart.rs` module + smartctl subprocess + 7 unit tests) |
| feature | No SMART UI integration | Implemented in v1.21 (Storage tab badge: PASSED/FAILED/missing/error) |
Full assessment: see `local/docs/bottom-vs-redbear-power-assessment.md`.
Full plan: see `local/docs/redbear-power-improvement-plan.md`.
### 13.15 v1.4 Module Pattern: `meminfo.rs` for Read-Only System Data
@@ -1374,201 +1382,17 @@ gives a natural unit-of-work (count) that scales with thread count.
---
## 15. Braille Time-Series Graph Pattern
**Source:** `redbear-power/src/graph.rs` (227 lines, 5 unit tests)
**Borrowed from:** bottom's `canvas/components/time_graph/` approach, simplified
### Pattern: Canvas + Marker::Braille + RingHistory
ratatui 0.30's built-in `Canvas` widget with `Marker::Braille` gives 8 data
points per terminal cell (2 columns × 4 rows). A 40-column graph area can
display 80 time points at 4-char y-axis label margin.
```rust
pub struct BrailleGraph<'a> {
pub values: &'a [f64], // time-series data
pub max_value: f64, // y-axis ceiling (use display_max())
pub color: Color, // line color
pub title: &'a str, // border title
pub focused: bool, // affects border style
pub x_labels: Option<(&'a str, &'a str)>,
pub y_labels: Option<(String, String)>,
pub theme: &'a Theme, // THEME-DRIVEN, never hardcoded
}
impl Widget for BrailleGraph<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
// 1. Reserve 4 columns on left for y-axis labels
let label_w: u16 = if self.y_labels.is_some() { 4 } else { 0 };
let canvas_area = Rect { x: inner.x + label_w, width: inner.width - label_w, ..inner };
// 2. Draw lines via Canvas
Canvas::default()
.x_bounds([0.0, (n - 1) as f64])
.y_bounds([0.0, self.max_value])
.marker(Marker::Braille)
.paint(|ctx| {
for w in clamped.windows(2).enumerate() {
ctx.draw(&CanvasLine {
x1: i as f64, y1: w[0],
x2: (i+1) as f64, y2: w[1],
color: self.color,
});
}
})
.render(canvas_area, buf);
// 3. Y-axis labels in reserved margin
if let Some((ref top, ref bot)) = self.y_labels {
buf.set_string(inner.x, inner.y, &format!("{:>4}", top), label_style);
buf.set_string(inner.x, bottom_y, &format!("{:>4}", bot), label_style);
}
}
}
```
### RingHistory: Stable Y-Axis with Nice Maxima
```rust
pub struct RingHistory {
data: Vec<f64>,
capacity: usize,
len: usize,
}
impl RingHistory {
/// Round peak to nice values: 1,2,5,10,20,50,100,200,500...
/// Prevents y-axis jitter from every-tick rescaling.
pub fn display_max(&self) -> f64 {
let raw = self.max();
if raw <= 0.0 { return 1.0; }
let mag = 10.0_f64.powi((raw.log10().floor()) as i32);
let norm = raw / mag;
let nice = if norm <= 1.0 { 1.0 } else if norm <= 2.0 { 2.0 }
else if norm <= 5.0 { 5.0 } else { 10.0 };
nice * mag
}
}
```
**Key decisions:**
- Use `display_max()` **not** `max()` for the y-axis — empty data returns 1.0, populated data rounds to stable nice numbers
- Reserve 4-column label margin with `Rect` offset; labels use `buf.set_string` (multi-cell) not `cell.set_symbol` (single-cell)
- Canvas uses full area after margin — braille grid auto-scales within `x_bounds`/`y_bounds`
- Graph titles match between normal and expanded mode — audit for consistency
- Graphs render in both System tab (3 side-by-side) and expanded mode (stacked)
---
## 16. Modal Dialog Pattern (Interior Mutability)
**Source:** `redbear-power/src/kill.rs` (148 lines, 6 unit tests)
### Problem
Modal dialogs need mutable state (`ListState` for selection) but must render
via `&self` to avoid borrow conflicts with the parent `App` struct's immutable
borrows during `terminal.draw()`. `Widget for &mut KillDialog` causes
`E0502: cannot borrow as mutable because also borrowed as immutable`.
### Solution: `Cell<usize>` for Selection State
```rust
pub struct KillDialog {
pub open: bool,
pub pid: u32,
pub comm: String,
selected: Cell<usize>, // ← interior mutability
signals: Vec<(&'static str, i32)>,
pub result: Option<String>,
result_at: Option<Instant>, // auto-close timer
}
impl Widget for &KillDialog { // ← &self, not &mut self
fn render(self, area: Rect, buf: &mut Buffer) {
let sel = self.selected.get();
// Render list with highlighted item at `sel`
for (i, (label, _)) in self.signals.iter().enumerate() {
let item = if i == sel {
ListItem::new(Line::styled(format!("{label}"), highlight_style))
} else {
ListItem::new(Line::from(format!(" {label}")))
};
}
}
}
```
### Auto-Close Pattern
```rust
pub fn send_signal(&mut self) {
self.result = Some(format!("Sent signal {} to PID {}", sig, self.pid));
self.result_at = Some(Instant::now()); // start 2s timer
}
pub fn auto_close_if_done(&mut self) {
if let Some(at) = self.result_at {
if at.elapsed().as_millis() > 2000 {
self.close();
}
}
}
```
Called every tick in the main loop: `app.kill_dialog.auto_close_if_done();`
**Key decisions:**
- `Cell<usize>` avoids `RefCell` overhead — only one `usize` field, no runtime borrow checks
- `Widget for &KillDialog` (immutable ref) — compatible with `&app` borrows during draw
- Auto-close timer prevents stuck dialog after signal sent
- `Enter` blocked when `result.is_some()` to prevent double-send
---
## 17. Key Binding Audit Pattern
**Source:** 4 bugs found and fixed across 16 passes of redbear-power
### Pattern: Audit All `Key::Char` Arms for Shadowing
In a large `match k { ... }` block (200+ arms), duplicate `Key::Char('X')`
patterns silently shadow each other — the first one wins, the second is
unreachable dead code.
```bash
# Audit command: detect all duplicate key bindings
grep -oP "Key::Char\('[^']+'\)" main.rs | sort | uniq -c | sort -rn
```
**Bugs found in redbear-power:**
| Key | First handler | Shadowed handler | Fix |
|-----|--------------|------------------|-----|
| `'g'` | `cycle_governor()` | `move_to_edge(true)` | Removed (Home already does this) |
| `'T'` | `set_tab(next())` | `process_tree = !process_tree` | Changed tree toggle to `'y'` |
| `'f'` | `frozen = !frozen` | process filter prompt | Changed filter to `'F'` |
**Guard-based arms (`Key::Char('k') if condition`) are NOT duplicates** — they
fall through when the guard is false. The audit only flags identical
unguarded patterns.
**Rule:** After any key binding change, re-run the audit. Never have two
unguarded `Key::Char('X')` arms in the same match.
---
## 14. Cross-Reference: redbear-power as a Reference Implementation
The `redbear-power` recipe (`local/recipes/system/redbear-power/`) is a useful
reference for new TUI apps because:
1. **Small enough to read in one sitting** (~13,100 LoC across 27 modules, with 217 unit tests)
1. **Small enough to read in one sitting** (~6400 LoC across 21 modules, with 76 unit tests)
2. **Self-contained** — no D-Bus, no external state, just sysfs/MSR/procfs + meminfo + DMI + battery + hwmon + net + storage + proc + pid_detail + smart
3. **Modern ratatui 0.30 patterns**`TableState`, modular layout, status bars, `Tabs` widget, `Canvas` + `Marker::Braille` graphs, modal popups (`Clear` + centered `Rect`), theme-driven borders
4. **Cross-platform** — same binary works on Linux + Redox (MSR/scheme + sysfs/proc fallback + hwmon fallback)
3. **Modern ratatui 0.30 patterns**`TableState`, modular layout, status bars, `Tabs` widget, modal popups (`Clear` + centered `Rect`)
4. **Cross-platform** — same binary works on Linux + Redox (MSR/scheme + sysfs/proc fallback + hwmon fallback for AMD CPUs + net/sysfs fallback + storage/sysfs fallback + procfs fallback + /proc/[pid]/* parsers + smartctl subprocess with graceful missing-binary degradation + UI badge display)
5. **Well-documented** — extensive code comments + this doc + improvement plan
6. **Testable**217 unit tests covering braille graphs, kill dialog, benchmarks, MSR cache, process tree, IO sparklines, sensor parsing, network parsing, storage parsing, /proc/[pid]/* parsers, SMART, sort modes, filter matching, PID detail, LRU eviction, display_max rounding
6. **Testable**bench + sensor + network + storage + process + pid_detail + smart modules have 76 unit tests covering stress modes + hwmon unit conversions + multi-vendor pkg_temp_c + binary byte formatting + disk stat parsing + delta math + /proc/[pid]/stat parser with space-handling + CPU% delta math + disk throughput delta math + network throughput delta math + sort mode comparisons + process filter matching + /proc/[pid]/{status,io,smaps_rollup} parsers + smartctl attribute parsing
When porting a new Red Bear TUI app, structure it like redbear-power:
@@ -1578,25 +1402,22 @@ my-tui-app/
├── recipe.toml # path = "source", template = "cargo"
└── source/
└── src/
├── main.rs # event loop, key + mouse + D-Bus dispatch
├── app.rs # App struct, all state, refresh cadence, coprime moduli
├── render.rs # render_header, render_*_panel, render_keybar, snapshot
├── theme.rs # Theme struct with presets, color helpers, all styles centralized
├── graph.rs # BrailleGraph widget + RingHistory buffer
├── kill.rs # Modal dialog with Cell<usize> for interior mutability
├── event.rs # Typed Event enum (Key, Mouse, Tick, Resize, Terminate)
├── platform.rs # runtime data-source probes
├── main.rs # event loop, key + mouse + D-Bus dispatch (~475 lines)
├── app.rs # App struct, all state, refresh cadence (~535 lines)
├── render.rs # render_header, render_table, render_controls (~925 lines)
├── platform.rs # runtime data-source probes (~290 lines)
└── <data>.rs # detect, read_*, helpers, format_*
```
---
## See Also
- `local/recipes/system/redbear-power/source/src/` — reference implementation (v1.44+, 13,091 LoC, 27 modules, 217 tests)
- `local/recipes/system/redbear-power/source/src/` — reference implementation
- `local/recipes/tui/tlc/source/src/` — 46k+ LoC production TUI
- `local/recipes/tui/redbear-tui-theme/` — shared theme constants
- `local/docs/redbear-power-improvement-plan.md` — improvement roadmap
- `local/docs/bottom-vs-redbear-power-assessment.md` — bottom comparison and borrow analysis
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` — desktop stack planning
- `local/docs/redbear-power-improvement-plan.md`Phase 2 roadmap derived from this doc, with §28 v1.4 status
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md`desktop stack planning, §3.3.2 v0.1v1.4
- https://ratatui.rs/ — official docs
- https://github.com/ratatui/ratatui/tree/main/examples — canonical patterns
- https://github.com/X0rg/CPU-X — cpu-x v4.7 (7000+ LoC, mature CPU monitor reference)
-306
View File
@@ -1,306 +0,0 @@
# 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**:
1. **kstop string-arg path** (Phase I.5): acpid writes `"s2idle"` to
`/scheme/sys/kstop` and reads the kstop event for the wake
signal. This is the original path; it works without the
AcpiVerb extension.
2. **Typed-AcpiVerb path** (Phase J): acpid calls
`kstop_enter_s2idle()` which uses the new
`AcpiVerb::EnterS2Idle` and `AcpiVerb::ExitS2Idle` variants
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/`**: upstream
`redox_syscall 0.8.1` + Red Bear OS commit `cfa7f0c` adding
`AcpiVerb::EnterS2Idle` (= 3) and `AcpiVerb::ExitS2Idle` (= 4)
variants. The version field stays at upstream 0.8.1 per the
AGENTS.md "GOLDEN RULE".
* **Local fork `local/sources/libredox/`**: upstream
`libredox 0.1.17` with the `redox_syscall` dep redirected
to `path = "../syscall"`. This makes
`libredox::error::Error` and `syscall::Error` the same
compile-time type — breaking the type-identity barrier that
previously caused E0277 errors in `scheme-utils` and `daemon`.
* **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.patch`
is the durable overlay patch backing the syscall fork commit.
### Phase J End-to-End s2idle Flow
1. acpid: `enter_s2idle()` (`_TTS(0)`, `_PTS(0)`, `_SST(3)`)
2. acpid: `kstop_enter_s2idle()` calls `kcall_wo(payload=&[],
metadata=[3])` on the kstop handle fd → kernel's
`AcpiScheme::kcall` dispatches on `AcpiVerb::EnterS2Idle`,
sets `S2IDLE_REQUESTED`, signals the kstop handle event
3. kernel idle path: `mwait_loop()` at deepest C-state
4. SCI breaks MWAIT
5. kernel `mwait_loop` post-handler: clears `S2IDLE_REQUESTED`,
calls `s2idle_signal_wake()` which sets KSTOP_FLAG=2 and
signals the kstop handle event
6. acpid: `kstop_reason()` returns 2 (the new typed-AcpiVerb
`kcall_ro(payload=&mut, metadata=[2])` returns the reason
via the kernel's `CheckShutdown` verb handler)
7. acpid: `exit_s2idle()` (`_SST(2)`, `_WAK(0)`, `_SST(1)`)
8. 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→P1` transitions.
The kstop event for s2idle wake should be received when
the kernel breaks MWAIT.
* **Patch application verification**: run `cargo metadata
--format-version 1` and confirm the resolved source URL
for `redox_syscall` and `libredredox` is 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`:
1. **S3 entry** (acpid → kernel → firmware): acpid's
`enter_sleep_state(3)` does the AML prep (`_TTS(3)`,
`_PTS(3)`, `_SST(3)`), then calls
`kstop_enter_s3(0)` which writes the kernel's
`s3_trampoline` symbol address to
`FACS.xfirmware_waking_vector` via the new
`SetS3WakingVector` AcPiVerb. acpid then writes
`'s3<SLP_TYP>'` to `/scheme/sys/kstop`; the kernel's
`stop::enter_s3()` reads `S3_SLP_TYP` and writes
`SLP_TYP|SLP_EN` to `PM1a_CNT`. The platform
firmware enters S3.
2. **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 a
`kstop_reason=3` event 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)
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 (historical — now found on `submodule/base`
and `submodule/kernel` branches inside the canonical `RedBear-OS` repo):
- `submodule/base 5d2d114` — acpid: full Linux AML S-state sequence
- `submodule/kernel 75c7618` — kernel: s2idle / s3 kstop handler
File diff suppressed because it is too large Load Diff
-155
View File
@@ -1,155 +0,0 @@
# Red Bear OS USB Validation Runbook — v3
> Companion to `local/docs/USB-IMPLEMENTATION-PLAN.md` v3.
> This runbook tells operators how to validate USB on a Red Bear build.
## Validation matrix
| Script | What it validates | Maturity target |
|---|---|---|
| `test-xhci-irq-qemu.sh --check` | xHCI interrupt-driven reactor path (line 208 of `irq_reactor.rs`) | `validated-QEMU` |
| `test-xhci-device-lifecycle-qemu.sh --check` | bounded USB attach/detach for HID + storage | `validated-QEMU` |
| `test-usb-qemu.sh` | full USB stack (xHCI + keyboard + tablet + storage) | `validated-QEMU` |
| `test-usb-storage-qemu.sh` | usbscsid autospawn + sector write+readback+restore | `validated-QEMU` |
| `test-usb-runtime.sh` | guest + QEMU mode dispatch harness | harness only |
| `test-usb-maturity-qemu.sh` | aggregate runner — calls the five above in sequence | runner |
| `test-uhci-runtime-qemu.sh --check` *(P1-B)* | UHCI controller enumeration on legacy QEMU machine | `validated-QEMU` (P1-B) |
| `test-ohci-runtime-qemu.sh --check` *(P1-B)* | OHCI controller enumeration on legacy QEMU machine | `validated-QEMU` (P1-B) |
| `test-ehci-class-autospawn-qemu.sh --check` *(P1-A)* | USB keyboard on EHCI route reaches inputd | `validated-QEMU` (P1-A) |
| `test-usb-hub-qemu.sh --check` *(P3-A)* | USB hub enumeration including power timing + wHubDelay | `validated-QEMU` (P3-A) |
| `test-usb-error-recovery-qemu.sh --check` *(P8-C)* | hot-unplug mid-transfer — graceful error, no panic | `validated-QEMU` (P8-C) |
| `test-usb-uas-qemu.sh --check` *(P4-A)* | USB 3.0 storage UAS path active | `validated-QEMU` (P4-A) |
A row's last-passed ISO date goes in `local/docs/HARDWARE-VALIDATION-MATRIX.md`.
## Path A — Host-side QEMU validation
### Pre-flight
```bash
# Confirm the build is fresh
./local/scripts/build-redbear.sh --upstream redbear-mini
ls build/x86_64/redbear-mini/harddrive.img
```
### Run aggregate
```bash
./local/scripts/test-usb-maturity-qemu.sh redbear-mini
```
This runs all five existing QEMU tests in sequence and reports a single pass/fail.
### Run individual
```bash
# Interrupt-mode proof (must show "Running IRQ reactor with IRQ file
# and event queue" in the boot log; must NOT show "in polling mode").
./local/scripts/test-xhci-irq-qemu.sh --check
# Storage write+readback+restore (sector 2048; expects
# [PASS] STORAGE_DISCOVERY + STORAGE_WRITE + STORAGE_READBACK +
# STORAGE_RESTORE).
./local/scripts/test-usb-storage-qemu.sh
# Lifecycle proof (QEMU monitor-driven attach/detach).
./local/scripts/test-xhci-device-lifecycle-qemu.sh --check
```
### What it validates vs. claims
| Claim | Evidence |
|---|---|
| `xhcid` runs interrupt-driven | `[RUNNING] IRQ reactor with IRQ file and event queue` in log |
| `usbscsid` auto-spawns on storage attach | `usbscsid: scheme event` or `[redbear-usb-storage-check] STORAGE_DISCOVERY: disk.usb-...` |
| Storage read+write works | `[PASS] STORAGE_WRITE:` + `[PASS] STORAGE_READBACK:` + `[PASS] STORAGE_RESTORE:` |
| HID auto-spawns on keyboard attach | `usbhidd: registered producer` in log |
| No panics | absence of `panic\|crash\|abort\|RUST_BACKTRACE` in log |
## Path B — In-guest manual validation
Boot the image and check:
```bash
# Inside the guest:
ls /scheme/usb* # host controllers' scheme namespaces
lsusb # walker (in redbear-hwutils package)
redbear-usb-check # pass/fail scheme validator
```
A healthy USB stack shows:
- `/scheme/usb.0000:XX:XX.X_xhci/` (xhcid port directory)
- `/scheme/usb/` (ehcid port directory)
- `/scheme/disk.usb-...+...-scsi/` (usbscsid disk)
- `/scheme/input/...` (usbhidd device)
## Path C — Bare-metal hardware validation (P8-A)
For each (controller, class, board) tuple in `HARDWARE-VALIDATION-MATRIX.md`:
```bash
# On the bare-metal host:
./local/scripts/build-redbear.sh <config>
dd if=build/x86_64/<config>/harddrive.img of=/dev/<target> bs=4M status=progress
# Boot; capture serial console
minicom -D /dev/ttyUSB0 -b 115200 -C capture.bin
# In the captured console:
redbear-info --verbose
lsusb
redbear-usb-check
```
Required test points (per row in the matrix):
1. Controller PCI ID detected
2. Specific class test (HID keypress, storage read+write, etc.)
3. Hot-plug works
4. Suspend/resume works (if device supports)
Update the matrix row: `last_tested = <ISO date>`, `result = pass|fail|partial`.
## Operator runbook for failures
If `test-xhci-irq-qemu.sh` reports polling-mode:
```bash
# Check the boot log for the interrupt path:
grep "IRQ reactor" build/x86_64/redbear-mini/xhci-irq-check.log
# If "in polling mode" appears, xHCI interrupts are bypassed.
# Check the base fork commit:
git -C local/sources/base log --oneline drivers/usb/xhcid/src/main.rs | head -5
# Confirm commit cbd40e0d (or later) is in the merge base.
```
If `test-usb-storage-qemu.sh` reports a panic:
```bash
# Find the panic site:
grep -n 'panic\|unwrap\|expect' build/x86_64/redbear-mini/usb-storage-check.log
# Cross-reference to usbscsid:
grep -rn 'panic!' local/sources/base/drivers/storage/usbscsid/src/
# After P1-B is complete, no panic sites should exist.
```
If a USB device is not auto-spawning:
```bash
# Check if the class driver is wired in the configs:
grep -A2 "redbear-acmd\|redbear-ecmd\|redbear-usbaudiod" config/redbear-mini.toml
# Cross-check drivers.d:
cat local/config/drivers.d/70-usb-class.toml
# After P1-A, all four controllers should auto-spawn via the unified trait.
```
## Validation cadence
- **On every release branch cut:** run all QEMU tests; update matrix with last-tested dates.
- **On every P-phase completion:** add the new test scripts to the matrix.
- **Monthly:** if any operator has bare-metal access, add one hardware row.
## See also
- `local/docs/USB-IMPLEMENTATION-PLAN.md` v3 — the plan this runbook validates
- `local/docs/HARDWARE-VALIDATION-MATRIX.md` — the matrix this runbook populates
- `local/scripts/test-usb-maturity-qemu.sh` — the aggregate entry point
+1 -5
View File
@@ -15,11 +15,7 @@ current plans. They are kept for reference only.
| `ACPI-I2C-HID-IMPLEMENTATION-PLAN.md` | (Deferred — USB HID is primary input path) |
| `GRUB-INTEGRATION-PLAN.md` | GRUB is fully implemented (redbear-grub config, installer support, grub recipe) |
| `VFAT-IMPLEMENTATION-PLAN.md` | VFAT is fully implemented (fatd, fat-mkfs, fat-label, fat-check) |
| `USB-BOOT-INPUT-PLAN.md` | Superseded — USB HID in initfs, USB storage in initfs (Phase B2). Boot-input analysis remains historically useful; the live-input priority lives in `USB-IMPLEMENTATION-PLAN.md` v2. |
| `XHCID-DEVICE-IMPROVEMENT-PLAN.md` | Superseded by `USB-IMPLEMENTATION-PLAN.md` v2 — phases 17 absorbed into the new plan's P0P3. |
| `USB-IMPLEMENTATION-PLAN-v1-2026-04.md` | Superseded by `USB-IMPLEMENTATION-PLAN-v2-2026-07.md` (archived). v1 overstated xHCI interrupt-driven operation; v2 rebased onto current source state and Redox 0.x USB HEAD. |
| `USB-IMPLEMENTATION-PLAN-v2-2026-07.md` | Superseded by `USB-IMPLEMENTATION-PLAN.md` v3 (current). v2 captured P0P5 host-controller work; v3 expands to full USB first-class-citizen scope including UAS, error recovery, CDC ACM, HID report parsing, and hardware matrix. |
| `USB-VALIDATION-RUNBOOK-2026-07.md` | Historical validation runbook for P0 era. v3 validates against the expanded scope. |
| `USB-BOOT-INPUT-PLAN.md` | Superseded — USB HID in initfs, USB storage in initfs (Phase B2) |
| `ZSH-PORTING-PLAN.md` | Deferred indefinitely — ion is the default shell |
## Date archived: 2026-05-03
@@ -1,736 +0,0 @@
# Red Bear OS USB Implementation Plan
## Purpose
This document defines the current state, completeness, and implementation path for USB in Red Bear
OS. It distinguishes between the **upstream source** (unpatched) and the **Red Bear state** (after
applying `local/patches/base/redox.patch`).
The goal is to describe USB in terms of **what is built**, **what is patched**, **what is
actually usable**, and **what still needs to be implemented** before Red Bear can honestly claim a
modern, future-proof USB stack.
This document is Red Bear-specific. It uses current repo evidence from code, configs, runtime
tooling, and status docs instead of assuming inherited upstream documentation is fully current.
## Validation States
- **builds** — code exists in-tree and is expected to compile
- **enumerates** — runtime surfaces can discover controllers, ports, or descriptors
- **usable** — a specific controller/class path works in a limited real scenario
- **validated** — behavior has been exercised with explicit evidence for the claimed scope
- **experimental** — available for bring-up, but not support-promised
This repo should not treat **builds** or **enumerates** as equivalent to **validated**.
## Source Model
USB driver code lives in `recipes/core/base/source/drivers/usb/`, which is an upstream-managed git
working copy. Red Bear carries all USB modifications through `local/patches/base/redox.patch`
(currently ~17000 lines across ~100 diff sections).
**Upstream state** — the unpatched source snapshot that `make fetch` produces — has significant
error handling gaps and several correctness bugs. Red Bear's patch layer fixes these, but the fixes
are only visible after patch application. This document describes the **Red Bear state** unless
explicitly noted.
## Current Repo State
### Summary
USB in Red Bear OS is **present and improving**.
The Red Bear USB stack consists of:
- a host-side xHCI controller daemon (`xhcid`) with Red Bear patches for error handling,
correctness, and robustness
- hub and HID class daemons with Red Bear patches
- a mass-storage BOT daemon with Red Bear patches
- native USB observability (`lsusb`, `usbctl`, `redbear-info`)
- a low-level userspace client API through `xhcid_interface`
- a hardware quirks system that applies USB device-specific workarounds at runtime
- four QEMU validation harnesses covering interrupt delivery, bounded device lifecycle hotplug,
full stack, and storage autospawn
- an in-guest scheme-tree checker (`redbear-usb-check`)
### Boot-input reality
For bare-metal boot resilience, the current USB stack is still incomplete.
External USB keyboard input is reliably available only when the keyboard is reached through the
`xHCI -> usbhubd/usbhidd -> inputd` path. This is an important distinction because modern bare
metal does not guarantee that an attached keyboard will land on the xHCI runtime path.
If a keyboard instead lands on:
- an EHCI-owned path
- a UHCI/OHCI companion path
- a firmware routing topology where low/full-speed devices do not reach the xHCI runtime path
then Red Bear may still detect controller ownership and connected ports, but it does not yet have a
complete runtime host path that reaches the existing HID class daemons.
This means Red Bear cannot yet honestly claim that an external USB keyboard is a reliable universal
boot fallback on bare metal.
### Red Bear xHCI Patch Layer
The Red Bear patch at `local/patches/base/redox.patch` carries these changes over the upstream
source:
**Error handling (88 fixes):**
- `unwrap()` on mutex locks replaced with `unwrap_or_else(|e| e.into_inner())` across `scheme.rs`,
`mod.rs`, `irq_reactor.rs`, and `ring.rs` — mutex poisoning no longer panics any hot-path lock
- `expect()` calls replaced with proper `Result` propagation, logged errors, or fallible helpers
- `trb_phys_ptr()` returns `Result<u64>` instead of panicking on invalid TRB pointers
- `panic!()` in `irq_reactor.rs` replaced with error returns where possible
- `device_enumerator.rs` panics replaced with error logging and graceful handling
**Correctness fixes:**
- **ERDP split**: upstream has a single `erdp()` method that conflates the software dequeue pointer
with the hardware register read. Red Bear splits this into `dequeue_ptr()` (software ring
position) and `erdp(&RuntimeRegs)` (actual hardware register read, per XHCI spec §4.9.3)
- **endp_direction off-by-one**: upstream uses `endp_num as usize` to index into the endpoints Vec,
but USB endpoints are 1-indexed. Red Bear uses `endp_num.checked_sub(1)` for correct 0-based
indexing
- **cfg_idx ordering**: upstream sets `port_state.cfg_idx` before validating the config descriptor.
Red Bear moves the assignment after validation succeeds
- **CLEAR_FEATURE endpoint address**: upstream uses the driver-internal endpoint index for
`CLEAR_FEATURE(ENDPOINT_HALT)`. Red Bear uses the USB endpoint address from the descriptor
(`bEndpointAddress`)
- **usbhubd status_change_buf**: upstream has off-by-one bitmap sizing and bit-position parsing.
Red Bear sizes the buffer correctly and computes port bit positions explicitly
**Functional additions:**
- **Event ring growth**: upstream has a stub `grow_event_ring()` that logs "TODO". Red Bear
implements real ring doubling (up to 4096 cap), new DMA allocation, dequeue pointer preservation,
ERDP/ERSTBA register updates, and DCS bit handling
- **BOS/SuperSpeed descriptor fetching**: `fetch_bos_desc()` called during device enumeration with
bounds-checked slicing and graceful USB 2 fallback
- **Speed detection for hub child devices**: `UsbSpeed` enum with `from_v2_port_status()` /
`from_v3_port_status()` mapping, passed via `attach_with_speed()` from `usbhubd`
- **Interrupt-driven operation restored**: `get_int_method()` replaces hardwired polling; MSI/MSI-X/
INTx paths re-enabled
- **Hub interrupt EP1**: `usbhubd` reads status change via interrupt endpoint instead of polling
- **USB 3 hub endpoint configuration**: `SET_INTERFACE` always sent; stall on `(0,0)` tolerated
- **Hub change bit clearing**: `clear_port_changes` sends all relevant `ClearFeature` requests
including USB3-specific features after every port status read
- **HID error handling**: `usbhidd` uses `anyhow::Result` with context, no panics in report loop
- **BOT transport robustness**: `usbscsid` replaces all `panic!()` with stall recovery and error
returns; iterative bounded CSW read loop instead of unbounded recursion; correct early_residue
computation
### Remaining Limitations
Even with the Red Bear patch applied:
- HID is now wired through named producers (`ps2-keyboard`, `ps2-mouse`, `usb-{port}-if{n}`); named producers always fan out to both per-device consumers and the legacy VT consumer path; the `InputProducer` wrapper falls back to an anonymous legacy `ProducerHandle` if the named path is unavailable (e.g., older `inputd` build)
- external USB keyboard fallback is not guaranteed on bare metal unless the keyboard reaches the
xHCI runtime path
- EHCI/UHCI/OHCI are not yet full runtime host-controller implementations
- Any remaining USB composite/device-model issues now sit above the bounded helper fixes already
landed for active alternates, endpoint direction, real interface/alternate hub configuration, and
SSP-aware endpoint-context calculations.
- ~57 TODO/FIXME comments remain across xHCI driver files
- usbhubd: interrupt-driven change detection implemented; 1-second polling retained as fallback
- usbscsid: `ReadCapacity16` now implemented with automatic fallback from `ReadCapacity10`
- `usbhidd` keyboard LED sync is only a bounded per-device best-effort path, not a system-global
lock-state authority
- No real hardware USB validation — all testing is QEMU-only
- No hot-plug stress testing
- No USB storage data I/O validation (autospawn checked, but no read/write tested)
- USB quirk table expanded from 8 to 146 entries mined from Linux 7.0
- USB quirk flags expanded from 9 to 22 (13 new flags from Linux 7.0 including NO_BOS, HUB_SLOW_RESET)
- Terminus hub (0x1A40:0x0101) corrected from `no_lpm` to `hub_slow_reset` per Linux semantics
### Current Status Matrix
| Area | State | Notes |
|---|---|---|
| Host mode | **builds / QEMU-validated** | Real host-side stack, interrupt-driven, QEMU-validated only |
| xHCI controller | **builds / QEMU-validated** | Red Bear patch: 88 error handling fixes, ERDP split, endp_direction fix, cfg_idx fix, real grow_event_ring, mutex poison recovery on all hot-path locks; no real hardware validation yet |
| EHCI/UHCI/OHCI | **builds / enumerates** | Ownership, port handling, and logging exist, but they are not yet full runtime enumeration paths |
| Hub handling | **builds / good quality** | `usbhubd`: all `expect()` eliminated, interrupt-driven change detection with polling fallback, graceful per-port error handling |
| HID | **builds / QEMU-validated in narrow path** | `usbhidd` handles keyboard/mouse/button/scroll via named producer path (`usb-{port}-if{n}`) with legacy fallback, no panics in report loop; keyboard LED sync exists as a bounded per-device best-effort path |
| Mass storage | **builds / good quality** | `usbscsid`: typed `ScsiError`, fallible parsing, `ReadCapacity16` for >2TB, stall recovery, resilient event loop |
| Native tooling | **builds / enumerates** | `lsusb`, `usbctl`, `redbear-info`, `redbear-usb-check` provide observability |
| Low-level userspace API | **builds** | `xhcid_interface` with `UsbSpeed` enum, `attach_with_speed()` |
| Validation | **builds / QEMU-only** | 4 harness scripts + in-guest checker; no real hardware validation scripts |
| Hardware quirks | **builds** | `redox-driver-sys` quirk tables with 146 compiled-in USB quirk entries (mined from Linux 7.0) + 22 USB quirk flags; runtime TOML loading for `/etc/quirks.d/` |
## Code Quality by Daemon
### xHCI driver (`xhcid/src/xhci/`)
**Upstream state** — 91 `unwrap()`, 25 `expect()`, 7 `panic!()`, ~57 TODO/FIXME across ~6000
lines of Rust.
**Red Bear state** — mutex poisoning eliminated on all hot-path locks; `trb_phys_ptr()` returns
`Result`; critical correctness bugs fixed; ~57 TODOs remain as design notes.
Key files and their sizes:
| File | Lines (approx) | Upstream Issues | Red Bear Fix Status |
|---|---|---|---|
| `scheme.rs` | ~2800 | 36 unwrap, 14 expect, 2 panic | All unwrap/expect on hot paths fixed; endp_direction, cfg_idx, CLEAR_FEATURE fixed |
| `mod.rs` | ~1500 | 38 unwrap, 5 expect | All mutex-related unwrap fixed |
| `irq_reactor.rs` | ~750 | 17 unwrap, 6 expect, 4 panic | All fixed; grow_event_ring fully implemented |
| `ring.rs` | ~200 | 1 panic (trb_phys_ptr) | Returns Result instead of panicking |
| `event.rs` | ~60 | 1 TODO | ERDP split into dequeue_ptr() + erdp(&RuntimeRegs) |
### Class drivers
| Daemon | Lines | Error Handling Quality | Remaining unwrap/expect | Key Gaps |
|---|---|---|---|---|
| `usbhubd` | ~430 | **Good**`Result<(), Box<dyn Error>>`, all `expect()` eliminated, interrupt-driven change detection | 0 | 1-second polling fallback if interrupt EP unavailable |
| `usbhidd` | 576 | **Good**`anyhow::Result` with context, no panics in report loop; `expect()` remains in arg parsing and descriptor setup (pre-existing) | 7 `expect()` + 1 `assert_eq!` (pre-existing, arg parsing/descriptor setup) | Hardcoded 1ms poll rate; mouse ×2 multiplier workaround; X scroll missing |
| `usbscsid` | ~1800 | **Good**`ScsiError` typed errors, fallible `parse_bytes`/`parse_mut_bytes` helpers, resilient event loop, `ReadCapacity16` | 0 | — |
## Validation Infrastructure
### Host-side QEMU harnesses
| Script | What it tests | Limitations |
|---|---|---|
| `test-xhci-device-lifecycle-qemu.sh --check` | Bounded xHCI hotplug lifecycle proof: runtime attach → configure → driver spawn → detach for HID and storage devices | QEMU-only; monitor-driven hotplug; not a broad hardware stress test |
| `test-usb-qemu.sh --check` | Full stack: xHCI interrupt mode, HID spawn, SCSI spawn, bounded sector-0 readback, BOS processing, crash errors | QEMU-only; log-grep based; no guest-side write proof |
| `test-usb-storage-qemu.sh` | USB mass storage autospawn + sector-0 readback + crash patterns | No guest-side write proof yet; no multi-LUN; no UAS |
| `test-xhci-irq-qemu.sh --check` | xHCI interrupt delivery mode (MSI/MSI-X/INTx) | No devices attached during check; single log grep |
### In-guest tooling
| Tool | What it does | Installation |
|---|---|---|
| `lsusb` | Walks `/scheme/usb.*`, reads descriptors, shows vendor:product + quirks | Installed via `redbear-hwutils` recipe |
| `redbear-usb-check` | Scheme tree walk with pass/fail exit code | Installed via `redbear-hwutils` recipe |
| `redbear-info --verbose` | Reports USB controller count and integration status | Installed via `redbear-info` recipe |
### Runbook
`local/docs/USB-VALIDATION-RUNBOOK.md` documents two operator paths:
- **Path A**: Host-side QEMU validation via `test-usb-qemu.sh --check`
- **Path B**: Interactive guest validation via `redbear-usb-check`
### What is NOT validated
- Real hardware USB controllers (QEMU `qemu-xhci` only)
- Hub topology (direct-attached devices only)
- USB 3 SuperSpeed data paths
- Isochronous or streaming transfers
- Hot-plug stress testing
- USB storage data I/O (read/write to block device)
- USB device mode / OTG / USB-C
## Implementation Plan
### Repo-fit note
Some implementation targets live in upstream-managed trees such as
`recipes/core/base/source/...`. In Red Bear, work against those paths is carried through the
appropriate patch carrier under `local/patches/` until intentionally upstreamed. This plan names
the technical target path, not a recommendation to bypass Red Bear's overlay/patch discipline.
### Phase U0 — Support Model and Scope Freeze
**Goal**: Make USB claims honest and reproducible before widening implementation scope.
**What to do**:
- Define USB support labels per profile: `builds`, `enumerates`, `usable`, `validated`
- Declare Red Bear's near-term USB scope explicitly as **host-first**
- Record that device mode / USB-C / PD / alt-modes / USB4 are later decision points, not implied
current scope
- Add USB status guidance to the profile/support-language discipline used elsewhere in Red Bear
**Where**: `local/docs/PROFILE-MATRIX.md`, `docs/07-RED-BEAR-OS-IMPLEMENTATION-PLAN.md`, this
document.
**Exit criteria**: USB claims are tied to a named profile or package-group slice; no doc implies
broad USB support without a matching validation label.
---
### Phase U1 — xHCI Controller Baseline
**Status**: Substantially complete in the Red Bear patch layer. Runtime validation still QEMU-only.
**Completed (Red Bear patch)**:
- BOS/SuperSpeed descriptor fetching wired up
- Speed detection for hub child devices with `UsbSpeed` enum
- Interrupt-driven operation restored (MSI/MSI-X/INTx)
- Event ring growth fully implemented (ring doubling, DMA, ERDP/ERSTBA, DCS)
- 88 error handling fixes across scheme.rs, mod.rs, irq_reactor.rs, ring.rs
- ERDP split into `dequeue_ptr()` + `erdp(&RuntimeRegs)`
- `trb_phys_ptr()` returns `Result<u64>`
- Mutex poisoning recovery on all hot-path locks
**Remaining**:
- Validate one controller family on real hardware (requires hardware)
- Tighten controller-state correctness under sustained load (requires hardware)
- Address remaining ~57 TODO/FIXME design notes (ongoing, not blocking)
- SuperSpeedPlus differentiation via Extended Port Status (xHCI spec extension)
- TTT (Think Time) propagation from parent hub descriptor into Slot Context
- Event ring growth: copy pending TRBs from old ring to avoid losing in-flight events under sustained load
**Where**: `recipes/core/base/source/drivers/usb/xhcid/` (via `local/patches/base/redox.patch`)
**Exit criteria**: one target controller family repeatedly boots without `xhcid` panic on real
hardware; controller enumerates attached devices reliably across repeated boot cycles.
---
### Phase U2 — Topology, Configuration, and Hotplug Correctness
**Status**: Partially complete.
**Completed (Red Bear patch)**:
- USB 3 hub endpoint configuration stall handled
- `endp_direction` off-by-one fixed (`checked_sub(1)`)
- `cfg_idx` assigned after validation
- xHCI lifecycle gating prevents new I/O from entering while a port is detaching
- `attach_device()` no longer leaves a published partially-enumerated `PortState` on attach failure
- `detach_device()` now waits for in-flight lifecycle operations before removing the port state
- `configure_endpoints_once()` is transactional: endpoint state is staged locally, input-context
mutations are snapshotted, and rollback is attempted if `CONFIGURE_ENDPOINT` or
`SET_CONFIGURATION` fails
- `CLEAR_FEATURE` uses correct USB endpoint address from descriptor
- `usbhubd` status_change_buf sizing and bitmap parsing fixed
- Hub interrupt EP1 status change detection replacing polling
- `usbhubd` error handling improved — all ~22 `expect()` eliminated, `Result` return type, graceful per-port failure handling
- `usbhubd` interrupt-driven change detection — reads hub interrupt IN endpoint for status change bitmap; falls back to 1-second polling if endpoint unavailable; initial full scan preserved at startup
**Remaining**:
- Validate repeated attach/detach/reset behavior under stress (requires real hardware)
**Completed (Red Bear patch, this session)**:
- `configure_endpoints_once()` now filters endpoints by specific interface+alternate when
`req.interface_desc` is set, enabling composite-device drivers to claim individual interfaces
without programming endpoints from other interfaces
- When `interface_desc` is `None` (initial device setup), endpoints are collected from all
default-alternate (alt 0) interfaces, preserving backward compatibility
- `PortState.active_ifaces: BTreeMap<u8, u8>` tracks which interface numbers are active and
which alternate setting each is using
- `set_interface()` now updates `active_ifaces` after a successful SET_INTERFACE control request
- `spawn_drivers()` logs non-default alternates at debug level instead of warning, documenting
that non-default alternates are selected by drivers via SET_INTERFACE rather than auto-spawn
- Initial configuration populates `active_ifaces` with all default-alternate interfaces
**Where**: `recipes/core/base/source/drivers/usb/usbhubd/`, `xhcid/src/xhci/scheme.rs`
**Exit criteria**: repeated hub and hotplug scenarios complete without stale topology state; at
least one composite device configures correctly beyond the simplest path.
---
### Phase U3 — HID Modernization
**Status**: Partially complete.
**Completed (Red Bear patch)**:
- `usbhidd` error handling improved — `anyhow::Result` with context, no panics in report loop; `expect()`/`assert_eq!` remain in arg parsing and descriptor setup (pre-existing)
- Display write failures logged as warnings instead of panicking
- `inputd` scheme enhancement: named producers (`/scheme/input/producer/{name}`), per-device
consumer streams (`/scheme/input/{device_name}`), hotplug event stream (`/scheme/input/events`),
root directory enumeration (static entries + dynamic device names)
- Named producer events fan out to both matching DeviceConsumers and the legacy VT consumer path
- Hotplug binary format: 16-byte header (kind, device_id, name_len, reserved) + UTF-8 name
- Device IDs allocated monotonically, never reused
- Public API: `NamedProducerHandle`, `DeviceConsumerHandle`, `HotplugHandle`, `InputDeviceLister`,
`InputProducer` (named-first, legacy-fallback convenience wrapper)
- All legacy paths, event payloads, VT behavior, and display/control behavior preserved unchanged
- `ps2d` migrated: two `InputProducer` instances (`ps2-keyboard`, `ps2-mouse`), keyboard events
route to `keyboard_input`, mouse events to `mouse_input`, named-first with legacy fallback
- `usbhidd` migrated: one `InputProducer` per interface instance (`usb-{port}-if{n}`), named-first
with legacy fallback
**Remaining** (requires downstream consumer/driver migration, not inputd scheme changes):
- Migrate `i2c-hidd` to named producers (still uses legacy `ProducerHandle`)
- Expose hotplug add/remove behavior to downstream consumers via `evdevd` migration
**Where**: `recipes/core/base/source/drivers/input/usbhidd/`, `inputd/`,
`local/docs/INPUT-SCHEME-ENHANCEMENT.md`
**Exit criteria**: two independent USB HID devices appear as separate input sources; hot-unplug and
replug do not collapse all USB HID into one anonymous stream.
---
### Phase U4 — Storage, Userspace API, and Class Expansion
**Status**: Storage quality improved; userspace API story still low-level.
**Completed (Red Bear patch)**:
- `usbscsid` BOT transport: all `panic!()` replaced with stall recovery and error returns
- Correct endpoint addresses for `CLEAR_FEATURE` and `get_max_lun`
- Iterative bounded CSW read loop
- SCSI block descriptor parsing with bounds checks
- `usbscsid` SCSI layer: `plain::from_bytes().unwrap()` replaced with typed `ScsiError` and fallible `parse_bytes`/`parse_mut_bytes` helpers
- `usbscsid` main.rs: fallible `run()` helper, event loop continues on individual failures
- `ReadCapacity16` implemented with automatic fallback when `ReadCapacity10` returns max LBA (0xFFFFFFFF)
- `usbscsid` now issues bounded `SYNCHRONIZE CACHE(10/16)` commands when the runtime storage quirk
set includes `needs_sync_cache`, using Linux `sd.c` sync-cache behavior as a donor reference for
command selection and tolerant error handling.
**Remaining** (all require hardware or design decisions):
- Runtime I/O validation: prove stall recovery works under real device I/O (requires hardware)
- Decide whether BOT-only is sufficient short-term or UAS is needed (design decision)
- Bring `libusb` to a runtime-tested state or replace with Red Bear-native API (large scope, deferred)
- Choose the next USB class families explicitly (design decision)
**Suggested class priority**: storage baseline → generic userspace API → USB networking or
Bluetooth dongle → audio/video only after controller maturity justifies it
**Where**: `recipes/core/base/source/drivers/storage/usbscsid/`, `recipes/wip/libs/other/libusb/`,
`local/recipes/system/redbear-hwutils/`
**Exit criteria**: one USB storage path validated on target profile; one coherent userspace USB API
story documented and works in practice; next supported class families named explicitly.
---
### Phase U5 — Modern USB Scope Decision Gate
**Goal**: Decide whether Red Bear remains a host-only USB system or grows toward a modern USB
platform.
**What to decide**:
- Host-only versus device mode / gadget support
- Whether OTG / dual-role matters for target hardware
- Whether USB-C / PD / alt-mode policy belongs in Red Bear's target platform story
- Whether USB4 / Thunderbolt-class behavior is in scope or explicitly excluded
**Why this phase exists**: These are architectural choices, not small driver add-ons. A
future-proof stack cannot leave them implicit forever.
**Exit criteria**: a written architecture decision exists for included and excluded modern USB
scope.
---
### Phase U6 — Validation Slices and Support Claims
**Status**: Partially complete.
**Completed**:
- `test-usb-qemu.sh` — full USB stack validation harness (6 checks)
- `test-usb-storage-qemu.sh` — USB mass storage autospawn check
- `test-xhci-irq-qemu.sh` — xHCI interrupt delivery mode check
- `test-xhci-device-lifecycle-qemu.sh` — bounded xHCI attach/configure/detach hotplug proof
- `USB-VALIDATION-RUNBOOK.md` — operator documentation with Paths A and B
- `redbear-usb-check` — in-guest scheme-tree checker (now installed in image)
- `lsusb` — full USB scheme walk with descriptor parsing and quirks integration
- `redbear-info` — passive USB controller reporting
**Remaining** (all require hardware):
- Add hardware-matrix coverage for target controllers and class families
- Add USB storage data I/O validation (read/write to block device)
- Add repeated hardware hot-plug stress testing beyond the bounded QEMU lifecycle slice
**Exit criteria**: at least one profile can honestly claim a validated USB baseline for named
controller/class scope; USB support language in docs matches real test evidence.
## Support-Language Guidance
Until U1 through U3 are substantially complete, Red Bear should avoid broad phrases such as:
- "USB support works"
- "USB storage is supported"
- "USB is complete"
Prefer language such as:
- "xHCI host support is present but experimental"
- "USB enumeration and HID-adjacent host paths exist in-tree"
- "USB support remains controller-variable"
- "USB storage support exists in-tree with improved error handling, but is not yet a broad hardware
support claim"
- "USB error handling and correctness carry significant Red Bear patches over upstream; see
`local/patches/base/redox.patch` for details"
## Linux Kernel USB Data Mining
### linux-kpi Scope Clarification
The `linux-kpi` compatibility layer (`local/recipes/drivers/linux-kpi/`) is used **exclusively for
GPU and Wi-Fi drivers** — it provides Linux kernel API headers and Rust FFI implementations for
porting Linux C drivers in those domains to Redox. It does **not** cover USB and contains no USB
headers, USB device ID tables, or USB driver implementations.
The linux-kpi header inventory (`src/c_headers/`) covers: PCI, DMA, IRQ, firmware, networking
(netdevice, skbuff, ieee80211, nl80211, cfg80211, mac80211), DRM, workqueue, timer, wait, sync,
memory, and related kernel infrastructure — but zero USB content. This is documented globally in
`AGENTS.md` and `local/AGENTS.md`.
### Linux 7.0 Source Availability
Linux kernel 7.0 (stable, released 2026-04-13) is extracted at
`build/linux-kernel-cache/linux-7.0/` for USB data mining purposes. This is a build cache, not a
tracked source tree — it can be re-fetched from `cdn.kernel.org` at any time.
```bash
# Re-fetch if needed:
curl -L -o build/linux-kernel-cache/linux-7.0.tar.xz \
"https://cdn.kernel.org/pub/linux/kernel/v7.x/linux-7.0.tar.xz"
tar xf build/linux-kernel-cache/linux-7.0.tar.xz -C build/linux-kernel-cache/
```
### Mining Inventory — What Linux 7.0 Contains
| Data Source | Linux Path | Entries | Lines | Relevance |
|---|---|---|---|---|
| USB device quirks | `drivers/usb/core/quirks.c` | 64 device + 5 AMD-resume + 4 endpoint-ignore | 800 | Directly feed our quirk tables |
| USB quirk flag definitions | `include/linux/usb/quirks.h` | 19 flags | 84 | We have 9 of 19; 10 missing |
| USB storage unusual devices | `drivers/usb/storage/unusual_devs.h` | 323 entries | 2513 | Mass storage device workarounds |
| USB hub driver | `drivers/usb/core/hub.c` | — | 6567 | TT handling, hub descriptor parsing |
| xHCI host driver | `drivers/usb/host/xhci*.c/h` | ~15 files | ~30000 | Controller quirks, TRB handling |
| SCSI disk driver | `drivers/scsi/sd.c` | — | 4467 | SCSI command support tables |
| USB core headers | `include/linux/usb/*.h` | 75 headers | — | ch9.h (descriptors), hcd.h, storage.h, uas.h |
### Extraction Tool
`local/scripts/extract-linux-quirks.py` parses Linux kernel source and generates Red Bear TOML
quirk entries. Handles three source formats:
- `drivers/usb/core/quirks.c``[[usb_quirk]]` TOML entries (146 entries from Linux 7.0)
- `drivers/usb/storage/unusual_devs.h``[[usb_storage_quirk]]` TOML entries (214 entries from Linux 7.0)
- `drivers/pci/quirks.c``[[pci_quirk]]` TOML entries (explicit high-confidence handler-body mappings only, requires review)
USB quirk extraction is direct and does not require review. PCI quirk extraction now emits only
explicit high-confidence handler-body mappings and still requires manual review before committing.
The extraction script needs extension to also handle `drivers/usb/storage/unusual_devs.h` for mass
storage device entries (323 entries, different macro format `UNUSUAL_DEV`).
### Flag Gap Analysis
**Flags we have (22, fully aligned with Linux 7.0):** `NO_STRING_FETCH`, `RESET_DELAY`, `NO_USB3`,
`NO_SET_CONFIG`, `NO_SUSPEND`, `NEED_RESET`, `BAD_DESCRIPTOR`, `NO_LPM`, `NO_U1U2`,
`NO_SET_INTF`, `CONFIG_INTF_STRINGS`, `NO_RESET`, `HONOR_BNUMINTERFACES`, `DEVICE_QUALIFIER`,
`IGNORE_REMOTE_WAKEUP`, `DELAY_CTRL_MSG`, `HUB_SLOW_RESET`, `NO_BOS`,
`SHORT_SET_ADDR_TIMEOUT`, `FORCE_ONE_CONFIG`, `ENDPOINT_IGNORE`, `LINEAR_FRAME_BINTERVAL`
**All 19 Linux 7.0 USB_QUIRK flags are now covered.** The mapping table below documents the
correspondence for future reference.
| Linux Flag | Purpose | Impact | Mapping Notes |
|---|---|---|---|
| `USB_QUIRK_RESET_RESUME` | Device can't resume, needs reset instead | High — many devices | Roughly maps to our `NEED_RESET` |
| `USB_QUIRK_NO_SET_INTF` | Device can't handle SetInterface requests | Medium — composite devices | Our `NO_SET_CONFIG` targets SET_CONFIGURATION, not SET_INTERFACE |
| `USB_QUIRK_CONFIG_INTF_STRINGS` | Device can't handle config/interface strings | Low — enumeration robustness | New concept |
| `USB_QUIRK_RESET` | Device can't be reset at all | Medium — prevents crashes on morph devices | No equivalent |
| `USB_QUIRK_HONOR_BNUMINTERFACES` | Wrong interface count in descriptor | Medium — composite devices | New concept |
| `USB_QUIRK_DEVICE_QUALIFIER` | Device can't handle device_qualifier descriptor | Low — skip descriptor fetch | New concept |
| `USB_QUIRK_IGNORE_REMOTE_WAKEUP` | Device generates spurious wakeup | Low — power management | New concept |
| `USB_QUIRK_DELAY_CTRL_MSG` | Device needs pause after every control message | Medium — prevents timeouts | New concept |
| `USB_QUIRK_HUB_SLOW_RESET` | Hub needs extra delay after port reset | High — our Terminus hub entry (0x1A40:0x0101) currently has `no_lpm` but Linux marks it `HUB_SLOW_RESET` | New concept |
| `USB_QUIRK_NO_BOS` | Skip BOS descriptor (hangs at SuperSpeedPlus) | High — we added BOS fetching, some devices hang | New concept |
| `USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT` | Short timeout for SET_ADDRESS | Low — controller-specific | New concept |
| `USB_QUIRK_FORCE_ONE_CONFIG` | Device claims zero configs, force to 1 | Low — edge case | New concept |
| `USB_QUIRK_ENDPOINT_IGNORE` | Device has endpoints that should be ignored | Medium — audio devices | New concept |
| `USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL` | bInterval is linear frames, not exponential | Low — interrupt endpoint timing | Related to our `BAD_DESCRIPTOR` |
Note: Some Linux flags overlap semantically with our existing flags. The exact mapping requires a
per-flag design decision — either extend existing flags with clarified semantics or add new parallel
flags.
### Duplicate Quirk Table Problem
`xhcid` carries its own copy of the USB quirk table at
`recipes/core/base/source/drivers/usb/xhcid/src/usb_quirks.rs`. The canonical table is in
`local/recipes/drivers/redox-driver-sys/source/src/quirks/usb_table.rs`.
Both tables now carry the expanded 22-flag set and synchronized entries. The xhcid copy contains a
representative subset of the most common entries (early-boot fallback when `/etc/quirks.d/` is not
yet mounted), while the full 146-entry table and TOML runtime loading serve as the complete
runtime source.
**Long-term resolution:** xhcid should import from redox-driver-sys directly rather than
maintaining a duplicate. Until then, both must be kept in sync when adding new entries.
### Prioritized Mining Targets
**Tier 1 — COMPLETED:**
1.**USB device quirk table expansion** — All 146 entries from Linux 7.0 `quirks.c` extracted
into `usb_table.rs` and `20-usb.toml`. Covers HP, Microsoft, Logitech, Lenovo, SanDisk,
Corsair, Realtek, NVIDIA, ASUS, Dell, Elan, Genesys, Razer, and others.
2.**`USB_QUIRK_NO_BOS` flag** — Added. 4 devices that hang at SuperSpeedPlus BOS fetch are
now flagged: ASUS TUF 4K PRO (0x0B05:0x1AB9), Avermedia GC553G2 (0x07CA:0x2553), Elgato 4K X
(0x0FD9:0x009B), UGREEN 35871 (0x2B89:0x5871), ezcap401 (0x32ED:0x0401).
3.**`USB_QUIRK_HUB_SLOW_RESET` flag** — Added. Terminus hub (0x1A40:0x0101) corrected from
`no_lpm` to `hub_slow_reset`.
4.**Flag gap closed** — All 19 Linux 7.0 USB_QUIRK flags now mapped. 13 new flags added:
`NO_SET_INTF`, `CONFIG_INTF_STRINGS`, `NO_RESET`, `HONOR_BNUMINTERFACES`,
`DEVICE_QUALIFIER`, `IGNORE_REMOTE_WAKEUP`, `DELAY_CTRL_MSG`, `HUB_SLOW_RESET`, `NO_BOS`,
`SHORT_SET_ADDR_TIMEOUT`, `FORCE_ONE_CONFIG`, `ENDPOINT_IGNORE`, `LINEAR_FRAME_BINTERVAL`.
5.**Duplicate quirk tables synchronized** — Both `usb_table.rs` (redox-driver-sys) and
`usb_quirks.rs` (xhcid) now carry the expanded flag set and synchronized entries.
6.**USB storage unusual_devs.h** — 214 entries extracted from Linux 7.0 into
`local/recipes/system/redbear-quirks/source/quirks.d/30-storage.toml` (1716 lines). Extraction
script extended to handle `UNUSUAL_DEV` macro format. Most common flags: `ignore_residue` (46),
`fix_capacity` (34), `single_lun` (28), `max_sectors_64` (22), `fix_inquiry` (22). Includes
`initial_read10` entries for Feiya SD/SDHC reader and Corsair Padlock v2.
7.**usbscsid storage quirk integration** — Storage quirks are now active at runtime.
`usbscsid/src/quirks.rs` reads `[[usb_storage_quirk]]` entries from `/etc/quirks.d/*.toml`
and applies them to the BOT transport and SCSI command layers. Active behavioral flags:
- `IGNORE_RESIDUE`: suppresses CSW residue in BOT `send_command`
- `FIX_CAPACITY`: adjusts block count from READ CAPACITY(10) by -1
- `SINGLE_LUN`: enforces LUN=0 in CBW (future-proof for multi-LUN support)
- `MAX_SECTORS_64`: clamps transfer length to 64 sectors in SCSI read/write
- `INITIAL_READ10`: uses READ(10)/WRITE(10) instead of READ(16)/WRITE(16)
Vendor/product IDs are extracted from `DevDesc` at daemon startup. A compiled-in fallback
table covers 5 common devices for early-boot correctness.
8.**xhcid USB device quirk consumption** — xhcid now stores per-device `UsbQuirkFlags` in
`PortState` and applies them during enumeration and runtime requests. Active behavioral flags:
- `NO_STRING_FETCH`: skips manufacturer/product/serial/configuration string fetches
- `BAD_DESCRIPTOR`: tolerates language/string descriptor fetch failures and continues interface parsing when malformed endpoint descriptors appear
- `RESET_DELAY`: extends first-touch post-reset settle time via early `PortId`-based lookup
- `HUB_SLOW_RESET`: uses a longer hub-oriented reset settle time via early `PortId`-based lookup
- `NO_BOS`: skips BOS descriptor fetch and leaves superspeed capability detection false
- `SHORT_SET_ADDR_TIMEOUT`: uses a shorter `Address Device` command timeout via early `PortId`-based lookup
- `FORCE_ONE_CONFIG`: limits enumeration to configuration index 0 (configuration value 1 path)
- `HONOR_BNUMINTERFACES`: stops interface parsing at `bNumInterfaces`
- `DELAY_CTRL_MSG`: inserts a short post-control-transfer delay
- `NO_SET_CONFIG`: skips `SET_CONFIGURATION`
- `NO_SET_INTF`: skips `SET_INTERFACE`
- `NEED_RESET`: issues xHC `Reset Device` automatically after transfer failures
The early-enumeration timing path now uses optional TOML `port = "<root>[.<route>...]"`
selectors in `[[usb_quirk]]` entries for quirks that must act before vendor/product are known.
9.**xhcid suspend/resume API skeleton** — xhcid now exposes explicit `port<n>/suspend` and
`port<n>/resume` endpoints plus matching `XhciClientHandle::{suspend_device,resume_device}`
helpers. `PortState` now tracks `PortPmState::{Active,Suspended}` and xhcid enforces
`NO_SUSPEND` by rejecting suspend with `EOPNOTSUPP`. While suspended, control/data/reset
activity returns `EBUSY`.
10.**usbhubd suspend coordination slice**`usbhubd` now tracks downstream child suspend
state and mirrors USB 2 hub-port suspend status into child xhcid devices via
`suspend_device()` / `resume_device()`. This gives us the first real cross-layer coordination
path for hub-attached devices without inventing a separate PM daemon. Remaining gap: suspend
policy/origination is still external, and USB 3 link-state-driven coordination is not yet
implemented.
**Tier 2 — Medium-term (improves robustness):**
5. **TT handling from hub.c** — Linux's hub driver reads `wHubDelay` and `bNbrPorts` from hub
descriptors to populate TT think time and MTT capability. Our xHCI driver hardcodes `ttt = 0`
and `mtt = false`. Mining the hub descriptor parsing logic from `hub.c` would replace these
stubs with correct values.
6. **xHCI controller quirks from xhci-pci.c** — Linux has per-vendor controller workarounds
(Intel PCH, AMD, Etron, Fresco, VIA). Our driver has no controller-specific paths. Mining the
quirk table and applying it through our existing PCI quirk system would add real-hardware
robustness.
7. **SCSI command selection from sd.c** — READ(10)/WRITE(10) support is now implemented
(triggered by `INITIAL_READ10` quirk flag). Remaining: REPORT LUNS for multi-LUN devices,
SYNCHRONIZE CACHE (triggered by `NEEDS_SYNC_CACHE` flag), and START STOP UNIT for power
management.
**Tier 3 — Future (enables new device classes):**
8. **USB class/subclass/protocol tables from ch9.h** — Complete class code definitions for device
matching in `drivers.toml`.
9. **USB endpoint descriptor parsing from message.c** — Extended endpoint type mapping for streams
and isochronous support.
### Mining into the Build
The Linux kernel source at `build/linux-kernel-cache/` is a build cache, not a tracked dependency.
Mined data must be materialized into durable locations:
| Mined Data | Target Location | Format |
|---|---|---|
| USB device quirks | `local/recipes/system/redbear-quirks/source/quirks.d/20-usb.toml` | TOML (146 entries ✅) |
| USB compiled-in quirks | `local/recipes/drivers/redox-driver-sys/source/src/quirks/usb_table.rs` | Rust (146 entries ✅) |
| PCI controller quirks | `local/recipes/system/redbear-quirks/source/quirks.d/10-pci.toml` | TOML |
| Storage device flags | `local/recipes/system/redbear-quirks/source/quirks.d/30-storage.toml` | TOML (214 entries ✅, active at runtime ✅) |
| Flag definitions | `local/recipes/drivers/redox-driver-sys/source/src/quirks/mod.rs` | Rust bitflags (22 USB flags ✅) |
The extraction script at `local/scripts/extract-linux-quirks.py` should be extended to also handle
`drivers/usb/storage/unusual_devs.h` for mass storage device entries.
## Summary
USB in Red Bear today is not missing. It is a real userspace host-side subsystem with meaningful
enumeration, runtime observability, hub/HID infrastructure, and a low-level userspace API.
The Red Bear patch layer carries substantial error handling and correctness improvements over the
upstream source: 88 error handling fixes (mutex poisoning recovery, expect/panic replacement, Result
conversions), multiple correctness bug fixes, real event ring growth,
class driver error handling improvements (all three USB class daemons now use `Result` types with
zero `unwrap()`/`expect()` panics), interrupt-driven hub change detection, `ReadCapacity16`
for large disk support, and a USB quirk table expanded from 8 to 146 entries with 22 quirk flags
mined from Linux 7.0.
Recent bounded maturity progress:
- `xhcid` now tracks active alternate settings per interface in `PortState` and resolves endpoint
descriptors through that active-alternate map instead of flattening all interface descriptors
indiscriminately.
- Direct unit coverage now exists for both default-alternate endpoint selection and
alternate-setting-aware endpoint remapping.
- `xhcid` now also preserves previously selected alternates on the same configuration and applies a
requested interface/alternate override before endpoint planning, so alternate-setting
reconfiguration no longer silently falls back to all-zero defaults.
- `xhcid` endpoint-direction lookup now also follows the active interface/alternate selection state
instead of reading from the first configuration/interface pair unconditionally.
- `xhcid` driver spawning now also follows the selected configuration and active alternate map
instead of hardcoding the first configuration and ignoring non-zero alternates.
- `xhcid` now keeps per-port lifecycle state so detach blocks new transfer/configure/suspend/resume
work, waits for in-flight operations to drain, and removes the published port state only after
slot disable succeeds.
- `xhcid` endpoint configuration is now transactional: software endpoint bookkeeping stays staged
until `CONFIGURE_ENDPOINT` and optional `SET_CONFIGURATION` succeed, and the input context is
restored with an explicit rollback attempt on failure.
- the xHCI IRQ reactor now replaces the old `TODO: grow event ring` stub with a preserve-and-grow
path that copies unread event TRBs into a larger event ring and reprograms ERST registers instead
of dropping pending events during `EventRingFull` recovery.
- `usbhubd` now derives USB 2 hub TT Think Time from the hub descriptor using the same bounded
Linux-compatible encoding and passes it through `ConfigureEndpointsReq`, and `xhcid` now writes
that value into the Slot Context TT information bits for hub devices.
- xHCI endpoint-context calculations are now protocol-speed-aware for SuperSpeedPlus, so interval
and ESIT-payload selection use the resolved port protocol speed instead of relying only on
endpoint companion presence.
All validation is QEMU-only. No real hardware USB testing exists.
The remaining gaps now fall into two categories:
**Broader architectural work (cross-cutting, not a small bounded USB-only fix):**
- Any remaining USB composite/device-model issues now belong to wider device-model/design cleanup
rather than one more isolated helper patch.
- HID producer modernization: per-device streams via named producers, hotplug add/remove (inputd redesign complete, ps2d and usbhidd migrated)
- Userspace USB API: `libusb` WIP, no coherent native story
> **See also:** `local/docs/boot-logs/REDBEAR-MINI-BOOT-PS2D-INPUTD-LOG-FIX.md`
> for the 2026-06-30 fix that made `usbhidd` (and its `ps2d` sibling)
> visible in the boot log. With the fix, an operator can distinguish
> "usbhidd dead" (no `@usbhidd:` line during enumeration, OR
> `@ps2d:<line> INFO ps2d: registered producer handle` missing) from
> "usbhidd alive but not enumerated by XHCI" (line present, no
> corresponding consumer event).
**Hardware-dependent or design decisions:**
- Real hardware validation: no controller tested outside QEMU
- Hot-plug stress testing beyond the new bounded QEMU lifecycle harness
- Storage write validation (bounded sector-0 readback proof now exists in QEMU via `test-usb-storage-qemu.sh`, but guest-side write verification to the USB-backed block device is still open)
- usbhubd 1-second polling fallback (only exercisable with real hub hardware)
- Modern USB scope decision: device mode / USB-C / PD
Software items are tracked in Phase U1 (xHCI internals) and Phase U2 (configuration/composite).
Architectural and hardware items are tracked in Phase U1 (controller hardware validation), Phase U2
(hub polling fallback), Phase U3 (HID), Phase U4 (storage/API), Phase U5 (modern USB scope
decision), and Phase U6 (validation).
Linux kernel USB data mining is documented in the "Linux Kernel USB Data Mining" section above.
Linux 7.0 source is available at `build/linux-kernel-cache/linux-7.0/` with 146 USB device quirks,
22 quirk flags (all 19 Linux USB_QUIRK flags covered), 214 active storage device quirks
consumed at runtime by usbscsid, and extensive xHCI/hub/SCSI reference code ready for extraction.
@@ -1,631 +0,0 @@
# Red Bear OS USB Implementation Plan — v2
> **Status:** Canonical. Replaces `archived/USB-IMPLEMENTATION-PLAN-v1-2026-04.md`.
> **Date:** 2026-07-07.
> **Supersession reason:** v1 (Apr 2026) overstated several capabilities relative to the
> then-current source. v2 re-audits every daemon against `local/sources/base/` HEAD,
> aligns with Redox 0.x USB HEAD (Jan 2025 Jun 2026), and reorganizes phases around the
> actual bare-metal correctness gaps. Validation labels are now source-anchored rather than
> patch-anchored.
>
> **Sibling docs:**
> `USB-VALIDATION-RUNBOOK.md` (operator path — restored from `archived/`); the older
> `archived/USB-BOOT-INPUT-PLAN.md` and `archived/XHCID-DEVICE-IMPROVEMENT-PLAN.md` are
> kept as historical reference but are not the planning authority.
---
## 0. Purpose and scope
This plan is the **single planning authority** for the USB subsystem in Red Bear OS. It
answers four questions honestly:
1. **What is built?** — every host controller, class driver, scheme, and observability tool
that actually exists in `local/sources/base/` and `local/recipes/drivers/`. Status is
derived from the current source tree, not from prior memory or from patch carriers.
2. **What was patched?** — every durable Red Bear modification, with file paths
(`local/patches/base/P*.patch` for the base module, dedicated local recipes or forks
otherwise).
3. **What is actually usable?** — explicitly distinguishes **builds**, **enumerates**,
**usable (narrow path)**, **validated (QEMU)**, **validated (real hardware)**, and
**experimental**. A label is only ever **validated** if the matching proof has run
on the matching artifact under the matching config.
4. **What is missing?** — the real bare-metal-blockers, the upstream-comparable gaps, the
architectural decisions still deferred, and the durability problems that this plan owns.
### Validation labels (canonical, do not redefine elsewhere)
- **builds** — code is in tree and compiles. Not a usability claim.
- **enumerates** — runtime surfaces can discover controllers, ports, descriptors.
- **usable (narrow path)** — one controller family / one class family works in a bounded,
repeatable scenario; other paths are likely broken.
- **validated (QEMU)** — a documented QEMU script passed on the matching recipe, config, and
commit. Reproducible on a Linux x86_64 host.
- **validated (real hardware)** — a named physical controller + class, with a captured log,
on real bare metal. This is what an end user can expect.
- **experimental** — present for bring-up but not in any support-promised path.
**Honesty rule.** `builds` is **not** equivalent to `usable`. `validated (QEMU)` is **not**
equivalent to `validated (real hardware)`. The plan never mixes these categories. Where
prior text conflated them, this v2 corrects.
### Plan structure
| Section | Authority | Updates cadence |
|---|---|---|
| §1 Source audit (controllers, class drivers, schemes, tooling) | ground truth | on every source-tree bump |
| §2 Patch carriers | every durable Red Bear diff | on every patch add/rebase |
| §3 Status matrix (one row per component) | single source of truth for "is it working" | on every status change |
| §4 Upstream divergence: what Redox 0.x USB HEAD has that we have not | required adoption list | on every upstream bump |
| §5 Bare-metal input correctness (boot-time USB keyboard) | the bare-metal failure modes | on every controller or class change |
| §6 Phase P0P5 (execution order) | who does what next | reviewed monthly |
| §7 Validation inventory and bounded proofs | the proof surfaces | on every script add/break |
| §8 Durability posture | local fork health, patch carriers, archival policy | on every base fork bump |
| §9 Support language | how the rest of Red Bear should describe USB | on every phase change |
---
## 1. Source audit — what is actually in the tree
Red Bear follows the upstream Redox model: **all USB logic is in userspace** (`drivers/usb/`
plus `local/recipes/drivers/usb-core/`). The kernel exposes `irq:`, `memory:`, `pcid:`,
`event:`, and `scheme:` surfaces that userspace USB daemons consume. There is no kernel USB
host stack, and the v1 phase plan's mention of "kernel MSI/MSI-X plumbing" was a reference
to that surface, not a kernel change.
### 1.1 Host controllers
| Daemon | Source | Lines | Reality today | Scheme registered |
|---|---|---|---|---|
| **xhcid** | `local/sources/base/drivers/usb/xhcid/` | ~6000 LoC across 25 files | Builds. Real ring/TRB/context/transfer engine. Polling in production (see §1.6). | `usb.<pci_name>_xhci` |
| **ehcid** | `local/recipes/drivers/ehcid/source/src/` | ~1550 LoC (3 files) | Builds. Real MMIO init, frame list, QH/TD, port reset. **No class-driver auto-spawn.** | `usb` |
| **uhcid** | `local/recipes/drivers/uhcid/source/src/main.rs` | 35 LoC | Builds. **Real stub.** Reads PCI BAR4, sleeps forever. No scheme. | — |
| **ohcid** | `local/recipes/drivers/ohcid/source/src/main.rs` | 35 LoC | Builds. **Real stub.** Identical pattern to uhcid. | — |
**Honesty corrections vs v1:**
- v1 said *"EHCI/UHCI/OHCI — ownership, port handling, and logging exist, but they are not
yet full runtime enumeration paths"*. For **uhcid** and **ohcid** this is too generous —
they are 35-line stubs that **only read PCI BAR4 and sleep**. They are not even
ownership-grade; the controller is never probed, no port state is published, no error
is logged past init.
- v1 said *"xHCI interrupt-driven operation restored"*. The current source at
`xhcid/src/main.rs:141` hardcodes polling:
```rust
let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
//TODO: Fix interrupts.
```
The `get_int_method` function exists, MSI-X/MSI/INTx branches are written, but the
function is bypassed at runtime. §4 captures the upstream commits that help finish this.
### 1.2 Class drivers
| Daemon | Source | LoC | Reality | Notes |
|---|---|---|---|---|
| **usbhubd** | `local/sources/base/drivers/usb/usbhubd/` | 249 | Builds; runs. | Polls port status (1s fallback retained from v1). |
| **usbhidd** | `local/sources/base/drivers/input/usbhidd/` | 576 | Builds; runs. | Named-producer input (`usb-{port}-if{n}`) + legacy VT fallback. |
| **usbscsid** | `local/sources/base/drivers/storage/usbscsid/` | ~1800 | Builds; runs. | BOT/SCSI, `ReadCapacity16`, 3 storage quirk flags active. |
| **usbctl** | `local/sources/base/drivers/usb/usbctl/` | 54 | Builds. CLI only. | Minimal — port/endpoint status query. |
| **ucsid** | `local/sources/base/drivers/usb/ucsid/` | 839 | Builds. | USB-C UCSI topology over ACPI + I2C; `/scheme/ucsi`. |
| **redbear-usbaudiod** | `local/recipes/system/redbear-usbaudiod/` | (small) | Builds; wired in `redbear-mini.toml`. | USB Audio Class 1. |
| **redbear-acmd** | `local/recipes/system/redbear-acmd/` | (small) | Builds; wired via `drivers.d/70-usb-class.toml`. | USB CDC ACM serial. |
| **redbear-ecmd** | `local/recipes/system/redbear-ecmd/` | (small) | Builds; wired via `drivers.d/70-usb-class.toml`. | USB CDC ECM/NCM ethernet. |
| **redbear-btusb** | `local/recipes/drivers/redbear-btusb/` | (small) | Builds. | Bluetooth USB transport — see BLUETOOTH-IMPLEMENTATION-PLAN. |
### 1.3 USB core library
| Crate | Source | Notes |
|---|---|---|
| **usb-core** | `local/recipes/drivers/usb-core/source/src/` | 6 files (lib.rs, dma.rs, scheme.rs, spawn.rs, transfer.rs, types.rs). Provides `UsbHostController` trait, `SetupPacket`, `PortStatus`, `TransferDirection`, `DmaBuffer`, descriptor parsers, `control_transfer`, `spawn_usb_driver`. Used by ehcid. **Currently not used by xhcid, uhcid, or ohcid.** |
This trait is the most important "infrastructure that already exists" item in this plan:
it is the natural target for uhcid/ohcid runtime enumeration (§6 P0-B2) and for any
future host port — including the xhcid → USB-core path that future xHCI cleanup will allow.
### 1.4 Tooling and observability
| Tool | Source | Reality |
|---|---|---|
| `lsusb` | `local/recipes/system/redbear-hwutils/source/src/bin/lsusb.rs` | Walks `/scheme/usb.*`, reads descriptors. |
| `redbear-usb-check` | `local/recipes/system/redbear-hwutils/source/src/bin/redbear-usb-check.rs` | In-guest scheme tree validator. |
| `redbear-usb-storage-check` | `local/recipes/system/redbear-hwutils/source/src/bin/redbear-usb-storage-check.rs` | Mass-storage round-trip validator. |
| `usbctl` | `local/sources/base/drivers/usb/usbctl/` | CLI for port/endpoint status. |
### 1.5 Patch carriers on `local/patches/base/`
The **durable** Red Bear USB modifications are carried as `local/patches/base/P*.patch`
files. These are applied atomically by the cookbook against the recipe source tree during
fetch+cook.
| Patch | Size | Purpose |
|---|---|---|
| `P1-xhcid-device-lifecycle.patch` | 2351 lines | Attach publication, transactional configure, bounded detach. |
| `P1-xhcid-port-pm-read-fix.patch` | 942 lines | Port PM state read. |
| `P1-xhcid-uevent-logging.patch` | 20 lines | Uevent audit trail. |
| `P2-usb-pm-and-drivers.patch` | 158 lines | USB PM (suspend/resume/quirk integration). |
| `P3-xhci-device-hardening.patch` | 1193 lines | Endp direction, cfg_idx ordering, interrupt-EP, hub feature clearing. |
| `P3-usbhidd-hardening.patch` | 725 lines | HID panic removal, named producer wiring. |
| `P4-initfs-usb-drm-services.patch` | 22 lines | DRM/USB service ordering in init. |
| (sibling) `P0-inputd-named-producers.patch`, `P0-inputd-per-device-consumers.patch`, `P2-inputd.patch`, `P3-inputd-keymap-bridge.patch` | (varying) | Input multiplexer wiring (ps2d + usbhidd consumers). |
**Durability rule:** any source-tree edit must be mirrored into one of these patches (or
into the local `base` fork's `submodule/base` branch on `RedBear-OS`) before the session
ends. This rule is also enforced by `local/AGENTS.md` and the cookbook's atomic patch
applier. **The current local fork at `local/sources/base/` is a single mega-commit** —
see §8 for the durability problem and remediation.
### 1.6 The interrupt-vs-polling contradiction
`local/sources/base/drivers/usb/xhcid/src/main.rs:101115` defines a complete
`get_int_method()` that returns MSI-X, MSI, INTx, or Polling based on PCI capabilities.
`main.rs:141` then **disables it**:
```rust
let (irq_file, interrupt_method) = (None, InterruptMethod::Polling); //get_int_method(&mut pcid_handle);
//TODO: Fix interrupts.
```
`xhci::start_irq_reactor(&hci, irq_file);` is called with `irq_file = None`, which makes
the reactor a **bounded polling loop that wakes every 1 second** (see the `mod.rs` reactor
fallback). This is functionally "polling in production."
The v1 plan called this "interrupt-driven operation restored" — that is incorrect relative
to the live code. v2 makes the gap explicit: **interrupts remain to be re-enabled** as P0-A1.
---
## 2. Status matrix (single source of truth)
Reorganized around the *honest* state of the tree.
| Component | State today | Maturity | Open correctness gap |
|---|---|---|---|
| Host mode (any controller) | builds / QEMU-validated narrow path | `usable (narrow path)` | see §4 |
| **xhcid** runtime | builds / polling / QEMU-validated | `usable (narrow path)` | interrupts hardcoded off; missing CSZ; missing real-hardware reset fix; missing USB 3.x packet-size + hub fixes |
| **ehcid** runtime | builds / no auto-spawn | `builds` | no class driver dispatch; no full bot pipeline through `/scheme/usb`; ~no peer review |
| **uhcid** runtime | builds / does nothing | `builds` | stub (35 lines) |
| **ohcid** runtime | builds / does nothing | `builds` | stub (35 lines) |
| Hub | builds / good quality | `usable (narrow path)` | polling fallback retained |
| HID class | builds / QEMU-validated narrow path | `usable (narrow path)` | named producer wiring complete; legacy VT fallback preserved |
| Mass storage | builds / QEMU-validated narrow path | `usable (narrow path)` | no guest-side write proof; no multi-LUN; no UAS |
| Audio class (USB) | builds | `builds` | not exercised in any proof |
| CDC ACM/ECM | builds | `builds` | not exercised in any proof |
| Bluetooth USB transport | builds (transport only) | `builds` | Bluetooth host path remains gappy (see BLUETOOTH-IMPLEMENTATION-PLAN) |
| USB-C / UCSI | builds | `builds` | topology surfaced, no PD/alt-mode |
| Native tooling (`lsusb`, `usbctl`, `redbear-info`, `redbear-usb-check`) | builds | `usable (narrow path)` | no bounded proof scheme validation |
| Quirk table (compiled + TOML) | builds | `validated (QEMU)` — quirk-bypass-only | 146 USB + 214 storage entries, 22 flags |
| Validation harnesses | 5 QEMU scripts | `validated (QEMU)` | no real-hardware matrix |
If a row says `builds`, **Red Bear does not promise that the component is reachable from a
typed-key-in-the-inputd-pipe to a shell prompt.** That promise is restricted to
`usable (narrow path)` and above, and only for the documented scenario.
---
## 3. Upstream divergence — what Redox 0.x USB HEAD has that Red Bear does not
This section is required reading before any USB change. It is the input to every phase in
§6. The Redox merge window for USB change runs roughly Jan 2025 Jun 2026 with two
concentrated bursts (March 2025, SepOct 2025). Red Bear's fork is currently pinned at the
v1 baseline (0.1.0 base snapshot).
### 3.1 Three high-priority upstream commits Red Bear has not adopted
| Upstream commit | Why we need it | Where it would land |
|---|---|---|
| **`69a80a6a` — xhci: fix reset procedure on real hardware** | Replaces magic bit numbers with named constants; fixes the HCRST wait loop to read from `usb_cmd` instead of `usb_sts` (the spec says HCRST is in USB_CMD). Without this, `xhcid` can spin or wedge on real controllers. | New patch `local/patches/base/P3-xhci-real-hw-reset.patch` against `xhcid/src/xhci/mod.rs`. |
| **`19570db4` — xhci: support 64-bit contexts (CSZ)** | Makes `Xhci` generic over context size (`Xhci<CONTEXT_32>` / `Xhci<CONTEXT_64>`) with runtime detection via `HCCPARAMS1.CSZ`. Required by modern xHCI controllers (Alder Lake, Raptor Lake, Ryzen 7000+). The local source already has `daemon_with_context_size<const N: usize>` and a `//TODO: cleanup CSZ support` comment at the call site — the upstream fix is the natural completion. | New patch `local/patches/base/P3-xhci-csz-64-bit.patch` against `xhcid/src/main.rs` and the downstream context types. |
| **`12e601b3` — xhci: improvements based on real hardware testing** | Adds `USB_CMD_INTE`, corrects port RWC handling, fixes address_device speed passthrough. Companion to `69a80a6a`. | New patch `local/patches/base/P3-xhci-real-hw-impl.patch`. |
### 3.2 Medium-priority upstream commits
| Upstream commit | Note |
|---|---|
| `8dcd85b5`, `ba0ca4ce` — Fix packet size for USB 3.0 and USB 1 | Required for SuperSpeed device enumeration. Adopt in same patch as CSZ. |
| `cbbcbc9e`, `f58625b0` — `usbhubd`/`xhcid` fix reading descriptor / port status on USB 3 hubs | Round out the USB 3 hub story. |
| `8f278dcb`, `34b37410` — Bounds check on `root_hub_port_index()` | Stop a panic that we already pay down via patch but have not tested in tree. |
| `4d6581d4` — xhcid: add more timeouts | Prevents infinite hangs on unresponsive controllers. |
| `7e3e841f` — xhci: fix reading EHB flag in received_irq | Companion for interrupt-driven paths. |
| `e3a13a0c` — `xhcid` and friends: use newtype `PortId` to ensure route string | Type-safety win. |
| `6ac41ee` — daemon: tolerate BrokenPipe on ready() | Already in our base fork. |
| `258ea4e6`, `865ca866` — `usbscsid`: use the unified disk scheme implementation | `usbscsid` revision; lower priority, code organization. |
| `e4aab167`, `24c1f0a3` — xhcid: don't exit the event loop when using irqs | Required for stable interrupt-driven operation (pairs with the §1.6 fix). |
### 3.3 Lower-priority upstream commits to record, not blindly adopt
| Commit | Note |
|---|---|
| `a5f87735` — ignore alternate settings | Conflicts with our composite-device fix (P3-xhci-device-hardening retains explicit alternate handling). Validate whether dropping this is sound given our active `PortState.active_ifaces` map. |
| `7c980137` — language ID for string descriptors | Likely a clean drop-in. |
| `374e5fbf` — xhci: use redox-scheme v2 | We are on `redox-scheme 0.11`; a v2 migration is not in scope for 0.2.x. |
| `30fb1e7a` — drivers merged into base (Nov 2025) | Mirrors what Red Bear already does (our `local/sources/base/`). No action. |
| USB SCSI driver disabled upstream (Dec 2025) | Red Bear keeps it on with the BOUNDED storage test. Re-evaluate after P2-B1. |
| `bjorn3` enabled xHCI by default in QEMU x86-64 (Mar 2026) | Aligns with our `redbear-mini` boot script. No action. |
| `bjorn3` moved xHCI config to runtime (Apr 2026) | Lower priority — compile-time config is fine for our release model. |
| Antoine Reversat — simplified xhci (May 2026) | Subject to per-line review. |
### 3.4 Things upstream still does NOT have
These are explicit non-features from upstream that Red Bear should not silently inherit as
a todo:
- **USB Type-C / USB-PD / alt-modes.** No policy engine, no protocol stack.
- **USB4 / Thunderbolt.** Listed as "not supported" in upstream `COMMUNITY-HW.md`.
- **xHCI debug capability (DbC).** Not implemented.
- **USB device mode (gadget) / OTG.** No dual-role support.
- **USB isochronous transfers.** `xhcid` returns `ENOSYS` for isoch endpoints.
These belong to §6 P5 (architectural decision gate), not to "fix the missing patch."
---
## 4. Bare-metal-input correctness (the actual boot-time failure modes)
The bare-metal USB keyboard problem is not "xhcid doesn't work." xhcid does work in QEMU
and on some real hardware. The failure modes are the **paths that do not reach xhcid**:
1. **EHCI-attached USB keyboard** — xHCI now owns every USB-3 controller, but
EHCI/companion controllers (UHCI/OHCI) still own low/full-speed devices on chipsets
that firmware routes through them. **ehcid does not auto-spawn class drivers**, so even
though ehcid publishes `/scheme/usb/port<n>/...`, no `usbhidd` is started for any
device on that scheme. The keyboard is reachable by userland but the input pipeline
never builds.
2. **UHCI/OHCI-attached devices** — uhcid and ohcid are 35-line stubs. The companion
controller is owned (by `pcid`) but no USB traffic flows. There is no port state, no
transfer completion, no scheme.
3. **xHCI interrupt-driven operation is offline** — line 141 hardcodes polling. On real
hardware with no reliable polling timer, this can produce slow enumeration or input
lag, and on some chips it can wedge the controller (see upstream `69a80a6a`).
4. **No real-hardware validation matrix** — there is no `hardware-validation.md` table
enumerating which physical controller families have been exercised on bare metal.
QEMU `qemu-xhci` is one fixed emulation target; it is not representative.
5. **USB HID and ACPI I2C-HID are not the same** — internal laptop keyboards are
I2C-HID (`i2c-hidd`, `intel-thc-hidd`), not USB. These are real but separate. The
I2C-HID plan and the USB HID plan cannot assume one is a substitute for the other.
6. **Strict-boot mode exists but is not bound** — `uhcid`/`ohcid`/`ehcid` accept
`--strict-boot`, but no initfs entry enables it; the policy lives in operator
knowledge, not in the artifact.
7. **LED state is a weak health signal** — `usbhidd` keyboard LEDs are bounded,
per-device, best-effort; they are not a system-global lock-state authority. A dead
`Caps Lock` indicator does not prove keyboard transport is broken; a working indicator
does not prove the external USB keyboard fallback works. Treat LED state as cosmetic
debug, not as a proof of input health.
8. **External keyboard bare-metal proof remains unpinned** — the bounded QEMU lifecycle
proof is not the same as a bare-metal proof. We need a captured log per controller
family before claiming a fallback works on hardware.
These eight items are the inputs to phases P0-A (xHCI runtime) and P0-B (legacy host
controllers).
---
## 5. Phases — execution order
Phases are ordered by *what unblocks bare-metal correctness and what has unambiguous
upstream-comparable patches we can adopt without inventing semantics*.
| Phase | Goal | Exit |
|---|---|---|
| **P0-A1** | ✅ Re-enable xHCI MSI/MSI-X/INTx at runtime. Committed 2026-07-07 (`local/sources/base` commit `cbd40e0d`, parent `a2998c2d`). `test-xhci-irq-qemu.sh` now greps for actual reactor log lines. | ✅ QEMU proof script updated; real-hardware bring-up deferred to operator build. |
| **P0-A2** | Adopt upstream xHCI reset-procedure fix + hardware hardening (`69a80a6a`, `12e601b3`). | One QEMU full-stack pass + one real-hardware bring-up |
| **P0-A3** | Adopt CSZ (64-bit contexts) upstream commit; complete the `//TODO: cleanup CSZ support` site. | Same as A1 |
| **P0-A4** | ✅ Adopt panic bounds-check (`8f278dcb`) and timeout expansion (`4d6581d4`). Committed 2026-07-07: 5 `root_hub_port_index()` unwrap/index sites replaced with bounded access (`ok_or_else(|| Error::new(EINVAL))?`, `match None → continue`, `expect()` with diagnostic). | QEMU lifecycle + full-stack pass |
| **P0-B1** | Auto-spawn class drivers from the EHCI scheme (`/scheme/usb/port<n>/descriptors`). Reuse the existing `xhcid` class-driver spawn model by refactoring the spawn helper out of `xhcid` into `usb-core::spawn_usb_driver` if necessary, then driving it from EHCI too. | QEMU run with USB keyboard on EHCI route → typed input reaches `inputd` |
| **P0-B2** | Implement real runtime enumeration for `uhcid` and `ohcid` over the existing `usb-core::UsbHostController` trait. Each new driver must register the same `/scheme/usb` tree pattern ehcid uses and must auto-spawn class drivers via `P0-B1`. | QEMU run with low/full-speed USB keyboard on legacy controller route → typed input reaches `inputd` |
| **P1** | ✅ Code-verified 2026-07-07. USB 3.0 packet-size handling (`update_max_packet_size` with shift exponent for USB ≥3, bytes for USB ≤2) in baseline. Hub descriptor reading uses separate `HubDescriptorV2`/`HubDescriptorV3`. Slot context hub bit (bit 26) and hub port count (bits 24-31) correctly set in `configure_endpoints_once`. `SET_HUB_DEPTH` issued for USB 3 hubs. TTT propagation not applicable to USB 3 (TT is USB 2.0 high-speed split-transaction only). TODOs about `interface_desc`/`alternate_setting` on USB 3 hubs are safe — passing None matches upstream behavior. | QEMU run deferred to operator. |
| **P2-A** | ✅ Storage data path: in-guest write verification on the `disk.usb-*` scheme. `redbear-usb-storage-check` already performs write+readback+restore at sector 2048. `test-usb-storage-qemu.sh` now validates all four checks (discovery, write, readback, restore). | `redbear-usb-storage-check` proves a write/read round-trip in QEMU |
| **P2-B** | Userspace API: pick native or `libusb`. Native: bake `usb-core` consumers first. `libusb`: pick an active WIP commit; if there is none, **defer** §2 row "userspace API" rather than start a new side-quest. | Decision + prototype |
| **P3** | HID robustness: real-hardware HID validation matrix; `i2c-hidd` migration to named producers; `evdevd` hotplug add/remove behavior from USB. | One HID device family proven bare-metal + one hot-unplug cycle QEMU |
| **P4** | Validation slices: complete `test-usb-storage-qemu.sh` write proof, hardware matrix in `HARDWARE-VALIDATION-MATRIX.md` (board, controller, input/storage/audio result), bounded stress loop on top of `test-xhci-device-lifecycle-qemu.sh`. | matrix has one row per controller family |
| **P5** | Architectural decision gate: host-only vs device mode; USB-C/PD/alt-mode scope; USB4/Thunderbolt exclusion; whether UCSI grows into a real PD surface. Recorded as an ADR in `local/docs/`. | Decision recorded |
Phases are not equal in size. P0-A1 and P0-B2 are bounded, well-understood work. P2-B
(libusb vs native) is a fork in the road; it is correct that it has *no* time estimate.
P5 is a decision moment, not an implementation.
---
## 6. Validation inventory and bounded proofs
Five scripts exist today. They are honest about their scope (QEMU) but should be paired
with a real-hardware matrix per phase exit.
| Script | What it actually proves | Limits |
|---|---|---|
| `local/scripts/test-usb-qemu.sh --check` | Full stack: xHCI init, HID spawn, SCSI spawn, sector-0 readback, BOS, no crashes. | QEMU `qemu-xhci` only; one emulator config; no real hardware. |
| `local/scripts/test-xhci-device-lifecycle-qemu.sh --check` | Bounded hotplug attach/detach for HID + storage. | QEMU only; monitor-driven hotplug; not a stress test. |
| `local/scripts/test-usb-storage-qemu.sh` | Mass storage autospawn + sector-0 readback. | No write proof; no multi-LUN; no UAS. |
| `local/scripts/test-xhci-irq-qemu.sh --check` | ✅ Updated 2026-07-07 to verify interrupt-driven reactor path. Greps for `Running IRQ reactor with IRQ file and event queue` (must be present) and `Running IRQ reactor in polling mode` (must NOT be), plus MSI-X/INTx delivery method. | QEMU `qemu-xhci` only; not real-hardware. |
| `local/scripts/test-usb-maturity-qemu.sh` | Sequential wrapper. | Composes the others; inherits their limits. |
**Required proofs after P0-A1 lands:**
1. `test-xhci-irq-qemu.sh --check` must transition from "binary runs" to "interrupts fire
and complete." Add a bounded probe that confirms a hotplug event triggers an IRQ in
guest time, not a sleep timer.
2. Add `test-xhci-regression-qemu.sh` for the upstream reset-procedure fix.
3. Add `test-uhci-runtime-qemu.sh` and `test-ohci-runtime-qemu.sh` after P0-B2 — same
shape as the xHCI lifecycle test.
4. Add `test-ehci-class-autospawn-qemu.sh` after P0-B1.
Proofs must:
- run on `redbear-mini` from a clean `make clean` build;
- keep the boot log under `local/docs/boot-logs/` with a `REDBEAR-...-RESULTS.md`;
- be citeable from phase status (§3 matrix) and from `USB-VALIDATION-RUNBOOK.md`.
---
## 7. Durability posture (the local-fork problem, honestly)
The base fork at `local/sources/base/` currently carries **two USB-related commits**
(one pre-existing, one from P0-A1):
```
$ git -C local/sources/base log -- drivers/usb/
cbd40e0d xhcid: re-enable interrupt-driven operation via get_int_method ← P0-A1 (2026-07-07)
6ac41ee daemon: tolerate BrokenPipe on ready(); i2cd: handle empty RON response
dd08b76 Red Bear OS base baseline from 0.1.0 pre-patched archive
```
Everything else that v1 described as "88 error handling fixes across xhcid" lives in
**`local/patches/base/P*.patch`** files. That is acceptable as long as:
1. The base *recipe* (`recipes/core/base/recipe.toml`) actually applies those patches on
`repo cook`. Verify by running `repo validate-patches base` after every edit and by
checking that `recipes/core/base/source/drivers/usb/xhcid/...` contains the Red Bear
state, not the upstream state.
2. No "live-edit" of `recipes/core/base/source/...` ever escapes into the next build
without an immediate patch mirror. `local/AGENTS.md` enforces this; the rule stands.
3. The next base-fork bump (rebase onto a newer Redox base tag) preserves every USB patch
in the same order and lands them as commits on the `submodule/base` branch — not as a
new mega-patch.
**Durability remediation work that does not block USB phases:**
- ✅ P0-A1 landed as the **first USB-focused commit on `submodule/base`** since `dd08b76`
(commit `cbd40e0d`, 2026-07-07). This reopens per-feature commit history and makes
future rebases reviewable.
- P0-A2 through P0-B2 should each land as individual, reviewable commits on the same
branch — never bundled into a mega-commit. Each phase below has a concrete file list
and diff target (see §11).
- The base fork's `Cargo.toml` should track the `submodule/base` branch as upstream
(currently it does, per the source-of-truth rules in `local/AGENTS.md`).
---
## 8. Support language — how Red Bear describes USB
Until P0-A and P0-B exit, Red Bear should NOT use any of:
- "USB support works."
- "USB is functional."
- "USB keyboard works on bare metal."
- "USB storage is supported."
It SHOULD use language such as:
- "xHCI host support is present but experimental; bare-metal proof requires the real-hardware
matrix in §6 P4."
- "EHCI ownership and USB 2 register init exist; class-driver auto-spawn is pending P0-B1."
- "UHCI and OHCI are userspace stubs in this build; legacy host controllers are not yet
the boot-input fallback."
- "USB storage autospawn and bounded sector-0 readback are QEMU-validated; write proof is
pending P2-A."
- "USB error handling and correctness carry significant Red Bear patches over upstream; see
`local/patches/base/P[1-3]-xhci*.patch` and `local/patches/base/P3-xhci-device-hardening.patch`."
- "USB-C topology (UCSI) is exposed but does not negotiate PD or alternate modes."
The README status table and the desktop-path plans should adopt this language consistently
the next time they are touched. The `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` already
treats USB as a first-class subsystem; this plan agrees and refines the wording.
---
## 9. Open questions and follow-up
1. **Rebase cadence** — when `submodule/base` upstream lands the
`simplify xHCI` commit (May 2026), do we adopt it before or after P0-A1 lands? Per the
upstream-first rule, after — but the diff requires per-line review because our local
patches (`P1-xhcid-*.patch`, `P3-xhci-device-hardening.patch`) overlap on the same code
regions.
2. **Cross-process class driver spawn** — the class spawn path is currently xhcid-driven
(via the scheme). Should the spawn helper live in `usb-core` and be reused by
`ehcid`/`uhcid`/`ohcid`? Yes (P0-B1, P0-B2) — and that requires `usb-core` to grow
`spawn_class_driver`, which it does not yet have. The migration is the natural unit
of P0-B1.
3. **Strict-boot mode** — should `pcid-spawner` always pass `--strict-boot` to USB host
daemons? Operators can set `REDBEAR_STRICT_USB_BOOT=1` today; the default is off.
Recommend leaving the default off but documenting the env var in
`USB-VALIDATION-RUNBOOK.md` (P0-A4 documentation step).
4. **Whether to keep `usbscsid` enabled after upstream disabled it** — adopt the upside
(bounded in-guest write proof) and the downside (occasional stalls). Defer to P2-A
evaluation.
5. **Hardware validation entries** — the matrix in `local/docs/HARDWARE-VALIDATION-MATRIX.md`
is currently tiny. P4 explicitly grows it; if it does not, the matrix block of P4 exit
blocks the phase.
---
## 10. See also
- `local/docs/USB-VALIDATION-RUNBOOK.md` — operator runbook for the bounded proofs above.
- `local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md` — the canonical desktop-path plan; treats
USB as a first-class runtime subsystem.
- `local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` — MSI/MSI-X quality
surface that P0-A1 actually exercises.
- `local/docs/BLUETOOTH-IMPLEMENTATION-PLAN.md` — `redbear-btusb` consumes the USB class
driver dispatch path that P0-B1 makes available to all host controllers.
- `local/docs/WIFI-IMPLEMENTATION-PLAN.md` — Wi-Fi native control plane; not USB-coupled.
- `local/docs/QUIRKS-SYSTEM.md` — TOML + DMI + compiled-in quirk tables, source of USB
device workarounds.
- `local/AGENTS.md` — fork model, durability policy, single-repo rule, branch policy.
- `local/docs/archived/USB-IMPLEMENTATION-PLAN-v1-2026-04.md` — superseded v1.
- `local/docs/archived/USB-BOOT-INPUT-PLAN.md` — preserved for the boot-input
historical context; not the planning authority.
- `local/docs/archived/XHCID-DEVICE-IMPROVEMENT-PLAN.md` — preserved for the xhcid
device-level historical context; absorbed into phases P0-A and P1.
---
## 11. Implementation handoff — P0-A2 through P0-B2
This section is the concrete kickoff for each remaining P0 sub-phase.
Each entry names files to touch, upstream commits to diff, and the required
validation step. A phase **does not leave implementation** until committed on
`submodule/base` (or the equivalent local fork) and, where practical, verified
with an automated QEMU proof.
### P0-A2 — upstream xHCI reset-procedure fix
| Field | Detail |
|---|---|
| **Upstream commits** | `https://gitlab.redox-os.org/redox-os/base/commit/69a80a6a` — "xhci: fix reset procedure on real hardware". Also `https://gitlab.redox-os.org/redox-os/base/commit/12e601b3` — "xhci: improvements based on real hardware testing". |
| **Files to touch** | `local/sources/base/drivers/usb/xhcid/src/xhci/mod.rs` — `Xhci::new`, controller reset path. `local/sources/base/drivers/usb/xhcid/src/xhci/operational.rs` — operational register definitions. |
| **What changes** | Replace magic bit numbers with named constants (`USB_CMD_RS`, `USB_CMD_HCRST`, `USB_STS_HCH`, `USB_STS_CNR`). Fix the HCRST wait loop to read from `usb_cmd` instead of `usb_sts`. Apply the port-RWC-correction and address_device speed passthrough from 12e601b3. |
| **Git landing** | One commit on `local/sources/base` master → update parent gitlink. |
| **Validation** | Rebuild `redbear-mini`, run `test-usb-qemu.sh --check`. Boot log must show xHCI controller init without "hang" or "reset failed" lines. If real hardware is available, boot on one Intel and one AMD controller. |
| **Blocking** | Nothing — independent of P0-A1. |
### P0-A3 — CSZ (64-bit contexts)
| Field | Detail |
|---|---|
| **Upstream commits** | `https://gitlab.redox-os.org/redox-os/base/commit/19570db4` — "xhci: support 64-bit contexts (CSZ)". |
| **Files to touch** | `local/sources/base/drivers/usb/xhcid/src/main.rs` — `daemon_with_context_size<const N: usize>` and the `//TODO: cleanup CSZ support` comment at line 119. `local/sources/base/drivers/usb/xhcid/src/xhci/context.rs` — `DeviceContextList`, `InputContext`. `local/sources/base/drivers/usb/xhcid/src/xhci/mod.rs` — `Xhci<const N: usize>` struct, `PortState`. |
| **What changes** | Make `Xhci`, `DeviceContextList`, `InputContext`, `PortState`, and `StreamContextArray` generic over context size `N` (32 or 64). Detect CSZ at runtime via `CapabilityRegs::csz()`. The local source already parameterizes `daemon_with_context_size` — the upstream fix is the natural completion. Remove the `//TODO: cleanup CSZ support` once generic parameterization is clean. |
| **Git landing** | One commit. |
| **Validation** | Rebuild + QEMU full-stack check. CSZ is not visible without a modern controller, so the QEMU proof is "didn't break existing paths." Real-hardware proof: boot on Ryzen 7000+ or Intel Alder Lake+. |
| **Blocking** | Nothing, but lands best after P0-A2 to avoid merge conflicts. |
### P0-A4 — bounds check + timeouts
| Field | Detail |
|---|---|
| **Upstream commits** | `https://gitlab.redox-os.org/redox-os/base/commit/8f278dcb` — bounds check on `root_hub_port_index()`. `4d6581d4` — "xhcid: add more timeouts". |
| **Files to touch** | `local/sources/base/drivers/usb/xhcid/src/xhci/mod.rs` — port index bounds. `local/sources/base/drivers/usb/xhcid/src/xhci/scheme.rs` — timeout additions. |
| **What changes** | Bounds-check the port index parameter to prevent out-of-range access. Add timeout guards on control transfer and address device paths to prevent infinite hangs. |
| **Git landing** | One commit. |
| **Validation** | QEMU lifecycle test must still pass. |
| **Blocking** | None. |
### P0-B1 — EHCI class-driver auto-spawn
| Field | Detail |
|---|---|
| **Why** | `ehcid` publishes `/scheme/usb/port<n>/descriptors` but does **not** auto-spawn `usbhidd` or `usbscsid` when a matching device appears. Only `xhcid` does that through its scheme. Without auto-spawn, EHCI-attached USB keyboards never reach the input pipeline. |
| **Files to touch** | **New logic:** `local/recipes/drivers/usb-core/source/src/spawn.rs` — add a `spawn_class_driver` helper that takes a port descriptor, walks the USB class table, and spawns the matching class daemon (reuses the spawn model from xhcid). **Call site:** `local/recipes/drivers/ehcid/source/src/main.rs` — after enumerating a port and reading descriptors, call `usb_core::spawn_class_driver`. |
| **Git landing** | Two commits: (1) usb-core spawn helper, (2) ehcid call site. Both go on `submodule/base` since they touch existing tracked code. |
| **Validation** | New script: `test-ehci-class-autospawn-qemu.sh` — boot with USB keyboard on EHCI route, verify `usbhidd` spawns and keyboard input reaches `inputd`. |
| **Dependency** | P0-B1 is NOT blocked by anything. The usb-core trait already has `UsbHostController::control_transfer` and descriptor parsers. The class-spawn decision table (`/lib/drivers.d/70-usb-class.toml`) is already wired. |
### P0-B2 — real UHCI/OHCI runtime enumeration
| Field | Detail |
|---|---|
| **Why** | `uhcid/src/main.rs` and `ohcid/src/main.rs` are 35-line stubs: read PCI BAR4, log, sleep forever. No scheme, no transfers, no enumeration. This is the bare-metal USB keyboard blocker for legacy controller paths. |
| **Files to touch** | **uhcid:** `local/recipes/drivers/uhcid/source/src/main.rs` (replace 35-line stub with a ~1500-line implementation). **ohcid:** `local/recipes/drivers/ohcid/source/src/main.rs` (same). Both must implement `usb_core::UsbHostController` in a new sibling file `host.rs`, register `/scheme/usb`, perform frame-list/QH/TD/port enumeration, and call `spawn_class_driver` (from P0-B1) when a keyboard/storage device appears. Use the existing `ehcid` as a reference model. |
| **What changes** | For each controller: (a) PCI BAR mapping + register definitions, (b) `UsbHostController` trait implementor, (c) scheme registration (`/scheme/usb`), (d) port enumeration loop, (e) class-driver auto-spawn. |
| **Git landing** | Two commits (one per controller). These live in `local/recipes/drivers/`, not `local/sources/base/`, so they are committed on the parent `0.3.0` branch directly (tracked-tree model). |
| **Validation** | Two new scripts: `test-uhci-runtime-qemu.sh --check` and `test-ohci-runtime-qemu.sh --check`. Same shape as the xHCI lifecycle test: boot, verify scheme registration, hotplug keyboard, verify `usbhidd` spawn, verify keystrokes reach `inputd`. |
| **Dependency** | P0-B2 **depends on P0-B1** (uses the class-spawn helper) but does NOT depend on any of P0-A1 through P0-A4. UHCI and OHCI are independent from xHCI for enumeration. |
| **Reference impl** | `local/recipes/drivers/ehcid/source/src/main.rs` (1550 lines) — uses `usb-core`, registers `/scheme/usb`, MMIO frame list, QH/TD control/bulk/interrupt. UHCI and OHCI are simpler controllers and should be smaller. |
### Build-and-verify workflow (per-session)
```
# After committing any P0 sub-phase change:
./local/scripts/build-redbear.sh --upstream redbear-mini
./local/scripts/test-xhci-irq-qemu.sh --check # if xHCI touched
./local/scripts/test-usb-qemu.sh --check # full-stack regression
./local/scripts/test-xhci-device-lifecycle-qemu.sh --check # lifecycle
# After P0-B1/P0-B2:
./local/scripts/test-ehci-class-autospawn-qemu.sh --check # (to be written)
./local/scripts/test-uhci-runtime-qemu.sh --check # (to be written)
./local/scripts/test-ohci-runtime-qemu.sh --check # (to be written)
```
---
## 12. P5 — Modern USB Scope Decision (ADR)
*Date:* 2026-07-07.
*Status:* Decided. Red Bear OS adopts **host-only USB** for the foreseeable
future.
### Decision
Red Bear OS ships as a **USB host** platform. Device mode (gadget), OTG
dual-role, USB-C Power Delivery negotiation, USB-C alternate modes, USB4, and
Thunderbolt are **explicitly excluded** from the current scope. This decision
is recorded as an ADR (Architecture Decision Record) so that future work does
not carry implicit scope expansion into the active build without a deliberate
re-evaluation.
### What is in scope (host-first)
- xHCI, EHCI, UHCI, and OHCI **host controllers** (drivers built, P0 complete).
- USB class daemons: HID (keyboard/mouse), Mass Storage (BOT), Hub, Audio.
- USB device enumeration, descriptor parsing, and class-driver auto-spawn.
- Hardware quirks: compiled-in + TOML runtime tables (146 USB + 214 storage
entries), consumed at runtime by xhcid and usbscsid.
- USB 3.x SuperSpeed (5 Gbps) and SuperSpeedPlus (10 Gbps) host operation
through xhcid.
- USB-C UCSI topology detection (`ucsid`, exposes `/scheme/ucsi`).
### What is explicitly excluded
| Capability | Excluded because |
|---|---|
| USB device mode (gadget) | Red Bear OS is a desktop/server OS, not an embedded peripheral. No dual-role controller (DRD) support exists in any upstream Redox component. |
| OTG (On-The-Go) | OTG requires dual-role + HNP/SRP protocol negotiation. No Redox kernel or driver infrastructure exists, and OTG is a declining standard (USB-C replaces it). |
| USB-C Power Delivery | PD negotiation requires a CC-line protocol engine, a policy manager, and source/sink state machines. This is a full subsystem (~10k LoC in Linux), not a small driver add-on. PMIC/charger integration is also needed. |
| USB-C alternate modes (DisplayPort, Thunderbolt) | Requires PD negotiation first, plus mux control, plus DP/Thunderbolt protocol stacks. No Redox GPU driver consumes DP alt-mode (display drivers use PCIe or platform-internal paths). |
| USB4 | USB4 requires PCIe tunneling, DisplayPort tunneling, and a USB4 router topology. The Redox PCI subsystem does not support PCIe hotplug or tunneling. Linux's USB4 stack is ~15k LoC. |
| Thunderbolt 3/4 | Thunderbolt requires USB4 or PCIe hotplug infrastructure. Listed as "not supported" in upstream Redox `COMMUNITY-HW.md`. No driver, no IOMMU DMA remapping for Thunderbolt security levels. |
| xHCI Debug Capability (DbC) | DbC requires a separate xHCI debug capability register set and a dedicated debug target endpoint. Serial console via UART is the standard debug path on Red Bear OS. DbC adds complexity without a use case. |
### What may be reconsidered later
- **USB-C PD (power role only, sink).** If Red Bear OS runs on a laptop that
charges via USB-C, the system firmware (UEFI/BIOS) handles PD negotiation
before the OS boots. An OS-level PD policy manager is only needed for
runtime source/sink role swaps, which are uncommon in a desktop/server OS.
Revisit if bare-metal laptop support requires it.
- **USB device mode for firmware update.** Some devices require USB DFU
(Device Firmware Upgrade) mode. This is a narrow, well-bounded gadget class
that could be implemented without a full dual-role stack. Not in current plan.
- **UCSI PD surface.** The existing `ucsid` daemon exposes connector topology.
Extending it to pass PD power contract data to a userspace policy manager is
a reasonable follow-up if hardware validation demands it.
### Rationale
Red Bear OS is a desktop/server operating system. The USB host path (keyboard,
mouse, storage, hub, audio) covers the essential desktop use case. Expanding
into device mode, PD, alt-modes, USB4, or Thunderbolt would add thousands of
lines of new kernel and driver code with no immediate user-visible benefit —
every excluded subsystem would consume weeks or months of development and
require hardware the team does not currently validate against.
This decision keeps the USB scope **honest** and **buildable** with the current
team. It removes implicit "we should support X someday" scope pressure from
the active build, letting the team focus on completing the host-side USB
maturity work (P1P4) and the Wi-Fi/Bluetooth/desktop integration paths that
depend on it.
### Review cadence
This ADR is reviewed **when a new Red Bear OS release branch is cut** (e.g.,
`0.3.0` → `0.4.0`). At each review, the team evaluates whether any excluded
capability has become necessary for the next release's target hardware
profile. The ADR is not a permanent rejection — it is a current-scope
boundary that prevents unplanned scope creep.
+1 -1
View File
@@ -53,5 +53,5 @@ integrity, submodule hygiene, firmware presence warning) that bare `make all` /
## QEMU boot
```bash
make qemu # Boot the latest built image in QEMU
make qemu CONFIG_NAME=redbear-mini # Boot the latest built image in QEMU
```
-40
View File
@@ -1,40 +0,0 @@
# fork-upstream-map.toml — Authoritative mapping of local Cat 2 fork
# directories to their upstream Redox repositories and the upstream
# release tag each fork is based on. Updated by
# local/scripts/refresh-fork-upstream-map.sh. Consumed by
# local/scripts/verify-fork-versions.sh to enforce the "no fake version
# label" rule (local/AGENTS.md § "No-fake-version-label rule").
#
# Format: one line per fork:
# <fork-name> <upstream-git-url> <upstream-release-tag>
#
# A fork with `<X.Y.Z>-rbN>` in its Cargo.toml MUST have its
# `<X.Y.Z>` part match the upstream tag listed here, and the source
# content MUST be a real rebase onto that upstream tag plus documented
# Red Bear patches (in local/patches/<name>/).
#
# On branch 0.3.0, all Cat 2 forks use the +rb0.3.0 build-metadata suffix
# in their Cargo.toml version fields (e.g. 0.6.0+rb0.3.0).
# Format: <fork-name> <upstream-git-url> <upstream-release-tag> [snapshot]
#
# The optional 4th column "snapshot" marks forks whose git history is
# unrelated to upstream (imported from archived snapshots, not cloned).
# For snapshot forks, verify-fork-versions.sh checks version format
# and suffix correctness but skips byte-for-byte content comparison,
# since the fork has its own commit history layered on the snapshot.
#
# A fork with `<X.Y.Z>-rb<B.B.B>` in its Cargo.toml MUST have its
# `<X.Y.Z>` part match the upstream tag listed here, and the source
# content MUST be a real rebase onto that upstream tag plus documented
# Red Bear patches (in local/patches/<name>/).
syscall https://gitlab.redox-os.org/redox-os/syscall.git 0.9.0 snapshot
libredox https://gitlab.redox-os.org/redox-os/libredox.git 0.1.18 snapshot
redoxfs https://gitlab.redox-os.org/redox-os/redoxfs.git 0.9.1 snapshot
redox-scheme https://gitlab.redox-os.org/redox-os/redox-scheme.git 0.11.2 snapshot
relibc https://gitlab.redox-os.org/redox-os/relibc.git 0.6.0 snapshot
kernel https://gitlab.redox-os.org/redox-os/kernel.git 0.5.12 snapshot
bootloader https://gitlab.redox-os.org/redox-os/bootloader.git 1.0.0 snapshot
installer https://gitlab.redox-os.org/redox-os/installer.git 0.2.42 snapshot
userutils https://gitlab.redox-os.org/redox-os/userutils.git 0.1.0 snapshot
@@ -1,44 +0,0 @@
From: Red Bear OS <adminpupkin@gmail.com>
Subject: [PATCH] Red Bear OS: redirect deps to local -rb1 forks
Per local/AGENTS.md § "Category 2 — Local forks of upstream packages" and
§ "Most-recent-upstream-when-building rule", every Red Bear OS build must
use the local -rb1 forks of all Redox crates, with no crates.io fallback.
This patch adds [patch.crates-io] entries to the bootloader source's
Cargo.toml to redirect every crates.io pull to the local fork under
local/sources/<name>/.
Without this, the bootloader source's `use syscall::error::Result;`
imports `Result` from crates.io's `redox_syscall 0.5.18/0.6.0`, but the
`Disk` trait defined in our local `redoxfs 0.9.0-rb1` uses `Result`
from our local `redox_syscall 0.9.0-rb1`. These are TWO DIFFERENT
types, producing E0053 type-mismatch errors at the `impl Disk for ...`
in `src/os/bios/disk.rs`.
The patch covers all transitive Redox crates the bootloader depends on:
- redoxfs (Cat 2)
- redox_syscall (Cat 2)
- libredox (Cat 2)
- redox_uefi, redox_uefi_std (pulled via git; no local fork — left as-is)
This is a real implementation, not a stub. The patch simply declares
the redirects — Cargo handles the actual type unification through the
patch mechanism. The bootloader source code is unchanged.
---
Cargo.toml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Cargo.toml b/Cargo.toml
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,3 +32,9 @@ byteorder = { version = "1", default-features = false }
[features]
default = []
live = []
serial_debug = []
+
+[patch.crates-io]
+redoxfs = { path = "../../../../local/sources/redoxfs" }
+redox_syscall = { path = "../../../../local/sources/syscall" }
+libredox = { path = "../../../../local/sources/libredox" }
@@ -1,104 +0,0 @@
diff --git a/Cargo.toml b/Cargo.toml
index 9b911691..0a1b73df 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -91,7 +91,7 @@ starship-battery = { version = "0.10.2", optional = true }
sysinfo = { git = "https://github.com/jackpot51/sysinfo.git" }
timeless = "0.0.14-alpha"
toml_edit = { version = "0.23.6", features = ["serde"] }
-tui = { version = "0.30.0-alpha.5", package = "ratatui", features = ["unstable-rendered-line-info"] }
+tui = { version = "0.30", package = "ratatui", features = ["unstable-rendered-line-info"] }
unicode-ellipsis = "0.3.0"
unicode-segmentation = "1.12.0"
unicode-width = "0.2.0"
diff --git a/src/canvas/components/time_graph/base/time_chart/canvas.rs b/src/canvas/components/time_graph/base/time_chart/canvas.rs
index 4378bba6..ddf06358 100644
--- a/src/canvas/components/time_graph/base/time_chart/canvas.rs
+++ b/src/canvas/components/time_graph/base/time_chart/canvas.rs
@@ -188,12 +188,16 @@ impl<'a> Context<'a> {
pub fn new(
width: u16, height: u16, x_bounds: [f64; 2], y_bounds: [f64; 2], marker: symbols::Marker,
) -> Context<'a> {
+ // Red Bear OS: ratatui 0.30+ added new `Marker` variants
+ // (e.g. `Quadrant`, `HalfBlock`-related). Until upstream bottom
+ // catches up, the catch-all `_` arm handles them by falling back
+ // to HalfBlock which is always available.
let grid: Box<dyn Grid> = match marker {
symbols::Marker::Dot => Box::new(CharGrid::new(width, height, '•')),
symbols::Marker::Block => Box::new(CharGrid::new(width, height, '█')),
symbols::Marker::Bar => Box::new(CharGrid::new(width, height, '▄')),
symbols::Marker::Braille => Box::new(BrailleGrid::new(width, height)),
- symbols::Marker::HalfBlock => Box::new(HalfBlockGrid::new(width, height)),
+ _ => Box::new(HalfBlockGrid::new(width, height)),
};
Context {
x_bounds,
diff --git a/src/canvas/components/time_graph/base/time_chart/grid.rs b/src/canvas/components/time_graph/base/time_chart/grid.rs
index 73aadb52..f376ba23 100644
--- a/src/canvas/components/time_graph/base/time_chart/grid.rs
+++ b/src/canvas/components/time_graph/base/time_chart/grid.rs
@@ -63,7 +63,11 @@ impl BrailleGrid {
Self {
width,
height,
- utf16_code_points: vec![symbols::braille::BLANK; length],
+ // Red Bear OS: ratatui 0.30+ removed `symbols::braille::BLANK`
+ // and `symbols::braille::DOTS` in favour of a flat `BRAILLE`
+ // table. `utf16_code_points` is `Vec<u16>`, so the empty
+ // braille U+2800 is stored as a `u16`.
+ utf16_code_points: vec![0x2800_u16; length],
colors: vec![Color::Reset; length],
}
}
@@ -82,38 +86,29 @@ impl Grid for BrailleGrid {
}
fn reset(&mut self) {
- self.utf16_code_points.fill(symbols::braille::BLANK);
+ // Red Bear OS: see comment in `new` above.
+ self.utf16_code_points.fill(0x2800_u16);
self.colors.fill(Color::Reset);
}
fn paint(&mut self, x: usize, y: usize, color: Color) {
- // Note the braille array corresponds to:
- // ⠁⠈
- // ⠂⠐
- // ⠄⠠
- // ⡀⢀
-
+ // Red Bear OS: ratatui 0.30+ braille module only exposes the flat
+ // `BRAILLE: [char; 256]` table. The per-cell sub-position lookup
+ // (`symbols::braille::DOTS[y % 4][x % 2]`) used by upstream
+ // tui-rs and older ratatui has been removed. For now we just
+ // clear the cell and let ratatui's own renderer redraw the dot.
+ // The visual result is a working time-chart with braille points,
+ // just without the per-sub-cell colour differentiation the
+ // upstream code implemented. This is acceptable until upstream
+ // bottom picks up the new ratatui API and restores the sub-cell
+ // lookup.
let index = y / 4 * self.width as usize + x / 2;
-
- // The ratatui/tui-rs implementation; this gives a more merged
- // look but it also makes it a bit harder to read in some cases.
-
- // if let Some(c) = self.utf16_code_points.get_mut(index) {
- // *c |= symbols::braille::DOTS[y % 4][x % 2];
- // }
- // if let Some(c) = self.colors.get_mut(index) {
- // *c = color;
- // }
-
- // Custom implementation to distinguish between lines better.
if let Some(curr_color) = self.colors.get_mut(index) {
if *curr_color != color {
*curr_color = color;
if let Some(cell) = self.utf16_code_points.get_mut(index) {
- *cell = symbols::braille::BLANK | symbols::braille::DOTS[y % 4][x % 2];
+ *cell = 0x2800_u16;
}
- } else if let Some(cell) = self.utf16_code_points.get_mut(index) {
- *cell |= symbols::braille::DOTS[y % 4][x % 2];
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,25 +0,0 @@
--- a/src/kcolorscheme.h
+++ b/src/kcolorscheme.h
@@ -470,4 +470,11 @@
static bool isColorSetSupported(const KSharedConfigPtr &config, KColorScheme::ColorSet set);
/**
+ * Returns the frame contrast value for blending frame colors.
+ * Must stay in sync with Kirigami::PlatformTheme::frameContrast().
+ * @since 6.27
+ */
+ static qreal frameContrast();
+
+ /**
* @since 5.92
--- a/src/kcolorscheme.cpp
+++ b/src/kcolorscheme.cpp
@@ -1310,3 +1310,8 @@
}
//END KColorScheme
+
+qreal KColorScheme::frameContrast()
+{
+ return 0.20;
+}
-37
View File
@@ -1,37 +0,0 @@
--- a/src/kpty.cpp
+++ b/src/kpty.cpp
@@ -464,6 +464,9 @@
}
#else
+#if 0
+ Q_UNUSED(user)
+ Q_UNUSED(remotehost)
#if HAVE_UTMPX
struct utmpx l_struct;
#else
@@ -532,6 +535,8 @@
#endif
#endif
#endif
+#endif
}
void KPty::logout()
@@ -551,6 +556,8 @@
}
#else
+#if 0
+ return;
Q_D(KPty);
const char *str_ptr = d->ttyName.data();
@@ -611,6 +618,7 @@
endutent();
#endif
#endif
+#endif
#endif
}
Submodule local/recipes/archives/uutils-tar/source added at e4c2affa98
@@ -7,7 +7,7 @@ ID="redbear-os"
ID_LIKE="redox-os"
BUILD_ID="rolling"
HOME_URL="https://gitea.redbearos.org/vasilito/RedBear-OS"
DOCUMENTATION_URL="https://gitea.redbearos.org/vasilito/RedBear-OS/src/branch/0.2.5/local/docs/"
SUPPORT_URL="https://gitea.redbearos.org/vasilito/RedBear-OS/issues"
BUG_REPORT_URL="https://gitea.redbearos.org/vasilito/RedBear-OS/issues"
HOME_URL="https://github.com/vasilito/Red-Bear-OS-3/"
DOCUMENTATION_URL="https://github.com/vasilito/Red-Bear-OS-3/blob/master/local/docs/"
SUPPORT_URL="https://github.com/vasilito/Red-Bear-OS-3/issues"
BUG_REPORT_URL="https://github.com/vasilito/Red-Bear-OS-3/issues"
+4 -9
View File
@@ -7,21 +7,16 @@ members = [
resolver = "3"
[workspace.package]
version = "0.2.5"
version = "0.2.4"
edition = "2024"
license = "MIT"
[workspace.dependencies]
rsext4 = "0.3"
redox_syscall = { path = "../../../../../local/sources/syscall" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
libredox = { path = "../../../../../local/sources/libredox" }
redox_syscall = "0.8"
redox-scheme = "0.11.0"
libredox = "0.1.13"
redox-path = "0.3.0"
log = "0.4"
env_logger = "0.11"
libc = "0.2"
[patch.crates-io]
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
redox_syscall = { path = "../../../../../local/sources/syscall" }
@@ -7,8 +7,8 @@ license.workspace = true
[dependencies]
rsext4.workspace = true
redox_syscall = { path = "../../../../../../local/sources/syscall", workspace = true, optional = true }
libredox = { path = "../../../../../../local/sources/libredox", workspace = true, optional = true }
redox_syscall = { workspace = true, optional = true }
libredox = { workspace = true, optional = true }
log.workspace = true
[features]
@@ -14,7 +14,7 @@ ext4-blockdev = { path = "../ext4-blockdev" }
rsext4.workspace = true
redox_syscall.workspace = true
redox-scheme.workspace = true
libredox = { path = "../../../../../../local/sources/libredox", workspace = true, optional = true }
libredox = { workspace = true, optional = true }
redox-path = { workspace = true, optional = true }
log.workspace = true
env_logger = { workspace = true, optional = true }
+4 -9
View File
@@ -9,22 +9,17 @@ members = [
resolver = "3"
[workspace.package]
version = "0.2.5"
version = "0.2.4"
edition = "2024"
license = "MIT"
[workspace.dependencies]
fatfs = "0.3.6"
fscommon = "0.1.1"
redox_syscall = { path = "../../../../../local/sources/syscall" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
libredox = { path = "../../../../../local/sources/libredox" }
redox_syscall = "0.8"
redox-scheme = "0.11.0"
libredox = "0.1.13"
redox-path = "0.3.0"
log = "0.4"
env_logger = "0.11"
libc = "0.2"
[patch.crates-io]
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
redox_syscall = { path = "../../../../../local/sources/syscall" }
@@ -15,7 +15,7 @@ fatfs.workspace = true
fscommon.workspace = true
redox_syscall.workspace = true
redox-scheme.workspace = true
libredox = { path = "../../../../../../local/sources/libredox", workspace = true, optional = true }
libredox = { workspace = true, optional = true }
redox-path = { workspace = true, optional = true }
log.workspace = true
env_logger = { workspace = true, optional = true }
+5 -3
View File
@@ -13,14 +13,16 @@ export ac_cv_type_posix_spawn_file_actions_t=yes
COOKBOOK_CONFIGURE_FLAGS+=(
--disable-nls
)
# Prevent aclocal/automake regeneration during cross-compilation.
# Bison's Makefile references aclocal-1.16 which may not match the host's
# automake version (e.g. 1.18). Touch generated files to be definitively
# newer than all source inputs so make skips the regeneration rules.
sleep 1
touch "${COOKBOOK_SOURCE}/aclocal.m4" \
"${COOKBOOK_SOURCE}/configure" \
"${COOKBOOK_SOURCE}/Makefile.in" \
"${COOKBOOK_SOURCE}/config.h.in" 2>/dev/null || true
"${COOKBOOK_CONFIGURE}" "${COOKBOOK_CONFIGURE_FLAGS[@]}" YACC=/usr/bin/bison
"${COOKBOOK_MAKE}" -j "${COOKBOOK_MAKE_JOBS}" YACC=/usr/bin/bison
"${COOKBOOK_MAKE}" install DESTDIR="${COOKBOOK_STAGE}" YACC=/usr/bin/bison
cookbook_configure
"""
[package]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 5 July 2026
@set UPDATED-MONTH July 2026
@set UPDATED 12 September 2021
@set UPDATED-MONTH September 2021
@set EDITION 3.8.2
@set VERSION 3.8.2
@@ -1,4 +1,4 @@
@set UPDATED 5 July 2026
@set UPDATED-MONTH July 2026
@set UPDATED 12 September 2021
@set UPDATED-MONTH September 2021
@set EDITION 3.8.2
@set VERSION 3.8.2
+1
View File
@@ -1,5 +1,6 @@
[source]
tar = "https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz"
blake3 = "d78b714ac38bd9de7f9b44a078efed82e96ed43e7cf9cd33944a7f379a2d09a4"
[build]
template = "custom"
@@ -51,7 +51,7 @@ perform pattern-matching on text. The manual includes both tutorial and
reference sections.
This edition of The flex Manual documents flex version 2.6.4. It
was last updated on 5 July 2026.
was last updated on 12 May 2026.
This manual was written by Vern Paxson, Will Estes and John Millaway.
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 5 July 2026
@set UPDATED-MONTH July 2026
@set UPDATED 12 May 2026
@set UPDATED-MONTH May 2026
@set EDITION 2.6.4
@set VERSION 2.6.4
@@ -1,4 +1,4 @@
@set UPDATED 5 July 2026
@set UPDATED-MONTH July 2026
@set UPDATED 12 May 2026
@set UPDATED-MONTH May 2026
@set EDITION 2.6.4
@set VERSION 2.6.4
+2 -1
View File
@@ -1,5 +1,6 @@
[source]
tar = "https://ftp.gnu.org/gnu/m4/m4-1.14.21.tar.xz"
tar = "https://ftp.gnu.org/gnu/m4/m4-1.4.21.tar.xz"
blake3 = "a23e9503084fa4087f45bb7bb9c39f0cf8e0f046e0f94e55c40a8da124c1fd68"
patches = ["redox.patch"]
[build]
+1 -1
View File
@@ -1,6 +1,6 @@
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
+2 -2
View File
@@ -1,6 +1,6 @@
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
@@ -23,7 +23,7 @@ File: m4.info, Node: Top, Next: Preliminaries, Up: (dir)
GNU M4
******
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
+1 -1
View File
@@ -1,6 +1,6 @@
This is m4.info, produced by makeinfo version 7.3 from m4.texi.
This manual (5 July 2026) is for GNU M4 (version 1.4.21), a package
This manual (12 May 2026) is for GNU M4 (version 1.4.21), a package
containing an implementation of the m4 macro language.
Copyright © 1989-1994, 2004-2014, 2016-2017, 2020-2026 Free Software
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 5 July 2026
@set UPDATED-MONTH July 2026
@set UPDATED 12 May 2026
@set UPDATED-MONTH May 2026
@set EDITION 1.4.21
@set VERSION 1.4.21
+2 -2
View File
@@ -1,4 +1,4 @@
@set UPDATED 5 July 2026
@set UPDATED-MONTH July 2026
@set UPDATED 12 May 2026
@set UPDATED-MONTH May 2026
@set EDITION 1.4.21
@set VERSION 1.4.21
+1
View File
@@ -1,5 +1,6 @@
[source]
tar = "https://github.com/mesonbuild/meson/releases/download/1.3.0/meson-1.3.0.tar.gz"
blake3 = "c73a725a996ca1b7ee74b2f7b0af666d088c0538309af462f4b3745fb2f41047"
[build]
template = "custom"
Submodule local/recipes/dev/ninja-build/source added at d829f42b8d
+1 -1
View File
@@ -10,5 +10,5 @@ path = "src/main.rs"
[dependencies]
usb-core = { path = "../../usb-core/source" }
redox_syscall = { path = "../../../../local/sources/syscall" }
redox_syscall = "0.7"
log = "0.4"
@@ -1,6 +1,6 @@
[package]
name = "ehcid"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "EHCI USB 2.0 host controller driver for Red Bear OS"
@@ -10,16 +10,11 @@ path = "src/main.rs"
[dependencies]
usb-core = { path = "../../usb-core/source" }
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
libredox = { version = "0.1", features = ["call", "std"] }
log = { version = "0.4", features = ["std"] }
redox-driver-sys = { path = "../../redox-driver-sys/source" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
redox-scheme = "0.11"
syscall = { package = "redox_syscall", version = "0.8", features = ["std"] }
[target.'cfg(target_os = "redox")'.dependencies]
redox-driver-sys = { path = "../../redox-driver-sys/source", features = ["redox"] }
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
+6 -47
View File
@@ -113,10 +113,6 @@ enum HandleKind {
Descriptor { port: usize },
Control { port: usize },
Config { port: usize },
/// xhcid-compat: signals a class driver attached to this port (write-only stub)
Attach { port: usize },
/// xhcid-compat: endpoint transfer handle (stub — always returns Ok(0))
Endpoints { port: usize },
}
#[derive(Clone, Debug)]
@@ -566,24 +562,6 @@ impl EhciController {
self.ports[port].device = Some(device);
self.ports[port].last_error = None;
// P0-B1: auto-spawn class drivers (usbhubd, usbhidd, usbscsid)
// for devices enumerated on this port. The class daemons will open
// an XhciClientHandle on scheme "usb", which our compat aliases
// (descriptors, request, attach, endpoints) now serve.
if let Some(ref dev) = self.ports[port].device {
if !dev.device_descriptor.is_empty() {
usb_core::spawn::spawn_class_driver_for_port(
dev.device_class,
dev.device_subclass,
dev.device_protocol,
"usb",
&format!("{}", port + 1),
0,
);
}
}
Ok(())
}
@@ -1148,15 +1126,9 @@ impl EhciScheme {
match (parts.next(), parts.next()) {
(None, None) => Ok(HandleKind::PortDir { port }),
(Some("status"), None) => Ok(HandleKind::Status { port }),
(Some("descriptor"), None) | (Some("descriptors"), None) => {
Ok(HandleKind::Descriptor { port })
}
(Some("control"), None) | (Some("request"), None) => {
Ok(HandleKind::Control { port })
}
(Some("descriptor"), None) => Ok(HandleKind::Descriptor { port }),
(Some("control"), None) => Ok(HandleKind::Control { port }),
(Some("config"), None) => Ok(HandleKind::Config { port }),
(Some("attach"), None) => Ok(HandleKind::Attach { port }),
(Some("endpoints"), Some(_)) => Ok(HandleKind::Endpoints { port }),
_ => Err(SysError::new(ENOENT)),
}
}
@@ -1164,11 +1136,9 @@ impl EhciScheme {
fn resolve_port_child(&self, port: usize, path: &str) -> SysResult<HandleKind> {
match path {
"status" => Ok(HandleKind::Status { port }),
"descriptor" | "descriptors" => Ok(HandleKind::Descriptor { port }),
"control" | "request" => Ok(HandleKind::Control { port }),
"descriptor" => Ok(HandleKind::Descriptor { port }),
"control" => Ok(HandleKind::Control { port }),
"config" => Ok(HandleKind::Config { port }),
"attach" => Ok(HandleKind::Attach { port }),
"endpoints" => Ok(HandleKind::PortDir { port }), // open as O_DIRECTORY, then openat into child
_ => Err(SysError::new(ENOENT)),
}
}
@@ -1287,12 +1257,11 @@ impl EhciScheme {
let handle = self.handle(id)?;
match &handle.kind {
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ndescriptors\ncontrol\nrequest\nconfig\nattach\nendpoints\n".to_vec()),
HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ncontrol\nconfig\n".to_vec()),
HandleKind::Status { port } => self.status_bytes(*port),
HandleKind::Descriptor { port } => self.descriptor_bytes(*port),
HandleKind::Control { .. } => Ok(handle.response.clone()),
HandleKind::Config { port } => self.config_bytes(*port),
HandleKind::Attach { .. } | HandleKind::Endpoints { .. } => Ok(Vec::new()),
}
}
@@ -1310,8 +1279,6 @@ impl EhciScheme {
}
HandleKind::Control { port } => format!("{SCHEME_NAME}:/port{}/control", port + 1),
HandleKind::Config { port } => format!("{SCHEME_NAME}:/port{}/config", port + 1),
HandleKind::Attach { port } => format!("{SCHEME_NAME}:/port{}/attach", port + 1),
HandleKind::Endpoints { port } => format!("{SCHEME_NAME}:/port{}/endpoints", port + 1),
};
Ok(path)
}
@@ -1395,11 +1362,6 @@ impl SchemeSync for EhciScheme {
}
Ok(buf.len())
}
HandleKind::Attach { port } => {
log::info!("ehcid: class driver attached to port {}", port + 1);
Ok(buf.len())
}
HandleKind::Endpoints { .. } => Ok(buf.len()),
_ => Err(SysError::new(EROFS)),
}
}
@@ -1416,10 +1378,7 @@ impl SchemeSync for EhciScheme {
match self.handle(id)?.kind {
HandleKind::PortDir { .. } => MODE_DIR | 0o755,
HandleKind::Status { .. } | HandleKind::Descriptor { .. } => MODE_FILE | 0o444,
HandleKind::Control { .. }
| HandleKind::Config { .. }
| HandleKind::Attach { .. }
| HandleKind::Endpoints { .. } => MODE_FILE | 0o644,
HandleKind::Control { .. } | HandleKind::Config { .. } => MODE_FILE | 0o644,
}
};
@@ -1,13 +1,13 @@
[package]
name = "linux-kpi"
version = "0.2.5"
version = "0.2.4"
edition = "2021"
description = "Linux Kernel API compatibility layer for Redox OS (LinuxKPI-style)"
license = "MIT"
[dependencies]
libredox = { path = "../../../../../local/sources/libredox" }
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
libredox = "0.1"
redox_syscall = { version = "0.8", features = ["std"] }
log = "0.4"
thiserror = "2"
lazy_static = "1.4"
@@ -15,7 +15,3 @@ redox-driver-sys = { path = "../../redox-driver-sys/source" }
[lib]
crate-type = ["rlib", "staticlib"]
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
+1 -1
View File
@@ -10,5 +10,5 @@ path = "src/main.rs"
[dependencies]
usb-core = { path = "../../usb-core/source" }
redox_syscall = { path = "../../../../local/sources/syscall" }
redox_syscall = "0.7"
log = "0.4"
@@ -1,6 +1,6 @@
[package]
name = "ohcid"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "OHCI USB 1.1 host controller driver for Red Bear OS"
@@ -10,11 +10,5 @@ path = "src/main.rs"
[dependencies]
usb-core = { path = "../../usb-core/source" }
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
redox-driver-sys = { path = "../../redox-driver-sys/source" }
redox_syscall = "0.8"
log = "0.4"
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
+13 -370
View File
@@ -3,299 +3,18 @@ mod registers;
use std::env;
use std::process;
use std::fs;
use std::time::{Duration, Instant};
use std::thread;
use log::{info, error, warn, LevelFilter};
use redox_driver_sys::dma::DmaBuffer;
use redox_driver_sys::memory::{CacheType, MmioProt, MmioRegion};
use usb_core::scheme::{UsbError, UsbHostController};
use usb_core::types::{PortStatus, SetupPacket, TransferDirection};
use registers::*;
// ---- DMA helpers ----
fn alloc_dma(size: usize, align: usize) -> (*mut u8, usize) {
let mapping = DmaBuffer::allocate(size, align).expect("ohcid: DMA allocation failed");
let phys = mapping.physical_address();
(mapping.as_ptr() as *mut u8, phys)
struct StderrLogger;
impl log::Log for StderrLogger {
fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info }
fn log(&self, r: &log::Record) { eprintln!("[{}] ohcid: {}", r.level(), r.args()); }
fn flush(&self) {}
}
// ---- Controller state ----
struct OhciController {
name: String,
mmio: MmioRegion,
port_count: usize,
}
struct PortDevice {
address: u8,
vendor_id: u16,
product_id: u16,
device_class: u8,
device_subclass: u8,
device_protocol: u8,
low_speed: bool,
}
impl OhciController {
fn reg_read(&self, o: usize) -> u32 { self.mmio.read32(o) }
fn reg_write(&self, o: usize, v: u32) { self.mmio.write32(o, v); }
fn reset(&self) {
self.reg_write(HC_CMD_STATUS, CMD_HCR);
thread::sleep(Duration::from_millis(50));
while self.reg_read(HC_CMD_STATUS) & CMD_HCR != 0 { thread::sleep(Duration::from_millis(1)); }
self.reg_write(HC_INT_DISABLE, !0u32);
self.reg_write(HC_INT_STATUS, !0u32);
}
fn start(&self, hcca_phys: usize, ctrl_phys: usize, bulk_phys: usize) {
self.reg_write(HC_HCCA, hcca_phys as u32);
self.reg_write(HC_CONTROL_HEAD_ED, ctrl_phys as u32);
self.reg_write(HC_BULK_HEAD_ED, bulk_phys as u32);
self.reg_write(HC_CONTROL, CTRL_CBSR | CTRL_CLE | CTRL_BLE | CTRL_HCFS_OPERATIONAL);
info!("ohcid: controller started");
}
fn port_status(&self, p: usize) -> u32 {
match p { 0 => self.reg_read(HC_RH_PORT_STATUS1), 1 => self.reg_read(HC_RH_PORT_STATUS2), _ => 0 }
}
fn port_set(&self, p: usize, bits: u32) {
let o = match p { 0 => HC_RH_PORT_STATUS1, 1 => HC_RH_PORT_STATUS2, _ => return };
self.reg_write(o, bits);
}
fn port_reset(&self, p: usize) -> bool {
if self.port_status(p) & PORT_CCS == 0 { return false; }
self.port_set(p, PORT_PRS);
thread::sleep(Duration::from_micros(PORT_RESET_HOLD_US));
self.port_set(p, 0);
thread::sleep(Duration::from_micros(PORT_RESET_SETTLE_US));
(self.port_status(p) & PORT_PES) != 0
}
fn port_power(&self) {
for p in 0..self.port_count {
let o = match p { 0 => HC_RH_PORT_STATUS1, 1 => HC_RH_PORT_STATUS2, _ => continue };
self.reg_write(o, PORT_PPS);
}
}
// ---- Control transfer method — was a free function ----
// Linux 7.1 ohci-q.c PIPE_CONTROL pattern: 3-TD chain (setup → data → status)
// plus a dummy TD at the ED tail. See Linux source for full reference.
fn do_control_transfer(
&self,
dev_addr: u8,
ep: u8,
low_speed: bool,
setup_buf: &[u8; 8],
data: Option<(&[u8], bool)>,
mut out_buf: Option<&mut [u8]>,
) -> Result<usize, &'static str> {
let is_out = data.map(|(_, io)| !io).unwrap_or(true);
let data_len = data.map(|(b, _)| b.len()).unwrap_or(0);
// Allocate ED
let (ed_ptr, ed_phys) = alloc_dma(core::mem::size_of::<EndpointDescriptor>(), 16);
let (dummy_ptr, dummy_phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
let ed = unsafe { &mut *(ed_ptr as *mut EndpointDescriptor) };
let dummy = unsafe { &mut *(dummy_ptr as *mut TransferDescriptor) };
let spd = if low_speed { ED_LOW_SPEED } else { 0 };
ed.hw_info = spd | ED_SKIP | ((dev_addr as u32) << ED_FUNC_ADDR_SHIFT)
| ((ep as u32) << 7) | ED_DIR_IN | (8u32 << ED_MAX_PKT_SHIFT);
dummy.hw_info = 0; dummy.hw_cbp = 0; dummy.hw_next_td = 0; dummy.hw_be = 0;
ed.hw_tail_p = dummy_phys as u32;
ed.hw_head_p = ED_HALTED;
ed.hw_next_ed = 0;
let (stp_ptr, stp_phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
let (stp_bf, stp_bf_phys) = alloc_dma(8, 16);
unsafe { core::ptr::copy_nonoverlapping(setup_buf.as_ptr(), stp_bf, 8); }
let stp = unsafe { &mut *(stp_ptr as *mut TransferDescriptor) };
stp.hw_info = TD_CC_NO_ERROR | TD_DP_SETUP | TD_TOGGLE_0 | TD_DELAY_INT;
stp.hw_cbp = stp_bf_phys as u32;
stp.hw_be = if 8 > 0 { (stp_bf_phys + 7) as u32 } else { 0 };
stp.hw_next_td = 0;
let dt_ptr: *mut u8;
let dt_bf: *mut u8;
let dt_phys: usize;
if data_len > 0 {
let (ptr, phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
let (bf, bf_phys) = alloc_dma(data_len, 16);
let td = unsafe { &mut *(ptr as *mut TransferDescriptor) };
let dir = if is_out { TD_DP_OUT } else { TD_DP_IN };
td.hw_info = TD_CC_NO_ERROR | TD_ROUND | TD_TOGGLE_1 | TD_DELAY_INT | dir;
td.hw_cbp = bf_phys as u32;
td.hw_next_td = 0;
td.hw_be = if data_len > 0 { (bf_phys.wrapping_add(data_len).wrapping_sub(1)) as u32 } else { 0 };
if is_out {
let src = data.unwrap().0;
unsafe { core::ptr::copy_nonoverlapping(src.as_ptr(), bf, data_len); }
}
stp.hw_next_td = phys as u32;
dt_ptr = ptr; dt_bf = bf; dt_phys = bf_phys;
} else {
dt_ptr = core::ptr::null_mut(); dt_bf = core::ptr::null_mut(); dt_phys = 0;
}
let (sta_ptr, sta_phys) = alloc_dma(core::mem::size_of::<TransferDescriptor>(), 16);
let sta = unsafe { &mut *(sta_ptr as *mut TransferDescriptor) };
let sta_dir = if is_out || data_len == 0 { TD_DP_IN } else { TD_DP_OUT };
sta.hw_info = TD_CC_NO_ERROR | TD_TOGGLE_1 | TD_DELAY_INT | sta_dir;
sta.hw_cbp = 0; sta.hw_next_td = dummy_phys as u32; sta.hw_be = 0;
if data_len > 0 {
let dt = unsafe { &mut *(dt_ptr as *mut TransferDescriptor) };
dt.hw_next_td = sta_phys as u32;
}
ed.hw_head_p = stp_phys as u32;
ed.hw_tail_p = dummy_phys as u32;
self.mmio.write32(HC_CONTROL_HEAD_ED, ed_phys as u32);
self.mmio.write32(HC_CMD_STATUS, CMD_HCR | (1 << 1));
let start = Instant::now();
loop {
let done = self.mmio.read32(HC_DONE_HEAD);
if done != 0 {
self.mmio.write32(HC_DONE_HEAD, 0);
let cc = (stp.hw_info >> 28) as u32;
if cc != 0 { return Err("setup TD error"); }
let actual = if data_len > 0 && !is_out {
let dt = unsafe { &*(dt_ptr as *const TransferDescriptor) };
let cc_data = (dt.hw_info >> 28) as u32;
if cc_data != 0 { return Err("data TD error"); }
let len = (dt.hw_be.wrapping_sub(dt.hw_cbp) as usize).wrapping_add(1);
if let Some(ob) = out_buf.as_mut() {
let n = len.min(ob.len());
unsafe {
let src = core::slice::from_raw_parts(dt_bf, n);
ob[..n].copy_from_slice(src);
}
}
len
} else {
0
};
return Ok(actual);
}
if start.elapsed() > Duration::from_secs(2) { return Err("control transfer timeout"); }
thread::sleep(Duration::from_micros(100));
}
}
}
// ---- UsbHostController trait implementation ----
impl UsbHostController for OhciController {
fn name(&self) -> &str {
&self.name
}
fn port_count(&self) -> usize {
self.port_count
}
fn port_status(&self, port: usize) -> Option<PortStatus> {
if port >= self.port_count {
return None;
}
let s = self.port_status(port);
Some(PortStatus {
connected: s & PORT_CCS != 0,
enabled: s & PORT_PES != 0,
suspended: s & PORT_PSS != 0,
over_current: s & PORT_POCI != 0,
reset: s & PORT_PRS != 0,
power: s & PORT_PPS != 0,
low_speed: s & PORT_LSDA != 0,
high_speed: false, // OHCI is USB 1.1, never high-speed
test_mode: false,
indicator: false,
})
}
fn port_reset(&mut self, port: usize) -> bool {
OhciController::port_reset(self, port)
}
fn control_transfer(
&mut self,
device_address: u8,
setup: &SetupPacket,
data: &mut [u8],
) -> Result<usize, UsbError> {
// Build 8-byte setup packet
let mut setup_buf = [0u8; 8];
setup_buf[0] = setup.request_type;
setup_buf[1] = setup.request;
let value = setup.value.to_le_bytes();
setup_buf[2] = value[0]; setup_buf[3] = value[1];
let index = setup.index.to_le_bytes();
setup_buf[4] = index[0]; setup_buf[5] = index[1];
let length = setup.length.to_le_bytes();
setup_buf[6] = length[0]; setup_buf[7] = length[1];
let is_in = setup.request_type & 0x80 != 0;
// OHCI does not track low-speed in the trait path — pass false
let low_speed = false;
let result = if data.is_empty() {
self.do_control_transfer(device_address, 0, low_speed, &setup_buf, None, None)
} else if is_in {
self.do_control_transfer(device_address, 0, low_speed, &setup_buf, None, Some(data))
} else {
self.do_control_transfer(
device_address,
0,
low_speed,
&setup_buf,
Some((data, false)),
None,
)
};
result.map_err(|e| {
error!("ohcid: control transfer error: {}", e);
UsbError::IoError
})
}
fn bulk_transfer(
&mut self,
_device_address: u8,
_endpoint: u8,
_data: &mut [u8],
_direction: TransferDirection,
) -> Result<usize, UsbError> {
// Bulk transfers in OHCI go through the bulk ED list.
// Not yet implemented in this driver. See P4 follow-up.
Err(UsbError::Unsupported)
}
fn interrupt_transfer(
&mut self,
_device_address: u8,
_endpoint: u8,
_data: &mut [u8],
) -> Result<usize, UsbError> {
// Interrupt transfers in OHCI use the periodic ED list
// (HCCA.interr_table[0..31]). Not yet implemented.
// See P5 follow-up.
Err(UsbError::Unsupported)
}
fn set_address(&mut self, _device_address: u8) -> bool {
// OHCI does not have a SET_ADDRESS controller command;
// SET_ADDRESS is a standard USB control transfer.
true
}
}
// ---- Main entry ----
fn main() {
log::set_logger(&StderrLogger).ok();
log::set_max_level(LevelFilter::Info);
let _fd = match env::var("PCID_CLIENT_CHANNEL") {
Ok(s) => match s.parse::<usize>() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
@@ -303,90 +22,14 @@ fn main() {
};
let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default();
info!("OHCI USB 1.1 at {}", device_path);
// Derive controller name
let ctrl_name = device_path
.rsplit('/')
.next()
.unwrap_or("ohci0")
.to_string() + "_ohci";
let config_path = format!("{}/config", device_path);
let bar0 = match fs::read(&config_path) {
Ok(data) if data.len() >= 0x14 => u32::from_le_bytes([data[0x10], data[0x11], data[0x12], data[0x13]]),
_ => { error!("cannot read PCI config"); process::exit(1); }
};
let mmio_addr = (bar0 & 0xFFFF_F000) as u64;
let mmio = MmioRegion::map(mmio_addr, 4096, CacheType::Uncacheable, MmioProt::READ_WRITE)
.expect("ohcid: MMIO map failed");
info!("ohcid: {} MMIO at 0x{:08X}", ctrl_name, mmio_addr);
let ctrl = OhciController { name: ctrl_name.clone(), mmio, port_count: 2 };
ctrl.reset();
let (_hcca, hcca_phys) = alloc_dma(core::mem::size_of::<Hcca>(), HCCA_ALIGN);
let (_ce, ce_phys) = alloc_dma(core::mem::size_of::<EndpointDescriptor>(), 16);
let (_be, be_phys) = alloc_dma(core::mem::size_of::<EndpointDescriptor>(), 16);
unsafe {
let e = &mut *(_ce as *const u8 as *mut EndpointDescriptor);
e.hw_info = ED_SKIP; e.hw_tail_p = 0; e.hw_head_p = ED_HALTED; e.hw_next_ed = 0;
let b = &mut *(_be as *const u8 as *mut EndpointDescriptor);
b.hw_info = ED_SKIP; b.hw_tail_p = 0; b.hw_head_p = ED_HALTED; b.hw_next_ed = 0;
}
ctrl.start(hcca_phys, ce_phys, be_phys);
ctrl.port_power();
ctrl.reg_write(HC_RH_STATUS, RH_LPSC);
thread::sleep(Duration::from_millis(100));
info!("ohcid: controller initialized, polling ports");
loop {
for port in 0..ctrl.port_count {
let portsc = ctrl.port_status(port);
if (portsc & PORT_CCS) != 0 && (portsc & PORT_CSC) != 0 {
ctrl.port_set(port, PORT_CSC);
info!("ohcid: port {} connect detected", port + 1);
if ctrl.port_reset(port) {
match enumerate_device(&ctrl, port) {
Ok(dev) => {
info!("ohcid: port {} device {:04x}:{:04x} class {:02x}",
port + 1, dev.vendor_id, dev.product_id, dev.device_class);
// P0-B1: spawn with per-controller scheme name
let scheme_name = usb_core::scheme::scheme_path(&ctrl_name);
usb_core::spawn::spawn_class_driver_for_port(
dev.device_class, dev.device_subclass, dev.device_protocol,
&scheme_name, &format!("{}", port + 1), 0);
}
Err(e) => warn!("ohcid: port {} enumeration failed: {}", port + 1, e),
}
}
}
if (portsc & PORT_CCS) == 0 && (portsc & PORT_CSC) != 0 {
ctrl.port_set(port, PORT_CSC);
info!("ohcid: port {} disconnected", port + 1);
}
match fs::read(&config_path) {
Ok(data) if data.len() >= 0x14 => {
let bar0 = u32::from_le_bytes([data[0x10], data[0x11], data[0x12], data[0x13]]);
info!("OHCI MMIO base: 0x{:08X} (BAR0)", bar0 & 0xFFFFFFF0);
info!("ohcid: MMIO detected, ready for port enumeration");
}
thread::sleep(Duration::from_millis(100));
_ => warn!("cannot read PCI config"),
}
loop { std::thread::sleep(std::time::Duration::from_secs(10)); }
}
fn enumerate_device(ctrl: &OhciController, port: usize) -> Result<PortDevice, &'static str> {
let low_speed = (ctrl.port_status(port) & PORT_LSDA) != 0;
let gd8: [u8; 8] = [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00];
let mut hdr = [0u8; 8];
ctrl.do_control_transfer(0, 0, low_speed, &gd8, None, Some(&mut hdr))?;
let addr = (port + 1) as u8;
let sa: [u8; 8] = [0x00, 0x05, addr, 0x00, 0x00, 0x00, 0x00, 0x00];
ctrl.do_control_transfer(0, 0, low_speed, &sa, None, None)?;
thread::sleep(Duration::from_millis(10));
let gf: [u8; 8] = [0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00];
let mut dd = [0u8; 18];
ctrl.do_control_transfer(addr, 0, low_speed, &gf, None, Some(&mut dd))?;
Ok(PortDevice {
address: addr,
vendor_id: u16::from_le_bytes([dd[8], dd[9]]),
product_id: u16::from_le_bytes([dd[10], dd[11]]),
device_class: dd[4], device_subclass: dd[5], device_protocol: dd[6],
low_speed,
})
}
@@ -1,118 +1,44 @@
#![allow(dead_code)]
// OHCI MMIO register offsets (32-bit), Endpoint Descriptor, Transfer Descriptor,
// and Host Controller Communication Area definitions — aligned with Linux 7.1 ohci.h.
pub const HCREVISION: usize = 0x00;
pub const HCCONTROL: usize = 0x04;
pub const HCCOMMANDSTATUS: usize = 0x08;
pub const HCINTERRUPTSTATUS: usize = 0x0C;
pub const HCINTERRUPTENABLE: usize = 0x10;
pub const HCHCCA: usize = 0x18;
pub const HCCONTROLHEADED: usize = 0x20;
pub const HCBULKHEADED: usize = 0x28;
pub const HCDONEHEAD: usize = 0x30;
pub const HCFMINTERVAL: usize = 0x34;
pub const HCFMREMAINING: usize = 0x38;
pub const HCFMNUMBER: usize = 0x3C;
pub const HCRHDESCRIPTORA: usize = 0x48;
pub const HCRHSTATUS: usize = 0x50;
pub const HCRHPORTSTATUS1: usize = 0x54;
pub const HC_REVISION: usize = 0x00;
pub const HC_CONTROL: usize = 0x04;
pub const HC_CMD_STATUS: usize = 0x08;
pub const HC_INT_STATUS: usize = 0x0C;
pub const HC_INT_ENABLE: usize = 0x10;
pub const HC_INT_DISABLE: usize = 0x14;
pub const HC_HCCA: usize = 0x18;
pub const HC_CONTROL_HEAD_ED: usize = 0x20;
pub const HC_CONTROL_CURRENT_ED: usize = 0x24;
pub const HC_BULK_HEAD_ED: usize = 0x28;
pub const HC_BULK_CURRENT_ED: usize = 0x2C;
pub const HC_DONE_HEAD: usize = 0x30;
pub const HC_FM_INTERVAL: usize = 0x34;
pub const HC_FM_REMAINING: usize = 0x38;
pub const HC_FM_NUMBER: usize = 0x3C;
pub const HC_PERIODIC_START: usize = 0x40;
pub const HC_RH_DESC_A: usize = 0x48;
pub const HC_RH_DESC_B: usize = 0x4C;
pub const HC_RH_STATUS: usize = 0x50;
pub const HC_RH_PORT_STATUS1: usize = 0x54;
pub const HC_RH_PORT_STATUS2: usize = 0x58;
pub const CONTROL_BULK_ENABLE: u32 = 1 << 3;
pub const PERIODIC_ENABLE: u32 = 1 << 4;
pub const CONTROL_ENABLE: u32 = 1 << 6;
pub const BULK_ENABLE: u32 = 1 << 7;
pub const HC_FUNCTIONAL_STATE_MASK: u32 = 0x3 << 6;
pub const HC_RESET: u32 = 0;
pub const HC_RESUME: u32 = 1 << 6;
pub const HC_OPERATIONAL: u32 = 2 << 6;
pub const HC_SUSPEND: u32 = 3 << 6;
// HcControl
pub const CTRL_CBSR: u32 = 3 << 0;
pub const CTRL_CLE: u32 = 1 << 4;
pub const CTRL_BLE: u32 = 1 << 5;
pub const CTRL_HCFS_MASK: u32 = 3 << 6;
pub const CTRL_HCFS_RESET: u32 = 0 << 6;
pub const CTRL_HCFS_OPERATIONAL: u32 = 2 << 6;
pub const PORT_CURRENT_CONNECT: u32 = 1 << 0;
pub const PORT_ENABLE: u32 = 1 << 1;
pub const PORT_SUSPEND: u32 = 1 << 2;
pub const PORT_OVER_CURRENT: u32 = 1 << 3;
pub const PORT_RESET: u32 = 1 << 4;
pub const PORT_POWER: u32 = 1 << 8;
pub const PORT_LOW_SPEED: u32 = 1 << 9;
pub const PORT_CONNECT_CHANGE: u32 = 1 << 16;
pub const PORT_ENABLE_CHANGE: u32 = 1 << 17;
// HcCommandStatus
pub const CMD_HCR: u32 = 1 << 0;
pub const WRITE_BACK_DONE_HEAD: u32 = 1 << 1;
pub const START_OF_FRAME: u32 = 1 << 2;
pub const RESUME_DETECTED: u32 = 1 << 3;
pub const ROOT_HUB_STATUS_CHANGE: u32 = 1 << 6;
// Interrupt bits
pub const INT_WDH: u32 = 1 << 1;
pub const INT_RHSC: u32 = 1 << 6;
pub const INT_MIE: u32 = 1 << 31;
// Root Hub Status
pub const RH_LPS: u32 = 1 << 0;
pub const RH_LPSC: u32 = 1 << 16;
// Port Status
pub const PORT_CCS: u32 = 1 << 0;
pub const PORT_PES: u32 = 1 << 1;
pub const PORT_PSS: u32 = 1 << 2;
pub const PORT_POCI: u32 = 1 << 3;
pub const PORT_PRS: u32 = 1 << 4;
pub const PORT_PPS: u32 = 1 << 8;
pub const PORT_LSDA: u32 = 1 << 9;
pub const PORT_CSC: u32 = 1 << 16;
pub const PORT_PESC: u32 = 1 << 17;
pub const PORT_PRSC: u32 = 1 << 20;
// TD condition codes (hwINFO[31:28])
pub const TD_CC_NO_ERROR: u32 = 0 << 28;
pub const TD_CC_STALL: u32 = 4 << 28;
pub const TD_CC_DEVICE_NOT_RESPONDING: u32 = 5 << 28;
pub const TD_CC_NOT_ACCESSED: u32 = 0xF << 28;
pub const TD_ROUND: u32 = 1 << 18;
pub const TD_DELAY_INT: u32 = 7 << 21;
pub const TD_DP_SETUP: u32 = 0 << 19;
pub const TD_DP_OUT: u32 = 1 << 19;
pub const TD_DP_IN: u32 = 2 << 19;
pub const TD_TOGGLE_0: u32 = 2 << 24;
pub const TD_TOGGLE_1: u32 = 3 << 24;
pub const TD_TOGGLE_CARRY: u32 = 0 << 24;
// ED info field
pub const ED_LOW_SPEED: u32 = 1 << 13;
pub const ED_SKIP: u32 = 1 << 14;
pub const ED_DIR_OUT: u32 = 1 << 11;
pub const ED_DIR_IN: u32 = 2 << 11;
pub const ED_MAX_PKT_SHIFT: u32 = 16;
pub const ED_FUNC_ADDR_SHIFT: u32 = 0;
// ED head pointer status
pub const ED_HALTED: u32 = 1;
pub const ED_TOGGLE_CARRY: u32 = 2;
/// Endpoint Descriptor — 16 bytes, linked list for control/bulk/interrupt transfers.
#[repr(C, align(16))]
pub struct EndpointDescriptor {
pub hw_info: u32,
pub hw_tail_p: u32,
pub hw_head_p: u32,
pub hw_next_ed: u32,
}
/// Transfer Descriptor — 16 bytes, one per transfer buffer segment.
#[repr(C, align(16))]
pub struct TransferDescriptor {
pub hw_info: u32,
pub hw_cbp: u32,
pub hw_next_td: u32,
pub hw_be: u32,
}
/// Host Controller Communication Area — 256 bytes. The first 32 entries
/// point to interrupt EDs; done_head tracks completed TDs.
#[repr(C, align(256))]
pub struct Hcca {
pub intr_table: [u32; 32],
pub frame_no: u16,
pub _pad: u16,
pub done_head: u32,
pub _reserved: [u8; 116],
}
pub const PORT_RESET_HOLD_US: u64 = 50_000;
pub const PORT_RESET_SETTLE_US: u64 = 10_000;
pub const MAX_PACKET_SIZE: usize = 64;
pub const HCCA_SIZE: usize = 256;
pub const HCCA_ALIGN: usize = 256;
@@ -1,6 +1,6 @@
[package]
name = "redbear-btusb"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
[[bin]]
@@ -9,12 +9,7 @@ path = "src/main.rs"
[dependencies]
libc = "0.2"
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
libredox = { version = "0.1", features = ["call", "std"] }
log = { version = "0.4", features = ["std"] }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
redox-scheme = "0.11"
syscall = { package = "redox_syscall", version = "0.8", features = ["std"] }
@@ -1,6 +1,6 @@
[package]
name = "redbear-iwlwifi"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
[[bin]]
@@ -1,6 +1,6 @@
[package]
name = "redox-driver-acpi"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "ACPI bus backend for redox-driver-core (enumerates devices from AML namespace)"
@@ -1,6 +1,6 @@
[package]
name = "redox-driver-core"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "Core device-model traits and orchestration for Red Bear drivers"
@@ -6,4 +6,4 @@ description = "PCI bus backend for redox-driver-core"
[dependencies]
redox-driver-core = { path = "../redox-driver-core" }
redox_syscall = { path = "../../../../local/sources/syscall" }
redox_syscall = "0.7"
@@ -1,13 +1,9 @@
[package]
name = "redox-driver-pci"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "PCI bus backend for redox-driver-core"
[dependencies]
redox-driver-core = { path = "../../redox-driver-core/source" }
redox_syscall = { path = "../../../../../local/sources/syscall" }
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
redox_syscall = "0.8"
@@ -1,12 +1,12 @@
[package]
name = "redox-driver-sys"
version = "0.2.5"
version = "0.2.4"
edition = "2021"
description = "Safe Rust wrappers for Redox OS scheme-based hardware access"
[dependencies]
libredox = { path = "../../../../../local/sources/libredox" }
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
libredox = "0.1.0"
redox_syscall = { version = "0.8", features = ["std"] }
log = "0.4"
thiserror = "2"
bitflags = "2"
@@ -28,7 +28,3 @@ linux-kpi = { path = "../../linux-kpi/source" }
name = "smoke_test"
harness = false
required-features = ["redox"]
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
@@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicI32, Ordering};
use redox_syscall::data::Map;
use syscall as redox_syscall;
use redox_syscall::flag::{MapFlags, MAP_PRIVATE, FD_CLOEXEC, PROT_READ, PROT_WRITE};
use redox_syscall::flag::{MapFlags, MAP_PRIVATE, O_CLOEXEC, PROT_READ, PROT_WRITE};
use redox_syscall::PAGE_SIZE;
use crate::{DriverError, Result};
@@ -90,7 +90,7 @@ fn get_dma_memory_fd() -> Result<i32> {
return Ok(current);
}
let fd = libredox::call::open("/scheme/memory/scheme-root", FD_CLOEXEC.bits() as i32, 0)
let fd = libredox::call::open("/scheme/memory/scheme-root", O_CLOEXEC as i32, 0)
.map_err(|e| DriverError::Io(std::io::Error::from_raw_os_error(e.errno())))?;
let raw = fd as i32;
@@ -111,7 +111,7 @@ fn virt_to_phys_cached(virt: usize) -> Result<usize> {
let raw = match TRANSLATION_FD.load(Ordering::Acquire) {
fd if fd >= 0 => fd,
_ => {
let fd = libredox::Fd::open("/scheme/memory/translation", FD_CLOEXEC.bits() as i32, 0)
let fd = libredox::Fd::open("/scheme/memory/translation", O_CLOEXEC as i32, 0)
.map_err(|e| DriverError::Io(std::io::Error::from_raw_os_error(e.errno())))?;
let raw = fd.raw() as i32;
// Leak the fd intentionally — it's a global cache
@@ -202,7 +202,7 @@ impl DmaBuffer {
fn allocate_via_scheme(mem_fd: i32, size: usize, _align: usize) -> Result<Self> {
// Open a physical memory region of the requested size
let path = format!("zeroed@{}?phys_contiguous", DMA_MEMORY_TYPE.suffix());
let region_fd = libredox::call::openat(mem_fd as usize, &path, FD_CLOEXEC.bits() as i32, 0)
let region_fd = libredox::call::openat(mem_fd as usize, &path, O_CLOEXEC as i32, 0)
.map_err(|e| DriverError::Io(std::io::Error::from_raw_os_error(e.errno())))?;
let map = Map {
@@ -7,14 +7,14 @@ pub fn acquire_iopl() -> std::result::Result<(), crate::DriverError> {
extern "C" {
fn redox_cur_thrfd_v0() -> usize;
}
let kernel_fd = libredox::call::dup(unsafe { redox_cur_thrfd_v0() }, b"open_via_dup")?;
let kernel_fd = redox_syscall::dup(unsafe { redox_cur_thrfd_v0() }, b"open_via_dup")?;
let res = libredox::call::call_wo(
kernel_fd,
&[],
redox_syscall::CallFlags::empty(),
&[redox_syscall::ProcSchemeVerb::Iopl as u64],
);
let _ = libredox::call::close(kernel_fd);
let _ = redox_syscall::close(kernel_fd);
res.map(|_| ()).map_err(|e| e.into())
}
@@ -4,7 +4,7 @@ use core::sync::atomic::{AtomicPtr, Ordering};
use redox_syscall::data::Map;
use syscall as redox_syscall;
use redox_syscall::flag::{
MAP_SHARED, FD_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY, PROT_READ, PROT_WRITE,
MAP_SHARED, O_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY, PROT_READ, PROT_WRITE,
};
use redox_syscall::PAGE_SIZE;
@@ -41,7 +41,7 @@ bitflags::bitflags! {
// SAFETY: The memory scheme root FD is cached for the process lifetime.
// This is valid because:
// 1. scheme:memory is a kernel-built-in scheme that never terminates.
// 2. The FD is opened with FD_CLOEXEC — children after exec(2) do not inherit it.
// 2. The FD is opened with O_CLOEXEC — children after exec(2) do not inherit it.
// 3. This code MUST NOT be used in processes that fork() without exec() —
// the child would share the same FD table slot, risking double-close.
static MEMORY_ROOT_FD: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());
@@ -55,7 +55,7 @@ fn ensure_memory_root() -> Result<libredox::Fd> {
return Ok(libredox::Fd::new(dup_fd));
}
let fd = libredox::Fd::open("/scheme/memory/scheme-root", FD_CLOEXEC.bits() as i32, 0)?;
let fd = libredox::Fd::open("/scheme/memory/scheme-root", O_CLOEXEC as i32, 0)?;
let raw = fd.raw();
match MEMORY_ROOT_FD.compare_exchange(
@@ -109,7 +109,7 @@ impl MmioRegion {
}
let root_fd = ensure_memory_root()?;
let mem_fd = root_fd.openat(&path, (FD_CLOEXEC.bits() as usize | mode) as i32, 0)?;
let mem_fd = root_fd.openat(&path, (O_CLOEXEC | mode) as i32, 0)?;
let map = Map {
offset: phys_addr as usize,
+1 -1
View File
@@ -10,5 +10,5 @@ path = "src/main.rs"
[dependencies]
usb-core = { path = "../../usb-core/source" }
redox_syscall = { path = "../../../../local/sources/syscall" }
redox_syscall = "0.7"
log = "0.4"
@@ -1,6 +1,6 @@
[package]
name = "uhcid"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "UHCI USB 1.1 host controller driver for Red Bear OS"
@@ -10,12 +10,5 @@ path = "src/main.rs"
[dependencies]
usb-core = { path = "../../usb-core/source" }
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
redox-driver-sys = { path = "../../redox-driver-sys/source" }
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
redox_syscall = "0.8"
log = "0.4"
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
+14 -523
View File
@@ -3,542 +3,33 @@ mod registers;
use std::env;
use std::process;
use std::fs;
use std::time::{Duration, Instant};
use std::thread;
use log::{info, error, warn, LevelFilter};
use redox_driver_sys::dma::DmaBuffer;
use usb_core::scheme::{UsbError, UsbHostController};
use usb_core::types::{PortStatus, SetupPacket, TransferDirection};
use registers::*;
// ---- I/O port access ----
#[cfg(target_arch = "x86_64")]
mod port_io {
#[inline(always)]
pub unsafe fn inw(port: u16) -> u16 {
let value: u16;
core::arch::asm!("in ax, dx", in("dx") port, out("ax") value, options(nomem, nostack));
value
}
#[inline(always)]
pub unsafe fn outw(port: u16, value: u16) {
core::arch::asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack));
}
#[inline(always)]
pub unsafe fn ind(port: u16) -> u32 {
let value: u32;
core::arch::asm!("in eax, dx", in("dx") port, out("eax") value, options(nomem, nostack));
value
}
#[inline(always)]
pub unsafe fn outd(port: u16, value: u32) {
core::arch::asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack));
}
}
use port_io::{inw, outw, ind, outd};
// ---- DMA helpers ----
fn alloc_dma(size: usize, align: usize) -> (*mut u8, usize) {
let mapping = DmaBuffer::allocate(size, align)
.expect("uhcid: DMA allocation failed");
let phys = mapping.physical_address();
(mapping.as_ptr() as *mut u8, phys)
struct StderrLogger;
impl log::Log for StderrLogger {
fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info }
fn log(&self, r: &log::Record) { eprintln!("[{}] uhcid: {}", r.level(), r.args()); }
fn flush(&self) {}
}
// ---- Controller state ----
struct UhciController {
name: String,
io_base: u16,
port_count: usize,
devices: Vec<Option<PortDevice>>,
}
struct PortDevice {
address: u8,
vendor_id: u16,
product_id: u16,
device_class: u8,
device_subclass: u8,
device_protocol: u8,
device_descriptor: [u8; 18],
_config_descriptor: Vec<u8>,
low_speed: bool,
}
// ---- I/O register helpers ----
impl UhciController {
fn read_reg(&self, offset: u16) -> u16 {
unsafe { inw(self.io_base + offset) }
}
fn write_reg(&self, offset: u16, value: u16) {
unsafe { outw(self.io_base + offset, value) };
}
fn write32(&self, offset: u16, value: u32) {
unsafe { outd(self.io_base + offset, value) };
}
fn port_status(&self, port: usize) -> u16 {
let offset = match port {
0 => PORTSC1,
1 => PORTSC2,
_ => return 0,
};
self.read_reg(offset)
}
fn port_write(&self, port: usize, set: u16, clear: u16) -> u16 {
let offset = match port {
0 => PORTSC1,
1 => PORTSC2,
_ => return 0,
};
let current = self.read_reg(offset);
let value = (current & !clear) | set;
let change_bits = current & PORT_CHANGE_BITS;
self.write_reg(offset, value | change_bits);
self.read_reg(offset)
}
fn reset_controller(&self) {
self.write_reg(USBCMD, CMD_GLOBAL_RESET);
thread::sleep(Duration::from_millis(50));
self.write_reg(USBCMD, 0);
thread::sleep(Duration::from_millis(10));
self.write_reg(USBSTS, STS_HALTED);
}
fn init_frame_list(&self, frame_list_phys: usize, _qh_phys: usize) {
self.write32(FRBASEADD, (frame_list_phys & 0xFFFF_F000) as u32);
self.write_reg(USBCMD, CMD_CONFIGURE | CMD_MAX_PACKET_64);
self.write_reg(USBCMD, CMD_RUN_STOP | CMD_CONFIGURE | CMD_MAX_PACKET_64);
for _ in 0..100 {
if self.read_reg(USBSTS) & STS_HALTED == 0 {
break;
}
thread::sleep(Duration::from_millis(1));
}
info!("uhcid: controller started, frame list at 0x{:08X}", frame_list_phys);
}
fn port_reset(&self, port: usize) -> bool {
let portsc = self.port_status(port);
if portsc & PORT_CONNECT == 0 {
return false;
}
self.port_write(port, PORT_RESET, 0);
thread::sleep(Duration::from_micros(PORT_RESET_HOLD_US));
let new = self.port_write(port, 0, PORT_RESET);
thread::sleep(Duration::from_micros(PORT_RESET_SETTLE_US));
(new & PORT_ENABLE) != 0
}
// ---- Control transfer method (formerly free function) ----
// Implements the UHCI-specific 3-TD chain for a USB control transfer.
// Returns Ok(bytes_transferred) on success, Err(&'static str) on failure.
// `data_buf` is Option<(&mut [u8], bool)> = (buffer, is_in).
fn do_control_transfer(
&self,
device_addr: u8,
endpoint: u8,
low_speed: bool,
setup_packet: &[u8; 8],
mut data_buf: Option<(&mut [u8], bool)>,
) -> Result<Option<usize>, &'static str> {
let (setup_dma_buf, setup_phys) = alloc_dma(8, 16);
let (_status_dma_buf, _) = alloc_dma(0, 16);
let data_phys = match &data_buf {
Some((buf, _)) if !buf.is_empty() => {
let (data_dma, phys) = alloc_dma(buf.len(), 16);
if data_buf.as_ref().map(|(_, is_in)| *is_in).unwrap_or(false) {
// IN transfer: buffer filled by HC, we copy out after
} else {
// OUT transfer: copy data into DMA buffer
unsafe {
core::ptr::copy_nonoverlapping(
buf.as_ptr(),
data_dma,
buf.len(),
);
}
}
// Leak DMA for now (cleaned up on process exit)
core::mem::forget(unsafe { Box::from_raw(data_dma) });
phys
}
_ => 0,
};
// Build setup TD
unsafe {
core::ptr::copy_nonoverlapping(setup_packet.as_ptr(), setup_dma_buf, 8);
}
let setup_td_phys = unsafe {
let td = &mut *(setup_dma_buf.sub(32) as *mut TransferDescriptor);
td.link = PTR_TERM;
td.token = PID_SETUP
| ((device_addr as u32) << 8)
| ((endpoint as u32) << 15)
| (7 << 21); // maxlen = 8-1
td.buffer = setup_phys as u32;
td.status = TD_CTRL_ACTIVE | TD_CTRL_IOC
| if low_speed { TD_CTRL_LS } else { 0 }
| (3 << 27); // 3 retries
setup_dma_buf as usize - 32
};
// Poll for setup completion
let start = Instant::now();
loop {
let td_status = unsafe { (*(setup_dma_buf.sub(32) as *const TransferDescriptor)).status };
if td_status & TD_CTRL_ACTIVE == 0 {
if td_status & TD_STATUS_STALLED != 0 {
return Err("setup TD stalled");
}
if td_status & (TD_STATUS_CRC | TD_STATUS_BABBLE) != 0 {
return Err("setup TD error");
}
break;
}
if start.elapsed() > Duration::from_secs(2) {
return Err("setup TD timeout");
}
thread::sleep(Duration::from_micros(100));
}
// Handle data phase
let actual_len = if let Some((ref mut buf, is_in)) = data_buf {
if buf.is_empty() {
None
} else {
unsafe {
let td = &mut *(setup_dma_buf as *mut TransferDescriptor);
td.link = PTR_TERM;
td.token = if is_in {
PID_IN | ((device_addr as u32) << 8) | ((endpoint as u32) << 15)
| ((buf.len() as u32 - 1) << 21)
} else {
PID_OUT | ((device_addr as u32) << 8) | ((endpoint as u32) << 15)
| ((buf.len() as u32 - 1) << 21)
};
td.buffer = data_phys as u32;
td.status = TD_CTRL_ACTIVE | TD_CTRL_IOC
| TD_CTRL_SPD
| if low_speed { TD_CTRL_LS } else { 0 }
| (3 << 27);
setup_dma_buf as usize
};
let mut actual_len = 0usize;
loop {
let td_status = unsafe { (*(setup_dma_buf as *const TransferDescriptor)).status };
if td_status & TD_CTRL_ACTIVE == 0 {
if td_status & TD_STATUS_STALLED != 0 {
return Err("data TD stalled");
}
actual_len = (td_status & TD_ACTLEN_MASK) as usize + 1;
if is_in {
let n = actual_len.min(buf.len());
let src = unsafe { core::slice::from_raw_parts(setup_dma_buf as *const u8, n) };
(&mut buf[..n]).copy_from_slice(src);
}
break;
}
if start.elapsed() > Duration::from_secs(2) {
return Err("data TD timeout");
}
thread::sleep(Duration::from_micros(100));
}
Some(actual_len)
}
} else {
None
};
Ok(actual_len)
}
}
// ---- UsbHostController trait implementation ----
impl UsbHostController for UhciController {
fn name(&self) -> &str {
&self.name
}
fn port_count(&self) -> usize {
self.port_count
}
fn port_status(&self, port: usize) -> Option<PortStatus> {
if port >= self.port_count {
return None;
}
let s = self.port_status(port);
Some(PortStatus {
connected: s & PORT_CONNECT != 0,
enabled: s & PORT_ENABLE != 0,
suspended: s & PORT_SUSPEND != 0,
over_current: s & PORT_OVER_CURRENT != 0,
reset: s & PORT_RESET != 0,
power: s & PORT_POWER != 0,
low_speed: s & PORT_LOW_SPEED != 0,
high_speed: false, // UHCI is USB 1.1, never high-speed
test_mode: false,
indicator: false,
})
}
fn port_reset(&mut self, port: usize) -> bool {
UhciController::port_reset(self, port)
}
fn control_transfer(
&mut self,
device_address: u8,
setup: &SetupPacket,
data: &mut [u8],
) -> Result<usize, UsbError> {
// Build 8-byte setup packet from SetupPacket
let mut setup_buf = [0u8; 8];
setup_buf[0] = setup.request_type;
setup_buf[1] = setup.request;
let value = setup.value.to_le_bytes();
setup_buf[2] = value[0];
setup_buf[3] = value[1];
let index = setup.index.to_le_bytes();
setup_buf[4] = index[0];
setup_buf[5] = index[1];
let length = setup.length.to_le_bytes();
setup_buf[6] = length[0];
setup_buf[7] = length[1];
// Determine direction from request_type bit 7
let is_in = setup.request_type & 0x80 != 0;
let result = if data.is_empty() {
self.do_control_transfer(
device_address,
0, // endpoint 0
false, // low_speed not tracked per-transfer in trait
&setup_buf,
None,
)
} else {
let data_opt = if is_in {
Some((data, true))
} else {
Some((data, false))
};
self.do_control_transfer(
device_address,
0,
false,
&setup_buf,
data_opt,
)
};
result.map(|opt| opt.unwrap_or(0)).map_err(|e| {
error!("uhcid: control transfer error: {}", e);
UsbError::IoError
})
}
fn bulk_transfer(
&mut self,
_device_address: u8,
_endpoint: u8,
_data: &mut [u8],
_direction: TransferDirection,
) -> Result<usize, UsbError> {
// Bulk transfers not yet implemented in UHCI driver.
// UHCI has no native bulk list — bulk transfers go through the
// control transfer path's TD chain. See P4 follow-up.
Err(UsbError::Unsupported)
}
fn interrupt_transfer(
&mut self,
_device_address: u8,
_endpoint: u8,
_data: &mut [u8],
) -> Result<usize, UsbError> {
// Interrupt transfers in UHCI use the periodic frame list slots.
// Not yet implemented in this driver. See P5 follow-up.
Err(UsbError::Unsupported)
}
fn set_address(&mut self, _device_address: u8) -> bool {
// UHCI does not have a SET_ADDRESS controller command;
// SET_ADDRESS is a standard USB control transfer handled via
// control_transfer(). Returns true here to indicate "address
// accepted" (the caller is expected to send SET_ADDRESS via
// control_transfer with the standard device request).
true
}
}
// ---- Main entry ----
fn main() {
log::set_logger(&StderrLogger).ok();
log::set_max_level(LevelFilter::Info);
let _fd = match env::var("PCID_CLIENT_CHANNEL") {
Ok(s) => match s.parse::<usize>() {
Ok(fd) => fd,
Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); }
},
Ok(s) => match s.parse::<usize>() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } },
Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); }
};
let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default();
info!("UHCI USB 1.1 at {}", device_path);
// Derive controller name from device path
let ctrl_name = device_path
.rsplit('/')
.next()
.unwrap_or("uhci0")
.to_string() + "_uhci";
// Read BAR4 (I/O base) from PCI config space
let config_path = format!("{}/config", device_path);
let bar4 = match fs::read(&config_path) {
Ok(data) if data.len() >= 0x24 => {
u32::from_le_bytes([data[0x20], data[0x21], data[0x22], data[0x23]])
match fs::read(&config_path) {
Ok(data) if data.len() >= 0x14 => {
let bar4 = u32::from_le_bytes([data[0x20], data[0x21], data[0x22], data[0x23]]);
info!("UHCI I/O base: 0x{:04X} (BAR4)", bar4 & 0xFFE0);
info!("uhcid: I/O port detected, ready for port enumeration");
}
_ => { error!("cannot read PCI config"); process::exit(1); }
};
let io_base = (bar4 & 0xFFE0) as u16;
info!("UHCI I/O base: 0x{:04X} as {}", io_base, ctrl_name);
// Allocate DMA memory
let (frame_list, frame_list_phys) = alloc_dma(FRAME_COUNT * 4, FRAME_LIST_ALIGN);
let (dummy_qh, dummy_qh_phys) = alloc_dma(core::mem::size_of::<QueueHead>(), 16);
unsafe {
let qh = &mut *(dummy_qh as *mut QueueHead);
qh.link = PTR_TERM;
qh.element = PTR_TERM;
}
unsafe {
let frames = core::slice::from_raw_parts_mut(frame_list as *mut u32, FRAME_COUNT);
for entry in frames.iter_mut() {
*entry = (dummy_qh_phys as u32) | PTR_QH;
}
}
let ctrl = UhciController {
name: ctrl_name,
io_base,
port_count: 2,
devices: vec![None, None],
};
ctrl.reset_controller();
ctrl.init_frame_list(frame_list_phys, dummy_qh_phys);
info!("uhcid: {} controller initialized, polling ports", ctrl.name);
// Main polling loop
loop {
for port in 0..ctrl.port_count {
let portsc = ctrl.port_status(port);
if (portsc & PORT_CONNECT) != 0 && (portsc & PORT_CSC) != 0 {
ctrl.port_write(port, 0, 0);
info!("uhcid: port {} connect detected", port + 1);
if ctrl.port_reset(port) {
match enumerate_device(&ctrl, port) {
Ok(dev) => {
info!(
"uhcid: port {} device {:04x}:{:04x} class {:02x}",
port + 1, dev.vendor_id, dev.product_id, dev.device_class,
);
// P0-B1: auto-spawn class drivers via the trait-based path
let scheme_name = usb_core::scheme::scheme_path(&ctrl.name);
usb_core::spawn::spawn_class_driver_for_port(
dev.device_class,
dev.device_subclass,
dev.device_protocol,
&scheme_name,
&format!("{}", port + 1),
0,
);
}
Err(e) => {
warn!("uhcid: port {} enumeration failed: {}", port + 1, e);
}
}
}
}
if (portsc & PORT_CONNECT) == 0 && (portsc & PORT_CSC) != 0 {
ctrl.port_write(port, 0, 0);
info!("uhcid: port {} disconnected", port + 1);
}
}
thread::sleep(Duration::from_millis(100));
_ => warn!("cannot read PCI config"),
}
loop { std::thread::sleep(std::time::Duration::from_secs(10)); }
}
fn enumerate_device(ctrl: &UhciController, port: usize) -> Result<PortDevice, &'static str> {
let portsc = ctrl.port_status(port);
let low_speed = (portsc & PORT_LOW_SPEED) != 0;
// Step 1: Get 8-byte device descriptor header
let get_desc: [u8; 8] = [
0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x08, 0x00,
];
let mut header = [0u8; 8];
ctrl.do_control_transfer(0, 0, low_speed, &get_desc, Some((&mut header, true)))?;
// Step 2: Set address
let addr: u8 = (port + 1) as u8;
let set_addr: [u8; 8] = [
0x00, 0x05, addr, 0x00, 0x00, 0x00, 0x00, 0x00,
];
ctrl.do_control_transfer(0, 0, low_speed, &set_addr, None)?;
thread::sleep(Duration::from_millis(10));
// Step 3: Get full device descriptor
let mut dev_desc = [0u8; 18];
let get_full_desc: [u8; 8] = [
0x80, 0x06, 0x00, 0x01, 0x00, 0x00, 0x12, 0x00,
];
ctrl.do_control_transfer(addr, 0, low_speed, &get_full_desc, Some((&mut dev_desc, true)))?;
let vendor_id = u16::from_le_bytes([dev_desc[8], dev_desc[9]]);
let product_id = u16::from_le_bytes([dev_desc[10], dev_desc[11]]);
let device_class = dev_desc[4];
let device_subclass = dev_desc[5];
let device_protocol = dev_desc[6];
// Step 4: Get config descriptor header
let mut cfg_header = [0u8; 9];
let get_cfg_hdr: [u8; 8] = [
0x80, 0x06, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00,
];
ctrl.do_control_transfer(addr, 0, low_speed, &get_cfg_hdr, Some((&mut cfg_header, true)))?;
let total_len = u16::from_le_bytes([cfg_header[2], cfg_header[3]]);
// Step 5: Get full config descriptor
let mut config = vec![0u8; total_len as usize];
let get_cfg: [u8; 8] = [
0x80, 0x06, 0x00, 0x02, 0x00, 0x00,
(total_len & 0xFF) as u8,
((total_len >> 8) & 0xFF) as u8,
];
ctrl.do_control_transfer(addr, 0, low_speed, &get_cfg, Some((&mut config, true)))?;
Ok(PortDevice {
address: addr,
vendor_id,
product_id,
device_class,
device_subclass,
device_protocol,
device_descriptor: dev_desc,
_config_descriptor: config,
low_speed,
})
}
@@ -1,6 +1,4 @@
#![allow(dead_code)]
// UHCI I/O register offsets (16-bit aligned, accessed via inw/outw)
pub const USBCMD: u16 = 0x00;
pub const USBSTS: u16 = 0x02;
pub const USBINTR: u16 = 0x04;
@@ -10,104 +8,24 @@ pub const SOFMOD: u16 = 0x0C;
pub const PORTSC1: u16 = 0x10;
pub const PORTSC2: u16 = 0x12;
// USBCMD bits
pub const CMD_RUN_STOP: u16 = 1 << 0;
pub const CMD_HOST_RESET: u16 = 1 << 1;
pub const CMD_GLOBAL_RESET: u16 = 1 << 2;
pub const CMD_CONFIGURE: u16 = 1 << 6;
pub const CMD_MAX_PACKET_64: u16 = 1 << 7;
// USBSTS bits
pub const STS_INTERRUPT: u16 = 1 << 0;
pub const STS_ERROR: u16 = 1 << 1;
pub const STS_RESUME: u16 = 1 << 2;
pub const STS_HOST_ERROR: u16 = 1 << 3;
pub const STS_HALTED: u16 = 1 << 5;
// PORTSC bits
pub const PORT_CONNECT: u16 = 1 << 0;
pub const PORT_CSC: u16 = 1 << 1; // Connect Status Change
pub const PORT_ENABLE: u16 = 1 << 2;
pub const PORT_PEC: u16 = 1 << 3; // Port Enable Change
pub const PORT_OVER_CURRENT: u16 = 1 << 3; // alias of PEC for clarity
pub const PORT_RESUME: u16 = 1 << 6;
pub const PORT_ENABLE: u16 = 1 << 1;
pub const PORT_SUSPEND: u16 = 1 << 2;
pub const PORT_OVER_CURRENT: u16 = 1 << 3;
pub const PORT_RESET: u16 = 1 << 4;
pub const PORT_LOW_SPEED: u16 = 1 << 8;
pub const PORT_RESET: u16 = 1 << 9;
pub const PORT_SUSPEND: u16 = 1 << 12;
pub const PORT_POWER: u16 = 1 << 12; // alias of SUSPEND (different bit on real UHCI hardware; non-standard)
// PORTSC change bits (write 1 to clear)
pub const PORT_CHANGE_BITS: u16 = PORT_CSC | PORT_PEC;
pub const FRAME_COUNT: usize = 1024;
pub const FRAME_LIST_ALIGN: usize = 4096;
// ---- Transfer Descriptor (TD) and Queue Head (QH) structures ----
//
// These are the hardware-level data structures the UHCI controller
// walks in DMA-accessible memory. Each field is a 32-bit little-endian
// value. We represent them as packed C structs and access them via
// raw pointers.
/// Link pointer — points to the next QH, TD, or is a terminator.
/// Bits 1-0 encode the type: 00=TD, 10=QH, 01=Depth-first, 01+Depth=Breadth-first
/// Bit 0 = Terminate (1 = end of list)
pub const PTR_TERM: u32 = 1; // Terminate this list
pub const PTR_QH: u32 = 2; // Points to a Queue Head
pub const PTR_DEPTH: u32 = 4; // Depth-first traversal
/// A Queue Head. Links into the frame list. The element pointer
/// is updated by the HC as it processes TDs.
#[repr(C, align(16))]
pub struct QueueHead {
pub link: u32, // next QH pointer (or TERM)
pub element: u32, // next TD pointer (updated by HC)
}
/// A Transfer Descriptor. Linked list of these forms a USB transfer.
/// The status field is updated by the HC on completion.
///
/// Fields:
/// link — next TD pointer (PTR_TERM or another TD's phys addr)
/// status — status+control word (written by HC on completion)
/// bits: 29:SPD 27-28:C_ERR 26:LS 25:IOS 24:IOC 23:ACTIVE
/// 22:STALLED 21:DBUFERR 20:BABBLE 19:NAK 18:CRC/TIMEO
/// 17:BITSTUFF 0-10:ACTLEN(actual length - 1)
/// token — token word
/// bits: 21-31:MAXLEN 19:TOGGLE 15-18:ENDP 8-14:DEVADDR 0-7:PID
/// buffer — physical address of data buffer
#[repr(C, align(16))]
pub struct TransferDescriptor {
pub link: u32,
pub status: u32,
pub token: u32,
pub buffer: u32,
}
// TD status/control bits
pub const TD_CTRL_ACTIVE: u32 = 1 << 23;
pub const TD_CTRL_IOC: u32 = 1 << 24; // Interrupt on Complete
pub const TD_CTRL_LS: u32 = 1 << 26; // Low Speed
pub const TD_CTRL_C_ERR_MASK: u32 = 3 << 27; // Error counter
pub const TD_CTRL_SPD: u32 = 1 << 29; // Short Packet Detect
pub const TD_STATUS_STALLED: u32 = 1 << 22;
pub const TD_STATUS_BABBLE: u32 = 1 << 20;
pub const TD_STATUS_NAK: u32 = 1 << 19;
pub const TD_STATUS_CRC: u32 = 1 << 18;
pub const TD_ACTLEN_MASK: u32 = 0x7FF; // bits 0-10
// USB Packet IDs for the token field
pub const PID_SETUP: u32 = 0x2D;
pub const PID_IN: u32 = 0x69;
pub const PID_OUT: u32 = 0xE1;
// Maximum NAK/error retry count for a single transfer
pub const MAX_RETRIES: u32 = 3;
// Port reset hold time (50ms in microseconds)
pub const PORT_RESET_HOLD_US: u64 = 50_000;
// Port reset settle time (10ms in microseconds)
pub const PORT_RESET_SETTLE_US: u64 = 10_000;
// Max bulk packet size for USB 1.1
pub const MAX_PACKET_SIZE: usize = 64;
@@ -1,6 +1,6 @@
[package]
name = "usb-core"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "Shared USB types and primitives for Red Bear host controller drivers"
@@ -10,10 +10,8 @@ pub mod transfer;
pub mod types;
pub use dma::{DmaBuffer, DmaError};
pub use scheme::{scheme_path, UsbError, UsbHostController, SCHEME_PREFIX};
pub use scheme::{UsbError, UsbHostController};
pub use spawn::spawn_usb_driver;
pub use spawn::class_driver_for_usb_class;
pub use spawn::spawn_class_driver_for_port;
pub use transfer::{
control_transfer, parse_config_descriptor, parse_device_descriptor, parse_endpoint_descriptor,
};
@@ -1,32 +1,18 @@
use crate::types::{PortStatus, SetupPacket, TransferDirection};
/// Trait that all USB host controller drivers must implement.
///
/// This provides a uniform scheme interface regardless of HC type
/// (XHCI / EHCI / OHCI / UHCI). Class drivers call these methods and
/// do not need to know which host controller they are talking to.
///
/// All controllers must register a scheme at `usb_core::scheme_prefix() +
/// controller.name()`. Class drivers are spawned with this scheme name
/// and open the controller through the trait API.
/// This provides a uniform scheme interface regardless of HC type (XHCI/EHCI/OHCI/UHCI).
pub trait UsbHostController {
/// Controller name for scheme registration and logging.
/// Example: `"0000:00:14.0_xhci"`. Must be unique system-wide.
fn name(&self) -> &str;
/// Number of root-hub ports on this controller.
/// Get the number of ports on this controller
fn port_count(&self) -> usize;
/// Get status of a specific port. Returns None if the port index is invalid.
/// Get status of a specific port
fn port_status(&self, port: usize) -> Option<PortStatus>;
/// Reset a port (USB enumeration step 1).
/// Returns true if the port enabled successfully after reset.
/// Reset a port (USB enumeration step 1)
fn port_reset(&mut self, port: usize) -> bool;
/// Submit a control transfer to endpoint 0.
/// `data` is the data-phase buffer (empty for setup-only transfers).
/// Returns the number of bytes transferred.
/// Submit a control transfer to endpoint 0
fn control_transfer(
&mut self,
device_address: u8,
@@ -34,7 +20,7 @@ pub trait UsbHostController {
data: &mut [u8],
) -> Result<usize, UsbError>;
/// Submit a bulk transfer. Use for mass-storage and similar.
/// Submit a bulk transfer
fn bulk_transfer(
&mut self,
device_address: u8,
@@ -43,8 +29,7 @@ pub trait UsbHostController {
direction: TransferDirection,
) -> Result<usize, UsbError>;
/// Submit an interrupt transfer. Use for HID, hubs.
/// Returns the number of bytes received.
/// Submit an interrupt transfer
fn interrupt_transfer(
&mut self,
device_address: u8,
@@ -52,17 +37,11 @@ pub trait UsbHostController {
data: &mut [u8],
) -> Result<usize, UsbError>;
/// Assign a device address (called during enumeration after port reset).
/// Set device address (after reset, before config)
fn set_address(&mut self, device_address: u8) -> bool;
}
/// Common scheme prefix for all USB host controllers.
/// Class drivers are spawned with this prefix + the controller's `name()`.
pub const SCHEME_PREFIX: &str = "usb.";
/// Compose a scheme path for a controller. Example: `"usb.0000:00:14.0_xhci"`.
pub fn scheme_path(name: &str) -> alloc::string::String {
alloc::format!("{SCHEME_PREFIX}{name}")
/// Get the controller name for logging
fn name(&self) -> &str;
}
#[derive(Debug)]
@@ -76,5 +55,3 @@ pub enum UsbError {
IoError,
Unsupported,
}
extern crate alloc;
@@ -1,78 +1,5 @@
/// Map a USB class/subclass/protocol triplet to the corresponding class driver
/// binary path. Returns `None` if no driver is registered for this class.
///
/// USB class codes from <https://www.usb.org/defined-class-codes>:
/// 0x01 — Audio (USB Audio Class)
/// 0x02 — Communications (CDC ACM/ECM)
/// 0x03 — HID (Human Interface Device)
/// 0x08 — Mass Storage
/// 0x09 — Hub
/// 0x0A — CDC Data (paired with CDC Comm)
/// 0xFF — Vendor-specific (FTDI, CP210x, etc.)
pub fn class_driver_for_usb_class(class: u8, subclass: u8, _protocol: u8) -> Option<&'static str> {
match (class, subclass) {
(0x01, 0x01) | (0x01, 0x02) => Some("/usr/bin/redbear-usbaudiod"), // Audio Control/Streaming
(0x02, 0x02) => Some("/usr/bin/redbear-acmd"), // CDC ACM (Abstract Control Model)
(0x02, 0x06) => Some("/usr/bin/redbear-ecmd"), // CDC ECM (Ethernet Control Model)
(0x02, _) => Some("/usr/bin/redbear-acmd"), // CDC generic → ACM fallback
(0x03, _) => Some("/usr/bin/usbhidd"), // HID
(0x08, _) => Some("/usr/bin/usbscsid"), // Mass Storage
(0x09, _) => Some("/usr/bin/usbhubd"), // Hub
(0x0A, _) => None, // CDC Data — paired with CDC Comm
(0xFF, _) => Some("/usr/bin/redbear-ftdi"), // Vendor-specific → FTDI
_ => None,
}
}
/// Spawn a class driver for a USB device port.
///
/// `scheme_name` — the host-controller scheme name (e.g. `"usb"` for ehcid,
/// `"usb.0000:00:14.0_xhci"` for xhcid).
/// `port` — the port number (string form, as passed to the class daemon).
/// `iface_num` — the interface number within the configuration.
/// `protocol` — the interface protocol byte (mass storage uses this).
///
/// The driver binary is selected from `class`, spawned with
/// `<scheme_name> <port> <iface_num_or_protocol>` as arguments, and disconnected
/// from stdin. The caller should own the scheme registration that the class
/// daemon will open.
#[cfg(feature = "std")]
pub fn spawn_class_driver_for_port(
class: u8,
subclass: u8,
protocol: u8,
scheme_name: &str,
port: &str,
iface_num: u8,
) {
let Some(driver_binary) = class_driver_for_usb_class(class, subclass, protocol) else {
return;
};
// Class daemons expect different argv layout.
// usbhidd: <scheme> <port> <interface>
// usbscsid: <scheme> <port> <protocol>
// usbhubd: <scheme> <port> <interface>
//
// For HID and Hub, we pass the interface number. For Storage, pass the
// bInterfaceProtocol byte (CBI, BOT, UAS, etc.).
let extra_arg = match class {
0x08 => protocol.to_string(),
_ => iface_num.to_string(),
};
let mut cmd = std::process::Command::new(driver_binary);
cmd.env_clear();
cmd.stdin(std::process::Stdio::null());
cmd.arg(scheme_name);
cmd.arg(port);
cmd.arg(&extra_arg);
let _ = cmd.spawn();
}
/// Spawn a child USB class driver by binary path and a single device-path
/// argument. Kept for backward compatibility; new code should prefer
/// `spawn_class_driver_for_port`.
/// Spawn a child USB class driver (hub, HID, storage).
/// On Redox, this forks and execs the driver binary with the USB device path.
#[cfg(feature = "std")]
pub fn spawn_usb_driver(driver_binary: &str, device_path: &str) {
if driver_binary.is_empty()
@@ -96,8 +23,6 @@ fn is_trusted_usb_driver(driver_binary: &str) -> bool {
matches!(
driver_binary,
"/usr/bin/usbhubd" | "/usr/bin/usbhidd" | "/usr/bin/usbscsid"
| "/usr/bin/redbear-acmd" | "/usr/bin/redbear-ecmd"
| "/usr/bin/redbear-usbaudiod" | "/usr/bin/redbear-ftdi"
)
}
@@ -106,19 +31,6 @@ fn is_trusted_usb_driver(driver_binary: &str) -> bool {
#[cfg(not(feature = "std"))]
pub fn spawn_usb_driver(_driver_binary: &str, _device_path: &str) {}
/// On no_std builds, class-driver spawning is not available.
#[cfg(not(feature = "std"))]
pub fn class_driver_for_usb_class(_class: u8, _subclass: u8, _protocol: u8) -> Option<&'static str> {
None
}
/// On no_std builds, class-driver spawning is not available.
#[cfg(not(feature = "std"))]
pub fn spawn_class_driver_for_port(
_class: u8, _subclass: u8, _protocol: u8,
_scheme_name: &str, _port: &str, _iface_num: u8,
) {}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::is_trusted_usb_driver;
@@ -128,10 +40,6 @@ mod tests {
assert!(is_trusted_usb_driver("/usr/bin/usbhubd"));
assert!(is_trusted_usb_driver("/usr/bin/usbhidd"));
assert!(is_trusted_usb_driver("/usr/bin/usbscsid"));
assert!(is_trusted_usb_driver("/usr/bin/redbear-acmd"));
assert!(is_trusted_usb_driver("/usr/bin/redbear-ecmd"));
assert!(is_trusted_usb_driver("/usr/bin/redbear-usbaudiod"));
assert!(is_trusted_usb_driver("/usr/bin/redbear-ftdi"));
}
#[test]
@@ -1,6 +1,6 @@
[package]
name = "virtio-inputd"
version = "0.2.5"
version = "0.2.4"
edition = "2024"
description = "virtio-input daemon v6.0 2026: reads virtio-input PCI events and writes Linux evdev events to /scheme/input-evdev"
@@ -11,13 +11,9 @@ path = "src/main.rs"
[dependencies]
anyhow = "1"
log = "0.4"
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
libredox = { version = "=0.1.16", features = ["call", "std"] }
redox_syscall = { version = "0.7", features = ["std"] }
redox-driver-sys = { path = "../../redox-driver-sys/source" }
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
syscall = { package = "redox_syscall", version = "0.7", features = ["std"] }
inputd = { path = "../../../../sources/base/drivers/inputd" }
common = { path = "../../../../sources/base/drivers/common" }
[patch.crates-io]
redox_syscall = { path = "../../../../../local/sources/syscall" }
libredox = { path = "../../../../../local/sources/libredox" }
@@ -1,16 +1,16 @@
[package]
name = "redox-drm"
version = "0.2.5"
version = "0.2.4"
edition = "2021"
description = "DRM scheme daemon for Redox OS — provides GPU modesetting and buffer management"
[dependencies]
redox-driver-sys = { version = "0.2", path = "../../../drivers/redox-driver-sys/source" }
linux-kpi = { version = "0.2", path = "../../../drivers/linux-kpi/source" }
libredox = { path = "../../../../../local/sources/libredox" }
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
libredox = "0.1"
redox_syscall = { version = "0.8", features = ["std"] }
syscall04 = { package = "redox_syscall", version = "0.4" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
redox-scheme = "0.11"
daemon = { path = "../../../../../recipes/core/base/source/daemon" }
log = "0.4"
thiserror = "2"
@@ -20,6 +20,3 @@ getrandom = "0.2"
[patch.crates-io]
redox-driver-sys = { path = "../../../drivers/redox-driver-sys/source" }
linux-kpi = { path = "../../../drivers/linux-kpi/source" }
libredox = { path = "../../../../../local/sources/libredox" }
redox-scheme = { path = "../../../../../local/sources/redox-scheme" }
redox_syscall = { path = "../../../../../local/sources/syscall" }
+2 -2
View File
@@ -6,8 +6,8 @@
# kwin headers at configure time; disabled via CMake option. The widget style plugin should
# build independently with just qtbase + KF6 deps.
[source]
tar = "https://download.kde.org/stable/plasma/6.7.2/breeze-6.7.2.tar.xz"
blake3 = "8d9df73d56ebe7eb92185530d82104460d6ac7214a0ead5c29d0026bd3837357"
tar = "https://invent.kde.org/plasma/breeze/-/archive/v6.3.4/breeze-v6.3.4.tar.gz"
blake3 = "ac311365edb6540c06ec6923e0c44accfe204beaa4d0630bf8f398982eb01d89"
[build]
template = "custom"
@@ -4,6 +4,7 @@
# kdesu is not built (needs sudo or kdesu backend which doesn't exist on Redox).
[source]
tar = "https://invent.kde.org/plasma/kde-cli-tools/-/archive/v6.3.4/kde-cli-tools-v6.3.4.tar.gz"
blake3 = "65b55bfdea0ba55a2dc78480ded5a2e2230d0d3ab00194de9610677c91ede1c6"
[build]
template = "custom"
+2 -2
View File
@@ -1,7 +1,7 @@
#TODO: KDecoration3 — window decoration library. Required by KWin.
[source]
tar = "https://invent.kde.org/plasma/kdecoration/-/archive/v6.7.2/kdecoration-v6.7.2.tar.gz"
blake3 = "f9802589d7e61099a4f26b3723c5f54e92e60919d35e6df348f0a7eccf2700de"
tar = "https://invent.kde.org/plasma/kdecoration/-/archive/v6.3.4/kdecoration-v6.3.4.tar.gz"
blake3 = "43ce6200f914e72c27dfa547f3a50859a42e0e13eb0756e8e68af912bbbfbda5"
[build]
template = "custom"
@@ -1,107 +0,0 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR This file is copyright:
# This file is distributed under the same license as the kdecoration package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: kdecoration\n"
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
"POT-Creation-Date: 2026-01-13 08:42+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Irish Gaelic <kde-i18n-doc@kde.org>\n"
"Language: ga\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n < 11 ? "
"3 : 4\n"
#: decorationbutton.cpp:325
#, kde-format
msgctxt ""
"@info:tooltip The Name of the menu that appears when you click a window's "
"app icon"
msgid "Window menu"
msgstr ""
#: decorationbutton.cpp:327
#, kde-format
msgid "Application menu"
msgstr ""
#: decorationbutton.cpp:330
#, kde-format
msgid "On one desktop"
msgstr ""
#: decorationbutton.cpp:332
#, kde-format
msgid "On all desktops"
msgstr ""
#: decorationbutton.cpp:334
#, kde-format
msgid "Minimize"
msgstr ""
#: decorationbutton.cpp:337
#, kde-format
msgid "Restore"
msgstr ""
#: decorationbutton.cpp:339
#, kde-format
msgid "Maximize"
msgstr ""
#: decorationbutton.cpp:341
#, kde-format
msgid "Close"
msgstr ""
#: decorationbutton.cpp:343
#, kde-format
msgid "Context help"
msgstr ""
#: decorationbutton.cpp:346
#, kde-format
msgid "Unshade"
msgstr ""
#: decorationbutton.cpp:348
#, kde-format
msgid "Shade"
msgstr ""
#: decorationbutton.cpp:351
#, kde-format
msgid "Don't keep below other windows"
msgstr ""
#: decorationbutton.cpp:353
#, kde-format
msgid "Keep below other windows"
msgstr ""
#: decorationbutton.cpp:356
#, kde-format
msgid "Don't keep above other windows"
msgstr ""
#: decorationbutton.cpp:358
#, kde-format
msgid "Keep above other windows"
msgstr ""
#: decorationbutton.cpp:361
#, kde-format
msgid "The window is hidden from Screencast. Press to make it visible"
msgstr ""
#: decorationbutton.cpp:363
#, kde-format
msgid "Hide from Screencast"
msgstr ""
+2 -2
View File
@@ -2,8 +2,8 @@
# Provides KF6::Attica cmake target needed by kf6-knewstuff.
# QML, tests, and examples disabled.
[source]
tar = "https://download.kde.org/stable/frameworks/6.27/attica-6.27.0.tar.xz"
blake3 = "d22d07aa538f3a0404e652f811fcb6816f125f493c8a246f36478a439094929b"
tar = "https://invent.kde.org/frameworks/attica/-/archive/v6.10.0/attica-v6.10.0.tar.gz"
blake3 = "0b4d10aca536ffc4c2a81d6d99f2ea7f88f36490c0d9aa45041cd4db1c3304ad"
[build]
template = "custom"

Some files were not shown because too many files have changed in this diff Show More