From 7f10d21b5936c0b3f567f53c41ec28f2383e6615 Mon Sep 17 00:00:00 2001 From: "Michael C. Ferguson" Date: Sat, 22 Aug 2026 23:50:05 -0500 Subject: [PATCH 1/4] =?UTF-8?q?docs(de25):=20D0=20recon=20dossiers=20?= =?UTF-8?q?=E2=80=94=20boot=20chain,=20FPGA=20reconfig,=20patch=20portabil?= =?UTF-8?q?ity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase D0 of the DE25-Nano readiness plan, executed as three multi-agent runs. Desk research only: no hardware, nothing built. de25-boot-chain.md (D0.1) closes §8's open questions. The SDM cannot boot from the microSD on this board [V], so the QSPI seam is permanent rather than an artifact of missing documentation. All DDR/pinmux handoff lives inside the QSPI bitstream [V]. saveenv writes back to wherever the env loaded from, so a FAT miss lands in QSPI [V]. Terasic publish the factory QSPI image, which was downloaded and hashed (golden_top_hps.jic, 16,777,447 B, sha256 e3d20c2d…b38a4). Q2/Q3/Q4 are parked with named blockers, handed to D2.2. §7's brick inventory went through a three-lens adversarial pass that amended 8 of 22 claims and added 7 new vectors; §9 records what survived and what was merely never challenged. One risk stands unmitigated: "no release writes QSPI" is enforced by prose only — git grep finds no board-identity assertion anywhere outside docs/, so ADR 0027 Decision 4 remains unimplemented. de25-fpga-reconfig.md (D0.2) confirms DP-9: fpga-region/DT-overlay is the Agilex-native idiom, so the UIO doorbell patches 0043-0045 are not ported. de25-patch-portability.md (D0.3) classifies all 40 unique patches. Run triage-first on owner direction rather than as the flat per-patch sweep the task list specified — risk-rate everything, deep-dive only the DE10/Cyclone-V set — which cut it from ~55 agents to ~20. Adds a target-series verdict per patch (shared / de10-only / de25-only) that the original task did not ask for, serving the goal of one repo building both boards off a shared base. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FYjrnzEEu4HzJSPWNAaRvm --- docs/de25-boot-chain.md | 774 +++++++++++++++++++++++----- docs/de25-fpga-reconfig.md | 903 +++++++++++++++++++++++++++++++++ docs/de25-patch-portability.md | 731 ++++++++++++++++++++++++++ 3 files changed, 2284 insertions(+), 124 deletions(-) create mode 100644 docs/de25-fpga-reconfig.md create mode 100644 docs/de25-patch-portability.md diff --git a/docs/de25-boot-chain.md b/docs/de25-boot-chain.md index c780edc..31f1a76 100644 --- a/docs/de25-boot-chain.md +++ b/docs/de25-boot-chain.md @@ -1,166 +1,692 @@ # DE25-Nano boot chain — QSPI, SD, and the user experience -**Status:** desk-research first pass, 2026-08-19 — no hardware touched. This is a -*partial* result of task D0.1 ([`de25-nano-tasks.md`](de25-nano-tasks.md)); §8 lists -what remains, and every load-bearing claim here must still survive D0.1's adversarial -verify and D2.2's on-hardware test before any flash-path code trusts it. Claims are -tagged **[V]** (verified against a named source) / **[U]** (unverified). Cross-refs: -[`de25-nano-plan.md`](de25-nano-plan.md) §4.1, -[ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md), and — for the DE10 -baseline this is contrasted against — [`boot-chain.md`](boot-chain.md). - -**Sources** (retrieved 2026-08-19): +**Status:** desk research **complete** for D0.1 — synthesis pass 2026-08-21, adversarial +refutation pass 2026-08-21, **no hardware touched**. The six §8 questions are each either +resolved to **[V]** or explicitly parked with a named blocker and an inheriting task. §9 records +which claims survived refutation, which were amended, which remain contested, and which were +never challenged — read it before trusting any single row. What remains before flash-path code +runs on a real board: D2.2's first-contact hardware tests, in the order §6 fixes. Claims are +tagged **[V]** (verified against a named source) / **[U]** (unverified, with what is missing +named). Cross-refs: [`de25-nano-plan.md`](de25-nano-plan.md) §4.1, +[ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md), and — for the DE10 baseline this +is contrasted against — [`boot-chain.md`](boot-chain.md). + +> **Three facts shaped this document and are carried through §1–§7, not just §8:** (a) the QSPI +> seam is **confirmed real** — the SDM cannot boot this board from the microSD socket (§8.1); +> (b) **RSU does not fit** the DE25's 16 MB flash at the vendor's own reference sizing (§8.4), so +> there is no *demonstrated* power-loss-safe QSPI update path on this board and QSPI writes are +> ruled out of anything shipped — as **policy chosen fail-closed**, not as a proven impossibility; +> (c) mainline U-Boot's environment can silently land **in QSPI** (§8.5) — §7 row 5. +> +> **And one fact the refutation pass added, which changes §5 and §6 materially:** Terasic **does** +> publish the factory QSPI image. `golden_top_hps.jic` ships inside the downloadable Resource +> Package, and the Getting Started Guide documents the restore procedure. The first pass's +> "no image exists to write" premise was wrong — it had never been checked by opening the archive. + +**Sources.** + +Retrieved 2026-08-19: - Terasic **DE25-Nano User Manual**, rev. 2025-09-05 (51 pp., via DigiKey mirror - `mm.digikey.com/...P0804.pdf`) — cited below as *UM* §/page. + `mm.digikey.com/…/P0804.pdf`) — cited as *UM* §/page. Re-read pp. 11–12, 41–44 on 2026-08-21; + text re-extracted locally from the PDF this pass. - Altera **Agilex 5 E-series GHRD Linux boot examples**, rel-25.1 - (`altera-fpga.github.io/rel-25.1/.../ug-linux-boot-agx5e-premium/`) — cited as *GHRD*. -- Intel **Agilex 5 SoC FPGA Boot Overview** (doc 813762) and **Agilex SoC Boot / - Configuration User Guides** (cdrdv2 667140, 704696) — cited as *Intel-boot* / - *Intel-cfg*. -- RocketBoards **Building Bootloader for Agilex 5**. + (`altera-fpga.github.io/rel-25.1/…/ug-linux-boot-agx5e-premium/`) — cited as *GHRD*. +- RocketBoards **Building Bootloader for Agilex 5** — cited as *RB-boot*. Re-read 2026-08-21. + +Added 2026-08-21 (synthesis pass): +- Terasic **DE25-Nano resource page**, `terasic.com.tw/cgi-bin/page/archive.pl?…No=1384&PartNo=4` + (the legacy `de25-nano.terasic.com/cd/` URL 302s here) — cited as *T-res*. Note: on the + refutation pass this host was TLS-unreachable; the download list was re-verified via the + `dl2.terasic.com/resources/de25-nano/` index instead. +- **"DE25 Nano — Build Linux image from scratch"**, `github.com/johnnyfan1979/public_doc`, + linked from *T-res* — community-authored, **not** a Terasic statement — cited as *T-guide*. + Its FSBL claim is now superseded by direct inspection of the vendor artifacts (below). +- Altera **HPS Remote System Update example, Agilex 5 E-series**, rel-25.1 + (`altera-fpga.github.io/rel-25.1/embedded-designs/agilex-5/e-series/premium/rsu/ug-rsu-agx5e-soc/`) + — cited as *RSU-ex*. +- Mainline **U-Boot** at `master`: `configs/socfpga_agilex5_defconfig`, `common/spl/Kconfig`, + `arch/arm/mach-socfpga/spl_soc64.c`, `env/env.c` — cited as *u-boot:path*. +- Local kernel tree `output/build/linux-6.18.44` — cited as `linux:path:line`. **Every kernel + cite in this document was re-checked against that tree on 2026-08-21**; the ones that did not + hold are recorded in §9.2. +- Altera **Agilex 5 Device Configuration User Guide** (813773) — **TOC level only**; section + bodies on `intel.com` returned **HTTP 403**. + +Added 2026-08-21 (refutation pass — vendor artifacts, opened and inspected, not merely listed): +- **`DE25-Nano_revA_v.1.0.0_ResourcePackage.zip`** (183,940,156 B, from + `dl2.terasic.com/resources/de25-nano/`) — cited as *RP-A*. Contents referenced here were + re-extracted and re-hashed by this pass from the local copy; the download itself was performed + by a refuter leg, not by this pass. +- **`DE25_Nano_Getting_Started_Guide.pdf`** (footer date September 11, 2025), inside *RP-A* — + cited as *GSG*. Text extracted locally 2026-08-21. +- The factory SPL, carved from *RP-A*'s `…/GHRD/software/u-boot/spl/u-boot-spl-dtb.hex` and + decompiled — cited as *SPL-dtb*. Model string: `SoCFPGA Agilex5 Terasic DE25-Nano`. + +**Sources we could not obtain** (recorded so nobody re-walks the dead ends): Intel doc +**813762 / 813763** (Agilex 5 boot overview / HPS booting UG) — 403 on `cdrdv2-public.intel.com` +and `intel.com`, on independent attempts across both passes; the Agilex 5 **Reset Manager** +pages (814346, 786901) — 403; **Intel 852610** (Agilex 5 quad-SPI flash layout) — 403, and it is +the document where an RSU *minimum* layout would live, which is why §8.4's residual stays open. +The first-pass version of this doc cited *Intel-boot* (813762) and *Intel-cfg* (667140, 704696); +those citations are **retained only where a second, retrievable source carries the same claim**, +and are otherwise downgraded to [U]. Nothing here is quoted from a document we did not open. --- ## 1. The headline answer -**Routine use never writes QSPI [V].** The QSPI ships factory-programmed (*UM* §3.1), -and in the HPS-first flow everything a user or a release touches — U-Boot proper, ATF -BL31, the fabric bitstream, kernel, rootfs — lives on the SD card (*GHRD*). Burning a -fresh SD card and inserting it is the whole workflow; the DIP switches stay at factory -default. New-card UX therefore matches the DE10. +**The seam is real and it is permanent [V].** The first two boot-chain links — SDM firmware and +the FSBL — live in the board's 16 MB QSPI and are board-resident; everything after lives on the +SD card and changes per release. The escape hatch the first pass hoped for (SDM boots phase-1 + +FSBL straight off the microSD, everything-on-card, full DE10 parity) **does not exist on this +board**: the microSD socket is wired to the *HPS* SD/MMC controller, not to SDM_IO, and *UM* +Table 3-2 documents no SD/MMC MSEL scheme (§8.1). This is board-gated, not merely undocumented. + +**Routine use never writes QSPI — and nothing we ship is permitted to, by policy [V for the +default flow; the rule is policy, not a mechanism].** The QSPI ships factory-programmed (*UM* +§3.1, *GSG*); in the HPS-first flow everything a user or a release touches — U-Boot proper, ATF +BL31, the fabric bitstream, kernel, rootfs — lives on the SD card (*GHRD*). Burning a fresh SD +card and inserting it is the whole workflow; the DIP switches stay at factory default; +new-card UX matches the DE10. **But "never writes QSPI" is a rule we must enforce, not a +property of the board**: mainline U-Boot's `socfpga_agilex5` build compiles in a *UBI* +environment fallback that resolves to QSPI (§8.5, §7 row 5); U-Boot's `mtd`/`ubi` commands are +in that defconfig (§7 row 11); Linux can drive the same flash as an ordinary MTD device (§7 +row 10); and Terasic's own Resource Package ships one-click `.bat` files that erase and +reprogram it (§7 row 13). As of 2026-08-21 the rule is **enforced by documentation only** — see +§5 and §9.3. -What the DE10 never had is a **seam**: the first two boot-chain links (SDM firmware + -FSBL) live in QSPI, board-resident, while everything after lives on the card and -changes per release. §4–§5 are about managing that seam; it is a *project-side* -decision made once, not a user-facing per-card or per-release event. +**There is no *demonstrated* safe way to write QSPI in the field [V for the sizing datum; +[U] for impossibility].** RSU — Altera's power-loss-safe QSPI update framework, the thing §6 was +going to lean on — is sized by Altera's own Agilex 5 reference example at a **2 Gbit (256 MB)** +flash with three **16 MB** application slots. One slot is the DE25-Nano's entire flash (§8.4). +No sub-16 MB layout has been demonstrated or documented, and Intel's flash-layout UG (852610) is +403-blocked. So the §5 rule is: **nothing we ship writes QSPI**, adopted fail-closed. Posture 2 +(§4) survives only as a one-time bench operation with a PC and JTAG attached, never as an +update-channel artifact. + +**Recovery exists, is documented by the vendor, and needs a PC [V].** MSEL→`111` (JTAG) + +Quartus Programmer over the on-board USB-Blaster III, programming `golden_top_hps.jic` from the +Resource Package — *GSG* documents exactly this. What it needs: a PC with Quartus, the **correct +board revision's** package, and an archived copy (vendor URLs rot). What is still [U]: that the +published JIC matches, or boots, any given physical board (§6, D2.2). ## 2. Boot chain, link by link -1. **Power-on → SDM** (Secure Device Manager — hard microcontroller, boots first) - **[V Intel-boot]**. MSEL[2:0] selects the configuration source (§3). -2. **SDM loads from QSPI** (AS-Fast, the board default): its own firmware plus the - *phase-1* "HPS-first" bitstream — HPS pin/DDR configuration only, with the **FSBL - embedded** (U-Boot SPL or ATF BL2) **[V GHRD, Intel-boot]**. SDM configures HPS - SDRAM pins, places the FSBL in OCRAM, releases the HPS **[V Intel-boot]**. -3. **FSBL initializes DDR, reads the SD card**: loads `u-boot.itb` — a FIT carrying - ATF **BL31** + U-Boot proper + DTB — from the FAT partition **[V GHRD]**. From here - the chain is card-resident. -4. **U-Boot loads the phase-2 fabric bitstream** (`core.rbf`, `fpga load`) and the - kernel from the same FAT partition; boots Linux from the rootfs partition - **[V GHRD]**. (A U-Boot-less "ATF-to-Linux" variant exists in *GHRD*; noted, not - pursued.) -5. **Linux reconfigures the fabric at runtime** via the SDM mailbox — the - `stratix10-soc` FPGA manager + DT overlays (the eventual core-switching path; - latency dossier is task D0.2) **[V driver exists / U latency]**. - -**No 0xA2 analogue [V].** The DE10's raw-partition-scanned-by-BootROM mechanism does -not exist here; the FSBL finds `u-boot.itb` **by name on the FAT partition** (*GHRD*). -The partition-order lore in [`boot-chain.md`](boot-chain.md) §2 does not transfer. +1. **Power-on → SDM** (Secure Device Manager — hard microcontroller, boots first). + MSEL[2:0] selects the configuration source (§4) **[V UM Table 3-2 / U for the SDM-internal + sequence: doc 813762 unobtainable]**. +2. **SDM loads from QSPI** (AS Fast, the board default): its own firmware plus the *phase-1* + "HPS-first" bitstream — HPS pin/DDR configuration only, with the **FSBL embedded** + **[V GHRD, RB-boot]**. On the DE25-Nano that FSBL is **U-Boot SPL**, not ATF BL2 — now + verified against the vendor's own artifact rather than the community guide: *RP-A* ships + `…/GHRD/software/u-boot/spl/u-boot-spl-dtb.hex` (721,613 B) beside the JIC, and the DTB + carved from it identifies as `model = "SoCFPGA Agilex5 Terasic DE25-Nano"` **[V RP-A, + SPL-dtb]**. BL31 is folded into `u-boot.itb` instead. *(This supersedes the "U-Boot SPL or + ATF BL2" hedge in the first pass and the "ATF BL2 as FSBL" row in + [`de25-nano-plan.md`](de25-nano-plan.md) §1, which should be corrected there.)* +3. **All DDR/pinmux handoff data rides inside that QSPI bitstream [V RB-boot]:** *"For Agilex 5, + all the handoff information created by the Quartus compilation is part of the configuration + bitstream. The bsp-editor is not used, and the bootloader build flow does not depend on the + Quartus outputs."* There is no DE10-style QTS handoff header compiled into a preloader we + build. DDR is **QSPI-owned**; our build cannot change it (§8.6). +4. **FSBL initializes DDR, reads the SD card**: loads `u-boot.itb` — a FIT carrying ATF **BL31** + + U-Boot proper + DTB — from the FAT partition **[V GHRD]**. Structurally this is mainline + behaviour: `SPL_LOAD_FIT=y` makes `SPL_FS_LOAD_PAYLOAD_NAME` default to `"u-boot.itb"` + (*u-boot:common/spl/Kconfig*), the FS-boot partition is `SYS_MMCSD_FS_BOOT_PARTITION` + (default 1, same file), `spl_boot_mode()` returns `MMCSD_MODE_FS` when `SPL_FS_FAT` is set, + and `board_boot_order()` honours `/chosen`'s `u-boot,spl-boot-order` + (*u-boot:arch/arm/mach-socfpga/spl_soc64.c*) **[V]**. **The factory SPL's actual boot order is + now known at the desk**: `u-boot,spl-boot-order = "/soc/mmc0@10808000", + "/soc/spi@108d2000/flash@0", "/soc/nand@10b80000", "/memory"` (*SPL-dtb* line 429) — SD first, + then QSPI, then NAND **[V]** — though that same DTB leaves the QSPI node `status = "disabled"` + (*SPL-dtb* line 225), so the QSPI fallback should not in fact probe **[V, inspection]**. + From here the chain is card-resident. +5. **U-Boot loads the phase-2 fabric bitstream** (`core.rbf`, `fpga load`) and the kernel from + the same FAT partition; boots Linux from the rootfs partition **[V GHRD]**. (A U-Boot-less + "ATF-to-Linux" variant exists in *GHRD*; noted, not pursued.) **Open, and possibly + project-shaping:** whether a phase-2 `core.rbf` from *our* Quartus compilation may be paired + with Terasic's factory phase-1 — see §7 row 15. +6. **Linux reconfigures the fabric at runtime** via the SDM mailbox — `stratix10-soc` FPGA + manager + DT overlays, an explicit `COMMAND_RECONFIG` request, full or partial + (`linux:include/linux/firmware/intel/stratix10-svc-client.h:145-151`, + `linux:drivers/fpga/stratix10-soc.c:195`) — the eventual core-switching path; latency dossier + is D0.2, delivered as [`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) (the SMC/SDM path end + to end, the DT and Kconfig a DE25 must author, the VAB unknown, and the HPS↔FPGA memory + contract) **[V driver exists / U latency]**. **Caveat found this pass:** mainline + `linux:arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi` (826 lines) contains **no + `firmware { svc { … } }` node at all** — only the `service_reserved: svcbuffer@0` region at + `:23` — whereas `linux:arch/arm64/boot/dts/intel/socfpga_agilex.dtsi:63-72` instantiates + `intel,agilex-svc` + `intel,agilex-soc-fpga-mgr`. The driver match table accepts + `intel,agilex-svc` (`linux:drivers/firmware/stratix10-svc.c:1133-1135`), so the code is + present but **nothing probes it on Agilex 5 without DTS we write ourselves** (DP-9 work) + **[V]**. + +**No 0xA2 analogue [V].** The DE10's raw-partition-scanned-by-BootROM mechanism does not exist +here; the FSBL finds `u-boot.itb` **by name on a FAT filesystem** (*GHRD*, and the mainline +Kconfig defaults above). The partition-order lore in [`boot-chain.md`](boot-chain.md) §2 does +not transfer. ## 3. What lives where | Artifact | Location | Owner / change cadence | |---|---|---| | SDM firmware | QSPI | Altera toolchain version; effectively never (posture §4) | -| Phase-1 HPS bitstream (pin/DDR handoff) | QSPI | board vendor design; effectively never | -| FSBL (U-Boot SPL / ATF BL2) | QSPI (embedded in phase-1 image) | same | -| `u-boot.itb` (ATF BL31 + U-Boot + DTB) | SD, FAT partition | **ours, per release** | -| Phase-2 fabric bitstream (`core.rbf`) | SD, FAT | ours / eventually per-core | +| Phase-1 HPS bitstream **incl. all DDR/pinmux handoff** | QSPI | board vendor design; effectively never; **not overridable from our build [V RB-boot]** | +| FSBL = **U-Boot SPL** (`u-boot-spl-dtb.hex`) | QSPI (embedded in the phase-1 JIC) | same **[V RP-A, SPL-dtb]** | +| `u-boot.itb` (ATF BL31 + U-Boot + DTB) | SD, FAT partition 1 | **ours, per release** | +| U-Boot environment | SD FAT (`uboot.env`, `mmc0:1`) — **but a UBI fallback in QSPI is compiled in by default** | ours; the fallback is a §7 hazard, not a feature **[V u-boot defconfig + RB-boot log]** | +| Phase-2 fabric bitstream (`core.rbf`) | SD, FAT | ours / eventually per-core — **pairing with phase-1 unproven, §7 row 15** | | Kernel (`Image`/FIT), DTB | SD, FAT | ours, per release | | Rootfs | SD | ours, per release | -Board facts (*UM* p.9, §3.8.4): 128 Mbit (16 MB) QSPI, ASx4; **USB-Blaster III -on-board** (USB-C); 1 GB LPDDR4 on HPS "shared with FPGA"; microSD socket wired to the -**HPS**, described as "not only … external storage for the HPS but also … an -alternative boot option" — one ambiguous sentence, see §8-Q1. +Board facts (*UM* p.9, §3.2, §3.8.4): 128 Mbit (**16 MB**) QSPI, ASx4 — the part is named in +Terasic's own programming file as `SEC_Device(MT25QU128)`, on device `A5EB013BB23BE4SR1` +**[V RP-A `…/HDMI_ASx4/demo_batch/flash.cdf`]**; **USB-Blaster III on-board** (USB-C); 1 GB +LPDDR4 on HPS "shared with FPGA" — the factory SPL DTB's memory node reads +`reg = <0x0 0x80000000 0x0 0x40000000>` = 1 GiB at 0x8000_0000 **[V SPL-dtb]**; microSD socket +wired to the HPS. *UM* §3.8.4's "not only … external storage for the HPS but also … an +alternative boot option" is **resolved**: it means the FSBL/HPS reading `u-boot.itb` (step 4), +not a second SDM path (§8.1). + +**OPN discrepancy, unresolved and worth carrying [U]:** *UM* p.8 prints `A5EB013BB23BE4SR1` +(twice), the demo `flash.cdf` targets `A5EB013BB23BE4SR1`, and the Terasic BSP build record says +`A5EB013BB23BE4SCS` **[V, all three read directly]**. Which OPN a given board carries is a +first-contact observation for D2.2 (Quartus auto-detect over JTAG), and it matters at exactly +one moment: choosing which JIC to program (§7 row 14). ## 4. MSEL and the "is QSPI writing required?" decision -*UM* Table 3-2 documents exactly **two** configuration schemes **[V]**: +*UM* Table 3-2 (p.11) documents exactly **two** configuration schemes; SW5 is a 4-pin DIP with +only MSEL0–2 wired (SW5.4 = N/A) **[V, UM text re-extracted 2026-08-21]**: | MSEL[2:0] | Scheme | Meaning | |---|---|---| | `001` | AS Fast | FPGA configured from QSPI Flash (**factory default**, pre-programmed) | | `111` | JTAG | configure via on-board USB-Blaster III | -No SD/MMC scheme is documented for this board, although Agilex 5 silicon has an SD/MMC -active-configuration controller (*Intel-cfg*) — §8-Q1. - -Three postures for the QSPI-resident links, one to be chosen (this graduates with the -DP-1 ADR at D2.7; the recommendation below is not yet a decision): - -1. **Pin to factory QSPI; never write it** — *recommended for v1*; the exact analogue - of the DE10 "stock `uboot.img`, byte-identical" posture. Our SD payload must be - loadable by Terasic's shipped FSBL: their SPL must find and boot our mainline - `u-boot.itb`. The name+location contract is the stable interface **[V GHRD]**; - whether their specific SPL build honors it for our FIT is **[U → D2.2 first test]**. - If it works: pure DE10 UX forever; we record the factory QSPI version we tested - against (§5). -2. **Ship our own QSPI image** (SDM fw + phase-1 + our FSBL): full control of the - SPL/DDR handoff, at the cost of a **one-time QSPI flash per board** at onboarding — - a real gotcha the DE10 never had. If ever forced here, updates go through **RSU** - (§6), never raw writes. -3. **Hybrid**: start at 1; fall back to 2 only if the factory FSBL proves - incompatible, shipping the QSPI update RSU-protected as a documented one-time step. +There is **no SD/MMC configuration scheme** for this board, and — unlike the first pass's +assertion — we no longer claim Agilex 5 silicon has one: the Agilex 5 Device Configuration UG +(813773) lists AVST, AS, JTAG and CvP at TOC level with no SD/MMC section, and the +original-generation Agilex guide that does document SD/MMC is a **different device family** +**[U on the silicon question — 813773's body is 403-blocked; see §8.1]**. The board-level answer +does not depend on it. + +Three postures for the QSPI-resident links, one to be chosen (this graduates with the DP-1 ADR +at D2.7; the recommendation below is not yet a decision): + +1. **Pin to factory QSPI; never write it** — **recommended for v1, and chosen fail-closed, not + forced.** *(Amended: the first synthesis called it "close to forced" on the strength of an + impossibility that is not proven — see §9.2.)* The exact analogue of the DE10 "stock + `uboot.img`, byte-identical" posture. Our SD payload must be loadable by Terasic's shipped + SPL. The name+location contract is the stable interface **[V GHRD, u-boot Kconfig]**; whether + *their* SPL build accepts *our* FIT is **[U → D2.2 first test]**. The sharpest sub-risk was + **FIT signature enforcement** — mainline `socfpga_agilex5_defconfig` sets + `CONFIG_SPL_FIT_SIGNATURE=y` **[V, defconfig re-fetched 2026-08-21]** — and it now looks + **defused**: the DTB carved from Terasic's published SPL contains **no `/signature` node and + no key material** (`grep -i 'signature|required|rsa|key-name|algo'` over the decompiled DTS: + zero hits) **[V SPL-dtb, inspected 2026-08-21]**, and U-Boot's SPL accepts unsigned FITs when + the control DTB requires no keys. Residual: whether the factory-programmed flash matches the + published build (§7 row 6). +2. **Ship our own QSPI image** (SDM fw + phase-1 + our FSBL): full control of the SPL/DDR + handoff, at the cost of a QSPI flash per board at onboarding. **This is a bench-only + operation** — PC + Quartus Prime Pro + JTAG, MSEL to `111` and back — because no + power-loss-safe update mechanism is demonstrated to fit the 16 MB flash (§6, §8.4). It is not + shipped through the update channel in any form. +3. **Hybrid**: start at 1; fall back to 2 only if the factory SPL proves incompatible, as a + documented one-time bench step performed with a PC attached — **not** RSU-protected, because + RSU is not available here. + +**Revisit trigger for the DP-1 ADR:** if a sub-16 MB RSU layout is ever *proven* (§8.4 residual — +phase-1 image size plus Intel 852610's minima), posture 2/3 becomes technically shippable and the +decision must be re-opened rather than treated as settled by impossibility. + +**How tightly does posture 1 pin us to Terasic?** Tighter than the first pass implied +(§8.2/§8.3/§8.6): we inherit their SPL binary, their DDR/pinmux handoff (which lives in the +bitstream and is not ours to change), and whatever FIT policy their SPL was built with. Their +published build record for the shipped BSP is Quartus Pro **25.1.1**, ATF **2.12.0**, U-Boot +**2025.01**, kernel **6.12.11-lts**, device `A5EB013BB23BE4SCS` **[V T-res]**. What we control is +exactly one interface: a FIT named `u-boot.itb` on the FAT boot partition. That is a thin +contract — which is the good news and the whole risk at once. ## 5. The version-skew seam (the one gotcha to engineer away) -QSPI side: SDM firmware + FSBL + DDR/pinmux handoff (board-resident, ~static). SD -side: everything else (ours, per release). Failure mode: a release whose `u-boot.itb` -the resident FSBL cannot load — the DE25 equivalent of the DE10's zImage_dtb contract -break, except the user cannot see or fix it by re-imaging the card *if* the mismatch -is QSPI-side. Release discipline that keeps this impossible: - -- Every DE25 release records **which factory QSPI version(s) it is tested against** - (Terasic System CD revision; QSPI image hash once we can dump it — D2.2). -- The FSBL→`u-boot.itb` interface (FAT partition number, filename, FIT format) is - treated as a frozen contract; changes to any of it are release-blocking findings. -- No release ever writes QSPI as a side effect. If a QSPI update is ever shipped - (posture 2/3), it is a separate, explicit, RSU-protected artifact with its own - documentation — the `updateboot` analogue **must not** be a cargo-culted raw `dd`: - the DE10 habit (whole-disk `dd` + env wipe, - [`downloader-contract.md`](downloader-contract.md) §8) is board-fatal here. +QSPI side: SDM firmware + U-Boot SPL + DDR/pinmux handoff (board-resident, static, **not +reproducible from our tree**). SD side: everything else (ours, per release). Failure mode: a +release whose `u-boot.itb` the resident SPL cannot load — the DE25 equivalent of the DE10's +`zImage_dtb` contract break, except the user cannot see or fix it by re-imaging the card *if* +the mismatch is QSPI-side. Release discipline that keeps this impossible: + +- **Every DE25 release records which factory QSPI it is tested against.** *(Amended: the + synthesis pass wrote "there is no System CD to cite a revision of". Two refuters showed that is + wrong in a way that matters — Terasic's own *GSG* still instructs the reader to "Copy the + factory code from the path: **System CD**\Demonstration\SoC_FPGA\GHRD\output_files\ + program_qspi_flash\" **[V GSG, text extracted 2026-08-21]**, and *UM* §3.8.6 likewise cites a + "system CD". The **Resource Package is the System CD's current incarnation**, and it carries a + version.)* The identity handles to record are: **board revision** (rev A / rev B), the + **Resource Package version** (revA/revB v1.0.0 — the CD-revision analogue), the **Linux Console + BSP** version (v1.1) and its build record above, the Terasic fork tags `de25_nano_revA_v1.0`, + and the **silicon OPN as read over JTAG** (§3) **[V T-res, GSG, T-guide]**. +- **A factory-QSPI image hash *is* obtainable — the first pass's "no `.jic` is published" was + wrong.** *(Amended: three refuters independently opened the published archive, which no + research leg had done.)* *RP-A* contains + `Demonstration/SoC_FPGA/GHRD/output_files/golden_top_hps.jic` — **16,777,447 B, sha256 + `e3d20c2d066761fd02897422ba361aa19faafcd0240dd2782965754cc61b38a4`** — beside + `golden_top_hps.sof` (1,474,367 B) and the programming scripts. **Re-hashed by this pass from + the local copy of the archive, 2026-08-21 [V]**. What remains genuinely [U] is whether any + *physical* board's flash equals it: no readback procedure is documented by Terasic, whose + scripts are write-side only. Two candidate ways to close it, both for D2.2: (a) `quartus_pgm` + **verify-only** against this JIC — the vendor's own `flash_program.bat` already uses the `pv` + action letters (`-o "pvi;golden_top_hps.jic"`) **[V RP-A, read directly]**; (b) an HPS-side MTD + read, which is the family-standard path — mainline ships the controller node + (`linux:arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi:476-488`, `intel,socfpga-qspi`), the + driver is in our tree (`linux:drivers/spi/spi-cadence-quadspi.c:2213`), and the SoCDK board DTS + demonstrates the flash+partitions pattern + (`linux:arch/arm64/boot/dts/intel/socfpga_agilex5_socdk.dts:68-97`) **[V]** — noting that the + SoC `.dtsi` ships the node `status = "disabled"`, so this is an opt-in we would have to make + (and §7 row 10 is the reason to make it read-only if at all). **Do not write "hash-pinned" in + DE25 release docs until a verify or a readback has actually succeeded on hardware.** +- **The SPL→`u-boot.itb` interface is a frozen contract with four terms — and the first synthesis + named the wrong Kconfig symbol for the first of them.** *(Amended: all three refuters caught + it.)* The terms are: **(1) the FS-boot partition, governed by + `CONFIG_SYS_MMCSD_FS_BOOT_PARTITION` (int, default 1, *u-boot:common/spl/Kconfig*) on the SPL + boot device (`spl_boot_device()` → `BOOT_DEVICE_MMC1`, *u-boot:arch/arm/mach-socfpga/ + spl_soc64.c*) — *not* `ENV_FAT_DEVICE_AND_PART`, which places U-Boot proper's environment file + and merely happens to also read `"0:1"`; (2) the filename `u-boot.itb` + (`SPL_FS_LOAD_PAYLOAD_NAME`, default under `SPL_LOAD_FIT`) — present verbatim as a string in + Terasic's shipped SPL binary; (3) FIT format / config-node selection; (4) signature policy + (`SPL_FIT_SIGNATURE`)** **[V, defconfig and Kconfig re-fetched 2026-08-21]**. The environment + file's location (`ENV_FAT_DEVICE_AND_PART="0:1"`) is a **separate, fifth** contract term, and + it belongs to §7 row 5, not here. Freezing the wrong symbol would let a future partition-layout + change validate green against the env setting while the resident SPL looks elsewhere. Changes + to any term are release-blocking findings. +- **DDR is not ours.** Our `u-boot.itb` must not assume it may re-init or re-tune DDR; the + handoff is inside the QSPI bitstream **[V RB-boot]**. Any future need to change DDR settings is + a posture-2 event (bitstream rebuild + bench flash), not a release. +- **No release writes QSPI, by any mechanism, including the U-Boot environment (§7 row 5).** + This is the load-bearing rule of the document, and its exact status as of 2026-08-21 is: + **a policy, enforced by prose only.** The "guards" refutation lens verified, and this pass + re-states without softening, that **ADR 0027 Decision 4's board-identity assertion has no + implementation anywhere in the tree** — no shipped script reads `/proc/device-tree/compatible` + or otherwise checks board identity; `ADR 0027` lines 80-83, `de25-nano-tasks.md` 120/155 and + `downloader-contract.md` 1208-1215 are all design prose. Nothing today would stop DE10 flash + semantics being cargo-culted onto a DE25 tree except the accident that no DE25 tree exists yet. + **Before the first DE25 release:** (a) implement the identity assertion in the updater *and* in + any `updateboot` analogue; (b) add a release-blocking CI check that the DE25 U-Boot config has + `ENV_IS_IN_UBI` unset and ships no QSPI-write command set; (c) only then may "no release writes + QSPI" be tagged **[V]** rather than "[policy, unenforced]". If a QSPI update is ever shipped + (posture 2/3) it is a separate, explicit, documented **bench** procedure — the `updateboot` + analogue **must not** be a cargo-culted raw `dd`: the DE10 habit (whole-disk `dd` + env wipe, + [`downloader-contract.md`](downloader-contract.md) §8) is release-fatal here even though, per + §7 row 3, it cannot physically reach QSPI. ## 6. QSPI update safety and the unbrick path -- **RSU (Remote System Update)** is Altera's power-loss-safe QSPI update framework: - a factory fallback image plus application image slots; the SDM falls back - automatically on a corrupt/interrupted image **[V Intel docs / U fit-in-16MB — §8-Q4]**. - Any future QSPI-writing flow uses RSU or does not exist. (This is the concrete form - of the DP-3 caveat recorded in [`de25-nano-plan.md`](de25-nano-plan.md) §6: the - *boot-firmware* layer has no pull-the-card recovery, unlike the rootfs.) -- **The board is effectively unbrickable [V UM §3.2]:** the USB-Blaster III is - on-board — worst case is MSEL→`111` (JTAG) + Quartus Programmer on a PC over USB-C - to reflash QSPI, then MSEL back to `001`. Annoying (needs a PC + Quartus install), - not fatal, and needs no external programmer hardware. +- **RSU does not fit this board's flash at reference sizing [V]; that it cannot fit at *any* + sizing is [U].** RSU (Remote System Update) is Altera's power-loss-safe QSPI update framework — + a factory fallback image plus application image slots, with the SDM falling back automatically + on a corrupt or interrupted image. Altera's own Agilex 5 E-series SoC RSU example is built on a + **2 Gbit (256 MB)** QSPI (`QSPI02G`), with a ~7 MB factory image and **three 16 MB application + slots** plus BOOT_INFO/SPT/CPB overhead **[V RSU-ex]**. **One application slot alone is the + DE25-Nano's entire 16 MB flash**, which must additionally already hold the factory phase-1 + image. The first pass's rule — *"any future QSPI-writing flow uses RSU or does not exist"* — + therefore resolves to its second branch in practice: **nothing we ship writes QSPI.** Honest + statement of what is *not* proven: those are the *example's* choices, not documented minima; + the RSU layout is user-authored via `quartus_pfg` partitions; and this pass measured the + factory JIC's non-`0xFF` content at **3,007,196 bytes (2.87 MiB)** out of a 16,777,447 B + container (`golden_top_hps.sof` itself is 1,474,367 B) **[V, measured locally 2026-08-21 — + with the caveat that a JIC is a container and non-`0xFF` density is a proxy for programmed + extent, not a proven image size]**. A hand-tuned sub-16 MB layout is therefore **unproven, not + disproven**. Blocker and owner in §8.4. +- **The kernel-side RSU code exists; the Agilex 5 device tree does not wire it up.** *(Amended: + the synthesis pass said RSU's absence is "a flash-geometry problem, not a software-support + problem" — two refuters showed that understates it, and one of its cites was wrong.)* + `linux:drivers/firmware/stratix10-rsu.c` is present, and `COMMAND_RSU_*` sits at + `linux:include/linux/firmware/intel/stratix10-svc-client.h:153-159` — **not** `:152-156` as + first written (`:152` is the `/* for RSU */` comment) **[V, re-checked line-by-line this + pass]**. But adopting RSU on this board needs three things beyond the driver: a flash big + enough (unproven, above), an **RSU-formatted flash layout** (BOOT_INFO/SPT/CPB written by + `quartus_pfg`) which the factory image does not have — i.e. a posture-2 bench reflash *first* — + and a **device tree that instantiates the SDM service node**, which mainline Agilex 5 does not + ship (§2 step 6) **[V]**. Nobody should read "the driver is there" as "a bigger flash makes + this a software toggle". +- **The board is recoverable at a bench, and the payload to recover it with is published [V].** + *(Amended: the synthesis pass's "you must have an image to write, and Terasic publishes none" + was refuted by all three lenses, by opening the archive.)* The USB-Blaster III is on-board + (*UM* §3.2); *GSG* documents the restore verbatim — connect USB-C to the Blaster III + connector, copy the factory code from `…/GHRD/output_files/program_qspi_flash\`, run + `flash_program.bat`, which is `quartus_pgm.exe -m jtag -c 1 -o "pvi;..\golden_top_hps.jic"` + **[V GSG + RP-A, both read directly 2026-08-21]**. The real conditions are therefore: **a PC + with Quartus Programmer; the correct board revision's package; and an archived copy** (the + vendor URL is the single point of failure, and the local copy in the session scratchpad is + currently the project's only one — move it somewhere durable). Still [U] and owned by D2.2: + that the published JIC actually boots a physical board, and that factory flash equals it. + Note also that *GSG* names no MSEL change in the restore procedure — whether `001` suffices or + `111` is required is **[U → D2.2]**. +- **D2.2 ordering rule, fail-closed** (this exists because the vendor scripts make erase-first + the path of least resistance — `flash_erase.bat` is `quartus_pgm … -o "ri;…"` **[V RP-A]**): + (1) `quartus_pgm` **verify-only** against `golden_top_hps.jic`, to learn whether factory == + published; (2) attempt a readback/dump and archive it; (3) only then any write. **Never run + `flash_erase.bat` as step one** — it destroys the only known-good baseline that has ever + existed for that board. +- Net: the *boot-firmware* layer has no pull-the-card recovery (the DP-3 caveat in + [`de25-nano-plan.md`](de25-nano-plan.md) §6 stands): recovery is bench-only and needs a PC — but + it is no longer blocked on an image we do not have. ## 7. What would brick or strand the board (fail-closed inventory, desk-research level) +Rows 1–9 are the synthesis inventory as adjudicated; rows 10–16 were added by the refutation +pass. No row has been deleted. Severity vocabulary: **brick-class** = needs JTAG + PC; +**strand-class** = fixed by re-imaging the SD card. + | # | Action | Consequence | Guard | |---|---|---|---| -| 1 | Raw/partial QSPI overwrite (non-RSU), interrupted | No SDM config → no boot from AS; recover only via JTAG+PC | §5 rule: no release writes QSPI; RSU-only if ever | -| 2 | Shipping `u-boot.itb` the resident FSBL can't parse | Board strands at FSBL; user re-images card in vain if told "it's the card" | frozen FSBL contract + per-release factory-QSPI test matrix (§5) | -| 3 | Porting DE10 `updateboot` semantics (raw `dd`, env wipe at fixed sectors) | Writes garbage at Agilex-meaningless offsets; worst case hits QSPI-adjacent state | board-identity assertion before any flash step (ADR 0027 §Decision 4) | -| 4 | MSEL switched away from `001` by a user following DE10-era lore | No boot until switched back | docs: "switches stay at default" is the only user-facing rule | - -## 8. Open questions (the remainder of D0.1) - -- **Q1 — SDM boot from SD/MMC on *this board*.** Silicon has an SD/MMC configuration - controller (*Intel-cfg*) and *UM* §3.8.4 calls the microSD "an alternative boot - option", yet Table 3-2 offers no SD scheme and the socket wires to the HPS. If an - undocumented MSEL combination let the SDM fetch phase-1 + FSBL from the card, the - QSPI seam vanishes entirely (everything-on-card, full DE10 parity). **Ask Terasic / - test on hardware.** [U] -- **Q2 — Factory QSPI contents.** Which FSBL (U-Boot SPL vs ATF BL2), which - SDM-firmware/Quartus version, dumpable hash, and whether Terasic's System CD ships - the QSPI image for re-flash. [U] -- **Q3 — FSBL→`u-boot.itb` contract verification.** Does the factory SPL boot a - mainline-built FIT (BL31 + U-Boot 2026.x + DTB)? First test of D2.2. [U] -- **Q4 — RSU layout in 16 MB.** Factory + how many app images fit, given Agilex 5 - phase-1 image sizes. [U] -- **Q5 — U-Boot environment location** (QSPI via SDM? FAT file? nowhere/default-env) - and the warm-reboot / core-preload story ([`boot-chain.md`](boot-chain.md) §6 - analogue). [U] -- **Q6 — DDR handoff coupling.** How much of the DDR init lives QSPI-side (phase-1 - handoff) vs in our `u-boot.itb`, i.e. how tightly posture-1 pins us to Terasic's - DDR configuration. [U] +| 1 | Raw/partial QSPI overwrite, interrupted | **Brick-class.** No SDM config → no boot from AS; recover only via JTAG + PC + a JIC in hand | §5 rule: nothing we ship writes QSPI; RSU is not available as mitigation (§6) | +| 2 | Shipping `u-boot.itb` the resident SPL can't parse | **Strand-class**, not brick-class: QSPI is untouched, so re-imaging the card with the *previous* release or Terasic's BSP card boots the board — but re-imaging with the *same* release fails identically, and without a serial console it is indistinguishable from a bad card | frozen four-term SPL contract (§5, corrected symbols) + a per-release factory-QSPI boot test. **Recorded objection** (guards lens): that test matrix exists nowhere — no DE25 hardware, no CI lane, no test doc. It must be created at D2.2 before the first release | +| 3 | Porting DE10 `updateboot` semantics (raw `dd`, env wipe at fixed sectors) | **Strand-class, and the first synthesis overstated it.** *(Amended — all three lenses.)* The `dd` leg writes garbage at Agilex-meaningless offsets **on the SD card only**: `/dev/mmcblk*` is the HPS SD/MMC controller; QSPI is a physically separate Cadence controller behind the SDM (`linux:…/socfpga_agilex5.dtsi:476-488`, `spi@108d2000`). No sector arithmetic on `mmcblk0` can reach boot flash. The **env-wipe leg** is the one that can — via row 5 or row 11 | board-identity assertion before any flash step (ADR 0027 §Decision 4 — **unimplemented**, §5), plus rows 5 and 11 | +| 4 | MSEL switched away from `001` by a user following DE10-era lore | No boot until switched back; no damage | docs: "switches stay at default". **Recorded objection** (guards lens): that sentence appears in **no user-facing document** — `docs/user/` has zero DE25 or MSEL content; today the rule lives only in this developer doc. Writing it into the DE25 user docs is a first-release blocker, not an existing guard | +| 5 | **`saveenv` (or any env write) with `ENV_IS_IN_UBI` compiled in and no `uboot.env` on FAT** | **Brick-class.** `env_save()` targets the location the env *loaded* from; on FAT-miss that is the **UBI volume in QSPI** → a routine operation writes boot flash | build our U-Boot with `CONFIG_ENV_IS_IN_UBI=n`; ship a valid `uboot.env` on FAT; assert the boot log says `Saving Environment to FAT` (§8.5) | +| 6 | **Factory SPL built with FIT signature required; we ship an unsigned `u-boot.itb`** | Every release strands at SPL on every board — indistinguishable from a bad card to the user | D2.2 first test before any release. **Recorded objection** (completeness lens, and this pass agrees on the evidence): the DTB carved from Terasic's *published* SPL carries **no `/signature` node and no keys** (*SPL-dtb*, inspected 2026-08-21), so this drops from "posture-1 killer" to a routine first-contact check. Residual is only the published-build-vs-factory-flash gap | +| 7 | **Any QSPI write attempted with no archived known-good JIC** | *(Amended — all three lenses: the premise was false.)* The JTAG path is **not** empty-handed: `golden_top_hps.jic` is published (§5) and *GSG* documents the restore. The real exposures are vendor **link-rot**, **wrong-revision** substitution (row 14), and the fact that the published JIC's bootability is untested | archive both revisions' Resource Packages **with hashes** now, locally and durably; verify before erase (§6 ordering rule); never rely on the vendor URL staying live | +| 8 | **Posture-2 QSPI flash performed as a field/OTA step** | **Brick-class today.** *(Amended — all three lenses: state the rule, not an impossibility.)* No power-loss-safe layout for 16 MB is **demonstrated or documented** (§8.4 residual; the measured ~2.87 MiB phase-1 payload makes a custom layout plausible but unproven), so a power cut mid-write leaves JTAG-only recovery for an end user | posture 2 is bench-only, PC-attached, documented one-time; never an update-channel artifact. Revisit only if §8.4's residual resolves in RSU's favour | +| 9 | Assuming our `u-boot.itb` can set/repair DDR or pinmux | It cannot — the handoff is inside the QSPI bitstream **[V RB-boot]**; a "fix it in U-Boot" reflex produces silent misconfiguration or no boot | treat DDR/pinmux as QSPI-owned; any change is a bitstream rebuild + bench flash (§5) | +| 10 | **Shipping a DTB that exposes the QSPI as a writable MTD, then any root shell / porting script running `flash_erase /dev/mtdX` or an mtd write** | **Brick-class, and it bypasses every guard aimed at `dd` and env semantics** — one userspace command from a healthy booted system erases SDM firmware + phase-1 | Our DTS omits the QSPI node, or enables it **read-only**. Precise state today: the SoC `.dtsi` ships `spi@108d2000` **disabled** (`linux:…/socfpga_agilex5.dtsi:476-488`) and it is the *board* DTS that enables it with writable `fixed-partitions` (`linux:…/socfpga_agilex5_socdk.dts:68-97`) **[V]** — so this is a hazard we would have to opt into, including if we opt in for the §5 readback. Audit Terasic's kernel DTB for the same at D2.2. [U] whether the SDM grants HPS access to the flash on this board — D2.2 | +| 11 | **Shipping `fw_setenv`/libubootenv with an `fw_env.config` that names an MTD device** (copied from a reference BSP, or a DE10 env-wipe habit ported) | **Brick-class and silent**: Linux-side env writes reach QSPI without U-Boot involved, headless, repeatedly. Row 5's `ENV_IS_IN_UBI=n` does **not** protect this path — `fw_setenv` does not consult U-Boot's compiled-in drivers | if shipped at all, `fw_env.config` names only the FAT-partition `uboot.env` file; CI greps the rootfs for MTD-pointing env configs | +| 12 | **Booting a U-Boot with `ENV_IS_IN_UBI` against a QSPI whose `root` MTD partition is blank**: the env **load** path runs `ubi part root`, and a UBI attach on an empty MTD auto-formats it | Worse than row 5 — QSPI is written on an environment *load* miss, with no user action at all, destroying the pristine-factory baseline before it can be hashed | same guard as row 5, promoted from prudent to mandatory: `CONFIG_ENV_IS_IN_UBI=n`. **[U] mechanism:** U-Boot's `ubi_part` auto-format-on-attach behaviour was **not** verified this pass — named missing input: read `env/ubi.c` + `cmd/ubi.c` in a U-Boot checkout. Note the SoCDK DTS does define a partition literally labelled `root` (`linux:…/socfpga_agilex5_socdk.dts:91-93`), matching `ENV_UBI_PART="root"` **[V]** | +| 13 | **A user runs a Terasic *demo*'s `flash_program.bat`** from the same Resource Package our docs will point them at | **Strand-class-with-a-bench-recovery.** These demos ship full-flash **fabric-only** JICs and a one-click programming flow: `…/FPGA/HDMI_ASx4/demo_batch/golden_top.jic` (16,777,447 B) with `flash.cdf` = `ActionCode(Cfg) Device PartName(A5EB013BB23BE4SR1) … File("golden_top.jic") … SEC_Device(MT25QU128)` **[V RP-A, read directly]**. Programming one **erases the HPS-first factory image — SPL and all** — so SD Linux boot is dead until the *GSG* restore is run at a bench. This is a **vendor-sanctioned, user-facing QSPI write path entirely outside our control** | user docs: the only QSPI rule is "never run any demo `flash_program.bat`"; our recovery page carries the *GSG* restore recipe and the per-revision factory JIC hash so support can walk a user back | +| 14 | **Recovery or bench flash with the wrong board revision's or wrong OPN's JIC** | Programming succeeds (JTAG validates little beyond the die) but the DDR/pinmux handoff inside the bitstream is wrong for the board → no boot or subtly wrong DDR, **and the original factory image is now gone**. The recovery attempt manufactures row 7's state. Terasic ships revA and revB packages separately; *UM* prints OPN `…SR1` while the BSP record says `…SCS` (§3) | archive both revisions with hashes, keyed to device + board revision; read the OPN over JTAG (Quartus auto-detect) and the revision off the PCB before any write; forbid "any Agilex 5 JIC" substitution. Desk follow-up: diff the revA/revB GHRD projects | +| 15 | **Shipping a phase-2 `core.rbf` from our own Quartus compilation against the factory phase-1 resident in QSPI** | Altera doctrine for split (HPS-first) configuration is that periphery and core images come from the **same** Quartus compilation. If that holds here, every boot-time fabric design we ship is pinned to Terasic's exact factory compile — a **project-shaping constraint on the whole core-switching model**, and a strand-at-U-Boot failure when violated. **[U]** — consistently reported across Altera-derived sources but the authoritative UG (813773) is 403-blocked | D0.2/D2.2: (a) test a self-recompiled GHRD `core.rbf` against the untouched factory QSPI; (b) test whether runtime `COMMAND_RECONFIG` full reconfiguration is compilation-independent — that decides whether core switching routes through U-Boot phase-2 at all or must be Linux-runtime-only. Until answered, treat boot-time phase-2 as pinned to the factory compilation | +| 16 | **Running `flash_erase.bat` (or `flash_program.bat`, which erases) as the first act of a D2.2 bench session** | The factory content — never dumped, never compared — is destroyed first; if the published JIC then fails to boot this board, no known-good image has ever existed for it | the §6 ordering rule, written into the D2.2 task *before* hardware arrives: verify-only → readback/archive → only then write | + +## 8. Resolved questions (D0.1 close-out) + +**Provenance note.** Six research legs were run, one per question. **Q4 and Q6 returned +placeholder content** ("test", claim text `a`, source `b`) and were **discarded in full**; the +answers below for those two are sourced independently. One Q5 citation +(`stratix10-svc.c:172-191` for `COMMAND_RECONFIG`) **does not hold** — that range is +`svc_pa_to_va()` in our tree; corrected line numbers are used above and re-verified this pass. +One Q5 source (an Intel PDF cited with section numbers 14.3.3.3–14.3.3.5 under a document number +that does not match that title) **could not be re-retrieved** (403); its section and page numbers +are **dropped**, not repeated, and the claim they supported is carried as [U]. + +### 8.1 Q1 — Can the SDM boot from SD/MMC on this board? — **RESOLVED: no [V]** + +Board-gated, not merely undocumented. Three independent facts close it: + +- *UM* Table 3-23 names the microSD socket's HPS-side signals `HPS_SD_CLK`, `HPS_SD_CMD`, + `HPS_SD_DATA[0..3]` — HPS peripheral pins, the same class as `HPS_USB_*` / `HPS_UART_*`, + **not** SDM_IO configuration pins **[V UM, text re-extracted 2026-08-21]**. +- *UM* Table 3-2 lists exactly two MSEL schemes (`001` AS Fast, `111` JTAG); SW5 wires only + MSEL0–2 **[V UM]**. No strap selects an SD/MMC scheme. +- *UM* §3.8.4 — *"It serves not only as an external storage for the HPS but also as an + alternative boot option for the DE25-Nano board."* — is the FSBL-reads-SD step (§2 step 4), not + a second SDM path **[V UM]**. *RB-boot*'s own "boot from SD card" example sets MSEL to **JTAG**, + never to an SD value **[V RB-boot]**. Corroborating at the artifact level: the factory SPL's + `u-boot,spl-boot-order` starts at `/soc/mmc0@10808000` — the *HPS* MMC controller, reached only + after the SDM has already configured the device from QSPI **[V SPL-dtb:429]**. + +Residual [U], non-blocking: whether Agilex 5 *silicon* has an SD/MMC configuration scheme at all +(813773's body is 403-blocked; only its TOC was readable, showing AVST/AS/JTAG/CvP), and whether +the `HPS_SD_*` pins are mux-capable to SDM_IO at the device level (would need the board's +pin-planner file or Terasic confirmation). **Neither changes the answer**, because Table 3-2 +offers no strap to select such a scheme even if it exists. + +Optional hardening question for Terasic: *"Does the microSD socket connect only to the HPS +SD/MMC peripheral (Table 3-23), or is it also routed to SDM_IO? Is there any MSEL/SW5 value +beyond 001/111?"* + +### 8.2 Q2 — Factory QSPI contents — **LARGELY RESOLVED [V]; parked only on the dump.** + +*(Substantially upgraded by the refutation pass, which opened the published archive that the +research legs had only read the index of.)* + +- The branded "System CD" is no longer a separate download — `de25-nano.terasic.com/cd/` 302s to + the resource page **[V]** — but the **name survives in Terasic's own current documentation**, + and the **Resource Package is what it now denotes** (*GSG*'s restore path begins `System CD\`) + **[V GSG]**. Downloads are: User Manual (revA/revB), **Resource Packages** revA/revB v1.0.0, + and a **Linux Console microSD BSP** v1.1 **[V `dl2.terasic.com/resources/de25-nano/` index]**. +- **The factory QSPI image *is* published**: `golden_top_hps.jic`, 16,777,447 B, sha256 + `e3d20c2d066761fd02897422ba361aa19faafcd0240dd2782965754cc61b38a4`, in + *RP-A* `Demonstration/SoC_FPGA/GHRD/output_files/` **[V, re-hashed locally this pass]**, with + `golden_top_hps.sof` (1,474,367 B) and `program_qspi_flash/{flash_program,flash_erase}.bat` + beside it. *GSG* names this tree as "the factory code" **[V]**. +- The BSP's description block gives its build record verbatim: device `A5EB013BB23BE4SCS`, + Quartus **25.1.1 Pro**, ATF branch **2.12.0**, U-Boot branch **2025.01**, kernel + **6.12.11-lts**, Ubuntu 22.04.3 **[V T-res]** — the **SD-side** record; no QSPI/factory build + record is itemised on the page **[V, absence observed]**. +- **FSBL identity: U-Boot SPL [V, now vendor-sourced]** — *RP-A* ships + `…/GHRD/software/u-boot/spl/u-boot-spl-dtb.hex` (721,613 B); the DTB carved from it declares + `model = "SoCFPGA Agilex5 Terasic DE25-Nano"` **[V SPL-dtb]**. *T-guide*'s account of the + build (embed the hex into the GHRD Quartus project, `sof_with_hps` → `sof_to_jic`; BL31 into + `u-boot.itb`; Terasic forks tagged `de25_nano_revA_v1.0`) is now corroborated by the artifacts + rather than resting on a community page **[V for the artifacts; U for the process narrative]**. +- Whether the Resource Package contains the GHRD project — the first pass's open [U] — is + **resolved yes**: `Demonstration/SoC_FPGA/GHRD/` with `output_files/` and `software/u-boot/` + **[V, archive listed]**. + +**PARKED — blocker:** *no readback/dump procedure for SDM-mediated QSPI is documented by +Terasic* (its scripts are write-side only) — so we can hash the **published** image but not yet +a **board's** image. Two candidate closures, both in §5, both **inheriting D2.2**: +`quartus_pgm` verify-only against the published JIC, or an HPS-side MTD read from a QSPI-enabled +DTB (weighing §7 row 10 first). + +### 8.3 Q3 — Will the factory SPL boot a mainline-built `u-boot.itb`? — **PARKED on hardware; structure resolved [V], and two of three blockers closed at the desk.** + +Structure, mainline-defined **[V, `u-boot:` at `master`, re-fetched 2026-08-21]**: +`configs/socfpga_agilex5_defconfig` sets `CONFIG_SPL_LOAD_FIT=y`, +`CONFIG_SPL_LOAD_FIT_ADDRESS=0x82000000`, `CONFIG_SPL_FS_FAT=y`, `CONFIG_SPL_ATF=y`, +`CONFIG_SPL_ATF_NO_PLATFORM_PARAM=y`, `CONFIG_ENV_IS_IN_FAT=y`, +`CONFIG_ENV_FAT_DEVICE_AND_PART="0:1"`, **`CONFIG_SPL_FIT_SIGNATURE=y`**, and also +`CONFIG_CMD_MTD=y` / `CONFIG_CMD_UBI=y` (§7 row 11). `common/spl/Kconfig` defaults +`SPL_FS_LOAD_PAYLOAD_NAME` to `"u-boot.itb"` whenever `SPL_LOAD_FIT=y`, and +`SYS_MMCSD_FS_BOOT_PARTITION` to 1. `arch/arm/mach-socfpga/spl_soc64.c` gives `spl_boot_device()` +→ `BOOT_DEVICE_MMC1`, `board_boot_order()` reading `/chosen`'s `u-boot,spl-boot-order`, and +`spl_boot_mode()` → `MMCSD_MODE_FS`. + +Blocker status: +1. Whether Terasic's SPL was built from mainline or their fork (BSP record: U-Boot **2025.01**), + and **whether its DTB carries required signature keys** — **closed at the desk for the + published build: it does not** (no `/signature` node, no keys, *SPL-dtb*) **[V]**. Residual: + published build vs factory-programmed flash **[U → D2.2]**. +2. The `u-boot,spl-boot-order` baked into their handoff DT — **closed [V]**: + `"/soc/mmc0@10808000", "/soc/spi@108d2000/flash@0", "/soc/nand@10b80000", "/memory"` + (*SPL-dtb*:429), with the QSPI node itself `status = "disabled"` in that DTB (*SPL-dtb*:225). +3. `board_fit_config_name_match()` for socfpga/agilex5 (multi-config-node selection): still not + located — `board/socfpga/socfpga.c` 404'd over the web. **[U]** — a desk loose end, resolvable + by reading a local U-Boot checkout rather than fetching files one at a time. + +**Inherits: D2.2** (the actual boot test, plus blocker 1's residual), and a trivial desk +follow-up for blocker 3. + +### 8.4 Q4 — Does RSU fit in 16 MB? — **RESOLVED to the decision-relevant answer: not at reference sizing [V]. Impossibility NOT established; residual parked.** + +*(Leg output discarded as placeholder; sourced independently.)* Altera's rel-25.1 **HPS Remote +System Update example for Agilex 5 E-series** is built on a **2 Gbit (256 MB)** QSPI +(`QSPI02G`), with a ~**7 MB** factory image and **three 16 MB** application slots, plus +BOOT_INFO / SPT0-1 / CPB0-1 overhead **[V RSU-ex]**. The DE25-Nano has **16 MB total**, already +occupied by the factory phase-1 image. A single reference application slot is the whole device. + +*Honesty about what this source is:* the page was read through a fetch summariser and the offset +column it returned is internally inconsistent (SPT0 at `0x910000` sits outside the stated 2.1 MB +BOOT_INFO region). **The offsets are not relied on** — only device size, factory-image size and +per-slot size, which are mutually consistent and decisive on their own. + +**PARKED — residual blocker, and the refutation pass sharpened it in the direction of *doubt*:** +the reference sizes are an example's choices, not documented minima; RSU layouts are user-authored +via `quartus_pfg`; and the phase-1 payload is now measured — `golden_top_hps.sof` is 1,474,367 B +and the JIC's non-`0xFF` content is 2.87 MiB **[V, measured 2026-08-21]** — which makes +"factory + one small application slot inside 16 MB" arithmetically plausible. Named missing +inputs: (a) **Intel 852610** (Agilex 5 quad-SPI flash layout), the document where minima would +live — **403-blocked**; (b) confirmation that a JIC's non-`0xFF` extent is a fair proxy for its +programmed size. **Inherits: a follow-on desk task or D2.1. Until it lands, §6's conclusion +("nothing we ship writes QSPI") stands as policy and §7 rows 1/8 are the operative guards — but +it must not be written into the DP-1 ADR as a proven impossibility (§9.2).** + +### 8.5 Q5 — U-Boot environment location and the warm-reboot story — **Env: RESOLVED [V], and it is §7 rows 5/11/12. Warm reboot: PARKED [U].** + +**Environment.** Mainline `socfpga_agilex5_defconfig` compiles in **two** env locations: +`CONFIG_ENV_IS_IN_FAT=y` (`ENV_FAT_DEVICE_AND_PART="0:1"`) **and** `CONFIG_ENV_IS_IN_UBI=y` +(`ENV_UBI_PART="root"`, `ENV_UBI_VOLUME="env"`), with `ENV_SIZE=0x2000`; there is no +`ENV_IS_IN_MMC` and no `ENV_IS_IN_SPI_FLASH` **[V, defconfig re-fetched and re-read 2026-08-21]**. +Precedence is observable in *RB-boot*'s own boot log: `Loading Environment from FAT... Unable to +read "uboot.env" from mmc0:1...` then `Loading Environment from UBI...` → `Select Environment on +UBI: OK` **[V RB-boot]** — FAT first, UBI as fallback, and on a QSPI-boot posture that UBI volume +lives **in QSPI**. + +The hazard is in `env_save()`, not the defconfig: `env_load()` records `gd->env_load_prio` for +the driver that **succeeded**, and `env_save()` does +`env_driver_lookup(ENVOP_SAVE, gd->env_load_prio)` — **`saveenv` writes back to wherever the +environment was read from** (*u-boot:env/env.c*) **[V]**. FAT env present → saves to FAT (safe). +FAT env absent **and** a UBI `env` volume reachable → **`saveenv` writes QSPI**. Both fail → +`best_prio = 0`, the highest-priority driver. + +- In our posture-1 SD flow the *intent* is FAT-only, and whether the factory QSPI even contains a + UBI volume named `env` is **[U → D2.2]**. The defence must not depend on that: build with + **`CONFIG_ENV_IS_IN_UBI=n`** and ship a valid `uboot.env`, so the dangerous branch is + unreachable by construction. **§7 row 5** — and see rows 11 and 12 for the two paths that + `ENV_IS_IN_UBI=n` alone does **not** close. +- Which of `fat`/`ubi` is priority 0 is set by linker-list ordering, not by `env/Makefile` order; + *RB-boot*'s log shows FAT first in practice **[V empirically / U as a read-the-linker-script + claim]**. + +**Warm reboot / core preload — PARKED.** No SDM-mailbox or DDR-resident-flag mechanism analogous +to the DE10's Main_MiSTer ⇄ U-Boot mailbox at `0x1FFFF000` +([`boot-chain.md`](boot-chain.md) §6) is documented in any source we opened (*GHRD*, *RB-boot*, +*UM*, *GSG*) — an **absence observed**, not a documented negative **[U]**. What is positively +established: deliberate runtime fabric reconfiguration on this family is an explicit mailbox +request through `stratix10-svc`/`stratix10-soc` +(`linux:drivers/fpga/stratix10-soc.c:195`) **[V]** — Linux-mediated, not a U-Boot +`fpgacheck`-style dispatcher — **and it does not probe at all on mainline Agilex 5 DTs** (§2 step +6) **[V]**. The first-pass leg's claim that an HPS warm reset leaves fabric configuration +untouched is carried **[U]**: the document it cited could not be re-retrieved (403) and its +identification was internally inconsistent, so its section/page numbers are dropped rather than +repeated. **Blocker:** Agilex 5 boot/reset documentation (813762/813763, 814346, 786901) is +403-blocked on every mirror tried. **Inherits: D2.2** — trigger an HPS warm reset and observe +whether the fabric image survives; on the same run, confirm which env location our build loads +from and saves to. + +### 8.6 Q6 — DDR-handoff coupling: how tightly does posture 1 pin us to Terasic? — **RESOLVED at the structural level [V]; magnitude parked.** + +*(Leg output discarded as placeholder; sourced independently.)* **All of it is QSPI-side.** +*RB-boot* states it plainly for this family: *"For Agilex 5, all the handoff information created +by the Quartus compilation is part of the configuration bitstream. The bsp-editor is not used, +and the bootloader build flow does not depend on the Quartus outputs."* **[V RB-boot]**. Two +consequences pull in opposite directions and both matter: + +- **Good:** our bootloader build needs **no** Quartus handoff artefacts — no DE10-style + `qts-filter`/handoff-header step, nothing to keep in sync per board revision. The DE10's + "SPL carries QTS handoff headers" model does **not** transfer + ([`boot-chain.md`](boot-chain.md) §1). +- **Binding:** DDR and pinmux configuration is **not ours to change** under posture 1. We inherit + Terasic's memory timings, pin assignments and HPS-first handoff exactly as compiled into the + factory JIC. Any change is a posture-2 bitstream rebuild + bench flash (§4, §7 row 9). The same + coupling is what makes §7 rows 14 and 15 dangerous. + +**PARKED — residual:** the *magnitude* of what is inherited (which specific DDR parameters, +whether rev A and rev B differ in it, whether a rev-B board's factory bitstream would mis-handoff +to a rev-A-built payload) is **[U]**. Named missing input: a diff of the two revisions' GHRD +projects — **now desk-obtainable, since both Resource Packages are downloadable and revA is +already in hand**. **Inherits: D2.1/D2.2**, and it is a live argument for recording board revision +in every DE25 release's test matrix (§5) *and* in the recovery kit (§7 row 14). + +### 8.7 Accept criterion for D0.1 — **met** + +The criterion was: every §8 question either resolved to **[V]** or explicitly parked with a +**named blocker** and a **named inheriting task**. Status: + +| Q | Outcome | Blocker if parked | Inherits | +|---|---|---|---| +| Q1 SDM-from-SD | **RESOLVED [V]** — no | (silicon-level residual, non-blocking) | — | +| Q2 factory QSPI | **RESOLVED [V]** for contents + published image hash; **PARKED** for a board's own image | no vendor-documented readback procedure | D2.2 (verify-only, then readback) | +| Q3 SPL accepts our FIT | **PARKED** (structure [V]; blockers 1–2 closed at desk) | needs the real board; `board_fit_config_name_match()` unread | D2.2 + trivial desk follow-up | +| Q4 RSU in 16 MB | **RESOLVED [V]** at reference sizing; **PARKED** on impossibility | Intel 852610 (403); JIC-extent proxy unvalidated | desk task / D2.1 | +| Q5 env location | **RESOLVED [V]**; warm-reboot **PARKED** | 813762/813763, 814346, 786901 all 403 | D2.2 | +| Q6 DDR coupling | **RESOLVED [V]** structurally; magnitude **PARKED** | revA/revB GHRD diff not yet done (desk-obtainable) | D2.1/D2.2 | + +Every hardware-gated item is handed to **D2.2**, and D2.2 now inherits an explicit ordering rule +(§6) so that its first QSPI-touching act cannot be destructive. + +## 9. Refutation record + +### 9.1 What ran + +An adversarial refutation pass ran **2026-08-21** against the §5–§7 claim set (21 claims), with +three independent lenses: + +1. **Mechanism** — is the described Agilex 5 / DE25 hardware-firmware behaviour real; is JTAG + recovery genuinely sufficient in every listed failure mode; is "no RSU / JTAG-only recovery" + stated at the right strength. Re-derived every claim from primary sources where reachable. +2. **Guards** — for each claim, does the named guard exist *today*, is it mechanically enforced + or merely written intention, and can a well-meaning contributor bypass it unnoticed. Read the + named flash scripts end-to-end and grepped the tree for any board-identity assertion. +3. **Severity/completeness** — is each consequence over- or under-stated, and what is missing + from the inventory entirely. + +Two of the three lenses **downloaded and opened the published Terasic Resource Package** — which +no research leg had done, and which is where several first-pass "nothing is published" claims +died. Adjudication rule applied here: **AMEND** = a majority of refuters refuted it → rewritten +to the corrected form; **OPEN CONCERN** = exactly one refuter objected → the claim is kept and +the objection recorded in-line, naming the lens; **STANDS** = left alone. + +### 9.2 Amended (majority-refuted, rewritten above) + +| Claim | What changed | +|---|---| +| §5 no-System-CD | "No System CD exists to cite a revision of" → **the name survives in Terasic's own *GSG*/*UM* and denotes the Resource Package**; record the package version as the CD-revision analogue | +| §5 no-QSPI-hash | "No `.jic` published; a hash is unobtainable" → **the factory JIC is published and hashed** (`golden_top_hps.jic`, sha256 `e3d20c2d…`). Only a *board's* image remains unhashed; HPS-side MTD readback added as a second candidate route | +| §5 four-term-contract | Term 1 cited the wrong Kconfig symbol: `ENV_FAT_DEVICE_AND_PART` is the **environment file's** location; the SPL payload partition is **`SYS_MMCSD_FS_BOOT_PARTITION`** (default 1). Corrected, with the env location split out as a separate fifth term | +| §6 RSU-kernel-side-exists | Line range corrected (`stratix10-svc-client.h:153-159`, not `:152-156`) and the framing fixed: RSU also needs an **RSU-formatted flash layout** and a **DT node mainline Agilex 5 does not ship** — not "geometry only" | +| §6 unbrickable-conditional | "You must have an image and Terasic publishes none" → **the image is published and the restore is documented (*GSG*)**; the real conditions are a Quartus PC, the right board revision, and an archive against link-rot | +| §7 row 3 | "Worst case hits QSPI-adjacent state" had **no mechanism** — `mmcblk*` and the QSPI controller are physically separate. Row is now **strand-class**, with the env-tooling leg named as the actual QSPI-reaching path | +| §7 row 7 | Premise false (see no-QSPI-hash); rewritten around link-rot, wrong-revision substitution, and untested bootability | +| §7 row 8 | "No power-loss-safe framework fits 16 MB" [V] → **"none is demonstrated or documented"**; the guard is kept as **policy**, not as a consequence of a proven impossibility | +| §4 posture-1-forced | "Close to forced" → **chosen fail-closed**, with a recorded revisit trigger; the signature risk that would have forced it now tests negative on the published SPL | + +**Citation defects found and fixed** (house rule 1 is load-bearing, so these are logged): +`stratix10-svc.c:172-191` for `COMMAND_RECONFIG` — wrong, that range is `svc_pa_to_va()`; +`stratix10-svc-client.h:152-156` for `COMMAND_RSU_*` — wrong, the enum entries are `:153-159`; +`stratix10-soc.c:186-196` — the actual `COMMAND_RECONFIG` send is `:195`. All three re-checked +against `/mnt/source/Buildroot_MiSTer/output/build/linux-6.18.44` by this pass. + +### 9.3 Open concerns (single-refuter objections — kept, not dropped) + +| Claim | Lens | Objection, recorded verbatim in substance | +|---|---|---| +| §5 no-release-writes-QSPI | Guards | The rule exists; **the guard does not, except as sentences.** No shipped script reads `/proc/device-tree/compatible`; ADR 0027 Decision 4, `de25-nano-tasks.md` and `downloader-contract.md` are all prose; no CI check pins a future DE25 U-Boot to `ENV_IS_IN_UBI=n`. Tag it [V] only after (a) the identity assertion exists as code and (b) a release-blocking CI check exists. **Carried in §5, final bullet.** | +| §7 row 2 | Guards | Consequence overstated (**strand-class**, card-recoverable, since QSPI is untouched) and the "per-release factory-QSPI test matrix" exists nowhere. **Carried in the row.** | +| §7 row 4 | Guards | "Switches stay at default" appears in **no user-facing doc**; `docs/user/` has zero DE25/MSEL content. It is a task, not a guard. **Carried in the row.** | +| §7 row 6 | Severity | Antecedent is desk-testable and tests **negative**: the published SPL's DTB has no signature keys, so this drops from posture-1 killer to a first-contact check. **Carried in the row** (this pass independently re-inspected the DTS and agrees). | + +### 9.4 Stood unchallenged by every lens + +§5 seam-exists; §5 DDR-not-ours; §6 RSU-does-not-fit (at reference sizing); §6 RSU-residual; +§7 rows 1, 5, 5-residual, 9; §2/§3 FSBL-is-SPL. + +### 9.5 What has *not* been challenged, and by whom + +A reader must be able to tell survival from silence. **Not covered by any lens this pass:** + +- **§1–§3 and §8 as prose.** The refutation targeted the §5–§7 claim set. §2's step list, §3's + table and §8's parked-question wording were re-derived by this synthesis, not attacked. +- **The new §7 rows 10–16.** They are the refuters' own missing-vector findings, so nothing has + refuted *them*. Rows 10, 13, 14, 16 rest on artifacts this pass re-verified directly; **row 12's + UBI auto-format mechanism is explicitly [U]** and row 15's same-compilation doctrine is **[U]** + on a 403-blocked UG. Treat both as hypotheses with named tests, not findings. +- **Everything Terasic-page-sourced during the mechanism lens.** `terasic.com.tw` was + TLS-unreachable from the refuting environment, so *T-res* claims **stand unchallenged rather + than re-proven**; the `dl2.terasic.com` index and the archive contents *were* independently + verified. +- **Every Intel-document claim.** 813762, 813763, 813773 (body), 814346, 786901, 852610 — all + 403 on every attempt across both passes. Nothing in this document quotes them. +- **Everything on hardware.** No board has been touched. Every [V] here is a document, a source + file, or a vendor artifact — none is a measurement. + +## 10. Corrections owed to sibling documents + +- [`de25-nano-plan.md`](de25-nano-plan.md) §1 lists **ATF BL2 as the FSBL**; it is **U-Boot SPL** + (§2 step 2, §8.2) **[V]**. +- [`de25-nano-plan.md`](de25-nano-plan.md) §6's DP-3 caveat stands and hardens: boot-firmware + recovery is bench-only and needs a PC — but it is *not* blocked on an unobtainable image (§6). +- The DP-1 ADR (D2.7) must record posture 1 as **chosen fail-closed**, with "a proven sub-16 MB + RSU layout" as its explicit revisit trigger — **not** as forced by impossibility (§4, §9.2). +- ADR 0027 Decision 4's board-identity assertion needs an implementation task with a release-block + attached, not another restatement (§5, §9.3). diff --git a/docs/de25-fpga-reconfig.md b/docs/de25-fpga-reconfig.md new file mode 100644 index 0000000..6cf12e7 --- /dev/null +++ b/docs/de25-fpga-reconfig.md @@ -0,0 +1,903 @@ +# DE25-Nano FPGA reconfiguration and the HPS↔FPGA memory contract + +**Status:** desk research, 2026-08-21 — **no hardware touched**, nothing built, nothing +measured. This is the D0.2 deliverable ([`de25-nano-tasks.md`](de25-nano-tasks.md)), and it +has been through D0.2's adversarial refuter pass (§10). The DP-9 verdict (§8) and the +UX conclusion (§1) survived that pass; both were amended by it, and every number in §6 is +superseded the day D2.5 measures one. Claims are tagged **[V]** (verified against a named +source) / **[U]** (unverified, with the missing thing named). Cross-refs: +[`de25-nano-plan.md`](de25-nano-plan.md) §1, §6 (DP-9, DP-10), §7; +[`de25-boot-chain.md`](de25-boot-chain.md) §2 step 5 and §5; +[ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md); and — for the DE10 baseline +contrasted against throughout — the carried patch series in +`board/mister/de10nano/linux-patches{,-beta}/`. + +**Sources:** + +- Mainline **Linux 6.18.44**, the tree this repo builds: `output/build/linux-6.18.44`. + Cited as bare `file:line`. Every kernel line number below was opened and read on + 2026-08-21, including a re-verification pass after the refutation (§10). This is the + authoritative source in this document — the only one that could be read in full. +- Carried DE10 patches: `board/mister/de10nano/linux-patches-beta/0043-dts-uio-doorbells.patch`, + `0044-dts-uio-fpga-regions.patch`, `0045-uio-writecombine.patch`, and + `linux-patches/0001-fbdev-add-MiSTer_fb-driver.patch`. +- Terasic **DE25-Nano User Manual**, rev. 2025-09-05, 51 pp. — *UM*. **Read in full this + session**: the DigiKey mirror `mm.digikey.com/Volume0/opasdata/d220001/medias/docus/7704/P0804.pdf` + was fetched and text-extracted on 2026-08-21, so *UM* citations here are read-document + citations, not snippets. Cited by section and by extracted quote. +- **Upstream Agilex-5 FPGA-manager series**, Khairul Anuar Romli (Altera), Nov 2025 — + *A5-series*. Read on 2026-08-21 via the `lkml.iu.edu` hypermail mirror: + `2511.1/03492.html` (driver, 2025-11-11), `2511.1/05879.html` (binding v3, 2025-11-14), + `2511.1/05877.html` (DTS v3, 2025-11-14). Respun through v6 (2025-11-19). **Not in + 6.18.44.** +- Altera **Agilex 5 E-series GHRD Linux boot examples** (`altera-fpga.github.io`, rel-24.2 + / rel-25.1 as cited in [`de25-boot-chain.md`](de25-boot-chain.md)), retrieved + 2026-08-21 — *GHRD*. +- RocketBoards **Agilex 7 Configuring FPGA Fabric From Linux**, retrieved 2026-08-21 — + *RB-A7*. Agilex **7**, not 5; used only where labelled as analogy. +- community.altera.com thread **312795** "Agilex 5 – HPS first – u-boot stuck if + rebooting" (AXE5-Eagle) — *AXE5*. Direct fetch 403s; the log figures come from the + draft's Wayback snapshot `20260215052804`, and the thread's **subject and resolution** + were independently corroborated by search on 2026-08-21 (§6.1 row 4a). +- community.altera.com thread **314152** "Agilex 5 – configure FPGA from running linux via + dt-overlay" (marked Solved) — *A5-overlay*. Direct fetch and the community.intel.com + mirror both 403 on 2026-08-21; **search-snippet grade only**, tagged accordingly. +- Intel docs **683673** (Agilex Configuration UG), **813918** (Agilex 5 datasheet), + **814346/813752** (Agilex 5 HPS TRM), **762191** (device security): **not read.** + intel.com and docs.altera.com returned HTTP 403 or JS-only shells to every fetch + attempted on 2026-08-21. Anything attributed to them here is search-snippet paraphrase + and is tagged **[U]** on that ground alone, regardless of how plausible it reads. + +--- + +## 1. The headline answer + +**The mechanism is sound and is reported working on Agilex 5 silicon; the latency is +unmeasured; the authentication question is open and could be fatal; and there is dated +on-silicon evidence that *repeated* configuration is where this breaks +[V mechanism / U latency / U auth / U repeat].** + +Reconfiguration on Agilex 5 is not a variant of the DE10's approach — it is a different +architecture. There is no memory-mapped FPGA manager for Main_MiSTer to write. The kernel +hands the bitstream to EL3 firmware over SMC, and the SDM writes the fabric; Linux never +parses the `.rbf` **[V, §2]**. For this manager, the only entry point to that path from +outside a kernel driver is applying a device-tree overlay **[V, §2.6]**. + +Four things stand between that and a working core switch, in descending order of how much +they could hurt: + +1. **VAB / bitstream authentication.** If Terasic's factory QSPI ships with authentication + keys provisioned, the SDM will reject unsigned community bitstreams and MiSTer-style + core switching does not exist on this board until a signing story is built. Nothing + reachable this session settles this **[U, §5.2]** — the *UM*, now read in full, is + completely silent on VAB, signing, authentication, QKY and efuses **[V, §5.2]**, which + is weak evidence for a permissive default and no more. It is cheap to settle on + hardware and expensive to be wrong about before buying hardware. +2. **Repeated reconfiguration.** MiSTer switches cores dozens of times a session. The one + dated on-Agilex-5-silicon report in evidence is a thread whose *subject* is the system + hanging when the fabric is configured a second time, with the LWH2F bridge implicated, + described in-thread as a silicon issue with no fix and a `bridge enable 0x3b` + workaround that skips the F2H bridge **[U, search-corroborated 2026-08-21, §6.1]**. + That report is U-Boot-context, not the Linux overlay path, so it is not dispositive — + but it is the opposite of an absence of evidence, and it promotes "does the region + accept a second overlay cleanly" from an unmeasured curiosity to the first thing D2.5 + should try. +3. **Agilex 5 is absent from *this* kernel — but not from upstream.** 6.18.44 has no `svc`, + `fpga-mgr` or `fpga-region` node in `socfpga_agilex5.dtsi` and no Agilex-5 compatible + string anywhere in the stack **[V, §3.1]**. An Altera-authored series adding + `intel,agilex5-soc-fpga-mgr` **with `intel,agilex-soc-fpga-mgr` as its declared DT + fallback** was posted in Nov 2025 and reviewed by the FPGA maintainer **[V *A5-series*]**. + That is the vendor asserting gen1 command-compatibility, and it means a DE25 board DTS + written today against the unmodified 6.18.44 driver has a supported path (§3.1). +4. **There is no overlay loader in mainline.** The + `/sys/kernel/config/device-tree/overlays/` workflow every vendor document and every + working community report describes is `CONFIG_OF_CONFIGFS`, an out-of-tree patch that + does not exist in 6.18.44 **[V absence / U provenance, §3.2]**. The DE25 needs its own + plumbing here, exactly as the DE10 needed its own UIO wiring. + +**UX viability.** MiSTer-style core switching is UX-viable on Agilex 5 at +**low-to-moderate confidence, on desk research only**, and the honest statement of *what* +is viable is narrower than "the user won't notice": + +- The user **will** perceive every core switch. HDMI on this board is an ADV7513 wired to + FPGA fabric pins with an FPGA-side I2C control bus **[V *UM* §3.7.3]**, so a full + reconfiguration blanks the display until the new core reinitializes the transmitter and + the monitor re-syncs. The supportable claim is **parity with the DE10**, where video is + equally fabric-owned and a switch already blanks the screen — not imperceptibility. The + DE10 baseline itself ("a few seconds") is **[U]**, unmeasured by anyone, and must be + measured as D2.5's control before any comparison is quoted. +- What *does* hold is that the reconfiguration transaction is unlikely to be the dominant + term. The kernel does no parsing or validation work proportional to core complexity — it + copies a byte stream through four 512 KiB buffers **[V §2.4]** — so per-core kernel cost + scales only with bitstream size at memcpy speed. +- Input survives the switch. The USB 2.0 OTG port is an **HPS** peripheral: a ULPI PHY + driven by the USB 2.0 controller in the HPS **[V *UM* §3.8.5]**, so controllers and their + HPS-side drivers are untouched by reconfiguration, exactly as on the DE10. +- Latency: nothing found measures it. The driver's timeouts (300 ms / 720 ms / a + caller-supplied ceiling) are ceilings, not durations **[V, §2.4]**. The honest estimate + for the reconfiguration transaction proper is **~10 ms to ~1 s**, with a real and + in-evidence mechanism for a multi-second tail (§6.2), and the largest unmeasured risk is + the *overlay* apply/remove cost — dynamic node creation and driver bind/unbind — not the + SDM's fabric write. + +## 2. The reconfiguration path, end to end + +Follow a bitstream from the SD card to configured fabric. Every step is in this tree. + +### 2.1 The file on disk + +`request_firmware()` searches, in order: the runtime-settable `firmware_class.path` +parameter, `/lib/firmware/updates/`, `/lib/firmware/updates`, +`/lib/firmware/`, `/lib/firmware` +**[V `drivers/base/firmware_loader/main.c:471-483`]**. The parameter is +`module_param_string(path, …, 0644)`, i.e. writable at runtime through +`/sys/module/firmware_class/parameters/path` **[V same file :485]** — so cores can live on +`/media/fat` and be pointed at, rather than copied into the rootfs. That matters for a +board whose rootfs is reflashed wholesale (memory: persistent state lives on `/media/fat`). + +### 2.2 The userspace call + +Userspace applies a device-tree overlay whose fragment targets the `fpga-region` node and +carries `firmware-name`. `of-fpga-region.c` reads these properties off the overlay: + +| Overlay property | Effect | Source | +|---|---|---| +| `firmware-name` | the `.rbf` to `request_firmware()` | `of-fpga-region.c:232-238` | +| `partial-fpga-config` | sets `FPGA_MGR_PARTIAL_RECONFIG` | `:223-224` | +| `external-fpga-config` | sets `FPGA_MGR_EXTERNAL_CONFIG` (already configured; skip) | `:226-227` | +| `encrypted-fpga-config` | sets `FPGA_MGR_ENCRYPTED_BITSTREAM` — **ignored by the Stratix10/Agilex manager** | `:229-230`; `stratix10-soc.c:186-193` | +| `config-complete-timeout-us` | the completion-poll ceiling; **effectively mandatory**, see §4.3 | `:246-247` | +| `region-{freeze,unfreeze}-timeout-us` | bridge timeouts; inert with no bridges | `:240-244` | + +**[V all six rows.]** An overlay that adds a *child* region carrying `firmware-name` is +rejected outright **[V `:207-214`, `:162-172`]**, and a region that already has an overlay +applied rejects a second one with "Region already has overlay applied." +**[V `:203`, `:301`]**. + +### 2.3 Overlay apply → program + +`of-fpga-region.c` registers an overlay notifier at module init +**[V `:455`]**. The notifier acts on `OF_OVERLAY_PRE_APPLY` and `OF_OVERLAY_POST_REMOVE` +and explicitly ignores everything else **[V `:353-363`, `:375-379`]**. On `PRE_APPLY` it +builds the image info and calls `fpga_region_program_fpga()` **[V `:306`]**, which: + +1. takes the region and locks the manager **[V `drivers/fpga/fpga-region.c:97-114`]**; +2. calls `fpga_bridges_disable()` — **a no-op when the bridge list is empty**, which it is + on Agilex **[V `fpga-region.c:127`; `drivers/fpga/fpga-bridge.c:190-203` is a bare + `list_for_each_entry`]**; +3. calls `fpga_mgr_load()` **[V `fpga-region.c:133`]**; +4. calls `fpga_bridges_enable()`, likewise a no-op **[V `fpga-region.c:139`; + `fpga-bridge.c:166-181`]**. + +### 2.4 Manager → SDM + +`fpga_mgr_load()` dispatches on how the image was supplied; with a `firmware_name` it goes +to `fpga_mgr_firmware_load()` **[V `drivers/fpga/fpga-mgr.c:572-582`]**, which +`request_firmware()`s the whole file into kernel memory **[V `:536-548`]** and loads it. +Because `stratix10-soc.c` implements `.write` **[V `:394`]**, the fast path applies and the +buffer is passed straight through — no scatter-gather conversion +**[V `fpga-mgr.c:50-51`, `:480`]**. Then, in `drivers/fpga/stratix10-soc.c`: + +| Step | What it does | Timeout | Line | +|---|---|---|---| +| `write_init` | `COMMAND_RECONFIG` (+ `COMMAND_RECONFIG_FLAG_PARTIAL` if asked); on OK, allocates **4 × 512 KiB** service-layer buffers | 300 ms | `:175-230`; sizes `:19-20`; alloc `:215-216`; timeout `include/linux/firmware/intel/stratix10-svc-client.h:68` | +| `write` | producer loop: fill a free buffer, `COMMAND_RECONFIG_DATA_SUBMIT`, reclaim via `COMMAND_RECONFIG_DATA_CLAIM` | 720 ms per buffer | `:250-262`, `:278-346`; timeout `stratix10-svc-client.h:69` | +| `write_complete` | poll `COMMAND_RECONFIG_STATUS` until `SVC_STATUS_COMPLETED` / `SVC_STATUS_ERROR` | caller-supplied (`config-complete-timeout-us`) | `:348-390` | + +**[V all three rows.]** The manager binds by requesting the `SVC_CLIENT_FPGA` channel at +probe, failing with `couldn't get service channel (fpga)` if the service layer is not there +**[V `:412-417`]** — the exact string community reports quote when the DT is wrong (§3.1). + +Two facts here are load-bearing for everything downstream: **Linux never parses the +bitstream** — it is a byte stream copied into buffers — and **every timeout above is a +ceiling, not a measurement**. + +Note also what `write_complete` does *not* test: it clears `SVC_STATUS_COMPLETED` and +`SVC_STATUS_ERROR` and loops on anything else **[V `:376-388`]**, so a `SVC_STATUS_BUSY` +answer re-sends `COMMAND_RECONFIG_STATUS` against a decremented budget rather than failing. +That is the mechanism behind §6.2's multi-second tail. + +### 2.5 Service layer → EL3 → fabric + +`drivers/firmware/stratix10-svc.c` is not a mailbox driver. It marshals commands into +`INTEL_SIP_SMC_FPGA_CONFIG_*` SMCCC calls issued from a CPU-0-bound kthread +**[V `:835` `arm_smccc_smc`, `:856` `arm_smccc_hvc`]**. ATF at EL3 is what actually drives +the SDM. The shared DMA region used for the bitstream buffers is obtained by asking the +secure world: `INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM` returns address and size +**[V `:696`]**, which is then `devm_memremap(…, MEMREMAP_WC)`'d into a genpool +**[V `:787`]**. The driver **never reads the DT `memory-region` property** — grep for it in +that file returns nothing **[V, 2026-08-21]**. The DT node's only job is to keep Linux off +that RAM; the binding requires it anyway **[V +`Documentation/devicetree/bindings/firmware/intel,stratix10-svc.yaml:51-64`]**. + +The status poll is where multi-second behaviour is expressible: the service thread polls +`INTEL_SIP_SMC_FPGA_CONFIG_ISDONE` once per second — `FPGA_CONFIG_STATUS_TIMEOUT_SEC 30` +**[V `:40`]**, a countdown seeded at `:283` and an `msleep(1000)` at `:295` — and reports +`SVC_STATUS_BUSY` when the countdown expires **[V `:301`]**. + +Bridge freeze/thaw during reconfiguration therefore happens **inside SDM/ATF, invisible to +Linux** — confirmed structurally by the absence of any `fpga-bridges` property on the +Agilex region node and of any arm64 Intel DTS reference to `altr,socfpga-*-bridge` +**[V `arch/arm64/boot/dts/intel/socfpga_agilex.dtsi:75-80`; grep across +`arch/arm64/boot/dts/intel/` returns nothing, 2026-08-21]**. + +### 2.6 There is no second door — for this manager + +The FPGA manager's sysfs surface is `name`, `state`, `status` — all `DEVICE_ATTR_RO` +**[V `drivers/fpga/fpga-mgr.c:655-657`]**. The region's is `compat_id`, likewise read-only +**[V `drivers/fpga/fpga-region.c:175`]**. Neither exposes a write path or an ioctl. + +**Scoped claim (corrected in refutation, §10):** *for the stratix10/agilex SoC manager*, +applying an overlay is the only way in. Mainline at large has one other userspace door: +`drivers/fpga/dfl-fme-pr.c:146` reaches `fpga_region_program_fpga()` — and hence +`fpga_mgr_load()` — from a DFL FME partial-reconfiguration **ioctl** **[V, read +2026-08-21]**. That path binds only DFL-enumerated hardware with DFL's own region and +manager, so it is unreachable on this SoC; the earlier universal phrasing ("the only +mainline way in") was falsifiable as written and has been narrowed. The consequence for +the DE25 is unchanged, and it is why §3.2 (no loader exists) is a blocker rather than a +convenience. + +## 3. The two gaps mainline does not fill + +### 3.1 Agilex 5 is absent from 6.18.44 — and being added upstream + +`socfpga_agilex5.dtsi` contains **no** `svc`, `fpga-mgr`, `fpga-region`, or bridge node. +It carries only a `reserved-memory` stub with no consumer **[V +`arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi:18-29`; whole file grepped for +`fpga`/`bridge`/`svc`, the only hits are stmmac's `agilex5` MAC compatibles and QSPI, +2026-08-21]**: + +```dts +service_reserved: svcbuffer@0 { + compatible = "shared-dma-pool"; + reg = <0x0 0x80000000 0x0 0x2000000>; /* 32 MiB at 2 GiB */ + alignment = <0x1000>; + no-map; +}; +``` + +Nor does any compatible string in 6.18.44 name Agilex 5: + +| Component | Strings it matches | Source | +|---|---|---| +| service layer driver | `intel,stratix10-svc`, `intel,agilex-svc` | `drivers/firmware/stratix10-svc.c:1133-1136` | +| service layer binding | same two | `Documentation/devicetree/bindings/firmware/intel,stratix10-svc.yaml:33-36` | +| FPGA manager driver | `intel,stratix10-soc-fpga-mgr`, `intel,agilex-soc-fpga-mgr` | `drivers/fpga/stratix10-soc.c:448-452` | +| FPGA manager binding | same two (`enum`) | `Documentation/devicetree/bindings/fpga/intel,stratix10-soc-fpga-mgr.yaml:22-25` | + +**[V all four rows.]** No driver anywhere under `drivers/` mentions `agilex5` except the +stmmac Ethernet MAC **[V grep, 2026-08-21]**. + +**What upstream is doing about it [V *A5-series*, read 2026-08-21].** Khairul Anuar Romli +(Altera) posted a series in Nov 2025, respun to v6 by 2025-11-19 and carrying a +`Reviewed-by:` from Xu Yilun: + +- `fpga: stratix10-soc: Add support for Agilex5` (2025-11-11, `lkml.iu.edu/2511.1/03492.html`) + is a **one-line** addition of `{.compatible = "intel,agilex5-soc-fpga-mgr"},` to + `s10_of_match` — no behavioural change whatsoever. +- `dt-bindings: fpga: stratix10: add support for Agilex5` (v3, 2025-11-14, + `.../05879.html`) restructures the `compatible` schema from a flat `enum` into a `oneOf` + in which Agilex 5 is `intel,agilex5-soc-fpga-mgr` **with `intel,agilex-soc-fpga-mgr` as + the declared fallback**. +- `arm64: dts: agilex5: add fpga-region and fpga-mgr nodes` (v3 2/2, 2025-11-14, + `.../05877.html`) adds the `fpga-mgr` node under an `svc` block already carrying + `method = "smc"`, `memory-region = <&service_reserved>` and `iommus = <&smmu 10>`, plus a + top-level `fpga-region`. + +Three consequences the plan should absorb: + +1. The gen1 string is not a guess any more. The **vendor** publishes it as Agilex 5's + DT-level fallback, and the driver change is a match-table entry only — so a DE25 DTS + using `intel,agilex-soc-fpga-mgr` against the **unmodified 6.18.44 driver** is the + configuration Altera is standardising, not a workaround. Whether it functions on + silicon is still **[U]** (nobody in this document has run it), but the evidence class + has moved from "inference from a shared binding enum" to "vendor-declared fallback, + maintainer-reviewed". +2. The upstream DTS's `svc` node has `iommus = <&smmu 10>` and 6.18.44's + `socfpga_agilex5.dtsi` has **no `smmu` node at all** **[V grep, 2026-08-21]**. The DE25 + board DTS must either omit that property or the DE25 must carry a newer DTSI. Whether + omitting it is safe on Agilex 5 (does SDM DMA to the svc buffer traverse an SMMU that + Linux must be told about?) is **[U]** — that is a concrete, cheap question for D0.3. +3. Whichever way the DE25 goes, it must author the `svc`/`fpga-mgr`/`fpga-region` nodes in + a board file today, because none of the series is in 6.18.44. + +*Record correction:* leg 2 of this task read the shared 6.18.44 binding enum as evidence +that the binding was intended to cover Agilex 5. That inference was unsupported by the YAML +text, which names neither Agilex 5 nor any intent, and it was demoted to **[U]** by the +refuter pass. The *A5-series* finding above independently supports the same conclusion from +a source that does say it — but it is a different, later document, and the demotion of the +original reasoning stands. + +### 3.2 There is no generic overlay loader in mainline 6.18.44 + +`of_overlay_fdt_apply()` is exported **[V `drivers/of/overlay.c:1000`, `:1090`]** and has +exactly two non-test callers in this tree, both drivers applying a built-in overlay: +`drivers/misc/lan966x_pci.c:131` and `drivers/misc/rp1/rp1_pci.c:281` (plus KUnit helpers +in `drivers/of/of_kunit_helpers.c` and `unittest.c`) **[V grep, 2026-08-21]**. There is no +`configfs.c` in `drivers/of/`, and no source file mentions `device-tree/overlays` **[V]**. + +Both callers apply exactly one overlay when their device probes and remove it when the +device goes away — `lan966x_pci.c:136`, `rp1_pci.c:299` and `:315` **[V]**. That is one +apply/remove per device lifetime. **MiSTer's pattern — dozens of apply/remove cycles per +session against a live region — has no mainline user at all**, so §11 row 4 is not merely +unmeasured, it is unexercised. Taken with the *AXE5* second-configuration hang (§6.1), +this belongs beside VAB in the headline conditions, and it is why the D2.5 plan (§6.3) +runs twenty iterations rather than one. + +The `mkdir /sys/kernel/config/device-tree/overlays/0; echo … > path` workflow that *RB-A7*, +the Altera community threads and *A5-overlay* all describe is therefore **not a mainline +interface**. It is `CONFIG_OF_CONFIGFS`, named explicitly in the community kernel-config +recipe alongside `CONFIG_OF_OVERLAY`, `CONFIG_FPGA_MGR_STRATIX10_SOC`, `CONFIG_FPGA_BRIDGE`, +`CONFIG_FPGA_REGION` and `CONFIG_OF_FPGA_REGION` **[U — *A5-overlay*, snippet grade, +2026-08-21]**. Its provenance is Pantelis Antoniou's *"OF: DT-Overlay configfs interface"* +series, posted to LKML in 2014 (v3 `lkml.rescloud.iu.edu/1403.2/01461.html`, v8 +`lkml.iu.edu/hypermail/linux/kernel/1410.3/03532.html`) and **never merged** — it survives +as a vendor-kernel carry **[U — the series exists and is dated; that the symbol vendors ship +descends from it is inference, and no vendor tree was opened this session]**. + +Leg 2 of this task reported the configfs workflow as the practical Linux-side flow; leg 1 +established from source that it does not exist here. Both are right about their own object, +and the DE25 must resolve the difference by choosing one of: + +| Option | What it costs | Notes | +|---|---|---| +| (a) carry the out-of-tree `OF_CONFIGFS` patch | one more carried patch, unmerged since 2014, no upstream prospect | matches every vendor doc and the one working Agilex 5 community recipe, so vendor recipes work verbatim | +| (b) a small board driver exposing sysfs/ioctl over `of_overlay_fdt_apply()` | ~100 lines we own, plus a UAPI we own forever | mirrors how the DE10 got its UIO wiring; no upstream dependency | +| (c) preload the fabric in U-Boot only | no runtime core switching at all | acceptable for the L1 developer OS; **not** acceptable for MiSTer parity | + +For the bare developer OS (ADR 0027 Decision 6), **(c) is sufficient and (a)/(b) are +optional** — this is the choice that lets D2 ship without solving overlay loading. Core +switching needs (a) or (b), and that is an L0/D2 decision, not one this task closes. + +## 4. What a DE25 kernel and device tree must contain + +### 4.1 Kconfig + +| Symbol | Value | Dependency | Source | +|---|---|---|---| +| `CONFIG_ARCH_INTEL_SOCFPGA` | y | — | platform | +| `CONFIG_INTEL_STRATIX10_SERVICE` | y | `ARCH_INTEL_SOCFPGA && ARM64 && HAVE_ARM_SMCCC` | `drivers/firmware/Kconfig:142-144` | +| `CONFIG_FPGA` | y | — | `drivers/fpga/Kconfig:6` (`menuconfig FPGA`) | +| `CONFIG_FPGA_MGR_STRATIX10_SOC` | y | `ARCH_INTEL_SOCFPGA && INTEL_STRATIX10_SERVICE` | `drivers/fpga/Kconfig:61-63` | +| `CONFIG_FPGA_BRIDGE` | y | — (needed only because `FPGA_REGION` depends on it; no bridge driver is used) | `drivers/fpga/Kconfig:106` | +| `CONFIG_FPGA_REGION` | y | `FPGA_BRIDGE` | `drivers/fpga/Kconfig:145` | +| `CONFIG_OF_FPGA_REGION` | y | `OF && FPGA_REGION` | `drivers/fpga/Kconfig:153` | +| `CONFIG_OF_OVERLAY` | y | **not selected by `OF_FPGA_REGION`** — see below | `drivers/of/Kconfig:105-109` | +| `CONFIG_FW_LOADER` | y | default y | `drivers/base/firmware_loader/Kconfig:4-7` | + +**[V all rows.]** `CONFIG_SOCFPGA_FPGA_BRIDGE` and `CONFIG_FPGA_MGR_SOCFPGA` are **Cyclone V +/ Arria 10 only** and must not be set **[V `drivers/fpga/Kconfig:15`, `:112`]**. + +**The `OF_OVERLAY` trap [V].** `OF_FPGA_REGION` is `depends on OF && FPGA_REGION` with no +`select` **[V `drivers/fpga/Kconfig:153-155`]**, and with `OF_OVERLAY=n`, +`of_overlay_notifier_register()` is a static-inline stub returning 0 +**[V real prototype `include/linux/of.h:1763`, stub `:1784`; `drivers/of/Makefile` builds +`overlay.o` only under `CONFIG_OF_OVERLAY`]**. The FPGA region driver therefore *registers +successfully at boot and its notifier can never fire*. A defconfig missing +`CONFIG_OF_OVERLAY` is silently non-functional — no error, no warning, no reconfiguration. +Given this repo's standing lesson that `linux.config` is a minimal defconfig and an absent +symbol is not necessarily off (memory: `linux-config-is-minimal-defconfig`), this belongs in +`scripts/check-kernel-defconfig-sync.sh`'s per-board sentinel set when D1.2 parameterizes it. + +### 4.2 Device-tree nodes to author + +Modelled on `socfpga_agilex.dtsi:63-80` **[V]** and cross-checked against the *A5-series* +DTS patch **[V]**, added by the DE25 board DTS on top of the in-tree +`socfpga_agilex5.dtsi` (which already provides `service_reserved`): + +```dts +/ { + firmware { + svc { + compatible = "intel,agilex-svc"; /* [U] gen1 string on Agilex 5 silicon */ + method = "smc"; + memory-region = <&service_reserved>; + /* upstream's Agilex 5 svc node also has iommus = <&smmu 10>; + 6.18.44's socfpga_agilex5.dtsi has no smmu node — §3.1 note 2 [U] */ + + fpga_mgr: fpga-mgr { + /* upstream will prefer: + compatible = "intel,agilex5-soc-fpga-mgr", + "intel,agilex-soc-fpga-mgr"; + 6.18.44's driver only matches the second — [V §3.1] */ + compatible = "intel,agilex-soc-fpga-mgr"; + }; + }; + }; + + base_fpga_region: fpga-region { + compatible = "fpga-region"; + #address-cells = <0x2>; + #size-cells = <0x2>; + fpga-mgr = <&fpga_mgr>; + }; +}; +``` + +Writing the two-string form now is free and forward-compatible: 6.18.44 matches on the +fallback, a newer kernel matches on the specific string. + +Note what is *not* there: no `fpga-bridges` property, and no bridge nodes at all — the +Cyclone V shape (`fpga_bridge0..3` at `0xff400000`/`0xff500000`/`0xff600000`/`0xffc25080` +plus `fpgamgr@ff706000` and a `base_fpga_region` at `:90`, +`arch/arm/boot/dts/intel/socfpga/socfpga.dtsi:90,526-561` **[V]**) has no Agilex analogue +and must not be transliterated. + +One practical warning from the one working community report: the base DT must not describe +fabric peripherals that the *pre-switch* bitstream does not implement — *A5-overlay*'s +resolution involved stripping fabric-side nodes (LEDs, a `soc@0` section, a USB node) from +the main DT before the overlay would apply, and the characteristic failure is +`OF: overlay: find target, node: /fragment@0, path '/soc/base_fpga_region' not found` +**[U, snippet grade, 2026-08-21]**. For MiSTer this generalises to: the *base* DT describes +only HPS-side hardware; everything fabric-side arrives with the core's overlay (§7.3). + +### 4.3 The overlay a core switch applies + +```dts +/dts-v1/; +/plugin/; + +&base_fpga_region { + #address-cells = <0x2>; + #size-cells = <0x2>; + firmware-name = "cores/minimig.core.rbf"; + config-complete-timeout-us = <30000000>; /* mandatory in practice — see below */ + + /* per-core child nodes describing what this bitstream exposes, + if the DE25 ends up carrying core-specific DT fragments — §7.3 */ +}; +``` + +**`config-complete-timeout-us` is effectively mandatory on this manager [V code / U +consequence].** `fpga_image_info_alloc()` uses `devm_kzalloc`, so the field defaults to 0 +**[V `drivers/fpga/fpga-mgr.c:115`]**; `of-fpga-region.c:246-247` overwrites it only if the +property exists; `stratix10-soc.c:356` does `usecs_to_jiffies(0)` → 0 and passes that to +`wait_for_completion_timeout()` **[V `:356-370`]**. An overlay omitting the property will +**almost always** fail immediately with `timeout waiting for RECONFIG_COMPLETED` / +`-ETIMEDOUT` rather than waiting — *almost*, because `wait_for_completion_timeout(x, 0)` +returns non-zero if the completion is already signalled, so a callback landing in the window +between `reinit_completion()`/send and the wait would let it through **[V code / U +consequence — corrected from "will fail" during refutation]**. This is inferred from source, +not executed: it is a one-line experiment at D2.5, and it should be run more than once for +exactly that race. It also explains why both *RB-A7*'s Agilex 7 example and the *A5-overlay* +working recipe set 30 s. + +## 5. RBF formats and the authentication question + +### 5.1 Formats + +- HPS-first splits the design into a **phase-1** `.periph.rbf` (periphery + HPS + pin/DDR bring-up, FSBL embedded, built with `quartus_pfg -o hps=on -o hps_path=…`, + QSPI-resident) and a **phase-2** `.core.rbf` (the fabric image, SD-resident) + **[V *GHRD*, retrieved 2026-08-21; matches `de25-boot-chain.md` §2 steps 2-5]**. +- **Linux does not parse either.** `request_firmware()` reads the file whole and + `stratix10-soc.c` copies it into service-layer buffers verbatim **[V §2.4]**. Format + compatibility — RBF version, Quartus version, device match — is entirely an SDM/ATF + concern, and any mismatch surfaces in Linux only as an SVC error status. That is a + diagnosability cost worth writing down before D2.5 debugging starts. +- **Partial reconfiguration** is expressible (`partial-fpga-config` → + `COMMAND_RECONFIG_FLAG_PARTIAL` **[V `of-fpga-region.c:223-224`; + `stratix10-soc.c:186-193`]**) but whether Agilex 5 E-series supports PR, and whether a + MiSTer-style core would ever want it rather than full reconfiguration, is **[U]** and out + of scope here. +- **Encryption:** `encrypted-fpga-config` sets a flag this driver ignores + **[V `stratix10-soc.c:186-193` — only `FPGA_MGR_PARTIAL_RECONFIG` is tested]**. Encrypted + bitstreams are handled transparently by SDM or not at all; the Linux property is inert + here. +- No cross-version compatibility requirement between the QSPI-resident phase-1 image and an + SD-resident phase-2 `core.rbf` was found stated anywhere; a direct fetch of the *GHRD* + boot-examples page returned "not addressed in this document" for that question + **[U, retrieved 2026-08-21]**. This corroborates — it does not resolve — + [`de25-boot-chain.md`](de25-boot-chain.md) §5's treatment of version skew as a + project-side risk managed by convention. + +### 5.2 VAB / authentication — the project-defining unknown + +What is supported by a source: once QKY/efuse authentication keys are provisioned on an +Agilex device, the SDM will no longer accept a configuration bitstream that is unsigned or +signed with the wrong key; U-Boot's VAB validates SHA384-signed FIT images (U-Boot, ATF, +kernel, DTB) through the SDM **[U — Altera Community thread 317341 and the u-boot-list VAB +series (mail-archive msg399550/397090/399718), search-snippet only; intel.com doc 762191 +§Device Security returned HTTP 403 on 2026-08-21]**. + +What is **not** established, and is the question that decides whether this board can host +MiSTer at all: + +1. Does an **unprovisioned** Agilex 5 accept unsigned bitstreams? The converse of the quote + above implies yes, but no source states it. **[U]** +2. Does **Terasic's factory QSPI image on the DE25-Nano** ship with keys provisioned? + **The *UM* rev. 2025-09-05 was read in full this session and contains zero occurrences + of VAB, authentication, signing, QKY, efuse or secure boot [V — text extracted from the + DigiKey mirror and grepped, 2026-08-21].** Silence is weak evidence for a permissive + default and is not proof; a board manual would not necessarily mention provisioning + either way. + +The evidence base supports "very likely permissive out of the box" and supports nothing +stronger. Because a wrong answer here invalidates the entire core-switching premise — +not the schedule, the premise — this should be settled *before* hardware money is +committed if a document can be obtained, and in the first hour of D2.5 otherwise. + +## 6. Latency + +### 6.1 Evidence classes, kept apart + +| # | Evidence | Class | What it actually says | Tag | +|---|---|---|---|---| +| 1 | Config time = bitstream ÷ (config clock × bus width); ~250 MHz internal config clock off OSC_CLK_1 | vendor figure, **unread** | a formula, no Agilex-5 numbers to put in it | **[U]** doc 683673 403'd, snippet paraphrase, 2026-08-21 | +| 2 | Agilex 5 datasheet "Configuration Bit Stream Sizes" table exists; A5EB013 row unread | vendor figure, **unread** | nothing usable | **[U]** doc 813918 is a JS SPA, 2026-08-21 | +| 3 | AXE5-Eagle `design.core.rbf` = 2,568,192 bytes (A5ED065/A5ED043, 656K/334K LE) | community report, dated | one real Agilex-5 bitstream size, on a **larger** die than A5EB013 (138K LE) | **[V]** *AXE5* via Wayback 20260215052804 (draft's copy; not re-fetchable 2026-08-21) | +| 4 | Same log: `2568192 bytes read in 124 ms (19.8 MiB/s)` then `FPGA reconfiguration OK!`, **no timestamp for the `fpga load` step** | community report, dated, **U-Boot context** | the 124 ms is the eMMC read; the SDM write is untimed but drew no comment. Bounds at most the SDM-write phase — **not** the Linux overlay transaction | **[V figure / U applicability]** as above | +| 4a | **The same thread's subject is the failure, not the success**: the system hangs when `core.rbf` is configured a second time or after a warm reboot; the LWH2F bridge is implicated; described in-thread as a silicon issue with no fix; workaround is `bridge enable 0x3b` to skip enabling the F2H bridge, possibly with F2H disabled in the Quartus design too | community report, adverse | dated on-Agilex-5-silicon evidence *against* repeated configuration — in U-Boot, not the Linux overlay path | **[U]** search-corroborated 2026-08-21 (thread 312795 title + resolution); primary 403, Wayback unreachable this session | +| 5 | Driver ceilings: 300 ms request, 720 ms/buffer, 4 × 512 KiB in flight | source, exact | upper bounds the firmware authors chose; **not durations** | **[V]** `stratix10-soc.c:19-20`; `stratix10-svc-client.h:68-69` | +| 6 | *RB-A7* overlay sets `config-complete-timeout-us = 30000000`; the *A5-overlay* working recipe sets the same | analogy (Agilex **7**) + ceiling | a 30 s safety ceiling; the one dmesg timestamp on that page is time-since-boot | **[U]** retrieved 2026-08-21 | +| 7 | Service-layer status poll: `msleep(1000)` per iteration, `FPGA_CONFIG_STATUS_TIMEOUT_SEC 30`, `SVC_STATUS_BUSY` on expiry, which `write_complete` does not test and so re-sends | source, exact | **any configuration not complete at the first status poll quantizes to ≥1 s** | **[V]** `stratix10-svc.c:40,283,295,301`; `stratix10-soc.c:376-388` | +| 8 | DE10 core switch "a few seconds", dominated by SD read + Main_MiSTer reinit | inference, **unmeasured** | plausible, and the baseline this is compared against — but nobody has measured the DE10 split either | **[U]** unsourced inference; D2.5 should measure the DE10 control | + +Note what rows 5 and 6 are not: a 720 ms per-buffer timeout does not mean a buffer takes +720 ms, and a 30 s completion ceiling does not mean configuration takes 30 s. Reading +either as a duration is the most likely way this section gets misquoted downstream. + +Row 4a was added by the refutation pass. The draft cited *AXE5* only for its favourable +timing figure while the thread is, in substance, an adverse report about exactly the +condition (§11 row 4) the draft called "completely unmeasured". Citing the favourable half +of the only on-silicon source in evidence was a selective read and is corrected here. + +### 6.2 The estimate + +First-principles arithmetic over rows 1 and 3 (a **[U]** clock times a **[V]** size, so the +product is **[U]**): 2.5 MB at 250 MHz × 4–8 lanes ≈ 500 MB/s–1 GB/s gives 2–5 ms of raw +fabric write. Add mailbox round-trips, DMA handshakes, and the fact that A5EB013 is a +smaller die whose MiSTer-scale bitstream plausibly lands at 1–4 MB (compression tracks used +content more than LE count **[U, general property]**). + +| Phase | Estimate | Basis | Confidence | +|---|---|---|---| +| Storage read of a 1–4 MB `.rbf` | 50–500 ms | row 4 (measured on Agilex 5, different medium, U-Boot) | medium | +| `request_firmware` + copy into 4 × 512 KiB buffers | < 50 ms | arithmetic over row 5's buffer geometry | medium-low | +| SDM fabric write proper | 2–300 ms, **or ≥1 s in ≥1 s quanta if not done at the first status poll** | rows 1 + 3 arithmetic; row 7 for the quantization | **low** | +| Overlay apply/remove: node create/destroy, driver bind/unbind | **unmeasured, no bound, and unexercised by any mainline user** (§3.2) | nothing found | **none** | +| Userspace teardown/reinit | framework-defined, L0 | — | n/a | + +**Stated as one range: the reconfiguration transaction (overlay apply → fabric running) is +~10 ms to ~1 s, low confidence, with a pessimistic tail to a few seconds.** That tail is not +hand-waving: row 7 is a concrete in-tree mechanism for it — the service layer polls +configuration status once per second, so anything not finished when the first +`ISDONE` lands costs a whole second, and a `SVC_STATUS_BUSY` answer is silently retried +rather than surfaced. *The draft's sentence "nothing in the evidence puts it above one +second" was refuted on the draft's own citations and has been deleted.* Row 4 remains the +strongest single favourable signal and it is both an absence-of-complaint argument and a +U-Boot measurement that cannot speak to the overlay half of the transaction. + +The real risk is still the row with no bound. The DE10 switches cores by writing a +memory-mapped register from userspace; the DE25 would tear down and rebuild a slice of the +live device tree on every switch. That is categorically heavier, it has no mainline +precedent at MiSTer's cycle frequency (§3.2), there is dated on-silicon evidence of a +second-configuration hang on a related path (row 4a), and it is the thing D2.5 must +actually measure. + +### 6.3 D2.5 hardware-measurement plan (runnable) + +Prerequisite: a DE25 board booted to Linux with §4's DT and defconfig, an overlay loader +per §3.2, and one `core.rbf` built for A5EB013. + +```sh +# 0. Establish that the stack bound at all. +ls -d /sys/class/fpga_manager/*/ && cat /sys/class/fpga_manager/fpga0/name +cat /sys/class/fpga_manager/fpga0/state # expect "operating"/"unknown", not absent +ls -d /sys/class/fpga_region/*/ +dmesg | grep -iE 'stratix10|svc|fpga' # record the SMC/shared-mem probe lines. + # "couldn't get service channel (fpga)" + # means the svc node is wrong (§2.4) + +# 1. Record the bitstream size — the missing input to §6.2's arithmetic. +stat -c '%n %s' /lib/firmware/cores/*.rbf + +# 2. Isolate the storage read from the SDM write (drop caches, then warm). +cp /media/fat/cores/x.core.rbf /lib/firmware/cores/ +sync; echo 3 > /proc/sys/vm/drop_caches +S=$(date +%s.%N); cat /lib/firmware/cores/x.core.rbf > /dev/null; E=$(date +%s.%N) +echo "cold read: $(echo "$E-$S" | bc)" # repeat warm; the delta is the read cost + +# 3. Time the reconfiguration transaction itself, warm cache, 20 iterations. +for i in $(seq 20); do + S=$(date +%s.%N) + # the §3.2 loader, whichever option won + E=$(date +%s.%N); echo "apply $i $(echo "$E-$S" | bc)" + S=$(date +%s.%N) + + E=$(date +%s.%N); echo "remove $i $(echo "$E-$S" | bc)" +done +# Report min/median/max for apply and remove separately. Iteration 1 vs 2..20 is THE +# question (§6.1 row 4a): does the region accept a second overlay cleanly, does HPS->fabric +# access still work after the second configuration, and does anything leak? Watch dmesg for +# the OF property-leak warnings community reports mention, and probe an LWH2F-window read +# after every switch, not just the first. + +# 4. Kernel-side attribution for one run. +echo 1 > /sys/kernel/debug/tracing/events/enable # or: dynamic_debug on drivers/fpga +dmesg -c >/dev/null; ; dmesg | ts # 'Requesting full reconfiguration' -> + # 'RECONFIG_COMPLETED' is the SDM window. + # A window that is a near-exact multiple + # of 1 s means row 7's poll dominated. + +# 5. The authentication question (§5.2) — do this FIRST, it is binary. +# Load an unsigned, locally built core.rbf. If SDM rejects it, everything above is moot. + +# 6. Failure-mode checks worth one run each. +# - overlay WITHOUT config-complete-timeout-us -> expect -ETIMEDOUT (§4.3); run it +# several times, the zero-jiffy wait has a benign race +# - a truncated .rbf -> confirm the error is diagnosable, not a hang +# - apply an overlay while one is already applied -> expect "Region already has overlay +# applied" (of-fpga-region.c:203) +``` + +Deliverable: a table of min/median/max for read, apply, remove, and SDM window; the +bitstream size; and a yes/no on unsigned acceptance. That table replaces §6.2 wholesale and +resolves five of §11's rows. **A DE10 control run** instrumenting `fpga_io.cpp`'s core-load +path is not optional garnish — §1's "parity with the DE10" claim is unfalsifiable until the +DE10 baseline is a number rather than "a few seconds". Do it in the same session. + +## 7. HPS↔FPGA memory semantics + +### 7.1 What Linux can see + +Nothing. Unlike Cyclone V — four address-bearing bridge nodes with real windows and two +in-tree bridge drivers **[V `arch/arm/boot/dts/intel/socfpga/socfpga.dtsi:526-561`; +`drivers/fpga/altera-hps2fpga.c`, `altera-fpga2sdram.c`]** — the Agilex family exposes no +bridge device to Linux at all **[V §2.5]**. Bridge control is SDM/ATF's. Whatever apertures +exist are things a board DTS must *declare* from documentation, not things the kernel +discovers. The *AXE5* report (§6.1 row 4a) is a reminder that this invisibility cuts both +ways: when a bridge misbehaves after a second configuration, Linux has no driver, no sysfs +and no error path through which to notice. + +### 7.2 What the documentation says (weakly) + +Agilex 5's HPS reportedly presents three MPU-visible H2F windows totalling 256 GB (1 GB at +`0x0_4000_0000`, 15 GB at `0x4_4000_0000`, 240 GB at `0x44_0000_0000`) with LWH2F at +`0x0_2000_0000`, and carries a CCU whose F2H/F2SDRAM datapaths are steerable per +transaction via an AXUSER signal the fabric master drives — meaning **coherency is opt-in +at fabric-IP design time, not a SoC-wide guarantee** **[U — Intel HPS TRM 814346/813752 +returned HTTP 403 to every fetch on 2026-08-21; these are search-snippet paraphrases +corroborated across snippets but the primary document was never read]**. *Leg 4 of this task +tagged the address windows [V]; they are demoted to [U] here on the house rule that a +snippet is not a read document, and both refuters upheld the demotion. F2H and F2SDRAM +window addresses and widths were not recovered at all.* + +If the AXUSER/CCU story holds, the consequence for MiSTer is concrete and unpleasant: a +frame reader or DMA master in the fabric must be *built* to assert coherency, and the HPS +side cannot assume cache maintenance is free. That is a gateware-design constraint, i.e. an +L0 constraint, and it is precisely the "jointly accessible when required" phrase from plan +§1 refusing to mean anything until measured. + +### 7.3 What a MiSTer-style framebuffer would require + +The DE10 shape, for reference: `MiSTer_fb` is a plain fbdev platform driver that +`memremap()`s a fixed window — `reg = <0x22000000 0x800000>` (8 MiB), IRQ SPI 40 — publishes +`/dev/fb0` with pixels at `+4096`, and turns the FPGA's per-frame interrupt into +`FBIO_WAITFORVSYNC` **[V `board/mister/de10nano/linux-patches/0001-fbdev-add-MiSTer_fb-driver.patch:6-11,31-45`]**. + +Ported to DE25 that needs four things, three of which are not this repo's to decide: + +| Requirement | Status | +|---|---| +| An HPS-visible DRAM window the fabric can write | **LPDDR4A** is the bank that can implement the HPS hard EMIF — *UM* §3.7.4: "The I/O bank where LPDDR4A is located can implement the Intel Agilex 5 FPGA EMIF IP with the Hard Processor Subsystem (HPS). If no HPS EMIF IP is used in a system, the LPDDR4A bank can be used for the EMIF IP of the FPGA", and the HPS feature list reads "1GB LPDDR4 (32-bit data bus), share with FPGA" **[V *UM* p.7, §3.7.4]**. Putting the framebuffer in LPDDR4B would force the HPS to read *through* H2F into the fabric's own EMIF — the reverse direction, structurally worse, unprecedented in MiSTer's model. | +| A `/reserved-memory` `no-map` node over that window, plus the window's address | needs the GHRD; address is **[U]** | +| A per-frame interrupt from fabric to HPS | **the doorbell problem again** — see §8. `simplefb` has no vsync-interrupt concept and cannot serve `FBIO_WAITFORVSYNC` **[U — inferred from `drivers/video/fbdev/simplefb.c`'s static-buffer binding, not from an explicit statement]**, so a forward-ported `MiSTer_fb` beats DRM/simplefb for this ABI | +| Coherency or an explicit cache-maintenance discipline | **[U]**, §7.2 | + +The port cost is plausibly DT content and a window rather than a rewrite: the ioctl ABI is +mainline UAPI with a fixed-width `__u32` argument and is architecture-independent +**[V patch header, ABI section]**. *This is an inference, not a verified audit* — the cited +patch lines establish the ABI's architecture-independence and list the 6.18 API churn, not +that the driver body is free of arm32 assumptions; the refuter pass flagged the original +**[V]** as leaning past its citation and it is restated as inference here. Everything +expensive is upstream of the driver either way. + +### 7.4 DP-10 — closable now + +DP-10's premise is "**if** the HDMI 2.0 transmitter is HPS-reachable". It is not, and the +premise is doubly wrong: the transmitter is fabric-owned, and it is not a 2.0-class part. + +- *UM* §3.7.3 is filed under **§3.7 "Peripherals Connected to the FPGA"**, not §3.8 + "Peripherals Connected to the Hard Processor System", whose subsections are exhaustively + push-buttons/LEDs, Gigabit Ethernet, UART-to-USB, micro SD, USB 2.0 OTG and the + accelerometer — **no HDMI** **[V *UM* contents pp.1-2, §3.8]**. +- The video path is FPGA pins: `HDMI_TX_D0..D23` and the clocks are FPGA pin assignments + (Table 3-13), and the control bus is `HDMI_I2C_SCL PIN_BT1` / `HDMI_I2C_SDA PIN_BW2`, + FPGA pins, alongside a separate `HPS_I2C_SCL/SDA` pair for the HPS controller (Table 3-7) + **[V *UM* §3.6, §3.7.3]**. +- **Capability footnote:** *UM* §3.7.3 says the ADV7513 "incorporates HDMI v1.4 features, + including 3D video support and 165MHz support for all video formats up to 1080p and + UXGA", while Terasic's own feature bullet on p.7 says "HDMI 2.0 Output Port (Support + 1080P)" **[V both, *UM*]**. That is an internal contradiction in the manual; the body + text describing the actual part is the primary figure, and this project's docs (including + the D0.2 brief) inherited the marketing "2.0". Use v1.4 / 165 MHz / 1080p. +- The MIPI D-PHY connector (§3.7.7) is likewise fabric-wired **[V *UM*]**. + +**DP-10's stated unknown is answered: the display transmitter is fabric-owned and +1.4-class, so the "kernel DRM path independent of the fabric scaler" fork does not exist on +this board.** DP-10 stays tabled — but tabled with its question closed rather than open, +and plan §6's "[U, D0.1]" tag on it can be retired. + +One residual **[U]**: *UM* §3.6 refers to "Figure 3-13 Control mechanism for the I2C +multiplexer" and says the board's I2C devices "are connected to the HPS and FPGA I2C bus +independently". The figure is an image and was not readable from the extracted text, so +whether the ADV7513's *control* I2C can be muxed onto the HPS bus is not settled. It does +not change the verdict: the 24-bit video bus and clocks are unambiguously FPGA pins, so +there is no HPS-side scanout regardless of who owns the I2C. + +## 8. DP-9 — verdict + +**Confirmed on the decision. Refuted on the rationale. The scope must be narrowed in the +record.** Both refuters upheld this split verdict; neither refuted it. + +DP-9 as written: *"the DE25 (including any RT variant) adopts the Agilex-native idioms: +DTS/fpga-region configuration in place of the carried UIO doorbell patches, provided D0.2 +confirms that is the proper architecture. The beta-series UIO patches (0043–0045) are not +ported by default."* That sentence contains two claims. They have different answers. + +**Claim A — fpga-region is the proper reconfiguration architecture: CONFIRMED [V].** Not +merely native — *sole*, for this manager. There is no memory-mapped FPGA manager on Agilex +to write (§2.5); the manager's and region's sysfs are read-only and there is no ioctl on +this path (§2.6); the only route to `fpga_mgr_load()` from userspace on this SoC is an +overlay apply (§2.3). A "port the DE10 approach" alternative does not exist to be weighed. +The *A5-series* upstream patches (§3.1) are Altera standardising exactly this shape for +Agilex 5, which is independent confirmation from the vendor. + +Separately, patches 0043 and 0044 are *literally* unportable: 0043 allocates Cyclone V GIC +SPI cells 48..55 for `f2h_irq8..15`, and 0044 names `0xff200000 + 0x200000` (the lwhps2fpga +window) and `0x20000000 + 0x20000000` (the DDR3 f2sdram aperture) **[V patch texts, read +2026-08-21]**. None of those addresses or interrupt numbers exists on Agilex 5, whose base +DTSI has no bridge nodes at all (§3.1). "Not ported by default" is the only correct +disposition, and it would be correct even if fpga-region did not exist. + +**Claim B — fpga-region stands "in place of" 0043–0045: REFUTED [V].** The overlay notifier +fires on `OF_OVERLAY_PRE_APPLY` and `OF_OVERLAY_POST_REMOVE` and explicitly declines +everything else **[V `of-fpga-region.c:353-379`]**. It has nothing to say about how a +*running* core signals the HPS, or how the HPS reaches that core's registers and shared +memory. That is exactly and only what 0043 (blocking `read()` on `/dev/uioN` replacing a +cause-register spin) and 0044/0045 (named, size-bounded, optionally write-combined +apertures replacing `/dev/mem`) were written to do **[V patch rationale sections]**. All +four D0.2 research legs reached this independently, no leg dissented, and both refuters +confirmed it against the source. + +Patch **0045** deserves separate mention: it is a generic, arch-independent UIO kernel +feature (`UIO_MEM_PHYS_WC = 6` in `include/linux/uio_driver.h`, an `else if` in +`uio_mmap_physical`, property parsing in `uio_pdrv_genirq`) fixing a page-attribute +throughput floor measured at ~100 MB/s write / ~54 MB/s read on Cyclone V **[V patch body, +read 2026-08-21]**. Normal-NC vs Device-nGnRnE is the same distinction on ARMv8, and nothing +in this task shows the floor disappears on Agilex 5's HPS↔FPGA path. DP-9 does not settle +0045's question and should not be cited as having done so. + +**What the record should say instead.** DP-9's proviso is met, narrowed to: *the DE25 adopts +DTS/fpga-region as its bitstream-loading architecture; the carried UIO patches 0043–0045 are +not ported, because 0043/0044 encode Cyclone-V-only addresses and interrupt numbers and +0045 has nothing to attach to until a GHRD exists.* The **runtime HPS↔FPGA signaling and +aperture contract is not decided by DP-9 and is not closed by D0.2.** It is gated on L0 +(a Main_MiSTer aarch64 HAL) and on a GHRD defining the topology, and its likely answers are +(a) a doorbell-style UIO binding again, re-derived for Agilex 5, (b) per-core DT fragments +shipped alongside each `.rbf` and applied by the same overlay, or (c) a native binding once +Intel documents one. That deserves its own DP rather than being absorbed into DP-9's +confirmation — an accepted-and-forgotten DP-9 would leave the project believing a solved +problem where it has an unexamined one. + +**Record edit this document does not make — action item.** Both refuters made the +"confirmed" enum conditional on the narrowing landing as an actual edit to +[`de25-nano-plan.md`](de25-nano-plan.md) §6, whose DP-9 bullet still reads "in place of the +carried UIO doorbell patches", and on a new DP being opened for the runtime +signaling/aperture/coherency contract (§11 row 11). **That edit has not been made here** — +D0.2's brief scoped this task to writing this dossier and adding one cross-link to +[`de25-boot-chain.md`](de25-boot-chain.md), and unilaterally rewriting a plan decision +record is outside it. It is recorded as a blocking follow-up: until it lands, plan §6 reads +as having settled a problem D0.2 explicitly found unsettled. + +**Confirming DP-9 because it was written down would have been the failure mode here.** It +survives because the alternative architecture does not exist, not because the plan said so; +and half its sentence does not survive at all. + +## 9. Where this task's legs disagreed + +Recorded so the disagreements are not silently resolved by whoever reads this next. + +| Subject | Disagreement | Resolution here | +|---|---|---| +| Userspace overlay loading | Leg 2: the practical flow is `/sys/kernel/config/device-tree/overlays/`. Leg 1: `of_overlay_fdt_apply` has no generic caller in this tree. | Both correct about their object. Mainline 6.18.44 has no such interface **[V]**; vendor kernels carry `CONFIG_OF_CONFIGFS`, descended from a 2014 series never merged **[U provenance]**. §3.2. | +| `intel,agilex-soc-fpga-mgr` on Agilex 5 | Leg 2 read the 6.18.44 binding enum as intent to cover Agilex 5. Legs 1 and 4 found no Agilex-5 string anywhere. | Leg 2's *reasoning* is unsupported and stays demoted to **[U]**; its *conclusion* is now independently supported by the Nov-2025 *A5-series*, which declares the gen1 string as Agilex 5's fallback. §3.1. | +| Agilex 5 H2F/LWH2F windows | Leg 4 tagged them **[V]** from corroborated search snippets. | Demoted to **[U]** — the TRM was never read (403). Both refuters upheld. §7.2. | +| Whether DP-9's proviso is met | Legs 1 and 4 leaned "met only for loading"; legs 2 and 3 leaned "met, with the doorbell purpose merely unaddressed". | Same finding, different emphasis. Resolved as §8's split verdict, which both refuters upheld. | + +## 10. Refutation record + +**Pass ran 2026-08-21, two refuters, both against the pre-adversarial draft of this +document.** + +**Lenses.** (1) *Evidence-chain* — re-open every cited file at every cited line, check the +source says what is attributed to it, hunt for drifted or fabricated citations and for +selective reads. (2) *UX viability, adversarial* — argue as hard as the evidence permits +that core switching is **not** viable on Agilex 5 (latency tail, blackout and peripheral +loss during reconfiguration, overlay apply/remove reliability at MiSTer switch frequency, +authentication, driver rebind), then judge whether that case wins; and separately, whether +dropping 0043–0045 for fpga-region idioms is actually sufficient. + +**Outcome.** Neither conclusion was overturned. The UX-viability conclusion **survives**; +the DP-9 split verdict **survives**. Both survived unanimously — no minority refutation to +carry — but both were amended, and the amendments are substantive rather than cosmetic. + +**Verification I did myself before writing (2026-08-21).** I re-opened, at the cited lines, +`stratix10-soc.c`, `of-fpga-region.c`, `fpga-mgr.c`, `fpga-region.c`, `fpga-bridge.c`, +`stratix10-svc.c`, `dfl-fme-pr.c`, `firmware_loader/main.c`, the four Kconfig files, +`include/linux/of.h`, both binding YAMLs, `socfpga_agilex5.dtsi`, `socfpga_agilex.dtsi` and +`socfpga.dtsi`, plus the four DE10 patch texts. Corrections applied to line numbers the +draft got slightly wrong: `fpga_bridges_disable` is at `fpga-bridge.c:190` (draft said 181); +`fpga_image_info_alloc`'s `devm_kzalloc` is at `fpga-mgr.c:115` (draft cited a range); +`stratix10-svc.c`'s compatible table is at `:1133-1136` (draft said 1134-1135); the manager +match table runs to `:452`; `CONFIG_FPGA` is a `menuconfig` at `drivers/fpga/Kconfig:6`. No +fabricated citation was found in the draft. I also fetched and text-extracted the *UM* PDF +myself, upgrading four claims from snippet grade to read-document grade, and found the +*A5-series* upstream patches, which neither the draft nor either refuter had. + +**Corrections accepted and applied.** + +| # | From | Correction | Where | +|---|---|---|---| +| 1 | R1 | *AXE5* is not a neutral timing datapoint — the thread's subject is a second-configuration hang. Add as adverse evidence. | §6.1 row 4a, §1 item 2, §6.2, §6.3 step 3 | +| 2 | R1 | Delete "nothing in the evidence puts it above one second"; the `msleep(1000)`/30 s status poll is an in-evidence multi-second mechanism. | §2.5, §6.1 row 7, §6.2 | +| 3 | R1 | Scope "the only mainline path to `fpga_mgr_load()`" to this manager; `dfl-fme-pr.c:146` is a userspace-ioctl counterexample. | §2.6 | +| 4 | R1 + R2 | Drop "HDMI 2.0" for the ADV7513; the *UM* body says v1.4 / 165 MHz / 1080p, and the manual contradicts itself. | §7.4 | +| 5 | R1 | Soften "will fail" to "will almost always fail" for a missing `config-complete-timeout-us`; a benign race exists. | §4.3, §6.3 step 6 | +| 6 | R1 | The `MiSTer_fb` port-cost **[V]** leans past its citation; restate as inference. | §7.3 | +| 7 | R2 | "Very unlikely to be perceived" is over-written; every switch blanks fabric-owned HDMI. Claim parity with the DE10 instead, and flag that the DE10 baseline is itself unmeasured. | §1, §6.3 | +| 8 | R2 | Add the two *UM*-verified peripheral facts: USB 2.0 OTG is HPS-side (input survives); HDMI is fabric-side (display does not). Cite the fetchable mirror. | §1, §7.4 | +| 9 | R2 | Both `of_overlay_fdt_apply()` callers apply once per device lifetime; MiSTer's cycling has no mainline precedent. | §3.2 | +| 10 | R2 | Scope *AXE5* to the SDM-write phase; it is a U-Boot measurement, not the Linux overlay transaction. | §6.1 row 4, §6.2 | +| 11 | R2 | "No per-core kernel work proportional to core complexity" → "no parsing or validation work"; the copy is size-proportional. | §1 | + +One refinement of my own to correction 9: R2 wrote that the two callers "never remove" +their overlay. They do — `lan966x_pci.c:136`, `rp1_pci.c:299,315` **[V]** — on device +teardown. The accurate statement, used above, is *one* apply/remove per device lifetime, +never repeated cycling in service. R2's conclusion is unaffected. + +**Correction not applied, and why.** R2's requirement that the DP-9 narrowing "must land as +an actual edit to `docs/de25-nano-plan.md` §6" and that a new DP be opened is **sound and +accepted in substance, but out of this task's scope to execute**: D0.2's brief is this +dossier plus one cross-link in `de25-boot-chain.md`, and rewriting a decision record in the +plan unilaterally — while another agent may be editing those docs — is not mine to do here. +It is carried as an explicit blocking action item in §8 and as §11 row 11 rather than +dropped. Anyone accepting §8's "confirmed" enum inherits that edit. + +**Not challenged by either refuter** (i.e. verified and undisputed, so quotable): the +SMCCC-to-EL3 architecture and `GET_MEM` shared-buffer discovery; the full +overlay→program→load→SDM sequence; the buffer geometry and every timeout being a ceiling; +the ignored `encrypted-fpga-config` flag; the zero-default `config_complete_timeout_us` +chain; the bareness of `socfpga_agilex5.dtsi`; the absence of any Agilex-5 compatible in +6.18.44; the absence of a mainline overlay loader; the `OF_OVERLAY` non-select trap; the +whole Kconfig table; the firmware search path; the Cyclone-V-only content of 0043/0044 and +the generic nature of 0045; and the demotion of the H2F window map to **[U]**. + +**Still contested / weakest links after the pass.** (a) *AXE5* could not be re-fetched by +either refuter or by me (403 live, Wayback unreachable from this environment); its figures +rest on the draft's Wayback snapshot and its adverse subject on 2026-08-21 search +corroboration. (b) *A5-overlay* (thread 314152) is snippet-grade only and is doing real work +in §3.2 and §4.2 — it is the only report of the Linux overlay path actually working on +Agilex 5, and it has not been read directly. (c) Everything attributed to Intel documents +remains unread and **[U]**. + +## 11. Open [U] — how each is settled, and who inherits it + +| # | Unknown | How it gets settled | Inherits | +|---|---|---|---| +| 1 | Does the factory DE25-Nano QSPI ship with VAB/QKY keys provisioned — i.e. will unsigned community `.rbf` load? | Load a locally built unsigned `core.rbf` and observe SDM accept/reject; or obtain and fully read Intel doc 762191 / the Agilex 5 Configuration UG (both 403 on 2026-08-21). The *UM* is now read and is silent, which is weak permissive evidence only | **D2.5** (first hour); a doc fetch is worth attempting before hardware purchase | +| 2 | Do `intel,agilex-svc` / `intel,agilex-soc-fpga-mgr` bind and function on Agilex 5 silicon? | Substantially de-risked by the *A5-series* fallback declaration (§3.1) but not proven: author §4.2's DT, boot, check `/sys/class/fpga_manager/` and the probe dmesg. Desk follow-up: check whether the series landed in a post-6.18 kernel and what else it dragged in | **D2.5**; the mainline-landing check is desk work for **D0.3** | +| 3 | Measured reconfiguration latency: storage read vs overlay apply vs SDM write vs overlay remove | §6.3, steps 2–4, 20 iterations. Watch for SDM windows that are near-exact 1 s multiples (§6.1 row 7) | **D2.5** | +| 4 | Does the region accept repeated overlay apply/remove cycles cleanly — no leaks, no second-switch failure, HPS→fabric access still alive afterwards? | §6.3 step 3, iterations 2..20, with an LWH2F-window read after every switch. **Raised in priority**: no mainline user cycles overlays (§3.2) and *AXE5* reports a second-configuration hang on Agilex 5 (§6.1 row 4a) | **D2.5** | +| 5 | Does an overlay omitting `config-complete-timeout-us` fail immediately? | §6.3 step 6, several runs (benign race, §4.3) | **D2.5** | +| 6 | Which overlay-loader option — carry `OF_CONFIGFS`, write a board driver, or U-Boot-preload only | Design decision once §3.2's options are costed; classify the configfs patch against `docs/patch-provenance.md` (origin is the 2014 Antoniou series; the shipping form was not located) | **D2** (design), **D0.3** may classify the patch | +| 7 | Actual A5EB013 `core.rbf` size, and the SDM's internal config clock/bus width | Build a GHRD-based `core.rbf` in Quartus Prime Pro and `stat` it; read the Agilex 5 datasheet PDF locally rather than the JS docs site | **D2.5** / desk, whenever a PDF mirror is found | +| 8 | Agilex 5 F2H and F2SDRAM window addresses and widths | Read Intel HPS TRM 814346/813752 directly — needs an authenticated fetch, a mirror, or a human-supplied PDF | **D0.1/D2.2** recon; blocks §7.3 | +| 9 | Is fabric access to HPS LPDDR4A coherent by default, or must the fabric master assert CCU/ACE-Lite via AXUSER? | Same TRM chapter, then measure on hardware once a GHRD test image exists | **D2.2/D2.5**; plan §7 calls this the highest-value unknown and it stays that way | +| 10 | Does the 0045 write-combine throughput floor reproduce on Agilex 5/ARMv8? | Map the shared aperture Device-nGnRnE vs Normal-NC and measure streaming read/write each way — the same test that produced the DE10's ~100/~54 MB/s | **D2.5**; feeds the DP that replaces DP-9's Claim B | +| 11 | What runtime doorbell/aperture mechanism the DE25 uses (the actual purpose of 0043–0045) | Gated on L0's register-access model and on a GHRD; not resolvable by desk research. **Needs its own DP**, plus the plan §6 DP-9 wording edit (§8) | **L0 watch (D0.4)** → its own DP, *not* DP-9 | +| 12 | Does Terasic's GHRD wire LPDDR4A to the HPS hard EMIF by default, or is that a per-design Platform Designer choice? *UM* §3.7.4 says the bank *can*, and that the FPGA may claim it if the HPS does not | Inspect the System CD's `golden_top` / `.qsys` once obtained | **D0.1** (BSP quality already flagged unassessed) | +| 13 | Phase-1/phase-2 version-skew tolerance across Quartus/SDM versions | Not stated in any reachable Altera doc; treat as project-side convention per `de25-boot-chain.md` §5 until a doc says otherwise | **D0.1 Q2**, **D2.2** | +| 14 | Does the Agilex 5 `svc` node need `iommus = <&smmu 10>` (upstream has it; 6.18.44's DTSI has no `smmu` node)? | Read the *A5-series* DTS patch against whichever DTSI it targets; test both forms on hardware | **D0.3** desk, **D2.5** confirm | +| 15 | Can the ADV7513's control I2C be muxed onto the HPS bus (*UM* Figure 3-13, not text-extractable)? | Read the figure from the PDF as an image, or the System CD schematic | **D0.1**; does not change DP-10 (§7.4) | diff --git a/docs/de25-patch-portability.md b/docs/de25-patch-portability.md new file mode 100644 index 0000000..5cd5996 --- /dev/null +++ b/docs/de25-patch-portability.md @@ -0,0 +1,731 @@ +# DE25-Nano kernel patch portability — all 40 patches, two verdicts each + +**Status:** **desk audit, 2026-08-21. No hardware. Nothing built, nothing rebased, no file +moved.** This is task **D0.3** of [`de25-nano-tasks.md`](de25-nano-tasks.md) §D0.3, produced as +a **draft**: the series-layout proposal in §3 is a proposal, and the *portable* verdicts that +touch `arch/`, a DTS, or a Kconfig (§8) are queued for an independent spot-verification pass +that has **not** yet run. Every load-bearing claim is tagged **[V]** (a file was opened and the +relevant lines read — the patch, the provenance record, or `output/build/linux-6.18.44`) or +**[U]** (not settled by reading, with the thing that would settle it named). A **[V]** here is a +*source-reading* claim only: **nothing in this document has been compiled for aarch64 and +nothing has been run on Agilex 5 silicon.** + +**Cross-refs:** [`de25-nano-plan.md`](de25-nano-plan.md) §5 (the ~28/~8 estimate this +reconciles against) and §6 DP-9/DP-10 · [ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md) +· [`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) §7–§8 (D0.2 — authoritative on +reconfiguration and on what DP-9 does and does not settle) · +[`de25-boot-chain.md`](de25-boot-chain.md) (D0.1) · [`patch-provenance.md`](patch-provenance.md) +· [`kernel-recon/`](kernel-recon/) · [`abi-contract.md`](abi-contract.md) §4.3 (the framebuffer +and vsync userspace contract) · [`rt-beta-kernel.md`](rt-beta-kernel.md) §9 (the beta-local +observability patches). + +**Sources read this pass.** All 40 unique patch files under +`board/mister/de10nano/linux-patches/` (36) and `board/mister/de10nano/linux-patches-beta/` +(40 + a `series` file); `docs/patch-provenance.md`; `docs/kernel-recon/`; the local kernel tree +`output/build/linux-6.18.44` (cited as `linux:path:line`), including +`arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi`, `socfpga_agilex5_socdk.dts` and +`socfpga_agilex.dtsi` for the Agilex-side comparison. + +> **Four facts shaped this document.** +> +> 1. **The owner's prior survived, cleanly and on evidence.** All **25** patches under +> `drivers/hid`, `drivers/input` and `drivers/hid/usbhid` are portable as-is to arm64 and +> belong in a **shared** series. Every one was opened; the specific arch-coupling classes +> (32-bit time/ioctl, unaligned access, endianness, DMA/coherency, `__u32`-vs-pointer struct +> layout, absent HW blocks) were looked for by name and **none was found** (§5). **[V]** +> 2. **But the prior's *complement* is understated, and that is where the money is.** The prior +> names fb / vsync / f2h_irq as the worry set. The single hardest arm64 break found is in +> **none of them**: `0002` (MiSTer audio SPI) **does not compile on aarch64** — it passes +> `unsigned int *` where `dma_alloc_coherent()` wants `dma_addr_t *`, and `dma_addr_t` is +> `u64` on every 64-bit arch **[V `linux:kernel/dma/Kconfig:35`, +> `linux:include/linux/types.h:157-161`]**. Anyone porting on the prior alone hits it at the +> first aarch64 compile. +> 3. **One break compiles clean and fails silently — the dangerous class.** `0001`'s +> `memremap(…, MEMREMAP_WT)` yields **Normal non-cacheable** on arm +> (`linux:arch/arm/include/asm/io.h:390`, `ioremap_wt == ioremap_wc`) and **Device-nGnRE** on +> arm64 (`linux:include/asm-generic/io.h:1166-1167` → `linux:arch/arm64/include/asm/io.h:284`) +> **[V]**. The patch's own header justifies its direct-dereference drawing ops with *"memremap +> (MEMREMAP_WT) returns a normal-memory mapping"* — **true on arm, false on arm64**. Generalise +> the lesson: *any patch whose **correctness argument** cites an arm32 memory-attribute +> encoding needs the argument re-derived on arm64, even when the code is unchanged.* +> 4. **No patch is dropped and none is superseded.** Zero of 40. The 0037/BTN_Z trap class — +> dropping something that looks cosmetic but shifts an index userspace depends on — is +> therefore **not engaged anywhere in this audit**, and no drop-justifying provenance cite is +> owed. The one verdict that *resembles* a drop (0043/0044/0045 not going to DE25) is DP-9's +> already-taken decision, recorded here with the capability each provides so the +> Agilex-native replacement can be **checked** rather than assumed equivalent. + +--- + +## 1. How to read this + +Every patch gets **two** verdicts, and they answer different questions. + +**Verdict 1 — portability.** *Would this patch build and behave correctly on arm64/Agilex 5?* +One of `portable-as-is` (applies and works unchanged), `portable-with-rework` (the mechanism is +sound; named, bounded changes are required first), `board-specific` (encodes Cyclone V +addresses, interrupt numbers, register maps or gateware contracts — a reimplementation, not a +rebase), or `superseded-upstream` (the change already landed in 6.18; **no patch earned this +verdict**). + +**Verdict 2 — target series.** *Which patch directory should it live in once the tree builds two +boards?* One of `shared` (applied to both), `de10-only`, `de25-only` (none yet), or `drop` +(none). + +**The second column is the one that serves the owner's goal.** Portability alone would let you +put all 33 portable patches in one pile and call it reuse; it would also let you conclude that +`0043` "does not port" and quietly delete a patch that ships, today, on the DE10 RT beta. The +two verdicts are independent in both directions: + +- A patch can be **portable and still DE10-only** — `0045` (UIO write-combining) is generic, + arch-independent kernel code that would build and work on arm64, but per DP-9 it has nothing + to attach to on the DE25 until a GHRD exists, so it stays in the DE10 beta series. +- A patch can be **board-flavoured in its rationale and still shared** — `0030` (quiet the + DesignWare I2C timeout) exists because the DE10's optional RTC add-on is usually absent, but + the file it edits is the generic Synopsys driver that Agilex 5's HPS I2C also uses, so it + belongs in the shared series. + +**`de10-only` never means "delete".** Where this document says a patch is not ported to the +DE25, the patch stays live and unmodified where it is. Two triage verdicts of `drop` (`0043`, +`0044`) were **overturned to `de10-only`** for exactly this reason during the deep-dive pass. + +**Depth is uneven, and the document says so.** Eight patches got a full deep dive (`0001`, +`0002`, `0003`, `0004`, `0043`, `0044`, `0045`, `0046`) plus a cross-cutting mechanism pass; the +other 32 got a single-pass triage read. **Where a deep dive contradicted triage, the deep dive +wins and the table flags it (⚠).** That flag is the signal for how much to trust the +un-deep-dived rows: on the four patches where a second, more careful pass was applied to +*non-obvious* code, it found something triage had missed **three times out of four** — a +build-breaking `NO_IRQ` (`0001`), a build-breaking `dma_addr_t` (`0002`), and an arm64-absent +`CONFIG_CMDLINE_EXTEND` (`0043`). The rows that are pure device-ID tables, evdev keymaps and +log-severity changes are cheap to be right about; the rows involving memory attributes, DMA and +DT bindings are not. + +**Provenance cites.** House rule: a `drop` or `superseded` verdict *must* cite the provenance +record. No verdict here is either, so a cite is carried only where a leg actually took one; `—` +means no per-patch provenance row was needed for this verdict, not that none exists. Four +patches have a **[V] negative** provenance finding worth recording: `0043`, `0044`, `0045` and +`0046` have **no entry at all** in `patch-provenance.md` (grep for `ramoops`, `doorbell`, +`writecombine`, `uio` returns nothing relevant), yet each patch header cites *"Disposition +'carry' (docs/patch-provenance.md 3.1)"* — and §3.1 (`patch-provenance.md:330-346`) is the +Class A table covering `0001`–`0004` only. Those headers cite a row that was never written. +That is a documentation defect to close, not a disposition problem: all four are locally +authored with no fork ancestry, so there is no upstream disposition to reconcile. + +--- + +## 2. The table — all 40 + +Risk is a reading-effort signal, not a verdict: 🟢 green = mechanism obviously arch-neutral; +🟡 amber = a named, bounded change or decision is required; 🔴 red = board-specific by +construction. ⚠ marks a row where the deep dive overturned or materially corrected triage. + +| # | Patch | Subsystem | Risk | Portability | Series | Rationale (one line) | Provenance | +|---|---|---|---|---|---|---|---| +| 1 | `0001-fbdev-add-MiSTer_fb-driver` ⚠ | fbdev | 🔴 | board-specific | de10-only | Driver is inseparable from a DE10 fabric frame-reader at `0x22000000` + GIC SPI 40; **and** it uses arm-only `NO_IRQ` (won't compile on arm64) and relies on `MEMREMAP_WT` being Normal memory (it is Device on arm64) — both missed by triage. | `patch-provenance.md:334`, `:558-640`, `:1173`, `:1507` | +| 2 | `0002-sound-add-MiSTer-audio-spi-and-snd-dummy-MiSTer-model` ⚠ | sound | 🟡 | portable-with-rework | shared (gated) | Chrdev+SPI driver is subsystem-generic, but **breaks the aarch64 build**: `&MrBufferInfo.addr` (`unsigned int`) passed as `dma_addr_t *`. Fix must not widen the 16-byte FPGA wire descriptor. `dummy.c` half is arch-neutral and mandatory on DE10. | `patch-provenance.md:335`, `:634-760`, `:280-306` (N4), `:1174` | +| 3 | `0003-cpufreq-cyclone5-de10nano-overclock` | cpufreq | 🔴 | board-specific | de10-only | Pokes Cyclone V gen5 clock-manager MMIO at fixed offsets via `altr,clk-mgr`; Agilex 5 has a different clkmgr (`intel,agilex5-clkmgr`) **with no in-tree driver at all** in 6.18.44 — nothing to port *to*. | `patch-provenance.md:336`, `:757-880`, `:1175`, `:1511` (B6) | +| 4 | `0004-dts-de10nano-MiSTer` | dts | 🔴 | board-specific | de10-only | The DE10 board DTS itself: gen5 FPGA bridges, KSZ9031 PCB skews, `MiSTer_fb@22000000`, DE10 header pinout. DE25 needs a from-scratch arm64 DTS. | `patch-provenance.md:337`, `:930-975`, `:1176` | +| 5 | `0010-hid-guncon2` | hid | 🟢 | portable-as-is | shared | Raw `usb_driver` for a Namco lightgun using generic USB/input APIs; fixed 6-byte report is a peripheral protocol detail, not an arch assumption. | — | +| 6 | `0011-hid-guncon3` | hid | 🟢 | portable-as-is | shared | Standalone USB interrupt-URB HID driver; `usb_maxpacket()`/`strscpy()` already match 6.18 API. USB-generic, works over any host controller incl. Agilex 5's dwc2. | — | +| 7 | `0012-hid-fanatec` | hid | 🟢 | portable-as-is | shared | ~1745 lines of USB HID force-feedback (hrtimer, spinlocks, jiffies, sysfs); no MMIO, DMA, endianness or pointer-size ABI exposure anywhere. | — | +| 8 | `0013-hid-flydigi-vader` | hid | 🟢 | portable-as-is | shared | New Bluetooth HID remap driver using only `hid_parse`/`hid_hw_start`/`input_report_key`; `BTN_GRIP*` are arch-independent UAPI constants. | — | +| 9 | `0014-hid-gamecube-adapter` | hid | 🟢 | portable-as-is | shared | Generic USB-HID driver over `u8` buffers with RCU + `work_struct` hot-plug; uses modern `hid_is_usb()`. Not upstream, so not superseded. | — | +| 10 | `0015-hid-nintendo-nso-famicom` | hid | 🟢 | portable-as-is | shared | Two enum values, two type helpers, two dispatch arms, one button table in `hid-nintendo.c`. Beta copy differs by **context re-anchoring only**; functional hunks byte-identical. | — | +| 11 | `0016-hid-microsoft-elite2-paddles` | hid | 🟢 | portable-as-is | shared | HID usage-mapping quirk decoding an 8-bit paddle bitmask; no arch, pointer-size or DMA assumption. | — | +| 12 | `0017-xpad-mister-deltas` | joystick | 🟢 | portable-as-is | shared | `cpoll` param, GIP exclusion, Qanba/Flydigi table entries; endianness handled correctly via `le16_to_cpup()` for the raw-mode axes. | — | +| 13 | `0018-hid-controllable-quirk` | hid | 🟢 | portable-as-is | shared | Two device-ID table rows routing a BT VID:PID through the existing PANTHERLORD driver. No code at all beyond table data. | — | +| 14 | `0019-hidpp-k400-fn-inversion` | hid | 🟢 | portable-as-is | shared | HID++ feature-protocol logic plus one ID-table row; all payloads are single bytes. | — | +| 15 | `0020-mmc-no-led-on-send-status` | mmc | 🟢 | portable-as-is | shared | One conditional in core `mmc_start_request()`, gating the LED trigger on `MMC_SEND_STATUS`. Core-layer, host-driver-agnostic; the DE10 `hps_led0` framing is a consequence, not a dependency. | — | +| 16 | `0022-hid-playstation-ds4-mac-fix` | hid | 🟢 | portable-as-is | shared | Turns a hard probe failure into warn-and-continue in `dualshock4_get_mac_address()`. Pure error-handling policy. | — | +| 17 | `0023-hid-wiimote-fixes` | hid | 🟢 | portable-as-is | shared | Sets `input_dev->uniq` across ~11 extension probes, remaps a keymap to `BTN_*`, corrects two ABS ranges. Bluetooth HID input mapping only. | — | +| 18 | `0024-hid-input-keyrah-europe1` | hid | 🟢 | portable-as-is | shared | A **single byte** changed in the `hid_keyboard[256]` scancode table. Architecture-agnostic by construction. | — | +| 19 | `0025-usbhid-jspoll-gamepad` | hid | 🟢 | portable-as-is | shared | One `case HID_GD_GAMEPAD:` fallthrough in `usbhid_start()`'s polling-interval switch. | — | +| 20 | `0026-input-mousedev-eviocgrab` | input | 🟢 | portable-as-is | shared | New `->ignore_grab` flag + `EVIOCGRAB` on mousedev. **`EVIOCGRAB`'s argument is a bool, not a pointer**, so there is no `compat_ptr()`/32-vs-64 ioctl hazard; the patch header states it was written with other arches in mind. | — | +| 21 | `0027-mt76x2u-release-xbox-adapter-ids` | net (mt76) | 🟢 | portable-as-is | shared | Deletes two `USB_DEVICE()` rows so `xone` can claim the device. Driver-match priority policy; a USB ID table has no CPU architecture. | — | +| 22 | `0028-dwc2-fix-unaligned-in-split` | usb | 🟢 | portable-as-is | shared | Moves an `align_buf` copy-back into the shared split-completion path — a **real general dwc2 bug**, not a MiSTer quirk. Agilex 5 declares `snps,dwc2` too, so it applies there identically. | — | +| 23 | `0029-leds-gpio-brightness-hw-changed` | leds | 🟢 | portable-as-is | shared | Generic `leds-gpio` + LED-class change. Shared as *code*; it only *does* anything on a board whose DTS wires an activity LED (DE10: `hps_led0` from `0004`). | `patch-provenance.md:429`, `:1198` | +| 24 | `0030-i2c-designware-quiet-timeout` | i2c | 🟢 | portable-as-is | shared | One `dev_err`→`dev_dbg` in the generic Synopsys DW I2C master, which Agilex 5's HPS I2C also uses. Beta copy differs by re-anchoring only; the changed line is byte-identical. | — | +| 25 | `0031-exfat-samsung-symlinks` | exfat | 🟢 | portable-as-is | shared | Filesystem-format code; the attribute-bit overload is a 16-bit DOS value handled by existing helpers. `inode_nohighmem()` is a generic VFS call, a no-op without HIGHMEM (normal on arm64). | — | +| 26 | `0032-hid-nintendo-joycon-combo-led` | hid | 🟢 | portable-as-is | shared | Registers a virtual `led_classdev` used as a userspace pairing mailbox; downgrades two warns to debug. | — | +| 27 | `0033-hid-playstation-dualsense-player-id-led` | hid | 🟢 | portable-as-is | shared | Replaces five auto-lit player LEDs with one writable `player_id_led` that Main_MiSTer drives. LED-class/HID only. | — | +| 28 | `0034-hid-nintendo-nes-famicom-stock-ab-mapping` | hid | 🟢 | portable-as-is | shared | Swaps two rows in two static button-mapping tables to restore stock's A/B order. Same userspace consumes it on both boards. | — | +| 29 | `0035-hid-nintendo-home-led-nonfatal` | hid | 🟢 | portable-as-is | shared | Makes home-LED `devm_led_classdev_register()` failure non-fatal at probe. Only **partially** fixed upstream — the registration path is still fatal in 6.18, so **carry, not superseded**. | `patch-provenance.md:363` | +| 30 | `0036-btusb-csr-clone-lmp-subver-2512` | bluetooth | 🟢 | portable-as-is | shared | One `else if` comparing `le16_to_cpu(rp->lmp_subver)` against a clone signature. USB-attached, host-controller-agnostic. | — | +| 31 | `0037-hid-playstation-dualsense-mute-btn-z` | hid | 🟢 | portable-as-is | shared | **Functional, not cosmetic.** `BTN_Z` (0x135) sits between `BTN_WEST` and `BTN_TL` and shifts every higher `EV_KEY` ordinal, so the shipped `gamecontrollerdb` `platform:MiSTer` rows depend on it. The RT beta drops it; that is a **known divergence, not a precedent**. Beta copy differs by hunk offsets only. | `patch-provenance.md:370`, `:1222`, `:1537` | +| 32 | `0038-hid-nintendo-nso-genesis-bt-pid` | hid | 🟢 | portable-as-is | shared | 20-line `hdev->product` rewrite keyed off a controller-reported type byte, before `devm_input_allocate_device()`. | — | +| 33 | `0039-hid-nintendo-nso-n64-genesis-stock-button-mapping` | hid | 🟢 | portable-as-is | shared | Reassigns evdev codes in two static mapping tables to stock's order. Not bench-verified against real pads — a testing gap, **not** a portability risk. | — | +| 34 | `0040-hid-nintendo-imu-name-suffix` | hid | 🟢 | portable-as-is | shared | One format-string token (`"%s (IMU)"` → `"%s IMU"`) restoring the substring Main_MiSTer filters on. | — | +| 35 | `0041-hid-nintendo-stock-led-classdev-names` | hid | 🟢 | portable-as-is | shared | `devm_kasprintf()` format change restoring stock's flat LED names so Main_MiSTer's hardcoded `fopen()` paths resolve. | — | +| 36 | `0042-hid-playstation-stock-lightbar-led-names` | hid | 🟢 | portable-as-is | shared | Adds stock-compatible R/G/B LED classdevs alongside mainline's multicolor device, using an explicit back-pointer+index instead of `container_of()`. LED-class/HID only. | — | +| 37 | `0043-dts-uio-doorbells` ⚠ | dts (uio) | 🔴 | board-specific | de10-only *(beta)* | Eight interrupt-only `generic-uio` nodes on Cyclone V GIC SPI 48–55 (`f2h_irq8..15`). Triage said `drop`; **overturned — it ships on DE10.** Deep dive added: **`CONFIG_CMDLINE_EXTEND` does not exist on arm64**, so the DE10 binding recipe has no arm64 counterpart. Not ported to DE25 per DP-9. | **none exists** (see §1); `de25-nano-plan.md:211-218`; `de25-fpga-reconfig.md` §8 | +| 38 | `0044-dts-uio-fpga-regions` ⚠ | dts (uio) | 🔴 | board-specific | de10-only *(beta)* | Two reg-bearing UIO nodes: the 2 MiB lwhps2fpga window at `0xff200000` (never write-combined) and the 512 MiB f2sdram DDR aperture at `0x20000000` (WC-capable). Triage said `drop`; **overturned.** Depends on the `mem=511M` bootarg. Not ported per DP-9. | **none exists**; `de25-nano-plan.md:213-219` | +| 39 | `0045-uio-writecombine` | uio | 🟡 | **portable-as-is** | de10-only *(beta)* | Generic `UIO_MEM_PHYS_WC` memtype — **arch-independent, and the Normal-NC vs Device-nGnRnE distinction it exploits is identical on arm64** (`linux:arch/arm64/include/asm/pgtable.h:789-792`). Not ported *because it has nothing to attach to until a GHRD exists*, not because it fails. **D0.2 §8 states plainly that DP-9 does not settle this patch's question.** Splitting the generic half to shared/upstream is an open owner decision (§7). | **none exists**; `de25-fpga-reconfig.md` §8 | +| 40 | `0046-dts-ramoops` | dts | 🔴 | board-specific | de10-only *(beta)* | 1 MiB `ramoops` carve-out at `0x1FE00000` — every number derives from `mem=511M`, MiSTer's fixed mailbox at `0x1FFFF000`, and ARM32's lowmem/HIGHMEM model. The **capability** (crash forensics for a silent hang) is board-agnostic and arguably worth more on DE25; the **address** is not portable. The four `CONFIG_PSTORE_*` symbols are a genuine shared-fragment candidate. | **none exists**; `rt-beta-kernel.md` §9, §9.1, `:74`, `:730-732`, `:228` | + +**Totals.** 33 `portable-as-is` · 1 `portable-with-rework` · 6 `board-specific` · **0 +`superseded`** · **0 `drop`**. Series: **33 shared** · **7 de10-only** · 0 de25-only. + +**Series membership and the four divergent copies.** 36 patches are in both directories; 4 +(`0043`–`0046`) are beta-only. Of the 36, **32 are byte-identical between the two series** — +including `0002`, `0003` and `0004`, verified by `cmp`/`diff` **[V]**. Four differ, and **none +of the differences changes a verdict** **[V]**: + +| Patch | What differs between the shipped and beta copies | Verdict impact | +|---|---|---| +| `0001` | `#include ` → `#include "core/fbcon.h"` (7.x moved `fbcon_update_vcs()`'s declaration into the fbdev core), plus a 7-line note asking that the two be kept in lockstep. | None. Kernel-version churn, not arch coupling. **Both copies carry the identical `NO_IRQ` and `MEMREMAP_WT` problems.** | +| `0015` | Context re-anchoring around `JOYCON_CTLR_TYPE_LIC_PRO = 0x06`, which the 7.x tree already carries. Functional hunks byte-identical. | None. | +| `0030` | Context re-anchoring (`i2c_dw_init_master()` → 7.x's renamed `i2c_dw_init()`) and a shifted line number. The changed line is byte-identical. | None. | +| `0037` | Hunk-header offsets only, re-anchored around 7.x's added DualSense-Edge paddle block. Added lines byte-identical. | None — but note the beta *series file* **drops `0037` entirely**, which is the known divergence recorded in row 31, not a licence to do the same on DE25. | + +--- + +## 3. Series layout — the proposal + +**This is a proposal. No files were moved and no `series` file was written.** It is the concrete +answer to the owner's question: *how do we get one tree building two boards while reusing as +much as possible?* + +### 3.1 The shape + +``` +board/mister/ + common/ + linux-patches/ <- SHARED: 33 patches, applied to BOTH boards + de10nano/ + linux-patches/ <- DE10-only: 0001 0002-audio-node? 0003 0004 (4) + linux-patches-beta/ <- DE10 RT beta delta: 0043 0044 0045 0046 (4) + linux.config / linux-rt.fragment + de25nano/ + linux-patches/ <- DE25-only: empty today (0) + linux.config +``` + +**The shared series holds 33 of 40 patches — 82.5%.** That is the reuse figure, and it is real: +every one of those 33 was opened and judged arch-neutral on its own text, not assumed neutral +because it lives under `drivers/hid`. + +**The DE10-only series holds 7.** Four in the stock series (`0001`, `0003`, `0004`, and — see +below — the board half of `0002`), four beta-local (`0043`–`0046`). Every one encodes a Cyclone +V physical address, GIC SPI number, clock-manager register offset, or gateware contract. + +**The DE25-only series is empty today, by design.** ADR 0027 scopes the initial DE25 image to a +bare developer OS with **no MiSTer binaries**. The DE25 will eventually need its own board DTS +(the analogue of `0004`), and probably its own `ramoops` node (the analogue of `0046`, whose +capability is *more* valuable on a board whose reconfiguration path has a reported +hang-on-second-load); neither is written and neither should be written speculatively. + +### 3.2 Two decisions the layout forces, neither of which this document takes + +**(a) `0002` is one file doing two jobs.** Its `MiSTer-audio-spi.c` half is bound to a +`MiSTer,spi-audio` DT node and a MiSTer gateware wire protocol → DE10-only in substance. Its +`sound/drivers/dummy.c` half is arch-neutral ALSA core → a shared candidate. But the patch +header warns in as many words that *the two halves ship together*: `asound.conf` pins format and +rate but **not** channel count, which is negotiated against `hw:0`, so pinning snd-dummy to 2ch +is the only thing stopping a mono client teeing mono frames into a driver that reads them as +4-byte stereo. Omit the `dummy.c` hunks on DE10 and you get a perfectly healthy `/dev/MrAudio` +and **wrong or silent audio** (`patch-provenance.md:280-306`, N4) **[V]**. + +Two viable options. **Option 1 (recommended, lowest risk):** keep `0002` whole in the *shared* +series and let the DE25 defconfig simply not set `CONFIG_SND_MISTER_AUDIO` / +`CONFIG_SND_DUMMY`. The driver half is inert without a DT node; the `dummy.c` half is **not** +inert — it changes snd-dummy's global defaults on any board that builds `CONFIG_SND_DUMMY` — so +the defconfig gate is doing real work, not decoration. **Option 2:** split into a shared +`dummy.c` patch and a DE10-only `MiSTer-audio-spi` patch. Safe for DE10 only if both stay +applied there. **Owner call required** — and it should be taken with the knowledge that `0002` +does not currently compile for aarch64 at all (§4.4), so under Option 1 the aarch64 build must +either carry the type fix or not build the file. + +**(b) `0045` may be worth splitting for upstream, not just for reuse.** Its `uio.c`, +`uio_driver.h` and `uio-howto.rst` hunks are generic, arch-independent kernel code written — by +its own header — to upstreamable standards; only the `mister,map-writecombine` property parse in +`uio_pdrv_genirq.c` is board-flavoured, and even that is only a name. `de25-fpga-reconfig.md` §8 +states explicitly that **DP-9 does not settle 0045's question and should not be cited as having +done so**. Splitting the generic half to the shared series (or upstream) costs nothing on DE10 +and would leave the DE25 with the memory-attribute escape hatch already in place if it ever +needs one — which, given §4.1's finding that a naively ported `MiSTer_fb` lands on Device memory +on arm64, is not hypothetical. + +### 3.3 What the layout does **not** solve + +The shared series is 33 patches of *device support*. It contains nothing that makes the two +boards behave alike where it matters — video, audio, fabric signalling — because those five +mechanisms are exactly where the boards differ. **That is not a failure of the split; it is the +correct answer to the question, and §4 is the part of this document that carries the weight.** + +--- + +## 4. The fabric-facing mechanisms + +Five mechanisms account for all 7 DE10-only patches and for every hard finding in this audit. +For each: what the DE10 does, what the Agilex path looks like, and — the part that matters — +**the capability stated so that a replacement can be *checked* rather than assumed equivalent.** + +Per DP-9, the DE25 adopts Agilex-native DTS/fpga-region idioms instead of the carried UIO +patches. **That decision is not re-litigated here.** What is recorded is what each carried patch +*provides*, in testable form, because "fpga-region replaces it" is a claim about reconfiguration +and several of these capabilities are about **runtime signalling and apertures**, which +`of-fpga-region.c` has nothing to say about (`de25-fpga-reconfig.md` §8, Claim B **refuted** +**[V]**). + +### 4.1 Framebuffer — `0001`, `0004`, (`0044`) + +**DE10.** Two *independent* paths sharing one patch, and conflating them would produce a DE25 +port that looks right and is not. **(a) Pixels:** Main_MiSTer never mmaps `/dev/fb0` — its only +mmap is on `/dev/mem`, at raw physical `FB_ADDR = 0x22000000`, for `1920*1080*4*3` = +24,883,200 B of triple buffer (`abi-contract.md:416`, `:528-533`, `:542-555`) **[V]**. +**(b) fbdev:** `0004` declares `MiSTer_fb@22000000` with `reg = <0x22000000 0x800000>` — 8 MiB, +**buffer 0 only**, deliberately not the full 24.9 MB, and it **must not be "fixed" upward** +(`abi-contract.md:553-555`) **[V]**. `/dev/fb0` exists to serve `FBIO_WAITFORVSYNC` and to let +fbcon paint; nothing else consumes it. The window sits above the `mem=511M` cap, so it has no +`struct page`s and no cacheable linear alias, and the fabric reads it over f2sdram without +snooping the A9 caches. + +**Agilex path — three blockers, two invisible to a green DE10 build.** + +1. **Hard compile break [V].** `MiSTer_fb.c:303,324,325` reference `NO_IRQ`, defined **only** in + `linux:arch/arm/include/asm/irq.h:22`. No arm64 and no asm-generic definition exists. (Aside, + true on both arches: `irq_of_parse_and_map()` returns `0` on failure, never `NO_IRQ`, so the + guard is dead code and `free_irq()` can be called on a never-requested IRQ.) +2. **Hard semantic break that compiles clean [V].** `MEMREMAP_WT` → `ioremap_wt()` + (`linux:kernel/iomem.c:113-114`). arm: `ioremap_wt == ioremap_wc` = Normal-NC + (`linux:arch/arm/include/asm/io.h:390`). arm64: no `ioremap_wt`, so + `linux:include/asm-generic/io.h:1166-1167` aliases it to plain `ioremap()` = + `PROT_DEVICE_nGnRE` (`linux:arch/arm64/include/asm/io.h:284`). Consequence: `mode_set()`'s + full-window `memset()` and the `sys_fillrect`/`sys_copyarea`/`sys_imageblit` direct + dereferences become memcpy/memset over **Device** memory, where arm64 forbids unaligned + access and can fault — and the mapping falls to the throughput floor `0045` exists to lift. + The fix is `MEMREMAP_WC` (→ `ioremap_wc` → `PROT_NORMAL_NC`), which reproduces arm's + attribute exactly. +3. **Addresses and DT shape [V].** Agilex 5 DRAM base is `0x80000000` + (`socfpga_agilex5_socdk.dts:32-35`), so `0x22000000` is not DRAM on that SoC. Root + `#address-cells`/`#size-cells` are `<2>`/`<2>` (`socfpga_agilex5.dtsi:15-16`) against Cyclone + V's `<1>`/`<1>`, so the 2-cell `reg` is not even syntactically valid there. Cosmetic but + real: `fb_probe()` prints `(unsigned)fix.smem_start` with `%x`, truncating a 64-bit address. + +Where the DE25 window goes is **[U]** — `de25-fpga-reconfig.md` §7.3 already tabulates the four +requirements (LPDDR4A as the HPS-shared bank per DE25 UM §3.7.4; a `/reserved-memory` `no-map` +node; a per-frame fabric IRQ; a coherency discipline) and marks the address pending the GHRD. +DP-10 (tabled) may make this a DRM/KMS question instead, which would change the userspace ABI. + +> **Capability to preserve — framebuffer.** +> 1. **Pixel round trip.** Write a known 32-bit pattern at offset 0 through whatever userspace +> mapping replaces `/dev/mem@0x22001000`; it must (a) appear on HDMI and (b) read back +> byte-identical **through a second, independently created mapping**. The second mapping is +> the point: a silent copy-on-write failure passes a read-back through the *same* mapping. +> 2. **Attribute, not just function.** The pixel window must map **Normal non-cacheable, never +> Device**. Test by `memset()`ing the whole window and checking (a) no alignment fault and +> (b) fill bandwidth in the DE10's order. Falling to ~100 MB/s write / ~54 MB/s read is the +> exact signature of a Device mapping — the floor `0045` measured on Cyclone V. +> 3. **The custom ABI, byte for byte.** `echo "8888 1 " > /sys/module/MiSTer_fb/parameters/mode` +> — a plain shell redirect, so it must remain a real writable sysfs attribute, not an ioctl — +> must succeed, leave `width`/`height`/`stride`/`format`/`rb` reading back exactly those +> values, increment `res_count` by exactly 1, and have zeroed the window. Formats `8888` +> (default), `565`, `1555`, `8` (pseudocolor, 256-entry palette) must all exist, each with the +> `rb` red/blue swap. Main_MiSTer writes this with `fprintf` **and** with +> `system("echo …")` (`video.cpp:3548`, `:4390`). +> 4. **Node size.** The fbdev region covers buffer 0 only (≥ 8,298,496 B; 8 MiB is correct) while +> userspace maps 3×. A DE25 DTS that "helpfully" sizes it to the full triple buffer is a +> regression. +> 5. **fbcon paints.** With no Main_MiSTer running, kernel console output is visible, and stays +> visible after a mode write. This is what breaks first if the mapping story is wrong, and it +> fails silently — as a blank terminal, not an error. +> +> **Note for a DRM/KMS replacement (DP-10):** it satisfies *neither* half automatically. `simplefb` +> has no vsync-interrupt concept and cannot serve `FBIO_WAITFORVSYNC` (**[U, inferred]**, +> `de25-fpga-reconfig.md` §7.3), and the pixel path is not an fbdev consumer at all. + +### 4.2 Vsync — `0001`, `0004`, (`0043`) + +**DE10.** The fabric's `HDMI_TX_VS` pulse drives `f2h_irq0` → Cyclone V GIC **SPI 40**, declared +as `interrupts = <0 40 IRQ_TYPE_EDGE_RISING>` — edge, because it is a true pulse with no cause +register to read back. `irq_handler()` bumps `frame_count` and `wake_up_interruptible(&vs_wait)`; +`fb_wait_for_vsync()` snapshots the count and waits with a 50 ms timeout. The ABI is +`FBIO_WAITFORVSYNC = _IOW('F', 0x20, __u32)` = `0x40044620` — mainline UAPI, **byte-identical on +aarch64 because the argument is a fixed-width `__u32`** (`abi-contract.md:583-607`) **[V]**. +`*arg != 0` → `-ENODEV`; any other cmd → `-ENOTTY`; no vsync in 50 ms → `-ETIMEDOUT`. + +**The contract is stronger than "the wake arrives."** Main_MiSTer opens `/dev/fb0`, waits once, +then **times** a second wait with `getus()` (`video.cpp:3856-3868`) — it measures the +inter-vsync interval. Lose the IRQ entirely and every call returns `-ETIMEDOUT` after 50 ms, +which is a hard **20 fps cap on the menu with no error message anywhere** +(`abi-contract.md:612-614`) **[V]**. + +**Agilex path.** The *syntax* ports free: Agilex 5's `intc` is `arm,gic-v3` with +`#interrupt-cells = <3>` and `GIC_SPI == 0` in cell 0 (`socfpga_agilex5.dtsi:69-84`) **[V]**, and +the ioctl number is arch-independent. **The number 40 does not port.** Cyclone V routes its 64 +f2h lines onto SPI 40–103; Agilex 5's f2h SPI base is **[U]** — `socfpga_agilex5.dtsi` declares +no f2h lines at all, and its in-tree SPI indices are HPS peripherals. Settled by the Agilex 5 +HPS TRM's f2h interrupt table (which `de25-fpga-reconfig.md` §7.2 records as returning **HTTP +403** to every fetch on 2026-08-21 and **never read**) plus the DE25 GHRD. + +A latent race worth fixing *while* porting **[V code / U impact]**: `frame_count` is a plain +`u32` incremented from IRQ context and read from process context with no `READ_ONCE`, atomic, or +explicit barrier. `wake_up_interruptible()` carries the barrier that makes it converge in +practice on two Cortex-A9s; 2×A76 + 2×A55 is not a reason to assume the DE10's luck transfers. +Relatedly, the DE10's IRQ-affinity arrangement (GIC puts SPIs on CPU0; Main_MiSTer pins *itself* +to CPU1) must be **re-derived** on a big.LITTLE part — copying it is meaningless when CPU1 is an +A55 and CPU2/3 are A76s. + +> **Capability to preserve — vsync.** +> 1. `ioctl(fb, FBIO_WAITFORVSYNC, &zero)` returns 0, never `-ETIMEDOUT`, sustained under load. +> This is the existing DE10 gate (`abi-contract.md` H-2) and it is **necessary but not +> sufficient**. +> 2. The interval between two consecutive successful calls equals the core's frame period +> (~16.7 ms at 60 Hz) within a few hundred microseconds, with jitter no worse than the DE10's. +> **The DE10 baseline has never been measured** — only "returns 0" was ever gated. Measure +> DE10 first or the comparison is vacuous. +> 3. Error semantics unchanged: `*arg != 0` → `-ENODEV`; other cmd → `-ENOTTY`; 50 ms timeout. +> 4. `/sys/module/MiSTer_fb/parameters/frame_count` advances monotonically at the frame rate — +> this is how you distinguish "the IRQ fires" from "the wait happens to return". +> 5. **The vsync line must be exclusively kernel-owned.** `uio_pdrv_genirq` cannot share an IRQ +> at all — its probe fails with *"interrupt configuration error"* when `IRQF_SHARED` is set +> (`linux:drivers/uio/uio_pdrv_genirq.c:146`) **[V]** — so a UIO node on the vsync line would +> permanently and silently deny it to the framebuffer. +> 6. Trigger type must match the signal: **edge for a pulse, level for a cause-register-backed +> line.** Backwards loses events with no diagnostic. + +### 4.3 f2h_irq and the FPGA bridges — `0004`, `0001`, (`0043`, `0044`) + +**DE10.** Cyclone V routes 64 FPGA-to-HPS lines onto GIC SPI 40–103. The allocation this repo +fixes, and which is the model for whatever the DE25 does: + +| GIC SPI (DT cell) | f2h line | Owner | Trigger | +|---|---|---|---| +| 40 | `f2h_irq0` | `MiSTer_fb` — HDMI_TX_VS. Kernel-owned, **not** in the doorbell pool | edge-rising | +| 41 | `f2h_irq1` | `video_sync`, driven by stock gateware (`sys_top.v:573`), **measured 60.17 Hz over 42 s**. Deliberately unclaimed | — | +| 42–47 | `f2h_irq2..7` | Platform reserve (audio ring watermark, CEC). No nodes | — | +| 48–55 | `f2h_irq8..15` | `0043` UIO doorbell pool, `mister_doorbell1..8` | level-high | +| 56–103 | `f2h_irq16..63` | Reserved, no nodes | — | + +**The second interrupt cell is the SPI *index*, not the GIC INTID** (INTID = cell + 32). `0043`'s +header flags "correcting" this by subtracting 32 as a **silent brick** — it lands on HPS +peripheral interrupts. `0004` additionally enables `&fpga_bridge0/1/2` (lwhps2fpga, hps2fpga, +fpga2hps); fpga2sdram stays off. + +**Agilex path — the structural difference, not just different numbers.** Cyclone V exposes four +address-bearing bridge nodes with in-tree drivers (`linux:drivers/fpga/altera-hps2fpga.c:118-122`). +`socfpga_agilex5.dtsi` has **none** — no bridge node, no `fpga-mgr`, no `fpga-region`, no +`firmware`/`svc` node; grepping the whole file for `fpga` returns only the SoC and stmmac +compatibles **[V]**. For contrast, `socfpga_agilex.dtsi` (Agilex 7) has all three at `:63-80` +**[V]**. And `grep -rn agilex5 drivers/fpga/` returns **nothing** **[V]**. So +`&fpga_bridge0..2 { status = "okay"; }` has no counterpart: on Agilex the bridges are SDM/ATF-owned +and invisible to Linux, and enabling the F2H path is a firmware/U-Boot action, not a DTS one +(`de25-fpga-reconfig.md` §7.1) **[V]**. + +**And the F2H path specifically is the one under suspicion**: `de25-fpga-reconfig.md` §1 item 2 +records a dated on-Agilex-5-silicon report of the system hanging when the fabric is configured a +**second** time, with the LWH2F bridge implicated and a `bridge enable 0x3b` workaround that +skips F2H **[U, search-corroborated]**. That report is U-Boot-context and not dispositive, but it +means the f2h path should be the **first** thing exercised on DE25, not the last. + +> **Capability to preserve — f2h_irq.** *The test shape matters as much as the result.* +> 1. A fabric-raised interrupt wakes a blocked userspace thread. Reproduce `0043`'s measurement +> discipline: arm a consumer on **every** declared line, run ≥ 42 s, and read the **per-line** +> event counter. `0043`'s own header records the trap — the first run was read as "eight lines +> green" because eight probe processes exited 0, when in fact one line was seeing 60 Hz and +> seven were seeing nothing. **Process exit codes are not evidence.** +> 2. Every line the GHRD says is driven shows its expected rate; every line it says is undriven +> shows **exactly zero** events *and* a zero initial latched count. +> 3. **No coalescing** on a cause-register-backed line: raise two events before acking the first, +> count two. This is why `0043` uses level-high, and why `uio_pdrv_genirq` sets +> `IRQ_DISABLE_UNLAZY` for level IRQs (`linux:drivers/uio/uio_pdrv_genirq.c:185`) **[V]**. +> 4. **Exclusivity acknowledged.** The line the framebuffer uses must have no UIO node and must +> not be claimable by one; verify the failure is loud. +> 5. Wake-to-userspace latency measured on DE25 **and** compared against a DE10 measurement — +> which does not exist yet and must be taken as the control. +> 6. Reconfiguration-side behaviour (does f2h still work after a *second* overlay apply?) is +> `de25-fpga-reconfig.md` §6.3's runnable D2.5 plan. Don't duplicate it; **do** run it before +> trusting any of the above. + +### 4.4 Audio — `0002`, `0004` + +**DE10.** Not an ALSA driver, and two halves that must ship together. +**Half 1:** `sound/drivers/MiSTer-audio-spi.c`, an SPI driver on DT `compatible = "MiSTer,spi-audio"` +(HPS SPIM0, mode 3, 10 MHz), which `dma_alloc_coherent()`s a 512 KiB ring (~2.6 s of audio), +exposes `/dev/MrAudio`, truncates writes to whole 4-byte S16_LE **stereo** frames, and then +`spi_write()`s a **16-byte descriptor** `{addr, len, ptr, reserved}` of four `u32`. **The audio +never crosses SPI**: `addr` is the *physical* DMA address of the ring, and the fabric slave is an +**AXI master into HPS DRAM** that reads that ring itself. **Half 2:** `sound/drivers/dummy.c` +gains a `model_MiSTer` (S16_LE / 48000 / **2 ch**) and `fake_buffer` defaults to 0. +`/etc/asound.conf` routes `plug → rate → file("/dev/MrAudio") → hw:0` and the `file` plugin +**tees**; asound.conf pins format and rate but **not** channel count, which is negotiated against +`hw:0`. + +**Agilex path — this is the patch that breaks the owner's prior, and it is in none of the three +categories the prior names.** + +- **Hard compile break [V].** `dma_alloc_coherent(&g_spi->dev, BUFFER_LEN, &MrBufferInfo.addr, + GFP_KERNEL)` passes `unsigned int *` where the prototype wants `dma_addr_t *`. `dma_addr_t` is + `u64` whenever `CONFIG_ARCH_DMA_ADDR_T_64BIT` (`linux:include/linux/types.h:157-161`), which is + `def_bool 64BIT || PHYS_ADDR_T_64BIT` (`linux:kernel/dma/Kconfig:35`) — unconditionally `y` on + arm64. `-Wincompatible-pointer-types` is an error by default in the GCC generation this tree + uses. The patch's own forward-port note states the dead premise outright: *"ARM_LPAE is off on + Cyclone V, so `dev_t` and `dma_addr_t` are both u32."* +- **And fixing the C type is not fixing the bug [V].** `Info_t` is **wire format** to the fabric. + aarch64-LE preserves the byte order, so the *format* survives — but 32 bits cannot express an + address above 4 GiB, and Agilex 5 DRAM starts at `0x80000000`. Either constrain the ring below + 4 GiB (`dma_set_coherent_mask(DMA_BIT_MASK(32))` already asks, and becomes a *real* constraint + on arm64 rather than the no-op it is on Cyclone V) **or widen the descriptor — which is a + gateware change, not a kernel one.** Widening the C type alone truncates silently. *This is the + general shape of the DE25 port: several "kernel" problems are actually gateware contracts.* +- **Coherency is not free [U].** On Cyclone V the fabric reads the ring over f2sdram without + snooping the A9 caches, and `dma_alloc_coherent()`'s Normal-NC mapping makes that safe. + `de25-fpga-reconfig.md` §7.2 records that Agilex 5's CCU makes F2H/F2SDRAM coherency **opt-in + per transaction** via an AXUSER signal the fabric master drives — making "the fabric can read + the coherent ring" a gateware-design obligation, not a SoC guarantee. That §7.2 is explicitly + **[U]**: TRM 814346/813752 returned HTTP 403 and was never read; it is search-snippet paraphrase. +- **What ports free [V].** The SPI controller is the *identical* `snps,dw-apb-ssi` on both + (`socfpga_agilex5.dtsi:326-341`, spi0@`0x10da4000`, GIC_SPI 99). Whether SPIM0's pins reach + fabric on the DE25 board is **[U]** — needs the schematic/UM pin tables. One Agilex-side + difference to watch: Agilex 5's spi0 declares `dmas`/`dma-names` where Cyclone V's does not, so + the dw-spi core installs `can_dma`; the 4- and 16-byte transfers *should* stay PIO because + `dw_spi_can_dma()` returns false for `len <= fifo_len`, which keeps `device_open()`'s **stack** + `int rptr` safe under `VMAP_STACK` — but that holds only if the runtime-probed FIFO is ≥ 16 + bytes **[U until read from a real DE25 boot log]**. + +> **Capability to preserve — audio.** +> 1. `/dev/MrAudio` with the same identity: dynamic major, chrdev region `MrAudio_proc`, class +> `MrAudio_sys`, node `MrAudio`. +> 2. Write semantics: length truncated to a multiple of 4; `0` or `> 512 KiB` → `-EFAULT`; the +> ring wraps at 512 KiB; ≥ ~2.6 s of buffering. +> 3. **The mono test — the whole point of the `dummy.c` half.** Card 0 must advertise exactly 2 +> channels, and a **mono** client must still tee **stereo** into `/dev/MrAudio`. A system that +> passes the stereo test and fails the mono test **looks healthy and is broken**. Card 0's +> names must stay snd-dummy's stock strings (`Dummy` / `Dummy 1`) — ABI A11/A12. +> 4. End to end with the verbatim `asound.conf` chain: audible, non-glitching, correct-*pitch* +> audio. Wrong rate or channel count shows up as pitch/speed error, not silence. +> 5. **The descriptor contract**, the thing most likely to be silently wrong on arm64: the +> physical ring address the driver prints must be the address the fabric actually reads, and +> must be **below 4 GiB** while the descriptor field is 32 bits. **Assert this in the driver +> rather than trusting the allocator.** +> 6. Honest diagnostics: with SPI down, `read()` must report a failure string, not a fabricated +> length larger than the ring (the B4 bug, fixed in this forward-port — +> `patch-provenance.md:1509`). Regressing it makes the one diagnostic you read when SPI breaks +> itself wrong. + +### 4.5 Doorbells, apertures and the crash record — `0043`, `0044`, `0045`, `0046` + +All four are beta-local; none ships in the stock series. Recorded as capability, per DP-9. + +| Patch | What it provides | +|---|---| +| `0043` | A **named, blocking wait on an FPGA-raised interrupt** — eight interrupt-only `generic-uio` nodes, level-typed so a second event raised before ack is not coalesced away, replacing a cause-register spin over the LW bridge. The node **name** is the ABI (`/sys/class/uio/uioN/name`, derived via `%pfwP`), never the minor — adding any UIO node renumbers every later minor. | +| `0044` | **Named, size-bounded mmap** of the two FPGA windows replacing raw `/dev/mem`, so a mapping bug is an `mmap` failure rather than a write into arbitrary physical memory. Carries two hardware rules: **H-1** (a read of an *undecoded* LW offset **hard-hangs the HPS** — no bus fault, no exception, no panic, not one console byte, power-cycle only; confirmed 3× on silicon, once with a capturing serial console; writes presumed equally lethal, never tested) and **MAP_SHARED-always** (UIO has no `VM_SHARED` check anywhere in `drivers/uio/uio.c`, so `MAP_PRIVATE` silently COWs and every store goes to anonymous RAM the FPGA never sees — while a read-back through the same mapping still passes). | +| `0045` | Lifts a **page-attribute throughput floor** measured on Cyclone V at ~100 MB/s write / ~54 MB/s read (23.3 µs for a 2352-byte sector) — a property of the page attribute, not of the bridge. | +| `0046` | A **crash record that survives a warm reset**. Stated honestly by the patch itself: for an H-1-class hang there is **no kmsg dump** — the kernel never reaches its die path, so the dmesg records stay empty for exactly the failure that motivated the node. What survives is the **console** area (printk path) and the **pmsg** area (`/dev/pmsg0` breadcrumbs), which converts a silent brick into a *named offset*. | + +**Agilex path.** DP-9 stands and is not re-litigated; `de25-fpga-reconfig.md` is authoritative. +Three things to carry forward. **(i)** D0.2 **splits** DP-9 and the split must not be lost: Claim +A (fpga-region is the proper — indeed *sole* — reconfiguration architecture on Agilex 5) is +**CONFIRMED [V]**; Claim B (fpga-region stands *"in place of"* 0043–0045) is **REFUTED [V]** — +`of-fpga-region.c:353-379` fires only on overlay apply/remove and has nothing to say about how a +*running* core signals the HPS or how the HPS reaches its registers and shared memory. **The +runtime signalling + aperture + coherency contract is open and needs its own DP** (§7). +**(ii)** `0043`/`0044` are literally unportable and would be even if fpga-region did not exist. +**(iii)** `0046`'s *address* is Cyclone-V-only, but its *capability* is board-agnostic and +arguably more valuable on DE25 — and on Agilex 5 the arithmetic must be re-derived from scratch: +DRAM starts at `0x80000000`, the first 32 MiB is already reserved by `svcbuffer@0` +(`socfpga_agilex5.dtsi:23-27`, `no-map`) **[V]**, arm64 has **no HIGHMEM** at all so the +lowmem-hole placement argument *ceases to exist* rather than changing its numbers **[V]**, and +there is no `mem=` cap in the DE25 boot story. + +> **Capability to preserve — doorbells and apertures.** +> 1. **Blocking wake:** a consumer blocked on a doorbell fd is woken by a fabric event; the +> per-line counter increments exactly once per event; two events raised before the first ack +> yield two counted events. +> 2. **Named bounded mapping:** the *name* is the stable handle and survives adding other nodes +> (which renumber every later minor); the exported size equals the DT size; an mmap past it +> **fails** rather than reaching adjacent physical memory. This is the whole point of replacing +> `/dev/mem`, and the one property trivially lost by "just use `/dev/mem` again on aarch64". +> 3. **Attribute discipline:** the register window maps Device/strongly-ordered and **never** +> write-combining — on arm64 restate the argument as *Normal-NC is speculatively readable*, so +> a WC mapping of a window where undecoded reads hang the board could hang it off a +> **mispredicted path**, with no program access at any bad offset. The bulk aperture maps +> Normal-NC and must materially beat the Device floor; measure write and read bandwidth the way +> they were measured on DE10 (~100 / ~54 MB/s is the *failure signature*, not the target). +> 4. **MAP_SHARED:** a `MAP_PRIVATE` mapping must be rejected, or proven not to silently COW. +> Today it silently COWs on both arches and passes a same-mapping read-back self-test. Make it +> an actual test, not a comment. +> 5. **Never-probe (H-1):** the rule that *mapping* the register window is safe but *touching* an +> undecoded offset is lethal must be re-established on DE25 hardware, not assumed to carry. It +> covers reads **and** writes; only reads were ever measured. +> 6. **Crash record:** after `echo c > /proc/sysrq-trigger` **and** after a watchdog-induced warm +> reset, `mount -t pstore pstore /sys/fs/pstore` yields `console-ramoops-0` and +> `pmsg-ramoops-0` containing pre-hang text. Preserve the honest limits in the docs as well as +> the code: for a hang that never reaches the die path the dmesg records are empty **by +> design**, and DRAM survives a warm reset, not a power cycle — which is the argument for a +> watchdog that can turn a hang into a warm reset. Agilex 5 exposes five `snps,dw-wdt` +> instances for it (`socfpga_agilex5.dtsi:431-470`, all `disabled`) **[V]**. + +--- + +## 5. The hypothesis — did the owner's prior survive? + +**The prior.** *"The majority of patches should convert just fine as they're mostly input +related. The ones we need to worry about are the MiSTer / DE10-Nano specific ones for vsync, +framebuffer, and f2h_irq. I would bet money the USB/HID input patches (majority) will be OK on +DE25-Nano."* + +**Verdict: SURVIVED on its own terms — 25 of 25 — and it is the *complement* that needs +amending.** + +### 5.1 What was actually checked + +The instruction was to hunt for input/HID patches that are secretly arch- or platform-coupled. +Every one of the 25 patches under `drivers/hid`, `drivers/input` and `drivers/hid/usbhid` was +opened and its hunks read, and these specific coupling classes were looked for **by name**: + +| Coupling class hunted | Found in any HID/input patch? | +|---|---| +| 32-bit `time_t` / `timeval` in a struct or ioctl | **No.** No HID/input patch touches a time-bearing struct. | +| ioctl argument that is a **pointer** (needs `compat_ptr()`/`->compat_ioctl`) | **No.** The only ioctl added anywhere in the 25 is `EVIOCGRAB` in `0026`, whose argument is a **bool, not a pointer** — and the patch header records that its `CONFIG_COMPAT` wiring was *"kept correct for other arches"*, i.e. it was written with non-ARM32 targets in mind. **[V]** | +| Unaligned access / direct dereference of a mapped aperture | **No.** No HID/input patch maps anything; all use `copy_from_user`/`hid_hw_*`/URB buffers. | +| Endianness in an on-wire structure | **No — and where it mattered it was already correct.** `0017` uses `le16_to_cpup()` for the Flydigi raw-mode stick axes; `0036` uses `le16_to_cpu()` on `lmp_subver`. **[V]** | +| DMA / coherency assumptions | **No.** `0011` and `0017` use `usb_alloc_coherent()`, which is the USB core's own API and arch-neutral; none allocates a DMA buffer whose *address* is exposed to anything. | +| `__u32` vs pointer-size struct layout (a pointer stuffed into a fixed-width field) | **No — in the HID/input set.** But see §5.3: this exact hazard **was** found, twice, outside it. | +| A HW block Agilex 5 lacks or has a different version of | **No.** The one hardware-adjacent patch in the set, `0028` (dwc2), targets an IP Agilex 5 **also has** (`snps,dwc2`, `socfpga_agilex5.dtsi:419`), and the bug it fixes is generic host-controller logic that the patch header itself recommends upstreaming. | +| Upstream supersession (patch no longer needed on 6.18) | **No.** Checked case by case against `output/build/linux-6.18.44`. The closest call is `0035`, where provenance records a **partial** upstream fix — the *set*-failure path landed upstream, the *registration*-failure path this patch addresses is still fatal — so it is **carry, not superseded** (`patch-provenance.md:363`) **[V]**. | + +**A tested hypothesis that survives is a real result, and this one did.** It is worth being +explicit about *why* it held rather than treating it as luck: the HID/input patches are +overwhelmingly (a) device-ID table rows, (b) evdev keycode/axis table data, (c) LED-classdev and +sysfs naming, (d) probe-failure tolerance, and (e) self-contained USB drivers built on the USB +core's own DMA-safe APIs. None of those five categories has an architectural surface. That is a +structural reason, not an anecdote, and it is why the prior was a good bet. + +### 5.2 One caveat that is not a counter-example + +`0037` (DualSense mic-mute as `BTN_Z`) is portable in the arch sense and shared in the series +sense — but it is the project's known trap for a *different* reason: `BTN_Z` (0x135) sits between +`BTN_WEST` and `BTN_TL`, so it shifts every higher `EV_KEY` ordinal and therefore every +`gamecontrollerdb` `bN` index. The RT beta series drops it. **That is a known divergence, not a +precedent.** It must be in the shared series on both boards, because the same Main_MiSTer +userspace reads the same database on both. The same reasoning applies to `0034`, `0039`, `0040`, +`0041` and `0042`: they *look* cosmetic (table swaps, format strings) and are **userspace ABI**. +An arch audit that filed them as "trivially portable, low value" would be right about the arch +and wrong about the stakes. + +### 5.3 The counter-examples — where the prior is wrong + +They are all **outside** the HID/input set, and two of the three are **outside the vsync / +framebuffer / f2h_irq set the prior names**: + +1. **`0002` — MiSTer audio SPI. The single hardest arm64 break in the whole audit, and the prior + does not name audio at all.** It does not compile for aarch64: an `unsigned int` struct member + is passed where `dma_addr_t *` is required. **[V]** And the type fix alone is wrong — the + member is FPGA wire format, so the real question is a gateware one (§4.4). +2. **`0001` — MiSTer_fb.** The prior *does* name the framebuffer, so this is a confirmation, not + a counter-example — except in one respect: the prior's framing is "board-specific hardware, so + we'll rewrite it", whereas the two findings here are **generic arm-vs-arm64 defects that would + bite any port of this code even to a board with identical hardware**: arm-only `NO_IRQ`, and a + `MEMREMAP_WT` correctness argument that is false on arm64. **[V]** +3. **`0043` — the doorbell DTS.** Board-specific as expected, but the deep dive found an arch + coupling triage missed that lives at the **Kconfig** level, not in any data structure: + `CONFIG_CMDLINE_EXTEND` **does not exist on arm64** (defined in arm, riscv, powerpc, sh, + loongarch and `usr/Kconfig` only — `linux:arch/arm64/Kconfig` offers `CMDLINE`, + `CMDLINE_FROM_BOOTLOADER`, `CMDLINE_FORCE`) **[V]**. So the DE10's `generic-uio` binding recipe + has no arm64 counterpart; a DE25 would need U-Boot bootargs / DT `/chosen`, or + `UIO_PDRV_GENIRQ` as a module with a `modprobe of_id=`. `CMDLINE_FORCE` is **not** an + acceptable substitute — it discards the bootloader's `root=`/`console=`. + +**Bottom line for whoever ports.** The prior is a good *triage* heuristic and a bad *stopping +rule*. The right generalisation is: **the risk is not in the subsystem, it is in whether the +patch's correctness argument mentions an address, a memory attribute, a DMA address, or a fixed +width.** Grep the 40 patch *headers* for `LPAE`, `dma_addr_t`, `memremap`, `NO_IRQ`, +`pgprot_`, `strongly-ordered`, `mem=` and `CMDLINE` before grepping the code; every finding in +this audit would have surfaced. + +--- + +## 6. Reconciliation against the plan's "~28 portable / ~8 board" + +`de25-nano-plan.md:135-136` estimates *"Kernel patches — input/HID (~28) … port as-is"* and +*"Kernel patches — board (~8: MiSTer_fb, audio SPI, DTS, overclock…)"*. + +**Measured: 33 shared / 7 DE10-only, over a union of 40.** The estimate was close, and every +delta is explainable. + +| Delta | Explanation | +|---|---| +| **Denominator: 36 → 40.** | The plan's row is written against the 36-patch shipped series (its own text says "all 36+40 patch-by-patch", so it anticipated the union). The union adds `0043`–`0046`, all beta-local; **three of the four are board-specific**, which is why the board count grew rather than the portable count. | +| **"~28 input/HID" → 33 shared.** | The plan's 28 counts the *input/HID class*. The strict HID/input/usbhid count is **25**. The shared series is larger — 33 — because it also holds **8 non-HID portable patches** the row's label does not cover: `0020` (mmc core), `0026` (input core + mousedev — arguably in-class), `0027` (mt76 ID table), `0028` (dwc2), `0029` (leds-gpio), `0030` (i2c-designware), `0031` (exfat), `0036` (btusb), plus `0002` at portable-with-rework. **Net: the plan under-counted reuse.** | +| **"~8 board" → 6 board-specific (7 DE10-only).** | Two reclassifications. **(a)** `0002` (audio SPI) is named in the plan's board list but is judged **portable-with-rework, shared**: its driver code is subsystem-generic C, and only the DT binding and the gateware wire contract are board-coupled — so it belongs in the shared series behind a defconfig gate, not in a board series. **(b)** `0045` is **de10-only by DP-9, not by portability** — the patch itself is generic arch-independent kernel code. Counting the plan's way (audio as board), the figure is 7 against an estimate of ~8: **within one.** | +| **"port as-is" for the whole input/HID row.** | **Confirmed for all 25 [V]** — no rework required on any of them. | +| **The plan's "meaningless until L0; do not port speculatively" for the board row.** | **Confirmed and reinforced.** §4 additionally establishes that mainline 6.18.44 does not yet supply the Agilex-5 idioms a port would build on: no `fpga-mgr`, no `fpga-region`, no bridge nodes, **no MMC/SD node at all**, and **no driver for `intel,agilex5-clkmgr`** (binding and dt-bindings header only) **[V]**. That is a fact D0.2/D1 must confront rather than assume. | +| **Superseded-upstream: 0.** | The plan does not predict a count. None was found; the nearest call (`0035`) is *partially* landed and remains a carry (`patch-provenance.md:363`). | + +**Net assessment: the plan's estimate holds.** It under-stated reuse slightly (33 vs 28) and +over-stated the board burden slightly (6–7 vs 8). No structural surprise. The surprises are all +in §4 and §5.3, and none of them changes a *count*. + +--- + +## 7. Open questions — what reading could not settle + +| # | Question | Tag | What settles it | Blocks | +|---|---|---|---|---| +| 1 | What GIC SPI range carries `f2h_irq` on Agilex 5, and how many lines? `socfpga_agilex5.dtsi` declares none. | [U] | Agilex 5 HPS TRM f2h interrupt map — `de25-fpga-reconfig.md` §7.2 records TRM 814346/813752 returning **HTTP 403** on 2026-08-21, never read — **plus** the DE25 GHRD's `f2h_irq` wiring. | Any vsync node, any doorbell node (§4.2, §4.3) | +| 2 | Where does the HPS-visible, fabric-writable DRAM window live on Agilex 5, and how big? | [U] | The DE25 GHRD. `de25-fpga-reconfig.md` §7.3 identifies LPDDR4A as the candidate bank (DE25 UM §3.7.4) and marks the address pending. | Framebuffer node, `ramoops` placement, any shared aperture | +| 3 | Is HPS↔fabric coherency automatic on Agilex 5, or **opt-in per transaction** via an AXUSER signal the fabric master drives? | [U] | The TRM (403, never read) or a hardware experiment on the GHRD. `de25-fpga-reconfig.md` §7.2 says opt-in, **from search snippets only**. | Whether a ported `MiSTer_fb` window and the MrAudio ring need explicit cache maintenance — a **gateware** constraint | +| 4 | Does SPIM0 reach FPGA fabric pins on the DE25 board at all? | [U] | DE25 schematic / UM pin tables. | Whether the MiSTer audio topology has any DE25 analogue (§4.4) | +| 5 | Is Agilex 5's SDM served by the existing `intel,agilex-svc` / `intel,agilex-soc-fpga-mgr` drivers, or does it need new compatibles? No agilex5 DTS in 6.18.44 instantiates either, and `drivers/fpga` contains no `agilex5` string **[V]**. | [U] | A newer kernel, the Altera GSRD tree, or the A5-series upstream patches `de25-fpga-reconfig.md` §3.1 tracks. | DP-9's *execution* — the native idiom has no in-tree starting point at this kernel level | +| 6 | Where does the DE25's `intel,agilex5-clkmgr` driver come from? Binding + dt-bindings header exist; **no driver does** **[V]**. | [U] | D0.2/D1 — vendor tree, later mainline, or ATF/SCMI-managed with no Linux clkmgr driver. | Any Agilex 5 clock/DVFS question (§ row 3 of the table) | +| 7 | Does the H-1 failure class (undecoded fabric read wedging the CPU silently) reproduce on Agilex 5, or does the NoC/firewall terminate it as a bus error/SError? | [U] | Hardware. If Agilex 5 aborts cleanly, the console/pmsg areas drop from "only forensic channel" to "convenience". | The never-probe rule, and how much `0046`'s capability is worth on DE25 | +| 8 | Is `0002`'s 16-byte descriptor widened to carry a 64-bit ring address, or is the ring constrained below 4 GiB? | **Owner decision** | Must be taken **before** the driver is ported; a kernel-only fix truncates silently. | §4.4 | +| 9 | Does DE25 need `/dev/fb0` at all, or does an aarch64 Main_MiSTer HAL replace the vsync ioctl with a doorbell `read()`? (DP-10 is tabled and may make this DRM/KMS.) | **Owner decision**, gated on L0 | Determines whether `0001` is forward-ported or retired — and therefore whether the `NO_IRQ` / `MEMREMAP_WT` fixes are worth making. | §4.1, §4.2 | +| 10 | Split `0045` so the generic UIO memtype goes shared/upstream, keeping only the property parse board-local? | **Owner decision** | Its own header says the generic half is upstreamable. `de25-fpga-reconfig.md` §8 states DP-9 does **not** settle this. | §3.2(b) | +| 11 | Split `0002` into a shared `dummy.c` patch and a DE10-only audio-spi patch, or keep it whole behind a defconfig gate? | **Owner decision** | The halves ship together on DE10; splitting is safe there only if both stay applied. | §3.2(a) | +| 12 | **Blocking follow-up inherited from D0.2.** `de25-fpga-reconfig.md` §8 records that `de25-nano-plan.md` §6's DP-9 bullet still reads *"in place of the carried UIO doorbell patches"*, which D0.2's own analysis **refutes**, and that a **new DP** must be opened for the runtime HPS↔FPGA signalling / aperture / coherency contract. **That edit has not been made.** | **Action item** | An edit to `de25-nano-plan.md` §6 plus a new DP. | Until it lands, plan §6 reads as having settled a problem D0.2 explicitly found unsettled. | +| 13 | **Measurement debt — blocks every DE25 comparison in §4.** Three DE10 baselines the capability tests compare against **do not exist**: vsync inter-frame interval and jitter (only *"returns 0"* was ever gated, `abi-contract.md` H-2), wake-to-userspace doorbell latency, and core-switch duration (`de25-fpga-reconfig.md` §1 calls the "a few seconds" figure [U], unmeasured). | [U] | **Take them on DE10 first.** No hardware needed beyond the board already in use. | Otherwise every "no worse than the DE10" test in §4 is vacuous. | + +--- + +## 8. Verification obligation — the spot-check list + +Per the D0.3 brief, every **portable** verdict that touches `arch/`, a DTS, or a Kconfig gets an +independent spot-verification pass. Enumerating the diffs by target path **[V]**, six patches +qualify strictly: + +| Patch | Verdict | Path that triggers the check | What the check should target | +|---|---|---|---| +| `0002-sound-add-MiSTer-audio-spi-and-snd-dummy-MiSTer-model` | portable-with-rework | `sound/drivers/Kconfig` | **The `dma_addr_t` finding.** Confirm the call site and that `-Wincompatible-pointer-types` is an error, not a warning, in this tree's toolchain. Confirm the proposed fix does **not** widen `Info_t`. | +| `0010-hid-guncon2` | portable-as-is | `drivers/hid/Kconfig` | New-symbol placement and `depends on` chain; that nothing in the added driver assumes 32-bit. | +| `0011-hid-guncon3` | portable-as-is | `drivers/hid/Kconfig` | Same, plus the `usb_maxpacket()` 2-arg form against 6.18. | +| `0012-hid-fanatec` | portable-as-is | `drivers/hid/Kconfig` | Largest patch in the shared series (~1745 lines); confirm no `unsigned long`-width or jiffies-arithmetic assumption slipped past triage. | +| `0013-hid-flydigi-vader` | portable-as-is | `drivers/hid/Kconfig` | Symbol placement; report-parsing widths. | +| `0014-hid-gamecube-adapter` | portable-as-is | `drivers/hid/Kconfig` | Symbol placement; the RCU/`work_struct` hot-plug path. | + +**Plus one added by judgment, not by the rule** — `0045-uio-writecombine` touches none of those +paths (`drivers/uio/*.c`, `include/linux/uio_driver.h`, `Documentation/`), but it carries a +**portable-as-is** verdict that rests entirely on an **arch memory-attribute claim** +(`pgprot_writecombine` = `MT_NORMAL_NC` vs `pgprot_noncached` = `MT_DEVICE_nGnRnE` on arm64), and +it is the same claim class that produced the `MEMREMAP_WT` finding in `0001`. It should be +spot-checked with the six. + +No `board-specific` verdict needs spot-verification for portability — but `0043`'s +`CMDLINE_EXTEND` finding and `0001`'s `NO_IRQ` / `MEMREMAP_WT` findings are the audit's most +load-bearing negative results and are cheap to re-confirm (three greps, all cited inline in §4 +and §5.3). + +--- + +## 9. Method, and what this pass did not do + +**Did not:** run git, commit, build, run `make`, touch CI, move a file, write a `series` file, or +rebase anything. **Did:** open all 40 patch files; grep `patch-provenance.md` per patch rather +than reading it end to end; read the relevant `kernel-recon/` records; and check every +Agilex-side and arm64-side claim against `output/build/linux-6.18.44`. + +**Where the depth is uneven, §2 flags it (⚠) and §1 says how to read the flag.** Eight patches +got a deep dive plus a cross-cutting mechanism pass; 32 got a single triage read. Three of the +eight deep dives overturned or materially corrected their triage verdict — all three on +memory-attribute, DMA or Kconfig grounds, none on subsystem grounds. **The un-deep-dived rows are +device-ID tables, evdev keymaps, format strings and log-severity changes; the confidence in them +is high and the reason is structural, not statistical (§5.1).** + +**One caution about the `[V]` tags in this document.** They mean *a file was opened and the cited +lines read* — by this pass for every claim in §4 and §5.3 and for the arch findings, and by the +triage/deep-dive legs for the per-patch rows in §2, whose sources are cited inline. **No `[V]` +here means "built", "booted" or "measured".** Every one of the §4 capability tests is written to +be run on hardware that this audit never touched, and several of them compare against a DE10 +baseline that has never been taken (§7 row 13). From 17cee7d47cc15a9f11db79b8a0821f907c119031 Mon Sep 17 00:00:00 2001 From: "Michael C. Ferguson" Date: Sat, 22 Aug 2026 23:50:21 -0500 Subject: [PATCH 2/4] =?UTF-8?q?docs(de25):=20D1=20readiness=20guards=20?= =?UTF-8?q?=E2=80=94=20coupling=20ledger,=20arch-assert=20design,=20channe?= =?UTF-8?q?l=20namespace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase D1. Docs only by design: no script, config, workflow or package file is touched, per ADR 0027 Decision 3. de25-readiness-ledger.md (D1.1) covers every de10nano / BR2_arm / zImage site outside board/mister/de10nano/ and docs/ — 54 files, 457 lines, 65 rows, 14 of them semantic blockers. Each row carries a one-sentence "when you touch this, do this instead" instruction, which is the actual product. The reconciliation command is git grep, not grep -r, and that matters: the grep wrapper in this environment honours .gitignore, so the same query returns 457 lines here and 15,796 under GNU grep, which walks work/ and .claude/. A coverage count that depends on the operator's grep is not a coverage count. Three couplings ADR 0027's "four semantic couplings" did not anticipate: lint.yml hard-codes ~20 board paths and fails silently, so a second board would go unlinted behind a green check — a live hole for the DE10 today, not only a DE25 problem; the renovate.json + renovate-hash-sync.yml bump axis, where a new defconfig added without them lands a stale kernel pin; and release.yml's 13 coupled lines. D1.2's arch-assert generalization is designed as a section of the ledger and holds fail-closed for the DE10, but the code change is still owed — both guards hard-assert ^BR2_arm, so D2.1's aarch64 defconfig cannot land until it exists. D1.3 adds forward-pointer sections reserving the DE25 channel namespace: downloader-contract.md §13 and a DE25 section in db-json-versioning.md. Both state the names are reserved, not implemented. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FYjrnzEEu4HzJSPWNAaRvm --- docs/db-json-versioning.md | 13 + docs/de25-readiness-ledger.md | 438 ++++++++++++++++++++++++++++++++++ docs/downloader-contract.md | 32 +++ 3 files changed, 483 insertions(+) create mode 100644 docs/de25-readiness-ledger.md diff --git a/docs/db-json-versioning.md b/docs/db-json-versioning.md index d2d027e..9a29dc0 100644 --- a/docs/db-json-versioning.md +++ b/docs/db-json-versioning.md @@ -107,3 +107,16 @@ back to back** from an already-subscribed user base. Both columns can't simultaneously be "yes" without a P2.6 change to how `/MiSTer.version` itself is derived. This task picks the column that keeps updates flowing, and documents the cost. + +## DE25-Nano: reserved, not a second version scheme + +[ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md) reserves a second Pages +document, `db-de25nano.json`, for a possible future DE25-Nano board — not implemented +yet; see [`docs/de25-nano-plan.md`](de25-nano-plan.md) for the staged plan. The +reservation does not introduce a second version scheme: the release-date-driven approach +documented above (ADR 0018) carries over unchanged, and a future DE25 `db.json` would +derive its own `linux.version` from *its own* release's publish date the same way this +one does. Each board's db.json versions independently — a DE25 release date has no +bearing on, and is never compared against, the DE10 db.json's version or the DE10 +`/MiSTer.version`, and vice versa. Do not read the "residual trade-off" above as +something a second board doc could resolve or share; it is per-board by construction. diff --git a/docs/de25-readiness-ledger.md b/docs/de25-readiness-ledger.md new file mode 100644 index 0000000..1928c24 --- /dev/null +++ b/docs/de25-readiness-ledger.md @@ -0,0 +1,438 @@ +# DE25-Nano readiness ledger — every `de10nano` / `BR2_arm` / zImage site outside the board dir + +**Status:** D1.1 complete 2026-08-21 — **docs only, no code changed**. This file is also the +home of **D1.2** (§5), which has no separate deliverable file of its own. Nothing here has +been applied: §6's diffs are *proposals for separate review*, not commits. Claims are tagged +**[V]** (a file was opened and the cited line read) / **[U]** (unverified, named as such). + +**Cross-refs:** [ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md) (the four +couplings, Decision 3's obligation), [`de25-nano-plan.md`](de25-nano-plan.md) §3 (the same +four, with what each needs), [`de25-nano-tasks.md`](de25-nano-tasks.md) §D1 (this task and its +accept criterion), [`de25-boot-chain.md`](de25-boot-chain.md) (D0.1's boot-flow research that +DP-2 depends on). + +--- + +## 1. Why this document exists, and what it is *not* + +A fresh grep for `de10nano|BR2_arm|zImage` outside `board/mister/de10nano/` and `docs/` hits +**55 files and 457 lines** (§7). Almost all of them are a **path** or a **name**. Exactly four +things in this repo are *semantically* ARM32/Cyclone-V — they encode a fact about the silicon +or the boot ROM that has no aarch64 analogue at all **[V** ADR 0027:42-46**]**: + +| # | Coupling | Where it actually lives | Why it cannot be parameterized | +|---|---|---|---| +| (a) | `zImage_dtb` cat-concatenation contract | `scripts/check-zimage-dtb.sh`, `scripts/inventory/kernel_extract.py`, `board/mister/de10nano/post-image.sh` | U-Boot reads the DTB offset out of the ARM zImage header's declared-end field at `+0x2C` **[V** `check-zimage-dtb.sh:5-16`**]**; arm64 `Image` has no header field to read | +| (b) | 0xA2 BootROM SD partition layout | `board/mister/de10nano/genimage-sdcard.cfg:133`, `scripts/mk-sdcard.sh`, `scripts/check-sdcard.sh:92,203-212` | The Cyclone-V BootROM scans the MBR for `sys_ind == 0xA2` **[V** `genimage-sdcard.cfg:17-19`**]**; Agilex boots via SDM/FSBL from QSPI into a GSRD-shaped card | +| (c) | `^BR2_arm` / `^BR2_cortex` asserts | `.github/actions/buildroot-build/action.yml:190` **[V]**, `scripts/check-kernel-defconfig-sync.sh:83,129` **[V]** | Buildroot's arch symbols are mutually-exclusive Kconfig *choices*, not values a variable can carry | +| (d) | armv7-pinned `package/azcopy` | `package/azcopy/Config.in:64` (`depends on BR2_arm`) **[V]** | Gates a Go cross-build that only exists for 32-bit ARM | + +Everything else — 51 of the 55 files — says `de10nano` because that is where the board's files +*live*, or says `zImage_dtb` because that is what today's only kernel artifact is *called*. +Those are cheap: a `BOARD=` variable, or a second sibling file, fixes them at the moment +someone touches them. **Collapsing the two classes together is the failure mode this ledger +exists to prevent** — it would either (i) invite someone to "generalize" the zImage header +parser, which is not generalizable, or (ii) inflate the DE25 port's apparent cost from four +real design decisions to fifty-five. + +**Decision 3's obligation, restated:** we do not pre-build DE25 plumbing. We stop *deepening* +the four. Any newly written or substantially rewritten script/CI step that would hard-code +`de10nano`, `BR2_arm`, or zImage semantics takes a board parameter instead **[V** ADR +0027:74**]**. + +--- + +## 2. The ledger — scripts + +Severity: **semantic-blocker** = do not parameterize, a design decision is owed first; +**parameterize** = mechanical `BOARD=` substitution when next touched; **cosmetic** = a name +in prose, no action or a one-word reword. + +| file:line | coupling | sev | when you touch this, do this instead | +|---|---|---|---| +| `scripts/check-zimage-dtb.sh:3,7-10,14-15,27,66-81` | The whole script asserts the U-Boot↔ARM-zImage contract: magic `0x016f2818` at `+0x24`, DTB offset read from `+0x2C` **[V]** | semantic-blocker | Leave it ARM-only forever and write a wholly new checker for whatever DP-2 picks — there is no offset to parameterize on aarch64. | +| `scripts/inventory/kernel_extract.py:3-22,41-95,135-150` (whole 180-line module) | Parses the same ARM zImage self-relocating header to carve `zImage_dtb` apart **[V]** | semantic-blocker | Write a separate `kernel_extract_fit.py` selected by board rather than adding an aarch64 branch to this parser. | +| `scripts/mk-sdcard.sh:29-63,107-131,190,316-541,711` | Builds the installer card around both (a) and (b): snapshots/relinks/restores `output/images/zImage_dtb`, then runs genimage on the 0xA2 `genimage-sdcard.cfg` **[V]** | semantic-blocker | Give DE25 its own card-assembly script against its own genimage config once DP-2 and the Agilex GSRD layout are pinned; do not thread an arch branch through this one. | +| `scripts/check-sdcard.sh:10-21,51,73,91-92,113,203-212` | Asserts MBR p1=FAT32-LBA / p2=type `0xA2` ≥1 MiB and that the 0xA2 head is the pinned `uboot.img` **[V]** | semantic-blocker | This is coupling (b) in assertion form — write a sibling checker for the Agilex layout instead of relaxing these constants. | +| `scripts/test-sdcard-install.sh:8-10,60,67,70,77-78,139-154` | QEMU install test builds and boots an ARM `zImage` end to end (`ARCH=arm`, `arm-buildroot-linux-gnueabihf-`, `multi_v7_defconfig`, `arch/arm/boot/zImage`) **[V]** | semantic-blocker | Write a dedicated aarch64 install-test script once DP-2 fixes the DE25 payload format; do not bolt an `if aarch64` branch onto this harness. | +| `scripts/test-initramfs.sh:46,60,67,83,90,94,174,191,217-221,275,365` | Same shape: `qemu-system-arm -M virt` + `multi_v7_defconfig` + `arch/arm/boot/zImage`, because QEMU has no Cyclone-V model **[V]** | semantic-blocker | Mirror this script for `qemu-system-aarch64 -M virt` as its own file (the cpio itself is arch-neutral **[V** plan §3 item 1**]**); *separately*, `:83`'s hardcoded `configs/mister_de10nano_defconfig` version read is a mechanical `BOARD` substitution. | +| `scripts/export-kernel-tree.sh:63,159-160,706-707,917-918,933-938,977,991,1025,1124-1126,1154-1155,1176,1184` | Every recipe it emits (header, `EXPORT.md`, `build-mister-modules.sh`, `README.md`) hardcodes `ARCH=arm`, `arm-linux-gnueabihf-`, and the `zImage` target **[V]** | semantic-blocker | Write a second export path for the Agilex kernel when D2 gates it — this script exports *the DE10's* patch series and `MiSTer_defconfig`, so an `ARCH=` flag would be a lie. | +| `scripts/check-kernel-defconfig-sync.sh:83,129` | Sentinel + family loops asserting `BR2_arm`/`BR2_cortex_a9`/`BR2_ARM_` **[V]** — ADR 0027 coupling (c) | semantic-blocker | Do not widen the sentinel set; adopt §5's per-board expected-symbol table so DE25 gets its own row rather than a loosened shared check. | +| `scripts/check-kernel-defconfig-sync.sh:61-62` | `MAIN_DEFCONFIG`/`KERNEL_DEFCONFIG` hardcode the two de10nano filenames **[V]** | parameterize | Take `BOARD` (env or `$1`, default `de10nano`) and compose both paths from it — see §6.1. | +| `scripts/check-linux-img.sh:8,14,41` | Pins the ext4 feature set/label/UUID against `configs/mister_de10nano_defconfig` by name **[V]** | parameterize | Read the pinned contract from `configs/mister_${BOARD}_defconfig`; the ext4 assertions themselves are arch-neutral and carry over. | +| `scripts/hash-sync-kernel.sh:12,53,134,196,223` | Reads the kernel version from `configs/mister_de10nano_defconfig` and writes `board/mister/de10nano/patches/linux/linux.hash` **[V]** | parameterize | Both paths are board-scoped data, not arch facts — compose them from `BOARD` when this script is next touched. | +| `scripts/lint-kernel-patches.sh:53,67` | `DEFCONFIG` hardcodes `configs/mister_de10nano_defconfig`; lints the series named in it **[V]** | parameterize | Same `BOARD` substitution; the provenance-header lint itself is board-agnostic and should lint every board's series. | +| `scripts/fetch-sdcard-payload.sh:8-12,53,361` | Comments describe overlaying our `linux.img`/`zImage_dtb`; `:361` reads `board/mister/de10nano/fat-payload` **[V]** | parameterize | `:361` is a `BOARD` path substitution; the `zImage_dtb` mentions are naming that follows whatever DP-2 names the DE25 payload. | +| `scripts/ci-tests.sh:80,110-113,202,932,1059-1095,1200-1221,1301-1360,1595,1763` | `$IMAGES/zImage_dtb` plus ~12 `board/mister/de10nano/...` reference paths in assertions and their failure messages **[V]** | parameterize | Compose the board dir once from `BOARD` at the top and use it throughout; every individual assertion here is arch-neutral. | +| `scripts/check-fork-sync.sh:8-9,115-116` | Names `linux-patches/` and `linux-patches-upstream/` under `board/mister/de10nano/` in code and in generated prose **[V]** | parameterize | Board-scope the two directory paths; the carried/upstream-only taxonomy is board-independent. | +| `scripts/verify-stock-payload.sh:474` | `must` list includes `files/linux/zImage_dtb` — the stock DE10 payload's own filename **[V]** | cosmetic | Leave it: this verifies *stock MiSTer's* published payload, which will never contain an aarch64 artifact. | +| `scripts/list-kernel-variants.sh:44` | Comment notes the variant probe looks for `output-main/images/zImage_dtb` **[V]** | cosmetic | Reword to the board's image name only if/when the variant registry itself grows a board axis. | +| `scripts/ci-lib.sh:37` | Comment explains a size helper's GPL reasoning by naming `zImage_dtb` **[V]** | cosmetic | No action; `ci_lib_sz` is a byte-counter with no format knowledge. | +| `scripts/test-timezone.sh:5,56` | `SRC` points at the dhcpcd hook under `board/mister/de10nano/rootfs-overlay/` **[V]** | parameterize | `BOARD` path substitution; the hook and its test are arch-neutral. | +| `scripts/test-installer-splash.sh:4,25` | `INIT` defaults to `board/mister/de10nano/installer-overlay/init` **[V]** | parameterize | Same substitution — note `:25` already accepts `$1`, so the default value is the only hardcode. | +| `scripts/test-initramfs/qemu-test-kernel.config:8` | Comment says this fragment is *not* `board/mister/de10nano/linux.config` and never will be **[V]** | cosmetic | No action; a DE25 QEMU harness brings its own fragment (see the `test-initramfs.sh` row). | +| `scripts/test-initramfs/marker-init.c:10` | Comment points at the real init it stands in for **[V]** | cosmetic | No action. | +| `scripts/inventory/gen-kernel-config-dts.sh:2,5,34,140,143` | Usage/prose take a `` argument and document the extraction method **[V]** | cosmetic | Rename the argument to the payload's actual name if this is ever pointed at a DE25 artifact; the `dtc` half is format-agnostic. | +| `scripts/inventory/run-all.sh:2,13,31,73` | Optional 2nd argument named `zImage_dtb`, feeding item (f) **[V]** | cosmetic | Same reword-only note. | +| `scripts/inventory/README.md:12,20,27,53,65` | Documents that argument and the stock artifacts it reads **[V]** | cosmetic | Reword alongside the two scripts above. | +| `scripts/inventory/lz4_legacy.py:3,105` | Docstrings say "zImage"; the decoder itself handles the kernel-generic legacy LZ4 frame magic `0x184C2102` **[V]** | cosmetic | **Reuse this module as-is** for any DE25 payload compressed the same way — only the docstrings' `zImage` wording needs updating. | + +## 3. The ledger — CI + +| file:line | coupling | sev | when you touch this, do this instead | +|---|---|---|---| +| `.github/actions/buildroot-build/action.yml:190-195` | Toolchain-fingerprint sentinel requires `^BR2_arm` and `^BR2_cortex` or the build fails loud **[V]** — coupling (c) | semantic-blocker | Adopt §5's `BOARD_FINGERPRINT_SENTINELS` row lookup with a validated `board` input; never add a second arch string beside these, and never soften the assert to a warning. | +| `.github/actions/buildroot-build/action.yml:152,154` | `fp_defconfig` branches on `variant` only, and hardcodes `configs/mister_de10nano_defconfig` for `main` **[V]** | parameterize | The board and variant axes are orthogonal — resolve the defconfig from `BOARD` *and* `VARIANT` rather than adding a third literal filename. | +| `.github/actions/buildroot-build/action.yml:166` | `pd="board/mister/de10nano/patches/$d"` for the toolchain-patch hash **[V]** | parameterize | Board-scope the path; the deny-list filter at `:158`/`:181` stays global policy and must not be board-keyed. | +| `.github/actions/kernel-leg/action.yml:4,21,131,146-167,177,198` | Stages `output-$KERNEL/images/zImage_dtb` as `zImage_dtb-$KERNEL` and renders it into the job summary and the `bootimage=` instructions **[V]** | parameterize | The artifact *name* follows DP-2; take the image filename from a board-scoped variable so this shared action does not need a second copy. | +| `.github/actions/README.md:6` | Table row describes kernel-leg's staged artifact as `zImage_dtb + .config + modules tar` **[V]** | cosmetic | Reword when the action above is generalized. | +| `.github/workflows/build.yml:225,258,276,305` | Reads and uploads `output/images/zImage_dtb` and reports its size **[V]** | parameterize | Same: source the image filename from the board's config rather than a literal, when this step is next rewritten. | +| `.github/workflows/release.yml:184,232,285,357-367,453-454,552,665,796-832,856,869,898,921` | The whole release surface — asset names, `SHA256SUMS`, attestation globs, the variant round-trip check, and `make mister_de10nano_defconfig` at `:665` **[V]** | parameterize | ADR 0027 Decision 4 reserves a separate `de25-YYYYMMDD` tag namespace, so a DE25 release is a *second* set of assets, not a rename — keep this job DE10's and give the board axis its own asset naming when D2 gates it. | +| `.github/workflows/lint.yml:17,37-61,165-199,221` | 20+ `board/mister/de10nano/...` shellcheck targets and path filters **[V]** | parameterize | Glob `board/mister/*/...` (or enumerate boards) rather than adding a duplicated second block — this is the highest-value pure-path fix in the repo. | +| `.github/workflows/reproducibility.yml:90,156` | Hashes `linux.img zImage_dtb` and names them in the bisect hint **[V]** | parameterize | Take the artifact list from the board's config; the double-build comparison logic is board-agnostic. | +| `.github/workflows/renovate-hash-sync.yml:71,303,417` | Path filter + `git add` on `configs/mister_de10nano_defconfig` and `board/mister/de10nano/patches/linux/linux.hash` **[V]** | parameterize | Board-scope both paths, or the DE25 kernel bump will land with a stale hash and no CI signal. | +| `renovate.json:38,111,113` | `fileMatch` and its long rationale pin `configs/mister_de10nano_defconfig` (and `mister_kernel_defconfig`) by literal name **[V]** | parameterize | Add the DE25 defconfig to the same manager's file list — one PR touching every board is the intent, and a per-board manager would recreate exactly the half-the-repo bump this comment documents. | + +## 4. The ledger — configs, packages, top level + +| file:line | coupling | sev | when you touch this, do this instead | +|---|---|---|---| +| `package/azcopy/Config.in:64` | `depends on BR2_arm` gates the package to 32-bit ARM **[V]** | semantic-blocker | **Leave it exactly as is** — this decision is already taken, not pending: the DE25 package set drops azcopy entirely because Microsoft ships an official arm64 binary **[V** plan:139**]**. Do not add a `BR2_aarch64` alternative. | +| `package/azcopy/Config.in:22,51`, `azcopy.mk:19,143`, `azcopy-profile.sh:19,24`, `azcopy.hash:34` | Prose naming the defconfig / the overlay dir / the `make` line **[V]** | cosmetic | No action. The armv7-specific *content* is `Config.in:64` plus the two vendored patches (`0001` GOARCH=arm Timeval, `0002` ARM OABI→EABI keyctl), neither of which is in these files. | +| `configs/mister_kernel_defconfig:31-32` | `BR2_arm=y` / `BR2_cortex_a9=y`, held byte-identical to the main defconfig by the lockstep check named in this file's own header at `:13-20` **[V]** | semantic-blocker | Add a parallel DE25 kernel-variant defconfig with its own lockstep target; never loosen these two in place. | +| `configs/mister_kernel_defconfig:93` | `BR2_LINUX_KERNEL_ZIMAGE=y` **[V]** — coupling (a) at the Kconfig level | semantic-blocker | The aarch64 image format is DP-2's call (`Image`/FIT); it belongs in a DE25 kernel-variant config, not in a branch here. | +| `configs/mister_kernel_defconfig:95` | `BR2_LINUX_KERNEL_INTREE_DTS_NAME="intel/socfpga/socfpga_cyclone5_de10nano"` **[V]** | semantic-blocker | DP-9 says DE25 uses Agilex-native DTS idioms, so its config supplies its own DTS name entirely. | +| `configs/mister_kernel_defconfig:62,89,91,123` | `BR2_GLOBAL_PATCH_DIR` / `LINUX_KERNEL_PATCH` / `CUSTOM_CONFIG_FILE` / `ROOTFS_POST_IMAGE_SCRIPT` embed `board/mister/de10nano/` **[V]** | parameterize | Pure path substitution — a DE25 sibling repeats the pattern with its own board dir. | +| `configs/mister_de10nano_defconfig:105-106` | `BR2_arm=y` / `BR2_cortex_a9=y` **[V]** | cosmetic | Not one of the four: this file is inherently *board-owned*. DE25 gets `configs/mister_de25nano_defconfig` with its own arch stanza; these lines are never edited. | +| `configs/mister_de10nano_defconfig:151,153` | `BR2_LINUX_KERNEL_ZIMAGE=y`, Cyclone-V in-tree DTS name **[V]** | cosmetic | Same reasoning — board-owned file, DE25 sets its own values in its own copy. | +| `configs/mister_de10nano_defconfig:122-124,143,147,149,830` | Six board-dir paths (post-build, post-image, global patch dir, overlay, kernel patches, kernel config, busybox fragment) **[V]** | cosmetic | Do not make these generic *inside this file*; each board's defconfig points at its own `board/mister//` tree. | +| `configs/mister_de10nano_defconfig:1,83` | The filename in the header comment and one prose mention **[V]** | cosmetic | No action. | +| `configs/mister_initramfs_defconfig:28-29` | `BR2_arm=y` / `BR2_cortex_a9=y`; the file's own `:26-27` says arch here only has to match "the same silicon as the main build" **[V]** | semantic-blocker | A DE25 initramfs is a new sibling defconfig with the aarch64 arch + core stanza, not a widened symbol here. | +| `configs/mister_initramfs_defconfig:5,56,59,84` | Prose plus busybox-config / overlay / post-build-script paths under the board dir **[V]** | parameterize | Path substitution only — no arch semantics in any of the three. | +| `configs/mister_installer_defconfig:60-64` | `BR2_arm=y` / `BR2_cortex_a9=y` / NEON / VFP / FPU_NEON for the throwaway installer OS **[V]** | cosmetic | Board-owned file: DE25 gets its own installer defconfig if and when its installer flow is designed (which DP-2 and coupling (b) gate anyway). | +| `configs/mister_installer_defconfig:109,116` | Installer busybox config + installer overlay under the board dir **[V]** | cosmetic | Same — the DE25 copy repeats the pattern. | +| `configs/mister_rt.fragment:23,62,100,105,108` | Kernel-patch dir, RT fragment path, and the hash coupling all name `board/mister/de10nano/`; `:100` records a `zImage_dtb` build check **[V]** | parameterize | Board-scope the three paths when the RT variant is next touched; note DP-6 says RT on big.LITTLE must be re-evaluated, not assumed to port. | +| `Makefile:69` | `INITRAMFS_INIT := $(ROOT_DIR)/board/mister/de10nano/initramfs-overlay/init` **[V]** | parameterize | Take the board dir from a `BOARD` make variable defaulting to `de10nano` — see §6.2. | +| `Makefile:330` | `$(BR_MAKE) mister_de10nano_defconfig` in the `.config` recipe **[V]** | parameterize | Drive the defconfig name off the same `BOARD` variable so `make BOARD=de25nano` is the whole invocation surface — see §6.2. | +| `Makefile:393,436,450,541,628,654,701,886` | Eight more board-dir paths and error strings, incl. `:701` invoking `board/mister/de10nano/post-image.sh` **[V]** | parameterize | Same `BOARD` substitution; `:701` is the one that actually *runs* something, so it moves with `:69`/`:330` in the same diff. | +| `Makefile:80,139-140,294,421-451,484-578,684-720,784-788` | ~20 `zImage`/`ZIMAGE_DTB_*` references: the RT variant's artifact, the initramfs-inside-the-zImage invariant, the `zimage-dtb` target and its help text **[V]** | parameterize | The `zimage-dtb` target is coupling (a)'s make front-end — when a board axis lands, give DE25 its own image-assembly target rather than making this one arch-aware. | +| `Makefile:15,685-686,740` | Comments and `make help` naming `mister_de10nano_defconfig` and `post-image.sh` **[V]** | cosmetic | Update *together with* `:330` if that is parameterized, so the help text does not go stale; no independent action. | +| `external.mk:47-78` | The `CONFIG_INITRAMFS_SOURCE` fixup is arch-agnostic kconfig machinery; only `:57` and `:77` say "zImage" in prose **[V]** | cosmetic | **Reuse this block unchanged** for aarch64 — `Image` embeds an initramfs identically; reword two comments at most. Plan §3 item 1 already records this as carrying over **[V]**. | +| `external.mk:32,36` | Comments naming the defconfig and `linux.config` to explain a `pkg-kconfig.mk` constraint **[V]** | cosmetic | The constraint applies to any board's defconfig; reword to "the board's defconfig" or leave. | +| `Config.in:20` | Comment pointing at `board/mister/de10nano/linux.config` as the partner list for the WiFi driver menu **[V]** | cosmetic | No Kconfig symbol here is board-specific (packages are sourced via `$BR2_EXTERNAL_MISTER_PATH`); update the pointer only if a DE25 `linux.config` grows an equivalent list. | +| `package/dualsensectl/Config.in:26` | Comment cites patches `0033/0037/0042` in the board's `linux-patches/` **[V]** | cosmetic | These input/HID patches are classified arch-neutral and expected to port as-is (**[V** plan:135**]**); reword the path when the DE25 series exists. | +| `package/7zip/7zip.mk:178` | Comment names `{linux.img, zImage_dtb}` as what the two exFAT writers consume **[V]** | cosmetic | Reword to the DE25 payload name if 7-Zip is carried over; no build logic depends on it. | +| `install.sh:69-74,93-114,370` | Fetches `board/mister/de10nano/fat-payload/Scripts/*` over raw.githubusercontent and prints `/media/fat/linux/zImage_dtb` in its summary **[V]** | parameterize | Board-scope the three URL defaults; note the printed payload names follow DP-2 and Decision 4's separate `db-de25nano.json` namespace. | +| `CONTRIBUTING.md:42,45,101,138,163,203` | Prose naming the two kernel-patch dirs, the defconfig, and the licence of `linux-patches/*.patch` **[V]** | cosmetic | Add a third bullet for `board/mister/de25nano/linux-patches/` alongside these rather than abstracting the paths into board-generic prose. | +| `README.md` (18 lines), `PLAN.md` (23), `TASKS.md` (22), `MISTER-KERNEL-PATCH-RECON.md` (6) | Repo-root narrative docs describing the DE10 build, its artifacts, and the kernel-patch recon **[V** line lists in §7 workfile**]** | cosmetic | These describe *the DE10 product as built today* and stay accurate as-is; a DE25 gets its own sections or its own docs, per ADR 0027's staged framing. No edit is owed by D1. | + +--- + +## 5. Semantic blockers — the four, and what each is waiting on + +D1.2 has no separate deliverable file; §5.1–§5.7 below **are** D1.2. + +| # | Blocker | Design decision owed | Owner | +|---|---|---|---| +| (a) | zImage header / `zImage_dtb` concatenation | What container a DE25 kernel+DT payload ships in (FIT by default, or `Image`+dtb) | **DP-2** (plan:168), pinned against D0.1's boot flow ([`de25-boot-chain.md`](de25-boot-chain.md)) | +| (b) | 0xA2 BootROM SD layout | The Agilex GSRD-shaped partition layout and its own genimage config | **D2.2** on-hardware, informed by D0.1 §4–§5's QSPI/SD seam | +| (c) | `^BR2_arm`/`^BR2_cortex` asserts | How the board axis plugs into the shared build action and the lockstep check | **D1.2 = §5.1–§5.7 below**, implemented at **D2.1** | +| (d) | armv7 `package/azcopy` | *Already decided* — azcopy is dropped from the DE25 set (MS ships arm64 binaries) | closed; no work owed | + +Note that (a) and (b) are *not* blocked on this repo at all — they are blocked on hardware and +on DP-2. Only (c) has design work that can be done cold, which is why D1.2 exists and why it is +the only one of the four with a spec below. + +### 5.1 What exists today (verbatim) + +**`scripts/check-kernel-defconfig-sync.sh`** — paths **[V :61-62]** `configs/mister_de10nano_defconfig` +and `configs/mister_kernel_defconfig`; sentinel assert **[V :83]** +`for must in BR2_arm BR2_cortex_a9 BR2_KERNEL_HEADERS BR2_TOOLCHAIN_BUILDROOT_CXX` (`rc=1` if any +is absent, `:84-89`); family name-set assert **[V :129]** +`for family in BR2_arm BR2_ARM_ BR2_cortex BR2_KERNEL_HEADERS BR2_TOOLCHAIN_BUILDROOT_` (`rc=1` on +set inequality, `:130-139`). Exit taxonomy is documented in its own header **[V :54-55]**: +0 = lockstep, 1 = drift/sentinel-loss, 2 = usage/IO error (already used for a missing input at `:65`). + +**`.github/actions/buildroot-build/action.yml`** — fingerprint source **[V :151-155]** +(`fp_defconfig` selected by `VARIANT`, not by board); deny-list filter **[V :158,:181]** stripping +`^($|BR2_PACKAGE_|BR2_LINUX_KERNEL)`, which is board-invariant *policy* and stays global; +sentinel assert **[V :190-195]** `for must in '^BR2_arm' '^BR2_cortex'` → `::error::` + `exit 1`. +`:166`'s `board/mister/de10nano/patches/$d` is a *path* hardcode, handled in §3, not here. + +### 5.2 Table shape, location, and key + +A new sourced-only data file, `scripts/lib/board-expectations.sh`, following the house +convention of `scripts/ci-lib.sh` (sourced from `run:` blocks — **[V]** `build.yml:247,320`, +`kernel-leg/action.yml:102`) and `scripts/lib/hash-sync-common.sh`. **One file, not a copy per +consumer** — this guard exists because copied stanzas drift; the table must not repeat that +mistake against itself. + +```bash +# scripts/lib/board-expectations.sh — sourced, not executed. +# Per-board expected-symbol tables for the arch/toolchain guards in +# scripts/check-kernel-defconfig-sync.sh and +# .github/actions/buildroot-build/action.yml. Adding a board means adding a +# row here; there is deliberately no fallback row and no wildcard match. + +# Arch-independent sentinels/families every board shares (KERNEL_HEADERS is a +# choice under package/linux-headers, TOOLCHAIN_BUILDROOT_CXX is under +# package/gcc — neither varies by board, so neither is duplicated per row). +BOARD_COMMON_SENTINELS="BR2_KERNEL_HEADERS BR2_TOOLCHAIN_BUILDROOT_CXX" +BOARD_COMMON_FAMILIES="BR2_KERNEL_HEADERS BR2_TOOLCHAIN_BUILDROOT_" + +declare -A BOARD_ARCH_SENTINELS=( + [de10nano]="BR2_arm BR2_cortex_a9" + [de25nano]="BR2_aarch64 BR2_cortex_a76_a55" # [U] — CPU choice is D2.1's call +) +declare -A BOARD_ARCH_FAMILIES=( + [de10nano]="BR2_arm BR2_ARM_ BR2_cortex" + [de25nano]="BR2_aarch64 BR2_ARM_ BR2_cortex" # [U] — BR2_ARM_ likely empty-set + # on both sides; harmless if so +) +declare -A BOARD_FINGERPRINT_SENTINELS=( + [de10nano]="^BR2_arm ^BR2_cortex" + [de25nano]="^BR2_aarch64 ^BR2_cortex" # [U] +) +``` + +**Why an explicit BOARD string key, not something derived:** + +- *Not the defconfig filename stem* — that conflates the **board** axis with the **variant** + axis the action already has (`main` vs a kernel-only flavor, **[V :103-129]**), which are + orthogonal. `mister_kernel_defconfig` carries no board name at all today; that is a + single-board assumption in a *filename*, a §3/§4 path fact, not a table fact. +- *Not derived by reading `BR2_arm` vs `BR2_aarch64` back out and picking a generic 32/64-bit + row* — that is precisely the failure the accept criterion forbids: a third board sharing an + architecture would silently inherit an existing row, and "forgot to add a board" would be + indistinguishable from "this board intentionally matches." Each row must be an **affirmative, + reviewed claim**, not an inference. +- An explicit key makes a **missing entry a hard error**: under `set -u`, an unset associative + key is already an unbound-variable error, and the design additionally requires an explicit + `[ -z "${BOARD_ARCH_SENTINELS[$BOARD]+set}" ]` check so the message *names the missing board* + instead of surfacing a bare bash trace. Both consumers use the same idiom. + +### 5.3 The DE10 row + +| | Sentinels | Families | +|---|---|---| +| common (all boards) | `BR2_KERNEL_HEADERS`, `BR2_TOOLCHAIN_BUILDROOT_CXX` | `BR2_KERNEL_HEADERS`, `BR2_TOOLCHAIN_BUILDROOT_` | +| `de10nano` (arch) | `BR2_arm`, `BR2_cortex_a9` | `BR2_arm`, `BR2_ARM_`, `BR2_cortex` | + +Merged (arch row first, then common), this reproduces **[V]** the exact four sentinels at `:83` +and the exact five families at `:129`, in that order — byte-identity is what makes §5.6's +migration check meaningful. Fingerprint row `^BR2_arm ^BR2_cortex` reproduces **[V :190]**. + +### 5.4 The DE25 row, honestly + +| | Sentinels | Families | +|---|---|---| +| `de25nano` (arch) | `BR2_aarch64` **[V]**, `BR2_cortex_a76_a55` **[U]** | `BR2_aarch64`, `BR2_ARM_` **[U]**, `BR2_cortex` | + +- `BR2_aarch64` **[V]** — a live Buildroot arch choice at `work/buildroot/arch/Config.in:54`, + sibling of `BR2_arm` under the same choice (`:411`). Upstream's + `configs/qemu_aarch64_ebbr_defconfig:1` sets only `BR2_aarch64=y` and no `BR2_ARM_*` **[V]**, + consistent with that family bucket being legitimately empty on both sides — which the + set-equality check accepts (empty == empty). +- `BR2_cortex_a76_a55` **[U]** — the symbol exists (`work/buildroot/arch/Config.in.arm:474`, + "cortex-A76/A55 big.LITTLE") and matches the HPS's 2×A76 + 2×A55 per ADR 0027, but whether + `configs/mister_de25nano_defconfig` actually selects it (versus a generic `cortex-a53` + fallback) is **D2.1's decision**. Treat this cell as a placeholder to confirm or correct when + D2.1 lands the real defconfig. +- `BR2_cortex` as a *family prefix* carries over **[V]** — both 32- and 64-bit cores live in the + same `BR2_cortex_*` namespace under `arch/Config.in.arm` (`:204` A9, `:474` A76/A55); there is + no separate `Config.in.aarch64` file **[V]**. + +### 5.5 The toolchain-fingerprint equivalent + +Only `:190-195` becomes per-board: replaced by a lookup into +`BOARD_FINGERPRINT_SENTINELS[$BOARD]`, with a new `board` input defaulting to `"de10nano"`, +validated exactly the way `variant` already is at **[V :112-123]** — an unrecognized board hits +the same `::error::` + `exit 1` idiom that file already uses for an unrecognized variant. +The deny-list filter (`:158`/`:181`) and the toolchain-patch hash (`:165-170`) stay **global, +unchanged policy**; `:166`'s board path is a §3 path fix, deliberately out of scope here. + +### 5.6 Migration — zero behaviour change for DE10, and how a reviewer proves it + +Both consumers take a `BOARD` that **defaults to `de10nano`**. No caller passes one today (the +three call sites in the script's own header **[V :46-52]**; the action's implicit default), so +every existing invocation resolves to the merged common+de10nano row, defined to be textually +identical, in the same order, to today's literal `for … in …` lists. No filename selection +logic changes. + +1. `scripts/check-kernel-defconfig-sync.sh` with no args, before vs. after — stdout must be + byte-identical, specifically the `OK — $shared shared BR2_ symbol(s) agree, …` line + **[V :155]** with the same `$shared` count. +2. `BOARD=de10nano scripts/check-kernel-defconfig-sync.sh` vs. the no-arg run — must **also** be + byte-identical, proving the default and the explicit path are one code path, not two that can + quietly diverge. +3. A deliberately broken kernel defconfig (drop `BR2_cortex_a9=y`) must still fail identically, + with the same sentinel-missing text **[V :85-87]** modulo table-driven wording, so the + message keeps pointing readers at that file's LOCKSTEP header. +4. For the action: diff the `Toolchain fingerprint (N lines):` block **[V :196-197]** between a + pre- and post-change run of the same ref on `variant: main` — line count, content, order must + match exactly. Nothing about `fp_defconfig` or the deny-list moves, so only the sentinel + *implementation* can differ, never its inputs or its log. +5. **New test:** an unknown `board:` (e.g. `bogus`) must fail at the same early point the unknown- + variant check fails **[V :107-121]**, with an `::error::` naming the unknown board — this is + the fail-closed property, exercised directly. + +### 5.7 Fail-closed statement, and the risks to it + +Both guards keep exactly their current DE10 behaviour — identical sentinel sets, identical +family sets, identical exit codes 0/1/2, identical `::error::`/`exit 1` idiom — under the +default. The only new failure surface is a *third* outcome, an unrecognized board, designed to +fail loudly (script: exit 2, its existing usage/IO class **[V :55]**; action: `::error::` + +`exit 1`, matching the unknown-variant precedent **[V :119-122]**). **No path in this design +turns either assert into a warning, and no path lets an unrecognized board proceed with an empty +or inherited expectation set.** + +Named risks, so a reviewer can check them rather than take the claim: + +1. The DE25 CPU sentinel is **[U]** — the symbol matches the SoC, the defconfig's actual choice + is D2.1's. +2. Whether an aarch64 defconfig ever populates `BR2_ARM_*` is unverified beyond one upstream + example. If the guess is wrong the bucket is simply always-empty on both sides, which still + *passes correctly* — cosmetic, not fail-open. +3. This design does not resolve the **file-path axis** (what the DE25 defconfigs are named, how + `fp_defconfig`/`VARIANT` compose with a board) — deliberately left to §3/§4 and D2.1. An + implementer who wires `BOARD` through without adding `configs/mister_de25nano_defconfig` + fails at the file-existence check **[V :65]** rather than at the table: still fail-closed, + just at a different line than intended. +4. **This is a design, not a merged diff.** The "no behaviour change" claim rests on the default + semantics being implemented *exactly* as specified. A sloppy implementation — `BOARD` + defaulting to the empty string, or an unknown key falling through to the last array entry — + would silently destroy the property this design exists to guarantee. §5.6's checks 1, 2 and 5 + are the ones that catch that, and a reviewer must actually run them. + +--- + +## 6. Proposed follow-up diffs — **proposals only, NOT made by this task** + +Zero-risk `BOARD=` introductions, offered for **separate review**. Nothing below has been +applied; no script, config, workflow, or package file was modified by D1.1. Each is +behaviour-neutral while `BOARD=de10nano` remains the only value, and each should be reviewed and +landed on its own merit, not as a batch. + +### 6.1 `scripts/check-kernel-defconfig-sync.sh:61-62` + +```sh +BOARD="${BOARD:-de10nano}" +MAIN_DEFCONFIG="$ROOT/configs/mister_${BOARD}_defconfig" +KERNEL_DEFCONFIG="$ROOT/configs/mister_kernel_defconfig" # see §5.2 on the variant axis +``` + +### 6.2 `Makefile:69` and `Makefile:330` + +```make +BOARD ?= de10nano +INITRAMFS_INIT := $(ROOT_DIR)/board/mister/$(BOARD)/initramfs-overlay/init +``` + +```make +$(OUTPUT_DIR)/.config: | $(BR_STAMP) + @mkdir -p $(OUTPUT_DIR) + $(BR_MAKE) mister_$(BOARD)_defconfig +``` + +If `:330` lands, `Makefile:15` and `Makefile:740`'s help text move in the **same** commit, or the +documented invocation goes stale. + +### 6.3 `configs/mister_kernel_defconfig:62,89,91,123` and `configs/mister_initramfs_defconfig:56,59,84` + +Same substitution pattern applied to the board-dir path prefix only. **Lower confidence:** +Buildroot defconfigs are not make files and do not expand a `BOARD` variable — these two rows +most likely resolve as *sibling files per board* (as §4 says for `mister_de10nano_defconfig` +itself) rather than as a substitution. Do not land 6.3 without settling that first. + +### 6.4 `scripts/test-initramfs.sh:83`, `scripts/check-linux-img.sh:41`, `scripts/hash-sync-kernel.sh:196`, `scripts/lint-kernel-patches.sh:67` + +All four read a pinned value out of `configs/mister_de10nano_defconfig` by literal name. Each +takes `BOARD="${BOARD:-de10nano}"` and composes the path — four independent one-line diffs, no +shared state, reviewable in isolation. + +### 6.5 `.github/workflows/lint.yml:37-61,165-199` + +Replace ~20 literal `board/mister/de10nano/...` entries with `board/mister/*/...` globs. The +highest-value pure-path fix in the repo, and the one most likely to silently *stop linting* a +DE25 tree if it is skipped. + +--- + +## 7. Coverage reconciliation + +Command run from the repo root, 2026-08-21: + +```sh +git grep -nE 'de10nano|BR2_arm|zImage' \ + -- ':(exclude)board/mister/de10nano/' ':(exclude)docs/' +``` + +`git grep` is the canonical form because it searches **tracked files only**, which is what the +accept criterion means and what makes the counts below reproducible on any machine. The raw-grep +equivalent needs every ignored tree excluded by hand and still varies by grep implementation: + +```sh +grep -rEn 'de10nano|BR2_arm|zImage' \ + --exclude-dir=.git --exclude-dir=output --exclude-dir=output-initramfs \ + --exclude-dir=output-rt --exclude-dir=dl --exclude-dir=buildroot \ + --exclude-dir=work --exclude-dir=.claude . \ + | grep -vE '^(\./)?(board/mister/de10nano/|docs/)' +``` + +| | count | +|---|---| +| Matching lines | 457 | +| Distinct files hit | **54** | +| Files with a ledger row | **54** (100%) | +| Files deliberately omitted | **0** | +| §2 scripts | 25 files → 26 rows | +| §3 CI | 9 files (8 under `.github/` + `renovate.json`) → 11 rows | +| §4 configs / packages / top-level | 20 files → 28 rows | +| **Total rows** | **65** | +| Severity: semantic-blocker | 14 rows | +| Severity: parameterize | 26 rows | +| Severity: cosmetic | 25 rows | + +Rows exceed files because several files carry rows at more than one severity +(`configs/mister_kernel_defconfig` and `Makefile` carry four each, +`.github/actions/buildroot-build/action.yml` three); the four repo-root narrative docs share +one row. 14 + 26 + 25 = 65 = 26 + 11 + 28, so every row is severity-tagged and no file is +counted twice. + +**Grep-hygiene notes, so a future re-run reconciles:** + +- **Use `git grep`.** The raw-grep form's result depends on which grep is installed. `ugrep` + (and `rg`) honour `.gitignore` by default and therefore skip `work/` and `.claude/`; GNU grep + does not, and without `--exclude-dir=work --exclude-dir=.claude` it returns **15 796** lines + instead of 457 — `work/U-Boot_MiSTer` alone carries 102 files matching `zImage`, and + `.claude/worktrees` holds whole repo copies. A reconciliation that silently depends on the + operator's grep is not a reconciliation; verified both ways 2026-08-21. +- The `^(\./)?` alternation matters in the raw-grep form — `ugrep` emits paths *without* a + leading `./`, so the exclusion filter published in earlier drafts (`^\./(…)`) silently matched + nothing and pulled ~200 `docs/` and board-dir lines back in. +- `board/mister/de10nano/` and `docs/` are excluded **by the accept criterion**, not by + convenience: the board dir is by definition board-owned (that is the axis ADR 0027 Decision 1 + creates), and `docs/` describes the DE10 as built rather than constraining a DE25 port. + `README.md`, `PLAN.md`, `TASKS.md` and `MISTER-KERNEL-PATCH-RECON.md` are the same class of + content that happens to sit at the repo root; they get one shared cosmetic row in §4 rather + than a row per line. + +**Three things the "four couplings" framing did not anticipate**, surfaced by the full sweep: + +1. **`.github/workflows/lint.yml` is the single largest path hardcode in the repo** (~20 literal + board paths, `:37-61` and `:165-199`) and it is *silent* when wrong: a DE25 tree simply would + not be linted, with a green check. ADR 0027 §Context's survey did not name it. +2. **`renovate.json` + `renovate-hash-sync.yml` form a fourth path axis** — the dependency-bump + machinery pins the defconfig *and* the kernel hash file by literal name, and + `renovate.json:111` documents that a manager covering only some of the files "bumps half the + repo and leaves the copy behind" **[V]**. A DE25 defconfig added without touching these lands + with a stale kernel pin and no CI signal — the same class of bug that comment was written + about, one axis over. +3. **`.github/workflows/release.yml` is far more coupled than the survey's "4 CI files" implied** + (13 hit lines spanning asset names, `SHA256SUMS`, attestation globs, and a variant round-trip + check). This is not a blocker — ADR 0027 Decision 4 already reserves a separate + `de25-YYYYMMDD` tag namespace, so DE25 releases are a *second* asset set — but the cost is + real and belongs in D2's estimate, not discovered during it. + +Nothing in the sweep contradicted the four couplings, and no *fifth* semantic coupling was +found: every one of the 51 non-(a)-(d) files is a path, a name, or prose. diff --git a/docs/downloader-contract.md b/docs/downloader-contract.md index 1bb4c40..ead6f15 100644 --- a/docs/downloader-contract.md +++ b/docs/downloader-contract.md @@ -1182,3 +1182,35 @@ db wins the race.) db.json must equal, case-insensitively, the section name in the example `downloader.ini`/drop-in file shown in P4.8's docs, or onboarding fails at `DbEntityValidationException` (§1, §9.5) on the user's very first run. + +--- + +## 13. DE25-Nano: a reserved channel namespace, not a second arbitration point + +[ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md) reserves — does not +implement — a second, parallel channel for a possible future DE25-Nano board: release +tags `de25-YYYYMMDD` (DE10 keeps bare `YYYYMMDD`), a second Pages document +`db-de25nano.json` with its own db_id `mister_linux_modernization_de25nano`, and a +per-board updater script. See [`docs/de25-nano-plan.md`](de25-nano-plan.md) for the +staged plan these names belong to. + +Nothing in this document changes today. The load-bearing point for anyone editing this +contract is what the reservation rules out, not what it adds: + +* **No arbitration point exists, or is planned, where DE10 and DE25 artifacts could meet + on one device.** §9's "first wins" hazard is a same-board, multi-db race; it has no + cross-board analogue to design against, because the two boards' updaters, private + inis, and db.json documents are entirely separate from the start. +* **`/MiSTer.version` stays exactly six bytes on both boards** (§3, §12.2) — the + Downloader's comparison (`current_linux_version == linux['version'][-6:]`) is + per-device and per-file; a DE25 build does not widen, reinterpret, or share that + contract, it just runs the identical one against its own six bytes. +* **Board identity is never read from that file.** It comes from the device tree. Do not + design any future DE25 assertion (ADR 0027 Decision 4's "board-identity assertion + before any flash step") around `/MiSTer.version` — it was never a board discriminator + and this reservation doesn't make it one. +* **The stock `updateboot` raw-`dd`s the whole disk** (§8) with no board check of its + own. Crossing a DE10 archive onto a DE25 device, or vice versa, through this mechanism + is board-fatal, not merely a failed update — this is exactly why Decision 4 requires a + board-identity assertion to exist *before* any future DE25 flash step reuses this code + path. From f521927cb2f6cac8152130561fb3bcc9b4d61b08 Mon Sep 17 00:00:00 2001 From: "Michael C. Ferguson" Date: Sat, 22 Aug 2026 23:50:45 -0500 Subject: [PATCH 3/4] docs(de25): reference-board analysis, settled implementation path, task-list status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two documents the task list never anticipated, plus the status pass over it. A third-party DE25-Nano that boots Linux to the MiSTer MENU was made available as reference (kernel and U-Boot repos, read-only, nothing adopted, no code copied). de25-reference-implementation.md analyses it and root-causes the two symptoms reported second-hand. Neither is a property of Agilex 5: "only N cores in firmware" was literal — cores staged into QSPI as RSU images, whose hardcoded slot map consumes the whole 16 MiB device — and "very slow" was a staging path pushing bitstreams through the FCS mailbox in 4096-byte writes with a 0.05 s settle each, ~817 round-trips and >=41 s of sleep() for a 3.35 MB core, then a reboot. The fast path he intended is the one he never tested end to end. That document is partly salvaged. A spend limit killed 5 of 9 agents mid-run; four research legs had completed, so their output was recovered from the run journal rather than discarded. It is marked unrefuted throughout because the adversarial pass never ran, and one of its claims was already found false and is annotated in place as a worked example of why that marking matters. de25-implementation-path.md is the settled path. Binding the mainline FPGA manager needs no carried patch — a two-string DT fallback compatible binds the stock driver, since both match tables are data-free — but binding is not programming, and mainline svc lacks behaviour Terasic's vendor tree keys off the agilex5 compatible. The one mainline reconfiguration attempted on real silicon wedged a board, so §2.6 specifies the four-step hardware test that settles it. Confidence there is deliberately low. Kernel pinned to 7.2: 6.18 has no clk-agilex5.c, so clkmgr never probes and every consumer including mmc0 defers forever. The mmc0 DMA fault is a separate problem the version pin does not fix — it is a vendor-vs-mainline driver delta, not a regression, and needs a carried sdhci-cadence patch coupled to its DTS string. §1.1 exists to stop those two being read as one fix. de25-nano-tasks.md records D0/D1 completion and corrects a gate drawn at the wrong level: D2 was read as uniformly hardware-gated, but D2.1's own acceptance criterion is a local green build, and D2.3/D2.4 are largely authorable now. The closing section also records why a verify stage must not run concurrently with the stage producing what it verifies — D0.1 was launched that way, and 17 of its 22 brick-risk claims turned out to be generated by the research it ran alongside. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FYjrnzEEu4HzJSPWNAaRvm --- docs/de25-implementation-path.md | 829 +++++++++++++++++++++++++ docs/de25-nano-tasks.md | 130 +++- docs/de25-reference-implementation.md | 855 ++++++++++++++++++++++++++ 3 files changed, 1800 insertions(+), 14 deletions(-) create mode 100644 docs/de25-implementation-path.md create mode 100644 docs/de25-reference-implementation.md diff --git a/docs/de25-implementation-path.md b/docs/de25-implementation-path.md new file mode 100644 index 0000000..add5bc5 --- /dev/null +++ b/docs/de25-implementation-path.md @@ -0,0 +1,829 @@ +# DE25-Nano implementation path — the mainline-first route to a bootable, reconfigurable board + +**Status:** desk analysis, **2026-08-22**, **no hardware touched**. This document synthesises four +investigation legs (open-source DTS survey, the no-carried-patch question, kernel-version choice, +U-Boot build shape), then re-verifies every load-bearing claim against source, and then applies an +adversarial verification pass over both the legs and the synthesis. Where the legs disagreed, §9 +records the disagreement and how it was settled; §10 records exactly what was checked, what changed +under challenge, and what nobody has verified. Claims are tagged **[V]** (read from a named source) +/ **[U]** (unverified, with the missing input named). + +Cross-refs: [`de25-boot-chain.md`](de25-boot-chain.md) §5, §7, §8.3, §8.5; +[`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) §3.1, §4.1, §4.2; +[`de25-patch-portability.md`](de25-patch-portability.md); +[`de25-readiness-ledger.md`](de25-readiness-ledger.md) §5.4; +[`de25-reference-implementation.md`](de25-reference-implementation.md) (salvaged; its §7 verification +record now records which of its claims survived this pass); [ADR 0027](decisions/0027-de25-nano-multi-board-readiness.md). + +--- + +## 0. The two findings that change the plan + +> **Finding 1 — a 6.18 base costs a whole SoC clock driver.** +> `drivers/clk/socfpga/clk-agilex5.c` **does not exist in 6.18** and lands in **v6.19**, where the +> file is exactly **561 lines** **[V, fetched from `torvalds/linux@v6.19` this pass; `wc -l` = 561; +> matches `intel,agilex5-clkmgr` at `:544`]**. Mainline 6.18.44 ships the `intel,agilex5-clkmgr` +> *binding*, the *clock-ID header*, and the *DT node* — but no driver **[V]**. On a 6.18 base every +> peripheral whose `clocks =` phandle points at `&clkmgr` — `mmc0` and all three `gmac`s included — +> defers forever. A 6.18-based DE25 therefore requires a carried clock-driver backport before it can +> boot from SD at all. No leg caught this; it inverts the kernel recommendation. See §5. + +> **Finding 2 — "no carried patch" is true about *binding*, and unproven about *programming*.** +> A DT fallback compatible does bind mainline's stock `stratix10-svc` and `stratix10-soc` drivers on +> Agilex 5, with zero driver patches — that is settled from the OF core and both match tables **[V]**. +> But **binding is not programming.** Terasic's vendor 6.12.11 `stratix10-svc.c` keys *real, +> Agilex-5-specific behaviour* off the `intel,agilex5-svc` string that mainline has never had: it +> attaches the svc device to the SMMU, builds an IOVA carveout, adds +> `AGILEX5_SDM_DMA_ADDR_OFFSET 0x80000000` to every buffer address handed to the SDM, and issues +> `INTEL_SIP_SMC_SDM_REMAPPER_CONFIG` to *bypass the SDM remapper* — and it hard-fails `-ENODEV` if +> the SMMU is absent **[V, fetched and read this pass: `terasic/linux-socfpga@de25-nano-6.12.11-lts: +> drivers/firmware/stratix10-svc.c:59,63-64,3244,3443,3504-3556`]**. Mainline `stratix10-svc.c` +> contains none of it — it hands the SDM raw `gen_pool` physical addresses and knows nothing about +> an IOMMU or a remapper, at 6.18.44 **and** at v7.2 (`grep` for `iommu|REMAPPER|dma_addr_offset` → +> **zero hits** in both) **[V]**. The one time anyone drove a mainline-path reconfiguration on real +> Agilex 5 silicon, it reached `fpga_manager fpga0: writing … to Stratix10 SOC FPGA Manager` and then +> **timed out waiting for `RECONFIG_REQUEST` and wedged the board** +> **[V `de25-reference-implementation.md:115`]**. That is exactly the failure shape the vendor code +> predicts. Confidence in end-to-end reconfiguration via stock mainline drivers is therefore **low**, +> not medium, and §2.6 names the on-hardware test that settles it. + +Neither finding changes a decision. Finding 1 changes the kernel pin. Finding 2 changes what we +claim, what confidence we attach, and what the first hardware session must measure. + +--- + +## Sources + +All retrieved or read **2026-08-22** unless stated. + +- Local kernel tree `output/build/linux-6.18.44` — cited as `linux:path:line`. +- Mainline Linux at tags `v6.18`, `v6.19`, `v7.0`, `v7.1`, `v7.2` and at `master` (post-7.2, → 7.3), + via `raw.githubusercontent.com/torvalds/linux//…` — cited as `mainline@:path`. +- Read-only reference fork `/mnt/source/de25-linux` (Linux 6.18.38, the friend's working board) — + cited as `de25-linux:path`. **Read only; never executed, modified, or checked out.** +- `github.com/altera-fpga/linux-socfpga` branch `socfpga-6.18.20-lts` (the org formerly + `altera-opensource`), and `github.com/terasic/linux-socfpga` branch `de25-nano-6.12.11-lts`. +- Mainline U-Boot at tag `v2026.07`; mainline TF-A at tag `v2.15.0`. +- LKML, both retrieved and read this pass: + - Khairul Anuar Romli, **“[PATCH v6 1/2] dt-bindings: fpga: stratix10: add support for Agilex5”**, + `lkml.iu.edu/2511.2/10025.html`, dated **2025-11-18** **[V]**. + - Khairul Anuar Romli, **“[PATCH v2 2/2] arm64: dts: agilex5: add fpga-region and fpga-mgr nodes”**, + `lkml.iu.edu/2511.1/07883.html`, dated **2025-11-12** **[V]**. Note the version labels: the + archive copy of the DTS companion we can reach is the **v2** revision, not v6. An earlier draft + of this document called both “v6”; corrected. The v6 respin of 2/2 is presumably on lore, which + was bot-walled this pass — its *content* is **[U]**, its *effect* is verified against tree content + instead (§2.5). + +--- + +## 1. Decisions taken + +These are settled. The rest of this document works inside them and does not re-open them. + +| # | Decision | What it forecloses | +|---|---|---| +| 1 | **Core loading uses `fpga_manager` + DT overlay.** | U-Boot-side core loading, RSU slots, QSPI-resident cores — all rejected. | +| 2 | **Scope is "make the hardware available". Main_MiSTer does the loading.** | We ship kernel + DTS plumbing so an fpga-manager and an fpga-region probe and are reachable. We do **not** build a core loader, switching worker, or staging script. | +| 3 | **Two partitions: p1 FAT, p2 everything else.** | Forced by the factory SPL (`CONFIG_SPL_FS_FAT=y`, `SYS_MMCSD_FS_BOOT_PARTITION=1`). Part of the DE25 Buildroot output. | +| 4 | **No shared SD card between DE10 and DE25.** | Explicit non-goal. No cross-board layout compromises. | +| 5 | **Mainline-first, strongly.** | A carried patch requires justification that **no mainline route existed** — not that it was easier. | +| 6 | **Kernel version open in our favour**, up to and including 7.2. | The DE25 is not pinned to the DE10's 6.18. | +| 7 | **Kernel pinned to mainline 7.2 for the DE25** (2026-08-22). | Supersedes decision 6's openness. 6.18 is out; see §5.1. | +| 8 | **We carry the `sdhci-cadence` 40-bit DMA-mask patch** (2026-08-22), as an *upstreamable* carry to be submitted, not a permanent fork. | Accepted as a carried patch under decision 5 because no mainline route exists — see the coupling note below. | +| 9 | **Vendor patches are a last resort** (2026-08-22). A Terasic `stratix10-svc.c` carry is permitted **only if** on-hardware testing proves mainline `svc` cannot program the fabric (§2.6), and only for capability critical to running MiSTer. The same bar applies to any vendor-kernel behaviour: it must buy a real win on something critical, not merely be what the vendor happens to do. | Vendor-tree divergence adopted wholesale; "the vendor does it this way" as a justification. | + +Decision 2 carries an honest tension that this document does not paper over: *"hardware available" +still needs some userspace entry point, and mainline provides none.* §7 answers it. + +### 1.1 Decisions 7 and 8 fix two *independent* problems — do not conflate them + +They are easy to read as one fix and they are not. Dropping either one leaves a board that does +not boot from SD. + +| Problem | What fixes it | What does **not** fix it | +|---|---|---| +| `clkmgr` never probes, so every `&clkmgr` consumer (`mmc0`, gmac) defers forever | **The 7.2 pin.** `clk-agilex5.c` exists in 7.2 and matches `"intel,agilex5-clkmgr"` with real `.data` (`drivers/clk/socfpga/clk-agilex5.c:544`, verified in the local 7.2 tree 2026-08-22) **[V]**. 6.18.44 has no such file; its `clk-agilex.c:546` matches only `"intel,agilex-clkmgr"` while the DTS declares `"intel,agilex5-clkmgr"` **[V]** | The sdhci patch. Entirely unrelated code path. | +| `mmc0` DMA faults through the SMMU (mainline takes the 64-bit branch; Terasic's vendor tree caps the controller at 40 bits) | **The carried `sdhci-cadence` patch.** | The 7.2 pin. This is a vendor-vs-mainline driver delta, **not** a 6.18 regression — it travels forward to 6.19 and 7.2 unchanged. Verified: `sdhci-cadence.c` in the local 7.2 tree has no DMA-mask quirk of any kind **[V]** | + +**Coupling note for whoever implements decision 8.** `sdhci-cadence.c:656` matches +`{ .compatible = "cdns,sd4hc" }` with **no `.data`** (verified in 7.2) — so a 40-bit mask cannot be +hung off the existing entry. The patch must add a *new* match entry carrying data (the file already +uses `.quirks2` on its per-variant data structs, e.g. `:485`, `:499`), which means **the DTS must +declare that new compatible string first in `mmc0`'s list**. Patch and DTS are coupled; neither +works alone. The 40-bit value itself is a **hypothesis inferred from the vendor quirk and is +untested on hardware** — it is the second item on §2.6's bring-up test list, not an established fix. + +--- + +## 2. The mainline-first question — can we do this with DTS only? + +**Headline, stated at the precision the evidence supports:** + +- **Binding: yes, DTS-only, zero driver patches, on a 6.19-or-newer base [V].** A two-string + fallback compatible binds the stock `stratix10-svc`, `stratix10-soc` and `sdhci-cadence` drivers. + The friend's driver commit `d1878a320` was avoidable and must not be copied. +- **Programming: not established, and one observed on-hardware failure argues against it [V].** + See §2.6. This is the single largest risk in the DE25 plan and it is a hardware question. +- **The only carried patch a 6.18 base would force is not the fpga-mgr patch at all — it is the + clock driver [V].** Moving to ≥6.19 removes it (§5). + +### 2.1 Why a fallback compatible is sufficient to bind — mechanism, read from source + +The drivers we need are pure match-on-string drivers with **no per-compatible data and no branch on +which string matched**: + +| Driver | Match table | `.data`? | Branches on matched string? | +|---|---|---|---| +| `stratix10-svc` | `intel,stratix10-svc`, `intel,agilex-svc` | none | no | +| `stratix10-soc` (fpga-mgr) | `intel,stratix10-soc-fpga-mgr`, `intel,agilex-soc-fpga-mgr` | none | no | +| `sdhci-cadence` | three vendor entries with `.data`, then a **bare** `cdns,sd4hc` | none on the bare entry | no | + +**[V** at 6.18.44: `linux:drivers/firmware/stratix10-svc.c:1133-1137` (file is 1334 lines), +`linux:drivers/fpga/stratix10-soc.c:448-452`, `linux:drivers/mmc/host/sdhci-cadence.c:643-658`; +`grep` of both stratix10 files for `of_device_get_match_data` / `of_device_is_compatible` / +`match->data` → **no hits**. **[V** at v7.2, both files fetched in full this pass: tables +byte-identical in content, at `mainline@v7.2:drivers/firmware/stratix10-svc.c:1911-1915` (that file +has grown to 2113 lines, mostly FCS command plumbing) and +`mainline@v7.2:drivers/fpga/stratix10-soc.c:448-452`; `grep -c agilex5` → **0** in both.**]** + +OF matching walks the node's **entire** `compatible` list. `__of_device_is_compatible()` iterates +every string, and a hit at index *i* returns `score = INT_MAX/2 - (i << 2)` +**[V `linux:drivers/of/base.c:338-356`]**; `__of_match_node()` accepts any `score > 0`, keeping the +highest **[V `linux:drivers/of/base.c:1073-1091`]**. Position therefore affects only *which table +entry wins among several* — never match-versus-no-match. Because neither driver keys behaviour on +the matched entry, **a fallback match is functionally identical to an exact match**. So: + +```dts +compatible = "intel,agilex5-soc-fpga-mgr", "intel,agilex-soc-fpga-mgr"; +``` + +binds the **stock, unmodified** `stratix10-soc` driver. + +`sdhci-cadence` is the same shape with one extra safeguard worth naming: `sdhci_cdns_probe()` calls +`of_device_get_match_data()` and, on `NULL`, explicitly falls back to `&sdhci_cdns_drv_data` +**[V `linux:drivers/mmc/host/sdhci-cadence.c:561-563`]** — so the bare `cdns,sd4hc` entry's absent +`.data` is a supported, exercised path, not an accident. + +**Three hard structural constraints come with this shape.** All three are node *names* or plain +properties, not compatibles, so they are easy to lose when authoring by hand: + +1. `s10_init()` does `of_find_node_by_name(NULL, "svc")` and then + `of_platform_populate(fw_np, s10_of_match, …)` **[V `linux:drivers/fpga/stratix10-soc.c:471-484`]**. + **The fpga-mgr's parent node must literally be named `svc`.** The child's node name is free. +2. `stratix10_svc_init()` does `of_find_node_by_name(NULL, "firmware")` + **[V `linux:drivers/firmware/stratix10-svc.c:1307`]**. **The grandparent must literally be named + `firmware`.** +3. `get_invoke_func()` fails probe with `-ENXIO` unless the svc node carries + `method = "smc"` or `"hvc"` **[V `linux:drivers/firmware/stratix10-svc.c:865-885`]**. + +Mainline's own Agilex 5 shape satisfies all three (`firmware { svc { method = "smc"; … } }`, +`mainline@v7.2:arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi`) **[V]**, as does the friend's +(`de25-linux:…/socfpga_agilex5.dtsi:213-218`) **[V]**. On a 6.18 base, where we would author the +whole subtree, they must be met deliberately. + +### 2.2 The independent empirical check — what it proves, and what it does not + +The friend's board boots from SD on real DE25 silicon. His `drivers/mmc/host/sdhci-cadence.c` is +**byte-identical to stock 6.18.44** (`diff -q` → identical, re-run this pass) **[V]**, and his +`mmc0` declares `compatible = "intel,agilex5-sd4hc", "cdns,sd4hc"` +**[V `de25-linux:arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi:387`]**. That is the fallback idiom, +matching on the bare `cdns,sd4hc` entry, working on **real Agilex 5 hardware, with an unpatched +mainline driver**. + +**The necessary qualifier, added under challenge:** that boot runs with `sdhci.debug_quirks=0x60` +baked into the production `boot.scr.uimg` bootargs, which forces SDHCI into **PIO** instead of ADMA +**[V `de25-reference-implementation.md` §3, commit `716559020`]**. So the empirical check proves +**binding, probe and PIO operation through a fallback compatible on real silicon**. It does **not** +prove the DMA path. §8 Q2 is where that lives, and §5.1 no longer treats it as a settled non-issue. + +Note in passing that his board `.dts` sets nine `cdns,phy-use-*` / `cdns,phy-io-mask-*` / +`cdns,phy-sync-method` properties **[V `de25-linux:…/socfpga_agilex5_de25_nano.dts:127-135`]** that +the stock driver's property table **does not parse** — it knows only eleven +`cdns,phy-input-delay-*` / `cdns,phy-dll-delay-*` names **[V `linux:drivers/mmc/host/sdhci-cadence.c:108-118`]**. +Those are **dead devicetree on a mainline driver**; do not transcribe them. Their inertness also +means his working SD path runs on the stock driver's **default** PHY configuration, which is what we +would inherit. + +**A correction to an earlier draft of this section:** `altr,smmu_enable_quirk` was described as dead +devicetree on the same footing. That is true **only** of the friend's mainline-based tree +(`grep -rn smmu_enable_quirk /mnt/source/de25-linux/drivers/` → no hits) **[V]**. In Terasic's vendor +tree the property is **live and load-bearing**: `stratix10-svc.c:3508` reads it, and without it the +agilex5-svc probe path returns `-ENODEV` **[V, fetched this pass]**. It is mainline-inert, not +meaningless — and the fact that a vendor driver gates SDM DMA setup on it is evidence for §2.6, not +against it. + +### 2.3 What the friend actually carries, counted + +Diffing `/mnt/source/de25-linux` (6.18.38) against `linux-6.18.44`, filtering 6.18.38→.44 stable +churn **[V]**: + +| Carried change | Size | Avoidable? | +|---|---|---| +| `+{.compatible = "intel,agilex5-soc-fpga-mgr"},` in `s10_of_match` (`de25-linux:drivers/fpga/stratix10-soc.c:451`) | 1 line | **Yes** — declare the fallback in DT instead | +| `+{.compatible = "intel,agilex5-svc"},` in `stratix10_svc_drv_match` (`de25-linux:drivers/firmware/stratix10-svc.c:1134`) | 1 line | **Yes** — same | +| `drivers/clk/socfpga/clk-agilex5.c` + `Makefile:6` | **847 lines** (his vendor backport; the mainline v6.19 file is 561) | **Yes, by moving to ≥6.19** (§5) | +| `drivers/misc/de25_fpga_trigger.c` | 95 lines | **No** — see §7 | + +**[V** all four, `wc -l` and `grep` this pass.**]** + +He needed the two match-table lines only because his DT declares `intel,agilex5-svc` and +`intel,agilex5-soc-fpga-mgr` **with no fallback string** +**[V `de25-linux:…/socfpga_agilex5.dtsi:215,224`]**. That is a DT authoring choice, not a kernel +constraint. `of-fpga-region.c` and `fpga-mgr.c` are untouched in his tree **[V `diff`]** — he added +no writable attribute; the trigger is the separate module. + +### 2.4 What would falsify the "binds and works" reading + +1. **An `fpga-mgr` node carrying only the fallback string fails to bind, or `s10_probe()` errors on + real Agilex 5 silicon.** Low risk — §2.1's mechanism is read from the OF core, and §2.2 shows the + idiom working on this silicon for a different driver. +2. **The fpga-mgr binds and then cannot program.** **This is no longer hypothetical.** Terasic's + vendor svc driver does Agilex-5-specific SDM plumbing that mainline lacks entirely (§0 Finding 2), + Khairul's own changelog rationale for wanting an Agilex 5 compatible is that Agilex 5 "changes how + reserved memory is mapped and accessed" **[U, rationale text only]** — and the one mainline-path + attempt on hardware failed at the SDM mailbox **[V]**. See §2.6. +3. **Upstream adds per-compatible `.data` to `s10_of_match` or `stratix10_svc_drv_match`.** The + moment either table keys behaviour on the string, the fallback becomes correct-but-degraded and + this section must be re-derived. **Watch those two tables on every kernel bump** — and watch + specifically for upstream adopting the vendor's remapper/SMMU/DMA-offset behaviour, which is the + *realised* form of this falsifier sitting in a shipping vendor tree today. + +### 2.5 The `dtbs_check` wrinkle — a correction owed + +Khairul's v6 binding patch, which restructures the fpga-mgr `compatible` from a flat `enum` into a +`oneOf` blessing the two-string fallback **[V, patch body read at `lkml.iu.edu/2511.2/10025.html`]**, +**has not landed anywhere upstream** — not in v7.2, not at `master` +**[V `mainline@v7.2` and `mainline@master:Documentation/devicetree/bindings/fpga/intel,stratix10-soc-fpga-mgr.yaml`, +both still a plain two-value `enum`]**. Consequently: + +| `fpga-mgr` compatible form | Binds stock driver? | `dtbs_check` today | Forward-safe? | +|---|---|---|---| +| `"intel,agilex5-soc-fpga-mgr"` alone | **no** | fails (not in enum) | only after a driver patch | +| `"intel,agilex5-soc-fpga-mgr", "intel,agilex-soc-fpga-mgr"` | **yes** | **warns** (enum, not `items`) | yes — clean once v6 lands | +| `"intel,agilex-soc-fpga-mgr"` alone | **yes** | clean | loses SoC identity if `.data` is ever added | + +**Recommendation: use the two-string form and accept a transient `dtbs_check` warning**, tracked as +a known-warning entry that disappears when the v6 binding lands. This **corrects** +[`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) §4.2, which states that "writing the two-string +form now is free and forward-compatible" — it is free *at runtime*, but it is **not** schema-clean +today, and that document's own note assumed the binding patch had landed. + +### 2.6 The real evidentiary position on "no carried patch", and the test that settles it + +State it plainly, because it is the difference between a plan and a hope: + +| Question | Status | Evidence | +|---|---|---| +| Does a fallback compatible make `/sys/class/fpga_manager/fpga0` and `/sys/class/fpga_region/region0` appear, with stock drivers? | **Very likely yes** | OF core mechanism [V]; same idiom proven on this silicon for `sdhci-cadence` [V]; friend's board reaches probe with only string additions [V] | +| Does mainline's `stratix10-svc` actually **program an Agilex 5 fabric**? | **Not established. One observed failure.** | Mainline svc hands the SDM raw `gen_pool` physical addresses with no IOMMU mapping and no offset (`linux:drivers/firmware/stratix10-svc.c:277,458,785-807,1007`) [V]. Terasic's vendor svc adds `+0x80000000`, attaches an IOMMU domain, and *disables the SDM remapper* for `intel,agilex5-svc` [V]. Mainline has none of this at 6.18.44 or v7.2 [V]. The single mainline-path attempt on hardware timed out on `RECONFIG_REQUEST` and wedged the board [V] | + +**Confidence: high on binding; low on end-to-end reconfiguration through stock mainline drivers.** +The friend's board is **not** evidence that mainline svc can program this fabric — his own working +reconfigurations ran on Terasic's vendor 6.12.11 kernel, which has all of the above. + +**The on-hardware test that settles it, and it is cheap:** + +1. Boot a DE25 on a ≥6.19 mainline kernel with **no driver patches at all** and a DT carrying + `firmware { svc { compatible = "intel,agilex-svc"; method = "smc"; memory-region = <&service_reserved>; + iommus = <&smmu 10>; fpga-mgr { compatible = "intel,agilex5-soc-fpga-mgr","intel,agilex-soc-fpga-mgr"; }; }; }` + plus a root `fpga-region`. +2. Confirm `fpga0` and `region0` appear and `dmesg` shows `of-fpga-region fpga-region: FPGA Region probed`. + *(This is the binding half. Expected to pass.)* +3. Apply an overlay with `firmware-name` pointing at a small `.rbf` and watch for either + `SVC_STATUS_BUFFER_DONE` progress and config-complete, or the `RECONFIG_REQUEST` timeout. + *(This is the programming half. This is the actual experiment.)* +4. **Run step 3 twice: once with the `smmu` node disabled and once enabled.** The vendor code's + structure — remapper bypass only when the SMMU is on — makes SMMU state the most likely + discriminator, and it is a one-line DT change. + +**If step 3 fails**, the mainline-first posture on the *svc layer* is refuted for Agilex 5 and we +have a real decision to take: carry a port of the vendor's agilex5 svc behaviour (a substantial +patch, not a one-liner), or drive reconfiguration some other way. Nothing else in this document +depends on that outcome — the clock driver, the SD controller, the DTS node set, U-Boot and the +partition layout are all independent of it. **Do this test first in the D2 hardware session.** + +--- + +## 3. Minimal DTS node set + +What mainline gives us, by base. `6.18.44` is the local tree; `v7.2` is the current release; +`master` is post-7.2 (→ 7.3, ~Oct 2026 by cadence). + +| Node | 6.18.44 | v7.2 | master | What we author | +|---|---|---|---|---| +| `clkmgr` **driver** | **absent** (binding + header + DT node present, **no driver**) | present (since v6.19) | present | nothing on ≥6.19; **carried backport** on 6.18 | +| `/firmware/svc` | absent | present, `compatible = "intel,agilex5-svc"` — **binds nothing** | same | 6.18: whole node. 7.x: **override `compatible` to `"intel,agilex-svc"`** | +| `fpga-mgr` | absent | absent | absent | **author on every base** | +| `fpga-region` | absent | absent | absent | **author on every base** | +| `smmu` (`arm,smmu-v3`) | absent | present, `status = "disabled"` | present, **enabled** | 6.18: whole node. v7.2: `status = "okay"` | +| `mmc0` | **absent** | **absent** | **absent** | **author on every base** — this is the SD-boot gate | +| `gmac0..2` | present, real per-compatible `.data` | present (+`iommus`/`dma-coherent` since v7.0) | present | board-level `status`/`phy-mode`/`phy-handle` only | + +**[V** 6.18.44 rows by `grep` over `linux:arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi` (826 lines; +case-insensitive `mmc|sd4hc|sdhci|smmu|iommu` → **zero hits**, re-run this pass; `service_reserved` +svcbuffer at `:23`, `clkmgr` node at `:144-148`, QSPI at `:477`, three stmmac compatibles at +`:491,:603,:715`); clock-ID header at `linux:include/dt-bindings/clock/intel,agilex5-clkmgr.h`; and +**no** file under `linux:drivers/clk/socfpga/` naming agilex5 (`ls`, this pass). v7.2 and master +fetched in full and grepped; `clk-agilex5.c` first appears at tag `v6.19` (commit `2050b57ecda0`, +Ang Tien Sung / Khairul Anuar Romli, Altera; via Dinh Nguyen), **561 lines**, matching +`intel,agilex5-clkmgr` at `:544`.**]** + +### 3.1 The nodes, as authored (on a 7.x base) + +```dts +/* mainline 7.x ships /firmware/svc with a compatible that binds no driver. + Override it; keep iommus (a top-level property in the binding, legal for any + compatible) and add the fpga-mgr child the svc binding already blesses. + The node NAMES 'firmware' and 'svc' and the 'method' property are all + load-bearing — see §2.1. */ +&{/firmware/svc} { + compatible = "intel,agilex-svc"; /* [V] binds; schema-clean */ + /* method = "smc" and memory-region inherited from mainline's node */ + + fpga_mgr: fpga-mgr { + compatible = "intel,agilex5-soc-fpga-mgr", + "intel,agilex-soc-fpga-mgr"; /* [V] binds on the fallback */ + }; +}; + +/ { + base_fpga_region: fpga-region { + compatible = "fpga-region"; + #address-cells = <0x2>; + #size-cells = <0x2>; + fpga-mgr = <&fpga_mgr>; + }; +}; + +&smmu { + status = "okay"; /* needed at v7.2; already on at master */ +}; + +/* mmc0 is absent from EVERY mainline base and must be authored in full. + The sd4hc form binds stock sdhci-cadence through the bare cdns,sd4hc entry. + Whether it carries iommus/dma-coherent is an open experiment — §8 Q2. */ +``` + +**Notes that are load-bearing, not stylistic.** + +- The svc override to the gen1 string is schema-clean: `intel,agilex-svc` is in the binding's + `enum`, and `iommus` is declared as a plain top-level property, so keeping `iommus = <&smmu 10>` + is legal **[V `mainline@v7.2:Documentation/devicetree/bindings/firmware/intel,stratix10-svc.yaml` + — `enum` includes all three strings, `iommus: maxItems: 1` top-level, `additionalProperties: false`, + and an `allOf` that *requires* `iommus` when the compatible contains `intel,agilex5-svc`]**. The + two-string form `"intel,agilex5-svc","intel,agilex-svc"` would bind but **fails** `dtbs_check`, + because the svc binding is a plain `enum`, not the `items` form. **Whether dropping the agilex5 + string while keeping `iommus` is functionally equivalent is [U]** — and §0 Finding 2 sharpens why + it may not be: the vendor driver treats the agilex5 string as the switch that turns on IOMMU + attach, IOVA carveout and remapper bypass. Mainline does none of that under *either* string, so + the override is safe *relative to mainline* — but if we ever port vendor behaviour, the string + becomes semantic. See §8 Q3. +- **Mainline 7.x has a real bug here worth a one-line patch upstream:** it ships an Agilex 5 svc DT + node whose compatible matches no driver on any released kernel through v7.2 or at `master` **[V]**. + The fix is one match-table line. Sending it to `linux-fpga` is cheap, is squarely within decision 5, + and would remove our override entirely. +- No `fpga-bridges` property and **no bridge nodes at all**. The Cyclone V shape has no Agilex + analogue and must not be transliterated — see [`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) §4.2. +- `CONFIG_FPGA_BRIDGE=y` is still required, because `FPGA_REGION depends on FPGA_BRIDGE` and + `OF_FPGA_REGION depends on OF && FPGA_REGION` **[V `linux:drivers/fpga/Kconfig:145-158`]**, even + though no bridge driver is used. And `CONFIG_OF_OVERLAY` is **not** selected by `OF_FPGA_REGION` — + with it off, the notifier registration is a stub and reconfiguration silently never fires. That + trap is already documented at [`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) §4.1 and belongs in + the per-board sentinel set in `scripts/check-kernel-defconfig-sync.sh`. + +--- + +## 4. Prior art — a complete DE25-Nano DTS already exists, twice + +**Do not author the board file from scratch.** Two open-source trees carry a full +`socfpga_agilex5_de25_nano.dts`, and the more current one is vendor-authored: + +| Tree | Branch / HEAD | Kernel | What it carries | +|---|---|---|---| +| `github.com/terasic/linux-socfpga` | `de25-nano-6.12.11-lts`, `d7d192a9dd`, 2025-06-27 | 6.12.11 | 207-line board `.dts` + 1255-line `.dtsi`; `mmc0` = `"intel,agilex5-sd4hc","cdns,sd4hc"`, `smmu`, `svc`/`fpga_mgr`/`fpga-region` all wired | +| **`github.com/altera-fpga/linux-socfpga`** (formerly `altera-opensource`) | **`socfpga-6.18.20-lts`** (default branch), pushed 2026-08-11 | 6.18.20 | 206-line board `.dts` + 1274-line `.dtsi`; upstream-style cleanup of Terasic's file; `svc`/`fpga_mgr`/`fpga-region` at `:82-96,:188-193`; `smmu` enabled from the board file | + +**[V** both files fetched in full; Altera dtsi `firmware/svc` at `:82-96` with `fpga_mgr` child at +`:92-95`, root `fpga-region` at `:188-193`, `mmc0` at `:356-362`.**]** + +That Altera itself has taken the DE25-Nano board file in-house — on its **current default branch**, +pushed eleven days before this retrieval — is the most useful thing this survey found. It makes the +following unnecessary: authoring the board `.dts` from first principles; guessing pinmux, PHY +wiring, regulator GPIOs, QSPI partition layout, or the `temp_volt` hwmon channel map; and treating +the friend's back-port as the only reference. It is a **better primary reference** than the friend's +tree: newer, closer to our base, and traceable to a named vendor org. + +### 4.1 But it is not drop-in, and the reason matters + +Altera's 6.18.20-lts `mmc0` declares `compatible = "altr,agilex5-sd6hc", "cdns,sd6hc"` **[V +`socfpga-6.18.20-lts:…/socfpga_agilex5.dtsi:356`]**, with a `COMBOPHY_RESET` stage and no static +PHY timing properties — it targets their **out-of-tree SD6HC driver rewrite** (dynamic timing +calculation replacing static DT params, by Tanmay Kathpalia, Altera; visible in the sibling +`comet-a13-6.18.2-lts` branch's commit log, state `pending` **[V, commit messages only]**). + +**`cdns,sd6hc` does not exist anywhere in mainline** — not in 6.18.44, not at v7.2, not at `master` +**[V `grep` of `drivers/mmc/` and `Documentation/devicetree/bindings/mmc/` on all three]**. Copying +Altera's `mmc0` verbatim therefore **buys a carried driver patch** and violates decision 5. The +older SD4HC form, which Terasic and the friend both use and which the friend's board demonstrably +boots on with an unpatched driver (§2.2), does not. + +**Rule for harvesting: take Altera's board `.dts` as the wiring reference — pinmux, PHY, regulators, +QSPI partitions, hwmon channels — and take `mmc0` in the SD4HC form.** Reconcile every node against +what mainline's driver actually matches before adopting it. The prior art tells us *what the board +looks like*; it does not tell us *what our kernel binds*. §4.2 is the same lesson applied to the +svc node. + +### 4.2 Read the vendor trees for *behaviour*, not just for nodes + +The survey's second-most useful output is negative: the vendor trees carry **driver** behaviour that +their DTS silently depends on. Terasic's `stratix10-svc.c` is 3733 lines to mainline's 1334, and the +delta includes the entire Agilex 5 SDM DMA story (§0 Finding 2) **[V]**; its `sdhci-cadence.c` gives +`intel,agilex5-sd4hc` a dedicated `.data` carrying `SDHCI_QUIRK2_40_BIT_DMA_MASK`, a quirk that +**does not exist anywhere in mainline** **[V, §5.1]**. A DTS harvested from a vendor tree and dropped +onto a mainline kernel is therefore not a like-for-like transplant, and the differences are invisible +at build time. Every node we adopt gets checked against *the driver mainline will actually bind*. + +Also flagged, not pursued: `Thejayman77/retroDE*` (an apparently independent MiSTer-core-on-DE25 +porting effort) and `GM-Benji/agilex5-linux-amp` **[U, not inspected]**. MiSTeX has **no** Agilex 5 +support — GitHub code search for `agilex5` in `MiSTeX-devel/MiSTeX-hardware` returns zero hits **[V]**. + +--- + +## 5. Kernel version — recommend mainline 7.2; minimum acceptable base is 6.19 + +**Recommendation: pin the DE25 to mainline 7.2, the line this repo already builds for the DE10 RT +beta. Do not base the DE25 on 6.18.** Confidence: **high** on the mechanism driving the choice, +**medium** on the operational cost, which is a treadmill question rather than a technical one. + +### 5.1 Why 6.18 fails on its own terms + +The 6.18 case rested on three supports. Two collapse and the third has been re-characterised: + +1. **"6.18 is the only LTS."** True — 6.18 was designated longterm, supported into Dec 2028 **[V]**; + 6.19/7.0/7.1/7.2 are ordinary stable lines. But this is an argument about *bump cadence*, not + capability, and it is outweighed by (3). +2. **"The mmc0/SMMU regression argues for a newer kernel."** **Re-characterised, and it now argues + for nothing.** An earlier reading called the fault a 6.12→6.18 *kernel-version regression* on a + byte-identical DT. That is **not established, and the evidence points elsewhere.** The "working + 6.12" baseline is **Terasic's vendor tree**, whose `sdhci-cadence.c` gives `intel,agilex5-sd4hc` + a dedicated match entry carrying **`SDHCI_QUIRK2_40_BIT_DMA_MASK`** + **[V `terasic/linux-socfpga@de25-nano-6.12.11-lts:drivers/mmc/host/sdhci-cadence.c:783-786,952-953`, + fetched this pass]**. That quirk **exists nowhere in mainline** — `grep '40_BIT_DMA_MASK'` over + `drivers/mmc/` and `include/` at 6.18.44 → zero hits; over `sdhci.h` at v7.2 and `master` → zero + hits **[V]**. Mainline's `sdhci_set_dma_mask()` therefore takes the 64-bit branch + (`dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))`, `linux:drivers/mmc/host/sdhci.c:4117-4123`) + **[V]** on a controller the vendor deliberately caps at 40 bits. Under SMMU translation with + top-down IOVA allocation, an IOVA above the controller's wired address bits truncates — which is + precisely an `F_TRANSLATION` "input address caused fault". **The DT was byte-identical; the + drivers were not.** Mainline 6.12 was never tested and could not have been (no Agilex 5 clk driver + in mainline before v6.19). **Consequence: this is a vendor-vs-mainline delta, not a version + regression, so it does NOT disappear on 6.19 or 7.2 — it travels with mainline `sdhci-cadence` + + SMMU on every version [V].** It is now an open hardware question with a named leading hypothesis + and a cheap upstreamable fix (§8 Q2), not a version-selection input in either direction. Note + separately that mainline has never shipped an Agilex 5 `mmc0` node on any version 6.18 → `master` + **[V]**, so `iommus` and `dma-coherent` are knobs we control. +3. **"A 6.18 DE25 carries no driver patches."** **False, and this is decisive.** `clk-agilex5.c` + lands in **v6.19** and is absent from 6.18 **[V]**. Mainline 6.18.44 has the + `intel,agilex5-clkmgr` binding, the clock-ID header, and the DT node at `:144-148` — and no driver + at all; the *only* `agilex5` compatible string in the whole of 6.18.44's `drivers/` is stmmac's + **[V, `grep -rl` this pass]**. Every consumer of `&clkmgr` — `mmc0` included — would defer + forever. The friend confirms this by construction: he backported an 847-line vendor + `clk-agilex5.c` into his 6.18.38 tree and wired it into `drivers/clk/socfpga/Makefile:6` **[V]**. + +Support 3 is decisive under decision 5. Basing on 6.18 means carrying a whole SoC clock driver when +a mainline route plainly exists one release later. That is exactly the carried patch decision 5 +tells us to justify — and there is no justification available. + +### 5.2 What each base actually costs + +| Base | Carried driver code | DTS work | Line status | +|---|---|---|---| +| 6.18.44 | **clkmgr backport** (561 lines mainline / 847 lines vendor) | author `svc` + `smmu` + `fpga-mgr` + `fpga-region` + `mmc0` | LTS → Dec 2028 | +| 6.19 | **none** | author `fpga-mgr` + `fpga-region` + `mmc0`; override `svc` compatible; enable `smmu` | EOL | +| **7.2** | **none** | same as 6.19 | stable, short tail | +| master → 7.3 | none | same, minus `smmu` enable | unreleased | + +Moving from 6.18 to ≥6.19 removes a driver backport and two authored nodes. Moving from 6.19 to 7.2 +gains `iommus`/`dma-coherent` on the ethernet nodes (landed by v7.0 **[V]**) and puts us on the line +the repo already builds. Note the correction to one leg's reading: at **tag v7.2** the `smmu` node +still carries `status = "disabled"` (`:379-389`); only at `master` is it enabled (`:379-388`) **[V, +both fetched]**. Two legs disagreed here because one read `master` and called it 7.2. Either way we +set `status = "okay"` from the board file, as Altera's own board file does. + +**No version of mainline fixes anything on the reconfiguration question (§2.6).** `stratix10-svc.c` +grows from 1334 lines at 6.18.44 to 2113 at v7.2, but the growth is FCS command plumbing; a `grep` +for `iommu|REMAPPER|dma_addr_offset` at v7.2 returns **zero hits**, exactly as at 6.18.44 **[V]**. +Choosing 7.2 buys the clock driver and the ethernet `iommus`; it does not buy Agilex 5 SDM support. + +### 5.3 Cost of a different kernel from the DE10 + +Small, and already paid. `de25-patch-portability.md` triages our 40-patch series against 6.18.44: +**33/40 (82.5%) portable-as-is**, all subsystem-generic (HID/USB/mmc-core/i2c/exfat/leds), none +6.18-coupled **[V `de25-patch-portability.md:124-166`]**. `rt-beta-kernel.md` records a **completed +build measurement**: the full 40-entry series applies to **Linux 7.2 final** at Buildroot's `-F0` +with **40/40 applying and zero hunks taking fuzz**, verified 2026-08-17 **[V `rt-beta-kernel.md:222`]**. +That is on the DE10 ARM32 tree, so transfer to aarch64 is **[U]** — but the touched files are +architecture-generic C, so confidence is high. ADR 0021's per-variant machinery (shared +`linux.config` + version fragment + symlinked series) is proven end-to-end on that same 7.2 line, so +a DE25 pin at 7.2 is a **new instance of an existing pattern**, not new engineering. + +The real cost is the **bump treadmill**: 7.2 is not LTS, so the DE25 inherits the RT beta's +discipline (`rt-clean`, TOFU-hash re-verification, boot re-proved per version) indefinitely. Pinning +the DE25 to the same 7.x line the RT beta already tracks keeps that at **two** kernel lines in the +repo rather than three, and lets one bump serve both. + +**Re-open this if:** kernel.org designates a 7.x release longterm (pin that one instead); or 7.3 +ships with the `smmu` already enabled *and* any Agilex 5 `mmc0` node, which would be the first +upstream test case for the SD/SMMU question either way. + +--- + +## 6. U-Boot + +**We build `u-boot.itb` only.** The factory SPL in QSPI is untouched — that is the posture-1 +contract fixed in [`de25-boot-chain.md`](de25-boot-chain.md) §2, §8.3. + +**Version: mainline v2026.07** (current stable; v2026.10 is at -rc2, due 2026-10-05) **[V]**, paired +with **mainline TF-A v2.15.0**, which does carry `plat/intel/soc/agilex5/` **[V]**. Flag plainly: +**this pairing is unblessed and untested by us [U]**. Terasic and Altera document only vendor forks +(`u-boot-socfpga socfpga_v2023.10` + `arm-trusted-firmware socfpga_v2.10.0`). There is **no** +Terasic DE25-Nano board anywhere in mainline U-Boot — `board/terasic/*` has de0-nano-soc, de1-soc, +de10-nano, de10-standard, sockit and no de25 entry, and no `configs/*de25*` exists in the full +40,995-path v2026.07 tree **[V]**. We carry the board fragment ourselves; there is nothing to select. + +### 6.1 The FIT contract we must hit + +`u-boot.itb` is produced by **binman**, not the legacy `u-boot.itb:` Makefile rule (which is gated +on `U_BOOT_ITS`, set only under the deprecated `SPL_FIT_GENERATOR`, unused here — `Makefile:1785`). +`ARCH_SOCFPGA_AGILEX5` `select`s `BINMAN if SPL_ATF` (`arch/arm/mach-socfpga/Kconfig:72-78`) and the +defconfig sets `SPL_ATF=y` (**[V]** re-verified this pass: `socfpga_agilex5_defconfig:54` +`CONFIG_SPL_ATF=y`), so `make all` runs `.binman_stamp` (`Makefile:1393,1399`) against +`arch/arm/dts/socfpga_soc64_fit-u-boot.dtsi` **[V]**: + +| FIT element | Content | +|---|---| +| image `uboot` | `u-boot-nodtb.bin`, `type=standalone`, `arch=arm64`, `load = 0x80200000` (= `CONFIG_TEXT_BASE`) | +| image `atf` | `bl31.bin`, `type=firmware`, `os=arm-trusted-firmware`, `load = entry = 0x80000000` | +| image `fdt-0` | `u-boot.dtb`, description `"socfpga_socdk"` → rename per board | +| config `board-0` | `default`; `firmware="atf" loadables="uboot" fdt="fdt-0"`; `signature { algo = "crc32"; … }` — an integrity stamp, no keys, consistent with the factory SPL accepting unsigned FITs | + +**Residual closed:** `board_fit_config_name_match()` *is* compiled in for SOC64 +(`arch/arm/mach-socfpga/board.c:148-158`) and matches each config node's **`description`** — not its +node name — formatted `"board_%u"` from `socfpga_get_board_id()` (`:114-146`), and +`fit_find_config_node()` (`boot/common_fit.c`) falls back to `/configurations/default` when nothing +matches **[V]**. A single-config FIT therefore boots correctly **regardless of board ID**. + +**Build shape.** Nothing forces `CONFIG_SPL` on — `ARCH_SOCFPGA_AGILEX5` selects `BINMAN if SPL_ATF`, +`CLK`, `FPGA_INTEL_SDM_MAILBOX`, `SPL_CLK if SPL`, and `ARCH_SOCFPGA_SOC64` **[V]** — and none of the +binman FIT images references anything under `spl/`. So our fragment can carry +`# CONFIG_SPL is not set`, genuinely eliminating SPL compilation. **[U] that this builds clean**; +it is reasoned from the Kconfig graph, not build-tested, and is the first thing to check at first build. + +### 6.2 The env configuration that makes a QSPI write structurally impossible + +This is stronger than "never call `saveenv`", and it upgrades ledger row 12 from **[U]** to +**[V, code-traced]**. Re-verified against `v2026.07` sources this pass. + +The stock `socfpga_agilex5_defconfig` sets **both** `CONFIG_ENV_IS_IN_FAT=y` (`:72`) and +**`CONFIG_ENV_IS_IN_UBI=y`** (`:73`), with `ENV_FAT_DEVICE_AND_PART="0:1"` (`:74`), +`ENV_UBI_PART="root"` (`:75`), `ENV_UBI_VOLUME="env"` (`:76`) **[V, fetched at v2026.07]**. FAT is +tried before UBI in `env_locations[]`, and `env_save()` targets whatever `env_load()` last succeeded +from (`gd->env_load_prio`, `env/env.c:33-73`) **[V]** — so with a valid `uboot.env` on FAT, `saveenv` +never reaches UBI. + +**But the hazard is on the load path, with no save involved.** `env_ubi_load()` (`env/ubi.c:107`) +calls `ubi_part()` **unconditionally** at **`env/ubi.c:128`** whenever the FAT load fails +**[V, file fetched at v2026.07 and read this pass]**. That runs `ubi_dev_scan()` → `ubi_init()` → +`ubi_attach_mtd_dev()` → `ubi_attach()`; against a **fully erased** MTD partition this **succeeds** +(`ai->is_empty = 1`, "empty MTD device detected", `drivers/mtd/ubi/attach.c:1100-1126`), whereupon +`ubi_read_volume_table()` calls `create_empty_lvol()` → `create_vtbl()` (`vtbl.c:299,495,780,808-809`) +and **writes a fresh UBI layout volume into QSPI** **[V]**. + +> **Missing or corrupt FAT env + a blank `root` MTD ⇒ QSPI is written on the very first +> `env_load()`, with zero user action and no `saveenv` anywhere.** + +**The guard must therefore be `# CONFIG_ENV_IS_IN_UBI is not set` in our board fragment.** Nothing +in `ARCH_SOCFPGA_AGILEX5`/`ARCH_SOCFPGA_SOC64` `select`s it — it is a plain defconfig choice **[V]**. +Recommended fragment, with a comment that survives future editing: + +``` +CONFIG_ENV_IS_IN_FAT=y +CONFIG_ENV_FAT_DEVICE_AND_PART="0:1" +# CONFIG_ENV_IS_IN_UBI is not set # env_ubi_load() -> ubi_part() -> UBI attach -> + # create_vtbl() writes a layout volume into a blank + # QSPI partition on LOAD, not just on save. The write + # is in the UBI attach path, not in env/ubi.c itself — + # grepping env/ubi.c for a write finds nothing. See §6.2. +# CONFIG_SPL is not set # factory SPL is untouched; we ship u-boot.itb only +``` + +### 6.3 exFAT — a blocker that has expired + +Mainline U-Boot now has real exFAT: `fs/exfat/`, `CONFIG_FS_EXFAT` ("read/write support", +`imply CMD_FS_GENERIC if CMDLINE`), added by commit `b86a651b64` on **2025-03-17** — *after* the +reference board's U-Boot 2025.01 base **[V]**. That is precisely why the friend had to hand-roll +`libexfat` and a custom exFAT-aware SPL; on current mainline the equivalent is one defconfig line. +Note the stock defconfig enables `SPL_FS_FAT` (`:22`) but **not** `FS_FAT`/`CMD_FAT`/`FS_EXFAT` for +U-Boot proper **[V, re-checked this pass]** — we set what we need either way. + +This removes a capability gap; it does **not** decide p2's filesystem. **Decision 3 fixes the +partition count and p1's FAT type only — p2's filesystem is still an open owner decision [U].** +Kernel-on-p1 sidesteps the question entirely and matches the SPL contract's spirit; kernel-on-p2 is +now viable if p2 ends up exFAT. The 4 GiB FAT32 per-file cap is irrelevant to a kernel `Image`. + +--- + +## 7. The residual scope question — the minimal kernel-side trigger + +Decision 2 says we make the hardware available and Main_MiSTer does the loading. The honest problem +is that **mainline provides no userspace entry point at all**, so "available" is not achievable with +zero non-mainline code. This section states the options and their costs. It does **not** design a +loader. + +**What mainline exposes [V, all re-read this pass]:** `fpga_manager` class devices carry `name`, +`state`, `status` — all `DEVICE_ATTR_RO`, and `fpga_mgr_attrs[]` holds exactly those three +(`linux:drivers/fpga/fpga-mgr.c:655-664`). `fpga_region` carries only `compat_id`, also RO +(`linux:drivers/fpga/fpga-region.c:175`). There is no writable attribute anywhere. `OF_CONFIGFS` +**does not exist in mainline** — `linux:drivers/of/Kconfig` has only `OF_OVERLAY:105` and +`OF_OVERLAY_KUNIT_TEST:116`, and there is no `drivers/of/configfs.c`. Altera's vendor 6.12-lts tree +does carry it — i.e. the friend's original path was the vendor path. + +**The only mainline trigger is an OF overlay carrying `firmware-name`**, caught by of-fpga-region's +overlay notifier, registered at module init (`linux:drivers/fpga/of-fpga-region.c:340,455`). + +| Option | Mainline surface | Cost | Verdict | +|---|---|---|---| +| (a) Small out-of-tree module calling `of_overlay_fdt_apply()` | **adds one file, modifies none** | ~95 lines; GPL-only symbol; must track OF API drift | **Recommended** | +| (b) Carry `OF_CONFIGFS` | permanently forks `drivers/of` | repeatedly rejected upstream; conflicts on every rebase | Rejected | +| (c) Static base-DT `fpga-region` with `firmware-name` | zero code | **does not work — see below** | **Closed [V]** | + +**(c) is settled closed.** `firmware-name` is read **only** inside `of_fpga_region_parse_ov()` +(`linux:drivers/fpga/of-fpga-region.c:232`, plus the child-region rejection helper at `:162`), and +that function is reached **only** from the overlay notifier. `of_fpga_region_probe()` (`:396`) never +reads it **[V, function read in full]**. A base-tree `firmware-name` programs nothing at boot. This +closes Leg B's open unknown about a zero-out-of-tree-code path: **there isn't one.** + +**(a) is the recommendation.** `of_overlay_fdt_apply()` and `of_overlay_remove()` are +`EXPORT_SYMBOL_GPL` (`linux:drivers/of/overlay.c:1090,1272`) **[V]**, so a small GPL module can drive +reconfiguration without patching any mainline file. The friend's `de25_fpga_trigger.c` is 95 lines +doing exactly this — `request_firmware()` → `of_overlay_fdt_apply()` → `DEVICE_ATTR_WO(trigger)` on a +misc device **[V `de25-linux:drivers/misc/de25_fpga_trigger.c`, 95 lines by `wc -l`]**. + +**What that module's hardware history actually shows — corrected.** An earlier draft said "it is +running on real hardware." That overstates it. On real silicon the module **reached the fpga-mgr +write path** — `dmesg` shows `fpga_manager fpga0: writing de25_live_switch_current.rbf to Stratix10 +SOC FPGA Manager` — and the **single** reconfiguration attempt then **timed out waiting for +`RECONFIG_REQUEST` and wedged the board** **[V `de25-reference-implementation.md:115`, §5]**. So the +module is an existence proof that *the trigger shape works* — a userspace write does reach +`fpga_region_program_fpga()` through the overlay notifier — and it is **not** evidence that +programming succeeds. Those are two different claims and §2.6 owns the second. + +**The honest tension, stated plainly.** Option (a) is still non-mainline code. Decision 5 cannot be +fully satisfied for the trigger, because **no mainline route exists** — which is precisely the +justification decision 5 asks for, and (as of this pass) the only one of our carried items that has +it. What (a) buys over (b) is rebase safety: a new file never conflicts, and it does not relitigate +a rejected upstream design. Scope discipline holds: the module's entire job is to accept "apply this +overlay" and hand it to `of_overlay_fdt_apply()`. Choosing the overlay, staging the `.rbf`, +sequencing switches, and quiescing the fabric client are **Main_MiSTer's**, per decision 2. + +**Answer to the residual question, in one sentence:** *the minimal kernel-side trigger is a small +out-of-tree GPL misc driver exposing one write-only attribute that calls `of_overlay_fdt_apply()` on +a `firmware-name`-bearing overlay — roughly 100 lines, adding one file and modifying none, because +mainline exposes no writable fpga-manager attribute, has no configfs overlay loader, and ignores +`firmware-name` outside the overlay-notifier path.* + +--- + +## 8. Open [U] — what is unsettled and what settles each + +| # | Open question | Why it matters | How it is settled | +|---|---|---|---| +| 1 | **Can mainline's `stratix10-svc` actually program an Agilex 5 fabric?** | The headline risk. A fallback-matched fpga-mgr probes cleanly and then fails to program — the worst failure mode, and it has been **observed once on real silicon** [V]. Terasic's vendor svc does IOMMU attach, an IOVA carveout, a `+0x80000000` SDM address offset and an `INTEL_SIP_SMC_SDM_REMAPPER_CONFIG` remapper bypass under `intel,agilex5-svc`; mainline passes raw `gen_pool` physical addresses and has none of it at 6.18.44 **or v7.2** [V]. | **§2.6's four-step hardware test — do it first.** In parallel, read TF-A `plat/intel/soc/agilex5` SIP handlers against `include/linux/firmware/intel/stratix10-smc.h`, and diff Terasic's `stratix10-svc.c` against mainline's to size a port if step 3 fails. Confidence today: **low**. | +| 2 | Does an authored `mmc0` fault `arm-smmu-v3` `F_TRANSLATION` on a 7.x base, and is the cause a DMA-mask width? | **Leading hypothesis, newly evidenced:** mainline takes `DMA_BIT_MASK(64)` (`sdhci.c:4117-4123`) where Terasic's vendor driver caps this controller at 40 bits via `SDHCI_QUIRK2_40_BIT_DMA_MASK` — a quirk absent from mainline at 6.18.44, v7.2 and `master` [V]. Under SMMU translation, top-down IOVA allocation above the controller's wired address bits truncates → "input address caused fault". This is a **vendor-vs-mainline driver delta, so it does not go away on a newer kernel.** | Boot **five** ways: `iommus` present/absent × `dma-coherent` present/absent, **plus** a fifth leg capping mmc0's addressing (`dma-ranges` / `bus_dma_limit` in DT, or a locally applied 40-bit mask) with the SMMU on. Watch dmesg for `F_TRANSLATION`; measure `dd`/`hdparm` to separate working ADMA from forced PIO. **If the fifth leg fixes it, the mainline-first remedy is a one-entry `sdhci-cadence` upstream patch** (`intel,agilex5-sd4hc` + a 40-bit mask), which removes the PIO throughput cost for everyone. No public evidence exists either way **[V, searched: zero agilex5-specific hits; no smmu/iommu commit in the last 100 on `socfpga-6.18.20-lts`]**. | +| 3 | Is overriding svc's compatible to `intel,agilex-svc` while keeping `iommus = <&smmu 10>` functionally equivalent? | Safe against **mainline**, which branches on neither string [V]. Not safe as a general assumption: the binding's `allOf` *requires* `iommus` for the agilex5 string, and the vendor driver makes that string the switch for IOMMU attach + remapper bypass, hard-failing `-ENODEV` without it [V]. | Confirm `iommus` is honoured by the generic `of_dma_configure()` path independent of the matched compatible (`drivers/of/device.c`, `drivers/iommu/of_iommu.c`), then verify on-device that svc buffers in `service_reserved` are SDM-reachable. Folded into Q1's test. | +| 4 | Has Khairul's fpga-mgr series landed anywhere we would ship? | The binding is absent from v7.2 **and** `master`, nine months after the v6 posting (2025-11-18) — long enough to suspect it stalled. Until it lands, our two-string form warns under `dtbs_check` (§2.5). | Fetch the lore thread for a maintainer "Applied" from Xu Yilun or Dinh Nguyen; grep linux-next's `socfpga_agilex5.dtsi` for `fpga-region`. Re-check on every kernel bump. | +| 5 | Does mainline TF-A v2.15.0's Agilex 5 BL31 boot this board, and does it handle >2 MB reconfiguration? | The reference board needed non-stock ATF buffer-size fixes. Stock ATF is untested here, and it is the other half of Q1's SMC contract. | D2.2 hardware pass: build stock v2.15.0 `bl31.bin`, package into `u-boot.itb`, boot under the factory SPL; separately exercise the >2 MB fabric-reconfigure path. | +| 6 | Does `# CONFIG_SPL is not set` build clean and still emit `u-boot.itb`? | Reasoned from the Kconfig graph only. | A desk build of v2026.07 + our fragment; check for `u-boot.itb` and no fatal errors. | +| 7 | Which filesystem for p2, and therefore does the kernel live on p1 or p2? | Decision 3 fixes partition count and p1 only. U-Boot can now read either **[V]**, so this is a project decision, not a capability gap. | Owner decision, informed by the DE10 two-partition precedent and what else p2 must hold. | +| 8 | Do our 33 portable-as-is patches apply cleanly on **aarch64** at 7.2? | Verified at 7.2 on ARM32 with 40/40 and zero fuzz; the touched files are arch-generic C, so transfer is likely but unproven. | First DE25 Buildroot build with the shared series symlinked at a 7.2 pin. | +| 9 | Are `Thejayman77/retroDE*` and `GM-Benji/agilex5-linux-amp` relevant parallel efforts? | Possible prior art for MiSTer cores on this SoC. | One short follow-up leg: inspect for board DTS or fpga-manager plumbing. | +| 10 | Does upstream ever adopt the vendor's Agilex 5 svc behaviour? | If it does, `stratix10_svc_drv_match` gains per-compatible meaning and §2.1's "fallback is free" argument stops holding. This is falsifier (3) in its realised form. | Diff both match tables and `grep` for `iommu|REMAPPER|dma_addr_offset` in `stratix10-svc.c` on **every** kernel bump. Add to the bump checklist. | + +--- + +## 9. Where the legs disagreed, and corrections owed + +**Resolved disagreements.** + +1. **"No driver patch needed" vs "Altera's own tree carries the match entries."** Both observations + are true and they do not conflict *for binding*. Altera and the friend chose *exact* compatible + strings in their DT, which forces the match-table additions; the fallback route was simply not + taken. The OF core makes a fallback binding-equivalent **[V]**, and we author our own DT, so the + choice is ours. The salvaged doc's claim is **upheld in substance for binding**, with two + corrections: the upstream *binding* patch has not landed (so the two-string form warns under + `dtbs_check`), and **binding was never the whole question** — §2.6. +2. **`smmu` status at v7.2.** One leg read `master` and labelled it 7.2. At **tag v7.2** the node has + `status = "disabled"` (`:379-389`); at `master` it is enabled (`:379-388`) **[V, both fetched]**. + Immaterial to the plan — we set `status = "okay"` from the board file either way — but it slightly + shrinks the "7.x gives it free" claim. +3. **Kernel choice.** One leg recommended 6.18 LTS on the grounds that no newer kernel offered + anything concrete. That analysis did not check `drivers/clk/socfpga/`. With `clk-agilex5.c` + landing in v6.19, 6.18 costs a carried driver and the recommendation inverts (§5). + +**Corrections owed to sibling documents.** + +| Document | Claim | Correction | +|---|---|---| +| `de25-fpga-reconfig.md` §4.2 | "Writing the two-string form now is free and forward-compatible" | Free at **runtime**; **not** schema-clean — the binding is still a plain `enum` at `master`, so `dtbs_check` warns until Khairul's v6 lands (§2.5). | +| `de25-reference-implementation.md` (line ~349) | "Mainline 6.18.x cannot reconfigure an Agilex 5 fabric until BOTH a DT node set and a driver match-table entry are added" | The driver half is **avoidable for binding** via a DT fallback compatible **[V]**. What 6.18 *does* unavoidably need is the **clkmgr driver**, which that document does not mention. And "can reconfigure" is not established for mainline drivers at any version (§2.6). | +| `de25-reference-implementation.md` §3 (mmc0/SMMU) | "a real kernel-version regression, not a DT gap" | **Not established.** The 6.12 baseline is Terasic's **vendor** tree, whose `sdhci-cadence` carries a 40-bit DMA-mask quirk that exists nowhere in mainline **[V]**. Vendor-vs-mainline driver delta; mainline 6.12 never tested. The fault therefore travels forward to 6.19/7.2, and 6.18 is not implicated as a version. Recorded in that document's own verification record. | +| `de25-readiness-ledger.md` row 12 | U-Boot env→QSPI write, mechanism **[U]** | Promote to **[V, code-traced]**: the *load* path alone writes a UBI layout volume to a blank QSPI partition; the guard is `# CONFIG_ENV_IS_IN_UBI is not set`, not "don't call `saveenv`" (§6.2). | +| `de25-patch-portability.md` (patch 3 rationale) | "Agilex 5 has a different clkmgr with no in-tree driver at all in 6.18.44" | Correct, and **understated** — it is a boot blocker for the whole board on 6.18, not just context for a cpufreq patch. Landed upstream in v6.19. | + +**Corrections applied to this document's own earlier draft**, listed so the change is auditable: + +- §2.2 no longer presents the friend's SD boot as unqualified evidence — it runs in PIO. +- §2.2 no longer calls `altr,smmu_enable_quirk` dead devicetree without qualification; it is + mainline-inert but vendor-live. +- §7 no longer says the trigger module "is running on real hardware"; its one attempt failed. +- §5.1 support 2 rewritten from "the regression does not materialise" to "the regression was + mis-attributed, and the real delta travels forward". +- §2.4/§8 Q1 confidence on end-to-end reconfiguration lowered from **medium** to **low**, with the + vendor-driver evidence attached. +- The lkml version label for the DTS companion corrected from v6 to v2 (the binding half is v6). +- Line-number citations corrected: friend's `mmc0` compatible is at `socfpga_agilex5.dtsi:387`; + 6.18.44 `sdhci-cadence` match table at `:643-658`; `of_iommu`/OF base score function at + `base.c:338-356`. +- §8 gained Q10 (watch upstream for adopted vendor svc behaviour). + +**Corrections proposed and rejected**, with the reason: + +| Proposed | Rejected because | +|---|---| +| "The v7.2 citation `stratix10-svc.c:1911-1915` is a wrong line number; the table sits near `:1133`." | **The citation is correct.** `:1133` is the **6.18.44** location, in a 1334-line file. The **v7.2** file has grown to 2113 lines (FCS command plumbing) and the table genuinely sits at `:1911-1915` **[V, `mainline@v7.2:drivers/firmware/stratix10-svc.c` fetched and grepped this pass]**. Both citations retained, each labelled with its base. | +| "`clk-agilex5.c … 561 lines` is unconfirmed; the commit adds 736 lines across 7 files." | **The file is exactly 561 lines** **[V, `torvalds/linux@v6.19` fetched, `wc -l` = 561]**. The 736-line figure is the whole commit including Kconfig/Makefile/header churn — a different measure of a different thing. Kept 561 for the file, and now name the friend's 847-line vendor backport alongside it so the three numbers cannot be confused again. | + +**Never challenged by any leg**, and worth flagging as such: that `s10_init()`'s +`of_find_node_by_name(NULL, "svc")` makes the parent node **name** load-bearing, that +`stratix10_svc_init()` does the same for `firmware`, and that a missing `method` property fails +probe outright (§2.1); and that Altera's SD6HC `mmc0` is unusable on mainline (§4.1). + +--- + +## 10. Verification record + +**What ran this pass (2026-08-22).** Two adversarial verification lenses over the draft and its +source legs — one on the headline binding claim, one spot-checking the salvaged +`de25-reference-implementation.md` — followed by a re-verification of every correction against +primary source before it was applied. Nothing was accepted on a verifier's say-so alone. + +**Read directly from local source** (`output/build/linux-6.18.44`, read-only): +`drivers/firmware/stratix10-svc.c` (match table, `of_find_node_by_name`, `get_invoke_func`, +`gen_pool`/`paddr` handling), `drivers/fpga/stratix10-soc.c` (match table, `s10_init`, +`NUM_SVC_BUFS`), `drivers/fpga/of-fpga-region.c` (`firmware-name` read sites, probe, notifier), +`drivers/fpga/fpga-mgr.c` and `fpga-region.c` (sysfs attribute groups), `drivers/of/base.c` +(match scoring), `drivers/of/overlay.c` (exports), `drivers/of/Kconfig`, `drivers/fpga/Kconfig`, +`drivers/mmc/host/sdhci-cadence.c` (match table, probe `.data` fallback, PHY property table), +`drivers/mmc/host/sdhci.c` (`sdhci_set_dma_mask`), `arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi`, +`drivers/clk/socfpga/` (directory listing), and a tree-wide `grep -rl agilex5 drivers/`. + +**Read read-only from `/mnt/source/de25-linux`:** `drivers/clk/socfpga/clk-agilex5.c` (847 lines), +`drivers/misc/de25_fpga_trigger.c` (95 lines), both patched match tables, `socfpga_agilex5.dtsi` +(svc/fpga-mgr/mmc0 compatibles), `diff -q` of `sdhci-cadence.c` against ours (identical), and a +`grep` for `smmu_enable_quirk` consumers (none). + +**Fetched and read this pass:** `torvalds/linux@v6.19:drivers/clk/socfpga/clk-agilex5.c`; +`torvalds/linux@v7.2` and `@master` copies of `stratix10-svc.c`, `stratix10-soc.c`, +`drivers/mmc/host/sdhci.h`, `drivers/mmc/host/sdhci-cadence.c`; +`terasic/linux-socfpga@de25-nano-6.12.11-lts:drivers/mmc/host/sdhci-cadence.c` and +`drivers/firmware/stratix10-svc.c`; `u-boot/u-boot@v2026.07:env/ubi.c` and +`configs/socfpga_agilex5_defconfig`; both LKML archive pages. + +**What changed under challenge.** Six substantive changes, all listed in §9 under "corrections +applied to this document's own earlier draft". The two most consequential: the mmc0/SMMU fault is +re-characterised from a kernel-version regression (which a newer kernel would escape) to a +vendor-vs-mainline driver delta (which it will not); and confidence in end-to-end reconfiguration +through stock mainline drivers is lowered to **low**, with §2.6 added to state the position honestly +and name the hardware test. + +**What remains contested.** Nothing, materially — both rejected corrections (§9) were settled by +going back to the primary source, and the record of why is kept above so the question does not +reopen on memory. + +**What was NOT verified this pass, and must not be read as verified.** + +1. **The whole of §6 beyond the four spot-checks named there.** The U-Boot leg's binman/FIT contract, + `board_fit_config_name_match()` tracing, `Makefile` line citations, the TF-A v2.15.0 Agilex 5 + claim, and the exFAT commit `b86a651b64` were **not** re-read this pass. Re-verified: the + `socfpga_agilex5_defconfig` env/SPL/FS lines, and `env/ubi.c`'s unconditional `ubi_part()`. The + rest carries its original leg's evidence standard. +2. **Khairul's v6 2/2 DTS patch body.** Only the v2 revision is reachable on the archive we can read; + lore was bot-walled. Its *effect* is verified against tree content instead. +3. **TF-A `plat/intel/soc/agilex5` source.** Nobody in this project has read it. Q1 and Q5 both + depend on it. +4. **Altera's `socfpga-6.18.20-lts` and Terasic's `de25-nano-6.12.11-lts` board `.dts` files + node-by-node.** They were fetched and structurally surveyed (§4), not audited. §4.2's rule exists + because of that. +5. **Anything on hardware.** No DE25 board was touched. Every claim about what happens at runtime on + Agilex 5 silicon in this document is either inherited from the reference implementation's record + or is a prediction. §2.6 exists to convert the most important prediction into a measurement. diff --git a/docs/de25-nano-tasks.md b/docs/de25-nano-tasks.md index 06c3e25..27f9dd2 100644 --- a/docs/de25-nano-tasks.md +++ b/docs/de25-nano-tasks.md @@ -10,6 +10,37 @@ orchestration shape and the *smallest agent tier that can do the work honestly* over-tiering wastes tokens, under-tiering produces confident nonsense in exactly the places (boot flow, flash paths) where this board can be bricked. +> ## Execution status — 2026-08-22 +> +> **Phase D0 and Phase D1 are COMPLETE.** Everything below them is either done, hardware-gated, +> or framework-gated. Nine owner decisions were taken across 2026-08-19 → 2026-08-22 and are +> recorded in [`de25-implementation-path.md`](de25-implementation-path.md) §1 — read those before +> planning any D2 work; several of them foreclose options this task list still describes as open. +> +> | Task | State | Deliverable | +> |---|---|---| +> | D0.1 | **done** 2026-08-21 | [`de25-boot-chain.md`](de25-boot-chain.md) — §8 Q1–Q6 closed or parked; §9 refutation record | +> | D0.2 | **done** 2026-08-21 | [`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) — DP-9 **confirmed** | +> | D0.3 | **done** 2026-08-21 | [`de25-patch-portability.md`](de25-patch-portability.md) — all 40 patches | +> | D0.4 | **open** | not started; a `/schedule` routine, not a workflow | +> | D1.1 | **done** 2026-08-21 | [`de25-readiness-ledger.md`](de25-readiness-ledger.md) — 54 files, 65 rows | +> | D1.2 | **designed, NOT implemented** | design lives in the ledger §5; the code change is still owed | +> | D1.3 | **done** 2026-08-21 | forward-pointer sections in `downloader-contract.md` §13, `db-json-versioning.md` | +> +> **Two documents exist that this task list never anticipated**, both created 2026-08-22 from a +> third-party DE25-Nano that boots Linux to the MiSTer MENU (repos supplied by the owner as +> reference; nothing adopted): +> [`de25-reference-implementation.md`](de25-reference-implementation.md) — analysis of that board, +> partly salvaged from a spend-limited run and **partly unrefuted**, read its status header — and +> [`de25-implementation-path.md`](de25-implementation-path.md) — the settled implementation path: +> kernel 7.2, the minimal DTS node set, prior art, U-Boot shape, and the answer to how the fabric +> is reached from userspace. +> +> **The single biggest open risk is not in this task list:** binding the mainline FPGA-manager +> driver is proven, but *programming* the fabric through mainline `svc` is not, and one attempt on +> real silicon wedged a board. See `de25-implementation-path.md` §2.6 for the four-step hardware +> test that settles it. Run it before building anything that assumes it works. + **Agent tier legend** | Tier | Use for | Never for | @@ -43,12 +74,21 @@ field update looks like (the `updateboot` analogue — including whether QSPI is written and the recovery path if it's interrupted). Primary sources: DE25-Nano User Manual (DigiKey PDF P0804), Terasic System CD, Altera GSRD boot-example docs (rel-24.x/25.x), RocketBoards Agilex-5 bootloader doc, mainline U-Boot + ATF source. -- **Status 2026-08-19:** a desk-research first pass exists — +- **DONE 2026-08-21.** The doc is now 690 lines. Q1 resolved **[V]**: the SDM *cannot* boot from + the microSD on this board, so the QSPI seam is permanent. Q6 **[V]**: all DDR/pinmux handoff + lives inside the QSPI bitstream. Q5 **[V]**: `saveenv` writes back to wherever the env loaded + from, so a FAT miss lands in QSPI. The factory QSPI image is public and was downloaded and + hashed (`golden_top_hps.jic`, 16,777,447 B, sha256 `e3d20c2d…b38a4`, independently re-verified). + Q2/Q3/Q4 partial, parked with named blockers → D2.2. §7 survived a 3-lens refutation pass that + amended 8 of 22 claims and added 7 new brick vectors; §9 records it. + **Standing risk:** "no release writes QSPI" is enforced by prose only — `git grep` finds zero + board-identity assertion outside `docs/`. ADR 0027 Decision 4 is unimplemented. +- ~~**Status 2026-08-19:** a desk-research first pass exists — [`de25-boot-chain.md`](de25-boot-chain.md) (boot chain, QSPI/SD split, MSEL table, postures, brick inventory §7). **The task narrows to that doc's §8 Q1–Q6** (SDM-from-SD on this board incl. a Terasic inquiry, factory QSPI contents, FSBL→`u-boot.itb` contract, RSU sizing, env location, DDR-handoff coupling) plus the adversarial-verify - pass over §7's brick-risk claims, which have not yet survived refutation. + pass over §7's brick-risk claims, which have not yet survived refutation.~~ - **Orchestration (narrowed):** 1 `sonnet` leg per open Q (≤6, medium) → 1 `opus` synthesis into the doc → `fable` adversarial verify (3 refuters, high) on §7. ~10 agents. @@ -56,6 +96,10 @@ Manual (DigiKey PDF P0804), Terasic System CD, Altera GSRD boot-example docs (hardware-gated items hand off to D2.2); §7 survived refutation. ### D0.2 FPGA reconfig + shared-memory dossier → `docs/de25-fpga-reconfig.md` +- **DONE 2026-08-21.** **DP-9 CONFIRMED** — fpga-region/DT-overlay is the Agilex-native idiom and + the UIO doorbell patches 0043–0045 are not ported. Core-switching judged UX-viable at + low-to-moderate confidence (desk only). Later corrected by the 2026-08-22 pass: *binding is not + programming* — see the status block at the top of this file. How core loading would actually work: `stratix10-soc` FPGA manager + SDM mailbox path (read the in-tree driver), RBF formats, DT-overlay region flow, authentication/VAB requirements if any, and — the highest-value unknown — expected **full-reconfiguration @@ -86,8 +130,20 @@ provenance record cited). - **Accept:** a table with one row per patch, verdict + one-line rationale + provenance cite; totals reconciled against the plan's "~28 portable / ~8 board" estimate, with deltas explained. +- **DONE 2026-08-21**, but **not in the shape specified above.** On owner direction the flat + ~55-agent per-patch sweep was replaced with **triage-first**: 5 batched legs risk-rated all 40 + unique patches, only the RED (DE10/Cyclone-V-specific) set got a per-patch deep dive, plus one + cross-cutting leg tracing vsync / framebuffer / f2h_irq / audio / doorbells as *mechanisms* + rather than per-file. ~20 agents instead of ~55, and it fit the default size guideline. + The doc adds a second verdict per patch the original task never asked for — **target series** + (shared / de10-only / de25-only / drop) — serving the owner's standing goal of one repo building + both boards off a shared base. Inventory established: **40 unique basenames**; 4 beta-only + (0043–0046); 4 present in both series but **differing in content** (0001, 0015, 0030, 0037). + **Prefer this shape for any future large audit.** -### D0.4 Upstream watch (recurring; NOT an ultracode task) +### D0.4 Upstream watch (recurring; NOT an ultracode task) — **STILL OPEN** +- Worth doing sooner than it looks: Altera took the DE25-Nano board file in-house on its default + branch **11 days before we went looking**. This is exactly the signal class D0.4 exists to catch. Monthly brief: MiSTer framework/aarch64 port signals, Terasic BSP/System-CD releases, mainline `agilex5` movement, MiSTeX direction. Too small for a workflow — a scheduled routine (`/schedule`, monthly) with 2–3 web-search legs appending dated entries to @@ -96,7 +152,14 @@ not silently logged. ## Phase D1 — Readiness guards (no gate; opportunistic, cheap) -### D1.1 Coupling ledger → `docs/de25-readiness-ledger.md` +### D1.1 Coupling ledger → `docs/de25-readiness-ledger.md` — **DONE 2026-08-21** +- 54 files / 457 matching lines / 65 rows; 14 semantic blockers. Coverage reconciled against the + canonical grep. **Use `git grep` for the reconciliation**, not `grep -r`: the wrapper `grep` in + this environment honours `.gitignore` and returns 457 lines where GNU grep returns 15,796. +- **Three couplings ADR 0027's "four" did not anticipate:** `.github/workflows/lint.yml` hard-codes + ~20 board paths and **fails silently** (a DE25 tree goes unlinted behind a green check — this is + a live hole for the DE10 too); the `renovate.json` + `renovate-hash-sync.yml` bump axis (a new + defconfig added without them lands a stale kernel pin); `release.yml`'s 13 coupled lines. Inventory every hard-coded `de10nano` / `BR2_arm` / zImage-semantics site (survey found ~14 scripts, 4 CI files, 4 defconfig path lines, `external.mk`'s initramfs default) and write the per-file "when you touch this, do this instead" instruction. Code changes are @@ -107,7 +170,11 @@ introductions may be proposed as a follow-up diff for separate review. - **Accept:** ledger covers 100% of a fresh grep for `de10nano|BR2_arm|zImage` outside `board/mister/de10nano/` and `docs/`; each entry is actionable in one sentence. -### D1.2 Arch-assert generalization design (design only) +### D1.2 Arch-assert generalization design (design only) — **DESIGNED, NOT IMPLEMENTED** +- The design has no separate deliverable file; it lives as a section of + [`de25-readiness-ledger.md`](de25-readiness-ledger.md). It holds fail-closed for the DE10. +- **The code change is still owed, and D2.1 cannot land without it**: both guards hard-assert + `^BR2_arm`, so an aarch64 defconfig cannot pass them as they stand. Spec (not implement) the per-board expected-symbol tables for `scripts/check-kernel-defconfig-sync.sh` and the buildroot-build action's toolchain fingerprint, so DE25's defconfig lands against ready guards rather than weakened ones. @@ -115,18 +182,39 @@ fingerprint, so DE25's defconfig lands against ready guards rather than weakened ultracode; fine to run inline). - **Accept:** the design keeps both checks fail-closed for DE10 exactly as today. -### D1.3 Channel namespace reservation (docs only) +### D1.3 Channel namespace reservation (docs only) — **DONE 2026-08-21** +- `downloader-contract.md` §13 and a DE25 section in `db-json-versioning.md`. Cross-reference ADR 0027 §Decision-4's reserved names (tags `de25-YYYYMMDD`, `db-de25nano.json`, db_id, updater script name, board-identity assertion) into `docs/downloader-contract.md` (a short forward-pointer section) and `docs/db-json-versioning.md`. 1 `sonnet` agent, inline; no workflow. -## Phase D2 — Bring-up (gate: hardware in hand) — produces L1 +## Phase D2 — Bring-up (gate: hardware in hand **per task, not per phase**) — produces L1 Hardware-in-the-loop work does not fan out; ultracode's role in D2 is the *design and review* passes around each step, not the step itself. Every flash/QSPI-touching script gets a `fable` adversarial review before it ever runs on the board (rule 2). +> **Correction 2026-08-22 — the gate was drawn at the wrong level.** Reading it as +> "all of D2 waits for hardware" is wrong and was costing real progress. Several D2 tasks +> never touch a board; only their *validation* does: +> +> | Task | Needs hardware? | +> |---|---| +> | **D2.1** defconfig builds green | **No.** Its own accept criterion is `make` green *locally*. Blocked only on D1.2's implementation. | +> | **D2.3** DTS authoring | **Partly.** Authoring + `dtc` + `dtbs_check` now; only *booting* it needs the board. The node set is specified in `de25-implementation-path.md` §3.1 and prior art exists upstream. | +> | **D2.4** genimage cfg + check script | **Partly.** Both are writable now, plus `u-boot.itb` can be built and its shape verified with `dumpimage` against the SPL contract. Cold-flash boot needs the board. | +> | **D2.6** manual CI lane | **No.** | +> | **D2.7** DP-1 ADR | **No.** | +> | **D2.2 / D2.5 / D2.8** | **Yes** — UART bring-up, measured reconfig latency, published attested artifacts. | +> +> Also doable now and not listed as a task anywhere: compile-testing the D0.3 *shared*-series +> patches against aarch64/7.2 (turns desk verdicts into compile-verified ones — the cheapest +> de-risk available for D3.1), writing the two kernel patches decisions 8 and 9 imply, writing and +> compile-testing the ~100-line overlay-trigger driver, and extending +> `scripts/test-initramfs.sh` to an aarch64 `qemu-system-aarch64 -M virt` path so the +> initramfs / loop-root / overlay-services userland is exercised with no board at all. + | Task | Deliverable | Accept | Orchestration | |---|---|---|---| | D2.1 | `configs/mister_de25nano_defconfig` (aarch64, minimal rootfs) builds green locally | `make` green; defconfig-sync guards extended per D1.2, still green for DE10 | 1 `opus` implementer + `sonnet` helpers; `/code-review` after | @@ -173,13 +261,27 @@ its own evidence base. --- -## Suggested first invocation +## What to do next — 2026-08-22 -With no hardware and no framework, the only live work is D0 + D1. A reasonable single -opening move: +D0 and D1 are done; the opening move this section used to describe has been executed. The live +work now, in the order that unblocks the most: -> ultracode — run D0.1 and D0.2 from docs/de25-nano-tasks.md as two sequential -> workflows, then D1.1. Standing rules 1–3 apply; tag everything [V]/[U]. +1. **Implement D1.2** (the arch-assert generalization), then **D2.1** — the aarch64 defconfig built + green on 7.2. D2.1 is blocked on D1.2 and on nothing else. Local build; keep it off CI. +2. **Author the DE25 DTS** (D2.3's desk half) and validate with `dtc` + `dtbs_check`. Node set in + `de25-implementation-path.md` §3.1; harvest board wiring from Altera's in-house DE25 board file + but take `mmc0` in **SD4HC** form — `cdns,sd6hc` is nowhere in mainline and buys a carried + driver rewrite. +3. **Compile-test the D0.3 shared-series patches against aarch64/7.2.** Highest de-risk per unit + of effort; converts desk verdicts into compile-verified ones. +4. **An ADR for the nine owner decisions.** They are load-bearing and currently live only in + `de25-implementation-path.md` §1 — ADR 0027 predates all of them. +5. **Fix `lint.yml`'s silent failure** (ledger finding) — it affects the DE10 today. +6. **Stand up D0.4** as a `/schedule` routine. -D0.3 (⚠ size) is worth a dedicated turn with the size guideline raised. D0.4 is a -`/schedule` routine, not a workflow. +Sequencing note learned the hard way on 2026-08-21: when a research phase feeds a claim set that a +later phase must refute, run them **sequentially**, not in parallel. D0.1 was first launched with +its Q-legs and refuters concurrent; 17 of the 22 brick-risk claims turned out to be *generated by* +the Q findings, so the refuters would have attacked a claim set that no longer existed. Resequenced +and re-run, the refuters killed or amended 8. Parallelising a verify stage against the stage that +produces what it verifies is a false economy. diff --git a/docs/de25-reference-implementation.md b/docs/de25-reference-implementation.md new file mode 100644 index 0000000..b53ac86 --- /dev/null +++ b/docs/de25-reference-implementation.md @@ -0,0 +1,855 @@ +# DE25-Nano reference implementation — analysis of a working third-party board + +**Status: COMPLETE, closed out 2026-08-22.** *(Was: SALVAGED PARTIAL, 2026-08-21.)* + +This document was produced by a 9-agent ultracode run that was cut off by an account spend limit +after 4 of 9 agents completed. What survived was the raw output of four research legs, preserved +here rather than lost. + +**The two gaps are now closed, by a separate run on 2026-08-22:** + +1. **The missing Leg 4 (U-Boot deltas) has been done** — its findings are folded in as + [§7](#7-leg-4-u-boot--filled-in-2026-08-22) below. The QSPI **posture** half of that leg's brief + is **moot**: the owner has since settled the card layout (two partitions, p1 FAT, factory SPL + untouched, no QSPI writes from Linux), so there is no posture-1-vs-posture-2 decision left to + analyse. Leg 4's *technical* half — what we build, what the FIT must contain, and the one + defconfig line that keeps a QSPI write structurally impossible — is what §7 records. +2. **The adversarial verification pass has run**, over the claims this document's successor leans + on. [§8](#8-verification-record-2026-08-22) records exactly which claims were independently + checked, which survived, and which failed. **The document as a whole has still not been refuted + claim-by-claim** — §8 covers the load-bearing subset, not all of it. Anything not named in §8 + still carries the original single-agent standard, which is weaker than + [`de25-boot-chain.md`](de25-boot-chain.md), [`de25-fpga-reconfig.md`](de25-fpga-reconfig.md) or + [`de25-patch-portability.md`](de25-patch-portability.md). + +**Never ran, and no longer will:** the synthesis pass and the neutrality audit that was to strip +smuggled recommendations. Where a leg's wording drifts toward advocacy, that is why. The synthesis +this document was to feed now lives in +[`de25-implementation-path.md`](de25-implementation-path.md), which supersedes it as the +decision-bearing document; this file remains the **record of what the reference repos contain**. + +**Nothing here is adopted.** The owner's standing instruction governs: this is reference material, +and every divergence from our plan is his decision, not a recommendation. Where a leg's wording +drifts toward advocacy, that is an artifact of the missing neutrality pass. + +**Sources.** Two read-only reference repos, supplied by the owner 2026-08-21: +`/mnt/source/de25-linux` (kernel; `881d4404a`, tag `v2026.07.27-v618-kernel-port-f9-fb-fix-5`) +and `/mnt/source/de25-uboot-socfpga` (U-Boot **2025.01**, branch `de25-mister-exfat-boot`, +tag `v2026.07.13-uboot-hf-launcher`). Neither was executed, modified, or checked out. +Comparison baseline is our `output/build/linux-6.18.44`. + +**What he achieved, stated fairly:** a DE25-Nano booting Linux to the MiSTer MENU, with +`fpga_manager` and `fpga_region` probing on real hardware — further than any other work we know +of. His own `SETUP.md` §9 records that core switching is **not** wired up on his current card +layout, and the intended fast path is explicitly marked not end-to-end tested. + +--- + +## 1. The two symptoms — why cores were limited and slow + +The owner heard second-hand that only a certain number of cores could be loaded "into firmware" +and that loading was very slow, and suspected misconfiguration. Both are real, both are explained +by code in the reference repos, and **neither is a property of Agilex 5 SDM reconfiguration.** +Both come from the U-Boot/QSPI core-loading route — the route this project has already excluded. + +### (a) "Only a certain number of cores into firmware" + +"Firmware" was literal: **QSPI**. His first working core-switch tier put each core into the SDM's +configuration flash as an RSU application image and rebooted into it. Three independent caps +stacked up: + +1. **The slot map exhausts the 16 MiB device.** Hard-coded in three places — MiSTer's binary + carries the constants `0x00600000`, `0x00900000`, `0x00D00000` **[V]**; `de25_stage_core.sh:11-15` + documents them as P1 = MENU 3 MB (never staged), P2 = 4 MB, P3 = 3 MB, merged 2026-07-07 into a + single 7 MB staging slot "so >4 MB cores fit" **[V]**. P2 base `0x900000` + `0x700000` lands + exactly on `0x1000000` = 16 MiB **[V arithmetic]**. So: **MENU plus two — later one — cores + resident in firmware.** Every other core needed a full erase/write/verify. +2. **A ~2 MiB ceiling on the Linux live path.** Not folklore: mainline `stratix10-soc.c:19-20` + allocates `NUM_SVC_BUFS 4 × SVC_BUF_SIZE SZ_512K` = exactly **2 MiB in flight**, and + `s10_ops_write()` cannot proceed past that without `SVC_STATUS_BUFFER_DONE` callbacks recycling + buffers **[V]**. His note attributes the stall to his own BL31 modification + (`FPGA_CONFIG_BUFFER_SIZE=16`) killing the reply that drives recycling **[U — the ATF source is + in neither repo]**. With 5 of his 8 cores under 2 MB and 3 over, "only some cores load" is + literally what a user would have seen **[V]**. +3. **Only eight cores have ever been rebuilt for A5EB013** (`CORES.manifest`) — cores are + device-specific and must be recompiled **[V]**. + +> **This one matters for us.** Cap 2 is *mainline* code, and our own cores will be 1.92–3.65 MB — +> straddling that 2 MiB in-flight window. Buffer recycling working correctly is therefore a +> load-bearing assumption for any Linux-side core loading we build, and belongs in D2.5's +> measurement plan rather than being assumed. + +### (b) "Very slow to load cores" + +Every path he actually shipped is dominated by something that is not reconfiguration: + +| Path | Dominant cost | Evidence | +|---|---|---| +| RSU / QSPI rewrite | `de25_fcs_stage_core.py` pushes the `.rpd` through the FCS→SDM mailbox in **4096-byte writes with a 0.05 s settle each**, re-asserting chip-select per write, then reads the slot back for an md5 verify in 4 KiB calls. For a 3.35 MB core that is **≥817 round-trips and ≥41 s of `sleep()` alone** before mailbox cost — then a reboot. His own script calls it "the ~minutes-long FCS rewrite". | `:65-70,95-107`; `de25_stage_core.sh:72` **[V]** | +| U-Boot launcher | No QSPI write, but a **warm reboot** — MiSTer's OSD string is `"Loading core... (board reboots ~30s)"`; his hook comments say ~35 s. On this SoC every reset is a full SDM configuration cycle with no warm/cold distinction. | **[V]** | +| Kernel `fpga_manager` + DT overlay (the intended fast path) | "~8 s total", "NES 3,325,952 B live-switches in ~3 s" | comment **[V]**, but see below | + +**The fast path is the one he never proved.** Commit `881d4404a` (2026-08-01) records that the +script wrote to `/sys/kernel/config/device-tree/overlays/fullcore` — a configfs path that does not +exist in his mainline-based 6.18 tree — and states the replacement (the ~95-line misc driver over +`of_overlay_fdt_apply()`, `30d9c99a8`) **"has NOT yet been end-to-end tested"** **[V]**. MiSTer's +Tier A/B binaries still target the same dead configfs paths **[V strings]**. + +### Verdict for our D0.2 conclusion + +His experience **neither confirms nor undermines** our low-to-moderate-confidence finding that +core switching is UX-viable. He has never measured Agilex 5 reconfiguration on a working Linux +overlay path. What he measured is QSPI rewriting (minutes) and reboots (~30–35 s). + +One genuinely intrinsic floor does appear, from two independent code paths: U-Boot's SDM driver +sleeps `udelay(1000000)` before its first `RECONFIG_STATUS` poll and then polls at 1 s intervals +**[V]**, mirroring the kernel service layer's `msleep(1000)` that our `de25-fpga-reconfig.md` §6.1 +row 7 already flagged. **Expect a ~1 s quantization floor per full reconfiguration on this +silicon.** + +--- + +## 2. What his work settles for us + +Our five open unknowns, as answered by two independent legs. Where the legs differ in emphasis, +both readings are given — there was no synthesis pass to reconcile them. + + +### From LEG 1 — Device tree (de25-linux @ 881d4404a, tag v2026.07.27-v618-kernel-port-f9-fb-fix-5-g881d4404a) vs mainline linux-6.18.44 + +**[V] U1 — Do mainline's intel,agilex-svc / intel,agilex-soc-fpga-mgr compatibles bind on Agilex 5?** + +No. Mainline 6.18.44's match tables contain only stratix10 and agilex (gen1) strings, and the Agilex 5 DTS declares intel,agilex5-svc / intel,agilex5-soc-fpga-mgr. Two one-line of_device_id additions fix it, and BOTH are required (svc is built-in and matches at __init, so the mgr cannot be created until svc binds). Separately, mainline 6.18.44's socfpga_agilex5.dtsi instantiates no svc/fpga-mgr/fpga-region node at all, so the DTS content must be supplied too. He reports fpga0/region0 binding on real hardware after the patch. + +*Source:* `linux-6.18.44 drivers/firmware/stratix10-svc.c:1133-1137, drivers/fpga/stratix10-soc.c:448-452; /mnt/source/de25-linux commit d1878a320; /mnt/source/de25-linux/.../socfpga_agilex5.dtsi:213-243` + +**[V] U2 — Is there a usable DT-overlay path for reconfiguration?** + +Yes, but only via of_overlay_fdt_apply() from kernel code. The configfs device-tree path (/sys/kernel/config/device-tree/overlays/) does not exist upstream and never worked on his kernel — his switch script had been failing structurally on it for its whole history. Mainline fpga_manager sysfs is read-only (no writable firmware attribute), so he wrote a 95-line misc driver whose one write-only sysfs attribute re-applies a static .dtbo naming firmware-name=core.rbf under /fpga-region. The mechanism is HW-proven to reach the real reconfigure path; the one test attempt then failed at the SDM mailbox ('timeout waiting for RECONFIG_REQUEST') and wedged the board. This directly answers our fpga-reconfig §11 row 6 (carry OF_CONFIGFS vs write a board driver vs U-Boot-preload): OF_CONFIGFS is not an option, and he chose 'write a board driver'. + +*Source:* `/mnt/source/de25-linux commits 30d9c99a8 and 881d4404a; drivers/misc/de25_fpga_trigger.c; analysis/full_config/de25_full_core.dtso` + +**[U] U3 — Does the factory FSBL boot a mainline-built u-boot.itb?** + +Not answered, and this leg found no evidence either way. He flashes his own exFAT-aware SPL in QSPI (posture 2), so the factory-FSBL path is never exercised in his tree. Nothing in the kernel repo bears on it. Treat as still open. + +*Source:* `absence of evidence in /mnt/source/de25-linux (kernel repo only); his SETUP.md §3 boot chain` + +**[U] U4 — Is bitstream authentication (VAB) enforced out of the box?** + +Not settled by the DTS, but two adjacent facts: his dtsi instantiates the full Intel FCS subtree (fcs-hal 'intel,agilex5-soc-fcs-hal', fcs-crypto, fcs-config) and his overlay ships plain unsigned .rbf/.rpd payloads which the SDM accepts, with the only rejection documented being a wrong-recipe RSU magic (0xa9129446 vs the expected 0x95482962), not a signature failure. That is consistent with VAB not being enforced on this board, but it is inference, not a read of a fuse or a config bit. + +*Source:* `/mnt/source/de25-linux/.../socfpga_agilex5.dtsi:232-253; board_overlay/usr/local/bin/de25_stage_core.sh:42-58` + +**[V] U5 — Real reconfiguration latency** + +His numbers, from source comments rather than a log I read: a 3,325,952-byte NES core live-switches in ~3 s of config time, ~8 s end-to-end including a mandatory 3 s pre-quiesce settle and a mandatory 5 s post-config settle; the reboot fallback path is ~35 s. Both settle delays are HW-forced, not padding: trimming the pre-quiesce to 1 s reproducibly wedged the board on the NEXT switch, and any LWH2F access inside the post-config window hangs a CPU on the AXI bus with no timeout (RCU stall, warm reset comes back dark). So ~8 s, of which ~5 s is unavoidable dead time, is the honest UX figure for his design. + +*Source:* `/mnt/source/de25-linux/board_overlay/usr/local/bin/de25_live_switch_core.sh:7-12, 81-93, 123-132; de25_launch_core.sh:8-18` + +**[V] Our fpga-reconfig §11 row 14 — Does the Agilex 5 svc node need iommus = <&smmu 10>?** + +His does, and it also carries altr,smmu_enable_quirk on both the svc node and the fpga-mgr child. He supplies the smmu node himself (mainline 6.18.44 has none) and enables it from the board DTS. He did not test the no-iommus form, so 'needs' is not proven — but the working configuration is known. + +*Source:* `/mnt/source/de25-linux/.../socfpga_agilex5.dtsi:213-226, 461-471; socfpga_agilex5_de25_nano.dts:182-184` + +**[V] DP-10's residual question (is the display pipeline HPS-reachable?)** + +Corroborated as no. His DTS has no display, DRM, HDMI or I2C-transmitter node whatsoever; Linux's only display device is MiSTer_fb writing a DRAM region the fabric scaler reads. He also enables exactly one HPS I2C bus with no children, which means the DE10's ADV7513-on-/dev/i2c-0..2 discovery contract cannot work on this board. + +*Source:* `full read of /mnt/source/de25-linux/.../socfpga_agilex5_de25_nano.dts; /mnt/source/Buildroot_MiSTer/docs/de25-fpga-reconfig.md:696-727` + + +### From Leg 2 — Kernel Deltas + +**[V] U1 — Do intel,agilex-svc/soc-fpga-mgr compatibles bind on Agilex 5?** + +No — mainline's stratix10-svc.c/stratix10-soc.c need an added 'intel,agilex5-svc'/'intel,agilex5-soc-fpga-mgr' of_device_id entry each (his commit message: this gap exists in current mainline too). His DTS already declares those compatibles and a fpga-region node inherited unmodified from his kernel base — but our own linux-6.18.44 baseline lacks those same DTS nodes entirely, an unresolved discrepancy between two nominally-mainline-descended point releases. + +*Source:* `de25-linux commit d1878a320; both repos' socfpga_agilex5.dtsi` + +**[V] U2 — Is there a usable DT-overlay reconfig path?** + +Yes, mechanism confirmed on real hardware: apply a DT overlay with a firmware-name property via of_overlay_fdt_apply(); of-fpga-region.c's own notifier drives the real reconfigure. The RPi-style OF-configfs path some earlier code assumed was never upstream and always failed. Reliability under real SDM mailbox timing is NOT yet demonstrated — one live test hit a 300ms RECONFIG_REQUEST timeout and wedged the board. + +*Source:* `de25-linux commits 30d9c99a8, 881d4404a` + +**[U] U3 — Does the factory FSBL boot a mainline-built u-boot.itb?** + +Not established — he replaced the SPL with his own exFAT-aware build and there is no evidence in SETUP.md or the repo that the stock factory FSBL was ever tested against any other u-boot.itb. + +*Source:* `/mnt/source/de25-linux/SETUP.md (absence of any such test)` + +**[V] U4 — Is VAB (bitstream authentication) enforced out of the box?** + +No — CONFIG_SPL_FIT_SIGNATURE=y is set (FIT image signing) but CONFIG_SOCFPGA_SECURE_VAB_AUTH is not set anywhere in the defconfig, board header, or board directory. + +*Source:* `/mnt/source/de25-uboot-socfpga/configs/socfpga_agilex5_de25_nano_defconfig and related files (grep, both symbols)` + +**[V] U5 — Real reconfiguration latency.** + +No clean success-path number obtained. Working path (reboot) measured at ~35s. Fast path (live DT-overlay) targets ~8s but is unverified end-to-end; the one real hardware attempt at it hit the hardcoded 300ms SDM RECONFIG_REQUEST timeout and wedged the board on resume — empirical (if limited, n=1, unhardened-driver) evidence that the timeout-tightness risk our own D0.2 dossier already flagged on paper is real on Agilex 5 silicon. + +*Source:* `de25-linux commit 30d9c99a8; board_overlay/usr/local/bin/de25_live_switch_core.sh, de25_launch_core.sh` + + +### From Leg 3 — the two symptoms ("only a certain number of cores into firmware", "very slow to load cores"), mechanism-level explanation from the reference repos + +**[V] U1 — Do mainline's 'intel,agilex-svc' / 'intel,agilex-soc-fpga-mgr' compatibles bind on Agilex 5?** + +Not as shipped, for two independent reasons, and he fixed both. (i) DT: mainline 6.18.44's socfpga_agilex5.dtsi instantiates no svc, fpga-mgr or fpga-region node at all — only the svcbuffer reserved-memory. He authored all three: a firmware/svc node with compatible 'intel,agilex5-svc' (method smc, memory-region svcbuffer, iommus <&smmu 10>, altr,smmu_enable_quirk, GIC_SPI 0), a child fpga-mgr 'intel,agilex5-soc-fpga-mgr', and a top-level fpga-region with fpga-mgr = <&fpga_mgr>. (ii) Drivers: neither stratix10-svc.c nor stratix10-soc.c matches an agilex5 string in 6.18.44 — his two-line patch adds one entry to each match table. His commit message records that both were required together: fixing only the fpga-mgr left the mailbox failing with "couldn't get service channel (fpga)", because the SVC node binds at __init before its children can be created. HW-confirmed on the board: /sys/class/fpga_manager/fpga0 and /sys/class/fpga_region/region0 exist and 'of-fpga-region fpga-region: FPGA Region probed' appears in dmesg — his words, 'the first time this has worked on this board'. Both match tables have no per-compatible .data, so the third entry is functionally free. NOTE: this is the fix an already-posted Altera series does upstream with 'intel,agilex-soc-fpga-mgr' as the declared DT fallback (our §3.1) — he did not use the fallback route, he added the strings. + +*Source:* `/mnt/source/de25-linux commit d1878a320; arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi:213-244 (added in bf8e5c9c6); diff vs /mnt/source/Buildroot_MiSTer/output/build/linux-6.18.44/drivers/{firmware/stratix10-svc.c,fpga/stratix10-soc.c} and .../dts/intel/socfpga_agilex5.dtsi` + +**[V] U2 — Is there a usable DT-overlay path for reconfiguration?** + +Yes, and our §3.2 option table is now settled empirically rather than by inference. Option (a) is real and is what the vendor ships: Terasic's stock DE25 kernel config (6.12.11) has CONFIG_OF_CONFIGFS=y — so the /sys/kernel/config/device-tree/overlays/ workflow every Altera document describes works verbatim on the vendor kernel, and its provenance as a vendor carry is no longer [U]. Option (b) is real and now has a working reference implementation: his drivers/misc/de25_fpga_trigger.c, ~95 lines, request_firmware()s a static .dtbo and calls the mainline exported of_overlay_fdt_apply(); of-fpga-region's overlay notifier then calls fpga_region_program_fpga(). HW-tested to the point of 'fpga_manager fpga0: writing de25_live_switch_current.rbf to Stratix10 SOC FPGA Manager' in dmesg. Two caveats: his design re-applies a STATIC overlay naming a FIXED firmware path and stages the chosen core to that path first (avoiding rebuilding a DTB per switch), and his end-to-end integration is still untested. His confirmation that mainline fpga_manager class devices expose no writable 'firmware' attribute — only RO name/state/status — matches our §2.6 'there is no second door'. + +*Source:* `/mnt/source/de25-linux/analysis/smc_bridge/board_kernel.config:2246; commits 30d9c99a8 and 881d4404a` + +**[U] U3 — Does the factory FSBL boot a mainline-built u-boot.itb?** + +NOT ANSWERED, and he is structurally unable to answer it: he replaced the SPL (his exFAT-aware SPL is inside his own .jic) precisely because the factory one cannot read exFAT. What his repo does settle is the baseline: the u-boot.itb he pulled off the board identifies as '2025.01-gd0f8813fd6bf' with vendor=terasic — Terasic shipped a build of this very tree at the 'add support for de25-nano' commit, confirming U-Boot 2025.01 + a board commit that is public. His own build carries the identical version string, so he never moved off 2025.01 either. Two facts sharpen our Q3: his defconfig sets CONFIG_SPL_FIT_SIGNATURE=y (as mainline's agilex5 defconfig does) yet he loads locally built, unsigned FITs — consistent with our finding that the factory SPL's control DTB requires no keys; and the SPL FIT load address contract is visible (CONFIG_SPL_LOAD_FIT_ADDRESS=0x82000000, CONFIG_SYS_SPI_U_BOOT_OFFS=0x04000000). + +*Source:* `/mnt/source/de25-uboot-socfpga/configs/socfpga_agilex5_de25_nano_defconfig; strings of /mnt/source/de25-linux/analysis/uboot_board_orig_pulled.itb; SETUP.md §3` + +**[V] U4 — Is bitstream authentication (VAB) enforced on this board out of the box?** + +Strong practical evidence that it is NOT — but not from a stock board. He loads locally built, unsigned .uboot.rbf files into the fabric both from U-Boot ('fpga load' + 'FPGA reconfiguration OK!') and from Linux via the fpga_manager, across at least eight cores, with no signing step anywhere in the repos; grep for VAB/QKY/efuse/authentication over his scripts, docs and configs returns nothing relevant. His defconfig does not set CONFIG_SOCFPGA_SECURE_VAB_AUTH (upstream keeps that in a separate socfpga_agilex5_vab_defconfig). The caveat is real and unremovable: he flashed his own .jic over the factory QSPI, so this is evidence about HIS device state, not proof about a factory-fresh DE25. It does establish that the A5EB013 silicon in front of him is not efuse-locked, which is the half of the question that would have been fatal. + +*Source:* `/mnt/source/de25-uboot-socfpga/configs/socfpga_agilex5_{de25_nano,vab}_defconfig; grep over /mnt/source/de25-linux; SETUP.md §3` + +**[U] U5 — Real reconfiguration latency.** + +STILL UNMEASURED on a working Linux overlay path, and this leg cannot close it. The only in-repo numbers are comment/commit prose from the 6.12.11-era configfs path — '~3 s config' for a 3,325,952 B NES core, '16-18 s' for an a2600<->NES round trip, '~8 s' claimed live-switch total — and the referenced backing artifacts (analysis/SUB10S_ATF_KERNEL_DIVERGENCE.md, the memory/*.md notes, com9_*_throttle_validate.log) are NOT in the shared repo, so none is verifiable here. What IS new and verifiable are three real inputs to our §6.2 arithmetic: (1) actual MiSTer-scale Agilex 5 bitstream sizes for the A5EB013 die — 1.92 MB (menu) to 3.65 MB — which lands at the top of our 1-4 MB guess; (2) a second, independent ~1 s poll quantization, in U-Boot's intel_sdm_mb.c, matching the kernel service layer's msleep(1000) (our §6.1 row 7); (3) hard evidence that reconfiguration cost is NOT the dominant term in a perceived switch — his own design spends >=3 s quiescing and 5 s settling around it, both for HW-proven safety reasons. + +*Source:* `/mnt/source/de25-linux/board_overlay/media/fat/cores/CORES.manifest; de25_live_switch_core.sh:7-12,74-92,120-131; /mnt/source/de25-uboot-socfpga/drivers/fpga/intel_sdm_mb.c:20-21,1060-1070` + + +### From Leg 5 — platform gotchas, secure-boot, and the two symptoms (plus U1–U5 resolution) + +**[V] U1 -- Do mainline's intel,agilex-svc / intel,agilex-soc-fpga-mgr compatibles bind on Agilex 5?** + +No, not as-is on this kernel generation: neither string matched an agilex5-specific DTS compatible in his tree, and mainline 6.18.44 has no svc/fpga-mgr/fpga-region node in socfpga_agilex5.dtsi at all (confirms D0.2 §3.1's absence finding). His fix -- adding explicit intel,agilex5-svc / intel,agilex5-soc-fpga-mgr match-table entries -- is a small, zero-risk kernel patch, and is HW-confirmed to bind and probe. This independently corroborates Altera's own upstream A5-series patch, which adds the identical fpga-mgr string. + +*Source:* `de25-linux commit d1878a320` + +**[V] U2 -- Is there a usable DT-overlay path for reconfiguration?** + +Yes, and it is the only usable path -- but the /sys/kernel/config/device-tree/overlays/ configfs workflow every vendor doc describes never existed in his kernel (confirmed against kernel.org: no CONFIG_OF_CONFIGFS subsystem upstream), matching D0.2 §3.2 exactly. He built a small (~90-line) misc-device driver calling the exported of_overlay_fdt_apply() directly, HW-confirmed to reach the real fpga_manager write path via of-fpga-region.c's own notifier. + +*Source:* `de25-linux commits 30d9c99a8, 881d4404a` + +**[U] U3 -- Does the factory FSBL boot a mainline-built u-boot.itb?** + +Untested. He replaced Terasic's factory SPL with his own exFAT-aware SPL before the board was ever brought up in this repo's recorded history; no commit or doc anywhere in his tree exercises the unmodified factory FSBL against any FIT image, mainline-built or otherwise. This remains a genuinely open question for our posture-1 plan. + +*Source:* `de25-uboot-socfpga commit history d0f8813fd6->096da9d4bb->38eff87fc0; absence confirmed by full log review` + +**[V] U4 -- Is bitstream authentication (VAB) enforced on this board out of the box?** + +No, on his specific unit: his defconfig never sets CONFIG_SOCFPGA_SECURE_VAB_AUTH (confirmed against the Kconfig, which shows that symbol lives only in separate *_vab_defconfig files), and his board repeatedly boots a self-built, unsigned .jic via JTAG all the way to a running MiSTer MENU -- the strongest practical evidence available that VAB is unprovisioned on this board class. This is single-unit evidence, not a guarantee about every DE25-Nano's eFuse state. + +*Source:* `de25-uboot-socfpga configs/ + arch/arm/mach-socfpga/Kconfig:27; de25-linux SETUP.md §3 and full working-boot history` + +**[V] U5 -- Real reconfiguration latency** + +One real number now exists, single-source and self-reported: a 3.3MB core's SDM reconfiguration window is ~3s (post-BUF4-fix), and the full quiesce->overlay->settle->service-restart worker completes in ~8s regardless of core size, with a documented a2600<->NES round trip of 16-18s. This lands inside D0.2 §6.2's estimated range and is the first hardware corroboration of it, but it is not independently reproduced and the pre-fix pathology (an SDM/ATF buffer-reclaim bug, not a fundamental SoC limit) shows how easily a latency measurement can be dominated by a project-specific bug rather than the platform. + +*Source:* `de25-linux board_overlay/usr/local/bin/de25_live_switch_core.sh header + commit 7fb52ac3f` + + +--- + +## 3. Platform gotchas we inherit regardless of design choices + +These are properties of the SoC and the board, not of his design decisions, so our port meets them +whichever posture we choose. + + +### Mainline 6.18's Agilex 5 DTSI has no SD/MMC controller node and no SMMU node. A board DTS that references &mmc or &smmu is a dtc compile error; without an mmc node the board cannot boot from SD at all. + +- **Evidence:** grep -i 'mmc|sd4hc|smmu|iommu' over /mnt/source/Buildroot_MiSTer/output/build/linux-6.18.44/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi returns nothing; the same nodes appear as + hunks in his fork at :384 and :461 +- **His fix:** Back-ported mmc0@10808000 ('intel,agilex5-sd4hc','cdns,sd4hc', iommus=<&smmu 5>, dma-coherent) and smmu: iommu@16000000 ('arm,smmu-v3') from Terasic's 6.12.11-LTS tree into the dtsi (bf8e5c9c6). +- **Applies to us:** Unavoidably. Any DE25 port we do needs these nodes from somewhere. The only open choice is which file they live in and whether we try to upstream them. + +### Mainline 6.18's own gmac0 node for Agilex 5 does not work on real DE25 hardware — ethernet never probes. + +- **Evidence:** /mnt/source/de25-linux commit e57c54c74: -EIO 'Cannot register the MDIO bus', preceded by 'Version ID not available' / 'No HW DMA feature register supported' on every boot +- **His fix:** Replaced the mainline node (compatible altr,socfpga-stmmac-agilex5, one macirq, reset-names stmmaceth/ahb) with Terasic's full node: altr,socfpga-stmmac-a10-s10 + 17 per-queue IRQ vectors, snps,multi-irq-en, snps,pblx8, altr,smtg-hub, iommus=<&smmu 1>, dma-coherent, and reset-names stmmaceth/stmmaceth-ocp. He identifies the concrete mechanism: dwmac-socfpga.c acquires the OCP reset exclusively by the name 'stmmaceth-ocp', which mainline's 'ahb' naming never satisfies. +- **Applies to us:** Yes, verbatim — this is a property of mainline's DTS vs this silicon, independent of any design choice of ours. It also means mainline's Agilex 5 ethernet support is untested on hardware and we should not trust other mainline Agilex 5 nodes by default. + +### With the SMMU enabled, any DMA-capable peripheral missing an iommus stream ID faults on every transfer. + +- **Evidence:** /mnt/source/de25-linux commit 36f39d30d: usb0 without iommus=<&smmu 6> produced AHB ERROR on every dwc2 channel; lsusb showed only root hubs +- **His fix:** Added iommus = <&smmu 6> to usb0. Stream IDs in use in his tree: gmac0=1, mmc0=5, usb0=6, svc=10. +- **Applies to us:** Yes, if we enable the SMMU. The failure mode is a device that enumerates nothing rather than a probe error, so it is easy to misdiagnose as a USB or PHY problem. + +### mmc0 DMA through the SMMU F_TRANSLATION-faults on 6.18 but not on 6.12, with a byte-identical devicetree. ~~It is a kernel regression, not a DT gap.~~ — **the FAULT is confirmed; the "kernel regression" ATTRIBUTION is CORRECTED, 2026-08-22** + +> **Correction (2026-08-22).** The **fault is real and well-evidenced** — commit `716559020`'s +> retract-of-a-retraction stands, verified [V]. What does **not** hold is calling it a *kernel-version +> regression*. The "working 6.12" baseline is **Terasic's vendor tree**, not mainline, and its +> `sdhci-cadence.c` gives `intel,agilex5-sd4hc` a dedicated match entry carrying +> **`SDHCI_QUIRK2_40_BIT_DMA_MASK`** (`terasic/linux-socfpga@de25-nano-6.12.11-lts: +> drivers/mmc/host/sdhci-cadence.c:783-786,952-953`) **[V, fetched 2026-08-22]**. That quirk **exists +> nowhere in mainline** — zero hits in `drivers/mmc/` and `include/` at 6.18.44, and zero in +> `sdhci.h` at v7.2 and `master` **[V]**. His 6.18 tree binds the bare `cdns,sd4hc` entry with a +> byte-identical-to-mainline driver **[V `diff -q`]**, so mainline's +> `sdhci_set_dma_mask()` takes the 64-bit branch (`DMA_BIT_MASK(64)`, `sdhci.c:4117-4123`) **[V]** on +> a controller the vendor deliberately caps at 40 bits. Under SMMU translation, top-down IOVA +> allocation above the controller's wired address bits truncates — exactly an `F_TRANSLATION` +> "input address caused fault". **The DT was byte-identical; the DRIVERS were not.** Mainline 6.12 +> was never tested and could not have been (no Agilex 5 clk driver in mainline before v6.19). +> +> **Consequence for us, and it is the opposite of what this section originally implied:** the fault +> does **not** disappear on 6.19 or 7.2. It travels with mainline `sdhci-cadence` + SMMU on every +> version. It is therefore **not** an argument for a newer kernel, and it has a cheap +> mainline-first remedy to test: a one-entry upstream `sdhci-cadence` patch adding +> `intel,agilex5-sd4hc` with a 40-bit mask. See +> [`de25-implementation-path.md`](de25-implementation-path.md) §5.1 and §8 Q2. + +- **Evidence:** /mnt/source/de25-linux commit 716559020: the earlier 'false alarm / warm-reboot artifact' conclusion is retracted; the 'clean' retest had sdhci.debug_quirks=0x60 silently inherited from a JTAG-recovery script, which forces PIO and never exercised the faulting path. A genuine power-cycle through the unmodified production boot script reproduced it decisively. +- **His fix:** sdhci.debug_quirks=0x60 baked into the production boot.scr.uimg — forces SDHCI into PIO instead of ADMA. Root cause deliberately not bisected. +- **Applies to us:** Yes, and on **every** mainline kernel version, not just 6.18 (see the correction box). The mitigation costs the entire SD DMA path. This is also a methodology warning we should adopt: do not test SMMU-adjacent behaviour on this board via a warm reboot, and audit your own test harness's default bootargs before declaring a fault gone. A third methodology lesson, added by the 2026-08-22 pass: **a "byte-identical devicetree" isolates nothing when the two kernels are a vendor tree and a mainline tree** — diff the drivers before attributing a fault to a version. + +### Touching the LWH2F window too soon after config-complete hangs a CPU on the AXI bus with no bus timeout — RCU stall, and a warm reset from that state brings the board back DARK (power-cycle only). + +- **Evidence:** /mnt/source/de25-linux/board_overlay/usr/local/bin/de25_live_switch_core.sh:123-132, described as 'HW-proven 2026-07-13, the hard way' +- **His fix:** Mandatory 5 s settle after config-complete before any fabric access, and a mandatory 3 s settle before starting a new reconfigure (trimming that to 1 s reproducibly wedged the next switch). Both are dead time in every core switch. +- **Applies to us:** Yes if we do live reconfiguration on this SoC. It sets a hard floor of several seconds on switch latency that no software optimisation removes, and it means any 'is the fabric up?' probe must be gated, not speculative. + +### On an unconfigured fabric, a raw MMIO read of the fabric sysid is a FATAL async SError — a kernel panic, not a recoverable probe failure. + +- **Evidence:** /mnt/source/de25-linux/board_overlay/usr/local/bin/de25_fabric_ready.py (HPS-First guard comment, 'HW-proven on the first Stage-1 HPS-First boot') +- **His fix:** A guard script that refuses the MMIO probe unless a U-Boot-supplied de25_core= breadcrumb names a configured core, or fpga0/state reads 'operating' after a kernel-driven config. Note his finding that fpga0/state reads 'unknown' for a U-Boot-configured fabric, so it cannot be used as a universal signal. +- **Applies to us:** Yes. Any Main_MiSTer aarch64 HAL that probes the fabric the way DE10's does will panic the kernel on an unconfigured board. This is a hard constraint on how L0's HAL discovers the fabric. + +### The configfs device-tree overlay path does not exist in upstream Linux and never will as things stand. + +- **Evidence:** /mnt/source/de25-linux commits 881d4404a and 30d9c99a8: 'confirmed against kernel.org: the RPi-style drivers/of/configfs.c subsystem this assumed was never upstream'; his defconfig even carries an inert CONFIG_OF_CONFIGFS=y line for a symbol that does not exist +- **His fix:** Wrote a misc driver that calls of_overlay_fdt_apply() from kernel context. +- **Applies to us:** Yes — it removes one of the three options our fpga-reconfig §11 row 6 was going to cost out. Also a caution: an inert CONFIG_ symbol in a defconfig produces no warning and no behaviour. + +### With CONFIG_STRICT_DEVMEM=y, ordinary System RAM cannot be mmap'd via /dev/mem; only no-map reserved regions can. + +- **Evidence:** /mnt/source/de25-linux commit 9454a984d; arch/arm64/configs/de25_defconfig:12312 +- **His fix:** A no-map reserved-memory node over the AO486 0xB0000000 window. He notes the failure was silent and cost several investigation sessions — Main released the x86 CPU over a never-staged BIOS and the core triple-faulted. +- **Applies to us:** Yes, for any Main_MiSTer shared-memory window, if we keep STRICT_DEVMEM. Note our DE10 config sets '# CONFIG_STRICT_DEVMEM is not set' (board/mister MiSTer_defconfig), so this is a divergence we would inherit only if the DE25 config turns it on. + +### ~~drivers/input/mousedev.c is missing #include ~~ — **REFUTED by the main session, 2026-08-21** + +> **This claim is wrong and is retained only as a worked example of why this document's +> missing adversarial pass matters.** Checked directly: `output/build/linux-6.18.44/drivers/input/mousedev.c` +> has no `#include ` and needs none — there is **no `compat_ptr()` call**. The only +> occurrence of that token is inside a comment at `:891-893` explaining why translation is +> *unnecessary*: "EVIOCGRAB's argument is a truth value, not a pointer, so the compat entry point +> needs no `compat_ptr()` translation." Our own patch `0026-input-mousedev-eviocgrab.patch:461-464` +> adds exactly that construct, deliberately. The agent appears to have pattern-matched the word +> `compat_ptr` in prose. **There is no build error on arm64.** +> +> What *is* true and worth keeping: our patch's own note (`:85`) records that `CONFIG_COMPAT` is +> off on 32-bit ARM, so this `.compat_ioctl` path has **never been exercised** on the DE10. On +> aarch64 with `CONFIG_COMPAT=y` it becomes live for the first time. The patch author already +> reasoned about the safety (truth value, not pointer), so this is a review item, not a defect. + +- **Evidence:** de25-linux commit 9a3e73919; CONFIG_COMPAT=y confirmed in his defconfig +- **His fix:** One-line #include add. +- **Applies to us:** Any aarch64 DE25 defconfig of ours that sets CONFIG_COMPAT=y (likely, for any 32-bit compat needs) will hit the identical implicit-declaration build error and need the identical one-line fix — trivial but must not be missed. + +### stratix10-svc.c / stratix10-soc.c of_device_id tables lack 'intel,agilex5-svc' / 'intel,agilex5-soc-fpga-mgr' entries — a genuine mainline gap for this SoC family, not board-specific. + +- **Evidence:** de25-linux commit d1878a320, explicitly stated to reproduce on current mainline too +- **His fix:** Add one compatible-string line to each driver's match table (zero functional risk, no .data/branching on the tables). +- **Applies to us:** Any DE25 board work using mainline-flavored kernel sources needs this same two-line fix before /sys/class/fpga_manager or /sys/class/fpga_region will bind at all. + +### There is no writable userspace attribute on fpga_manager class devices in current mainline, and the RPi-style OF-configfs overlay subsystem was never upstreamed — any design assuming a configfs-triggered reconfigure path will fail structurally, not intermittently. + +- **Evidence:** de25-linux commits 30d9c99a8, 881d4404a, explicit statement 'confirmed against kernel.org' +- **His fix:** A custom misc-device kernel module applying a static prebuilt DT overlay via of_overlay_fdt_apply(), relying on of-fpga-region.c's own notifier. +- **Applies to us:** Any of our own reconfigure design must go through the DT-overlay + fpga-region-notifier path (or write our own trigger like his), not a configfs write. + +### Agilex 5 Rev A silicon requires a runtime-detected, non-optional Intel-upstream FPGA-reconfig workaround (register-gated, not a defconfig symbol), dated 2023 and explicitly scoped to Rev A only. + +- **Evidence:** de25-uboot-socfpga commit 2030244a1e, confirmed ancestor of his branch +- **His fix:** Already inherited automatically via mainline U-Boot; no action needed beyond using a U-Boot new enough to carry it. +- **Applies to us:** If our target boards are Rev A Agilex 5 silicon (needs confirming against our own board's stepping), we inherit this same requirement for free from any modern U-Boot — but must confirm our chosen U-Boot baseline actually includes it. + +### The SDM reconfigure mailbox protocol has a hardcoded, tight 300ms RECONFIG_REQUEST timeout and a 4×512KiB buffer pool with a 720ms per-buffer reclaim timeout — both drivers our and his kernels share verbatim. + +- **Evidence:** stratix10-svc-client.h:68-69 in both repos; matches docs/de25-fpga-reconfig.md's own independent desk-research figures +- **His fix:** None yet — his one live test hit this timeout and wedged the board; the fix path (retry/backoff/hardened quiesce) is explicitly not yet built. +- **Applies to us:** Confirms our D0.2 dossier's latency-risk flag was well-founded rather than overcautious; any of our own live-reconfigure implementation needs to budget for this timeout being real and tight on actual hardware, not just in the datasheet. + +### Mainline 6.18.x cannot reconfigure an Agilex 5 fabric at all until BOTH a DT node set and a driver match-table entry are added — and adding only one of them fails in a way that looks like a driver bug. + +- **Evidence:** mainline socfpga_agilex5.dtsi has no svc/fpga-mgr/fpga-region node (only svcbuffer@0); stratix10-svc.c and stratix10-soc.c match only stratix10-*/agilex-*. His commit records that patching only the fpga-mgr left modprobe succeeding while the mailbox still failed with "couldn't get service channel (fpga)", because the SVC node binds at __init before its children can be created. +- **His fix:** Two one-line match-table additions (intel,agilex5-svc, intel,agilex5-soc-fpga-mgr) plus authoring firmware/svc + fpga-mgr + fpga-region nodes in the SoC dtsi. Note his svc node needs memory-region=<&service_reserved>, iommus=<&smmu 10> and altr,smmu_enable_quirk on this SMMU-enabled SoC. +- **Applies to us:** Directly and unavoidably — this is our D0.2 §3.1/§4.2 work item, now with a working reference. The upstream Altera series uses 'intel,agilex-soc-fpga-mgr' as a DT fallback instead; either route works, but the fallback route avoids carrying driver patches. Our decision, not his. + +### Reconfiguration on this SoC quantizes to ~1 s regardless of bitstream size, from two independent code paths. + +- **Evidence:** U-Boot's intel_sdm_mb.c does udelay(RECONFIG_STATUS_INTERVAL_DELAY_US = 1000000) after sending the bitstream, before its FIRST RECONFIG_STATUS poll, then polls at 1 s intervals (60 s ceiling, 100-retry inner loop at 20 ms). The kernel service layer independently polls with msleep(1000) (our §6.1 row 7). +- **His fix:** None — accepted. It is why even a ~3 s claimed 'live' config is nowhere near the raw fabric-write time. +- **Applies to us:** Yes. Our §6.2 estimate of '~10 ms to ~1 s' should treat ~1 s as the practical floor, not the optimistic tail. It is still small next to a reboot. + +### An MMIO read of an unconfigured or half-configured fabric is a FATAL async SError on Agilex 5 — a kernel panic, not a failed probe — and there is no AXI bus timeout, so a poke during the post-config settle window hangs a CPU (RCU stall) and a warm reset from that state brings the board back DARK (power-cycle or JTAG only). + +- **Evidence:** de25_fabric_ready.py's header ('on Agilex 5 the sysid read below is then a fatal async SError (kernel panic, not a recoverable failed probe; HW-proven)'); de25_live_switch_core.sh's settle comment ('the first LWH2F poke during that window ... hangs the CPU on the AXI bus — no timeout on Agilex → RCU stall, and a warm reset from that state leaves the board DARK'); MiSTer's own 'FULL-config WEDGE ... JTAG recovery needed' strings. +- **His fix:** A hard guard before Main is ever started (a de25_core= bootargs breadcrumb from U-Boot, an HPS_FIRST_MODE marker, and a fpga_manager state check), a 5 s settle after config-complete before any fabric access, and a policy of NEVER warm-rebooting out of a suspected wedge. +- **Applies to us:** Yes, and it is a design constraint on Main_MiSTer's DE25 port, not just on scripts: anything that probes the fabric must be gated on a positive fabric-configured signal. Note also his HW-proven finding that fpga0/state reads 'unknown' (a false negative) for a fabric configured by U-Boot rather than by the kernel driver — so fpga0/state alone is not a usable guard. + +### A hard-kill of the fabric client followed immediately by a reconfigure poisons the SDM service layer; the quiesce delay before a reconfigure is load-bearing and cannot be tuned down. + +- **Evidence:** de25_live_switch_core.sh's F8 note: HW-tested 2026-07-16, trimming the 3 s quiesce to 1 s made the very NEXT switch fail with 'timeout waiting for svc layer buffers', and the following service restart hung a CPU on the AXI bus (RCU stall), wedging the board silently — JTAG-recoverable only. His reading is that the sleep also lets the prior reconfigure's svc buffer pool fully reclaim. +- **His fix:** Left at 3 s quiesce + 5 s settle, with an explicit DO-NOT-REDUCE comment. +- **Applies to us:** Yes, if we do runtime switching. This is ~8 s of pure sequencing per switch on top of any reconfiguration cost, and it is the real answer to 'how fast can a core switch be' — far more than the SDM write. It should be measured in D2.5 as its own term. + +### MiSTer-scale Agilex 5 bitstreams for the A5EB013 die are 1.9-3.6 MB, i.e. they straddle the kernel's 2 MiB in-flight buffer pool (4 x 512 KiB). + +- **Evidence:** CORES.manifest sizes (menu 1,929,216 ... ao486 3,645,440); mainline stratix10-soc.c NUM_SVC_BUFS=4, SVC_BUF_SIZE=SZ_512K. +- **His fix:** None needed once the buffer-recycling replies work; his wall was self-inflicted via an ATF mod. +- **Applies to us:** As a sizing input, yes — it fills in the missing input to our §6.2 arithmetic and confirms our 1-4 MB guess. As a wall, only if we ever modify ATF's FPGA config buffer handling. It also means any bitstream transfer necessarily makes at least 4-8 buffer round trips, so BUFFER_DONE callback health is on the critical path for every switch. + +### The SDM owns the QSPI; the HPS cannot drive the Cadence QSPI controller directly (that path hangs). All HPS-side flash access goes through the FCS mailbox, in tiny chunks, with a chip-select re-assert required before EVERY write. + +- **Evidence:** de25_fcs_qspi.py header ('The HPS cannot touch it through the cadence controller (that path hangs)'); de25_fcs_stage_core.py:56-70 ('the SDM deselects the QSPI chip after every write cycle, so each consecutive write MUST re-assert cs(0) first -- else the 2nd write returns SDM status 4 and WEDGES the async channel (needs JTAG/power-cycle recovery)'); qspi_read max 4096 bytes per call. +- **His fix:** 4 KiB write chunks with cs-per-write and a 50 ms settle; cancel checks only BETWEEN operations, never mid-op. +- **Applies to us:** Only if we ever write QSPI from Linux — which our posture 1 says we never do. It is decisive evidence FOR posture 1: any QSPI-write-based update mechanism on this board is minutes-long, wedge-prone, and JTAG-recoverable at best. It also means our Q4 RSU sizing question has a companion: even if RSU fits, writing it is this. + +### The SD host needed capping at 25 MHz (default speed, no UHS/SDR50) for stability; SCR-corruption and ADMA issues appear around JTAG-adjacent boots. + +- **Evidence:** His board DTS caps &mmc max-frequency at 25000000 with an explicit stability rationale; SETUP.md §9 additionally requires sdhci.debug_quirks=0x60 and forbids iommu.passthrough=1 for post-JTAG full-SOF boots or SDHCI ADMA corrupts early SD init. +- **His fix:** DTS clock cap plus documented cmdline requirements for the JTAG development flow. +- **Applies to us:** Probably — it is a board/PHY-level finding, and the cdns,phy-* tuning block in his DTS is inherited from Terasic. If we hit SD instability, this is the first knob, and it costs bitstream read bandwidth (a 3.5 MB core at 4-bit/25 MHz is bounded near 12.5 MB/s theoretical). + +### Every reset on this SoC is a full SDM reconfiguration cycle, and software cannot distinguish a deliberate reboot from a power-cycle: RSTMGR_SOC64_STATUS reads 'Reset state: Cold' for both, and ATF's handler for software reboot is literally mailbox_reset_cold(). + +- **Evidence:** boot.scr.uimg's 2026-07-20 note, stated as validated against his own com9_coldboot_throttle_validate.log vs com9_warmreboot_throttle_validate.log (both logs absent from the shared repo, so the validation itself is [U]). +- **His fix:** A one-shot marker file written by the single legitimate reboot path and consumed by boot.scr on read, so any unplanned reset defaults to MENU. +- **Applies to us:** Yes for our Q5 (warm reboot, parked) and for any boot-time state machine we build. It also means a reboot can never be cheap on this board: it is a full SDM configuration cycle every time. + +### Partial reconfiguration is not a free speed win: PR personas must come from one base compile, and loading a persona that does not match wedges the SDM. + +- **Evidence:** MiSTer binary strings 'fpga_load_rbf: SDM WEDGE on %s (persona not from base compile?) - JTAG recovery needed' and 'fpga_load_rbf: restoring PR base before partial'; analysis/pr_partial/de25_pr_partial.dtso documents that the earlier FULL-config overlay HUNG the HPS in 'Experiment 2' and that partial-fpga-config is 'the make-or-break difference'; the PR partial bitstream in that experiment was 344 KB vs multi-MB full cores. +- **His fix:** He kept a de25base.core.rbf and restored it before a partial swap, then abandoned the tier in favour of full config once HPS-first compilation made full reconfiguration survivable. +- **Applies to us:** Relevant if anyone proposes PR as the MiSTer core-switch mechanism: it would make every core a persona of one frozen base compile — an FPGA-project-level constraint, not a software one — in exchange for a ~10x smaller bitstream. + +### Every DMA-capable peripheral needs an explicit iommus= stream-ID property once an smmu node is enabled on Agilex 5 -- omitting it on even one master (he missed usb0) silently AHB-faults every DMA transfer on that device with no obvious error pointing at the DT. + +- **Evidence:** de25-linux commits 36f39d30d/c917db658: usb0 was missing iommus=<&smmu 6> after being ported from a different upstream base than the vendor tree it was checked against; fixed with one line, HW-validated. +- **His fix:** Add the correct iommus=<&smmu N> property with the vendor tree's stream ID for every boot-relevant DMA master. +- **Applies to us:** Mainline 6.18.44's socfpga_agilex5.dtsi ships neither an smmu node nor any iommus= properties at all (verified directly). Whoever authors our DE25 board DTS (D0.3/D2) must add the smmu node and correctly stream-ID every master ourselves, sourced from a vendor tree or Altera's newer DTS series, and audit every DMA-capable node individually -- this is not a one-time fix, it's a class of omission to check for on every node we add. + +### mmc0/SDHCI on Agilex 5 faults arm-smmu-v3 F_TRANSLATION on a genuine cold boot with an SMMU node enabled and a v6.18-generation kernel, even with a devicetree byte-identical to a working 6.12.11 baseline -- ~~a real kernel-version regression~~ **[CORRECTED 2026-08-22: a vendor-vs-mainline DRIVER delta, not a version regression — see the correction box in §3 above; the leading root cause is a 64-bit vs 40-bit DMA mask under SMMU translation, and it travels forward to 6.19/7.2]**, not a DT authoring mistake, root cause unresolved. + +- **Evidence:** de25-linux commits 314a0be6c (initial false-negative) and 716559020 (retraction + reproduction via a genuine physical power-cycle through the unmodified production boot chain). +- **His fix:** sdhci.debug_quirks=0x60 on the kernel command line, forcing SDHCI/mmc0 into PIO instead of ADMA, sidestepping the DMA-through-IOMMU path entirely. Baked into the default production boot.scr.uimg bootargs, not a JTAG-only special case. +- **Applies to us:** Our kernel baseline is also 6.18.x mainline. If we enable an SMMU node for Agilex 5 (which D0.2 flagged as an open question -- §3.1 note 2, whether the svc node even needs iommus=<&smmu 10>), we should expect to hit the identical mmc0/ADMA fault the moment SD/MMC is DT-enabled alongside SMMU, and plan for the PIO-quirk mitigation (with its throughput cost) rather than treating it as a DT-authoring bug to debug from scratch. **[CORRECTED 2026-08-22:** there is no "upstream fix" coming, because there is no upstream regression — the delta is vendor-driver-vs-mainline-driver (a 40-bit vs 64-bit DMA mask) and it is present on 6.19 and 7.2 too. The mainline-first remedy to test is a one-entry `sdhci-cadence` patch adding `intel,agilex5-sd4hc` with a 40-bit mask.**]** + +### Reconfiguring the fabric and then touching it (an LWH2F/fabric-side register poke) too soon after config-complete hangs the CPU on the AXI bus with no timeout -- an RCU stall that leaves the board completely dark, JTAG-recoverable only, not warm-reset-recoverable. + +- **Evidence:** de25-linux board_overlay/usr/local/bin/de25_live_switch_core.sh: HW-tested trimming the post-quiesce sleep from 3s to 1s reproduced this exact failure mode; a hard-kill of MiSTer followed by immediate reconfig was separately found to poison the SDM service layer. +- **His fix:** A generous, empirically-tuned settle window: 3s before starting reconfiguration (to let the prior fabric session's buffer pool fully reclaim) and 5s after config-complete before any fabric-side access (to let the new core's internal resets/PLLs lock). +- **Applies to us:** This directly bears on D0.2 §11 row 4 (does the region accept repeated overlay apply/remove cycles cleanly). His answer is 'yes, but only with settle windows this project had to discover by bricking the board twice' -- our own D2.5 hardware-measurement plan should budget for exactly this discovery rather than assuming a bare of_overlay_fdt_apply()/remove() pair is safe back-to-back, and any MiSTer-style core-switch UI on this board needs a mandatory settle floor baked into the framework, not left to a user-triggerable spam-switch. + +### The SDM mailbox layer exhibits an apparent request-budget/throttling behavior across reconfiguration attempts that is broader than any single driver bug -- a raw overlay-trigger write timed out waiting for RECONFIG_REQUEST even after the buffer-size (BUF4) bug that had caused the '>2MB wall' was already fixed. + +- **Evidence:** de25-linux commit 30d9c99a8 message, explicitly describing this as 'the same class of pre-existing mailbox/budget issue already documented elsewhere in this project.' The referenced memory files documenting that broader pattern are not present in this repo snapshot -- root cause is [U] to us. +- **His fix:** Not fixed at the point this repo snapshot ends; the live-switch worker treats an unresponsive trigger write as fatal (30s deadline, then park rather than reboot) rather than retrying. +- **Applies to us:** We should not assume the mainline stratix10-soc.c/stratix10-svc.c stack is bug-free on Agilex 5 just because the compatible-string gap (U1) is a known, fixable issue. There appears to be at least one more class of SDM-mailbox fragility on this platform that neither his fixes nor our D0.2 desk research have fully characterized; D2.5's hardware-measurement plan should treat 'the mailbox occasionally times out for reasons unrelated to buffer size' as an open risk to budget time against, not a solved problem. + +--- + +## 4. Decisions for the owner + +Every point below is a place his approach diverges from our documented plan. **These are not +recommendations.** The neutrality audit that was to strip advocacy from this section never ran, so +read any evaluative wording with that in mind. + + +### DTSI provenance — fork mainline's socfpga_agilex5.dtsi vs override from the board DTS + +- **His approach:** Forked mainline's socfpga_agilex5.dtsi and back-ported vendor nodes into it, including a wholesale replacement of the mainline gmac0 node. Board DTS is Terasic's file verbatim. +- **Our plan:** Parity-with-stock methodology: author our own board DTS on top of mainline's, using &label overrides, and keep patches off shared .dtsi files (the DE10 patch explicitly refuses to carry a socfpga.dtsi hunk for exactly this reason). +- **What forced his choice:** Forced for three of the nodes: mmc0, smmu and the firmware/svc subtree do not exist in mainline 6.18 at all, and the board DTS references &mmc, &smmu and &temp_volt by label, so their absence is a dtc compile error, not a silent gap (bf8e5c9c6 says so explicitly). The gmac0 replacement was forced by a real probe failure (e57c54c74) and he states it is a whole-node structural difference — split IRQs, reset-names, iommus — not expressible as a board-level override. +- **Cost of his way:** A forked shared .dtsi that every socfpga board in the tree inherits; every kernel bump is a merge against upstream's own evolving Agilex 5 DTSI, and upstream has already been seen to independently re-add a conflicting gmac0 revision. +- **Cost of our way:** Not available for mmc0/smmu/svc — there is nothing to override. A mainline-only DTS for this board does not boot from SD. We would have to add those nodes somewhere; the only parity-preserving choice is which file, and whether we upstream them. +- **Our doc:** docs/de25-nano-plan.md §4.1; docs/dts-comparison.md; board/mister/de10nano/linux-patches/0004-dts-de10nano-MiSTer.patch (Forward-port note refusing the shared-dtsi hunk) + +### Reconfiguration trigger — custom in-tree misc driver + +- **His approach:** drivers/misc/de25_fpga_trigger.c: a new 95-line misc driver with one write-only sysfs attribute that re-applies a static .dtbo via of_overlay_fdt_apply(), plus a new Kconfig symbol CONFIG_DE25_FPGA_TRIGGER. +- **Our plan:** Not decided — our fpga-reconfig §11 row 6 lists three costed options (carry OF_CONFIGFS, write a board driver, U-Boot-preload only) and defers the choice to D2. +- **What forced his choice:** Forced: mainline exposes no userspace path to fpga_mgr_load(); of-fpga-region only programs on OF_OVERLAY_PRE_APPLY; and the configfs overlay path he had assumed (RPi-style drivers/of/configfs.c) was never upstream, so his script's core step had never once worked. +- **Cost of his way:** A permanently carried out-of-tree kernel patch adding a board-specific driver + Kconfig symbol, with no upstreaming story. The trigger has no quiesce sequencing of its own, so driving it directly is documented by its own author as unsafe. +- **Cost of our way:** U-Boot-preload-only means every core switch is a reboot (~35 s on his measurements), which is the UX premise our D0.2 called into question. OF_CONFIGFS is now known not to be a real option. +- **Our doc:** docs/de25-fpga-reconfig.md §11 row 6; §8 Claim A + +### Runtime HPS↔fabric access — raw /dev/mem instead of UIO + +- **His approach:** CONFIG_UIO is not set. Every fabric register access (sysid probe, ascal fbcfg base) is an mmap of /dev/mem at fixed physical addresses from Python scripts run by systemd units. +- **Our plan:** DP-9 (as narrowed by D0.2 §8) leaves the runtime signaling/aperture contract explicitly undecided and flags the /dev/mem practice as the thing patches 0044/0045 exist to replace. +- **What forced his choice:** No forcing reason found in the commits; it appears to be the path of least resistance carried forward from bring-up scripting. He has not needed an aperture binding because Main's fabric access is not yet performance-bound on his setup. +- **Cost of his way:** No named/size-bounded apertures, no write-combining, no page-attribute control, and STRICT_DEVMEM had to be worked around with a no-map reserved region for AO486's 256 MB window. Also no protection against Main touching a half-configured region — a documented board-wedge hazard. +- **Cost of our way:** Re-deriving 0043/0044/0045 for Agilex 5 is real work gated on a GHRD that defines the topology, which does not exist yet. +- **Our doc:** docs/de25-fpga-reconfig.md §8 (Claim B refuted), §11 rows 10 and 11 + +### Vsync interrupt / FBIO_WAITFORVSYNC + +- **His approach:** No interrupts property on the fb node; MiSTer_fb.c patched so an absent IRQ is legal and the ioctl simply becomes unavailable. +- **Our plan:** Our D0.2 §7.3 names 'a per-frame interrupt from fabric to HPS' as one of four requirements for a MiSTer-style framebuffer and calls it 'the doorbell problem again'. +- **What forced his choice:** No f2h IRQ is wired in his golden_top/GHRD, and the mainline Agilex 5 DTSI has no bridge or f2h interrupt-cell scheme to name one. He worked around the driver's arm32 NO_IRQ check rather than wire an interrupt. +- **Cost of his way:** FBIO_WAITFORVSYNC is silently unavailable — an ABI Main_MiSTer uses on DE10. Any tearing/pacing behavior that depends on it is gone with no error. +- **Cost of our way:** Requires the GHRD to expose an f2h interrupt and a documented Agilex 5 SPI number for it. Not resolvable from Linux alone. +- **Our doc:** docs/de25-fpga-reconfig.md §7.3 (table row 3); board/mister/de10nano/linux-patches/0004-dts-de10nano-MiSTer.patch:195-201 + +### Audio + +- **His approach:** MiSTer-audio-spi driver ported and built as a module, but no DT node, so it never probes. No spi0/spidev nodes either. +- **Our plan:** Our DE10 DTS wires spi0 → 'MiSTer,spi-audio' @10 MHz and spi1 → spidev for brightness/add-on control, as stock-parity ABI. +- **What forced his choice:** Not stated in any commit. Most likely simply not reached yet — his SETUP.md scope is reaching the MENU. I could not find a forcing reason. +- **Cost of his way:** No audio path through the documented MiSTer mechanism; whatever audio exists is fabric/HDMI-side only. /dev/spidev1.0 (Main's brightness.cpp) does not exist. +- **Cost of our way:** Requires knowing which Agilex 5 SPI controller the GHRD wires to the audio link, which is a GHRD question. +- **Our doc:** board/mister/de10nano/linux-patches/0004-dts-de10nano-MiSTer.patch (spi0/spi1 sections); docs/dts-comparison.md + +### SD performance posture + +- **His approach:** max-frequency = <25000000>, no-1-8-v, sd-uhs-sdr50 and the sdhci-caps/sdhci-caps-mask overrides removed; plus sdhci.debug_quirks=0x60 (PIO, no ADMA) baked into the production boot script. +- **Our plan:** Parity with stock DE10 means SD performance is not deliberately crippled; nothing in our docs contemplates a PIO-mode root filesystem. +- **What forced his choice:** Both forced by real faults: corrupted SCR reads on post-JTAG boots (b3cc7d53f) and an SMMU F_TRANSLATION fault on mmc0 DMA that he established is a 6.12→6.18 kernel regression with a byte-identical devicetree (716559020). Root cause not bisected, deferred by explicit choice. +- **Cost of his way:** Default-speed 25 MHz 4-bit AND PIO for every block read on a Cortex-A55. This is a large, permanent, whole-system I/O penalty that touches every core load, every ROM load and the loop-mounted rootfs. It is the strongest DTS-level candidate for symptom (b) that is independent of reconfiguration cost. +- **Cost of our way:** Bisecting the 6.12→6.18 mmc0/SMMU regression, or shipping with the SMMU off / iommu.passthrough, each of which has its own consequences (his SETUP.md §9 warns iommu.passthrough=1 corrupts early SD init after a JTAG load). +- **Our doc:** docs/de25-boot-chain.md §4 (posture); docs/de25-nano-plan.md §4.1 + +### Core payload location — QSPI RSU slots vs SD files + +- **His approach:** Original design staged core bitstreams into QSPI RSU application slots: P1 3 MB fixed MENU hub, plus (originally) a 4 MB P2 and a 3 MB P3, merged 2026-07-07 into one 7 MB staging slot so >4 MB cores would fit. The current design reads .uboot.rbf from the exFAT card instead. +- **Our plan:** Our posture-1 v1 recommendation never writes QSPI at all; cores live on the FAT partition, as on DE10. +- **What forced his choice:** He flashes his own QSPI image anyway (his SPL is exFAT-aware), so the RSU slot mechanism was available and gave a reboot-based switch that worked before the kernel fpga_manager path did. +- **Cost of his way:** A hard, small ceiling on how many core images the firmware can hold — one MENU plus effectively one staged core — and a per-core QSPI write cycle. This is the most literal match for the owner's second-hand symptom (a), 'only being able to load a certain number of cores into firmware'. +- **Cost of our way:** None on this axis; the SD-file model has no such ceiling. But it depends on a working live-switch or a reboot-with-fpga-load path. +- **Our doc:** docs/de25-boot-chain.md §4 (posture 1 vs 2) + +### Core-switch mechanism actually in production use + +- **His approach:** A full warm reboot (~35s) per core switch, with U-Boot re-running `fpga load` at boot per a persisted core_select.txt. His in-kernel live DT-overlay reconfigure path (~8s target) exists as of Aug 1, 2026 but is not yet reliable — it can time out at the SDM mailbox and wedge the board, and its safety net (park the board rather than reboot on a stuck-mid-config SDM) is deliberate but user-visible. +- **Our plan:** Our D0.2 dossier's DP-9 concluded core-switching is 'UX-viable at low-to-moderate confidence', implicitly assuming a fast (non-reboot) in-kernel reconfigure path is achievable, with latency still an open unknown (U5) pending measurement. +- **What forced his choice:** The reboot fallback is what actually works reliably today; the live path is new (committed same day as the failed HW test) and explicitly documented by him as not yet safe for routine use ('the raw trigger has no quiesce/settle sequencing of its own... unsafe for routine use'). +- **Cost of his way:** 35s per core switch if the live path is disabled/unreliable — materially worse UX than instant Cyclone-V-style core swap, and a documented failure mode (board wedge, JTAG-only recovery) on the fast path as currently implemented. +- **Cost of our way:** Building and hardening the same live DT-overlay + SDM-mailbox mechanism ourselves, with no guarantee it clears the same 300ms RECONFIG_REQUEST timeout more reliably than his first attempt did — this is real engineering work, not a known-solved problem, and his result is the first empirical Agilex-5-hardware data point either project has. +- **Our doc:** docs/de25-fpga-reconfig.md §8 (DP-9 verdict), §6 (latency, U5 marked open) + +### Bitstream authentication (VAB) + +- **His approach:** VAB is not enabled — only FIT image signing (CONFIG_SPL_FIT_SIGNATURE=y). Anyone who can write the SD card/QSPI can boot arbitrary code. +- **Our plan:** Not yet decided by our project (U4 was an open unknown, not yet dispositioned in policy). +- **What forced his choice:** No evidence of an explicit security requirement in his project; consistent with a developer-focused, JTAG-recoverable bring-up posture rather than a hardened consumer deployment. +- **Cost of his way:** No bitstream-tampering protection; acceptable for a dev board, not necessarily for a shipped image. +- **Cost of our way:** Enabling VAB would require CONFIG_SOCFPGA_SECURE_VAB_AUTH plus a real key/fusing workflow, a project we have not scoped and he has not attempted either. +- **Our doc:** docs/de25-boot-chain.md (U4 listed as open unknown) + +### Non-stock ATF/BL31 buffer-size modification + +- **His approach:** His comments describe carrying a custom BL31 change ('FPGA_CONFIG_BUFFER_SIZE=16' mod, later a 'BUF4' fix) affecting the SDM reconfigure buffer-reclaim BUSY-reply path — a modification to Arm Trusted Firmware itself, not to U-Boot or the kernel. +- **Our plan:** Our plan is to use a modern/current U-Boot as a reference and has not scoped any ATF/BL31-level customization at all. +- **What forced his choice:** Presumably to work around an observed >2MB live-reconfigure failure — but I could not verify this in code (the ATF source is not among the repos we have access to), so I cannot confirm whether it is a real fix, a misdiagnosis, or unnecessary against a stock/current ATF. +- **Cost of his way:** An unverifiable, out-of-band firmware customization layered under U-Boot that neither of us can currently audit. +- **Cost of our way:** If we adopt current/stock ATF without this change, we do not know whether we would hit the same >2MB wall he describes — unresolved, not established either way. +- **Our doc:** docs/de25-nano-plan.md §4.1 (modern U-Boot direction) + +### QSPI posture — he writes the SDM configuration flash; our v1 recommendation is never to write it + +- **His approach:** Flashes a custom .jic over the factory QSPI via JTAG (golden_top_menu_exfat_spl_ro_bootcmd_20260704.jic), carrying his own exFAT-aware SPL, his own bootcmd, and his own fabric image. This is our posture 2. An earlier variant was HPS-First (Phase-1 .hps.jic, fabric left unconfigured for U-Boot to fill), and an even earlier one carried a full RSU application-slot layout (rsu_initial_v2). +- **Our plan:** Posture 1: pin factory QSPI byte-untouched, boot our own u-boot.itb from Terasic's factory SPL, treat the FSBL->u-boot.itb name+location contract as the only interface we own. +- **What forced his choice:** Forced, and the forcing reason is legible: he chose a single-partition exFAT card with the rootfs as a loop-mounted /linux/rootfs.ext4, and the factory SPL cannot read exFAT. His two DE25 U-Boot commits are exactly 'Add SPL exFAT boot support for DE25' (a new common/spl/spl_exfat.c plus an imported libexfat) and 'Make DE25 exFAT boot read-only and script-first'. An exFAT SPL cannot live on the SD card it must read, so it has to go into QSPI. +- **Cost of his way:** Every board needs a bench JTAG flash before it boots, with Quartus Pro on a PC. The QSPI becomes a per-board version-skew surface (his SETUP.md §10 troubleshooting is 'you flashed the wrong .jic'). It cost him at least one hard brick (2026-07-12: a stale RSU fallback armed reboot_image over a Phase-1 image, SDM booted garbage, JTAG reflash). And it makes the SPL/bootcmd our-code, not vendor-code, forever. +- **Cost of our way:** We inherit whatever the factory SPL can read — realistically FAT — so the single-exFAT-card layout and the loop-root design are off the table for us, and the version-skew seam our de25-boot-chain.md §5 identifies stays. We also cannot fix an SPL bug without moving to posture 2/3. +- **Our doc:** docs/de25-boot-chain.md §4 (postures 1/2/3) and §5 (version-skew seam); docs/de25-nano-plan.md §4.1; DP-1 + +### Card layout — one exFAT partition with a loop-mounted ext4 rootfs vs our FAT + rootfs model + +- **His approach:** Single active MBR partition, exFAT, spanning the card; boot files at the root; rootfs is /linux/rootfs.ext4 (3.5 GiB) loop-mounted as /dev/loop8 with the outer exFAT bind-mounted at /media/fat. Kernel-side init/do_mounts carries a custom mount_mister_loop_root path. +- **Our plan:** DE10-style separation: a FAT boot partition read by the bootloader plus a rootfs the kernel mounts directly; persistent state on the FAT partition. +- **What forced his choice:** Stated in SETUP.md §1: it supersedes an earlier FAT32-p1 + ext4-p2 layout so the whole card is one user-visible exFAT volume (the MiSTer /media/fat experience) with no small boot partition to manage. It is what forced the exFAT SPL, hence the QSPI flash. +- **Cost of his way:** An exFAT SPL, a QSPI flash per board, a kernel patch to init/do_mounts, an ext4-in-a-file rootfs that cannot be grown or fsck'd normally, and a documented Windows failure mode copying the 3.5 GiB image (ERROR 1006 on some readers). He also hit live ext4-in-loop rootfs corruption during a failed boot incident. +- **Cost of our way:** Two partitions the user can see, and a FAT-side/rootfs-side split for persistent state — exactly the DE10 model our persistent-state rule already assumes. +- **Our doc:** docs/de25-boot-chain.md §3 ('What lives where'); docs/de25-nano-plan.md §4.1 + +### U-Boot base version + +- **His approach:** U-Boot 2025.01, the Terasic/Altera socfpga vendor fork (Makefile VERSION=2025 PATCHLEVEL=01), with his exFAT work applied on top. He never rebased. +- **Our plan:** Modern/current upstream U-Boot, per the owner's standing instruction. +- **What forced his choice:** He started from the board's own factory u-boot.itb (which identifies as 2025.01-gd0f8813fd6bf, i.e. a build of this tree at Terasic's board commit) and needed the vendor DDR/SDM/handoff code that is only in that fork. Rebasing was never the goal; booting was. +- **Cost of his way:** His SPL exFAT series is against a 2025.01 vendor fork; nothing of it is upstreamable as-is and it does not transplant to current U-Boot without work. He also inherits the fork's Agilex 5 quirks with no upstream fix path. +- **Cost of our way:** We must establish that current upstream U-Boot has enough Agilex 5 support for this board, and carry the Terasic board DTS/defconfig ourselves. His repo shows the board support commit is small and self-contained (8 files) — that is encouraging, not proof. +- **Our doc:** docs/uboot-mainline-port.md / docs/uboot-tasks.md; owner's standing instruction + +### Core storage model — cores in QSPI 'firmware' slots vs cores as files on the card + +- **His approach:** Three successive models: (Tier C/RSU) stage a core's .rpd into a QSPI RSU application slot via the FCS mailbox and reboot the SDM into it; (Tier C/launcher) leave cores on the card as .uboot.rbf and warm-reboot so U-Boot fpga-loads the selection named in /core_select.txt; (live) kernel fpga_manager overlay, no reboot. He moved forward through all three; the RSU tier is now explicitly defused. +- **Our plan:** Cores are ordinary files on the card, loaded at runtime through the kernel fpga_manager + DT overlay; QSPI is never written. +- **What forced his choice:** The forcing reason is legible from the tier ordering: the RSU path was the first thing that worked, because it needs no working overlay loader and no kernel fpga_manager binding — the SDM does the configuration at reset. He only reached the fpga_manager path after fixing the agilex5 compatibles on 2026-08-01. +- **Cost of his way:** This IS symptom (a) and half of symptom (b): a hard cap of MENU + two (then one) cores resident in firmware, minutes to re-stage, a reboot per switch, and a brick class where a mis-armed reboot_image points the SDM at a slot that no longer holds a valid image. +- **Cost of our way:** We depend entirely on the overlay path working, repeatedly, which our §6.1 row 4a and §3.2 flag as the unexercised risk — with no fallback tier if it does not. +- **Our doc:** docs/de25-fpga-reconfig.md §2, §3.2 option (c); docs/de25-nano-plan.md DP-9 + +### Overlay loader choice + +- **His approach:** Currently option (b): a custom ~95-line misc driver (de25_fpga_trigger) over of_overlay_fdt_apply(), applying a STATIC pre-built .dtbo that names a FIXED firmware path, with the chosen core copied to that path first. Previously he relied on option (a), the vendor kernel's CONFIG_OF_CONFIGFS, without knowing it was vendor-only. +- **Our plan:** Undecided between (a) carry OF_CONFIGFS, (b) our own small driver, (c) U-Boot preload only; (c) is sufficient for the L1 developer OS. +- **What forced his choice:** Not a design preference — a forced discovery. His scripts assumed configfs because every Altera document describes it; when he moved from the stock 6.12.11 kernel (CONFIG_OF_CONFIGFS=y) to his mainline-based 6.18 port, the path silently vanished and his core-write step failed structurally. +- **Cost of his way:** ~95 lines and a UAPI he owns forever, plus a static-overlay design that cannot vary anything per switch except the file contents at a fixed path. In exchange: no out-of-tree Kconfig carry and no dependence on an unmerged 2014 series. +- **Cost of our way:** Option (a) matches every vendor recipe verbatim but carries a patch unmerged since 2014; option (c) means no runtime core switching at all. +- **Our doc:** docs/de25-fpga-reconfig.md §3.2 (option table a/b/c) + +### Fabric-at-boot posture — HPS-First (fabric unconfigured until something loads a core) vs golden/MENU-in-QSPI + +- **His approach:** Tried HPS-First (Phase-1 .hps.jic; U-Boot fpga-loads the selected core before booting Linux) and RETIRED it in favour of a golden .jic that configures a MENU fabric from QSPI at every reset. Guards were needed either way: a de25_core= bootargs breadcrumb, an /media/fat/HPS_FIRST_MODE marker, and a fabric-ready probe, because on Agilex 5 an MMIO read of an unconfigured fabric is a fatal async SError (kernel panic), not a failed probe. +- **Our plan:** Not yet decided; our de25-boot-chain.md assumes the factory QSPI configures the fabric and our fpga-reconfig doc assumes Linux reconfigures it afterwards. +- **What forced his choice:** HPS-First produced a reproducible fatal async SError on the first fabric read ~26 s into every warm-reboot boot, board unreachable, JTAG recovery required — he explicitly tested and REFUTED the leading hypothesis (a redundant double `bridge enable`) and still could not fix it. Retiring the posture was the fix. +- **Cost of his way:** A cold boot always lands on the MENU fabric, matching DE10's menu.rbf-at-every-boot guarantee, at the cost of putting a fabric image in QSPI (posture 2 again). +- **Cost of our way:** If we stay on the factory QSPI we inherit whatever fabric Terasic's golden image configures, and our first fabric access from Linux must be guarded the same way — an unguarded probe is a panic, not an error. +- **Our doc:** docs/de25-boot-chain.md §2, §7; docs/de25-fpga-reconfig.md §7 + +### Warm reboot as a core-switch mechanism, and the cold/warm indistinguishability it forces + +- **His approach:** The reboot tier writes /core_select.txt and reboots; because U-Boot cannot tell a deliberate reboot from a power-cycle (RSTMGR_SOC64_STATUS reads 'Reset state: Cold' for both, and ATF's own handler for every software reboot is mailbox_reset_cold()), he added a one-shot /core_switch_pending.txt marker that boot.scr consumes on read, so any unplanned reset falls back to MENU. +- **Our plan:** No reboot-based switching contemplated; our Q5 parks warm-reboot behaviour as [U]. +- **What forced his choice:** Forced by the SoC: he validated the indistinguishability against his own cold-boot and warm-reboot serial logs. Without the marker, a core that hangs at boot is retried forever. +- **Cost of his way:** An extra file-based state machine spanning Linux and U-Boot, and a boot that must be orchestrated by exactly one code path to be honoured. +- **Cost of our way:** None directly — but Q5 should record that a warm reboot on this SoC is a full SDM reconfiguration cycle and is not distinguishable in software from a cold boot. +- **Our doc:** docs/de25-boot-chain.md §8.5 (Q5, warm reboot parked) + +### Kernel base + +- **His approach:** A mainline-derived 6.18.38 tree with the DE25 board port and the MiSTer kernel series merged in, plus a de25_defconfig. The board's shipped rootfs still carries modules for the Terasic 6.12.11 vendor kernel, and his autostart script insmods loose .ko files by absolute path because they are not depmod-wired. +- **Our plan:** Buildroot-built mainline 6.18.44 with our patch series, one coherent module tree. +- **What forced his choice:** He is running Terasic's STOCK rootfs (extracted from de25_nano_revA_sdcard_console_v1.1.img p2) with an overlay dropped on top, so the userspace and the kernel come from different worlds and the seams are patched by hand. +- **Cost of his way:** Hand-installed modules, an insmod-by-path loop in a service, and a documented class of silent failure when the module tree and the running kernel disagree — the same trap our own memory notes call out for kernel bumps. +- **Cost of our way:** We build the rootfs, so this class does not arise; but we also do not get Terasic's vendor userspace for free. +- **Our doc:** docs/de25-nano-plan.md; ADR 0027 (bare developer OS scope) + +### QSPI-resident boot posture + +- **His approach:** He flashes his own custom .jic (a Terasic golden_top GHRD rebuild with a custom exFAT-aware, read-only, script-first SPL) over JTAG, rather than the factory-shipped image. His SPL sources boot.scr.uimg from an exFAT partition instead of the DE10-style FAT32/0xA2 split. +- **Our plan:** Posture 1 (v1 recommendation, not yet decided): pin factory QSPI byte-untouched, never write it, and prove our own u-boot.itb loads under Terasic's unmodified factory SPL. +- **What forced his choice:** His stated reason (SETUP.md §1/§3) is that the persistent exFAT + single-partition loop-root boot flow needed an exFAT-aware SPL, which the factory SPL is not; an earlier all-core U-Boot fpga-load launcher approach wedged the SDM and was retired, motivating a rebuilt, more conservative SPL. +- **Cost of his way:** He owns SDM firmware + FSBL + DDR/pinmux handoff for every board onboarded (a bench JTAG operation per unit); factory-FSBL compatibility with any mainline artifact (U3) is now untested and unknown for his design. +- **Cost of our way:** Posture 1 constrains the SD-side boot flow to whatever partition scheme the factory SPL can read (its SPL_FS_LOAD_PAYLOAD_NAME/FS_BOOT_PARTITION contract), and is untested against a mainline-built u-boot.itb until D2.2's first hardware pass. +- **Our doc:** docs/de25-boot-chain.md §4 (three postures) and §8.3 (Q3, parked) + +### Core-switch mechanism and QSPI role + +- **His approach:** Runtime core switching is a two-tier system: a fast, no-reboot Linux-kernel overlay path (de25_fpga_trigger -> of_overlay_fdt_apply -> stratix10-soc.c) as the default, falling back to a warm-reboot U-Boot fpga-load path; an earlier QSPI-resident RSU multi-slot staging scheme for baking cores into flash was tried and partly retired after a board-wedging failure. +- **Our plan:** DP-9 (decided in direction): DTS/fpga-region overlay configuration is the sole native reconfiguration architecture on Agilex 5, adopted in place of the DE10's carried UIO doorbell patches. Our plan does not touch QSPI for core switching at all (per de25-boot-chain.md §5's 'no release writes QSPI' rule). +- **What forced his choice:** QSPI staging was his path to give any-size cores a boot-time selection mechanism before the Linux overlay driver existed and worked; it was abandoned once the overlay-based live-switch matured, not because QSPI residency was ever the intended long-term design. +- **Cost of his way:** He hit two board-wedging failures along the way (the retired all-core fpga-load launcher, and the 2026-07-12 stale-.rpd/reboot_image wedge) before landing on the Linux-overlay-based approach our DP-9 already independently arrived at from source-code reading alone. +- **Cost of our way:** None yet paid -- D0.2's overlay-only conclusion is confirmed as correct by his hardware trail, at the cost of two wedges he already absorbed and we have not. +- **Our doc:** docs/de25-fpga-reconfig.md §8 (DP-9 verdict), docs/de25-nano-plan.md §6 DP-9 + +### HPS Ethernet MAC address + +- **His approach:** A single fixed MAC (CE:24:6F:BD:E3:59) baked into a NetworkManager cloned-mac-address profile shipped inside the released rootfs image -- identical on every board flashed from that image. +- **Our plan:** No DE25-specific mechanism designed yet; the DE10 precedent (ADR-adjacent, installer-overlay/init) generates one random locally-administered MAC per physical card at flash time and injects it via U-Boot's fdt_fixup_ethernet()/aliases mechanism, giving each card a distinct address; the project's stated philosophy (ADR 0015, per-device SSH host keys) explicitly rejects shipping identical per-device identifiers. +- **What forced his choice:** His stated reason (comment in the .nmconnection file) is purely pragmatic: get a deterministic DHCP lease for a single development board without touching U-Boot env/QSPI at all, given the HPS has no fused MAC and CONFIG_NET_RANDOM_ETHADDR randomizes it every cold boot. +- **Cost of his way:** Every board flashed from his released rootfs image collides on the same MAC and the same DHCP lease if two are ever on the same network -- fine for one dev unit, not viable as a multi-user distribution default. +- **Cost of our way:** Requires either reusing the DE10's flash-time random-MAC-into-u-boot.txt mechanism (needs a persisted env location on the exFAT/loop-root layout, which is a different boot flow than DE10's) or a first-boot systemd/udev generator writing a per-device NetworkManager profile -- undesigned, not yet costed. +- **Our doc:** board/mister/de10nano/installer-overlay/init:856-885; memory: ssh-host-keys-shared-across-all-misters (ADR 0015 philosophy) + +--- + +## 5. Open questions this pass did not close + +- Does the DE25-Nano HPS actually have 2 GiB of DRAM? His memory node hard-codes reg = <0 0x80000000 0 0x80000000> despite a comment saying the bootloader fills it in, while our plan §4.1 records 1 GB LPDDR4 for the HPS from the User Manual. If it is 1 GiB, x86ram@b0000000+0x10000000 sits exactly at the top of RAM and the declared size is wrong — worth checking against a booted /proc/meminfo or the U-Boot log before we copy any of this map. +- Is his claim that the FDT reserved-memory scanner silently drops the LAST child node true on 6.18? He designs around it (x86ram deliberately not last) but cites only a document in a parent repo I do not have. If false, it is harmless superstition; if true, it is a mainline bug we should know about. +- Which of the four SMMU stream IDs (gmac0=1, mmc0=5, usb0=6, svc=10) are architectural for Agilex 5 and which are Terasic-GHRD-specific? He inherited all four from the vendor BSP without a citation to an Intel document. +- Does the svc node actually require iommus=<&smmu 10> and altr,smmu_enable_quirk, or are they cargo from the vendor tree? He never tested the form without them, and mainline gen1 socfpga_agilex.dtsi has no such properties on its svc node. +- Is the ~3 s / 3.3 MB reconfiguration figure real? It appears only as a source comment citing analysis/SUB10S_ATF_KERNEL_DIVERGENCE.md and a COM9 serial log, neither of which is present in this repo. The single documented HW attempt at the kernel fpga_manager path FAILED with 'timeout waiting for RECONFIG_REQUEST', so the ~3 s number most plausibly comes from the U-Boot fpga-load path, not the Linux path. Someone should establish which path it measures before D0.2's UX conclusion is revisited on it. +- Does the >2 MB live-load wall really disappear with a stock (unmodified) BL31, or only with his BUF4 build? He attributes the wall to his own FPGA_CONFIG_BUFFER_SIZE=16 modification, which would mean a stock ATF never had the problem — but that is his diagnosis of his own bug, and it is the load-bearing claim for symptom (a) being self-inflicted rather than intrinsic. +- How does Main_MiSTer find the HDMI transmitter on DE25? On DE10 it probes 0x39 across /dev/i2c-0..2 (video.cpp:1448) and refuses to scan past bus 2. His DTS enables exactly one HPS I2C adapter with no children, and the ADV7513 control I2C is on FPGA pins. Either his aarch64 Main fork removed that probe or the transmitter is configured entirely from the fabric — neither is established here. +- Where did the audio path go? MiSTer-audio-spi is ported and built as a module but has no DT node. Is HDMI audio driven entirely from the fabric on this board, making the SPI audio link unnecessary, or is audio simply not implemented yet? +- Whether the ascal-scratch/framebuffer split at 0x9e000000 / 0xa0000000 is forced by hardware or is a consequence of his particular golden_top's fbcfg wiring. If it is a design choice, our port need not inherit the 64 MiB of reservations. +- Does 'a certain number of cores' refer to the 8-core roster limit (CORES.manifest) or the (claimed-fixed, unverifiable) >2MB BL31 buffer wall, or both? Ambiguous, not disambiguated by any source available to us. +- Is the claimed 'NES live-switches in ~3s' result (script header, referencing an analysis file not present in either repo we have access to) still valid, given the same-day 30d9c99a8 commit shows a fresh standalone HW test of the reconfigure mechanism failing? Could not resolve. +- Does the 'FPGA_CONFIG_BUFFER_SIZE'/'BUF4 BL31' fix he describes actually exist and do what the comments claim? The ATF/arm-trusted-firmware source is not among the repos we were given — unverifiable from here. +- Why does our own linux-6.18.44 baseline lack the agilex5-svc/soc-fpga-mgr/fpga-region DT nodes that his 6.18.38-based tree (a numerically earlier stable point release) already has? Worth checking directly against a plain kernel.org 6.18.44 tarball rather than assuming either tree's provenance. +- Was the factory Terasic FSBL ever tested against a non-custom (mainline-built) u-boot.itb? No evidence either way in the materials available. +- What is the real Rev A vs Rev B silicon stepping of the DE25-Nano boards our project would target, given the Intel Rev-A-only U-Boot workaround (2030244a1e) is conditional on that? +- His record is internally contradictory about whether a no-reboot core switch was EVER achieved. 7fb52ac3f (2026-07-13) claims 'HW-proven a2600<->NES round trip in 16-18s' via the configfs overlay; 881d4404a (2026-08-01) says that same write 'had ALWAYS failed here, structurally, since before this file's own history begins'. The reconciliation the dates support is that the July tests ran on Terasic's stock 6.12.11 kernel (CONFIG_OF_CONFIGFS=y [V]) and the August finding is about his mainline 6.18 port, which first booted on hardware 2026-07-24 [V]. Worth asking him directly — because if the 16-18 s round trip is real, it is the single most valuable data point in existence for our D0.2 §6, and if it is not, nobody has yet switched a core on Agilex 5 without a reboot. +- Which symptom did the owner's friend actually report, and from which era? '~35 s reboot per switch' (launcher tier, 2026-07-13 onward), 'minutes per switch' (RSU/FCS staging tier, before that), and 'only small cores load' (the 2 MiB svc-buffer wall) are three different experiences of three different designs. The second-hand phrasing 'a certain number of cores into firmware' matches the RSU tier specifically. +- The backing artifacts for every timing claim are absent from the shared repos: analysis/SUB10S_ATF_KERNEL_DIVERGENCE.md, the whole memory/*.md set referenced by name in a dozen script comments, com9_coldboot_throttle_validate.log / com9_warmreboot_throttle_validate.log, and analysis/boot-hf-launcher-production.cmd. If he can share the COM9 serial logs and the SUB10S analysis, that is the only path to closing U5 without our own hardware. +- Exactly what his BL31 'FPGA_CONFIG_BUFFER_SIZE=16' modification changed, and whether stock ATF (2.12.0 per Terasic's build record, or 2.14) has the same behaviour. The kernel-side 2 MiB geometry is [V] mainline; the ATF side is his prose only, and neither reference repo contains ATF source. If the BUSY/BUFFER_DONE contract is fragile in stock ATF too, the 2 MiB wall becomes OUR problem and not just his. +- Is the DE25's QSPI actually 16 MiB or 256 MiB? The Terasic U-Boot DTS says mt25qu02g (256 MiB) but the node is verbatim SoCDK boilerplate; his RSU layout terminates exactly at 16 MiB. This bears directly on our Q4 ('Does RSU fit in 16 MB?') — if the part is really 2 Gbit, that question changes shape entirely. Settle it against the DE25 UM/schematic or an on-board `sf probe`, not against a copied DTS node. +- Whether Terasic's FACTORY QSPI (as opposed to his replacement .jic) enforces bitstream authentication. His unsigned-RBF success is on a board whose QSPI he overwrote; U4 is answered for the silicon but not strictly for the factory configuration. +- Whether the fpga-region overlay path survives DOZENS of apply/remove cycles — our §3.2's 'no mainline user at all' concern. His design deliberately re-applies a STATIC overlay via of_overlay_remove() + of_overlay_fdt_apply() each time, which is exactly that pattern, but he has not run it end-to-end even once. +- Whether the golden/MENU-in-QSPI posture he settled on is compatible with our posture 1 at all: does Terasic's FACTORY .jic configure a fabric that a MiSTer MENU can run on, or does reaching the MENU inherently require replacing the QSPI fabric image? If the latter, posture 1 is only viable for the L1 developer OS and DP-1 must be re-opened for the MiSTer image. Nothing in either repo answers this — he never ran on the factory .jic. +- What exactly is the 'throttle'/'budget depleted' mailbox issue referenced in commit 30d9c99a8? The memory files it cites are not in this repo snapshot (they live in an external monorepo we were not given), so its root cause, frequency, and whether it is deterministic or transient remain unknown to us. +- Does Terasic's factory-shipped (unmodified) SPL/FSBL accept a mainline-built u-boot.itb at all (U3)? Neither this task nor his repo has ever tested it -- he replaced the SPL before first boot. This is the single biggest open item for our posture-1 plan and is not answerable from either reference repo; it needs a real board running the untouched factory .jic. +- Is his board's permissive VAB state (U4) representative of the DE25-Nano product line generally, or could Terasic ship (or a customer provision) units with VAB fused? His evidence is single-unit and structural (an unsigned .jic boots), not a statement about eFuse policy across the SKU. +- Does the mmc0/SMMU F_TRANSLATION regression (6.12->6.18 window) have a known upstream fix, a tracked kernel bug, or a narrower root cause than 'somewhere in arm-smmu-v3 or DMA-mapping'? He explicitly deferred bisection; this is worth a targeted search before our own D0.3/D2 work reaches the same wall. +- What is the actual current state of end-to-end live core switching as of his latest commits (881d4404a, 2026-08-01)? SETUP.md (dated 2026-07-04) says core switching is not wired up; later commits show it working in principle (mechanism HW-confirmed) but the most recent standalone test of the new trigger driver hit a mailbox timeout. Whether a full menu-driven live switch has been demonstrated end-to-end with all fixes (agilex5 compatibles + de25_fpga_trigger + BUF4 BL31) applied together is not established from what's in this repo. +- Does his DP-1-relevant Terasic stock image (de25_nano_revA_sdcard_console_v1.1.img) represent a genuine board-support reference analogous to what DE10's stock-shadowing model needs, or is it materially different in kind (a generic Linux console BSP vs. a running MiSTer install)? This bears on whether DP-1's premise needs revisiting and by how much -- flagged, not resolved, per the task's standing instruction not to decide DP-1 here. + +--- + +## 6. What still needs doing + +1. ~~**Re-run the killed legs**~~ — **DONE / MOOT, 2026-08-22.** Leg 4's U-Boot half was re-run and + is folded in as [§7](#7-leg-4-u-boot--filled-in-2026-08-22). Its posture half is **moot**: the + owner settled the card layout and the QSPI posture (two partitions, p1 FAT, factory SPL + untouched, Linux never writes QSPI), so there is no posture-1-vs-posture-2 call left to make. +2. **Adversarial verification.** **PARTIAL, 2026-08-22** — see + [§8](#8-verification-record-2026-08-22). The load-bearing platform claims the successor document + leans on were independently checked; most held, two failed and are corrected in place. **The + brick-adjacent and flash-path claims (FCS/QSPI staging, the SError/LWH2F wedge, the RSU slot map) + were NOT re-checked** and still do not meet the bar. +3. **The posture-1 gate remains open (D0.1 Q3).** Nobody has booted a mainline-built `u-boot.itb` + from Terasic's *factory* SPL — his repo is structurally unable to answer it, since he replaced + the SPL. The best indirect evidence: his own defconfig sets `CONFIG_SPL_FIT_SIGNATURE=y` and he + loads locally built **unsigned** FITs successfully, which is the same U-Boot code path and is + consistent with a control DTB that requires no keys **[V]**. + + +--- + +## 7. Leg 4 (U-Boot) — filled in 2026-08-22 + +The leg that never ran. Its brief had two halves. **The posture half is moot**: the owner has since +settled the QSPI posture and the card layout — two partitions (p1 FAT, p2 everything else), the +factory SPL in QSPI untouched, and Linux never writing QSPI — so there is no posture-1-vs-posture-2 +cost analysis to write. What follows is the technical half, and it is recorded here as reference +material in the same spirit as the rest of this document. The decision-bearing write-up, with the +build fragment, lives in [`de25-implementation-path.md`](de25-implementation-path.md) §6. + +**Note on evidence standard.** These findings were produced by a single leg on 2026-08-22 and are +**mostly not adversarially verified** — the exceptions are the four items explicitly marked +"re-verified" below, which were fetched from `u-boot/u-boot@v2026.07` and read during the +verification pass. Everything else here carries this document's usual single-agent standard. + +**What his tree is, for comparison [V].** U-Boot **2025.01**, branch `de25-mister-exfat-boot`. The +`u-boot.itb` he pulled off the factory board identifies as `2025.01-gd0f8813fd6bf` with +`vendor=terasic` — Terasic shipped a build of this very tree at its "add support for de25-nano" +commit, and his own build carries the identical version string, so he never moved off 2025.01. His +defconfig sets `CONFIG_SPL_FIT_SIGNATURE=y` (as mainline's agilex5 defconfig does) yet he loads +locally built **unsigned** FITs successfully; the SPL FIT load contract is visible as +`CONFIG_SPL_LOAD_FIT_ADDRESS=0x82000000`, `CONFIG_SYS_SPI_U_BOOT_OFFS=0x04000000`. + +**1. There is no DE25-Nano in mainline U-Boot [V].** `board/terasic/*` at `v2026.07` has +de0-nano-soc, de1-soc, de10-nano, de10-standard and sockit — no de25 — and no `configs/*de25*` +exists anywhere in the 40,995-path tree. We carry the board fragment ourselves; there is nothing to +select. Mainline's generic `socfpga_agilex5_defconfig` is the starting point. + +**2. `u-boot.itb` comes from binman, not the legacy Makefile rule [V].** The legacy `u-boot.itb:` +rule is gated on `U_BOOT_ITS`, set only under the deprecated `SPL_FIT_GENERATOR`. `ARCH_SOCFPGA_AGILEX5` +`select`s `BINMAN if SPL_ATF`, and the defconfig sets `CONFIG_SPL_ATF=y` *(re-verified: line 54 of +`socfpga_agilex5_defconfig@v2026.07`)*, so `make all` runs binman against +`arch/arm/dts/socfpga_soc64_fit-u-boot.dtsi`. The resulting FIT carries: `uboot` = +`u-boot-nodtb.bin` (standalone, arm64, load `0x80200000`); `atf` = `bl31.bin` +(`os=arm-trusted-firmware`, load = entry = `0x80000000`); `fdt-0` = `u-boot.dtb`; and one config +node `board-0` marked `default`, signed only with a `crc32` integrity stamp — no keys. That last +detail is consistent with this document's §2 U3/U4 finding that the factory SPL's control DTB +requires no signing keys. + +**3. Board-ID matching cannot lock us out [V].** `board_fit_config_name_match()` *is* compiled in +for SOC64 (`arch/arm/mach-socfpga/board.c:148-158`) and matches each config node's **`description`** +— not its node name — against `"board_%u"` from `socfpga_get_board_id()`. `fit_find_config_node()` +falls back to `/configurations/default` when nothing matches. A single-config FIT therefore boots +correctly regardless of what board ID the SPL reports. This closes a residual that the original +posture-1 analysis had left open. + +**4. The env configuration is a latent QSPI-write hazard, and the guard is a defconfig line, not +discipline [V, re-verified].** Mainline's `socfpga_agilex5_defconfig@v2026.07` sets **both** +`CONFIG_ENV_IS_IN_FAT=y` (`:72`) and **`CONFIG_ENV_IS_IN_UBI=y`** (`:73`), with +`ENV_FAT_DEVICE_AND_PART="0:1"` (`:74`), `ENV_UBI_PART="root"` (`:75`), `ENV_UBI_VOLUME="env"` +(`:76`) *(all four lines re-verified by fetching the defconfig)*. The hazard is **not** `saveenv` — +it is `env_load()`. `env_ubi_load()` calls `ubi_part()` **unconditionally** at +`env/ubi.c:128` whenever the FAT load fails *(re-verified: `env/ubi.c@v2026.07` fetched and read; +`ubi_part()` at `:128` inside `env_ubi_load()` at `:107`)*. Against a **fully erased** MTD partition +`ubi_attach()` *succeeds* (`ai->is_empty = 1`), and `ubi_read_volume_table()` then calls +`create_empty_lvol()` → `create_vtbl()`, **writing a fresh UBI layout volume into QSPI**. So: a +missing or corrupt FAT env plus a blank `root` MTD writes QSPI on the first boot, with no user +action. **The guard is `# CONFIG_ENV_IS_IN_UBI is not set` in our fragment.** Nothing in +`ARCH_SOCFPGA_AGILEX5`/`ARCH_SOCFPGA_SOC64` selects it — it is a plain defconfig choice. This +promotes [`de25-readiness-ledger.md`](de25-readiness-ledger.md) row 12 from **[U]** to +**[V, code-traced]**. + +**5. `CONFIG_SPL` looks droppable [U].** Nothing forces it on: `ARCH_SOCFPGA_AGILEX5` selects +`BINMAN if SPL_ATF`, `CLK`, `FPGA_INTEL_SDM_MAILBOX`, `SPL_CLK if SPL`, and `ARCH_SOCFPGA_SOC64`, +and none of the binman FIT images references anything under `spl/`. So `# CONFIG_SPL is not set` +should genuinely eliminate SPL compilation while still emitting `u-boot.itb`. **Reasoned from the +Kconfig graph, not build-tested** — first thing to check at first build. + +**6. The exFAT blocker has expired [V].** Mainline U-Boot has real exFAT — `fs/exfat/`, +`CONFIG_FS_EXFAT` ("read/write support") — added by commit `b86a651b64` on **2025-03-17**, i.e. +*after* his 2025.01 base. That is exactly why he had to hand-roll `libexfat` and a custom +exFAT-aware SPL, and it is why that whole workstream is **not** something we inherit: on current +mainline the equivalent is one defconfig line. Note the stock defconfig enables `SPL_FS_FAT` (`:22`) +but not `FS_FAT`/`CMD_FAT`/`FS_EXFAT` for U-Boot proper *(re-verified)*. This removes a capability +gap; it does not by itself decide p2's filesystem. + +**7. Version pairing, and the honest caveat [V for versions, U for the pairing].** Mainline +**v2026.07** is current stable (v2026.10 is at -rc2, due 2026-10-05), and mainline **TF-A v2.15.0** +does carry `plat/intel/soc/agilex5/`. **That pairing is unblessed and untested by anyone we know +of** — Terasic and Altera document only vendor forks (`u-boot-socfpga socfpga_v2023.10` + +`arm-trusted-firmware socfpga_v2.10.0`), and he stayed on 2025.01 throughout. Whether a stock +v2.15.0 BL31 boots this board, and whether it handles >2 MB reconfiguration without his +`FPGA_CONFIG_BUFFER_SIZE` modification, is open. + +**What Leg 4 does *not* answer.** It does not close U3 — nobody has booted a mainline-built +`u-boot.itb` from Terasic's **factory** SPL, and no amount of source reading can. That remains the +single biggest open item for posture 1 and needs a real board on an untouched factory `.jic`. + +--- + +## 8. Verification record (2026-08-22) + +A separate run put this document's load-bearing platform claims through the adversarial pass that +the original run never got to. **Scope: the subset that the successor document +[`de25-implementation-path.md`](de25-implementation-path.md) leans on.** Claims not listed here were +not re-checked and still carry the original single-agent standard. + +### Independently verified — claims that HELD + +| Claim (where it lives here) | Outcome | How it was checked | +|---|---|---| +| Mainline 6.18.44's `socfpga_agilex5.dtsi` has **no** `mmc0`, `smmu`, `svc`, `fpga-mgr` or `fpga-region` node (§2 U1, §3) | **VERIFIED** | Case-insensitive `grep` for `mmc\|sd4hc\|sdhci\|smmu\|iommu` over the 826-line file → **zero hits**; only `service_reserved` svcbuffer at `:23`, `clkmgr` at `:144-148`, QSPI at `:477`, three stmmac compatibles at `:491,:603,:715` | +| Neither `stratix10-svc.c` nor `stratix10-soc.c` matches an agilex5 string in 6.18.44, and both tables carry **no `.data`** (§2 U1, §3) | **VERIFIED** | Tables read at `stratix10-svc.c:1133-1137` and `stratix10-soc.c:448-452`; `grep` for `of_device_get_match_data`/`of_device_is_compatible`/`match->data` in both files → zero hits. Also re-verified at **v7.2** (`:1911-1915` and `:448-452`) and at `master` — still no agilex5 string | +| His fix is exactly two one-line match-table additions (§2 U1, §3) | **VERIFIED** | `de25-linux:drivers/fpga/stratix10-soc.c:451`, `drivers/firmware/stratix10-svc.c:1134` | +| The fault that stopped his one Linux-path reconfiguration attempt: mailbox timeout, board wedged (§2 U2/U5, §3) | **VERIFIED** | Re-read here at `:115`; consistent across Legs 1/2/3/5 | +| `stratix10-soc.c` allocates `NUM_SVC_BUFS 4 × SVC_BUF_SIZE SZ_512K` = 2 MiB in flight (§1a cap 2, §3) | **VERIFIED** | `stratix10-soc.c:19-20`, allocation loop at `:215-216` | +| `fpga_manager` class devices expose **no** writable attribute; `fpga_region` exposes only RO `compat_id`; `OF_CONFIGFS` is not in mainline (§2 U2, §3) | **VERIFIED** | `fpga-mgr.c:655-664` (three `DEVICE_ATTR_RO`, and `fpga_mgr_attrs[]` holds exactly those); `fpga-region.c:175`; `drivers/of/Kconfig` has only `OF_OVERLAY:105` and `OF_OVERLAY_KUNIT_TEST:116`; no `drivers/of/configfs.c` | +| Reconfiguration quantizes to ~1 s from two independent code paths (§1b, §3) | **VERIFIED** | `de25-uboot-socfpga:drivers/fpga/intel_sdm_mb.c:20-21` (60 s timeout, 1 s interval, `udelay(1 s)` before the first poll); kernel `stratix10-svc.c:295` `msleep(1000)`, 30 s ceiling. Caveat kept: the kernel sleep fires only if the first poll is not already complete, so "~1 s practical floor" is the right framing — which is what this document already said | +| The mmc0/SMMU `F_TRANSLATION` fault itself is real, not a warm-reboot artifact (§3) | **VERIFIED** | Commit `716559020`'s retract-of-a-retraction; the "clean" disproof boot had `sdhci.debug_quirks=0x60` silently baked in by a JTAG-recovery script default | +| MiSTer-scale A5EB013 bitstreams are 1.9–3.6 MB (§1a cap 3, §3) | **VERIFIED** | `board_overlay/media/fat/cores/CORES.manifest`: menu 1,929,216 … ao486 3,645,440. Minor discrepancy noted: the NES figure quoted in prose (3,325,952) vs the manifest's 3,346,432 — different build cutover, immaterial to the >2 MiB arithmetic | +| The friend's SD path runs an **unmodified** mainline `sdhci-cadence` (§3) | **VERIFIED** | `diff -q` against `linux-6.18.44` → identical. Qualifier added by the pass: it runs with `sdhci.debug_quirks=0x60` (PIO), so it proves binding and PIO, **not** the DMA path | + +### Independently verified — claims that FAILED and are CORRECTED + +| Claim | Outcome | Correction | +|---|---|---| +| "mmc0 DMA F_TRANSLATION-faults on 6.18 but not 6.12 with a byte-identical DT — **a kernel-version regression**" (§3, twice) | **FAILED — attribution wrong; fault real** | Corrected in place at both sites. The 6.12 baseline is Terasic's **vendor** tree, whose `sdhci-cadence` gives `intel,agilex5-sd4hc` a `SDHCI_QUIRK2_40_BIT_DMA_MASK` that exists nowhere in mainline; his 6.18 tree binds bare `cdns,sd4hc` and gets a 64-bit mask. Vendor-vs-mainline **driver** delta, not a version regression — and it therefore **travels forward to 6.19 and 7.2** | +| "Both match tables have no per-compatible `.data`, so the third entry is **functionally free**" (§2 Leg 3 U1, §3) | **HOLDS for mainline; MISLEADING as a general claim** | True of mainline at 6.18.44, v7.2 and `master` [V]. But Terasic's **vendor** `stratix10-svc.c` keys real behaviour off `of_device_is_compatible(node, "intel,agilex5-svc")` at `:3507`: IOMMU attach + IOVA carveout, `AGILEX5_SDM_DMA_ADDR_OFFSET 0x80000000` added to every buffer address sent to the SDM (`:59,:3244`), an `INTEL_SIP_SMC_SDM_REMAPPER_CONFIG` remapper bypass (`:3550-3551`), and a hard `-ENODEV` if the SMMU is absent. So the "per-compatible behaviour appears" falsifier is not hypothetical — it already exists downstream, and it is the concrete mechanism behind the mailbox timeout his one Linux-path attempt hit | +| ~~`drivers/input/mousedev.c` is missing `#include `~~ | **FAILED, refuted 2026-08-21** | Already annotated in place in §3. Left exactly as it was, as the worked example of why this pass mattered | + +### What the pass concluded about this document as a whole + +- **No contamination found.** The known-false mousedev claim did not spread. Every claim the + successor document leans on was re-checked; the two that failed did so for + **evidence-characterisation** reasons — attributing a fault to the wrong variable, and + generalising a mainline property to vendor trees — **not fabrication**. The document's `[V]`/`[U]` + tagging proved broadly honest under spot-check. +- **One framing this document got right and the successor initially got wrong:** that end-to-end + Linux reconfiguration has **never** succeeded on a mainline-driver path on this silicon. This + document says so plainly at `:115` and in §5. A draft of the successor briefly described the + trigger module as "running on real hardware"; that was corrected against this text. + +### Not re-checked, and still at the original standard + +Everything in §1 (the RSU slot map, the FCS staging arithmetic, the ~35 s launcher figure), the +brick-adjacent claims in §3 (SError on unconfigured fabric, the LWH2F post-config wedge, the quiesce +window, the QSPI-via-FCS constraint, the SD 25 MHz cap), the whole of §4 (decisions for the owner), +all of §5's open questions, and §7's Leg-4 findings apart from the four items marked "re-verified" +there. The timing numbers in particular remain unverifiable from here: their backing artifacts +(`analysis/SUB10S_ATF_KERNEL_DIVERGENCE.md`, the `memory/*.md` set, the COM9 serial logs) are absent +from the shared repos, as §5 already records. From 6ef98f7fb3f96e3da96b5047b53a82303b697f62 Mon Sep 17 00:00:00 2001 From: "Michael C. Ferguson" Date: Wed, 2 Sep 2026 00:21:20 -0500 Subject: [PATCH 4/4] =?UTF-8?q?docs(de25):=20address=20Copilot=20review=20?= =?UTF-8?q?on=20#132=20=E2=80=94=20reconcile=20file=20counts,=20qualify=20?= =?UTF-8?q?two-string=20dtbs=5Fcheck=20claim?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ledger intro said 55 files where §7's reconciliation table says 54; the hard-coded 51/55 split is replaced by prose so the headline cannot drift from the table again. fpga-reconfig's "free and forward-compatible" now says at runtime only, cross-referencing implementation-path §2.5, which records that dtbs_check warns on the two-string form today. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01FYjrnzEEu4HzJSPWNAaRvm --- docs/de25-fpga-reconfig.md | 8 ++++++-- docs/de25-readiness-ledger.md | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/de25-fpga-reconfig.md b/docs/de25-fpga-reconfig.md index 6cf12e7..f3e6b97 100644 --- a/docs/de25-fpga-reconfig.md +++ b/docs/de25-fpga-reconfig.md @@ -414,8 +414,12 @@ DTS patch **[V]**, added by the DE25 board DTS on top of the in-tree }; ``` -Writing the two-string form now is free and forward-compatible: 6.18.44 matches on the -fallback, a newer kernel matches on the specific string. +Writing the two-string form now is free and forward-compatible **at runtime**: 6.18.44 +matches on the fallback, a newer kernel matches on the specific string. It is **not** +schema-clean today — the fpga-mgr binding has not adopted the `items`/`oneOf` form, so +`dtbs_check` warns on the two-string compatible until Khairul's series lands (see +[`de25-implementation-path.md`](de25-implementation-path.md) §2.5 and §8 Q4). Runtime +binding and CI cleanliness are separate claims; only the first holds now. Note what is *not* there: no `fpga-bridges` property, and no bridge nodes at all — the Cyclone V shape (`fpga_bridge0..3` at `0xff400000`/`0xff500000`/`0xff600000`/`0xffc25080` diff --git a/docs/de25-readiness-ledger.md b/docs/de25-readiness-ledger.md index 1928c24..dd4f0d9 100644 --- a/docs/de25-readiness-ledger.md +++ b/docs/de25-readiness-ledger.md @@ -16,7 +16,7 @@ DP-2 depends on). ## 1. Why this document exists, and what it is *not* A fresh grep for `de10nano|BR2_arm|zImage` outside `board/mister/de10nano/` and `docs/` hits -**55 files and 457 lines** (§7). Almost all of them are a **path** or a **name**. Exactly four +**54 files and 457 lines** (§7). Almost all of them are a **path** or a **name**. Exactly four things in this repo are *semantically* ARM32/Cyclone-V — they encode a fact about the silicon or the boot ROM that has no aarch64 analogue at all **[V** ADR 0027:42-46**]**: @@ -27,13 +27,13 @@ or the boot ROM that has no aarch64 analogue at all **[V** ADR 0027:42-46**]**: | (c) | `^BR2_arm` / `^BR2_cortex` asserts | `.github/actions/buildroot-build/action.yml:190` **[V]**, `scripts/check-kernel-defconfig-sync.sh:83,129` **[V]** | Buildroot's arch symbols are mutually-exclusive Kconfig *choices*, not values a variable can carry | | (d) | armv7-pinned `package/azcopy` | `package/azcopy/Config.in:64` (`depends on BR2_arm`) **[V]** | Gates a Go cross-build that only exists for 32-bit ARM | -Everything else — 51 of the 55 files — says `de10nano` because that is where the board's files +Everything else — the other fifty-odd files — says `de10nano` because that is where the board's files *live*, or says `zImage_dtb` because that is what today's only kernel artifact is *called*. Those are cheap: a `BOARD=` variable, or a second sibling file, fixes them at the moment someone touches them. **Collapsing the two classes together is the failure mode this ledger exists to prevent** — it would either (i) invite someone to "generalize" the zImage header parser, which is not generalizable, or (ii) inflate the DE25 port's apparent cost from four -real design decisions to fifty-five. +real design decisions to fifty-four. **Decision 3's obligation, restated:** we do not pre-build DE25 plumbing. We stop *deepening* the four. Any newly written or substantially rewritten script/CI step that would hard-code @@ -435,4 +435,4 @@ counted twice. real and belongs in D2's estimate, not discovered during it. Nothing in the sweep contradicted the four couplings, and no *fifth* semantic coupling was -found: every one of the 51 non-(a)-(d) files is a path, a name, or prose. +found: every one of the remaining non-(a)-(d) files is a path, a name, or prose.