/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 */
#ifndef _ASM_ELF_H
#define _ASM_ELF_H

#include <linux/auxvec.h>
#include <linux/fs.h>
#include <uapi/linux/elf.h>

#include <asm/current.h>
#include <asm/vdso.h>

/* The ABI of a file. */
#define EF_LOONGARCH_ABI_LP64_SOFT_FLOAT	0x1
#define EF_LOONGARCH_ABI_LP64_SINGLE_FLOAT	0x2
#define EF_LOONGARCH_ABI_LP64_DOUBLE_FLOAT	0x3

#define EF_LOONGARCH_ABI_ILP32_SOFT_FLOAT	0x5
#define EF_LOONGARCH_ABI_ILP32_SINGLE_FLOAT	0x6
#define EF_LOONGARCH_ABI_ILP32_DOUBLE_FLOAT	0x7

/* LoongArch relocation types used by the dynamic linker */
#define R_LARCH_NONE				0
#define R_LARCH_32				1
#define R_LARCH_64				2
#define R_LARCH_RELATIVE			3
#define R_LARCH_COPY				4
#define R_LARCH_JUMP_SLOT			5
#define R_LARCH_TLS_DTPMOD32			6
#define R_LARCH_TLS_DTPMOD64			7
#define R_LARCH_TLS_DTPREL32			8
#define R_LARCH_TLS_DTPREL64			9
#define R_LARCH_TLS_TPREL32			10
#define R_LARCH_TLS_TPREL64			11
#define R_LARCH_IRELATIVE			12
#define R_LARCH_MARK_LA				20
#define R_LARCH_MARK_PCREL			21
#define R_LARCH_SOP_PUSH_PCREL			22
#define R_LARCH_SOP_PUSH_ABSOLUTE		23
#define R_LARCH_SOP_PUSH_DUP			24
#define R_LARCH_SOP_PUSH_GPREL			25
#define R_LARCH_SOP_PUSH_TLS_TPREL		26
#define R_LARCH_SOP_PUSH_TLS_GOT		27
#define R_LARCH_SOP_PUSH_TLS_GD			28
#define R_LARCH_SOP_PUSH_PLT_PCREL		29
#define R_LARCH_SOP_ASSERT			30
#define R_LARCH_SOP_NOT				31
#define R_LARCH_SOP_SUB				32
#define R_LARCH_SOP_SL				33
#define R_LARCH_SOP_SR				34
#define R_LARCH_SOP_ADD				35
#define R_LARCH_SOP_AND				36
#define R_LARCH_SOP_IF_ELSE			37
#define R_LARCH_SOP_POP_32_S_10_5		38
#define R_LARCH_SOP_POP_32_U_10_12		39
#define R_LARCH_SOP_POP_32_S_10_12		40
#define R_LARCH_SOP_POP_32_S_10_16		41
#define R_LARCH_SOP_POP_32_S_10_16_S2		42
#define R_LARCH_SOP_POP_32_S_5_20		43
#define R_LARCH_SOP_POP_32_S_0_5_10_16_S2	44
#define R_LARCH_SOP_POP_32_S_0_10_10_16_S2	45
#define R_LARCH_SOP_POP_32_U			46
#define R_LARCH_ADD8				47
#define R_LARCH_ADD16				48
#define R_LARCH_ADD24				49
#define R_LARCH_ADD32				50
#define R_LARCH_ADD64				51
#define R_LARCH_SUB8				52
#define R_LARCH_SUB16				53
#define R_LARCH_SUB24				54
#define R_LARCH_SUB32				55
#define R_LARCH_SUB64				56
#define R_LARCH_GNU_VTINHERIT			57
#define R_LARCH_GNU_VTENTRY			58
#define R_LARCH_B16				64
#define R_LARCH_B21				65
#define R_LARCH_B26				66
#define R_LARCH_ABS_HI20			67
#define R_LARCH_ABS_LO12			68
#define R_LARCH_ABS64_LO20			69
#define R_LARCH_ABS64_HI12			70
#define R_LARCH_PCALA_HI20			71
#define R_LARCH_PCALA_LO12			72
#define R_LARCH_PCALA64_LO20			73
#define R_LARCH_PCALA64_HI12			74
#define R_LARCH_GOT_PC_HI20			75
#define R_LARCH_GOT_PC_LO12			76
#define R_LARCH_GOT64_PC_LO20			77
#define R_LARCH_GOT64_PC_HI12			78
#define R_LARCH_GOT_HI20			79
#define R_LARCH_GOT_LO12			80
#define R_LARCH_GOT64_LO20			81
#define R_LARCH_GOT64_HI12			82
#define R_LARCH_TLS_LE_HI20			83
#define R_LARCH_TLS_LE_LO12			84
#define R_LARCH_TLS_LE64_LO20			85
#define R_LARCH_TLS_LE64_HI12			86
#define R_LARCH_TLS_IE_PC_HI20			87
#define R_LARCH_TLS_IE_PC_LO12			88
#define R_LARCH_TLS_IE64_PC_LO20		89
#define R_LARCH_TLS_IE64_PC_HI12		90
#define R_LARCH_TLS_IE_HI20			91
#define R_LARCH_TLS_IE_LO12			92
#define R_LARCH_TLS_IE64_LO20			93
#define R_LARCH_TLS_IE64_HI12			94
#define R_LARCH_TLS_LD_PC_HI20			95
#define R_LARCH_TLS_LD_HI20			96
#define R_LARCH_TLS_GD_PC_HI20			97
#define R_LARCH_TLS_GD_HI20			98
#define R_LARCH_32_PCREL			99
#define R_LARCH_RELAX				100

#ifndef ELF_ARCH

/* ELF register definitions */

/*
 * General purpose have the following registers:
 *	Register	Number
 *	GPRs		32
 *	ORIG_A0		1
 *	ERA		1
 *	BADVADDR	1
 *	CRMD		1
 *	PRMD		1
 *	EUEN		1
 *	ECFG		1
 *	ESTAT		1
 *	Reserved	5
 */
#define ELF_NGREG	45

/*
 * Floating point have the following registers:
 *	Register	Number
 *	FPR		32
 *	FCC		1
 *	FCSR		1
 */
#define ELF_NFPREG	34

typedef unsigned long elf_greg_t;
typedef elf_greg_t elf_gregset_t[ELF_NGREG];

typedef double elf_fpreg_t;
typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];

void loongarch_dump_regs64(u64 *uregs, const struct pt_regs *regs);

#ifdef CONFIG_32BIT
/*
 * This is used to ensure we don't load something for the wrong architecture.
 */
#define elf_check_arch elf32_check_arch

/*
 * These are used to set parameters in the core dumps.
 */
#define ELF_CLASS	ELFCLASS32

#define ELF_CORE_COPY_REGS(dest, regs) \
	loongarch_dump_regs32((u32 *)&(dest), (regs));

#endif /* CONFIG_32BIT */

#ifdef CONFIG_64BIT
/*
 * This is used to ensure we don't load something for the wrong architecture.
 */
#define elf_check_arch elf64_check_arch

/*
 * These are used to set parameters in the core dumps.
 */
#define ELF_CLASS	ELFCLASS64

#define ELF_CORE_COPY_REGS(dest, regs) \
	loongarch_dump_regs64((u64 *)&(dest), (regs));

#endif /* CONFIG_64BIT */

/*
 * These are used to set parameters in the core dumps.
 */
#define ELF_DATA	ELFDATA2LSB
#define ELF_ARCH	EM_LOONGARCH

#endif /* !defined(ELF_ARCH) */

#define loongarch_elf_check_machine(x) ((x)->e_machine == EM_LOONGARCH)

#define vmcore_elf32_check_arch loongarch_elf_check_machine
#define vmcore_elf64_check_arch loongarch_elf_check_machine

/*
 * Return non-zero if HDR identifies an 32bit ELF binary.
 */
#define elf32_check_arch(hdr)						\
({									\
	int __res = 1;							\
	struct elfhdr *__h = (hdr);					\
									\
	if (!loongarch_elf_check_machine(__h))				\
		__res = 0;						\
	if (__h->e_ident[EI_CLASS] != ELFCLASS32)			\
		__res = 0;						\
									\
	__res;								\
})

/*
 * Return non-zero if HDR identifies an 64bit ELF binary.
 */
#define elf64_check_arch(hdr)						\
({									\
	int __res = 1;							\
	struct elfhdr *__h = (hdr);					\
									\
	if (!loongarch_elf_check_machine(__h))				\
		__res = 0;						\
	if (__h->e_ident[EI_CLASS] != ELFCLASS64)			\
		__res = 0;						\
									\
	__res;								\
})

#ifdef CONFIG_32BIT

#define SET_PERSONALITY2(ex, state)					\
do {									\
	current->thread.vdso = &vdso_info;				\
									\
	loongarch_set_personality_fcsr(state);				\
									\
	if (personality(current->personality) != PER_LINUX)		\
		set_personality(PER_LINUX);				\
} while (0)

#endif /* CONFIG_32BIT */

#ifdef CONFIG_64BIT

#define SET_PERSONALITY2(ex, state)					\
do {									\
	unsigned int p;							\
									\
	clear_thread_flag(TIF_32BIT_REGS);				\
	clear_thread_flag(TIF_32BIT_ADDR);				\
									\
	current->thread.vdso = &vdso_info;				\
	loongarch_set_personality_fcsr(state);				\
									\
	p = personality(current->personality);				\
	if (p != PER_LINUX32 && p != PER_LINUX)				\
		set_personality(PER_LINUX);				\
} while (0)

#endif /* CONFIG_64BIT */

#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE	PAGE_SIZE

/*
 * This yields a mask that user programs can use to figure out what
 * instruction set this cpu supports. This could be done in userspace,
 * but it's not easy, and we've already done it here.
 */

#define ELF_HWCAP	(elf_hwcap)
extern unsigned int elf_hwcap;
#include <asm/hwcap.h>

/*
 * This yields a string that ld.so will use to load implementation
 * specific libraries for optimization.	 This is more specific in
 * intent than poking at uname or /proc/cpuinfo.
 */

#define ELF_PLATFORM  __elf_platform
extern const char *__elf_platform;

#define ELF_PLAT_INIT(_r, load_addr)	do { \
	_r->regs[1] = _r->regs[2] = _r->regs[3] = _r->regs[4] = 0;	\
	_r->regs[5] = _r->regs[6] = _r->regs[7] = _r->regs[8] = 0;	\
	_r->regs[9] = _r->regs[10] = _r->regs[11] = _r->regs[12] = 0;	\
	_r->regs[13] = _r->regs[14] = _r->regs[15] = _r->regs[16] = 0;	\
	_r->regs[17] = _r->regs[18] = _r->regs[19] = _r->regs[20] = 0;	\
	_r->regs[21] = _r->regs[22] = _r->regs[23] = _r->regs[24] = 0;	\
	_r->regs[25] = _r->regs[26] = _r->regs[27] = _r->regs[28] = 0;	\
	_r->regs[29] = _r->regs[30] = _r->regs[31] = 0;			\
} while (0)

/*
 * This is the location that an ET_DYN program is loaded if exec'ed. Typical
 * use of this is to invoke "./ld.so someprog" to test out a new version of
 * the loader. We need to make sure that it is out of the way of the program
 * that it will "exec", and that there is sufficient room for the brk.
 */

#define ELF_ET_DYN_BASE		(TASK_SIZE / 3 * 2)

/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
#define ARCH_DLINFO							\
do {									\
	NEW_AUX_ENT(AT_SYSINFO_EHDR,					\
		    (unsigned long)current->mm->context.vdso);		\
} while (0)

#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
				       int uses_interp);

struct arch_elf_state {
	int fp_abi;
	int interp_fp_abi;
};

#define LOONGARCH_ABI_FP_ANY	(0)

#define INIT_ARCH_ELF_STATE {			\
	.fp_abi = LOONGARCH_ABI_FP_ANY,		\
	.interp_fp_abi = LOONGARCH_ABI_FP_ANY,	\
}

extern int arch_elf_pt_proc(void *ehdr, void *phdr, struct file *elf,
			    bool is_interp, struct arch_elf_state *state);

extern int arch_check_elf(void *ehdr, bool has_interpreter, void *interp_ehdr,
			  struct arch_elf_state *state);

extern void loongarch_set_personality_fcsr(struct arch_elf_state *state);

#endif /* _ASM_ELF_H */
