Stage 3 follow-on: fix stack-ownership corruption + DoE switch columns (FABRIC-3.md §XXVIII.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

DoE CSV gained 6 switch-signal columns (switch_count_cumulative,
switch_current_slot, switch_*_readiness, switch_ticks_since), and verifying
them with a boot-time HB-ON probe surfaced a real livelock: the preemption
checkpoint could fire inside a VM-EXEC-nested execute_colon_word() call and
switch away from a stack it didn't own, parking a borrowed region of the
caller's stack under the wrong VM's saved-context pointer. The trampoline
bounce was the visible (safe) half of this; the corruption was the quiet
half, live in every prior "clean" Stage 3 boot without ever showing up in
the log.

Fixed by gating the checkpoint on being at the outermost vm_interpret()
call (g_vm_interpret_depth / sk_vm_at_outermost_interpret(), vm_core.c),
per Bob's decision. Also fixed two related bugs found in the same pass:
g_switch_back_to was a single global stale after first entry, now per-VM
state (native_switch_back_to); note_switch_performed() fired on resume
instead of switch-out, now called before the switch.

Verified on all 3 architectures: steady log growth (no freeze), zero
leaked QEMU processes, DoE columns internally consistent, Hermes/Artemis
confirmed genuinely executing (not just trampoline-bouncing). Temporary
HB-ON boot probe reverted after capture.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S
This commit is contained in:
Robert Allan James
2026-09-13 23:58:59 -04:00
co-authored by Claude Sonnet 5
parent 986d042aa7
commit 862d7d9c48
19 changed files with 79695 additions and 19 deletions
@@ -56,6 +56,11 @@ static int g_slot_count;
static int g_pending;
static VMUuid g_pending_target;
/* DoE CSV exposure (FABRIC-3.md §XXVIII Stage 3 follow-on, 2026-09-13). */
static int g_current_slot_cached = -1;
static uint64_t g_switch_count;
static uint32_t g_ticks_since_switch;
static int slot_for_vm_id(VMUuid vm_id) {
int i;
for (i = 0; i < g_slot_count; i++) {
@@ -81,6 +86,9 @@ void sk_vm_switch_signal_tick(void) {
current_slot = slot_for_vm_id(current->stadium_vm_id);
if (current_slot < 0) return; /* current VM isn't a registered participant */
g_current_slot_cached = current_slot;
g_ticks_since_switch++;
for (i = 0; i < g_slot_count; i++) {
if (i == current_slot) {
g_slots[i].readiness = 0;
@@ -106,4 +114,36 @@ VMUuid sk_vm_switch_signal_take_pending(void) {
return g_pending_target;
}
void sk_vm_switch_signal_note_switch_performed(void) {
g_switch_count++;
g_ticks_since_switch = 0;
}
uint64_t sk_vm_switch_signal_switch_count(void) {
return g_switch_count;
}
uint32_t sk_vm_switch_signal_ticks_since_switch(void) {
return g_ticks_since_switch;
}
int sk_vm_switch_signal_current_slot(void) {
return g_current_slot_cached;
}
int sk_vm_switch_signal_slot_count(void) {
return g_slot_count;
}
uint32_t sk_vm_switch_signal_readiness(int slot) {
if (slot < 0 || slot >= g_slot_count) return 0;
return g_slots[slot].readiness;
}
uint32_t sk_vm_switch_signal_readiness_of(VMUuid vm_id) {
int slot = slot_for_vm_id(vm_id);
if (slot < 0) return 0;
return g_slots[slot].readiness;
}
#endif /* __STARKERNEL__ */