diff --git a/config/redbear-live-mini.toml b/config/redbear-live-mini.toml index 22db7f34..883d32d4 100644 --- a/config/redbear-live-mini.toml +++ b/config/redbear-live-mini.toml @@ -11,7 +11,7 @@ include = ["minimal.toml", "redbear-legacy-base.toml", "redbear-netctl.toml"] [general] -filesystem_size = 768 +filesystem_size = 384 [packages] # Red Bear OS branding and host utilities. @@ -40,19 +40,25 @@ data = "wired-dhcp\n" [[files]] path = "/usr/lib/init.d/30_console" -data = "" +data = """ +requires_weak 10_net +inputd -A 2 +nowait getty 2 +nowait getty /scheme/debug/no-preserve -J +""" [[files]] path = "/usr/lib/init.d/29_activate_console.service" data = """ [unit] -description = "Activate console (text-only no-op)" +description = "Activate console VT" requires_weak = [ - "00_base.target", + "10_net.target", ] [service] -cmd = "true" +cmd = "inputd" +args = ["-A", "2"] type = "oneshot" """ @@ -67,7 +73,7 @@ requires_weak = [ [service] cmd = "getty" -args = ["/scheme/tty"] +args = ["2"] type = "oneshot_async" """ @@ -77,7 +83,7 @@ data = """ [unit] description = "Debug console" requires_weak = [ - "00_base.target", + "10_net.target", ] [service] @@ -86,6 +92,125 @@ args = ["/scheme/debug/no-preserve", "-J"] type = "oneshot_async" """ +[[files]] +path = "/etc/init.d/10_smolnetd.service" +data = """ +[unit] +description = "Network stack (non-blocking on live-mini)" +requires_weak = [ + "00_pcid-spawner.service", +] + +[service] +cmd = "netstack" +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/10_dhcpd.service" +data = """ +[unit] +description = "DHCP client daemon (non-blocking on live-mini)" +requires_weak = [ + "10_smolnetd.service", +] + +[service] +cmd = "dhcpd" +args = ["-f"] +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/00_i2cd.service" +data = """ +[unit] +description = "I2C adapter registry (non-blocking on live-mini)" +requires_weak = [ + "00_base.target", +] + +[service] +cmd = "i2cd" +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/00_i2c-hidd.service" +data = """ +[unit] +description = "ACPI I2C HID bring-up daemon (non-blocking on live-mini)" +requires_weak = [ + "00_i2cd.service", + "00_i2c-dw-acpi.service", + "00_intel-gpiod.service", + "00_i2c-gpio-expanderd.service", +] + +[service] +cmd = "i2c-hidd" +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/00_i2c-gpio-expanderd.service" +data = """ +[unit] +description = "I2C GPIO expander companion bridge (non-blocking on live-mini)" +requires_weak = [ + "00_i2cd.service", + "00_gpiod.service", +] + +[service] +cmd = "i2c-gpio-expanderd" +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/00_ucsid.service" +data = """ +[unit] +description = "USB-C UCSI topology detector (non-blocking on live-mini)" +requires_weak = [ + "00_base.target", + "00_i2cd.service", +] + +[service] +cmd = "ucsid" +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/00_gpiod.service" +data = """ +[unit] +description = "GPIO controller registry (non-blocking on live-mini)" +requires_weak = [ + "00_base.target", +] + +[service] +cmd = "gpiod" +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/00_intel-gpiod.service" +data = """ +[unit] +description = "Intel ACPI GPIO registrar (non-blocking on live-mini)" +requires_weak = [ + "00_gpiod.service", + "00_i2cd.service", +] + +[service] +cmd = "intel-gpiod" +type = "oneshot_async" +""" + [[files]] path = "/etc/motd" data = """ @@ -96,26 +221,69 @@ data = """ ################################## """ +[[files]] +path = "/etc/init.d/20_audiod.service" +data = """ +[unit] +description = "Audio multiplexer (non-blocking on live-mini)" +requires_weak = [ + "00_base.target", +] + +[service] +cmd = "audiod" +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/01_debug_console.service" +data = """ +[unit] +description = "Debug serial login" +requires_weak = [ + "00_ptyd.service", +] + +[service] +cmd = "getty" +args = ["/scheme/debug/no-preserve", "-J"] +type = "oneshot_async" +""" + +[[files]] +path = "/etc/init.d/02_serial_probe.service" +data = """ +[unit] +description = "Serial boot probe marker" +requires_weak = [ + "00_base.target", +] + +[service] +cmd = "ion" +args = ["-c", "echo RB_SERIAL_PROBE_OK"] +type = "oneshot" +""" + [[files]] path = "/etc/pcid.d/ihdgd.toml" data = """ -# redbear-live-mini: text-only image, disable Intel HD graphics auto-spawn -[[drivers]] -name = "disabled ihdgd sentinel" -class = 0xFF -vendor = 0xFFFF -device = 0xFFFF -command = ["ihdgd"] +# redbear-live-mini: text-only image; override upstream ihdgd config with empty file """ [[files]] path = "/etc/pcid.d/virtio-gpud.toml" data = """ -# redbear-live-mini: text-only image, disable virtio GPU auto-spawn -[[drivers]] -name = "disabled virtio-gpud sentinel" -class = 0xFF -vendor = 0xFFFF -device = 0xFFFF -command = ["virtio-gpud"] +# redbear-live-mini: text-only image; override upstream virtio-gpud config with empty file +""" + +[[files]] +path = "/etc/pcid.d/00_text_mode_gpu_mask.toml" +data = """ +# redbear-live-mini: force text-only mode by consuming all display-class PCI devices +# with a no-op command, before any graphics-capable driver rules are evaluated. +[[drivers]] +name = "Text-only live-mini display mask" +class = 0x03 +command = ["/bin/true"] """ diff --git a/config/redbear-minimal.toml b/config/redbear-minimal.toml index ed279302..ea283210 100644 --- a/config/redbear-minimal.toml +++ b/config/redbear-minimal.toml @@ -51,7 +51,12 @@ data = "wired-dhcp\n" # minimal.toml: "inputd -A 2", "nowait getty 2", "nowait getty /scheme/debug/no-preserve -J" [[files]] path = "/usr/lib/init.d/30_console" -data = "" +data = """ +requires_weak 10_net +inputd -A 2 +nowait getty 2 +nowait getty /scheme/debug/no-preserve -J +""" [[files]] path = "/usr/lib/init.d/15_fatd.service" diff --git a/local/docs/ACPI-I2C-HID-IMPLEMENTATION-PLAN.md b/local/docs/ACPI-I2C-HID-IMPLEMENTATION-PLAN.md index 1da7d522..b5b77731 100644 --- a/local/docs/ACPI-I2C-HID-IMPLEMENTATION-PLAN.md +++ b/local/docs/ACPI-I2C-HID-IMPLEMENTATION-PLAN.md @@ -151,6 +151,13 @@ surface that can matter during boot on modern bare metal. - GPIO expanders used to expose reset, enable, interrupt, or wake lines for input devices - platform-specific I2C controller companions that gate access to the actual `I2C-HID` device +Concrete implementation carriers in local Linux reference tree: + +- `drivers/hid/intel-thc-hid/intel-quicki2c/*` for Intel THC QuickI2C-backed HID paths + (Lunar/Panther/Nova/Wildcat generations) +- `drivers/gpio/*` families used as ACPI `GpioInt`/`GpioIo` providers for input reset/wake rails +- `drivers/i2c/i2c-core-acpi.c` resource binding behavior for controller/device matching semantics + These are not always directly user-visible as "devices", but they are boot-relevant whenever the keyboard/touchpad path depends on them. @@ -191,3 +198,79 @@ For boot-to-login on modern laptops, the correct priority is: 4. introduce the minimal native I2C userspace substrate 5. implement Intel LPSS controller ownership 6. implement `i2c-hidd` + +## Boot-Critical I2C Addendum (post-Phase D) + +The remaining boot-critical order after initial `i2c-hidd` is: + +1. Intel THC QuickI2C transport path for ACPI-described HID devices on new Intel laptops +2. GPIO companion completeness for `GpioInt` and `GpioIo` reset/wake wiring +3. platform-specific I2C controller companions only where they gate input availability + +Anything outside this list should not preempt keyboard/touchpad path completion for boot-to-login. + +### Concrete device classes to implement next (boot-first order) + +| Priority | Device class | Linux carrier in tree | Red Bear status | +|---|---|---|---| +| P0 | Intel THC QuickI2C transport (`HID over THC`) | `drivers/hid/intel-thc-hid/intel-quicki2c/*` | detection/parking landed, transport still missing | +| P1 | GPIO companions for `GpioInt`/`GpioIo` (reset/wake rails) | `drivers/gpio/*`, ACPI resource flow | partially landed, board-specific gaps remain | +| P2 | Controller-companion ACPI methods (`_DSM/_DSD`) that gate input | `i2c-core-acpi.c`, QuickI2C ACPI helpers | partially landed, still platform-dependent | +| P3 | USB-C/UCSI I2C only on machines where input depends on it | `drivers/usb/typec/ucsi/*` and ACPI glue | partial: ACPI UCSI (`PNP0CA0`/`AMDI0042`) discovery + bounded I2C probe/policy surface landed; runtime UCSI transport/partner path still missing | + +This order is strict for boot-to-login resilience on modern laptops. + +Current in-tree staging note: +- initfs boot ownership for ACPI/PIC is now explicit: `40_pcid.service` -> `41_acpid.service` -> `40_hwd.service` -> `40_pcid-spawner-initfs.service`; `hwd` no longer spawns `acpid` or `pcid` ad hoc. +- `redbear-live-mini` now enables `00_i2c-hidd.service` in non-blocking mode (`oneshot_async`) instead of masking it with `cmd = "true"`. +- `redbear-live-mini` now carries non-blocking `00_i2c-gpio-expanderd.service` and `00_ucsid.service` overlays so the boot-minimal image keeps companion GPIO and UCSI topology diagnostics aligned with the boot-critical I2C path. +- `intel-thc-hidd` now performs ACPI companion resolution, `_DSM` capability reads, `PNP0C50` scan, THC-bound candidate diagnostics, BAR mapping, and registers a minimal `intel-thc-quicki2c` adapter into `i2cd` with transfer handling through THC I2C subIP (DesignWare-style path). +- `intel-thc-hidd` now also consumes bounded ACPI controller-companion methods `ICRS` and `ISUB` when present, using them to refine adapter speed/addressing diagnostics and to apply ACPI timing overrides for DW SCL high/low counters. +- `intel-thc-hidd` now emits compact `RB_THC_HIDD_SCHEMA` / `RB_THC_HIDD` marker lines with THC-bound PNP0C50 candidate counts and status reasons (`available` vs `not-available`) so boot logs can distinguish missing ACPI binding surfaces from transport/runtime faults. +- `intel-thc-hidd` now canonicalizes the primary `RB_THC_HIDD` status field and warns/coerces unknown values to `error`, matching the parser-robust status policy used in `hwd`/`i2c-hidd`. +- `RB_THC_HIDD` / `RB_THC_HIDD_FATAL` markers now include `generation=` for explicit correlation with other boot-readiness streams. +- `RB_THC_HIDD` status semantics now explicitly include `not-ready` (ACPI symbols not ready) and `error` (ACPI symbol scan failure), so init ordering and enumeration faults are disambiguated in CI logs. +- `intel-thc-hidd` now emits `RB_THC_HIDD_FATAL status=error ...` markers on hard-stop failures (BAR map/size failures, unexpected DW component type, i2cd registration failure, provider-loop failure) so fatal transport bring-up exits are machine-classifiable. +- `hwd` now emits compact `RB_THC_QUICKI2C status=not-ready ...` and UCSI `RB_UCSI_* status=not-ready ...` markers even when ACPI symbol enumeration returns `WouldBlock`, preserving machine-readable readiness signals during early init ordering windows. +- `hwd` now reports THC companion `ICRS`/`ISUB` method readiness counts (`thc_quicki2c_ready`) during ACPI probe so missing controller-companion surfaces are visible before driver bring-up. +- `hwd` now also emits compact `RB_THC_QUICKI2C_SCHEMA` / `RB_THC_QUICKI2C` marker lines (`status=absent|available|not-ready`) for THC companion readiness scraping in CI/log pipelines. +- `RB_THC_QUICKI2C` now also includes `generation=` for consistent correlation semantics with other marker streams. +- `hwd` now assigns marker generation per ACPI probe pass and threads it through UCSI/THC fallback markers as well, eliminating `generation=0` ambiguity on local fallback paths. +- schema markers now also carry generation (`RB_UCSI_SCHEMA`, `RB_THC_QUICKI2C_SCHEMA`, `RB_UCSID_SCHEMA`, `RB_I2C_HIDD_SCHEMA`, `RB_THC_HIDD_SCHEMA`) so schema and data lines share the same correlation key shape. +- `i2c-hidd` now consults THC companion `ICRS` when bound through `intel-thc-quicki2c`, and performs a bounded slave-address override if HID `_CRS` I2C address and companion-method address disagree. +- `i2c-hidd` now emits compact `RB_I2C_HIDD_SCHEMA` and `RB_I2C_HIDD_BLOCKER` markers so unresolved THC resource-source adapter matches are machine-readable in boot logs (`reason=thc_transport_adapter_unavailable`). +- `i2c-hidd` marker emitters now canonicalize status fields (`RB_I2C_HIDD_SNAPSHOT`, `RB_I2C_HIDD_BLOCKER`) and warn/coerce unknown values to `error` for parser-safe output under schema drift. +- `RB_I2C_HIDD_BLOCKER` now also includes `generation=`, aligned to the current scan cycle so blocker and snapshot events are directly correlatable. +- `RB_I2C_HIDD_BLOCKER` status semantics now also include `not-ready` (`acpi_symbols_not_ready`) and `error` (`acpi_symbol_scan_error`) on the ACPI symbol scan path, aligning HID-consumer readiness reporting with THC producer markers. +- `i2c-hidd` scan-state handling now preserves that distinction in snapshots: ACPI `WouldBlock` yields `RB_I2C_HIDD_SNAPSHOT status=not-ready reason=acpi_symbols_not_ready` (not `not-available`), avoiding false “no devices” classification during early init ordering. +- `i2c-hidd` now emits `RB_I2C_HIDD_SNAPSHOT` per scan cycle (`status`, `reason`, `devices`, `adapters`, `started`, `probe_ok`, `probe_failed`) so boot logs expose HID bring-up progress and failure density, not only blocker edges. +- `RB_I2C_HIDD_SNAPSHOT` now also carries `generation=`, allowing cycle-level correlation with other readiness marker streams. +- the same `RB_I2C_HIDD_SNAPSHOT` stream now includes `status=error reason=scan_failed` on scan-cycle exceptions, preserving explicit machine-readable failure state even when scan aborts early. +- The in-tree bridge now derives adapter speed profile from ACPI-bound devices and includes bounded one-shot controller recovery/reinit on non-addressing transfer failures. +- `ucsid` now exposes `/scheme/ucsi` summary/device records with policy-driven `input_critical` classification, per-node probe policy, bounded AMDI0042 I2C probe telemetry, and PNP0CA0 ACPI `_DSM` capability-mask diagnostics (Linux carrier aligned with `ucsi_acpi.c` function-mask semantics). +- `ucsid` now also supports a policy/env-gated bounded PNP0CA0 `_DSM` function-2 read-call probe (`REDBEAR_UCSI_DSM_READ_PROBE` / `probe_dsm_read`) to surface ACPI transport readiness without enabling full UCSI command transport. +- when that bounded read probe returns a buffer payload, `ucsid` now surfaces a minimal UCSI header snapshot (`version_bcd` at offset 0 and `cci` at offset 4) for early transport-readiness diagnostics. +- `hwd` now opportunistically consumes `/scheme/ucsi/summary` (when available) and logs UCSI transport-readiness snapshot counts/devices on the ACPI probe path. +- `hwd` now classifies UCSI summary availability as `available` / `not-ready` / `not-available` / `error`, making initfs service-order gaps distinguishable from summary parse/probe failures. +- `hwd` now emits a compact `RB_UCSI_SNAPSHOT ...` key-value marker line for CI/log scrapers in addition to the human-readable UCSI status logs. +- `ucsid` now emits compact `RB_UCSID_SUMMARY ...` and per-device `RB_UCSID_DEVICE ...` marker lines so readiness can be scraped directly from daemon logs without scheme reads. +- `hwd` now mirrors per-device compact markers as `RB_UCSI_DEVICE ...` when `/scheme/ucsi/summary` is available, so CI can consume transport-readiness from the `hwd` boot stream as well. +- `ucsid` now classifies `transport_blocker` for `input_critical` devices that are not transport-ready, and exports `transport_blocked_input_critical` in summary markers for boot-priority triage. +- `ucsid`/`hwd` compact markers now include `health=ok|degraded` plus dedicated `RB_UCSID_HEALTH` / `RB_UCSI_HEALTH` lines keyed by `transport_blocked_input_critical`. +- `RB_UCSID_SUMMARY` and mirrored `RB_UCSI_SUMMARY` now carry `generation=` so CI can correlate `hwd` snapshots with a specific `ucsid` scan cycle and detect stale reads. +- generation is now assigned before each `ucsid` scan pass and included on per-device markers (`RB_UCSID_DEVICE` / mirrored `RB_UCSI_DEVICE`) so device-level lines are cycle-correlated as well. +- `RB_UCSI_SNAPSHOT` is now self-contained with `generation`, `health`, and `transport_blocked_input_critical` when summary is available, while preserving explicit `status=*` for not-ready/not-available/error cases. +- `hwd` now emits `RB_UCSI_SNAPSHOT status=absent ...` when no ACPI UCSI candidates are discovered, so CI can distinguish true surface absence from service readiness failures. +- `ucsid` compact markers now emit explicit status on every scan cycle (`status=available|absent|not-ready|error`) via `RB_UCSID_SUMMARY` / `RB_UCSID_HEALTH`. +- `ucsid` now also emits `RB_UCSID_SUMMARY` / `RB_UCSID_HEALTH` with `status=error` and `health=unknown` on scan-cycle failures, so CI can distinguish explicit UCSI scan faults from stale/missing marker streams. +- on scan failure, `ucsid` now also resets shared `/scheme/ucsi/summary` counters to a clean `status=error` state for that generation (instead of carrying stale counters from the previous successful cycle). +- `ucsid` ACPI-symbol `WouldBlock` no longer collapses into `status=absent`; it now emits explicit `status=not-ready` markers so early-init readiness is not misclassified as true UCSI surface absence. +- `/scheme/ucsi/summary` now carries explicit producer status (`available|absent|not-ready|error`) and `hwd` consumes that field when mirroring `RB_UCSI_*`, preventing false `status=available` interpretations from zeroed summary counters during not-ready/error cycles. +- `hwd` now normalizes UCSI summary status parsing (trims whitespace and accepts case variants) and warns before coercing unknown statuses to `error`, improving resilience to producer/schema drift. +- `hwd` now also cross-checks status against summary counters: contradictory payloads (`available` with zero devices, or `absent` with nonzero devices) are warned and coerced to safe marker output (`absent` / `error`) instead of being mirrored verbatim. +- for `status=not-ready|not-available|error`, `hwd` now warns if nonzero payload counters/devices are present before emitting fallback status markers, making producer-status drift explicit in logs. +- `ucsid` startup default summary state is now explicitly `status=not-ready` (instead of implicit empty/default status), so early pre-scan reads of `/scheme/ucsi/summary` remain unambiguous. +- mirrored `hwd` markers now carry explicit status on `RB_UCSI_SUMMARY` / `RB_UCSI_HEALTH` (`available|absent|not-ready|not-available|error`), keeping status semantics aligned across both producers and fallback paths. +- mirrored `RB_UCSI_DEVICE` now carries richer readiness context (`i2c_backed`, DSM read support/probe flags) to match `ucsid` diagnostics from a single boot-log stream. +- for `status=not-ready|not-available|error`, `hwd` now emits fallback `RB_UCSI_SUMMARY` / `RB_UCSI_HEALTH` lines (not just `RB_UCSI_SNAPSHOT`) so CI can rely on the same marker keys in every status path. +- Native THC DMA/report transport is still missing. diff --git a/local/docs/ACPI-IMPROVEMENT-PLAN.md b/local/docs/ACPI-IMPROVEMENT-PLAN.md index 325fe34f..67ef4dc5 100644 --- a/local/docs/ACPI-IMPROVEMENT-PLAN.md +++ b/local/docs/ACPI-IMPROVEMENT-PLAN.md @@ -24,7 +24,8 @@ What is still open: - userspace AML bootstrap no longer depends solely on `RSDP_ADDR` on x86, but the explicit boot-path handoff contract is still underdocumented and non-BIOS paths remain unresolved, - normal service ownership is still transitional: `hwd` and `acpid` live on the initfs boot path rather than under a stable long-lived rootfs service contract, - AML readiness is still coupled to PCI registration timing, -- `hwd` still spawns `acpid` ad hoc and the non-ACPI `LegacyBackend` fallback is effectively a TODO no-op, +- initfs boot order now starts `pcid` and `acpid` explicitly before `hwd`, and `hwd` no longer spawns `acpid` ad hoc, +- the non-ACPI `LegacyBackend` fallback is still effectively a TODO no-op, - failed `/scheme/acpi/register_pci` handoff now uses a bounded retry path before degrading, but the degraded contract is still not strong enough to call Wave 1 closed, - the `\_S5` / shutdown path is not yet trustworthy enough to call robust, - `/scheme/acpi/power` is still not a trustworthy runtime power surface, @@ -122,9 +123,9 @@ bounded-hardware, or release-grade completeness. - `acpid` startup still contains active panic-grade `expect` paths. - userspace AML bootstrap now has an explicit handoff path plus x86 BIOS fallback, but the producer side of that contract is still underdocumented and non-BIOS fallback remains unresolved. -- service lifecycle is still transitional: `hwd` and `acpid` are primarily initfs-owned and `acpid` is still spawned ad hoc rather than by an explicit long-lived rootfs unit. +- service lifecycle is still transitional: `hwd` and `acpid` are primarily initfs-owned rather than by an explicit long-lived rootfs unit. - `\_S5` derivation currently depends on AML readiness that is still gated on PCI registration. -- `hwd` owns an ad hoc `acpid` spawn path, while `LegacyBackend` fallback is still a TODO no-op rather than a meaningful degraded probe path. +- `hwd` no longer owns an ad hoc `acpid` spawn path; `LegacyBackend` fallback is still a TODO no-op rather than a meaningful degraded probe path. - `pcid` can continue without ACPI integration after a bounded retry window, so AML readiness still transitions from transient-not-ready to durable degraded mode without a stronger recovery contract. - post-PCI AML bootstrap failure is now surfaced as an explicit error instead of a quietly empty symbol surface, but that path still needs broader boot-path proof. - `set_global_s_state()` is effectively `S5`-only. @@ -198,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, ad hoc `acpid` spawn) 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 | 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 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/LINUX-BORROWING-RUST-IMPLEMENTATION-PLAN.md b/local/docs/LINUX-BORROWING-RUST-IMPLEMENTATION-PLAN.md index 96b48e4a..8459a035 100644 --- a/local/docs/LINUX-BORROWING-RUST-IMPLEMENTATION-PLAN.md +++ b/local/docs/LINUX-BORROWING-RUST-IMPLEMENTATION-PLAN.md @@ -195,19 +195,26 @@ Grounded in: - `recipes/core/base/source/drivers/hwd/src/backend/acpi.rs` - `recipes/core/base/source/drivers/hwd/src/main.rs` - `recipes/core/base/source/drivers/acpid/src/acpi.rs` +- `recipes/core/base/source/init.initfs.d/40_pcid.service` +- `recipes/core/base/source/init.initfs.d/41_acpid.service` +- `recipes/core/base/source/init.initfs.d/40_hwd.service` +- `recipes/core/base/source/init.initfs.d/40_pcid-spawner-initfs.service` Strict order: 1. kernel bootstrap / memory / early ACPI / IRQ / serio baseline 2. userspace bootstrap -3. `hwd` starts -4. `hwd` ACPI backend spawns `acpid` -5. `hwd` main spawns `pcid` -6. `acpid` waits for PCI registration before AML-symbol readiness +3. `pcid` starts in initfs (`40_pcid.service`) +4. `acpid` starts in initfs (`41_acpid.service`) +5. `hwd` starts (`40_hwd.service`) and probes only after `pcid` + `acpid` +6. `pcid-spawner` runs (`40_pcid-spawner-initfs.service`) +7. `acpid` waits for PCI registration before AML-symbol readiness ### Shared initfs target membership (not strict serialization) Grounded in: +- `recipes/core/base/source/init.initfs.d/40_pcid.service` - `recipes/core/base/source/init.initfs.d/40_hwd.service` +- `recipes/core/base/source/init.initfs.d/41_acpid.service` - `recipes/core/base/source/init.initfs.d/40_pcid-spawner-initfs.service` - `recipes/core/base/source/init.initfs.d/40_ps2d.service` - `recipes/core/base/source/init.initfs.d/40_drivers.target` @@ -216,7 +223,7 @@ Grounded in: - `recipes/core/base/source/init.initfs.d/20_graphics.target` Important nuance: -- `ps2d`, `hwd`, and `pcid-spawner-initfs` all participate in early initfs driver bring-up. +- `ps2d`, `pcid`, `acpid`, `hwd`, and `pcid-spawner-initfs` all participate in early initfs driver bring-up. - They are grouped by `40_drivers.target`, but they are **not** one single strict serial chain. ## 3. What Linux material Red Bear should borrow into Rust diff --git a/local/patches/base/P2-boot-runtime-fixes.patch b/local/patches/base/P2-boot-runtime-fixes.patch new file mode 100644 index 00000000..469fd109 --- /dev/null +++ b/local/patches/base/P2-boot-runtime-fixes.patch @@ -0,0 +1,333 @@ +diff --git a/drivers/hwd/src/backend/acpi.rs b/drivers/hwd/src/backend/acpi.rs +index 3da41d63..5d1a9466 100644 +--- a/drivers/hwd/src/backend/acpi.rs ++++ b/drivers/hwd/src/backend/acpi.rs +@@ -1,27 +1,36 @@ + use amlserde::{AmlSerde, AmlSerdeValue}; +-use std::{error::Error, fs, process::Command}; ++use std::{error::Error, fs}; + + use super::Backend; + + pub struct AcpiBackend { +- rxsdt: Vec, ++ _rxsdt: Vec, + } + + impl Backend for AcpiBackend { + fn new() -> Result> { + let rxsdt = fs::read("/scheme/kernel.acpi/rxsdt")?; + +- // Spawn acpid +- //TODO: pass rxsdt data to acpid? +- #[allow(deprecated, reason = "we can't yet move this to init")] +- daemon::Daemon::spawn(Command::new("acpid")); +- +- Ok(Self { rxsdt }) ++ Ok(Self { _rxsdt: rxsdt }) + } + + fn probe(&mut self) -> Result<(), Box> { ++ let mut boot_critical_input_candidates = 0usize; ++ let mut thc_candidates = 0usize; ++ let mut non_hid_i2c_candidates = 0usize; ++ + // Read symbols from acpi scheme +- let entries = fs::read_dir("/scheme/acpi/symbols")?; ++ let entries = match fs::read_dir("/scheme/acpi/symbols") { ++ Ok(entries) => entries, ++ Err(err) ++ if err.kind() == std::io::ErrorKind::WouldBlock ++ || err.raw_os_error() == Some(11) => ++ { ++ log::debug!("hwd: ACPI symbols are not ready yet"); ++ return Ok(()); ++ } ++ Err(err) => return Err(Box::new(err)), ++ }; + // TODO: Reimplement with getdents? + let symbols_fd = libredox::Fd::open( + "/scheme/acpi/symbols", +@@ -100,12 +109,102 @@ impl Backend for AcpiBackend { + "PNP0C0F" => "PCI interrupt link", + "PNP0C50" => "I2C HID", + "PNP0F13" => "PS/2 port for PS/2-style mouse", ++ "80860F41" | "808622C1" => "DesignWare I2C controller", ++ "AMDI0010" | "AMDI0019" | "AMDI0510" => "AMD laptop I2C controller", ++ "INT33C2" | "INT33C3" | "INT3432" | "INT3433" | "INTC10EF" => { ++ "Intel LPSS/SerialIO I2C controller" ++ } ++ "INT34C5" | "INTC1055" => "Intel GPIO controller", ++ "INTC1050" | "INTC1051" | "INTC1080" | "INTC1081" | "INTC1082" => { ++ "Intel THC companion (QuickI2C/QuickSPI path)" ++ } ++ _ if is_elan_touchpad_id(&id) => "ELAN touchpad (I2C/SMBus path)", ++ _ if is_cypress_touchpad_id(&id) => { ++ "Cypress/Trackpad (non-HID I2C path)" ++ } ++ _ if is_synaptics_rmi_id(&id) => "Synaptics RMI touchpad (I2C/SMBus path)", + _ => "?", + }; + log::debug!("{}: {} ({})", name, id, what); ++ if is_boot_critical_i2c_surface(&id) { ++ boot_critical_input_candidates += 1; ++ log::info!("{}: {} is boot-critical for laptop input path", name, id); ++ } ++ if is_thc_companion(&id) { ++ thc_candidates += 1; ++ log::warn!( ++ "{}: {} indicates Intel THC path; DMA/report fast-path is not complete yet", ++ name, ++ id ++ ); ++ } ++ if is_non_hid_i2c_input_id(&id) { ++ non_hid_i2c_candidates += 1; ++ } + } + } + } ++ ++ if boot_critical_input_candidates == 0 { ++ log::warn!( ++ "hwd: no ACPI boot-critical I2C input candidates found; built-in laptop input may require additional controller/device support" ++ ); ++ } else { ++ log::info!( ++ "hwd: ACPI input candidates: total={} thc={} non_hid_i2c={}", ++ boot_critical_input_candidates, ++ thc_candidates, ++ non_hid_i2c_candidates ++ ); ++ } ++ + Ok(()) + } + } ++ ++fn is_boot_critical_i2c_surface(id: &str) -> bool { ++ matches!( ++ id, ++ "PNP0C50" ++ | "ACPI0C50" ++ | "80860F41" ++ | "808622C1" ++ | "AMDI0010" ++ | "AMDI0019" ++ | "AMDI0510" ++ | "INT33C2" ++ | "INT33C3" ++ | "INT3432" ++ | "INT3433" ++ | "INTC10EF" ++ | "INT34C5" ++ | "INTC1055" ++ | "INTC1050" ++ | "INTC1051" ++ | "INTC1080" ++ | "INTC1081" ++ | "INTC1082" ++ ) || is_elan_touchpad_id(id) ++ || is_cypress_touchpad_id(id) ++ || is_synaptics_rmi_id(id) ++} ++ ++fn is_thc_companion(id: &str) -> bool { ++ matches!(id, "INTC1050" | "INTC1051" | "INTC1080" | "INTC1081" | "INTC1082") ++} ++ ++fn is_elan_touchpad_id(id: &str) -> bool { ++ id.starts_with("ELAN") ++} ++ ++fn is_cypress_touchpad_id(id: &str) -> bool { ++ id.starts_with("CYAP") ++} ++ ++fn is_synaptics_rmi_id(id: &str) -> bool { ++ id.starts_with("SYNA") ++} ++ ++fn is_non_hid_i2c_input_id(id: &str) -> bool { ++ is_elan_touchpad_id(id) || is_cypress_touchpad_id(id) || is_synaptics_rmi_id(id) ++} +diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs +index a968f4d4..4f0aa7f7 100644 +--- a/drivers/pcid-spawner/src/main.rs ++++ b/drivers/pcid-spawner/src/main.rs +@@ -1,4 +1,5 @@ + use std::fs; ++use std::env; + use std::process::Command; + + use anyhow::{anyhow, Context, Result}; +@@ -6,6 +7,38 @@ use anyhow::{anyhow, Context, Result}; + use pcid_interface::config::Config; + use pcid_interface::PciFunctionHandle; + ++fn strict_usb_boot() -> bool { ++ matches!( ++ env::var("REDBEAR_STRICT_USB_BOOT") ++ .ok() ++ .as_deref() ++ .map(str::to_ascii_lowercase) ++ .as_deref(), ++ Some("1" | "true" | "yes" | "on") ++ ) ++} ++ ++fn should_detach_in_initfs(initfs: bool, class: u8, subclass: u8, strict_usb_boot: bool) -> bool { ++ if !initfs { ++ return false; ++ } ++ ++ // Keep storage controller drivers synchronous in initfs so rootfs discovery stays ++ // deterministic. Everything else is non-blocking to avoid wedging boot on optional ++ // hardware paths (GPU, HID companions, etc). ++ if class == 0x01 { ++ return false; ++ } ++ ++ // When explicitly requested, keep USB host controller startup synchronous ++ // for rootfs-on-USB scenarios. ++ if strict_usb_boot && class == 0x0C && subclass == 0x03 { ++ return false; ++ } ++ ++ true ++} ++ + fn main() -> Result<()> { + let mut args = pico_args::Arguments::from_env(); + let initfs = args.contains("--initfs"); +@@ -30,6 +63,7 @@ fn main() -> Result<()> { + } + + let config: Config = toml::from_str(&config_data)?; ++ let strict_usb_boot = strict_usb_boot(); + + for entry in fs::read_dir("/scheme/pci")? { + let entry = entry.context("failed to get entry")?; +@@ -55,10 +89,11 @@ fn main() -> Result<()> { + }; + + let full_device_id = handle.config().func.full_device_id; ++ let device_addr = handle.config().func.addr; + + log::debug!( + "pcid-spawner enumerated: PCI {} {}", +- handle.config().func.addr, ++ device_addr, + full_device_id.display() + ); + +@@ -67,7 +102,7 @@ fn main() -> Result<()> { + .iter() + .find(|driver| driver.match_function(&full_device_id)) + else { +- log::debug!("no driver for {}, continuing", handle.config().func.addr); ++ log::debug!("no driver for {}, continuing", device_addr); + continue; + }; + +@@ -85,16 +120,61 @@ fn main() -> Result<()> { + let mut command = Command::new(program); + command.args(args); + +- log::info!("pcid-spawner: spawn {:?}", command); +- +- handle.enable_device(); ++ log::info!( ++ "pcid-spawner: matched {} to driver {:?}", ++ device_addr, ++ driver.command ++ ); ++ log::info!("pcid-spawner: enabling {} before spawn", device_addr); ++ ++ if let Err(err) = handle.try_enable_device() { ++ log::error!( ++ "pcid-spawner: failed to enable {} before spawn: {}", ++ device_addr, ++ err ++ ); ++ continue; ++ } + + let channel_fd = handle.into_inner_fd(); + command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string()); + ++ log::info!("pcid-spawner: spawn {:?}", command); + #[allow(deprecated, reason = "we can't yet move this to init")] +- daemon::Daemon::spawn(command); +- syscall::close(channel_fd as usize).unwrap(); ++ let spawn_result = ++ if should_detach_in_initfs( ++ initfs, ++ full_device_id.class, ++ full_device_id.subclass, ++ strict_usb_boot, ++ ) { ++ log::warn!( ++ "pcid-spawner: detached initfs spawn for {} to avoid blocking early boot", ++ device_addr ++ ); ++ daemon::Daemon::spawn_detached(command) ++ } else { ++ daemon::Daemon::spawn(command) ++ }; ++ if let Err(err) = spawn_result { ++ log::error!( ++ "pcid-spawner: spawn/readiness failed for {}: {}", ++ device_addr, ++ err ++ ); ++ log::error!( ++ "pcid-spawner: {} remains enabled without a confirmed ready driver", ++ device_addr ++ ); ++ } ++ if let Err(err) = syscall::close(channel_fd as usize) { ++ log::error!( ++ "pcid-spawner: failed to close channel fd {} for {}: {}", ++ channel_fd, ++ device_addr, ++ err ++ ); ++ } + } + + Ok(()) +diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs +index 61cd9a78..a15e5f38 100644 +--- a/drivers/pcid/src/main.rs ++++ b/drivers/pcid/src/main.rs +@@ -12,6 +12,7 @@ use pci_types::{ + }; + use redox_scheme::scheme::register_sync_scheme; + use scheme_utils::Blocking; ++use syscall::{SendFdFlags, sendfd}; + + use crate::cfg_access::Pcie; + use pcid_interface::{FullDeviceId, LegacyInterruptLine, PciBar, PciFunction, PciRom}; +@@ -262,14 +263,13 @@ fn daemon(daemon: daemon::Daemon) -> ! { + let access_fd = socket + .create_this_scheme_fd(0, access_id, syscall::O_RDWR, 0) + .expect("failed to issue this resource"); +- let access_bytes = access_fd.to_ne_bytes(); +- let _ = register_pci +- .call_wo( +- &access_bytes, +- syscall::CallFlags::WRITE | syscall::CallFlags::FD, +- &[], +- ) +- .expect("failed to send pci_fd to acpid"); ++ sendfd( ++ register_pci.raw(), ++ access_fd as usize, ++ SendFdFlags::empty().bits(), ++ 0, ++ ) ++ .expect("failed to send pci_fd to acpid"); + } + Err(err) => { + if err.errno() == libredox::errno::ENODEV { diff --git a/local/recipes/system/redbear-quirks/recipe.toml b/local/recipes/system/redbear-quirks/recipe.toml index 5121fc35..04fbb14c 100644 --- a/local/recipes/system/redbear-quirks/recipe.toml +++ b/local/recipes/system/redbear-quirks/recipe.toml @@ -13,4 +13,5 @@ cp "${COOKBOOK_SOURCE}/quirks.d/30-net.toml" "${COOKBOOK_STAGE}/etc/quirks.d/30- cp "${COOKBOOK_SOURCE}/quirks.d/40-storage.toml" "${COOKBOOK_STAGE}/etc/quirks.d/40-storage.toml" cp "${COOKBOOK_SOURCE}/quirks.d/50-system.toml" "${COOKBOOK_STAGE}/etc/quirks.d/50-system.toml" cp "${COOKBOOK_SOURCE}/quirks.d/60-i2c-hid.toml" "${COOKBOOK_STAGE}/etc/quirks.d/60-i2c-hid.toml" +cp "${COOKBOOK_SOURCE}/quirks.d/70-ucsi.toml" "${COOKBOOK_STAGE}/etc/quirks.d/70-ucsi.toml" """ diff --git a/local/recipes/system/redbear-quirks/source/quirks.d/70-ucsi.toml b/local/recipes/system/redbear-quirks/source/quirks.d/70-ucsi.toml new file mode 100644 index 00000000..567a6a12 --- /dev/null +++ b/local/recipes/system/redbear-quirks/source/quirks.d/70-ucsi.toml @@ -0,0 +1,38 @@ +# ACPI UCSI policy overrides. +# +# ucsid discovers ACPI UCSI devices and tracks I2C-backed ports. This file +# provides platform-specific overrides for boot-policy classification and +# bounded I2C probing. +# +# Matching is exact. At least one match field must be present. +# Multiple matching rules are applied in file order; later rules override +# earlier values for the same key. +# +# Supported match keys: +# sys_vendor, board_vendor, board_name, board_version, +# product_name, product_version, bios_version, +# acpi_path, ucsi_id +# +# Supported action keys: +# input_critical mark this UCSI node as input-critical for boot diagnostics +# probe_i2c override bounded UCSI I2C probing for this node +# probe_dsm_read override bounded ACPI UCSI _DSM function-2 read probe +# +# Global probe default comes from REDBEAR_UCSI_I2C_PROBE. +# Global DSM-read probe default comes from REDBEAR_UCSI_DSM_READ_PROBE. +# Use per-platform rules here to force enable or disable probing. +# +# When `probe_dsm_read` is enabled and succeeds, ucsid records a bounded +# snapshot (`version_bcd`, `cci`) from returned UCSI header bytes when present. +# ucsid also emits per-device `transport_ready` and `transport_readiness_reason` +# so boot consumers can use a single readiness signal instead of recomputing +# policy/capability/probe outcomes. +# +# Example: +# +# [[ucsi_policy]] +# match.sys_vendor = "AMD" +# match.product_name = "Example Laptop 14" +# match.ucsi_id = "AMDI0042" +# input_critical = true +# probe_i2c = true diff --git a/local/scripts/apply-patches.sh b/local/scripts/apply-patches.sh index c125bbcc..f8fef08e 100755 --- a/local/scripts/apply-patches.sh +++ b/local/scripts/apply-patches.sh @@ -69,6 +69,7 @@ done echo "==> Linking recipe patches from local/patches/..." symlink "../../../local/patches/kernel/redox.patch" "recipes/core/kernel/redox.patch" symlink "../../../local/patches/base/redox.patch" "recipes/core/base/redox.patch" +symlink "../../../local/patches/base/P2-boot-runtime-fixes.patch" "recipes/core/base/P2-boot-runtime-fixes.patch" # ── 3. Custom recipe symlinks ────────────────────────────────────── echo "==> Linking custom recipes from local/recipes/..." diff --git a/local/scripts/integrate-redbear.sh b/local/scripts/integrate-redbear.sh index 980c3573..d7d9e8e9 100755 --- a/local/scripts/integrate-redbear.sh +++ b/local/scripts/integrate-redbear.sh @@ -215,6 +215,7 @@ echo "" section "Ensuring recipe patch symlinks..." symlink "../../../local/patches/kernel/redox.patch" "recipes/core/kernel/redox.patch" symlink "../../../local/patches/base/redox.patch" "recipes/core/base/redox.patch" +symlink "../../../local/patches/base/P2-boot-runtime-fixes.patch" "recipes/core/base/P2-boot-runtime-fixes.patch" if [ -d "recipes/core/installer" ]; then symlink "../../../local/patches/installer/redox.patch" "recipes/core/installer/redox.patch" else diff --git a/local/scripts/test-live-mini-uefi.sh b/local/scripts/test-live-mini-uefi.sh new file mode 100755 index 00000000..71d6abf8 --- /dev/null +++ b/local/scripts/test-live-mini-uefi.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +ISO_PATH="${1:-$ROOT_DIR/build/x86_64/redbear-live-mini.iso}" +LOG_PATH="${2:-/tmp/redbear-live-mini-uefi.log}" +TIMEOUT_SECS="${TIMEOUT_SECS:-120}" + +if [[ ! -f "$ISO_PATH" ]]; then + echo "error: ISO not found: $ISO_PATH" >&2 + exit 1 +fi + +CODE_FD="/usr/share/edk2/x64/OVMF_CODE.4m.fd" +VARS_SRC="/usr/share/edk2/x64/OVMF_VARS.4m.fd" +VARS_FD="/tmp/rb-ovmf-vars-live-mini.fd" + +if [[ ! -f "$CODE_FD" || ! -f "$VARS_SRC" ]]; then + echo "error: OVMF files not found under /usr/share/edk2/x64" >&2 + exit 1 +fi + +cp -f "$VARS_SRC" "$VARS_FD" + +ACCEL="tcg" +if [[ -r /dev/kvm && -w /dev/kvm ]]; then + ACCEL="kvm:tcg" +fi + +echo "ISO: $ISO_PATH" +echo "LOG: $LOG_PATH" +echo "ACCEL: $ACCEL" +echo "TIMEOUT:${TIMEOUT_SECS}s" + +timeout "${TIMEOUT_SECS}s" qemu-system-x86_64 \ + -machine "q35,accel=${ACCEL}" \ + -cpu max -smp 4 -m 4096 \ + -nographic -serial mon:stdio \ + -drive "if=pflash,format=raw,readonly=on,file=${CODE_FD}" \ + -drive "if=pflash,format=raw,file=${VARS_FD}" \ + -cdrom "$ISO_PATH" \ + >"$LOG_PATH" 2>&1 || true + +echo "---- markers ----" +grep -nE "RedBear OS starting|switchroot to /scheme/initfs|switchroot to /usr|pcid-spawner: matched 0000:00:01.0|panic|UNHANDLED EXCEPTION|emergency shell" "$LOG_PATH" | sed -n '1,200p' || true + +echo "---- tail ----" +tail -n 80 "$LOG_PATH" | sed -e 's/\x1b\[[0-9;]*m//g' + diff --git a/recipes/core/base/P2-boot-runtime-fixes.patch b/recipes/core/base/P2-boot-runtime-fixes.patch new file mode 120000 index 00000000..8e76040e --- /dev/null +++ b/recipes/core/base/P2-boot-runtime-fixes.patch @@ -0,0 +1 @@ +../../../local/patches/base/P2-boot-runtime-fixes.patch \ No newline at end of file diff --git a/recipes/core/base/recipe.toml b/recipes/core/base/recipe.toml index 6fae20bf..6f00d007 100644 --- a/recipes/core/base/recipe.toml +++ b/recipes/core/base/recipe.toml @@ -1,6 +1,6 @@ [source] git = "https://gitlab.redox-os.org/redox-os/base.git" -patches = ["redox.patch"] +patches = ["redox.patch", "P2-boot-runtime-fixes.patch"] [build] template = "custom" @@ -30,12 +30,15 @@ cp -v \ # Drivers that are built on all architectures, and NOT in drivers-initfs BINS=( gpiod + i2c-gpio-expanderd intel-gpiod amd-mp2-i2cd + dw-acpi-i2cd e1000d ihdad ihdgd i2c-hidd + intel-thc-hidd intel-lpss-i2cd ixgbed pcid @@ -48,6 +51,7 @@ BINS=( usbctl usbhidd usbhubd + ucsid usbscsid virtio-gpud virtio-netd @@ -77,7 +81,7 @@ export CARGO_PROFILE_RELEASE_PANIC=abort $(for bin in "${BINS[@]}"; do echo "-p" "${bin}"; done) for bin in "${BINS[@]}" do - if [[ "${bin}" == "gpiod" || "${bin}" == "intel-gpiod" || "${bin}" == "i2cd" || "${bin}" == "inputd" || "${bin}" == "pcid" || "${bin}" == "pcid-spawner" || "${bin}" == "redoxerd" ]]; then + if [[ "${bin}" == "gpiod" || "${bin}" == "i2c-gpio-expanderd" || "${bin}" == "intel-gpiod" || "${bin}" == "i2cd" || "${bin}" == "dw-acpi-i2cd" || "${bin}" == "i2c-hidd" || "${bin}" == "inputd" || "${bin}" == "pcid" || "${bin}" == "pcid-spawner" || "${bin}" == "redoxerd" || "${bin}" == "ucsid" ]]; then cp -v "target/${TARGET}/${build_type}/${bin}" "${COOKBOOK_STAGE}/usr/bin" else cp -v "target/${TARGET}/${build_type}/${bin}" "${COOKBOOK_STAGE}/usr/lib/drivers"