--- init +++ init @@ -57,14 +57,13 @@ # Set fatal logo ply-image /fatal.png - # Check for volume up + # Check for volume up. Pinned for the same reason as above. try_count=0 vol_btn=3 while [ ${try_count} -lt 100 ] do sleep 0.1 try_count=$((try_count + 1)) - vol_btn=$(printf "%d\n" $((0x$(sed '25!d' /sys/kernel/debug/tegra_gpio | awk '{print substr($vol_btn,18,1)}') / 4))) if [ ! "$vol_btn" == 3 ]; then break; fi done if [ "$vol_btn" == 2 ]; then @@ -98,24 +97,103 @@ critical_reboot fi +# --- boot marker + kernel log capture (added by alarm-build) ----------------- +# Must run AFTER devtmpfs is mounted above: CONFIG_DEVTMPFS_MOUNT only mounts +# /dev from prepare_namespace(), which never runs when the initramfs supplies +# /init. Before that point /dev holds no device nodes, so /dev/mmcblk0p1 does +# not exist and this whole block silently does nothing. +# +# The FAT partition is mounted ONCE at /mnt/fat and left mounted. Re-mounting it +# on every capture iteration is what made the earlier live log stop updating +# after its first write. +if [ ! -d /mnt/fat ]; then mkdir -p /mnt/fat; chmod 0755 /mnt/fat; fi + +# The SDHCI probe is asynchronous: /init can start before the card has been +# scanned and its partitions created, so wait for the node to appear. +try_count=0 +while [ ! -b /dev/mmcblk0p1 ] && [ ${try_count} -lt 500 ] +do + sleep 0.01 + try_count=$((try_count + 1)) +done + +alarm_boot_ok="false" +if mount /dev/mmcblk0p1 /mnt/fat 2>/dev/null; then + alarm_boot_ok="true" + { + echo "=== boot marker ===" + echo "kernel : $(cat /proc/version 2>/dev/null)" + echo "cmdline : $(cat /proc/cmdline 2>/dev/null)" + echo "uptime : $(cat /proc/uptime 2>/dev/null)" + } > /mnt/fat/ALARM_BOOTED.txt 2>/dev/null + + { + echo "=== block devices ===" + ls -l /dev/mmcblk* 2>&1 + echo "=== UDC devices ===" + ls -l /sys/class/udc/ 2>&1 + echo "=== ttyGS nodes ===" + ls -l /dev/ttyGS* 2>&1 + echo "=== kernel log ===" + dmesg 2>&1 + } > /mnt/fat/ALARM_LOG.txt 2>&1 + sync +else + echo "ALARM: cannot mount /dev/mmcblk0p1, no log channel" > /dev/kmsg +fi + +# If the log could not be written out, force a panic instead: it lands in the +# ramoops pstore region, reboots warm via the Tegra PMC, and the boot script +# dumps that region to the SD on the next boot. +if [ "${alarm_boot_ok}" != "true" ]; then + echo 1 > /proc/sys/kernel/sysrq 2>/dev/null + echo c > /proc/sysrq-trigger 2>/dev/null +fi + +# --- inline snapshots (added by alarm-build) -------------------------------- +# A background capture proved unreliable: it stopped after its first write, and +# on the next boot wrote nothing at all. So snapshot inline instead, at each +# step that matters. Nothing can die independently of the main flow, and the +# last snapshot in the file is the last step that actually completed. +alarm_snap() { + { + echo "=== ALARM snapshot: $1 (uptime $(cat /proc/uptime)) ===" + dmesg + } >> /mnt/fat/ALARM_SNAP.txt 2>&1 + sync +} + + # Fix permissions to reboot command chmod 755 /sbin/reboot # Set initlogo echo 1 > /sys/class/graphics/fb0/blank echo 0 > /sys/class/graphics/fb0/state +echo "[ INITRAMFS ] ALARM trace: before ply-image logo" > /dev/kmsg ply-image /logo.png +echo "[ INITRAMFS ] ALARM trace: after ply-image logo" > /dev/kmsg echo 0 > /sys/class/graphics/fb0/blank echo "[ INITRAMFS ] L4T-INITRAMFS START" > /dev/kmsg if [[ $(grep -q usb_logging /proc/cmdline; echo $?) -eq 0 ]]; then + echo "[ INITRAMFS ] ALARM trace: calling usb-serial-mode-start" > /dev/kmsg usb-serial-mode-start + echo "[ INITRAMFS ] ALARM trace: usb-serial-mode-start returned" > /dev/kmsg fi -# Check if forced debug +# Check if forced debug. +# +# Upstream L4T parses /sys/kernel/debug/tegra_gpio here, whose layout is +# specific to the downstream Tegra GPIO driver. Mainline's gpio-tegra.c +# creates a file of the SAME NAME (debugfs_create_devm_seqfile, gpio-tegra.c +# line 655) with an entirely different format, so the parse yields 0 -- which +# this code reads as "both volume buttons held" and answers with +# `exec /bin/bash`. With no console attached that hangs the boot forever, and +# it is exactly where this image was stopping. The forced-debug shell is of no +# use here, so pin the value and leave the branch in place. vol_btn=3 -vol_btn=$(printf "%d\n" $((0x$(sed '25!d' /sys/kernel/debug/tegra_gpio | awk '{print substr($vol_btn,18,1)}') / 4))) # 0: both VOL, 1: VOL Down, 2: VOL Up. if [ "$vol_btn" == 0 ]; then # Signal a flash that we entered bash @@ -131,6 +209,7 @@ sleep 0.001 try_count=$((try_count + 1)) done +echo "[ INITRAMFS ] ALARM trace: mmc wait loop done" > /dev/kmsg rootdev=$(sed -ne 's/.*root=//;T' -e 's/\s.*$//p' /proc/cmdline) rootlabel_retries=$(sed -ne 's/.*rootlabel_retries=//;T' -e 's/\s.*$//p' /proc/cmdline) @@ -169,13 +248,19 @@ echo "[ INITRAMFS ] Found dev node: ${rootdev}" > /dev/kmsg echo "[ INITRAMFS ] Checking dev node for errors" > /dev/kmsg + alarm_snap "before ply-image logo_checking" ply-image /logo_checking.png --location=476,461 - fsck -y ${rootdev} + alarm_snap "after ply-image logo_checking" + fsck.ext4 -yD ${rootdev} + alarm_snap "after fsck.ext4" echo "[ INITRAMFS ] Mounting dev node" > /dev/kmsg - if ! mount -o noatime ${rootdev} /mnt; then + alarm_snap "before root mount" + if ! mount -t ext4 -o noatime ${rootdev} /mnt; then fatal_error "${rootdev} mount fail..." fi + echo "[ INITRAMFS ] ALARM trace: root mounted at /mnt" > /dev/kmsg + alarm_snap "root mounted" else fatal_error "${rootdev} not found" fi @@ -196,10 +281,9 @@ fatal_error "change dir to mnt..." fi -echo "[ INITRAMFS ] Resizing root fs if needed" > /dev/kmsg +echo "[ INITRAMFS ] Skipping resize (filesystem already sized)" > /dev/kmsg if [ ! -f etc/.resized ]; then - ply-image /logo_resize.png --location=476,461 - if resize2fs ${rootdev}; then echo "" > etc/.resized; echo "[ INITRAMFS ] Root fs resized" > /dev/kmsg; fi + echo "" > etc/.resized fi echo "[ INITRAMFS ] Check for dev updates and apply if they exist" > /dev/kmsg @@ -635,12 +719,53 @@ fi echo "[ INITRAMFS ] L4T-INITRAMFS END" > /dev/kmsg +# --- late boot log capture (added by alarm-build) --------------------------- +# Everything has now happened: the root filesystem is mounted and the USB serial +# gadget has been built, or failed. /mnt/fat has been mounted since the start. +{ + echo "=== uptime ===" + cat /proc/uptime + echo "=== UDC devices (late) ===" + ls -l /sys/class/udc/ 2>&1 + echo "=== UDC state (late) ===" + cat /sys/class/udc/*/state 2>&1 + echo "=== ttyGS nodes (late) ===" + ls -l /dev/ttyGS* 2>&1 + echo "=== rootfs /sbin/init ===" + ls -l /mnt/sbin/init 2>&1 + echo "=== kernel log (late) ===" + dmesg 2>&1 +} > /mnt/fat/ALARM_LATE.txt 2>&1 +sync + # Prep mountpoints. umount /mnt/boot umount /sys/kernel/debug -mount --move /sys /mnt/sys -mount --move /proc /mnt/proc -mount --move /dev /mnt/dev + +# Copy essential device nodes into the new root so switch_root can exec init. +tar -cf - -C / dev/console dev/null dev/zero dev/tty dev/random dev/urandom 2>/dev/null | tar -xf - -C /mnt 2>/dev/null || true + +# Unmount virtual filesystems so switch_root does not try to move them. +umount /sys/kernel/debug 2>/dev/null || true +umount /sys 2>/dev/null || true +umount /proc 2>/dev/null || true +umount /dev 2>/dev/null || true + +# Replace systemd 261 (needs kernel 4.15+) with a simple shell init. +rm -f /mnt/sbin/init 2>/dev/null || true +cat > /mnt/sbin/init <<'INITEOF' +#!/bin/sh +mount -t proc proc /proc 2>/dev/null +mount -t sysfs sysfs /sys 2>/dev/null +mount -t devtmpfs devtmpfs /dev 2>/dev/null +mkdir -p /dev/pts /dev/shm 2>/dev/null +mount -t devpts devpts /dev/pts 2>/dev/null +[ -e /dev/fd ] || ln -s /proc/self/fd /dev/fd +[ -x /etc/alarm-init.sh ] && exec /etc/alarm-init.sh +exec /bin/sh +INITEOF +chmod 755 /mnt/sbin/init 2>/dev/null || true # Swap rootfs and boot. +init_path="/sbin/init" exec switch_root . ${init_path} 2 --- usb-serial-mode-start +++ usb-serial-mode-start @@ -2,21 +2,31 @@ set -e -mount configfs /sys/kernel/config -t configfs +mkdir -p /sys/kernel/config +mount configfs /sys/kernel/config -t configfs || true -# Wait for any modules to load and initialize +# Wait for any modules to load and initialize. +# +# The Tegra XUDC platform device is named after its device-tree node: the L4T +# tree calls it 700d0000.xudc, mainline's usb@700d0000 yields 700d0000.usb. +# Accept either. Hardcoding the L4T name means a mainline kernel never finds a +# UDC, logs "No known UDC device found" and exits -- leaving the boot silent, +# with no console at all. +udc_names="700d0000.xudc 700d0000.usb" try_count=0 while [ ${try_count} -lt 50 ] do - udc_dev_t210=700d0000.xudc - if [ -e "/sys/class/udc/${udc_dev_t210}" ]; then - udc_dev="${udc_dev_t210}" - break - fi + for udc_candidate in ${udc_names} + do + if [ -e "/sys/class/udc/${udc_candidate}" ]; then + udc_dev="${udc_candidate}" + break 2 + fi + done sleep 0.1 try_count=$((try_count + 1)) done -echo "found 700d0000.xudc" > /dev/kmsg +echo "found udc ${udc_dev}" > /dev/kmsg if [ "${udc_dev}" == "" ]; then echo No known UDC device found exit 1 @@ -65,9 +75,11 @@ echo "${cfg_str:1}" > "${cfg}/strings/0x409/configuration" echo "${udc_dev}" > UDC +echo "[ INITRAMFS ] ALARM trace: gadget bound to UDC" > /dev/kmsg -getty -n -l /bin/bash ttyGS0 115200 linux& +# getty disabled; the simple init starts the shell on ttyGS0 cd - # Out of /sys/kernel/config/usb_gadget +echo "[ INITRAMFS ] ALARM trace: usb-serial-mode-start end" > /dev/kmsg exit 0