--- /dev/null
+From c2396651309eba291c15e32db8fbe44c738b5921 Mon Sep 17 00:00:00 2001
+From: Xi Ruoyao <xry111@xry111.site>
+Date: Wed, 17 Jan 2024 12:43:08 +0800
+Subject: LoongArch: Fix and simplify fcsr initialization on execve()
+
+From: Xi Ruoyao <xry111@xry111.site>
+
+commit c2396651309eba291c15e32db8fbe44c738b5921 upstream.
+
+There has been a lingering bug in LoongArch Linux systems causing some
+GCC tests to intermittently fail (see Closes link). I've made a minimal
+reproducer:
+
+ zsh% cat measure.s
+ .align 4
+ .globl _start
+ _start:
+ movfcsr2gr $a0, $fcsr0
+ bstrpick.w $a0, $a0, 16, 16
+ beqz $a0, .ok
+ break 0
+ .ok:
+ li.w $a7, 93
+ syscall 0
+ zsh% cc mesaure.s -o measure -nostdlib
+ zsh% echo $((1.0/3))
+ 0.33333333333333331
+ zsh% while ./measure; do ; done
+
+This while loop should not stop as POSIX is clear that execve must set
+fenv to the default, where FCSR should be zero. But in fact it will
+just stop after running for a while (normally less than 30 seconds).
+Note that "$((1.0/3))" is needed to reproduce this issue because it
+raises FE_INVALID and makes fcsr0 non-zero.
+
+The problem is we are currently relying on SET_PERSONALITY2() to reset
+current->thread.fpu.fcsr. But SET_PERSONALITY2() is executed before
+start_thread which calls lose_fpu(0). We can see if kernel preempt is
+enabled, we may switch to another thread after SET_PERSONALITY2() but
+before lose_fpu(0). Then bad thing happens: during the thread switch
+the value of the fcsr0 register is stored into current->thread.fpu.fcsr,
+making it dirty again.
+
+The issue can be fixed by setting current->thread.fpu.fcsr after
+lose_fpu(0) because lose_fpu() clears TIF_USEDFPU, then the thread
+switch won't touch current->thread.fpu.fcsr.
+
+The only other architecture setting FCSR in SET_PERSONALITY2() is MIPS.
+I've ran a similar test on MIPS with mainline kernel and it turns out
+MIPS is buggy, too. Anyway MIPS do this for supporting different FP
+flavors (NaN encodings, etc.) which do not exist on LoongArch. So for
+LoongArch, we can simply remove the current->thread.fpu.fcsr setting
+from SET_PERSONALITY2() and do it in start_thread(), after lose_fpu(0).
+
+The while loop failing with the mainline kernel has survived one hour
+after this change on LoongArch.
+
+Fixes: 803b0fc5c3f2baa ("LoongArch: Add process management")
+Closes: https://github.com/loongson-community/discussions/issues/7
+Link: https://lore.kernel.org/linux-mips/7a6aa1bbdbbe2e63ae96ff163fab0349f58f1b9e.camel@xry111.site/
+Cc: stable@vger.kernel.org
+Signed-off-by: Xi Ruoyao <xry111@xry111.site>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/loongarch/include/asm/elf.h | 5 -----
+ arch/loongarch/kernel/elf.c | 5 -----
+ arch/loongarch/kernel/process.c | 1 +
+ 3 files changed, 1 insertion(+), 10 deletions(-)
+
+--- a/arch/loongarch/include/asm/elf.h
++++ b/arch/loongarch/include/asm/elf.h
+@@ -241,8 +241,6 @@ void loongarch_dump_regs64(u64 *uregs, c
+ do { \
+ current->thread.vdso = &vdso_info; \
+ \
+- loongarch_set_personality_fcsr(state); \
+- \
+ if (personality(current->personality) != PER_LINUX) \
+ set_personality(PER_LINUX); \
+ } while (0)
+@@ -259,7 +257,6 @@ do { \
+ 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) \
+@@ -340,6 +337,4 @@ extern int arch_elf_pt_proc(void *ehdr,
+ 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 */
+--- a/arch/loongarch/kernel/elf.c
++++ b/arch/loongarch/kernel/elf.c
+@@ -23,8 +23,3 @@ int arch_check_elf(void *_ehdr, bool has
+ {
+ return 0;
+ }
+-
+-void loongarch_set_personality_fcsr(struct arch_elf_state *state)
+-{
+- current->thread.fpu.fcsr = boot_cpu_data.fpu_csr0;
+-}
+--- a/arch/loongarch/kernel/process.c
++++ b/arch/loongarch/kernel/process.c
+@@ -82,6 +82,7 @@ void start_thread(struct pt_regs *regs,
+ euen = regs->csr_euen & ~(CSR_EUEN_FPEN);
+ regs->csr_euen = euen;
+ lose_fpu(0);
++ current->thread.fpu.fcsr = boot_cpu_data.fpu_csr0;
+
+ clear_thread_flag(TIF_LSX_CTX_LIVE);
+ clear_thread_flag(TIF_LASX_CTX_LIVE);