#!/bin/bash

# This script is used to setup a build environment and a configuration file
# from the command line options for mklinuximg build scripts. The result
# will be a RAM image of Linux that can boot on a SPARC/LEON with MMU.
#

usage() {
    echo "Usage:"
    echo "$0 <linux-image> <out-file> [optional parameters]..."
    echo "  <linux-image> linux image, for Linux 3.4 and older <linux-dir>/arch/sparc/boot/image."
    echo "                and for Linux 3.5 and newer <linux-dir>/vmlinux."
    echo "  <out-file>    output image that can be uploaded using GRMON or run in TSIM."
    echo ""
    echo " optional parameters:"
    echo "  -base <baseaddr>  optional baseaddress. The default is 0x40000000."
    echo "  -cmdline <string> kernel parameter string. Default is \"console=ttyS0,38400\"."
    echo "  -freq <frequency> optional frequency parameter in case if it cannot be retrieved from the Timer scalar."
    echo "  -amp <string>     optional string of format <idx0>=<val0>:<idx1>=<val1>:... that sets for core index"
    echo "                    n property ampopts to value n. Example 0=4:1=6 will set core index 0's ampopts to 4"
    echo "                    and core index 1's ampopts to 6."
    echo "  -ethmac <string>  set the ethernet mac address as 12 dgt hex. Default: 00007ccc0145"
    echo "  -ipi <irq_num>    IRQ number used by Linux SMP for IPIs. May not be shared. Allowed values: 1..14"
    echo "  -ioarea <ioarea>  GRLIB AMBA Plug&Play I/O AREA Base address. Defaults to 0xfff00000"
    echo "  -uartidx <index>  Select UART by index for PROM Console. Default 0 (first UART0)"
    echo "  -maxcpu <num>     Disable/Limit number of Linux CPUs 1..8 (default 8)"
    echo "  -mcpu <name>      Use -mcpu=<name> instead of the default -mcpu=leon3."
    echo "                    Specify '-' to not pass on any -mcpu, getting the default of the toolchain."
    echo "  -wakediscpus      Wake the disabled/limited CPUs"
    echo "  --version         Print version of mklinuximg"
    echo "  -xml <file>       Specify an xml file that adds extra properties and nodes to the device tree."
    echo "  -flat             Create a flat device-tree."
    echo "  -grgpio-noprobe   Do not probe grgpio cores (otherwise mklinuximg probes grgpio cores for info)."
    echo "  -grgpio-nbits <comma-separated-list>"
    echo "                    Provide number of lines for grgpio cores (in scan order)."
    echo "  -grgpio-imask <comma-separated-list-of-bitmasks>"
    echo "                    Provide the imask generic (which gpio lines can generate interrupts)"
    echo "                    for grgpio cores (in scan order)."
    echo "  -grgpio-irqgen <comma-separated-list>"
    echo "                    Provide the irqgen generic for grgpio cores (in scan order)."
    echo "  -cflags <string>  Add compilation flags. Can be used multiple times."
    echo "  -sym <filename>   Export debug information for mklinuximg to file."
    echo "  -strip            Strip output image of symbols and debug information."
#    echo "  -maxcpu <max_num> Maximum CPUs in the system [1..8]"

    exit 1;
}

version() {
    grep VERSION $prefix/VERSION
    exit 1;
}

failexit() {
    if [ "$isdump" == "0" ]; then
	rm -rf $tmpdir
    fi
    exit 1
}

prefix=`dirname $(readlink -f $0)`/

if [ -z "$CROSS_COMPILE" ]; then
    export CROSS_COMPILE=sparc-linux-
fi
OBJDUMP=${CROSS_COMPILE}objdump
READELF=${CROSS_COMPILE}readelf

if ! $OBJDUMP --version &> /dev/null; then
    echo >&2 "ERROR: Toolchain ${CROSS_COMPILE} not in PATH"
    echo >&2 ""
    exit 1
fi

xml=;
nolin=0;ioarea="";uartidx="0";
isdump=0;isdbg=0;istest=0; ofile=""; ldir=""; baddr="0x40000000"; freq="0"; cmdline="console=ttyS0,38400"; amp=""; smp=""; ipi="0";
ethmac="00007ccc0145";
max_cpus="8"
wake_dis_cpus="0";wake_dis_cpus_str="NO"
macrostr=""
grgpio_probe=1
symfile=""
strip=""
mcpu="leon3"
while [ $# -ne 0 ]
do
    case "$1" in
	#-o) shift; ofile="$1";
	-dump) isdump=1;;
	-d) isdbg=1; macrostr="${macrostr} -O0";;
	-nolin) nolin=1;;
	-t) istest=1;;
	-freq) shift; freq="$1";;
	-flat) macrostr="${macrostr} -DFLATOFTREE ";;
	-cmdline) shift; cmdline="$1";;
	-base) shift; baddr="$1";;
	-ethmac) shift; ethmac="$1";;
	-amp) shift; amp="$1";;
	-xml) shift; xml="$1";;
	-smp) ;; # ignore deprecated SMP flag, is autodetected
	-ipi) shift; ipi="$1";;
	-ioarea) shift; ioarea="$1";;
	-uartidx) shift; uartidx="$1";;
	-maxcpu) shift; max_cpus="$1";;
	-wakediscpus) wake_dis_cpus=1; wake_dis_cpus_str="YES";;
	--version) version ;;
	-grgpio-noprobe) grgpio_probe=0 ;;
	-grgpio-nbits) shift; grgpio_nbits="$1" ;;
	-grgpio-imask) shift; grgpio_imask="$1" ;;
	-grgpio-irqgen) shift; grgpio_irqgen="$1" ;;
	-cflags) shift; macrostr="${macrostr} $1" ;;
	-sym) shift; symfile="$1" ;;
	-strip) strip=1 ;;
	-mcpu) shift; mcpu="$1" ;;
	*) a[${#a[*]}]="$1";;
    esac
    shift
done

limg=`readlink -f ${a[0]}`
o=${a[1]}

if [ "${#a[*]}" != "2" ]; then usage; fi


# Setup build environment
tmpdir=`mktemp -d`
#tmpdir="/tmp/mklinuximg"
mkdir -p $tmpdir/include
cfg="$tmpdir/include/config_auto.h"
ampoptfile="$tmpdir/include/config_ampopts.h"
xmlo="xml.c"

# Autodetect if SMP image (if symbol trapbase_cpu0 exists)
smp_img="NO"
if $READELF -s $limg | grep trapbase_cpu0 > /dev/null ; then
	smp_img="YES"
	smp="1"
fi


# Autodetect Linux version from Linux header in head_32.S
hdr_base_adr=`$OBJDUMP -x $limg | grep root_flags | sed 's/[0-9a-fA-F][ ].*$//g'`
hdr_base_adr=`echo ${hdr_base_adr}0`
linux_ver=`$READELF -x 1 $limg | grep -E "^  0x${hdr_base_adr}" | cut -d' ' -f5`


# Find Linux BSS in Virtual Addresses
bss_addr=0x`$OBJDUMP -h $limg | grep \\\\.bss\\[\\[:space:\\]\\] | awk '{ print $4 }' `;
bss_size=0x`$OBJDUMP -h $limg | grep \\\\.bss\\[\\[:space:\\]\\] | awk '{ print $3 }' `;
bss_end=$((($bss_addr+$bss_size)))

# Print settings
echo "======== DETECTED SETTINGS ========"
echo "Linux version :" $linux_ver
echo "Linux-img     :" $limg
echo "Output:       :" $o
echo "prefix        :" $prefix
echo "tmpdir        :" $tmpdir
echo "base          :" $baddr
echo "amp           :" $amp
echo "ethmac        :" $ethmac
echo "SMP detected  :" $smp_img
echo "ipi           :" $ipi "(zero = use Linux default)"
echo "Linux BSS VA  :" $bss_addr " of size " $bss_size " bytes"
echo "custom-xml    :" ${xml}
echo "Max CPU       :" $max_cpus
echo "-wakediscpus  :" $wake_dis_cpus_str
echo "==================================="
echo
echo

if [ -z "$linux_ver" -o \
	-z "$bss_addr" -o "$bss_addr" = "0x" -o \
	-z "$bss_size" -o "$bss_size" = "0x" \
   ] ; then
    echo >&2 "ERROR: Could not extract needed information."
    echo >&2 "Did you use the right kind of image as input image?"
    echo >&2 ""
    exit 1
fi

# force a "symbol <FORCE_GRMON_LOAD_SYM>" after loading in grmon and grsim
macrostr="${macrostr} -DFORCE_GRMON_LOAD_SYM="\"\\\"${limg}\\\"\"" "

# Create configuration header file from arguments and Linux image, the
# header-file will override the defaults in include/config_auto.h

echo "/* Auto generated by mklinuximg utility */" > $cfg
echo "" >> $cfg
echo "#define CONFIG_RAM_START " $baddr >> $cfg
if [ "$freq" != "0" ]; then
  echo "#define CONFIG_BOOTLOADER_FREQ $freq">> $cfg
fi
if [ -n "$ioarea" ]; then
  echo "#define CONFIG_AMBA_IO_AREA 0x$ioarea">> $cfg
fi
echo "#define CONFIG_IPI_NUM " $ipi >> $cfg
if [ -n "$smp" ]; then
  echo "#define CONFIG_SMP" >> $cfg
fi
echo "#define CONFIG_MAX_CPUS $max_cpus" >> $cfg
echo "#define CONFIG_WAKE_DISABLED_CPUS $wake_dis_cpus" >> $cfg
echo "#define CONFIG_ETHMAC 0x${ethmac}ULL" >> $cfg
if [ -n "$amp" ]; then
  echo "#define CONFIG_AMPOPTS" >> $cfg
fi
echo "#define CONFIG_LINUX_CMDLINE \"$cmdline\"" >> $cfg
echo "#define CONFIG_LINUX_VERSION_CODE 0x${linux_ver}" >> $cfg
echo "#define CONFIG_LINUX_BSS_START ${bss_addr}" >> $cfg
echo "#define CONFIG_LINUX_BSS_SIZE ${bss_size}" >> $cfg
echo "#define CONFIG_UART_INDEX ${uartidx}" >> $cfg
echo "#define CONFIG_GRGPIO_PROBE ${grgpio_probe}" >> $cfg
echo "#define CONFIG_GRGPIO_NBITS ${grgpio_nbits}" >> $cfg
echo "#define CONFIG_GRGPIO_IMASKGEN ${grgpio_imask}" >> $cfg
echo "#define CONFIG_GRGPIO_IRQGEN ${grgpio_irqgen}" >> $cfg

if [ "$isdbg" != 0 ]; then
  echo "#define CONFIG_DEBUG" >> $cfg
fi
# For debugging only
if [ "$nolin" != 0 ]; then
  echo "#define CONFIG_NO_LINUX" >> $cfg
fi

# Build optional AMP Options c-file
echo "" > ${ampoptfile}
if [ -n "$amp" ]; then
    echo $amp | awk '{split($0,a,":"); for (v in a) { print a[v]; }; }' | awk '{split($0,a,"="); print "\t{" a[1] "," a[2] "},"; }; ' >> ${ampoptfile}
fi

# custom xml 
if [ "${xml}" != "" ]; then
    echo "processing xml: ${prefix}scanxml ${xml} ${tmpdir}/${xmlo}"
    ${prefix}scanxml ${xml} ${tmpdir}/${xmlo} || failexit
    macrostr="${macrostr} -DHASXML="\"\\\"${tmpdir}/${xmlo}\\\"\"" "
fi

# Build mklinuximg
cd $tmpdir
make -C $prefix/src O=`pwd` LINUX_IMAGE=$limg CFLAGS_EXTRA="${macrostr}" \
     CROSS_COMPILE=$CROSS_COMPILE MCPU="$mcpu" all || failexit
cd -
mv $tmpdir/image $o || failexit

echo
if [ -n "$symfile" ]; then
    echo "Storing mklinuximg debug info in $symfile"
    ${CROSS_COMPILE}objcopy --only-keep-debug $o "$symfile"
fi
if [ -n "$strip" ]; then
    echo "Stripping $o"
    ${CROSS_COMPILE}strip $o
fi

if [ "$isdump" == "0" ]; then
    rm -rf $tmpdir
else
    echo TMPDIR: $tmpdir
fi
echo

ls -l $o
$OBJDUMP -h $o
