#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-only

# ./genconfig.sh - generate C header for setting the correct preprocessor definitions for the kernel version
# Usage ./genconfig.sh <kernel version> <make flags>
# The make flags are used for determining concurrency for the feature tests, it pulls out the value of the -j flag.
# ./genconfig.sh `uname -r` "-j4", four threads for running feature tests
# ./genconfig.sh `uname -r` "-i -j4 -d", doesn't care about other flags present

SRC_DIR=$(dirname "$0")
OUTPUT_FILE=$SRC_DIR/kernel-config.h
FEATURE_TEST_DIR="$SRC_DIR/configure-tests/feature-tests"
FEATURE_TEST_FILES="$FEATURE_TEST_DIR/*.c"
SYMBOL_TESTS_FILE="$SRC_DIR/configure-tests/symbol-tests"
KERNEL_VERSION=$(uname -r)
MAX_THREADS=$(echo "$2" | sed -E 's/.*-j\s*([0-9]+).*/\1/')
if ! [[ "$MAX_THREADS" =~ '^[0-9]+$' ]]; then # if there was no -j flag provided, default to the number of processors
	MAX_THREADS=$(getconf _NPROCESSORS_ONLN)
fi

if [ ! -z "$1" ]; then
	KERNEL_VERSION="$1"
fi

SYSTEM_MAP_FILE_VARIANTS=(
	"/lib/modules/${KERNEL_VERSION}/System.map"
	"/boot/System.map-${KERNEL_VERSION}"
	"/usr/lib/debug/boot/System.map-${KERNEL_VERSION}"
)

for FILE_VARIANT in ${SYSTEM_MAP_FILE_VARIANTS[@]}; do
	if [ -f "$FILE_VARIANT" ] && grep -q '__per_cpu_start' "$FILE_VARIANT"; then
		SYSTEM_MAP_FILE="$FILE_VARIANT"
		break
	fi
done

if [ -z "$SYSTEM_MAP_FILE" ]; then
	if [ "$(uname -r)" == "${KERNEL_VERSION}" ]; then
		echo "System.map file not found for current kernel version; using /proc/kallsyms"
		SYSTEM_MAP_FILE="/proc/kallsyms"
	else
		echo "System.map file not found for kernel version ${KERNEL_VERSION}"
		exit 1
	fi
fi

echo "using System.map file: ${SYSTEM_MAP_FILE}"
echo "generating configurations for kernel-${KERNEL_VERSION}"

rm -f $OUTPUT_FILE

echo "//The values in this file should be generated by the build process. Do not alter." >> $OUTPUT_FILE
echo "" >> $OUTPUT_FILE
echo "#ifndef DATTOBD_KERNEL_CONFIG_H" >> $OUTPUT_FILE
echo "#define DATTOBD_KERNEL_CONFIG_H" >> $OUTPUT_FILE
echo "" >> $OUTPUT_FILE

make -s -C $FEATURE_TEST_DIR clean KERNELVERSION=$KERNEL_VERSION

run_one_test() {
	local TEST="$(basename $1 .c)"
	local MACRO_NAME="HAVE_$(echo ${TEST} | awk '{print toupper($0)}')"
	local PREFIX="performing configure test: $MACRO_NAME -"
	if make -C $FEATURE_TEST_DIR TEST=$TEST KERNELVERSION=$KERNEL_VERSION &>/dev/null ; then
		echo "$PREFIX present"
		echo "#define $MACRO_NAME" >> $OUTPUT_FILE
	else
		echo "$PREFIX not present"
	fi
}
export -f run_one_test
export FEATURE_TEST_DIR
export KERNEL_VERSION
export OUTPUT_FILE

ls -1 -q $FEATURE_TEST_FILES | xargs -P "$MAX_THREADS" -d"\n" -I {} bash -c 'run_one_test {}'

make -s -C $FEATURE_TEST_DIR clean KERNELVERSION=$KERNEL_VERSION

while read SYMBOL_NAME; do
	if [ -z "$SYMBOL_NAME" ]; then
		continue
	fi

	echo "performing $SYMBOL_NAME lookup"
	MACRO_NAME="$(echo ${SYMBOL_NAME} | awk '{print toupper($0)}')_ADDR"
	SYMBOL_ADDR=$(grep " ${SYMBOL_NAME}$" "${SYSTEM_MAP_FILE}" | awk '{print $1}')
	if [ -z "$SYMBOL_ADDR" ]; then
		SYMBOL_ADDR="0"
	fi
	echo "#define $MACRO_NAME 0x$SYMBOL_ADDR" >> $OUTPUT_FILE
done < $SYMBOL_TESTS_FILE

echo "" >> $OUTPUT_FILE
echo "#endif" >> $OUTPUT_FILE
