[RFC PATCH v2 12/45] arm64: interrupts: Add common exception state helpers
Jinjie Ruan
ruanjinjie at huawei.com
Tue Jul 28 01:20:56 PDT 2026
在 2026/7/28 0:34, Vladimir Murzin 写道:
> From: Ada Couprie Diaz <ada.coupriediaz at arm.com>
>
> The entry code and the rest of the kernel require different interrupt
> masking APIs, but all need a common representation of the hardware
> exception state.
>
> Introduce exception contexts that map DAIF and PMR state to the
> corresponding masking context. Provide helpers to translate between
> exception contexts and hardware state, verify the current state, and
> update the hardware state.
>
> Updating PMR is unnecessary when a caller knows that its value has not
> changed. Allow such callers to avoid the update, while providing a
> force option for callers that cannot rely on the previous PMR state.
>
> Entry specific and general purpose masking APIs will be built on these
> helpers in subsequent patches.
>
> Signed-off-by: Ada Couprie Diaz <ada.coupriediaz at arm.com>
> Signed-off-by: Vladimir Murzin <vladimir.murzin at arm.com>
> ---
> .../include/asm/interrupts/common_flags.h | 208 ++++++++++++++++++
> 1 file changed, 208 insertions(+)
> create mode 100644 arch/arm64/include/asm/interrupts/common_flags.h
>
> diff --git a/arch/arm64/include/asm/interrupts/common_flags.h b/arch/arm64/include/asm/interrupts/common_flags.h
> new file mode 100644
> index 000000000000..f034dcafccbc
> --- /dev/null
> +++ b/arch/arm64/include/asm/interrupts/common_flags.h
> @@ -0,0 +1,208 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2025 Arm Ltd.
> + */
> +#ifndef __ASM_INTERRUPTS_COMMON_FLAGS_H
> +#define __ASM_INTERRUPTS_COMMON_FLAGS_H
> +
> +#include <asm/arch_gicv3.h>
> +#include <asm/bug.h>
> +#include <asm/cpufeature.h>
> +#include <asm/ptrace.h>
> +#include <asm/sysreg.h>
> +#include <asm/irqflags.h>
> +
> +#define DAIF_PROCCTX 0
> +#define DAIF_PROCCTX_NOIRQ (PSR_I_BIT | PSR_F_BIT)
> +#define DAIF_ERRCTX (PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
> +#define DAIF_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
> +
> +/*
> + * Exception context mapping
> + *
> + * pseudo-NMI
> + *
> + * CRITICAL -> DAIF + IRQON (corresponds to the state on exception entry)
> + * ERROR -> AIF + IRQON
> + * NONMI -> IF + IRQON
> + * NOIRQ -> 0 + IRQOFF
> + * PROCESS -> 0 + IRQON
> + *
> + * Otherwise
> + *
> + * CRITICAL -> DAIF (corresponds to the state on exception entry)
> + * ERROR -> AIF
> + * NONMI -> IF
> + * NOIRQ -> IF
> + * PROCESS -> 0
> + */
> +typedef enum arm64_exc_context {
> + PROCESS_CONTEXT,
> + NOIRQ_CONTEXT,
> + NONMI_CONTEXT,
> + ERROR_CONTEXT,
> + CRITICAL_CONTEXT,
> +} arm64_exc_context_t;
> +
> +static __always_inline
> +arm64_exc_hwstate_t __arm64_exc_hwstate_of_process_context(void)
> +{
> + if (system_uses_irq_prio_masking())
> + return (arm64_exc_hwstate_t){.daif=DAIF_PROCCTX, .pmr=GIC_PRIO_IRQON};
> +
> + return (arm64_exc_hwstate_t){.daif=DAIF_PROCCTX};
> +}
> +
> +static __always_inline
> +arm64_exc_hwstate_t __arm64_exc_hwstate_of_noirq_context(void)
> +{
> + if (system_uses_irq_prio_masking())
> + return (arm64_exc_hwstate_t){.daif=DAIF_PROCCTX, .pmr=GIC_PRIO_IRQOFF};
> +
> + return (arm64_exc_hwstate_t){.daif=DAIF_PROCCTX_NOIRQ};
> +}
> +
> +static __always_inline
> +arm64_exc_hwstate_t __arm64_exc_hwstate_of_nonmi_context(void)
> +{
> + if (system_uses_irq_prio_masking())
> + return (arm64_exc_hwstate_t){.daif=DAIF_PROCCTX_NOIRQ, .pmr=GIC_PRIO_IRQON};
> +
> + return (arm64_exc_hwstate_t){.daif=DAIF_PROCCTX_NOIRQ};
> +}
> +
> +static __always_inline
> +arm64_exc_hwstate_t __arm64_exc_hwstate_of_error_context(void)
> +{
> + if (system_uses_irq_prio_masking())
> + return (arm64_exc_hwstate_t){.daif=DAIF_ERRCTX, .pmr=GIC_PRIO_IRQON};
> +
> + return (arm64_exc_hwstate_t){.daif=DAIF_ERRCTX};
> +}
> +
> +static __always_inline
> +arm64_exc_hwstate_t __arm64_exc_hwstate_of_critical_context(void)
> +{
> + if (system_uses_irq_prio_masking())
> + return (arm64_exc_hwstate_t){.daif=DAIF_MASK, .pmr=GIC_PRIO_IRQON};
> +
> + return (arm64_exc_hwstate_t){.daif=DAIF_MASK};
> +}
> +
> +static __always_inline
> +arm64_exc_hwstate_t arm64_exc_hwstate_of_context(arm64_exc_context_t context) {
> + switch (context) {
> + case PROCESS_CONTEXT:
> + return __arm64_exc_hwstate_of_process_context();
> + case NOIRQ_CONTEXT:
> + return __arm64_exc_hwstate_of_noirq_context();
> + case NONMI_CONTEXT:
> + return __arm64_exc_hwstate_of_nonmi_context();
> + case ERROR_CONTEXT:
> + return __arm64_exc_hwstate_of_error_context();
> + case CRITICAL_CONTEXT:
> + return __arm64_exc_hwstate_of_critical_context();
> + default:
> + BUG();
> + }
> +}
Hi Vladimir,
Using two tables as below to map different contexts to the hwstate
status makes the code appear more concise and clear. What do you think?
--- /dev/null
+++ b/arch/arm64/include/asm/interrupts/common_flags.h
+typedef enum arm64_exc_context {
+ PROCESS_CONTEXT,
+ NOIRQ_CONTEXT,
+ NONMI_CONTEXT,
+ ERROR_CONTEXT,
+ CRITICAL_CONTEXT,
+ NR_CONTEXTS,
+} arm64_exc_context_t;
+static const arm64_exc_hwstate_t daif_context_hwstate[] = {
+ [PROCESS_CONTEXT] = { .daif = DAIF_PROCCTX },
+ [NOIRQ_CONTEXT] = { .daif = DAIF_PROCCTX_NOIRQ },
+ [NONMI_CONTEXT] = { .daif = DAIF_PROCCTX_NOIRQ },
+ [ERROR_CONTEXT] = { .daif = DAIF_ERRCTX },
+ [CRITICAL_CONTEXT] = { .daif = DAIF_MASK },
+};
+
+static const arm64_exc_hwstate_t pseudo_nmi_context_hwstate[] = {
+ [PROCESS_CONTEXT] = { .daif = DAIF_PROCCTX, .pmr =
GIC_PRIO_IRQON },
+ [NOIRQ_CONTEXT] = { .daif = DAIF_PROCCTX, .pmr =
GIC_PRIO_IRQOFF},
+ [NONMI_CONTEXT] = { .daif = DAIF_PROCCTX_NOIRQ, .pmr =
GIC_PRIO_IRQON },
+ [ERROR_CONTEXT] = { .daif = DAIF_ERRCTX, .pmr =
GIC_PRIO_IRQON },
+ [CRITICAL_CONTEXT] = { .daif = DAIF_MASK, .pmr =
GIC_PRIO_IRQON },
+};
+
+static __always_inline
+arm64_exc_hwstate_t arm64_exc_hwstate_of_context(arm64_exc_context_t
context)
+{
+ if (unlikely((unsigned int)context >= NR_CONTEXTS))
+ BUG();
+
+ if (system_uses_irq_prio_masking())
+ return pnmi_table[context];
+
+ return legacy_table[context];
+}
+
> +
> +static __always_inline
> +arm64_exc_hwstate_t arm64_inherit_exc_hwstate(struct pt_regs *regs)
> +{
> + arm64_exc_hwstate_t state = {.daif=regs->pstate & DAIF_MASK};
> +
> + if (system_uses_irq_prio_masking())
> + state.pmr = regs->pmr;
> +
> + return state;
> +}
> +
> +static __always_inline
> +void arm64_debug_exc_hwstate(arm64_exc_hwstate_t expected)
> +{
> + arm64_exc_hwstate_t actual;
> +
> + if (!IS_ENABLED(CONFIG_DEBUG_IRQFLAGS))
> + return;
> +
> + actual.flags = arch_local_save_flags();
> +
> + if (expected.flags == actual.flags)
> + return;
> +
> + if (system_uses_irq_prio_masking()) {
> + WARN_ONCE(1, "Unexpected DAIF+PMR: 0x%x + 0x%x (expected 0x%x + 0x%x)\n",
> + actual.daif, actual.pmr, expected.daif, expected.pmr);
> + } else {
> + WARN_ONCE(1, "Unexpected DAIF: 0x%x (expected 0x%x)\n",
> + actual.daif, expected.daif);
> + }
> +}
> +
> +static __always_inline
> +void arm64_debug_exc_context(arm64_exc_context_t context)
> +{
> + arm64_exc_hwstate_t expected = arm64_exc_hwstate_of_context(context);
> +
> + arm64_debug_exc_hwstate(expected);
> +}
> +
> +static __always_inline
> +void __arm64_update_exc_hwstate(arm64_exc_hwstate_t hwstate, bool force)
> +{
> + barrier();
I don't understand the purpose of the barrier at the beginning and end
of the function. Is this fix also required for the original
implementation of local_daif_restore()?
> +
> + if (system_uses_irq_prio_masking() &&
> + hwstate.pmr == GIC_PRIO_IRQOFF &&
> + force) {
> + /*
> + * There has been concern that the write to daif
> + * might be reordered before this write to PMR.
> + * From the ARM ARM DDI 0487D.a, section D1.7.1
> + * "Accessing PSTATE fields":
> + * Writes to the PSTATE fields have side-effects on
> + * various aspects of the PE operation. All of these
> + * side-effects are guaranteed:
> + * - Not to be visible to earlier instructions in
> + * the execution stream.
> + * - To be visible to later instructions in the
> + * execution stream
> + *
> + * Also, writes to PMR are self-synchronizing, so no
> + * interrupts with a lower priority than PMR is signaled
> + * to the PE after the write.
> + *
> + * So we don't need additional synchronization here.
> + */
> + write_sysreg_s(hwstate.pmr, SYS_ICC_PMR_EL1);
> + }
> +
> + barrier();
This barrier seems redundant and inconsistent with the comment above.
> + write_sysreg(hwstate.daif, daif);
> + barrier();
> +
> + if (system_uses_irq_prio_masking() &&
> + hwstate.pmr == GIC_PRIO_IRQON &&
> + force) {
> + write_sysreg_s(hwstate.pmr, SYS_ICC_PMR_EL1);
> + pmr_sync();
> + }
> +
> + barrier();
> +}
If we no use pesudo NMI, the barrier is not nessary,
static __always_inline
void __arm64_update_exc_hwstate(arm64_exc_hwstate_t hwstate, bool force)
{
if (system_uses_irq_prio_masking() && force) {
if (hwstate.pmr == GIC_PRIO_IRQOFF)
write_sysreg_s(hwstate.pmr, SYS_ICC_PMR_EL1);
write_sysreg(hwstate.daif, daif);
if (hwstate.pmr == GIC_PRIO_IRQON) {
barrier();
write_sysreg_s(hwstate.pmr, SYS_ICC_PMR_EL1);
pmr_sync();
}
} else {
write_sysreg(hwstate.daif, daif);
}
}
otherwise, LGTM
Reviewed-by: Jinjie Ruan <ruanjinjie at huawei.com>
> +
> +static __always_inline
> +void arm64_update_exc_hwstate(arm64_exc_hwstate_t hwstate)
> +{
> + __arm64_update_exc_hwstate(hwstate, true);
> +}
> +
> +static __always_inline
> +void arm64_update_exc_context(arm64_exc_context_t context)
> +{
> + arm64_exc_hwstate_t hwstate = arm64_exc_hwstate_of_context(context);
> +
> + arm64_update_exc_hwstate(hwstate);
> +}
> +#endif /* __ASM_INTERRUPTS_COMMON_FLAGS_H */
More information about the linux-arm-kernel
mailing list