From 462e0275d3cc5ef13a495dfd13ecc34b87e9a883 Mon Sep 17 00:00:00 2001 From: sergio Date: Sat, 20 Jun 2026 10:42:40 -0400 Subject: [PATCH] Etapa B (B1+B2): boot desde disco real ext4, sin switch_root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B1 — kernel con disco+FS: linux.toml añade VIRTIO_BLK/VIRTIO_PCI/VIRTIO_NET/EXT4_FS built-in (=y, sin módulos), aditivo al path initramfs. B2 — infra de disco: - scripts/disk-image.sh: empaqueta un rootfs como imagen ext4 booteable con `mke2fs -d` bajo `unshare -r` (archivos root-owned sin sudo, como el cpio --owner=root:root; evita EACCES en el copy-up de overlay del rebuild). - scripts/drive-rebuild.py: modo DISK= ⇒ QEMU monta la imagen como virtio /dev/vda y el kernel arranca con `root=/dev/vda rw rdinit=/sbin/init` — sin initramfs. Verificado en QEMU/KVM: el kernel monta /dev/vda ext4 como root REAL y arje-zero arranca como PID 1 DIRECTO, sin el hack /init+switch_root (el muro pivot_root del path initramfs desaparece por construcción: / ya es un mount pivotable). Co-Authored-By: Claude Opus 4.8 --- recipes/linux.toml | 7 +++++- scripts/disk-image.sh | 53 ++++++++++++++++++++++++++++++++++++++++ scripts/drive-rebuild.py | 23 ++++++++++++++--- 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100755 scripts/disk-image.sh diff --git a/recipes/linux.toml b/recipes/linux.toml index 1f158442..48118e10 100644 --- a/recipes/linux.toml +++ b/recipes/linux.toml @@ -25,6 +25,10 @@ # Config: defconfig x86_64 (base PC booteable en QEMU: 8250 serial, initrd, PCI) + ajustes monolíticos # - E1000/OVERLAY_FS/USER_NS =y (built-in): el builder usa e1000 (vendoring) + overlay (sandbox bwrap) # + user namespaces (bwrap). Built-in ⇒ sin inyección de .ko ni version-magic. +# - VIRTIO_BLK/VIRTIO_PCI/VIRTIO_NET/EXT4_FS =y (Etapa B, imagen en disco real): permiten montar un +# disco virtio (`/dev/vda`, ext4) como root sin initramfs ⇒ `root=/dev/vda rw` directo, sin el hack +# /init+switch_root. Built-in (no módulos) porque no hay initramfs que cargue .ko antes del root. +# Aditivos: no tocan el camino initramfs del selfhost-verify (que sigue booteando igual). # - MODULE_SIG/DEBUG_INFO_BTF off: evitan firma de módulos y pahole (BTF), ausente del toolchain. # - UNWINDER_FRAME_POINTER en vez de ORC: reduce objtool pero NO lo elimina — en x86_64 las # mitigaciones (RETHUNK/retpoline-validation) seleccionan CONFIG_OBJTOOL igual, y objtool necesita @@ -71,13 +75,14 @@ scripts/config -d MODULE_SIG -d MODULE_SIG_ALL -d DEBUG_INFO_BTF -d DEBUG_INFO \ -e E1000 -e OVERLAY_FS -e USER_NS -e NAMESPACES \ -e FANOTIFY -e FANOTIFY_ACCESS_PERMISSIONS \ -e OVERLAY_FS_REDIRECT_DIR -e OVERLAY_FS_INDEX -e OVERLAY_FS_XINO_AUTO -e OVERLAY_FS_METACOPY \ + -e VIRTIO -e VIRTIO_PCI -e VIRTIO_BLK -e VIRTIO_NET -e EXT4_FS \ -d DRM -d AGP -d FB -d BACKLIGHT_CLASS_DEVICE -d SOUND -d SND \ -d USB_SUPPORT -d MEDIA_SUPPORT -d INFINIBAND -d BT -d NFC -d CAN \ -d WLAN -d WIRELESS -d CFG80211 -d MAC80211 -d RFKILL \ -d HID_SUPPORT -d HID -d INPUT_MOUSE -d INPUT_JOYSTICK -d INPUT_TABLET -d INPUT_TOUCHSCREEN \ -d WATCHDOG -d THUNDERBOLT -d FIREWIRE -d HWMON -d POWER_SUPPLY -d MEMSTICK -d MMC \ -d ATA -d SCSI -d NVME_CORE -d MD -d STAGING -d IIO -d PPS -d PTP_1588_CLOCK \ - -d EXT4_FS -d XFS_FS -d BTRFS_FS -d F2FS_FS -d JFS_FS -d REISERFS_FS -d GFS2_FS -d NTFS3_FS \ + -d XFS_FS -d BTRFS_FS -d F2FS_FS -d JFS_FS -d REISERFS_FS -d GFS2_FS -d NTFS3_FS \ -d DRM_I915 -d DRM_AMDGPU -d DRM_NOUVEAU -d DRM_RADEON && \ make ARCH=x86_64 olddefconfig """ diff --git a/scripts/disk-image.sh b/scripts/disk-image.sh new file mode 100755 index 00000000..57e167b1 --- /dev/null +++ b/scripts/disk-image.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# disk-image.sh — Etapa B (imagen en disco real): empaqueta un rootfs como imagen ext4 booteable de +# disco virtio (/dev/vda) en vez de initramfs. Con un kernel que trae VIRTIO_BLK+EXT4 built-in +# (recipes/linux.toml), QEMU monta la imagen como root REAL y el kernel arranca con +# `root=/dev/vda rw rdinit=/sbin/init` — sin initramfs, sin el wrapper /init+switch_root: `/` ya es un +# mount ext4 pivotable (el muro pivot_root del path initramfs desaparece por construcción). +# +# `mke2fs -d ` puebla la imagen DESDE un directorio sin montar nada y sin root (e2fsprogs >= 1.43). +# +# Uso: +# ./scripts/disk-image.sh # crea work/hammer-disk.img desde work/builder-rootfs +# BOOT=1 KERNEL= KVM=1 ./scripts/disk-image.sh # además bootea y corre rebuild-stage1 +# +# Variables: +# ROOTFS dir rootfs a empaquetar (default work/builder-rootfs, lo arma selfhost-verify) +# IMG imagen de salida (default work/hammer-disk.img) +# SIZE tamaño de la imagen (default 6G; el rootfs ronda ~2G + holgura para el rebuild) +# BOOT 1 ⇒ bootea con drive-rebuild.py tras crear la imagen +# KERNEL bzImage con VIRTIO_BLK+EXT4 (requerido si BOOT=1) +# KVM/MEM/NET passthrough a drive-rebuild.py +set -eu +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +ROOTFS="${ROOTFS:-work/builder-rootfs}" +IMG="${IMG:-work/hammer-disk.img}" +SIZE="${SIZE:-6G}" + +[ -d "$ROOTFS" ] || { echo "no existe ROOTFS: $ROOTFS (corré selfhost-verify para armar el builder)" >&2; exit 1; } +[ -x "$ROOTFS/sbin/init" ] || [ -L "$ROOTFS/sbin/init" ] || { echo "ROOTFS sin /sbin/init (arje-zero)" >&2; exit 1; } + +echo "==> rootfs : $ROOTFS ($(du -sh "$ROOTFS" | cut -f1))" +echo "==> imagen : $IMG ($SIZE, ext4, label hammer-root)" +rm -f "$IMG" +# Ownership root en la imagen (igual que el `--owner=root:root` del cpio): el rebuild usa bwrap+overlay +# con un userns que mapea 0→0; si los ficheros van con el uid del host (1000) el copy-up falla EACCES. +# `unshare -r` mapea nuestro uid→0, así los ficheros uid-1000 se ven como root y mke2fs escribe uid 0 +# en la imagen — sin sudo. mke2fs -d puebla desde el dir sin montar. Fallback a mke2fs directo si no hay +# unshare (la imagen quedaría uid-host: bootea y da shell, pero el rebuild fallaría en el copy-up). +if command -v unshare >/dev/null 2>&1 && unshare -r true 2>/dev/null; then + unshare -r mke2fs -q -t ext4 -L hammer-root -d "$ROOTFS" "$IMG" "$SIZE" +else + echo " (sin unshare -r: imagen con uid del host; el rebuild in-VM podría fallar el copy-up)" >&2 + mke2fs -q -t ext4 -L hammer-root -d "$ROOTFS" "$IMG" "$SIZE" +fi +echo "==> imagen creada: $(du -h "$IMG" | cut -f1)" + +if [ "${BOOT:-0}" = 1 ]; then + [ -n "${KERNEL:-}" ] || { echo "BOOT=1 requiere KERNEL=" >&2; exit 1; } + echo "==> booteando de disco (root=/dev/vda, sin initramfs ni switch_root)" + DISK="$IMG" KERNEL="$KERNEL" KVM="${KVM:-0}" MEM="${MEM:-6144}" NET="${NET:-1}" \ + python3 scripts/drive-rebuild.py +fi diff --git a/scripts/drive-rebuild.py b/scripts/drive-rebuild.py index 5eed8532..228975cf 100755 --- a/scripts/drive-rebuild.py +++ b/scripts/drive-rebuild.py @@ -8,6 +8,10 @@ # # Variables de entorno (todas opcionales): # BUILDER_CPIO initramfs del builder (default work/builder.cpio.gz) +# DISK imagen ext4 a bootear como ROOT real (Etapa B). Si está seteada, se ignora el +# initramfs: QEMU monta la imagen como disco virtio (/dev/vda) y el kernel arranca con +# `root=/dev/vda rw rdinit=/sbin/init` — sin initramfs ni switch_root (/ ya es un mount +# ext4 real, pivotable). Requiere un kernel con VIRTIO_BLK+EXT4 (recipes/linux.toml). # KERNEL bzImage/vmlinuz a bootear (default /boot/vmlinuz-linux) # MEM RAM de la VM en MiB (default 6144) # CPU modelo de CPU TCG (default Broadwell; AVX2 sin KVM) @@ -29,6 +33,9 @@ def envpath(name, default): CPIO = envpath("BUILDER_CPIO", "work/builder.cpio.gz") +DISK = os.environ.get("DISK", "") # Etapa B: si está, booteamos de disco real en vez de initramfs. +if DISK and not os.path.isabs(DISK): + DISK = os.path.join(ROOT, DISK) KERNEL = os.environ.get("KERNEL", "/boot/vmlinuz-linux") LOG = envpath("LOG", "work/rebuild-run.log") MEM = os.environ.get("MEM", "6144") @@ -37,7 +44,8 @@ DEADLINE = int(os.environ.get("DEADLINE", "14400")) WANT_KVM = os.environ.get("KVM", "0") == "1" WANT_NET = os.environ.get("NET", "1") == "1" -for p, what in [(CPIO, "initramfs del builder"), (KERNEL, "kernel")]: +_needed = [(DISK, "imagen de disco")] if DISK else [(CPIO, "initramfs del builder")] +for p, what in _needed + [(KERNEL, "kernel")]: if not os.path.exists(p): sys.exit(f"no existe {what}: {p}") @@ -54,12 +62,19 @@ net = ["-netdev", "user,id=n0", "-device", "e1000,netdev=n0"] if WANT_NET else [ # (p.ej. el frente kernel-from-source: el bzImage hammer bootea pero bwrap falla en pivot_root porque / # es el rootfs absoluto del namespace; `rootfstype=tmpfs` NO lo arregla — la causa es que / no tiene # mount padre, no el tipo de fs). Override con APPEND="…". -APPEND = os.environ.get("APPEND", "console=ttyS0 rdinit=/sbin/init") +# Boot de disco (Etapa B) vs initramfs. En disco / ya es un mount ext4 real ⇒ rdinit=/sbin/init directo +# (arje-zero PID1), sin el wrapper switch_root del path initramfs. +if DISK: + APPEND = os.environ.get("APPEND", "console=ttyS0 root=/dev/vda rw rdinit=/sbin/init") + media = ["-drive", f"file={DISK},if=virtio,format=raw", "-append", APPEND] +else: + APPEND = os.environ.get("APPEND", "console=ttyS0 rdinit=/sbin/init") + media = ["-initrd", CPIO, "-append", APPEND] QEMU = (["qemu-system-x86_64", "-m", MEM, "-no-reboot", "-nographic"] + accel + net + - ["-kernel", KERNEL, "-initrd", CPIO, "-append", APPEND]) + ["-kernel", KERNEL] + media) print(f"==> kernel : {KERNEL}") -print(f"==> initramfs : {CPIO}") +print(f"==> {'disco ' if DISK else 'initramfs '}: {DISK or CPIO}") print(f"==> mem : {MEM} MiB deadline: {DEADLINE}s net: {'sí' if WANT_NET else 'no'}") print(f"==> log : {LOG}")