Files
hammer/scripts/efi-flicker-test.sh
sergioandClaude Opus 4.8 3ac329ec8f arranque-grafo: cero-parpadeo VERDE + fix marcador INIT-OK tras switch_root
efi-flicker-test valida por CONTRIBUCIÓN del kernel al framebuffer (2 capturas:
handoff-firmware vs post-boot; Δ≈0 ⇒ el kernel no tocó el FB). Con el kernel
metal cero-parpadeo: Δ=0.000 VERDE — el splash de la firmware sobrevive intacto,
sin texto de boot ni cursor (el kernel viejo saltaba de ~3 a ~11). No mide negro
absoluto: el splash de OVMF (TianoCore) daría falso-positivo y no existe en metal.

Fix: busybox switch_root NO mueve /dev ⇒ los wrappers /sbin/init montan devtmpfs
antes de tee'ar HAMMER-EFI-INIT-OK a /dev/ttyS0 (si no, el nodo no existe y el
marcador se perdía). efi-disk-boot-test VERDE con el kernel quiet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 21:04:05 -04:00

89 lines
5.4 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# efi-flicker-test.sh — valida el CERO-PARPADEO (ADR 0010 paso 4) del kernel metal, por captura de
# framebuffer en OVMF-GOP. Arma el disco EFI real (install-image-efi.sh, cmdline `quiet` horneada +
# FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER) y lo bootea en OVMF.
#
# Criterio (robusto y honesto con el metal): el kernel logra cero-parpadeo si NO TOCA el framebuffer —
# lo deja tal como la firmware lo entregó. Con deferred-takeover + quiet, fbcon nunca toma el FB, así que
# el contenido de la firmware (en OVMF: el splash TianoCore + líneas BdsDxe; en metal: el logo del
# fabricante) SOBREVIVE intacto hasta que un compositor (mirada, no presente acá) abre el DRM master. Por
# eso NO medimos "pantalla negra absoluta" (el splash de OVMF daría falso-positivo y NO existe en metal),
# sino la CONTRIBUCIÓN del kernel: capturamos temprano (t≈BASE_T, justo tras el handoff) y tarde (t≈WAIT)
# y exigimos que la media de brillo casi no cambie ⇒ el kernel no pintó nada. Un kernel verboso saltaría
# de ~3 (sólo splash) a ~11 (splash + texto de boot).
#
# NOTA: el *seamless* i915 (firmware GOP → simplefb → i915 sin re-modeset) sólo se valida en metal Intel
# real; OVMF no trae i915.
#
# Uso: ./scripts/efi-flicker-test.sh
# Vars: ROOTFS KERNEL MEM (def 2048) BASE_T (def 4) WAIT (def 16) DELTA_MAX (def 1.0) OVMF_CODE OVMF_VARS
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
MEM="${MEM:-2048}"; BASE_T="${BASE_T:-4}"; WAIT="${WAIT:-16}"; DELTA_MAX="${DELTA_MAX:-1.0}"
OVMF_CODE="${OVMF_CODE:-/usr/share/edk2/x64/OVMF_CODE.4m.fd}"
OVMF_VARS="${OVMF_VARS:-/usr/share/edk2/x64/OVMF_VARS.4m.fd}"
WORK="$ROOT/work/efi-flicker-test"; mkdir -p "$WORK"
IMG="$WORK/hammer-efi-disk.img"; BASE="$WORK/base.ppm"; SHOT="$WORK/screen.ppm"; VARS="$WORK/vars.fd"; QMP="$WORK/qmp.sock"
[ -r "$OVMF_CODE" ] && [ -r "$OVMF_VARS" ] || { echo "no encuentro OVMF (set OVMF_CODE/OVMF_VARS)"; exit 1; }
command -v identify >/dev/null || { echo "falta imagemagick (identify)"; exit 1; }
echo "==> armando el disco EFI con install-image-efi.sh (kernel metal actual)"
IMG="$IMG" ROOTFS="${ROOTFS:-}" KERNEL="${KERNEL:-}" ./scripts/install-image-efi.sh >"$WORK/build.log" 2>&1 \
|| { echo "✗ build falló — ver $WORK/build.log"; tail -15 "$WORK/build.log"; exit 1; }
echo "==> confirmando que el kernel trae la cmdline quiet + deferred-takeover"
KCFG=$(ls -dt store/*-linux-metal/boot/config-* 2>/dev/null | head -1)
grep -q 'CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y' "$KCFG" && echo " ✓ DEFERRED_TAKEOVER=y" \
|| echo " ! el kernel metal AÚN no tiene DEFERRED_TAKEOVER=y (¿rebuild pendiente?)"
echo "==> boot en OVMF-GOP + 2 screendumps (t=${BASE_T}s handoff-firmware · t=${WAIT}s post-boot)"
cp "$OVMF_VARS" "$VARS"; rm -f "$QMP"
accel=(); [ -w /dev/kvm ] && accel=(-enable-kvm -cpu host)
qemu-system-x86_64 -m "$MEM" -no-reboot "${accel[@]}" \
-drive if=pflash,unit=0,format=raw,readonly=on,file="$OVMF_CODE" \
-drive if=pflash,unit=1,format=raw,file="$VARS" \
-drive file="$IMG",format=raw,if=virtio \
-vga std -display none -serial file:"$WORK/serial.log" \
-qmp "unix:$QMP,server,nowait" >/dev/null 2>&1 &
QPID=$!
# QMP: capabilities → screendump a BASE_T → screendump a WAIT
python3 - "$QMP" "$BASE" "$BASE_T" "$SHOT" "$WAIT" <<'PY'
import socket, json, sys, time
qmp, base, base_t, shot, wait = sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4], int(sys.argv[5])
s=None
for _ in range(60):
try: s=socket.socket(socket.AF_UNIX); s.connect(qmp); break
except OSError: time.sleep(0.25)
if not s: print("NO-QMP"); sys.exit(2)
f=s.makefile("rwb",buffering=0); f.readline()
f.write(b'{"execute":"qmp_capabilities"}\n'); f.readline()
time.sleep(base_t)
f.write(json.dumps({"execute":"screendump","arguments":{"filename":base}}).encode()+b"\n"); f.readline()
time.sleep(max(0, wait-base_t))
f.write(json.dumps({"execute":"screendump","arguments":{"filename":shot}}).encode()+b"\n"); f.readline()
time.sleep(0.5)
PY
kill "$QPID" 2>/dev/null || true; wait "$QPID" 2>/dev/null || true
echo "==> analizando la CONTRIBUCIÓN del kernel al framebuffer"
[ -r "$BASE" ] && [ -r "$SHOT" ] || { echo "✗ no se capturaron ambos framebuffers"; exit 1; }
# %[fx:mean] = media de brillo NORMALIZADA 0..1 (fiable entre versiones/quantum). ×255 → 0..255.
GEO=$(identify -format '%wx%h' "$SHOT")
BMEAN=$(python3 -c "print(float('$(identify -format '%[fx:mean]' "$BASE")')*255)")
FMEAN=$(python3 -c "print(float('$(identify -format '%[fx:mean]' "$SHOT")')*255)")
DELTA=$(python3 -c "print(abs($FMEAN-$BMEAN))")
printf " framebuffer %s · media handoff=%.3f post-boot=%.3f Δ=%.3f / 255 (umbral: Δ < %.1f)\n" \
"$GEO" "$BMEAN" "$FMEAN" "$DELTA" "$DELTA_MAX"
echo " (la base incluye el splash de la firmware —TianoCore en OVMF—; en metal es el logo del fabricante)"
echo
if python3 -c "import sys; sys.exit(0 if float('$DELTA') < float('$DELTA_MAX') else 1)"; then
echo "✓✓ CERO-PARPADEO VERDE: el kernel NO tocó el framebuffer (Δ≈0) — el contenido de la firmware"
echo " sobrevive intacto hasta que mirada abra el DRM. Sin texto de boot ni cursor pintados."
echo " (El seamless i915 firmware→escritorio sólo se valida en metal Intel real.)"
else
echo "✗ efi-flicker-test FALLÓ: el kernel modificó el framebuffer (Δ=${DELTA}${DELTA_MAX})." >&2
echo " Revisá $SHOT — texto de boot (¿kernel sin rebuild / sin quiet?) o getty en tty0." >&2
exit 1
fi