83 lines
3.9 KiB
Diff
83 lines
3.9 KiB
Diff
|
|
From 0cb9a00d295cbf0ade0a55cea1039aec793fddf0 Mon Sep 17 00:00:00 2001
|
||
|
|
From: =?UTF-8?q?Jan=20Kl=C3=B6tzke?= <jan.kloetzke@kernkonzept.com>
|
||
|
|
Date: Fri, 13 Sep 2024 15:31:50 +0100
|
||
|
|
Subject: [PATCH] hw/intc/arm_gic: fix spurious level triggered interrupts
|
||
|
|
MIME-Version: 1.0
|
||
|
|
Content-Type: text/plain; charset=UTF-8
|
||
|
|
Content-Transfer-Encoding: 8bit
|
||
|
|
|
||
|
|
On GICv2 and later, level triggered interrupts are pending when either
|
||
|
|
the interrupt line is asserted or the interrupt was made pending by a
|
||
|
|
GICD_ISPENDRn write. Making a level triggered interrupt pending by
|
||
|
|
software persists until either the interrupt is acknowledged or cleared
|
||
|
|
by writing GICD_ICPENDRn. As long as the interrupt line is asserted,
|
||
|
|
the interrupt is pending in any case.
|
||
|
|
|
||
|
|
This logic is transparently implemented in gic_test_pending() for
|
||
|
|
GICv1 and GICv2. The function combines the "pending" irq_state flag
|
||
|
|
(used for edge triggered interrupts and software requests) and the
|
||
|
|
line status (tracked in the "level" field). However, we also
|
||
|
|
incorrectly set the pending flag on a guest write to GICD_ISENABLERn
|
||
|
|
if the line of a level triggered interrupt was asserted. This keeps
|
||
|
|
the interrupt pending even if the line is de-asserted after some
|
||
|
|
time.
|
||
|
|
|
||
|
|
This incorrect logic is a leftover of the initial 11MPCore GIC
|
||
|
|
implementation. That handles things slightly differently to the
|
||
|
|
architected GICv1 and GICv2. The 11MPCore TRM does not give a lot of
|
||
|
|
detail on the corner cases of its GIC's behaviour, and historically
|
||
|
|
we have not wanted to investigate exactly what it does in reality, so
|
||
|
|
QEMU's GIC model takes the approach of "retain our existing behaviour
|
||
|
|
for 11MPCore, and implement the architectural standard for later GIC
|
||
|
|
revisions".
|
||
|
|
|
||
|
|
On that basis, commit 8d999995e45c10 in 2013 is where we added the
|
||
|
|
"level-triggered interrupt with the line asserted" handling to
|
||
|
|
gic_test_pending(), and we deliberately kept the old behaviour of
|
||
|
|
gic_test_pending() for REV_11MPCORE. That commit should have added
|
||
|
|
the "only if 11MPCore" condition to the setting of the pending bit on
|
||
|
|
writes to GICD_ISENABLERn, but forgot it.
|
||
|
|
|
||
|
|
Add the missing "if REV_11MPCORE" condition, so that our behaviour
|
||
|
|
on GICv1 and GICv2 matches the GIC architecture requirements.
|
||
|
|
|
||
|
|
Cc: qemu-stable@nongnu.org
|
||
|
|
Fixes: 8d999995e45c10 ("arm_gic: Fix GIC pending behavior")
|
||
|
|
Signed-off-by: Jan Klötzke <jan.kloetzke@kernkonzept.com>
|
||
|
|
Message-id: 20240911114826.3558302-1-jan.kloetzke@kernkonzept.com
|
||
|
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||
|
|
[PMM: expanded comment a little and converted to coding-style form;
|
||
|
|
expanded commit message with the historical backstory]
|
||
|
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||
|
|
(cherry picked from commit 110684c9a69a02cbabfbddcd3afa921826ad565c)
|
||
|
|
Signed-off-by: zhujun2 <zhujun2_yewu@cmss.chinamobile.com>
|
||
|
|
---
|
||
|
|
hw/intc/arm_gic.c | 11 ++++++++---
|
||
|
|
1 file changed, 8 insertions(+), 3 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||
|
|
index 074cf50af2..dfe7a0a729 100644
|
||
|
|
--- a/hw/intc/arm_gic.c
|
||
|
|
+++ b/hw/intc/arm_gic.c
|
||
|
|
@@ -1263,9 +1263,14 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||
|
|
trace_gic_enable_irq(irq + i);
|
||
|
|
}
|
||
|
|
GIC_DIST_SET_ENABLED(irq + i, cm);
|
||
|
|
- /* If a raised level triggered IRQ enabled then mark
|
||
|
|
- is as pending. */
|
||
|
|
- if (GIC_DIST_TEST_LEVEL(irq + i, mask)
|
||
|
|
+ /*
|
||
|
|
+ * If a raised level triggered IRQ enabled then mark
|
||
|
|
+ * it as pending on 11MPCore. For other GIC revisions we
|
||
|
|
+ * handle the "level triggered and line asserted" check
|
||
|
|
+ * at the other end in gic_test_pending().
|
||
|
|
+ */
|
||
|
|
+ if (s->revision == REV_11MPCORE
|
||
|
|
+ && GIC_DIST_TEST_LEVEL(irq + i, mask)
|
||
|
|
&& !GIC_DIST_TEST_EDGE_TRIGGER(irq + i)) {
|
||
|
|
DPRINTF("Set %d pending mask %x\n", irq + i, mask);
|
||
|
|
GIC_DIST_SET_PENDING(irq + i, mask);
|
||
|
|
--
|
||
|
|
2.41.0.windows.1
|
||
|
|
|