Set group status of PPIs and SGIs correctly on GICv3 systems
authorAchin Gupta <achin.gupta@arm.com>
Mon, 9 Mar 2015 21:54:40 +0000 (21:54 +0000)
committerAchin Gupta <achin.gupta@arm.com>
Fri, 20 Mar 2015 08:52:13 +0000 (08:52 +0000)
On a GICv2 system, the group status of PPIs and SGIs is set in the GICD_IGROUPR0
register. On a GICv3 system, if affinity routing is enabled for the non-secure
state, then the group status of PPIs and SGIs should be set in the GICR_IGROUPR0
register. ARM Trusted firmware sets the group status using the GICv2
sequence. On a GICv3 system, if the group status of an interrupt is set to Group
1 through a write to the GICD_IGROUPR0, then the GICR_IGROUPR0 is updated as
well.

The current sequence is incorrect since it first marks all PPIs and SGIs as
Group 1. It then clears the bits in GICD_IGROUPR0 corresponding to secure
interrupts to set their group status to Group 0. This operation is a no-op. It
leaves the secure generic timer interrupt (#29) used by the TSP marked as Group
1. This causes the interrupt to interfere with the execution of non-secure
software. Once an interrupt has been marked as Group 1, the GICR_IGROUPR0 should
be programmed to change its group status.

This patch rectifies this issue by setting the group status of only the
non-secure PPI and SGIs to Group 1 in the first place. GICD_IGROUPR0 resets to
0. So secure interrupts are marked as Group 0 by default.

Change-Id: I958b4b15f3e2b2444ce4c17764def36216498d00

drivers/arm/gic/arm_gic.c

index 58fbc89a7f3c5446bdaa05a711a9e2673654b2d0..2888e719f633baf9cb83122a721005ae74bfcca2 100644 (file)
@@ -219,13 +219,10 @@ void arm_gic_cpuif_deactivate(void)
  ******************************************************************************/
 void arm_gic_pcpu_distif_setup(void)
 {
-       unsigned int index, irq_num;
+       unsigned int index, irq_num, sec_ppi_sgi_mask;
 
        assert(g_gicd_base);
 
-       /* Mark all 32 SGI+PPI interrupts as Group 1 (non-secure) */
-       gicd_write_igroupr(g_gicd_base, 0, ~0);
-
        /* Setup PPI priorities doing four at a time */
        for (index = 0; index < 32; index += 4) {
                gicd_write_ipriorityr(g_gicd_base, index,
@@ -233,16 +230,25 @@ void arm_gic_pcpu_distif_setup(void)
        }
 
        assert(g_irq_sec_ptr);
+       sec_ppi_sgi_mask = 0;
        for (index = 0; index < g_num_irqs; index++) {
                irq_num = g_irq_sec_ptr[index];
                if (irq_num < MIN_SPI_ID) {
-                       /* We have an SGI or a PPI */
-                       gicd_clr_igroupr(g_gicd_base, irq_num);
+                       /* We have an SGI or a PPI. They are Group0 at reset */
+                       sec_ppi_sgi_mask |= 1U << irq_num;
                        gicd_set_ipriorityr(g_gicd_base, irq_num,
                                GIC_HIGHEST_SEC_PRIORITY);
                        gicd_set_isenabler(g_gicd_base, irq_num);
                }
        }
+
+       /*
+        * Invert the bitmask to create a mask for non-secure PPIs and
+        * SGIs. Program the GICD_IGROUPR0 with this bit mask. This write will
+        * update the GICR_IGROUPR0 as well in case we are running on a GICv3
+        * system. This is critical if GICD_CTLR.ARE_NS=1.
+        */
+       gicd_write_igroupr(g_gicd_base, 0, ~sec_ppi_sgi_mask);
 }
 
 /*******************************************************************************