mirror of
https://gitee.com/open-loongarch/linux-6.12.git
synced 2026-10-25 12:37:11 +08:00
loongarch: fix fpu panic bug
Changelog: 修复fpu的panic bug Signed-off-by: maoxiaochuan <maoxiaochuan@loongson.cn>
This commit is contained in:
@@ -27,12 +27,60 @@ static u32 unaligned_instructions_user;
|
||||
static u32 unaligned_instructions_kernel;
|
||||
#endif
|
||||
|
||||
static inline unsigned long read_fpr(unsigned int idx);
|
||||
static inline void write_fpr(unsigned int idx, unsigned long value);
|
||||
|
||||
/*
|
||||
* Temporarily enable the FPU while touching hardware FP registers.
|
||||
* The exception entry disables EUEN, so using base FP instructions in the
|
||||
* unaligned-access emulator would otherwise trigger an FPD exception.
|
||||
*/
|
||||
static inline unsigned long read_fpr_hw(unsigned int idx)
|
||||
{
|
||||
unsigned long value;
|
||||
|
||||
preempt_disable();
|
||||
value = read_fpr(idx);
|
||||
preempt_enable();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void write_fpr_hw(unsigned int idx, unsigned long value)
|
||||
{
|
||||
preempt_disable();
|
||||
write_fpr(idx, value);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static inline unsigned long read_fpr(unsigned int idx)
|
||||
{
|
||||
#define READ_FPR(idx, __value) \
|
||||
__asm__ __volatile__("movfr2gr.d %0, $f"#idx"\n\t" : "=r"(__value));
|
||||
|
||||
unsigned long __value;
|
||||
unsigned int ori_euen_fpu;
|
||||
unsigned int ori_CRMD_IE;
|
||||
|
||||
ori_CRMD_IE = 0;
|
||||
__asm__ volatile (
|
||||
"addi.d %0, $zero, 0x0\t\n"
|
||||
"addi.d $t2, $zero, 0x4\t\n"
|
||||
"csrxchg %0, $t2, 0x0\t\n"
|
||||
: "+r"(ori_CRMD_IE)
|
||||
:
|
||||
: "$t2"
|
||||
);
|
||||
|
||||
ori_euen_fpu = 0;
|
||||
__asm__ volatile (
|
||||
"addi.d %0, $zero, 0x1\t\n"
|
||||
"addi.d $t2, $zero, 0x1\t\n"
|
||||
"csrxchg %0, $t2, 0x2\t\n"
|
||||
: "+r"(ori_euen_fpu)
|
||||
:
|
||||
: "$t2"
|
||||
);
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
@@ -134,6 +182,21 @@ static inline unsigned long read_fpr(unsigned int idx)
|
||||
default:
|
||||
panic("unexpected idx '%d'", idx);
|
||||
}
|
||||
__asm__ volatile (
|
||||
"addi.d $t2, $zero, 0x1\t\n"
|
||||
"csrxchg %0, $t2, 0x2\t\n"
|
||||
:
|
||||
: "r"(ori_euen_fpu)
|
||||
: "$t2"
|
||||
);
|
||||
|
||||
__asm__ volatile (
|
||||
"addi.d $t2, $zero, 0x4\t\n"
|
||||
"csrxchg %0, $t2, 0x0\t\n"
|
||||
:
|
||||
: "r"(ori_CRMD_IE)
|
||||
: "$t2"
|
||||
);
|
||||
#undef READ_FPR
|
||||
return __value;
|
||||
}
|
||||
@@ -143,6 +206,29 @@ static inline void write_fpr(unsigned int idx, unsigned long value)
|
||||
#define WRITE_FPR(idx, value) \
|
||||
__asm__ __volatile__("movgr2fr.d $f"#idx", %0\n\t" :: "r"(value));
|
||||
|
||||
unsigned int ori_euen_fpu;
|
||||
unsigned int ori_CRMD_IE;
|
||||
|
||||
ori_CRMD_IE = 0;
|
||||
__asm__ volatile (
|
||||
"addi.d %0, $zero, 0x0\t\n"
|
||||
"addi.d $t2, $zero, 0x4\t\n"
|
||||
"csrxchg %0, $t2, 0x0\t\n"
|
||||
: "+r"(ori_CRMD_IE)
|
||||
:
|
||||
: "$t2"
|
||||
);
|
||||
|
||||
ori_euen_fpu = 0;
|
||||
__asm__ volatile (
|
||||
"addi.d %0, $zero, 0x1\t\n"
|
||||
"addi.d $t2, $zero, 0x1\t\n"
|
||||
"csrxchg %0, $t2, 0x2\t\n"
|
||||
: "+r"(ori_euen_fpu)
|
||||
:
|
||||
: "$t2"
|
||||
);
|
||||
|
||||
switch (idx) {
|
||||
case 0:
|
||||
WRITE_FPR(0, value);
|
||||
@@ -243,6 +329,22 @@ static inline void write_fpr(unsigned int idx, unsigned long value)
|
||||
default:
|
||||
panic("unexpected idx '%d'", idx);
|
||||
}
|
||||
__asm__ volatile (
|
||||
"addi.d $t2, $zero, 0x1\t\n"
|
||||
"csrxchg %0, $t2, 0x2\t\n"
|
||||
:
|
||||
: "r"(ori_euen_fpu)
|
||||
: "$t2"
|
||||
);
|
||||
|
||||
__asm__ volatile (
|
||||
"addi.d $t2, $zero, 0x4\t\n"
|
||||
"csrxchg %0, $t2, 0x0\t\n"
|
||||
:
|
||||
: "r"(ori_CRMD_IE)
|
||||
: "$t2"
|
||||
);
|
||||
|
||||
#undef WRITE_FPR
|
||||
}
|
||||
|
||||
@@ -431,7 +533,7 @@ void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned i
|
||||
regs->regs[insn.reg3_format.rd] = value;
|
||||
else {
|
||||
if (is_fpu_owner())
|
||||
write_fpr(insn.reg3_format.rd, value);
|
||||
write_fpr_hw(insn.reg3_format.rd, value);
|
||||
else
|
||||
set_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0, value);
|
||||
}
|
||||
@@ -441,7 +543,7 @@ void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned i
|
||||
value = regs->regs[insn.reg3_format.rd];
|
||||
else {
|
||||
if (is_fpu_owner())
|
||||
value = read_fpr(insn.reg3_format.rd);
|
||||
value = read_fpr_hw(insn.reg3_format.rd);
|
||||
else
|
||||
value = get_fpr64(¤t->thread.fpu.fpr[insn.reg3_format.rd], 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user