#ifndef __KPATCH_SYSCALL_H_
#define __KPATCH_SYSCALL_H_

#include "kpatch-macros.h"

/*
 * These kpatch-specific syscall definition macros can be used for patching a
 * syscall.
 *
 * Attempting to patch a syscall typically results in an error, due to a
 * missing fentry hook in the inner __do_sys##name() function.  The fentry hook
 * is missing because of the 'inline' annotation, which invokes 'notrace'.
 *
 * These macros are copied almost verbatim from the kernel, the main difference
 * being a 'kpatch' prefix added to the __do_sys##name() function name.  This
 * causes kpatch-build to treat it as a new function (due to
 * its new name), and its caller __se_sys##name() function is inlined by its own
 * caller __x64_sys##name() function, which has an fentry hook.

 * To patch a syscall, just replace the use of the SYSCALL_DEFINE1 (or similar)
 * macro with the "KPATCH_" prefixed version.
 */

#define KPATCH_IGNORE_SYSCALL_SECTIONS					\
	KPATCH_IGNORE_SECTION("__syscalls_metadata");			\
	KPATCH_IGNORE_SECTION("_ftrace_events")

#define KPATCH_SYSCALL_DEFINE1(name, ...) KPATCH_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define KPATCH_SYSCALL_DEFINE2(name, ...) KPATCH_SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define KPATCH_SYSCALL_DEFINE3(name, ...) KPATCH_SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define KPATCH_SYSCALL_DEFINE4(name, ...) KPATCH_SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define KPATCH_SYSCALL_DEFINE5(name, ...) KPATCH_SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define KPATCH_SYSCALL_DEFINE6(name, ...) KPATCH_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)

#define KPATCH_SYSCALL_DEFINEx(x, sname, ...)				\
	KPATCH_IGNORE_SYSCALL_SECTIONS;					\
	__KPATCH_SYSCALL_DEFINEx(x, sname, __VA_ARGS__)

#ifdef CONFIG_X86_64

/* arch/x86/include/asm/syscall_wrapper.h versions */

# if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)

#  define __KPATCH_SYSCALL_DEFINEx(x, name, ...)			\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
	__X64_SYS_STUBx(x, name, __VA_ARGS__)				\
	__IA32_SYS_STUBx(x, name, __VA_ARGS__)				\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)

#  define __KPATCH_SYSCALL_DEFINEx(x, name, ...)			\
	asmlinkage long __x64_sys##name(const struct pt_regs *regs);	\
	ALLOW_ERROR_INJECTION(__x64_sys##name, ERRNO);			\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
	asmlinkage long __x64_sys##name(const struct pt_regs *regs)	\
	{								\
		return __se_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\
	}								\
	__IA32_SYS_STUBx(x, name, __VA_ARGS__)				\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

# endif /* LINUX_VERSION_CODE */

#elif defined(CONFIG_S390)

/* arch/s390/include/asm/syscall_wrapper.h versions */
# if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)

#define __KPATCH_S390_SYS_STUBx(x, name, ...)                                          \
	long __s390_sys##name(struct pt_regs *regs);                            \
	ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO);                         \
	static inline long ___se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__));   \
	long __s390_sys##name(struct pt_regs *regs)                             \
	{                                                                       \
		return ___se_sys##name(SC_S390_REGS_TO_ARGS(x, __VA_ARGS__));   \
	}                                                                       \
	static inline long ___se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__))    \
	{                                                                       \
		__MAP(x, __SC_TEST, __VA_ARGS__);                               \
		return __kpatch_do_sys##name(__MAP(x, __SC_COMPAT_CAST, __VA_ARGS__)); \
	}

#define __KPATCH_SYSCALL_DEFINEx(x, name, ...)					\
       long __s390x_sys##name(struct pt_regs *regs);                           \
       ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO);                        \
       static inline long __se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__));    \
       static inline long __kpatch_do_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__));    \
       __KPATCH_S390_SYS_STUBx(x, name, __VA_ARGS__);                                 \
       long __s390x_sys##name(struct pt_regs *regs)                            \
       {                                                                       \
               return __se_sys##name(SC_S390_REGS_TO_ARGS(x, __VA_ARGS__));    \
       }                                                                       \
       static inline long __se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__))     \
       {                                                                       \
               __MAP(x, __SC_TEST, __VA_ARGS__);                               \
               return __kpatch_do_sys##name(__MAP(x, __SC_CAST, __VA_ARGS__));        \
       }                                                                       \
       static inline long __kpatch_do_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__))
# else

#define __KPATCH_S390_SYS_STUBx(x, name, ...)					\
	long __s390_sys##name(struct pt_regs *regs);				\
	ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO);				\
	long __s390_sys##name(struct pt_regs *regs)				\
	{									\
		long ret = __kpatch_do_sys##name(SYSCALL_PT_ARGS(x, regs,	\
			__SC_COMPAT_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__)));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);					\
		return ret;							\
	}

#define __KPATCH_SYSCALL_DEFINEx(x, name, ...)						\
	__diag_push();									\
	__diag_ignore(GCC, 8, "-Wattribute-alias",					\
		      "Type aliasing is used to sanitize syscall arguments");		\
	long __s390x_sys##name(struct pt_regs *regs)					\
		__attribute__((alias(__stringify(__se_sys##name))));			\
	ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO);				\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	long __se_sys##name(struct pt_regs *regs);					\
	__KPATCH_S390_SYS_STUBx(x, name, __VA_ARGS__)					\
	long __se_sys##name(struct pt_regs *regs)					\
	{										\
		long ret = __kpatch_do_sys##name(SYSCALL_PT_ARGS(x, regs,		\
				    __SC_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__)));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);						\
		return ret;								\
	}										\
	__diag_pop();									\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

#endif /* LINUX_VERSION_CODE */

#elif defined(CONFIG_PPC64)

/* arch/powerpc/include/asm/syscall_wrapper.h versions */

# if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)

#  define __KPATCH_SYSCALL_DEFINEx(x, name, ...)			\
	long sys##name(const struct pt_regs *regs);			\
	ALLOW_ERROR_INJECTION(sys##name, ERRNO);			\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));		\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
	__attribute__((optimize("-fno-optimize-sibling-calls")))		\
	long sys##name(const struct pt_regs *regs)			\
	{									\
		return __se_sys##name(SC_POWERPC_REGS_TO_ARGS(x,__VA_ARGS__));	\
	}									\
	static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))		\
	{									\
		long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
		__MAP(x,__SC_TEST,__VA_ARGS__);					\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));		\
		return ret;							\
	}									\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

# endif /* LINUX_VERSION_CODE */

#endif /* CONFIG_X86_64 */


#ifndef __KPATCH_SYSCALL_DEFINEx

/* include/linux/syscalls.h versions */

# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
#  define __KPATCH_SYSCALL_DEFINEx(x, name, ...)			\
	__diag_push();							\
	__diag_ignore(GCC, 8, "-Wattribute-alias",			\
		      "Type aliasing is used to sanitize syscall arguments");\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))	\
		__attribute__((alias(__stringify(__se_sys##name))));	\
	ALLOW_ERROR_INJECTION(sys##name, ERRNO);			\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
	asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	asmlinkage long __attribute__((optimize("-fno-optimize-sibling-calls")))\
			__se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	__diag_pop();							\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
#  define __KPATCH_SYSCALL_DEFINEx(x, name, ...)			\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))	\
		__attribute__((alias(__stringify(__se_sys##name))));	\
	ALLOW_ERROR_INJECTION(sys##name, ERRNO);			\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\
	asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = __kpatch_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	static inline long __kpatch_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))

# else
#  define __KPATCH_SYSCALL_DEFINEx(x, name, ...)			\
	asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))	\
		__attribute__((alias(__stringify(SyS##name))));		\
	static inline long __kpatch_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));	\
	asmlinkage long SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))	\
	{								\
		long ret = __kpatch_SYSC##name(__MAP(x,__SC_CAST,__VA_ARGS__));	\
		__MAP(x,__SC_TEST,__VA_ARGS__);				\
		__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__));	\
		return ret;						\
	}								\
	static inline long __kpatch_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))

# endif

#endif /* __KPATCH_SYSCALL_DEFINEx */

#endif /* __KPATCH_SYSCALL_H_ */
