#!/bin/bash
# Copyright (c) 2000-2016 Synology Inc. All rights reserved.
# vim:ft=sh

# ========================================================================
# To specify which platform depends on this kernel,
# please edit the section [platform kernel] in config/project.depends
# ========================================================================

# Copy key file
cp -sv /lnxscripts/config/{signing_key,trusted_certificates,untrusted_certificates} .

export ARCH=${ARCH}
CC=`echo ${CC} | awk -F" " '{print $1}'`
export CROSS_COMPILE=${ToolChainPrefix} CC="${CC}"
KERNEL_CC="${CC}"
KERNEL_STRIP=${STRIP_ORI}

if [ "$PLATFORM_FAMILY" = "SYNOPLAT_F_X86_64" ] && [ "$BUILD_TARGET" != "DOCKERX64" ]; then
ln -s "${SysRootInclude}/hydrogen/hydrogen.h" "${KernelDir}/include/crypto/hydrogen.h"
	ln -s "${SysRootInclude}/hydrogen/hydrogen.c" "${KernelDir}/crypto/hydrogen/hydrogen.c"
	ln -s "${SysRootInclude}/hydrogen/impl" "${KernelDir}/crypto/hydrogen/impl"
	sed -i "s/__RAMDISK_SIGN_PUBLIC_KEY__/$(cat /lnxscripts/config/rd_sign.pub | xxd -i -c 32 )/" \
		"${KernelDir}/init/initramfs.c"
fi

case ${MakeClean} in
	[Yy][Ee][Ss])
		[ -f Makefile ] && make distclean
		;;
esac

case ${CleanOnly} in
	[Yy][Ee][Ss])
		return
		;;
esac

# Version Infomation
LNXVERSION="${KernelDir}/.version"
if [ -f ${VERSION_FILE} ]; then
	expr $(GetDSMBuildNumber) - 1 > ${LNXVERSION}
fi

mkdir -p ${ImageDir}/modules
mkdir -p ${SysRootInclude}/crypto


KernelImage=${KernelDir}/arch/arm/boot/uImage
KernelTarget=uImage

case "$BUILD_TARGET" in
	ALPINE|STM_MONACO|MARVELL_ARMADA38X|HISILICON_HI3536)
		KernelConfig=synoconfigs/${PLATFORM_ABBR}
		;;
	*)
		SkipThisProject
		return
		;;
esac

config_debug_mode()
{
    local _protect_ver="$(dpkg -l | grep -w synoprotection | awk '{print $3}' | cut -d'-' -f2 | head -n1)"
    local _dsm_ver="$(GetDSMBuildNumber)"
    if [ -z "$_protect_ver" ]; then
        WARNING "Unable to detect dpkg 'synoprotection'"
    elif [ "$_dsm_ver" != "$_protect_ver" ]; then
        WARNING "buildnumber in synoversion/VERSION doesn't match dpkg 'synoprotection'"
        exit 1
    fi

    local _cfg=
    local _enabled="
        CONFIG_DEBUG_FS
        CONFIG_PERF_EVENTS

        CONFIG_DEBUG_INFO
        CONFIG_FRAME_POINTER

        CONFIG_DEBUG_SPINLOCK
        CONFIG_DEBUG_MUTEXES
        CONFIG_DEBUG_ATOMIC_SLEEP

        CONFIG_FTRACE
        CONFIG_BRANCH_PROFILE_NONE
        CONFIG_FUNCTION_TRACER
        CONFIG_FTRACE_SYSCALLS
        CONFIG_IRQSOFF_TRACER
        CONFIG_PREEMPT_TRACER
        CONFIG_SCHED_TRACER
        CONFIG_BLK_DEV_IO_TRACE
        CONFIG_STACK_TRACER
        CONFIG_UPROBE_EVENT
        CONFIG_FUNCTION_PROFILER

        CONFIG_KPROBES
        CONFIG_BPF_SYSCALL
    "
    local _disabled="
    "

    for _cfg in $_enabled; do
        INFO "Enable $_cfg"
        ./scripts/config --enable $_cfg
    done

    for _cfg in $_disabled; do
        INFO "Disable $_cfg"
        ./scripts/config --disable $_cfg
    done
}

# Build kernel and modules
rm -f ${KernelImage}
rm -f ${ImageDir}/modules/*

cp -f ${KernelConfig} .config
mkdir -p include/linux/modules

if [ -f ".debug" ]; then
    INFO ".debug detected. Configured as debug mode."
    config_debug_mode
    make olddefconfig
else
    make oldconfig
fi

echo "===== Build Synology Linux kernel 3.10.x BSP ====="
make ${MAKE_FLAGS} ${KernelTarget} modules CC=${KERNEL_CC}

# Check if kernel is built correctly
if [ ! -f "${KernelImage}" ]; then
	echo "Error! ${KernelImage} does not exist!"
	exit 1;
fi

# Compare kernel config with re-generated one to see
# if any changes or new options not reapplied to ${KernelConfig}.
ConfigDiff=`diff -bu ${KernelConfig} .config`
if [ -z "$ConfigDiff" ]; then
	echo
	echo "Kernel config matches generated .config"
	echo
else
	echo ========== diff -bu ${KernelConfig} .config
	diff -bu ${KernelConfig} .config
	echo ========== END of diff

        if [ ! -f ".debug" ]; then
            echo
            echo "!!! Diff EXISTS between ${KernelConfig} & generated .config"
            echo "!!! Recheck before committing."
            echo
        fi
fi

check_macro_fail()
{
    local WarnMsg=$1
    local Macro=$2
    # Build failed if not debug mode
    [ -f ".debug" ] || WARNING "!!! CONFIG_SYNO check fail : $Macro"
    WARNING "$WarnMsg"
    WARNING "Please reconfirm your modifications before committing."
    echo ""
}

check_define_macro()
{
    INFO "Check CONFIG_SYNO* macro define"
    local GrepSynoDefine=""
    local Macro=""
    local SynoMacro=""
    local SynoKconfigTable=`find . -name "Kconfig*" -exec grep -n "config SYNO" {} +`
    declare -A array
    declare -A SynoMacroWhiteList=( [SYNO_EXPORT_CONFIG]=1 \
            [SYNO_UNICODE]=1 \
            [SYNO_YOTAWIMAX_SUPPORT]=1 \
            [SYNO_SATA_MV_EH]=1 \
            [SYNO_FS_OPTIONS]=1 \
            [SYNO_EVANSPORT_POWEROFF]=1 \
            [SYNO_MD_DISKSTATS]=1 \
            [SYNO_CONFIGFS_BACKPORT]=1 \
            [SYNO_CVE_2017_0861]=1 \
            [SYNO_REMOVE_RA_ERROR_LOG]=1 \
	    )

    IFS=$'\n'

    GrepSynoDefine=`grep -E -o -rn --exclude-dir={.git,SynoBuildConf} "defined *\(CONFIG_SYNO([0-9]|[a-z]|[A-Z]|_)*"`
    for WarnMsg in $GrepSynoDefine; do
        SynoMacro=`echo "$WarnMsg" | cut -d "(" -f2 | cut -c 8-`
        if [ ! -z ${array["$SynoMacro"]} ]; then
            continue;
        fi
        if [ `echo $SynoKconfigTable | grep "config $SynoMacro" | wc -l` -eq 0 ]; then
            check_macro_fail $WarnMsg $SynoMacro
        else
            array[$SynoMacro]=1
        fi
    done

    GrepSynoDefine=`grep -E -o -rn --exclude-dir={.git,SynoBuildConf} "ifdef *CONFIG_SYNO([0-9]|[a-z]|[A-Z]|_)*"`
    for WarnMsg in $GrepSynoDefine; do
        SynoMacro=`echo "$WarnMsg" | sed 's/CONFIG_/@/g' | cut -d "@" -f2`
        if [ ! -z ${array["$SynoMacro"]} ]; then
            continue;
        fi
        if [ `echo $SynoKconfigTable | grep "config $SynoMacro" | wc -l` -eq 0 ]; then
            check_macro_fail $WarnMsg $SynoMacro
        else
            array[$SynoMacro]=1
        fi
    done

    #check: ifndef CONFIG_SYNO* case
    GrepSynoDefine=`grep -E -o -rn --exclude-dir={.git,SynoBuildConf} "ifndef *CONFIG_SYNO"`
    if [ `echo "$GrepSynoDefine" | wc -l` -ne 0  ]; then
        for WarnMsg in $GrepSynoDefine; do
            SynoMacro=`echo "$WarnMsg" | sed 's/CONFIG_/@/g' | cut -d "@" -f2`
            check_macro_fail $WarnMsg $SynoMacro
        done
    fi

    #check: if defined(SYNO_* case
    GrepSynoDefine=`grep -E -o -rn --exclude-dir={.git,SynoBuildConf} "defined *\(SYNO([0-9]|[a-z]|[A-Z]|_)*"`
    for WarnMsg in $GrepSynoDefine; do
        Macro=`echo "$WarnMsg" | cut -d "(" -f2`
        if [ -z ${SynoMacroWhiteList["$Macro"]} ]; then
            check_macro_fail $WarnMsg $Macro
        fi
    done

    #check: ifdef SYNO_* case
    GrepSynoDefine=`grep -E -o -rn --exclude-dir={.git,SynoBuildConf} "ifdef *SYNO([0-9]|[a-z]|[A-Z]|_)*"`
    for WarnMsg in $GrepSynoDefine; do
        Macro=`echo "$WarnMsg" | awk '{print substr($0, index($0, "SYNO"))}'`
        if [ -z ${SynoMacroWhiteList["$Macro"]} ]; then
            check_macro_fail $WarnMsg $Macro
        fi
    done

    unset IFS
}

check_define_macro

check_alpine_kernel_size()
{
	# 32M(max arm32 branch offset) - 20M (module size + pmt size) - 1M (reserved)
	KERNEL_SIZE_LIMIT=11534336
	VMLINUX="${KernelDir}/vmlinux"
	READELF=${ToolChainPrefix}readelf

	bss_address=0x`$READELF -S $VMLINUX | grep ".bss" |awk '{print $4}'`
	bss_size=0x`$READELF -S $VMLINUX | grep ".bss" | awk '{print $6}'`
	bss_end=`echo $bss_address $bss_size | gawk --non-decimal-data '{printf "%d\n", $1+$2}'`

	headtext_address=0x`$READELF -S $VMLINUX | grep ".head.text" |awk '{print $5}'`
	headtext_offset=0x`$READELF -S $VMLINUX | grep ".head.text" |awk '{print $6}'`

	page_offset=`echo $headtext_address $headtext_offset | gawk --non-decimal-data '{printf "%d\n", $1-$2}'`

	kernel_size=`echo $bss_end $page_offset | gawk --non-decimal-data '{printf "%d\n", $1-$2}'`

	if [ $kernel_size -gt $KERNEL_SIZE_LIMIT ]; then
		[ -f ".debug" ] || WARNING "!!! Kernel size check fail :"
		WARNING "Kernel size $kernel_size Byte exceed limitation! $KERNEL_SIZE_LIMIT Byte"
	fi
}

case "$BUILD_TARGET" in
	ALPINE)
		check_alpine_kernel_size
		;;
	*)
		;;
esac
