U-Boot: one stray serial byte parks the board at a prompt forever (CONFIG_BOOT_RETRY) #35

Closed
opened 2026-08-28 05:59:30 +00:00 by tiagoagueda · 1 comment
Owner

A single noise byte on the serial RX line during the boot window leaves the board
sitting at a prompt indefinitely. With no remote power control that is a
brick-until-someone-visits, and the serial adapter is permanently attached.

The mechanism

common/cli_readline.c:479:

if (first && timeout) {
        u64 etime = endtick(timeout);
        ...
        first = 0;
}

The timeout is honoured only until the first character arrives. After that the
read blocks forever.

common/menu.c:216 compounds it:

if (!choice_item)
        m->timeout = 0;

so an unrecognised menu choice clears the timeout for every subsequent read.

Both the bootdelay countdown and the extlinux Enter choice: menu
(38-userspace-hardening.md) go through this path.

Why this is not hypothetical

  • A CP210x is permanently attached at /dev/ttyUSB0 on ouranos as the black box.
  • tools/flood-interrupt.ps1 exists precisely because
    sending stray bytes reliably stops a boot.
  • The 2026-08-27 SPL incident showed the line is capable of producing garbage
    characters on its own.

Change

The guard is already wired into that same code path — bootretry_tstc_timeout() at
common/cli_readline.c:477. It just needs enabling:

CONFIG_BOOT_RETRY=y
CONFIG_BOOT_RETRY_TIME=60

Idle at any prompt for 60 s re-runs bootcmd. This covers the bootdelay prompt, the
extlinux menu, and a manual <INTERRUPT> in one change. CONFIG_RESET_TO_RETRY is
available if a full reset is preferred over re-running bootcmd; start without it.

Note

This is not covered by CONFIG_WATCHDOG_AUTOSTART. U-Boot pets the watchdog from
its own idle loop, so a parked prompt reads as healthy. The two changes are
complementary.

Acceptance

  • Send a single byte during the extlinux window, walk away, and confirm the board
    boots on its own within ~60 s.
  • Confirm a deliberate <INTERRUPT> at the prompt also resumes booting rather than
    waiting forever.
A single noise byte on the serial RX line during the boot window leaves the board sitting at a prompt indefinitely. With no remote power control that is a brick-until-someone-visits, and the serial adapter is permanently attached. ## The mechanism `common/cli_readline.c:479`: ```c if (first && timeout) { u64 etime = endtick(timeout); ... first = 0; } ``` The timeout is honoured only until the **first** character arrives. After that the read blocks forever. `common/menu.c:216` compounds it: ```c if (!choice_item) m->timeout = 0; ``` so an unrecognised menu choice clears the timeout for every subsequent read. Both the `bootdelay` countdown and the extlinux `Enter choice:` menu ([38-userspace-hardening.md](38-userspace-hardening.md)) go through this path. ## Why this is not hypothetical - A CP210x is permanently attached at `/dev/ttyUSB0` on ouranos as the black box. - [tools/flood-interrupt.ps1](tools/flood-interrupt.ps1) exists precisely because sending stray bytes reliably stops a boot. - The 2026-08-27 SPL incident showed the line is capable of producing garbage characters on its own. ## Change The guard is already wired into that same code path — `bootretry_tstc_timeout()` at `common/cli_readline.c:477`. It just needs enabling: ``` CONFIG_BOOT_RETRY=y CONFIG_BOOT_RETRY_TIME=60 ``` Idle at any prompt for 60 s re-runs `bootcmd`. This covers the bootdelay prompt, the extlinux menu, and a manual `<INTERRUPT>` in one change. `CONFIG_RESET_TO_RETRY` is available if a full reset is preferred over re-running `bootcmd`; start without it. ## Note This is **not** covered by `CONFIG_WATCHDOG_AUTOSTART`. U-Boot pets the watchdog from its own idle loop, so a parked prompt reads as healthy. The two changes are complementary. ## Acceptance - Send a single byte during the extlinux window, walk away, and confirm the board boots on its own within ~60 s. - Confirm a deliberate `<INTERRUPT>` at the prompt also resumes booting rather than waiting forever.
Author
Owner

Done and verified 2026-08-28.

One correction to the plan here: CONFIG_BOOT_RETRY=y + CONFIG_BOOT_RETRY_TIME=60 alone
does not build.

common/cli_hush.c:1032:9: error: #error "This only works with CONFIG_RESET_TO_RETRY or
                                  CONFIG_BOOT_RETRY_COMMAND enabled"

CONFIG_BOOT_RETRY_COMMAND does not exist anywhere in the tree - that #error string is
stale. The symbol wanted is CONFIG_RETRY_BOOTCMD ("Run bootcmd on retry"), which is the
right one anyway: re-running bootcmd is smaller than RESET_TO_RETRY and does not increment
the boot counter, so serial noise cannot walk the board into #36's known-good fallback.

Verified by reproducing the fault - bytes pushed at the RX line during the autoboot window,
then the port left alone:

Hit any key to stop autoboot: 0
=> xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Timeout waiting for command
Retrieving file: /boot/extlinux/extlinux.conf
...
   Image Name:   Linux-7.2.0-ge2df6e79588f-dirty

Reachable again 72 s after the last byte - 60 s of retry plus the boot. No SPL banner in
between, so it was the retry and not a watchdog reset.

That stale #error string is a one-line upstream cleanup nobody has sent.

**Done and verified 2026-08-28.** One correction to the plan here: `CONFIG_BOOT_RETRY=y` + `CONFIG_BOOT_RETRY_TIME=60` alone does not build. ``` common/cli_hush.c:1032:9: error: #error "This only works with CONFIG_RESET_TO_RETRY or CONFIG_BOOT_RETRY_COMMAND enabled" ``` **`CONFIG_BOOT_RETRY_COMMAND` does not exist anywhere in the tree** - that `#error` string is stale. The symbol wanted is `CONFIG_RETRY_BOOTCMD` ("Run bootcmd on retry"), which is the right one anyway: re-running `bootcmd` is smaller than `RESET_TO_RETRY` and does not increment the boot counter, so serial noise cannot walk the board into #36's known-good fallback. Verified by reproducing the fault - bytes pushed at the RX line during the autoboot window, then the port left alone: ``` Hit any key to stop autoboot: 0 => xxxxxxxxxxxxxxxxxxxxxxxxxxxx Timeout waiting for command Retrieving file: /boot/extlinux/extlinux.conf ... Image Name: Linux-7.2.0-ge2df6e79588f-dirty ``` Reachable again **72 s after the last byte** - 60 s of retry plus the boot. No SPL banner in between, so it was the retry and not a watchdog reset. That stale `#error` string is a one-line upstream cleanup nobody has sent.
Sign in to join this conversation.
No description provided.