[PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine

Ruslan Ruslichenko posted 33 patches 1 month, 3 weeks ago
Only 18 patches received!
There is a newer version of this series
[PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Add new '-hw-dtb' command-line option and corresponding
MachineState property.
The 'hw-dtb' option allows to specify a Device tree
binary with hardware description which Qemu should
emulate.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/machine.c        | 19 +++++++++++++++++++
 include/hw/core/boards.h |  1 +
 qemu-options.hx          |  9 +++++++++
 system/vl.c              |  3 +++
 4 files changed, 32 insertions(+)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index d4ef620c17..affd24cd49 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp)
     ms->dtb = g_strdup(value);
 }
 
+static char *machine_get_hw_dtb(Object *obj, Error **errp)
+{
+    MachineState *ms = MACHINE(obj);
+
+    return g_strdup(ms->hw_dtb);
+}
+
+static void machine_set_hw_dtb(Object *obj, const char *value, Error **errp)
+{
+    MachineState *ms = MACHINE(obj);
+
+    ms->hw_dtb = g_strdup(value);
+}
+
 static char *machine_get_dumpdtb(Object *obj, Error **errp)
 {
     MachineState *ms = MACHINE(obj);
@@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, const void *data)
     object_class_property_set_description(oc, "dtb",
         "Linux kernel device tree file");
 
+    object_class_property_add_str(oc, "hw-dtb",
+                            machine_get_hw_dtb, machine_set_hw_dtb);
+    object_class_property_set_description(oc, "hw-dtb",
+                "A device tree binary used to describe the hardware to QEMU");
+
     object_class_property_add_str(oc, "dumpdtb",
         machine_get_dumpdtb, machine_set_dumpdtb);
     object_class_property_set_description(oc, "dumpdtb",
diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h
index edbe8d03e5..722b79b5e4 100644
--- a/include/hw/core/boards.h
+++ b/include/hw/core/boards.h
@@ -403,6 +403,7 @@ struct MachineState {
 
     void *fdt;
     char *dtb;
+    char *hw_dtb;
     char *dumpdtb;
     int phandle_start;
     char *dt_compatible;
diff --git a/qemu-options.hx b/qemu-options.hx
index 33fcfe7ce6..1568f3ce4c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4518,6 +4518,15 @@ SRST(initrd)
 
 ERST
 
+DEF("hw-dtb", HAS_ARG, QEMU_OPTION_hw_dtb, \
+    "-hw-dtb file    use 'file' as device tree image\n", QEMU_ARCH_ALL)
+SRST
+``-hw-dtb file``
+    Use <file> as a device tree binary (dtb) image used to create the
+    emulated machine. This dtb will not be passed to the kernel, use -dtb
+    for that.
+ERST
+
 DEF("dtb", HAS_ARG, QEMU_OPTION_dtb, \
     "-dtb    file    use 'file' as device tree image\n", QEMU_ARCH_ALL)
 SRST
diff --git a/system/vl.c b/system/vl.c
index aa9a155041..c890da586c 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -3020,6 +3020,9 @@ void qemu_init(int argc, char **argv)
             case QEMU_OPTION_dtb:
                 qdict_put_str(machine_opts_dict, "dtb", optarg);
                 break;
+            case QEMU_OPTION_hw_dtb:
+                qdict_put_str(machine_opts_dict, "hw-dtb", optarg);
+                break;
             case QEMU_OPTION_cdrom:
                 drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS);
                 break;
-- 
2.43.0
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by Thomas Huth 1 month, 2 weeks ago
  Hi!

On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> 
> Add new '-hw-dtb' command-line option and corresponding
> MachineState property.
> The 'hw-dtb' option allows to specify a Device tree
> binary with hardware description which Qemu should
> emulate.
> 
> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> ---
>   hw/core/machine.c        | 19 +++++++++++++++++++
>   include/hw/core/boards.h |  1 +
>   qemu-options.hx          |  9 +++++++++
>   system/vl.c              |  3 +++
>   4 files changed, 32 insertions(+)
> 
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index d4ef620c17..affd24cd49 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp)
>       ms->dtb = g_strdup(value);
>   }
>   
> +static char *machine_get_hw_dtb(Object *obj, Error **errp)
> +{
> +    MachineState *ms = MACHINE(obj);
> +
> +    return g_strdup(ms->hw_dtb);
> +}
> +
> +static void machine_set_hw_dtb(Object *obj, const char *value, Error **errp)
> +{
> +    MachineState *ms = MACHINE(obj);
> +
> +    ms->hw_dtb = g_strdup(value);
> +}
> +
>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
>   {
>       MachineState *ms = MACHINE(obj);
> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, const void *data)
>       object_class_property_set_description(oc, "dtb",
>           "Linux kernel device tree file");
>   
> +    object_class_property_add_str(oc, "hw-dtb",
> +                            machine_get_hw_dtb, machine_set_hw_dtb);
> +    object_class_property_set_description(oc, "hw-dtb",
> +                "A device tree binary used to describe the hardware to QEMU");

Do the hardware dtbs differ from the ones that are passed to the Linux 
kernel? If not, I think you likely could use the existing "dtb" property for 
your new feature?

...
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 33fcfe7ce6..1568f3ce4c 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -4518,6 +4518,15 @@ SRST(initrd)
>   
>   ERST
>   
> +DEF("hw-dtb", HAS_ARG, QEMU_OPTION_hw_dtb, \
> +    "-hw-dtb file    use 'file' as device tree image\n", QEMU_ARCH_ALL)
> +SRST
> +``-hw-dtb file``
> +    Use <file> as a device tree binary (dtb) image used to create the
> +    emulated machine. This dtb will not be passed to the kernel, use -dtb
> +    for that.
> +ERST

Please don't add any new top level options if it is not really necessary. We 
already got too much duplicated logic in QEMU, so we try to avoid this 
nowadays. In your case here, it should be fine to simply configure the 
hw-dtb via the -machine property.

  Thanks,
   Thomas
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by Alex Bennée 1 month, 2 weeks ago
Thomas Huth <thuth@redhat.com> writes:

>  Hi!
>
> On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>> Add new '-hw-dtb' command-line option and corresponding
>> MachineState property.
>> The 'hw-dtb' option allows to specify a Device tree
>> binary with hardware description which Qemu should
>> emulate.
>> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>> ---
>>   hw/core/machine.c        | 19 +++++++++++++++++++
>>   include/hw/core/boards.h |  1 +
>>   qemu-options.hx          |  9 +++++++++
>>   system/vl.c              |  3 +++
>>   4 files changed, 32 insertions(+)
>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>> index d4ef620c17..affd24cd49 100644
>> --- a/hw/core/machine.c
>> +++ b/hw/core/machine.c
>> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp)
>>       ms->dtb = g_strdup(value);
>>   }
>>   +static char *machine_get_hw_dtb(Object *obj, Error **errp)
>> +{
>> +    MachineState *ms = MACHINE(obj);
>> +
>> +    return g_strdup(ms->hw_dtb);
>> +}
>> +
>> +static void machine_set_hw_dtb(Object *obj, const char *value, Error **errp)
>> +{
>> +    MachineState *ms = MACHINE(obj);
>> +
>> +    ms->hw_dtb = g_strdup(value);
>> +}
>> +
>>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
>>   {
>>       MachineState *ms = MACHINE(obj);
>> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, const void *data)
>>       object_class_property_set_description(oc, "dtb",
>>           "Linux kernel device tree file");
>>   +    object_class_property_add_str(oc, "hw-dtb",
>> +                            machine_get_hw_dtb, machine_set_hw_dtb);
>> +    object_class_property_set_description(oc, "hw-dtb",
>> +                "A device tree binary used to describe the hardware to QEMU");
>
> Do the hardware dtbs differ from the ones that are passed to the Linux
> kernel? If not, I think you likely could use the existing "dtb"
> property for your new feature?

They have to - the normal DTB cannot define the whole system because its
only concerned with reporting what the kernel can see to the kernel and
not defining the whole system including what the firmware sees.

>
> ...
>> diff --git a/qemu-options.hx b/qemu-options.hx
>> index 33fcfe7ce6..1568f3ce4c 100644
>> --- a/qemu-options.hx
>> +++ b/qemu-options.hx
>> @@ -4518,6 +4518,15 @@ SRST(initrd)
>>     ERST
>>   +DEF("hw-dtb", HAS_ARG, QEMU_OPTION_hw_dtb, \
>> +    "-hw-dtb file    use 'file' as device tree image\n", QEMU_ARCH_ALL)
>> +SRST
>> +``-hw-dtb file``
>> +    Use <file> as a device tree binary (dtb) image used to create the
>> +    emulated machine. This dtb will not be passed to the kernel, use -dtb
>> +    for that.
>> +ERST
>
> Please don't add any new top level options if it is not really
> necessary. We already got too much duplicated logic in QEMU, so we try
> to avoid this nowadays. In your case here, it should be fine to simply
> configure the hw-dtb via the -machine property.
>
>  Thanks,
>   Thomas

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by Ruslan Ruslichenko 1 month, 2 weeks ago
On Mon, Feb 23, 2026 at 2:23 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> Thomas Huth <thuth@redhat.com> writes:
>
> >  Hi!
> >
> > On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
> >> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >> Add new '-hw-dtb' command-line option and corresponding
> >> MachineState property.
> >> The 'hw-dtb' option allows to specify a Device tree
> >> binary with hardware description which Qemu should
> >> emulate.
> >> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >> ---
> >>   hw/core/machine.c        | 19 +++++++++++++++++++
> >>   include/hw/core/boards.h |  1 +
> >>   qemu-options.hx          |  9 +++++++++
> >>   system/vl.c              |  3 +++
> >>   4 files changed, 32 insertions(+)
> >> diff --git a/hw/core/machine.c b/hw/core/machine.c
> >> index d4ef620c17..affd24cd49 100644
> >> --- a/hw/core/machine.c
> >> +++ b/hw/core/machine.c
> >> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp)
> >>       ms->dtb = g_strdup(value);
> >>   }
> >>   +static char *machine_get_hw_dtb(Object *obj, Error **errp)
> >> +{
> >> +    MachineState *ms = MACHINE(obj);
> >> +
> >> +    return g_strdup(ms->hw_dtb);
> >> +}
> >> +
> >> +static void machine_set_hw_dtb(Object *obj, const char *value, Error **errp)
> >> +{
> >> +    MachineState *ms = MACHINE(obj);
> >> +
> >> +    ms->hw_dtb = g_strdup(value);
> >> +}
> >> +
> >>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
> >>   {
> >>       MachineState *ms = MACHINE(obj);
> >> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, const void *data)
> >>       object_class_property_set_description(oc, "dtb",
> >>           "Linux kernel device tree file");
> >>   +    object_class_property_add_str(oc, "hw-dtb",
> >> +                            machine_get_hw_dtb, machine_set_hw_dtb);
> >> +    object_class_property_set_description(oc, "hw-dtb",
> >> +                "A device tree binary used to describe the hardware to QEMU");
> >
> > Do the hardware dtbs differ from the ones that are passed to the Linux
> > kernel? If not, I think you likely could use the existing "dtb"
> > property for your new feature?
>
> They have to - the normal DTB cannot define the whole system because its
> only concerned with reporting what the kernel can see to the kernel and
> not defining the whole system including what the firmware sees.
>

Yes, Indeed.
Normal DTB was explored, but there is not enough information to create
QEMU models from it.
Thus we need to have a separate device tree.

> >
> > ...
> >> diff --git a/qemu-options.hx b/qemu-options.hx
> >> index 33fcfe7ce6..1568f3ce4c 100644
> >> --- a/qemu-options.hx
> >> +++ b/qemu-options.hx
> >> @@ -4518,6 +4518,15 @@ SRST(initrd)
> >>     ERST
> >>   +DEF("hw-dtb", HAS_ARG, QEMU_OPTION_hw_dtb, \
> >> +    "-hw-dtb file    use 'file' as device tree image\n", QEMU_ARCH_ALL)
> >> +SRST
> >> +``-hw-dtb file``
> >> +    Use <file> as a device tree binary (dtb) image used to create the
> >> +    emulated machine. This dtb will not be passed to the kernel, use -dtb
> >> +    for that.
> >> +ERST
> >
> > Please don't add any new top level options if it is not really
> > necessary. We already got too much duplicated logic in QEMU, so we try
> > to avoid this nowadays. In your case here, it should be fine to simply
> > configure the hw-dtb via the -machine property.

Agreed. As for now there is only one user of this hw option, we can move to the
arm-generic-fdt machine option itself. I'll move this for the next version.

> >
> >  Thanks,
> >   Thomas
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro


--
BR,
Ruslan Ruslichenko
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by BALATON Zoltan 1 month, 2 weeks ago
On Mon, 23 Feb 2026, Ruslan Ruslichenko wrote:
> On Mon, Feb 23, 2026 at 2:23 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> Thomas Huth <thuth@redhat.com> writes:
>>
>>>  Hi!
>>>
>>> On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>> Add new '-hw-dtb' command-line option and corresponding
>>>> MachineState property.
>>>> The 'hw-dtb' option allows to specify a Device tree
>>>> binary with hardware description which Qemu should
>>>> emulate.
>>>> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>> ---
>>>>   hw/core/machine.c        | 19 +++++++++++++++++++
>>>>   include/hw/core/boards.h |  1 +
>>>>   qemu-options.hx          |  9 +++++++++
>>>>   system/vl.c              |  3 +++
>>>>   4 files changed, 32 insertions(+)
>>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>>>> index d4ef620c17..affd24cd49 100644
>>>> --- a/hw/core/machine.c
>>>> +++ b/hw/core/machine.c
>>>> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp)
>>>>       ms->dtb = g_strdup(value);
>>>>   }
>>>>   +static char *machine_get_hw_dtb(Object *obj, Error **errp)
>>>> +{
>>>> +    MachineState *ms = MACHINE(obj);
>>>> +
>>>> +    return g_strdup(ms->hw_dtb);
>>>> +}
>>>> +
>>>> +static void machine_set_hw_dtb(Object *obj, const char *value, Error **errp)
>>>> +{
>>>> +    MachineState *ms = MACHINE(obj);
>>>> +
>>>> +    ms->hw_dtb = g_strdup(value);
>>>> +}
>>>> +
>>>>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
>>>>   {
>>>>       MachineState *ms = MACHINE(obj);
>>>> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, const void *data)
>>>>       object_class_property_set_description(oc, "dtb",
>>>>           "Linux kernel device tree file");
>>>>   +    object_class_property_add_str(oc, "hw-dtb",
>>>> +                            machine_get_hw_dtb, machine_set_hw_dtb);
>>>> +    object_class_property_set_description(oc, "hw-dtb",
>>>> +                "A device tree binary used to describe the hardware to QEMU");
>>>
>>> Do the hardware dtbs differ from the ones that are passed to the Linux
>>> kernel? If not, I think you likely could use the existing "dtb"
>>> property for your new feature?
>>
>> They have to - the normal DTB cannot define the whole system because its
>> only concerned with reporting what the kernel can see to the kernel and
>> not defining the whole system including what the firmware sees.
>>
>
> Yes, Indeed.
> Normal DTB was explored, but there is not enough information to create
> QEMU models from it.
> Thus we need to have a separate device tree.

What additional data is needed that's not present in dtb from firmware and 
what if that additional data is passed to the kernel? I think the kernel 
would just ignore the additional data that it doesn't need so one dtb 
might be enough even if it has to be more detailed than what the kernel 
normally gets. If that works we don't need a separate option.

This series seems to be more complex than Bernhard's e500-fdt experiment 
(I've linked to that in 
<https://lists.nongnu.org/archive/html/qemu-devel/2026-01/msg05470.html>) 
Bernhard's patch only added minimal support for creating devices from fdt 
and then put additional logic in the devices instead of handling them in 
generic code. Would such an approach lead to simpler fdt handling? If not 
why is the additional complexity needed?

Regards,
BALATON Zoltan
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by Ruslan Ruslichenko 1 month, 2 weeks ago
On Tue, Feb 24, 2026 at 12:55 AM BALATON Zoltan <balaton@eik.bme.hu> wrote:
>
> On Mon, 23 Feb 2026, Ruslan Ruslichenko wrote:
> > On Mon, Feb 23, 2026 at 2:23 PM Alex Bennée <alex.bennee@linaro.org> wrote:
> >>
> >> Thomas Huth <thuth@redhat.com> writes:
> >>
> >>>  Hi!
> >>>
> >>> On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
> >>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >>>> Add new '-hw-dtb' command-line option and corresponding
> >>>> MachineState property.
> >>>> The 'hw-dtb' option allows to specify a Device tree
> >>>> binary with hardware description which Qemu should
> >>>> emulate.
> >>>> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >>>> ---
> >>>>   hw/core/machine.c        | 19 +++++++++++++++++++
> >>>>   include/hw/core/boards.h |  1 +
> >>>>   qemu-options.hx          |  9 +++++++++
> >>>>   system/vl.c              |  3 +++
> >>>>   4 files changed, 32 insertions(+)
> >>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
> >>>> index d4ef620c17..affd24cd49 100644
> >>>> --- a/hw/core/machine.c
> >>>> +++ b/hw/core/machine.c
> >>>> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp)
> >>>>       ms->dtb = g_strdup(value);
> >>>>   }
> >>>>   +static char *machine_get_hw_dtb(Object *obj, Error **errp)
> >>>> +{
> >>>> +    MachineState *ms = MACHINE(obj);
> >>>> +
> >>>> +    return g_strdup(ms->hw_dtb);
> >>>> +}
> >>>> +
> >>>> +static void machine_set_hw_dtb(Object *obj, const char *value, Error **errp)
> >>>> +{
> >>>> +    MachineState *ms = MACHINE(obj);
> >>>> +
> >>>> +    ms->hw_dtb = g_strdup(value);
> >>>> +}
> >>>> +
> >>>>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
> >>>>   {
> >>>>       MachineState *ms = MACHINE(obj);
> >>>> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, const void *data)
> >>>>       object_class_property_set_description(oc, "dtb",
> >>>>           "Linux kernel device tree file");
> >>>>   +    object_class_property_add_str(oc, "hw-dtb",
> >>>> +                            machine_get_hw_dtb, machine_set_hw_dtb);
> >>>> +    object_class_property_set_description(oc, "hw-dtb",
> >>>> +                "A device tree binary used to describe the hardware to QEMU");
> >>>
> >>> Do the hardware dtbs differ from the ones that are passed to the Linux
> >>> kernel? If not, I think you likely could use the existing "dtb"
> >>> property for your new feature?
> >>
> >> They have to - the normal DTB cannot define the whole system because its
> >> only concerned with reporting what the kernel can see to the kernel and
> >> not defining the whole system including what the firmware sees.
> >>
> >
> > Yes, Indeed.
> > Normal DTB was explored, but there is not enough information to create
> > QEMU models from it.
> > Thus we need to have a separate device tree.
>
> What additional data is needed that's not present in dtb from firmware and
> what if that additional data is passed to the kernel? I think the kernel
> would just ignore the additional data that it doesn't need so one dtb
> might be enough even if it has to be more detailed than what the kernel
> normally gets. If that works we don't need a separate option.
>

Here are few specific examples:

1. DTS compatibles and QOM type names are all different.
Using Linux device tree would require to maintain some massive hardcoded mapping
even for devices that otherwise don't require any special handling.

2. Object properties. We would probably need to make sure there is no conflicts
and all properties are either ignored by Linux drivers or have exactly
the same meaning.

3. Devices mapped to Secure Firmwares only. Those are not accessible by Linux
and trying to initialize them from the Linux kernel would cause memory aborts.

4. Connecting devices to backends such as serial, block devices, etc.
We need to specify corresponding 'chardev' and 'driver' properties.

5. Interrupt wiring. Some hardware lines are transparent to Linux, but need be
wired by QEMU. For example, interrupt controllers are wired to CPUs, etc.
If we would want to initialize it from dts eventually, this would need to be
passed somehow.

Overall, my understanding is Device models typically need to support
booting existing
OS images and device trees from regular platform builds.
And at least due points 2-4, we cannot use them directly.
Since we still need to modify the device tree for QEMU anyway, having support
for unified DTS doesn't really make sense.

> This series seems to be more complex than Bernhard's e500-fdt experiment
> (I've linked to that in
> <https://lists.nongnu.org/archive/html/qemu-devel/2026-01/msg05470.html>)
> Bernhard's patch only added minimal support for creating devices from fdt
> and then put additional logic in the devices instead of handling them in
> generic code. Would such an approach lead to simpler fdt handling? If not
> why is the additional complexity needed?
>

Most devices can be created from generic code. Supporting one common
approach to initialize some specific device class would be simpler and more
maintainable than writing a custom FDT handler logic within each device model.


> Regards,
> BALATON Zoltan

--
BR,
Ruslan Ruslichenko
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by BALATON Zoltan 1 month, 2 weeks ago
On Tue, 24 Feb 2026, Ruslan Ruslichenko wrote:
> On Tue, Feb 24, 2026 at 12:55 AM BALATON Zoltan <balaton@eik.bme.hu> wrote:
>> On Mon, 23 Feb 2026, Ruslan Ruslichenko wrote:
>>> On Mon, Feb 23, 2026 at 2:23 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>>>>
>>>> Thomas Huth <thuth@redhat.com> writes:
>>>>
>>>>>  Hi!
>>>>>
>>>>> On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
>>>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>>>> Add new '-hw-dtb' command-line option and corresponding
>>>>>> MachineState property.
>>>>>> The 'hw-dtb' option allows to specify a Device tree
>>>>>> binary with hardware description which Qemu should
>>>>>> emulate.
>>>>>> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>>>> ---
>>>>>>   hw/core/machine.c        | 19 +++++++++++++++++++
>>>>>>   include/hw/core/boards.h |  1 +
>>>>>>   qemu-options.hx          |  9 +++++++++
>>>>>>   system/vl.c              |  3 +++
>>>>>>   4 files changed, 32 insertions(+)
>>>>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>>>>>> index d4ef620c17..affd24cd49 100644
>>>>>> --- a/hw/core/machine.c
>>>>>> +++ b/hw/core/machine.c
>>>>>> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const char *value, Error **errp)
>>>>>>       ms->dtb = g_strdup(value);
>>>>>>   }
>>>>>>   +static char *machine_get_hw_dtb(Object *obj, Error **errp)
>>>>>> +{
>>>>>> +    MachineState *ms = MACHINE(obj);
>>>>>> +
>>>>>> +    return g_strdup(ms->hw_dtb);
>>>>>> +}
>>>>>> +
>>>>>> +static void machine_set_hw_dtb(Object *obj, const char *value, Error **errp)
>>>>>> +{
>>>>>> +    MachineState *ms = MACHINE(obj);
>>>>>> +
>>>>>> +    ms->hw_dtb = g_strdup(value);
>>>>>> +}
>>>>>> +
>>>>>>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
>>>>>>   {
>>>>>>       MachineState *ms = MACHINE(obj);
>>>>>> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, const void *data)
>>>>>>       object_class_property_set_description(oc, "dtb",
>>>>>>           "Linux kernel device tree file");
>>>>>>   +    object_class_property_add_str(oc, "hw-dtb",
>>>>>> +                            machine_get_hw_dtb, machine_set_hw_dtb);
>>>>>> +    object_class_property_set_description(oc, "hw-dtb",
>>>>>> +                "A device tree binary used to describe the hardware to QEMU");
>>>>>
>>>>> Do the hardware dtbs differ from the ones that are passed to the Linux
>>>>> kernel? If not, I think you likely could use the existing "dtb"
>>>>> property for your new feature?
>>>>
>>>> They have to - the normal DTB cannot define the whole system because its
>>>> only concerned with reporting what the kernel can see to the kernel and
>>>> not defining the whole system including what the firmware sees.
>>>>
>>>
>>> Yes, Indeed.
>>> Normal DTB was explored, but there is not enough information to create
>>> QEMU models from it.
>>> Thus we need to have a separate device tree.
>>
>> What additional data is needed that's not present in dtb from firmware and
>> what if that additional data is passed to the kernel? I think the kernel
>> would just ignore the additional data that it doesn't need so one dtb
>> might be enough even if it has to be more detailed than what the kernel
>> normally gets. If that works we don't need a separate option.
>>
>
> Here are few specific examples:
>
> 1. DTS compatibles and QOM type names are all different.
> Using Linux device tree would require to maintain some massive hardcoded mapping
> even for devices that otherwise don't require any special handling.

Bernhard's patches had the same issue and solved it by adding subclasses 
with names matching the dtb. I think that would not work but a better 
approach is to allow alias names for devices (we already have machine name 
aliases I thought something similar for devices) that would solve this 
issue without too much additional complexity. Then the device would just 
need an additional line registering an alias name to match the dtb name 
(or more of different machines have different compatible names).

> 2. Object properties. We would probably need to make sure there is no conflicts
> and all properties are either ignored by Linux drivers or have exactly
> the same meaning.
>
> 3. Devices mapped to Secure Firmwares only. Those are not accessible by Linux
> and trying to initialize them from the Linux kernel would cause memory aborts.
>
> 4. Connecting devices to backends such as serial, block devices, etc.
> We need to specify corresponding 'chardev' and 'driver' properties.

This is probably not part of the machine but a specific invocation so 
these should be specified on the command line and not in the machine 
definion.

> 5. Interrupt wiring. Some hardware lines are transparent to Linux, but need be
> wired by QEMU. For example, interrupt controllers are wired to CPUs, etc.
> If we would want to initialize it from dts eventually, this would need to be
> passed somehow.
>
> Overall, my understanding is Device models typically need to support
> booting existing
> OS images and device trees from regular platform builds.
> And at least due points 2-4, we cannot use them directly.

Some of the points may be solved other than modifying dtb but some may 
indeed need extensions so a separate hw-dtb may be needed in more complex 
cases.

> Since we still need to modify the device tree for QEMU anyway, having support
> for unified DTS doesn't really make sense.

However it may be worthwile to try keeping the modifications minimal so 
for simple machines like the e500-ppc that has no secure world or other 
things not described by the normal dtb it could just use the regular dtb 
and the hw-dtb option would be optional for more complex machines that 
need it.

>> This series seems to be more complex than Bernhard's e500-fdt experiment
>> (I've linked to that in
>> <https://lists.nongnu.org/archive/html/qemu-devel/2026-01/msg05470.html>)
>> Bernhard's patch only added minimal support for creating devices from fdt
>> and then put additional logic in the devices instead of handling them in
>> generic code. Would such an approach lead to simpler fdt handling? If not
>> why is the additional complexity needed?
>
> Most devices can be created from generic code. Supporting one common
> approach to initialize some specific device class would be simpler and more
> maintainable than writing a custom FDT handler logic within each device model.

I think putting device specific knowledge in generic code is against the 
object oriented design of QEMU and would be less maintainable because it 
would not belong to the device maintainers that way. If you have common 
code for some device classes then those devices probably have a superclass 
where this code could be put. The problem of mapping property names to 
device specific properties could also be done easier within a device model 
which has better knowledge of the device and then we would not need a 
global table mapping these. So while at first look it may seem more 
fragmented putting some logic in devices instead of a central place it's 
better fits the object model and groups device specific logic in the 
device model which is more maintainable than a global mapping that does 
not belong to any device but touches several unrelated devices.

Regards,
BALATON Zoltan
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by BALATON Zoltan 1 month, 2 weeks ago
On Tue, 24 Feb 2026, BALATON Zoltan wrote:
> On Tue, 24 Feb 2026, Ruslan Ruslichenko wrote:
>> On Tue, Feb 24, 2026 at 12:55 AM BALATON Zoltan <balaton@eik.bme.hu> wrote:
>>> On Mon, 23 Feb 2026, Ruslan Ruslichenko wrote:
>>>> On Mon, Feb 23, 2026 at 2:23 PM Alex Bennée <alex.bennee@linaro.org> 
>>>> wrote:
>>>>> 
>>>>> Thomas Huth <thuth@redhat.com> writes:
>>>>>
>>>>>>  Hi!
>>>>>> 
>>>>>> On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
>>>>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>>>>> Add new '-hw-dtb' command-line option and corresponding
>>>>>>> MachineState property.
>>>>>>> The 'hw-dtb' option allows to specify a Device tree
>>>>>>> binary with hardware description which Qemu should
>>>>>>> emulate.
>>>>>>> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>>>>> ---
>>>>>>>   hw/core/machine.c        | 19 +++++++++++++++++++
>>>>>>>   include/hw/core/boards.h |  1 +
>>>>>>>   qemu-options.hx          |  9 +++++++++
>>>>>>>   system/vl.c              |  3 +++
>>>>>>>   4 files changed, 32 insertions(+)
>>>>>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
>>>>>>> index d4ef620c17..affd24cd49 100644
>>>>>>> --- a/hw/core/machine.c
>>>>>>> +++ b/hw/core/machine.c
>>>>>>> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const 
>>>>>>> char *value, Error **errp)
>>>>>>>       ms->dtb = g_strdup(value);
>>>>>>>   }
>>>>>>>   +static char *machine_get_hw_dtb(Object *obj, Error **errp)
>>>>>>> +{
>>>>>>> +    MachineState *ms = MACHINE(obj);
>>>>>>> +
>>>>>>> +    return g_strdup(ms->hw_dtb);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void machine_set_hw_dtb(Object *obj, const char *value, Error 
>>>>>>> **errp)
>>>>>>> +{
>>>>>>> +    MachineState *ms = MACHINE(obj);
>>>>>>> +
>>>>>>> +    ms->hw_dtb = g_strdup(value);
>>>>>>> +}
>>>>>>> +
>>>>>>>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
>>>>>>>   {
>>>>>>>       MachineState *ms = MACHINE(obj);
>>>>>>> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc, 
>>>>>>> const void *data)
>>>>>>>       object_class_property_set_description(oc, "dtb",
>>>>>>>           "Linux kernel device tree file");
>>>>>>>   +    object_class_property_add_str(oc, "hw-dtb",
>>>>>>> +                            machine_get_hw_dtb, machine_set_hw_dtb);
>>>>>>> +    object_class_property_set_description(oc, "hw-dtb",
>>>>>>> +                "A device tree binary used to describe the hardware 
>>>>>>> to QEMU");
>>>>>> 
>>>>>> Do the hardware dtbs differ from the ones that are passed to the Linux
>>>>>> kernel? If not, I think you likely could use the existing "dtb"
>>>>>> property for your new feature?
>>>>> 
>>>>> They have to - the normal DTB cannot define the whole system because its
>>>>> only concerned with reporting what the kernel can see to the kernel and
>>>>> not defining the whole system including what the firmware sees.
>>>>> 
>>>> 
>>>> Yes, Indeed.
>>>> Normal DTB was explored, but there is not enough information to create
>>>> QEMU models from it.
>>>> Thus we need to have a separate device tree.
>>> 
>>> What additional data is needed that's not present in dtb from firmware and
>>> what if that additional data is passed to the kernel? I think the kernel
>>> would just ignore the additional data that it doesn't need so one dtb
>>> might be enough even if it has to be more detailed than what the kernel
>>> normally gets. If that works we don't need a separate option.
>>> 
>> 
>> Here are few specific examples:
>> 
>> 1. DTS compatibles and QOM type names are all different.
>> Using Linux device tree would require to maintain some massive hardcoded 
>> mapping
>> even for devices that otherwise don't require any special handling.
>
> Bernhard's patches had the same issue and solved it by adding subclasses with 
> names matching the dtb. I think that would not work but a better approach is 
> to allow alias names for devices (we already have machine name aliases I 
> thought something similar for devices) that would solve this issue without 
> too much additional complexity. Then the device would just need an additional 
> line registering an alias name to match the dtb name (or more of different 
> machines have different compatible names).
>
>> 2. Object properties. We would probably need to make sure there is no 
>> conflicts
>> and all properties are either ignored by Linux drivers or have exactly
>> the same meaning.
>> 
>> 3. Devices mapped to Secure Firmwares only. Those are not accessible by 
>> Linux
>> and trying to initialize them from the Linux kernel would cause memory 
>> aborts.
>> 
>> 4. Connecting devices to backends such as serial, block devices, etc.
>> We need to specify corresponding 'chardev' and 'driver' properties.
>
> This is probably not part of the machine but a specific invocation so these 
> should be specified on the command line and not in the machine definion.
>
>> 5. Interrupt wiring. Some hardware lines are transparent to Linux, but need 
>> be
>> wired by QEMU. For example, interrupt controllers are wired to CPUs, etc.
>> If we would want to initialize it from dts eventually, this would need to 
>> be
>> passed somehow.
>> 
>> Overall, my understanding is Device models typically need to support
>> booting existing
>> OS images and device trees from regular platform builds.
>> And at least due points 2-4, we cannot use them directly.
>
> Some of the points may be solved other than modifying dtb but some may indeed 
> need extensions so a separate hw-dtb may be needed in more complex cases.
>
>> Since we still need to modify the device tree for QEMU anyway, having 
>> support
>> for unified DTS doesn't really make sense.
>
> However it may be worthwile to try keeping the modifications minimal so for 
> simple machines like the e500-ppc that has no secure world or other things 
> not described by the normal dtb it could just use the regular dtb and the 
> hw-dtb option would be optional for more complex machines that need it.
>
>>> This series seems to be more complex than Bernhard's e500-fdt experiment
>>> (I've linked to that in
>>> <https://lists.nongnu.org/archive/html/qemu-devel/2026-01/msg05470.html>)
>>> Bernhard's patch only added minimal support for creating devices from fdt
>>> and then put additional logic in the devices instead of handling them in
>>> generic code. Would such an approach lead to simpler fdt handling? If not
>>> why is the additional complexity needed?
>> 
>> Most devices can be created from generic code. Supporting one common
>> approach to initialize some specific device class would be simpler and more
>> maintainable than writing a custom FDT handler logic within each device 
>> model.
>
> I think putting device specific knowledge in generic code is against the 
> object oriented design of QEMU and would be less maintainable because it 
> would not belong to the device maintainers that way. If you have common code 
> for some device classes then those devices probably have a superclass where 
> this code could be put. The problem of mapping property names to device 
> specific properties could also be done easier within a device model which has 
> better knowledge of the device and then we would not need a global table 
> mapping these. So while at first look it may seem more fragmented putting 
> some logic in devices instead of a central place it's better fits the object 
> model and groups device specific logic in the device model which is more 
> maintainable than a global mapping that does not belong to any device but 
> touches several unrelated devices.

It does not help that Google randomly refuses my emails saying it's spam 
if I cc it to more than one gmail user. See my reply above if you've 
missed the previous mail. Hopefully this gets through.

Regards,
BALATON Zoltan
Re: [PATCH v2 19/33] hw/core/machine: add '-hw-dtb' option for machine
Posted by Ruslan Ruslichenko 1 month, 2 weeks ago
On Wed, Feb 25, 2026 at 1:07 AM BALATON Zoltan <balaton@eik.bme.hu> wrote:
>
> On Tue, 24 Feb 2026, BALATON Zoltan wrote:
> > On Tue, 24 Feb 2026, Ruslan Ruslichenko wrote:
> >> On Tue, Feb 24, 2026 at 12:55 AM BALATON Zoltan <balaton@eik.bme.hu> wrote:
> >>> On Mon, 23 Feb 2026, Ruslan Ruslichenko wrote:
> >>>> On Mon, Feb 23, 2026 at 2:23 PM Alex Bennée <alex.bennee@linaro.org>
> >>>> wrote:
> >>>>>
> >>>>> Thomas Huth <thuth@redhat.com> writes:
> >>>>>
> >>>>>>  Hi!
> >>>>>>
> >>>>>> On 19/02/2026 15.48, Ruslan Ruslichenko wrote:
> >>>>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >>>>>>> Add new '-hw-dtb' command-line option and corresponding
> >>>>>>> MachineState property.
> >>>>>>> The 'hw-dtb' option allows to specify a Device tree
> >>>>>>> binary with hardware description which Qemu should
> >>>>>>> emulate.
> >>>>>>> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >>>>>>> ---
> >>>>>>>   hw/core/machine.c        | 19 +++++++++++++++++++
> >>>>>>>   include/hw/core/boards.h |  1 +
> >>>>>>>   qemu-options.hx          |  9 +++++++++
> >>>>>>>   system/vl.c              |  3 +++
> >>>>>>>   4 files changed, 32 insertions(+)
> >>>>>>> diff --git a/hw/core/machine.c b/hw/core/machine.c
> >>>>>>> index d4ef620c17..affd24cd49 100644
> >>>>>>> --- a/hw/core/machine.c
> >>>>>>> +++ b/hw/core/machine.c
> >>>>>>> @@ -363,6 +363,20 @@ static void machine_set_dtb(Object *obj, const
> >>>>>>> char *value, Error **errp)
> >>>>>>>       ms->dtb = g_strdup(value);
> >>>>>>>   }
> >>>>>>>   +static char *machine_get_hw_dtb(Object *obj, Error **errp)
> >>>>>>> +{
> >>>>>>> +    MachineState *ms = MACHINE(obj);
> >>>>>>> +
> >>>>>>> +    return g_strdup(ms->hw_dtb);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void machine_set_hw_dtb(Object *obj, const char *value, Error
> >>>>>>> **errp)
> >>>>>>> +{
> >>>>>>> +    MachineState *ms = MACHINE(obj);
> >>>>>>> +
> >>>>>>> +    ms->hw_dtb = g_strdup(value);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>>   static char *machine_get_dumpdtb(Object *obj, Error **errp)
> >>>>>>>   {
> >>>>>>>       MachineState *ms = MACHINE(obj);
> >>>>>>> @@ -1145,6 +1159,11 @@ static void machine_class_init(ObjectClass *oc,
> >>>>>>> const void *data)
> >>>>>>>       object_class_property_set_description(oc, "dtb",
> >>>>>>>           "Linux kernel device tree file");
> >>>>>>>   +    object_class_property_add_str(oc, "hw-dtb",
> >>>>>>> +                            machine_get_hw_dtb, machine_set_hw_dtb);
> >>>>>>> +    object_class_property_set_description(oc, "hw-dtb",
> >>>>>>> +                "A device tree binary used to describe the hardware
> >>>>>>> to QEMU");
> >>>>>>
> >>>>>> Do the hardware dtbs differ from the ones that are passed to the Linux
> >>>>>> kernel? If not, I think you likely could use the existing "dtb"
> >>>>>> property for your new feature?
> >>>>>
> >>>>> They have to - the normal DTB cannot define the whole system because its
> >>>>> only concerned with reporting what the kernel can see to the kernel and
> >>>>> not defining the whole system including what the firmware sees.
> >>>>>
> >>>>
> >>>> Yes, Indeed.
> >>>> Normal DTB was explored, but there is not enough information to create
> >>>> QEMU models from it.
> >>>> Thus we need to have a separate device tree.
> >>>
> >>> What additional data is needed that's not present in dtb from firmware and
> >>> what if that additional data is passed to the kernel? I think the kernel
> >>> would just ignore the additional data that it doesn't need so one dtb
> >>> might be enough even if it has to be more detailed than what the kernel
> >>> normally gets. If that works we don't need a separate option.
> >>>
> >>
> >> Here are few specific examples:
> >>
> >> 1. DTS compatibles and QOM type names are all different.
> >> Using Linux device tree would require to maintain some massive hardcoded
> >> mapping
> >> even for devices that otherwise don't require any special handling.
> >
> > Bernhard's patches had the same issue and solved it by adding subclasses with
> > names matching the dtb. I think that would not work but a better approach is
> > to allow alias names for devices (we already have machine name aliases I
> > thought something similar for devices) that would solve this issue without
> > too much additional complexity. Then the device would just need an additional
> > line registering an alias name to match the dtb name (or more of different
> > machines have different compatible names).
> >
> >> 2. Object properties. We would probably need to make sure there is no
> >> conflicts
> >> and all properties are either ignored by Linux drivers or have exactly
> >> the same meaning.
> >>
> >> 3. Devices mapped to Secure Firmwares only. Those are not accessible by
> >> Linux
> >> and trying to initialize them from the Linux kernel would cause memory
> >> aborts.
> >>
> >> 4. Connecting devices to backends such as serial, block devices, etc.
> >> We need to specify corresponding 'chardev' and 'driver' properties.
> >
> > This is probably not part of the machine but a specific invocation so these
> > should be specified on the command line and not in the machine definion.
> >
> >> 5. Interrupt wiring. Some hardware lines are transparent to Linux, but need
> >> be
> >> wired by QEMU. For example, interrupt controllers are wired to CPUs, etc.
> >> If we would want to initialize it from dts eventually, this would need to
> >> be
> >> passed somehow.
> >>
> >> Overall, my understanding is Device models typically need to support
> >> booting existing
> >> OS images and device trees from regular platform builds.
> >> And at least due points 2-4, we cannot use them directly.
> >
> > Some of the points may be solved other than modifying dtb but some may indeed
> > need extensions so a separate hw-dtb may be needed in more complex cases.
> >
> >> Since we still need to modify the device tree for QEMU anyway, having
> >> support
> >> for unified DTS doesn't really make sense.
> >
> > However it may be worthwile to try keeping the modifications minimal so for
> > simple machines like the e500-ppc that has no secure world or other things
> > not described by the normal dtb it could just use the regular dtb and the
> > hw-dtb option would be optional for more complex machines that need it.

In fact, machine code now decides which device tree binary to pass to
the generic fdt parser.
So e500-ppc can indeed use a normal dtb instead and if corresponding
aliases are registered
then the machine should be created with the fdt_generic framework too.
However, for more complex cases we would indeed need HW dtb.

> >
> >>> This series seems to be more complex than Bernhard's e500-fdt experiment
> >>> (I've linked to that in
> >>> <https://lists.nongnu.org/archive/html/qemu-devel/2026-01/msg05470.html>)
> >>> Bernhard's patch only added minimal support for creating devices from fdt
> >>> and then put additional logic in the devices instead of handling them in
> >>> generic code. Would such an approach lead to simpler fdt handling? If not
> >>> why is the additional complexity needed?
> >>
> >> Most devices can be created from generic code. Supporting one common
> >> approach to initialize some specific device class would be simpler and more
> >> maintainable than writing a custom FDT handler logic within each device
> >> model.
> >
> > I think putting device specific knowledge in generic code is against the
> > object oriented design of QEMU and would be less maintainable because it
> > would not belong to the device maintainers that way. If you have common code
> > for some device classes then those devices probably have a superclass where
> > this code could be put. The problem of mapping property names to device
> > specific properties could also be done easier within a device model which has
> > better knowledge of the device and then we would not need a global table
> > mapping these. So while at first look it may seem more fragmented putting
> > some logic in devices instead of a central place it's better fits the object
> > model and groups device specific logic in the device model which is more
> > maintainable than a global mapping that does not belong to any device but
> > touches several unrelated devices.
>

Currently, there are only a few special cases for serial, blockdev type devices
to set up their properties, but no awareness about any device class. Even this
may probably be improved to be more generic in future too.
All other properties are set without any specific mapping to device class,
so the parsing logic is generic.
The complexity exists because fdt_generic handles the logic for
machine construction,
such as creating custom memory regions, mapping devices to these regions, and
parsing irq-maps.
Specifically, this generic memory mapping logic was required to instantiate
secure devices and create the correct address space mapping for the PCIe bus
in order to build the sbsa-ref example machine.


> It does not help that Google randomly refuses my emails saying it's spam
> if I cc it to more than one gmail user. See my reply above if you've
> missed the previous mail. Hopefully this gets through.
>
> Regards,
> BALATON Zoltan

--
BR,
Ruslan Ruslichenko
[PATCH v2 20/33] hw/arm: add generic ARM machine initialized by FDT
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

This patch introduces new ARM machine model, which is
fully instantiated from a Device Tree description.

This model uses fdt_generic framework to dynamically
construct system topology based on provided DTB, instead
of standard machines which rely on hardcoded models in
C files.

This allows to contruct machines with custom memory maps,
device structures without the need to modify and re-build
QEMU sources each time.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/arm/arm_generic_fdt.c | 180 +++++++++++++++++++++++++++++++++++++++
 hw/arm/meson.build       |   2 +
 2 files changed, 182 insertions(+)
 create mode 100644 hw/arm/arm_generic_fdt.c

diff --git a/hw/arm/arm_generic_fdt.c b/hw/arm/arm_generic_fdt.c
new file mode 100644
index 0000000000..49206269aa
--- /dev/null
+++ b/hw/arm/arm_generic_fdt.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2012 Xilinx. Inc
+ * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@xilinx.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "hw/core/boards.h"
+#include "hw/core/loader.h"
+#include "hw/core/hw-error.h"
+#include "qapi/error.h"
+#include "hw/block/flash.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "system/system.h"
+#include "system/qtest.h"
+#include "hw/arm/boot.h"
+#include "hw/arm/machines-qom.h"
+
+#include <libfdt.h>
+#include "hw/core/fdt_generic_util.h"
+
+#define GENERAL_MACHINE_NAME "arm-generic-fdt"
+
+#define QTEST_RUNNING (qtest_enabled() && qtest_driver())
+
+#define SMP_BOOT_ADDR 0xfffffff0
+/* Meaningless, but keeps arm boot happy */
+#define SMP_BOOTREG_ADDR 0xfffffffc
+
+static struct arm_boot_info arm_generic_fdt_binfo = {};
+
+typedef struct {
+    ram_addr_t ram_kernel_base;
+    ram_addr_t ram_kernel_size;
+} memory_info;
+
+typedef struct {
+    int fdt_size;
+    char *dtb;
+} FdtBlob;
+
+FdtBlob sw_fdt;
+
+static memory_info init_memory(void *fdt, ram_addr_t ram_size)
+{
+    FDTMachineInfo *fdti;
+    MemoryRegion *mem_area;
+    memory_info kernel_info;
+    Error *errp = NULL;
+    char **node_path;
+
+    /* Find a memory node or add new one if needed */
+    node_path = qemu_fdt_node_unit_path(fdt, "memory", &errp);
+    if (errp) {
+        error_report_err(errp);
+        exit(1);
+    }
+    if (!g_str_has_prefix(node_path[0], "/memory")) {
+        error_report("Failed to find /memory node");
+        exit(1);
+    }
+
+    /* Instantiate peripherals from the FDT.  */
+    fdti = fdt_generic_create_machine(fdt, NULL);
+
+    mem_area = MEMORY_REGION(object_resolve_path(node_path[0], NULL));
+
+    kernel_info.ram_kernel_base = object_property_get_int(OBJECT(mem_area),
+                                                            "addr", NULL);
+
+    kernel_info.ram_kernel_size = object_property_get_int(OBJECT(mem_area),
+                                                          "size", NULL);
+
+    if (kernel_info.ram_kernel_size == -1) {
+        kernel_info.ram_kernel_size = ram_size;
+    }
+
+    fdt_init_destroy_fdti(fdti);
+    g_strfreev(node_path);
+
+    return kernel_info;
+}
+
+static void *get_ref_dtb(const struct arm_boot_info *binfo, int *fdt_size)
+{
+    *fdt_size = sw_fdt.fdt_size;
+    return sw_fdt.dtb;
+}
+
+static void arm_generic_fdt_init(MachineState *machine)
+{
+    void *fdt = NULL;
+    int fdt_size;
+    const char *hw_dtb_arg, *dtb_arg;
+    memory_info kernel_info;
+
+    dtb_arg = machine->dtb;
+    hw_dtb_arg = machine->hw_dtb;
+    if (!dtb_arg && !hw_dtb_arg) {
+        if (!QTEST_RUNNING) {
+            /*
+             * Just return without error if running qtest, as we never have a
+             * device tree
+             */
+            hw_error("DTB must be specified for %s machine model\n",
+                     MACHINE_GET_CLASS(machine)->name);
+        }
+        return;
+    }
+
+    /* Software dtb is always the -dtb arg */
+    if (dtb_arg) {
+        sw_fdt.dtb = load_device_tree(dtb_arg, &sw_fdt.fdt_size);
+        if (!sw_fdt.dtb) {
+            error_report("Error: Unable to load Device Tree %s", dtb_arg);
+            exit(1);
+        }
+    }
+
+    /* If the user provided a -hw-dtb, use it as the hw description.  */
+    if (hw_dtb_arg) {
+        fdt = load_device_tree(hw_dtb_arg, &fdt_size);
+        if (!fdt) {
+            error_report("Error: Unable to load Device Tree %s", hw_dtb_arg);
+            exit(1);
+        }
+    } else if (sw_fdt.dtb) {
+        fdt = sw_fdt.dtb;
+        fdt_size = sw_fdt.fdt_size;
+    }
+
+    kernel_info = init_memory(fdt, machine->ram_size);
+
+    arm_generic_fdt_binfo.get_dtb = get_ref_dtb;
+    arm_generic_fdt_binfo.ram_size = kernel_info.ram_kernel_size;
+    arm_generic_fdt_binfo.kernel_filename = machine->kernel_filename;
+    arm_generic_fdt_binfo.kernel_cmdline = machine->kernel_cmdline;
+    arm_generic_fdt_binfo.initrd_filename = machine->initrd_filename;
+    arm_generic_fdt_binfo.smp_loader_start = SMP_BOOT_ADDR;
+    arm_generic_fdt_binfo.smp_bootreg_addr = SMP_BOOTREG_ADDR;
+    arm_generic_fdt_binfo.board_id = 0xd32;
+    arm_generic_fdt_binfo.loader_start = kernel_info.ram_kernel_base;
+    arm_generic_fdt_binfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC;
+
+    if (!machine->kernel_filename) {
+        arm_generic_fdt_binfo.firmware_loaded = true;
+        arm_generic_fdt_binfo.psci_conduit = QEMU_PSCI_CONDUIT_DISABLED;
+    }
+
+    arm_load_kernel(ARM_CPU(first_cpu), machine, &arm_generic_fdt_binfo);
+
+    return;
+}
+
+static void arm_generic_fdt_machine_init(MachineClass *mc)
+{
+    mc->desc = "ARM device tree driven machine model";
+    mc->init = arm_generic_fdt_init;
+    mc->max_cpus = 64;
+    mc->default_cpus = 4;
+
+    mc->pci_allow_0_address = true;
+    mc->minimum_page_bits = 12;
+    mc->smp_props.clusters_supported = true;
+
+}
+
+DEFINE_MACHINE_ARM("arm-generic-fdt", arm_generic_fdt_machine_init)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 47cdc51d13..1ef0b8c2c1 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -1,4 +1,6 @@
 arm_ss = ss.source_set()
+arm_ss.add(files('arm_generic_fdt.c'))
+
 arm_common_ss = ss.source_set()
 arm_common_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c'))
 arm_common_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
-- 
2.43.0
[PATCH v2 21/33] hw/core/sysbus: implement FDT_GENERIC_MMAP_CLASS interface
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The patch implements FDTGenericMMap interface for sysbus
device class. The implementation maps device's MMIO regions
based on 'reg' property in the Device tree.

This enables automatic memory mapping for the most QEMU
peripherals without the need of device-specific FDT
handling code.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/sysbus.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
index 3adf2f2faf..78b7827708 100644
--- a/hw/core/sysbus.c
+++ b/hw/core/sysbus.c
@@ -23,6 +23,8 @@
 #include "monitor/monitor.h"
 #include "system/address-spaces.h"
 
+#include "hw/core/fdt_generic_util.h"
+
 static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent);
 static char *sysbus_get_fw_dev_path(DeviceState *dev);
 
@@ -291,11 +293,32 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev)
     return g_strdup(qdev_fw_name(dev));
 }
 
+static bool sysbus_parse_reg(FDTGenericMMap *obj, FDTGenericRegPropInfo reg,
+                             Error **errp)
+{
+    int i;
+
+    for (i = 0; i < reg.n; ++i) {
+        MemoryRegion *mr_parent = (MemoryRegion *)
+            object_dynamic_cast(reg.parents[i], TYPE_MEMORY_REGION);
+        if (!mr_parent) {
+            /* evil */
+            mr_parent = get_system_memory();
+        }
+        memory_region_add_subregion_overlap(mr_parent, reg.a[i],
+                                 sysbus_mmio_get_region(SYS_BUS_DEVICE(obj), i),
+                                 reg.p[i]);
+    }
+    return false;
+}
+
 static void sysbus_device_class_init(ObjectClass *klass, const void *data)
 {
     DeviceClass *k = DEVICE_CLASS(klass);
+    FDTGenericMMapClass *fmc = FDT_GENERIC_MMAP_CLASS(klass);
     k->realize = sysbus_device_realize;
     k->bus_type = TYPE_SYSTEM_BUS;
+    fmc->parse_reg = sysbus_parse_reg;
     /*
      * device_add plugs devices into a suitable bus.  For "real" buses,
      * that actually connects the device.  For sysbus, the connections
@@ -354,6 +377,11 @@ static const TypeInfo sysbus_types[] = {
         .abstract       = true,
         .class_size     = sizeof(SysBusDeviceClass),
         .class_init     = sysbus_device_class_init,
+        .interfaces = (InterfaceInfo[]) {
+        { TYPE_FDT_GENERIC_MMAP },
+        { },
+    },
+
     },
     {
         .name           = TYPE_DYNAMIC_SYS_BUS_DEVICE,
-- 
2.43.0
[PATCH v2 22/33] hw/intc/arm_gic: implement FDT_GENERIC_INTC and fdt support
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The patch implements TYPE_FDT_GENERIC_INTC interface, which
enables GIC to be instantiated and wired via the device tree
description.

The implemented interface method are following:
1. 'get_irq': Parses device tree interrupt specifiers
and return correct qemu_irq for devices which has IRQ's wired
to GIC.
2. 'auto_parent': Automatically connect the GIC's output signals
to the CPU's found in current machine configuration.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/intc/arm_gic.c        | 32 +++++++++++++++++++++++++
 hw/intc/arm_gic_common.c | 50 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
index 4d4b79e6f3..2be44d8e5b 100644
--- a/hw/intc/arm_gic.c
+++ b/hw/intc/arm_gic.c
@@ -24,12 +24,15 @@
 #include "gic_internal.h"
 #include "qapi/error.h"
 #include "hw/core/cpu.h"
+#include "hw/core/boards.h"
 #include "qemu/log.h"
 #include "qemu/module.h"
 #include "trace.h"
 #include "system/kvm.h"
 #include "system/qtest.h"
 
+#include "hw/core/fdt_generic_util.h"
+
 /* #define DEBUG_GIC */
 
 #ifdef DEBUG_GIC
@@ -2162,12 +2165,41 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
 
 }
 
+static void arm_gic_fdt_auto_parent(FDTGenericIntc *obj, Error **errp)
+{
+    GICState *s = ARM_GIC(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    int num_cpus = current_machine->smp.cpus;
+    CPUState *cs;
+    int i = 0;
+
+    for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
+        if (i >= s->num_cpu) {
+            break;
+        }
+
+        sysbus_connect_irq(sbd, i,
+                           qdev_get_gpio_in(DEVICE(cs), 0));
+        sysbus_connect_irq(sbd, i + num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 1));
+        sysbus_connect_irq(sbd, i + 2 * num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 2));
+        sysbus_connect_irq(sbd, i + 3 * num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 3));
+        i++;
+    }
+
+    /* FIXME: Add some error checking */
+}
+
 static void arm_gic_class_init(ObjectClass *klass, const void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     ARMGICClass *agc = ARM_GIC_CLASS(klass);
+    FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(klass);
 
     device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize);
+    fgic->auto_parent = arm_gic_fdt_auto_parent;
 }
 
 static const TypeInfo arm_gic_info = {
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
index 304d89cf56..04787cff79 100644
--- a/hw/intc/arm_gic_common.c
+++ b/hw/intc/arm_gic_common.c
@@ -28,6 +28,8 @@
 #include "migration/vmstate.h"
 #include "system/kvm.h"
 
+#include "hw/core/fdt_generic_util.h"
+
 static int gic_pre_save(void *opaque)
 {
     GICState *s = (GICState *)opaque;
@@ -348,6 +350,51 @@ static void arm_gic_common_linux_init(ARMLinuxBootIf *obj,
     }
 }
 
+static int arm_gic_common_fdt_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
+                                      uint32_t *cells, int ncells, int max,
+                                      Error **errp)
+{
+    GICState *gs = ARM_GIC_COMMON(obj);
+    int cpu = 0;
+    uint32_t idx;
+
+    if (ncells != 3) {
+        error_setg(errp, "ARM GIC requires 3 interrupt cells, %d cells given",
+                   ncells);
+        return 0;
+    }
+    idx = cells[1];
+
+    switch (cells[0]) {
+    case 0:
+        if (idx >= gs->num_irq) {
+            error_setg(errp, "ARM GIC SPI has maximum index of %" PRId32 ", "
+                       "index %" PRId32 " given", gs->num_irq - 1, idx);
+            return 0;
+        }
+        (*irqs) = qdev_get_gpio_in(DEVICE(obj), cells[1]);
+        return 1;
+    case 1: /* PPI */
+        if (idx >= 16) {
+            error_setg(errp, "ARM GIC PPI has maximum index of 15, "
+                       "index %" PRId32 " given", idx);
+            return 0;
+        }
+        for (cpu = 0; cpu < max && cpu < gs->num_cpu; cpu++) {
+            if (cells[2] & 1 << (cpu + 8)) {
+                *irqs = qdev_get_gpio_in(DEVICE(obj),
+                                         gs->num_irq - 16 + idx + cpu * 32);
+            }
+            irqs++;
+        }
+        return cpu;
+    default:
+        error_setg(errp, "Invalid cell 0 value in interrupt binding: %d",
+                   cells[0]);
+        return 0;
+    }
+}
+
 static const Property arm_gic_common_properties[] = {
     DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1),
     DEFINE_PROP_UINT32("first-cpu-index", GICState, first_cpu_index, 0),
@@ -368,12 +415,14 @@ static void arm_gic_common_class_init(ObjectClass *klass, const void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     ResettableClass *rc = RESETTABLE_CLASS(klass);
     ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass);
+    FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(klass);
 
     rc->phases.hold = arm_gic_common_reset_hold;
     dc->realize = arm_gic_common_realize;
     device_class_set_props(dc, arm_gic_common_properties);
     dc->vmsd = &vmstate_gic;
     albifc->arm_linux_init = arm_gic_common_linux_init;
+    fgic->get_irq = arm_gic_common_fdt_get_irq;
 }
 
 static const TypeInfo arm_gic_common_type = {
@@ -385,6 +434,7 @@ static const TypeInfo arm_gic_common_type = {
     .abstract = true,
     .interfaces = (const InterfaceInfo[]) {
         { TYPE_ARM_LINUX_BOOT_IF },
+        { TYPE_FDT_GENERIC_INTC },
         { },
     },
 };
-- 
2.43.0
[PATCH v2 23/33] target/arm/cpu: add fdt support for armv8-timer
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Implement FDT compatibility, so that timer can initilize
and wire irqs based on device tree information.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 target/arm/cpu.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 7542444b18..6e68aa457e 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -54,6 +54,8 @@
 #include "target/arm/gtimer.h"
 
 #include "trace.h"
+#include "hw/core/fdt_generic_util.h"
+
 
 static void arm_cpu_set_pc(CPUState *cs, vaddr value)
 {
@@ -2455,3 +2457,116 @@ static void arm_cpu_register_types(void)
 }
 
 type_init(arm_cpu_register_types)
+
+#ifndef CONFIG_USER_ONLY
+
+static Object *fdt_armv8_timer_get_intc(FDTMachineInfo *fdti, char *node_path)
+{
+    char intc_node_path[DT_PATH_LENGTH];
+    uint32_t intc_phandle;
+    Error *errp = NULL;
+    DeviceState *intc;
+
+    intc_phandle = qemu_fdt_getprop_cell_inherited(fdti->fdt, node_path,
+                                         "interrupt-parent",
+                                         0, &errp);
+
+    /* There must be an interrupt-parent */
+    if (errp ||
+        qemu_devtree_get_node_by_phandle(fdti->fdt,
+                                         intc_node_path, intc_phandle)) {
+        g_assert_not_reached();
+    }
+
+    while (!fdt_init_has_opaque(fdti, intc_node_path)) {
+        fdt_init_yield(fdti);
+    }
+
+    intc = DEVICE(fdt_init_get_opaque(fdti, intc_node_path));
+
+    while (!intc->realized) {
+        fdt_init_yield(fdti);
+    }
+
+    return OBJECT(intc);
+}
+
+static int armv8_timer_fdt_init(char *node_path, FDTMachineInfo *fdti,
+                                void *priv)
+{
+    Object *intc = fdt_armv8_timer_get_intc(fdti, node_path);
+    CPUState *cpu;
+    bool map_mode = false;
+    qemu_irq *sec_irqs = NULL;
+    qemu_irq *ns_irqs;
+    qemu_irq *v_irqs;
+    qemu_irq *h_irqs;
+    uint32_t first_cpu_idx;
+    uint32_t num_cpu;
+    bool has_sec_ext;
+    Error *err = NULL;
+
+    first_cpu_idx = object_property_get_uint(intc, "first-cpu-idx", &err);
+    if (!err) {
+        num_cpu = object_property_get_uint(intc, "num-cpu", &err);
+        assert(!err);
+        has_sec_ext = object_property_get_bool(intc, "has-security-extensions",
+                                               &err);
+        assert(!err);
+    } else {
+        /*
+         * Connect all CPUs with the ARM_FEATURE_GENERIC_TIMER set for
+         * backwards compatibility when the 'first-cpu-idx' property does not
+         * exist.
+         */
+        num_cpu = 0;
+        has_sec_ext = true;
+    }
+
+    if (has_sec_ext) {
+        sec_irqs = fdt_get_irq(fdti, node_path, 0, &map_mode);
+        ns_irqs = fdt_get_irq(fdti, node_path, 1, &map_mode);
+        v_irqs = fdt_get_irq(fdti, node_path, 2, &map_mode);
+        h_irqs = fdt_get_irq(fdti, node_path, 3, &map_mode);
+    } else {
+        ns_irqs = fdt_get_irq(fdti, node_path, 0, &map_mode);
+        v_irqs = fdt_get_irq(fdti, node_path, 1, &map_mode);
+        h_irqs = fdt_get_irq(fdti, node_path, 2, &map_mode);
+    }
+
+    assert(!map_mode); /* not supported for PPI */
+
+    for (cpu = first_cpu; cpu; cpu = CPU_NEXT(cpu)) {
+        ARMCPU *acpu = ARM_CPU(cpu);
+        bool is_gic_cpu;
+
+        if (!arm_feature(&acpu->env, ARM_FEATURE_GENERIC_TIMER)) {
+            continue;
+        }
+
+        is_gic_cpu = cpu->cpu_index >= first_cpu_idx &&
+                     cpu->cpu_index < (first_cpu_idx + num_cpu);
+
+        if (!num_cpu || is_gic_cpu) {
+
+            assert(*ns_irqs);
+            assert(*v_irqs);
+            assert(*h_irqs);
+            qdev_connect_gpio_out(DEVICE(acpu), 0, *ns_irqs++);
+            qdev_connect_gpio_out(DEVICE(acpu), 1, *v_irqs++);
+            qdev_connect_gpio_out(DEVICE(acpu), 2, *h_irqs++);
+
+            if (has_sec_ext) {
+                assert(*sec_irqs);
+                qdev_connect_gpio_out(DEVICE(acpu), 3, *sec_irqs++);
+            }
+        }
+    }
+
+    return 0;
+}
+
+fdt_register_compatibility(armv8_timer_fdt_init,
+                           "compatible:arm,armv8-timer");
+
+#endif
-- 
2.43.0
[PATCH v2 24/33] qom/object: export object_resolve_link()
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Export `object_resovle_link()` for use by other
subsystems.

This allow callers to resolve object path and verify
type contrains of a link property.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 include/qom/object.h | 12 ++++++++++++
 qom/object.c         |  2 +-
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/include/qom/object.h b/include/qom/object.h
index 26df6137b9..1573d41d4f 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -2034,6 +2034,18 @@ int object_child_foreach_recursive(Object *obj,
  */
 Object *object_property_add_new_container(Object *obj, const char *name);
 
+/*
+ * object_resolve_link:
+ *
+ * Lookup an object and ensure its type matches the link property type.  This
+ * is similar to object_resolve_path() except type verification against the
+ * link property is performed.
+ *
+ * Returns: The matched object or NULL on path lookup failures.
+ */
+Object *object_resolve_link(Object *obj, const char *name,
+    const char *path, Error **errp);
+
 /**
  * object_property_help:
  * @name: the name of the property
diff --git a/qom/object.c b/qom/object.c
index ff8ede8a32..7e16765cb4 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1904,7 +1904,7 @@ static void object_get_link_property(Object *obj, Visitor *v,
  *
  * Returns: The matched object or NULL on path lookup failures.
  */
-static Object *object_resolve_link(Object *obj, const char *name,
+Object *object_resolve_link(Object *obj, const char *name,
                                    const char *path, Error **errp)
 {
     const char *type;
-- 
2.43.0
[PATCH v2 25/33] system/memory: add setters for MemoryRegion properties
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Currently, MemoryRegion properties such as addr, size,
priority and container are read-only and don't have setters.

This patch implements the missing setters function, so that
properties can be modified dynamically.

The changes allow MemoryRegions objects to be fully configured
via properties, which enables them be created from a device
tree description.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 system/memory.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 216 insertions(+), 5 deletions(-)

diff --git a/system/memory.c b/system/memory.c
index c51d0798a8..fc1f6cff0c 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -55,6 +55,9 @@ static GHashTable *flat_views;
 
 typedef struct AddrRange AddrRange;
 
+static void memory_region_update_container_subregions(MemoryRegion *subregion);
+static void memory_region_readd_subregion(MemoryRegion *mr);
+
 /*
  * Note that signed integers are needed for negative offsetting in aliases
  * (large MemoryRegion::alias_offset).
@@ -1251,6 +1254,73 @@ void memory_region_init(MemoryRegion *mr,
     memory_region_do_init(mr, owner, name, size);
 }
 
+static void memory_region_get_addr(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    uint64_t value = mr->addr;
+
+    visit_type_uint64(v, name, &value, errp);
+}
+
+static void memory_region_set_addr(Object *obj, Visitor *v, const char *name,
+                                   void *opaque, Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    Error *local_err = NULL;
+    uint64_t value;
+
+    visit_type_uint64(v, name, &value, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    memory_region_set_address(mr, value);
+}
+
+static void memory_region_set_container(Object *obj, Visitor *v,
+                                        const char *name, void *opaque,
+                                        Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    Error *local_err = NULL;
+    MemoryRegion *old_container = mr->container;
+    MemoryRegion *new_container = NULL;
+    char *path = NULL;
+
+    visit_type_str(v, name, &path, &local_err);
+
+    if (!local_err && strcmp(path, "") != 0) {
+        new_container = MEMORY_REGION(object_resolve_link(obj, name, path,
+                                      &local_err));
+        while (new_container->alias) {
+            new_container = new_container->alias;
+        }
+    }
+
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    object_ref(OBJECT(new_container));
+
+    memory_region_transaction_begin();
+    memory_region_ref(mr);
+    if (old_container) {
+        memory_region_del_subregion(old_container, mr);
+    }
+    mr->container = new_container;
+    if (new_container) {
+        memory_region_update_container_subregions(mr);
+    }
+    memory_region_unref(mr);
+    memory_region_transaction_commit();
+
+    object_unref(OBJECT(old_container));
+}
+
 static void memory_region_get_container(Object *obj, Visitor *v,
                                         const char *name, void *opaque,
                                         Error **errp)
@@ -1275,6 +1345,53 @@ static Object *memory_region_resolve_container(Object *obj, void *opaque,
     return OBJECT(mr->container);
 }
 
+static void memory_region_set_alias(const Object *obj, const char *name,
+                                    Object *val, Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    MemoryRegion *subregion, *next;
+
+    /*
+     * Be conservative and only allow one shotting for the mo
+     * FIXME: Use a softer error than assert
+     */
+    assert(!mr->alias);
+
+    /*
+     * FIXME: check we don't already have subregions and
+     * anything else that might be mutex with aliasing
+     */
+
+    memory_region_transaction_begin();
+    QTAILQ_FOREACH_SAFE(subregion, &mr->subregions, subregions_link, next) {
+        object_property_set_link(OBJECT(subregion), "container",
+                                 OBJECT(val), errp);
+    }
+    memory_region_ref(mr);
+    mr->alias = MEMORY_REGION(val);
+    mr->alias->mapped_via_alias++;
+    memory_region_unref(mr);
+    memory_region_transaction_commit();
+    /* FIXME: add cleanup destructors etc etc */
+}
+
+static void memory_region_set_alias_offset_prop(Object *obj, Visitor *v,
+                                                const char *name,
+                                                void *opaque, Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    Error *local_err = NULL;
+    uint64_t value;
+
+    visit_type_uint64(v, name, &value, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    memory_region_set_alias_offset(mr, value);
+}
+
 static void memory_region_get_priority(Object *obj, Visitor *v,
                                        const char *name, void *opaque,
                                        Error **errp)
@@ -1285,6 +1402,70 @@ static void memory_region_get_priority(Object *obj, Visitor *v,
     visit_type_int32(v, name, &value, errp);
 }
 
+static void memory_region_set_priority(Object *obj, Visitor *v,
+                                       const char *name, void *opaque,
+                                       Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    Error *local_err = NULL;
+    int32_t value;
+
+    visit_type_uint32(v, name, (uint32_t *)&value, &error_abort);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    if (mr->priority != value) {
+        mr->priority = value;
+        memory_region_readd_subregion(mr);
+    }
+}
+
+static void memory_region_do_set_ram(MemoryRegion *mr)
+{
+    if (mr->addr) {
+        qemu_ram_free(mr->ram_block);
+    }
+    if (int128_eq(mr->size, int128_make64(0))) {
+        return;
+    }
+    if (mr->ram) {
+        mr->ram_block = qemu_ram_alloc(int128_get64(mr->size),
+                                       RAM_SHARED, mr, &error_abort);
+    }
+}
+
+static void memory_region_set_ram(Object *obj, Visitor *v, const char *name,
+                                  void *opaque, Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    Error *local_err = NULL;
+    uint8_t value;
+
+    visit_type_uint8(v, name, &value, &error_abort);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+
+    mr->dirty_log_mask |= tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0;
+    /* FIXME: Sanitize error handling */
+    /* FIXME: Probably need all that transactions stuff */
+    if (mr->ram == value) {
+        return;
+    }
+
+    mr->ram = value;
+    mr->terminates = !!value; /*FIXME: Wrong */
+
+    if (int128_eq(int128_2_64(), mr->size)) {
+        return;
+    }
+
+    memory_region_do_set_ram(mr);
+}
+
 static void memory_region_get_size(Object *obj, Visitor *v, const char *name,
                                    void *opaque, Error **errp)
 {
@@ -1294,6 +1475,19 @@ static void memory_region_get_size(Object *obj, Visitor *v, const char *name,
     visit_type_uint64(v, name, &value, errp);
 }
 
+static void memory_region_set_object_size(Object *obj, Visitor *v,
+                                          const char *name, void *opaque,
+                                          Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    Error *local_err = NULL;
+    uint64_t size;
+
+    visit_type_uint64(v, name, &size, &local_err);
+
+    memory_region_set_size(mr, size);
+}
+
 static void memory_region_initfn(Object *obj)
 {
     MemoryRegion *mr = MEMORY_REGION(obj);
@@ -1309,19 +1503,33 @@ static void memory_region_initfn(Object *obj)
     op = object_property_add(OBJECT(mr), "container",
                              "link<" TYPE_MEMORY_REGION ">",
                              memory_region_get_container,
-                             NULL, /* memory_region_set_container */
+                             memory_region_set_container,
                              NULL, NULL);
     op->resolve = memory_region_resolve_container;
 
-    object_property_add_uint64_ptr(OBJECT(mr), "addr",
-                                   &mr->addr, OBJ_PROP_FLAG_READ);
+    object_property_add_link(OBJECT(mr), "alias", TYPE_MEMORY_REGION,
+                             (Object **)&mr->alias,
+                             memory_region_set_alias,
+                             0);
+    object_property_add(OBJECT(mr), "alias-offset", "uint64",
+                        NULL,  /* FIXME: Add getter */
+                        memory_region_set_alias_offset_prop,
+                        NULL, NULL);
+    object_property_add(OBJECT(mr), "addr", "uint64",
+                        memory_region_get_addr,
+                        memory_region_set_addr,
+                        NULL, NULL);
     object_property_add(OBJECT(mr), "priority", "uint32",
                         memory_region_get_priority,
-                        NULL, /* memory_region_set_priority */
+                        memory_region_set_priority,
+                        NULL, NULL);
+    object_property_add(OBJECT(mr), "ram", "uint8",
+                        NULL, /* FIXME: Add getter */
+                        memory_region_set_ram,
                         NULL, NULL);
     object_property_add(OBJECT(mr), "size", "uint64",
                         memory_region_get_size,
-                        NULL, /* memory_region_set_size, */
+                        memory_region_set_object_size,
                         NULL, NULL);
 }
 
@@ -2730,6 +2938,9 @@ void memory_region_set_size(MemoryRegion *mr, uint64_t size)
     }
     memory_region_transaction_begin();
     mr->size = s;
+    if (mr->ram) {
+        memory_region_do_set_ram(mr);
+    }
     memory_region_update_pending = true;
     memory_region_transaction_commit();
 }
-- 
2.43.0
[PATCH v2 26/33] system/memory: implement FDT_GENERIC_MMAP interface
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The patch implements TYPE_FDT_GENERIC_MMAP interface
for the MemoryRegion class.

This enables memory region objects to be automatically
constructed by FDT parser based on the 'reg' property
in the Device tree. The implementation parses register
tuples and set base address, size and priority properties.

Optionally parent container can be set of newly created
memory region.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 system/memory.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/system/memory.c b/system/memory.c
index fc1f6cff0c..81e6846c60 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -38,6 +38,8 @@
 
 #include "memory-internal.h"
 
+#include "hw/core/fdt_generic_util.h"
+
 //#define DEBUG_UNASSIGNED
 
 static unsigned memory_region_transaction_depth;
@@ -3783,6 +3785,57 @@ static gboolean mtree_info_flatview_free(gpointer key, gpointer value,
     return true;
 }
 
+static bool memory_region_parse_reg(FDTGenericMMap *obj,
+    FDTGenericRegPropInfo reg, Error **errp)
+{
+    MemoryRegion *mr = MEMORY_REGION(obj);
+    uint64_t base_addr = ~0ull;
+    uint64_t total_size = 0;
+    uint64_t max_addr = 0;
+    int i;
+
+    if (!reg.n) {
+        return false;
+    }
+
+    for (i = 0; i < reg.n; ++i) {
+        base_addr = MIN(base_addr, reg.a[i]);
+        max_addr = MAX(max_addr, reg.a[i] + reg.s[i]);
+        total_size += reg.s[i];
+        if (reg.p[i] != reg.p[0]) {
+            error_setg(errp, "FDT generic memory parser does not support"
+                       "mixed priorities");
+            return false;
+        }
+    }
+
+    if (total_size != max_addr - base_addr) {
+        error_setg(errp, "FDT generic memory parse does not "
+        "spport discontiguous or overlapping memory regions");
+        return false;
+    }
+
+    /*
+     * FIXME: parent should not be optional but we need to implement
+     * reg-extended in kernel before we can do things properly
+     */
+    if (reg.parents[0]) {
+        object_property_set_link(OBJECT(mr), "container", reg.parents[0],
+                                 &error_abort);
+    }
+    object_property_set_int(OBJECT(mr), "size", total_size, &error_abort);
+    object_property_set_int(OBJECT(mr), "addr", base_addr, &error_abort);
+    object_property_set_int(OBJECT(mr), "priority", reg.p[0], &error_abort);
+    return false;
+}
+
+static void memory_region_class_init(ObjectClass *oc, const void *data)
+{
+    FDTGenericMMapClass *fmc = FDT_GENERIC_MMAP_CLASS(oc);
+
+    fmc->parse_reg = memory_region_parse_reg;
+}
+
 static void mtree_info_flatview(bool dispatch_tree, bool owner)
 {
     struct FlatViewInfo fvi = {
@@ -4037,6 +4090,11 @@ static const TypeInfo memory_region_info = {
     .instance_size      = sizeof(MemoryRegion),
     .instance_init      = memory_region_initfn,
     .instance_finalize  = memory_region_finalize,
+    .class_init         = memory_region_class_init,
+    .interfaces         = (InterfaceInfo[]) {
+        { TYPE_FDT_GENERIC_MMAP },
+        { },
+    },
 };
 
 static const TypeInfo iommu_memory_region_info = {
-- 
2.43.0
[PATCH v2 27/33] hw/core/fdt_generic_util: initialize serial devices
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The patch adds proper handling and registration
for serial devices instantiated via FDT.

The devices with 'chardev' properties are
connected to serial backends, if corresponding dts
property serial id number is set.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/fdt_generic_util.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 3179248142..c58a9ccdc7 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -36,6 +36,7 @@
 #include "system/system.h"
 #include "system/reset.h"
 #include "qemu/cutils.h"
+#include "chardev/char.h"
 #include "qemu/log.h"
 #include "qemu/config-file.h"
 #include "hw/core/boards.h"
@@ -1216,6 +1217,7 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
     props = qemu_devtree_get_props(fdti->fdt, node_path);
     for (prop = props; prop->name; prop++) {
         const char *propname = trim_vendor(prop->name);
+        int len = prop->len;
         ObjectProperty *p = NULL;
 
         p = object_property_find(OBJECT(dev), propname);
@@ -1235,6 +1237,13 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
             continue;
         }
 
+        /* Special case for chardevs.  */
+        if (!strcmp(propname, "chardev") && !strcmp(p->type, "str")) {
+            qdev_prop_set_chr(DEVICE(dev), "chardev",
+                              serial_hd(get_int_be(prop->value, len)));
+            continue;
+        }
+
         fdt_init_qdev_scalar_prop(OBJECT(dev), p, fdti, node_path, prop);
     }
 
-- 
2.43.0
[PATCH v2 28/33] system/memory: add QOM aliases for fdt support
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The patch registers FDT compatibility handlers and
QOM aliases of memory subsystem, so that memory
regions can be created from a device tree model.

The alias 'qemu:system-memory' could be used to attach
subregion to root address space.
The system memory can also be attached to another
container with other priority, which may be used
for implementing secure memory structures.

Also 'qemu-memory-region' types can be used to create
new memory regions.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 system/memory.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 99 insertions(+)

diff --git a/system/memory.c b/system/memory.c
index 81e6846c60..66b5f6a484 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -4120,3 +4120,102 @@ static void memory_register_types(void)
 }
 
 type_init(memory_register_types)
+
+static int sysmem_fdt_init(char *node_path, FDTMachineInfo *fdti,
+                           void *priv)
+{
+    int priority = 0;
+    uint32_t container_phandle;
+    MemoryRegion *container;
+    char container_node_path[DT_PATH_LENGTH];
+    Error *errp = NULL;
+
+    fdt_init_set_opaque(fdti, node_path, OBJECT(get_system_memory()));
+
+    /* allow to set system_memory region as subregion */
+    container_phandle = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+                                            "container",
+                                            0, &errp);
+    if (!errp) {
+        /*
+         * TBD: move reg_info work to fdt_generic_util.c
+         */
+        int sc = 0;
+        sc = qemu_fdt_getprop_cell(fdti->fdt, node_path, "#address-cells",
+                                    0, &errp);
+        if (!errp && sc) {
+            error_report("can't change sysmem address");
+            return -1;
+        }
+
+        if (errp) {
+            error_free(errp); errp = NULL;
+        }
+
+        sc = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+                                    "#size-cells", 0, &errp);
+        if (!errp && sc) {
+            error_report("can't change sysmem size");
+            return -1;
+        }
+
+        error_free(errp); errp = NULL;
+        /* Check for custom "#priority-cells" extension */
+        sc = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+                                    "#priority-cells", 0, &errp);
+        if (errp) {
+            /* priotity is optional */
+            priority = 0;
+            error_free(errp); errp = NULL;
+        } else if (sc) {
+            priority = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+                                    "reg", 0, &errp);
+            if (errp) {
+                error_report("failed to read priority from reg");
+                error_free(errp);
+                return -1;
+            }
+        }
+
+        if (qemu_devtree_get_node_by_phandle(fdti->fdt, container_node_path,
+                                             container_phandle)) {
+            error_report("failed to get contaier node");
+            return -1;
+        }
+
+        while (!fdt_init_has_opaque(fdti, container_node_path)) {
+            fdt_init_yield(fdti);
+        }
+
+        container = MEMORY_REGION(fdt_init_get_opaque(fdti,
+                                                      container_node_path));
+
+        memory_region_add_subregion_overlap(container, 0,
+                                            get_system_memory(), priority);
+    } else {
+        /* container is optional property */
+        error_free(errp);
+        errp = NULL;
+    }
+
+    return 0;
+}
+
+
+fdt_register_compatibility(sysmem_fdt_init, "compatible:qemu:system-memory");
+
+static const TypeInfo fdt_qom_aliases[] = {
+    {   .name = "qemu-memory-region",       .parent = "memory-region"  },
+    {   .name = "simple-bus",               .parent = "memory-region"  },
+};
+
+static void fdt_memory_types(void)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(fdt_qom_aliases); ++i) {
+        type_register_static(&fdt_qom_aliases[i]);
+    }
+}
+
+type_init(fdt_memory_types)
-- 
2.43.0
[PATCH v2 29/33] hw/intc/arm_gicv3: Implement FDTGenericIntc interface
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

This patch implements the FDTGenericIntc interface for the
ARM GICv3 interrupt controller.

This enables the generic FDT machine infrastructure to
automatically wire up the GIC and resolve interrupts
defined in the Device Tree.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/intc/arm_gicv3.c        | 45 +++++++++++++++++++++++++
 hw/intc/arm_gicv3_common.c | 68 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 113 insertions(+)

diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c
index 542f81ea49..e7e98ef9a5 100644
--- a/hw/intc/arm_gicv3.c
+++ b/hw/intc/arm_gicv3.c
@@ -18,9 +18,13 @@
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu/module.h"
+#include "hw/core/cpu.h"
+#include "hw/core/boards.h"
 #include "hw/intc/arm_gicv3.h"
 #include "gicv3_internal.h"
 
+#include "hw/core/fdt_generic_util.h"
+
 static bool irqbetter(GICv3CPUState *cs, int irq, uint8_t prio, bool nmi)
 {
     /* Return true if this IRQ at this priority should take
@@ -452,14 +456,55 @@ static void arm_gic_realize(DeviceState *dev, Error **errp)
     gicv3_init_cpuif(s);
 }
 
+static void arm_gic_fdt_auto_parent(FDTGenericIntc *obj, Error **errp)
+{
+    GICv3State *s = ARM_GICV3(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+    int num_cpus = s->num_cpu;
+    CPUState *cs;
+    int i = 0;
+
+    for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
+        if (i >= s->num_cpu) {
+            break;
+        }
+
+        sysbus_connect_irq(sbd, i,
+                           qdev_get_gpio_in(DEVICE(cs), 0));
+        sysbus_connect_irq(sbd, i + num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 1));
+        sysbus_connect_irq(sbd, i + 2 * num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 2));
+        sysbus_connect_irq(sbd, i + 3 * num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 3));
+        sysbus_connect_irq(sbd, i + 4 * num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 4));
+        sysbus_connect_irq(sbd, i + 5 * num_cpus,
+                           qdev_get_gpio_in(DEVICE(cs), 5));
+
+        if (s->maint_irq) {
+            int intbase = s->num_irq - GIC_INTERNAL + i * GIC_INTERNAL;
+            qemu_irq irq = qdev_get_gpio_in(DEVICE(sbd),
+                                intbase + s->maint_irq);
+            qdev_connect_gpio_out_named(DEVICE(cs),
+                                               "gicv3-maintenance-interrupt",
+                                                0, irq);
+        }
+
+        i++;
+    }
+}
+
 static void arm_gicv3_class_init(ObjectClass *klass, const void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
     ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
     ARMGICv3Class *agc = ARM_GICV3_CLASS(klass);
+    FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(klass);
 
     agcc->post_load = arm_gicv3_post_load;
     device_class_set_parent_realize(dc, arm_gic_realize, &agc->parent_realize);
+    fgic->auto_parent = arm_gic_fdt_auto_parent;
 }
 
 static const TypeInfo arm_gicv3_info = {
diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c
index 9200671c7a..a393540825 100644
--- a/hw/intc/arm_gicv3_common.c
+++ b/hw/intc/arm_gicv3_common.c
@@ -34,6 +34,7 @@
 #include "system/kvm.h"
 #include "system/whpx.h"
 
+#include "hw/core/fdt_generic_util.h"
 
 static void gicv3_gicd_no_migration_shift_bug_post_load(GICv3State *cs)
 {
@@ -367,6 +368,69 @@ void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
     }
 }
 
+#define FDT_GENERIC_GICV3_TYPE_AFFINITY_ALL 0x01000000U
+#define FDT_GENERIC_GICV3_TYPE_AFFINITY_IDX 0x02000000U
+
+static int arm_gicv3_common_fdt_get_irq(FDTGenericIntc *obj, qemu_irq *irqs,
+                                      uint32_t *cells, int ncells, int max,
+                                      Error **errp)
+{
+    GICv3State *gs = ARM_GICV3_COMMON(obj);
+    int cpu = 0;
+    uint32_t qemu_type;
+    uint32_t cpu_mask;
+    uint32_t idx;
+
+    if (ncells != 3) {
+        error_setg(errp, "ARM GIC requires 3 interrupt cells, %d cells given",
+                   ncells);
+        return 0;
+    }
+    idx = cells[1];
+    qemu_type = cells[0] & 0xff000000;
+
+    switch (cells[0] & 0x00ffffff) {
+    case 0:
+        if (idx >= gs->num_irq) {
+            error_setg(errp, "ARM GIC SPI has maximum index of %" PRId32 ", "
+                       "index %" PRId32 " given", gs->num_irq - 1, idx);
+            return 0;
+        }
+        (*irqs) = qdev_get_gpio_in(DEVICE(obj), cells[1]);
+        return 1;
+    case 1: /* PPI */
+        if (idx >= 16) {
+            error_setg(errp, "ARM GIC PPI has maximum index of 15, "
+                       "index %" PRId32 " given", idx);
+            return 0;
+        }
+        if (qemu_type == FDT_GENERIC_GICV3_TYPE_AFFINITY_IDX) {
+            cpu = cells[2] >> 8;
+            *irqs = qdev_get_gpio_in(DEVICE(obj),
+                                         gs->num_irq - 16 + idx + cpu * 32);
+            return cpu;
+        }
+
+        cpu_mask = cells[2] >> 8;
+        while ((cpu_mask || qemu_type == FDT_GENERIC_GICV3_TYPE_AFFINITY_ALL)
+               && cpu < max && cpu < gs->num_cpu) {
+            if ((cpu_mask & 1) ||
+                 qemu_type == FDT_GENERIC_GICV3_TYPE_AFFINITY_ALL) {
+                *irqs = qdev_get_gpio_in(DEVICE(obj),
+                                         gs->num_irq - 16 + idx + cpu * 32);
+                irqs++;
+            }
+            cpu_mask >>= 1;
+            cpu++;
+        }
+        return cpu;
+    default:
+        error_setg(errp, "Invalid cell 0 value in interrupt binding: %d",
+                   cells[0]);
+        return 0;
+    }
+}
+
 static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
 {
     GICv3State *s = ARM_GICV3_COMMON(dev);
@@ -624,12 +688,15 @@ static void arm_gicv3_common_class_init(ObjectClass *klass, const void *data)
     DeviceClass *dc = DEVICE_CLASS(klass);
     ResettableClass *rc = RESETTABLE_CLASS(klass);
     ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_CLASS(klass);
+    FDTGenericIntcClass *fgic = FDT_GENERIC_INTC_CLASS(klass);
+
 
     rc->phases.hold = arm_gicv3_common_reset_hold;
     dc->realize = arm_gicv3_common_realize;
     device_class_set_props(dc, arm_gicv3_common_properties);
     dc->vmsd = &vmstate_gicv3;
     albifc->arm_linux_init = arm_gic_common_linux_init;
+    fgic->get_irq = arm_gicv3_common_fdt_get_irq;
 }
 
 static const TypeInfo arm_gicv3_common_type = {
@@ -641,6 +708,7 @@ static const TypeInfo arm_gicv3_common_type = {
     .abstract = true,
     .interfaces = (const InterfaceInfo[]) {
         { TYPE_ARM_LINUX_BOOT_IF },
+        { TYPE_FDT_GENERIC_INTC },
         { },
     },
 };
-- 
2.43.0
[PATCH v2 30/33] hw/core/fdt_generic_util: Add deferred device initialization support
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Add a mechanism to defer device initialization
until after the main FDT traversal.

This introduces a deferred queue in FDTMachineInfo.
Devices in this queue are processed in fdt_init_deferred(),
which handles their realization, reset registration,
and resource parsing (interrupts and memory regions).

This allows resolving initialization where devices need to
be realized on latest stages or outside of co-routine context.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/fdt_generic_util.c    | 45 +++++++++++++++++++++++++++++++++++
 include/hw/core/fdt_generic.h |  8 +++++++
 2 files changed, 53 insertions(+)

diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index c58a9ccdc7..4ed9602d47 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -99,6 +99,11 @@ static bool qemu_irq_shared_or_handler(bool *inputs, int n)
     return false;
 }
 
+static void fdt_parse_node_reg_prop(FDTMachineInfo *fdti, char *node_path,
+                             Object *dev);
+static void fdt_parse_node_irq_prop(FDTMachineInfo *fdti, char *node_path,
+                             Object *dev);
+
 static void qemu_irq_shared_handler(void *opaque, int n, int level)
 {
     QEMUIRQSharedState *s = opaque;
@@ -168,6 +173,45 @@ static void fdt_init_cpu_clusters(FDTMachineInfo *fdti)
     }
 }
 
+static void fdt_init_deferred(FDTMachineInfo *fdti)
+{
+    while (fdti->deferred) {
+        FDTDeferredNode *dnode = fdti->deferred;
+        DeviceClass *dc = DEVICE_GET_CLASS(dnode->dev);
+        int length = 0;
+
+        int offset = fdt_path_offset(fdti->fdt, dnode->node_path);
+        if (offset < 0) {
+            error_report("%s Couldn't find node %s: %s", __func__,
+                         dnode->node_path, fdt_strerror(offset));
+        }
+        const char *blockdev = fdt_stringlist_get(fdti->fdt, offset,
+                                              "blockdev-node-name", 0, &length);
+
+        DB_PRINT(0, "FDT: Deferred realize node: %s\n",
+                 dnode->node_path);
+
+        if (blockdev && object_property_find(OBJECT(dnode->dev), "drive")) {
+            fdt_attach_blockdev(fdti, dnode->node_path, OBJECT(dnode->dev));
+        }
+
+        object_property_set_bool(OBJECT(dnode->dev), "realized", true,
+                                &error_fatal);
+        if (dc->legacy_reset) {
+            qemu_register_reset((void (*)(void *))dc->legacy_reset,
+                                dnode->dev);
+        }
+
+        fdt_parse_node_reg_prop(fdti, dnode->node_path, OBJECT(dnode->dev));
+
+        fdt_parse_node_irq_prop(fdti, dnode->node_path, OBJECT(dnode->dev));
+
+        fdti->deferred = dnode->next;
+        g_free(dnode->node_path);
+        g_free(dnode);
+    }
+}
+
 FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
 {
     char node_path[DT_PATH_LENGTH];
@@ -183,6 +227,7 @@ FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
         while (qemu_co_enter_next(fdti->cq, NULL)) {
             ;
         }
+        fdt_init_deferred(fdti);
         fdt_init_cpu_clusters(fdti);
         fdt_init_all_irqs(fdti);
         memory_region_transaction_commit();
diff --git a/include/hw/core/fdt_generic.h b/include/hw/core/fdt_generic.h
index ad9e249156..dc2d7a8876 100644
--- a/include/hw/core/fdt_generic.h
+++ b/include/hw/core/fdt_generic.h
@@ -34,6 +34,12 @@ typedef struct FDTIRQConnection {
     void *next;
 } FDTIRQConnection;
 
+typedef struct FDTDeferredNode {
+    DeviceState *dev;
+    char *node_path;
+    void *next;
+} FDTDeferredNode;
+
 typedef struct FDTMachineInfo {
     /* the fdt blob */
     void *fdt;
@@ -47,6 +53,8 @@ typedef struct FDTMachineInfo {
     FDTIRQConnection *irqs;
     /* list of all CPU clusters */
     FDTCPUCluster *clusters;
+    /* list of devices for deferred init */
+    FDTDeferredNode *deferred;
 } FDTMachineInfo;
 
 /*
-- 
2.43.0
[PATCH v2 31/33] hw/core/fdt_generic_util: Add blockdev binding support
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

The path add possibility to bind to QEMU block backends
via the blockdev-node-name property.

This enables support for the modern -blockdev command line option.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/core/fdt_generic_util.c | 82 ++++++++++++++++++++++++++++++++++----
 1 file changed, 75 insertions(+), 7 deletions(-)

diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 4ed9602d47..68dfeee096 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -75,6 +75,8 @@ static void fdt_get_irq_info_from_intc(FDTMachineInfo *fdti, qemu_irq *ret,
                                        char *intc_node_path,
                                        uint32_t *cells, uint32_t num_cells,
                                        uint32_t max, Error **errp);
+static bool fdt_attach_blockdev(FDTMachineInfo *fdti,
+                                const char *node_path, Object *dev);
 
 typedef struct QEMUIRQSharedState {
     qemu_irq sink;
@@ -241,6 +243,8 @@ FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
     current_machine->smp.cpus = fdt_generic_num_cpus;
     current_machine->smp.max_cpus = fdt_generic_num_cpus;
 
+    bdrv_drain_all();
+
     DB_PRINT(0, "FDT: Device tree scan complete\n");
     return fdti;
 }
@@ -987,6 +991,52 @@ static void fdt_init_qdev_array_prop(Object *obj,
     DB_PRINT_NP(0, "set property %s propname to <list>\n", propname);
 }
 
+/*
+ * Try to attach by matching drive created by '-blockdev node-name=LABEL'
+ * iff the FDT node contains property 'blockdev-node-name=LABEL'.
+ *
+ * Return false unless the given node_path has the property.
+ *
+ * Presence of the property also disables the node from ever attached
+ * to any drive created by the legacy '-drive' QEMU option.
+ *
+ * For more on '-blockdev', see:
+ *   http://events17.linuxfoundation.org/sites/events/files/slides/talk_11.pdf
+ */
+static bool fdt_attach_blockdev(FDTMachineInfo *fdti,
+                                const char *node_path, Object *dev)
+{
+    static const char propname[] = "blockdev-node-name";
+    const char *label;
+
+    /* Inspect FDT node for blockdev-only binding */
+    label = qemu_fdt_getprop(fdti->fdt, node_path, propname,
+                             NULL, NULL);
+
+    /* Skip legacy node */
+    if (!label) {
+        return false;
+    }
+
+    /*
+     * Missing matching bdev is not an error: attachment is optional.
+     *
+     * error_setg() aborts, never returns: 'goto ret' is just sanity.
+     */
+    if (!label[0]) {
+        error_setg(&error_abort, "FDT-node '%s': property '%s' = <empty>",
+                   node_path, propname);
+        return false;
+    }
+
+    if (!bdrv_find_node(label)) {
+        return false;
+    }
+
+    object_property_set_str(OBJECT(dev), "drive", label, NULL);
+    return true;
+}
+
 static void fdt_parse_node_reg_prop(FDTMachineInfo *fdti, char *node_path,
                             Object *dev)
 {
@@ -1173,6 +1223,7 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
     char *dev_type = NULL;
     QEMUDevtreeProp *prop, *props;
     char parent_node_path[DT_PATH_LENGTH];
+    bool defer_realize = false;
 
     if (!compat) {
         return 1;
@@ -1289,6 +1340,11 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
             continue;
         }
 
+        if (!strcmp(propname, "drive")) {
+            defer_realize = true;
+            continue;
+        }
+
         fdt_init_qdev_scalar_prop(OBJECT(dev), p, fdti, node_path, prop);
     }
 
@@ -1307,18 +1363,30 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
              */
             fdt_init_register_user_cpu_cluster(fdti, OBJECT(dev));
         } else {
-            object_property_set_bool(OBJECT(dev), "realized", true,
-                                     &error_fatal);
-            if (dc->legacy_reset) {
-                qemu_register_reset((void (*)(void *))dc->legacy_reset,
-                                    dev);
+            if (defer_realize) {
+                FDTDeferredNode *dnode = g_new0(FDTDeferredNode, 1);
+                *dnode = (FDTDeferredNode) {
+                        .dev = DEVICE(dev),
+                        .node_path = g_strdup(node_path),
+                        .next = fdti->deferred
+                };
+                fdti->deferred = dnode;
+            } else {
+                object_property_set_bool(OBJECT(dev), "realized", true,
+                                        &error_fatal);
+                if (dc->legacy_reset) {
+                    qemu_register_reset((void (*)(void *))dc->legacy_reset,
+                                        dev);
+                }
             }
         }
     }
 
-    fdt_parse_node_reg_prop(fdti, node_path, dev);
+    if (!defer_realize) {
+        fdt_parse_node_reg_prop(fdti, node_path, dev);
 
-    fdt_parse_node_irq_prop(fdti, node_path, dev);
+        fdt_parse_node_irq_prop(fdti, node_path, dev);
+    }
 
     g_free(dev_type);
     g_free(props);
-- 
2.43.0
[PATCH v2 32/33] hw/pci-host: add gsi-irqs property array
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

Add a new gsi-irqs array property to the GPEX PCI host controller.
This enables them to be configured on realize callback.

This is required to init GPEX PCI host from a static configuration,
such as Hardware device tree.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 hw/pci-host/gpex.c         | 6 ++++++
 include/hw/pci-host/gpex.h | 3 +++
 2 files changed, 9 insertions(+)

diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c
index e66784ce51..6125b2c319 100644
--- a/hw/pci-host/gpex.c
+++ b/hw/pci-host/gpex.c
@@ -148,6 +148,10 @@ static void gpex_host_realize(DeviceState *dev, Error **errp)
         s->irq[i].irq_num = -1;
     }
 
+    for (i = 0; i < s->gsi_irq_num; i++) {
+        gpex_set_irq_num(s, i, s->gsi_irqs[i]);
+    }
+
     pci->bus = pci_register_root_bus(dev, "pcie.0", gpex_set_irq,
                                      gpex_swizzle_map_irq_fn,
                                      s, &s->io_mmio, &s->io_ioport, 0,
@@ -190,6 +194,8 @@ static const Property gpex_host_properties[] = {
     DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MMIO_SIZE, GPEXHost,
                      gpex_cfg.mmio64.size, 0),
     DEFINE_PROP_UINT8("num-irqs", GPEXHost, num_irqs, PCI_NUM_PINS),
+    DEFINE_PROP_ARRAY("gsi-irqs", GPEXHost, gsi_irq_num,
+                      gsi_irqs, qdev_prop_uint32, uint32_t),
 };
 
 static void gpex_host_class_init(ObjectClass *klass, const void *data)
diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h
index 1da9c85bce..f8e9874756 100644
--- a/include/hw/pci-host/gpex.h
+++ b/include/hw/pci-host/gpex.h
@@ -64,6 +64,9 @@ struct GPEXHost {
     GPEXIrq *irq;
     uint8_t num_irqs;
 
+    uint32_t *gsi_irqs;
+    uint32_t gsi_irq_num;
+
     bool allow_unmapped_accesses;
 
     struct GPEXConfig gpex_cfg;
-- 
2.43.0
[PATCH v2 33/33] tests/functional: Add functional tests for arm-generic-fdt machine
Posted by Ruslan Ruslichenko 1 month, 3 weeks ago
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>

This patch adds functional tests for the new `arm-generic-fdt` machine type.
It introduces two new test suites:

1. test_arm_generic_fdt.py: Validates the firmware boot chain (TF-A and
   EDK2). It checks the console output for specific patterns indicating
   successful execution of BL1, BL2, BL31, and UEFI stages.

2. test_arm_generic_fdt_alpine.py: Validates a full OS boot by launching
   an Alpine Linux ISO and waiting for the welcome message.

To support these tests, two pre-compiled Device Tree Blob (DTB) files are
added to `tests/data`.

The tests reuse the existing firmware assets and Alpine ISO infrastructure
from the sbsa-ref tests.

Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
 .../arm-generic-fdt/arm64-sbsa-guest.dtb      | Bin 0 -> 673 bytes
 .../aarch64/arm-generic-fdt/arm64-sbsa-hw.dtb | Bin 0 -> 5414 bytes
 tests/functional/aarch64/meson.build          |   2 +
 .../aarch64/test_arm_generic_fdt.py           | 114 ++++++++++++++++++
 .../aarch64/test_arm_generic_fdt_alpine.py    |  61 ++++++++++
 5 files changed, 177 insertions(+)
 create mode 100644 tests/data/dtb/aarch64/arm-generic-fdt/arm64-sbsa-guest.dtb
 create mode 100644 tests/data/dtb/aarch64/arm-generic-fdt/arm64-sbsa-hw.dtb
 create mode 100755 tests/functional/aarch64/test_arm_generic_fdt.py
 create mode 100755 tests/functional/aarch64/test_arm_generic_fdt_alpine.py

diff --git a/tests/data/dtb/aarch64/arm-generic-fdt/arm64-sbsa-guest.dtb b/tests/data/dtb/aarch64/arm-generic-fdt/arm64-sbsa-guest.dtb
new file mode 100644
index 0000000000000000000000000000000000000000..5f2a1e9b2826eec45ed97e6dd704649a05d0af4c
GIT binary patch
literal 673
zcmZuuJx|0i40R8LjsXD+Gtw;#SBZ~>u>%r210zx{4c9=LRB?)cf5SiGkFYT#>@=y4
za+VVN*)R69@_F#{1u**nfNSzg@@I68=^W8v>j8@IA^dX}3GI;IO)>k7PbPKlJ=>_$
zyb&K#d~3ArUzfN-QF`@A85%4bhsws7-xk^i8PPE3l;S(a)gIquNmVr;U=mj7fSh1$
zyjOg4GfyjW=Rr%HA-g`3DVKL)?Q9um?L}~%Gj9Dhl*jr#VOEm)4-;HZaGP+RU!7Be
zjNkJnQ7<RRM$W~6ryQO;axS-XxIS{OwsW{|=CR*zg>1A{<%{yDoh@5!orix}@kH4L
u>UUL2WembX)U{WL2};*?BIuP-;ME#J<CUlpq$3VU(_^6ifKEOu`m|p^QBCmx

literal 0
HcmV?d00001

diff --git a/tests/data/dtb/aarch64/arm-generic-fdt/arm64-sbsa-hw.dtb b/tests/data/dtb/aarch64/arm-generic-fdt/arm64-sbsa-hw.dtb
new file mode 100644
index 0000000000000000000000000000000000000000..0f12655f9c4b4f12556dd0c55c3bc552ff3f8a5e
GIT binary patch
literal 5414
zcmd5=JC7Vi5bil%Heif-h=&MPtiXUg*4~|M46M)^84+1BSU|X*o9*2lyz{u3+4CJj
z!U3|xFR+A&9FmhD0s==!NJx&zh!6>g2oWIQ`)ayoXYS7CupLlR?{sxl*H=|t)7`V*
zANcDRV;+Cr7<1B?NB+R`1++tG52JyWj$-^9>9&77$KHkK-LB6u@-Lwsdi%{Y?{*HK
zd*j@x&O3H&bDM<raOc!JXFBgic{vYeoijl=jS^c;Ry$9CT|!g7>v%7iMM2TlU2!t%
zdto+r;(9J^k(##XBXtKo5!EupFGStP(E5o@H*8*5KUwjEJoYh%3To@EZTvY3K`I{(
zgsu4xUi<9ZrhMJ$s{ST;=27{}=+jq?ZLkamq;mSJ`Mwo@xBEx`)gJx0ZGX8qpr*gN
zu6N2`wRcDSUES#~eP_E<{;Iv(^;c@5jAEO&eI}=C$nw78_yfiqBqn7KHTn-U`uDp&
zedN2}N6jBW+c@dhNP=6~aGqN~wsD$oK@p^L^c^yPJ94r|@$9eLc<yHTxt&C5!l`wA
zTt%Ng3I=tD#a023rp|8fb5s5W7>%E{PSOoL>0j-`tFT+zxETQD)c?vom8VbaH{u@V
zK|EOLRRmEx_6|ot9r>+ey9QSMs{Oc0F4Paftr9(0c9?jGar(t|Le6QrqCxdg|8E7i
zjyDS<JB;ISJQ;>*QU=(mUAeVmcF=lYaQ<FmWNz<4lNbfdhCw{6cX^HBJEuq9#cRxO
zU`QMQ?G=-GrLJI3GP{ZRqO=`e3x~#Uv6k;u9G6Gn>>3;Oh{^i9ehELImJgR?$e65p
zQnr`;wqjlQFpVbDg(I+~*nGE`7v;O#c&>x`x)1&9Xj*ujqidsMeD6q<{bzrRG#pm@
zOl<>umFX8zoXxDi=1%%1t|)F>tKHt|8^!5mv)=OZ;6$&t$~UdXeGt7X!WqN3X|siU
zo}7?WBRT5)h^Fs6^VS#p=zyx4SG;34#M=+uop>A(+JUF*w=13v2giv7;_OxoXDfsF
zt!*;aez8^rz6~iApU1PHn9B6_d@7J+_M(P<u0}<dUjHi5EaZPy;i=ra4>}qzo*vGv
zW=(oex9xe)f&UcE<=lG_{U;l|k3LD|?g}M5u)O2j;z7Q>*VkrgcwuBWs;Q!jTzIFm
zg);5Yw|+Q|j=FKR%{)o2P{TCguua#EE8ncc+o^5Z^P;4*c|OldpIddF&So|rpsrBO
zu9U?Oobht5`;#c#SOo*({0^F&-2+w*$-Tt`RsqPBob%m#-wi2$0X+N4m)qnY!C(ht
zD*pt1eK)wypM!s@A^+0#RsZX@zPa3xQ*_Pobn_|)&}0oH&S6v*gJZ{fCwYWIQu%><
zP$L&UbVMXRvWz7kr}w#vrnQCtKcHV@)cXc=y|CZhH>gYB7*B2cJeZ0E!^JB528YRS
ze~(p%G14Q65%{aJZ^;b-qt)CdKJ|9u(<`AGU-4F5F^m0+qmmoP!un48G@$l7=u7U-
zw(Xm<p5Q=S<6(~XQ{%fx>XUoUZ~|0lg2abO7Nvt%Zt6pG?;wEqaGtY;YD|ssL^H-<
z8b(f$Id0~TK51+VyJ8NKDn_?1-tQQpeYMfYRTF2GujVC|O<>`eS#Bg$_4S<Ley|hs
z5JpKYObKv+QMI9%grT;}pWPh1JRXnh5*2-7be#_8lW*=zJ@D$2$}uE*)Ftr_r+voG
zK6U$mCQo@yE^Qna+IC)>kM-7Y;}~ILiT4ThhxRq+vM2Li=i|aNO$%2-#^e3xD5F`-
zyNW(*hWU{k6>&V@Smgykj!6s)Ml%Pj3yqan+CR8UpZ+o2stz>^M5^opd+C|P;!|W)
zkHOy3v*75L#<%YJ%5Q=vE_pdO_}@h3J4ReAa#U3X-tvPEk=#>l{y)#<h5vgvmqnTJ
z{M)(A71+NxFS7uDOqC{1M|NhqMf9QdLpz%lrW=e#xh)FE&V#({8GN^zFpaaIjMiq>
z;Fb`Dc35s@*2I|~jK@(Dm0M6NERN!}AU9!Bj`MZnzLwkH&fSF@6=uyBCYuJy2oT*Y
zkJ21Qoga%&F@7T-!Pls%J}czcPsd|Gn{XQByu!#`#sIZUbAM)&NjWviJoaV0Fc+f{
z2C69hidl;$zD-6^keF!HLt92$L0dJr>>Npg7-8qphOKO4uAd-xz6f$Vii*;G-Qy}U
zM`m;6sNlywarvJapAk|r4GLfK!Qkv>X%ngyCdQX0m;iql`gKD5Xk^^~0h~9>BD0V!
z?mVViPItcvd6V<Uxm};*&&Mr6&e9wweKAY%i`A@vM|cM(MMSqCD}#wONP8UQTk@&#
E4{LL%!~g&Q

literal 0
HcmV?d00001

diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build
index 49eca12058..3ec20f5bb2 100644
--- a/tests/functional/aarch64/meson.build
+++ b/tests/functional/aarch64/meson.build
@@ -41,6 +41,8 @@ tests_aarch64_system_thorough = [
   'sbsaref',
   'sbsaref_alpine',
   'sbsaref_freebsd',
+  'arm_generic_fdt',
+  'arm_generic_fdt_alpine',
   'smmu',
   'tcg_plugins',
   'tuxrun',
diff --git a/tests/functional/aarch64/test_arm_generic_fdt.py b/tests/functional/aarch64/test_arm_generic_fdt.py
new file mode 100755
index 0000000000..7b0d81196e
--- /dev/null
+++ b/tests/functional/aarch64/test_arm_generic_fdt.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots a kernel and checks the console
+#
+# Based on test_arm_generic_fdt.py
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import QemuSystemTest, Asset
+from qemu_test import wait_for_console_pattern
+from qemu_test import interrupt_interactive_console_until_pattern
+
+from pathlib import Path
+
+def fetch_firmware(test):
+    """
+    Flash volumes generated using:
+
+    Toolchain from Debian:
+    aarch64-linux-gnu-gcc (Debian 12.2.0-14) 12.2.0
+
+    Used components:
+
+    - Trusted Firmware         v2.12.0
+    - Tianocore EDK2           edk2-stable202411
+    - Tianocore EDK2-platforms 4b3530d
+
+    """
+
+    # Secure BootRom (TF-A code)
+    fs0_path = test.uncompress(Aarch64ArmGenericFdtMachine.ASSET_FLASH0,
+                                format="xz")
+
+    # Non-secure rom (UEFI and EFI variables)
+    fs1_path = test.uncompress(Aarch64ArmGenericFdtMachine.ASSET_FLASH1,
+                                format="xz")
+
+    for path in [fs0_path, fs1_path]:
+        with open(path, "ab+") as fd:
+            fd.truncate(256 << 20)  # Expand volumes to 256MiB
+
+    test.vm.add_args(
+        "-blockdev", f"driver=file,filename={fs0_path},node-name=pflash0",
+        "-blockdev", f"driver=file,filename={fs1_path},node-name=pflash1",
+    )
+
+
+class Aarch64ArmGenericFdtMachine(QemuSystemTest):
+    """
+    As firmware runs at a higher privilege level than the hypervisor we
+    can only run these tests under TCG emulation.
+    """
+
+    timeout = 180
+
+    # SBSA_FLASH0.fd.xz
+    ASSET_FLASH0 = Asset(
+            'https://share.linaro.org/downloadFile?id=kyoMLGC9zXa4oA7',
+            '76eb89d42eebe324e4395329f47447cda9ac920aabcf99aca85424609c3384a5')
+
+    # SBSA_FLASH1.fd.xz
+    ASSET_FLASH1 = Asset(
+        'https://share.linaro.org/downloadFile?id=Dj1HRXnDnKtU6Nj',
+        'f850f243bd8dbd49c51e061e0f79f1697546938f454aeb59ab7d93e5f0d412fc')
+
+    current_dir = Path(__file__).resolve().parent
+
+    hw_dtb_path = current_dir.parent.parent/ "data" / "dtb" / "aarch64" / \
+                    "arm-generic-fdt" / "arm64-sbsa-hw.dtb"
+
+    dtb_path = current_dir.parent.parent/ "data" / "dtb" / "aarch64" / \
+                    "arm-generic-fdt" / "arm64-sbsa-guest.dtb"
+
+    def test_edk2_firmware(self):
+
+        self.set_machine('arm-generic-fdt')
+
+        fetch_firmware(self)
+
+        self.vm.add_args('-hw-dtb', str(self.hw_dtb_path))
+        self.vm.add_args('-dtb', str(self.dtb_path))
+
+        self.vm.add_args('-device', "bochs-display")
+        self.vm.add_args('-device', "bochs-display")
+
+        self.vm.add_args('-d', 'guest_errors,unimp')
+        self.vm.set_console()
+        self.vm.launch()
+
+        # TF-A boot sequence:
+        #
+        # https://github.com/ARM-software/arm-trusted-firmware/blob/v2.8.0/\
+        #     docs/design/trusted-board-boot.rst#trusted-board-boot-sequence
+        # https://trustedfirmware-a.readthedocs.io/en/v2.8/\
+        #     design/firmware-design.html#cold-boot
+
+        # AP Trusted ROM
+        wait_for_console_pattern(self, "Booting Trusted Firmware")
+        wait_for_console_pattern(self, "BL1: v2.12.0(release):")
+        wait_for_console_pattern(self, "BL1: Booting BL2")
+
+        # Trusted Boot Firmware
+        wait_for_console_pattern(self, "BL2: v2.12.0(release)")
+        wait_for_console_pattern(self, "Booting BL31")
+
+        # EL3 Runtime Software
+        wait_for_console_pattern(self, "BL31: v2.12.0(release)")
+
+        # Non-trusted Firmware
+        wait_for_console_pattern(self, "UEFI firmware (version 1.0")
+        interrupt_interactive_console_until_pattern(self, "QEMU SBSA-REF Machine")
+
+if __name__ == '__main__':
+    QemuSystemTest.main()
diff --git a/tests/functional/aarch64/test_arm_generic_fdt_alpine.py b/tests/functional/aarch64/test_arm_generic_fdt_alpine.py
new file mode 100755
index 0000000000..10429d21a3
--- /dev/null
+++ b/tests/functional/aarch64/test_arm_generic_fdt_alpine.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots a kernel and checks the console
+#
+# Based on test_arm_generic_fdt_alpine.py
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import QemuSystemTest, Asset, skipSlowTest
+from qemu_test import wait_for_console_pattern
+from test_arm_generic_fdt import fetch_firmware
+
+from pathlib import Path
+
+class Aarch64ArmGenericFdtAlpine(QemuSystemTest):
+
+    ASSET_ALPINE_ISO = Asset(
+        ('https://dl-cdn.alpinelinux.org/'
+         'alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso'),
+        '5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027')
+
+    current_dir = Path(__file__).resolve().parent
+
+    hw_dtb_path = current_dir.parent.parent/ "data" / "dtb" / "aarch64" / \
+        "arm-generic-fdt" / "arm64-sbsa-hw.dtb"
+
+    dtb_path = current_dir.parent.parent/ "data" / "dtb" / "aarch64" / \
+        "arm-generic-fdt" / "arm64-sbsa-guest.dtb"
+
+
+    # This tests the whole boot chain from EFI to Userspace
+    # We only boot a whole OS for the current top level CPU and GIC
+    # Other test profiles should use more minimal boots
+    def boot_alpine_linux(self):
+        self.set_machine('arm-generic-fdt')
+
+        fetch_firmware(self)
+        iso_path = self.ASSET_ALPINE_ISO.fetch()
+
+        self.vm.set_console()
+        self.vm.add_args('-hw-dtb', str(self.hw_dtb_path))
+        self.vm.add_args('-dtb', str(self.dtb_path))
+        self.vm.add_args(
+            "-device", f"ide-cd,bus=ahci.0,unit=0,drive=cdrom0",
+        )
+        self.vm.add_args(
+            "-drive", f"file={iso_path},if=none,id=cdrom0",
+        )
+        self.vm.add_args('-device', "bochs-display")
+        self.vm.add_args('-netdev', "user,id=net0")
+        self.vm.add_args('-device', "e1000e,netdev=net0")
+
+        self.vm.launch()
+        wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17")
+
+    def test_alpine_linux(self):
+        self.boot_alpine_linux()
+
+
+if __name__ == '__main__':
+    QemuSystemTest.main()
-- 
2.43.0