Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/nornir-build-thing/tests/iso_boot.rs

//! **Pure-Rust ISO — master + BOOT-under-QEMU/OVMF MATRIX TEST.**
//!
//! Closes the "no ISO-boot proof" gap: masters a bootable UEFI ISO-9660 image in
//! **pure Rust** (via [`nornir_build_thing::PureRustIsoBuilder`] →
//! [`nornir_build_thing::iso_writer::IsoWriter`], zero external ISO tool, zero
//! subprocess in the ISO path), then **boots it under QEMU + OVMF (UEFI)** off a
//! `-cdrom` and asserts the appliance payload reached `KORP-APPLIANCE BOOT-OK` in
//! the captured serial log. A real booted firmware producing real, asserted serial
//! output — not "the bytes were written".
//!
//! ## What is and isn't a subprocess here
//!
//! The **ISO is mastered 100% in Rust** — no `xorriso`/`mkisofs`/`genisoimage`,
//! no `Command::new` on the mastering path (the constellation LAW). This test
//! then shells to exactly two SANCTIONED tools that are NOT ISO tools:
//! * `cargo … --target x86_64-unknown-uefi` — the Rust COMPILER, building the
//!   `tests/fixtures/korp-efistub` `no_std` UEFI payload from source (no vendored
//!   binary blob); and
//! * `qemu-system-x86_64` — the one accepted hypervisor for a KVM/OVMF boot.
//!
//! ## Heavy / `#[ignore]`
//!
//! Self-gates on `qemu-system-x86_64`, an OVMF CODE+VARS pair, and a buildable
//! `x86_64-unknown-uefi` target; SKIPs with a loud note (never a faked green) when
//! any is absent. `#[ignore]`'d so it joins the `--ignored` heavy pass, exactly
//! like the sibling `tunnr` `boot_matrix` / `korp` `korp_matrix` boots.
//!
//! ```bash
//! cargo test -p nornir-build-thing --test iso_boot -- --ignored --nocapture
//! ```

use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::{Duration, Instant};

use nornir_build_thing::iso::{IsoBuild, IsoFormat};
use nornir_build_thing::{build_iso, bt_assert_emit, PureRustIsoBuilder};

/// The boot marker the korp appliance payload prints to serial on success.
const BOOT_MARKER: &str = "KORP-APPLIANCE BOOT-OK";

#[test]
#[ignore = "heavy: masters a pure-Rust ISO then boots it under QEMU+OVMF (UEFI), asserting BOOT-OK"]
fn pure_rust_iso_boots_under_qemu_ovmf_to_boot_ok() {
    // ── self-gate: skip loudly (never fake green) when the host can't boot ──
    let Some(qemu) = which("qemu-system-x86_64") else {
        eprintln!("SKIP pure_rust_iso_boots: no qemu-system-x86_64 on PATH");
        return;
    };
    let Some((ovmf_code, ovmf_vars)) = locate_ovmf() else {
        eprintln!("SKIP pure_rust_iso_boots: no OVMF CODE+VARS pair (install edk2/ovmf)");
        return;
    };

    let tmp = tempfile::tempdir().expect("tempdir");
    let root = tmp.path();

    // ── 1. build the no_std UEFI payload from source (Rust compiler, not an ISO tool) ──
    let Some(efi_app) = build_efi_stub(root) else {
        eprintln!(
            "SKIP pure_rust_iso_boots: could not build the x86_64-unknown-uefi payload \
             (run `rustup target add x86_64-unknown-uefi`)"
        );
        return;
    };

    // ── 2. build the EFI System Partition (FAT) in pure Rust (fatfs) ──
    let staging = root.join("iso-root");
    std::fs::create_dir_all(&staging).unwrap();
    build_esp(&staging.join("efiboot.img"), &efi_app);

    // ── 3. master the bootable UEFI ISO through the REAL builder — PURE RUST ──
    let iso = IsoBuild {
        name: "korp-iso".into(),
        staging: "iso-root".into(),
        volume_id: "KORP_UEFI".into(),
        boot_params: vec!["console=ttyS0".into()],
        format: IsoFormat::Uefi,
        ..Default::default()
    };
    // `into_result`, not a bare `?`: a target build now RECORDS its failure instead
    // of propagating it, and this test wants the builder's own reason at the point it
    // failed rather than an `is_file()` assertion three lines down that says only
    // that no ISO appeared.
    let out = build_iso(&PureRustIsoBuilder::new(), &iso, "korp", root)
        .and_then(|o| o.into_result())
        .unwrap_or_else(|e| panic!("pure-Rust ISO master failed: {e:#}"));
    assert_eq!(out.attestation.record.engine, "pure-rust", "mastered by the pure-Rust builder");
    let iso_path = root.join(".iso-out").join("korp-iso.iso");
    assert!(iso_path.is_file(), "a real .iso was mastered at {}", iso_path.display());

    // ── 4. BOOT it under QEMU + OVMF, capture the serial log, assert BOOT-OK ──
    let serial = boot_and_capture(&qemu, &ovmf_code, &ovmf_vars, &iso_path, root, Duration::from_secs(90));
    eprintln!("─── OVMF serial log ───\n{serial}\n───────────────────────");
    let booted = serial.contains(BOOT_MARKER);

    bt_assert_emit!(
        "iso-build",
        "pure-rust-iso-boots-under-ovmf",
        booted,
        "pure-Rust ISO booted under QEMU/OVMF and reached '{BOOT_MARKER}' (serial {} bytes)",
        serial.len()
    );
}

/// Build the `tests/fixtures/korp-efistub` UEFI payload; `None` if the target is
/// unavailable. Returns the path to the produced `bootx64.efi`.
fn build_efi_stub(root: &Path) -> Option<PathBuf> {
    let fixture = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures/korp-efistub/Cargo.toml");
    let target_dir = root.join("efi-target");
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".into());
    let status = Command::new(&cargo)
        .args([
            "build",
            "--release",
            "--target",
            "x86_64-unknown-uefi",
            "--manifest-path",
        ])
        .arg(&fixture)
        .arg("--target-dir")
        .arg(&target_dir)
        .status()
        .ok()?;
    if !status.success() {
        return None;
    }
    let efi = target_dir.join("x86_64-unknown-uefi/release/bootx64.efi");
    efi.is_file().then_some(efi)
}

/// Format a FAT EFI System Partition at `path` carrying `EFI/BOOT/BOOTX64.EFI` —
/// pure Rust via the `fatfs` crate (no `mkfs`/`mtools` subprocess).
fn build_esp(path: &Path, efi_app: &Path) {
    let f = std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .truncate(true)
        .open(path)
        .expect("create ESP image");
    f.set_len(1440 * 1024).expect("size ESP"); // 1.44 MiB FAT12
    fatfs::format_volume(&f, fatfs::FormatVolumeOptions::new().volume_label(*b"EFIESP     "))
        .expect("format FAT ESP");
    let fs = fatfs::FileSystem::new(&f, fatfs::FsOptions::new()).expect("mount ESP");
    let root = fs.root_dir();
    let boot = root.create_dir("EFI").unwrap().create_dir("BOOT").unwrap();
    let mut out = boot.create_file("BOOTX64.EFI").unwrap();
    let bytes = std::fs::read(efi_app).expect("read efi payload");
    out.write_all(&bytes).unwrap();
    out.flush().unwrap();
}

/// Boot the ISO as a UEFI `-cdrom` under OVMF pflash, capturing the serial console
/// to a file. Polls for [`BOOT_MARKER`] up to `timeout`, then kills QEMU (the guest
/// hlt-loops after printing, so it never exits on its own).
fn boot_and_capture(
    qemu: &Path,
    ovmf_code: &Path,
    ovmf_vars_tmpl: &Path,
    iso: &Path,
    workdir: &Path,
    timeout: Duration,
) -> String {
    // A per-boot writable OVMF NVRAM copy (the vars store must not be read-only).
    let vars = workdir.join("OVMF_VARS.fd");
    std::fs::copy(ovmf_vars_tmpl, &vars).expect("stage writable OVMF_VARS");
    let log = workdir.join("serial.log");

    let mut child = Command::new(qemu)
        .args(["-machine", "q35,accel=kvm:tcg", "-m", "512", "-display", "none", "-no-reboot"])
        .arg("-drive")
        .arg(format!("if=pflash,format=raw,readonly=on,file={}", ovmf_code.display()))
        .arg("-drive")
        .arg(format!("if=pflash,format=raw,file={}", vars.display()))
        .arg("-cdrom")
        .arg(iso)
        .args(["-boot", "d"])
        .arg("-serial")
        .arg(format!("file:{}", log.display()))
        .spawn()
        .expect("spawn qemu");

    let deadline = Instant::now() + timeout;
    let mut captured = String::new();
    while Instant::now() < deadline {
        if let Ok(bytes) = std::fs::read(&log) {
            captured = String::from_utf8_lossy(&bytes).replace('\u{0}', "");
            if captured.contains(BOOT_MARKER) {
                break;
            }
        }
        if let Ok(Some(_)) = child.try_wait() {
            break; // qemu exited on its own
        }
        std::thread::sleep(Duration::from_millis(400));
    }
    let _ = child.kill();
    let _ = child.wait();
    if let Ok(bytes) = std::fs::read(&log) {
        captured = String::from_utf8_lossy(&bytes).replace('\u{0}', "");
    }
    captured
}

/// Resolve a bare binary name on `PATH`.
fn which(bin: &str) -> Option<PathBuf> {
    let path = std::env::var_os("PATH")?;
    std::env::split_paths(&path)
        .map(|d| d.join(bin))
        .find(|c| c.is_file())
}

/// Locate an OVMF firmware CODE+VARS pair (the common Debian/Fedora/Arch spots).
fn locate_ovmf() -> Option<(PathBuf, PathBuf)> {
    let pairs = [
        ("/usr/share/OVMF/OVMF_CODE_4M.fd", "/usr/share/OVMF/OVMF_VARS_4M.fd"),
        ("/usr/share/OVMF/OVMF_CODE.fd", "/usr/share/OVMF/OVMF_VARS.fd"),
        ("/usr/share/edk2/x64/OVMF_CODE.4m.fd", "/usr/share/edk2/x64/OVMF_VARS.4m.fd"),
        ("/usr/share/edk2/ovmf/OVMF_CODE.fd", "/usr/share/edk2/ovmf/OVMF_VARS.fd"),
        ("/usr/share/qemu/OVMF_CODE.fd", "/usr/share/qemu/OVMF_VARS.fd"),
    ];
    pairs.iter().find_map(|(c, v)| {
        let (c, v) = (PathBuf::from(c), PathBuf::from(v));
        (c.is_file() && v.is_file()).then_some((c, v))
    })
}