🩺 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>
This commit is contained in:
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user