Files
takana/scripts/selfhost-verify.sh
T
Sergio 1c3e185167 takana: las variables de entorno leen las dos formas, gana la nueva
TAKANA_X con caída a HAMMER_X (ADR 0016). No es un sed: estas variables son
contrato de usuario —knobs del instalador, el entorno del worker, mirror-env.sh—
y viven en perfiles de shell y units FUERA del repo, así que un renombre duro no
falla ruidosamente: la variable no aparece, se toma el default y el build se
comporta distinto sin que nada lo diga.

Rust: takana_core::env::{var,var_os} toma el nombre canónico y deriva el viejo
cambiando el prefijo — se le pasa TAKANA_* para que un grep del nombre nuevo
encuentre todas las lecturas. 5 tests, con DOS controles negativos: sin ninguna
de las dos no hay valor, y un nombre sin prefijo no inventa una caída.
Migrados los 17 sitios directos y los indirectos que el grep no mostraba
(bases_de_mirror, las constantes de kernel_cmd, ROOT_ENV de qorpa, env_path de
recover). takana-recover lleva la caída inline: es un mini-binario que se copia
a /usr/sbin y no vale arrastrarle una dep entera por dos líneas.

Scripts: 30 lecturas pasan a default, manteniendo el
nombre INTERNO de la variable para no tocar sus 190 usos.

Y donde el script EXPORTA en vez de leer, se ponen LAS DOS (mirror-env.sh y el
fragmento in-VM de bootstrap): ahí el lector puede ser un binario viejo —worker
sin recompilar, el /usr/bin/hammer pinado del baseline— que sólo conoce HAMMER_*.
La caída sirve para lectores nuevos; los viejos necesitan que la vieja siga puesta.

Verificado de punta a punta con el binario, no sólo con unit tests: HAMMER_LAB
sigue surtiendo efecto, TAKANA_LAB hace lo mismo, y con las dos gana TAKANA_LAB.
605 tests en verde y el hash de zlib sigue en b3:dc363f26… , intacto.

Cambio de comportamiento que va aparte y hay que decir: el hostname por defecto
de una instalación nueva pasa de 'hammer' a 'takana' (sólo si no se fija ninguna
de las dos variables).

NO se tocan: las rutas /var/lib/hammer de sistemas instalados, el volid
HAMMER_LIVE del ISO, ni el namespace HARKAQ_*, que es de otro subsistema.
2026-09-09 19:14:49 +00:00

273 lines
17 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# selfhost-verify.sh — Verifica el auto-alojamiento bit a bit de Stage 1 de punta a punta
# (SDD 11 §7; runbook docs/runbooks/stage1-vm-boot.md §8c).
#
# Hace TODO el pipeline en una corrida, pensado para un host con KVM + RAM holgada (p. ej. una
# laptop): construye el store baseline, ensambla el builder, lo empaqueta como initramfs y lo bootea
# en QEMU, donde `rebuild-stage1` reconstruye stage1' con el toolchain de adentro y compara su
# content-hash (`of_tree`) con la referencia anclada afuera → ✓ REPRODUCIBLE / ✗ DIVERGENTE.
#
# Prerequisitos:
# - Lab de dev: ./scripts/bootstrap-devfs.sh (deja .dev-fs/alpine + .dev-fs/tools/zig)
# - qemu-system-x86_64, cpio, gzip; un kernel x86_64 en $KERNEL.
# - Red (el fetch de Rust hace `cargo vendor` desde crates.io).
#
# Variables (override por entorno):
# STORE store content-addressed (default ./store)
# KERNEL kernel a bootear en la VM (default /boot/vmlinuz-linux)
# MEM RAM de la VM en MiB (default 6144; con KVM subí a 8192+)
# KVM 1 ⇒ -enable-kvm -cpu host (default auto: 1 si hay /dev/kvm)
# PRESEED "all" rebuild 4/4 in-VM | "hammerd" preseed C+arje, sólo hammerd in-VM
# (default all; "hammerd" es el camino barato/rápido si la RAM va justa)
# EXPECT_REF content-hash esperado (cross-check de reproducibilidad entre máquinas; opcional)
# STAGE0_URL / STAGE0_SHA256 / SEED_VERSION semilla zig (defaults: zig 0.16.0)
#
# Uso típico en la laptop (con KVM):
# KVM=1 MEM=10240 ./scripts/selfhost-verify.sh
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
STORE="${STORE:-store}"
KERNEL="${KERNEL:-/boot/vmlinuz-linux}"
MEM="${MEM:-6144}"
PRESEED="${PRESEED:-all}"
SEED_VERSION="${SEED_VERSION:-0.16.0}"
STAGE0_URL="${STAGE0_URL:-https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz}"
STAGE0_SHA256="${STAGE0_SHA256:-70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00}"
TOOLCHAIN="${TOOLCHAIN:-.dev-fs/alpine}"
TOOLCHAIN_TAG="${TOOLCHAIN_TAG:-alpine-3.23.4-builder}"
# Referencia conocida-buena (store baseline en el host de dev, runbook §8c). Si tu corrida produce
# otra, o bien cambió algo del pin (toolchain/recipes/seed) o hay un no-determinismo nuevo.
# Baseline DETERMINISTA con codegen-units=1 + CARGO_BUILD_JOBS=1 en el sandbox: el segundo serializa
# el backend paralelo de rustc/LLVM (ThinLTO), sin el cual arje-zero divergía ~62 KB a >1 CPU (el
# viejo 0039b2b9… era un build paralelo no-reproducible). Ver §8c y SDD 09 §2.
EXPECT_REF="${EXPECT_REF:-b3:9adefb82b8457c8616cc89e6bb725a787ea52641f65d60d989d819e4872735ac}"
: "${KVM:=$([[ -w /dev/kvm ]] && echo 1 || echo 0)}"
say() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; }
die() { printf '\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; }
[[ -x "$TOOLCHAIN/usr/bin/rustc" ]] || die "falta el lab: corré ./scripts/bootstrap-devfs.sh (no veo $TOOLCHAIN)"
[[ -r "$KERNEL" ]] || die "no puedo leer el kernel $KERNEL (set KERNEL=…)"
command -v qemu-system-x86_64 >/dev/null || die "falta qemu-system-x86_64"
# 0) Binario hammer estático (musl, crt-static) — el builder lo bootea adentro.
HAMMER_BIN="target/x86_64-unknown-linux-musl/release/hammer"
say "build hammer estático ($HAMMER_BIN)"
cargo build --release --target x86_64-unknown-linux-musl -p takana-cli
HAMMER="./$HAMMER_BIN"
say "stage0 — ingerir la semilla zig (idempotente)"
"$HAMMER" --store "$STORE" bootstrap stage0 \
--url "$STAGE0_URL" --sha256 "$STAGE0_SHA256" --version "$SEED_VERSION"
# El seed_hash sale del nombre del artefacto sellado (robusto: no parsea stdout, donde el sha256 del
# tarball también es 64-hex y podría confundirse).
SEED_HASH="$(ls -d "$STORE"/*-seed-zig 2>/dev/null | head -1 | sed -E 's#.*/([0-9a-f]{64})-seed-zig#b3:\1#')"
[[ "$SEED_HASH" == b3:* && ${#SEED_HASH} -eq 67 ]] || die "no encuentro la semilla sellada en $STORE"
say "semilla: $SEED_HASH"
# 0c) Auto-alojamiento del COMPILADOR (variante b, pieza rust). A DIFERENCIA de make/busybox/bwrap/…
# (herramientas que ORQUESTAN o COPIAN ⇒ bytes idénticos), **rustc EMITE los binarios del 4/4**
# (arje-zero, hammerd): un rustc distinto ⇒ of_tree DIVERGE del baseline Alpine 9adefb82. Por eso
# el criterio no es igualdad con Alpine sino AUTO-CONSISTENCIA: el REF se RECOMPUTA con hammer-rust
# (nuevo EXPECT_REF) y la VM debe reproducir ESE. El swap entra en $TOOLCHAIN, que sirve TANTO al
# build host del REF (este script, abajo) COMO al toolchain in-VM del builder (--toolchain); se
# restaura al salir (trap). hammer-rust se construye con scripts/rust-frontier (climb mrustc→1.91.1).
# SWAP_RUST=1 → overlay .scratch/rust-1.91.1-prefix sobre /toolchain/usr/{bin,lib}.
# RUST_PREFIX=DIR → prefix hammer-rust alternativo (default .scratch/rust-1.91.1-prefix).
# RUST_EXPECT_REF=… → REF conocido-bueno de hammer-rust (cross-check; vacío ⇒ sólo lo imprime).
# ⚠️ Usá PRESEED=hammerd con SWAP_RUST: arje-zero (monorepo tawasuyu) vendorea ~1973 crates;
# en un rebuild in-VM completo (PRESEED=all) ese vendor desborda el rootfs en RAM de la VM
# (ENOSPC). PRESEED=hammerd preseedea el arje-zero host-built (hammer-rust, locked) y sólo
# reconstruye hammerd in-VM. ✓ REPRODUCIBLE verificado así (2026-06-18, of_tree 7fa6cb4e).
if [[ "${SWAP_RUST:-0}" == 1 ]]; then
RUST_PREFIX="${RUST_PREFIX:-.scratch/rust-1.91.1-prefix}"
[[ -x "$RUST_PREFIX/bin/rustc" ]] || die "SWAP_RUST=1 pero no veo el prefix hammer-rust en $RUST_PREFIX (corré el climb: scripts/rust-frontier/README.md)"
say "variante b (compilador) — swapear hammer-rust 1.91.1 en $TOOLCHAIN (criterio: auto-consistencia)"
scripts/rust-frontier/swap-rust-into-toolchain.sh --prefix "$RUST_PREFIX" --toolchain "$TOOLCHAIN"
trap 'scripts/rust-frontier/swap-rust-into-toolchain.sh --restore --toolchain "$TOOLCHAIN" >/dev/null 2>&1 || true' EXIT
# OJO: el input-hash del store NO incluye el rustc (recipe.hash_inputs = source+compiler+target+
# link+patches+flags+phases+deps), así que en el ./store compartido arje-zero/hammerd quedarían
# CACHEADOS con los bytes de Alpine y el swap sería un no-op. Usamos un store DEDICADO (store-rust)
# para forzar el rebuild de los 4/4 con hammer-rust; así el of_tree refleja el compilador nuevo.
STORE="${RUST_STORE:-store-rust}"
say "SWAP_RUST: store dedicado $STORE (el ./store baseline no se toca; fuerza rebuild con hammer-rust)"
# Con hammer-rust el REF NO es el 9adefb82 de Alpine; comparamos contra RUST_EXPECT_REF, el
# of_tree(stage1) auto-consistente de hammer-rust 1.91.1 (reproducido 2× en host, 2026-06-17,
# store-rust y store-rust2). Override con RUST_EXPECT_REF= para re-anclar.
EXPECT_REF="${RUST_EXPECT_REF:-b3:7fa6cb4e70a934d72206cf3d95cf46a13f0a6464ec73855b98d547195f4ed047}"
fi
# 1) Store baseline: stage1 (idempotente; rebuildea lo que falte con -mcpu=baseline ya en fuente).
say "stage1 — construir y sellar el rootfs baseline"
"$HAMMER" --store "$STORE" bootstrap stage1 --seed-hash "$SEED_HASH" --recipes recipes
ROOTFS_HASH="$(ls -d "$STORE"/*-stage1-rootfs | head -1 | sed -E 's#.*/([0-9a-f]{64})-stage1-rootfs#b3:\1#')"
[[ "$ROOTFS_HASH" == b3:* ]] || die "no encuentro el stage1-rootfs en $STORE"
say "stage1 rootfs: $ROOTFS_HASH"
# 2) Referencia of_tree(stage1) que la VM debe reproducir.
REF="$("$HAMMER" --store "$STORE" bootstrap stage2 --rootfs "$ROOTFS_HASH" 2>&1 \
| grep -oE 'content-hash: b3:[0-9a-f]{64}|content=b3:[0-9a-f]{64}' | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
[[ "$REF" == b3:* ]] || die "no obtuve la referencia de stage2"
say "referencia of_tree(stage1) = $REF"
if [[ -n "$EXPECT_REF" && "$REF" != "$EXPECT_REF" ]]; then
printf '\033[1;33mAVISO: la referencia difiere de la conocida-buena del host de dev:\n esta %s\n espera %s\nReproducibilidad entre máquinas rota O cambió un pin (toolchain/recipes/seed). Seguimos igual.\033[0m\n' "$REF" "$EXPECT_REF"
fi
# 3) Inyectar los módulos del kernel DE ESTA máquina al toolchain (el builder los carga: overlay para
# el sandbox bwrap, e1000 para la red del vendoring). Deben matchear el kernel que bootea la VM.
# La versión de los módulos DEBE matchear el kernel que bootea la VM ($KERNEL), no el del host
# corriendo el script ($(uname -r)) — en máquinas con varios kernels instalados difieren y el
# insmod falla con "version magic mismatch" → sin overlayfs, bwrap revienta. Sacamos la versión
# del propio bzImage; si no se puede, caemos a uname -r.
KVER="$(file -bL "$KERNEL" 2>/dev/null | grep -oE 'version [0-9][^ ]*' | awk '{print $2}')"
[[ -n "$KVER" && -d "/lib/modules/$KVER" ]] || KVER="$(uname -r)"
say "inyectar overlay.ko + e1000.ko (kernel $KVER) al toolchain"
moddir="/lib/modules/$KVER"
for m in overlay e1000; do
src="$(find "$moddir" -name "$m.ko*" 2>/dev/null | head -1)"
if [[ -n "$src" ]]; then
# Descomprimir si viene .ko.zst/.ko.gz/.ko.xz; el builder hace insmod del .ko crudo.
case "$src" in
*.zst) zstd -dqf "$src" -o "$TOOLCHAIN/lib/$m.ko" 2>/dev/null || cp "$src" "$TOOLCHAIN/lib/$(basename "$src")" ;;
*.gz) gzip -dc "$src" > "$TOOLCHAIN/lib/$m.ko" ;;
*.xz) xz -dc "$src" > "$TOOLCHAIN/lib/$m.ko" ;;
*) cp "$src" "$TOOLCHAIN/lib/$m.ko" ;;
esac
echo " $m: $src"
else
echo " $m: NO encontrado en $moddir (si la VM lo necesita, fallará: bwrap overlay / red)"
fi
done
# 3b) Auto-alojamiento *puro* (variante b, SDD 11 §7.2b): reemplazar piezas del toolchain Alpine por
# recetas hammer construidas desde fuente, con Stage 2 reverificando que `of_tree(stage1')` NO
# cambia (el make hammer compila los 4/4 igual de bit-a-bit que el de Alpine). Opt-in:
# SWAP_MAKE=1 → construye recipes/make.toml y lo monta sobre /toolchain/usr/bin/make.
# SWAP_BUSYBOX=1 → monta el busybox de hammer sobre /toolchain/bin/busybox (los symlinks de
# applets de Alpine — sh/sed/grep/awk/tar/find — pasan a usarlo; cp/mkdir/
# install siguen siendo GNU coreutils, intactos).
# SWAP_LINUX_HEADERS=1 → construye recipes/linux-headers.toml y swap-directorio de los 13 subdirs
# kernel-owned de /toolchain/usr/include (linux/asm/…); musl bits/sys intactos.
# SWAP_BWRAP=1 → construye recipes/bwrap.toml (+libcap, su dep, vía deps.build) y lo monta
# sobre /toolchain/usr/bin/bwrap (el sandbox del propio lab).
# SWAP_COREUTILS=1 → construye recipes/coreutils.toml (multicall) y monta el binario sobre
# /toolchain/bin/coreutils; los ~100 symlinks (cp/mkdir/install/…) lo siguen.
# SWAPS="name=hash[:rel] …" → swaps explícitos extra (formato del flag --swap).
# Por defecto, off ⇒ corrida pura-Alpine (variante a), idéntica a la baseline conocida-buena.
# Piezas validadas en host: make+busybox reproducen of_tree=9adefb82 in-VM; linux-headers da un
# header-tree byte-idéntico a Alpine (diff -r vacío); bwrap rebuildea musl byte-idéntico de sandbox.
SWAP_ARGS=()
if [[ "${SWAP_MAKE:-0}" == 1 ]]; then
say "variante b — construir make desde fuente (recipes/make.toml) y swapearlo en /toolchain"
MAKE_HASH="$("$HAMMER" --store "$STORE" build recipes/make.toml | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
[[ "$MAKE_HASH" == b3:* ]] || die "no obtuve el hash sellado de make"
say "make hammer: $MAKE_HASH"
SWAP_ARGS+=(--swap "make=$MAKE_HASH")
fi
if [[ "${SWAP_BUSYBOX:-0}" == 1 ]]; then
say "variante b — construir busybox desde fuente y swapearlo en /toolchain/bin/busybox"
BB_HASH="$("$HAMMER" --store "$STORE" build recipes/busybox.toml | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
[[ "$BB_HASH" == b3:* ]] || die "no obtuve el hash sellado de busybox"
say "busybox hammer: $BB_HASH"
SWAP_ARGS+=(--swap "busybox=$BB_HASH:bin/busybox")
fi
if [[ "${SWAP_LINUX_HEADERS:-0}" == 1 ]]; then
# Pieza 3: los headers UAPI del kernel. NO es un binario sino un árbol — el swap-directorio
# (assemble_builder) reemplaza cada subdir entero. Swapeamos exactamente los subdirs que el
# artefacto sellado produce (linux/asm/asm-generic/cxl/fwctl/misc/mtd/rdma/regulator/scsi/sound/
# video/xen), que son justo los que el paquete linux-headers de Alpine posee; los de musl
# (bits/sys/net/…) quedan intactos.
say "variante b — construir linux-headers desde fuente y swapear /toolchain/usr/include/{linux,asm,…}"
LH_HASH="$("$HAMMER" --store "$STORE" build recipes/linux-headers.toml | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
[[ "$LH_HASH" == b3:* ]] || die "no obtuve el hash sellado de linux-headers"
say "linux-headers hammer: $LH_HASH"
LH_INC="$(ls -d "$STORE"/*-linux-headers/usr/include 2>/dev/null | head -1)"
[[ -d "$LH_INC" ]] || die "no encuentro usr/include en el linux-headers sellado"
for d in "$LH_INC"/*/; do
sub="$(basename "$d")"
SWAP_ARGS+=(--swap "linux-headers=${LH_HASH}:usr/include/$sub")
done
fi
if [[ "${SWAP_BWRAP:-0}" == 1 ]]; then
# Pieza 4: bwrap (bubblewrap), EL sandbox del lab. Binario estático ⇒ swap de archivo sobre
# /toolchain/usr/bin/bwrap. Su dep libcap la construye y materializa el lab solo (deps.build).
# bwrap es herramienta, no input del 4/4: no necesita casar byte-a-byte con Alpine, sólo aislar
# igual (validado en host — musl rebuildeó byte-idéntico bajo hammer-bwrap).
say "variante b — construir bwrap (+libcap) desde fuente y swapearlo en /toolchain/usr/bin/bwrap"
BW_HASH="$("$HAMMER" --store "$STORE" build recipes/bwrap.toml | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
[[ "$BW_HASH" == b3:* ]] || die "no obtuve el hash sellado de bwrap"
say "bwrap hammer: $BW_HASH"
SWAP_ARGS+=(--swap "bwrap=${BW_HASH}:usr/bin/bwrap")
fi
if [[ "${SWAP_COREUTILS:-0}" == 1 ]]; then
# Pieza 5: GNU coreutils (cp/mkdir/ln/chmod/mv/install…). Multicall (--enable-single-binary), igual
# layout que Alpine: un binario /bin/coreutils + ~100 symlinks. UN swap del binario rutea todos los
# applets (los symlinks del toolchain ya apuntan a coreutils). Tool, no input: validado en host —
# musl Y busybox rebuildearon byte-idéntico bajo hammer-coreutils.
say "variante b — construir coreutils desde fuente y swapearlo en /toolchain/bin/coreutils"
CU_HASH="$("$HAMMER" --store "$STORE" build recipes/coreutils.toml | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
[[ "$CU_HASH" == b3:* ]] || die "no obtuve el hash sellado de coreutils"
say "coreutils hammer: $CU_HASH"
SWAP_ARGS+=(--swap "coreutils=${CU_HASH}:bin/coreutils")
fi
for s in ${SWAPS:-}; do SWAP_ARGS+=(--swap "$s"); done
# 4) Ensamblar el builder con la referencia embebida.
say "ensamblar builder (toolchain in-rootfs + hammer baseline + ref${SWAP_ARGS:+ + swaps})"
"$HAMMER" --store "$STORE" bootstrap builder \
--stage1 "$ROOTFS_HASH" --seed-hash "$SEED_HASH" \
--hammer-bin "$HAMMER_BIN" \
--toolchain "$TOOLCHAIN" --toolchain-tag "$TOOLCHAIN_TAG" \
--ref-content "$REF" \
"${SWAP_ARGS[@]}" \
--work-cache work --out work/builder-rootfs
# 4b) Preseed opcional para abaratar el rebuild in-VM (sólo hammerd se reconstruye).
if [[ "$PRESEED" == "hammerd" ]]; then
say "preseed musl+busybox+arje-zero (sólo hammerd se reconstruye in-VM)"
for n in musl busybox arje-zero; do
d="$(ls -d "$STORE"/*-"$n" 2>/dev/null | head -1)"
[[ -n "$d" ]] && cp -a "$d" work/builder-rootfs/store/ && echo " preseed: $(basename "$d")"
done
fi
# 4c) HAMMER_KERNEL=1 — wrapper /init para escapar del rootfs (frente kernel-from-source).
# Con un kernel hammer-built (recipes/linux.toml) el `/` del initramfs es el rootfs ABSOLUTO del
# mount-namespace (su propio padre, no movible) y bwrap del sandbox falla en `pivot_root: Invalid
# argument` (el kernel host no lo exigía — quirk suyo). El fix portable (funciona en cualquier
# kernel): un /init PID1 que copia el rootfs a un tmpfs y hace `switch_root`, dejando `/` como un
# mount tmpfs real (pivotable). El kernel debe arrancar con rdinit=/init (ver APPEND abajo). Off por
# defecto ⇒ el camino del kernel host queda intacto (rdinit=/sbin/init directo).
if [[ "${TAKANA_KERNEL:-${HAMMER_KERNEL:-0}}" == 1 ]]; then
say "HAMMER_KERNEL: inyectar /init wrapper (copy-to-tmpfs + switch_root, escapa del rootfs)"
cat > work/builder-rootfs/init <<'INIT'
#!/bin/sh
# PID1 wrapper: el rootfs del initramfs no es pivotable (bwrap pivot_root EINVAL); copiamos a un tmpfs
# y switch_root para que / sea un mount real. Luego exec del init real (arje-zero).
/bin/busybox mkdir -p /newroot
/bin/busybox mount -t tmpfs tmpfs /newroot
cd /
for e in /*; do
[ "$e" = /newroot ] && continue
/bin/busybox cp -a "$e" /newroot/
done
exec /bin/busybox switch_root /newroot /sbin/init
INIT
chmod +x work/builder-rootfs/init
export APPEND="console=ttyS0 rdinit=/init"
fi
# 5) Empaquetar como initramfs. --owner=root:root OBLIGATORIO: si los ficheros viajan con el uid del
# host, el userns de bwrap (mapea 0→0) no lo mapea y el copy-up de overlay falla con EACCES.
say "empaquetar initramfs (cpio newc, --owner=root:root)"
( cd work/builder-rootfs && find . -print0 | cpio --null -o -H newc --owner=root:root 2>/dev/null | gzip -1 ) > work/builder.cpio.gz
echo " work/builder.cpio.gz: $(du -h work/builder.cpio.gz | cut -f1)"
# 6) Bootear + driver no-interactivo → veredicto.
say "bootear la VM y correr rebuild-stage1 (KVM=$KVM MEM=$MEM)"
KVM="$KVM" MEM="$MEM" KERNEL="$KERNEL" BUILDER_CPIO="work/builder.cpio.gz" \
python3 scripts/drive-rebuild.py