arranque y GC: las dos rutas EFI, guardián de ESP y el kernel vivo como raíz

Primera tanda de los ADR 0017/0018, con las mediciones que la corrigieron.

ADR 0018 §3 — install-image-efi.sh y takana-live-install.sh escriben el
kernel en \EFI\takana\takanax64.efi ADEMAS de la ruta fallback, con un
guardián de capacidad que comprueba ANTES y con los números a la vista
(duplicar el kernel cuesta, y el costo se dice). En el live-install la
verificación es por TAMAÑO, no por presencia: un cp truncado en FAT32
deja el fichero ahí y test -e diría que todo salió bien.

Verificado con imagen real en OVMF: las dos copias dan el mismo sha256
que el bzImage del store, y la imagen arranca hasta arje-zero PID1.
CONTROL NEGATIVO: pisando la fallback con 4K de basura el firmware dice
"No bootable option or device was found" y no aparece HAMMER-EFI ni una
vez ⇒ el escenario de Windows, reproducido.

Y lo que NO se pudo verificar cambia el alcance, así que va en el ADR:
sin entrada NVRAM el firmware NO busca el vendor path. La copia vendor
es hoy un seguro que no se cobra solo — el §3 es necesario y NO
suficiente sin el §1. Eso asciende la receta efibootmgr a bloqueante.

ADR 0017 §4 — store-gc.sh suma como raíz el kernel EN EJECUCIÓN,
identificado por .config byte a byte. Sin esto el artefacto del kernel
vivo cae en "superados" cuando la receta se movió, y se borra: el
sistema sigue andando perfecto hasta el día que hace falta volver atrás.
Probado en los tres sentidos, incluido el control que TIENE que seguir
condenado.

Además, dos bugs preexistentes que aparecieron al ir a medir:

- install-image-efi.sh abortaba con "ROOTFS sin /sbin/init" en TODO
  rootfs sano: /sbin/init es un symlink ABSOLUTO (→/usr/bin/arje-zero) y
  [ -e ] lo sigue contra la raíz del HOST. Un chequeo que validaba algo
  distinto de lo que creía validar. Arreglado resolviendo el destino
  dentro del rootfs.
- (no arreglado, es del entorno) el cp -al del staging da EXDEV si
  ROOTFS y STAGE no están en el MISMO MOUNT — y el bind-mount del store
  cuenta como otro mount aunque sea el mismo /dev/sdb.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHAKWoBqof9XvsYCvDicEj
This commit is contained in:
Sergio
2026-09-11 20:40:34 +00:00
co-authored by Claude Opus 5
parent 2546f5cdf8
commit 294959c1da
5 changed files with 208 additions and 11 deletions
+17
View File
@@ -109,6 +109,23 @@ en el CAS: lo que no es alcanzable desde una raíz desaparece sin avisar.
`scripts/store-gc.sh` y cualquier GC nuevo leen el hash del kernel vivo y lo tratan como raíz.
Guardián con **rotura a propósito y control que tiene que pasar**, no sólo con el caso feliz.
> **ENMIENDA 2026-09-11 — el §4 está implementado en `scripts/store-gc.sh` y probado en los tres
> sentidos.** El GC suma ahora una raíz: el artefacto de kernel cuyo `boot/config-*` coincide byte a
> byte con el `.config` del kernel vivo (`/proc/config.gz`, o `/boot/config-$(uname -r)`).
>
> | caso | esperado | resultado |
> |---|---|---|
> | store de juguete con el kernel **vivo** marcado «superado» | se rescata | ✅ movido a vigentes, y lo **dice**: «1 estaban marcados para BORRAR y se rescataron» |
> | **control:** un kernel con `.config` distinto, también marcado | **sigue condenado** | ✅ no protege de más |
> | store real, en el hub (kernel ajeno de Artix) | no protege nada, y lo explica | ✅ «NINGUN artefacto del store lo tiene» |
>
> El control del medio es el que importa: sin él, una función que devolviera «protegido» siempre
> pasaría la primera prueba y volvería inútil al GC sin que nadie lo notara.
>
> Identifica por **contenido del `.config`**, no por hash de artefacto: dos artefactos con el mismo
> `.config` y distinta toolchain se protegen los dos. Es el lado correcto para un GC, y se vuelve
> exacto el día que el §3 ponga el hash en el kernel vivo.
### 5. Si alguna vez hace falta livepatch, es una **variante**, no un flag
La salida correcta **no** es encender `MODULES` en el kernel general —eso revierte una decisión de
+28
View File
@@ -38,6 +38,7 @@ Conviene además separar los dos modos de fallo, porque se arreglan distinto y s
| receta `efibootmgr`, `efivar` en el catálogo | **no existen** |
| scripts que escriben `\EFI\BOOT\BOOTX64.EFI` | **5** (`install-image-efi.sh:190`, `install-image.sh`, `iso-image.sh`, `metal-usb-sdboot.sh:64`, `takana-live-install.sh`) |
| entradas NVRAM que takana crea | **ninguna, nunca** |
| el instalador live en UEFI, ¿respeta particiones existentes? | **no**: particiona el disco ENTERO en MBR (`takana-live-install.sh:286-292`) ⇒ **hoy no existe el caso «instalar al lado de Windows»**, no es que esté frágil |
**El hallazgo que ordena todo el ADR:** takana **jamás crea una entrada de arranque**. Depende
íntegramente de `\EFI\BOOT\BOOTX64.EFI`, que **por especificación UEFI es la ruta de medios
@@ -80,6 +81,33 @@ Hace falta algo que no dependa de haber arrancado:
tonto. Es lo único que funciona cuando todo lo demás falló, y **ninguna distro te lo dice antes de
que lo necesites** — te lo dice un foro, a las dos de la mañana, desde otro dispositivo.
> **ENMIENDA 2026-09-11 — implementado el §3 en las dos imágenes, y la medición corrigió el alcance.**
>
> `install-image-efi.sh` y `takana-live-install.sh` escriben ya el kernel en las **dos** rutas, con
> guardián de capacidad previo. Verificado construyendo una imagen real y arrancándola en OVMF:
>
> | prueba | resultado |
> |---|---|
> | las dos copias en la ESP vs el bzImage del store | **sha256 idéntico** (`162cd18e4f8f1f5c`), 15 299 584 B cada una |
> | arranque de la imagen completa | ✅ `HAMMER-EFI-DISK-PIVOT-OK` → `HAMMER-EFI-INIT-OK: arje-zero PID1` |
> | **control negativo:** pisar `\EFI\BOOT\BOOTX64.EFI` con 4 KiB de basura, sin otra vía | ✅ `BdsDxe: No bootable option or device was found` — **cero** apariciones de `HAMMER-EFI` |
>
> El control negativo es el que da valor al resto: confirma que **pisar la ruta fallback mata el
> arranque por completo** cuando es el único camino. Es el escenario de Windows, reproducido.
>
> ⚠ **Y lo que NO se pudo verificar cambia el alcance del §3, así que va acá y no en una nota al
> pie:** no se logró arrancar POR el vendor path. Sin entrada NVRAM el firmware no lo busca —OVMF
> cayó en `No bootable option` con el kernel intacto en `\EFI\takana\takanax64.efi`— y no hay
> forma de crear esa entrada desde este hub (`efibootmgr` existe en el host pero escribe en la NVRAM
> **del host**, no en un fichero `OVMF_VARS`; no hay `virt-firmware`; este OVMF no cae al UEFI Shell,
> así que el truco del `startup.nsh` tampoco corre).
>
> ⇒ **La copia en el vendor path es hoy un seguro que todavía no se puede cobrar solo.** Protege
> contra que *pisen el fichero*, pero sólo si además existe la entrada NVRAM del §1; sin ella el
> rescate es manual, desde el menú del firmware, en los firmwares que dejan navegar a un `.efi`
> arbitrario. **El §3 es condición necesaria y NO suficiente: sin el §1 la mitad del seguro falta.**
> Eso mueve la receta `efibootmgr` de «dependencia que este ADR crea» a **bloqueante del §3**.
### 4. El instalador mira el terreno y lo dice en voz alta
Antes de tocar nada: qué hay en la ESP, cuánto espacio libre queda, cómo está `BootOrder`, si hay un