starkernel: Phase 0 acceptance -- three-arch boot + reproducibility (item 0.10)

Adds a one-time boot diagnostic in kernel_main.c, right before sk_repl()
is entered: bounded wait for 3 real heartbeat ticks, then prints tick
count, TIME-TRUST, and variance. Needed because printing immediately
after apic_timer_start() (as first tried) measured 1 tick on amd64 and
0 on riscv64 -- not evidence the heartbeat doesn't work, just that
almost no wall time elapses between arming the timer and that point in
boot; report it honestly rather than let it stand as a false negative.

Verified this session (logs/20260804-001727, -001805, -001850,
-001948, -002021):
- All three architectures boot to ok>.
- Tick count non-zero: amd64 4, riscv64 3, aarch64 3.
- riscv64: trust=Q48_ONE exactly, variance=0 -- architecturally
  invariant counter, as designed.
- amd64: dict_hash=0x3d4e1daf289da94f, identical to the pre-item-0.8
  baseline (logs/20260803-231322) -- unchanged output, satisfying the
  GAP-A1 control.
- Two consecutive amd64 boots produced the identical dict hash --
  reproducible, no wall-clock leakage into patron state.

Phase 0 (Substrate) is complete.

Punch list §25 item 0.10 complete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-04 00:21:23 -04:00
co-authored by Claude Sonnet 5
parent bfc0cb9bfd
commit 024f8a84b5
13 changed files with 52210 additions and 2 deletions
+46
View File
@@ -662,6 +662,52 @@ static void kernel_main_deep(BootInfo *boot_info) {
0, NULL);
}
/* Phase 0 acceptance (§25.1 item 0.10): "tick count non-zero" has to be
* true, not merely likely -- the timer was just armed above, so with no
* wait here the count depends on how much boot work happened to run
* concurrently with interrupts enabled, which measured 1 tick on amd64
* and 0 on riscv64 in practice. Bounded busy-wait for a few real ticks
* (not a virtual-tick construct; §16.4/§18.5 govern patron state, not
* this one-time boot diagnostic) rather than reporting whatever count
* happened to land. */
{
uint64_t wait_start = heartbeat_ticks();
uint64_t spins = 0;
while (heartbeat_ticks() - wait_start < 3 && spins < 100000000ULL) {
arch_relax();
spins++;
}
}
console_puts("Heartbeat: ");
{
char buf[24]; uint64_t v = heartbeat_ticks(); int i = 0, j = 0; char t[24];
if (v == 0) buf[i++] = '0';
else { while (v > 0) { t[j++] = (char)('0' + (v % 10)); v /= 10; } while (j > 0) buf[i++] = t[--j]; }
buf[i] = '\0';
console_puts(buf);
}
console_puts(" ticks, trust=0x");
{
char buf[9]; uint32_t v = (uint32_t)heartbeat_trust();
for (int k = 7; k >= 0; k--) {
int nib = (int)((v >> (k * 4)) & 0xF);
buf[7 - k] = (char)(nib < 10 ? '0' + nib : 'a' + nib - 10);
}
buf[8] = '\0';
console_puts(buf);
}
console_puts(", variance=0x");
{
char buf[9]; uint32_t v = (uint32_t)heartbeat_state()->variance;
for (int k = 7; k >= 0; k--) {
int nib = (int)((v >> (k * 4)) & 0xF);
buf[7 - k] = (char)(nib < 10 ? '0' + nib : 'a' + nib - 10);
}
buf[8] = '\0';
console_puts(buf);
}
console_println("");
sk_repl(mama);
#endif