src/13_prepare_iso.sh
#!/bin/sh

set -e

# Load common properties and functions in the current script.
. ./common.sh

init() {
  # Remove the old ISO generation area if it exists.
  echo "Removing old ISO image work area. This may take a while."
  rm -rf $ISOIMAGE

  echo "Preparing new ISO image work area."
  mkdir -p $ISOIMAGE
}

prepare_mll_bios() {
  # This is the folder where we keep legacy BIOS boot artifacts.
  mkdir -p $ISOIMAGE/boot

  # Now we copy the kernel.
  cp $KERNEL_INSTALLED/kernel \
    $ISOIMAGE/boot/kernel.xz

  # Now we copy the root file system.
  cp $WORK_DIR/rootfs.cpio.xz \
    $ISOIMAGE/boot/rootfs.xz
}

prepare_overlay() {
  # Now we copy the overlay content if it exists.
  if [ -d $ISOIMAGE_OVERLAY \
    -a ! "`ls $ISOIMAGE_OVERLAY`" = "" ] ; then

    echo "The ISO image will have overlay structure."
    cp -r $ISOIMAGE_OVERLAY/* $ISOIMAGE
  else
    echo "The ISO image will have no overlay structure."
  fi
}

prepare_boot_bios() {
  # Add the Syslinux configuration files for legacy BIOS and additional
  # UEFI startup script.
  #
  # The existing UEFI startup script does not guarantee that you can run
  # MLL on UEFI systems. This script is invoked only in case your system
  # drops you in UEFI shell with support level 1 or above. See UEFI shell
  # specification 2.2, section 3.1. Depending on your system configuration
  # you may not end up with UEFI shell even if your system supports it.
  # In this case MLL will not boot and you will end up with some kind of
  # UEFI error message.
  cp -r $SRC_DIR/minimal_boot/bios/* \
    $ISOIMAGE

  # Find the Syslinux build directory.
  WORK_SYSLINUX_DIR=`ls -d $WORK_DIR/syslinux/syslinux-*`

  # Copy the precompiled files 'isolinux.bin' and 'ldlinux.c32'. These files
  # are used by Syslinux during the legacy BIOS boot process.
  mkdir -p $ISOIMAGE/boot/syslinux
  cp $WORK_SYSLINUX_DIR/bios/core/isolinux.bin \
    $ISOIMAGE/boot/syslinux
  cp $WORK_SYSLINUX_DIR/bios/com32/elflink/ldlinux/ldlinux.c32 \
    $ISOIMAGE/boot/syslinux
}

# Generate 'El Torito' boot image as per UEFI specification 2.7,
# sections 13.3.1.x and 13.3.2.x.
prepare_boot_uefi() {
  # Find the build architecture based on the Busybox executable.
  BUSYBOX_ARCH=$(file $ROOTFS/bin/busybox | cut -d' ' -f3)

  # Determine the proper UEFI configuration. The default image file
  # names are described in UEFI specification 2.7, section 3.5.1.1.
  # Note that the x86_64 UEFI image file name indeed contains small
  # letter 'x'.
  if [ "$BUSYBOX_ARCH" = "64-bit" ] ; then
    MLL_CONF=x86_64
    LOADER=$WORK_DIR/systemd-boot/systemd-boot*/uefi_root/EFI/BOOT/BOOTx64.EFI
  else
    MLL_CONF=x86
    LOADER=$WORK_DIR/systemd-boot/systemd-boot*/uefi_root/EFI/BOOT/BOOTIA32.EFI
  fi

  # Find the kernel size in bytes.
  kernel_size=`du -b $KERNEL_INSTALLED/kernel | awk '{print \$1}'`

  # Find the initramfs size in bytes.
  rootfs_size=`du -b $WORK_DIR/rootfs.cpio.xz | awk '{print \$1}'`

  loader_size=`du -b $LOADER | awk '{print \$1}'`

  # The EFI boot image is 64KB bigger than the kernel size.
  image_size=$((kernel_size + rootfs_size + loader_size + 65536))

  echo "Creating UEFI boot image file '$WORK_DIR/uefi.img'."
  rm -f $WORK_DIR/uefi.img
  truncate -s $image_size $WORK_DIR/uefi.img

  echo "Attaching hard disk image file to loop device."
  LOOP_DEVICE_HDD=$(losetup -f)
  losetup $LOOP_DEVICE_HDD $WORK_DIR/uefi.img

  echo "Formatting hard disk image with FAT filesystem."
  mkfs.vfat $LOOP_DEVICE_HDD

  echo "Preparing 'uefi' work area."
  rm -rf $WORK_DIR/uefi
  mkdir -p $WORK_DIR/uefi
  mount $WORK_DIR/uefi.img $WORK_DIR/uefi

#  # Add the configuration files for UEFI boot.
#  cp -r $SRC_DIR/minimal_boot/uefi/* \
#    $ISOIMAGE

  echo "Preparing kernel and rootfs."
  mkdir -p $WORK_DIR/uefi/minimal/$MLL_CONF
  cp $KERNEL_INSTALLED/kernel \
    $WORK_DIR/uefi/minimal/$MLL_CONF/kernel.xz
  cp $WORK_DIR/rootfs.cpio.xz \
    $WORK_DIR/uefi/minimal/$MLL_CONF/rootfs.xz

  echo "Preparing 'systemd-boot' UEFI boot loader."
  mkdir -p $WORK_DIR/uefi/EFI/BOOT
  cp $LOADER \
    $WORK_DIR/uefi/EFI/BOOT

  echo "Preparing 'systemd-boot' configuration."
  mkdir -p $WORK_DIR/uefi/loader/entries
  cp $SRC_DIR/minimal_boot/uefi/loader/loader.conf \
    $WORK_DIR/uefi/loader
  cp $SRC_DIR/minimal_boot/uefi/loader/entries/mll-${MLL_CONF}.conf \
    $WORK_DIR/uefi/loader/entries

  echo "Setting the default UEFI boot entry."
  sed -i "s|default.*|default mll-$MLL_CONF|" $WORK_DIR/uefi/loader/loader.conf

  echo "Unmounting UEFI boot image file."
  sync
  umount $WORK_DIR/uefi
  sync
  sleep 1

  # The directory is now empty (mount point for loop device).
  rm -rf $WORK_DIR/uefi

  # Make sure the UEFI boot image is readable.
  chmod ugo+r $WORK_DIR/uefi.img

  mkdir -p $ISOIMAGE/boot
  cp $WORK_DIR/uefi.img \
    $ISOIMAGE/boot
}

check_root() {
  if [ ! "$(id -u)" = "0" ] ; then
    cat << CEOF

  ISO image preparation process for UEFI systems requires root permissions
  but you don't have such permissions. Restart this script with root
  permissions in order to generate UEFI compatible ISO structure.

CEOF
    exit 1
  fi
}

echo "*** PREPARE ISO BEGIN ***"

# Read the 'FIRMWARE_TYPE' property from '.config'
FIRMWARE_TYPE=`read_property FIRMWARE_TYPE`
echo "Firmware type is '$FIRMWARE_TYPE'."

case $FIRMWARE_TYPE in
  bios)
    init
    prepare_boot_bios
    prepare_mll_bios
    prepare_overlay
    ;;

  uefi)
    check_root
    init
    prepare_boot_uefi
    prepare_overlay
    ;;

  both)
    check_root
    init
    prepare_boot_uefi
    prepare_boot_bios
    prepare_mll_bios
    prepare_overlay
    ;;

  *)
    echo "Firmware type '$FIRMWARE_TYPE' is not recognized. Cannot continue."
    exit 1
    ;;
esac


cd $SRC_DIR

echo "*** PREPARE ISO END ***"
