From 2b7e1b4a2876083f09f66cb3f4115709cb66704f Mon Sep 17 00:00:00 2001
From: Pellegrino Prevete <pellegrinoprevete@gmail.com>
Date: Wed, 25 May 2022 14:49:02 +0000
Subject: [PATCH] Add support for GRUB ia32 UEFI in mkarchiso, update READMEs.

---
 README.rst              |   1 +
 archiso/mkarchiso       | 127 +++++++++++++++++++++++++++++++++++++---
 docs/README.profile.rst |  13 +++-
 3 files changed, 131 insertions(+), 10 deletions(-)

diff --git a/README.rst b/README.rst
index 62cea013..6ac74342 100644
--- a/README.rst
+++ b/README.rst
@@ -17,6 +17,7 @@ The following packages need to be installed to be able to create an image with t
 * e2fsprogs
 * erofs-utils (optional)
 * findutils
+* grub
 * gzip
 * libarchive
 * libisoburn
diff --git a/archiso/mkarchiso b/archiso/mkarchiso
index f6b33950..3e0a86f1 100755
--- a/archiso/mkarchiso
+++ b/archiso/mkarchiso
@@ -39,6 +39,7 @@ airootfs_image_tool_options=()
 cert_list=()
 sign_netboot_artifacts=""
 declare -A file_permissions=()
+efiboot_files=()
 # adapted from GRUB_EARLY_INITRD_LINUX_STOCK in https://git.savannah.gnu.org/cgit/grub.git/tree/util/grub-mkconfig.in
 readonly ucodes=('intel-uc.img' 'intel-ucode.img' 'amd-uc.img' 'amd-ucode.img' 'early_ucode.cpio' 'microcode.cpio')
 
@@ -516,6 +517,84 @@ _make_efibootimg() {
     mmd -i "${work_dir}/efiboot.img" ::/EFI ::/EFI/BOOT
 }
 
+_make_bootmode_uefi-ia32.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 i386-efi \
+                      --modules="part_gpt part_msdos fat iso9660" \
+                      --locales="en@quot" \
+                      --themes="" \
+                      -o "${work_dir}/BOOTIA32.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}/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")
+
+        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
+    mcopy -i "${work_dir}/efiboot.img" \
+        "${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
+
+    # 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
+    fi
+
+    _msg_info "Done! GRUB set up for UEFI booting successfully."
+}
+
+# Prepare GRUB for El Torito booting
+_make_bootmode_uefi-ia32.grub.eltorito() {
+    # El Torito UEFI boot requires an image containing the EFI system partition.
+    # uefi-ia32.grub.eltorito has the same requirements as uefi-ia32.grub.esp
+    _run_once _make_bootmode_uefi-ia32.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}/BOOTIA32.EFI" \
+        "${isofs_dir}/EFI/BOOT/BOOTIA32.EFI"
+
+    # Copy GRUB configuration files
+    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"
+    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
@@ -528,13 +607,13 @@ _make_bootmode_uefi-x64.systemd-boot.esp() {
         fi
     done
     # Calculate the required FAT image size in bytes
-    efiboot_imgsize="$(du -bc \
-        "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \
-        "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" \
-        "${profile}/efiboot/" \
-        "${pacstrap_dir}/boot/vmlinuz-"* \
-        "${pacstrap_dir}/boot/initramfs-"*".img" \
-        "${_available_ucodes[@]}" \
+    efiboot_files+=("${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi"
+                    "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi"
+                    "${profile}/efiboot/"
+                    "${pacstrap_dir}/boot/vmlinuz-"*
+                    "${pacstrap_dir}/boot/initramfs-"*".img"
+                    "${_available_ucodes[@]}")
+    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"
@@ -689,6 +768,20 @@ _validate_requirements_bootmode_uefi-x64.systemd-boot.eltorito() {
     _validate_requirements_bootmode_uefi-x64.systemd-boot.esp
 }
 
+_validate_requirements_bootmode_uefi-ia32.grub.esp() {
+    # 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
+    _validate_requirements_bootmode_uefi-x64.systemd-boot.esp
+}
+
+_validate_requirements_bootmode_uefi-ia32.grub.eltorito() {
+    # uefi-ia32.grub.eltorito has the exact same requirements as uefi-ia32.grub.esp
+    _validate_requirements_bootmode_uefi-ia32.grub.esp
+}
+
 # Build airootfs filesystem image
 _prepare_airootfs_image() {
     _run_once "_mkairootfs_${airootfs_image_type}"
@@ -907,6 +1000,22 @@ _add_xorrisofs_options_bios.syslinux.mbr() {
     )
 }
 
+# GRUB in an attached EFI system partition
+_add_xorrisofs_options_uefi-ia32.grub.esp() {
+    # shellcheck disable=SC2076
+    if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' ]]; then
+        _add_xorrisofs_options_uefi-x64.systemd-boot.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
+    fi
+}
+
 # systemd-boot in an attached EFI system partition
 _add_xorrisofs_options_uefi-x64.systemd-boot.esp() {
     # Move the first partition away from the start of the ISO, otherwise the GPT will not be valid and ISO 9660
@@ -921,7 +1030,7 @@ _add_xorrisofs_options_uefi-x64.systemd-boot.esp() {
         # 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.systemd-boot.eltorito ' ]]; then
+        if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.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.
@@ -938,7 +1047,7 @@ _add_xorrisofs_options_uefi-x64.systemd-boot.esp() {
 # systemd-boot via El Torito
 _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito() {
     # shellcheck disable=SC2076
-    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' ]]; then
+    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' || " ${bootmodes[*]} " =~ ' uefi-ia32.grub.esp ' ]]; then
         # systemd-boot in an attached EFI system partition via El Torito
         xorrisofs_options+=(
             # Start a new El Torito boot entry for UEFI
diff --git a/docs/README.profile.rst b/docs/README.profile.rst
index c93228d5..a3d43093 100644
--- a/docs/README.profile.rst
+++ b/docs/README.profile.rst
@@ -10,6 +10,7 @@ An archiso profile consists of several configuration files and a directory for f
    ├── airootfs/
    ├── efiboot/
    ├── syslinux/
+   ├── grub/
    ├── bootstrap_packages.arch
    ├── packages.arch
    ├── pacman.conf
@@ -45,6 +46,8 @@ The image file is constructed from some of the variables in ``profiledef.sh``: `
 
   - ``bios.syslinux.mbr``: Syslinux for x86 BIOS booting from a disk
   - ``bios.syslinux.eltorito``: Syslinux for x86 BIOS booting from an optical disc
+  - ``uefi-ia32.grub.esp``: GRUB for IA32 UEFI booting from a disk
+  - ``uefi-ia32.grub.eltorito``: GRUB for IA32 UEFI booting from an optical disc
   - ``uefi-x64.systemd-boot.esp``: systemd-boot for x86_64 UEFI booting from a disk
   - ``uefi-x64.systemd-boot.eltorito``: systemd-boot for x86_64 UEFI booting from an optical disc
     Note that BIOS El Torito boot mode must always be listed before UEFI El Torito boot mode.
@@ -158,6 +161,14 @@ This directory is mandatory when the ``bios.syslinux.mbr`` or the ``bios.syslinu
 ``profiledef.sh``.
 It contains configuration files for `syslinux <https://wiki.syslinux.org/wiki/index.php?title=SYSLINUX>`_ or `isolinux
 <https://wiki.syslinux.org/wiki/index.php?title=ISOLINUX>`_ , or `pxelinux
-<https://wiki.syslinux.org/wiki/index.php?title=PXELINUX>`_ used in the resuling image.
+<https://wiki.syslinux.org/wiki/index.php?title=PXELINUX>`_ used in the resulting image.
 
 The *custom template identifiers* are understood in all `.cfg` files in this directory.
+
+grub
+----
+
+This directory is mandatory when the ``uefi-ia32.grub.esp`` or ``uefi-ia32.grub.eltorito`` bootmodes are selected in
+``profiledef.sh``.
+It contains configuration files for `GRUB <https://www.gnu.org/software/grub/>`_
+used in the resulting image.
-- 
GitLab