#!/bin/bash

DATADIR=$PWD/data
PYTHONDIR=$PWD
KERNELSRC=$1

# to keep sorting in order
export LC_ALL=C

if [ -z $KERNELSRC ]; then
	echo "give me path to Linux kernel sources:"
	echo ""
	echo "$0 path/to/Linux/kernel/sources"
	echo ""
	exit 1
fi

if [ ! -e ${KERNELSRC}/Makefile ]; then
	echo "No Makefile in $KERNELSRC directory!"
	exit 1
fi

KVER=$(make -C ${KERNELSRC} kernelversion -s)
TEMP=$(mktemp -d)

grab_syscall_names_from_tables()
{
	for tbl_file in $(find ${KERNELSRC}/arch -name syscall*.tbl)
	do
		grep -E -v "(^#|^$|sys_ni_syscall)" $tbl_file | awk '{ print $3 }' >> ${TEMP}/syscall-names.tosort
	done

	drop_bad_entries
}

grab_syscall_names_from_unistd_h()
{
	if [[ -n "$private_prefix" ]]
	then
		prefixes="(__NR|$private_prefix)"
	else
		prefixes="__NR"
	fi
	grep -E -h "^#define ${prefixes}_" ${PWD}/headers/usr/include/asm/unistd*.h \
		                    ${PWD}/headers/usr/include/asm-generic/unistd.h > ${TEMP}/syscall-names.tosort

	drop_bad_entries
}

drop_bad_entries()
{
	if [[ -n "$private_prefix" ]]
	then
		prefixes="\(__NR\|$private_prefix\)"
	else
		prefixes="__NR"
	fi
	grep -E -v "(unistd.h|NR3264|__NR_syscall|__SC_COMP|__NR_.*Linux|__NR_FAST)"  ${TEMP}/syscall-names.tosort |
	grep -E -vi "(not implemented|available|unused|reserved|xtensa|spill)" |
	grep -E -v "(__SYSCALL|SYSCALL_BASE|SYSCALL_MASK)" |
	sed -e "s/#define\s*${prefixes}_//g" -e "s/\s.*//g" |
	sort -u >${TEMP}/syscall-names.text
	cat ${DATADIR}/syscall-names.text >>${TEMP}/syscall-names.text
	grep -w -v -f ${DATADIR}/removed-names.text ${TEMP}/syscall-names.text | sort -u >${DATADIR}/syscall-names.text
}

generate_table()
{
	echo -n "$arch "
	echo $arch >> ${DATADIR}/architectures-present-in-kernel.text

	if [ $bits == 32 ]; then
		extraflags="${extraflags} -D__BITS_PER_LONG=32"
	fi

	grab_syscall_names_from_unistd_h
	generate_list_syscalls_c >${TEMP}/list-syscalls.c
	gcc list-syscalls.c -U__LP64__ -U__ILP32__ -U__i386__ -D${arch^^} \
		-D__${arch}__ ${extraflags} -I headers/usr/include/ -o list-syscalls &>/dev/null
	./list-syscalls > "${DATADIR}/tables/syscalls-$arch"
}

generate_list_syscalls_c()
{
	echo "
	#include <stdio.h>
	#include <asm/unistd.h>

	int main(void)
	{
	"

	for syscall in `cat ${DATADIR}/syscall-names.text`
	do
		echo "
	#if defined(__NR_$syscall)
		printf(\"$syscall\\t%d\\n\", __NR_$syscall);"

		if [[ -n "$private_prefix" ]]
		then
			echo "
	#elif defined(${private_prefix}_$syscall)
		printf(\"$syscall\\t%d\\n\", ${private_prefix}_$syscall);"
		fi

		echo "
	#else
		printf(\"$syscall\\n\");
	#endif
"
	done

	echo " return 0;
	}
	"
}

export_headers()
{
	make -s -C ${KERNELSRC} ARCH=${arch} O=${PWD}/headers headers_install &>/dev/null
}

do_all_tables()
{
	cd $TEMP
	rm -f ${DATADIR}/architectures-present-in-kernel.text
	mkdir headers

	for archdir in $KERNELSRC/arch/*
	do 
		arch=`basename $archdir`

		bits=64
		extraflags=

		case ${arch} in
		Kconfig)
			continue;
			;;
		um)
			continue;
			;;
		esac

		export_headers

		case ${arch} in
		arm)
			bits=32
			arch=armoabi		extraflags=			private_prefix=__ARM_NR	generate_table
			arch=arm		extraflags=-D__ARM_EABI__	private_prefix=__ARM_NR	generate_table
			;;
		loongarch)
			# 32-bit variant of loongarch may appear
			arch=loongarch64	extraflags=-D_LOONGARCH_SZLONG=64	generate_table
			;;
		mips)
			arch=mips64		extraflags=-D_MIPS_SIM=_MIPS_SIM_ABI64	generate_table
			bits=32
			arch=mipso32		extraflags=-D_MIPS_SIM=_MIPS_SIM_ABI32	generate_table
			arch=mips64n32		extraflags=-D_MIPS_SIM=_MIPS_SIM_NABI32	generate_table
			;;
		parisc)
											generate_table
			bits=64
			arch=parisc64		extraflags=-D__LP64__			generate_table
			;;
		powerpc)
											generate_table
			arch=powerpc64							generate_table
			;;
		riscv)
			arch=riscv64		extraflags=-D__LP64__			generate_table
			bits=32
			arch=riscv32		extraflags=-D__SIZEOF_POINTER__=4	generate_table
			;;
		s390)
			bits=32
											generate_table
			bits=64
			arch=s390x							generate_table
			;;
		sparc)
			bits=32
						extraflags=-D__32bit_syscall_numbers__	generate_table
			bits=64
			arch=sparc64		extraflags=-D__arch64__			generate_table
			;;
		x86)
			arch=x86_64		extraflags=-D__LP64__			generate_table
			bits=32
			arch=i386							generate_table
			arch=x32		extraflags=-D__ILP32__			generate_table
			;;
		arc|csky|hexagon|m68k|microblaze|nios2|openrisc|sh|xtensa)
			bits=32 							generate_table
			;;
		*)
			generate_table
			;;
		esac
	done

	echo ""
	cd - >/dev/null
}

create_tables_for_python()
{
	for table in data/tables/syscalls-*
	do
		tmp=${table#data/tables/}
		arch=${tmp#syscalls-}

		TARGET_FILENAME=${PYTHONDIR}/system_calls/tables/${arch}.py

		echo "# Content autogenerated. Do not edit." > $TARGET_FILENAME
		echo "" >> $TARGET_FILENAME
		echo "syscalls_${arch} = {" >> $TARGET_FILENAME
		grep -P '\t' $table | sed -e 's/^\(.*\)\t\(.*\)$/    "\1": \2,/g' >> $TARGET_FILENAME
		echo "}" >> $TARGET_FILENAME
	done

	TARGET_FILENAME=${PYTHONDIR}/system_calls/tables/names.py
	echo "# Content autogenerated. Do not edit." > $TARGET_FILENAME
	echo "syscalls_names = [" >> $TARGET_FILENAME
	sed -e 's/^\(.*\)/    "\1",/g' data/syscall-names.text >> $TARGET_FILENAME
	echo "]" >> $TARGET_FILENAME

	TARGET_FILENAME=${PYTHONDIR}/system_calls/architectures_in_kernel.py

	echo "# Content autogenerated. Do not edit." > $TARGET_FILENAME
	echo "architectures = [" >> $TARGET_FILENAME
	sed -e 's/^\(.*\)/    "\1",/g' data/architectures-present-in-kernel.text >> $TARGET_FILENAME
	echo "]" >> $TARGET_FILENAME

	echo "linux_version = \"${KVER}\"" > ${PYTHONDIR}/system_calls/linux_version.py
}

grab_syscall_names_from_tables

do_all_tables
create_tables_for_python

rm -rf $TEMP
