#!/bin/sh

# Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in 
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

rm -rf build
_KVER=$1
TMPDIR1=build
MAKEFILE="${TMPDIR1}/Makefile"
TEST_H="${TMPDIR1}/test.h"
TEST_C="${TMPDIR1}/test.c"

mkdir -p $TMPDIR1

config_host_h=config-host.h

rm -rf $config_host_h

remove_test() {
   make clean >> config.log 2>&1
   cd ..
   truncate -s 0 $TEST_C
}

output_sym() {
  echo "#define $1 1" >> $config_host_h
}

fatal() {
  echo $@
  echo "Configure failed, check config.log and/or the above output"
  rm -rf $config_host_h
  exit 1
}

compile_prog() {
   cd ${TMPDIR1}
   echo $1 '\n' >> config.log
   if [ $# -gt 1 ]; then
	kflags=$2
	make $kflags >> config.log 2>&1
   else
        make >> config.log 2>&1
   fi
   ret=$?
   if [ "$ret" -eq 0 ]
   then
        echo '\n' $1 "yes" >> config.log
	echo $1 "yes"
	echo '\n' "Invoking make clean" >> config.log
        remove_test
        return 0	
   else
        echo '\n' $1 "no"  >> config.log
        echo $1 "no"
	echo '\n' "Invoking make clean" >> config.log
        remove_test
	return 1
   fi
}

echo "/*" > $config_host_h
echo " * Automatically generated by configure - do not modify" >> $config_host_h
printf " * Configured with:" >> $config_host_h
printf " * '%s'" "$0" "$@" >> $config_host_h
echo "" >> $config_host_h
echo " */" >> $config_host_h

echo "#ifndef CONFIG_HOST_H" >> $config_host_h
echo "#define CONFIG_HOST_H" >> $config_host_h

#Check if KVER is already passed, if not assign the current kernel version
if [ -z "$_KVER" ]
then
	export KVER=$(uname -r)
else
	export KVER=${_KVER}
fi
export MODULES_DIR=/lib/modules/$KVER
export KDIR=$MODULES_DIR/build

cat > $MAKEFILE <<EOF
obj-m += test.o
all:
	make -j8 -C $KDIR M=$PWD/build modules
clean:
	make -j8 -C $KDIR M=$PWD/build clean
EOF

cat > $TEST_H <<EOF
#include <linux/module.h>
#include <linux/kernel.h>

MODULE_LICENSE("GPL v2");
EOF

cat > $TEST_C <<EOF


#include "test.h"
#include <linux/file.h>

int test (void)
{
	struct fd fd1;
	struct file* f = fd1.file;
        return 0;
}

EOF
if compile_prog "checking if struct fd has file parameter..."; then
        output_sym "HAVE_STRUCT_FD_FILE_PARAM"
fi

cat > $TEST_C <<EOF


#include <linux/uaccess.h>
#include "test.h"

int test (void)
{
        access_ok(VERIFY_READ, NULL, 0);
        access_ok(VERIFY_WRITE, NULL, 0);
        return 0;
}

EOF
if compile_prog "checking if uaccess.h access_ok has 3 parameters..."; then
        output_sym "HAVE_ACCESS_OK_3_PARAMS"
fi

cat > $TEST_C <<EOF

#include <linux/uaccess.h>
#include "test.h"

int test (void)
{
        access_ok(NULL, 0);
        return 0;
}

EOF
if compile_prog "checking if uaccess.h access_ok has 2 parameters..."; then
        output_sym "HAVE_ACCESS_OK_2_PARAMS"
fi

cat > $TEST_C <<EOF
#include <linux/blkdev.h>
#include <linux/blk-mq.h>
#include "test.h"

int test (void)
{
	blk_rq_payload_bytes(NULL);
	return 0;
}

EOF
if compile_prog "Checking if blkdev.h has blk_rq_payload_bytes..."; then
	output_sym "HAVE_BLK_RQ_PAYLOAD_BYTES"
fi

cat > $TEST_C <<EOF
#include <linux/fs.h>
#include "test.h"

int test (void)
{

                call_read_iter(NULL, NULL, NULL);
                call_write_iter(NULL, NULL, NULL);
                return 0;
}

EOF
if compile_prog "Checking if fs.h has call_read_iter and call_write_iter..."; then
        output_sym "HAVE_CALL_READ_WRITE_ITER"
fi

cat > $TEST_C <<EOF
#include <linux/fs.h>
#include <linux/pagemap.h>
#include "test.h"

int test (void)
{
        filemap_range_has_page(NULL, 0, 0);
        return 0;
}

EOF
if compile_prog "Checking if fs.h has filemap_range_has_page..."; then
        output_sym "HAVE_FILEMAP_RANGE_HAS_PAGE"
fi

cat > $TEST_C <<EOF
#include <linux/security.h>
#include <linux/fs.h>
#include "test.h"

int test (void)
{
        security_file_permission(NULL, MAY_READ);
        security_file_permission(NULL, MAY_WRITE);
        return 0;
}

EOF
if grep -w "__kstrtab_security_file_permission" /boot/System.map-${KVER} >> /dev/null 2>&1 && \
	compile_prog "Checking if security_file_permission API exist..."; then
        output_sym "HAVE_SECURITY_FILE_PERMISSION"
fi

cat > $TEST_C <<EOF
#include <linux/fs.h>
#include "test.h"

int test (void)
{
        struct kiocb iocb;
        iocb.ki_complete = NULL;

        return 0;
}
EOF
if compile_prog "Checking if kiocb structue has ki_complete field... "; then
        output_sym "HAVE_KI_COMPLETE"
fi

cat > $TEST_C <<EOF
#include <linux/mm_types.h>
#include "test.h"

int test (void)
{
        vm_fault_t a;

        return 0;
}
EOF
if compile_prog "Checking if vm_fault_t exist in mm_types.h... "; then
        output_sym "HAVE_VM_FAULT"
fi

cat > $TEST_C <<EOF
#include <linux/pci.h>
#include "test.h"

int test (void)
{
        int x = PCIE_SPEED_32_0GT;

        return x;
}
EOF
if compile_prog "Checking if enum PCIE_SPEED_32_0GT exists in pci.h... "; then
        output_sym "HAVE_PCIE_SPEED_32_0GT"
fi

cat > $TEST_C <<EOF
#include <linux/pci.h>
#include "test.h"

int test (void)
{
        int x = PCIE_SPEED_64_0GT;

        return x;
}
EOF
if compile_prog "Checking if enum PCIE_SPEED_64_0GT exists in pci.h... "; then
        output_sym "HAVE_PCIE_SPEED_64_0GT"
fi

cat > $TEST_C <<EOF
#include <linux/types.h>
#include "test.h"

int test (void)
{
	atomic64_t var;
	atomic64_set(&var, 0);
	printk("%lu", atomic64_read(&var));
	return 0;
}
EOF
if compile_prog "Checking if atomic64_t counter is of type long... " "KCFLAGS=-Werror"; then
        output_sym "HAVE_ATOMIC64_LONG"
fi

cat > $TEST_C <<EOF
#include <linux/blkdev.h>
#include "test.h"

int test (void)
{
	int flags = RQF_COPY_USER;
	return flags;
}
EOF
if compile_prog "Checking if RQF_COPY_USER is present or not..."; then
        output_sym "HAVE_RQF_COPY_USER"
fi

cat > $TEST_C <<EOF
#include <linux/blkdev.h>
#include "test.h"

int test (void)
{
	struct request_queue q;
	q.dma_drain_size = 0;
	q.dma_drain_needed = NULL;
	return 0;
}
EOF
if compile_prog "Checking if dma_drain_size and dma_drain_needed are present in struct request_queue... "; then
        output_sym "HAVE_DMA_DRAIN_IN_REQUEST_QUEUE"
fi

cat > $TEST_C <<EOF
#include <linux/proc_fs.h>
#include "test.h"

int test (void)
{
	struct proc_ops ops;
	memset(&ops, 0, sizeof(struct proc_ops));
	return 0;
}
EOF
if compile_prog "Checking if struct proc_ops is present or not ..."; then
        output_sym "HAVE_STRUCT_PROC_OPS"
fi

cat > $TEST_C <<EOF
#include <linux/mm.h>
#include "test.h"

int test (void)
{
	struct vm_operations_struct vm_ops;
	vm_ops.split = NULL;
	return 0;
}
EOF
if compile_prog "Checking if split is present in vm_operations_struct  or not ..."; then
        output_sym "HAVE_VM_OPS_SPLIT"
fi
cat > $TEST_C <<EOF
#include <linux/mm.h>
#include "test.h"
int test_mremap(struct vm_area_struct *area) {
	return 0;
}
int test (void)
{
	struct vm_operations_struct vm_ops;
	vm_ops.mremap = test_mremap;
	return 0;
}
EOF
if compile_prog "Checking if mremap in vm_operations_struct has one parameter..."; then
        output_sym "HAVE_VM_OPS_MREMAP_ONE_PARAM"
fi

cat > $TEST_C <<EOF
#include <linux/mm.h>
#include "test.h"
int test_mremap(struct vm_area_struct *area, unsigned long addr) {
	return 0;
}
int test (void)
{
	struct vm_operations_struct vm_ops;
	vm_ops.mremap = test_mremap;
	return 0;
}
EOF
if compile_prog "Checking if mremap in vm_operations_struct has two parameters..."; then
        output_sym "HAVE_VM_OPS_MREMAP_TWO_PARAM"
fi

cat > $TEST_C <<EOF
#include <linux/module.h>
#include "test.h"

int test (void)
{
        mutex_lock(&module_mutex);
        mutex_unlock(&module_mutex);
        return 0;
}
EOF
if compile_prog "Checking if symbol module_mutex is present..."; then
        output_sym "HAVE_MODULE_MUTEX"
fi

cat > $TEST_C <<EOF
#include <linux/blk-integrity.h>
#include "test.h"

int test (void)
{
	blk_integrity_rq(NULL);
	return 0;
}

EOF
if compile_prog "Checking if blk-integrity.h is present..."; then
	output_sym "HAVE_BLK_INTEGRITY_H"
fi

cat > $TEST_C <<EOF
#include <linux/fs.h>
#include "test.h"

static void check_io_complete(struct kiocb *kiocb, long res, long res2) {
        return; 
}

int test (void)
{
        struct kiocb common;
        common.ki_complete = check_io_complete;
	return 0;
}
EOF
if compile_prog "Checking if KI_COMPLETE has 3 parameters ..."; then
        output_sym "KI_COMPLETE_HAS_3_PARAMETERS"
fi

cat > $TEST_C <<EOF
#include <linux/mm_types.h>
#include <linux/mm.h>
#include "test.h"

int test (void)
{
	struct temp {
		int a;
	};
	struct temp t;
	struct page *p;
	pin_user_pages_fast((unsigned long)&t, 1, 1, &p);
	return 0;
}
EOF
if compile_prog "Checking if pin_user_pages_fast symbol is present in kernel or not ..."; then
        output_sym "HAVE_PIN_USER_PAGES_FAST"
fi

cat > $TEST_C <<EOF
#include <linux/random.h>
#include "test.h"

int test (void)
{
	unsigned int temp = prandom_u32();
	return 0;
}
EOF
if compile_prog "Checking if prandom_u32 symbol is present in kernel or not ..."; then
        output_sym "HAVE_PRANDOM_U32"
fi

cat > $TEST_C <<EOF

#include "test.h"
#include <linux/device.h>
static char *temp_devnode(struct device *dev, umode_t *mode)
{
	return NULL;
}
int test (void)
{
	struct class *temp;
	temp->devnode = temp_devnode;
	return 0;
}
EOF
if compile_prog "Checking if devnode in class has doesn't have const device or not ..."; then
        output_sym "HAVE_NO_CONST_DEVICE_IN_DEVNODE"
fi

cat > $TEST_C <<EOF

#include "test.h"
#include <linux/device.h>
#include <linux/export.h>
int test (void)
{
    struct class* nvfs_class;
    nvfs_class = class_create(THIS_MODULE, "testclass");
    return 0;
}
EOF
if compile_prog "Checking if class_create has two parameters or not ..."; then
        output_sym "CLASS_CREATE_HAS_TWO_PARAMS"
fi

cat > $TEST_C <<EOF

#include "test.h"
#include <linux/mm_types.h>
int test (void)
{
    struct vm_area_struct vma;
    vma.vm_flags = 0;
    return 0;
}
EOF
if compile_prog "Checking if vma_flags are modifiable directly ..."; then
        output_sym "NVFS_VM_FLAGS_NOT_CONSTANT"
fi

cat > $TEST_C <<EOF
#include <linux/blk-mq-pci.h>
#include "test.h"

int test (void)
{
        return 0;
}

EOF
if compile_prog "Checking if blk-mq-pci.h is present..."; then
        output_sym "HAVE_BLK_MQ_PCI_H"
fi


echo "#endif" >> $config_host_h
rm -rf build

