fix: xorriso append_partition for real USB boot + grub-mkstandalone

Root cause of USB boot failure: our xorriso used -e boot/grub/efi.img
to embed the EFI image inside the ISO. This works for CD-ROM and QEMU
but NOT for USB on real UEFI hardware.

Fix: use the Will Haley / Debian live-build approach:
- -append_partition 2 (GPT type EFI) appends efi.img AFTER ISO data
- -e --interval:appended_partition_2:all:: references the appended partition
- --mbr-force-bootable forces MBR active flag
- grub-mkstandalone with embedded bootstrap config (searches for grub.cfg)
- grub.cfg placed in both /boot/grub/ AND /EFI/BOOT/ on ISO
- grub.cfg uses search --label ARCHIPELAGO to find the ISO root

This is the exact approach used by StartOS, TAILS, and every production
custom Debian live ISO that boots from USB.

Also: iso-debug, iso-branding skills + reference docs, dev-start.sh
option 0 for branding dev, improved dev-branding.sh and test-iso-qemu.sh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-27 23:33:31 +00:00
parent b49f850c85
commit 6cfb8082c5
6 changed files with 1006 additions and 217 deletions

View File

@@ -637,16 +637,25 @@ umount /installer/proc 2>/dev/null || true
echo " [container] Creating installer squashfs..."
mksquashfs /installer /output/filesystem.squashfs -comp xz -Xbcj x86 -noappend -quiet
# Build GRUB EFI image
# Build GRUB EFI image with embedded bootstrap config (grub-mkstandalone)
# This ensures GRUB can find its config on real hardware, not just QEMU
echo " [container] Building GRUB EFI image..."
grub-mkimage -O x86_64-efi -o /output/BOOTX64.EFI -p /boot/grub \
part_gpt part_msdos fat iso9660 udf normal boot linux search \
search_fs_uuid search_fs_file search_label configfile echo cat \
ls test true loopback gfxterm gfxmenu font png all_video video \
video_bochs video_cirrus efi_gop efi_uga
cat > /tmp/grub-embed.cfg <<GRUBEMBED
search --no-floppy --set=root --label ARCHIPELAGO
set prefix=(\\\$root)/boot/grub
configfile \\\$prefix/grub.cfg
GRUBEMBED
# Create EFI FAT image (4MB)
dd if=/dev/zero of=/output/efi.img bs=1M count=4 2>/dev/null
grub-mkstandalone -O x86_64-efi \
--modules="part_gpt part_msdos fat iso9660 all_video font gfxterm" \
--locales="" \
--themes="" \
--fonts="" \
--output=/output/BOOTX64.EFI \
"boot/grub/grub.cfg=/tmp/grub-embed.cfg"
# Create EFI FAT image (20MB — includes GRUB binary + grub.cfg)
dd if=/dev/zero of=/output/efi.img bs=1M count=20 2>/dev/null
mkfs.vfat /output/efi.img >/dev/null
mmd -i /output/efi.img ::/EFI ::/EFI/BOOT
mcopy -i /output/efi.img /output/BOOTX64.EFI ::/EFI/BOOT/BOOTX64.EFI
@@ -683,7 +692,7 @@ cp "$WORK_DIR/vmlinuz" "$INSTALLER_ISO/live/vmlinuz"
cp "$WORK_DIR/initrd.img" "$INSTALLER_ISO/live/initrd.img"
cp "$WORK_DIR/filesystem.squashfs" "$INSTALLER_ISO/live/filesystem.squashfs"
cp "$WORK_DIR/BOOTX64.EFI" "$INSTALLER_ISO/EFI/BOOT/BOOTX64.EFI"
cp "$WORK_DIR/efi.img" "$INSTALLER_ISO/boot/grub/efi.img"
# Note: efi.img stays in $WORK_DIR — it gets appended as GPT partition 2 by xorriso
cp "$WORK_DIR/isolinux.bin" "$INSTALLER_ISO/isolinux/isolinux.bin"
cp "$WORK_DIR/ldlinux.c32" "$INSTALLER_ISO/isolinux/ldlinux.c32"
cp "$WORK_DIR/menu.c32" "$INSTALLER_ISO/isolinux/menu.c32" 2>/dev/null || true
@@ -2501,11 +2510,19 @@ echo "Step 5: Configuring boot loaders..."
# Create GRUB configuration
echo " Writing GRUB config..."
cat > "$INSTALLER_ISO/boot/grub/grub.cfg" <<'GRUBCFG'
insmod part_gpt
insmod part_msdos
insmod fat
insmod iso9660
insmod all_video
search --no-floppy --set=root --label ARCHIPELAGO
set timeout=5
set default=0
# Load font for graphical menu
if loadfont /boot/grub/font.pf2; then
if loadfont ($root)/boot/grub/font.pf2; then
set gfxmode=auto
insmod gfxterm
insmod png
@@ -2513,26 +2530,25 @@ if loadfont /boot/grub/font.pf2; then
fi
# Archipelago GRUB theme
if [ -f /boot/grub/themes/archipelago/theme.txt ]; then
# Load theme fonts
loadfont /boot/grub/themes/archipelago/dejavu_12.pf2
loadfont /boot/grub/themes/archipelago/dejavu_14.pf2
loadfont /boot/grub/themes/archipelago/dejavu_16.pf2
loadfont /boot/grub/themes/archipelago/dejavu_24.pf2
set theme=/boot/grub/themes/archipelago/theme.txt
if [ -f ($root)/boot/grub/themes/archipelago/theme.txt ]; then
loadfont ($root)/boot/grub/themes/archipelago/dejavu_12.pf2
loadfont ($root)/boot/grub/themes/archipelago/dejavu_14.pf2
loadfont ($root)/boot/grub/themes/archipelago/dejavu_16.pf2
loadfont ($root)/boot/grub/themes/archipelago/dejavu_24.pf2
set theme=($root)/boot/grub/themes/archipelago/theme.txt
else
set menu_color_normal=light-gray/black
set menu_color_highlight=white/dark-gray
fi
menuentry "Install Archipelago" --hotkey=i {
linux /live/vmlinuz boot=live components quiet console=ttyS0,115200 console=tty0
initrd /live/initrd.img
linux ($root)/live/vmlinuz boot=live components quiet console=ttyS0,115200 console=tty0
initrd ($root)/live/initrd.img
}
menuentry "Install Archipelago (verbose)" --hotkey=v {
linux /live/vmlinuz boot=live components
initrd /live/initrd.img
linux ($root)/live/vmlinuz boot=live components console=ttyS0,115200 console=tty0
initrd ($root)/live/initrd.img
}
menuentry "Boot from local disk" --hotkey=b {
@@ -2541,6 +2557,14 @@ menuentry "Boot from local disk" --hotkey=b {
}
GRUBCFG
# Copy grub.cfg to EFI/BOOT on ISO filesystem AND into the FAT EFI image
# The embedded grub bootstrap does configfile "${cmdpath}/grub.cfg"
cp "$INSTALLER_ISO/boot/grub/grub.cfg" "$INSTALLER_ISO/EFI/BOOT/grub.cfg"
if [ -f "$WORK_DIR/efi.img" ]; then
mcopy -oi "$WORK_DIR/efi.img" "$INSTALLER_ISO/boot/grub/grub.cfg" ::/EFI/BOOT/grub.cfg 2>/dev/null || \
echo " WARNING: Could not copy grub.cfg into efi.img (mtools required)"
fi
# Create ISOLINUX configuration (legacy BIOS boot)
echo " Writing ISOLINUX config..."
cat > "$INSTALLER_ISO/isolinux/isolinux.cfg" <<'ISOCFG'
@@ -2607,35 +2631,44 @@ if [ ! -f "$ISOHDPFX" ]; then
done
fi
# EFI boot image was built in Step 2
EFI_IMG="$INSTALLER_ISO/boot/grub/efi.img"
# EFI boot image was built in Step 2 and placed at staging/efiboot.img
# The Will Haley / Debian live-build approach: append EFI as GPT partition 2
# This is what makes USB boot work on real UEFI hardware (not just QEMU)
EFIBOOT="$WORK_DIR/efi.img"
if [ ! -f "$EFI_IMG" ]; then
if [ ! -f "$EFIBOOT" ]; then
echo " WARNING: No EFI boot image — ISO will only support Legacy BIOS boot"
xorriso -as mkisofs -o "$OUTPUT_ISO" \
-volid "ARCHIPELAGO" \
-iso-level 3 \
-J -joliet-long -R \
-full-iso9660-filenames \
--mbr-force-bootable -partition_offset 16 \
-joliet -joliet-long -rational-rock \
-isohybrid-mbr "$ISOHDPFX" \
-c isolinux/boot.cat \
-b isolinux/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-partition_offset 16 \
-eltorito-boot isolinux/isolinux.bin \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
--eltorito-catalog isolinux/isolinux.cat \
"$INSTALLER_ISO"
else
xorriso -as mkisofs -o "$OUTPUT_ISO" \
-volid "ARCHIPELAGO" \
-iso-level 3 \
-J -joliet-long -R \
-full-iso9660-filenames \
--mbr-force-bootable -partition_offset 16 \
-joliet -joliet-long -rational-rock \
-isohybrid-mbr "$ISOHDPFX" \
-c isolinux/boot.cat \
-b isolinux/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-eltorito-boot isolinux/isolinux.bin \
-no-emul-boot \
-boot-load-size 4 \
-boot-info-table \
--eltorito-catalog isolinux/isolinux.cat \
-eltorito-alt-boot \
-e boot/grub/efi.img \
-no-emul-boot \
-isohybrid-gpt-basdat \
-partition_offset 16 \
-e --interval:appended_partition_2:all:: \
-no-emul-boot \
-isohybrid-gpt-basdat \
-append_partition 2 C12A7328-F81F-11D2-BA4B-00A0C93EC93B "$EFIBOOT" \
"$INSTALLER_ISO"
fi