Following a full repo-wide grep and source review of all quirks
files (mod.rs, pci_table.rs, usb_table.rs, dmi.rs, toml_loader.rs,
pci.rs, i2c-hidd/quirks.rs), this companion to QUIRKS-SYSTEM.md
documents the current ground-truth state of the subsystem as of
2026-06-29.
Findings:
- 11 Rust + 6 C consumers of the redox-driver-sys quirks engine
- 1 separate DMI quirks engine in i2c-hidd (uses RON, not TOML)
- 6 dead-code entry points that return empty flags:
* lookup_pci_quirks_full actions (Vec::new)
* lookup_xhci_controller_quirks_full (stub)
* lookup_hid_quirks (stub)
* load_dmi_acpi_quirks (stub)
* load_platform_dmi_quirks (stub, empty PLATFORM_RULES)
* load_drm_panel_orientation (empty PANEL_ORIENTATION_TABLE)
- 4 PciQuirkFlags bits (22-25) have no entry in PCI_FLAG_NAMES,
so they cannot be set via /etc/quirks.d/*.toml
- DMI is consumed in two incompatible ways:
* redox-driver-sys reads /scheme/acpi/dmi as a flat key=value file
* i2c-hidd reads /scheme/acpi/dmi/{system_vendor,...} as per-field
subpaths, and matches case-insensitive
- redbear-quirks stages 10 TOML files to /etc/quirks.d/; the
TOML layer is fully live (this corrects an earlier claim
that it was dormant)
The audit concludes that the system is correctly initialized and
utilized per its design. No redesign is needed; the existing
QUIRKS-SYSTEM.md is mostly accurate but understates how many
defined functions return empty data and how many flag names
are missing from the TOML schema.
Source comes from tar — local edits are overwritten on fetch.
Patch adds #include <stdlib.h> before Qt headers in common_helpers_p.h
so GCC <cstdlib> sees ::strtold declared.
GCC <cstdlib> needs ::strtold declared. Qt's include chain pulls in
<cstdlib> before stdlib.h. Adding #include <stdlib.h> at the top of
common_helpers_p.h fixes the 'strtold has not been declared in ::'
error. Reverted -U_GLIBCXX_USE_C99_STDLIB toolchain breakage.
Updates the local/sources/base submodule pointer to pick up the
PIIX4 IDE BAR quirk + vgaarb logging + archiso loop_mnt, and
updates the base-initfs recipe to cross-compile loop_mnt before
the redox-initfs-ar archive step.
See local/sources/base commit 2055dcd for the individual
changes.
GCC <cstdlib> uses C99 stdlib wrappers (strtold etc.) gated by this macro.
relibc declares but doesn't implement strtold. Undef at toolchain level
prevents 'using ::strtold' errors in KF6/Qt C++ builds.
GCC <cstdlib> needs ::strtold declared. relibc stdlib.h provides it,
but Qt include chains can pull in <cstdlib> before stdlib.h.
Adding -include stdlib.h at the toolchain level fixes all Qt/KF6 recipes.
Reference analysis for Red Bear OS integration based on running the
latest CachyOS desktop ISO (28 Jun 2026) under QEMU/KVM. Documents
the hardware-enumeration and kernel-init sequences a reference Linux
distro produces on the i440FX + PIIX machine type that Red Bear OS
also targets, with line-by-line mapping to Red Bear OS subsystems
(pcid, ided, e1000d, vesad, xhcid, hwd/acpid).
- local/docs/CACHYOS-INTEGRATION.md: Cross-cutting analysis
(executive summary, hardware inventory, ACPI table coverage,
PCI quirks, boot-phase ordering, init system comparison, action
items).
- local/docs/boot-logs/cachyos-kernel-20260629-0520.log: Captured
441-line dmesg-grade log from the CachyOS kernel boot (SeaBIOS
handover through ACPI, PCI, USB, ATA, network enumeration and
up to a rootfs shell prompt).
- local/docs/boot-logs/cachyos-boot-20260629-0520.md: Pointer
document with the capture command and the rationale for the
indirect-kernel invocation (the QEMU + CachyOS + KVM boot stalled
at the ISOLINUX EDD probe when run from CD; bypassing with
-kernel/-initrd and an explicit console=ttyS0 earlyprintk command
line recovered the full log).
Sources used: https://cachyos.org/ (release info) and the on-disk
ISOLINUX + archiso boot path extracted from
cachyos-desktop-linux-260628.iso.
GCC <cstdlib> does 'using ::strtold;' which requires strtold declared in
global namespace. relibc declares it correctly in stdlib.h, but Qt's
include chain (qchar.h → qglobal.h → ... → cstdlib) pulls in cstdlib
before stdlib.h is fully processed. Adding -include stdlib.h ensures
the C stdlib declarations are in scope before any C++ header.
Old patch had @@ -3,5 — missing trailing context line
'add_subdirectory(qmllint)' that diff -u includes.
Regenerated from actual source.tmp to match fetched tarball.
The previous commit changed the existence probe from is_dir/exists
(read_dir uses stat) to read_dir.is_ok(), but the second and subsequent
calls to read_dir for /scheme/acpi/thermal still return Err at runtime.
The first discover_zone_dirs call at startup succeeds (it sees 0 zones,
matching the acpid scheme's empty Thermal directory). The periodic
monitor_loop recheck then fails with 'unavailable' even though the
path is in fact present.
The real reason is not fully understood yet, but may relate to
scheme-namespace state after userland init, fd table churn, or
Redox-specific read_dir semantics on empty scheme directories.
The warn-once check is redundant: discover_zone_dirs already runs at
startup, and update_policy() sees the empty state every poll cycle and
re-renders the TUI accordingly. Drop the periodic recheck so the
already-diagnosed empty surface no longer logs a false alarm.
- acpid scheme: add Thermal and Power as new HandleKind variants,
register them in the openat match, and add entries to the TopLevel
getdents list. thermald and redbear-upower probe /scheme/acpi/thermal
and /scheme/acpi/power via read_dir; an empty directory is the
correct fallback for desktops and QEMU where there is no ACPI
_TZ/_PR data to enumerate.
- acpid scheme: also handle getdents on the new Thermal and Power
variants. Without this, the scheme returned EIO which std::fs::read_dir
treats as 'the path is not a directory or doesn't exist', and thermald
emits a false 'thermal surface unavailable' warning every poll cycle.
Both daemons previously checked /scheme/acpi/thermal (thermald) and
/scheme/acpi/power (redbear-upower) existence with Path::exists or
is_dir, which use stat/lstat. On Redox these syscalls can return
errors for scheme paths even when the scheme IS registered, leading
to false 'unavailable' warnings even after the new acpid thermal/
power directories were added.
Use fs::read_dir (which already worked for actual zone enumeration)
as the existence probe instead. The match expression intentionally
calls read_dir to discard the iterator and keep only the Result;
using ? here would break the warn-once pattern.
- app.rs: rapl_status field shows 'n/a (run as root)' or
'n/a (unsupported)' or 'sampling...' depending on state
- render.rs: header PkgW shows rapl_status when unavailable;
removed per-CPU PkgW column (package power is socket-wide)
- acpi.rs: fallback creates 6 evenly-spaced P-states from
cpuinfo_min_freq / cpuinfo_max_freq when scaling_available_frequencies
is absent (intel_pstate, amd-pstate drivers)
- platform.rs: probe accepts cpuinfo_max_freq as valid PSS source
- app.rs: match current frequency against synthetic P-state table
to compute current_idx without MSR access
- pss_source label: 'sysfs (cpuinfo min-max)' for intel_pstate
- msr.rs: add all Intel RAPL MSR addresses (0x606-0x64D) and AMD Zen
equivalents (0xC0010299-0xC001029B), RaplUnit struct for unit register
parsing with energy_to_uj/power_to_w conversion, read_rapl_energy()
and read_rapl_energy_uj() functions
- acpi.rs: read_rapl_package_energy() now tries MSR first (Intel then
AMD PKG energy MSRs) with unit-based µJ conversion, falls back to
Linux powercap sysfs
- local/docs/RAPL-IMPLEMENTATION-PLAN.md: comprehensive 3-phase plan
based on Linux 7.1 kernel analysis, Intel SDM, Fuchsia RFC-0203
patterns. Documents P0 blocker: /scheme/sys/msr/ not implemented
in kernel
On Redox the kernel's sys:cpu scheme is a single file (kernel/src/scheme/
sys/cpu.rs) whose contents start with 'CPUs: N\n', not a per-CPU directory.
The kernel does not create /dev/cpu/ at all, so the prior read_dir-based
enumeration always fell through to the single-CPU fallback on Redox —
hiding the fact that the kernel had successfully brought up the APs and
reporting only CPU 0 to the governor / power TUI.
Read /scheme/sys/cpu and parse the 'CPUs:' line first; fall back to
/dev/cpu/ for Linux hosts.
The local init fork's Service struct does not have a 'before' field
(it supports cmd, args, envs, inherit_envs, type only), and the
deny_unknown_fields attribute makes init panic at boot when it parses
'before':
init: /etc/init.d/12_dbus.service: unknown field 'before', expected
one of 'cmd', 'args', 'envs', 'inherit_envs', 'type' in 'service'
The 'before = [13_redbear-sessiond.service]' line was redundant
anyway: 13_redbear-sessiond.service already declares
requires_weak = ['12_dbus.service'], which orders it after dbus.
Fix both redbear-mini.toml and redbear-full.toml.
After cooking relibc, copy the freshly built libc.a and libc.so to both
the prefix toolchain and the redoxer toolchain (the latter is what
'make live' actually consumes for cross-recipe builds).
Without this, recipes that link against the dynamic libc.so see a stale
copy (no eventfd, no __fseterr, ...) and fail with 'undefined reference'
at link time. 'make prefix' does this sync via its sysroot rule, but
'make r.relibc' alone does not — covering both paths prevents the
first recipe that needs a new symbol from breaking the build.
- gettext: use -I${COOKBOOK_HOST_SYSROOT}/share/aclocal instead of
/usr/share/aclocal so autoreconf picks up the Redox-patched libtool 2.5.4
macros instead of the host system's libtool 2.6.1, fixing version mismatch
at build time
- libxau: add ACLOCAL=true AUTOMAKE=true AUTOHEADER=true to make invocations
to prevent automake regeneration when host autotools version differs from
what the source expects
- build-redbear.sh: make pre-cook failures non-fatal (warn only) and run with
COOKBOOK_OFFLINE=false so packages that need source fetching can succeed
- redox-drm: restore source from git history (was deleted in dc6805430);
update Cargo.toml version 0.1.0 -> 0.2.4 and dependency constraints to
match current project version
- recipes/dev/python312/recipe.toml: use COOKBOOK_TOOLCHAIN for
--with-build-python instead of /tmp/python312, which the build system
never stages. Add [ -x ] guard for clear failure on missing dev-dep.
- recipes/core/userutils/recipe.toml: switch from upstream git URL to
local fork (local/sources/userutils/) per the local fork model. The
upstream source opens /scheme/pty/ptmx which the ptyd scheme does not
recognize; the local fork opens /scheme/pty correctly and avoids the
getty PTY panic.
- local/scripts/build-preflight.sh: warn when a recipe build script
references /tmp/<known-package>/, since the cookbook does not stage
host dev-deps under /tmp/<name>. Points authors at COOKBOOK_TOOLCHAIN.
- local/scripts/build-redbear.sh: replace 'tail -1 || true' on pre-cook
failures with proper error capture, last-50-lines tail on failure, and
exit-1. Verify the pkgar exists after a successful cook.
The tab keys (1-9 jump directly, T cycles next) were
always wired in main.rs but not documented anywhere
visible. Operators could not discover how to switch
between the 9 data tabs (Per-CPU / System / Info /
Motherboard / Battery / Sensors / Network / Storage
/ Process) without reading the source.
Two surfaces now surface the tab keys as the FIRST
entries, ahead of governor / P-state / throttle
controls:
1. Controls panel (visible at the bottom of every
TUI frame): adds a 'Tabs:' line listing the 9
direct-jump keys with abbreviated tab names, plus
a [T] cycle line. Implemented in
render_controls().
2. --help output (HELP_TEXT constant, shown via
'redbear-power --help' and the in-TUI [?] help
overlay): adds a TABS: section at the top of the
INTERACTIVE CONTROLS block, listing all 9 tabs in
3-row grid + the [T] cycle key.
The keys themselves are unchanged: '1'-'9' for
direct tab jump, 'T' for cycle. No new keybindings
or behaviors added.
Discovered during interactive smoke-testing on the
Linux host: the binary worked perfectly but tab
navigation was a discoverability hole. Fix-only,
no semantic change.
186/186 tests still pass (the controls panel
render is covered by the existing snapshot tests).
Adds §68 (v1.44 plan) and revises §67.7 (v1.43
deferred list) to reflect the v1.43 audit findings.
Key corrections captured in the doc:
- The Redox kernel ALREADY implements
sched_setaffinity and sched_getaffinity
(local/sources/kernel/src/syscall/process.rs:322-382).
Only the relibc POSIX <sched.h> wrapper is missing.
- The existing P7-pthread-affinity relibc patch
provides cpu_set_t, cpuset_to_u64, copy_u64_to_cpuset
— v1.44 can reuse these instead of duplicating.
- Kernel pid=0 limitation is documented at
kernel/src/syscall/process.rs:336-338 as a TODO;
v1.44's UX is honest about this ('pin redbear-power's
own TUI to this process's CPU list, not the
highlighted process's').
- Per-thread CPU% is REJECTED for v1.44 because the
Redox proc scheme doesn't expose /proc/<pid>/task/<tid>/stat.
Same trap as v1.41 read_thread_io. Tracked as a kernel
follow-up, not a redbear-power feature.
- disk_history cap is REJECTED for v1.44 because the
natural bound on block device count (~4-8 typical)
makes the cap moot. Drive-by include it elsewhere.
Implementation plan documented:
1. relibc patch P12-sched-setaffinity.patch (~110 LoC,
reusing P7 helpers)
2. redbear-power affinity.rs module (~30 minutes)
3. main.rs key binding (capital A) + PID detail
popup integration (~1.5 hours)
4. 5-7 tests (round-trip, pid=0 limitation, parse/format,
integration against /proc/self/status)
5. Doc update §69 on what shipped
Effort estimate: ~1 working day, end-to-end. The
relibc patch alone is <3 hours given P7 reuse.
Downstream recipe impact audited (LOW):
- Mesa and xz both wrap sched_getaffinity in
defensive probes — they go from ENOSYS to
'current process mask works' (strict improvement,
no break).
No code changes — planning-only commit.
Add local/scripts/cleanup-build.sh - a git-aware cleanup script that
uses 'git ls-files' to whitelist tracked files before deletion.
Prevents the class of cleanup disasters that deleted local recipe
sources and local fork sources.
Update AGENTS.md with the new safe cleanup procedure.
Update CONSOLE-TO-KDE-DESKTOP-PLAN.md to v5.8 with the qtdeclarative
qfeatures.h fix and the new safe cleanup script.
The qtquick-config.h file was being generated empty by the cmake
build, causing 'division by zero in #if' errors in downstream
consumers (SDDM, KWin) because QT_CONFIG(quick_shadereffect) and
QT_CONFIG(quick_draganddrop) were undefined.
Two fixes:
1. Explicitly enable QT_FEATURE_quick_shadereffect and
QT_FEATURE_quick_draganddrop in the cmake configuration.
2. Add a safety net that regenerates qtquick-config.h with the
required feature definitions if cmake produces an empty file.
The first item from the v1.42 deferred list: a
configurable LRU cap on the per-PID history maps.
On long uptimes with thousands of short-lived procs
(build servers, CI runners), the maps would grow
without bound, eventually consuming significant
memory. v1.43 caps the maps at 500 PIDs by default
and evicts the LRU entry on overflow.
The cap
- App::max_history_pids: usize (default 500)
- 0 = disable (reaper still prunes exited PIDs)
- Shared across the 3 per-PID maps (io_history,
cpu_history, rss_history). They are always
populated in lockstep so a per-PID CPU history
without the corresponding IO history would be
a 'ghost' entry that confuses the renderer.
- disk_history is NOT capped (keyed by disk name,
natural bound on block device count).
LRU tagging
- New App::pid_last_seen: BTreeMap<u32, u64>
- New App::refresh_tick: u64 (incremented on every
update_io_history call)
- We use a refresh counter, not Frame::count(),
because the history update happens during
refresh (not during render). Frame::count would
tag currently-visible PIDs rather than
recently-updated PIDs — a different (and
incorrect) notion.
Eviction algorithm
1. Increment refresh_tick
2. Reap exited PIDs from all 3 maps and
pid_last_seen
3. If pid_last_seen.len() > cap: sort by tick
ascending, take the first overflow entries,
remove from all 3 maps + pid_last_seen
4. Continue with the existing pipeline
Cost: O(n log n) per refresh, n bounded by 500.
At 500 PIDs: ~4500 comparisons per refresh,
<100µs. Memory budget: ~28 KB at cap, vs
unbounded growth without the cap (~5.5 MB at
100k PIDs).
Tests
- 3 new tests (eviction removes oldest, cap=0
disables, no-op under cap).
- 186/186 tests pass (was 183 in v1.42).
The improvement plan doc is also updated with §67
covering the v1.43 architecture, the cap policy,
the LRU tagging, the eviction algorithm, the
memory budget, and the v1.44 deferred list.
The next item from the v1.41 deferred list: read
/proc/<pid>/status:Cpus_allowed_list and display it as
both a single-char row indicator and a full expanded
list in the PID detail popup. htop parity.
Kernel format
The kernel emits the list as comma-separated ranges:
"0-3,5,7-11" means CPUs 0, 1, 2, 3, 5, 7, 8, 9,
10, 11
Cpus_allowed_list is the HARD affinity mask (settable
via sched_setaffinity(2)). v1.42 reads it because it
matches what an operator sees with 'taskset'.
New functions
- read_cpu_affinity(pid): parses the kernel string
- parse_cpu_list(s): public, testable parser
- format_cpu_list(ids): inverse of parse_cpu_list
- read_cpu_affinity_for_pid(pid): pub wrapper for the
PID detail popup
Two display modes
- Process panel row: '*' (subset), ' ' (all CPUs),
'?' (unknown). Single char so COMM stays visible.
- PID detail popup: full range string + expanded
Vec (truncated to 8 items on large machines).
New field on ProcessInfo
- cpu_affinity: Option<Vec<u32>>
Robustness
- Whitespace tolerated
- Out-of-order or duplicate IDs deduped and sorted
- Non-numeric chunks silently dropped
- Reversed ranges (start > end) silently dropped
- Empty input returns empty Vec (popup distinguishes
'no data' / None vs 'explicitly empty' / Some(empty))
Tests
- 13 new tests (11 in process.rs for parse/format/
read, 1 self-affinity test, 1 missing-pid test).
- 183/183 tests pass (was 170 in v1.41).
The improvement plan doc is also updated with §66
covering the v1.42 architecture, kernel format, the
two display modes, the parse/format inverse pair, and
the v1.43 deferred list.
The qtdeclarative recipe manually copied include files, which missed
the build-time generated qfeatures.h. Without it, downstream consumers
(SDDM, KWin, etc.) hit 'division by zero in #if' errors in qquickitem.h
because QT_CONFIG(quick_shadereffect) and QT_CONFIG(quick_draganddrop)
were undefined, triggering the deliberate 1/0 preprocessor trap.
Fix: use cmake --install to properly install all files, and add a safety
net that copies qfeatures.h from the build tree if cmake --install
does not place it in the install prefix.
The next item from the v1.40 deferred list: walk
/proc/<pid>/task/*/io for every process and sum
read_bytes and write_bytes across all TIDs. Surfaces
'is one thread of this 32-thread process hammering
disk?' which the process total hides.
Linux kernel attribution quirk
/proc/<pid>/io:read_bytes is the process total,
NOT the per-thread sum — the kernel attributes all
IO to the process even when threads initiate it.
So the two surfaces can match, diverge, or be
independently None depending on kernel version and
permission model. We never compare or subtract them.
New fields on ProcessInfo
- thread_io_read_kb, thread_io_write_kb (summed bytes)
- thread_io_read_rate_kbs, thread_io_write_rate_kbs
(delta-based rate via the same compute_rate_kbs
helper used for the process-total rates)
New sort modes
- ThreadIo: read+write total
- ThreadIoR: read only
- ThreadIoW: write only
The cycle reaches all 3 via a back-door arm of next()
that returns to Rss (not Name) to avoid breaking the
main loop.
New column in Process panel
T-IO between the per-thread rate and the MEM column.
Now 12 columns total. PID detail popup gets a
[thread_io] section that re-reads the task dir on
open for a current value.
Tests
- 6 new tests (3 in process.rs for read_thread_io,
3 for the sort modes + cycle).
- 170/170 tests pass (was 164 in v1.40).
The improvement plan doc is also updated with §65
covering the v1.41 architecture, the Linux kernel
attribution quirk, the new fields, the sort cycle
back-door, the cost analysis (~500 reads/sec at
typical desktop loads, ~7500 at 128-thread server
loads — well within budget), and the v1.42
deferred list.
The first item from the v1.39 deferred list: the user's
tab, sort mode, sort direction, tree mode, filter, and
fold set now survive a restart of redbear-power.
Architecture
- New module session.rs (separate from config.rs which
is read-only system-wide config).
- config.rs: behavior config (refresh interval, theme,
keybindings) — read once at startup, never written.
- session.rs: mutable per-user runtime state — read at
startup AND written on every tab change and on quit.
Storage
- $XDG_CONFIG_HOME/redbear-power/session.toml (preferred)
- ~/.config/redbear-power/session.toml (fallback)
- Writes are atomic: temp file + rename(). A crash
between write and rename leaves the prior session
intact.
Save hooks
- Every set_tab() call: tab is the high-signal event
the user explicitly opted into.
- On graceful quit (q/Esc): captures the final sort,
filter, and fold set.
Failure modes
- load() never errors. Missing file = defaults.
Corrupt file = defaults + one-line warning.
- save() never errors. Permission denied = eprintln!
warning. The next launch reads the prior session
(or defaults) and starts from there.
Tests
- 6 new tests in session.rs (round-trip, missing-file,
malformed-toml, atomic-save, default-state, end-to-end
via App::save_session()).
- 164/164 tests pass (was 158 in v1.39).
The improvement plan doc is also updated with §64
covering the v1.40 architecture, storage paths, save
policy, failure modes, and the v1.41 deferred list.
v1.39 lands three htop/btop parity features plus the
audit-fix discipline introduced in v1.37/v1.38:
1. Cursor preservation across sort
o and i keypresses no longer reset the cursor to row 0.
The cursor follows the currently-selected PID through the
re-sort. Implemented as App::remember_and_restore_cursor()
which walks the post-filter visible list. Three regression
tests: follows PID, respects filter, falls back when PID
exits.
2. Per-thread IO rate (T-IO column)
New ProcessInfo::io_per_thread_rate_kbs() returns the
aggregate IO rate divided by num_threads, surfacing
thread-pool pressure that's hidden in the aggregate. A
32-thread process at 320 KiB/s is the same as a 1-thread
process at 10 KiB/s in aggregate, but very different
in operator-relevant 'IO per worker' terms. Returns None
when num_threads <= 0 (data error, not '0 KiB/s per
thread' which would mislead the operator). Three unit
tests cover the divide, the missing-total case, and the
zero-threads case.
3. Process environ in PID detail
/proc/<pid>/environ read as NUL-separated KEY=VALUE
pairs, sorted by key for stable popup rendering.
Rendered as the first 8 vars in the PID detail popup
with a '(N variables)' header. htop F7 parity. Three
unit tests: parse self environ, missing PID, value
containing '=' (must split on FIRST '=' only).
The improvement plan doc is also updated with sections
56-63 covering v1.32 (sparklines) through v1.39
(per-thread IO + environ) since the doc previously
stopped at v1.31.
Test count: 158/158 pass (was 149 in v1.38.1).