Resolved SS V.1's flagged gap: standard RISC-V SBI boot protocol, confirmed via OpenSBI's own docs -- a0=hart ID, a1=DTB pointer, S-mode entry, universal across FW_DYNAMIC firmware regardless of vendor, not chain-specific guesswork. Decided: observation is HDMI-only, same reasoning and constraint as the Pi 5 (no bridge hardware for this board's own first bring-up either). Traced boot_info->acpi_table's real riscv64 consumers the same way as aarch64: pci_init() again (Mars's M.2 slot is PCIe-attached, same shape of gap as the Pi 5's RP1); timer.c is already fully DTB-driven, no work needed there. One real, already-flagged risk found while tracing this: PLIC_BASE is a QEMU-virt-specific constant, not DTB-discovered -- arch/riscv64/ plic.c's own doc comment already warned about this; it becomes concrete now that real hardware is actually in scope. Real punch-list item, not hypothetical. 6-item no-hardware-needed punch list (entry stub, DTB->BootInfo constructor, DTB-discovered PLIC base, unresearched JH7110 framebuffer flagged honestly rather than assumed, shared pci_init() DTB path with the Pi 5, image-packaging tooling) plus 5 items deferred to 2026-09-17. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
568 lines
39 KiB
Markdown
568 lines
39 KiB
Markdown
# FABRIC-3.md — bare metal boot
|
||
|
||
**Status:** Living working document, opened 2026-09-04 as the successor to `FABRIC-2.md`
|
||
(now closed/archival — see its own header). Topic for this document, per direct instruction:
|
||
**bare metal boot** — getting LithosAnanke to actually boot on real hardware, not just QEMU.
|
||
`FABRIC-2.md` §I.6 (Milestone 8) already named this as the one item that pass couldn't close
|
||
from a coding session at all, for exactly this reason — it needs a real machine and a human
|
||
physically present. This document is where that work, and everything downstream of it, gets
|
||
tracked.
|
||
|
||
**How to use this document going forward.** New findings, new punch-list items, and new
|
||
decisions for bare-metal-boot work get added here, not to `FABRIC-2.md`. Same discipline every
|
||
prior document in this series used: write the decision and its reasoning down before building,
|
||
close items with a dated note citing real evidence, never silently drop a stale claim.
|
||
|
||
---
|
||
|
||
## I.1 — Task 1: merge `v2.0.1` into `master`, verify build/function equivalence
|
||
|
||
**Written up before executing**, per direct instruction and this series' own standing
|
||
discipline.
|
||
|
||
**Why this is task 1.** `FABRIC-2.md`'s entire 7-step closure pass (§I.1–§I.5, §I.7, plus
|
||
today's FABRIC-series rename) happened on the `v2.0.1` branch, not `master`. Before any real
|
||
bare-metal-boot work starts, that work needs to land where `.claude/CLAUDE.md` says the
|
||
project's sole production line actually lives: `master`. Doing this first, cleanly, before
|
||
starting new work avoids ever having two divergent lines to reconcile later.
|
||
|
||
**Investigated before writing this up, not assumed:**
|
||
- `git merge-base --is-ancestor master v2.0.1` — **true**. `master` (local HEAD `d2a0305`) is
|
||
a strict ancestor of `v2.0.1` (HEAD `b031b80`) — `v2.0.1` is exactly `master` plus 47 commits
|
||
forward, no divergent history on either side. This means the "merge" is a pure **fast-forward**,
|
||
not a real three-way merge — nothing to resolve, no conflict possible.
|
||
- `origin/master` carries exactly one commit beyond local `master` (`58c59e8`, "Initial
|
||
commit") that local `master` hadn't fetched yet — confirmed already contained in `v2.0.1`'s
|
||
own history (`git merge-base --is-ancestor 58c59e8 v2.0.1` — true), so it introduces no
|
||
discrepancy either.
|
||
- `master`'s own tree still has the *old* `FABRIC.md`/`FABRIC-2.md`/`FABRIC-3.md` naming
|
||
(unrenamed) — expected, since today's rename commit (`b031b80`) only exists on `v2.0.1` so
|
||
far. The fast-forward brings the rename to `master` along with everything else; nothing
|
||
separate needs doing for it.
|
||
|
||
**Plan:**
|
||
1. Fast-forward `master` to `v2.0.1`'s tip (`git checkout master && git merge --ff-only v2.0.1`)
|
||
— refuses loudly instead of silently doing a real merge if the ancestor relationship somehow
|
||
isn't what the investigation above found, so this step re-verifies its own precondition.
|
||
2. Push `master` to `origin`.
|
||
3. **Verify build/function equivalence on a genuinely clean tree**, not by inference: `git clean`
|
||
(after confirming nothing untracked-but-wanted is present), then the full acceptance sequence
|
||
`.claude/CLAUDE.md` already mandates for any kernel change — `clean qemu` on all three
|
||
architectures, in the foreground, one at a time, each reaching `ok>` and shutting down
|
||
cleanly. Since the tree is byte-identical to `v2.0.1`'s post-fast-forward, this is expected
|
||
to reproduce exactly what `v2.0.1`'s own last acceptance pass already showed — the point of
|
||
re-running it here is to confirm that expectation holds on `master` itself, not to assume it
|
||
from the fast-forward alone.
|
||
4. Return to `v2.0.1` as the working branch afterward (`.claude/CLAUDE.md`'s own rule: always
|
||
return to the correct working branch after any out-of-branch work), unless told otherwise.
|
||
|
||
**DONE 2026-09-04, exactly as planned:**
|
||
1. Committed the write-up above on `v2.0.1` first (`72c14cb`), pushed. This became `v2.0.1`'s
|
||
new tip.
|
||
2. `git checkout master && git merge --ff-only v2.0.1` — **Fast-forward**, `d2a0305..72c14cb`,
|
||
confirming the investigated ancestor relationship held exactly as expected; no conflict, no
|
||
merge commit.
|
||
3. `git push origin master` — `origin/master` moved `58c59e8..72c14cb`.
|
||
4. **Verified on a genuinely clean `master` tree**, not inferred from the fast-forward:
|
||
- Hosted build (`make clean && make`): clean compile, zero warnings, same as `v2.0.1`.
|
||
- Full 3-arch kernel acceptance (`clean qemu`, amd64/aarch64/riscv64, each in the foreground):
|
||
all three reached `(zuse) ok>`/`ok>` and shut down cleanly, zero build errors, zero
|
||
unexpected warnings — identical outcome to `v2.0.1`'s own last acceptance pass, confirmed
|
||
directly rather than assumed. Logs: `logs/20260904-113208/amd64/`,
|
||
`logs/20260904-113320/aarch64/`, `logs/20260904-113552/riscv64/`.
|
||
5. `master` and `v2.0.1` are now identical (`72c14cb` on both, `origin` and local). Returned to
|
||
`v2.0.1` as the working branch per plan step 4.
|
||
|
||
**Task 1 closed.** `master` genuinely is the production line again, current through today's
|
||
FABRIC-series rename and the full `FABRIC-2.md` §I closure. Bare-metal-boot work (this
|
||
document's actual topic) starts from here.
|
||
|
||
## I.2 — Task 2: version correction — the `v2.0.1` bump and `v2.0.0` tag were premature
|
||
|
||
**Direct instruction, 2026-09-04**: the `LITHOS_VERSION` bump to `2.0.1` (and the branch name
|
||
that followed it) got ahead of the real state — per `Makefile.starkernel`'s own versioning
|
||
policy (`v2.0.0` = QEMU release, even major/LTS; `v2.0.1` = the SER5 hardware-track *line*,
|
||
RDRAND backend + thumbdrive image goal), claiming `2.0.1` implies hardware-track progress that
|
||
was never actually verified on real hardware — that verification is precisely `FABRIC-3.md`'s
|
||
whole open topic (§I.6 in the closed `FABRIC-2.md`). The current `master` HEAD is, correctly,
|
||
still a `v2.0.0`-class QEMU-only release. "Nothing harmful" — a version-label correction, not a
|
||
functional rollback.
|
||
|
||
**Found and fixed while correcting this, not left half-done:**
|
||
- A real gap in the FABRIC-series rename from earlier today: `Makefile.starkernel`,
|
||
`Kconfig.kernel`, `scripts/bleach_zuse_img.sh`, four `proof/*.thy` files, and
|
||
`src/starkernel/arch/amd64/isr.S` all still had stale `FABRIC.md`/`FABRIC-2.md`/`FABRIC-3.md`
|
||
citations — the original sweep's file-list only matched `--include=*.md/*.c/*.h/*.4th`, which
|
||
silently skipped every file without one of those four extensions. Found by re-grepping with
|
||
the extensions excluded instead of included. Fixed with the same safe placeholder-substitution
|
||
technique the original rename used (each file, one pass, ordered `FABRIC-3→2→1→0` placeholders
|
||
then resolved) — verified no double-shifted or broken references remained afterward.
|
||
`.claude/settings.local.json`'s own historical Bash-permission-grant log (literal past command
|
||
strings naming the file as it was called *at the time*) was deliberately left alone — rewriting
|
||
it would falsify an audit trail, not fix a stale citation.
|
||
- `ClaudeEXPORT/memories.json`/`conversations.json` also still reference the old names — left
|
||
untouched on purpose, same reasoning as the memory note on that archive: it's a frozen export,
|
||
mining material, not live documentation to keep in sync.
|
||
|
||
**Changes:**
|
||
1. `Makefile.starkernel`: `LITHOS_VERSION ?= 2.0.1` → `2.0.0`.
|
||
2. The rename-gap fix above (7 files).
|
||
3. Verified 3-arch boot (`clean qemu`, amd64/aarch64/riscv64, each in the foreground): all three
|
||
show `LithosAnanke v2.0.0` in the boot banner (confirmed directly in each serial log, not
|
||
assumed from the Makefile edit alone), zero build errors, zero unexpected warnings, clean
|
||
shutdown.
|
||
4. Moved the existing `v2.0.0` git tag (previously at `2efd7fe`, the original QEMU-release
|
||
milestone commit — that commit and its own message stay fully intact in history, only the
|
||
tag pointer moves) to the current `master`/`v2.0.1`-branch HEAD, per explicit instruction —
|
||
the prior tag placement was itself part of the same "got ahead of myself" correction, not a
|
||
separate decision. No remote tag existed yet (`git ls-remote --tags origin` was empty for
|
||
`v2.0.0`), so no destructive remote operation was needed, only a local move-and-push.
|
||
5. **Follow-up, same day**: `v2.0.1` (the working branch this and Task 1 happened on) deleted,
|
||
local and `origin` — confirmed a strict ancestor of `master`'s new HEAD first, so nothing
|
||
was lost. `master` is the repo's only branch from here on.
|
||
|
||
---
|
||
|
||
## II. Three architectures, three different hardware scopes
|
||
|
||
Per direct instruction, 2026-09-04. The real-hardware targets are **not** symmetric across
|
||
architectures — each gets its own section below because the actual scope of "done" is
|
||
different for each:
|
||
|
||
- **amd64 — genericity is the goal, not just the SER5.** The Beelink SER5 is the machine in
|
||
hand and the development/reference target, but the real requirement is broader: this needs
|
||
to boot on *any* x86_64 machine — laptop, desktop, tower, or mini PC — not just one vendor's
|
||
quirks. SER5-only success is necessary but not sufficient; anything that works only because
|
||
of an SER5-specific assumption (a particular ACPI table shape, a specific UEFI
|
||
implementation's quirks) is a bug against this goal, not a deferred nice-to-have.
|
||
- **aarch64 — Raspberry Pi 5, and only the Raspberry Pi 5.** No genericity requirement across
|
||
aarch64 boards — this is the one and only target for this architecture.
|
||
- **riscv64 — Milk-V Mars, and only the Milk-V Mars.** Same as aarch64: one specific board,
|
||
not a generic riscv64-SBC goal.
|
||
|
||
**How to use sections III–V below.** Same discipline as everything else in this series: plan
|
||
before building, one section at a time, iterating — not all three architectures in parallel,
|
||
and not front-loading a complete plan before any real hardware is in front of us. Each section
|
||
starts with what's already true (existing repo infrastructure, already-decided policy) and
|
||
what's still genuinely unknown, not assumed.
|
||
|
||
## III. amd64 — generic x86_64 bare metal (reference hardware: Beelink SER5)
|
||
|
||
**Already true, not to be re-derived:**
|
||
- `ROADMAP.md`'s "Board-by-board hardware rollout" already names this `v2.2.0`'s gate: the
|
||
generic GPT/FAT32 thumbdrive image (`make -f Makefile.starkernel ARCH=amd64 thumbdrive`,
|
||
already built — `Makefile.starkernel:1018`) flashes to and boots on the real SER5 via its
|
||
real UEFI, reaching POST + `ok>`, with the amd64 RDRAND entropy backend
|
||
(`src/starkernel/rng/rng.c`, already built and part of `master`) serving live entropy.
|
||
- `iso-usb` (`Makefile.starkernel:1060`) is the alternate, novice-friendly path (UEFI
|
||
isohybrid ISO for tools like GNOME Disks "Restore Disk Image...") — same underlying image,
|
||
different flashing UX.
|
||
- `FABRIC-2.md` §I.6's own 8-step physical-boot sequence (build ISO, identify the target
|
||
device, flash it, physically boot, decide an observation method, confirm POST, confirm
|
||
`ok>`, document) is the closest thing to an existing plan — but it predates the genericity
|
||
requirement and was written with no hardware in hand yet.
|
||
|
||
**Decided in conversation, 2026-09-04:**
|
||
- **Observation: HDMI (interactive) + serial (logged transcript), both.** The kernel's own
|
||
VT100 framebuffer console (`console.c`/`vt100.c`/`framebuffer.c`) already gives a real
|
||
interactive display over HDMI — no new code needed there. Serial capture, if the SER5
|
||
exposes a UART header, uses the Raspberry Pi's own GPIO UART as the USB-serial bridge
|
||
(already available hardware, not a purchase blocker) — this needs the SER5's own UART pins
|
||
physically identified first (not yet confirmed it has an accessible header at all).
|
||
- **Genericity is verified by standards-compliance, not a second machine** — no second x86_64
|
||
box is available right now. The bar is: nothing in the boot path may depend on an
|
||
SER5-specific assumption (a particular ACPI table shape, a specific UEFI implementation's
|
||
quirk) — argued by code audit against real UEFI/ACPI standards, not by testing on a second
|
||
board, until one becomes available. This is a real constraint on the punch list below (item
|
||
6), not a deferred nice-to-have.
|
||
- **Secure Boot: already disabled on this SER5.** No signed-loader work needed for this pass —
|
||
"Secure Boot disabled in firmware setup" is the supported path, documented as such rather
|
||
than built around.
|
||
|
||
**Punch list, this cadence's actual next steps:**
|
||
1. Build the generic thumbdrive image: `make -f Makefile.starkernel ARCH=amd64 thumbdrive`.
|
||
2. Flash it to a USB stick (`dd`, per the target's own existing usage message).
|
||
3. Physically inspect the SER5 for an exposed UART header/pins; if present, wire the
|
||
Raspberry Pi's GPIO UART to it as the serial bridge. If absent, HDMI-only for this pass —
|
||
not a blocker, just a scope note for step 7's log.
|
||
4. Connect HDMI + keyboard to the SER5.
|
||
5. Boot the SER5 from the flashed stick (firmware boot-order menu as needed — Secure Boot
|
||
already disabled, confirmed above, so no signing prompt expected).
|
||
6. **Code audit pass** (can happen before or in parallel with 1–5, doesn't need the hardware
|
||
in hand): review the amd64 boot path (`src/starkernel/boot/uefi_loader.c`,
|
||
`arch/amd64/*.c`) for anything that assumes SER5-specific hardware rather than standard
|
||
UEFI/ACPI — this is what "genericity" actually rests on per the decision above, not the
|
||
SER5 boot succeeding alone.
|
||
7. Capture the boot: confirm POST reaches the same `1012/0/0` result QEMU shows, confirm
|
||
`ok>`/`zuse)ok>`, confirm `rng: backend = rdrand` (live entropy, not the QEMU-only
|
||
`virtio-rng` path), save the serial transcript (if wired) the same way `logs/` already
|
||
keeps QEMU's.
|
||
8. Mint a Zuse identity on a second thumbdrive on the real SER5, confirm it re-attaches —
|
||
the same real-hardware round-trip `ROADMAP.md`'s `v2.2.0` gate already names.
|
||
9. Update this section with results — pass/fail per step, any SER5-specific or genuinely
|
||
generic-UEFI finding either way, before moving to aarch64.
|
||
|
||
## IV. aarch64 — Raspberry Pi 5
|
||
|
||
**Already true:** `ROADMAP.md` names this `v2.4.0`'s gate: boots on the real board, aarch64
|
||
peripheral-RNG backend live, Zuse mint/attach on real media. The peripheral-RNG backend itself
|
||
is **not yet built** — today's `rng_get_bytes()` (`src/starkernel/rng/rng.c`) only has a
|
||
`virtio-rng` path, real on QEMU, meaningless on real Pi 5 hardware (no virtio device there).
|
||
|
||
**Decided in conversation, 2026-09-04:**
|
||
- **Observation: HDMI-only for this board's own bring-up.** No second Pi, no dedicated
|
||
USB-serial adapter available. The Milk-V Mars could in principle serve as a GPIO-UART
|
||
bridge once it arrives (same 40-pin-header shape as the SER5 plan), but using it to observe
|
||
the Pi 5 before the Mars has been independently validated itself would be a chicken-and-egg
|
||
dependency, not a real plan. Revisit serial capture later if genuinely needed, once at least
|
||
one board is proven working — not a blocker for this pass.
|
||
- **Both boards (Pi 5, Milk-V Mars) arrive 2026-09-17.** Real runway exists to finish the
|
||
design/code work below *before* any hardware is in hand — "plan well before doing," per
|
||
direct instruction.
|
||
|
||
**Still genuinely open, not yet decided:**
|
||
- **A pinned GPIO VM, theory-stage** — see `FABRIC-4.md` §2. Raised in conversation, not yet
|
||
scoped; downstream of §IV.1's own native-boot-flow work (a GPIO VM needs GPIO addresses
|
||
from the DTB the same way the rest of this bring-up does).
|
||
|
||
### IV.1 — Boot-chain decision: UEFI vs. native, researched 2026-09-04
|
||
|
||
**Researched, not assumed** (web search, current as of this session):
|
||
|
||
**UEFI option investigated and found weak.** A real UEFI+ACPI firmware for Pi 5 exists —
|
||
[`rpi5-uefi`](https://github.com/worproject/rpi5-uefi) (TF-A + EDK2, SBBR-compliant). But:
|
||
it's **archived as of 2025-02-04**, support ended because newer Pi EEPROM firmware broke
|
||
compatibility with it; its own README says ACPI support is "under development and limited to
|
||
a few devices"; RP1 Ethernet/GPIO/PWM/EEPROM don't work under it. This kernel's whole
|
||
aarch64 boot path (`boot/uefi_loader.c`, `BootInfo->acpi_table`) assumes UEFI+ACPI the same
|
||
way amd64 and the QEMU aarch64 target do — but that assumption may not hold on a real,
|
||
current-firmware Pi 5 at all.
|
||
|
||
**Native boot flow — the real alternative, researched concretely:**
|
||
- Boot partition needs `bcm2712-rpi-5-b.dtb`, `config.txt`, and the kernel image itself —
|
||
Pi 5 firmware defaults to loading `kernel_2712.img`, falling back to `kernel8.img` if that's
|
||
absent.
|
||
- `config.txt` needs `os_check=0` for a non-Linux image, or the firmware assumes Linux and
|
||
loads from `0x200000` instead of the classic Pi bare-metal load address `0x80000`.
|
||
- Entry protocol: `x0` = 32-bit DTB pointer (upper 32 bits of the 64-bit register
|
||
unspecified — must mask before use), `x1`–`x3` reserved/zero. **No UEFI PE loader, no ACPI
|
||
at all** — a completely different entry shape from `boot/uefi_loader.c`.
|
||
- Framebuffer: the VideoCore **mailbox property interface** (channel 8) — a real, different
|
||
mechanism from UEFI GOP, no precedent anywhere in this codebase today.
|
||
|
||
**Decision, per direct instruction 2026-09-04: native boot flow.** Not UEFI. The archived,
|
||
partially-working UEFI project is too fragile a foundation to build a real-hardware release
|
||
on top of.
|
||
|
||
**What this actually means for the codebase, named honestly rather than estimated small:**
|
||
- A **new, non-UEFI entry path** for aarch64 real hardware — this kernel's boot sequence
|
||
currently assumes `uefi_loader.c`'s PE-loader shape unconditionally on aarch64; a Pi 5
|
||
native boot needs its own entry point (linked at `0x80000`, receiving `x0` = DTB pointer
|
||
directly, no `BootInfo` from UEFI at all).
|
||
- A **DTB-driven `BootInfo` equivalent** replacing ACPI-sourced data for this path — memory
|
||
map, peripheral addresses (UART, etc.) all come from the devicetree instead.
|
||
- **One real, genuine piece of reusable groundwork**: `starkernel/hal/fdt.c`/`fdt.h`, the
|
||
minimal FDT reader already built for riscv64's `timebase-frequency` lookup
|
||
(`arch/riscv64/timer.c`), is directly extensible for this — parsing `bcm2712-rpi-5-b.dtb`
|
||
for peripheral addresses is the same kind of lookup, not a new mechanism.
|
||
- A **new mailbox-property-interface framebuffer driver** — genuinely new code, no existing
|
||
precedent in this codebase, needed before the VT100 console framework
|
||
(`console.c`/`vt100.c`/`framebuffer.c`) has anything to draw onto for this board.
|
||
- This is a real architectural fork for aarch64, not a small per-board addition — QEMU
|
||
aarch64 keeps its existing UEFI+ACPI path unchanged; Pi 5 real hardware gets a second,
|
||
parallel entry path. **Not yet scoped into a punch list** — that's the next step, once this
|
||
fork's own shape (how much of `kernel_main.c`'s post-entry sequence can stay shared between
|
||
the two paths vs. needs its own branch) is thought through.
|
||
|
||
### IV.2 — Peripheral RNG: unresolved, not just under-researched
|
||
|
||
`ROADMAP.md` names an "aarch64 peripheral-RNG backend" as part of `v2.4.0`'s gate. Researched
|
||
directly rather than assumed still-TODO: Broadcom's `iproc-rng200` block (real, on Pi 4/BCM2711
|
||
as `brcm,bcm2711-rng200`) has **no `bcm2712` compatible-string entry anywhere in current
|
||
mainline Linux** (checked the actual driver's `of_device_id` table directly). The RP1
|
||
companion chip's own published peripheral list (GPIO/USB/Ethernet/DMA/ADC/PLLs/SRAM/
|
||
UARTs/SPIs) doesn't mention an RNG either. Two real possibilities, not yet distinguished:
|
||
BCM2712 still has the RNG200 block but Linux hasn't wired it into a devicetree binding yet, or
|
||
it genuinely isn't exposed to the ARM cores this generation. No public register address exists
|
||
to target right now — this needs either a Broadcom datasheet (if one becomes available) or
|
||
direct hardware probing once the board is in hand (scan the known BCM2711 RNG200 offset region
|
||
on the BCM2712 memory map and see if anything responds — risky without a datasheet confirming
|
||
it's safe to touch, so likely a "board in hand, careful probe" task, not a today task).
|
||
Deliberately **not a blocker for the first native boot** — reaching `ok>` doesn't require a
|
||
live entropy backend; `rng_get_bytes()` already has a "no entropy backend available" WARNING
|
||
path (`rng.c`) rather than a hard failure, so this can land after boot succeeds.
|
||
|
||
### IV.3 — Punch list: design/code work, no hardware needed (before 2026-09-17)
|
||
|
||
Traced against real code before writing this, not estimated: `boot_info->acpi_table`'s only
|
||
aarch64-relevant consumers today are `pci_init()` (`kernel_main.c:589`, unconditional, not
|
||
amd64-gated — relevant because RP1 is PCIe-attached on real Pi 5 hardware) and this session's
|
||
own `running_under_hypervisor()` (`arch/aarch64/timer.c`, already degrades safely to "not a
|
||
hypervisor" when `acpi_table` is `NULL` — no fix needed there).
|
||
`ioapic_init()`/`i8042_init()` are `#ifdef ARCH_AMD64`-gated, irrelevant here.
|
||
`arch/aarch64/apic.c` (GIC init) already only ever tries `boot_info->dtb`, never
|
||
`acpi_table` — its own doc comment already anticipated DTB-based discovery, just blocked
|
||
until now because QEMU's own UEFI firmware never publishes one; Pi 5 native boot removes that
|
||
blocker for free.
|
||
|
||
1. **Entry stub**: new `boot/native_rpi5_entry.S` (or similar) — linked at `0x80000`, receives
|
||
`x0` = DTB pointer per the researched protocol (§IV.1), minimal early setup (stack — either
|
||
a small fixed BSS region, matching `BootInfo.kernel_stack_base`'s existing "zero = fall
|
||
back to 2 MiB BSS stack" convention, or something new if that's insufficient this early).
|
||
2. **DTB → `BootInfo` constructor**: new C function populating the *existing* `BootInfo`
|
||
struct (`include/starkernel/uefi.h`) from the DTB instead of UEFI protocols — `dtb` = the
|
||
real pointer, `acpi_table` = `NULL` (already the correct value for "no ACPI," per IV's own
|
||
research above), `runtime_services` = `NULL`, `memory_map`/`framebuffer`/`args` populated
|
||
from DTB `/memory`+`/reserved-memory`, the mailbox interface (next item), and DTB `/chosen`
|
||
`bootargs` respectively. Then calls the **existing, unmodified** `kernel_main()` — this is
|
||
the crux of why most of M1–M9 stays shared.
|
||
3. **Mailbox-property-interface framebuffer driver** — genuinely new code (§IV.1's own
|
||
assessment), populating `BootInfo.framebuffer` the same shape UEFI GOP currently does, so
|
||
`console.c`/`vt100.c`/`framebuffer.c` need no changes at all downstream.
|
||
4. **`fdt.c`/`fdt.h` extension**: today's reader only supports "first match anywhere in the
|
||
tree" (`fdt_find_prop`'s own doc comment) — sufficient for the single global properties
|
||
used so far (riscv64's `timebase-frequency`), **not** sufficient for node-scoped lookups
|
||
(a specific peripheral's own `reg` address, e.g. UART or the mailbox registers) once more
|
||
than one node could plausibly define a same-named property. `fdt.h`'s own header comment
|
||
already flagged this as a known future need ("item 0.6 will need node-scoped `reg`
|
||
lookups... may extend this") — this is that extension, now with a real consumer.
|
||
5. **`pci_init()` DTB path**: a devicetree-based alternative for RP1 discovery, since
|
||
`boot_info->acpi_table` will be `NULL` on this path and RP1 is PCIe-attached, not directly
|
||
memory-mapped.
|
||
6. **`config.txt` contents, decided from research**: `kernel=kernel_2712.img` (or `kernel8.img`
|
||
with `os_check=0` if the Pi-5-specific name isn't used), `arm_64bit=1`, pointing at
|
||
`bcm2712-rpi-5-b.dtb`.
|
||
|
||
**Hardware-dependent, after 2026-09-17 (not started until then):**
|
||
7. Build the boot media (SD card: `config.txt`, `bcm2712-rpi-5-b.dtb`, kernel image).
|
||
8. Connect HDMI + keyboard (observation decision above).
|
||
9. Boot; confirm `ok>`/`zuse)ok>` reached.
|
||
10. Mint a Zuse identity on real media, confirm re-attach — the `v2.4.0` gate's own
|
||
requirement, same shape as amd64's.
|
||
11. Update this section with results before moving to riscv64's own hardware-dependent steps.
|
||
|
||
## V. riscv64 — Milk-V Mars
|
||
|
||
**Already true:** `ROADMAP.md` names this (generically, "Milk-V") as part of `v2.5.0`'s gate:
|
||
boots on the real board, the Zkr (RNDR) entropy backend live. Same gap as aarch64:
|
||
`rng_get_bytes()` has no riscv64 hardware-RNG path today, only `virtio-rng`.
|
||
|
||
### V.1 — Boot chain: resolved, researched 2026-09-04
|
||
|
||
**Resolved, not left open.** The Mars is a documented mainline U-Boot board target in its own
|
||
right ([U-Boot docs — Milk-V Mars](https://docs.u-boot.org/en/latest/board/starfive/milk-v_mars.html)),
|
||
and it uses **the exact same U-Boot binaries as the StarFive VisionFive 2** — same SoC
|
||
(StarFive JH7110), board identity detected at SPL time, devicetree patched accordingly, no
|
||
separate Mars-specific firmware. This directly answers §V's own previously-open question:
|
||
**U-Boot + OpenSBI + devicetree, not UEFI** — same fork this kernel already decided for
|
||
aarch64 (§IV.1), now confirmed for riscv64 too.
|
||
|
||
**Boot chain, concretely:**
|
||
1. BootROM (ZSBL), StarFive's on-chip loader at `0x2A000000`, selects boot media by GPIO pins.
|
||
2. U-Boot SPL (FSBL) — initializes DRAM, configures PLLs.
|
||
3. OpenSBI (`fw_dynamic.bin`) — M-mode runtime services.
|
||
4. U-Boot main, S-mode, depends on OpenSBI.
|
||
5. Boot media: QSPI flash (recommended) or UART XMODEM (recovery). SD/eMMC boot modes are
|
||
deprecated in current U-Boot.
|
||
|
||
**Entry protocol, from real VisionFive 2 bare-metal work (same SoC, directly applicable per
|
||
§VI's own cross-reference):**
|
||
- Entry point `0x40000000`.
|
||
- Core identification via the `mhartid` CSR — the SiFive S7 monitor core is hart 0, the four
|
||
U74 application cores are harts 1–4 (matches the QEMU riscv64 target's own hart numbering
|
||
convention already assumed elsewhere in this codebase — worth double-checking, not
|
||
assuming, once real hardware is in hand).
|
||
- UART at `0x10000000`, 115200 baud, already initialized by firmware before handoff.
|
||
- Custom bare-metal images package via `vf2-imager` (invokes U-Boot's `mkimage`) into a FIT
|
||
image — same tooling should apply to the Mars, unconfirmed until tried.
|
||
- **Not yet found**: what registers carry the DTB pointer/hart ID at the actual kernel entry
|
||
point under this specific chain (the source consulted covered the image-packaging tooling,
|
||
not the OpenSBI→kernel handoff register convention). **Resolved 2026-09-04**: standard
|
||
RISC-V SBI boot protocol, confirmed via OpenSBI's own docs — `a0`=hart ID, `a1`=DTB pointer,
|
||
S-mode entry. Not chain-specific guesswork; this is the universal convention OpenSBI's
|
||
`FW_DYNAMIC` firmware type uses regardless of vendor, so it applies to this chain directly.
|
||
|
||
**What this means for the codebase** — same shape of fork as aarch64 (§IV.1): a non-UEFI
|
||
entry path, a DTB-driven `BootInfo` equivalent (the existing `starkernel/hal/fdt.c` reader
|
||
extends here too, same as for the Pi 5), no ACPI.
|
||
|
||
**Decided in conversation, 2026-09-04: observation is HDMI-only**, same reasoning and same
|
||
constraint as the Pi 5 (§IV) — no bridge hardware available for this board's own first
|
||
bring-up either; the Mars has its own HDMI 2.0 output (§VI).
|
||
|
||
### V.2 — Peripheral RNG and Zkr: still genuinely open
|
||
|
||
- Zkr/RNDR instruction availability on the Mars's actual CPU (riscv64 Scalar Crypto extension
|
||
support varies by implementation) — not yet confirmed; the VisionFive 2 bare-metal research
|
||
above didn't surface this either, would need its own targeted look (or a real-hardware
|
||
probe of `misa`/the Zkr extension discovery mechanism). Deliberately **not a blocker for
|
||
first boot**, same reasoning as §IV.2's aarch64 RNG gap — `rng_get_bytes()` already
|
||
WARNs rather than hard-fails with no backend.
|
||
- **Whether the Mars needs the same pinned-GPIO-VM treatment as the Pi 5** — explicitly
|
||
**not decided either way**, per direct instruction ("same for Milk-V (? not sure here)").
|
||
See `FABRIC-4.md` §2. The Mars does have its own 40-pin GPIO header (§VI), so the open
|
||
question is the VM architecture around it, not whether the hardware exists.
|
||
|
||
### V.3 — Punch list: design/code work, no hardware needed (before 2026-09-17)
|
||
|
||
Traced against real code before writing this, same discipline as §IV.3: `pci_init()`
|
||
(`kernel_main.c:589`, unconditional) is the one real `acpi_table` consumer relevant here too
|
||
— the Mars's M.2 E-Key slot (§VI) is PCIe-attached, same shape of gap as the Pi 5's RP1.
|
||
`riscv64/timer.c` is **already** fully DTB-driven (both `timebase-frequency` and this
|
||
session's own hypervisor-detection check) — no further work needed there; it was built DTB-
|
||
first from the start, unlike aarch64's timer which needed a new ACPI-based check today.
|
||
|
||
**One real, already-flagged risk found while tracing this**: `arch/riscv64/apic.c`'s own doc
|
||
comment says the PLIC base address is "a constant, not discovered from `boot_info->dtb`" —
|
||
and `arch/riscv64/plic.c`'s own doc comment (predating this document) already warned
|
||
`PLIC_BASE`/`PLIC_CONTEXT_S` are "QEMU-virt-specific... not assumed stable across" other
|
||
configurations. That warning becomes concrete now: the JH7110's real PLIC address on the Mars
|
||
is not confirmed to match QEMU-virt's, and the interrupt controller will not work correctly if
|
||
it doesn't. This is a real punch-list item, not a hypothetical.
|
||
|
||
1. **Entry stub**: new native riscv64 entry point at `0x40000000` (§V.1), receiving `a0`=hart
|
||
ID, `a1`=DTB pointer directly (now-confirmed SBI convention) — no UEFI, no PE loader.
|
||
2. **DTB → `BootInfo` constructor**: same shape as aarch64's (§IV.3 item 2) — `dtb`=real
|
||
pointer, `acpi_table`=`NULL`, memory map from DTB `/memory`+`/reserved-memory`, `args` from
|
||
`/chosen`/`bootargs`.
|
||
3. **PLIC base address: make it DTB-discovered**, not the current QEMU-virt-specific
|
||
constant — the one concrete, already-flagged risk above. Uses the same `fdt.c` node-scoped
|
||
lookup extension §IV.3 already scopes for the Pi 5's UART/mailbox addresses — one extension,
|
||
two consumers.
|
||
4. **Framebuffer for HDMI output**: JH7110's display path is genuinely unresearched this
|
||
pass — unlike the Pi 5's mailbox interface (well-documented, reused across many Pi bare-
|
||
metal projects), no equivalent research done yet for JH7110's own display controller.
|
||
Flagged here rather than assumed simple.
|
||
5. **`pci_init()` DTB path**: shares the same new code §IV.3 item 5 scopes for the Pi 5's RP1
|
||
— one implementation, two consumers (M.2 here, RP1 there), assuming the underlying DTB PCI
|
||
binding shape is similar enough (ECAM-based, most likely, but not yet confirmed for JH7110
|
||
specifically).
|
||
6. **Boot image packaging**: `vf2-imager`/`mkimage`-based FIT image (§V.1) — confirm this
|
||
tooling's actual invocation once building the first real image, not just cited from
|
||
VisionFive 2 research.
|
||
|
||
**Hardware-dependent, after 2026-09-17:**
|
||
7. Build and flash the boot image to QSPI flash (or attempt UART XMODEM recovery boot if QSPI
|
||
flashing isn't set up yet — both are real supported paths per §V.1).
|
||
8. Connect HDMI + keyboard.
|
||
9. Boot; confirm `ok>`/`zuse)ok>` reached.
|
||
10. Mint a Zuse identity on real media, confirm re-attach — the `v2.5.0` gate's own
|
||
requirement.
|
||
11. Update this section with results.
|
||
|
||
---
|
||
|
||
## VI. Hardware identification reference
|
||
|
||
Per-board SoC/CPU facts, consolidated here so later sections don't have to re-derive them.
|
||
Researched 2026-09-04 (web search, sources cited); anything not directly confirmed against
|
||
the actual unit in hand is flagged as such rather than assumed.
|
||
|
||
### amd64 — Beelink SER5 (reference/development machine)
|
||
|
||
- **CPU: AMD Ryzen 7 family.** Beelink has shipped the "SER5" name with several different
|
||
Ryzen 7 SKUs over its product life (5700U, 5800H, 7735HS all confirmed to exist under this
|
||
branding) — **exact SKU on this unit not yet confirmed**; check `dmesg`/BIOS/the physical
|
||
unit when convenient (`cat /proc/cpuinfo` or the BIOS splash screen under Linux/before
|
||
LithosAnanke boots, since LithosAnanke itself has no CPU-identification word yet). Not
|
||
load-bearing for this document's own genericity requirement (§III) — the boot path must not
|
||
depend on which SKU this is, by design — but worth pinning down for this reference's own
|
||
accuracy.
|
||
- **Architecture generation**: Zen2 (5700U/5800H) or Zen3 (7735HS) depending on the SKU above
|
||
— matters for any future CPU-feature-detection work (e.g. RDRAND is present on all of
|
||
these; that part's already confirmed live via `rng: backend = rdrand`, §III).
|
||
- Sources: [Gentoo wiki — SER5 5560U](https://wiki.gentoo.org/wiki/Beelink_SER5_AMD_Ryzen_5_5560U_Mini_PC),
|
||
[Starry Hope — SER5](https://www.starryhope.com/minipcs/models/beelink-ser5-mini-pc/),
|
||
[Starry Hope — SER5 Pro](https://www.starryhope.com/minipcs/models/beelink-ser5-pro-mini-pc/),
|
||
[Minixpc — SER5 Max](https://minixpc.com/blogs/news/beelink-ser5-max-review-powered-by-amd-ryzen7-5800h-processor).
|
||
|
||
### aarch64 — Raspberry Pi 5 (sole target)
|
||
|
||
- **SoC: Broadcom BCM2712.**
|
||
- **CPU**: quad-core 64-bit Arm Cortex-A76, 2.4 GHz, 512 KB per-core L2 cache, 2 MB shared L3.
|
||
- **GPU**: VideoCore VII, 12-core, 800 MHz, OpenGL ES 3.1 + Vulkan 1.2 (not relevant to
|
||
LithosAnanke's own framebuffer work — that goes through the mailbox property interface,
|
||
§IV.1 — but recorded here for completeness).
|
||
- **RAM**: LPDDR4X-4267, board variants at 1/2/4/8/16 GB, 32-bit memory interface, ~17 GB/s
|
||
bandwidth.
|
||
- **I/O**: RP1 companion chip (PCIe 2.0 x4-attached) handles GPIO, USB 2.0/3.0, Gigabit
|
||
Ethernet, CSI/DSI, analog video — confirmed separately (§IV.2) to have no RNG peripheral in
|
||
its own published peripheral list.
|
||
- **Cortex-A76 and `FEAT_RNG` (ARMv8.5 `RNDR`/`RNDRRS`)**: not confirmed present — A76 is not
|
||
among the cores that typically implement this feature (more common on newer cores like
|
||
Cortex-X2/A710); if this matters for any future entropy-source decision, verify via `ID_AA64ISAR0_EL1`
|
||
directly on the real board rather than assuming either way.
|
||
- Sources: [CNX Software — Pi 5 launch](https://www.cnx-software.com/2023/09/28/raspberry-pi-5-sbc-broadcom-bcm2712-quad-core-cortex-a76-soc/),
|
||
[Raspberry Pi — Processors doc](https://www.raspberrypi.com/documentation/computers/processors.html),
|
||
[sbcwiki — BCM2712](https://sbcwiki.com/docs/soc-manufacturers/broadcom/bcm2712/boards/rasperrypi-5/).
|
||
|
||
### riscv64 — Milk-V Mars (sole target)
|
||
|
||
- **SoC: StarFive JH7110**, 28 nm.
|
||
- **CPU**: 4× SiFive U74-MC application cores (RV64GC) + 1× SiFive S7 monitor core, up to
|
||
1.5 GHz.
|
||
- **RAM**: up to 8 GB LPDDR4; storage via eMMC slot + microSD slot.
|
||
- **I/O**: 3× USB 3.0, 1× USB 2.0, HDMI 2.0 (4K), Gigabit Ethernet with PoE support, M.2 E-Key
|
||
(WiFi/BT), 4-lane + 2-lane MIPI CSI, 40-pin GPIO header.
|
||
- **Physical**: designed to Raspberry Pi 3B dimensions — cases/heatsinks/fans for that form
|
||
factor are compatible.
|
||
- **Multimedia**: H.264/H.265 4K@60fps decode, H.265 1080p@30fps encode (not relevant to
|
||
LithosAnanke's own bring-up, recorded for completeness).
|
||
- Same JH7110 SoC as the StarFive VisionFive 2 — any VisionFive 2 bring-up material found
|
||
while researching §V's own boot-chain question is likely directly applicable here too, worth
|
||
checking first before assuming Mars-specific research is needed from scratch.
|
||
- Sources: [milkv.io — Mars overview](https://milkv.io/docs/mars/overview),
|
||
[milkv.io — Mars product page](https://milkv.io/mars),
|
||
[TinyComputers.io — Mars review](https://tinycomputers.io/posts/milk-v-mars-review.html).
|
||
|
||
### Noted for later, not yet in scope — BeagleBone Black
|
||
|
||
Added to this reference per direct instruction 2026-09-04, **recorded only — no work scoped
|
||
around it yet.** Genuinely different from the three targets above: the BeagleBone Black's
|
||
SoC is a **32-bit ARM** part, not aarch64 — a fourth architecture this kernel has no support
|
||
for at all today (amd64/aarch64/riscv64 only), not another board under an existing one.
|
||
|
||
- **SoC: TI Sitara AM335x.**
|
||
- **CPU**: single-core ARM Cortex-A8, 1 GHz, armv7-a (32-bit) — up to ~2000 MIPS.
|
||
- **RAM**: 512 MB DDR3L. Storage: 4 GB eMMC (default boot source) + microSD (secondary/
|
||
overridable to primary).
|
||
- **Other on-die units**: PowerVR SGX530 3D GPU; 2× PRU (Programmable Realtime Unit) 32-bit
|
||
200 MHz microcontrollers — real-time I/O coprocessors, no equivalent on any of the three
|
||
boards above; crypto accelerators.
|
||
- **Boot modes**: eMMC, microSD, serial, USB.
|
||
- Sources: [element14 — BBB product page](https://www.element14.com/community/docs/DOC-84108/l/beaglebone-black-development-board-with-1ghz-am335x-arm%C3%A3%C3%A2-cortex-a8-processor),
|
||
[TI.com — BEAGL-BONE-BLACK](https://www.ti.com/tool/BEAGL-BONE-BLACK).
|
||
|
||
### Noted for later, not yet in scope — Zynq-7000 (Puzhi PZ7010/PZ7020 "StarLite")
|
||
|
||
Added per direct instruction 2026-09-04, **recorded only — no work scoped around it yet.**
|
||
Unlike BeagleBone Black above, this one isn't a random addition: `ROADMAP.md` already names
|
||
**Zynq FPGA as the next big milestone beyond v2.5.0** — "the step where the battle-tested
|
||
amd64/aarch64/riscv64 story rides on configurable silicon," and the three-product split
|
||
decided alongside it names "hardware steady-state machinery with sealed executions,
|
||
HOL-proven" as the FPGA-native product this board would ultimately serve. This entry just
|
||
puts a concrete, purchasable board under that already-named milestone.
|
||
|
||
- **Board**: Puzhi PZ7010-StarLite (XC7Z010) or PZ7020-StarLite (XC7Z020) — same board design,
|
||
two SoC variants. 90×60mm, black PCB, immersion gold finish.
|
||
- **SoC: Xilinx/AMD Zynq-7000**, combining a **Processing System (PS)** — dual-core ARM
|
||
Cortex-A9, up to 667 MHz (`-1` speed grade) or 800 MHz (`-2`, XC7Z020 only) — with
|
||
**Programmable Logic (PL)**, 28 nm Artix-7/Kintex-7-based FPGA fabric. Genuinely a fifth
|
||
architecture class in this reference: ARMv7-A again (like BeagleBone Black), but a
|
||
different core (Cortex-A9 vs. A8) *and* an FPGA fabric with no equivalent on any board
|
||
above — this is the "configurable silicon" milestone `ROADMAP.md` already flagged as
|
||
reshaping the hardware story (soft/hard CPU cores, PL fabric, non-standard memory map,
|
||
custom peripherals), not a small per-board addition even in concept.
|
||
- **PS details** (identical between both variants): 256 KB on-chip memory, DDR3 controller,
|
||
32 KB I-cache + 32 KB D-cache per core, 512 KB shared L2.
|
||
- **PL resources (the actual XC7Z010 vs. XC7Z020 difference)**: XC7Z010 — 4,400 logic slices,
|
||
17,600 6-input LUTs, 35,200 flip-flops, 270 KB block RAM, 80 DSP slices. XC7Z020 — 13,300
|
||
logic slices, 53,200 LUTs, 106,400 flip-flops, 630 KB block RAM, 220 DSP slices.
|
||
- **RAM/storage**: 512 MB/1 GB DDR3, QSPI flash, EEPROM, SD boot.
|
||
- **I/O**: JTAG, UART, HDMI out, Gigabit Ethernet, USB 2.0 host, 40-pin expansion; MIPI CSI on
|
||
the 7020 variant only.
|
||
- Sources: [Puzhi — PZ7010-StarLite](https://www.en.puzhi.com/Product/AMD-FPGA-Development-Board/Zynq-7000-SoC/PZ7010-StarLite),
|
||
[Puzhi — PZ7020-StarLite](https://www.en.puzhi.com/Product/AMD-FPGA-Development-Board/Zynq-7000-SoC/PZ7020-StarLite),
|
||
[Xilinx/AMD — Zynq-7000 SoC Data Sheet (DS190)](https://www.mouser.com/datasheet/2/903/ds190-Zynq-7000-Overview-1595492.pdf),
|
||
[PCBSync — XC7Z010 vs XC7Z020 comparison](https://pcbsync.com/xilinx-xc7z010/).
|