# SPDX-License-Identifier: GPL-3.0-only
# Copyright (c) 2022 Caleb La Grange <thonkpeasant@protonmail.com>
# Copyright (c) 2022 Ferass El Hafidi <vitali64pmemail@protonmail.com>
# Copyright (c) 2023-2025 Leah Rowe <leah@libreboot.org>

cbcfgsdir="config/coreboot"
hashfiles="vendorhashes blobhashes" # used to detect and error out
			# if running on an archive that needs vendor files
dontflash="!!! AN ERROR OCCURED! Do NOT flash these images !!!"
tmpromdel="$PWD/tmp/DO_NOT_FLASH"

cv="CONFIG_GBE_BIN_PATH CONFIG_IFD_BIN_PATH"

eval "`setvars "" tree new_mac archive boarddir rom cbdir xchanged \
    tmpromdir IFD_platform ifdprefix xromsize $cv`"

fail_inject()
{
	[ -L "$tmpromdel" ] || [ ! -d "$tmpromdel" ] || \
	    rm -Rf "$tmpromdel" || :
	printf "\n\n%s\n\n" "$dontflash" 1>&2
	printf "WARNING: File '%s' was NOT modified.\n\n" "$archive" 1>&2
	fail "$1"
}

xbmk_inject()
{
	_olderr="$err"
	err="fail_inject"
	remkdir "$tmpromdel"

	set +u +e; [ $# -lt 1 ] && $err "No options specified. - $dontflash"
	eval "`setvars "" new_mac xchanged`"

	# randomise the MAC address by default
	# TODO: support setting CBFS MAC address for GA-G41M-ES2L
	new_mac="??:??:??:??:??:??"

	archive="$1";
	[ $# -gt 1 ] && case "$2" in
	setmac)
		[ $# -gt 2 ] && new_mac="$3" && \
		    [ -z "$new_mac" ] && $err \
		    "You set an empty MAC address string" ;;
	*) $err "Unrecognised inject mode: '$2'"
	esac

	check_release "$archive" || \
	    $err "You must run this script on a release archive. - $dontflash"

	[ "$new_mac" = "restore" ] && \
	    printf "Restoring default GbE for '$archive', board '$board'\n"

	if readcfg && readkconfig; then
		patch_release_roms
	fi

	[ "$xchanged" != "y" ] && \
		printf "\nRelease archive '%s' was *NOT* modified.\n" \
		    "$archive" 1>&2
	[ "$xchanged" = "y" ] && \
		printf "\nRelease archive '%s' successfully patched.\n" \
		    "$archive" && \
		printf "You may now extract '%s' and flash images from it.\n" \
		    "$archive"

	err="$_olderr"
	return 0
}

check_release()
{
	[ -L "$archive" ] && \
	    $err "'$archive' is a symlink, not a file - $dontflash"
	[ -f "$archive" ] || return 1
	archivename="`basename "$archive"`"
	[ -z "$archivename" ] && \
	    $err "Cannot determine archive file name - $dontflash"

	case "$archivename" in
	*_src.tar.xz)
		$err "'$archive' is a src archive, silly!" ;;
	grub_*|seagrub_*|custom_*|seauboot_*|seabios_withgrub_*)
		return 1 ;;
	*.tar.xz) _stripped_prefix="${archivename#*_}"
		board="${_stripped_prefix%.tar.xz}" ;;
	*) $err "'$archive': could not detect board type - $dontflash"
	esac; :
}

readcfg()
{
	if [ "$board" = "serprog_rp2040" ] || \
	    [ "$board" = "serprog_stm32" ] || \
	    [ "$board" = "serprog_pico" ]; then
		printf "'%s' is a serprog firmware archive.\n" "$archive" 1>&2
		return 1
	fi
	boarddir="$cbcfgsdir/$board"

	eval "`setcfg "$boarddir/target.cfg"`"
	chkvars tree
	x_ ./mk -d coreboot "$tree"

	cbdir="src/coreboot/$tree"
	cbfstool="elf/cbfstool/$tree/cbfstool"
	rmodtool="elf/cbfstool/$tree/rmodtool"
	cbfstool="elf/cbfstool/$tree/cbfstool"
	ifdtool="elf/ifdtool/$tree/ifdtool"
	[ -n "$IFD_platform" ] && ifdprefix="-p $IFD_platform"; :
}

readkconfig()
{
	check_defconfig "$boarddir" 1>"$TMPDIR/vendorcfg.list" && return 1

	rm -f "$TMPDIR/tmpcbcfg" || $err "!rm $TMPDIR/tmpcbcfg - $dontflash"
	while read -r cbcfgfile; do
		for cbc in $cv; do
			rm -f "$TMPDIR/tmpcbcfg2" || \
			    $err "!rm $TMPDIR/tmpcbcfg2 - $dontflash"
			grep "$cbc" "$cbcfgfile" 1>"$TMPDIR/tmpcbcfg2" \
			    2>/dev/null || :
			[ -f "$TMPDIR/tmpcbcfg2" ] || continue
			cat "$TMPDIR/tmpcbcfg2" >> "$TMPDIR/tmpcbcfg" || \
			    $err "!cat $TMPDIR/tmpcbcfg2 - $dontflash"
		done
	done < "$TMPDIR/vendorcfg.list"

	eval "`setcfg "$TMPDIR/tmpcbcfg"`"; :
}

patch_release_roms()
{
	tmpromdir="tmp/DO_NOT_FLASH/bin/$board"
	remkdir "${tmpromdir%"/bin/$board"}"
	tar -xf "$archive" -C "${tmpromdir%"/bin/$board"}" || \
		$err "Can't extract '$archive'"

	for _hashes in $hashfiles; do
		[ -L "$tmpromdir/$_hashes" ] && \
		    $err "'$archive' -> the hashfile is a symlink. $dontflash"
		[ -f "$tmpromdir/$_hashes" ] && $err \
		    "'$archive': vendorfile insertion unsupported; $dontflash"
	done

	if [ -n "$new_mac" ]; then
		if ! modify_mac_addresses; then
			printf "\nNo GbE region defined for '%s'\n" "$board" \
			    1>&2
			printf "Therefore, changing the MAC is impossible.\n" \
			    1>&2
			printf "This board probably lacks Intel ethernet.\n" \
			    1>&2
			printf "(or it's pre-IFD Intel with Intel GbE NIC)\n" \
			    1>&2
		fi
	fi

	[ "$xchanged" = "y" ] || rm -Rf "$tmpromdel" || :
	[ "$xchanged" = "y" ] || return 0
	(
		cd "${tmpromdir%"/bin/$board"}" || \
		    $err "Can't cd '${tmpromdir%"/bin/$board"}'; $dontflash"
		# ../../ is the root of lbmk
		mkrom_tarball "bin/$board"
	) || $err "Cannot re-generate '$archive' - $dontflash"

	mv "${tmpromdir%"/bin/$board"}/bin/${relname}_${board}.tar.xz" \
	    "$archive" || \
	    $err "'$archive' -> Cannot overwrite - $dontflash"; :
}

modify_mac_addresses()
{
	[ "$nukemode" = "nuke" ] && \
	    $err "Cannot modify MAC addresses while nuking vendor files"

	# chkvars CONFIG_GBE_BIN_PATH
	[ -n "$CONFIG_GBE_BIN_PATH" ] || return 1
	e "${CONFIG_GBE_BIN_PATH##*../}" f n && $err "missing gbe file"

	[ "$new_mac" != "restore" ] && \
	    x_ make -C util/nvmutil

	x_ mkdir -p tmp
	[ -L "tmp/gbe" ] && $err "tmp/gbe exists but is a symlink"
	[ -d "tmp/gbe" ] && $err "tmp/gbe exists but is a directory"
	if [ -e "tmp/gbe" ]; then
		[ -f "tmp/gbe" ] || $err "tmp/gbe exists and is not a file"
	fi
	x_ cp "${CONFIG_GBE_BIN_PATH##*../}" "tmp/gbe"

	[ "$new_mac" != "restore" ] && \
	    x_ "util/nvmutil/nvm" "tmp/gbe" setmac "$new_mac"

	find "$tmpromdir" -maxdepth 1 -type f -name "*.rom" > "tmp/rom.list" \
	    || $err "'$archive' -> Can't make tmp/rom.list - $dontflash"

	while read -r _xrom; do
		[ -L "$_xrom" ] && continue
		[ -f "$_xrom" ] || continue
		"$ifdtool" $ifdprefix -i GbE:"tmp/gbe" "$_xrom" -O \
		    "$_xrom" || $err "'$_xrom': Can't insert new GbE file"
		xchanged="y"
	done < "tmp/rom.list"
	printf "\nThe following GbE NVM words were written in '%s':\n" \
	    "$archive"
	x_ util/nvmutil/nvm tmp/gbe dump | grep -v "bytes read from file" || :

	[ "$new_mac" = "restore" ] && \
	    printf "\nNOTE: User specified setmac 'restore' argument.\n" && \
	    printf "Default GbE file '%s' written without running nvmutil.\n" \
	    "${CONFIG_GBE_BIN_PATH##*../}"; :
}
