feat: ARM64 generic UEFI disk image (GPT + GRUB A/B)
Produces a UEFI-bootable raw disk image for generic ARM64 hosts (QEMU virt, Ampere/Graviton cloud, ARM64 SBCs with UEFI). Reuses the existing 4-partition A/B layout from x86 (EFI 256 MB FAT32 + System A 512 MB ext4 + System B 512 MB ext4 + Data ext4 remainder). Changes: - build/scripts/create-disk-image.sh: TARGET_ARCH env var (amd64 default, arm64). Selects kernel source path, grub-mkimage target (x86_64-efi vs arm64-efi), EFI binary name (bootx64.efi vs BOOTAA64.EFI), grub.cfg variant, and whether to also install BIOS GRUB (x86 only). - build/grub/grub-arm64.cfg: ARM64 variant of grub.cfg. Identical A/B logic; console=ttyAMA0+ttyS0 to cover QEMU virt PL011, Ampere PL011, and Graviton 16550-compat. - build/Dockerfile.builder: add grub-efi-amd64-bin, grub-efi-arm64-bin, grub-pc-bin, grub-common, grub2-common so the builder container can produce EFI images for both architectures. - hack/dev-vm-arm64.sh: split into kernel mode (direct -kernel/-initrd, fast iteration) and --disk mode (UEFI firmware + GRUB + disk image, full integration test). Probes common UEFI firmware paths on Ubuntu/Fedora/macOS. Default kernel path now points at kernel-arm64-generic/Image with fallback to the renamed custom-kernel-rpi/Image. - test/qemu/test-boot-arm64-disk.sh: new CI test for the full UEFI -> GRUB -> kernel -> stage-90 boot chain. Uses a scratch copy of the disk so grubenv writes don't mutate the source artifact. - Makefile: new disk-image-arm64 target (depends on rootfs-arm64 + kernel-arm64), new test-boot-arm64-disk target, .PHONY + help updates. Phase 3 scaffold is in place. First real end-to-end ARM64 build runs in the next step on the Odroid runner — that's where we find out what's actually broken. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
129
test/qemu/test-boot-arm64-disk.sh
Executable file
129
test/qemu/test-boot-arm64-disk.sh
Executable file
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
# test-boot-arm64-disk.sh — Boot the ARM64 .arm64.img via UEFI + GRUB and
|
||||
# verify the init system reaches stage 90.
|
||||
#
|
||||
# This is the full-stack integration test: UEFI firmware -> GRUB -> kernel ->
|
||||
# initramfs -> staged init. Contrast with test-boot-arm64.sh which skips the
|
||||
# bootloader and loads kernel/initramfs directly.
|
||||
#
|
||||
# Exit 0 = PASS, Exit 1 = FAIL.
|
||||
#
|
||||
# Usage: ./test/qemu/test-boot-arm64-disk.sh [disk.img]
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
VERSION="$(cat "$PROJECT_ROOT/VERSION")"
|
||||
|
||||
DISK_IMAGE="${1:-$PROJECT_ROOT/output/kubesolo-os-${VERSION}.arm64.img}"
|
||||
TIMEOUT=180
|
||||
|
||||
echo "==> ARM64 UEFI Disk Boot Test"
|
||||
echo " Disk image: $DISK_IMAGE"
|
||||
echo " Timeout: ${TIMEOUT}s"
|
||||
|
||||
if [ ! -f "$DISK_IMAGE" ]; then
|
||||
echo "ERROR: Disk image not found: $DISK_IMAGE"
|
||||
echo " Run 'make disk-image-arm64' to build it."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v qemu-system-aarch64 >/dev/null 2>&1; then
|
||||
echo "ERROR: qemu-system-aarch64 not found."
|
||||
echo " apt install qemu-system-arm # Debian/Ubuntu"
|
||||
echo " dnf install qemu-system-aarch64 # Fedora/RHEL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Locate UEFI firmware ---
|
||||
UEFI_FW=""
|
||||
for candidate in \
|
||||
/usr/share/qemu-efi-aarch64/QEMU_EFI.fd \
|
||||
/usr/share/AAVMF/AAVMF_CODE.fd \
|
||||
/usr/share/edk2/aarch64/QEMU_EFI.fd \
|
||||
/usr/share/qemu/edk2-aarch64-code.fd \
|
||||
/opt/homebrew/share/qemu/edk2-aarch64-code.fd \
|
||||
/usr/local/share/qemu/edk2-aarch64-code.fd
|
||||
do
|
||||
if [ -f "$candidate" ]; then
|
||||
UEFI_FW="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$UEFI_FW" ]; then
|
||||
echo "ERROR: No ARM64 UEFI firmware found."
|
||||
echo " apt install qemu-efi-aarch64"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo " UEFI fw: $UEFI_FW"
|
||||
|
||||
# Copy disk image to a scratch file so the test doesn't mutate the source.
|
||||
# UEFI will write to grubenv on the EFI partition; we don't want to bake those
|
||||
# changes into the canonical build artifact.
|
||||
SCRATCH_DISK=$(mktemp /tmp/kubesolo-arm64-disk-test-XXXXXX.img)
|
||||
SERIAL_LOG=$(mktemp /tmp/kubesolo-arm64-disk-serial-XXXXXX.log)
|
||||
QEMU_PID=""
|
||||
|
||||
cleanup() {
|
||||
[ -n "$QEMU_PID" ] && kill "$QEMU_PID" 2>/dev/null || true
|
||||
rm -f "$SCRATCH_DISK" "$SERIAL_LOG"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
cp --reflink=auto "$DISK_IMAGE" "$SCRATCH_DISK" 2>/dev/null || cp "$DISK_IMAGE" "$SCRATCH_DISK"
|
||||
|
||||
# --- Launch QEMU ---
|
||||
qemu-system-aarch64 \
|
||||
-machine virt \
|
||||
-cpu cortex-a72 \
|
||||
-m 2048 \
|
||||
-smp 2 \
|
||||
-nographic \
|
||||
-bios "$UEFI_FW" \
|
||||
-drive "file=$SCRATCH_DISK,format=raw,if=virtio,media=disk" \
|
||||
-net nic,model=virtio \
|
||||
-net user \
|
||||
-serial "file:$SERIAL_LOG" &
|
||||
QEMU_PID=$!
|
||||
|
||||
echo " Waiting for boot (PID $QEMU_PID)..."
|
||||
ELAPSED=0
|
||||
SUCCESS=0
|
||||
while [ "$ELAPSED" -lt "$TIMEOUT" ]; do
|
||||
if grep -q "\[kubesolo-init\] \[OK\] Stage 90-kubesolo.sh complete" "$SERIAL_LOG" 2>/dev/null; then
|
||||
SUCCESS=1
|
||||
break
|
||||
fi
|
||||
if grep -q "KubeSolo is running" "$SERIAL_LOG" 2>/dev/null; then
|
||||
SUCCESS=1
|
||||
break
|
||||
fi
|
||||
if ! kill -0 "$QEMU_PID" 2>/dev/null; then
|
||||
echo ""
|
||||
echo "==> FAIL: QEMU exited prematurely"
|
||||
echo " Last 30 lines of serial output:"
|
||||
tail -30 "$SERIAL_LOG" 2>/dev/null || echo " (no output)"
|
||||
exit 1
|
||||
fi
|
||||
sleep 2
|
||||
ELAPSED=$((ELAPSED + 2))
|
||||
printf "\r Elapsed: %ds / %ds" "$ELAPSED" "$TIMEOUT"
|
||||
done
|
||||
echo ""
|
||||
|
||||
kill "$QEMU_PID" 2>/dev/null || true
|
||||
wait "$QEMU_PID" 2>/dev/null || true
|
||||
QEMU_PID=""
|
||||
|
||||
if [ "$SUCCESS" = "1" ]; then
|
||||
echo "==> ARM64 UEFI Disk Boot Test PASSED (${ELAPSED}s)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "==> ARM64 UEFI Disk Boot Test FAILED (timeout ${TIMEOUT}s)"
|
||||
echo ""
|
||||
echo "==> Last 50 lines of serial output:"
|
||||
tail -50 "$SERIAL_LOG" 2>/dev/null || echo " (no output)"
|
||||
exit 1
|
||||
Reference in New Issue
Block a user