Per direct instruction: amd64's real goal is genericity (any x86_64 laptop/desktop/tower/mini, not just the Beelink SER5 reference machine); aarch64 targets the Raspberry Pi 5 exclusively; riscv64 targets the Milk-V Mars exclusively -- no cross-board genericity requirement for the latter two, unlike amd64. Each section starts from what's already true (ROADMAP.md's existing v2.2.0/v2.4.0/v2.5.0 board-by-board gates, the already-built thumbdrive/iso-usb Makefile targets, the amd64 RDRAND backend) and names what's genuinely still unknown rather than assuming -- most notably whether the Milk-V Mars boots via UEFI (like this project's QEMU riscv64 target) or via U-Boot+OpenSBI+devicetree, which would need a different boot entry path, not just different peripheral addresses. Doc-only change, no acceptance build needed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
14 KiB
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 HEADd2a0305) is a strict ancestor ofv2.0.1(HEADb031b80) —v2.0.1is exactlymasterplus 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/mastercarries exactly one commit beyond localmaster(58c59e8, "Initial commit") that localmasterhadn't fetched yet — confirmed already contained inv2.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 oldFABRIC.md/FABRIC-2.md/FABRIC-3.mdnaming (unrenamed) — expected, since today's rename commit (b031b80) only exists onv2.0.1so far. The fast-forward brings the rename tomasteralong with everything else; nothing separate needs doing for it.
Plan:
- Fast-forward
mastertov2.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. - Push
mastertoorigin. - 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.mdalready mandates for any kernel change —clean qemuon all three architectures, in the foreground, one at a time, each reachingok>and shutting down cleanly. Since the tree is byte-identical tov2.0.1's post-fast-forward, this is expected to reproduce exactly whatv2.0.1's own last acceptance pass already showed — the point of re-running it here is to confirm that expectation holds onmasteritself, not to assume it from the fast-forward alone. - Return to
v2.0.1as 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:
- Committed the write-up above on
v2.0.1first (72c14cb), pushed. This becamev2.0.1's new tip. 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.git push origin master—origin/mastermoved58c59e8..72c14cb.- Verified on a genuinely clean
mastertree, not inferred from the fast-forward:- Hosted build (
make clean && make): clean compile, zero warnings, same asv2.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 tov2.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/.
- Hosted build (
masterandv2.0.1are now identical (72c14cbon both,originand local). Returned tov2.0.1as 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, fourproof/*.thyfiles, andsrc/starkernel/arch/amd64/isr.Sall still had staleFABRIC.md/FABRIC-2.md/FABRIC-3.mdcitations — 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, orderedFABRIC-3→2→1→0placeholders 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.jsonalso 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:
Makefile.starkernel:LITHOS_VERSION ?= 2.0.1→2.0.0.- The rename-gap fix above (7 files).
- Verified 3-arch boot (
clean qemu, amd64/aarch64/riscv64, each in the foreground): all three showLithosAnanke v2.0.0in the boot banner (confirmed directly in each serial log, not assumed from the Makefile edit alone), zero build errors, zero unexpected warnings, clean shutdown. - Moved the existing
v2.0.0git tag (previously at2efd7fe, the original QEMU-release milestone commit — that commit and its own message stay fully intact in history, only the tag pointer moves) to the currentmaster/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 originwas empty forv2.0.0), so no destructive remote operation was needed, only a local move-and-push. - Follow-up, same day:
v2.0.1(the working branch this and Task 1 happened on) deleted, local andorigin— confirmed a strict ancestor ofmaster's new HEAD first, so nothing was lost.masteris 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 thisv2.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 ofmaster) 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, confirmok>, document) is the closest thing to an existing plan — but it predates the genericity requirement and was written with no hardware in hand yet.
Genuinely open, not yet decided:
- What "generic enough" actually needs to be verified against, beyond the SER5 — is there a second, different machine available to cross-check against, or does genericity get argued from firmware-standards-compliance (real UEFI, no vendor-specific assumptions in the boot code) rather than a second physical test right away?
- Observation method for a headless/serial-less real machine (no QMP/serial socket the way QEMU gives us) — HDMI + keyboard? A serial console cable if the board exposes UART pins?
- Whether Secure Boot needs handling, and how, on real UEFI firmware (QEMU/OVMF's own Secure Boot behavior may not match every real vendor's).
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).
Genuinely open, not yet decided:
- Raspberry Pi 5's own boot chain — UEFI (e.g. via the community EDK2 port) or the Pi's native boot flow? This determines whether the existing UEFI-targeted boot code needs a different entry path for this board at all, or just different ACPI/DTB-provided data.
- Which peripheral RNG the Pi 5 actually exposes, and how to read it (memory-mapped peripheral vs. a firmware call) — not yet researched.
- Same observation-method question as amd64 (no QEMU serial socket on real hardware) — possibly shared tooling/approach across both boards once decided once.
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.
Genuinely open, not yet decided:
- Milk-V Mars's actual boot chain — this project's QEMU riscv64 target boots via UEFI
(EDK2
RISCV_VIRTfirmware, confirmed inMakefile.starkernel's ownqemurecipe), but real riscv64 SBCs commonly boot via U-Boot + OpenSBI + a devicetree instead of UEFI. Which one the Mars actually uses is not yet confirmed — this is the single most consequential unknown across all three sections, since it could mean this board needs a genuinely different boot entry path, not just different peripheral addresses. - Zkr/RNDR instruction availability on the Mars's actual CPU (riscv64 Scalar Crypto extension support varies by implementation) — not yet confirmed.
- Same observation-method question as the other two boards.