Re: [PATCH 05/38] arm64: entry: Introduce entry specific exception masking helpers
From: Vladimir Murzin <hidden>
Date: 2026-09-21 14:15:25
On 9/17/26 10:53, Jinjie Ruan wrote:
在 2026/9/14 18:20, Vladimir Murzin 写道:quoted
From: Ada Couprie Diaz <redacted> The entry code handles interrupt masking differently from the rest of the kernel. Exception handlers enter and exit with all exceptions masked, but they must temporarily unmask the appropriate set of exceptions so that the rest of the handler executes with the expected exception state. For EL0 handlers, this means dropping to masking context appropriate for the work to be performed. For EL1 handlers, this means restoring the masking context of the interrupted task. In both cases, all exceptions must be masked again before returning from the exception handler. The rest of the kernel typically follows the opposite pattern: it raises the masking context to protect a critical section and later restores the previous context. Given these different usage patterns, introduce a dedicated set of exception masking helpers for the entry code. Keeping these helpers separate from the generic interrupt masking APIs makes the intended usage explicit and helps avoid mixing the two masking models. Signed-off-by: Ada Couprie Diaz <redacted> Signed-off-by: Vladimir Murzin <redacted> Reviewed-by: Jinjie Ruan <redacted> --- arch/arm64/include/asm/interrupts/entry.h | 100 ++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 arch/arm64/include/asm/interrupts/entry.hdiff --git a/arch/arm64/include/asm/interrupts/entry.h b/arch/arm64/include/asm/interrupts/entry.h new file mode 100644 index 000000000000..0cfca62aa25b --- /dev/null +++ b/arch/arm64/include/asm/interrupts/entry.h@@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2025 Arm Ltd. + */ +#ifndef __ASM_INTERRUPTS_ENTRY_H +#define __ASM_INTERRUPTS_ENTRY_H + +#include <asm/arch_gicv3.h> +#include <asm/bug.h> +#include <asm/cpufeature.h> +#include <asm/interrupts/common_flags.h> + + +static __always_inline +arm64_exc_hwstate_t __arm64_switch_exc_hwstate_to(arm64_exc_hwstate_t prev, + arm64_exc_hwstate_t next) +{ + bool irqs_disabled = arch_irqs_disabled_flags(next.flags); + bool force; + + arm64_debug_exc_hwstate(prev); + + if (prev.flags == next.flags) + return next; + + if (!irqs_disabled) + trace_hardirqs_on(); + + force = system_uses_irq_prio_masking() && prev.pmr != next.pmr; + + __arm64_update_exc_hwstate(next, force); + + if (irqs_disabled) + trace_hardirqs_off(); + + return next; +} + +static __always_inline +arm64_exc_hwstate_t arm64_inherit_exc_context(struct pt_regs *regs) +{ + arm64_exc_hwstate_t prev = arm64_exc_hwstate_of_context(CRITICAL_CONTEXT); + arm64_exc_hwstate_t next = arm64_inherit_exc_hwstate(regs);Could it be renamed to a more intuitive name, such as arm64_exc_hwstate_of_regs()?
Ack.
quoted
+ + return __arm64_switch_exc_hwstate_to(prev, next); +} + +static __always_inline +arm64_exc_hwstate_t arm64_drop_exc_context(arm64_exc_hwstate_t prev, arm64_exc_context_t context) +{ + arm64_exc_hwstate_t next = arm64_exc_hwstate_of_context(context); + + if (IS_ENABLED(CONFIG_DEBUG_IRQFLAGS)) { + WARN_ON_ONCE(prev.daif < next.daif); + + if (prev.daif == next.daif) { + /* + * GIC_PRIO_IRQON is larger that GIC_PRIO_IRQOFF so larger PMR value is weaker + */ + WARN_ON_ONCE(system_uses_irq_prio_masking() && prev.pmr > next.pmr); + }The if branch is a dead code now, we can remove it.
Let compiler do the job :)
arm64_drop_exc_context is only used directly in the context of el0t_64_error_handler below. It switches from ERROR_CONTEXT to PROCESS_CONTEXT.
Such specialization goes against the idea of generalization. We don't want to touch the helpers every time we decide to change a caller - it's too much cognitive load to keep all these details in mind, and they're easy to miss. Thanks Vladimir
And now arm64_unmask_exc_context always switch from CRITICAL_CONTEXT to ERROR_CONTEXT/NONMI_CONTEXT/NOIRQ_CONTEXT/PROCESS_CONTEXT. So there is no case where prev and next DAIF are the same. 1009 static void noinstr __el0_error_handler_common(struct pt_regs *regs) 1010 { 1011 >-------unsigned long esr = read_sysreg(esr_el1); 1012 >-------arm64_exc_hwstate_t hwstate; 1013 >-------irqentry_state_t state; 1014 1015 >-------arm64_enter_from_user_mode(regs); 1016 >-------hwstate = arm64_unmask_exc_context(ERROR_CONTEXT); 1017 >-------state = irqentry_nmi_enter(regs); 1018 >-------do_serror(regs, esr); 1019 >-------irqentry_nmi_exit(regs, state); 1020 >-------hwstate = arm64_drop_exc_context(hwstate, PROCESS_CONTEXT); 1021 >-------arm64_exit_to_user_mode(regs, hwstate); 1022 >-------arm64_debug_exc_context(CRITICAL_CONTEXT); 1023 } 1024 1025 asmlinkage void noinstr el0t_64_error_handler(struct pt_regs *regs) 1026 { 1027 >-------__el0_error_handler_common(regs); 1028 }quoted
+ } + + return __arm64_switch_exc_hwstate_to(prev, next); +} + +static __always_inline +arm64_exc_hwstate_t arm64_lift_exc_context(arm64_exc_hwstate_t prev, arm64_exc_context_t context) +{ + arm64_exc_hwstate_t next = arm64_exc_hwstate_of_context(context); + + if (IS_ENABLED(CONFIG_DEBUG_IRQFLAGS)) { + WARN_ON_ONCE(prev.daif > next.daif); + + if (prev.daif == next.daif) { + /* + * GIC_PRIO_IRQON is larger that GIC_PRIO_IRQOFF so smaller PMR value is stronger + */ + WARN_ON_ONCE(system_uses_irq_prio_masking() && prev.pmr < next.pmr); + } + } + + return __arm64_switch_exc_hwstate_to(prev, next); +} + +static __always_inline +arm64_exc_hwstate_t arm64_unmask_exc_context(arm64_exc_context_t context) +{ + arm64_exc_hwstate_t prev = arm64_exc_hwstate_of_context(CRITICAL_CONTEXT); + + return arm64_drop_exc_context(prev, context); +} + +static __always_inline +arm64_exc_hwstate_t arm64_mask_exc_context(arm64_exc_hwstate_t prev) +{ + return arm64_lift_exc_context(prev, CRITICAL_CONTEXT); +} + +#endif /* __ASM_INTERRUPTS_ENTRY_H */-- Best regards, Jinjie