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>
This commit is contained in:
Executable
+76
@@ -0,0 +1,76 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user