Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f0cd5ac

Browse files
mrutland-armwilldeacon
authored andcommitted
arm64: entry: fix NMI {user, kernel}->kernel transitions
Exceptions which can be taken at (almost) any time are consdiered to be NMIs. On arm64 that includes: * SDEI events * GICv3 Pseudo-NMIs * Kernel stack overflows * Unexpected/unhandled exceptions ... but currently debug exceptions (BRKs, breakpoints, watchpoints, single-step) are not considered NMIs. As these can be taken at any time, kernel features (lockdep, RCU, ftrace) may not be in a consistent kernel state. For example, we may take an NMI from the idle code or partway through an entry/exit path. While nmi_enter() and nmi_exit() handle most of this state, notably they don't save/restore the lockdep state across an NMI being taken and handled. When interrupts are enabled and an NMI is taken, lockdep may see interrupts become disabled within the NMI code, but not see interrupts become enabled when returning from the NMI, leaving lockdep believing interrupts are disabled when they are actually disabled. The x86 code handles this in idtentry_{enter,exit}_nmi(), which will shortly be moved to the generic entry code. As we can't use either yet, we copy the x86 approach in arm64-specific helpers. All the NMI entrypoints are marked as noinstr to prevent any instrumentation handling code being invoked before the state has been corrected. Signed-off-by: Mark Rutland <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: James Morse <[email protected]> Cc: Will Deacon <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent 7cd1ea1 commit f0cd5ac

File tree

4 files changed

+48
-10
lines changed

4 files changed

+48
-10
lines changed

arch/arm64/include/asm/exception.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ asmlinkage void noinstr enter_el1_irq_or_nmi(struct pt_regs *regs);
3535
asmlinkage void noinstr exit_el1_irq_or_nmi(struct pt_regs *regs);
3636
asmlinkage void enter_from_user_mode(void);
3737
asmlinkage void exit_to_user_mode(void);
38+
void arm64_enter_nmi(struct pt_regs *regs);
39+
void arm64_exit_nmi(struct pt_regs *regs);
3840
void do_mem_abort(unsigned long addr, unsigned int esr, struct pt_regs *regs);
3941
void do_undefinstr(struct pt_regs *regs);
4042
void do_bti(struct pt_regs *regs);

arch/arm64/kernel/entry-common.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,48 @@ static void noinstr exit_to_kernel_mode(struct pt_regs *regs)
6363
}
6464
}
6565

66+
void noinstr arm64_enter_nmi(struct pt_regs *regs)
67+
{
68+
regs->lockdep_hardirqs = lockdep_hardirqs_enabled();
69+
70+
__nmi_enter();
71+
lockdep_hardirqs_off(CALLER_ADDR0);
72+
lockdep_hardirq_enter();
73+
rcu_nmi_enter();
74+
75+
trace_hardirqs_off_finish();
76+
ftrace_nmi_enter();
77+
}
78+
79+
void noinstr arm64_exit_nmi(struct pt_regs *regs)
80+
{
81+
bool restore = regs->lockdep_hardirqs;
82+
83+
ftrace_nmi_exit();
84+
if (restore) {
85+
trace_hardirqs_on_prepare();
86+
lockdep_hardirqs_on_prepare(CALLER_ADDR0);
87+
}
88+
89+
rcu_nmi_exit();
90+
lockdep_hardirq_exit();
91+
if (restore)
92+
lockdep_hardirqs_on(CALLER_ADDR0);
93+
__nmi_exit();
94+
}
95+
6696
asmlinkage void noinstr enter_el1_irq_or_nmi(struct pt_regs *regs)
6797
{
6898
if (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && !interrupts_enabled(regs))
69-
nmi_enter();
99+
arm64_enter_nmi(regs);
70100
else
71101
enter_from_kernel_mode(regs);
72102
}
73103

74104
asmlinkage void noinstr exit_el1_irq_or_nmi(struct pt_regs *regs)
75105
{
76106
if (IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) && !interrupts_enabled(regs))
77-
nmi_exit();
107+
arm64_exit_nmi(regs);
78108
else
79109
exit_to_kernel_mode(regs);
80110
}

arch/arm64/kernel/sdei.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <linux/uaccess.h>
1111

1212
#include <asm/alternative.h>
13+
#include <asm/exception.h>
1314
#include <asm/kprobes.h>
1415
#include <asm/mmu.h>
1516
#include <asm/ptrace.h>
@@ -223,16 +224,16 @@ static __kprobes unsigned long _sdei_handler(struct pt_regs *regs,
223224
}
224225

225226

226-
asmlinkage __kprobes notrace unsigned long
227+
asmlinkage noinstr unsigned long
227228
__sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
228229
{
229230
unsigned long ret;
230231

231-
nmi_enter();
232+
arm64_enter_nmi(regs);
232233

233234
ret = _sdei_handler(regs, arg);
234235

235-
nmi_exit();
236+
arm64_exit_nmi(regs);
236237

237238
return ret;
238239
}

arch/arm64/kernel/traps.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <asm/daifflags.h>
3535
#include <asm/debug-monitors.h>
3636
#include <asm/esr.h>
37+
#include <asm/exception.h>
3738
#include <asm/extable.h>
3839
#include <asm/insn.h>
3940
#include <asm/kprobes.h>
@@ -753,8 +754,10 @@ const char *esr_get_class_string(u32 esr)
753754
* bad_mode handles the impossible case in the exception vector. This is always
754755
* fatal.
755756
*/
756-
asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
757+
asmlinkage void notrace bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
757758
{
759+
arm64_enter_nmi(regs);
760+
758761
console_verbose();
759762

760763
pr_crit("Bad mode in %s handler detected on CPU%d, code 0x%08x -- %s\n",
@@ -786,14 +789,16 @@ void bad_el0_sync(struct pt_regs *regs, int reason, unsigned int esr)
786789
DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack)
787790
__aligned(16);
788791

789-
asmlinkage void handle_bad_stack(struct pt_regs *regs)
792+
asmlinkage void noinstr handle_bad_stack(struct pt_regs *regs)
790793
{
791794
unsigned long tsk_stk = (unsigned long)current->stack;
792795
unsigned long irq_stk = (unsigned long)this_cpu_read(irq_stack_ptr);
793796
unsigned long ovf_stk = (unsigned long)this_cpu_ptr(overflow_stack);
794797
unsigned int esr = read_sysreg(esr_el1);
795798
unsigned long far = read_sysreg(far_el1);
796799

800+
arm64_enter_nmi(regs);
801+
797802
console_verbose();
798803
pr_emerg("Insufficient stack space to handle exception!");
799804

@@ -865,15 +870,15 @@ bool arm64_is_fatal_ras_serror(struct pt_regs *regs, unsigned int esr)
865870
}
866871
}
867872

868-
asmlinkage void do_serror(struct pt_regs *regs, unsigned int esr)
873+
asmlinkage void noinstr do_serror(struct pt_regs *regs, unsigned int esr)
869874
{
870-
nmi_enter();
875+
arm64_enter_nmi(regs);
871876

872877
/* non-RAS errors are not containable */
873878
if (!arm64_is_ras_serror(esr) || arm64_is_fatal_ras_serror(regs, esr))
874879
arm64_serror_panic(regs, esr);
875880

876-
nmi_exit();
881+
arm64_exit_nmi(regs);
877882
}
878883

879884
/* GENERIC_BUG traps */

0 commit comments

Comments
 (0)