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 1569Shell 203Rust 109Markdown 993728 Definiciones

🩺 EFI: el initramfs perdía la carrera contra la enumeración USB — y en silencio

Síntoma en metal ajeno: la pantalla se congela justo tras `sdc: sdc1 sdc2 sdc3 sdc4`
/ `Attached SCSI removable disk`. No estaba colgado.

Prueba de que el root nunca se montó: con `ignore_loglevel` un montaje ext4 imprime
`EXT4-fs (...): mounted filesystem` en pantalla sí o sí, y no aparecía.

Dos causas encadenadas:

1. CARRERA. El rdinit arranca en cuanto se desempaqueta el initramfs, pero el bus USB
   enumera asíncrono y tarda segundos (reset de hub, settling de 1s por dispositivo,
   scan SCSI). Los 5 reintentos de 1s no alcanzaban. En QEMU el disco es virtio y está
   desde el instante cero ⇒ la carrera NUNCA se veía en validación. Es el `rootwait`
   que no podemos usar porque no hay root= en el cmdline. Ahora espera 60s.

2. CEGUERA. El cmdline horneado es `console=tty0 console=ttyS0,115200` y /dev/console
   = la ÚLTIMA `console=` ⇒ el serial. El `exec sh` de rescate nacía invisible. Es el
   MISMO bug que costó el 1er viaje físico de KDE, una capa más temprano: allá se
   arregló para después del switch_root (getty en tty1) y el initramfs quedó ciego.
   Ahora el log del pivote va a /dev/tty0, el marcador INIT-OK también, y el rescate
   abre shell en tty1 listando los bloques visibles.

+ `timeout 15` a `hammer boot menu`: corre ANTES del exec de arje-zero ⇒ colgarse ahí
  deja un arranque sin PID1 ni pantalla, indistinguible de un kernel muerto. El
  `|| true` protegía del fallo, no del bloqueo.

Validado arrancando la imagen COMO USB (qemu-xhci + usb-storage, -serial null, con
pantalla): marcadores del pivote visibles, motd y prompt `/ #`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

705a70c33a7646167cff412a2c658fce9a0adcd4 · por sergio · (hace 2 meses) · padre 166355d1f · ver el árbol en este commit

Delta semántico

Ninguna definición cambió: el cambio es de texto, documentación o datos.

2 archivos · +43 −12

diff --git a/scripts/cosmic/metal-desktop-image.sh b/scripts/cosmic/metal-desktop-image.sh
index de96ce49..d8f5e3d8 100755
--- a/scripts/cosmic/metal-desktop-image.sh
+++ b/scripts/cosmic/metal-desktop-image.sh
@@ -89,7 +89,7 @@ install -Dm755 scripts/cosmic/cosmic-start-qemu.sh "$MERGED/usr/bin/cosmic-start
 #
 # Se arregla acá y no en el cmdline: un getty sobre tty1 no depende del cmdline (en metal siempre hay
 # VT) y no obliga a re-sellar el kernel. El getty de `console` se conserva para debug por serie.
-echo "==> añadiendo getty en tty1 (el prompt de `console` va al SERIAL, invisible en metal)"
+echo '==> añadiendo getty en tty1 (el prompt de `console` va al SERIAL, invisible en metal)'
 cat > "$MERGED/usr/bin/console-login" <<'LOGIN'
 #!/bin/sh
 # arje-zero lanza los getty con envp VACÍO ⇒ sin PATH no se resuelve ni `cat` ni `cosmic-start`.
diff --git a/scripts/install-image-efi.sh b/scripts/install-image-efi.sh
index 3ff0050b..9537ad98 100755
--- a/scripts/install-image-efi.sh
+++ b/scripts/install-image-efi.sh
@@ -76,7 +76,7 @@ ESP_IMG="$STAGE/esp.img"; IRD="$STAGE/initramfs.cpio.gz"
 echo "==> initramfs de pivote (busybox estático + /init switch_root a hammer-root)"
 mkdir -p "$IRD_TREE/bin" "$IRD_TREE/dev" "$IRD_TREE/proc" "$IRD_TREE/sys" "$IRD_TREE/newroot"
 cp "$BUSYBOX" "$IRD_TREE/bin/busybox"; chmod 0755 "$IRD_TREE/bin/busybox"
-for a in sh mount umount findfs switch_root mkdir echo sleep; do ln -sf busybox "$IRD_TREE/bin/$a"; done
+for a in sh mount umount findfs switch_root mkdir echo sleep ls cat setsid dmesg blkid; do ln -sf busybox "$IRD_TREE/bin/$a"; done
 cat > "$IRD_TREE/init" <<'INIT'
 #!/bin/busybox sh
 # initramfs de pivote (EFI-stub → aquí). Monta las pseudo-fs, resuelve la ext4 real por LABEL, monta
@@ -86,20 +86,44 @@ export PATH=/bin
 mount -t proc     proc /proc 2>/dev/null || true
 mount -t sysfs    sys  /sys  2>/dev/null || true
 mount -t devtmpfs dev  /dev  2>/dev/null || true
-# El cmdline horneado del kernel pone console=tty0 al final ⇒ el stdout de userspace va a la PANTALLA,
-# no al serial. Para que el pivote sea visible en un OVMF headless (y para depurar en metal), tee'amos
-# los marcadores también a /dev/ttyS0.
-log() { echo "$*"; echo "$*" > /dev/ttyS0 2>/dev/null || true; }
+# ⚠ EL CMDLINE HORNEADO ES "console=tty0 console=ttyS0,115200 …" Y EL KERNEL HACE /dev/console = LA
+# ÚLTIMA `console=` ⇒ **ttyS0**. O sea que el stdout de este script va al SERIAL, no a la pantalla: en
+# un metal sin puerto serie TODO lo que pase acá es INVISIBLE y el arranque parece congelado en el
+# último printk del kernel. Es el mismo bug que costó el 1er viaje físico de KDE, una capa más
+# temprano: allá se arregló para DESPUÉS del switch_root (getty en tty1) y acá seguía sin arreglar.
+# Por eso el log va explícitamente a **/dev/tty0** (la VT en primer plano = la pantalla) además del
+# serial, y nunca se confía en el stdout heredado.
+log() {
+    echo "$*"
+    for _d in /dev/tty0 /dev/ttyS0; do echo "$*" > "$_d" 2>/dev/null || true; done
+}
+# Shell de rescate VISIBLE: si algo falla, un `exec sh` a secas nace en el serial y el usuario ve una
+# pantalla muerta sin manera de averiguar nada. Se abre uno en tty1 (pantalla) y se conserva el serial.
+rescate() {
+    log "HAMMER-EFI-FALLO: $*"
+    log "   bloques visibles: $(ls /dev/sd* /dev/nvme* /dev/vd* /dev/mmcblk* 2>/dev/null | tr '\n' ' ')"
+    log "   abriendo shell de rescate en tty1 — probá: blkid ; dmesg | tail -30"
+    setsid -c sh -c 'exec /bin/sh </dev/tty1 >/dev/tty1 2>&1' >/dev/null 2>&1 &
+    exec sh
+}
 log "HAMMER-EFI: initramfs de pivote — resolviendo hammer-root por LABEL"
+# ⏱ LA ESPERA TIENE QUE SER LARGA, Y ÉSTE ES EL MOTIVO: el rdinit arranca en cuanto se desempaqueta el
+# initramfs, pero el bus USB enumera de forma ASÍNCRONA y tarda SEGUNDOS (reset del hub, settling de 1s
+# por dispositivo, scan SCSI). En QEMU el disco es virtio y está desde el instante cero, así que la
+# carrera NUNCA se ve en validación: sólo aparece al bootear de un USB en metal, y con 5 reintentos de
+# 1s se pierde. Es el equivalente de `rootwait`, que acá no aplica porque no hay root= en el cmdline.
 ROOTDEV=""
-for _try in 1 2 3 4 5; do
+_try=0
+while [ "$_try" -lt 60 ]; do
     ROOTDEV="$(findfs LABEL=hammer-root 2>/dev/null || true)"
     [ -n "$ROOTDEV" ] && break
-    log "HAMMER-EFI: hammer-root aún no visible, reintento $_try…"; sleep 1
+    _try=$(( _try + 1 ))
+    [ $(( _try % 5 )) -eq 0 ] && log "HAMMER-EFI: esperando hammer-root… ${_try}s (el USB tarda en enumerar)"
+    sleep 1
 done
-[ -n "$ROOTDEV" ] || { log "HAMMER-EFI-FALLO: no encuentro LABEL=hammer-root"; exec sh; }
+[ -n "$ROOTDEV" ] || rescate "no encuentro LABEL=hammer-root tras ${_try}s"
 log "HAMMER-EFI: hammer-root = $ROOTDEV → montando en /newroot"
-mount -t ext4 "$ROOTDEV" /newroot || { log "HAMMER-EFI-FALLO: no pude montar $ROOTDEV"; exec sh; }
+mount -t ext4 "$ROOTDEV" /newroot || rescate "no pude montar $ROOTDEV"
 # /store y /var/lib/hammer por LABEL (no bloqueante: el wrapper del root también reintenta).
 STOREDEV="$(findfs LABEL=hammer-store 2>/dev/null || true)"
 STATEDEV="$(findfs LABEL=hammer-state 2>/dev/null || true)"
@@ -136,12 +160,19 @@ cat > "$ROOT_TREE/sbin/init" <<'RINIT'
 # busybox switch_root NO mueve /dev al nuevo root ⇒ montamos devtmpfs antes del marcador (si no,
 # /dev/ttyS0 no existe todavía y el echo se pierde). arje-zero re-monta lo que necesite, es idempotente.
 /bin/busybox mount -t devtmpfs dev /dev 2>/dev/null || true
-echo "HAMMER-EFI-INIT-OK: arje-zero PID1" > /dev/ttyS0 2>/dev/null || true
+# A la PANTALLA (tty0) además del serial: en metal sin puerto serie éste es el único indicio de que el
+# pivote llegó al init real. Ver el comentario largo del initramfs.
+for _d in /dev/tty0 /dev/ttyS0; do
+    echo "HAMMER-EFI-INIT-OK: arje-zero PID1" > "$_d" 2>/dev/null || true
+done
 # Menú de arranque por grafo (ADR 0010): emite /run/hammer/boot-graph.json y, si hay un compositor
 # (mirada), lo pinta sobre KMS para elegir un nodo. Graceful: sin compositor, emite el grafo y sigue.
 # Salida al serial (no pinta tty0 ⇒ respeta el cero-parpadeo). No bloquea el arranque si falla.
+# El `timeout` no es paranoia decorativa: esto corre ANTES del exec de arje-zero, o sea que si se
+# cuelga no hay PID1, no hay getty y no hay pantalla — un arranque muerto e indistinguible de un kernel
+# colgado. El `|| true` protege del fallo, no del bloqueo.
 if [ -x /usr/bin/hammer ]; then
-    /usr/bin/hammer boot menu > /dev/ttyS0 2>&1 || true
+    /bin/busybox timeout 15 /usr/bin/hammer boot menu > /dev/ttyS0 2>&1 || true
 fi
 exec /usr/bin/arje-zero
 RINIT