diff mbox

[RFC,v4,3/8] arm_gic: Fix GIC pending behavior

Message ID 1387606179-22709-4-git-send-email-christoffer.dall@linaro.org
State New
Headers show

Commit Message

Christoffer Dall Dec. 21, 2013, 6:09 a.m. UTC
The existing implementation of the pending behavior in gic_set_irq,
gic_acknowledge_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 change the behavior to be in
accordance with the GICv2.0 specs for "generic implementations"
(s->revision == 1 || s->revision == 2).

Generic implementations also mandate that level-triggered interrupts
that are pending due to a write to GICD_ISPENDR stay pending until the
interrupt is acknowledged or until there's a write to the GICD_ICPENDR.
Similarly, only clear the pending state on writes to GICD_ICPENDR if the
interrupt signal is not asserted.  This requires an extra state variable
to keep track of writes to the set/clear registers, corresponding to the
D-flip flop in the GICv2.0 specs Figure 4-10.

The following changes are added:

gic_set_irq:
Split out the 11MPCore from the generic behavior.  For the generic
behavior, simply set pending state on positive level and edge triggered
interrupts and clear the pending state only for level triggered
interrupts.

gic_acknowledge_irq:
Keep existing functionality for 11MPCore, but only clear the pending
state if the interrupt is edge-triggered or if the interrupt is
non-asserted and level-triggered.

gic_complete_irq:
Only resample the line for line-triggered interrupts on an 11MPCore.
Generic implementations will latch the pending state when the level
changes, on reads of GICC_IAR, and on wrtites to GICD_I{SC}PENDRn.

gic_dist_writeb:
1. Fix bugs to ensure that writes to GICD_ICPENDR and GICD_ISPENDR are
   ignored for SGIs (common behavior for 11MPCore and generic
   implementations).
2. Do not clear the pending state for level-triggered interrupts on
   writes to GICD_ICPENDR if the interrupt signal remains asserted.

Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
Changes [v3 -> v4]:
 - Maintain 11MPCore semantics
 - Combine all pending interrupts fixing patches into this patch.  See
   the detailed description above.

Changes [v1 -> v2]:
 - Fix bisection issue, by not using gic_clear_pending yet.

 hw/intc/arm_gic.c                | 106 ++++++++++++++++++++++++++++++---------
 hw/intc/arm_gic_common.c         |   5 +-
 hw/intc/gic_internal.h           |   3 ++
 include/hw/intc/arm_gic_common.h |   2 +
 4 files changed, 91 insertions(+), 25 deletions(-)

Comments

Peter Maydell Jan. 19, 2014, 7:49 p.m. UTC | #1
On 21 December 2013 06:09, Christoffer Dall <christoffer.dall@linaro.org> wrote:
> The existing implementation of the pending behavior in gic_set_irq,
> gic_acknowledge_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 change the behavior to be in
> accordance with the GICv2.0 specs for "generic implementations"
> (s->revision == 1 || s->revision == 2).
>
> Generic implementations also mandate that level-triggered interrupts
> that are pending due to a write to GICD_ISPENDR stay pending until the
> interrupt is acknowledged or until there's a write to the GICD_ICPENDR.
> Similarly, only clear the pending state on writes to GICD_ICPENDR if the
> interrupt signal is not asserted.  This requires an extra state variable
> to keep track of writes to the set/clear registers, corresponding to the
> D-flip flop in the GICv2.0 specs Figure 4-10.

So this means we now have two lots of pending state, the pending
and sw_pending fields. I think this is not in fact correct, and there
is only one lot of state in a real h/w GIC (it's that flipflop). AIUI the
correct behaviour is:

  * GIC_TEST_PENDING() should read:
    ((s->irq_state[irq].pending & (cm) != 0) ||
       (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm))
     -- this corresponds to the OR gate in fig 4-10; this is the value
    to be used in reads from ICPENDR/ISPENDR and other "is this
    interrupt pending" checks like the one in gic_update()
  * in gic_set_irq(), if level is 1 (rising edge) we GIC_SET_PENDING
     only if the interrupt is edge-triggered (pending status isn't latched
     for level triggered interrupts). We always GIC_SET_LEVEL(irq, 1).
  * in gic_set_irq(), if level is 0 (falling edge) we don't touch the pending
    state, but just GIC_SET_LEVEL(irq, 0)
  * gic_acknowledge_irq() always does a GIC_CLEAR_PENDING()
    (this won't actually make a level-triggered interrupt be not pending
    because of the second clause of our updated GIC_TEST_PENDING)
  * writes to the I[CS]PENDR registers always result in GIC_SET_PENDING
    or GIC_CLEAR_PENDING calls (updating the latch state)

(The above applies to GIC v1/v2 only; 11mpcore wants to keep the
current GIC_TEST_PENDING definition, etc, but I figured it would
be clearer to just describe the v1/v2 behaviour without peppering it
with "not 11mpcore" caveats.)

The v7M NVIC (ie s->rev ==REV_NVIC) should behave like the
11MPcore GIC. (At least, the v7M specified behaviour for the
pending bits is that they are cleared on exception entry and then
the interrupt re-pends if the signal is still asserted on exception
exit. I suspect that there are cases where v7M diverges from
11MPCore GIC behaviour, but that's what we're currently treating
it like, so leaving it that way will not be a regression.)

I haven't thought too hard about how this should interact with the
KVM GIC save/restore interface, but I think that it should be OK
to just do a simple "save/load the value of the I*PENDR registers",
because the only case where this isn't just a simple replication
of the source state to the destination is:
 * the source has a 1 bit in the pending register because of a
   level triggered interrupt whose input is currently asserted,
   but its pending latch is not set
 * in migration we write a 1 bit into the destination pending
   register, which causes the destination's pending latch to be
   set even though it was clear on the source
 * however this is fairly harmless, because when the guest takes
   the interrupt after migration (which it would have done anyway
   because of the asserted input) the interrupt acknowlege process
   will clear the pending latch and we'll be back in sync with
   where we should be
I think it's technically possible to write a guest which can detect
the difference (it would have to disable interrupts, prod the device
to assert its interrupt, clear the interrupt status on the device and
then reenable interrupts again, at which point if we've migrated
in the middle of that we get a single spurious interrupt). But I would
be entirely unsurprised if the guest saw the same behaviour if
it was running on bare metal and there was a power management
CPU & GIC power-down/power-up transition, since the power
management code also has to do GIC state save and restore...
In any case any non-perverse guest should never notice.

Apologies for the "you need to rewrite this" review at this late
stage, but I didn't find the necessary information about the
behaviour here until the very tail end of last year.

> @@ -423,25 +470,38 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
>          irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
>          if (irq >= s->num_irq)
>              goto bad_reg;
> -        if (irq < 16)
> -          irq = 0;
> +        if (irq < GIC_NR_SGIS)
> +          value = 0;

This change is correct but it should be its own patch since it's a
standalone bugfix.

Minor style note: coding style demands braces on all if() statements,
even one-liners like this one (and even if you're just making a slight
tweak to code that was already present and not coding-standard
compliant). scripts/checkpatch.pl ought to spot this.

> --- a/include/hw/intc/arm_gic_common.h
> +++ b/include/hw/intc/arm_gic_common.h
> @@ -27,6 +27,7 @@
>  #define GIC_MAXIRQ 1020
>  /* First 32 are private to each CPU (SGIs and PPIs). */
>  #define GIC_INTERNAL 32
> +#define GIC_NR_SGIS 16

If we're going to introduce this (and it seems like a good idea) we should
use it more consistently in the places that currently have a hardcoded "16".
(You might want to make that a separate patch.)

thanks
-- PMM
Christoffer Dall Jan. 28, 2014, 2:09 a.m. UTC | #2
On Sun, Jan 19, 2014 at 07:49:58PM +0000, Peter Maydell wrote:
> On 21 December 2013 06:09, Christoffer Dall <christoffer.dall@linaro.org> wrote:
> > The existing implementation of the pending behavior in gic_set_irq,
> > gic_acknowledge_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 change the behavior to be in
> > accordance with the GICv2.0 specs for "generic implementations"
> > (s->revision == 1 || s->revision == 2).
> >
> > Generic implementations also mandate that level-triggered interrupts
> > that are pending due to a write to GICD_ISPENDR stay pending until the
> > interrupt is acknowledged or until there's a write to the GICD_ICPENDR.
> > Similarly, only clear the pending state on writes to GICD_ICPENDR if the
> > interrupt signal is not asserted.  This requires an extra state variable
> > to keep track of writes to the set/clear registers, corresponding to the
> > D-flip flop in the GICv2.0 specs Figure 4-10.
> 
> So this means we now have two lots of pending state, the pending
> and sw_pending fields. I think this is not in fact correct, and there
> is only one lot of state in a real h/w GIC (it's that flipflop). AIUI the
> correct behaviour is:
> 
>   * GIC_TEST_PENDING() should read:
>     ((s->irq_state[irq].pending & (cm) != 0) ||
>        (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm))
>      -- this corresponds to the OR gate in fig 4-10; this is the value
>     to be used in reads from ICPENDR/ISPENDR and other "is this
>     interrupt pending" checks like the one in gic_update()
>   * in gic_set_irq(), if level is 1 (rising edge) we GIC_SET_PENDING
>      only if the interrupt is edge-triggered (pending status isn't latched
>      for level triggered interrupts). We always GIC_SET_LEVEL(irq, 1).
>   * in gic_set_irq(), if level is 0 (falling edge) we don't touch the pending
>     state, but just GIC_SET_LEVEL(irq, 0)
>   * gic_acknowledge_irq() always does a GIC_CLEAR_PENDING()
>     (this won't actually make a level-triggered interrupt be not pending
>     because of the second clause of our updated GIC_TEST_PENDING)
>   * writes to the I[CS]PENDR registers always result in GIC_SET_PENDING
>     or GIC_CLEAR_PENDING calls (updating the latch state)

ok, it's probably cleaner code-wise, but should produce the same result
as far as I can see.  I guess I see the pending field as the result of
the final or-gate (possibly because I'm too tied to the way the
in-kernel emulation works where we don't have a separate 'level' state).
Anyway, I'll respin the patch according to your directions (my only
concern with that is that it feels a bit unlogical to have a bitfield
called pending where some of the bits are not set despite the
corresponding interrupts being pending, but we'll see if the overall
code looks more clean with this approach).

Thanks.

> 
> (The above applies to GIC v1/v2 only; 11mpcore wants to keep the
> current GIC_TEST_PENDING definition, etc, but I figured it would
> be clearer to just describe the v1/v2 behaviour without peppering it
> with "not 11mpcore" caveats.)
> 
> The v7M NVIC (ie s->rev ==REV_NVIC) should behave like the
> 11MPcore GIC. (At least, the v7M specified behaviour for the
> pending bits is that they are cleared on exception entry and then
> the interrupt re-pends if the signal is still asserted on exception
> exit. I suspect that there are cases where v7M diverges from
> 11MPCore GIC behaviour, but that's what we're currently treating
> it like, so leaving it that way will not be a regression.)
> 
> I haven't thought too hard about how this should interact with the
> KVM GIC save/restore interface, but I think that it should be OK
> to just do a simple "save/load the value of the I*PENDR registers",
> because the only case where this isn't just a simple replication
> of the source state to the destination is:
>  * the source has a 1 bit in the pending register because of a
>    level triggered interrupt whose input is currently asserted,
>    but its pending latch is not set
>  * in migration we write a 1 bit into the destination pending
>    register, which causes the destination's pending latch to be
>    set even though it was clear on the source
>  * however this is fairly harmless, because when the guest takes
>    the interrupt after migration (which it would have done anyway
>    because of the asserted input) the interrupt acknowlege process
>    will clear the pending latch and we'll be back in sync with
>    where we should be

agreed, isn't this analagous to suspend/resume on hardware, or would you
assume that the device state would be restored to assert the input
again?

> I think it's technically possible to write a guest which can detect
> the difference (it would have to disable interrupts, prod the device
> to assert its interrupt, clear the interrupt status on the device and
> then reenable interrupts again, at which point if we've migrated
> in the middle of that we get a single spurious interrupt). But I would
> be entirely unsurprised if the guest saw the same behaviour if
> it was running on bare metal and there was a power management
> CPU & GIC power-down/power-up transition, since the power
> management code also has to do GIC state save and restore...
> In any case any non-perverse guest should never notice.
> 
> Apologies for the "you need to rewrite this" review at this late
> stage, but I didn't find the necessary information about the
> behaviour here until the very tail end of last year.
> 

No worries, you have a lot on your plate.

> > @@ -423,25 +470,38 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
> >          irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
> >          if (irq >= s->num_irq)
> >              goto bad_reg;
> > -        if (irq < 16)
> > -          irq = 0;
> > +        if (irq < GIC_NR_SGIS)
> > +          value = 0;
> 
> This change is correct but it should be its own patch since it's a
> standalone bugfix.
> 
> Minor style note: coding style demands braces on all if() statements,
> even one-liners like this one (and even if you're just making a slight
> tweak to code that was already present and not coding-standard
> compliant). scripts/checkpatch.pl ought to spot this.
>
> > --- a/include/hw/intc/arm_gic_common.h
> > +++ b/include/hw/intc/arm_gic_common.h
> > @@ -27,6 +27,7 @@
> >  #define GIC_MAXIRQ 1020
> >  /* First 32 are private to each CPU (SGIs and PPIs). */
> >  #define GIC_INTERNAL 32
> > +#define GIC_NR_SGIS 16
> 
> If we're going to introduce this (and it seems like a good idea) we should
> use it more consistently in the places that currently have a hardcoded "16".
> (You might want to make that a separate patch.)
> 
Ok, moving these changes into two separate patches and fixing the code
style.

-Christoffer
Peter Maydell Jan. 28, 2014, 9:01 a.m. UTC | #3
On 28 January 2014 02:09, Christoffer Dall <christoffer.dall@linaro.org> wrote:
> On Sun, Jan 19, 2014 at 07:49:58PM +0000, Peter Maydell wrote:
>> So this means we now have two lots of pending state, the pending
>> and sw_pending fields. I think this is not in fact correct, and there
>> is only one lot of state in a real h/w GIC (it's that flipflop). AIUI the
>> correct behaviour is:
>>
>>   * GIC_TEST_PENDING() should read:
>>     ((s->irq_state[irq].pending & (cm) != 0) ||
>>        (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm))
>>      -- this corresponds to the OR gate in fig 4-10; this is the value
>>     to be used in reads from ICPENDR/ISPENDR and other "is this
>>     interrupt pending" checks like the one in gic_update()
>>   * in gic_set_irq(), if level is 1 (rising edge) we GIC_SET_PENDING
>>      only if the interrupt is edge-triggered (pending status isn't latched
>>      for level triggered interrupts). We always GIC_SET_LEVEL(irq, 1).
>>   * in gic_set_irq(), if level is 0 (falling edge) we don't touch the pending
>>     state, but just GIC_SET_LEVEL(irq, 0)
>>   * gic_acknowledge_irq() always does a GIC_CLEAR_PENDING()
>>     (this won't actually make a level-triggered interrupt be not pending
>>     because of the second clause of our updated GIC_TEST_PENDING)
>>   * writes to the I[CS]PENDR registers always result in GIC_SET_PENDING
>>     or GIC_CLEAR_PENDING calls (updating the latch state)
>
> ok, it's probably cleaner code-wise, but should produce the same result
> as far as I can see.  I guess I see the pending field as the result of
> the final or-gate (possibly because I'm too tied to the way the
> in-kernel emulation works where we don't have a separate 'level' state).
> Anyway, I'll respin the patch according to your directions (my only
> concern with that is that it feels a bit unlogical to have a bitfield
> called pending where some of the bits are not set despite the
> corresponding interrupts being pending, but we'll see if the overall
> code looks more clean with this approach).

The key thing is that we definitely shouldn't be keeping more
state than the hardware does; that's always a bad sign.

thanks
-- PMM
diff mbox

Patch

diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 6c59650..dd76e1d 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -97,6 +97,38 @@  void gic_set_pending_private(GICState *s, int cpu, int irq)
     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);
+        GIC_SET_PENDING(irq, target);
+    } else {
+        /* Clear level-triggered interrupts that have not been set to pending
+         * through GICD_SPENDR.
+         */
+        if (!GIC_TEST_EDGE_TRIGGER(irq) && !GIC_TEST_SW_PENDING(irq, cm)) {
+            GIC_CLEAR_PENDING(irq, target);
+        }
+        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 +158,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) {
+        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);
 }
 
@@ -160,9 +189,23 @@  uint32_t gic_acknowledge_irq(GICState *s, int cpu)
         return 1023;
     }
     s->last_active[new_irq][cpu] = s->running_irq[cpu];
-    /* Clear pending flags for both level and edge triggered interrupts.
-       Level triggered IRQs will be reasserted once they become inactive.  */
-    GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
+
+    cm = GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm;
+    GIC_CLEAR_SW_PENDING(new_irq, cm);
+    if (s->revision == REV_11MPCORE) {
+        /* Clear pending flags for both level and edge triggered interrupts.
+         * Level triggered IRQs will be reasserted once they become inactive.
+         */
+        GIC_CLEAR_PENDING(new_irq, cm);
+    } else {
+        /* Clear pending flags for edge-triggered and non-asserted
+         * level-triggered interrupts
+         */
+        if (GIC_TEST_EDGE_TRIGGER(new_irq) || !GIC_TEST_LEVEL(new_irq, cpu)) {
+            GIC_CLEAR_PENDING(new_irq, cm);
+        }
+    }
+
     gic_set_running_irq(s, cpu, new_irq);
     DPRINTF("ACK %d\n", new_irq);
     return new_irq;
@@ -195,14 +238,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) {
+        /* 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];
@@ -423,25 +470,38 @@  static void gic_dist_writeb(void *opaque, hwaddr offset,
         irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 16)
-          irq = 0;
+        if (irq < GIC_NR_SGIS)
+          value = 0;
 
-        for (i = 0; i < 8; i++) {
+        for (i = 0; i < 8; i++, irq++) {
             if (value & (1 << i)) {
-                GIC_SET_PENDING(irq + i, GIC_TARGET(irq + i));
+                GIC_SET_PENDING(irq, GIC_TARGET(irq));
+                GIC_SET_SW_PENDING(irq, GIC_TARGET(irq));
             }
         }
     } else if (offset < 0x300) {
+        int cm = (1 << cpu);
         /* Interrupt Clear Pending.  */
         irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        for (i = 0; i < 8; i++) {
+        if (irq < GIC_NR_SGIS)
+            value = 0;
+
+        for (i = 0; i < 8; i++, irq++) {
             /* ??? This currently clears the pending bit for all CPUs, even
                for per-CPU interrupts.  It's unclear whether this is the
                corect behavior.  */
             if (value & (1 << i)) {
-                GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
+                GIC_CLEAR_SW_PENDING(irq, cm);
+                if (s->revision == REV_11MPCORE) {
+                    GIC_CLEAR_PENDING(irq, ALL_CPU_MASK);
+                } else {
+                    if (GIC_TEST_EDGE_TRIGGER(irq) ||
+                        !GIC_TEST_LEVEL(irq, cm)) {
+                        GIC_CLEAR_PENDING(irq, ALL_CPU_MASK);
+                    }
+                }
             }
         }
     } else if (offset < 0x400) {
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index 710607b..8c7621a 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -43,11 +43,12 @@  static int gic_post_load(void *opaque, int version_id)
 
 static const VMStateDescription vmstate_gic_irq_state = {
     .name = "arm_gic_irq_state",
-    .version_id = 1,
-    .minimum_version_id = 1,
+    .version_id = 2,
+    .minimum_version_id = 2,
     .fields = (VMStateField[]) {
         VMSTATE_UINT8(enabled, gic_irq_state),
         VMSTATE_UINT8(pending, gic_irq_state),
+        VMSTATE_UINT8(sw_pending, gic_irq_state),
         VMSTATE_UINT8(active, gic_irq_state),
         VMSTATE_UINT8(level, gic_irq_state),
         VMSTATE_BOOL(model, gic_irq_state),
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
index 8c02d58..3ebe4dc 100644
--- a/hw/intc/gic_internal.h
+++ b/hw/intc/gic_internal.h
@@ -35,6 +35,9 @@ 
 #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_SW_PENDING(irq, cm) (s->irq_state[irq].sw_pending |= (cm))
+#define GIC_CLEAR_SW_PENDING(irq, cm) (s->irq_state[irq].sw_pending &= ~(cm))
+#define GIC_TEST_SW_PENDING(irq, cm) ((s->irq_state[irq].sw_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)
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
index 40cd3d6..126e122 100644
--- a/include/hw/intc/arm_gic_common.h
+++ b/include/hw/intc/arm_gic_common.h
@@ -27,6 +27,7 @@ 
 #define GIC_MAXIRQ 1020
 /* First 32 are private to each CPU (SGIs and PPIs). */
 #define GIC_INTERNAL 32
+#define GIC_NR_SGIS 16
 /* Maximum number of possible CPU interfaces, determined by GIC architecture */
 #define GIC_NCPU 8
 
@@ -34,6 +35,7 @@  typedef struct gic_irq_state {
     /* The enable bits are only banked for per-cpu interrupts.  */
     uint8_t enabled;
     uint8_t pending;
+    uint8_t sw_pending; /* keep track of GICD_ISPENDR and GICD_ICPENDR writes */
     uint8_t active;
     uint8_t level;
     bool model; /* 0 = N:N, 1 = 1:N */