Files
hammer/scripts/efi-flicker-test.sh
T
sergioandClaude Opus 4.8 e80f15b6bc arranque-grafo: harness EFI robustos a quiet + efi-flicker-test (ADR 0010 paso 4)
Con el kernel metal cero-parpadeo (quiet), los marcadores serie kernel-side
(EFI-stub/Linux-version/EXT4) desaparecen. Los /init de pivote + /sbin/init
real ahora tee'an marcadores a /dev/ttyS0 (escritura al puerto directo, sortea
el printk ⇒ sobreviven quiet): HAMMER-EFI-DISK-PIVOT-OK + HAMMER-EFI-INIT-OK.
efi-disk-boot-test y efi-install-test pasan a apoyarse en ésos (kernel-side =
informativos). Compatible con el kernel viejo (verbose) también.

efi-flicker-test.sh: valida cero-parpadeo por captura de framebuffer OVMF-GOP
(media de brillo < umbral ⇒ pantalla negra, sin texto de boot). El seamless
i915 queda para metal Intel real.

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

77 lines
4.3 KiB
Bash
Executable File
Raw 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), lo bootea en OVMF con una GPU emulada y hace `screendump` por
# QMP a los N s. Con deferred-takeover + quiet, fbcon NO toca el framebuffer ⇒ debe quedar NEGRO (media
# de brillo ≈ 0) hasta que un compositor (mirada, no presente acá) abra el DRM master.
#
# NOTA: el *seamless* i915 (firmware GOP → simplefb → i915 sin re-modeset) sólo se valida en metal Intel
# real; OVMF no trae i915. Lo que esto prueba es la mitad "pantalla limpia": sin texto de boot ni cursor.
#
# Uso: ./scripts/efi-flicker-test.sh
# Vars: ROOTFS KERNEL MEM (def 2048) WAIT (def 16) MEAN_MAX (def 1.5) OVMF_CODE OVMF_VARS
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
MEM="${MEM:-2048}"; WAIT="${WAIT:-16}"; MEAN_MAX="${MEAN_MAX:-1.5}"
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"; 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 + screendump a los ${WAIT}s"
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 → esperar → screendump
python3 - "$QMP" "$SHOT" "$WAIT" <<'PY'
import socket, json, sys, time, os
qmp, shot, wait = sys.argv[1], sys.argv[2], int(sys.argv[3])
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(wait)
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 el framebuffer capturado"
[ -r "$SHOT" ] || { echo "✗ no se capturó el framebuffer"; exit 1; }
# media de brillo normalizada 0..255 (imagemagick reporta en % o 0..1 según versión → forzamos 0..255)
MEAN=$(identify -verbose "$SHOT" | awk '/[Mm]ean:/ {gsub(/[()]/,""); print $2; exit}')
# si identify reportó fracción 0..1, escalar ×255
MEAN255=$(python3 -c "m=float('$MEAN'); print(m*255 if m<=1.0 else m)")
GEO=$(identify -format '%wx%h' "$SHOT")
printf " framebuffer %s · media de brillo = %.3f / 255 (umbral cero-parpadeo: < %.1f)\n" "$GEO" "$MEAN255" "$MEAN_MAX"
echo
if python3 -c "import sys; sys.exit(0 if float('$MEAN255') < float('$MEAN_MAX') else 1)"; then
echo "✓✓ CERO-PARPADEO VERDE: el framebuffer queda NEGRO durante el arranque (sin texto de boot ni cursor)."
echo " (El seamless i915 firmware→escritorio sólo se valida en metal Intel real.)"
else
echo "✗ efi-flicker-test FALLÓ: el framebuffer tiene contenido (media ${MEAN255}${MEAN_MAX})." >&2
echo " Revisá $SHOT — probablemente texto de boot (¿kernel sin rebuild?) o getty en tty0." >&2
exit 1
fi