diff mbox

[2/5] hw/intc/arm_gic_common: Provide property to make IRQs reset as NonSecure

Message ID 1435669649-3035-3-git-send-email-peter.maydell@linaro.org
State Superseded
Headers show

Commit Message

Peter Maydell June 30, 2015, 1:07 p.m. UTC
Implement a property which causes the GIC to reset with interrupts
configured into group 1 (NonSecure) rather than the usual group 0,
and with their initial priority set to the highest NonSecure priority
rather than the usual highest Secure priority.

This is necessary for the situation where we directly boot a kernel
in NonSecure on a system where the GIC supports the security extensions:
otherwise the kernel will be unable to access any of its interrupts.

The property is writable after realize, because we won't know whether
we need to set it until after machine creation is complete. If the
setting is changed it will take effect at the next reset.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gic_common.c         | 55 +++++++++++++++++++++++++++++++++++++---
 include/hw/intc/arm_gic_common.h |  1 +
 2 files changed, 53 insertions(+), 3 deletions(-)
diff mbox

Patch

diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index a64d071..bc8b9c9 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -84,6 +84,33 @@  static const VMStateDescription vmstate_gic = {
     }
 };
 
+static bool arm_gic_irq_reset_nonsecure_get(Object *obj, Error **errp)
+{
+    GICState *s = ARM_GIC_COMMON(obj);
+
+    return s->irq_reset_nonsecure;
+}
+
+static void arm_gic_irq_reset_nonsecure_set(Object *obj, bool val, Error **errp)
+{
+    GICState *s = ARM_GIC_COMMON(obj);
+
+    s->irq_reset_nonsecure = val;
+}
+
+static void arm_gic_common_init(Object *obj)
+{
+    /* This property should be set true if the GIC should configure
+     * IRQs as group 1 (NS) on reset, rather than the usual group 0.
+     * It is essentially a standard bool property, except that it can be
+     * set after realize (and will take effect at the next reset).
+     */
+    object_property_add_bool(obj, "irqs-reset-nonsecure",
+                             arm_gic_irq_reset_nonsecure_get,
+                             arm_gic_irq_reset_nonsecure_set,
+                             &error_abort);
+}
+
 static void arm_gic_common_realize(DeviceState *dev, Error **errp)
 {
     GICState *s = ARM_GIC_COMMON(dev);
@@ -124,12 +151,27 @@  static void arm_gic_common_reset(DeviceState *dev)
 {
     GICState *s = ARM_GIC_COMMON(dev);
     int i, j;
+    int resetprio;
+
+    /* If we're resetting a TZ-aware GIC as if secure firmware
+     * had set it up ready to start a kernel in non-secure,
+     * we need to set interrupt priorities to a "zero for the
+     * NS view" value. This is particularly critical for the
+     * priority_mask[] values, because if they are zero then NS
+     * code cannot ever rewrite the priority to anything else.
+     */
+    if (s->security_extn && s->irq_reset_nonsecure) {
+        resetprio = 0x80;
+    } else {
+        resetprio = 0;
+    }
+
     memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state));
     for (i = 0 ; i < s->num_cpu; i++) {
         if (s->revision == REV_11MPCORE) {
             s->priority_mask[i] = 0xf0;
         } else {
-            s->priority_mask[i] = 0;
+            s->priority_mask[i] = resetprio;
         }
         s->current_pending[i] = 1023;
         s->running_irq[i] = 1023;
@@ -138,7 +180,7 @@  static void arm_gic_common_reset(DeviceState *dev)
         s->bpr[i] = GIC_MIN_BPR;
         s->abpr[i] = GIC_MIN_ABPR;
         for (j = 0; j < GIC_INTERNAL; j++) {
-            s->priority1[j][i] = 0;
+            s->priority1[j][i] = resetprio;
         }
         for (j = 0; j < GIC_NR_SGIS; j++) {
             s->sgi_pending[j][i] = 0;
@@ -150,7 +192,7 @@  static void arm_gic_common_reset(DeviceState *dev)
     }
 
     for (i = 0; i < ARRAY_SIZE(s->priority2); i++) {
-        s->priority2[i] = 0;
+        s->priority2[i] = resetprio;
     }
 
     for (i = 0; i < GIC_MAXIRQ; i++) {
@@ -161,6 +203,12 @@  static void arm_gic_common_reset(DeviceState *dev)
             s->irq_target[i] = 0;
         }
     }
+    if (s->security_extn && s->irq_reset_nonsecure) {
+        for (i = 0; i < GIC_MAXIRQ; i++) {
+            GIC_SET_GROUP(i, ALL_CPU_MASK);
+        }
+    }
+
     s->ctlr = 0;
 }
 
@@ -191,6 +239,7 @@  static const TypeInfo arm_gic_common_type = {
     .name = TYPE_ARM_GIC_COMMON,
     .parent = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(GICState),
+    .instance_init = arm_gic_common_init,
     .class_size = sizeof(ARMGICCommonClass),
     .class_init = arm_gic_common_class_init,
     .abstract = true,
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 899db3d..cfc1cce 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -118,6 +118,7 @@  typedef struct GICState {
     uint32_t num_irq;
     uint32_t revision;
     bool security_extn;
+    bool irq_reset_nonsecure; /* configure IRQs as group 1 (NS) on reset? */
     int dev_fd; /* kvm device fd if backed by kvm vgic support */
 } GICState;