[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PULL 15/29] arm_gic: Fix GIC pending behavior
From: |
Peter Maydell |
Subject: |
[Qemu-devel] [PULL 15/29] arm_gic: Fix GIC pending behavior |
Date: |
Sat, 8 Feb 2014 15:57:50 +0000 |
From: Christoffer Dall <address@hidden>
The existing implementation of the pending behavior in gic_set_irq,
gic_complete_irq, and the distributor pending set/clear registers does
not follow the semantics of the GICv2.0 specs, but may implement the
11MPCore support. Therefore, maintain the existing semantics for
11MPCore and v7M NVIC and change the behavior to be in accordance with
the GICv2.0 specs for "generic implementations" (s->revision == 1 ||
s->revision == 2).
Generic implementations distinguish between setting a level-triggered
interrupt pending through writes to the GICD_ISPENDR and when hardware
raises the interrupt line. Writing to the GICD_ICPENDR will not cause
the interrupt to become non-pending if the line is still active, and
conversely, if the line is deactivated but the interrupt is marked as
pending through a write to GICD_ISPENDR, the interrupt remains pending.
Handle this situation in the GIC_TEST_PENDING (which now becomes a
static inline named gic_test_pending) and let the 'pending' field
correspond only to the latched state of the D-flip flop in the GICv2.0
specs Figure 4-10.
The following changes are added:
gic_test_pending:
Make this a static inline and split out the 11MPCore from the generic
behavior. For the generic behavior, consider interrupts pending if:
((s->irq_state[irq].pending & (cm) != 0) ||
(!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm))
gic_set_irq:
Split out the 11MPCore from the generic behavior. For the generic
behavior, always GIC_SET_LEVEL() on positive level, but only
GIC_SET_PENDING for edge-triggered interrupts and always simply
GIC_CLEAR_LEVEL() on negative level.
gic_complete_irq:
Only resample the line for line-triggered interrupts on an 11MPCore.
Generic implementations will sample the line directly in
gic_test_pending().
Signed-off-by: Christoffer Dall <address@hidden>
Reviewed-by: Peter Maydell <address@hidden>
Signed-off-by: Peter Maydell <address@hidden>
---
hw/intc/arm_gic.c | 64 ++++++++++++++++++++++++++++++++++++--------------
hw/intc/gic_internal.h | 16 ++++++++++++-
2 files changed, 62 insertions(+), 18 deletions(-)
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 1c4a114..77519c0 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -66,7 +66,7 @@ void gic_update(GICState *s)
best_prio = 0x100;
best_irq = 1023;
for (irq = 0; irq < s->num_irq; irq++) {
- if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) {
+ 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;
@@ -89,14 +89,43 @@ void gic_set_pending_private(GICState *s, int cpu, int irq)
{
int cm = 1 << cpu;
- if (GIC_TEST_PENDING(irq, cm))
+ if (gic_test_pending(s, irq, cm)) {
return;
+ }
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
GIC_SET_PENDING(irq, cm);
gic_update(s);
}
+static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
+ int cm, int target)
+{
+ if (level) {
+ GIC_SET_LEVEL(irq, cm);
+ if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
+ DPRINTF("Set %d pending mask %x\n", irq, target);
+ GIC_SET_PENDING(irq, target);
+ }
+ } else {
+ GIC_CLEAR_LEVEL(irq, cm);
+ }
+}
+
+static void gic_set_irq_generic(GICState *s, int irq, int level,
+ int cm, int target)
+{
+ if (level) {
+ GIC_SET_LEVEL(irq, cm);
+ DPRINTF("Set %d pending mask %x\n", irq, target);
+ if (GIC_TEST_EDGE_TRIGGER(irq)) {
+ GIC_SET_PENDING(irq, target);
+ }
+ } else {
+ GIC_CLEAR_LEVEL(irq, cm);
+ }
+}
+
/* Process a change in an external IRQ input. */
static void gic_set_irq(void *opaque, int irq, int level)
{
@@ -126,15 +155,12 @@ static void gic_set_irq(void *opaque, int irq, int level)
return;
}
- if (level) {
- GIC_SET_LEVEL(irq, cm);
- if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
- DPRINTF("Set %d pending mask %x\n", irq, target);
- GIC_SET_PENDING(irq, target);
- }
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
+ gic_set_irq_11mpcore(s, irq, level, cm, target);
} else {
- GIC_CLEAR_LEVEL(irq, cm);
+ gic_set_irq_generic(s, irq, level, cm, target);
}
+
gic_update(s);
}
@@ -195,14 +221,18 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
}
if (s->running_irq[cpu] == 1023)
return; /* No active IRQ. */
- /* Mark level triggered interrupts as pending if they are still
- raised. */
- if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
- && 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->revision == REV_11MPCORE || s->revision == REV_NVIC) {
+ /* Mark level triggered interrupts as pending if they are still
+ raised. */
+ if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
+ && 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 (irq != s->running_irq[cpu]) {
/* Complete an IRQ that is not currently running. */
int tmp = s->running_irq[cpu];
@@ -273,7 +303,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
res = 0;
mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
for (i = 0; i < 8; i++) {
- if (GIC_TEST_PENDING(irq + i, mask)) {
+ if (gic_test_pending(s, irq + i, mask)) {
res |= (1 << i);
}
}
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 8c02d58..92a6f7a 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -34,7 +34,6 @@
#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0)
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
-#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
@@ -63,4 +62,19 @@ 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);
+static inline bool gic_test_pending(GICState *s, int irq, int cm)
+{
+ if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) {
+ return s->irq_state[irq].pending & cm;
+ } else {
+ /* Edge-triggered interrupts are marked pending on a rising edge, but
+ * level-triggered interrupts are either considered pending when the
+ * level is active or if software has explicitly written to
+ * GICD_ISPENDR to set the state pending.
+ */
+ return (s->irq_state[irq].pending & cm) ||
+ (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm));
+ }
+}
+
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
--
1.8.5
- [Qemu-devel] [PULL 28/29] hw/arm/allwinner-a10: initialize EMAC, (continued)
- [Qemu-devel] [PULL 28/29] hw/arm/allwinner-a10: initialize EMAC, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 05/29] target-arm: A64: Implement scalar pairwise ops, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 17/29] arm_gic: Support setting/getting binary point reg, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 26/29] util/fifo8: clear fifo head upon reset, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 23/29] disas/libvixl: Fix upstream libvixl compilation issues, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 09/29] target-arm: A64: Implement 2-register misc compares, ABS, NEG, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 21/29] rules.mak: Link with C++ if we have a C++ compiler, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 13/29] target-arm: A64: Add FNEG and FABS to the SIMD 2-reg-misc group, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 08/29] target-arm: A64: Add skeleton decode for SIMD 2-reg misc group, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 02/29] target-arm: A64: Implement remaining non-pairwise int SIMD 3-reg-same insns, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 15/29] arm_gic: Fix GIC pending behavior,
Peter Maydell <=
- [Qemu-devel] [PULL 27/29] hw/net: add support for Allwinner EMAC Fast Ethernet controller, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 20/29] rules.mak: Support .cc as a C++ source file suffix, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 03/29] target-arm: A64: Implement pairwise integer ops from 3-reg-same SIMD, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 14/29] target-arm: Add support for AArch32 64bit VCVTB and VCVTT, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 18/29] vmstate: Add uint32 2D-array support, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 24/29] disas: Implement disassembly output for A64, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 04/29] tcg: Add TCGV_UNUSED_PTR, TCGV_IS_UNUSED_PTR, TCGV_EQUAL_PTR, Peter Maydell, 2014/02/08
- [Qemu-devel] [PULL 22/29] disas: Add subset of libvixl sources for A64 disassembler, Peter Maydell, 2014/02/08
- Re: [Qemu-devel] [PULL 00/29] target-arm queue, Peter Maydell, 2014/02/11