Files
LithosAnanke/kernel/src/capsule/capsule_run.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

289 lines
8.2 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.
*/
/**
* capsule_run.c - DoE Run Logging and Parity (M7.1)
*
* Ring buffer for capsule execution logging and parity record emission.
* Freestanding - no libc dependency except for console output hooks.
*/
#include "starkernel/capsule_run.h"
#include "starkernel/capsule.h"
#ifdef __STARKERNEL__
#include "starkernel/console.h" /* parity output goes straight to the console */
#endif
/* Parity output sink.
*
* In the monolithic kernel/PE build, taking the address of a cross-translation-
* unit function (e.g. &console_puts) is resolved through an absolute
* R_X86_64_32S relocation that the PE base-relocator cannot fix up, so a stored
* console function pointer holds a garbage address and faults #GP when called.
* Direct calls to console_puts/console_putc are PC-relative and always valid, so
* the kernel parity path calls the console directly rather than via a pointer.
* The hosted build keeps the redirectable function-pointer hooks. */
#ifdef __STARKERNEL__
#define PARITY_HAVE_SINK 1
#define PARITY_EMIT(s) console_puts(s)
#else
#define PARITY_HAVE_SINK (parity_puts != 0)
#define PARITY_EMIT(s) do { if (parity_puts) parity_puts(s); } while (0)
#endif
/*===========================================================================
* Run Log Ring Buffer
*===========================================================================*/
static CapsuleRunRecord run_log[CAPSULE_MAX_RUN_RECORDS];
static uint32_t run_log_head = 0; /* Next write position */
static uint32_t run_log_count = 0; /* Total records logged */
static uint64_t run_id_counter = 0; /* Sequential run ID */
void capsule_run_log_init(void) {
run_log_head = 0;
run_log_count = 0;
run_id_counter = 0;
/* Zero the ring buffer */
for (uint32_t i = 0; i < CAPSULE_MAX_RUN_RECORDS; i++) {
run_log[i].run_id = 0;
run_log[i].vm_id = vm_uuid_none(); /* not {0,0} -- that's Hera's reserved id (item 3.8) */
run_log[i].reserved = 0;
run_log[i].capsule_id = 0;
run_log[i].capsule_hash = 0;
run_log[i].pre_dict_hash = 0;
run_log[i].post_dict_hash = 0;
run_log[i].started_ns = 0;
run_log[i].ended_ns = 0;
run_log[i].result_code = 0;
run_log[i].flags = 0;
}
}
uint64_t capsule_run_log_record(const CapsuleRunRecord *record) {
if (!record) {
return 0;
}
/* Assign run ID */
uint64_t id = ++run_id_counter;
/* Copy record to ring buffer */
CapsuleRunRecord *slot = &run_log[run_log_head];
*slot = *record;
slot->run_id = id;
/* Advance head (circular) */
run_log_head = (run_log_head + 1) % CAPSULE_MAX_RUN_RECORDS;
/* Track total count (saturates at buffer size for wrap detection) */
if (run_log_count < CAPSULE_MAX_RUN_RECORDS) {
run_log_count++;
}
return id;
}
int capsule_run_log_get(uint64_t run_id, CapsuleRunRecord *out) {
if (!out || run_id == 0 || run_id > run_id_counter) {
return -1;
}
/* Search ring buffer for matching run_id */
for (uint32_t i = 0; i < CAPSULE_MAX_RUN_RECORDS; i++) {
if (run_log[i].run_id == run_id) {
*out = run_log[i];
return 0;
}
}
return -1; /* Not found (may have been overwritten) */
}
uint32_t capsule_run_log_count(void) {
return run_log_count;
}
/*===========================================================================
* Parity Logging
*
* These functions emit structured parity records to the console/serial.
* Format is designed for deterministic verification and debugging.
*
* Output goes through a console hook that can be redirected to:
* - Serial port (kernel mode)
* - stdout (hosted mode)
* - Debug port 0x402 (QEMU)
*===========================================================================*/
/* Console output hook - to be set by platform layer */
static void (*parity_putc)(char c) = 0;
static void (*parity_puts)(const char *s) = 0;
void capsule_parity_set_output(void (*putc_fn)(char), void (*puts_fn)(const char *)) {
parity_putc = putc_fn;
parity_puts = puts_fn;
}
/* Helper: output hex value */
static void parity_put_hex64(uint64_t val) {
static const char hex[] = "0123456789abcdef";
char buf[19]; /* "0x" + 16 hex + null */
buf[0] = '0';
buf[1] = 'x';
for (int i = 15; i >= 0; i--) {
buf[2 + (15 - i)] = hex[(val >> (i * 4)) & 0xF];
}
buf[18] = '\0';
PARITY_EMIT(buf);
}
/* item 3.8: VMUuid printed as its two hex64 halves, hyphen-separated --
* reuses parity_put_hex64 rather than a new hex-formatting routine. */
static void parity_put_uuid(VMUuid id) {
parity_put_hex64(id.hi);
PARITY_EMIT("-");
parity_put_hex64(id.lo);
}
static void parity_put_u32(uint32_t val) {
char buf[12];
int i = 11;
buf[i--] = '\0';
if (val == 0) {
buf[i--] = '0';
} else {
while (val > 0 && i >= 0) {
buf[i--] = '0' + (val % 10);
val /= 10;
}
}
PARITY_EMIT(&buf[i + 1]);
}
static void parity_put_u64(uint64_t val) {
char buf[21];
int i = 20;
buf[i--] = '\0';
if (val == 0) {
buf[i--] = '0';
} else {
while (val > 0 && i >= 0) {
buf[i--] = '0' + (val % 10);
val /= 10;
}
}
PARITY_EMIT(&buf[i + 1]);
}
void capsule_parity_log_birth(
VMUuid vm_id,
uint64_t capsule_id,
uint64_t capsule_hash,
uint64_t dict_hash)
{
if (!PARITY_HAVE_SINK) return;
PARITY_EMIT("PARITY:BIRTH vm_id=");
parity_put_uuid(vm_id);
PARITY_EMIT(" capsule_id=");
parity_put_hex64(capsule_id);
PARITY_EMIT(" mode=p capsule_hash=");
parity_put_hex64(capsule_hash);
PARITY_EMIT(" dict_hash=");
parity_put_hex64(dict_hash);
PARITY_EMIT("\n");
}
void capsule_parity_log_birth_failed(
VMUuid vm_id,
uint64_t capsule_id,
CapsuleRunResult error,
uint64_t partial_dict_hash)
{
if (!PARITY_HAVE_SINK) return;
PARITY_EMIT("PARITY:BIRTH_FAILED vm_id=");
parity_put_uuid(vm_id);
PARITY_EMIT(" capsule_id=");
parity_put_hex64(capsule_id);
PARITY_EMIT(" error=");
parity_put_u32((uint32_t)error);
PARITY_EMIT(" partial_dict_hash=");
parity_put_hex64(partial_dict_hash);
PARITY_EMIT("\n");
}
void capsule_parity_log_run(
VMUuid vm_id,
uint64_t run_id,
uint64_t capsule_id,
uint64_t pre_dict_hash,
uint64_t post_dict_hash)
{
if (!PARITY_HAVE_SINK) return;
PARITY_EMIT("PARITY:RUN vm_id=");
parity_put_uuid(vm_id);
PARITY_EMIT(" run_id=");
parity_put_u64(run_id);
PARITY_EMIT(" capsule_id=");
parity_put_hex64(capsule_id);
PARITY_EMIT(" mode=e pre_dict=");
parity_put_hex64(pre_dict_hash);
PARITY_EMIT(" post_dict=");
parity_put_hex64(post_dict_hash);
PARITY_EMIT("\n");
}
/*===========================================================================
* Mama Init Parity
*===========================================================================*/
void capsule_parity_log_mama_init(
uint64_t capsule_id,
uint64_t capsule_hash,
uint64_t dict_hash)
{
if (!PARITY_HAVE_SINK) return;
PARITY_EMIT("PARITY:MAMA_INIT capsule_id=");
parity_put_hex64(capsule_id);
PARITY_EMIT(" mode=m capsule_hash=");
parity_put_hex64(capsule_hash);
PARITY_EMIT(" dict_hash=");
parity_put_hex64(dict_hash);
PARITY_EMIT("\n");
}
void capsule_parity_log_kill(VMUuid vm_id, const char *name)
{
if (!PARITY_HAVE_SINK) return;
PARITY_EMIT("PARITY:KILL vm_id=");
parity_put_uuid(vm_id);
PARITY_EMIT(" name=");
if (name) PARITY_EMIT(name);
PARITY_EMIT("\n");
}