Files
LithosAnanake/kernel/src/vm/bootstrap/sk_vm_bootstrap.c
T
rajamesandJunie a8b70e88d3 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>
2026-10-01 15:40:09 -04:00

332 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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__ */