SHELL = /bin/bash
KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build

# If CC hasn't been set explicitly, check the value of CONFIG_CC_VERSION_TEXT.
# Look for the compiler specified there, and use it by default, if found.
ifeq ($(origin CC),default)
        cc_version_text=$(firstword $(shell . $(KDIR)/.config; \
                      echo "$$CONFIG_CC_VERSION_TEXT"))

        ifneq ($(cc_version_text),)
                ifeq ($(shell command -v $(cc_version_text)),)
                        $(warning WARNING: Unable to locate the compiler $(cc_version_text) \
                        from CONFIG_CC_VERSION_TEXT in the kernel configuration.)
                else
                        CC=$(cc_version_text)
                endif
        endif
endif

ifeq ($(NVIDIA_DRIVER_DIR),)
	NV_DRIVER_VER=$(shell if [ -f /sys/module/nvidia/version ]; then cat /sys/module/nvidia/version; fi)
	ifeq ($(NV_DRIVER_VER),)
		SPATH=$(wildcard /var/lib/dkms/nvidia/*/source)
	else
		SPATH=$(wildcard /var/lib/dkms/nvidia/$(NV_DRIVER_VER)/source)
		ifeq ($(SPATH),)
			SPATH=$(wildcard /usr/src/nvidia-*)
		endif
	endif
	ifneq ($(SPATH),)
		NVIDIA_DRIVER_DIR := $(word 1, $(SPATH))
	endif
endif

ifneq ($(KVER),)
	KERNEL_UNAME := KERNEL_UNAME=$(KVER)
endif

SUDO=$(shell if [ "$(whoami)" != "root" ]; then \
                echo "sudo"; \
        fi;)
NVIDIA_OK:=$(shell if ! [[ -f $(NVIDIA_DRIVER_DIR)/nvidia/nv-p2p.h ]]; then \
		if [[ -z "$(NVIDIA_DRIVER_DIR)" ]]; then \
			echo "Please specify NVIDIA_DRIVER_DIR ex: NVIDIA_DRIVER_DIR=/usr/src/nvidia-460.32.03"; \
		else \
			echo $(NVIDIA_DRIVER_DIR) is not valid NVIDIA_DRIVER_DIR; \
		fi; \
	else \
		if ! [[ -f $(NVIDIA_DRIVER_DIR)/Module.symvers ]]; then \
			$(SUDO) ${DEVTOOLSET} $(MAKE) -C $(NVIDIA_DRIVER_DIR) $(KERNEL_UNAME) -j8; \
		fi; \
	fi;)
ifeq (,$(wildcard $(NVIDIA_DRIVER_DIR)/Module.symvers))
	$(error "Failed to prepare NVIDIA driver: " $(NVIDIA_OK))
endif

ifneq ($(KERNELRELEASE),)

# .o_<gs|rt|nort>_<fp|nofp>

OBJ_SUFFIX_1:=nort
OBJ_SUFFIX_2:=nofp

OBJDIR := $(src)/obj

quiet_cmd_copybin = CC [M]  $@
COMPAT_CONFIG_H := $(src)/compat/config.h

ifndef CONFIG_FRAME_POINTER
    ifndef CONFIG_FUNCTION_TRACER
        $(error Neither CONFIG_FUNCTION_TRACER nor CONFIG_FRAME_POINTER is defined.)
    endif
endif

ifndef CONFIG_SMP
    $(error CONFIG_SMP is not defined.)
endif

ifdef CONFIG_X86
    ifndef CONFIG_X86_64
        $(error CONFIG_X86 is defined but CONFIG_X86_64 is not defined.)
    endif
endif

ifndef CONFIG_STACKPROTECTOR
    ifndef CONFIG_CC_STACKPROTECTOR
        $(error Neither CONFIG_STACKPROTECTOR nor CONFIG_CC_STACKPROTECTOR is defined.)
    endif
endif

CANARY_IS_NOT_HARDCODED := $(shell grep -q "define HAS_HARDCODED_STACK_CANARY_GS40" ${COMPAT_CONFIG_H} || echo "y")
ifeq ($(CANARY_IS_NOT_HARDCODED),y)
    ifndef CONFIG_RETHUNK
        ifndef CONFIG_MITIGATION_RETHUNK
            ifdef CONFIG_X86_64
                $(error FIXED_stack_canary is not defined but CONFIG_RETHUNK and CONFIG_MITIGATION_RETHUNK are not defined neither.)
            endif
        endif
    endif
    OBJ_SUFFIX_1:=gs
else
    ifdef CONFIG_RETHUNK
        OBJ_SUFFIX_1:=rt
    endif
    ifdef CONFIG_MITIGATION_RETHUNK
        OBJ_SUFFIX_1:=rt
    endif
endif

ifdef CONFIG_FRAME_POINTER
    OBJ_SUFFIX_2:=fp
else
    OBJ_SUFFIX_2:=nofp
endif

cmd_copybin=
ifdef CONFIG_X86_64
    cmd_copybin = cp $(OBJDIR)/$(notdir $@)_${OBJ_SUFFIX_1}_${OBJ_SUFFIX_2} $@
else
    cmd_copybin = cp $(OBJDIR)/$(notdir $@)_arm $@
endif

ifdef CONFIG_OBJTOOL
cmd_copybin += $(cmd_objtool)
endif

GCC_VERSION_GEQ_8 := $(shell expr `gcc -dumpversion | cut -f1 -d.` \>= 8)

SUFFIX_1_EXCLUDE_FLAGS=-mfunction-return=% -mstack-protector-guard-symbol=% -mstack-protector-guard-reg=%
GS_FLAGS=-mfunction-return=thunk-extern -mstack-protector-guard-symbol=__ref_stack_chk_guard
RT_FLAGS=-mfunction-return=thunk-extern
NORT_FLAGS=

ifeq ($(GCC_VERSION_GEQ_8),1)
    GS_FLAGS += -mstack-protector-guard-reg=gs
    RT_FLAGS += -mstack-protector-guard-reg=gs
    NORT_FLAGS += -mstack-protector-guard-reg=gs
else
    $(warning "GCC version < 8,skip -mstack-protector-guard-reg flag.")
endif

FP_FILTER_OUT_FLAGS=-fno-omit-frame-pointer -fomit-frame-pointer -fno-optimize-sibling-calls
FP_FLAGS=-fno-omit-frame-pointer -fno-optimize-sibling-calls

NOFP_FILTER_OUT_FLAGS=-fno-omit-frame-pointer -fno-optimize-sibling-calls
NOFP_FLAGS=

NOSP_FILTER_OUT_FLAGS=-fstack-protector% -mstack-protector%
NOSP_FLAGS=-fno-stack-protector

$(OBJDIR)/%.o_gs_fp: $(src)/$(notdir %).c | $(OBJDIR)
	@echo MAKEOBJ_GS_FP $@
	$(CC) $(filter-out $(SUFFIX_1_EXCLUDE_FLAGS) $(FP_FILTER_OUT_FLAGS),$(c_flags)) $(GS_FLAGS) $(FP_FLAGS) -c -o $@ $<

$(OBJDIR)/%.o_rt_fp: $(src)/$(notdir %).c | $(OBJDIR)
	@echo MAKEOBJ_RT_FP $@
	$(CC) $(filter-out $(SUFFIX_1_EXCLUDE_FLAGS) $(FP_FILTER_OUT_FLAGS),$(c_flags)) $(RT_FLAGS) $(FP_FLAGS) -c -o $@ $<

$(OBJDIR)/%.o_nort_fp: $(src)/$(notdir %).c | $(OBJDIR)
	@echo MAKEOBJ_NORT_FP $@
	$(CC) $(filter-out $(SUFFIX_1_EXCLUDE_FLAGS) $(FP_FILTER_OUT_FLAGS),$(c_flags)) $(NORT_FLAGS) $(FP_FLAGS) -c -o $@ $<

$(OBJDIR)/%.o_gs_nofp: $(src)/$(notdir %).c | $(OBJDIR)
	@echo MAKEOBJ_GS_NOFP $@
	$(CC) $(filter-out $(SUFFIX_1_EXCLUDE_FLAGS) $(NOFP_FILTER_OUT_FLAGS),$(c_flags)) $(GS_FLAGS) $(NOFP_FLAGS) -c -o $@ $<

$(OBJDIR)/%.o_rt_nofp: $(src)/$(notdir %).c | $(OBJDIR)
	@echo MAKEOBJ_RT_NOFP $@
	$(CC) $(filter-out $(SUFFIX_1_EXCLUDE_FLAGS) $(NOFP_FILTER_OUT_FLAGS),$(c_flags)) $(RT_FLAGS) $(NOFP_FLAGS) -c -o $@ $<

$(OBJDIR)/%.o_nort_nofp: $(src)/$(notdir %).c | $(OBJDIR)
	@echo MAKEOBJ_NORT_NOFP $@
	$(CC) $(filter-out $(SUFFIX_1_EXCLUDE_FLAGS) $(NOFP_FILTER_OUT_FLAGS),$(c_flags)) $(NORT_FLAGS) $(NOFP_FLAGS) -c -o $@ $<

$(OBJDIR)/%.o_arm: $(src)/$(notdir %).c | $(OBJDIR)
	@echo MAKEOBJ_ARM $@
	$(CC) $(filter-out $(NOSP_FILTER_OUT_FLAGS),$(c_flags)) $(NOSP_FLAGS) -c -o $@ $<

$(OBJDIR):
	mkdir $(OBJDIR)

PREQ_TEMPLATE := PREQ_PLACEHOLDER.o_arm
ifdef CONFIG_X86_64
    PREQ_TEMPLATE := PREQ_PLACEHOLDER.o_nort_fp PREQ_PLACEHOLDER.o_nort_nofp PREQ_PLACEHOLDER.o_rt_fp PREQ_PLACEHOLDER.o_rt_nofp
    ifeq ($(GCC_VERSION_GEQ_8),1)
        PREQ_TEMPLATE := ${PREQ_TEMPLATE} PREQ_PLACEHOLDER.o_gs_fp PREQ_PLACEHOLDER.o_gs_nofp
    else
        $(warning "GCC version < 8, will not compile _gs objects.")
    endif
endif

BOUNDED_POOL_PREQ := $(subst PREQ_PLACEHOLDER,$(OBJDIR)/bounded_pool,$(PREQ_TEMPLATE))
CPU_ARCH_PREQ := $(subst PREQ_PLACEHOLDER,$(OBJDIR)/cpu_arch,$(PREQ_TEMPLATE))
CPU_PJOB_PREQ := $(subst PREQ_PLACEHOLDER,$(OBJDIR)/cpu-parity-job,$(PREQ_TEMPLATE))
RANGE_LOCK_PREQ := $(subst PREQ_PLACEHOLDER,$(OBJDIR)/range_lock,$(PREQ_TEMPLATE))

$(obj)/bounded_pool.o: $(BOUNDED_POOL_PREQ) FORCE
	$(call if_changed,copybin)

$(obj)/cpu_arch.o: $(CPU_ARCH_PREQ) FORCE
	$(call if_changed,copybin)

$(obj)/cpu-parity-job.o: $(CPU_PJOB_PREQ) FORCE
	$(call if_changed,copybin)

$(obj)/range_lock.o: $(RANGE_LOCK_PREQ) FORCE
	$(call if_changed,copybin)

ifeq ($(DEBUG), 1)
DEBUG_CFLAGS = -DDEBUG -g
endif

RHEL_DRM_VERSION=$(shell eval $$(grep RHEL_DRM_ /lib/modules/$(KERNELRELEASE)/build/Makefile|sed 's/ //g'); expr 0$${RHEL_DRM_VERSION} \* 65536 + 0$${RHEL_DRM_PATCHLEVEL} \* 256 + 0$${RHEL_DRM_SUBLEVEL})
RHEL_MAJOR=$(shell eval $$(grep ^RHEL_ /lib/modules/$(KERNELRELEASE)/build/Makefile|sed 's/ //g'); expr 0 + 0$${RHEL_MAJOR})

ifneq ($(RHEL_DRM_VERSION),0)
COMPAT_CFLAGS := -DRHEL_DRM_VERSION_CODE=$(RHEL_DRM_VERSION)
endif

GCC_CHK_FALLTHRU=$(shell expr `$(CC) -dumpversion | cut -f1 -d.` \>= 7)
ifeq ($(GCC_CHK_FALLTHRU),1)
EXTRA_CFLAGS += -Wimplicit-fallthrough=2
endif

EXTRA_CFLAGS += -I$(PWD)/../../common -Wall -Werror -O3 ${DEBUG_CFLAGS}
ifdef CONFIG_X86
EXTRA_CFLAGS += -mpopcnt
endif
ifdef CONFIG_ARM64
EXTRA_CFLAGS += -mno-outline-atomics
endif

ccflags-y += ${EXTRA_CFLAGS}

KBUILD_EXTRA_SYMBOLS=$(NVIDIA_DRIVER_DIR)/Module.symvers

CFLAGS_virtual-nvme.o += $(DEBUG_CFLAGS)
CFLAGS_module.o += $(DEBUG_CFLAGS)
CFLAGS_core.o += $(COMPAT_CFLAGS)
CFLAGS_dma.o += $(COMPAT_CFLAGS)

graid-objs := blk-forward.o cache.o core.o dma.o gpu.o module.o page_mapping.o \
	      pci-nvme.o user.o virtual-nvme.o graid-atomic.o graid-page-pool.o \
	      bounded_pool.o cpu_arch.o cpu-parity-job.o range_lock.o \
	      stripecache/stripecache.o \
	      stripecache/stripecache-log.o \
	      stripecache/stripecache-resend.o \
	      sraid.o

obj-m := graid.o

CFLAGS_gpu_nvidia.o += -I$(NVIDIA_DRIVER_DIR)/nvidia

graid-nvidia-objs := gpu_nvidia.o

obj-m += graid-nvidia.o

else # normal makefile

default:
	$(MAKE) -C $(KDIR) M=$$PWD NVIDIA_DRIVER_DIR=$(NVIDIA_DRIVER_DIR) CC=$(CC)

install:
	$(MAKE) -C $(KDIR) M=$$PWD NVIDIA_DRIVER_DIR=$(NVIDIA_DRIVER_DIR) CC=$(CC) modules_install
	depmod -a

clean:
	rm -rf *.mod.c *.mod.o *.o *.ko modules.order Module.symvers .*.ko.cmd .*.mod.o.cmd .*.o.cmd .*.o.d .tmp_versions .built-in.a.cmd .graid.mod.cmd built-in.a graid.mod stripecache/.*.cmd stripecache/*.o

.PHONEY: clean

endif

