Punch list §25 item 0.4 complete.
Adds aarch64_current_el() in arch.c: reads CurrentEL[3:2] on first call,
caches the result (CurrentEL cannot change post-ExitBootServices, so every
consumer gets the same answer without repeating the MRS). Called from
arch_interrupts_init() in interrupts.c -- the earliest point with both a
working console (up since M1) and a genuine first consumer (vector
installation is the first EL-dependent operation) -- and the detected level
is printed to the boot log there.
Declared via extern-in-place in interrupts.c rather than added to the shared
arch.h: "exception level" has no amd64/riscv64 equivalent, matching the
convention already used for riscv64_timer_rearm() in item 0.3.
Verified on real QEMU output: "AArch64: running at EL1", correctly positioned
immediately before "IDT installed." in the serial log. Boots clean, dict_hash
0x3d4e1daf289da94f unchanged from the item 0.1-0.3 baseline.
Scope: this item establishes the detection and exposes it; it does not yet
change VBAR/ELR/SPSR or timer-register selection to use it. arch_interrupts_init()
still writes VBAR_EL1 unconditionally, and now says so explicitly in its own
doc comment -- if aarch64_current_el() ever reports 2 on real hardware,
exceptions taken at EL2 vector through VBAR_EL2, which nothing programs yet.
That gap is items 0.5 (vectors/saved-state) and 0.7 (CNTP vs CNTHP) to close,
per FABRIC.md's GAP-B3 finding. The boot-log EL2 case prints a note pointing
at both.
Only aarch64-scoped files touched (arch.c, interrupts.c) -- no shared loader
or header changed, so amd64 and riscv64 are provably unaffected; not rebuilt
for this item.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 0.3 complete.
The functional work (FDT reader, `time` CSR switch, SBI TIME extension
arm/re-arm, sie.STIE) was committed separately by Captain Bob as accd79f,
honestly labeled "NOT complete." This finishes it: stale documentation
cleanup, then rigorous verification against the item's literal acceptance
text, which the prior commit had not yet done.
Doc cleanup: every remaining reference to `rdcycle` / "assumed 1 GHz" in
riscv64/timer.c and riscv64/apic.c rewritten to describe the actual `time`
CSR / SBI-armed behavior. Caught and corrected my own arithmetic error in
the process: a draft claimed the ns-overflow bound improved to "~58 years"
under the new counter; direct computation shows the bound is actually
~3.26 days (2^48 ns) and is *frequency-independent* -- s_ns_per_tick and
tick rate scale inversely and cancel. Verified with a script before
writing the final comment, not asserted.
Verification, since "boots to prompt" was not sufficient for this item's
acceptance ("heartbeat_ticks() advances ... within measurement noise"):
- No FORTH word exposes heartbeat_ticks() to the REPL, and adding one would
be a new primitive outside this item's scope. GDB-over-QEMU-stub reads of
the static counter failed ("Cannot access memory"), likely a virtual-vs-
runtime address mismatch; abandoned rather than debugged further, since a
better instrument was available.
- QEMU's own `-d int` trap tracing (independent of all guest code) shows
2,797 `cause:5 desc=s_timer` interrupts delivered by the time boot reaches
the prompt, and the interleaved `desc=supervisor_ecall` entries are exactly
the SBI set_timer calls from riscv64_timer_rearm(), confirming the full
interrupt -> handler -> re-arm -> SBI loop.
- Measured rate over a real 10.127 s window: 1,013 further interrupts,
100.028 Hz observed against 100 Hz configured -- 0.03% deviation. The
sustained, non-decaying rate is itself proof the one-shot re-arm succeeds
on every cycle, not just the first.
- Confirmed regression-free on amd64 and aarch64 too: uefi_loader.c, uefi.h,
boot_info_offsets.h and the new fdt.c/fdt.h are shared across all three
builds (amd64 also consumes the offset constants via kernel_entry.S), so
all three were rebuilt and booted. Identical dict_hash
0x3d4e1daf289da94f on all three, matching the item 0.1/0.2 baseline.
Reported, not fixed, per rule 3:
- DOE_INJECT=1's EXEC-DOE now fails as "UNKNOWN WORD" against the pruned
Hera-alone capsule -- doe.4th was never loaded by init.4th even before
item 0.1's prune, so this is a pre-existing gap the prune surfaced, not a
new defect. Discovered while chasing why a 200-rep injected DoE run
produced no new CSV rows after 25 minutes; it had failed in the first
millisecond, not run the whole time.
- repl.c's own comment claims a "Heartbeat: N ticks" diagnostic prints to
the serial log; no such print exists anywhere in the tree.
- riscv64/arch.c's arch_read_timestamp() still uses rdcycle with a stale
1 GHz comment, but it has zero callers on this architecture -- dead code,
left alone.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 0.3 NOT complete.
- Added `starkernel/fdt.h` and `fdt.c` for minimal read-only devicetree parsing: sufficient for boot-time lookups such as `timebase-frequency`.
- Bootloader now captures the devicetree blob (DTB) from `EFI_DTB_TABLE_GUID` into `BootInfo::dtb`.
- RISC-V timer subsystem now uses the `time` CSR as the primary timestamp source, abandoning the hardcoded `cycle` frequency assumption.
- Timer rate is read from `timebase-frequency` in the DTB when accessible; otherwise, a fallback value is used with a RELATIVE trust level.
- Integrated the SBI TIME extension for one-shot timer deadlines, ensuring re-arming occurs after each tick to avoid missing heartbeats.
Verified: riscv64 builds clean, boots to the ok> prompt with no regression; `riscv64/timer.c` reports accurate frequencies on QEMU's default firmware.
Signed-off-by: Robert Allan James <robert.allan.james@gmail.com>
Punch list §25 item 0.2 complete.
Verified: riscv64 builds clean and boots to the ok> prompt with no regression;
dict_hash 0x3d4e1daf289da94f, unchanged from item 0.1's baseline. Disassembly
confirms the 320-byte frame, all 16 integer caller-saved registers, the FS
check, and SRET on exit; riscv64_trap_entry lands at 0x414fa8, 4-byte aligned
as stvec direct mode requires.
Not verified, and the item says so: neither new path was exercised. No timer is
armed until 0.3, so riscv64_interrupt_handler never ran, and no exception
occurred during boot, so the fatal path was not observed -- it is preserved
structurally, same branch to the same unchanged handler. This is why C2
rewrote the acceptance to no-regression rather than to having taken and
returned from a trap.
Register set is the LP64D psABI caller-saved list, not this document's summary:
integer ra/t0-t6/a0-a7 (16), FP ft0-ft11/fa0-fa7 (20) plus fcsr, and sepc +
sstatus. Callee-saved registers are the C handler's responsibility.
The FP half is conditional on sstatus.FS != Off, which the item did not
anticipate. Nothing in boot.S or kernel_entry.S programs FS, so its value is
whatever firmware leaves; touching an f-register with FS == Off raises an
illegal-instruction trap, and doing that inside the trap handler would be
unrecoverable. Omitting the FP save is not an option either -- the built
riscv64 image contains 530 FP instructions (fld, fmul.d, fcvt.lu.d among them),
confirming B2's finding against the binary rather than the build flags alone.
So the save is conditional, and sstatus is restored after the f-registers.
Dispatch: scause bit 63 routes to riscv64_interrupt_handler with scause in a0;
cause 5 (supervisor timer) calls heartbeat_tick(). Other causes are ignored
rather than fatal -- none are enabled to arrive. Everything else still falls
through to riscv64_exception_handler, unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Punch list §25 item 0.1 complete.
Verified: all three architectures build clean and boot to the ok> prompt with
Hera alone. Blocks executed are exactly 2057 -> 2049 (-> lib.4th/4050) -> 2050.
Zero occurrences of "Hermes" or "Tripod" in any of the three serial logs, and
the only parity record is MAMA_INIT -- no baby VM is born. mkcapsule --lint
passes 26 files, 0 failing, 0 violations.
Deleted blocks 2051, 2052, 2053, 2054, 2055, 2056, 2058, 2059 -- readiness
handshake, broadcast test, TRIPOD-TEST, HERMES-E2E and the fleet-DoE
scaffolding. Edited 2057 (banner), 2049 (dropped both births with their
CD-INIT calls and the common:msg.4th / process.4th loads, which are wholly
Hermes-dependent; kept lib.4th; VM-TREE and VM-CHILDREN no longer name absent
children) and 2050 (kept the BOOT-BANNER call, dropped the two calls to the
deleted words). capsules/hermes/ and capsules/artemis/ untouched on disk.
New Hera-alone parity baseline, identical across all three ISAs:
dict_hash=0x3d4e1daf289da94f capsule_hash=0xb4c4b5559146a3bd
This supersedes the pre-prune baselines in logs/ and is what item 0.10's
double-boot reproducibility check compares against.
Commit contents beyond the item's own edit, noted rather than slipped in:
capsules/BLOCK_MAP.md is regenerated by any build; the three serial logs are
this item's acceptance evidence and CLAUDE.md requires committing them; the
DoE CSVs are auto-extracted by the qemu target on every run. Leaving generated
output from this item's acceptance run for a later item to sweep up would be
worse than including it here.
Reported, not fixed: tools/mkcapsule.c emits two -Wstringop-truncation warnings
on the host build (:404, :562), pre-existing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The 30-replicate Artemis surface-stress campaign ran across all three
architectures: 30 reps x 50 trials x 3 arches = 4500 trials, zero failures.
The block_words.c stale-pointer cache-aliasing fix holds at scale.
Adds ART-STRESS-CAMPAIGN (block 4174) plus ART-STRESS-REP rep-tagging in
the CSV header/summary emitters, so a multi-replicate run is distinguishable
in the serial log. Campaign auto-invoke is left enabled in block 4170 for
now; Makefile.starkernel's QEMU boot deadlines stay at 12h to accommodate
long-running experiments.
Adds docs/working/architecture/ARTEMIS-COMPUDYNAMICS-IMPLEMENTATION-PLAN-20260802.md,
which documents the real gap this campaign exposed: block heat and message
heat do not decay at all. ART-TICK has zero call sites anywhere in the tree,
and HERMES-TICK has zero C call sites -- every caller is Hera poking it by
hand. BLK-HEAT@/MSG-HEAT@ read a number nothing ages, so blocks never reap
by cooling and message TTL never expires on its own.
The plan mirrors word-level physics as the reference model: lazy decay at
each access point against vm->heartbeat.tick_count, plus a bounded
background sweep with a resumable cursor (the existing answer to "sweeping
22,998 blocks per tick is too expensive"). Phase 1 Artemis, Phase 2 Hermes,
Phase 3 K participation deferred behind the Logical BAM.
The plan's preamble also records a wrong turn taken while investigating:
chasing VM-fleet heat instead of block heat, and building synthetic
Hera-driven VM-EXEC calls to force a physics reading -- which TRIPOD.md
prohibits outright. That work was reverted; the record is kept so it isn't
repeated.
Status: plan approved in shape, NOT final and NOT started. Six open
questions need answers and further design discussion is pending.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The generic block subsystem (blk_format_or_load_disk) auto-reformatted
any disk lacking its own low-level 'STFR' header at attach time, before
Artemis's Forth-level BLANK/LithosAnanke/Unrecognized classification
ever ran -- so ART-HALT-UNRECOG's "Disk preserved" message was false.
Split detection from commit: an unrecognized/blank disk is now left
PROVISIONAL (geometry computed in memory only, all writes refused)
until explicitly confirmed via the new blk_subsys_confirm_format() /
BLK-CONFIRM-FORMAT primitive. Artemis calls it from ART-FORMAT and
ART-RESUME, never from ART-HALT-UNRECOG.
Verified on amd64/aarch64/riscv64: parity intact (identical dict_hash),
normal recognized-disk resume + persist-read unaffected, and a
regenerated disk/artemis-unrecognized-test.img (the old copy had itself
been silently corrupted by this exact bug) now stays byte-for-byte
identical across a halted boot on amd64 and riscv64.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>