All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/41] arm: Implement GICv4
@ 2022-04-08 14:15 Peter Maydell
  2022-04-08 14:15 ` [PATCH 01/41] hw/intc/arm_gicv3_its: Add missing blank line Peter Maydell
                   ` (41 more replies)
  0 siblings, 42 replies; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

This patchset implements emulation of GICv4 in our TCG GIC and ITS
models, and makes the virt board use it where appropriate.

The GICv4 provides a single new feature: direct injection of virtual
interrupts from the ITS to a VM. In QEMU terms this means that if you
have an outer QEMU which is emulating a CPU with EL2, and the outer
guest passes through a PCI device (probably one emulated by the outer
QEMU) to an inner guest, interrupts from that device can go directly
to the inner guest, rather than having to go to the outer guest and
the outer guest then synthesizing virtual interrupts to the inner
guest. (If you aren't configuring the inner guest with a passthrough
PCI device then this new feature is of no interest.)

The basic structure of the patchset is as follows:

(1) There are a handful of preliminary patches fixing some minor
existing nits.

(2) The v4 ITS has some new in-guest-memory data structures and new
ITS commands that let the guest set them up. The next sequence of
patches implement all those commands. Where the command needs to
actually do something (eg "deliver a vLPI"), these patches call
functions in the redistributor which are left as unimplemented stubs
to be filled in in subsequent patches. This first chunk of patches
sticks to the data-structure handling and all the command argument
unpacking and error checking.

(3) The redistributor has a new redistributor frame (ie the amount of
guest memory used by redistributor registers is larger) with a two new
registers in it. We implement these initially as reads-as-written.

(4) The CPU interface needs relatively minor changes: as well as
looking at the list registers to determine the highest priority
pending virtual interrupt, we must also look at the highest priority
pending vLPI. We implement these changes, again leaving the interfaces
from this code into the redistributor as stubs for the moment.

(5) Now we can fill in all the stub code in the redistributor.  This
is almost all working with the pending and config tables for virtual
LPIs. (Side note: in real hardware some of this work is done by the
ITS rather than the redistributor, but in our implementation we split
between the two source files slightly differently. I've made the vLPI
handling follow the pattern of the existing LPI handling.)

(6) Finally, we can update the ID registers which tell the guest about
the presence of v4 features, allow the GIC device to accept 4 as a
value for its QOM revision property, and make the virt board set that
when appropriate.

General notes:

Since the only useful thing in GICv4 is direct virtual interrupt
injection, it isn't expected that you would have a system with a GICv4
and a CPU without EL2. So I've made this an error, and the virt board
will only use GICv4 if the user also enables emulation of
virtualization.

Because the redistributor frame is twice the size in GICv4, the
number of redistributors we can fit into a given area of memory
is reduced. This means that when using GICv4 the maximum number
of CPUs supported on the virt board drops from 512 to 317. (No,
I'm not sure why this is 317 and not 256 :-))

I have not particularly considered performance in this initial
implementation. In particular, we will do a complete re-scan of a
virtual LPI pending table every time the outer guest reschedules a
vCPU (and writes GICR_VPENDBASER). The spec provides scope for
optimisation here, by allowing part of the LPI table to have IMPDEF
contents, which we could in principle use to cache information like
the current highest priority pending vLPI. Given that emulating
nested guests with PCI passthrough is a fairly niche activity,
I propose that we not do this unless the three people doing that
complain about this all being too slow :-)

Tested with a Linux kernel passing through a virtio-blk device
to an inner Linux VM with KVM/QEMU. (NB that to get the outer
Linux kernel to actually use the new GICv4 functionality you
need to pass it "kvm-arm.vgic_v4_enable=1", as the kernel
will not use it by default.)

thanks
-- PMM

Peter Maydell (41):
  hw/intc/arm_gicv3_its: Add missing blank line
  hw/intc/arm_gicv3: Sanity-check num-cpu property
  hw/intc/arm_gicv3: Insist that redist region capacity matches CPU count
  hw/intc/arm_gicv3: Report correct PIDR0 values for ID registers
  target/arm/cpu.c: ignore VIRQ and VFIQ if no EL2
  hw/intc/arm_gicv3_its: Factor out "is intid a valid LPI ID?"
  hw/intc/arm_gicv3_its: Implement GITS_BASER2 for GICv4
  hw/intc/arm_gicv3_its: Implement VMAPI and VMAPTI
  hw/intc/arm_gicv3_its: Implement VMAPP
  hw/intc/arm_gicv3_its: Distinguish success and error cases of CMD_CONTINUE
  hw/intc/arm_gicv3_its: Factor out "find ITE given devid, eventid"
  hw/intc/arm_gicv3_its: Factor out CTE lookup sequence
  hw/intc/arm_gicv3_its: Split out process_its_cmd() physical interrupt code
  hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd()
  hw/intc/arm_gicv3: Keep pointers to every connected ITS
  hw/intc/arm_gicv3_its: Implement VMOVP
  hw/intc/arm_gicv3_its: Implement VSYNC
  hw/intc/arm_gicv3_its: Implement INV command properly
  hw/intc/arm_gicv3_its: Implement INV for virtual interrupts
  hw/intc/arm_gicv3_its: Implement VMOVI
  hw/intc/arm_gicv3_its: Implement VINVALL
  hw/intc/arm_gicv3: Implement GICv4's new redistributor frame
  hw/intc/arm_gicv3: Implement new GICv4 redistributor registers
  hw/intc/arm_gicv3_cpuif: Split "update vIRQ/vFIQ" from
    gicv3_cpuif_virt_update()
  hw/intc/arm_gicv3_cpuif: Support vLPIs
  hw/intc/arm_gicv3_cpuif: Don't recalculate maintenance irq unnecessarily
  hw/intc/arm_gicv3_redist: Factor out "update hpplpi for one LPI" logic
  hw/intc/arm_gicv3_redist: Factor out "update hpplpi for all LPIs" logic
  hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes
  hw/intc/arm_gicv3_redist: Factor out "update bit in pending table" code
  hw/intc/arm_gicv3_redist: Implement gicv3_redist_process_vlpi()
  hw/intc/arm_gicv3_redist: Implement gicv3_redist_vlpi_pending()
  hw/intc/arm_gicv3_redist: Use set_pending_table_bit() in mov handling
  hw/intc/arm_gicv3_redist: Implement gicv3_redist_mov_vlpi()
  hw/intc/arm_gicv3_redist: Implement gicv3_redist_vinvall()
  hw/intc/arm_gicv3_redist: Implement gicv3_redist_inv_vlpi()
  hw/intc/arm_gicv3: Update ID and feature registers for GICv4
  hw/intc/arm_gicv3: Allow 'revision' property to be set to 4
  hw/arm/virt: Use VIRT_GIC_VERSION_* enum values in create_gic()
  hw/arm/virt: Abstract out calculation of redistributor region capacity
  hw/arm/virt: Support TCG GICv4

 docs/system/arm/virt.rst               |   5 +-
 hw/intc/gicv3_internal.h               | 231 ++++++-
 include/hw/arm/virt.h                  |  19 +-
 include/hw/intc/arm_gicv3_common.h     |  13 +
 include/hw/intc/arm_gicv3_its_common.h |   1 +
 hw/arm/virt.c                          | 102 ++-
 hw/intc/arm_gicv3_common.c             |  54 +-
 hw/intc/arm_gicv3_cpuif.c              | 195 +++++-
 hw/intc/arm_gicv3_dist.c               |   7 +-
 hw/intc/arm_gicv3_its.c                | 876 ++++++++++++++++++++-----
 hw/intc/arm_gicv3_its_kvm.c            |   2 +
 hw/intc/arm_gicv3_kvm.c                |   5 +
 hw/intc/arm_gicv3_redist.c             | 480 +++++++++++---
 target/arm/cpu.c                       |  12 +-
 hw/intc/trace-events                   |  18 +-
 15 files changed, 1695 insertions(+), 325 deletions(-)

-- 
2.25.1



^ permalink raw reply	[flat|nested] 88+ messages in thread

* [PATCH 01/41] hw/intc/arm_gicv3_its: Add missing blank line
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-08 23:16   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 02/41] hw/intc/arm_gicv3: Sanity-check num-cpu property Peter Maydell
                   ` (40 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

In commit b6f96009acc we split do_process_its_cmd() from
process_its_cmd(), but forgot the usual blank line between function
definitions.  Add it.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_its.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 87466732139..44914f25780 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -380,6 +380,7 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
     }
     return CMD_CONTINUE;
 }
+
 static ItsCmdResult process_its_cmd(GICv3ITSState *s, const uint64_t *cmdpkt,
                                     ItsCmdType cmd)
 {
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 02/41] hw/intc/arm_gicv3: Sanity-check num-cpu property
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
  2022-04-08 14:15 ` [PATCH 01/41] hw/intc/arm_gicv3_its: Add missing blank line Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-08 23:17   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 03/41] hw/intc/arm_gicv3: Insist that redist region capacity matches CPU count Peter Maydell
                   ` (39 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

In the GICv3 code we implicitly rely on there being at least one CPU
and thus at least one redistributor and CPU interface.  Sanity-check
that the property the board code sets is not zero.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
Doing this would be a board code error, but we might as well
get a clean diagnostic for it and not have to think about
num_cpu == 0 as a special case later.
---
 hw/intc/arm_gicv3_common.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 4ca5ae9bc56..90204be25b6 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -328,6 +328,10 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
                    s->num_irq, GIC_INTERNAL);
         return;
     }
+    if (s->num_cpu == 0) {
+        error_setg(errp, "num-cpu must be at least 1");
+        return;
+    }
 
     /* ITLinesNumber is represented as (N / 32) - 1, so this is an
      * implementation imposed restriction, not an architectural one,
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 03/41] hw/intc/arm_gicv3: Insist that redist region capacity matches CPU count
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
  2022-04-08 14:15 ` [PATCH 01/41] hw/intc/arm_gicv3_its: Add missing blank line Peter Maydell
  2022-04-08 14:15 ` [PATCH 02/41] hw/intc/arm_gicv3: Sanity-check num-cpu property Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-08 23:20   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 04/41] hw/intc/arm_gicv3: Report correct PIDR0 values for ID registers Peter Maydell
                   ` (38 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Boards using the GICv3 need to configure it with both the total
number of CPUs and also the sizes of all the memory regions which
contain redistributors (one redistributor per CPU).  At the moment
the GICv3 checks that the number of CPUs specified is not too many to
fit in the defined redistributor regions, but in fact the code
assumes that the two match exactly.  For instance when we set the
GICR_TYPER.Last bit on the final redistributor in each region, we
assume that we don't need to consider the possibility of a region
being only half full of redistributors or even completely empty.  We
also assume in gicv3_redist_read() and gicv3_redist_write() that we
can calculate the CPU index from the offset within the MemoryRegion
and that this will always be in range.

Fortunately all the board code sets the redistributor region sizes to
exactly match the CPU count, so this isn't a visible bug.  We could
in theory make the GIC code handle non-full redistributor regions, or
have it automatically reduce the provided region sizes to match the
CPU count, but the simplest thing is just to strengthen the error
check and insist that the CPU count and redistributor region size
settings match exactly, since all the board code does that anyway.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 90204be25b6..c797c82786b 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -354,9 +354,9 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
     for (i = 0; i < s->nb_redist_regions; i++) {
         rdist_capacity += s->redist_region_count[i];
     }
-    if (rdist_capacity < s->num_cpu) {
+    if (rdist_capacity != s->num_cpu) {
         error_setg(errp, "Capacity of the redist regions(%d) "
-                   "is less than number of vcpus(%d)",
+                   "does not match the number of vcpus(%d)",
                    rdist_capacity, s->num_cpu);
         return;
     }
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 04/41] hw/intc/arm_gicv3: Report correct PIDR0 values for ID registers
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (2 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 03/41] hw/intc/arm_gicv3: Insist that redist region capacity matches CPU count Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-08 23:26   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 05/41] target/arm/cpu.c: ignore VIRQ and VFIQ if no EL2 Peter Maydell
                   ` (37 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

We use the common function gicv3_idreg() to supply the CoreSight ID
register values for the GICv3 for the copies of these ID registers in
the distributor, redistributor and ITS register frames.  This isn't
quite correct, because while most of the register values are the
same, the PIDR0 value should vary to indicate which of these three
frames it is.  (You can see this and also the correct values of these
PIDR0 registers by looking at the GIC-600 or GIC-700 TRMs, for
example.)

Make gicv3_idreg() take an extra argument for the PIDR0 value.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h   | 15 +++++++++++++--
 hw/intc/arm_gicv3_dist.c   |  2 +-
 hw/intc/arm_gicv3_its.c    |  2 +-
 hw/intc/arm_gicv3_redist.c |  2 +-
 4 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 2bf1baef047..dec413f7cfa 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -555,7 +555,12 @@ static inline uint32_t gicv3_iidr(void)
     return 0x43b;
 }
 
-static inline uint32_t gicv3_idreg(int regoffset)
+/* CoreSight PIDR0 values for ARM GICv3 implementations */
+#define GICV3_PIDR0_DIST 0x92
+#define GICV3_PIDR0_REDIST 0x93
+#define GICV3_PIDR0_ITS 0x94
+
+static inline uint32_t gicv3_idreg(int regoffset, uint8_t pidr0)
 {
     /* Return the value of the CoreSight ID register at the specified
      * offset from the first ID register (as found in the distributor
@@ -565,7 +570,13 @@ static inline uint32_t gicv3_idreg(int regoffset)
     static const uint8_t gicd_ids[] = {
         0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x3B, 0x00, 0x0D, 0xF0, 0x05, 0xB1
     };
-    return gicd_ids[regoffset / 4];
+
+    regoffset /= 4;
+
+    if (regoffset == 4) {
+        return pidr0;
+    }
+    return gicd_ids[regoffset];
 }
 
 /**
diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c
index 28d913b2114..7f6275363ea 100644
--- a/hw/intc/arm_gicv3_dist.c
+++ b/hw/intc/arm_gicv3_dist.c
@@ -557,7 +557,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset,
     }
     case GICD_IDREGS ... GICD_IDREGS + 0x2f:
         /* ID registers */
-        *data = gicv3_idreg(offset - GICD_IDREGS);
+        *data = gicv3_idreg(offset - GICD_IDREGS, GICV3_PIDR0_DIST);
         return true;
     case GICD_SGIR:
         /* WO registers, return unknown value */
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 44914f25780..f8467b61ec5 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1161,7 +1161,7 @@ static bool its_readl(GICv3ITSState *s, hwaddr offset,
         break;
     case GITS_IDREGS ... GITS_IDREGS + 0x2f:
         /* ID registers */
-        *data = gicv3_idreg(offset - GITS_IDREGS);
+        *data = gicv3_idreg(offset - GITS_IDREGS, GICV3_PIDR0_ITS);
         break;
     case GITS_TYPER:
         *data = extract64(s->typer, 0, 32);
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 412a04f59cf..dc9729e8395 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -234,7 +234,7 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset,
         *data = cs->gicr_nsacr;
         return MEMTX_OK;
     case GICR_IDREGS ... GICR_IDREGS + 0x2f:
-        *data = gicv3_idreg(offset - GICR_IDREGS);
+        *data = gicv3_idreg(offset - GICR_IDREGS, GICV3_PIDR0_REDIST);
         return MEMTX_OK;
     default:
         return MEMTX_ERROR;
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 05/41] target/arm/cpu.c: ignore VIRQ and VFIQ if no EL2
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (3 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 04/41] hw/intc/arm_gicv3: Report correct PIDR0 values for ID registers Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-08 23:39   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 06/41] hw/intc/arm_gicv3_its: Factor out "is intid a valid LPI ID?" Peter Maydell
                   ` (36 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

In a GICv3, it is impossible for the GIC to deliver a VIRQ or VFIQ to
the CPU unless the CPU has EL2, because VIRQ and VFIQ are only
configurable via EL2-only system registers.  Moreover, in our
implementation we were only calculating and updating the state of the
VIRQ and VFIQ lines in gicv3_cpuif_virt_irq_fiq_update() when those
EL2 system registers changed.  We were therefore able to assert in
arm_cpu_set_irq() that we didn't see a VIRQ or VFIQ line update if
EL2 wasn't present.

This assumption no longer holds with GICv4:
 * even if the CPU does not have EL2 the guest is able to cause the
   GIC to deliver a virtual LPI by programming the ITS (which is a
   silly thing for it to do, but possible)
 * because we now need to recalculate the state of the VIRQ and VFIQ
   lines in more cases than just "some EL2 GIC sysreg was written",
   we will see calls to arm_cpu_set_irq() for "VIRQ is 0, VFIQ is 0"
   even if the guest is not using the virtual LPI parts of the ITS

Remove the assertions, and instead simply ignore the state of the
VIRQ and VFIQ lines if the CPU does not have EL2.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target/arm/cpu.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 5d4ca7a2270..1140ce5829e 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -694,6 +694,16 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level)
         [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ
     };
 
+    if (!arm_feature(env, ARM_FEATURE_EL2) &&
+        (irq == ARM_CPU_VIRQ || irq == ARM_CPU_VFIQ)) {
+        /*
+         * The GIC might tell us about VIRQ and VFIQ state, but if we don't
+         * have EL2 support we don't care. (Unless the guest is doing something
+         * silly this will only be calls saying "level is still 0".)
+         */
+        return;
+    }
+
     if (level) {
         env->irq_line_state |= mask[irq];
     } else {
@@ -702,11 +712,9 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level)
 
     switch (irq) {
     case ARM_CPU_VIRQ:
-        assert(arm_feature(env, ARM_FEATURE_EL2));
         arm_cpu_update_virq(cpu);
         break;
     case ARM_CPU_VFIQ:
-        assert(arm_feature(env, ARM_FEATURE_EL2));
         arm_cpu_update_vfiq(cpu);
         break;
     case ARM_CPU_IRQ:
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 06/41] hw/intc/arm_gicv3_its: Factor out "is intid a valid LPI ID?"
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (4 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 05/41] target/arm/cpu.c: ignore VIRQ and VFIQ if no EL2 Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-08 23:45   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 07/41] hw/intc/arm_gicv3_its: Implement GITS_BASER2 for GICv4 Peter Maydell
                   ` (35 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

In process_mapti() we check interrupt IDs to see whether they are
in the valid LPI range. Factor this out into its own utility
function, as we're going to want it elsewhere too for GICv4.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_its.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index f8467b61ec5..400cdf83794 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -79,6 +79,12 @@ typedef enum ItsCmdResult {
     CMD_CONTINUE = 1,
 } ItsCmdResult;
 
+static inline bool intid_in_lpi_range(uint32_t id)
+{
+    return id >= GICV3_LPI_INTID_START &&
+        id < (1ULL << (GICD_TYPER_IDBITS + 1));
+}
+
 static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
 {
     uint64_t result = 0;
@@ -410,7 +416,6 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
     uint32_t devid, eventid;
     uint32_t pIntid = 0;
     uint64_t num_eventids;
-    uint32_t num_intids;
     uint16_t icid = 0;
     DTEntry dte;
     ITEntry ite;
@@ -438,7 +443,6 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
         return CMD_STALL;
     }
     num_eventids = 1ULL << (dte.size + 1);
-    num_intids = 1ULL << (GICD_TYPER_IDBITS + 1);
 
     if (icid >= s->ct.num_entries) {
         qemu_log_mask(LOG_GUEST_ERROR,
@@ -460,7 +464,7 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
         return CMD_CONTINUE;
     }
 
-    if (pIntid < GICV3_LPI_INTID_START || pIntid >= num_intids) {
+    if (!intid_in_lpi_range(pIntid)) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "%s: invalid interrupt ID 0x%x\n", __func__, pIntid);
         return CMD_CONTINUE;
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 07/41] hw/intc/arm_gicv3_its: Implement GITS_BASER2 for GICv4
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (5 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 06/41] hw/intc/arm_gicv3_its: Factor out "is intid a valid LPI ID?" Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-08 23:55   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 08/41] hw/intc/arm_gicv3_its: Implement VMAPI and VMAPTI Peter Maydell
                   ` (34 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The GICv4 defines a new in-guest-memory table for the ITS: this is
the vPE table.  Implement the new GITS_BASER2 register which the
guest uses to tell the ITS where the vPE table is located, including
the decode of the register fields into the TableDesc structure which
we do for the GITS_BASER<n> when the guest enables the ITS.

We guard provision of the new register with the its_feature_virtual()
function, which does a check of the GITS_TYPER.Virtual bit which
indicates presence of ITS support for virtual LPIs.  Since this bit
is currently always zero, GICv4-specific features will not be
accessible to the guest yet.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h               | 16 ++++++++++++++++
 include/hw/intc/arm_gicv3_its_common.h |  1 +
 hw/intc/arm_gicv3_its.c                | 25 +++++++++++++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index dec413f7cfa..4613b9e59ba 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -280,6 +280,7 @@ FIELD(GITS_CTLR, ENABLED, 0, 1)
 FIELD(GITS_CTLR, QUIESCENT, 31, 1)
 
 FIELD(GITS_TYPER, PHYSICAL, 0, 1)
+FIELD(GITS_TYPER, VIRTUAL, 1, 1)
 FIELD(GITS_TYPER, ITT_ENTRY_SIZE, 4, 4)
 FIELD(GITS_TYPER, IDBITS, 8, 5)
 FIELD(GITS_TYPER, DEVBITS, 13, 5)
@@ -298,6 +299,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define GITS_BASER_PAGESIZE_64K               2
 
 #define GITS_BASER_TYPE_DEVICE               1ULL
+#define GITS_BASER_TYPE_VPE                  2ULL
 #define GITS_BASER_TYPE_COLLECTION           4ULL
 
 #define GITS_PAGE_SIZE_4K       0x1000
@@ -419,6 +421,20 @@ FIELD(DTE, ITTADDR, 6, 44)
 FIELD(CTE, VALID, 0, 1)
 FIELD(CTE, RDBASE, 1, RDBASE_PROCNUM_LENGTH)
 
+/*
+ * 8 bytes VPE table entry size:
+ * Valid = 1 bit, VPTsize = 5 bits, VPTaddr = 36 bits, RDbase = 16 bits
+ *
+ * Field sizes for Valid and size are mandated; field sizes for RDbase
+ * and VPT_addr are IMPDEF.
+ */
+#define GITS_VPE_SIZE 0x8ULL
+
+FIELD(VTE, VALID, 0, 1)
+FIELD(VTE, VPTSIZE, 1, 5)
+FIELD(VTE, VPTADDR, 6, 36)
+FIELD(VTE, RDBASE, 42, RDBASE_PROCNUM_LENGTH)
+
 /* Special interrupt IDs */
 #define INTID_SECURE 1020
 #define INTID_NONSECURE 1021
diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h
index 0f130494dd3..7d1cc0f7177 100644
--- a/include/hw/intc/arm_gicv3_its_common.h
+++ b/include/hw/intc/arm_gicv3_its_common.h
@@ -78,6 +78,7 @@ struct GICv3ITSState {
 
     TableDesc  dt;
     TableDesc  ct;
+    TableDesc  vpet;
     CmdQDesc   cq;
 
     Error *migration_blocker;
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 400cdf83794..609c29496a0 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -79,6 +79,12 @@ typedef enum ItsCmdResult {
     CMD_CONTINUE = 1,
 } ItsCmdResult;
 
+/* True if the ITS supports the GICv4 virtual LPI feature */
+static bool its_feature_virtual(GICv3ITSState *s)
+{
+    return s->typer & R_GITS_TYPER_VIRTUAL_MASK;
+}
+
 static inline bool intid_in_lpi_range(uint32_t id)
 {
     return id >= GICV3_LPI_INTID_START &&
@@ -946,6 +952,15 @@ static void extract_table_params(GICv3ITSState *s)
                 idbits = 16;
             }
             break;
+        case GITS_BASER_TYPE_VPE:
+            td = &s->vpet;
+            /*
+             * For QEMU vPEIDs are always 16 bits. (GICv4.1 allows an
+             * implementation to implement fewer bits and report this
+             * via GICD_TYPER2.)
+             */
+            idbits = 16;
+            break;
         default:
             /*
              * GITS_BASER<n>.TYPE is read-only, so GITS_BASER_RO_MASK
@@ -1425,6 +1440,7 @@ static void gicv3_its_reset(DeviceState *dev)
     /*
      * setting GITS_BASER0.Type = 0b001 (Device)
      *         GITS_BASER1.Type = 0b100 (Collection Table)
+     *         GITS_BASER2.Type = 0b010 (vPE) for GICv4 and later
      *         GITS_BASER<n>.Type,where n = 3 to 7 are 0b00 (Unimplemented)
      *         GITS_BASER<0,1>.Page_Size = 64KB
      * and default translation table entry size to 16 bytes
@@ -1442,6 +1458,15 @@ static void gicv3_its_reset(DeviceState *dev)
                              GITS_BASER_PAGESIZE_64K);
     s->baser[1] = FIELD_DP64(s->baser[1], GITS_BASER, ENTRYSIZE,
                              GITS_CTE_SIZE - 1);
+
+    if (its_feature_virtual(s)) {
+        s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, TYPE,
+                                 GITS_BASER_TYPE_VPE);
+        s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, PAGESIZE,
+                                 GITS_BASER_PAGESIZE_64K);
+        s->baser[2] = FIELD_DP64(s->baser[2], GITS_BASER, ENTRYSIZE,
+                                 GITS_VPE_SIZE - 1);
+    }
 }
 
 static void gicv3_its_post_load(GICv3ITSState *s)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 08/41] hw/intc/arm_gicv3_its: Implement VMAPI and VMAPTI
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (6 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 07/41] hw/intc/arm_gicv3_its: Implement GITS_BASER2 for GICv4 Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09  0:17   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 09/41] hw/intc/arm_gicv3_its: Implement VMAPP Peter Maydell
                   ` (33 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the GICv4 VMAPI and VMAPTI commands. These write
an interrupt translation table entry that maps (DeviceID,EventID)
to (vPEID,vINTID,doorbell). The only difference between VMAPI
and VMAPTI is that VMAPI assumes vINTID == EventID rather than
both being specified in the command packet.

(This code won't be reachable until we allow the GIC version to be
set to 4.  Support for reading this new virtual-interrupt DTE and
handling it correctly will be implemented in a later commit.)

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h |  9 ++++
 hw/intc/arm_gicv3_its.c  | 91 ++++++++++++++++++++++++++++++++++++++++
 hw/intc/trace-events     |  2 +
 3 files changed, 102 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 4613b9e59ba..d3670a8894e 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -329,6 +329,8 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define GITS_CMD_INVALL           0x0D
 #define GITS_CMD_MOVALL           0x0E
 #define GITS_CMD_DISCARD          0x0F
+#define GITS_CMD_VMAPTI           0x2A
+#define GITS_CMD_VMAPI            0x2B
 
 /* MAPC command fields */
 #define ICID_LENGTH                  16
@@ -368,6 +370,13 @@ FIELD(MOVI_0, DEVICEID, 32, 32)
 FIELD(MOVI_1, EVENTID, 0, 32)
 FIELD(MOVI_2, ICID, 0, 16)
 
+/* VMAPI, VMAPTI command fields */
+FIELD(VMAPTI_0, DEVICEID, 32, 32)
+FIELD(VMAPTI_1, EVENTID, 0, 32)
+FIELD(VMAPTI_1, VPEID, 32, 16)
+FIELD(VMAPTI_2, VINTID, 0, 32) /* VMAPTI only */
+FIELD(VMAPTI_2, DOORBELL, 32, 32)
+
 /*
  * 12 bytes Interrupt translation Table Entry size
  * as per Table 5.3 in GICv3 spec
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 609c29496a0..e1f26a205e4 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -91,6 +91,12 @@ static inline bool intid_in_lpi_range(uint32_t id)
         id < (1ULL << (GICD_TYPER_IDBITS + 1));
 }
 
+static inline bool valid_doorbell(uint32_t id)
+{
+    /* Doorbell fields may be an LPI, or 1023 to mean "no doorbell" */
+    return id == INTID_SPURIOUS || intid_in_lpi_range(id);
+}
+
 static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
 {
     uint64_t result = 0;
@@ -486,6 +492,85 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
     return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL;
 }
 
+static ItsCmdResult process_vmapti(GICv3ITSState *s, const uint64_t *cmdpkt,
+                                   bool ignore_vintid)
+{
+    uint32_t devid, eventid, vintid, doorbell, vpeid;
+    uint32_t num_eventids;
+    DTEntry dte;
+    ITEntry ite;
+
+    if (!its_feature_virtual(s)) {
+        return CMD_CONTINUE;
+    }
+
+    devid = FIELD_EX64(cmdpkt[0], VMAPTI_0, DEVICEID);
+    eventid = FIELD_EX64(cmdpkt[1], VMAPTI_1, EVENTID);
+    vpeid = FIELD_EX64(cmdpkt[1], VMAPTI_1, VPEID);
+    doorbell = FIELD_EX64(cmdpkt[2], VMAPTI_2, DOORBELL);
+    if (ignore_vintid) {
+        vintid = eventid;
+        trace_gicv3_its_cmd_vmapi(devid, eventid, vpeid, doorbell);
+    } else {
+        vintid = FIELD_EX64(cmdpkt[2], VMAPTI_2, VINTID);
+        trace_gicv3_its_cmd_vmapti(devid, eventid, vpeid, vintid, doorbell);
+    }
+
+    if (devid >= s->dt.num_entries) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid DeviceID 0x%x (must be less than 0x%x)\n",
+                      __func__, devid, s->dt.num_entries);
+        return CMD_CONTINUE;
+    }
+
+    if (get_dte(s, devid, &dte) != MEMTX_OK) {
+        return CMD_STALL;
+    }
+
+    if (!dte.valid) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: no entry in device table for DeviceID 0x%x\n",
+                      __func__, devid);
+        return CMD_CONTINUE;
+    }
+
+    num_eventids = 1ULL << (dte.size + 1);
+
+    if (eventid >= num_eventids) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: EventID 0x%x too large for DeviceID 0x%x "
+                      "(must be less than 0x%x)\n",
+                      __func__, eventid, devid, num_eventids);
+        return CMD_CONTINUE;
+    }
+    if (!intid_in_lpi_range(vintid)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: VIntID 0x%x not a valid LPI\n",
+                      __func__, vintid);
+        return CMD_CONTINUE;
+    }
+    if (!valid_doorbell(doorbell)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Doorbell 0x%x not 1023 and not a valid LPI\n",
+                      __func__, doorbell);
+        return CMD_CONTINUE;
+    }
+    if (vpeid >= s->vpet.num_entries) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: VPEID 0x%x out of range (must be less than 0x%x)\n",
+                      __func__, vpeid, s->vpet.num_entries);
+        return CMD_CONTINUE;
+    }
+    /* add ite entry to interrupt translation table */
+    ite.valid = true;
+    ite.inttype = ITE_INTTYPE_VIRTUAL;
+    ite.intid = vintid;
+    ite.icid = 0;
+    ite.doorbell = doorbell;
+    ite.vpeid = vpeid;
+    return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL;
+}
+
 /*
  * Update the Collection Table entry for @icid to @cte. Returns true
  * on success, false if there was a memory access error.
@@ -872,6 +957,12 @@ static void process_cmdq(GICv3ITSState *s)
         case GITS_CMD_MOVALL:
             result = process_movall(s, cmdpkt);
             break;
+        case GITS_CMD_VMAPTI:
+            result = process_vmapti(s, cmdpkt, false);
+            break;
+        case GITS_CMD_VMAPI:
+            result = process_vmapti(s, cmdpkt, true);
+            break;
         default:
             trace_gicv3_its_cmd_unknown(cmd);
             break;
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 53414aa1979..c6b2b9ab459 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -187,6 +187,8 @@ gicv3_its_cmd_mapti(uint32_t devid, uint32_t eventid, uint32_t icid, uint32_t in
 gicv3_its_cmd_inv(void) "GICv3 ITS: command INV or INVALL"
 gicv3_its_cmd_movall(uint64_t rd1, uint64_t rd2) "GICv3 ITS: command MOVALL RDbase1 0x%" PRIx64 " RDbase2 0x%" PRIx64
 gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MOVI DeviceID 0x%x EventID 0x%x ICID 0x%x"
+gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x"
+gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x"
 gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
 gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
 gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 09/41] hw/intc/arm_gicv3_its: Implement VMAPP
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (7 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 08/41] hw/intc/arm_gicv3_its: Implement VMAPI and VMAPTI Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09  0:21   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 10/41] hw/intc/arm_gicv3_its: Distinguish success and error cases of CMD_CONTINUE Peter Maydell
                   ` (32 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the GICv4 VMAPP command, which writes an entry to the vPE
table.

For GICv4.1 this command has extra fields in the command packet
and additional behaviour. We define the 4.1-only fields with the
FIELD macro, but only implement the GICv4.0 version of the command.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
GICv4.1 emulation is on my todo list, but I'm starting with
just the 4.0 parts first.
---
 hw/intc/gicv3_internal.h | 12 ++++++
 hw/intc/arm_gicv3_its.c  | 88 ++++++++++++++++++++++++++++++++++++++++
 hw/intc/trace-events     |  2 +
 3 files changed, 102 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index d3670a8894e..bbb8a20ce61 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -329,6 +329,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define GITS_CMD_INVALL           0x0D
 #define GITS_CMD_MOVALL           0x0E
 #define GITS_CMD_DISCARD          0x0F
+#define GITS_CMD_VMAPP            0x29
 #define GITS_CMD_VMAPTI           0x2A
 #define GITS_CMD_VMAPI            0x2B
 
@@ -377,6 +378,17 @@ FIELD(VMAPTI_1, VPEID, 32, 16)
 FIELD(VMAPTI_2, VINTID, 0, 32) /* VMAPTI only */
 FIELD(VMAPTI_2, DOORBELL, 32, 32)
 
+/* VMAPP command fields */
+FIELD(VMAPP_0, ALLOC, 8, 1) /* GICv4.1 only */
+FIELD(VMAPP_0, PTZ, 9, 1) /* GICv4.1 only */
+FIELD(VMAPP_0, VCONFADDR, 16, 36) /* GICv4.1 only */
+FIELD(VMAPP_1, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */
+FIELD(VMAPP_1, VPEID, 32, 16)
+FIELD(VMAPP_2, RDBASE, 16, 36)
+FIELD(VMAPP_2, V, 63, 1)
+FIELD(VMAPP_3, VPTSIZE, 0, 8) /* For GICv4.0, bits [7:6] are RES0 */
+FIELD(VMAPP_3, VPTADDR, 16, 36)
+
 /*
  * 12 bytes Interrupt translation Table Entry size
  * as per Table 5.3 in GICv3 spec
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index e1f26a205e4..ea2b4b9e5a7 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -61,6 +61,12 @@ typedef struct ITEntry {
     uint32_t vpeid;
 } ITEntry;
 
+typedef struct VTEntry {
+    bool valid;
+    unsigned vptsize;
+    uint32_t rdbase;
+    uint64_t vptaddr;
+} VTEntry;
 
 /*
  * The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options
@@ -842,6 +848,85 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
     return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE : CMD_STALL;
 }
 
+/*
+ * Update the vPE Table entry at index @vpeid with the entry @vte.
+ * Returns true on success, false if there was a memory access error.
+ */
+static bool update_vte(GICv3ITSState *s, uint32_t vpeid, const VTEntry *vte)
+{
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t entry_addr;
+    uint64_t vteval = 0;
+    MemTxResult res = MEMTX_OK;
+
+    trace_gicv3_its_vte_write(vpeid, vte->valid, vte->vptsize, vte->vptaddr,
+                              vte->rdbase);
+
+    if (vte->valid) {
+        vteval = FIELD_DP64(vteval, VTE, VALID, 1);
+        vteval = FIELD_DP64(vteval, VTE, VPTSIZE, vte->vptsize);
+        vteval = FIELD_DP64(vteval, VTE, VPTADDR, vte->vptaddr);
+        vteval = FIELD_DP64(vteval, VTE, RDBASE, vte->rdbase);
+    }
+
+    entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res);
+    if (res != MEMTX_OK) {
+        return false;
+    }
+    if (entry_addr == -1) {
+        /* No L2 table for this index: discard write and continue */
+        return true;
+    }
+    address_space_stq_le(as, entry_addr, vteval, MEMTXATTRS_UNSPECIFIED, &res);
+    return res == MEMTX_OK;
+}
+
+static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+    VTEntry vte;
+    uint32_t vpeid;
+
+    if (!its_feature_virtual(s)) {
+        return CMD_CONTINUE;
+    }
+
+    vpeid = FIELD_EX64(cmdpkt[1], VMAPP_1, VPEID);
+    vte.rdbase = FIELD_EX64(cmdpkt[2], VMAPP_2, RDBASE);
+    vte.valid = FIELD_EX64(cmdpkt[2], VMAPP_2, V);
+    vte.vptsize = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTSIZE);
+    vte.vptaddr = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTADDR);
+
+    trace_gicv3_its_cmd_vmapp(vpeid, vte.rdbase, vte.valid,
+                              vte.vptaddr, vte.vptsize);
+
+    /*
+     * For GICv4.0 the VPT_size field is only 5 bits, whereas we
+     * define our field macros to include the full GICv4.1 8 bits.
+     * The range check on VPT_size will catch the cases where
+     * the guest set the RES0-in-GICv4.0 bits [7:6].
+     */
+    if (vte.vptsize > FIELD_EX64(s->typer, GITS_TYPER, IDBITS)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid VPT_size 0x%x\n", __func__, vte.vptsize);
+        return CMD_CONTINUE;
+    }
+
+    if (vte.valid && vte.rdbase >= s->gicv3->num_cpu) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid rdbase 0x%x\n", __func__, vte.rdbase);
+        return CMD_CONTINUE;
+    }
+
+    if (vpeid >= s->vpet.num_entries) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: VPEID 0x%x out of range (must be less than 0x%x)\n",
+                      __func__, vpeid, s->vpet.num_entries);
+        return CMD_CONTINUE;
+    }
+
+    return update_vte(s, vpeid, &vte) ? CMD_CONTINUE : CMD_STALL;
+}
+
 /*
  * Current implementation blocks until all
  * commands are processed
@@ -963,6 +1048,9 @@ static void process_cmdq(GICv3ITSState *s)
         case GITS_CMD_VMAPI:
             result = process_vmapti(s, cmdpkt, true);
             break;
+        case GITS_CMD_VMAPP:
+            result = process_vmapp(s, cmdpkt);
+            break;
         default:
             trace_gicv3_its_cmd_unknown(cmd);
             break;
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index c6b2b9ab459..2fcc9e40e55 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -189,6 +189,7 @@ gicv3_its_cmd_movall(uint64_t rd1, uint64_t rd2) "GICv3 ITS: command MOVALL RDba
 gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MOVI DeviceID 0x%x EventID 0x%x ICID 0x%x"
 gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x"
 gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x"
+gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x"
 gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
 gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
 gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
@@ -199,6 +200,7 @@ gicv3_its_ite_write(uint64_t ittaddr, uint32_t eventid, int valid, int inttype,
 gicv3_its_dte_read(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table read for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
 gicv3_its_dte_write(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table write for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
 gicv3_its_dte_read_fault(uint32_t devid) "GICv3 ITS: Device Table read for DeviceID 0x%x: faulted"
+gicv3_its_vte_write(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table write for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x"
 
 # armv7m_nvic.c
 nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 10/41] hw/intc/arm_gicv3_its: Distinguish success and error cases of CMD_CONTINUE
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (8 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 09/41] hw/intc/arm_gicv3_its: Implement VMAPP Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09  0:23   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 11/41] hw/intc/arm_gicv3_its: Factor out "find ITE given devid, eventid" Peter Maydell
                   ` (31 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

In the ItsCmdResult enum, we currently distinguish only CMD_STALL
(failure, stall processing of the command queue) and CMD_CONTINUE
(keep processing the queue), and we use the latter both for "there
was a parameter error, go on to the next command" and "the command
succeeded, go on to the next command".  Sometimes we would like to
distinguish those two cases, so add CMD_CONTINUE_OK to the enum to
represent the success situation, and use it in the relevant places.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_its.c | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index ea2b4b9e5a7..ba1893c072b 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -78,11 +78,13 @@ typedef struct VTEntry {
  * and continue processing.
  * The process_* functions which handle individual ITS commands all
  * return an ItsCmdResult which tells process_cmdq() whether it should
- * stall or keep going.
+ * stall, keep going because of an error, or keep going because the
+ * command was a success.
  */
 typedef enum ItsCmdResult {
     CMD_STALL = 0,
     CMD_CONTINUE = 1,
+    CMD_CONTINUE_OK = 2,
 } ItsCmdResult;
 
 /* True if the ITS supports the GICv4 virtual LPI feature */
@@ -400,9 +402,9 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
         ITEntry ite = {};
         /* remove mapping from interrupt translation table */
         ite.valid = false;
-        return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL;
+        return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
     }
-    return CMD_CONTINUE;
+    return CMD_CONTINUE_OK;
 }
 
 static ItsCmdResult process_its_cmd(GICv3ITSState *s, const uint64_t *cmdpkt,
@@ -495,7 +497,7 @@ static ItsCmdResult process_mapti(GICv3ITSState *s, const uint64_t *cmdpkt,
     ite.icid = icid;
     ite.doorbell = INTID_SPURIOUS;
     ite.vpeid = 0;
-    return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL;
+    return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
 static ItsCmdResult process_vmapti(GICv3ITSState *s, const uint64_t *cmdpkt,
@@ -574,7 +576,7 @@ static ItsCmdResult process_vmapti(GICv3ITSState *s, const uint64_t *cmdpkt,
     ite.icid = 0;
     ite.doorbell = doorbell;
     ite.vpeid = vpeid;
-    return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE : CMD_STALL;
+    return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
 /*
@@ -635,7 +637,7 @@ static ItsCmdResult process_mapc(GICv3ITSState *s, const uint64_t *cmdpkt)
         return CMD_CONTINUE;
     }
 
-    return update_cte(s, icid, &cte) ? CMD_CONTINUE : CMD_STALL;
+    return update_cte(s, icid, &cte) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
 /*
@@ -696,7 +698,7 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, const uint64_t *cmdpkt)
         return CMD_CONTINUE;
     }
 
-    return update_dte(s, devid, &dte) ? CMD_CONTINUE : CMD_STALL;
+    return update_dte(s, devid, &dte) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
 static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt)
@@ -725,13 +727,13 @@ static ItsCmdResult process_movall(GICv3ITSState *s, const uint64_t *cmdpkt)
 
     if (rd1 == rd2) {
         /* Move to same target must succeed as a no-op */
-        return CMD_CONTINUE;
+        return CMD_CONTINUE_OK;
     }
 
     /* Move all pending LPIs from redistributor 1 to redistributor 2 */
     gicv3_redist_movall_lpis(&s->gicv3->cpu[rd1], &s->gicv3->cpu[rd2]);
 
-    return CMD_CONTINUE;
+    return CMD_CONTINUE_OK;
 }
 
 static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
@@ -845,7 +847,7 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
 
     /* Update the ICID field in the interrupt translation table entry */
     old_ite.icid = new_icid;
-    return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE : CMD_STALL;
+    return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
 /*
@@ -924,7 +926,7 @@ static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt)
         return CMD_CONTINUE;
     }
 
-    return update_vte(s, vpeid, &vte) ? CMD_CONTINUE : CMD_STALL;
+    return update_vte(s, vpeid, &vte) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
 /*
@@ -963,7 +965,7 @@ static void process_cmdq(GICv3ITSState *s)
     }
 
     while (wr_offset != rd_offset) {
-        ItsCmdResult result = CMD_CONTINUE;
+        ItsCmdResult result = CMD_CONTINUE_OK;
         void *hostmem;
         hwaddr buflen;
         uint64_t cmdpkt[GITS_CMDQ_ENTRY_WORDS];
@@ -1055,7 +1057,8 @@ static void process_cmdq(GICv3ITSState *s)
             trace_gicv3_its_cmd_unknown(cmd);
             break;
         }
-        if (result == CMD_CONTINUE) {
+        if (result != CMD_STALL) {
+            /* CMD_CONTINUE or CMD_CONTINUE_OK */
             rd_offset++;
             rd_offset %= s->cq.num_entries;
             s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 11/41] hw/intc/arm_gicv3_its: Factor out "find ITE given devid, eventid"
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (9 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 10/41] hw/intc/arm_gicv3_its: Distinguish success and error cases of CMD_CONTINUE Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09  0:39   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 12/41] hw/intc/arm_gicv3_its: Factor out CTE lookup sequence Peter Maydell
                   ` (30 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The operation of finding an interrupt table entry given a (DeviceID,
EventID) pair is necessary in multiple different ITS commands.  The
process requires first using the DeviceID as an index into the device
table to find the DTE, and then useng the EventID as an index into
the interrupt table specified by that DTE to find the ITE.  We also
need to handle all the possible error cases: indexes out of range,
table memory not readable, table entries not valid.

Factor this out into a separate lookup_ite() function which we
can then call from the places where we were previously open-coding
this sequence. We'll also need this for some of the new GICv4.0
commands.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_its.c | 124 +++++++++++++++++++++-------------------
 1 file changed, 64 insertions(+), 60 deletions(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index ba1893c072b..fe1bea2dd81 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -314,6 +314,60 @@ out:
     return res;
 }
 
+/*
+ * Given a (DeviceID, EventID), look up the corresponding ITE, including
+ * checking for the various invalid-value cases. If we find a valid ITE,
+ * fill in @ite and @dte and return CMD_CONTINUE_OK. Otherwise return
+ * CMD_STALL or CMD_CONTINUE as appropriate (and the contents of @ite
+ * should not be relied on).
+ *
+ * The string @who is purely for the LOG_GUEST_ERROR messages,
+ * and should indicate the name of the calling function or similar.
+ */
+static ItsCmdResult lookup_ite(GICv3ITSState *s, const char *who,
+                               uint32_t devid, uint32_t eventid, ITEntry *ite,
+                               DTEntry *dte)
+{
+    uint64_t num_eventids;
+
+    if (devid >= s->dt.num_entries) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid command attributes: devid %d>=%d",
+                      who, devid, s->dt.num_entries);
+        return CMD_CONTINUE;
+    }
+
+    if (get_dte(s, devid, dte) != MEMTX_OK) {
+        return CMD_STALL;
+    }
+    if (!dte->valid) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid command attributes: "
+                      "invalid dte for %d\n", who, devid);
+        return CMD_CONTINUE;
+    }
+
+    num_eventids = 1ULL << (dte->size + 1);
+    if (eventid >= num_eventids) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid command attributes: eventid %d >= %"
+                      PRId64 "\n", who, eventid, num_eventids);
+        return CMD_CONTINUE;
+    }
+
+    if (get_ite(s, eventid, dte, ite) != MEMTX_OK) {
+        return CMD_STALL;
+    }
+
+    if (!ite->valid) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid command attributes: invalid ITE\n", who);
+        return CMD_CONTINUE;
+    }
+
+    return CMD_CONTINUE_OK;
+}
+
 /*
  * This function handles the processing of following commands based on
  * the ItsCmdType parameter passed:-
@@ -325,42 +379,17 @@ out:
 static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
                                        uint32_t eventid, ItsCmdType cmd)
 {
-    uint64_t num_eventids;
     DTEntry dte;
     CTEntry cte;
     ITEntry ite;
+    ItsCmdResult cmdres;
 
-    if (devid >= s->dt.num_entries) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: devid %d>=%d",
-                      __func__, devid, s->dt.num_entries);
-        return CMD_CONTINUE;
+    cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
     }
 
-    if (get_dte(s, devid, &dte) != MEMTX_OK) {
-        return CMD_STALL;
-    }
-    if (!dte.valid) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: "
-                      "invalid dte for %d\n", __func__, devid);
-        return CMD_CONTINUE;
-    }
-
-    num_eventids = 1ULL << (dte.size + 1);
-    if (eventid >= num_eventids) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: eventid %d >= %"
-                      PRId64 "\n",
-                      __func__, eventid, num_eventids);
-        return CMD_CONTINUE;
-    }
-
-    if (get_ite(s, eventid, &dte, &ite) != MEMTX_OK) {
-        return CMD_STALL;
-    }
-
-    if (!ite.valid || ite.inttype != ITE_INTTYPE_PHYSICAL) {
+    if (ite.inttype != ITE_INTTYPE_PHYSICAL) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "%s: invalid command attributes: invalid ITE\n",
                       __func__);
@@ -740,10 +769,10 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
 {
     uint32_t devid, eventid;
     uint16_t new_icid;
-    uint64_t num_eventids;
     DTEntry dte;
     CTEntry old_cte, new_cte;
     ITEntry old_ite;
+    ItsCmdResult cmdres;
 
     devid = FIELD_EX64(cmdpkt[0], MOVI_0, DEVICEID);
     eventid = FIELD_EX64(cmdpkt[1], MOVI_1, EVENTID);
@@ -751,37 +780,12 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
 
     trace_gicv3_its_cmd_movi(devid, eventid, new_icid);
 
-    if (devid >= s->dt.num_entries) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: devid %d>=%d",
-                      __func__, devid, s->dt.num_entries);
-        return CMD_CONTINUE;
-    }
-    if (get_dte(s, devid, &dte) != MEMTX_OK) {
-        return CMD_STALL;
+    cmdres = lookup_ite(s, __func__, devid, eventid, &old_ite, &dte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
     }
 
-    if (!dte.valid) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: "
-                      "invalid dte for %d\n", __func__, devid);
-        return CMD_CONTINUE;
-    }
-
-    num_eventids = 1ULL << (dte.size + 1);
-    if (eventid >= num_eventids) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: eventid %d >= %"
-                      PRId64 "\n",
-                      __func__, eventid, num_eventids);
-        return CMD_CONTINUE;
-    }
-
-    if (get_ite(s, eventid, &dte, &old_ite) != MEMTX_OK) {
-        return CMD_STALL;
-    }
-
-    if (!old_ite.valid || old_ite.inttype != ITE_INTTYPE_PHYSICAL) {
+    if (old_ite.inttype != ITE_INTTYPE_PHYSICAL) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "%s: invalid command attributes: invalid ITE\n",
                       __func__);
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 12/41] hw/intc/arm_gicv3_its: Factor out CTE lookup sequence
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (10 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 11/41] hw/intc/arm_gicv3_its: Factor out "find ITE given devid, eventid" Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09  0:47   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 13/41] hw/intc/arm_gicv3_its: Split out process_its_cmd() physical interrupt code Peter Maydell
                   ` (29 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Factor out the sequence of looking up a CTE from an ICID including
the validity and error checks.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
I think process_movi() in particular is now a lot cleaner
to read with all the error-checking factored out.
---
 hw/intc/arm_gicv3_its.c | 109 ++++++++++++++--------------------------
 1 file changed, 39 insertions(+), 70 deletions(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index fe1bea2dd81..2cbac76256d 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -368,6 +368,36 @@ static ItsCmdResult lookup_ite(GICv3ITSState *s, const char *who,
     return CMD_CONTINUE_OK;
 }
 
+/*
+ * Given an ICID, look up the corresponding CTE, including checking for various
+ * invalid-value cases. If we find a valid CTE, fill in @cte and return
+ * CMD_CONTINUE_OK; otherwise return CMD_STALL or CMD_CONTINUE (and the
+ * contents of @cte should not be relied on).
+ *
+ * The string @who is purely for the LOG_GUEST_ERROR messages,
+ * and should indicate the name of the calling function or similar.
+ */
+static ItsCmdResult lookup_cte(GICv3ITSState *s, const char *who,
+                               uint32_t icid, CTEntry *cte)
+{
+    if (icid >= s->ct.num_entries) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid ICID 0x%x\n", who, icid);
+        return CMD_CONTINUE;
+    }
+    if (get_cte(s, icid, cte) != MEMTX_OK) {
+        return CMD_STALL;
+    }
+    if (!cte->valid) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid CTE\n", who);
+        return CMD_CONTINUE;
+    }
+    if (cte->rdbase >= s->gicv3->num_cpu) {
+        return CMD_CONTINUE;
+    }
+    return CMD_CONTINUE_OK;
+}
+
+
 /*
  * This function handles the processing of following commands based on
  * the ItsCmdType parameter passed:-
@@ -396,29 +426,9 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
         return CMD_CONTINUE;
     }
 
-    if (ite.icid >= s->ct.num_entries) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
-                      __func__, ite.icid);
-        return CMD_CONTINUE;
-    }
-
-    if (get_cte(s, ite.icid, &cte) != MEMTX_OK) {
-        return CMD_STALL;
-    }
-    if (!cte.valid) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: invalid CTE\n",
-                      __func__);
-        return CMD_CONTINUE;
-    }
-
-    /*
-     * Current implementation only supports rdbase == procnum
-     * Hence rdbase physical address is ignored
-     */
-    if (cte.rdbase >= s->gicv3->num_cpu) {
-        return CMD_CONTINUE;
+    cmdres = lookup_cte(s, __func__, ite.icid, &cte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
     }
 
     if ((cmd == CLEAR) || (cmd == DISCARD)) {
@@ -792,54 +802,13 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
         return CMD_CONTINUE;
     }
 
-    if (old_ite.icid >= s->ct.num_entries) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
-                      __func__, old_ite.icid);
-        return CMD_CONTINUE;
+    cmdres = lookup_cte(s, __func__, old_ite.icid, &old_cte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
     }
-
-    if (new_icid >= s->ct.num_entries) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: ICID 0x%x\n",
-                      __func__, new_icid);
-        return CMD_CONTINUE;
-    }
-
-    if (get_cte(s, old_ite.icid, &old_cte) != MEMTX_OK) {
-        return CMD_STALL;
-    }
-    if (!old_cte.valid) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: "
-                      "invalid CTE for old ICID 0x%x\n",
-                      __func__, old_ite.icid);
-        return CMD_CONTINUE;
-    }
-
-    if (get_cte(s, new_icid, &new_cte) != MEMTX_OK) {
-        return CMD_STALL;
-    }
-    if (!new_cte.valid) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: "
-                      "invalid CTE for new ICID 0x%x\n",
-                      __func__, new_icid);
-        return CMD_CONTINUE;
-    }
-
-    if (old_cte.rdbase >= s->gicv3->num_cpu) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: CTE has invalid rdbase 0x%x\n",
-                      __func__, old_cte.rdbase);
-        return CMD_CONTINUE;
-    }
-
-    if (new_cte.rdbase >= s->gicv3->num_cpu) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: CTE has invalid rdbase 0x%x\n",
-                      __func__, new_cte.rdbase);
-        return CMD_CONTINUE;
+    cmdres = lookup_cte(s, __func__, new_icid, &new_cte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
     }
 
     if (old_cte.rdbase != new_cte.rdbase) {
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 13/41] hw/intc/arm_gicv3_its: Split out process_its_cmd() physical interrupt code
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (11 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 12/41] hw/intc/arm_gicv3_its: Factor out CTE lookup sequence Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 17:55   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 14/41] hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd() Peter Maydell
                   ` (28 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Split the part of process_its_cmd() which is specific to physical
interrupts into its own function.  This is the part which starts by
taking the ICID and looking it up in the collection table.  The
handling of virtual interrupts is significantly different (involving
a lookup in the vPE table) so structuring the code with one
sub-function for the physical interrupt case and one for the virtual
interrupt case will be clearer than putting both cases in one large
function.

The code for handling the "remove mapping from ITE" for the DISCARD
command remains in process_its_cmd() because it is common to both
virtual and physical interrupts.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_its.c | 51 ++++++++++++++++++++++++++---------------
 1 file changed, 33 insertions(+), 18 deletions(-)

diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 2cbac76256d..8ea1fc366d3 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -397,6 +397,19 @@ static ItsCmdResult lookup_cte(GICv3ITSState *s, const char *who,
     return CMD_CONTINUE_OK;
 }
 
+static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
+                                         int irqlevel)
+{
+    CTEntry cte;
+    ItsCmdResult cmdres;
+
+    cmdres = lookup_cte(s, __func__, ite->icid, &cte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
+    }
+    gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite->intid, irqlevel);
+    return CMD_CONTINUE_OK;
+}
 
 /*
  * This function handles the processing of following commands based on
@@ -410,34 +423,36 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
                                        uint32_t eventid, ItsCmdType cmd)
 {
     DTEntry dte;
-    CTEntry cte;
     ITEntry ite;
     ItsCmdResult cmdres;
+    int irqlevel;
 
     cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
     if (cmdres != CMD_CONTINUE_OK) {
         return cmdres;
     }
 
-    if (ite.inttype != ITE_INTTYPE_PHYSICAL) {
-        qemu_log_mask(LOG_GUEST_ERROR,
-                      "%s: invalid command attributes: invalid ITE\n",
-                      __func__);
-        return CMD_CONTINUE;
+    irqlevel = (cmd == CLEAR || cmd == DISCARD) ? 0 : 1;
+
+    switch (ite.inttype) {
+    case ITE_INTTYPE_PHYSICAL:
+        cmdres = process_its_cmd_phys(s, &ite, irqlevel);
+        break;
+    case ITE_INTTYPE_VIRTUAL:
+        if (!its_feature_virtual(s)) {
+            /* Can't happen unless guest is illegally writing to table memory */
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: invalid type %d in ITE (table corrupted?)\n",
+                          __func__, ite.inttype);
+            return CMD_CONTINUE;
+        }
+        /* The GICv4 virtual interrupt handling will go here */
+        g_assert_not_reached();
+    default:
+        g_assert_not_reached();
     }
 
-    cmdres = lookup_cte(s, __func__, ite.icid, &cte);
-    if (cmdres != CMD_CONTINUE_OK) {
-        return cmdres;
-    }
-
-    if ((cmd == CLEAR) || (cmd == DISCARD)) {
-        gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 0);
-    } else {
-        gicv3_redist_process_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid, 1);
-    }
-
-    if (cmd == DISCARD) {
+    if (cmdres == CMD_CONTINUE_OK && cmd == DISCARD) {
         ITEntry ite = {};
         /* remove mapping from interrupt translation table */
         ite.valid = false;
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 14/41] hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (12 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 13/41] hw/intc/arm_gicv3_its: Split out process_its_cmd() physical interrupt code Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:02   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 15/41] hw/intc/arm_gicv3: Keep pointers to every connected ITS Peter Maydell
                   ` (27 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

For GICv4, interrupt table entries read by process_its_cmd() may
indicate virtual LPIs which are to be directly injected into a VM.
Implement the ITS side of the code for handling this.  This is
similar to the existing handling of physical LPIs, but instead of
looking up a collection ID in a collection table, we look up a vPEID
in a vPE table.  As with the physical LPIs, we leave the rest of the
work to code in the redistributor device.

The redistributor half will be implemented in a later commit;
for now we just provide a stub function which does nothing.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h   | 17 +++++++
 hw/intc/arm_gicv3_its.c    | 99 +++++++++++++++++++++++++++++++++++++-
 hw/intc/arm_gicv3_redist.c |  9 ++++
 hw/intc/trace-events       |  2 +
 4 files changed, 125 insertions(+), 2 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index bbb8a20ce61..6e22c8072e9 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -527,6 +527,23 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
 void gicv3_dist_set_irq(GICv3State *s, int irq, int level);
 void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level);
 void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level);
+/**
+ * gicv3_redist_process_vlpi:
+ * @cs: GICv3CPUState
+ * @irq: (virtual) interrupt number
+ * @vptaddr: (guest) address of VLPI table
+ * @doorbell: doorbell (physical) interrupt number (1023 for "no doorbell")
+ * @level: level to set @irq to
+ *
+ * Process a virtual LPI being directly injected by the ITS. This function
+ * will update the VLPI table specified by @vptaddr and @vptsize. If the
+ * vCPU corresponding to that VLPI table is currently running on
+ * the CPU associated with this redistributor, directly inject the VLPI
+ * @irq. If the vCPU is not running on this CPU, raise the doorbell
+ * interrupt instead.
+ */
+void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
+                               int doorbell, int level);
 void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
 /**
  * gicv3_redist_update_lpi:
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 8ea1fc366d3..21bc1a6c18b 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -314,6 +314,42 @@ out:
     return res;
 }
 
+/*
+ * Read the vPE Table entry at index @vpeid. On success (including
+ * successfully determining that there is no valid entry for this index),
+ * we return MEMTX_OK and populate the VTEntry struct accordingly.
+ * If there is an error reading memory then we return the error code.
+ */
+static MemTxResult get_vte(GICv3ITSState *s, uint32_t vpeid, VTEntry *vte)
+{
+    MemTxResult res = MEMTX_OK;
+    AddressSpace *as = &s->gicv3->dma_as;
+    uint64_t entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res);
+    uint64_t vteval;
+
+    if (entry_addr == -1) {
+        /* No L2 table entry, i.e. no valid VTE, or a memory error */
+        vte->valid = false;
+        goto out;
+    }
+    vteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res);
+    if (res != MEMTX_OK) {
+        goto out;
+    }
+    vte->valid = FIELD_EX64(vteval, VTE, VALID);
+    vte->vptsize = FIELD_EX64(vteval, VTE, VPTSIZE);
+    vte->vptaddr = FIELD_EX64(vteval, VTE, VPTADDR);
+    vte->rdbase = FIELD_EX64(vteval, VTE, RDBASE);
+out:
+    if (res != MEMTX_OK) {
+        trace_gicv3_its_vte_read_fault(vpeid);
+    } else {
+        trace_gicv3_its_vte_read(vpeid, vte->valid, vte->vptsize,
+                                 vte->vptaddr, vte->rdbase);
+    }
+    return res;
+}
+
 /*
  * Given a (DeviceID, EventID), look up the corresponding ITE, including
  * checking for the various invalid-value cases. If we find a valid ITE,
@@ -397,6 +433,38 @@ static ItsCmdResult lookup_cte(GICv3ITSState *s, const char *who,
     return CMD_CONTINUE_OK;
 }
 
+/*
+ * Given a VPEID, look up the corresponding VTE, including checking
+ * for various invalid-value cases. if we find a valid VTE, fill in @vte
+ * and return CMD_CONTINUE_OK; otherwise return CMD_STALL or CMD_CONTINUE
+ * (and the contents of @vte should not be relied on).
+ *
+ * The string @who is purely for the LOG_GUEST_ERROR messages,
+ * and should indicate the name of the calling function or similar.
+ */
+static ItsCmdResult lookup_vte(GICv3ITSState *s, const char *who,
+                               uint32_t vpeid, VTEntry *vte)
+{
+    if (vpeid >= s->vpet.num_entries) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid VPEID 0x%x\n", who, vpeid);
+        return CMD_CONTINUE;
+    }
+
+    if (get_vte(s, vpeid, vte) != MEMTX_OK) {
+        return CMD_STALL;
+    }
+    if (!vte->valid) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid VTE for VPEID 0x%x\n", who, vpeid);
+        return CMD_CONTINUE;
+    }
+
+    if (vte->rdbase >= s->gicv3->num_cpu) {
+        return CMD_CONTINUE;
+    }
+    return CMD_CONTINUE_OK;
+}
+
 static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
                                          int irqlevel)
 {
@@ -411,6 +479,33 @@ static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
     return CMD_CONTINUE_OK;
 }
 
+static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite,
+                                         int irqlevel)
+{
+    VTEntry vte;
+    ItsCmdResult cmdres;
+
+    cmdres = lookup_vte(s, __func__, ite->vpeid, &vte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
+    }
+
+    if (!intid_in_lpi_range(ite->intid) ||
+        ite->intid >= (1ULL << (vte.vptsize + 1))) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: intid 0x%x out of range\n",
+                      __func__, ite->intid);
+        return CMD_CONTINUE;
+    }
+
+    /*
+     * For QEMU the actual pending of the vLPI is handled in the
+     * redistributor code
+     */
+    gicv3_redist_process_vlpi(&s->gicv3->cpu[vte.rdbase], ite->intid,
+                              vte.vptaddr << 16, ite->doorbell, irqlevel);
+    return CMD_CONTINUE_OK;
+}
+
 /*
  * This function handles the processing of following commands based on
  * the ItsCmdType parameter passed:-
@@ -446,8 +541,8 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
                           __func__, ite.inttype);
             return CMD_CONTINUE;
         }
-        /* The GICv4 virtual interrupt handling will go here */
-        g_assert_not_reached();
+        cmdres = process_its_cmd_virt(s, &ite, irqlevel);
+        break;
     default:
         g_assert_not_reached();
     }
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index dc9729e8395..b08b599c887 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -788,6 +788,15 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
     gicv3_redist_update_lpi(dest);
 }
 
+void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
+                               int doorbell, int level)
+{
+    /*
+     * The redistributor handling for being handed a VLPI by the ITS
+     * will be added in a subsequent commit.
+     */
+}
+
 void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
 {
     /* Update redistributor state for a change in an external PPI input line */
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 2fcc9e40e55..d529914eca2 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -200,6 +200,8 @@ gicv3_its_ite_write(uint64_t ittaddr, uint32_t eventid, int valid, int inttype,
 gicv3_its_dte_read(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table read for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
 gicv3_its_dte_write(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table write for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64
 gicv3_its_dte_read_fault(uint32_t devid) "GICv3 ITS: Device Table read for DeviceID 0x%x: faulted"
+gicv3_its_vte_read(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table read for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x"
+gicv3_its_vte_read_fault(uint32_t vpeid) "GICv3 ITS: vPE Table read for vPEID 0x%x: faulted"
 gicv3_its_vte_write(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table write for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x"
 
 # armv7m_nvic.c
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 15/41] hw/intc/arm_gicv3: Keep pointers to every connected ITS
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (13 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 14/41] hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:06   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 16/41] hw/intc/arm_gicv3_its: Implement VMOVP Peter Maydell
                   ` (26 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The GICv4 ITS VMOVP command's semantics require it to perform the
operation on every ITS connected to the same GIC that the ITS that
received the command is attached to.  This means that the GIC object
needs to keep a pointer to every ITS that is connected to it
(previously it was sufficient for the ITS to have a pointer to its
GIC).

Add a glib ptrarray to the GICv3 object which holds pointers to every
connected ITS, and make the ITS add itself to the array for the GIC
it is connected to when it is realized.

Note that currently all QEMU machine types with an ITS have exactly
one ITS in the system, so typically the length of this ptrarray will
be 1.  Multiple ITSes are typically used to improve performance on
real hardware, so we wouldn't need to have more than one unless we
were modelling a real machine type that had multile ITSes.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h           | 9 +++++++++
 include/hw/intc/arm_gicv3_common.h | 2 ++
 hw/intc/arm_gicv3_common.c         | 2 ++
 hw/intc/arm_gicv3_its.c            | 2 ++
 hw/intc/arm_gicv3_its_kvm.c        | 2 ++
 5 files changed, 17 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 6e22c8072e9..69a59daf867 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -709,4 +709,13 @@ static inline void gicv3_cache_all_target_cpustates(GICv3State *s)
 
 void gicv3_set_gicv3state(CPUState *cpu, GICv3CPUState *s);
 
+/*
+ * The ITS should call this when it is realized to add itself
+ * to its GIC's list of connected ITSes.
+ */
+static inline void gicv3_add_its(GICv3State *s, DeviceState *its)
+{
+    g_ptr_array_add(s->itslist, its);
+}
+
 #endif /* QEMU_ARM_GICV3_INTERNAL_H */
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index fc38e4b7dca..08b27789385 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -272,6 +272,8 @@ struct GICv3State {
     uint32_t gicd_nsacr[DIV_ROUND_UP(GICV3_MAXIRQ, 16)];
 
     GICv3CPUState *cpu;
+    /* List of all ITSes connected to this GIC */
+    GPtrArray *itslist;
 };
 
 #define GICV3_BITMAP_ACCESSORS(BMP)                                     \
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index c797c82786b..dcc5ce28c6a 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -414,6 +414,8 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
         cpuidx += s->redist_region_count[i];
         s->cpu[cpuidx - 1].gicr_typer |= GICR_TYPER_LAST;
     }
+
+    s->itslist = g_ptr_array_new();
 }
 
 static void arm_gicv3_finalize(Object *obj)
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 21bc1a6c18b..6ff3c3b0348 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1680,6 +1680,8 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
         }
     }
 
+    gicv3_add_its(s->gicv3, dev);
+
     gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
 
     /* set the ITS default features supported */
diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c
index 0b4cbed28b3..529c7bd4946 100644
--- a/hw/intc/arm_gicv3_its_kvm.c
+++ b/hw/intc/arm_gicv3_its_kvm.c
@@ -106,6 +106,8 @@ static void kvm_arm_its_realize(DeviceState *dev, Error **errp)
     kvm_arm_register_device(&s->iomem_its_cntrl, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
                             KVM_VGIC_ITS_ADDR_TYPE, s->dev_fd, 0);
 
+    gicv3_add_its(s->gicv3, dev);
+
     gicv3_its_init_mmio(s, NULL, NULL);
 
     if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 16/41] hw/intc/arm_gicv3_its: Implement VMOVP
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (14 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 15/41] hw/intc/arm_gicv3: Keep pointers to every connected ITS Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:13   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC Peter Maydell
                   ` (25 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the GICv4 VMOVP command, which updates an entry in the vPE
table to change its rdbase field. This command is unique in the ITS
command set because its effects must be propagated to all the other
ITSes connected to the same GIC as the ITS which executes the VMOVP
command.

The GICv4 spec allows two implementation choices for handling the
propagation to other ITSes:
 * If GITS_TYPER.VMOVP is 1, the guest only needs to issue the command
   on one ITS, and the implementation handles the propagation to
   all ITSes
 * If GITS_TYPER.VMOVP is 0, the guest must issue the command on
   every ITS, and arrange for the ITSes to synchronize the updates
   with each other by setting ITSList and Sequence Number fields
   in the command packets

We choose the GITS_TYPER.VMOVP = 1 approach, and synchronously
execute the update on every ITS.

For GICv4.1 this command has extra fields in the command packet and
additional behaviour.  We define the 4.1-only fields with the FIELD
macro, but only implement the GICv4.0 version of the command.

Note that we don't update the reported GITS_TYPER value here;
we'll do that later in a commit which updates all the reported
feature bit and ID register values for GICv4.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h | 18 ++++++++++
 hw/intc/arm_gicv3_its.c  | 75 ++++++++++++++++++++++++++++++++++++++++
 hw/intc/trace-events     |  1 +
 3 files changed, 94 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 69a59daf867..c1467ce7263 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -329,6 +329,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define GITS_CMD_INVALL           0x0D
 #define GITS_CMD_MOVALL           0x0E
 #define GITS_CMD_DISCARD          0x0F
+#define GITS_CMD_VMOVP            0x22
 #define GITS_CMD_VMAPP            0x29
 #define GITS_CMD_VMAPTI           0x2A
 #define GITS_CMD_VMAPI            0x2B
@@ -389,6 +390,14 @@ FIELD(VMAPP_2, V, 63, 1)
 FIELD(VMAPP_3, VPTSIZE, 0, 8) /* For GICv4.0, bits [7:6] are RES0 */
 FIELD(VMAPP_3, VPTADDR, 16, 36)
 
+/* VMOVP command fields */
+FIELD(VMOVP_0, SEQNUM, 32, 16) /* not used for GITS_TYPER.VMOVP == 1 */
+FIELD(VMOVP_1, ITSLIST, 0, 16) /* not used for GITS_TYPER.VMOVP == 1 */
+FIELD(VMOVP_1, VPEID, 32, 16)
+FIELD(VMOVP_2, RDBASE, 16, 36)
+FIELD(VMOVP_2, DB, 63, 1) /* GICv4.1 only */
+FIELD(VMOVP_3, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */
+
 /*
  * 12 bytes Interrupt translation Table Entry size
  * as per Table 5.3 in GICv3 spec
@@ -718,4 +727,13 @@ static inline void gicv3_add_its(GICv3State *s, DeviceState *its)
     g_ptr_array_add(s->itslist, its);
 }
 
+/*
+ * The ITS can use this for operations that must be performed on
+ * every ITS connected to the same GIC that it is
+ */
+static inline void gicv3_foreach_its(GICv3State *s, GFunc func, void *opaque)
+{
+    g_ptr_array_foreach(s->itslist, func, opaque);
+}
+
 #endif /* QEMU_ARM_GICV3_INTERNAL_H */
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 6ff3c3b0348..bd82c84b46d 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1012,6 +1012,78 @@ static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt)
     return update_vte(s, vpeid, &vte) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
+typedef struct VmovpCallbackData {
+    uint64_t rdbase;
+    uint32_t vpeid;
+    /*
+     * Overall command result. If more than one callback finds an
+     * error, STALL beats CONTINUE.
+     */
+    ItsCmdResult result;
+} VmovpCallbackData;
+
+static void vmovp_callback(gpointer data, gpointer opaque)
+{
+    /*
+     * This function is called to update the VPEID field in a VPE
+     * table entry for this ITS. This might be because of a VMOVP
+     * command executed on any ITS that is connected to the same GIC
+     * as this ITS.  We need to read the VPE table entry for the VPEID
+     * and update its RDBASE field.
+     */
+    GICv3ITSState *s = data;
+    VmovpCallbackData *cbdata = opaque;
+    VTEntry vte;
+    ItsCmdResult cmdres;
+
+    cmdres = lookup_vte(s, __func__, cbdata->vpeid, &vte);
+    switch (cmdres) {
+    case CMD_STALL:
+        cbdata->result = CMD_STALL;
+        return;
+    case CMD_CONTINUE:
+        if (cbdata->result != CMD_STALL) {
+            cbdata->result = CMD_CONTINUE;
+        }
+        return;
+    case CMD_CONTINUE_OK:
+        break;
+    }
+
+    vte.rdbase = cbdata->rdbase;
+    if (!update_vte(s, cbdata->vpeid, &vte)) {
+        cbdata->result = CMD_STALL;
+    }
+}
+
+static ItsCmdResult process_vmovp(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+    VmovpCallbackData cbdata;
+
+    if (!its_feature_virtual(s)) {
+        return CMD_CONTINUE;
+    }
+
+    cbdata.vpeid = FIELD_EX64(cmdpkt[1], VMOVP_1, VPEID);
+    cbdata.rdbase = FIELD_EX64(cmdpkt[2], VMOVP_2, RDBASE);
+
+    trace_gicv3_its_cmd_vmovp(cbdata.vpeid, cbdata.rdbase);
+
+    if (cbdata.rdbase >= s->gicv3->num_cpu) {
+        return CMD_CONTINUE;
+    }
+
+    /*
+     * Our ITS implementation reports GITS_TYPER.VMOVP == 1, which means
+     * that when the VMOVP command is executed on an ITS to change the
+     * VPEID field in a VPE table entry the change must be propagated
+     * to all the ITSes connected to the same GIC.
+     */
+    cbdata.result = CMD_CONTINUE_OK;
+    gicv3_foreach_its(s->gicv3, vmovp_callback, &cbdata);
+    return cbdata.result;
+}
+
 /*
  * Current implementation blocks until all
  * commands are processed
@@ -1136,6 +1208,9 @@ static void process_cmdq(GICv3ITSState *s)
         case GITS_CMD_VMAPP:
             result = process_vmapp(s, cmdpkt);
             break;
+        case GITS_CMD_VMOVP:
+            result = process_vmovp(s, cmdpkt);
+            break;
         default:
             trace_gicv3_its_cmd_unknown(cmd);
             break;
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index d529914eca2..a2dd1bdb6c3 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -190,6 +190,7 @@ gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS:
 gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x"
 gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x"
 gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x"
+gicv3_its_cmd_vmovp(uint32_t vpeid, uint64_t rdbase) "GICv3 ITS: command VMOVP vPEID 0x%x RDbase 0x%" PRIx64
 gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
 gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
 gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (15 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 16/41] hw/intc/arm_gicv3_its: Implement VMOVP Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:17   ` Richard Henderson
  2022-04-09 18:25   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 18/41] hw/intc/arm_gicv3_its: Implement INV command properly Peter Maydell
                   ` (24 subsequent siblings)
  41 siblings, 2 replies; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The VSYNC command forces the ITS to synchronize all outstanding ITS
operations for the specified vPEID, so that subsequent writse to
GITS_TRANSLATER honour them.  The QEMU implementation is always in
sync, so for us this is a nop, like the existing SYNC command.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h |  1 +
 hw/intc/arm_gicv3_its.c  | 11 +++++++++++
 hw/intc/trace-events     |  1 +
 3 files changed, 13 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index c1467ce7263..ef1d75b3cf4 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -330,6 +330,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define GITS_CMD_MOVALL           0x0E
 #define GITS_CMD_DISCARD          0x0F
 #define GITS_CMD_VMOVP            0x22
+#define GITS_CMD_VSYNC            0x25
 #define GITS_CMD_VMAPP            0x29
 #define GITS_CMD_VMAPTI           0x2A
 #define GITS_CMD_VMAPI            0x2B
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index bd82c84b46d..05d64630450 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1165,6 +1165,17 @@ static void process_cmdq(GICv3ITSState *s)
              */
             trace_gicv3_its_cmd_sync();
             break;
+        case GITS_CMD_VSYNC:
+            /*
+             * VSYNC also is a nop, because our implementation is always
+             * in sync.
+             */
+            if (!its_feature_virtual(s)) {
+                result = CMD_CONTINUE;
+                break;
+            }
+            trace_gicv3_its_cmd_vsync();
+            break;
         case GITS_CMD_MAPD:
             result = process_mapd(s, cmdpkt);
             break;
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index a2dd1bdb6c3..b9efe14c690 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -191,6 +191,7 @@ gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t d
 gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x"
 gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x"
 gicv3_its_cmd_vmovp(uint32_t vpeid, uint64_t rdbase) "GICv3 ITS: command VMOVP vPEID 0x%x RDbase 0x%" PRIx64
+gicv3_its_cmd_vsync(void) "GICv3 ITS: command VSYNC"
 gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
 gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
 gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 18/41] hw/intc/arm_gicv3_its: Implement INV command properly
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (16 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:22   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 19/41] hw/intc/arm_gicv3_its: Implement INV for virtual interrupts Peter Maydell
                   ` (23 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

We were previously implementing INV (like INVALL) to just blow away
cached highest-priority-pending-LPI information on all connected
redistributors.  For GICv4.0, this isn't going to be sufficient,
because the LPI we are invalidating cached information for might be
either physical or virtual, and the required action is different for
those two cases.  So we need to do the full process of looking up the
ITE from the devid and eventid.  This also means we can do the error
checks that the spec lists for this command.

Split out INV handling into a process_inv() function like our other
command-processing functions.  For the moment, stick to handling only
physical LPIs; we will add the vLPI parts later.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
We could also improve INVALL to only prod the one redistributor
specified by the ICID in the command packet, but since INVALL
is only for physical LPIs I am leaving it as it is.
---
 hw/intc/gicv3_internal.h   | 12 +++++++++
 hw/intc/arm_gicv3_its.c    | 50 +++++++++++++++++++++++++++++++++++++-
 hw/intc/arm_gicv3_redist.c | 11 +++++++++
 hw/intc/trace-events       |  3 ++-
 4 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index ef1d75b3cf4..25ea19de385 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -373,6 +373,10 @@ FIELD(MOVI_0, DEVICEID, 32, 32)
 FIELD(MOVI_1, EVENTID, 0, 32)
 FIELD(MOVI_2, ICID, 0, 16)
 
+/* INV command fields */
+FIELD(INV_0, DEVICEID, 32, 32)
+FIELD(INV_1, EVENTID, 0, 32)
+
 /* VMAPI, VMAPTI command fields */
 FIELD(VMAPTI_0, DEVICEID, 32, 32)
 FIELD(VMAPTI_1, EVENTID, 0, 32)
@@ -573,6 +577,14 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs);
  * an incoming migration has loaded new state.
  */
 void gicv3_redist_update_lpi_only(GICv3CPUState *cs);
+/**
+ * gicv3_redist_inv_lpi:
+ * @cs: GICv3CPUState
+ * @irq: LPI to invalidate cached information for
+ *
+ * Forget or update any cached information associated with this LPI.
+ */
+void gicv3_redist_inv_lpi(GICv3CPUState *cs, int irq);
 /**
  * gicv3_redist_mov_lpi:
  * @src: source redistributor
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 05d64630450..6ba554c16ea 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1084,6 +1084,50 @@ static ItsCmdResult process_vmovp(GICv3ITSState *s, const uint64_t *cmdpkt)
     return cbdata.result;
 }
 
+static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+    uint32_t devid, eventid;
+    ITEntry ite;
+    DTEntry dte;
+    CTEntry cte;
+    ItsCmdResult cmdres;
+
+    devid = FIELD_EX64(cmdpkt[0], INV_0, DEVICEID);
+    eventid = FIELD_EX64(cmdpkt[1], INV_1, EVENTID);
+
+    trace_gicv3_its_cmd_inv(devid, eventid);
+
+    cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
+    }
+
+    switch (ite.inttype) {
+    case ITE_INTTYPE_PHYSICAL:
+        cmdres = lookup_cte(s, __func__, ite.icid, &cte);
+        if (cmdres != CMD_CONTINUE_OK) {
+            return cmdres;
+        }
+        gicv3_redist_inv_lpi(&s->gicv3->cpu[cte.rdbase], ite.intid);
+        break;
+    case ITE_INTTYPE_VIRTUAL:
+        if (!its_feature_virtual(s)) {
+            /* Can't happen unless guest is illegally writing to table memory */
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: invalid type %d in ITE (table corrupted?)\n",
+                          __func__, ite.inttype);
+            return CMD_CONTINUE;
+        }
+        /* We will implement the vLPI invalidation in a later commit */
+        g_assert_not_reached();
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    return CMD_CONTINUE_OK;
+}
+
 /*
  * Current implementation blocks until all
  * commands are processed
@@ -1192,14 +1236,18 @@ static void process_cmdq(GICv3ITSState *s)
             result = process_its_cmd(s, cmdpkt, DISCARD);
             break;
         case GITS_CMD_INV:
+            result = process_inv(s, cmdpkt);
+            break;
         case GITS_CMD_INVALL:
             /*
              * Current implementation doesn't cache any ITS tables,
              * but the calculated lpi priority information. We only
              * need to trigger lpi priority re-calculation to be in
              * sync with LPI config table or pending table changes.
+             * INVALL operates on a collection specified by ICID so
+             * it only affects physical LPIs.
              */
-            trace_gicv3_its_cmd_inv();
+            trace_gicv3_its_cmd_invall();
             for (i = 0; i < s->gicv3->num_cpu; i++) {
                 gicv3_redist_update_lpi(&s->gicv3->cpu[i]);
             }
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index b08b599c887..78650a3bb4c 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -681,6 +681,17 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
     gicv3_redist_lpi_pending(cs, irq, level);
 }
 
+void gicv3_redist_inv_lpi(GICv3CPUState *cs, int irq)
+{
+    /*
+     * The only cached information for LPIs we have is the HPPLPI.
+     * We could be cleverer about identifying when we don't need
+     * to do a full rescan of the pending table, but until we find
+     * this is a performance issue, just always recalculate.
+     */
+    gicv3_redist_update_lpi(cs);
+}
+
 void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
 {
     /*
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index b9efe14c690..ae4a3cfb004 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -184,7 +184,8 @@ gicv3_its_cmd_mapd(uint32_t devid, uint32_t size, uint64_t ittaddr, int valid) "
 gicv3_its_cmd_mapc(uint32_t icid, uint64_t rdbase, int valid) "GICv3 ITS: command MAPC ICID 0x%x RDbase 0x%" PRIx64 " V %d"
 gicv3_its_cmd_mapi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MAPI DeviceID 0x%x EventID 0x%x ICID 0x%x"
 gicv3_its_cmd_mapti(uint32_t devid, uint32_t eventid, uint32_t icid, uint32_t intid) "GICv3 ITS: command MAPTI DeviceID 0x%x EventID 0x%x ICID 0x%x pINTID 0x%x"
-gicv3_its_cmd_inv(void) "GICv3 ITS: command INV or INVALL"
+gicv3_its_cmd_inv(uint32_t devid, uint32_t eventid) "GICv3 ITS: command INV DeviceID 0x%x EventID 0x%x"
+gicv3_its_cmd_invall(void) "GICv3 ITS: command INVALL"
 gicv3_its_cmd_movall(uint64_t rd1, uint64_t rd2) "GICv3 ITS: command MOVALL RDbase1 0x%" PRIx64 " RDbase2 0x%" PRIx64
 gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MOVI DeviceID 0x%x EventID 0x%x ICID 0x%x"
 gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 19/41] hw/intc/arm_gicv3_its: Implement INV for virtual interrupts
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (17 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 18/41] hw/intc/arm_gicv3_its: Implement INV command properly Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:23   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 20/41] hw/intc/arm_gicv3_its: Implement VMOVI Peter Maydell
                   ` (22 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the ITS side of the handling of the INV command for
virtual interrupts; as usual this calls into a redistributor
function which we leave as a stub to fill in later.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h   |  9 +++++++++
 hw/intc/arm_gicv3_its.c    | 16 ++++++++++++++--
 hw/intc/arm_gicv3_redist.c |  8 ++++++++
 3 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 25ea19de385..2f653a9b917 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -585,6 +585,15 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs);
  * Forget or update any cached information associated with this LPI.
  */
 void gicv3_redist_inv_lpi(GICv3CPUState *cs, int irq);
+/**
+ * gicv3_redist_inv_vlpi:
+ * @cs: GICv3CPUState
+ * @irq: vLPI to invalidate cached information for
+ * @vptaddr: (guest) address of vLPI table
+ *
+ * Forget or update any cached information associated with this vLPI.
+ */
+void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr);
 /**
  * gicv3_redist_mov_lpi:
  * @src: source redistributor
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 6ba554c16ea..c8b90e6b0d9 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1090,6 +1090,7 @@ static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt)
     ITEntry ite;
     DTEntry dte;
     CTEntry cte;
+    VTEntry vte;
     ItsCmdResult cmdres;
 
     devid = FIELD_EX64(cmdpkt[0], INV_0, DEVICEID);
@@ -1118,8 +1119,19 @@ static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt)
                           __func__, ite.inttype);
             return CMD_CONTINUE;
         }
-        /* We will implement the vLPI invalidation in a later commit */
-        g_assert_not_reached();
+
+        cmdres = lookup_vte(s, __func__, ite.vpeid, &vte);
+        if (cmdres != CMD_CONTINUE_OK) {
+            return cmdres;
+        }
+        if (!intid_in_lpi_range(ite.intid) ||
+            ite.intid >= (1ULL << (vte.vptsize + 1))) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: intid 0x%x out of range\n",
+                          __func__, ite.intid);
+            return CMD_CONTINUE;
+        }
+        gicv3_redist_inv_vlpi(&s->gicv3->cpu[vte.rdbase], ite.intid,
+                              vte.vptaddr << 16);
         break;
     default:
         g_assert_not_reached();
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 78650a3bb4c..856494b4e8f 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -808,6 +808,14 @@ void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
      */
 }
 
+void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr)
+{
+    /*
+     * The redistributor handling for invalidating cached information
+     * about a VLPI will be added in a subsequent commit.
+     */
+}
+
 void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
 {
     /* Update redistributor state for a change in an external PPI input line */
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 20/41] hw/intc/arm_gicv3_its: Implement VMOVI
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (18 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 19/41] hw/intc/arm_gicv3_its: Implement INV for virtual interrupts Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:27   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 21/41] hw/intc/arm_gicv3_its: Implement VINVALL Peter Maydell
                   ` (21 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the GICv4 VMOVI command, which moves the pending state
of a virtual interrupt from one redistributor to another. As with
MOVI, we handle the "parse and validate command arguments and
table lookups" part in the ITS source file, and pass the final
results to a function in the redistributor which will do the
actual operation. As with the "make a VLPI pending" change,
for the moment we leave that redistributor function as a stub,
to be implemented in a later commit.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h   | 23 +++++++++++
 hw/intc/arm_gicv3_its.c    | 82 ++++++++++++++++++++++++++++++++++++++
 hw/intc/arm_gicv3_redist.c | 10 +++++
 hw/intc/trace-events       |  1 +
 4 files changed, 116 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 2f653a9b917..050e19d133b 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -329,6 +329,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define GITS_CMD_INVALL           0x0D
 #define GITS_CMD_MOVALL           0x0E
 #define GITS_CMD_DISCARD          0x0F
+#define GITS_CMD_VMOVI            0x21
 #define GITS_CMD_VMOVP            0x22
 #define GITS_CMD_VSYNC            0x25
 #define GITS_CMD_VMAPP            0x29
@@ -403,6 +404,13 @@ FIELD(VMOVP_2, RDBASE, 16, 36)
 FIELD(VMOVP_2, DB, 63, 1) /* GICv4.1 only */
 FIELD(VMOVP_3, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */
 
+/* VMOVI command fields */
+FIELD(VMOVI_0, DEVICEID, 32, 32)
+FIELD(VMOVI_1, EVENTID, 0, 32)
+FIELD(VMOVI_1, VPEID, 32, 16)
+FIELD(VMOVI_2, D, 0, 1)
+FIELD(VMOVI_2, DOORBELL, 32, 32)
+
 /*
  * 12 bytes Interrupt translation Table Entry size
  * as per Table 5.3 in GICv3 spec
@@ -614,6 +622,21 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq);
  * by the ITS MOVALL command.
  */
 void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest);
+/**
+ * gicv3_redist_mov_vlpi:
+ * @src: source redistributor
+ * @src_vptaddr: (guest) address of source VLPI table
+ * @dest: destination redistributor
+ * @dest_vptaddr: (guest) address of destination VLPI table
+ * @irq: VLPI to update
+ * @doorbell: doorbell for destination (1023 for "no doorbell")
+ *
+ * Move the pending state of the specified VLPI from @src to @dest,
+ * as required by the ITS VMOVI command.
+ */
+void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
+                           GICv3CPUState *dest, uint64_t dest_vptaddr,
+                           int irq, int doorbell);
 
 void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
 void gicv3_init_cpuif(GICv3State *s);
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index c8b90e6b0d9..aef024009b2 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1084,6 +1084,85 @@ static ItsCmdResult process_vmovp(GICv3ITSState *s, const uint64_t *cmdpkt)
     return cbdata.result;
 }
 
+static ItsCmdResult process_vmovi(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+    uint32_t devid, eventid, vpeid, doorbell;
+    bool doorbell_valid;
+    DTEntry dte;
+    ITEntry ite;
+    VTEntry old_vte, new_vte;
+    ItsCmdResult cmdres;
+
+    if (!its_feature_virtual(s)) {
+        return CMD_CONTINUE;
+    }
+
+    devid = FIELD_EX64(cmdpkt[0], VMOVI_0, DEVICEID);
+    eventid = FIELD_EX64(cmdpkt[1], VMOVI_1, EVENTID);
+    vpeid = FIELD_EX64(cmdpkt[1], VMOVI_1, VPEID);
+    doorbell_valid = FIELD_EX64(cmdpkt[2], VMOVI_2, D);
+    doorbell = FIELD_EX64(cmdpkt[2], VMOVI_2, DOORBELL);
+
+    trace_gicv3_its_cmd_vmovi(devid, eventid, vpeid, doorbell_valid, doorbell);
+
+    if (doorbell_valid && !valid_doorbell(doorbell)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid doorbell 0x%x\n", __func__, doorbell);
+        return CMD_CONTINUE;
+    }
+
+    cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
+    }
+
+    if (ite.inttype != ITE_INTTYPE_VIRTUAL) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: ITE is not for virtual interrupt\n",
+                      __func__);
+        return CMD_CONTINUE;
+    }
+
+    cmdres = lookup_vte(s, __func__, ite.vpeid, &old_vte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
+    }
+    cmdres = lookup_vte(s, __func__, vpeid, &new_vte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
+    }
+
+    if (!intid_in_lpi_range(ite.intid) ||
+        ite.intid >= (1ULL << (old_vte.vptsize + 1)) ||
+        ite.intid >= (1ULL << (new_vte.vptsize + 1))) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: ITE intid 0x%x out of range\n",
+                      __func__, ite.intid);
+        return CMD_CONTINUE;
+    }
+
+    ite.vpeid = vpeid;
+    if (doorbell_valid) {
+        ite.doorbell = doorbell;
+    }
+
+    /*
+     * Move the LPI from the old redistributor to the new one. We don't
+     * need to do anything if the guest somehow specified the
+     * same pending table for source and destination.
+     */
+    if (old_vte.vptaddr != new_vte.vptaddr) {
+        gicv3_redist_mov_vlpi(&s->gicv3->cpu[old_vte.rdbase],
+                              old_vte.vptaddr << 16,
+                              &s->gicv3->cpu[new_vte.rdbase],
+                              new_vte.vptaddr << 16,
+                              ite.intid,
+                              ite.doorbell);
+    }
+
+    /* Update the ITE to the new VPEID and possibly doorbell values */
+    return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
+}
+
 static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt)
 {
     uint32_t devid, eventid;
@@ -1282,6 +1361,9 @@ static void process_cmdq(GICv3ITSState *s)
         case GITS_CMD_VMOVP:
             result = process_vmovp(s, cmdpkt);
             break;
+        case GITS_CMD_VMOVI:
+            result = process_vmovi(s, cmdpkt);
+            break;
         default:
             trace_gicv3_its_cmd_unknown(cmd);
             break;
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 856494b4e8f..dc25997d1f9 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -808,6 +808,16 @@ void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
      */
 }
 
+void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
+                           GICv3CPUState *dest, uint64_t dest_vptaddr,
+                           int irq, int doorbell)
+{
+    /*
+     * The redistributor handling for moving a VLPI will be added
+     * in a subsequent commit.
+     */
+}
+
 void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr)
 {
     /*
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index ae4a3cfb004..9894756e55a 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -193,6 +193,7 @@ gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t
 gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x"
 gicv3_its_cmd_vmovp(uint32_t vpeid, uint64_t rdbase) "GICv3 ITS: command VMOVP vPEID 0x%x RDbase 0x%" PRIx64
 gicv3_its_cmd_vsync(void) "GICv3 ITS: command VSYNC"
+gicv3_its_cmd_vmovi(uint32_t devid,  uint32_t eventid, uint32_t vpeid, int dbvalid, uint32_t doorbell) "GICv3 ITS: command VMOVI DeviceID 0x%x EventID 0x%x vPEID 0x%x D %d Dbell_pINTID 0x%x"
 gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
 gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
 gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 21/41] hw/intc/arm_gicv3_its: Implement VINVALL
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (19 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 20/41] hw/intc/arm_gicv3_its: Implement VMOVI Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:28   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 22/41] hw/intc/arm_gicv3: Implement GICv4's new redistributor frame Peter Maydell
                   ` (20 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The VINVALL command should cause any cached information in the
ITS or redistributor for the specified vCPU to be dropped or
otherwise made consistent with the in-memory LPI configuration
tables.

Here we implement the command and table parsing, leaving the
redistributor part as a stub for the moment, as usual.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h   | 13 +++++++++++++
 hw/intc/arm_gicv3_its.c    | 26 ++++++++++++++++++++++++++
 hw/intc/arm_gicv3_redist.c |  5 +++++
 hw/intc/trace-events       |  1 +
 4 files changed, 45 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 050e19d133b..8d58d38836f 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -335,6 +335,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
 #define GITS_CMD_VMAPP            0x29
 #define GITS_CMD_VMAPTI           0x2A
 #define GITS_CMD_VMAPI            0x2B
+#define GITS_CMD_VINVALL          0x2D
 
 /* MAPC command fields */
 #define ICID_LENGTH                  16
@@ -411,6 +412,9 @@ FIELD(VMOVI_1, VPEID, 32, 16)
 FIELD(VMOVI_2, D, 0, 1)
 FIELD(VMOVI_2, DOORBELL, 32, 32)
 
+/* VINVALL command fields */
+FIELD(VINVALL_1, VPEID, 32, 16)
+
 /*
  * 12 bytes Interrupt translation Table Entry size
  * as per Table 5.3 in GICv3 spec
@@ -637,6 +641,15 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest);
 void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
                            GICv3CPUState *dest, uint64_t dest_vptaddr,
                            int irq, int doorbell);
+/**
+ * gicv3_redist_vinvall:
+ * @cs: GICv3CPUState
+ * @vptaddr: address of VLPI pending table
+ *
+ * On redistributor @cs, invalidate all cached information associated
+ * with the vCPU defined by @vptaddr.
+ */
+void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr);
 
 void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
 void gicv3_init_cpuif(GICv3State *s);
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index aef024009b2..6c44cccd369 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1163,6 +1163,29 @@ static ItsCmdResult process_vmovi(GICv3ITSState *s, const uint64_t *cmdpkt)
     return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
 }
 
+static ItsCmdResult process_vinvall(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+    VTEntry vte;
+    uint32_t vpeid;
+    ItsCmdResult cmdres;
+
+    if (!its_feature_virtual(s)) {
+        return CMD_CONTINUE;
+    }
+
+    vpeid = FIELD_EX64(cmdpkt[1], VINVALL_1, VPEID);
+
+    trace_gicv3_its_cmd_vinvall(vpeid);
+
+    cmdres = lookup_vte(s, __func__, vpeid, &vte);
+    if (cmdres != CMD_CONTINUE_OK) {
+        return cmdres;
+    }
+
+    gicv3_redist_vinvall(&s->gicv3->cpu[vte.rdbase], vte.vptaddr << 16);
+    return CMD_CONTINUE_OK;
+}
+
 static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt)
 {
     uint32_t devid, eventid;
@@ -1364,6 +1387,9 @@ static void process_cmdq(GICv3ITSState *s)
         case GITS_CMD_VMOVI:
             result = process_vmovi(s, cmdpkt);
             break;
+        case GITS_CMD_VINVALL:
+            result = process_vinvall(s, cmdpkt);
+            break;
         default:
             trace_gicv3_its_cmd_unknown(cmd);
             break;
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index dc25997d1f9..7c75dd6f072 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -818,6 +818,11 @@ void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
      */
 }
 
+void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr)
+{
+    /* The redistributor handling will be added in a subsequent commit */
+}
+
 void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr)
 {
     /*
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 9894756e55a..004a1006fb8 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -194,6 +194,7 @@ gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr
 gicv3_its_cmd_vmovp(uint32_t vpeid, uint64_t rdbase) "GICv3 ITS: command VMOVP vPEID 0x%x RDbase 0x%" PRIx64
 gicv3_its_cmd_vsync(void) "GICv3 ITS: command VSYNC"
 gicv3_its_cmd_vmovi(uint32_t devid,  uint32_t eventid, uint32_t vpeid, int dbvalid, uint32_t doorbell) "GICv3 ITS: command VMOVI DeviceID 0x%x EventID 0x%x vPEID 0x%x D %d Dbell_pINTID 0x%x"
+gicv3_its_cmd_vinvall(uint32_t vpeid) "GICv3 ITS: command VINVALL vPEID 0x%x"
 gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x"
 gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x"
 gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 22/41] hw/intc/arm_gicv3: Implement GICv4's new redistributor frame
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (20 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 21/41] hw/intc/arm_gicv3_its: Implement VINVALL Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:32   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 23/41] hw/intc/arm_gicv3: Implement new GICv4 redistributor registers Peter Maydell
                   ` (19 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The GICv4 extends the redistributor register map -- where GICv3
had two 64KB frames per CPU, GICv4 has four frames. Add support
for the extra frame by using a new gicv3_redist_size() function
in the places in the GIC implementation which currently use
a fixed constant size for the redistributor register block.
(Until we implement the extra registers they will RAZ/WI.)

Any board that wants to use a GICv4 will need to also adjust
to handle the different sized redistributor register block;
that will be done separately.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h           | 21 +++++++++++++++++++++
 include/hw/intc/arm_gicv3_common.h |  5 +++++
 hw/intc/arm_gicv3_common.c         |  2 +-
 hw/intc/arm_gicv3_redist.c         |  8 ++++----
 4 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 8d58d38836f..9720ccf7507 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -489,6 +489,27 @@ FIELD(VTE, RDBASE, 42, RDBASE_PROCNUM_LENGTH)
 
 /* Functions internal to the emulated GICv3 */
 
+/**
+ * gicv3_redist_size:
+ * @s: GICv3State
+ *
+ * Return the size of the redistributor register frame in bytes
+ * (which depends on what GIC version this is)
+ */
+static inline int gicv3_redist_size(GICv3State *s)
+{
+    /*
+     * Redistributor size is controlled by the redistributor GICR_TYPER.VLPIS.
+     * It's the same for every redistributor in the GIC, so arbitrarily
+     * use the register field in the first one.
+     */
+    if (s->cpu[0].gicr_typer & GICR_TYPER_VLPIS) {
+        return GICV4_REDIST_SIZE;
+    } else {
+        return GICV3_REDIST_SIZE;
+    }
+}
+
 /**
  * gicv3_intid_is_special:
  * @intid: interrupt ID
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 08b27789385..40bc404a652 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -38,7 +38,12 @@
 
 #define GICV3_LPI_INTID_START 8192
 
+/*
+ * The redistributor in GICv3 has two 64KB frames per CPU; in
+ * GICv4 it has four 64KB frames per CPU.
+ */
 #define GICV3_REDIST_SIZE 0x20000
+#define GICV4_REDIST_SIZE 0x40000
 
 /* Number of SGI target-list bits */
 #define GICV3_TARGETLIST_BITS 16
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index dcc5ce28c6a..18999e3c8bb 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -295,7 +295,7 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
 
         memory_region_init_io(&region->iomem, OBJECT(s),
                               ops ? &ops[1] : NULL, region, name,
-                              s->redist_region_count[i] * GICV3_REDIST_SIZE);
+                              s->redist_region_count[i] * gicv3_redist_size(s));
         sysbus_init_mmio(sbd, &region->iomem);
         g_free(name);
     }
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 7c75dd6f072..9f1fe09a78e 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -442,8 +442,8 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data,
      * in the memory map); if so then the GIC has multiple MemoryRegions
      * for the redistributors.
      */
-    cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
-    offset %= GICV3_REDIST_SIZE;
+    cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
+    offset %= gicv3_redist_size(s);
 
     cs = &s->cpu[cpuidx];
 
@@ -501,8 +501,8 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
      * in the memory map); if so then the GIC has multiple MemoryRegions
      * for the redistributors.
      */
-    cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
-    offset %= GICV3_REDIST_SIZE;
+    cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
+    offset %= gicv3_redist_size(s);
 
     cs = &s->cpu[cpuidx];
 
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 23/41] hw/intc/arm_gicv3: Implement new GICv4 redistributor registers
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (21 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 22/41] hw/intc/arm_gicv3: Implement GICv4's new redistributor frame Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:40   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 24/41] hw/intc/arm_gicv3_cpuif: Split "update vIRQ/vFIQ" from gicv3_cpuif_virt_update() Peter Maydell
                   ` (18 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the new GICv4 redistributor registers: GICR_VPROPBASER
and GICR_VPENDBASER; for the moment we implement these as simple
reads-as-written stubs, together with the necessary migration
and reset handling.

We don't put ID-register checks on the handling of these registers,
because they are all in the only-in-v4 extra register frames, so
they're not accessible in a GICv3.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
GICv4.1 adds two further registers in the new VLPI frame,
as well as changing the layout of VPROPBASER and VPENDBASER,
but we aren't implementing v4.1 yet, just v4.
---
 hw/intc/gicv3_internal.h           | 21 +++++++++++
 include/hw/intc/arm_gicv3_common.h |  3 ++
 hw/intc/arm_gicv3_common.c         | 22 ++++++++++++
 hw/intc/arm_gicv3_redist.c         | 56 ++++++++++++++++++++++++++++++
 4 files changed, 102 insertions(+)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 9720ccf7507..795bf57d2b3 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -77,6 +77,7 @@
  * Redistributor frame offsets from RD_base
  */
 #define GICR_SGI_OFFSET 0x10000
+#define GICR_VLPI_OFFSET 0x20000
 
 /*
  * Redistributor registers, offsets from RD_base
@@ -109,6 +110,10 @@
 #define GICR_IGRPMODR0        (GICR_SGI_OFFSET + 0x0D00)
 #define GICR_NSACR            (GICR_SGI_OFFSET + 0x0E00)
 
+/* VLPI redistributor registers, offsets from VLPI_base */
+#define GICR_VPROPBASER       (GICR_VLPI_OFFSET + 0x70)
+#define GICR_VPENDBASER       (GICR_VLPI_OFFSET + 0x78)
+
 #define GICR_CTLR_ENABLE_LPIS        (1U << 0)
 #define GICR_CTLR_CES                (1U << 1)
 #define GICR_CTLR_RWP                (1U << 3)
@@ -143,6 +148,22 @@ FIELD(GICR_PENDBASER, PTZ, 62, 1)
 
 #define GICR_PROPBASER_IDBITS_THRESHOLD          0xd
 
+/* These are the GICv4 VPROPBASER and VPENDBASER layouts; v4.1 is different */
+FIELD(GICR_VPROPBASER, IDBITS, 0, 5)
+FIELD(GICR_VPROPBASER, INNERCACHE, 7, 3)
+FIELD(GICR_VPROPBASER, SHAREABILITY, 10, 2)
+FIELD(GICR_VPROPBASER, PHYADDR, 12, 40)
+FIELD(GICR_VPROPBASER, OUTERCACHE, 56, 3)
+
+FIELD(GICR_VPENDBASER, INNERCACHE, 7, 3)
+FIELD(GICR_VPENDBASER, SHAREABILITY, 10, 2)
+FIELD(GICR_VPENDBASER, PHYADDR, 16, 36)
+FIELD(GICR_VPENDBASER, OUTERCACHE, 56, 3)
+FIELD(GICR_VPENDBASER, DIRTY, 60, 1)
+FIELD(GICR_VPENDBASER, PENDINGLAST, 61, 1)
+FIELD(GICR_VPENDBASER, IDAI, 62, 1)
+FIELD(GICR_VPENDBASER, VALID, 63, 1)
+
 #define ICC_CTLR_EL1_CBPR           (1U << 0)
 #define ICC_CTLR_EL1_EOIMODE        (1U << 1)
 #define ICC_CTLR_EL1_PMHE           (1U << 6)
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 40bc404a652..7ff5a1aa5fc 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -179,6 +179,9 @@ struct GICv3CPUState {
     uint32_t gicr_igrpmodr0;
     uint32_t gicr_nsacr;
     uint8_t gicr_ipriorityr[GIC_INTERNAL];
+    /* VLPI_base page registers */
+    uint64_t gicr_vpropbaser;
+    uint64_t gicr_vpendbaser;
 
     /* CPU interface */
     uint64_t icc_sre_el1;
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 18999e3c8bb..14d76d74840 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -144,6 +144,25 @@ const VMStateDescription vmstate_gicv3_cpu_sre_el1 = {
     }
 };
 
+static bool gicv4_needed(void *opaque)
+{
+    GICv3CPUState *cs = opaque;
+
+    return cs->gic->revision > 3;
+}
+
+const VMStateDescription vmstate_gicv3_gicv4 = {
+    .name = "arm_gicv3_cpu/gicv4",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = gicv4_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(gicr_vpropbaser, GICv3CPUState),
+        VMSTATE_UINT64(gicr_vpendbaser, GICv3CPUState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
 static const VMStateDescription vmstate_gicv3_cpu = {
     .name = "arm_gicv3_cpu",
     .version_id = 1,
@@ -175,6 +194,7 @@ static const VMStateDescription vmstate_gicv3_cpu = {
     .subsections = (const VMStateDescription * []) {
         &vmstate_gicv3_cpu_virt,
         &vmstate_gicv3_cpu_sre_el1,
+        &vmstate_gicv3_gicv4,
         NULL
     }
 };
@@ -444,6 +464,8 @@ static void arm_gicv3_common_reset(DeviceState *dev)
         cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep;
         cs->gicr_propbaser = 0;
         cs->gicr_pendbaser = 0;
+        cs->gicr_vpropbaser = 0;
+        cs->gicr_vpendbaser = 0;
         /* If we're resetting a TZ-aware GIC as if secure firmware
          * had set it up ready to start a kernel in non-secure, we
          * need to set interrupts to group 1 so the kernel can use them.
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 9f1fe09a78e..c310d7f8ff2 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -236,6 +236,23 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset,
     case GICR_IDREGS ... GICR_IDREGS + 0x2f:
         *data = gicv3_idreg(offset - GICR_IDREGS, GICV3_PIDR0_REDIST);
         return MEMTX_OK;
+        /*
+         * VLPI frame registers. We don't need a version check for
+         * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+         * prevent pre-v4 GIC from passing us offsets this high.
+         */
+    case GICR_VPROPBASER:
+        *data = extract64(cs->gicr_vpropbaser, 0, 32);
+        return MEMTX_OK;
+    case GICR_VPROPBASER + 4:
+        *data = extract64(cs->gicr_vpropbaser, 32, 32);
+        return MEMTX_OK;
+    case GICR_VPENDBASER:
+        *data = extract64(cs->gicr_vpendbaser, 0, 32);
+        return MEMTX_OK;
+    case GICR_VPENDBASER + 4:
+        *data = extract64(cs->gicr_vpendbaser, 32, 32);
+        return MEMTX_OK;
     default:
         return MEMTX_ERROR;
     }
@@ -379,6 +396,23 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
                       "%s: invalid guest write to RO register at offset "
                       TARGET_FMT_plx "\n", __func__, offset);
         return MEMTX_OK;
+        /*
+         * VLPI frame registers. We don't need a version check for
+         * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+         * prevent pre-v4 GIC from passing us offsets this high.
+         */
+    case GICR_VPROPBASER:
+        cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 0, 32, value);
+        return MEMTX_OK;
+    case GICR_VPROPBASER + 4:
+        cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 32, 32, value);
+        return MEMTX_OK;
+    case GICR_VPENDBASER:
+        cs->gicr_vpendbaser = deposit64(cs->gicr_vpendbaser, 0, 32, value);
+        return MEMTX_OK;
+    case GICR_VPENDBASER + 4:
+        cs->gicr_vpendbaser = deposit64(cs->gicr_vpendbaser, 32, 32, value);
+        return MEMTX_OK;
     default:
         return MEMTX_ERROR;
     }
@@ -397,6 +431,17 @@ static MemTxResult gicr_readll(GICv3CPUState *cs, hwaddr offset,
     case GICR_PENDBASER:
         *data = cs->gicr_pendbaser;
         return MEMTX_OK;
+        /*
+         * VLPI frame registers. We don't need a version check for
+         * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+         * prevent pre-v4 GIC from passing us offsets this high.
+         */
+    case GICR_VPROPBASER:
+        *data = cs->gicr_vpropbaser;
+        return MEMTX_OK;
+    case GICR_VPENDBASER:
+        *data = cs->gicr_vpendbaser;
+        return MEMTX_OK;
     default:
         return MEMTX_ERROR;
     }
@@ -418,6 +463,17 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
                       "%s: invalid guest write to RO register at offset "
                       TARGET_FMT_plx "\n", __func__, offset);
         return MEMTX_OK;
+        /*
+         * VLPI frame registers. We don't need a version check for
+         * VPROPBASER and VPENDBASER because gicv3_redist_size() will
+         * prevent pre-v4 GIC from passing us offsets this high.
+         */
+    case GICR_VPROPBASER:
+        cs->gicr_vpropbaser = value;
+        return MEMTX_OK;
+    case GICR_VPENDBASER:
+        cs->gicr_vpendbaser = value;
+        return MEMTX_OK;
     default:
         return MEMTX_ERROR;
     }
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 24/41] hw/intc/arm_gicv3_cpuif: Split "update vIRQ/vFIQ" from gicv3_cpuif_virt_update()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (22 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 23/41] hw/intc/arm_gicv3: Implement new GICv4 redistributor registers Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 18:45   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 25/41] hw/intc/arm_gicv3_cpuif: Support vLPIs Peter Maydell
                   ` (17 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The function gicv3_cpuif_virt_update() currently sets all of vIRQ,
vFIQ and the maintenance interrupt.  This implies that it has to be
used quite carefully -- as the comment notes, setting the maintenance
interrupt will typically cause the GIC code to be re-entered
recursively.  For handling vLPIs, we need the redistributor to be
able to tell the cpuif to update the vIRQ and vFIQ lines when the
highest priority pending vLPI changes.  Since that change can't cause
the maintenance interrupt state to change, we can pull the "update
vIRQ/vFIQ" parts of gicv3_cpuif_virt_update() out into a separate
function, which the redistributor can then call without having to
worry about the reentrancy issue.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h  | 11 +++++++
 hw/intc/arm_gicv3_cpuif.c | 64 ++++++++++++++++++++++++---------------
 hw/intc/trace-events      |  3 +-
 3 files changed, 53 insertions(+), 25 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 795bf57d2b3..f25ddeca579 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -707,6 +707,17 @@ void gicv3_init_cpuif(GICv3State *s);
  */
 void gicv3_cpuif_update(GICv3CPUState *cs);
 
+/*
+ * gicv3_cpuif_virt_irq_fiq_update:
+ * @cs: GICv3CPUState for the CPU to update
+ *
+ * Recalculate whether to assert the virtual IRQ or FIQ lines after
+ * a change to the current highest priority pending virtual interrupt.
+ * Note that this does not recalculate and change the maintenance
+ * interrupt status (for that, see gicv3_cpuif_virt_update()).
+ */
+void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs);
+
 static inline uint32_t gicv3_iidr(void)
 {
     /* Return the Implementer Identification Register value
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 1a3d440a54b..5fb64d4663c 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -370,30 +370,20 @@ static uint32_t maintenance_interrupt_state(GICv3CPUState *cs)
     return value;
 }
 
-static void gicv3_cpuif_virt_update(GICv3CPUState *cs)
+void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs)
 {
-    /* Tell the CPU about any pending virtual interrupts or
-     * maintenance interrupts, following a change to the state
-     * of the CPU interface relevant to virtual interrupts.
-     *
-     * CAUTION: this function will call qemu_set_irq() on the
-     * CPU maintenance IRQ line, which is typically wired up
-     * to the GIC as a per-CPU interrupt. This means that it
-     * will recursively call back into the GIC code via
-     * gicv3_redist_set_irq() and thus into the CPU interface code's
-     * gicv3_cpuif_update(). It is therefore important that this
-     * function is only called as the final action of a CPU interface
-     * register write implementation, after all the GIC state
-     * fields have been updated. gicv3_cpuif_update() also must
-     * not cause this function to be called, but that happens
-     * naturally as a result of there being no architectural
-     * linkage between the physical and virtual GIC logic.
+    /*
+     * Tell the CPU about any pending virtual interrupts.
+     * This should only be called for changes that affect the
+     * vIRQ and vFIQ status and do not change the maintenance
+     * interrupt status. This means that unlike gicv3_cpuif_virt_update()
+     * this function won't recursively call back into the GIC code.
+     * The main use of this is when the redistributor has changed the
+     * highest priority pending virtual LPI.
      */
     int idx;
     int irqlevel = 0;
     int fiqlevel = 0;
-    int maintlevel = 0;
-    ARMCPU *cpu = ARM_CPU(cs->cpu);
 
     idx = hppvi_index(cs);
     trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx);
@@ -410,16 +400,42 @@ static void gicv3_cpuif_virt_update(GICv3CPUState *cs)
         }
     }
 
+    trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel, irqlevel);
+    qemu_set_irq(cs->parent_vfiq, fiqlevel);
+    qemu_set_irq(cs->parent_virq, irqlevel);
+}
+
+static void gicv3_cpuif_virt_update(GICv3CPUState *cs)
+{
+    /*
+     * Tell the CPU about any pending virtual interrupts or
+     * maintenance interrupts, following a change to the state
+     * of the CPU interface relevant to virtual interrupts.
+     *
+     * CAUTION: this function will call qemu_set_irq() on the
+     * CPU maintenance IRQ line, which is typically wired up
+     * to the GIC as a per-CPU interrupt. This means that it
+     * will recursively call back into the GIC code via
+     * gicv3_redist_set_irq() and thus into the CPU interface code's
+     * gicv3_cpuif_update(). It is therefore important that this
+     * function is only called as the final action of a CPU interface
+     * register write implementation, after all the GIC state
+     * fields have been updated. gicv3_cpuif_update() also must
+     * not cause this function to be called, but that happens
+     * naturally as a result of there being no architectural
+     * linkage between the physical and virtual GIC logic.
+     */
+    ARMCPU *cpu = ARM_CPU(cs->cpu);
+    int maintlevel = 0;
+
+    gicv3_cpuif_virt_irq_fiq_update(cs);
+
     if ((cs->ich_hcr_el2 & ICH_HCR_EL2_EN) &&
         maintenance_interrupt_state(cs) != 0) {
         maintlevel = 1;
     }
 
-    trace_gicv3_cpuif_virt_set_irqs(gicv3_redist_affid(cs), fiqlevel,
-                                    irqlevel, maintlevel);
-
-    qemu_set_irq(cs->parent_vfiq, fiqlevel);
-    qemu_set_irq(cs->parent_virq, irqlevel);
+    trace_gicv3_cpuif_virt_set_maint_irq(gicv3_redist_affid(cs), maintlevel);
     qemu_set_irq(cpu->gicv3_maintenance_interrupt, maintlevel);
 }
 
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 004a1006fb8..36c3fe4da0b 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -152,7 +152,8 @@ gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x va
 gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64
 gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64
 gicv3_cpuif_virt_update(uint32_t cpuid, int idx) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d"
-gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d maintenance-irq %d"
+gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d"
+gicv3_cpuif_virt_set_maint_irq(uint32_t cpuid, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting maintenance-irq %d"
 
 # arm_gicv3_dist.c
 gicv3_dist_read(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d"
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 25/41] hw/intc/arm_gicv3_cpuif: Support vLPIs
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (23 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 24/41] hw/intc/arm_gicv3_cpuif: Split "update vIRQ/vFIQ" from gicv3_cpuif_virt_update() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 19:20   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 26/41] hw/intc/arm_gicv3_cpuif: Don't recalculate maintenance irq unnecessarily Peter Maydell
                   ` (16 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The CPU interface changes to support vLPIs are fairly minor:
in the parts of the code that currently look at the list registers
to determine the highest priority pending virtual interrupt, we
must also look at the highest priority pending vLPI. To do this
we change hppvi_index() to check the vLPI and return a special-case
value if that is the right virtual interrupt to take. The callsites
(which handle HPPIR and IAR registers and the "raise vIRQ and vFIQ
lines" code) then have to handle this special-case value.

This commit includes two interfaces with the as-yet-unwritten
redistributor code:
 * the new GICv3CPUState::hppvlpi will be set by the redistributor
   (in the same way as the existing hpplpi does for physical LPIs)
 * when the CPU interface acknowledges a vLPI it needs to set it
   to non-pending; the new gicv3_redist_vlpi_pending() function
   (which matches the existing gicv3_redist_lpi_pending() used
   for physical LPIs) is a stub that will be filled in later

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h           |  13 ++++
 include/hw/intc/arm_gicv3_common.h |   3 +
 hw/intc/arm_gicv3_common.c         |   1 +
 hw/intc/arm_gicv3_cpuif.c          | 119 +++++++++++++++++++++++++++--
 hw/intc/arm_gicv3_redist.c         |   8 ++
 hw/intc/trace-events               |   2 +-
 6 files changed, 140 insertions(+), 6 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index f25ddeca579..07644b2be6f 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -612,6 +612,19 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level);
  */
 void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
                                int doorbell, int level);
+/**
+ * gicv3_redist_vlpi_pending:
+ * @cs: GICv3CPUState
+ * @irq: (virtual) interrupt number
+ * @level: level to set @irq to
+ *
+ * Set/clear the pending status of a virtual LPI in the vLPI table
+ * that this redistributor is currently using. (The difference between
+ * this and gicv3_redist_process_vlpi() is that this is called from
+ * the cpuif and does not need to do the not-running-on-this-vcpu checks.)
+ */
+void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level);
+
 void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
 /**
  * gicv3_redist_update_lpi:
diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h
index 7ff5a1aa5fc..4e416100559 100644
--- a/include/hw/intc/arm_gicv3_common.h
+++ b/include/hw/intc/arm_gicv3_common.h
@@ -219,6 +219,9 @@ struct GICv3CPUState {
      */
     PendingIrq hpplpi;
 
+    /* Cached information recalculated from vLPI tables in guest memory */
+    PendingIrq hppvlpi;
+
     /* This is temporary working state, to avoid a malloc in gicv3_update() */
     bool seenbetter;
 };
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 14d76d74840..3f47b3501fe 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -487,6 +487,7 @@ static void arm_gicv3_common_reset(DeviceState *dev)
 
         cs->hppi.prio = 0xff;
         cs->hpplpi.prio = 0xff;
+        cs->hppvlpi.prio = 0xff;
 
         /* State in the CPU interface must *not* be reset here, because it
          * is part of the CPU's reset domain, not the GIC device's.
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index 5fb64d4663c..f11863ff613 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -21,6 +21,12 @@
 #include "hw/irq.h"
 #include "cpu.h"
 
+/*
+ * Special case return value from hppvi_index(); must be larger than
+ * the architecturally maximum possible list register index (which is 15)
+ */
+#define HPPVI_INDEX_VLPI 16
+
 static GICv3CPUState *icc_cs_from_env(CPUARMState *env)
 {
     return env->gicv3state;
@@ -157,10 +163,18 @@ static int ich_highest_active_virt_prio(GICv3CPUState *cs)
 
 static int hppvi_index(GICv3CPUState *cs)
 {
-    /* Return the list register index of the highest priority pending
+    /*
+     * Return the list register index of the highest priority pending
      * virtual interrupt, as per the HighestPriorityVirtualInterrupt
      * pseudocode. If no pending virtual interrupts, return -1.
+     * If the highest priority pending virtual interrupt is a vLPI,
+     * return HPPVI_INDEX_VLPI.
+     * (The pseudocode handles checking whether the vLPI is higher
+     * priority than the highest priority list register at every
+     * callsite of HighestPriorityVirtualInterrupt; we check it here.)
      */
+    ARMCPU *cpu = ARM_CPU(cs->cpu);
+    CPUARMState *env = &cpu->env;
     int idx = -1;
     int i;
     /* Note that a list register entry with a priority of 0xff will
@@ -202,6 +216,23 @@ static int hppvi_index(GICv3CPUState *cs)
         }
     }
 
+    /*
+     * "no pending vLPI" is indicated with prio = 0xff, which always
+     * fails the priority check here. vLPIs are only considered
+     * when we are in Non-Secure state.
+     */
+    if (cs->hppvlpi.prio < prio && !arm_is_secure(env)) {
+        if (cs->hppvlpi.grp == GICV3_G0) {
+            if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG0) {
+                return HPPVI_INDEX_VLPI;
+            }
+        } else {
+            if (cs->ich_vmcr_el2 & ICH_VMCR_EL2_VENG1) {
+                return HPPVI_INDEX_VLPI;
+            }
+        }
+    }
+
     return idx;
 }
 
@@ -289,6 +320,47 @@ static bool icv_hppi_can_preempt(GICv3CPUState *cs, uint64_t lr)
     return false;
 }
 
+static bool icv_hppvlpi_can_preempt(GICv3CPUState *cs)
+{
+    /*
+     * Return true if we can signal the highest priority pending vLPI.
+     * We can assume we're Non-secure because hppvi_index() already
+     * tested for that.
+     */
+    uint32_t mask, rprio, vpmr;
+
+    if (!(cs->ich_hcr_el2 & ICH_HCR_EL2_EN)) {
+        /* Virtual interface disabled */
+        return false;
+    }
+
+    vpmr = extract64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT,
+                     ICH_VMCR_EL2_VPMR_LENGTH);
+
+    if (cs->hppvlpi.prio >= vpmr) {
+        /* Priority mask masks this interrupt */
+        return false;
+    }
+
+    rprio = ich_highest_active_virt_prio(cs);
+    if (rprio == 0xff) {
+        /* No running interrupt so we can preempt */
+        return true;
+    }
+
+    mask = icv_gprio_mask(cs, cs->hppvlpi.grp);
+
+    /*
+     * We only preempt a running interrupt if the pending interrupt's
+     * group priority is sufficient (the subpriorities are not considered).
+     */
+    if ((cs->hppvlpi.prio & mask) < (rprio & mask)) {
+        return true;
+    }
+
+    return false;
+}
+
 static uint32_t eoi_maintenance_interrupt_state(GICv3CPUState *cs,
                                                 uint32_t *misr)
 {
@@ -386,8 +458,18 @@ void gicv3_cpuif_virt_irq_fiq_update(GICv3CPUState *cs)
     int fiqlevel = 0;
 
     idx = hppvi_index(cs);
-    trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx);
-    if (idx >= 0) {
+    trace_gicv3_cpuif_virt_update(gicv3_redist_affid(cs), idx,
+                                  cs->hppvlpi.irq, cs->hppvlpi.grp,
+                                  cs->hppvlpi.prio);
+    if (idx == HPPVI_INDEX_VLPI) {
+        if (icv_hppvlpi_can_preempt(cs)) {
+            if (cs->hppvlpi.grp == GICV3_G0) {
+                fiqlevel = 1;
+            } else {
+                irqlevel = 1;
+            }
+        }
+    } else if (idx >= 0) {
         uint64_t lr = cs->ich_lr_el2[idx];
 
         if (icv_hppi_can_preempt(cs, lr)) {
@@ -619,7 +701,11 @@ static uint64_t icv_hppir_read(CPUARMState *env, const ARMCPRegInfo *ri)
     int idx = hppvi_index(cs);
     uint64_t value = INTID_SPURIOUS;
 
-    if (idx >= 0) {
+    if (idx == HPPVI_INDEX_VLPI) {
+        if (cs->hppvlpi.grp == grp) {
+            value = cs->hppvlpi.irq;
+        }
+    } else if (idx >= 0) {
         uint64_t lr = cs->ich_lr_el2[idx];
         int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
 
@@ -650,6 +736,18 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp)
     cs->ich_apr[grp][regno] |= (1 << regbit);
 }
 
+static void icv_activate_vlpi(GICv3CPUState *cs)
+{
+    uint32_t mask = icv_gprio_mask(cs, cs->hppvlpi.grp);
+    int prio = cs->hppvlpi.prio & mask;
+    int aprbit = prio >> (8 - cs->vprebits);
+    int regno = aprbit / 32;
+    int regbit = aprbit % 32;
+
+    cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit);
+    gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0);
+}
+
 static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri)
 {
     GICv3CPUState *cs = icc_cs_from_env(env);
@@ -657,7 +755,12 @@ static uint64_t icv_iar_read(CPUARMState *env, const ARMCPRegInfo *ri)
     int idx = hppvi_index(cs);
     uint64_t intid = INTID_SPURIOUS;
 
-    if (idx >= 0) {
+    if (idx == HPPVI_INDEX_VLPI) {
+        if (cs->hppvlpi.grp == grp && icv_hppvlpi_can_preempt(cs)) {
+            intid = cs->hppvlpi.irq;
+            icv_activate_vlpi(cs);
+        }
+    } else if (idx >= 0) {
         uint64_t lr = cs->ich_lr_el2[idx];
         int thisgrp = (lr & ICH_LR_EL2_GROUP) ? GICV3_G1NS : GICV3_G0;
 
@@ -2632,6 +2735,12 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque)
     GICv3CPUState *cs = opaque;
 
     gicv3_cpuif_update(cs);
+    /*
+     * Because vLPIs are only pending in NonSecure state,
+     * an EL change can change the VIRQ/VFIQ status (but
+     * cannot affect the maintenance interrupt state)
+     */
+    gicv3_cpuif_virt_irq_fiq_update(cs);
 }
 
 void gicv3_init_cpuif(GICv3State *s)
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index c310d7f8ff2..3464972c139 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -855,6 +855,14 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
     gicv3_redist_update_lpi(dest);
 }
 
+void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level)
+{
+    /*
+     * The redistributor handling for changing the pending state
+     * of a vLPI will be added in a subsequent commit.
+     */
+}
+
 void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
                                int doorbell, int level)
 {
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 36c3fe4da0b..5271590304b 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -151,7 +151,7 @@ gicv3_icv_hppir_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_HPPIR%d rea
 gicv3_icv_dir_write(uint32_t cpu, uint64_t val) "GICv3 ICV_DIR write cpu 0x%x value 0x%" PRIx64
 gicv3_icv_iar_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_IAR%d read cpu 0x%x value 0x%" PRIx64
 gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d write cpu 0x%x value 0x%" PRIx64
-gicv3_cpuif_virt_update(uint32_t cpuid, int idx) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d"
+gicv3_cpuif_virt_update(uint32_t cpuid, int idx, int hppvlpi, int grp, int prio) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d HPPVLPI %d grp %d prio %d"
 gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d"
 gicv3_cpuif_virt_set_maint_irq(uint32_t cpuid, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting maintenance-irq %d"
 
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 26/41] hw/intc/arm_gicv3_cpuif: Don't recalculate maintenance irq unnecessarily
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (24 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 25/41] hw/intc/arm_gicv3_cpuif: Support vLPIs Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 19:23   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 27/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for one LPI" logic Peter Maydell
                   ` (15 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The maintenance interrupt state depends only on:
 * ICH_HCR_EL2
 * ICH_LR<n>_EL2
 * ICH_VMCR_EL2 fields VENG0 and VENG1

Now we have a separate function that updates only the vIRQ and vFIQ
lines, use that in places that only change state that affects vIRQ
and vFIQ but not the maintenance interrupt.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_cpuif.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index f11863ff613..d627ddac90f 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -543,7 +543,7 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU;
 
-    gicv3_cpuif_virt_update(cs);
+    gicv3_cpuif_virt_irq_fiq_update(cs);
     return;
 }
 
@@ -588,7 +588,7 @@ static void icv_bpr_write(CPUARMState *env, const ARMCPRegInfo *ri,
 
     write_vbpr(cs, grp, value);
 
-    gicv3_cpuif_virt_update(cs);
+    gicv3_cpuif_virt_irq_fiq_update(cs);
 }
 
 static uint64_t icv_pmr_read(CPUARMState *env, const ARMCPRegInfo *ri)
@@ -615,7 +615,7 @@ static void icv_pmr_write(CPUARMState *env, const ARMCPRegInfo *ri,
     cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VPMR_SHIFT,
                                  ICH_VMCR_EL2_VPMR_LENGTH, value);
 
-    gicv3_cpuif_virt_update(cs);
+    gicv3_cpuif_virt_irq_fiq_update(cs);
 }
 
 static uint64_t icv_igrpen_read(CPUARMState *env, const ARMCPRegInfo *ri)
@@ -682,7 +682,7 @@ static void icv_ctlr_write(CPUARMState *env, const ARMCPRegInfo *ri,
     cs->ich_vmcr_el2 = deposit64(cs->ich_vmcr_el2, ICH_VMCR_EL2_VEOIM_SHIFT,
                                  1, value & ICC_CTLR_EL1_EOIMODE ? 1 : 0);
 
-    gicv3_cpuif_virt_update(cs);
+    gicv3_cpuif_virt_irq_fiq_update(cs);
 }
 
 static uint64_t icv_rpr_read(CPUARMState *env, const ARMCPRegInfo *ri)
@@ -2452,7 +2452,7 @@ static void ich_ap_write(CPUARMState *env, const ARMCPRegInfo *ri,
     trace_gicv3_ich_ap_write(ri->crm & 1, regno, gicv3_redist_affid(cs), value);
 
     cs->ich_apr[grp][regno] = value & 0xFFFFFFFFU;
-    gicv3_cpuif_virt_update(cs);
+    gicv3_cpuif_virt_irq_fiq_update(cs);
 }
 
 static uint64_t ich_hcr_read(CPUARMState *env, const ARMCPRegInfo *ri)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 27/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for one LPI" logic
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (25 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 26/41] hw/intc/arm_gicv3_cpuif: Don't recalculate maintenance irq unnecessarily Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 19:28   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 28/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for all LPIs" logic Peter Maydell
                   ` (14 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Currently the functions which update the highest priority pending LPI
information by looking at the LPI Pending and Configuration tables
are hard-coded to use the physical LPI tables addressed by
GICR_PENDBASER and GICR_PROPBASER.  To support virtual LPIs we will
need to do essentially the same job, but looking at the current
virtual LPI Pending and Configuration tables and updating cs->hppvlpi
instead of cs->hpplpi.

Factor out the common part of the gicv3_redist_check_lpi_priority()
function into a new update_for_one_lpi() function, which updates
a PendingIrq struct if the specified LPI is higher priority than
what is currently recorded there.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 74 ++++++++++++++++++++++++--------------
 1 file changed, 47 insertions(+), 27 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 3464972c139..571e0fa8309 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -60,6 +60,49 @@ static uint32_t gicr_read_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs,
     return reg;
 }
 
+/**
+ * update_for_one_lpi: Update pending information if this LPI is better
+ *
+ * @cs: GICv3CPUState
+ * @irq: interrupt to look up in the LPI Configuration table
+ * @ctbase: physical address of the LPI Configuration table to use
+ * @ds: true if priority value should not be shifted
+ * @hpp: points to pending information to update
+ *
+ * Look up @irq in the Configuration table specified by @ctbase
+ * to see if it is enabled and what its priority is. If it is an
+ * enabled interrupt with a higher priority than that currently
+ * recorded in @hpp, update @hpp.
+ */
+static void update_for_one_lpi(GICv3CPUState *cs, int irq,
+                               uint64_t ctbase, bool ds, PendingIrq *hpp)
+{
+    uint8_t lpite;
+    uint8_t prio;
+
+    address_space_read(&cs->gic->dma_as,
+                       ctbase + ((irq - GICV3_LPI_INTID_START) * sizeof(lpite)),
+                       MEMTXATTRS_UNSPECIFIED, &lpite, sizeof(lpite));
+
+    if (!(lpite & LPI_CTE_ENABLED)) {
+        return;
+    }
+
+    if (ds) {
+        prio = lpite & LPI_PRIORITY_MASK;
+    } else {
+        prio = ((lpite & LPI_PRIORITY_MASK) >> 1) | 0x80;
+    }
+
+    if ((prio < hpp->prio) ||
+        ((prio == hpp->prio) && (irq <= hpp->irq))) {
+        hpp->irq = irq;
+        hpp->prio = prio;
+        /* LPIs and vLPIs are always non-secure Grp1 interrupts */
+        hpp->grp = GICV3_G1NS;
+    }
+}
+
 static uint8_t gicr_read_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs,
                                     int irq)
 {
@@ -598,34 +641,11 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
 
 static void gicv3_redist_check_lpi_priority(GICv3CPUState *cs, int irq)
 {
-    AddressSpace *as = &cs->gic->dma_as;
-    uint64_t lpict_baddr;
-    uint8_t lpite;
-    uint8_t prio;
+    uint64_t lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
 
-    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
-
-    address_space_read(as, lpict_baddr + ((irq - GICV3_LPI_INTID_START) *
-                       sizeof(lpite)), MEMTXATTRS_UNSPECIFIED, &lpite,
-                       sizeof(lpite));
-
-    if (!(lpite & LPI_CTE_ENABLED)) {
-        return;
-    }
-
-    if (cs->gic->gicd_ctlr & GICD_CTLR_DS) {
-        prio = lpite & LPI_PRIORITY_MASK;
-    } else {
-        prio = ((lpite & LPI_PRIORITY_MASK) >> 1) | 0x80;
-    }
-
-    if ((prio < cs->hpplpi.prio) ||
-        ((prio == cs->hpplpi.prio) && (irq <= cs->hpplpi.irq))) {
-        cs->hpplpi.irq = irq;
-        cs->hpplpi.prio = prio;
-        /* LPIs are always non-secure Grp1 interrupts */
-        cs->hpplpi.grp = GICV3_G1NS;
-    }
+    update_for_one_lpi(cs, irq, lpict_baddr,
+                       cs->gic->gicd_ctlr & GICD_CTLR_DS,
+                       &cs->hpplpi);
 }
 
 void gicv3_redist_update_lpi_only(GICv3CPUState *cs)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 28/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for all LPIs" logic
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (26 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 27/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for one LPI" logic Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 19:54   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 29/41] hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes Peter Maydell
                   ` (13 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Factor out the common part of gicv3_redist_update_lpi_only() into
a new function update_for_all_lpis(), which does a full rescan
of an LPI Pending table and sets the specified PendingIrq struct
with the highest priority pending enabled LPI it finds.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 66 ++++++++++++++++++++++++++------------
 1 file changed, 46 insertions(+), 20 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 571e0fa8309..2379389d14e 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -103,6 +103,48 @@ static void update_for_one_lpi(GICv3CPUState *cs, int irq,
     }
 }
 
+/**
+ * update_for_all_lpis: Fully scan LPI tables and find best pending LPI
+ *
+ * @cs: GICv3CPUState
+ * @ptbase: physical address of LPI Pending table
+ * @ctbase: physical address of LPI Configuration table
+ * @ptsizebits: size of tables, specified as number of interrupt ID bits minus 1
+ * @ds: true if priority value should not be shifted
+ * @hpp: points to pending information to set
+ *
+ * Recalculate the highest priority pending enabled LPI from scratch,
+ * and set @hpp accordingly.
+ *
+ * We scan the LPI pending table @ptbase; for each pending LPI, we read the
+ * corresponding entry in the LPI configuration table @ctbase to extract
+ * the priority and enabled information.
+ *
+ * We take @ptsizebits in the form idbits-1 because this is the way that
+ * LPI table sizes are architecturally specified in GICR_PROPBASER.IDBits
+ * and in the VMAPP command's VPT_size field.
+ */
+static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase,
+                                uint64_t ctbase, unsigned ptsizebits,
+                                bool ds, PendingIrq *hpp)
+{
+    AddressSpace *as = &cs->gic->dma_as;
+    uint8_t pend;
+    uint32_t pendt_size = (1ULL << (ptsizebits + 1));
+    int i, bit;
+
+    hpp->prio = 0xff;
+
+    for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
+        address_space_read(as, ptbase + i, MEMTXATTRS_UNSPECIFIED, &pend, 1);
+        while (pend) {
+            bit = ctz32(pend);
+            update_for_one_lpi(cs, i * 8 + bit, ctbase, ds, hpp);
+            pend &= ~(1 << bit);
+        }
+    }
+}
+
 static uint8_t gicr_read_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs,
                                     int irq)
 {
@@ -657,11 +699,7 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs)
      * priority is lower than the last computed high priority lpi interrupt.
      * If yes, replace current LPI as the new high priority lpi interrupt.
      */
-    AddressSpace *as = &cs->gic->dma_as;
-    uint64_t lpipt_baddr;
-    uint32_t pendt_size = 0;
-    uint8_t pend;
-    int i, bit;
+    uint64_t lpipt_baddr, lpict_baddr;
     uint64_t idbits;
 
     idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
@@ -671,23 +709,11 @@ void gicv3_redist_update_lpi_only(GICv3CPUState *cs)
         return;
     }
 
-    cs->hpplpi.prio = 0xff;
-
     lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
+    lpict_baddr = cs->gicr_propbaser & R_GICR_PROPBASER_PHYADDR_MASK;
 
-    /* Determine the highest priority pending interrupt among LPIs */
-    pendt_size = (1ULL << (idbits + 1));
-
-    for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
-        address_space_read(as, lpipt_baddr + i, MEMTXATTRS_UNSPECIFIED, &pend,
-                           sizeof(pend));
-
-        while (pend) {
-            bit = ctz32(pend);
-            gicv3_redist_check_lpi_priority(cs, i * 8 + bit);
-            pend &= ~(1 << bit);
-        }
-    }
+    update_for_all_lpis(cs, lpipt_baddr, lpict_baddr, idbits,
+                        cs->gic->gicd_ctlr & GICD_CTLR_DS, &cs->hpplpi);
 }
 
 void gicv3_redist_update_lpi(GICv3CPUState *cs)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 29/41] hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (27 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 28/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for all LPIs" logic Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:10   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 30/41] hw/intc/arm_gicv3_redist: Factor out "update bit in pending table" code Peter Maydell
                   ` (12 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

The guest uses GICR_VPENDBASER to tell the redistributor when it is
scheduling or descheduling a vCPU.  When it writes and changes the
VALID bit from 0 to 1, it is scheduling a vCPU, and we must update
our view of the current highest priority pending vLPI from the new
Pending and Configuration tables.  When it writes and changes the
VALID bit from 1 to 0, it is descheduling, which means that there is
no longer a highest priority pending vLPI.

The specification allows the implementation to use part of the vLPI
Pending table as an IMPDEF area where it can cache information when a
vCPU is descheduled, so that it can avoid having to do a full rescan
of the tables when the vCPU is scheduled again.  For now, we don't
take advantage of this, and simply do a complete rescan.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 87 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 84 insertions(+), 3 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 2379389d14e..bfdde36a206 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -185,6 +185,87 @@ static void gicr_write_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs, int irq,
     cs->gicr_ipriorityr[irq] = value;
 }
 
+static void gicv3_redist_update_vlpi_only(GICv3CPUState *cs)
+{
+    uint64_t ptbase, ctbase, idbits;
+
+    if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) {
+        cs->hppvlpi.prio = 0xff;
+        return;
+    }
+
+    ptbase = cs->gicr_vpendbaser & R_GICR_VPENDBASER_PHYADDR_MASK;
+    ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK;
+    idbits = FIELD_EX64(cs->gicr_vpropbaser, GICR_VPROPBASER, IDBITS);
+
+    update_for_all_lpis(cs, ptbase, ctbase, idbits, true, &cs->hppvlpi);
+}
+
+static void gicv3_redist_update_vlpi(GICv3CPUState *cs)
+{
+    gicv3_redist_update_vlpi_only(cs);
+    gicv3_cpuif_virt_irq_fiq_update(cs);
+}
+
+static void gicr_write_vpendbaser(GICv3CPUState *cs, uint64_t newval)
+{
+    /* Write @newval to GICR_VPENDBASER, handling its effects */
+    bool oldvalid = FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID);
+    bool newvalid = FIELD_EX64(newval, GICR_VPENDBASER, VALID);
+    bool pendinglast;
+
+    /*
+     * The DIRTY bit is read-only and for us is always zero;
+     * other fields are writeable.
+     */
+    newval &= R_GICR_VPENDBASER_INNERCACHE_MASK |
+        R_GICR_VPENDBASER_SHAREABILITY_MASK |
+        R_GICR_VPENDBASER_PHYADDR_MASK |
+        R_GICR_VPENDBASER_OUTERCACHE_MASK |
+        R_GICR_VPENDBASER_PENDINGLAST_MASK |
+        R_GICR_VPENDBASER_IDAI_MASK |
+        R_GICR_VPENDBASER_VALID_MASK;
+
+    if (oldvalid && newvalid) {
+        /*
+         * Changing other fields while VALID is 1 is UNPREDICTABLE;
+         * we choose to log and ignore the write.
+         */
+        if (cs->gicr_vpendbaser ^ newval) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: Changing GICR_VPENDBASER when VALID=1 "
+                          "is UNPREDICTABLE\n", __func__);
+        }
+        return;
+    }
+    if (!oldvalid && !newvalid) {
+        cs->gicr_vpendbaser = newval;
+        return;
+    }
+
+    if (newvalid) {
+        /*
+         * Valid going from 0 to 1: update hppvlpi from tables.
+         * If IDAI is 0 we are allowed to use the info we cached in
+         * the IMPDEF area of the table.
+         * PendingLast is RES1 when we make this transition.
+         */
+        pendinglast = true;
+    } else {
+        /*
+         * Valid going from 1 to 0:
+         * Set PendingLast if there was a pending enabled interrupt
+         * for the vPE that was just descheduled.
+         * If we cache info in the IMPDEF area, write it out here.
+         */
+        pendinglast = cs->hppvlpi.prio != 0xff;
+    }
+
+    newval = FIELD_DP64(newval, GICR_VPENDBASER, PENDINGLAST, pendinglast);
+    cs->gicr_vpendbaser = newval;
+    gicv3_redist_update_vlpi(cs);
+}
+
 static MemTxResult gicr_readb(GICv3CPUState *cs, hwaddr offset,
                               uint64_t *data, MemTxAttrs attrs)
 {
@@ -493,10 +574,10 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
         cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 32, 32, value);
         return MEMTX_OK;
     case GICR_VPENDBASER:
-        cs->gicr_vpendbaser = deposit64(cs->gicr_vpendbaser, 0, 32, value);
+        gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 0, 32, value));
         return MEMTX_OK;
     case GICR_VPENDBASER + 4:
-        cs->gicr_vpendbaser = deposit64(cs->gicr_vpendbaser, 32, 32, value);
+        gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 32, 32, value));
         return MEMTX_OK;
     default:
         return MEMTX_ERROR;
@@ -557,7 +638,7 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
         cs->gicr_vpropbaser = value;
         return MEMTX_OK;
     case GICR_VPENDBASER:
-        cs->gicr_vpendbaser = value;
+        gicr_write_vpendbaser(cs, value);
         return MEMTX_OK;
     default:
         return MEMTX_ERROR;
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 30/41] hw/intc/arm_gicv3_redist: Factor out "update bit in pending table" code
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (28 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 29/41] hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:21   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 31/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_process_vlpi() Peter Maydell
                   ` (11 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Factor out the code which sets a single bit in an LPI pending table.
We're going to need this for handling vLPI tables, not just the
physical LPI table.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 49 +++++++++++++++++++++++---------------
 1 file changed, 30 insertions(+), 19 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index bfdde36a206..64e5d96ac36 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -145,6 +145,34 @@ static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase,
     }
 }
 
+/**
+ * set_lpi_pending_bit: Set or clear pending bit for an LPI
+ *
+ * @cs: GICv3CPUState
+ * @ptbase: physical address of LPI Pending table
+ * @irq: LPI to change pending state for
+ * @level: 0 to clear pending state, 1 to set
+ *
+ * Returns true if we needed to do something, false if the pending bit
+ * was already at @level.
+ */
+static bool set_pending_table_bit(GICv3CPUState *cs, uint64_t ptbase,
+                                  int irq, int level)
+{
+    AddressSpace *as = &cs->gic->dma_as;
+    uint64_t addr = ptbase + irq / 8;
+    uint8_t pend;
+
+    address_space_read(as, addr, MEMTXATTRS_UNSPECIFIED, &pend, 1);
+    if (extract32(pend, irq % 8, 1) == level) {
+        /* Bit already at requested state, no action required */
+        return false;
+    }
+    pend = deposit32(pend, irq % 8, 1, level ? 1 : 0);
+    address_space_write(as, addr, MEMTXATTRS_UNSPECIFIED, &pend, 1);
+    return true;
+}
+
 static uint8_t gicr_read_ipriorityr(GICv3CPUState *cs, MemTxAttrs attrs,
                                     int irq)
 {
@@ -809,30 +837,13 @@ void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level)
      * This function updates the pending bit in lpi pending table for
      * the irq being activated or deactivated.
      */
-    AddressSpace *as = &cs->gic->dma_as;
     uint64_t lpipt_baddr;
-    bool ispend = false;
-    uint8_t pend;
 
-    /*
-     * get the bit value corresponding to this irq in the
-     * lpi pending table
-     */
     lpipt_baddr = cs->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
-
-    address_space_read(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
-                       MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
-
-    ispend = extract32(pend, irq % 8, 1);
-
-    /* no change in the value of pending bit, return */
-    if (ispend == level) {
+    if (!set_pending_table_bit(cs, lpipt_baddr, irq, level)) {
+        /* no change in the value of pending bit, return */
         return;
     }
-    pend = deposit32(pend, irq % 8, 1, level ? 1 : 0);
-
-    address_space_write(as, lpipt_baddr + ((irq / 8) * sizeof(pend)),
-                        MEMTXATTRS_UNSPECIFIED, &pend, sizeof(pend));
 
     /*
      * check if this LPI is better than the current hpplpi, if yes
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 31/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_process_vlpi()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (29 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 30/41] hw/intc/arm_gicv3_redist: Factor out "update bit in pending table" code Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:28   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 32/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vlpi_pending() Peter Maydell
                   ` (10 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the function gicv3_redist_process_vlpi(), which was left as
just a stub earlier.  This function deals with being handed a VLPI by
the ITS.  It must set the bit in the pending table.  If the vCPU is
currently resident we must recalculate the highest priority pending
vLPI; otherwise we may need to ring a "doorbell" interrupt to let the
hypervisor know it might want to reschedule the vCPU.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 48 ++++++++++++++++++++++++++++++++++----
 1 file changed, 44 insertions(+), 4 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 64e5d96ac36..be36978b45c 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -60,6 +60,19 @@ static uint32_t gicr_read_bitmap_reg(GICv3CPUState *cs, MemTxAttrs attrs,
     return reg;
 }
 
+static bool vcpu_resident(GICv3CPUState *cs, uint64_t vptaddr)
+{
+    /*
+     * Return true if a vCPU is resident, which is defined by
+     * whether the GICR_VPENDBASER register is marked VALID and
+     * has the right virtual pending table address.
+     */
+    if (!FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, VALID)) {
+        return false;
+    }
+    return vptaddr == (cs->gicr_vpendbaser & R_GICR_VPENDBASER_PHYADDR_MASK);
+}
+
 /**
  * update_for_one_lpi: Update pending information if this LPI is better
  *
@@ -1004,10 +1017,37 @@ void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level)
 void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
                                int doorbell, int level)
 {
-    /*
-     * The redistributor handling for being handed a VLPI by the ITS
-     * will be added in a subsequent commit.
-     */
+    bool bit_changed;
+    bool resident = vcpu_resident(cs, vptaddr);
+    uint64_t ctbase;
+
+    if (resident) {
+        uint32_t idbits = FIELD_EX64(cs->gicr_vpropbaser, GICR_VPROPBASER, IDBITS);
+        if (irq >= (1ULL << (idbits + 1))) {
+            return;
+        }
+    }
+
+    bit_changed = set_pending_table_bit(cs, vptaddr, irq, level);
+    if (resident && bit_changed) {
+        if (level) {
+            /* Check whether this vLPI is now the best */
+            ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK;
+            update_for_one_lpi(cs, irq, ctbase, true, &cs->hppvlpi);
+            gicv3_cpuif_virt_irq_fiq_update(cs);
+        } else {
+            /* Only need to recalculate if this was previously the best vLPI */
+            if (irq == cs->hppvlpi.irq) {
+                gicv3_redist_update_vlpi(cs);
+            }
+        }
+    }
+
+    if (!resident && level && doorbell != INTID_SPURIOUS &&
+        (cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
+        /* vCPU is not currently resident: ring the doorbell */
+        gicv3_redist_process_lpi(cs, doorbell, 1);
+    }
 }
 
 void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 32/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vlpi_pending()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (30 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 31/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_process_vlpi() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:32   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 33/41] hw/intc/arm_gicv3_redist: Use set_pending_table_bit() in mov handling Peter Maydell
                   ` (9 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the function gicv3_redist_vlpi_pending(), which was
previously left as a stub.  This is the function that is called by
the CPU interface when it changes the state of a vLPI.  It's similar
to gicv3_redist_process_vlpi(), but we know that the vCPU is
definitely resident on the redistributor and the irq is in range, so
it is a bit simpler.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index be36978b45c..eadf5e8265e 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -1009,9 +1009,28 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
 void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level)
 {
     /*
-     * The redistributor handling for changing the pending state
-     * of a vLPI will be added in a subsequent commit.
+     * Change the pending state of the specified vLPI.
+     * Unlike gicv3_redist_process_vlpi(), we know here that the
+     * vCPU is definitely resident on this redistributor, and that
+     * the irq is in range.
      */
+    uint64_t vptbase, ctbase;
+
+    vptbase = FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, PHYADDR) << 16;
+
+    if (set_pending_table_bit(cs, vptbase, irq, level)) {
+        if (level) {
+            /* Check whether this vLPI is now the best */
+            ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK;
+            update_for_one_lpi(cs, irq, ctbase, true, &cs->hppvlpi);
+            gicv3_cpuif_virt_irq_fiq_update(cs);
+        } else {
+            /* Only need to recalculate if this was previously the best vLPI */
+            if (irq == cs->hppvlpi.irq) {
+                gicv3_redist_update_vlpi(cs);
+            }
+        }
+    }
 }
 
 void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 33/41] hw/intc/arm_gicv3_redist: Use set_pending_table_bit() in mov handling
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (31 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 32/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vlpi_pending() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:34   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 34/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_mov_vlpi() Peter Maydell
                   ` (8 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

We can use our new set_pending_table_bit() utility function
in gicv3_redist_mov_lpi() to clear the bit in the source
pending table, rather than doing the "load, clear bit, store"
ourselves.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index eadf5e8265e..3127af3e2ca 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -909,11 +909,9 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
      * we choose to NOP. If LPIs are disabled on source there's nothing
      * to be transferred anyway.
      */
-    AddressSpace *as = &src->gic->dma_as;
     uint64_t idbits;
     uint32_t pendt_size;
     uint64_t src_baddr;
-    uint8_t src_pend;
 
     if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
         !(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
@@ -932,15 +930,10 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
 
     src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
 
-    address_space_read(as, src_baddr + (irq / 8),
-                       MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
-    if (!extract32(src_pend, irq % 8, 1)) {
+    if (!set_pending_table_bit(src, src_baddr, irq, 0)) {
         /* Not pending on source, nothing to do */
         return;
     }
-    src_pend &= ~(1 << (irq % 8));
-    address_space_write(as, src_baddr + (irq / 8),
-                        MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
     if (irq == src->hpplpi.irq) {
         /*
          * We just made this LPI not-pending so only need to update
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 34/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_mov_vlpi()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (32 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 33/41] hw/intc/arm_gicv3_redist: Use set_pending_table_bit() in mov handling Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:39   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 35/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vinvall() Peter Maydell
                   ` (7 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the gicv3_redist_mov_vlpi() function (previously left as a
stub).  This function handles the work of a VMOVI command: it marks
the vLPI not-pending on the source and pending on the destination.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 3127af3e2ca..9866dd94c60 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -1067,9 +1067,25 @@ void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
                            int irq, int doorbell)
 {
     /*
-     * The redistributor handling for moving a VLPI will be added
-     * in a subsequent commit.
+     * Move the specified vLPI's pending state from the source redistributor
+     * to the destination.
      */
+    if (!set_pending_table_bit(src, src_vptaddr, irq, 0)) {
+        /* Not pending on source, nothing to do */
+        return;
+    }
+    if (vcpu_resident(src, src_vptaddr) && irq == src->hppvlpi.irq) {
+        /*
+         * Update src's cached highest-priority pending vLPI if we just made
+         * it not-pending
+         */
+        gicv3_redist_update_vlpi(src);
+    }
+    /*
+     * Mark the vLPI pending on the destination (ringing the doorbell
+     * if the vCPU isn't resident)
+     */
+    gicv3_redist_process_vlpi(dest, irq, dest_vptaddr, doorbell, irq);
 }
 
 void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 35/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vinvall()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (33 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 34/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_mov_vlpi() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:40   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 36/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_inv_vlpi() Peter Maydell
                   ` (6 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the gicv3_redist_vinvall() function (previously left as a
stub).  This function handles the work of a VINVALL command: it must
invalidate any cached information associated with a specific vCPU.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 9866dd94c60..a586c9ef498 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -1090,7 +1090,13 @@ void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
 
 void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr)
 {
-    /* The redistributor handling will be added in a subsequent commit */
+    if (!vcpu_resident(cs, vptaddr)) {
+        /* We don't have anything cached if the vCPU isn't resident */
+        return;
+    }
+
+    /* Otherwise, our only cached information is the HPPVLPI info */
+    gicv3_redist_update_vlpi(cs);
 }
 
 void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 36/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_inv_vlpi()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (34 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 35/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vinvall() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:40   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 37/41] hw/intc/arm_gicv3: Update ID and feature registers for GICv4 Peter Maydell
                   ` (5 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Implement the function gicv3_redist_inv_vlpi(), which was previously
left as a stub.  This is the function that does the work of the INV
command for a virtual interrupt.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_redist.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index a586c9ef498..0738d3822d1 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -1102,9 +1102,12 @@ void gicv3_redist_vinvall(GICv3CPUState *cs, uint64_t vptaddr)
 void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr)
 {
     /*
-     * The redistributor handling for invalidating cached information
-     * about a VLPI will be added in a subsequent commit.
+     * The only cached information for LPIs we have is the HPPLPI.
+     * We could be cleverer about identifying when we don't need
+     * to do a full rescan of the pending table, but until we find
+     * this is a performance issue, just always recalculate.
      */
+    gicv3_redist_vinvall(cs, vptaddr);
 }
 
 void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 37/41] hw/intc/arm_gicv3: Update ID and feature registers for GICv4
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (35 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 36/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_inv_vlpi() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:47   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 38/41] hw/intc/arm_gicv3: Allow 'revision' property to be set to 4 Peter Maydell
                   ` (4 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Update the various GIC ID and feature registers for GICv4:
 * PIDR2 [7:4] is the GIC architecture revision
 * GICD_TYPER.DVIS is 1 to indicate direct vLPI injection support
 * GICR_TYPER.VLPIS is 1 to indicate redistributor support for vLPIs
 * GITS_TYPER.VIRTUAL is 1 to indicate vLPI support
 * GITS_TYPER.VMOVP is 1 to indicate that our VMOVP implementation
   handles cross-ITS synchronization for the guest
 * ICH_VTR_EL2.nV4 is 0 to indicate direct vLPI injection support

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/gicv3_internal.h   | 15 +++++++++++----
 hw/intc/arm_gicv3_common.c |  7 +++++--
 hw/intc/arm_gicv3_cpuif.c  |  6 +++++-
 hw/intc/arm_gicv3_dist.c   |  7 ++++---
 hw/intc/arm_gicv3_its.c    |  7 ++++++-
 hw/intc/arm_gicv3_redist.c |  2 +-
 6 files changed, 32 insertions(+), 12 deletions(-)

diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index 07644b2be6f..0bf68452395 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -309,6 +309,7 @@ FIELD(GITS_TYPER, SEIS, 18, 1)
 FIELD(GITS_TYPER, PTA, 19, 1)
 FIELD(GITS_TYPER, CIDBITS, 32, 4)
 FIELD(GITS_TYPER, CIL, 36, 1)
+FIELD(GITS_TYPER, VMOVP, 37, 1)
 
 #define GITS_IDREGS           0xFFD0
 
@@ -747,23 +748,29 @@ static inline uint32_t gicv3_iidr(void)
 #define GICV3_PIDR0_REDIST 0x93
 #define GICV3_PIDR0_ITS 0x94
 
-static inline uint32_t gicv3_idreg(int regoffset, uint8_t pidr0)
+static inline uint32_t gicv3_idreg(GICv3State *s, int regoffset, uint8_t pidr0)
 {
     /* Return the value of the CoreSight ID register at the specified
      * offset from the first ID register (as found in the distributor
      * and redistributor register banks).
-     * These values indicate an ARM implementation of a GICv3.
+     * These values indicate an ARM implementation of a GICv3 or v4.
      */
     static const uint8_t gicd_ids[] = {
-        0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x3B, 0x00, 0x0D, 0xF0, 0x05, 0xB1
+        0x44, 0x00, 0x00, 0x00, 0x92, 0xB4, 0x0B, 0x00, 0x0D, 0xF0, 0x05, 0xB1
     };
+    uint32_t id;
 
     regoffset /= 4;
 
     if (regoffset == 4) {
         return pidr0;
     }
-    return gicd_ids[regoffset];
+    id = gicd_ids[regoffset];
+    if (regoffset == 6) {
+        /* PIDR2 bits [7:4] are the GIC architecture revision */
+        id |= s->revision << 4;
+    }
+    return id;
 }
 
 /**
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 3f47b3501fe..181f342f32c 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -406,8 +406,8 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
          *  Last == 1 if this is the last redistributor in a series of
          *            contiguous redistributor pages
          *  DirectLPI == 0 (direct injection of LPIs not supported)
-         *  VLPIS == 0 (virtual LPIs not supported)
-         *  PLPIS == 0 (physical LPIs not supported)
+         *  VLPIS == 1 if vLPIs supported (GICv4 and up)
+         *  PLPIS == 1 if LPIs supported
          */
         cpu_affid = object_property_get_uint(OBJECT(cpu), "mp-affinity", NULL);
 
@@ -422,6 +422,9 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
 
         if (s->lpi_enable) {
             s->cpu[i].gicr_typer |= GICR_TYPER_PLPIS;
+            if (s->revision > 3) {
+                s->cpu[i].gicr_typer |= GICR_TYPER_VLPIS;
+            }
         }
     }
 
diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c
index d627ddac90f..8404f46ee0b 100644
--- a/hw/intc/arm_gicv3_cpuif.c
+++ b/hw/intc/arm_gicv3_cpuif.c
@@ -2578,11 +2578,15 @@ static uint64_t ich_vtr_read(CPUARMState *env, const ARMCPRegInfo *ri)
     uint64_t value;
 
     value = ((cs->num_list_regs - 1) << ICH_VTR_EL2_LISTREGS_SHIFT)
-        | ICH_VTR_EL2_TDS | ICH_VTR_EL2_NV4 | ICH_VTR_EL2_A3V
+        | ICH_VTR_EL2_TDS | ICH_VTR_EL2_A3V
         | (1 << ICH_VTR_EL2_IDBITS_SHIFT)
         | ((cs->vprebits - 1) << ICH_VTR_EL2_PREBITS_SHIFT)
         | ((cs->vpribits - 1) << ICH_VTR_EL2_PRIBITS_SHIFT);
 
+    if (cs->gic->revision < 4) {
+        value |= ICH_VTR_EL2_NV4;
+    }
+
     trace_gicv3_ich_vtr_read(gicv3_redist_affid(cs), value);
     return value;
 }
diff --git a/hw/intc/arm_gicv3_dist.c b/hw/intc/arm_gicv3_dist.c
index 7f6275363ea..b9ed955e36b 100644
--- a/hw/intc/arm_gicv3_dist.c
+++ b/hw/intc/arm_gicv3_dist.c
@@ -383,7 +383,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset,
          * No1N == 1 (1-of-N SPI interrupts not supported)
          * A3V == 1 (non-zero values of Affinity level 3 supported)
          * IDbits == 0xf (we support 16-bit interrupt identifiers)
-         * DVIS == 0 (Direct virtual LPI injection not supported)
+         * DVIS == 1 (Direct virtual LPI injection supported) if GICv4
          * LPIS == 1 (LPIs are supported if affinity routing is enabled)
          * num_LPIs == 0b00000 (bits [15:11],Number of LPIs as indicated
          *                      by GICD_TYPER.IDbits)
@@ -399,8 +399,9 @@ static bool gicd_readl(GICv3State *s, hwaddr offset,
          * so we only need to check the DS bit.
          */
         bool sec_extn = !(s->gicd_ctlr & GICD_CTLR_DS);
+        bool dvis = s->revision >= 4;
 
-        *data = (1 << 25) | (1 << 24) | (sec_extn << 10) |
+        *data = (1 << 25) | (1 << 24) | (dvis << 18) | (sec_extn << 10) |
             (s->lpi_enable << GICD_TYPER_LPIS_SHIFT) |
             (0xf << 19) | itlinesnumber;
         return true;
@@ -557,7 +558,7 @@ static bool gicd_readl(GICv3State *s, hwaddr offset,
     }
     case GICD_IDREGS ... GICD_IDREGS + 0x2f:
         /* ID registers */
-        *data = gicv3_idreg(offset - GICD_IDREGS, GICV3_PIDR0_DIST);
+        *data = gicv3_idreg(s, offset - GICD_IDREGS, GICV3_PIDR0_DIST);
         return true;
     case GICD_SGIR:
         /* WO registers, return unknown value */
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index 6c44cccd369..56f43faafd4 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -1699,7 +1699,7 @@ static bool its_readl(GICv3ITSState *s, hwaddr offset,
         break;
     case GITS_IDREGS ... GITS_IDREGS + 0x2f:
         /* ID registers */
-        *data = gicv3_idreg(offset - GITS_IDREGS, GICV3_PIDR0_ITS);
+        *data = gicv3_idreg(s->gicv3, offset - GITS_IDREGS, GICV3_PIDR0_ITS);
         break;
     case GITS_TYPER:
         *data = extract64(s->typer, 0, 32);
@@ -1946,6 +1946,11 @@ static void gicv3_arm_its_realize(DeviceState *dev, Error **errp)
     s->typer = FIELD_DP64(s->typer, GITS_TYPER, DEVBITS, ITS_DEVBITS);
     s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIL, 1);
     s->typer = FIELD_DP64(s->typer, GITS_TYPER, CIDBITS, ITS_CIDBITS);
+    if (s->gicv3->revision >= 4) {
+        /* Our VMOVP handles cross-ITS synchronization itself */
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, VMOVP, 1);
+        s->typer = FIELD_DP64(s->typer, GITS_TYPER, VIRTUAL, 1);
+    }
 }
 
 static void gicv3_its_reset(DeviceState *dev)
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index 0738d3822d1..82dab296456 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -441,7 +441,7 @@ static MemTxResult gicr_readl(GICv3CPUState *cs, hwaddr offset,
         *data = cs->gicr_nsacr;
         return MEMTX_OK;
     case GICR_IDREGS ... GICR_IDREGS + 0x2f:
-        *data = gicv3_idreg(offset - GICR_IDREGS, GICV3_PIDR0_REDIST);
+        *data = gicv3_idreg(cs->gic, offset - GICR_IDREGS, GICV3_PIDR0_REDIST);
         return MEMTX_OK;
         /*
          * VLPI frame registers. We don't need a version check for
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 38/41] hw/intc/arm_gicv3: Allow 'revision' property to be set to 4
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (36 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 37/41] hw/intc/arm_gicv3: Update ID and feature registers for GICv4 Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:50   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 39/41] hw/arm/virt: Use VIRT_GIC_VERSION_* enum values in create_gic() Peter Maydell
                   ` (3 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Now that we have implemented all the GICv4 requirements, relax the
error-checking on the GIC object's 'revision' property to allow a TCG
GIC to be a GICv4, whilst still constraining the KVM GIC to GICv3.

Our 'revision' property doesn't consider the possibility of wanting
to specify the minor version of the GIC -- for instance there is a
GICv3.1 which adds support for extended SPI and PPI ranges, among
other things, and also GICv4.1.  But since the QOM property is
internal to QEMU, not user-facing, we can cross that bridge when we
come to it. Within the GIC implementation itself code generally
checks against the appropriate ID register feature bits, and the
only use of s->revision is for setting those ID register bits.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv3_common.c | 12 +++++++-----
 hw/intc/arm_gicv3_kvm.c    |  5 +++++
 2 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 181f342f32c..5634c6fc788 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -326,12 +326,14 @@ static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
     GICv3State *s = ARM_GICV3_COMMON(dev);
     int i, rdist_capacity, cpuidx;
 
-    /* revision property is actually reserved and currently used only in order
-     * to keep the interface compatible with GICv2 code, avoiding extra
-     * conditions. However, in future it could be used, for example, if we
-     * implement GICv4.
+    /*
+     * This GIC device supports only revisions 3 and 4. The GICv1/v2
+     * is a separate device.
+     * Note that subclasses of this device may impose further restrictions
+     * on the GIC revision: notably, the in-kernel KVM GIC doesn't
+     * support GICv4.
      */
-    if (s->revision != 3) {
+    if (s->revision != 3 && s->revision != 4) {
         error_setg(errp, "unsupported GIC revision %d", s->revision);
         return;
     }
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 5ec5ff9ef6e..06f5aceee52 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -781,6 +781,11 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
         return;
     }
 
+    if (s->revision != 3) {
+        error_setg(errp, "unsupported GIC revision %d for in-kernel GIC",
+                   s->revision);
+    }
+
     if (s->security_extn) {
         error_setg(errp, "the in-kernel VGICv3 does not implement the "
                    "security extensions");
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 39/41] hw/arm/virt: Use VIRT_GIC_VERSION_* enum values in create_gic()
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (37 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 38/41] hw/intc/arm_gicv3: Allow 'revision' property to be set to 4 Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:52   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 40/41] hw/arm/virt: Abstract out calculation of redistributor region capacity Peter Maydell
                   ` (2 subsequent siblings)
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Everywhere we need to check which GIC version we're using, we look at
vms->gic_version and use the VIRT_GIC_VERSION_* enum values, except
in create_gic(), which copies vms->gic_version into a local 'int'
variable and makes direct comparisons against values 2 and 3.

For consistency, change this function to check the GIC version
the same way we do elsewhere. This includes not implicitly relying
on the enumeration type values happening to match the integer
'revision' values the GIC device object wants.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/arm/virt.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index d2e5ecd234a..594a3d0660a 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -690,14 +690,29 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
     /* We create a standalone GIC */
     SysBusDevice *gicbusdev;
     const char *gictype;
-    int type = vms->gic_version, i;
+    int i;
     unsigned int smp_cpus = ms->smp.cpus;
     uint32_t nb_redist_regions = 0;
+    int revision;
 
-    gictype = (type == 3) ? gicv3_class_name() : gic_class_name();
+    if (vms->gic_version == VIRT_GIC_VERSION_2) {
+        gictype = gic_class_name();
+    } else {
+        gictype = gicv3_class_name();
+    }
 
+    switch (vms->gic_version) {
+    case VIRT_GIC_VERSION_2:
+        revision = 2;
+        break;
+    case VIRT_GIC_VERSION_3:
+        revision = 3;
+        break;
+    default:
+        g_assert_not_reached();
+    }
     vms->gic = qdev_new(gictype);
-    qdev_prop_set_uint32(vms->gic, "revision", type);
+    qdev_prop_set_uint32(vms->gic, "revision", revision);
     qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus);
     /* Note that the num-irq property counts both internal and external
      * interrupts; there are always 32 of the former (mandated by GIC spec).
@@ -707,7 +722,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
         qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure);
     }
 
-    if (type == 3) {
+    if (vms->gic_version == VIRT_GIC_VERSION_3) {
         uint32_t redist0_capacity =
                     vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
         uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
@@ -742,7 +757,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
     gicbusdev = SYS_BUS_DEVICE(vms->gic);
     sysbus_realize_and_unref(gicbusdev, &error_fatal);
     sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base);
-    if (type == 3) {
+    if (vms->gic_version == VIRT_GIC_VERSION_3) {
         sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base);
         if (nb_redist_regions == 2) {
             sysbus_mmio_map(gicbusdev, 2,
@@ -780,7 +795,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
                                                    ppibase + timer_irq[irq]));
         }
 
-        if (type == 3) {
+        if (vms->gic_version == VIRT_GIC_VERSION_3) {
             qemu_irq irq = qdev_get_gpio_in(vms->gic,
                                             ppibase + ARCH_GIC_MAINT_IRQ);
             qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
@@ -806,9 +821,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
 
     fdt_add_gic_node(vms);
 
-    if (type == 3 && vms->its) {
+    if (vms->gic_version == VIRT_GIC_VERSION_3 && vms->its) {
         create_its(vms);
-    } else if (type == 2) {
+    } else if (vms->gic_version == VIRT_GIC_VERSION_2) {
         create_v2m(vms);
     }
 }
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 40/41] hw/arm/virt: Abstract out calculation of redistributor region capacity
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (38 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 39/41] hw/arm/virt: Use VIRT_GIC_VERSION_* enum values in create_gic() Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:53   ` Richard Henderson
  2022-04-08 14:15 ` [PATCH 41/41] hw/arm/virt: Support TCG GICv4 Peter Maydell
  2022-04-08 14:29 ` [PATCH 00/41] arm: Implement GICv4 Peter Maydell
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

In several places in virt.c we calculate the number of redistributors that
fit in a region of our memory map, which is the size of the region
divided by the size of a single redistributor frame. For GICv4, the
redistributor frame is a different size from that for GICv3. Abstract
out the calculation of redistributor region capacity so that we have
one place we need to change to handle GICv4 rather than several.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 include/hw/arm/virt.h |  9 +++++++--
 hw/arm/virt.c         | 11 ++++-------
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 7e76ee26198..360463e6bfb 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -185,11 +185,16 @@ OBJECT_DECLARE_TYPE(VirtMachineState, VirtMachineClass, VIRT_MACHINE)
 void virt_acpi_setup(VirtMachineState *vms);
 bool virt_is_acpi_enabled(VirtMachineState *vms);
 
+/* Return number of redistributors that fit in the specified region */
+static uint32_t virt_redist_capacity(VirtMachineState *vms, int region)
+{
+    return vms->memmap[region].size / GICV3_REDIST_SIZE;
+}
+
 /* Return the number of used redistributor regions  */
 static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
 {
-    uint32_t redist0_capacity =
-                vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+    uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);
 
     assert(vms->gic_version == VIRT_GIC_VERSION_3);
 
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 594a3d0660a..577c1e65188 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -723,8 +723,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
     }
 
     if (vms->gic_version == VIRT_GIC_VERSION_3) {
-        uint32_t redist0_capacity =
-                    vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
+        uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);
         uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
 
         nb_redist_regions = virt_gicv3_redist_region_count(vms);
@@ -743,7 +742,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
 
         if (nb_redist_regions == 2) {
             uint32_t redist1_capacity =
-                    vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
+                virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2);
 
             qdev_prop_set_uint32(vms->gic, "redist-region-count[1]",
                 MIN(smp_cpus - redist0_count, redist1_capacity));
@@ -2048,10 +2047,8 @@ static void machvirt_init(MachineState *machine)
      * many redistributors we can fit into the memory map.
      */
     if (vms->gic_version == VIRT_GIC_VERSION_3) {
-        virt_max_cpus =
-            vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE;
-        virt_max_cpus +=
-            vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE;
+        virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST) +
+            virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2);
     } else {
         virt_max_cpus = GIC_NCPU;
     }
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* [PATCH 41/41] hw/arm/virt: Support TCG GICv4
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (39 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 40/41] hw/arm/virt: Abstract out calculation of redistributor region capacity Peter Maydell
@ 2022-04-08 14:15 ` Peter Maydell
  2022-04-09 20:55   ` Richard Henderson
  2022-04-08 14:29 ` [PATCH 00/41] arm: Implement GICv4 Peter Maydell
  41 siblings, 1 reply; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:15 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

Add support for the TCG GICv4 to the virt board. For the board,
the GICv4 is very similar to the GICv3, with the only difference
being the size of the redistributor frame. The changes here are thus:
 * calculating virt_redist_capacity correctly for GICv4
 * changing various places which were "if GICv3" to be "if not GICv2"
 * the commandline option handling

Note that using GICv4 reduces the maximum possible number of CPUs on
the virt board from 512 to 317, because we can now only fit half as
many redistributors into the redistributor regions we have defined.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 docs/system/arm/virt.rst |  5 ++-
 include/hw/arm/virt.h    | 12 +++++--
 hw/arm/virt.c            | 70 ++++++++++++++++++++++++++++++----------
 3 files changed, 67 insertions(+), 20 deletions(-)

diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index 1544632b674..5d13ec2798a 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -99,11 +99,14 @@ gic-version
     GICv2
   ``3``
     GICv3
+  ``4``
+    GICv4 (requires ``virtualization`` to be ``on``)
   ``host``
     Use the same GIC version the host provides, when using KVM
   ``max``
     Use the best GIC version possible (same as host when using KVM;
-    currently same as ``3``` for TCG, but this may change in future)
+    with TCG this is currently ``3`` if ``virtualization`` is ``off`` and
+    ``4`` if ``virtualization`` is ``on``, but this may change in future)
 
 its
   Set ``on``/``off`` to enable/disable ITS instantiation. The default is ``on``
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index 360463e6bfb..15feabac63d 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -113,6 +113,7 @@ typedef enum VirtGICType {
     VIRT_GIC_VERSION_HOST,
     VIRT_GIC_VERSION_2,
     VIRT_GIC_VERSION_3,
+    VIRT_GIC_VERSION_4,
     VIRT_GIC_VERSION_NOSEL,
 } VirtGICType;
 
@@ -188,7 +189,14 @@ bool virt_is_acpi_enabled(VirtMachineState *vms);
 /* Return number of redistributors that fit in the specified region */
 static uint32_t virt_redist_capacity(VirtMachineState *vms, int region)
 {
-    return vms->memmap[region].size / GICV3_REDIST_SIZE;
+    uint32_t redist_size;
+
+    if (vms->gic_version == VIRT_GIC_VERSION_3) {
+        redist_size = GICV3_REDIST_SIZE;
+    } else {
+        redist_size = GICV4_REDIST_SIZE;
+    }
+    return vms->memmap[region].size / redist_size;
 }
 
 /* Return the number of used redistributor regions  */
@@ -196,7 +204,7 @@ static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
 {
     uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);
 
-    assert(vms->gic_version == VIRT_GIC_VERSION_3);
+    assert(vms->gic_version != VIRT_GIC_VERSION_2);
 
     return (MACHINE(vms)->smp.cpus > redist0_capacity &&
             vms->highmem_redists) ? 2 : 1;
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 577c1e65188..dfedc6b22ee 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -522,7 +522,7 @@ static void fdt_add_gic_node(VirtMachineState *vms)
     qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2);
     qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2);
     qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0);
-    if (vms->gic_version == VIRT_GIC_VERSION_3) {
+    if (vms->gic_version != VIRT_GIC_VERSION_2) {
         int nb_redist_regions = virt_gicv3_redist_region_count(vms);
 
         qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
@@ -708,6 +708,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
     case VIRT_GIC_VERSION_3:
         revision = 3;
         break;
+    case VIRT_GIC_VERSION_4:
+        revision = 4;
+        break;
     default:
         g_assert_not_reached();
     }
@@ -722,7 +725,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
         qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure);
     }
 
-    if (vms->gic_version == VIRT_GIC_VERSION_3) {
+    if (vms->gic_version != VIRT_GIC_VERSION_2) {
         uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);
         uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);
 
@@ -756,7 +759,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
     gicbusdev = SYS_BUS_DEVICE(vms->gic);
     sysbus_realize_and_unref(gicbusdev, &error_fatal);
     sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base);
-    if (vms->gic_version == VIRT_GIC_VERSION_3) {
+    if (vms->gic_version != VIRT_GIC_VERSION_2) {
         sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base);
         if (nb_redist_regions == 2) {
             sysbus_mmio_map(gicbusdev, 2,
@@ -794,7 +797,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
                                                    ppibase + timer_irq[irq]));
         }
 
-        if (vms->gic_version == VIRT_GIC_VERSION_3) {
+        if (vms->gic_version != VIRT_GIC_VERSION_2) {
             qemu_irq irq = qdev_get_gpio_in(vms->gic,
                                             ppibase + ARCH_GIC_MAINT_IRQ);
             qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
@@ -820,7 +823,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
 
     fdt_add_gic_node(vms);
 
-    if (vms->gic_version == VIRT_GIC_VERSION_3 && vms->its) {
+    if (vms->gic_version != VIRT_GIC_VERSION_2 && vms->its) {
         create_its(vms);
     } else if (vms->gic_version == VIRT_GIC_VERSION_2) {
         create_v2m(vms);
@@ -1672,10 +1675,10 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
          * purposes are to make TCG consistent (with 64-bit KVM hosts)
          * and to improve SGI efficiency.
          */
-        if (vms->gic_version == VIRT_GIC_VERSION_3) {
-            clustersz = GICV3_TARGETLIST_BITS;
-        } else {
+        if (vms->gic_version == VIRT_GIC_VERSION_2) {
             clustersz = GIC_TARGETLIST_BITS;
+        } else {
+            clustersz = GICV3_TARGETLIST_BITS;
         }
     }
     return arm_cpu_mp_affinity(idx, clustersz);
@@ -1808,6 +1811,10 @@ static void finalize_gic_version(VirtMachineState *vms)
                 error_report(
                     "gic-version=3 is not supported with kernel-irqchip=off");
                 exit(1);
+            case VIRT_GIC_VERSION_4:
+                error_report(
+                    "gic-version=4 is not supported with kernel-irqchip=off");
+                exit(1);
             }
         }
 
@@ -1845,6 +1852,9 @@ static void finalize_gic_version(VirtMachineState *vms)
         case VIRT_GIC_VERSION_2:
         case VIRT_GIC_VERSION_3:
             break;
+        case VIRT_GIC_VERSION_4:
+            error_report("gic-version=4 is not supported with KVM");
+            exit(1);
         }
 
         /* Check chosen version is effectively supported by the host */
@@ -1868,7 +1878,12 @@ static void finalize_gic_version(VirtMachineState *vms)
     case VIRT_GIC_VERSION_MAX:
         if (module_object_class_by_name("arm-gicv3")) {
             /* CONFIG_ARM_GICV3_TCG was set */
-            vms->gic_version = VIRT_GIC_VERSION_3;
+            if (vms->virt) {
+                /* GICv4 only makes sense if CPU has EL2 */
+                vms->gic_version = VIRT_GIC_VERSION_4;
+            } else {
+                vms->gic_version = VIRT_GIC_VERSION_3;
+            }
         } else {
             vms->gic_version = VIRT_GIC_VERSION_2;
         }
@@ -1876,6 +1891,12 @@ static void finalize_gic_version(VirtMachineState *vms)
     case VIRT_GIC_VERSION_HOST:
         error_report("gic-version=host requires KVM");
         exit(1);
+    case VIRT_GIC_VERSION_4:
+        if (!vms->virt) {
+            error_report("gic-version=4 requires virtualization enabled");
+            exit(1);
+        }
+        break;
     case VIRT_GIC_VERSION_2:
     case VIRT_GIC_VERSION_3:
         break;
@@ -2043,14 +2064,16 @@ static void machvirt_init(MachineState *machine)
         vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC;
     }
 
-    /* The maximum number of CPUs depends on the GIC version, or on how
-     * many redistributors we can fit into the memory map.
+    /*
+     * The maximum number of CPUs depends on the GIC version, or on how
+     * many redistributors we can fit into the memory map (which in turn
+     * depends on whether this is a GICv3 or v4).
      */
-    if (vms->gic_version == VIRT_GIC_VERSION_3) {
+    if (vms->gic_version == VIRT_GIC_VERSION_2) {
+        virt_max_cpus = GIC_NCPU;
+    } else {
         virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST) +
             virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2);
-    } else {
-        virt_max_cpus = GIC_NCPU;
     }
 
     if (max_cpus > virt_max_cpus) {
@@ -2431,8 +2454,19 @@ static void virt_set_mte(Object *obj, bool value, Error **errp)
 static char *virt_get_gic_version(Object *obj, Error **errp)
 {
     VirtMachineState *vms = VIRT_MACHINE(obj);
-    const char *val = vms->gic_version == VIRT_GIC_VERSION_3 ? "3" : "2";
+    const char *val;
 
+    switch (vms->gic_version) {
+    case VIRT_GIC_VERSION_4:
+        val = "4";
+        break;
+    case VIRT_GIC_VERSION_3:
+        val = "3";
+        break;
+    default:
+        val = "2";
+        break;
+    }
     return g_strdup(val);
 }
 
@@ -2440,7 +2474,9 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
 {
     VirtMachineState *vms = VIRT_MACHINE(obj);
 
-    if (!strcmp(value, "3")) {
+    if (!strcmp(value, "4")) {
+        vms->gic_version = VIRT_GIC_VERSION_4;
+    } else if (!strcmp(value, "3")) {
         vms->gic_version = VIRT_GIC_VERSION_3;
     } else if (!strcmp(value, "2")) {
         vms->gic_version = VIRT_GIC_VERSION_2;
@@ -2898,7 +2934,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
                                   virt_set_gic_version);
     object_class_property_set_description(oc, "gic-version",
                                           "Set GIC version. "
-                                          "Valid values are 2, 3, host and max");
+                                          "Valid values are 2, 3, 4, host and max");
 
     object_class_property_add_str(oc, "iommu", virt_get_iommu, virt_set_iommu);
     object_class_property_set_description(oc, "iommu",
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 88+ messages in thread

* Re: [PATCH 00/41] arm: Implement GICv4
  2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
                   ` (40 preceding siblings ...)
  2022-04-08 14:15 ` [PATCH 41/41] hw/arm/virt: Support TCG GICv4 Peter Maydell
@ 2022-04-08 14:29 ` Peter Maydell
  41 siblings, 0 replies; 88+ messages in thread
From: Peter Maydell @ 2022-04-08 14:29 UTC (permalink / raw)
  To: qemu-arm, qemu-devel; +Cc: Marc Zyngier

On Fri, 8 Apr 2022 at 15:15, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> This patchset implements emulation of GICv4 in our TCG GIC and ITS
> models, and makes the virt board use it where appropriate.

> Tested with a Linux kernel passing through a virtio-blk device
> to an inner Linux VM with KVM/QEMU. (NB that to get the outer
> Linux kernel to actually use the new GICv4 functionality you
> need to pass it "kvm-arm.vgic_v4_enable=1", as the kernel
> will not use it by default.)

I guess I might as well post my notes here about how I set up
that test environment. These are a bit too scrappy (and rather
specific about a niche thing) to be proper documentation, but
having them in the list archives might be helpful in future...

===nested-setup.txt===
How to set up an environment to test QEMU's emulation of virtualization,
with PCI passthrough of a virtio-blk-pci device to the L2 guest

(1) Set up a Debian aarch64 guest (the instructions in the old
blog post
https://translatedcode.wordpress.com/2017/07/24/installing-debian-on-qemus-64-bit-arm-virt-board/
still work; I used Debian bullseye for my testing).

(2) Copy the hda.qcow2 to hda-for-inner.qcow2; run the L1 guest
using the 'runme' script.

Caution: the virtio devices need to be in this order (hda.qcow2,
network,hda-for-inner.qcow2), because systemd in the guest names
the ethernet interface based on which PCI slot it goes in.

(3) In the L1 guest, first we need to fix up the hda-for-inner.qcow2
so that it has different UUIDs and partition UUIDs from hda.qcow2.
You'll need to make sure you have the blkid, gdisk, tune2fs, swaplabel
utilities installed in the guest.

 swapoff -a   # L1 guest might have swapped onto /dev/vdb2 by accident
 # print current partition IDs; you'll see that vda and vdb currently
 # share IDs for their partitions, and we must change those for vdb
 blkid
 # first change the PARTUUIDs with gdisk; this is the answer from
 # https://askubuntu.com/questions/1250224/how-to-change-partuuid
 gdisk /dev/vdb
 x   # change to experts menu
 c   # change partition ID
 1   # for partition 1
 R   # pick a random ID
 c   # ditto for partitions 2, 3
 2
 R
 c
 3
 R
 m   # back to main menu
 w   # write partition table
 q   # quit
 # change UUIDs; from
https://unix.stackexchange.com/questions/12858/how-to-change-filesystem-uuid-2-same-uuid
 tune2fs -U random /dev/vdb1
 tune2fs -U random /dev/vdb2
 swaplabel -U $(uuidgen) /dev/vdb3
 # Check the UUIDs and PARTUUIDs are now all changed:
 blkid
 # Now update the fstab in the L2 filesystem:
 mount /dev/vdb2 /mnt
 # Finally, edit /mnt/etc/fstab to set the UUID values for /, /boot and swap to
 # the new ones for /dev/vdb's partitions
 vi /mnt/etc/fstab # or editor of your choice
 umount /mnt
 # shutdown the L1 guest now, to ensure that all the changes to that
 # qcow2 file are committed
 shutdown -h now

(4) Copy necessary files into the L1 guest's filesystem;
you can run the L1 guest and run scp there to copy from your host machine,
or any other method you like. You'll need:
 - the vmlinuz (same one being used for L1)
 - the initrd
 - some scripts [runme-inner, runme-inner-nopassthru, reassign-vdb]
 - a copy of hda-for-inner.qcow2 (probably best to copy it to a temporary
   file while the L1 guest is not running, then copy that into the guest)
 - the qemu-system-aarch64 you want to use as the L2 QEMU
   (I cross-compiled this on my x86-64 host. The packaged Debian bullseye
   qemu-system-aarch64 will also work if you don't need to use a custom
   QEMU for L2.)

(5) Now you can run the L2 guest without using PCI passthrough like this:
 ./runme-inner-nopassthru ./qemu-system-aarch64

(6) And you can run the L2 guest with PCI passthrough like this:
 # you only need to run reassign-vdb once for any given run of the
 # L1 guest, to give the PCI device to vfio-pci rather than to the
 # L1 virtio driver. After that you can run the L2 QEMU multiple times.
 ./reassign-vdb
 ./runme-inner ./qemu-system-aarch64

Notes:

I have set up the various 'runme' scripts so that L1 has a mux of
stdio and the monitor, which means that you can kill it with ^A-x,
and ^C will be delivered to the L1 guest. The L2 guest has plain
'-serial stdio', which means that ^C will kill the L2 guest.

The 'runme' scripts expect their first argument to be the path to
the QEMU you want to run; any further arguments are extra arguments
to that QEMU. So you can do things like:

   # pass more arguments to QEMU, here disabling the ITS
   ./runme ~/qemu-system-aarch64 -machine its=off
   # run gdb, and run QEMU under gdb
   ./runme gdb --args ~/qemu-system-aarch64 -machine its=off

The 'runme' scripts should be in the same directory as the
kernel etc files they go with; but you don't need to be
in that directory to run them.
===endit===

===runme===
#!/bin/sh -e
TESTDIR="$(cd "$(dirname "$0")"; pwd)"
QEMU="$@"

# Run with GICv3 and the disk image with a nested copy in it
# (for testing EL2/GICv3-virt emulation)

: ${KERNEL:=$TESTDIR/vmlinuz-5.10.0-9-arm64}
: ${INITRD:=$TESTDIR/initrd.img-5.10.0-9-arm64}
: ${DISK:=$TESTDIR/hda.qcow2}
: ${INNERDISK:=$TESTDIR/hda-for-inner.qcow2}

# Note that the virtio-net-pci must be the 2nd PCI device,
# because otherwise the network interface name it gets will
# not match /etc/network/interfaces.

# set up with -serial mon:stdio so we can ^C the inner QEMU

IOMMU_ADDON=',iommu_platform=on,disable-modern=off,disable-legacy=on'

${QEMU} \
  -cpu cortex-a57 \
  -machine type=virt \
  -machine gic-version=max \
  -machine virtualization=true \
  -machine iommu=smmuv3 \
  -m 1024M \
  -kernel "${KERNEL}" -initrd "${INITRD}" \
  -drive if=none,id=mydrive,file="${DISK}",format=qcow2 \
  -device virtio-blk-pci,drive=mydrive \
  -netdev user,id=mynet \
  -device virtio-net-pci,netdev=mynet \
  -drive if=none,id=innerdrive,file="${INNERDISK}",format=qcow2 \
  -device virtio-blk-pci,drive=innerdrive"$IOMMU_ADDON" \
  -append 'console=ttyAMA0,38400 keep_bootcon root=/dev/vda2
kvm-arm.vgic_v4_enable=1' \
  -chardev socket,id=monitor,host=127.0.0.1,port=4444,server=on,wait=off,telnet=on
\
  -mon chardev=monitor,mode=readline \
  -display none -serial mon:stdio
===endit===

===reassign-vdb===
#!/bin/sh -e
# Script to detach the /dev/vdb PCI device from the virtio-blk driver
# and hand it to vfio-pci

PCIDEV=0000:00:03.0

echo -n "$PCIDEV" > /sys/bus/pci/drivers/virtio-pci/unbind
modprobe vfio-pci

echo vfio-pci > /sys/bus/pci/devices/"$PCIDEV"/driver_override

echo -n "$PCIDEV" > /sys/bus/pci/drivers/vfio-pci/bind
===endit===

===runme-inner===
#!/bin/sh -e
TESTDIR="$(cd "$(dirname "$0")"; pwd)"
QEMU="$@"

# run the inner guest, passing it the passthrough PCI device
: ${KERNEL:=$TESTDIR/vmlinuz-5.10.0-9-arm64}
: ${INITRD:=$TESTDIR/initrd.img-5.10.0-9-arm64}

# set up with -serial stdio so we can ^C the inner QEMU
# use -net none to work around the default virtio-net-pci
# network device wanting to load efi-virtio.rom, which the
# L1 guest's debian package puts somewhere other than where
# our locally compiled qemu-system-aarch64 wants to find it.

${QEMU} \
  -cpu cortex-a57 \
  -enable-kvm \
  -machine type=virt \
  -machine gic-version=3 \
  -m 256M \
  -kernel "${KERNEL}" -initrd "${INITRD}" \
  -append 'console=ttyAMA0,38400 keep_bootcon root=/dev/vda2' \
  -display none -serial stdio \
  -device vfio-pci,host=0000:00:03.0,id=pci0 \
  -net none
===endit===

===runme-inner-nopassthru===
#!/bin/sh -e
TESTDIR="$(cd "$(dirname "$0")"; pwd)"
QEMU="$@"

# run the inner guest, passing it a disk image
: ${KERNEL:=$TESTDIR/vmlinuz-5.10.0-9-arm64}
: ${INITRD:=$TESTDIR/initrd.img-5.10.0-9-arm64}
: ${DISK:=$TESTDIR/hda-for-inner.qcow2}

# set up with -serial stdio so we can ^C the inner QEMU
# use -net none to work around the default virtio-net-pci
# network device wanting to load efi-virtio.rom, which the
# L1 guest's debian package puts somewhere other than where
# our locally compiled qemu-system-aarch64 wants to find it.

${QEMU} \
  -cpu cortex-a57 \
  -enable-kvm \
  -machine type=virt \
  -machine gic-version=3 \
  -m 256M \
  -kernel "${KERNEL}" -initrd "${INITRD}" \
  -drive if=none,id=mydrive,file="${DISK}",format=qcow2 \
  -device virtio-blk-pci,drive=mydrive \
  -append 'console=ttyAMA0,38400 keep_bootcon root=/dev/vda2' \
  -display none -serial stdio \
  -net none
===endit===

-- PMM


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 01/41] hw/intc/arm_gicv3_its: Add missing blank line
  2022-04-08 14:15 ` [PATCH 01/41] hw/intc/arm_gicv3_its: Add missing blank line Peter Maydell
@ 2022-04-08 23:16   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-08 23:16 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> In commit b6f96009acc we split do_process_its_cmd() from
> process_its_cmd(), but forgot the usual blank line between function
> definitions.  Add it.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_its.c | 1 +
>   1 file changed, 1 insertion(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 02/41] hw/intc/arm_gicv3: Sanity-check num-cpu property
  2022-04-08 14:15 ` [PATCH 02/41] hw/intc/arm_gicv3: Sanity-check num-cpu property Peter Maydell
@ 2022-04-08 23:17   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-08 23:17 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> In the GICv3 code we implicitly rely on there being at least one CPU
> and thus at least one redistributor and CPU interface.  Sanity-check
> that the property the board code sets is not zero.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> Doing this would be a board code error, but we might as well
> get a clean diagnostic for it and not have to think about
> num_cpu == 0 as a special case later.
> ---
>   hw/intc/arm_gicv3_common.c | 4 ++++
>   1 file changed, 4 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 03/41] hw/intc/arm_gicv3: Insist that redist region capacity matches CPU count
  2022-04-08 14:15 ` [PATCH 03/41] hw/intc/arm_gicv3: Insist that redist region capacity matches CPU count Peter Maydell
@ 2022-04-08 23:20   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-08 23:20 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Boards using the GICv3 need to configure it with both the total
> number of CPUs and also the sizes of all the memory regions which
> contain redistributors (one redistributor per CPU).  At the moment
> the GICv3 checks that the number of CPUs specified is not too many to
> fit in the defined redistributor regions, but in fact the code
> assumes that the two match exactly.  For instance when we set the
> GICR_TYPER.Last bit on the final redistributor in each region, we
> assume that we don't need to consider the possibility of a region
> being only half full of redistributors or even completely empty.  We
> also assume in gicv3_redist_read() and gicv3_redist_write() that we
> can calculate the CPU index from the offset within the MemoryRegion
> and that this will always be in range.
> 
> Fortunately all the board code sets the redistributor region sizes to
> exactly match the CPU count, so this isn't a visible bug.  We could
> in theory make the GIC code handle non-full redistributor regions, or
> have it automatically reduce the provided region sizes to match the
> CPU count, but the simplest thing is just to strengthen the error
> check and insist that the CPU count and redistributor region size
> settings match exactly, since all the board code does that anyway.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_common.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 04/41] hw/intc/arm_gicv3: Report correct PIDR0 values for ID registers
  2022-04-08 14:15 ` [PATCH 04/41] hw/intc/arm_gicv3: Report correct PIDR0 values for ID registers Peter Maydell
@ 2022-04-08 23:26   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-08 23:26 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> We use the common function gicv3_idreg() to supply the CoreSight ID
> register values for the GICv3 for the copies of these ID registers in
> the distributor, redistributor and ITS register frames.  This isn't
> quite correct, because while most of the register values are the
> same, the PIDR0 value should vary to indicate which of these three
> frames it is.  (You can see this and also the correct values of these
> PIDR0 registers by looking at the GIC-600 or GIC-700 TRMs, for
> example.)
> 
> Make gicv3_idreg() take an extra argument for the PIDR0 value.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h   | 15 +++++++++++++--
>   hw/intc/arm_gicv3_dist.c   |  2 +-
>   hw/intc/arm_gicv3_its.c    |  2 +-
>   hw/intc/arm_gicv3_redist.c |  2 +-
>   4 files changed, 16 insertions(+), 5 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 05/41] target/arm/cpu.c: ignore VIRQ and VFIQ if no EL2
  2022-04-08 14:15 ` [PATCH 05/41] target/arm/cpu.c: ignore VIRQ and VFIQ if no EL2 Peter Maydell
@ 2022-04-08 23:39   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-08 23:39 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> In a GICv3, it is impossible for the GIC to deliver a VIRQ or VFIQ to
> the CPU unless the CPU has EL2, because VIRQ and VFIQ are only
> configurable via EL2-only system registers.  Moreover, in our
> implementation we were only calculating and updating the state of the
> VIRQ and VFIQ lines in gicv3_cpuif_virt_irq_fiq_update() when those
> EL2 system registers changed.  We were therefore able to assert in
> arm_cpu_set_irq() that we didn't see a VIRQ or VFIQ line update if
> EL2 wasn't present.
> 
> This assumption no longer holds with GICv4:
>   * even if the CPU does not have EL2 the guest is able to cause the
>     GIC to deliver a virtual LPI by programming the ITS (which is a
>     silly thing for it to do, but possible)
>   * because we now need to recalculate the state of the VIRQ and VFIQ
>     lines in more cases than just "some EL2 GIC sysreg was written",
>     we will see calls to arm_cpu_set_irq() for "VIRQ is 0, VFIQ is 0"
>     even if the guest is not using the virtual LPI parts of the ITS
> 
> Remove the assertions, and instead simply ignore the state of the
> VIRQ and VFIQ lines if the CPU does not have EL2.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   target/arm/cpu.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 06/41] hw/intc/arm_gicv3_its: Factor out "is intid a valid LPI ID?"
  2022-04-08 14:15 ` [PATCH 06/41] hw/intc/arm_gicv3_its: Factor out "is intid a valid LPI ID?" Peter Maydell
@ 2022-04-08 23:45   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-08 23:45 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> In process_mapti() we check interrupt IDs to see whether they are
> in the valid LPI range. Factor this out into its own utility
> function, as we're going to want it elsewhere too for GICv4.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_its.c | 10 +++++++---
>   1 file changed, 7 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> index f8467b61ec5..400cdf83794 100644
> --- a/hw/intc/arm_gicv3_its.c
> +++ b/hw/intc/arm_gicv3_its.c
> @@ -79,6 +79,12 @@ typedef enum ItsCmdResult {
>       CMD_CONTINUE = 1,
>   } ItsCmdResult;
>   
> +static inline bool intid_in_lpi_range(uint32_t id)
> +{
> +    return id >= GICV3_LPI_INTID_START &&
> +        id < (1ULL << (GICD_TYPER_IDBITS + 1));
> +}

Ok, I guess.  Though there's no need for ULL, and a 64-bit comparison.

> -    uint32_t num_intids;

You didn't have one here, and GICD_TYPER_IDBITS is 15.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 07/41] hw/intc/arm_gicv3_its: Implement GITS_BASER2 for GICv4
  2022-04-08 14:15 ` [PATCH 07/41] hw/intc/arm_gicv3_its: Implement GITS_BASER2 for GICv4 Peter Maydell
@ 2022-04-08 23:55   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-08 23:55 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The GICv4 defines a new in-guest-memory table for the ITS: this is
> the vPE table.  Implement the new GITS_BASER2 register which the
> guest uses to tell the ITS where the vPE table is located, including
> the decode of the register fields into the TableDesc structure which
> we do for the GITS_BASER<n> when the guest enables the ITS.
> 
> We guard provision of the new register with the its_feature_virtual()
> function, which does a check of the GITS_TYPER.Virtual bit which
> indicates presence of ITS support for virtual LPIs.  Since this bit
> is currently always zero, GICv4-specific features will not be
> accessible to the guest yet.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h               | 16 ++++++++++++++++
>   include/hw/intc/arm_gicv3_its_common.h |  1 +
>   hw/intc/arm_gicv3_its.c                | 25 +++++++++++++++++++++++++
>   3 files changed, 42 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 08/41] hw/intc/arm_gicv3_its: Implement VMAPI and VMAPTI
  2022-04-08 14:15 ` [PATCH 08/41] hw/intc/arm_gicv3_its: Implement VMAPI and VMAPTI Peter Maydell
@ 2022-04-09  0:17   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09  0:17 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> +                      "%s: Doorbell 0x%x not 1023 and not a valid LPI\n",

A little confusing for hex not decimal.

Otherwise,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 09/41] hw/intc/arm_gicv3_its: Implement VMAPP
  2022-04-08 14:15 ` [PATCH 09/41] hw/intc/arm_gicv3_its: Implement VMAPP Peter Maydell
@ 2022-04-09  0:21   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09  0:21 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the GICv4 VMAPP command, which writes an entry to the vPE
> table.
> 
> For GICv4.1 this command has extra fields in the command packet
> and additional behaviour. We define the 4.1-only fields with the
> FIELD macro, but only implement the GICv4.0 version of the command.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> GICv4.1 emulation is on my todo list, but I'm starting with
> just the 4.0 parts first.
> ---
>   hw/intc/gicv3_internal.h | 12 ++++++
>   hw/intc/arm_gicv3_its.c  | 88 ++++++++++++++++++++++++++++++++++++++++
>   hw/intc/trace-events     |  2 +
>   3 files changed, 102 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 10/41] hw/intc/arm_gicv3_its: Distinguish success and error cases of CMD_CONTINUE
  2022-04-08 14:15 ` [PATCH 10/41] hw/intc/arm_gicv3_its: Distinguish success and error cases of CMD_CONTINUE Peter Maydell
@ 2022-04-09  0:23   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09  0:23 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> In the ItsCmdResult enum, we currently distinguish only CMD_STALL
> (failure, stall processing of the command queue) and CMD_CONTINUE
> (keep processing the queue), and we use the latter both for "there
> was a parameter error, go on to the next command" and "the command
> succeeded, go on to the next command".  Sometimes we would like to
> distinguish those two cases, so add CMD_CONTINUE_OK to the enum to
> represent the success situation, and use it in the relevant places.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_its.c | 29 ++++++++++++++++-------------
>   1 file changed, 16 insertions(+), 13 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 11/41] hw/intc/arm_gicv3_its: Factor out "find ITE given devid, eventid"
  2022-04-08 14:15 ` [PATCH 11/41] hw/intc/arm_gicv3_its: Factor out "find ITE given devid, eventid" Peter Maydell
@ 2022-04-09  0:39   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09  0:39 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The operation of finding an interrupt table entry given a (DeviceID,
> EventID) pair is necessary in multiple different ITS commands.  The
> process requires first using the DeviceID as an index into the device
> table to find the DTE, and then useng the EventID as an index into
> the interrupt table specified by that DTE to find the ITE.  We also
> need to handle all the possible error cases: indexes out of range,
> table memory not readable, table entries not valid.
> 
> Factor this out into a separate lookup_ite() function which we
> can then call from the places where we were previously open-coding
> this sequence. We'll also need this for some of the new GICv4.0
> commands.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_its.c | 124 +++++++++++++++++++++-------------------
>   1 file changed, 64 insertions(+), 60 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 12/41] hw/intc/arm_gicv3_its: Factor out CTE lookup sequence
  2022-04-08 14:15 ` [PATCH 12/41] hw/intc/arm_gicv3_its: Factor out CTE lookup sequence Peter Maydell
@ 2022-04-09  0:47   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09  0:47 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Factor out the sequence of looking up a CTE from an ICID including
> the validity and error checks.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> I think process_movi() in particular is now a lot cleaner
> to read with all the error-checking factored out.
> ---
>   hw/intc/arm_gicv3_its.c | 109 ++++++++++++++--------------------------
>   1 file changed, 39 insertions(+), 70 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 13/41] hw/intc/arm_gicv3_its: Split out process_its_cmd() physical interrupt code
  2022-04-08 14:15 ` [PATCH 13/41] hw/intc/arm_gicv3_its: Split out process_its_cmd() physical interrupt code Peter Maydell
@ 2022-04-09 17:55   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 17:55 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Split the part of process_its_cmd() which is specific to physical
> interrupts into its own function.  This is the part which starts by
> taking the ICID and looking it up in the collection table.  The
> handling of virtual interrupts is significantly different (involving
> a lookup in the vPE table) so structuring the code with one
> sub-function for the physical interrupt case and one for the virtual
> interrupt case will be clearer than putting both cases in one large
> function.
> 
> The code for handling the "remove mapping from ITE" for the DISCARD
> command remains in process_its_cmd() because it is common to both
> virtual and physical interrupts.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_its.c | 51 ++++++++++++++++++++++++++---------------
>   1 file changed, 33 insertions(+), 18 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 14/41] hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd()
  2022-04-08 14:15 ` [PATCH 14/41] hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd() Peter Maydell
@ 2022-04-09 18:02   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:02 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> For GICv4, interrupt table entries read by process_its_cmd() may
> indicate virtual LPIs which are to be directly injected into a VM.
> Implement the ITS side of the code for handling this.  This is
> similar to the existing handling of physical LPIs, but instead of
> looking up a collection ID in a collection table, we look up a vPEID
> in a vPE table.  As with the physical LPIs, we leave the rest of the
> work to code in the redistributor device.
> 
> The redistributor half will be implemented in a later commit;
> for now we just provide a stub function which does nothing.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h   | 17 +++++++
>   hw/intc/arm_gicv3_its.c    | 99 +++++++++++++++++++++++++++++++++++++-
>   hw/intc/arm_gicv3_redist.c |  9 ++++
>   hw/intc/trace-events       |  2 +
>   4 files changed, 125 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 15/41] hw/intc/arm_gicv3: Keep pointers to every connected ITS
  2022-04-08 14:15 ` [PATCH 15/41] hw/intc/arm_gicv3: Keep pointers to every connected ITS Peter Maydell
@ 2022-04-09 18:06   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:06 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The GICv4 ITS VMOVP command's semantics require it to perform the
> operation on every ITS connected to the same GIC that the ITS that
> received the command is attached to.  This means that the GIC object
> needs to keep a pointer to every ITS that is connected to it
> (previously it was sufficient for the ITS to have a pointer to its
> GIC).
> 
> Add a glib ptrarray to the GICv3 object which holds pointers to every
> connected ITS, and make the ITS add itself to the array for the GIC
> it is connected to when it is realized.
> 
> Note that currently all QEMU machine types with an ITS have exactly
> one ITS in the system, so typically the length of this ptrarray will
> be 1.  Multiple ITSes are typically used to improve performance on
> real hardware, so we wouldn't need to have more than one unless we
> were modelling a real machine type that had multile ITSes.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h           | 9 +++++++++
>   include/hw/intc/arm_gicv3_common.h | 2 ++
>   hw/intc/arm_gicv3_common.c         | 2 ++
>   hw/intc/arm_gicv3_its.c            | 2 ++
>   hw/intc/arm_gicv3_its_kvm.c        | 2 ++
>   5 files changed, 17 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 16/41] hw/intc/arm_gicv3_its: Implement VMOVP
  2022-04-08 14:15 ` [PATCH 16/41] hw/intc/arm_gicv3_its: Implement VMOVP Peter Maydell
@ 2022-04-09 18:13   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:13 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the GICv4 VMOVP command, which updates an entry in the vPE
> table to change its rdbase field. This command is unique in the ITS
> command set because its effects must be propagated to all the other
> ITSes connected to the same GIC as the ITS which executes the VMOVP
> command.
> 
> The GICv4 spec allows two implementation choices for handling the
> propagation to other ITSes:
>   * If GITS_TYPER.VMOVP is 1, the guest only needs to issue the command
>     on one ITS, and the implementation handles the propagation to
>     all ITSes
>   * If GITS_TYPER.VMOVP is 0, the guest must issue the command on
>     every ITS, and arrange for the ITSes to synchronize the updates
>     with each other by setting ITSList and Sequence Number fields
>     in the command packets
> 
> We choose the GITS_TYPER.VMOVP = 1 approach, and synchronously
> execute the update on every ITS.
> 
> For GICv4.1 this command has extra fields in the command packet and
> additional behaviour.  We define the 4.1-only fields with the FIELD
> macro, but only implement the GICv4.0 version of the command.
> 
> Note that we don't update the reported GITS_TYPER value here;
> we'll do that later in a commit which updates all the reported
> feature bit and ID register values for GICv4.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h | 18 ++++++++++
>   hw/intc/arm_gicv3_its.c  | 75 ++++++++++++++++++++++++++++++++++++++++
>   hw/intc/trace-events     |  1 +
>   3 files changed, 94 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC
  2022-04-08 14:15 ` [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC Peter Maydell
@ 2022-04-09 18:17   ` Richard Henderson
  2022-04-09 18:25   ` Richard Henderson
  1 sibling, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:17 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The VSYNC command forces the ITS to synchronize all outstanding ITS
> operations for the specified vPEID, so that subsequent writse to
> GITS_TRANSLATER honour them.  The QEMU implementation is always in
> sync, so for us this is a nop, like the existing SYNC command.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h |  1 +
>   hw/intc/arm_gicv3_its.c  | 11 +++++++++++
>   hw/intc/trace-events     |  1 +
>   3 files changed, 13 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 18/41] hw/intc/arm_gicv3_its: Implement INV command properly
  2022-04-08 14:15 ` [PATCH 18/41] hw/intc/arm_gicv3_its: Implement INV command properly Peter Maydell
@ 2022-04-09 18:22   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:22 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> We were previously implementing INV (like INVALL) to just blow away
> cached highest-priority-pending-LPI information on all connected
> redistributors.  For GICv4.0, this isn't going to be sufficient,
> because the LPI we are invalidating cached information for might be
> either physical or virtual, and the required action is different for
> those two cases.  So we need to do the full process of looking up the
> ITE from the devid and eventid.  This also means we can do the error
> checks that the spec lists for this command.
> 
> Split out INV handling into a process_inv() function like our other
> command-processing functions.  For the moment, stick to handling only
> physical LPIs; we will add the vLPI parts later.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> We could also improve INVALL to only prod the one redistributor
> specified by the ICID in the command packet, but since INVALL
> is only for physical LPIs I am leaving it as it is.
> ---
>   hw/intc/gicv3_internal.h   | 12 +++++++++
>   hw/intc/arm_gicv3_its.c    | 50 +++++++++++++++++++++++++++++++++++++-
>   hw/intc/arm_gicv3_redist.c | 11 +++++++++
>   hw/intc/trace-events       |  3 ++-
>   4 files changed, 74 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 19/41] hw/intc/arm_gicv3_its: Implement INV for virtual interrupts
  2022-04-08 14:15 ` [PATCH 19/41] hw/intc/arm_gicv3_its: Implement INV for virtual interrupts Peter Maydell
@ 2022-04-09 18:23   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:23 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the ITS side of the handling of the INV command for
> virtual interrupts; as usual this calls into a redistributor
> function which we leave as a stub to fill in later.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h   |  9 +++++++++
>   hw/intc/arm_gicv3_its.c    | 16 ++++++++++++++--
>   hw/intc/arm_gicv3_redist.c |  8 ++++++++
>   3 files changed, 31 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC
  2022-04-08 14:15 ` [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC Peter Maydell
  2022-04-09 18:17   ` Richard Henderson
@ 2022-04-09 18:25   ` Richard Henderson
  1 sibling, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:25 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The VSYNC command forces the ITS to synchronize all outstanding ITS
> operations for the specified vPEID, so that subsequent writse to

writes.


r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 20/41] hw/intc/arm_gicv3_its: Implement VMOVI
  2022-04-08 14:15 ` [PATCH 20/41] hw/intc/arm_gicv3_its: Implement VMOVI Peter Maydell
@ 2022-04-09 18:27   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:27 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the GICv4 VMOVI command, which moves the pending state
> of a virtual interrupt from one redistributor to another. As with
> MOVI, we handle the "parse and validate command arguments and
> table lookups" part in the ITS source file, and pass the final
> results to a function in the redistributor which will do the
> actual operation. As with the "make a VLPI pending" change,
> for the moment we leave that redistributor function as a stub,
> to be implemented in a later commit.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h   | 23 +++++++++++
>   hw/intc/arm_gicv3_its.c    | 82 ++++++++++++++++++++++++++++++++++++++
>   hw/intc/arm_gicv3_redist.c | 10 +++++
>   hw/intc/trace-events       |  1 +
>   4 files changed, 116 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 21/41] hw/intc/arm_gicv3_its: Implement VINVALL
  2022-04-08 14:15 ` [PATCH 21/41] hw/intc/arm_gicv3_its: Implement VINVALL Peter Maydell
@ 2022-04-09 18:28   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:28 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The VINVALL command should cause any cached information in the
> ITS or redistributor for the specified vCPU to be dropped or
> otherwise made consistent with the in-memory LPI configuration
> tables.
> 
> Here we implement the command and table parsing, leaving the
> redistributor part as a stub for the moment, as usual.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h   | 13 +++++++++++++
>   hw/intc/arm_gicv3_its.c    | 26 ++++++++++++++++++++++++++
>   hw/intc/arm_gicv3_redist.c |  5 +++++
>   hw/intc/trace-events       |  1 +
>   4 files changed, 45 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 22/41] hw/intc/arm_gicv3: Implement GICv4's new redistributor frame
  2022-04-08 14:15 ` [PATCH 22/41] hw/intc/arm_gicv3: Implement GICv4's new redistributor frame Peter Maydell
@ 2022-04-09 18:32   ` Richard Henderson
  2022-04-22  8:39     ` Peter Maydell
  0 siblings, 1 reply; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:32 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index 7c75dd6f072..9f1fe09a78e 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -442,8 +442,8 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data,
>        * in the memory map); if so then the GIC has multiple MemoryRegions
>        * for the redistributors.
>        */
> -    cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
> -    offset %= GICV3_REDIST_SIZE;
> +    cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
> +    offset %= gicv3_redist_size(s);
>   
>       cs = &s->cpu[cpuidx];
>   
> @@ -501,8 +501,8 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
>        * in the memory map); if so then the GIC has multiple MemoryRegions
>        * for the redistributors.
>        */
> -    cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
> -    offset %= GICV3_REDIST_SIZE;
> +    cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
> +    offset %= gicv3_redist_size(s);

In these two cases, it would be better to call the function only once.

Otherwise,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~



^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 23/41] hw/intc/arm_gicv3: Implement new GICv4 redistributor registers
  2022-04-08 14:15 ` [PATCH 23/41] hw/intc/arm_gicv3: Implement new GICv4 redistributor registers Peter Maydell
@ 2022-04-09 18:40   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:40 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the new GICv4 redistributor registers: GICR_VPROPBASER
> and GICR_VPENDBASER; for the moment we implement these as simple
> reads-as-written stubs, together with the necessary migration
> and reset handling.
> 
> We don't put ID-register checks on the handling of these registers,
> because they are all in the only-in-v4 extra register frames, so
> they're not accessible in a GICv3.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> GICv4.1 adds two further registers in the new VLPI frame,
> as well as changing the layout of VPROPBASER and VPENDBASER,
> but we aren't implementing v4.1 yet, just v4.
> ---
>   hw/intc/gicv3_internal.h           | 21 +++++++++++
>   include/hw/intc/arm_gicv3_common.h |  3 ++
>   hw/intc/arm_gicv3_common.c         | 22 ++++++++++++
>   hw/intc/arm_gicv3_redist.c         | 56 ++++++++++++++++++++++++++++++
>   4 files changed, 102 insertions(+)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 24/41] hw/intc/arm_gicv3_cpuif: Split "update vIRQ/vFIQ" from gicv3_cpuif_virt_update()
  2022-04-08 14:15 ` [PATCH 24/41] hw/intc/arm_gicv3_cpuif: Split "update vIRQ/vFIQ" from gicv3_cpuif_virt_update() Peter Maydell
@ 2022-04-09 18:45   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 18:45 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The function gicv3_cpuif_virt_update() currently sets all of vIRQ,
> vFIQ and the maintenance interrupt.  This implies that it has to be
> used quite carefully -- as the comment notes, setting the maintenance
> interrupt will typically cause the GIC code to be re-entered
> recursively.  For handling vLPIs, we need the redistributor to be
> able to tell the cpuif to update the vIRQ and vFIQ lines when the
> highest priority pending vLPI changes.  Since that change can't cause
> the maintenance interrupt state to change, we can pull the "update
> vIRQ/vFIQ" parts of gicv3_cpuif_virt_update() out into a separate
> function, which the redistributor can then call without having to
> worry about the reentrancy issue.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h  | 11 +++++++
>   hw/intc/arm_gicv3_cpuif.c | 64 ++++++++++++++++++++++++---------------
>   hw/intc/trace-events      |  3 +-
>   3 files changed, 53 insertions(+), 25 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 25/41] hw/intc/arm_gicv3_cpuif: Support vLPIs
  2022-04-08 14:15 ` [PATCH 25/41] hw/intc/arm_gicv3_cpuif: Support vLPIs Peter Maydell
@ 2022-04-09 19:20   ` Richard Henderson
  2022-04-10  8:49     ` Peter Maydell
  0 siblings, 1 reply; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 19:20 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The CPU interface changes to support vLPIs are fairly minor:
> in the parts of the code that currently look at the list registers
> to determine the highest priority pending virtual interrupt, we
> must also look at the highest priority pending vLPI. To do this
> we change hppvi_index() to check the vLPI and return a special-case
> value if that is the right virtual interrupt to take. The callsites
> (which handle HPPIR and IAR registers and the "raise vIRQ and vFIQ
> lines" code) then have to handle this special-case value.
> 
> This commit includes two interfaces with the as-yet-unwritten
> redistributor code:
>   * the new GICv3CPUState::hppvlpi will be set by the redistributor
>     (in the same way as the existing hpplpi does for physical LPIs)
>   * when the CPU interface acknowledges a vLPI it needs to set it
>     to non-pending; the new gicv3_redist_vlpi_pending() function
>     (which matches the existing gicv3_redist_lpi_pending() used
>     for physical LPIs) is a stub that will be filled in later
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


> @@ -2632,6 +2735,12 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque)
>       GICv3CPUState *cs = opaque;
>   
>       gicv3_cpuif_update(cs);
> +    /*
> +     * Because vLPIs are only pending in NonSecure state,
> +     * an EL change can change the VIRQ/VFIQ status (but
> +     * cannot affect the maintenance interrupt state)
> +     */
> +    gicv3_cpuif_virt_irq_fiq_update(cs);

I'm a little bit surprised this is here, and not in arm_cpu_exec_interrupt (or a 
subroutine).  Is this because if a virq has highest priority, we have to find the highest 
prio physical interrupt?


r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 26/41] hw/intc/arm_gicv3_cpuif: Don't recalculate maintenance irq unnecessarily
  2022-04-08 14:15 ` [PATCH 26/41] hw/intc/arm_gicv3_cpuif: Don't recalculate maintenance irq unnecessarily Peter Maydell
@ 2022-04-09 19:23   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 19:23 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> The maintenance interrupt state depends only on:
>   * ICH_HCR_EL2
>   * ICH_LR<n>_EL2
>   * ICH_VMCR_EL2 fields VENG0 and VENG1
> 
> Now we have a separate function that updates only the vIRQ and vFIQ
> lines, use that in places that only change state that affects vIRQ
> and vFIQ but not the maintenance interrupt.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_cpuif.c | 10 +++++-----
>   1 file changed, 5 insertions(+), 5 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 27/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for one LPI" logic
  2022-04-08 14:15 ` [PATCH 27/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for one LPI" logic Peter Maydell
@ 2022-04-09 19:28   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 19:28 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Currently the functions which update the highest priority pending LPI
> information by looking at the LPI Pending and Configuration tables
> are hard-coded to use the physical LPI tables addressed by
> GICR_PENDBASER and GICR_PROPBASER.  To support virtual LPIs we will
> need to do essentially the same job, but looking at the current
> virtual LPI Pending and Configuration tables and updating cs->hppvlpi
> instead of cs->hpplpi.
> 
> Factor out the common part of the gicv3_redist_check_lpi_priority()
> function into a new update_for_one_lpi() function, which updates
> a PendingIrq struct if the specified LPI is higher priority than
> what is currently recorded there.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 74 ++++++++++++++++++++++++--------------
>   1 file changed, 47 insertions(+), 27 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 28/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for all LPIs" logic
  2022-04-08 14:15 ` [PATCH 28/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for all LPIs" logic Peter Maydell
@ 2022-04-09 19:54   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 19:54 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Factor out the common part of gicv3_redist_update_lpi_only() into
> a new function update_for_all_lpis(), which does a full rescan
> of an LPI Pending table and sets the specified PendingIrq struct
> with the highest priority pending enabled LPI it finds.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 66 ++++++++++++++++++++++++++------------
>   1 file changed, 46 insertions(+), 20 deletions(-)
> 
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index 571e0fa8309..2379389d14e 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -103,6 +103,48 @@ static void update_for_one_lpi(GICv3CPUState *cs, int irq,
>       }
>   }
>   
> +/**
> + * update_for_all_lpis: Fully scan LPI tables and find best pending LPI
> + *
> + * @cs: GICv3CPUState
> + * @ptbase: physical address of LPI Pending table
> + * @ctbase: physical address of LPI Configuration table
> + * @ptsizebits: size of tables, specified as number of interrupt ID bits minus 1
> + * @ds: true if priority value should not be shifted
> + * @hpp: points to pending information to set
> + *
> + * Recalculate the highest priority pending enabled LPI from scratch,
> + * and set @hpp accordingly.
> + *
> + * We scan the LPI pending table @ptbase; for each pending LPI, we read the
> + * corresponding entry in the LPI configuration table @ctbase to extract
> + * the priority and enabled information.
> + *
> + * We take @ptsizebits in the form idbits-1 because this is the way that
> + * LPI table sizes are architecturally specified in GICR_PROPBASER.IDBits
> + * and in the VMAPP command's VPT_size field.
> + */
> +static void update_for_all_lpis(GICv3CPUState *cs, uint64_t ptbase,
> +                                uint64_t ctbase, unsigned ptsizebits,
> +                                bool ds, PendingIrq *hpp)
> +{
> +    AddressSpace *as = &cs->gic->dma_as;
> +    uint8_t pend;
> +    uint32_t pendt_size = (1ULL << (ptsizebits + 1));

Code movement, so ok, but no need for ull.

Anyway,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 29/41] hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes
  2022-04-08 14:15 ` [PATCH 29/41] hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes Peter Maydell
@ 2022-04-09 20:10   ` Richard Henderson
  2022-04-10  8:56     ` Peter Maydell
  0 siblings, 1 reply; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:10 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> +    if (oldvalid && newvalid) {
> +        /*
> +         * Changing other fields while VALID is 1 is UNPREDICTABLE;
> +         * we choose to log and ignore the write.
> +         */
> +        if (cs->gicr_vpendbaser ^ newval) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "%s: Changing GICR_VPENDBASER when VALID=1 "
> +                          "is UNPREDICTABLE\n", __func__);
> +        }
> +        return;
> +    }

...

> @@ -493,10 +574,10 @@ static MemTxResult gicr_writel(GICv3CPUState *cs, hwaddr offset,
>           cs->gicr_vpropbaser = deposit64(cs->gicr_vpropbaser, 32, 32, value);
>           return MEMTX_OK;
>       case GICR_VPENDBASER:
> -        cs->gicr_vpendbaser = deposit64(cs->gicr_vpendbaser, 0, 32, value);
> +        gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 0, 32, value));
>           return MEMTX_OK;
>       case GICR_VPENDBASER + 4:
> -        cs->gicr_vpendbaser = deposit64(cs->gicr_vpendbaser, 32, 32, value);
> +        gicr_write_vpendbaser(cs, deposit64(cs->gicr_vpendbaser, 32, 32, value));
>           return MEMTX_OK;
>       default:
>           return MEMTX_ERROR;
> @@ -557,7 +638,7 @@ static MemTxResult gicr_writell(GICv3CPUState *cs, hwaddr offset,
>           cs->gicr_vpropbaser = value;
>           return MEMTX_OK;
>       case GICR_VPENDBASER:
> -        cs->gicr_vpendbaser = value;
> +        gicr_write_vpendbaser(cs, value);
>           return MEMTX_OK;
>       default:
>           return MEMTX_ERROR;

It it really valid to write to vpendbaser with other than a 64-bit write?  I suppose it's 
possible to order the 32-bit writes to make sure the update to valid comes last...

Anyway, not really related to the real logic here,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 30/41] hw/intc/arm_gicv3_redist: Factor out "update bit in pending table" code
  2022-04-08 14:15 ` [PATCH 30/41] hw/intc/arm_gicv3_redist: Factor out "update bit in pending table" code Peter Maydell
@ 2022-04-09 20:21   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:21 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> +    if (extract32(pend, irq % 8, 1) == level) {

Here you assume level in {0,1}...

> +    pend = deposit32(pend, irq % 8, 1, level ? 1 : 0);

... and here you force it into {0,1}.
Better to have the compiler do that with bool level.

You might consider

     uint8_t bit = 1 << (irq % 8);
     read();
     if (!(pend & bit) ^ level) {
        no change
     }
     pend ^= bit;
     write();

as a follow up; extract + deposit seems unnecessary.

Anyway, with the bool thing fixed,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 31/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_process_vlpi()
  2022-04-08 14:15 ` [PATCH 31/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_process_vlpi() Peter Maydell
@ 2022-04-09 20:28   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:28 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the function gicv3_redist_process_vlpi(), which was left as
> just a stub earlier.  This function deals with being handed a VLPI by
> the ITS.  It must set the bit in the pending table.  If the vCPU is
> currently resident we must recalculate the highest priority pending
> vLPI; otherwise we may need to ring a "doorbell" interrupt to let the
> hypervisor know it might want to reschedule the vCPU.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 48 ++++++++++++++++++++++++++++++++++----
>   1 file changed, 44 insertions(+), 4 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 32/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vlpi_pending()
  2022-04-08 14:15 ` [PATCH 32/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vlpi_pending() Peter Maydell
@ 2022-04-09 20:32   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:32 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the function gicv3_redist_vlpi_pending(), which was
> previously left as a stub.  This is the function that is called by
> the CPU interface when it changes the state of a vLPI.  It's similar
> to gicv3_redist_process_vlpi(), but we know that the vCPU is
> definitely resident on the redistributor and the irq is in range, so
> it is a bit simpler.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 23 +++++++++++++++++++++--
>   1 file changed, 21 insertions(+), 2 deletions(-)
> 
> diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> index be36978b45c..eadf5e8265e 100644
> --- a/hw/intc/arm_gicv3_redist.c
> +++ b/hw/intc/arm_gicv3_redist.c
> @@ -1009,9 +1009,28 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
>   void gicv3_redist_vlpi_pending(GICv3CPUState *cs, int irq, int level)
>   {
>       /*
> -     * The redistributor handling for changing the pending state
> -     * of a vLPI will be added in a subsequent commit.
> +     * Change the pending state of the specified vLPI.
> +     * Unlike gicv3_redist_process_vlpi(), we know here that the
> +     * vCPU is definitely resident on this redistributor, and that
> +     * the irq is in range.
>        */
> +    uint64_t vptbase, ctbase;
> +
> +    vptbase = FIELD_EX64(cs->gicr_vpendbaser, GICR_VPENDBASER, PHYADDR) << 16;
> +
> +    if (set_pending_table_bit(cs, vptbase, irq, level)) {
> +        if (level) {
> +            /* Check whether this vLPI is now the best */
> +            ctbase = cs->gicr_vpropbaser & R_GICR_VPROPBASER_PHYADDR_MASK;
> +            update_for_one_lpi(cs, irq, ctbase, true, &cs->hppvlpi);
> +            gicv3_cpuif_virt_irq_fiq_update(cs);
> +        } else {
> +            /* Only need to recalculate if this was previously the best vLPI */
> +            if (irq == cs->hppvlpi.irq) {
> +                gicv3_redist_update_vlpi(cs);
> +            }
> +        }
> +    }

I'll note that looks a lot like the previous patch, modulo "resident".
Perhaps this could be better factored?

Anyway,
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>


r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 33/41] hw/intc/arm_gicv3_redist: Use set_pending_table_bit() in mov handling
  2022-04-08 14:15 ` [PATCH 33/41] hw/intc/arm_gicv3_redist: Use set_pending_table_bit() in mov handling Peter Maydell
@ 2022-04-09 20:34   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:34 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> We can use our new set_pending_table_bit() utility function
> in gicv3_redist_mov_lpi() to clear the bit in the source
> pending table, rather than doing the "load, clear bit, store"
> ourselves.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 9 +--------
>   1 file changed, 1 insertion(+), 8 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 34/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_mov_vlpi()
  2022-04-08 14:15 ` [PATCH 34/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_mov_vlpi() Peter Maydell
@ 2022-04-09 20:39   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:39 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the gicv3_redist_mov_vlpi() function (previously left as a
> stub).  This function handles the work of a VMOVI command: it marks
> the vLPI not-pending on the source and pending on the destination.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 20 ++++++++++++++++++--
>   1 file changed, 18 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 35/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vinvall()
  2022-04-08 14:15 ` [PATCH 35/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vinvall() Peter Maydell
@ 2022-04-09 20:40   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:40 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the gicv3_redist_vinvall() function (previously left as a
> stub).  This function handles the work of a VINVALL command: it must
> invalidate any cached information associated with a specific vCPU.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 8 +++++++-
>   1 file changed, 7 insertions(+), 1 deletion(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 36/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_inv_vlpi()
  2022-04-08 14:15 ` [PATCH 36/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_inv_vlpi() Peter Maydell
@ 2022-04-09 20:40   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:40 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Implement the function gicv3_redist_inv_vlpi(), which was previously
> left as a stub.  This is the function that does the work of the INV
> command for a virtual interrupt.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_redist.c | 7 +++++--
>   1 file changed, 5 insertions(+), 2 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 37/41] hw/intc/arm_gicv3: Update ID and feature registers for GICv4
  2022-04-08 14:15 ` [PATCH 37/41] hw/intc/arm_gicv3: Update ID and feature registers for GICv4 Peter Maydell
@ 2022-04-09 20:47   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:47 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Update the various GIC ID and feature registers for GICv4:
>   * PIDR2 [7:4] is the GIC architecture revision
>   * GICD_TYPER.DVIS is 1 to indicate direct vLPI injection support
>   * GICR_TYPER.VLPIS is 1 to indicate redistributor support for vLPIs
>   * GITS_TYPER.VIRTUAL is 1 to indicate vLPI support
>   * GITS_TYPER.VMOVP is 1 to indicate that our VMOVP implementation
>     handles cross-ITS synchronization for the guest
>   * ICH_VTR_EL2.nV4 is 0 to indicate direct vLPI injection support
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/gicv3_internal.h   | 15 +++++++++++----
>   hw/intc/arm_gicv3_common.c |  7 +++++--
>   hw/intc/arm_gicv3_cpuif.c  |  6 +++++-
>   hw/intc/arm_gicv3_dist.c   |  7 ++++---
>   hw/intc/arm_gicv3_its.c    |  7 ++++++-
>   hw/intc/arm_gicv3_redist.c |  2 +-
>   6 files changed, 32 insertions(+), 12 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 38/41] hw/intc/arm_gicv3: Allow 'revision' property to be set to 4
  2022-04-08 14:15 ` [PATCH 38/41] hw/intc/arm_gicv3: Allow 'revision' property to be set to 4 Peter Maydell
@ 2022-04-09 20:50   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:50 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Now that we have implemented all the GICv4 requirements, relax the
> error-checking on the GIC object's 'revision' property to allow a TCG
> GIC to be a GICv4, whilst still constraining the KVM GIC to GICv3.
> 
> Our 'revision' property doesn't consider the possibility of wanting
> to specify the minor version of the GIC -- for instance there is a
> GICv3.1 which adds support for extended SPI and PPI ranges, among
> other things, and also GICv4.1.  But since the QOM property is
> internal to QEMU, not user-facing, we can cross that bridge when we
> come to it. Within the GIC implementation itself code generally
> checks against the appropriate ID register feature bits, and the
> only use of s->revision is for setting those ID register bits.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/intc/arm_gicv3_common.c | 12 +++++++-----
>   hw/intc/arm_gicv3_kvm.c    |  5 +++++
>   2 files changed, 12 insertions(+), 5 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 39/41] hw/arm/virt: Use VIRT_GIC_VERSION_* enum values in create_gic()
  2022-04-08 14:15 ` [PATCH 39/41] hw/arm/virt: Use VIRT_GIC_VERSION_* enum values in create_gic() Peter Maydell
@ 2022-04-09 20:52   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:52 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Everywhere we need to check which GIC version we're using, we look at
> vms->gic_version and use the VIRT_GIC_VERSION_* enum values, except
> in create_gic(), which copies vms->gic_version into a local 'int'
> variable and makes direct comparisons against values 2 and 3.
> 
> For consistency, change this function to check the GIC version
> the same way we do elsewhere. This includes not implicitly relying
> on the enumeration type values happening to match the integer
> 'revision' values the GIC device object wants.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   hw/arm/virt.c | 31 +++++++++++++++++++++++--------
>   1 file changed, 23 insertions(+), 8 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 40/41] hw/arm/virt: Abstract out calculation of redistributor region capacity
  2022-04-08 14:15 ` [PATCH 40/41] hw/arm/virt: Abstract out calculation of redistributor region capacity Peter Maydell
@ 2022-04-09 20:53   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:53 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> In several places in virt.c we calculate the number of redistributors that
> fit in a region of our memory map, which is the size of the region
> divided by the size of a single redistributor frame. For GICv4, the
> redistributor frame is a different size from that for GICv3. Abstract
> out the calculation of redistributor region capacity so that we have
> one place we need to change to handle GICv4 rather than several.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   include/hw/arm/virt.h |  9 +++++++--
>   hw/arm/virt.c         | 11 ++++-------
>   2 files changed, 11 insertions(+), 9 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 41/41] hw/arm/virt: Support TCG GICv4
  2022-04-08 14:15 ` [PATCH 41/41] hw/arm/virt: Support TCG GICv4 Peter Maydell
@ 2022-04-09 20:55   ` Richard Henderson
  0 siblings, 0 replies; 88+ messages in thread
From: Richard Henderson @ 2022-04-09 20:55 UTC (permalink / raw)
  To: Peter Maydell, qemu-arm, qemu-devel; +Cc: Marc Zyngier

On 4/8/22 07:15, Peter Maydell wrote:
> Add support for the TCG GICv4 to the virt board. For the board,
> the GICv4 is very similar to the GICv3, with the only difference
> being the size of the redistributor frame. The changes here are thus:
>   * calculating virt_redist_capacity correctly for GICv4
>   * changing various places which were "if GICv3" to be "if not GICv2"
>   * the commandline option handling
> 
> Note that using GICv4 reduces the maximum possible number of CPUs on
> the virt board from 512 to 317, because we can now only fit half as
> many redistributors into the redistributor regions we have defined.
> 
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
>   docs/system/arm/virt.rst |  5 ++-
>   include/hw/arm/virt.h    | 12 +++++--
>   hw/arm/virt.c            | 70 ++++++++++++++++++++++++++++++----------
>   3 files changed, 67 insertions(+), 20 deletions(-)

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>

r~


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 25/41] hw/intc/arm_gicv3_cpuif: Support vLPIs
  2022-04-09 19:20   ` Richard Henderson
@ 2022-04-10  8:49     ` Peter Maydell
  0 siblings, 0 replies; 88+ messages in thread
From: Peter Maydell @ 2022-04-10  8:49 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Marc Zyngier, qemu-arm, qemu-devel

On Sat, 9 Apr 2022 at 20:20, Richard Henderson
<richard.henderson@linaro.org> wrote:
> On 4/8/22 07:15, Peter Maydell wrote:
> > @@ -2632,6 +2735,12 @@ static void gicv3_cpuif_el_change_hook(ARMCPU *cpu, void *opaque)
> >       GICv3CPUState *cs = opaque;
> >
> >       gicv3_cpuif_update(cs);
> > +    /*
> > +     * Because vLPIs are only pending in NonSecure state,
> > +     * an EL change can change the VIRQ/VFIQ status (but
> > +     * cannot affect the maintenance interrupt state)
> > +     */
> > +    gicv3_cpuif_virt_irq_fiq_update(cs);
>
> I'm a little bit surprised this is here, and not in arm_cpu_exec_interrupt (or a
> subroutine).  Is this because if a virq has highest priority, we have to find the highest
> prio physical interrupt?

It's because if we switch from Secure to NonSecure that changes
whether there's a highest-priority-virtual-LPI available (there
never is for NS). This is a change from before vLPI support:
when there are only list registers the calculation of the highest
priority virtual interrupt doesn't care about S vs NS and thus
not about the current EL. (In other words, what
gicv3_cpuif_virt_irq_fiq_update() reports on the VIRQ/VFIQ
lines now depends on S vs NS, so needs to be recalculated.)

thanks
-- PMM


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 29/41] hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes
  2022-04-09 20:10   ` Richard Henderson
@ 2022-04-10  8:56     ` Peter Maydell
  0 siblings, 0 replies; 88+ messages in thread
From: Peter Maydell @ 2022-04-10  8:56 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Marc Zyngier, qemu-arm, qemu-devel

On Sat, 9 Apr 2022 at 21:10, Richard Henderson
<richard.henderson@linaro.org> wrote:
> It it really valid to write to vpendbaser with other than a 64-bit write?  I suppose it's
> possible to order the 32-bit writes to make sure the update to valid comes last...

Yes, that's valid. The GICv3 spec states specifically when registers are
64-bit only (see for instance GITS_SGIR). As you say, you can write the
bottom half first and the top half with the valid bit second.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 88+ messages in thread

* Re: [PATCH 22/41] hw/intc/arm_gicv3: Implement GICv4's new redistributor frame
  2022-04-09 18:32   ` Richard Henderson
@ 2022-04-22  8:39     ` Peter Maydell
  0 siblings, 0 replies; 88+ messages in thread
From: Peter Maydell @ 2022-04-22  8:39 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Marc Zyngier, qemu-arm, qemu-devel

On Sat, 9 Apr 2022 at 19:32, Richard Henderson
<richard.henderson@linaro.org> wrote:
>
> On 4/8/22 07:15, Peter Maydell wrote:
> > diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
> > index 7c75dd6f072..9f1fe09a78e 100644
> > --- a/hw/intc/arm_gicv3_redist.c
> > +++ b/hw/intc/arm_gicv3_redist.c
> > @@ -442,8 +442,8 @@ MemTxResult gicv3_redist_read(void *opaque, hwaddr offset, uint64_t *data,
> >        * in the memory map); if so then the GIC has multiple MemoryRegions
> >        * for the redistributors.
> >        */
> > -    cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
> > -    offset %= GICV3_REDIST_SIZE;
> > +    cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
> > +    offset %= gicv3_redist_size(s);
> >
> >       cs = &s->cpu[cpuidx];
> >
> > @@ -501,8 +501,8 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
> >        * in the memory map); if so then the GIC has multiple MemoryRegions
> >        * for the redistributors.
> >        */
> > -    cpuidx = region->cpuidx + offset / GICV3_REDIST_SIZE;
> > -    offset %= GICV3_REDIST_SIZE;
> > +    cpuidx = region->cpuidx + offset / gicv3_redist_size(s);
> > +    offset %= gicv3_redist_size(s);
>
> In these two cases, it would be better to call the function only once.

The compiler is smart enough to only actually call the function once
(and thus to do the / and % with a single div and imul), so I don't
think that explicitly adding a local variable and doing the call-once
by hand is really worthwhile.

thanks
-- PMM


^ permalink raw reply	[flat|nested] 88+ messages in thread

end of thread, other threads:[~2022-04-22  9:12 UTC | newest]

Thread overview: 88+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-08 14:15 [PATCH 00/41] arm: Implement GICv4 Peter Maydell
2022-04-08 14:15 ` [PATCH 01/41] hw/intc/arm_gicv3_its: Add missing blank line Peter Maydell
2022-04-08 23:16   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 02/41] hw/intc/arm_gicv3: Sanity-check num-cpu property Peter Maydell
2022-04-08 23:17   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 03/41] hw/intc/arm_gicv3: Insist that redist region capacity matches CPU count Peter Maydell
2022-04-08 23:20   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 04/41] hw/intc/arm_gicv3: Report correct PIDR0 values for ID registers Peter Maydell
2022-04-08 23:26   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 05/41] target/arm/cpu.c: ignore VIRQ and VFIQ if no EL2 Peter Maydell
2022-04-08 23:39   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 06/41] hw/intc/arm_gicv3_its: Factor out "is intid a valid LPI ID?" Peter Maydell
2022-04-08 23:45   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 07/41] hw/intc/arm_gicv3_its: Implement GITS_BASER2 for GICv4 Peter Maydell
2022-04-08 23:55   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 08/41] hw/intc/arm_gicv3_its: Implement VMAPI and VMAPTI Peter Maydell
2022-04-09  0:17   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 09/41] hw/intc/arm_gicv3_its: Implement VMAPP Peter Maydell
2022-04-09  0:21   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 10/41] hw/intc/arm_gicv3_its: Distinguish success and error cases of CMD_CONTINUE Peter Maydell
2022-04-09  0:23   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 11/41] hw/intc/arm_gicv3_its: Factor out "find ITE given devid, eventid" Peter Maydell
2022-04-09  0:39   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 12/41] hw/intc/arm_gicv3_its: Factor out CTE lookup sequence Peter Maydell
2022-04-09  0:47   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 13/41] hw/intc/arm_gicv3_its: Split out process_its_cmd() physical interrupt code Peter Maydell
2022-04-09 17:55   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 14/41] hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd() Peter Maydell
2022-04-09 18:02   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 15/41] hw/intc/arm_gicv3: Keep pointers to every connected ITS Peter Maydell
2022-04-09 18:06   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 16/41] hw/intc/arm_gicv3_its: Implement VMOVP Peter Maydell
2022-04-09 18:13   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 17/41] hw/intc/arm_gicv3_its: Implement VSYNC Peter Maydell
2022-04-09 18:17   ` Richard Henderson
2022-04-09 18:25   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 18/41] hw/intc/arm_gicv3_its: Implement INV command properly Peter Maydell
2022-04-09 18:22   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 19/41] hw/intc/arm_gicv3_its: Implement INV for virtual interrupts Peter Maydell
2022-04-09 18:23   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 20/41] hw/intc/arm_gicv3_its: Implement VMOVI Peter Maydell
2022-04-09 18:27   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 21/41] hw/intc/arm_gicv3_its: Implement VINVALL Peter Maydell
2022-04-09 18:28   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 22/41] hw/intc/arm_gicv3: Implement GICv4's new redistributor frame Peter Maydell
2022-04-09 18:32   ` Richard Henderson
2022-04-22  8:39     ` Peter Maydell
2022-04-08 14:15 ` [PATCH 23/41] hw/intc/arm_gicv3: Implement new GICv4 redistributor registers Peter Maydell
2022-04-09 18:40   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 24/41] hw/intc/arm_gicv3_cpuif: Split "update vIRQ/vFIQ" from gicv3_cpuif_virt_update() Peter Maydell
2022-04-09 18:45   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 25/41] hw/intc/arm_gicv3_cpuif: Support vLPIs Peter Maydell
2022-04-09 19:20   ` Richard Henderson
2022-04-10  8:49     ` Peter Maydell
2022-04-08 14:15 ` [PATCH 26/41] hw/intc/arm_gicv3_cpuif: Don't recalculate maintenance irq unnecessarily Peter Maydell
2022-04-09 19:23   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 27/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for one LPI" logic Peter Maydell
2022-04-09 19:28   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 28/41] hw/intc/arm_gicv3_redist: Factor out "update hpplpi for all LPIs" logic Peter Maydell
2022-04-09 19:54   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 29/41] hw/intc/arm_gicv3_redist: Recalculate hppvlpi on VPENDBASER writes Peter Maydell
2022-04-09 20:10   ` Richard Henderson
2022-04-10  8:56     ` Peter Maydell
2022-04-08 14:15 ` [PATCH 30/41] hw/intc/arm_gicv3_redist: Factor out "update bit in pending table" code Peter Maydell
2022-04-09 20:21   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 31/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_process_vlpi() Peter Maydell
2022-04-09 20:28   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 32/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vlpi_pending() Peter Maydell
2022-04-09 20:32   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 33/41] hw/intc/arm_gicv3_redist: Use set_pending_table_bit() in mov handling Peter Maydell
2022-04-09 20:34   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 34/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_mov_vlpi() Peter Maydell
2022-04-09 20:39   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 35/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_vinvall() Peter Maydell
2022-04-09 20:40   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 36/41] hw/intc/arm_gicv3_redist: Implement gicv3_redist_inv_vlpi() Peter Maydell
2022-04-09 20:40   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 37/41] hw/intc/arm_gicv3: Update ID and feature registers for GICv4 Peter Maydell
2022-04-09 20:47   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 38/41] hw/intc/arm_gicv3: Allow 'revision' property to be set to 4 Peter Maydell
2022-04-09 20:50   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 39/41] hw/arm/virt: Use VIRT_GIC_VERSION_* enum values in create_gic() Peter Maydell
2022-04-09 20:52   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 40/41] hw/arm/virt: Abstract out calculation of redistributor region capacity Peter Maydell
2022-04-09 20:53   ` Richard Henderson
2022-04-08 14:15 ` [PATCH 41/41] hw/arm/virt: Support TCG GICv4 Peter Maydell
2022-04-09 20:55   ` Richard Henderson
2022-04-08 14:29 ` [PATCH 00/41] arm: Implement GICv4 Peter Maydell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.