2 * Jailhouse, a Linux-based partitioning hypervisor
4 * Copyright (c) ARM Limited, 2014
7 * Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
13 #include <jailhouse/control.h>
14 #include <jailhouse/mmio.h>
15 #include <asm/gic_common.h>
16 #include <asm/irqchip.h>
17 #include <asm/platform.h>
18 #include <asm/setup.h>
20 static unsigned int gic_num_lr;
22 extern void *gicd_base;
23 extern unsigned int gicd_size;
25 unsigned int gicc_size;
28 unsigned int gich_size;
30 static int gic_init(void)
34 /* FIXME: parse device tree */
35 gicc_base = GICC_BASE;
36 gicc_size = GICC_SIZE;
37 gich_base = GICH_BASE;
38 gich_size = GICH_SIZE;
39 gicv_base = GICV_BASE;
41 err = arch_map_device(gicc_base, gicc_base, gicc_size);
45 err = arch_map_device(gich_base, gich_base, gich_size);
50 static int gic_cpu_reset(struct per_cpu *cpu_data, bool is_shutdown)
53 bool root_shutdown = is_shutdown && (cpu_data->cell == &root_cell);
56 u32 gicc_ctlr, gicc_pmr;
58 /* Clear list registers */
59 for (i = 0; i < gic_num_lr; i++)
62 /* Deactivate all PPIs */
63 active = mmio_read32(gicd_base + GICD_ISACTIVER);
64 for (i = 16; i < 32; i++) {
65 if (test_bit(i, (unsigned long *)&active))
66 mmio_write32(gicc_base + GICC_DIR, i);
69 /* Disable PPIs if necessary */
71 mmio_write32(gicd_base + GICD_ICENABLER, 0xffff0000);
72 /* Ensure IPIs are enabled */
73 mmio_write32(gicd_base + GICD_ISENABLER, 0x0000ffff);
75 mmio_write32(gich_base + GICH_APR, 0);
78 mmio_write32(gich_base + GICH_HCR, 0);
81 gich_vmcr = mmio_read32(gich_base + GICH_VMCR);
83 gicc_pmr = (gich_vmcr >> GICH_VMCR_PMR_SHIFT) << GICV_PMR_SHIFT;
85 if (gich_vmcr & GICH_VMCR_EN0)
86 gicc_ctlr |= GICC_CTLR_GRPEN1;
87 if (gich_vmcr & GICH_VMCR_EOImode)
88 gicc_ctlr |= GICC_CTLR_EOImode;
90 mmio_write32(gicc_base + GICC_CTLR, gicc_ctlr);
91 mmio_write32(gicc_base + GICC_PMR, gicc_pmr);
95 mmio_write32(gich_base + GICH_VMCR, gich_vmcr);
100 static int gic_cpu_init(struct per_cpu *cpu_data)
103 u32 cell_gicc_ctlr, cell_gicc_pmr;
105 /* Ensure all IPIs are enabled */
106 mmio_write32(gicd_base + GICD_ISENABLER, 0x0000ffff);
108 cell_gicc_ctlr = mmio_read32(gicc_base + GICC_CTLR);
109 cell_gicc_pmr = mmio_read32(gicc_base + GICC_PMR);
111 mmio_write32(gicc_base + GICC_CTLR,
112 GICC_CTLR_GRPEN1 | GICC_CTLR_EOImode);
113 mmio_write32(gicc_base + GICC_PMR, GICC_PMR_DEFAULT);
115 vtr = mmio_read32(gich_base + GICH_VTR);
116 gic_num_lr = (vtr & 0x3f) + 1;
118 /* VMCR only contains 5 bits of priority */
119 vmcr = (cell_gicc_pmr >> GICV_PMR_SHIFT) << GICH_VMCR_PMR_SHIFT;
121 * All virtual interrupts are group 0 in this driver since the GICV
122 * layout seen by the guest corresponds to GICC without security
124 * - A read from GICV_IAR doesn't acknowledge group 1 interrupts
125 * (GICV_AIAR does it, but the guest never attempts to accesses it)
126 * - A write to GICV_CTLR.GRP0EN corresponds to the GICC_CTLR.GRP1EN bit
127 * Since the guest's driver thinks that it is accessing a GIC with
128 * security extensions, a write to GPR1EN will enable group 0
130 * - Group 0 interrupts are presented as virtual IRQs (FIQEn = 0)
132 if (cell_gicc_ctlr & GICC_CTLR_GRPEN1)
133 vmcr |= GICH_VMCR_EN0;
134 if (cell_gicc_ctlr & GICC_CTLR_EOImode)
135 vmcr |= GICH_VMCR_EOImode;
137 mmio_write32(gich_base + GICH_VMCR, vmcr);
138 mmio_write32(gich_base + GICH_HCR, GICH_HCR_EN);
143 static void gic_eoi_irq(u32 irq_id, bool deactivate)
146 * The GIC doesn't seem to care about the CPUID value written to EOIR,
147 * which is rather convenient...
149 mmio_write32(gicc_base + GICC_EOIR, irq_id);
151 mmio_write32(gicc_base + GICC_DIR, irq_id);
154 static void gic_route_spis(struct cell *config_cell, struct cell *dest_cell)
158 static void gic_cell_init(struct cell *cell)
160 struct jailhouse_memory gicv_region;
163 * target_cpu_map has not been populated by all available CPUs when the
164 * setup code initialises the root cell. It is assumed that the kernel
165 * already has configured all its SPIs anyway, and that it will redirect
166 * them when unplugging a CPU.
168 if (cell != &root_cell)
169 gic_route_spis(cell, cell);
171 gicv_region.phys_start = (unsigned long)gicv_base;
173 * WARN: some SoCs (EXYNOS4) use a modified GIC which doesn't have any
174 * banked CPU interface, so we should map per-CPU physical addresses
176 * As for now, none of them seem to have virtualization extensions.
178 gicv_region.virt_start = (unsigned long)gicc_base;
179 gicv_region.size = gicc_size;
180 gicv_region.flags = JAILHOUSE_MEM_DMA | JAILHOUSE_MEM_READ
181 | JAILHOUSE_MEM_WRITE;
184 * Let the guest access the virtual CPU interface instead of the
187 arch_map_memory_region(cell, &gicv_region);
190 static void gic_cell_exit(struct cell *cell)
192 /* Reset interrupt routing of the cell's spis*/
193 gic_route_spis(cell, &root_cell);
196 static int gic_send_sgi(struct sgi *sgi)
200 if (!is_sgi(sgi->id))
203 val = (sgi->routing_mode & 0x3) << 24
204 | (sgi->targets & 0xff) << 16
207 mmio_write32(gicd_base + GICD_SGIR, val);
212 static int gic_inject_irq(struct per_cpu *cpu_data, struct pending_irq *irq)
219 elsr = mmio_read32(gich_base + GICH_ELSR0);
220 elsr |= (u64)mmio_read32(gich_base + GICH_ELSR1) << 32;
221 for (i = 0; i < gic_num_lr; i++) {
222 if (test_bit(i, (unsigned long *)&elsr)) {
223 /* Entry is available */
224 if (first_free == -1)
229 /* Check that there is no overlapping */
231 if ((lr & GICH_LR_VIRT_ID_MASK) == irq->virt_id)
235 if (first_free == -1) {
236 /* Enable maintenance IRQ */
238 hcr = mmio_read32(gich_base + GICH_HCR);
240 mmio_write32(gich_base + GICH_HCR, hcr);
245 /* Inject group 0 interrupt (seen as IRQ by the guest) */
247 lr |= GICH_LR_PENDING_BIT;
250 lr |= GICH_LR_HW_BIT;
251 lr |= irq->type.irq << GICH_LR_PHYS_ID_SHIFT;
253 lr |= irq->type.sgi.cpuid << GICH_LR_CPUID_SHIFT;
254 if (irq->type.sgi.maintenance)
255 lr |= GICH_LR_SGI_EOI_BIT;
258 gic_write_lr(first_free, lr);
263 static int gic_mmio_access(struct per_cpu *cpu_data,
264 struct mmio_access *access)
266 void *address = (void *)access->addr;
268 if (address >= gicd_base && address < gicd_base + gicd_size)
269 return gic_handle_dist_access(cpu_data, access);
271 return TRAP_UNHANDLED;
274 struct irqchip_ops gic_irqchip = {
276 .cpu_init = gic_cpu_init,
277 .cpu_reset = gic_cpu_reset,
278 .cell_init = gic_cell_init,
279 .cell_exit = gic_cell_exit,
281 .send_sgi = gic_send_sgi,
282 .handle_irq = gic_handle_irq,
283 .inject_irq = gic_inject_irq,
284 .eoi_irq = gic_eoi_irq,
285 .mmio_access = gic_mmio_access,