--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ * Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/control.h>
+#include <jailhouse/mmio.h>
+#include <asm/gic_common.h>
+#include <asm/irqchip.h>
+#include <asm/platform.h>
+#include <asm/setup.h>
+
+static unsigned int gic_num_lr;
+
+extern void *gicd_base;
+extern unsigned int gicd_size;
+void *gicc_base;
+unsigned int gicc_size;
+void *gicv_base;
+void *gich_base;
+unsigned int gich_size;
+
+static int gic_init(void)
+{
+ int err;
+
+ /* FIXME: parse device tree */
+ gicc_base = GICC_BASE;
+ gicc_size = GICC_SIZE;
+ gich_base = GICH_BASE;
+ gich_size = GICH_SIZE;
+ gicv_base = GICV_BASE;
+
+ err = arch_map_device(gicc_base, gicc_base, gicc_size);
+ if (err)
+ return err;
+
+ err = arch_map_device(gich_base, gich_base, gich_size);
+
+ return err;
+}
+
+static int gic_cpu_reset(struct per_cpu *cpu_data, bool is_shutdown)
+{
+ unsigned int i;
+ bool root_shutdown = is_shutdown && (cpu_data->cell == &root_cell);
+ u32 active;
+ u32 gich_vmcr = 0;
+ u32 gicc_ctlr, gicc_pmr;
+
+ /* Clear list registers */
+ for (i = 0; i < gic_num_lr; i++)
+ gic_write_lr(i, 0);
+
+ /* Deactivate all PPIs */
+ active = mmio_read32(gicd_base + GICD_ISACTIVER);
+ for (i = 16; i < 32; i++) {
+ if (test_bit(i, (unsigned long *)&active))
+ mmio_write32(gicc_base + GICC_DIR, i);
+ }
+
+ /* Disable PPIs if necessary */
+ if (!root_shutdown)
+ mmio_write32(gicd_base + GICD_ICENABLER, 0xffff0000);
+ /* Ensure IPIs are enabled */
+ mmio_write32(gicd_base + GICD_ISENABLER, 0x0000ffff);
+
+ mmio_write32(gich_base + GICH_APR, 0);
+
+ if (is_shutdown)
+ mmio_write32(gich_base + GICH_HCR, 0);
+
+ if (root_shutdown) {
+ gich_vmcr = mmio_read32(gich_base + GICH_VMCR);
+ gicc_ctlr = 0;
+ gicc_pmr = (gich_vmcr >> GICH_VMCR_PMR_SHIFT) << GICV_PMR_SHIFT;
+
+ if (gich_vmcr & GICH_VMCR_EN0)
+ gicc_ctlr |= GICC_CTLR_GRPEN1;
+ if (gich_vmcr & GICH_VMCR_EOImode)
+ gicc_ctlr |= GICC_CTLR_EOImode;
+
+ mmio_write32(gicc_base + GICC_CTLR, gicc_ctlr);
+ mmio_write32(gicc_base + GICC_PMR, gicc_pmr);
+
+ gich_vmcr = 0;
+ }
+ mmio_write32(gich_base + GICH_VMCR, gich_vmcr);
+
+ return 0;
+}
+
+static int gic_cpu_init(struct per_cpu *cpu_data)
+{
+ u32 vtr, vmcr;
+ u32 cell_gicc_ctlr, cell_gicc_pmr;
+
+ /* Ensure all IPIs are enabled */
+ mmio_write32(gicd_base + GICD_ISENABLER, 0x0000ffff);
+
+ cell_gicc_ctlr = mmio_read32(gicc_base + GICC_CTLR);
+ cell_gicc_pmr = mmio_read32(gicc_base + GICC_PMR);
+
+ mmio_write32(gicc_base + GICC_CTLR,
+ GICC_CTLR_GRPEN1 | GICC_CTLR_EOImode);
+ mmio_write32(gicc_base + GICC_PMR, GICC_PMR_DEFAULT);
+
+ vtr = mmio_read32(gich_base + GICH_VTR);
+ gic_num_lr = (vtr & 0x3f) + 1;
+
+ /* VMCR only contains 5 bits of priority */
+ vmcr = (cell_gicc_pmr >> GICV_PMR_SHIFT) << GICH_VMCR_PMR_SHIFT;
+ /*
+ * All virtual interrupts are group 0 in this driver since the GICV
+ * layout seen by the guest corresponds to GICC without security
+ * extensions:
+ * - A read from GICV_IAR doesn't acknowledge group 1 interrupts
+ * (GICV_AIAR does it, but the guest never attempts to accesses it)
+ * - A write to GICV_CTLR.GRP0EN corresponds to the GICC_CTLR.GRP1EN bit
+ * Since the guest's driver thinks that it is accessing a GIC with
+ * security extensions, a write to GPR1EN will enable group 0
+ * interrups.
+ * - Group 0 interrupts are presented as virtual IRQs (FIQEn = 0)
+ */
+ if (cell_gicc_ctlr & GICC_CTLR_GRPEN1)
+ vmcr |= GICH_VMCR_EN0;
+ if (cell_gicc_ctlr & GICC_CTLR_EOImode)
+ vmcr |= GICH_VMCR_EOImode;
+
+ mmio_write32(gich_base + GICH_VMCR, vmcr);
+ mmio_write32(gich_base + GICH_HCR, GICH_HCR_EN);
+
+ return 0;
+}
+
+static void gic_eoi_irq(u32 irq_id, bool deactivate)
+{
+ /*
+ * The GIC doesn't seem to care about the CPUID value written to EOIR,
+ * which is rather convenient...
+ */
+ mmio_write32(gicc_base + GICC_EOIR, irq_id);
+ if (deactivate)
+ mmio_write32(gicc_base + GICC_DIR, irq_id);
+}
+
+static void gic_route_spis(struct cell *config_cell, struct cell *dest_cell)
+{
+}
+
+static void gic_cell_init(struct cell *cell)
+{
+ struct jailhouse_memory gicv_region;
+
+ /*
+ * target_cpu_map has not been populated by all available CPUs when the
+ * setup code initialises the root cell. It is assumed that the kernel
+ * already has configured all its SPIs anyway, and that it will redirect
+ * them when unplugging a CPU.
+ */
+ if (cell != &root_cell)
+ gic_route_spis(cell, cell);
+
+ gicv_region.phys_start = (unsigned long)gicv_base;
+ /*
+ * WARN: some SoCs (EXYNOS4) use a modified GIC which doesn't have any
+ * banked CPU interface, so we should map per-CPU physical addresses
+ * here.
+ * As for now, none of them seem to have virtualization extensions.
+ */
+ gicv_region.virt_start = (unsigned long)gicc_base;
+ gicv_region.size = gicc_size;
+ gicv_region.flags = JAILHOUSE_MEM_DMA | JAILHOUSE_MEM_READ
+ | JAILHOUSE_MEM_WRITE;
+
+ /*
+ * Let the guest access the virtual CPU interface instead of the
+ * physical one
+ */
+ arch_map_memory_region(cell, &gicv_region);
+}
+
+static void gic_cell_exit(struct cell *cell)
+{
+ /* Reset interrupt routing of the cell's spis*/
+ gic_route_spis(cell, &root_cell);
+}
+
+static int gic_send_sgi(struct sgi *sgi)
+{
+ u32 val;
+
+ if (!is_sgi(sgi->id))
+ return -EINVAL;
+
+ val = (sgi->routing_mode & 0x3) << 24
+ | (sgi->targets & 0xff) << 16
+ | (sgi->id & 0xf);
+
+ mmio_write32(gicd_base + GICD_SGIR, val);
+
+ return 0;
+}
+
+static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq)
+{
+ int i;
+ int first_free = -1;
+ u32 lr;
+ u64 elsr;
+
+ elsr = mmio_read32(gich_base + GICH_ELSR0);
+ elsr |= (u64)mmio_read32(gich_base + GICH_ELSR1) << 32;
+ for (i = 0; i < gic_num_lr; i++) {
+ if (test_bit(i, (unsigned long *)&elsr)) {
+ /* Entry is available */
+ if (first_free == -1)
+ first_free = i;
+ continue;
+ }
+
+ /* Check that there is no overlapping */
+ lr = gic_read_lr(i);
+ if ((lr & GICH_LR_VIRT_ID_MASK) == irq->virt_id)
+ return -EINVAL;
+ }
+
+ if (first_free == -1) {
+ /* Enable maintenance IRQ */
+ u32 hcr;
+ hcr = mmio_read32(gich_base + GICH_HCR);
+ hcr |= GICH_HCR_UIE;
+ mmio_write32(gich_base + GICH_HCR, hcr);
+
+ return -EBUSY;
+ }
+
+ /* Inject group 0 interrupt (seen as IRQ by the guest) */
+ lr = irq->virt_id;
+ lr |= GICH_LR_PENDING_BIT;
+
+ if (irq->hw) {
+ lr |= GICH_LR_HW_BIT;
+ lr |= irq->type.irq << GICH_LR_PHYS_ID_SHIFT;
+ } else {
+ lr |= irq->type.sgi.cpuid << GICH_LR_CPUID_SHIFT;
+ if (irq->type.sgi.maintenance)
+ lr |= GICH_LR_SGI_EOI_BIT;
+ }
+
+ gic_write_lr(first_free, lr);
+
+ return 0;
+}
+
+static int gic_mmio_access(struct per_cpu *cpu_data,
+ struct mmio_access *access)
+{
+ void *address = (void *)access->addr;
+
+ if (address >= gicd_base && address < gicd_base + gicd_size)
+ return gic_handle_dist_access(cpu_data, access);
+
+ return TRAP_UNHANDLED;
+}
+
+struct irqchip_ops gic_irqchip = {
+ .init = gic_init,
+ .cpu_init = gic_cpu_init,
+ .cpu_reset = gic_cpu_reset,
+ .cell_init = gic_cell_init,
+ .cell_exit = gic_cell_exit,
+
+ .send_sgi = gic_send_sgi,
+ .handle_irq = gic_handle_irq,
+ .inject_irq = gic_inject_irq,
+ .eoi_irq = gic_eoi_irq,
+ .mmio_access = gic_mmio_access,
+};
--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ * Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef _JAILHOUSE_ASM_GIC_V2_H
+#define _JAILHOUSE_ASM_GIC_V2_H
+
+#define GICD_CIDR0 0xff0
+#define GICD_CIDR1 0xff4
+#define GICD_CIDR2 0xff8
+#define GICD_CIDR3 0xffc
+
+#define GICD_PIDR0 0xfe0
+#define GICD_PIDR1 0xfe4
+#define GICD_PIDR2 0xfe8
+#define GICD_PIDR3 0xfec
+#define GICD_PIDR4 0xfd0
+#define GICD_PIDR5 0xfd4
+#define GICD_PIDR6 0xfd8
+#define GICD_PIDR7 0xfdc
+
+#define GICC_CTLR 0x0000
+#define GICC_PMR 0x0004
+#define GICC_BPR 0x0008
+#define GICC_IAR 0x000c
+#define GICC_EOIR 0x0010
+#define GICC_RPR 0x0014
+#define GICC_HPPIR 0x0018
+#define GICC_ABPR 0x001c
+#define GICC_AIAR 0x0020
+#define GICC_AEOIR 0x0024
+#define GICC_AHPPIR 0x0028
+#define GICC_APR0 0x00d0
+#define GICC_APR1 0x00d4
+#define GICC_APR2 0x00d8
+#define GICC_APR3 0x00dc
+#define GICC_NSAPR0 0x00e0
+#define GICC_NSAPR1 0x00e4
+#define GICC_NSAPR2 0x00e8
+#define GICC_NSAPR3 0x00ec
+#define GICC_IIDR 0x00fc
+#define GICC_DIR 0x1000
+
+#define GICC_CTLR_GRPEN1 (1 << 0)
+#define GICC_CTLR_EOImode (1 << 9)
+
+#define GICC_PMR_DEFAULT 0xf0
+
+#define GICH_HCR 0x000
+#define GICH_VTR 0x004
+#define GICH_VMCR 0x008
+#define GICH_MISR 0x010
+#define GICH_EISR0 0x020
+#define GICH_EISR1 0x024
+#define GICH_ELSR0 0x030
+#define GICH_ELSR1 0x034
+#define GICH_APR 0x0f0
+#define GICH_LR_BASE 0x100
+
+#define GICV_PMR_SHIFT 3
+#define GICH_VMCR_PMR_SHIFT 27
+#define GICH_VMCR_EN0 (1 << 0)
+#define GICH_VMCR_EN1 (1 << 1)
+#define GICH_VMCR_ACKCtl (1 << 2)
+#define GICH_VMCR_EOImode (1 << 9)
+
+#define GICH_HCR_EN (1 << 0)
+#define GICH_HCR_UIE (1 << 1)
+#define GICH_HCR_LRENPIE (1 << 2)
+#define GICH_HCR_NPIE (1 << 3)
+#define GICH_HCR_VGRP0EIE (1 << 4)
+#define GICH_HCR_VGRP0DIE (1 << 5)
+#define GICH_HCR_VGRP1EIE (1 << 6)
+#define GICH_HCR_VGRP1DIE (1 << 7)
+#define GICH_HCR_EOICOUNT_SHIFT 27
+
+#define GICH_LR_HW_BIT (1 << 31)
+#define GICH_LR_GRP1_BIT (1 << 30)
+#define GICH_LR_ACTIVE_BIT (1 << 29)
+#define GICH_LR_PENDING_BIT (1 << 28)
+#define GICH_LR_PRIORITY_SHIFT 23
+#define GICH_LR_SGI_EOI_BIT (1 << 19)
+#define GICH_LR_CPUID_SHIFT 10
+#define GICH_LR_PHYS_ID_SHIFT 10
+#define GICH_LR_VIRT_ID_MASK 0x3ff
+
+#ifndef __ASSEMBLY__
+
+#include <jailhouse/mmio.h>
+
+static inline u32 gic_read_lr(unsigned int i)
+{
+ extern void *gich_base;
+
+ return mmio_read32(gich_base + GICH_LR_BASE + i * 4);
+}
+
+static inline void gic_write_lr(unsigned int i, u32 value)
+{
+ extern void *gich_base;
+
+ mmio_write32(gich_base + GICH_LR_BASE + i * 4, value);
+}
+
+static inline u32 gic_read_iar(void)
+{
+ extern void *gicc_base;
+
+ return mmio_read32(gicc_base + GICC_IAR) & 0x3ff;
+}
+
+#endif /* !__ASSEMBLY__ */
+#endif /* _JAILHOUSE_ASM_GIC_V2_H */