Merge tag 'irqchip-fixes-5.13-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorThomas Gleixner <tglx@linutronix.de>
Thu, 17 Jun 2021 13:22:31 +0000 (15:22 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Thu, 17 Jun 2021 13:22:31 +0000 (15:22 +0200)
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
drivers/irqchip/irq-gic-v3.c

index 37a23aa..66d623f 100644 (file)
@@ -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))