#!/bin/bash

# Copyright (c) 2011-2013, NVIDIA CORPORATION.  All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#  * Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#  * Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#  * Neither the name of NVIDIA CORPORATION nor the names of its
#    contributors may be used to endorse or promote products derived
#    from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


#
# flash.sh: Flash the target board.
#	    flash.sh performs the best in LDK release environment.
#
# Usage: Place the board in recovery mode and run:
#
#	flash.sh [options] <target board> <root_device>
#
#	for more detail enter 'flash.sh -h'
#
# Examples:
# ./flash.sh <target board> mmcblk0p1			- boot <target board> from internal emmc
# ./flash.sh <target board> mmcblk1p1			- boot <target board> from SDCARD
# ./flash.sh <target board> sda1			- boot <target board> from USB device
# ./flash.sh -N <IP addr>:/nfsroot <target board> eth0	- boot <target board> from NFS
# ./flash.sh -k 6 <target board> mmcblk1p1		- update <target board> kernel
#							- in partition 6.
#
# Optional Environment Variables:
# ROOTFS_SIZE ------------ Linux RootFS size (internal emmc/nand only).
# ODMDATA ---------------- Odmdata to be used.
# FLASHAPP --------------- Flash application running in host machine.
# FLASHER ---------------- Flash server running in target machine.
# BOOTLOADER ------------- Bootloader binary to be flashed
# BCTFILE ---------------- Boot control table configuration file to be used.
# CFGFILE ---------------- Partition table configuration file to be used.
# KERNEL_IMAGE ----------- Linux kernel zImage file to be flashed.
# ROOTFS ----------------- Linux RootFS directory name.
# NFSROOT ---------------- NFSROOT i.e. <my IP addr>:/exported/rootfs_dir
# NFSARGS ---------------- Static Network assignments.
#			   <C-ipa>:<S-ipa>:<G-ipa>:<netmask>
# KERNEL_INITRD ---------- Initrd file to be flashed.
# CMDLINE ---------------- Target cmdline. See help for more information.
# UBOOT_TEXT_BASE -------- U-boot Image Load Address.
# UIMAGE_LABEL ----------- Kernel version for U-boot image header.
# UIMAGE_NAME ------------ uImage file name.
# UBOOTSCRIPT ------------ U-boot HUSH boot script file.
# BOOTPART_SIZE ---------- Total eMMC HW boot partition size.
# BOOTPART_LIMIT --------- GPT data limit. (== Max BCT size + PPT size)
# DTBFILE ---------------- Device Tree file to be used.
#
LDK_DIR=$(cd `dirname $0` && pwd)
target_board=
target_sku=
target_rootdev=
target_partid=0
# Reserved Fixed partitions:
#	2 - BCT (absolute)
#	3 - PT  (strongly recommended)
#	4 - EBT (strongly recommended)
DFLT_FLASHAPPNAME="nvflash"
DFLT_FLASHERNAME="fastboot.bin"
DFLT_BOOTLOADERNAME="fastboot.bin"
MIN_KERN_PARTID=6;
rootdev_type="external";
rootfs_size=${ROOTFS_SIZE};
rootfs_type="ext3";
odmdata=${ODMDATA};
flashapp=${FLASHAPP};
flasher=${FLASHER};
bootloader=${BOOTLOADER};
ubootscript=${UBOOTSCRIPT}
bootdelay=2;
bctfile=${BCTFILE};
cfgfile=${CFGFILE};
kernel_image=${KERNEL_IMAGE-${LDK_DIR}/kernel/zImage};
rootfs=${ROOTFS-${LDK_DIR}/rootfs};
nfsroot=${NFSROOT};
nfsargs=${NFSARGS};
kernel_initrd=${KERNEL_INITRD};
cmdline=
usercmdline=${CMDLINE};
ignorefastbootcmdline="false";
zflag="false";
uboot_text_base=;
uimage_label=;
uimage_name=${UIMAGE_NAME-vmlinux.uimg};
reuse_systemimg="false";
bootpart_size=${BOOTPART_SIZE};
bootpart_limit=${BOOTPART_LIMIT};
dtbfile=${DTBFILE};
dtbfilename=
emmc_size=${EMMC_SIZE};
itsfile=${ITSFILE};
itsfilename=
dtc_tool_dir=${LDK_DIR}/kernel
# put our DTC tool path first in PATH
export PATH="${dtc_tool_dir}:${PATH}"

pr_conf()
{
	echo "target_board=${target_board}";
	echo "target_rootdev=${target_rootdev}";
	echo "rootdev_type=${rootdev_type}";
	echo "rootfs_size=${rootfs_size}";
	echo "odmdata=${odmdata}";
	echo "flashapp=${flashapp}";
	echo "flasher=${flasher}";
	echo "bootloader=${bootloader}";
	echo "ubootscript=${ubootscript}";
	echo "bctfile=${bctfile}";
	echo "cfgfile=${cfgfile}";
	echo "kernel_image=${kernel_image}";
	echo "rootfs=${rootfs}";
	echo "nfsroot=${nfsroot}";
	echo "nfsargs=${nfsargs}";
	echo "kernel_initrd=${kernel_initrd}";
	echo "usercmdline=${usercmdline}";
	exit 0;
}

validIP ()
{
	local ip=$1;
	local ret=1;

	if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
		OIFS=${IFS};
		IFS='.';
		ip=($ip);
		IFS=${OIFS};
		[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 && \
		   ${ip[2]} -le 255 && ${ip[3]} -le 255 ]];
		ret=$?;
	fi;
	return ${ret};
}

netmasktbl=(\
	"255.255.255.252" \
	"255.255.255.248" \
	"255.255.255.240" \
	"255.255.255.224" \
	"255.255.255.192" \
	"255.255.255.128" \
	"255.255.255.0" \
	"255.255.254.0" \
	"255.255.252.0" \
	"255.255.248.0" \
	"255.255.240.0" \
	"255.255.224.0" \
	"255.255.192.0" \
	"255.255.128.0" \
	"255.255.0.0" \
	"255.254.0.0" \
	"255.252.0.0" \
	"255.248.0.0" \
	"255.240.0.0" \
	"255.224.0.0" \
	"255.192.0.0" \
	"255.128.0.0" \
	"255.0.0.0" \
);

validNETMASK ()
{
	local i;
	local nm=$1;
	for (( i=0; i<${#netmasktbl[@]}; i++ )); do
		if [ "${nm}" = ${netmasktbl[$i]} ]; then
			return 0;
		fi;
	done;
	return 1;
}

validNFSargs ()
{
	local a=$1;

	OIFS=${IFS};
	IFS=':';
	a=($a);
	IFS=${OIFS};

	if [ ${#a[@]} -ne 4 ]; then
		return 1;
	fi;
	if ! validIP ${a[0]}; then
		return 1;
	fi;
	ipaddr=${a[0]};
	if ! validIP ${a[1]}; then
		return 1;
	fi;
	if [ "${serverip}" = "" ]; then
		serverip=${a[1]};
	fi;
	if ! validIP ${a[2]}; then
		return 1;
	fi;
	gatewayip=${a[2]};
	if ! validNETMASK ${a[3]}; then
		return 1;
	fi;
	netmask=${a[3]};
	return 0;
}

validNFSroot ()
{
	local a=$1;

	OIFS=${IFS};
	IFS=':';
	a=($a);
	IFS=${OIFS};
	if [ ${#a[@]} -ne 2 ]; then
		return 1;
	fi;
	validIP ${a[0]};
	if [ $? -ne 0 ]; then
		return 1;
	fi;
	if [[ "${a[1]}" != /* ]]; then
		return 1;
	fi;
	tftppath=${a[0]}:/tftpboot/${uimage_name};
	tftpfdtpath=${a[0]}:/tftpboot/${dtbfilename};
	if [ "${serverip}" = "" ]; then
		serverip=${a[0]};
	fi;
	return 0;
}

usage ()
{
	state=$1
	retval=$2

	if [[ $state == allunknown ]]; then
		echo -e "Usage: sudo ./flash.sh [options] <target board> <rootdev>\n       Where,
		target board: Valid target board name.
		rootdev: Proper rootdev."

	elif [[ $state == rootdevunknown ]]; then
		echo -e "Usage: sudo ./flash.sh [options] ${target_board} <rootdev>\n      Where,
		rootdev for ${target_board}:
		${ROOT_DEV}"

	else
		echo "Usage: sudo ./flash.sh [options] ${target_board} ${target_rootdev}"
	fi;

	cat << EOF
                options:
                   -h ------------------- print this message.
                   -b <bctfile> --------- nvflash boot control table config file.
                   -c <cfgfile> --------- nvflash partition table config file.
                   -d <dtbfile> --------- device tree file.
                   -e <emmc size> ------- Target device's eMMC size.
                   -f <flashapp> -------- Path to flash application: nvflash or tegra-rcm
                   -i ------------------- pass user kernel commandline as-is to kernel
                   -k <partition id> ---- kernel partition id to be updated. (minimum = 5)
                   -n <nfs args> -------- Static nfs network assignments
                                          <Client IP>:<Server IP>:<Gateway IP>:<Netmask>
                   -p <bp size> --------- Total eMMC HW boot partition size.
                   -r ------------------- skip building and reuse existing system.img.
                   -s <ubootscript> ----- HUSH bootscript file for U-Boot.
                   -C <cmdline> --------- Kernel commandline.
                                          WARNING:
                                          Each option in this kernel commandline gets higher
                                          preference over the same option from fastboot.
                                          In case of NFS booting, this script adds NFS booting
                                          related arguments, if -i option is omitted.
                   -F <flasher> --------- Flash server such as fastboot.bin
                   -I <initrd> ---------- initrd file. Null initrd is default.
                   -K <kernel> ---------- Kernel image such as zImage.
                   -L <bootloader> ------ Bootloader such as fastboot.bin or u-boot.bin
                   -P <end of PPT + 1> -- Primary GPT start address + size of PPT + 1.
                   -R <rootfs dir> ------ Sample rootfs directory.
                   -N <nfsroot> --------- nfsroot. i.e. <my IP addr>:/my/exported/nfs/rootfs.
                   -S <size> ------------ Rootfs size in bytes. Valid only for internal rootdev.
                                          KiB, MiB, GiB short hands are allowed, for example,
                                          1GiB means 1024 * 1024 * 1024 bytes.
                   -T <its file> -------- ITS file name. Valid only for u-boot.
EOF
	echo -e "\t\t   -o <odmdata> --------- ODM data ${target_board} ${ODMDATA}"
	exit $retval;
}

while getopts "hb:c:d:e:f:ik:n:o:p:rs:F:L:C:K:I:P:R:N:S:T:Z" OPTION
do
	case $OPTION in
	h) usage allunknown 0;
	   ;;
	b) bctfile=${OPTARG};
	   ;;
	c) cfgfile=${OPTARG};
	   ;;
	d) dtbfile=${OPTARG};
	   ;;
	e) emmc_size=${OPTARG};
	   if [[ ${emmc_size} == *KiB ]]; then
		emmc_size=$(( ${emmc_size%KiB} * 1024 ))
	   elif [[ ${emmc_size} == *MiB ]]; then
		emmc_size=$(( ${emmc_size%MiB} * 1024 * 1024 ))
	   elif [[ ${emmc_size} == *GiB ]]; then
		emmc_size=$(( ${emmc_size%GiB} * 1024 * 1024 * 1024 ))
	   fi
	   ;;
	i) ignorefastbootcmdline="true";
	   ;;
	k) target_partid=${OPTARG};
	   if [ $target_partid -lt $MIN_KERN_PARTID ]; then
		echo "Error: invalid partition id (min=${MIN_KERN_PARTID})";
		exit 1;
	   fi;
	   ;;
	f) flashapp=${OPTARG};
	   ;;
	n) nfsargs=${OPTARG};
	   if ! validNFSargs ${nfsargs}; then
		echo "Error: Invalid nfsargs(${nfsargs})";
		exit 1;
	   fi;
	   ;;
	o) odmdata=${OPTARG};
	   ;;
	p) bootpart_size=${OPTARG};
	   if [[ ${bootpart_size} == *KiB ]]; then
		bootpart_size=$(( ${bootpart_size%KiB} * 1024 ))
	   elif [[ ${bootpart_size} == *MiB ]]; then
		bootpart_size=$(( ${bootpart_size%MiB} * 1024 * 1024 ))
	   elif [[ ${bootpart_size} == *GiB ]]; then
		bootpart_size=$(( ${bootpart_size%GiB} * 1024 * 1024 * 1024 ))
	   fi
	   ;;
	r) reuse_systemimg="true";
	   ;;
	s) ubootscript=${OPTARG};
	   ;;
	F) flasher=${OPTARG};
	   ;;
	L) bootloader=${OPTARG};
	   ;;
	C) usercmdline=${OPTARG};
	   ;;
	K) kernel_image=${OPTARG};
	   ;;
	I) kernel_initrd=${OPTARG};
	   ;;
	P) bootpart_limit=${OPTARG};
	   if [[ ${bootpart_limit} == *KiB ]]; then
		bootpart_limit=$(( ${bootpart_limit%KiB} * 1024 ))
	   elif [[ ${bootpart_limit} == *MiB ]]; then
		bootpart_limit=$(( ${bootpart_limit%MiB} * 1024 * 1024 ))
	   elif [[ ${bootpart_limit} == *GiB ]]; then
		bootpart_limit=$(( ${bootpart_limit%GiB} * 1024 * 1024 * 1024 ))
	   fi
	   ;;
	R) rootfs=${OPTARG};
	   ;;
	N) nfsroot=${OPTARG};
	   if ! validNFSroot ${nfsroot}; then
		echo "Error: Invalid nfsroot(${nfsroot})";
		exit 1;
	   fi;
	   ;;
	S) rootfs_size=${OPTARG};
	   if [[ ${rootfs_size} == *KiB ]]; then
		rootfs_size=$(( ${rootfs_size%KiB} * 1024 ))
	   elif [[ ${rootfs_size} == *MiB ]]; then
		rootfs_size=$(( ${rootfs_size%MiB} * 1024 * 1024 ))
	   elif [[ ${rootfs_size} == *GiB ]]; then
		rootfs_size=$(( ${rootfs_size%GiB} * 1024 * 1024 * 1024 ))
	   fi
	   ;;
	T) itsfile=${OPTARG};
	   ;;
	Z) zflag="true";
	   ;;
	?) usage allunknown 1;
	   ;;
	esac
done
shift $((OPTIND - 1));
if [ $# -lt 2 ]; then
	usage allunknown 1;
fi;
target_board=$1;
target_rootdev=$2;

# Canonicalize all file path input if any.
#
if [ "${ubootscript}" != "" -a -f "${ubootscript}" ]; then
	ubootscript=`readlink -f "${ubootscript}"`;
fi;
if [ "${bctfile}" != "" -a -f "${bctfile}" ]; then
	bctfile=`readlink -f "${bctfile}"`;
fi;
if [ "${cfgfile}" != "" -a -f "${cfgfile}" ]; then
	cfgfile=`readlink -f "${cfgfile}"`;
fi;
if [ "${dtbfile}" != "" -a -f "${dtbfile}" ]; then
	dtbfile=`readlink -f "${dtbfile}"`;
fi;
if [ "${itsfile}" != "" -a -f "${itsfile}" ]; then
	itsfile=`readlink -f "${itsfile}"`;
fi;
if [ "${flashapp}" != "" -a -f "${flashapp}" ]; then
	flashapp=`readlink -f "${flashapp}"`;
fi;
if [ "${flasher}" != "" -a -f "${flasher}" ]; then
	flasher=`readlink -f "${flasher}"`;
fi;
if [ "${bootloader}" != "" -a -f "${bootloader}" ]; then
	bootloader=`readlink -f "${bootloader}"`;
fi;
if [ "${kernel_image}" != "" -a -f "${kernel_image}" ]; then
	kernel_image=`readlink -f "${kernel_image}"`;
fi;
if [ "${kernel_initrd}" != "" -a -f "${kernel_initrd}" ]; then
	kernel_initrd=`readlink -f "${kernel_initrd}"`;
fi;
if [ "${rootfs}" != "" -a -d "${rootfs}" ]; then
	rootfs=`readlink -f "${rootfs}"`;
fi;
if [ "${nfsroot}" != "" -a -d "${nfsroot}" ]; then
	nfsroot=`readlink -f "${nfsroot}"`;
fi;

if [ -r ${target_board}.conf ]; then
	source ${target_board}.conf
else
	echo "Error: Invalid target board."
	usage allunknown 1;
fi

# Default BSF(bootscript) files.
#
DFLT_BSF_DIR="${LDK_DIR}/bootloader/${target_board}";

# Default eMMC Boot Partition sizes.
#
DFLT_BOOTPARTLIMIT=10485760;

# Default BCT files.
#
DFLT_BCT_DIR="${LDK_DIR}/bootloader/${target_board}/BCT";

# Default CFG files.
#
DFLT_CFG_DIR="${LDK_DIR}/bootloader/${target_board}/cfg";

# Default DTB files.
#
DFLT_DTB_DIR="${LDK_DIR}/kernel/dtb";

# Default ITS files.
#
DFLT_ITS_DIR="${LDK_DIR}/bootloader/${target_board}";

if [ "${flashapp}" = "" ]; then
	flashapp=${LDK_DIR}/bootloader/${DFLT_FLASHAPPNAME};
fi;
flashappname=`basename ${flashapp}`;
if [ "${flasher}" = "" ]; then
	flasher=${LDK_DIR}/bootloader/${target_board}/${DFLT_FLASHERNAME};
fi;
flashername=`basename ${flasher}`;
if [ "${bootloader}" = "" ]; then
	bootloader=${LDK_DIR}/bootloader/${target_board}/${DFLT_BOOTLOADERNAME};
fi;
bootloadername=`basename ${bootloader}`;

if [ "${bootpart_limit}" = "" ]; then
	bootpart_limit=${DFLT_BOOTPARTLIMIT};
fi;

if [[ "${target_rootdev}" == mmcblk0p* ]]; then
	rootdev_type="internal";
	if [ "${rootfs_type}" = "" ]; then
		rootfs_type="ext3";
	fi;
elif [ "${target_rootdev}" = "eth0" -o \
	"${target_rootdev}" = "eth1" ]; then
	rootdev_type="network";
elif [[ "${target_rootdev}" != mmcblk1p* && \
	"${target_rootdev}" != sd* ]]; then
	echo "Error: Invalid target rootdev($target_rootdev).";
	usage rootdevunknown 1;
fi;
if [ "${rootfs_size}" = "" ]; then
	rootfs_size=1073741824;
fi;
if [ "${odmdata}" = "" ]; then
	odmdata=${ODMDATA};
fi;
if [ "${ubootscript}" = "" ]; then
	if [ $rootdev_type = "network" ]; then
		ubootscript="${DFLT_BSF_DIR}/${NET_BSF}";
	else
		ubootscript="${DFLT_BSF_DIR}/${EMMC_BSF}";
	fi;
fi;
if [ "${bctfile}" = "" ]; then
	bctfile="${DFLT_BCT_DIR}/${EMMC_BCT}";
fi;
if [ "${cfgfile}" = "" ]; then
	cfgfile="${DFLT_CFG_DIR}/${EMMC_CFG}";
fi;
if [ "${dtbfile}" = "" ]; then
	dtbfile="${DFLT_DTB_DIR}/${DTB_FILE}";
fi;
if [ "${itsfile}" = "" ]; then
	itsfile="${DFLT_ITS_DIR}/${ITS_FILE}";
fi;
if [ "${bootpart_size}" = "" ]; then
	bootpart_size=${BOOTPARTSIZE};
fi;
if [ "${emmc_size}" = "" ]; then
	emmc_size=${EMMCSIZE};
fi;

if [ "${zflag}" = "true" ]; then
	pr_conf ;
	exit 0;
fi;

if [ ! -f "${flashapp}" ]; then
	echo "Error: missing host side flash application($flashapp).";
	usage allknown 1;
fi;
if [ ! -f "${flasher}" ]; then
	echo "Error: missing target side flash application($flasher).";
	usage allknown 1;
fi;
if [ ! -f "${bootloader}" ]; then
	echo "Error: missing bootloader($bootloader).";
	usage allknown 1;
fi;

if [ $target_partid -lt $MIN_KERN_PARTID ]; then
	if [ ! -f $bctfile ]; then
		echo "Error: missing BCT file($bctfile).";
		usage allknown 1;
	fi;
	if [ ! -f $cfgfile ]; then
		echo "Error: missing config file($cfgfile).";
		usage allknown 1;
	fi;
fi;
if [ ! -f "${kernel_image}" ]; then
	echo "Error: missing kernel image file($kernel_image).";
	usage allknown 1;
fi;

if [ "${rootdev_type}" = "internal" ]; then
	if [ ${reuse_systemimg} = "false" ]; then
		if [ -z "${rootfs}" -o ! -d "${rootfs}" ]; then
			echo "Error: missing rootfs($rootfs).";
			popd > /dev/null 2>&1;
			usage allknown 1;
		fi;
	fi;
fi;

nvflashfolder=${LDK_DIR}/bootloader;
if [ ! -d $nvflashfolder ]; then
	echo "Error: missing nvflash folder($nfvlashfolder).";
	usage allknown 1;
fi;

pushd $nvflashfolder > /dev/null 2>&1;

localbctfile=flash.bct;
localbctcfgfile=bct.cfg;
localcfgfile=flash.cfg;
localpptfile=ppt.img;
localsptfile=gpt.img;
localefifile=efi.img;

rm -f ${localbctfile} > /dev/null 2>&1;
rm -f ${localbctcfgfile} > /dev/null 2>&1;
rm -f ${localcfgfile} > /dev/null 2>&1;
rm -f ${localpptfile} > /dev/null 2>&1;
rm -f ${localsptfile} > /dev/null 2>&1;

RECOVERY_TAG="-e /filename=recovery.img/d";
if [ -f recovery.img ]; then
	RECOVERY_TAG="-e s/#filename=recovery.img/filename=recovery.img/";
fi;

GPT_TAG="";
if [ "${bootpart_size}" != "" ]; then
	wrblksize=2097152;
	bplmod=$(( ${bootpart_limit} % ${wrblksize} ));
	bpsmod=$(( ${bootpart_size} % ${wrblksize} ));
	gptsize=$(( ${bootpart_limit} - ${bootpart_size} ));

	if [ ${bplmod} -ne 0 ]; then
		echo "Error: Boot partition limit is not modulo 2MiB";
		exit 1;
	fi;
	if [ ${bpsmod} -ne 0 ]; then
		echo "Error: Boot partition size is not modulo 2MiB";
		exit 1;
	fi;
	if [ ${gptsize} -lt ${wrblksize} ]; then
		echo "Error: No space for primary GPT.";
		exit 1;
	fi;

	GPT_TAG="-e s/size=2097152\s\+#BCTSIZE/size=${bootpart_size}/ ";
	GPT_TAG+="-e s/size=8388608\s\+#PPTSIZE/size=${gptsize}/ ";
	GPT_TAG+="-e s/#filename=ppt.img/filename=${localpptfile}/ ";
	GPT_TAG+="-e s/#filename=spt.img/filename=${localsptfile}/ ";
fi;

# Minimum FAT32 partition size is 64MiB (== 1 FAT cluster)
efifs_size=67108864;
EFI_TAG="-e s/size=67108864\s\+#EFISIZE/size=${efifs_size}/ ";
if [ "${bootloadername}" = "uefi.bin" ]; then
	EFI_TAG+="-e s/#filename=efi.img/filename=${localefifile}/";

	echo "Making ${localefifile}... ";
	umount /dev/loop0 > /dev/null 2>&1;
	losetup -d /dev/loop0 > /dev/null 2>&1;
	rm -f ${localefifile} > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "clearing ${localefifile} failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	bcnt=$(( ${efifs_size} / 512 ));
	bcntdiv=$(( ${efifs_size} % 512 ));
	if [ $bcnt -eq 0 -o $bcntdiv -ne 0 ]; then
		echo "Error: efi file system size has to be 512 bytes allign.";
		popd > /dev/null 2>&1;
		exit 1;
	fi
	dd if=/dev/zero of=${localefifile} bs=512 count=$bcnt > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "making ${localefifile} failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	losetup /dev/loop0 ${localefifile} > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "mapping ${localefifile} to loop device failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	mkfs.msdos -I -F 32 /dev/loop0 > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "formating filesystem on ${localefifile} failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	sync;
	rm -rf mnt;
	if [ $? -ne 0 ]; then
		echo "clearing EFI mount point failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	mkdir -p mnt;
	if [ $? -ne 0 ]; then
		echo "making EFI mount point failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	mount /dev/loop0 mnt > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "mounting ${localefifile} failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;

	echo -n -e "\tpopulating ${kernel_image} to ${localefifile}... ";
	mkdir -p mnt/boot/dtb;
	if [ $? -ne 0 ]; then
		echo "making ${localefifile}/boot/dtb failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	cp -f ${kernel_image} mnt/boot;
	if [ $? -ne 0 ]; then
		echo "Copying ${kernel_image} failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";
	if [ -f "${dtbfile}" ]; then
		echo -n -e "\tpopulating ${dtbfile} to ${localefifile}... ";
		cp -f ${dtbfile} mnt/boot/dtb/tegra.dtb;
		if [ $? -ne 0 ]; then
			echo "Copying ${dtbfile} failed.";
			popd > /dev/null 2>&1;
			exit 1;
		fi;
		echo "done.";
	fi;

	echo -n -e "\tpopulating cmdline.txt to /boot... ";
	mkdir -p mnt/boot;
	if [ $? -ne 0 ]; then
		echo "making /boot failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	touch -f mnt/boot/cmdline.txt;
	if [ $? -ne 0 ]; then
		echo "Creating cmdline.txt failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";

	echo -e -n "\tSync'ing ${localefifile}... ";
	sync; sync;	# Paranoid.
	sleep 5;	# Give host time to terminate file brower gracefully.
	echo "done.";
	umount /dev/loop0 > /dev/null 2>&1;
	losetup -d /dev/loop0 > /dev/null 2>&1;
	rmdir mnt > /dev/null 2>&1;
	echo "${localefifile} built successfully. ";
fi;

MKUARG="";
mkimageapp="";
if [ "${bootloadername}" = "u-boot.bin" ]; then
	if [ "${itsfile}" != "" -a -f ${itsfile} -a \
	     "${dtbfile}" != "" -a -f ${dtbfile} ]; then
		itsfilename=`basename ${itsfile}`;
		dtbfilename=`basename ${dtbfile}`;
		# Copy ITS file.
		ITSCONV="";
		ITSCONV+="-e s/gzip/none/ ";
		ITSCONV+="-e s?vmlinux.bin.gz?${kernel_image}? ";
		ITSCONV+="-e s/LOADADDR/${uboot_text_base}/ ";
		ITSCONV+="-e s/ENTRYPOINT/${uboot_text_base}/ ";
		ITSCONV+="-e s/LinuxKernel/${uimage_label}/ ";
		ITSCONV+="-e s/target.dtb/${dtbfilename}/ ";
		cat ${itsfile} | sed ${ITSCONV} > ${itsfilename};
		MKUARG="-f ${itsfilename}";
	else
		MKUARG+="-A arm ";
		MKUARG+="-O linux ";
		MKUARG+="-T kernel ";
		MKUARG+="-C none ";
		MKUARG+="-a ${uboot_text_base} ";
		MKUARG+="-e ${uboot_text_base} ";
		MKUARG+="-n ${uimage_label} ";
		MKUARG+="-d ${kernel_image} ";
	fi;
	mkimageapp=${LDK_DIR}/bootloader/mkimage;
fi;

if [ -f "${dtbfile}" ]; then
	dtbfilename=`basename ${dtbfile}`;
	echo -n "copying dtbfile(${dtbfile}) to ${dtbfilename}... ";
	cp -f ${dtbfile} ${dtbfilename};
	if [ $? -ne 0 ]; then
		echo "failed.";
		popd > /dev/null 2>&1;
	fi;
	echo "done.";
fi;

if [ "${rootdev_type}" = "external" -o "${rootdev_type}" = "network" ]; then
	if [ "${rootdev_type}" = "network" ]; then
		if [ "${nfsroot}" != "" ]; then
			if [ -f "${dtbfile}" ]; then
				dtbfilename=`basename ${dtbfile}`;
				if ! validNFSroot ${nfsroot}; then
					echo "Error: Invalid ${nfsroot}";
					exit 1;
				fi;
			fi;
			nfsdargs="root=/dev/nfs rw netdevwait";
			cmdline+="${nfsdargs} ";
			if [ "${nfsargs}" != "" ]; then
				nfsiargs="ip=${nfsargs}";
				nfsiargs+="::${target_rootdev}:off";
			else
				nfsiargs="ip=:::::${target_rootdev}:on";
			fi;
			cmdline+="${nfsiargs} ";
			cmdline+="nfsroot=${nfsroot} ";
		fi;
	fi;
elif [ $target_partid -ge $MIN_KERN_PARTID ]; then
	echo "Just updating kernel and boot device.";
	if [ "${bootloadername}" = "u-boot.bin" ]; then
		echo "Error: not supported for u-boot.";
		exit 1;
	fi;
elif [ ${reuse_systemimg} = "true" ]; then
	echo "Reusing existing system.img... ";
	if [ ! -e system.img ]; then
		echo "file does not exist.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";
else
	if [ "${bootloadername}" = "u-boot.bin" ]; then
		echo -e -n "\tpopulating kernel to rootfs... ";
		mkdir -p ${rootfs}/boot;
		rm -f ${rootfs}/boot/${uimage_name};
		${mkimageapp} ${MKUARG} ${rootfs}/boot/${uimage_name};
		if [ $? -ne 0 ]; then
			echo "failed.";
			popd > /dev/null 2>&1;
			popd > /dev/null 2>&1;
			exit 1;
		fi;
		echo "done.";
	fi;
	echo "Making system.img... ";
	umount /dev/loop0 > /dev/null 2>&1;
	losetup -d /dev/loop0 > /dev/null 2>&1;
	rm -f system.img;
	if [ $? -ne 0 ]; then
		echo "clearing system.img failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	bcnt=$(( ${rootfs_size} / 512 ));
	bcntdiv=$(( ${rootfs_size} % 512 ));
	if [ $bcnt -eq 0 -o $bcntdiv -ne 0 ]; then
		echo "Error: root file system size has to be 512 bytes allign.";
		popd > /dev/null 2>&1;
		exit 1;
	fi
	dd if=/dev/zero of=system.img bs=512 count=$bcnt > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "making system.img failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	losetup /dev/loop0 system.img > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "mapping system.img to loop device failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	mkfs -t ${rootfs_type} /dev/loop0 > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "formating filesystem on system.img failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	sync;
	rm -rf mnt;
	if [ $? -ne 0 ]; then
		echo "clearing mount point failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	mkdir -p mnt;
	if [ $? -ne 0 ]; then
		echo "making mount point failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	mount /dev/loop0 mnt > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "mounting system.img failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	pushd mnt > /dev/null 2>&1;
	echo -n -e "\tpopulating rootfs from ${rootfs}... ";
	(cd ${rootfs}; tar cf - *) | tar xf - ;
	if [ $? -ne 0 ]; then
		echo "failed.";
		popd > /dev/null 2>&1;
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";

	popd > /dev/null 2>&1;
	echo -e -n "\tSync'ing... ";
	sync; sync;	# Paranoid.
	echo "done.";
	umount /dev/loop0 > /dev/null 2>&1;
	losetup -d /dev/loop0 > /dev/null 2>&1;
	rmdir mnt > /dev/null 2>&1;
	echo "System.img built successfully. ";
fi;

if [ $target_partid -lt $MIN_KERN_PARTID ]; then
	DTB_TAG="";
	if [ -f "${dtbfile}" ]; then
		DTB_TAG="-e s/#filename=tegra.dtb/filename=${dtbfilename}/ ";
	else
		DTB_TAG="-e s/name=DTB/name=DTX/ ";
	fi;

	if [[ "${bctfile}" == *.cfg ]]; then
		localbctfile=${localbctcfgfile};
	fi;
	echo -n "copying bctfile(${bctfile}) to ${localbctfile}... ";
	cp -f $bctfile $localbctfile
	if [ $? -ne 0 ]; then
		echo "failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";

	echo -n "copying cfgfile(${cfgfile}) to ${localcfgfile}... ";
	CFGCONV=" ";
	if [ "${bootloadername}" = "fastboot.bin" ]; then
	    CFGCONV+="-e s/\#filename=flashboot.img/filename=boot.img/ ";
	    CFGCONV+="-e s/filename=flashboot.img/filename=boot.img/ ";
	elif [ "${bootloadername}" = "u-boot.bin" ]; then
	    if [ "${ubootscript}" != "" -a -f ${ubootscript} ]; then
		CFGCONV+="-e s/\#filename=flashboot.img/filename=boot.img/ ";
		CFGCONV+="-e s/filename=flashboot.img/filename=boot.img/ ";
	    else
		CFGCONV+="-e /\#filename=flashboot.img/d ";
		CFGCONV+="-e /filename=flashboot.img/d ";
		CFGCONV+="-e /filename=boot.img/d ";
	    fi;
	else
		CFGCONV+="-e /filename=flashboot.img/d ";
		CFGCONV+="-e /filename=boot.img/d ";
	fi;
	CFGCONV+="-e s/filename=bootloader.bin/filename=${bootloadername}/ ";
	CFGCONV+="-e s/filename=fastboot.bin/filename=${bootloadername}/ ";
	CFGCONV+="-e s/filename=u-boot.bin/filename=${bootloadername}/ ";
	CFGCONV+="-e s/\#filename=bootloader.bin/filename=${bootloadername}/ ";
	CFGCONV+="-e s/\#filename=fastboot.bin/filename=${bootloadername}/ ";
	CFGCONV+="-e s/\#filename=u-boot.bin/filename=${bootloadername}/ ";
	CFGCONV+="-e s/size=268435456/size=${rootfs_size}/ ";
	CFGCONV+="-e s/size=134217728/size=${rootfs_size}/ ";
	CFGCONV+="-e s/size=734003200/size=${rootfs_size}/ ";
	CFGCONV+="-e s/size=471859200/size=${rootfs_size}/ ";
	CFGCONV+="-e s/size=1073741824/size=${rootfs_size}/ ";
	CFGCONV+="-e s/size=402653184/size=${rootfs_size}/ ";
	CFGCONV+="${RECOVERY_TAG} ";
	CFGCONV+="${GPT_TAG} ";
	CFGCONV+="${DTB_TAG} ";
	CFGCONV+="${EFI_TAG} ";
	if [ "${rootdev_type}" = "external" -o \
	     "${rootdev_type}" = "network" ]; then
		CFGCONV+="-e /filename=system.img/d ";
	fi;
	cat ${cfgfile} | sed ${CFGCONV} > ${localcfgfile};
	if [ $? -ne 0 ]; then
		echo "failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";

	if [ ! -z "${bootpart_size}" -a ! -z "${emmc_size}" ]; then
		echo -n "creating gpt(${localpptfile})... ";
		MKGPTOPTS="-c ${localcfgfile} -P ${localpptfile} ";
		MKGPTOPTS+="-t ${emmc_size} -b ${bootpart_size} -s 4KiB ";
		MKGPTOPTS+="-a GPT -v GP1 ";
		MKGPTOPTS+="-V ";
		./mkgpt ${MKGPTOPTS};
		if [ $? -ne 0 ]; then
			echo "failed.";
			popd > /dev/null 2>&1;
			exit 1;
		fi;
		echo "done.";
	fi;
fi;

if [ "${bootloader}" != "${nvflashfolder}/${bootloadername}" ]; then
	echo -n "copying bootloader(${bootloader})... ";
	cp -f ${bootloader} ${nvflashfolder}/${bootloadername};
	if [ $? -ne 0 ]; then
		echo "failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";
else
	echo "Existing bootloader(${bootloader}) reused.";
fi;

if [ "${flasher}" != "${bootloader}" ]; then
	if [ "${flasher}" != "${nvflashfolder}/${flashername}" ]; then
		echo -n "copying flasher(${flasher})... ";
		cp -f ${flasher} ${nvflashfolder}/${flashername};
		if [ $? -ne 0 ]; then
			echo "failed.";
			popd > /dev/null 2>&1;
			exit 1;
		fi;
		echo "done.";
	else
		echo "Existing flasher(${flasher}) reused.";
	fi;
else
	echo "Bootloader(${bootloader}) used as flasher.";
fi;

if [ "${flashapp}" != "${nvflashfolder}/${flashappname}" ]; then
	echo -n "copying flash application(${flashapp})... ";
	cp -f ${flashapp} ${nvflashfolder}/${flashappname};
		if [ $? -ne 0 ]; then
		echo "failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done.";
else
	echo "Existing flash application(${flashapp}) reused.";
fi;

if [ "${bootloadername}" = "u-boot.bin" ]; then
	echo -n "generating boot script(${ubootscript}) image... ";
	if [ "${ubootscript}" != "" -a -f ${ubootscript} ]; then
		ubootscriptname=`basename ${ubootscript}`;
		NFSCONV="-e s/bootdelay=3/bootdelay=${bootdelay}/ ";
		NFSCONV+="-e s/IPADDR/${ipaddr}/ ";
		NFSCONV+="-e s/SERVERIP/${serverip}/ ";
		NFSCONV+="-e s/GATEWAYIP/${gatewayip}/ ";
		NFSCONV+="-e s/NETMASK/${netmask}/ ";
		NFSCONV+="-e s%TFTPPATH%${tftppath}% ";
		NFSCONV+="-e s%TFTPFDTPATH%${tftpfdtpath}% ";
		NFSCONV+="-e s/NFSARGS/${nfsiargs}/ ";
		NFSCONV+="-e s%NFSROOT%${nfsroot}% ";
		NFSCONV+="-e s%DTBFILENAME%${dtbfilename}% ";
		cat ${ubootscript} | sed ${NFSCONV} > ./${ubootscriptname};
		./mkubootscript -i ./${ubootscriptname} -o ./boot.img
		if [ $? -ne 0 ]; then
			echo "Error: Encoding failed.";
			popd > /dev/null 2>&1;
			exit 1;
		else
			echo "done.";
		fi;
	else
		echo "Missing. Using embedded bootscript.";
	fi;
else
	if [ "$kernel_initrd" != "" -a -f "$kernel_initrd" ]; then
		echo -n "copying initrd(${kernel_initrd})... ";
		cp -f ${kernel_initrd} initrd;
		if [ $? -ne 0 ]; then
			echo "failed.";
			popd > /dev/null 2>&1;
			exit 1;
		fi;
		echo "done.";
	else
		echo "making zero initrd... ";
		rm -f initrd;
		if [ $? -ne 0 ]; then
			echo "failed.";
			popd > /dev/null 2>&1;
			exit 1;
		fi;
		touch initrd;
		if [ $? -ne 0 ]; then
			echo "failed.";
			popd > /dev/null 2>&1;
			exit 1;
		fi;
		echo "done.";
	fi;

	echo -n "Making Boot image... "
	MKBOOTARG="";
	MKBOOTARG+="--kernel ${kernel_image} ";
	MKBOOTARG+="--ramdisk initrd ";
	MKBOOTARG+="--board ${target_rootdev} ";
	MKBOOTARG+="--output boot.img";
	if [ "x${ignorefastbootcmdline}" == "xtrue" ]; then
		cmdline="ignorefastboot ${usercmdline}";
	else
		cmdline+="${usercmdline}";
	fi
	./mkbootimg ${MKBOOTARG} --cmdline "${cmdline}" > /dev/null 2>&1;
	if [ $? -ne 0 ]; then
		echo "failed.";
		popd > /dev/null 2>&1;
		exit 1;
	fi;
	echo "done";
fi;

if [ $target_partid -ge ${MIN_KERN_PARTID} ]; then
	echo "*** Flashing kernel update started. ***"
	FLASHARGS="--download ${target_partid} boot.img ";
	FLASHARGS+="--bl ${flashername} --go";
	LD_LIBRARY_PATH=. ./${flashappname} ${FLASHARGS};
	if [ $? -ne 0 ]; then
		echo "Failed to flash ${target_board}."
		popd > /dev/null 2>&1;
		exit 2
	fi;
	echo "*** The kernel has been updated successfully. ***"
	popd > /dev/null 2>&1;
	exit 0;
fi;

echo "*** Flashing target device started. ***"
FLASHARGS="--bct ${localbctfile} --setbct --configfile ${localcfgfile} ";
FLASHARGS+="--create --bl ${flashername} --odmdata $odmdata --go";
echo "./${flashappname} ${FLASHARGS}";
./${flashappname} ${FLASHARGS};
if [ $? -ne 0 ]; then
	echo "Failed flashing ${target_board}.";
	popd > /dev/null 2>&1;
	exit 3;
fi;

echo "*** The target ${target_board} has been flashed successfully. ***"
if [ "${rootdev_type}" = "internal" ]; then
	echo "Reset the board to boot from internal eMMC."
elif [ "${rootdev_type}" = "network" ]; then
	if [ "${nfsroot}" != "" ]; then
		echo -n "Make target nfsroot(${nfsroot}) exported ";
		echo "on the network and reset the board to boot";
	else
		echo -n "Make the target nfsroot exported on the network, ";
		echo -n "configure your own DHCP server with ";
		echo -n "\"option-root=<nfsroot export path>;\" ";
		echo "preperly and reset the board to boot";
	fi;
else
	echo -n "Make the target filesystem available to the device ";
	echo "and reset the board to boot from external ${target_rootdev}."
fi;
echo
popd > /dev/null 2>&1;
exit 0;
