arranque-grafo: rama EFI-stub en el instalador live (ADR 0010 paso 5)
hammer-live-install.sh detecta /sys/firmware/efi y ramifica a EFI-stub soberano: MBR con ESP tipo 0xEF (UEFI arranca una ESP MBR igual que GPT ⇒ sólo busybox fdisk/mkfs.vfat/mount, sin GPT-tool ni mtools en el live). Arma el mismo initramfs de pivote (findfs LABEL=hammer-root → switch_root) que el builder host-side; /sbin/init = wrapper hammer-recover→arje-zero (el pivote ya montó store/estado por LABEL). La rama BIOS+GRUB queda intacta tras la detección. iso-image.sh INSTALLER=1 bundlea el kernel metal EFI-stub (bzImage-efi) en el payload; el genérico no sirve (no hornea initrd=/rdinit= en la cmdline). MBR+ESP-EF spot-checked en OVMF (firmware → BOOTX64.EFI → pivote → arje-zero). ADR 0010 actualizado: pasos 2 y 5 ✅. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# ADR 0010 — Arranque por grafo: el menú de boot como navegación del grafo de estados (mirada + hammer)
|
||||
|
||||
**Estado:** aceptado (plan), sin implementar.
|
||||
**Fecha:** 2026-07-06.
|
||||
**Estado:** aceptado; lado hammer implementado (pasos 1–3, 5 ✅; paso 2 ✅ vía pivote a ext4; paso 4
|
||||
—cero-parpadeo— pendiente). Falta validación 2-stage del instalador live + metal, y el lado mirada.
|
||||
**Fecha:** 2026-07-06 (impl. EFI-stub 2026-07-10).
|
||||
**Contexto cruzado:** hammer (arranque/kernel/generaciones) + tawasuyu/mirada (render). El
|
||||
espejo de este ADR para el lado mirada vive en `tawasuyu/HANDOFF-arranque-grafo.md`.
|
||||
|
||||
@@ -94,8 +95,11 @@ muerto antes del takeover).
|
||||
## Plan (lado hammer)
|
||||
|
||||
1. **Este ADR + el handoff a tawasuyu** con el contrato (hecho en este commit).
|
||||
2. **squashfs+overlay** en el medio/instalador → initrd chico → **reprobar EFI-stub directo** en
|
||||
OVMF, dejarlo listo para el metal. (Refinamiento #2 de [post-etapa-e].)
|
||||
2. **initrd chico → EFI-stub directo reprobado en OVMF** ✅ (2026-07-10): el disco instalado NO usa un
|
||||
rootfs en RAM sino un **initramfs mínimo de pivote** (~770K) que resuelve `hammer-root` por LABEL y
|
||||
hace `switch_root` a la ext4 real. Ese initrd chico es lo que destraba EFI-stub directo (el cuelgue
|
||||
del metal era por un initrd de 100MB). Validado en OVMF por `scripts/efi-disk-boot-test.sh`. (La vía
|
||||
squashfs+overlay del [post-etapa-e] queda como optimización futura; el pivote a ext4 ya cumple.)
|
||||
3. **Contrato de generaciones-grafo** ✅ (módulo `hammer_upgrade::boot_graph` + subcomando `hammer
|
||||
boot`): el modelo in-place de generaciones se lee como un **grafo de estados** navegable (id =
|
||||
`of_tree`, DAG por `parent`), `hammer boot graph` emite `/run/hammer/boot-graph.json` con el
|
||||
@@ -104,9 +108,18 @@ muerto antes del takeover).
|
||||
ante un "redo" hacia una generación huérfana). Validado e2e por el CLI. **mirada ya puede maquetar
|
||||
contra el grafo real.**
|
||||
4. **Afinar kernel/cmdline** para el handoff sin costura (punto cero-parpadeo).
|
||||
5. **install-to-disk EFI** (`hammer-live-install` rama EFI): GPT+ESP con el bzImage como
|
||||
`\EFI\BOOT\BOOTX64.EFI` (EFI-stub), root/store/var en particiones; validar el disco instalado en
|
||||
OVMF hasta que mirada tome el control.
|
||||
5. **install-to-disk EFI** ✅ (2026-07-10): el bzImage `linux-metal` ES el binario EFI
|
||||
(`\EFI\BOOT\BOOTX64.EFI`, ruta fallback removible, sin GRUB ni systemd-boot). Como no hay LoadOptions
|
||||
en esa ruta, el `CONFIG_CMDLINE` horneado (`… initrd=/initramfs.cpio.gz rdinit=/init`) activa el
|
||||
pivote. Dos caminos, mismos artefactos:
|
||||
- **host-side** `scripts/install-image-efi.sh` — GPT+ESP+root/store/state, ESP con mtools de hammer,
|
||||
ext4 con `mke2fs -d` bajo `unshare -r`; **validado VERDE en OVMF** (`scripts/efi-disk-boot-test.sh`:
|
||||
firmware → BOOTX64.EFI → pivote → arje-zero PID1).
|
||||
- **live-side** `scripts/hammer-live-install.sh` rama EFI — detecta `/sys/firmware/efi` y hace
|
||||
**MBR con ESP tipo 0xEF** (UEFI arranca una ESP MBR igual que GPT ⇒ alcanza busybox
|
||||
fdisk/mkfs.vfat/mount, sin GPT-tool ni mtools en el live). El kernel EFI-stub viaja en el payload
|
||||
(`iso-image.sh INSTALLER=1` bundlea `bzImage-efi`). El arranque MBR+ESP-EF quedó **spot-checked en
|
||||
OVMF**. Falta sólo la validación 2-stage (live UEFI → instalar → rebootear el disco) y el metal.
|
||||
|
||||
## Consecuencias
|
||||
|
||||
|
||||
@@ -29,6 +29,150 @@ BB=/bin/busybox
|
||||
|
||||
[ -b "$DEV" ] || { echo "hammer-install: $DEV no es un block device" >&2; exit 1; }
|
||||
[ -d "$PAYLOAD" ] || { echo "hammer-install: falta el payload $PAYLOAD (¿ISO sin INSTALLER=1?)" >&2; exit 1; }
|
||||
|
||||
# --- Detección de firmware: UEFI ⇒ rama EFI-stub SOBERANA (ADR 0010 paso 5); BIOS ⇒ MBR+GRUB (abajo) ---
|
||||
# La rama EFI usa una ESP tipo 0xEF en tabla MBR (UEFI arranca \EFI\BOOT\BOOTX64.EFI de una ESP MBR igual
|
||||
# que de GPT) ⇒ alcanza con busybox fdisk/mkfs.vfat/mount, sin GPT-tool ni mtools. El bzImage metal ES el
|
||||
# binario EFI (EFI-stub, cmdline horneada carga el initrd); un initramfs mínimo pivota por LABEL. Mismos
|
||||
# artefactos que scripts/install-image-efi.sh (validado en OVMF), armados desde dentro del live.
|
||||
if [ -d /sys/firmware/efi ]; then
|
||||
[ -r "$PAYLOAD/kernel/bzImage-efi" ] || { echo "hammer-install: live en UEFI pero falta $PAYLOAD/kernel/bzImage-efi (ISO sin kernel metal)" >&2; exit 1; }
|
||||
ESP_MB="${ESP_MB:-64}"
|
||||
echo "==> hammer-install (UEFI): EFI-stub soberano en $DEV (ESP ${ESP_MB}M, root ${ROOT_MB}M, store ${STORE_MB}M, estado=resto)"
|
||||
echo " ¡esto BORRA $DEV!"
|
||||
|
||||
# MBR, 4 primarias: p1=ESP(0xEF) p2=root p3=store p4=estado. Sectores explícitos (como la rama BIOS).
|
||||
SPM=2048
|
||||
P1S=2048; P1E=$(( P1S + ESP_MB * SPM - 1 ))
|
||||
P2S=$(( P1E + 1 )); P2E=$(( P2S + ROOT_MB * SPM - 1 ))
|
||||
P3S=$(( P2E + 1 )); P3E=$(( P3S + STORE_MB* SPM - 1 ))
|
||||
P4S=$(( P3E + 1 )) # estado = resto del disco
|
||||
echo "==> particionando $DEV (MBR: ESP ${P1S}-${P1E} tipo ef, root ${P2S}-${P2E}, store ${P3S}-${P3E}, estado ${P4S}-fin)"
|
||||
"$BB" fdisk "$DEV" >/dev/null 2>&1 <<FDISK || true
|
||||
o
|
||||
n
|
||||
p
|
||||
1
|
||||
$P1S
|
||||
$P1E
|
||||
n
|
||||
p
|
||||
2
|
||||
$P2S
|
||||
$P2E
|
||||
n
|
||||
p
|
||||
3
|
||||
$P3S
|
||||
$P3E
|
||||
n
|
||||
p
|
||||
4
|
||||
$P4S
|
||||
|
||||
t
|
||||
1
|
||||
ef
|
||||
a
|
||||
1
|
||||
w
|
||||
FDISK
|
||||
"$BB" partprobe "$DEV" 2>/dev/null || true
|
||||
"$BB" sync; sleep 1
|
||||
P1="${DEV}1"; P2="${DEV}2"; P3="${DEV}3"; P4="${DEV}4"
|
||||
for p in "$P1" "$P2" "$P3" "$P4"; do
|
||||
[ -b "$p" ] || { echo "hammer-install: no apareció la partición $p tras particionar" >&2; exit 1; }
|
||||
done
|
||||
|
||||
echo "==> formateando (ESP FAT32 + 3 ext2 etiquetadas para el pivote por LABEL)"
|
||||
"$BB" mkfs.vfat -F 32 -n HAMMER "$P1" >/dev/null 2>&1
|
||||
"$BB" mke2fs -F -L hammer-root "$P2" >/dev/null 2>&1
|
||||
"$BB" mke2fs -F -L hammer-store "$P3" >/dev/null 2>&1
|
||||
"$BB" mke2fs -F -L hammer-state "$P4" >/dev/null 2>&1
|
||||
|
||||
# initramfs de pivote (busybox estático del live + /init: findfs LABEL=hammer-root → switch_root).
|
||||
echo "==> armando el initramfs de pivote (busybox + /init switch_root a hammer-root)"
|
||||
IRD_TREE=/run/hammer-efi-ird
|
||||
"$BB" rm -rf "$IRD_TREE"
|
||||
"$BB" mkdir -p "$IRD_TREE/bin" "$IRD_TREE/dev" "$IRD_TREE/proc" "$IRD_TREE/sys" "$IRD_TREE/newroot"
|
||||
cp "$BB" "$IRD_TREE/bin/busybox"; "$BB" chmod 0755 "$IRD_TREE/bin/busybox"
|
||||
for a in sh mount umount findfs switch_root mkdir echo sleep; do "$BB" ln -sf busybox "$IRD_TREE/bin/$a"; done
|
||||
cat > "$IRD_TREE/init" <<'INIT'
|
||||
#!/bin/busybox sh
|
||||
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
|
||||
log() { echo "$*"; echo "$*" > /dev/ttyS0 2>/dev/null || true; }
|
||||
log "HAMMER-EFI: initramfs de pivote — resolviendo hammer-root por LABEL"
|
||||
ROOTDEV=""
|
||||
for _try in 1 2 3 4 5; 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
|
||||
done
|
||||
[ -n "$ROOTDEV" ] || { log "HAMMER-EFI-FALLO: no encuentro LABEL=hammer-root"; exec sh; }
|
||||
log "HAMMER-EFI: hammer-root = $ROOTDEV → montando en /newroot"
|
||||
mount -t ext4 "$ROOTDEV" /newroot || { log "HAMMER-EFI-FALLO: no pude montar $ROOTDEV"; exec sh; }
|
||||
STOREDEV="$(findfs LABEL=hammer-store 2>/dev/null || true)"
|
||||
STATEDEV="$(findfs LABEL=hammer-state 2>/dev/null || true)"
|
||||
[ -n "$STOREDEV" ] && mount -t ext4 "$STOREDEV" /newroot/store 2>/dev/null || true
|
||||
[ -n "$STATEDEV" ] && mount -t ext4 "$STATEDEV" /newroot/var/lib/hammer 2>/dev/null || true
|
||||
log "HAMMER-EFI-DISK-PIVOT-OK: switch_root → /sbin/init"
|
||||
exec switch_root /newroot /sbin/init
|
||||
INIT
|
||||
"$BB" chmod +x "$IRD_TREE/init"
|
||||
( cd "$IRD_TREE" && "$BB" find . | "$BB" cpio -o -H newc 2>/dev/null | "$BB" gzip -1 ) > /run/hammer-efi-ird.cpio.gz
|
||||
|
||||
# Poblar la ESP (FAT32, montable por el live): BOOTX64.EFI = bzImage metal + el initramfs de pivote.
|
||||
echo "==> poblando la ESP (\\EFI\\BOOT\\BOOTX64.EFI = kernel metal + \\initramfs.cpio.gz)"
|
||||
ESPMNT=/run/hammer-esp
|
||||
"$BB" mkdir -p "$ESPMNT"
|
||||
"$BB" mount -t vfat "$P1" "$ESPMNT"
|
||||
"$BB" mkdir -p "$ESPMNT/EFI/BOOT"
|
||||
cp "$PAYLOAD/kernel/bzImage-efi" "$ESPMNT/EFI/BOOT/BOOTX64.EFI"
|
||||
cp /run/hammer-efi-ird.cpio.gz "$ESPMNT/initramfs.cpio.gz"
|
||||
"$BB" sync; "$BB" umount "$ESPMNT"
|
||||
|
||||
# Poblar la root (copia del live) — sin wrapper que monte particiones (el pivote ya montó store/estado).
|
||||
echo "==> copiando la raíz del live → $P2 (excluye virtuales/efímeros, /store, /var/lib/hammer, payload)"
|
||||
MNT=/run/hammer-install
|
||||
"$BB" mkdir -p "$MNT"
|
||||
"$BB" mount -t ext4 "$P2" "$MNT"
|
||||
for entry in /* /.[!.]*; do
|
||||
[ -e "$entry" ] || continue
|
||||
name="${entry##*/}"
|
||||
case "$name" in
|
||||
proc|sys|dev|run|tmp|store|init) continue ;;
|
||||
*) cp -a "$entry" "$MNT/" 2>/dev/null || true ;;
|
||||
esac
|
||||
done
|
||||
"$BB" rm -rf "$MNT/var/lib/hammer" 2>/dev/null || true
|
||||
"$BB" rm -rf "$MNT/usr/lib/hammer/install" 2>/dev/null || true
|
||||
"$BB" mkdir -p "$MNT/store" "$MNT/var/lib/hammer" "$MNT/proc" "$MNT/sys" "$MNT/dev" "$MNT/run" "$MNT/tmp"
|
||||
# /sbin/init: wrapper que auto-sana un upgrade interrumpido y arranca arje-zero (el pivote ya montó
|
||||
# store/estado por LABEL, así que acá NO hace falta montar nada — sólo hammer-recover + exec).
|
||||
if [ -x "$PAYLOAD/hammer-recover" ]; then
|
||||
"$BB" mkdir -p "$MNT/usr/sbin"
|
||||
cp "$PAYLOAD/hammer-recover" "$MNT/usr/sbin/hammer-recover"; "$BB" chmod 0755 "$MNT/usr/sbin/hammer-recover"
|
||||
fi
|
||||
"$BB" rm -f "$MNT/sbin/init"
|
||||
cat > "$MNT/sbin/init" <<'INIT'
|
||||
#!/bin/sh
|
||||
# Wrapper PID1 (rama UEFI): el initramfs de pivote ya montó / /store /var/lib/hammer por LABEL antes del
|
||||
# switch_root; sólo auto-sanamos un upgrade interrumpido y arrancamos el init real.
|
||||
[ -x /usr/sbin/hammer-recover ] && /usr/sbin/hammer-recover || true
|
||||
exec /usr/bin/arje-zero
|
||||
INIT
|
||||
"$BB" chmod +x "$MNT/sbin/init"
|
||||
"$BB" sync
|
||||
"$BB" umount "$MNT"
|
||||
|
||||
echo "✓ hammer-install (UEFI): $DEV instalado con EFI-stub SOBERANO (sin GRUB ni systemd-boot). Reiniciá sin el medio live."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ===================== rama BIOS (MBR + GRUB) — el firmware NO es UEFI ==============================
|
||||
[ -r "$PAYLOAD/grub/boot.img" ] && [ -r "$PAYLOAD/grub/core.img" ] || { echo "hammer-install: payload GRUB incompleto" >&2; exit 1; }
|
||||
[ -r "$PAYLOAD/kernel/bzImage" ] || { echo "hammer-install: falta el kernel en el payload" >&2; exit 1; }
|
||||
|
||||
|
||||
@@ -92,6 +92,16 @@ if [ "${INSTALLER:-0}" = 1 ]; then
|
||||
IPL="$RFS/root/usr/lib/hammer/install"
|
||||
mkdir -p "$IPL/grub/i386-pc" "$IPL/kernel"
|
||||
cp "$KERNEL" "$IPL/kernel/bzImage"; chmod 0644 "$IPL/kernel/bzImage"
|
||||
# Rama EFI del instalador (ADR 0010 paso 5): el kernel `linux-metal` hornea CONFIG_CMDLINE con
|
||||
# initrd=/initramfs.cpio.gz rdinit=/init ⇒ arranca por EFI-stub como \EFI\BOOT\BOOTX64.EFI sin
|
||||
# bootloader. Lo bundleamos junto al genérico; hammer-install lo usa si el live corre en UEFI.
|
||||
EFI_KERNEL="${EFI_KERNEL:-$(ls -dt store/*-linux-metal/boot/bzImage 2>/dev/null | head -1)}"
|
||||
if [ -n "${EFI_KERNEL:-}" ] && [ -r "$EFI_KERNEL" ]; then
|
||||
cp "$EFI_KERNEL" "$IPL/kernel/bzImage-efi"; chmod 0644 "$IPL/kernel/bzImage-efi"
|
||||
echo " + kernel EFI-stub (metal) para la rama UEFI del instalador"
|
||||
else
|
||||
echo " ! sin kernel linux-metal en el store ⇒ el instalador sólo tendrá rama BIOS/MBR" >&2
|
||||
fi
|
||||
cp "$GRUB_LIB/boot.img" "$IPL/grub/boot.img"
|
||||
cp "$GRUB_LIB"/*.mod "$GRUB_LIB"/*.lst "$IPL/grub/i386-pc/" 2>/dev/null || true
|
||||
# core.img para disco MBR: prefix a la 1ª partición msdos, módulos de disco (biosdisk/part_msdos/ext2).
|
||||
|
||||
Reference in New Issue
Block a user