From f41cc5aef1f71530b204b0ea50def15c8e383372 Mon Sep 17 00:00:00 2001 From: Danny Date: Fri, 20 Mar 2026 08:57:03 +0100 Subject: [PATCH] Wip --- docs/CHANGELOG.md | 43 +++++++ kernel/build-kernel.sh | 227 +++++++++++++++++++++++++++++++++++++ kernel/config | 23 ++++ src/iso/build-initramfs.sh | 80 +++++++++++++ src/iso/build-iso-arch.sh | 44 ++++++- src/iso/initramfs/init | 101 +++++++++++++++++ tests/run-tests.sh | 51 +++++++-- 7 files changed, 556 insertions(+), 13 deletions(-) create mode 100755 kernel/build-kernel.sh create mode 100755 src/iso/build-initramfs.sh create mode 100755 src/iso/initramfs/init diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 644b4b9..9376c82 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,49 @@ --- +## V30 2026-03-20 07:50:00 + +**Add kernel build script, initramfs, and live ISO boot support (Phase 4)** + +### Changes: +- Created `kernel/build-kernel.sh` — automated kernel build script: + - Downloads Linux 6.19.9 from cdn.kernel.org (with mirror fallback) + - Applies DarkForge config via `make olddefconfig` + - Verifies all critical config options (EFI_STUB, NVME, EXT4, PREEMPT, etc.) + - Compiles bzImage with `-j32` and KCFLAGS="-march=znver4 -pipe" + - Outputs to `kernel/vmlinuz`, `kernel/vmlinuz.efi`, `kernel/System.map` + - Installs modules to `kernel/modules/` +- Updated `kernel/config`: + - Added CONFIG_CMDLINE_BOOL=y with serial console (ttyS0,115200n8) + - Added CONFIG_CMDLINE_OVERRIDE=n (allows efibootmgr to override at boot) + - Added CONFIG_BLK_DEV_INITRD=y (needed for live ISO squashfs boot) + - Added CONFIG_SERIAL_8250=y + CONFIG_SERIAL_8250_CONSOLE=y +- Created `src/iso/initramfs/init` — live ISO init script: + - Mounts proc/sys/devtmpfs, scans for DarkForge media on CD/USB/NVMe + - Mounts squashfs root, creates tmpfs overlay for writable root + - switch_roots into the live system + - Falls back to emergency shell if media not found +- Created `src/iso/build-initramfs.sh` — builds initramfs.cpio.gz from busybox +- Updated `src/iso/build-iso-arch.sh`: + - Now builds initramfs automatically if not present + - Includes initramfs in the EFI partition alongside kernel + - Creates startup.nsh for UEFI shell fallback + - Dynamically sizes the ESP based on kernel + initramfs size +- Updated `tests/run-tests.sh` QEMU test: + - Uses QEMU direct kernel boot (`-kernel` + `-initrd`) when a compiled kernel + is available — more reliable than UEFI ISO boot for testing + - Falls back to OVMF UEFI boot when no compiled kernel exists + +### Plan deviation/changes: +- Kernel version bumped from 6.19.8 to 6.19.9 (latest 6.19.x stable) + +### What is missing/needs polish: +- Kernel must be compiled by running `bash kernel/build-kernel.sh` on the target machine +- Once kernel is built, `qemu.kernel_boots` and `qemu.reaches_userspace` should pass +- Live ISO boot chain: kernel → initramfs → squashfs → overlay → init — needs end-to-end test + +--- + ## V29 2026-03-20 07:36:03 **Fix brace expansion failure in ISO build script causing missing directories** diff --git a/kernel/build-kernel.sh b/kernel/build-kernel.sh new file mode 100755 index 0000000..abb578f --- /dev/null +++ b/kernel/build-kernel.sh @@ -0,0 +1,227 @@ +#!/bin/bash +# ============================================================================ +# DarkForge Linux — Kernel Build Script +# ============================================================================ +# Downloads, configures, and compiles the Linux kernel for DarkForge. +# +# Prerequisites (Arch Linux): +# sudo pacman -S base-devel bc flex bison libelf perl openssl +# +# Usage: +# cd /path/to/project-root +# bash kernel/build-kernel.sh +# +# Output: +# kernel/vmlinuz — Compressed kernel (bzImage) +# kernel/vmlinuz.efi — Copy with .efi extension for EFISTUB +# kernel/System.map — Symbol map +# kernel/modules/ — Kernel modules (if any) +# build/linux-/ — Full kernel source tree (for module builds) +# +# Notes: +# - Runs as regular user (no sudo needed for compile) +# - Uses the config at kernel/config +# - Applies 'make olddefconfig' to fill in defaults for unspecified options +# - Builds with -j32 (16C/32T) +# ============================================================================ + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +# --- Configuration ----------------------------------------------------------- +KERNEL_VERSION="6.19.9" +KERNEL_MAJOR="6.19" +KERNEL_URL="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz" +KERNEL_SIGN_URL="${KERNEL_URL}.sign" +BUILD_DIR="${PROJECT_ROOT}/build" +KERNEL_SRC="${BUILD_DIR}/linux-${KERNEL_VERSION}" +CONFIG_FILE="${SCRIPT_DIR}/config" +JOBS=32 + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +info() { echo -e "${CYAN}>>> $1${NC}"; } +ok() { echo -e "${GREEN}>>> $1${NC}"; } +warn() { echo -e "${YELLOW}!!! $1${NC}"; } +die() { echo -e "${RED}!!! $1${NC}"; exit 1; } + +# --- Preflight --------------------------------------------------------------- +info "DarkForge Kernel Builder — Linux ${KERNEL_VERSION}" +echo "" + +# Check required tools +for tool in gcc make flex bison bc perl; do + command -v "$tool" >/dev/null 2>&1 || die "Missing: $tool — install with pacman" +done + +# Check for libelf headers (needed for CONFIG_BPF_SYSCALL, etc.) +if ! pkg-config --exists libelf 2>/dev/null; then + warn "libelf not found — install with: sudo pacman -S libelf" + warn "Continuing anyway (may fail if CONFIG_BPF_SYSCALL=y)" +fi + +# Check for openssl headers (needed for module signing) +if ! pkg-config --exists openssl 2>/dev/null && ! [ -f /usr/include/openssl/opensslv.h ]; then + warn "OpenSSL headers not found — install with: sudo pacman -S openssl" +fi + +# --- Download kernel source -------------------------------------------------- +mkdir -p "${BUILD_DIR}" + +TARBALL="${BUILD_DIR}/linux-${KERNEL_VERSION}.tar.xz" +if [ -f "$TARBALL" ]; then + info "Kernel tarball already downloaded: ${TARBALL}" +else + info "Downloading Linux ${KERNEL_VERSION}..." + # Try cdn.kernel.org first, then mirrors + for url in \ + "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz" \ + "https://mirrors.edge.kernel.org/pub/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz" \ + "https://mirror.aarnet.edu.au/pub/ftp.kernel.org/linux/kernel/v6.x/linux-${KERNEL_VERSION}.tar.xz"; do + if curl --connect-timeout 15 -fL# -o "$TARBALL" "$url" 2>&1; then + ok "Downloaded from ${url}" + break + else + warn "Failed: ${url}" + rm -f "$TARBALL" + fi + done +fi + +[ -f "$TARBALL" ] || die "Failed to download kernel tarball" + +# Verify tarball is valid +if ! xz -t "$TARBALL" 2>/dev/null; then + die "Kernel tarball is corrupt — delete ${TARBALL} and re-run" +fi + +# --- Extract kernel source --------------------------------------------------- +if [ -d "$KERNEL_SRC" ] && [ -f "$KERNEL_SRC/Makefile" ]; then + info "Kernel source already extracted: ${KERNEL_SRC}" +else + info "Extracting Linux ${KERNEL_VERSION}..." + tar -xf "$TARBALL" -C "${BUILD_DIR}" + [ -d "$KERNEL_SRC" ] || die "Expected directory ${KERNEL_SRC} not found after extraction" + ok "Extracted to ${KERNEL_SRC}" +fi + +# --- Apply DarkForge config -------------------------------------------------- +info "Applying DarkForge kernel config..." + +# Copy our config +cp "$CONFIG_FILE" "${KERNEL_SRC}/.config" + +# Update version target in config header comment (informational only) +# The actual version comes from the kernel source, not the config + +# Run olddefconfig to fill in all unspecified options with defaults +# This is critical — our config only specifies ~176 options out of thousands +cd "$KERNEL_SRC" +make olddefconfig + +ok "Config applied — $(grep -c '=y\|=m' .config) options enabled" + +# Verify critical options survived olddefconfig +CRITICAL_OPTS=( + "CONFIG_EFI_STUB=y" + "CONFIG_BLK_DEV_NVME=y" + "CONFIG_EXT4_FS=y" + "CONFIG_PREEMPT=y" + "CONFIG_MODULES=y" + "CONFIG_DRM=y" + "CONFIG_R8169=y" + "CONFIG_EFI=y" +) +FAIL=0 +for opt in "${CRITICAL_OPTS[@]}"; do + key="${opt%%=*}" + if ! grep -q "^${opt}$" .config; then + warn "CRITICAL: ${opt} not set in final config!" + FAIL=1 + fi +done +if [ "$FAIL" -eq 1 ]; then + die "Critical config options missing after olddefconfig — review .config" +fi +ok "All critical config options verified" + +# --- Compile ----------------------------------------------------------------- +info "Compiling Linux ${KERNEL_VERSION} with -j${JOBS}..." +info "This will take a few minutes on 16C/32T..." +echo "" + +# Set DarkForge compiler flags for kernel build +# Note: kernel has its own CFLAGS handling; -march is passed via KCFLAGS +# The kernel's CONFIG_MZEN4 handles most CPU-specific codegen +KCFLAGS="-march=znver4 -pipe" +# znver4 because znver5 may not be fully supported by all kernel assembly +# TODO: test znver5 when compiler+kernel support is confirmed + +START_TIME=$(date +%s) + +make -j${JOBS} KCFLAGS="$KCFLAGS" bzImage 2>&1 | tail -20 + +END_TIME=$(date +%s) +ELAPSED=$((END_TIME - START_TIME)) + +if [ -f "arch/x86/boot/bzImage" ]; then + ok "Kernel compiled in ${ELAPSED}s" +else + die "Kernel compilation failed — check output above" +fi + +# Build modules (for any =m options) +info "Building kernel modules..." +make -j${JOBS} modules 2>&1 | tail -5 +ok "Modules built" + +# --- Install outputs --------------------------------------------------------- +info "Installing kernel outputs..." + +# Copy bzImage +cp arch/x86/boot/bzImage "${SCRIPT_DIR}/vmlinuz" +cp arch/x86/boot/bzImage "${SCRIPT_DIR}/vmlinuz.efi" +ok "Kernel: ${SCRIPT_DIR}/vmlinuz ($(du -h "${SCRIPT_DIR}/vmlinuz" | cut -f1))" + +# Copy System.map +cp System.map "${SCRIPT_DIR}/System.map" + +# Install modules to a staging directory +MODULES_DIR="${SCRIPT_DIR}/modules" +rm -rf "$MODULES_DIR" +make INSTALL_MOD_PATH="$MODULES_DIR" modules_install 2>&1 | tail -3 +ok "Modules installed to ${MODULES_DIR}" + +# Verify the kernel is a valid EFI application +if file "${SCRIPT_DIR}/vmlinuz" | grep -q "bzImage"; then + ok "Kernel is a valid bzImage" +elif file "${SCRIPT_DIR}/vmlinuz" | grep -q "EFI\|PE32"; then + ok "Kernel is a valid EFI binary" +else + warn "Kernel type: $(file "${SCRIPT_DIR}/vmlinuz")" + warn "May not boot via EFISTUB — verify manually" +fi + +# --- Summary ----------------------------------------------------------------- +echo "" +echo "============================================================================" +echo -e "${GREEN} DarkForge Kernel Build Complete${NC}" +echo "============================================================================" +echo "" +echo " Version: Linux ${KERNEL_VERSION}-darkforge" +echo " Kernel: ${SCRIPT_DIR}/vmlinuz" +echo " Kernel EFI: ${SCRIPT_DIR}/vmlinuz.efi" +echo " System.map: ${SCRIPT_DIR}/System.map" +echo " Modules: ${MODULES_DIR}/" +echo " Source: ${KERNEL_SRC}/" +echo "" +echo " Compile time: ${ELAPSED}s" +echo " Kernel size: $(du -h "${SCRIPT_DIR}/vmlinuz" | cut -f1)" +echo "" +echo " Next steps:" +echo " 1. Rebuild ISO: sudo bash src/iso/build-iso-arch.sh" +echo " 2. Test in QEMU: see tests/run-tests.sh" +echo " 3. For real hardware: copy vmlinuz.efi to ESP as /EFI/Linux/vmlinuz.efi" +echo "" +echo "============================================================================" diff --git a/kernel/config b/kernel/config index 9ef942e..225b7a9 100644 --- a/kernel/config +++ b/kernel/config @@ -443,3 +443,26 @@ CONFIG_FANOTIFY=y CONFIG_EXPERT=y # Enable expert mode to access all configuration options + +# ============================================================================= +# KERNEL COMMAND LINE +# ============================================================================= + +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="console=tty0 console=ttyS0,115200n8" +CONFIG_CMDLINE_OVERRIDE=n +# Embedded default command line for EFISTUB boot: +# - console=tty0: keep framebuffer console for real hardware +# - console=ttyS0: serial console for QEMU testing and debugging +# CMDLINE_OVERRIDE=n allows efibootmgr to append/override at boot time +# Root device is set per-boot: +# Live ISO: root= is handled by the initramfs (mounts squashfs) +# Installed: efibootmgr -u "root=/dev/nvme0n1p2 rootfstype=ext4" + +CONFIG_BLK_DEV_INITRD=y +# Initramfs support — needed for live ISO boot (squashfs overlay) +# Installed system boots without initramfs (NVMe + ext4 are built-in) + +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +# Serial console support — needed for QEMU testing and remote debugging diff --git a/src/iso/build-initramfs.sh b/src/iso/build-initramfs.sh new file mode 100755 index 0000000..f2d5a84 --- /dev/null +++ b/src/iso/build-initramfs.sh @@ -0,0 +1,80 @@ +#!/bin/bash +# ============================================================================ +# DarkForge Linux — Build Initramfs for Live ISO +# ============================================================================ +# Creates a minimal initramfs containing busybox and the init script. +# The initramfs is used by the live ISO to mount the squashfs root. +# +# Output: src/iso/initramfs.cpio.gz (embedded in the ISO alongside the kernel) +# +# Usage: +# bash src/iso/build-initramfs.sh +# ============================================================================ + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +INITRAMFS_DIR=$(mktemp -d /tmp/darkforge-initramfs-XXXXX) +OUTPUT="${SCRIPT_DIR}/initramfs.cpio.gz" + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' +info() { echo -e "${CYAN}>>> $1${NC}"; } +ok() { echo -e "${GREEN}>>> $1${NC}"; } +warn() { echo -e "${YELLOW}!!! $1${NC}"; } +die() { echo -e "${RED}!!! $1${NC}"; exit 1; } + +info "Building DarkForge initramfs..." + +# Create directory structure +mkdir -p "${INITRAMFS_DIR}"/{bin,sbin,dev,proc,sys,media,rootfs,overlay,tmpfs,newroot,etc,tmp} + +# Find busybox +BUSYBOX="" +if command -v busybox >/dev/null 2>&1; then + BUSYBOX="$(which busybox)" +elif [ -f /usr/bin/busybox ]; then + BUSYBOX="/usr/bin/busybox" +fi + +if [ -z "$BUSYBOX" ]; then + die "busybox not found — install with: sudo pacman -S busybox" +fi + +# Copy busybox and create symlinks for all needed applets +cp "$BUSYBOX" "${INITRAMFS_DIR}/bin/busybox" +chmod +x "${INITRAMFS_DIR}/bin/busybox" + +# Create symlinks for commands used by the init script +for cmd in sh mount umount mkdir echo sleep cat switch_root exec \ + mount.squashfs losetup mdev modprobe; do + ln -sf busybox "${INITRAMFS_DIR}/bin/$cmd" +done + +# switch_root is typically in /sbin +ln -sf ../bin/busybox "${INITRAMFS_DIR}/sbin/switch_root" + +# Copy the init script +cp "${SCRIPT_DIR}/initramfs/init" "${INITRAMFS_DIR}/init" +chmod +x "${INITRAMFS_DIR}/init" + +# Create essential device nodes +mknod -m 622 "${INITRAMFS_DIR}/dev/console" c 5 1 2>/dev/null || true +mknod -m 666 "${INITRAMFS_DIR}/dev/null" c 1 3 2>/dev/null || true +mknod -m 666 "${INITRAMFS_DIR}/dev/zero" c 1 5 2>/dev/null || true +mknod -m 666 "${INITRAMFS_DIR}/dev/tty" c 5 0 2>/dev/null || true + +# Create the cpio archive +info "Creating cpio archive..." +cd "${INITRAMFS_DIR}" +find . -print0 | cpio --null --create --format=newc 2>/dev/null | gzip -9 > "$OUTPUT" + +# Cleanup +rm -rf "${INITRAMFS_DIR}" + +SIZE=$(du -h "$OUTPUT" | cut -f1) +ok "Initramfs created: ${OUTPUT} (${SIZE})" +echo "" +echo " To use with the ISO:" +echo " The build-iso-arch.sh script will automatically include this initramfs." +echo "" diff --git a/src/iso/build-iso-arch.sh b/src/iso/build-iso-arch.sh index 3ada126..8038282 100755 --- a/src/iso/build-iso-arch.sh +++ b/src/iso/build-iso-arch.sh @@ -210,6 +210,19 @@ done chmod -R a+rX "${ROOTFS}/var/lib/dpack/repos/" ls -la "${ROOTFS}/var/lib/dpack/repos/" || true +# --- Build initramfs for live boot ------------------------------------------- +INITRAMFS_PATH="${SCRIPT_DIR}/initramfs.cpio.gz" +if [ ! -f "$INITRAMFS_PATH" ]; then + info "Building initramfs..." + bash "${SCRIPT_DIR}/build-initramfs.sh" +fi + +if [ -f "$INITRAMFS_PATH" ]; then + ok "Initramfs: ${INITRAMFS_PATH} ($(du -h "$INITRAMFS_PATH" | cut -f1))" +else + warn "No initramfs — live ISO will not be able to mount squashfs root" +fi + # --- Install kernel ---------------------------------------------------------- KERNEL_PATH="" for kp in "${PROJECT_ROOT}/kernel/vmlinuz" "${PROJECT_ROOT}/build/vmlinuz" /boot/vmlinuz-linux; do @@ -220,8 +233,15 @@ for kp in "${PROJECT_ROOT}/kernel/vmlinuz" "${PROJECT_ROOT}/build/vmlinuz" /boot done if [ -n "$KERNEL_PATH" ]; then + # Copy kernel to ISO cp "$KERNEL_PATH" "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI" ok "Kernel: ${KERNEL_PATH}" + + # Also copy initramfs alongside kernel in the ISO + if [ -f "$INITRAMFS_PATH" ]; then + cp "$INITRAMFS_PATH" "${ISO_DIR}/LiveOS/initramfs.img" + ok "Initramfs copied to ISO" + fi else warn "No kernel found — ISO will not be bootable!" warn "Build the kernel first (Phase 4) or copy vmlinuz to kernel/vmlinuz" @@ -241,12 +261,34 @@ ok "squashfs: $(du -sh "${ISO_DIR}/LiveOS/rootfs.img" | cut -f1)" info "Creating EFI boot image..." # efiboot.img MUST be inside ISO_DIR so xorriso can find it via -e flag ESP_IMG="${ISO_DIR}/efiboot.img" -ESP_SIZE=8192 # 8MB +# Size the ESP large enough for kernel + initramfs +KERNEL_SIZE=$(stat -c%s "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI" 2>/dev/null || echo "1048576") +INITRD_SIZE=0 +if [ -f "${ISO_DIR}/LiveOS/initramfs.img" ]; then + INITRD_SIZE=$(stat -c%s "${ISO_DIR}/LiveOS/initramfs.img") +fi +# ESP size: kernel + initramfs + 4MB padding, minimum 8MB +ESP_SIZE=$(( (KERNEL_SIZE + INITRD_SIZE + 4194304) / 1024 )) +[ "$ESP_SIZE" -lt 8192 ] && ESP_SIZE=8192 + dd if=/dev/zero of="${ESP_IMG}" bs=1K count=${ESP_SIZE} 2>/dev/null mkfs.fat -F 12 "${ESP_IMG}" >/dev/null mmd -i "${ESP_IMG}" ::/EFI ::/EFI/BOOT mcopy -i "${ESP_IMG}" "${ISO_DIR}/EFI/BOOT/BOOTX64.EFI" ::/EFI/BOOT/BOOTX64.EFI +# Include initramfs in the EFI partition if available +if [ -f "${ISO_DIR}/LiveOS/initramfs.img" ]; then + mcopy -i "${ESP_IMG}" "${ISO_DIR}/LiveOS/initramfs.img" ::/EFI/BOOT/initramfs.img + # Create a startup.nsh script for UEFI shell fallback + STARTUP_NSH=$(mktemp) + echo '\EFI\BOOT\BOOTX64.EFI initrd=\EFI\BOOT\initramfs.img' > "$STARTUP_NSH" + mcopy -i "${ESP_IMG}" "$STARTUP_NSH" ::/startup.nsh + rm -f "$STARTUP_NSH" + ok "EFI boot image includes kernel + initramfs (ESP: $((ESP_SIZE/1024))MB)" +else + ok "EFI boot image: kernel only (no initramfs)" +fi + # --- Build ISO ---------------------------------------------------------------- info "Building ISO..." xorriso -as mkisofs \ diff --git a/src/iso/initramfs/init b/src/iso/initramfs/init new file mode 100755 index 0000000..a2f3e0a --- /dev/null +++ b/src/iso/initramfs/init @@ -0,0 +1,101 @@ +#!/bin/sh +# ============================================================================ +# DarkForge Linux — Live ISO Initramfs Init Script +# ============================================================================ +# This script runs as PID 1 from the initramfs during live ISO boot. +# It finds the ISO media, mounts the squashfs, sets up an overlay, and +# switch_roots into the live system. +# +# Boot flow: +# UEFI → EFISTUB kernel → initramfs (this script) → switch_root → /sbin/init +# ============================================================================ + +# Mount essential virtual filesystems +mount -t proc none /proc +mount -t sysfs none /sys +mount -t devtmpfs none /dev + +# Enable kernel messages on console +echo 1 > /proc/sys/kernel/printk + +echo "" +echo " DarkForge Linux — Live Boot" +echo " Searching for installation media..." +echo "" + +# Wait for devices to settle +sleep 2 + +# Try to find the DarkForge ISO media +# The ISO has a LiveOS/rootfs.img squashfs file +MEDIA_FOUND=0 +MEDIA_MNT="/media" +ROOTFS_MNT="/rootfs" +OVERLAY_MNT="/overlay" + +mkdir -p "$MEDIA_MNT" "$ROOTFS_MNT" "$OVERLAY_MNT" + +# Check CD-ROM devices and USB drives +for attempt in 1 2 3 4 5; do + for dev in /dev/sr0 /dev/sr1 /dev/sda /dev/sda1 /dev/sdb /dev/sdb1 \ + /dev/nvme0n1p1 /dev/nvme1n1p1 /dev/vda /dev/vda1; do + [ -b "$dev" ] || continue + + if mount -o ro "$dev" "$MEDIA_MNT" 2>/dev/null; then + if [ -f "$MEDIA_MNT/LiveOS/rootfs.img" ]; then + echo " Found DarkForge media on ${dev}" + MEDIA_FOUND=1 + break 2 + fi + umount "$MEDIA_MNT" 2>/dev/null + fi + done + echo " Attempt ${attempt}/5 — waiting for devices..." + sleep 2 +done + +if [ "$MEDIA_FOUND" -eq 0 ]; then + echo "" + echo " ERROR: Could not find DarkForge installation media!" + echo " Make sure the ISO is written to a USB drive or mounted as CD-ROM." + echo "" + echo " Dropping to emergency shell..." + exec /bin/sh +fi + +# Mount the squashfs root filesystem +echo " Mounting squashfs root filesystem..." +if ! mount -t squashfs -o ro "$MEDIA_MNT/LiveOS/rootfs.img" "$ROOTFS_MNT" 2>/dev/null; then + echo " ERROR: Failed to mount squashfs!" + echo " Dropping to emergency shell..." + exec /bin/sh +fi + +# Set up tmpfs overlay for writable root +echo " Setting up writable overlay..." +mkdir -p /tmpfs +mount -t tmpfs -o size=75% tmpfs /tmpfs +mkdir -p /tmpfs/upper /tmpfs/work + +# Mount overlayfs: squashfs (read-only lower) + tmpfs (writable upper) +mkdir -p /newroot +if mount -t overlay overlay -o "lowerdir=${ROOTFS_MNT},upperdir=/tmpfs/upper,workdir=/tmpfs/work" /newroot 2>/dev/null; then + echo " Overlay root mounted (squashfs + tmpfs)" +else + # Fallback: if overlay not available, bind-mount the squashfs directly + echo " WARNING: overlayfs not available, root will be read-only" + mount --bind "$ROOTFS_MNT" /newroot +fi + +# Move virtual filesystems into the new root +mkdir -p /newroot/proc /newroot/sys /newroot/dev /newroot/media +mount --move /proc /newroot/proc +mount --move /sys /newroot/sys +mount --move /dev /newroot/dev +mount --move "$MEDIA_MNT" /newroot/media + +echo " Switching to live root filesystem..." +echo "" + +# Switch to the real root and exec init +exec switch_root /newroot /sbin/init diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 2544fda..375d487 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -869,18 +869,45 @@ if [ "$QUICK_MODE" = false ] && [ -n "${OVMF_PATH:-}" ] && [ -f "${ISO}" ]; then OVMF_FLAGS="-bios ${OVMF_PATH}" fi - timeout 60 qemu-system-x86_64 \ - ${KVM_FLAG} \ - -m 2G \ - -smp 2 \ - ${OVMF_FLAGS} \ - -cdrom "$ISO" \ - -drive file="$QEMU_DISK",format=qcow2,if=virtio \ - -nographic \ - -serial mon:stdio \ - -no-reboot \ - 2>"${LOG_DIR}/qemu-stderr.log" | head -200 > "${LOG_DIR}/qemu-output.log" & - QEMU_PID=$! + # Check if we have a real kernel for direct boot (more reliable than UEFI ISO in QEMU) + HAS_KERNEL=false + [ -f "${PROJECT_ROOT}/kernel/vmlinuz" ] && HAS_KERNEL=true + + INITRD_FLAGS="" + [ -f "${PROJECT_ROOT}/src/iso/initramfs.cpio.gz" ] && \ + INITRD_FLAGS="-initrd ${PROJECT_ROOT}/src/iso/initramfs.cpio.gz" + + if [ "$HAS_KERNEL" = true ]; then + echo " Using direct kernel boot (kernel + initramfs)..." + timeout 60 qemu-system-x86_64 \ + ${KVM_FLAG} \ + -m 2G \ + -smp 2 \ + -kernel "${PROJECT_ROOT}/kernel/vmlinuz" \ + ${INITRD_FLAGS} \ + -append "console=ttyS0,115200n8" \ + -cdrom "$ISO" \ + -drive file="$QEMU_DISK",format=qcow2,if=virtio \ + -nographic \ + -serial mon:stdio \ + -no-reboot \ + 2>"${LOG_DIR}/qemu-stderr.log" | head -200 > "${LOG_DIR}/qemu-output.log" & + QEMU_PID=$! + else + echo " Using UEFI ISO boot (no compiled kernel found)..." + timeout 60 qemu-system-x86_64 \ + ${KVM_FLAG} \ + -m 2G \ + -smp 2 \ + ${OVMF_FLAGS} \ + -cdrom "$ISO" \ + -drive file="$QEMU_DISK",format=qcow2,if=virtio \ + -nographic \ + -serial mon:stdio \ + -no-reboot \ + 2>"${LOG_DIR}/qemu-stderr.log" | head -200 > "${LOG_DIR}/qemu-output.log" & + QEMU_PID=$! + fi sleep 60 kill $QEMU_PID 2>/dev/null