fix: ensure the ISO is actually UEFI-bootable

BOOT=efi only changed the GRUB inside the rootfs squashfs — grub-mkrescue
builds the ISO bootloader from the build host's GRUB, so an ISO assembled
on a host without the x86_64-efi modules + mtools shipped with NO UEFI
boot entry. Booting that ISO on a UEFI machine showed:

    "make sure there is a bootable uefi x64 image"

- check-host-deps.sh now verifies the GRUB module dirs it needs for a
  hybrid ISO (/usr/lib/grub/x86_64-efi from grub-efi-amd64-bin,
  /usr/lib/grub/i386-pc from grub-pc-bin) and mtools, not just
  grub-mkrescue.
- build-iso.sh verifies the finished ISO has both EFI and BIOS boot
  entries (xorriso El Torito report) and prints the result; BOOT=efi
  builds FAIL if the EFI entry is missing.
- GitLab CI build image gains grub-pc-bin, grub-efi-amd64-bin, mtools.
- docs/building.md: hybrid-ISO burning instructions (dd) and the
  "bootable uefi x64 image" troubleshooting path.
This commit is contained in:
Blake Ridgway
2026-08-21 18:53:31 -05:00
parent 0361c12c07
commit 7284b4ec0b
4 changed files with 73 additions and 1 deletions

View File

@@ -31,7 +31,7 @@ validate:
image: debian:trixie image: debian:trixie
before_script: before_script:
- apt-get update -qq - apt-get update -qq
- apt-get install -y -qq debootstrap squashfs-tools grub2-common xorriso cpio curl git make bash gdisk parted rsync dosfstools qemu-utils unzip dpkg openssl sbsigntool - apt-get install -y -qq debootstrap squashfs-tools grub2-common grub-pc-bin grub-efi-amd64-bin mtools xorriso cpio curl git make bash gdisk parted rsync dosfstools qemu-utils unzip dpkg openssl sbsigntool
script: script:
- make ${TARGET:-iso}-${EDITION} - make ${TARGET:-iso}-${EDITION}
artifacts: artifacts:

View File

@@ -120,3 +120,33 @@ The ISO boots a **live** system (via `live-boot`): the rootfs is compressed to
a squashfs and mounted on boot, so you can try an edition before installing it a squashfs and mounted on boot, so you can try an edition before installing it
to disk. The same rootfs can be installed with the btrfs layout via to disk. The same rootfs can be installed with the btrfs layout via
`btrfs/init.sh` (the installer is follow-up work — see `docs/architecture.md`). `btrfs/init.sh` (the installer is follow-up work — see `docs/architecture.md`).
### Burning the ISO
The ISO is **hybrid** (BIOS + UEFI). Write it to a USB stick with `dd` (or
Ventoy), not as a file copy:
```bash
sudo dd if=build/artifacts/arcline-server-0.1.0-amd64.iso of=/dev/sdX bs=4M status=progress
```
The build verifies the finished ISO actually contains **both** boot entries and
prints `✓ EFI boot entry present` / `✓ BIOS boot entry present`; `BOOT=efi`
builds **fail** if the EFI entry is missing.
### UEFI says "make sure there is a bootable uefi x64 image"
That firmware error means the ISO had no UEFI boot entry. `grub-mkrescue`
builds the ISO bootloader from the **build host's** GRUB, so the host needs the
UEFI modules even though the image is `BOOT=efi`:
```bash
sudo apt-get install -y grub-efi-amd64-bin grub-pc-bin mtools
scripts/check-host-deps.sh # should now pass; rebuild the ISO
```
If the entry was present but it still won't boot, enable/disable Secure Boot
(our `BOOT=bios` ISOs are unsigned and will be refused by Secure Boot — use
`BOOT=efi make iso-server` plus the MOK flow in `docs/secureboot.md` for
signed, Secure-Boot-friendly media).

View File

@@ -87,5 +87,32 @@ grub-mkrescue -o "$ARTIFACT" "$ISOFILES" -- \
-volume-label "ARCLINE_${EDITION^^}" 2>/dev/null || \ -volume-label "ARCLINE_${EDITION^^}" 2>/dev/null || \
grub-mkrescue -o "$ARTIFACT" "$ISOFILES" grub-mkrescue -o "$ARTIFACT" "$ISOFILES"
# ── 5. verify hybrid boot entries ───────────────────────────────────────────
# grub-mkrescue builds the ISO bootloader from the HOST's GRUB modules. If the
# host lacks the x86_64-efi modules or mtools, the ISO has no UEFI boot entry
# and UEFI firmware shows "make sure there is a bootable uefi x64 image".
# Check the finished ISO instead of trusting the host.
if command -v xorriso >/dev/null 2>&1; then
log "verifying ISO boot entries"
ET="$(xorriso -indev "$ARTIFACT" -report_el_torito cmd 2>/dev/null || true)"
HAS_EFI=0
echo "$ET" | grep -qiE 'boot_image efi|efi\.img|BOOTX64\.EFI' && HAS_EFI=1
if [[ $HAS_EFI -eq 0 ]]; then
# fallback: some xorriso versions name it differently — look in the fs
xorriso -indev "$ARTIFACT" -find / -name 'efi.img' 2>/dev/null | grep -qi 'efi\.img' && HAS_EFI=1
fi
HAS_BIOS=0
echo "$ET" | grep -qiE 'boot_image isolinux|boot_image grub' && HAS_BIOS=1
[[ $HAS_EFI -eq 1 ]] && log " ✓ EFI boot entry present" || warn " ISO has NO EFI boot entry (UEFI will refuse it)"
[[ $HAS_BIOS -eq 1 ]] && log " ✓ BIOS boot entry present" || warn " ISO has NO BIOS boot entry (legacy BIOS will refuse it)"
if [[ "$BOOT" == "efi" && $HAS_EFI -eq 0 ]]; then
die "BOOT=efi but the ISO has no EFI boot entry — install grub-efi-amd64-bin + mtools on the build host (scripts/check-host-deps.sh --install), then rebuild"
fi
fi
log "ISO artifact: $ARTIFACT" log "ISO artifact: $ARTIFACT"
sha256sum "$ARTIFACT" | tee "$ARTIFACT.sha256" sha256sum "$ARTIFACT" | tee "$ARTIFACT.sha256"

View File

@@ -31,6 +31,8 @@ NEEDED=(
"openssl|openssl" "openssl|openssl"
"sbsign|sbsigntool" "sbsign|sbsigntool"
"sbverify|sbsigntool" "sbverify|sbsigntool"
# hybrid ISO: mtools builds the embedded EFI image that grub-mkrescue uses
"mformat|mtools"
) )
MISSING=() MISSING=()
@@ -49,6 +51,19 @@ for entry in "${NEEDED[@]}"; do
fi fi
done done
# grub-mkrescue assembles the ISO bootloader from the HOST's GRUB modules.
# A missing x86_64-efi module set produces an ISO with no UEFI boot entry —
# the exact cause of "make sure there is a bootable uefi x64 image" on UEFI
# machines. Verify the module dirs directly (they are not binaries).
for spec in "/usr/lib/grub/x86_64-efi:grub-efi-amd64-bin:UEFI" \
"/usr/lib/grub/i386-pc:grub-pc-bin:BIOS"; do
dir="${spec%%:*}"; rest="${spec#*:}"; pkg="${rest%%:*}"; what="${rest##*:}"
if [[ ! -d "$dir" ]]; then
warn "missing: GRUB $what modules in $dir (package: $pkg) — ISO will not boot via $what"
MISSING+=("$pkg")
fi
done
if [[ ${#MISSING[@]} -eq 0 ]]; then if [[ ${#MISSING[@]} -eq 0 ]]; then
log "all host build dependencies present ✓" log "all host build dependencies present ✓"
exit 0 exit 0