arm64: Fix interrupt tracing in the presence of NMIs
In the presence of any form of instrumentation, nmi_enter() should be
done before calling any traceable code and any instrumentation code.
Currently, nmi_enter() is done in handle_domain_nmi(), which is much
too late as instrumentation code might get called before. Move the
nmi_enter/exit() calls to the arch IRQ vector handler.
On arm64, it is not possible to know if the IRQ vector handler was
called because of an NMI before acknowledging the interrupt. However, It
is possible to know whether normal interrupts could be taken in the
interrupted context (i.e. if taking an NMI in that context could
introduce a potential race condition).
When interrupting a context with IRQs disabled, call nmi_enter() as soon
as possible. In contexts with IRQs enabled, defer this to the interrupt
controller, which is in a better position to know if an interrupt taken
is an NMI.
Fixes: bc3c03ccb4
("arm64: Enable the support of pseudo-NMIs")
Cc: <stable@vger.kernel.org> # 5.1.x-
Cc: Will Deacon <will.deacon@arm.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Julien Thierry <julien.thierry@arm.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:

committed by
Catalin Marinas

parent
f57065782f
commit
17ce302f31
@@ -435,6 +435,20 @@ tsk .req x28 // current thread_info
|
||||
irq_stack_exit
|
||||
.endm
|
||||
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
/*
|
||||
* Set res to 0 if irqs were unmasked in interrupted context.
|
||||
* Otherwise set res to non-0 value.
|
||||
*/
|
||||
.macro test_irqs_unmasked res:req, pmr:req
|
||||
alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
||||
sub \res, \pmr, #GIC_PRIO_IRQON
|
||||
alternative_else
|
||||
mov \res, xzr
|
||||
alternative_endif
|
||||
.endm
|
||||
#endif
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
@@ -631,21 +645,21 @@ ENDPROC(el1_sync)
|
||||
el1_irq:
|
||||
kernel_entry 1
|
||||
enable_da_f
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
alternative_if ARM64_HAS_IRQ_PRIO_MASKING
|
||||
ldr x20, [sp, #S_PMR_SAVE]
|
||||
alternative_else
|
||||
mov x20, #GIC_PRIO_IRQON
|
||||
alternative_endif
|
||||
cmp x20, #GIC_PRIO_IRQOFF
|
||||
/* Irqs were disabled, don't trace */
|
||||
b.ls 1f
|
||||
#endif
|
||||
bl trace_hardirqs_off
|
||||
alternative_else_nop_endif
|
||||
test_irqs_unmasked res=x0, pmr=x20
|
||||
cbz x0, 1f
|
||||
bl asm_nmi_enter
|
||||
1:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
bl trace_hardirqs_off
|
||||
#endif
|
||||
|
||||
irq_handler
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
@@ -662,14 +676,22 @@ alternative_else_nop_endif
|
||||
bl preempt_schedule_irq // irq en/disable is done inside
|
||||
1:
|
||||
#endif
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
/*
|
||||
* if IRQs were disabled when we received the interrupt, we have an NMI
|
||||
* and we are not re-enabling interrupt upon eret. Skip tracing.
|
||||
*/
|
||||
cmp x20, #GIC_PRIO_IRQOFF
|
||||
b.ls 1f
|
||||
test_irqs_unmasked res=x0, pmr=x20
|
||||
cbz x0, 1f
|
||||
bl asm_nmi_exit
|
||||
1:
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
#ifdef CONFIG_ARM64_PSEUDO_NMI
|
||||
test_irqs_unmasked res=x0, pmr=x20
|
||||
cbnz x0, 1f
|
||||
#endif
|
||||
bl trace_hardirqs_on
|
||||
1:
|
||||
|
Reference in New Issue
Block a user