]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
LoongArch: Make do_xyz() exception handlers more robust
authorTiezhu Yang <yangtiezhu@loongson.cn>
Thu, 24 Apr 2025 12:15:41 +0000 (20:15 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Sat, 26 Apr 2025 01:58:12 +0000 (09:58 +0800)
Currently, interrupts need to be disabled before single-step mode is
set, it requires that CSR_PRMD_PIE be cleared in save_local_irqflag()
which is called by setup_singlestep(), this is reasonable.

But in the first kprobe breakpoint exception, if the irq is enabled at
the beginning of do_bp(), it will not be disabled at the end of do_bp()
due to the CSR_PRMD_PIE has been cleared in save_local_irqflag(). So for
this case, it may corrupt exception context when restoring the exception
after do_bp() in handle_bp(), this is not reasonable.

In order to restore exception safely in handle_bp(), it needs to ensure
the irq is disabled at the end of do_bp(), so just add a local variable
to record the original interrupt status in the parent context, then use
it as the check condition to enable and disable irq in do_bp().

While at it, do the similar thing for other do_xyz() exception handlers
to make them more robust.

Fixes: 6d4cc40fb5f5 ("LoongArch: Add kprobes support")
Suggested-by: Jinyang He <hejinyang@loongson.cn>
Suggested-by: Huacai Chen <chenhuacai@loongson.cn>
Co-developed-by: Tianyang Zhang <zhangtianyang@loongson.cn>
Signed-off-by: Tianyang Zhang <zhangtianyang@loongson.cn>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/kernel/traps.c

index 2ec3106c0da3d115061d79b2c4d7bde5af3bef0e..47fc2de6d15018bb77fbede4f3e5308e15fe21c9 100644 (file)
@@ -553,9 +553,10 @@ asmlinkage void noinstr do_ale(struct pt_regs *regs)
        die_if_kernel("Kernel ale access", regs);
        force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr);
 #else
+       bool pie = regs_irqs_disabled(regs);
        unsigned int *pc;
 
-       if (regs->csr_prmd & CSR_PRMD_PIE)
+       if (!pie)
                local_irq_enable();
 
        perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, regs->csr_badvaddr);
@@ -582,7 +583,7 @@ sigbus:
        die_if_kernel("Kernel ale access", regs);
        force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr);
 out:
-       if (regs->csr_prmd & CSR_PRMD_PIE)
+       if (!pie)
                local_irq_disable();
 #endif
        irqentry_exit(regs, state);
@@ -621,12 +622,13 @@ static void bug_handler(struct pt_regs *regs)
 asmlinkage void noinstr do_bce(struct pt_regs *regs)
 {
        bool user = user_mode(regs);
+       bool pie = regs_irqs_disabled(regs);
        unsigned long era = exception_era(regs);
        u64 badv = 0, lower = 0, upper = ULONG_MAX;
        union loongarch_instruction insn;
        irqentry_state_t state = irqentry_enter(regs);
 
-       if (regs->csr_prmd & CSR_PRMD_PIE)
+       if (!pie)
                local_irq_enable();
 
        current->thread.trap_nr = read_csr_excode();
@@ -692,7 +694,7 @@ asmlinkage void noinstr do_bce(struct pt_regs *regs)
        force_sig_bnderr((void __user *)badv, (void __user *)lower, (void __user *)upper);
 
 out:
-       if (regs->csr_prmd & CSR_PRMD_PIE)
+       if (!pie)
                local_irq_disable();
 
        irqentry_exit(regs, state);
@@ -710,11 +712,12 @@ bad_era:
 asmlinkage void noinstr do_bp(struct pt_regs *regs)
 {
        bool user = user_mode(regs);
+       bool pie = regs_irqs_disabled(regs);
        unsigned int opcode, bcode;
        unsigned long era = exception_era(regs);
        irqentry_state_t state = irqentry_enter(regs);
 
-       if (regs->csr_prmd & CSR_PRMD_PIE)
+       if (!pie)
                local_irq_enable();
 
        if (__get_inst(&opcode, (u32 *)era, user))
@@ -780,7 +783,7 @@ asmlinkage void noinstr do_bp(struct pt_regs *regs)
        }
 
 out:
-       if (regs->csr_prmd & CSR_PRMD_PIE)
+       if (!pie)
                local_irq_disable();
 
        irqentry_exit(regs, state);
@@ -1015,6 +1018,7 @@ static void init_restore_lbt(void)
 
 asmlinkage void noinstr do_lbt(struct pt_regs *regs)
 {
+       bool pie = regs_irqs_disabled(regs);
        irqentry_state_t state = irqentry_enter(regs);
 
        /*
@@ -1024,7 +1028,7 @@ asmlinkage void noinstr do_lbt(struct pt_regs *regs)
         * (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)
+       if (!pie)
                local_irq_enable();
 
        if (!cpu_has_lbt) {
@@ -1038,7 +1042,7 @@ asmlinkage void noinstr do_lbt(struct pt_regs *regs)
        preempt_enable();
 
 out:
-       if (regs->csr_prmd & CSR_PRMD_PIE)
+       if (!pie)
                local_irq_disable();
 
        irqentry_exit(regs, state);