evidence · 10.0 KiB

README.md

Committed at docs/evidence/mainline-boot-splash/board/README.md. This is what grounds the requirements that cite it.

Open raw source

Mainline boot splash on the board, 2026-10-12

Hardware gates 2.1-2.4 of the-mainline-kernel-keeps-the-boot-splash, run on the one board in place (no second card: the operator answered “In place is fine”), with warm reboots standing in for power-on (the operator answered “Warm reboot is enough”). Every film below is a warm reboot, not a cold power-on. Camera: the bench webcam (/dev/video0, MJPEG 1280x720, 30 fps), which now views the panel from its long edge at a grazing angle; the far half of the panel is outside the frame.

System under test: k230-mainline-drm-shell-splash, toplevel /nix/store/kc3iyr4cnjlswa87kkpi81qgc1is6hli-nixos-system-nixos-26.11.20260919.20b1ddd, bundle /nix/store/kjlnrxv8vdk92lh84w2bkd1v0mx456hy-k230-coherent-shell-boot-files (source impl/mainline-splash-2026-10-12 a03999c3). Its Image and initrd are byte-identical to the previously installed 298a2bf6 system; it differs in the device tree (framebuffer@10000000, 0x400000, no-map), in bootargs (no console=tty0) and in carrying logo.xrgb (SHA-256 6c7a3608…a3af). U-Boot is the card’s existing source-built stage 1 with patches 0004/0005.

Raw serial logs and films stay off-repo (private); this directory holds sanitized excerpts, single frames and the scripts that produced every figure.

Sequence

StepToolResult
Preflight, stage, one-shot candidate bootcoherent-shell-board-* pipelinepreflight.json, host-inspection.json; PASS
Candidate runtimeruntime-check.py candidatecandidate-runtime.json; PASS
U-Boot hold with ArUco target logo (film2)tools/splash-uboot-session.py hold2.2 measurement
U-Boot hold + framebuffer poke (film3)… hold --pokeliveness, uboot-hold-poke-serial-excerpt.txt
Idle-kernel boot (film6)… idle2.1 photo, 2.3, idle-kernel-serial-excerpt.txt
Install as default, ordinary autoboot (film7)coherent-shell-board-boot.py --installdeployment-authorization.json, ordinary-runtime.json; PASS; 2.4

The candidate-boot film (film1) from the one-shot boot is the second 2.4 film.

2.1 Splash on the glass before Starting kernel

splash-at-uboot-before-kernel.jpg is film6 at session t = 17.99 s. The session transcript (idle-kernel-serial-excerpt.txt, same clock, film start 2.01 s before the session) shows U-Boot printing 2799104 bytes read … RM69A10 direct XRGB8888 logo.xrgb full-screen OSD4 before the autoboot prompt, the boot files loading from t = 13.3 s, bootm sent at t = 26.26 s, and only then Starting kernel .... The photograph is taken while U-Boot is still loading, 8 s before bootm.

Deviation: the task names ./tools/capture-boot.py; the capture here is tools/splash-uboot-session.py, which also timestamps every console line on the host clock that the film start is recorded against.

2.2 Motion of the U-Boot-lit image

U-Boot drives the panel with {3, 97, 0x17, 0x87} at 49.5 MHz (patch 0004, the values dsi-hsfreqrange-hardcoded.md measured). The logo was temporarily replaced by tools/panel-target.py’s target converted with target-to-xrgb.py (SHA-256 e2bb5820…bd60), the board held at K230#, and the panel filmed (aruco-target-at-uboot-prompt.jpg).

tools/panel-measure.py could not register: only markers 0 and 1 are in the current camera frame (it needs three). Its --record mode also fixes the exposure at 200, which made the first attempt too dark to find any marker. So the measurement uses camera-px-motion.py: the same norm1/shift routine on an unregistered camera-pixel strip across the barcode field, as the 0x87 rows of dsi-hsfreqrange-hardcoded.md were measured (camera px, fixed camera, no registration). A static object in the same clip is the camera’s noise floor.

film2, t = 15..50 s at K230# (1050 frames)U-Boot target, strip y330:360 x560:1010control: keyboard, y60:120 x330:6400x87 run 2 (earlier doc)
motion std0.01 px0.02 px0.02 px
span0.10 px0.15 px0.09 px
frame-to-frame mean0.01 px0.03 px0.00 px
frame-to-frame max0.06 px0.14 px0.04 px
jumps > 2 px000
brightness range100..100%99..102%100..100%

The U-Boot image’s motion is at the 0x87 run-2 level and below the static keyboard’s apparent motion, i.e. below what this camera can resolve. Frozen-panel check (the failure the earlier document warns about): in film3 mw.l 0x10000000 0xffffffff 0x1bbc0 at the U-Boot prompt (t = 38.28 s) turns the top 200 rows white by t = 38.52 s (fb-poke-before.jpg, fb-poke-after.jpg), and md.l reads the change back; the panel is scanning the splash buffer live. The original logo was restored and its hash checked before the next boot.

2.3 Idle kernel, nothing opening the display

tools/splash-uboot-session.py idle loaded the staged candidate’s files from mmc 1:2 (each size and CRC32 checked), imported its bootargs, appended systemd.mask=shell.service systemd.mask=seatd.service systemd.mask=k230-drm-splash.service for that boot only, and booted. A first idle run without the third mask showed k230-drm-splash (the Linux-side static splash owner) opening DRM at 45.2 s, so it was repeated. At the serial root prompt, and again after a 90 s hold (idle-kernel-serial-excerpt.txt):

splash-at-idle-kernel-prompt.jpg is film6 at session t = 147.99 s, during the hold at that prompt: the logo is still on the glass. Film6 classifies the logo continuously from 11.43 s to 204.77 s (the reboot), with only 1-2-frame other blips at reflection sweeps.

2.4 Frame timings, splash to the compositor’s first frame

frame-classes.py reduces the panel box of each frame to an edge map and correlates it with a logo and a shell reference frame from the same film (room reflections sweep the glass and make brightness useless; see film*-classes.txt).

Film (warm reboot)logo firstlogo lastcompositor first framebetween
film1, one-shot candidate boot16.37 s80.93 s80.97 snone
film7, installed ordinary autoboot42.43 s121.93 s121.97 snone

Across the Linux takeover by k230-drm-splash (kernel t = 45.2 s, mid-span) the image does not change: the first Linux frame is the logo. Most non-logo classifications inside the spans are the intact logo under a moving reflection or a camera refocus (film7 49.43, 63-64, 69, 105.6 s; film1 21.23 s). Some are not — see “Transient scanout glitches” below. The switch to the compositor is a single 33 ms frame step (handoff-film1-strip.jpg, handoff-film7-strip.jpg). The compositor’s first frame is the shell’s near-black navy background; the terminal prompt draws on it ~0.7 s later (film7 122.70 s; film1 101.2 s, which an earlier brightness reading had mistaken for the handoff). In film7 the whole picture, including the keyboard and USB plug off the panel, dims 5-7% at 122.03-122.6 s: that is the camera’s auto-exposure, not the panel.

Limit: a camera cannot tell a black frame from the compositor’s near-black background, so “no dark frame” is shown as: no intermediate state between the last logo frame and the compositor’s first frame at 30 fps, plus the kernel’s own preserving VO/DSI to first plane update lines.

Transient scanout glitches on the splash system (open)

Every splash-kernel boot filmed shows the stage-1 image glitching for 1-4 frames during early userspace, with the scene static (film7 75.0 s: keyboard phase-correlation shift 0.00 px while the panel box shifts):

FilmFilm timeWhat the frame showsKernel time (approx.)
film143.30, 44.10 slogo doubled/smeared~17 s (initrd activation, 16.5-19.3 s)
film656.70-56.73, 57.90-58.00 slogo wrapped along the panel (“230K”)~27-28 s
film775.00-75.03, 76.00-76.07 slogo displaced and garbled~26.5-27.5 s

Kernel times are film time minus an estimated kernel start (film1/film6 from the bootm stamp plus ~1.3 s; film7 from the compositor’s first frame at 121.97 s against sway reading its config at kernel 72.6 s), so they are good to about +-2 s. The ~27 s windows fall inside Load Kernel Modules and udev coldplug in the real root (25.9-29.9 s); no kernel message falls on a glitch. The image always recovers on the next frames and is never corrupted afterwards, which points at a scanout/sync disturbance rather than at the reserved buffer being written. These are not dark frames, so the 2.4 criterion as written is met, but they are visible: the operator reported “the display flickers sometimes. real close to perfect”. Cause not established; task 4.1 of the change keeps it open.

Ordinary kernel without the reservation (the flicker the operator saw)

After the U-Boot holds, the board autobooted the then-installed 298a2bf6 system, whose device tree has no splash reservation, with logo.xrgb now on /boot. In films 2 and 3 that kernel tears the stage-1 image on single frames (film2 58.9, 84.3-88.9 s; film3 65.3, 90.7-95.5 s; unreserved-kernel-tear-film2-strip.jpg) and leaves noise on the glass (film6 260 s) until its compositor draws. Neither splash-kernel boot shows any such frame. Installing the splash system as the default (film7, 5418 frames) removed it; the operator had reported “the display flickers sometimes” during these trials.

Not shown

Cold power-on (operator accepted warm reboot); a second card; ArUco-registered panel-row figures (camera out of the bench geometry); any touch or finger observation.