diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2021-06-17 15:22:31 +0200 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2021-06-17 15:22:31 +0200 |
commit | a13d0f8d117ca6b7885b51c4b21fe8d5a9eae714 (patch) | |
tree | a6db3c1726cc0f5e22c898b70b7354baa2fbffd3 | |
parent | 009c9aa5be652675a06d5211e1640e02bbb1c33d (diff) | |
parent | 382e6e177bc1c02473e56591fe5083ae1e4904f6 (diff) |
Merge tag 'irqchip-fixes-5.13-2' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms into irq/urgent
Pull irqchip fixes from Marc Zyngier:
- Fix GICv3 NMI handling where an IRQ could be mistakenly handled
as a NMI, with disatrous effects
Link: https://lore.kernel.org/r/20210610171127.2404752-1-maz@kernel.org
-rw-r--r-- | drivers/irqchip/irq-gic-v3.c | 36 |
1 files changed, 35 insertions, 1 deletions
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 37a23aa6de37..66d623f91678 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -642,11 +642,45 @@ static inline void gic_handle_nmi(u32 irqnr, struct pt_regs *regs) nmi_exit(); } +static u32 do_read_iar(struct pt_regs *regs) +{ + u32 iar; + + if (gic_supports_nmi() && unlikely(!interrupts_enabled(regs))) { + u64 pmr; + + /* + * We were in a context with IRQs disabled. However, the + * entry code has set PMR to a value that allows any + * interrupt to be acknowledged, and not just NMIs. This can + * lead to surprising effects if the NMI has been retired in + * the meantime, and that there is an IRQ pending. The IRQ + * would then be taken in NMI context, something that nobody + * wants to debug twice. + * + * Until we sort this, drop PMR again to a level that will + * actually only allow NMIs before reading IAR, and then + * restore it to what it was. + */ + pmr = gic_read_pmr(); + gic_pmr_mask_irqs(); + isb(); + + iar = gic_read_iar(); + + gic_write_pmr(pmr); + } else { + iar = gic_read_iar(); + } + + return iar; +} + static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqnr; - irqnr = gic_read_iar(); + irqnr = do_read_iar(regs); /* Check for special IDs first */ if ((irqnr >= 1020 && irqnr <= 1023)) |