]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
arm: GICv3: filter the guests' SGIs
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>
Mon, 23 Jun 2014 13:33:53 +0000 (14:33 +0100)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 19 Dec 2014 10:04:07 +0000 (11:04 +0100)
In GICv3, IPIs are sent by writing the system register `ICC_SGIR'.
This patch moderates those writes by injecting the IPIs into the
appropriate cells, and issues an hypervisor IPI to let the cell's CPUs
fill their list registers.

Since there shouldn't be many cases where Jailhouse needs to emulate
system register accesses, this patch keeps it simple, by calling directly
the GICv3 function from the trap handler, without abstracting it through
irqchip.
However, this change adds an ungraceful ifdef, since the GICv2 and v3
headers are mutually exclusive for the moment.
In GICv2, the SGIR register is 32bit and will be handled directly in the
gic-common.c code, using an MMIO trap of the distributor accesses.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
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/control.h
hypervisor/arch/arm/include/asm/gic_v3.h
hypervisor/arch/arm/include/asm/traps.h
hypervisor/arch/arm/traps.c

index 97345ad57dbbbe0b5a7c358bcf2bd89bc995784a..7c76d72fb3b14cbc4e95a36c638a1669b7fdb1eb 100644 (file)
 
 void arch_handle_sgi(struct per_cpu *cpu_data, u32 irqn)
 {
-
+       switch (irqn) {
+       case SGI_INJECT:
+               irqchip_inject_pending(cpu_data);
+               break;
+       }
 }
 
 void arch_handle_exit(struct per_cpu *cpu_data, struct registers *regs)
index 6a0386b26479f567167943a2b5481f5a797d76e6..f45a992f9d5d2d15f5d90f7bebff3f740789426d 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/irqchip.h>
 #include <asm/platform.h>
 #include <asm/setup.h>
+#include <asm/traps.h>
 
 /*
  * This implementation assumes that the kernel driver already initialised most
@@ -149,6 +150,40 @@ static int gic_send_sgi(struct sgi *sgi)
        return 0;
 }
 
+int gicv3_handle_sgir_write(struct per_cpu *cpu_data, u64 sgir)
+{
+       struct sgi sgi;
+       struct cell *cell = cpu_data->cell;
+       unsigned int cpu;
+       unsigned long this_cpu = cpu_data->cpu_id;
+       unsigned long routing_mode = !!(sgir & ICC_SGIR_ROUTING_BIT);
+       unsigned long targets = sgir & ICC_SGIR_TARGET_MASK;
+       u32 irq = sgir >> ICC_SGIR_IRQN_SHIFT & 0xf;
+
+       /* FIXME: clusters are not supported yet. */
+       sgi.targets = 0;
+       sgi.routing_mode = routing_mode;
+       sgi.aff1 = sgir >> ICC_SGIR_AFF1_SHIFT & 0xff;
+       sgi.aff2 = sgir >> ICC_SGIR_AFF2_SHIFT & 0xff;
+       sgi.aff3 = sgir >> ICC_SGIR_AFF3_SHIFT & 0xff;
+       sgi.id = SGI_INJECT;
+
+       for_each_cpu_except(cpu, cell->cpu_set, this_cpu) {
+               if (routing_mode == 0 && !test_bit(cpu, &targets))
+                       continue;
+               else if (routing_mode == 1 && cpu == this_cpu)
+                       continue;
+
+               irqchip_set_pending(per_cpu(cpu), irq, false);
+               sgi.targets |= (1 << cpu);
+       }
+
+       /* Let the other CPUS inject their SGIs */
+       gic_send_sgi(&sgi);
+
+       return TRAP_HANDLED;
+}
+
 /*
  * Handle the maintenance interrupt, the rest is injected into the cell.
  * Return true when the IRQ has been handled by the hyp.
index 1e901483be85f6d33a8f9576ed64794a8be3e1ec..ed571a21663bab13e91b0962d19bfa8e9928a2fe 100644 (file)
@@ -16,6 +16,8 @@
 #include <asm/cell.h>
 #include <asm/percpu.h>
 
+#define SGI_INJECT     0
+
 #ifndef __ASSEMBLY__
 
 int arch_mmu_cell_init(struct cell *cell);
index 6e2ddb6836dc06765e6e021f20387f044c8518cd..5dc83e6302b73ff9e1c4c5a538c8b332bd081356 100644 (file)
@@ -244,5 +244,8 @@ static inline void gic_write_lr(unsigned int n, u64 val)
        }
 }
 
+struct per_cpu;
+int gicv3_handle_sgir_write(struct per_cpu *cpu_data, u64 sgir);
+
 #endif /* __ASSEMBLY__ */
 #endif /* _JAILHOUSE_ASM_GIC_V3_H */
index 3e3745f334d203544052e0c5c279b8a64cb9183a..3f291d41c5a3ba59cf919dcc8c6362ae253a3184 100644 (file)
@@ -23,6 +23,7 @@
 enum trap_return {
        TRAP_HANDLED            = 1,
        TRAP_UNHANDLED          = 0,
+       TRAP_FORBIDDEN          = -1,
 };
 
 struct trap_context {
index 3c0b896a5433c1dccd66b8026e0c5b778fd84655..8dc5ab57bbb445081fdda2d5429e60fda20acac0 100644 (file)
@@ -15,6 +15,8 @@
  */
 
 #include <asm/control.h>
+#include <asm/gic_common.h>
+#include <asm/platform.h>
 #include <asm/traps.h>
 #include <asm/sysregs.h>
 #include <jailhouse/printk.h>
@@ -195,8 +197,39 @@ static int arch_handle_hvc(struct per_cpu *cpu_data, struct trap_context *ctx)
        return TRAP_HANDLED;
 }
 
+static int arch_handle_cp15_64(struct per_cpu *cpu_data, struct trap_context *ctx)
+{
+       unsigned long rt_val, rt2_val;
+       u32 opc1        = ctx->esr >> 16 & 0x7;
+       u32 rt2         = ctx->esr >> 10 & 0xf;
+       u32 rt          = ctx->esr >> 5 & 0xf;
+       u32 crm         = ctx->esr >> 1 & 0xf;
+       u32 read        = ctx->esr & 1;
+
+       if (!read) {
+               access_cell_reg(ctx, rt, &rt_val, true);
+               access_cell_reg(ctx, rt2, &rt2_val, true);
+       }
+
+#ifdef CONFIG_ARM_GIC_V3
+       /* Trapped ICC_SGI1R write */
+       if (!read && opc1 == 0 && crm == 12) {
+               arch_skip_instruction(ctx);
+               return gicv3_handle_sgir_write(cpu_data,
+                               (u64)rt2_val << 32 | rt_val);
+       }
+#else
+       /* Avoid `unused' warning... */
+       crm = crm;
+       opc1 = opc1;
+#endif
+
+       return TRAP_UNHANDLED;
+}
+
 static const trap_handler trap_handlers[38] =
 {
+       [ESR_EC_CP15_64]        = arch_handle_cp15_64,
        [ESR_EC_HVC]            = arch_handle_hvc,
 };