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).
121 KiB
Red Bear OS — pci-spawner → driver-manager Migration Plan
Document status: v3.1 canonical planning authority (supersedes v3.0 with the completed P0/LDR/P2 implementation and the operator-ratified cutover: driver-manager now owns the boot path in all redbear-* configs; pcid-spawner is gated as the fallback)
Generated: 2026-07-20
Toolchain: Rust nightly-2026-05-24 (edition 2024)
Architecture: Microkernel OS in Rust (Redox fork)
Cross-reference baseline: Linux kernel 7.1 at commit ab9de95c9 (local/reference/linux-7.1/), CachyOS (local/reference/cachyos/ — linux-cachyos 0001-cachyos-base-all.patch 6.17.9 + desktop ISO 260628)
Assessment: local/docs/evidence/driver-manager/ASSESSMENT-2026-07-22.md (findings B1–G10, § 11 services integration)
v3.1 status update over v3.0 (implementation + cutover round, 2026-07-23):
The v3.0 work program is implemented and the operator has ratified the
cutover: driver-manager owns the boot path in every redbear-*
config; pcid-spawner is retired from configs and gated behind
/etc/driver-manager.d/disabled as the operator fallback (never
deleted). Future OS development is based on driver-manager.
Completed items:
- P0-1 ✅ Claim-via-channel collapse.
probe()does a singlePciFunctionHandle::connect_by_path();ENOLCK→ next candidate;enable_device+into_inner_fd→PCID_CLIENT_CHANNEL.claim_pci_device/open_pcid_channeldeleted;SpawnedDriverholds the channel fd (claim lifetime == child lifetime). - P0-2 ✅ Orphan patches resolved. bind-scheme + uevent-fix moved to
local/patches/legacy-superseded-2026-07-22/with a SUPERSEDED.md audit entry; aer-scheme retained as the P2-1 producer blueprint. - P0-3 ✅ Advisory theater stripped.
modern_tech.rsandexec.rsdeleted; the useful parts correctly wired as spawn env hints (REDBEAR_DRIVER_IOMMU_GROUP/REDBEAR_DRIVER_NUMA_NODE/REDBEAR_DRIVER_MSIX_VECTORS); redox-driver-core C/P-state coordinators removed (no consumers). - LDR-2 ✅ Spawned-mode
pci_register_driver. linux-kpi honorsPCID_CLIENT_CHANNEL(probe only the granted device; never enumerate). Standalone mode retained for CLI tools. - LDR-3 ✅ iwlwifi onboarded.
--daemonmode (honorsPCID_DEVICE_PATH, full-init, resident) +70-wifi.tomlstaged;--import-linux-idswires linux_loader into production (Linuxid_table→[[driver.match]]TOML). - LDR-4 ✅ Verified convergent. redox-drm already honors the pcid handoff; no changes needed.
- LDR-5 ✅ linux-kpi API completion. Real MSI/MSI-X via
pcid_interface (vector allocation + table programming; linux-kpi owns
the channel in linux-kpi daemons),
pci_request_regions,pcie_capability_read/write_*,pci_set_power_state/pci_save_state/pci_restore_state. C header synced. - P2-2 ✅ Operator surface. Scheme endpoints
bind,unbind,new_id,remove_id,driver_override,rescan; DeviceManager gained Tier-1driver_overrideprecedence +bind_device. - P2-3 ✅ Success trigger. A successful bind immediately retries
deferred probes (Linux
driver_deferred_probe_trigger), inrun_enumerationand the scheme bind handler. - Cutover ✅. init learned
ConditionPathExists(with!negation — the C0 dormancy was a parser-rejection accident); driver-manager services run by default in init.d/init.initfs.d and allredbear-*configs; pcid-spawner services gated to/etc/driver-manager.d/disabled.make lint-configclean. - Services integration ✅ (assessment § 11). driver-params bridge verified live; udev-shim driver-binding view noted as a P3 gap; D-Bus correctly absent (bridge-on-demand pattern documented).
Open items (unchanged): P0-4 QEMU runtime gate (build running; first boot with driver-manager owning the path), P2-1 pcid producers (AER registers, pciehp slot events), P3 policy/hygiene (quirk phases, AER recovery actions, per-vendor firmware packaging, udev-shim driver-binding view, dep-crate warnings, stale-tree cleanup).
v3.0 status update over v2.2 (the assessment round):
v3.0 records the first runtime-grade audit of the migration. 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 found that the v1.3–v2.2 "Done" claims were compile-grade, and that three of them are broken at runtime. The full evidence register (B1–G10) is in the assessment doc; the corrections and the resulting work program follow.
Corrections to earlier rounds (stale claims removed):
- The
<addr>/bindclaim endpoint does not exist. Earlier rounds marked "D1 C4 BAR request ✅ Done —claim_pci_devicevia<addr>/bind" (and the § diagram "claims devices via<addr>/bind"). pcid exposes onlychannelandconfigper device; the patches that would add/bind(local/patches/base/P3-pcid-bind-scheme.patch,P3-pcid-aer-scheme.patch,P3-pcid-uevent-format-fix.patch) are orphaned — never committed tosubmodule/base, and the cookbook does not apply patches forpath =fork recipes. At runtime, every probe would failENOENTand defer-retry forever, binding nothing. Resolution: P0-1 below — the separate claim endpoint is dropped in favor of pcid's existing channel exclusivity (ENOLCK), the model pcid-spawner has always used. The orphaned patches are resolved per the orphan-patch decision tree (bind-scheme: SUPERSEDED by the channel model; aer-scheme: kept as the P2-1 producer blueprint). - The pciehp and AER listeners are inert. v1.9/v2.0 marked them
done; in fact nothing in the tree produces
/scheme/pci/pciehpor/scheme/acpi/aer, and both readers fail-soft on missing files. Device add/remove is covered by the 250 ms hotplug enumeration poll (which works). The listeners are restated as parsers ready, producers pending (P2-1). exclusive_withis not "the CachyOS pattern." CachyOS solves two-drivers-one-device by probe-time deferral (its 6.17.9 patch makes ahci return-ENODEVon remapped-NVMe controllers sointel-nvme-remapbinds instead) and by userspace modprobe.d blacklists. Match-timeexclusive_withis a third, complementary mechanism — a superset, retained. The plan text is corrected.modern_tech"wired" was advisory theater. Bind/unbind emit hardcoded C/P-state constants into JSON files nothing reads, and the MSI-X proposal is computed at spawn and discarded. P0-3 strips it to the honest core.- "8 QEMU-functional test scripts" was aspirational. The scripts exist; no driver-manager QEMU boot has ever been run. Runtime validation is now a hard gate (P0-4) and the D5 audit grades capabilities runtime as well as compile.
v3.0 work program (the tracks):
| Track | Items | Goal |
|---|---|---|
| P0 — Correctness blockers | P0-1 claim-via-channel collapse · P0-2 restate pciehp/AER + resolve orphaned patches · P0-3 strip modern_tech + delete exec.rs · P0-4 runtime validation gate (QEMU) | Make the existing D-phase true at runtime |
| LDR — Linux-Driver-Reuse | LDR-1 unified claim model · LDR-2 spawned-mode pci_register_driver · LDR-3 iwlwifi onboarding · LDR-4 amdgpu path convergence · LDR-5 linux-kpi API completion |
The critical goal: reuse Linux drivers |
| P2 — Operator & event surface | P2-1 pcid producers (AER registers, pciehp slot events) · P2-2 scheme write endpoints (bind/unbind/new_id/remove_id/driver_override/rescan) · P2-3 trigger-based deferred retry |
Linux-grade operability |
| P3 — Policy & hygiene | quirk pass phases (early/runtime) · AER recovery actions (pci_ers_result mapping) · per-vendor firmware packaging · dep-crate warnings + stale-tree cleanup |
Long-term evolution |
P0 — Correctness blockers (preconditions for everything else):
- P0-1 Collapse claim into the channel open. Replace
claim_pci_device()+open_pcid_channel()with a singlePciFunctionHandle::connect_by_path():ENOLCK→ "already claimed → next candidate" (mirrors Linux's probe-time-EBUSY/-ENODEVhandoff), thenenable_device()andinto_inner_fd()→PCID_CLIENT_CHANNEL. Claim lifetime == channel fd lifetime == child lifetime — exactly correct, pcid-spawner-proven, zero pcid fork changes. The exclusive_with check moves after the channel open (claim-then-check, as in v2.1–v2.2, with close-on-defer). - P0-2 Restate pciehp/AER; resolve orphaned patches. Plan text
corrected above.
P3-pcid-bind-scheme.patch→ SUPERSEDED (channel model);P3-pcid-aer-scheme.patch→ retained as the P2-1 producer blueprint;P3-pcid-uevent-format-fix.patch→ split: AER parts with the producer work, uevent stub dropped (polling is the accepted v1 model). Move superseded files per the orphan-patch decision tree. - P0-3 Strip modern_tech to the honest core; delete exec.rs. Remove
the constant C/P-state JSON writers (no consumers); pass the MSI-X
proposal to the child as
REDBEAR_DRIVER_MSIX_VECTORS=<n>(same env-hint channel as the quirk hints) or drop it until a consumer exists. Deleteexec.rs(deadspawn_driverwith#[allow(dead_code)]). - P0-4 Runtime validation gate. Boot
redbear-miniin QEMU with driver-manager enabled in place of pcid-spawner (C-phase dry run): e1000 binds through the real claim path,/boundreports it, heartbeat counters move, deferred drivers retry and bind. Until this passes, no capability is "Done" — the D5 audit gains a Runtime column.
LDR — Linux-Driver-Reuse track (the critical goal):
One ownership model: driver-manager owns enumeration, matching, policy, claiming, and spawning for native and Linux-port daemons alike; linux-kpi is the in-process Linux API surface inside the spawned daemon, never a second enumerator.
- LDR-1 Unified claim model. All device binding flows through
driver-manager's match→claim→spawn. linux-kpi never opens
/scheme/pci/for binding decisions. - LDR-2 Spawned-mode
pci_register_driver. WhenPCID_CLIENT_CHANNELis present, linux-kpi skips enumeration, wraps the granted channel, matches only the device it was spawned for, and calls the C probe for exactly that device. Standalone self-enumeration mode remains for CLI tools. This eliminates the double-claim risk class (assessment B2). - LDR-3 redbear-iwlwifi onboarding. Generate its driver-manager TOML
from the iwlwifi
id_tablevialinux_loader(un-cfg(test)the pipeline:linux_loader → TOML → /lib/drivers.d/); spawn it as a daemon under the unified claim; retire its manual/scheme/pci/scanning CLI path for the auto-bind case. - LDR-4 amdgpu path convergence. Keep redox-drm as the spawned
daemon (its FFI into
libamdgpu_dc_redox.sois legitimate in-process composition, not a second claim system — but its PCI open must go through the granted channel, notPciDevice::open_locationon its own enumeration). - LDR-5 linux-kpi API completion (priority order). (a) real MSI/MSI-X
via
pcid_interface::enable_feature(replace syntheticallocate_vectors); (b)pci_request_regions/pci_release_regionsas BAR-mapping claims over the channel; (c)pcie_capability_read/ write_*via channel config R/W (exists in pcid_interface); (d) power state (pci_save_state/restore_state/set_power_statevia PMCSR); (e)pci_reset_function(FLR) if pcid grows the hook.
P2 — Operator & event surface:
- P2-1 pcid producers. Per-device AER register files from the retained aer-scheme blueprint; pciehp slot-status events (the five hardware-defined bits ABP/PFD/PDC/DLLSC/CC) produced by pcid polling the slot status register.
- P2-2 Scheme write endpoints.
/bind,/unbind,/new_id,/remove_id,/driver_override,/rescanon the driver-manager scheme — the Linux sysfs operator surface, scheme-shaped. dynids are already implemented in redox-driver-core; this exposes them.driver_overrideadds Linux's Tier-1 match precedence. - P2-3 Trigger-based deferred retry. Retry deferred probes on (a)
any successful bind (mirrors
driver_deferred_probe_trigger), (b) dependency-scheme arrival (watch/scheme/for the named scheme in "dependency scheme not ready" deferrals), (c) firmware-loader readiness forNEED_FIRMWAREdeferrals. Blind 250 ms polling becomes the fallback, not the mechanism.
P3 — Policy & hygiene: quirk pass phases (early pre-BAR vs runtime —
Linux has 8 pci_fixup_pass phases; we need at least the early/runtime
split for pre-BAR quirks); AER recovery actions wired to drivers
(pci_ers_result 6-state mapping over scheme IPC); per-vendor firmware
packaging mirroring linux-firmware splits; dependency-crate warnings
(libredox, pcid Option::insert, redox-driver-sys Once/vendor);
remove the stale local/recipes/drivers/redox-driver-core/src/
duplicate tree (live tree is source/src/); fold the concurrent path's
double bus enumeration into one pass.
v2.2 status update over v2.1:
v2.2 records the ninth round of D-phase work, fixing defects found by the
first full x86_64-unknown-redox compile of the v2.1 tree and by a
dead-code/warning sweep:
- Concurrent probe path is now real (
redox-driver-core). TheConcurrentDeviceManagerpreviously returned a syntheticProbeResult::Boundfromrun_probewithout ever callingDriver::probe()— on any bus with ≥ 4 unbound devices (the productionmax_concurrent_probes = 4config) this reported bindings with no driver spawned and bypassedexclusive_with, quirks, and the blacklist.DeviceManagernow stores drivers asArc<dyn Driver>; concurrent jobs carry priority-ordered candidate lists (static match table and dynids); workers invoke the realprobe()with serial-equivalent per-device semantics (Bound → claim + record, Deferred → enqueue, Fatal → stop, NotSupported → next candidate) and apply bound/deferred state in the worker. Three new tests cover real probe invocation, candidate fallback, and deferred enqueue. - Redox-target build errors fixed (
driver-manager).SchemeSync::writenow matches the redox-scheme trait (buf: &[u8]); the/modaliaswrite stores the looked-up driver name per-handle and a subsequent read returns it.O_WRONLY/O_RDWRcome fromsyscall::flag(usize) instead oflibc(i32). - Double-claim bug fixed.
probe()claimed the PCI device twice (once before theexclusive_withcheck, once before spawn); since pcid binds are exclusive, the second claim would always failEALREADYon real hardware and no device would ever bind. The probe now claims once and threads the same handle through to spawn. - Driver registry wired.
set_registered_drivers()is now called at startup — previously the registry stayed empty, making theexclusive_withcheck and the/modaliaslookup silent no-ops. - Real signal handlers.
SIGCHLDandSIGHUPhandlers are now actually installed vialibc::signalinsidespawn_reaper_thread/spawn_reload_worker. The previousinstall_handler/install_sighup_handlerbodies were empty placeholders, so the reaper and the blacklist reload never fired in production. - Heartbeat counters wired.
record_bound/record_spawned/record_deferred/record_on_errorare called from the enumerate path;record_bound/record_unboundfrom the hotplug loop. The heartbeat JSON no longer publishes permanent zeros. - AER routing wired. The unified event listener calls
route_to_driverper AER event against a live bound-device snapshot (newbound_device_pairs()scheme accessor) and logs the decidedRecoveryAction. - Dead code removed or test-gated. The superseded standalone
pciehp/AER listener threads, the single-variant
ProbeOutcomeenum,SharedBlacklist::len/snapshot, the placeholder install functions, the heartbeatcv/stopmachinery, andset_reload_flagare gone;end_to_end_test,linux_loader,set_reap_flag,compute_modalias, andpolicy::SharedBlacklist::snapshotare#[cfg(test)]-gated;compute_match_modalias/lookup_modaliasare#[cfg(any(target_os = "redox", test))](their only production caller is the redox-only scheme server). - Zero warnings.
driver-managercompiles warning-free on the host and on thex86_64-unknown-redoxtarget (remaining warnings are pre-existing in dependency crates: libredox, pcid, redox-driver-sys).
v2.2 test totals: 56 driver-manager + 33 redox-driver-core lib + 5
dynid integration = 94 tests, all passing. repo cook driver-manager
succeeds for x86_64-unknown-redox with zero crate-local warnings.
§ 0.5 audit gate at v2.2: 0 violations.
v1.4 status update over v1.3:
v1.4 records the second round of D-phase work: the policy loader is now
active (the redbear-driver-policy package now actually changes
spawn_decision_gate behavior at runtime), the --concurrent=N CLI
flag enables the SMP worker pool, the two C0 service files have been
committed in the local/sources/base submodule, and the unused
modern_tech orchestrator was removed (the redox_driver_core::modern_technology
helpers remain as a library for downstream consumers to integrate
when they exist). The § 0.5 audit gate still reports 0 violations
across 34 files. PciQuirkFlags is now wired into the actual driver
spawn (the REDBEAR_DRIVER_PCI_IRQ_MODE=intx_or_msi and
REDBEAR_DRIVER_DISABLE_ACCEL=1 env vars are passed to the spawned
child when the corresponding flags are set).
| Item | Status at v1.4 | Where |
|---|---|---|
| § 5.1 D0 foundation verified | ✅ Done | cargo test 33+3+16 = 52 tests passing |
§ 5.1 D1 C1 capability (add_dynid/remove_dynid) |
✅ Done | redox-driver-core/src/{dynid,manager}.rs |
§ 5.1 D1 C2 capability (Driver::remove + WAIT_FOR_REAPPEAR) |
✅ Done | driver-manager/src/config.rs (real SIGTERM+SIGKILL) |
| § 5.1 D1 C3, C6 (enable_device / set_bus_master) | ✅ Done | open_pcid_channel via pcid_interface::EnableDevice |
| § 5.1 D1 C4 (BAR request) | 🟡 Corrected at v3.0 | <addr>/bind never existed in pcid (see v3.0 correction 1); claim collapses into channel exclusivity per P0-1 |
| § 5.1 D1 C5, C7, C8, C12, C16, C17 (child-side) | ✅ Done | pre-existing driver daemons |
| § 5.1 D1 format coexistence | ✅ Done | convert_legacy function in config.rs |
| § 5.1 D1 SpawnDecision committee | ✅ Done | spawn_decision_gate() function |
| § 5.1 D1 policy blacklist | ✅ Done at v1.4 | policy::Blacklist::load_dir() + blacklist_match() consult at probe |
| § 5.2 D2.1 SMP concurrent probe | ✅ Done | redox-driver-core/src/concurrent.rs (worker pool) |
| § 5.2 D2.2 C-state / P-state advisors | ✅ Library only at v1.4 | redox-driver-core/src/modern_technology.rs (orchestrator removed) |
| § 5.2 D2.3 IOMMU + MSI-X + NUMA helpers | ✅ Library only at v1.4 | same module (combined surface) |
| § 5.2 D2.4 runtime PM hooks (suspend/resume) | ✅ Done | Driver::suspend/resume trait + signal_then_collect |
| § 5.2 D2.5 AER foundation (on_error) | ✅ Done | Driver::on_error + ErrorSeverity + RecoveryAction |
| § 5.2 D2.6 PciQuirkFlags integration (D4) | ✅ Done at v1.4 | driver-manager/src/quirks.rs (redox-driver-sys dep) + spawn-time env vars |
| § 5.2 D3 event-driven hotplug (polling fallback) | ✅ Done | hotplug poll reduced to 250ms; pcid event delivery is the next step |
| § 5.2 D4 audit-no-stubs.py + 8 test scripts | ✅ Done | 0 violations across 34 files |
| § 5.2 D4 redbear-driver-policy package | ✅ Done | local/recipes/system/redbear-driver-policy/ |
| § 5.2 D4 --concurrent=N CLI flag | ✅ Done at v1.4 | main.rs parses arg, calls ConcurrentDeviceManager::enumerate(cap) |
| § 5.2 D5 audit document | ✅ Done | local/docs/evidence/driver-manager/D5-AUDIT.md |
| § 5.2 C0 dormant service files | ✅ Committed at v1.4 | local/sources/base submodule (commit 0407d9cc) |
| Operator ratification | 🔴 Not yet | required before C1 begins |
v1.4 test totals: 52 tests across the three crates, all passing.
§ 0.5 audit gate at v1.4: 0 violations across 34 scanned files.
v1.3 status update over v1.2:
v1.3 records the full D-phase implementation: every D1–D4 deliverable
in the § 5.1 D-phase is now in source. The § 0.5 comprehensive-implementation
audit gate (local/scripts/driver-manager-audit-no-stubs.py) returns
zero violations across 34 files. The § 0.6 parallel-development
constraint is preserved — the migration is fully implemented but driver-manager
remains excluded from any boot path until operator ratification. See
local/docs/evidence/driver-manager/D5-AUDIT.md for capability-by-capability
status.
| Item | Status at v1.3 | Where |
|---|---|---|
| § 5.1 D0 foundation verified | ✅ Done | cargo test 28+5 + 3 + 13 = 49 tests passing |
§ 5.1 D1 C1 capability (add_dynid/remove_dynid) |
✅ Done | redox-driver-core/src/{dynid,manager}.rs |
§ 5.1 D1 C2 capability (Driver::remove + WAIT_FOR_REAPPEAR) |
✅ Done | driver-manager/src/config.rs (real SIGTERM+SIGKILL) |
| § 5.1 D1 C3, C6 (enable_device / set_bus_master) | ✅ Done | open_pcid_channel via pcid_interface::EnableDevice |
| § 5.1 D1 C4 (BAR request) | 🟡 Corrected at v3.0 | <addr>/bind never existed in pcid (see v3.0 correction 1); claim collapses into channel exclusivity per P0-1 |
| § 5.1 D1 C5, C7, C8, C12, C16, C17 (child-side) | ✅ Done | pre-existing driver daemons |
| § 5.1 D1 format coexistence | ✅ Done | convert_legacy function in config.rs |
| § 5.1 D1 SpawnDecision committee | ✅ Done | spawn_decision_gate() function |
| § 5.2 D2.1 SMP concurrent probe | ✅ Done | redox-driver-core/src/concurrent.rs (worker pool) |
| § 5.2 D2.2 C-state / P-state advisors | 🟡 Corrected at v3.0 | advisory theater (assessment G6) — P0-3 strips to honest core (MSI-X env hint or removal) |
| § 5.2 D2.3 IOMMU + MSI-X + NUMA helpers | ✅ Done | same module (combined surface) |
| § 5.2 D2.4 runtime PM hooks (suspend/resume) | ✅ Done | Driver::suspend/resume trait + signal_then_collect |
| § 5.2 D2.5 AER foundation (on_error) | ✅ Done | Driver::on_error + ErrorSeverity + RecoveryAction |
| § 5.2 D2.6 PciQuirkFlags integration (D4) | ✅ Done | driver-manager/src/quirks.rs (redox-driver-sys dep) |
| § 5.2 D3 event-driven hotplug (polling fallback) | ✅ Done | hotplug poll reduced to 250ms; pcid event delivery is the next step |
| § 5.2 D4 audit-no-stubs.py + 8 test scripts | ✅ Done | 0 violations across 34 files |
| § 5.2 D4 redbear-driver-policy package | ✅ Done | local/recipes/system/redbear-driver-policy/ |
| § 5.2 D5 audit document | ✅ Done | local/docs/evidence/driver-manager/D5-AUDIT.md |
| § 5.2 C0 dormant service files | ✅ Done | local/sources/base/init.{,initfs.d/}/*-manager.service |
| Operator ratification | 🔴 Not yet | required before C1 begins |
Test totals at v1.3:
redox-driver-core: 28 unit + 5 integration = 33 testsredox-driver-pci: 3 testsdriver-manager: 13 unit tests- Total: 49 tests, all passing
§ 0.5 audit gate at v1.3: 0 violations across 34 scanned files.
v1.2 status update over v1.1:
The § 0 strategic context and § 5 D/C phase structure are unchanged from v1.1.
This revision records what was implemented during the parallel-development
sprint (v1.1 → v1.2). See local/docs/evidence/driver-manager/D5-AUDIT.md for
the formal capability-by-capability audit.
| Item | Status at v1.2 | Where |
|---|---|---|
| § 5.1 D0 foundation verified | ✅ Done (v1.1) | redacted |
§ 5.1 D1 C1 capability (add_dynid/remove_dynid) |
✅ Done (v1.2) | local/recipes/drivers/redox-driver-core/source/src/{dynid,manager}.rs |
§ 5.1 D1 C2 capability (Driver::remove + WAIT_FOR_REAPPEAR) |
✅ Done (v1.2) | local/recipes/system/driver-manager/source/src/config.rs |
| § 5.1 D1 C3, C6 (enable_device / set_bus_master) | ✅ Covered (v1.2) | already in open_pcid_channel via pcid_interface |
| § 5.1 D1 C4 (BAR request explicitness) | ✅ Covered (v1.2) | already in claim_pci_device |
| § 5.1 D1 C5, C7, C8, C12, C16, C17 (child-side) | ✅ Done (pre-D1) | already in driver daemons |
§ 5.1 D1 format coexistence (legacy [[drivers]] + new [[driver]]) |
✅ Done (v1.2) | local/recipes/system/driver-manager/source/src/config.rs::convert_legacy |
| § 5.1 D1 SpawnDecision committee | ✅ Done (v1.2) | local/recipes/system/driver-manager/source/src/config.rs::spawn_decision_gate |
| § 5.2 D2 modern-tech surface (PM/AER/MSI-X/IOMMU/C-state/P-state) | 🟡 Partial (v1.2) | Driver::suspend/resume/on_error trait; signal_then_collect; SMP/IOMMU defer to D5 |
| § 5.2 D3 event-driven hotplug | 🟡 Polling fallback (v1.2) | redox_driver-pci::subscribe_hotplug returns Unsupported until pcid event delivery lands |
| § 5.2 D4 audit-no-stubs.py + 8 test scripts | ✅ Done (v1.2) | local/scripts/driver-manager-audit-no-stubs.py, local/scripts/test-driver-manager-*.sh |
| § 5.2 D4 redbear-driver-policy package | ✅ Done (v1.2) | local/recipes/system/redbear-driver-policy/ |
| § 5.2 D5 audit document | ✅ Done (v1.2) | local/docs/evidence/driver-manager/D5-AUDIT.md |
| § 5.2 C0 dormant service files | ✅ Done (v1.2) | local/sources/base/init.{,initfs.d/}/*-manager.service |
v1.1 additions over v1.0:
- New
§ 0 Strategic Contextwith six subsections (rationale, success criteria, upstream risk, modern tech surface, comprehensive principle, parallel development). - Restructured
§ 5 Phased migration planinto D-Phase (parallel development) and C-Phase (cutover & validation) tracks. The two tracks are sequential, not interleaved. - The original P0–P6 dual-mode strategy is folded into the new structure. driver-manager is now never enabled until the D-phase feature-complete gate (D5) ratifies.
- Cross-referenced § 0 references in § 1, § 2, § 4, § 5, § 9.
Title and intent
pcid-spawner is the current single-purpose daemon that reads /etc/pcid.d/*.toml,
walks /scheme/pci/, and forks driver daemons for matched devices. It is correct,
small (127 lines), and ships the boot-critical 00_pcid-spawner.service plus the
initfs 40_pcid-spawner-initfs.service unit. It cannot grow further without
becoming a different program: deferred-probe semantics, dependency graphs, runtime
hotplug, driver parameters, and a managed device tree all require the more capable
abstraction in local/recipes/system/driver-manager/ (already drafted but not
wired).
driver-manager is the intended replacement. Its implementation is partial: the
abstract redox-driver-core machinery exists, the DeviceManager orchestrates
buses/drivers/binding, a PciBus enumerates /scheme/pci, deferred probing and
hotplug are sketched in hotplug.rs, and a scheme:driver-manager server exposes
read-only observability. The piece that has never landed is the integration:
package it, write 00_driver-manager.service, retire pcid-spawner cleanly,
adopt the Linux-style capability split (C1–C18), and absorb the CachyOS policy
patterns that have proven necessary on AMD-first hardware.
This plan produces that migration without losing any boot path, honoring the
project's never-delete, never-disable, fix-root-cause policy
(local/AGENTS.md § COMPREHENSIVE IMPLEMENTATION POLICY). pcid-spawner stays
buildable and present in the tree at every phase; it is the safe fallback.
0. Strategic context
This section captures the why, what done looks like, upstream
risk, modern-technology surface, comprehensiveness principle, and
parallel-development constraint of the migration. Every later section
implements these principles — they are not aspirations, they are binding
constraints derived from local/AGENTS.md and the project's engineering
standards.
0.1 Rationale for driver-manager (why not extend pcid-spawner)
Three converging forces make pcid-spawner insufficient as the long-term
driver spawner:
-
Architectural ceiling. pcid-spawner is a 127-line single-purpose launcher that enumerates PCI devices and forks drivers against a static config table. It cannot grow to handle: deferred-probe semantics (driver needs a scheme that is not yet registered); dependency-graph scheduling (drivers depending on other drivers); runtime hotplug (PCIe native, USB, Thunderbolt); driver parameter mutation (a userspace tool writing
iwlwifiparameters at runtime); AER error recovery; runtime-PM suspend/resume. Adding any of these to a 127-line launcher requires restructuring into a different program. -
Driver coverage roadmap. Red Bear's planned driver surface grows from 17 to ~30+ daemons (per
local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md). New drivers —amd-mp2-i2cd,intel-thc-hidd, Wi-Fi (iwlwifi,mt76), GPU backends (redox-drmAMD DC variant), Thunderbolt, NVMe namespaces — each carry their own configuration model, dependency graph, and runtime parameter surface. A per-driver ad-hoc spawner is no longer maintainable. -
Driver ecosystem contract demands. Wi-Fi drivers need delayed probe (firmware load + regulatory database check); I2C/audio controllers need dependency-aware ordering (acpi → i2c → driver); GPU drivers need stable device-id→driver binding with explicit version handling; hotplug events require an event-driven loop, not a one-shot enumeration. pcid-spawner's architecture cannot satisfy any of these contractually.
What we lose by NOT moving to driver-manager:
- The existing
redox-driver-coreframework (Bus/Driver/DeviceManager/ ProbeEvent/DriverMatch/DriverParams) becomes dead code. - The hard-won work in
redox-driver-acpi,redox-driver-pci, andlocal/recipes/system/driver-params/becomes single-purpose. - Wi-Fi, USB hotplug, AER, and runtime-PM work has to be re-architected against a different manager primitive.
- The CachyOS-style policy layer (blacklist, autoload, ordering, options)
cannot live cleanly without a manager that reads
/etc/driver-manager.d/.
Why not just extend pcid-spawner:
- pcid-spawner is 127 LOC doing exactly one thing. Adding deferred probing + dependency graphs + hotplug + params + AER + PM grows it to thousands of LOC with a fundamentally different architecture.
- The
redox-driver-coreframework already exists with the right trait surface. Throwing it away to extend pcid-spawner would violatelocal/AGENTS.md§ LOCAL FORK SUPREMACY POLICY: "Red Bear adapts to upstream, never throws away working code." - The Linux-style capability split (C1–C18 in § 2.3) is the right design. pcid-spawner implements none of C9, C11, C13–C18; bolting them on is more work than building the manager they belong in.
Why this is happening now:
- The framework is already built (
local/recipes/system/driver-manager/is a compilable Rust crate at version0.3.1with 1250 LOC and a 783KB binary artifact). - The 17 drivers' Cargo manifests depend on
pcid_interface(PciFunctionHandle::connect_default()). They are already compatible with the manager contract — no driver-code changes are required at any phase. - Driver parameters and dependency gating are needed before Wi-Fi (per
local/docs/WIFI-IMPLEMENTATION-PLAN.md). - USB hotplug (per
local/docs/USB-IMPLEMENTATION-PLAN.md) needs the manager's hotplug surface. - AER recovery (per
local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md) needs the manager'sDriver::on_errorsurface.
0.2 Success criteria (definition of done)
The migration is complete when ALL of the following hold simultaneously:
-
Functional parity with pcid-spawner, verified by
local/scripts/test-driver-manager-parity.sh: every device pcid-spawner binds, driver-manager binds. No regression in driver set coverage. -
Linux 7.1 capability bridge complete. All of C1–C18 are implemented and tested, at the priority levels in § 2.3:
- P0 set: C1, C2, C3, C4, C5, C6, C7, C8, C12, C16
- P1 set: C9, C10, C11, C14, C17, C18
- P2 set: C13, C15
-
Modern technology surface integrated (§ 0.4):
- SMP-aware concurrent probe verified
- C-state and P-state coordination verified
- Runtime PM suspend/resume callback chain verified
- IOMMU group management verified
- AER error recovery verified
-
Hotplug functional and event-driven (not polling). Verified on QEMU synthetic and at least one real-hardware hot-add scenario.
-
Quirk policy honored.
redox-driver-sys::PciQuirkFlagsconsulted for every bind/remove decision.NO_MSIX,NO_PME,NEED_FIRMWARE,DISABLE_ACCEL,FORCE_LEGACY_IRQ,NEED_IOMMU, etc., all flow through the manager. -
Hardware validation matrix passes on:
- AMD Threadripper (canonical AMD-first profile)
- AMD Zen4 desktop
- Intel Alder Lake or later desktop
- At least one bus category each: storage (NVMe + AHCI), network (e1000d + rtl8168d), USB (xhcid), GPU (Intel or AMD display path)
-
30-day stability gate. In production cutover (C4), the
ConditionPathExistsguard on00_pcid-spawner.servicehas not fired in 30 days of continuous operation. -
Regressions clean. All of
local/scripts/test-driver-manager-{parity,active,initfs,hotplug,pm,cutover}.shpass on every release branch for the same 30-day window. -
No-deferred-comments cleanup. The inline deferred comments in
config/redbear-mini.toml:31andconfig/redbear-device-services.toml:9-13are removed (no longer accurate). -
Operator ratification. Explicit human acknowledgement at each C-phase boundary and at the final C4 transition.
0.3 Upstream risk — what if Redox upstream ships their own driver-manager
This is not a remote possibility — Redox upstream has been discussing driver-manager-style architectures. Red Bear's response is encoded as policy ahead of time.
What Redox upstream's plan could look like:
- A single-purpose scheme:pci consumer that spawns drivers against a config table (very similar to pcid-spawner, the canonical upstream).
- A more capable per-bus spawner (closer to Red Bear's driver-manager).
- A kernel-side enumeration manager (drastic, unlikely in Redox's microkernel model).
Red Bear's response strategy:
-
Compare against the C1–C18 capability list (§ 2.3). A manager is "equivalent" if it covers at least the P0 set of capabilities and exposes a compatible driver handoff contract.
-
If upstream ships a fully equivalent manager, evaluate migrating to use theirs per
local/AGENTS.md§ LOCAL FORK SUPREMACY POLICY (Red Bear adapts to upstream; never pins/holds back). This is the most likely scenario for the long term. -
If upstream ships a different approach (per-bus spawning, a kernel-side manager, a Lua-scripted policy layer), Red Bear's driver-manager becomes a Red Bear extension layer on top of the upstream primitive, OR is preserved as a local recipe that supplements the upstream manager rather than replacing it. The decision is taken at the time based on coverage.
-
If upstream ships the equivalent manager within 6 months of our D5 gate, the migration plan is re-evaluated against theirs. The C-phases (§ 5.2) become a no-op or a thin shim. pcid-spawner remains the fallback regardless.
Concrete protections already in the plan:
00_driver-manager.serviceis a service name, not a binary. Swapping thecmd=to upstream's binary is a one-line change.- The driver's runtime contract is
PCID_CLIENT_CHANNEL=<fd>(preserved from upstream Redox pcid-spawner). Any manager that delivers this env var keeps drivers unmodified. - The capability taxonomy (C1–C18) is a Red Bear design tool, not an upstream commitment. We use it to evaluate upstream choices.
What does NOT change:
local/recipes/system/driver-manager/source/is and remains a local Red Bear crate, not an upstream-fork clone.- The D-phase development continues regardless of upstream decisions.
- pcid-spawner stays as the safe fallback until C4.
0.4 Modern technology surface (SMP, S/P states, runtime PM, IOMMU, AER, hotplug, MSI-X, NUMA)
The driver-manager is genuinely modern by design. The technologies listed below are integrated as functional features (not stubs):
-
SMP awareness.
- Manager enumeration runs single-threaded for race-free exploration (correct — exploration must be deterministic).
- Concurrent probe dispatch:
max_concurrent_probesis enforced via a worker pool. Today it is policy metadata; in D2 it becomes real concurrent dispatch viastd::thread::scopeorrayon. - Per-vector IRQ CPU affinity: MSI-X vectors are bound to specific
CPUs based on topology (CPUID leaf
0Bhfor AMD,0Fhfor Intel). - Hotplug event loop is a separate thread; events post through a lockless queue to the manager (no shared mutable state with the probe path).
-
C-states (CPU idle states).
- Manager exposes driver-bound/unbound events to
cpufreqd/thermald. Hotplug-removing a device → CPU may drop to a deeper C-state. - Manager signals probe-completion → CPU may return to its former governor state.
- System-wide C-state transitions coordinated through
redox-driver-acpi(via scheme:acpi).
- Manager exposes driver-bound/unbound events to
-
P-states (CPU performance states).
- On driver bind/unbound, manager publishes a P-state advisory via
cpufreqdsocket or apstatescheme. - Cold-boot probe order is documented and P-state-aware: storage drivers spawn first (high P-state by default), audio/USB spawn later (low P-state allowed).
- Driver spawn during boot is gated on
cpufreqdconfirming P-state is high enough for the driver to initialise (e.g., NVMe completion timeouts require a high enough P-state).
- On driver bind/unbound, manager publishes a P-state advisory via
-
Runtime PM (per-driver).
Driver::suspend()/Driver::resume()callbacks are fully implemented with real state save + power-down (default: real D3 transition through pcid; notOk(())).- PCIe D-state transitions (D0 ↔ D3hot ↔ D0) coordinated through pcid's
SetPowerStaterequest viaPcidClient. - Hotplug-remove pre-empts runtime PM: the manager calls
force_suspend()before unbinding.
-
IOMMU groups (
local/recipes/system/iommu/).- Per-device group registration via the
iommuscheme. - DMA isolation enforcement: drivers cannot bypass their group's domain.
- Group-awareness for hotplug: a sibling device in the same group must also be quiesced before removal.
- Per-device group registration via the
-
AER (PCI Express Advanced Error Recovery).
Driver::on_error(severity)callback is fully implemented with severity dispatch (CORRECTABLE → log; NONFATAL → retry with masking; FATAL → slot reset → config restore → re-bind).- Manager registers with
pciehpfor hotplug events (Presence Detect Changed, Data Link Layer State Changed, MRL Sensor Changed). - Per-driver error recovery action: drivers implement
Driver::on_errorfor their specific hardware (NVMe reset, e1000 soft-reinit, etc.).
-
MSI-X vector allocation.
pci_alloc_irq_vectors()semantics preserved (MSI-X preferred, fallback to MSI, fallback to INTx; capability-aware honouring ofPciQuirkFlags::NO_MSIX).- Per-vector fd returned to driver (each vector independently
reachable through
/scheme/irq). - Multiple MSI-X vectors per device (e.g., xHCI's 16-vector interrupt setup).
-
NUMA awareness (when
local/recipes/system/numad/is live).- Driver's memory regions classified by NUMA node.
DmaBuffer::allocate(size, align, node_hint)prefers same-node.- Hot-add monitored for NUMA topology changes.
-
Modern vendor-specific paths.
- AMD: PSP firmware load (via
firmware-loader), DCN hardware blocks, GTT addressing, AMD-Vi IOMMU integration. - Intel: GGTT setup, hot-plug through
pciehp, MFG/RC6 power management, IOMMU group isolation. - Per-vendor path is a
redox-drmbackend (out of scope here); the manager's runtime-PM / quirk / hotplug hooks are exercised by all of them.
- AMD: PSP firmware load (via
Why these technologies, and why not others:
- We do NOT adopt
cgroup-vram(CachyOS extras) — Redox has no cgroups. - We do NOT adopt
sched-ext/BOREscheduler — kernel-side work, perlocal/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.mdPhase T. - We DO adopt CachyOS-style
modprobe.dpatterns (blacklist, options, autoload) — see § 2.2. These translate cleanly to/etc/driver-manager.d/.
0.5 Comprehensive implementation principle (no stubs, comprehensive code only)
The driver-manager implementation is comprehensive, systematic, methodical.
local/AGENTS.md § STUB AND WORKAROUND POLICY — ZERO TOLERANCE applies
without exception. The principle is re-stated here in the specific form that
applies to driver-manager:
- No stubs. Every public API in
redox-driver-core::driver::Driver,redox-driver-core::bus::Bus, anddriver-manager::scheme::*is fully implemented. Nounimplemented!(), notodo!(), no empty match arms returning trivial defaults. - No
#ifdef-guarded no-ops. No Rustcfggates that disable entire feature paths. - No
LD_PRELOAD-style tricks. No override layer that hides unimplemented features. - No
sed/awkrewriting. TOML config is parsed, not preprocessed.
Per-method completeness requirements (any violation blocks D5):
| Surface | Forbidden pattern | Required behaviour |
|---|---|---|
Driver::probe(info) |
unimplemented!() or Ok(()) skip |
Full match-table evaluation + dependency check + device claim + spawn |
Driver::remove(info) |
_ => Ok(()) drop |
Reclaim bind handle, terminate child via SIGTERM, refresh scheme:driver-manager:/bound |
Driver::suspend(info) |
Ok(()) default |
Real PCI D-state transition via pcid (SetPowerState), save config space, log |
Driver::resume(info) |
Ok(()) default |
Restore config space, walk IRQ vectors (re-arm MSI-X), signal ready |
Driver::on_error(info, severity) |
Ok(()) or Unsupported |
Severity dispatch: CORRECTABLE → log; NONFATAL → retry; FATAL → slot reset |
Driver::params() |
DriverParams::default() |
Real parameter definitions (priority, debug, exclusive_with, runtime flags) |
Bus::enumerate_devices() |
Ok(vec![]) skip |
Walk scheme:pci config files, populate DeviceInfo with vendor/device/class/subclass/prog_if/revision/subsystem IDs |
Bus::subscribe_hotplug() |
Err(BusError::Unsupported) |
Subscribe to /scheme/pci/events (or pcid uevent channel); return real HotplugSubscription |
Manager::enumerate() |
empty loop | Walk all buses, dispatch to drivers, emit ProbeEvents with deferred support |
Manager::retry_deferred() |
no-op | Re-evaluate deferred queue, re-probe if dependencies now satisfied, emit events |
Honest absence vs. stub matters. If a feature is genuinely not applicable (e.g., NUMA awareness on a non-NUMA system), the manager code calls a runtime probe that returns an honest answer:
// PREFERRED (D4+):
if !query_numa_support() {
return BusError::Unsupported; // honest absence
}
// FORBIDDEN at any phase:
// if !query_numa_support() { return Ok(()) }
// if cfg!(...) { return BusError::Unsupported } else { return Ok(()) }
The result: type signatures are honest, runtime is honest, documentation is
honest. A driver receiving Unsupported knows to fall back to uniform memory
without assuming the manager is silently doing the right thing.
Systematic means the same architectural pattern applies across all bus
types (PCI, USB, ACPI, future platform), all driver types (storage, network,
GPU, USB, audio), and all capability surfaces (C1–C18). The Driver trait is
the single contract; the Bus trait is the single contract; the
scheme:driver-manager:/<node> filesystem is the single observability surface.
Methodical means the work unfolds in the D-phases of § 5.1 with
verifiable exit criteria. No phase skips ahead. No phase declares done with
deferred items. The audit script local/scripts/driver-manager-audit-no-stubs.py
(D4 deliverable) is the formal gatekeeper.
Right-component implementation rule (from local/AGENTS.md § STUB AND
WORKAROUND POLICY):
| Symptom | Wrong fix | Required fix |
|---|---|---|
eventfd() not in relibc |
Stub in libc wrapper | Implement in relibc proper |
Bus::subscribe_hotplug not in pcid scheme |
Stub the manager side | Implement event delivery in pcid scheme |
| TOML config gap | Accept both formats but emit empty drivers | Implement FULL conversion from legacy pcid.d to new drivers.d |
| Driver needs runtime PM | suspend: fn() { Ok(()) } default |
Implement genuine D3 transition in pcid, expose via trait |
| AER not yet wired | Mark feature as Unsupported and ignore errors |
Implement severity dispatch + recovery action in Driver::on_error |
| Hotplug loop is polling-only | Document as "future work" | Implement event subscription via Bus::subscribe_hotplug() |
TOML [[driver.match]] field missing |
Skip silently | Return Err(format!("unsupported match field: {name}")) from loader |
0.6 Parallel development constraint (built but not utilised until fully developed)
The driver-manager is being implemented in parallel and not being utilized before fully developed. This is the binding development model.
Current state (2026-07-20):
- The local-recipe at
local/recipes/system/driver-manager/source/accumulates improvements in commits. pcid-spawneris the boot path in everyredbear-*.toml.driver-manageris NOT added to any[packages]section.driver-managerdoes NOT have an00_driver-manager.service.driver-manager's binary MAY be built locally (it is at 783KB), but it is NOT shipped in any produced ISO or harddrive image.- Inline deferred comments in
config/redbear-mini.toml:31andconfig/redbear-device-services.toml:9-13reflect this.
Operational reality during D-Phase:
local/recipes/system/driver-manager/source/is committed to the current branch as part of normal development.config/redbear-*.tomldoes not includedriver-manager = {}.- The ISO build (
./local/scripts/build-redbear.sh redbear-mini) does not include the driver-manager binary. - Test scripts
local/scripts/test-driver-manager-{parity,active,...}.share written but not in the ISO; they run only on developer workstations and CI runners. local/scripts/TOOLS.mdandlocal/AGENTS.mdcontinue to describe driver-manager as "in development" until D5 ratifies.
When does driver-manager become eligible for the boot path?
When ALL of D0–D5 exit gates are satisfied (see § 5.1):
- D0 (foundation) complete
- D1 (Linux capability bridge) complete — all P0 capabilities of C1–C18 implemented and tested
- D2 (modern technology surface) complete — SMP, S/P states, runtime PM, IOMMU groups, AER all implemented
- D3 (hotplug event-driven) complete
- D4 (quark integration + comprehensive audit) complete
- D5 (feature-complete gate) — formal audit confirms no stubs in § 0.5 matrix, operator ratification received
Until then, the parallel development continues without touching the live boot path.
Implications for the original P0–P6 phasing (v1.0 of this plan):
The original plan's P0 (dual-mode observation) and P1 (rootfs active driver-manager) are now folded into the C-phases (§ 5.2). The D-phases describe the pre-cutover work (i.e., the parallel development the team is currently doing). The C-phases describe the post-completion cutover. The two are sequential, not interleaved.
This constraint is the one operational reality that separates this migration from any ordinary refactor. A feature that is not fully complete is NOT shipped; the build system does not "auto-enable" it; CI does not bundle it. The first time driver-manager appears in a boot path is after D5 + operator ratification. No exceptions.
Position in the doc set
- This is the canonical planning authority for
pci-spawner→driver-managermigration beneathlocal/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md. - It does NOT replace any subsystem-specific plan. It supersedes the inline
comment in
config/redbear-mini.toml(line 31): "driver-manager requires driver config migration and is not yet ready" and the inline comment inconfig/redbear-device-services.toml(line 9-13): "driver-manager intentionally not included: it is the intended future replacement for pcid-spawner but is not yet ready (driver config migration pending) and was previously packaged-but-unused — its service launched pcid-spawner instead of driver-manager." - Cross-references:
local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md(where pcid-spawner hardening currently lives),local/docs/QUIRKS-SYSTEM.md+QUIRKS-IMPROVEMENT-PLAN.md(where the driver-manager→redox-driver-sys quirks bridge is planned),local/docs/USB-IMPLEMENTATION-PLAN.md(where the Bus trait is wired for USB),local/docs/WIFI-IMPLEMENTATION-PLAN.md(where matching semantics live for mac80211/cfg80211).
Scope
In scope:
- Lifecycle and integration of the existing
local/recipes/system/driver-manager/crate to replacelocal/sources/base/drivers/pcid-spawner/. - Format migration of
/etc/pcid.d/*.toml(legacypcid_interface::Config) to/lib/drivers.d/*.toml(driver-managerDriverMatch), plus a back-compat loader that lets both formats coexist during transition. - Init service rewrite:
00_pcid-spawner.service(rootfs) →00_driver-manager.servicewith00_pcid-spawner.serviceretained as a fallback unit that runs only whendriver-manageris absent on the initfs. Initfs unit40_pcid-spawner-initfs.serviceis renamed to40_driver-manager-initfs.servicewith the same blocking-oneshot contract. - Linux 7.1 capability bridging: the manager exposes, and the spawned drivers consume via PCID_CLIENT_CHANNEL env continuation, the capabilities C1–C18 (driver match, lifecycle, BAR mapping, IRQ vectors, runtime PM, AER error recovery, etc.).
- CachyOS policy patterns: policy-layer TOML with blacklist/options/autoload,
driven by the manager (not by
modprobesince Red Bear has no modules).
Out of scope:
- Adding USB / ACPI / platform buses to the device manager — those are in
local/docs/USB-IMPLEMENTATION-PLAN.mdand follow once the PCI manager lands. - Replacing
pciditself (the scheme:pci owner) — it stays the canonical PCI scheme surface, just like Linux'spci_bus_typeowning/sys/bus/pci/devices/. - Replacing
redox-driver-sys— driver-manager consumes it for the hardware-quirks surface once Q3 lands (seelocal/docs/QUIRKS-IMPROVEMENT-PLAN.mdTask 2.1). - Replacing the kernel — Redox's PCI scheme is userspace; the kernel's role (memory/irq/acpi/dt/serio schemes, MMIO mapping, MSI/MSI-X vector delivery) is unchanged.
Policy constraints (binding)
Every step in this plan inherits the following constraints from
local/AGENTS.md:
- NEVER DELETE a package, service, config, or patch. pcid-spawner stays.
- NEVER IGNORE a package by
"ignore". driver-manager lands as additive. - NEVER COMMENT OUT a service or config entry to "fix" a build.
- ALWAYS FIX the root cause when something breaks.
- The migration is operator-ratified: each phase has an explicit exit gate and is not closed until the gate is met AND human-acknowledged.
The durability rule applies: any source-tree edit during this migration is
mirrored into local/patches/ or local/sources/<component>/ in the same
work session. local/patches/base/ and the fork local/sources/base/ carry
the changes; recipes/core/base/source/ is not the durable location.
1. Current state assessment
1.1 What pcid-spawner does today
Source: local/sources/base/drivers/pcid-spawner/src/main.rs (127 lines)
Cargo: local/sources/base/drivers/pcid-spawner/Cargo.toml
Recipe: recipes/core/base/drivers/pcid-spawner/recipe.toml (path-fork)
Single main() function with the following behaviour, ordered:
- Wait for
/scheme/pci/to appear (200 × 50ms retry, 10s total) — this was added by Red Bear patchlocal/patches/base/P4-pcid-spawner-pci-coordinate-env.patchalongside the PCI coordinate env vars. - Load merged driver configs via
config::config("pcid")— concatenates/etc/pcid.d/*.toml(rootfs) or/scheme/initfs/etc/pcid.d/*.toml(--initfsmode) into oneConfigstruct. - Iterate
/scheme/pci/directory entries. For each:PciFunctionHandle::connect_by_path(&device_path)— opens the per-devicechannelhandle inscheme:pci. The handle is the mutual-exclusion lock: pcid returnsENOLCKto any second opener. This is the only claim mechanism.- On
ENOLCK: log debug,continue. On other errors: log error,continue. The spawner never aborts on a single bad device. config.match_function(&full_device_id)— legacy matching (class → subclass → interface → vendor/device, optional multi-vendoridsmap, optional device_id_range).- On miss: log debug,
continue.
- Enable the PCI function (
handle.enable_device()) — pcid writes the command register to enable bus mastering + memory + IO. - Build
Commandfromdriver.command, resolvingprogrameither absolute or prefixed/usr/lib/drivers/<program>. - Pass env vars to the child:
PCID_CLIENT_CHANNEL=<channel_fd>— the bincode channel FD the driver uses forPciFunctionHandle::connect_default()to reach pcid.PCID_SEGMENT=<hex>— added by Red Bear P4 patch.PCID_BUS=<hex>— added by Red Bear P4 patch.PCID_DEVICE=<hex>— added by Red Bear P4 patch.PCID_FUNCTION=<dec>— added by Red Bear P4 patch.
- Spawn via
daemon::Daemon::spawn(command)— parent blocks until the child signals readiness. This is the driver-ready handshake and is honoured by every existing driver daemon (ahcid,e1000d,xhcid, etc.). - Close the parent's copy of the channel FD.
1.2 What the existing driver-manager implements
Source root: local/recipes/system/driver-manager/source/
Cargo: local/recipes/system/driver-manager/source/Cargo.toml (Cat 1, version 0.3.1)
Recipe: local/recipes/system/driver-manager/recipe.toml (template=cargo, path="source")
Five source files (src/main.rs, config.rs, scheme.rs, hotplug.rs,
exec.rs) totalling ~1250 lines. Built and present at
recipes/system/driver-manager/target/x86_64-unknown-redox/stage/usr/bin/driver-manager
(783KB ELF, dated 2026-07-19). Binary-store artifact cached in
repo/x86_64-unknown-redox/driver-manager.pkgar. NOT packaged into any
current [packages] section of redbear-*.toml.
| Area | Status | Notes |
|---|---|---|
| Compile | ✅ | Builds clean on x86_64-unknown-redox |
| Core abstraction | ✅ | redox-driver-core (Bus/Driver/DeviceManager/ProbeEvent/ManagerConfig/DriverParams/MatchTable/HotplugEvent) |
| PCI bus | ✅ | redox-driver-pci::PciBus reads /scheme/pci raw config space; class/subclass/program-if/subdevice IDs returned |
| TOML config loader | ✅ | /lib/drivers.d/*.toml (name, description, priority, command, match, depends_on) |
| Probe pipeline | ✅ | ProbeResult::Bound / NotSupported / Deferred / Fatal with retry queue |
| Deferred retry | 🟡 | 30 retries × 500ms; logs deferred-retry-limit-reached and exits 0 |
| Async probe | 🟡 | ManagerConfig::async_probe = true is set, but thread::spawn(...).join() is used immediately, making it actually synchronous |
max_concurrent_probes |
🟡 | Stored as policy metadata; never enforced (all probes are serial) |
| Hotplug | 🟡 | run_hotplug_loop() polls every 2s; Bus::subscribe_hotplug() is hidden behind an unused hotplug feature flag |
scheme:driver-manager |
✅ | Read-only /devices, /bound, /events, /devices/<pci-addr>, plus param persistence to /tmp/redbear-driver-params/<addr>/{driver,enabled} |
| Spawn via PCID_CLIENT_CHANNEL | 🟡 Corrected at v3.0 | <addr>/bind never existed in pcid; P0-1 collapses claim into the channel open (ENOLCK exclusivity) and passes PCID_CLIENT_CHANNEL=<fd> — the pcid-spawner model |
| Driver parameters | 🟡 | Driver::params() declared but driver-manager's own schema (enabled, priority) is parameter-only; runtime writes via scheme:driver-params (separate daemon local/recipes/system/driver-params) |
| Quirks integration | 🔴 | redox-driver-sys is not in the dep list; pci_has_quirk / pci_get_quirk_flags are NOT consulted |
| Service wiring | 🔴 | No 00_driver-manager.service exists; absent from all [packages] sections |
| Tests | 🔴 | Zero driver-manager tests; 12 unit tests in redox-driver-core |
| Documentation | 🟡 | No dedicated doc; mentioned in UPSTREAM-SYNC-PROCEDURE.md § "driver-manager: Deferred" and PACKAGE-BUILD-QUIRKS.md § "driver-manager Removed During Sync" |
1.3 Service inventory that depends on pcid-spawner
Service definitions:
| Service | Mode | Flag | Where |
|---|---|---|---|
00_pcid-spawner.service |
oneshot_async |
(none) | local/sources/base/init.d/ |
40_pcid-spawner-initfs.service |
oneshot (blocking) |
--initfs |
local/sources/base/init.initfs.d/ |
/etc/init.d/00_pcid-spawner.service (override) |
oneshot_async |
(none) | config/redbear-mini.toml lines 575-583 |
00_base.target requires_weak |
— | — | local/sources/base/init.d/00_base.target |
Service dependencies (every service that references 00_pcid-spawner.service):
| Config | Service referencing it | Reference role |
|---|---|---|
config/redbear-mini.toml |
10_evdevd.service |
requires_weak |
config/redbear-mini.toml |
20_udev-shim.service |
requires_weak |
config/redbear-mini.toml |
11 other services | requires_weak |
config/redbear-full.toml |
10_evdevd.service |
requires_weak |
config/redbear-full.toml |
redox-drm consumers |
requires_weak |
config/redbear-greeter-services.toml |
redbear-greeter (seatd chain) |
requires_weak |
config/redbear-device-services.toml |
explicit comment that driver-manager is deferred | — |
config/acid.toml |
acid test config | requires_weak |
config/redoxer.toml |
redoxer CI config | requires_weak |
Migrating this dependency graph without breaking any boot path requires
keeping 00_pcid-spawner.service available as a fallback through P0–P2 and
running both managers transiently during P3.
1.4 Configuration surfaces
Active now (legacy format):
config/redbear-mini.toml:500-512—/etc/pcid.d/*.tomloverrides (ihdgd.toml,virtio-gpud.toml,00_text_mode_gpu_mask.toml)local/config/pcid.d/intel_gpu.toml— Intel GPU matchlocal/config/pcid.d/amd_gpu.toml— AMD GPU match
Already drafted (driver-manager format, currently inert):
config/redbear-device-services.toml:116-490— full set of/lib/drivers.d/*.toml:00-storage.toml(ahcid, ided, nvmed, virtio-blkd, usb-bot)10-network.toml(e1000d, rtl8168d, rtl8139d, ixgbed, virtio-netd)20-usb.toml(xhcid, ehcid, ohcid, uhcid, usbhubd)30-graphics.toml(vesad, virtio-gpud, redox-drm)40-input.toml(ps2d, usbhidd, intel-thc-hidd)50-audio.toml(ihdad, ac97d, sb16d)70-usb-class.toml(usbscsid, usbaudiod, redbear-ftdi)
Decision-needed: are these drafts correctly normalised for driver-manager's
TOML schema ([driver] with match tables, priority, depends_on)? D1
verifies them. Many entries will need editing to converge on a single matching
language.
1.5 Fork / source provenance
local/sources/base/is the local fork of thebaseRedox upstream submodule. It holds BOTHdrivers/pcid/(the scheme:pci owner) ANDdrivers/pcid-spawner/(the legacy spawner).local/recipes/system/driver-manager/is a local recipe (Cat 1 in-house crate), not a submodule. It does NOT duplicate upstream code; it's a fresh Red Bear creation. Nosubmodule/driver-managerbranch exists, no.gitmodulesentry, no need to create one (perlocal/AGENTS.md§ BRANCH AND SUBMODULE POLICY: "We don't create new branches/repos. We work on existing submodules first. … A new submodule is only justified when the component is large enough that submodule pinning provides real value over a tracked tree or local recipe. driver-manager satisfies none of these").- The 17 driver daemons under
local/sources/base/drivers/{storage,net,graphics,audio,usb}/each havepcid = { path = "../../pcid" }in their Cargo.toml. They depend onpcid_interface(a lib crate alongside pcid), NOT on the pcid-spawner binary. The runtime contract they honour isPCID_CLIENT_CHANNEL=<fd>, which driver-manager preserves. No driver-binary changes are required.
2. Cross-reference: pcid-spawner vs driver-manager vs Linux 7.1 vs CachyOS
2.1 Linux 7.1 PCI driver model surface (local/reference/linux-7.1/ at ab9de95c9)
| Subsystem | Linux source | Capability exposed |
|---|---|---|
| PCI bus type | drivers/pci/pci-driver.c:1726-1742 (pci_bus_type), include/linux/pci.h:1021-1154 (pci_driver + pci_device_id macros) |
C1: id_table + match |
| Probe / remove | drivers/pci/pci-driver.c:473-553 (pci_device_probe / pci_device_remove / pci_device_shutdown) |
C2: device lifecycle |
| Module loading | kernel/kmod.c:64-179 (__request_module → call_modprobe) + drivers/pci/pci-driver.c:1587-1617 (pci_uevent with MODALIAS) |
C1′: driver lookup with alias |
| Driver-core binding | drivers/base/bus.c (bus_add_driver / bus_probe_device / device_attach / driver_attach) |
C2′: symmetric device/driver iteration |
| Sysfs surface | drivers/pci/pci-sysfs.c (vendor, device, class, subsystem_*, resource, config, bind, unbind, new_id, remove_id, driver_override) |
C1 + C2 + C11 + observability |
| PCIe native hotplug | drivers/pci/hotplug/pciehp_core.c:185-372 (pciehp_probe + presence/attention/MRL/DLLSC handlers) |
C14: hotplug events |
| AER | drivers/pci/pcie/aer.c:1203-1280 (aer_recover_work_func + pcie_do_recovery) + include/linux/pcieport_if.h (pci_error_handlers) |
C13: error recovery |
| Runtime PM | drivers/pci/pci-driver.c:1314-1440 (pci_pm_runtime_* + pci_dev_pm_ops) |
C9 + C18: suspend/resume |
| IOMMU / DMA | drivers/pci/pci-driver.c:1668-1711 (pci_dma_configure) + drivers/iommu/iommu.c (iommu_device_use_default_domain) |
C15: DMA / IOMMU |
| MSI / MSI-X | drivers/pci/msi/api.c:205-264 (pci_alloc_irq_vectors_affinity) + include/linux/pci.h:1166-1168 (pci_irq_vector) |
C7 + C8: vector allocation |
| BAR / MMIO | drivers/pci/pci.c:2120-238, 3916-4172 (pci_enable_device / pci_request_regions / pci_set_master / pci_ioremap_bar) |
C3 + C4 + C5 + C6: device + bar map |
| Power states | include/linux/pci.h:183-188 (PCI_D0..PCI_D3cold) + pci.h pci_set_power_state |
C9: power state control |
| Capabilities search | drivers/pci/pci.c (pci_find_capability / pci_find_ext_capability) |
C16: capability iteration |
Synthesis (cross-reference pci_driver ↔ Driver trait ↔ driver-manager):
Linux pci_driver member |
Linux function | Redox current (redox-driver-core) |
Redox pcid-spawner path | Gap to fill |
|---|---|---|---|---|
.probe |
pci_device_probe |
Driver::probe(&DeviceInfo) -> ProbeResult |
(deferred to child spawn) | async + retry + dependency-aware |
.remove |
pci_device_remove |
Driver::remove(&DeviceInfo) -> Result<(), DriverError> |
(no remove path at all) | full lifecycle |
.suspend / .resume |
pci_pm_runtime_* |
Driver::suspend() / resume() (default Ok) |
(no path) | graceful shutdown + wake |
.id_table |
pci_match_device |
Driver::match_table() -> &[DriverMatch] |
legacy match_function() |
unify on DriverMatch |
.shutdown |
pci_device_shutdown |
(not in trait) | (no path) | add to trait or handle via systemd |
.err_handler |
pcie_do_recovery |
(not in trait) | (no path) | add Driver::on_error() |
.driver_managed_dma |
iommu_device_use_default_domain |
(not in trait) | (no path) | add Driver::dma_managed() |
2.2 CachyOS pattern surface (CachyOS-Settings master)
| Pattern | CachyOS location | Red Bear equivalent |
|---|---|---|
| udev rule w/ subsystem match + sysfs write | usr/lib/udev/rules.d/60-ioschedulers.rules (HDD/SSD/NVMe scheduler assignment) |
driver-manager scheme event set_io_scheduler(dev, "bfq"|"mq-deadline"|"kyber") |
udev rule w/ bind/unbind lifecycle |
usr/lib/udev/rules.d/71-nvidia.rules (NVIDIA PM lifecycle) |
driver-manager register_add_event + register_remove_event matching on vendor + class |
| Cross-subsystem reaction (battery state → audio params) | usr/lib/udev/rules.d/20-audio-pm.rules (snd_hda_intel powersave) |
driver-manager event filter {AC=>BATT} → action set_param(driver, "power_save", value) |
| Sentinel-based idempotency | /run/udev/snd-hda-intel-powersave |
driver-manager in-memory init_done: BTreeSet<(driver_name, param_set)> |
| modprobe.d blacklist | usr/lib/modprobe.d/blacklist.conf (blacklist iTCO_wdt) |
/etc/driver-manager.d/00-blacklist.conf: [[blacklist]] module = "iTCO_wdt" |
| modprobe.d options | usr/lib/modprobe.d/amdgpu.conf (options amdgpu si_support=1) |
/etc/driver-manager.d/50-amdgpu.conf: [amdgpu.params] si_support = true |
| modules-load.d | usr/lib/modules-load.d/ntsync.conf (ntsync) |
/etc/driver-manager.d/autoload.d/ntsync.conf: module = "ntsync" |
| Driver mutual exclusion (two drivers claim same PCI IDs) | usr/lib/modprobe.d/amdgpu.conf (amdgpu SI/CIK ↔ radeon SI/CIK off) |
driver-manager priority + explicit exclusive_with field |
| Path-triggered (config change) service activation | usr/lib/udev/rules.d/85-iw-regulatory.rules + cachyos-iw-set-regdomain.{path,service} |
driver-manager + scheme watcher over /etc/driver-manager.d/ |
| Ramdisk driver manifest (mkinitcpio hook ordering) | linux-cachyos preset (microcode/modconf/kms/block/filesystems/fsck) |
/usr/lib/driver-manager.d/initfs.manifest (ramdisk_drivers = ["ahci", "nvme", "redoxfs"], ordered) |
| Cold-boot parameter injection (immutable module params) | modprobe.d/options ... |
driver-manager set_pre_spawn_param(driver, param, value) recorded in BOOT_TIMELINE |
| Curated-policy-as-a-package (cachyos-settings) | cachyos-settings ARCH package |
redbear-driver-policy local recipe (or redbear-meta extension) |
2.3 Capability table — what a full driver-manager must provide (C1–C18)
Note: the "Priority" column uses P0/P1/P2 as capability-priority labels — not phase references. The P0/P1/P2 capability priorities pre-date the § 5 D/C phase restructure. Capability priorities are:
- P0 (capability priority) = blocker for desktop path
- P1 (capability priority) = desktop-ready
- P2 (capability priority) = production-grade
These priorities map onto specific phases in § 5.1 D-Phase (P0 set lands in D1; P1 set lands in D2/D3; P2 set lands in D2/D4).
| # | Capability | Linux equivalent | driver-manager API | Priority | Notes |
|---|---|---|---|---|---|
| C1 | ID-table match + dynamic ID add | pci_match_device + sysfs new_id / remove_id |
DeviceManager.register_driver() + match_table() + add_dynid() |
P0 | Already present in redox-driver-core. add_dynid not yet exposed in driver-manager; D1 must wire it |
| C2 | Device lifecycle (probe/remove) | pci_device_probe / pci_device_remove |
Driver::probe(&DeviceInfo) + Driver::remove(&DeviceInfo) |
P0 | Already present. driver-manager calls Driver::probe only after claim + dependency check |
| C3 | Enable / disable device | pci_enable_device / pci_disable_device |
enable_device(pci_addr) → pcid EnableDevice request |
P0 | driver-manager must call pcid EnableDevice BEFORE spawning the child |
| C4 | BAR request/release | pci_request_regions / pci_release_regions |
channel open (exclusive ENOLCK) + BAR map via pcid_interface::map_bar |
P0 | Resolved at v3.0: <addr>/bind never existed; claim == channel (P0-1) |
| C5 | BAR map / unmap | pci_ioremap_bar / pci_iounmap |
child scheme:memory/physical@<wc|wb|uc> via redox_driver_sys::pci::PciDevice::map_bar |
P0 | Not a manager task — child uses redox-driver-sys directly |
| C6 | Bus master on/off | pci_set_master / pci_clear_master |
set_bus_master(pci_addr, true) |
P0 | driver-manager sets on probe, clears on remove |
| C7 | Allocate IRQ vectors | pci_alloc_irq_vectors |
pci_alloc_vectors(pci_addr, min, max, flags) -> vector_count |
P0 | driver-manager proposes; child daemon allocates via PcidClient |
| C8 | Resolve vector → IRQ handle | pci_irq_vector |
pci_vector_handle(pci_addr, index) -> scheme:irq fd |
P0 | child uses scheme:irq directly |
| C9 | Runtime PM | pci_pm_runtime_* |
set_power_state(pci_addr, D0|D3hot|D3cold) |
P1 | pcid SetPowerState request exists; driver-manager exposes it (lands in D2) |
| C10 | Save/restore config space | pci_save_state / pci_restore_state |
child uses PcidClient::read_config() |
P1 | not a manager concern |
| C11 | Driver override | sysfs driver_override |
set_driver_override(pci_addr, driver_name) |
P1 | driver-manager exposes it as a scheme op (writes pci_addr/override) — D2 |
| C12 | Config space read/write | pci_read_config_* |
child uses PcidClient::read_config() / write_config() |
P0 | not a manager concern |
| C13 | AER error recovery | pci_error_handlers + pcie_do_recovery |
Driver::on_error(error_kind) -> RecoveryAction |
P2 | Lands in D2 (AER recovery is part of the modern technology surface) |
| C14 | Hotplug events (PDC / DLLSC / MRL) | pciehp_* |
manager.subscribe_hotplug() -> HotplugEvent |
P1 | Requires event-driven scheme:pci surface; D3 delivers event-driven hotplug |
| C15 | DMA mask / IOMMU | dma_set_mask + iommu_device_use_default_domain |
set_dma_mask(pci_addr, bits) + iommu_assign_group(pci_addr) |
P2 | iommu daemon present (local/recipes/system/iommu/); wiring lands in D2 (modern tech surface) |
| C16 | Find capabilities | pci_find_capability / pci_find_ext_capability |
child uses redox_driver_sys::pci::PciDevice::find_capability |
P0 | not a manager concern |
| C17 | INTx on/off | pci_intx |
child via PcidClient |
P1 | not a manager concern |
| C18 | System PM (suspend/resume) | pci_pm_* |
Driver::suspend() / resume() callbacks via manager |
P2 | driver-manager keeps a registry of bound drivers for the systemd-supervisor to call; D2 |
Source-of-truth invariant: C7 (vector allocation) is implemented inside pcid
itself, not in driver-manager. driver-manager's role is to (a) call enable_device
through pcid before spawning, (b) hand the channel FD to the child, and (c) call
remove (which clears bus master + sends ULP/cleanup requests) when a device
disappears from hotplug. The same split holds for C5, C12, C16, C17: those are
the child's contract via redox-driver-sys.
2.4 boot-timeline and observability alignment
Note: the "Priority" column reuses P0/P1/P2 as capability-priority labels (the
same priority ladder used in § 2.3) — not phase references. Capacity
deliveries for these priorities land in the § 5 D-Phase track (P0 set in
D1; P1 set in D2/D3; P2 set in D4). AER-aware reset handling (P1 here)
is part of D2's modern-technology surface.
| Linux surface | driver-manager equivalent | Status | Priority |
|---|---|---|---|
dmesg pci ... BAR X set to ... |
BOOT_TIMELINE_PATH = /tmp/redbear-boot-timeline.json (already present) |
✅ exists | (D0) |
journalctl -k |
scheme:driver-manager:/events (read-only ring buffer, 256 lines) |
✅ exists | (D0) |
lspci -v |
scheme:driver-manager:/devices/<addr> (vendor / device / class / driver / enabled=true) |
🟡 partial | P0 |
lspci -vv -s ... + lspci -xxx -s ... |
not exposed | not yet | P0 |
find /sys/bus/pci/devices -name reset |
not exposed | not yet | P1 — needed for AER + reboot (D2) |
pci=nomsi kernel cmdline |
REDBEAR_DRIVER_MANAGER_FLAGS=disable_msi env |
not yet | P1 |
pci=realloc= |
not exposed | not yet | P2 |
lspci -tv (tree) |
scheme:driver-manager:/tree (planned) |
not yet | P2 |
3. Target architecture
3.1 Process model (post-migration)
┌─────────────────────────────────────────────┐
│ DRIVER-MANAGER (PID 1 child) │
│ ▸ Subscribes to /scheme/pci as a client │
│ ▸ Owns Driver trait objects from TOML │
│ ▸ Maintains Bus → Device → Driver graph │
│ ▸ Claims via channel open (ENOLCK) │
│ ▸ Spawns driver daemons with: │
│ PCID_CLIENT_CHANNEL=<fd> │
│ PCID_SEGMENT / PCID_BUS / PCID_DEVICE │
│ PCID_FUNCTION (Red Bear P4 contract) │
│ ▸ Registers scheme:driver-manager │
│ ▸ Writes /tmp/redbear-driver-params/... │
│ ▸ Writes /tmp/redbear-boot-timeline.json │
│ ▸ On hotplug (P1): detects add/remove │
│ ▸ On AER (P5): invokes error handler │
└───────────────────┬─────────────────────────┘
│
┌───────────────────────────┼──────────────────────────┐
▼ ▼ ▼
┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐
│ PCID │ │ SCHEME:PCI │ │ REDOX-DRIVER-SYS │
│ (Canonical PCI │ │ (per-device │ │ (registered │
│ scheme owner) │◀──│ channel+bind+ │ │ quirk surface) │
│ │ │ config+feature) │ │ /scheme:driver-sys│
└─────────┬──────────┘ └─────────┬──────────┘ └────────────────────┘
│ │
│ /scheme/memory │
│ /scheme/irq │
│ /scheme:acpi │
│ /scheme/iommu (P5) │
▼ ▼
─────────── KERNEL ───────────
3.2 New component list
| Component | Lives at | Source-of-truth | Phase |
|---|---|---|---|
driver-manager daemon |
local/recipes/system/driver-manager/source/ |
local recipe (Cat 1) | built in D0–D5, enabled at C0 |
driver-manager-config package |
local/recipes/system/driver-manager-config/ (NEW) |
local recipe (Meta) | D4 (policy layer) |
/etc/driver-manager.d/*.toml configs |
NEW dir, shipped by driver-manager-config |
policy | D4 |
/lib/drivers.d/*.toml |
NEW dir, per-config files | match tables | D1 (format); D4 (content) |
00_driver-manager.service |
local/sources/base/init.d/ |
service | C0 (created dormant); C3 (active) |
40_driver-manager-initfs.service |
local/sources/base/init.initfs.d/ |
service | C0 (dormant); C2 (active blocking) |
local/recipes/system/driver-params |
already exists; extends to consume scheme:driver-manager rather than its own bound-text-parsing |
observer | C1 (switch over) |
redbear-driver-policy package |
local/recipes/system/redbear-driver-policy/ (NEW) |
curated package | D4 (created); C3 (packaged) |
3.3 What changes for pcid-spawner (NOT deleted, demoted)
The v1.1 D/C phase restructure changes pcid-spawner's role as follows:
| Track / Phase | pcid-spawner state |
|---|---|
| D-phase (D0–D5) | No change. pcid-spawner remains the live boot path. driver-manager is being built locally but is NOT shipped, NOT packaged, NOT enabled. The boot path is identical to baseline. |
| C0 | 00_pcid-spawner.service gains ConditionPathExists=!/etc/driver-manager.d/disabled. Service is dormant by default. |
| C1 | driver-manager runs in --observe mode (writes BOOT_TIMELINE, does NOT spawn). pcid-spawner is still the actual spawner. |
| C2 | 40_driver-manager-initfs.service becomes oneshot blocking in initfs; pcid-spawner initfs service becomes fallback only via ConditionPathExists. |
| C3 | 00_driver-manager.service becomes the primary rootfs spawner. 00_pcid-spawner.service is retained as fallback only (idem for initfs variant). |
| C4 | pcid-spawner source tree receives a final commit on the submodule/base branch documenting the maintenance-mode status. Never deleted. |
3.4 Wire-level contracts that MUST stay invariant
| Contract | Description | Invariant |
|---|---|---|
| C-ENV-1 | PCID_CLIENT_CHANNEL=<fd> is passed to every spawned driver daemon |
Preserved by config.rs::probe() already |
| C-ENV-2 | PCID_SEGMENT/PCID_BUS/PCID_DEVICE/PCID_FUNCTION env vars (Red Bear P4) |
Preserved — driver-manager's probe path must add them |
| C-ENV-3 | Program resolution: absolute OR /usr/lib/drivers/<name> |
Preserved — same prefix logic moves to driver-manager |
| C-CHAN-1 | Per-device channel open in scheme:pci yields mutual-exclusion (ENOLCK to second opener) |
Not the responsibility of driver-manager — that's pcid. driver-manager must use bind (NOT channel) so multi-driver retry can re-probe without consuming the lock. Confirm with pcid that bind is also exclusive, or document the asymmetry. |
| C-CHAN-2 | Drivers call PciFunctionHandle::connect_default() and get a working bincode channel via the FD |
Continues unchanged |
| C-SVC-1 | Every service that previously requires_weak=00_pcid-spawner.service gains requires_weak=00_driver-manager.service as an alternative. Driver-manager has requires_weak=00_pcid-spawner.service for fallback coordination. |
Provides fallback-safety |
| C-DAEMON-1 | daemon::Daemon::spawn() blocks parent on child ready |
driver-manager must use the same primitive to preserve the boot-order guarantee — ahcid/nvmed up before redoxfs mounts |
| C-RECIPE-1 | recipes/core/base/drivers/pcid-spawner/recipe.toml keeps path = "../../../local/sources/base/drivers/pcid-spawner" |
Never changes |
| C-SCHEME-1 | scheme:driver-manager exposes: /devices, /devices/<addr>, /bound, /events (read-only) |
Already present |
| C-PARAM-1 | driver-params reads from /scheme/driver-manager/bound (instead of its current text-parsing of the bound file) |
C1 wiring |
3.5 Module / crate dependencies (target state)
recipes/system/driver-manager/source/Cargo.toml ─── D0 plus phase-by-phase additions
├── redox-driver-core (already: local/recipes/drivers/redox-driver-core)
├── redox-driver-pci (already: local/recipes/drivers/redox-driver-pci)
├── redox-driver-sys (NEW dep ─ added at D4; previously optional, now required)
├── pcid_interface (already: local/sources/base/drivers/pcid)
├── redox-scheme (already: local/sources/redox-scheme)
├── redox_syscall (already: local/sources/syscall, features=["std"])
├── log, toml, serde (already)
├── event-stream (NEW in D3 — for hotplug event consumption)
├── bitflags (NEW in D2 — for CapabilityFlag aggregation)
└── [patch.crates-io] (already: redox_syscall, libredox, redox-scheme)
pcid-spawner Cargo.toml is untouched (it stays in the base submodule fork for
fallback). No retroactive changes to daemon::Daemon::spawn,
PciFunctionHandle::connect_default, or any of the 17 driver daemons.
4. Critical risk register
| # | Risk | Severity | Mitigation |
|---|---|---|---|
| R1 | Double-spawn race: driver-manager and pcid-spawner both spawn the same driver daemon | 🔴 BLOCKING | C1 sequencing: driver-manager runs only in --observe mode first. The actual spawn is gated by spawn_enabled flag set by the C1 exit gate. 00_driver-manager.service is dormant in C0; observable in C1; primary in C3. |
| R2 | Driver daemon crashes after spawn, no one restarts it | 🔴 BLOCKING | driver-manager persists bound_devices map to /tmp/redbear-driver-params/. On WAIT_FOR_REAPPEAR per-driver (delivered in D2) the manager retries after a 5s backoff. systemd Restart= on the driver service files provides the surface-process fallback (D4). |
| R3 | The match table migration misses a vendor/device pair | 🔴 BLOCKING | D1 adds "no-match ↔ spin in driver-manager, no spawn" tests for the existing 17 drivers. C1 runs both managers side-by-side and emits unbound_device_addr=<addr> lines to BOOT_TIMELINE for any device the new manager misses but pcid-spawner bound. |
| R4 | The bind vs channel claim differs in exclusivity |
🟡 HIGH | D1 step is a QEMU test that opens bind from one driver and channel from another; verifies the latter gets ENOLCK. Document the asymmetry. |
| R5 | Initfs switchroot breaks (storage drivers must be ready before redoxfs mounts) | 🔴 BLOCKING | C2 keeps 40_pcid-spawner-initfs.service available (via ConditionPathExists). 40_driver-manager-initfs.service is added in observation mode at C0 and only switches on at C2 once the storage path passes on at least 3 hardware configs (QEMU virtio + AMD NVMe + Intel SATA). |
| R6 | redox-driver-sys quirks not consulted during spawn decisions |
🟡 MEDIUM | D4 phase. Until then, the kernel-side MSI/MSI-X fallback in pcid remains authoritative. No regression on bare metal. |
| R7 | Atomic patch governance failure: live source edit goes unmirrored | 🟡 (procedural) | Every source edit during this plan is committed to the submodule/base branch (for pcid-spawner edits) or as commits within local/recipes/system/driver-manager/source/ (which is a local-recipe tree, not a fetched source tree). Per local/AGENTS.md DURABILITY POLICY. |
| R8 | The new scheme:driver-manager collides with scheme:driver-params |
🟡 MEDIUM | C-PARAM-1 above resolves this: driver-params becomes a thin observer over scheme:driver-manager:/bound, no separate parsing path. Wired at C1. |
| R9 | The migration leaves the repo in a state that cannot fall back | 🔴 BLOCKING | During D-phase, no boot path is affected (driver-manager is not in the system). During C-phase, both managers may run; pcid-spawner is the fallback at every cutover step (ConditionPathExists=!/etc/driver-manager.d/disabled gates it). At no phase is the boot path single-point-of-failure. |
| R10 | The TOML match-table migration produces a silent NO-OP for an edge device (e.g., ID 0x1234:0x5678 not in any entry → driver never binds) | 🟡 HIGH | D1 implements missing_match = warn so any unresolved match is logged. QEMU tests with the 17 known drivers verify each entry before C3. |
| R11 | The redox-driver-core DeviceManager::probe_device locks the entire manager mutex for the duration of a probe, blocking retry_deferred for slow drivers |
🟡 MEDIUM | D2 introduces per-driver Mutex<DriverState> for finer-grained concurrency; manager's outer mutex held only during dispatch. |
| R12 | Device hot-add during switchroot causes race with pcid (which may re-enumerate) | 🟡 MEDIUM | Hotplug is off during initfs → rootfs transition. driver-manager starts in --hotplug=off until switchroot completes. |
| R13 | D-phase work produces a stub or workaround instead of comprehensive code | 🔴 BLOCKING | local/scripts/driver-manager-audit-no-stubs.py (D4 deliverable) is the formal gatekeeper. Static analysis + QEMU integration test. Any D-phase work that violates § 0.5 fails the D4 audit and blocks D5 ratification. No partial-credit D5 exit. |
| R14 | driver-manager enters a boot path before D5 ratification (i.e., someone packages it earlier than planned) | 🔴 BLOCKING | Build-system guard: local/scripts/build-redbear.sh and the cookbook CI refuse any commit that adds driver-manager to [packages] in config/redbear-*.toml during D-phase. Documented in § 0.6. |
Out-of-scope (deferred to OTHER plans)
- USB / ACPI / platform buses →
local/docs/USB-IMPLEMENTATION-PLAN.md,local/docs/ACPI-IMPROVEMENT-PLAN.md - GPU render / DRM completion →
local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md - Wi-Fi / Bluetooth →
local/docs/WIFI-IMPLEMENTATION-PLAN.md,BLUETOOTH-IMPLEMENTATION-PLAN.md - IRQ / MSI-X quality →
local/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md - IOMMU runtime proof → still QEMU-only per HardWARE matrix
- Replacement of
pcid(the scheme:pci owner) — far future, no decision today
5. Phased migration plan
The migration has two tracks operating sequentially, not interleaved:
- D-Phase (Parallel Development). driver-manager is being built; pcid-spawner remains the live boot system. No boot path is affected. This phase starts at D0 (current state) and continues through D5 (feature-complete gate). The team's parallel development work lives here.
- C-Phase (Cutover & Validation). driver-manager is fully built; the boot path is switched over from pcid-spawner to driver-manager in stages. C0 starts only after D5 + operator ratification.
Authority: the § 0.6 Parallel Development constraint is binding. driver-manager does NOT enter any boot path until D5 ratifies. The original v1.0 P0–P6 dual-mode strategy is folded into the C-phases.
5.1 D-Phase — Parallel Development
State during D-Phase:
- pcid-spawner is the boot path in every
redbear-*.toml. - driver-manager is built locally (committed to
local/recipes/system/driver-manager/source/) but NOT shipped. - driver-manager has no
00_driver-manager.service. - No files in
/etc/driver-manager.d/are read by the live system. - No
[packages]entry containsdriver-manager. - The team's work is recorded in commits to the local recipe source.
Phase D0 — Foundation (current state)
Status (2026-07-20): partially complete.
What exists:
redox-driver-coreframework:Bustrait,Drivertrait,DeviceManager,DriverMatch,DriverParams,ProbeEvent,ProbeResult. Compiled with 12 unit tests.redox-driver-pci::PciBusreads/scheme/pci/, parses PCI config space, returnsDeviceInfowith vendor/device/class/subclass/prog_if/revision/ subsystem IDs.local/recipes/system/driver-manager/source/src/{main.rs,config.rs,scheme.rs,hotplug.rs,exec.rs}— 1250 lines compiling to a 783KB ELF.scheme:driver-managerregistered with/devices,/bound,/events,/devices/<addr>paths (read-only).- Boot timeline JSON at
/tmp/redbear-boot-timeline.json.
What does NOT yet exist (D1–D5 work):
- Linux 7.x capability bridge: C9, C11, C13, C14, C15, C17, C18 are absent
- Modern technology surface integration (SMP-aware concurrent probe, S/P-state hooks, runtime PM, AER)
- Event-driven hotplug (current code polls every 2s)
- Quirk integration with
redox-driver-sys(noredox-driver-sysimport insource/Cargo.toml) - Format-coexistence TOML loader (current loader only accepts new
[[driver]]format)
Exit criteria for D0:
cargo build --release -p driver-managerclean.cargo test -p redox-driver-core12/12 green.local/recipes/system/driver-manager/target/x86_64-unknown-redox/stage/usr/bin/driver-managerexists.- No
00_driver-manager.servicein any init.d. - No
driver-manager = {}in any[packages]section.
Phase D1 — Linux 7.x capability bridge (target: 6–10 weeks)
Preconditions: D0 exit criteria met.
Goal: Implement every P0 capability from § 2.3 (C1, C2, C3, C4, C5, C6, C7, C8, C12, C16).
Deliverables:
redox-driver-core::driver::Drivertrait gains fullsuspend()/resume()/on_error()/params()implementations with concrete behaviour (not stubs — see § 0.5).redox-driver-core::bus::Bustrait gains event-drivensubscribe_hotplug()implementation inPciBus.local/recipes/system/driver-manager/src/manager-probe.rs(new module) handles the SpawnDecision committee (C2', C3, C4, C6).- Format-coexistence TOML loader accepting both
[drivers](legacy pcid-spawner) and[[driver]](driver-manager) formats from the same directory tree. - New test script
local/scripts/test-driver-manager-parity.sh(used in C1, but drafted here).
Exit criteria for D1:
- Every P0 capability of C1–C18 is implemented and unit-tested.
local/scripts/test-driver-manager-parity.shreturns identical bound sets under both formats.- Integration test (QEMU + all 17 drivers) shows driver-manager can shadow pcid-spawner exactly.
Phase D2 — Modern technology surface (target: 8–12 weeks)
Preconditions: D1 exit criteria met.
Goal: Integrate SMP-aware concurrent probe, C/P-state coordination, runtime PM hooks, IOMMU groups, MSI-X vector allocation, AER recovery (see § 0.4 for the full tech surface).
Deliverables:
redox-driver-core::manager::DeviceManager::probe_devicebecomes truly concurrent (std::thread::scopeorrayon,max_concurrent_probesenforced).- MSI-X vector allocation with per-vector fds exposed via
redox-driver-sys::irq::MsixTable. - Per-driver P-state advisory publisher (interacts with
cpufreqd). - C-state coordination hook with
cpufreqd/thermald. - IOMMU group registration via
local/recipes/system/iommu/. - D-state transitions (D0 ↔ D3hot ↔ D0) through pcid's
SetPowerStaterequest.
Exit criteria for D2:
- Concurrent probe dispatch verified (
max_concurrent_probes = 4shows 4-way parallelism in QEMU timings). - Per-driver P-state advisory verified on real AMD/Intel hardware.
- IOMMU-group-aware binding demonstrated on at least one QEMU + vfio-passthrough run.
- Runtime PM suspend/resume callback chain verified.
Phase D3 — Hotplug event-driven (target: 4–6 weeks)
Preconditions: D2 exit criteria met.
Goal: Replace polling-based hotplug with event-driven subscription.
Deliverables:
Bus::subscribe_hotplug()returnsHotplugSubscriptionmapping to a real event fd (viaBus::poll_hotplug_events()or equivalent).PciBusconsumes/scheme/pci/events(requires pcid scheme event delivery — feature request to pcid maintainer).Manager::hotplug_loop()becomes event-driven, removing the 2-second poll entirely.
Exit criteria for D3:
- Hotplug event arrival latency < 200 ms on QEMU synthetic add/remove.
- Real-hardware hotplug (PCIe Native or USB) verified on at least one AMD and one Intel bare-metal target.
Phase D4 — Quirk integration + comprehensive audit (target: 6–8 weeks)
Preconditions: D3 exit criteria met.
Goal: Wire redox-driver-sys::PciQuirkFlags into bind/remove decisions;
complete feature audit confirming § 0.5 compliance.
Deliverables:
local/recipes/system/driver-manager/source/Cargo.tomladdsredox-driver-sysas path dep.manager-probe.rsconsultsPciQuirkFlags::NO_MSIX,NO_PME,NEED_FIRMWARE,DISABLE_ACCEL, etc., during spawn decisions.- Audit script
local/scripts/driver-manager-audit-no-stubs.pyruns against the source tree and confirms: every public trait method has a concrete implementation, nounimplemented!(), notodo!(), no#[allow(dead_code)]for production code paths, noOk(())defaults in trait methods that have a real responsibility (per § 0.5 matrix). - Quirk policy delivered:
/etc/driver-manager.d/{00-blacklist.conf, 50-amdgpu.toml,autoload.d/ntsync.conf}patterns work as designed.
Exit criteria for D4:
driver-manager-audit-no-stubs.pyreports zero violations.- Quirk-driven spawn decisions match expected behaviour (e.g.,
DISABLE_ACCELcausesredox-drm --no-renderto be invoked). - Hardware-validation-matrix AMD Threadripper + Intel Alder Lake both pass.
Phase D5 — Feature-complete gate (audit before cutover)
Preconditions: D4 exit criteria met, operator ratification.
Goal: Formal declaration that driver-manager is feature-complete and ready to cut over.
Deliverables:
- Audit document
local/docs/evidence/driver-manager/D5-AUDIT.mdlisting every C1–C18 capability with status (✅ Production / 🟡 Build-grade / 🔴 Not Implemented). - Updated
local/docs/HARDWARE-VALIDATION-MATRIX.mdwith driver-manager rows. - This document's
Last reviewedline updated to the D5 completion date. - Operator's written ratification (chat log, email, or commit message).
Exit criteria for D5:
- Every entry on the C1–C18 table is at least 🟡 Build-grade.
- The § 0.5 no-stub audit is clean (audit script returns 0).
- Hardware validation matrix passes on at least one AMD and one Intel profile with at least 3 driver categories each (storage, network, GPU).
- The § 0.2 success criteria 1–6 are met (criteria 7–10 are C-phase criteria).
- Operator has ratified in writing.
At this point, the D-phase work is OVER. The C-phase begins.
5.2 C-Phase — Cutover & Validation
The C-phase begins only after D5 ratifies. The cutover is sequential,
gated, and operator-ratified at every phase boundary. The cutover does
NOT require pcid-spawner to be removed — it stays as a ConditionPathExists
fallback.
Phase C0 — Service wiring (dormant service file)
Preconditions: D5 complete.
Goal: Add the dormant service file and [packages] entry. Driver-manager
remains inactive.
Deliverables:
local/sources/base/init.d/00_driver-manager.service(NEW, dormant by default — never auto-starts).local/sources/base/init.initfs.d/40_driver-manager-initfs.service(NEW, dormant by default).local/sources/base/init.d/00_pcid-spawner.servicegainsConditionPathExists=!/etc/driver-manager.d/disabled(auto-skip when driver-manager is operational; remains the actual spawner in C0).config/redbear-{mini,full,grub,device-services,greeter-services}.tomladddriver-manager = {}to[packages].[packages]removal ofpcid-spawnerdoes NOT happen in C0.
Acceptance test:
[ BOOT ] init: 00_pcid-spawner.service running
[ BOOT ] init: 00_driver-manager.service skipped (dormant)
[ PASS ] C0: pcid-spawner still spawns; driver-manager binary present but inactive
Exit criteria for C0:
- ISO boots identically to pre-C0 baseline.
make lint-configclean.scripts/validate-collision-log.shclean.driver-manager-audit-no-stubs.pystill clean.
Phase C1 — Dual-mode observation
Preconditions: C0 exit criteria met.
Goal: driver-manager runs in --observe mode (no spawn). Both managers
see the same devices. Parity confirmed before any actual spawn.
Deliverables:
00_driver-manager.serviceruns in--observemode (writes BOOT_TIMELINE, no spawn).00_pcid-spawner.serviceis still the actual spawner.- Both managers write parity data to their BOOT_TIMELINE files.
local/scripts/test-driver-manager-parity.shruns as a CI gate.
Acceptance test:
[ BOOT ] driver-manager: enumerate begin (observe)
[ BOOT ] driver-manager: enumeration complete: 17 bound (observe), 0 deferred
[ BOOT ] pcid-spawner: spawn e1000d for 0000:00:03.0
[ BOOT ] driver-manager: 17/17 binds agree with pcid-spawner
[ BOOT ] driver-manager: BOOT_TIMELINE written
[ PASS ] C1: parity clean
Exit criteria for C1:
test-driver-manager-parity.shclean in 5 back-to-back QEMU runs.- Real-hardware parity verified on AMD Threadripper + Intel Alder Lake.
- Both managers' BOOT_TIMELINE files match.
Phase C2 — Initfs switchover
Preconditions: C1 exit criteria met, operator ratification.
Goal: driver-manager becomes the initfs spawner (storage drivers).
Deliverables:
40_driver-manager-initfs.servicebecomesoneshot(blocking) and replaces40_pcid-spawner-initfs.servicein initfs.- pcid-spawner initfs service moves to
ConditionPathExists=!/etc/driver-manager.d/initfs-activefallback. local/scripts/test-driver-manager-initfs.shclean.
Acceptance test:
[ INITFS ] driver-manager-initfs: storage drivers ready (ahcid/nvmed/virtio-blkd/ided)
[ INITFS ] redoxfs: mount /dev/redoxfs /scheme/root ✓
[ INITFS ] switchroot: /usr loaded
[ PASS ] C2: initfs path via driver-manager
Exit criteria for C2:
- Initfs-only QEMU run completes with driver-manager and the switchroot happens within 5 s.
- AHCI + NVMe + virtio-blkd paths tested on at least one AMD and one Intel system.
- pcid-spawner-initfs fallback can be activated by deleting
/etc/driver-manager.d/initfs-active, and the boot still completes.
Phase C3 — Rootfs switchover
Preconditions: C2 exit criteria met, operator ratification.
Goal: driver-manager becomes the primary rootfs spawner.
Deliverables:
00_driver-manager.servicebecomes the primary rootfs spawner.00_pcid-spawner.serviceis retained as fallback only.local/scripts/test-driver-manager-active.shruns end-to-end.driver-paramsswitches to consumingscheme:driver-manager:/bound.
Acceptance test:
[ BOOT ] driver-manager: registered scheme:driver-manager
[ BOOT ] driver-manager: load_all: 7 config files, 17 drivers registered
[ BOOT ] driver-manager: enumeration complete: 17 bound, 0 deferred
[ BOOT ] driver-manager: hotplug: starting event loop (event-driven)
[ PASS ] C3: driver-manager is the active rootfs spawner
Exit criteria for C3:
test-driver-manager-active.shclean in 5 back-to-back QEMU runs.- driver-params via scheme:driver-manager produces correct driver list.
- parity test (C1's) keeps reporting identical binds.
Phase C4 — Operator-ratified production cutover
Preconditions: C3 exit criteria met, operator ratification, two-week soak, three hardware reboots with identical driver-bound sets.
Goal: pcid-spawner moves to maintenance-mode fallback. Status rows update.
Deliverables:
- pcid-spawner source tree receives a final commit on
submodule/basedocumenting the maintenance-mode status. local/docs/UPSTREAM-SYNC-PROCEDURE.md§ "driver-manager: Deferred" replaced with "driver-manager: production".- README.md status row updates.
local/AGENTS.md§ PLANNING NOTES pointer updates.- Inline deferred comments in
config/redbear-mini.toml:31andconfig/redbear-device-services.toml:9-13removed.
Exit criteria for C4:
- Two-week soak completes with no
ConditionPathExistsguard firing. - Three consecutive bare-metal reboots produce identical driver-bound sets.
- Hardware validation matrix shows parity or improvement on every row.
- Operator ratification documented in
local/docs/evidence/driver-manager/C4-RATIFICATION.md.
At no point is pcid-spawner DELETED. It moves to "always-shipped,
on-disk fallback" alongside 00_driver-manager.service.
6. Per-phase dependency diagram
The D-phase and C-phase tracks are sequential under § 0.6's parallel development constraint:
D-PHASE: Parallel Development C-PHASE: Cutover & Validation
(driver-manager being built; (driver-manager enabled;
pcid-spawner is the live system) pcid-spawner demoted to fallback)
D0 ─► D1 ─► D2 ─► D3 ─► D4 ─► D5 ─── operator ratification + feature-complete gate ─── C0 ─► C1 ─► C2 ─► C3 ─► C4
│ │ │ │ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
fnd cap tech hp quk audit svc obs ifs rfs prod
frame bridge face ev int (no stubs) wir dual sw sw cut
present +PM ent- gra- + HW validate e mode itc itc over
+AER drive ted dom dor ove ove (op
+IOM (dor r r era
MU man (do (do tor
+MS t) t) t) ra
I-X tif
ied
)
│ │
└──── During D-phase, pcid-spawner remains the boot path. During C-phase, both
No boot path is changed. managers may run; pcid-spawner
is the fallback at every cutover step.
Phase key:
- D0: Foundation (current state)
- D1: Linux 7.x capability bridge (C1–C18 P0 set)
- D2: Modern technology surface (SMP, S/P states, runtime PM, IOMMU, AER, MSI-X)
- D3: Hotplug (event-driven)
- D4: Quirk integration + comprehensive audit (no stubs)
- D5: Feature-complete gate (operator ratification)
- C0: Service wiring (dormant service file)
- C1: Dual-mode observation
- C2: Initfs switchover
- C3: Rootfs switchover
- C4: Operator-ratified production cutover
The workstream structure deliberately mirrors local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md
(Phase 1–5) and local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md (Workstream A–E)
to keep the project's planning grammar consistent.
7. Test scripts (local/scripts/)
All scripts land in local/scripts/ per project convention and follow the
existing naming (test-X.sh for shell, test-X-y.sh for variants).
Each script returns exit 0 on success, emits a [PASS] line, and writes a
log to local/docs/evidence/driver-manager/.
| Script | Phase (D / C) | Purpose | Exit code signal |
|---|---|---|---|
test-driver-manager-parity.sh |
C1 (drafted in D1) | Both managers active, observation only; verify they agree on every device-id-to-driver mapping | 0 = same set bound |
test-driver-manager-active.sh |
C3 | driver-manager primary; pcid-spawner fallback; verify all 17 drivers | 0 = all expected drivers present |
test-driver-manager-initfs.sh |
C2 | initfs use only; verify storage drivers come up before redoxfs | 0 = redoxfs mounted |
test-driver-manager-hotplug.sh |
D3 (gate) | Hotplug add/remove cycle; unbind/rebind sequence | 0 = unbind + rebind within budget |
test-driver-manager-pm.sh |
D2 (gate) | Suspend/resume cycle, parameter write-through | 0 = clean suspend+resume |
test-driver-manager-cutover.sh |
C4 | Final acceptance: identical driver set before/after cutover | 0 = no diff in bound-set logs |
driver-manager-audit-no-stubs.py |
D4 (gate) | Static analysis that the source contains no unimplemented!(), no todo!(), no Ok(()) trait-method defaults — per § 0.5 |
0 = no violations |
test-driver-manager-no-stubs-qemu.sh |
D4 (gate) | QEMU run with synthesized edge-case devices exercising every P0 capability C1–C18 | 0 = every test bus emits one bind event |
8. Cross-cutting concerns
8.1 GPU / DRM
The redox-drm driver daemon currently relies on PciFunctionHandle::connect_default()
plus redox-driver-sys::pci::PciDevice. driver-manager must continue passing
PCID_CLIENT_CHANNEL for redox-drm to receive a working channel. The match
table that triggers redox-drm (30-graphics.toml already in
config/redbear-device-services.toml) is consumed by driver-manager; no driver
side changes. The redox-driver-sys::PciQuirkFlags policy is consulted in D4
(NEED_FIRMWARE, DISABLE_ACCEL, NO_MSIX). AMD-specific decisions live in
local/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md — the present plan does not
re-open those decisions, it only carries them into the manager's match-time
decision logic.
8.2 USB
xhcid, ehcid, ohcid, uhcid, usbhubd, usbscsid, usbhidd, usbaudiod
all spawn from /lib/drivers.d/{20-usb,70-usb-class}.toml. These TOMLs exist
in config/redbear-device-services.toml (lines 116-490) but are inert today.
driver-manager must wake them correctly during C3. The Bus trait will gain a
UsbBus in local/docs/USB-IMPLEMENTATION-PLAN.md; that is out of scope
for this migration.
8.3 Audio
ihdad, ac97d, sb16d are in 50-audio.toml. These spawn late (no initfs
dependency); driver-manager handles them as ordinary bound drivers with
priority below storage/network. CachyOS's audio-power management patterns
(20-audio-pm.rules) are reflected in D4's depends_on and hotplug
hooks for these drivers.
8.4 Storage init path
This is the highest-risk surface. ahcid, ided, nvmed, virtio-blkd,
usbscsid, usb-bot MUST be ready before redoxfs mounts. Phase C2's initfs
migration has its own gate, and a fallback (40_pcid-spawner-initfs.service
via ConditionPathExists) is required at every C-phase before C4.
8.5 Cross-config consistency
The three compile targets (redbear-mini, redbear-full, redbear-grub) all
include init services that depend on 00_pcid-spawner.service. All three need
the same 00_driver-manager.service substitution. The change is mechanical
but must be applied to every [service].requires_weak reference and every
[[files]] install in the same PR. make lint-config enforces consistency.
8.6 Doc references
After C3:
local/AGENTS.md§ PLANNING NOTES gets a one-liner pointing here.local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.mdPhase 1 row updates from "🟡 | in progress" to "🟡 | driver-manager parity accepted".local/docs/PATCH-GOVERNANCE.md§ "Local recipe priority vs upstream WIP" notes thatlocal/recipes/system/driver-manageris the canonical owner.local/docs/UPSTREAM-SYNC-PROCEDURE.md§ "driver-manager: Deferred" replaced with "driver-manager: live (00_driver-manager.service)".
After C4 (production cutover):
- README.md status row updates to ✅.
config/redbear-mini.toml:31andconfig/redbear-device-services.toml:9-13inline deferred comments are removed (no longer accurate once driver-manager is live).local/docs/UPSTREAM-SYNC-PROCEDURE.md§ "driver-manager: Deferred" replaced with "driver-manager: production".
8.7 Branch / submodule policy
Per local/AGENTS.md § BRANCH AND SUBMODULE POLICY, no new submodule is created.
driver-manager stays a local recipe under local/recipes/system/driver-manager/.
This is preserved at every phase. The 9 declared submodules remain unchanged.
9. Appendices
9.1 File map (post-migration, target end state)
Created (tracked):
local/recipes/system/driver-manager/source/src/
├── main.rs (modified at D3, D4, C1, C3 across their phases)
├── config.rs (modified at D1 [format coexistence], D2 [PM hooks], D3 [event-driven], D4 [quirk integration])
├── scheme.rs (modified at D2 [PM scheme surface], D4 [quirk reporting])
├── hotplug.rs (rewritten at D3 — event-driven)
├── exec.rs (removed at D0; spawn was already inlined in config.rs::probe())
├── driver-probe.rs (NEW at D1 [SpawnDecision committee], evolves D2 [PM dispatch], D4 [AER routing])
└── errors.rs (NEW at D0 [error-tag normalisation], evolves D2 [PM error codes])
local/recipes/system/driver-manager/recipe.toml (no change)
local/recipes/system/driver-manager/source/Cargo.toml (deps added at D2 [bitflags], D3 [event-stream], D4 [redox-driver-sys])
local/sources/base/init.d/00_driver-manager.service (NEW dormant at C0; evolves C1, C3)
local/sources/base/init.initfs.d/40_driver-manager-initfs.service (NEW dormant at C0; becomes blocking at C2)
local/config/driver-manager.d/ (NEW at D4)
├── 00-blacklist.conf
├── autoload.d/ntsync.conf
├── 50-amdgpu.toml
└── initfs.manifest
local/scripts/test-driver-manager-{parity,active,initfs,hotplug,pm,cutover}.sh (one per phase — see § 7)
local/scripts/driver-manager-audit-no-stubs.py (NEW at D4; gates D5)
local/recipes/system/driver-params/source/src/main.rs (modified at C1 to consume scheme:driver-manager:/bound)
local/recipes/system/redbear-driver-policy/ (NEW at D4; curated policy package; packaged at C3)
Modified (kept tracked):
config/redbear-mini.toml (C0: driver-manager added to [packages]; superseded comments at C4)
config/redbear-full.toml (C0: same)
config/redbear-grub.toml (C0: same)
config/redbear-greeter-services.toml (C0: same)
config/redbear-device-services.toml (C0 + D4: driver-manager config inclusion)
local/docs/UPSTREAM-SYNC-PROCEDURE.md (§ driver-manager: Deferred -> live at C3; -> production at C4)
local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md (Phase 1 row update at C3)
local/docs/HARDWARE-VALIDATION-MATRIX.md (multiple rows updated at D5)
README.md (status row at C4)
AGENTS.md (no change beyond pointer)
local/AGENTS.md (PLANNING NOTES section gains pointer)
Untouched (preserved for fallback throughout D/C phases and after C4):
local/sources/base/drivers/pcid-spawner/ (entire tree — fallback for life)
local/sources/base/drivers/pcid/ (unchanged — still the scheme:pci owner)
local/sources/base/init.d/00_pcid-spawner.service (becomes dormant fallback service at C3)
local/sources/base/init.initfs.d/40_pcid-spawner-initfs.service (becomes dormant fallback at C2)
local/patches/base/ (all P-numbered patches preserved)
local/recipes/core/pcid-spawner/recipe.toml (path-fork unchanged)
recipes/core/base/drivers/pcid-spawner/ (path-fork path unchanged)
The 17 driver daemons' source trees and Cargo.tomls.
9.2 Environment variable contract (preserved)
| Var | Source | Consumer | Format |
|---|---|---|---|
PCID_CLIENT_CHANNEL |
pcid-spawner (legacy), driver-manager (new) | driver daemon via PciFunctionHandle::connect_default() |
ASCII decimal fd |
PCID_SEGMENT |
both | driver daemon | 4-hex (e.g., 0000) |
PCID_BUS |
both | driver daemon | 2-hex (e.g., 03) |
PCID_DEVICE |
both | driver daemon | 2-hex (e.g., 0a) |
PCID_FUNCTION |
both | driver daemon | decimal |
REDBEAR_DRIVER_MANAGER_ACTIVE |
env (env file) | driver-manager | 0 = observation; non-zero = spawn |
DRIVER_PARAMS_LOG |
existing | driver-params | one of trace/debug/info/warn/error |
DRIVER_PARAMS_BOUND_PATH |
existing | driver-params | path string |
INIT_NOTIFY |
init | driver-params, driver-manager (from C0 onwards) | int fd |
9.3 Capability-to-driver matrix
| Driver daemon | C3 enable | C4 bar request | C5 map | C6 bus master | C7 vectors | C12 config R/W | C16 caps | C17 INTx |
|---|---|---|---|---|---|---|---|---|
ahcid |
✅ via PCID_CLIENT_CHANNEL | implicit via connect | driver-side via redox-driver-sys |
yes | MSI via pcid client | yes | yes | yes |
ided |
same | same | same | yes | n/a | yes | yes | yes |
nvmed |
same | same | same | yes | MSI-X | yes | yes | yes |
virtio-blkd |
same | same | same | yes | n/a | yes | yes | yes |
e1000d |
same | same | same | yes | MSI via redox-driver-sys |
yes | yes | yes |
rtl8168d |
same | same | same | yes | MSI | yes | yes | yes |
rtl8139d |
same | same | same | yes | INTx | yes | yes | yes |
ixgbed |
same | same | same | yes | MSI-X | yes | yes | yes |
virtio-netd |
same | same | same | yes | n/a | yes | yes | yes |
ihdgd |
same | same | same | yes | MSI-X | yes | yes | yes |
virtio-gpud |
same | same | same | yes | n/a | yes | yes | yes |
xhcid |
same | same | same | yes | MSI-X | yes | yes | yes |
ihdad |
same | same | same | yes | MSI | yes | yes | yes |
ac97d |
same | same | same | yes | INTx | yes | yes | yes |
vboxd |
same | same | same | yes | n/a | yes | yes | yes |
amd-mp2-i2cd |
same | same | same | yes | MSI | yes | yes | yes |
intel-thc-hidd |
same | same | same | yes | MSI | yes | yes | yes |
Crucial invariant: all 17 drivers already fulfil C3–C8, C12, C16, C17
through pcid_interface::PciFunctionHandle::connect_default() followed by
PciClient::* calls. driver-manager's role is to deliver a working channel
FD into the child's env. No driver rewrite required at any phase.
9.4 TOML schema (canonical end-state at C3)
Match table (/lib/drivers.d/*.toml):
# matches are AND-ed within an entry, OR-ed across entries
[[driver]]
name = "e1000d"
description = "Intel e1000 Gigabit Ethernet"
priority = 50
[driver.command]
argv = ["e1000d"] # or absolute path
[driver.depends_on]
schemes = ["pci", "firmware"]
[[driver.match]]
vendor = 0x8086
device = 0x100E
class = 0x02 # network controller
subclass = 0x00 # ethernet
[[driver.match]]
vendor = 0x8086
device = 0x10D3
# wildcard rest
[driver.params]
debug = { type = "bool", default = false, writable = true }
priority_arg = { type = "int", default = 0, writable = true }
exclusive_with = { type = "string", default = "", writable = false }
Policy layer (/etc/driver-manager.d/*.toml):
# Blacklist
[[blacklist]]
module = "iTCO_wdt"
reason = "CachyOS precedent; conflicts with iTCO hardware"
# Forced module-options
[amdgpu.params]
si_support = true
cik_support = true
radeon_overlap = "exclusive" # CachyOS mutual exclusion pattern
# Autoload (cold-boot pre-driver)
[[autoload]]
module = "ntsync"
Initfs manifest (/etc/driver-manager.d/initfs.manifest):
# ordered (CachyOS-style hook ordering)
[ramdisk]
microcode = ["amd-ucode", "intel-ucode"]
policy = "00-driver-conf"
kms_drivers = ["amdgpu", "i915"]
block_drivers = ["ahci", "nvme", "virtio_blk"]
fs_drivers = ["redoxfs", "ext4"]
9.5 Migration effort & ownership
| Phase | Effort estimate | Owner role |
|---|---|---|
| D-Phase | Effort estimate | Owner role |
| --- | --- | --- |
| D1 — Linux capability bridge | 6–10 weeks | driver-runtime engineer |
| D2 — Modern technology surface | 8–12 weeks (largest phase; SMP + S/P + PM + IOMMU + AER + MSI-X + NUMA) | driver-runtime + kernel-interface engineer + ACPI engineer |
| D3 — Hotplug event-driven | 4–6 weeks | driver-runtime engineer |
| D4 — Quirk integration + audit | 6–8 weeks (includes the § 0.5 no-stubs audit) | driver-runtime + SRE |
| D5 — Feature-complete gate | 1–2 weeks | driver-runtime engineer + operator ratification |
| D-phase total | 25–38 weeks (~6–9 months) |
| C-Phase | Effort estimate | Owner role |
|---|---|---|
| C0 — Service wiring (dormant) | 1 week | driver-runtime engineer |
| C1 — Dual-mode observation | 2–4 weeks (must run alongside pcid-spawner in production) | driver-runtime engineer + build-system engineer |
| C2 — Initfs switchover | 2–3 weeks | driver-runtime + boot-system engineer |
| C3 — Rootfs switchover | 2–4 weeks | driver-runtime engineer |
| C4 — Operator-ratified production cutover | 1–2 weeks + 2-week soak + 3 hardware reboots | operator + driver-runtime engineer |
| C-phase total | 8–14 weeks + soak (~2–4 months) |
Grand total: 33–52 weeks (~8–13 months) for full D+C trajectory.
Operator ratification required at the following phase boundaries:
- D5 → C0 — feature-complete gate (the § 0.5 audit clean + hardware matrix passes)
- C1 → C2 — rootfs observation confirmed dual-mode parity
- C2 → C3 — initfs switchover ratified
- C3 → C4 — rootfs switchover ratified
- C4 transition — production cutover ratified
Phase boundaries without explicit operator ratification are not allowed to advance.
9.6 References
Internal (Red Bear OS):
local/AGENTS.md— policy authoritylocal/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md— supersedes for desktop pathlocal/docs/DRM-MODERNIZATION-EXECUTION-PLAN.md— GPU-specific decisionslocal/docs/IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md— PCI/IRQ qualitylocal/docs/USB-IMPLEMENTATION-PLAN.md— USB bus (out of scope here)local/docs/WIFI-IMPLEMENTATION-PLAN.md— Wi-Fi bus (out of scope here)local/docs/QUIRKS-SYSTEM.md—redox-driver-sysquirks surfacelocal/docs/QUIRKS-IMPROVEMENT-PLAN.md— Task 2.1 driver-manager bridgelocal/docs/HARDWARE-VALIDATION-MATRIX.md— status of driver runslocal/docs/UPSTREAM-SYNC-PROCEDURE.md— historical status note to updatelocal/docs/PATCH-GOVERNANCE.md— patch carrier ruleslocal/docs/RELEASE-BUMP-WORKFLOW.md— version synclocal/scripts/TOOLS.md— tool inventory
External cross-reference (not authoritative here, cited for design intent):
- Linux 7.1 reference tree at
local/reference/linux-7.1/(commitab9de95c9):drivers/pci/pci-driver.c:1021-1742— pci_driver, pci_bus_typedrivers/pci/pci.c:2120, 3916, 4172—pci_enable_device,pci_request_regions,pci_set_masterdrivers/pci/msi/api.c:205-264—pci_alloc_irq_vectors_affinitydrivers/pci/hotplug/pciehp_core.c:185-372— native hotplugdrivers/pci/pcie/aer.c:1203-1280— AER recoverydrivers/base/bus.c:732, 818, 612, 869— driver-core binding
- CachyOS-Settings (
master, v1.3.5): https://github.com/CachyOS/CachyOS-Settingsusr/lib/udev/rules.d/60-ioschedulers.rules— kernel-event → sysfs-write policyusr/lib/udev/rules.d/71-nvidia.rules— bind/unbind lifecycle symmetryusr/lib/udev/rules.d/20-audio-pm.rules— cross-subsystem reactionusr/lib/udev/rules.d/85-iw-regulatory.rules+cachyos-iw-set-regdomain.{path,service}— udev→systemd delegationusr/lib/modprobe.d/{amdgpu,blacklist,nvidia}.conf— driver-exclusion pattern
- CachyOS/linux-cachyos: scheduler variants,
mkinitcpiopreset ordering.
10. Position statement
This plan supersedes the inline deferred notes that accompany the existing
driver-manager recipe:
config/redbear-mini.toml:31("driver-manager requires driver config migration and is not yet ready")config/redbear-device-services.toml:9-13("driver-manager intentionally not included … its service launched pcid-spawner instead of driver-manager").
These notes remain accurate at the moment of writing (2026-07-20). They will
be amended at the relevant phase boundaries as the migration advances. At
no point in the migration is pcid-spawner removed from the tree, the
recipe, or the live image. The boot path is never single-point-of-failure.
v1.1 additions (this revision): § 0 Strategic Context is added with rationale, success criteria, upstream risk, modern-technology surface, comprehensive-implementation principle, and the parallel-development constraint. § 5 is restructured into D-Phase (parallel development) and C-Phase (cutover & validation). All cross-references in §§ 1, 2, 4, 5, 6, 7, 8, 9 are updated to the new phase nomenclature. The § 0.5 comprehensive principle is the binding code-quality bar; the § 0.6 parallel-development constraint is the binding development model.
Binding constraints derived from § 0:
- Comprehensive implementation — every public trait method on
Bus,Driver, andManageris fully implemented. Nounimplemented!(), notodo!(), no empty match arms returning trivial defaults, no#[cfg(...)]-gated no-ops. Honoringlocal/AGENTS.md§ STUB AND WORKAROUND POLICY — ZERO TOLERANCE. - Modern technology surface — SMP-aware concurrent probe, C/P-state coordination, runtime PM, IOMMU group management, AER error recovery, MSI-X vector allocation, NUMA-aware DMA. None of these are stubs.
- Parallel development model — driver-manager is being built in parallel with the live pcid-spawner system. It is NOT shipped, NOT enabled, NOT included in any boot path until the D5 feature-complete gate ratifies. No exceptions.
- Upstream-first response — if Redox upstream ships an equivalent manager within 6 months of D5, evaluate and consider migrating per Local Fork Supremacy Policy. The migration plan is robust to upstream divergence; it does NOT block on upstream decisions.
Status of § 0.6 (parallel development) at the moment of writing: driver-manager is being built. It is NOT shipped. The boot path is pcid-spawner. The migration plan enters C-phase only after D5 ratifies.
The migration's authority chain is:
this document → local/AGENTS.md → AGENTS.md. Every implementation
step must be defensible against this chain, the § 0.5 comprehensive
principle, and the § 0.6 parallel-development constraint.
Last reviewed: 2026-07-20 (v1.1: strategic-context + D/C phase restructure)
Next review: at D5 exit gate, after local/scripts/driver-manager-audit-no-stubs.py
reports zero violations AND local/docs/HARDWARE-VALIDATION-MATRIX.md
shows driver-manager validated on at least one AMD and one Intel profile
across storage, network, and GPU categories.