#!/usr/bin/env bash
: <<.
/*
 * Copyright (c) 2007, 2008 University of Tsukuba
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the University of Tsukuba 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 AND CONTRIBUTORS "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.
 */
/*
 * Copyright (c) 2010-2012 Yuichi Watanabe
 */
.

printhelp(){
	echo "usage: $0 [-f] [-l lba1] [-i lba2] [-c cmdline] device [loader] [vmm] [module1]"
	echo "       $0 -s [-l lba1] device"
	echo '  -f         first time (do not check existing data)'
	echo '  -l lba1    a lba where a boot record is installed (default 0; MBR)'
	echo '  -i lba2    a lba where a image is installed (default 1)'
	echo '  -c         cmdline boot parameters to vmm'
	echo '  -s         show the installing status'
	echo '  device     write to device (ex. /dev/sda)'
	echo '  loader     a path to bootloader (default: bootloader/bootloader)'
	echo '  vmm        a path to vmm.elf (default: vmm/vmm/vmm.elf)'
	echo '  module1    a path to guest bios (default: bios/out/bios.bin)'
}

show(){
	if [ ! -e "$device" ]; then
		echo "$device not found."
		return 1;
	fi

	length=`dd if=$device bs=1 skip="$((lba1 * 512 + 4))" count=4 2> /dev/null | od -t d8 | cut -d ' ' -f 2- -s`
	lba2=`dd if=$device bs=1 skip="$((lba1 * 512 + 8))" count=8 2> /dev/null | od -t d8 | cut -d ' ' -f 2- -s`
	cmdlineoff=`dd if=$device bs=1 skip="$((lba1 * 512 + 16))" count=4 2> /dev/null | od -t d8 | cut -d ' ' -f 2- -s`
	moduleoff=`dd if=$device bs=1 skip="$((lba1 * 512 + 20))" count=4 2> /dev/null | od -t d8 | cut -d ' ' -f 2- -s`

	if dd if="$device" skip="$lba2" count=1 2>/dev/null | od |
		grep -q '^0000000 042577 043114'
	then
		true
	else
		echo "VMM not found."
		return 1
	fi

	vmm_size=$cmdlineoff;
	if [ $vmm_size -le 0 ]; then
		vmm_size=$moduleoff;
	fi
	if [ $vmm_size -le 0 ]; then
		vmm_size=$length;
	fi

	echo "lba2: $((lba2))"
	echo "vmm: off 0 size $(($vmm_size))"

	if [ $cmdlineoff -le 0 ]; then
		echo "cmdline: (none)"
	else
		cmdline_size=$moduleoff
		if [ $cmdline_size -le 0 ]; then
		    cmdline_size=$length
		fi
		cmdline_size=$(($cmdline_size - $cmdlineoff));
		echo -n "cmdline: off $((cmdlineoff)) size $(($cmdline_size)) ("
		dd if=$device bs=1 skip=$((lba2 * 512 + $cmdlineoff)) count=$cmdline_size 2> /dev/null
		echo ")"
	fi

	if [ $moduleoff -le 0 ]; then
		echo "module1: (none)"
	else
		module_size=$(($length - $moduleoff));
		echo "module1: off $(($moduleoff)) size $(($module_size))"
	fi
	return 0;
}

printbin(){
	cnt=$1
	x=$(($2))
	while test $cnt -gt 0
	do
		a=$(($x%256))
		x=$(($x/256))
		printf \\`printf %o $a`
		cnt=$(($cnt-1))
	done
}

generatembr(){
	length=$1
	cmdlineoff=$2
	moduleoff=$3
	dd if="$loader" bs=1 count=4 2> /dev/null	#   0-   4 bytes
	printbin 4 $length				#   4-   4 bytes
	printbin 8 "$lba2"				#   8-   8 bytes
	printbin 4 $cmdlineoff				#  16-   4 bytes
	printbin 4 $moduleoff				#  20-   4 bytes
	dd if="$loader" bs=1 skip=24 count=416 2> /dev/null			#  24- 416 bytes
	dd if="$device" bs=1 skip="$((lba1 * 512 + 440))" count=70 2> /dev/null	# 440-  70 bytes
	dd if="$loader" bs=1 skip=510 count=2 2> /dev/null			# 510-   2 bytes
}

first=0
device=
lba1=0
lba2=1
loader=bootloader/bootloader
vmm=vmm/vmm.elf
cmdline=
module1=bios/out/bios.bin
arg=.
show=0

while test $# -gt 0
do
	case "$1" in
	-f)	first=1;;
	-s)	show=1;;
	-l)
		shift;
		lba1=$1;;
	-i)
		shift;
		lba2=$1;;
	-c)
		shift;
		cmdline=$1;;
	-*)	echo "unrecognized option \`$1'" >&2
		exit 1;;
	*)	case $arg in
		.)		device="$1";;
		..)		loader="$1";;
		...)		vmm="$1";;
		....)		module1="$1";;
		.....)		echo "extra operand \`$1'" >&2
				exit 1;;
		esac
		arg=.$arg;;
	esac
	shift
done

case $arg in
.)	printhelp
	exit 1;;
esac

case $show in
1)
	show
	exit $?;
esac

date

if [ ! -e "$device" ]; then
	echo "$device not found."
	exit 1
fi
	
case $first in
0)	if dd if="$device" skip="$lba2" count=1 2> /dev/null | od |
		grep -q '^0000000 042577 043114'
	then
		echo "found ELF header." >&2
	else
		echo "ELF header not found. use -f option to install." >&2
		exit 1
	fi;;
esac

size0=`cat "$vmm" | wc -c`
cmdlinesize=0
size1=0
cmdlineoff=0
off1=0
total=$size0
if [ -n "$cmdline" ]; then
	cmdlineoff=$total
	cmdlinesize=`echo -n "$cmdline" | wc -c`
	cmdlinesize=$(($cmdlinesize + 1))
	total=$(($total + $cmdlinesize))
	echo "cmdline: $cmdline"
fi
if [ -n "$module1" ]; then
	off1=$total
	size1=`cat "$module1" | wc -c`
	total=$(($total + $size1))
fi
echo "Writing to lba $lba1"
generatembr $total $cmdlineoff $off1 | dd of="$device" seek="$lba1" conv=notrunc 2> /dev/null

echo "Writing to lba $lba2"
(
    cat "$vmm"
    if [ -n "$cmdline" ]; then
	    echo -n $cmdline;
	    echo -n -e '\000'; # octal value
    fi
    if [ -n "$module1" ]; then
	    cat "$module1"
    fi
) | dd of="$device" seek="$lba2" conv=notrunc 2> /dev/null
