Commit Graph
62 Commits
Author SHA1 Message Date
Robert Allan JamesandClaude Sonnet 5 72487e7fff starkernel: item 3.6 -- Hera as patron zero, pinned (Phase 3 core complete)
Punch list §25 item 3.6 complete. Phase 3 (§25.4) core is now done:
items 3.1-3.6 all closed.

stadium_evict() now panics via sk_hal_panic() if a resident cell 0
(Hera, patron zero by construction of §6's boot order) is ever
selected for eviction. Placement is deliberate: the check runs before
the pin/contains refusal checks, not after -- if it ran after, a
wrongly-cleared pin would let the ordinary refusal path quietly return
-1 instead of ever reaching the panic, defeating the point of a check
that's supposed to be independent of pin holding.

Per §20.5 #3's explicit wording, not implemented as a filter:
stadium_admit()'s least-dense search is unchanged, still relying on
the general pin skip from item 3.5. Adding a second filter there would
have done exactly what that section warns against ("filtering hides
the bug, asserting reports it").

The panic path is, and will remain, unexercised by the acceptance
mechanism: sk_hal_panic() halts the machine, and triggering it
deliberately is incompatible with the three-arch boot being this
project's sole acceptance test. Correctness rests on the placement
argument, not a test -- same honesty precedent as items 3.4 and 3.5's
other unexercised paths.

Verified: three-architecture boot (amd64, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f matching the
item-3.5 baseline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:36:23 -04:00
Robert Allan JamesandClaude Sonnet 5 f8a50561b0 starkernel: item 3.5 -- admission and eviction
Punch list §25 item 3.5 complete.

stadium_admit(candidate) places into an unused cell if one exists (no
comparison needed), otherwise finds the least-dense resident -- skipping
pinned and contains-gated patrons, which are never eviction candidates
-- and evicts it only if the candidate is strictly denser, per §19.3.
stadium_evict(cell_index) dispatches the departing patron's behaviour
before clearing its slot, per §17.2.

Caught a real bug before it ran: the first draft used contains == 0 to
mean "holds nothing," but cell index 0 is a valid index (Hera, item
3.6). Fixed with a proper sentinel, STADIUM_CONTAINS_NONE (UINT32_MAX).

A second-pass review found mass was not accounted for: both functions
handled exactly one cell regardless of the candidate's stated mass,
which leaks cells on eviction of any mass > 1 patron and breaks
capacity conservation. Fixed by refusing any candidate with mass != 1
-- multi-cell patrons need the per-VM free lists item 3.2 already
deferred (§22.3), not built here.

Documented, not fixed: the discriminator bitmap can't distinguish free
from continuation cells, so the free-cell scan reads continuation-cell
payload bytes under the header layout -- latent since nothing creates
continuation cells yet, and the mass != 1 refusal keeps it provably
latent. Superseded by the free list when it exists.

Unexercised at runtime: nothing calls either function yet (no real
patron kind is wired to the Stadium). No self-test added -- filling
~74,000+ cells to reach the eviction-on-full branch was judged
impractical, following item 2.2's own precedent for its unexercised
fleet-full path.

Verified: three-architecture boot (amd64, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f matching the
item-3.4 baseline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:28:38 -04:00
Robert Allan JamesandClaude Sonnet 5 0b47c256fc starkernel: item 3.4 -- density ranking
Punch list §25 item 3.4 complete.

stadium_density(cell_index) reads a header's heat and mass and returns
heat / mass -- a division on demand from fields already stored in the
cell, matching §19.3's "read, not computed by a scheduler" literally.
Stays valid Q48.16 without a special fixed-point routine, since heat
is already Q48.16 and mass is a plain integer divisor.

mass == 0 and an out-of-range cell_index both return 0 rather than
dividing by zero -- an empty or never-admitted slot has no footprint
to be dense within.

Deliberately not built here, per the item's own wording: finding the
densest or least-dense resident (§19.3's admission/eviction
comparison) is item 3.5's scope, not this one's. Nothing calls
stadium_density() yet either.

Verified: three-architecture boot (amd64, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f matching the
item-3.3 baseline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:16:46 -04:00
Robert Allan JamesandClaude Sonnet 5 378d688898 starkernel: item 3.3 -- behaviour enumeration and dispatch
Punch list §25 item 3.3 complete.

StadiumBehaviour (stadium.h) enumerates exactly the four tags §18.3
already names -- MIGRATE, DELIVER, EXPIRE, COOL -- mapped from §17.1's
patron table: blocks->MIGRATE, messages->DELIVER, ACLs->EXPIRE, words
and VMs both->COOL. Nothing invented; the tag set and mapping were
already in the document.

stadium_dispatch(cell_index, behaviour) dispatches on the tag only,
never asks what kind of patron departed. Handlers are stubs -- the
real actions belong to subsystems not yet migrated onto the Stadium
(Phase 4). Nothing calls stadium_dispatch() yet; item 3.5 is its first
consumer.

The switch is exhaustive with no default case, making §13's "closed
enumeration, fixed at build time" a compiler-enforced property under
this project's -Wall -Werror rather than just prose. Verified live:
temporarily deleted the COOL case, rebuild failed with
error: enumeration value 'STADIUM_BEHAVIOUR_COOL' not handled in
switch [-Werror=switch], restored it, confirmed clean again.

The header's behaviour field stays uint8_t, not the enum type itself,
since C does not guarantee an enum's underlying type and that field's
offset is load-bearing for item 3.1's validated 64-byte layout.

Verified: three-architecture boot (amd64, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f matching the
item-3.2 baseline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 17:06:14 -04:00
Robert Allan JamesandClaude Sonnet 5 eb0fd4fffa starkernel: item 3.2 -- Stadium boot-time allocation
Punch list §25 item 3.2 complete.

stadium_boot_init() (src/starkernel/vm/stadium.c) sizes the global
cell array at boot from a real memory-budget query rather than a
hardcoded count: pmm_get_stats().free_bytes at the point of
allocation, times the new STADIUM_MEMORY_PERCENT Kconfig symbol
(default 1%), rounded down to whole 64-byte cells. Matches §17.6's
position (b) literally. Also allocates the header/continuation
discriminator bitmap item 3.1 declared but did not allocate. Both are
kmalloc'd and explicitly zero-filled (kmalloc does not zero).

Called from kernel_main.c immediately before sk_vm_bootstrap_parity(),
i.e. before any VM exists (§6). Failure is soft -- logs and continues,
does not halt boot -- matching the existing precedent one line below
it (VM bootstrap parity failure does the same).

Added a "Stadium: N cells (M KB)" boot console line at the allocation
site so the acceptance logs are evidence the array was actually
allocated, not just that the kernel still boots -- the same blind spot
item 3.1's uncompiled-header gap exposed.

Verified: three-architecture boot (amd64, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f matching the
item-3.1 baseline, and the Stadium boot line confirmed present in all
three serial logs (amd64: 74234 cells/4639 KB, aarch64: 161329
cells/10083 KB, riscv64: 76122 cells/4757 KB).

Not built here, reported per §25.0 rule 3: per-VM free lists (§22.3)
-- granted when Hera assigns quota, not this item's scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 16:48:13 -04:00
Robert Allan JamesandClaude Sonnet 5 1b2f0677de starkernel: item 3.1 reopened -- two Kconfig symbols items 1.1/1.4 deferred here
Punch list §25 item 3.1 re-closed after reopening.

Items 1.1 and 1.4's resolutions both explicitly named this item as
where their Kconfig symbols would be implemented, but 3.1's own stated
scope never mentioned them, so the first close missed both:

- STADIUM_CONTAINS_DEPTH_MAX (default 5) -- item 1.1's contains-chain
  depth cap. No consumer yet; reap-gating enforcement is item 3.5.
- STADIUM_CAPACITY_TICK (default 1000) -- item 1.4's capacity
  arbitration cadence in virtual ticks. No consumer yet; capacity
  arbitration itself is not on the punch list.

Both added following STADIUM_MAX_VM_COUNT's exact pattern:
Kconfig.kernel entry, Makefile.starkernel kconfig_int +
VM_FEATURE_FLAG_VARS forwarding, starforth_config.h fallback default.
stadium.h now includes starforth_config.h and carries two more
C99-portable compile-time checks proving both symbols are defined and
sane, same discipline as the byte-count checks. Declaration only --
not inventing the consuming logic to close this out early.

Verified: three-architecture boot (amd64, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f, re-run after
the reopening.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 15:15:59 -04:00
Robert Allan JamesandClaude Sonnet 5 d55ec3241b starkernel: item 3.1 -- the Stadium cell and header
Punch list §25 item 3.1 complete.

Defines StadiumPatronHeader and StadiumContinuationCell in new
include/starkernel/vm/stadium.h, unioned as StadiumCell per §3's
closed two-valued union. src/starkernel/vm/stadium.c added to
Makefile.starkernel's LOADER_EXTRA_SRCS/KERNEL_EXTRA_SRCS so the
header's compile-time size checks are actually compiled, not merely
included by something that never builds.

Discriminator ruled an external side bitmap (Captain Bob), not a
header field -- amended into §3 and §23.3 before this code was
written. Item 3.1 declares the bitmap's purpose/indexing in a comment
only; allocating it is item 3.2's scope.

Both cell shapes counted for real at exactly 64 bytes with zero
compiler-inserted padding (three C99-portable negative-array-size
assertions -- no _Static_assert, this project targets C99). Header
matches §23.3's original 32+32 split unchanged, since the
discriminator moving outside the cell left nothing to compete for that
space. Continuation cell matches item 1.12's 4+60 figure unchanged for
the same reason.

Verified the size assertion is actually live: broke it to 63,
confirmed the build failed with the expected negative-array-size
error, restored it, confirmed a clean compile.

Verified: three-architecture boot (amd64, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f matching the
item-2.2 baseline. Confirmed stadium.o present in both obj/loader/vm
and obj/kernel/vm post-build on amd64, closing the gap the item-2.2 WIP
exposed (an uncompiled header proves nothing).

Left open, not fabricated: §23.4 #2 ("does a typical message fit in
one cell") is unanswerable today -- no message patron struct exists
anywhere in this tree yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 14:55:30 -04:00
Robert Allan JamesandClaude Sonnet 5 5572e5e429 WIP: item 2.2 -- bound the VM registry (not complete, do not check off)
Partial work toward FABRIC.md punch list item 2.2. Adds the
STADIUM_MAX_VM_COUNT Kconfig symbol (default 4, per item 1.5) wired
through Makefile.starkernel, a new CAPSULE_RUN_ERR_FLEET_FULL result
code, and a vm_registry_live_count() helper in capsule_birth.c that
counts LIVE VMs only (distinct from the existing monotonic
vm_registry_count, which never decrements on death).

NOT YET DONE: nothing calls vm_registry_live_count() yet -- the actual
birth-refusal check is not wired into capsule_birth_baby(). Not built,
not boot-tested. FABRIC.md's item 2.2 checkbox is deliberately left
unchecked; this commit exists only to save in-progress work before a
pause, not to claim the item complete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 13:30:06 -04:00
Robert Allan JamesandClaude Sonnet 5 542d7dbf0d starkernel: restate VM fleet heat transfer on the virtual tick
Punch list §25 item 2.1 complete.
vm_physics_touch() no longer takes a wall-clock timestamp -- it reads
fleet_heartbeat_tick_count internally, which is execution-paced
(vm_runtime.c:143), not wall-clock. VMPhysics.last_active_ns ->
last_active_tick, VMFleetTouchSample.elapsed_us -> elapsed_ticks, and a
new explicit `touched` flag replaces the old `> 0` sentinel (tick 0 is
a legitimate value a first touch can land on, unlike wall-clock ns).

Verified: three-architecture boot (amd64 x2, aarch64, riscv64), all
reaching ok> with identical dict_hash=0x3d4e1daf289da94f matching the
item-0.10 baseline. No new compiler warnings in the touched files.

Honestly flagged, not fixed: with Tripod pruned to Hera alone (item
0.1), vm_physics_touch()'s fan-out has no other live VM to pull heat
from, so the fleet-heat-sum acceptance criterion is trivially satisfied
rather than genuinely stress-tested -- a real check needs Phase 4's
multi-VM fleet. fleet_transfer_slope_q48's seed (65536/3) was
calibrated for elapsed microseconds and has not been re-fit for elapsed
ticks; left as-is rather than guessed, deferred to item 5.1's DoE work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 13:15:50 -04:00
Robert Allan JamesandClaude Sonnet 5 3699be964d starkernel: converge the tick path and wire the adaptive heartbeat (item 0.8)
Introduces src/starkernel/heartbeat.c as the shared top/bottom-half
implementation of heartbeat_init/tick/service/ticks/trust/state, replacing
the per-architecture duplicates in amd64/riscv64/aarch64 timer.c. Each
arch's timer.c now contributes only heartbeat_read_counter() (rdtsc /
rdtime / CNTPCT_EL0). Per the GAP-A1 ruling the top half stays counter+
latch only; heartbeat_service() (called every REPL idle iteration,
unconditionally per FABRIC.md's fidelity note) does the window/variance/
trust work outside interrupt context. vm_tick()'s call sites are
unchanged -- the engine still runs on the virtual tick.

Per FABRIC.md §26 (ruled 2026-08-03): wires Loop #7's execution-derived
stable/volatile signal into the physical re-arm period. vm_runtime.c's
existing Loop #7 site now calls heartbeat_set_adaptive_period_ns() with
tick_target_ns ratio-rescaled onto a 10ms kernel base (not the hosted
10us HEARTBEAT_TICK_NS -- see §26.3 for the scale mismatch). Each
architecture's re-arm function (apic_timer_rearm() on amd64/aarch64,
riscv64_timer_rearm()) now converts heartbeat_next_period_ns() to its
own raw counter units instead of a fixed constant; amd64 gained a
rearm function it didn't previously need, since periodic-mode auto-reload
never required one before this item.

Verified: all three architectures build with no new warnings and boot
cleanly to ok> with dict_hash=0x3d4e1daf289da94f, unchanged from the
pre-change baseline -- no regression. Verified NOT achieved: live re-arm
period variation under load. A temporary diagnostic (added and reverted)
confirmed Loop #7 never actually fired during a live QEMU session -- a
synthetic word-execution loop drove ~6,500 executions, past the 1000-tick
inference frequency, without tripping vm_tick_inference_engine()'s
pre-existing !vm->rolling_window.is_warm gate. That gate predates this
item and was not investigated -- out of scope. FABRIC.md's Done-when is
amended to record this honestly rather than claim it.

Punch list §25 item 0.8 complete (per amended, weaker acceptance -- see
the item's own annotation).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 00:01:48 -04:00
Robert Allan James accd79fc70 riscv64: integrate minimal flattened devicetree reader and switch timer to time CSR
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>
2026-08-03 13:10:17 -04:00
Robert Allan James a5ed8c3d87 Initial commit — LithosAnanke kernel 2026-08-01 07:49:56 -04:00