#!/bin/sh
#
# scripts/checkinitrd.sh
# 
# initrd address checker
# 
# Copyright 2005,2006,2007 Sony Corporation
# 
# 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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
#

DEBUG=0
if [ x$KBUILD_VERBOSE = x2 ]; then
	DEBUG=1
fi

CPP=/usr/bin/cpp

echo -n "Checking initrd address: "

#
# Read physical address of initrd from autoconf.h in decimal
#
#  1. get CONFIG_CMDLINE
tmp=`$CPP -P -nostdinc -Iinclude -include linux/autoconf.h <<EOF
CONFIG_CMDLINE
EOF`
cmdline=`echo $tmp | sed -e "s/^\"\(.*\)\"$/\\1/"`
#  2. extract "initrd=" in decimal.
initrd=`echo $cmdline | awk '
	BEGIN {
		RS = "[[:space:]]";
		count = 0;
	}
	/^initrd=/ {
		count++;
		split($1, s, "[=,]");
		addr=strtonum(s[2]);
	}
	END {
		if (count > 1){ # multiple initrd=
			exit 2;
		}
		if (count == 1){
			printf("%d", addr);
			if (addr == 0){ # illegal string
				exit 3;
			}
			if (addr % 4096 != 0){ # not page aligned
				exit 4;
			}
		}
		exit count;
	}'`
ret=$?
if [ $ret = 0 ]; then
	echo "initrd is not specified. SKIP."
	exit 0
elif [ $ret = 2 ]; then
	echo -e "\n    ERROR: multiple \"initrd=\" found."
	exit 1
elif [ $ret = 3 ]; then
	printf "\n    ERROR: illegal initrd address: 0x%08x\n" $initrd
	exit 1;
elif [ $ret = 4 ]; then
	printf "\n    ERROR: initrd address is not page aligned: 0x%08x\n" $initrd
	exit 1
fi
if [ $DEBUG != 0 ]; then
	printf "initrd=0x%08x\n" $initrd
fi

#
# Read virtual address of "_end" from System.map in decimal.
#
end=`tail -n 1 System.map | awk '
	NF==3 {
		if ($3 == "_end"){
			end = "0x" $1;
			printf("%d", strtonum(end));
			exit 0
		}
	}
	{
		exit 1
	}'`
if [ $? != 0 ]; then
	echo -e "\n    ERROR: _end symbol not found."
	exit 1
fi
if [ $DEBUG != 0 ]; then
	printf "_end = 0x%08x\n" $end
fi

#
# Read kernel start address (physical address) from autoconf.h in hexdecimal.
#
#  1. get CONFIG_CXD41XX_PHYS_OFFSET.
phys=`$CPP -P -nostdinc -Iinclude -include linux/autoconf.h <<EOF
CONFIG_CXD4105_PHYS_OFFSET
EOF`
if [ $phys = CONFIG_CXD4105_PHYS_OFFSET ]; then
phys=`$CPP -P -nostdinc -Iinclude -include linux/autoconf.h <<EOF
CONFIG_CXD4115_PHYS_OFFSET
EOF`
fi
if [ $DEBUG != 0 ]; then
	echo PHYS_OFFSET = $phys
fi

#
# Calc physical address of bootmap
#
bootmap=`echo $end $phys | awk '
	{
		phys = strtonum($2);
		# check phys
		if (phys % 0x100000 != 0){ # not 1MB aligned
			exit 1;
		}
		# physical address of "_end"
		end = $1 - 0xc0000000 + phys;
		# page align
		bootmap = and(end+0xfff, 0xfffff000);
		printf("%d", bootmap);
		exit 0;
	}'`
if [ $? != 0 ]; then
	echo -e "\n    ERROR: CONFIG_CXD4105_PHYS_OFFSET is not 1MB aligned"
	exit 1
fi
if [ $DEBUG != 0 ]; then
	printf "bootmap = 0x%08x\n" $bootmap
fi

#
# Read memory size from autoconf.h in decimal
#
#  1. extract "mem="
memsize=`echo $cmdline | awk '
	BEGIN {
		RS = "[[:space:]]";
		count = 0;
		sum = 0;
	}
	/^mem=/ {
		count++;
		split($1, s, "[=@]");
		mem=s[2];
		if (mem ~ "[Mm]$"){ # MBytes
			sub("[Mm]$", "", mem);
			n = strtonum(mem) * 1024 * 1024;
		} else if (mem ~ "[Kk]$"){ # KBytes
			sub("[Kk]$", "", mem);
			n = strtonum(mem) * 1024;
		} else {
			n = strtonum(mem);
		}
		if (n == 0){ # illegal string
			printf("%d", n);
			exit 3;
		}
		if (n % 4096 != 0){
			printf("%d", n);
			exit 4;
		}
		sum += n;
	}
	END {
		if (count == 0) {
			exit 0;
		}
		printf("%d", sum);
		exit 1;
	}'`
mem_ret=$?
if [ $mem_ret = 3 ]; then
    printf "\n    ERROR: illegal memory size: 0x%08x\n" $memsize
    exit 1;
elif [ $mem_ret = 4 ]; then
    printf "\n    ERROR: memory size is not multiple of page size: 0x%08x\n" $memsize
    exit 1
fi
if [ $DEBUG != 0 ]; then
	printf "mem=0x%08x\n" $memsize
fi

#
# Read size of NODE0 and NODE1 from autoconf.h
#
#  get CONFIG_CXD4105_SIZE_NODE0 and CONFIG_CXD4105_SIZE_NODE1.
node0=`$CPP -P -nostdinc -Iinclude -include linux/autoconf.h <<EOF
CONFIG_CXD4105_SIZE_NODE0
EOF`
node1=`$CPP -P -nostdinc -Iinclude -include linux/autoconf.h <<EOF
CONFIG_CXD4105_SIZE_NODE1
EOF`
if [ $DEBUG != 0 ]; then
	echo CONFIG_CXD4105_SIZE_NODE0 = $node0
	echo CONFIG_CXD4105_SIZE_NODE1 = $node1
fi

if [ $node0 = CONFIG_CXD4105_SIZE_NODE0 ]; then
	if [ $DEBUG != 0 ]; then
		echo "non-DISCONTIGMEM system"
	fi
	if [ $mem_ret = 0 ]; then
	    echo -e "\n    ERROR: mem= is not specified."
	    exit 1
	fi
else
	if [ $DEBUG != 0 ]; then
		echo "DISCONTIGMEM system"
	fi
	if [ $mem_ret != 0 ]; then
	    echo -e "\n    ERROR: mem= is specified, though DISCONTIGMEM."
	    exit 1
	fi
	memsize=`echo $node0 $node1 | awk '
		{
			size0 = strtonum($1);
			size1 = strtonum($2);
			if (size0 == 0 || size1 == 0){ # illegal string
				exit 1;
			}
			if (size0 % 4096 != 0 || size1 % 4096 != 0){
				exit 2;
			}
			printf("%d", size0 + size1);
			exit 0;
		}'`
	ret=$?
	if [ $ret = 1 ]; then
		printf "\n    ERROR: illegal size: %s, %s\n", $node0, $node1
		exit 1
	elif [ $ret = 2 ]; then
		printf "\n    ERROR: size is not multiple of page size: %s, %s\n", $node0, $node1
		exit 1
	fi
fi
if [ $DEBUG != 0 ]; then
	echo memsize = $memsize
fi

#
# Calc start address of free area
#
freearea=`echo $bootmap $memsize | awk '
	{
		# sizeof bootmap
		pages = $2/4096;
		n = (pages + 7) / 8;
		# page align
		n = and(n+0xfff, 0xfffff000);
		# calc free area
		printf("%d", $1 + n);
	}'`
if [ $DEBUG != 0 ]; then
	printf "freearea=0x%08x\n" $freearea
fi

#
# Check address of initrd
#
if [ "$initrd" -lt "$freearea" ]; then
	echo -e "\n    ERROR: initrd and kernel are overlapped."
	printf "        initrd address = 0x%08x\n" $initrd
	printf "        free area      = 0x%08x\n" $freearea
	exit 1
fi
echo OK.
exit 0
