From 550b5be07de1fc2d4800f34d44e396ade3c7b6c0 Mon Sep 17 00:00:00 2001
From: plainlinen <bcdedit@hotmail.com>
Date: Thu, 9 Jun 2022 22:37:10 -0700
Subject: [PATCH] Add *_uefi-x64.grub.* functions to mkarchiso

---
 archiso/mkarchiso | 272 ++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 251 insertions(+), 21 deletions(-)

diff --git a/archiso/mkarchiso b/archiso/mkarchiso
index f8c180a0..b683d481 100755
--- a/archiso/mkarchiso
+++ b/archiso/mkarchiso
@@ -493,30 +493,42 @@ _make_boot_on_fat() {
 # Create a FAT image (efiboot.img) which will serve as the EFI system partition
 # $1: image size in bytes
 _make_efibootimg() {
-    local imgsize="0"
+    local imgsize_kib="0"
+    local imgsize_bytes=${1}
+
+    if (( imgsize_bytes < 2*1024*1024 )); then
+        _msg_info "Validating '${bootmode}': efiboot.img size is ${imgsize_bytes} bytes is less than 2 MiB! Bumping up to 2 MiB"
+        imgsize_bytes=$((2*1024*1024))
+    fi
 
     # Convert from bytes to KiB and round up to the next full MiB with an additional MiB for reserved sectors.
-    imgsize="$(awk 'function ceil(x){return int(x)+(x>int(x))}
+    imgsize_kib="$(awk 'function ceil(x){return int(x)+(x>int(x))}
             function byte_to_kib(x){return x/1024}
             function mib_to_kib(x){return x*1024}
-            END {print mib_to_kib(ceil((byte_to_kib($1)+1024)/1024))}' <<< "${1}"
+            END {print mib_to_kib(ceil((byte_to_kib($1)+1024)/1024))}' <<< "${imgsize_bytes}"
     )"
     # The FAT image must be created with mkfs.fat not mformat, as some systems have issues with mformat made images:
     # https://lists.gnu.org/archive/html/grub-devel/2019-04/msg00099.html
     rm -f -- "${work_dir}/efiboot.img"
-    _msg_info "Creating FAT image of size: ${imgsize} KiB..."
+    _msg_info "Creating FAT image of size: ${imgsize_kib} KiB..."
     if [[ "${quiet}" == "y" ]]; then
         # mkfs.fat does not have a -q/--quiet option, so redirect stdout to /dev/null instead
         # https://github.com/dosfstools/dosfstools/issues/103
-        mkfs.fat -C -n ARCHISO_EFI "${work_dir}/efiboot.img" "${imgsize}" > /dev/null
+        mkfs.fat -C -n ARCHISO_EFI "${work_dir}/efiboot.img" "${imgsize_kib}" > /dev/null
     else
-        mkfs.fat -C -n ARCHISO_EFI "${work_dir}/efiboot.img" "${imgsize}"
+        mkfs.fat -C -n ARCHISO_EFI "${work_dir}/efiboot.img" "${imgsize_kib}"
     fi
 
     # Create the default/fallback boot path in which a boot loaders will be placed later.
     mmd -i "${work_dir}/efiboot.img" ::/EFI ::/EFI/BOOT
 }
 
+# Copy the grub.cfg file in efiboot.img which is used by both UEFI ia32 and UEFI x64.
+_make_efibootimg_grubcfg() {
+    mcopy -i "${work_dir}/efiboot.img" \
+        "${work_dir}/grub.cfg" ::/EFI/BOOT/grub.cfg
+}
+
 _make_bootmode_uefi-ia32.grub.esp() {
     # Fill Grub configuration files
     sed "s|%ARCHISO_LABEL%|${iso_label}|g;
@@ -537,16 +549,16 @@ _make_bootmode_uefi-ia32.grub.esp() {
     efiboot_files+=("${work_dir}/BOOTIA32.EFI"
                     "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi")
 
-    if [[ ! " ${bootmodes[*]} " =~ uefi-x64.systemd-boot.esp ]]; then
-       efiboot_files+=("${pacstrap_dir}/boot/vmlinuz-"*
-                       "${pacstrap_dir}/boot/initramfs-"*".img")
-
+    if [[ " ${bootmodes[*]} " =~ uefi-x64.systemd-boot.esp ]]; then
+        # TODO: Remove this branch.
+        _run_once _make_bootmode_uefi-x64.systemd-boot.esp
+    elif [[ " ${bootmodes[*]} " =~ uefi-x64.grub.esp ]]; then
+        _run_once _make_bootmode_uefi-x64.grub.esp
+    else
         efiboot_imgsize="$(du -bc "${efiboot_files[@]}" \
             2>/dev/null | awk 'END { print $1 }')"
         # Create a FAT image for the EFI system partition
         _make_efibootimg "$efiboot_imgsize"
-    else
-        _run_once _make_bootmode_uefi-x64.systemd-boot.esp
     fi
 
     # Copy grub EFI binary to the default/fallback boot path
@@ -554,10 +566,8 @@ _make_bootmode_uefi-ia32.grub.esp() {
         "${work_dir}/BOOTIA32.EFI" ::/EFI/BOOT/BOOTIA32.EFI
 
     # Copy GRUB configuration files
-    mcopy -i "${work_dir}/efiboot.img" \
-        "${work_dir}/grub.cfg" ::/EFI/BOOT/grub.cfg
+    _run_once _make_efibootimg_grubcfg
 
-    # shellia32.efi is picked up automatically when on /
     if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" ]]; then
         mcopy -i "${work_dir}/efiboot.img" \
             "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" ::/shellia32.efi
@@ -586,7 +596,6 @@ _make_bootmode_uefi-ia32.grub.eltorito() {
     install -m 0644 -- "${work_dir}/grub.cfg" "${isofs_dir}/EFI/BOOT/grub.cfg"
 
     # edk2-shell based UEFI shell
-    # shellia32.efi is picked up automatically when on /
     if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" ]]; then
         install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" \
                            "${isofs_dir}/shellia32.efi"
@@ -595,6 +604,75 @@ _make_bootmode_uefi-ia32.grub.eltorito() {
     _msg_info "Done!"
 }
 
+_make_bootmode_uefi-x64.grub.esp() {
+    # Fill Grub configuration files
+    sed "s|%ARCHISO_LABEL%|${iso_label}|g;
+         s|%INSTALL_DIR%|${install_dir}|g;
+         s|%ARCH%|${arch}|g" \
+        "${profile}/grub/grub.cfg" > "${work_dir}/grub.cfg"
+
+    # shellcheck disable=SC2016
+    printf 'configfile ${cmdpath}/grub.cfg\n' > "${work_dir}/grub-embed.cfg"
+
+    # Create EFI file
+    grub-mkstandalone -O x86_64-efi \
+                      --modules="part_gpt part_msdos fat iso9660" \
+                      --locales="en@quot" \
+                      --themes="" \
+                      -o "${work_dir}/BOOTx64.EFI" "boot/grub/grub.cfg=${work_dir}/grub-embed.cfg"
+    # Add GRUB to the list of files used to calculate the required FAT image size.
+    efiboot_files+=("${work_dir}/BOOTx64.EFI"
+                    "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi")
+
+    efiboot_imgsize="$(du -bc "${efiboot_files[@]}" \
+        2>/dev/null | awk 'END { print $1 }')"
+
+    # Create a FAT image for the EFI system partition
+    _make_efibootimg "$efiboot_imgsize"
+
+    # Copy grub EFI binary to the default/fallback boot path
+    mcopy -i "${work_dir}/efiboot.img" \
+        "${work_dir}/BOOTx64.EFI" ::/EFI/BOOT/BOOTx64.EFI
+
+    _run_once _make_efibootimg_grubcfg
+
+    if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then
+        mcopy -i "${work_dir}/efiboot.img" \
+            "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi
+    fi
+
+    _msg_info "Done! GRUB set up for UEFI booting successfully."
+}
+
+# Prepare GRUB for El Torito booting
+_make_bootmode_uefi-x64.grub.eltorito() {
+    # El Torito UEFI boot requires an image containing the EFI system partition.
+    # uefi-x64.grub.eltorito has the same requirements as uefi-x64.grub.esp
+    _run_once _make_bootmode_uefi-x64.grub.esp
+
+    # Additionally set up system-boot in ISO 9660. This allows creating a medium for the live environment by using
+    # manual partitioning and simply copying the ISO 9660 file system contents.
+    # This is not related to El Torito booting and no firmware uses these files.
+    _msg_info "Preparing an /EFI directory for the ISO 9660 file system..."
+    install -d -m 0755 -- "${isofs_dir}/EFI/BOOT"
+
+    # Copy GRUB EFI binary to the default/fallback boot path
+    install -m 0644 -- "${work_dir}/BOOTx64.EFI" \
+        "${isofs_dir}/EFI/BOOT/BOOTx64.EFI"
+
+    # Copy GRUB configuration files
+    #  install -m 0644 -- "${work_dir}/grub.cfg" "${isofs_dir}"
+    # TODO: ${cmdpath} is (cd0)/EFI/BOOT, so we should put grub.cfg here?
+    install -m 0644 -- "${work_dir}/grub.cfg" "${isofs_dir}/EFI/BOOT"
+
+    # edk2-shell based UEFI shell
+    if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then
+        install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${isofs_dir}/shellx64.efi"
+    fi
+
+    _msg_info "Done!"
+}
+
 # Prepare system-boot for booting when written to a disk (isohybrid)
 _make_bootmode_uefi-x64.systemd-boot.esp() {
     local _file efiboot_imgsize
@@ -724,6 +802,11 @@ _validate_requirements_bootmode_bios.syslinux.eltorito() {
 }
 
 _validate_requirements_bootmode_uefi-x64.systemd-boot.esp() {
+    # shellcheck disable=SC2076
+    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.grub.esp ' ]]; then
+        _msg_error "Validating '${bootmode}': cannot be used with bootmode uefi-x64.grub.esp!" 0
+    fi
+
     # Check if mkfs.fat is available
     if ! command -v mkfs.fat &> /dev/null; then
         (( validation_error=validation_error+1 ))
@@ -764,6 +847,11 @@ _validate_requirements_bootmode_uefi-x64.systemd-boot.esp() {
 }
 
 _validate_requirements_bootmode_uefi-x64.systemd-boot.eltorito() {
+    # shellcheck disable=SC2076
+    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.grub.eltorito ' ]]; then
+        _msg_error "Validating '${bootmode}': cannot be used with bootmode uefi-x64.grub.eltorito!" 0
+    fi
+
     # uefi-x64.systemd-boot.eltorito has the exact same requirements as uefi-x64.systemd-boot.esp
     _validate_requirements_bootmode_uefi-x64.systemd-boot.esp
 }
@@ -774,7 +862,15 @@ _validate_requirements_bootmode_uefi-ia32.grub.esp() {
         (( validation_error=validation_error+1 ))
         _msg_error "Validating '${bootmode}': grub-install is not available on this host. Install 'grub'!" 0
     fi
-    _validate_requirements_bootmode_uefi-x64.systemd-boot.esp
+
+    # shellcheck disable=SC2076
+    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' ]]; then
+        _validate_requirements_bootmode_uefi-x64.systemd-boot.esp
+    elif [[ " ${bootmodes[*]} " =~ ' uefi-x64.grub.esp ' ]]; then
+        _validate_requirements_bootmode_uefi-x64.grub.esp
+    else
+        _msg_error "Validating '${bootmode}': requires one of bootmode uefi-x64.systemd-boot.esp or uefi-x64.grub.esp" 0
+    fi
 }
 
 _validate_requirements_bootmode_uefi-ia32.grub.eltorito() {
@@ -782,6 +878,65 @@ _validate_requirements_bootmode_uefi-ia32.grub.eltorito() {
     _validate_requirements_bootmode_uefi-ia32.grub.esp
 }
 
+_validate_requirements_bootmode_uefi-x64.grub.esp() {
+    # shellcheck disable=SC2076
+    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' ]]; then
+        _msg_error "Validating '${bootmode}': cannot be used with bootmode uefi-x64.systemd-boot.esp!" 0
+    fi
+
+    # Check if GRUB is available
+    if ! command -v grub-mkstandalone &> /dev/null; then
+        (( validation_error=validation_error+1 ))
+        _msg_error "Validating '${bootmode}': grub-install is not available on this host. Install 'grub'!" 0
+    fi
+
+        # Check if mkfs.fat is available
+    if ! command -v mkfs.fat &> /dev/null; then
+        (( validation_error=validation_error+1 ))
+        _msg_error "Validating '${bootmode}': mkfs.fat is not available on this host. Install 'dosfstools'!" 0
+    fi
+
+    # Check if mmd and mcopy are available
+    if ! { command -v mmd &> /dev/null && command -v mcopy &> /dev/null; }; then
+        _msg_error "Validating '${bootmode}': mmd and/or mcopy are not available on this host. Install 'mtools'!" 0
+    fi
+
+    # Check if GRUB configuration files exist
+    if [[ ! -d "${profile}/grub" ]]; then
+        (( validation_error=validation_error+1 ))
+        _msg_error "Validating '${bootmode}': The '${profile}/grub' directory is missing!" 0
+    else
+        if [[ ! -e "${profile}/grub/grub.cfg" ]]; then
+            (( validation_error=validation_error+1 ))
+            _msg_error "Validating '${bootmode}': File '${profile}/grub/grub.cfg' not found!" 0
+        fi
+        local conffile
+        for conffile in "${profile}/grub/"*'.cfg'; do
+            if [[ -e "${conffile}" ]]; then
+                break
+            else
+                (( validation_error=validation_error+1 ))
+                _msg_error "Validating '${bootmode}': No configuration file found in '${profile}/grub/'!" 0
+            fi
+        done
+    fi
+
+    # Check for optional packages
+    # shellcheck disable=SC2076
+    if [[ ! " ${pkg_list[*]} " =~ ' edk2-shell ' ]]; then
+        _msg_info "'edk2-shell' is not in the package list. The ISO will not contain a bootable UEFI shell."
+    fi
+}
+
+_validate_requirements_bootmode_uefi-x64.grub.eltorito() {
+    # shellcheck disable=SC2076
+    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.eltorito ' ]]; then
+        _msg_error "Validating '${bootmode}': cannot be used with bootmode uefi-x64.systemd-boot.eltorito!" 0
+    fi
+    # uefi-x64.grub.eltorito has the exact same requirements as uefi-x64.grub.esp
+    _validate_requirements_bootmode_uefi-x64.grub.esp
+}
+
 # Build airootfs filesystem image
 _prepare_airootfs_image() {
     _run_once "_mkairootfs_${airootfs_image_type}"
@@ -1002,17 +1157,20 @@ _add_xorrisofs_options_bios.syslinux.mbr() {
 
 # GRUB in an attached EFI system partition
 _add_xorrisofs_options_uefi-ia32.grub.esp() {
+    # TODO: how does the bootmodes systemd-boot vs x64.grub affect ${bootmodes[*]} tests in _add_xorrisofs_options_uefi-x64.systemd-boot.esp etc?
     # shellcheck disable=SC2076
-    if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' ]]; then
-        _add_xorrisofs_options_uefi-x64.systemd-boot.esp
+    if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' && ! " ${bootmodes[*]} " =~ ' uefi-x64.grub.esp ' ]]; then
+        # _add_xorrisofs_options_uefi-x64.systemd-boot.esp
+        _add_xorrisofs_options_uefi-x64.grub.esp
     fi
 }
 
 # GRUB via El Torito
 _add_xorrisofs_options_uefi-ia32.grub.eltorito() {
     # shellcheck disable=SC2076
-    if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.eltorito ' ]]; then
-        _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito
+    if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.eltorito ' && ! " ${bootmodes[*]} " =~ ' uefi-x64.grub.eltorito ' ]]; then
+        # _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito
+        _add_xorrisofs_options_uefi-x64.grub.eltorito
     fi
 }
 
@@ -1086,6 +1244,78 @@ _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito() {
     [[ " ${bootmodes[*]} " =~ ' bios.' ]] || xorrisofs_options+=('-eltorito-catalog' 'EFI/boot.cat')
 }
 
+# GRUB in an attached EFI system partition.
+# Same as _add_xorrisofs_options_uefi-x64.systemd-boot.esp.
+_add_xorrisofs_options_uefi-x64.grub.esp() {
+    # Move the first partition away from the start of the ISO, otherwise the GPT will not be valid and ISO 9660
+    # partition will not be mountable
+    # shellcheck disable=SC2076
+    [[ " ${xorrisofs_options[*]} " =~ ' -partition_offset ' ]] || xorrisofs_options+=('-partition_offset' '16')
+    # Attach efiboot.img as a second partition and set its partition type to "EFI system partition"
+    xorrisofs_options+=('-append_partition' '2' 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' "${work_dir}/efiboot.img")
+    # Ensure GPT is used as some systems do not support UEFI booting without it
+    # shellcheck disable=SC2076
+    if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]]; then
+        # A valid GPT prevents BIOS booting on some systems, instead use an invalid GPT (without a protective MBR).
+        # The attached partition will have the EFI system partition type code in MBR, but in the invalid GPT it will
+        # have a Microsoft basic partition type code.
+        if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.grub.eltorito ' &&  ! " ${bootmodes[*]} " =~ ' uefi-ia32.grub.eltorito ' ]]; then
+            # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the
+            # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e',
+            # the appended EFI system partition will have the Microsoft basic data type GUID in GPT.
+            if [[ ! " ${xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then
+                xorrisofs_options+=('-isohybrid-gpt-basdat')
+            fi
+        fi
+    else
+        # Use valid GPT if BIOS booting support will not be required
+        xorrisofs_options+=('-appended_part_as_gpt')
+    fi
+}
+
+# GRUB via El Torito
+# Same as _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito.
+_add_xorrisofs_options_uefi-x64.grub.eltorito() {
+    # shellcheck disable=SC2076
+    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.grub.esp ' || " ${bootmodes[*]} " =~ ' uefi-ia32.grub.esp ' ]]; then
+        # grub in an attached EFI system partition via El Torito
+        xorrisofs_options+=(
+            # Start a new El Torito boot entry for UEFI
+            '-eltorito-alt-boot'
+            # Set the second partition as the El Torito UEFI boot image
+            '-e' '--interval:appended_partition_2:all::'
+            # Boot image is not emulating floppy or hard disk; required for all known boot loaders
+            '-no-emul-boot'
+        )
+        # A valid GPT prevents BIOS booting on some systems, use an invalid GPT instead.
+        if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]]; then
+            # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the
+            # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e',
+            # the appended EFI system partition will have the Microsoft basic data type GUID in GPT.
+            if [[ ! " ${xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then
+                xorrisofs_options+=('-isohybrid-gpt-basdat')
+            fi
+        fi
+    else
+        # The ISO will not contain a GPT partition table, so to be able to reference efiboot.img, place it as a
+        # file inside the ISO 9660 file system
+        install -d -m 0755 -- "${isofs_dir}/EFI/archiso"
+        cp -a -- "${work_dir}/efiboot.img" "${isofs_dir}/EFI/archiso/efiboot.img"
+        # grub in an embedded efiboot.img via El Torito
+        xorrisofs_options+=(
+            # Start a new El Torito boot entry for UEFI
+            '-eltorito-alt-boot'
+            # Set efiboot.img as the El Torito UEFI boot image
+            '-e' 'EFI/archiso/efiboot.img'
+            # Boot image is not emulating floppy or hard disk; required for all known boot loaders
+            '-no-emul-boot'
+        )
+    fi
+    # Specify where to save the El Torito boot catalog file in case it is not already set by bios.syslinux.eltorito
+    # shellcheck disable=SC2076
+    [[ " ${bootmodes[*]} " =~ ' bios.' ]] || xorrisofs_options+=('-eltorito-catalog' 'EFI/boot.cat')
+}
+
 # Build bootstrap image
 _build_bootstrap_image() {
     local _bootstrap_parent
-- 
GitLab