Implement NMI callbacks for GICv3 irqchip. Install NMI safe handlers
when setting up interrupt line as NMI.

Only SPIs and PPIs are allowed to be set up as NMI.

Signed-off-by: Julien Thierry <julien.thie...@arm.com>
Cc: Thomas Gleixner <t...@linutronix.de>
Cc: Jason Cooper <ja...@lakedaemon.net>
Cc: Marc Zyngier <marc.zyng...@arm.com>
---
 drivers/irqchip/irq-gic-v3.c | 73 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 1af2fcc..b1b255a 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -311,6 +311,70 @@ static int gic_irq_get_irqchip_state(struct irq_data *d,
        return 0;
 }
 
+static int gic_irq_set_irqchip_prio(struct irq_data *d, u8 prio)
+{
+       struct irq_desc *desc = irq_to_desc(d->irq);
+       /* Number of CPUs having PPI (idx + 16) setup as NMI */
+       static uint32_t nb_ppinmi_refs[16] = { 0 };
+
+       if (gic_peek_irq(d, GICD_ISENABLER)) {
+               pr_err("Cannot set NMI property of enabled IRQ %u\n", d->irq);
+               return -EINVAL;
+       }
+
+       /*
+        * A secondary irq_chip should be in charge of LPI request,
+        * it should not be possible to get there
+        */
+       if (WARN_ON(gic_irq(d) >= 8192))
+               return -EINVAL;
+
+       /* desc lock should already be held */
+       if (prio == GICD_INT_NMI_PRI) {
+               if (gic_irq(d) < 32) {
+                       /* Setting up NMI, only switch handler for first NMI */
+                       if (nb_ppinmi_refs[gic_irq(d) - 16] == 0)
+                               desc->handle_irq = 
handle_percpu_devid_fasteoi_nmi;
+
+                       nb_ppinmi_refs[gic_irq(d) - 16]++;
+               } else {
+                       desc->handle_irq = handle_fasteoi_nmi;
+               }
+       } else if (prio == GICD_INT_DEF_PRI) {
+               if (gic_irq(d) < 32) {
+                       /* Tearing down NMI, only switch handler for last NMI */
+                       if (nb_ppinmi_refs[gic_irq(d) - 16] == 1)
+                               desc->handle_irq = handle_percpu_devid_irq;
+
+                       nb_ppinmi_refs[gic_irq(d) - 16]--;
+               } else {
+                       desc->handle_irq = handle_fasteoi_irq;
+               }
+       } else {
+               return -EINVAL;
+       }
+
+       gic_set_irq_prio(gic_irq(d), gic_dist_base(d), prio);
+
+       return 0;
+}
+
+static int gic_irq_nmi_setup(struct irq_data *d)
+{
+       if (!gic_supports_nmi())
+               return -EINVAL;
+
+       return gic_irq_set_irqchip_prio(d, GICD_INT_NMI_PRI);
+}
+
+static void gic_irq_nmi_teardown(struct irq_data *d)
+{
+       if (WARN_ON(!gic_supports_nmi()))
+               return;
+
+       gic_irq_set_irqchip_prio(d, GICD_INT_DEF_PRI);
+}
+
 static void gic_eoi_irq(struct irq_data *d)
 {
        gic_write_eoir(gic_irq(d));
@@ -937,6 +1001,8 @@ static inline void gic_cpu_pm_init(void) { }
        .irq_set_affinity       = gic_set_affinity,
        .irq_get_irqchip_state  = gic_irq_get_irqchip_state,
        .irq_set_irqchip_state  = gic_irq_set_irqchip_state,
+       .irq_nmi_setup          = gic_irq_nmi_setup,
+       .irq_nmi_teardown       = gic_irq_nmi_teardown,
        .flags                  = IRQCHIP_SET_TYPE_MASKED |
                                  IRQCHIP_SKIP_SET_WAKE |
                                  IRQCHIP_MASK_ON_SUSPEND,
@@ -952,6 +1018,8 @@ static inline void gic_cpu_pm_init(void) { }
        .irq_get_irqchip_state  = gic_irq_get_irqchip_state,
        .irq_set_irqchip_state  = gic_irq_set_irqchip_state,
        .irq_set_vcpu_affinity  = gic_irq_set_vcpu_affinity,
+       .irq_nmi_setup          = gic_irq_nmi_setup,
+       .irq_nmi_teardown       = gic_irq_nmi_teardown,
        .flags                  = IRQCHIP_SET_TYPE_MASKED |
                                  IRQCHIP_SKIP_SET_WAKE |
                                  IRQCHIP_MASK_ON_SUSPEND,
@@ -1147,6 +1215,11 @@ static int partition_domain_translate(struct irq_domain 
*d,
 static void gic_enable_nmi_support(void)
 {
        static_branch_enable(&have_non_secure_prio_view);
+
+       if (static_branch_likely(&supports_deactivate_key))
+               gic_eoimode1_chip.flags |= IRQCHIP_SUPPORTS_NMI;
+       else
+               gic_chip.flags |= IRQCHIP_SUPPORTS_NMI;
 }
 
 static int __init gic_init_bases(void __iomem *dist_base,
-- 
1.9.1

Reply via email to