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>
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));
*/
static unsigned int gic_num_lr;
+static unsigned int gic_num_priority_bits;
static void *gicr_base;
static unsigned int gicr_size;
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;
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)
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;
* 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);
}
}
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,
};
#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)
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);
};
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);
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;
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);
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;