From 89bbb19a8429d5abb3e028db398394cc37802014 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Fri, 11 Apr 2014 17:14:58 +0100 Subject: WIP: Initial attempt at GICv2 (without security extensions). Change-Id: I9767d7a9aad43e8762a7b615c6f9dd1a00799f18 Signed-off-by: Daniel Thompson --- hw/arm/vexpress.c | 2 + hw/intc/arm_gic.c | 93 ++++++++++++++++++++++++++++++++++------ hw/intc/arm_gic_common.c | 9 +++- hw/intc/armv7m_nvic.c | 2 +- hw/intc/gic_internal.h | 3 ++ include/hw/intc/arm_gic_common.h | 6 ++- 6 files changed, 98 insertions(+), 17 deletions(-) diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 169eb061a..66063d5f1 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -228,6 +228,8 @@ static void init_cpus(const char *cpu_model, const char *privdev, DeviceState *cpudev = DEVICE(qemu_get_cpu(n)); sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(busdev, n + smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); } } diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 955b8d494..c37a857cb 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -22,7 +22,7 @@ #include "gic_internal.h" #include "qom/cpu.h" -//#define DEBUG_GIC +#define DEBUG_GIC #ifdef DEBUG_GIC #define DPRINTF(fmt, ...) \ @@ -52,14 +52,15 @@ void gic_update(GICState *s) int best_irq; int best_prio; int irq; - int level; + int irq_level; + int fiq_level; int cpu; int cm; for (cpu = 0; cpu < NUM_CPU(s); cpu++) { cm = 1 << cpu; s->current_pending[cpu] = 1023; - if (!s->enabled || !s->cpu_enabled[cpu]) { + if (!s->enabled_grp0 || !s->cpu_enabled[cpu]) { qemu_irq_lower(s->parent_irq[cpu]); return; } @@ -73,15 +74,25 @@ void gic_update(GICState *s) } } } - level = 0; + irq_level = 0; if (best_prio < s->priority_mask[cpu]) { s->current_pending[cpu] = best_irq; if (best_prio < s->running_priority[cpu]) { DPRINTF("Raised pending IRQ %d (cpu %d)\n", best_irq, cpu); - level = 1; + irq_level = 1; } } - qemu_set_irq(s->parent_irq[cpu], level); + + fiq_level = 0; + if (s->revision >= 2) { + if (s->enabled_fiq && !GIC_TEST_GROUP(best_irq)) { + fiq_level = irq_level; + irq_level = 0; + } + } + + qemu_set_irq(s->parent_irq[cpu], irq_level); + qemu_set_irq(s->parent_fiq[cpu], fiq_level); } } @@ -294,15 +305,35 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset) cpu = gic_get_current_cpu(s); cm = 1 << cpu; if (offset < 0x100) { - if (offset == 0) - return s->enabled; + if (offset == 0) { + if (s->revision < 2) { + return s->enabled_grp0; + } else { + return (s->enabled_grp0) | (s->enabled_grp1 << 1) | + (s->enabled_fiq << 3); + } + } if (offset == 4) return ((s->num_irq / 32) - 1) | ((NUM_CPU(s) - 1) << 5); if (offset < 0x08) return 0; if (offset >= 0x80) { - /* Interrupt Security , RAZ/WI */ - return 0; + /* Interrupt Group. */ + if (s->revision < 2) { + /* GICv1 (or security extensions), RAZ/WI */ + res = 0; + } else { + /* GICv2+ without security extensions */ + irq = (offset - 0x80) * 8 + GIC_BASE_IRQ; + if (irq >= s->num_irq) + goto bad_reg; + res = 0; + for (i = 0; i < 8; i++) { + if (GIC_TEST_GROUP(irq + i)) { + res |= (1 << i); + } + } + } } goto bad_reg; } else if (offset < 0x200) { @@ -441,12 +472,45 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, cpu = gic_get_current_cpu(s); if (offset < 0x100) { if (offset == 0) { - s->enabled = (value & 1); - DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); + s->enabled_grp0 = (value & (1 << 0)); + if (s->revision < 2) { + DPRINTF("Distribution %sabled\n", + s->enabled_grp0 ? "En" : "Dis"); + } else { + s->enabled_grp1 = (value & (1 << 1)); + s->enabled_fiq = (value & (1 << 3)); + DPRINTF("Interrupt distribution: " + "group0 %sabled; group1 %sabled; fiq %sabled\n", + s->enabled_grp0 ? "En" : "Dis", + s->enabled_grp1 ? "En" : "Dis", + s->enabled_fiq ? "En" : "Dis"); + } } else if (offset < 4) { /* ignored. */ } else if (offset >= 0x80) { - /* Interrupt Security Registers, RAZ/WI */ + /* Interrupt Group. */ + if (s->revision < 2) { + /* GICv1 (or security extensions), RAZ/WI */ + } else { + /* GICv2+ without security extensions */ + irq = (offset - 0x80) * 8 + GIC_BASE_IRQ; + irq += GIC_BASE_IRQ; + if (irq >= s->num_irq) + goto bad_reg; + for (i = 0; i < 8; i++) { + if (value & (1 << i)) { + if (!GIC_TEST_GROUP(irq + i)) { + DPRINTF("Set IRQ %d to group1\n", irq + i); + } + GIC_SET_GROUP(irq + i); + } else { + if (GIC_TEST_GROUP(irq + i)) { + DPRINTF("Set IRQ %d to group0\n", irq + i); + } + GIC_CLEAR_GROUP(irq + i); + } + } + } } else { goto bad_reg; } @@ -786,6 +850,9 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq) for (i = 0; i < NUM_CPU(s); i++) { sysbus_init_irq(sbd, &s->parent_irq[i]); } + for (i = 0; i < NUM_CPU(s); i++) { + sysbus_init_irq(sbd, &s->parent_fiq[i]); + } memory_region_init_io(&s->iomem, OBJECT(s), &gic_dist_ops, s, "gic_dist", 0x1000); } diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 6d884eca3..6935331b4 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -52,6 +52,7 @@ static const VMStateDescription vmstate_gic_irq_state = { VMSTATE_UINT8(level, gic_irq_state), VMSTATE_BOOL(model, gic_irq_state), VMSTATE_BOOL(edge_trigger, gic_irq_state), + VMSTATE_BOOL(group, gic_irq_state), VMSTATE_END_OF_LIST() } }; @@ -63,7 +64,9 @@ static const VMStateDescription vmstate_gic = { .pre_save = gic_pre_save, .post_load = gic_post_load, .fields = (VMStateField[]) { - VMSTATE_BOOL(enabled, GICState), + VMSTATE_BOOL(enabled_grp0, GICState), + VMSTATE_BOOL(enabled_grp1, GICState), + VMSTATE_BOOL(enabled_fiq, GICState), VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU), VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, vmstate_gic_irq_state, gic_irq_state), @@ -138,7 +141,9 @@ static void arm_gic_common_reset(DeviceState *dev) s->irq_target[i] = 1; } } - s->enabled = false; + s->enabled_grp0 = false; + s->enabled_grp1 = false; + s->enabled_fiq = false; } static Property arm_gic_common_properties[] = { diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 6066fa683..f3d1f4347 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -466,7 +466,7 @@ static void armv7m_nvic_reset(DeviceState *dev) s->gic.cpu_enabled[0] = true; s->gic.priority_mask[0] = 0x100; /* The NVIC as a whole is always enabled. */ - s->gic.enabled = true; + s->gic.enabled_grp0 = true; systick_reset(s); } diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 48a58d789..017b0174f 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -29,6 +29,9 @@ through the normal GIC interface. */ #define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0) +#define GIC_SET_GROUP(irq) s->irq_state[irq].group = true +#define GIC_CLEAR_GROUP(irq) s->irq_state[irq].group = false +#define GIC_TEST_GROUP(irq) (s->irq_state[irq].group) #define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) #define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) #define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index f6887ed92..7bd1fc01a 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -42,6 +42,7 @@ typedef struct gic_irq_state { uint8_t level; bool model; /* 0 = N:N, 1 = 1:N */ bool edge_trigger; /* true: edge-triggered, false: level-triggered */ + bool group; /* true: group1, false: group0 */ } gic_irq_state; typedef struct GICState { @@ -50,7 +51,10 @@ typedef struct GICState { /*< public >*/ qemu_irq parent_irq[GIC_NCPU]; - bool enabled; + qemu_irq parent_fiq[GIC_NCPU]; + bool enabled_grp0; + bool enabled_grp1; + bool enabled_fiq; bool cpu_enabled[GIC_NCPU]; gic_irq_state irq_state[GIC_MAXIRQ]; -- cgit v1.2.3