986d042aa71783145270400615d9a7835e55017a
11
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
986d042aa7 |
Stage 3: timer-driven preemptive switching, live on all 3 arches (FABRIC-3.md §XXVIII)
Fourth stage of the preemptive context-switching plan, and the biggest. LithosAnanke now genuinely, continuously preempts between Hera, Hermes, and Artemis -- timer-driven, running live for the entire remainder of every boot once the Tripod fleet registers, not a bounded probe. A real design fork was resolved before writing code: the naive approach (the timer ISR calling Stage 2's sk_vm_context_switch() directly) is broken -- Stage 0's trap frame lives on whatever stack was active at interrupt time, and jumping to a different stack via Stage 2's own independent swap mid-handler would abandon that trap frame unresumed, guaranteed corruption on the first tick. Chose the safer of two named options: the ISR only ever sets a flag and returns completely normally through its own full epilogue; the actual switch happens moments later, via Stage 2's already-proven mechanism, at a safe cooperative checkpoint on the mainline (execute_colon_word()'s per-word dispatch loop, checked on literally every word, not throttled to the existing 256-word heartbeat-tuning cadence) -- confirmed with the user that word-level granularity is fine-grained enough given the eventual Zynq FPGA target where a word is a mnemonic. New capsule_vm_switch_signal.c/.h: a purpose-built run-readiness signal, deliberately separate from capsule_vm_physics.c's execution-heat engine (that one's own header documents itself as never touched from interrupt context, by design). Slot table sized with headroom (8) rather than hardcoded to today's 3 participants, so extending participation later is another register() call, not a redesign -- per direct request to leave room for swapping the participant set. Simple linear accumulate-then- threshold for this first cut; a fancier law can replace it later without touching the mechanism around it. heartbeat_tick() gains its one deliberate, documented amendment to this file's own top-half/bottom-half discipline -- the first time this codebase reaches into VM-scheduling state from real ISR context. Registration happens only after all three VMs are fully born, right before the REPL starts -- no critical-section protection yet against being switched away mid-birth-setup. Known, flagged rough edge (not reconciled this pass): MSG-TICK's own idle-pump and this new mechanism can still independently move control between the same VMs; not observed to interact badly in verification, but not fully unified either. Verified interactively at the console on all 3 architectures with continuous background preemption running throughout -- amd64 computed `1 1 + .` -> 2, aarch64 computed `1 1 + dup DUP * . CR` -> 4, both correct, REPL fully responsive, zero fault indicators over sustained runtime. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S |
||
|
|
57ac3fc304 |
Stage 1: per-VM native stacks, allocated but not yet executed on (FABRIC-3.md §XXVIII)
Second stage of the preemptive context-switching plan. Every VM (Hera, every capsule_birth_baby()-born VM including WIREBIND identities) now gets its own dedicated 2 MiB native C stack at birth -- but nothing runs on it yet, that's Stage 2. Pure allocation-machinery proof. Design correction made before writing code: the plan called for cloning sk_vm_arena_alloc()'s guard-page pattern, but that pattern turns out to be Mama-only -- host_services.c's kernel_alloc() gives every baby VM a plain kmalloc() block for its dictionary arena, not a real guarded PMM allocation. Stacks get the real treatment instead (new sk_vm_native_stack_alloc()/_free() in arena.c): independent pmm_alloc_contiguous() + guard pages for every VM without exception, no singleton, no kmalloc fallback -- a stack overflow is exactly the failure mode guard pages exist for, and a corrupted stack could corrupt whatever saved context Stage 2 trusts. 2 MiB size matches this project's own established kernel-stack convention (g_kernel_stack/g_rpi5_native_stack), not a guess -- that one shared 2 MiB stack today already carries all VMs' combined nested VM-EXEC recursion. Three new VM struct fields, freed in vm_cleanup() alongside the existing call_stack free. Allocation failure is non-fatal to birth. All 3 architectures re-verified clean boot to ok>, no native-stack allocation failures for any Tripod-fleet VM. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S |
||
|
|
2a30212bd3 |
Real per-VM log persistence: source attribution + ACL pin (FABRIC-3.md §XXVII)
Wires the previously-unused vm_log_attributed_vm() into LOG-APPEND's kernel primitive so persisted log records carry a trustworthy source (the real attributed VM's registry name, or "HADES" pseudo-source) instead of a caller-supplied, trivially forgeable string. Drops src-addr/src-u from LOG-APPEND's stack signature accordingly. Pins LOG-APPEND via bare ACL-PIN in Artemis's own init.4th, matching BIRTH/CAPSULE-BIRTH's precedent for a privileged word that can't reach the shared, host-portable ACL.4th. Also fixes two console-banner nitpicks: a mis-rendering em dash (U+2014) in the boot banner, and drops "Emergency" from the CLI banner text. Doc corrections to artemis_sig.h/zuse_eligibility_list.h reconciling the three fixed devblock ranges now in play. LOG-FLUSH (the intended normal entry point) and level-aware log eviction remain open, flagged not fixed. Re-verified clean boot to ok> on all 3 architectures after every change. riscv64 showed one new, unrelated virtio_blk write-timeout anomaly during Artemis's early physics self-test (self-recovered, boot unaffected, sector doesn't map to the log region) -- flagged, not investigated. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S |
||
|
|
2c1b3cd695 |
Four bugs found live verifying the 8 identity thumbdrives (FABRIC-3.md §IX)
All found by actually running the identity workflow §VII/§VIII made possible, not by code review: 1. Zuse/WIREBIND cross-contamination on detach: capsule_zuse_boot_logout() and capsule_wirebind_unclean_detach() both had no device parameter, so an unrelated device detaching (while the real owner's own stayed attached) incorrectly tore down the wrong session. Both now compare the departing device against their own tracked one, mirroring capsule_wirebind.c's pre-existing g_wirebind_attached_dev precedent. 2. Dictionary-entry memory leak: vm_create_word()'s sf_malloc()'d DictEntry (plus a second per-entry allocation for transition_metrics) was never freed by vm_cleanup(), in both the hosted and kernel implementations. Caused a real kernel PANIC after 8-9 repeated VM birth/kill cycles in one boot. Fixed by walking vm->latest in both. 3. sf_malloc/sf_free (alloc_kernel.c) was a 4MB bump arena with a deliberate no-op free, sized on "VM born once, never killed" -- fix #2 alone didn't stop the panic because free() itself discarded the pointer regardless. Given a real free list (first-fit reuse). 4. Headless-console gate didn't re-engage after a mid-boot logout: the original fix (sk_console_mark_login(), one-way sticky) only gated the first login of the boot. Replaced with a live check (sk_console_identity_present()) re-evaluated continuously, including inside sk_console_readline()'s own blocking idle loop -- the console is normally sitting blocked there when a hot-unplug logout happens, so checking only at the top of the REPL loop wasn't enough. Also: MINT now verifies its own write (verify_mint(), capsule_mint.c) by reading back through the same check a real attach performs, rather than trusting blkio_write()'s BLK_OK alone -- logged via log_message(), not console_println(), per direct instruction. Verified live, amd64: the full 8-identity repeated attach/detach cycle that previously panicked at the same point every time now completes clean, and a full serial-log sweep found zero bare unauthenticated prompts anywhere in the run. Three-arch clean-qemu acceptance passed. Still open, not fixed here: a 3+-simultaneous-device USB enumeration failure found in a separate live test, not yet root-caused. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018EjXFo7mPXjUMjfJeuUUz4 |
||
|
|
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 |
||
|
|
e5cbc71f46 |
Phase 8 C (2/n): expand cert storage; NVRAM persistence crashed, reverted
Cert storage expanded from the old 16-byte placeholder to a real 32-byte seed + 32-byte pubkey. vm_zuse_cert_install() now has a kernel-side duplicate in src/starkernel/vm/vm_core.c -- the kernel build's VM_EXCLUDE list drops src/vm.c entirely (same reason vm_set_base() already has two independent copies), so the hosted-only version added earlier this session was never actually linked into the kernel. FORTH-side ZUSE-CERT-LO@/HI@ replaced with ZUSE-PUBKEY@ (i -- u) over the public half only; ACL-ZUSE-BOOT now checks ZUSE-CERT-INSTALLED? before authenticating instead of unconditionally. Attempted NVRAM-based persistence (GetVariable/SetVariable) for the first-boot mint flow: page-faulted inside OVMF's variable service (CR2 in the flash MMIO window). Moving the call site to match the one proven-safe existing SetVariable call site in this codebase produced the identical crash -- not a timing issue. Localized with debug markers (one boot): GetVariable works; SetVariable with real data never returns. The existing "working" precedent call is actually a delete-of-nonexistent-variable (size=0, data=NULL), a cheaper path that never touches flash, so it proved nothing about real writes. Root cause: this kernel's VMM never maps the region OVMF's variable service needs for real flash writes -- a genuine gap in UEFI runtime- services support, not Zuse-specific, and not obviously fixable in a 3-arch-uniform way (flash window location is firmware/arch-specific). Independently, storing the raw seed in RUNTIME_ACCESS NVRAM would have been a real security defect regardless of the crash -- readable by any later-loaded UEFI app or the booted OS. Reverted to a known-safe state: all NVRAM/mint code removed from kernel_main.c, init.4th's ACL.4th line back to its documented commented-out default. Verified clean compile and clean boot on all three architectures. Cert storage expansion (the part that works) stays. A dedicated system-identity disk (virtio-blk, already proven for writes via Artemis) is the recommended next substrate -- not yet decided or built. Full investigation documented in FABRIC-3.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U14ET9CWAtbQMbYqomKgXd |
||
|
|
39838b604f |
Quiet the POST ECW dispatch trace; three-arch clean-boot verification
vm_core.c: demote the per-word "ECW: w=... func=... 'NAME'" dispatch trace from LOG_INFO to LOG_DEBUG. POST forces the logger to LOG_TEST for the duration of the self-test run, and LOG_TEST includes LOG_INFO, so every single word execution during POST was echoing this trace -- hundreds of lines burying the actual module summaries and pass/fail tally. Still available via --log-level=debug. Combined with HEARTBEAT_DOE_LOG=0 (command-line Kconfig override, no default change -- experiments/bare_metal/'s own DoE tooling still gets HEARTBEAT_DOE_LOG=1 by default), all three architectures now boot clean: UEFI -> POST summary -> Mama birth -> Hermes self-test -> heartbeat -> ok>, with no [HADES][DOE] rows and no ECW flood. Verified by three-arch QEMU boot; logs attached. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
5a28458b21 |
starkernel: item 4.2 -- Hermes native on the Stadium (complete)
Migrates Hermes's message/channel lifecycle onto the Stadium's unified heat/capacity economy: MSG-ALLOC/FREE-NODE and CH-ALLOC/FREE-NODE now route entirely through stadium_admit()/stadium_evict(), replacing the old local free-list + independent heat-field mechanism. Eight kernel-only STADIUM-* FORTH primitives (ADMIT, EVICT, RES@, RES-PULL, RES-PUSH, HEAT@, HEAT!, WORD-HEAT), VM.stadium_vm_id threaded through all three vm_core.c dispatch sites (replacing item 4.1's hardcoded vm_uuid_hera()), and the stadium_owner[idx] fix so evict-credit lands in the VM that actually admitted a patron, not whoever owned cell 0. This session's own contribution, on top of that pre-existing implementation: found and fixed two bugs blocking the item's own K≡1.0 conservation self-check (HERMES-K was reading 0, not 65536): - Q.SLOT admission-heat fix (capsules/hermes/init.4th): MSG-SEND/ CH-ACCEPT admitted with Q.1 (the entire fleet-wide "1.0" unit) per item, a leftover from before the Stadium migration when each message/channel had its own unconstrained heat field. Instantly drained the shared, finite reservoir. - Reservoir floor for word-execution admission (stadium_words.c): stadium_word_dispatch() (item 4.1) pulls STADIUM_WORD_HEAT_QUANTUM on every word dispatch, not just first admission -- exhausts a VM's entire reservoir in ~32 dispatches, starving any application-level economy sharing that VM's reservoir before it gets a chance to pull anything. word_dispatch_pull() now clamps word-execution's own pulls to leave a Q48_ONE/3 floor (same fair-share figure COMMON-CH's own floor already uses); application-level pulls are unaffected. - STADIUM-WORD-HEAT primitive + stadium_words_resident_heat(): the floor deliberately leaves word-execution residents holding real heat, invisible to HERMES-K's original formula (MSG+CH+reservoir, no term for word patrons). Adding this term closes K to exactly 65536 on all three architectures. Also rules on two open scope questions in FABRIC.md: MBR-ALLOC/ MBR-FREE-NODE stay off the Stadium (membership records have no heat field, never did -- the acceptance bullet's inclusion of them was a completeness gesture predating a check of the actual layout), and records the effort number (12 implementation files, +759/-120 lines). Verified: all three architectures boot clean, full self-test passes, Stadium conservation closes exactly (resident_sum + reservoir = Q48_ONE) at both the C/Stadium level and the FORTH-level HERMES-K check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
3d0b9351bd |
starkernel: item 4.1 -- hot words onto the Stadium, density-ranked eviction
Punch list §25 item 4.1 complete. Replaces the round-robin hotwords cache with Stadium density-ranked admission/eviction on the kernel side, via the §17.7 reservoir mechanism and a kernel-side word_id -> cell_index map (no DictEntry change, dict_hash untouched). Adds stadium_birth_hera() to close the cell-0 panic hazard, STADIUM_WORD_HEAT_QUANTUM/STADIUM_WORD_COOL_RATE_Q48 Kconfig knobs (flagged untuned), and a stadium_word_forget() FORGET coherence hook to close a recycled-word_id aliasing gap. Verified: all five hotwords_cache_* call sites in dictionary_management.c bypassed under __STARKERNEL__; word dispatch feeds the Stadium at all three vm_core.c physics_execution_heat_increment() sites; hosted make unaffected; all three architectures booted to ok> with matching dict_hash (0x3d4e1daf289da94f) and matching conservation stats (promotions=354 evictions=0, resident_sum=65536 reservoir=0 sum=65536). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
cc6c8c43f3 |
Fix ABORT to actually unwind to QUIT instead of one level
ABORT is documented and tested in this codebase as standard FORTH-79 behavior -- system_words_test.c:63: "Should clear stacks and return to QUIT" -- meaning it should unwind all the way back to the outermost interpreter loop, abandoning whatever's left of the current line/block. The implementation only unwound one level: every place that checked vm->abort_requested cleared it the instant it saw it, so it never survived to propagate past the first nested frame. This surfaced via Artemis's ART-HALT-UNRECOG (capsules/artemis/init.4th): on an unrecognized disk it correctly printed "ARTEMIS HALT: unrecognized disk content" and called ABORT, but WELCOME (the next line in the same block) ran anyway, and Artemis announced ready to Hermes and joined the fleet normally -- contradicting .claude/ARTEMIS.md's "Refuse to mount... do not overwrite it" requirement. Root cause is general, not Artemis-specific, and present identically in both the hosted and kernel VM cores. Fixed at every level execution can nest through, verified by exhaustively grepping every !vm->error-gated continuation loop and adding the parallel !vm->abort_requested check: - execute_colon_word (src/vm.c, src/starkernel/vm/vm_core.c): stop clearing the flag on return -- every colon-word call is a recursive call to this same function, so leaving it set lets every enclosing frame's own check also unwind. - vm_interpret (src/vm.c, src/starkernel/vm/vm_core.c): stop parsing further words in the current input string once the flag is set. - exec_block_with_retry (src/starkernel/capsule/capsule_loader.c): capsule birth's line-by-line block executor -- stop processing further lines in the current block, but return 0 (not -1), so capsule_exec_payload still loads later blocks in the same capsule payload. Returning -1 here would have silently broken word definitions in blocks that come after the aborting one for reasons unrelated to why it aborted (concretely, Artemis's ART-PING/LOAD-DOE in blocks 4851/4852, which follow the entry block 4133). - THRU and --> (src/word_source/block_words.c): stop processing further blocks/lines in their own loops. - DODOES (src/word_source/defining_words.c): the CREATE...DOES> runtime has its own hand-rolled execution loop, separate from execute_colon_word -- same bug class, same fix. Also guarded the post-loop "if (vm->rsp < base_rsp) vm->rsp = base_rsp" clamp so it doesn't fire on an abort exit -- ABORT's own reset_vm_state() already set rsp; restoring it to base_rsp would have partially undone that. - Both REPL loops (src/repl.c, src/starkernel/repl.c x2 call sites): clear the flag after each line, mirroring the existing vm->error pattern, so a mid-line abort doesn't silently freeze subsequent interactive input. Verified directly: ": AB-TEST 1 2 3 ABORT 999 . ; AB-TEST 42 . CR 777 . CR" -- 999 never prints (stops mid-colon-word), 42 never prints (stops the rest of the same line), 777 prints fine (next line unaffected). Artemis: WELCOME/"Artemis ready" no longer fires after the halt message. No regression: all three architectures still show PASS: persist-read, PASS: E2E msg flow, and matching dict_hash on the normal (non-aborted) boot path; hosted test suite 965 passed / 0 failed. Known follow-up, not fixed here (see memory for details): Artemis still announces ready to Hermes via a separate call path (CD-INIT, block 4141) that never went through capsule_exec_payload's block chain in the first place, and the disk file still picks up incidental writes even on a correctly-halted boot -- likely generic block-subsystem housekeeping, not traced yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
a5ed8c3d87 | Initial commit — LithosAnanke kernel |