From 5acadfac8c529963c656b8f3e9e61dbde8e1b961 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:38 +0200 Subject: hw/intc/arm_gic: Request FIQ sources Preparing for FIQ lines from GIC to CPUs, which is needed for GIC Security Extensions. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 3 +++ include/hw/intc/arm_gic_common.h | 1 + 2 files changed, 4 insertions(+) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index db9110c1c..f6fdbe2c0 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -789,6 +789,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/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index f6887ed92..01c6f248a 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -50,6 +50,7 @@ typedef struct GICState { /*< public >*/ qemu_irq parent_irq[GIC_NCPU]; + qemu_irq parent_fiq[GIC_NCPU]; bool enabled; bool cpu_enabled[GIC_NCPU]; -- cgit v1.2.3 From 362ebb2c2eae6d9467216bd48535c1cdce296451 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:39 +0200 Subject: hw/arm/vexpress.c: Wire FIQ between CPU <> GIC Connect FIQ output of the GIC CPU interfaces to the CPUs. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/arm/vexpress.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index aed218f01..118422c64 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -229,6 +229,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)); } } -- cgit v1.2.3 From d21c5be2df7923689ffbcd0beeeed0b3da7417d4 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:40 +0200 Subject: hw/intc/arm_gic: Add Security Extensions property The existing implementation does not support Security Extensions mentioned in the GICv1 and GICv2 architecture specification. Security Extensions are not available on all GICs. This property makes it possible to enable Security Extensions. It also makes GICD_TYPER/ICDICTR.SecurityExtn RAO for GICs which implement Security Extensions. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 5 ++++- hw/intc/arm_gic_common.c | 1 + include/hw/intc/arm_gic_common.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index f6fdbe2c0..581e2d60d 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -298,7 +298,10 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset) if (offset == 0) return s->enabled; if (offset == 4) - return ((s->num_irq / 32) - 1) | ((NUM_CPU(s) - 1) << 5); + /* Interrupt Controller Type Register */ + return ((s->num_irq / 32) - 1) + | ((NUM_CPU(s) - 1) << 5) + | (s->security_extn << 10); if (offset < 0x08) return 0; if (offset >= 0x80) { diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 18b01ba0c..f51cc7a48 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -149,6 +149,7 @@ static Property arm_gic_common_properties[] = { * (Internally, 0xffffffff also indicates "not a GIC but an NVIC".) */ DEFINE_PROP_UINT32("revision", GICState, revision, 1), + DEFINE_PROP_UINT8("security-extn", GICState, security_extn, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 01c6f248a..4e2501766 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -105,6 +105,7 @@ typedef struct GICState { MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */ uint32_t num_irq; uint32_t revision; + uint8_t security_extn; int dev_fd; /* kvm device fd if backed by kvm vgic support */ } GICState; -- cgit v1.2.3 From 981e2187abe0b8f20932c1f8916e3c8bbc29f330 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:41 +0200 Subject: hw/intc/arm_gic: Add ns_access() function Security Extensions for GICv1 and GICv2 use register banking to provide transparent access to seperate Secure and Non-secure copies of GIC configuration registers. This function will later be replaced by code determining the security state of a read/write access to a register. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 581e2d60d..7b5d45196 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -45,6 +45,13 @@ static inline int gic_get_current_cpu(GICState *s) return 0; } +/* Security state of a read / write access */ +static inline bool ns_access(void) +{ + /* TODO: use actual security state */ + return true; +} + /* TODO: Many places that call this routine could be optimized. */ /* Update interrupt status after enabled or pending bits have been changed. */ void gic_update(GICState *s) -- cgit v1.2.3 From 140ece8f745bb1282745fff7ae7012cd8e235737 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:42 +0200 Subject: hw/intc/arm_gic: Add Interrupt Group Registers Interrupt Group Registers (previously called Interrupt Security Registers) as defined in GICv1 with Security Extensions or GICv2 allow to configure interrupts as Secure (Group0) or Non-secure (Group1). In GICv2 these registers are implemented independent of the existence of Security Extensions. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 47 +++++++++++++++++++++++++++++++++++++--- hw/intc/arm_gic_common.c | 1 + hw/intc/gic_internal.h | 4 ++++ include/hw/intc/arm_gic_common.h | 1 + 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 7b5d45196..3569affeb 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -312,8 +312,26 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset) if (offset < 0x08) return 0; if (offset >= 0x80) { - /* Interrupt Security , RAZ/WI */ - return 0; + /* Interrupt Group Registers + * + * For GIC with Security Extn and Non-secure access RAZ/WI + * For GICv1 without Security Extn RAZ/WI + */ + res = 0; + if (!(s->security_extn && ns_access()) && + ((s->revision == 1 && s->security_extn) + || s->revision == 2)) { + irq = (offset - 0x080) * 8 + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + for (i = 0; i < 8; i++) { + if (!GIC_TEST_GROUP0(irq + i, cm)) { + res |= (1 << i); + } + } + } + return res; } goto bad_reg; } else if (offset < 0x200) { @@ -457,7 +475,30 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, } else if (offset < 4) { /* ignored. */ } else if (offset >= 0x80) { - /* Interrupt Security Registers, RAZ/WI */ + /* Interrupt Group Registers + * + * For GIC with Security Extn and Non-secure access RAZ/WI + * For GICv1 without Security Extn RAZ/WI + */ + if (!(s->security_extn && ns_access()) && + ((s->revision == 1 && s->security_extn) + || s->revision == 2)) { + irq = (offset - 0x080) * 8 + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + for (i = 0; i < 8; i++) { + /* Group bits are banked for private interrupts (internal)*/ + int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; + if (value & (1 << i)) { + /* Group1 (Non-secure) */ + GIC_SET_GROUP1(irq + i, cm); + } else { + /* Group0 (Secure) */ + GIC_SET_GROUP0(irq + i, cm); + } + } + } } else { goto bad_reg; } diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index f51cc7a48..686876057 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_UINT8(group, gic_irq_state), VMSTATE_END_OF_LIST() } }; diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 48a58d789..b0430ff16 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -50,6 +50,10 @@ s->priority1[irq][cpu] : \ s->priority2[(irq) - GIC_INTERNAL]) #define GIC_TARGET(irq) s->irq_target[irq] +#define GIC_SET_GROUP0(irq, cm) (s->irq_state[irq].group |= (cm)) +#define GIC_SET_GROUP1(irq, cm) (s->irq_state[irq].group &= ~(cm)) +#define GIC_TEST_GROUP0(irq, cm) ((s->irq_state[irq].group & (cm)) == 0) + /* The special cases for the revision property: */ #define REV_11MPCORE 0 diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 4e2501766..a61e52e35 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 */ + uint8_t group; } gic_irq_state; typedef struct GICState { -- cgit v1.2.3 From e3272a7694eb606236b9cca8555fabe59a32e97b Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:43 +0200 Subject: hw/intc/arm_gic: Make ICDDCR/GICD_CTLR banked ICDDCR/GICD_CTLR is banked in GICv1 implementations with Security Extensions or in GICv2 in independent from Security Extensions. This makes it possible to enable forwarding of interrupts from Distributor to the CPU interfaces for Group0 and Group1. EnableGroup0 (Bit [1]) in GICv1 is IMPDEF. Since this bit (Enable Non-secure) is present in the integrated IC of the Cortex-A9 MPCore, which implements the GICv1 profile, we support this bit in GICv1 too. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 34 ++++++++++++++++++++++++++++++---- hw/intc/arm_gic_common.c | 2 +- include/hw/intc/arm_gic_common.h | 7 ++++++- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 3569affeb..4172def1b 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -302,8 +302,18 @@ 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) { + res = 0; + if ((s->revision == 2 && !s->security_extn) + || (s->security_extn && !ns_access())) { + res = (s->enabled_grp[1] << 1) | s->enabled_grp[0]; + } else if (s->security_extn && ns_access()) { + res = s->enabled_grp[1]; + } else { + /* Neither GICv2 nor Security Extensions present */ + res = s->enabled; + } + } if (offset == 4) /* Interrupt Controller Type Register */ return ((s->num_irq / 32) - 1) @@ -470,8 +480,24 @@ 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"); + if ((s->revision == 2 && !s->security_extn) + || (s->security_extn && !ns_access())) { + s->enabled_grp[0] = value & (1U << 0); /* EnableGrp0 */ + /* For a GICv1 with Security Extn "EnableGrp1" is IMPDEF. */ + s->enabled_grp[1] = value & (1U << 1); /* EnableGrp1 */ + DPRINTF("Group0 distribution %sabled\n" + "Group1 distribution %sabled\n", + s->enabled_grp[0] ? "En" : "Dis", + s->enabled_grp[1] ? "En" : "Dis"); + } else if (s->security_extn && ns_access()) { + s->enabled_grp[1] = (value & 1U); + DPRINTF("Group1 distribution %sabled\n", + s->enabled_grp[1] ? "En" : "Dis"); + } else { + /* Neither GICv2 nor Security Extensions present */ + s->enabled = (value & 1U); + DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); + } } else if (offset < 4) { /* ignored. */ } else if (offset >= 0x80) { diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 686876057..63579b28c 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -64,7 +64,7 @@ static const VMStateDescription vmstate_gic = { .pre_save = gic_pre_save, .post_load = gic_post_load, .fields = (VMStateField[]) { - VMSTATE_BOOL(enabled, GICState), + VMSTATE_UINT8_ARRAY(enabled_grp, GICState, GIC_NR_GROUP), VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU), VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, vmstate_gic_irq_state, gic_irq_state), diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index a61e52e35..a39b0664c 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -30,6 +30,8 @@ #define GIC_NR_SGIS 16 /* Maximum number of possible CPU interfaces, determined by GIC architecture */ #define GIC_NCPU 8 +/* Number of Groups (Group0 [Secure], Group1 [Non-secure]) */ +#define GIC_NR_GROUP 2 #define MAX_NR_GROUP_PRIO 128 #define GIC_NR_APRS (MAX_NR_GROUP_PRIO / 32) @@ -52,7 +54,10 @@ typedef struct GICState { qemu_irq parent_irq[GIC_NCPU]; qemu_irq parent_fiq[GIC_NCPU]; - bool enabled; + union { + uint8_t enabled; + uint8_t enabled_grp[GIC_NR_GROUP]; /* EnableGrp0 and EnableGrp1 */ + }; bool cpu_enabled[GIC_NCPU]; gic_irq_state irq_state[GIC_MAXIRQ]; -- cgit v1.2.3 From 4d66a710e9facb2a01fda487179c0da391dbd69b Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:44 +0200 Subject: hw/intc/arm_gic: Make ICCICR/GICC_CTLR banked ICCICR/GICC_CTLR is banked in GICv1 implementations with Security Extensions or in GICv2 in independent from Security Extensions. This makes it possible to enable forwarding of interrupts from the CPU interfaces to the connected processors for Group0 and Group1. We also allow to set additional bits like AckCtl and FIQEn by changing the type from bool to uint32. Since the field does not only store the enable bit anymore and since we are touching the vmstate, we use the opportunity to rename the field to cpu_control. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 54 ++++++++++++++++++++++++++++++++++++---- hw/intc/arm_gic_common.c | 5 ++-- hw/intc/arm_gic_kvm.c | 8 +++--- hw/intc/armv7m_nvic.c | 2 +- hw/intc/gic_internal.h | 14 +++++++++++ include/hw/intc/arm_gic_common.h | 2 +- 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 4172def1b..98d6d680d 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -66,7 +66,7 @@ void gic_update(GICState *s) 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 || !(s->cpu_control[cpu][0] & 1)) { qemu_irq_lower(s->parent_irq[cpu]); return; } @@ -240,6 +240,52 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val) } } +uint32_t gic_get_cpu_control(GICState *s, int cpu) +{ + if ((s->revision >= 2 && !s->security_extn) + || (s->security_extn && !ns_access())) { + return s->cpu_control[cpu][0]; + } else if (s->security_extn && ns_access()) { + return s->cpu_control[cpu][1]; + } else { + return s->cpu_control[cpu][0]; + } +} + +void gic_set_cpu_control(GICState *s, int cpu, uint32_t value) +{ + /* CPU Interface Control is banked for GICv2 and GICv1 with Security Extn */ + if ((s->revision >= 2 && !s->security_extn) + || (s->security_extn && !ns_access())) { + /* Write to Secure instance of the register */ + s->cpu_control[cpu][0] = (value & GICC_CTLR_S_MASK); + /* Synchronize EnableGrp1 alias of Non-secure copy */ + s->cpu_control[cpu][1] &= ~GICC_CTLR_NS_EN_GRP1; + s->cpu_control[cpu][1] |= (value & GICC_CTLR_S_EN_GRP1) ? + GICC_CTLR_NS_EN_GRP1 : 0; + + DPRINTF("CPU Interface %d: Group0 Interrupts %sabled, " + "Group1 Interrupts %sabled\n", cpu, + (s->cpu_control[cpu][0] & GICC_CTLR_S_EN_GRP0) ? "En" : "Dis", + (s->cpu_control[cpu][0] & GICC_CTLR_S_EN_GRP1) ? "En" : "Dis"); + } else if (s->security_extn && ns_access()) { + /* Write to Non-secure instance of the register */ + s->cpu_control[cpu][1] = (value & GICC_CTLR_NS_MASK); + /* Synchronize EnableGrp1 alias of Secure copy */ + s->cpu_control[cpu][0] &= ~GICC_CTLR_S_EN_GRP1; + s->cpu_control[cpu][0] |= (value & GICC_CTLR_NS_EN_GRP1) ? + GICC_CTLR_S_EN_GRP1 : 0; + + DPRINTF("CPU Interface %d: Group1 Interrupts %sabled\n", cpu, + (s->cpu_control[cpu][1] & GICC_CTLR_NS_EN_GRP1) ? "En" : "Dis"); + } else { + s->cpu_control[cpu][0] = (value & 1); + + DPRINTF("CPU Interface %d %sabled\n", cpu, + s->cpu_control[cpu][0] ? "En" : "Dis"); + } +} + void gic_complete_irq(GICState *s, int cpu, int irq) { int update = 0; @@ -745,7 +791,7 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset) { switch (offset) { case 0x00: /* Control */ - return s->cpu_enabled[cpu]; + return gic_get_cpu_control(s, cpu); case 0x04: /* Priority mask */ return s->priority_mask[cpu]; case 0x08: /* Binary Point */ @@ -771,9 +817,7 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value) { switch (offset) { case 0x00: /* Control */ - s->cpu_enabled[cpu] = (value & 1); - DPRINTF("CPU %d %sabled\n", cpu, s->cpu_enabled[cpu] ? "En" : "Dis"); - break; + return gic_set_cpu_control(s, cpu, value); case 0x04: /* Priority mask */ s->priority_mask[cpu] = (value & 0xff); break; diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 63579b28c..b19e02f12 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -65,7 +65,7 @@ static const VMStateDescription vmstate_gic = { .post_load = gic_post_load, .fields = (VMStateField[]) { VMSTATE_UINT8_ARRAY(enabled_grp, GICState, GIC_NR_GROUP), - VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU), + VMSTATE_UINT32_2DARRAY(cpu_control, GICState, GIC_NCPU, GIC_NR_GROUP), VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, vmstate_gic_irq_state, gic_irq_state), VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ), @@ -127,7 +127,8 @@ static void arm_gic_common_reset(DeviceState *dev) s->current_pending[i] = 1023; s->running_irq[i] = 1023; s->running_priority[i] = 0x100; - s->cpu_enabled[i] = false; + s->cpu_control[i][0] = false; + s->cpu_control[i][1] = false; } for (i = 0; i < GIC_NR_SGIS; i++) { GIC_SET_ENABLED(i, ALL_CPU_MASK); diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 5038885af..9a6a2bb58 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -379,8 +379,8 @@ static void kvm_arm_gic_put(GICState *s) */ for (cpu = 0; cpu < s->num_cpu; cpu++) { - /* s->cpu_enabled[cpu] -> GICC_CTLR */ - reg = s->cpu_enabled[cpu]; + /* s->cpu_enabled[cpu][0] -> GICC_CTLR */ + reg = s->cpu_control[cpu]; kvm_gicc_access(s, 0x00, cpu, ®, true); /* s->priority_mask[cpu] -> GICC_PMR */ @@ -478,9 +478,9 @@ static void kvm_arm_gic_get(GICState *s) */ for (cpu = 0; cpu < s->num_cpu; cpu++) { - /* GICC_CTLR -> s->cpu_enabled[cpu] */ + /* GICC_CTLR -> s->cpu_control[cpu][0] */ kvm_gicc_access(s, 0x00, cpu, ®, false); - s->cpu_enabled[cpu] = (reg & 1); + s->cpu_control[cpu][0] = reg; /* GICC_PMR -> s->priority_mask[cpu] */ kvm_gicc_access(s, 0x04, cpu, ®, false); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 1a7af450a..57486fce2 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -465,7 +465,7 @@ static void armv7m_nvic_reset(DeviceState *dev) * as enabled by default, and with a priority mask which allows * all interrupts through. */ - s->gic.cpu_enabled[0] = true; + s->gic.cpu_control[0][0] = true; s->gic.priority_mask[0] = 0x100; /* The NVIC as a whole is always enabled. */ s->gic.enabled = true; diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index b0430ff16..e9814f43f 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -54,6 +54,17 @@ #define GIC_SET_GROUP1(irq, cm) (s->irq_state[irq].group &= ~(cm)) #define GIC_TEST_GROUP0(irq, cm) ((s->irq_state[irq].group & (cm)) == 0) +#define GICC_CTLR_S_EN_GRP0 (1U << 0) +#define GICC_CTLR_S_EN_GRP1 (1U << 1) +#define GICC_CTLR_S_ACK_CTL (1U << 2) +#define GICC_CTLR_S_FIQ_EN (1U << 3) +#define GICC_CTLR_S_CBPR (1U << 4) /* GICv1: SBPR */ + +#define GICC_CTLR_S_MASK 0x7ff + +#define GICC_CTLR_NS_EN_GRP1 (1U << 0) +#define GICC_CTLR_NS_MASK (1 | 3 << 5 | 1 << 9) + /* The special cases for the revision property: */ #define REV_11MPCORE 0 @@ -65,6 +76,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq); void gic_update(GICState *s); void gic_init_irqs_and_distributor(GICState *s, int num_irq); void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val); +uint32_t gic_get_cpu_control(GICState *s, int cpu); +void gic_set_cpu_control(GICState *s, int cpu, uint32_t value); + static inline bool gic_test_pending(GICState *s, int irq, int cm) { diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index a39b0664c..a9129720b 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -58,7 +58,7 @@ typedef struct GICState { uint8_t enabled; uint8_t enabled_grp[GIC_NR_GROUP]; /* EnableGrp0 and EnableGrp1 */ }; - bool cpu_enabled[GIC_NCPU]; + uint32_t cpu_control[GIC_NCPU][GIC_NR_GROUP]; gic_irq_state irq_state[GIC_MAXIRQ]; uint8_t irq_target[GIC_MAXIRQ]; -- cgit v1.2.3 From 7c995d63e7d0db6bdad892d0d1bb8341e770f1a1 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:45 +0200 Subject: hw/intc/arm_gic: Make ICCBPR/GICC_BPR banked This register is banked in GICs with Security Extensions. Storing the non-secure copy of BPR in the abpr, which is an alias to the non-secure copy for secure access. ABPR itself is only accessible from secure state if the GIC implements Security Extensions. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 25 +++++++++++++++++++++---- include/hw/intc/arm_gic_common.h | 8 +++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 98d6d680d..bac70caef 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -795,7 +795,12 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset) case 0x04: /* Priority mask */ return s->priority_mask[cpu]; case 0x08: /* Binary Point */ - return s->bpr[cpu]; + if (s->security_extn && ns_access()) { + /* BPR is banked. Non-secure copy stored in ABPR. */ + return s->abpr[cpu]; + } else { + return s->bpr[cpu]; + } case 0x0c: /* Acknowledge */ return gic_acknowledge_irq(s, cpu); case 0x14: /* Running Priority */ @@ -803,7 +808,14 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset) case 0x18: /* Highest Pending Interrupt */ return s->current_pending[cpu]; case 0x1c: /* Aliased Binary Point */ - return s->abpr[cpu]; + if ((s->security_extn && ns_access())) { + /* If Security Extensions are present ABPR is a secure register, + * only accessible from secure state. + */ + return 0; + } else { + return s->abpr[cpu]; + } case 0xd0: case 0xd4: case 0xd8: case 0xdc: return s->apr[(offset - 0xd0) / 4][cpu]; default: @@ -822,12 +834,17 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value) s->priority_mask[cpu] = (value & 0xff); break; case 0x08: /* Binary Point */ - s->bpr[cpu] = (value & 0x7); + if (s->security_extn && ns_access()) { + /* BPR is banked. Non-secure copy stored in ABPR. */ + s->abpr[cpu] = (value & 0x7); + } else { + s->bpr[cpu] = (value & 0x7); + } break; case 0x10: /* End Of Interrupt */ return gic_complete_irq(s, cpu, value & 0x3ff); case 0x1c: /* Aliased Binary Point */ - if (s->revision >= 2) { + if (s->revision >= 2 && !(s->security_extn && ns_access())) { s->abpr[cpu] = (value & 0x7); } break; diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index a9129720b..c547418c2 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -78,9 +78,11 @@ typedef struct GICState { uint16_t running_priority[GIC_NCPU]; uint16_t current_pending[GIC_NCPU]; - /* We present the GICv2 without security extensions to a guest and - * therefore the guest can configure the GICC_CTLR to configure group 1 - * binary point in the abpr. + /* If we present the GICv2 without security extensions to a guest, + * the guest can configure the GICC_CTLR to configure group 1 binary point + * in the abpr. + * For a GIC with Security Extensions we use use bpr for the + * secure copy and abpr as storage for the non-secure copy of the register. */ uint8_t bpr[GIC_NCPU]; uint8_t abpr[GIC_NCPU]; -- cgit v1.2.3 From 2d05a9643a8c0e4ca3d5a1c4b4b5dc84494a8e8a Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:46 +0200 Subject: hw/intc/arm_gic: Implement Non-secure view of RPR For GICs with Security Extensions Non-secure reads have a restricted view on the current running priority. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 17 ++++++++++++++++- hw/intc/gic_internal.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index bac70caef..210ac2b7e 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -286,6 +286,21 @@ void gic_set_cpu_control(GICState *s, int cpu, uint32_t value) } } +uint8_t gic_get_running_priority(GICState *s, int cpu) +{ + if (s->security_extn && ns_access()) { + if (s->running_priority[cpu] & 0x80) { + /* Running priority in upper half, return Non-secure view */ + return s->running_priority[cpu] << 1; + } else { + /* Running priority in lower half, RAZ */ + return 0; + } + } else { + return s->running_priority[cpu]; + } +} + void gic_complete_irq(GICState *s, int cpu, int irq) { int update = 0; @@ -804,7 +819,7 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset) case 0x0c: /* Acknowledge */ return gic_acknowledge_irq(s, cpu); case 0x14: /* Running Priority */ - return s->running_priority[cpu]; + return gic_get_running_priority(s, cpu); case 0x18: /* Highest Pending Interrupt */ return s->current_pending[cpu]; case 0x1c: /* Aliased Binary Point */ diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index e9814f43f..433d75e8f 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -78,6 +78,7 @@ void gic_init_irqs_and_distributor(GICState *s, int num_irq); void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val); uint32_t gic_get_cpu_control(GICState *s, int cpu); void gic_set_cpu_control(GICState *s, int cpu, uint32_t value); +uint8_t gic_get_running_priority(GICState *s, int cpu); static inline bool gic_test_pending(GICState *s, int irq, int cm) -- cgit v1.2.3 From 5f262eb873430f3be8a85481e45f15c76929e2b8 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:47 +0200 Subject: hw/intc/arm_gic: Handle grouping for GICC_HPPIR Grouping (GICv2) and Security Extensions change the behaviour of reads of the highest priority pending interrupt register (ICCHPIR/GICC_HPPIR). Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 29 ++++++++++++++++++++++++++++- hw/intc/gic_internal.h | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 210ac2b7e..030751602 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -301,6 +301,33 @@ uint8_t gic_get_running_priority(GICState *s, int cpu) } } +uint16_t gic_get_current_pending_irq(GICState *s, int cpu) +{ + bool isGrp0; + uint16_t pendingId = s->current_pending[cpu]; + + if (pendingId < GIC_MAXIRQ && (s->revision >= 2 || s->security_extn)) { + isGrp0 = GIC_TEST_GROUP0(pendingId, (1 << cpu)); + if ((isGrp0 && !s->enabled_grp[0]) + || (!isGrp0 && !s->enabled_grp[1])) { + return 1023; + } + if (s->security_extn) { + if (isGrp0 && ns_access()) { + /* Group0 interrupts hidden from Non-secure access */ + return 1023; + } + if (!isGrp0 && !ns_access() + && !(s->cpu_control[cpu][0] & GICC_CTLR_S_ACK_CTL)) { + /* Group1 interrupts only seen by Secure access if + * AckCtl bit set. */ + return 1022; + } + } + } + return pendingId; +} + void gic_complete_irq(GICState *s, int cpu, int irq) { int update = 0; @@ -821,7 +848,7 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset) case 0x14: /* Running Priority */ return gic_get_running_priority(s, cpu); case 0x18: /* Highest Pending Interrupt */ - return s->current_pending[cpu]; + return gic_get_current_pending_irq(s, cpu); case 0x1c: /* Aliased Binary Point */ if ((s->security_extn && ns_access())) { /* If Security Extensions are present ABPR is a secure register, diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 433d75e8f..17632c18a 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -79,6 +79,7 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val); uint32_t gic_get_cpu_control(GICState *s, int cpu); void gic_set_cpu_control(GICState *s, int cpu, uint32_t value); uint8_t gic_get_running_priority(GICState *s, int cpu); +uint16_t gic_get_current_pending_irq(GICState *s, int cpu); static inline bool gic_test_pending(GICState *s, int irq, int cm) -- cgit v1.2.3 From d399372ae5bde19dfe2c7a1af3cd68f3f8c9f115 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:48 +0200 Subject: hw/intc/arm_gic: Change behavior of EOIR writes Grouping (GICv2) and Security Extensions change the behavior of EOIR writes. Completing Group0 interrupts is only allowed from Secure state and completing Group1 interrupts from Secure state is only allowed if AckCtl bit is set. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 030751602..92bc3f59d 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -356,6 +356,21 @@ void gic_complete_irq(GICState *s, int cpu, int irq) GIC_SET_PENDING(irq, cm); update = 1; } + } else if ((s->revision >= 2 && !s->security_extn) + || (s->security_extn && !ns_access())) { + /* Handle GICv2 without Security Extensions or GIC with Security + * Extensions and a secure write. + */ + if (!GIC_TEST_GROUP0(irq, cm) + && !(s->cpu_control[cpu][0] & GICC_CTLR_S_ACK_CTL)) { + /* Unpredictable. We choose to ignore. */ + DPRINTF("EOI for Group1 interrupt %d ignored " + "(AckCtl disabled)\n", irq); + return; + } + } else if (s->security_extn && ns_access() && GIC_TEST_GROUP0(irq, cm)) { + DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq); + return; } if (irq != s->running_irq[cpu]) { -- cgit v1.2.3 From fea0d247416feabcc90e9275dc3d6915a02bbc8f Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:49 +0200 Subject: hw/intc/arm_gic: Change behavior of IAR writes Grouping (GICv2) and Security Extensions change the behavior of IAR reads. Acknowledging Group0 interrupts is only allowed from Secure state and acknowledging Group1 interrupts from Secure state is only allowed if AckCtl bit is set. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 92bc3f59d..e84d226ef 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -190,11 +190,35 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu) int ret, irq, src; int cm = 1 << cpu; irq = s->current_pending[cpu]; + bool isGrp0; if (irq == 1023 || GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) { DPRINTF("ACK no pending IRQ\n"); return 1023; } + + if (s->revision >= 2 || s->security_extn) { + isGrp0 = GIC_TEST_GROUP0(irq, (1 << cpu)); + if ((isGrp0 && (!s->enabled_grp[0] + || !(s->cpu_control[cpu][0] & GICC_CTLR_S_EN_GRP0))) + || (!isGrp0 && (!s->enabled_grp[1] + || !(s->cpu_control[cpu][1] & GICC_CTLR_NS_EN_GRP1)))) { + return 1023; + } + + if ((s->revision >= 2 && !s->security_extn) + || (s->security_extn && !ns_access())) { + if (!isGrp0 && !(s->cpu_control[cpu][0] & GICC_CTLR_S_ACK_CTL)) { + DPRINTF("Read of IAR ignored for Group1 interrupt %d " + "(AckCtl disabled)\n", irq); + return 1022; + } + } else if (s->security_extn && ns_access() && isGrp0) { + DPRINTF("Non-secure read of IAR ignored for Group0 interrupt %d\n", + irq); + return 1023; + } + } s->last_active[irq][cpu] = s->running_irq[cpu]; if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { -- cgit v1.2.3 From 6fc4812ad30045034a5a54124a2ab2d50f282122 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:50 +0200 Subject: hw/intc/arm_gic: Restrict priority view GICs with Security Extensions restrict the non-secure view of the interrupt priority and priority mask registers. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 66 +++++++++++++++++++++++++++++++++++++++++++++----- hw/intc/gic_internal.h | 3 +++ 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index e84d226ef..17a7290a3 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -257,11 +257,66 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu) void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val) { + uint8_t prio = val; + + if (s->security_extn && ns_access()) { + if (GIC_TEST_GROUP0(irq, (1 << cpu))) { + return; /* Ignore Non-secure access of Group0 IRQ */ + } + prio = 0x80 | (prio >> 1); /* Non-secure view */ + } + if (irq < GIC_INTERNAL) { - s->priority1[irq][cpu] = val; + s->priority1[irq][cpu] = prio; } else { - s->priority2[(irq) - GIC_INTERNAL] = val; + s->priority2[(irq) - GIC_INTERNAL] = prio; + } +} + +uint32_t gic_get_priority(GICState *s, int cpu, int irq) +{ + uint32_t prio = GIC_GET_PRIORITY(irq, cpu); + + if (s->security_extn && ns_access()) { + if (GIC_TEST_GROUP0(irq, (1 << cpu))) { + return 0; /* Non-secure access cannot read priority of Group0 IRQ */ + } + prio = (prio << 1); /* Non-secure view */ } + return prio; +} + +void gic_set_priority_mask(GICState *s, int cpu, uint8_t val) +{ + uint8_t pmask = (val & 0xff); + + if (s->security_extn && ns_access()) { + if (s->priority_mask[cpu] & 0x80) { + /* Priority Mask in upper half */ + pmask = 0x80 | (pmask >> 1); + } else { + /* Non-secure write ignored if priority mask is in lower half */ + return; + } + } + s->priority_mask[cpu] = pmask; +} + +uint32_t gic_get_priority_mask(GICState *s, int cpu) +{ + uint32_t pmask = s->priority_mask[cpu]; + + if (s->security_extn && ns_access()) { + if (pmask & 0x80) { + /* Priority Mask in upper half, return Non-secure view */ + pmask = (pmask << 1); + } else { + /* Priority Mask in lower half, RAZ */ + pmask = 0; + } + } + return pmask; + } uint32_t gic_get_cpu_control(GICState *s, int cpu) @@ -519,7 +574,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset) irq = (offset - 0x400) + GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; - res = GIC_GET_PRIORITY(irq, cpu); + res = gic_get_priority(s, cpu, irq); } else if (offset < 0xc00) { /* Interrupt CPU Target. */ if (s->num_cpu == 1 && s->revision != REV_11MPCORE) { @@ -874,7 +929,7 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset) case 0x00: /* Control */ return gic_get_cpu_control(s, cpu); case 0x04: /* Priority mask */ - return s->priority_mask[cpu]; + return gic_get_priority_mask(s, cpu); case 0x08: /* Binary Point */ if (s->security_extn && ns_access()) { /* BPR is banked. Non-secure copy stored in ABPR. */ @@ -912,8 +967,7 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value) case 0x00: /* Control */ return gic_set_cpu_control(s, cpu, value); case 0x04: /* Priority mask */ - s->priority_mask[cpu] = (value & 0xff); - break; + return gic_set_priority_mask(s, cpu, value); case 0x08: /* Binary Point */ if (s->security_extn && ns_access()) { /* BPR is banked. Non-secure copy stored in ABPR. */ diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 17632c18a..8d951ccc0 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -76,6 +76,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq); void gic_update(GICState *s); void gic_init_irqs_and_distributor(GICState *s, int num_irq); void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val); +uint32_t gic_get_priority(GICState *s, int cpu, int irq); +void gic_set_priority_mask(GICState *s, int cpu, uint8_t val); +uint32_t gic_get_priority_mask(GICState *s, int cpu); uint32_t gic_get_cpu_control(GICState *s, int cpu); void gic_set_cpu_control(GICState *s, int cpu, uint32_t value); uint8_t gic_get_running_priority(GICState *s, int cpu); -- cgit v1.2.3 From 5e13b6fa42b007ce4b24d881043bbb9693beee2e Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:51 +0200 Subject: hw/intc/arm_gic: Break out gic_update() function Prepare to split gic_update() in two functions, one for GICs with interrupt grouping and one without grouping (existing). Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 11 ++++++++--- hw/intc/gic_internal.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 17a7290a3..10d1f9a9c 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -52,9 +52,7 @@ static inline bool ns_access(void) return true; } -/* TODO: Many places that call this routine could be optimized. */ -/* Update interrupt status after enabled or pending bits have been changed. */ -void gic_update(GICState *s) +inline void gic_update_no_grouping(GICState *s) { int best_irq; int best_prio; @@ -93,6 +91,13 @@ void gic_update(GICState *s) } } +/* TODO: Many places that call this routine could be optimized. */ +/* Update interrupt status after enabled or pending bits have been changed. */ +void gic_update(GICState *s) +{ + gic_update_no_grouping(s); +} + void gic_set_pending_private(GICState *s, int cpu, int irq) { int cm = 1 << cpu; diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 8d951ccc0..150f8674a 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -73,6 +73,7 @@ void gic_set_pending_private(GICState *s, int cpu, int irq); uint32_t gic_acknowledge_irq(GICState *s, int cpu); void gic_complete_irq(GICState *s, int cpu, int irq); +inline void gic_update_no_grouping(GICState *s); void gic_update(GICState *s); void gic_init_irqs_and_distributor(GICState *s, int num_irq); void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val); -- cgit v1.2.3 From 198db95b8a9781d5f071c3b0532e0c77609eb1a6 Mon Sep 17 00:00:00 2001 From: Fabian Aggeler Date: Fri, 22 Aug 2014 12:29:52 +0200 Subject: hw/intc/arm_gic: add gic_update() for grouping GICs with grouping (GICv2 or GICv1 with Security Extensions) have a different exception generation model which is more complicated than without interrupt grouping. We add a new function to handle this model. Signed-off-by: Fabian Aggeler Signed-off-by: Greg Bellows --- hw/intc/arm_gic.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++- hw/intc/gic_internal.h | 1 + 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 10d1f9a9c..bdfc870d9 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -52,6 +52,87 @@ static inline bool ns_access(void) return true; } +inline void gic_update_with_grouping(GICState *s) +{ + int best_irq; + int best_prio; + int irq; + int irq_level; + int fiq_level; + int cpu; + int cm; + bool next_int; + bool next_grp0; + bool gicc_grp0_enabled; + bool gicc_grp1_enabled; + + for (cpu = 0; cpu < NUM_CPU(s); cpu++) { + cm = 1 << cpu; + gicc_grp0_enabled = s->cpu_control[cpu][0] & GICC_CTLR_S_EN_GRP0; + gicc_grp1_enabled = s->cpu_control[cpu][1] & GICC_CTLR_S_EN_GRP1; + next_int = 0; + next_grp0 = 0; + + s->current_pending[cpu] = 1023; + if ((!s->enabled_grp[0] && !s->enabled_grp[1]) + || (!gicc_grp0_enabled && !gicc_grp1_enabled)) { + qemu_irq_lower(s->parent_irq[cpu]); + qemu_irq_lower(s->parent_fiq[cpu]); + return; + } + + /* Determine highest priority pending interrupt */ + best_prio = 0x100; + best_irq = 1023; + for (irq = 0; irq < s->num_irq; irq++) { + if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm)) { + if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { + best_prio = GIC_GET_PRIORITY(irq, cpu); + best_irq = irq; + } + } + } + + /* Priority of IRQ higher than priority mask? */ + if (best_prio < s->priority_mask[cpu]) { + s->current_pending[cpu] = best_irq; + if (GIC_TEST_GROUP0(best_irq, cm) && s->enabled_grp[0]) { + /* TODO: Add subpriority handling (binary point register) */ + if (best_prio < s->running_priority[cpu]) { + next_int = true; + next_grp0 = true; + } + } else if (!GIC_TEST_GROUP0(best_irq, cm) && s->enabled_grp[1]) { + /* TODO: Add subpriority handling (binary point register) */ + if (best_prio < s->running_priority[cpu]) { + next_int = true; + next_grp0 = false; + } + } + } + + fiq_level = 0; + irq_level = 0; + if (next_int) { + if (next_grp0 && (s->cpu_control[cpu][0] & GICC_CTLR_S_FIQ_EN)) { + if (gicc_grp0_enabled) { + fiq_level = 1; + DPRINTF("Raised pending FIQ %d (cpu %d)\n", best_irq, cpu); + } + } else { + if ((next_grp0 && gicc_grp0_enabled) + || (!next_grp0 && gicc_grp1_enabled)) { + irq_level = 1; + DPRINTF("Raised pending IRQ %d (cpu %d)\n", best_irq, cpu); + } + } + } + /* Set IRQ/FIQ signal */ + qemu_set_irq(s->parent_irq[cpu], irq_level); + qemu_set_irq(s->parent_fiq[cpu], fiq_level); + } +} + inline void gic_update_no_grouping(GICState *s) { int best_irq; @@ -95,7 +176,11 @@ inline void gic_update_no_grouping(GICState *s) /* Update interrupt status after enabled or pending bits have been changed. */ void gic_update(GICState *s) { - gic_update_no_grouping(s); + if (s->revision >= 2 || s->security_extn) { + gic_update_with_grouping(s); + } else { + gic_update_no_grouping(s); + } } void gic_set_pending_private(GICState *s, int cpu, int irq) diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 150f8674a..6f67a0906 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -73,6 +73,7 @@ void gic_set_pending_private(GICState *s, int cpu, int irq); uint32_t gic_acknowledge_irq(GICState *s, int cpu); void gic_complete_irq(GICState *s, int cpu, int irq); +inline void gic_update_with_grouping(GICState *s); inline void gic_update_no_grouping(GICState *s); void gic_update(GICState *s); void gic_init_irqs_and_distributor(GICState *s, int num_irq); -- cgit v1.2.3