Support EFI on 32-bit systems, increase EFI image size + switch from isohybrid to...
authorMichael Prokop <mika@grml.org>
Wed, 7 Jun 2017 21:25:34 +0000 (23:25 +0200)
committerMichael Prokop <mika@grml.org>
Fri, 9 Jun 2017 08:18:41 +0000 (10:18 +0200)
EFI on 32-bit systems is a requested feature for Grml-Forensic,
since cheap tablets and notebooks (e.g. Intel Atom-based tablets)
are out there with only 32-bit EFI support (and UEFI only, so no
legacy BIOS support), quoting clairelyclaire from
https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/1341944:

| As of now, Ubuntu and other major Linux distributions do not
| support the use of a 32-bit EFI bootloader on UEFI machines. This
| has become extremely problematic due to the popularity of Intel
| Atom-based tablets and compact laptops. Atom-based devices are
| generally limited in storage space (32GB or 64GB eMMC is common),
| and as a result these devices almost universally ship with
| Windows 8.1 32-bit installed (winsxs consumes a significant
| amount of storage space in order to support 32-bit binaries in a
| 64-bit environment). By design, UEFI must use the same
| architecture used by the bootloader.
|
| While most modern computers indeed use a 64-bit UEFI
| implementation due to the fact that new computers generally ship
| with a 64-bit operating system (be it OS X or Windows 8.1),
| Atom-based devices do *not* use a 64-bit operating system or UEFI
| implementation. This is by design.
|
| Intel released a new Atom iteration (Bay Trail) in late 2013 and
| has indicated that they will continue to develop and release Atom
| CPUs due to consumer market demand. At the time of this filing
| there are a number of Atom-based tablets and compact
| laptops/netbooks being actively sold and marketed by major OEMs
| including Dell, HP, ASUS, and Acer. None of these devices have
| 64-bit UEFI firmware. It is also important to note that these
| Atom CPUs are 64-bit, but explicitly require a 32-bit UEFI
| bootloader.
|
| The current Linux kernel in Ubuntu 14.04 does support booting the
| 64-bit signed kernel from a 32-bit Grub EFI bootloader. I can
| confirm this on at least two 32-bit UEFI devices, the ASUS
| Transformer T100TA and the Acer Aspire Switch 10.

Increase EFI image size (previously automatically
calculated on-demand, resulting in ~285KB) to 4MB,
giving us more flexibility with what we're installing
into the image (esp. useful with usage on USB drives).

The isohybrid binary doesn't support 32-bit FI systems
and fails hard when using `--uefi` on a 32-bit ISO.
But xorriso with appropriate options for EFI usage
(see $EFI_ARGS) and /usr/lib/ISOLINUX/isohdpfx.bin
from the isolinux package seems to provide everything
we need.

Useful resources for further information:
* http://www.syslinux.org/wiki/index.php?title=Isohybrid
* https://fedoraproject.org/wiki/Using_UEFI_with_QEMU
* https://wiki.archlinux.org/index.php/Remastering_the_Install_ISO

For testing the resulting 32-bit ISO with EFI the
OVMF.fd file from OVMF-IA32-r15214.zip available from
https://sourceforge.net/projects/edk2/files/OVMF/ works via e.g.:

| qemu-system-i386 -m 1024 -bios ./OVMF.fd -cdrom grml.iso

debian/control
etc/grml/fai/config/scripts/GRMLBASE/45-grub-images
grml-live

index 29a09af..f28c684 100644 (file)
@@ -19,6 +19,7 @@ Depends: bc,
          bzip2,
          fai-client (>= 3.4.0),
          fai-server (>= 3.4.0),
+         isolinux (>= 3:6.03+dfsg-5+deb8u1~),
          memtest86+,
          mksh,
          moreutils,
index 7b66dcb..42ed130 100755 (executable)
@@ -7,9 +7,11 @@
 ################################################################################
 
 set -e
+set -u
 
 
 BOOTX64="${target}/boot/bootx64.efi"
+BOOTX32="${target}/boot/bootia32.efi"
 EFI_IMG="${target}/boot/efi.img"
 TMP_CONFIG="${target}/tmp/grub_config_efi"
 
@@ -24,6 +26,7 @@ echo "E: Could not find root device!"
 EOF
 
 BOOTX64="${BOOTX64##${target}}"
+BOOTX32="${BOOTX32##${target}}"
 EFI_IMG="${EFI_IMG##${target}}"
 TMP_CONFIG="${TMP_CONFIG##${target}}"
 
@@ -40,17 +43,21 @@ if ifclass AMD64 ; then
     echo "/usr/lib/grub/x86_64-efi/moddep.lst could not be found, skipping."
     echo "NOTE: grub-efi-amd64-bin not installed?"
   fi
+elif ifclass I386 ; then
+  if [ -r "${target}"/usr/lib/grub/i386-efi/moddep.lst ] ; then
+    ARCHS+=(i386-efi)
+    ADDITIONAL_MODULES[i386-efi]="efi_gop efi_uga"
+  else
+    echo "/usr/lib/grub/i386-efi/moddep.lst could not be found, skipping."
+    echo "NOTE: grub-efi-ia32 not installed?"
+  fi
 fi
 
-BOOTX64="${BOOTX64##${target}}"
-EFI_IMG="${EFI_IMG##${target}}"
-TMP_CONFIG="${TMP_CONFIG##${target}}"
-
 for arch in ${ARCHS[@]} ; do
-$ROOTCMD grub-mkimage -O $arch -o /boot/$arch.img --prefix=/boot/grub/ --config="$TMP_CONFIG" \
-  echo iso9660 part_msdos search_fs_file test \
-  fat ext2 reiserfs xfs btrfs squash4 part_gpt lvm \
-  ${ADDITIONAL_MODULES[$arch]}
+  $ROOTCMD grub-mkimage -O $arch -o /boot/$arch.img --prefix=/boot/grub/ --config="$TMP_CONFIG" \
+    echo iso9660 part_msdos search_fs_file test \
+    fat ext2 reiserfs xfs btrfs squash4 part_gpt lvm \
+    ${ADDITIONAL_MODULES[$arch]}
 done
 
 if [ -f "${target}/boot/i386-pc.img" ] ; then
@@ -61,27 +68,40 @@ if [ -f "${target}/boot/x86_64-efi.img" ] ; then
   mv "${target}/boot/x86_64-efi.img" "${target}/${BOOTX64}"
 fi
 
+if [ -f "${target}/boot/i386-efi.img" ] ; then
+  mv "${target}/boot/i386-efi.img" "${target}/${BOOTX32}"
+fi
+
 if ifclass AMD64 ; then
   if ! [ -r "${target}/${BOOTX64}" ] ; then
-    echo "Can not access grub efi image." >&2
+    echo "Can not access grub efi image ${BOOTX64}." >&2
     exit 1
   fi
 
-  SIZE=$(du --apparent-size -sk "${target}/${BOOTX64}" | awk -F" " '{print $1}')
-  SIZE=$(((($SIZE / 32 )+2)*32))
-
-  dd if=/dev/zero of="${target}/${EFI_IMG}" bs=1k count="$SIZE" 2>/dev/null
+  dd if=/dev/zero of="${target}/${EFI_IMG}" bs=4M count=1 2>/dev/null
   $ROOTCMD mkfs.vfat -n GRML "$EFI_IMG" >/dev/null
   $ROOTCMD mmd -i "$EFI_IMG" ::EFI
   $ROOTCMD mmd -i "$EFI_IMG" ::EFI/BOOT
   $ROOTCMD mcopy -i "$EFI_IMG" "$BOOTX64" ::EFI/BOOT/bootx64.efi >/dev/null
-  echo "Generated EFI image $BOOTX64"
-  echo "Generated bootx64 image $EFI_IMG"
+  echo "Generated 64-bit EFI image $BOOTX64"
+elif ifclass I386 ; then
+  if ! [ -r "${target}/${BOOTX32}" ] ; then
+    echo "Can not access grub efi image ${BOOTX32}." >&2
+    exit 1
+  fi
+
+  dd if=/dev/zero of="${target}/${EFI_IMG}" bs=4M count=1 2>/dev/null
+  $ROOTCMD mkfs.vfat -n GRML "$EFI_IMG" >/dev/null
+  $ROOTCMD mmd -i "$EFI_IMG" ::EFI
+  $ROOTCMD mmd -i "$EFI_IMG" ::EFI/BOOT
+  $ROOTCMD mcopy -i "$EFI_IMG" "$BOOTX32" ::EFI/BOOT/bootia32.efi >/dev/null
+  echo "Generated 32-bit EFI image $BOOTX32"
 fi
 
+echo "Generated EFI image $EFI_IMG"
+
 rm -f "${target}/${TMP_CONFIG}"
 echo "Generated Grub images"
 
-
 ## END OF FILE #################################################################
 # vim:ft=sh expandtab ai tw=80 tabstop=4 shiftwidth=2
index bdefd28..2e7aad7 100755 (executable)
--- a/grml-live
+++ b/grml-live
@@ -891,13 +891,23 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
 
        # EFI boot files
        if [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootx64.efi" ] ; then
-          einfo "Moving EFI boot files into ISO path."
-          log "Moving EFI boot files into ISO path."
-          RC=$0
-          mv "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
-          mkdir -p "${BUILD_OUTPUT}/efi/boot/" || RC=$?
-          mv "${CHROOT_OUTPUT}/boot/bootx64.efi" "${BUILD_OUTPUT}/efi/boot/bootx64.efi" || RC=$?
-          eend $?
+         einfo "Moving 64-bit EFI boot files into ISO path."
+         log "Moving 64-bit EFI boot files into ISO path."
+         RC=$0
+         mv "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
+         mkdir -p "${BUILD_OUTPUT}/efi/boot/" || RC=$?
+         mv "${CHROOT_OUTPUT}/boot/bootx64.efi" "${BUILD_OUTPUT}/efi/boot/bootx64.efi" || RC=$?
+         eend $?
+       elif [ -r "${CHROOT_OUTPUT}/boot/efi.img" -a -r "${CHROOT_OUTPUT}/boot/bootia32.efi" ] ; then
+         einfo "Moving 32-bit EFI boot files into ISO path."
+         log "Moving 32-bit EFI boot files into ISO path."
+         RC=$0
+         mv "${CHROOT_OUTPUT}/boot/efi.img" "${BUILD_OUTPUT}/boot/" || RC=$?
+         mkdir -p "${BUILD_OUTPUT}/efi/boot/" || RC=$?
+         mv "${CHROOT_OUTPUT}/boot/bootia32.efi" "${BUILD_OUTPUT}/efi/boot/bootia32.efi" || RC=$?
+         eend $?
+       else
+         ewarn "No EFI boot files found, skipping." ; eend 0
        fi
 
        [ -n "$TEMPLATE_DIRECTORY" ] || TEMPLATE_DIRECTORY='/usr/share/grml-live/templates'
@@ -999,10 +1009,14 @@ if [ "$ARCH" = i386 ] || [ "$ARCH" = amd64 ] ; then
        cp -a "${CHROOT_OUTPUT}"/boot/grub/core.img "${BUILD_OUTPUT}"/boot/grub/
        cp -a "${CHROOT_OUTPUT}"/boot/grub/grub.img "${BUILD_OUTPUT}"/boot/grub/
 
-       # copy modules for UEFI grub
+       # copy modules for UEFI grub, 64-bit
        mkdir -p "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
        cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/x86_64-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/x86_64-efi/
 
+       # copy modules for UEFI grub, 32-bit
+       mkdir -p "${BUILD_OUTPUT}"/boot/grub/i386-efi/
+       cp -a "${CHROOT_OUTPUT}"/usr/lib/grub/i386-efi/*.{mod,lst} "${BUILD_OUTPUT}"/boot/grub/i386-efi/
+
        if ! [ -d "${TEMPLATE_DIRECTORY}"/GRML ] ; then
           log    "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting."
           eerror "Error: ${TEMPLATE_DIRECTORY}/GRML does not exist. Exiting." ; eend 1
@@ -1277,6 +1291,9 @@ fi
 
 if [ "$BOOT_METHOD" = "isolinux" ] ; then
    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat"
+   if [ "$HYBRID_METHOD" = "isohybrid" ] ; then
+     EFI_ARGS="-isohybrid-mbr /usr/lib/ISOLINUX/isohdpfx.bin -eltorito-alt-boot -e boot/efi.img -no-emul-boot -isohybrid-gpt-basdat"
+   fi
 elif [ "$BOOT_METHOD" = "grub2" ] ; then
    BOOT_ARGS="-no-emul-boot -boot-load-size 4 -b boot/grub/toriboot.bin"
 fi
@@ -1350,9 +1367,9 @@ else
          echo 1 16 | mksh "${SCRIPTS_DIRECTORY}/bootgrub.mksh" -B 11 | \
             dd of=boot/grub/toriboot.bin conv=notrunc 2>/dev/null
       fi
-      log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS -o ${ISO_OUTPUT}/${ISO_NAME} ."
+      log "$MKISOFS -V '${GRML_NAME} ${VERSION}' -publisher 'grml-live | grml.org' -l -r -J $BOOT_ARGS $EFI_ARGS -no-pad -o ${ISO_OUTPUT}/${ISO_NAME} ."
       $MKISOFS -V "${GRML_NAME} ${VERSION}" -publisher 'grml-live | grml.org' \
-              -l -r -J $BOOT_ARGS -no-pad \
+              -l -r -J $BOOT_ARGS $EFI_ARGS -no-pad \
               -o "${ISO_OUTPUT}/${ISO_NAME}" . ; RC=$?
       # both of these need core.img there, so it’s easier to write it here
       if [ "$BOOT_METHOD" = "grub2" ] || [ "$HYBRID_METHOD" = "grub2" ]; then
@@ -1400,33 +1417,7 @@ else
           eend $?
         fi
       elif [ "$HYBRID_METHOD" = "isohybrid" ] ; then
-        if ! which isohybrid >/dev/null 2>&1 ; then
-          bailout 12 "isohybrid binary not found - please install syslinux/syslinux-common/syslinux-utils"
-        else
-          log   "Creating hybrid ISO file with isohybrid method"
-          einfo "Creating hybrid ISO file with isohybrid method"
-          # Notes for consideration:
-          # "-entry 4 -type 1c"
-          # * using 4 as the partition number is supposed to help with BIOSes
-          #   that only support USB-Zip boot
-          # * using 1c (i.e. hidden FAT32 LBA), instead of the default 0x17
-          #   (hidden NTFS, IIRC), as the partition type is sometimes needed
-          #   to get the BIOS even look at the partition created by isohybrid
-          if isohybrid --help | grep -q -- --uefi ; then
-            if echo $CLASSES | grep -qw I386 ; then
-              log   "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
-              einfo "Detected uefi support for isohybrid but 32bit systems do not support it, ignoring."
-            else
-              log   "Detected uefi support for isohybrid, enabling"
-              einfo "Detected uefi support for isohybrid, enabling"
-              ISOHYBRID_OPTIONS=--uefi
-            fi
-          fi
-
-          log "isohybrid $ISOHYBRID_OPTIONS ${ISO_OUTPUT}/${ISO_NAME}"
-          isohybrid $ISOHYBRID_OPTIONS "${ISO_OUTPUT}/${ISO_NAME}"
-          eend $?
-        fi
+        : # nothing to do, handled via $MKISOFS $EFI_ARGS already
       else
         bailout 12 "Unknown HYBRID_METHOD [${HYBRID_METHOD}]. Supported values: disable, isohybrid, grub2, manifold"
       fi