Fix riscv64 boot crash: add missing kernel stack trampoline

kernel_main on riscv64 ran directly on EDK2's UEFI boot-time stack, with
no dedicated stack switch — amd64 has always had a kernel_entry.S
trampoline for exactly this reason (its own comment: "the FORTH
interpreter + DOE experiment loop can easily exceed that depth").
aarch64 happens to get away without one because its firmware's default
stack is apparently larger, but that was never a guarantee.

On riscv64 the VM bootstrap's call depth (27 word-registration modules
-> physics/SSM init -> Tripod capsule birth) overflowed that small
stack, corrupting a return address and producing a wild jump / page
fault right after vm_init_with_host() returned — reproduced consistently
across the 2026-08-01 DoE campaign logs.

- src/starkernel/arch/riscv64/kernel_entry.S (new): RISC-V stack-switch
  trampoline mirroring amd64's, giving the kernel a dedicated 2 MiB BSS
  stack before anything deep runs.
- kernel_main.c: riscv64 now builds kernel_main_impl (invoked via the
  trampoline) instead of kernel_main directly, same pattern as amd64.
- Makefile.starkernel: wires the new file into the riscv64 build.
- uefi_loader.c: RAW_LOG() was silently a no-op on every non-amd64 arch;
  added a real raw-UART writer for riscv64 (QEMU virt's uart8250 at MMIO
  0x10000000) so existing loader diagnostics actually produce output.

Verified: all three architectures boot clean to [Hera] ok> in the
required order (amd64, aarch64, riscv64); logs and DoE CSVs from these
runs included.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-02 06:59:48 -04:00
co-authored by Claude Sonnet 5
parent a852db2209
commit 736627510b
11 changed files with 1368435 additions and 6 deletions
+41
View File
@@ -180,6 +180,47 @@ static void raw_serial_puts(const char *s)
}
}
#define RAW_LOG(str) raw_serial_puts(str)
#elif defined(__riscv) || defined(ARCH_RISCV64)
/*
* QEMU's riscv "virt" machine exposes a 16550-compatible UART (OpenSBI
* reports "Platform Console Device: uart8250") as byte-addressed MMIO at
* 0x10000000 (matches Domain0 Region03 in the OpenSBI boot banner). This
* writes directly to the UART registers, exactly like the amd64
* raw_serial_* helpers above, so RAW_LOG() actually produces output on
* riscv64 instead of silently no-op'ing (previously the case for every
* arch except amd64).
*/
#define UART_MMIO_BASE 0x10000000UL
static inline void raw_mmio_outb(uint64_t addr, uint8_t val)
{
*(volatile uint8_t *)addr = val;
}
static inline uint8_t raw_mmio_inb(uint64_t addr)
{
return *(volatile uint8_t *)addr;
}
static void raw_serial_putc(char c)
{
while ((raw_mmio_inb(UART_MMIO_BASE + 5) & 0x20) == 0) { }
raw_mmio_outb(UART_MMIO_BASE + 0, (uint8_t)c);
}
static void raw_serial_puts(const char *s)
{
while (*s)
{
char c = *s++;
if (c == '\n') raw_serial_putc('\r');
raw_serial_putc(c);
}
}
#define RAW_LOG(str) raw_serial_puts(str)
#else
#define RAW_LOG(str) ((void)0)
#endif