tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1182Shell 87Rust 63Markdown 541592 Definiciones

⎇ 2e4cfcaa6b78066e05fa0d49c8d7c8ebf0a055d6 scripts/efi-flicker-test.sh Shell · 88 líneas · 5.4 KB Historia crudo

arranque-grafo: cero-parpadeo VERDE + fix marcador INIT-OK tras switch_root 3ac329ec8 · sergio ·

1#!/usr/bin/env bash
2# efi-flicker-test.sh — valida el CERO-PARPADEO (ADR 0010 paso 4) del kernel metal, por captura de
3# framebuffer en OVMF-GOP. Arma el disco EFI real (install-image-efi.sh, cmdline `quiet` horneada +
4# FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER) y lo bootea en OVMF.
5#
6# Criterio (robusto y honesto con el metal): el kernel logra cero-parpadeo si NO TOCA el framebuffer —
7# lo deja tal como la firmware lo entregó. Con deferred-takeover + quiet, fbcon nunca toma el FB, así que
8# el contenido de la firmware (en OVMF: el splash TianoCore + líneas BdsDxe; en metal: el logo del
9# fabricante) SOBREVIVE intacto hasta que un compositor (mirada, no presente acá) abre el DRM master. Por
10# eso NO medimos "pantalla negra absoluta" (el splash de OVMF daría falso-positivo y NO existe en metal),
11# sino la CONTRIBUCIÓN del kernel: capturamos temprano (t≈BASE_T, justo tras el handoff) y tarde (t≈WAIT)
12# y exigimos que la media de brillo casi no cambie ⇒ el kernel no pintó nada. Un kernel verboso saltaría
13# de ~3 (sólo splash) a ~11 (splash + texto de boot).
14#
15# NOTA: el *seamless* i915 (firmware GOP → simplefb → i915 sin re-modeset) sólo se valida en metal Intel
16# real; OVMF no trae i915.
17#
18# Uso:  ./scripts/efi-flicker-test.sh
19# Vars: ROOTFS KERNEL MEM (def 2048) BASE_T (def 4) WAIT (def 16) DELTA_MAX (def 1.0) OVMF_CODE OVMF_VARS
20set -euo pipefail
21ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
22MEM="${MEM:-2048}"; BASE_T="${BASE_T:-4}"; WAIT="${WAIT:-16}"; DELTA_MAX="${DELTA_MAX:-1.0}"
23OVMF_CODE="${OVMF_CODE:-/usr/share/edk2/x64/OVMF_CODE.4m.fd}"
24OVMF_VARS="${OVMF_VARS:-/usr/share/edk2/x64/OVMF_VARS.4m.fd}"
25WORK="$ROOT/work/efi-flicker-test"; mkdir -p "$WORK"
26IMG="$WORK/hammer-efi-disk.img"; BASE="$WORK/base.ppm"; SHOT="$WORK/screen.ppm"; VARS="$WORK/vars.fd"; QMP="$WORK/qmp.sock"
27[ -r "$OVMF_CODE" ] && [ -r "$OVMF_VARS" ] || { echo "no encuentro OVMF (set OVMF_CODE/OVMF_VARS)"; exit 1; }
28command -v identify >/dev/null || { echo "falta imagemagick (identify)"; exit 1; }
29
30echo "==> armando el disco EFI con install-image-efi.sh (kernel metal actual)"
31IMG="$IMG" ROOTFS="${ROOTFS:-}" KERNEL="${KERNEL:-}" ./scripts/install-image-efi.sh >"$WORK/build.log" 2>&1 \
32    || { echo "✗ build falló — ver $WORK/build.log"; tail -15 "$WORK/build.log"; exit 1; }
33
34echo "==> confirmando que el kernel trae la cmdline quiet + deferred-takeover"
35KCFG=$(ls -dt store/*-linux-metal/boot/config-* 2>/dev/null | head -1)
36grep -q 'CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y' "$KCFG" && echo "  ✓ DEFERRED_TAKEOVER=y" \
37    || echo "  ! el kernel metal AÚN no tiene DEFERRED_TAKEOVER=y (¿rebuild pendiente?)"
38
39echo "==> boot en OVMF-GOP + 2 screendumps (t=${BASE_T}s handoff-firmware · t=${WAIT}s post-boot)"
40cp "$OVMF_VARS" "$VARS"; rm -f "$QMP"
41accel=(); [ -w /dev/kvm ] && accel=(-enable-kvm -cpu host)
42qemu-system-x86_64 -m "$MEM" -no-reboot "${accel[@]}" \
43    -drive if=pflash,unit=0,format=raw,readonly=on,file="$OVMF_CODE" \
44    -drive if=pflash,unit=1,format=raw,file="$VARS" \
45    -drive file="$IMG",format=raw,if=virtio \
46    -vga std -display none -serial file:"$WORK/serial.log" \
47    -qmp "unix:$QMP,server,nowait" >/dev/null 2>&1 &
48QPID=$!
49# QMP: capabilities → screendump a BASE_T → screendump a WAIT
50python3 - "$QMP" "$BASE" "$BASE_T" "$SHOT" "$WAIT" <<'PY'
51import socket, json, sys, time
52qmp, base, base_t, shot, wait = sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4], int(sys.argv[5])
53s=None
54for _ in range(60):
55    try: s=socket.socket(socket.AF_UNIX); s.connect(qmp); break
56    except OSError: time.sleep(0.25)
57if not s: print("NO-QMP"); sys.exit(2)
58f=s.makefile("rwb",buffering=0); f.readline()
59f.write(b'{"execute":"qmp_capabilities"}\n'); f.readline()
60time.sleep(base_t)
61f.write(json.dumps({"execute":"screendump","arguments":{"filename":base}}).encode()+b"\n"); f.readline()
62time.sleep(max(0, wait-base_t))
63f.write(json.dumps({"execute":"screendump","arguments":{"filename":shot}}).encode()+b"\n"); f.readline()
64time.sleep(0.5)
65PY
66kill "$QPID" 2>/dev/null || true; wait "$QPID" 2>/dev/null || true
67
68echo "==> analizando la CONTRIBUCIÓN del kernel al framebuffer"
69[ -r "$BASE" ] && [ -r "$SHOT" ] || { echo "✗ no se capturaron ambos framebuffers"; exit 1; }
70# %[fx:mean] = media de brillo NORMALIZADA 0..1 (fiable entre versiones/quantum). ×255 → 0..255.
71GEO=$(identify -format '%wx%h' "$SHOT")
72BMEAN=$(python3 -c "print(float('$(identify -format '%[fx:mean]' "$BASE")')*255)")
73FMEAN=$(python3 -c "print(float('$(identify -format '%[fx:mean]' "$SHOT")')*255)")
74DELTA=$(python3 -c "print(abs($FMEAN-$BMEAN))")
75printf "  framebuffer %s · media handoff=%.3f  post-boot=%.3f  Δ=%.3f / 255 (umbral: Δ < %.1f)\n" \
76    "$GEO" "$BMEAN" "$FMEAN" "$DELTA" "$DELTA_MAX"
77echo "  (la base incluye el splash de la firmware —TianoCore en OVMF—; en metal es el logo del fabricante)"
78
79echo
80if python3 -c "import sys; sys.exit(0 if float('$DELTA') < float('$DELTA_MAX') else 1)"; then
81    echo "✓✓ CERO-PARPADEO VERDE: el kernel NO tocó el framebuffer (Δ≈0) — el contenido de la firmware"
82    echo "   sobrevive intacto hasta que mirada abra el DRM. Sin texto de boot ni cursor pintados."
83    echo "   (El seamless i915 firmware→escritorio sólo se valida en metal Intel real.)"
84else
85    echo "✗ efi-flicker-test FALLÓ: el kernel modificó el framebuffer (Δ=${DELTA} ≥ ${DELTA_MAX})." >&2
86    echo "  Revisá $SHOT — texto de boot (¿kernel sin rebuild / sin quiet?) o getty en tty0." >&2
87    exit 1
88fi