[PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs

Luc Michel posted 48 patches 4 months ago
Maintainers: Alistair Francis <alistair@alistair23.me>, "Edgar E. Iglesias" <edgar.iglesias@gmail.com>, Peter Maydell <peter.maydell@linaro.org>, Jason Wang <jasowang@redhat.com>
There is a newer version of this series
[PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Luc Michel 4 months ago
Improve the IRQ index in the VersalMap structure to turn it into a
descriptor:
   - the lower 16 bits still represent the IRQ index
   - bit 18 is used to indicate a shared IRQ connected to a OR gate
   - bits 19 to 22 indicate the index on the OR gate.

This allows to share an IRQ among multiple devices. An OR gate is
created to connect the devices to the actual IRQ pin.

Signed-off-by: Luc Michel <luc.michel@amd.com>
---
 hw/arm/xlnx-versal.c | 62 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c
index 58176fa11e5..89c93278336 100644
--- a/hw/arm/xlnx-versal.c
+++ b/hw/arm/xlnx-versal.c
@@ -41,10 +41,21 @@
 #define GEM_REVISION        0x40070106
 
 #define VERSAL_NUM_PMC_APB_IRQS 18
 #define NUM_OSPI_IRQ_LINES 3
 
+/*
+ * IRQ descriptor to catch the following cases:
+ *   - Multiple devices can connect to the same IRQ. They are OR'ed together.
+ */
+FIELD(VERSAL_IRQ, IRQ, 0, 16)
+FIELD(VERSAL_IRQ, ORED, 18, 1)
+FIELD(VERSAL_IRQ, OR_IDX, 19, 4) /* input index on the IRQ OR gate */
+
+#define OR_IRQ(irq, or_idx) \
+    (R_VERSAL_IRQ_ORED_MASK | ((or_idx) << R_VERSAL_IRQ_OR_IDX_SHIFT) | (irq))
+
 typedef struct VersalSimplePeriphMap {
     uint64_t addr;
     int irq;
 } VersalSimplePeriphMap;
 
@@ -172,13 +183,56 @@ static inline Object *versal_get_child_idx(Versal *s, const char *child,
     g_autofree char *n = g_strdup_printf("%s[%zu]", child, idx);
 
     return versal_get_child(s, n);
 }
 
+/*
+ * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is
+ * used to return the corresponding or gate input IRQ. The or gate is created if
+ * not already existant.
+ *
+ * Or gates are placed under the /soc/irq-or-gates QOM container.
+ */
+static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx,
+                                          qemu_irq target_irq)
+{
+    Object *container = versal_get_child(s, "irq-or-gates");
+    DeviceState *dev;
+    g_autofree char *name;
+    int idx, or_idx;
+
+    idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
+    or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX);
+
+    name = g_strdup_printf("irq[%d]", idx);
+    dev = DEVICE(object_resolve_path_at(container, name));
+
+    if (dev == NULL) {
+        dev = qdev_new(TYPE_OR_IRQ);
+        object_property_add_child(container, name, OBJECT(dev));
+        qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH);
+        qdev_realize_and_unref(dev, NULL, &error_abort);
+        qdev_connect_gpio_out(dev, 0, target_irq);
+    }
+
+    return qdev_get_gpio_in(dev, or_idx);
+}
+
 static qemu_irq versal_get_irq(Versal *s, int irq_idx)
 {
-    return qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), irq_idx);
+    qemu_irq irq;
+    bool ored;
+
+    ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED);
+
+    irq = qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), irq_idx);
+
+    if (ored) {
+        irq = versal_get_irq_or_gate_in(s, irq_idx, irq);
+    }
+
+    return irq;
 }
 
 static void versal_sysbus_connect_irq(Versal *s, SysBusDevice *sbd,
                                       int sbd_idx, int irq_idx)
 {
@@ -1209,10 +1263,11 @@ static uint32_t fdt_add_clk_node(Versal *s, const char *name,
 
 static void versal_realize(DeviceState *dev, Error **errp)
 {
     Versal *s = XLNX_VERSAL_BASE(dev);
     qemu_irq pic[XLNX_VERSAL_NR_IRQS];
+    Object *container;
     const VersalMap *map = versal_get_map(s);
     size_t i;
 
     if (s->cfg.fdt == NULL) {
         int fdt_size;
@@ -1223,10 +1278,15 @@ static void versal_realize(DeviceState *dev, Error **errp)
     s->phandle.clk_25mhz = fdt_add_clk_node(s, "/clk25", 25 * 1000 * 1000);
     s->phandle.clk_125mhz = fdt_add_clk_node(s, "/clk125", 125 * 1000 * 1000);
 
     versal_create_apu_cpus(s);
     versal_create_apu_gic(s, pic);
+
+    container = object_new(TYPE_CONTAINER);
+    object_property_add_child(OBJECT(s), "irq-or-gates", container);
+    object_unref(container);
+
     versal_create_rpu_cpus(s);
 
     for (i = 0; i < map->num_uart; i++) {
         versal_create_uart(s, &map->uart[i], i);
     }
-- 
2.50.0
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Peter Maydell 3 months, 1 week ago
On Wed, 16 Jul 2025 at 10:55, Luc Michel <luc.michel@amd.com> wrote:
>
> Improve the IRQ index in the VersalMap structure to turn it into a
> descriptor:
>    - the lower 16 bits still represent the IRQ index
>    - bit 18 is used to indicate a shared IRQ connected to a OR gate
>    - bits 19 to 22 indicate the index on the OR gate.
>
> This allows to share an IRQ among multiple devices. An OR gate is
> created to connect the devices to the actual IRQ pin.


> +/*
> + * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is
> + * used to return the corresponding or gate input IRQ. The or gate is created if
> + * not already existant.
> + *
> + * Or gates are placed under the /soc/irq-or-gates QOM container.
> + */
> +static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx,
> +                                          qemu_irq target_irq)
> +{
> +    Object *container = versal_get_child(s, "irq-or-gates");
> +    DeviceState *dev;
> +    g_autofree char *name;
> +    int idx, or_idx;
> +
> +    idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
> +    or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX);
> +
> +    name = g_strdup_printf("irq[%d]", idx);
> +    dev = DEVICE(object_resolve_path_at(container, name));
> +
> +    if (dev == NULL) {
> +        dev = qdev_new(TYPE_OR_IRQ);

Here we create a device...

> +        object_property_add_child(container, name, OBJECT(dev));
> +        qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH);
> +        qdev_realize_and_unref(dev, NULL, &error_abort);
> +        qdev_connect_gpio_out(dev, 0, target_irq);
> +    }
> +
> +    return qdev_get_gpio_in(dev, or_idx);

...but then we don't save the pointer to it, so the only
thing still hanging onto it is the QOM tree.

If you want to change "embedded device struct" into
"allocate memory to create devices" that's fine, but the
SoC should still keep track of everything it's creating,
so that (at least in theory) it could clean it up on
unrealize.

thanks
-- PMM
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Luc Michel 2 months, 4 weeks ago
Hi Peter,

On 13:24 Mon 04 Aug     , Peter Maydell wrote:
> On Wed, 16 Jul 2025 at 10:55, Luc Michel <luc.michel@amd.com> wrote:
> >
> > Improve the IRQ index in the VersalMap structure to turn it into a
> > descriptor:
> >    - the lower 16 bits still represent the IRQ index
> >    - bit 18 is used to indicate a shared IRQ connected to a OR gate
> >    - bits 19 to 22 indicate the index on the OR gate.
> >
> > This allows to share an IRQ among multiple devices. An OR gate is
> > created to connect the devices to the actual IRQ pin.
> 
> 
> > +/*
> > + * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is
> > + * used to return the corresponding or gate input IRQ. The or gate is created if
> > + * not already existant.
> > + *
> > + * Or gates are placed under the /soc/irq-or-gates QOM container.
> > + */
> > +static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx,
> > +                                          qemu_irq target_irq)
> > +{
> > +    Object *container = versal_get_child(s, "irq-or-gates");
> > +    DeviceState *dev;
> > +    g_autofree char *name;
> > +    int idx, or_idx;
> > +
> > +    idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
> > +    or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX);
> > +
> > +    name = g_strdup_printf("irq[%d]", idx);
> > +    dev = DEVICE(object_resolve_path_at(container, name));
> > +
> > +    if (dev == NULL) {
> > +        dev = qdev_new(TYPE_OR_IRQ);
> 
> Here we create a device...
> 
> > +        object_property_add_child(container, name, OBJECT(dev));
> > +        qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH);
> > +        qdev_realize_and_unref(dev, NULL, &error_abort);
> > +        qdev_connect_gpio_out(dev, 0, target_irq);
> > +    }
> > +
> > +    return qdev_get_gpio_in(dev, or_idx);
> 
> ...but then we don't save the pointer to it, so the only
> thing still hanging onto it is the QOM tree.
> 
> If you want to change "embedded device struct" into
> "allocate memory to create devices" that's fine, but the
> SoC should still keep track of everything it's creating,
> so that (at least in theory) it could clean it up on
> unrealize.

I'm not sure I fully understand your point:

   - The OR gate device is parented to the "irq-or-gates" container,
     which is itself parented to the SoC. So finalizing (freeing) the
     SoC would trigger a cascade of free calls to the children,
     including those objects right?
   - Are you talking specifically about an unrealize call on the SoC
     object, without a subsequent free? I guess we don't expect to see
     such thing to happen in QEMU. The warning in the qdev_unrealize()
     documentation seems to go in that direction.

For reference, the QOM tree is as follows:

/machine (amd-versal-virt-machine)
  [...]
  /xlnx-versal (xlnx-versal)
    [...]
    /irq-or-gates (container)
      /gic-irq[121] (or-irq)
        /unnamed-gpio-in[0] (irq)
        /unnamed-gpio-in[10] (irq)
        /unnamed-gpio-in[11] (irq) 
        /unnamed-gpio-in[12] (irq)
        /unnamed-gpio-in[13] (irq) 
        /unnamed-gpio-in[14] (irq)
        /unnamed-gpio-in[15] (irq) 
        /unnamed-gpio-in[1] (irq)
        /unnamed-gpio-in[2] (irq)  
        /unnamed-gpio-in[3] (irq)
        /unnamed-gpio-in[4] (irq)
        /unnamed-gpio-in[5] (irq)
        /unnamed-gpio-in[6] (irq)
        /unnamed-gpio-in[7] (irq)
        /unnamed-gpio-in[8] (irq)
        /unnamed-gpio-in[9] (irq)
    [...]

Thanks

Luc
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Peter Maydell 2 months, 3 weeks ago
On Mon, 18 Aug 2025 at 14:30, Luc Michel <luc.michel@amd.com> wrote:
>
> Hi Peter,
>
> On 13:24 Mon 04 Aug     , Peter Maydell wrote:
> > On Wed, 16 Jul 2025 at 10:55, Luc Michel <luc.michel@amd.com> wrote:
> > >
> > > +static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx,
> > > +                                          qemu_irq target_irq)
> > > +{
> > > +    Object *container = versal_get_child(s, "irq-or-gates");
> > > +    DeviceState *dev;
> > > +    g_autofree char *name;
> > > +    int idx, or_idx;
> > > +
> > > +    idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
> > > +    or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX);
> > > +
> > > +    name = g_strdup_printf("irq[%d]", idx);
> > > +    dev = DEVICE(object_resolve_path_at(container, name));
> > > +
> > > +    if (dev == NULL) {
> > > +        dev = qdev_new(TYPE_OR_IRQ);
> >
> > Here we create a device...
> >
> > > +        object_property_add_child(container, name, OBJECT(dev));
> > > +        qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH);
> > > +        qdev_realize_and_unref(dev, NULL, &error_abort);
> > > +        qdev_connect_gpio_out(dev, 0, target_irq);
> > > +    }
> > > +
> > > +    return qdev_get_gpio_in(dev, or_idx);
> >
> > ...but then we don't save the pointer to it, so the only
> > thing still hanging onto it is the QOM tree.
> >
> > If you want to change "embedded device struct" into
> > "allocate memory to create devices" that's fine, but the
> > SoC should still keep track of everything it's creating,
> > so that (at least in theory) it could clean it up on
> > unrealize.
>
> I'm not sure I fully understand your point:
>
>    - The OR gate device is parented to the "irq-or-gates" container,
>      which is itself parented to the SoC. So finalizing (freeing) the
>      SoC would trigger a cascade of free calls to the children,
>      including those objects right?

Ah, I hadn't noticed that we add the object as a QOM child
of the SoC here.

It does mean you can't ever get back to the OR gate object
except by walking the QOM tree, but I suppose that's OK.

It would be helpful if you could run "make check" under
the clang leak sanitizer with your patches added, to see
if it complains about anything. (Unfortunately it will
definitely complain about at least some pre-existing
leaks, I suspect.)

thanks
-- PMM
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Luc Michel 2 months, 3 weeks ago
On 16:25 Tue 19 Aug     , Peter Maydell wrote:
> On Mon, 18 Aug 2025 at 14:30, Luc Michel <luc.michel@amd.com> wrote:
> >
> > Hi Peter,
> >
> > On 13:24 Mon 04 Aug     , Peter Maydell wrote:
> > > On Wed, 16 Jul 2025 at 10:55, Luc Michel <luc.michel@amd.com> wrote:
> > > >
> > > > +static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx,
> > > > +                                          qemu_irq target_irq)
> > > > +{
> > > > +    Object *container = versal_get_child(s, "irq-or-gates");
> > > > +    DeviceState *dev;
> > > > +    g_autofree char *name;
> > > > +    int idx, or_idx;
> > > > +
> > > > +    idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
> > > > +    or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX);
> > > > +
> > > > +    name = g_strdup_printf("irq[%d]", idx);
> > > > +    dev = DEVICE(object_resolve_path_at(container, name));
> > > > +
> > > > +    if (dev == NULL) {
> > > > +        dev = qdev_new(TYPE_OR_IRQ);
> > >
> > > Here we create a device...
> > >
> > > > +        object_property_add_child(container, name, OBJECT(dev));
> > > > +        qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH);
> > > > +        qdev_realize_and_unref(dev, NULL, &error_abort);
> > > > +        qdev_connect_gpio_out(dev, 0, target_irq);
> > > > +    }
> > > > +
> > > > +    return qdev_get_gpio_in(dev, or_idx);
> > >
> > > ...but then we don't save the pointer to it, so the only
> > > thing still hanging onto it is the QOM tree.
> > >
> > > If you want to change "embedded device struct" into
> > > "allocate memory to create devices" that's fine, but the
> > > SoC should still keep track of everything it's creating,
> > > so that (at least in theory) it could clean it up on
> > > unrealize.
> >
> > I'm not sure I fully understand your point:
> >
> >    - The OR gate device is parented to the "irq-or-gates" container,
> >      which is itself parented to the SoC. So finalizing (freeing) the
> >      SoC would trigger a cascade of free calls to the children,
> >      including those objects right?
> 
> Ah, I hadn't noticed that we add the object as a QOM child
> of the SoC here.
> 
> It does mean you can't ever get back to the OR gate object
> except by walking the QOM tree, but I suppose that's OK.
> 
> It would be helpful if you could run "make check" under
> the clang leak sanitizer with your patches added, to see
> if it complains about anything. (Unfortunately it will
> definitely complain about at least some pre-existing
> leaks, I suspect.)
Yes I did that before sending my series and fixed all my leaks. As you
say there are some existing ones, mainly in the register API (IIRC it
does create an object that is not correctly parented).

I'll send v2 with your other remarks addressed then.

Thanks

Luc
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Peter Maydell 2 months, 3 weeks ago
On Wed, 20 Aug 2025 at 08:19, Luc Michel <luc.michel@amd.com> wrote:
> > It would be helpful if you could run "make check" under
> > the clang leak sanitizer with your patches added, to see
> > if it complains about anything. (Unfortunately it will
> > definitely complain about at least some pre-existing
> > leaks, I suspect.)
> Yes I did that before sending my series and fixed all my leaks. As you
> say there are some existing ones, mainly in the register API (IIRC it
> does create an object that is not correctly parented).

Yeah, register_init_block() seems to be broken in two ways:
 (1) it calls object_initialize() rather than
     object_initialize_child(), so the objects won't
     be automatically unreffed; but it doesn't manually
     unref them in register_finalize_block() either
 (2) the TYPE_REGISTER objects are a subclass of TYPE_DEVICE,
     but the code never calls realize on them. This means that
     if you fix point (1) then you trip the assert in
     qdev_assert_realized_properly_cb() which checks that
     every TYPE_DEVICE in the QOM tree was realized...

-- PMM
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Luc Michel 2 months, 3 weeks ago
On 14:55 Thu 21 Aug     , Peter Maydell wrote:
> On Wed, 20 Aug 2025 at 08:19, Luc Michel <luc.michel@amd.com> wrote:
> > > It would be helpful if you could run "make check" under
> > > the clang leak sanitizer with your patches added, to see
> > > if it complains about anything. (Unfortunately it will
> > > definitely complain about at least some pre-existing
> > > leaks, I suspect.)
> > Yes I did that before sending my series and fixed all my leaks. As you
> > say there are some existing ones, mainly in the register API (IIRC it
> > does create an object that is not correctly parented).
> 
> Yeah, register_init_block() seems to be broken in two ways:
>  (1) it calls object_initialize() rather than
>      object_initialize_child(), so the objects won't
>      be automatically unreffed; but it doesn't manually
>      unref them in register_finalize_block() either
>  (2) the TYPE_REGISTER objects are a subclass of TYPE_DEVICE,
>      but the code never calls realize on them. This means that
>      if you fix point (1) then you trip the assert in
>      qdev_assert_realized_properly_cb() which checks that
>      every TYPE_DEVICE in the QOM tree was realized...
> 

I'm willing to address this in a follow-up series.

Luc
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Peter Maydell 2 months, 3 weeks ago
On Fri, 22 Aug 2025 at 16:19, Luc Michel <luc.michel@amd.com> wrote:
>
> On 14:55 Thu 21 Aug     , Peter Maydell wrote:
> > On Wed, 20 Aug 2025 at 08:19, Luc Michel <luc.michel@amd.com> wrote:
> > > > It would be helpful if you could run "make check" under
> > > > the clang leak sanitizer with your patches added, to see
> > > > if it complains about anything. (Unfortunately it will
> > > > definitely complain about at least some pre-existing
> > > > leaks, I suspect.)
> > > Yes I did that before sending my series and fixed all my leaks. As you
> > > say there are some existing ones, mainly in the register API (IIRC it
> > > does create an object that is not correctly parented).
> >
> > Yeah, register_init_block() seems to be broken in two ways:
> >  (1) it calls object_initialize() rather than
> >      object_initialize_child(), so the objects won't
> >      be automatically unreffed; but it doesn't manually
> >      unref them in register_finalize_block() either
> >  (2) the TYPE_REGISTER objects are a subclass of TYPE_DEVICE,
> >      but the code never calls realize on them. This means that
> >      if you fix point (1) then you trip the assert in
> >      qdev_assert_realized_properly_cb() which checks that
> >      every TYPE_DEVICE in the QOM tree was realized...
> >
>
> I'm willing to address this in a follow-up series.

That would be great; the leaks have been around for ages, so
there's no huge rush about trying to fix them, and they're the
"object was going to stay around for the life of the simulation
anyway" kind of leak where the major reason to squash them is
just that they clutter up the output when you run ASAN...

The "need to realize the objects" part is a bit awkward, because
the current API assumes you can just call one function in your
instance_init, and I guess we'd need to turn this into "provide an
init and a realize", or else insist that you only call
register_init_block() in realize methods...

There's also basically the same problem in xlnx-versal-canfd.c
canfd_populate_regarray() which seems to be rolling its own
version of register_init_block(), and doing it with at least
one extra bug. (Maybe that could be refactored to use
register_init_block() ?)

thanks
-- PMM
Re: [PATCH 13/48] hw/arm/xlnx-versal: VersalMap: add support for OR'ed IRQs
Posted by Francisco Iglesias 3 months, 2 weeks ago
On Wed, Jul 16, 2025 at 11:53:55AM +0200, Luc Michel wrote:
> Improve the IRQ index in the VersalMap structure to turn it into a
> descriptor:
>    - the lower 16 bits still represent the IRQ index
>    - bit 18 is used to indicate a shared IRQ connected to a OR gate
>    - bits 19 to 22 indicate the index on the OR gate.
> 
> This allows to share an IRQ among multiple devices. An OR gate is
> created to connect the devices to the actual IRQ pin.
> 
> Signed-off-by: Luc Michel <luc.michel@amd.com>

Reviewed-by: Francisco Iglesias <francisco.iglesias@amd.com>

> ---
>  hw/arm/xlnx-versal.c | 62 +++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 61 insertions(+), 1 deletion(-)
> 
> diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c
> index 58176fa11e5..89c93278336 100644
> --- a/hw/arm/xlnx-versal.c
> +++ b/hw/arm/xlnx-versal.c
> @@ -41,10 +41,21 @@
>  #define GEM_REVISION        0x40070106
>  
>  #define VERSAL_NUM_PMC_APB_IRQS 18
>  #define NUM_OSPI_IRQ_LINES 3
>  
> +/*
> + * IRQ descriptor to catch the following cases:
> + *   - Multiple devices can connect to the same IRQ. They are OR'ed together.
> + */
> +FIELD(VERSAL_IRQ, IRQ, 0, 16)
> +FIELD(VERSAL_IRQ, ORED, 18, 1)
> +FIELD(VERSAL_IRQ, OR_IDX, 19, 4) /* input index on the IRQ OR gate */
> +
> +#define OR_IRQ(irq, or_idx) \
> +    (R_VERSAL_IRQ_ORED_MASK | ((or_idx) << R_VERSAL_IRQ_OR_IDX_SHIFT) | (irq))
> +
>  typedef struct VersalSimplePeriphMap {
>      uint64_t addr;
>      int irq;
>  } VersalSimplePeriphMap;
>  
> @@ -172,13 +183,56 @@ static inline Object *versal_get_child_idx(Versal *s, const char *child,
>      g_autofree char *n = g_strdup_printf("%s[%zu]", child, idx);
>  
>      return versal_get_child(s, n);
>  }
>  
> +/*
> + * When the R_VERSAL_IRQ_ORED flag is set on an IRQ descriptor, this function is
> + * used to return the corresponding or gate input IRQ. The or gate is created if
> + * not already existant.
> + *
> + * Or gates are placed under the /soc/irq-or-gates QOM container.
> + */
> +static qemu_irq versal_get_irq_or_gate_in(Versal *s, int irq_idx,
> +                                          qemu_irq target_irq)
> +{
> +    Object *container = versal_get_child(s, "irq-or-gates");
> +    DeviceState *dev;
> +    g_autofree char *name;
> +    int idx, or_idx;
> +
> +    idx = FIELD_EX32(irq_idx, VERSAL_IRQ, IRQ);
> +    or_idx = FIELD_EX32(irq_idx, VERSAL_IRQ, OR_IDX);
> +
> +    name = g_strdup_printf("irq[%d]", idx);
> +    dev = DEVICE(object_resolve_path_at(container, name));
> +
> +    if (dev == NULL) {
> +        dev = qdev_new(TYPE_OR_IRQ);
> +        object_property_add_child(container, name, OBJECT(dev));
> +        qdev_prop_set_uint16(dev, "num-lines", 1 << R_VERSAL_IRQ_OR_IDX_LENGTH);
> +        qdev_realize_and_unref(dev, NULL, &error_abort);
> +        qdev_connect_gpio_out(dev, 0, target_irq);
> +    }
> +
> +    return qdev_get_gpio_in(dev, or_idx);
> +}
> +
>  static qemu_irq versal_get_irq(Versal *s, int irq_idx)
>  {
> -    return qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), irq_idx);
> +    qemu_irq irq;
> +    bool ored;
> +
> +    ored = FIELD_EX32(irq_idx, VERSAL_IRQ, ORED);
> +
> +    irq = qdev_get_gpio_in(DEVICE(&s->fpd.apu.gic), irq_idx);
> +
> +    if (ored) {
> +        irq = versal_get_irq_or_gate_in(s, irq_idx, irq);
> +    }
> +
> +    return irq;
>  }
>  
>  static void versal_sysbus_connect_irq(Versal *s, SysBusDevice *sbd,
>                                        int sbd_idx, int irq_idx)
>  {
> @@ -1209,10 +1263,11 @@ static uint32_t fdt_add_clk_node(Versal *s, const char *name,
>  
>  static void versal_realize(DeviceState *dev, Error **errp)
>  {
>      Versal *s = XLNX_VERSAL_BASE(dev);
>      qemu_irq pic[XLNX_VERSAL_NR_IRQS];
> +    Object *container;
>      const VersalMap *map = versal_get_map(s);
>      size_t i;
>  
>      if (s->cfg.fdt == NULL) {
>          int fdt_size;
> @@ -1223,10 +1278,15 @@ static void versal_realize(DeviceState *dev, Error **errp)
>      s->phandle.clk_25mhz = fdt_add_clk_node(s, "/clk25", 25 * 1000 * 1000);
>      s->phandle.clk_125mhz = fdt_add_clk_node(s, "/clk125", 125 * 1000 * 1000);
>  
>      versal_create_apu_cpus(s);
>      versal_create_apu_gic(s, pic);
> +
> +    container = object_new(TYPE_CONTAINER);
> +    object_property_add_child(OBJECT(s), "irq-or-gates", container);
> +    object_unref(container);
> +
>      versal_create_rpu_cpus(s);
>  
>      for (i = 0; i < map->num_uart; i++) {
>          versal_create_uart(s, &map->uart[i], i);
>      }
> -- 
> 2.50.0
>