projects/linux·commit 9df7f15e

Merge tag 'irq_urgent_for_v5.13_rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

9df7f15e · Linus Torvalds <[email protected]> · 2021-06-20 09:38:14 -07:00

Pull irq fix from Borislav Petkov:
 "A single fix for GICv3 to not take an interrupt in an NMI context"

* tag 'irq_urgent_for_v5.13_rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  irqchip/gic-v3: Workaround inconsistent PMR setting on NMI entry
Commit
9df7f15ee922429dcd6bcde68559ccb23f2dbd20
Parents
8363e795 · a13d0f8d

Merge commit. Changes below are compared with the first parent: 8363e795.

Changes

1 files changed · +35 −1

FileStatusAddDelContent
drivers/irqchip/irq-gic-v3.cmodified+35−1View file

drivers/irqchip/irq-gic-v3.c

View file
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 37a23aa..66d623f 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))