#!/bin/sh

#------------------------------------------------------------------------------
# This scripts defines common functions and variables
#------------------------------------------------------------------------------

export KVER VMLINUX SYSMAP KCONFIG MODDIR UNISTD_H
export INSMOD RMMOD OBJDUMP
export RET_STR
export ADDR_DIFF

#------------------------------------------------------------------------------
# utility functions
#

# Arguments $1: keywords (separated by ',')
#           $2: file
#           $3: error string
grep_chk() {
	local key
	for key in `echo $1 |sed 's@,@ @g'`; do
		RET_STR=`grep "\<$key\>" $2 2> /dev/null`
		[ $? -eq 0 ] && return
	done
	echo -e "$3"
	exit 1
}

# Arguments $1: $VMLINUX
#           $2: check file (e.g. System.map)
#           $3: correct file (e.g. $SYSMAP)
#           $4: description of file $2 (e.g. "Symbol table file")
kernel_update_chk() {
	local vmlinux_path
	vmlinux_path=`dirname $1`
	if [ $vmlinux_path != `dirname $3` ]; then
		if [ -f $vmlinux_path/$2 ]; then
			diff -u $vmlinux_path/$2 $3 > /dev/null 2>&1
			if [ $? -ne 0 ]; then
				echo "$4($3) doesn't match with the current kernel($1)."
				echo "   It looks like you re-configurated the kernel, but didn't install it yet."
				exit 1
			fi
		fi
	fi
}

# Arguments $1: cmd
cmd_chk() {
	PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin"
	RET_STR=`which $1 2>&1`
	if [ $? -ne 0 ]; then
		echo "'$1' not found on this system."
		echo "   Maybe you should check the PATH environment variable."
		exit 1
	fi
}

# Arguments -:
get_kdiff_addr() {
	local v
	if [ "$RET_STR" ]; then
		v=`printf "%d\n" 0x$RET_STR`
		v=`expr $v - $ADDR_DIFF`
		RET_STR=`printf "%x\n" $v`
	fi
}

# Arguments $1: symbols
get_ksym_addr() {
	grep_chk $1 $SYSMAP "Symbol '`echo $1 | sed \"s@,@' or '@g\"`' not found in symbol table file($SYSMAP)."
	RET_STR=`echo $RET_STR | cut -d' ' -f1`
	get_kdiff_addr
}

# Arguments $1: symbols
get_djprobe_addr_and_size() {
	local line size
	LANG=C
	line=`__bt_djp_disym $2 | grep '^Parameter:'`
	if [ $? -ne 0 ]; then
		echo "Symbol '$2' not found in symbol table file($SYSMAP)."
		exit 1
	fi
	size=`echo $line | cut -d'=' -f3`
	RET_STR=`echo $line | sed 's@.*addr=0x\([^ ]*\).*@\1@'`
	get_kdiff_addr
	RET_STR="$1_addr=0x$RET_STR $1_size=$size"
}

# Arguments $*: arguments of insmod command
do_insmod() {
	local ecode
	$INSMOD $* > /dev/null 2>&1
	ecode=$?
	if [ $ecode -ne 0 ]; then
		echo "insmod '$1' failed with exit code=$ecode."
		if [ `whoami` == "root" ]; then
			echo "   Please check the output of the 'dmesg' command."
		else
			echo "   You need root permission."
		fi
		exit 1
	fi
}

# Arguments $1: checking kernel version (e.g. "2.6.18")
# return value: version code (e.g. "132626" = 2 << 16 + 6 << 8 + 18)
get_int_kver() {
	local v p s
	v=`echo $1|sed 's@\([0-9]*\).\([0-9]*\).\([0-9]*\)@\1@'`
	p=`echo $1|sed 's@\([0-9]*\).\([0-9]*\).\([0-9]*\)@\2@'`
	s=`echo $1|sed 's@\([0-9]*\).\([0-9]*\).\([0-9]*\)@\3@'`
	expr $v \* 65536 + $p \* 256 + $s
}

# Arguments $1: checking kernel version (e.g. "2.6.18")
# return value: 1 ($1 > $KVER)
#               0 ($1 = $KVER)
#              -1 ($1 < $KVER)
kver_chk() {
	local real_kver int_real_kver int_chk_kver
	real_kver=`echo $KVER | sed 's@[-_].*@@'`
	int_real_kver=`get_int_kver $real_kver`
	int_chk_kver=`get_int_kver $1`
	if [ $int_real_kver -gt $int_chk_kver ]; then
		echo 1
	elif [ $int_real_kver -eq $int_chk_kver ]; then
		echo 0
	else
		echo -1
	fi
}



#------------------------------------------------------------------------------
# check vmlinux, System.map, .config and unistd.h files
#
[ -n "$KVER" ] || KVER=`uname -r`

[ -n "$VMLINUX" ] || VMLINUX=/lib/modules/$KVER/build/vmlinux
[ -f $VMLINUX ] || VMLINUX=/usr/lib/debug/lib/modules/$KVER/vmlinux
if ! [ -f $VMLINUX ]; then
	echo "vmlinux not found."
	exit 1
fi

[ -n "$SYSMAP" ] || SYSMAP=/boot/System.map-$KVER
[ -f $SYSMAP ] || SYSMAP=/lib/modules/$KVER/build/System.map
if ! [ -f $SYSMAP ]; then
	echo "Symbol table file not found."
	exit 1
fi

[ -n "$KCONFIG" ] || KCONFIG=/boot/config-$KVER
[ -f $KCONFIG ] || KCONFIG=/lib/modules/$KVER/build/.config
[ -f $KCONFIG ] || KCONFIG=/lib/modules/$KVER/source/.config
if ! [ -f $KCONFIG ]; then
	echo "Kernel configuration file not found."
	exit 1
fi

[ -n "$MODDIR" ] || MODDIR=/lib/modules/$KVER/kernel/drivers
if ! [ -d $MODDIR ]; then
	echo "Kernel module directory not found."
	exit 1
fi

[ -n "$UNISTD_H" ] || UNISTD_H=/lib/modules/$KVER/source/include/asm/unistd.h
[ -f $UNISTD_H ] || UNISTD_H=/usr/include/linux/sys.h
if ! [ -f $UNISTD_H ]; then
	echo "System call header file not found."
	exit 1
fi

export COMMON_ENV_CHECKED
if [ -z "$COMMON_ENV_CHECKED" ]; then
	#echo "Do common environment variable checking."

	kernel_update_chk $VMLINUX System.map $SYSMAP "Symbol table file"
	kernel_update_chk $VMLINUX .config $KCONFIG "Kernel configuration file"

	grep_chk "NR_syscalls" $UNISTD_H \
		"$UNISTD_H file not includes definition of 'NR_syscalls'."

	cmd_chk insmod;  INSMOD=$RET_STR
	cmd_chk rmmod;   RMMOD=$RET_STR
	cmd_chk objdump; OBJDUMP=$RET_STR

	#----------------------------------------------------------------------
	# Check kernel configuration
	#
	emsg="
	   Btrax needs following kernel configuratinos.
	      X86_LOCAL_APIC
	      X86_IO_APIC
	      KALLSYMS
	      PROC_FS"
	if [ `kver_chk 2.6.18` -ge 0 ]; then
	     emsg="$emsg
	      DEBUG_FS
	      RELAY
	      KPROBES"
	fi

	grep_chk "CONFIG_X86_LOCAL_APIC=y" $KCONFIG \
		"Kernel config disabled (X86_LOCAL_APIC)$emsg"
	grep_chk "CONFIG_X86_IO_APIC=y" $KCONFIG \
		"Kernel config disabled (X86_IO_APIC)$emsg"
	grep_chk "CONFIG_KALLSYMS=y" $KCONFIG \
		"Kernel config disabled (KALLSYMS)$emsg"
	grep_chk "CONFIG_PROC_FS=y" $KCONFIG \
		"Kernel config disabled (PROC_FS)$emsg"
	if [ `kver_chk 2.6.18` -ge 0 ]; then
		grep_chk "CONFIG_DEBUG_FS=y" $KCONFIG \
			"Kernel config disabled (DEBUG_FS)$emsg"
		grep_chk "CONFIG_RELAY=y" $KCONFIG \
			"Kernel config disabled (RELAY)$emsg"
		grep_chk "CONFIG_KPROBES=y" $KCONFIG \
			"Kernel config disabled (KPROBES)$emsg"
	fi

	#----------------------------------------------------------------------
	# Check kernel address diff
	#
	grep_chk "_stext" $SYSMAP "$SYSMAP not contain the entry of '_stext'."
	sysmap_text=`echo $RET_STR | cut -d' ' -f1`
	grep_chk "_stext" /proc/kallsyms \
		"/proc/kallsyms not contain the entry of '_stext'."
	kallsyms_text=`echo $RET_STR | cut -d' ' -f1`
	sysmap_val=`printf "%d\n" 0x$sysmap_text`
	kallsyms_val=`printf "%d\n" 0x$kallsyms_text`
	ADDR_DIFF=`expr $sysmap_val - $kallsyms_val`

	COMMON_ENV_CHECKED="yes"
fi

