// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 */

#ifndef __POINTER_AUTH_CONTEXT_H
#define __POINTER_AUTH_CONTEXT_H

struct pt_regs;

enum pac_pt_regs {
	REGS_X16 = 0,
	REGS_X17,
	REGS_LR,
	REGS_SP,
	REGS_PC,
	REGS_PSTATE,
};

void sign_thread_context(void *cpu_context);
void auth_thread_context(void *cpu_context);

void sign_exception_context_asm(void *regs);
void auth_exception_context_asm(void *regs);

int set_exception_context_register_asm(void *regs, int offset, u64 val);

#ifdef CONFIG_COMPAT
void sign_compat_exception_context_asm(void *regs);
void auth_compat_exception_context_asm(void *regs);

int set_compat_exception_context_register_asm(void *regs, int offset, u64 val);
#else
static inline void sign_compat_exception_context_asm(void *regs)
{
}

static inline void auth_compat_exception_context_asm(void *regs)
{
}

static inline int set_compat_exception_context_register_asm(void *regs, int offset, u64 val)
{
	return 0;
}
#endif

static inline void sign_compat_exception_context_unsafe(void *regs)
{
	return sign_compat_exception_context_asm(regs);
}

static inline void auth_compat_exception_context_unsafe(void *regs)
{
	return auth_compat_exception_context_asm(regs);
}

static inline void sign_exception_context_unsafe(void *regs)
{
	if (compat_user_mode((struct pt_regs *)regs)) {
		sign_compat_exception_context_asm(regs);
	} else {
		sign_exception_context_asm(regs);
	}
}

static inline void auth_exception_context_unsafe(void *regs)
{
	if (compat_user_mode((struct pt_regs *)regs)) {
		auth_compat_exception_context_asm(regs);
	} else {
		auth_exception_context_asm(regs);
	}
}

#define resign_compat_exception_context_start(regs)		\
do {								\
	unsigned long irq_flags;				\
	local_irq_save(irq_flags);				\
	auth_compat_exception_context_asm(regs);

#define resign_compat_exception_context_end(regs)		\
	sign_compat_exception_context_asm(regs);		\
	local_irq_restore(irq_flags);				\
} while (0)

#define resign_exception_context_start(regs)			\
do {								\
	unsigned long irq_flags;				\
	local_irq_save(irq_flags);				\
	auth_exception_context_unsafe(regs);

#define resign_exception_context_end(regs)			\
	sign_exception_context_unsafe(regs);				\
	local_irq_restore(irq_flags);				\
} while (0)

#define sign_exception_context_start(regs)			\
do {								\
	unsigned long irq_flags;				\
	local_irq_save(irq_flags);

#define sign_exception_context_end(regs)			\
	sign_exception_context_unsafe(regs);				\
	local_irq_restore(irq_flags);				\
} while (0)

void sign_compat_exception_context(void *regs);
void auth_compat_exception_context(void *regs);
void sign_exception_context(void *regs);
void auth_exception_context(void *regs);

int set_compat_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val);
void set_compat_exception_context_register_index(struct pt_regs *regs, int index, u64 val);
int set_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val);
void set_exception_context_register_index(struct pt_regs *regs, int index, u64 val);

#endif /* __POINTER_AUTH_CONTEXT_H */