diff options
| -rw-r--r-- | arch/loongarch/Kconfig | 15 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/asm-prototypes.h | 1 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/asmmacro.h | 51 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/lbt.h | 109 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/loongarch.h | 4 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/processor.h | 26 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/switch_to.h | 2 | ||||
| -rw-r--r-- | arch/loongarch/include/asm/thread_info.h | 4 | ||||
| -rw-r--r-- | arch/loongarch/include/uapi/asm/ptrace.h | 6 | ||||
| -rw-r--r-- | arch/loongarch/include/uapi/asm/sigcontext.h | 10 | ||||
| -rw-r--r-- | arch/loongarch/kernel/Makefile | 2 | ||||
| -rw-r--r-- | arch/loongarch/kernel/asm-offsets.c | 18 | ||||
| -rw-r--r-- | arch/loongarch/kernel/cpu-probe.c | 14 | ||||
| -rw-r--r-- | arch/loongarch/kernel/fpu.S | 9 | ||||
| -rw-r--r-- | arch/loongarch/kernel/lbt.S | 155 | ||||
| -rw-r--r-- | arch/loongarch/kernel/process.c | 15 | ||||
| -rw-r--r-- | arch/loongarch/kernel/ptrace.c | 54 | ||||
| -rw-r--r-- | arch/loongarch/kernel/signal.c | 188 | ||||
| -rw-r--r-- | arch/loongarch/kernel/traps.c | 41 | 
19 files changed, 693 insertions, 31 deletions
| diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 465759f6b0ed..21cc2e2d1f27 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -254,6 +254,9 @@ config AS_HAS_LSX_EXTENSION  config AS_HAS_LASX_EXTENSION  	def_bool $(as-instr,xvld \$xr0$(comma)\$a0$(comma)0) +config AS_HAS_LBT_EXTENSION +	def_bool $(as-instr,movscr2gr \$a0$(comma)\$scr0) +  menu "Kernel type and options"  source "kernel/Kconfig.hz" @@ -534,6 +537,18 @@ config CPU_HAS_LASX  	  If unsure, say Y. +config CPU_HAS_LBT +	bool "Support for the Loongson Binary Translation Extension" +	depends on AS_HAS_LBT_EXTENSION +	help +	  Loongson Binary Translation (LBT) introduces 4 scratch registers (SCR0 +	  to SCR3), x86/ARM eflags (eflags) and x87 fpu stack pointer (ftop). +	  Enabling this option allows the kernel to allocate and switch registers +	  specific to LBT. + +	  If you want to use this feature, such as the Loongson Architecture +	  Translator (LAT), say Y. +  config CPU_HAS_PREFETCH  	bool  	default y diff --git a/arch/loongarch/include/asm/asm-prototypes.h b/arch/loongarch/include/asm/asm-prototypes.h index ed06d3997420..cf8e1a4e7c19 100644 --- a/arch/loongarch/include/asm/asm-prototypes.h +++ b/arch/loongarch/include/asm/asm-prototypes.h @@ -1,6 +1,7 @@  /* SPDX-License-Identifier: GPL-2.0 */  #include <linux/uaccess.h>  #include <asm/fpu.h> +#include <asm/lbt.h>  #include <asm/mmu_context.h>  #include <asm/page.h>  #include <asm/ftrace.h> diff --git a/arch/loongarch/include/asm/asmmacro.h b/arch/loongarch/include/asm/asmmacro.h index af542a8d847f..c9544f358c33 100644 --- a/arch/loongarch/include/asm/asmmacro.h +++ b/arch/loongarch/include/asm/asmmacro.h @@ -41,12 +41,51 @@  	.macro fpu_save_csr thread tmp  	movfcsr2gr	\tmp, fcsr0 -	stptr.w	\tmp, \thread, THREAD_FCSR +	stptr.w		\tmp, \thread, THREAD_FCSR +#ifdef CONFIG_CPU_HAS_LBT +	/* TM bit is always 0 if LBT not supported */ +	andi		\tmp, \tmp, FPU_CSR_TM +	beqz		\tmp, 1f +	/* Save FTOP */ +	x86mftop	\tmp +	stptr.w		\tmp, \thread, THREAD_FTOP +	/* Turn off TM to ensure the order of FPR in memory independent of TM */ +	x86clrtm +1: +#endif  	.endm -	.macro fpu_restore_csr thread tmp -	ldptr.w	\tmp, \thread, THREAD_FCSR -	movgr2fcsr	fcsr0, \tmp +	.macro fpu_restore_csr thread tmp0 tmp1 +	ldptr.w		\tmp0, \thread, THREAD_FCSR +	movgr2fcsr	fcsr0, \tmp0 +#ifdef CONFIG_CPU_HAS_LBT +	/* TM bit is always 0 if LBT not supported */ +	andi		\tmp0, \tmp0, FPU_CSR_TM +	beqz		\tmp0, 2f +	/* Restore FTOP */ +	ldptr.w		\tmp0, \thread, THREAD_FTOP +	andi		\tmp0, \tmp0, 0x7 +	la.pcrel	\tmp1, 1f +	alsl.d		\tmp1, \tmp0, \tmp1, 3 +	jr		\tmp1 +1: +	x86mttop	0 +	b	2f +	x86mttop	1 +	b	2f +	x86mttop	2 +	b	2f +	x86mttop	3 +	b	2f +	x86mttop	4 +	b	2f +	x86mttop	5 +	b	2f +	x86mttop	6 +	b	2f +	x86mttop	7 +2: +#endif  	.endm  	.macro fpu_save_cc thread tmp0 tmp1 @@ -246,7 +285,7 @@  	.macro	lsx_restore_all	thread tmp0 tmp1  	lsx_restore_data	\thread, \tmp0  	fpu_restore_cc		\thread, \tmp0, \tmp1 -	fpu_restore_csr		\thread, \tmp0 +	fpu_restore_csr		\thread, \tmp0, \tmp1  	.endm  	.macro	lsx_save_upper vd base tmp off @@ -456,7 +495,7 @@  	.macro	lasx_restore_all thread tmp0 tmp1  	lasx_restore_data	\thread, \tmp0  	fpu_restore_cc		\thread, \tmp0, \tmp1 -	fpu_restore_csr		\thread, \tmp0 +	fpu_restore_csr		\thread, \tmp0, \tmp1  	.endm  	.macro	lasx_save_upper xd base tmp off diff --git a/arch/loongarch/include/asm/lbt.h b/arch/loongarch/include/asm/lbt.h new file mode 100644 index 000000000000..e671978bf552 --- /dev/null +++ b/arch/loongarch/include/asm/lbt.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Author: Qi Hu <huqi@loongson.cn> + *         Huacai Chen <chenhuacai@loongson.cn> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited + */ +#ifndef _ASM_LBT_H +#define _ASM_LBT_H + +#include <asm/cpu.h> +#include <asm/current.h> +#include <asm/loongarch.h> +#include <asm/processor.h> + +extern void _init_lbt(void); +extern void _save_lbt(struct loongarch_lbt *); +extern void _restore_lbt(struct loongarch_lbt *); + +static inline int is_lbt_enabled(void) +{ +	if (!cpu_has_lbt) +		return 0; + +	return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LBTEN) ? +		1 : 0; +} + +static inline int is_lbt_owner(void) +{ +	return test_thread_flag(TIF_USEDLBT); +} + +#ifdef CONFIG_CPU_HAS_LBT + +static inline void enable_lbt(void) +{ +	if (cpu_has_lbt) +		csr_xchg32(CSR_EUEN_LBTEN, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN); +} + +static inline void disable_lbt(void) +{ +	if (cpu_has_lbt) +		csr_xchg32(0, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN); +} + +static inline void __own_lbt(void) +{ +	enable_lbt(); +	set_thread_flag(TIF_USEDLBT); +	KSTK_EUEN(current) |= CSR_EUEN_LBTEN; +} + +static inline void own_lbt_inatomic(int restore) +{ +	if (cpu_has_lbt && !is_lbt_owner()) { +		__own_lbt(); +		if (restore) +			_restore_lbt(¤t->thread.lbt); +	} +} + +static inline void own_lbt(int restore) +{ +	preempt_disable(); +	own_lbt_inatomic(restore); +	preempt_enable(); +} + +static inline void lose_lbt_inatomic(int save, struct task_struct *tsk) +{ +	if (cpu_has_lbt && is_lbt_owner()) { +		if (save) +			_save_lbt(&tsk->thread.lbt); + +		disable_lbt(); +		clear_tsk_thread_flag(tsk, TIF_USEDLBT); +	} +	KSTK_EUEN(tsk) &= ~(CSR_EUEN_LBTEN); +} + +static inline void lose_lbt(int save) +{ +	preempt_disable(); +	lose_lbt_inatomic(save, current); +	preempt_enable(); +} + +static inline void init_lbt(void) +{ +	__own_lbt(); +	_init_lbt(); +} +#else +static inline void own_lbt_inatomic(int restore) {} +static inline void lose_lbt_inatomic(int save, struct task_struct *tsk) {} +static inline void init_lbt(void) {} +static inline void lose_lbt(int save) {} +#endif + +static inline int thread_lbt_context_live(void) +{ +	if (!cpu_has_lbt) +		return 0; + +	return test_thread_flag(TIF_LBT_CTX_LIVE); +} + +#endif /* _ASM_LBT_H */ diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h index a500efe0fd92..33531d432b49 100644 --- a/arch/loongarch/include/asm/loongarch.h +++ b/arch/loongarch/include/asm/loongarch.h @@ -1410,6 +1410,10 @@ __BUILD_CSR_OP(tlbidx)  #define FPU_CSR_RU	0x200	/* towards +Infinity */  #define FPU_CSR_RD	0x300	/* towards -Infinity */ +/* Bit 6 of FPU Status Register specify the LBT TOP simulation mode */ +#define FPU_CSR_TM_SHIFT	0x6 +#define FPU_CSR_TM		(_ULCAST_(1) << FPU_CSR_TM_SHIFT) +  #define read_fcsr(source)	\  ({	\  	unsigned int __res;	\ diff --git a/arch/loongarch/include/asm/processor.h b/arch/loongarch/include/asm/processor.h index 636e1c66398c..c3bc44b5f5b3 100644 --- a/arch/loongarch/include/asm/processor.h +++ b/arch/loongarch/include/asm/processor.h @@ -80,11 +80,22 @@ BUILD_FPR_ACCESS(32)  BUILD_FPR_ACCESS(64)  struct loongarch_fpu { -	unsigned int	fcsr;  	uint64_t	fcc;	/* 8x8 */ +	uint32_t	fcsr; +	uint32_t	ftop;  	union fpureg	fpr[NUM_FPU_REGS];  }; +struct loongarch_lbt { +	/* Scratch registers */ +	unsigned long scr0; +	unsigned long scr1; +	unsigned long scr2; +	unsigned long scr3; +	/* Eflags register */ +	unsigned long eflags; +}; +  #define INIT_CPUMASK { \  	{0,} \  } @@ -113,15 +124,6 @@ struct thread_struct {  	unsigned long csr_ecfg;  	unsigned long csr_badvaddr;	/* Last user fault */ -	/* Scratch registers */ -	unsigned long scr0; -	unsigned long scr1; -	unsigned long scr2; -	unsigned long scr3; - -	/* Eflags register */ -	unsigned long eflags; -  	/* Other stuff associated with the thread. */  	unsigned long trap_nr;  	unsigned long error_code; @@ -133,6 +135,7 @@ struct thread_struct {  	 * context because they are conditionally copied at fork().  	 */  	struct loongarch_fpu fpu FPU_ALIGN; +	struct loongarch_lbt lbt; /* Also conditionally copied */  	/* Hardware breakpoints pinned to this task. */  	struct perf_event *hbp_break[LOONGARCH_MAX_BRP]; @@ -174,8 +177,9 @@ struct thread_struct {  	 * FPU & vector registers				\  	 */							\  	.fpu			= {				\ -		.fcsr		= 0,				\  		.fcc		= 0,				\ +		.fcsr		= 0,				\ +		.ftop		= 0,				\  		.fpr		= {{{0,},},},			\  	},							\  	.hbp_break		= {0},				\ diff --git a/arch/loongarch/include/asm/switch_to.h b/arch/loongarch/include/asm/switch_to.h index 24e3094bebab..5b225aff3ba2 100644 --- a/arch/loongarch/include/asm/switch_to.h +++ b/arch/loongarch/include/asm/switch_to.h @@ -7,6 +7,7 @@  #include <asm/cpu-features.h>  #include <asm/fpu.h> +#include <asm/lbt.h>  struct task_struct; @@ -34,6 +35,7 @@ extern asmlinkage struct task_struct *__switch_to(struct task_struct *prev,  #define switch_to(prev, next, last)						\  do {										\  	lose_fpu_inatomic(1, prev);						\ +	lose_lbt_inatomic(1, prev);						\  	hw_breakpoint_thread_switch(next);					\  	(last) = __switch_to(prev, next, task_thread_info(next),		\  		 __builtin_return_address(0), __builtin_frame_address(0));	\ diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h index 1a3354ca056e..8cb653d49a54 100644 --- a/arch/loongarch/include/asm/thread_info.h +++ b/arch/loongarch/include/asm/thread_info.h @@ -84,6 +84,8 @@ register unsigned long current_stack_pointer __asm__("$sp");  #define TIF_SINGLESTEP		16	/* Single Step */  #define TIF_LSX_CTX_LIVE	17	/* LSX context must be preserved */  #define TIF_LASX_CTX_LIVE	18	/* LASX context must be preserved */ +#define TIF_USEDLBT		19	/* LBT was used by this task this quantum (SMP) */ +#define TIF_LBT_CTX_LIVE	20	/* LBT context must be preserved */  #define _TIF_SIGPENDING		(1<<TIF_SIGPENDING)  #define _TIF_NEED_RESCHED	(1<<TIF_NEED_RESCHED) @@ -101,6 +103,8 @@ register unsigned long current_stack_pointer __asm__("$sp");  #define _TIF_SINGLESTEP		(1<<TIF_SINGLESTEP)  #define _TIF_LSX_CTX_LIVE	(1<<TIF_LSX_CTX_LIVE)  #define _TIF_LASX_CTX_LIVE	(1<<TIF_LASX_CTX_LIVE) +#define _TIF_USEDLBT		(1<<TIF_USEDLBT) +#define _TIF_LBT_CTX_LIVE	(1<<TIF_LBT_CTX_LIVE)  #endif /* __KERNEL__ */  #endif /* _ASM_THREAD_INFO_H */ diff --git a/arch/loongarch/include/uapi/asm/ptrace.h b/arch/loongarch/include/uapi/asm/ptrace.h index 06e3be52cb04..ac915f841650 100644 --- a/arch/loongarch/include/uapi/asm/ptrace.h +++ b/arch/loongarch/include/uapi/asm/ptrace.h @@ -56,6 +56,12 @@ struct user_lasx_state {  	uint64_t vregs[32*4];  }; +struct user_lbt_state { +	uint64_t scr[4]; +	uint32_t eflags; +	uint32_t ftop; +}; +  struct user_watch_state {  	uint64_t dbg_info;  	struct { diff --git a/arch/loongarch/include/uapi/asm/sigcontext.h b/arch/loongarch/include/uapi/asm/sigcontext.h index 4cd7d16f7037..6c22f616b8f1 100644 --- a/arch/loongarch/include/uapi/asm/sigcontext.h +++ b/arch/loongarch/include/uapi/asm/sigcontext.h @@ -59,4 +59,14 @@ struct lasx_context {  	__u32	fcsr;  }; +/* LBT context */ +#define LBT_CTX_MAGIC		0x42540001 +#define LBT_CTX_ALIGN		8 +struct lbt_context { +	__u64	regs[4]; +	__u32	eflags; +	__u32	ftop; +}; + +  #endif /* _UAPI_ASM_SIGCONTEXT_H */ diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 8e279f04f9e7..5341dcf15a17 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_EFI) 		+= efi.o  obj-$(CONFIG_CPU_HAS_FPU)	+= fpu.o kfpu.o +obj-$(CONFIG_CPU_HAS_LBT)	+= lbt.o +  obj-$(CONFIG_ARCH_STRICT_ALIGN)	+= unaligned.o  ifdef CONFIG_FUNCTION_TRACER diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c index 505e4bf59603..8da0726777ed 100644 --- a/arch/loongarch/kernel/asm-offsets.c +++ b/arch/loongarch/kernel/asm-offsets.c @@ -118,13 +118,6 @@ void output_thread_defines(void)  	OFFSET(THREAD_CSRECFG, task_struct,  	       thread.csr_ecfg); -	OFFSET(THREAD_SCR0, task_struct, thread.scr0); -	OFFSET(THREAD_SCR1, task_struct, thread.scr1); -	OFFSET(THREAD_SCR2, task_struct, thread.scr2); -	OFFSET(THREAD_SCR3, task_struct, thread.scr3); - -	OFFSET(THREAD_EFLAGS, task_struct, thread.eflags); -  	OFFSET(THREAD_FPU, task_struct, thread.fpu);  	OFFSET(THREAD_BVADDR, task_struct, \ @@ -172,6 +165,17 @@ void output_thread_fpu_defines(void)  	OFFSET(THREAD_FCSR, loongarch_fpu, fcsr);  	OFFSET(THREAD_FCC,  loongarch_fpu, fcc); +	OFFSET(THREAD_FTOP, loongarch_fpu, ftop); +	BLANK(); +} + +void output_thread_lbt_defines(void) +{ +	OFFSET(THREAD_SCR0,  loongarch_lbt, scr0); +	OFFSET(THREAD_SCR1,  loongarch_lbt, scr1); +	OFFSET(THREAD_SCR2,  loongarch_lbt, scr2); +	OFFSET(THREAD_SCR3,  loongarch_lbt, scr3); +	OFFSET(THREAD_EFLAGS, loongarch_lbt, eflags);  	BLANK();  } diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c index e925579c7a71..55320813ee08 100644 --- a/arch/loongarch/kernel/cpu-probe.c +++ b/arch/loongarch/kernel/cpu-probe.c @@ -144,6 +144,20 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)  		c->options |= LOONGARCH_CPU_LVZ;  		elf_hwcap |= HWCAP_LOONGARCH_LVZ;  	} +#ifdef CONFIG_CPU_HAS_LBT +	if (config & CPUCFG2_X86BT) { +		c->options |= LOONGARCH_CPU_LBT_X86; +		elf_hwcap |= HWCAP_LOONGARCH_LBT_X86; +	} +	if (config & CPUCFG2_ARMBT) { +		c->options |= LOONGARCH_CPU_LBT_ARM; +		elf_hwcap |= HWCAP_LOONGARCH_LBT_ARM; +	} +	if (config & CPUCFG2_MIPSBT) { +		c->options |= LOONGARCH_CPU_LBT_MIPS; +		elf_hwcap |= HWCAP_LOONGARCH_LBT_MIPS; +	} +#endif  	config = read_cpucfg(LOONGARCH_CPUCFG6);  	if (config & CPUCFG6_PMP) diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 80dccf8e39a7..d53ab10f4644 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -138,6 +138,13 @@  	.macro sc_save_fcsr base, tmp0  	movfcsr2gr	\tmp0, fcsr0  	EX	st.w	\tmp0, \base, 0 +#if defined(CONFIG_CPU_HAS_LBT) +	/* TM bit is always 0 if LBT not supported */ +	andi		\tmp0, \tmp0, FPU_CSR_TM +	beqz		\tmp0, 1f +	x86clrtm +1: +#endif  	.endm  	.macro sc_restore_fcsr base, tmp0 @@ -309,7 +316,7 @@ EXPORT_SYMBOL(_save_fp)   */  SYM_FUNC_START(_restore_fp)  	fpu_restore_double	a0 t1		# clobbers t1 -	fpu_restore_csr		a0 t1 +	fpu_restore_csr		a0 t1 t2  	fpu_restore_cc		a0 t1 t2	# clobbers t1, t2  	jr			ra  SYM_FUNC_END(_restore_fp) diff --git a/arch/loongarch/kernel/lbt.S b/arch/loongarch/kernel/lbt.S new file mode 100644 index 000000000000..9c75120a26d8 --- /dev/null +++ b/arch/loongarch/kernel/lbt.S @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Author: Qi Hu <huqi@loongson.cn> + *         Huacai Chen <chenhuacai@loongson.cn> + * + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited + */ +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/asm-extable.h> +#include <asm/asm-offsets.h> +#include <asm/errno.h> +#include <asm/regdef.h> + +#define SCR_REG_WIDTH 8 + +	.macro	EX insn, reg, src, offs +.ex\@:	\insn	\reg, \src, \offs +	_asm_extable .ex\@, .L_lbt_fault +	.endm + +/* + * Save a thread's lbt context. + */ +SYM_FUNC_START(_save_lbt) +	movscr2gr	t1, $scr0		# save scr +	stptr.d		t1, a0, THREAD_SCR0 +	movscr2gr	t1, $scr1 +	stptr.d		t1, a0, THREAD_SCR1 +	movscr2gr	t1, $scr2 +	stptr.d		t1, a0, THREAD_SCR2 +	movscr2gr	t1, $scr3 +	stptr.d		t1, a0, THREAD_SCR3 + +	x86mfflag	t1, 0x3f		# save eflags +	stptr.d		t1, a0, THREAD_EFLAGS +	jr		ra +SYM_FUNC_END(_save_lbt) +EXPORT_SYMBOL(_save_lbt) + +/* + * Restore a thread's lbt context. + */ +SYM_FUNC_START(_restore_lbt) +	ldptr.d		t1, a0, THREAD_SCR0	# restore scr +	movgr2scr	$scr0, t1 +	ldptr.d		t1, a0, THREAD_SCR1 +	movgr2scr	$scr1, t1 +	ldptr.d		t1, a0, THREAD_SCR2 +	movgr2scr	$scr2, t1 +	ldptr.d		t1, a0, THREAD_SCR3 +	movgr2scr	$scr3, t1 + +	ldptr.d		t1, a0, THREAD_EFLAGS	# restore eflags +	x86mtflag	t1, 0x3f +	jr		ra +SYM_FUNC_END(_restore_lbt) +EXPORT_SYMBOL(_restore_lbt) + +/* + * Load scr/eflag with zero. + */ +SYM_FUNC_START(_init_lbt) +	movgr2scr	$scr0, zero +	movgr2scr	$scr1, zero +	movgr2scr	$scr2, zero +	movgr2scr	$scr3, zero + +	x86mtflag	zero, 0x3f +	jr		ra +SYM_FUNC_END(_init_lbt) + +/* + * a0: scr + * a1: eflag + */ +SYM_FUNC_START(_save_lbt_context) +	movscr2gr	t1, $scr0		# save scr +	EX	st.d	t1, a0, (0 * SCR_REG_WIDTH) +	movscr2gr	t1, $scr1 +	EX	st.d	t1, a0, (1 * SCR_REG_WIDTH) +	movscr2gr	t1, $scr2 +	EX	st.d	t1, a0, (2 * SCR_REG_WIDTH) +	movscr2gr	t1, $scr3 +	EX	st.d	t1, a0, (3 * SCR_REG_WIDTH) + +	x86mfflag	t1, 0x3f		# save eflags +	EX 	st.w	t1, a1, 0 +	li.w		a0, 0			# success +	jr		ra +SYM_FUNC_END(_save_lbt_context) + +/* + * a0: scr + * a1: eflag + */ +SYM_FUNC_START(_restore_lbt_context) +	EX	ld.d	t1, a0, (0 * SCR_REG_WIDTH)	# restore scr +	movgr2scr	$scr0, t1 +	EX	ld.d	t1, a0, (1 * SCR_REG_WIDTH) +	movgr2scr	$scr1, t1 +	EX	ld.d	t1, a0, (2 * SCR_REG_WIDTH) +	movgr2scr	$scr2, t1 +	EX	ld.d	t1, a0, (3 * SCR_REG_WIDTH) +	movgr2scr	$scr3, t1 + +	EX 	ld.w	t1, a1, 0			# restore eflags +	x86mtflag	t1, 0x3f +	li.w		a0, 0			# success +	jr		ra +SYM_FUNC_END(_restore_lbt_context) + +/* + * a0: ftop + */ +SYM_FUNC_START(_save_ftop_context) +	x86mftop	t1 +	st.w		t1, a0, 0 +	li.w		a0, 0			# success +	jr		ra +SYM_FUNC_END(_save_ftop_context) + +/* + * a0: ftop + */ +SYM_FUNC_START(_restore_ftop_context) +	ld.w		t1, a0, 0 +	andi		t1, t1, 0x7 +	la.pcrel	a0, 1f +	alsl.d		a0, t1, a0, 3 +	jr		a0 +1: +	x86mttop	0 +	b	2f +	x86mttop	1 +	b	2f +	x86mttop	2 +	b	2f +	x86mttop	3 +	b	2f +	x86mttop	4 +	b	2f +	x86mttop	5 +	b	2f +	x86mttop	6 +	b	2f +	x86mttop	7 +2: +	li.w		a0, 0			# success +	jr		ra +SYM_FUNC_END(_restore_ftop_context) + +.L_lbt_fault: +	li.w		a0, -EFAULT		# failure +	jr		ra diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c index 4ee1e9d6a65f..91ccedd9db6a 100644 --- a/arch/loongarch/kernel/process.c +++ b/arch/loongarch/kernel/process.c @@ -38,6 +38,7 @@  #include <asm/cpu.h>  #include <asm/elf.h>  #include <asm/fpu.h> +#include <asm/lbt.h>  #include <asm/io.h>  #include <asm/irq.h>  #include <asm/irq_regs.h> @@ -82,9 +83,11 @@ void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)  	euen = regs->csr_euen & ~(CSR_EUEN_FPEN);  	regs->csr_euen = euen;  	lose_fpu(0); +	lose_lbt(0);  	clear_thread_flag(TIF_LSX_CTX_LIVE);  	clear_thread_flag(TIF_LASX_CTX_LIVE); +	clear_thread_flag(TIF_LBT_CTX_LIVE);  	clear_used_math();  	regs->csr_era = pc;  	regs->regs[3] = sp; @@ -121,10 +124,14 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)  	preempt_enable(); -	if (used_math()) -		memcpy(dst, src, sizeof(struct task_struct)); -	else +	if (!used_math())  		memcpy(dst, src, offsetof(struct task_struct, thread.fpu.fpr)); +	else +		memcpy(dst, src, offsetof(struct task_struct, thread.lbt.scr0)); + +#ifdef CONFIG_CPU_HAS_LBT +	memcpy(&dst->thread.lbt, &src->thread.lbt, sizeof(struct loongarch_lbt)); +#endif  	return 0;  } @@ -189,8 +196,10 @@ out:  	ptrace_hw_copy_thread(p);  	clear_tsk_thread_flag(p, TIF_USEDFPU);  	clear_tsk_thread_flag(p, TIF_USEDSIMD); +	clear_tsk_thread_flag(p, TIF_USEDLBT);  	clear_tsk_thread_flag(p, TIF_LSX_CTX_LIVE);  	clear_tsk_thread_flag(p, TIF_LASX_CTX_LIVE); +	clear_tsk_thread_flag(p, TIF_LBT_CTX_LIVE);  	return 0;  } diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index f72adbf530c6..c114c5ef1332 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -38,6 +38,7 @@  #include <asm/cpu.h>  #include <asm/cpu-info.h>  #include <asm/fpu.h> +#include <asm/lbt.h>  #include <asm/loongarch.h>  #include <asm/page.h>  #include <asm/pgtable.h> @@ -338,6 +339,46 @@ static int simd_set(struct task_struct *target,  #endif /* CONFIG_CPU_HAS_LSX */ +#ifdef CONFIG_CPU_HAS_LBT +static int lbt_get(struct task_struct *target, +		   const struct user_regset *regset, +		   struct membuf to) +{ +	int r; + +	r = membuf_write(&to, &target->thread.lbt.scr0, sizeof(target->thread.lbt.scr0)); +	r = membuf_write(&to, &target->thread.lbt.scr1, sizeof(target->thread.lbt.scr1)); +	r = membuf_write(&to, &target->thread.lbt.scr2, sizeof(target->thread.lbt.scr2)); +	r = membuf_write(&to, &target->thread.lbt.scr3, sizeof(target->thread.lbt.scr3)); +	r = membuf_write(&to, &target->thread.lbt.eflags, sizeof(u32)); +	r = membuf_write(&to, &target->thread.fpu.ftop, sizeof(u32)); + +	return r; +} + +static int lbt_set(struct task_struct *target, +		   const struct user_regset *regset, +		   unsigned int pos, unsigned int count, +		   const void *kbuf, const void __user *ubuf) +{ +	int err = 0; +	const int eflags_start = 4 * sizeof(target->thread.lbt.scr0); +	const int ftop_start = eflags_start + sizeof(u32); + +	err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, +				  &target->thread.lbt.scr0, +				  0, 4 * sizeof(target->thread.lbt.scr0)); +	err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, +				  &target->thread.lbt.eflags, +				  eflags_start, ftop_start); +	err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf, +				  &target->thread.fpu.ftop, +				  ftop_start, ftop_start + sizeof(u32)); + +	return err; +} +#endif /* CONFIG_CPU_HAS_LBT */ +  #ifdef CONFIG_HAVE_HW_BREAKPOINT  /* @@ -802,6 +843,9 @@ enum loongarch_regset {  #ifdef CONFIG_CPU_HAS_LASX  	REGSET_LASX,  #endif +#ifdef CONFIG_CPU_HAS_LBT +	REGSET_LBT, +#endif  #ifdef CONFIG_HAVE_HW_BREAKPOINT  	REGSET_HW_BREAK,  	REGSET_HW_WATCH, @@ -853,6 +897,16 @@ static const struct user_regset loongarch64_regsets[] = {  		.set		= simd_set,  	},  #endif +#ifdef CONFIG_CPU_HAS_LBT +	[REGSET_LBT] = { +		.core_note_type	= NT_LOONGARCH_LBT, +		.n		= 5, +		.size		= sizeof(u64), +		.align		= sizeof(u64), +		.regset_get	= lbt_get, +		.set		= lbt_set, +	}, +#endif  #ifdef CONFIG_HAVE_HW_BREAKPOINT  	[REGSET_HW_BREAK] = {  		.core_note_type = NT_LOONGARCH_HW_BREAK, diff --git a/arch/loongarch/kernel/signal.c b/arch/loongarch/kernel/signal.c index ceb899366c0a..504fdfe85203 100644 --- a/arch/loongarch/kernel/signal.c +++ b/arch/loongarch/kernel/signal.c @@ -32,6 +32,7 @@  #include <asm/cacheflush.h>  #include <asm/cpu-features.h>  #include <asm/fpu.h> +#include <asm/lbt.h>  #include <asm/ucontext.h>  #include <asm/vdso.h> @@ -44,6 +45,9 @@  /* Make sure we will not lose FPU ownership */  #define lock_fpu_owner()	({ preempt_disable(); pagefault_disable(); })  #define unlock_fpu_owner()	({ pagefault_enable(); preempt_enable(); }) +/* Make sure we will not lose LBT ownership */ +#define lock_lbt_owner()	({ preempt_disable(); pagefault_disable(); }) +#define unlock_lbt_owner()	({ pagefault_enable(); preempt_enable(); })  /* Assembly functions to move context to/from the FPU */  extern asmlinkage int @@ -59,6 +63,13 @@ _save_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);  extern asmlinkage int  _restore_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr); +#ifdef CONFIG_CPU_HAS_LBT +extern asmlinkage int _save_lbt_context(void __user *regs, void __user *eflags); +extern asmlinkage int _restore_lbt_context(void __user *regs, void __user *eflags); +extern asmlinkage int _save_ftop_context(void __user *ftop); +extern asmlinkage int _restore_ftop_context(void __user *ftop); +#endif +  struct rt_sigframe {  	struct siginfo rs_info;  	struct ucontext rs_uctx; @@ -75,6 +86,7 @@ struct extctx_layout {  	struct _ctx_layout fpu;  	struct _ctx_layout lsx;  	struct _ctx_layout lasx; +	struct _ctx_layout lbt;  	struct _ctx_layout end;  }; @@ -215,6 +227,52 @@ static int copy_lasx_from_sigcontext(struct lasx_context __user *ctx)  	return err;  } +#ifdef CONFIG_CPU_HAS_LBT +static int copy_lbt_to_sigcontext(struct lbt_context __user *ctx) +{ +	int err = 0; +	uint64_t __user *regs	= (uint64_t *)&ctx->regs; +	uint32_t __user *eflags	= (uint32_t *)&ctx->eflags; + +	err |= __put_user(current->thread.lbt.scr0, ®s[0]); +	err |= __put_user(current->thread.lbt.scr1, ®s[1]); +	err |= __put_user(current->thread.lbt.scr2, ®s[2]); +	err |= __put_user(current->thread.lbt.scr3, ®s[3]); +	err |= __put_user(current->thread.lbt.eflags, eflags); + +	return err; +} + +static int copy_lbt_from_sigcontext(struct lbt_context __user *ctx) +{ +	int err = 0; +	uint64_t __user *regs	= (uint64_t *)&ctx->regs; +	uint32_t __user *eflags	= (uint32_t *)&ctx->eflags; + +	err |= __get_user(current->thread.lbt.scr0, ®s[0]); +	err |= __get_user(current->thread.lbt.scr1, ®s[1]); +	err |= __get_user(current->thread.lbt.scr2, ®s[2]); +	err |= __get_user(current->thread.lbt.scr3, ®s[3]); +	err |= __get_user(current->thread.lbt.eflags, eflags); + +	return err; +} + +static int copy_ftop_to_sigcontext(struct lbt_context __user *ctx) +{ +	uint32_t  __user *ftop	= &ctx->ftop; + +	return __put_user(current->thread.fpu.ftop, ftop); +} + +static int copy_ftop_from_sigcontext(struct lbt_context __user *ctx) +{ +	uint32_t  __user *ftop	= &ctx->ftop; + +	return __get_user(current->thread.fpu.ftop, ftop); +} +#endif +  /*   * Wrappers for the assembly _{save,restore}_fp_context functions.   */ @@ -272,6 +330,41 @@ static int restore_hw_lasx_context(struct lasx_context __user *ctx)  	return _restore_lasx_context(regs, fcc, fcsr);  } +/* + * Wrappers for the assembly _{save,restore}_lbt_context functions. + */ +#ifdef CONFIG_CPU_HAS_LBT +static int save_hw_lbt_context(struct lbt_context __user *ctx) +{ +	uint64_t __user *regs	= (uint64_t *)&ctx->regs; +	uint32_t __user *eflags	= (uint32_t *)&ctx->eflags; + +	return _save_lbt_context(regs, eflags); +} + +static int restore_hw_lbt_context(struct lbt_context __user *ctx) +{ +	uint64_t __user *regs	= (uint64_t *)&ctx->regs; +	uint32_t __user *eflags	= (uint32_t *)&ctx->eflags; + +	return _restore_lbt_context(regs, eflags); +} + +static int save_hw_ftop_context(struct lbt_context __user *ctx) +{ +	uint32_t __user *ftop	= &ctx->ftop; + +	return _save_ftop_context(ftop); +} + +static int restore_hw_ftop_context(struct lbt_context __user *ctx) +{ +	uint32_t __user *ftop	= &ctx->ftop; + +	return _restore_ftop_context(ftop); +} +#endif +  static int fcsr_pending(unsigned int __user *fcsr)  {  	int err, sig = 0; @@ -519,6 +612,77 @@ static int protected_restore_lasx_context(struct extctx_layout *extctx)  	return err ?: sig;  } +#ifdef CONFIG_CPU_HAS_LBT +static int protected_save_lbt_context(struct extctx_layout *extctx) +{ +	int err = 0; +	struct sctx_info __user *info = extctx->lbt.addr; +	struct lbt_context __user *lbt_ctx = +		(struct lbt_context *)get_ctx_through_ctxinfo(info); +	uint64_t __user *regs	= (uint64_t *)&lbt_ctx->regs; +	uint32_t __user *eflags	= (uint32_t *)&lbt_ctx->eflags; + +	while (1) { +		lock_lbt_owner(); +		if (is_lbt_owner()) +			err |= save_hw_lbt_context(lbt_ctx); +		else +			err |= copy_lbt_to_sigcontext(lbt_ctx); +		if (is_fpu_owner()) +			err |= save_hw_ftop_context(lbt_ctx); +		else +			err |= copy_ftop_to_sigcontext(lbt_ctx); +		unlock_lbt_owner(); + +		err |= __put_user(LBT_CTX_MAGIC, &info->magic); +		err |= __put_user(extctx->lbt.size, &info->size); + +		if (likely(!err)) +			break; +		/* Touch the LBT context and try again */ +		err = __put_user(0, ®s[0]) | __put_user(0, eflags); + +		if (err) +			return err; +	} + +	return err; +} + +static int protected_restore_lbt_context(struct extctx_layout *extctx) +{ +	int err = 0, tmp __maybe_unused; +	struct sctx_info __user *info = extctx->lbt.addr; +	struct lbt_context __user *lbt_ctx = +		(struct lbt_context *)get_ctx_through_ctxinfo(info); +	uint64_t __user *regs	= (uint64_t *)&lbt_ctx->regs; +	uint32_t __user *eflags	= (uint32_t *)&lbt_ctx->eflags; + +	while (1) { +		lock_lbt_owner(); +		if (is_lbt_owner()) +			err |= restore_hw_lbt_context(lbt_ctx); +		else +			err |= copy_lbt_from_sigcontext(lbt_ctx); +		if (is_fpu_owner()) +			err |= restore_hw_ftop_context(lbt_ctx); +		else +			err |= copy_ftop_from_sigcontext(lbt_ctx); +		unlock_lbt_owner(); + +		if (likely(!err)) +			break; +		/* Touch the LBT context and try again */ +		err = __get_user(tmp, ®s[0]) | __get_user(tmp, eflags); + +		if (err) +			return err; +	} + +	return err; +} +#endif +  static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,  			    struct extctx_layout *extctx)  { @@ -539,6 +703,11 @@ static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,  	else if (extctx->fpu.addr)  		err |= protected_save_fpu_context(extctx); +#ifdef CONFIG_CPU_HAS_LBT +	if (extctx->lbt.addr) +		err |= protected_save_lbt_context(extctx); +#endif +  	/* Set the "end" magic */  	info = (struct sctx_info *)extctx->end.addr;  	err |= __put_user(0, &info->magic); @@ -584,6 +753,13 @@ static int parse_extcontext(struct sigcontext __user *sc, struct extctx_layout *  			extctx->lasx.addr = info;  			break; +		case LBT_CTX_MAGIC: +			if (size < (sizeof(struct sctx_info) + +				    sizeof(struct lbt_context))) +				goto invalid; +			extctx->lbt.addr = info; +			break; +  		default:  			goto invalid;  		} @@ -636,6 +812,11 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc  	else if (extctx.fpu.addr)  		err |= protected_restore_fpu_context(&extctx); +#ifdef CONFIG_CPU_HAS_LBT +	if (extctx.lbt.addr) +		err |= protected_restore_lbt_context(&extctx); +#endif +  bad:  	return err;  } @@ -700,6 +881,13 @@ static unsigned long setup_extcontext(struct extctx_layout *extctx, unsigned lon  			  sizeof(struct fpu_context), FPU_CTX_ALIGN, new_sp);  	} +#ifdef CONFIG_CPU_HAS_LBT +	if (cpu_has_lbt && thread_lbt_context_live()) { +		new_sp = extframe_alloc(extctx, &extctx->lbt, +			  sizeof(struct lbt_context), LBT_CTX_ALIGN, new_sp); +	} +#endif +  	return new_sp;  } diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 89699db45cec..7ed01a7b2542 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -36,6 +36,7 @@  #include <asm/break.h>  #include <asm/cpu.h>  #include <asm/fpu.h> +#include <asm/lbt.h>  #include <asm/inst.h>  #include <asm/loongarch.h>  #include <asm/mmu_context.h> @@ -966,13 +967,47 @@ out:  	irqentry_exit(regs, state);  } +static void init_restore_lbt(void) +{ +	if (!thread_lbt_context_live()) { +		/* First time LBT context user */ +		init_lbt(); +		set_thread_flag(TIF_LBT_CTX_LIVE); +	} else { +		if (!is_lbt_owner()) +			own_lbt_inatomic(1); +	} + +	BUG_ON(!is_lbt_enabled()); +} +  asmlinkage void noinstr do_lbt(struct pt_regs *regs)  {  	irqentry_state_t state = irqentry_enter(regs); -	local_irq_enable(); -	force_sig(SIGILL); -	local_irq_disable(); +	/* +	 * BTD (Binary Translation Disable exception) can be triggered +	 * during FP save/restore if TM (Top Mode) is on, which may +	 * cause irq_enable during 'switch_to'. To avoid this situation +	 * (including the user using 'MOVGR2GCSR' to turn on TM, which +	 * will not trigger the BTE), we need to check PRMD first. +	 */ +	if (regs->csr_prmd & CSR_PRMD_PIE) +		local_irq_enable(); + +	if (!cpu_has_lbt) { +		force_sig(SIGILL); +		goto out; +	} +	BUG_ON(is_lbt_enabled()); + +	preempt_disable(); +	init_restore_lbt(); +	preempt_enable(); + +out: +	if (regs->csr_prmd & CSR_PRMD_PIE) +		local_irq_disable();  	irqentry_exit(regs, state);  } | 
