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
+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.
+331
View File
@@ -0,0 +1,331 @@
/*
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.
*/
/**
* bootstrap.c - Kernel VM bootstrap + parity emit
*
* Gated by STARFORTH_ENABLE_VM to avoid touching stable hosted VM builds.
* Initializes the VM using kernel host hooks, collects parity, and prints it.
*/
#ifdef __STARKERNEL__
#include "vm.h"
#include "log.h"
#include "block_subsystem.h"
#include "starkernel/vm/parity.h"
#include "starkernel/vm/bootstrap/sk_vm_bootstrap.h"
#include "starkernel/hal/hal.h"
#include "starkernel/console.h"
#include "starkernel/capsule_birth.h"
#include "starkernel/capsule_run.h"
#include "starkernel/capsule_vm_physics.h"
#include "starkernel/vm/arena.h" /* FABRIC-3.md §XXVIII Stage 1 -- sk_vm_native_stack_alloc() */
#include "vm_internal.h"
#include "platform_time.h"
#include "test_runner/include/test_runner.h"
#include "word_source/include/mama_forth_words.h"
#if SK_PARITY_DEBUG
extern char __text_start[];
extern char __text_end[];
extern char __rodata_start[];
extern char __rodata_end[];
extern char __data_start[];
extern char __data_end[];
extern char __bss_start[];
extern char __bss_end[];
/**
* @brief Print a 64-bit value as "0xNNNNNNNNNNNNNNNN" to the kernel console.
*
* Used by @c sk_bootstrap_debug_log_xt() when @c SK_PARITY_DEBUG is enabled
* to dump raw pointer values during XT validation. Formats directly into a
* 19-byte stack buffer; no libc dependency.
*
* Only compiled when @c SK_PARITY_DEBUG is non-zero.
*
* @param value 64-bit value to format as uppercase-nibble hex.
*/
static void sk_bootstrap_debug_print_hex(uint64_t value) {
char buf[19];
buf[0] = '0';
buf[1] = 'x';
buf[18] = '\0';
for (int i = 0; i < 16; ++i) {
uint8_t nibble = (uint8_t)((value >> ((15 - i) * 4)) & 0xF);
buf[i + 2] = (nibble < 10) ? (char)('0' + nibble) : (char)('a' + nibble - 10);
}
console_puts(buf);
}
/**
* @brief Test whether a 64-bit address is a canonical x86-64 virtual address.
*
* Checks that bits [63:48] are all 0x0000 (user-space range) or all 0xFFFF
* (kernel-space range). A non-canonical address indicates a corrupt function
* pointer and triggers a @c sk_hal_panic() in the calling debug log function.
*
* Only compiled when @c SK_PARITY_DEBUG is non-zero.
*
* @param addr 64-bit address to test.
* @return Non-zero if canonical, 0 if non-canonical.
*/
static int sk_bootstrap_xt_is_canonical(uint64_t addr) {
uint64_t upper = addr >> 48;
return (upper == 0x0000ULL) || (upper == 0xFFFFULL);
}
/**
* @brief Return a short string naming the memory region containing @p addr.
*
* Classifies @p addr into:
* - @c "null" — zero address
* - @c "text" — within the kernel .text section [@c sk_hal_text_start(),
* @c sk_hal_text_end())
* - @c "directmap" — ≥ @c 0xffff800000000000 (kernel direct-map range)
* - @c "unknown" — anything else
*
* Used by @c sk_bootstrap_debug_log_xt() to label XTs for diagnostic output.
* Rodata/data/bss are not checked because XTs should only reside in .text.
* Uses @c sk_hal_text_start() / @c sk_hal_text_end() to avoid GOT-relative
* accesses under @c -fPIC.
*
* Only compiled when @c SK_PARITY_DEBUG is non-zero.
*
* @param addr 64-bit address to classify.
* @return Static string literal naming the region.
*/
static const char *sk_bootstrap_region_name(uint64_t addr) {
if (addr == 0) return "null";
/* Use HAL getters to avoid GOT indirection issues with -fPIC */
uint64_t text_start = sk_hal_text_start();
uint64_t text_end = sk_hal_text_end();
if (addr >= text_start && addr < text_end) {
return "text";
}
/* For rodata/data/bss, we rely on the HAL whitelist check instead,
* since those symbols also have the same GOT issue. XTs should be
* in text section anyway. */
if (addr >= 0xffff800000000000ULL) {
return "directmap";
}
return "unknown";
}
/**
* @brief Log a dictionary entry's XT pointer and panic if it is invalid.
*
* Prints a @c "[SK_BOOTDBG] <label> entry=ADDR xt=ADDR region=NAME" line to
* the kernel console. If the XT address is non-canonical or in the "unknown"
* region, prints an additional error line and calls @c sk_hal_panic() —
* never returns in that case.
*
* Marked @c __attribute__((unused)) because call sites are conditionally
* compiled; the function exists for use during active debug sessions.
* Only compiled when @c SK_PARITY_DEBUG is non-zero.
*
* @param label Short label string (e.g., the word name) for diagnostic output.
* @param entry Dictionary entry whose XT pointer to validate.
*/
static void __attribute__((unused)) sk_bootstrap_debug_log_xt(const char *label, const DictEntry *entry) {
console_puts("[SK_BOOTDBG] ");
console_puts(label ? label : "(word)");
console_puts(" entry=");
sk_bootstrap_debug_print_hex((uint64_t)(uintptr_t)entry);
const void *xt = entry ? (const void *)entry->func : NULL;
console_puts(" xt=");
sk_bootstrap_debug_print_hex((uint64_t)(uintptr_t)xt);
console_puts(" region=");
const char *region = sk_bootstrap_region_name((uint64_t)(uintptr_t)xt);
console_puts(region);
console_println("");
uint64_t addr = (uint64_t)(uintptr_t)xt;
if (!sk_bootstrap_xt_is_canonical(addr) || (region[0] == 'u' && region[1] == 'n')) {
console_puts("[SK_BOOTDBG] invalid XT for ");
console_puts(label ? label : "(word)");
console_puts(" region=");
console_puts(region);
console_println("");
sk_hal_panic("bootstrap XT pointer invalid");
}
}
#endif
#ifndef STARFORTH_ENABLE_VM
#define STARFORTH_ENABLE_VM 0
#endif
#if STARFORTH_ENABLE_VM
static VM sk_mama_vm; /* Mama's VM — persists after bootstrap */
/* 1 MB POST block RAM — mirrors hosted blk_ram in main.c (BSS, no PE file bloat) */
static uint8_t sk_post_blk_ram[BLK_RAM_BLOCKS * BLK_FORTH_SIZE];
/**
* @brief Return a pointer to the Mama VM instance.
*
* The Mama VM (@c sk_mama_vm) is a file-scope static that persists for the
* lifetime of the kernel. This accessor is used by the capsule subsystem and
* other kernel components that need to call back into the VM after bootstrap
* without holding a direct reference to the static variable.
*
* Only compiled when @c STARFORTH_ENABLE_VM is non-zero.
*
* @return Pointer to @c sk_mama_vm cast to @c void*.
*/
void *sk_get_mama_vm(void) {
return (void *)&sk_mama_vm;
}
/**
* @brief Bootstrap the Mama VM, run POST, collect parity, and print it.
*
* This is the top-level M7 entry point called from @c kernel_main() after all
* hardware milestones (M0–M6) are complete. Execution order:
*
* 1. @c sk_hal_init() + @c sf_time_init() — initialise HAL and time shim.
* 2. @c vm_init_with_host() — initialise Mama VM using HAL host services.
* On failure, collects and prints the parity packet and returns -1.
* 3. Capsule subsystem setup: @c capsule_vm_hooks_register(),
* @c capsule_vm_registry_init(), @c capsule_run_log_init().
* 4. @c register_mama_forth_words() — BIRTH, KILL, START, STOP, USE + capsule words.
* 5. @c vm_enable_interpreter() — required for POST @c vm_interpret() calls.
* 6. @c blk_subsys_init() — POST block RAM (mirrors hosted main.c init order).
* 7. @c run_all_tests() — full POST (936+ test cases).
* 8. @c sk_parity_collect() + @c sk_parity_print() — emit M7.1a/M7.1b lines.
* 9. @c sk_hal_freeze_exec_range() — lock the XT whitelist.
*
* The PARITY:OK / PARITY:FAIL verdict and the POST: PASSED / FAILED lines are
* the normative acceptance signals visible in the QEMU serial log.
*
* Only compiled when @c STARFORTH_ENABLE_VM is non-zero.
*
* @param out Optional caller-supplied @c ParityPacket to receive the parity
* data. If @c NULL, a local @c ParityPacket is used (data is
* printed but not returned to the caller).
* @return 0 if bootstrap and POST succeeded, -1 on any failure.
*/
int sk_vm_bootstrap_parity(ParityPacket *out) {
VM *vm = &sk_mama_vm;
ParityPacket local_pkt;
ParityPacket *pkt = out ? out : &local_pkt;
/* Reset parity packet */
pkt->bootstrap_result = SK_BOOTSTRAP_INIT_FAIL;
/* Initialize host services (allocator/time/console) */
sk_hal_init();
sf_time_init();
vm_init_with_host(vm, sk_hal_host_services());
if (vm->error) {
console_println("VM: init failed");
sk_parity_collect(vm, pkt);
sk_parity_print(pkt);
return -1;
}
/* FABRIC-3.md §XXVIII, Stage 1 (2026-09-13): Hera gets her own native
* stack too, same as every baby/WIREBIND VM (capsule_birth_baby()).
* Not fatal on failure, same reasoning as there -- nothing executes on
* this yet (Stage 2). */
{
sk_vm_native_stack_t ns;
if (sk_vm_native_stack_alloc(&ns) == 0) {
vm->native_stack_paddr = ns.paddr;
vm->native_stack_guard_vaddr = ns.guard_vaddr;
vm->native_stack_top = ns.stack_top;
} else {
console_println("VM: Hera native stack allocation failed");
}
}
/* Capsule subsystem: hooks, registry (Hera), run log.
* Called exactly once here for Mama — child VMs go through
* capsule_birth_baby() which never calls vm_init_with_host(). */
capsule_vm_hooks_register();
capsule_vm_registry_init(vm); /* establishes [Hera] console prefix */
vm->stadium_vm_id = vm_uuid_hera(); /* item 4.2 */
vm_physics_init(vm_uuid_hera()); /* Hera: the fleet's root, seeded Q48_ONE */
capsule_run_log_init();
register_mama_forth_words(vm); /* BIRTH KILL START STOP USE + capsule words */
#if SK_PARITY_DEBUG
log_set_level(LOG_DEBUG);
#else
/* Enable LOG_TEST level to see test output and count statistics */
log_set_level(LOG_TEST);
#endif
/* Enable interpreter - required for POST to execute vm_interpret() calls */
vm_enable_interpreter(vm);
/* Initialize block subsystem for POST — mirrors hosted main.c init order.
* Without this, all block word tests fail with vm->error=1 (blk_is_valid→0). */
blk_subsys_init(vm, sk_post_blk_ram, sizeof(sk_post_blk_ram));
/* Run full POST (Power-On Self Test) */
console_println("POST: Running comprehensive test suite...");
run_all_tests(vm);
/* Collect and print parity (includes test results) */
sk_parity_collect(vm, pkt);
sk_parity_print(pkt);
/* Verify POST success: parity OK, no test failures, no test errors */
if (pkt->bootstrap_result == SK_BOOTSTRAP_OK &&
pkt->tests_failed == 0 &&
pkt->tests_errors == 0) {
console_println("POST: PASSED");
} else {
console_println("POST: FAILED");
}
sk_hal_freeze_exec_range();
return (pkt->bootstrap_result == SK_BOOTSTRAP_OK) ? 0 : -1;
}
#endif /* STARFORTH_ENABLE_VM */
#endif /* __STARKERNEL__ */