#!/bin/bash

# usage information
usage() {
    echo 'usage:'
    echo ' -o <output>  - path to output file for compressed kernel image'
    echo ' -a <arch>    - PowerPC sub-architecture'
    echo ' -k <kernel>  - path to kernel image file in ELF format'
    echo ' -s <sysmap>  - path to System.map file'
    echo ' -i <initrd>  - path to initrd image file'
    echo ' -d <objdir>  - path to directory with additional object files'
    echo ' -r <release> - kernel release number'
    echo ' -l <address> - Address where the kernel should be relocated'
    echo ' -n           - do not use an initrd'
    echo ' -z           - assume the initrd image file is already compressed'
    echo ' -u           - assume the initrd image file is not compressed'
    echo ' -v           - verbose operation (print commands)'

    exit 0
}

# echo a shell command if desired, and execute it
do_cmd() {
    if test -n "$verbose"; then
	echo $@
	eval $@
    else
	eval $@ > /dev/null 2>&1
    fi
    return
}

# parse command line
while getopts o:k:s:i:d:r:a:l:nuzv option; do
    case "$option" in
	o) output=$OPTARG ;;
	k) kernel=$OPTARG ;;
	s) sysmap=$OPTARG ;;
	i) initrd=$OPTARG ;;
	d) objdir=$OPTARG ;;
	r) release=$OPTARG ;;
	a) arch=$OPTARG ;;
	l) linkaddr=$OPTARG ;;
	n) noinitrd="Yes" ;;
	z) compressed="Yes" ;;
	u) compressed="No" ;;
	v) verbose="Yes" ;;
	*) usage; exit 0 ;;
    esac
done

# Let's provide some subarch dependent defaults
case $arch in
    apus|miboot)
    	# miboot and apus don't use builtin initrds,
	# and need to stay elf kernels, as we don't use a wrapper.
    	elf="Yes"
    	noinitrd="Yes"
    	;;
esac

# use non-option arguments as release version and kernel image file if needed
shift $(( $OPTIND - 1 ))
if test -z "$release" -a -n "$1"; then
    release=$1
fi
if test -z "$kernel" -a -n "$2"; then
    kernel=$2
fi

# if no sub-architecture was specified, read it from /proc
if test -z "$arch"; then
    case $(grep ^machine /proc/cpuinfo) in
	*PReP*Blackhawk*) arch=ppcbug ;;
	*PReP*) arch=prep ;;
	*CHRP*) arch=chrp ;;
	*Amiga*) arch=apus ;;
	*)
	    case $(grep ^pmac-generation /proc/cpuinfo) in
		*NewWorld) arch=pmac ;;
		*OldWorld) arch=coff ;;
	    esac
	    ;;
    esac
fi

test -z "$verbose" || echo === Building for sub-architecture $arch.

# if no kernel was specified, try to find one
if test -z "$kernel"; then

    # guess the location of a kernel
    if kernel=/boot/vmlinux-$release; test -n "$release" -a -r $kernel; then
	:
    elif kernel=/boot/vmlinux-$(uname -r); test -r $kernel; then
	:
    else
	kernel=""
    fi

    # we couldn't find a kernel, and therefore give up
    if test -z "$kernel"; then
	echo Could not find a kernel image file, please specify one.
	exit 1
    fi

    # sanitize the location of the kernel
    kernel=$(readlink -f $kernel)

fi

test -z "$verbose" || echo === Using kernel image file $kernel.

# if no initrd was specified, try to find one that goes with the kernel
if test -z "$noinitrd" -a -z "$initrd"; then

    # guess the location of an initrd, but don't try too hard
    if initrd=${kernel/vmlinux/initrd.img}; test -r $initrd; then
	:
    else
	initrd=""
    fi

    # sanitize the location of the initrd
    if test -n "$initrd"; then
	initrd=$(readlink -f $initrd)
    fi

fi

test -z "$verbose" -o -z "$initrd" || echo === Using initrd image file $initrd.

# if no release was specified, extract it from the kernel image name
if test -z "$release"; then
    release=$(echo $kernel | sed s/.*vmlinux-//)
    if echo $release | grep -q '2\.[46]\.[0-9]*'; then
	:
    else
	release=""
    fi
fi

if dpkg --compare-versions $release ge 2.6.16; then
  post_2_6_16=Yes
else
  post_2_6_16=
fi

test -z "$verbose" || echo === Release version seems to be $release.

# if no object file directory was specified, try to find one
if test -z "$objdir"; then

    # try using $STEM-image, then linux-image, then kernel-image
    # in a default location, then use the current directory
    if objdir=/usr/lib/$STEM-image-$release; test -d $objdir; then
	:
    elif objdir=/usr/lib/linux-image-$release; test -d $objdir; then
	:
    elif objdir=/usr/lib/kernel-image-$release; test -d $objdir; then
	:
    elif objdir=/usr/lib/mkvmlinuz; test -d $objdir && test "$arch" = "chrp"; then
    	post_2_6_16=Yes
    else
	objdir="$PWD"
    fi
fi

test -z "$verbose" || echo === Using object files from $objdir.

# if no output file was specified, source the configuration file
if test -z "$output" -a -r /etc/mkvmlinuz/output; then
    . /etc/mkvmlinuz/output
fi

# we absolutely need an output file, and won't attempt guesses
if test -z "$output"; then
    echo Please specify an output file.
    exit 1
fi

test -z "$verbose" || echo === Building a bootable compressed kernel image in $output.

# create a work directory
work=$(mktemp -d)

test -z "$verbose" || echo === Doing build in $work.

# utilities
if test "$post_2_6_16"; then
  ADDNOTE=$objdir/addnote
  ADDRAMDISK=$objdir/addRamDisk
  objs=$objdir
  STRIP="strip -s -R .comment"
  GZIP="gzip --force --best"
else
  ADDNOTE=$objdir/utils/addnote
  HACKOFF=$objdir/utils/hack-coff
  MKNOTE=$objdir/utils/mknote
  MKPREP=$objdir/utils/mkprep
  MKBUGBOOT=$objdir/utils/mkbugboot
  libs=$objdir/lib
  objs=$objdir/obj
  GZIP=gzip
  STRIP=strip
fi
LD=ld
OBJCOPY=objcopy
MV=mv

case "$arch","$post_2_6_16","$release" in
    apus,Yes,*|miboot,Yes,*|prep,Yes,*|ppcbug,Yes,*)
	;;
    chrp,Yes,*|pmac,Yes,*)
	dummy_kernel=$objs/mkvmlinuz-kernel-vmlinux.strip.o
	dummy_initrd=$objs/mkvmlinuz-kernel-initrd.o
	OBJS="$objs/crt0.o $objs/string.o $objs/prom.o $objs/stdio.o $objs/main.o $objs/div64.o"
	OBJS="$OBJS $objs/infblock.o $objs/infcodes.o $objs/inffast.o $objs/inflate.o $objs/inftrees.o $objs/infutil.o"
	;;
    apus,,*|miboot,,*|*,Yes,*)
	;;
    prep,,2.6*|ppcbug,,2.6*)
	test -d $objs/simple && objs=$objs/simple
	if test -r $objs/legacy.o; then prepmap=legacy; else prepmap=prepmap; fi
	OBJS="$objs/head.o $objs/relocate.o $objs/$prepmap.o $objs/misc.o $objs/misc-prep.o $objs/mpc10x_memory.o"
	LIBS="$libs/common.a $libs/ppc.a $libs/of.a"
	dummy=$objs/dummy.o
	;;
    pmac,,2.6*)
	test -d $objs/openfirmware && objs=$objs/openfirmware
	OBJS="$objs/start.o $objs/misc.o $objs/common.o $objs/newworldmain.o"
	LIBS="$libs/lib.a $libs/ppc.a $libs/of.a $libs/common.a"
	dummy=$objs/dummy.o
	;;
    *,,2.6*)
	test -d $objs/openfirmware && objs=$objs/openfirmware
	OBJS="$objs/start.o $objs/misc.o $objs/common.o $objs/${arch}main.o"
	LIBS="$libs/lib.a $libs/ppc.a $libs/of.a $libs/common.a"
	dummy=$objs/dummy.o
	;;
    chrp,,2.4*)
	test -d $objs/chrp && objs=$objs/chrp
	OBJS="$objs/start.o $objs/main.o $objs/misc.o"
	OBJS="$OBJS $objs/../common/string.o $objs/../common/ofcommon.o"
	LIBS="$libs/lib.a $libs/zlib.a $libs/of1275.a"
	dummy=$objs/../common/dummy.o
	;;
esac

# off we go...

# create the compressed initrd image file
if test -n "$initrd"; then
    test -z "$verbose" || echo === Creating compressed initrd image initrd.gz...
    if test -z "$compressed"; then
        # Detect if the file was already compressed by gzip.
    	if test "`od -A n -c -N 2 $initrd`" = " 037 213"; then 
	    compressed="Yes"
	else
	    compressed="No"
	fi
    fi
    case "$compressed" in
    	Yes)
	    do_cmd cp -p $initrd $work/initrd.gz
	    ;;
    	No)
	    do_cmd cp -p $initrd $work/initrd
	    do_cmd $GZIP $work/initrd
	    ;;
    esac
fi

# create the compressed kernel image file
test -z "$verbose" || echo === Creating compressed kernel image vmlinux.gz...
if test -n "$elf" || test -n "$post_2_6_16"; then
    do_cmd $STRIP $kernel -o $work/vmlinux
else
    do_cmd $OBJCOPY -O binary $kernel $work/vmlinux
fi
do_cmd $GZIP $work/vmlinux

# create the raw ELF image file; this is not needed for the miBoot image
if test -z "$elf"; then
    test -z "$verbose" || echo === Putting everything into ELF image file image.o...
    if test -n "$post_2_6_16"; then
      do_cmd $OBJCOPY $dummy_kernel $work/dummy_kernel.o \
    	--add-section=.kernel:vmlinux.strip=$work/vmlinux.gz \
	--set-section-flags=.kernel:vmlinux.strip=contents,alloc,load,readonly,data
      if test -n "$initrd"; then
	do_cmd $OBJCOPY $dummy_initrd $work/dummy_initrd.o \
	    --add-section=.kernel:initrd=$work/initrd.gz \
	    --set-section-flags=.kernel:initrd=contents,alloc,load,readonly,data
      fi
    else
      do_cmd $OBJCOPY -R .comment $dummy $work/image.o
      do_cmd $OBJCOPY $work/image.o $work/image.o \
	--add-section=.image=$work/vmlinux.gz \
	--set-section-flags=.image=contents,alloc,load,readonly,data
      if test -n "$sysmap"; then
	do_cmd $OBJCOPY $work/image.o $work/image.o \
	    --add-section=.sysmap=$sysmap \
	    --set-section-flags=.sysmap=contents,alloc,load,readonly,data
      fi
      if test -n "$initrd"; then
	do_cmd $OBJCOPY $work/image.o $work/image.o \
	    --add-section=.ramdisk=$work/initrd.gz \
	    --set-section-flags=.ramdisk=contents,alloc,load,readonly,data
      fi
    fi
else
	do_cmd $MV $work/vmlinux.gz $work/vmlinuz.$arch.tmp
fi

# link everything into the final image file and make it bootable
vmlinuz=$work/vmlinuz.$arch
if test "$post_2_6_16"; then
  LD_ARGS="-m elf32ppc -T $objs/zImage.lds"
else
  LD_ARGS="-T $objdir/boot/ld.script"
fi
OBJCOPY_ARGS="-R .comment"
test -z "$verbose" || echo === Creating bootable kernel image file vmlinuz.$arch...
case "$arch","$post_2_6_16","$release" in
    chrp,Yes,*|pmac,Yes,*)
	if test -n "$initrd"; then
		do_cmd $LD $LD_ARGS -o $vmlinuz $OBJS $work/dummy_kernel.o $work/dummy_initrd.o
	else
		do_cmd $LD $LD_ARGS -o $vmlinuz $OBJS $work/dummy_kernel.o 
	fi
	do_cmd $ADDNOTE $vmlinuz
    	;;
    chrp,,2.6*)
    	if [ -z "$linkaddr" ]; then linkaddr=0x00800000; fi
	LD_ARGS="$LD_ARGS -e _start -Ttext $linkaddr"
	do_cmd $LD -o $vmlinuz $LD_ARGS $objs/crt0.o $OBJS $work/image.o $LIBS
	do_cmd $OBJCOPY $OBJCOPY_ARGS $vmlinuz $vmlinuz
	do_cmd cp -p $objdir/boot/note $work
	do_cmd $ADDNOTE $vmlinuz
	;;
    chrp,,2.4*)
    	if [ -z "$linkaddr" ]; then linkaddr=0x00800000; fi
	LD_ARGS="$LD_ARGS -e _start -Ttext $linkaddr"
	do_cmd $LD -o $vmlinuz $LD_ARGS $objs/../common/crt0.o $OBJS $work/image.o $LIBS
	do_cmd $OBJCOPY $OBJCOPY_ARGS $vmlinuz $vmlinuz
	do_cmd $ADDNOTE $vmlinuz
	;;
    coff,,2.6*)
    	if [ -z "$linkaddr" ]; then linkaddr=0x00500000; fi
	LD_ARGS="$LD_ARGS -e _start -Ttext $linkaddr -Bstatic"
	OBJCOPY_ARGS="-O aixcoff-rs6000 -R .stab -R .stabstr $OBJCOPY_ARGS"
	do_cmd $LD -o $vmlinuz $LD_ARGS $objs/coffcrt0.o $OBJS $work/image.o $LIBS
	do_cmd $OBJCOPY $OBJCOPY_ARGS $vmlinuz $vmlinuz
	do_cmd $HACKOFF $vmlinuz
	;;
    pmac,,2.6*)
    	if [ -z "$linkaddr" ]; then linkaddr=0x01000000; fi
	LD_ARGS="$LD_ARGS -e _start -Ttext $linkaddr"
	OBJCOPY_ARGS="--add-section=.note=$work/note $OBJCOPY_ARGS"
	do_cmd $LD -o $vmlinuz $LD_ARGS $objs/crt0.o $OBJS $LIBS $work/image.o
	do_cmd $MKNOTE > $work/note
	do_cmd $OBJCOPY $OBJCOPY_ARGS $vmlinuz $vmlinuz
	;;
    ppcbug,,2.6*)
    	if [ -z "$linkaddr" ]; then linkaddr=0x00800000; fi
	LD_ARGS="$LD_ARGS -Ttext $linkaddr -Bstatic"
	OBJCOPY_ARGS="-O elf32-powerpc $OBJCOPY_ARGS -R .stab -R .stabstr -R .sysmap"
	do_cmd $LD -o $vmlinuz $LD_ARGS $OBJS $work/image.o $LIBS
	do_cmd $OBJCOPY $OBJCOPY_ARGS $vmlinuz $vmlinuz.tmp
	do_cmd $MKBUGBOOT $vmlinuz.tmp $vmlinuz
	;;
    prep,,2.6*)
    	if [ -z "$linkaddr" ]; then linkaddr=0x00800000; fi
	LD_ARGS="$LD_ARGS -Ttext $linkaddr -Bstatic"
	OBJCOPY_ARGS="-O elf32-powerpc $OBJCOPY_ARGS -R .stab -R .stabstr -R .sysmap"
	do_cmd $LD -o $vmlinuz $LD_ARGS $OBJS $work/image.o $LIBS
	do_cmd $OBJCOPY $OBJCOPY_ARGS $vmlinuz $vmlinuz.tmp
	do_cmd $MKPREP -pbp $vmlinuz.tmp $vmlinuz
	;;
    miboot,,2.6*|apus,,2.6*)
        do_cmd $MV $vmlinuz.tmp $vmlinuz
	;;
    *)
	echo Sorry, I do not know how to handle PowerPC sub-architecture $arch in version $release.
esac

# move bootable kernel image to its final location and creating symlinks.
if test -e $vmlinuz; then
    if test -n "$linkname"; then
    	oldlink="`readlink $linkname`"
    fi
    test -z "$verbose" || echo === Moving bootable kernel image file to $output...
    if test -e $output; then
	echo Output file $output exists, attempting to back it up...
	mv $output $output.old 2> /dev/null
	if test -n "$linkname" && test "$oldlink" = "`basename $output`"; then
	    oldlink=$output.old
	fi
    fi
    cat $vmlinuz > $output
    if test -n "$linkname"; then
	test -z "$verbose" || echo Creating symlink : $linkname -\> $output
	if test -L "$linkname"; then
	    unlink $linkname
	fi
	ln -sf "`basename $output`" $linkname
    	if test -n "$oldlink"; then
	    test -z "$verbose" || echo Creating backup symlink : $linkname.old -\> $oldlink
	    if test -L "$linkname.old"; then
	    	unlink $linkname.old
	    fi
	    ln -sf $oldlink $linkname.old
        fi
    fi
fi

# clean up
test -z "$verbose" || echo === Cleaning up...
rm -rf $work
