Files
LithosAnanake/docs/working/archive/historical/2026-07-amd64-isr-fix
rajamesandClaude Opus 5.5 7aed588f78 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>
2026-10-14 16:03:56 -04:00
..

HISTORICAL (docs triage 2026-10-14). The write-up of one fix (APIC timer ISR, amd64), done. Instead: the code itself (kernel/src/arch/amd64/). Moved here from docs/lithosananke/amd64-isr-fix/README.md.

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:

.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:

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.

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:

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