Mainline UART and shell readiness source audit
Recorded 2026-10-02T18:42:38Z. Evidence class: read-only host source and
candidate-DTB inspection. No board, serial, MMIO, build or controller edit.
Worktree /home/jadams/tmp/k230-mainline-uart-readiness, branch
audit/mainline-uart-readiness, base
6c03cc496fe8f74d1222099280fe0de458d8b05e. Only this note is owned;
no board/build reservation. Cached start tools/work-status.py completed
with exit zero at idle I/O priority.
The coordinator reports that the quiet --runtime-shutdown-trace trial of
the exact asj7l4zj... / 4wkhxf55... candidate printed Run /bin/sh as init process at kernel uptime 4.026622, then a no-job-control message and
sh-5.3#. Receipt lines had already been sent during boot; input echoes
contained garbling, bells/control sequences, and the later shell showed a
continuation prompt > . No fresh receipt was accepted and no candidate
reboot was requested. These are coordinator-provided observations, not an
independent capture review or new physical proof by this auditor. The shell
was preserved; no corrective input is proposed for that unknown parser state.
Concrete timing issue precedes any UART-cause inference
At the audited controller revision, main() waits for Linux version 7.3.0-rc5 and invokes await_initrd_ready() only for debug_shutdown
(tools/mainline-drm-initrd-shell-trial.py:1276–1291). Runtime shutdown tracing
and ordinary minimal mode proceed directly to run_probe_protocol() after
the version banner. The latter immediately enters await_reception()
(703–734): up to eight attempts, one second each, drain received output and
send the single-quoted builtin receipt line (224–226, 545–576):
PATH=/bin:/sbin; export PATH; printf 'K230_RDINIT_RX FRESH_32_HEX_TOKEN\n'
The kernel version banner is early boot output, not a shell readiness marker.
session.pump() drains host RX, not the board’s UART or shell input queue.
Fresh-token parsing correctly rejects echoed/stale receipt text but does not
prevent sending before the consumer exists. If part of a quoted line is lost
or retained across startup, later retries can enter an unfinished shell quote;
there is no automatic proof that the next line starts at a fresh command.
The reported continuation prompt is consistent with incomplete shell input;
its appearance does not by itself identify baud corruption or a kernel stall.
The coordinator’s restart agent owns the common readiness-gate correction. This audit changes no controller or tests. Missing receipt still means no accepted command execution, no following child probe and no reboot request.
Exact UART mapping and clocks
Full kernel/source/configuration/bundle paths and hashes are preserved in
the shutdown audit.
Source below is the exact installed
/nix/store/jvz4v73g8a67pqwm06v079h6s1cpqr9s-linux-mainline-k230-drm-src,
based on Linux 72d3fcf802c45d00b300f25b848a93c3a2bd7c7e.
fdtget on
/nix/store/asj7l4zj5jrjkgng4nrcx3y72p7jf7aa-k230-mainline-drm-trial-boot-files/k230-tdisplay-mainline-drm.dtb
confirmed:
| Property | Exact candidate value |
|---|---|
aliases.serial0 | /soc/serial@91400000 |
chosen.stdout-path | serial0:115200n8 |
| UART compatible/status | snps,dw-apb-uart, okay |
| UART address/access | 0x91400000, four-byte register access, register shift 2 |
| UART interrupt | DT specifier 16/type 4, not a measured Linux IRQ count |
| Functional clock | sysclk phandle 6, ID 161 (K230_LS_UART0_RATE) |
| Reset | controller phandle 7, ID 50 (RST_UART0) |
clock-names, fixed clock-frequency, pinctrl-0 | absent |
The bundle selects console=ttyS0,115200n8; host Session opens the same
serial device once at 115200 (1038) and does not reopen it at Linux/shell
startup. Configuration has SERIAL_8250_DW, SERIAL_8250_CONSOLE and
SERIAL_8250_DWLIB built in. The SoC UART0 node is at
arch/riscv/boot/dts/canaan/k230.dtsi:112–121; the board enables it without
adding a pinctrl consumer. This gives no source basis for a new UART0 pinmux
selection during this boot. DT selection is not an independent wire/baud
measurement.
Importantly, both UART0 gates are critical:
drivers/clk/clk-k230.c:678–682 marks its APB gate CLK_IS_CRITICAL, and
903–914 marks its functional gate critical and provides a divider 1–8 from
PLL0/16. The clock core prepares/enables critical clocks at registration
(drivers/clk/clk.c:4133–4156) and refuses their last disable/unprepare
(1120, 1260). Its unused-clock walk skips nonzero enable counts (1539–1578).
Direct UART0 gate loss from normal unused-clock cleanup therefore ranks below
the concrete timing problem; it is not supported merely by other peripherals’
different clock flags. Parent rates, divisor accuracy and actual RX error
counts remain unmeasured.
Real console-to-TTY transitions can discard early input
The exact DesignWare driver uses the unnamed functional-clock fallback when
baudclk is absent, obtains the optional apb_pclk, deasserts reset, then
registers the port (drivers/tty/serial/8250/8250_dw.c:708–735). This compatible
uses the normal clock-setting termios path, not the skip-set-rate quirk.
dw8250_set_termios() (473–491) requests baud × 16, rounds/sets the clock,
then programs UART termios/divisors. At 115200 the requested rate is
1,843,200 Hz; the actual rounded clock and resulting wire baud cannot be
derived without the actual parent rate. Critical gates do not forbid divider
changes.
uart_port_startup() calls UART startup and then inherits/reapplies console
termios (drivers/tty/serial/serial_core.c:304–336). The 8250 startup clears
RX/TX FIFOs (8250_port.c:2325–2331; helper 496–503); DW idle entry disables
RX interrupts and clears FIFOs while entering divisor-change idle
(8250_dw.c:135–171). Thus input arriving across startup/reconfiguration can
be dropped or partially retained without a persistent wrong-baud condition.
These source paths establish possibility, not which path affected this trial.
The kernel opens /dev/console for init’s descriptors after basic initcalls
and initramfs completion (init/main.c:1632–1645,1677–1680). Its
Run /bin/sh as init process line is printed before kernel_execve
(1472–1484). That line is a useful phase checkpoint, not proof that shell
initialization/readline has completed. Readable kernel TX also does not prove
that a complete host command reached shell RX. The historical same-bundle
minimal trial
did return receipt/true/proc/uptime; that is prior bidirectional command
evidence, not acceptance of this later failed receipt or every boot state.
Small discriminator and limits
For a later coordinator-owned trial after protected recovery, first passively observe the fresh candidate phase and kernel shell-entry checkpoint in every mode. Where available, observe the initial shell prompt before the first quoted command. Then require a fresh executed receipt before any child. A prompt, echo or shell-entry line alone cannot replace the receipt. Keep the readiness wait finite and preserve missing-marker/unknown recovery semantics; do not try to repair an unmatched quote by sending more receipt lines or closing/reopening the port.
Returned clean receipt/minimal gates after that timing correction would distinguish early-input contamination from a persistent RX problem on that new trial. Continued garbling after passive shell readiness would leave baud/clock/divisor accuracy, receive errors and host bridge state as separate questions needing bounded evidence. No clock-ignore flag, MMIO read, arbitrary interrupt survey, pinmux change or UART driver patch is justified by this source audit. It does not explain or prove restart callback execution, and runtime shutdown tracing was not reached by the failed receipt protocol.
Checks were narrow rg -n/sed -n reads of the exact source/controller and
configuration, plus fdtget -t s/-t x of the table properties. No private raw
UART or credentials were read/published. Validation:
openspec validate the-board-runs-a-mainline-kernel --strict and
git diff --check. No new physical pass, cause finding or task completion.