#!/bin/sh -efu
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2020-2023  Alexey Gladkov <gladkov.alexey@gmail.com>

# shellcheck source=tools/mki-chroot-sh-functions
. /.host/mki-chroot-sh-functions

PROG='mki-copy-efiboot-chrooted'

# External globals
verbose="${verbose-}"
EFI_CERT="${EFI_CERT-}"
EFI_FILES_COPY="${EFI_FILES_COPY-}"
EFI_FILES_REPLACE="${EFI_FILES_REPLACE-}"
UUID_ISO="${UUID_ISO:-}"

cd /.image

img='.efiboot.img'
boot="EFI/BOOT"
boot_grub="boot/grub"
cert="EFI/enroll"
efi_bindir='/usr/lib64/efi'
grub_modules='/usr/lib64/grub'

if [ -n "$UUID_ISO" ]; then
	touch "$UUID_ISO"
fi

mkdir $verbose -p -- "$boot"

copy_cert()
{
	[ -n "$EFI_CERT" ] ||
		return 0

	local keyfile="/etc/pki/uefi/$EFI_CERT.cer"

	[ -s "$keyfile" ] ||
		fatal "invalid $keyfile"

	mkdir $verbose -p "$cert"
	cp $verbose -pLft "$cert" -- "$keyfile"
}

copy_shim()
{
	copy_cert

	local shim_max_version shim_dir_prefix shim_arch
	shim_max_version=
	shim_dir_prefix="/usr/share/shim"

	[ ! -d "$shim_dir_prefix" ] ||
		shim_max_version="$(find "$shim_dir_prefix" -maxdepth 1 -mindepth 1 -type d | sed 's|\./||' | sort -n -r | head -1)"

	for shim_arch in ia32 x64; do
		SHIM_ARCH="$(echo "$shim_arch" | tr '[:lower:]' '[:upper:]')"
		if [ -s "$efi_bindir/shim${shim_arch}.efi" ]; then
			cp $verbose -pLf "$efi_bindir/shim${shim_arch}.efi" "$boot/BOOT${SHIM_ARCH}.EFI"
			cp $verbose -pLf "$efi_bindir/mm${shim_arch}.efi" "$boot"
		elif [ -n "$shim_max_version" ] && [ -s "$shim_max_version/${shim_arch}/shim${shim_arch}.efi" ]; then
			# use unsigned shim if signed is not available
			cp $verbose -pLf "$shim_max_version/${shim_arch}/shim${shim_arch}.efi" "$boot/BOOT${SHIM_ARCH}.EFI"
			cp $verbose -pLf "$shim_max_version/${shim_arch}/mm${shim_arch}.efi" "$boot"
			cp $verbose -pLf "$shim_max_version/${shim_arch}/shim${shim_arch}.hash" "$boot/BOOT${SHIM_ARCH}.hash"
			# fix name of shim in hash
			sed -i "s/shim${shim_arch}.efi/BOOT${SHIM_ARCH}.EFI/" "$boot/BOOT${SHIM_ARCH}.hash"
		fi
	done
}

kimage=
copy_kernel()
{
	if [ -s "$boot/vmlinuz" ] || [ -s "$boot/full.cz" ]; then
		return 0
	fi

	if [ -f syslinux/alt0/vmlinuz ]; then
		cp $verbose -lpLft "$boot" -- \
			syslinux/alt0/vmlinuz \
			syslinux/alt0/full.cz
		return 0
	fi

	if [ -n "$kimage" ]; then
		cp $verbose -af "$kimage" "$boot"/vmlinuz
		[ ! -f /boot/full.cz ] ||
			cp $verbose -af /boot/full.cz "$boot"/full.cz
	fi
}

copy_grub_primary() {
	local efi_arch
	for efi_arch in x64 ia32 aa64 riscv64 loongarch64; do
		[ -f "$efi_bindir/grub${efi_arch}.efi" ] || continue
		if [ "${efi_arch}" = x64 ] && [ -d "$grub_modules/arm64-efi" ]; then
			# for p9 branch
			EFI_ARCH=aa64
		else
			EFI_ARCH="${efi_arch}"
		fi
		if [ -n "$EFI_CERT" ]; then
			cp $verbose -pLf "$efi_bindir/grub${efi_arch}.efi" "$boot/grub${EFI_ARCH}.efi"
		else
			EFI_ARCH="$(echo "$EFI_ARCH" | tr '[:lower:]' '[:upper:]')"
			cp $verbose -pLf "$efi_bindir/grub${efi_arch}.efi" "$boot/BOOT${EFI_ARCH}.EFI"
		fi
	done
}

copy_grub_secondary() {
	[ -d "$boot_grub/fonts" ] ||
		mkdir $verbose -p "$boot_grub/fonts"

	local grub_arch

	for grub_arch in x86_64-efi i386-efi arm64-efi riscv64-efi loongarch64-efi; do
		[ -d "$grub_modules/$grub_arch" ] ||
			continue

		mkdir -p -- "$boot_grub/$grub_arch"

		find "$grub_modules/$grub_arch" -type f \! -name '*.module' \
			-exec cp $verbose -arf -t "$boot_grub/$grub_arch" -- '{}' '+'
	done

	cp $verbose -Lf /boot/grub/fonts/unicode.pf2 "$boot_grub/fonts"
	cp $verbose -Lf /boot/grub/unifont.pf2 "$boot_grub"

	[ ! -d /boot/grub/themes ] || [ -d "$boot_grub/themes" ] ||
		cp $verbose -arf /boot/grub/themes "$boot_grub"

	if [ ! -e "$boot_grub/locale" ]; then
		mkdir "$boot_grub/locale"

		find /usr/share/locale/ -name grub.mo -print |
		while read -r i; do
			lct="$(printf '%s\n' "$i" | cut -d/ -f5)"
			cp $verbose -arf "$i" "$boot_grub/locale/$lct.mo"
		done
	fi
}

add_grub_cfg() {
	local main_grub_cfg="$boot_grub/grub.cfg"
	local interim_grub_cfg="$boot/grub.cfg"

	# If both the main and intermediate grub.cfg files already exist,
	# no action is required.
	if [ -f "$interim_grub_cfg" ] && [ -f "$main_grub_cfg" ]; then
		return 0
	fi

	# If the intermediate grub.cfg exists, but the main grub.cfg doesn't
	# use it as the main one.
	if [ -f "$interim_grub_cfg" ] && [ ! -f "$main_grub_cfg" ]; then
		mv -T "$interim_grub_cfg" "$main_grub_cfg"
	fi

	[ -f "$main_grub_cfg" ] ||
		fatal "GRUB config $main_grub_cfg doesn't exist!"

	# The intermediate grub.cfg is essential to minimize the size
	# of the efiboot.img.
	cat >"$interim_grub_cfg" <<-GRUB_EOF
		search --file --set=root /${UUID_ISO:-.disk/info}
		set prefix=(\$root)/$boot_grub
		source \$prefix/grub.cfg
	GRUB_EOF
}

write_efiboot_img () {
	imgsize="$(( $(du -lsB32k EFI/ | cut -f1) + 10 ))"

	# additional files or directories for efiboot.img
	for efi_file in $EFI_FILES_COPY $EFI_FILES_REPLACE; do
		[ -n "${efi_file##*/*}" ] ||
			fatal "EFI_FILES_COPY or EFI_FILES_REPLACE contains '/': $efi_file"
		[ -e "$efi_file" ] || fatal "$efi_file does not exist"
		imgsize="$(( imgsize + $(du -lsB32k "$efi_file" | cut -f1) ))"
	done

	dd if=/dev/zero of="$img" bs=32k count="$imgsize"

	# dosfstools-4.0 has dropped those ancient symlinks, *sigh*
	mkfs=
	for bin in mkfs.fat mkfs.vfat; do
		if $bin --help >/dev/null 2>&1; then
			mkfs="$bin"
			break
		fi
	done

	[ -n "$mkfs" ] ||
		fatal "Not found: mkfs.fat or mkfs.vfat"

	"$mkfs" $verbose -n "El Torito" "$img"

	# mtools insists that total number of sectors
	# is a multiple of sectors per track (the infamous 63),
	# and dosfstools-4.0 doesn't follow that anymore
	echo "mtools_skip_check=1" >~/.mtoolsrc

	add_grub_cfg

	mcopy $verbose -i "$img" -s EFI \
		$EFI_FILES_COPY $EFI_FILES_REPLACE ::

	# cleanup additional files for efiboot.img only
	[ -z "$EFI_FILES_REPLACE" ] || rm -r $EFI_FILES_REPLACE

	mv "$img" EFI/
}

[ -z "$EFI_CERT" ] ||
	copy_shim

copy_grub_primary

# The find_vmlinuz() function defines the kimage variable, which is
# subsequently used by the copy_kernel() function.
find_vmlinuz kimage

[ -d "$boot_grub" ] || mkdir -p "$boot_grub"
[ -f "EFI/$img" ] || write_efiboot_img
copy_kernel
copy_grub_secondary

# use ISO9660 hardlinks support if possible
hardlink $verbose -c EFI/
