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
+22 -10
View File
@@ -40,15 +40,15 @@ None has been fixed: each needs your word.
| S4 | `BYE` resets through the i8042, not the ACPI reset register. | finding 4 | `BYE` may do nothing. It does not hang. | | S4 | `BYE` resets through the i8042, not the ACPI reset register. | finding 4 | `BYE` may do nothing. It does not hang. |
| S5 | Whether the SER5 has a serial header is not known. | `FABRIC-3.md` III | Without one, and with A3, the SER5 can be watched over HDMI and not typed to. | | S5 | Whether the SER5 has a serial header is not known. | `FABRIC-3.md` III | Without one, and with A3, the SER5 can be watched over HDMI and not typed to. |
The loader probes COM1 before using it. The kernel's own console writes to port `0x3F8` The loader probes COM1 before using it, and since 2026-10-13 so does the kernel's console.
without a probe; on a board with no COM1 the status port reads all ones and the write goes Before that, on a board with no COM1 the status port read all ones: writes went nowhere, and
nowhere. the prompt was told a key was waiting on every poll and read `0xFF` for ever.
### 1.3 Raspberry Pi 5 ### 1.3 Raspberry Pi 5
| # | What | Where | Effect | | # | What | Where | Effect |
|---|---|---|---| |---|---|---|---|
| R1 | **Blocks first boot.** Every character the console prints is also written to a PL011 UART at `0x09000000`, QEMU's address, after waiting on a flag read from `0x09000018`. On a Pi 5 that is ordinary RAM. | `kernel/src/hal/console.c:84-93`, `:223` | Each character overwrites a word of RAM at 144 MiB; and if the word at `0x09000018` has bit 5 set, the first character waits for ever. `rpi5_uart_base()` finds the real UART and nothing calls it. | | R1 | **Fixed 2026-10-13.** Every character the console printed was also written to a PL011 at `0x09000000`, QEMU's address, which on a Pi 5 is ordinary RAM. The console now finds its serial line from the devicetree's `/chosen/stdout-path`: on the Pi 5 that is `serial10`, the debug connector, a PL011 at `0x107d001000`. | `kernel/src/hal/console.c`, `kernel/src/hal/fdt_uart.c`; `tools/fdt_uart_test.c` reads the board's real devicetree | Checked off the board only. The first run is the board's. |
| R2 | Entry is at EL2 with the MMU off. Nothing on this path has ever run. | `boards/raspi/README.md`; `FABRIC-3.md` IV.3 | Unknown. | | R2 | Entry is at EL2 with the MMU off. Nothing on this path has ever run. | `boards/raspi/README.md`; `FABRIC-3.md` IV.3 | Unknown. |
| R3 | Two details of the framebuffer request are unverified: the size field of the allocate tag, and whether the address returned needs its top bits masked. The mode is fixed at 1920x1080x32. | `FABRIC-3.md` IV.3 item 3 | No picture, or a wrong one. With R1 unfixed there is then no channel at all. | | R3 | Two details of the framebuffer request are unverified: the size field of the allocate tag, and whether the address returned needs its top bits masked. The mode is fixed at 1920x1080x32. | `FABRIC-3.md` IV.3 item 3 | No picture, or a wrong one. With R1 unfixed there is then no channel at all. |
| R4 | USB, Ethernet and GPIO are behind the RP1 chip on PCIe, which needs a Broadcom-specific driver that is not written. | `FABRIC-3.md` IV.3 item 5 | No USB on the Pi 5 whatever is done about A1. | | R4 | USB, Ethernet and GPIO are behind the RP1 chip on PCIe, which needs a Broadcom-specific driver that is not written. | `FABRIC-3.md` IV.3 item 5 | No USB on the Pi 5 whatever is done about A1. |
@@ -58,7 +58,7 @@ nowhere.
| # | What | Where | Effect | | # | What | Where | Effect |
|---|---|---|---| |---|---|---|---|
| M1 | **Likely blocks first boot; to be confirmed against the board's devicetree.** The console writes to a 16550 UART at `0x10000000` with its registers one byte apart, and waits on the status register at offset 5. The JH7110's UART is at that address, but as I recall its devicetree gives `reg-shift = <2>`: registers four bytes apart, status at offset `0x14`. | `kernel/src/hal/console.c:99-109` | If so, the first character waits for ever on a byte that is not the status register. QEMU's UART is a byte-spaced 16550, so the QEMU boot through U-Boot did not show this. | | M1 | **Fixed 2026-10-13.** The console, and the UEFI loader before it, wrote to a 16550 at `0x10000000` with registers one byte apart. The JH7110's UART is at that address with `reg-shift = <2>` and `reg-io-width = <4>` (mainline `jh7110.dtsi`): the loader would have waited for ever on its first character. Both now take the layout from the devicetree. | `kernel/src/hal/console.c`, `kernel/src/boot/uefi_loader.c` | Rehearsed in QEMU through U-Boot, which hands over a devicetree: "serial line is a 16550 at 0x10000000 (named by the devicetree)". QEMU's UART is byte-spaced, so the four-byte layout itself is checked by the host test only. |
| M2 | The display depends on U-Boot offering a framebuffer to the EFI loader. | `boards/milkv/README.md` | Unknown. With M1 unfixed there is then no channel at all. | | M2 | The display depends on U-Boot offering a framebuffer to the EFI loader. | `boards/milkv/README.md` | Unknown. With M1 unfixed there is then no channel at all. |
| M3 | USB is behind the JH7110's PCIe controller; whether that is plain ECAM has not been looked up. | `FABRIC-3.md` V.3 item 5 | No USB until it is. | | M3 | USB is behind the JH7110's PCIe controller; whether that is plain ECAM has not been looked up. | `FABRIC-3.md` V.3 item 5 | No USB until it is. |
| M4 | `FABRIC-3.md` V describes a native entry at `0x40000000`; the image built today is the UEFI loader run by U-Boot's `bootefi`. The README here is the newer and is what the build does. | `boards/milkv/README.md` | None; noted so the two are not confused on the day. | | M4 | `FABRIC-3.md` V describes a native entry at `0x40000000`; the image built today is the UEFI loader run by U-Boot's `bootefi`. The README here is the newer and is what the build does. | `boards/milkv/README.md` | None; noted so the two are not confused on the day. |
@@ -71,12 +71,22 @@ port, `kernel/src/arch/armv7` (entry, MMU, GIC, private timer); and the four fil
`__int128` or have no ARMv7 branch (`boards/zynq7020/README.md` names them). It is the host for `__int128` or have no ARMv7 branch (`boards/zynq7020/README.md` names them). It is the host for
the v4 mesh, so it also waits on A6. the v4 mesh, so it also waits on A6.
### 1.6 What I would close before the bench day ### 1.6 Found in rehearsal, 2026-10-13: the bring-up build floods its prompt
Built with `EMERGENCY_CONSOLE_ENABLED=1` and booted with nothing typed, the Mars image prints
`ok> ok` without end: 1.4 million times in 200 seconds, through U-Boot in QEMU. Under the
usual QEMU firmware the first prompt waits, a typed line is answered, and then the same flood
begins. The image built from the commit before the console change does the same, so it is
not the serial line. **Not found yet, and it blocks a usable prompt on every board's bring-up
build.** The production build, which waits for a login, does not show it: the three-ISA
acceptance boots reach one `zuse@Hera` prompt and stop there.
### 1.7 What I would close before the bench day
In this order, each as a design for approval first: In this order, each as a design for approval first:
1. R1 and M1: a console that finds its UART from the devicetree, and does nothing when there 1. ~~R1 and M1: a console that finds its UART from the devicetree, and does nothing when
is none. Without them the Pi 5 and the Mars may show nothing at all. there is none.~~ Done 2026-10-13. Then 1.6, the flood.
2. A6: the v4 switch in the board images. The v4 node needs only a console to reach a prompt; 2. A6: the v4 switch in the board images. The v4 node needs only a console to reach a prompt;
it has no login, no fleet and no disk to wait for, so it is the shorter road to a first it has no login, no fleet and no disk to wait for, so it is the shorter road to a first
prompt on each board. prompt on each board.
@@ -123,10 +133,12 @@ make boot_image TARGET=RASPI EMERGENCY_CONSOLE_ENABLED=1
make boot_image TARGET=MILKV EMERGENCY_CONSOLE_ENABLED=1 make boot_image TARGET=MILKV EMERGENCY_CONSOLE_ENABLED=1
``` ```
- [ ] **`make clean TARGET=<board>` first.** The switch is compiled in, and objects built
without it are not rebuilt when it changes: an image built over old objects boots
headless (seen 2026-10-13).
- [ ] Each ends with its image in `build/boards/<board>/`. Record `sha256sum` of each. - [ ] Each ends with its image in `build/boards/<board>/`. Record `sha256sum` of each.
- [ ] Confirm the switch took: the image booted in QEMU (3.2) shows a prompt with no - [ ] Confirm the switch took: the image booted in QEMU (3.2) shows a prompt with no
thumbdrive attached. That the root `Makefile` passes the setting down has not been thumbdrive attached. After a clean build it does (tried on `MILKV`).
tried.
- [ ] The Pi 5 build fetches the board's DTB once from the pinned firmware release; with no - [ ] The Pi 5 build fetches the board's DTB once from the pinned firmware release; with no
network, pass `RPI5_DTB=/path/to/bcm2712-rpi-5-b.dtb`. network, pass `RPI5_DTB=/path/to/bcm2712-rpi-5-b.dtb`.
- [ ] Put back `v3/include/version.h` and `capsules/BLOCK_MAP.md` afterwards: the build - [ ] Put back `v3/include/version.h` and `capsules/BLOCK_MAP.md` afterwards: the build
+14 -1
View File
@@ -57,12 +57,25 @@
#include <stddef.h> #include <stddef.h>
#include "uefi.h" #include "uefi.h"
/**
* Find the board's serial console line. Must be called before anything is
* printed: until it is, and on a board with no such line, the console writes
* to no serial port and reads from none. On a PC it is COM1, if a UART
* answers there; elsewhere it is the UART the devicetree names as the
* console (/chosen/stdout-path); with no devicetree at all it is the UART of
* QEMU's virt machine. @p dtb may be NULL.
*/
void console_attach_serial(const void *dtb);
/** /**
* Initialize serial console (UART). * Initialize serial console (UART).
* Must be called once during early kernel boot. * Must be called once during early kernel boot, after console_attach_serial().
*/ */
void console_init(void); void console_init(void);
/** Print one line saying which serial line the console found, or that there is none. */
void console_describe_serial(void);
/** /**
* Initialize the framebuffer VT100 terminal. * Initialize the framebuffer VT100 terminal.
* Call after UEFI boot services have been exited and the GOP framebuffer * Call after UEFI boot services have been exited and the GOP framebuffer
+33
View File
@@ -167,4 +167,37 @@ const void* fdt_next_child_node(const void* fdt, const void* parent,
const void* fdt_find_prop_in_node(const void* fdt, const void* node, const void* fdt_find_prop_in_node(const void* fdt, const void* node,
const char* name, uint32_t* len_out); const char* name, uint32_t* len_out);
/**
* @brief Is the structure block whole?
* @c fdt_valid() checks the header; this checks also that the structure
* block ends with its end token, as a blob that has not been cut short
* does. A reader that takes a property's absence to mean its default
* (a UART's "reg-shift") must not be handed half a tree.
* @return 1 if @p fdt is valid and its structure block ends with FDT_END.
*/
int fdt_complete(const void* fdt);
/**
* @brief The root node.
* @param fdt Blob; checked with @c fdt_valid().
* @return Handle to the root node, for @c fdt_find_child() and
* @c fdt_find_prop_in_node(), or NULL.
*/
const void* fdt_root_node(const void* fdt);
/**
* @brief Find a direct child of a node by its whole name.
* The name is matched exactly, unit address included ("serial@10000000"
* is not "serial"), and is given with its length so that one component of
* a path can be passed without copying it. Only @p parent's own children
* are looked at, never their descendants.
* @param fdt Blob; checked with @c fdt_valid().
* @param parent Node handle; NULL is safe and returns NULL.
* @param name The child's name; need not be NUL-terminated.
* @param name_len Its length in bytes.
* @return Handle to the child, or NULL if @p parent has none of that name.
*/
const void* fdt_find_child(const void* fdt, const void* parent,
const char* name, uint32_t name_len);
#endif /* STARKERNEL_FDT_H */ #endif /* STARKERNEL_FDT_H */
+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 */
+54 -6
View File
@@ -53,6 +53,8 @@
#include "elf64.h" #include "elf64.h"
#include "starkernel/cmdline.h" #include "starkernel/cmdline.h"
#include "starkernel/boot_info_offsets.h" #include "starkernel/boot_info_offsets.h"
#include "starkernel/fdt.h"
#include "starkernel/fdt_uart.h"
#include <stddef.h> #include <stddef.h>
/* Verify BootInfo field offsets match boot_info_offsets.h (BOOT_INFO_OFFSETS) */ /* Verify BootInfo field offsets match boot_info_offsets.h (BOOT_INFO_OFFSETS) */
@@ -242,20 +244,56 @@ static void raw_serial_puts(const char *s)
*/ */
#define UART_MMIO_BASE 0x10000000UL #define UART_MMIO_BASE 0x10000000UL
static inline void raw_mmio_outb(uint64_t addr, uint8_t val) /* That is QEMU's. A real board's console UART is the one its devicetree
* names, and may be laid out otherwise: the Milk-V Mars's is at the same
* address with its registers four bytes apart, and the byte-spaced write
* below would wait for ever on what is not its status register
* (boards/BRINGUP.md). So the line is found first, raw_serial_find(), by
* the lookup the kernel's console uses (starkernel/fdt_uart.h); until then,
* and on a board whose devicetree names no line this can use, RAW_LOG()
* writes nothing. A line that does not come ready is given up. */
#define RAW_SERIAL_THRE_MAX_SPIN 4000000u
static int raw_serial_ready = 0;
static uint64_t raw_uart_base = UART_MMIO_BASE;
static unsigned raw_uart_shift = 0;
static unsigned raw_uart_width = 1;
static inline uint32_t raw_uart_read(unsigned reg)
{ {
*(volatile uint8_t *)addr = val; uint64_t addr = raw_uart_base + ((uint64_t)reg << raw_uart_shift);
return raw_uart_width == 4 ? *(volatile uint32_t *)addr : *(volatile uint8_t *)addr;
} }
static inline uint8_t raw_mmio_inb(uint64_t addr) static inline void raw_uart_write(unsigned reg, uint8_t val)
{ {
return *(volatile uint8_t *)addr; uint64_t addr = raw_uart_base + ((uint64_t)reg << raw_uart_shift);
if (raw_uart_width == 4) *(volatile uint32_t *)addr = val;
else *(volatile uint8_t *)addr = val;
}
/* `dtb`: the devicetree the firmware handed over, or NULL if it gave none. */
static void raw_serial_setup(const void *dtb)
{
fdt_uart_t u;
raw_serial_ready = 0;
if (fdt_console_uart(dtb, &u)) {
if (u.kind != FDT_UART_16550) return;
raw_uart_base = u.base; raw_uart_shift = u.reg_shift; raw_uart_width = u.io_width;
raw_serial_ready = 1;
} else if (!fdt_valid(dtb)) {
raw_uart_base = UART_MMIO_BASE; raw_uart_shift = 0; raw_uart_width = 1; /* no devicetree: QEMU's */
raw_serial_ready = 1;
}
} }
static void raw_serial_putc(char c) static void raw_serial_putc(char c)
{ {
while ((raw_mmio_inb(UART_MMIO_BASE + 5) & 0x20) == 0) { } unsigned int spin = 0;
raw_mmio_outb(UART_MMIO_BASE + 0, (uint8_t)c); if (!raw_serial_ready) return;
while ((raw_uart_read(5) & 0x20) == 0) {
if (++spin >= RAW_SERIAL_THRE_MAX_SPIN) { raw_serial_ready = 0; return; }
}
raw_uart_write(0, (uint8_t)c);
} }
static void raw_serial_puts(const char *s) static void raw_serial_puts(const char *s)
@@ -691,6 +729,16 @@ EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable
raw_serial_init(); raw_serial_init();
RAW_LOG("RAW SERIAL UP\n"); RAW_LOG("RAW SERIAL UP\n");
debug_checkpoint(SystemTable, 2, L"Serial (COM1) initialized"); debug_checkpoint(SystemTable, 2, L"Serial (COM1) initialized");
#elif defined(__riscv) || defined(ARCH_RISCV64)
{
/* the serial line, before the first RAW_LOG(): from the devicetree, if the firmware gave one */
const void *early_dtb = NULL;
EFI_CONFIGURATION_TABLE *tables = (EFI_CONFIGURATION_TABLE *)SystemTable->ConfigurationTable;
UINTN t;
for (t = 0; tables && t < SystemTable->NumberOfTableEntries; ++t)
if (guid_equals(&tables[t].VendorGuid, &EFI_DTB_TABLE_GUID)) { early_dtb = tables[t].VendorTable; break; }
raw_serial_setup(early_dtb);
}
#endif #endif
#ifndef MONOLITHIC_BUILD #ifndef MONOLITHIC_BUILD
+163 -124
View File
@@ -50,22 +50,47 @@
#include "vt100.h" #include "vt100.h"
#include "arch.h" #include "arch.h"
#include "starkernel/fdt.h"
#include "starkernel/fdt_uart.h"
/* THE SERIAL LINE. A board has one serial line that is its console, the
* lowest common denominator: everything printed goes out on it and what is
* typed on it comes in, beside the screen and the keyboard where a board
* has those. Which line it is, is found once, by console_attach_serial():
* on a PC, COM1 if a UART answers there; elsewhere the UART the board's
* devicetree names as its console (fdt_uart.h), which the firmware has set
* up; and with no devicetree at all -- QEMU under UEFI firmware -- the UART
* of QEMU's virt machine. Until it is found, and on a board that has none,
* there is no line: nothing is written anywhere and nothing is read.
*
* Before this the addresses were QEMU's, fixed. On a Raspberry Pi 5 the
* aarch64 one is ordinary RAM, and on a Milk-V Mars the riscv64 one is the
* board's UART with its registers laid out otherwise (boards/BRINGUP.md). */
enum { SERIAL_NONE = 0, SERIAL_PORT16550, SERIAL_MMIO16550, SERIAL_PL011 };
static int g_serial_kind; /* SERIAL_NONE until console_attach_serial() */
static uintptr_t g_serial_base; /* register 0: an I/O port, or an address */
static unsigned g_serial_shift; /* register n is at base + (n << shift) */
static unsigned g_serial_width; /* bytes at a time, by memory: 1 or 4 */
static int g_serial_from_dt; /* the devicetree named it */
/* How long to wait for the line to take a character before giving the line
* up for dead. At 115200 a character takes 87 microseconds; this is many
* thousand times that on any of the boards. A console that cannot print
* must not stop the boot. */
#define SERIAL_TX_MAX_SPIN 4000000u
/* 16550: transmit and receive are register 0, line status is register 5 */
#define UART16550_LSR 5u
#define UART16550_LSR_DR (1u << 0)
#define UART16550_LSR_THRE (1u << 5)
/* PL011: data is register 0, flags are register 6 (offset 0x18) */
#define PL011_FR 6u
#define PL011_FR_RXFE (1u << 4)
#define PL011_FR_TXFF (1u << 5)
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
#define SERIAL_SUPPORTED 1 #define COM1_PORT 0x3F8
/* UART 16550 I/O ports (COM1) */
#define SERIAL_PORT_BASE 0x3F8
#define SERIAL_DATA_PORT (SERIAL_PORT_BASE + 0)
#define SERIAL_INT_ENABLE_PORT (SERIAL_PORT_BASE + 1)
#define SERIAL_FIFO_CTRL_PORT (SERIAL_PORT_BASE + 2)
#define SERIAL_LINE_CTRL_PORT (SERIAL_PORT_BASE + 3)
#define SERIAL_MODEM_CTRL_PORT (SERIAL_PORT_BASE + 4)
#define SERIAL_LINE_STATUS_PORT (SERIAL_PORT_BASE + 5)
/* Line Status Register bits */
#define SERIAL_LSR_DATA_READY (1 << 0)
#define SERIAL_LSR_THR_EMPTY (1 << 5)
static inline void outb(uint16_t port, uint8_t val) { static inline void outb(uint16_t port, uint8_t val) {
__asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
@@ -76,89 +101,137 @@ static inline uint8_t inb(uint16_t port) {
__asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port)); __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
return ret; return ret;
} }
#elif defined(__aarch64__)
#define SERIAL_SUPPORTED 1
/* PL011 UART — QEMU virt machine UART0 at 0x09000000 */
#define PL011_BASE ((volatile unsigned int *)0x09000000UL)
#define PL011_DR (PL011_BASE + 0) /* Data Register (offset 0x000) */
#define PL011_FR (PL011_BASE + 6) /* Flag Register (offset 0x018) */
#define PL011_FR_TXFF (1u << 5) /* TX FIFO full */
static inline void pl011_putc(char c)
{
while (*PL011_FR & PL011_FR_TXFF) { }
*PL011_DR = (unsigned int)(unsigned char)c;
}
#elif defined(__riscv)
#define SERIAL_SUPPORTED 1
/* NS16550 UART — QEMU virt machine UART0 at 0x10000000 (MMIO, byte-wide) */
#define NS16550_BASE ((volatile unsigned char *)0x10000000UL)
#define NS16550_THR (NS16550_BASE + 0) /* Transmit Holding Register */
#define NS16550_LSR (NS16550_BASE + 5) /* Line Status Register */
#define NS16550_LSR_THRE (1u << 5) /* Transmit Holding Register Empty */
#define NS16550_LSR_DR (1u << 0) /* Data Ready */
static inline void ns16550_putc(char c)
{
while (!(*NS16550_LSR & NS16550_LSR_THRE)) { }
*NS16550_THR = (unsigned char)c;
}
#else
#define SERIAL_SUPPORTED 0
/* Stubs for other architectures */
static inline void outb(uint16_t port, uint8_t val) {
(void)port;
(void)val;
}
static inline uint8_t inb(uint16_t port) {
(void)port;
return 0;
}
#endif #endif
static uint32_t serial_reg_read(unsigned n)
{
#if defined(__x86_64__) || defined(__i386__)
if (g_serial_kind == SERIAL_PORT16550) return inb((uint16_t)(g_serial_base + n));
#endif
if (g_serial_width == 4) return *(volatile uint32_t *)(g_serial_base + ((uintptr_t)n << g_serial_shift));
return *(volatile uint8_t *)(g_serial_base + ((uintptr_t)n << g_serial_shift));
}
static void serial_reg_write(unsigned n, uint32_t v)
{
#if defined(__x86_64__) || defined(__i386__)
if (g_serial_kind == SERIAL_PORT16550) { outb((uint16_t)(g_serial_base + n), (uint8_t)v); return; }
#endif
if (g_serial_width == 4) *(volatile uint32_t *)(g_serial_base + ((uintptr_t)n << g_serial_shift)) = v;
else *(volatile uint8_t *)(g_serial_base + ((uintptr_t)n << g_serial_shift)) = (uint8_t)v;
}
static int serial_tx_ready(void)
{
if (g_serial_kind == SERIAL_PL011) return !(serial_reg_read(PL011_FR) & PL011_FR_TXFF);
return (serial_reg_read(UART16550_LSR) & UART16550_LSR_THRE) != 0;
}
static int serial_rx_ready(void)
{
if (g_serial_kind == SERIAL_NONE) return 0;
if (g_serial_kind == SERIAL_PL011) return !(serial_reg_read(PL011_FR) & PL011_FR_RXFE);
return (serial_reg_read(UART16550_LSR) & UART16550_LSR_DR) != 0;
}
/* One character out on the line, if there is one. A line that does not
* come ready in SERIAL_TX_MAX_SPIN tries is given up: from then on there is
* no line, and nothing waits on it again. */
static void serial_putc(char c)
{
unsigned spin = 0;
if (g_serial_kind == SERIAL_NONE) return;
while (!serial_tx_ready()) {
if (++spin >= SERIAL_TX_MAX_SPIN) { g_serial_kind = SERIAL_NONE; return; }
arch_relax();
}
serial_reg_write(0, (uint8_t)c);
}
void console_attach_serial(const void *dtb)
{
g_serial_kind = SERIAL_NONE;
g_serial_from_dt = 0;
#if defined(__x86_64__) || defined(__i386__)
(void)dtb;
/* COM1, if a UART answers there: its scratch register gives back what
* was written to it. A port no UART owns reads all ones, which would
* otherwise look like a character waiting, for ever. The loader makes
* the same probe (boot/uefi_loader.c, raw_serial_init). */
outb(COM1_PORT + 7, 0x5A);
if (inb(COM1_PORT + 7) != 0x5A) return;
outb(COM1_PORT + 7, 0xA5);
if (inb(COM1_PORT + 7) != 0xA5) return;
g_serial_base = COM1_PORT; g_serial_shift = 0; g_serial_width = 1;
g_serial_kind = SERIAL_PORT16550;
#elif defined(__aarch64__) || defined(__riscv)
{
fdt_uart_t u;
if (fdt_console_uart(dtb, &u)) {
g_serial_base = (uintptr_t)u.base; g_serial_shift = u.reg_shift; g_serial_width = u.io_width;
g_serial_kind = (u.kind == FDT_UART_PL011) ? SERIAL_PL011 : SERIAL_MMIO16550;
g_serial_from_dt = 1;
return;
}
/* A devicetree that names no console this can use: no line. It is
* a real board's, and QEMU's addresses are not to be tried on it. */
if (fdt_valid(dtb)) return;
}
/* No devicetree at all: QEMU's virt machine under UEFI firmware, which
* has set its UART up. */
#if defined(__aarch64__)
g_serial_base = 0x09000000UL; g_serial_shift = 2; g_serial_width = 4;
g_serial_kind = SERIAL_PL011;
#else
g_serial_base = 0x10000000UL; g_serial_shift = 0; g_serial_width = 1;
g_serial_kind = SERIAL_MMIO16550;
#endif
#else
(void)dtb;
#endif
}
/** /**
* Initialize serial console * Initialize serial console
* x86: programs 16550 UART. * x86: programs the 16550 at COM1, if console_attach_serial() found one.
* aarch64: PL011 is already initialized by UEFI firmware on QEMU virt. * aarch64, riscv64: the line was set up by the firmware; nothing to do.
*/ */
void console_init(void) { void console_init(void) {
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
outb(SERIAL_INT_ENABLE_PORT, 0x00); if (g_serial_kind != SERIAL_PORT16550) return;
outb(SERIAL_LINE_CTRL_PORT, 0x80); outb(COM1_PORT + 1, 0x00);
outb(SERIAL_DATA_PORT, 0x01); outb(COM1_PORT + 3, 0x80);
outb(SERIAL_INT_ENABLE_PORT, 0x00); outb(COM1_PORT + 0, 0x01);
outb(SERIAL_LINE_CTRL_PORT, 0x03); outb(COM1_PORT + 1, 0x00);
outb(SERIAL_FIFO_CTRL_PORT, 0xC7); outb(COM1_PORT + 3, 0x03);
outb(SERIAL_MODEM_CTRL_PORT, 0x0B); outb(COM1_PORT + 2, 0xC7);
outb(SERIAL_MODEM_CTRL_PORT, 0x1E); outb(COM1_PORT + 4, 0x0B);
outb(SERIAL_DATA_PORT, 0xAE); outb(COM1_PORT + 4, 0x1E);
if (inb(SERIAL_DATA_PORT) != 0xAE) { outb(COM1_PORT + 0, 0xAE);
if (inb(COM1_PORT + 0) != 0xAE) {
g_serial_kind = SERIAL_NONE; /* it does not loop back: not a UART to be trusted */
return; return;
} }
outb(SERIAL_MODEM_CTRL_PORT, 0x0F); outb(COM1_PORT + 4, 0x0F);
#elif defined(__aarch64__)
/* PL011 already enabled by AAVMF; nothing to do */
(void)0;
#elif defined(__riscv)
/* NS16550 already enabled by UEFI firmware on QEMU virt; nothing to do */
(void)0;
#else
(void)SERIAL_SUPPORTED;
#endif #endif
} }
#if defined(__x86_64__) || defined(__i386__) void console_describe_serial(void)
static int serial_transmit_empty(void) { {
return inb(SERIAL_LINE_STATUS_PORT) & SERIAL_LSR_THR_EMPTY; static const char hex[] = "0123456789abcdef";
char buf[19];
int i;
uint64_t v = (uint64_t)g_serial_base;
if (g_serial_kind == SERIAL_NONE) { console_println("Console: no serial line"); return; }
buf[0] = '0'; buf[1] = 'x';
for (i = 0; i < 16; i++) buf[2 + i] = hex[(v >> (60 - 4 * i)) & 0xF];
buf[18] = '\0';
console_puts("Console: serial line is a ");
console_puts(g_serial_kind == SERIAL_PL011 ? "PL011" : "16550");
console_puts(g_serial_kind == SERIAL_PORT16550 ? " at port " : " at ");
console_puts(buf);
console_println(g_serial_kind == SERIAL_PORT16550 ? " (COM1, probed)" : g_serial_from_dt ? " (named by the devicetree)" : " (no devicetree: QEMU's)");
} }
#endif
/* Active VM name for [Name] line prefix; NULL = no prefix. /* Active VM name for [Name] line prefix; NULL = no prefix.
* *
@@ -219,22 +292,8 @@ void console_save_vm_name(char *out, size_t cap) {
/* Raw single-character write — no prefix logic, called by emit_prefix() */ /* Raw single-character write — no prefix logic, called by emit_prefix() */
static void raw_putc(char c) { static void raw_putc(char c) {
#if defined(__aarch64__) if (c == '\n') serial_putc('\r');
if (c == '\n') pl011_putc('\r'); serial_putc(c);
pl011_putc(c);
#elif defined(__riscv)
if (c == '\n') ns16550_putc('\r');
ns16550_putc(c);
#elif defined(__x86_64__) || defined(__i386__)
if (c == '\n') {
while (!serial_transmit_empty()) { arch_relax(); }
outb(SERIAL_DATA_PORT, '\r');
}
while (!serial_transmit_empty()) { arch_relax(); }
outb(SERIAL_DATA_PORT, (uint8_t)c);
#else
(void)c;
#endif
} }
/* Emit "[VMName] " to both serial (raw_putc) and, when available, the /* Emit "[VMName] " to both serial (raw_putc) and, when available, the
@@ -372,17 +431,7 @@ void console_println(const char *s) {
* Check if data is available to read * Check if data is available to read
*/ */
int console_poll(void) { int console_poll(void) {
#if defined(__x86_64__) || defined(__i386__) return serial_rx_ready();
return inb(SERIAL_LINE_STATUS_PORT) & SERIAL_LSR_DATA_READY;
#elif defined(__aarch64__)
/* PL011_FR bit 4 = RXFE (RX FIFO empty); data available when RXFE == 0 */
#define PL011_FR_RXFE (1u << 4)
return !(*PL011_FR & PL011_FR_RXFE);
#elif defined(__riscv)
return *NS16550_LSR & NS16550_LSR_DR;
#else
return 0;
#endif
} }
/** /**
@@ -447,16 +496,6 @@ void console_fb_erase_cursor(void)
* Returns -1 if no character available * Returns -1 if no character available
*/ */
int console_getc(void) { int console_getc(void) {
#if defined(__x86_64__) || defined(__i386__) if (!serial_rx_ready()) return -1;
if (!console_poll()) return -1; return (int)(serial_reg_read(0) & 0xFF);
return (int)(unsigned char)inb(SERIAL_DATA_PORT);
#elif defined(__aarch64__)
if (!console_poll()) return -1;
return (int)(*PL011_DR & 0xFF);
#elif defined(__riscv)
if (!console_poll()) return -1;
return (int)(*NS16550_THR & 0xFF);
#else
return -1;
#endif
} }
+100
View File
@@ -506,6 +506,106 @@ const void* fdt_next_child_node(const void* fdt, const void* parent,
return (void*)0; return (void*)0;
} }
int fdt_complete(const void* fdt)
{
const fdt_header_t* h = (const fdt_header_t*)fdt;
uint32_t size_struct;
if (!fdt_valid(fdt)) return 0;
size_struct = be32(&h->size_dt_struct);
if (size_struct < 4 || (size_struct & 3u)) return 0;
return be32((const unsigned char*)fdt + be32(&h->off_dt_struct) + size_struct - 4) == FDT_END;
}
const void* fdt_root_node(const void* fdt)
{
const fdt_header_t* h = (const fdt_header_t*)fdt;
const unsigned char *p, *end;
if (!fdt_valid(fdt)) return (void*)0;
p = (const unsigned char*)fdt + be32(&h->off_dt_struct);
end = p + be32(&h->size_dt_struct);
while (p + 4 <= end)
{
uint32_t token = be32(p);
p += 4;
if (token == FDT_NOP) continue;
if (token != FDT_BEGIN_NODE) break;
{
const unsigned char* q = p;
while (q < end && *q) q++;
if (q >= end) break;
return (const void*)(((uintptr_t)(q + 1) + 3u) & ~(uintptr_t)3u);
}
}
return (void*)0;
}
const void* fdt_find_child(const void* fdt, const void* parent,
const char* name, uint32_t name_len)
{
const fdt_header_t* h = (const fdt_header_t*)fdt;
const unsigned char *end, *p;
if (!fdt_valid(fdt) || !parent || !name) return (void*)0;
end = (const unsigned char*)fdt + be32(&h->off_dt_struct) + be32(&h->size_dt_struct);
p = (const unsigned char*)parent;
/* parent's own properties first, then its children one after another:
* each child that is not the one wanted is skipped whole. */
while (p + 4 <= end)
{
uint32_t token = be32(p);
p += 4;
if (token == FDT_BEGIN_NODE)
{
const unsigned char* q = p;
const unsigned char* body;
uint32_t i;
while (q < end && *q) q++;
if (q >= end) return (void*)0;
body = (const unsigned char*)(((uintptr_t)(q + 1) + 3u) & ~(uintptr_t)3u);
/* the whole name, unit address and all, and nothing more */
if ((uint32_t)(q - p) == name_len)
{
for (i = 0; i < name_len && p[i] == (unsigned char)name[i]; i++) { }
if (i == name_len) return (const void*)body;
}
p = skip_node_body(body, end);
}
else if (token == FDT_PROP)
{
uint32_t len;
if (p + 8 > end) break;
len = be32(p);
p += 8;
if (len > (uint32_t)(end - p)) break;
p = (const unsigned char*)(((uintptr_t)(p + len) + 3u) & ~(uintptr_t)3u);
}
else if (token == FDT_NOP)
{
/* no payload */
}
else
{
/* FDT_END_NODE (parent closing), FDT_END, or unknown */
return (void*)0;
}
}
return (void*)0;
}
const void* fdt_find_prop_in_node(const void* fdt, const void* node, const void* fdt_find_prop_in_node(const void* fdt, const void* node,
const char* name, uint32_t* len_out) const char* name, uint32_t* len_out)
{ {
+167
View File
@@ -0,0 +1,167 @@
/*
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.c - the console's serial line, as a board's devicetree names it.
* See fdt_uart.h.
*
* The node is reached a component of its path at a time from the root, so
* that each bus on the way is in hand: a node's "reg" is an address on its
* parent's bus, and is carried up to the CPU's own addresses through the
* "ranges" of every bus above it (DT spec 2.3.8). A bus with an empty
* "ranges" passes addresses on unchanged; one with none at all has no
* mapping, and the line is not found.
*/
#include "starkernel/fdt_uart.h"
#include "starkernel/fdt.h"
#define PATH_DEPTH 8 /* nodes from the root to the UART, the root counted */
static uint32_t be32(const unsigned char* b)
{
return ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | (uint32_t)b[3];
}
/* a property that is one cell, or `otherwise` */
static uint32_t cell_prop(const void* fdt, const void* node, const char* name, uint32_t otherwise)
{
uint32_t len;
const void* v = fdt_find_prop_in_node(fdt, node, name, &len);
return (v && len == 4) ? be32((const unsigned char*)v) : otherwise;
}
/* one or two cells as a number; 0 cells is 0 */
static uint64_t cells(const unsigned char* p, uint32_t n)
{
uint64_t v = 0;
while (n--) { v = (v << 32) | be32(p); p += 4; }
return v;
}
/* is `want` one of the strings of a "compatible" list? */
static int compatible_has(const char* list, uint32_t len, const char* want)
{
uint32_t i = 0;
while (i < len)
{
uint32_t j = 0;
while (i + j < len && list[i + j] && want[j] && list[i + j] == want[j]) j++;
if (i + j < len && !list[i + j] && !want[j]) return 1;
while (i < len && list[i]) i++;
i++;
}
return 0;
}
int fdt_console_uart(const void* fdt, fdt_uart_t* out)
{
const void* node[PATH_DEPTH];
const void* root;
const char *path, *v;
uint32_t len, path_len, depth, i, ac, sc;
uint64_t addr;
if (!out) return 0;
out->kind = FDT_UART_NONE; out->base = 0; out->reg_shift = 0; out->io_width = 1;
if (!fdt_complete(fdt)) return 0; /* an absent property means its default: not in half a tree */
root = fdt_root_node(fdt);
if (!root) return 0;
/* /chosen/stdout-path: a path, or an alias, and then perhaps ":115200n8" */
path = (const char*)fdt_find_prop_in_node(fdt, fdt_find_child(fdt, root, "chosen", 6), "stdout-path", &len);
if (!path || len == 0) return 0;
for (path_len = 0; path_len < len && path[path_len] && path[path_len] != ':'; path_len++) { }
if (path_len == 0) return 0;
if (path[0] != '/')
{
char alias[32];
if (path_len >= sizeof alias) return 0;
for (i = 0; i < path_len; i++) alias[i] = path[i];
alias[path_len] = '\0';
path = (const char*)fdt_find_prop_in_node(fdt, fdt_find_child(fdt, root, "aliases", 7), alias, &len);
if (!path || len == 0 || path[0] != '/') return 0;
for (path_len = 0; path_len < len && path[path_len]; path_len++) { }
}
/* down the path, keeping every node on the way */
node[0] = root;
depth = 1;
i = 0;
while (i < path_len)
{
uint32_t from;
while (i < path_len && path[i] == '/') i++;
from = i;
while (i < path_len && path[i] != '/') i++;
if (i == from) break;
if (depth == PATH_DEPTH) return 0;
node[depth] = fdt_find_child(fdt, node[depth - 1], path + from, i - from);
if (!node[depth]) return 0;
depth++;
}
if (depth < 2) return 0; /* the root is not a UART */
v = (const char*)fdt_find_prop_in_node(fdt, node[depth - 1], "status", &len);
if (v && !compatible_has(v, len, "okay") && !compatible_has(v, len, "ok")) return 0;
/* its address on its parent's bus */
ac = cell_prop(fdt, node[depth - 2], "#address-cells", 2);
sc = cell_prop(fdt, node[depth - 2], "#size-cells", 1);
if (ac < 1 || ac > 2 || sc > 2) return 0;
v = (const char*)fdt_find_prop_in_node(fdt, node[depth - 1], "reg", &len);
if (!v || len < 4 * (ac + sc)) return 0;
addr = cells((const unsigned char*)v, ac);
/* and up through each bus to the CPU's addresses */
for (i = depth - 2; i >= 1; i--)
{
uint32_t child_ac = cell_prop(fdt, node[i], "#address-cells", 2);
uint32_t child_sc = cell_prop(fdt, node[i], "#size-cells", 1);
uint32_t parent_ac = cell_prop(fdt, node[i - 1], "#address-cells", 2);
uint32_t entry, at;
int carried = 0;
v = (const char*)fdt_find_prop_in_node(fdt, node[i], "ranges", &len);
if (!v) return 0; /* no mapping to the bus above */
if (len == 0) continue; /* the same addresses above */
if (child_ac < 1 || child_ac > 2 || parent_ac < 1 || parent_ac > 2 || child_sc < 1 || child_sc > 2) return 0;
entry = 4 * (child_ac + parent_ac + child_sc);
for (at = 0; at + entry <= len; at += entry)
{
const unsigned char* e = (const unsigned char*)v + at;
uint64_t from = cells(e, child_ac);
uint64_t to = cells(e + 4 * child_ac, parent_ac);
uint64_t size = cells(e + 4 * (child_ac + parent_ac), child_sc);
if (addr >= from && addr - from < size) { addr = to + (addr - from); carried = 1; break; }
}
if (!carried) return 0;
}
v = (const char*)fdt_find_prop_in_node(fdt, node[depth - 1], "compatible", &len);
if (!v) return 0;
if (compatible_has(v, len, "arm,pl011") || compatible_has(v, len, "arm,pl011-axi"))
{
out->reg_shift = 0;
out->io_width = 4;
out->base = addr;
out->kind = FDT_UART_PL011;
return 1;
}
if (compatible_has(v, len, "ns16550a") || compatible_has(v, len, "ns16550") || compatible_has(v, len, "snps,dw-apb-uart"))
{
uint32_t shift = cell_prop(fdt, node[depth - 1], "reg-shift", 0);
uint32_t width = cell_prop(fdt, node[depth - 1], "reg-io-width", 1);
if (shift > 2 || (width != 1 && width != 4)) return 0;
out->reg_shift = shift;
out->io_width = width;
out->base = addr;
out->kind = FDT_UART_16550;
return 1;
}
return 0;
}
+5
View File
@@ -394,11 +394,16 @@ void kernel_main(BootInfo *boot_info) {
* will fail silently. arch_early_init() installs a minimal GDT with a * will fail silently. arch_early_init() installs a minimal GDT with a
* proper 64-bit code segment at 0x08 and reloads CS via lretq. * proper 64-bit code segment at 0x08 and reloads CS via lretq.
*/ */
/* The serial line first: arch_early_init() prints on some ISAs, and until
* the line is found the console writes nowhere (hal/console.c). */
console_attach_serial(boot_info ? boot_info->dtb : (void *)0);
arch_early_init(); arch_early_init();
/* M1: Console initialization — serial UART first */ /* M1: Console initialization — serial UART first */
console_init(); console_init();
print_banner(); print_banner();
console_describe_serial();
print_boot_info(boot_info); print_boot_info(boot_info);
/* M2: Physical Memory Manager */ /* M2: Physical Memory Manager */
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,218 @@
[=3hBdsDxe: loading Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
BdsDxe: starting Boot0002 "UEFI QEMU DVD-ROM QM00005 " from PciRoot(0x0)/Pci(0x1F,0x2)/Sata(0x2,0xFFFF,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
RAW SERIAL UP
[CKPT 002] Serial (COM1) initialized
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
EBS...
EBS OK
Calling kernel_main (monolithic)...
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0 amd64
Architecture: amd64
Build: Oct 13 2026 21:11:22
UEFI BootServices: EXITED
Console: serial line is a 16550 at port 0x00000000000003f8 (COM1, probed)
=== StarKernel Boot Information ===
Memory map entries: 131
Total memory: 1023 MB
Usable memory: 966 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 248734
Free pages : 247211
Used pages : 1523
Total MB : 971
Free MB : 965
Used MB : 5
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
IDT installed.
APIC: init...
APIC: IA32_APIC_BASE MSR=0x00000000fee00900
APIC: stale-ISR drain: 0 EOI(s) issued
APIC: initialized (xAPIC MMIO, TPR=0, SIVR=0x1FF, EOI-clear)
APIC: init done
I/O APIC: init...
I/O APIC: base=0xfec00000, gsi_base=0, overrides=5
override: source=00 gsi=2 flags=0x0000
override: source=05 gsi=5 flags=0x000d
override: source=09 gsi=9 flags=0x000d
override: source=0a gsi=10 flags=0x000d
override: source=0b gsi=11 flags=0x000d
i8042: keyboard ACKed enable-scanning
i8042: IRQ1 enabled in controller config byte
I/O APIC: keyboard IRQ1 routed (masked)
Timer: init...
Timer: init start
Timer: PM_TMR_BLK discovered from FADT at port 1544
Timer: VM mode detected (hypervisor present).
Timer: HPET calibration disabled (VM-exit MMIO would poison timing).
Timer: WARNING: invariant TSC not present under hypervisor.
Timer: continuing in RELATIVE mode (no determinism guarantees).
Timer: RDTSCP not present; using RDTSC (less serialized).
Timer: CPUID frequency unavailable; trying PM Timer...
Timer: trust=1 (0=NONE,1=REL,2=ABS), TSC=2100263096 Hz
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 536870872
Free bytes: 536870872
Used bytes: 0
Peak bytes: 0
Heap base addr: 24641536
Heap end addr: 561512448
Heartbeat: init...
APIC Timer: calibrating...
APIC Timer: apic_hz=1008377146, tick_hz=100, initial_count=10083771
APIC Timer: configured (masked, ready to start)
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x3b4a82485b10bfb7 capsule_hash=0x3b4a82485b10bfb7 words=311
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x6b56e15226ee3719
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
PCI: ECAM mapped (amd64)
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 71091804] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
Artemis: virtio-blk attached
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> T{
UNKNOWN WORD: 'T{'
ERROR
ok> RS1
UNKNOWN WORD: 'RS1'
ERROR
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> 5 7 PORT!
No one on that port
ERROR
ok> 1 2 + .
3 ok
ok> 5 AWAIT
7 8 * .
Interrupted
ERROR
ok> 56 ok
ok> : N4 400 0 DO I . LOOP ;
ok
ok> N4
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Stack overflow
ERROR
ok> 3 4 + .
7 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
ok
ok> WORDS
N4 ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFO-STR LOG-WARN-STR
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS Q.- Q.+ Q.SCALE
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D= D0< D0=
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
(ME) NODE-ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
[LITERAL] ['] ' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
ok
ok> DEPTH .
26 ok
ok> : AW 5 AWAIT ;
ok
ok> 1
ok
ok> AW
Stack overflow
ERROR
ok> 4321 .
4321 ok
ok> HERE .
8326 ok
ok> BYE
BYE: cold restart
@@ -0,0 +1,191 @@
UEFI firmware (version 2025.11-3ubuntu7.3 built at 15:40:26 on Sep 23 2026)
[=3hBdsDxe: failed to load Boot0002 "UEFI Misc Device" from PciRoot(0x0)/Pci(0x2,0x0): Not Found
BdsDxe: failed to load Boot0003 "UEFI QEMU QEMU USB HARDDRIVE 1-0000:00:04.0-1" from PciRoot(0x0)/Pci(0x4,0x0)/USB(0x0,0x0): Not Found
BdsDxe: loading Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
BdsDxe: starting Boot0004 "UEFI Misc Device 2" from PciRoot(0x0)/Pci(0x6,0x0)
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
Collecting boot information...
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0 aarch64
Architecture: aarch64
Build: Oct 13 2026 21:14:17
UEFI BootServices: EXITED
Console: serial line is a PL011 at 0x0000000009000000 (no devicetree: QEMU's)
=== StarKernel Boot Information ===
Memory map entries: 108
Total memory: 4093 MB
Usable memory: 4054 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 1039345
Free pages : 1037934
Used pages : 1411
Total MB : 4059
Free MB : 4054
Used MB : 5
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
AArch64: running at EL1
IDT installed.
APIC: init...
PSCI: no DTB -- using HVC (QEMU virt-machine default)
GICv2: no DTB GIC node -- using QEMU virt-machine defaults
GICv2: distributor+CPU interface enabled, PPI 30
APIC: init done
Timer: init...
Timer: AArch64 generic timer initialised.
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 2147483608
Free bytes: 2147483608
Used bytes: 0
Peak bytes: 0
Heap base addr: 1207959552
Heap end addr: 3355443200
Heartbeat: init...
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x3b4a82485b10bfb7 capsule_hash=0x3b4a82485b10bfb7 words=311
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x6b56e15226ee3719
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 1364255] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
Artemis: virtio-blk attached
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> T{
UNKNOWN WORD: 'T{'
ERROR
ok> RS1
UNKNOWN WORD: 'RS1'
ERROR
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> 5 7 PORT!
No one on that port
ERROR
ok> 1 2 + .
3 ok
ok> 5 AWAIT
7 8 * .
Interrupted
ERROR
ok> 56 ok
ok> : N4 400 0 DO I . LOOP ;
ok
ok> N4
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Stack overflow
ERROR
ok> 3 4 + .
7 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
ok
ok> WORDS
N4 ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFO-STR LOG-WARN-STR
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS Q.- Q.+ Q.SCALE
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D= D0< D0=
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
(ME) NODE-ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
[LITERAL] ['] ' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
ok
ok> DEPTH .
26 ok
ok> : AW 5 AWAIT ;
ok
ok> 1
ok
ok> AW
Stack overflow
ERROR
ok> 4321 .
4321 ok
ok> HERE .
8326 ok
ok> BYE
BYE: cold restart
@@ -0,0 +1,261 @@
OpenSBI v1.8
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|
Platform Name : riscv-virtio,qemu
Platform Features : medeleg
Platform HART Count : 1
Platform HART Protection : pmp
Platform IPI Device : aclint-mswi
Platform Timer Device : aclint-mtimer @ 10000000Hz
Platform Console Device : uart8250
Platform HSM Device : ---
Platform PMU Device : ---
Platform Reboot Device : syscon-reboot
Platform Shutdown Device : syscon-poweroff
Platform Suspend Device : ---
Platform CPPC Device : ---
Firmware Base : 0x80000000
Firmware Size : 321 KB
Firmware RW Offset : 0x40000
Firmware RW Size : 65 KB
Firmware Heap Offset : 0x47000
Firmware Heap Size : 37 KB (total), 0 KB (reserved), 12 KB (used), 23 KB (free)
Firmware Scratch Size : 4096 B (total), 1464 B (used), 2632 B (free)
Runtime SBI Version : 3.0
Standard SBI Extensions : ipi,pmu,srst,sse,hsm,rfnc,fwft,time,base,legacy,dbcn,dbtr
Experimental SBI Extensions : none
Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000080040000-0x000000008005ffff M: (F,R,W) S/U: ()
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff M: (F,R,X) S/U: ()
Domain0 Region02 : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
Domain0 Region03 : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region04 : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region05 : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
Domain0 Region06 : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
Domain0 Region07 : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address : 0x0000000020000000
Domain0 Next Arg1 : 0x00000000bfe00000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes
Domain0 SysSuspend : yes
Boot HART ID : 0
Boot HART Domain : root
Boot HART Priv Version : v1.12
Boot HART Base ISA : rv64imafdch
Boot HART ISA Extensions : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
Boot HART PMP Count : 16
Boot HART PMP Granularity : 2 bits
Boot HART PMP Address Bits : 54
Boot HART MHPM Info : 16 (0x0007fff8)
Boot HART Debug Triggers : 2 triggers
Boot HART MIDELEG : 0x0000000000001666
Boot HART MEDELEG : 0x0000000000f4b509
[=3hRISC-V EDK2 firmware version 2025.11-3ubuntu7.3
Press ESCAPE within 5 seconds for boot options ERROR: C40000002:V03051002 I0 6D33944A-EC75-4855-A54D-809C75241F6C 83FFF850
BdsDxe: failed to load Boot0001 "UEFI Misc Device" fr
om PciRoot(0x0)/Pci(0x1,0x0): Not Found
[=3hStarKernel UEFI Loader
Loading kernel from ESP...
[CKPT 001] Entered efi_main - ConOut live
Monolithic build - kernel linked directly
Collecting boot information...
CmdLine: parsed OK
[CKPT 004] Command line parsed
[CKPT 005] Kernel stack allocation decided
[CKPT 006] Boot info collected (ACPI table located)
GOP: linear framebuffer found
[CKPT 007] GOP: linear framebuffer found
[CKPT 008] About to enter ExitBootServices retry loop
Calling kernel_main (monolithic)...
riscv64 item 4.3.5a: satp state at kernel entry (before switch)
satp.MODE = 0x000000000000000a
satp.PPN = 0x00000000000bf868
__kernel_start = 0x00000000bdc95794
riscv64: satp cleared -- Bare mode, explicit (item 4.3.5a)
_____ _ _ __ _
/ ____| | | |/ / | |
| (___ | |_ __ _ _ __| ' / ___ _ __ _ __ ___| |
\___ \| __/ _` | '__| < / _ \ '__| '_ \ / _ \ |
____) | || (_| | | | . \ __/ | | | | | __/ |
|_____/ \__\__,_|_| |_|\_\___|_| |_| |_|\___|_|
LithosAnanke v2.1.0 riscv64
Architecture: riscv64
Build: Oct 13 2026 21:18:17
UEFI BootServices: EXITED
Console: serial line is a 16550 at 0x0000000010000000 (no devicetree: QEMU's)
=== StarKernel Boot Information ===
Memory map entries: 100
Total memory: 1020 MB
Usable memory: 975 MB
===================================
PMM initialized.
PMM statistics:
Total pages: 250264
Free pages : 249612
Used pages : 652
Total MB : 977
Free MB : 975
Used MB : 2
VMM initialized (mapped RAM, CR3 switched)
VMM initialized (mapped RAM, CR3 switched)
VMM self-test: mapped OK at 0xffff800000000000
VMM self-test complete.
IDT installed.
APIC: init...
PLIC: no DTB PLIC node -- using QEMU virt-machine default (base=0x0c000000)
PLIC: init (S-mode context 1, threshold=0)
APIC: init done
Timer: init...
Timer: RISC-V time CSR @ 10000000 Hz (FALLBACK, no devicetree)
Timer: init done
Kernel heap initialized.
Heap statistics:
Total bytes: 536870872
Free bytes: 536870872
Used bytes: 0
Peak bytes: 0
Heap base addr: 2214588416
Heap end addr: 2751459328
Heartbeat: init...
Heartbeat: init done
Kernel initialization complete.
Boot successful!
StarForth v4: one host node, the F18-derived engine
V4: capsule v4:nucleus-64.f18 signature: missing (unsigned)
PARITY:V4_NUCLEUS name=v4:nucleus-64.f18 capsule_id=0x3b4a82485b10bfb7 capsule_hash=0x3b4a82485b10bfb7 words=311
V4: capsule v4:forth79.4th signature: missing (unsigned)
PARITY:V4_CAPSULE name=v4:forth79.4th capsule_id=0x4055641ee17d176b capsule_hash=0x4055641ee17d176b dict_hash=0x6b56e15226ee3719
PARITY:V4_POST tests=538 pass=538 fail=0
PARITY:V4_SYSTEM word_count=317 dict_hash=0xe5a57f3eb72ff94a
PARITY:OK
POST: PASSED
[HADES][INFO ] [KRELTSC: 0] blk: raw device LBN 2048..3071 (1024 blocks)
PCI: init...
virtio-blk: found device
[HADES][INFO ] [KRELTSC: 30621024] blk: disk 'StarForth Volume' v2 LBN 3072..789009 (785938 user b
Artemis: virtio-blk attached
ok> : SQ DUP * ;
ok
ok> 7 SQ . 3 4 U* . .
49 0 12 ok
ok> T{
UNKNOWN WORD: 'T{'
ERROR
ok> RS1
UNKNOWN WORD: 'RS1'
ERROR
ok> COLD
FORTH-79 Cold Start
System initialized.
ok
ok> 3 4 U* . .
0 12 ok
ok> FORGET U*
Protected word
ERROR
ok> 9 KERNEL-WORD ASK9 ASK9
Argument out of range
ERROR
ok> 1 BLOCK C@ . 2047 BLOCK C@ .
0 0 ok
ok> 2100 BLOCK 1024 BLANK 65 2100 BLOCK C! UPDATE SAVE-BUFFERS EMPTY-BUFFERS 2100 BLOCK C@ .
65 ok
ok> 3072 BLOCK C@ .
0 ok
ok> 3072 BLOCK DROP UPDATE SAVE-BUFFERS
Storage refused
ERROR
ok> 9999999 BLOCK
Block out of range
ERROR
ok> 5 7 PORT!
No one on that port
ERROR
ok> 1 2 + .
3 ok
ok> 5 AWAIT
7 8 * .
Interrupted
ERROR
ok> 56 ok
ok> : N4 400 0 DO I . LOOP ;
ok
ok> N4
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Stack overflow
ERROR
ok> 3 4 + .
7 ok
ok> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
ok
ok> WORDS
N4 ASK9 U/MOD U* (UD) (NB) (BITS) BYE ACL-WORD-ID ACL-HEAT@ ACL-INIT-PRIMITIVES
ACL-INHERIT ACL-ALLOW! ACL-TTL! ACL-MODE! ACL-PIN ACL-PINNED? ACL-ALLOW@
ACL-TTL@ ACL-MODE@ ACL-HOOK LOG-DEBUG" LOG-TEST" LOG-INFO" LOG-WARN"
LOG-ERROR" (LOG") LOG-DEBUG-STR LOG-TEST-STR LOG-INFO-STR LOG-WARN-STR
LOG-ERROR-STR LOG-LEVEL! LOG-LEVEL@ LOG-DEBUG LOG-TEST LOG-INFO
LOG-WARN LOG-ERROR THRU --> LOAD LIST EMPTY-BUFFERS FLUSH SAVE-BUFFERS
UPDATE BLOCK BUFFER BLK SCR Q.PRINT Q.COS Q.SIN Q.LOG Q.SQRT Q.EXP
Q./ Q.* Q.MIN Q.MAX Q.0= Q.> Q.< Q.= Q.NEG Q.ABS Q.- Q.+ Q.SCALE
Q.0 Q.1 Q.TO-INT Q.FROM-INT DEFER@ IS DEFER COLD WARM PAGE VERSION
79-STANDARD FORGET VLIST WORDS ORDER VOCABULARY DEFINITIONS FORTH
CURRENT CONTEXT FENCE SKIP SCAN SEARCH COMPARE -TRAILING BLANK ERASE
FILL MOVE CMOVE> ?TERMINAL DMIN DMAX D< M- M+ D2/ D2* D= D0< D0=
DABS D- RSHIFT LSHIFT WITHIN MIN MAX ABS U> U< >= <= <> 0> 0<> -!
2! 2@ INVERT FALSE TRUE NOP 2R@ 2R> 2>R 2ROT 2OVER 2SWAP DUMP .S
? D. D.R U. U.R . .R OCTAL HEX DECIMAL SPACES #> #S # SIGN HOLD
<# STATE PAD BASE SPAN >IN TIB BL > < = NOT 0< 0= OR XOR AND M/MOD
*/ */MOD MOD / /MOD M* S>D 2/ 2* 2- 2+ 1- 1+ NEGATE * - + CELLS
+! ! @ UNLOOP LEAVE J I R@ R> >R ROLL PICK DEPTH 2DROP 2DUP ?DUP
-ROT ROT SWAP OVER DROP DUP S" (S") ABORT" (ABORT") ." (.") ABORT
QUIT (WORD-FORGOTTEN) (WORD-DEFINED) (REFUSED) (LOST) (CONSOLE)
(ME) NODE-ERROR (SEAL) PORT! AWAIT SEND-ON SEND NO-ROUTES GONE NO-ROUTE
DEFAULT-ROUTE NEIGHBOUR ROUTE +LOOP LOOP ?DO DO ENDCASE ENDOF OF
CASE REPEAT WHILE AGAIN UNTIL BEGIN THEN ELSE IF DOES> CONSTANT
VARIABLE CREATE [COMPILE] COMPILE IMMEDIATE EXIT KERNEL-WORD ; :
[LITERAL] ['] ' LITERAL ] [ INTERPRET EXECUTE FIND HIDDEN SMUDGE
TRAVERSE >BODY PFA CFA NAME> NFA >NAME LINK> LFA >LINK LATEST 2,
C, , ALLOT ALIGN HERE \ ( NUMBER CONVERT ENCLOSE WORD QUERY EXPECT
SOURCE TYPE COUNT SPACE CR KEY EMIT CMOVE C! C@ DNEGATE D+ UM*
ok
ok> DEPTH .
26 ok
ok> : AW 5 AWAIT ;
ok
ok> 1
ok
ok> AW
Stack overflow
ERROR
ok> 4321 .
4321 ok
ok> HERE .
8326 ok
ok> BYE
BYE: cold restart
+264
View File
@@ -0,0 +1,264 @@
/* fdt_uart_test.c -- host test of the console's serial-line lookup
* (kernel/src/hal/fdt_uart.c): which UART a board's devicetree names as its
* console, where it is, and how its registers are laid out.
*
* No board is needed. The test builds devicetrees shaped like the Milk-V
* Mars's (JH7110), the Raspberry Pi 5's (BCM2712) and QEMU's riscv64 virt
* machine's, with the values those boards' own devicetree sources give, and
* checks what the lookup finds in each; then ones that must find nothing.
* Given the path of a real blob and what it should give, it checks that too.
*
* Build and run, from the repository root:
*
* cc -std=c99 -Wall -Wextra -Werror -Ikernel/include \
* tools/fdt_uart_test.c kernel/src/hal/fdt.c kernel/src/hal/fdt_uart.c \
* -o build/fdt_uart_test
* build/fdt_uart_test
* build/fdt_uart_test build/cache/raspi-<tag>/bcm2712-rpi-5-b.dtb pl011 0x107d001000 0 1
*
* It is not part of any build; like tools/ttftest.c it is run by hand.
*/
#include "starkernel/fdt.h"
#include "starkernel/fdt_uart.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int failures = 0, checks = 0;
#define CHECK(c, ...) do { checks++; if (!(c)) { failures++; printf("FAIL %s:%d: ", __FILE__, __LINE__); printf(__VA_ARGS__); printf("\n"); } } while (0)
/* ---- a devicetree blob, written a token at a time ---- */
static unsigned char st[4096], strs[1024], blob[8192];
static unsigned st_len, strs_len;
static void put32(unsigned char *p, uint32_t v) { p[0] = (unsigned char)(v >> 24); p[1] = (unsigned char)(v >> 16); p[2] = (unsigned char)(v >> 8); p[3] = (unsigned char)v; }
static void tok(uint32_t v) { put32(st + st_len, v); st_len += 4; }
static void begin(const char *name)
{
size_t n = strlen(name) + 1;
tok(1);
memcpy(st + st_len, name, n);
st_len += (unsigned)n;
while (st_len & 3u) st[st_len++] = 0;
}
static void end(void) { tok(2); }
static uint32_t str_off(const char *name)
{
unsigned i = 0;
while (i < strs_len) { if (strcmp((const char *)strs + i, name) == 0) return i; i += (unsigned)strlen((const char *)strs + i) + 1; }
memcpy(strs + strs_len, name, strlen(name) + 1);
i = strs_len; strs_len += (unsigned)strlen(name) + 1;
return i;
}
static void prop(const char *name, const void *value, uint32_t len)
{
tok(3); tok(len); tok(str_off(name));
if (len) memcpy(st + st_len, value, len);
st_len += len;
while (st_len & 3u) st[st_len++] = 0;
}
static void prop_str(const char *name, const char *s) { prop(name, s, (uint32_t)strlen(s) + 1); }
static void prop_cells(const char *name, unsigned n, const uint32_t *c)
{
unsigned char b[64];
unsigned i;
for (i = 0; i < n; i++) put32(b + 4 * i, c[i]);
prop(name, b, 4 * n);
}
static void prop_u32(const char *name, uint32_t v) { prop_cells(name, 1, &v); }
static void start(void) { st_len = 0; strs_len = 0; begin(""); }
static const void *finish(void)
{
unsigned off_struct = 40 + 16, off_strs;
end(); tok(9);
off_strs = off_struct + st_len;
memset(blob, 0, sizeof blob);
put32(blob + 0, 0xd00dfeedu);
put32(blob + 4, off_strs + strs_len);
put32(blob + 8, off_struct);
put32(blob + 12, off_strs);
put32(blob + 16, 40);
put32(blob + 20, 17);
put32(blob + 24, 16);
put32(blob + 32, strs_len);
put32(blob + 36, st_len);
memcpy(blob + off_struct, st, st_len);
memcpy(blob + off_strs, strs, strs_len);
return blob;
}
static const char *kind_name(int k) { return k == FDT_UART_PL011 ? "pl011" : k == FDT_UART_16550 ? "16550" : "none"; }
static int is(const fdt_uart_t *u, int kind, uint64_t base, uint32_t shift, uint32_t width)
{
return u->kind == kind && u->base == base && u->reg_shift == shift && u->io_width == width;
}
#define SHOW(u) kind_name((u).kind), (unsigned long long)(u).base, (unsigned)(u).reg_shift, (unsigned)(u).io_width
/* the Mars: jh7110.dtsi's uart0 and jh7110-common.dtsi's aliases and chosen */
static const void *mars(const char *stdout_path, const char *status, int with_ranges)
{
static const uint32_t reg[4] = { 0, 0x10000000u, 0, 0x10000u };
start();
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
begin("aliases"); prop_str("serial0", "/soc/serial@10000000"); end();
begin("chosen"); if (stdout_path) prop_str("stdout-path", stdout_path); end();
begin("soc");
prop_str("compatible", "simple-bus");
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
if (with_ranges) prop("ranges", "", 0);
begin("serial@10000000");
prop("compatible", "starfive,jh7110-uart\0snps,dw-apb-uart", 38);
prop_cells("reg", 4, reg);
prop_u32("reg-io-width", 4); prop_u32("reg-shift", 2);
if (status) prop_str("status", status);
end();
end();
return finish();
}
/* the Pi 5: bcm2712.dtsi's soc bus and its debug UART, serial10 */
static const void *pi5(const char *compatible, unsigned compatible_len)
{
static const uint32_t ranges[4] = { 0x7c000000u, 0x10, 0x7c000000u, 0x80000000u };
static const uint32_t reg[2] = { 0x7d001000u, 0x200u };
static const uint32_t rp1[4] = { 0xc0, 0x40030000u, 0, 0x100 };
start();
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
begin("aliases");
prop_str("serial0", "/axi/pcie@1000120000/rp1/serial@30000");
prop_str("serial10", "/soc@107c000000/serial@7d001000");
end();
begin("chosen"); prop_str("stdout-path", "serial10:115200n8"); end();
begin("soc@107c000000");
prop_str("compatible", "simple-bus");
prop_u32("#address-cells", 1); prop_u32("#size-cells", 1);
prop_cells("ranges", 4, ranges);
begin("serial@7d001000");
prop("compatible", compatible, compatible_len);
prop_cells("reg", 2, reg);
prop_str("status", "okay");
end();
end();
begin("axi");
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
prop("ranges", "", 0);
begin("pcie@1000120000");
prop_u32("#address-cells", 3); prop_u32("#size-cells", 2);
begin("rp1");
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
begin("serial@30000"); prop("compatible", "arm,pl011-axi", 14); prop_cells("reg", 4, rp1); prop_str("status", "disabled"); end();
end();
end();
end();
return finish();
}
/* QEMU's riscv64 virt machine, as U-Boot hands it on: the console named by its path */
static const void *virt(void)
{
static const uint32_t reg[4] = { 0, 0x10000000u, 0, 0x100u };
start();
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
begin("chosen"); prop_str("stdout-path", "/soc/serial@10000000"); end();
begin("soc");
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
prop("ranges", "", 0);
begin("serial@10000000"); prop_str("compatible", "ns16550a"); prop_cells("reg", 4, reg); end();
end();
return finish();
}
int main(int argc, char **argv)
{
fdt_uart_t u;
const void *b;
if (argc == 6) { /* a real blob, and what it should give */
static unsigned char real[2 * 1024 * 1024];
FILE *f = fopen(argv[1], "rb");
size_t n;
int kind = strcmp(argv[2], "pl011") == 0 ? FDT_UART_PL011 : strcmp(argv[2], "16550") == 0 ? FDT_UART_16550 : FDT_UART_NONE;
if (!f) { printf("cannot open %s\n", argv[1]); return 2; }
n = fread(real, 1, sizeof real, f);
fclose(f);
memset(&u, 0xAA, sizeof u);
CHECK(n > 40 && fdt_console_uart(real, &u) && is(&u, kind, strtoull(argv[3], 0, 0), (uint32_t)strtoul(argv[4], 0, 0), (uint32_t)strtoul(argv[5], 0, 0)),
"%s: %s at 0x%llx, registers %u apart (shift), %u wide", argv[1], SHOW(u));
printf(" %s: %s at 0x%llx, shift %u, width %u\n", argv[1], SHOW(u));
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}
/* ---- the three boards ---- */
b = mars("serial0:115200n8", 0, 1);
CHECK(fdt_valid(b), "the blob the test builds is a devicetree");
memset(&u, 0xAA, sizeof u);
CHECK(fdt_console_uart(b, &u) && is(&u, FDT_UART_16550, 0x10000000ull, 2, 4),
"the Mars: a 16550 at 0x10000000 with its registers four bytes apart and four wide: %s 0x%llx %u %u", SHOW(u));
CHECK(fdt_console_uart(mars("serial0", 0, 1), &u) && is(&u, FDT_UART_16550, 0x10000000ull, 2, 4), "the same with no line settings after the alias");
CHECK(fdt_console_uart(mars("serial0:115200n8", "okay", 1), &u) && u.kind == FDT_UART_16550, "and with status okay");
memset(&u, 0xAA, sizeof u);
CHECK(fdt_console_uart(pi5("arm,pl011-axi\0arm,pl011\0arm,primecell", 37), &u) && is(&u, FDT_UART_PL011, 0x107d001000ull, 0, 4),
"the Pi 5: serial10, a PL011, its address carried through the bus's ranges to 0x107d001000: %s 0x%llx %u %u", SHOW(u));
CHECK(fdt_console_uart(pi5("arm,pl011-axi", 14), &u) && is(&u, FDT_UART_PL011, 0x107d001000ull, 0, 4), "a node that says only arm,pl011-axi is a PL011 too");
memset(&u, 0xAA, sizeof u);
CHECK(fdt_console_uart(virt(), &u) && is(&u, FDT_UART_16550, 0x10000000ull, 0, 1),
"QEMU's riscv64 machine: named by its path, a 16550 with registers a byte apart: %s 0x%llx %u %u", SHOW(u));
/* ---- what must find nothing, and leave *out as no line ---- */
memset(&u, 0xAA, sizeof u);
CHECK(!fdt_console_uart(mars(0, 0, 1), &u) && u.kind == FDT_UART_NONE, "no stdout-path: no line, and it is not guessed");
CHECK(!fdt_console_uart(mars("serial7:115200n8", 0, 1), &u) && u.kind == FDT_UART_NONE, "an alias that is not there");
CHECK(!fdt_console_uart(mars("/soc/serial@20000000", 0, 1), &u) && u.kind == FDT_UART_NONE, "a path to no node");
CHECK(!fdt_console_uart(mars("/soc/serial", 0, 1), &u) && u.kind == FDT_UART_NONE, "a node's name without its address is not that node");
CHECK(!fdt_console_uart(mars("serial0:115200n8", "disabled", 1), &u) && u.kind == FDT_UART_NONE, "a UART that is disabled");
CHECK(!fdt_console_uart(mars("serial0:115200n8", 0, 0), &u) && u.kind == FDT_UART_NONE, "a bus with no ranges: its addresses cannot be carried up");
CHECK(!fdt_console_uart(pi5("brcm,bcm2835-aux-uart", 22), &u) && u.kind == FDT_UART_NONE, "a UART of a kind the console has no driver for");
{
static const uint32_t far_ranges[4] = { 0x10000000u, 0x10, 0x10000000u, 0x1000u };
static const uint32_t reg[2] = { 0x7d001000u, 0x200u };
start();
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
begin("chosen"); prop_str("stdout-path", "/soc/serial@7d001000"); end();
begin("soc"); prop_u32("#address-cells", 1); prop_u32("#size-cells", 1); prop_cells("ranges", 4, far_ranges);
begin("serial@7d001000"); prop_str("compatible", "arm,pl011"); prop_cells("reg", 2, reg); end();
end();
CHECK(!fdt_console_uart(finish(), &u) && u.kind == FDT_UART_NONE, "an address none of the bus's ranges covers");
}
{
/* the console named is behind a bus with three address cells, as the Pi 5's RP1 UARTs are: not handled, and said so */
b = pi5("arm,pl011", 10);
start();
prop_u32("#address-cells", 2); prop_u32("#size-cells", 2);
begin("chosen"); prop_str("stdout-path", "/pcie/serial@30000"); end();
begin("pcie"); prop_u32("#address-cells", 3); prop_u32("#size-cells", 2); prop("ranges", "", 0);
{ static const uint32_t reg[5] = { 0, 0xc0, 0x40030000u, 0, 0x100 };
begin("serial@30000"); prop_str("compatible", "arm,pl011"); prop_cells("reg", 5, reg); end(); }
end();
CHECK(!fdt_console_uart(finish(), &u) && u.kind == FDT_UART_NONE, "a UART behind a bus with three address cells");
}
memset(&u, 0xAA, sizeof u);
CHECK(!fdt_console_uart(0, &u) && u.kind == FDT_UART_NONE, "no devicetree at all");
b = mars("serial0:115200n8", 0, 1);
blob[0] = 0;
CHECK(!fdt_console_uart(b, &u) && u.kind == FDT_UART_NONE, "what is not a devicetree");
{
/* cut short at every length: never read past the end, never find a line in half a tree */
unsigned n, total, found = 0;
static unsigned char cut[8192];
b = mars("serial0:115200n8", 0, 1);
total = (unsigned)((blob[4] << 24) | (blob[5] << 16) | (blob[6] << 8) | blob[7]);
for (n = 40; n < total; n += 4) {
memcpy(cut, blob, total);
put32(cut + 36, n > 56 ? (n - 56 < st_len ? n - 56 : st_len) : 0); /* the structure block ends early */
if (fdt_console_uart(cut, &u) && !is(&u, FDT_UART_16550, 0x10000000ull, 2, 4)) found++;
}
CHECK(found == 0, "a structure block cut short at any length gives the right line or none: %u wrong", found);
}
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}