Files
RedBear-OS/local/docs/evidence/driver-manager/ASSESSMENT-2026-07-22.md
T
vasilito 884f9e8e0d driver-manager: v3.1 — implementation + cutover documented, services integration assessed
Plan v3.1 records the completed P0/LDR/P2 work program and the
operator-ratified cutover (driver-manager owns the boot path in all
redbear-* configs; pcid-spawner gated as fallback). Assessment § 11
documents the OS-services integration: init ConditionPathExists gate,
live driver-params bridge, udev-shim driver-binding gap (P3), and the
correct absence of D-Bus (bridge-on-demand pattern).
2026-07-23 18:08:03 +09:00

23 KiB
Raw Blame History

Driver-Manager Migration — Major Assessment (2026-07-22)

Scope: implementation quality, plan viability, correctness, robustness, AI-introduced code patterns, adjacent technology (pcid, pcid_interface, redox-driver-pci, linux-kpi, redox-driver-sys, firmware-loader, redox-drm, redbear-iwlwifi), gaps/blockers/inconsistencies. Cross-references: Linux 7.1 (local/reference/linux-7.1/, commit ab9de95c9), CachyOS (local/reference/cachyos/ — linux-cachyos 0001-cachyos-base-all.patch 6.17.9 + desktop ISO 260628). Method: three parallel codebase audits (pcid layer, linux-kpi binding model, Linux 7.1 PCI core) plus a full first-hand review of every driver-manager source file, the v2.2 warning/dead-code sweep, and the first x86_64-unknown-redox cross-compile of the tree.


1. Executive verdict

The plan is viable. The implementation is compile-grade solid after v2.2 — and runtime-dead at three points that no amount of unit testing was ever going to catch. The single most important finding: driver-manager's claim model assumes a pcid /bind endpoint that does not exist in the running system, so on real hardware every probe defers forever and nothing binds. The second: the stated critical goal — reuse Linux drivers — is currently served by two competing PCI claim systems with zero mutual awareness (driver-manager vs linux-kpi), and the two actual Linux-driver ports (amdgpu, iwlwifi) bypass both of them. The third: the pciehp and AER listeners poll files no producer creates.

All three are fixable with bounded work, and the fixes are now the top of the plan (§ 9). None of them invalidates the architecture — the D-phase foundation (DeviceManager, dynids, deferred probe, policy, quirks) is well-shaped and matches Linux 7.1 semantics where it matters.


2. Blockers (severity-ranked)

B1 — RUNTIME BLOCKER: /scheme/pci/<addr>/bind does not exist

driver-manager/src/config.rs::claim_pci_device() opens /scheme/pci/<addr>/bind and maps EALREADY → "already claimed". The pcid fork (local/sources/base/drivers/pcid/src/scheme.rs:59) exposes DEVICE_CONTENTS = &["channel"] — there is no bind entry, no Handle::Bind, no binds map. The three patches that add it (local/patches/base/P3-pcid-bind-scheme.patch, P3-pcid-aer-scheme.patch, P3-pcid-uevent-format-fix.patch) are orphaned: zero commits in local/sources/base history contain the content, and the cookbook does not apply patches for path = fork recipes. This also violates orphan-patch governance (AGENTS.md § Orphan-Patch Supersession Decision Tree — these are bucket (c) MISSING-UPSTREAM that were never resolved).

Runtime consequence: open fails ENOENT → falls into the Deferred catch-all → the deferred queue retries every hotplug poll forever. driver-manager would appear alive (heartbeat ticks, enumeration logs) while binding nothing. Worse than a crash.

Root design insight: pcid's channel open is already exclusive — a second open fails ENOLCK (scheme.rs:396-398). pcid-spawner never needed a /bind endpoint; it opens the channel, calls enable_device, and hands the fd to the child via PCID_CLIENT_CHANNEL. The v2.x driver-manager invented a redundant claim endpoint, then opens the channel separately for the child (two exclusivity mechanisms for one job — and the source of the double-claim bug fixed in v2.2).

Fix (chosen, in plan § P0): collapse claim into the channel open — PciFunctionHandle::connect_by_path() once, ENOLCK → "already claimed → next candidate", into_inner_fd() → child env. Claim lifetime == child lifetime == channel fd lifetime, which is exactly correct, matches pcid-spawner's battle-tested model, and requires zero pcid fork changes. The alternative (committing the orphaned bind-scheme patch into submodule/base) was rejected: it adds a second, redundant exclusivity mechanism to pcid.

B2 — GOAL BLOCKER: two competing PCI claim systems, zero mutual awareness

For the stated goal reuse Linux drivers, the integration surface is linux-kpi. Today (linux-kpi/src/rust_impl/pci.rs:620):

  • pci_register_driver self-enumerates /scheme/pci/ via redox_driver_sys, matches the C id_table, and calls the C probe in-process, synchronously — no /bind claim, no PCID_CLIENT_CHANNEL, no awareness of driver-manager. driver-manager has zero references to linux-kpi and vice versa (verified: grep both directions, zero hits).
  • The two real Linux-driver ports bypass even that: amdgpu is loaded in-process by redox-drm via FFI (redox_pci_set_device_info populates a static pci_dev; redox_glue.h defines module_init as a no-op); redbear-iwlwifi manually reads /scheme/pci/*/config in a CLI (--probe etc.) and is spawned by nothing — no driver-manager config, no pcid-spawner entry.
  • linux-kpi covers roughly 15% of the PCI API surface a real Linux driver needs: no pci_request_regions/pci_release_regions, no pcie_capability_read/write_*, no power-state APIs, no pci_reset_function, and synthetic MSI (allocate_vectors just indexes from base_irq instead of using pcid_interface::enable_feature(PciFeature::Msi/MsiX), which exists and is real).

Fix (plan § LDR — Linux-Driver-Reuse track): one ownership model. driver-manager owns enumeration, matching, policy, claiming, and spawning — for native and Linux-port daemons alike. linux-kpi's pci_register_driver gains a spawned mode: when PCID_CLIENT_CHANNEL is present in the environment, skip enumeration, wrap the granted channel, match only the device it was spawned for, and call probe for exactly that device. The standalone self-enumeration mode remains for CLI tools. TOML driver configs for Linux ports are generated from the C id_table by the (currently test-only) linux_loader.

B3 — SILENT DEAD FEATURES: no pciehp or AER producers

unified_events.rs polls /scheme/pci/pciehp and /scheme/acpi/aer every 500 ms. Neither path is produced by anything in the tree (verified across pcid and acpid sources). Both readers fail-soft on !path.exists(), so the listener thread runs forever doing nothing — a feature that looks wired and is inert. The orphaned P3-pcid-aer-scheme.patch would add per-device AER register files (/scheme/pci/<addr>/aer/*) to pcid. pciehp has no producer patch at all.

Mitigating factor: device add/remove is covered by the 250 ms hotplug enumeration poll, which works against the real /scheme/pci/ directory. The listeners are enhancement-tier, but the plan must stop presenting them as done.


3. Implementation quality by component

Component Grade Evidence
redox-driver-core manager + dynids A Real probe semantics, priority ordering, dynid add/remove/list with validation; v2.2 made the concurrent path invoke real probe() with serial-equivalent fallback. Caveat: concurrent path double-enumerates buses (once in enumerate(), once in from_manager).
driver-manager probe/spawn (config.rs) B+ (was D) v2.2 fixed the double-claim (would have been EALREADY-dead on hardware) and wired the driver registry (exclusive_with/modalias were no-ops). Claim model itself is B1 — redesign in plan.
Scheme server (scheme.rs) B Correct trait impl after v2.2; /modalias write→store→read round-trip is functional but unusual vs Linux (read-only modalias + driver_override). Missing operator surface (§ G4).
Policy/blacklist (policy.rs) A Real TOML loading, live SharedBlacklist::replace(), and — after v2.2 — a actually installed SIGHUP handler.
Signal handling (reaper/sighup) B+ (was F) v2.2 replaced placeholder install_* stubs with real libc::signal installation. Before that, the reaper and reload never fired in production.
Hotplug (hotplug.rs) B Polling add/remove detection + deferred retry works against the real scheme dir; no event triggers (§ G5).
unified_events / aer / pciehp D Parsers are real; producers don't exist (B3). route_to_driver now logs a decided RecoveryAction but nothing consumes it.
Heartbeat B+ Counters wired into enumerate/hotplug in v2.2; publishes real numbers now.
modern_tech.rs D Advisory theater — see § 5.
linux_loader.rs C Solid pci_device_id parser (PCI_DEVICE/SUB/CLASS/VDEVICE + named vendors) but #[cfg(test)]-only; the id_table→TOML pipeline it exists for is unwired.
exec.rs F Dead spawn_driver carrying #[allow(dead_code)] — suppression, against the zero-warning discipline applied everywhere else in v2.2. Remove.
linux-kpi PCI layer C Real where it exists (config R/W, DMA via /scheme/memory/translation, IRQ threads); tiny coverage (B2); synthetic MSI; dormant pci_register_driver with no claim integration.
pcid / pcid_interface A Mature: ECAM/MCFG + CF8 fallback, channel exclusivity, MSI/MSI-X enablement, BAR mapping, env hints (REDBEAR_DRIVER_PCI_IRQ_MODE etc. consumed end-to-end). The strongest layer in the stack.

4. Plan viability

The D-phase/C-phase structure is sound and the § 0.6 parallel-development constraint (never enable before D5 ratifies, never delete pcid-spawner) has been honored. What the plan got wrong:

  1. It marked capabilities " Done" at compile grade that are broken at runtime — the <addr>/bind claim (plan lines 94/134/771/921/975), the pciehp listener, and the AER listener are the three cases. The D5 audit needs an explicit runtime bar per capability (see § 9, P0-4).
  2. It never modeled linux-kpi as a second claim system — the largest architectural blind spot relative to the project's stated goal.
  3. It presented exclusive_with as "the CachyOS pattern." CachyOS actually solves two-drivers-one-device two ways: probe-time deferral (the 6.17.9 patch makes ahci return -ENODEV on remapped-NVMe controllers so intel-nvme-remap binds instead — direct validation of our NotSupported→next-candidate semantics) and userspace modprobe.d blacklists. Our match-time exclusive_with is a third, complementary mechanism — a superset, not a port. The plan now says so.
  4. "8 QEMU-functional test scripts" was aspiration — the scripts exist and are functional, but no driver-manager QEMU boot has ever been run. Runtime validation is now a hard gate (P0-4).

5. AI-introduced code patterns (the "strange code" hunt)

Patterns found across this session's review, most now fixed in v2.2:

  1. Fake concurrency (fixed v2.2). ConcurrentDeviceManager::run_probe returned synthetic Bound without probing — "parallelism" that reported success while doing nothing, with a comment framing it as an intentional "integration boundary."
  2. Placeholder installers (fixed v2.2). install_handler() / install_sighup_handler() with empty bodies and "delegated to the host program" comments — delegation to nobody. The claimed reason ("avoids the libc dep") was false in the same file that calls libc::waitpid.
  3. Advisory theater (open — plan P0-3). modern_tech.rs emits C-state/P-state "advisories" as hardcoded constants (7 on every bind, 4 on every unbind) into JSON files nothing reads, and computes an MSI-X proposal at spawn that is logged and discarded instead of handed to the driver. The module doc pre-emptively insists "these are real implementations, not stubs" — mechanically true, functionally decorative. modern_tech_for_path(key) ignores its argument and returns a global.
  4. Suppressed dead code (open — plan P0-3). exec.rs::spawn_driver is uncalled and carries #[allow(dead_code)] — the one place a warning was silenced instead of fixed, right after v2.2 removed every other instance.
  5. Unwired registries (fixed v2.2). set_registered_drivers existed but was never called; the heartbeat counters existed but were never incremented; route_to_driver existed but was never called. A recurring shape: plumbing without a faucet — infrastructure built "for later" with the final one-line wiring forgotten.
  6. Redundant invention (open — plan P0-1). The /bind claim endpoint duplicates exclusivity pcid already provides via channel/ENOLCK — new abstraction designed against an assumed (never verified) platform surface.

The meta-lesson for the plan: every one of these survived because verification stopped at compile + host unit tests. The audit gate (§ 0.5) catches stub-shaped text, not behavior-shaped fiction. P0-4 adds the missing gate.


6. Adjacent technology assessment

Layer State Interaction with driver-manager
pcid Strong. ECAM+CF8, channel exclusivity (ENOLCK), per-device config + channel. Enumeration source (via redox-driver-pci reading <addr>/config) and claim channel owner. Missing: /bind (unneeded after P0-1), AER/pciehp producers (P2).
pcid_interface Strong. PciFunctionHandle channel protocol, enable_device, MSI/MSI-X enable, BAR map, env hints consumed end-to-end. The correct claim+channel vehicle (P0-1 builds on it).
redox-driver-pci Adequate. Directory+config-file enumeration feeding DeviceInfo. driver-manager's bus. No hotplug push — polling only.
redox-driver-sys Strong. Quirks tables (compiled + TOML + DMI), MMIO/IRQ wrappers. Quirk flags flow into spawn env vars end-to-end. Two pre-existing warnings (unused Once, unused vendor).
linux-kpi Partial (B2). Real DMA/IRQ/config; ~15% PCI API; synthetic MSI; dormant registration path. None today. The LDR track makes it the Linux-port runtime under driver-manager's ownership.
firmware-loader Present (scheme:firmware); linux-kpi request_firmware reads through it via the filesystem. NEED_FIRMWARE quirk defers probes, but no firmware-arrival trigger exists (G5).
redox-drm + amdgpu Works via in-process FFI; bypasses both claim systems. Spawned as a driver-manager child (class 0x03). LDR-4 migrates amdgpu to the spawned-mode path.
redbear-iwlwifi CLI tool, manual PCI scan, spawned by nothing. LDR-3 gives it a driver-manager config and proper claim.
pcid-spawner The incumbent. Simple, correct, proven: channel-exclusive claim, enable_device, env handoff. The behavioral reference for P0-1; stays in-tree as the dormant fallback after cutover.

7. Linux 7.1 cross-reference (divergence audit)

Mechanism Linux 7.1 (local/reference/linux-7.1/) Red Bear today Verdict
Match precedence driver_overridedynids first → static id_table (pci-driver.c:136-180) dynids static by driver priority; no driver_override Aligned except G3 — add per-device override
Match "scoring" None — first table entry wins; order = specificity (pci.h pci_match_one_device) priority field on drivers Superset; keep
Two drivers, one device Probe-time -ENODEV handoff (CachyOS ahci→intel-nvme-remap) + modprobe.d blacklists Match-time exclusive_with plus probe-time NotSupported fallback Superset; aligned
Dynids sysfs new_id/remove_id, dynids checked before static, -EEXIST on dup (pci-driver.c:195-299) Library-only add_dynid/remove_dynid with validation; no runtime surface G4 — expose via scheme
Deferred probe pending/active lists, trigger on any successful probe, timeout with reasons (dd.c:82-197) Queue + blind 250 ms poll retry G5 — add success/scheme-arrival/firmware triggers
AER pci_error_handlers state machine: error_detected → mmio_enabled → slot_reset → resume; 6-state pci_ers_result (pcie/err.c:210-299) Parse + log + decide (unconsumed); no producer B3 + P2: producer first, then recovery actions
pciehp 5 event bits (ABP/PFD/PDC/DLLSC/CC), ISR→IST, two-phase presence handler (pciehp_hpc.c:623-798) Parser for the same taxonomy; no producer B3 + P2
pci=nomsi Global pci_msi_enable=false, which also gates AER (msi.c:985, aer.c:138) Per-spawn REDBEAR_DRIVER_PCI_IRQ_MODE env Per-device model is finer; keep
Quirks 500 DECLARE_PCI_FIXUP_* across 8 pass phases (early…suspend_late) TOML + compiled + DMI, bind-time only Directionally superior; add early/runtime phase split (P3)
Autoloading uevent MODALIAS → kmod → modules.alias (pci-driver.c:1587-1617) Poll → match_table/dynids; /modalias scheme = operator lookup Aligned; document the mapping
Operator surface per-driver bind/unbind/new_id/remove_id, per-device driver_override/rescan/remove /devices, /bound, /events, /modalias only G4 — add the write endpoints

8. CachyOS cross-reference

Source: local/reference/cachyos/linux-cachyos/0001-cachyos-base-all.patch (6.17.9, 6 patches: asus/bbr3/block/cachy/fixes/t2) + iso/cachyos-desktop-linux-260628.pkgs.txt.

  1. CachyOS does not redesign driver loading. Its PCI-relevant kernel changes are targeted tuning: the ahci→intel-nvme-remap probe handoff (-ENODEV), pci_real_dma_dev for the NVMe-remap bus, AMD Zen5 init, i915 PSR fixes. This validates our approach: mirror Linux 7.1 semantics, deviate only where the microkernel forces it.
  2. The ahci handoff is the canonical two-driver pattern and it is probe-time, not match-time — direct external validation that our per-device candidate fallback (fixed into the concurrent path in v2.2) models reality. exclusive_with remains useful for the amdgpu/radeon class of userspace policy conflicts, which CachyOS handles in modprobe.d — our /etc/driver-manager.d blacklist is the same layer.
  3. Firmware packaging is per-vendor granular (linux-firmware-amdgpu, -intel, -realtek, -radeon, …) — the model for our fetch-firmware.sh subsets and future firmware-loader packaging (P3).
  4. Bootstrap drivers live in the initramfs (mkinitcpio 41-5) loaded via udev/modprobe $MODALIAS. Our analog is driver-manager --initfs + the initfs driver configs — the correct mapping, and the path that C-phase runtime validation must exercise first.
  5. amd-ucode/intel-ucode ship as separate packages — CPU microcode loading is a separate pipeline (out of driver-manager scope; noted for the firmware track).
  6. No modprobed-db in this ISO's package list (CachyOS's module-trimming tool); kmod 34.2 present.

9. Findings register (severity-ordered)

# Severity Finding Plan item
B1 Blocker /bind endpoint absent; claim model runtime-dead P0-1 (claim-via-channel)
B2 Goal-blocker linux-kpi vs driver-manager: two claim systems, zero awareness; ~15% API; synthetic MSI LDR-1…LDR-5
B3 High pciehp/AER producers absent; listeners inert P0-2 (restate), P2-1 (producers)
G1 High Orphaned P3 patches in local/patches/base/ (bind/aer/uevent) violate orphan governance P0-2 (decision-tree resolution)
G2 High Zero runtime validation at any layer; D5 "Done" claims are compile-grade P0-4 (runtime gate)
G3 Medium No driver_override equivalent (Linux Tier-1) P2-2
G4 Medium dynids library-only; no /new_id /remove_id /bind /unbind /rescan scheme surface P2-2
G5 Medium Deferred retry is blind polling; no success/scheme-arrival/firmware-arrival triggers P2-3
G6 Medium modern_tech advisory theater; msix proposal discarded P0-3 (strip to honest core)
G7 Low exec.rs dead code with #[allow(dead_code)] P0-3 (delete)
G8 Low Concurrent path double-enumerates buses P3 (fold enumeration into dispatcher)
G9 Low redbear-iwlwifi unspawned; amdgpu bypasses both models LDR-3, LDR-4
G10 Low Pre-existing warnings in libredox/pcid/redox-driver-sys; stale redox-driver-core/src/ duplicate tree P3 hygiene

10. What is genuinely good

Worth stating plainly, because the register above is necessarily harsh:

  • The D-phase skeleton is right: DeviceManager/dynids/deferred-probe/ policy/quirks map cleanly onto Linux 7.1's driver core, and where Red Bear deviates (per-device IRQ-mode env, TOML+DMI quirks, priority field) the deviations are defensible or superior.
  • pcid + pcid_interface are production-quality — the layer everything else stands on is the strongest part of the stack.
  • The v2.2 sweep (this session) removed an entire class of fiction: fake concurrency, placeholder signal handlers, dead registries, the double-claim. The tree after v2.2 is honest at compile grade.
  • The § 0.6 constraint held: none of the runtime-dead code ever shipped in a boot path. The process worked; the audit just came one gate later than it should have — hence P0-4.

Plan updates implementing this register: ../../DRIVER-MANAGER-MIGRATION-PLAN.md v3.0.


11. OS-services integration assessment (added 2026-07-23, cutover round)

How driver-manager fits the wider OS service architecture — D-Bus, zbus, udev-shim, driver-params, and the init system:

Surface State Verdict
init service model Fixed in this round. init learned ConditionPathExists (with ! negation) — the C0 dormancy was previously a parser-rejection accident. Cutover wiring: driver-manager services run by default; pcid-spawner services are gated behind /etc/driver-manager.d/disabled as the operator fallback. Correct and reversible
driver-params daemon Live integration, pre-existing and correct. driver-params reads /scheme/driver-manager/bound (read_driver_manager_bound, tolerating ENODEV when the scheme is absent) and exposes enabled=true params per bound driver; driver-manager's notify_bind writes per-device params the other direction. Working bridge
udev-shim Reads /scheme/pci directly — the raw bus view, without driver-binding information. On Linux, libudev exposes DRIVER= and driver links; post-cutover udev-shim should also read /scheme/driver-manager/{bound,devices} so udisks/KDE-Solid-style consumers see bindings. 🟡 Gap (P3, non-blocking)
D-Bus / zbus driver-manager has no D-Bus presence, and that is correct today. The Linux analog: udev events feed domain daemons (udisks2, upower, NetworkManager) which own the D-Bus APIs — udev itself does not publish on the bus. Red Bear's scheme is the native device-event transport; a driver-manager D-Bus service now would duplicate the scheme for zero consumers. If a desktop consumer ever needs bind signals over D-Bus, the pattern is a thin zbus bridge daemon (same shape as redbear-sessiond for login1), not D-Bus code inside driver-manager. Correctly absent; bridge only on demand
redbear-dbus-services No activation entry needed (no bus name). Nothing to do
redox-drm / display stack Already convergent: honors the pcid handoff (connect_default), uses only non-exclusive config + MMIO access. Verified
initfs storage path 40_driver-manager-initfs.service (oneshot, --initfs) is default-on; pcid-spawner-initfs gated as fallback. The initfs driver-manager exits after enumeration (oneshot) — correct for the pre-switchroot phase. Wired; first QEMU gate target

Cutover commits: e30f2499 (init gate), c72d4247 (service gates), b338760111 + parent bumps (config cutover).