[PATCH 15/65] hw/intc/arm_gicv5: Implement IRS_IST_{BASER, STATUSR, CFGR}

Peter Maydell posted 65 patches 1 month, 2 weeks ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Pierrick Bouvier <pierrick.bouvier@linaro.org>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, Eduardo Habkost <eduardo@habkost.net>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>
There is a newer version of this series
[PATCH 15/65] hw/intc/arm_gicv5: Implement IRS_IST_{BASER, STATUSR, CFGR}
Posted by Peter Maydell 1 month, 2 weeks ago
Implement the three registers that handle configuration of the
interrupt status table for physical LPIs:

 * IRS_IST_BASER holds the base address of the table, and
   has the VALID bit that tells the IRS to start using the config
 * IRS_IST_CFGR has all the other config data for the table
 * IRS_IST_STATUSR has the IDLE bit that tells software when
   updates to IRS_IST_BASER have taken effect

Implement these registers.  Note that neither BASER nor CFGR can be
written when VALID == 1, except to clear the VALID bit.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv5.c                | 71 ++++++++++++++++++++++++++++++
 hw/intc/arm_gicv5_common.c         |  4 ++
 include/hw/intc/arm_gicv5_common.h |  3 ++
 3 files changed, 78 insertions(+)

diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index f34bb81966..f5933197ea 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -265,6 +265,24 @@ REG64(IRS_SWERR_SYNDROMER0, 0x3c8)
 REG64(IRS_SWERR_SYNDROMER1, 0x3d0)
     FIELD(IRS_SWERR_SYNDROMER2, ADDR, 3, 53)
 
+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 VALID is set, ADDR is RO and we can only update VALID */
+        bool valid = FIELD_EX64(value, IRS_IST_BASER, VALID);
+        if (valid) {
+            /* Ignore 1->1 transition */
+            return;
+        }
+        cs->irs_ist_baser[domain] = FIELD_DP64(cs->irs_ist_baser[domain],
+                                               IRS_IST_BASER, VALID, valid);
+        return;
+    }
+    cs->irs_ist_baser[domain] = value;
+}
+
 static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
                          uint64_t *data, MemTxAttrs attrs)
 {
@@ -323,6 +341,26 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
     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
+         * instantaneously, and the guest can never see the IDLE bit as 0.
+         */
+        *data = R_IRS_IST_STATUSR_IDLE_MASK;
+        return true;
+
+    case A_IRS_IST_CFGR:
+        *data = cs->irs_ist_cfgr[domain];
+        return true;
     }
     return false;
 }
@@ -330,18 +368,51 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
 static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset,
                           uint64_t data, MemTxAttrs attrs)
 {
+    GICv5Common *cs = ARM_GICV5_COMMON(s);
+
+    switch (offset) {
+    case A_IRS_IST_BASER:
+        irs_ist_baser_write(s, domain,
+                            deposit64(cs->irs_ist_baser[domain], 0, 32, data));
+        return true;
+    case A_IRS_IST_BASER + 4:
+        irs_ist_baser_write(s, domain,
+                            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)) {
+            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]);
+        } else {
+            cs->irs_ist_cfgr[domain] = data;
+        }
+        return true;
+    }
     return false;
 }
 
 static bool config_readll(GICv5 *s, GICv5Domain domain, hwaddr offset,
                           uint64_t *data, MemTxAttrs attrs)
 {
+    GICv5Common *cs = ARM_GICV5_COMMON(s);
+
+    switch (offset) {
+    case A_IRS_IST_BASER:
+        *data = cs->irs_ist_baser[domain];
+        return true;
+    }
     return false;
 }
 
 static bool config_writell(GICv5 *s, GICv5Domain domain, hwaddr offset,
                            uint64_t data, MemTxAttrs attrs)
 {
+    switch (offset) {
+    case A_IRS_IST_BASER:
+        irs_ist_baser_write(s, domain, data);
+        return true;
+    }
     return false;
 }
 
diff --git a/hw/intc/arm_gicv5_common.c b/hw/intc/arm_gicv5_common.c
index 046dcdf5a3..751df2001c 100644
--- a/hw/intc/arm_gicv5_common.c
+++ b/hw/intc/arm_gicv5_common.c
@@ -62,6 +62,10 @@ void gicv5_common_init_irqs_and_mmio(GICv5Common *cs,
 
 static void gicv5_common_reset_hold(Object *obj, ResetType type)
 {
+    GICv5Common *cs = ARM_GICV5_COMMON(obj);
+
+    memset(cs->irs_ist_baser, 0, sizeof(cs->irs_ist_baser));
+    memset(cs->irs_ist_cfgr, 0, sizeof(cs->irs_ist_cfgr));
 }
 
 static void gicv5_common_init(Object *obj)
diff --git a/include/hw/intc/arm_gicv5_common.h b/include/hw/intc/arm_gicv5_common.h
index 7db2c87ddc..2a49d58679 100644
--- a/include/hw/intc/arm_gicv5_common.h
+++ b/include/hw/intc/arm_gicv5_common.h
@@ -62,6 +62,9 @@ struct GICv5Common {
 
     MemoryRegion iomem[NUM_GICV5_DOMAINS];
 
+    uint64_t irs_ist_baser[NUM_GICV5_DOMAINS];
+    uint32_t irs_ist_cfgr[NUM_GICV5_DOMAINS];
+
     /* Bits here are set for each physical interrupt domain implemented */
     uint8_t implemented_domains;
 
-- 
2.43.0
Re: [PATCH 15/65] hw/intc/arm_gicv5: Implement IRS_IST_{BASER, STATUSR, CFGR}
Posted by Jonathan Cameron via qemu development 1 month ago
On Mon, 23 Feb 2026 17:01:22 +0000
Peter Maydell <peter.maydell@linaro.org> wrote:

> Implement the three registers that handle configuration of the
> interrupt status table for physical LPIs:
> 
>  * IRS_IST_BASER holds the base address of the table, and
>    has the VALID bit that tells the IRS to start using the config
>  * IRS_IST_CFGR has all the other config data for the table
>  * IRS_IST_STATUSR has the IDLE bit that tells software when
>    updates to IRS_IST_BASER have taken effect
> 
> Implement these registers.  Note that neither BASER nor CFGR can be
> written when VALID == 1, except to clear the VALID bit.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

One query inline but it'll probably become obvious later, so 
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> diff --git a/include/hw/intc/arm_gicv5_common.h b/include/hw/intc/arm_gicv5_common.h
> index 7db2c87ddc..2a49d58679 100644
> --- a/include/hw/intc/arm_gicv5_common.h
> +++ b/include/hw/intc/arm_gicv5_common.h
> @@ -62,6 +62,9 @@ struct GICv5Common {
>  
>      MemoryRegion iomem[NUM_GICV5_DOMAINS];
>  
> +    uint64_t irs_ist_baser[NUM_GICV5_DOMAINS];
> +    uint32_t irs_ist_cfgr[NUM_GICV5_DOMAINS];

Feels like you are going to have a lot of these, why not an array of
structures?

> +
>      /* Bits here are set for each physical interrupt domain implemented */
>      uint8_t implemented_domains;
>
Re: [PATCH 15/65] hw/intc/arm_gicv5: Implement IRS_IST_{BASER, STATUSR, CFGR}
Posted by Peter Maydell 1 month ago
On Fri, 6 Mar 2026 at 17:39, Jonathan Cameron
<jonathan.cameron@huawei.com> wrote:
>
> On Mon, 23 Feb 2026 17:01:22 +0000
> Peter Maydell <peter.maydell@linaro.org> wrote:
>
> > Implement the three registers that handle configuration of the
> > interrupt status table for physical LPIs:
> >
> >  * IRS_IST_BASER holds the base address of the table, and
> >    has the VALID bit that tells the IRS to start using the config
> >  * IRS_IST_CFGR has all the other config data for the table
> >  * IRS_IST_STATUSR has the IDLE bit that tells software when
> >    updates to IRS_IST_BASER have taken effect
> >
> > Implement these registers.  Note that neither BASER nor CFGR can be
> > written when VALID == 1, except to clear the VALID bit.
> >
> > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>
> One query inline but it'll probably become obvious later, so
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
>
> > diff --git a/include/hw/intc/arm_gicv5_common.h b/include/hw/intc/arm_gicv5_common.h
> > index 7db2c87ddc..2a49d58679 100644
> > --- a/include/hw/intc/arm_gicv5_common.h
> > +++ b/include/hw/intc/arm_gicv5_common.h
> > @@ -62,6 +62,9 @@ struct GICv5Common {
> >
> >      MemoryRegion iomem[NUM_GICV5_DOMAINS];
> >
> > +    uint64_t irs_ist_baser[NUM_GICV5_DOMAINS];
> > +    uint32_t irs_ist_cfgr[NUM_GICV5_DOMAINS];
>
> Feels like you are going to have a lot of these, why not an array of
> structures?

Six of them in total, at least in this series:

    uint64_t irs_ist_baser[NUM_GICV5_DOMAINS];
    uint32_t irs_ist_cfgr[NUM_GICV5_DOMAINS];
    uint32_t irs_spi_selr[NUM_GICV5_DOMAINS];
    uint32_t irs_cr0[NUM_GICV5_DOMAINS];
    uint32_t irs_cr1[NUM_GICV5_DOMAINS];
    uint32_t irs_pe_selr[NUM_GICV5_DOMAINS];

I tend to implement banked registers as separate arrays
because it's what I'm used to from how we've done the
target/arm/cpu.h banked registers, and because
 cs->irs_ist_baser[domain]
is a bit shorter and more straightforward
 cs->regs[domain].irs_ist_baser
and because you generally don't want "the struct with all
the regs for this domain" as a thing itself.

-- PMM