summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-06-20 09:38:14 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-06-20 09:38:14 -0700
commit9df7f15ee922429dcd6bcde68559ccb23f2dbd20 (patch)
tree3adadcc709a3f63864f7574aa7273b4b9706a6e0
parent8363e795eb79a74c857ba89e3b04668a33fe3344 (diff)
parenta13d0f8d117ca6b7885b51c4b21fe8d5a9eae714 (diff)
Merge tag 'irq_urgent_for_v5.13_rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
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
-rw-r--r--drivers/irqchip/irq-gic-v3.c36
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))