From b06c262b45cf7afcf56dd0f2189ad8948b117e7d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 8 Sep 2015 17:38:42 +0100 Subject: armv7m_nvic: Implement ICSR without using internal GIC state Change the implementation of the Interrupt Control and State Register in the v7M NVIC to not use the running_irq and last_active internal state fields in the GIC. These fields don't correspond to state in a real GIC and will be removed soon. The changes to the ICSR are: * the VECTACTIVE field is documented as identical to the IPSR[8:0] field, so implement it that way * implement RETTOBASE via looking at the active state bits Signed-off-by: Peter Maydell Message-id: 1438089748-5528-2-git-send-email-peter.maydell@linaro.org --- hw/intc/armv7m_nvic.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index e13b729..3ec8408 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -185,26 +185,25 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) return cpu->midr; case 0xd04: /* Interrupt Control State. */ /* VECTACTIVE */ - val = s->gic.running_irq[0]; + cpu = ARM_CPU(current_cpu); + val = cpu->env.v7m.exception; if (val == 1023) { val = 0; } else if (val >= 32) { val -= 16; } - /* RETTOBASE */ - if (s->gic.running_irq[0] == 1023 - || s->gic.last_active[s->gic.running_irq[0]][0] == 1023) { - val |= (1 << 11); - } /* VECTPENDING */ if (s->gic.current_pending[0] != 1023) val |= (s->gic.current_pending[0] << 12); - /* ISRPENDING */ + /* ISRPENDING and RETTOBASE */ for (irq = 32; irq < s->num_irq; irq++) { if (s->gic.irq_state[irq].pending) { val |= (1 << 22); break; } + if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) { + val |= (1 << 11); + } } /* PENDSTSET */ if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending) -- cgit v1.1 From df92cfa60eef82dad112ca5c5d0239ec5ba7aac3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 8 Sep 2015 17:38:42 +0100 Subject: hw/intc/arm_gic: Running priority is group priority, not full priority Priority values for the GIC are divided into a "group priority" and a "subpriority" (with the division being determined by the binary point register). The running priority is only determined by the group priority of the active interrupts, not the subpriority. In particular, this means that there can't be more than one active interrupt at any particular group priority. Signed-off-by: Peter Maydell Message-id: 1438089748-5528-3-git-send-email-peter.maydell@linaro.org --- hw/intc/arm_gic.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'hw/intc') diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index a8c5d19..1b8e839 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -219,13 +219,39 @@ static uint16_t gic_get_current_pending_irq(GICState *s, int cpu, return pending_irq; } +static int gic_get_group_priority(GICState *s, int cpu, int irq) +{ + /* Return the group priority of the specified interrupt + * (which is the top bits of its priority, with the number + * of bits masked determined by the applicable binary point register). + */ + int bpr; + uint32_t mask; + + if (gic_has_groups(s) && + !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) && + GIC_TEST_GROUP(irq, (1 << cpu))) { + bpr = s->abpr[cpu]; + } else { + bpr = s->bpr[cpu]; + } + + /* a BPR of 0 means the group priority bits are [7:1]; + * a BPR of 1 means they are [7:2], and so on down to + * a BPR of 7 meaning no group priority bits at all. + */ + mask = ~0U << ((bpr & 7) + 1); + + return GIC_GET_PRIORITY(irq, cpu) & mask; +} + static void gic_set_running_irq(GICState *s, int cpu, int irq) { s->running_irq[cpu] = irq; if (irq == 1023) { s->running_priority[cpu] = 0x100; } else { - s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu); + s->running_priority[cpu] = gic_get_group_priority(s, cpu, irq); } gic_update(s); } -- cgit v1.1 From 51fd06e0eee8257fdcc147200796e362cf2298ea Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 8 Sep 2015 17:38:42 +0100 Subject: hw/intc/arm_gic: Fix handling of GICC_APR, GICC_NSAPR registers A GICv2 has both GICC_APR and GICC_NSAPR registers, with the latter holding the active priority bits for Group 1 interrupts (usually Nonsecure interrupts), and the Nonsecure view of the GICC_APR is the second half of the GICC_NSAPR registers. Turn our half-hearted implementation of APR into a proper implementation of both APR and NSAPR: * Add the underlying state for NSAPR * Make sure APR aren't visible for pre-GICv2 * Implement reading of NSAPR * Make non-secure reads of APR behave correctly * Implement writing to APR and NSAPR Signed-off-by: Peter Maydell Message-id: 1438089748-5528-4-git-send-email-peter.maydell@linaro.org --- hw/intc/arm_gic.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++- hw/intc/arm_gic_common.c | 5 ++- 2 files changed, 115 insertions(+), 4 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 1b8e839..34f781f 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -948,6 +948,68 @@ static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data, } } +static inline uint32_t gic_apr_ns_view(GICState *s, int cpu, int regno) +{ + /* Return the Nonsecure view of GICC_APR. This is the + * second half of GICC_NSAPR. + */ + switch (GIC_MIN_BPR) { + case 0: + if (regno < 2) { + return s->nsapr[regno + 2][cpu]; + } + break; + case 1: + if (regno == 0) { + return s->nsapr[regno + 1][cpu]; + } + break; + case 2: + if (regno == 0) { + return extract32(s->nsapr[0][cpu], 16, 16); + } + break; + case 3: + if (regno == 0) { + return extract32(s->nsapr[0][cpu], 8, 8); + } + break; + default: + g_assert_not_reached(); + } + return 0; +} + +static inline void gic_apr_write_ns_view(GICState *s, int cpu, int regno, + uint32_t value) +{ + /* Write the Nonsecure view of GICC_APR. */ + switch (GIC_MIN_BPR) { + case 0: + if (regno < 2) { + s->nsapr[regno + 2][cpu] = value; + } + break; + case 1: + if (regno == 0) { + s->nsapr[regno + 1][cpu] = value; + } + break; + case 2: + if (regno == 0) { + s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 16, 16, value); + } + break; + case 3: + if (regno == 0) { + s->nsapr[0][cpu] = deposit32(s->nsapr[0][cpu], 8, 8, value); + } + break; + default: + g_assert_not_reached(); + } +} + static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, uint64_t *data, MemTxAttrs attrs) { @@ -988,8 +1050,31 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, } break; case 0xd0: case 0xd4: case 0xd8: case 0xdc: - *data = s->apr[(offset - 0xd0) / 4][cpu]; + { + int regno = (offset - 0xd0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + *data = 0; + } else if (s->security_extn && !attrs.secure) { + /* NS view of GICC_APR is the top half of GIC_NSAPR */ + *data = gic_apr_ns_view(s, regno, cpu); + } else { + *data = s->apr[regno][cpu]; + } + break; + } + case 0xe0: case 0xe4: case 0xe8: case 0xec: + { + int regno = (offset - 0xe0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) || + (s->security_extn && !attrs.secure)) { + *data = 0; + } else { + *data = s->nsapr[regno][cpu]; + } break; + } default: qemu_log_mask(LOG_GUEST_ERROR, "gic_cpu_read: Bad offset %x\n", (int)offset); @@ -1027,8 +1112,33 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, } break; case 0xd0: case 0xd4: case 0xd8: case 0xdc: - qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n"); + { + int regno = (offset - 0xd0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + return MEMTX_OK; + } + if (s->security_extn && !attrs.secure) { + /* NS view of GICC_APR is the top half of GIC_NSAPR */ + gic_apr_write_ns_view(s, regno, cpu, value); + } else { + s->apr[regno][cpu] = value; + } break; + } + case 0xe0: case 0xe4: case 0xe8: case 0xec: + { + int regno = (offset - 0xe0) / 4; + + if (regno >= GIC_NR_APRS || s->revision != 2) { + return MEMTX_OK; + } + if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { + return MEMTX_OK; + } + s->nsapr[regno][cpu] = value; + break; + } default: qemu_log_mask(LOG_GUEST_ERROR, "gic_cpu_write: Bad offset %x\n", (int)offset); diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index fe64b51..43c103e 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -59,8 +59,8 @@ static const VMStateDescription vmstate_gic_irq_state = { static const VMStateDescription vmstate_gic = { .name = "arm_gic", - .version_id = 10, - .minimum_version_id = 10, + .version_id = 11, + .minimum_version_id = 11, .pre_save = gic_pre_save, .post_load = gic_post_load, .fields = (VMStateField[]) { @@ -80,6 +80,7 @@ static const VMStateDescription vmstate_gic = { VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU), VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU), VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU), + VMSTATE_UINT32_2DARRAY(nsapr, GICState, GIC_NR_APRS, GIC_NCPU), VMSTATE_END_OF_LIST() } }; -- cgit v1.1 From 72889c8a809f4c65796b98d5af6a18c92510ed86 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 8 Sep 2015 17:38:42 +0100 Subject: hw/intc/arm_gic: Drop running_irq and last_active arrays The running_irq and last_active arrays represent state which doesn't exist in a real hardware GIC. The only thing we use them for is updating the running priority when an interrupt is completed, but in fact we can use the active-priority registers to do this. The running priority is always the priority corresponding to the lowest set bit in the active priority registers, because only one interrupt at any particular priority can be active at once. Signed-off-by: Peter Maydell Message-id: 1438089748-5528-5-git-send-email-peter.maydell@linaro.org --- hw/intc/arm_gic.c | 103 ++++++++++++++++++++++++++++++++++------------- hw/intc/arm_gic_common.c | 7 +--- 2 files changed, 76 insertions(+), 34 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 34f781f..9daa8cd 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -245,15 +245,72 @@ static int gic_get_group_priority(GICState *s, int cpu, int irq) return GIC_GET_PRIORITY(irq, cpu) & mask; } -static void gic_set_running_irq(GICState *s, int cpu, int irq) +static void gic_activate_irq(GICState *s, int cpu, int irq) { - s->running_irq[cpu] = irq; - if (irq == 1023) { - s->running_priority[cpu] = 0x100; + /* Set the appropriate Active Priority Register bit for this IRQ, + * and update the running priority. + */ + int prio = gic_get_group_priority(s, cpu, irq); + int preemption_level = prio >> (GIC_MIN_BPR + 1); + int regno = preemption_level / 32; + int bitno = preemption_level % 32; + + if (gic_has_groups(s) && GIC_TEST_GROUP(irq, (1 << cpu))) { + s->nsapr[regno][cpu] &= (1 << bitno); } else { - s->running_priority[cpu] = gic_get_group_priority(s, cpu, irq); + s->apr[regno][cpu] &= (1 << bitno); } - gic_update(s); + + s->running_priority[cpu] = prio; +} + +static int gic_get_prio_from_apr_bits(GICState *s, int cpu) +{ + /* Recalculate the current running priority for this CPU based + * on the set bits in the Active Priority Registers. + */ + int i; + for (i = 0; i < GIC_NR_APRS; i++) { + uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu]; + if (!apr) { + continue; + } + return (i * 32 + ctz32(apr)) << (GIC_MIN_BPR + 1); + } + return 0x100; +} + +static void gic_drop_prio(GICState *s, int cpu, int group) +{ + /* Drop the priority of the currently active interrupt in the + * specified group. + * + * Note that we can guarantee (because of the requirement to nest + * GICC_IAR reads [which activate an interrupt and raise priority] + * with GICC_EOIR writes [which drop the priority for the interrupt]) + * that the interrupt we're being called for is the highest priority + * active interrupt, meaning that it has the lowest set bit in the + * APR registers. + * + * If the guest does not honour the ordering constraints then the + * behaviour of the GIC is UNPREDICTABLE, which for us means that + * the values of the APR registers might become incorrect and the + * running priority will be wrong, so interrupts that should preempt + * might not do so, and interrupts that should not preempt might do so. + */ + int i; + + for (i = 0; i < GIC_NR_APRS; i++) { + uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu]; + if (!*papr) { + continue; + } + /* Clear lowest set bit */ + *papr &= *papr - 1; + break; + } + + s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu); } uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) @@ -276,7 +333,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq); return 1023; } - s->last_active[irq][cpu] = s->running_irq[cpu]; if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { /* Clear pending flags for both level and edge triggered interrupts. @@ -307,7 +363,8 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) } } - gic_set_running_irq(s, cpu, irq); + gic_activate_irq(s, cpu, irq); + gic_update(s); DPRINTF("ACK %d\n", irq); return ret; } @@ -437,8 +494,9 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs) void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { - int update = 0; int cm = 1 << cpu; + int group; + DPRINTF("EOI %d\n", irq); if (irq >= s->num_irq) { /* This handles two cases: @@ -451,8 +509,9 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) */ return; } - if (s->running_irq[cpu] == 1023) + if (s->running_priority[cpu] == 0x100) { return; /* No active IRQ. */ + } if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { /* Mark level triggered interrupts as pending if they are still @@ -461,11 +520,12 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { DPRINTF("Set %d pending mask %x\n", irq, cm); GIC_SET_PENDING(irq, cm); - update = 1; } } - if (s->security_extn && !attrs.secure && !GIC_TEST_GROUP(irq, cm)) { + group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + + if (s->security_extn && !attrs.secure && !group) { DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq); return; } @@ -475,23 +535,8 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) * i.e. go ahead and complete the irq anyway. */ - if (irq != s->running_irq[cpu]) { - /* Complete an IRQ that is not currently running. */ - int tmp = s->running_irq[cpu]; - while (s->last_active[tmp][cpu] != 1023) { - if (s->last_active[tmp][cpu] == irq) { - s->last_active[tmp][cpu] = s->last_active[irq][cpu]; - break; - } - tmp = s->last_active[tmp][cpu]; - } - if (update) { - gic_update(s); - } - } else { - /* Complete the current running IRQ. */ - gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]); - } + gic_drop_prio(s, cpu, group); + gic_update(s); } static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 43c103e..aa0e7d8 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -59,8 +59,8 @@ static const VMStateDescription vmstate_gic_irq_state = { static const VMStateDescription vmstate_gic = { .name = "arm_gic", - .version_id = 11, - .minimum_version_id = 11, + .version_id = 12, + .minimum_version_id = 12, .pre_save = gic_pre_save, .post_load = gic_post_load, .fields = (VMStateField[]) { @@ -71,10 +71,8 @@ static const VMStateDescription vmstate_gic = { VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ), VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU), VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL), - VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU), VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU), VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU), - VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU), VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU), VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU), VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU), @@ -174,7 +172,6 @@ static void arm_gic_common_reset(DeviceState *dev) s->priority_mask[i] = 0; } s->current_pending[i] = 1023; - s->running_irq[i] = 1023; s->running_priority[i] = 0x100; s->cpu_ctlr[i] = 0; s->bpr[i] = GIC_MIN_BPR; -- cgit v1.1 From d5523a13656fb8df902a15a9fd8bd652b85e97e0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 8 Sep 2015 17:38:43 +0100 Subject: hw/intc/arm_gic: Actually set the active bits for active interrupts Although we were correctly handling interrupts becoming active and then inactive, we weren't actually exposing this to the guest by setting the 'active' flag for the interrupt, so reads of GICD_ICACTIVERn and GICD_ISACTIVERn would generally incorrectly return zeroes. Correct this oversight. Signed-off-by: Peter Maydell Message-id: 1438089748-5528-6-git-send-email-peter.maydell@linaro.org --- hw/intc/arm_gic.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'hw/intc') diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 9daa8cd..2df550c 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -262,6 +262,7 @@ static void gic_activate_irq(GICState *s, int cpu, int irq) } s->running_priority[cpu] = prio; + GIC_SET_ACTIVE(irq, 1 << cpu); } static int gic_get_prio_from_apr_bits(GICState *s, int cpu) @@ -536,6 +537,7 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) */ gic_drop_prio(s, cpu, group); + GIC_CLEAR_ACTIVE(irq, cm); gic_update(s); } -- cgit v1.1 From 8ff41f3995ad2d942ecafb72519c1f09cb811259 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 8 Sep 2015 17:38:43 +0100 Subject: hw/intc/arm_gic_common: Configure IRQs as NS if doing direct NS kernel boot If we directly boot a kernel in NonSecure on a system where the GIC supports the security extensions then we must cause the GIC to configure its interrupts into group 1 (NonSecure) rather than the usual group 0, and with their initial priority set to the highest NonSecure priority rather than the usual highest Secure priority. Otherwise the guest kernel will be unable to use any interrupts. Implement this behaviour, controlled by a flag which we set if appropriate when the ARM bootloader code calls our ARMLinuxBootIf interface callback. Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite Reviewed-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Tested-by: Edgar E. Iglesias Message-id: 1441383782-24378-4-git-send-email-peter.maydell@linaro.org --- hw/intc/arm_gic_common.c | 51 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) (limited to 'hw/intc') diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index aa0e7d8..9c82b97 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -19,6 +19,7 @@ */ #include "gic_internal.h" +#include "hw/arm/linux-boot-if.h" static void gic_pre_save(void *opaque) { @@ -164,12 +165,27 @@ static void arm_gic_common_reset(DeviceState *dev) { GICState *s = ARM_GIC_COMMON(dev); int i, j; + int resetprio; + + /* If we're resetting a TZ-aware GIC as if secure firmware + * had set it up ready to start a kernel in non-secure, + * we need to set interrupt priorities to a "zero for the + * NS view" value. This is particularly critical for the + * priority_mask[] values, because if they are zero then NS + * code cannot ever rewrite the priority to anything else. + */ + if (s->security_extn && s->irq_reset_nonsecure) { + resetprio = 0x80; + } else { + resetprio = 0; + } + memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); for (i = 0 ; i < s->num_cpu; i++) { if (s->revision == REV_11MPCORE) { s->priority_mask[i] = 0xf0; } else { - s->priority_mask[i] = 0; + s->priority_mask[i] = resetprio; } s->current_pending[i] = 1023; s->running_priority[i] = 0x100; @@ -177,7 +193,7 @@ static void arm_gic_common_reset(DeviceState *dev) s->bpr[i] = GIC_MIN_BPR; s->abpr[i] = GIC_MIN_ABPR; for (j = 0; j < GIC_INTERNAL; j++) { - s->priority1[j][i] = 0; + s->priority1[j][i] = resetprio; } for (j = 0; j < GIC_NR_SGIS; j++) { s->sgi_pending[j][i] = 0; @@ -189,7 +205,7 @@ static void arm_gic_common_reset(DeviceState *dev) } for (i = 0; i < ARRAY_SIZE(s->priority2); i++) { - s->priority2[i] = 0; + s->priority2[i] = resetprio; } for (i = 0; i < GIC_MAXIRQ; i++) { @@ -200,9 +216,32 @@ static void arm_gic_common_reset(DeviceState *dev) s->irq_target[i] = 0; } } + if (s->security_extn && s->irq_reset_nonsecure) { + for (i = 0; i < GIC_MAXIRQ; i++) { + GIC_SET_GROUP(i, ALL_CPU_MASK); + } + } + s->ctlr = 0; } +static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, + bool secure_boot) +{ + GICState *s = ARM_GIC_COMMON(obj); + + if (s->security_extn && !secure_boot) { + /* We're directly booting a kernel into NonSecure. If this GIC + * implements the security extensions then we must configure it + * to have all the interrupts be NonSecure (this is a job that + * is done by the Secure boot firmware in real hardware, and in + * this mode QEMU is acting as a minimalist firmware-and-bootloader + * equivalent). + */ + s->irq_reset_nonsecure = true; + } +} + static Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1), DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32), @@ -219,11 +258,13 @@ static Property arm_gic_common_properties[] = { static void arm_gic_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass); dc->reset = arm_gic_common_reset; dc->realize = arm_gic_common_realize; dc->props = arm_gic_common_properties; dc->vmsd = &vmstate_gic; + albifc->arm_linux_init = arm_gic_common_linux_init; } static const TypeInfo arm_gic_common_type = { @@ -233,6 +274,10 @@ static const TypeInfo arm_gic_common_type = { .class_size = sizeof(ARMGICCommonClass), .class_init = arm_gic_common_class_init, .abstract = true, + .interfaces = (InterfaceInfo []) { + { TYPE_ARM_LINUX_BOOT_IF }, + { }, + }, }; static void register_types(void) -- cgit v1.1