#!/bin/sh -e
#
#    u-boot-update-bootmenu - Generate and update Bootmenu for U-Boot
#    Copyright (C) 2012  Pali Rohár <pali.rohar@gmail.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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
#

BOOTMENU_EMMC_DIR="/home/user/MyDocs"
BOOTMENU_CFG_DIR="/etc/bootmenu.d"
BOOTMENU_BOOT_DIR="/boot"
BOOTMENU_OPTBOOT_DIR="/opt/boot"
BOOTMENU_IMG_DIR="bootmenu.img.d"

BOOTMENU_CMD="/var/tmp/bootmenu.cmd"
BOOTMENU_SCR="$BOOTMENU_EMMC_DIR/bootmenu.scr"

BOOTMENU_DEFAULT="/etc/default/bootmenu.item"

INT_CARD="1"
EXT_CARD="0"

HELP="0"

while test "$#" -gt 0; do
	case "$1" in
		-e|--emmc)
			BOOTMENU_EMMC_DIR="$2"
			shift
			;;
		-c|--cfg)
			BOOTMENU_CFG_DIR="$2"
			shift
			;;
		-b|--boot)
			BOOTMENU_BOOT_DIR="$2"
			shift
			;;
		-o|--optboot)
			BOOTMENU_OPTBOOT_DIR="$2"
			shift
			;;
		-d|--default)
			BOOTMENU_DEFAULT="$2"
			shift
			;;
		-h|--help)
			HELP="1"
			;;
		*)
			echo "Unknown option: $2"
			exit 1
			;;
	esac
	shift
done

if test "$HELP" = "1"; then
	echo "This script generates U-Boot Bootmenu script $BOOTMENU_SCR"
	echo "and needed images in $BOOTMENU_EMMC_DIR/$BOOTMENU_IMG_DIR"
	echo "Bootmenu entries are read from config files stored in $BOOTMENU_CFG_DIR"
	echo
	echo "You need to run this script every time you install or uninstall"
	echo "kernel images in $BOOTMENU_BOOT_DIR, $BOOTMENU_OPTBOOT_DIR"
	echo "or when bootmenu config files in $BOOTMENU_CFG_DIR are changed"
	echo
	echo "To run this script, first eMMC partition must be mounted to"
	echo "$BOOTMENU_EMMC_DIR"
	echo
	echo "Options for custom paths:"
	echo "-e|--emmc    - path to mounted eMMC partition"
	echo "-c|--cfg     - path to cfg directory"
	echo "-b|--boot    - path to boot directory"
	echo "-o|--optboot - path to optified boot directory"
	echo "-d|--default - path to default symlink item"
	echo "-h|--help    - show this help"
	echo
	echo "Supported filesystems for first eMMC partition:"
	echo "ext2 ext3 ext4 vfat"
	echo
	echo "Author: Pali Rohár <pali.rohar@gmail.com>"
	echo "License: GNU GPL v3"
	exit 1
fi

if ! mount | grep -q "$BOOTMENU_EMMC_DIR"; then
	echo "First eMMC partition $BOOTMENU_EMMC_DIR is not mounted!"
	exit 1
fi

type=$(mount | grep "$BOOTMENU_EMMC_DIR" | sed 's/.*type //;s/ .*//')
if test "$type" != "ext2" && test "$type" != "ext3" && test "$type" != "ext4" && test "$type" != "vfat"; then
	echo "First eMMC partition $BOOTMENU_EMMC_DIR must be ext2, ext3, ext4 or FAT32!"
	exit 1
fi

rm -f "$BOOTMENU_CMD"
rm -f "$BOOTMENU_CMD.new"
rm -f "$BOOTMENU_CMD.default"
rm -f "$BOOTMENU_SCR"
rm -rf "$BOOTMENU_EMMC_DIR/$BOOTMENU_IMG_DIR"
mkdir -p "$BOOTMENU_EMMC_DIR/$BOOTMENU_IMG_DIR"

count=0
default=""

# check for default entry
if test -f "$BOOTMENU_DEFAULT" && test -h "$BOOTMENU_DEFAULT"; then
	default="$(readlink -f $BOOTMENU_DEFAULT)"
	if ! test -f "$default"; then
		default=""
	else
		echo "Default bootmenu entry is '$default'"
		count=$(($count+1))
	fi
fi

# add entry for attached kernel
system="$(osso-product-info -q OSSO_PRODUCT_RELEASE_NAME 2>/dev/null || true)"
kernel="$(zimage-get-version -f /dev/mtd3ro 2>/dev/null || true)"
if test -z "$system" && test -z "$kernel"; then
	ITEM_NAME="Attached kernel"
elif test -z "$system"; then
	ITEM_NAME="Attached kernel $kernel"
elif test -z "$kernel"; then
	ITEM_NAME="$system with attached kernel (Internal Nand)"
else
	ITEM_NAME="$system with attached kernel $kernel (Internal Nand)"
fi

echo "Adding bootmenu entry for: '$ITEM_NAME'"
printf "setenv bootmenu_$count '$ITEM_NAME=run attachboot';" >> "$BOOTMENU_CMD"
count=$(($count+1))

for file in $BOOTMENU_CFG_DIR/*.item; do

	if ! test -f "$file"; then continue; fi

	echo
	echo "Configuration file: $file"

	ITEM_NAME=""
	ITEM_DEVICE=""
	ITEM_FSTYPE=""
	ITEM_KERNEL=""
	ITEM_INITRD=""
	ITEM_SCRIPT=""
	ITEM_CMDLINE=""
	ITEM_USEATAG="" # Obsoleted
	ITEM_REUSEATAGS="" # Obsoleted
	ITEM_OMAPATAG=""
	. $file
	mmcnum=""
	mmcpart=""
	mmctype=""
	mmckernfile=""
	mmcinitrdfile=""
	mmcscriptfile=""

	if test -z "$ITEM_NAME"; then
		echo "Error: Invalid file - missing name"
		continue
	fi

	if test -z "$ITEM_KERNEL" && test -z "$ITEM_SCRIPT"; then
		echo "Warning: Invalid file - missing kernel or script file - skipping..."
		continue
	fi

	if test "$ITEM_DEVICE" = "nand" || test "$ITEM_DEVICE" = "mtd5" || test "$ITEM_DEVICE" = "rootfs" || test -z "$ITEM_DEVICE"; then

		# kernel and initrd are on rootfs, need to convert to uimage format and store to eMMC

		# kernel and initrd images can be optified, check full path
		kernel_path=""
		initrd_path=""

		if test -f "$BOOTMENU_BOOT_DIR/zImage-$ITEM_KERNEL" || test -f "$BOOTMENU_OPTBOOT_DIR/zImage-$ITEM_KERNEL"; then
			ITEM_KERNEL="zImage-$ITEM_KERNEL"
		fi

		if test -f "$BOOTMENU_BOOT_DIR/$ITEM_KERNEL"; then
			kernel_path="$BOOTMENU_BOOT_DIR/$ITEM_KERNEL"
		elif test -f "$BOOTMENU_OPTBOOT_DIR/$ITEM_KERNEL"; then
			kernel_path="$BOOTMENU_OPTBOOT_DIR/$ITEM_KERNEL"
		else
			echo "Error: Kernel image '$ITEM_KERNEL' was not found in $BOOTMENU_BOOT_DIR or $BOOTMENU_OPTBOOT_DIR" >&2
			continue
		fi

		mmcnum="$INT_CARD"
		mmcpart="1"
		mmctype="fat"
		mmckernfile="$BOOTMENU_IMG_DIR/$ITEM_KERNEL"

		if ! test -z "$(mkimage -l $kernel_path 2>&1 1>/dev/null)"; then

			echo "Generating u-boot image for kernel '$ITEM_KERNEL'..."
			mkimage -A arm -O linux -T kernel -C none -a 80008000 -e 80008000 -n "$ITEM_KERNEL" -d "$kernel_path" "$BOOTMENU_EMMC_DIR/$BOOTMENU_IMG_DIR/$ITEM_KERNEL" >/dev/null

			if test "$?" != "0"; then
				echo "Error: Generating u-boot image for kernel '$ITEM_KERNEL' failed" >&2
				continue
			fi

		fi

		if ! test -z "$ITEM_INITRD"; then

			# we have also initrd

			if test -f "$BOOTMENU_BOOT_DIR/initrd-$ITEM_INITRD" || test -f "$BOOTMENU_OPTBOOT_DIR/initrd-$ITEM_INITRD"; then
				ITEM_INITRD="initrd-$ITEM_INITRD"
			fi

			if test -f "$BOOTMENU_BOOT_DIR/$ITEM_INITRD"; then
				initrd_path="$BOOTMENU_BOOT_DIR/$ITEM_INITRD"
			elif test -f "$BOOTMENU_OPTBOOT_DIR/$ITEM_INITRD"; then
				initrd_path="$BOOTMENU_OPTBOOT_DIR/$ITEM_INITRD"
			else
				echo "Error: Initrd image '$ITEM_INITRD' was not found in $BOOTMENU_BOOT_DIR or $BOOTMENU_OPTBOOT_DIR" >&2
				continue
			fi

			mmcinitrdfile="$BOOTMENU_IMG_DIR/$ITEM_INITRD"

			if ! test -z "$(mkimage -l $initrd_path 2>&1 1>/dev/null)"; then

				echo "Generating u-boot image for initrd '$ITEM_INITRD'..."
				mkimage -A arm -O linux -T ramdisk -C none -a 0 -e 0 -n "$ITEM_INITRD" -d "$initrd_path" "$BOOTMENU_EMMC_DIR/$BOOTMENU_IMG_DIR/$ITEM_INITRD" >/dev/null

				if test "$?" != "0"; then
					echo "Error: Generating u-boot image for initrd '$ITEM_INITRD' failed" >&2
					continue
				fi

			fi

		fi

	else

		# kernel, initrd or script are on eMMC or SD card
		mmcnum="$(echo $ITEM_DEVICE | cut -f1 -dp)"
		mmcpart="$(echo ${ITEM_DEVICE}p1 | cut -f2 -dp)" # HACK: if ITEM_DEVICE has no part first is selected

		if test -z "$mmcnum" || test -z "$mmcpart"; then
			echo "Error: Incorrect device '$ITEM_DEVICE'" >&2
			continue
		fi

		if test "$ITEM_FSTYPE" = "fat" || test "$ITEM_FSTYPE" = "vfat"; then
			mmctype="fat"
		elif test "$ITEM_FSTYPE" = "ext2" || test "$ITEM_FSTYPE" = "ext3"; then
			mmctype="ext2"
		elif test "$ITEM_FSTYPE" = "ext4"; then
			mmctype="ext4"
		else
			echo "Error: Unknown filesystem '$ITEM_FSTYPE'" >&2
			continue
		fi

		if ! test -z "$ITEM_SCRIPT"; then
			mmcscriptfile="$ITEM_SCRIPT"
		elif ! test -z "$ITEM_INITRD"; then
			mmckernfile="$ITEM_KERNEL"
			mmcinitrdfile="$ITEM_INITRD"
		elif ! test -z "$ITEM_KERNEL"; then
			mmckernfile="$ITEM_KERNEL"
		fi

	fi

	if ! test -z "$ITEM_USEATAG"; then
		echo "Warning: Item file has obsolated option ITEM_USEATAG. Change it to ITEM_OMAPATAG"
		ITEM_OMAPATAG=$ITEM_USEATAG
	fi

	if ! test -z "$ITEM_REUSEATAGS"; then
		echo "Warning: Item file has obsolated option ITEM_REUSEATAGS. Change it to ITEM_OMAPATAG"
		ITEM_OMAPATAG=$ITEM_REUSEATAGS
	fi

	rm -f "$BOOTMENU_CMD.new"

	echo "Adding bootmenu entry for: '$ITEM_NAME'"
	printf "setenv bootmenu_$count '$ITEM_NAME=" >> "$BOOTMENU_CMD.new"

	printf "setenv mmcnum $mmcnum;" >> "$BOOTMENU_CMD.new"
	printf "setenv mmcpart $mmcpart;" >> "$BOOTMENU_CMD.new"
	printf "setenv mmctype $mmctype;" >> "$BOOTMENU_CMD.new"

	if test -z "$mmcscriptfile" && ! test -z "$ITEM_CMDLINE"; then
		printf "setenv bootargs $ITEM_CMDLINE;" >> "$BOOTMENU_CMD.new"
	else
		printf "setenv bootargs;" >> "$BOOTMENU_CMD.new"
	fi

	if test -z "$mmcscriptfile" && ! test -z "$ITEM_OMAPATAG"; then
		printf "setenv setup_omap_atag 1;" >> "$BOOTMENU_CMD.new"
	else
		printf "setenv setup_omap_atag;" >> "$BOOTMENU_CMD.new"
	fi

	if ! test -z "$mmcscriptfile"; then
		printf "setenv mmckernfile;" >> "$BOOTMENU_CMD.new"
		printf "setenv mmcinitrdfile;" >> "$BOOTMENU_CMD.new"
		printf "setenv mmcscriptfile $mmcscriptfile;" >> "$BOOTMENU_CMD.new"
		printf "run trymmcscriptboot;" >> "$BOOTMENU_CMD.new"
	elif ! test -z "$mmcinitrdfile"; then
		printf "setenv mmckernfile $mmckernfile;" >> "$BOOTMENU_CMD.new"
		printf "setenv mmcinitrdfile $mmcinitrdfile;" >> "$BOOTMENU_CMD.new"
		printf "setenv mmcscriptfile;" >> "$BOOTMENU_CMD.new"
		printf "run trymmckerninitrdboot;" >> "$BOOTMENU_CMD.new"
	elif ! test -z "$mmckernfile"; then
		printf "setenv mmckernfile $mmckernfile;" >> "$BOOTMENU_CMD.new"
		printf "setenv mmcinitrdfile;" >> "$BOOTMENU_CMD.new"
		printf "setenv mmcscriptfile;" >> "$BOOTMENU_CMD.new"
		printf "run trymmckernboot;" >> "$BOOTMENU_CMD.new"
	else
		echo "Error: Unknown action" >&2
	fi

	printf "';" >> "$BOOTMENU_CMD.new"

	if test "$default" = "$file" && ! test -f "$BOOTMENU_CMD.default"; then
		echo "Configuring this bootmenu entry as default"
		sed 's/^setenv bootmenu_[0-9]*/setenv bootmenu_0/' "$BOOTMENU_CMD.new" > "$BOOTMENU_CMD.default"
	else
		cat "$BOOTMENU_CMD.new" >> "$BOOTMENU_CMD"
		count=$(($count+1))
	fi

	rm -f "$BOOTMENU_CMD.new"

done

# add last entry
printf "setenv bootmenu_$count;" >> "$BOOTMENU_CMD"

# if problems add some default entry
if test -f "$BOOTMENU_DEFAULT" && test -h "$BOOTMENU_DEFAULT" && ! test -f "$BOOTMENU_CMD.default"; then
	echo
	echo "Configuring attached kernel as default bootmenu entry"
	printf "setenv bootmenu_0 'Attached kernel=run attachboot';" > "$BOOTMENU_CMD.default"
fi

if test -f "$BOOTMENU_CMD.default"; then
	cat "$BOOTMENU_CMD.default" "$BOOTMENU_CMD" > "$BOOTMENU_CMD.new"
	mv "$BOOTMENU_CMD.new" "$BOOTMENU_CMD"
fi

echo
echo "Generating u-boot bootmenu script..."
mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n bootmenu -d "$BOOTMENU_CMD" "$BOOTMENU_SCR" >/dev/null

if test "$?" != "0"; then
	echo "Error: Generating u-boot bootmenu script failed" >&2
	exit 1
fi

rm -f "$BOOTMENU_CMD"
rm -f "$BOOTMENU_CMD.new"
rm -f "$BOOTMENU_CMD.default"
