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 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