Skip to content

Commit 5cd474e

Browse files
D Scott Phillipswilldeacon
authored andcommitted
arm64: sdei: abort running SDEI handlers during crash
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. Track the active SDEI handler per-cpu so that we can COMPLETE_AND_RESUME the handler, discarding the interrupted context. Fixes: f5df269 ("arm64: kernel: Add arch-specific SDEI entry code and CPU masking") Signed-off-by: D Scott Phillips <scott@os.amperecomputing.com> Cc: stable@vger.kernel.org Reviewed-by: James Morse <james.morse@arm.com> Tested-by: Mihai Carabas <mihai.carabas@oracle.com> Link: https://lore.kernel.org/r/20230627002939.2758-1-scott@os.amperecomputing.com Signed-off-by: Will Deacon <will@kernel.org>
1 parent b9d6012 commit 5cd474e

File tree

6 files changed

+59
-6
lines changed

6 files changed

+59
-6
lines changed

arch/arm64/include/asm/sdei.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
#include <asm/virt.h>
1919

20+
DECLARE_PER_CPU(struct sdei_registered_event *, sdei_active_normal_event);
21+
DECLARE_PER_CPU(struct sdei_registered_event *, sdei_active_critical_event);
22+
2023
extern unsigned long sdei_exit_mode;
2124

2225
/* Software Delegated Exception entry point from firmware*/
@@ -29,6 +32,9 @@ asmlinkage void __sdei_asm_entry_trampoline(unsigned long event_num,
2932
unsigned long pc,
3033
unsigned long pstate);
3134

35+
/* Abort a running handler. Context is discarded. */
36+
void __sdei_handler_abort(void);
37+
3238
/*
3339
* The above entry point does the minimum to call C code. This function does
3440
* anything else, before calling the driver.

arch/arm64/kernel/entry.S

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -986,9 +986,13 @@ SYM_CODE_START(__sdei_asm_handler)
986986

987987
mov x19, x1
988988

989-
#if defined(CONFIG_VMAP_STACK) || defined(CONFIG_SHADOW_CALL_STACK)
989+
/* Store the registered-event for crash_smp_send_stop() */
990990
ldrb w4, [x19, #SDEI_EVENT_PRIORITY]
991-
#endif
991+
cbnz w4, 1f
992+
adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x6
993+
b 2f
994+
1: adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x6
995+
2: str x19, [x5]
992996

993997
#ifdef CONFIG_VMAP_STACK
994998
/*
@@ -1055,6 +1059,14 @@ SYM_CODE_START(__sdei_asm_handler)
10551059

10561060
ldr_l x2, sdei_exit_mode
10571061

1062+
/* Clear the registered-event seen by crash_smp_send_stop() */
1063+
ldrb w3, [x4, #SDEI_EVENT_PRIORITY]
1064+
cbnz w3, 1f
1065+
adr_this_cpu dst=x5, sym=sdei_active_normal_event, tmp=x6
1066+
b 2f
1067+
1: adr_this_cpu dst=x5, sym=sdei_active_critical_event, tmp=x6
1068+
2: str xzr, [x5]
1069+
10581070
alternative_if_not ARM64_UNMAP_KERNEL_AT_EL0
10591071
sdei_handler_exit exit_mode=x2
10601072
alternative_else_nop_endif
@@ -1065,4 +1077,15 @@ alternative_else_nop_endif
10651077
#endif
10661078
SYM_CODE_END(__sdei_asm_handler)
10671079
NOKPROBE(__sdei_asm_handler)
1080+
1081+
SYM_CODE_START(__sdei_handler_abort)
1082+
mov_q x0, SDEI_1_0_FN_SDEI_EVENT_COMPLETE_AND_RESUME
1083+
adr x1, 1f
1084+
ldr_l x2, sdei_exit_mode
1085+
sdei_handler_exit exit_mode=x2
1086+
// exit the handler and jump to the next instruction.
1087+
// Exit will stomp x0-x17, PSTATE, ELR_ELx, and SPSR_ELx.
1088+
1: ret
1089+
SYM_CODE_END(__sdei_handler_abort)
1090+
NOKPROBE(__sdei_handler_abort)
10681091
#endif /* CONFIG_ARM_SDE_INTERFACE */

arch/arm64/kernel/sdei.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ DEFINE_PER_CPU(unsigned long *, sdei_shadow_call_stack_normal_ptr);
4747
DEFINE_PER_CPU(unsigned long *, sdei_shadow_call_stack_critical_ptr);
4848
#endif
4949

50+
DEFINE_PER_CPU(struct sdei_registered_event *, sdei_active_normal_event);
51+
DEFINE_PER_CPU(struct sdei_registered_event *, sdei_active_critical_event);
52+
5053
static void _free_sdei_stack(unsigned long * __percpu *ptr, int cpu)
5154
{
5255
unsigned long *p;

arch/arm64/kernel/smp.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,10 +1044,8 @@ void crash_smp_send_stop(void)
10441044
* If this cpu is the only one alive at this point in time, online or
10451045
* not, there are no stop messages to be sent around, so just back out.
10461046
*/
1047-
if (num_other_online_cpus() == 0) {
1048-
sdei_mask_local_cpu();
1049-
return;
1050-
}
1047+
if (num_other_online_cpus() == 0)
1048+
goto skip_ipi;
10511049

10521050
cpumask_copy(&mask, cpu_online_mask);
10531051
cpumask_clear_cpu(smp_processor_id(), &mask);
@@ -1066,7 +1064,9 @@ void crash_smp_send_stop(void)
10661064
pr_warn("SMP: failed to stop secondary CPUs %*pbl\n",
10671065
cpumask_pr_args(&mask));
10681066

1067+
skip_ipi:
10691068
sdei_mask_local_cpu();
1069+
sdei_handler_abort();
10701070
}
10711071

10721072
bool smp_crash_stop_failed(void)

drivers/firmware/arm_sdei.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,3 +1095,22 @@ int sdei_event_handler(struct pt_regs *regs,
10951095
return err;
10961096
}
10971097
NOKPROBE_SYMBOL(sdei_event_handler);
1098+
1099+
void sdei_handler_abort(void)
1100+
{
1101+
/*
1102+
* If the crash happened in an SDEI event handler then we need to
1103+
* finish the handler with the firmware so that we can have working
1104+
* interrupts in the crash kernel.
1105+
*/
1106+
if (__this_cpu_read(sdei_active_critical_event)) {
1107+
pr_warn("still in SDEI critical event context, attempting to finish handler.\n");
1108+
__sdei_handler_abort();
1109+
__this_cpu_write(sdei_active_critical_event, NULL);
1110+
}
1111+
if (__this_cpu_read(sdei_active_normal_event)) {
1112+
pr_warn("still in SDEI normal event context, attempting to finish handler.\n");
1113+
__sdei_handler_abort();
1114+
__this_cpu_write(sdei_active_normal_event, NULL);
1115+
}
1116+
}

include/linux/arm_sdei.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,12 @@ int sdei_unregister_ghes(struct ghes *ghes);
4747
int sdei_mask_local_cpu(void);
4848
int sdei_unmask_local_cpu(void);
4949
void __init sdei_init(void);
50+
void sdei_handler_abort(void);
5051
#else
5152
static inline int sdei_mask_local_cpu(void) { return 0; }
5253
static inline int sdei_unmask_local_cpu(void) { return 0; }
5354
static inline void sdei_init(void) { }
55+
static inline void sdei_handler_abort(void) { }
5456
#endif /* CONFIG_ARM_SDE_INTERFACE */
5557

5658

0 commit comments

Comments
 (0)