Problem
If Bao encounters a fatal error (ERROR()) before console_init() completes, the system hangs silently with no diagnostic output. The caller is stuck in the console_write() busy-wait loop (src/core/console.c:44) waiting for console_ready — a flag that the master CPU sets only after full UART MMIO mapping and hardware initialization.
The initialization order is:
cpu_init() ← errors here are silent
mem_init() ← errors here are silent
platform_init() ← errors here are silent
console_init() ← console_ready = true
ERROR() calls console_printk() → console_write() → spins on console_ready. Examples of code that can trigger this before the console is ready:
cpu.c:61 — IPI message node allocation failure
mem.c:559, 581, 585 — root pool init, additional pool creation, static memory reservation
Current Behavior
- System hangs silently at the
while (!console_ready) spin loop.
- No diagnostic output is ever produced.
- The hang is indistinguishable from a successful boot that stalled elsewhere or a hardware issue.
Expected Behavior
A minimal early print path should be available from the very start of init() so that fatal errors during cpu_init(), mem_init(), and platform_init() produce some output rather than silently deadlocking.
Proposed Solution
Add an early console mechanism — a minimal, dependency-free output path usable before console_init():
- Architecture-agnostic interface:
early_putchar(char c) / early_puts(const char*) with a weak default no-op, overridden per architecture/platform.
- RISC-V: Delegate to
sbi_console_putchar() — already available in src/arch/riscv/sbi.c with zero prerequisites (the existing sbi_uart driver could be promoted to serve as the early console on RISC-V platforms).
- ARM (aarch64 / armv8): Use a compile-time physical address constant (
CONFIG_EARLY_UART_BASE) and write directly to it using a physical (or identity-mapped) address before the MMU-managed mapping is in place.
- MPU platforms (Cortex-R / M): Direct physical address write is simpler since there is no dynamic MMU mapping phase.
An EARLY_ERROR() macro (or a flag that switches ERROR() behaviour based on init phase) would use this path, ensuring at minimum that the offending subsystem is named before the system halts.
Affected Files
src/core/init.c — initialization order
src/core/console.c — console_ready flag and spin loop
src/core/inc/bao.h — ERROR(), WARNING(), INFO() macros
src/arch/riscv/sbi.c — SBI early output (RISC-V)
src/arch/armv8/aarch64/ — early UART write (ARM64)
- Platform descriptors —
platform.console.base (compile-time constant needed for early path)
References
console_write() spin loop: src/core/console.c:44
console_ready set: src/core/console.c:36
ERROR() macro: src/core/inc/bao.h:21
mem_init() error callsites: src/core/mem.c:559, 581, 585
cpu_init() error callsite: src/core/cpu.c:61
- SBI console driver:
src/platform/drivers/sbi_uart/sbi_uart.c
Problem
If Bao encounters a fatal error (
ERROR()) beforeconsole_init()completes, the system hangs silently with no diagnostic output. The caller is stuck in theconsole_write()busy-wait loop (src/core/console.c:44) waiting forconsole_ready— a flag that the master CPU sets only after full UART MMIO mapping and hardware initialization.The initialization order is:
ERROR()callsconsole_printk()→console_write()→ spins onconsole_ready. Examples of code that can trigger this before the console is ready:cpu.c:61— IPI message node allocation failuremem.c:559, 581, 585— root pool init, additional pool creation, static memory reservationCurrent Behavior
while (!console_ready)spin loop.Expected Behavior
A minimal early print path should be available from the very start of
init()so that fatal errors duringcpu_init(),mem_init(), andplatform_init()produce some output rather than silently deadlocking.Proposed Solution
Add an early console mechanism — a minimal, dependency-free output path usable before
console_init():early_putchar(char c)/early_puts(const char*)with a weak default no-op, overridden per architecture/platform.sbi_console_putchar()— already available insrc/arch/riscv/sbi.cwith zero prerequisites (the existingsbi_uartdriver could be promoted to serve as the early console on RISC-V platforms).CONFIG_EARLY_UART_BASE) and write directly to it using a physical (or identity-mapped) address before the MMU-managed mapping is in place.An
EARLY_ERROR()macro (or a flag that switchesERROR()behaviour based on init phase) would use this path, ensuring at minimum that the offending subsystem is named before the system halts.Affected Files
src/core/init.c— initialization ordersrc/core/console.c—console_readyflag and spin loopsrc/core/inc/bao.h—ERROR(),WARNING(),INFO()macrossrc/arch/riscv/sbi.c— SBI early output (RISC-V)src/arch/armv8/aarch64/— early UART write (ARM64)platform.console.base(compile-time constant needed for early path)References
console_write()spin loop:src/core/console.c:44console_readyset:src/core/console.c:36ERROR()macro:src/core/inc/bao.h:21mem_init()error callsites:src/core/mem.c:559, 581, 585cpu_init()error callsite:src/core/cpu.c:61src/platform/drivers/sbi_uart/sbi_uart.c