#!/bin/sh

# Copyright (C) 2006  Joey Hess  <joeyh@debian.org>
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011  Martin Michlmayr <tbm@cyrius.com>
# Copyright (C) 2011  Loïc Minier <lool@dooz.org>
# Copyright (C) 2011  Julian Andres Klode <jak@debian.org>

# 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 2
# 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
# USA.

set -e

self="$(basename "$0")"

PROC_CPUINFO="/proc/cpuinfo"
PROC_MTD="/proc/mtd"

error() {
	echo "$@" >&2
	exit 1
}

mtdblock() {
	local mtdname="$1"

	sed -rn "s,^mtd([^:]*).*\"$mtdname\"\$,/dev/mtdblock\\1,p" "$PROC_MTD"
}

check_dev_mtdblock() {
	local dev="$1"

	if [ ! -b "$dev" ]; then
		error "$dev is not a block device"
	fi
}

mtdsize() {
	local mtdname="$1"

	size=$(grep "\"$mtdname\"$" "$PROC_MTD" | cut -d " " -f 2)
	printf "%d" "0x$size"
}

check_kflavors() {
	local kfile_suffix="$1"
	shift

	if [ -z "$kfile_suffix" ]; then
		return 0
	fi
	for kflavor; do
		if [ "$kfile_suffix" = "$kflavor" ]; then
			return 0
		fi
	done
	return 1
}

check_size() {
	local mtd_name="$1"
	local required_size="$2"
	local actual_size="$3"

	if [ $required_size -gt $actual_size ]; then
		error "Not enough space in MTD $mtd_name (need $required_size but is actually $actual_size)."
	fi
}

check_supported() {
	local machine="$1"
	local field
	local value

	echo "$MACHINE_DB" | {
		while read field value; do
			if [ "$field" = "Machine:" ] && [ "$value" = "$machine" ]; then
				return 0
			fi
		done
		return 1
	}
}

get_cpuinfo_hardware() {
	grep "^Hardware" "$PROC_CPUINFO" | sed 's/Hardware\s*:\s*//'
}

get_kfile_suffix() {
	local kfile="$1"

	echo "$kfile" | sed -e 's/.*-//'
}

# this is case-sensitive and doesn't support fields spanning multiple lines
get_machine_field() {
	local machine="$1"
	local field_name="$2"
	local state="machine"
	local field
	local value

	echo "$MACHINE_DB" | {
		while read field value; do
			if [ "$state" = "machine" ] &&
				[ "$field" = "Machine:" ] &&
				[ "$value" = "$machine" ]; then
				state="fields"
			fi
			if [ "$state" = "fields" ]; then
				case "$field" in
					"${field_name}:")
						echo "$value"
						return 0
					;;
					"")
						state="machine"
					;;
				esac
			fi
		done
		return 1
	}
}

# output ARM instructions to set machine number; argument is the decimal
# machine number as found in linux/arch/arm/tools/mach-types
set_machine_id() {
	local machine_id="$1"
	local high
	local low

	if [ -z "$machine_id" ]; then
		return
	fi

	high="$(printf "%02x" $(($machine_id / 256)))"
	low="$(printf "%02x" $(($machine_id % 256)))"

	devio "wl 0xe3a01c$high,4" "wl 0xe38110$low,4"
}

gen_kernel() {
	local input="$1"
	local output="$2"
	local machine_id="$3"

	{
		set_machine_id "$machine_id"
		cat "$input"
	} >"$output"
}

flash_kernel() {
	local input_file="$1"
	local output_mtd="$2"
	local machine_id="$3"

	printf "Flashing kernel... " >&2
	gen_kernel "$input_file" "$output_mtd" "$machine_id" || error "failed."
	echo "done." >&2
}

flash_initrd() {
	local input_file="$1"
	local output_mtd="$2"
	local pad="$3"

	printf "Flashing initramfs... " >&2
	{
		cat "$input_file"
		if [ "$pad" -gt 0 ]; then
			dd if=/dev/zero bs="$pad" count=1 2>/dev/null
		fi
	} >"$output_mtd" || error "failed."
	echo "done." >&2
}

mkimage_kernel() {
	local kaddr="$1"
	local kdesc="$2"
	local kdata="$3"
	local uimage="$4"

	printf "Generating kernel u-boot image... " >&2
	mkimage -A arm -O linux -T kernel -C none -a "$kaddr" -e "$kaddr" \
		-n "$kdesc" -d "$kdata" "$uimage" >&2 1>/dev/null
	echo "done." >&2
}

mkimage_initrd() {
	local iaddr="$1"
	local idesc="$2"
	local idata="$3"
	local uinitrd="$4"

	printf "Generating initramfs u-boot image... " >&2
	mkimage -A arm -O linux -T ramdisk -C gzip -a "$iaddr" -e "$iaddr" \
		-n "$idesc" -d "$idata" "$uinitrd" >&2 1>/dev/null
	echo "done." >&2
}

mkimage_script() {
	local baddr="$1"
	local bdesc="$2"
	local bdata="$3"
	local bscript="$4"
	
	printf "Generating u-boot script image... " >&2
	mkimage -A arm -O linux -a "$baddr" -e "$baddr" -T script -C none \
		-n "$bdesc" -d "$bdata" "$bscript" >&2 1>/dev/null
	echo "done." >&2
}

mkimage_multi() {
	local maddr="$1"
	local mdesc="$2"
	local kdata="$3"
	local idata="$4"
	local umulti="$5"

	printf "Generating u-boot image..." >&2
	mkimage -A arm -O linux -T multi -C none -a "$maddr" -e "$maddr" \
		-n "$mdesc" -d "$kdata:$idata" "$umulti" >&2 1>/dev/null
	echo "done." >&2
}

backup_and_install() {
	local source="$1"
	local dest="$2"

	if [ -e "$dest" ]; then
		echo "Creating backup of $dest." >&2
		mv "$dest" "$dest.bak"
	fi
	echo "Creating new $dest." >&2
	mv "$source" "$dest"
}

# See http://www.nslu2-linux.org/wiki/Info/BootFlash -- the NSLU2 uses a
# 16 byte MTD header, the first four bytes (big endian) give the length of
# the remainder of the image, and the remaining bytes are zero.  Generate
# this header.
sercomm_header() {
	perl -e 'print pack("N4", shift)' "$1"
}

nslu2_swap() {
	if [ "$little_endian" ]; then
		devio "<<$1" "xp $,4"
	else
		cat "$1"
	fi
}

android_find_mmc_bootpart() {
	local size
	for dev in /dev/mmcblk?p?; do
		size=$(abootimg -i $dev 2>/dev/null| grep "image size"|cut -d ' ' -f5)
		if [ $size ] && \
		   [ -z "$boot_minsize" -o "$size" -gt "$boot_minsize" ]; then
			echo $dev
			return 0
		fi
	done
	error "Could not find an Android boot partition on a MMC device"
}

android_flash() {
	local device="$1"
	local args=""
	printf "Flashing kernel and initramfs to $device... " >&2

	# AC100: Configuration file. Should be replaced by something more generic
	[ ! -e /boot/bootimg.cfg ] || args="$args -f /boot/bootimg.cfg"

	abootimg -u $device $args -k $kfile -r $ifile >/dev/null || error "failed."
	echo "done." >&2
}

# somewhat RFC2822 based, but case sensitive, not tolerant to spaces etc.
MACHINE_DB="
Machine: Buffalo Linkstation LiveV3 (LS-CHL)
Kernel-Flavors: orion5x
Machine-Id: 2913
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage.buffalo
Boot-Initrd-Path: /boot/initrd.buffalo

Machine: Buffalo Linkstation Mini
Kernel-Flavors: orion5x
Machine-Id: 1858
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage.buffalo
Boot-Initrd-Path: /boot/initrd.buffalo

Machine: Buffalo Linkstation Pro/Live
Kernel-Flavors: orion5x
Machine-Id: 1585
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage.buffalo
Boot-Initrd-Path: /boot/initrd.buffalo

Machine: Buffalo/Revogear Kurobox Pro
Kernel-Flavors: orion5x
Machine-Id: 1509
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage.buffalo
Boot-Initrd-Path: /boot/initrd.buffalo

Machine: D-Link DNS-323
Kernel-Flavors: orion5x
Machine-Id: 1542
Mtd-Kernel: Linux Kernel
Mtd-Initrd: File System
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x00800000

Machine: Genesi Efika Smartbook
Machine-alias: Genesi Efika MX (Smartbook)
Kernel-Flavors: mx5
U-Boot-Kernel-Address: 0x90008000
U-Boot-Initrd-Address: 0x0
U-Boot-Script-Address: 0x0
U-Boot-Script-Name: bootscr.mx5
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd
Boot-Script-Path: /boot/boot.scr

Machine: Genesi EfikaMX nettop
Machine-alias: Genesi Efika MX (Smarttop)
Kernel-Flavors: mx5
U-Boot-Kernel-Address: 0x90008000
U-Boot-Initrd-Address: 0x0
U-Boot-Script-Address: 0x0
U-Boot-Script-Name: bootscr.mx5
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd
Boot-Script-Path: /boot/boot.scr

Machine: GLAN Tank
Kernel-Flavors: iop32x
Machine-Id: 1100

Machine: GTA02
Kernel-Flavors: s3c24xx
U-Boot-Multi-Address: 0x30008000
Boot-Multi-Path: /boot/uImage.bin

Machine: HP Media Vault mv2120
Kernel-Flavors: orion5x
Machine-Id: 1693
U-Boot-Multi-Address: 0x01600000
Boot-Multi-Path: /boot/uImage

Machine: HP t5325 Thin Client
Kernel-Flavors: kirkwood
Machine-Id: 2846
U-Boot-Multi-Address: 0x01600000
Boot-Multi-Path: /boot/uImage

# Really: Intel SS4000-e and compatibles
Machine: Lanner EM7210
Kernel-Flavors: iop32x
Machine-Id: 1212
Mtd-Kernel: zImage
Mtd-Initrd: ramdisk.gz

Machine: Linksys NSLU2
Kernel-Flavors: ixp4xx
Mtd-Kernel: Kernel
Mtd-Initrd: Ramdisk

Machine: Marvell DB-78x00-BP Development Board
U-Boot-Kernel-Address: 0x2000000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: Marvell GuruPlug Reference Board
Kernel-Flavors: kirkwood
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: Marvell OpenRD Base Board
Kernel-Flavors: kirkwood
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: Marvell OpenRD Client Board
Kernel-Flavors: kirkwood
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: Marvell OpenRD Ultimate Board
Kernel-Flavors: kirkwood
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: Marvell SheevaPlug Reference Board
Kernel-Flavors: kirkwood
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: Marvell eSATA SheevaPlug Reference Board
Kernel-Flavors: kirkwood
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: QNAP TS-109/TS-209
Kernel-Flavors: orion5x
Machine-Id: 1565
Mtd-Kernel: Kernel
Mtd-Initrd: RootFS1
U-Boot-Kernel-Address: 0x00008000

Machine: QNAP TS-119/TS-219
Kernel-Flavors: kirkwood
Machine-Id: 2139
Mtd-Kernel: Kernel
Mtd-Initrd: RootFS1
U-Boot-Kernel-Address: 0x00008000

Machine: QNAP TS-409
Kernel-Flavors: orion5x
Machine-Id: 1601
Mtd-Kernel: Kernel
Mtd-Initrd: RootFS1
U-Boot-Kernel-Address: 0x00008000

Machine: QNAP TS-41x
Kernel-Flavors: kirkwood
Machine-Id: 2502
Mtd-Kernel: Kernel
Mtd-Initrd: RootFS1
U-Boot-Kernel-Address: 0x00008000

Machine: Seagate FreeAgent DockStar
Kernel-Flavors: kirkwood
U-Boot-Kernel-Address: 0x00008000
U-Boot-Initrd-Address: 0x0
Boot-Kernel-Path: /boot/uImage
Boot-Initrd-Path: /boot/uInitrd

Machine: Thecus N2100
Kernel-Flavors: iop32x
Machine-Id: 1101
Mtd-Kernel: kernel
Mtd-Initrd: ramdisk

Machine: Toshiba AC100 / Dynabook AZ
Boot-Type: Android
Boot-Device-Minimal-Size: 5242881
"

main() {
if [ "x$1" = "x--machine" ]; then
	machine="$2"
	shift 2
else
	machine="$(get_cpuinfo_hardware)"
fi

if [ "x$1" = "x--supported" ]; then
	if check_supported "$machine"; then
		exit 0
	fi
	exit 1
fi

if [ -n "$1" ]; then
	kvers="$1"
	kfile="/boot/vmlinuz-$kvers"
	ifile="/boot/initrd.img-$kvers"
	desc="Debian kernel $kvers"
	idesc="Debian ramdisk $kvers"
else
	if [ -e /vmlinuz ]; then
		kfile=/vmlinuz
		ifile=/initrd.img
	elif [ -e /boot/vmlinuz ]; then
		kfile=/boot/vmlinuz
		ifile=/boot/initrd.img
	else
		error "Cannot find a default kernel in /vmlinuz or /boot/vmlinuz"
	fi
	desc="Debian kernel"
	idesc="Debian ramdisk"
fi

if [ ! -e $kfile ] || [ ! -e $ifile ]; then
	error "Can't find $kfile and $ifile"
fi
kfilesize=$(stat -c '%s' "$kfile")
ifilesize=$(stat -c '%s' "$ifile")

if [ -L "$kfile" ]; then
	kfile=$(readlink -e "$kfile")
fi
kfile_suffix=$(get_kfile_suffix "$kfile")

if ! check_supported "$machine"; then
	error "Unsupported platform."
fi

if kflavors="$(get_machine_field "$machine" "Kernel-Flavors")"; then
	if ! check_kflavors "$kfile_suffix" $kflavors; then
		echo "Kernel suffix $kfile_suffix does not match any of the expected flavors ($kflavors), therefore not writing it to flash." >&2
		exit 0
	fi
fi

machine_id="$(get_machine_field "$machine" "Machine-Id")" || :
mtd_kernel="$(get_machine_field "$machine" "Mtd-Kernel")" || :
mtd_initrd="$(get_machine_field "$machine" "Mtd-Initrd")" || :
ukaddr="$(get_machine_field "$machine" "U-Boot-Kernel-Address")" || :
uiaddr="$(get_machine_field "$machine" "U-Boot-Initrd-Address")" || :
ubaddr="$(get_machine_field "$machine" "U-Boot-Script-Address")" || :
umaddr="$(get_machine_field "$machine" "U-Boot-Multi-Address")" || :
boot_kernel_path="$(get_machine_field "$machine" "Boot-Kernel-Path")" || :
boot_initrd_path="$(get_machine_field "$machine" "Boot-Initrd-Path")" || :
boot_script_path="$(get_machine_field "$machine" "Boot-Script-Path")" || :
boot_multi_path="$(get_machine_field "$machine" "Boot-Multi-Path")" || :
boot_type="$(get_machine_field "$machine" "Boot-Type")" || :
boot_minsize="$(get_machine_field "$machine" "Boot-Device-Minimal-Size")" || :
ubfile="$(get_machine_field "$machine" "U-Boot-Script-Name")" || :

if [ -n "$mtd_kernel" ] || [ -n "$mtd_initrd" ]; then
	if [ ! -e "$PROC_MTD" ]; then
		error "$PROC_MTD doesn't exist"
	fi
fi
if [ -n "$mtd_kernel" ]; then
	kmtd=$(mtdblock "$mtd_kernel")
	if [ -z "$kmtd" ]; then
		error "Cannot find mtd partition '$mtd_kernel'"
	fi
	check_dev_mtdblock "$kmtd"
	kmtdsize=$(mtdsize "$mtd_kernel")
	kreqsize=$kfilesize
	# setting the machine id prepends 8 bytes in front of the kernel
	if [ -n "$machine_id" ]; then
		kreqsize=$(($kreqsize + 8))
	fi
	# encapsulating in an U-Boot image grows the size by 64 bytes
	if [ -n "$ukaddr" ]; then
		kreqsize=$(($kreqsize + 64))
	fi
	check_size "$mtd_kernel" $kreqsize $kmtdsize
fi
if [ -n "$mtd_initrd" ]; then
	imtd=$(mtdblock "$mtd_initrd")
	if [ -z "$imtd" ]; then
		error "Cannot find mtd partition '$mtd_initrd'"
	fi
	check_dev_mtdblock "$imtd"
	imtdsize=$(mtdsize "$mtd_initrd")
	ireqsize=$ifilesize
	# encapsulating in an U-Boot image grows the size by 64 bytes
	if [ -n "$uiaddr" ]; then
		ireqsize=$(($ireqsize + 64))
	fi
	check_size "$mtd_initrd" $ireqsize $imtdsize
fi

tmpdir=""
cleanup_tmpdir() {
	rm -rf "$tmpdir"
}
trap cleanup_tmpdir EXIT HUP INT QUIT ILL KILL SEGV PIPE TERM
tmpdir="$(mktemp -dt "$self.XXXXXXXX")"


case "$boot_type" in
	"Android")
		android_bootpart=$(android_find_mmc_bootpart)
		android_flash $android_bootpart
	;;
esac


case "$machine" in
	"Buffalo Linkstation LiveV3 (LS-CHL)" |\
	"Buffalo Linkstation Mini" |\
	"Buffalo Linkstation Pro/Live" |\
	"Buffalo/Revogear Kurobox Pro" |\
	"D-Link DNS-323" |\
	"Genesi Efika Smartbook" |\
	"Genesi EfikaMX nettop" |\
	"HP Media Vault mv2120" |\
	"HP t5325 Thin Client" |\
	"Marvell DB-78x00-BP Development Board" |\
	"Marvell GuruPlug Reference Board" |\
	"Marvell OpenRD Base Board" |\
	"Marvell OpenRD Client Board" |\
	"Marvell OpenRD Ultimate Board" |\
	"Marvell SheevaPlug Reference Board" |\
	"Marvell eSATA SheevaPlug Reference Board" |\
	"QNAP TS-109/TS-209" |\
	"QNAP TS-119/TS-219" |\
	"QNAP TS-409" |\
	"QNAP TS-41x" |\
	"Seagate FreeAgent DockStar")
		kernel="$kfile"
		initrd="$ifile"
		if [ -n "$machine_id" ]; then
			gen_kernel "$kernel" "$tmpdir/kernel" "$machine_id"
			kernel="$tmpdir/kernel"
		fi
		if [ -n "$ukaddr" ]; then
			mkimage_kernel "$ukaddr" "$desc" "$kernel" \
				"$tmpdir/uImage"
			kernel="$tmpdir/uImage"
			rm -f "$tmpdir/kernel"
		fi
		if [ -n "$umaddr" ]; then
			mkimage_multi "$umaddr" "$desc" "$kernel" "$initrd" \
				"$tmpdir/uImage"
			rm -f "$tmpdir/kernel"
		fi
		if [ -n "$boot_kernel_path" ]; then
			# don't mv the original kernel
			if [ "$kernel" != "$kfile" ]; then
				backup_and_install "$kernel" \
					"$boot_kernel_path"
			else
				# TODO add support for kernel symlink
				:
			fi
		elif [ -n "$kmtd" ]; then
			flash_kernel "$tmpdir/uImage" "$kmtd" ""
			rm -f "$tmpdir/uImage"
		fi
		if [ -n "$boot_multi_path" ]; then
			backup_and_install "$tmpdir/uImage" "$boot_multi_path"
		fi
		if [ -n "$uiaddr" ]; then
			mkimage_initrd "$uiaddr" "$idesc" "$initrd" \
				"$tmpdir/uInitrd"
			initrd="$tmpdir/uInitrd"
		fi
		if [ -n "$boot_initrd_path" ]; then
			# don't mv the original initrd
			if [ "$initrd" != "$ifile" ]; then
				backup_and_install "$initrd" \
					"$boot_initrd_path"
			else
				# TODO add support for initrd symlink
				:
			fi
		elif [ -n "$imtd" ]; then
			ipad=0
			# padding isn't needed for U-Boot images
			if [ -z "$uiaddr" ]; then
				ipad=$(($imtdsize - $ireqsize))
			fi
			flash_initrd "$initrd" "$imtd" $ipad
			rm -f "$tmpdir/uInitrd"
		fi
		if [ -n "$ubfile" ]; then
			ubdir="/boot/flash-kernel/"
			ubdesc="Debian U-Boot Boot Script"
			if [ ! -d "$ubdir" ]; then 
				mkdir -p "$ubdir"
				# XXX HACK: It needs better way of doing this
				rootfs="\/dev\/sda2"
				sed -e "s/@ROOTPARTITION@/$rootfs/" \
					/usr/share/flash-kernel/bootscript/"$ubfile" \
					>"$ubdir"/"$ubfile"
			fi
			if [ -e "$boot_script_path" ]; then
				cp -a "$boot_script_path" "$boot_script_path".bak
			fi
			mkimage_script "$ubaddr" "$ubdesc" "$ubdir"/"$ubfile" \
				"$boot_script_path"
		fi
	;;
	"GLAN Tank")
		rm -f /boot/initrd /boot/zImage
		ln -s "$(basename "$ifile")" /boot/initrd
		gen_kernel "$kfile" "/boot/zImage" "$machine_id"
	;;
	"GTA02")
		gen_kernel "$kfile" "$tmpdir/kernel" ""
		# Hack to work around a bug in some U-Boot versions:
		if [ $(($(stat -c '%s' "$tmpdir/kernel") % 4)) -eq 0 ]; then
			echo >> "$tmpdir/kernel"
		fi
		mkimage_multi "$umaddr" "$desc" "$tmpdir/kernel" "$ifile" \
			"$tmpdir/uImage"
		rm -f "$tmpdir/kernel"
		backup_and_install "$tmpdir/uImage" "$boot_multi_path"
	;;
	"Lanner EM7210" | "Thecus N2100")
		flash_kernel "$kfile" "$kmtd" "$machine_id"
		pad=$(($imtdsize - $ifilesize))
		flash_initrd "$ifile" "$imtd" $pad
	;;
	"Linksys NSLU2")
		case "$(dpkg --print-architecture)" in
			arm|armel)
				little_endian=1
			;;
			armeb)
				little_endian=0
			;;
		esac
		mtd_fis="FIS directory"
		fismtd=$(mtdblock "$mtd_fis")
		if [ -z "$fismtd" ]; then
			error "Cannot find mtd partition '$mtd_fis'"
		fi
		check_size "$mtd_kernel" $(($kfilesize + 16 + 16)) $kmtdsize
		check_size "$mtd_initrd" $(($ifilesize + 16)) $imtdsize
		# The following devio magic parses the FIS directory to
		# obtain the size, offset and name of each partition.  This
		# used used to obtain the offset of the Kernel partition.
		offset=$(echo "$(devio "<<$fismtd" '
			<= $ 0x20000 -
			L= 0x1000
			$( 1
				# 0xff byte in name[0] ends the partition table
				$? @ 255 =
				# output size base name
				<= f15+
				.= b 0xfffffff &
				<= f4+
				.= b
				pf "%lu %lu "
				<= f28-
				cp 16
				pn
				<= f240+
				L= L256-
			$) L255>')" |
			while read a b c; do
				if [ "$c" = "Kernel" ]; then
					echo $b
				fi
			done)
		# The Kernel partition, starting at $offset, is divided into
		# two areas at $boundary.  We therefore need to split the
		# kernel into two and write them to flash with two Sercomm
		# headers.
		boundary=1441792 # 0x00160000
		ksize1=$(($boundary - $offset - 16))
		printf "Flashing kernel: " >&2
		{
			sercomm_header $(($kfilesize + 16))
			dd if="$kfile" of="$tmpdir/kpart1" bs=$ksize1 count=1 2>/dev/null
			nslu2_swap "$tmpdir/kpart1"
			rm -f "$tmpdir/kpart1"
			sercomm_header 131072
			dd if="$kfile" of="$tmpdir/kpart2" ibs=$ksize1 skip=1 2>/dev/null
			nslu2_swap "$tmpdir/kpart2"
			rm -f "$tmpdir/kpart2"
		} > "$kmtd" || error "failed."
		echo "done." >&2
		printf "Flashing initramfs: " >&2
		dd if="$ifile" of="$tmpdir/initrd" ibs=$(($imtdsize - 16)) conv=sync 2>/dev/null
		{
			sercomm_header $ifilesize
			nslu2_swap "$tmpdir/initrd"
			rm -f "$tmpdir/initrd"
		} > "$imtd" || error "failed."
		echo "done." >&2
	;;
esac
}

if [ -z "$TEST_FLASH_KERNEL" ]; then
	main "$@"
fi
