#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# Copyright (C) 2025, Advanced Micro Devices, Inc. All rights reserved.

# Generate driver/amdxdna/config_kernel.h by feature-testing the kernel headers.

set -e

# ---- Locate kernel source tree -----------------------------------------

# Priority:
#   1) $KERNEL_VER (Can set this from CMake)
#   2) $kernelver (DKMS gives this)
#   3) $(uname -r) (Fall back to current running kernel)
KERNEL_VER="${KERNEL_VER:-${kernelver:-$(uname -r)}}"

KERNEL_SRC="${KERNEL_SRC:-/lib/modules/${KERNEL_VER}/build}"
if [ ! -d "$KERNEL_SRC/include/linux" ]; then
    echo "ERROR: Cannot find kernel headers under $KERNEL_SRC" >&2
    exit 1
fi

# ---- Output header path ------------------------------------------------

OUT="driver/amdxdna/config_kernel.h"

# ---- Helper: try to compile a small snippet ----------------------------

# Usage:
#   try_compile MACRO_NAME << 'EOF'
#   #include <linux/...>
#   int main(void) { ...; return 0; }
#   EOF
#
# The code snippet is compiled as a out-of-tree kernel module.
# If compilation succeeds, we add "#define MACRO_NAME 1" to the header.

try_compile() {
    macro="$1"
    shift

    tmpdir=$(mktemp -d /tmp/conftest-XXXXXX)
    conftest_c="$tmpdir/conftest.c"
    conftest_mk="$tmpdir/Makefile"

    # Minimal Kbuild for an external module
    cat > "$conftest_mk" <<EOF
obj-m := conftest.o
EOF

    # Preamble: module metadata to satisfy modpost
    cat > "$conftest_c" <<EOF
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("amdxdna conftest");
EOF

    # Append the actual test code from stdin
    cat >> "$conftest_c"

    # Now build it like your real driver
    if make -s -C "$KERNEL_SRC" M="$tmpdir"  modules >/dev/null 2>&1; then
        echo "#define $macro 1" >> "$OUT"
        echo ">>>  + $macro: yes" >&2
    else
        echo ">>>  - $macro: no" >&2
    fi

    rm -rf "$tmpdir"
}

# ---- Write header preamble ---------------------------------------------

cat > "$OUT" <<EOF
/* Auto-generated by configure_kernel.sh. Do not edit. */

#ifndef AMDXDNA_CONFIG_KERNEL_H
#define AMDXDNA_CONFIG_KERNEL_H

/* Detected kernel features: */

EOF

echo ">>> Probing kernel features in $KERNEL_SRC..." >&2

#
# Add/remove tests here as needed.
# Please test it manually before adding the code snippet here.
#
# To test manually, simply create a directory under /tmp. Add two files under it as below:
#
# 1. Makefile with content:
# obj-m := test.o
# 2. test.c with content:
# #include <linux/module.h>
# MODULE_LICENSE("GPL");
# MODULE_DESCRIPTION("amdxdna conftest");
# <your-test-code-snippet-here>
#
# Run make -s -C /lib/modules/`uname -r`/build M=<your-directory> modules
# Make sure you see expected behavior with the compilation.
#

# Test drm_sched_job_init() signature in 6.17+:
# int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity,
#		         u32 credits, void *owner, u64 drm_client_id);
try_compile HAVE_6_17_drm_sched_job_init << 'EOF'
#include <drm/gpu_scheduler.h>
int main(void)
{
	struct drm_sched_job *a = NULL;
	struct drm_sched_entity *b = NULL;
	u32 c = 0;
	void *d = NULL;
	u64 e = 0;

	(void)drm_sched_job_init(a, b, c, d, e);
	return 0;
}
EOF

# Test drm_sched_init() signature in 6.15+:
# int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_args *args)
try_compile HAVE_6_15_drm_sched_init << 'EOF'
#include <drm/gpu_scheduler.h>
int main(void)
{
	struct drm_gpu_scheduler *a = NULL;
	struct drm_sched_init_args *b = NULL;

	(void)drm_sched_init(a, b);
	return 0;
}
EOF

# Test iommu_dev_enable_feature()/iommu_dev_disable_feature() signature in 6.15-:
# int iommu_dev_disable_feature(struct device *dev, enum iommu_dev_features f)
# int iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features f)
try_compile HAVE_iommu_dev_enable_disable_feature << 'EOF'
#include <linux/iommu.h>
int main(void)
{
	struct device *a = NULL;
	enum iommu_dev_features b = 0;

	(void)iommu_dev_enable_feature(a, b);
	(void)iommu_dev_disable_feature(a, b);
	return 0;
}
EOF

# Test device_iommu_mapped() signature in 6.13+:
# static inline bool device_iommu_mapped(struct device *dev)
try_compile HAVE_device_iommu_mapped << 'EOF'
#include <linux/device.h>
int main(void)
{
	struct device *a = NULL;

	(void)device_iommu_mapped(a);
	return 0;
}
EOF

# Test system_percpu_wq in 6.17+:
# struct workqueue_struct *system_percpu_wq
try_compile HAVE_system_percpu_wq << 'EOF'
#include <linux/workqueue.h>
int main(void)
{
	struct work_struct *a = NULL;

	queue_work(system_percpu_wq, a);
	return 0;
}
EOF

# Test MODULE_IMPORT_NS signature in 6.13+:
# #define MODULE_IMPORT_NS(ns)
try_compile HAVE_6_13_MODULE_IMPORT_NS << 'EOF'
#include <linux/module.h>
#include <linux/dma-buf.h>
int main(void)
{
	MODULE_IMPORT_NS("DMA_BUF");

	struct dma_buf *a = NULL;
	struct iosys_map *b = NULL;
	(void)dma_buf_vmap(a, b);
	return 0;
}
EOF

# Test drm_gem_vmap()/drm_gem_vunmap signature in 6.16+:
# int drm_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map)
# void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
try_compile HAVE_drm_gem_vmap_vunmap << 'EOF'
#include <drm/drm_gem.h>
int main(void)
{
	struct drm_gem_object *a = NULL;
	struct iosys_map *b = NULL;

	(void)drm_gem_vmap(a, b);
	(void)drm_gem_vunmap(a, b);
	return 0;
}
EOF

# Test dma_buf_ops->cache_sgt_mapping in 6.15-:
# struct dma_buf_ops {
#         bool cache_sgt_mapping;
#         ...
# }
try_compile HAVE_cache_sgt_mapping << 'EOF'
#include <linux/dma-buf.h>
int main(void)
{
	const struct dma_buf_ops amdxdna_dmabuf_ops = {
		.cache_sgt_mapping = true,
	};

	return 0;
}
EOF

# ---- Header trailer ----------------------------------------------------

cat >> "$OUT" <<EOF

#endif /* AMDXDNA_CONFIG_KERNEL_H */
EOF

#echo ">>> $(pwd)/$OUT is generated" >&2
