From: Jan Klötzke <jan.kloet...@kernkonzept.com> 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-sta...@nongnu.org Fixes: 8d999995e45c10 ("arm_gic: Fix GIC pending behavior") Signed-off-by: Jan Klötzke <jan.kloet...@kernkonzept.com> Message-id: 20240911114826.3558302-1-jan.kloet...@kernkonzept.com Reviewed-by: Peter Maydell <peter.mayd...@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.mayd...@linaro.org> (cherry picked from commit 110684c9a69a02cbabfbddcd3afa921826ad565c) Signed-off-by: Michael Tokarev <m...@tls.msk.ru> diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index e4b8437f8b..f0582f7a49 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.39.5