/* StarForth — Steady-State Virtual Machine Runtime Copyright (c) 2023–2025 Robert A. James All rights reserved. Licensed under the Starship License, Version 1.0 (SL-1.0) */ #ifndef __STARKERNEL__ #error "capsule_console.c is kernel-only" #endif #include "starkernel/capsule_console.h" #include "starkernel/capsule.h" #include "starkernel/capsule_birth.h" #include "starkernel/xxhash64.h" #include "starkernel/kmalloc.h" #include /* FABRIC-3.6.md Phase 4 (Stage E), 2026-09-22: used to load * common:messaging.4th + MSG-CD-INIT here -- "just enough vocabulary * to send/receive messages" -- but a console-proxy VM's own traffic * (CONSOLE-CMD-EVENT) has gone through kernel-Hermes directly * (sk_hermes_send_one(), repl.c) since task 3.9, with no dependency on * this VM's own dictionary containing any messaging vocabulary at all. * common:messaging.4th no longer exists as of this strip, so this * needed to come out regardless. Genuinely empty now, past the * mandatory "Block NNNN" header capsule_exec_payload() requires * (§F.18) -- confirmed live that an empty body is a safe no-op when * interpreted (task 3.8's own finding: vm_interpret() on an empty * string is a no-op, no error, no UNKNOWN WORD). 4997 is an unused * block number. */ static const char CONSOLE_IDENTITY_SRC[] = "Block 4997\n"; CapsuleRunResult capsule_console_birth(const char *console_name, VMUuid parent, VMUuid *out_vm_id, void **out_vm_ctx) { if (!console_name) return CAPSULE_RUN_ERR_INVALID; size_t source_len = sizeof(CONSOLE_IDENTITY_SRC) - 1u; uint8_t *arena = (uint8_t *)kmalloc(source_len); if (!arena) return CAPSULE_RUN_ERR_STILLBORN; memcpy(arena, CONSOLE_IDENTITY_SRC, source_len); /* Heap-built single-entry directory -- same shape RUNCAP already * established (§F.6/§F.18); never freed, matches that precedent * (a VM's IDENTITY exec reads directly from this arena). */ CapsuleNameEntry name_entry; memset(name_entry.name, 0, sizeof(name_entry.name)); { size_t n = strlen(console_name); if (n >= CAPSULE_NAME_MAX) n = CAPSULE_NAME_MAX - 1u; memcpy(name_entry.name, console_name, n); } CapsuleDesc desc; memset(&desc, 0, sizeof(desc)); desc.magic = CAPSULE_MAGIC_PACK(CAPSULE_VERSION_0, CAPSULE_HASH_XXHASH64); desc.content_hash = xxhash64_capsule(arena, source_len); desc.capsule_id = desc.content_hash; desc.offset = 0; desc.length = source_len; desc.flags = CAPSULE_FLAG_ACTIVE | CAPSULE_FLAG_PRODUCTION; desc.owner_vm = 0; desc.birth_count = 0; desc.created_ns = 0; CapsuleDirHeader dir; memset(&dir, 0, sizeof(dir)); dir.magic = CAPSULE_DIR_MAGIC; dir.arena_base = (uint64_t)(uintptr_t)arena; dir.arena_size = source_len; dir.desc_count = 1; dir.desc_capacity = 1; dir.name_count = 1; dir.dir_hash = 0; CapsuleRunResult r = capsule_birth_baby( console_name, &dir, &desc, &name_entry, arena, parent, 1 /* skip_pki_sig -- not build-time content, same rationale as RUNCAP */, out_vm_id, out_vm_ctx); /* capsule_birth_baby() never sets the registry entry's own .name -- * found live in RUNCAP (§F.18), same fix needed here. */ if (r == CAPSULE_RUN_OK && out_vm_id) { capsule_vm_registry_set_name(*out_vm_id, console_name); } return r; }