docs: reorganise -- current reference gathered, finished work archived, superseded and dead ideas marked as such
docs/CLAUDE.md Phase 2, 2026-10-14. All moves by git mv; every archived file carries a header saying what it is, why, and where to look instead. - docs/reference/ (new): roadmap and versioning policy, M7.1, word-ACL design, kernel args, hosted acceptance test, dictionary, primitives, ontology, L8 table -- previously spread over docs/, docs/lithosananke/ and docs/03-architecture/. - docs/working/archive/historical/: finished work (session plan, test-gap audit, birthing plan, fix write-ups, the January architecture draft). - docs/working/archive/superseded/: the Hermes-VM Tripod docs and July Hermes designs (-> FABRIC-3.5/3.6), the StarForth roadmap, every working doc already flagged superseded or obsolete. - docs/working/archive/dead/: the hal_* target contract (never adopted), L4Re, metrics-and-knobs. - Papers and proposals into docs/working/papers/. - References rewritten across the repo (not logs/, not docs/patent/); CLAUDE.md's documentation section rewritten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1a4b163861
commit
7aed588f78
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,195 +0,0 @@
|
||||
# LithosAnanke — StarForth Kernel Branch
|
||||
|
||||
> **⚠ PARTIALLY STALE (flagged 2026-10-09).** This page predates the repo split and the
|
||||
> Tripod/kernel reshuffle. There is no `lithosananke` branch in this repo — `master` is the
|
||||
> sole production line — and the VM fleet is now **Hera / Artemis / Hestia** (the Hermes VM
|
||||
> was removed; messaging moved into the kernel as kernel-Hermes). M7.1 is **live**, not
|
||||
> merely designed. For authoritative status see `.claude/CLAUDE.md` and
|
||||
> `docs/fabric/FABRIC-2.md` / `FABRIC-3.md` / `FABRIC-3.5.md` / `FABRIC-3.6.md`; for the
|
||||
> milestone plan see `ROADMAP.md` (this file is an orientation page).
|
||||
|
||||
**Line:** `master` (sole production line post-split)
|
||||
**Status:** M7 Complete (VM Parity) · M7.1 Live (capsules, Tripod, Stadium, word-level ACL Phases 1–7)
|
||||
|
||||
---
|
||||
|
||||
## What is LithosAnanke?
|
||||
|
||||
LithosAnanke is the kernel branch of StarForth — a UEFI-bootable microkernel with the StarForth VM as its native execution engine.
|
||||
|
||||
**Etymology:** *Lithos* (stone/foundation) + *Ananke* (necessity/inevitability)
|
||||
|
||||
The name reflects the project's nature: a necessary foundation for StarshipOS.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ UEFI Firmware │
|
||||
│ • ExitBootServices → handoff to kernel │
|
||||
└─────────────────────┬───────────────────────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ LithosAnanke Boot (M0) │
|
||||
│ • UEFI loader collects BootInfo │
|
||||
│ • Memory map, ACPI tables, framebuffer │
|
||||
└─────────────────────┬───────────────────────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ LithosAnanke HAL (M1-M6) │
|
||||
│ • PMM: Physical Memory Manager (bitmap) │
|
||||
│ • VMM: Virtual Memory Manager (4-level paging) │
|
||||
│ • IDT: Interrupt Descriptor Table │
|
||||
│ • APIC: Timer + IRQ routing │
|
||||
│ • Heap: kmalloc/kfree │
|
||||
│ • Console: Serial UART │
|
||||
└─────────────────────┬───────────────────────────────────────┘
|
||||
│
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ StarForth VM (M7+) │
|
||||
│ • 295 FORTH-79 words │
|
||||
│ • Parity validation (hash=0x684bbf2fa1d96d55) │
|
||||
│ • Physics-driven adaptive runtime │
|
||||
│ • Init capsule system (M7.1) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Current Milestones
|
||||
|
||||
| Milestone | Description | Status |
|
||||
|-----------|-------------|--------|
|
||||
| M0 | UEFI Boot + Serial Output | Complete |
|
||||
| M1 | Physical Memory Manager | Complete |
|
||||
| M2 | Virtual Memory Manager | Complete |
|
||||
| M3 | IDT + Interrupts | Complete |
|
||||
| M4 | APIC Timer | Complete |
|
||||
| M5 | Timer Calibration | Complete |
|
||||
| M6 | Kernel Heap | Complete |
|
||||
| **M7** | **VM Parity Validation** | **Complete** |
|
||||
| **M7.1** | **Capsules · Tripod · Stadium · ACL** | **Live** (see FABRIC docs) |
|
||||
| M8 | REPL (keyboard input) | Superseded — see FABRIC-0 §25.5 item 4.4 |
|
||||
| M9 | Block Storage / Block I/O | Live (virtio-blk + block subsystem) |
|
||||
|
||||
---
|
||||
|
||||
## Build & Run
|
||||
|
||||
```bash
|
||||
# Build kernel with VM integration (loader + kernel)
|
||||
make -f kernel/Makefile ARCH=amd64 STARFORTH_ENABLE_VM=1
|
||||
|
||||
# Run in QEMU
|
||||
make -f kernel/Makefile ARCH=amd64 STARFORTH_ENABLE_VM=1 qemu
|
||||
|
||||
# Other architectures
|
||||
make -f kernel/Makefile ARCH=aarch64 qemu
|
||||
make -f kernel/Makefile ARCH=riscv64 qemu
|
||||
```
|
||||
|
||||
**Output artifacts:**
|
||||
- `build/amd64/kernel/starkernel_loader.efi` — UEFI loader
|
||||
- `build/amd64/kernel/starkernel_kernel.elf` — Kernel binary
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
### Core Design
|
||||
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [M7.1.md](M7.1.md) | Init Capsule Architecture |
|
||||
| [ROADMAP.md](ROADMAP.md) | Milestone roadmap |
|
||||
| [amd64-isr-fix/README.md](amd64-isr-fix/README.md) | APIC timer ISR fix + kernel 3-arch acceptance test |
|
||||
| [hosted-acceptance-test/README.md](hosted-acceptance-test/README.md) | Hosted VM 3-arch acceptance test (amd64 native, aarch64/riscv64 under QEMU user-mode) |
|
||||
|
||||
### HAL (Hardware Abstraction Layer)
|
||||
|
||||
| Document | Description |
|
||||
|----------|-------------|
|
||||
| [hal/overview.md](hal/overview.md) | HAL architecture and principles |
|
||||
| [hal/interfaces.md](hal/interfaces.md) | HAL interface specifications |
|
||||
| [hal/lithosananke-integration.md](hal/lithosananke-integration.md) | Kernel-specific HAL implementation |
|
||||
| [hal/platform-implementations.md](hal/platform-implementations.md) | Platform implementation guide |
|
||||
| [hal/migration-plan.md](hal/migration-plan.md) | Migration from hosted to kernel |
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### One Truth Per VM
|
||||
|
||||
Every VM has exactly one birth capsule — its identity and provenance.
|
||||
|
||||
```
|
||||
PARITY:BIRTH vm_id=42 capsule_id=0xA13F mode=p capsule_hash=0x... dict_hash=0x...
|
||||
```
|
||||
|
||||
### DOMAIN vs PERSONALITY
|
||||
|
||||
**DOMAIN** is a construction concept that exists **only in Mama FORTH**. It's how Mama classifies, composes, and selects capsules. DOMAIN is never visible to baby VMs.
|
||||
|
||||
**PERSONALITY** is the result of executing a production `(p)` INIT capsule. It's what the baby VM *is* — its vocabulary, invariants, capabilities. PERSONALITY is immutable and defines the VM's entire worldview.
|
||||
|
||||
### Production (p) vs Experiment (e)
|
||||
|
||||
- **(p) capsules** are truth-bearing — they can birth VMs
|
||||
- **(e) capsules** are Mama-only workloads — experiments that never touch babies
|
||||
|
||||
A baby VM never sees, executes, or knows about (e) capsules.
|
||||
|
||||
### Content-Addressed Capsules
|
||||
|
||||
Capsules are identified by content hash, not names. Names lie. Hashes don't.
|
||||
|
||||
### Mama FORTH
|
||||
|
||||
The root VM that holds all truths, manages child VMs, and enforces the birth protocol. Mama:
|
||||
- Maintains the capsule directory
|
||||
- Selects (p) capsules for birth
|
||||
- Executes (e) capsules for experimentation
|
||||
- Logs all births and runs for provenance
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/starkernel/
|
||||
├── boot/ # UEFI loader, ELF loader
|
||||
├── hal/ # Console, host services
|
||||
├── memory/ # PMM, VMM, kmalloc
|
||||
├── arch/amd64/ # x86_64 specific (APIC, IDT, timer)
|
||||
├── vm/ # VM integration
|
||||
│ ├── arena.c # VM memory arena
|
||||
│ ├── parity.c # Parity validation
|
||||
│ ├── bootstrap/ # sk_vm_bootstrap
|
||||
│ └── host/ # Host services shim
|
||||
└── kernel_main.c # Entry point
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
The contributing rules are:
|
||||
|
||||
1. Maintain parity validation (M7 baseline)
|
||||
2. Pass the three-arch QEMU boot test (`ARCH={amd64,aarch64,riscv64}`)
|
||||
3. Not break the hosted VM build (plain `make` at the repo root)
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [StarForth README](../../README.md) — Main project
|
||||
- [FORTH-79 Standard](http://www.forth.org/forth-79.html)
|
||||
- [OSDev Wiki](https://wiki.osdev.org/)
|
||||
- [UEFI Specification](https://uefi.org/specifications)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,97 +0,0 @@
|
||||
# APIC Timer ISR Fix — amd64
|
||||
|
||||
**Date**: 2026-06-03
|
||||
**Branch**: `claude/starforth-architecture-review-oubN0`
|
||||
**Commits**: `fb0c256`, `901f827`
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
The LithosAnanke amd64 kernel was triple-faulting on the first APIC timer interrupt,
|
||||
causing QEMU to exit immediately after printing `ok>`. The adaptive heartbeat never
|
||||
fired and `Heartbeat: N ticks` never appeared in serial logs.
|
||||
|
||||
## Root Cause
|
||||
|
||||
The kernel is compiled with `-fPIC -fvisibility=hidden` (required for UEFI PE
|
||||
relocation). Assembly symbols `isr_stub_table` and `isr_stub0` in `isr.S` were
|
||||
declared `.global` with **default visibility**. The C compiler therefore accessed
|
||||
them through GOT indirection. However, the linker — building a final non-shared
|
||||
binary — collapsed the GOT references to point directly at the symbols themselves.
|
||||
|
||||
The net effect: "load from GOT[`isr_stub_table`]" loaded the 8-byte instruction
|
||||
bytes of `isr_stub0` (`0x006a90eb006a006a`) rather than its address. Both
|
||||
`link_time_addr` and `runtime_addr` in `arch_interrupts_init()` became the same
|
||||
wrong value (instruction bytes), so `reloc_offset = 0` and all 256 IDT entries
|
||||
received garbage handler addresses. IDT[32] was set to `0x6a006afffffe91e9`
|
||||
(non-canonical), which triggered #GP → double fault → triple fault on the first
|
||||
timer delivery.
|
||||
|
||||
The bug was confirmed by disassembling `arch_interrupts_init()` in the loader PE:
|
||||
the compiler emitted a `mov` (load from memory) for `&isr_stub0` instead of a
|
||||
`lea` (compute address), reading the function's instruction bytes.
|
||||
|
||||
## Fix
|
||||
|
||||
Two changes, one line each:
|
||||
|
||||
**`src/starkernel/arch/amd64/isr.S`** — Added `.hidden` directives for all symbols
|
||||
referenced from C:
|
||||
|
||||
```asm
|
||||
.hidden isr_stub_table
|
||||
.hidden isr_stub0
|
||||
.hidden isr_stub32
|
||||
.hidden isr_stub_default
|
||||
.hidden isr_common_entry
|
||||
```
|
||||
|
||||
**`src/starkernel/arch/amd64/interrupts.c`** — Added visibility attributes to the
|
||||
`extern` declarations:
|
||||
|
||||
```c
|
||||
extern void *isr_stub_table[IDT_ENTRIES] __attribute__((visibility("hidden")));
|
||||
extern void isr_stub0(void) __attribute__((visibility("hidden")));
|
||||
```
|
||||
|
||||
With hidden visibility, the compiler uses direct RIP-relative `lea` instead of GOT
|
||||
indirection, yielding correct runtime addresses for all 256 IDT entries.
|
||||
|
||||
## Result
|
||||
|
||||
| Criterion | Before | After |
|
||||
|---|---|---|
|
||||
| IDT[32] handler | `0x6a006afffffe91e9` (corrupt) | `0x3d90251c` (valid canonical address) |
|
||||
| First timer interrupt | #GP → triple fault → QEMU exit | ISR fires cleanly, returns via `iretq` |
|
||||
| `PARITY:OK` | ✅ (unaffected) | ✅ |
|
||||
| `Init: init.4th OK` | ✅ (unaffected) | ✅ |
|
||||
| `ok>` REPL | ✅ reached, then immediate crash | ✅ runs indefinitely |
|
||||
| `Heartbeat: N ticks` | Never | Every second at 100 Hz, TIME-TRUST ≈ 0.999 |
|
||||
|
||||
## Secondary Fix
|
||||
|
||||
`repl_print_dec32`, `repl_lapic_ccr`, `repl_print_hex32`, and `idle_iters` in
|
||||
`repl.c` are x86-64 specific (LAPIC MMIO, `pushfq`). They were unguarded, causing
|
||||
`-Werror=unused-function` / `unused-variable` failures on aarch64 (clang) and
|
||||
riscv64 (gcc). All moved inside `#ifdef ARCH_AMD64`.
|
||||
|
||||
## Acceptance Testing
|
||||
|
||||
All three architectures must boot to `ok>` with `PARITY:OK` and `Init: init.4th OK`.
|
||||
amd64 additionally shows `Heartbeat: N ticks TIME-TRUST≈0.999` repeating at 1 Hz.
|
||||
|
||||
```bash
|
||||
make -f Makefile.starkernel ARCH=amd64 clean qemu
|
||||
make -f Makefile.starkernel ARCH=aarch64 clean qemu
|
||||
make -f Makefile.starkernel ARCH=riscv64 clean qemu
|
||||
```
|
||||
|
||||
Verify each log:
|
||||
|
||||
```bash
|
||||
grep "PARITY:OK" logs/qemu-<arch>-*.log
|
||||
grep "Init: init.4th OK" logs/qemu-<arch>-*.log
|
||||
grep "ok>" logs/qemu-<arch>-*.log
|
||||
grep "Heartbeat:" logs/qemu-amd64-*.log | head -5
|
||||
```
|
||||
@@ -1,178 +0,0 @@
|
||||
# Hosted StarForth — 3-Architecture Acceptance Test
|
||||
|
||||
**Date**: 2026-07-24 (original), corrected 2026-08-02
|
||||
**Branch**: `master`
|
||||
**Commits**: `edced063` (original block-subsystem fix), `4485c38` (doc +
|
||||
3 clang-surfaced bug fixes), `e287334` (asm register-reuse fix), this pass
|
||||
(`riscv64-clang` Makefile target)
|
||||
|
||||
---
|
||||
|
||||
## What this verifies
|
||||
|
||||
The **hosted** StarForth VM (`make`, not `Makefile.starkernel`) is a static
|
||||
binary. This test confirms it starts cleanly — reaches the `ok>` REPL prompt
|
||||
with no errors — on all three architectures the project targets:
|
||||
|
||||
- **amd64** — native execution on the build host
|
||||
- **aarch64** — cross-compiled, run under QEMU user-mode emulation
|
||||
- **riscv64** — cross-compiled, run under QEMU user-mode emulation
|
||||
|
||||
This is distinct from the kernel (`Makefile.starkernel`) 3-arch acceptance
|
||||
test documented in
|
||||
[`../amd64-isr-fix/README.md`](../amd64-isr-fix/README.md), which boots a
|
||||
full UEFI bare-metal image under system-mode QEMU. This test only exercises
|
||||
the hosted Linux binary, using QEMU **user-mode** emulation to run a
|
||||
foreign-architecture static binary directly on the x86_64 host — no kernel,
|
||||
no UEFI firmware, no system emulation.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Cross toolchains, QEMU user-mode emulation, and clang (needed for the
|
||||
riscv64 leg — see below):
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y \
|
||||
gcc-aarch64-linux-gnu \
|
||||
gcc-riscv64-linux-gnu \
|
||||
clang-18 lld-18 \
|
||||
qemu-user
|
||||
```
|
||||
|
||||
`qemu-user` alone is enough — the binaries built below are statically
|
||||
linked (`-static`), so the non-static `qemu-<arch>` emulators run them
|
||||
directly with no sysroot. (The `qemu-user-static` package, if you see it
|
||||
referenced elsewhere, provides statically-linked *emulators*; it's not
|
||||
required just because the *guest* binary is static.)
|
||||
|
||||
---
|
||||
|
||||
## Invocation note
|
||||
|
||||
None of the three binaries accept a `-c "<script>"` flag — earlier
|
||||
revisions of this doc showed one, but `src/cli.c`/`src/main.c` have never
|
||||
had one in this repo's history. Pipe the script via stdin with `-s`
|
||||
(script/silent mode) instead. Startup always runs the full internal POST
|
||||
test suite before the REPL takes stdin, so expect substantial test-suite
|
||||
output before your script's result appears at the very end.
|
||||
|
||||
---
|
||||
|
||||
## amd64 — native
|
||||
|
||||
```bash
|
||||
make clean && make
|
||||
echo "1 2 + . BYE" | ./build/amd64/standard/starforth -s
|
||||
```
|
||||
|
||||
Expect `3 Goodbye!` at the end of the output, then a clean exit. No
|
||||
emulation needed — this is the build host's native architecture.
|
||||
|
||||
---
|
||||
|
||||
## aarch64 — cross-compile + QEMU user-mode
|
||||
|
||||
```bash
|
||||
make rpi4-cross
|
||||
echo "1 2 + . BYE" | qemu-aarch64 ./build/raspi/fastest/starforth -s
|
||||
```
|
||||
|
||||
`make rpi4-cross` cross-compiles with `aarch64-linux-gnu-gcc`
|
||||
(`ARCH=raspi`, `TARGET=fastest`, `-static`); see `Makefile:547-554`. The
|
||||
output binary is statically linked, so `qemu-aarch64` can execute it
|
||||
directly with no sysroot.
|
||||
|
||||
---
|
||||
|
||||
## riscv64 — cross-compile + QEMU user-mode
|
||||
|
||||
```bash
|
||||
make riscv64-clang
|
||||
echo "1 2 + . BYE" | qemu-riscv64 ./build/riscv64/fastest/starforth -s
|
||||
```
|
||||
|
||||
`make riscv64-clang` cross-compiles with `clang-18` (`ARCH=riscv64`,
|
||||
`TARGET=fastest`, `-static`); see `Makefile:566-579`. **This leg must use
|
||||
clang, not GCC**: `riscv64-linux-gnu-gcc` fails to build this tree
|
||||
(`nanosleep` visibility failure under `-std=c99`, unrelated to any project
|
||||
change — root cause not yet chased down, see
|
||||
`docs/working/archive/session-logs/2026-07-24-punch-list.md` item #3). The
|
||||
target's `CFLAGS` deliberately does not reuse the Makefile's
|
||||
`$(BASE_CFLAGS)` (which hardcodes `-std=c99`) — clang needs `-std=c11
|
||||
-pthread` here instead, plus `-fuse-ld=lld` in `LDFLAGS` (folding it into
|
||||
`CC` makes clang reject it as an unused argument during `-c`/compile-only
|
||||
invocations under `-Werror`).
|
||||
|
||||
---
|
||||
|
||||
## Pass criteria
|
||||
|
||||
For each architecture:
|
||||
|
||||
1. Build completes with no errors.
|
||||
2. `blk_subsys_init` succeeds — no `"Failed to initialize block subsystem"`
|
||||
error. (This was broken on every architecture, including amd64, prior to
|
||||
`edced063` — see below.)
|
||||
3. The internal POST suite runs (substantial log output — expected, not a
|
||||
failure by itself) and the piped `1 2 + . BYE` script prints `3 Goodbye!`
|
||||
at the end.
|
||||
|
||||
---
|
||||
|
||||
## Background: the block-subsystem bug this depends on
|
||||
|
||||
Before `edced063`, the hosted VM could not start on **any** architecture on
|
||||
this branch. `src/main.c` allocated a hardcoded `blk_ram[1024 * 1024]`
|
||||
buffer, stale relative to `BLK_RAM_BLOCKS=2080` (`include/block_subsystem.h`)
|
||||
— a deliberate redesign that moved the RAM/ramdrive boundary to physical
|
||||
block 2080 (user-visible LBN 2048), per `BLK_FORTH_SYS_RESERVED=32`. The
|
||||
kernel-side buffer in `src/starkernel/vm/bootstrap/sk_vm_bootstrap.c` already
|
||||
derived its size correctly from the shared constant; `main.c` was the one
|
||||
remaining hardcoded holdout. The fix:
|
||||
|
||||
```c
|
||||
#include "block_subsystem.h"
|
||||
...
|
||||
static uint8_t blk_ram[BLK_RAM_BLOCKS * BLK_FORTH_SIZE]; /* was: blk_ram[1024 * 1024] */
|
||||
```
|
||||
|
||||
This is why the acceptance test above is meaningful on this branch
|
||||
specifically: it did not pass before this fix, on any of the three
|
||||
architectures.
|
||||
|
||||
---
|
||||
|
||||
## Background: the riscv64 clang build (2026-08-02 correction)
|
||||
|
||||
The riscv64 command above was broken in this doc from 2026-07-24 until this
|
||||
correction — `docs/working/archive/session-logs/2026-07-24-punch-list.md`
|
||||
(item #2) recorded that the working clang invocation had been found and
|
||||
fixed in the old pre-split monorepo's `master` branch's `.claude/CLAUDE.md`,
|
||||
but the correction was never copied into this doc. Verifying it end-to-end
|
||||
for this correction surfaced that the clang build also needs three small
|
||||
source fixes from that same punch list (item #1), previously ported to the
|
||||
old monorepo's `master` but not to this line:
|
||||
|
||||
- `src/math_portable.c` — `-100LL << 16` is UB (shifting a negative value)
|
||||
under `-Wshift-negative-value`; changed to `-(100LL << 16)`.
|
||||
- `src/physics_pipelining_metrics.c` — removed dead `q48_mul_q48()`
|
||||
(`-Wunused-function` under clang; GCC doesn't catch this by default).
|
||||
- `src/word_source/editor_words.c` — removed dead `set_scr()` (same reason).
|
||||
|
||||
The punch list's highest-priority item — a genuine SIGSEGV-causing
|
||||
register-reuse hazard in `include/vm_asm_opt_riscv64.h`'s `vm_pop_asm`/
|
||||
`vm_rpop_asm` (clang's register allocator can pick the same register for a
|
||||
memory operand referenced both before and after a write-back, corrupting
|
||||
the result; GCC happens not to hit it) was *not* required just to get this
|
||||
acceptance test's `-O3` build to compile and pass, but has since been
|
||||
fixed too (commit `e287334`) — and turned out not to be merely a latent
|
||||
risk: it was silently corrupting 10 `CASE.*` control-flow tests under this
|
||||
exact build (`955`/`10 failed` → `965`/`0 failed`, `ALL IMPLEMENTED TESTS
|
||||
PASSED!`, with nothing else changed).
|
||||
|
||||
Item #3 of the same punch list — no `make` target for this leg, only the
|
||||
long manual invocation — is also resolved as of this pass: `make
|
||||
riscv64-clang` wires in the same recipe verified above directly into the
|
||||
Makefile.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
||||
# docs/lithosananke/kernel-args/
|
||||
|
||||
Design doc for kernel command-line arguments (`KernelArgs`).
|
||||
|
||||
- **[DESIGN.md](DESIGN.md)** — Design for runtime-configurable memory sizing
|
||||
and behavior (heap size, stack depth, DoE self-start, log verbosity)
|
||||
currently baked in as compile-time constants. Covers the UEFI
|
||||
`EFI_LOADED_IMAGE_PROTOCOL->LoadOptions` mechanism, the `KernelArgs`
|
||||
struct, `BootInfo` changes, the assembly trampoline
|
||||
(`kernel_entry.S`), the `cmdline.c` parser, loader/`kernel_main.c`/
|
||||
Makefile integration, passing args under QEMU, and the `REBOOT` FORTH
|
||||
word (including its NVRAM one-shot mechanism and hosted-VM stub). Status:
|
||||
design complete, awaiting implementation.
|
||||
|
||||
## See also
|
||||
|
||||
- [`docs/lithosananke/README.md`](../README.md) — kernel branch overview.
|
||||
- [Project root](../../../README.md)
|
||||
@@ -1,183 +0,0 @@
|
||||
# Kernel Stack Trampoline Fix — riscv64
|
||||
|
||||
**Date**: 2026-08-02
|
||||
**Branch**: `master`
|
||||
**Commit**: `7366275`
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
The riscv64 kernel crashed partway through VM bootstrap during the 2026-08-01
|
||||
DoE campaign run, well after amd64 and aarch64 had both booted cleanly to
|
||||
`ok>` in the same session. The crash was a wild jump: execution landed at an
|
||||
address nowhere inside the kernel's own linked image, then almost immediately
|
||||
faulted trying to load from a small, near-null address.
|
||||
|
||||
```
|
||||
Registering FORTH-79 Standard word set...
|
||||
Registering FORTH-79 arithmetic words...
|
||||
FORTH-79 Standard word set registration complete
|
||||
*** EXCEPTION (riscv64) ***
|
||||
scause = 0x000000000000000d
|
||||
sepc = 0x00000000be27c3c2
|
||||
stval = 0x00000000000074d8
|
||||
```
|
||||
|
||||
`scause = 0xd` is a supervisor-mode load page fault. `sepc` (~0xbe27c3c2) sat
|
||||
near the top of QEMU's 1 GB RAM window — nowhere close to the kernel's own
|
||||
`.text`/`.data`/`.bss` footprint (`0x400000`–`0xcdf9a0`, confirmed via `nm`/
|
||||
`readelf` on the exact build that produced the log). `stval` (0x74d8, ~30 KB)
|
||||
stayed fixed across rebuilds even as `sepc` drifted — the signature of a
|
||||
corrupted return address rather than a fixed logic bug at one source line.
|
||||
|
||||
## Investigation
|
||||
|
||||
Two dead ends were ruled out before the real cause surfaced:
|
||||
|
||||
1. **ELF relocation / buffer overlap in the split-build loader.** The
|
||||
codebase has a `!MONOLITHIC_BUILD` path (`elf_load_kernel()` in
|
||||
`elf_loader.c`) that loads `kernel.elf` from the ESP into a UEFI
|
||||
`AllocateAnyPages` buffer. This looked like a plausible overlap candidate
|
||||
at first, but `Makefile.starkernel` defaults to `MONOLITHIC=1`, meaning
|
||||
`kernel_main` is linked directly into the loader and that code path never
|
||||
runs. Confirmed by checking the built binary for the investigation's own
|
||||
diagnostic strings — absent, because the compilation unit was built with
|
||||
`-DMONOLITHIC_BUILD` and the code was preprocessed out.
|
||||
2. **`RAW_LOG()` silently no-ops on every non-amd64 arch.** All of
|
||||
`load_kernel_from_esp()`'s narration (and an early ConOut-based
|
||||
diagnostic) never reached the captured serial log, on riscv64, in any
|
||||
build. `RAW_LOG` was defined only for `ARCH_AMD64`; everywhere else it
|
||||
expanded to `((void)0)`. This made straightforward diagnosis impossible
|
||||
until a real raw-UART writer was added for riscv64 (see Secondary Fix).
|
||||
|
||||
With real output available, `console_println()` checkpoints were bisected
|
||||
through `vm_init_with_host()` and `sk_vm_bootstrap_parity()`. Every checkpoint
|
||||
inside `vm_init_with_host()` printed successfully, including the very last
|
||||
line of the function — but the checkpoint immediately after its *return*
|
||||
never appeared. Re-running with a trivial, unrelated local-variable addition
|
||||
shifted the crash earlier and changed the exception type entirely (from a
|
||||
load fault to an instruction-fetch fault at `sepc == stval`, i.e. the CPU
|
||||
tried to *execute* an unmapped address). A crash whose location and even
|
||||
category shift under unrelated code changes is the standard signature of
|
||||
stack corruption, not a fixed bug at a specific call site.
|
||||
|
||||
## Root Cause
|
||||
|
||||
`kernel_main.c` documented the gap directly, once read closely:
|
||||
|
||||
> On amd64, `kernel_entry.S` switches the stack from UEFI's default to a
|
||||
> 2 MiB zero-initialised BSS stack and tail-calls this function as
|
||||
> `kernel_main_impl`. On aarch64 and riscv64 the assembly trampoline is not
|
||||
> yet implemented and the UEFI loader calls `kernel_main` directly.
|
||||
|
||||
amd64 has carried a dedicated stack-switch trampoline
|
||||
(`src/starkernel/arch/amd64/kernel_entry.S`) since early on, with its own
|
||||
comment explaining why: *"UEFI hands control to kernel_main() running on a
|
||||
small UEFI-provided stack (typically 128 KB–256 KB). The FORTH interpreter +
|
||||
DOE experiment loop can easily exceed that depth."* riscv64 never had the
|
||||
equivalent. It ran the entire VM bootstrap — 27 chained word-registration
|
||||
modules, physics/SSM init, Tripod capsule birth — directly on whatever small
|
||||
stack EDK2's riscv64 firmware handed the loader, overflowing it and
|
||||
corrupting a return address. aarch64 has the same theoretical exposure but
|
||||
happens not to trip it, apparently because its firmware's default boot stack
|
||||
is larger — incidental, not a guarantee.
|
||||
|
||||
## Fix
|
||||
|
||||
**`src/starkernel/arch/riscv64/kernel_entry.S`** (new) — RISC-V stack-switch
|
||||
trampoline mirroring amd64's:
|
||||
|
||||
```asm
|
||||
.section .bss
|
||||
.align 4
|
||||
.global g_kernel_stack
|
||||
g_kernel_stack:
|
||||
.space 0x200000
|
||||
g_kernel_stack_top:
|
||||
.global g_kernel_stack_top
|
||||
|
||||
.section .text
|
||||
.extern kernel_main_impl
|
||||
.global kernel_main
|
||||
kernel_main:
|
||||
ld t0, BOOT_INFO_KERNEL_STACK_BASE_OFFSET(a0)
|
||||
bnez t0, .Ldynamic_stack
|
||||
la t0, g_kernel_stack_top
|
||||
j .Lstack_ready
|
||||
.Ldynamic_stack:
|
||||
ld t1, BOOT_INFO_KERNEL_STACK_SIZE_OFFSET(a0)
|
||||
add t0, t0, t1
|
||||
.Lstack_ready:
|
||||
andi t0, t0, -16
|
||||
mv sp, t0
|
||||
mv s0, zero
|
||||
tail kernel_main_impl
|
||||
```
|
||||
|
||||
**`src/starkernel/kernel_main.c`** — riscv64 now builds `kernel_main_impl`
|
||||
(invoked via the trampoline) instead of `kernel_main` directly, same pattern
|
||||
as amd64:
|
||||
|
||||
```c
|
||||
#if defined(__x86_64__) || defined(__riscv)
|
||||
void kernel_main_impl(BootInfo *boot_info) {
|
||||
#else
|
||||
void kernel_main(BootInfo *boot_info) {
|
||||
#endif
|
||||
```
|
||||
|
||||
**`Makefile.starkernel`** — wires the new file into the riscv64 loader build:
|
||||
|
||||
```makefile
|
||||
ifeq ($(ARCH),riscv64)
|
||||
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/kernel_entry.S
|
||||
endif
|
||||
```
|
||||
|
||||
## Secondary Fix
|
||||
|
||||
`RAW_LOG()` in `src/starkernel/boot/uefi_loader.c` was a no-op on every arch
|
||||
except amd64, silencing all of the loader's own diagnostic messages on
|
||||
riscv64 (and aarch64). Added a real raw-UART writer for riscv64, targeting
|
||||
QEMU virt's 16550-compatible `uart8250` at MMIO `0x10000000` (matches
|
||||
`Domain0 Region03` in the OpenSBI boot banner) — the same byte-polling
|
||||
protocol as the existing amd64 port-I/O version, just MMIO instead of `outb`/
|
||||
`inb`. Existing and future `RAW_LOG()` call sites in the loader now actually
|
||||
produce output on riscv64.
|
||||
|
||||
## Result
|
||||
|
||||
| Criterion | Before | After |
|
||||
|---|---|---|
|
||||
| `sepc` after word registration | Wild address outside kernel image (`0xbe27c3c2`-ish, drifting per build) | N/A — no longer occurs |
|
||||
| Exception | Load or instruction-fetch page fault, non-deterministic between builds | None |
|
||||
| `[Hera] ok>` (riscv64) | Never reached | ✅ reached, Tripod fleet (Hera/Hermes/Artemis) runs to completion |
|
||||
| `PASS: E2E msg flow` | Never logged | ✅ |
|
||||
| `PARITY:MAMA_INIT` / `Init: Mama birth OK` | Never logged | ✅ |
|
||||
| Loader `RAW_LOG()` output on riscv64 | Silent no-op | ✅ visible in serial log |
|
||||
|
||||
## Acceptance Testing
|
||||
|
||||
All three architectures must boot to `ok>`, run the Tripod fleet, and reach
|
||||
`[Hera] ok>`. Per repo convention, run in this exact order, one QEMU instance
|
||||
at a time, always `clean` before `qemu`:
|
||||
|
||||
```bash
|
||||
make -f Makefile.starkernel ARCH=amd64 clean qemu
|
||||
make -f Makefile.starkernel ARCH=aarch64 clean qemu
|
||||
make -f Makefile.starkernel ARCH=riscv64 clean qemu
|
||||
```
|
||||
|
||||
Verify each log:
|
||||
|
||||
```bash
|
||||
grep "ok>" logs/<session>/<arch>/qemu-<arch>-*.log
|
||||
grep "PASS: E2E msg flow" logs/<session>/<arch>/qemu-<arch>-*.log
|
||||
grep "PARITY:MAMA_INIT" logs/<session>/<arch>/qemu-<arch>-*.log
|
||||
grep "EXCEPTION" logs/<session>/<arch>/qemu-<arch>-*.log # must be empty
|
||||
```
|
||||
|
||||
All three architectures confirmed clean on 2026-08-02 (see
|
||||
`logs/20260802-064050/riscv64/`, `logs/20260802-064424/amd64/`,
|
||||
`logs/20260802-064656/aarch64/`, committed alongside this fix).
|
||||
Reference in New Issue
Block a user