#! /bin/sh
set -e

# Copyright (C) 2010, 2011 Canonical Ltd.
#           (C) 2019 Steve McIntyre <93sam@debian.org>
# 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 an EFI boot image.

if [ -z "$1" ] || [ -z "$2" ]; then
	echo "usage: $0 OUTPUT-DIRECTORY GRUB-PLATFORM EFI-NAME NETBOOT-PREFIX EFI-SIGNED"
	exit 1
fi

outdir="$1"
platform="$2"
efi_name="$3"
netboot_prefix="$4"
efi_signed="$5"

memdisk_img=
workdir=

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

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

memdisk_img="$(mktemp efi-image.XXXXXX)"
workdir="$(mktemp -d efi-image.XXXXXX)"

# Skeleton configuration file which finds the real boot disk.
mkdir -p "$workdir/boot/grub"
cat >"$workdir/boot/grub/grub.cfg" <<EOF
search --file --set=root /.disk/info
set prefix=(\$root)/boot/grub
source \$prefix/$platform/grub.cfg
EOF

mkdir -p "$outdir/boot/grub/$platform"
(for i in /usr/lib/grub/$platform/part_*.mod; do
    i=`echo $i | sed 's?^.*/??g;s?\.mod$??g;'`
	echo "insmod $i"
 done; \
 echo "source /boot/grub/grub.cfg") >"$outdir/boot/grub/$platform/grub.cfg"

if [ $efi_signed = y ]; then
	# Just copy existing shim and Grub binaries into place.
	# First, the binaries we use for disc or CD boot.
	echo "$0: Using pre-signed grub-efi and shim binaries for $efi_name"
	cp /usr/lib/shim/shim$efi_name.efi.signed \
		$workdir/boot$efi_name.efi
	cp /usr/lib/grub/$platform-signed/grub$efi_name.efi.signed \
		$workdir/grub$efi_name.efi

	# Now the binaries for netboot. These are *not* actually used
	# in generating the image here, but will be picked up later on
	# in the d-i build and copied into the output netboot
	# tree. Hence the different output directory.
	cp /usr/lib/shim/shim$efi_name.efi.signed \
		$outdir/bootnet$efi_name.efi
	# Copy the pre-built signed grub-netboot binary that is
	# configured just for d-i, with the right prefix baked in
	# (debian-installer/$arch/grub)". We can't change the prefix,
	# hence the special build. (See #928750)
	cp /usr/lib/grub/$platform-signed/grubnet$efi_name-installer.efi.signed \
		$outdir/grub$efi_name.efi
else
	# Build the core image for disc/CD boot
	echo "$0: Building non-signed grub-efi binaries for $efi_name"
	find "$workdir" -newermt "@$SOURCE_DATE_EPOCH" -print0 \
		| xargs -0r touch --no-dereference --date="@$SOURCE_DATE_EPOCH"
	(cd "$workdir"; find boot -print0 | LC_ALL=C sort -z | tar --null -T - -cf -) >"$memdisk_img"
	grub-mkimage -O "$platform" -m "$memdisk_img" \
		-o "$workdir/boot$efi_name.efi" -p '(memdisk)/boot/grub' \
		search iso9660 configfile normal memdisk tar part_msdos part_gpt fat

	# Now the image for netboot. This is *not* used to generate
	# the image here, but will be picked up later on in the d-i
	# build and copied into the output netboot tree. Hence the
	# different output directory.
	grub-mkimage -O "$platform" \
		-o "$outdir/bootnet$efi_name.efi" -p "$netboot_prefix/grub" \
		search configfile normal efinet tftp net
fi

# Stuff it into a FAT filesystem, making it as small as
# possible. Count up the sizes of all the files we need to fit in there.
#
# 32KiB headroom seems to be enough; (x+31)/32*32 rounds up to multiple of 32.
size=0
for file in $(find $workdir -type f -name *.efi -o -name grub.cfg); do
	this_size=$(( ($(stat -c %s $file) / 1024 + 1 + 32 + 31) / 32 * 32 ))
	size=$(($size + $this_size))
done

# Specify a deterministic volume ID; we use DOS_VOLUME_ID here for
# consistency.
mkfs.msdos --invariant -v -i deb00001 -C "$outdir/efi.img" $size

mmd -i "$outdir/efi.img" ::efi
mmd -i "$outdir/efi.img" ::efi/boot
mcopy -i "$outdir/efi.img" "$workdir/boot$efi_name.efi" \
	"::efi/boot/boot$efi_name.efi"

if [ $efi_signed = y ]; then
	# In this case, also add the grub binary
	mcopy -i "$outdir/efi.img" "$workdir/grub$efi_name.efi" \
		"::efi/boot/grub$efi_name.efi"

	# And finally the skeleton grub.cfg - the signed grub binaries
	# will have prefix set to "/EFI/debian", so put this there to
	# redirect to the correct location.
	mmd -i "$outdir/efi.img" ::efi/debian
	mcopy -i "$outdir/efi.img" "$workdir/boot/grub/grub.cfg" \
		"::efi/debian/grub.cfg"
fi

grub-cpmodules "$outdir" "$platform"

exit 0
