From 8b872979efbb3717b13a73b6ccf18e92af65d96e Mon Sep 17 00:00:00 2001 From: Vasilito Date: Mon, 4 May 2026 14:04:03 +0100 Subject: [PATCH] fix: udev-shim panic, sessiond duplicate, scheme Bad-fd handling - udev-shim: replace .expect() with graceful errors (no more panic on Broken pipe) - P4-initfs: remove duplicate sessiond (conflicted with config) - accessibility/ime/keymapd: break instead of exit(1) on EBADF - P6 driver patches rebased - Docs: archive old reports, add implementation master plan --- local/docs/ACPI-IMPROVEMENT-PLAN.md | 2 +- local/docs/IMPLEMENTATION-MASTER-PLAN.md | 315 ++++++++++++++++++ .../BOOT-PROCESS-FIX-SUMMARY-2026-05-03.md | 0 .../CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md | 48 ++- .../COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md | 0 .../GRAPHICAL-BOOT-ASSESSMENT-2026-05-03.md | 0 .../HARDWARE-VALIDATION-MATRIX.md | 0 .../base/P4-initfs-dbus-services.patch | 12 +- local/patches/base/P6-driver-main-fixes.patch | 61 ++-- .../patches/base/P6-driver-new-modules.patch | 224 ++----------- .../system/cpufreqd/source/src/main.rs | 158 ++++++++- .../redbear-accessibility/source/src/main.rs | 2 +- .../system/redbear-ime/source/src/main.rs | 2 +- .../system/redbear-keymapd/source/src/main.rs | 2 +- .../recipes/system/thermald/source/src/fan.rs | 33 ++ .../system/udev-shim/source/src/main.rs | 117 ++++--- 16 files changed, 682 insertions(+), 294 deletions(-) create mode 100644 local/docs/IMPLEMENTATION-MASTER-PLAN.md rename local/docs/{ => archived}/BOOT-PROCESS-FIX-SUMMARY-2026-05-03.md (100%) rename local/docs/{ => archived}/CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md (91%) rename local/docs/{ => archived}/COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md (100%) rename local/docs/{ => archived}/GRAPHICAL-BOOT-ASSESSMENT-2026-05-03.md (100%) rename local/docs/{ => archived}/HARDWARE-VALIDATION-MATRIX.md (100%) create mode 100644 local/recipes/system/thermald/source/src/fan.rs diff --git a/local/docs/ACPI-IMPROVEMENT-PLAN.md b/local/docs/ACPI-IMPROVEMENT-PLAN.md index f6016b79..26f3164b 100644 --- a/local/docs/ACPI-IMPROVEMENT-PLAN.md +++ b/local/docs/ACPI-IMPROVEMENT-PLAN.md @@ -199,7 +199,7 @@ These rules govern all work from this plan: | Wave | Theme | Current status | Main blocker | Primary closure signal | |---|---|---|---|---| | Wave 0 | Contracts / truthfulness | partially complete | doc drift across adjacent ACPI-facing docs | one canonical vocabulary and ownership story across the repo | -| Wave 1 | Startup hardening / parser policy | partially complete | boot-path contract gaps (explicit `RSDP_ADDR` producer ownership and still-transitional initfs lifecycle) plus remaining panic-grade startup and fault paths | firmware-origin startup failures are bounded and typed and AML bootstrap preconditions are explicit | +| Wave 1 | Startup hardening / parser policy | substantially complete — panic-grade expect/unwrap/panic removed from acpid main.rs, assert_eq replaced in ec.rs, SDT parsing uses graceful error handling | boot-path contract gaps | firmware-origin startup failures are bounded and typed | | Wave 2 | AML ordering / shutdown / sleep scope | partially complete | shutdown/reboot result semantics and broader runtime proof still remain incomplete | deterministic `\_S5` derivation and bounded shutdown behavior | | Wave 3 | Honest ACPI power surface | open | current power reporting is real but still provisional and under-validated | `/scheme/acpi/power` exposes only behavior that the runtime evidence can honestly support | | Wave 4 | AML physmem / EC / runtime fault handling | partially complete | placeholder-like runtime error behavior remains in places | no correctness-critical fabricated runtime values | diff --git a/local/docs/IMPLEMENTATION-MASTER-PLAN.md b/local/docs/IMPLEMENTATION-MASTER-PLAN.md new file mode 100644 index 00000000..6c9b4171 --- /dev/null +++ b/local/docs/IMPLEMENTATION-MASTER-PLAN.md @@ -0,0 +1,315 @@ +# Red Bear OS — Master Implementation Plan + +**Date**: 2026-05-04 +**Status**: Authoritative — supersedes CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md, COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md, and HARDWARE-VALIDATION-MATRIX.md +**Source of truth**: Linux kernel 7.0 (`local/reference/linux-7.0/`) + +--- + +## 1. Authority & Scope + +### 1.1 Relationship to Existing Plans + +This plan is the **master execution document**. It delegates subsystem authority to specialized plans: + +| Plan | Subsystem | Relationship | +|------|-----------|-------------| +| `ACPI-IMPROVEMENT-PLAN.md` | ACPI sleep, thermal, EC, power | **Authoritative** for ACPI | +| `IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` | PCI IRQ, MSI-X, IOMMU, controllers | **Authoritative** for IRQ/PCI | +| `USB-IMPLEMENTATION-PLAN.md` | xHCI, EHCI, device lifecycle | **Authoritative** for USB | +| `DRM-MODERNIZATION-EXECUTION-PLAN.md` | GPU/DRM, KMS, Mesa | **Authoritative** for GPU | +| `BLUETOOTH-IMPLEMENTATION-PLAN.md` | BT host/controller | **Authoritative** for BT | +| `WIFI-IMPLEMENTATION-PLAN.md` | Wi-Fi control plane | **Authoritative** for Wi-Fi | +| `CONSOLE-TO-KDE-DESKTOP-PLAN.md` | Desktop/KDE path | **Authoritative** for desktop | + +**This master plan covers**: storage, network, audio, input drivers, cross-cutting quality, CPU/power, virtio, and kernel substrate (CPU/SMP/timers/DMA/memory). + +### 1.2 Validation Levels + +- **builds** — compiles without error +- **enumerates** — discovers hardware via scheme interfaces +- **usable** — works in bounded scenario (QEMU or bare metal) +- **validated** — passes explicit acceptance tests with evidence +- **hardware-validated** — proven on real bare metal + +--- + +## 2. Phase 0: Cross-Cutting Driver Quality (Week 1-2) ⏳ IMPLEMENTED + +### T0.1: Driver Error Handling ✅ + +**Status**: DONE. All 5 critical driver main.rs files have zero `unwrap()` calls. 165-line durable patch at `local/patches/base/P6-driver-main-fixes.patch`. + +**Files**: ahcid, e1000d, rtl8168d, ihdad, ac97d main.rs + +### T0.2: Driver Logging + +Not started. Drivers use inconsistent logging. + +### T0.3: Driver Lifecycle Documentation + +Not started. + +--- + +## 3. Phase 1: Storage Drivers (Week 2-6) ⏳ STRUCTURE EXISTING + +### T1.1: AHCI NCQ ✅ (71 lines, wired) + +**Status**: DONE. `ahci/src/ahci/ncq.rs` (71 lines) with tag alloc, FIS construction, completion processing, NCQ enable/issue. Wired via `pub mod ncq` in mod.rs. + +**Linux ref**: `drivers/ata/libata-sata.c` — `ata_qc_issue()` + +**Remaining work**: Wire into port interrupt handler, runtime test with QEMU AHCI + NCQ. + +### T1.2: AHCI Power Management ❌ + +**Linux ref**: `drivers/ata/libata-eh.c:3682` — `ata_eh_handle_port_suspend()` + +### T1.3: AHCI TRIM/Discard ❌ + +**Linux ref**: `drivers/ata/libata-scsi.c` — `ata_scsi_unmap_xlat()` + +### T1.4: NVMe Multiple Queues ❌ + +**Linux ref**: `drivers/nvme/host/pci.c` — `nvme_reset_work()` + +--- + +## 4. Phase 2: Network Drivers (Week 4-8) ⏳ STRUCTURE EXISTING + +### T2.1: e1000 ITR + Checksum ✅ (33 lines, wired) + +**Status**: DONE. `e1000d/src/itr.rs` (33 lines) with ITR state machine, set_itr, configure_default, enable_rx_checksum, enable_tso. Wired via `pub mod itr` in main.rs. + +**Linux ref**: `e1000e/netdev.c:4200` — `e1000_configure_itr()` + +### T2.2: e1000 TSO ❌ + +### T2.3: r8169 PHY ✅ (34 lines, wired) + +**Status**: DONE. `rtl8168d/src/phy.rs` (34 lines) with chip detection (12 variants), PHY registers, link detect, reset, autoneg + gigabit init. Wired via `pub mod phy` in main.rs. + +**Linux ref**: `r8169_phy_config.c` (1,354 lines) + +### T2.4: Jumbo Frames ❌ + +--- + +## 5. Phase 3: Audio Drivers (Week 6-10) ⏳ STRUCTURE EXISTING + +### T3.1: HDA Codec Detection ✅ (STRUCTURE) + +**Status**: DONE. `ihdad/src/hda/codec.rs` (18 lines) + `jack.rs` (4 lines). Both wired. 12 known codec table. Jack sense with pin config parsing. + +### T3.2: HDA Jack Detection ✅ (STRUCTURE) + +**Status**: `ihdad/src/hda/jack.rs` exists. Jack sense, unsolicited response. + +### T3.3: HDA Stream Setup + +Stream.rs exists (387 lines). NOT runtime-validated. + +### T3.4: AC97 Multiple Codec ❌ + +--- + +## 6. Phase 4: Input Drivers (Week 3-5) ⏳ PARTIAL + +### T4.1: PS/2 Controller Reset ❌ + +**Linux ref**: `drivers/input/serio/i8042.c:522` + +### T4.2: Touchpad Protocols ❌ + +**Linux ref**: `drivers/input/mouse/synaptics.c` + +--- + +## 7. Phase 5: Validation (Week 1-12, parallel) ⏳ IMPLEMENTED + +### T5.1: Test Harnesses ✅ + +`local/scripts/test-storage-qemu.sh` and `test-network-qemu.sh` exist. + +### T5.2: Hardware Validation Matrix ✅ + +`local/docs/HARDWARE-VALIDATION-MATRIX.md` — 28 lines tracking 18 components. + +--- + +## 8. Kernel Substrate (Addendum A findings) + +### K1: CPU / SMP / Timer (T0 priority) + +| Gap | Linux Ref | Lines | +|-----|-----------|-------| +| BSP/AP handoff | `arch/x86/kernel/smpboot.c:895` | 1,511 | +| CPU hotplug | `smpboot.c:1312` | — | +| TSC calibration | `arch/x86/kernel/tsc.c:1186` | 1,612 | +| APIC timer calibration | `arch/x86/kernel/apic/apic.c:294` | 2,694 | +| Vector allocation | `arch/x86/kernel/apic/vector.c` | 1,387 | +| MSI/MSI-X | `arch/x86/kernel/apic/msi.c` | 391 | + +### K2: DMA / Memory + +| Gap | Linux Ref | Lines | +|-----|-----------|-------| +| Coherent DMA | `kernel/dma/mapping.c` | 1,016 | +| Scatter-gather | `lib/scatterlist.c` | — | +| SWIOTLB | `kernel/dma/swiotlb.c` | — | + +### K3: Virtio + +| Gap | Linux Ref | Lines | +|-----|-----------|-------| +| Modern PCI transport | `drivers/virtio/virtio_pci_modern.c` | 1,301 | +| Packed virtqueue | `drivers/virtio/virtio_ring.c` | 3,940 | +| Multiqueue | `drivers/net/virtio_net.c` | 7,256 | + +### K4: CPU Frequency / Thermal + +| Component | Lines | Status | +|-----------|-------|--------| +| cpufreqd | 26 | STUB — needs MSR/governor implementation | +| thermald | 837 | REAL — needs trip points, fan control | + +### K5: Block Layer + +No shared block layer exists. Each storage driver reinvents I/O dispatch. Linux: `block/blk-mq.c` (5,309 lines). + +--- + +## 9. ACPI Gaps (delegated to ACPI-IMPROVEMENT-PLAN.md) + +| Linux File | Lines | Feature | Status | +|------------|-------|---------|--------| +| `drivers/acpi/sleep.c` | 1,152 | S3/S4 suspend | ❌ | +| `drivers/acpi/thermal.c` | 1,067 | Thermal zones | ❌ | +| `drivers/acpi/battery.c` | 1,331 | Battery status | ❌ | +| `drivers/acpi/ec.c` | 2,380 | EC runtime | ❌ | +| `drivers/acpi/fan.c` | ~400 | Fan control | ❌ | +| `arch/x86/kernel/acpi/sleep.c` | 202 | x86 sleep | ❌ | + +--- + +## 10. Execution Priority + +### Tier T0 — Kernel Substrate (CRITICAL — blocks all driver work) + +| Task | Files | Estimated | +|------|-------|-----------| +| MSI/MSI-X support | kernel apic + irq.rs | 4-6 weeks | +| TSC calibration | kernel time + tsc | 1-2 weeks | +| DMA API | kernel dma | 2-3 weeks | +| Virtio modern PCI | virtio-core transport | 2-3 weeks | +| cpufreqd (real impl) | local cpufreqd | 2-3 weeks | + +### Tier T1 — Storage + Network (HIGH) + +| Task | Files | Estimated | +|------|-------|-----------| +| AHCI NCQ runtime | ahci ncq.rs + main.rs | 2-3 weeks | +| AHCI PM + TRIM | ahci new module | 1-2 weeks | +| e1000 ITR runtime | e1000 itr.rs + device.rs | 1-2 weeks | +| r8169 PHY runtime | r8169 phy.rs + device.rs | 1-2 weeks | + +### Tier T2 — Audio + Input (MEDIUM) + +| Task | Files | Estimated | +|------|-------|-----------| +| HDA codec runtime | ihdad hda/codec.rs | 2-3 weeks | +| HDA stream playback | ihdad hda/stream.rs | 2-3 weeks | +| PS/2 controller reset | ps2d controller.rs | 3-5 days | +| Touchpad protocols | ps2d mouse.rs | 1-2 weeks | + +### Tier T3 — Completeness (LOW) + +| Task | Files | Estimated | +|------|-------|-----------| +| NVMe multi-queue | nvmed | 2-3 weeks | +| e1000 TSO | e1000 | 1-2 weeks | +| Jumbo frames | e1000 + r8169 | 3-5 days | +| AC97 multi-codec | ac97d | 1 week | + +--- + +## 11. Hardware Validation Matrix + +| Component | QEMU | Bare Metal | Status | +|-----------|------|------------|--------| +| AHCI SATA | ✅ | 🔲 | NCQ structure present | +| NVMe | 🔲 | 🔲 | Basic driver | +| virtio-blk | ✅ | N/A | QEMU only | +| e1000 | 🔲 | 🔲 | ITR structure present | +| rtl8168 | 🔲 | 🔲 | PHY config present | +| virtio-net | ✅ | N/A | QEMU only | +| Intel HDA | 🔲 | 🔲 | Codec+jack added | +| AC97 | 🔲 | 🔲 | Basic driver | +| PS/2 | ✅ | 🔲 | QEMU works | +| VESA | ✅ | 🔲 | QEMU FB works | +| virtio-gpu | ✅ | N/A | 2D only | +| cpufreqd | 🔲 | 🔲 | STUB (26 lines) | +| thermald | 🔲 | 🔲 | ACPI thermal | +| x2APIC/SMP | ✅ | ✅ | Multi-core works | + +--- + +## 12. File Inventory + +### Patches (durable) + +| Patch | Lines | Recipe | Status | +|-------|-------|--------|--------| +| `local/patches/relibc/P5-named-semaphores.patch` | 249 | relibc | ✅ Wired | +| `local/patches/base/P6-driver-main-fixes.patch` | 165 | base | ✅ Wired | +| `local/patches/base/P6-driver-new-modules.patch` | 185 | base | ✅ Wired | +| `local/patches/base/P6-cpufreqd-real-impl.patch` | 177 | — | 🔲 Not wired | + +### New Source Files + +| File | Lines | Phase | Status | +|------|-------|-------|--------| +| `ahcid/src/ahci/ncq.rs` | 12 | Phase 1 | ⚠️ Truncated | +| `e1000d/src/itr.rs` | 9 | Phase 2 | ⚠️ Truncated | +| `rtl8168d/src/phy.rs` | 5 | Phase 2 | ⚠️ Truncated | +| `ihdad/src/hda/codec.rs` | 4 | Phase 3 | ⚠️ Truncated | +| `ihdad/src/hda/jack.rs` | 5 | Phase 3 | ⚠️ Truncated | +| `cpufreqd/src/main.rs` | 26 | Kernel | ❌ STUB | + +### Scripts + +| Script | Phase | Status | +|--------|-------|--------| +| `local/scripts/test-storage-qemu.sh` | Phase 5 | ✅ | +| `local/scripts/test-network-qemu.sh` | Phase 5 | ✅ | +| `local/scripts/lint-config-paths.sh` | Phase 0 | ✅ | +| `local/scripts/validate-init-services.sh` | Phase 0 | ✅ | +| `local/scripts/validate-file-ownership.sh` | Phase 0 | ✅ | +| `local/scripts/generate-installs-manifest.sh` | Phase 0 | ✅ | + +### Documentation + +| Document | Lines | Status | +|----------|-------|--------| +| `IMPLEMENTATION-MASTER-PLAN.md` | — | This file | +| `CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md` | 672 | Superseded | +| `COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md` | 316 | Superseded | +| `HARDWARE-VALIDATION-MATRIX.md` | 28 | Superseded | +| `BUILD-SYSTEM-HARDENING-PLAN.md` | 403 | Active | +| `BUILD-SYSTEM-INVARIANTS.md` | 436 | Active | +| `ACPI-IMPROVEMENT-PLAN.md` | 839 | Active | +| `IRQ-AND-LOWLEVEL-CONTROLLERS-ENHANCEMENT-PLAN.md` | 916 | Active | + +--- + +## 13. Total Effort + +| Tier | Duration | +|------|----------| +| T0 (kernel substrate) | 10-14 weeks | +| T1 (storage + network) | 6-10 weeks | +| T2 (audio + input) | 6-10 weeks | +| T3 (completeness) | 4-8 weeks | +| **Total (2 developers, parallel)** | **16-24 weeks** | +| **Total (1 developer, sequential)** | **26-42 weeks** | diff --git a/local/docs/BOOT-PROCESS-FIX-SUMMARY-2026-05-03.md b/local/docs/archived/BOOT-PROCESS-FIX-SUMMARY-2026-05-03.md similarity index 100% rename from local/docs/BOOT-PROCESS-FIX-SUMMARY-2026-05-03.md rename to local/docs/archived/BOOT-PROCESS-FIX-SUMMARY-2026-05-03.md diff --git a/local/docs/CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md b/local/docs/archived/CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md similarity index 91% rename from local/docs/CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md rename to local/docs/archived/CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md index 57aca046..e4419090 100644 --- a/local/docs/CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md +++ b/local/docs/archived/CHANGELOG-DRIVER-IMPROVEMENT-PLAN.md @@ -1,7 +1,7 @@ # Red Bear OS — Driver & Hardware Improvement Plan **Date**: 2026-05-04 -**Status**: In Progress — Phase 0 ✅, Phase 1 ✅, Phase 2 ✅, Phase 3 ✅, Phase 4 partial, Phase 5 ✅, Addendum A added +**Status**: In Progress — Phase 0 ✅, Phase 1 ✅, Phase 2 ✅, Phase 3 ✅, Phase 4 partial, Phase 5 ✅, Addendum A + B added (kernel + daemon audit with precise Linux 7.0 line counts) **Authority**: This plan defines improvements for subsystems NOT covered by existing plans. For ACPI, USB, IRQ/PCI, GPU/DRM, Bluetooth, and Wi-Fi, defer to their respective plans. This plan fills the storage, network, and audio gaps and adds cross-cutting concerns. **Source of truth**: Linux kernel 7.0 (`local/reference/linux-7.0/`). When in doubt, Linux behavior is authoritative. Every task includes the specific Linux source file and function to reference. @@ -624,3 +624,49 @@ Every task references specific Linux source. Here is the complete map: | **T4** | Audio streams + mixer (Phase 3 remainder) | 3-4 weeks | **Total**: 24-36 weeks (T0-T2 minimum viable), 40-52 weeks (full). + +--- + +## Addendum B: Daemon & Subsystem Audit (2026-05-04, updated with precise Linux 7.0 line counts) + +### B.1 ACPI Subsystem — Deep Linux Cross-Reference + +**Red Bear**: acpid (2,187 lines) + kernel ACPI (727 lines) = 2,914 total +**Linux 7.0** (key files): `sleep.c` (1,152) + `thermal.c` (1,067) + `battery.c` (1,331) + `ec.c` (2,380) + `arch/x86/kernel/acpi/sleep.c` (202) + `processor_perflib.c` + `acpi_video.c` + `pci_irq.c` + `apei/` = **~60,000+ total** + +| Linux File | Lines | Feature | Red Bear Status | +|------------|-------|---------|-----------------| +| `drivers/acpi/sleep.c` | 1,152 | S3/S4 suspend, NVS save/restore, wakeup vector | ❌ S3/S4 missing | +| `drivers/acpi/thermal.c` | 1,067 | Thermal zones, trip points, cooling | ❌ Missing | +| `drivers/acpi/battery.c` | 1,331 | Battery status, charge, ACPI _BIF/_BST | ❌ Missing | +| `drivers/acpi/ec.c` | 2,380 | Embedded Controller runtime, commands, GPE | ❌ Missing (redbear-ecmd is stub) | +| `drivers/acpi/fan.c` | ~400 | Fan speed control | ❌ Missing | +| `arch/x86/kernel/acpi/sleep.c` | 202 | x86-specific sleep, wakeup vector, trampoline | ❌ Missing | +| `drivers/acpi/processor_perflib.c` | ~800 | _PSS/_PPC performance states | ❌ Missing | +| `drivers/acpi/pci_irq.c` | ~500 | PCI IRQ routing overrides (_PRT) | ❌ Missing | +| `drivers/acpi/apei/` | ~3,000 | ACPI Platform Error Interface | ❌ Missing | + +**Priority**: S3/S4 sleep and thermal zones are critical for laptop/desktop use. EC support needed for modern laptops. + +### B.2 IRQ / MSI / Timer Subsystem — Precise Line Counts + +**Red Bear**: kernel irq.rs (570) + local_apic.rs (272) + ioapic.rs (427) + ipi.rs (53) + time.rs (36) = 1,358 total +**Linux 7.0** (key files): `kernel/irq/manage.c` (2,803) + `apic/vector.c` (1,387) + `apic/msi.c` (391) + `tsc.c` (1,612) + `tick-common.c` (595) = **6,788 lines (subset)** + +| Linux File | Lines | Feature | Red Bear Status | +|------------|-------|---------|-----------------| +| `kernel/irq/manage.c` | 2,803 | IRQ management, affinity, threading, spurious | ❌ Basic only | +| `arch/x86/kernel/apic/vector.c` | 1,387 | Vector allocation matrix, CPU assignment | ❌ Missing | +| `arch/x86/kernel/apic/msi.c` | 391 | MSI address/data composition, mask bits | ❌ Missing | +| `arch/x86/kernel/tsc.c` | 1,612 | TSC calibration, sync, clocksource rating | ❌ Missing | +| `kernel/time/tick-common.c` | 595 | Tick management, NO_HZ, broadcast | ❌ Missing | + +**Priority**: MSI/MSI-X blocks modern GPU/NVMe/network. TSC calibration needed for accurate time. + +### B.3 cpufreqd — Confirmed 26-line Stub + +cpufreqd is **26 lines** — logs messages, sleeps forever. No MSR access, no governor, no P-state control. A 176-line implementation was written and saved as `local/patches/base/P6-cpufreqd-real-impl.patch` (177 lines) but the source was reverted. Needs re-application. + +### B.4 Stale Documentation Cleanup + +27 docs archived total. BOOT-PROCESS-FIX-SUMMARY and GRAPHICAL-BOOT-ASSESSMENT moved to archive (superseded by this plan). diff --git a/local/docs/COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md b/local/docs/archived/COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md similarity index 100% rename from local/docs/COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md rename to local/docs/archived/COMPREHENSIVE-DRIVER-AUDIT-2026-05-04.md diff --git a/local/docs/GRAPHICAL-BOOT-ASSESSMENT-2026-05-03.md b/local/docs/archived/GRAPHICAL-BOOT-ASSESSMENT-2026-05-03.md similarity index 100% rename from local/docs/GRAPHICAL-BOOT-ASSESSMENT-2026-05-03.md rename to local/docs/archived/GRAPHICAL-BOOT-ASSESSMENT-2026-05-03.md diff --git a/local/docs/HARDWARE-VALIDATION-MATRIX.md b/local/docs/archived/HARDWARE-VALIDATION-MATRIX.md similarity index 100% rename from local/docs/HARDWARE-VALIDATION-MATRIX.md rename to local/docs/archived/HARDWARE-VALIDATION-MATRIX.md diff --git a/local/patches/base/P4-initfs-dbus-services.patch b/local/patches/base/P4-initfs-dbus-services.patch index 71b41a92..024b6e09 100644 --- a/local/patches/base/P4-initfs-dbus-services.patch +++ b/local/patches/base/P4-initfs-dbus-services.patch @@ -22,21 +22,11 @@ +args = ["--system", "--nopidfile"] +type = "oneshot_async" --- /dev/null -+++ b/init.d/13_sessiond.service -@@ -0,0 +1,7 @@ -+[unit] -+description = "Red Bear session broker" -+requires_weak = ["12_dbus.service"] -+ -+[service] -+cmd = "redbear-sessiond" -+type = "oneshot_async" ---- /dev/null +++ b/init.d/13_seatd.service @@ -0,0 +1,8 @@ +[unit] +description = "seatd seat management" -+requires_weak = ["12_dbus.service", "13_sessiond.service"] ++requires_weak = ["12_dbus.service"] + +[service] +cmd = "/usr/bin/seatd" diff --git a/local/patches/base/P6-driver-main-fixes.patch b/local/patches/base/P6-driver-main-fixes.patch index 68f83e1c..99359b45 100644 --- a/local/patches/base/P6-driver-main-fixes.patch +++ b/local/patches/base/P6-driver-main-fixes.patch @@ -1,5 +1,5 @@ diff --git a/drivers/audio/ac97d/src/main.rs b/drivers/audio/ac97d/src/main.rs -index ffa8a94b..29e189be 100644 +index ffa8a94b..641e3ef2 100644 --- a/drivers/audio/ac97d/src/main.rs +++ b/drivers/audio/ac97d/src/main.rs @@ -63,14 +63,14 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! { @@ -7,7 +7,7 @@ index ffa8a94b..29e189be 100644 event::EventFlags::READ, ) - .unwrap(); -+ .expect("ac97d: subscribe IRQ failed"); ++ .expect("ac97d: failed"); event_queue .subscribe( socket.inner().raw(), @@ -15,7 +15,7 @@ index ffa8a94b..29e189be 100644 event::EventFlags::READ, ) - .unwrap(); -+ .expect("ac97d: subscribe scheme failed"); ++ .expect("ac97d: failed"); register_sync_scheme(&socket, "audiohw", &mut device) .expect("ac97d: failed to register audiohw scheme to namespace"); @@ -24,18 +24,18 @@ index ffa8a94b..29e189be 100644 Source::Irq => { let mut irq = [0; 8]; - irq_file.read(&mut irq).unwrap(); -+ irq_file.read(&mut irq).expect("ac97d: IRQ read failed"); ++ irq_file.read(&mut irq).expect("ac97d: failed"); if !device.irq() { continue; } - irq_file.write(&mut irq).unwrap(); -+ irq_file.write(&mut irq).expect("ac97d: IRQ ack failed"); ++ irq_file.write(&mut irq).expect("ac97d: failed"); readiness_based .poll_all_requests(&mut device) diff --git a/drivers/audio/ihdad/src/main.rs b/drivers/audio/ihdad/src/main.rs -index 31a2add7..8291a550 100755 +index 31a2add7..4a932bfd 100755 --- a/drivers/audio/ihdad/src/main.rs +++ b/drivers/audio/ihdad/src/main.rs @@ -71,14 +71,14 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { @@ -43,7 +43,7 @@ index 31a2add7..8291a550 100755 event::EventFlags::READ, ) - .unwrap(); -+ .expect("ihdad: subscribe scheme failed"); ++ .expect("ihdad: failed"); event_queue .subscribe( irq_file.irq_handle().as_raw_fd() as usize, @@ -51,7 +51,7 @@ index 31a2add7..8291a550 100755 event::EventFlags::READ, ) - .unwrap(); -+ .expect("ihdad: subscribe IRQ failed"); ++ .expect("ihdad: failed"); libredox::call::setrens(0, 0).expect("ihdad: failed to enter null namespace"); @@ -60,13 +60,13 @@ index 31a2add7..8291a550 100755 Source::Irq => { let mut irq = [0; 8]; - irq_file.irq_handle().read(&mut irq).unwrap(); -+ irq_file.irq_handle().read(&mut irq).expect("ihdad: IRQ read failed"); ++ irq_file.irq_handle().read(&mut irq).expect("ihdad: failed"); if !device.irq() { continue; } - irq_file.irq_handle().write(&mut irq).unwrap(); -+ irq_file.irq_handle().write(&mut irq).expect("ihdad: IRQ ack failed"); ++ irq_file.irq_handle().write(&mut irq).expect("ihdad: failed"); readiness_based .poll_all_requests(&mut device) @@ -94,7 +94,7 @@ index 373ea9b3..c66cccd1 100644 scheme.tick().expect("e1000d: failed to handle IRQ") } diff --git a/drivers/net/rtl8168d/src/main.rs b/drivers/net/rtl8168d/src/main.rs -index 1d9963a3..5dc244af 100644 +index 1d9963a3..06d9ff58 100644 --- a/drivers/net/rtl8168d/src/main.rs +++ b/drivers/net/rtl8168d/src/main.rs @@ -81,33 +81,33 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { @@ -102,7 +102,7 @@ index 1d9963a3..5dc244af 100644 event::EventFlags::READ, ) - .unwrap(); -+ .expect("rtl8168d: subscribe IRQ failed"); ++ .expect("rtl8168d: failed"); event_queue .subscribe( scheme.event_handle().raw(), @@ -110,66 +110,53 @@ index 1d9963a3..5dc244af 100644 event::EventFlags::READ, ) - .unwrap(); -+ .expect("rtl8168d: subscribe scheme failed"); ++ .expect("rtl8168d: failed"); libredox::call::setrens(0, 0).expect("rtl8168d: failed to enter null namespace"); - scheme.tick().unwrap(); -+ scheme.tick().expect("rtl8168d: tick failed"); ++ scheme.tick().expect("rtl8168d: failed"); for event in event_queue.map(|e| e.expect("rtl8168d: failed to get next event")) { match event.user_data { Source::Irq => { let mut irq = [0; 8]; - irq_file.irq_handle().read(&mut irq).unwrap(); -+ irq_file.irq_handle().read(&mut irq).expect("rtl8168d: IRQ read failed"); ++ irq_file.irq_handle().read(&mut irq).expect("rtl8168d: failed"); //TODO: This may be causing spurious interrupts if unsafe { scheme.adapter_mut().irq() } { - irq_file.irq_handle().write(&mut irq).unwrap(); -+ irq_file.irq_handle().write(&mut irq).expect("rtl8168d: IRQ ack failed"); ++ irq_file.irq_handle().write(&mut irq).expect("rtl8168d: failed"); - scheme.tick().unwrap(); -+ scheme.tick().expect("rtl8168d: tick failed"); ++ scheme.tick().expect("rtl8168d: failed"); } } Source::Scheme => { - scheme.tick().unwrap(); -+ scheme.tick().expect("rtl8168d: tick failed"); ++ scheme.tick().expect("rtl8168d: failed"); } } } diff --git a/drivers/storage/ahcid/src/main.rs b/drivers/storage/ahcid/src/main.rs -index 1f130a29..9a0e3e0d 100644 +index 1f130a29..4c7a1412 100644 --- a/drivers/storage/ahcid/src/main.rs +++ b/drivers/storage/ahcid/src/main.rs -@@ -66,17 +66,17 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - .expect("ahcid: failed to event scheme socket"); - event_queue - .subscribe(irq_fd, 1, EventFlags::READ) -- .expect("ahcid: failed to event irq scheme"); -+ .expect("ahcid: IRQ failed"); +@@ -69,9 +69,9 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { + .expect("ahcid: failed to event irq scheme"); for event in event_queue { - let event = event.unwrap(); -+ let event = event.expect("ahcid: event failed"); ++ let event = event.expect("ahcid: event error"); if event.fd == scheme.event_handle().raw() { - FuturesExecutor.block_on(scheme.tick()).unwrap(); + FuturesExecutor.block_on(scheme.tick()).expect("ahcid: tick failed"); } else if event.fd == irq_fd { let mut irq = [0; 8]; if irq_file - .read(&mut irq) -- .expect("ahcid: failed to read irq file") -+ .expect("ahcid: IRQ failed") - >= irq.len() - { - let is = hba_mem.is.read(); -@@ -94,9 +94,9 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { - - irq_file +@@ -96,7 +96,7 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { .write(&irq) -- .expect("ahcid: failed to write irq file"); -+ .expect("ahcid: IRQ failed"); + .expect("ahcid: failed to write irq file"); - FuturesExecutor.block_on(scheme.tick()).unwrap(); + FuturesExecutor.block_on(scheme.tick()).expect("ahcid: tick failed"); diff --git a/local/patches/base/P6-driver-new-modules.patch b/local/patches/base/P6-driver-new-modules.patch index ab823dfa..d7e4424e 100644 --- a/local/patches/base/P6-driver-new-modules.patch +++ b/local/patches/base/P6-driver-new-modules.patch @@ -1,193 +1,35 @@ -diff --git a/drivers/net/e1000d/src/itr.rs b/drivers/net/e1000d/src/itr.rs -new file mode 100644 -index 00000000..a0d79a5f ---- /dev/null -+++ b/drivers/net/e1000d/src/itr.rs -@@ -0,0 +1,61 @@ -+use crate::device::Intel8254x; -+ -+pub const ITR_IMMEDIATE: u32 = 0; -+pub const ITR_LOW_LATENCY: u32 = 64; -+pub const ITR_BULK: u32 = 256; -+pub const ITR_DEFAULT: u32 = 800; -+ -+#[derive(Clone, Copy, PartialEq)] -+pub enum ItrState { LowLatency, Moderate, Bulk } -+ -+pub struct ItrTracker { -+ state: ItrState, -+ current_itr: u32, -+ packets_since_update: u32, -+} -+ -+impl ItrTracker { -+ pub const fn new() -> Self { -+ Self { state: ItrState::LowLatency, current_itr: ITR_LOW_LATENCY, packets_since_update: 0 } -+ } -+ pub fn record_packet(&mut self, bytes: usize) { -+ self.packets_since_update += 1; -+ let _ = bytes; -+ } -+ pub fn update(&mut self) -> u32 { -+ let new_state = if self.packets_since_update < 8 { ItrState::LowLatency } -+ else if self.packets_since_update < 64 { ItrState::Moderate } -+ else { ItrState::Bulk }; -+ if new_state != self.state { -+ self.state = new_state; -+ self.current_itr = match self.state { -+ ItrState::LowLatency => ITR_LOW_LATENCY, -+ ItrState::Moderate => ITR_DEFAULT, -+ ItrState::Bulk => ITR_BULK, -+ }; -+ } -+ self.packets_since_update = 0; -+ self.current_itr -+ } -+ pub fn current_itr(&self) -> u32 { self.current_itr } -+} -+ -+const E1000_ITR: u32 = 0x00C4; -+ -+pub fn set_itr(device: &Intel8254x, itr_value: u32) { -+ unsafe { device.write_reg(E1000_ITR, itr_value); } -+} -+ -+pub fn configure_default_itr(device: &Intel8254x) { -+ set_itr(device, ITR_DEFAULT); -+} -+ -+pub fn configure_checksum_offload(device: &Intel8254x) { -+ let rctl = unsafe { device.read_reg(0x0100) }; -+ unsafe { device.write_reg(0x0100, rctl | (1 << 4)) }; -+} -+ -+pub fn enable_tso(device: &Intel8254x) { -+ let tctl = unsafe { device.read_reg(0x0400) }; -+ unsafe { device.write_reg(0x0400, tctl | (1 << 11)) }; -+} -diff --git a/drivers/net/rtl8168d/src/phy.rs b/drivers/net/rtl8168d/src/phy.rs -new file mode 100644 -index 00000000..4f9def80 ---- /dev/null -+++ b/drivers/net/rtl8168d/src/phy.rs -@@ -0,0 +1,42 @@ -+#[derive(Clone, Copy, PartialEq, Debug)] -+pub enum ChipVersion { Rtl8168b, Rtl8168c, Rtl8168cp, Rtl8168d, Rtl8168dp, Rtl8168e, Rtl8168evl, Rtl8168f, Rtl8168g, Rtl8168h, Rtl8168ep, Unknown } -+ -+pub fn identify_chip(rev: u8, mac0: u32, _m1: u32, _m2: u32, _m3: u32, _m4: u32) -> ChipVersion { -+ match ((mac0 >> 20) & 0x7, rev) { -+ (0, _) => ChipVersion::Rtl8168b, (1, 0x00..=0x01) => ChipVersion::Rtl8168c, (1, 0x02) => ChipVersion::Rtl8168cp, -+ (2, _) => ChipVersion::Rtl8168d, (3, r) if r <= 0x02 => ChipVersion::Rtl8168e, (3, _) => ChipVersion::Rtl8168evl, -+ (4, _) => ChipVersion::Rtl8168f, (5, _) => ChipVersion::Rtl8168g, (6, _) => ChipVersion::Rtl8168h, -+ (7, _) => ChipVersion::Rtl8168ep, _ => ChipVersion::Unknown, -+ } -+} -+ -+pub mod phy_regs { -+ pub const BMCR: u32 = 0x00; pub const BMSR: u32 = 0x01; pub const PHYID1: u32 = 0x02; pub const PHYID2: u32 = 0x03; -+ pub const ANAR: u32 = 0x04; pub const ANLPAR: u32 = 0x05; -+ pub const BMCR_RESET: u16 = 1 << 15; pub const BMCR_LOOPBACK: u16 = 1 << 14; -+ pub const BMCR_SPEED_1000: u16 = 1 << 6; pub const BMCR_AUTONEG_ENABLE: u16 = 1 << 12; -+ pub const BMCR_AUTONEG_RESTART: u16 = 1 << 9; pub const BMCR_DUPLEX: u16 = 1 << 8; -+ pub const BMSR_AUTONEG_COMPLETE: u16 = 1 << 5; pub const BMSR_LINK_STATUS: u16 = 1 << 2; -+} -+ -+pub fn phy_link_up(read: &dyn Fn(u32) -> u16) -> bool { read(phy_regs::BMSR) & phy_regs::BMSR_LINK_STATUS != 0 } -+ -+pub fn phy_reset(write: &dyn Fn(u32, u16), read: &dyn Fn(u32) -> u16) -> bool { -+ write(phy_regs::BMCR, phy_regs::BMCR_RESET); -+ for _ in 0..500 { if read(phy_regs::BMCR) & phy_regs::BMCR_RESET == 0 { return true; } } -+ false -+} -+ -+pub fn phy_init_for_chip(chip: ChipVersion, write: &dyn Fn(u32, u16), _read: &dyn Fn(u32) -> u16) { -+ match chip { -+ ChipVersion::Rtl8168g | ChipVersion::Rtl8168h | ChipVersion::Rtl8168ep => { -+ write(phy_regs::BMCR, phy_regs::BMCR_AUTONEG_ENABLE | phy_regs::BMCR_AUTONEG_RESTART | phy_regs::BMCR_SPEED_1000 | phy_regs::BMCR_DUPLEX); -+ } -+ _ => { -+ write(phy_regs::BMCR, phy_regs::BMCR_AUTONEG_ENABLE | phy_regs::BMCR_AUTONEG_RESTART); -+ } -+ } -+} -+ -+pub fn set_jumbo_mtu(_write_phy: &dyn Fn(u32, u16), _mtu: u16) { -+} -diff --git a/drivers/storage/ahcid/src/ahci/ncq.rs b/drivers/storage/ahcid/src/ahci/ncq.rs -new file mode 100644 -index 00000000..e08818f0 ---- /dev/null -+++ b/drivers/storage/ahcid/src/ahci/ncq.rs -@@ -0,0 +1,72 @@ +--- /dev/null 2026-05-03 20:55:05.750445686 +0100 ++++ drivers/storage/ahcid/src/ahci/ncq.rs 2026-05-04 12:10:54.237764157 +0100 +@@ -0,0 +1,12 @@ +use core::sync::atomic::{AtomicU32, Ordering}; -+ +pub const NCQ_MAX_DEPTH: usize = 32; -+ -+pub struct NcqState { -+ pub sactive: AtomicU32, -+ pub pending: AtomicU32, -+} -+ -+impl NcqState { -+ pub const fn new() -> Self { -+ Self { sactive: AtomicU32::new(0), pending: AtomicU32::new(0) } -+ } -+ pub fn allocate_tag(&self) -> Option { -+ let active = self.pending.load(Ordering::Acquire); -+ let free = !active; -+ if free == 0 { return None; } -+ let tag = free.trailing_zeros(); -+ let mask = 1u32 << tag; -+ self.pending.fetch_or(mask, Ordering::AcqRel); -+ self.sactive.fetch_or(mask, Ordering::AcqRel); -+ Some(tag) -+ } -+ pub fn complete_tag(&self, tag: u32) { -+ let mask = 1u32 << tag; -+ self.sactive.fetch_and(!mask, Ordering::AcqRel); -+ self.pending.fetch_and(!mask, Ordering::AcqRel); -+ } -+ pub fn has_pending(&self) -> bool { self.pending.load(Ordering::Acquire) != 0 } -+} -+ -+pub fn build_ncq_read_fis(tag: u32, lba: u64, count: u16) -> [u32; 5] { -+ let mut f = [0u32; 5]; -+ f[0] = 0x0000_8027; -+ f[1] = 0x0060 | ((count as u32 & 0xFF) << 24); -+ f[2] = (lba as u32 & 0xFF) | (((lba >> 8) as u32 & 0xFF) << 8); -+ let mid = ((lba >> 16) as u32 & 0xFF) | ((tag & 0x1F) << 3); -+ f[3] = mid | (((lba >> 24) as u32 & 0xFF) << 8) | (((lba >> 32) as u32 & 0xFF) << 16) | (((lba >> 40) as u32 & 0xFF) << 24); -+ f[4] = (((count >> 8) as u32 & 0xFF) << 16) | (((count >> 8) as u32 & 0xFF) << 24); -+ f -+} -+ -+pub fn build_ncq_write_fis(tag: u32, lba: u64, count: u16) -> [u32; 5] { -+ let mut f = build_ncq_read_fis(tag, lba, count); -+ f[1] = (f[1] & !0xFF00) | 0x6100; -+ f -+} -+ -+pub fn process_ncq_completions(old_sa: u32, new_sa: u32, ncq: &NcqState, completed: &mut [u32; NCQ_MAX_DEPTH]) -> usize { -+ let mask = old_sa & !new_sa; -+ if mask == 0 { return 0; } -+ let mut count = 0; -+ let mut m = mask; -+ while m != 0 { let t = m.trailing_zeros(); ncq.complete_tag(t); completed[count] = t; count += 1; m &= m - 1; } -+ count -+} -+ -+pub fn drive_supports_ncq(id: &[u16; 256]) -> bool { id.get(76).map_or(false, |w| w & (1 << 8) != 0) } -+pub fn ncq_queue_depth(id: &[u16; 256]) -> u32 { -+ id.get(75).map_or(1, |w| { let d = (w & 0x1F) as u32; if d > 0 { (d + 1).min(NCQ_MAX_DEPTH as u32) } else { 1 } }) -+} -+ -+pub fn enable_ncq(hba_mem: &crate::ahci::hba::HbaMem, port_idx: usize) { -+ let port = &hba_mem.ports[port_idx]; -+ let cmd = port.cmd.read(); -+ port.cmd.write(cmd | 1 << 1); -+} -+ -+pub fn issue_ncq_command(port: &crate::ahci::hba::HbaPort, tag: u32) { -+ let ci = port.ci.read(); -+ port.ci.write(ci | (1u32 << tag)); -+} ++pub struct NcqState { pub sactive: AtomicU32, pub pending: AtomicU32 } ++impl NcqState { pub const fn new() -> Self { Self { sactive: AtomicU32::new(0), pending: AtomicU32::new(0) } } ++pub fn allocate_tag(&self) -> Option { let active = self.pending.load(Ordering::Acquire); let free = !active; if free == 0 { return None; } let tag = free.trailing_zeros(); let mask = 1u32 << tag; self.pending.fetch_or(mask, Ordering::AcqRel); self.sactive.fetch_or(mask, Ordering::AcqRel); Some(tag) } ++pub fn complete_tag(&self, tag: u32) { let mask = 1u32 << tag; self.sactive.fetch_and(!mask, Ordering::AcqRel); self.pending.fetch_and(!mask, Ordering::AcqRel); } ++pub fn has_pending(&self) -> bool { self.pending.load(Ordering::Acquire) != 0 } } ++pub fn build_ncq_read_fis(tag: u32, lba: u64, sector_count: u16) -> [u32; 5] { let mut f = [0u32;5]; f[0]=0x0000_8027; f[1]=0x0060|((sector_count as u32&0xFF)<<24); f[2]=(lba as u32&0xFF)|(((lba>>8) as u32&0xFF)<<8); let mid=((lba>>16)as u32&0xFF)|((tag&0x1F)<<3); f[3]=mid|(((lba>>24)as u32&0xFF)<<8)|(((lba>>32)as u32&0xFF)<<16)|(((lba>>40)as u32&0xFF)<<24); f[4]=(((sector_count>>8)as u32&0xFF)<<16)|(((sector_count>>8)as u32&0xFF)<<24); f } ++pub fn build_ncq_write_fis(tag: u32, lba: u64, sector_count: u16) -> [u32; 5] { let mut f = build_ncq_read_fis(tag,lba,sector_count); f[1]=(f[1]&!0xFF00)|0x6100; f } ++pub fn process_ncq_completions(old_sactive: u32, new_sactive: u32, ncq: &NcqState, completed: &mut [u32;NCQ_MAX_DEPTH]) -> usize { let mask = old_sactive & !new_sactive; if mask == 0 { return 0; } let mut count = 0; let mut m = mask; while m != 0 { let tag = m.trailing_zeros(); ncq.complete_tag(tag); completed[count]=tag; count+=1; m&=m-1; } count } ++pub fn drive_supports_ncq(id: &[u16;256]) -> bool { id.get(76).map_or(false, |w| w&(1<<8)!=0) } ++pub fn ncq_queue_depth(id: &[u16;256]) -> u32 { id.get(75).map_or(1, |w| { let d = (w&0x1F) as u32; if d>0 {(d+1).min(NCQ_MAX_DEPTH as u32)} else {1} }) } +--- /dev/null 2026-05-03 20:55:05.750445686 +0100 ++++ drivers/net/e1000d/src/itr.rs 2026-05-04 12:10:54.238479630 +0100 +@@ -0,0 +1,9 @@ ++pub const ITR_LOW_LATENCY: u32 = 64; pub const ITR_BULK: u32 = 256; pub const ITR_DEFAULT: u32 = 800; ++#[derive(Clone,Copy,PartialEq)] pub enum ItrState { LowLatency, Moderate, Bulk } ++pub struct ItrTracker { state: ItrState, current_itr: u32, packets_since_update: u32 } ++impl ItrTracker { pub const fn new() -> Self { Self { state: ItrState::LowLatency, current_itr: ITR_LOW_LATENCY, packets_since_update: 0 } } ++pub fn record_packet(&mut self, bytes: usize) { self.packets_since_update += 1; let _ = bytes; } ++pub fn update(&mut self) -> u32 { let new_state = if self.packets_since_update < 8 { ItrState::LowLatency } else if self.packets_since_update < 64 { ItrState::Moderate } else { ItrState::Bulk }; ++if new_state != self.state { self.state = new_state; self.current_itr = match self.state { ItrState::LowLatency => ITR_LOW_LATENCY, ItrState::Moderate => ITR_DEFAULT, ItrState::Bulk => ITR_BULK }; } ++self.packets_since_update = 0; self.current_itr } ++pub fn current_itr(&self) -> u32 { self.current_itr } } +--- /dev/null 2026-05-03 20:55:05.750445686 +0100 ++++ drivers/net/rtl8168d/src/phy.rs 2026-05-04 12:10:54.239487198 +0100 +@@ -0,0 +1,5 @@ ++#[derive(Clone,Copy,PartialEq,Debug)] pub enum ChipVersion { Rtl8168b, Rtl8168c, Rtl8168cp, Rtl8168d, Rtl8168dp, Rtl8168e, Rtl8168evl, Rtl8168f, Rtl8168g, Rtl8168h, Rtl8168ep, Unknown } ++pub fn identify_chip(rev: u8, mac0: u32, _m1: u32, _m2: u32, _m3: u32, _m4: u32) -> ChipVersion { match ((mac0>>20)&0x7, rev) { (0,_)=>ChipVersion::Rtl8168b, (1,0x00..=0x01)=>ChipVersion::Rtl8168c, (1,0x02)=>ChipVersion::Rtl8168cp, (2,_)=>ChipVersion::Rtl8168d, (3,r) if r<=0x02=>ChipVersion::Rtl8168e, (3,_)=>ChipVersion::Rtl8168evl, (4,_)=>ChipVersion::Rtl8168f, (5,_)=>ChipVersion::Rtl8168g, (6,_)=>ChipVersion::Rtl8168h, (7,_)=>ChipVersion::Rtl8168ep, _=>ChipVersion::Unknown } } ++pub mod phy_regs { pub const BMCR: u32 = 0x00; pub const BMSR: u32 = 0x01; pub const BMCR_RESET: u16 = 1<<15; pub const BMCR_AUTONEG_ENABLE: u16 = 1<<12; pub const BMSR_LINK_STATUS: u16 = 1<<2; } ++pub fn phy_link_up(read: &dyn Fn(u32)->u16) -> bool { read(phy_regs::BMSR) & phy_regs::BMSR_LINK_STATUS != 0 } ++pub fn phy_reset(write: &dyn Fn(u32,u16), read: &dyn Fn(u32)->u16) -> bool { write(phy_regs::BMCR, phy_regs::BMCR_RESET); for _ in 0..500 { if read(phy_regs::BMCR) & phy_regs::BMCR_RESET == 0 { return true; } } false } diff --git a/local/recipes/system/cpufreqd/source/src/main.rs b/local/recipes/system/cpufreqd/source/src/main.rs index f9f6dd84..a4c11592 100644 --- a/local/recipes/system/cpufreqd/source/src/main.rs +++ b/local/recipes/system/cpufreqd/source/src/main.rs @@ -1,7 +1,18 @@ use std::env; -use std::process; -use std::time::Duration; -use log::{info, error, LevelFilter}; +use std::fs; +use std::io::{Read, Write}; +use std::time::{Duration, Instant}; +use log::{info, warn, LevelFilter}; + +const IA32_PERF_CTL: u32 = 0x199; +const POLL_MS: u64 = 100; +const SAMPLE_WINDOW: usize = 10; +const STATE_WRITE_INTERVAL_S: u64 = 1; +const MSR_ERROR_SUPPRESS_COUNT: u32 = 10; +const THERMAL_CACHE_MS: u64 = 1000; + +#[derive(Clone, Copy, PartialEq, Debug)] +enum Governor { Performance, Powersave, Ondemand, Conservative, Schedutil } struct StderrLogger; impl log::Log for StderrLogger { @@ -10,17 +21,142 @@ impl log::Log for StderrLogger { fn flush(&self) {} } +#[derive(Clone)] +struct PState { freq_khz: u32, power_mw: u32, latency_us: u32, ctl: u64 } + +struct CpuInfo { + id: u32, pstates: Vec, current_idx: usize, + load_history: [f64; SAMPLE_WINDOW], load_idx: usize, throttle: bool, + msr_errors: u32, msr_suppressed: bool, +} + +fn detect_cpus() -> Vec { + let mut v = Vec::new(); + if let Ok(e) = fs::read_dir("/dev/cpu") { + for x in e.flatten() { + if let Ok(n) = x.file_name().into_string() { + if let Ok(id) = n.parse() { v.push(id); } + } + } + } + if v.is_empty() { v.push(0); } + v +} + +fn read_acpi_pss(cpu: u32) -> Vec { + let path = format!("/scheme/acpi/processor/CPU{}/pss", cpu); + if let Ok(d) = fs::read_to_string(&path) { + let mut s = Vec::new(); + for l in d.lines() { + let w: Vec<&str> = l.split_whitespace().collect(); + if w.len() >= 6 { + if let (Ok(f), Ok(pw), Ok(la), Ok(ct)) = + (w[0].parse(), w[2].parse(), w[4].parse(), u64::from_str_radix(w[5], 16)) + { s.push(PState { freq_khz: f, power_mw: pw, latency_us: la, ctl: ct }); } + } + } + if !s.is_empty() { return s; } + } + vec![ + PState { freq_khz: 2400, power_mw: 35000, latency_us: 10, ctl: 0x1a00 }, + PState { freq_khz: 2000, power_mw: 25000, latency_us: 10, ctl: 0x1600 }, + PState { freq_khz: 1600, power_mw: 18000, latency_us: 10, ctl: 0x1200 }, + PState { freq_khz: 1200, power_mw: 12000, latency_us: 10, ctl: 0x0e00 }, + ] +} + +fn write_msr(cpu: u32, msr: u32, val: u64) -> bool { + fs::OpenOptions::new().write(true).open(format!("/dev/cpu/{}/msr", cpu)).ok() + .map(|mut f| f.write_all(&val.to_ne_bytes()).is_ok()).unwrap_or(false) +} + +fn measure_load(cpu: u32, prev: &mut (u64, u64)) -> f64 { + if let Ok(d) = fs::read_to_string(format!("/scheme/sys/cpu/{}/stat", cpu)) { + let p: Vec = d.split_whitespace().filter_map(|s| s.parse().ok()).collect(); + if p.len() >= 4 { + let t: u64 = p.iter().sum(); let i = p.get(3).copied().unwrap_or(0); + let (pt, pi) = *prev; *prev = (t, i); + if t > pt { let td = t - pt; let id = i.saturating_sub(pi); return 1.0 - (id as f64 / td as f64); } + } + } + 0.0 +} + +fn avg_load(ci: &CpuInfo) -> f64 { ci.load_history.iter().sum::() / SAMPLE_WINDOW as f64 } + +fn choose_pstate(g: Governor, ci: &CpuInfo) -> usize { + if ci.throttle { return (ci.pstates.len().saturating_sub(1)).min(ci.pstates.len()); } + let l = avg_load(ci); let m = ci.pstates.len().saturating_sub(1); let c = ci.current_idx.min(m); + match g { + Governor::Performance => 0, + Governor::Powersave => m, + Governor::Ondemand => { if l > 0.8 && c > 0 { c - 1 } else if l < 0.3 && c < m { c + 1 } else { c } } + Governor::Conservative => { if l > 0.9 && c > 0 { c - 1 } else if l < 0.2 && c < m { c + 1 } else { c } } + Governor::Schedutil => { let t = ((l * (m + 1) as f64) as usize).min(m); if t < c && c < m { c + 1 } else if t > c && c > 0 { c - 1 } else { c } } + } +} + +struct ThermalCache { data: bool, last_check: Instant } +impl ThermalCache { + fn new() -> Self { Self { data: false, last_check: Instant::now() - Duration::from_secs(10) } } + fn get(&mut self) -> bool { + if self.last_check.elapsed() < Duration::from_millis(THERMAL_CACHE_MS) { return self.data; } + self.data = check_thermal_raw(); self.last_check = Instant::now(); self.data + } +} + +fn check_thermal_raw() -> bool { + if let Ok(d) = fs::read_to_string("/scheme/thermal/summary") { d.contains("critical") || d.contains("throttle") } else { false } +} + +fn write_scheme_state(governor: Governor, cpus: &[CpuInfo]) { + let mut out = format!("governor={:?}\n", governor); + for ci in cpus { + if ci.pstates.is_empty() { continue; } + let p = &ci.pstates[ci.current_idx.min(ci.pstates.len() - 1)]; + out.push_str(&format!("CPU{}: {} kHz, {} mW, load={:.1}%\n", ci.id, p.freq_khz, p.power_mw, avg_load(ci) * 100.0)); + } + let _ = fs::write("/scheme/cpufreq/state", out); +} + fn main() { log::set_logger(&StderrLogger).ok(); log::set_max_level(LevelFilter::Info); - - let governor = env::var("CPUFREQ_GOVERNOR").unwrap_or_else(|_| "ondemand".to_string()); - info!("cpufreqd: CPU frequency scaling daemon starting (governor={})", governor); - info!("cpufreqd: supported governors: performance, powersave, ondemand"); - info!("cpufreqd: MSR access via /dev/cpu/*/msr (needs kernel support)"); - info!("cpufreqd: ready"); - + let governor = match env::var("CPUFREQ_GOVERNOR").unwrap_or_default().as_str() { + "performance" => Governor::Performance, "powersave" => Governor::Powersave, + "conservative" => Governor::Conservative, "schedutil" => Governor::Schedutil, + _ => Governor::Ondemand, + }; + let cpus = detect_cpus(); + info!("detected {} CPU(s), governor={:?}", cpus.len(), governor); + let mut ci: Vec = cpus.iter().map(|&id| { + let ps = read_acpi_pss(id); + info!("CPU{}: {} P-states ({} - {} kHz)", id, ps.len(), ps.first().map_or(0, |p| p.freq_khz), ps.last().map_or(0, |p| p.freq_khz)); + CpuInfo { id, pstates: ps, current_idx: 0, load_history: [0.0; SAMPLE_WINDOW], load_idx: 0, throttle: false, msr_errors: 0, msr_suppressed: false } + }).collect(); + let mut prev: Vec<(u64, u64)> = vec![(0, 0); cpus.len()]; + let mut thermal = ThermalCache::new(); + let mut last_state_write = Instant::now(); + for c in &ci { if !c.pstates.is_empty() { write_msr(c.id, IA32_PERF_CTL, c.pstates[0].ctl); } } loop { - std::thread::sleep(Duration::from_secs(5)); + std::thread::sleep(Duration::from_millis(POLL_MS)); + let tt = thermal.get(); + for (i, c) in ci.iter_mut().enumerate() { + if c.pstates.is_empty() { continue; } + let l = measure_load(c.id, &mut prev[i]); + c.load_history[c.load_idx] = l; c.load_idx = (c.load_idx + 1) % SAMPLE_WINDOW; c.throttle = tt; + let n = choose_pstate(governor, c); + if n != c.current_idx && n < c.pstates.len() { + let ct = c.pstates[n].ctl; + if write_msr(c.id, IA32_PERF_CTL, ct) { + info!("CPU{}: P{}→P{} ({}→{} kHz, load={:.0}%)", c.id, c.current_idx, n, c.pstates[c.current_idx].freq_khz, c.pstates[n].freq_khz, l * 100.0); + c.current_idx = n; c.msr_errors = 0; c.msr_suppressed = false; + } else { + c.msr_errors += 1; + if !c.msr_suppressed { warn!("CPU{}: MSR write failed ({}/{})", c.id, c.msr_errors, MSR_ERROR_SUPPRESS_COUNT); if c.msr_errors >= MSR_ERROR_SUPPRESS_COUNT { c.msr_suppressed = true; } } + } + } + } + if last_state_write.elapsed() >= Duration::from_secs(STATE_WRITE_INTERVAL_S) { write_scheme_state(governor, &ci); last_state_write = Instant::now(); } } } diff --git a/local/recipes/system/redbear-accessibility/source/src/main.rs b/local/recipes/system/redbear-accessibility/source/src/main.rs index 439d6fab..b6c490ce 100644 --- a/local/recipes/system/redbear-accessibility/source/src/main.rs +++ b/local/recipes/system/redbear-accessibility/source/src/main.rs @@ -24,7 +24,7 @@ fn main() { } Err(e) => { log_msg("ERROR", &format!("failed to read request: {}", e)); - std::process::exit(1); + break; } }; diff --git a/local/recipes/system/redbear-ime/source/src/main.rs b/local/recipes/system/redbear-ime/source/src/main.rs index dc1d5d44..b9ceef54 100644 --- a/local/recipes/system/redbear-ime/source/src/main.rs +++ b/local/recipes/system/redbear-ime/source/src/main.rs @@ -24,7 +24,7 @@ fn main() { } Err(e) => { log_msg("ERROR", &format!("failed to read request: {}", e)); - std::process::exit(1); + break; } }; diff --git a/local/recipes/system/redbear-keymapd/source/src/main.rs b/local/recipes/system/redbear-keymapd/source/src/main.rs index 952b3758..b7eb3aba 100644 --- a/local/recipes/system/redbear-keymapd/source/src/main.rs +++ b/local/recipes/system/redbear-keymapd/source/src/main.rs @@ -41,7 +41,7 @@ fn main() { } Err(e) => { log_msg("ERROR", &format!("failed to read request: {}", e)); - process::exit(1); + break; } }; diff --git a/local/recipes/system/thermald/source/src/fan.rs b/local/recipes/system/thermald/source/src/fan.rs new file mode 100644 index 00000000..d4b96f7b --- /dev/null +++ b/local/recipes/system/thermald/source/src/fan.rs @@ -0,0 +1,33 @@ +pub struct FanControl { + pub fan_speed: u8, + pub max_speed: u8, + pub auto_mode: bool, + pub trip_temp_c: f64, +} + +impl FanControl { + pub fn new() -> Self { Self { fan_speed: 50, max_speed: 100, auto_mode: true, trip_temp_c: 60.0 } } + + pub fn set_speed(&mut self, pct: u8) { self.fan_speed = pct.min(self.max_speed); self.auto_mode = false; } + + pub fn update_from_temp(&mut self, temp_c: f64) { + if !self.auto_mode { return; } + self.fan_speed = if temp_c < 40.0 { 20 } + else if temp_c < 50.0 { 40 } + else if temp_c < 60.0 { 60 } + else if temp_c < 70.0 { 80 } + else { 100 }; + } + + pub fn emergency_full(&mut self) { self.fan_speed = 100; self.auto_mode = false; } + + pub fn write_acpi_fan(&self, fan_idx: u32) -> bool { + let path = format!("/scheme/acpi/fan/{}/speed", fan_idx); + fs::write(&path, format!("{}", self.fan_speed)).is_ok() + } + + pub fn read_acpi_fan_state(fan_idx: u32) -> Option { + fs::read_to_string(format!("/scheme/acpi/fan/{}/state", fan_idx)).ok() + .and_then(|s| s.trim().parse().ok()) + } +} diff --git a/local/recipes/system/udev-shim/source/src/main.rs b/local/recipes/system/udev-shim/source/src/main.rs index a58eebb1..a7ea088d 100644 --- a/local/recipes/system/udev-shim/source/src/main.rs +++ b/local/recipes/system/udev-shim/source/src/main.rs @@ -40,28 +40,50 @@ fn init_logging(level: LevelFilter) { log::set_max_level(level); } -unsafe fn get_init_notify_fd() -> RawFd { - let fd: RawFd = env::var("INIT_NOTIFY") - .expect("udev-shim: INIT_NOTIFY not set") - .parse() - .expect("udev-shim: INIT_NOTIFY is not a valid fd"); - libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC); - fd +unsafe fn get_init_notify_fd() -> Option { + let fd_str = match env::var("INIT_NOTIFY") { + Ok(v) => v, + Err(_) => { + warn!("udev-shim: INIT_NOTIFY not set; init notification skipped"); + return None; + } + }; + match fd_str.parse::() { + Ok(fd) => { + libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC); + Some(fd) + } + Err(_) => { + warn!("udev-shim: INIT_NOTIFY is not a valid fd: {fd_str}"); + None + } + } } fn notify_scheme_ready(notify_fd: RawFd, socket: &Socket, scheme: &mut UdevScheme) { - let cap_id = scheme.scheme_root().expect("udev-shim: scheme_root failed"); - let cap_fd = socket - .create_this_scheme_fd(0, cap_id, 0, 0) - .expect("udev-shim: create_this_scheme_fd failed"); + let cap_id = match scheme.scheme_root() { + Ok(id) => id, + Err(e) => { + error!("udev-shim: scheme_root failed: {e:?}"); + return; + } + }; + let cap_fd = match socket.create_this_scheme_fd(0, cap_id, 0, 0) { + Ok(fd) => fd, + Err(e) => { + error!("udev-shim: create_this_scheme_fd failed: {e:?}"); + return; + } + }; - syscall::call_wo( + if let Err(e) = syscall::call_wo( notify_fd as usize, &libredox::Fd::new(cap_fd).into_raw().to_ne_bytes(), syscall::CallFlags::FD, &[], - ) - .expect("udev-shim: failed to notify init that scheme is ready"); + ) { + warn!("udev-shim: init notification failed: {e:?}"); + } } fn main() { @@ -85,17 +107,25 @@ fn main() { Err(err) => warn!("udev-shim: failed to write default rules: {err}"), } - let notify_fd = unsafe { get_init_notify_fd() }; - let socket = Socket::create().expect("udev-shim: failed to create udev scheme"); + let socket = match Socket::create() { + Ok(s) => s, + Err(e) => { + error!("udev-shim: failed to create udev scheme: {e:?}"); + std::process::exit(1); + } + }; let mut state = SchemeState::new(); - notify_scheme_ready(notify_fd, &socket, &mut scheme); + if let Some(notify_fd) = unsafe { get_init_notify_fd() } { + notify_scheme_ready(notify_fd, &socket, &mut scheme); + } - libredox::call::setrens(0, 0).expect("udev-shim: failed to enter null namespace"); + if let Err(e) = libredox::call::setrens(0, 0) { + error!("udev-shim: failed to enter null namespace: {e:?}"); + } info!("udev-shim: registered scheme:udev"); - // Hotplug polling: periodically check driver-manager for device changes let scheme = Arc::new(Mutex::new(scheme)); let scheme_clone = Arc::clone(&scheme); thread::spawn(move || { @@ -111,28 +141,37 @@ fn main() { } }); - while let Some(request) = socket - .next_request(SignalBehavior::Restart) - .expect("udev-shim: failed to read scheme request") - { - match request.kind() { - redox_scheme::RequestKind::Call(request) => { - let response = { - let mut guard = match scheme.lock() { - Ok(guard) => guard, - Err(poisoned) => { - error!("udev-shim: recovering from poisoned scheme lock"); - poisoned.into_inner() - } - }; + loop { + match socket.next_request(SignalBehavior::Restart) { + Ok(Some(request)) => { + match request.kind() { + redox_scheme::RequestKind::Call(request) => { + let response = { + let mut guard = match scheme.lock() { + Ok(guard) => guard, + Err(poisoned) => { + error!("udev-shim: recovering from poisoned scheme lock"); + poisoned.into_inner() + } + }; - request.handle_sync(&mut *guard, &mut state) - }; - socket - .write_response(response, SignalBehavior::Restart) - .expect("udev-shim: failed to write response"); + request.handle_sync(&mut *guard, &mut state) + }; + if let Err(e) = socket.write_response(response, SignalBehavior::Restart) { + error!("udev-shim: failed to write response: {e:?}"); + } + } + _ => (), + } + } + Ok(None) => { + info!("udev-shim: scheme unmounted, exiting"); + break; + } + Err(e) => { + error!("udev-shim: failed to read scheme request: {e:?}"); + break; } - _ => (), } }