* [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups
@ 2026-09-14 16:00 Peter Maydell
2026-09-14 16:00 ` [PATCH v2 1/9] hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR Peter Maydell
` (9 more replies)
0 siblings, 10 replies; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
This patchset is a compilation of minor fixes and cleanups to the
GICv5 code that I found in the course of implementing virtualization
support:
* Various in-memory data structures are defined to be aligned to
their size, even if the guest provides a base address that is less
aligned
* We had some extra blank lines that didn't match the style in the
rest of the file
* We were unnecessarily extracting a bit from a raw register value
when we already had it cached as a boolean in a struct
* We weren't tidying up our allocated memory on finalize
* We can re-use arm_cpu_set_irq() rather than implementing the
set/clear IRQ/FIQ/NMI locally. (This doesn't matter much for these,
but VIRQ/VFIQ/VINMI have more complex logic in arm_cpu_set_irq()
that we don't want to have to duplicate.)
* Now we have a 'max-v9' we can clean up how we deal with the GICv5
needing a v9 CPU
* having the cpuif identify itself by IAFFID and the IRS do an
IAFFID->cpu index lookup is unnecessarily clunky
Changes v1->v2:
* fixed the lack of ~ in masking calcs in patches 1 and 2
* patches 7-10 are new
Only patch 3 here has been reviewed.
thanks
-- PMM
Peter Maydell (9):
hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR
hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR
hw/intc/arm_gicv5: Regularize spacing in config_readl() switch
hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state
hw/intc/arm_gicv5: Free memory in finalize
target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status
target/arm: Only allow GICv5 on CPUs without AArch32 at EL1..3
hw/intc/arm_gicv5: Pass CPU index to CPU interface
hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi()
hw/intc/arm_gicv5.c | 72 +++++++++++++++++++++---------
hw/intc/arm_gicv5_common.c | 5 ++-
include/hw/intc/arm_gicv5.h | 1 +
include/hw/intc/arm_gicv5_stream.h | 10 +++--
target/arm/cpu-features.h | 8 ++++
target/arm/cpu.c | 61 ++++++++++---------------
target/arm/cpu.h | 10 +++--
target/arm/internals.h | 14 ++++++
target/arm/tcg/gicv5-cpuif.c | 41 +++--------------
9 files changed, 120 insertions(+), 102 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 1/9] hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-10-21 14:51 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 2/9] hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR Peter Maydell
` (8 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
The IRS_IST_BASER.ADDR documentation says:
For IRS_IST_CFGR.STRUCTURE = 0:
Bits[N:0] of the resulting address are 0 where N depends on the ISTSZ
and LPI_ID_BITS fields in IRS_IST_CFGR as follows:
N = Max(5, (ISTSZ + 1) + LPI_ID_BITS)
For IRS_IST_CFGR.STRUCTURE = 1:
Bits[N:0] of the resulting address are 0 where N depends on L2SZ,
ISTSZ, and LPI_ID_BITS in IRS_IST_CFGR as follows:
N = Max(5, LPI_ID_BITS - ((10 - ISTSZ) + (2 * L2SZ)) + 2)
This enforces that the IST is aligned to the size of the IST table,
or to a 64-byte boundary if the table is smaller than 64 bytes, even
if the guest programs the low bits in the ADDR field to specify an
insufficiently aligned address.
We were missing this check, and instead trusted the guest-provided
address to be sufficiently aligned already; add the alignment
enforcement.
The GICv5 spec defines the masking to be used with the expressions
quoted above, which work with the raw field values from the
IRS_IST_CFGR register; these simplify down to some straightforward
expressions based on the sanitized values we've already extracted
form those fields. You can get to the expressions we use here either
by working through the simplification of the GIC spec expressions, or
from first principles (the number of bits needed for the size of the
table in bytes is the number of bits that index it plus the number of
bits specifying the size of an individual table entry).
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/intc/arm_gicv5.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 08878193dfc..34c3ee90d43 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -1313,6 +1313,7 @@ static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value)
*/
GICv5ISTConfig *cfg = &s->phys_lpi_config[domain];
uint8_t istbits, l2bits, l2_idx_bits;
+ int tableszbits;
uint8_t id_bits = FIELD_EX64(cs->irs_ist_cfgr[domain],
IRS_IST_CFGR, LPI_ID_BITS);
id_bits = MIN(MAX(id_bits, QEMU_GICV5_MIN_LPI_ID_BITS), QEMU_GICV5_ID_BITS);
@@ -1359,6 +1360,33 @@ static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value)
cfg->l2_idx_bits = l2_idx_bits;
cfg->structure = FIELD_EX64(cs->irs_ist_cfgr[domain],
IRS_IST_CFGR, STRUCTURE);
+
+ /*
+ * Bits [N:0] of the base addr are ignored, to enforce that in a
+ * 1-level IST it is aligned to the size of the ISTE array,
+ * and in a 2-level IST it is aligned to the size of the L1 table.
+ * The spec defines N in terms of the raw L2SZ, ISTSZ and LPI_ID_BITS
+ * fields, but we have already sanitized these to
+ * istbits = ISTSZ + 2
+ * l2bits = (L2SZ * 2) + 12
+ * id_bits = LPI_ID_BITS
+ * We have also already enforced the "bottom 6 bits are 0" via
+ * R_IRS_IST_BASER_ADDR_MASK, so can skip the Max() operation.
+ * We want tableszbits == the number of bits to zero, which is N + 1.
+ */
+ if (cfg->structure) {
+ /*
+ * L1 table is indexed by the (id_bits - l2_idx_bits) bits in
+ * the ID, so has 2^(id_bits - l2_idx_bits) entries, and each
+ * entry is 8 bytes == 2^3.
+ */
+ tableszbits = id_bits - l2_idx_bits + 3;
+ } else {
+ /* table has 2^id_bits entries of 2^istbits size each */
+ tableszbits = id_bits + istbits;
+ }
+ cfg->base &= ~MAKE_64BIT_MASK(0, tableszbits);
+
if (!cfg->lpi_cache) {
/*
* Keys are GINT_TO_POINTER(intid), so we want the g_direct_hash
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 2/9] hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
2026-09-14 16:00 ` [PATCH v2 1/9] hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-10-21 14:52 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 3/9] hw/intc/arm_gicv5: Regularize spacing in config_readl() switch Peter Maydell
` (7 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
The GICv5 spec defines that an L2 IST is always aligned to its size,
by stating that bits [N:0] of the address are 0 where
N = 11 + (2 * L2SZ)
We were missing this check, and only aligning by applying
R_L1_ISTE_L2_ADDR_MASK, so we would only enforce 4K alignment even if
the L2 IST was 16K or 64K.
Add the missing masking of low bits. We already needed to calculate
the number of bits needed to index each byte in the L2 table, so add
it to the GICv5ISTConfig to save having to re-calculate it later.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/intc/arm_gicv5.c | 6 ++++++
include/hw/intc/arm_gicv5.h | 1 +
2 files changed, 7 insertions(+)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 34c3ee90d43..124816d864c 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -595,6 +595,11 @@ static bool get_l2_iste_addr(GICv5Common *cs, const GICv5ISTConfig *cfg,
return false;
}
l2_base = l1_iste & R_L1_ISTE_L2_ADDR_MASK;
+ /*
+ * The effective base address of the L2 table is always
+ * aligned to the size of the L2 table.
+ */
+ l2_base &= ~MAKE_64BIT_MASK(0, cfg->l2bits);
id = extract32(id, 0, cfg->l2_idx_bits);
} else {
/* 1-level table */
@@ -1358,6 +1363,7 @@ static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value)
cfg->id_bits = id_bits;
cfg->istsz = 1 << istbits;
cfg->l2_idx_bits = l2_idx_bits;
+ cfg->l2bits = l2bits;
cfg->structure = FIELD_EX64(cs->irs_ist_cfgr[domain],
IRS_IST_CFGR, STRUCTURE);
diff --git a/include/hw/intc/arm_gicv5.h b/include/hw/intc/arm_gicv5.h
index b8baf003ad6..d763a7939c7 100644
--- a/include/hw/intc/arm_gicv5.h
+++ b/include/hw/intc/arm_gicv5.h
@@ -23,6 +23,7 @@ typedef struct GICv5ISTConfig {
uint8_t id_bits; /* number of bits in an ID for this table */
uint8_t l2_idx_bits; /* number of ID bits that index into L2 table */
uint8_t istsz; /* L2 ISTE size in bytes */
+ uint8_t l2bits; /* bits needed to index L2 table in bytes */
bool structure; /* true if using 2-level table */
bool valid; /* true if this table is valid and usable */
/* This caches IST information about pending LPIs */
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 3/9] hw/intc/arm_gicv5: Regularize spacing in config_readl() switch
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
2026-09-14 16:00 ` [PATCH v2 1/9] hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR Peter Maydell
2026-09-14 16:00 ` [PATCH v2 2/9] hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-10-21 14:52 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 4/9] hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state Peter Maydell
` (6 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
In this file we don't put blank lines between cases in switches,
except that for the switch in config_readl() we've written it half
with blank lines and half without. Delete the blank lines to make it
consistent with itself and the rest of the file.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
---
hw/intc/arm_gicv5.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 124816d864c..e477c805b43 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -1468,53 +1468,41 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
v &= ~R_IRS_IDR0_VIRT_ONE_N_MASK;
}
return true;
-
case A_IRS_IDR1:
*data = cs->irs_idr1;
return true;
-
case A_IRS_IDR2:
*data = cs->irs_idr2;
return true;
-
case A_IRS_IDR3:
/* In EL3 IDR0.VIRT is 0 so this is RES0 */
*data = domain == GICV5_ID_EL3 ? 0 : cs->irs_idr3;
return true;
-
case A_IRS_IDR4:
/* In EL3 IDR0.VIRT is 0 so this is RES0 */
*data = domain == GICV5_ID_EL3 ? 0 : cs->irs_idr4;
return true;
-
case A_IRS_IDR5:
*data = cs->irs_idr5;
return true;
-
case A_IRS_IDR6:
*data = cs->irs_idr6;
return true;
-
case A_IRS_IDR7:
*data = cs->irs_idr7;
return true;
-
case A_IRS_IIDR:
*data = cs->irs_iidr;
return true;
-
case A_IRS_AIDR:
*data = cs->irs_aidr;
return true;
-
case A_IRS_IST_BASER:
*data = extract64(cs->irs_ist_baser[domain], 0, 32);
return true;
-
case A_IRS_IST_BASER + 4:
*data = extract64(cs->irs_ist_baser[domain], 32, 32);
return true;
-
case A_IRS_IST_STATUSR:
/*
* For QEMU writes to IRS_IST_BASER and IRS_MAP_L2_ISTR take effect
@@ -1522,11 +1510,9 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
*/
*data = R_IRS_IST_STATUSR_IDLE_MASK;
return true;
-
case A_IRS_IST_CFGR:
*data = cs->irs_ist_cfgr[domain];
return true;
-
case A_IRS_SPI_STATUSR:
/*
* QEMU writes to IRS_SPI_{CFGR,DOMAINR,SELR,VMR} take effect
@@ -1537,7 +1523,6 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
v = FIELD_DP32(v, IRS_SPI_STATUSR, IDLE, 1);
*data = v;
return true;
-
case A_IRS_SPI_CFGR:
{
GICv5SPIState *spi = spi_for_selr(cs, domain);
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 4/9] hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
` (2 preceding siblings ...)
2026-09-14 16:00 ` [PATCH v2 3/9] hw/intc/arm_gicv5: Regularize spacing in config_readl() switch Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-10-21 14:53 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 5/9] hw/intc/arm_gicv5: Free memory in finalize Peter Maydell
` (5 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
When the guest writes to IRS_IST_BASER we effectively cache the value
of the VALID bit in the GICv5ISTConfig valid field entry. In a few
places we check whether the IST is valid by doing a bit extract on
the register value; switch those to look at cfg->structure instead.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/intc/arm_gicv5.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index e477c805b43..c6914f7310c 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -1223,8 +1223,7 @@ static void irs_map_l2_istr_write(GICv5 *s, GICv5Domain domain, uint64_t value)
uint64_t l1_iste;
MemTxResult res;
- if (!FIELD_EX64(cs->irs_ist_baser[domain], IRS_IST_BASER, VALID) ||
- !cfg->structure) {
+ if (!cfg->valid || !cfg->structure) {
/* WI if no IST set up or it is not 2-level */
return;
}
@@ -1294,7 +1293,7 @@ static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value)
{
GICv5Common *cs = ARM_GICV5_COMMON(s);
- if (FIELD_EX64(cs->irs_ist_baser[domain], IRS_IST_BASER, VALID)) {
+ if (s->phys_lpi_config[domain].valid) {
/* If VALID is set, ADDR is RO and we can only update VALID */
bool valid = FIELD_EX64(value, IRS_IST_BASER, VALID);
if (valid) {
@@ -1604,7 +1603,7 @@ static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset,
deposit64(cs->irs_ist_baser[domain], 32, 32, data));
return true;
case A_IRS_IST_CFGR:
- if (FIELD_EX64(cs->irs_ist_baser[domain], IRS_IST_BASER, VALID)) {
+ if (s->phys_lpi_config[domain].valid) {
qemu_log_mask(LOG_GUEST_ERROR,
"guest tried to write IRS_IST_CFGR for %s config frame "
"while IST_BASER.VALID set\n", domain_name[domain]);
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 5/9] hw/intc/arm_gicv5: Free memory in finalize
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
` (3 preceding siblings ...)
2026-09-14 16:00 ` [PATCH v2 4/9] hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-09-14 16:00 ` [PATCH v2 6/9] target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status Peter Maydell
` (4 subsequent siblings)
9 siblings, 0 replies; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
The GICv5 allocates memory and creates hashtables during realize and
as part of its operation. Free those resources in finalize.
Since the GICv5 is created at QEMU startup and remains in existence
for the lifetime of the process, this isn't an in-practice leak,
only a theoretical one.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/intc/arm_gicv5.c | 11 +++++++++++
hw/intc/arm_gicv5_common.c | 3 +++
2 files changed, 14 insertions(+)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index c6914f7310c..6b72870439b 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -1970,6 +1970,17 @@ static void gicv5_init(Object *obj)
static void gicv5_finalize(Object *obj)
{
+ GICv5 *s = ARM_GICV5(obj);
+
+ for (int i = 0; i < NUM_GICV5_DOMAINS; i++) {
+ GHashTable *ht = s->phys_lpi_config[i].lpi_cache;
+
+ if (ht) {
+ g_hash_table_destroy(ht);
+ }
+
+ g_free(s->hppi[i]);
+ }
}
static void gicv5_class_init(ObjectClass *oc, const void *data)
diff --git a/hw/intc/arm_gicv5_common.c b/hw/intc/arm_gicv5_common.c
index b155486af65..cc67246686e 100644
--- a/hw/intc/arm_gicv5_common.c
+++ b/hw/intc/arm_gicv5_common.c
@@ -112,6 +112,9 @@ static void gicv5_common_init(Object *obj)
static void gicv5_common_finalize(Object *obj)
{
+ GICv5Common *cs = ARM_GICV5_COMMON(obj);
+
+ g_free(cs->spi);
}
static void gicv5_common_realize(DeviceState *dev, Error **errp)
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 6/9] target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
` (4 preceding siblings ...)
2026-09-14 16:00 ` [PATCH v2 5/9] hw/intc/arm_gicv5: Free memory in finalize Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-10-07 13:20 ` Philippe Mathieu-Daudé
2026-09-14 16:00 ` [PATCH v2 7/9] target/arm: Only allow GICv5 on CPUs without AArch32 at EL1..3 Peter Maydell
` (3 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
Currently the GICv5 cpu interface directly calls cpu_interrupt() and
cpu_reset_interrupt() to raise and lower IRQ and FIQ. This is almost
but not exactly what we do for GICv2/GICv3 external GPIO IRQ/FIQ line
changes. Those are handled by arm_cpu_set_irq(), and as well as
calling cpu_interrupt() or cpu_reset_interrupt() they also update
env->irq_line_state.
For IRQ and FIQ it doesn't matter whether we set and clear the bits
in irq_line_state, because nothing looks at them. But for VIRQ and
VFIQ it will matter, because VIRQ and VFIQ are the logical OR of the
line state and some bits in HCR_EL2. We're going to need to call
arm_cpu_set_irq() for VIRQ and VFIQ; for consistency, handle IRQ and
FIQ that way too.
Since arm_cpu_set_irq() is now a function called from other parts of
the CPU code, we make its first argument a properly typed one, with a
wrapper taking void* for when we need to use it as a
qemu_irq_handler.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/cpu.c | 14 +++++++++++---
target/arm/internals.h | 14 ++++++++++++++
target/arm/tcg/gicv5-cpuif.c | 35 ++++-------------------------------
3 files changed, 29 insertions(+), 34 deletions(-)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 77aa78f00e2..2b1b87b0ed4 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -806,9 +806,8 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el)
#ifndef CONFIG_USER_ONLY
-static void arm_cpu_set_irq(void *opaque, int irq, int level)
+void arm_cpu_set_irq(ARMCPU *cpu, int irq, int level)
{
- ARMCPU *cpu = opaque;
CPUARMState *env = &cpu->env;
CPUState *cs = CPU(cpu);
static const int mask[] = {
@@ -860,6 +859,15 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level)
}
}
+/*
+ * Wrapper of arm_cpu_set_irq with the right argument types to be a
+ * qemu_irq_handler function for our inbound GPIO lines.
+ */
+static void arm_cpu_gpio_set_irq(void *opaque, int irq, int level)
+{
+ arm_cpu_set_irq(opaque, irq, level);
+}
+
static bool arm_cpu_internal_is_big_endian(CPUState *cs)
{
ARMCPU *cpu = ARM_CPU(cs);
@@ -1263,7 +1271,7 @@ static void arm_cpu_initfn(Object *obj)
*/
qdev_init_gpio_in(DEVICE(cpu), arm_cpu_kvm_set_irq, 6);
} else {
- qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 6);
+ qdev_init_gpio_in(DEVICE(cpu), arm_cpu_gpio_set_irq, 6);
}
qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs,
diff --git a/target/arm/internals.h b/target/arm/internals.h
index 1775835ad50..c623e875136 100644
--- a/target/arm/internals.h
+++ b/target/arm/internals.h
@@ -1883,6 +1883,20 @@ void define_gcs_cpregs(ARMCPU *cpu);
/* Add the cpreg definitions for OMAP CP15 regs */
void define_omap_cp_regs(ARMCPU *cpu);
+#ifndef CONFIG_USER_ONLY
+/**
+ * arm_cpu_set_irq: Assert or clear an IRQ/FIQ/etc interrupt line
+ * @cpu: CPU to set interrupt on
+ * @irq: one of the ARM_CPU_IRQ/ARM_CPU_FIQ/ARM_CPU_VIRQ/etc values
+ * @level: 0 to deassert, non-zero to assert
+ *
+ * This function is called by the qemu_irq_handler function
+ * when IRQ etc are real inbound GPIO lines; it is also called
+ * directly by the GICv5 CPU interface.
+ */
+void arm_cpu_set_irq(ARMCPU *cpu, int irq, int level);
+#endif
+
/* Add the cpreg definitions for the GICv5 CPU interface */
void define_gicv5_cpuif_regs(ARMCPU *cpu);
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index a6e6c440849..517c780d1f7 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -173,33 +173,6 @@ static GICv5PendingIrq gic_hppi(CPUARMState *env, GICv5Domain domain)
return best;
}
-static void cpu_interrupt_update(CPUARMState *env, int irqtype, bool new_state)
-{
- CPUState *cs = env_cpu(env);
-
- /*
- * OPT: calling cpu_interrupt() and cpu_reset_interrupt() has the
- * correct behaviour, but is not optimal for the case where we're
- * setting the interrupt line to the same level it already has.
- *
- * Clearing an already clear interrupt is free (it's just doing an
- * atomic AND operation). Signalling an already set interrupt is a
- * bit less ideal (it might unnecessarily kick the CPU).
- *
- * We could potentially use cpu_test_interrupt(), like
- * arm_cpu_update_{virq,vfiq,vinmi,vserr}, since we always hold
- * the BQL here; or perhaps there is an abstraction we could
- * provide in the core code that all these places could call.
- *
- * For now, this is simple and definitely correct.
- */
- if (new_state) {
- cpu_interrupt(cs, irqtype);
- } else {
- cpu_reset_interrupt(cs, irqtype);
- }
-}
-
static void gicv5_update_irq_fiq(CPUARMState *env)
{
/*
@@ -243,12 +216,12 @@ static void gicv5_update_irq_fiq(CPUARMState *env)
/*
* Unlike a GICv3 or GICv2, there is no external IRQ or FIQ line
* to the CPU. Instead we directly signal the interrupt via
- * cpu_interrupt()/cpu_reset_interrupt().
+ * arm_cpu_set_irq().
*/
trace_gicv5_update_irq_fiq(irq, fiq, superpriority);
- cpu_interrupt_update(env, CPU_INTERRUPT_HARD, irq);
- cpu_interrupt_update(env, CPU_INTERRUPT_FIQ, fiq);
- cpu_interrupt_update(env, CPU_INTERRUPT_NMI, superpriority);
+ arm_cpu_set_irq(env_archcpu(env), ARM_CPU_IRQ, irq);
+ arm_cpu_set_irq(env_archcpu(env), ARM_CPU_FIQ, fiq);
+ arm_cpu_set_irq(env_archcpu(env), ARM_CPU_NMI, superpriority);
}
static void gic_recalc_ppi_hppi(CPUARMState *env)
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 7/9] target/arm: Only allow GICv5 on CPUs without AArch32 at EL1..3
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
` (5 preceding siblings ...)
2026-09-14 16:00 ` [PATCH v2 6/9] target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-09-14 16:00 ` [PATCH v2 8/9] hw/intc/arm_gicv5: Pass CPU index to CPU interface Peter Maydell
` (2 subsequent siblings)
9 siblings, 0 replies; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
We previously allowed the GICv5 on any 64-bit CPU, with a hack where
enabling the GICv5 silently disabled AArch32 for EL1..3.
Now that we have a separate 'max-v8' and 'max-v9' where max-v9
correctly doesn't implement AArch32 at EL1..3, we can fix the "when
do we expose the has_gcie property" condition so that we only allow
the GICv5 when AArch32 doesn't exist above EL0, and remove the hack
disabling AArch32.
We could instead have defined a new ARM_FEATURE_V9, but "allow the
feature to be present on CPUs where it doesn't actively break
anything" fits the way we've generally handled things in QEMU, and it
avoids having a new V9 feature bit that we only use for this one
thing and where it's not clear when we should be setting it for KVM,
hvf, etc accelerators.
In practice the only CPUs which will allow GICv5 now are max, max-v9
and a64fx.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
target/arm/cpu-features.h | 8 ++++++++
target/arm/cpu.c | 37 +++++++------------------------------
2 files changed, 15 insertions(+), 30 deletions(-)
diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h
index eecbc3b889e..c4029c0f73c 100644
--- a/target/arm/cpu-features.h
+++ b/target/arm/cpu-features.h
@@ -1129,6 +1129,14 @@ static inline bool isar_feature_aa64_aa32_el3(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL3) >= 2;
}
+static inline bool isar_feature_aa64_aa32_above_el0(const ARMISARegisters *id)
+{
+ /* True if any EL above EL0 has AArch32 support */
+ return isar_feature_aa64_aa32_el1(id) ||
+ isar_feature_aa64_aa32_el2(id) ||
+ isar_feature_aa64_aa32_el3(id);
+}
+
static inline bool isar_feature_aa64_ras(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64PFR0, RAS) != 0;
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 2b1b87b0ed4..bbf4e0aa465 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1584,8 +1584,13 @@ static void arm_cpu_post_init(Object *obj)
&cpu->rvbar_prop,
OBJ_PROP_FLAG_READWRITE);
- /* We only allow GICv5 on a 64-bit v8 CPU */
- if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
+ /*
+ * We only allow GICv5 on a CPU with no AArch32 support above EL0
+ * (which is typically a v9 CPU), because GICv5 defines no AArch32
+ * system registers to control it.
+ */
+ if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) &&
+ !cpu_isar_feature(aa64_aa32_above_el0, cpu)) {
qdev_property_add_static(DEVICE(obj), &arm_cpu_has_gcie_property);
}
}
@@ -2235,34 +2240,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
/* Report FEAT_GCIE in our ID registers if property was set */
FIELD_DP64_IDREG(isar, ID_AA64PFR2, GCIE, cpu->has_gcie);
- if (cpu_isar_feature(aa64_gcie, cpu)) {
- if (!arm_feature(env, ARM_FEATURE_AARCH64)) {
- /*
- * We only create the have_gcie property for AArch64 CPUs,
- * but the user might have tried aarch64=off with has_gcie=on.
- */
- error_setg(errp, "Cannot both enable has_gcie and disable aarch64");
- return;
- }
-
- /*
- * FEAT_GCIE implies Armv9, which implies no AArch32 above EL0.
- * Usually we don't strictly insist on this kind of feature
- * dependency, but in this case we enforce it, because the
- * GICv5 CPU interface has no AArch32 versions of its system
- * registers, so interrupts wouldn't work if we allowed AArch32
- * in EL1 or above. Downgrade "AArch32 and AArch64" to "AArch64".
- */
- if (cpu_isar_feature(aa64_aa32_el3, cpu)) {
- FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL3, 1);
- }
- if (cpu_isar_feature(aa64_aa32_el2, cpu)) {
- FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL2, 1);
- }
- if (cpu_isar_feature(aa64_aa32_el1, cpu)) {
- FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL1, 1);
- }
- }
if (cpu_isar_feature(aa64_mte, cpu)) {
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 8/9] hw/intc/arm_gicv5: Pass CPU index to CPU interface
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
` (6 preceding siblings ...)
2026-09-14 16:00 ` [PATCH v2 7/9] target/arm: Only allow GICv5 on CPUs without AArch32 at EL1..3 Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-09-21 12:17 ` Philippe Mathieu-Daudé
2026-09-14 16:00 ` [PATCH v2 9/9] hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi() Peter Maydell
2026-10-07 12:53 ` [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
9 siblings, 1 reply; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
In a real hardware GICv5, the IRS knows which CPU is talking to it
because each IRS<->cpuif link is a separate 1:1 connection. In
QEMU's implementation, the cpuif code calls functions like
gicv5_set_priority(), and currently we usually don't pass anything to
tell the IRS which cpuif is making the call, because mostly the IRS
doesn't need to care. There are a few places where it does need to
know, notably in gicv5_get_hppi(). Here we have the cpuif pass its
IAFFID; the IRS then looks that up by doing a linear search of the
cpu_iaffids[] array to get the index of the CPU.
For virtualization, many of the stream commands which don't currently
need to say which cpuif is making the call will have to start doing
so, because changing the state of a virtual interrupt requires the
IRS to look up the VPE which is resident on the calling cpuif. This
makes the IAFFID->cpuidx lookup seem increasingly awkward.
Change gicv5_set_gicv5state() to pass the IRS's CPU index value for
the cpuif as well as the guest-visible IAFFID, and make the stream
functions identify the cpuif by CPU index rather than IAFFID, to
avoid the unnecessary IAFFID->cpuidx lookup.
As this is now three GICv5-related things that we are keeping in the
CPU state struct, we push them into a sub-struct to keep them tidy.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/intc/arm_gicv5_common.c | 2 +-
include/hw/intc/arm_gicv5_stream.h | 6 ++++--
target/arm/cpu.c | 10 ++++++----
target/arm/cpu.h | 10 ++++++----
target/arm/tcg/gicv5-cpuif.c | 6 +++---
5 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/hw/intc/arm_gicv5_common.c b/hw/intc/arm_gicv5_common.c
index cc67246686e..cfadfee4f05 100644
--- a/hw/intc/arm_gicv5_common.c
+++ b/hw/intc/arm_gicv5_common.c
@@ -176,7 +176,7 @@ static void gicv5_common_realize(DeviceState *dev, Error **errp)
}
for (int i = 0; i < cs->num_cpus; i++) {
- if (!gicv5_set_gicv5state(cs->cpus[i], cs, cs->cpu_iaffids[i])) {
+ if (!gicv5_set_gicv5state(cs->cpus[i], cs, cs->cpu_iaffids[i], i)) {
error_setg(errp,
"CPU %d (IAFFID 0x%x) does not implement GICv5 CPU interface",
i, cs->cpu_iaffids[i]);
diff --git a/include/hw/intc/arm_gicv5_stream.h b/include/hw/intc/arm_gicv5_stream.h
index 3cc9f611557..f45072d8c57 100644
--- a/include/hw/intc/arm_gicv5_stream.h
+++ b/include/hw/intc/arm_gicv5_stream.h
@@ -20,7 +20,8 @@ typedef struct GICv5Common GICv5Common;
* gicv5_set_gicv5state
* @cpu: CPU object to tell about its IRS
* @cs: the GIC IRS it is connected to
- * @iaffid: the IAFFID of this CPU
+ * @iaffid: the IAFFID of this CPU (guest-visible)
+ * @cpuidx: the index of this CPU for this IRS (QEMU-internal)
*
* Set the CPU object's GICv5 pointer to point to this GIC IRS. The
* IRS must call this when it is realized, for each CPU it is
@@ -29,7 +30,8 @@ typedef struct GICv5Common GICv5Common;
* Returns true on success, false if the CPU doesn't implement the
* GICv5 CPU interface.
*/
-bool gicv5_set_gicv5state(ARMCPU *cpu, GICv5Common *cs, uint32_t iaffid);
+bool gicv5_set_gicv5state(ARMCPU *cpu, GICv5Common *cs, uint32_t iaffid,
+ uint32_t cpuidx);
/*
* The architected Stream Protocol is asynchronous; commands can be
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index bbf4e0aa465..a64cdb8e895 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1213,17 +1213,19 @@ static void arm_cpu_dump_state(CPUState *cs, FILE *f, int flags)
}
#ifndef CONFIG_USER_ONLY
-bool gicv5_set_gicv5state(ARMCPU *cpu, GICv5Common *cs, uint32_t iaffid)
+bool gicv5_set_gicv5state(ARMCPU *cpu, GICv5Common *cs, uint32_t iaffid,
+ uint32_t cpuidx)
{
/*
* Set this CPU's gicv5state pointer to point to the GIC that we are
- * connected to, and record our IAFFID.
+ * connected to, and record our IAFFID and CPU index.
*/
if (!cpu_isar_feature(aa64_gcie, cpu)) {
return false;
}
- cpu->env.gicv5state = cs;
- cpu->env.gicv5_iaffid = iaffid;
+ cpu->env.gicv5.gicv5state = cs;
+ cpu->env.gicv5.iaffid = iaffid;
+ cpu->env.gicv5.cpuidx = cpuidx;
return true;
}
#endif
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index e3f931dba26..41a8770b121 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -814,10 +814,12 @@ typedef struct CPUArchState {
const struct arm_boot_info *boot_info;
/* Store GICv3CPUState to access from this struct */
void *gicv3state;
- /* Similarly, for a GICv5Common */
- void *gicv5state;
- /* For GICv5, this CPU's IAFFID */
- uint64_t gicv5_iaffid;
+ /* Information the GICv5 IRS passes to us */
+ struct {
+ void *gicv5state; /* GICv5Common struct */
+ uint64_t iaffid; /* this CPU's IAFFID */
+ uint32_t cpuidx; /* GIC IRS-internal index of this CPU */
+ } gicv5;
#else /* CONFIG_USER_ONLY */
/* For usermode syscall translation. */
bool eabi;
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index 517c780d1f7..eea66a4140e 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -87,7 +87,7 @@ FIELD(ICC_HPPIR_EL1, HPPIV, 32, 1)
static GICv5Common *gicv5_get_gic(CPUARMState *env)
{
- return env->gicv5state;
+ return env->gicv5.gicv5state;
}
static GICv5Domain gicv5_logical_domain(CPUARMState *env)
@@ -147,7 +147,7 @@ static GICv5PendingIrq gic_hppi(CPUARMState *env, GICv5Domain domain)
return GICV5_PENDING_IRQ_NONE;
}
- irs_hppi = gicv5_get_hppi(gic, domain, env->gicv5_iaffid);
+ irs_hppi = gicv5_get_hppi(gic, domain, env->gicv5.iaffid);
/*
* If the best PPI and the best interrupt from the IRS have the
@@ -788,7 +788,7 @@ static const ARMCPRegInfo gicv5_cpuif_reginfo[] = {
.opc0 = 3, .opc1 = 0, .crn = 12, .crm = 10, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_NO_RAW,
/* ICC_IAFFIDR_EL1 holds the IAFFID only, in its low bits */
- .fieldoffset = offsetof(CPUARMState, gicv5_iaffid),
+ .fieldoffset = offsetof(CPUARMState, gicv5.iaffid),
/*
* The field is a constant value set in gicv5_set_gicv5state(),
* so don't allow it to be overwritten by reset.
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v2 9/9] hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi()
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
` (7 preceding siblings ...)
2026-09-14 16:00 ` [PATCH v2 8/9] hw/intc/arm_gicv5: Pass CPU index to CPU interface Peter Maydell
@ 2026-09-14 16:00 ` Peter Maydell
2026-09-21 12:17 ` Philippe Mathieu-Daudé
2026-10-07 12:53 ` [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
9 siblings, 1 reply; 18+ messages in thread
From: Peter Maydell @ 2026-09-14 16:00 UTC (permalink / raw)
To: qemu-arm, qemu-devel
Now the CPU interface knows its own cpuidx, we can pass that to
gicv5_get_hppi() and avoid the IAFFID->cpuidx lookup in the IRS.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/intc/arm_gicv5.c | 5 ++---
include/hw/intc/arm_gicv5_stream.h | 4 ++--
target/arm/tcg/gicv5-cpuif.c | 2 +-
3 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 6b72870439b..66953606419 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -531,12 +531,11 @@ static void irs_recall_hppis(GICv5 *s, GICv5Domain domain)
}
GICv5PendingIrq gicv5_get_hppi(GICv5Common *cs, GICv5Domain domain,
- uint32_t iaffid)
+ uint32_t cpuidx)
{
GICv5 *s = ARM_GICV5(cs);
- int cpuidx = irs_cpuidx_from_iaffid(cs, iaffid);
- assert(cpuidx >= 0);
+ assert(cpuidx < cs->num_cpus);
return s->hppi[domain][cpuidx];
}
diff --git a/include/hw/intc/arm_gicv5_stream.h b/include/hw/intc/arm_gicv5_stream.h
index f45072d8c57..641f3d0f499 100644
--- a/include/hw/intc/arm_gicv5_stream.h
+++ b/include/hw/intc/arm_gicv5_stream.h
@@ -204,14 +204,14 @@ void gicv5_forward_interrupt(ARMCPU *cpu, GICv5Domain domain);
* gicv5_get_hppi
* @cs: GIC IRS to query
* @domain: interrupt domain to act on
- * @iaffid: IAFFID of this CPU interface
+ * @cpuidx: IRS CPU index of this CPU interface
*
* Ask the IRS for the highest priority pending interrupt that it has
* for this CPU. This returns the equivalent of what in the stream
* protocol is the outstanding interrupt sent with a Forward packet.
*/
GICv5PendingIrq gicv5_get_hppi(GICv5Common *cs, GICv5Domain domain,
- uint32_t iaffid);
+ uint32_t cpuidx);
/**
* gicv5_deactivate
diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c
index eea66a4140e..0b1033c1ef0 100644
--- a/target/arm/tcg/gicv5-cpuif.c
+++ b/target/arm/tcg/gicv5-cpuif.c
@@ -147,7 +147,7 @@ static GICv5PendingIrq gic_hppi(CPUARMState *env, GICv5Domain domain)
return GICV5_PENDING_IRQ_NONE;
}
- irs_hppi = gicv5_get_hppi(gic, domain, env->gicv5.iaffid);
+ irs_hppi = gicv5_get_hppi(gic, domain, env->gicv5.cpuidx);
/*
* If the best PPI and the best interrupt from the IRS have the
--
2.43.0
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [PATCH v2 8/9] hw/intc/arm_gicv5: Pass CPU index to CPU interface
2026-09-14 16:00 ` [PATCH v2 8/9] hw/intc/arm_gicv5: Pass CPU index to CPU interface Peter Maydell
@ 2026-09-21 12:17 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-21 12:17 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel
On 14/9/26 18:00, Peter Maydell wrote:
> In a real hardware GICv5, the IRS knows which CPU is talking to it
> because each IRS<->cpuif link is a separate 1:1 connection. In
> QEMU's implementation, the cpuif code calls functions like
> gicv5_set_priority(), and currently we usually don't pass anything to
> tell the IRS which cpuif is making the call, because mostly the IRS
> doesn't need to care. There are a few places where it does need to
> know, notably in gicv5_get_hppi(). Here we have the cpuif pass its
> IAFFID; the IRS then looks that up by doing a linear search of the
> cpu_iaffids[] array to get the index of the CPU.
>
> For virtualization, many of the stream commands which don't currently
> need to say which cpuif is making the call will have to start doing
> so, because changing the state of a virtual interrupt requires the
> IRS to look up the VPE which is resident on the calling cpuif. This
> makes the IAFFID->cpuidx lookup seem increasingly awkward.
>
> Change gicv5_set_gicv5state() to pass the IRS's CPU index value for
> the cpuif as well as the guest-visible IAFFID, and make the stream
> functions identify the cpuif by CPU index rather than IAFFID, to
> avoid the unnecessary IAFFID->cpuidx lookup.
>
> As this is now three GICv5-related things that we are keeping in the
> CPU state struct, we push them into a sub-struct to keep them tidy.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> hw/intc/arm_gicv5_common.c | 2 +-
> include/hw/intc/arm_gicv5_stream.h | 6 ++++--
> target/arm/cpu.c | 10 ++++++----
> target/arm/cpu.h | 10 ++++++----
> target/arm/tcg/gicv5-cpuif.c | 6 +++---
> 5 files changed, 20 insertions(+), 14 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 9/9] hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi()
2026-09-14 16:00 ` [PATCH v2 9/9] hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi() Peter Maydell
@ 2026-09-21 12:17 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-09-21 12:17 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel
On 14/9/26 18:00, Peter Maydell wrote:
> Now the CPU interface knows its own cpuidx, we can pass that to
> gicv5_get_hppi() and avoid the IAFFID->cpuidx lookup in the IRS.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> hw/intc/arm_gicv5.c | 5 ++---
> include/hw/intc/arm_gicv5_stream.h | 4 ++--
> target/arm/tcg/gicv5-cpuif.c | 2 +-
> 3 files changed, 5 insertions(+), 6 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
` (8 preceding siblings ...)
2026-09-14 16:00 ` [PATCH v2 9/9] hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi() Peter Maydell
@ 2026-10-07 12:53 ` Peter Maydell
9 siblings, 0 replies; 18+ messages in thread
From: Peter Maydell @ 2026-10-07 12:53 UTC (permalink / raw)
To: qemu-arm, qemu-devel
Ping for patch review on this series?
thanks
-- PMM
On Mon, 14 Sept 2026 at 17:00, Peter Maydell <peter.maydell@linaro.org> wrote:
>
> This patchset is a compilation of minor fixes and cleanups to the
> GICv5 code that I found in the course of implementing virtualization
> support:
>
> * Various in-memory data structures are defined to be aligned to
> their size, even if the guest provides a base address that is less
> aligned
> * We had some extra blank lines that didn't match the style in the
> rest of the file
> * We were unnecessarily extracting a bit from a raw register value
> when we already had it cached as a boolean in a struct
> * We weren't tidying up our allocated memory on finalize
> * We can re-use arm_cpu_set_irq() rather than implementing the
> set/clear IRQ/FIQ/NMI locally. (This doesn't matter much for these,
> but VIRQ/VFIQ/VINMI have more complex logic in arm_cpu_set_irq()
> that we don't want to have to duplicate.)
> * Now we have a 'max-v9' we can clean up how we deal with the GICv5
> needing a v9 CPU
> * having the cpuif identify itself by IAFFID and the IRS do an
> IAFFID->cpu index lookup is unnecessarily clunky
>
> Changes v1->v2:
> * fixed the lack of ~ in masking calcs in patches 1 and 2
> * patches 7-10 are new
>
> Only patch 3 here has been reviewed.
>
> thanks
> -- PMM
>
> Peter Maydell (9):
> hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR
> hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR
> hw/intc/arm_gicv5: Regularize spacing in config_readl() switch
> hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state
> hw/intc/arm_gicv5: Free memory in finalize
> target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status
> target/arm: Only allow GICv5 on CPUs without AArch32 at EL1..3
> hw/intc/arm_gicv5: Pass CPU index to CPU interface
> hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi()
>
> hw/intc/arm_gicv5.c | 72 +++++++++++++++++++++---------
> hw/intc/arm_gicv5_common.c | 5 ++-
> include/hw/intc/arm_gicv5.h | 1 +
> include/hw/intc/arm_gicv5_stream.h | 10 +++--
> target/arm/cpu-features.h | 8 ++++
> target/arm/cpu.c | 61 ++++++++++---------------
> target/arm/cpu.h | 10 +++--
> target/arm/internals.h | 14 ++++++
> target/arm/tcg/gicv5-cpuif.c | 41 +++--------------
> 9 files changed, 120 insertions(+), 102 deletions(-)
>
> --
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 6/9] target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status
2026-09-14 16:00 ` [PATCH v2 6/9] target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status Peter Maydell
@ 2026-10-07 13:20 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 18+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-10-07 13:20 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel
On 14/9/26 18:00, Peter Maydell wrote:
> Currently the GICv5 cpu interface directly calls cpu_interrupt() and
> cpu_reset_interrupt() to raise and lower IRQ and FIQ. This is almost
> but not exactly what we do for GICv2/GICv3 external GPIO IRQ/FIQ line
> changes. Those are handled by arm_cpu_set_irq(), and as well as
> calling cpu_interrupt() or cpu_reset_interrupt() they also update
> env->irq_line_state.
>
> For IRQ and FIQ it doesn't matter whether we set and clear the bits
> in irq_line_state, because nothing looks at them. But for VIRQ and
> VFIQ it will matter, because VIRQ and VFIQ are the logical OR of the
> line state and some bits in HCR_EL2. We're going to need to call
> arm_cpu_set_irq() for VIRQ and VFIQ; for consistency, handle IRQ and
> FIQ that way too.
>
> Since arm_cpu_set_irq() is now a function called from other parts of
> the CPU code, we make its first argument a properly typed one, with a
> wrapper taking void* for when we need to use it as a
> qemu_irq_handler.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
> target/arm/cpu.c | 14 +++++++++++---
> target/arm/internals.h | 14 ++++++++++++++
> target/arm/tcg/gicv5-cpuif.c | 35 ++++-------------------------------
> 3 files changed, 29 insertions(+), 34 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 1/9] hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR
2026-09-14 16:00 ` [PATCH v2 1/9] hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR Peter Maydell
@ 2026-10-21 14:51 ` Richard Henderson
0 siblings, 0 replies; 18+ messages in thread
From: Richard Henderson @ 2026-10-21 14:51 UTC (permalink / raw)
To: qemu-devel
On 9/14/26 18:00, Peter Maydell wrote:
> The IRS_IST_BASER.ADDR documentation says:
>
> For IRS_IST_CFGR.STRUCTURE = 0:
> Bits[N:0] of the resulting address are 0 where N depends on the ISTSZ
> and LPI_ID_BITS fields in IRS_IST_CFGR as follows:
> N = Max(5, (ISTSZ + 1) + LPI_ID_BITS)
> For IRS_IST_CFGR.STRUCTURE = 1:
>
> Bits[N:0] of the resulting address are 0 where N depends on L2SZ,
> ISTSZ, and LPI_ID_BITS in IRS_IST_CFGR as follows:
> N = Max(5, LPI_ID_BITS - ((10 - ISTSZ) + (2 * L2SZ)) + 2)
>
> This enforces that the IST is aligned to the size of the IST table,
> or to a 64-byte boundary if the table is smaller than 64 bytes, even
> if the guest programs the low bits in the ADDR field to specify an
> insufficiently aligned address.
>
> We were missing this check, and instead trusted the guest-provided
> address to be sufficiently aligned already; add the alignment
> enforcement.
>
> The GICv5 spec defines the masking to be used with the expressions
> quoted above, which work with the raw field values from the
> IRS_IST_CFGR register; these simplify down to some straightforward
> expressions based on the sanitized values we've already extracted
> form those fields. You can get to the expressions we use here either
> by working through the simplification of the GIC spec expressions, or
> from first principles (the number of bits needed for the size of the
> table in bytes is the number of bits that index it plus the number of
> bits specifying the size of an individual table entry).
>
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> hw/intc/arm_gicv5.c | 28 ++++++++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 2/9] hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR
2026-09-14 16:00 ` [PATCH v2 2/9] hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR Peter Maydell
@ 2026-10-21 14:52 ` Richard Henderson
0 siblings, 0 replies; 18+ messages in thread
From: Richard Henderson @ 2026-10-21 14:52 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel
On 9/14/26 18:00, Peter Maydell wrote:
> The GICv5 spec defines that an L2 IST is always aligned to its size,
> by stating that bits [N:0] of the address are 0 where
> N = 11 + (2 * L2SZ)
>
> We were missing this check, and only aligning by applying
> R_L1_ISTE_L2_ADDR_MASK, so we would only enforce 4K alignment even if
> the L2 IST was 16K or 64K.
>
> Add the missing masking of low bits. We already needed to calculate
> the number of bits needed to index each byte in the L2 table, so add
> it to the GICv5ISTConfig to save having to re-calculate it later.
>
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> hw/intc/arm_gicv5.c | 6 ++++++
> include/hw/intc/arm_gicv5.h | 1 +
> 2 files changed, 7 insertions(+)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/9] hw/intc/arm_gicv5: Regularize spacing in config_readl() switch
2026-09-14 16:00 ` [PATCH v2 3/9] hw/intc/arm_gicv5: Regularize spacing in config_readl() switch Peter Maydell
@ 2026-10-21 14:52 ` Richard Henderson
0 siblings, 0 replies; 18+ messages in thread
From: Richard Henderson @ 2026-10-21 14:52 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel
On 9/14/26 18:00, Peter Maydell wrote:
> In this file we don't put blank lines between cases in switches,
> except that for the switch in config_readl() we've written it half
> with blank lines and half without. Delete the blank lines to make it
> consistent with itself and the rest of the file.
>
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> Reviewed-by: Philippe Mathieu-Daudé<philmd@oss.qualcomm.com>
> ---
> hw/intc/arm_gicv5.c | 15 ---------------
> 1 file changed, 15 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 4/9] hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state
2026-09-14 16:00 ` [PATCH v2 4/9] hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state Peter Maydell
@ 2026-10-21 14:53 ` Richard Henderson
0 siblings, 0 replies; 18+ messages in thread
From: Richard Henderson @ 2026-10-21 14:53 UTC (permalink / raw)
To: Peter Maydell, qemu-arm, qemu-devel
On 9/14/26 18:00, Peter Maydell wrote:
> When the guest writes to IRS_IST_BASER we effectively cache the value
> of the VALID bit in the GICv5ISTConfig valid field entry. In a few
> places we check whether the IST is valid by doing a bit extract on
> the register value; switch those to look at cfg->structure instead.
>
> Signed-off-by: Peter Maydell<peter.maydell@linaro.org>
> ---
> hw/intc/arm_gicv5.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
r~
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-10-21 14:54 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-14 16:00 [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups Peter Maydell
2026-09-14 16:00 ` [PATCH v2 1/9] hw/intc/arm_gicv5: Ignore low bits in IRS_IST_BASER.ADDR Peter Maydell
2026-10-21 14:51 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 2/9] hw/intc/arm_gicv5: Ignore low bits in L1_ISTE.L2_ADDR Peter Maydell
2026-10-21 14:52 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 3/9] hw/intc/arm_gicv5: Regularize spacing in config_readl() switch Peter Maydell
2026-10-21 14:52 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 4/9] hw/intc/arm_gicv5: Use cached IRS_IST_BASER.VALID state Peter Maydell
2026-10-21 14:53 ` Richard Henderson
2026-09-14 16:00 ` [PATCH v2 5/9] hw/intc/arm_gicv5: Free memory in finalize Peter Maydell
2026-09-14 16:00 ` [PATCH v2 6/9] target/arm: GICv5 cpuif: Use arm_cpu_set_irq() to set IRQ/FIQ status Peter Maydell
2026-10-07 13:20 ` Philippe Mathieu-Daudé
2026-09-14 16:00 ` [PATCH v2 7/9] target/arm: Only allow GICv5 on CPUs without AArch32 at EL1..3 Peter Maydell
2026-09-14 16:00 ` [PATCH v2 8/9] hw/intc/arm_gicv5: Pass CPU index to CPU interface Peter Maydell
2026-09-21 12:17 ` Philippe Mathieu-Daudé
2026-09-14 16:00 ` [PATCH v2 9/9] hw/intc/arm_gicv5: Pass cpuidx rather than iaffid in gicv5_get_hppi() Peter Maydell
2026-09-21 12:17 ` Philippe Mathieu-Daudé
2026-10-07 12:53 ` [PATCH v2 0/9] hw/intc/arm_gicv5: Minor fixes and cleanups 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.