# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2014-2016,2020-2021,2023-2025 Leah Rowe <leah@libreboot.org>
# Copyright (C) 2015 Klemens Nanni <contact@autoboot.org>

set prefix=(memdisk)/boot/grub

insmod at_keyboard
insmod usb_keyboard
insmod nativedisk
insmod xhci
insmod ehci
insmod ohci
insmod uhci
insmod usb
insmod usbms
insmod regexp

terminal_input --append at_keyboard
terminal_input --append usb_keyboard
terminal_output --append cbmemc

# User interface overrides wherever "keystatus" is supported
# Keep SHIFT key pressed before powering on to disable graphics
if keystatus --shift; then
	terminal_output --append vga_text
else
	gfxpayload=keep
	terminal_output --append gfxterm

	for dt in cbfsdisk memdisk; do
		for it in png jpg; do
			if [ -f (${dt})/background.${it} ]; then
				insmod ${it}
				background_image (${dt})/background.${it}
			fi
		done
	done
fi

# Keep CTRL pressed to enable default serial terminal (COM1 or the like)
if keystatus --ctrl; then
	serial
	terminal_input --append serial
	terminal_output --append serial
fi

# Keep ALT pressed to enable spkmodem
if keystatus --alt; then
	terminal_output --append spkmodem
fi


set default="0"
if [ -f (cbfsdisk)/timeout.cfg ]; then
	source (cbfsdisk)/timeout.cfg
else	
	set timeout=8
fi
set grub_scan_disk="nvme ahci ata"
if [ -f (cbfsdisk)/scan.cfg ]; then
	source (cbfsdisk)/scan.cfg
fi

if [ -f (cbfsdisk)/keymap.gkb ]; then
	keymap (cbfsdisk)/keymap.gkb
fi

function really_try_user_config {
	set root="${1}"

	if [ -f /"${2}"/grub.cfg ]; then
		unset superusers
		configfile /"${2}"/grub.cfg
	fi
}

function try_user_config {
	# The @/... entries are for cases where the BTRFS filesystem is being used
	for dir in grub boot/grub @/grub @/boot/grub grub2 boot/grub2 @/grub2 @/boot/grub2 boot @/boot; do
		really_try_user_config "${1}" "${dir}"
	done
	for dir in ubuntu debian redhat; do
		really_try_user_config "${1}" "EFI/${dir}"
	done
}
function search_grub {
	echo -n "Attempting to load grub.cfg from '${1}' devices"
	for i in 0 1 2 3 4 5 6 7 8; do
		for part in 1 2 3 4 5 6 7 8 9 10 11 12; do
			if [ "${1}" != "nvme" ]; then
				try_user_config "(${1}${i},${part})"
			else
				# TODO: do we care about other namesapces
				try_user_config "(nvme${i}n1,${part})"
			fi
		done
		if [ "${1}" != "nvme" ]; then
			# raw devices e.g. (ahci0) instead of (ahci0,1)
			try_user_config "(${1}${i})"
		else
			# TODO: do we care about other namesapces
			try_user_config "(nvme${i}n1)"
		fi
	done
	echo # Insert newline
}

function try_isolinux_config {
	set root="${1}"
	for dir in '' /boot /EFI /@ /@/boot; do
		if [ -f "${dir}"/isolinux/isolinux.cfg ]; then
			syslinux_configfile -i "${dir}"/isolinux/isolinux.cfg
		elif [ -f "${dir}"/syslinux/syslinux.cfg ]; then
			syslinux_configfile -s "${dir}"/syslinux/syslinux.cfg
		elif [ -f "${dir}"/syslinux/extlinux.conf ]; then
			syslinux_configfile -s "${dir}"/syslinux/extlinux.conf
		elif [ -f "${dir}"/extlinux/extlinux.conf ]; then
			syslinux_configfile -s "${dir}"/extlinux/extlinux.conf
		fi
	done
}
function search_isolinux {
	echo "\nAttempting to parse iso/sys/extlinux config from '${1}' devices"
	for i in 0 1 2 3 4 5 6 7 8; do
		for part in 1 2 3 4 5 6 7 8 9 10 11 12; do
			if [ "${1}" != "nvme" ]; then
				try_isolinux_config "(${1}${i},${part})"
			else
				# TODO: see above
				try_isolinux_config "(nvme${i}n1,${part})"
			fi
		done
		if [ "${1}" != "nvme" ]; then
			# raw devices e.g. (usb0) instead of (usb0,1)
			try_isolinux_config "(${1}${i})"
		else
			# TODO: do we care about other namesapces
			try_isolinux_config "(nvme${i}n1)"
		fi
	done
	echo # Insert newline
}
function try_bootcfg {
	try_user_config "${1}"
	try_isolinux_config "${1}"
}
function search_bootcfg {
	search_grub "${1}"
	search_isolinux "${1}"
}
menuentry 'Load Operating System (incl. fully encrypted disks)  [o]' --hotkey='o' {

	for grub_disk in ${grub_scan_disk}; do
		search_bootcfg ${grub_disk}
	done

	# grub device enumeration is very slow, so checks are hardcoded

	raidvol="md/0 md/1 md/2 md/3 md/4 md/5 md/6 md/7 md/8 md/9"

	# in practise, doing multiple redundant checks is perfectly fast
	# TODO: optimize grub itself, and use */? here for everything

	for vol in ${raidvol} ; do
		try_bootcfg "${vol}"
	done

	unset bootdev
	for grub_disk in ${grub_scan_disk}; do
		for i in 0 1 2 3 4 5 6 7 8; do
			for part in 1 2 3 4 5 6 7 8 9 10 11 12; do
				if [ "${grub_disk}" = "ahci" ]; then
					bootdev="${bootdev} (ahci${i},${part})"
				elif [ "${grub_disk}" = "ata" ]; then
					bootdev="${bootdev} (ata${i},${part})"
				elif [ "${grub_disk}" = "nvme" ]; then
					# TODO: do we care about other namesapces
					bootdev="${bootdev} (nvme${i}n1,${part})"
				fi
			done
		done
	done

	set pager=0
	echo -n "Attempting to unlock encrypted volumes"
	for dev in ${bootdev} ${raidvol}; do
		if cryptomount "${dev}" ; then break ; fi
	done
	set pager=1
	echo

	search_bootcfg crypto

	lvmvol=""

	# after cryptomount, lvm volumes might be available
	# using * is slow on some machines, but we use it here,
	# just once. in so doing, we find every lvm volume
	for vol in (*); do
		if regexp ^\\(lvm/ $vol; then
			lvmvol="${lvmvol} ${vol}"
			try_bootcfg "${vol}"
		fi
	done

	# user might have put luks inside lvm
	set pager=0
	echo "Attempting to unlock encrypted LVMs"
	for vol in ${lvmvol}; do
		cryptomount "$vol"
	done
	set pager=1
	echo

	search_bootcfg crypto

	true # Prevent pager requiring to accept each line instead of whole screen
}

menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on USB  [s]' --hotkey='s' {
	search_bootcfg usb
}
menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on AHCI  [a]' --hotkey='a' {
	search_bootcfg ahci
}
menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on ATA/IDE  [d]' --hotkey='d' {
	search_bootcfg ata
}
menuentry 'Search for GRUB/SYSLINUX/EXTLINUX/ISOLINUX on NVMe  [e]' --hotkey='e' {
	search_bootcfg nvme
}
if [ -f (cbfsdisk)/grub.cfg ]; then
menuentry 'Load configuration (grub.cfg) in CBFS  [t]' --hotkey='t' {
	set root='(cbfsdisk)'
	if [ -f /grub.cfg ]; then
		configfile /grub.cfg
	fi
}
fi
if [ -f (cbfsdisk)/grubtest.cfg ]; then
menuentry 'Load test configuration (grubtest.cfg) in CBFS  [t]' --hotkey='t' {
	set root='(cbfsdisk)'
	if [ -f /grubtest.cfg ]; then
		configfile /grubtest.cfg
	fi
}
fi
if [ -f (cbfsdisk)/u-boot ]; then
menuentry 'U-Boot i386 payload (experimental) [u]' --hotkey='u' {
	set root='cbfsdisk'
	chainloader /u-boot
}
fi
if [ -f (cbfsdisk)/seabios.elf ]; then
if [ -f (cbfsdisk)/img/u-boot ]; then
menuentry 'Load SeaBIOS (U-Boot UEFI available in the ESC menu) [b]' --hotkey='b' {
	set root='cbfsdisk'
	chainloader /seabios.elf
}
else
menuentry 'Load SeaBIOS [b]' --hotkey='b' {
	set root='cbfsdisk'
	chainloader /seabios.elf
}
fi
fi
if [ -f (cbfsdisk)/img/grub2 ]; then
if [ -f (cbfsdisk)/img/u-boot ]; then
menuentry 'Return to SeaBIOS (U-Boot UEFI available in the ESC menu) [b]' --hotkey='b' {
	set root='cbfsdisk'
	chainloader /fallback/payload
}
else
menuentry 'Return to SeaBIOS [b]' --hotkey='b' {
	set root='cbfsdisk'
	chainloader /fallback/payload
}
fi
fi
menuentry 'Poweroff  [p]' --hotkey='p' {
	halt
}
menuentry 'Reboot  [r]' --hotkey='r' {
	reboot
}
if [ -f (cbfsdisk)/img/memtest ]; then
menuentry 'Load MemTest86+  [m]' --hotkey='m' {
	set root='cbfsdisk'
	chainloader /img/memtest
}
fi

submenu 'Other  [z]' --hotkey='z' {
	menuentry 'Enable default serial terminal  [s]' --hotkey='s'  {
		serial
		terminal_input --append serial
		terminal_output --append serial
	}

	menuentry 'Disable default serial terminal' {
		terminal_input --remove serial
		terminal_output --remove serial
	}

	menuentry 'Enable gfxterm' {
		terminal_output --append gfxterm
		terminal_output --remove vga_text
	}
	menuentry 'Disable gfxterm  [g]' --hotkey='g'  {
		terminal_output --remove gfxterm
		terminal_output --append vga_text
	}

	menuentry 'Enable spkmodem  [a]' --hotkey='a' {
		terminal_output --append spkmodem
	}

	menuentry 'Disable spkmodem  [z]' --hotkey='z' {
		terminal_output --remove spkmodem
	}
}
