Reorganize source tree: kernel/, v3/, v4/ split and board infrastructure

Source tree reorganization:
- Move StarForth v3 engine to v3/ (src/, include/, Makefile)
- Move kernel to kernel/ (src/, include/, linker/, Makefile)
- Create v4/ skeleton for F18-ISA golden model (DECOMPOSITION.md, JUSTIFICATION.md)
- Move FABRIC-0..4.md to docs/fabric/
- Move ONTOLOGY.md and ROADMAP.md to docs/

Board infrastructure:
- Add boards/ser5/, boards/raspi/, boards/milkv/, boards/zynq7020/
- Each board has board.mk (ISA, CPU flags, boot recipe) and README.md
- Root Makefile becomes thin dispatcher: boot_image, all, clean, docs take TARGET
- make boot_image TARGET=SER5|RASPI|MILKV builds one GPT/MBR image per board
- ZYNQ7020 target exists but stops with clear error (ARMv7 port not built yet)
- scripts/mkdiskimage.sh builds disk images for all boards

Docs pipeline:
- docs/book/ with LaTeX master (main.tex) and Makefile
- pandoc converts Markdown to LaTeX at build time
- Two Lua filters: table-widths.lua (wide tables wrap), code-breaks.lua (inline code breaks)
- make docs builds single PDF (754 pages, 0 missing characters)
- make docs TARGET=<board> adds board appendix
- build/docs/<book|board>/meta.tex stamps git commit into PDF

Bug fixes:
- 42 include paths that only worked by accident now use correct relative paths
- clang-18 hardcode replaced with configurable CC variable (fixed aarch64 build)
- Pi 5: kernel_2712.img linked at 0x80000, .bss zeroed, memory reserved
- Doxyfile, .clang-tidy, README.md, Kconfig paths updated

Verified:
- Hosted v3 build passes 1012 tests, 0 failures
- SER5 image boots in QEMU (OVMF), POST passes, K exact (65536 = Q48_ONE)
- Milk-V image boots in QEMU (OpenSBI + U-Boot + bootefi), POST passes
- make clean TARGET=<board> removes only that board and its ISA objects
- make all builds all boards, hosted v3, and docs in one run

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
rajames
2026-10-01 15:40:09 -04:00
co-authored by Junie
parent 3c709c115b
commit a8b70e88d3
630 changed files with 2646 additions and 2036 deletions
+30
View File
@@ -0,0 +1,30 @@
# src/starkernel/vm/
Kernel-side StarForth VM subsystem.
- `vm_core.c` — kernel port of the core interpreter loop.
- `vm_runtime.c` — kernel VM runtime support.
- `vm_bootstrap.c` — kernel VM bootstrap (distinct from
`bootstrap/sk_vm_bootstrap.c`, which owns the higher-level subsystem
wiring called from `kernel_main.c`).
- `arena.c` — capsule arena allocator (fixed-region allocation for capsule
payload data).
- `parity.c` — birth/execution parity logging (VM ID + capsule hash +
dictionary hash), enabling offline determinism verification.
- `alloc_kernel.c` — the kernel-side implementation of the VM's allocator
interface actually referenced by `Makefile.starkernel`'s build (see
`src/platform/README.md` for a note on a second, unwired copy).
- `q48_stubs.c` — deliberate no-op stubs for `q48_from_double()`/
`q48_to_double()` in the freestanding kernel build, where floating point
is unavailable; always returns zero, safe because shared VM code only
uses these results for logging/diagnostic paths that are inactive in
the kernel build.
- `vm_internal.h` — internal-only declarations shared across this
directory's files.
Subdirectories:
- `bootstrap/sk_vm_bootstrap.c` — owns VM init + capsule loading wiring,
called from `kernel_main.c`; `kernel_main.c` itself only owns hardware
milestones.
- `host/shim.c` — host-service shim bridging the kernel VM to
`src/starkernel/hal/host_services.c`.
+233
View File
@@ -0,0 +1,233 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* platform/alloc_kernel.c - Kernel (bare-metal) memory allocator
*
* FABRIC-3.md §X.4, 2026-09-07: this used to be its own isolated static
* 4MB arena (first-fit free list, no splitting/coalescing) -- sized small
* enough, and fragile enough under concurrent VM churn, that a live
* identity-heap-capacity test found the arena topping out around 6
* concurrent WIREBIND-born VMs, failing *before* true capacity exhaustion
* (fragmentation from concurrent background allocation, no coalescing to
* recover from it). Meanwhile `kmalloc.c` -- the kernel's general heap,
* already initialised at boot (M6, well before any VM is ever born) --
* sits right next to it: reserved from real PMM-tracked physical memory
* (not a fixed compile-time 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 *does* coalesce neighboring
* blocks on every free.
*
* Every VM's word dictionary (`vm_create_word()`, the shared/vendored VM
* core) allocates through this file's `sf_malloc()`/`sf_free()` --
* `platform_alloc.h`'s portable allocator abstraction, kernel-side. This
* file now simply delegates to `kmalloc()`/`kfree()` instead of managing
* its own separate, much smaller arena: same API contract callers already
* depend on, real headroom (whatever the boot-time heap ends up being,
* not a hardcoded 4MB), and real coalescing. No new allocator was
* invented -- `kmalloc.c` already existed, was already boot-tested on all
* three arches, and was simply never wired up as the backing store for
* VM dictionaries specifically.
*
* `sf_alloc_init()` is now a no-op (see its own doc comment below): with
* a *shared* heap serving every kernel subsystem, not an isolated arena
* used only for VM dictionaries, "reset" is no longer a sane operation --
* nothing external ever called it anyway (confirmed: no callers besides
* `sf_malloc()`'s own lazy-init guard, which this file's `sf_malloc()`
* still keeps, now as a readiness check rather than an initializer).
*
* This file's own alignment guarantee (`SF_ALIGN`, 8 bytes) is preserved
* via `kmalloc_aligned()` -- `kmalloc()`'s own default alignment
* (`KMALLOC_MIN_ALIGN`, 16 bytes) already satisfies it, but asking
* explicitly keeps this file's contract self-documenting rather than
* relying on kmalloc.c's own default not changing under it.
*/
#include "platform_alloc.h"
#include "starkernel/kmalloc.h"
#include <stdint.h>
#include <stddef.h>
/* Alignment for all allocations (8 bytes for 64-bit safety) -- unchanged
* from this file's previous arena-based implementation. */
#define SF_ALIGN 8
/* alloc_count/free_count have no kmalloc.c equivalent (it tracks bytes,
* not call counts) -- kept here as simple diagnostic counters layered on
* top of kmalloc's own byte-accurate stats, which sf_alloc_get_stats()
* below reads fresh on every call rather than shadowing them locally. */
static size_t g_alloc_count = 0;
static size_t g_free_count = 0;
/**
* @brief No-op: kept only for API compatibility with `platform_alloc.h`.
*
* The backing store is now the shared kernel heap (`kmalloc.c`), already
* initialised at boot (M6) well before any VM allocation can occur --
* there is nothing left for this function to set up, and "resetting" a
* heap shared by every other kernel subsystem would be actively wrong.
* Kept callable (matches its documented "safe to call at any point"
* contract) so no caller needs to change.
*
* @return 0 always (cannot fail)
*/
int sf_alloc_init(void)
{
return 0;
}
/*
* @brief Allocate memory from the shared kernel heap.
*
* Delegates to `kmalloc_aligned()` (`SF_ALIGN`-byte aligned, matching
* this file's previous guarantee). Returns @c NULL for zero-size
* requests and whenever the shared heap itself returns NULL (not yet
* initialised, or genuinely out of memory) -- both already part of this
* function's documented contract.
*
* @param size Number of bytes to allocate.
* @return Pointer to the allocated block on success, @c NULL on failure.
*/
void* sf_malloc(size_t size)
{
if (size == 0) return (void*)0;
void *ptr = kmalloc_aligned(size, SF_ALIGN);
if (ptr) g_alloc_count++;
return ptr;
}
/*
* @brief Allocate and zero-initialise a contiguous array from the kernel heap.
*
* Unchanged from this file's previous implementation: computes
* @p count × @p size, checks for integer overflow via
* @c total/size != count, then delegates to @c sf_malloc(). The returned
* block is zeroed with a manual byte loop rather than @c memset so that
* the kernel build remains freestanding with no libc dependency.
* Returns @c NULL when either argument is zero, on overflow, or on heap
* exhaustion.
*
* @param count Number of elements to allocate.
* @param size Size of each element in bytes.
* @return Pointer to the zeroed block on success, @c NULL on failure.
*/
void* sf_calloc(size_t count, size_t size)
{
if (count == 0 || size == 0) return (void*)0;
size_t total = count * size;
/* Check for overflow */
if (size != 0 && total / size != count)
{
return (void*)0;
}
void* ptr = sf_malloc(total);
if (ptr)
{
/* Zero-initialize */
uint8_t* p = (uint8_t*)ptr;
for (size_t i = 0; i < total; i++)
{
p[i] = 0;
}
}
return ptr;
}
/*
* @brief Resize an allocation in the kernel heap.
*
* Unchanged from this file's previous implementation: `kmalloc.c` has no
* realloc-equivalent, so a fresh block of @p new_size bytes is allocated
* and returned; the original block at @p ptr is left for the caller to
* free explicitly (this function does not free it, matching this file's
* prior documented behavior exactly -- not a regression introduced by
* the kmalloc.c switch).
*
* Special cases match the C standard:
* - @p ptr == @c NULL → equivalent to @c sf_malloc(@p new_size).
* - @p new_size == 0 → returns @c NULL (caller treats old block as freed).
*
* The caller is responsible for copying content from the old block
* before discarding the old pointer; this function does not perform the
* copy.
*
* @param ptr Pointer to the existing allocation (may be @c NULL).
* @param new_size Desired size of the new block in bytes.
* @return Pointer to the new block on success, @c NULL on failure or when
* @p new_size is zero.
*/
void* sf_realloc(void* ptr, size_t new_size)
{
if (!ptr) return sf_malloc(new_size);
if (new_size == 0) return (void*)0;
return sf_malloc(new_size);
}
/*
* @brief Release a kernel heap allocation, making it available for reuse.
*
* Delegates to @c kfree(), which coalesces the freed block with any free
* neighbors -- real reclamation, unlike this file's previous first-fit-
* no-splitting free list (FABRIC-3.md §X.4: that lack of coalescing was
* the proximate cause of a live-observed allocation failure under
* concurrent VM churn even with more than enough free bytes overall).
*
* @param ptr Pointer previously returned by @c sf_malloc() / @c sf_calloc()
* (may be @c NULL; silently ignored, matching @c kfree()'s own
* contract). Must not be used again by the caller after this
* call, and must not be freed twice.
*/
void sf_free(void* ptr)
{
if (!ptr) return;
kfree(ptr);
g_free_count++;
}
/*
* @brief Retrieve a snapshot of kernel allocator statistics.
*
* Reads fresh from `kmalloc_get_stats()` on every call -- the shared heap
* is the real source of truth, not a value shadowed locally. alloc_count/
* free_count (which kmalloc.c does not track) come from this file's own
* counters instead.
*
* @param stats Output buffer to receive the statistics; silently returns
* without writing if @p stats is @c NULL.
*/
void sf_alloc_get_stats(sf_alloc_stats_t* stats)
{
if (!stats) return;
kmalloc_stats_t k = kmalloc_get_stats();
stats->total_bytes = (size_t)k.total_bytes;
stats->used_bytes = (size_t)k.used_bytes;
stats->peak_bytes = (size_t)k.peak_bytes;
stats->alloc_count = g_alloc_count;
stats->free_count = g_free_count;
}
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
# src/starkernel/vm/bootstrap/
- `sk_vm_bootstrap.c` — owns kernel VM initialization and capsule-loading
wiring, called from `kernel_main.c`. Kept deliberately separate from
`kernel_main.c`, which owns only hardware milestones.
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
# src/starkernel/vm/host/
- `shim.c` — host-service shim bridging the kernel VM subsystem to
`src/starkernel/hal/host_services.c`, narrowing the interface the VM
core needs from the underlying hardware/firmware layer.
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
+45
View File
@@ -0,0 +1,45 @@
/*
* StarKernel Q48.16 fixed-point stubs
*
* Minimal implementations for kernel build - floating point
* conversion is not needed for core VM operation.
*/
#include "q48_16.h"
/*
* @brief Convert a @c double to Q48.16 fixed-point (kernel stub — always 0).
*
* Floating-point arithmetic is unavailable in the freestanding kernel build,
* so this function is a deliberate no-op stub. It discards @p d and returns
* a zero-valued @c q48_16_t. Shared VM code that calls @c q48_from_double()
* only uses it for logging and diagnostic output (e.g., @c WORD-ENTROPY),
* which are suppressed in the kernel build, so the zero return is safe.
*
* @param d Floating-point value to convert (ignored).
* @return Zero-valued @c q48_16_t.
*/
q48_16_t q48_from_double(double d)
{
(void)d;
q48_16_t zero = {0};
return zero;
}
/*
* @brief Convert a Q48.16 fixed-point value to @c double (kernel stub — always 0.0).
*
* Floating-point arithmetic is unavailable in the freestanding kernel build,
* so this function is a deliberate no-op stub. It discards @p q and returns
* @c 0.0. Shared VM code that calls @c q48_to_double() only uses the result
* for logging and RStudio/CSV export paths that are inactive in the kernel
* build, so the zero return is safe.
*
* @param q Q48.16 value to convert (ignored).
* @return @c 0.0 always.
*/
double q48_to_double(q48_16_t q)
{
(void)q;
return 0.0;
}
+157
View File
@@ -0,0 +1,157 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* session.c - Per-VM session bookkeeping (FABRIC-2.md §H.12 steps 2-3)
*
* Slot table sized from stadium_max_vm_count() at session_boot_init() time,
* mirroring stadium.c's own StadiumVMQuota table (kmalloc'd to a
* budget-computed count, not a fixed compile-time array -- population isn't
* knowable in advance, same reasoning stadium.c's own history already
* settled). No callers yet; wiring into the boot sequence and into
* stadium_birth_hera()/capsule_birth.c's admission path are later
* §H.12 steps.
*
* session_set_pinned()/session_is_pinned() (step 3) are the pin-authority
* choke point -- see their own doc comments in session.h for the
* authoritative-session-mirrored-Stadium-bit split.
*/
#ifdef __STARKERNEL__
#include "starkernel/session.h"
#include "starkernel/vm/stadium.h"
#include "starkernel/kmalloc.h"
static Session *g_sessions = (Session *)0;
static size_t g_session_slots = 0; /* capacity, from stadium_max_vm_count() */
static size_t g_session_count = 0; /* slots actually in use, 0..g_session_slots */
int session_boot_init(void)
{
size_t max_vm_count = stadium_max_vm_count();
Session *slots;
size_t i;
if (max_vm_count == 0) return -1; /* Stadium not yet initialized */
slots = (Session *)kmalloc(max_vm_count * sizeof(Session));
if (!slots) return -1;
for (i = 0; i < max_vm_count; i++) {
uint8_t *raw = (uint8_t *)&slots[i];
size_t j;
for (j = 0; j < sizeof(Session); j++) raw[j] = 0;
slots[i].vm_id = vm_uuid_none();
slots[i].parent = vm_uuid_none();
}
g_sessions = slots;
g_session_slots = max_vm_count;
g_session_count = 0;
return 0;
}
Session *session_find(VMUuid vm_id)
{
size_t i;
if (!g_sessions) return (Session *)0;
for (i = 0; i < g_session_slots; i++) {
if (!vm_uuid_is_none(g_sessions[i].vm_id) &&
vm_uuid_equal(g_sessions[i].vm_id, vm_id)) {
return &g_sessions[i];
}
}
return (Session *)0;
}
Session *session_register(VMUuid vm_id, VMUuid parent, const char *name)
{
size_t i;
Session *slot = (Session *)0;
if (!g_sessions) return (Session *)0;
if (session_find(vm_id)) return (Session *)0; /* already registered */
for (i = 0; i < g_session_slots; i++) {
if (vm_uuid_is_none(g_sessions[i].vm_id)) {
slot = &g_sessions[i];
break;
}
}
if (!slot) return (Session *)0; /* table full */
slot->vm_id = vm_id;
slot->parent = parent;
slot->pinned = 0;
slot->stadium_cell = SESSION_STADIUM_CELL_NONE;
{
size_t j = 0;
if (name) {
for (; j < SESSION_NAME_BUF - 1u && name[j]; j++) slot->name[j] = name[j];
}
slot->name[j] = '\0';
}
{
uint8_t *raw = (uint8_t *)&slot->identity;
size_t j;
for (j = 0; j < sizeof(VMIdentity); j++) raw[j] = 0;
}
g_session_count++;
return slot;
}
void session_set_pinned(VMUuid vm_id, int pinned)
{
Session *s = session_find(vm_id);
StadiumCell *cells;
if (!s) return;
s->pinned = pinned ? 1 : 0;
if (s->stadium_cell == SESSION_STADIUM_CELL_NONE) return;
if (s->stadium_cell >= stadium_cell_count()) return;
cells = stadium_cells();
if (!cells) return;
if (pinned) {
cells[s->stadium_cell].header.flags |= STADIUM_FLAG_PIN;
} else {
cells[s->stadium_cell].header.flags &= (uint8_t)~STADIUM_FLAG_PIN;
}
}
int session_is_pinned(VMUuid vm_id)
{
Session *s = session_find(vm_id);
if (!s) return 0;
return s->pinned;
}
#endif /* __STARKERNEL__ */
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
+141
View File
@@ -0,0 +1,141 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* switch.c - Cooperative VM context switch glue (FABRIC-3.md §XXVIII,
* Stage 2, 2026-09-13). See switch.h for the contract.
*/
#ifdef __STARKERNEL__
#include "starkernel/vm/switch.h"
#include "vm.h"
#include "starkernel/capsule_birth.h" /* capsule_vm_set_state */
#include <stdint.h>
/* Arch-specific (switch.S in each arch dir). sk_vm_switch_prime() builds
* the exact synthetic first-entry frame sk_vm_switch_to() expects -- kept
* in assembly, not duplicated here, so the layout can never drift out of
* sync between the two. */
extern void sk_vm_switch_to(uint64_t *save_sp_out, uint64_t new_sp);
extern uint64_t sk_vm_switch_prime(uint64_t stack_top, void *entry);
/* Single-writer-mainline global, safe under this stage's cooperative-only,
* one-VM-runs-at-a-time-on-one-hart discipline (same class of bookkeeping
* as vm_core.c's g_log_attrib_vm) -- identifies which VM a freshly-primed
* native stack belongs to. Read exactly once, synchronously, at the very
* top of sk_vm_switch_entry() before any other first-entry could occur.
* Safe to keep as a plain global (unlike the old g_switch_back_to this
* file used to also carry -- see native_switch_back_to's own doc comment
* in vm.h for why that one had to move to per-VM state): a VM's identity
* never changes across the lifetime of its own trampoline instance, only
* who most recently switched into it does. */
static VM *g_switch_entry_vm;
/* FABRIC-3.md §XXVIII Stage 3 follow-on (2026-09-14): which VM is
* currently running, from the switch mechanism's own point of view --
* independent of vm_log_attributed_vm() (vm_core.c), which is scoped to
* nested vm_interpret() calls for log-source attribution and has no way
* to know a VM is "running" while parked inside this file's own raw C
* trampoline (sk_vm_switch_entry()), never itself calling vm_interpret().
* sk_vm_switch_signal_tick() (capsule_vm_switch_signal.c) consults this
* instead of vm_log_attributed_vm() for exactly that reason -- see its
* own updated comment. Single-writer-mainline, same discipline as
* g_switch_entry_vm above: set unconditionally on every switch, by the
* only code path that can ever change who is physically running.
* Seeded once at boot via sk_vm_switch_set_current() for whichever VM is
* running before any switch has ever happened (Hera, at Stage 3
* registration time -- kernel_main.c). */
static VM *g_switch_current_vm;
VM *sk_vm_switch_current_vm(void) { return g_switch_current_vm; }
void sk_vm_switch_set_current(VM *vm) { g_switch_current_vm = vm; }
/**
* sk_vm_switch_entry - trampoline for a VM's first-ever entry.
*
* A freshly-entered VM has no production behavior defined yet -- what a
* switched-to VM should actually DO once running is later-stage work
* (message dispatch, an idle loop, whatever Stage 3+ needs). For now this
* is a minimal, permanent placeholder: immediately yield back to whoever
* most recently switched to it, forever. Reads self->native_switch_back_to
* fresh every iteration (not a value captured once) so a later switch-in
* from a different VM is honored, not silently bounced to whoever entered
* first. Must never fall off the end -- there is no legitimate return
* address below a synthetic first-entry frame, unlike an ordinary
* function.
*
* FABRIC-3.md §XXVIII Stage 3 follow-on (2026-09-14): an attempt to make
* this attributable by calling vm_interpret(self, "MSG-TICK") here was
* tried and reverted -- it introduced a second, distinct hang (a stall in
* sk_repl_headless_wait()'s WIREBIND-attachment wait, not root-caused) in
* place of the switch-storm it was meant to fix. The switch-storm's real
* cause (sk_vm_switch_signal_tick()'s "who is current" check via
* vm_log_attributed_vm() never seeing a VM that only ever runs this raw
* C trampoline) is instead fixed at the source in switch.c/
* capsule_vm_switch_signal.c: sk_vm_context_switch() now tracks "current"
* itself, independent of vm_interpret()'s own attribution bookkeeping.
* This placeholder's behavior is therefore unchanged from Stage 2.
*/
void sk_vm_switch_entry(void) {
VM *self = g_switch_entry_vm;
for (;;) {
sk_vm_context_switch(self, self->native_switch_back_to);
}
}
int sk_vm_context_switch(VM *from, VM *to) {
uint64_t new_sp;
if (!from || !to || to->native_stack_top == 0) {
return -1;
}
/* Recorded unconditionally (first-ever entry or a resume) so whoever
* is parked at `to`'s native stack -- most often this placeholder
* trampoline -- always knows who most recently switched into it. */
to->native_switch_back_to = from;
if (to->native_stack_saved_sp == 0) {
/* First-ever entry: synthesize the initial frame. */
g_switch_entry_vm = to;
new_sp = sk_vm_switch_prime(to->native_stack_top, (void *)&sk_vm_switch_entry);
} else {
new_sp = to->native_stack_saved_sp;
to->native_stack_saved_sp = 0; /* about to be running, not parked */
}
capsule_vm_set_state(to->stadium_vm_id, VM_STATE_LIVE);
capsule_vm_set_state(from->stadium_vm_id, VM_STATE_SWITCHED_OUT);
g_switch_current_vm = to;
sk_vm_switch_to(&from->native_stack_saved_sp, new_sp);
/* Resumes here only once something later switches back to `from` --
* from `from`'s own point of view, this call simply took a while. */
capsule_vm_set_state(from->stadium_vm_id, VM_STATE_LIVE);
g_switch_current_vm = from;
return 0;
}
#endif /* __STARKERNEL__ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef VM_INTERNAL_H
#define VM_INTERNAL_H
#include "../../../v3/include/vm.h"
#include "../../../v3/include/vm_host.h"
#ifndef PARITY_MODE
#define PARITY_MODE 0
#endif
#ifndef SK_PARITY_DEBUG
#define SK_PARITY_DEBUG 0
#endif
/* HISTORICAL: this condition also excluded L4RE_TARGET (`&&
* !defined(L4RE_TARGET)`) -- L4Re had no pthreads. L4Re support has been
* removed as an active target (it was hosted-only anyway; this kernel
* file never actually saw L4RE_TARGET defined). */
#if HEARTBEAT_THREAD_ENABLED
#include <pthread.h>
#define HEARTBEAT_HAS_THREADS 1
#else
#define HEARTBEAT_HAS_THREADS 0
#endif
#define HEARTBEAT_DECAY_BATCH 64u
typedef struct HeartbeatWorker {
#if HEARTBEAT_HAS_THREADS
pthread_t thread;
#endif
uint64_t tick_ns;
int running;
int stop_requested;
} HeartbeatWorker;
const VMHostServices* vm_default_host_services(void);
const VMHostServices* vm_host(const VM *vm);
void* vm_host_alloc(VM *vm, size_t size, size_t align);
void* vm_host_calloc(VM *vm, size_t n, size_t size);
void vm_host_free(VM *vm, void *ptr);
#ifndef __STARKERNEL__
void vm_reset_hosted_fake_ns(VM *vm);
#endif
uint64_t vm_monotonic_ns(VM *vm);
unsigned vm_get_base(const VM* vm);
void vm_set_base(VM* vm, unsigned b);
uint32_t heartbeat_snapshot_index_load(const volatile uint32_t *ptr);
void heartbeat_snapshot_index_store(volatile uint32_t *ptr, uint32_t value);
void heartbeat_publish_snapshot(VM *vm);
void vm_heartbeat_run_cycle(VM *vm);
#if HEARTBEAT_HAS_THREADS
void* heartbeat_thread_main(void *arg);
#endif
#endif /* VM_INTERNAL_H */
File diff suppressed because it is too large Load Diff