[PATCH] arm64: abort SDEI handlers during crash

D Scott Phillips scott at os.amperecomputing.com
Fri Feb 3 16:08:51 PST 2023


Interrupts are blocked in SDEI context, per the SDEI spec: "The client
interrupts cannot preempt the event handler." If we crashed in the SDEI
handler-running context (as with ACPI's AGDI) then we need to clean up the
SDEI state before proceeding to the crash kernel so that the crash kernel
can have working interrupts.  Try two COMPLETE_AND_RESUMEs in case both a
normal and critical event were being handled.

Fixes: f5df26961853 ("arm64: kernel: Add arch-specific SDEI entry code and CPU masking")
Signed-off-by: D Scott Phillips <scott at os.amperecomputing.com>
---
 arch/arm64/include/asm/sdei.h |  3 +++
 arch/arm64/kernel/entry.S     | 24 +++++++++++++++++++++---
 arch/arm64/kernel/smp.c       | 14 ++++++++++----
 3 files changed, 34 insertions(+), 7 deletions(-)

diff --git a/arch/arm64/include/asm/sdei.h b/arch/arm64/include/asm/sdei.h
index 4292d9bafb9d..1030568db7d3 100644
--- a/arch/arm64/include/asm/sdei.h
+++ b/arch/arm64/include/asm/sdei.h
@@ -29,6 +29,9 @@ asmlinkage void __sdei_asm_entry_trampoline(unsigned long event_num,
 						   unsigned long pc,
 						   unsigned long pstate);
 
+/* End a possibly still running handler. Context is discarded. */
+void sdei_handler_abort(void);
+
 /*
  * The above entry point does the minimum to call C code. This function does
  * anything else, before calling the driver.
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 11cb99c4d298..03dc233bdaa1 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -909,13 +909,18 @@ NOKPROBE(call_on_irq_stack)
 #include <asm/sdei.h>
 #include <uapi/linux/arm_sdei.h>
 
-.macro sdei_handler_exit exit_mode
-	/* On success, this call never returns... */
+.macro sdei_handler_exit_fallthrough exit_mode
 	cmp	\exit_mode, #SDEI_EXIT_SMC
 	b.ne	99f
 	smc	#0
-	b	.
+	b	100f
 99:	hvc	#0
+100:
+.endm
+
+.macro sdei_handler_exit exit_mode
+	sdei_handler_exit_fallthrough exit_mode=\exit_mode
+	/* On success, this call never returns... */
 	b	.
 .endm
 
@@ -1077,4 +1082,17 @@ alternative_else_nop_endif
 #endif
 SYM_CODE_END(__sdei_asm_handler)
 NOKPROBE(__sdei_asm_handler)
+
+SYM_CODE_START(sdei_handler_abort)
+	mov_q	x0, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME
+	adr	x1, 1f
+	ldr_l	x2, sdei_exit_mode
+	sdei_handler_exit_fallthrough exit_mode=x2
+	// either fallthrough if not in handler context, or exit the handler
+	// and jump to the next instruction. Exit will stomp x0-x17, PSTATE,
+	// ELR_ELx, and SPSR_ELx.
+1:	ret
+SYM_CODE_END(sdei_handler_abort)
+NOKPROBE(sdei_handler_abort)
+
 #endif /* CONFIG_ARM_SDE_INTERFACE */
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index ffc5d76cf695..bc1b3000197e 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -1047,10 +1047,8 @@ void crash_smp_send_stop(void)
 	 * If this cpu is the only one alive at this point in time, online or
 	 * not, there are no stop messages to be sent around, so just back out.
 	 */
-	if (num_other_online_cpus() == 0) {
-		sdei_mask_local_cpu();
-		return;
-	}
+	if (num_other_online_cpus() == 0)
+		goto skip_ipi;
 
 	cpumask_copy(&mask, cpu_online_mask);
 	cpumask_clear_cpu(smp_processor_id(), &mask);
@@ -1069,7 +1067,15 @@ void crash_smp_send_stop(void)
 		pr_warn("SMP: failed to stop secondary CPUs %*pbl\n",
 			cpumask_pr_args(&mask));
 
+skip_ipi:
 	sdei_mask_local_cpu();
+	/*
+	 * The crash may have happened in a critical event handler which
+	 * preempted a normal handler. So at most we might have two
+	 * levels of SDEI context to exit.
+	 */
+	sdei_handler_abort();
+	sdei_handler_abort();
 }
 
 bool smp_crash_stop_failed(void)
-- 
2.39.1




More information about the linux-arm-kernel mailing list