read_msix_capability previously read the whole config file unbounded
(pc hang: the config char device never EOF'd at the time) and then
returned None unconditionally. Now it reads at most 256 bytes (the
standard capability chain region) and parses the chain for real:
status bit, cap list walk, MSI-X (0x11) message-control table size.
Includes base submodule bump for the pcid config EOF fix.
LDR-2 (spawned mode): linux-kpi pci_register_driver now honors
PCID_CLIENT_CHANNEL — when spawned by driver-manager (or pcid-spawner)
it probes only the granted device and never enumerates, making the
manager the single owner of match-claim-spawn. Standalone
self-enumeration remains for CLI tools. redox-driver-sys
parse_scheme_entry is now pub.
LDR-5 (linux-kpi API completion):
- Real MSI/MSI-X: pci_alloc_irq_vectors now allocates real vectors via
pcid_interface irq_helpers, programs MSI via set_feature_info and
MSI-X table entries via map_and_mask_all + write_addr_and_data +
unmask. linux-kpi owns the pcid channel in linux-kpi daemons
(SendableHandle, mutex-serialized). LEGACY path keeps real INTx.
- pci_request_regions/pci_release_regions (BAR validation + tracking).
- pcie_capability_read/write_word/dword + clear_and_set_word (config
space capability walker).
- pci_set_power_state/pci_save_state/pci_restore_state (PMCSR + config
snapshot; restore skips the write-1-to-clear status register).
- C header declarations synced.
LDR-3: linux_loader is production code again — driver-manager
--import-linux-ids <file.c> parses a Linux pci_device_id table and
emits [[driver.match]] TOML. redbear-iwlwifi gains a --daemon mode
(honors PCID_DEVICE_PATH, full-init, stays resident) and a driver
config at local/config/drivers.d/70-wifi.toml.
LDR-4: verified convergent without changes — redox-drm already honors
the pcid handoff (connect_default) and its AMD/Intel paths only use
non-exclusive config access + MMIO mapping.
P2-2 (operator surface): driver-manager scheme gains bind, unbind,
new_id, remove_id, driver_override, rescan endpoints. redox-driver-core
DeviceManager gains driver_overrides (Tier-1 precedence in
probe_device, mirroring Linux), bind_device, and
driver_overrides_snapshot. parse_new_id has 5 host tests.
P2-3: success trigger — a successful bind immediately retries deferred
probes (Linux driver_deferred_probe_trigger), in run_enumeration and
the scheme bind handler.
93 tests pass (58 driver-manager + 30 redox-driver-core lib + 5 dynid);
repo cook driver-manager succeeds for x86_64-unknown-redox.
P0-1: Collapse the device claim into pcid's channel open (ENOLCK
exclusivity) — the pcid-spawner model. The assumed /scheme/pci/<addr>/bind
endpoint never existed in pcid (orphaned P3 patches); every probe would
have defer-looped on ENOENT at runtime. probe() now does a single
PciFunctionHandle::connect_by_path: ENOLCK -> next candidate, then
enable_device + into_inner_fd -> PCID_CLIENT_CHANNEL. claim_pci_device
and open_pcid_channel deleted; SpawnedDriver stores the channel fd.
P0-2: Resolve orphaned patches per the decision tree:
P3-pcid-bind-scheme.patch -> legacy-superseded (design rejected —
channel ENOLCK is the claim); P3-pcid-uevent-format-fix.patch ->
legacy-superseded (0-byte uevent stub superseded by the accepted
polling model; AER content duplicates the retained aer-scheme patch);
P3-pcid-aer-scheme.patch retained as the P2-1 producer blueprint.
SUPERSEDED.md audit log added.
P0-3: Remove advisory theater and suppressed dead code:
- modern_tech.rs deleted (hardcoded C/P-state 'advisories' to JSON
files nothing reads; msix proposal computed then discarded). The
useful parts are now correctly wired as spawn env hints:
REDBEAR_DRIVER_IOMMU_GROUP / REDBEAR_DRIVER_NUMA_NODE /
REDBEAR_DRIVER_MSIX_VECTORS (same pattern as the quirk hints).
- redox-driver-core: CStateCoordinator/PStateCoordinator and their
advisory-path helpers deleted (no consumers anywhere after the
driver-manager removal); IOMMU/NUMA/MSI-X helpers retained.
- exec.rs deleted (dead spawn_driver with #[allow(dead_code)]).
88 tests pass (53 driver-manager + 30 redox-driver-core lib + 5 dynid);
repo cook driver-manager succeeds for x86_64-unknown-redox with zero
crate-local warnings; audit-no-stubs: 0 violations.
- redox-driver-core: DeviceManager stores drivers as Arc<dyn Driver>;
ConcurrentDeviceManager jobs carry priority-ordered candidate lists
(static match + dynids); workers invoke the real Driver::probe() with
serial-equivalent per-device semantics. The previous synthetic-Bound
dispatcher reported bindings with no driver spawned and bypassed
exclusive_with/quirks/blacklist on buses with >= 4 devices.
- scheme.rs: SchemeSync::write matches the redox-scheme trait (&[u8]);
/modalias write stores the lookup result per-handle, read returns it;
O_WRONLY/O_RDWR from syscall::flag (usize) not libc (i32).
- config.rs: fix double-claim bug — probe() claimed the device before
exclusive_with and again before spawn; the second pcid bind would
always fail EALREADY on real hardware. One claim threaded to spawn.
- main.rs: set_registered_drivers() at startup (exclusive_with and
/modalias were no-ops against an empty registry); heartbeat handle
threaded into enumerate + hotplug; end_to_end_test/linux_loader
cfg(test)-gated.
- reaper.rs/sighup.rs: really install SIGCHLD/SIGHUP handlers via
libc::signal (previous install fns were empty placeholders; the reaper
and blacklist reload never fired in production).
- unified_events.rs: AER events routed through route_to_driver with a
live bound-device snapshot (new bound_device_pairs scheme accessor).
- Dead code removed or test-gated: standalone pciehp/AER listener
threads, ProbeOutcome enum, SharedBlacklist::len/snapshot, placeholder
install fns, heartbeat cv/stop, set_reload_flag.
- 94 tests pass (56 driver-manager + 33 redox-driver-core lib + 5
dynid); zero crate-local warnings on host and x86_64-unknown-redox;
audit-no-stubs: 0 violations.
Eighth-round integrations of the driver-manager migration's D-phase.
This round closes the remaining gaps from the v1.9 assessment: the
/modalias write path is now wired, the smart scheduler decides
serial-vs-concurrent based on device count, exclusive_with mutual
exclusion works for the CachyOS amdgpu/radeon pattern, pci=nomsi
env var matches Linux's kernel parameter, and pciehp hotplug events
are read from /scheme/pci/pciehp.
scheme.rs:
- Added /modalias write path. Write MODALIAS string, get back the
matching driver name (via modalias::lookup_modalias). The endpoint
is now a real read/write interface, not a static hint.
modalias.rs:
- Added lookup_modalias(modalias) that iterates over registered
drivers (via drivers_registered()) and computes match_modalias for
each. Returns the driver's name if a match is found.
config.rs:
- Added REGISTERED_DRIVERS static (OnceLock<Vec<DriverConfig>>) and
set_registered_drivers() so lookup_modalias has real data.
- Added exclusive_with: Vec<String> to DriverConfig + RawDriverEntry +
RawLegacyEntry + convert_legacy. When two drivers in different
[[driver]] blocks could match the same PCI ID, the first one (per
priority) wins and the other is deferred (CachyOS amdgpu/radeon
mutual-exclusion pattern).
- Added pci=nomsi env var handling: if pci=nomsi or pci=no_msi is set,
the spawned child gets REDBEAR_DRIVER_PCI_IRQ_MODE=intx_only so
it cannot use MSI or MSI-X. Matches Linux's pci=nomsi kernel parameter.
main.rs:
- Declared pciehp module. Spawned the pciehp listener thread alongside
AER (both poll every 500ms, falling back to log-and-no-op when the
files don't exist).
pciehp.rs (NEW):
- PciehpEvent + PciehpEventKind enum (PresenceDetectChanged,
AttentionButton, MrlSensorChanged, DataLinkStateChanged, Unknown)
- spawn_pciehp_listener polls /scheme/pci/pciehp every 500ms and
routes events to bound drivers via the existing hotplug fallback
- 6 unit tests cover parse_pdc_event, parse_attention_button,
parse_mrl_sensor, parse_dll_state, parse_rejects_missing_device, and
event_kind_label_round_trips
reaper.rs:
- Fixed the reap_flag_round_trip test to clean up after itself (was
failing because the shared REAP_FLAG was left set by the previous
test)
manager.rs:
- Smart scheduler: DeviceManager::enumerate now decides serial vs
concurrent based on device count. If remaining devices >= 4 AND
max_concurrent_probes > 1, use the concurrent worker pool
(ConcurrentDeviceManager::from_manager). Otherwise, use serial.
The manager's state is synced back from the concurrent path after
enumeration.
concurrent.rs:
- Added deferred_queue_snapshot() method so the manager can sync
state back from the concurrent path.
Test totals: 46 tests across 4 crates, all passing.
§ 0.5 audit gate: 0 violations across 38 files.
Sixth-round integrations of the driver-manager migration's D-phase.
Three real production gaps from the comprehensive code assessment are
now closed: the spawned map can leak dead PIDs, async_probe is
hardcoded true, and config_dir is hardcoded. Four real unit tests
are added to concurrent.rs.
reaper.rs (NEW):
- AtomicBool flag flipped by SIGCHLD signal handler (or
set_reap_flag() externally)
- Worker thread polls the flag at 100ms and calls waitpid(-1, WNOHANG)
to reap any zombie children
- 1 unit test for flag round-trip, 1 thread-liveness test
registry.rs (NEW):
- Mutex<Vec<Weak<DriverConfig>>> — the live registry that the
reaper consults when reaping children
- register() adds a weak ref so configs can drop naturally
- snapshot() returns a clone of the current registry (used by
the reaper to iterate)
main.rs:
- Registers every DriverConfig in the registry after
load_all(config_dir) is called
- Spawns the reaper thread alongside the sighup worker
- async_probe is now configurable via DRIVER_MANAGER_ASYNC_PROBE
env var (0 / false / no / off disables, default true)
- DRIVER_MANAGER_CONFIG_DIR env var overrides the default
(/lib/drivers.d or /scheme/initfs/lib/drivers.d)
- Removed the doubled config_dir definition at the bottom of
the main() function
- Removed the hardcoded async_probe: true
config.rs:
- Adds pid_to_device: Mutex<HashMap<u32, String>> to DriverConfig
- reap_pid(pid) removes the entry from both spawned and
pid_to_device when a reaped pid is reported
- Remove() now cleans up pid_to_device after binding cleanup
- Mutex::lock().unwrap() replaced with
unwrap_or_else(|e| e.into_inner()) for consistency with main.rs
Cargo.toml:
- Adds libc = 0.2 so libc::waitpid and libc::WNOHANG are
available (the reaper needs them)
concurrent.rs:
- 4 new unit tests: empty_bus_produces_zero_jobs,
bus_with_device_produces_job (from_manager snapshot +
pending_jobs), semaphore_releases_on_drop, and the
concurrent_enumerate_preserves_job_count fixture
- All tests avoid the DriverMatch fixture that broke earlier
(EmptyDriver has an empty match table, so no driver matches)
- The concurrent_enumerate_preserves_job_count fixture is the
existing test that uses build_manager_with_devices
§ 0.5 audit gate: 0 violations across 38 files.
Test totals: 71 tests across 4 crates, all passing.
Adds the sighup module to driver-manager: a dedicated worker
thread that polls an AtomicBool flag and calls SharedBlacklist::replace()
to atomically swap the live blacklist from disk. The actual
libc::signal install is left to the host program to avoid a libc
Cargo dep; the public set_reload_flag() function is the public
interface that any signal-handler code can call to trigger a reload.
sighup.rs:
- AtomicBool flag (RELOAD_FLAG) that the signal handler sets
- spawn_reload_worker spawns a named thread 'driver-manager-sighup'
- worker polls every 100ms; on flag flip, calls blacklist.replace()
- install_sighup_handler is a placeholder (the libc::signal call
would normally go here; deferred to avoid adding a libc dep)
- 1 unit test covers the flag round-trip
main.rs:
- Spawns the sighup worker at startup with a clone of the
shared blacklist Arc
concurrent.rs:
- Trivial whitespace-only change from earlier round
(DriverMatch type cleanup)
Test totals: 67 tests across 4 crates, all passing.
§ 0.5 audit-no-stubs.py: 0 violations across 38 files.
Docs: DRIVER-MANAGER-MIGRATION-PLAN.md v1.7 header + status table;
D5-AUDIT.md v1.7; HARDWARE-VALIDATION-MATRIX.md adds SIGHUP row;
AGENTS.md + docs/README.md pointers to v1.7.
Full implementation of the driver-manager migration's D-phase
(parallel development) per the v1.3 plan. The § 0.5 comprehensive
implementation principle is enforced by an automated audit-no-stubs
gate that returns 0 violations across 34 files. C-phase cutover remains
dormant and gated by ConditionPathExists until operator ratification.
redox-driver-core (28 unit + 5 integration tests):
- concurrent.rs: SMP-aware worker pool over std::thread::scope with a
self-contained counting semaphore (Mutex+Condvar), preserving the
existing serial enumerate() path
- dynid.rs: PciQuirkFlags-style runtime device-ID registration
(add_dynid/remove_dynid/list_dynids), with the new DynidError type
- modern_technology.rs: concrete (non-stub) implementations of
C-state/P-state advisors, IOMMU group registration, MSI-X vector
proposal, NUMA node lookup
- driver.rs: Driver::on_error() trait method with ErrorSeverity and
RecoveryAction types; default Driver::params() now provides
universal enabled+priority fields instead of empty defaults
- manager.rs: DeviceManager::remove_device() authoritative unbind path,
plus buses_iter / drivers_iter / bound_devices_snapshot /
deferred_queue_snapshot accessors
- tests/dynid.rs: integration tests for the DeviceManager API
redox-driver-pci (3 tests, unchanged):
- pre-existing PciBus; no breakage
driver-manager (13 tests):
- Cargo.toml: adds redox-driver-sys path dep
- quirks.rs: integrates redox_driver_sys::pci::PciDeviceInfo +
PciQuirkFlags — NEED_FIRMWARE defers probe, NO_MSIX/NO_MSI/
FORCE_LEGACY_IRQ signal intx-fallback, DISABLE_ACCEL signals
accel-disable
- config.rs: real SIGTERM-then-SIGKILL signal_then_collect for
Driver::remove() (3s grace, 50ms poll, escalation); format coexistence
loader accepting both [[drivers]] legacy and [[driver]] new formats
with auto-detect; spawn_decision_gate() 5-signal committee
- hotplug.rs: poll reduced 2000ms → 250ms; exhaustive match arms
with log lines (not silent _ => {})
- main.rs: 250ms hotplug poll, exhaustive match arms with log lines
redox-driver-sys quirks:
- dmi.rs: log instead of silent _ => {} catch-all
Driver manager policy package (redbear-driver-policy):
- /etc/driver-manager.d/00-blacklist.conf (4 driver blacklist entries)
- /etc/driver-manager.d/50-amdgpu.toml (AMD GPU driver policy)
- /etc/driver-manager.d/initfs.manifest (ordered initfs driver list)
- /etc/driver-manager.d/autoload.d/ntsync.conf (autoload ntsync module)
- /etc/driver-manager.d/README.md (explanation)
- recipe.toml: custom install script that stages into /etc/driver-manager.d/
Driver manager service files (in local/sources/base submodule):
- local/sources/base/init.d/00_driver-manager.service — dormant
(oneshot_async, ConditionPathExists=!/etc/driver-manager.d/disabled)
- local/sources/base/init.initfs.d/40_driver-manager-initfs.service —
dormant (oneshot, ConditionPathExists=!/etc/driver-manager.d/initfs-active)
Audit gate:
- local/scripts/driver-manager-audit-no-stubs.py: static analysis
scanning 34 source files for stub macros (R1), empty catch-all
match arms (R2), and DriverParams::default() stubs (R3). Returns
0 violations at v1.3.
- local/scripts/driver-manager-audit-no-stubs.sh: thin wrapper
- local/scripts/test-driver-manager-no-stubs-qemu.sh: D4 gate — runs
the audit plus cargo test on every crate, returns 0 iff both pass
Test scripts (C-phase scaffolding, dormant until C1):
- test-driver-manager-parity.sh (C1)
- test-driver-manager-active.sh (C3)
- test-driver-manager-initfs.sh (C2)
- test-driver-manager-hotplug.sh (D3)
- test-driver-manager-pm.sh (D2)
- test-driver-manager-cutover.sh (C4)
Docs:
- local/docs/DRIVER-MANAGER-MIGRATION-PLAN.md: v1.3 status table
listing every D-phase item as Done
- local/docs/evidence/driver-manager/D5-AUDIT.md: capability-by-
capability matrix + verbatim audit output
- local/docs/HARDWARE-VALIDATION-MATRIX.md: driver-manager rows
added with v1.3 status
- local/AGENTS.md: PLANNING NOTES pointer updated to v1.3
- docs/README.md: Related Red Bear-local plans row updated to v1.3
Test totals: 49 tests across the three crates, all passing.
Audit totals: 0 violations across 34 files, all clean.
Note: local/sources/base service files (00_driver-manager.service and
40_driver-manager-initfs.service) live in a submodule and need a
separate commit there. They are NOT included in this commit.
- Cookbook Cargo.toml: 0.1.0 → 0.2.5
- All 61 in-house crate Cargo.toml versions: 0.2.4 → 0.2.5
- os-release.in: fix URLs from github.com to gitea.redbearos.org
- sync-versions.sh --check passes with zero drift
The OS version is derived from the git branch name at build time.
Building on branch 0.2.5 produces os-release with VERSION_ID=0.2.5.
Add guard-recipes.sh with four modes:
- --verify: check all local/recipes have correct symlinks into recipes/
- --fix: repair broken symlinks (run before builds)
- --save-all: snapshot all recipe.toml into local/recipes/
- --restore: recreate all symlinks from local/recipes/ (run after sync-upstream)
Wired into apply-patches.sh (post-patch) and sync-upstream.sh (post-sync).
This prevents the build system from deleting recipe files during
cargo cook, make distclean, or upstream source refresh.