Commit Graph
557 Commits
Author SHA1 Message Date
Robert Allan JamesandClaude Sonnet 5 4ce28edf8f 3-arch QEMU acceptance + live diagnostic logs for the WIREBIND overflow trigger
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Per .claude/CLAUDE.md's non-negotiable acceptance criteria, ran
`make -f Makefile.starkernel ARCH=<amd64|aarch64|riscv64> clean qemu`
sequentially (one instance at a time, TCG) against the preceding commit.
All three booted cleanly to `(zuse) ok>` with identical dictionary hashes,
no regression from the new idle-tick overflow check.

Also includes the amd64 run (20260905-161517) that carried the temporary
TEST-OWNER-CLAIM diagnostic via SK_CMD, confirming blk_owner_has_claim()
live before the diagnostic word itself was reverted: PASS, no FAIL lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4
2026-09-05 16:17:35 -04:00
Robert Allan JamesandClaude Sonnet 5 4d4ab59189 Build the FIRSTTOUCH overflow trigger flagged in FABRIC-2.md §I.2
The overflow-triggered migration path (a WIREBIND-attached identity's own
drive running low on space) was scoped but never built -- only the
trigger-detection call site was missing, per this section's own text.

- capsule_wirebind.c now tracks the attached blkio_dev* alongside the
  already-tracked VM id, set in try_attach() and cleared in both
  EJECT/UNCLEAN paths.
- New capsule_wirebind_overflow_idle_check(), called once per idle tick
  in repl.c right alongside blk_migration_idle_check() (same cadence):
  reads the attached drive's free/total via blk_get_device_free_blocks(),
  and if free space is below a fixed 10% threshold, extends the
  identity's pool with a one-time blk_firsttouch_claim() of 8 additional
  devblocks on Artemis's system-resident device.
- New blk_owner_has_claim(owner_fp) in block_subsystem.c answers the
  debounce question blk_firsttouch_claim()'s own doc comment had left
  open: a disk scan, not a RAM flag, so the already-extended answer
  survives reboot/reattach, matching BMAPFMT's "ownership travels with
  the block" model.

Premise checked before building (does a WIREBIND-attached drive actually
give a real free/total signal, or does it stay PROVISIONAL/raw): traced
repl.c's attach sequence and confirmed blk_subsys_attach_device() runs on
the same dev pointer right after WIREBIND, and a WIREBIND-eligible drive
is always already STFR/v2-formatted, so the signal is real. Premise held,
unlike the BAM item's overstated one.

Verified with the mandatory 3-arch QEMU acceptance (identical dictionary
hashes, no regression) plus a live logic test of blk_owner_has_claim():
a temporary TEST-OWNER-CLAIM word, run once via SK_CMD and reverted,
confirmed it correctly detects the claiming owner and rejects an
unrelated one. The low-disk-space-triggers-a-claim path itself is not
verified end-to-end -- that needs a real minted WIREBIND-user thumbdrive
with deliberately tiny capacity, out of scope for this pass; noted as
such in the FABRIC-2.md §I.2 closure note rather than overclaimed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4
2026-09-05 16:17:19 -04:00
Robert Allan JamesandClaude Sonnet 5 d8ac27abb2 3-arch QEMU acceptance + live diagnostic logs for the BAM reconciliation fix
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Per .claude/CLAUDE.md's non-negotiable acceptance criteria, ran
`make -f Makefile.starkernel ARCH=<amd64|aarch64|riscv64> clean qemu`
sequentially (one instance at a time, TCG) against the preceding commit.
All three booted cleanly to `(zuse) ok>` with the full Tripod fleet
(Hera/Hermes/Artemis) alive and identical dictionary hashes:

  Mama:    dict_hash=0x8d712e07c690d713
  Hermes:  dict_hash=0xa07a57d794c1665d
  Artemis: dict_hash=0x9cc9a7e72b590250

Also includes the amd64 run (20260905-155329) that carried the temporary
TEST-BAM-RECON diagnostic via SK_CMD, confirming the fix live before the
diagnostic word itself was reverted: TEST-BAM-RECON: PASS, no FAIL lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4
2026-09-05 15:57:21 -04:00
Robert Allan JamesandClaude Sonnet 5 aa33aedca8 Fix blk_meta_t/BAM accounting reconciliation flagged in FABRIC-2.md §I.2
FIRSTTOUCH ownership (blk_meta_t's BLK_FLAG_CLAIMED/owner_fp) and the
generic block-allocation bitmap (BAM, blk_bam_entry_t) were two parallel,
unreconciled accounting systems: blk_firsttouch_claim() never touched the
BAM, and blk_allocate()/devblock_is_free() never checked BLK_FLAG_CLAIMED.
A FIRSTTOUCH claim could be silently overwritten by a later blk_allocate()
call, or could itself steal a devblock already in ordinary use via
BLOCK/UPDATE.

- devblock_is_free() (shared by blk_firsttouch_claim() and
  blk_migration_idle_check()) now also checks the BAM entries of all
  BLK_PACK_RATIO member LBNs, not just blk_meta_t.
- New devblock_claimed_by_lbn() helper wired into blk_allocate()'s
  free-scan, so it skips any LBN whose devblock is BLK_FLAG_CLAIMED.
- blk_firsttouch_claim() now marks the BAM allocated for all 3 member
  LBNs of each devblock it claims, which also fixes vol_meta.free_blocks
  never decrementing for FIRSTTOUCH claims.
- blk_meta_relocate_devblock() traced and confirmed NOT part of the bug —
  it already keeps BAM in sync via blk_subsys_relocate_block()'s own
  blk_mark_free()/blk_update() calls.

Both boundary cases (the reserved/user LBN split at a slot's start_lbn,
and BAM-array bounds) are guarded explicitly.

Verified with the mandatory 3-arch QEMU acceptance (identical dictionary
hashes, clean BYE) plus a live logic test: a temporary TEST-BAM-RECON
word, run once via SK_CMD and fully reverted, confirmed on running code
that ordinary allocation and a FIRSTTOUCH claim land on disjoint LBN
ranges in both directions. FABRIC-2.md §I.2 updated in place with the
closure note, per this project's documentation discipline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4
2026-09-05 15:57:03 -04:00
Claude 04cc17920e 3-arch QEMU acceptance logs for the FINDINGS.md defect-repair commits
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Per .claude/CLAUDE.md's non-negotiable acceptance criteria, ran
`make -f Makefile.starkernel ARCH=<amd64|aarch64|riscv64> clean qemu`
sequentially (one instance at a time, TCG) against the two preceding
commits. All three booted cleanly to `(zuse) ok>` with the full Tripod
fleet (Hera/Hermes/Artemis) alive, Stadium conservation holding
(resident_sum=43691 reservoir=21845 sum=65536), and identical dictionary
hashes across all three ISAs:

  Mama:    dict_hash=0x8d712e07c690d713
  Hermes:  dict_hash=0xa07a57d794c1665d
  Artemis: dict_hash=0x9cc9a7e72b590250

Confirms cross-arch determinism holds with the EXECUTE/format/TYPE/
vocabulary/control-flow fixes applied.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Qf6YcnHgaEtEygq3knx19
2026-09-05 14:46:24 +00:00
Claude 7191ce5a56 Refresh lfs/amd64/starforth binary artifact
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Rebuilt after the EXECUTE/format/TYPE/vocabulary/control-flow fixes in
the two preceding commits, per lfs/README.md's convention (this binary
is synced whenever the hosted amd64 build runs).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Qf6YcnHgaEtEygq3knx19
2026-09-05 14:07:51 +00:00
Claude d6895bdd15 Move vocabulary and control-flow state off file-scope statics onto VM
proof/FINDINGS.md's Isabelle/HOL word-source sweep (§1) found the two
defects severe enough to actively corrupt the live Tripod multi-VM fleet:
file-scope C statics standing in for state that belongs on struct VM.

- vocabulary_words.c (highest severity in the sweep): forth_vocab/
  context_vocab/current_vocab, context_var_addr/current_var_addr, the
  ctx_fc/forth_fc first-char search index, and the `initialized` guard
  were all process-wide statics. Only the first VM to touch any
  vocabulary word ever ran setup; every VM after that silently shared
  VM #1's dictionary-chain pointers and reused VM #1's byte-offset
  addresses as if valid in its own vm->memory. One VM's VOCABULARY/
  DEFINITIONS/FORTH silently changed where every other VM looked up and
  defined words.

- control_words.c: cf_stack/cf_sp/cf_last_mode (IF/THEN/BEGIN/DO/CASE
  compile-time nesting) and the LEAVE/ENDOF patch-site bookkeeping
  (leave_addrs/leave_sp/leave_mark_*, endof_addrs/endof_sp/endof_mark_*)
  were also process-wide statics. Two VMs compiling colon definitions at
  overlapping times would corrupt each other's nesting state.

Both moved onto struct VM, following the existing hold_addr/hold_pos
precedent in include/vm.h ("lives in each VM's own memory... so child
VMs never alias Hera's buffer"):

- New VocabularyState struct (vm->vocab): chain heads, VM-cell addresses,
  first-char index, initialized flag.
- New ControlFlowState struct (vm->cf): cf_stack/cf_sp/cf_last_mode plus
  the LEAVE/ENDOF patch-site stacks. cf_tag_t/cf_item_t/CF_STACK_MAX
  moved from control_words.c into include/vm.h since they're now part of
  the struct VM field's type.
- Sentinel fields (-1/-999, meaning "empty") explicitly initialized in
  both vm_init_with_host() implementations (hosted src/vm_bootstrap.c and
  kernel src/starkernel/vm/vm_bootstrap.c) alongside the existing
  dsp/rsp = -1 initialization, since the preceding zero-init leaves them
  at 0 rather than their empty sentinel.

Every word function in both files already took VM *vm, so no call sites
outside these two files needed to change; cf_push_item/cf_pop_item/
cf_peek_item gained a VM* parameter to reach vm->cf.

Verified: hosted (amd64) and kernel (amd64, __STARKERNEL__) both build
clean with -Wall -Werror after a full clean rebuild (struct VM's layout
changed size, and this Makefile has no header-dependency tracking, so a
stale incremental build would have linked mismatched object layouts).
Hosted POST suite 1012/1012 passing (0 regressions). Manually exercised
VOCABULARY/DEFINITIONS/FORTH/ORDER, and IF/ELSE, DO/LOOP/LEAVE,
BEGIN/WHILE/REPEAT, and CASE/OF/ENDOF/ENDCASE (including nested DO with
I/J) in the REPL -- all correct and unchanged from pre-refactor behavior.

Note: a pre-existing CASE/ENDCASE default-clause bug (the code after the
last OF...ENDOF pair does not correctly become the "default" value once
DROP runs) was found while testing this refactor and confirmed present
on unmodified master too -- not touched here, out of scope for this pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Qf6YcnHgaEtEygq3knx19
2026-09-05 14:07:41 +00:00
Claude c36bd99e1e Fix EXECUTE/?/DUMP/TYPE/DECIMAL-HEX-OCTAL/ALIGN defects from proof sweep
proof/FINDINGS.md's Isabelle/HOL word-source sweep (§4) flagged five real
defects; this fixes all five and records resolution in that doc:

- EXECUTE (system_words.c): cast a popped cell straight to a DictEntry*
  and called through it with only a null check. Now validates via a new
  shared vm_dict_entry_ok(), promoted out of starforth_words.c's
  ENTROPY@/ENTROPY! guard (dictionary_management.c) so EXECUTE gets the
  same live-entry check.

- ? and DUMP (format_words.c): dereferenced the popped cell as a raw host
  pointer, bypassing vm_addr_ok entirely (out-of-VM-bounds read). Both now
  go through VM_ADDR/vm_addr_ok/vm_load_cell/vm_ptr like every other
  memory word (@, `,`, editor_words.c).

- TYPE (io_words.c): bounds check computed addr+count in signed 64-bit
  arithmetic, which can overflow and bypass the check on large operands.
  Replaced with vm_addr_ok(), which is written to avoid that overflow.

- DECIMAL/HEX/OCTAL (format_words.c): wrote only the BASE memory cell,
  never vm->base, the host-mirror field number-output words actually read
  via current_base() -- so these words silently affected number parsing
  but never printing. Now call the existing vm_set_base() (previously
  only used at boot init), which updates both. vm_get_base/vm_set_base
  promoted to public declarations in include/vm.h.

- ALIGN vs ALLOT/,/C,/2, (dictionary_words.c): disagreed on dictionary
  growth ceiling (2MB vs 5MB). Investigated which was correct rather than
  blindly widening: vm_get_block_addr() maps block N to
  vm->memory + N*BLOCK_SIZE across the full 5MB arena, and
  USER_BLOCKS_START (block 2048) lines up exactly with
  DICTIONARY_MEMORY_SIZE -- so ALLOT/,/C,/2, letting `here` grow past 2MB
  could silently corrupt live block/user data sharing that memory.
  Tightened ALLOT/,/C,/2, to DICTIONARY_MEMORY_SIZE to match ALIGN.

Verified: hosted (amd64) and kernel (amd64, __STARKERNEL__) both build
clean with -Wall -Werror; hosted POST suite 1012/1012 passing (0
regressions); manually exercised EXECUTE, ?/DUMP, TYPE, HEX/DECIMAL/OCTAL,
and large-ALLOT rejection in the REPL.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Qf6YcnHgaEtEygq3knx19
2026-09-05 14:07:11 +00:00
Robert Allan JamesandClaude Sonnet 5 70dc8beba4 FABRIC-2.md §I.9 follow-on: blinking | cursor instead of static block
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Captain Bob asked for the framebuffer cursor to render as a blinking
vertical bar rather than the previous static solid-block glyph.

vt100_draw_cursor() (hal/vt100.c) now fills a thin bar (cell_w()/8, min
1px, full cell height) at the cursor's left edge instead of the whole
cell -- an I-beam shape. vt100_erase_cursor() is unchanged (clearing the
whole cell already safely covers the narrower bar).

Blinking is new in repl.c: sk_console_readline()'s idle branch toggles the
cursor on/off every SK_CURSOR_BLINK_INTERVAL (50 ticks, 500ms at 100Hz)
via alternating console_fb_draw_cursor()/console_fb_erase_cursor() calls,
independent of the heartbeat/idle-beat mechanism the §I.9 fix just touched
(deliberately not reused, to avoid recoupling to that path). Runs
regardless of n, so it blinks whether sitting at a bare prompt or paused
mid-edit. Every deterministic draw site (initial prompt, prompt reanchor,
backspace, character echo) now goes through a new helper, sk_cursor_show(),
which resets the blink cycle to "on" and redraws -- typing always shows a
solid cursor, never mid-blink.

Verified via the mandatory foreground 3-arch QEMU acceptance boot: amd64
(logs/20260905-021054, extensive live interactive typing including
multi-line : / ; word definitions and error cases, prompts stayed
correctly attached throughout), aarch64 (logs/20260905-021551), riscv64
(logs/20260905-022324) -- all three reached (zuse) ok> and shut down
cleanly via BYE.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 02:24:51 -04:00
Robert Allan JamesandClaude Sonnet 5 8edb95b65d FABRIC-2.md §I.9: fix the terminal phantom-linebreak defect
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
console_ensure_line_start() (hal/console.c) used to emit its newline
immediately via a path that deliberately skipped the tx-byte counter, so
that sk_repl_idle()'s unconditional per-beat call to it (repl.c, ~1s idle
heartbeat) could force a real newline the REPL's own prompt-reanchor logic
never noticed -- the prompt was never reprinted, and the next real
keystroke echoed onto the now-blank line, indistinguishable from Enter
having already been pressed at a bare prompt. Root-caused in the previous
commit (704573b); this commit applies the fix per explicit go-ahead.

Fix: defer the newline instead of emitting it eagerly.
console_ensure_line_start() now only sets a flag (g_pending_line_close);
the newline is realized -- for real, and counted by g_console_tx_count
like any other output -- on the next actual console_putc() call, or
silently discarded via the new console_cancel_deferred_line_start() if the
caller decides nothing was actually printed. sk_repl_idle() captures
tx_before_idle right after its console_ensure_line_start() call and cancels
the deferred newline at both of its exit points when console_tx_count()
hasn't moved. A beat with nothing to report now leaves the console
untouched; a beat that does print still closes the dangling prompt line
first, properly counted this time. The pre-existing n > 0 mid-edit gate in
sk_console_readline() is untouched -- independent purpose, not the bug.

Verified via the mandatory foreground 3-arch QEMU acceptance boot
(clean qemu, amd64 -> aarch64 -> riscv64, one at a time): all three reached
(zuse) ok>, all echoed the first real input on the same log line as the
prompt rather than a fresh line, all shut down cleanly via BYE. amd64's log
additionally shows live human backspace-correction still glued to the same
prompt line. Logs: logs/20260905-015854 (amd64), logs/20260905-020248
(aarch64), logs/20260905-020552 (riscv64); logs/20260905-015813 is a
foreground-rule-violation retry killed and redone correctly, kept per this
project's "never delete logs/" convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 02:08:06 -04:00
Robert Allan JamesandClaude Sonnet 5 704573bdfc FABRIC-2.md §I.9: root-cause the terminal defect, not fixed (identify-only)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
No code changed. This is a closure annotation on an existing open item
where it lives (FABRIC-2.md, marked archival by CLAUDE.md, but §I's punch
list landed there after the FABRIC series rename and genuinely still
lives there) -- not new design content in an archival doc.

Used QMP send-key to inject a real keypress into a running headless QEMU
guest -- no physical keyboard needed, overturning this item's own
"no way to observe the live QEMU GTK window from this environment"
assumption. A first pass (amd64 + aarch64, real first keypress after a
fresh boot, pre/post log captures) actually reproduced the defect but
misread it as clean: both logs showed the echoed key on a bare new
"[Hera]" line instead of appended to the still-visible "(zuse) ok> "
line, which looks like normal REPL output unless you know the prompt
should still be there. QMP screendump on aarch64 confirmed it visually.

Root cause traced to two specific interacting lines, not guessed:
console_ensure_line_start() (hal/console.c:312) emits its newline via
the tx-count-exempt console_putc_inner(), by design, so idle chatter
doesn't spam the REPL's prompt reanchor. But sk_repl_idle() (repl.c:178)
calls it unconditionally on every ~1s idle beat, including at a bare
prompt with nothing typed (n==0, repl.c:610) -- forcing a real but
tx-count-invisible newline that the reanchor check (repl.c:633) never
notices, so the prompt is never reprinted. The next real keystroke
echoes onto the now-blank line with a lazily-emitted "[VMName] " prefix,
indistinguishable from Enter having already been pressed. Not a
first-keypress race -- it fires on any ~1s+ human pause at a bare
prompt, i.e. nearly always, matching "consistent and reproducible."

repl.c's own comment at lines 598-609 already half-diagnosed this
exact failure mode and gates it for n>0 (mid-edit); the gap is the
identical n==0 case (bare prompt) was treated as harmless. Likely fix
location noted, not designed here.

Verified reproducible on both amd64 (i8042) and aarch64 (virtio-input)
input paths; riscv64 shares aarch64's virtio-input code path. Evidence:
three fresh boot logs plus before/after screendump PNGs
(evidence/aarch64/qemu-screenshot-20260905-013835-i9-*.png). No 3-arch
acceptance boot run for this commit -- no code changed, and these
logs/screenshots are themselves the evidentiary artifact, not a
generic regression check.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 01:47:53 -04:00
Robert Allan JamesandClaude Sonnet 5 dbaead0af2 xhci: route driver chatter through log_message(), silence at default log level
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
xhci.c and repl.c's USB attach/detach path printed every xHCI command
submission, completion, and BOT transfer step unconditionally via
console_println()/console_puts() -- floods the serial log on every boot,
regardless of whether anyone is debugging the USB stack.

Converted every "xhci:"-prefixed line to log_message() with a level
chosen by what it reports, not blanket debug:
- LOG_ERROR: allocation/mapping failures, timeouts, command failures,
  CSW signature/tag mismatches, CSW FAILED/PHASE ERROR, "not implemented"
  refusals, every "deferred ... setup failed" path
- LOG_WARN: dropped/skipped conditions (command ring busy, tracked-port
  range exceeded), unrecognized media (bad version/CRC), TUR retry
- LOG_DEBUG: routine progress (command submitted, succeeded, transfer
  completed, port connected) and expected outcomes (recognized/blank
  media)

Default log level is LOG_INFO, so the LOG_DEBUG chatter that was the
actual complaint is now silent by default and re-enabled with
--log-level=debug; LOG_ERROR/LOG_WARN stay visible so real faults aren't
buried.

xhci_log_hex32() now routes through log_message(LOG_DEBUG, ...) instead
of console_puts()/console_println() directly -- kept its own zero-padded
8-digit hex formatting rather than switching to log_message()'s %x
(which has no width control), since register values lining up in the
log is the reason this helper exists. console.h dropped from xhci.c,
no longer used directly.

Verified functionally unchanged, not just "still boots": all three
architectures reach zuse)ok>, log_message()-instrumented lines are gone
from the default-level log (grep -c xhci == 0 on all three, versus dozens
before), and the USB thumbdrive path still works end to end --
"Zuse: identity confirmed from attached thumbdrive" appears on all three
boots exactly as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 01:25:50 -04:00
Robert Allan JamesandClaude Sonnet 5 1bd041e84d FABRIC-3.md §IV.3 item 5: pci_init() DTB path re-scoped, doc-only, nothing implementable this pass
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Re-examined the Pi 5 RP1 PCIe access re-scoping (§IV.3 item 5) to see what
was actually buildable before real hardware arrives (2026-09-17), and
found nothing safely implementable this pass -- recorded three corrections:

1. The "vmm_map_range() needed for native boot" secondary finding is
   wrong as stated: arch_mmu_init() is a stub on both non-amd64 ISAs,
   vmm.c's load_cr3() is a no-op outside __x86_64__, and riscv64's satp
   is explicitly left at Bare (this session's own §V.3 item 7 fix). No
   non-amd64 ISA ever turns its own MMU on today, so VA==PA and there is
   nothing to map. Reframed as a future concern conditional on MMU
   bring-up, not a current prerequisite.

2. No silent-wrong-answer risk exists today: with no ACPI on native Pi 5
   boot, pci_init()'s MCFG parse already fails cleanly and aarch64 takes
   no ECAM fallback, rather than misreading RP1's 37 KB indirect window
   as flat ECAM.

3. The "make config-space dispatch runtime instead of compile-time" half
   of the re-scoped fix, considered on its own, isn't separable:
   portio_read32() and siblings use outl/inl inline asm that cannot exist
   in an aarch64/riscv64 translation unit, so any case referencing them
   stays #ifdef-gated regardless -- an enum wearing the same compile-time
   selection. It only becomes real dispatch once a second non-amd64
   mechanism is actually compiled in.

Broadcom indirect access itself remains unimplemented: no QEMU model
exists for "brcm,bcm2712-pcie" (zero branches would ever execute before
real hardware, unlike the guard-exercised GIC/PLIC DTB fixes), and
config-space access alone is insufficient for RP1 to enumerate without
the real driver's controller bring-up (link training, PERST, window
setup) -- building only the index/data window would compile and boot
while silently never working.

No code changed. 3-arch acceptance run anyway per convention: all three
reach zuse)ok> with identical virtio-blk/xHCI PCI device discovery to
the pre-change baseline (commit 9b6de5d), confirming no regression from
a change that touched no source.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 01:06:24 -04:00
Robert Allan JamesandClaude Sonnet 5 9b6de5d6c7 riscv64: PLIC base address DTB-discovered, QEMU-virt constant as fallback (§V.3 item 3)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
plic_init() now takes boot_info->dtb (threaded through apic_init()) and
tries fdt_find_node_by_compatible(dtb, "sifive,plic-1.0.0") ->
fdt_find_prop_in_node(..., "reg", ...) before falling back to the
QEMU-virt-specific constant it previously hardcoded unconditionally.
Reuses the node-scoped DTB lookup primitive built for the aarch64 GIC
base fix unchanged. s_plic_base is now a runtime uintptr_t, same shape
as apic.c's s_gicd_base/s_gicc_base.

This system's QEMU/UEFI riscv64 firmware does not forward a DTB to the
guest (timer.c's own timebase-frequency read falls back too, confirmed
in this boot's own log), so only the no-DTB fallback branch is exercised
here -- the success branch (a real DTB with a matching PLIC node) stays
unverified until real Milk-V Mars hardware. FABRIC-3.md's first-drafted
claim that the success branch would run (based on a stale comment in
plic.c's own pre-fix header) was checked against the actual log and
corrected before this commit.

3-arch acceptance: amd64/aarch64 don't compile these files, so their
runs are non-regression on untouched files only. riscv64's own boot log
confirms the fallback path prints exactly as designed and boot reaches
zuse)ok> unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 00:56:25 -04:00
Robert Allan JamesandClaude Sonnet 5 90ee8deb6d riscv64: skip satp Bare-mode switch when already Bare (§V.3 item 7 audit fix)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
arch_early_init() unconditionally cleared satp on every boot, justified
only by behavior observed under QEMU/EDK2 firmware (satp.MODE=10/Sv57,
kernel identity-mapped within it). That reasoning never applied to the
native U-Boot+OpenSBI boot path, where satp is conventionally already 0
at S-mode handoff -- the unconditional clear was likely a harmless no-op
there, but on an unverified assumption.

Fix: read satp.MODE first and return early when it's already 0 (nothing
to switch away from, no safety argument needed). The unconditional
csrw/sfence pair still runs unchanged for the confirmed QEMU/EDK2 case.
No Sv39/Sv48/Sv57 page-table walker built -- out of proportion to this
finding's severity.

3-arch acceptance: amd64/aarch64 don't compile this file, so their runs
are non-regression on untouched files only. riscv64's own boot log
confirms satp.MODE = 0xa at entry, so the mode != 0 branch ran and
"satp cleared -- Bare mode, explicit" printed exactly as before the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 00:17:35 -04:00
Robert Allan JamesandClaude Sonnet 5 350287850f arch/aarch64: fix VBAR_EL2 stale comment + hardcode HVC PSCI conduit
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
FABRIC-3.md §IV.3 item 7's code audit found two more findings; fixed both
per direct instruction naming them specifically.

Comments only, no behavior change:
interrupts.c's arch_interrupts_init() doc comment (and its matching
runtime EL2 console message) claimed VBAR_EL2 was "a known gap... not yet
wired." Checked against isr.S's aarch64_install_vectors() and found that
claim stale: the assembly already branches on aarch64_current_el() and
writes vbar_el2/vbar_el1 correctly, threading the same answer through
el2_mode_flag for the IRQ trampoline's ELR/SPSR selection too. There was
no gap to close -- only the comment was wrong. Also fixed a same-vintage
one-word staleness in arch.c's aarch64_install_vectors extern comment
("installs VBAR_EL1" -> EL-aware), caught while touching this.

Real behavior change, gated to preserve existing QEMU behavior:
arch_cold_reset() hardcoded the PSCI conduit to HVC, a documented
QEMU-specific workaround (QEMU's AAVMF has no genuine EL3 to answer SMC).
Real Pi 5 hardware's ATF means EL3 exists there; confirmed further this
pass that bcm2712.dtsi's own /psci node declares method="smc" directly,
not just inferred from ATF's presence.

New aarch64_psci_conduit_init(dtb) in arch.c: fdt_valid(dtb) ->
fdt_find_node_by_compatible(dtb, "arm,psci-1.0") ->
fdt_find_prop_in_node(..., "method", ...) -- sets a module-static
s_psci_use_smc flag to 1 only when method reads exactly "smc"; every
other outcome (no DTB, no PSCI node, method="hvc", property absent)
leaves it at its default 0/HVC, preserving this system's existing QEMU
behavior exactly. arch_cold_reset() now branches on that flag between
smc #0/hvc #0. Called once from apic_init() (arch/aarch64/apic.c), the
one point in boot with boot_info->dtb already in hand -- collocated with
its consumer in arch.c rather than defined in apic.c, since
arch_cold_reset() has no boot_info of its own by the time it runs (called
from deep in VM execution, via BYE).

Three-arch QEMU acceptance run exercised both new guard branches: the
aarch64 boot log shows "PSCI: no DTB -- using HVC (QEMU virt-machine
default)" immediately before the existing GIC discovery lines, then
boots clean to zuse)ok> -- confirming the QEMU/UEFI path is unchanged.
The SMC success branch itself stays unverified until real Pi 5 hardware
runs BYE.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 00:06:41 -04:00
Robert Allan JamesandClaude Sonnet 5 dc9445abec apic.c (aarch64): fix GIC base address for real Pi 5 hardware
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
FABRIC-3.md §IV.3 item 7's code audit found GICD_BASE_PA/GICC_BASE_PA
hardcoded to QEMU virt-machine constants (0x08000000/0x08010000),
self-documented as a deliberate exception because QEMU's aarch64 UEFI
firmware never forwards a DTB. That premise doesn't hold on the native
Pi 5 boot path (rpi5_native_boot.c), which does have a real DTB and calls
this same, unmodified apic_init() -- which ignored boot_info entirely and
always programmed the QEMU addresses. Real BCM2712 GIC-400 is at
0x10_7fff9000 (confirmed against bcm2712.dtsi's axi/gicv2 nodes), a
completely different region of the address space. Fixed per direct
instruction, naming this finding specifically.

apic_init() now calls gic_bases_from_dtb() first: fdt_valid(dtb) ->
fdt_find_node_by_compatible(dtb, "arm,gic-400") ->
fdt_find_prop_in_node(..., "reg", ...), reading the first two
2-address-cell/2-size-cell entries (GICD, then GICC -- the standard
arm,gic-400 binding order). Falls back to the QEMU constants on any
failure (no DTB, no matching node), so the existing QEMU/UEFI path is
unchanged. GICD_BASE_PA/GICC_BASE_PA became s_gicd_base/s_gicc_base
(module-static uintptr_t, no longer compile-time constants on this path)
-- every MMIO call site (apic_init, apic_spi_enable, apic_read_iar,
apic_eoi_intid) now reads through them.

Three-arch QEMU acceptance run exercised the guard itself, not just
compiled it: the aarch64 boot log shows "GICv2: no DTB GIC node -- using
QEMU virt-machine defaults" followed by the unchanged "distributor+CPU
interface enabled, PPI 30" line, then boots clean to zuse)ok>. The
success branch (a real DTB with a matching node) stays unverified until
real Pi 5 hardware -- this build never has a devicetree to exercise it
against.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:54:39 -04:00
Robert Allan JamesandClaude Sonnet 5 0f798256a0 FABRIC-3.md: aarch64/riscv64 boot-path code audit pass (§IV.3/§V.3 item 7)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Reviewed arch/aarch64/{apic,arch,timer,interrupts}.c and
arch/riscv64/{apic,plic,arch,interrupts,timer}.c for the same class of
QEMU-virt-vs-real-hardware assumption §III item 6's amd64 audit looked for.
Report only, per this project's "identify, don't fix unless asked" rule --
no source files changed.

Findings, aarch64:
- Severe, confirmed live: apic.c's GICD/GICC base addresses are hardcoded
  QEMU-virt constants, self-documented as a deliberate exception because
  QEMU's aarch64 firmware never forwards a DTB. That premise no longer
  holds -- the native Pi 5 boot path (item 2) receives a real DTB and
  calls the same, unmodified kernel_main(), whose M4 sequence calls
  apic_init(boot_info) unconditionally (kernel_main.c:413); apic_init()
  still ignores boot_info entirely. Real BCM2712 GIC-400 is at
  0x10_7fff9000, confirmed against bcm2712.dtsi -- a different region of
  the address space entirely from the hardcoded 0x08000000. With the MMU
  off at this point in boot, this blocks reaching ok> on real hardware as
  the code stands.
- Doc-only correction: interrupts.c's own comment claims VBAR_EL2 is never
  installed ("a known gap"). Checked against isr.S and found stale -- the
  actual implementation already branches on aarch64_current_el() and
  installs vbar_el2/vbar_el1 correctly. No functional gap; the comment
  describes one the code already closed.
- arch_cold_reset() hardcodes the PSCI conduit to HVC, a QEMU-specific
  workaround (QEMU's AAVMF has no genuine EL3). The Pi 5's real ATF means
  EL3 firmware exists there, making SMC the conventional real-hardware
  conduit -- no runtime detection exists.

Findings, riscv64:
- plic.c's QEMU-virt-hardcoded PLIC_BASE is already tracked (§V.3 item 3);
  this pass confirms rather than rediscovers it.
- arch_early_init()'s satp clear is justified entirely by behavior
  observed under QEMU's EDK2 firmware; the native boot path (U-Boot+
  OpenSBI, no EDK2) doesn't share that observation, though the action is
  likely still safe since OpenSBI's handoff conventionally leaves satp=0
  already. Lowest-severity finding in the set.
- Clean: the SBI timer path and arch_cold_reset()'s SBI SRST call are both
  genuinely hardware-independent -- named as the portable pattern amd64's
  i8042-pulse reset and aarch64's hardcoded-HVC PSCI call both lack.

Doc-only change. Three-arch QEMU acceptance (amd64/aarch64/riscv64, in
order) run to confirm non-regression only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:31:38 -04:00
Robert Allan JamesandClaude Sonnet 5 6a6ff9353f FABRIC-3.md: amd64 boot-path code audit pass (§III item 6)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Reviewed src/starkernel/boot/uefi_loader.c and every file in arch/amd64/
(arch.c, apic.c, ioapic.c, interrupts.c, i8042.c, timer.c) for anything that
assumes SER5-specific hardware rather than standard UEFI/ACPI. Report only,
per this project's "identify, don't fix unless asked" rule -- no source
files changed.

Verified clean: uefi_loader.c's COM1 presence probe, its two-pass ACPI
2.0-then-1.0 GUID preference, the GetMemoryMap/ExitBootServices golden
path, and GOP BltOnly degradation. apic.c/ioapic.c derive LAPIC mode,
APIC timer frequency, and I/O APIC base/GSI at runtime, not from hardcoded
constants.

Four findings recorded (not fixed):
1. HPET base (timer.c) hardcoded at 0xFED00000, never cross-checked against
   the ACPI "HPET" table -- the same bug class this file's own comment
   already documents and fixed for the PM_TMR port.
2. LAPIC base always 0xFEE00000; apic_init()'s own doc comment flags MADT
   relocation as unimplemented. Narrower than that suggests: ioapic.c's
   parser already reads MadtHeader.local_apic_address, just never plumbs
   it to apic.c. MADT type 5 (Local APIC Address Override) isn't parsed
   at all.
3. i8042 PS/2 controller (i8042.c) and the legacy 8259 PIC (interrupts.c)
   are poked unconditionally with no ACPI FADT presence check, unlike this
   same file's own raw_serial_init() COM1 probe.
4. arch_cold_reset() (confirmed live via BYE, mama_forth_words.c:1437)
   pulses the i8042 reset line instead of using the FADT's standards-defined
   RESET_REG/RESET_VALUE, which doesn't depend on i8042 existing at all.

None are certain to bite on the real SER5 -- the conventions assumed hold
on the large majority of PC-compatible x86_64 systems -- but the decision
in FABRIC-3.md §III requires arguing genericity from standards compliance,
not from "it booted," and these are the concrete gaps.

Doc-only change. Three-arch QEMU acceptance (amd64/aarch64/riscv64, in
order) run to confirm non-regression only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:15:09 -04:00
Robert Allan JamesandClaude Sonnet 5 ecdb32e0d6 boot_media/rpi5/config.txt: Pi 5 native boot config (FABRIC-3.md §IV.3 item 6)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Researched against the official raspberrypi.com config.txt reference and one
real, working Pi 5 bare-metal project's own checked-in config.txt
(leopoldch/BatMetal), not assumed from general Pi knowledge:

- kernel=kernel_2712.img, os_check=0, device_tree=bcm2712-rpi-5-b.dtb
- enable_uart=1 added (correction to the item's original text): costs
  nothing when unused, and is the only diagnostic channel that would survive
  a framebuffer failure on the untested mailbox path (rpi5_native_boot.c).
- arm_64bit=1 dropped (correction to the item's original text): confirmed
  inert on Pi 5 by official docs.
- dtparam=pciex1 researched, deliberately omitted with rationale recorded --
  community-reported only, not needed until item 5's re-scoped RP1/PCIe work.
- os_check=0 vs. BatMetal's own config (which omits it) recorded as an open,
  unresolved discrepancy rather than silently picked.

boot_media/rpi5/README.md notes the one remaining gap this exposes:
kernel_2712.img names a file nothing currently builds (item 1's
separate-image build target is still outstanding future work).

Doc-and-config only, no compiled code changed. Three-arch QEMU acceptance
(amd64/aarch64/riscv64, in order) run to confirm non-regression only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:05:39 -04:00
Robert Allan JamesandClaude Sonnet 5 11c93773d0 FABRIC-3.md: pci_init() DTB path item was wrong -- re-scope, don't build the ECAM version
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Investigated before writing code, per this document's own discipline, and
found the original item 5 text's assumption doesn't hold: it framed this
as "swap the ACPI MCFG lookup for a DTB one, same flat-ECAM access
underneath." Confirmed against three primary sources that this is wrong:

- bcm2712-rpi-5-b.dts: RP1 sits under pcie2 ("brcm,bcm2712-pcie"),
  reg = <0x10 0x00120000 0x00 0x9310> -- a ~37KB window, far too small
  for a flat 256MB ECAM region.
- pcie-brcmstb.c (the real Linux driver for this compatible string):
  brcm_pcie_map_bus() computes a standard ECAM-shaped offset but accesses
  it through an IDX_ADDR/DATA_ADDR indirect index+window pair, not a
  direct MMIO read. A real third config-access mechanism, not a bigger
  version of the DTB-lookup task.

Re-scoped item 5 (§IV.3) to record the real shape: pci.c's cfg_read32/
cfg_write32 family dispatches today via #ifdef ARCH_AMD64 vs. everything
else (flat ECAM) -- but QEMU aarch64 (flat ECAM, real ACPI MCFG) and Pi 5
aarch64 (Broadcom indirect windowing) are the same build, so this needs a
runtime dispatch change to a file all three architectures currently boot
through, not a small addition. Also flagged: pci_init()'s vmm_map_range()
for the ECAM window is amd64-only today (aarch64/riscv64 rely on UEFI's
identity map); native boot has no UEFI identity map at all, so any PCIe
work here needs its own explicit mapping regardless of access mechanism.

Also corrected §V.3 item 5's now-stale "shares the same code" cross-
reference -- the Mars's JH7110 PCIe controller needs its own primary-
source check, not an assumed-shared implementation with RP1's.

Not implemented this pass -- recording the real shape is the deliverable;
building the runtime-dispatch version is a materially larger, separate
task. Doc-only change; 3-arch boot verified per the project's standing
rule, proving only that nothing regressed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 22:47:18 -04:00
Robert Allan JamesandClaude Sonnet 5 7187d68082 rpi5_native_boot.c: carve /reserved-memory out of the Pi 5 memory map
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Previously deferred (rpi5_native_boot.c's own header comment flagged this
as needing interval-splitting logic written blind against hardware not
yet in hand) -- revisited by fetching bcm2712-ds.dtsi directly rather than
assuming reserved-memory was empty or absent. It has one static child
(atf@0, ARM Trusted Firmware's own region) and one dynamic child
(linux,cma, size/alloc-ranges only, no fixed reg) -- the dynamic one is
skipped, nothing fixed to carve and no allocator this early to service it
against anyway.

Adds two fdt.c primitives: fdt_find_node_by_name() (reserved-memory has
neither compatible nor device_type per DT spec) and fdt_next_child_node()
-- one exported symbol, not the two-primitive general sibling-walker
originally sketched, collapsed after review since the only real use here
is "iterate one node's direct children."

collect_reserved_ranges() reads each child's own #address-cells/
#size-cells with a fallback to root's only if absent -- confirmed
necessary, not just defensive: reserved-memory's own declared <2>/<1>
genuinely differs from root's <2>/<2>. emit_region_with_carveouts()
clips a sorted reserved-range list against each RAM region, emitting
alternating EfiConventionalMemory gaps and EfiReservedMemoryType
carve-outs (insertion sort, no libc qsort in freestanding).
RPI5_MAX_MEMMAP_ENTRIES is the exact worst-case count, recomputed rather
than estimated -- the rpi5_mailbox.c buffer-size bug is the standing
lesson for this pattern.

Compile-only-verified; nothing in the existing UEFI/QEMU path calls
rpi5_native_boot(), so this cannot be exercised until real hardware.
Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 22:24:57 -04:00
Robert Allan JamesandClaude Sonnet 5 67e3fb7459 rpi5_native_boot.c: Pi 5 DTB->BootInfo constructor (FABRIC-3.md §IV.3 item 2)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
rpi5_native_boot() populates the existing BootInfo struct from the
devicetree instead of UEFI protocols, then calls the existing, unmodified
kernel_main() -- the crux of why most of M1-M9 stays shared between the
UEFI and native boot paths. native_rpi5_entry.S now tail-calls into it
instead of halting.

memory_map is built from /memory's own reg, honoring the *root* node's
#address-cells/#size-cells (confirmed against bcm2712.dtsi's actual root
node -- <2>/<2> -- not assumed; a wrong cell width here would compile and
boot clean in QEMU while silently corrupting the real memory map on real
silicon). Required a new fdt_find_node_by_device_type() since /memory is
identified by device_type = "memory" per DT spec, not compatible.

args comes from /chosen's bootargs fed into the existing
cmdline_parse_ascii() (confirmed pure C99 with no UEFI coupling before
reusing it). framebuffer comes from the already-built
rpi5_mailbox_get_framebuffer() at a fixed 1920x1080x32 default -- no EDID
query exists in this codebase, flagged rather than guessed past.

Deliberately scoped out, not silently skipped: /reserved-memory is not
parsed. Carving reserved sub-ranges out of /memory's span needs
interval-splitting logic that would be written blind against hardware not
yet in hand -- exactly the kind of code that hides a bug until real
silicon. pmm.c's Pass 3 only ever clears pages this file lists as
EfiConventionalMemory, so the gap is "less usable RAM than optimal," never
"reserved RAM wrongly marked free." Left as its own future item.

Also fixes a real link failure this work surfaced: boot/cmdline.c was only
in LOADER_SRCS_BASE (the .efi target), not KERNEL_SRCS_BASE (the separate
.elf target arch/aarch64/*.c also wildcards into) -- added it there too.

Compile-only-verified; nothing in the existing UEFI/QEMU path calls
rpi5_native_boot(), so this cannot be exercised until real hardware.
Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 14:42:32 -04:00
Robert Allan JamesandClaude Sonnet 5 281de9547c native_rpi5_entry.S: Pi 5 native (non-UEFI) boot entry stub (FABRIC-3.md §IV.3 item 1)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
rpi5_native_start masks x0 down to the documented 32-bit DTB-pointer range
(the firmware's own entry protocol leaves the upper 32 bits unspecified),
stores it into g_rpi5_dtb_ptr for the still-open DTB->BootInfo constructor
(item 2) to read, then switches sp to a dedicated 2 MiB BSS stack -- this
path has no EDK2 boot stack to inherit, unlike every other entry path in
this codebase.

Intentionally halts (wfe/b loop) afterward rather than tail-calling into
item 2's constructor, which doesn't exist yet -- no stub function pretending
to be more than it is.

Not yet linked at the real 0x80000 load address; that needs its own linker
script/build target, not scoped into this item. Compiles and links into the
existing ARCH=aarch64 QEMU/UEFI acceptance build as dead code (ELF kernel
build's KERNEL_ASM wildcards every *.S in arch/aarch64/; nothing there
branches to it), same as rpi5_dtb.c/rpi5_mailbox.c before it.

Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 14:12:42 -04:00
Robert Allan JamesandClaude Sonnet 5 a32b0ebcbe rpi5_mailbox.c: wire the VideoCore mailbox message protocol (FABRIC-3.md §IV.3 item 3)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
New rpi5_mailbox_get_framebuffer() sends one property-tag request buffer
(phys size, virt size, depth, pixel order, virtual offset, allocate-buffer,
get-pitch) over the register layout rpi5_dtb.c already discovers, populating
an Rpi5FramebufferInfo kept in exact field-for-field sync with uefi.h's
FramebufferInfo so console.c/vt100.c/framebuffer.c need no downstream changes
once this is wired into a real entry stub.

Register offsets (+0x00/+0x18 MBOX0 read/status, +0x20/+0x38 MBOX1
write/status) confirmed against a Pi-5-specific bare-metal reference,
independently cross-checked against this codebase's own rpi5_dtb.c
translated base address.

Caught and fixed a real buffer-overflow bug before compiling: the static
request buffer was sized 32 words against an actual 35-word requirement for
the 7-tag sequence, recomputed exactly rather than re-estimated; resized to
40 words.

Two things flagged as genuinely unverified against real hardware (not
guessed past the comment): the allocate-buffer tag's request-size field
value, and whether its response address needs classic bus-alias masking on
Pi 5 specifically.

Compile-only-verified -- no caller yet (that's the still-open entry-stub/DTB
constructor items). Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 14:00:32 -04:00
Robert Allan JamesandClaude Sonnet 5 ca52ce8243 rpi5_dtb.c: wire fdt.c's node-scoped lookup into the Pi 5 UART/mailbox addresses
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
New include/starkernel/rpi5_dtb.h / src/starkernel/arch/aarch64/rpi5_dtb.c:
rpi5_uart_base()/rpi5_mailbox_base(), each finding their peripheral by
compatible string ("arm,pl011" / "brcm,bcm2835-mbox") via fdt_find_node_by_
compatible() then reading its "reg" via fdt_find_prop_in_node().

Found and fixed a real translation gap before it could have silently
produced a wrong address: confirmed directly against bcm2712.dtsi
(raspberrypi/linux) that both peripherals live under one "soc"
simple-bus node whose ranges property adds a fixed 0x10_0000_0000
offset to every child reg value. fdt.c's reader deliberately doesn't
apply ranges translation generally (not a general devicetree
library); this file applies that one, fixed, SoC-wide offset
explicitly, documented with the exact devicetree excerpt that
confirmed it.

Compile-only verification -- no caller wired in yet, these two
functions are what the still-open entry-stub and mailbox-framebuffer-
driver punch-list items will call. Verified 3-arch boot to ok>
(amd64/aarch64/riscv64, each in the foreground; rpi5_dtb.o confirmed
built on aarch64, the only arch that compiles this file).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 13:36:08 -04:00
Robert Allan JamesandClaude Sonnet 5 5e46f18fd9 fdt.c: node-scoped lookup extension (FABRIC-3.md SSIV.3/SSV.3 shared item)
Build / build-riscv64-img (push) Canceled after 0s
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Adds fdt_find_node_by_compatible() and fdt_find_prop_in_node() to the
minimal FDT reader -- the extension fdt.h's own header comment already
flagged as a known future need ("item 0.6 will need node-scoped reg
lookups"), now with real consumers: the Pi 5's UART/mailbox register
addresses (native boot, no ACPI) and the Milk-V Mars's real PLIC base
address (currently hardcoded to QEMU-virt's own value).

fdt_find_node_by_compatible() matches any entry in a node's
NUL-separated "compatible" list, first match in document order.
fdt_find_prop_in_node() scopes to that one node's own direct
properties only -- stops at the first child node or the node's own
end, per the DT spec's ordering guarantee that a node's properties
always precede its children. Same minimal, non-tree-building,
single-linear-scan-per-call style as the existing reader; no new
state, no allocation.

Compile-only verification -- no caller wired in yet, this is the
shared primitive both boards' own punch-list items will call once
built. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in
the foreground).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 13:05:42 -04:00
Robert Allan JamesandClaude Sonnet 5 bffd87615d FABRIC-3.md: riscv64 (Milk-V Mars) planning decided + punch list
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
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
2026-09-04 12:55:28 -04:00
Robert Allan JamesandClaude Sonnet 5 1ec2a29384 FABRIC-3.md: aarch64 (Pi 5) planning decided + punch list
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Decisions from conversation: observation is HDMI-only for this
board's own bring-up (no second Pi, no dedicated USB-serial adapter;
using the Milk-V Mars as a bridge before it's independently validated
would be circular). Both boards arrive 2026-09-17, giving real runway
to finish design/code work first ("plan well before doing").

Traced boot_info->acpi_table's actual aarch64 consumers against real
code before writing the punch list: only pci_init() (unconditional,
relevant since RP1 is PCIe-attached) and this session's own
running_under_hypervisor() (already degrades safely on NULL). GIC
init (arch/aarch64/apic.c) already only tries boot_info->dtb, never
acpi_table -- Pi 5 native boot removes its stated blocker for free.

6-item no-hardware-needed punch list: entry stub at 0x80000, a DTB ->
BootInfo constructor calling the existing unmodified kernel_main()
(the crux of why most of M1-M9 stays shared), the mailbox-interface
framebuffer driver, an fdt.c node-scoped-lookup extension (its own
header already flagged this as a known future need), a DTB-based
pci_init() path for RP1, and config.txt contents. Plus 5 items
deliberately deferred until hardware is in hand 2026-09-17.

Also fixed a formatting slip from an in-progress edit (a bullet
accidentally turned into a malformed heading) before it could compound.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:52:37 -04:00
Robert Allan JamesandClaude Sonnet 5 9142d50b73 FABRIC-3.md: Zynq-7000 (Puzhi PZ7010/PZ7020 StarLite) added to hardware reference
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Recorded only, no work scoped -- but unlike BeagleBone Black, this
one isn't a random addition: ROADMAP.md already names Zynq FPGA as
the next big milestone beyond v2.5.0 (the "configurable silicon"
step, tied to the three-product split's HOL-proven sealed-execution
hardware product). This puts a concrete, purchasable board under that
already-named milestone: Puzhi PZ7010-StarLite (XC7Z010) or
PZ7020-StarLite (XC7Z020), dual-core Cortex-A9 PS + Artix-7/Kintex-7
PL fabric, DDR3/JTAG/UART/HDMI/GbE/USB2.0/40-pin, MIPI CSI on the
7020 variant only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:44:57 -04:00
Robert Allan JamesandClaude Sonnet 5 5eeb993ca2 FABRIC-3.md: riscv64 boot chain resolved, BeagleBone Black noted, header bug fixed
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Milk-V Mars boot chain resolved via VisionFive 2 research: the Mars is
a documented mainline U-Boot target using the exact same binaries as
the VisionFive 2 (same StarFive JH7110 SoC) -- U-Boot + OpenSBI +
devicetree, not UEFI, same fork already decided for aarch64. Entry
point 0x40000000, mhartid CSR for core ID, UART at 0x10000000/115200 --
DTB pointer/hart-ID register convention at actual kernel entry not yet
confirmed for this specific chain.

Fixes a real bug from the earlier aarch64 edit: the "## V. riscv64 --
Milk-V Mars" section header had been accidentally dropped, leaving a
stale duplicate "Already true"/"Genuinely open" block sitting where
the real section should have started. Removed the duplicate, restored
the header, folded today's research into the section properly.

BeagleBone Black added to SS VI's hardware-identification reference per
direct instruction -- recorded only, no work scoped around it: it's a
32-bit ARM (TI AM335x, Cortex-A8) SoC, a genuinely different, fourth
architecture this kernel has no support for, not another board under
an existing one.

FABRIC-4.md SS2: captured the pinned-GPIO-VM idea raised in conversation
(Pi 5 needs one, Milk-V Mars explicitly undecided) as a theory-stage
note, cross-referenced from SSIV/SSV's own open-questions lists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:43:15 -04:00
Robert Allan JamesandClaude Sonnet 5 987c751352 FABRIC-3.md: per-board hardware-identification reference (SS VI)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Consolidated SoC/CPU/RAM/I-O facts for all three real-hardware
targets, researched via web search with sources cited rather than
assumed from memory:

- amd64 (Beelink SER5): Ryzen 7 family confirmed; exact SKU
  (5700U/5800H/7735HS all shipped under this branding) not yet
  confirmed against the actual unit -- flagged, not guessed.
- aarch64 (Raspberry Pi 5): BCM2712, quad-core Cortex-A76 @ 2.4GHz,
  VideoCore VII GPU, LPDDR4X-4267. FEAT_RNG (RNDR) presence on this
  core flagged as unconfirmed either way.
- riscv64 (Milk-V Mars): StarFive JH7110, 4x SiFive U74-MC + 1x S7
  monitor core, up to 1.5GHz. Noted it shares its SoC with the
  StarFive VisionFive 2 -- existing VisionFive 2 bring-up material
  may transfer directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:37:29 -04:00
Robert Allan JamesandClaude Sonnet 5 a66af477ac FABRIC-3.md: aarch64 boot-chain decision -- native boot flow, not UEFI
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Researched both options before deciding. The UEFI path (rpi5-uefi,
TF-A+EDK2, SBBR-compliant) is real but archived since 2025-02-04 --
support ended when newer Pi EEPROM firmware broke compatibility, and
its own README says ACPI support is limited/incomplete. Decided:
native boot flow instead (config.txt/kernel_2712.img/DTB, x0=DTB
pointer at entry, no ACPI at all).

Named the real scope rather than estimating it small: a new,
non-UEFI aarch64 entry path, a DTB-driven BootInfo equivalent, and a
new mailbox-property-interface framebuffer driver (no precedent in
this codebase). The one genuine piece of reusable groundwork:
starkernel/hal/fdt.c's minimal FDT reader, already built for
riscv64's timebase-frequency lookup, extends directly to Pi 5
peripheral discovery.

Also documents the peripheral-RNG research: BCM2712 has no
brcm,bcm2712-rng200 (or equivalent) entry anywhere in current
mainline Linux, and RP1's own published peripheral list doesn't
mention an RNG -- genuinely unresolved, not just under-researched.

Not yet turned into a punch list -- the boot-chain fork's own shape
needs thinking through first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:34:30 -04:00
Robert Allan JamesandClaude Sonnet 5 e5e28d5198 FABRIC-3.md: amd64 bare-metal-boot planning decided + punch list
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Decisions from conversation: observation is HDMI (interactive) +
serial via the Raspberry Pi's own GPIO UART as the bridge, if the SER5
exposes a UART header (not yet confirmed); genericity is verified by a
code audit against real UEFI/ACPI standards rather than a second
physical machine (none available); Secure Boot is already disabled on
this SER5, so no signed-loader work is needed for this pass.

9-item punch list follows: build+flash the thumbdrive image, physically
inspect for a UART header, connect HDMI, boot, a code-audit pass for
SER5-specific assumptions (the actual genericity bar), capture the
boot (POST/ok>/rdrand backend/serial transcript), mint+re-attach Zuse
on real hardware, then update this section with results before moving
to aarch64.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:27:38 -04:00
Robert Allan JamesandClaude Sonnet 5 7f9a4d4d4a FABRIC-3.md: three per-architecture bare-metal-boot planning sections
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
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
2026-09-04 12:02:45 -04:00
Robert Allan JamesandClaude Sonnet 5 8717416d36 FABRIC-3.md: version correction -- LITHOS_VERSION back to 2.0.0, plus a rename-gap fix
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
LITHOS_VERSION 2.0.1 was premature: per this project's own versioning
policy, 2.0.1 claims SER5 hardware-track progress (RDRAND backend +
thumbdrive image) that was never actually verified on real hardware --
that verification is FABRIC-3.md's own open topic. Reset to 2.0.0
(still a QEMU-only release, correctly). Verified 3-arch boot shows
"LithosAnanke v2.0.0" in each serial log directly, not assumed from the
Makefile edit alone.

Also closes a real gap found in today's earlier FABRIC-series rename:
Makefile.starkernel, Kconfig.kernel, scripts/bleach_zuse_img.sh, four
proof/*.thy files, and isr.S were never swept -- the original file list
only matched *.md/*.c/*.h/*.4th, silently skipping every other
extension. Fixed with the same safe placeholder substitution.
.claude/settings.local.json's historical permission-grant log and
ClaudeEXPORT/'s frozen export were deliberately left untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
v2.0.0
2026-09-04 11:53:44 -04:00
Robert Allan JamesandClaude Sonnet 5 fcba528273 FABRIC-3.md: Task 1 closed -- master fast-forwarded to v2.0.1, verified
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
master (d2a0305) was a strict ancestor of v2.0.1 (72c14cb) -- pure
fast-forward, no real merge needed. Fast-forwarded local master,
pushed to origin (58c59e8..72c14cb). Verified on a genuinely clean
master tree rather than inferring from the fast-forward: hosted build
clean, 3-arch kernel acceptance (amd64/aarch64/riscv64, each in the
foreground) all reached ok>/zuse)ok> and shut down cleanly, identical
to v2.0.1's own last acceptance pass. master and v2.0.1 are now
identical on both local and origin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:37:29 -04:00
Robert Allan JamesandClaude Sonnet 5 72c14cb9eb FABRIC-3.md: open new living document (bare metal boot); close FABRIC-2.md
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
FABRIC-2.md closed/archival as of 2026-09-04 -- its full punch list
(§I) was worked through to completion this session. FABRIC-3.md opens
as the new living document, topic: bare metal boot (FABRIC-2.md §I.6's
own Milestone 8, the one item that couldn't close from a coding
session -- needs a real machine and a human present).

First task written up before executing, per this series' own standing
discipline: merge v2.0.1 into master and verify build/function
equivalence on a clean master tree. Investigated the branch topology
first -- master is a strict ancestor of v2.0.1 (47 commits behind, no
divergent history), so this is a pure fast-forward, not a real merge.

Updated FABRIC-4.md's and .claude/CLAUDE.md's stale pointers at
FABRIC-2.md as "current/living" to point at FABRIC-3.md instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:31:04 -04:00
Robert Allan JamesandClaude Sonnet 5 b031b802e3 Rename FABRIC series: FABRIC.md->0, FABRIC-2.md->1, FABRIC-3.md->2, FABRIC-4.md unchanged
FABRIC.md -> FABRIC-0.md
FABRIC-2.md -> FABRIC-1.md
FABRIC-3.md -> FABRIC-2.md (the current/living document)
FABRIC-4.md unchanged (new #3 to follow separately)

Every cross-reference repo-wide updated to match, including doc-comment
citations inside kernel source (.c/.h) files -- done via an ordered
placeholder substitution (FABRIC-3.md->placeholder2, FABRIC-2.md->
placeholder1, FABRIC.md->placeholder0, then placeholders resolved to
final names) in a single pass per file to avoid double-shifting
already-renamed references.

One line in capsules/font.4th grew past the 64-char block-format limit
as a side effect of the longer filename; shortened it and reverified
with mkcapsule --lint (34/34 pass) before rebuilding.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground) after the fix; logs and DoE CSVs from this session's
verification runs included per this repo's own audit-artifact
convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:22:51 -04:00
Robert Allan JamesandClaude Sonnet 5 ff2941dfb9 FABRIC-3.md §I.7: close Milestone 9 deferral now that its precondition is met
The item only ever tracked whether the deferral itself was still
correctly in force, not the networking work. Milestone 7 (§I.5) closed
this session, satisfying the precondition, so the deferral resolves --
Milestone 9's actual networking design stays a separate, still-unscoped
effort, correctly out of scope on its own terms (FABRIC-2.md's own
sequencing: after ACL/PKI/contrib, not concurrent with it).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:05:51 -04:00
Robert Allan JamesandClaude Sonnet 5 eeceec21a5 FABRIC-3.md §I.5: Milestone 7 trust tiers (QEMU-vs-real-hardware), closing it
Closes the contributor-capsule/trust-tier punch-list item. Decided
direction: QEMU-vs-real-hardware conditional enforcement.

Found before building on that decision: the obvious mechanism (expose
TimerInfo.vm_mode) only works on amd64 -- aarch64 and riscv64 both had
vm_mode hardcoded to 1 unconditionally, meaning they'd always report
"running under QEMU" even on real hardware. Built real detection for
both instead of shipping that: aarch64 checks the ACPI RSDP's OEM ID
for QEMU's "BOCHS " SeaBIOS-heritage signature; riscv64 checks the
devicetree root compatible property for "qemu". Confirmed vm_mode was
otherwise unread anywhere else in either file first -- zero risk to
existing timing behavior.

CAPSULE_FLAG_CONTRIB (mkcapsule.c: FLAG_CONTRIB) path-matches on
capsules/contrib/, mirroring FLAG_MAMA_INIT's exact-match pattern.
contrib_capsule_refused() (capsule_birth.c) enforces: no additional
check under QEMU (same WARN-only as everything else); on real hardware,
a contrib capsule additionally requires CAPSULE_SIG_OK, since it has no
other provenance to fall back on. Wired into capsule_birth_baby() and
capsule_run_experiment().

Also updates §I.7 (Milestone 9): its stated precondition (Milestone 7
closing) is now met, flagged as stale rather than treated as a green
light to design networking from nothing.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground) -- compile/boot verification only; the real-hardware
enforcement branch is unverifiable from this environment, same as all
of §I.6. logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:04:25 -04:00
Robert Allan JamesandClaude Sonnet 5 5567d03c12 FABRIC-3.md §I.4: magic-number content-type detection, closing Milestone 6
Closes both remaining Milestone 6 (PKI) punch-list items.

Signature-status column: doc-only closure -- the underlying need was
already redirected to capsules/BLOCK_MAP.md's real Signed column
(2026-08-26); checking the box off as moot-as-worded rather than
leaving an accurate-but-permanently-unchecked marker.

Magic-number content-type detection (Section U item 14): built in
tools/mkcapsule.c. detect_content_type() classifies a file's actual
leading bytes (TTF/OpenType sfnt tags, DER's 0x30 SEQUENCE tag, or a
printable-ASCII/TAB/CR/LF heuristic for text) against
expected_type_from_ext()'s .4th/.md/.der/.ttf mapping; process_file()
warns on mismatch, never refuses -- same WARN-first rollout this
project already used for capsule signing. Verified against every real
capsule in the repo (38 files) with zero false positives. This is the
shared primitive Milestone 7's contrib-capsule validation can reuse
next.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground); logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 10:45:57 -04:00
Robert Allan JamesandClaude Sonnet 5 4018fe8b04 FABRIC-3.md §I.2: FIRSTTOUCH + migration state machine (blk_meta_relocate_devblock)
Closes the block-subsystem punch-list item -- built exactly to §F.11's
already-decided algorithm after re-verifying it against current
blk_meta_t (a 2026-09-03 re-scoping note had wrongly claimed the chain
fields no longer existed; they do, untouched by BMAPFMT).

blk_firsttouch_claim(): one linear scan of Artemis's own device
(new blk_get_first_disk_range(), correctly bounding the scan instead of
the global multi-device LBN space), scattered-chain claim via
prev_block/next_block/chain_length, owner_fp stamped on every member
devblock, fails outright with no partial claim.

blk_meta_relocate_devblock(): the real migration primitive -- bridges
the existing FORTH-block-granularity blk_subsys_relocate_block() up to
devblock granularity (BLK_PACK_RATIO=3, corrected mid-design), running
it 3x and transferring blk_meta_t ownership fields. The "migration
state machine" turned out to be just the 2 states BLK_FLAG_MIGRATING
already reserved; the real design work was the trigger. Two were
scoped in conversation (overflow onto Artemis; heat-based wear
leveling); heat/wear-leveling is built and wired into sk_repl_idle()
via blk_meta_t.write_count. Overflow is deliberately left open,
precisely scoped (needs a slot-lookup-by-device-pointer call site
threaded from WIREBIND) rather than guessed at.

Also flagged, not fixed: BMAPFMT's owner_fp/CLAIMED and the pre-existing
BAM allocator are two parallel, unreconciled accounting systems --
FIRSTTOUCH/relocate only touch the former.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground); logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 10:33:47 -04:00
Robert Allan JamesandClaude Sonnet 5 1d468a65b1 FABRIC-3.md §I.1: (user) console prompt segment, closing the 4.4s->4.3->1.11 chain
Extends the REPL prompt to "[VM name] (user) ok>" (e.g. "[Hera]
(zuse) ok>") per the locked FABRIC.md §4.4s spec, unblocked by this
session's own §I.3/§I.8 identity-tracking work. Adds
capsule_wirebind_attached_username() alongside the existing tracked
VMUuid, and a new sk_print_prompt() helper (repl.c) that all three
prompt call sites now go through -- checks Zuse first, then a WIREBIND
user, prints nothing when neither is attached.

Closes 4.4s, 4.3 (console umbrella), and formally settles 1.11
(dirty-event granularity) as region-based per FABRIC-2.md's own "no
independent path" ruling -- a decision closure only, not an
implementation, so §17.4 (framebuffer heat/decay physics) stays open,
re-scoped precisely: blocked on the dirty-region-tracking mechanism
existing, not on 1.11's decision.

Documents a reported-but-unreproduced terminal defect (§I.9) as a new
punch-list item -- investigated the readline/keyboard-bridge code
paths, found nothing conclusive, needs a live repro with a serial log
before it's actionable.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground); logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 09:42:26 -04:00
Robert Allan JamesandClaude Sonnet 5 60d9c2520e FABRIC-3.md §I.3/§I.8: WIREBIND EJECT/detach + EXPIRE re-scoped as logout
Closes §I.3 (Milestone 5 remainder): WIREBIND now tracks which VM is
attached via the home-blocks USB path, and a new EJECT word plus the
existing hot-unplug signal both flush/reset-console/kill through it
(FABRIC-3.md §F.10).

Closes §I.8 (EXPIRE/ACL), re-scoped: the original "admit the zuse
session as a Stadium patron and reap on TTL" plan was invalidated a
second time -- Zuse authenticates directly onto Hera, who is patron
zero and permanently pinned, so there is no patron for a reap sweep to
ever find. Built instead as a detach-triggered logout
(capsule_zuse_boot_logout()), the same trigger EJECT/hot-unplug use for
regular WIREBIND users, so neither identity is a special case. Required
a companion fix: install_and_activate() used to skip re-running
ACL-ZUSE-BOOT whenever the cert was already installed, which made a
logout permanent for the rest of the boot; the outer re-attach gate now
checks zuse_session (clears on logout) instead of zuse_cert_installed
(a deliberate permanent one-way ratchet, left untouched).

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground) after both steps; logs and DoE CSVs from this session's
verification runs included per this repo's own audit-artifact
convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 09:21:01 -04:00
Robert Allan JamesandClaude Sonnet 5 34203613bb FABRIC-4.md: new forward-looking design-notes scratchpad
Not a successor to FABRIC-3.md (still the living document) -- a
separate, lower-discipline space for theory-stage ideas caught before
they have real scope. First entry: the Stadium-level "wheel" idea from
conversation with Captain Bob -- a second, Q48.16 phase-accumulator
clock fed by RWOT + the inference engine, meant to schedule when a
future sieve-stack slips in, rate starting fixed and graduating to
adaptive later, mirroring L8's own hardcoded-to-inferred history.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 14:11:08 -04:00
Robert Allan JamesandClaude Sonnet 5 b16b54f5a8 FABRIC-3.md: add §I, consolidated live punch list of open items
Groups the 23 confirmed-still-open items from the 2026-09-03 stale-
checkbox audit (commit 403f53d) by subsystem instead of scattered
document chronology, each pointing back to its full original entry.
Doesn't replace §A/§B as historical record -- it's the live index;
closing an item here should also update its original location.

Two findings surfaced while writing this:
- The block-map first-touch allocation item's own described mechanism
  (blk_meta_t prev_block/next_block/chain_length) is stale -- those
  fields don't exist since §H.12's BMAPFMT rebuild; flagged for
  re-scoping against the current layout.
- EXPIRE (ACL)'s stated blocker ("Phase 8 doesn't exist yet") is now
  stale -- Phase 8 closed 2026-08-26/27 -- but the real remaining gaps
  (no zuse-session lifetime/logout, no Stadium generic ttl-reap
  mechanism) are narrower and still real, so it's flagged, not unblocked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 13:48:33 -04:00
Robert Allan JamesandClaude Sonnet 5 403f53d056 FABRIC-3.md: retroactively check 11 stale-carried-forward items
Audited all 34 unchecked checklist lines against the actual codebase.
Checked off 11 that were already done -- most were completed in
FABRIC-2.md/FABRIC-3.md sections written after the item's own carry-
forward, never reconciled back to the original checkbox: the DoE
Stadium-substrate re-run (5.1), xHCI stall recovery (G.1), CERTVERIFY,
the whole BINDSTEP cluster (key/lock design, hotplug-to-birth wiring,
USE guard), and the Milestone 6 PKI generation/embedding/signing steps.
Each gets a RETROACTIVELY CHECKED annotation citing real file:line
evidence. One item (MANIFEST_AUTO.md signature column) investigated and
left unchecked on purpose -- the goal was met via a different file
(BLOCK_MAP.md's Signed column), not the one this item names.
The other 23 unchecked items were verified still genuinely open (several
require real hardware, not verifiable from code) and left untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 13:34:29 -04:00
Robert Allan JamesandClaude Sonnet 5 2b9fa02354 §H.12 steps 21-22: ELEVATE-REQUEST + ELEVATE-GRANT + SEND-ELEVATE-REQUEST
Phase 7 complete, closing out §H.12's punch list. MSG-DELIVER turned out
to VM-EXEC payload text directly rather than dispatching by type, so the
"handler" is ELEVATE-GRANT, a word the delivered text calls. New Hera-only
C primitives (ZUSE-ELIGIBLE?, NAME>XT, ELEVATE-PUBKEY-UNPACK) stay plain
and unconditional; capsules/zuse-eligibility.4th composes the actual
eligibility check + ACL-ALLOW!/ACL-TTL! grant in FORTH.
SEND-ELEVATE-REQUEST (common:messaging.4th) builds the payload text and
sends it via the item-20-gated CH-REQUEST.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 13:15:40 -04:00
Robert Allan JamesandClaude Sonnet 5 cb6e079a73 §H.12 step 20: CH-REQUEST initiator-only gate (MY-CH-ID)
Added VARIABLE MY-CH-ID to messaging.4th (fail-closed -1 default) and
set it per-VM in hermes/init.4th and artemis/init.4th. CH-REQUEST now
refuses if the caller-supplied 'from' doesn't match the calling VM's
own id, closing a real spoofing gap found while implementing this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 12:09:29 -04:00