Message ID | 1548084825-8803-14-git-send-email-julien.thierry@arm.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | arm64: provide pseudo NMI with GICv3 | expand |
On Mon, 21 Jan 2019 15:33:32 +0000, Julien Thierry <julien.thierry@arm.com> wrote: > > The addition of PMR should not bypass the semantics of daifflags. > > When DA_F are set, I bit is also set as no interrupts (even of higher > priority) is allowed. > > When DA_F are cleared, I bit is cleared and interrupt enabling/disabling > goes through ICC_PMR_EL1. > > Signed-off-by: Julien Thierry <julien.thierry@arm.com> > Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> > Cc: Catalin Marinas <catalin.marinas@arm.com> > Cc: Will Deacon <will.deacon@arm.com> > Cc: James Morse <james.morse@arm.com> > --- > arch/arm64/include/asm/daifflags.h | 31 +++++++++++++++++++++++++++---- > 1 file changed, 27 insertions(+), 4 deletions(-) > > diff --git a/arch/arm64/include/asm/daifflags.h b/arch/arm64/include/asm/daifflags.h > index 546bc39..1fd390e 100644 > --- a/arch/arm64/include/asm/daifflags.h > +++ b/arch/arm64/include/asm/daifflags.h > @@ -18,6 +18,8 @@ > > #include <linux/irqflags.h> > > +#include <asm/cpufeature.h> > + > #define DAIF_PROCCTX 0 > #define DAIF_PROCCTX_NOIRQ PSR_I_BIT > > @@ -36,7 +38,13 @@ static inline unsigned long local_daif_save(void) > { > unsigned long flags; > > - flags = arch_local_save_flags(); > + flags = read_sysreg(daif); > + > + if (system_uses_irq_prio_masking()) { > + /* If IRQs are masked with PMR, reflect it in the flags */ > + if (read_sysreg_s(SYS_ICC_PMR_EL1) <= GIC_PRIO_IRQOFF) > + flags |= PSR_I_BIT; > + } > > local_daif_mask(); > > @@ -45,12 +53,27 @@ static inline unsigned long local_daif_save(void) > > static inline void local_daif_restore(unsigned long flags) > { > - if (!arch_irqs_disabled_flags(flags)) > + bool irq_disabled = flags & PSR_I_BIT; > + > + if (!irq_disabled) { > trace_hardirqs_on(); > > - arch_local_irq_restore(flags); > + if (system_uses_irq_prio_masking()) > + arch_local_irq_enable(); > + } else if (!(flags & PSR_A_BIT)) { > + /* > + * If interrupts are disabled but we can take > + * asynchronous errors, we can take NMIs > + */ > + if (system_uses_irq_prio_masking()) { > + flags &= ~PSR_I_BIT; > + arch_local_irq_disable(); > + } > + } > + > + write_sysreg(flags, daif); After much head scratching, I finally came to the conclusion that the above is safe, as a write to PSTATE is guaranteed not to be visible to instructions before it in program order (and the write to PMR is itself self-synchronising). I guess that a reference to the ARM ARM is in order here (no pun intended...). > > - if (arch_irqs_disabled_flags(flags)) > + if (irq_disabled) > trace_hardirqs_off(); > } > > -- > 1.9.1 > With the above, Reviewed-by: Marc Zyngier <marc.zyngier@arm.com> M.
diff --git a/arch/arm64/include/asm/daifflags.h b/arch/arm64/include/asm/daifflags.h index 546bc39..1fd390e 100644 --- a/arch/arm64/include/asm/daifflags.h +++ b/arch/arm64/include/asm/daifflags.h @@ -18,6 +18,8 @@ #include <linux/irqflags.h> +#include <asm/cpufeature.h> + #define DAIF_PROCCTX 0 #define DAIF_PROCCTX_NOIRQ PSR_I_BIT @@ -36,7 +38,13 @@ static inline unsigned long local_daif_save(void) { unsigned long flags; - flags = arch_local_save_flags(); + flags = read_sysreg(daif); + + if (system_uses_irq_prio_masking()) { + /* If IRQs are masked with PMR, reflect it in the flags */ + if (read_sysreg_s(SYS_ICC_PMR_EL1) <= GIC_PRIO_IRQOFF) + flags |= PSR_I_BIT; + } local_daif_mask(); @@ -45,12 +53,27 @@ static inline unsigned long local_daif_save(void) static inline void local_daif_restore(unsigned long flags) { - if (!arch_irqs_disabled_flags(flags)) + bool irq_disabled = flags & PSR_I_BIT; + + if (!irq_disabled) { trace_hardirqs_on(); - arch_local_irq_restore(flags); + if (system_uses_irq_prio_masking()) + arch_local_irq_enable(); + } else if (!(flags & PSR_A_BIT)) { + /* + * If interrupts are disabled but we can take + * asynchronous errors, we can take NMIs + */ + if (system_uses_irq_prio_masking()) { + flags &= ~PSR_I_BIT; + arch_local_irq_disable(); + } + } + + write_sysreg(flags, daif); - if (arch_irqs_disabled_flags(flags)) + if (irq_disabled) trace_hardirqs_off(); }