fix(console): the serial line is found, not assumed -- from the devicetree on a board, COM1 by probe on a PC

Each board has one serial line that is its console, the lowest common
denominator beside the screen and keyboard.  Its address was QEMU's,
fixed: on a Raspberry Pi 5 the aarch64 one is ordinary RAM, written to
with every character printed; on a Milk-V Mars the riscv64 one is the
board's UART with registers four bytes apart, and the UEFI loader would
have waited for ever on its first character; on a PC with no COM1 the
prompt read 0xFF for ever.

console_attach_serial(), called before anything prints: COM1 if a UART
answers there; elsewhere the UART that /chosen/stdout-path names
(fdt_uart.c: alias, path, the address carried up through each bus's
ranges, reg-shift and reg-io-width); with no devicetree, QEMU's.  A
devicetree that names no usable console gives no line, not a guess.  A
line that does not come ready is given up, not waited on.  The riscv64
loader's early serial does the same.

tools/fdt_uart_test.c, run by hand, 19 checks plain and under the
sanitizers: devicetrees shaped like the Mars's, the Pi 5's and QEMU's,
what must find nothing, and a block cut short at every length; and the
Pi 5's real devicetree gives a PL011 at 0x107d001000.

Acceptance, one QEMU at a time: v3 on amd64 (logs/20261013-204436),
aarch64 (-204803) and riscv64 (-205001) to the zuse prompt, no unknown
word, hash 0x08873e0f44b7cb2a on all three as before.  -204600 is an
aarch64 run during which the disk images were restored under it by a
script that had been refused a second QEMU; it passed and is kept, and
was run again.  v4 on the three ISAs (-211149, -211444, -211857): POST
538 of 538, dict_hash 0xe5a57f3eb72ff94a, the typed session unchanged.
The Mars image through OpenSBI and U-Boot in QEMU took its line and its
interrupt controller from the devicetree and passed POST.

Found on the way and not mended (boards/BRINGUP.md 1.6): built with
EMERGENCY_CONSOLE_ENABLED=1 the prompt floods with empty lines.  -210442
shows it; the commit before this does the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-13 21:21:32 -04:00
co-authored by Claude Opus 5.5
parent f4e359859a
commit c95191736e
26 changed files with 48560 additions and 141 deletions
+51
View File
@@ -0,0 +1,51 @@
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James. All rights reserved.
Licensed under the StarForth License, Version 1.0.
*/
/**
* fdt_uart.h - the console's serial line, as a board's devicetree names it.
*
* A board has one serial line that is its console, and its devicetree says
* which: /chosen/stdout-path, either the path of a node or an alias for one
* (/aliases), with line settings after a colon that are not used here. The
* firmware that handed the devicetree over has already set that line up.
* This finds what kind of UART it is, where it is as the CPU addresses it,
* and how its registers are laid out.
*
* Nothing is guessed. If the devicetree does not name a console, or names
* one this cannot place or has no driver for, there is no line.
*
* Verified off the boards by tools/fdt_uart_test.c.
*/
#ifndef STARKERNEL_FDT_UART_H
#define STARKERNEL_FDT_UART_H
#include <stdint.h>
enum
{
FDT_UART_NONE = 0, /**< no line */
FDT_UART_PL011, /**< ARM PL011: 32-bit registers, four bytes apart */
FDT_UART_16550 /**< 16550 and its kin (ns16550a, snps,dw-apb-uart), by memory */
};
typedef struct
{
int kind; /**< FDT_UART_* */
uint64_t base; /**< address of register 0, as the CPU sees it */
uint32_t reg_shift; /**< register n is at base + (n << reg_shift) */
uint32_t io_width; /**< bytes read or written at a time: 1 or 4 */
} fdt_uart_t;
/**
* @brief Find the console's serial line in a devicetree.
* @param fdt Blob; NULL and what is not a devicetree are safe.
* @param out Receives the line; its kind is FDT_UART_NONE when 0 is returned.
* @return 1 if the devicetree names a console UART that was found, placed
* and is of a kind listed above; 0 otherwise.
*/
int fdt_console_uart(const void* fdt, fdt_uart_t* out);
#endif /* STARKERNEL_FDT_UART_H */