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
@@ -0,0 +1,184 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
/**
* capsule_wirebind.h - WIREBIND: the real thumbdrive-attach call site
* (FABRIC-2.md §F.5/§F.23). Assembles pieces already built and
* individually verified this session -- CERTVERIFY (vm_identity.h's
* vm_identity_from_cert()), RUNCAP (capsule_runcap.h), the console-VM +
* user-VM pair (capsule_console.h, sk_repl_dispatch_line() in repl.c) --
* into one automatic sequence, replacing the RUNCAP-TEST/PAIR-TEST
* diagnostic words that exercised each piece by hand.
*/
#ifndef STARKERNEL_CAPSULE_WIREBIND_H
#define STARKERNEL_CAPSULE_WIREBIND_H
#ifdef __STARKERNEL__
#include "starkernel/homeblocks_sig.h"
#include "starkernel/vm_identity.h"
#include "vm.h"
struct blkio_dev;
/**
* capsule_wirebind_verify_cert - Read the cert region off dev and verify
* it against mama_vm's own Zuse identity. Shared by both
* capsule_wirebind_try_attach() (the original attach) and BINDSTEP
* (mama_word_use(), mama_forth_words.c -- re-verifies live on every USE
* of an identity-locked VM, per FABRIC-2.md §F.9 decision 1) so both
* call sites check the exact same thing the exact same way.
*
* No-op-and-fail (-1) if sig->cert_offset is 0 (no cert region -- a
* genesis-mode Zuse drive, or simply not a regular identity drive) or
* mama_vm has no installed Zuse cert yet.
*
* @param dev Already-open block device to read the cert from.
* @param sig Its already-checked homeblocks_sig_t.
* @param mama_vm Hera's own VM -- the trust root (zuse_cert_pubkey).
* @param out Filled with the verified identity on success.
* @return 0 on success, -1 on any failure (read, verify, or precondition).
*/
int capsule_wirebind_verify_cert(struct blkio_dev *dev,
const homeblocks_sig_t *sig,
VM *mama_vm, VMIdentity *out);
/**
* capsule_wirebind_try_attach - Try to verify and bind a just-attached
* regular (non-Zuse) identity drive.
*
* No-op if sig->cert_offset is 0 (a genesis-mode Zuse drive has no cert
* region -- that's capsule_zuse_boot_try_attach()'s own job, not this
* one's) or if mama_vm has no installed Zuse cert yet (nothing to verify
* the attached cert against). Otherwise: reads the cert devblock(s),
* calls vm_identity_from_cert() against mama_vm's own zuse_cert_pubkey
* and sig->drive_uuid. On success, reads the drive's own
* user_identity_seed_t for its username and births a console VM +
* RUNCAP-born user VM pair (idempotent -- no-ops if that username is
* already live this session), installs the verified VMIdentity onto the
* user VM, and registers the "<username>~user" pairing
* (sk_repl_dispatch_line(), repl.c, looks for this). Does NOT USE the
* new console automatically -- that stays an explicit, later,
* ACL-gated step (BINDSTEP, §F.9), not something a bare attach should
* trigger silently.
*
* @param dev The just-attached, already-open block device.
* @param sig Its already-checked homeblocks_sig_t.
* @param mama_vm Hera's own VM (the verifier -- her zuse_cert_pubkey is
* the trust root regular user certs are checked against).
*/
void capsule_wirebind_try_attach(struct blkio_dev *dev,
const homeblocks_sig_t *sig,
VM *mama_vm);
/**
* capsule_wirebind_eject - Graceful detach of whatever VM is currently
* attached via the home-blocks USB path (FABRIC-2.md §F.10, decision 1).
* The drive is still physically present when this runs.
*
* Sequence: resolve the tracked attached-VM id to a live registry entry
* (no-op, returns -1, if nothing is tracked or the entry is already
* dead/gone -- capsule_vm_kill()'s own idempotency covers a VM already
* killed by some other path); blk_vm_flush_all() while the VM is still
* alive; if the console's active VM is this same VM, reset it to Hera
* (sk_repl_set_active_vm(NULL)) *before* teardown -- required, not
* optional, to avoid a dangling console pointer; capsule_vm_kill() by
* name; clear the tracked state.
*
* EJECT is inherently about "whichever identity is currently paired to
* the one physical console" -- targets that singleton, no name argument.
* (Corrected 2026-09-14: this used to claim a "single-USB-device
* constraint (§F.8)" meant there was never more than one candidate --
* stale even at the time this correction was written; §XV/§XVI
* (2026-09-11/12) proved 9 identities genuinely simultaneously live via
* this same attach path. EJECT staying console-singleton-scoped is a
* deliberate UX choice now, not a hardware constraint -- see
* capsule_wirebind_unclean_detach()'s own doc below for the function
* that DOES need to reach every live identity, not just this one.)
*
* @return 0 on success, -1 if nothing was attached to eject.
*/
int capsule_wirebind_eject(void);
/**
* capsule_wirebind_unclean_detach - Abrupt-path counterpart to
* capsule_wirebind_eject() (FABRIC-2.md §F.10, decision 2 -- the UNCLEAN
* node, closed alongside EJECT). Called from the existing
* bot_msc_detach_pending hot-unplug signal (repl.c) -- the device is
* already gone by the time this runs, so no flush is attempted; data
* since the last flush is lost, which is correct unclean-removal
* semantics.
*
* FABRIC-3.md §VII follow-on, 2026-09-06: requires the departing device
* to actually be one WIREBIND is tracking -- a real bug otherwise, found
* live once genuine multi-device attach made a *different* device's
* detach reachable while a WIREBIND user's own stayed attached.
*
* FABRIC-3.md §XXVIII Stage 4, 2026-09-14: resolves the departing device
* against a per-device live-identity table now, not the single
* console-pairing global capsule_wirebind_eject() uses -- with several
* identities simultaneously live (§XV/§XVI), any one of them can be the
* device that just disappeared, not only the most recently attached.
* Refuses (rather than freeing) a VM currently VM_STATE_SWITCHED_OUT --
* see capsule_vm_kill()'s own guard -- leaving it tracked for the Stage 3
* switcher to reap on its own next resume attempt instead.
*
* @param dev The device that just detached; every other value is a no-op.
*/
void capsule_wirebind_unclean_detach(struct blkio_dev *dev);
/**
* capsule_wirebind_attached_username - The plain username (no "~user"
* registry-name suffix) of whichever identity is currently tracked as
* attached, or NULL if none is (FABRIC-2.md §I.1/4.4s -- the `(user)`
* console prompt segment reads this). Points into WIREBIND's own
* internal storage; valid only until the next attach/eject/detach, same
* caveat as console_get_vm_name().
*/
const char *capsule_wirebind_attached_username(void);
/**
* capsule_wirebind_overflow_idle_check - FABRIC-2.md §I.2's own "overflow
* trigger," decided and built 2026-09-05. Called once per idle tick
* (sk_repl_idle(), repl.c, alongside blk_migration_idle_check() -- same
* ~1 Hz cadence), same as that function's own convention.
*
* No-op if nothing is attached via WIREBIND. Otherwise reads the attached
* drive's own free/total via blk_get_device_free_blocks() (real numbers:
* a WIREBIND-attached drive is always HOMEBLOCKS_SIG_OK, i.e. already
* STFR/v2-formatted, by the time blk_subsys_attach_device() runs on the
* same dev pointer right after WIREBIND itself -- not PROVISIONAL, not
* raw). If free space is below a fixed threshold AND the attached
* identity does not already own a claim (blk_owner_has_claim() -- a disk
* scan, not a RAM flag, so this decision survives reboot/reattach for
* free), claims a fixed number of additional devblocks on Artemis's own
* device via blk_firsttouch_claim() -- a one-time-per-identity extension,
* not a growth loop, deliberately: this does not free space on the
* user's own drive, it only extends their pool onto system-resident
* space, so re-claiming every tick once already extended would walk
* Artemis's device to exhaustion for no benefit.
*/
void capsule_wirebind_overflow_idle_check(void);
/**
* capsule_wirebind_reap_idle_check - FABRIC-3.md §XXVIII Stage 4
* (2026-09-14): sweeps the per-device live-identity table for entries
* whose VM has been reaped (capsule_vm_force_reap(), called from the
* Stage 3 checkpoint on a pending_reap target -- see that function's own
* doc comment) and removes the now-stale table entry. Does NOT itself
* free anything or call capsule_vm_kill()/force_reap() -- purely
* bookkeeping cleanup after the fact. Called at the same ~1 Hz idle
* cadence as capsule_wirebind_overflow_idle_check().
*/
void capsule_wirebind_reap_idle_check(void);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_CAPSULE_WIREBIND_H */