Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546#!/bin/sh# AC Native OS init — DRM direct boot with crash recoveryexport PATH="/bin:/sbin:/usr/bin:/usr/sbin"# Capture kernel-handoff timestamp as early as possible so the boot# timing log has a meaningful zero. /proc/uptime is the first reliable# wall-clock source (shell `date` is usually epoch-zero at this point).# Falls back to 0 if /proc isn't mounted yet — which we fix in the next# block anyway.BOOT_INIT_START_SEC=$(awk '{print $1}' /proc/uptime 2>/dev/null || echo 0)mount -t proc proc /proc 2>/dev/nullmount -t sysfs sysfs /sys 2>/dev/nullmount -t devtmpfs devtmpfs /dev 2>/dev/nullmkdir -p /dev/pts /dev/shm /tmp /run /etcmount -t devpts devpts /dev/pts -o ptmxmode=0666 2>/dev/nullmount -t tmpfs tmpfs /dev/shm 2>/dev/nullmount -t tmpfs tmpfs /tmp 2>/dev/nullmount -t tmpfs tmpfs /run 2>/dev/nullmount -t efivarfs efivarfs /sys/firmware/efi/efivars 2>/dev/null# Boot-phase timing helper. Appends "phase: +X.XXs @ T.TTs" to# /run/boot-timing so we can see where seconds are spent between# kernel handoff and ac-native launch. The full file is copied to# /mnt/boot-timing.log near the top of the pre-launch diagnostic# dump (when the USB config partition is available).BOOT_TIMING_LOG="/run/boot-timing": > "$BOOT_TIMING_LOG"_boot_mark() { local now=$(awk '{print $1}' /proc/uptime 2>/dev/null || echo 0) local delta=$(awk "BEGIN{printf \"%.3f\", $now - $BOOT_INIT_START_SEC}") printf '%-32s Δ%6ss @%7ss\n' "$1" "$delta" "$now" >> "$BOOT_TIMING_LOG" BOOT_INIT_START_SEC=$now}_boot_mark "kernel-handoff"# debugfs — needed for ASoC DAPM state inspection in audio-diag.# CONFIG_DEBUG_FS must be y in the kernel; mount early so everything# downstream (audio-diag, gpio dump, etc) can read it.mkdir -p /sys/kernel/debug 2>/dev/nullmount -t debugfs debugfs /sys/kernel/debug 2>/dev/null# zram swap — skipped by default. notepat fits in ~200 MB, every ThinkPad# we target has ≥4 GB RAM, and the zram modprobe + mkswap chain adds# ~100-200 ms to boot for no observable benefit. Set AC_ZRAM=1 on the# kernel cmdline if you ever need it back (e.g. low-memory tablet boot).case " $(cat /proc/cmdline 2>/dev/null) " in *" AC_ZRAM=1 "*) modprobe zram 2>/dev/null || true if [ -e /sys/block/zram0/disksize ] && [ -b /dev/zram0 ]; then echo 1G > /sys/block/zram0/disksize && mkswap /dev/zram0 >/dev/null 2>&1 && swapon /dev/zram0 2>/dev/null fi ;;esac# Loopbackip link set lo up 2>/dev/null# Restore baked Claude credentials (tmpfs mount hid the originals).# Two bake paths produce creds at /:# /claude-creds.json legacy full JSON (linux ac-os, reads ~/.claude/.credentials.json)# /claude-token plain OAuth bearer (macOS flash-mac.sh, MongoDB year-long token)# Either presence triggers setup of /tmp/.claude/ so Claude Code boots without# onboarding/login prompts. pty.c separately reads /claude-token to set# CLAUDE_CODE_OAUTH_TOKEN in the child's env.if [ -f /claude-creds.json ] || [ -f /claude-token ]; then mkdir -p /tmp/.claude [ -f /claude-creds.json ] && cp /claude-creds.json /tmp/.claude/.credentials.json cp /claude-state.json /tmp/.claude.json 2>/dev/null # Pin Claude's default model to the "sonnet" alias — Anthropic's # CLI resolves "sonnet" to the latest Sonnet family member, which # is currently Sonnet 4.7. Users who want a specific pinned version # can override by writing a different model string into this file. # `theme: auto` matches the terminal's light/dark mode AND skips the # first-run theme picker, so Claude Code launches straight to a session. printf '{"permissions":{"allow":["Bash(*)","Read(*)","Write(*)","Edit(*)","Glob(*)","Grep(*)","WebFetch(*)","WebSearch(*)"]},"autoUpdates":false,"installMethod":"native","model":"sonnet","theme":"auto"}\n' > /tmp/.claude/settings.jsonfiif [ -f /tangled-key ] || [ -f /tangled-ssh-config ] || [ -f /tangled-known-hosts ]; then mkdir -p /tmp/.ssh [ -f /tangled-key ] && cp /tangled-key /tmp/.ssh/tangled 2>/dev/null && chmod 600 /tmp/.ssh/tangled 2>/dev/null [ -f /tangled-ssh-config ] && cp /tangled-ssh-config /tmp/.ssh/config 2>/dev/null && chmod 600 /tmp/.ssh/config 2>/dev/null [ -f /tangled-known-hosts ] && cp /tangled-known-hosts /tmp/.ssh/known_hosts 2>/dev/null && chmod 600 /tmp/.ssh/known_hosts 2>/dev/nullfimkdir -p /tmp/ac[ -f /device-claude.md ] && cp /device-claude.md /tmp/ac/CLAUDE.md 2>/dev/null[ -f /device-score.md ] && cp /device-score.md /tmp/ac/SCORE.md 2>/dev/null# /etc/group and /etc/passwd needed by seatd. Use a full 7-field passwd# entry: dropbear resolves root's home (/root, where authorized_keys lives)# and shell from here — a truncated entry left pw_dir empty and broke# key-based ssh. Interactive shells still use HOME=/tmp (set below + pty.c).echo "root:x:0:" > /etc/groupecho "root:x:0:0:root:/root:/bin/sh" > /etc/passwd_boot_mark "tmpfs-mounts + creds"# Kick off USB mount in the background — independent of GPU probe, so# we overlap the two waits instead of running them serially. The main# thread then waits for GPU (below) and finally waits for this mount# result before continuing. Shaves up to ~2s on cold boot.modprobe vfat 2>/dev/nullmodprobe nls_cp437 2>/dev/nullmodprobe nls_ascii 2>/dev/nullUSB_MOUNTED=0USB_PARTS="/dev/sda1 /dev/sda2 /dev/sda3 /dev/sdb1 /dev/sdb2 /dev/sdb3 /dev/sdc1 /dev/sdc2 /dev/sdc3 /dev/sdd1 /dev/sdd2 /dev/sdd3 /dev/nvme0n1p1 /dev/nvme0n1p2 /dev/nvme0n1p3"mount_usb_partition() { pass="$1" for p in $USB_PARTS; do if [ -b "$p" ]; then mkdir -p /mnt mount -t vfat "$p" /mnt 2>/dev/null || continue if [ "$pass" = "config" ] && [ -f /mnt/config.json ]; then return 0 fi if [ "$pass" = "boot" ] && { [ -f /mnt/EFI/BOOT/BOOTX64.EFI ] || [ -f /mnt/EFI/BOOT/KERNEL.EFI ]; }; then return 0 fi umount /mnt 2>/dev/null fi done return 1}# Background USB mount: try up to 10 times with 1s delay, write result# to /run/usb-mounted so the foreground thread can check after GPU wait.( for attempt in 1 2 3 4 5 6 7 8 9 10; do mount_usb_partition config && { echo 1 > /run/usb-mounted; exit 0; } mount_usb_partition boot && { echo 1 > /run/usb-mounted; exit 0; } sleep 1 done echo 0 > /run/usb-mounted) &USB_MOUNT_PID=$!# Wait for GPU (up to 3 seconds) — runs in parallel with USB mount above.i=0while [ ! -e /dev/dri/card0 ] && [ ! -e /dev/dri/card1 ] && [ ! -e /dev/fb0 ] && [ $i -lt 300 ]; do usleep 10000 2>/dev/null || sleep 1 i=$((i+1))done# Converge: wait for the background USB mount to settle, pick up its result.wait $USB_MOUNT_PID 2>/dev/null[ -f /run/usb-mounted ] && USB_MOUNTED=$(cat /run/usb-mounted 2>/dev/null) && [ -z "$USB_MOUNTED" ] && USB_MOUNTED=0_boot_mark "gpu-wait + usb-mount converge"# Performance governor (silently skip if cpufreq not available)if [ -d /sys/devices/system/cpu/cpu0/cpufreq ]; then for g in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo performance > "$g" 2>/dev/null donefi# Disable PCI runtime-PM on every audio controller so the SOF DSP +# SSP DAI + MAX98360A speaker amp don't auto-suspend after ~20s of# silence. Symptom was sdmode=1 at boot, sdmode=0 ~23s later, and# then the amp never came back on when notepat tried to play notes.# The kernel cmdline already has snd_hda_intel.power_save=0, but SOF# uses its own runtime PM policy; this is the portable backstop.for p in /sys/class/sound/card*/device/power/control \ /sys/bus/pci/drivers/sof-audio-pci-intel-icl/*/power/control \ /sys/bus/pci/drivers/snd_hda_intel/*/power/control; do [ -w "$p" ] && echo on > "$p" 2>/dev/nulldoneexport SSL_CERT_FILE="/etc/pki/tls/certs/ca-bundle.crt"export CURL_CA_BUNDLE="/etc/pki/tls/certs/ca-bundle.crt"export SSL_CERT_DIR="/etc/ssl/certs"export HOME="/tmp"# Claude Code CLI (/bin/claude) reads SHELL to know which interpreter to# spawn for its Bash tool. Without this it errors with "SHELL env var# isn't set". /bin/sh is busybox — good enough to run short commands.# TERM gives rich output; COLORTERM hints truecolor so the CLI uses colors.export SHELL="/bin/sh"export TERM="${TERM:-xterm-256color}"export COLORTERM="truecolor"export EDITOR="/bin/vi"# USB mount already settled above (kicked off in parallel with GPU wait).# USB_MOUNTED is populated from /run/usb-mounted at that wait() point.# Create samples directory on boot media + mount point for music USBif [ "$USB_MOUNTED" = "1" ]; then mkdir -p /mnt/samplesfimkdir -p /mediaif [ -x /scripts/usb-midi-gadget.sh ]; then if [ "$USB_MOUNTED" = "1" ] && [ -f /mnt/config.json ] && grep -Eq '"usbMidi"[[:space:]]*:[[:space:]]*true' /mnt/config.json 2>/dev/null; then /scripts/usb-midi-gadget.sh up >/tmp/usb-midi-gadget.log 2>&1 || true else /scripts/usb-midi-gadget.sh down >/tmp/usb-midi-gadget.log 2>&1 || true fifi# Run ac-native in a loop — if it crashes, restart; if clean exit, shutdownexport LD_LIBRARY_PATH="/lib64:/usr/lib64:${LD_LIBRARY_PATH:-}"# Pre-launch diagnostics — useful for audio probe / GPU / PCI debugging,# but the full dump writes ~60 KB to FAT32 (slow USB), does dozens of# shell forks over sysfs, and on older ThinkPads adds 1–2 s of wall time# to boot. The whole block now runs in a backgrounded subshell so# ac-native launches immediately; the dump completes concurrently while# the splash fade is drawing. Downstream tools (os-install-report,# post-mortem analysis) still get the same pre-launch.log at the same# path — just a second later than before.## Set AC_NOLAUNCH_DIAG=1 on the kernel cmdline to skip the dump entirely# (useful for locked-down production kiosks that don't want the sysfs# scraping cost at all)._boot_mark "power-governor + sound PM"# Debug console writes to /dev/tty0 have been removed — they flashed# raw "[init] USB_MOUNTED=1" style text over the splash for ~1s on# every boot. Everything useful is still captured in the pre-launch# log + the new /mnt/boot-timing.log, visible post-hoc on the USB.if [ "$USB_MOUNTED" = "1" ]; then LOG=/mnt/pre-launch.logelse LOG=/tmp/pre-launch.logfi_pre_launch_diag() {echo "=== PRE-LAUNCH ===" > $LOGls /dev/dri/ >> $LOG 2>&1echo "binary: $(ls -la /ac-native 2>&1)" >> $LOGecho "build: $(cat /etc/ac-build 2>&1)" >> $LOGecho "usb_mounted: $USB_MOUNTED" >> $LOGecho "gpu:" >> $LOGls -la /dev/dri/ >> $LOG 2>&1ls -la /dev/fb* >> $LOG 2>&1echo "block devs:" >> $LOGls /dev/sd* /dev/nvme* /dev/mmcblk* 2>/dev/null >> $LOGecho "net ifaces:" >> $LOGls /sys/class/net/ 2>/dev/null >> $LOGecho "rfkill:" >> $LOGfor rf in /sys/class/rfkill/rfkill*; do [ -d "$rf" ] || continue echo " $(basename $rf): type=$(cat $rf/type 2>/dev/null) soft=$(cat $rf/soft 2>/dev/null) hard=$(cat $rf/hard 2>/dev/null)" >> $LOGdoneecho "sound cards:" >> $LOGcat /proc/asound/cards 2>/dev/null >> $LOGecho "pci devices (storage/audio/net):" >> $LOGif [ -d /sys/bus/pci/devices ]; then for d in /sys/bus/pci/devices/*; do [ -d "$d" ] || continue class=$(cat $d/class 2>/dev/null) # 0x0c* = serial bus (eMMC), 0x0104* = RAID, 0x0106* = SATA, 0x0108* = NVMe # 0x0280* = network wireless, 0x0403* = audio case "$class" in 0x010*|0x028*|0x040*|0x080501|0x0805*) ven=$(cat $d/vendor 2>/dev/null) dev=$(cat $d/device 2>/dev/null) drv=$(basename $(readlink $d/driver 2>/dev/null) 2>/dev/null) echo " $(basename $d) class=$class ven=$ven dev=$dev drv=${drv:-NONE}" >> $LOG ;; esac donefiecho "=== DMESG (kernel ring) ===" >> $LOG# Kick off two /dev/kmsg readers in the background — do NOT block boot on# them. The foreground script used to sleep 15s here waiting for late SOF# machine-driver probes, which put a hard 15-second floor under every boot.# Instead:# - One reader appends to pre-launch.log for ~15s, then exits on its own.# - A persistent reader writes to /mnt/kmsg.log for the entire session so# runtime issues (audio probe failures, driver unbinds, OOMs) are still# captured. /init's wait-on-child keeps both alive long enough.if [ "$USB_MOUNTED" = "1" ]; then ( cat /dev/kmsg >> /mnt/kmsg.log 2>&1 ) & ( (cat /dev/kmsg & CAT_PID=$!; sleep 15; kill $CAT_PID 2>/dev/null) >> $LOG 2>&1 ) &fi# Snapshot devices + sound AFTER the ring-buffer dump so we see the state# that existed when drivers had their chance to probe.echo "=== POST-PROBE SNAPSHOT ===" >> $LOGecho "net ifaces:" >> $LOGls /sys/class/net/ 2>/dev/null >> $LOGecho "sound cards:" >> $LOGcat /proc/asound/cards 2>/dev/null >> $LOGecho "pci drivers:" >> $LOGfor d in /sys/bus/pci/devices/*; do [ -d "$d" ] || continue class=$(cat $d/class 2>/dev/null) case "$class" in 0x010*|0x028*|0x040*|0x0805*) drv=$(basename $(readlink $d/driver 2>/dev/null) 2>/dev/null) echo " $(basename $d) class=$class drv=${drv:-NONE}" >> $LOG ;; esacdone# Audio codec ACPI probe — if SOF isn't creating a card on this machine,# the usual cause is that coreboot's DSDT doesn't declare the RT5682 /# MAX98360A / etc. ACPI devices with the HIDs SOF's JSL match table looks# for (10EC5682, MX98360A, DLGS7219…). Log what the kernel actually sees# on the ACPI bus so we can cross-reference against sof's allowlist.echo "ACPI audio codecs:" >> $LOGfor d in /sys/bus/acpi/devices/*; do [ -d "$d" ] || continue hid=$(cat "$d/hid" 2>/dev/null) case "$hid" in 10EC*|RTL*|MX98*|DLGS*|ESSX*|1013*) path=$(basename "$d") status=$(cat "$d/status" 2>/dev/null) echo " hid=$hid acpi=$path status=$status" >> $LOG ;; esacdone# All INT* HIDs — need to know what pinctrl device (INT34C8 for JSL, INT34C5# for ICL, INT3450-INT3456 for other Intel platforms) is actually declared# on this firmware. Absence of any of these is the reason the Jasper Lake# pinctrl driver sits idle: the driver is linked and module_init ran, but# acpi_driver_match never fires without a matching device on the ACPI bus.echo "ACPI Intel HIDs (INT*):" >> $LOGfor d in /sys/bus/acpi/devices/*; do [ -d "$d" ] || continue hid=$(cat "$d/hid" 2>/dev/null) case "$hid" in INT*) drv=$(basename "$(readlink "$d/physical_node/driver" 2>/dev/null)" 2>/dev/null) echo " $hid drv=${drv:-NONE}" >> $LOG ;; esacdone# PNP + ACPI devices with the names coreboot uses for pinctrl on various# Intel platforms. Helps us find whether the board's DSDT simply uses a# different HID than upstream Linux expects.echo "ACPI pinctrl candidates:" >> $LOGfor d in /sys/bus/acpi/devices/*; do [ -d "$d" ] || continue hid=$(cat "$d/hid" 2>/dev/null) name=$(basename "$d") case "$hid:$name" in INT34*|*GPI*|*GPIO*|PNP0C*) echo " $hid $name" >> $LOG ;; esacdoneecho "SOF platform devices:" >> $LOGls /sys/bus/platform/devices/ 2>/dev/null | grep -iE "sof|jsl_rt5682|cs42l42|hda" >> $LOGecho "Platform drivers (audio-related):" >> $LOGls /sys/bus/platform/drivers/ 2>/dev/null | grep -iE "sof_|snd_|jsl|rt5682|max98|hda" >> $LOGecho "jsl_rt5682_def binding:" >> $LOGif [ -d /sys/bus/platform/devices/jsl_rt5682_def ]; then drv=$(basename "$(readlink /sys/bus/platform/devices/jsl_rt5682_def/driver 2>/dev/null)" 2>/dev/null) echo " driver=${drv:-UNBOUND}" >> $LOG ls /sys/bus/platform/devices/jsl_rt5682_def/ >> $LOG 2>&1fiecho "Sound class:" >> $LOGls /sys/class/sound/ 2>/dev/null >> $LOG# Per-PCM names — SOF topologies name PCMs like "Headset (*)", "Speakers (*)",# "HDMI1 (*)" in /proc/asound/card0/pcmNp/info. If ac-native is opening the# wrong PCM (speaker vs headphone), this answers it in one glance.echo "Card 0 PCM names:" >> $LOGfor p in /proc/asound/card0/pcm*p/info; do [ -r "$p" ] || continue dev=$(dirname "$p" | sed 's|.*/||') name=$(grep -E '^name:' "$p" 2>/dev/null | head -1) id=$(grep -E '^id:' "$p" 2>/dev/null | head -1) echo " $dev: $id / $name" >> $LOGdoneecho "ASoC debugfs:" >> $LOGmount -t debugfs debugfs /sys/kernel/debug 2>/dev/nullls /sys/kernel/debug/asoc/ 2>/dev/null >> $LOGcat /sys/kernel/debug/asoc/components 2>/dev/null >> $LOGcat /sys/kernel/debug/asoc/platforms 2>/dev/null >> $LOGcat /sys/kernel/debug/asoc/dais 2>/dev/null | head -20 >> $LOGecho "Loaded audio modules/built-in symbols:" >> $LOGcat /proc/modules 2>/dev/null | grep -iE "sof|snd|rt5682|max98" >> $LOG# Even built-in drivers show as symbols in /proc/kallsyms — confirms which# drivers linked into this kernel image. Broadened to also detect whether# PINCTRL_JASPERLAKE actually took effect (its symbols show up with# jsl_pinctrl_/pinctrl_jsl_ / jasperlake_ prefixes).grep -E " sof_rt5682| snd_sof_| jsl_pinctrl| pinctrl_jsl| jasperlake_| max98357" /proc/kallsyms 2>/dev/null | head -20 >> $LOG# Running kernel .config (IKCONFIG_PROC=y) — grep for our critical audio# symbols so we can definitively see whether olddefconfig accepted them.if [ -r /proc/config.gz ]; then echo "Running kernel config (audio+gpio):" >> $LOG zcat /proc/config.gz 2>/dev/null | grep -E "^CONFIG_(PINCTRL_JASPERLAKE|PINCTRL_INTEL|PINCTRL_METEORLAKE|GPIOLIB|GPIO_ACPI|I2C_DESIGNWARE|SND_SOC_MAX98357A|SND_SOC_INTEL_SOF_RT5682)=" >> $LOGfiecho "GPIO chips:" >> $LOGfor d in /sys/bus/gpio/devices/*; do [ -d "$d" ] || continue name=$(basename "$d") ngpio=$(cat "$d/dev" 2>/dev/null) label=$(cat "$d/label" 2>/dev/null) base=$(cat "$d/base" 2>/dev/null) # The device's parent is the platform_device — e.g. INT34C8:00 for JSL. parent=$(basename "$(readlink "$d/../../" 2>/dev/null)" 2>/dev/null) echo " $name label='$label' base=$base parent=$parent" >> $LOGdone# MAX98357A's GPIO consumer state — if the amp driver successfully got a# gpiod handle for SD_MODE, this directory has an `enable` gpio-* file.echo "MAX98357A GPIO consumers:" >> $LOGfor d in /sys/bus/platform/devices/MX98360A:00/ /sys/bus/platform/devices/MAX98357A:*/; do [ -d "$d" ] || continue ls -la "$d" 2>/dev/null | grep -iE 'gpio|sdmode|enable|of_node' | head -10 >> $LOGdone# gpio descriptor consumers — shows which driver holds which pin.if [ -r /sys/kernel/debug/gpio ]; then echo "gpio descriptor consumers (debugfs):" >> $LOG head -60 /sys/kernel/debug/gpio >> $LOG 2>&1fi# MAX98357A amp state — if the driver is bound but SD_MODE GPIO isn't# assigned, the amp stays in power-down mode and speakers are silent even# though the sound card registers cleanly. Dump its device dir so we see# what resources actually came through.echo "MAX98357A (speaker amp) state:" >> $LOGfor d in /sys/bus/platform/devices/*; do name=$(basename "$d") case "$name" in MX98360A*|MAX98357A*|MAX98360A*|i2c-MX98*) echo " ${name}:" >> $LOG ls -la "$d" 2>/dev/null | head -20 >> $LOG [ -L "$d/driver" ] && echo " driver=$(basename $(readlink $d/driver))" >> $LOG ;; esacdoneecho "ALSA mixer (card 0):" >> $LOGif command -v amixer >/dev/null 2>&1; then amixer -c 0 contents 2>/dev/null | head -60 >> $LOGelse echo " amixer not in initramfs (add to bundling if needed)" >> $LOGfiecho "DAPM widgets (speaker path):" >> $LOGfor d in /sys/kernel/debug/asoc/sof-rt5682/dapm/Speaker /sys/kernel/debug/asoc/card0/dapm/Speaker; do [ -r "$d" ] && { echo " $d:"; cat "$d"; } >> $LOG 2>&1doneecho "=== CPUINFO ===" >> $LOGhead -30 /proc/cpuinfo >> $LOG 2>&1echo "=== CMDLINE ===" >> $LOGcat /proc/cmdline >> $LOG 2>&1sync} # end _pre_launch_diag# Launch pre-launch diagnostics in the background unless cmdline disables.case " $(cat /proc/cmdline 2>/dev/null) " in *" AC_NOLAUNCH_DIAG=1 "*) : ;; *) _pre_launch_diag & ;;esac# GPU + USB state used to echo here as raw "[init] GPU: card0 USB=1"# over the splash. Dropped — both are captured in the pre-launch log# and the new boot-timing log for post-mortem review.# Start Swank server in background (if SBCL image exists)SWANK_PID=0if [ -x /ac-swank ]; then LD_LIBRARY_PATH="/lib64:/usr/lib64" /ac-swank --swank-only >/dev/null 2>&1 & SWANK_PID=$!fi_boot_mark "pre-ac-native launch"# Copy the timing log to USB before ac-native takes over stdout/err# — so the user can post-mortem "how long did each phase take" right# off the stick without having to dig into kmsg.if [ "$USB_MOUNTED" = "1" ]; then cp /run/boot-timing /mnt/boot-timing.log 2>/dev/nullfi# Main loop — ac-native runs as a child process (not PID 1).# First attempt: with SDL3/GPU env vars.# If it crashes (signal), retry without SDL (AC_NO_SDL=1).# If that also crashes, keep retrying without SDL with backoff.## All launch / exit / crash messages are appended to /mnt/ac-crash.log# (persistent) and the stderr log. We no longer write them to /dev/tty0# because that paints raw text on top of the splash during the boot# transition.CRASH_COUNT=0SDL_ENABLED=1while true; do if [ "$SDL_ENABLED" = "1" ]; then LD_LIBRARY_PATH="/lib64" LIBGL_DRIVERS_PATH="/lib64/dri" GBM_DRIVERS_PATH="/lib64/dri" MESA_LOADER_DRIVER_OVERRIDE=iris \ /ac-native /piece.mjs >>/mnt/ac-native-stdout.log 2>>/mnt/ac-native-stderr.log & else LD_LIBRARY_PATH="/lib64" AC_NO_SDL=1 \ /ac-native /piece.mjs >>/mnt/ac-native-stdout.log 2>>/mnt/ac-native-stderr.log & fi CHILD_PID=$! wait $CHILD_PID EXIT_CODE=$? # Exit info → log files only (not tty0; the splash stays clean). if [ "$USB_MOUNTED" = "1" ]; then echo "[init] ac-native pid=$CHILD_PID exit=$EXIT_CODE crash=$CRASH_COUNT sdl=$SDL_ENABLED $(date 2>/dev/null)" >> /mnt/ac-init.log fi # Save crash info to USB if mounted if [ "$USB_MOUNTED" = "1" ]; then echo "exit=$EXIT_CODE crash=$CRASH_COUNT $(date 2>/dev/null)" >> /mnt/ac-crash.log cp /tmp/ac-native-stderr.log /mnt/ac-native-stderr.log 2>/dev/null sync fi if [ "$EXIT_CODE" = "0" ]; then sync # Suppress all kernel output before shutdown echo 0 > /proc/sys/kernel/printk 2>/dev/null printf '\033[?25l\033[2J' > /dev/tty0 2>/dev/null poweroff -f 2>/dev/null echo o > /proc/sysrq-trigger 2>/dev/null sleep 30 break fi if [ "$EXIT_CODE" = "2" ]; then sync echo 0 > /proc/sys/kernel/printk 2>/dev/null printf '\033[?25l\033[2J' > /dev/tty0 2>/dev/null # Enable all sysrq if kernel supports it (belt-and-suspenders) echo 1 > /proc/sys/kernel/sysrq 2>/dev/null # Try every reboot path in order — different kernels accept different ones reboot -f 2>/dev/null echo b > /proc/sysrq-trigger 2>/dev/null # Last resort: direct reboot(2) syscall via busybox `reboot` busybox reboot -f 2>/dev/null halt -f 2>/dev/null sleep 5 # If we're still here, kernel panic is preferable to a silent freeze echo c > /proc/sysrq-trigger 2>/dev/null sleep 5 break fi CRASH_COUNT=$((CRASH_COUNT + 1)) # If crashed with SDL enabled, disable it and retry immediately if [ "$SDL_ENABLED" = "1" ] && [ "$EXIT_CODE" -gt 128 ]; then # Exit > 128 = killed by signal (128 + signal number) SIG=$((EXIT_CODE - 128)) SDL_ENABLED=0 if [ "$USB_MOUNTED" = "1" ]; then echo "sdl_crash: signal=$SIG, disabling SDL" >> /mnt/ac-crash.log sync fi sleep 1 continue fi # Flash screen red via framebuffer if [ -c /dev/fb0 ]; then dd if=/dev/zero bs=4096 count=512 2>/dev/null | tr '\0' '\377' > /dev/fb0 2>/dev/null fi sleep 2done