]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
arm: GIC: reset the CPU interface before running a new guest
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>
Wed, 9 Jul 2014 17:27:57 +0000 (18:27 +0100)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 19 Dec 2014 10:04:07 +0000 (11:04 +0100)
All pending interrupts need to be cleared before running a new guest.
This patch resets the list registers, the software pending queue, and
the GIC hypervisor config registers.
Since the suspend loop was entered through the IRQ handler, we also need
to deactivate that active IPI.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
[Jan: switch to mmio accessors]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/arm/control.c
hypervisor/arch/arm/gic-v3.c
hypervisor/arch/arm/include/asm/gic_v3.h
hypervisor/arch/arm/include/asm/irqchip.h
hypervisor/arch/arm/irqchip.c

index 7708411eb15099f1537582e91991b7e7e1e7410c..29f20702b5b27387571a66f1419c303c47d120c8 100644 (file)
@@ -26,6 +26,16 @@ static void arch_reset_self(struct per_cpu *cpu_data)
        if (err)
                printk("MMU setup failed\n");
 
+       /*
+        * We come from the IRQ handler, but we won't return there, so the IPI
+        * is deactivated here.
+        */
+       irqchip_eoi_irq(SGI_CPU_OFF, true);
+
+       err = irqchip_cpu_reset(cpu_data);
+       if (err)
+               printk("IRQ setup failed\n");
+
        arm_write_banked_reg(ELR_hyp, 0);
        arm_write_banked_reg(SPSR_hyp, RESET_PSR);
        memset(regs, 0, sizeof(struct registers));
index f45a992f9d5d2d15f5d90f7bebff3f740789426d..bb6ced3adf99d078fa09a5038088f76e3b3cf393 100644 (file)
@@ -30,6 +30,7 @@
  */
 
 static unsigned int gic_num_lr;
+static unsigned int gic_num_priority_bits;
 
 static void *gicr_base;
 static unsigned int gicr_size;
@@ -48,6 +49,47 @@ static int gic_init(void)
        return err;
 }
 
+static int gic_cpu_reset(struct per_cpu *cpu_data)
+{
+       unsigned int i;
+       void *gicr = cpu_data->gicr_base;
+       unsigned long active;
+
+       if (gicr == 0)
+               return -ENODEV;
+
+       /* Clear list registers */
+       for (i = 0; i < gic_num_lr; i++)
+               gic_write_lr(i, 0);
+
+       gicr += GICR_SGI_BASE;
+       active = mmio_read32(gicr + GICR_ICACTIVER);
+       /* Deactivate all active PPIs */
+       for (i = 16; i < 32; i++) {
+               if (test_bit(i, &active))
+                       arm_write_sysreg(ICC_DIR_EL1, i);
+       }
+
+       /* Disable all PPIs, ensure IPIs are enabled */
+       mmio_write32(gicr + GICR_ICENABLER, 0xffff0000);
+       mmio_write32(gicr + GICR_ISENABLER, 0x0000ffff);
+
+       /* Clear active priority bits */
+       if (gic_num_priority_bits >= 5)
+               arm_write_sysreg(ICH_AP1R0_EL2, 0);
+       if (gic_num_priority_bits >= 6)
+               arm_write_sysreg(ICH_AP1R1_EL2, 0);
+       if (gic_num_priority_bits > 6) {
+               arm_write_sysreg(ICH_AP1R2_EL2, 0);
+               arm_write_sysreg(ICH_AP1R3_EL2, 0);
+       }
+
+       arm_write_sysreg(ICH_VMCR_EL2, 0);
+       arm_write_sysreg(ICH_HCR_EL2, ICH_HCR_EN);
+
+       return 0;
+}
+
 static int gic_cpu_init(struct per_cpu *cpu_data)
 {
        u64 typer;
@@ -104,6 +146,7 @@ static int gic_cpu_init(struct per_cpu *cpu_data)
 
        arm_read_sysreg(ICH_VTR_EL2, ich_vtr);
        gic_num_lr = (ich_vtr & 0xf) + 1;
+       gic_num_priority_bits = (ich_vtr >> 29) + 1;
 
        ich_vmcr = (cell_icc_pmr & ICC_PMR_MASK) << ICH_VMCR_VPMR_SHIFT;
        if (cell_icc_igrpen1 & ICC_IGRPEN1_EN)
@@ -200,6 +243,13 @@ static bool arch_handle_phys_irq(struct per_cpu *cpu_data, u32 irqn)
        return false;
 }
 
+static void gic_eoi_irq(u32 irq_id, bool deactivate)
+{
+       arm_write_sysreg(ICC_EOIR1_EL1, irq_id);
+       if (deactivate)
+               arm_write_sysreg(ICC_DIR_EL1, irq_id);
+}
+
 static void gic_handle_irq(struct per_cpu *cpu_data)
 {
        bool handled = false;
@@ -226,10 +276,7 @@ static void gic_handle_irq(struct per_cpu *cpu_data)
                 * This allows to not be re-interrupted by a level-triggered
                 * interrupt that needs handling in the guest (e.g. timer)
                 */
-               arm_write_sysreg(ICC_EOIR1_EL1, irq_id);
-               /* Deactivate if necessary */
-               if (handled)
-                       arm_write_sysreg(ICC_DIR_EL1, irq_id);
+               gic_eoi_irq(irq_id, handled);
        }
 }
 
@@ -295,7 +342,9 @@ static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq)
 struct irqchip_ops gic_irqchip = {
        .init = gic_init,
        .cpu_init = gic_cpu_init,
+       .cpu_reset = gic_cpu_reset,
        .send_sgi = gic_send_sgi,
        .handle_irq = gic_handle_irq,
        .inject_irq = gic_inject_irq,
+       .eoi_irq = gic_eoi_irq,
 };
index 5dc83e6302b73ff9e1c4c5a538c8b332bd081356..5197c978378151fe596c0254b2123be5176048aa 100644 (file)
 #define ICC_SRE_EL2            SYSREG_32(4, c12, c9, 5)
 #define ICC_IGRPEN1_EL1                SYSREG_32(0, c12, c12, 7)
 #define ICC_SGI1R_EL1          SYSREG_64(0, c12)
+#define ICC_AP1R0_EL1          SYSREG_32(0, c12, c9, 0)
+#define ICC_AP1R1_EL1          SYSREG_32(0, c12, c9, 1)
+#define ICC_AP1R2_EL1          SYSREG_32(0, c12, c9, 2)
+#define ICC_AP1R3_EL1          SYSREG_32(0, c12, c9, 3)
 
 #define ICH_HCR_EL2            SYSREG_32(4, c12, c11, 0)
 #define ICH_VTR_EL2            SYSREG_32(4, c12, c11, 1)
 #define ICH_EISR_EL2           SYSREG_32(4, c12, c11, 3)
 #define ICH_ELSR_EL2           SYSREG_32(4, c12, c11, 5)
 #define ICH_VMCR_EL2           SYSREG_32(4, c12, c11, 7)
+#define ICH_AP1R0_EL2          SYSREG_32(4, c12, c9, 0)
+#define ICH_AP1R1_EL2          SYSREG_32(4, c12, c9, 1)
+#define ICH_AP1R2_EL2          SYSREG_32(4, c12, c9, 2)
+#define ICH_AP1R3_EL2          SYSREG_32(4, c12, c9, 3)
 
 /* Different on AArch32 and AArch64... */
 #define __ICH_LR0(x)           SYSREG_32(4, c12, c12, x)
index a6b05e4f4e42ab20b6f20e7d9cfba3ef9d9ee6cf..bdb7b99250c698aea724d1f4f4451d29c245b83d 100644 (file)
@@ -45,9 +45,11 @@ struct sgi {
 struct irqchip_ops {
        int     (*init)(void);
        int     (*cpu_init)(struct per_cpu *cpu_data);
+       int     (*cpu_reset)(struct per_cpu *cpu_data);
 
        int     (*send_sgi)(struct sgi *sgi);
        void    (*handle_irq)(struct per_cpu *cpu_data);
+       void    (*eoi_irq)(u32 irqn, bool deactivate);
        int     (*inject_irq)(struct per_cpu *cpu_data, struct pending_irq *irq);
 };
 
@@ -74,9 +76,11 @@ struct pending_irq {
 
 int irqchip_init(void);
 int irqchip_cpu_init(struct per_cpu *cpu_data);
+int irqchip_cpu_reset(struct per_cpu *cpu_data);
 
 int irqchip_send_sgi(struct sgi *sgi);
 void irqchip_handle_irq(struct per_cpu *cpu_data);
+void irqchip_eoi_irq(u32 irqn, bool deactivate);
 
 int irqchip_inject_pending(struct per_cpu *cpu_data);
 int irqchip_insert_pending(struct per_cpu *cpu_data, struct pending_irq *irq);
index 4432b2f66ced9bb95539a973dc8220a392ad8c33..ea8e46b21852d0c46ba742245c7a80e2ccb86624 100644 (file)
@@ -36,10 +36,16 @@ static struct irqchip_ops irqchip;
 
 static int irqchip_init_pending(struct per_cpu *cpu_data)
 {
-       struct pending_irq *pend_array = page_alloc(&mem_pool, 1);
+       struct pending_irq *pend_array;
+
+       if (cpu_data->pending_irqs == NULL) {
+               cpu_data->pending_irqs = pend_array = page_alloc(&mem_pool, 1);
+               if (pend_array == NULL)
+                       return -ENOMEM;
+       } else {
+               pend_array = cpu_data->pending_irqs;
+       }
 
-       if (pend_array == NULL)
-               return -ENOMEM;
        memset(pend_array, 0, PAGE_SIZE);
 
        cpu_data->pending_irqs = pend_array;
@@ -182,6 +188,11 @@ void irqchip_handle_irq(struct per_cpu *cpu_data)
        irqchip.handle_irq(cpu_data);
 }
 
+void irqchip_eoi_irq(u32 irqn, bool deactivate)
+{
+       irqchip.eoi_irq(irqn, deactivate);
+}
+
 int irqchip_send_sgi(struct sgi *sgi)
 {
        return irqchip.send_sgi(sgi);
@@ -201,6 +212,20 @@ int irqchip_cpu_init(struct per_cpu *cpu_data)
        return 0;
 }
 
+int irqchip_cpu_reset(struct per_cpu *cpu_data)
+{
+       int err;
+
+       err = irqchip_init_pending(cpu_data);
+       if (err)
+               return err;
+
+       if (irqchip.cpu_reset)
+               return irqchip.cpu_reset(cpu_data);
+
+       return 0;
+}
+
 /* Only the GIC is implemented */
 extern struct irqchip_ops gic_irqchip;