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:
@@ -6,28 +6,61 @@
|
||||
# Part 2: System A (512 MB, ext4) — vmlinuz + kubesolo-os.gz (active)
|
||||
# Part 3: System B (512 MB, ext4) — vmlinuz + kubesolo-os.gz (passive)
|
||||
# Part 4: Data (remaining, ext4) — persistent K8s state
|
||||
#
|
||||
# Supports both x86_64 (default) and ARM64 generic UEFI targets. ARM64 RPi
|
||||
# uses a different image format — see build/scripts/create-rpi-image.sh.
|
||||
#
|
||||
# Environment:
|
||||
# TARGET_ARCH amd64 (default) or arm64
|
||||
# IMG_SIZE_MB Image size in MB (default 4096)
|
||||
# CACHE_DIR Build cache (default <project>/build/cache)
|
||||
# ROOTFS_DIR Rootfs work dir (default <project>/build/rootfs-work)
|
||||
# OUTPUT_DIR Output dir (default <project>/output)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
ROOTFS_DIR="${ROOTFS_DIR:-$PROJECT_ROOT/build/rootfs-work}"
|
||||
CACHE_DIR="${CACHE_DIR:-$PROJECT_ROOT/build/cache}"
|
||||
OUTPUT_DIR="${OUTPUT_DIR:-$PROJECT_ROOT/output}"
|
||||
VERSION="$(cat "$PROJECT_ROOT/VERSION")"
|
||||
OS_NAME="kubesolo-os"
|
||||
TARGET_ARCH="${TARGET_ARCH:-amd64}"
|
||||
|
||||
IMG_OUTPUT="$OUTPUT_DIR/${OS_NAME}-${VERSION}.img"
|
||||
IMG_SIZE_MB="${IMG_SIZE_MB:-4096}" # 4 GB default (larger for A/B)
|
||||
|
||||
VMLINUZ="$ROOTFS_DIR/vmlinuz"
|
||||
# --- Arch-specific paths ---
|
||||
case "$TARGET_ARCH" in
|
||||
amd64)
|
||||
IMG_OUTPUT="$OUTPUT_DIR/${OS_NAME}-${VERSION}.img"
|
||||
VMLINUZ="$ROOTFS_DIR/vmlinuz"
|
||||
GRUB_CFG="$PROJECT_ROOT/build/grub/grub.cfg"
|
||||
GRUB_TARGET="x86_64-efi"
|
||||
GRUB_EFI_BIN="bootx64.efi"
|
||||
GRUB_INSTALL_BIOS=true
|
||||
;;
|
||||
arm64)
|
||||
IMG_OUTPUT="$OUTPUT_DIR/${OS_NAME}-${VERSION}.arm64.img"
|
||||
VMLINUZ="$CACHE_DIR/kernel-arm64-generic/Image"
|
||||
GRUB_CFG="$PROJECT_ROOT/build/grub/grub-arm64.cfg"
|
||||
GRUB_TARGET="arm64-efi"
|
||||
GRUB_EFI_BIN="BOOTAA64.EFI"
|
||||
GRUB_INSTALL_BIOS=false
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: TARGET_ARCH must be 'amd64' or 'arm64' (got: $TARGET_ARCH)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
INITRAMFS="$ROOTFS_DIR/kubesolo-os.gz"
|
||||
GRUB_CFG="$PROJECT_ROOT/build/grub/grub.cfg"
|
||||
GRUB_ENV_DEFAULTS="$PROJECT_ROOT/build/grub/grub-env-defaults"
|
||||
|
||||
for f in "$VMLINUZ" "$INITRAMFS" "$GRUB_CFG" "$GRUB_ENV_DEFAULTS"; do
|
||||
[ -f "$f" ] || { echo "ERROR: Missing $f"; exit 1; }
|
||||
done
|
||||
|
||||
echo "==> Creating ${IMG_SIZE_MB}MB disk image with A/B partitions..."
|
||||
echo "==> Creating ${IMG_SIZE_MB}MB ${TARGET_ARCH} disk image with A/B partitions..."
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# Create sparse image
|
||||
@@ -161,35 +194,44 @@ else
|
||||
mv "$GRUBENV_FILE.tmp" "$GRUBENV_FILE"
|
||||
fi
|
||||
|
||||
# Install GRUB EFI binary if available
|
||||
if command -v grub-mkimage >/dev/null 2>&1; then
|
||||
grub-mkimage -O x86_64-efi -o "$MNT_EFI/EFI/BOOT/bootx64.efi" \
|
||||
-p /boot/grub \
|
||||
part_gpt ext2 fat normal linux echo all_video test search \
|
||||
search_fs_uuid search_label configfile loadenv \
|
||||
2>/dev/null || echo " WARN: grub-mkimage failed — use QEMU -bios flag"
|
||||
elif command -v grub2-mkimage >/dev/null 2>&1; then
|
||||
grub2-mkimage -O x86_64-efi -o "$MNT_EFI/EFI/BOOT/bootx64.efi" \
|
||||
-p /boot/grub \
|
||||
part_gpt ext2 fat normal linux echo all_video test search \
|
||||
search_fs_uuid search_label configfile loadenv \
|
||||
2>/dev/null || echo " WARN: grub2-mkimage failed — use QEMU -bios flag"
|
||||
# Install GRUB EFI binary
|
||||
# Modules required: part_gpt + fat (boot partition), ext2 (system A/B),
|
||||
# normal + linux + echo + configfile + loadenv (boot menu + grubenv),
|
||||
# search_* (locate partitions by label).
|
||||
# all_video + test are x86-specific (DRM init); leave them out on arm64.
|
||||
if [ "$TARGET_ARCH" = "arm64" ]; then
|
||||
GRUB_MODULES="part_gpt ext2 fat normal linux echo test search search_fs_uuid search_label configfile loadenv"
|
||||
else
|
||||
echo " WARN: grub-mkimage not found — EFI boot image not created"
|
||||
echo " Install grub2-tools or use QEMU -kernel/-initrd flags"
|
||||
GRUB_MODULES="part_gpt ext2 fat normal linux echo all_video test search search_fs_uuid search_label configfile loadenv"
|
||||
fi
|
||||
|
||||
# For BIOS boot: install GRUB i386-pc modules if available
|
||||
if command -v grub-install >/dev/null 2>&1; then
|
||||
grub-install --target=i386-pc --boot-directory="$MNT_EFI/boot" \
|
||||
--no-floppy "$LOOP" 2>/dev/null || {
|
||||
echo " WARN: BIOS GRUB install failed — EFI-only or use QEMU -kernel"
|
||||
}
|
||||
elif command -v grub2-install >/dev/null 2>&1; then
|
||||
grub2-install --target=i386-pc --boot-directory="$MNT_EFI/boot" \
|
||||
--no-floppy "$LOOP" 2>/dev/null || {
|
||||
echo " WARN: BIOS GRUB install failed — EFI-only or use QEMU -kernel"
|
||||
}
|
||||
# shellcheck disable=SC2086 # GRUB_MODULES is intentionally word-split
|
||||
if command -v grub-mkimage >/dev/null 2>&1; then
|
||||
grub-mkimage -O "$GRUB_TARGET" -o "$MNT_EFI/EFI/BOOT/$GRUB_EFI_BIN" \
|
||||
-p /boot/grub $GRUB_MODULES \
|
||||
|| echo " WARN: grub-mkimage failed — use QEMU -bios flag"
|
||||
elif command -v grub2-mkimage >/dev/null 2>&1; then
|
||||
grub2-mkimage -O "$GRUB_TARGET" -o "$MNT_EFI/EFI/BOOT/$GRUB_EFI_BIN" \
|
||||
-p /boot/grub $GRUB_MODULES \
|
||||
|| echo " WARN: grub2-mkimage failed — use QEMU -bios flag"
|
||||
else
|
||||
echo " WARN: grub-mkimage not found — EFI boot image not created"
|
||||
echo " Install grub-efi-${TARGET_ARCH}-bin or use QEMU -kernel/-initrd flags"
|
||||
fi
|
||||
|
||||
# For BIOS boot: install GRUB i386-pc modules (x86 only — ARM64 is UEFI-only).
|
||||
if [ "$GRUB_INSTALL_BIOS" = "true" ]; then
|
||||
if command -v grub-install >/dev/null 2>&1; then
|
||||
grub-install --target=i386-pc --boot-directory="$MNT_EFI/boot" \
|
||||
--no-floppy "$LOOP" 2>/dev/null || {
|
||||
echo " WARN: BIOS GRUB install failed — EFI-only or use QEMU -kernel"
|
||||
}
|
||||
elif command -v grub2-install >/dev/null 2>&1; then
|
||||
grub2-install --target=i386-pc --boot-directory="$MNT_EFI/boot" \
|
||||
--no-floppy "$LOOP" 2>/dev/null || {
|
||||
echo " WARN: BIOS GRUB install failed — EFI-only or use QEMU -kernel"
|
||||
}
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- System A Partition (active) ---
|
||||
@@ -213,9 +255,9 @@ done
|
||||
sync
|
||||
|
||||
echo ""
|
||||
echo "==> Disk image created: $IMG_OUTPUT"
|
||||
echo "==> ${TARGET_ARCH} disk image created: $IMG_OUTPUT"
|
||||
echo " Size: $(du -h "$IMG_OUTPUT" | cut -f1)"
|
||||
echo " Part 1 (KSOLOEFI): GRUB + A/B boot config"
|
||||
echo " Part 1 (KSOLOEFI): GRUB ($GRUB_TARGET) + A/B boot config"
|
||||
echo " Part 2 (KSOLOA): System A — kernel + initramfs (active)"
|
||||
echo " Part 3 (KSOLOB): System B — kernel + initramfs (passive)"
|
||||
echo " Part 4 (KSOLODATA): Persistent K8s state"
|
||||
|
||||
Reference in New Issue
Block a user