Files
bfe-core1106-sdk/README.md
T
BFE EngineeringandClaude Fable 5 2756de0b46 review: iteration-1 fixes across CI, bridge, VM harness, and docs
CI/pipeline:
- KERNEL_TARBALL passed as a YAML env literal '~' was never tilde-expanded
  and would have failed every hosted kernel-build dispatch; the path is now
  exported from the shell. Verified reproducible before the fix.
- Every job gets timeout-minutes; boot smoke uses timeout -k so a wedged
  qemu is SIGKILLed instead of holding the job.
- Tarball fetch + fail-closed sha256 verification deduplicated into
  build/fetch-kernel-tarball.sh (with curl retries), used by build-kernel.sh
  and both CI jobs. busybox fetch gains retries too.
- ccache layer for kernel-build (cache keyed on defconfig+patches) recovers
  the incremental-compile speed the ephemeral-runner move cost.
- build-kernel.sh now asserts every fragment option survived olddefconfig —
  merge_config -m pastes text and Kconfig silently drops unmet symbols.

rs485-bridge:
- pending-buffer cap (2x max RTU ADU) instead of unbounded growth;
  explicit accept-loop error handling with backoff instead of .flatten();
  per-arm inline bounds instead of the string-keyed lookup whose default
  would have mis-bounded a future get-input; control-socket cleanup errors
  surfaced; flag-shaped values rejected in arg parsing; doc example uses a
  private mktemp dir. Test timing margins widened for contended runners
  (gap 25->120ms, 60x margin on the split-frame test).

VM harness:
- stage-1/stage-2 boot scripts share one validated slot parser and one
  by-name populator (qemu/rootfs/etc/warden-lib.sh) — the duplicated
  parser had already diverged on validation; userdata/oem mount failures
  now fail fast with a greppable sentinel; udhcpc fallback keys off the
  interface actually having an address; switch_root applet guarded.
- boot-smoke delegates the qemu invocation to run.sh (machine shape lives
  in ONE place); run.sh port 0 disables a hostfwd.
- mkimage: unknown partition names fail at build time; DISK_END is a max,
  not last-entry; --state keys validated as filenames.
- portal-scenario: mock readiness is asserted (no silent fall-through),
  hostfwd port collisions retried, mount-failure sentinel fails fast.
- ui-shot: fixed sleeps replaced with bounded screendump polling; the
  repaint assertion is real and documented as such. qmp.py loses its
  module-global and gains argv validation.

Docs/scrub: bench-host paths and the site AP name removed from six more
port docs and two evidence tables; path-bearing build artifacts (.elf,
.map) untracked (the 154-byte firmware .bin is path-free and stays);
ADR-0003 marked visibility-superseded by ADR-0007; stale section
cross-reference fixed; flare-edge noted as private for outside readers;
stale root-level review report removed per the new workspace rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018HUayid7W5w7jBdb9Rrj1K
2026-08-30 08:19:17 -06:00

7.6 KiB

warden-sdk

ci Lines of code Tests Coverage

The build, driver, and simulation SDK for WardenOS (the Luckfox Pico 86-Panel / RV1106 HMI). A from-scratch replacement for the twice-ported vendor stack (Rockchip SDK → Luckfox SDK → our patched fork), built to the same standard as the rest of the firmware: tested, benchmarked, reproducible, and honest about what runs on real silicon versus what we simulate.

Status: bringup. The hardware simulator and its tests, the RV1106 kernel forward-port as a reviewable patches/ series, the hermetic kernel build, two Tier-1 drivers at 100% MC/DC, and the QEMU device sim (qemu/, ADR-0006: boots the real kernel + real userspace on -M virt — check-in/OTA against the mock portal, watchdog, RS485-to-sim bridge, 720x720 display + touch, all emulation-verified) are in. What remains before this is on the production build path: having flare-edge consume warden-sdk as a dependency (maintainer-gated). Until then, flare-edge still builds firmware from the vendored SDK + sdk-patches/.

Why a new SDK

The vendored SDK is a ~2 GB opaque fork of a fork. Our real changes to it lived, until recently, as uncommitted edits in one working copy (flare-edge/sdk-patches/ is the tracked form). It bakes absolute paths, needs python (not python3), silently drops Kconfig options, and — the failure that motivated this repo — gives us no way to test hardware-dependent code off the device. Every driver change had to be validated by flashing a panel. That is slow, and it is dangerous: it is how a boot-loaded-watchdog change bricked a bench unit (the load address collided with unreserved kernel RAM — a mistake a target-config check or a memory-map model would have caught before any flash).

The SDK's job is to make the firmware buildable, testable, and hardenable without a panel in the loop, and to move us onto a modern, maintained kernel.

Goals (from future-features)

  1. Modern kernel. A self-built Linux 6.18.46, forward-ported directly from the vendor 5.10.160 tree (no plan44/OpenWrt code) on our current Buildroot LTS (2025.02.x). This is done and hardware-verified on warden-c8a3: essentially every RV1106 block the 86-Panel uses boots and works — clk, pinctrl, eMMC, GMAC, TRNG, OTP, SARADC/TSADC, RTC, USB host, PWM/backlight, VOP display, GT911 touch, AIC8800 wifi, RGA, I2S audio, HPMCU mailbox, the open NPU driver, and PVTM. Mainline was not viable (no DT/clk/display/RGA/NPU/ flash-boot upstream for RV1106); the direct 5.10→6.18 forward-port reuses the already-in-mainline rv1126 register data where it matches and carries our deltas as a reviewable patch series (patches/).
  2. Ported, hardened drivers → 100% MC/DC on the code we own. "100% MC/DC on 100% of drivers" is infeasible as literally stated: ~97% of driver LOC is vendor blobs (the AIC8800 wifi driver alone is 88.5K lines). So the target is tiered: real MC/DC on our hardware code (modbus master, relays, RGA wrapper, HPMCU supervisor, devmem/reset ladder); fault-injection + branch hardening for the vendor blobs behind a stable seam.
  3. A proper simulator. Simulate the hardware the vendor SDK cannot: RGA (2D blitter), the RISC-V HPMCU coprocessor, and the NPU — plus the register/SRAM (/dev/mem) and sysfs surfaces the drivers touch — so driver and supervisor logic runs and is tested on the host, in CI, with no panel.
  4. Its own repo, held to firmware standards. Tests, benchmarks, reproducible builds, CI. This repo.

Architecture — one seam, two backends

The organizing idea is a thin Hardware Abstraction Seam per hardware block. Firmware code talks to the seam (a trait in Rust, a function table in C); the seam has two backends:

        firmware / driver logic
                  │
          Hardware Abstraction Seam        (devmem, hpmcu, rga, npu, modbus, gpio)
             ┌────┴────┐
        real backend   sim backend
      (/dev/mem, ioctl, (software model,
       /proc, serial)    host-testable)
  • On-device, the seam binds the real backend (mmap /dev/mem, librga ioctls, the serial port, /proc/rknpu).
  • On the host, it binds the sim backend — a faithful software model of the block. The HPMCU sim, for example, runs the SCR1 watchdog firmware's exact state machine (boot-grace, heartbeat-timeout, fire) against an in-memory mailbox, so the flared supervisor's arm/beat protocol is exercised end-to-end in a unit test.

The seam is the same object the driver-hardening effort measures MC/DC against, and the same object the simulator implements — so the two goals reinforce rather than duplicate each other.

Layout

sim/        the hardware simulator (Rust): membus/devmem, HPMCU, CRU, Modbus, RGA, NPU.
qemu/       the device simulator (ADR-0006): QEMU -M virt boots the real kernel and
            real userspace; A/B disk layout, RS485 bridge into sim/, scenario tests.
drivers/    our own hardened drivers + their seams (relays, freshness; more migrate in).
patches/    the RV1106 kernel forward-port delta onto pristine linux-6.18.46 (subsystem-split).
kernel/     forward-port docs + provenance (rv1106-enablement/, PROVENANCE.md).
build/      the hermetic kernel build (fetch pristine → apply patches → zImage + dtb).
docs/       architecture + ADRs (decisions/) + ci-cd + generated workflow flowcharts.
tools/      dev tooling. config-lint: static target-config gates (MCU-load-vs-reserved-memory,
            the 0x40000 brick class); flowgen: the workflow-flowchart generator.
.github/    CI (workflows/ci.yml): patches-apply, host tests, coverage, MC/DC, benchmarks, badges.

Principles

Evaluated against the stack philosophy — openness, hardness, modernness:

  • Open over closed where we can: rkdeveloptool over the closed upgrade_tool; source-buildable librga over blobs where a source path exists; the simulator is fully open and ours.
  • Hard: every seam has a fault-injection path (a wedged SDIO link, a stalled MCU, an RGA timeout) so recovery code is tested against failure, not just success. On-device claims still need on-device evidence; the sim narrows which claims need a panel, it does not replace that rule.
  • Modern: newest kernel we can actually run; current Buildroot LTS; Rust for new host-testable code; reproducible builds.

Relationship to flare-edge

flare-edge (WardenOS: the LVGL UI + the flared daemon) is the product; warden-sdk is what builds and tests it. flare-edge is BlueFlare's private companion repo — not publicly available — so flare-edge issue references and checkout paths in this repo's docs are context, not reachable links. During bootstrap, flare-edge consumes warden-sdk piece by piece: first the simulator (as a dev/test dependency), later the image build. No flare-edge code moves here — only the SDK/build/sim/driver-seam layer.

License

GPL-2.0-only, repo-wide (see LICENSE; a per-file SPDX identifier governs where one is present, e.g. a few GPL-2.0-or-later kernel files). The kernel material in patches/ and kernel/rv1106-enablement/ is derivative of the Linux kernel and of GPL-2.0 vendor code either way — per-driver origin and license are tracked in kernel/rv1106-enablement/PROVENANCE.md. Contributions are accepted under the same license (inbound = outbound).