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:
co-authored by
Claude Sonnet 5
parent
a852db2209
commit
736627510b
@@ -385,6 +385,9 @@ LOADER_ASM := \
|
|||||||
ifeq ($(ARCH),amd64)
|
ifeq ($(ARCH),amd64)
|
||||||
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/kernel_entry.S
|
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/kernel_entry.S
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(ARCH),riscv64)
|
||||||
|
LOADER_ASM += $(KERNEL_SRC)/arch/$(ARCH)/kernel_entry.S
|
||||||
|
endif
|
||||||
|
|
||||||
LOADER_ARCH_SRCS := \
|
LOADER_ARCH_SRCS := \
|
||||||
$(KERNEL_SRC)/arch/$(ARCH)/arch.c \
|
$(KERNEL_SRC)/arch/$(ARCH)/arch.c \
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* kernel_entry.S — kernel stack setup and entry trampoline (riscv64)
|
||||||
|
*
|
||||||
|
* UEFI hands control to kernel_main() running on EDK2's own boot-time stack.
|
||||||
|
* Unlike amd64 (see arch/amd64/kernel_entry.S, which has carried this same
|
||||||
|
* trampoline since early on), riscv64 previously called straight into
|
||||||
|
* kernel_main_impl with no stack switch at all. The VM bootstrap's call
|
||||||
|
* depth (27 chained word-registration modules, physics/SSM init, capsule
|
||||||
|
* birth, Tripod fleet spawn) overflowed EDK2's riscv64 boot stack partway
|
||||||
|
* through vm_init_with_host()'s return, corrupting the return address and
|
||||||
|
* producing a wild jump shortly after — observed as a load/instruction
|
||||||
|
* page fault (scause 0xd or 0xc) whose exact address drifted between
|
||||||
|
* builds while the crash always landed in this same call region. amd64
|
||||||
|
* never had this problem because kernel_entry.S already switches to a
|
||||||
|
* dedicated 2 MiB BSS stack before anything deep runs; aarch64 apparently
|
||||||
|
* gets away with EDK2's default boot stack being large enough, but that's
|
||||||
|
* incidental, not a guarantee.
|
||||||
|
*
|
||||||
|
* kernel_main is the symbol the UEFI loader calls (extern in uefi_loader.c).
|
||||||
|
* kernel_main_impl is defined in kernel_main.c and contains all the C code.
|
||||||
|
*
|
||||||
|
* Calling convention: RISC-V LP64D — boot_info pointer arrives in a0 and is
|
||||||
|
* passed unchanged to kernel_main_impl via the tail-call.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "starkernel/boot_info_offsets.h"
|
||||||
|
|
||||||
|
/* 2 MiB kernel stack in BSS — fallback when no dynamic stack requested */
|
||||||
|
.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
|
||||||
|
|
||||||
|
/*
|
||||||
|
* kernel_main — stack-switch trampoline, then tail-call kernel_main_impl.
|
||||||
|
*
|
||||||
|
* a0 = BootInfo* (RISC-V LP64D ABI — first argument register)
|
||||||
|
*
|
||||||
|
* Stack selection (checked in order):
|
||||||
|
* 1. boot_info->kernel_stack_base != 0 → use loader-allocated stack
|
||||||
|
* (top = base + size, aligned down to 16)
|
||||||
|
* 2. Otherwise → fall back to g_kernel_stack (2 MiB BSS)
|
||||||
|
*
|
||||||
|
* Never returns; kernel_main_impl runs the REPL forever or panics.
|
||||||
|
*/
|
||||||
|
.global kernel_main
|
||||||
|
kernel_main:
|
||||||
|
ld t0, BOOT_INFO_KERNEL_STACK_BASE_OFFSET(a0)
|
||||||
|
bnez t0, .Ldynamic_stack
|
||||||
|
|
||||||
|
/* BSS fallback */
|
||||||
|
la t0, g_kernel_stack_top
|
||||||
|
j .Lstack_ready
|
||||||
|
|
||||||
|
.Ldynamic_stack:
|
||||||
|
ld t1, BOOT_INFO_KERNEL_STACK_SIZE_OFFSET(a0)
|
||||||
|
add t0, t0, t1 /* top = base + size (stack grows down) */
|
||||||
|
|
||||||
|
.Lstack_ready:
|
||||||
|
andi t0, t0, -16 /* 16-byte alignment (RISC-V psABI requirement) */
|
||||||
|
mv sp, t0
|
||||||
|
mv s0, zero /* terminate frame-pointer chain */
|
||||||
|
tail kernel_main_impl /* a0 (boot_info) already in place */
|
||||||
@@ -180,6 +180,47 @@ static void raw_serial_puts(const char *s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#define RAW_LOG(str) raw_serial_puts(str)
|
#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
|
#else
|
||||||
#define RAW_LOG(str) ((void)0)
|
#define RAW_LOG(str) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -325,10 +325,10 @@ static void print_banner(void) {
|
|||||||
/**
|
/**
|
||||||
* @brief Main kernel entry point after UEFI handoff — executes milestones M0–M6.
|
* @brief Main kernel entry point after UEFI handoff — executes milestones M0–M6.
|
||||||
*
|
*
|
||||||
* On amd64, @c kernel_entry.S switches the stack from UEFI's default to a 2 MiB
|
* On amd64 and riscv64, @c kernel_entry.S switches the stack from UEFI's default
|
||||||
* zero-initialised BSS stack and tail-calls this function as @c kernel_main_impl.
|
* to a 2 MiB zero-initialised BSS stack and tail-calls this function as
|
||||||
* On aarch64 and riscv64 the assembly trampoline is not yet implemented and the
|
* @c kernel_main_impl. On aarch64 the assembly trampoline is not yet implemented
|
||||||
* UEFI loader calls @c kernel_main directly.
|
* and the UEFI loader calls @c kernel_main directly.
|
||||||
*
|
*
|
||||||
* Milestone sequence:
|
* Milestone sequence:
|
||||||
* - **M0 — Architecture early init** (@c arch_early_init()): On amd64, installs a
|
* - **M0 — Architecture early init** (@c arch_early_init()): On amd64, installs a
|
||||||
@@ -357,7 +357,7 @@ static void print_banner(void) {
|
|||||||
* framebuffer descriptor, runtime services pointer, and parsed
|
* framebuffer descriptor, runtime services pointer, and parsed
|
||||||
* kernel command-line arguments.
|
* kernel command-line arguments.
|
||||||
*/
|
*/
|
||||||
#if defined(__x86_64__)
|
#if defined(__x86_64__) || defined(__riscv)
|
||||||
void kernel_main_impl(BootInfo *boot_info) {
|
void kernel_main_impl(BootInfo *boot_info) {
|
||||||
#else
|
#else
|
||||||
void kernel_main(BootInfo *boot_info) {
|
void kernel_main(BootInfo *boot_info) {
|
||||||
|
|||||||
Reference in New Issue
Block a user