]> git.proxmox.com Git - mirror_qemu.git/commitdiff
intc/ibex_plic: Ensure we don't loose interrupts
authorAlistair Francis <alistair.francis@wdc.com>
Wed, 11 Nov 2020 23:13:24 +0000 (15:13 -0800)
committerAlistair Francis <alistair.francis@wdc.com>
Sat, 14 Nov 2020 05:43:48 +0000 (21:43 -0800)
If an interrupt occurs between when we claim and complete an interrupt
we currently drop the interrupt in ibex_plic_irqs_set_pending(). This
somewhat matches hardware that also ignore the interrupt between the
claim and complete process.

In the case of hardware though the physical interrupt line will still
be asserted after we have completed the interrupt. This means we will
still act on the interrupt after the complete process. In QEMU we don't
and instead we drop the interrupt as it is never recorded.

This patch changed the behaviour of the Ibex PLIC so that we save all
interrupts that occur while we are between claiming and completing an
interrupt so that we can act on them after the completition process.

This fixes interrupts being dropped when running Tock on OpenTitain in
QEMU.

Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Message-id: e7bcf98c6925b1e6e7828e7c3f85293a09a65b12.1605136387.git.alistair.francis@wdc.com

hw/intc/ibex_plic.c
include/hw/intc/ibex_plic.h

index db9e0aa25f2afc126eaa5ab1e206a1c7a1c8f60a..341c9db405575e60d7d090d5bb77324af8e7e415 100644 (file)
@@ -48,6 +48,7 @@ static void ibex_plic_irqs_set_pending(IbexPlicState *s, int irq, bool level)
          * The interrupt has been claimed, but not completed.
          * The pending bit can't be set.
          */
+        s->hidden_pending[pending_num] |= level << (irq % 32);
         return;
     }
 
@@ -176,8 +177,21 @@ static void ibex_plic_write(void *opaque, hwaddr addr,
             s->claim = 0;
         }
         if (s->claimed[value / 32] & 1 << (value % 32)) {
+            int pending_num = value / 32;
+
             /* This value was already claimed, clear it. */
-            s->claimed[value / 32] &= ~(1 << (value % 32));
+            s->claimed[pending_num] &= ~(1 << (value % 32));
+
+            if (s->hidden_pending[pending_num] & (1 << (value % 32))) {
+                /*
+                 * If the bit in hidden_pending is set then that means we
+                 * received an interrupt between claiming and completing
+                 * the interrupt that hasn't since been de-asserted.
+                 * On hardware this would trigger an interrupt, so let's
+                 * trigger one here as well.
+                 */
+                s->pending[pending_num] |= 1 << (value % 32);
+            }
         }
     }
 
@@ -239,6 +253,7 @@ static void ibex_plic_realize(DeviceState *dev, Error **errp)
     int i;
 
     s->pending = g_new0(uint32_t, s->pending_num);
+    s->hidden_pending = g_new0(uint32_t, s->pending_num);
     s->claimed = g_new0(uint32_t, s->pending_num);
     s->source = g_new0(uint32_t, s->source_num);
     s->priority = g_new0(uint32_t, s->priority_num);
index 37f03356b3b4a7e510f3ba0f6704e02058a4e5b0..7fc495db992ed8743de6165c990814bcc8bb1ae0 100644 (file)
@@ -33,6 +33,7 @@ struct IbexPlicState {
     MemoryRegion mmio;
 
     uint32_t *pending;
+    uint32_t *hidden_pending;
     uint32_t *claimed;
     uint32_t *source;
     uint32_t *priority;