#!/bin/bash # pre-poweroff-load.sh - fixed SMBus load protocol for the A5 hang observation # window (TODO.md E1), fully automatic: runs the load, saves observations, # powers the machine off. # # Mixed, deterministic workload so that every poweroff in the window carries # the same, high and *diverse* bus exposure - both in the use_ecam=1 phase and # in the use_ecam=0 counter-proof phase. Each 3-minute block: # # 1. software effect (~2760 SMBus writes/s, steady stream) ~100 s # 2. rapid hw/sw switch burst (6 alternations back-to-back: # one-shot config-write bursts + apply latches) ~20 s # 3. hardware effect (engine-side, bus quiet) ~30 s # 4. full SPD dump of all 4 DIMMs via ee1004 (reads + # page-selects) + serial integrity check ~30 s # # Effects rotate through fixed lists, so run N of phase A and run N of phase B # execute the identical sequence. # # End of run: kernel-journal warnings and run context are saved to a per-run # file in tools/pre-poweroff-runs/, one summary line is appended to # pre-poweroff-load.log, and - only with --poweroff - the machine POWERS OFF # (15 s grace, Ctrl-C to abort). The last software effect is left running into # the poweroff on purpose: exposure should cover the shutdown path too; the # systemd boot scene restores the normal LED state at next boot. # # The one exception: an SPD serial mismatch saves everything and does NOT # power off - investigate before shutting down. # # Usage: sudo ./pre-poweroff-load.sh [minutes] [--poweroff] # (default 30 minutes, in 3-min blocks; re-execs itself under sudo if # started as a plain user; powers off at the end ONLY with --poweroff) set -eu [ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@" MINUTES=30 POWEROFF=0 for arg in "$@"; do case "$arg" in --poweroff) POWEROFF=1 ;; *) MINUTES="$arg" ;; esac done BLOCKS=$(((MINUTES + 2) / 3)) DIR="$(cd "$(dirname "$0")" && pwd)" LOG="$DIR/pre-poweroff-load.log" RUNDIR="$DIR/pre-poweroff-runs" START="$(date '+%Y-%m-%d %H:%M:%S')" RUNFILE="$RUNDIR/$(date '+%Y%m%d-%H%M').log" ANIMS=(aurora wave_rainbow plasma ripple gradient_pulse fire) FX=(respiro arcobaleno cometa battito ciclico lampeggio) # SPD EEPROMs at 0x50/0x52 (ee1004); i2c bus numbers depend on module load # order, so discover them instead of hardcoding bus IDs. mapfile -t EEPROMS < <(ls /sys/bus/i2c/devices/*-005[02]/eeprom 2>/dev/null | sort) [ "${#EEPROMS[@]}" -eq 4 ] || { echo "ERROR: expected 4 SPD EEPROMs, found ${#EEPROMS[@]}"; exit 1; } MODE="$(cat /sys/module/i2c_imc_skylake/parameters/use_ecam 2>/dev/null || echo '?')" SRCV="$(cat /sys/module/i2c_imc_skylake/srcversion 2>/dev/null || echo '?')" mkdir -p "$RUNDIR" spd_serials() { local e for e in "${EEPROMS[@]}"; do dd if="$e" bs=1 skip=325 count=4 2>/dev/null | xxd -p done } echo "== pre-poweroff load: ${BLOCKS} blocks (~$((BLOCKS * 3)) min), use_ecam=${MODE}, srcversion=${SRCV}" if [ "$POWEROFF" -eq 1 ]; then echo "== POWERS OFF at the end. Run file: ${RUNFILE}" else echo "== no poweroff at the end (use --poweroff). Run file: ${RUNFILE}" fi REF_SERIALS="$(spd_serials)" echo "== SPD serial reference: $(echo "$REF_SERIALS" | tr '\n' ' ')" SPD_BAD=0 for ((b = 0; b < BLOCKS; b++)); do anim="${ANIMS[b % ${#ANIMS[@]}]}" fx="${FX[b % ${#FX[@]}]}" echo "-- block $((b + 1))/${BLOCKS}: anim=${anim} fx=${fx}" furyrs anim "$anim" sleep 100 # rapid hw/sw alternation burst for ((i = 0; i < 3; i++)); do furyrs effect "${FX[(b + i) % ${#FX[@]}]}" sleep 2 furyrs anim "${ANIMS[(b + i) % ${#ANIMS[@]}]}" sleep 2 done furyrs effect "$fx" sleep 30 # full SPD dump (reads + ee1004 page-selects) + integrity check for e in "${EEPROMS[@]}"; do dd if="$e" bs=64 2>/dev/null >/dev/null done if [ "$(spd_serials)" != "$REF_SERIALS" ]; then SPD_BAD=1 echo "!! SPD SERIAL MISMATCH in block $((b + 1)) - stopping, will NOT power off" break fi done # leave a steady software effect running into the poweroff furyrs anim "${ANIMS[0]}" # Only warning-and-up: the dev_dbg per-transfer traffic is not an anomaly. WARNS="$(journalctl -k -p warning --since "$START" --no-pager 2>/dev/null | grep -i 'i2c-imc' || true)" N="$(printf '%s' "$WARNS" | grep -c . || true)" { echo "start: ${START}" echo "end: $(date '+%Y-%m-%d %H:%M:%S')" echo "use_ecam: ${MODE}" echo "srcversion: ${SRCV}" echo "blocks: ${BLOCKS}" echo "spd_ok: $((1 - SPD_BAD))" echo "spd_serials_ref:" echo "$REF_SERIALS" echo "driver_warnings: ${N}" [ "$N" -gt 0 ] && echo "$WARNS" } >"$RUNFILE" echo "$(date '+%Y-%m-%d %H:%M') use_ecam=${MODE} srcversion=${SRCV} blocks=${BLOCKS} warns=${N} spd_ok=$((1 - SPD_BAD))" >>"$LOG" [ -n "${SUDO_UID:-}" ] && chown "$SUDO_UID:${SUDO_GID:-$SUDO_UID}" "$RUNFILE" "$LOG" "$RUNDIR" sync if [ "$SPD_BAD" -ne 0 ]; then echo "== SPD integrity FAILED - observations in ${RUNFILE} - NOT powering off." exit 1 fi if [ "$N" -gt 0 ]; then echo "== ${N} driver warning(s) - SIGNAL, saved in ${RUNFILE}:" echo "$WARNS" else echo "== clean run, no driver warnings." fi if [ "$POWEROFF" -eq 1 ]; then echo "== powering off in 15 s (Ctrl-C to abort) ..." sleep 15 systemctl poweroff else echo "== done, NOT powering off (pass --poweroff to shut down at the end)." fi