[PATCH 20/65] hw/intc/arm_gicv5: Implement remaining set-config functions

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 20/65] hw/intc/arm_gicv5: Implement remaining set-config functions
Posted by Peter Maydell 1 month, 2 weeks ago
Implement the GICv5 functions corresponding to the stream protocol
SetEnabled, SetPending, SetHandling, and SetTarget commands.  These
work exactly like SetPriority: the IRS looks up the L2TE and updates
the corresponding field in it with the new value.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/intc/arm_gicv5.c                | 133 +++++++++++++++++++++++++++++
 hw/intc/trace-events               |   4 +
 include/hw/intc/arm_gicv5_stream.h |  68 +++++++++++++++
 include/hw/intc/arm_gicv5_types.h  |  15 ++++
 4 files changed, 220 insertions(+)

diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index af27fb7e63..3c6ef17573 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -492,6 +492,139 @@ void gicv5_set_priority(GICv5Common *cs, uint32_t id,
     put_l2_iste(cs, cfg, &h);
 }
 
+void gicv5_set_enabled(GICv5Common *cs, uint32_t id,
+                        bool enabled, GICv5Domain domain,
+                        GICv5IntType type, bool virtual)
+{
+    const GICv5ISTConfig *cfg;
+    GICv5 *s = ARM_GICV5(cs);
+    uint32_t *l2_iste_p;
+    L2_ISTE_Handle h;
+
+    trace_gicv5_set_enabled(domain_name[domain], inttype_name(type), virtual,
+                            id, enabled);
+    if (virtual) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_enabled: tried to set "
+                      "enable state of a virtual interrupt\n");
+        return;
+    }
+    if (type != GICV5_LPI) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_enabled: tried to set "
+                      "enable state of bad interrupt type %d\n", type);
+        return;
+    }
+    cfg = &s->phys_lpi_config[domain];
+    l2_iste_p = get_l2_iste(cs, cfg, id, &h);
+    if (!l2_iste_p) {
+        return;
+    }
+    *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, ENABLE, enabled);
+    put_l2_iste(cs, cfg, &h);
+}
+
+void gicv5_set_pending(GICv5Common *cs, uint32_t id,
+                       bool pending, GICv5Domain domain,
+                       GICv5IntType type, bool virtual)
+{
+    const GICv5ISTConfig *cfg;
+    GICv5 *s = ARM_GICV5(cs);
+    uint32_t *l2_iste_p;
+    L2_ISTE_Handle h;
+
+    trace_gicv5_set_pending(domain_name[domain], inttype_name(type), virtual,
+                            id, pending);
+    if (virtual) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_pending: tried to set "
+                      "pending state of a virtual interrupt\n");
+        return;
+    }
+    if (type != GICV5_LPI) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_pending: tried to set "
+                      "pending state of bad interrupt type %d\n", type);
+        return;
+    }
+    cfg = &s->phys_lpi_config[domain];
+    l2_iste_p = get_l2_iste(cs, cfg, id, &h);
+    if (!l2_iste_p) {
+        return;
+    }
+    *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, PENDING, pending);
+    put_l2_iste(cs, cfg, &h);
+}
+
+void gicv5_set_handling(GICv5Common *cs, uint32_t id,
+                        GICv5HandlingMode handling, GICv5Domain domain,
+                        GICv5IntType type, bool virtual)
+{
+    const GICv5ISTConfig *cfg;
+    GICv5 *s = ARM_GICV5(cs);
+    uint32_t *l2_iste_p;
+    L2_ISTE_Handle h;
+
+    trace_gicv5_set_handling(domain_name[domain], inttype_name(type), virtual,
+                            id, handling);
+    if (virtual) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_handling: tried to set "
+                      "handling mode of a virtual interrupt\n");
+        return;
+    }
+    if (type != GICV5_LPI) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_handling: tried to set "
+                      "handling mode of bad interrupt type %d\n", type);
+        return;
+    }
+    cfg = &s->phys_lpi_config[domain];
+    l2_iste_p = get_l2_iste(cs, cfg, id, &h);
+    if (!l2_iste_p) {
+        return;
+    }
+    *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, HM, handling);
+    put_l2_iste(cs, cfg, &h);
+}
+
+void gicv5_set_target(GICv5Common *cs, uint32_t id, uint32_t iaffid,
+                      GICv5RoutingMode irm, GICv5Domain domain,
+                      GICv5IntType type, bool virtual)
+{
+    const GICv5ISTConfig *cfg;
+    GICv5 *s = ARM_GICV5(cs);
+    uint32_t *l2_iste_p;
+    L2_ISTE_Handle h;
+
+    trace_gicv5_set_target(domain_name[domain], inttype_name(type), virtual,
+                           id, iaffid, irm);
+    if (virtual) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_target: tried to set "
+                      "target of a virtual interrupt\n");
+        return;
+    }
+    if (irm != GICV5_TARGETED) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_target: tried to set "
+                      "1-of-N routing\n");
+        /*
+         * In the cpuif insn "GIC CDAFF", IRM is RES0 for a GIC which does not
+         * support 1-of-N routing. So warn, and fall through to treat
+         * IRM=1 the same as IRM=0.
+         */
+    }
+    if (type != GICV5_LPI) {
+        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_target: tried to set "
+                      "target of bad interrupt type %d\n", type);
+        return;
+    }
+    cfg = &s->phys_lpi_config[domain];
+    l2_iste_p = get_l2_iste(cs, cfg, id, &h);
+    if (!l2_iste_p) {
+        return;
+    }
+    /*
+     * For QEMU we do not implement 1-of-N routing, and so L2_ISTE.IRM is RES0.
+     * We never read it, and we can skip explicitly writing it to zero here.
+     */
+    *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, IAFFID, iaffid);
+    put_l2_iste(cs, cfg, &h);
+}
+
 static void irs_map_l2_istr_write(GICv5 *s, GICv5Domain domain, uint64_t value)
 {
     GICv5Common *cs = ARM_GICV5_COMMON(s);
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 42f5e73d54..37ca6e8e12 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -236,6 +236,10 @@ gicv5_spi(uint32_t id, int level) "GICv5 SPI ID %u asserted at level %d"
 gicv5_ist_valid(const char *domain, uint64_t base, uint8_t id_bits, uint8_t l2_idx_bits, uint8_t istsz, bool structure) "GICv5 IRS %s IST now valid: base 0x%" PRIx64 " id_bits %u l2_idx_bits %u IST entry size %u 2-level %d"
 gicv5_ist_invalid(const char *domain) "GICv5 IRS %s IST no longer valid"
 gicv5_set_priority(const char *domain, const char *type, bool virtual, uint32_t id, uint8_t priority) "GICv5 IRS SetPriority %s %s virtual:%d ID %u prio %u"
+gicv5_set_enabled(const char *domain, const char *type, bool virtual, uint32_t id, bool enabled) "GICv5 IRS SetEnabled %s %s virtual:%d ID %u enabled %d"
+gicv5_set_pending(const char *domain, const char *type, bool virtual, uint32_t id, bool pending) "GICv5 IRS SetPending %s %s virtual:%d ID %u pending %d"
+gicv5_set_handling(const char *domain, const char *type, bool virtual, uint32_t id, int handling) "GICv5 IRS SetHandling %s %s virtual:%d ID %u handling %d"
+gicv5_set_target(const char *domain, const char *type, bool virtual, uint32_t id, uint32_t iaffid, int irm) "GICv5 IRS SetTarget %s %s virtual:%d ID %u IAFFID %u routingmode %d"
 
 # arm_gicv5_common.c
 gicv5_common_realize(uint32_t irsid, uint32_t num_cpus, uint32_t spi_base, uint32_t spi_irs_range, uint32_t spi_range) "GICv5 IRS realized: IRS ID %u, %u CPUs, SPI base %u, SPI IRS range %u, SPI range %u"
diff --git a/include/hw/intc/arm_gicv5_stream.h b/include/hw/intc/arm_gicv5_stream.h
index 3239a86f1a..db0e3e01c6 100644
--- a/include/hw/intc/arm_gicv5_stream.h
+++ b/include/hw/intc/arm_gicv5_stream.h
@@ -58,4 +58,72 @@ void gicv5_set_priority(GICv5Common *cs, uint32_t id,
                         uint8_t priority, GICv5Domain domain,
                         GICv5IntType type, bool virtual);
 
+/**
+ * gicv5_set_enabled
+ * @cs: GIC IRS to send command to
+ * @id: interrupt ID
+ * @enabled: new enabled state
+ * @domain: interrupt Domain to act on
+ * @type: interrupt type (LPI or SPI)
+ * @virtual: true if this is a virtual interrupt
+ *
+ * Set enabled state of an interrupt; matches stream interface
+ * SetEnabled command from CPUIF to IRS. There is no report back
+ * of success/failure to the CPUIF in the protocol.
+ */
+void gicv5_set_enabled(GICv5Common *cs, uint32_t id,
+                       bool enabled, GICv5Domain domain,
+                       GICv5IntType type, bool virtual);
+
+/**
+ * gicv5_set_pending
+ * @cs: GIC IRS to send command to
+ * @id: interrupt ID
+ * @pending: new pending state
+ * @domain: interrupt Domain to act on
+ * @type: interrupt type (LPI or SPI)
+ * @virtual: true if this is a virtual interrupt
+ *
+ * Set pending state of an interrupt; matches stream interface
+ * SetPending command from CPUIF to IRS. There is no report back
+ * of success/failure to the CPUIF in the protocol.
+ */
+void gicv5_set_pending(GICv5Common *cs, uint32_t id,
+                       bool pending, GICv5Domain domain,
+                       GICv5IntType type, bool virtual);
+
+/**
+ * gicv5_set_handling
+ * @cs: GIC IRS to send command to
+ * @id: interrupt ID
+ * @handling: new handling mode
+ * @domain: interrupt Domain to act on
+ * @type: interrupt type (LPI or SPI)
+ * @virtual: true if this is a virtual interrupt
+ *
+ * Set handling mode of an interrupt (edge/level); matches stream interface
+ * SetHandling command from CPUIF to IRS. There is no report back
+ * of success/failure to the CPUIF in the protocol.
+ */
+void gicv5_set_handling(GICv5Common *cs, uint32_t id,
+                        GICv5HandlingMode handling, GICv5Domain domain,
+                        GICv5IntType type, bool virtual);
+
+/**
+ * gicv5_set_target
+ * @cs: GIC IRS to send command to
+ * @id: interrupt ID
+ * @iaffid: new target PE's interrupt affinity
+ * @irm: interrupt routing mode (targeted vs 1-of-N)
+ * @domain: interrupt Domain to act on
+ * @type: interrupt type (LPI or SPI)
+ * @virtual: true if this is a virtual interrupt
+ *
+ * Set handling mode of an interrupt (edge/level); matches stream interface
+ * SetHandling command from CPUIF to IRS. There is no report back
+ * of success/failure to the CPUIF in the protocol.
+ */
+void gicv5_set_target(GICv5Common *cs, uint32_t id, uint32_t iaffid,
+                      GICv5RoutingMode irm, GICv5Domain domain,
+                      GICv5IntType type, bool virtual);
 #endif
diff --git a/include/hw/intc/arm_gicv5_types.h b/include/hw/intc/arm_gicv5_types.h
index b4452a7b7d..15d4d5c3f4 100644
--- a/include/hw/intc/arm_gicv5_types.h
+++ b/include/hw/intc/arm_gicv5_types.h
@@ -55,4 +55,19 @@ typedef enum GICv5IntType {
     GICV5_SPI = 3,
 } GICv5IntType;
 
+/* Interrupt handling mode (same encoding as L2_ISTE.HM) */
+typedef enum GICv5HandlingMode {
+    GICV5_EDGE = 0,
+    GICV5_LEVEL = 1,
+} GICv5HandlingMode;
+
+/*
+ * Interrupt routing mode (same encoding as L2_ISTE.IRM).
+ * Note that 1-of-N support is option and QEMU does not implement it.
+ */
+typedef enum GICv5RoutingMode {
+    GICV5_TARGETED = 0,
+    GICV5_1OFN = 1,
+} GICv5RoutingMode;
+
 #endif
-- 
2.43.0
Re: [PATCH 20/65] hw/intc/arm_gicv5: Implement remaining set-config functions
Posted by Jonathan Cameron via qemu development 1 month ago
On Mon, 23 Feb 2026 17:01:27 +0000
Peter Maydell <peter.maydell@linaro.org> wrote:

> Implement the GICv5 functions corresponding to the stream protocol
> SetEnabled, SetPending, SetHandling, and SetTarget commands.  These
> work exactly like SetPriority: the IRS looks up the L2TE and updates
> the corresponding field in it with the new value.
> 
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Trivial formatting and naming comments only.
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> ---
>  hw/intc/arm_gicv5.c                | 133 +++++++++++++++++++++++++++++
>  hw/intc/trace-events               |   4 +
>  include/hw/intc/arm_gicv5_stream.h |  68 +++++++++++++++
>  include/hw/intc/arm_gicv5_types.h  |  15 ++++
>  4 files changed, 220 insertions(+)
> 
> diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
> index af27fb7e63..3c6ef17573 100644
> --- a/hw/intc/arm_gicv5.c
> +++ b/hw/intc/arm_gicv5.c
> @@ -492,6 +492,139 @@ void gicv5_set_priority(GICv5Common *cs, uint32_t id,
>      put_l2_iste(cs, cfg, &h);
>  }
>  
> +void gicv5_set_enabled(GICv5Common *cs, uint32_t id,
> +                        bool enabled, GICv5Domain domain,
> +                        GICv5IntType type, bool virtual)
Seems like wrapping as:
void gicv5_set_enabled(GICv5Common *cs, uint32_t id, bool enabled,
                       GICv5Domain domain, GICv5IntType type, bool virtual)

might be better. I'm not seeing clear reason to do the 3 line version
unless maybe another parameter turns up in a later patch.
Same applies to other cases.


> +{
> +    const GICv5ISTConfig *cfg;
> +    GICv5 *s = ARM_GICV5(cs);
> +    uint32_t *l2_iste_p;
> +    L2_ISTE_Handle h;
> +
> +    trace_gicv5_set_enabled(domain_name[domain], inttype_name(type), virtual,
> +                            id, enabled);
> +    if (virtual) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_enabled: tried to set "
> +                      "enable state of a virtual interrupt\n");
> +        return;
> +    }
> +    if (type != GICV5_LPI) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_enabled: tried to set "
> +                      "enable state of bad interrupt type %d\n", type);
> +        return;
> +    }
> +    cfg = &s->phys_lpi_config[domain];
> +    l2_iste_p = get_l2_iste(cs, cfg, id, &h);
> +    if (!l2_iste_p) {
> +        return;
> +    }
> +    *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, ENABLE, enabled);
> +    put_l2_iste(cs, cfg, &h);
> +}

> +void gicv5_set_handling(GICv5Common *cs, uint32_t id,

Might be worth throwing _mode in there as set_handling almost
feels like 'set that it is being handled'  Also aligns better
with the HM naming in the spec.

Not that important though give from the actual code it is easy to
see this is HM.

> +                        GICv5HandlingMode handling, GICv5Domain domain,
> +                        GICv5IntType type, bool virtual)
> +{
> +    const GICv5ISTConfig *cfg;
> +    GICv5 *s = ARM_GICV5(cs);
> +    uint32_t *l2_iste_p;
> +    L2_ISTE_Handle h;
> +
> +    trace_gicv5_set_handling(domain_name[domain], inttype_name(type), virtual,
> +                            id, handling);
> +    if (virtual) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_handling: tried to set "
> +                      "handling mode of a virtual interrupt\n");
> +        return;
> +    }
> +    if (type != GICV5_LPI) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_handling: tried to set "
> +                      "handling mode of bad interrupt type %d\n", type);
> +        return;
> +    }
> +    cfg = &s->phys_lpi_config[domain];
> +    l2_iste_p = get_l2_iste(cs, cfg, id, &h);
> +    if (!l2_iste_p) {
> +        return;
> +    }
> +    *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, HM, handling);
> +    put_l2_iste(cs, cfg, &h);
> +}
Re: [PATCH 20/65] hw/intc/arm_gicv5: Implement remaining set-config functions
Posted by Peter Maydell 3 weeks, 2 days ago
On Wed, 11 Mar 2026 at 14:15, Jonathan Cameron
<jonathan.cameron@huawei.com> wrote:
>
> On Mon, 23 Feb 2026 17:01:27 +0000
> Peter Maydell <peter.maydell@linaro.org> wrote:
>
> > Implement the GICv5 functions corresponding to the stream protocol
> > SetEnabled, SetPending, SetHandling, and SetTarget commands.  These
> > work exactly like SetPriority: the IRS looks up the L2TE and updates
> > the corresponding field in it with the new value.
> >
> > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> Trivial formatting and naming comments only.
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>

> > +void gicv5_set_handling(GICv5Common *cs, uint32_t id,
>
> Might be worth throwing _mode in there as set_handling almost
> feels like 'set that it is being handled'  Also aligns better
> with the HM naming in the spec.

These functions are named to align with the stream protocol
command names in the spec. I agree that "set_handling_mode"
would be a nicer name, but the spec calls this command SetHandling...

thanks
-- PMM