Route VM-dictionary sf_malloc/sf_free through the kernel's kmalloc heap

Captain Bob's call after seeing the identity-heap-capacity findings
(FABRIC-3.md §X.4): the number of concurrently-running VMs is not known
in advance, and once this is a complete operating system the heap should
be able to use whatever memory is actually available -- not a hardcoded
compile-time ceiling. This was already half-built and just not wired up.

src/starkernel/vm/alloc_kernel.c previously implemented sf_malloc()/
sf_free() (platform_alloc.h's allocator abstraction -- what
vm_create_word() calls for every VM's word dictionary) as its own
isolated static 4MB arena: first-fit free list, no splitting or
coalescing. That's exactly the allocator that topped out around 6
concurrent WIREBIND-born identities, failing from fragmentation before
true capacity exhaustion (§X.4's own measurements).

Sitting right next to it, unused for this purpose: src/starkernel/memory/
kmalloc.c, the kernel's general heap. Already initialized at boot (M6,
kernel_main.c, well before any VM is ever born), reserved from real
PMM-tracked physical memory rather than a fixed array, defaults to a
2 GiB floor explicitly sized "for 256+ baby VMs" per its own comment,
overridable via the --heap= boot flag, and its free list actually
coalesces neighboring blocks on every free.

Change: alloc_kernel.c's sf_malloc()/sf_free() now delegate to
kmalloc_aligned()/kfree() instead of managing a separate arena.
sf_alloc_init() becomes a no-op (kmalloc is already initialized by the
time any VM allocation can happen, and "resetting" a heap now shared by
every kernel subsystem would be actively wrong -- confirmed no external
caller depended on its old reset semantics). sf_alloc_get_stats() reads
kmalloc_get_stats() fresh rather than shadowing byte counts locally;
alloc_count/free_count (which kmalloc.c doesn't track) stay as simple
local counters. sf_calloc()/sf_realloc() are otherwise unchanged. Kernel-
only: the hosted (non-kernel) StarForth build keeps its own separate
alloc_host.c implementation, untouched.

Verified live: replaying the exact hotplug sequence that previously
topped out at 6 identities (Zuse + 8 identities, one at a time via QMP
device_add) now succeeds for all 9, where identity 05 specifically used
to fail. Three-arch clean qemu acceptance (single Zuse device, the
standard regression case) passed on amd64, aarch64, and riscv64 -- one
aarch64 attempt hit an unrelated, already-documented one-off QEMU hiccup
(empty log, boot never progressed past firmware) and passed cleanly on
retry with no rebuild.

Not addressed here: the underlying free-list itself is still first-fit
without splitting (only coalescing changed, inherited from kmalloc.c);
per-VM dictionary sizing (shrinking what each WIREBIND VM's word set
actually needs) is a separate, still-open lever from FABRIC-3.md §X.4's
open architecture question.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Ec88YKxxhZGG1RNnune78
This commit is contained in:
Robert Allan James
2026-09-07 12:46:14 -04:00
co-authored by Claude Sonnet 5
parent 36f1d6ae9e
commit 56e19a00f9
6 changed files with 27048 additions and 172 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff