#! /bin/sh
# Title:
#    t20-etherboot.sh
#
# Author:
#    Karl Mowatt-Wilson
#    http://mowson.org/karl
#    copyright: 2007 Karl Mowatt-Wilson
#    licence: GPL v2
#
# Revisions:
#    v0.1  -  5 Jul 2007 - initial testing
#    v0.11 - 21 Jul 2007 - use getopts
#    v0.12 - 12 Oct 2007 - simplify echo test to not use hd/tr

Usage () {
cat <<-EOF
	
	USAGE: 
	   t20-etherboot.sh [-d] [SourceImage [EtherbootRom]]
	
	This script combines a standard WinNT Evo T20 image file with an etherboot
	image, to make a T20 image which can do PXE booting (ie. a PXE client).
	SourceImage is the name of the standard WinNT firmware for your model
	of T20 - default is "./U96CPQ163.bin" (for 96M/128M T20).
	EtherbootRom is the name of the etherboot image to install - this cannot
	be specified without specifying SourceImage as well - default
	is "./eb-5.4.3-natsemi.zhd"
	The patched image file will be called "./bootp.bin"
	
	OPTIONS:
	   -d  DANGEROUS!  
	       Disable test of MBR end marker.  It is unlikely that this option 
	       will help anyone, but there is a possibility that my marker test
	       code fails on your machine, but the rest of the script works, in
	       which case this option would be useful.  The risk is that without
	       checking MBR location, the script may install etherboot in the
	       wrong place and corrupt the image file.  
	       DANGEROUS!
	
	NOTES:
	   Shells:
	      This has been tested with dash and is hopefully posix compliant.
	      bash should work fine.
	      tcsh is completely unknown to me.
	
EOF
}
###########################################################################
# Default names of files to use:
T20_STANDARD_FIRMWARE="./U96CPQ163.bin"
ETHERBOOT="./eb-5.4.3-natsemi.zhd"

# We need real echo for some things, rather than built-in shell echo, since
# this script needs predictable octal escape expansion.
ECHO="/bin/echo"
ECHOOPTIONS='-n -e'

# IMAGE is the name of the file to create from combining the two files above.
IMAGE="./bootp.bin"

###########################################################################
## Function to exit with error message.
## First param is return code, remaining params are lines of error message.
#
Fail() {
   ExitCode=$1
   shift
   echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" >&2
   echo "$(basename "$0"): FATAL ERROR" >&2
   while [ $# -gt 0 ]; do
      echo "$1" >&2
      shift
   done
   sleep 2
   exit $ExitCode
}

###########################################################################
## Function to check a list of desired tools are available.
#
Toolcheck() {
   while [ $# -gt 0 ]; do
      TOOL="$1"
      shift
      echo "Checking '$TOOL'"
      which "$TOOL" >/dev/null 2>/dev/null \
         || Fail 3 "'which' failed for '$TOOL' - can't find this command."
   done
}   


###########################################################################
#==========================================================================
# Parse command-line options.

MBR_TEST="TRUE"
while getopts "d" OPT; do
   case $OPT in
      d)   # Disable test of MBR end marker
           MBR_TEST=""
           ;;
      *)   Usage 
           exit 
           ;;
    esac
done
shift $(($OPTIND - 1)); OPTIND=1

# Accept files specified on command-line.
[ "$1" ] && T20_STANDARD_FIRMWARE="$1"
[ "$2" ] && ETHERBOOT="$2"


#==========================================================================
echo "=== Checking tools =================================================="
Toolcheck \
   dd \
   strings \
   grep

echo "=== Sanity checks ================================================="
echo "Check source image exists and is readable."
[ -r "$T20_STANDARD_FIRMWARE" ] \
   || Fail 3 "Can't read from standard firmware '$T20_STANDARD_FIRMWARE'"

echo "Check etherboot image is readable."
[ -r "$ETHERBOOT" ] \
   || Fail 3 "Can't read etherboot image '$ETHERBOOT'"


echo "=== Start working ================================================="
echo "Copying standard firmware '" \
      $T20_STANDARD_FIRMWARE      \
      "' to working file '$IMAGE'"
cp "$T20_STANDARD_FIRMWARE" "$IMAGE" \
   || Fail 3 "Couldn't copy '$T20_STANDARD_FIRMWARE'"

# The chmod is in case the file has come from a read-only location.
chmod u+rw "$IMAGE" \
   || Fail 3 "Couldn't chmod '$IMAGE'"

echo "Searching for Master Boot Record."
MBR_SEARCH=$(strings -n20 -td "$IMAGE" \
   | grep -Em1 "( )*[0-9]+( )+Invalid partition table$")
[ "$MBR_SEARCH" ] \
   || Fail 3 "Couldn't find MBR search string in image '$IMAGE'" \
             "Is this really a WinNT image file?"

MBR_SEARCH_LOCATION=$(echo "$MBR_SEARCH" | grep -Eo '[0-9]+')

DISK_START=$(( $MBR_SEARCH_LOCATION - 139 ))

echo "Disk start set to $DISK_START"


###########################################################################
# Define the location of the partition table, in case we want to edit it
MBR_PARTITION1_DD_SEEK=$(( $DISK_START + 446 ))
MBR_PARTITION2_DD_SEEK=$(( $MBR_PARTITION1_DD_SEEK + 16 ))
MBR_PARTITION3_DD_SEEK=$(( $MBR_PARTITION1_DD_SEEK + 32 ))
MBR_PARTITION4_DD_SEEK=$(( $MBR_PARTITION1_DD_SEEK + 48 ))

# Partition-type byte is 4 bytes on from beginning of partition record.
PART_TYPE_OFFSET=4

# Define where to look for the partition sector marker
# (which looks like 0x55AA at the end of the sector)
PART_ID_DD_SKIP=$(( $DISK_START + 510 ))

PART_START=$(( $DISK_START + 512 ))
echo "Partition start set to $PART_START"


###########################################################################
# Test that partition table has the correct marker bytes at the end.
# If this test fails, it is very likely that nothing is going to work
# properly.
# This test is optional - controlled by a command-line switch - see USAGE.

[ "$MBR_TEST" ] && {
   echo "Confirming MBR end marker."

   # We need real echo rather than built-in shell echo, since we need
   # predictable octal escape expansion
   echo "Check that we can use real echo ($ECHO)."
   which $ECHO >/dev/null 2>/dev/null \
      || Fail 3 "Couldn't find $ECHO"

   echo "Check that echo handles octal escapes."
   TESTECHO="$($ECHO $ECHOOPTIONS '\061\062\063')"
   [ "$TESTECHO" = '123' ] \
      || Fail 3 "echo command ($ECHO $ECHOOPTIONS) did not perform as expected with octal escapes." \
                "Expected result to be '123'" \
                "but got               '$TESTECHO'"

   echo "Checking MBR has expected ID of 0x55 0xAA at end."
   # Set marker to 0x55 0xAA (octal bytes 125 252).
   MARKER="$($ECHO $ECHOOPTIONS '\125\252' | tail -c2)"
   
   # Extract marker from where we think it should be.
   EXTRACT="$(dd if="$IMAGE" skip=$PART_ID_DD_SKIP bs=1 count=2)"
   
   # Check that the marker is correct.
   [ "$MARKER" = "$EXTRACT" ] \
      || Fail 3 "Couldn't find marker - MBR search failed."
   
   echo "MBR 'confirmed' at $DISK_START"
}


###########################################################################
echo "=== Setup etherboot ==============================================="
dd if="$ETHERBOOT" of="$IMAGE" bs=1 conv=notrunc seek=$PART_START \
   || Fail 3 "Couldn't copy etherboot."


###########################################################################
echo "=== Finished! ====================================================="
echo
echo "Done!  You should now flash the T20 with '$IMAGE'"
echo
