#! /bin/sh
set -e

# Copyright (C) 2010, 2011, 2012 Canonical Ltd.
# Author: Colin Watson <cjwatson@ubuntu.com>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

# Make EFI boot images for signing.

if [ $# -lt 7 ]; then
	echo "usage: $0 GRUB-MKIMAGE GRUB-CORE OUTPUT-DIRECTORY DEB-ARCH PLATFORM EFI-NAME SBAT-CSV [EFI-VENDOR]"
fi

grub_mkimage="$1"
grub_core="$2"
outdir="$3"
deb_arch="$4"
platform="$5"
efi_name="$6"
sbat_csv="$7"
efi_vendor="${8:-$(dpkg-vendor --query vendor | tr '[:upper:]' '[:lower:]')}"

# mkfs.msdos may not be on the default PATH.
export PATH="$PATH:/sbin:/usr/sbin"

workdir=

cleanup () {
	[ -z "$workdir" ] || rm -rf "$workdir"
}
trap cleanup EXIT HUP INT QUIT TERM

# Return the number of 1KiB blocks needed to store a file in the
# memdisk, with an extra block for the directory entry
rounded_size () {
    BLOCK_SIZE=1024
    size=$(stat -c %s $1)
    rounded=$(( ( ($size + $BLOCK_SIZE - 1) / $BLOCK_SIZE) + 1))
    echo "Adding $rounded blocks to memdisk for $1" >&2
    echo $rounded
}

rm -rf "$outdir"
mkdir -p "$outdir"

workdir="$(mktemp -d build-efi-images.XXXXXX)"

# GRUB's rescue parser doesn't understand 'if'.
echo 'normal (memdisk)/grub.cfg' >"$workdir/grub-bootstrap.cfg"

# Skeleton configuration file which finds the real boot disk.
cat >"$workdir/grub.cfg" <<EOF
if [ -z "\$prefix" -o ! -e "\$prefix" ]; then
	if ! search --file --set=root /.disk/info; then
		search --file --set=root /.disk/mini-info
	fi
	set prefix=(\$root)/boot/grub
fi
if [ -e \$prefix/$platform/grub.cfg ]; then
	source \$prefix/$platform/grub.cfg
elif [ -e \$prefix/grub.cfg ]; then
	source \$prefix/grub.cfg
else
	source \$cmdpath/grub.cfg
fi
EOF

cat >"$workdir/grub-netboot.cfg" <<EOF
if [ -e \$prefix/$platform/grub.cfg ]; then
	source \$prefix/$platform/grub.cfg
else
	source \$prefix/grub.cfg
fi
EOF

# Calculate the size of the embedded filesystem needed
FATFS_SIZE=64 # 64KiB for the embedded grub.cfg and the metadata

# Only copy in unicode.pf2 for now, we don't want the binary images
# too large
FONTS=$grub_core/../unicode.pf2
for FONT in $FONTS; do
    FATFS_SIZE=$(($FATFS_SIZE + $(rounded_size $FONT)))
done

mkfs.msdos -C "$workdir/memdisk.fat" $FATFS_SIZE
mcopy -i "$workdir/memdisk.fat" "$workdir/grub.cfg" ::grub.cfg
mmd -i "$workdir/memdisk.fat" ::fonts
for FONT in $FONTS; do
    mcopy -i "$workdir/memdisk.fat" "$FONT" ::fonts/$(basename $FONT)
done
# Let's show what's here so we have it in build logs
mdir -/ -i "$workdir/memdisk.fat"

mkfs.msdos -C "$workdir/memdisk-netboot.fat" $FATFS_SIZE
mcopy -i "$workdir/memdisk-netboot.fat" "$workdir/grub-netboot.cfg" ::grub.cfg
mmd -i "$workdir/memdisk-netboot.fat" ::fonts
for FONT in $FONTS; do
    mcopy -i "$workdir/memdisk-netboot.fat" "$FONT" ::fonts/$(basename $FONT)
done
# Let's show what's here so we have it in build logs
mdir -/ -i "$workdir/memdisk.fat"

CD_MODULES="
	all_video
	boot
	btrfs
	cat
	chain
	configfile
	echo
	efifwsetup
	efinet
	ext2
	fat
	font
	f2fs
	gettext
	gfxmenu
	gfxterm
	gfxterm_background
	gzio
	halt
	help
	hfsplus
	iso9660
	jfs
	jpeg
	keystatus
	loadenv
	loopback
	linux
	ls
	lsefi
	lsefimmap
	lsefisystab
	lssal
	memdisk
	minicmd
	normal
	ntfs
	part_apple
	part_msdos
	part_gpt
	password_pbkdf2
	png
	probe
	reboot
	regexp
	search
	search_fs_uuid
	search_fs_file
	search_label
	serial
	sleep
        smbios
	squash4
	test
	true
	video
	xfs
	zfs
	zfscrypt
	zfsinfo
	"

# Platform-specific modules
case $platform in
    x86_64-efi|i386-efi)
	CD_MODULES="$CD_MODULES
	cpuid
	linuxefi
	play
	tpm
	"
	;;
esac

GRUB_MODULES="$CD_MODULES
	cryptodisk
	gcry_arcfour
	gcry_blowfish
	gcry_camellia
	gcry_cast5
	gcry_crc
	gcry_des
	gcry_dsa
	gcry_idea
	gcry_md4
	gcry_md5
	gcry_rfc2268
	gcry_rijndael
	gcry_rmd160
	gcry_rsa
	gcry_seed
	gcry_serpent
	gcry_sha1
	gcry_sha256
	gcry_sha512
	gcry_tiger
	gcry_twofish
	gcry_whirlpool
	luks
	luks2
	lvm
	mdraid09
	mdraid1x
	raid5rec
	raid6rec
	"
NET_MODULES="$CD_MODULES
	tftp
	"

# CD boot image
echo "Including modules $CD_MODULES in $outdir/gcd$efi_name.efi"
"$grub_mkimage" \
    -O "$platform" \
    -o "$outdir/gcd$efi_name.efi" \
    -c "$workdir/grub-bootstrap.cfg" \
    -d "$grub_core" \
    -m "$workdir/memdisk.fat" \
    -p /boot/grub \
    --sbat "$sbat_csv" \
    $CD_MODULES

# Normal disk boot image
echo "Including modules $GRUB_MODULES in $outdir/grub$efi_name.efi"
"$grub_mkimage" \
    -O "$platform" \
    -o "$outdir/grub$efi_name.efi" \
    -c "$workdir/grub-bootstrap.cfg" \
    -d "$grub_core" \
    -m "$workdir/memdisk.fat" \
    -p "/EFI/$efi_vendor" \
    --sbat "$sbat_csv" \
    $GRUB_MODULES

# Normal network boot image
echo "Including modules $NET_MODULES in $outdir/grubnet$efi_name.efi"
"$grub_mkimage" \
    -O "$platform" \
    -o "$outdir/grubnet$efi_name.efi" \
    -c "$workdir/grub-bootstrap.cfg" \
    -d "$grub_core" \
    -m "$workdir/memdisk-netboot.fat" \
    -p /grub \
    --sbat "$sbat_csv" \
    $NET_MODULES

# Special network boot image for d-i to use. Just the same as the
# normal network boot image, but with a different value baked in for
# the prefix setting
echo "Including modules $NET_MODULES in $outdir/grubnet$efi_name-installer.efi"
"$grub_mkimage" \
    -O "$platform" \
    -o "$outdir/grubnet$efi_name-installer.efi" \
    -c "$workdir/grub-bootstrap.cfg" \
    -d "$grub_core" \
    -m "$workdir/memdisk-netboot.fat" \
    -p "/${efi_vendor}-installer/$deb_arch/grub" \
    --sbat "$sbat_csv" \
    $NET_MODULES

exit 0
