Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -5577,6 +5577,10 @@ xilinx-vmk180-20242 login:

Inflating a 24 MB kernel takes about 7 s on the A72, spent entirely between the `Decompressing` and `Decompressed` lines with no intermediate output. Do not mistake that gap for a hang.

**Console input**

`DEBUG_UART=1` provides the console output path, and `uart_rx()` the matching non-blocking receive: it returns 1 when a character was read and 0 when the receive FIFO is empty. This is the only input path, as wolfBoot links `-nostdlib` and there is no C library `getchar()` behind it. Declare it as `extern int uart_rx(uint8_t *c);` rather than including `uart_drv.h`, which also declares `uart_tx()` and `uart_init()` with signatures this HAL does not use. When wolfBoot runs at EL2 the port is left exactly as the PLM configured it, so `uart_rx()` sets `UARTCR.RXE` itself on first use and touches nothing else. Blocking on input from a hook such as `wolfBoot_hook_postinit()` is safe: wolfBoot imposes no timeout of its own at that point.

**Diagnosing a boot that stops with no message**

wolfBoot runs at EL2 on Versal, where any abort is fatal. With `DEBUG_UART=1` (the default in both Versal example configs) the handlers print the syndrome:
Expand All @@ -5589,7 +5593,7 @@ FAR_EL2: 0x00000000F9200000
*** SYSTEM HALTED ***
```

`ESR_EL2[31:26]` is the exception class, `[5:0]` the fault status, and `FAR_EL2` the faulting address. A stop with no such banner is not an exception - check the PLM's `PMC EAM` output on the same UART. Error IDs `0xA`/`0xB` are `DDRMB_CR`/`DDRMB_NCR`, DDR controller correctable and uncorrectable errors, which the PLM logs without acting on.
`ESR_EL2[31:26]` is the exception class, `[5:0]` the fault status, and `FAR_EL2` the faulting address. An instruction abort (exception class `0x21`) reporting `ELR_EL2` and `FAR_EL2` as zero is a call through a null or unresolved function pointer rather than a bad data access. A stop with no such banner is not an exception - check the PLM's `PMC EAM` output on the same UART. Error IDs `0xA`/`0xB` are `DDRMB_CR`/`DDRMB_NCR`, DDR controller correctable and uncorrectable errors, which the PLM logs without acting on.

**Boot Performance**

Expand Down
22 changes: 22 additions & 0 deletions hal/raspi3.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
/* PL011 UART registers */
#define UART0_BASE GPIO_BASE + 0x1000
#define UART0_DR ((volatile unsigned int*)(UART0_BASE+0x00))
#define UART0_RSR ((volatile unsigned int*)(UART0_BASE+0x04))
#define UART0_FR ((volatile unsigned int*)(UART0_BASE+0x18))
#define UART0_IBRD ((volatile unsigned int*)(UART0_BASE+0x24))
#define UART0_FBRD ((volatile unsigned int*)(UART0_BASE+0x28))
Expand Down Expand Up @@ -142,6 +143,27 @@ void uart_tx(char c)
*UART0_DR = c;
}

/* Non-blocking receive. Returns 1 when a character was read, 0 otherwise. */
int uart_rx(uint8_t *c)
{
unsigned int dr;

/* receive fifo empty */
if (*UART0_FR & 0x10)
return 0;

dr = *UART0_DR;
if (dr & 0xF00) {
/* overrun, break, parity or framing error: drop the character and
* clear the sticky receive status */
*UART0_RSR = 0;
return 0;
}

*c = (uint8_t)(dr & 0xFF);
return 1;
}

char uart_read(void)
{
char c;
Expand Down
27 changes: 27 additions & 0 deletions hal/versal.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,33 @@ void uart_write(const char *buf, uint32_t len)
;
}

/* Non-blocking receive. Returns 1 when a character was read, 0 otherwise. */
int uart_rx(uint8_t *c)
{
uint32_t dr;

if ((UART_CR & UART_CR_RXE) == 0) {
/* At EL2 the port is left exactly as PLM configured it, and PLM only
* needs to transmit its own log. Enabling receive on its own does not
* disturb the baud rate or frame format already in place. */
UART_CR |= UART_CR_RXE;
}

if (UART_FR & UART_FR_RXFE)
return 0;

dr = UART_DR;
if (dr & UART_DR_ERR_MASK) {
/* Overrun, break, parity or framing error: drop the character and
* clear the sticky receive status. */
UART_RSR = 0;
return 0;
}

*c = (uint8_t)(dr & UART_DR_DATA_MASK);
return 1;
}

#else
#define uart_init() do {} while(0)
#endif /* DEBUG_UART */
Expand Down
8 changes: 8 additions & 0 deletions hal/versal.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,14 @@
#define UART_IMSC UART_REG(UART_IMSC_OFFSET)
#define UART_ICR UART_REG(UART_ICR_OFFSET)

/* Data Register (UARTDR) bits - receive side */
#define UART_DR_OE (1UL << 11) /* Overrun error */
#define UART_DR_BE (1UL << 10) /* Break error */
#define UART_DR_PE (1UL << 9) /* Parity error */
#define UART_DR_FE (1UL << 8) /* Framing error */
#define UART_DR_ERR_MASK (0xFUL << 8)
#define UART_DR_DATA_MASK 0xFFUL

/* Flag Register (UARTFR) bits - Status */
#define UART_FR_RI (1UL << 8) /* Ring indicator */
#define UART_FR_TXFE (1UL << 7) /* TX FIFO empty */
Expand Down
27 changes: 26 additions & 1 deletion tools/unit-tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ TESTS+=unit-imx95-ele
TESTS+=unit-imx95-disk
TESTS+=unit-versal-ext-write
TESTS+=unit-versal-wdt
TESTS+=unit-versal-uart-rx
TESTS+=unit-raspi3-uart-rx
TESTS+=unit-t10xx-qe-firmware
TESTS+=unit-t10xx-flash-status
TESTS+=unit-p1021-erase-advance
Expand Down Expand Up @@ -1599,6 +1601,28 @@ versal_wdt_extract.h: ../../hal/versal.c
unit-versal-wdt: unit-versal-wdt.c versal_wdt_extract.h versal_host.h
gcc -o $@ unit-versal-wdt.c -DTARGET_versal $(CFLAGS) $(LDFLAGS)

# unit-versal-uart-rx runs the real uart_rx() from hal/versal.c against
# an emulated PL011 register block. UART_REG() resolves to an absolute
# MMIO address, so the function is extracted verbatim and the test
# redefines the accessor over a local array.
versal_uart_rx_extract.h: ../../hal/versal.c
sed -n '/^int uart_rx(/,/^}/p' $< > $@
test -s $@

unit-versal-uart-rx: unit-versal-uart-rx.c versal_uart_rx_extract.h versal_host.h
gcc -o $@ unit-versal-uart-rx.c -DARCH_AARCH64 -DARCH_64BIT $(CFLAGS) $(LDFLAGS)

# unit-raspi3-uart-rx runs the real uart_rx() from hal/raspi3.c, the
# other PL011 console in the tree, against an emulated register block.
# Its accessors are absolute MMIO pointers, so the function is extracted
# verbatim and the test redefines them over a local array.
raspi3_uart_rx_extract.h: ../../hal/raspi3.c
sed -n '/^int uart_rx(/,/^}/p' $< > $@
test -s $@

unit-raspi3-uart-rx: unit-raspi3-uart-rx.c raspi3_uart_rx_extract.h
gcc -o $@ unit-raspi3-uart-rx.c $(CFLAGS) $(LDFLAGS)

# unit-t10xx-qe-firmware runs the real qe_check_firmware() from
# hal/nxp_t10xx.c (the per-microcode code_offset was never
# bounded against the declared image, so a self-consistent blob could
Expand Down Expand Up @@ -2246,7 +2270,8 @@ GENERATED_SRC:=aurix_erased_extract.h \
t2080_fman_extract.h \
ti_hercules_write_extract.h versal_ext_write_extract.h versal_host.c \
versal_wdt_extract.h \
versal_host.h versal_qspidev_extract.h zynq_erase_extract.h \
versal_host.h versal_qspidev_extract.h versal_uart_rx_extract.h \
raspi3_uart_rx_extract.h zynq_erase_extract.h \
bbram_crc_extract.h efuse_guards_extract.h \
zynq_write_extract.h \
fat32-test.img fat32-test.img.tmp fat32-payload.bin fat32-nested.bin \
Expand Down
135 changes: 135 additions & 0 deletions tools/unit-tests/unit-raspi3-uart-rx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/* unit-raspi3-uart-rx.c
*
* Exercises the real uart_rx() from hal/raspi3.c against an emulated
* PL011 register block. The register accessors are absolute MMIO
* pointers, so the Makefile extracts the function verbatim and they are
* redefined over a local array below before it is included.
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/

#include <check.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* Emulated register block, indexed by register offset. */
static unsigned int uart_regs[0x20];

#define UART0_DR ((volatile unsigned int*)&uart_regs[0x00 / 4])
#define UART0_RSR ((volatile unsigned int*)&uart_regs[0x04 / 4])
#define UART0_FR ((volatile unsigned int*)&uart_regs[0x18 / 4])

#define RXFE 0x10

#include "raspi3_uart_rx_extract.h"

static void setup(void)
{
memset(uart_regs, 0, sizeof(uart_regs));
}

static void teardown(void)
{
}

START_TEST(test_empty_fifo_returns_zero)
{
uint8_t c = 0xA5;

*UART0_FR = RXFE;
*UART0_DR = 'x';

ck_assert_int_eq(uart_rx(&c), 0);
ck_assert_uint_eq(c, 0xA5);
}
END_TEST

START_TEST(test_character_received)
{
uint8_t c = 0;

*UART0_FR = 0;
*UART0_DR = 'a';

ck_assert_int_eq(uart_rx(&c), 1);
ck_assert_uint_eq(c, 'a');
}
END_TEST

/* Bits above the data field must not reach the caller. The receive
* error field at [11:8] is covered by test_receive_error_dropped. */
START_TEST(test_status_bits_masked_off)
{
uint8_t c = 0;

*UART0_FR = 0;
*UART0_DR = 0xF000U | 'b';

ck_assert_int_eq(uart_rx(&c), 1);
ck_assert_uint_eq(c, 'b');
}
END_TEST

START_TEST(test_receive_error_dropped)
{
const unsigned int err[4] = { 0x800, 0x400, 0x200, 0x100 };
uint8_t c;
int i;

for (i = 0; i < 4; i++) {
c = 0xA5;
*UART0_FR = 0;
*UART0_DR = err[i] | 'c';
*UART0_RSR = 0xF;

ck_assert_int_eq(uart_rx(&c), 0);
ck_assert_uint_eq(c, 0xA5);
ck_assert_uint_eq(*UART0_RSR, 0);
}
}
END_TEST

Suite *raspi3_uart_rx_suite(void)
{
Suite *s = suite_create("raspi3-uart-rx");
TCase *tc = tcase_create("raspi3-uart-rx");

tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_test(tc, test_empty_fifo_returns_zero);
tcase_add_test(tc, test_character_received);
tcase_add_test(tc, test_status_bits_masked_off);
tcase_add_test(tc, test_receive_error_dropped);
suite_add_tcase(s, tc);

return s;
}

int main(void)
{
int fails;
Suite *s = raspi3_uart_rx_suite();
SRunner *sr = srunner_create(s);

srunner_run_all(sr, CK_NORMAL);
fails = srunner_ntests_failed(sr);
srunner_free(sr);

return fails;
}
Loading
Loading