evidence · 14.4 KiB

initrd-shell-diagnostic.md

Committed at docs/evidence/mainline-display/physical-2026-10-01/initrd-shell-diagnostic.md. This is what grounds the requirements that cite it.

Open raw source

Mainline initrd shell diagnostic preparation

Prepared after the two 2026-10-01 DRM trial boots stalled before a root login. This note records controller review and physical diagnostic attempts. The first rdinit=/bin/sh shell attempt reached PID 1 but its probe failed because the initrd environment had no usable PATH; it makes no claim about the stall’s cause.

The exact bundle remains /nix/store/gf3aqks2dg0z3ksz33ysfprnlbk83vw3-k230-mainline-drm-trial-boot-files, with matching system /nix/store/k9f4r2i9k9qj58z8z4l2kssy9rpwxxm1-nixos-system-nixos-26.11.20260919.20b1ddd. The committed source audit confirms the matching initrd contains executable /bin/sh (Bash 5.3p15) and reboot in its initrd command environment. The read-only IRQ review recommends this as the clean discriminator because it replaces /init before systemd with no extra debug boot arguments.

From the repository root, the prepared operator command is:

nix shell nixpkgs#python3Packages.pyserial --command \
  python3 tools/mainline-drm-initrd-shell-trial.py

The controller takes /tmp/k230-board.lock, checks the exact manifest’s five loads with complete standalone byte-count and U-Boot CRC reports, imports the matching 211-byte bootargs file, checks that its init= still names the exact system, then appends only volatile rdinit=/bin/sh. It uses the existing addresses and bootm 0x8000000 0x9000000 0x8400000. It never saves U-Boot environment or edits the normal boot selector/profile/files. Raw serial output is created with mode 0600 beneath /home/jadams/tmp/k230-coordination/ and is not sent to stdout or committed. The default log directory is /home/jadams/tmp/k230-coordination/mainline-initrd-private/ (mode 0700).

Before rebooting, a temporary helper streamed into /run verifies the fresh protected baseline in /home/jadams/tmp/k230-coherent-boot-board/received/after.json: installed system, profile, booted kernel, uname -r, normal init= selector, three services, and all eight protected boot/selector hashes. It also checks the four staged candidate files and the registration, store-paths, and SHA256SUMS hashes against the exact bundle, compares nix-store -qR with the staged closure, and runs nix-store --check-validity in batches. The helper and expected data are written only into /run.

Once the kernel version banner is seen, the controller sends a bounded shell command that first sets volatile PATH=/bin:/sbin, then mounts proc, sysfs, and devtmpfs in the in-memory initrd and records /proc/uptime, /proc/interrupts, /proc/cmdline, /proc/partitions, /proc/mounts, /sys/block, block device nodes, udev label links, and direct e2label results. It requires successful virtual mounts and at least one readable partition label. NIXOS_SD is recorded independently; a missing udev by-label link under direct rdinit is not treated as a cause. The sanitized result file records whether any partition reports that exact label. It does not send exit: the diagnostic shell is PID 1, and exiting it would panic the kernel. The command waits ten seconds and invokes /bin/reboot -ff as a child. Successful restoration is counted only after verifying the returned system, kernel, profile, kernel release, init= selector, three services, a new boot ID, and the same eight protected boot-file hashes. A login prompt alone is not recovery proof. If the kernel or shell does not reach the bounded markers, the operator must keep the board reservation and use the coordinator’s agreed physical power-cycle recovery; the controller clearly reports that PID 1 may still be the shell.

A responsive initrd shell would show that early userspace can run commands and report early device state. It would not prove stage2/root login, display usability, or touch acceptance. Existing logs show kernel/driver activity through roughly 7.22 seconds in the first boot. A read-only log audit also finds systemd waiting for /dev/disk/by-label/NIXOS_SD around 5.99 seconds; the kernel log detects the SD card and two partitions, but does not establish whether udev created that label link or whether the partition label matches. The captured directory listing and mounts will help distinguish root-device handoff from a broader early-userspace stall. The later truncated systemd debug line is not evidence of a failed sysctl write or udev failure. Task 5b.5 remains open until root login, deliberate touch interaction, and committed normal restoration evidence exist.

2026-10-01 staging repair and first corrected-path attempt

The first fresh preflight confirmed the protected normal system/profile, booted kernel, service state, and all eight baseline boot/selector hashes, then stopped at mainline-drm-normal-state.py:61: the candidate stage’s system symlink was absent. Read-only inventory found the four candidate load files and existing SHA256SUMS matched the immutable bundle, while registration and store-paths were absent. Only those two missing files were added from the exact bundle, with no-replace writes and board-side size/SHA-256 checks:

staged filebytesSHA-256
registration303363cbd962f866f3d715ab3e56cc30be01e3c0093588834df3d61a0e7e516847d865
store-paths4987605b84ef55caf96fd7785b1dbe9760e5276e64c424ac80ab24abce0794bf7509d

nix-store --check-validity passed for the exact candidate system before a no-replace symlink was created at the staging path; the resolved target was checked against that system. These changes are confined to the opt-in stage directory. The normal profile, normal boot files, selector files, and boot selection were not changed.

The next volatile rdinit=/bin/sh attempt reached the PID 1 shell and emitted its strict fresh-token probe marker, but returned RC=1. The raw private log showed that PATH resolution failed for cat, ls, mkdir, mount, reboot, and sleep. Therefore the accompanying NIXOS_SD=false value is invalid as a device observation: the probe did not inspect partitions or labels. Its reboot -ff command was also not found. Afterward the UART remained silent; one direct volatile-PATH /bin/reboot -ff attempt yielded zero bytes over 60 seconds. No corrected probe, normal recovery identity check, display check, or touch check was obtained. The controller now sets PATH=/bin:/sbin before the probe and invokes /bin/reboot -ff. Static inspection of the exact pinned kernel/initrd confirms the correction is compatible with that artifact: kernel startup initializes only HOME and TERM (init/main.c:198-199); the rdinit argument is selected before normal init (init/main.c:590-600), and the console is duplicated onto standard input/output/error (init/main.c:1631-1643). The pinned initrd’s /bin resolves to its executable environment, which contains Bash 5.3p15 and each external command used by the probe. Its reboot entry resolves to systemd 261.2’s systemctl; double-force reboot mode is implemented as a direct reboot syscall without asking the system manager (pinned systemctl source). The probe input is 1,934 bytes plus its line ending, below the pinned TTY canonical input limit of 4,096 bytes (drivers/tty/n_tty.c:59,1652-1653). These are source/artifact checks only. They do not establish that the corrected probe or reboot ran successfully on hardware. A passive capture following the requested physical reset expired after 15 minutes with no UART bytes or U-Boot, normal-kernel, or login markers. The reset and normal-system recovery were unobserved at that point. See the 2026-10-02 attempt and subsequent recovery below. Keep task 5b.5 open.

2026-10-02 observed power-swap recovery

The operator subsequently reported “power swapped it”. The fresh exclusive serial postflight passed: exact normal system/profile/kernel/init, Linux 6.6.36, all three shell services active, a new boot ID and unchanged hashes for all eight protected boot/selector files. Home is visible after injected DPMS/Home commands and transient-surface hide, with reviewed native and physical-camera captures. See recovery commands, result and limits. This supersedes the earlier unobserved-restoration status; it does not turn the failed PATH probe into valid partition evidence or satisfy mainline root-login/touch task 5b.5. The later corrected probe result is below.

2026-10-02 corrected PATH probe and recovery

At 2026-10-02T06:34:52Z, controller source revision b209129b1cf681669edfe3e1e05be317842bf6f4 ran one bounded trial from the exact gf3aqks2dg0z3ksz33ysfprnlbk83vw3 bundle and matching k9f4r2i9k9qj58z8z4l2kssy9rpwxxm1 system. The controller reached volatile bootm only after its strict five-load count/CRC checks and protected-normal preflight, including the selected p1 system/profile/kernel/init, three active services, eight protected boot-file hashes, staged artifact hashes, closure registration, and store validity. It observed the Linux 7.3.0-rc5 banner.

The corrected initrd command set PATH=/bin:/sbin and invoked external probe commands before its final marker. The controller did not receive a complete fresh-token K230_RDINIT_PROBE ... RC=... line within its 60-second bound and stopped without sending exit or issuing another command. Sanitized parsing of the private UART capture found one standalone K230_PROC marker, no complete later sys-block, device-block, by-label, ext4-label, NIXOS_SD, or reboot marker, and no login prompt. The full command echo including its token/final marker was not present. The kernel banner was followed by 89 timestamped kernel lines; the last reported candidate-kernel timestamp was 0.072493 seconds. A generic command-not-found string elsewhere in the capture could not be attributed to this probe. These observations do not establish what happened inside the subsequent proc read, and provide no disk-label, IRQ, or root-handoff evidence.

The raw capture remains outside Git at /home/jadams/tmp/k230-coordination/mainline-initrd-private/mainline-initrd-shell-20261002T063452Z.private.log, mode 0600, 43,726 bytes, SHA-256 984609649a9e790e43072e84a70830b4df3357346ceeda7c07b56362701d89df. The controller source SHA-256 for that run was 953e3dfdfa7e45f1c7781dd066e736698f19d59affd11b7ec30f142bbd174c10; the normal-state helper SHA-256 was 86bcf6f2c175d44d27f68015a9cc5a1ad7731ad50b6bbf3347859a4840cc7df7. No result JSON was written because the strict marker gate failed. The operator then performed a physical power-cycle; the coordinator independently verified the exact normal p1 system/profile/kernel/init, Linux 6.6.36, all three active services, unchanged eight protected hashes, and a new boot ID e7cf8096-44a0-4bdf-815d-2f1aec4126fb. This restores the normal system after this trial; reviewed native and physical Home captures are committed in corrected-probe recovery.

Host review found a false-failure case in the probe: e2label errors on any non-ext partition set the global RC to failure even if another partition had a readable label. The controller now reports unreadable per-device labels without failing the probe for that alone; the final gate still requires at least one readable ext label, while NIXOS_SD remains an independent exact observation. It also emits unique-token stage markers around mkdir, virtual mount checks, and proc/sys/device reads so a future bounded run can locate progress without treating command echo as execution. Generated survey command size is 2,885 bytes, below the 4,096-byte TTY canonical input limit. Focused host tests cover the strict stage parser and conservative label policy.

Host-only protocol revision (not physically run)

The controller’s default probe is now a short sequential discriminator rather than the full device survey. After the Linux banner, it retries only the safe built-in receipt line, with up to eight one-second windows and a fresh UUID for each attempt. It proceeds only after exactly one complete standalone receipt marker for the current attempt. This establishes that the serial shell processed that line; it does not establish general PID1 or userspace readiness. Exhaustion sends no external command and stops further probe input.

After receipt, the probe runs absolute /bin/true with its return code. Only when it returns zero does the controller create /proc in the volatile initramfs and attempt /bin/mount -t proc proc /proc. The exact initrd archive has no /proc directory, so this stage records separate mkdir RC, whether mount was attempted, and mount RC under strict fresh-token begin/end markers. The controller runs bracketed /bin/cat /proc/uptime only after both setup and mount return zero. It does not request reboot on a nonzero prerequisite RC; it writes a structured recovery-required result instead. A missing stage marker stops without further shell input and is classified as unknown completion.

After successful mount and uptime checks, the controller sends the bounded absolute /bin/reboot -ff request. The result schema is mainline-initrd-diagnostic-v3; the successful minimal probe schema is k230-initrd-minimal-v2, the survey is k230-initrd-survey-v2, and prerequisite failures have their own k230-initrd-prerequisite-failure-v1 schema. The controller detects the exact systemd chroot-refusal line only in the fresh serial slice after the reboot request, records it separately, and does not retry reboot. Normal login and the protected identity postflight remain mandatory for recovery proof.

The broader proc/interrupt/device/label survey remains available only through explicit --mode survey, and only after every minimal prerequisite succeeds: /bin/true, proc directory creation and mount, and the uptime read. It has a distinct result schema so a minimal run cannot be read as label or IRQ evidence. The focused fake-serial suite passes 21 tests, including early ignored input, retry exhaustion, echoed/stale markers, truncated/duplicate/missing/nonzero markers, timeouts, survey gating and reboot-marker absence. Python compilation, git diff --check, and strict OpenSpec validation pass. This host-only revision does not resolve why the previous probe stopped after K230_PROC; no additional board attempt was made with this revised setup. The earlier failed physical minimal attempt and its later independently verified recovery are recorded in docs/evidence/mainline-display/physical-2026-10-01/minimal-probe-2026-10-02/README.md. Task 5b.5 remains open.