§H.12 steps 5-6: pin Hera/Hermes/Artemis in generic capsule-birth admission

capsule_birth.c's generic admission block now registers a session for
every born VM (session_register) and pins it (session_set_pinned) when
the birthing capsule is Hera/Hermes/Artemis.

Bug found and fixed via a temporary probe (written, run, captured,
reverted): the fleet-foundation name check first used an exact-match
comparison against "Hermes"/"Artemis", but capsule_name is actually
"hermes:init.4th"/"artemis:init.4th" (the real namespace:filename
convention) -- the check silently never matched, both would-be-pinned
VMs stayed unpinned. Fixed with a new vm_name_prefix_eq_nocase() helper
matching everything before a literal ':'. Probe confirmed pinned=0 before
the fix, pinned=1 after, on all relevant VMs.

Session.parent is hardcoded to vm_uuid_hera() for now (every birth
through this path is Hera-initiated today); step 7 generalizes this to
the actual birthing VM's own id.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64) on the final,
probe-free code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-03 06:26:50 -04:00
co-authored by Claude Opus 5
parent 67793ea4a1
commit 09998af999
15 changed files with 46014 additions and 16 deletions
+53 -10
View File
@@ -34,6 +34,7 @@
#include "starkernel/kmalloc.h"
#include "starkernel/console.h"
#include "starkernel/vm/stadium.h" /* item 4.1a -- stadium_grant_quota() */
#include "starkernel/session.h" /* session_register()/session_set_pinned() -- FABRIC-3.md §H.12 step 5 */
#include "vm.h"
#include "platform_alloc.h"
/* No LOG_LINE_MAX include-order constraint anymore: vm.h's own
@@ -259,6 +260,21 @@ static int vm_name_eq_nocase(const char *a, const char *b) {
return *a == *b;
}
/* Case-insensitive ASCII prefix match up to (and not including) a literal
* ':' in `str`, or the whole of `str` if it has no ':' -- capsule names use
* a "namespace:filename" convention (e.g. "hermes:init.4th"), confirmed
* live via a temporary probe (§H.12 step 6) rather than assumed: bare
* vm_name_eq_nocase(capsule_name, "Hermes") never matched. `prefix` has no
* ':' of its own. */
static int vm_name_prefix_eq_nocase(const char *str, const char *prefix) {
while (*str && *str != ':' && *prefix) {
if (vm_to_lower(*str) != vm_to_lower(*prefix)) return 0;
str++; prefix++;
}
if (*prefix) return 0; /* prefix longer than str's namespace segment */
return (*str == '\0' || *str == ':');
}
int capsule_vm_find_by_name_nocase(const char *name, VMRegistryEntry *out) {
vm_node_t *node;
if (!name || !out) return -1;
@@ -572,22 +588,41 @@ CapsuleRunResult capsule_birth_baby(
/* FABRIC-3.md SS B, VM-COOL: admit this VM as a patron of its own
* quota -- identity 0 (same convention stadium_birth_hera() uses for
* "patron zero"), heat 0 (no reservoir cost), unpinned (unlike Hera --
* there is no unpin primitive, and pinning here would make the
* explicit KILL-time eviction below unreachable without adding one).
* Unpinned means unrelated quota pressure on this VM's own words/
* blocks could naturally evict this cell before KILL ever runs; that
* is tolerated, not a bug -- nothing wires COOL's dispatch body to
* kill anything, so the only visible effect is entry->stadium_
* patron_cell going stale, which the KILL-time eviction below already
* tolerates (stadium_evict() simply refuses if it's already gone).
* "patron zero"), heat 0 (no reservoir cost). Admitted unpinned here
* regardless of which VM this is -- pinning (when it applies) happens
* through session_set_pinned() below, after admission, same
* unpinned-then-pin ordering §H.12 step 4 already established for
* Hera (stadium_admit() has no admission-time-special pin handling,
* just copies the candidate header, so this ordering is safe).
*
* FABRIC-3.md §H.12 step 5: fleet-foundation VMs (Hera/Hermes/Artemis)
* are pinned -- permanent, exempt from COOL, per §H.1's decision.
* Ordinary/user VMs stay unpinned, matching the original comment's own
* reasoning here (unrelated quota pressure can naturally evict this
* cell before an explicit KILL runs; tolerated, not a bug -- nothing
* wires COOL's dispatch body to kill anything, so the only visible
* effect is entry->stadium_patron_cell going stale, which the
* KILL-time eviction below already tolerates). The original comment's
* "there is no unpin primitive" concern no longer applies to Hera
* herself (session_set_pinned() now provides one, §H.12 step 3) but
* still correctly describes why ordinary VMs -- which DO get killed --
* must stay unpinned: nothing here ever un-pins a killed ordinary VM,
* so it must never have been pinned to begin with.
*
* Soft failure, same as stadium_grant_quota() above -- a refused
* admission leaves stadium_patron_cell at STADIUM_CELL_NONE, and
* nothing downstream depends on it succeeding. */
* nothing downstream depends on it succeeding. session_register()/
* session_set_pinned() failures are soft-fail the same way (logged,
* non-fatal) -- same reasoning §H.12 step 4 already established for
* Hera. */
{
StadiumPatronHeader vm_patron;
uint8_t *raw = (uint8_t *)&vm_patron;
size_t i;
int is_fleet_foundation =
vm_name_prefix_eq_nocase(capsule_name, "Hera") ||
vm_name_prefix_eq_nocase(capsule_name, "Hermes") ||
vm_name_prefix_eq_nocase(capsule_name, "Artemis");
for (i = 0; i < sizeof(vm_patron); i++) raw[i] = 0;
vm_patron.identity = 0;
@@ -600,6 +635,14 @@ CapsuleRunResult capsule_birth_baby(
vm_patron.behaviour = (uint8_t)STADIUM_BEHAVIOUR_COOL;
entry->stadium_patron_cell = stadium_admit(vm_id, &vm_patron);
if (entry->stadium_patron_cell != STADIUM_CELL_NONE) {
Session *s = session_register(vm_id, vm_uuid_hera(), capsule_name);
if (s) {
s->stadium_cell = entry->stadium_patron_cell;
if (is_fleet_foundation) session_set_pinned(vm_id, 1);
}
}
}
const uint8_t *payload = capsule_get_payload(cap, arena);