#!/bin/bash

# Creates a u-boot bootscript image from a template file, for
# /dev/mmcblk[01]p[01]. Unless otherwise specified, the output is written to
# the same device that's hosting the boot.txt.in template.
#
# U-boot needs to know our boot device and partition, and our kernel
# uname. This script tries to discover those values itself, so that kernel
# updates, etc. will not leave you with a broken system.
#
# In particular, we try to get the right answer even when
# debootstrap/multistrap have chrooted us, and/or when we're moving our
# filesystem over from SD to eMMC.
#
# Environment variables: DEV, UNAME, INFILE, OUTFILE
#
# Examples:
#   $ /boot/update-bootscr
#
#   $ DEV=mmcblk1p1 /emmc/boot/update-bootscr
#
#   $ chroot emmc
#   $ /boot/update-bootscr
#

# TODO: Employ dpkg triggers or equivalent, to re-run this script if the kernel
# uname changes due to kernel upgrade.

# ref: https://stackoverflow.com/questions/59895/how-can-i-get-the-source-directory-of-a-bash-script-from-within-the-script-itsel
SOURCE=${BASH_SOURCE[0]}
while [ -h "$SOURCE" ]; do
  DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
  SOURCE=$(readlink "$SOURCE")
  [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE
done
BASE_DIR="${BASE_DIR:-$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )}"


# Unless told otherwise, we assume we are installed in the /boot directory of
# the desired boot device, and we use that same location for our template input
# and script output. By doing so, debootstrap/multistrap can do their chroot,
# etc. without confusing us. Our postinst invocation doesn't know or care the
# difference during an ordinary package update, since it's always running on
# the only boot device.
INFILE="${INFILE:-${BASE_DIR}/boot.txt.in}"
OUTFILE="${OUTFILE:-${BASE_DIR}/boot.scr}"

# Use findmnt(8) to find our root device, unless we've already been told.
#
# Using findmnt identifies host device correctly if we're chrooted, which is
# what we want when, i.e., debootstrap is trying to run a native, second-stage
# operation:
#
#  $ findmnt -n /
#    /      /dev/mmcblk1p1 ext4   rw,relatime
#
#  $ chroot <emmc>
#  $ findmnt -n /
#    /      /dev/mmcblk0p1 ext4   rw,relatime
#
DEV="${DEV:-$(findmnt -n /)}"
oldDEV="$DEV"

# Make sure the answer is something we recognize: if it doesn't refer to a
# partition on an mmcblk device, it's unlikely we're in the right building.
DEV="$(echo $DEV | grep -o -e "mmcblk[[:digit:]]p[[:digit:]]")"

# Parse the device and partition fields.
BOOTDEV="$(echo ${DEV} | cut -b 7)"
BOOTPART="$(echo ${DEV} | cut -b 9)"

# Did we end up with anything?
if [[ -z "$DEV" || -z "$BOOTDEV" || -z "$BOOTPART" ]] ; then
    echo "DEV must be mmcblk[:digit:]p[:digit:] (DEV=$oldDEV)"
    exit 1 # Nope.
fi

# Guess our kernel name from the list of options in BASE_DIR, unless we've been told the answer already.
UNAME="${UNAME:-$(find ${BASE_DIR} -name "vmlinuz-*" -type f | sort -n | tail -n 1 | sed "s%${BASE_DIR}/vmlinuz-%%g")}"
if [[ -z "$UNAME" ]] ; then
    echo "Can't find a UNAME in ${BASE_DIR}"
    exit 1
fi

# Fill out the various fields in $INFILE to create a temporary input file for
# mkimage.  We have to do this in a tempfile, since mkimage won't take input
# from stdin.
TMPFILE=$(mktemp)
trap "rm -f $TMPFILE" EXIT
sed "s/\${BOOTDEV}/${BOOTDEV}/g;s/\${BOOTPART}/${BOOTPART}/g;s/\${UNAME}/${UNAME}/g" < $INFILE > $TMPFILE

# Print some helpful diagnostics.
grep "setenv" $TMPFILE | grep -e uname -e bootpart

# Finally, write the bootscript.
mkimage -A arm -O linux -T script -C none -n "bootscript" -d $TMPFILE $OUTFILE
