* [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
@ 2026-01-26 17:42 Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 01/27] system/device_tree: update qemu_fdt_getprop/_cell Ruslan Ruslichenko
` (27 more replies)
0 siblings, 28 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
Origin
This feature originates from AMD QEMU repository and was originally developed by AMD.
The sources available by link: https://github.com/Xilinx/qemu.
Motivation
Currently, adding support for a new ARM board in QEMU required writing a dedicated C source file to define the memory map, instantiate devices, and wire interrupts.
Any modification to board configuration requires a corresponding change in the source code and rebuild of the QEMU binary.
This series introduce alternative approach to define board configuration via a Device Tree. The new arm-generic-fdt machine parses DTB at runtime to dynamically construct system topology.
Beyond providing more flexible board creation, the series provides the infrastructure needed to enable Hardware Co-Simulation workflows in future by using Remote-Port protocol:
https://mail.gnu.org/archive/html/qemu-devel/2025-12/msg02121.html.
In mixed simulation environments - where QEMU emulates CPU subsystem and external simulator (such as SystemC) handles custom logic - memory map and interrupt lines may need to be changed depending on external hardware configuration.
Implementation overview
The series implements FDT loading framework itself, which is capable of:
- Parsing and creating device models
- Set properties for them from a device tree
- Connect IRQs for SysBus devices
- Map memory regions for IO device or system RAM.
NOTE: The GPIO wiring for non-SysBus devices would be added in future patch series.
Patch Summary
- hw/core: Add Generic FDT parsing infrastructure and utility functions
- hw/arm: Add the arm-generic-machine model
- hw/core/sysbus: Add IO memory mapping for standard SysBus devices
- system/memory: Allow MemoryRegions to be configured from FDT
- hw/intc: Add FDT support for ARM GIC (IRQ translation and default wiring)
- target/arm: Add FDT support for CPU timers
Testing
Testing performed used Yocto core-image-minimal rootfs and kernel image.
The hardware description Device tree (used with '-hw-dtb' option) can be found here: https://gist.github.com/ruslichenkor/19a1b7d937dbf889190e670cb677e43e#file-arm64-virt-hw-dts
The guest device tree (used with standard '-dtb' option) can be found here: https://gist.github.com/ruslichenkor/19a1b7d937dbf889190e670cb677e43e#file-arm64-virt-guest-dts
Execute command itself:
./qemu-system-aarch64 \
-machine arm-generic-fdt \
-hw-dtb arm64-virt-hw.dtb \
-dtb arm64-virt-guest.dtb \
-cpu cortex-a57 -smp 4 -m 256 \
-drive id=disk0,file=./core-image-minimal-qemuarm64.rootfs-20251218190831.ext4,if=none,format=raw \
-device virtio-blk-device,drive=disk0 \
-kernel ./Image \
-nographic \
-append 'root=/dev/vda console=ttyAMA0 mem=256M swiotlb=0 '
Ruslan Ruslichenko (27):
system/device_tree: update qemu_fdt_getprop/_cell
system/device_tree: add few parsing and traversal helpers
util/log: add log entry for fdt generic utils
hw/core: introduce generic FDT device model registry
hw/core/fdt_generic: implement FDT machine creation helpers
hw/core/fdt_generic: add cpu clusters management
hw/core/fdt_generic_util: implement main fdt parse routine
hw/core/fdt_generic_util: implement fdt_init_qdev
hw/core/fdt_generic_util: initilize qdev properties from fdt
hw/core/fdt_generic_util: actually realize device
hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_MMAP
hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_INTC
hw/core/fdt_generic_util: implement fdt_get_irq/_info API
hw/core/fdt_generic_util: map device memory
hw/core/fdt_generic_util: Connect device irqs
hw/core/fdt_generic_util: realize cpu clusters
hw/core: add fdt_generic to the build
hw/core/machine: add '-hw-dtb' option for machine
hw/arm: add generic ARM machine initialized by FDT
hw/core/sysbus: implement FDT_GENERIC_MMAP_CLASS interface
hw/intc/arm_gic: implement FDT_GENERIC_INTC and fdt support
target/arm/cpu: add fdt support for armv8-timer
qom/object: export object_resolve_link()
system/memory: add setters for MemoryRegion properties
system/memory: implement FDT_GENERIC_MMAP interface
hw/core/fdt_generic_util: initialize serial devices
system/memory: add QOM aliases for fdt support
hw/arm/arm_generic_fdt.c | 166 ++++
hw/arm/boot.c | 8 +-
hw/arm/meson.build | 2 +
hw/arm/raspi4b.c | 8 +-
hw/arm/vexpress.c | 4 +-
hw/arm/xlnx-zcu102.c | 3 +-
hw/core/fdt_generic.c | 286 ++++++
hw/core/fdt_generic_util.c | 1349 ++++++++++++++++++++++++++++
hw/core/machine.c | 19 +
hw/core/meson.build | 2 +
hw/core/sysbus.c | 28 +
hw/intc/arm_gic.c | 32 +
hw/intc/arm_gic_common.c | 50 ++
include/hw/core/boards.h | 1 +
include/hw/core/fdt_generic.h | 127 +++
include/hw/core/fdt_generic_util.h | 140 +++
include/qemu/log.h | 1 +
include/qom/object.h | 12 +
include/system/device_tree.h | 62 +-
qemu-options.hx | 9 +
qom/object.c | 2 +-
system/device_tree.c | 236 ++++-
system/memory.c | 248 ++++-
system/vl.c | 3 +
target/arm/cpu.c | 115 +++
util/log.c | 1 +
26 files changed, 2875 insertions(+), 39 deletions(-)
create mode 100644 hw/arm/arm_generic_fdt.c
create mode 100644 hw/core/fdt_generic.c
create mode 100644 hw/core/fdt_generic_util.c
create mode 100644 include/hw/core/fdt_generic.h
create mode 100644 include/hw/core/fdt_generic_util.h
--
2.43.0
^ permalink raw reply [flat|nested] 42+ messages in thread
* [PATCH 01/27] system/device_tree: update qemu_fdt_getprop/_cell
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-27 1:56 ` David Gibson
2026-01-26 17:42 ` [PATCH 02/27] system/device_tree: add few parsing and traversal helpers Ruslan Ruslichenko
` (26 subsequent siblings)
27 siblings, 1 reply; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Alistair Francis, David Gibson
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Update 'qemu_fdt_getprop' and 'qemu_fdt_getprop_cell'
to support property inheritence from parent node,
in case 'inherit' argument is set.
Update 'qemu_fdt_getprop_cell' to allow accessing
specific cells within multi-cell property array.
Introduced 'qemu_devtreedd_getparent' as it is needed
by both internal and external users.
This will be used by hardware device tree parsing logic.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/arm/boot.c | 8 ++++----
hw/arm/raspi4b.c | 8 ++++----
hw/arm/vexpress.c | 4 ++--
hw/arm/xlnx-zcu102.c | 3 ++-
include/system/device_tree.h | 32 +++++++++++++++++++-------------
system/device_tree.c | 33 ++++++++++++++++++++++++---------
6 files changed, 55 insertions(+), 33 deletions(-)
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index c97d4c4e11..829b8ba12f 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -509,10 +509,10 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
return 0;
}
- acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
- NULL, &error_fatal);
- scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
- NULL, &error_fatal);
+ acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells", 0,
+ false, &error_fatal);
+ scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", 0,
+ false, &error_fatal);
if (acells == 0 || scells == 0) {
fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
goto fail;
diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c
index 3eeb8f447e..66eba5d667 100644
--- a/hw/arm/raspi4b.c
+++ b/hw/arm/raspi4b.c
@@ -42,10 +42,10 @@ static void raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len)
uint32_t acells, scells;
char *nodename = g_strdup_printf("/memory@%" PRIx64, mem_base);
- acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
- NULL, &error_fatal);
- scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
- NULL, &error_fatal);
+ acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells", 0,
+ false, &error_fatal);
+ scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", 0,
+ false, &error_fatal);
/* validated by arm_load_dtb */
g_assert(acells && scells);
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index cc6ae7d4c4..823e316f91 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -486,9 +486,9 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt)
const VEDBoardInfo *daughterboard = (const VEDBoardInfo *)info;
acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
- NULL, &error_fatal);
+ 0, false, &error_fatal);
scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
- NULL, &error_fatal);
+ 0, false, &error_fatal);
intc = find_int_controller(fdt);
if (!intc) {
/* Not fatal, we just won't provide virtio. This will
diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c
index 3ba2736bab..8f67c8be48 100644
--- a/hw/arm/xlnx-zcu102.c
+++ b/hw/arm/xlnx-zcu102.c
@@ -88,7 +88,8 @@ static void zcu102_modify_dtb(const struct arm_boot_info *binfo, void *fdt)
&error_fatal);
for (i = 0; node_path && node_path[i]; i++) {
- r = qemu_fdt_getprop(fdt, node_path[i], "method", &prop_len, NULL);
+ r = qemu_fdt_getprop(fdt, node_path[i], "method", &prop_len,
+ false, NULL);
method_is_hvc = r && !strcmp("hvc", r);
/* Allow HVC based firmware if EL2 is enabled. */
diff --git a/include/system/device_tree.h b/include/system/device_tree.h
index 49d8482ed4..f34b8b7ef9 100644
--- a/include/system/device_tree.h
+++ b/include/system/device_tree.h
@@ -96,27 +96,28 @@ int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
* @node_path: node path
* @property: name of the property to find
* @lenp: fdt error if any or length of the property on success
+ * @inherit: if not found in node, look for property in parent
* @errp: handle to an error object
*
* returns a pointer to the property on success and NULL on failure
*/
const void *qemu_fdt_getprop(void *fdt, const char *node_path,
const char *property, int *lenp,
- Error **errp);
+ bool inherit, Error **errp);
/**
- * qemu_fdt_getprop_cell: retrieve the value of a given 4 byte property
- * @fdt: pointer to the device tree blob
- * @node_path: node path
- * @property: name of the property to find
- * @lenp: fdt error if any or -EINVAL if the property size is different from
- * 4 bytes, or 4 (expected length of the property) upon success.
- * @errp: handle to an error object
- *
- * returns the property value on success
- */
+* qemu_fdt_getprop_cell: retrieve the value of a given 4 byte property
+* @fdt: pointer to the device tree blob
+* @node_path: node path
+* @property: name of the property to find
+* @ofset: the index of 32bit cell to retrive
+* @inherit: if not found in node, look for property in parent
+* @errp: handle to an error object
+*
+* returns the property value on success
+*/
uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
- const char *property, int *lenp,
- Error **errp);
+ const char *property, int offset,
+ bool inherit, Error **errp);
uint32_t qemu_fdt_get_phandle(void *fdt, const char *path);
uint32_t qemu_fdt_alloc_phandle(void *fdt);
int qemu_fdt_nop_node(void *fdt, const char *node_path);
@@ -193,6 +194,11 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
})
+int qemu_devtree_getparent(void *fdt, char *node_path,
+ const char *current);
+
+#define DT_PATH_LENGTH 1024
+
/**
* qemu_fdt_randomize_seeds:
* @fdt: device tree blob
diff --git a/system/device_tree.c b/system/device_tree.c
index 1ea1962984..41bde0ba5a 100644
--- a/system/device_tree.c
+++ b/system/device_tree.c
@@ -429,7 +429,8 @@ int qemu_fdt_setprop_string_array(void *fdt, const char *node_path,
}
const void *qemu_fdt_getprop(void *fdt, const char *node_path,
- const char *property, int *lenp, Error **errp)
+ const char *property, int *lenp,
+ bool inherit, Error **errp)
{
int len;
const void *r;
@@ -439,31 +440,35 @@ const void *qemu_fdt_getprop(void *fdt, const char *node_path,
}
r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
if (!r) {
+ char parent[DT_PATH_LENGTH];
+ if (inherit && !qemu_devtree_getparent(fdt, parent, node_path)) {
+ return qemu_fdt_getprop(fdt, parent, property, lenp, true, errp);
+ }
error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
node_path, property, fdt_strerror(*lenp));
+ return NULL;
}
return r;
}
uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
- const char *property, int *lenp, Error **errp)
+ const char *property, int offset,
+ bool inherit, Error **errp)
{
int len;
const uint32_t *p;
- if (!lenp) {
- lenp = &len;
- }
- p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
+ p = qemu_fdt_getprop(fdt, node_path, property, &len,
+ inherit, errp);
if (!p) {
return 0;
- } else if (*lenp != 4) {
+ }
+ if (len < (offset + 1) * 4) {
error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
__func__, node_path, property);
- *lenp = -EINVAL;
return 0;
}
- return be32_to_cpu(*p);
+ return be32_to_cpu(p[offset]);
}
uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
@@ -633,6 +638,16 @@ out:
return ret;
}
+int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
+{
+ int offset = fdt_path_offset(fdt, current);
+ int parent_offset = fdt_supernode_atdepth_offset(fdt, offset,
+ fdt_node_depth(fdt, offset) - 1, NULL);
+
+ return parent_offset >= 0 ?
+ fdt_get_path(fdt, parent_offset, node_path, DT_PATH_LENGTH) : 1;
+}
+
void qmp_dumpdtb(const char *filename, Error **errp)
{
ERRP_GUARD();
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 02/27] system/device_tree: add few parsing and traversal helpers
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 01/27] system/device_tree: update qemu_fdt_getprop/_cell Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-27 2:19 ` David Gibson
2026-01-26 17:42 ` [PATCH 03/27] util/log: add log entry for fdt generic utils Ruslan Ruslichenko
` (25 subsequent siblings)
27 siblings, 1 reply; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Alistair Francis, David Gibson
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
The patch adds few utility functions for parsing FDT nodes.
The helpers are required for upcoming Hardware device tree
feature.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
include/system/device_tree.h | 30 ++++++
system/device_tree.c | 203 +++++++++++++++++++++++++++++++++++
2 files changed, 233 insertions(+)
diff --git a/include/system/device_tree.h b/include/system/device_tree.h
index f34b8b7ef9..458dbb74b4 100644
--- a/include/system/device_tree.h
+++ b/include/system/device_tree.h
@@ -90,6 +90,13 @@ int qemu_fdt_setprop_string_array(void *fdt, const char *node_path,
int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
const char *property,
const char *target_node_path);
+
+uint64_t qemu_fdt_getprop_sized_cell(void *fdt, const char *node_path,
+ const char *property, int offset,
+ int size, Error **errp);
+const char *qemu_fdt_getprop_string(void *fdt, const char*node_path,
+ const char *property, int cell,
+ bool inherit, Error **errp);
/**
* qemu_fdt_getprop: retrieve the value of a given property
* @fdt: pointer to the device tree blob
@@ -193,9 +200,32 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
qdt_tmp); \
})
+typedef struct QEMUDevtreeProp {
+ char *name;
+ int len;
+ void *value;
+} QEMUDevtreeProp;
+
+/* node queries */
+
+char *qemu_devtree_get_node_name(void *fdt, const char *node_path);
+int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth);
+char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth);
+int qemu_devtree_num_props(void *fdt, const char *node_path);
+QEMUDevtreeProp *qemu_devtree_get_props(void *fdt, const char *node_path);
+/* node getters */
+
+int qemu_devtree_get_node_by_name(void *fdt, char *node_path,
+ const char *cmpname);
+int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle);
int qemu_devtree_getparent(void *fdt, char *node_path,
const char *current);
+int qemu_devtree_get_root_node(void *fdt, char *node_path);
+
+/* misc */
+
+int devtree_get_num_nodes(void *fdt);
#define DT_PATH_LENGTH 1024
diff --git a/system/device_tree.c b/system/device_tree.c
index 41bde0ba5a..7091a4928e 100644
--- a/system/device_tree.c
+++ b/system/device_tree.c
@@ -451,6 +451,50 @@ const void *qemu_fdt_getprop(void *fdt, const char *node_path,
return r;
}
+const char *qemu_fdt_getprop_string(void *fdt, const char*node_path,
+ const char *property, int cell,
+ bool inherit, Error **errp)
+{
+ int len;
+ const void *prop;
+ Error *err = NULL;
+
+ if (!errp) {
+ errp = &err;
+ }
+
+ prop = qemu_fdt_getprop(fdt, node_path, property, &len, inherit, errp);
+ if (*errp) {
+ return NULL;
+ }
+ while (cell) {
+ void *term = memchr(prop, '\0', len);
+ size_t diff;
+
+ if (!term) {
+ error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
+ node_path, property, fdt_strerror(len));
+ return NULL;
+ }
+ diff = term - prop + 1;
+ len -= diff;
+ assert(len >= 0);
+ prop += diff;
+ cell--;
+ }
+
+ if (!len) {
+ return NULL;
+ }
+
+ if (!*(char *)prop) {
+ error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
+ node_path, property, fdt_strerror(len));
+ return NULL;
+ }
+ return prop;
+}
+
uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
const char *property, int offset,
bool inherit, Error **errp)
@@ -471,6 +515,22 @@ uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
return be32_to_cpu(p[offset]);
}
+uint64_t qemu_fdt_getprop_sized_cell(void *fdt, const char *node_path,
+ const char *property, int offset,
+ int size, Error **errp)
+{
+ uint64_t ret = 0;
+ for (; size; size--) {
+ ret <<= 32;
+ ret |= qemu_fdt_getprop_cell(fdt, node_path, property, offset++, false,
+ errp);
+ if (errp && *errp) {
+ return 0;
+ }
+ }
+ return ret;
+}
+
uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
{
uint32_t r;
@@ -638,6 +698,117 @@ out:
return ret;
}
+char *qemu_devtree_get_node_name(void *fdt, const char *node_path)
+{
+ const char *ret = fdt_get_name(fdt, fdt_path_offset(fdt, node_path), NULL);
+ return ret ? strdup(ret) : NULL;
+}
+
+int qemu_devtree_num_props(void *fdt, const char *node_path)
+{
+ int offset = fdt_path_offset(fdt, node_path);
+ int ret = 0;
+
+ for (offset = fdt_first_property_offset(fdt, offset);
+ offset != -FDT_ERR_NOTFOUND;
+ offset = fdt_next_property_offset(fdt, offset)) {
+ ret++;
+ }
+ return ret;
+}
+
+QEMUDevtreeProp *qemu_devtree_get_props(void *fdt, const char *node_path)
+{
+ QEMUDevtreeProp *ret = g_new0(QEMUDevtreeProp,
+ qemu_devtree_num_props(fdt, node_path) + 1);
+ int offset = fdt_path_offset(fdt, node_path);
+ int i = 0;
+
+ for (offset = fdt_first_property_offset(fdt, offset);
+ offset != -FDT_ERR_NOTFOUND;
+ offset = fdt_next_property_offset(fdt, offset)) {
+ const char *propname;
+ const void *val = fdt_getprop_by_offset(fdt, offset, &propname,
+ &ret[i].len);
+
+ ret[i].name = g_strdup(propname);
+ ret[i].value = g_memdup2(val, ret[i].len);
+ i++;
+ }
+ return ret;
+}
+
+static void qemu_devtree_children_info(void *fdt, const char *node_path,
+ int depth, int *num, char **returned_paths) {
+ int offset = fdt_path_offset(fdt, node_path);
+ int root_depth = fdt_node_depth(fdt, offset);
+ int cur_depth = root_depth;
+
+ if (num) {
+ *num = 0;
+ }
+ for (;;) {
+ offset = fdt_next_node(fdt, offset, &cur_depth);
+ if (cur_depth <= root_depth) {
+ break;
+ }
+ if (cur_depth <= root_depth + depth || depth == 0) {
+ if (returned_paths) {
+ returned_paths[*num] = g_malloc0(DT_PATH_LENGTH);
+ fdt_get_path(fdt, offset, returned_paths[*num], DT_PATH_LENGTH);
+ }
+ if (num) {
+ (*num)++;
+ }
+ }
+ }
+}
+
+char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth)
+{
+ int num_children = qemu_devtree_get_num_children(fdt, node_path, depth);
+ char **ret = g_malloc0(sizeof(*ret) * num_children);
+
+ qemu_devtree_children_info(fdt, node_path, depth, &num_children, ret);
+ return ret;
+}
+
+int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth)
+{
+ int ret;
+
+ qemu_devtree_children_info(fdt, node_path, depth, &ret, NULL);
+ return ret;
+}
+
+int qemu_devtree_get_node_by_name(void *fdt, char *node_path,
+ const char *cmpname) {
+ int offset = 0;
+ char *name = NULL;
+
+ do {
+ char *at;
+
+ offset = fdt_next_node(fdt, offset, NULL);
+ name = (void *)fdt_get_name(fdt, offset, NULL);
+ if (!name) {
+ continue;
+ }
+ at = memchr(name, '@', strlen(name));
+ if (!strncmp(name, cmpname, at ? at - name : strlen(name))) {
+ break;
+ }
+ } while (offset > 0);
+ return offset > 0 ?
+ fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH) : 1;
+}
+
+int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle)
+{
+ return fdt_get_path(fdt, fdt_node_offset_by_phandle(fdt, phandle),
+ node_path, DT_PATH_LENGTH);
+}
+
int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
{
int offset = fdt_path_offset(fdt, current);
@@ -648,6 +819,38 @@ int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
fdt_get_path(fdt, parent_offset, node_path, DT_PATH_LENGTH) : 1;
}
+int qemu_devtree_get_root_node(void *fdt, char *node_path)
+{
+ return fdt_get_path(fdt, 0, node_path, DT_PATH_LENGTH);
+}
+
+static void devtree_scan(void *fdt, int *num_nodes)
+{
+ int depth = 0, offset = 0;
+
+ if (num_nodes) {
+ *num_nodes = 0;
+ }
+ for (;;) {
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (num_nodes) {
+ (*num_nodes)++;
+ }
+ if (offset <= 0 || depth <= 0) {
+ break;
+ }
+ }
+}
+
+int devtree_get_num_nodes(void *fdt)
+{
+ int ret;
+
+ devtree_scan(fdt, &ret);
+ return ret;
+}
+
+
void qmp_dumpdtb(const char *filename, Error **errp)
{
ERRP_GUARD();
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 03/27] util/log: add log entry for fdt generic utils
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 01/27] system/device_tree: update qemu_fdt_getprop/_cell Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 02/27] system/device_tree: add few parsing and traversal helpers Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 04/27] hw/core: introduce generic FDT device model registry Ruslan Ruslichenko
` (24 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce dedicated log mask for hardware device
tree processing, to selectively enable diagnostic
for FDT-based machine creation.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
include/qemu/log.h | 1 +
util/log.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/include/qemu/log.h b/include/qemu/log.h
index 7effba4da4..984879fbd3 100644
--- a/include/qemu/log.h
+++ b/include/qemu/log.h
@@ -38,6 +38,7 @@ bool qemu_log_separate(void);
#define CPU_LOG_TB_VPU (1u << 21)
#define LOG_TB_OP_PLUGIN (1u << 22)
#define LOG_INVALID_MEM (1u << 23)
+#define LOG_FDT (1u << 24)
/* Lock/unlock output. */
diff --git a/util/log.c b/util/log.c
index c44d66b5ce..c8310ddc58 100644
--- a/util/log.c
+++ b/util/log.c
@@ -523,6 +523,7 @@ const QEMULogItem qemu_log_items[] = {
"include VPU registers in the 'cpu' logging" },
{ LOG_INVALID_MEM, "invalid_mem",
"log invalid memory accesses" },
+ { LOG_FDT, "fdt", "log Device Tree info." },
{ 0, NULL, NULL },
};
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 04/27] hw/core: introduce generic FDT device model registry
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (2 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 03/27] util/log: add log entry for fdt generic utils Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 05/27] hw/core/fdt_generic: implement FDT machine creation helpers Ruslan Ruslichenko
` (23 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Introduce infrastructure to instantiate device models
directly from the device tree.
This includes the registry of known device models with
associated init functions and key API methods to
operate it.
The MIT license of fdt_generic.c preserved as in original
file from https://github.com/Xilinx/qemu.git repo.
The license for fdt_generic.h derived from matching
fdt_generic.c file.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic.c | 139 ++++++++++++++++++++++++++++++++++
include/hw/core/fdt_generic.h | 78 +++++++++++++++++++
2 files changed, 217 insertions(+)
create mode 100644 hw/core/fdt_generic.c
create mode 100644 include/hw/core/fdt_generic.h
diff --git a/hw/core/fdt_generic.c b/hw/core/fdt_generic.c
new file mode 100644
index 0000000000..d3ab8f9132
--- /dev/null
+++ b/hw/core/fdt_generic.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Tables of FDT device models and their init functions. Keyed by compatibility
+ * strings, device instance names.
+ *
+ * Copyright (c) 2010 PetaLogix Qld Pty Ltd.
+ * Copyright (c) 2010 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/fdt_generic.h"
+#include "hw/core/qdev-properties.h"
+#include "qemu/log.h"
+
+#ifndef FDT_GENERIC_ERR_DEBUG
+#define FDT_GENERIC_ERR_DEBUG 0
+#endif
+#define DB_PRINT(lvl, ...) do { \
+ if (FDT_GENERIC_ERR_DEBUG > (lvl)) { \
+ qemu_log_mask(LOG_FDT, ": %s: ", __func__); \
+ qemu_log_mask(LOG_FDT, ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define FDT_GENERIC_MAX_PATTERN_LEN 1024
+
+typedef struct TableListNode {
+ struct TableListNode *next;
+ char key[FDT_GENERIC_MAX_PATTERN_LEN];
+ FDTInitFn fdt_init;
+ void *opaque;
+} TableListNode;
+
+/* add a node to the table specified by *head_p */
+
+static void add_to_table(
+ FDTInitFn fdt_init,
+ const char *key,
+ void *opaque,
+ TableListNode **head_p)
+{
+ TableListNode *nn = malloc(sizeof(*nn));
+ nn->next = *head_p;
+ strcpy(nn->key, key);
+ nn->fdt_init = fdt_init;
+ nn->opaque = opaque;
+ *head_p = nn;
+}
+
+/* FIXME: add return codes that differentiate between not found and error */
+
+/*
+ * search a table for a key string and call the fdt init function if found.
+ * Returns 0 if a match is found, 1 otherwise
+ */
+
+static int fdt_init_search_table(
+ char *node_path,
+ FDTMachineInfo *fdti,
+ const char *key, /* string to match */
+ TableListNode **head) /* head of the list to search */
+{
+ TableListNode *iter;
+
+ for (iter = *head; iter != NULL; iter = iter->next) {
+ if (!strcmp(key, iter->key)) {
+ if (iter->fdt_init) {
+ return iter->fdt_init(node_path, fdti, iter->opaque);
+ }
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+TableListNode *compat_list_head;
+
+void add_to_compat_table(FDTInitFn fdt_init, const char *compat, void *opaque)
+{
+ add_to_table(fdt_init, compat, opaque, &compat_list_head);
+}
+
+int fdt_init_compat(char *node_path, FDTMachineInfo *fdti, const char *compat)
+{
+ return fdt_init_search_table(node_path, fdti, compat, &compat_list_head);
+}
+
+TableListNode *inst_bind_list_head;
+
+void add_to_inst_bind_table(FDTInitFn fdt_init, const char *name, void *opaque)
+{
+ add_to_table(fdt_init, name, opaque, &inst_bind_list_head);
+}
+
+int fdt_init_inst_bind(char *node_path, FDTMachineInfo *fdti,
+ const char *name)
+{
+ return fdt_init_search_table(node_path, fdti, name, &inst_bind_list_head);
+}
+
+static void dump_table(TableListNode *head)
+{
+ TableListNode *iter;
+
+ for (iter = head; iter != NULL; iter = iter->next) {
+ printf("key : %s, opaque data %p\n", head->key, head->opaque);
+ }
+}
+
+void dump_compat_table(void)
+{
+ printf("FDT COMPATIBILITY TABLE:\n");
+ dump_table(compat_list_head);
+}
+
+void dump_inst_bind_table(void)
+{
+ printf("FDT INSTANCE BINDING TABLE:\n");
+ dump_table(inst_bind_list_head);
+}
diff --git a/include/hw/core/fdt_generic.h b/include/hw/core/fdt_generic.h
new file mode 100644
index 0000000000..614a234868
--- /dev/null
+++ b/include/hw/core/fdt_generic.h
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Tables of FDT device models and their init functions. Keyed by compatibility
+ * strings, device instance names.
+ */
+
+#ifndef FDT_GENERIC_H
+#define FDT_GENERIC_H
+
+#include "qemu/help-texts.h"
+#include "system/device_tree.h"
+
+typedef struct FDTMachineInfo FDTMachineInfo;
+
+typedef int (*FDTInitFn)(char *, FDTMachineInfo *, void *);
+
+/* associate a FDTInitFn with a FDT compatibility */
+
+void add_to_compat_table(FDTInitFn, const char *, void *);
+
+/*
+ * try and find a device model for a particular compatibility. If found,
+ * the FDTInitFn associated with the compat will be called and 0 will
+ * be returned. Returns non-zero on not found or error
+ */
+
+int fdt_init_compat(char *, FDTMachineInfo *, const char *);
+
+/* same as above, but associates with a FDT node name (rather than compat) */
+
+void add_to_inst_bind_table(FDTInitFn, const char *, void *);
+int fdt_init_inst_bind(char *, FDTMachineInfo *, const char *);
+
+void dump_compat_table(void);
+void dump_inst_bind_table(void);
+
+/*
+ * Called from FDTInitFn's to inform the framework that a dependency is
+ * unresolved and the calling context needs to wait for another device to
+ * instantiate first. The calling thread will suspend until a change in state
+ * in the argument fdt machine is detected.
+ */
+
+void fdt_init_yield(FDTMachineInfo *);
+
+/* set, check and get per device opaques. Keyed by fdt node_paths */
+
+void fdt_init_set_opaque(FDTMachineInfo *fdti, char *node_path, void *opaque);
+int fdt_init_has_opaque(FDTMachineInfo *fdti, char *node_path);
+void *fdt_init_get_opaque(FDTMachineInfo *fdti, char *node_path);
+
+/* statically register a FDTInitFn as being associate with a compatibility */
+
+#define fdt_register_compatibility_opaque(function, compat, n, opaque) \
+static void __attribute__((constructor)) \
+function ## n ## _register_imp(void) { \
+ add_to_compat_table(function, compat, opaque); \
+}
+
+#define fdt_register_compatibility_n(function, compat, n) \
+fdt_register_compatibility_opaque(function, compat, n, NULL)
+
+#define fdt_register_compatibility(function, compat) \
+fdt_register_compatibility_n(function, compat, 0)
+
+#define fdt_register_instance_opaque(function, inst, n, opaque) \
+static void __attribute__((constructor)) \
+function ## n ## _register_imp(void) { \
+ add_to_inst_bind_table(function, inst, opaque); \
+}
+
+#define fdt_register_instance_n(function, inst, n) \
+fdt_register_instance_opaque(function, inst, n, NULL)
+
+#define fdt_register_instance(function, inst) \
+fdt_register_instance_n(function, inst, 0)
+
+#endif /* FDT_GENERIC_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 05/27] hw/core/fdt_generic: implement FDT machine creation helpers
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (3 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 04/27] hw/core: introduce generic FDT device model registry Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 06/27] hw/core/fdt_generic: add cpu clusters management Ruslan Ruslichenko
` (22 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
The patch adds infrastructure to parse device tree
and associate fdt nodes and created objects.
This will be used during instantiating machine from the
devices tree.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic.c | 70 +++++++++++++++++++++++++++++++++++
include/hw/core/fdt_generic.h | 26 ++++++++++++-
2 files changed, 95 insertions(+), 1 deletion(-)
diff --git a/hw/core/fdt_generic.c b/hw/core/fdt_generic.c
index d3ab8f9132..b3ef0b7d0a 100644
--- a/hw/core/fdt_generic.c
+++ b/hw/core/fdt_generic.c
@@ -28,7 +28,10 @@
#include "qemu/osdep.h"
#include "hw/core/fdt_generic.h"
#include "hw/core/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/coroutine.h"
#include "qemu/log.h"
+#include "system/reset.h"
#ifndef FDT_GENERIC_ERR_DEBUG
#define FDT_GENERIC_ERR_DEBUG 0
@@ -137,3 +140,70 @@ void dump_inst_bind_table(void)
printf("FDT INSTANCE BINDING TABLE:\n");
dump_table(inst_bind_list_head);
}
+
+void fdt_init_yield(FDTMachineInfo *fdti)
+{
+ static int yield_index;
+ int this_yield = yield_index++;
+
+ DB_PRINT(1, "Yield #%d\n", this_yield);
+ qemu_co_queue_wait(fdti->cq, NULL);
+ DB_PRINT(1, "Unyield #%d\n", this_yield);
+}
+
+void fdt_init_set_opaque(FDTMachineInfo *fdti, char *node_path, void *opaque)
+{
+ FDTDevOpaque *dp;
+ for (dp = fdti->dev_opaques;
+ dp->node_path && strcmp(dp->node_path, node_path);
+ dp++)
+ ;
+ if (!dp->node_path) {
+ dp->node_path = strdup(node_path);
+ }
+ dp->opaque = opaque;
+}
+
+int fdt_init_has_opaque(FDTMachineInfo *fdti, char *node_path)
+{
+ FDTDevOpaque *dp;
+ for (dp = fdti->dev_opaques; dp->node_path; dp++) {
+ if (!strcmp(dp->node_path, node_path)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void *fdt_init_get_opaque(FDTMachineInfo *fdti, char *node_path)
+{
+ FDTDevOpaque *dp;
+ for (dp = fdti->dev_opaques; dp->node_path; dp++) {
+ if (!strcmp(dp->node_path, node_path)) {
+ return dp->opaque;
+ }
+ }
+ return NULL;
+}
+
+FDTMachineInfo *fdt_init_new_fdti(void *fdt)
+{
+ FDTMachineInfo *fdti = g_malloc0(sizeof(*fdti));
+ fdti->fdt = fdt;
+ fdti->cq = g_malloc0(sizeof(*(fdti->cq)));
+ qemu_co_queue_init(fdti->cq);
+ fdti->dev_opaques = g_malloc0(sizeof(*(fdti->dev_opaques)) *
+ (devtree_get_num_nodes(fdt) + 1));
+ return fdti;
+}
+
+void fdt_init_destroy_fdti(FDTMachineInfo *fdti)
+{
+ FDTDevOpaque *dp;
+
+ for (dp = fdti->dev_opaques; dp->node_path; dp++) {
+ g_free(dp->node_path);
+ }
+ g_free(fdti->dev_opaques);
+ g_free(fdti);
+}
diff --git a/include/hw/core/fdt_generic.h b/include/hw/core/fdt_generic.h
index 614a234868..a21e3c2440 100644
--- a/include/hw/core/fdt_generic.h
+++ b/include/hw/core/fdt_generic.h
@@ -8,9 +8,33 @@
#define FDT_GENERIC_H
#include "qemu/help-texts.h"
+#include "hw/core/irq.h"
#include "system/device_tree.h"
+#include "qemu/coroutine.h"
+
+typedef struct FDTDevOpaque {
+ char *node_path;
+ void *opaque;
+} FDTDevOpaque;
+
+typedef struct FDTMachineInfo {
+ /* the fdt blob */
+ void *fdt;
+ /* irq descriptors for top level int controller */
+ qemu_irq *irq_base;
+ /* per-device specific opaques */
+ FDTDevOpaque *dev_opaques;
+ /* recheck coroutine queue */
+ CoQueue *cq;
+} FDTMachineInfo;
-typedef struct FDTMachineInfo FDTMachineInfo;
+/*
+ * create a new FDTMachineInfo. The client is responsible for setting irq_base.
+ * Client must call fdt_init_destroy_fdti to cleanup
+ */
+
+FDTMachineInfo *fdt_init_new_fdti(void *fdt);
+void fdt_init_destroy_fdti(FDTMachineInfo *fdti);
typedef int (*FDTInitFn)(char *, FDTMachineInfo *, void *);
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 06/27] hw/core/fdt_generic: add cpu clusters management
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (4 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 05/27] hw/core/fdt_generic: implement FDT machine creation helpers Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 07/27] hw/core/fdt_generic_util: implement main fdt parse routine Ruslan Ruslichenko
` (21 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add support for grouping CPUs into clusters during
FDT machine instantiation.
The clusters are automatically created if CPU parent
is not already an existing cluster.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic.c | 77 +++++++++++++++++++++++++++++++++++
include/hw/core/fdt_generic.h | 13 ++++++
2 files changed, 90 insertions(+)
diff --git a/hw/core/fdt_generic.c b/hw/core/fdt_generic.c
index b3ef0b7d0a..3c28f42491 100644
--- a/hw/core/fdt_generic.c
+++ b/hw/core/fdt_generic.c
@@ -31,6 +31,7 @@
#include "migration/vmstate.h"
#include "qemu/coroutine.h"
#include "qemu/log.h"
+#include "hw/cpu/cluster.h"
#include "system/reset.h"
#ifndef FDT_GENERIC_ERR_DEBUG
@@ -175,6 +176,75 @@ int fdt_init_has_opaque(FDTMachineInfo *fdti, char *node_path)
return 0;
}
+static int get_next_cpu_cluster_id(void)
+{
+ static int i;
+
+ return i++;
+}
+
+void fdt_init_register_user_cpu_cluster(FDTMachineInfo *fdti, Object *cluster)
+{
+ int i = get_next_cpu_cluster_id();
+ DeviceState *dev = DEVICE(cluster);
+ FDTCPUCluster *cl;
+
+ qdev_prop_set_uint32(dev, "cluster-id", i);
+
+ cl = g_new0(FDTCPUCluster, 1);
+ cl->cpu_cluster = cluster;
+ cl->next = fdti->clusters;
+ cl->user = true;
+
+ fdti->clusters = cl;
+
+ DB_PRINT(0, "%s: Registered user-defined cpu cluster with id %d\n",
+ object_get_canonical_path(cluster), i);
+}
+
+static void *fdt_init_add_cpu_cluster(FDTMachineInfo *fdti, char *compat)
+{
+ FDTCPUCluster *cl = g_malloc0(sizeof(*cl));
+ int i = get_next_cpu_cluster_id();
+ char *name = g_strdup_printf("cluster%d", i);
+ Object *obj;
+
+ obj = object_new(TYPE_CPU_CLUSTER);
+ object_property_add_child(object_get_root(), name, OBJECT(obj));
+ qdev_prop_set_uint32(DEVICE(obj), "cluster-id", i);
+
+ cl->cpu_type = g_strdup(compat);
+ cl->cpu_cluster = obj;
+ cl->next = fdti->clusters;
+
+ fdti->clusters = cl;
+
+ g_free(name);
+
+ return obj;
+}
+
+void *fdt_init_get_cpu_cluster(FDTMachineInfo *fdti, Object *parent,
+ char *compat)
+{
+ FDTCPUCluster *cl = fdti->clusters;
+
+ if (object_dynamic_cast(parent, TYPE_CPU_CLUSTER)) {
+ /* The direct parent of this CPU is a CPU cluster. Use it. */
+ return parent;
+ }
+
+ while (cl) {
+ if (!cl->user && !strcmp(compat, cl->cpu_type)) {
+ return cl->cpu_cluster;
+ }
+ cl = cl->next;
+ }
+
+ /* No cluster found so create and return a new one */
+ return fdt_init_add_cpu_cluster(fdti, compat);
+}
+
void *fdt_init_get_opaque(FDTMachineInfo *fdti, char *node_path)
{
FDTDevOpaque *dp;
@@ -199,8 +269,15 @@ FDTMachineInfo *fdt_init_new_fdti(void *fdt)
void fdt_init_destroy_fdti(FDTMachineInfo *fdti)
{
+ FDTCPUCluster *cl = fdti->clusters;
FDTDevOpaque *dp;
+ while (cl) {
+ FDTCPUCluster *tmp = cl;
+ cl = cl->next;
+ g_free(tmp->cpu_type);
+ g_free(tmp);
+ }
for (dp = fdti->dev_opaques; dp->node_path; dp++) {
g_free(dp->node_path);
}
diff --git a/include/hw/core/fdt_generic.h b/include/hw/core/fdt_generic.h
index a21e3c2440..46b7dc084b 100644
--- a/include/hw/core/fdt_generic.h
+++ b/include/hw/core/fdt_generic.h
@@ -17,6 +17,13 @@ typedef struct FDTDevOpaque {
void *opaque;
} FDTDevOpaque;
+typedef struct FDTCPUCluster {
+ char *cpu_type;
+ void *cpu_cluster;
+ void *next;
+ bool user;
+} FDTCPUCluster;
+
typedef struct FDTMachineInfo {
/* the fdt blob */
void *fdt;
@@ -26,6 +33,8 @@ typedef struct FDTMachineInfo {
FDTDevOpaque *dev_opaques;
/* recheck coroutine queue */
CoQueue *cq;
+ /* list of all CPU clusters */
+ FDTCPUCluster *clusters;
} FDTMachineInfo;
/*
@@ -73,6 +82,10 @@ void fdt_init_set_opaque(FDTMachineInfo *fdti, char *node_path, void *opaque);
int fdt_init_has_opaque(FDTMachineInfo *fdti, char *node_path);
void *fdt_init_get_opaque(FDTMachineInfo *fdti, char *node_path);
+void *fdt_init_get_cpu_cluster(FDTMachineInfo *fdti, Object *parent,
+ char *compat);
+void fdt_init_register_user_cpu_cluster(FDTMachineInfo *fdti, Object *cluster);
+
/* statically register a FDTInitFn as being associate with a compatibility */
#define fdt_register_compatibility_opaque(function, compat, n, opaque) \
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 07/27] hw/core/fdt_generic_util: implement main fdt parse routine
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (5 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 06/27] hw/core/fdt_generic: add cpu clusters management Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 08/27] hw/core/fdt_generic_util: implement fdt_init_qdev Ruslan Ruslichenko
` (20 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Implement main fdt_generic_create_machine() routine.
As of now do the following:
- parse each node and subnode in device tree
- check node has correct compatible or device types
- call fdt_init_qdev() which will eventually create
device object.
The MIT license of fdt_generic_util.c preserved as in original
file from https://github.com/Xilinx/qemu.git repo.
The license for fdt_generic_util.h derived from matching
fdt_generic_util.c file.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 242 +++++++++++++++++++++++++++++
include/hw/core/fdt_generic_util.h | 19 +++
2 files changed, 261 insertions(+)
create mode 100644 hw/core/fdt_generic_util.c
create mode 100644 include/hw/core/fdt_generic_util.h
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
new file mode 100644
index 0000000000..4338dc956b
--- /dev/null
+++ b/hw/core/fdt_generic_util.c
@@ -0,0 +1,242 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Utility functions for fdt generic framework
+ *
+ * Copyright (c) 2009 Edgar E. Iglesias.
+ * Copyright (c) 2009 Michal Simek.
+ * Copyright (c) 2011 PetaLogix Qld Pty Ltd.
+ * Copyright (c) 2011 Peter A. G. Crosthwaite <peter.crosthwaite@petalogix.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/fdt_generic_util.h"
+#include "system/memory.h"
+#include "system/address-spaces.h"
+#include "hw/core/sysbus.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "system/system.h"
+#include "system/reset.h"
+#include "qemu/cutils.h"
+#include "qemu/log.h"
+#include "qemu/config-file.h"
+#include "hw/core/boards.h"
+#include "qemu/option.h"
+
+#ifndef FDT_GENERIC_UTIL_ERR_DEBUG
+#define FDT_GENERIC_UTIL_ERR_DEBUG 3
+#endif
+#define DB_PRINT(lvl, ...) do { \
+ if (FDT_GENERIC_UTIL_ERR_DEBUG > (lvl)) { \
+ qemu_log_mask(LOG_FDT, ": %s: ", __func__); \
+ qemu_log_mask(LOG_FDT, ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define DB_PRINT_NP(lvl, ...) do { \
+ if (FDT_GENERIC_UTIL_ERR_DEBUG > (lvl)) { \
+ qemu_log_mask(LOG_FDT, "%s", node_path); \
+ DB_PRINT((lvl), ## __VA_ARGS__); \
+ } \
+} while (0)
+
+/* FIXME: wrap direct calls into libfdt */
+
+#include <libfdt.h>
+#include <stdlib.h>
+
+static int fdt_generic_num_cpus;
+
+static int simple_bus_fdt_init(char *bus_node_path, FDTMachineInfo *fdti);
+
+FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
+{
+ char node_path[DT_PATH_LENGTH];
+ FDTMachineInfo *fdti = fdt_init_new_fdti(fdt);
+
+ fdti->irq_base = cpu_irq;
+
+ /* parse the device tree */
+ if (!qemu_devtree_get_root_node(fdt, node_path)) {
+ memory_region_transaction_begin();
+ fdt_init_set_opaque(fdti, node_path, NULL);
+ simple_bus_fdt_init(node_path, fdti);
+ while (qemu_co_enter_next(fdti->cq, NULL)) {
+ ;
+ }
+ memory_region_transaction_commit();
+ } else {
+ fprintf(stderr, "FDT: ERROR: cannot get root node from device tree %s\n"
+ , node_path);
+ }
+
+ /* FIXME: Populate these from DTS and create CPU clusters. */
+ current_machine->smp.cores = fdt_generic_num_cpus;
+ current_machine->smp.cpus = fdt_generic_num_cpus;
+ current_machine->smp.max_cpus = fdt_generic_num_cpus;
+
+ DB_PRINT(0, "FDT: Device tree scan complete\n");
+ return fdti;
+}
+
+struct FDTInitNodeArgs {
+ char *node_path;
+ char *parent_path;
+ FDTMachineInfo *fdti;
+};
+
+static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat);
+
+static int check_compat(const char *prefix, const char *compat,
+ char *node_path, FDTMachineInfo *fdti)
+{
+ char *compat_prefixed = g_strconcat(prefix, compat, NULL);
+ const int done = !fdt_init_compat(node_path, fdti, compat_prefixed);
+ g_free(compat_prefixed);
+ return done;
+}
+
+static void fdt_init_node(void *args)
+{
+ struct FDTInitNodeArgs *a = args;
+ char *node_path = a->node_path;
+ FDTMachineInfo *fdti = a->fdti;
+ g_free(a);
+
+ simple_bus_fdt_init(node_path, fdti);
+
+ const char *prop;
+ char *all_compats = NULL, *node_name;
+ char *device_type = NULL;
+ int prop_len;
+
+ DB_PRINT_NP(1, "enter\n");
+
+ /* try instance binding first */
+ node_name = qemu_devtree_get_node_name(fdti->fdt, node_path);
+ DB_PRINT_NP(1, "node with name: %s\n", node_name ? node_name : "(none)");
+ if (!node_name) {
+ printf("FDT: ERROR: nameless node: %s\n", node_path);
+ }
+ if (!fdt_init_inst_bind(node_path, fdti, node_name)) {
+ DB_PRINT_NP(0, "instance bind successful\n");
+ goto exit;
+ }
+
+ /* fallback to compatibility binding */
+ prop = qemu_fdt_getprop(fdti->fdt, node_path, "compatible",
+ &prop_len, false, NULL);
+ all_compats = g_memdup2(prop, prop_len);
+ if (all_compats) {
+ char *compat = all_compats;
+ char * const end = &all_compats[prop_len - 1]; /* points to nul */
+
+ while (compat < end) {
+ if (check_compat("compatible:", compat, node_path, fdti)) {
+ goto exit;
+ }
+
+ if (!fdt_init_qdev(node_path, fdti, compat)) {
+ check_compat("postinit:", compat, node_path, fdti);
+ goto exit;
+ }
+
+ /* Scan forward to the end of the current compat. */
+ while (compat < end && *compat) {
+ ++compat;
+ }
+
+ /* Replace nul with space for the debug printf below. */
+ if (compat < end) {
+ *compat++ = ' ';
+ }
+ };
+ } else {
+ DB_PRINT_NP(0, "no compatibility found\n");
+ }
+
+ /*
+ * Try to create the device using device_type property
+ * Not every device tree node has compatible property, so
+ * try with device_type.
+ */
+ prop = qemu_fdt_getprop(fdti->fdt, node_path,
+ "device_type", &prop_len, false, NULL);
+ device_type = g_memdup2(prop, prop_len);
+ if (device_type) {
+ if (check_compat("device_type:", device_type, node_path, fdti)) {
+ goto exit;
+ }
+
+ if (!fdt_init_qdev(node_path, fdti, device_type)) {
+ goto exit;
+ }
+ }
+
+ if (all_compats) {
+ DB_PRINT_NP(0, "FDT: Unsupported peripheral invalidated - "
+ "compatibilities %s\n", all_compats);
+ qemu_fdt_setprop_string(fdti->fdt, node_path, "compatible",
+ "invalidated");
+ }
+exit:
+
+ DB_PRINT_NP(1, "exit\n");
+
+ if (!fdt_init_has_opaque(fdti, node_path)) {
+ fdt_init_set_opaque(fdti, node_path, NULL);
+ }
+ g_free(node_path);
+ g_free(all_compats);
+ g_free(device_type);
+ return;
+}
+
+static int simple_bus_fdt_init(char *node_path, FDTMachineInfo *fdti)
+{
+ int i;
+ int num_children = qemu_devtree_get_num_children(fdti->fdt, node_path,
+ 1);
+ char **children;
+
+ if (num_children == 0) {
+ return 0;
+ }
+ children = qemu_devtree_get_children(fdti->fdt, node_path, 1);
+
+ DB_PRINT_NP(num_children ? 0 : 1, "num child devices: %d\n", num_children);
+
+ for (i = 0; i < num_children; i++) {
+ struct FDTInitNodeArgs *init_args = g_malloc0(sizeof(*init_args));
+ init_args->node_path = children[i];
+ init_args->fdti = fdti;
+ qemu_coroutine_enter(qemu_coroutine_create(fdt_init_node, init_args));
+ }
+
+ g_free(children);
+ return 0;
+}
+
+static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
+{
+ return 0;
+}
+
diff --git a/include/hw/core/fdt_generic_util.h b/include/hw/core/fdt_generic_util.h
new file mode 100644
index 0000000000..e18c4f9c8b
--- /dev/null
+++ b/include/hw/core/fdt_generic_util.h
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: MIT
+
+#ifndef FDT_GENERIC_UTIL_H
+#define FDT_GENERIC_UTIL_H
+
+#include "qemu/help-texts.h"
+#include "fdt_generic.h"
+#include "system/memory.h"
+#include "qom/object.h"
+
+/*
+ * create a fdt_generic machine. the top level cpu irqs are required for
+ * systems instantiating interrupt devices. The client is responsible for
+ * destroying the returned FDTMachineInfo (using fdt_init_destroy_fdti)
+ */
+
+FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq);
+
+#endif /* FDT_GENERIC_UTIL_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 08/27] hw/core/fdt_generic_util: implement fdt_init_qdev
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (6 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 07/27] hw/core/fdt_generic_util: implement main fdt parse routine Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 09/27] hw/core/fdt_generic_util: initilize qdev properties from fdt Ruslan Ruslichenko
` (19 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Implement main routine to create device object from
compat.
The following steps implemented:
- parse node compatible and device types
- try to create object from compat
- set correct parent based on device tree.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 224 +++++++++++++++++++++++++++++++++++++
1 file changed, 224 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 4338dc956b..74310e9eb3 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -40,6 +40,7 @@
#include "qemu/config-file.h"
#include "hw/core/boards.h"
#include "qemu/option.h"
+#include "hw/cpu/cluster.h"
#ifndef FDT_GENERIC_UTIL_ERR_DEBUG
#define FDT_GENERIC_UTIL_ERR_DEBUG 3
@@ -235,8 +236,231 @@ static int simple_bus_fdt_init(char *node_path, FDTMachineInfo *fdti)
return 0;
}
+/* FIXME: figure out a real solution to this */
+
+#define DIGIT(a) ((a) >= '0' && (a) <= '9')
+#define LOWER_CASE(a) ((a) >= 'a' && (a) <= 'z')
+
+static void trim_version(char *x)
+{
+ long result;
+
+ for (;;) {
+ x = strchr(x, '-');
+ if (!x) {
+ return;
+ }
+ if (DIGIT(x[1])) {
+ /* Try to trim Xilinx version suffix */
+ const char *p;
+
+ qemu_strtol(x + 1, &p, 0, &result);
+
+ if (*p == '.') {
+ *x = 0;
+ return;
+ } else if (*p == 0) {
+ return;
+ }
+ } else if (x[1] == 'r' && x[3] == 'p') {
+ /* Try to trim ARM version suffix */
+ if (DIGIT(x[2]) && DIGIT(x[4])) {
+ *x = 0;
+ return;
+ }
+ }
+ x++;
+ }
+}
+
+static void substitute_char(char *s, char a, char b)
+{
+ for (;;) {
+ s = strchr(s, a);
+ if (!s) {
+ return;
+ }
+ *s = b;
+ s++;
+ }
+}
+
+static inline const char *trim_vendor(const char *s)
+{
+ /* FIXME: be more intelligent */
+ const char *ret = memchr(s, ',', strlen(s));
+ return ret ? ret + 1 : s;
+}
+
+static Object *fdt_create_from_compat(const char *compat, char **dev_type)
+{
+ Object *ret = NULL;
+ char *c = g_strdup(compat);
+
+ /* Try to create the object */
+ ret = object_new(c);
+
+ if (!ret) {
+ /* Trim the version off the end and try again */
+ trim_version(c);
+ ret = object_new(c);
+
+ if (!ret) {
+ /* Replace commas with full stops */
+ substitute_char(c, ',', '.');
+ ret = object_new(c);
+ }
+ }
+
+ if (!ret) {
+ /*
+ * Restart with the orginal string and now replace commas with full
+ * stops and try again. This means that versions are still included.
+ */
+ g_free(c);
+ c = g_strdup(compat);
+ substitute_char(c, ',', '.');
+ ret = object_new(c);
+ }
+
+ if (dev_type) {
+ *dev_type = c;
+ } else {
+ g_free(c);
+ }
+
+ if (!ret) {
+ const char *no_vendor = trim_vendor(compat);
+
+ if (no_vendor != compat) {
+ return fdt_create_from_compat(no_vendor, dev_type);
+ }
+ }
+ return ret;
+}
+
+/*
+ * Error handler for device creation failure.
+ *
+ * We look for qemu-fdt-abort-on-error properties up the tree.
+ * If we find one, we abort with the provided error message.
+ */
+static void fdt_dev_error(FDTMachineInfo *fdti, char *node_path, char *compat)
+{
+ const char *abort_on_error;
+ const char *warn_on_error;
+
+ warn_on_error = qemu_fdt_getprop(fdti->fdt, node_path,
+ "qemu-fdt-warn-on-error", 0,
+ true, NULL);
+ abort_on_error = qemu_fdt_getprop(fdti->fdt, node_path,
+ "qemu-fdt-abort-on-error", 0,
+ true, NULL);
+ if (warn_on_error) {
+ if (strncmp("device_type", compat, strlen("device_type"))) {
+ warn_report("%s: %s", compat, warn_on_error);
+ }
+ }
+
+ if (abort_on_error) {
+ error_report("Failed to create %s", compat);
+ error_setg(&error_fatal, "%s", abort_on_error);
+ }
+}
+
static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
{
+ Object *dev, *parent;
+ char *dev_type = NULL;
+ char parent_node_path[DT_PATH_LENGTH];
+
+ if (!compat) {
+ return 1;
+ }
+ dev = fdt_create_from_compat(compat, &dev_type);
+ if (!dev) {
+ DB_PRINT_NP(1, "no match found for %s\n", compat);
+ fdt_dev_error(fdti, node_path, compat);
+ return 1;
+ }
+ DB_PRINT_NP(1, "matched compat %s\n", compat);
+
+ /* Do this super early so fdt_generic_num_cpus is correct ASAP */
+ if (object_dynamic_cast(dev, TYPE_CPU)) {
+ fdt_generic_num_cpus++;
+ DB_PRINT_NP(0, "is a CPU - total so far %d\n", fdt_generic_num_cpus);
+ }
+
+ if (qemu_devtree_getparent(fdti->fdt, parent_node_path, node_path)) {
+ abort();
+ }
+ while (!fdt_init_has_opaque(fdti, parent_node_path) &&
+ !object_dynamic_cast(dev, TYPE_CPU)) {
+ fdt_init_yield(fdti);
+ }
+
+ parent = fdt_init_get_opaque(fdti, parent_node_path);
+
+ if (object_dynamic_cast(dev, TYPE_CPU)) {
+ parent = fdt_init_get_cpu_cluster(fdti, parent, compat);
+ }
+
+ if (dev->parent) {
+ DB_PRINT_NP(0, "Node already parented - skipping node\n");
+ } else if (parent) {
+ DB_PRINT_NP(1, "parenting node\n");
+ object_property_add_child(OBJECT(parent),
+ qemu_devtree_get_node_name(fdti->fdt, node_path),
+ OBJECT(dev));
+ if (object_dynamic_cast(dev, TYPE_DEVICE)) {
+ Object *parent_bus = parent;
+ unsigned int depth = 0;
+
+ DB_PRINT_NP(1, "bus parenting node\n");
+ /* Look for an FDT ancestor that is a Bus. */
+ while (parent_bus && !object_dynamic_cast(parent_bus, TYPE_BUS)) {
+ /*
+ * Assert against insanely deep hierarchies which are an
+ * indication of loops.
+ */
+ assert(depth < 4096);
+
+ parent_bus = parent_bus->parent;
+ depth++;
+ }
+
+ if (!parent_bus
+ && object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+ /*
+ * Didn't find any bus. Use the default sysbus one.
+ * This allows ad-hoc busses belonging to sysbus devices to be
+ * visible to -device bus=x.
+ */
+ parent_bus = OBJECT(sysbus_get_default());
+ }
+
+ if (parent_bus) {
+ qdev_set_parent_bus(DEVICE(dev), BUS(parent_bus),
+ &error_abort);
+ }
+ }
+ } else {
+ DB_PRINT_NP(1, "orphaning node\n");
+ if (object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+ qdev_set_parent_bus(DEVICE(dev), BUS(sysbus_get_default()),
+ &error_abort);
+ }
+
+ /* FIXME: Make this go away (centrally) */
+ object_property_add_child(
+ object_get_root(),
+ qemu_devtree_get_node_name(fdti->fdt, node_path),
+ OBJECT(dev));
+ }
+ fdt_init_set_opaque(fdti, node_path, dev);
+
+ g_free(dev_type);
+
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 09/27] hw/core/fdt_generic_util: initilize qdev properties from fdt
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (7 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 08/27] hw/core/fdt_generic_util: implement fdt_init_qdev Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 10/27] hw/core/fdt_generic_util: actually realize device Ruslan Ruslichenko
` (18 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
The patch adds possibility to read and parse device tree
node properties and set them to newly created object.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 282 +++++++++++++++++++++++++++++++++++++
1 file changed, 282 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 74310e9eb3..c1d6ed9105 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -40,6 +40,7 @@
#include "qemu/config-file.h"
#include "hw/core/boards.h"
#include "qemu/option.h"
+#include "hw/core/qdev-properties.h"
#include "hw/cpu/cluster.h"
#ifndef FDT_GENERIC_UTIL_ERR_DEBUG
@@ -59,6 +60,8 @@
} \
} while (0)
+#define PROP_ARRAY_LEN_PREFIX "len-"
+
/* FIXME: wrap direct calls into libfdt */
#include <libfdt.h>
@@ -339,6 +342,25 @@ static Object *fdt_create_from_compat(const char *compat, char **dev_type)
return ret;
}
+/*FIXME: roll into device tree functionality */
+
+static inline uint64_t get_int_be(const void *p, int len)
+{
+ switch (len) {
+ case 1:
+ return *((uint8_t *)p);
+ case 2:
+ return be16_to_cpu(*((uint16_t *)p));
+ case 4:
+ return be32_to_cpu(*((uint32_t *)p));
+ case 8:
+ return be32_to_cpu(*((uint64_t *)p));
+ default:
+ fprintf(stderr, "unsupported integer length\n");
+ abort();
+ }
+}
+
/*
* Error handler for device creation failure.
*
@@ -368,10 +390,248 @@ static void fdt_dev_error(FDTMachineInfo *fdti, char *node_path, char *compat)
}
}
+static void fdt_init_qdev_link_prop(Object *obj, ObjectProperty *p,
+ FDTMachineInfo *fdti,
+ const char *node_path,
+ const QEMUDevtreeProp *prop)
+{
+ int len = prop->len;
+ void *val = prop->value;
+ const char *propname = prop->name;
+
+ Object *linked_dev, *proxy;
+ char target_node_path[DT_PATH_LENGTH];
+ g_autofree char *propname_target = g_strconcat(propname, "-target", NULL);
+ Error *errp = NULL;
+
+ if (qemu_devtree_get_node_by_phandle(fdti->fdt, target_node_path,
+ get_int_be(val, len))) {
+ abort();
+ }
+
+ while (!fdt_init_has_opaque(fdti, target_node_path)) {
+ fdt_init_yield(fdti);
+ }
+ linked_dev = fdt_init_get_opaque(fdti, target_node_path);
+
+ proxy = linked_dev ? object_property_get_link(linked_dev,
+ propname_target,
+ &errp) : NULL;
+ if (!errp && proxy) {
+ DB_PRINT_NP(0, "detected proxy object for %s connection\n", propname);
+ linked_dev = proxy;
+ }
+
+ if (!linked_dev) {
+ return;
+ }
+
+ errp = NULL;
+ object_property_set_link(obj, propname, linked_dev, &errp);
+ if (errp) {
+ /* Unable to set the property, maybe it is a memory alias? */
+ MemoryRegion *alias_mr;
+ int offset = len / 2;
+ int region = 0;
+
+ if (len > 4) {
+ region = get_int_be(val + offset, len - offset);
+ }
+
+ alias_mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(linked_dev), region);
+
+ object_property_set_link(obj, propname, OBJECT(alias_mr), &error_abort);
+
+ }
+
+ DB_PRINT_NP(0, "set link %s\n", propname);
+}
+
+static void fdt_init_qdev_scalar_prop(Object *obj, ObjectProperty *p,
+ FDTMachineInfo *fdti,
+ const char *node_path,
+ const QEMUDevtreeProp *prop)
+{
+ const char *propname = trim_vendor(prop->name);
+ void *val = prop->value;
+ int len = prop->len;
+
+ /* FIXME: handle generically using accessors and stuff */
+ if (!strncmp(p->type, "link", 4)) {
+ fdt_init_qdev_link_prop(obj, p, fdti, node_path, prop);
+ return;
+ }
+
+ if (!strcmp(p->type, "uint8") || !strcmp(p->type, "uint16") ||
+ !strcmp(p->type, "uint32") || !strcmp(p->type, "uint64") ||
+ !strcmp(p->type, "int8") || !strcmp(p->type, "int16") ||
+ !strcmp(p->type, "int32") || !strcmp(p->type, "int64")) {
+ object_property_set_int(obj, propname,
+ get_int_be(val, len), &error_abort);
+ DB_PRINT_NP(0, "set property %s to 0x%llx\n", propname,
+ (unsigned long long)get_int_be(val, len));
+ return;
+ }
+
+ if (!strcmp(p->type, "boolean") || !strcmp(p->type, "bool")) {
+ object_property_set_bool(obj, propname,
+ !!get_int_be(val, len), &error_abort);
+ DB_PRINT_NP(0, "set property %s to %s\n", propname,
+ get_int_be(val, len) ? "true" : "false");
+ return;
+ }
+
+ if (!strcmp(p->type, "string") || !strcmp(p->type, "str")) {
+ object_property_set_str(obj, propname,
+ (const char *)val, &error_abort);
+ DB_PRINT_NP(0, "set property %s to %s\n", propname, (const char *)val);
+ return;
+ }
+
+ DB_PRINT_NP(0, "WARNING: property is of unknown type\n");
+}
+
+static size_t fdt_array_elem_len(FDTMachineInfo *fdti,
+ const char *node_path,
+ const char *propname)
+{
+ g_autofree char *elem_cells_propname = NULL;
+ Error *err = NULL;
+ uint32_t elem_cells;
+
+ /*
+ * Default element size to 1 uint32_t cell, unless it is explicitly
+ * given in the same FDT node (not inherited).
+ */
+ elem_cells_propname = g_strconcat("#", propname, "-cells", NULL);
+ elem_cells = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+ elem_cells_propname, 0, false, &err);
+
+ return (err ? 1 : elem_cells) * 4;
+}
+
+static ObjectProperty *fdt_array_elem_prop(Object *obj,
+ const char *propname, int k)
+{
+ g_autofree char *elem_name = g_strdup_printf("%s[%d]", propname, k);
+
+ return object_property_find(obj, elem_name);
+}
+
+static char *fdt_array_elem_type(ObjectProperty *e)
+{
+ size_t n = strlen(e->type);
+
+ if (strncmp(e->type, "link", 4)) {
+ return NULL;
+ }
+ if (n > 6) {
+ return g_strndup(&e->type[5], (n - 6));
+ }
+
+ return g_strdup(TYPE_OBJECT);
+}
+
+static ObjectProperty *fdt_array_link_elem_prop(Object *obj, ObjectProperty *e,
+ const char *elem_link_type)
+{
+ /*
+ * Starting QEMU 8.2.0, the array-scheme has changed to be a single
+ * property of list. See:
+ * https://mail.gnu.org/archive/html/qemu-devel/2023-09/msg01832.html
+ *
+ * Thus, fdt_init_qdev_array_prop() below will have to be changed
+ * substantially.
+ *
+ * So for now, use a temporary hack to work around DEFINE_PROP_ARRAY() not
+ * creating the proper elements of type "link" (see set_prop_arraylen()).
+ */
+ g_autofree char *elem_name = g_strdup(e->name);
+ void *elem_ptr = object_field_prop_ptr(obj, e->opaque);
+
+ object_property_del(obj, elem_name);
+ e = object_property_add_link(obj, elem_name,
+ elem_link_type, elem_ptr,
+ qdev_prop_allow_set_link_before_realize,
+ OBJ_PROP_LINK_STRONG);
+
+ return e;
+}
+
+static void fdt_init_qdev_array_prop(Object *obj,
+ FDTMachineInfo *fdti,
+ const char *node_path,
+ QEMUDevtreeProp *prop)
+{
+ const char *propname = trim_vendor(prop->name);
+ int nr = prop->len;
+ Error *local_err = NULL;
+ char *len_name;
+ uint32_t elem_len;
+ g_autofree char *elem_link_type = NULL;
+ ObjectProperty *e;
+
+ if (!prop->value || !nr) {
+ return;
+ }
+
+ elem_len = fdt_array_elem_len(fdti, node_path, propname);
+ if (nr % elem_len) {
+ return;
+ }
+
+ nr /= elem_len;
+
+ /*
+ * Fail gracefully on setting the 'len-' property, due to:
+ * 1. The property does not exist, or
+ * 2. The property is not integer type, or
+ * 3. The property has been set, e.g., by the '-global' cmd option
+ */
+ len_name = g_strconcat(PROP_ARRAY_LEN_PREFIX, propname, NULL);
+ object_property_set_int(obj, len_name, nr, &local_err);
+ g_free(len_name);
+
+ if (local_err) {
+ error_free(local_err);
+ return;
+ }
+
+ e = fdt_array_elem_prop(obj, propname, 0);
+ if (!e) {
+ return;
+ }
+
+ elem_link_type = fdt_array_elem_type(e);
+
+ while (nr--) {
+ QEMUDevtreeProp q;
+
+ e = fdt_array_elem_prop(obj, propname, nr);
+ if (!e) {
+ continue;
+ }
+
+ q = (QEMUDevtreeProp){.name = e->name,
+ .value = prop->value + nr * elem_len,
+ .len = elem_len,
+ };
+
+ if (elem_link_type) {
+ e = fdt_array_link_elem_prop(obj, e, elem_link_type);
+ q.name = e->name;
+ fdt_init_qdev_link_prop(obj, e, fdti, node_path, &q);
+ } else {
+ fdt_init_qdev_scalar_prop(obj, e, fdti, node_path, &q);
+ }
+ }
+}
+
static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
{
Object *dev, *parent;
char *dev_type = NULL;
+ QEMUDevtreeProp *prop, *props;
char parent_node_path[DT_PATH_LENGTH];
if (!compat) {
@@ -459,6 +719,28 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
}
fdt_init_set_opaque(fdti, node_path, dev);
+ props = qemu_devtree_get_props(fdti->fdt, node_path);
+ for (prop = props; prop->name; prop++) {
+ const char *propname = trim_vendor(prop->name);
+ ObjectProperty *p = NULL;
+
+ p = object_property_find(OBJECT(dev), propname);
+ if (p) {
+ DB_PRINT_NP(1, "matched property: %s of type %s, len %d\n",
+ propname, p->type, prop->len);
+ }
+ if (!p) {
+ fdt_init_qdev_array_prop(dev, fdti, node_path, prop);
+ continue;
+ }
+
+ if (!strcmp(propname, "type")) {
+ continue;
+ }
+
+ fdt_init_qdev_scalar_prop(OBJECT(dev), p, fdti, node_path, prop);
+ }
+
g_free(dev_type);
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 10/27] hw/core/fdt_generic_util: actually realize device
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (8 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 09/27] hw/core/fdt_generic_util: initilize qdev properties from fdt Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 11/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_MMAP Ruslan Ruslichenko
` (17 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Actually realize objects with TYPE_DEVICE after main
allocations done and properties set for qdev.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index c1d6ed9105..2c3f4d6b09 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -741,6 +741,26 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
fdt_init_qdev_scalar_prop(OBJECT(dev), p, fdti, node_path, prop);
}
+ if (object_dynamic_cast(dev, TYPE_DEVICE)) {
+ const char *short_name = qemu_devtree_get_node_name(fdti->fdt,
+ node_path);
+
+ /* Regular TYPE_DEVICE houskeeping */
+ DB_PRINT_NP(0, "Short naming node: %s\n", short_name);
+ (DEVICE(dev))->id = g_strdup(short_name);
+
+ if (object_dynamic_cast(dev, TYPE_CPU_CLUSTER)) {
+ /*
+ * CPU clusters must be realized at the end to make sure all child
+ * CPUs are parented.
+ */
+ fdt_init_register_user_cpu_cluster(fdti, OBJECT(dev));
+ } else {
+ object_property_set_bool(OBJECT(dev), "realized", true,
+ &error_fatal);
+ }
+ }
+
g_free(dev_type);
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 11/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_MMAP
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (9 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 10/27] hw/core/fdt_generic_util: actually realize device Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 12/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_INTC Ruslan Ruslichenko
` (16 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add new FDT_GENERIC_MMAP class and interface to allow
devices to register own handlers for mapping memory
regions in device tree's 'reg' property.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 11 ++++++++
include/hw/core/fdt_generic_util.h | 44 ++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 2c3f4d6b09..c0e974e53a 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -766,3 +766,14 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
return 0;
}
+static const TypeInfo fdt_generic_mmap_info = {
+ .name = TYPE_FDT_GENERIC_MMAP,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(FDTGenericMMapClass),
+};
+
+static void fdt_generic_register_types(void)
+{
+ type_register_static(&fdt_generic_mmap_info);
+}
+type_init(fdt_generic_register_types)
diff --git a/include/hw/core/fdt_generic_util.h b/include/hw/core/fdt_generic_util.h
index e18c4f9c8b..38086ec1e4 100644
--- a/include/hw/core/fdt_generic_util.h
+++ b/include/hw/core/fdt_generic_util.h
@@ -16,4 +16,48 @@
FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq);
+#define TYPE_FDT_GENERIC_MMAP "fdt-generic-mmap"
+
+#define FDT_GENERIC_MMAP_CLASS(klass) \
+ OBJECT_CLASS_CHECK(FDTGenericMMapClass, (klass), TYPE_FDT_GENERIC_MMAP)
+#define FDT_GENERIC_MMAP_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(FDTGenericMMapClass, (obj), TYPE_FDT_GENERIC_MMAP)
+#define FDT_GENERIC_MMAP(obj) \
+ INTERFACE_CHECK(FDTGenericMMap, (obj), TYPE_FDT_GENERIC_MMAP)
+
+typedef struct FDTGenericMMap {
+ /*< private >*/
+ Object parent_obj;
+} FDTGenericMMap;
+
+/*
+ * The number of "things" in the tuple. Not to be confused with the cell length
+ * of the tuple (which is variable based on content
+ */
+
+#define FDT_GENERIC_REG_TUPLE_LENGTH 4
+
+typedef struct FDTGenericRegPropInfo {
+ int n;
+ union {
+ struct {
+ uint64_t *a;
+ uint64_t *s;
+ uint64_t *b;
+ uint64_t *p;
+ };
+ uint64_t *x[FDT_GENERIC_REG_TUPLE_LENGTH];
+ };
+ Object **parents;
+} FDTGenericRegPropInfo;
+
+typedef struct FDTGenericMMapClass {
+ /*< private >*/
+ InterfaceClass parent_class;
+
+ /*< public >*/
+ bool (*parse_reg)(FDTGenericMMap *obj, FDTGenericRegPropInfo info,
+ Error **errp);
+} FDTGenericMMapClass;
+
#endif /* FDT_GENERIC_UTIL_H */
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 12/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_INTC
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (10 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 11/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_MMAP Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 13/27] hw/core/fdt_generic_util: implement fdt_get_irq/_info API Ruslan Ruslichenko
` (15 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add TYPE_FDT_GENERIC_INTC class and interface to allow
interrupt controllers to register callback which can
be used to get IRQ's from this controller.
Optionally, interrupt controllers may also provide
auto_parent() callabck which can be used to attach
it to corresponding CPU's or other interrupt parent.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 7 ++++
include/hw/core/fdt_generic_util.h | 59 ++++++++++++++++++++++++++++++
2 files changed, 66 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index c0e974e53a..b22c612e0f 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -766,6 +766,12 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
return 0;
}
+static const TypeInfo fdt_generic_intc_info = {
+ .name = TYPE_FDT_GENERIC_INTC,
+ .parent = TYPE_INTERFACE,
+ .class_size = sizeof(FDTGenericIntcClass),
+};
+
static const TypeInfo fdt_generic_mmap_info = {
.name = TYPE_FDT_GENERIC_MMAP,
.parent = TYPE_INTERFACE,
@@ -774,6 +780,7 @@ static const TypeInfo fdt_generic_mmap_info = {
static void fdt_generic_register_types(void)
{
+ type_register_static(&fdt_generic_intc_info);
type_register_static(&fdt_generic_mmap_info);
}
type_init(fdt_generic_register_types)
diff --git a/include/hw/core/fdt_generic_util.h b/include/hw/core/fdt_generic_util.h
index 38086ec1e4..c5aaa58dd6 100644
--- a/include/hw/core/fdt_generic_util.h
+++ b/include/hw/core/fdt_generic_util.h
@@ -16,6 +16,65 @@
FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq);
+#define TYPE_FDT_GENERIC_INTC "fdt-generic-intc"
+
+#define FDT_GENERIC_INTC_CLASS(klass) \
+ OBJECT_CLASS_CHECK(FDTGenericIntcClass, (klass), TYPE_FDT_GENERIC_INTC)
+#define FDT_GENERIC_INTC_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(FDTGenericIntcClass, (obj), TYPE_FDT_GENERIC_INTC)
+#define FDT_GENERIC_INTC(obj) \
+ INTERFACE_CHECK(FDTGenericIntc, (obj), TYPE_FDT_GENERIC_INTC)
+
+typedef struct FDTGenericIntc {
+ /*< private >*/
+ Object parent_obj;
+} FDTGenericIntc;
+
+typedef struct FDTGenericIntcClass {
+ /*< private >*/
+ InterfaceClass parent_class;
+
+ /*< public >*/
+ /**
+ * get irq - Based on the FDT generic interrupt binding for this device
+ * grab the irq(s) for the given interrupt cells description. In some device
+ * tree bindings (E.G. ARM GIC with its PPI) a single interrupt cell-tuple
+ * can describe more than one connection. So populates an array with all
+ * relevant IRQs.
+ *
+ * @obj - interrupt controller to get irqs input for ("interrupt-parent")
+ * @irqs - array to populate with irqs (must be >= @max length
+ * @cells - interrupt cells values. Must be >= ncells length
+ * @ncells - number of cells in @cells
+ * @max - maximum number of irqs to return
+ * @errp - Error condition
+ *
+ * @returns the number of interrupts populated in irqs. Undefined on error
+ * (use errp for error checking). If it is valid for the interrupt
+ * controller binding to specify no (or a disabled) connections it may
+ * return 0 as a non-error.
+ */
+
+ int (*get_irq)(FDTGenericIntc *obj, qemu_irq *irqs, uint32_t *cells,
+ int ncells, int max, Error **errp);
+
+ /**
+ * auto_parent. An interrupt controller often infers its own interrupt
+ * parent (usually a CPU or CPU cluster. This function allows an interrupt
+ * controller to implement its own auto-connections. Is called if an
+ * interrupt controller itself (detected via "interrupt-controller") has no
+ * "interrupt-parent" node.
+ *
+ * @obj - Interrupt controller top attempt autoconnection
+ * @errp - Error condition
+ *
+ * FIXME: More arguments need to be added for partial descriptions
+ */
+
+ void (*auto_parent)(FDTGenericIntc *obj, Error **errp);
+
+} FDTGenericIntcClass;
+
#define TYPE_FDT_GENERIC_MMAP "fdt-generic-mmap"
#define FDT_GENERIC_MMAP_CLASS(klass) \
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 13/27] hw/core/fdt_generic_util: implement fdt_get_irq/_info API
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (11 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 12/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_INTC Ruslan Ruslichenko
@ 2026-01-26 17:42 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 14/27] hw/core/fdt_generic_util: map device memory Ruslan Ruslichenko
` (14 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:42 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Both interfaces may be used by devices to get IRQ information
based device tree entry.
Both methods will identify interrupt parent for a device and
retrieve irq entry.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 261 +++++++++++++++++++++++++++++
include/hw/core/fdt_generic_util.h | 18 ++
2 files changed, 279 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index b22c612e0f..d57d9387a1 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -71,6 +71,11 @@ static int fdt_generic_num_cpus;
static int simple_bus_fdt_init(char *bus_node_path, FDTMachineInfo *fdti);
+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);
+
FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
{
char node_path[DT_PATH_LENGTH];
@@ -239,6 +244,262 @@ static int simple_bus_fdt_init(char *node_path, FDTMachineInfo *fdti)
return 0;
}
+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)
+{
+ FDTGenericIntcClass *intc_fdt_class;
+ DeviceState *intc;
+
+ while (!fdt_init_has_opaque(fdti, intc_node_path)) {
+ fdt_init_yield(fdti);
+ }
+ intc = DEVICE(fdt_init_get_opaque(fdti, intc_node_path));
+
+ if (!intc) {
+ goto fail;
+ }
+
+ while (!intc->realized) {
+ fdt_init_yield(fdti);
+ }
+
+ intc_fdt_class = FDT_GENERIC_INTC_GET_CLASS(intc);
+ if (!intc_fdt_class) {
+ goto fail;
+ }
+
+ intc_fdt_class->get_irq(FDT_GENERIC_INTC(intc), ret, cells, num_cells,
+ max, errp);
+
+ return;
+fail:
+ error_setg(errp, "%s", __func__);
+}
+
+static uint32_t imap_cache[32 * 1024];
+static bool imap_cached;
+
+qemu_irq *fdt_get_irq_info(FDTMachineInfo *fdti, char *node_path, int irq_idx,
+ char *info, bool *map_mode) {
+ void *fdt = fdti->fdt;
+ uint32_t intc_phandle, intc_cells, cells[32];
+ char intc_node_path[DT_PATH_LENGTH];
+ qemu_irq *ret = NULL;
+ int i;
+ Error *errp = NULL;
+
+ intc_phandle = qemu_fdt_getprop_cell(fdt, node_path, "interrupt-parent",
+ 0, true, &errp);
+ if (errp) {
+ errp = NULL;
+ intc_cells = qemu_fdt_getprop_cell(fdt, node_path,
+ "#interrupt-cells", 0, true, &errp);
+ *map_mode = true;
+ } else {
+ if (qemu_devtree_get_node_by_phandle(fdt, intc_node_path,
+ intc_phandle)) {
+ goto fail;
+ }
+
+ /* Check if the device is using interrupt-maps */
+ qemu_fdt_getprop_cell(fdt, node_path, "interrupt-map-mask", 0,
+ false, &errp);
+ if (!errp) {
+ errp = NULL;
+ intc_cells = qemu_fdt_getprop_cell(fdt, node_path,
+ "#interrupt-cells", 0,
+ true, &errp);
+ *map_mode = true;
+ } else {
+ errp = NULL;
+ intc_cells = qemu_fdt_getprop_cell(fdt, intc_node_path,
+ "#interrupt-cells", 0,
+ true, &errp);
+ *map_mode = false;
+ }
+ }
+
+ if (errp) {
+ goto fail;
+ }
+
+ DB_PRINT_NP(2, "%s intc_phandle: %d\n", node_path, intc_phandle);
+
+ for (i = 0; i < intc_cells; ++i) {
+ cells[i] = qemu_fdt_getprop_cell(fdt, node_path, "interrupts",
+ intc_cells * irq_idx + i, false, &errp);
+ if (errp) {
+ goto fail;
+ }
+ }
+
+ if (*map_mode) {
+ int k;
+ ret = g_new0(qemu_irq, 1);
+ int num_matches = 0;
+ int len;
+ g_autofree uint32_t *imap_mask = g_new(uint32_t, intc_cells);
+ const uint32_t *prop;
+ uint32_t *imap;
+ bool use_parent = false;
+
+ for (k = 0; k < intc_cells; ++k) {
+ imap_mask[k] = qemu_fdt_getprop_cell(fdt, node_path,
+ "interrupt-map-mask", k + 2,
+ true, &errp);
+ if (errp) {
+ goto fail;
+ }
+ }
+
+ /* Check if the device has an interrupt-map property */
+ prop = qemu_fdt_getprop(fdt, node_path, "interrupt-map", &len,
+ use_parent, &errp);
+ imap = g_memdup2(prop, len);
+ if (!imap || errp) {
+ /*
+ * If the device doesn't have an interrupt-map, try again with
+ * inheritance. This will return the parents interrupt-map
+ */
+ use_parent = true;
+ errp = NULL;
+
+ prop = qemu_fdt_getprop(fdt, node_path, "interrupt-map",
+ &len, use_parent, &errp);
+ if (!imap_cached) {
+ memcpy(imap_cache, prop, len);
+ imap_cached = true;
+ }
+ imap = imap_cache;
+
+ if (errp) {
+ goto fail;
+ }
+ }
+
+ len /= sizeof(uint32_t);
+
+ i = 0;
+ assert(imap);
+ while (i < len) {
+ if (!use_parent) {
+ /*
+ * Only re-sync the interrupt-map when the device has it's
+ * own map, to save time.
+ */
+ prop = qemu_fdt_getprop(fdt, node_path, "interrupt-map", &len,
+ use_parent, &errp);
+ if (imap != imap_cache) {
+ g_free(imap);
+ }
+ imap = g_memdup2(prop, len);
+ if (errp) {
+ goto fail;
+ }
+
+ len /= sizeof(uint32_t);
+ }
+
+ bool match = true;
+ uint32_t new_intc_cells, new_cells[32];
+ i++; i++; /* FIXME: do address cells properly */
+ for (k = 0; k < intc_cells; ++k) {
+ uint32_t map_val = be32_to_cpu(imap[i++]);
+ if ((cells[k] ^ map_val) & imap_mask[k]) {
+ match = false;
+ }
+ }
+ /*
+ * when caching, we hackishly store the number of cells for
+ * the parent in the MSB. +1, so zero MSB means non cachd
+ * and the full lookup is needed.
+ */
+ intc_phandle = be32_to_cpu(imap[i++]);
+ if (intc_phandle & (0xffu << 24)) {
+ new_intc_cells = (intc_phandle >> 24) - 1;
+ } else {
+ if (qemu_devtree_get_node_by_phandle(fdt, intc_node_path,
+ intc_phandle)) {
+ goto fail;
+ }
+ new_intc_cells = qemu_fdt_getprop_cell(fdt, intc_node_path,
+ "#interrupt-cells", 0,
+ false, &errp);
+ imap[i - 1] = cpu_to_be32(intc_phandle |
+ (new_intc_cells + 1) << 24);
+ if (errp) {
+ goto fail;
+ }
+ }
+ for (k = 0; k < new_intc_cells; ++k) {
+ new_cells[k] = be32_to_cpu(imap[i++]);
+ }
+ if (match) {
+ num_matches++;
+ ret = g_renew(qemu_irq, ret, num_matches + 1);
+ if (intc_phandle & (0xffu << 24)) {
+ if (qemu_devtree_get_node_by_phandle(fdt, intc_node_path,
+ intc_phandle &
+ ((1 << 24) - 1))) {
+ goto fail;
+ }
+ }
+
+ DB_PRINT_NP(2, "Getting IRQ information: %s -> 0x%x (%s)\n",
+ node_path, intc_phandle, intc_node_path);
+
+ memset(&ret[num_matches], 0, sizeof(*ret));
+ fdt_get_irq_info_from_intc(fdti, &ret[num_matches - 1],
+ intc_node_path, new_cells,
+ new_intc_cells, 1, NULL);
+ if (info) {
+ sprintf(info, "%s", intc_node_path);
+ info += strlen(info) + 1;
+ }
+ }
+ }
+
+ if (imap != imap_cache) {
+ g_free(imap);
+ }
+ return ret;
+ }
+
+ DB_PRINT_NP(2, "Getting IRQ information: %s -> %s\n",
+ node_path, intc_node_path);
+
+ ret = g_new0(qemu_irq, fdt_generic_num_cpus + 2);
+ fdt_get_irq_info_from_intc(fdti, ret, intc_node_path, cells, intc_cells,
+ fdt_generic_num_cpus, &errp);
+
+ if (errp) {
+ goto fail;
+ }
+
+ /* FIXME: Phase out this info bussiness */
+ if (info) {
+ sprintf(info, "%s", intc_node_path);
+ }
+
+ return ret;
+
+fail:
+ if (errp) {
+ sprintf(info, "%s", error_get_pretty(errp));
+ } else {
+ sprintf(info, "(none)");
+ }
+ return NULL;
+}
+
+qemu_irq *fdt_get_irq(FDTMachineInfo *fdti, char *node_path, int irq_idx,
+ bool *map_mode)
+{
+ return fdt_get_irq_info(fdti, node_path, irq_idx, NULL, map_mode);
+}
+
/* FIXME: figure out a real solution to this */
#define DIGIT(a) ((a) >= '0' && (a) <= '9')
diff --git a/include/hw/core/fdt_generic_util.h b/include/hw/core/fdt_generic_util.h
index c5aaa58dd6..dfc9fa3632 100644
--- a/include/hw/core/fdt_generic_util.h
+++ b/include/hw/core/fdt_generic_util.h
@@ -16,6 +16,24 @@
FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq);
+/*
+ * get an irq for a device. The interrupt parent of a device is idenitified
+ * and the specified irq (by the interrupts device-tree property) is retrieved
+ */
+
+qemu_irq *fdt_get_irq(FDTMachineInfo *fdti, char *node_path, int irq_idx,
+ bool *map_mode);
+
+/*
+ * same as above, but poulates err with non-zero if something goes wrong, and
+ * populates info with a human readable string giving some basic information
+ * about the interrupt connection found (or not found). Both arguments are
+ * optional (i.e. can be NULL)
+ */
+
+qemu_irq *fdt_get_irq_info(FDTMachineInfo *fdti, char *node_path, int irq_idx,
+ char *info, bool *map_mode);
+
#define TYPE_FDT_GENERIC_INTC "fdt-generic-intc"
#define FDT_GENERIC_INTC_CLASS(klass) \
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 14/27] hw/core/fdt_generic_util: map device memory
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (12 preceding siblings ...)
2026-01-26 17:42 ` [PATCH 13/27] hw/core/fdt_generic_util: implement fdt_get_irq/_info API Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 15/27] hw/core/fdt_generic_util: Connect device irqs Ruslan Ruslichenko
` (13 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Parse device mmio regions provided within 'reg' and
'reg-extened' device tree properties.
Call corresponding device interface class handlers to
create memory regions.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 103 +++++++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index d57d9387a1..af20ffd9db 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -622,6 +622,22 @@ static inline uint64_t get_int_be(const void *p, int len)
}
}
+/* FIXME: use structs instead of parallel arrays */
+
+static const char *fdt_generic_reg_size_prop_names[] = {
+ "#address-cells",
+ "#size-cells",
+ "#bus-cells",
+ "#priority-cells",
+};
+
+static const int fdt_generic_reg_cells_defaults[] = {
+ 1,
+ 1,
+ 0,
+ 0,
+};
+
/*
* Error handler for device creation failure.
*
@@ -892,6 +908,8 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
{
Object *dev, *parent;
char *dev_type = NULL;
+ Error *errp = NULL;
+ int i;
QEMUDevtreeProp *prop, *props;
char parent_node_path[DT_PATH_LENGTH];
@@ -1022,6 +1040,91 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
}
}
+ if (object_dynamic_cast(dev, TYPE_SYS_BUS_DEVICE) ||
+ object_dynamic_cast(dev, TYPE_FDT_GENERIC_MMAP)) {
+ FDTGenericRegPropInfo reg = {0};
+ char parent_path[DT_PATH_LENGTH];
+ int cell_idx = 0;
+ bool extended = true;
+
+ qemu_fdt_getprop_cell(fdti->fdt, node_path, "reg-extended", 0, false,
+ &errp);
+ if (errp) {
+ error_free(errp);
+ errp = NULL;
+ extended = false;
+ qemu_devtree_getparent(fdti->fdt, parent_path, node_path);
+ }
+
+ for (reg.n = 0;; reg.n++) {
+ char ph_parent[DT_PATH_LENGTH];
+ const char *pnp = parent_path;
+
+ reg.parents = g_renew(Object *, reg.parents, reg.n + 1);
+ reg.parents[reg.n] = parent;
+
+ if (extended) {
+ int p_ph = qemu_fdt_getprop_cell(fdti->fdt, node_path,
+ "reg-extended", cell_idx++,
+ false, &errp);
+ if (errp) {
+ error_free(errp);
+ errp = NULL;
+ goto exit_reg_parse;
+ }
+ if (qemu_devtree_get_node_by_phandle(fdti->fdt, ph_parent,
+ p_ph)) {
+ goto exit_reg_parse;
+ }
+ while (!fdt_init_has_opaque(fdti, ph_parent)) {
+ fdt_init_yield(fdti);
+ }
+ reg.parents[reg.n] = fdt_init_get_opaque(fdti, ph_parent);
+ pnp = ph_parent;
+ }
+
+ for (i = 0; i < FDT_GENERIC_REG_TUPLE_LENGTH; ++i) {
+ const char *size_prop_name = fdt_generic_reg_size_prop_names[i];
+ int nc = qemu_fdt_getprop_cell(fdti->fdt, pnp, size_prop_name,
+ 0, true, &errp);
+
+ if (errp) {
+ int size_default = fdt_generic_reg_cells_defaults[i];
+
+ DB_PRINT_NP(0, "WARNING: no %s for %s container, assuming "
+ "default of %d\n", size_prop_name, pnp,
+ size_default);
+ nc = size_default;
+ error_free(errp);
+ errp = NULL;
+ }
+
+ reg.x[i] = g_renew(uint64_t, reg.x[i], reg.n + 1);
+ reg.x[i][reg.n] = nc ?
+ qemu_fdt_getprop_sized_cell(fdti->fdt, node_path,
+ extended ? "reg-extended"
+ : "reg",
+ cell_idx, nc, &errp)
+ : 0;
+ cell_idx += nc;
+ if (errp) {
+ goto exit_reg_parse;
+ }
+ }
+ }
+exit_reg_parse:
+
+ if (object_dynamic_cast(dev, TYPE_FDT_GENERIC_MMAP)) {
+ FDTGenericMMapClass *fmc = FDT_GENERIC_MMAP_GET_CLASS(dev);
+ if (fmc->parse_reg) {
+ while (fmc->parse_reg(FDT_GENERIC_MMAP(dev), reg,
+ &error_abort)) {
+ fdt_init_yield(fdti);
+ }
+ }
+ }
+ }
+
g_free(dev_type);
return 0;
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 15/27] hw/core/fdt_generic_util: Connect device irqs
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (13 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 14/27] hw/core/fdt_generic_util: map device memory Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 16/27] hw/core/fdt_generic_util: realize cpu clusters Ruslan Ruslichenko
` (12 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
The patch implements IRQ wiring logic for fdt created
devices.
The wiring performed in two stages. As devices are
created, their IRQs saved in the list.
After all devices processed, fdt_init_all_irqs() goes
through all IRQs and does actual wiring. For IRQs
which have multiple source, shared IRQ handler
allocated.
For interrupt controllers with auto_parent callback
implemented and their fdt node has 'interrupt and
'interrupts-extended' properties empty,
the auto_parent() will be called to connect them
to their interrupt-parent, for example to CPUs.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 146 +++++++++++++++++++++++++++++++++-
include/hw/core/fdt_generic.h | 12 +++
2 files changed, 157 insertions(+), 1 deletion(-)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index af20ffd9db..2f49909a50 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -76,6 +76,88 @@ static void fdt_get_irq_info_from_intc(FDTMachineInfo *fdti, qemu_irq *ret,
uint32_t *cells, uint32_t num_cells,
uint32_t max, Error **errp);
+typedef struct QEMUIRQSharedState {
+ qemu_irq sink;
+ int num;
+ bool (*merge_fn)(bool *, int);
+/* FIXME: remove artificial limit */
+#define MAX_IRQ_SHARED_INPUTS 256
+ bool inputs[MAX_IRQ_SHARED_INPUTS];
+} QEMUIRQSharedState;
+
+static bool qemu_irq_shared_or_handler(bool *inputs, int n)
+{
+ int i;
+
+ assert(n < MAX_IRQ_SHARED_INPUTS);
+
+ for (i = 0; i < n; ++i) {
+ if (inputs[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void qemu_irq_shared_handler(void *opaque, int n, int level)
+{
+ QEMUIRQSharedState *s = opaque;
+
+ assert(n < MAX_IRQ_SHARED_INPUTS);
+ s->inputs[n] = level;
+ qemu_set_irq(s->sink, s->merge_fn(s->inputs, s->num));
+}
+
+static void fdt_init_all_irqs(FDTMachineInfo *fdti)
+{
+ while (fdti->irqs) {
+ FDTIRQConnection *first = fdti->irqs;
+ qemu_irq sink = first->irq;
+ bool (*merge_fn)(bool *, int) = first->merge_fn;
+ int num_sources = 0;
+ FDTIRQConnection *irq;
+
+ for (irq = first; irq; irq = irq->next) {
+ if (irq->irq == sink) { /* Same sink */
+ num_sources++;
+ }
+ }
+ if (num_sources > 1) {
+ QEMUIRQSharedState *s = g_malloc0(sizeof *s);
+ s->sink = sink;
+ s->merge_fn = merge_fn;
+ qemu_irq *sources = qemu_allocate_irqs(qemu_irq_shared_handler, s,
+ num_sources);
+ for (irq = first; irq; irq = irq->next) {
+ if (irq->irq == sink) {
+ char *shared_irq_name = g_strdup_printf("shared-irq-%p",
+ *sources);
+
+ if (irq->merge_fn != merge_fn) {
+ fprintf(stderr, "ERROR: inconsistent IRQ merge fns\n");
+ exit(1);
+ }
+
+ object_property_add_child(OBJECT(irq->dev), shared_irq_name,
+ OBJECT(*sources));
+ g_free(shared_irq_name);
+ irq->irq = *(sources++);
+ s->num++;
+ }
+ }
+ }
+ DB_PRINT(0, "%s: connected to %s irq line %d (%s)\n",
+ first->sink_info ? first->sink_info : "",
+ object_get_canonical_path(OBJECT(first->dev)),
+ first->i, first->name ? first->name : "");
+
+ qdev_connect_gpio_out_named(DEVICE(first->dev), first->name, first->i,
+ first->irq);
+ fdti->irqs = first->next;
+ g_free(first);
+ }
+}
+
FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
{
char node_path[DT_PATH_LENGTH];
@@ -91,6 +173,7 @@ FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
while (qemu_co_enter_next(fdti->cq, NULL)) {
;
}
+ fdt_init_all_irqs(fdti);
memory_region_transaction_commit();
} else {
fprintf(stderr, "FDT: ERROR: cannot get root node from device tree %s\n"
@@ -908,8 +991,9 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
{
Object *dev, *parent;
char *dev_type = NULL;
+ int is_intc;
Error *errp = NULL;
- int i;
+ int i, j;
QEMUDevtreeProp *prop, *props;
char parent_node_path[DT_PATH_LENGTH];
@@ -1125,6 +1209,66 @@ exit_reg_parse:
}
}
+ if (object_dynamic_cast(dev, TYPE_SYS_BUS_DEVICE)) {
+ {
+ int len;
+ fdt_get_property(fdti->fdt, fdt_path_offset(fdti->fdt, node_path),
+ "interrupt-controller", &len);
+ is_intc = len >= 0;
+ DB_PRINT_NP(is_intc ? 0 : 1, "is interrupt controller: %c\n",
+ is_intc ? 'y' : 'n');
+ }
+ /* connect irq */
+ j = 0;
+ for (i = 0;; i++) {
+ char irq_info[6 * 1024];
+ char *irq_info_p = irq_info;
+ bool map_mode;
+ int len = -1;
+ qemu_irq *irqs = fdt_get_irq_info(fdti, node_path, i, irq_info,
+ &map_mode);
+ /* INTCs inferr their top level, if no IRQ connection specified */
+ fdt_get_property(fdti->fdt, fdt_path_offset(fdti->fdt, node_path),
+ "interrupts-extended", &len);
+ if (!irqs && is_intc && i == 0 && len <= 0) {
+ FDTGenericIntc *id = (FDTGenericIntc *)object_dynamic_cast(
+ dev, TYPE_FDT_GENERIC_INTC);
+ FDTGenericIntcClass *idc = FDT_GENERIC_INTC_GET_CLASS(id);
+ if (id && idc->auto_parent) {
+ Error *err = NULL;
+ idc->auto_parent(id, &err);
+ } else {
+ irqs = fdti->irq_base;
+ }
+ }
+ if (!irqs) {
+ break;
+ }
+ while (*irqs) {
+ FDTIRQConnection *irq = g_new0(FDTIRQConnection, 1);
+ *irq = (FDTIRQConnection) {
+ .dev = DEVICE(dev),
+ .name = SYSBUS_DEVICE_GPIO_IRQ,
+ .merge_fn = qemu_irq_shared_or_handler,
+ .i = j,
+ .irq = *irqs,
+ .sink_info = g_strdup(irq_info_p),
+ .next = fdti->irqs
+ };
+ if (!map_mode) {
+ j++;
+ } else {
+ irq_info_p += strlen(irq_info_p) + 1;
+ }
+ fdti->irqs = irq;
+ irqs++;
+ }
+ if (map_mode) {
+ j++;
+ }
+ }
+ }
+
g_free(dev_type);
return 0;
diff --git a/include/hw/core/fdt_generic.h b/include/hw/core/fdt_generic.h
index 46b7dc084b..ad9e249156 100644
--- a/include/hw/core/fdt_generic.h
+++ b/include/hw/core/fdt_generic.h
@@ -24,6 +24,16 @@ typedef struct FDTCPUCluster {
bool user;
} FDTCPUCluster;
+typedef struct FDTIRQConnection {
+ DeviceState *dev;
+ const char *name;
+ int i;
+ bool (*merge_fn)(bool *, int);
+ qemu_irq irq;
+ char *sink_info; /* Debug only */
+ void *next;
+} FDTIRQConnection;
+
typedef struct FDTMachineInfo {
/* the fdt blob */
void *fdt;
@@ -33,6 +43,8 @@ typedef struct FDTMachineInfo {
FDTDevOpaque *dev_opaques;
/* recheck coroutine queue */
CoQueue *cq;
+ /* list of all IRQ connections */
+ FDTIRQConnection *irqs;
/* list of all CPU clusters */
FDTCPUCluster *clusters;
} FDTMachineInfo;
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 16/27] hw/core/fdt_generic_util: realize cpu clusters
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (14 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 15/27] hw/core/fdt_generic_util: Connect device irqs Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 17/27] hw/core: add fdt_generic to the build Ruslan Ruslichenko
` (11 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Realize cpu clusters at the end of fdt machine creation
process, as they need to be created after cpu's nodes
are realized.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 2f49909a50..83ce5b3466 100644
--- a/hw/core/fdt_generic_util.c
+++ b/hw/core/fdt_generic_util.c
@@ -158,6 +158,16 @@ static void fdt_init_all_irqs(FDTMachineInfo *fdti)
}
}
+static void fdt_init_cpu_clusters(FDTMachineInfo *fdti)
+{
+ FDTCPUCluster *cl = fdti->clusters;
+
+ while (cl) {
+ qdev_realize(DEVICE(cl->cpu_cluster), NULL, &error_fatal);
+ cl = cl->next;
+ }
+}
+
FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
{
char node_path[DT_PATH_LENGTH];
@@ -173,6 +183,7 @@ FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
while (qemu_co_enter_next(fdti->cq, NULL)) {
;
}
+ fdt_init_cpu_clusters(fdti);
fdt_init_all_irqs(fdti);
memory_region_transaction_commit();
} else {
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 17/27] hw/core: add fdt_generic to the build
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (15 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 16/27] hw/core/fdt_generic_util: realize cpu clusters Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 18/27] hw/core/machine: add '-hw-dtb' option for machine Ruslan Ruslichenko
` (10 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
Add `fdt_generic.c` and `fdt_generic_util.c` to build
by default.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/meson.build | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/core/meson.build b/hw/core/meson.build
index b5a545a0ed..6d6434b570 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -28,6 +28,8 @@ system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutl
system_ss.add(files(
'cpu-system.c',
+ 'fdt_generic.c',
+ 'fdt_generic_util.c',
'fw-path-provider.c',
'gpio.c',
'hotplug.c',
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 18/27] hw/core/machine: add '-hw-dtb' option for machine
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (16 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 17/27] hw/core: add fdt_generic to the build Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-27 8:40 ` Zhao Liu
2026-01-26 17:43 ` [PATCH 19/27] hw/arm: add generic ARM machine initialized by FDT Ruslan Ruslichenko
` (9 subsequent siblings)
27 siblings, 1 reply; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Paolo Bonzini, Zhao Liu, Yanan Wang, Philippe Mathieu-Daudé
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 6411e68856..fc04cf75c0 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -378,6 +378,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);
@@ -1287,6 +1301,11 @@ static void machine_initfn(Object *obj)
ms->ram_size = mc->default_ram_size;
ms->maxram_size = mc->default_ram_size;
+ object_property_add_str(obj, "hw-dtb",
+ machine_get_hw_dtb, machine_set_hw_dtb);
+ object_property_set_description(obj, "hw-dtb",
+ "A device tree used to describe the hardware to QEMU.");
+
if (mc->nvdimm_supported) {
ms->nvdimms_state = g_new0(NVDIMMState, 1);
object_property_add_bool(obj, "nvdimm",
diff --git a/include/hw/core/boards.h b/include/hw/core/boards.h
index 07f8938752..c28c505bb6 100644
--- a/include/hw/core/boards.h
+++ b/include/hw/core/boards.h
@@ -402,6 +402,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 bd014a3244..6dd3e04e38 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4492,6 +4492,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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 19/27] hw/arm: add generic ARM machine initialized by FDT
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (17 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 18/27] hw/core/machine: add '-hw-dtb' option for machine Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 20/27] hw/core/sysbus: implement FDT_GENERIC_MMAP_CLASS interface Ruslan Ruslichenko
` (8 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
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 | 166 +++++++++++++++++++++++++++++++++++++++
hw/arm/meson.build | 2 +
2 files changed, 168 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..d3912745c7
--- /dev/null
+++ b/hw/arm/arm_generic_fdt.c
@@ -0,0 +1,166 @@
+// 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;
+
+static memory_info init_memory(void *fdt, ram_addr_t ram_size)
+{
+ FDTMachineInfo *fdti;
+ char node_path[DT_PATH_LENGTH];
+ MemoryRegion *mem_area;
+ memory_info kernel_info;
+ Error *errp = NULL;
+
+ /* Find a memory node or add new one if needed */
+ while (qemu_devtree_get_node_by_name(fdt, node_path, "memory")) {
+ qemu_fdt_add_subnode(fdt, "/memory@0");
+ qemu_fdt_setprop_cells(fdt, "/memory@0", "reg", 0, ram_size);
+ }
+
+ if (!qemu_fdt_getprop(fdt, "/memory", "compatible", NULL, 0, NULL)) {
+ qemu_fdt_setprop_string(fdt, "/memory", "compatible",
+ "qemu:memory-region");
+ qemu_fdt_setprop_cells(fdt, "/memory", "qemu,ram", 1);
+ }
+
+ /* Instantiate peripherals from the FDT. */
+ fdti = fdt_generic_create_machine(fdt, NULL);
+
+ mem_area = MEMORY_REGION(object_resolve_path(node_path, NULL));
+
+ /*
+ * Look for the optional kernel-base prop. If not found fallback to
+ * start of memory.
+ */
+ kernel_info.ram_kernel_base = qemu_fdt_getprop_sized_cell(fdt, "/",
+ "kernel-base", 0, 2, &errp);
+ if (errp) {
+ 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);
+
+ return kernel_info;
+}
+
+static void arm_generic_fdt_init(MachineState *machine)
+{
+ void *fdt = NULL, *sw_fdt = NULL;
+ int fdt_size, sw_fdt_size;
+ const char *dtb_arg, *hw_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 = load_device_tree(dtb_arg, &sw_fdt_size);
+ if (!sw_fdt) {
+ 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) {
+ fdt = sw_fdt;
+ fdt_size = sw_fdt_size;
+ }
+
+ kernel_info = init_memory(fdt, machine->ram_size);
+
+ 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_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 = 64;
+}
+
+DEFINE_MACHINE_ARM("arm-generic-fdt", arm_generic_fdt_machine_init)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index aeaf654790..e606630c86 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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 20/27] hw/core/sysbus: implement FDT_GENERIC_MMAP_CLASS interface
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (18 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 19/27] hw/arm: add generic ARM machine initialized by FDT Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 21/27] hw/intc/arm_gic: implement FDT_GENERIC_INTC and fdt support Ruslan Ruslichenko
` (7 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Eduardo Habkost, Daniel P. Berrangé
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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 21/27] hw/intc/arm_gic: implement FDT_GENERIC_INTC and fdt support
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (19 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 20/27] hw/core/sysbus: implement FDT_GENERIC_MMAP_CLASS interface Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 22/27] target/arm/cpu: add fdt support for armv8-timer Ruslan Ruslichenko
` (6 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 22/27] target/arm/cpu: add fdt support for armv8-timer
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (20 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 21/27] hw/intc/arm_gic: implement FDT_GENERIC_INTC and fdt support Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 23/27] qom/object: export object_resolve_link() Ruslan Ruslichenko
` (5 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
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 6e1cbf3d61..96696ed4f1 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -53,6 +53,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)
{
@@ -2446,3 +2448,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(fdti->fdt, node_path,
+ "interrupt-parent",
+ 0, true, &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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 23/27] qom/object: export object_resolve_link()
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (21 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 22/27] target/arm/cpu: add fdt support for armv8-timer Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 24/27] system/memory: add setters for MemoryRegion properties Ruslan Ruslichenko
` (4 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Eduardo Habkost, Daniel P. Berrangé
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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 24/27] system/memory: add setters for MemoryRegion properties
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (22 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 23/27] qom/object: export object_resolve_link() Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 25/27] system/memory: implement FDT_GENERIC_MMAP interface Ruslan Ruslichenko
` (3 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Philippe Mathieu-Daudé
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 | 166 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 161 insertions(+), 5 deletions(-)
diff --git a/system/memory.c b/system/memory.c
index 4bf00d82bc..a683af525a 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)
@@ -1285,6 +1355,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 +1428,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 +1456,25 @@ 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(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);
}
@@ -2751,6 +2904,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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 25/27] system/memory: implement FDT_GENERIC_MMAP interface
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (23 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 24/27] system/memory: add setters for MemoryRegion properties Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 26/27] hw/core/fdt_generic_util: initialize serial devices Ruslan Ruslichenko
` (2 subsequent siblings)
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Philippe Mathieu-Daudé
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 a683af525a..866c741973 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;
@@ -3749,6 +3751,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 = {
@@ -3994,6 +4047,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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 26/27] hw/core/fdt_generic_util: initialize serial devices
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (24 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 25/27] system/memory: implement FDT_GENERIC_MMAP interface Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 27/27] system/memory: add QOM aliases for fdt support Ruslan Ruslichenko
2026-01-27 10:02 ` [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Peter Maydell
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
The patch adds proper handling and registration
for serial devices instantiated via FDT.
In specific, device's 'chardev' FDT properties
are correctly set to their objects.
Also devices with 'chardev' properties are
connected to serial backends.
Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
---
hw/core/fdt_generic_util.c | 44 ++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/hw/core/fdt_generic_util.c b/hw/core/fdt_generic_util.c
index 83ce5b3466..d183a44433 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"
@@ -68,6 +69,7 @@
#include <stdlib.h>
static int fdt_generic_num_cpus;
+static int fdt_serial_ports;
static int simple_bus_fdt_init(char *bus_node_path, FDTMachineInfo *fdti);
@@ -175,6 +177,8 @@ FDTMachineInfo *fdt_generic_create_machine(void *fdt, qemu_irq *cpu_irq)
fdti->irq_base = cpu_irq;
+ fdt_serial_ports = 0;
+
/* parse the device tree */
if (!qemu_devtree_get_root_node(fdt, node_path)) {
memory_region_transaction_begin();
@@ -1096,6 +1100,8 @@ 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;
+ void *val = prop->value;
ObjectProperty *p = NULL;
p = object_property_find(OBJECT(dev), propname);
@@ -1112,6 +1118,27 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
continue;
}
+ /* Special case for chardevs. It's an ordered list of strings. */
+ if (!strcmp(propname, "chardev") && !strcmp(p->type, "str")) {
+ const char *chardev = val;
+ const char *chardevs_end = chardev + len;
+
+ assert(errp == NULL);
+ while (chardev < chardevs_end) {
+ object_property_set_str(OBJECT(dev), propname,
+ (const char *)chardev, &errp);
+ if (!errp) {
+ fprintf(stderr, "set property %s to %s\n", propname,
+ chardev);
+ break;
+ }
+ chardev += strlen(chardev) + 1;
+ errp = NULL;
+ }
+ assert(errp == NULL);
+ continue;
+ }
+
fdt_init_qdev_scalar_prop(OBJECT(dev), p, fdti, node_path, prop);
}
@@ -1119,6 +1146,23 @@ static int fdt_init_qdev(char *node_path, FDTMachineInfo *fdti, char *compat)
const char *short_name = qemu_devtree_get_node_name(fdti->fdt,
node_path);
+ /* Connect chardev if we can */
+ if (serial_hd(fdt_serial_ports)) {
+ Chardev *value = (Chardev *) serial_hd(fdt_serial_ports);
+ char *chardev;
+
+ /* Check if the device already has a chardev. */
+ chardev = object_property_get_str(dev, "chardev", &errp);
+ if (!errp && !strcmp(chardev, "")) {
+ object_property_set_str(dev, "chardev", value->label, &errp);
+ if (!errp) {
+ /* It worked, the device is a charecter device */
+ fdt_serial_ports++;
+ }
+ }
+ errp = NULL;
+ }
+
/* Regular TYPE_DEVICE houskeeping */
DB_PRINT_NP(0, "Short naming node: %s\n", short_name);
(DEVICE(dev))->id = g_strdup(short_name);
--
2.43.0
^ permalink raw reply related [flat|nested] 42+ messages in thread
* [PATCH 27/27] system/memory: add QOM aliases for fdt support
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (25 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 26/27] hw/core/fdt_generic_util: initialize serial devices Ruslan Ruslichenko
@ 2026-01-26 17:43 ` Ruslan Ruslichenko
2026-01-27 10:02 ` [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Peter Maydell
27 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-26 17:43 UTC (permalink / raw)
To: qemu-devel
Cc: qemu-arm, peter.maydell, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko,
Philippe Mathieu-Daudé
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.
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 | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/system/memory.c b/system/memory.c
index 866c741973..867228922b 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -4077,3 +4077,27 @@ static void memory_register_types(void)
}
type_init(memory_register_types)
+
+static int sysmem_fdt_init(char *node_path, FDTMachineInfo *fdti,
+ void *priv)
+{
+ fdt_init_set_opaque(fdti, node_path, OBJECT(get_system_memory()));
+ 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" },
+};
+
+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
^ permalink raw reply related [flat|nested] 42+ messages in thread
* Re: [PATCH 01/27] system/device_tree: update qemu_fdt_getprop/_cell
2026-01-26 17:42 ` [PATCH 01/27] system/device_tree: update qemu_fdt_getprop/_cell Ruslan Ruslichenko
@ 2026-01-27 1:56 ` David Gibson
0 siblings, 0 replies; 42+ messages in thread
From: David Gibson @ 2026-01-27 1:56 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
Ruslan_Ruslichenko, Alistair Francis
[-- Attachment #1: Type: text/plain, Size: 7866 bytes --]
On Mon, Jan 26, 2026 at 06:42:47PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> Update 'qemu_fdt_getprop' and 'qemu_fdt_getprop_cell'
> to support property inheritence from parent node,
> in case 'inherit' argument is set.
A bare bool parameter is pretty ugly and requires updating every
existing caller. I'd suggest adding a new function for the inheriting
version, instead.
> Update 'qemu_fdt_getprop_cell' to allow accessing
> specific cells within multi-cell property array.
>
> Introduced 'qemu_devtreedd_getparent' as it is needed
> by both internal and external users.
>
> This will be used by hardware device tree parsing logic.
>
> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> ---
> hw/arm/boot.c | 8 ++++----
> hw/arm/raspi4b.c | 8 ++++----
> hw/arm/vexpress.c | 4 ++--
> hw/arm/xlnx-zcu102.c | 3 ++-
> include/system/device_tree.h | 32 +++++++++++++++++++-------------
> system/device_tree.c | 33 ++++++++++++++++++++++++---------
> 6 files changed, 55 insertions(+), 33 deletions(-)
>
> diff --git a/hw/arm/boot.c b/hw/arm/boot.c
> index c97d4c4e11..829b8ba12f 100644
[snip]
> diff --git a/include/system/device_tree.h b/include/system/device_tree.h
> index 49d8482ed4..f34b8b7ef9 100644
> --- a/include/system/device_tree.h
> +++ b/include/system/device_tree.h
> @@ -96,27 +96,28 @@ int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
> * @node_path: node path
> * @property: name of the property to find
> * @lenp: fdt error if any or length of the property on success
> + * @inherit: if not found in node, look for property in parent
> * @errp: handle to an error object
> *
> * returns a pointer to the property on success and NULL on failure
> */
> const void *qemu_fdt_getprop(void *fdt, const char *node_path,
> const char *property, int *lenp,
> - Error **errp);
> + bool inherit, Error **errp);
> /**
> - * qemu_fdt_getprop_cell: retrieve the value of a given 4 byte property
> - * @fdt: pointer to the device tree blob
> - * @node_path: node path
> - * @property: name of the property to find
> - * @lenp: fdt error if any or -EINVAL if the property size is different from
> - * 4 bytes, or 4 (expected length of the property) upon success.
> - * @errp: handle to an error object
> - *
> - * returns the property value on success
> - */
Spurious change to all the whitespace here.
> +* qemu_fdt_getprop_cell: retrieve the value of a given 4 byte property
This description is no longer accurate after your change.
> +* @fdt: pointer to the device tree blob
> +* @node_path: node path
> +* @property: name of the property to find
> +* @ofset: the index of 32bit cell to retrive
s/ofset/offset/. But, in any case in libfdt context 'offset' always
refers to a byte offset, so I think this is a potentially misleading
name.
> +* @inherit: if not found in node, look for property in parent
> +* @errp: handle to an error object
> +*
> +* returns the property value on success
> +*/
> uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
> - const char *property, int *lenp,
> - Error **errp);
> + const char *property, int offset,
> + bool inherit, Error **errp);
> uint32_t qemu_fdt_get_phandle(void *fdt, const char *path);
> uint32_t qemu_fdt_alloc_phandle(void *fdt);
> int qemu_fdt_nop_node(void *fdt, const char *node_path);
> @@ -193,6 +194,11 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
> })
>
>
> +int qemu_devtree_getparent(void *fdt, char *node_path,
> + const char *current);
> +
> +#define DT_PATH_LENGTH 1024
> +
> /**
> * qemu_fdt_randomize_seeds:
> * @fdt: device tree blob
> diff --git a/system/device_tree.c b/system/device_tree.c
> index 1ea1962984..41bde0ba5a 100644
> --- a/system/device_tree.c
> +++ b/system/device_tree.c
> @@ -429,7 +429,8 @@ int qemu_fdt_setprop_string_array(void *fdt, const char *node_path,
> }
>
> const void *qemu_fdt_getprop(void *fdt, const char *node_path,
> - const char *property, int *lenp, Error **errp)
> + const char *property, int *lenp,
> + bool inherit, Error **errp)
> {
> int len;
> const void *r;
> @@ -439,31 +440,35 @@ const void *qemu_fdt_getprop(void *fdt, const char *node_path,
> }
> r = fdt_getprop(fdt, findnode_nofail(fdt, node_path), property, lenp);
> if (!r) {
> + char parent[DT_PATH_LENGTH];
> + if (inherit && !qemu_devtree_getparent(fdt, parent, node_path)) {
> + return qemu_fdt_getprop(fdt, parent, property, lenp, true, errp);
> + }
This is a pretty horribly expensive way of doing it. On a flattened
tree, findnode_nofail() and devtree_getparent() each need to scan the
tree from the start, and you do both for each layer of the tree. You
could instead scan the tree once from the root, updating a pointer to
the deepest copy of the property found so far.
> error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
> node_path, property, fdt_strerror(*lenp));
> + return NULL;
> }
> return r;
> }
>
> uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
> - const char *property, int *lenp, Error **errp)
> + const char *property, int offset,
> + bool inherit, Error **errp)
> {
> int len;
> const uint32_t *p;
>
> - if (!lenp) {
> - lenp = &len;
> - }
> - p = qemu_fdt_getprop(fdt, node_path, property, lenp, errp);
> + p = qemu_fdt_getprop(fdt, node_path, property, &len,
> + inherit, errp);
> if (!p) {
> return 0;
> - } else if (*lenp != 4) {
> + }
> + if (len < (offset + 1) * 4) {
> error_setg(errp, "%s: %s/%s not 4 bytes long (not a cell?)",
> __func__, node_path, property);
Error message is no longer accurate.
> - *lenp = -EINVAL;
> return 0;
> }
> - return be32_to_cpu(*p);
> + return be32_to_cpu(p[offset]);
> }
>
> uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
> @@ -633,6 +638,16 @@ out:
> return ret;
> }
>
> +int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
This inherently needs to scan the tree from the beginning at least
once, but the implementation is much worse than that.
> +{
> + int offset = fdt_path_offset(fdt, current);
This scans the tree from the start.
> + int parent_offset = fdt_supernode_atdepth_offset(fdt, offset,
> + fdt_node_depth(fdt, offset) - 1, NULL);
This does it twice more (one for fdt_node_depth() and one for
fdt_supernode_atdepth_offset()).
> +
> + return parent_offset >= 0 ?
> + fdt_get_path(fdt, parent_offset, node_path, DT_PATH_LENGTH) : 1;
This does it a fourth time.
Since you're starting from a path, not an offset, you could instead
chop off the last path component, then do a single fdt_get_path().
> +}
> +
> void qmp_dumpdtb(const char *filename, Error **errp)
> {
> ERRP_GUARD();
> --
> 2.43.0
>
--
David Gibson (he or they) | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you, not the other way
| around.
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 02/27] system/device_tree: add few parsing and traversal helpers
2026-01-26 17:42 ` [PATCH 02/27] system/device_tree: add few parsing and traversal helpers Ruslan Ruslichenko
@ 2026-01-27 2:19 ` David Gibson
2026-01-27 21:22 ` Ruslan Ruslichenko
0 siblings, 1 reply; 42+ messages in thread
From: David Gibson @ 2026-01-27 2:19 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
Ruslan_Ruslichenko, Alistair Francis
[-- Attachment #1: Type: text/plain, Size: 11889 bytes --]
On Mon, Jan 26, 2026 at 06:42:48PM +0100, Ruslan Ruslichenko wrote:
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> The patch adds few utility functions for parsing FDT nodes.
> The helpers are required for upcoming Hardware device tree
> feature.
>
> Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> ---
> include/system/device_tree.h | 30 ++++++
> system/device_tree.c | 203 +++++++++++++++++++++++++++++++++++
> 2 files changed, 233 insertions(+)
>
> diff --git a/include/system/device_tree.h b/include/system/device_tree.h
> index f34b8b7ef9..458dbb74b4 100644
> --- a/include/system/device_tree.h
> +++ b/include/system/device_tree.h
> @@ -90,6 +90,13 @@ int qemu_fdt_setprop_string_array(void *fdt, const char *node_path,
> int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
> const char *property,
> const char *target_node_path);
> +
> +uint64_t qemu_fdt_getprop_sized_cell(void *fdt, const char *node_path,
> + const char *property, int offset,
> + int size, Error **errp);
> +const char *qemu_fdt_getprop_string(void *fdt, const char*node_path,
> + const char *property, int cell,
> + bool inherit, Error **errp);
> /**
> * qemu_fdt_getprop: retrieve the value of a given property
> * @fdt: pointer to the device tree blob
> @@ -193,9 +200,32 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
> qdt_tmp); \
> })
>
> +typedef struct QEMUDevtreeProp {
> + char *name;
> + int len;
> + void *value;
> +} QEMUDevtreeProp;
> +
> +/* node queries */
> +
> +char *qemu_devtree_get_node_name(void *fdt, const char *node_path);
> +int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth);
> +char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth);
> +int qemu_devtree_num_props(void *fdt, const char *node_path);
> +QEMUDevtreeProp *qemu_devtree_get_props(void *fdt, const char *node_path);
>
> +/* node getters */
> +
> +int qemu_devtree_get_node_by_name(void *fdt, char *node_path,
> + const char *cmpname);
> +int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle);
> int qemu_devtree_getparent(void *fdt, char *node_path,
> const char *current);
> +int qemu_devtree_get_root_node(void *fdt, char *node_path);
> +
> +/* misc */
> +
> +int devtree_get_num_nodes(void *fdt);
>
> #define DT_PATH_LENGTH 1024
>
> diff --git a/system/device_tree.c b/system/device_tree.c
> index 41bde0ba5a..7091a4928e 100644
> --- a/system/device_tree.c
> +++ b/system/device_tree.c
> @@ -451,6 +451,50 @@ const void *qemu_fdt_getprop(void *fdt, const char *node_path,
> return r;
> }
>
> +const char *qemu_fdt_getprop_string(void *fdt, const char*node_path,
> + const char *property, int cell,
> + bool inherit, Error **errp)
> +{
> + int len;
> + const void *prop;
> + Error *err = NULL;
> +
> + if (!errp) {
> + errp = &err;
> + }
> +
> + prop = qemu_fdt_getprop(fdt, node_path, property, &len, inherit, errp);
> + if (*errp) {
> + return NULL;
> + }
> + while (cell) {
In DT context, "cell" refers specifically to a 32-bit integer value.
It's not the right word for which string in a stringlist you retrieve.
Also, most of this is re-implementing fdt_stringlist_get() from libfdt.
> + void *term = memchr(prop, '\0', len);
> + size_t diff;
> +
> + if (!term) {
> + error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
> + node_path, property, fdt_strerror(len));
> + return NULL;
> + }
> + diff = term - prop + 1;
> + len -= diff;
> + assert(len >= 0);
> + prop += diff;
> + cell--;
> + }
> +
> + if (!len) {
> + return NULL;
> + }
> +
> + if (!*(char *)prop) {
> + error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
> + node_path, property, fdt_strerror(len));
> + return NULL;
> + }
> + return prop;
> +}
> +
> uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
> const char *property, int offset,
> bool inherit, Error **errp)
> @@ -471,6 +515,22 @@ uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
> return be32_to_cpu(p[offset]);
> }
>
> +uint64_t qemu_fdt_getprop_sized_cell(void *fdt, const char *node_path,
> + const char *property, int offset,
> + int size, Error **errp)
> +{
Again, "cell" is 32-bits in DT context, you need a different word.
> + uint64_t ret = 0;
Since ret is a u64, 'size' must be either 1 or 2. So you might as
well just have a get_u64() helper (getprop_cell already does u32).
> + for (; size; size--) {
> + ret <<= 32;
> + ret |= qemu_fdt_getprop_cell(fdt, node_path, property, offset++, false,
> + errp);
> + if (errp && *errp) {
> + return 0;
> + }
> + }
> + return ret;
> +}
> +
> uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
> {
> uint32_t r;
> @@ -638,6 +698,117 @@ out:
> return ret;
> }
>
> +char *qemu_devtree_get_node_name(void *fdt, const char *node_path)
> +{
> + const char *ret = fdt_get_name(fdt, fdt_path_offset(fdt, node_path), NULL);
This does a full scan of the fdt, which may be unexpected.
> + return ret ? strdup(ret) : NULL;
> +}
> +
> +int qemu_devtree_num_props(void *fdt, const char *node_path)
> +{
> + int offset = fdt_path_offset(fdt, node_path);
> + int ret = 0;
> +
> + for (offset = fdt_first_property_offset(fdt, offset);
> + offset != -FDT_ERR_NOTFOUND;
> + offset = fdt_next_property_offset(fdt, offset)) {
> + ret++;
> + }
> + return ret;
> +}
> +
> +QEMUDevtreeProp *qemu_devtree_get_props(void *fdt, const char *node_path)
> +{
> + QEMUDevtreeProp *ret = g_new0(QEMUDevtreeProp,
> + qemu_devtree_num_props(fdt, node_path) + 1);
> + int offset = fdt_path_offset(fdt, node_path);
> + int i = 0;
> +
> + for (offset = fdt_first_property_offset(fdt, offset);
> + offset != -FDT_ERR_NOTFOUND;
> + offset = fdt_next_property_offset(fdt, offset)) {
> + const char *propname;
> + const void *val = fdt_getprop_by_offset(fdt, offset, &propname,
> + &ret[i].len);
> +
> + ret[i].name = g_strdup(propname);
> + ret[i].value = g_memdup2(val, ret[i].len);
> + i++;
> + }
> + return ret;
How does the caller free the structure? Using g_free() will leak all
the copied names and values.
> +}
> +
> +static void qemu_devtree_children_info(void *fdt, const char *node_path,
> + int depth, int *num, char **returned_paths) {
> + int offset = fdt_path_offset(fdt, node_path);
> + int root_depth = fdt_node_depth(fdt, offset);
"root" implies the root of the whole tree, not of the node where you
started. Also, two full scans of the tree above.
> + int cur_depth = root_depth;
> +
> + if (num) {
> + *num = 0;
> + }
> + for (;;) {
> + offset = fdt_next_node(fdt, offset, &cur_depth);
> + if (cur_depth <= root_depth) {
> + break;
> + }
> + if (cur_depth <= root_depth + depth || depth == 0) {
> + if (returned_paths) {
> + returned_paths[*num] = g_malloc0(DT_PATH_LENGTH);
No test to avoid overrunning the length of the returned_paths[] array
here.
> + fdt_get_path(fdt, offset, returned_paths[*num], DT_PATH_LENGTH);
Another full tree scan for every entry here.
> + }
> + if (num) {
> + (*num)++;
> + }
> + }
> + }
> +}
> +
> +char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth)
> +{
> + int num_children = qemu_devtree_get_num_children(fdt, node_path, depth);
> + char **ret = g_malloc0(sizeof(*ret) * num_children);
> +
> + qemu_devtree_children_info(fdt, node_path, depth, &num_children, ret);
> + return ret;
> +}
> +
> +int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth)
> +{
> + int ret;
> +
> + qemu_devtree_children_info(fdt, node_path, depth, &ret, NULL);
> + return ret;
> +}
> +
> +int qemu_devtree_get_node_by_name(void *fdt, char *node_path,
> + const char *cmpname) {
The semantics of this make no sense to me. It finds the first node
with the name after the given node, whether it's a child, sibling or
unrelated node. I'm guessing you want something based on
fdt_subnode_offset() instead.
> + int offset = 0;
> + char *name = NULL;
> +
> + do {
> + char *at;
> +
> + offset = fdt_next_node(fdt, offset, NULL);
No test for error here, before using 'offset'.
> + name = (void *)fdt_get_name(fdt, offset, NULL);
> + if (!name) {
> + continue;
The only errors from fdt_get_name() indicate either a corrupted DT, or
bad parameters. continuing at this point will not do anything useful.
> + }
> + at = memchr(name, '@', strlen(name));
> + if (!strncmp(name, cmpname, at ? at - name : strlen(name))) {
> + break;
> + }
> + } while (offset > 0);
> + return offset > 0 ?
> + fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH) : 1;
> +}
> +
> +int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle)
> +{
> + return fdt_get_path(fdt, fdt_node_offset_by_phandle(fdt, phandle),
> + node_path, DT_PATH_LENGTH);
Two full tree scans.
> +}
> +
> int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
> {
> int offset = fdt_path_offset(fdt, current);
> @@ -648,6 +819,38 @@ int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
> fdt_get_path(fdt, parent_offset, node_path, DT_PATH_LENGTH) : 1;
> }
>
> +int qemu_devtree_get_root_node(void *fdt, char *node_path)
> +{
> + return fdt_get_path(fdt, 0, node_path, DT_PATH_LENGTH);
The root node's path is "/" by definition, you don't need this
function.
> +}
> +
> +static void devtree_scan(void *fdt, int *num_nodes)
"scan" is vague. The name should reflect what this actually does.
> +{
> + int depth = 0, offset = 0;
> +
> + if (num_nodes) {
> + *num_nodes = 0;
> + }
> + for (;;) {
> + offset = fdt_next_node(fdt, offset, &depth);
> + if (num_nodes) {
> + (*num_nodes)++;
> + }
> + if (offset <= 0 || depth <= 0) {
> + break;
> + }
> + }
> +}
> +
> +int devtree_get_num_nodes(void *fdt)
> +{
> + int ret;
> +
> + devtree_scan(fdt, &ret);
> + return ret;
> +}
> +
> +
> void qmp_dumpdtb(const char *filename, Error **errp)
> {
> ERRP_GUARD();
> --
> 2.43.0
>
--
David Gibson (he or they) | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au | minimalist, thank you, not the other way
| around.
http://www.ozlabs.org/~dgibson
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 18/27] hw/core/machine: add '-hw-dtb' option for machine
2026-01-26 17:43 ` [PATCH 18/27] hw/core/machine: add '-hw-dtb' option for machine Ruslan Ruslichenko
@ 2026-01-27 8:40 ` Zhao Liu
2026-01-27 20:12 ` Ruslan Ruslichenko
0 siblings, 1 reply; 42+ messages in thread
From: Zhao Liu @ 2026-01-27 8:40 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
Ruslan_Ruslichenko, Paolo Bonzini, Yanan Wang,
Philippe Mathieu-Daudé
> static char *machine_get_dumpdtb(Object *obj, Error **errp)
> {
> MachineState *ms = MACHINE(obj);
> @@ -1287,6 +1301,11 @@ static void machine_initfn(Object *obj)
> ms->ram_size = mc->default_ram_size;
> ms->maxram_size = mc->default_ram_size;
>
> + object_property_add_str(obj, "hw-dtb",
> + machine_get_hw_dtb, machine_set_hw_dtb);
What about object_class_property_add_str()? It seems this property
doesn't depend on any other state, so I think it could be at class
level, just like "dtb" did.
> + object_property_set_description(obj, "hw-dtb",
> + "A device tree used to describe the hardware to QEMU.");
maybe "A device tree binary used to describe the hardware to QEMU"?
and none of the other machine property descriptions have 'periods'.
> +
> if (mc->nvdimm_supported) {
> ms->nvdimms_state = g_new0(NVDIMMState, 1);
> object_property_add_bool(obj, "nvdimm",
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
` (26 preceding siblings ...)
2026-01-26 17:43 ` [PATCH 27/27] system/memory: add QOM aliases for fdt support Ruslan Ruslichenko
@ 2026-01-27 10:02 ` Peter Maydell
2026-01-27 14:29 ` BALATON Zoltan
2026-01-27 18:18 ` Ruslan Ruslichenko
27 siblings, 2 replies; 42+ messages in thread
From: Peter Maydell @ 2026-01-27 10:02 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: qemu-devel, qemu-arm, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
<ruslichenko.r@gmail.com> wrote:
>
> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>
> This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
I'm afraid this has been a feature that has been suggested from
time to time, but which I don't think is workable in general.
Device tree files are designed to provide enough information to
the guest kernel to allow it to find non-probeable hardware. They
are not designed to provide enough information to QEMU to allow
it to create and wire up all the hardware present on the system.
There are specific niches where it can be made to work -- I think
Xilinx have or had a setup where they were generating an FPGA
model and a device tree and a guest kernel all from the same
single data source, so they could put everything necessary into
the dtb, for example -- but I don't think it works in the
general case. As one simple example, the DTB doesn't generally
have any information about how the Secure world works in an Arm
system, because Linux doesn't care about the Secure world. It
also doesn't usually have information that the guest OS can
probe for itself at runtime.
There has been periodic discussion of more flexible user-driven
board creation, but that has generally been with the idea of using
the QMP monitor to orchestrate creation and connection of device
models.
thanks
-- PMM
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-27 10:02 ` [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Peter Maydell
@ 2026-01-27 14:29 ` BALATON Zoltan
2026-01-27 18:18 ` Ruslan Ruslichenko
1 sibling, 0 replies; 42+ messages in thread
From: BALATON Zoltan @ 2026-01-27 14:29 UTC (permalink / raw)
To: Peter Maydell
Cc: Ruslan Ruslichenko, qemu-devel, qemu-arm, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
Ruslan_Ruslichenko, Bernhard Beschow
On Tue, 27 Jan 2026, Peter Maydell wrote:
> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
> <ruslichenko.r@gmail.com> wrote:
>>
>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>
>> This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
>
> I'm afraid this has been a feature that has been suggested from
> time to time, but which I don't think is workable in general.
There were another experiment by Bernhard Beschow here:
<https://github.com/shentok/qemu/commits/e500-fdt/> which seems
surprisingly simple (the actual change to allow fdt based creation is
this:
<https://github.com/shentok/qemu/commit/857b19fe4bbb0ec42c92dab48411b5b8ab2da02c>)
and works for creating some PPC e500 based machines (still work in
progress though). We had a discussion of this here:
<https://lists.nongnu.org/archive/html/qemu-ppc/2025-02/msg00198.html>
I don't remember if an RFC was submitted later and there were more reviews
on that.
> Device tree files are designed to provide enough information to
> the guest kernel to allow it to find non-probeable hardware. They
> are not designed to provide enough information to QEMU to allow
> it to create and wire up all the hardware present on the system.
>
> There are specific niches where it can be made to work -- I think
> Xilinx have or had a setup where they were generating an FPGA
> model and a device tree and a guest kernel all from the same
> single data source, so they could put everything necessary into
> the dtb, for example -- but I don't think it works in the
> general case. As one simple example, the DTB doesn't generally
> have any information about how the Secure world works in an Arm
> system, because Linux doesn't care about the Secure world. It
> also doesn't usually have information that the guest OS can
> probe for itself at runtime.
>
> There has been periodic discussion of more flexible user-driven
> board creation, but that has generally been with the idea of using
> the QMP monitor to orchestrate creation and connection of device
> models.
Whatever interface is used to instantiate the objects it would need some
description language to describe the machine and FDT while not the most
user friendly can do that so it can be a candidate or alternative for
other description language which can be considered. If the fdt that comes
with Linux does not contain everything QEMU could still use a modified
enhanced fdt that adds the needed details as long as fdt itself can
describe that. It's nice if QEMU does not have to supply it's own FDT
files and can just use those from Linux but even if that would not work
does not mean fdt is not a usable way to define a machine.
Regards,
BALATON Zoltan
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-27 10:02 ` [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Peter Maydell
2026-01-27 14:29 ` BALATON Zoltan
@ 2026-01-27 18:18 ` Ruslan Ruslichenko
2026-01-28 17:48 ` Alex Bennée
1 sibling, 1 reply; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-27 18:18 UTC (permalink / raw)
To: Peter Maydell
Cc: qemu-devel, qemu-arm, artem_mygaiev, volodymyr_babchuk,
takahiro.nakata.wr, Edgar E . Iglesias, Ruslan_Ruslichenko
On Tue, Jan 27, 2026 at 11:03 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>
> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
> <ruslichenko.r@gmail.com> wrote:
> >
> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >
> > This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
>
> I'm afraid this has been a feature that has been suggested from
> time to time, but which I don't think is workable in general.
>
> Device tree files are designed to provide enough information to
> the guest kernel to allow it to find non-probeable hardware. They
> are not designed to provide enough information to QEMU to allow
> it to create and wire up all the hardware present on the system.
>
> There are specific niches where it can be made to work -- I think
> Xilinx have or had a setup where they were generating an FPGA
> model and a device tree and a guest kernel all from the same
> single data source, so they could put everything necessary into
> the dtb, for example -- but I don't think it works in the
> general case. As one simple example, the DTB doesn't generally
> have any information about how the Secure world works in an Arm
> system, because Linux doesn't care about the Secure world. It
> also doesn't usually have information that the guest OS can
> probe for itself at runtime.
>
> There has been periodic discussion of more flexible user-driven
> board creation, but that has generally been with the idea of using
> the QMP monitor to orchestrate creation and connection of device
> models.
>
I agree that a guest Device Tree is insufficient for describing a
complete QEMU machine model.
However, we separate the guest configuration from the machine
definition. The -hw-dtb option allows the user to provide a
QEMU-specific system description.
Those hw-dtb would not be passed to Linux Guest VM.
The fact that this workflow has been successfully used in production
by AMD/Xilinx demonstrates that FDT is a feasible format.
In contrast, QMP usage may end up even more complex and less
maintainable for the task of full system modeling.
To my understanding, this would need to generate too long configs,
which may eventually require some intermediate format by itself.
I am proposing to use FDT as a serialization format to describe a
machine configuration. Theoretically we can use other formats, like
XML, but in my opinion FDT perfectly matches the requirements.
BR,
Ruslan Ruslichenko
> thanks
> -- PMM
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 18/27] hw/core/machine: add '-hw-dtb' option for machine
2026-01-27 8:40 ` Zhao Liu
@ 2026-01-27 20:12 ` Ruslan Ruslichenko
0 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-27 20:12 UTC (permalink / raw)
To: Zhao Liu
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
Ruslan_Ruslichenko, Paolo Bonzini, Yanan Wang,
Philippe Mathieu-Daudé
On Tue, Jan 27, 2026 at 9:15 AM Zhao Liu <zhao1.liu@intel.com> wrote:
>
> > static char *machine_get_dumpdtb(Object *obj, Error **errp)
> > {
> > MachineState *ms = MACHINE(obj);
> > @@ -1287,6 +1301,11 @@ static void machine_initfn(Object *obj)
> > ms->ram_size = mc->default_ram_size;
> > ms->maxram_size = mc->default_ram_size;
> >
> > + object_property_add_str(obj, "hw-dtb",
> > + machine_get_hw_dtb, machine_set_hw_dtb);
>
> What about object_class_property_add_str()? It seems this property
> doesn't depend on any other state, so I think it could be at class
> level, just like "dtb" did.
>
Yes, it could be done by machine_class_init() indeed.
> > + object_property_set_description(obj, "hw-dtb",
> > + "A device tree used to describe the hardware to QEMU.");
>
> maybe "A device tree binary used to describe the hardware to QEMU"?
>
> and none of the other machine property descriptions have 'periods'.
>
Thanks, I will update it in the v2 patch series.
> > +
> > if (mc->nvdimm_supported) {
> > ms->nvdimms_state = g_new0(NVDIMMState, 1);
> > object_property_add_bool(obj, "nvdimm",
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 02/27] system/device_tree: add few parsing and traversal helpers
2026-01-27 2:19 ` David Gibson
@ 2026-01-27 21:22 ` Ruslan Ruslichenko
0 siblings, 0 replies; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-27 21:22 UTC (permalink / raw)
To: David Gibson
Cc: qemu-devel, qemu-arm, peter.maydell, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
Ruslan_Ruslichenko, Alistair Francis
On Tue, Jan 27, 2026 at 3:19 AM David Gibson
<david@gibson.dropbear.id.au> wrote:
>
> On Mon, Jan 26, 2026 at 06:42:48PM +0100, Ruslan Ruslichenko wrote:
> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >
> > The patch adds few utility functions for parsing FDT nodes.
> > The helpers are required for upcoming Hardware device tree
> > feature.
> >
> > Signed-off-by: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> > ---
> > include/system/device_tree.h | 30 ++++++
> > system/device_tree.c | 203 +++++++++++++++++++++++++++++++++++
> > 2 files changed, 233 insertions(+)
> >
> > diff --git a/include/system/device_tree.h b/include/system/device_tree.h
> > index f34b8b7ef9..458dbb74b4 100644
> > --- a/include/system/device_tree.h
> > +++ b/include/system/device_tree.h
> > @@ -90,6 +90,13 @@ int qemu_fdt_setprop_string_array(void *fdt, const char *node_path,
> > int qemu_fdt_setprop_phandle(void *fdt, const char *node_path,
> > const char *property,
> > const char *target_node_path);
> > +
> > +uint64_t qemu_fdt_getprop_sized_cell(void *fdt, const char *node_path,
> > + const char *property, int offset,
> > + int size, Error **errp);
> > +const char *qemu_fdt_getprop_string(void *fdt, const char*node_path,
> > + const char *property, int cell,
> > + bool inherit, Error **errp);
> > /**
> > * qemu_fdt_getprop: retrieve the value of a given property
> > * @fdt: pointer to the device tree blob
> > @@ -193,9 +200,32 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
> > qdt_tmp); \
> > })
> >
> > +typedef struct QEMUDevtreeProp {
> > + char *name;
> > + int len;
> > + void *value;
> > +} QEMUDevtreeProp;
> > +
> > +/* node queries */
> > +
> > +char *qemu_devtree_get_node_name(void *fdt, const char *node_path);
> > +int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth);
> > +char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth);
> > +int qemu_devtree_num_props(void *fdt, const char *node_path);
> > +QEMUDevtreeProp *qemu_devtree_get_props(void *fdt, const char *node_path);
> >
> > +/* node getters */
> > +
> > +int qemu_devtree_get_node_by_name(void *fdt, char *node_path,
> > + const char *cmpname);
> > +int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle);
> > int qemu_devtree_getparent(void *fdt, char *node_path,
> > const char *current);
> > +int qemu_devtree_get_root_node(void *fdt, char *node_path);
> > +
> > +/* misc */
> > +
> > +int devtree_get_num_nodes(void *fdt);
> >
> > #define DT_PATH_LENGTH 1024
> >
> > diff --git a/system/device_tree.c b/system/device_tree.c
> > index 41bde0ba5a..7091a4928e 100644
> > --- a/system/device_tree.c
> > +++ b/system/device_tree.c
> > @@ -451,6 +451,50 @@ const void *qemu_fdt_getprop(void *fdt, const char *node_path,
> > return r;
> > }
> >
> > +const char *qemu_fdt_getprop_string(void *fdt, const char*node_path,
> > + const char *property, int cell,
> > + bool inherit, Error **errp)
> > +{
> > + int len;
> > + const void *prop;
> > + Error *err = NULL;
> > +
> > + if (!errp) {
> > + errp = &err;
> > + }
> > +
> > + prop = qemu_fdt_getprop(fdt, node_path, property, &len, inherit, errp);
> > + if (*errp) {
> > + return NULL;
> > + }
> > + while (cell) {
>
> In DT context, "cell" refers specifically to a 32-bit integer value.
> It's not the right word for which string in a stringlist you retrieve.
>
> Also, most of this is re-implementing fdt_stringlist_get() from libfdt.
>
> > + void *term = memchr(prop, '\0', len);
> > + size_t diff;
> > +
> > + if (!term) {
> > + error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
> > + node_path, property, fdt_strerror(len));
> > + return NULL;
> > + }
> > + diff = term - prop + 1;
> > + len -= diff;
> > + assert(len >= 0);
> > + prop += diff;
> > + cell--;
> > + }
> > +
> > + if (!len) {
> > + return NULL;
> > + }
> > +
> > + if (!*(char *)prop) {
> > + error_setg(errp, "%s: Couldn't get %s/%s: %s", __func__,
> > + node_path, property, fdt_strerror(len));
> > + return NULL;
> > + }
> > + return prop;
> > +}
> > +
> > uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
> > const char *property, int offset,
> > bool inherit, Error **errp)
> > @@ -471,6 +515,22 @@ uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
> > return be32_to_cpu(p[offset]);
> > }
> >
> > +uint64_t qemu_fdt_getprop_sized_cell(void *fdt, const char *node_path,
> > + const char *property, int offset,
> > + int size, Error **errp)
> > +{
>
> Again, "cell" is 32-bits in DT context, you need a different word.
>
> > + uint64_t ret = 0;
>
> Since ret is a u64, 'size' must be either 1 or 2. So you might as
> well just have a get_u64() helper (getprop_cell already does u32).
>
> > + for (; size; size--) {
> > + ret <<= 32;
> > + ret |= qemu_fdt_getprop_cell(fdt, node_path, property, offset++, false,
> > + errp);
> > + if (errp && *errp) {
> > + return 0;
> > + }
> > + }
> > + return ret;
> > +}
> > +
> > uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
> > {
> > uint32_t r;
> > @@ -638,6 +698,117 @@ out:
> > return ret;
> > }
> >
> > +char *qemu_devtree_get_node_name(void *fdt, const char *node_path)
> > +{
> > + const char *ret = fdt_get_name(fdt, fdt_path_offset(fdt, node_path), NULL);
>
> This does a full scan of the fdt, which may be unexpected.
>
> > + return ret ? strdup(ret) : NULL;
> > +}
> > +
> > +int qemu_devtree_num_props(void *fdt, const char *node_path)
> > +{
> > + int offset = fdt_path_offset(fdt, node_path);
> > + int ret = 0;
> > +
> > + for (offset = fdt_first_property_offset(fdt, offset);
> > + offset != -FDT_ERR_NOTFOUND;
> > + offset = fdt_next_property_offset(fdt, offset)) {
> > + ret++;
> > + }
> > + return ret;
> > +}
> > +
> > +QEMUDevtreeProp *qemu_devtree_get_props(void *fdt, const char *node_path)
> > +{
> > + QEMUDevtreeProp *ret = g_new0(QEMUDevtreeProp,
> > + qemu_devtree_num_props(fdt, node_path) + 1);
> > + int offset = fdt_path_offset(fdt, node_path);
> > + int i = 0;
> > +
> > + for (offset = fdt_first_property_offset(fdt, offset);
> > + offset != -FDT_ERR_NOTFOUND;
> > + offset = fdt_next_property_offset(fdt, offset)) {
> > + const char *propname;
> > + const void *val = fdt_getprop_by_offset(fdt, offset, &propname,
> > + &ret[i].len);
> > +
> > + ret[i].name = g_strdup(propname);
> > + ret[i].value = g_memdup2(val, ret[i].len);
> > + i++;
> > + }
> > + return ret;
>
> How does the caller free the structure? Using g_free() will leak all
> the copied names and values.
>
> > +}
> > +
> > +static void qemu_devtree_children_info(void *fdt, const char *node_path,
> > + int depth, int *num, char **returned_paths) {
> > + int offset = fdt_path_offset(fdt, node_path);
> > + int root_depth = fdt_node_depth(fdt, offset);
>
> "root" implies the root of the whole tree, not of the node where you
> started. Also, two full scans of the tree above.
>
> > + int cur_depth = root_depth;
> > +
> > + if (num) {
> > + *num = 0;
> > + }
> > + for (;;) {
> > + offset = fdt_next_node(fdt, offset, &cur_depth);
> > + if (cur_depth <= root_depth) {
> > + break;
> > + }
> > + if (cur_depth <= root_depth + depth || depth == 0) {
> > + if (returned_paths) {
> > + returned_paths[*num] = g_malloc0(DT_PATH_LENGTH);
>
> No test to avoid overrunning the length of the returned_paths[] array
> here.
>
> > + fdt_get_path(fdt, offset, returned_paths[*num], DT_PATH_LENGTH);
>
> Another full tree scan for every entry here.
>
> > + }
> > + if (num) {
> > + (*num)++;
> > + }
> > + }
> > + }
> > +}
> > +
> > +char **qemu_devtree_get_children(void *fdt, const char *node_path, int depth)
> > +{
> > + int num_children = qemu_devtree_get_num_children(fdt, node_path, depth);
> > + char **ret = g_malloc0(sizeof(*ret) * num_children);
> > +
> > + qemu_devtree_children_info(fdt, node_path, depth, &num_children, ret);
> > + return ret;
> > +}
> > +
> > +int qemu_devtree_get_num_children(void *fdt, const char *node_path, int depth)
> > +{
> > + int ret;
> > +
> > + qemu_devtree_children_info(fdt, node_path, depth, &ret, NULL);
> > + return ret;
> > +}
> > +
> > +int qemu_devtree_get_node_by_name(void *fdt, char *node_path,
> > + const char *cmpname) {
>
> The semantics of this make no sense to me. It finds the first node
> with the name after the given node, whether it's a child, sibling or
> unrelated node. I'm guessing you want something based on
> fdt_subnode_offset() instead.
>
> > + int offset = 0;
> > + char *name = NULL;
> > +
> > + do {
> > + char *at;
> > +
> > + offset = fdt_next_node(fdt, offset, NULL);
>
> No test for error here, before using 'offset'.
>
> > + name = (void *)fdt_get_name(fdt, offset, NULL);
> > + if (!name) {
> > + continue;
>
> The only errors from fdt_get_name() indicate either a corrupted DT, or
> bad parameters. continuing at this point will not do anything useful.
>
> > + }
> > + at = memchr(name, '@', strlen(name));
> > + if (!strncmp(name, cmpname, at ? at - name : strlen(name))) {
> > + break;
> > + }
> > + } while (offset > 0);
> > + return offset > 0 ?
> > + fdt_get_path(fdt, offset, node_path, DT_PATH_LENGTH) : 1;
> > +}
> > +
> > +int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int phandle)
> > +{
> > + return fdt_get_path(fdt, fdt_node_offset_by_phandle(fdt, phandle),
> > + node_path, DT_PATH_LENGTH);
>
> Two full tree scans.
>
> > +}
> > +
> > int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
> > {
> > int offset = fdt_path_offset(fdt, current);
> > @@ -648,6 +819,38 @@ int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
> > fdt_get_path(fdt, parent_offset, node_path, DT_PATH_LENGTH) : 1;
> > }
> >
> > +int qemu_devtree_get_root_node(void *fdt, char *node_path)
> > +{
> > + return fdt_get_path(fdt, 0, node_path, DT_PATH_LENGTH);
>
> The root node's path is "/" by definition, you don't need this
> function.
>
> > +}
> > +
> > +static void devtree_scan(void *fdt, int *num_nodes)
>
> "scan" is vague. The name should reflect what this actually does.
>
> > +{
> > + int depth = 0, offset = 0;
> > +
> > + if (num_nodes) {
> > + *num_nodes = 0;
> > + }
> > + for (;;) {
> > + offset = fdt_next_node(fdt, offset, &depth);
> > + if (num_nodes) {
> > + (*num_nodes)++;
> > + }
> > + if (offset <= 0 || depth <= 0) {
> > + break;
> > + }
> > + }
> > +}
> > +
> > +int devtree_get_num_nodes(void *fdt)
> > +{
> > + int ret;
> > +
> > + devtree_scan(fdt, &ret);
> > + return ret;
> > +}
> > +
> > +
> > void qmp_dumpdtb(const char *filename, Error **errp)
> > {
> > ERRP_GUARD();
> > --
> > 2.43.0
> >
>
Hi David!
Thank you for the detailed review for this and previous patches.
I will follow up your comments in the v2 patch series.
> --
> David Gibson (he or they) | I'll have my music baroque, and my code
> david AT gibson.dropbear.id.au | minimalist, thank you, not the other way
> | around.
> http://www.ozlabs.org/~dgibson
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-27 18:18 ` Ruslan Ruslichenko
@ 2026-01-28 17:48 ` Alex Bennée
2026-01-28 21:41 ` BALATON Zoltan
2026-01-29 16:11 ` Edgar E. Iglesias
0 siblings, 2 replies; 42+ messages in thread
From: Alex Bennée @ 2026-01-28 17:48 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: Peter Maydell, qemu-devel, qemu-arm, artem_mygaiev,
volodymyr_babchuk, takahiro.nakata.wr, Edgar E . Iglesias,
Ruslan_Ruslichenko
Ruslan Ruslichenko <ruslichenko.r@gmail.com> writes:
> On Tue, Jan 27, 2026 at 11:03 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>>
>> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
>> <ruslichenko.r@gmail.com> wrote:
>> >
>> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>> >
>> > This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
>>
>> I'm afraid this has been a feature that has been suggested from
>> time to time, but which I don't think is workable in general.
>>
>> Device tree files are designed to provide enough information to
>> the guest kernel to allow it to find non-probeable hardware. They
>> are not designed to provide enough information to QEMU to allow
>> it to create and wire up all the hardware present on the system.
>>
>> There are specific niches where it can be made to work -- I think
>> Xilinx have or had a setup where they were generating an FPGA
>> model and a device tree and a guest kernel all from the same
>> single data source, so they could put everything necessary into
>> the dtb, for example -- but I don't think it works in the
>> general case. As one simple example, the DTB doesn't generally
>> have any information about how the Secure world works in an Arm
>> system, because Linux doesn't care about the Secure world. It
>> also doesn't usually have information that the guest OS can
>> probe for itself at runtime.
>>
>> There has been periodic discussion of more flexible user-driven
>> board creation, but that has generally been with the idea of using
>> the QMP monitor to orchestrate creation and connection of device
>> models.
>>
> I agree that a guest Device Tree is insufficient for describing a
> complete QEMU machine model.
> However, we separate the guest configuration from the machine
> definition. The -hw-dtb option allows the user to provide a
> QEMU-specific system description.
> Those hw-dtb would not be passed to Linux Guest VM.
>
> The fact that this workflow has been successfully used in production
> by AMD/Xilinx demonstrates that FDT is a feasible format.
Where are the extensions to the FDT used for hw-dtb documented? How does
it deal with PCI devices and the Secure world?
> In contrast, QMP usage may end up even more complex and less
> maintainable for the task of full system modeling.
> To my understanding, this would need to generate too long configs,
> which may eventually require some intermediate format by itself.
>
> I am proposing to use FDT as a serialization format to describe a
> machine configuration. Theoretically we can use other formats, like
> XML, but in my opinion FDT perfectly matches the requirements.
The QMP interface is self-documenting and introspectable and used for
the management of QEMU including things like hotplug. It is also QOM
aware so a natural fit for dealing with the underlying QOM machinery.
Previous discussions have entertained the idea of making the parsing of
hw-dtb an external script which would then translate into QMP commands
to build up the machine.
>
> BR,
> Ruslan Ruslichenko
>
>
>> thanks
>> -- PMM
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-28 17:48 ` Alex Bennée
@ 2026-01-28 21:41 ` BALATON Zoltan
2026-01-29 12:23 ` Alex Bennée
2026-01-29 16:11 ` Edgar E. Iglesias
1 sibling, 1 reply; 42+ messages in thread
From: BALATON Zoltan @ 2026-01-28 21:41 UTC (permalink / raw)
To: Alex Bennée
Cc: Ruslan Ruslichenko, Peter Maydell, qemu-devel, qemu-arm,
artem_mygaiev, volodymyr_babchuk, takahiro.nakata.wr,
Edgar E . Iglesias, Ruslan_Ruslichenko
[-- Attachment #1: Type: text/plain, Size: 4257 bytes --]
On Wed, 28 Jan 2026, Alex Bennée wrote:
> Ruslan Ruslichenko <ruslichenko.r@gmail.com> writes:
>> On Tue, Jan 27, 2026 at 11:03 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>>> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
>>> <ruslichenko.r@gmail.com> wrote:
>>>>
>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>>
>>>> This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
>>>
>>> I'm afraid this has been a feature that has been suggested from
>>> time to time, but which I don't think is workable in general.
>>>
>>> Device tree files are designed to provide enough information to
>>> the guest kernel to allow it to find non-probeable hardware. They
>>> are not designed to provide enough information to QEMU to allow
>>> it to create and wire up all the hardware present on the system.
>>>
>>> There are specific niches where it can be made to work -- I think
>>> Xilinx have or had a setup where they were generating an FPGA
>>> model and a device tree and a guest kernel all from the same
>>> single data source, so they could put everything necessary into
>>> the dtb, for example -- but I don't think it works in the
>>> general case. As one simple example, the DTB doesn't generally
>>> have any information about how the Secure world works in an Arm
>>> system, because Linux doesn't care about the Secure world. It
>>> also doesn't usually have information that the guest OS can
>>> probe for itself at runtime.
>>>
>>> There has been periodic discussion of more flexible user-driven
>>> board creation, but that has generally been with the idea of using
>>> the QMP monitor to orchestrate creation and connection of device
>>> models.
>>>
>> I agree that a guest Device Tree is insufficient for describing a
>> complete QEMU machine model.
>> However, we separate the guest configuration from the machine
>> definition. The -hw-dtb option allows the user to provide a
>> QEMU-specific system description.
>> Those hw-dtb would not be passed to Linux Guest VM.
>>
>> The fact that this workflow has been successfully used in production
>> by AMD/Xilinx demonstrates that FDT is a feasible format.
>
> Where are the extensions to the FDT used for hw-dtb documented? How does
> it deal with PCI devices and the Secure world?
PCI devices don't need to be included in a machine description as they are
not part of the machine only the PCI host is. PCI devices are plugged into
the host with -device as with boards defined in C. For PCI devices that
are instantiated by default on some machines including a node below the
PCI host should likely work. I know nothing about secure world, that may
need some extensions but fdt is basically describing objects and their
properties so if it's one or a hierarchy of QOM objects that needs to be
instantiated with defined properties it should be possible to describe it
in fdt.
>> In contrast, QMP usage may end up even more complex and less
>> maintainable for the task of full system modeling.
>> To my understanding, this would need to generate too long configs,
>> which may eventually require some intermediate format by itself.
>>
>> I am proposing to use FDT as a serialization format to describe a
>> machine configuration. Theoretically we can use other formats, like
>> XML, but in my opinion FDT perfectly matches the requirements.
>
> The QMP interface is self-documenting and introspectable and used for
> the management of QEMU including things like hotplug. It is also QOM
> aware so a natural fit for dealing with the underlying QOM machinery.
QOM seems to be more about operating a machine rather than defining it for
which fdt is an already established common way.
> Previous discussions have entertained the idea of making the parsing of
> hw-dtb an external script which would then translate into QMP commands
> to build up the machine.
That would be possible but it seems that directly parsing fdt in QEMU
could be done with very few code and with some additional support from
devices the latter of which is probably also needed when trying to
interactively define a machine from command line, monitor or QMP.
Regards,
BALATON Zoltan
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-28 21:41 ` BALATON Zoltan
@ 2026-01-29 12:23 ` Alex Bennée
2026-01-29 15:39 ` Ruslan Ruslichenko
0 siblings, 1 reply; 42+ messages in thread
From: Alex Bennée @ 2026-01-29 12:23 UTC (permalink / raw)
To: BALATON Zoltan
Cc: Ruslan Ruslichenko, Peter Maydell, qemu-devel, qemu-arm,
artem_mygaiev, volodymyr_babchuk, takahiro.nakata.wr,
Edgar E . Iglesias, Ruslan_Ruslichenko
BALATON Zoltan <balaton@eik.bme.hu> writes:
> On Wed, 28 Jan 2026, Alex Bennée wrote:
>> Ruslan Ruslichenko <ruslichenko.r@gmail.com> writes:
>>> On Tue, Jan 27, 2026 at 11:03 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>>>> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
>>>> <ruslichenko.r@gmail.com> wrote:
>>>>>
>>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>>>>>
>>>>> This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
>>>>
>>>> I'm afraid this has been a feature that has been suggested from
>>>> time to time, but which I don't think is workable in general.
>>>>
>>>> Device tree files are designed to provide enough information to
>>>> the guest kernel to allow it to find non-probeable hardware. They
>>>> are not designed to provide enough information to QEMU to allow
>>>> it to create and wire up all the hardware present on the system.
>>>>
>>>> There are specific niches where it can be made to work -- I think
>>>> Xilinx have or had a setup where they were generating an FPGA
>>>> model and a device tree and a guest kernel all from the same
>>>> single data source, so they could put everything necessary into
>>>> the dtb, for example -- but I don't think it works in the
>>>> general case. As one simple example, the DTB doesn't generally
>>>> have any information about how the Secure world works in an Arm
>>>> system, because Linux doesn't care about the Secure world. It
>>>> also doesn't usually have information that the guest OS can
>>>> probe for itself at runtime.
>>>>
>>>> There has been periodic discussion of more flexible user-driven
>>>> board creation, but that has generally been with the idea of using
>>>> the QMP monitor to orchestrate creation and connection of device
>>>> models.
>>>>
>>> I agree that a guest Device Tree is insufficient for describing a
>>> complete QEMU machine model.
>>> However, we separate the guest configuration from the machine
>>> definition. The -hw-dtb option allows the user to provide a
>>> QEMU-specific system description.
>>> Those hw-dtb would not be passed to Linux Guest VM.
>>>
>>> The fact that this workflow has been successfully used in production
>>> by AMD/Xilinx demonstrates that FDT is a feasible format.
>>
>> Where are the extensions to the FDT used for hw-dtb documented? How does
>> it deal with PCI devices and the Secure world?
>
> PCI devices don't need to be included in a machine description as they
> are not part of the machine only the PCI host is. PCI devices are
> plugged into the host with -device as with boards defined in C.
But if we are talking about a general purpose HW description DSL then we
do need to describe systems with embedded PCI busses and soldered on PCI
devices.
> For
> PCI devices that are instantiated by default on some machines
> including a node below the PCI host should likely work. I know nothing
> about secure world, that may need some extensions but fdt is basically
> describing objects and their properties so if it's one or a hierarchy
> of QOM objects that needs to be instantiated with defined properties
> it should be possible to describe it in fdt.
OK - hence I was asking for the documentation for how this is done. You
can't just assert that anything can be described in DTB because its
original use case is purely for OS descriptions. If we want to explore
this series further then I would suggest including some hw-dtb's in the
series to replicate some of our more embedded machines in the next
revision.
>>> In contrast, QMP usage may end up even more complex and less
>>> maintainable for the task of full system modeling.
>>> To my understanding, this would need to generate too long configs,
>>> which may eventually require some intermediate format by itself.
>>>
>>> I am proposing to use FDT as a serialization format to describe a
>>> machine configuration. Theoretically we can use other formats, like
>>> XML, but in my opinion FDT perfectly matches the requirements.
>>
>> The QMP interface is self-documenting and introspectable and used for
>> the management of QEMU including things like hotplug. It is also QOM
>> aware so a natural fit for dealing with the underlying QOM machinery.
>
> QOM seems to be more about operating a machine rather than defining it
> for which fdt is an already established common way.
>
>> Previous discussions have entertained the idea of making the parsing of
>> hw-dtb an external script which would then translate into QMP commands
>> to build up the machine.
>
> That would be possible but it seems that directly parsing fdt in QEMU
> could be done with very few code and with some additional support from
> devices the latter of which is probably also needed when trying to
> interactively define a machine from command line, monitor or QMP.
I agree QMP would need some new APIs to support this - and also have
some examples of how to replicate the existing machines over a pure QMP
interface.
>
> Regards,
> BALATON Zoltan
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-29 12:23 ` Alex Bennée
@ 2026-01-29 15:39 ` Ruslan Ruslichenko
2026-02-06 18:34 ` Alex Bennée
0 siblings, 1 reply; 42+ messages in thread
From: Ruslan Ruslichenko @ 2026-01-29 15:39 UTC (permalink / raw)
To: Alex Bennée
Cc: BALATON Zoltan, Peter Maydell, qemu-devel, qemu-arm,
artem_mygaiev, volodymyr_babchuk, takahiro.nakata.wr,
Edgar E . Iglesias, Ruslan_Ruslichenko
On Thu, Jan 29, 2026 at 1:23 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>
> BALATON Zoltan <balaton@eik.bme.hu> writes:
>
> > On Wed, 28 Jan 2026, Alex Bennée wrote:
> >> Ruslan Ruslichenko <ruslichenko.r@gmail.com> writes:
> >>> On Tue, Jan 27, 2026 at 11:03 AM Peter Maydell <peter.maydell@linaro.org> wrote:
> >>>> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
> >>>> <ruslichenko.r@gmail.com> wrote:
> >>>>>
> >>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >>>>>
> >>>>> This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
> >>>>
> >>>> I'm afraid this has been a feature that has been suggested from
> >>>> time to time, but which I don't think is workable in general.
> >>>>
> >>>> Device tree files are designed to provide enough information to
> >>>> the guest kernel to allow it to find non-probeable hardware. They
> >>>> are not designed to provide enough information to QEMU to allow
> >>>> it to create and wire up all the hardware present on the system.
> >>>>
> >>>> There are specific niches where it can be made to work -- I think
> >>>> Xilinx have or had a setup where they were generating an FPGA
> >>>> model and a device tree and a guest kernel all from the same
> >>>> single data source, so they could put everything necessary into
> >>>> the dtb, for example -- but I don't think it works in the
> >>>> general case. As one simple example, the DTB doesn't generally
> >>>> have any information about how the Secure world works in an Arm
> >>>> system, because Linux doesn't care about the Secure world. It
> >>>> also doesn't usually have information that the guest OS can
> >>>> probe for itself at runtime.
> >>>>
> >>>> There has been periodic discussion of more flexible user-driven
> >>>> board creation, but that has generally been with the idea of using
> >>>> the QMP monitor to orchestrate creation and connection of device
> >>>> models.
> >>>>
> >>> I agree that a guest Device Tree is insufficient for describing a
> >>> complete QEMU machine model.
> >>> However, we separate the guest configuration from the machine
> >>> definition. The -hw-dtb option allows the user to provide a
> >>> QEMU-specific system description.
> >>> Those hw-dtb would not be passed to Linux Guest VM.
> >>>
> >>> The fact that this workflow has been successfully used in production
> >>> by AMD/Xilinx demonstrates that FDT is a feasible format.
> >>
> >> Where are the extensions to the FDT used for hw-dtb documented? How does
> >> it deal with PCI devices and the Secure world?
> >
> > PCI devices don't need to be included in a machine description as they
> > are not part of the machine only the PCI host is. PCI devices are
> > plugged into the host with -device as with boards defined in C.
>
> But if we are talking about a general purpose HW description DSL then we
> do need to describe systems with embedded PCI busses and soldered on PCI
> devices.
>
> > For
> > PCI devices that are instantiated by default on some machines
> > including a node below the PCI host should likely work. I know nothing
> > about secure world, that may need some extensions but fdt is basically
> > describing objects and their properties so if it's one or a hierarchy
> > of QOM objects that needs to be instantiated with defined properties
> > it should be possible to describe it in fdt.
>
> OK - hence I was asking for the documentation for how this is done. You
> can't just assert that anything can be described in DTB because its
> original use case is purely for OS descriptions. If we want to explore
> this series further then I would suggest including some hw-dtb's in the
> series to replicate some of our more embedded machines in the next
> revision.
>
Sure, we can consider more functional examples for the v2 patch series.
Would you like to see some specific machine constructed with hw-dtb?
Also answering the previous question regarding Secure world.
We can define parent nodes in the FDT that represent Memory Region
containers, which are then mapped into the CPU's Secure Address Space.
Any child devices defined under these nodes in the FDT are automatically
instantiated as subregions of that parent container. This ensures they
are only visible to the Secure World.
> >>> In contrast, QMP usage may end up even more complex and less
> >>> maintainable for the task of full system modeling.
> >>> To my understanding, this would need to generate too long configs,
> >>> which may eventually require some intermediate format by itself.
> >>>
> >>> I am proposing to use FDT as a serialization format to describe a
> >>> machine configuration. Theoretically we can use other formats, like
> >>> XML, but in my opinion FDT perfectly matches the requirements.
> >>
> >> The QMP interface is self-documenting and introspectable and used for
> >> the management of QEMU including things like hotplug. It is also QOM
> >> aware so a natural fit for dealing with the underlying QOM machinery.
> >
> > QOM seems to be more about operating a machine rather than defining it
> > for which fdt is an already established common way.
> >
> >> Previous discussions have entertained the idea of making the parsing of
> >> hw-dtb an external script which would then translate into QMP commands
> >> to build up the machine.
> >
> > That would be possible but it seems that directly parsing fdt in QEMU
> > could be done with very few code and with some additional support from
> > devices the latter of which is probably also needed when trying to
> > interactively define a machine from command line, monitor or QMP.
>
> I agree QMP would need some new APIs to support this - and also have
> some examples of how to replicate the existing machines over a pure QMP
> interface.
>
> >
> > Regards,
> > BALATON Zoltan
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-28 17:48 ` Alex Bennée
2026-01-28 21:41 ` BALATON Zoltan
@ 2026-01-29 16:11 ` Edgar E. Iglesias
1 sibling, 0 replies; 42+ messages in thread
From: Edgar E. Iglesias @ 2026-01-29 16:11 UTC (permalink / raw)
To: Alex Bennée
Cc: Ruslan Ruslichenko, Peter Maydell, qemu-devel, qemu-arm,
artem_mygaiev, volodymyr_babchuk, takahiro.nakata.wr,
Ruslan_Ruslichenko
[-- Attachment #1: Type: text/plain, Size: 4343 bytes --]
On Wed, Jan 28, 2026 at 11:48 AM Alex Bennée <alex.bennee@linaro.org> wrote:
> Ruslan Ruslichenko <ruslichenko.r@gmail.com> writes:
>
> > On Tue, Jan 27, 2026 at 11:03 AM Peter Maydell <peter.maydell@linaro.org>
> wrote:
> >>
> >> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
> >> <ruslichenko.r@gmail.com> wrote:
> >> >
> >> > From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
> >> >
> >> > This patch series introduces new ARM machine model, arm-generic-fdt,
> and the underlying infrastructure required to instantiate a QEMU machine
> from a Device Tree.
> >>
> >> I'm afraid this has been a feature that has been suggested from
> >> time to time, but which I don't think is workable in general.
> >>
> >> Device tree files are designed to provide enough information to
> >> the guest kernel to allow it to find non-probeable hardware. They
> >> are not designed to provide enough information to QEMU to allow
> >> it to create and wire up all the hardware present on the system.
> >>
> >> There are specific niches where it can be made to work -- I think
> >> Xilinx have or had a setup where they were generating an FPGA
> >> model and a device tree and a guest kernel all from the same
> >> single data source, so they could put everything necessary into
> >> the dtb, for example -- but I don't think it works in the
> >> general case. As one simple example, the DTB doesn't generally
> >> have any information about how the Secure world works in an Arm
> >> system, because Linux doesn't care about the Secure world. It
> >> also doesn't usually have information that the guest OS can
> >> probe for itself at runtime.
> >>
> >> There has been periodic discussion of more flexible user-driven
> >> board creation, but that has generally been with the idea of using
> >> the QMP monitor to orchestrate creation and connection of device
> >> models.
> >>
> > I agree that a guest Device Tree is insufficient for describing a
> > complete QEMU machine model.
> > However, we separate the guest configuration from the machine
> > definition. The -hw-dtb option allows the user to provide a
> > QEMU-specific system description.
> > Those hw-dtb would not be passed to Linux Guest VM.
> >
> > The fact that this workflow has been successfully used in production
> > by AMD/Xilinx demonstrates that FDT is a feasible format.
>
> Where are the extensions to the FDT used for hw-dtb documented? How does
> it deal with PCI devices and the Secure world?
>
>
Hi,
When we first started using this, we tried using the kernel bindings but as
people have already noted on this thread those are not a good fit. So we
changed the approach to something more like a 1:1 mapping between FDT and
QOM. FDT compatible properties match on QOM device names, properties are
set more or less 1:1 with some exceptions.
Interconnects and TZ are modelled by instantiating memory regions, mapping
the selected set of "target" devices and connecting bus masters (CPUs or
DMA) "memory" links.
In addition to that, memory transaction attributes can be used for devices
that differentiate things on a per transaction basis.
This is all the same as you would do in C code with QOM/Qdev.
PCI is modelled the same way as with QOM/qdev.
Cheers,
Edgar
> > In contrast, QMP usage may end up even more complex and less
> > maintainable for the task of full system modeling.
> > To my understanding, this would need to generate too long configs,
> > which may eventually require some intermediate format by itself.
> >
> > I am proposing to use FDT as a serialization format to describe a
> > machine configuration. Theoretically we can use other formats, like
> > XML, but in my opinion FDT perfectly matches the requirements.
>
> The QMP interface is self-documenting and introspectable and used for
> the management of QEMU including things like hotplug. It is also QOM
> aware so a natural fit for dealing with the underlying QOM machinery.
>
> Previous discussions have entertained the idea of making the parsing of
> hw-dtb an external script which would then translate into QMP commands
> to build up the machine.
>
> >
> > BR,
> > Ruslan Ruslichenko
> >
> >
> >> thanks
> >> -- PMM
>
> --
> Alex Bennée
> Virtualisation Tech Lead @ Linaro
>
[-- Attachment #2: Type: text/html, Size: 5707 bytes --]
^ permalink raw reply [flat|nested] 42+ messages in thread
* Re: [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure
2026-01-29 15:39 ` Ruslan Ruslichenko
@ 2026-02-06 18:34 ` Alex Bennée
0 siblings, 0 replies; 42+ messages in thread
From: Alex Bennée @ 2026-02-06 18:34 UTC (permalink / raw)
To: Ruslan Ruslichenko
Cc: BALATON Zoltan, Peter Maydell, qemu-devel, qemu-arm,
artem_mygaiev, volodymyr_babchuk, takahiro.nakata.wr,
Edgar E . Iglesias, Ruslan_Ruslichenko
Ruslan Ruslichenko <ruslichenko.r@gmail.com> writes:
> On Thu, Jan 29, 2026 at 1:23 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>>
>> BALATON Zoltan <balaton@eik.bme.hu> writes:
>>
>> > On Wed, 28 Jan 2026, Alex Bennée wrote:
>> >> Ruslan Ruslichenko <ruslichenko.r@gmail.com> writes:
>> >>> On Tue, Jan 27, 2026 at 11:03 AM Peter Maydell <peter.maydell@linaro.org> wrote:
>> >>>> On Mon, 26 Jan 2026 at 17:43, Ruslan Ruslichenko
>> >>>> <ruslichenko.r@gmail.com> wrote:
>> >>>>>
>> >>>>> From: Ruslan Ruslichenko <Ruslan_Ruslichenko@epam.com>
>> >>>>>
>> >>>>> This patch series introduces new ARM machine model, arm-generic-fdt, and the underlying infrastructure required to instantiate a QEMU machine from a Device Tree.
>> >>>>
>> >>>> I'm afraid this has been a feature that has been suggested from
>> >>>> time to time, but which I don't think is workable in general.
>> >>>>
>> >>>> Device tree files are designed to provide enough information to
>> >>>> the guest kernel to allow it to find non-probeable hardware. They
>> >>>> are not designed to provide enough information to QEMU to allow
>> >>>> it to create and wire up all the hardware present on the system.
>> >>>>
>> >>>> There are specific niches where it can be made to work -- I think
>> >>>> Xilinx have or had a setup where they were generating an FPGA
>> >>>> model and a device tree and a guest kernel all from the same
>> >>>> single data source, so they could put everything necessary into
>> >>>> the dtb, for example -- but I don't think it works in the
>> >>>> general case. As one simple example, the DTB doesn't generally
>> >>>> have any information about how the Secure world works in an Arm
>> >>>> system, because Linux doesn't care about the Secure world. It
>> >>>> also doesn't usually have information that the guest OS can
>> >>>> probe for itself at runtime.
>> >>>>
>> >>>> There has been periodic discussion of more flexible user-driven
>> >>>> board creation, but that has generally been with the idea of using
>> >>>> the QMP monitor to orchestrate creation and connection of device
>> >>>> models.
>> >>>>
>> >>> I agree that a guest Device Tree is insufficient for describing a
>> >>> complete QEMU machine model.
>> >>> However, we separate the guest configuration from the machine
>> >>> definition. The -hw-dtb option allows the user to provide a
>> >>> QEMU-specific system description.
>> >>> Those hw-dtb would not be passed to Linux Guest VM.
>> >>>
>> >>> The fact that this workflow has been successfully used in production
>> >>> by AMD/Xilinx demonstrates that FDT is a feasible format.
>> >>
>> >> Where are the extensions to the FDT used for hw-dtb documented? How does
>> >> it deal with PCI devices and the Secure world?
>> >
>> > PCI devices don't need to be included in a machine description as they
>> > are not part of the machine only the PCI host is. PCI devices are
>> > plugged into the host with -device as with boards defined in C.
>>
>> But if we are talking about a general purpose HW description DSL then we
>> do need to describe systems with embedded PCI busses and soldered on PCI
>> devices.
>>
>> > For
>> > PCI devices that are instantiated by default on some machines
>> > including a node below the PCI host should likely work. I know nothing
>> > about secure world, that may need some extensions but fdt is basically
>> > describing objects and their properties so if it's one or a hierarchy
>> > of QOM objects that needs to be instantiated with defined properties
>> > it should be possible to describe it in fdt.
>>
>> OK - hence I was asking for the documentation for how this is done. You
>> can't just assert that anything can be described in DTB because its
>> original use case is purely for OS descriptions. If we want to explore
>> this series further then I would suggest including some hw-dtb's in the
>> series to replicate some of our more embedded machines in the next
>> revision.
>>
>
> Sure, we can consider more functional examples for the v2 patch series.
> Would you like to see some specific machine constructed with hw-dtb?
>
> Also answering the previous question regarding Secure world.
> We can define parent nodes in the FDT that represent Memory Region
> containers, which are then mapped into the CPU's Secure Address Space.
>
> Any child devices defined under these nodes in the FDT are automatically
> instantiated as subregions of that parent container. This ensures they
> are only visible to the Secure World.
I would suggest we pick a representative sample of non-virt machine
types to model in hw-dtb and extend the functional tests to run both. We
have these tests (trimmed of virt, pc, and q35 machines):
riscv64/test_boston.py:36: self.set_machine('boston-aia')
riscv64/test_sifive_u.py:30: self.set_machine('sifive_u')
riscv64/test_migration.py:17: self.set_machine('spike')
riscv64/test_opensbi.py:24: self.set_machine('spike')
riscv64/test_opensbi.py:28: self.set_machine('sifive_u')
sparc64/test_tuxrun.py:27: self.set_machine('sun4u')
sparc64/test_sun4u.py:28: self.set_machine('sun4u')
sparc64/test_migration.py:13: self.set_machine('sun4u')
sparc64/test_migration.py:17: self.set_machine('sun4u')
sparc64/test_migration.py:21: self.set_machine('sun4u')
mipsel/test_malta.py:41: self.set_machine('malta')
mipsel/test_malta.py:97: self.set_machine('malta')
ppc/test_74xx.py:19: self.set_machine('g3beige')
ppc/test_74xx.py:28: self.set_machine('g3beige')
ppc/test_74xx.py:37: self.set_machine('g3beige')
ppc/test_74xx.py:46: self.set_machine('g3beige')
ppc/test_74xx.py:55: self.set_machine('g3beige')
ppc/test_74xx.py:64: self.set_machine('g3beige')
ppc/test_74xx.py:73: self.set_machine('g3beige')
ppc/test_74xx.py:82: self.set_machine('g3beige')
ppc/test_74xx.py:91: self.set_machine('g3beige')
ppc/test_74xx.py:100: self.set_machine('g3beige')
ppc/test_74xx.py:109: self.set_machine('g3beige')
ppc/test_74xx.py:118: self.set_machine('g3beige')
mips64el/test_malta.py:46: self.set_machine('malta')
mips64el/test_malta.py:72: self.set_machine('malta')
mips64el/test_malta.py:145: self.set_machine('malta')
ppc/test_tuxrun.py:27: self.set_machine('ppce500')
mipsel/test_tuxrun.py:27: self.set_machine('malta')
ppc/test_bamboo.py:29: self.set_machine('bamboo')
sparc/test_sun4m.py:18: self.set_machine('SS-20')
ppc/test_replay.py:25: self.set_machine('g3beige')
ppc/test_replay.py:29: self.set_machine('mac99')
mipsel/test_replay.py:30: self.set_machine('malta')
sparc/test_replay.py:19: self.set_machine('SS-10')
mips64el/test_tuxrun.py:27: self.set_machine('malta')
mips64/test_tuxrun.py:27: self.set_machine('malta')
ppc/test_40p.py:40: self.set_machine('40p')
ppc/test_40p.py:55: self.set_machine('40p')
ppc/test_40p.py:66: self.set_machine('40p')
ppc/test_40p.py:81: self.set_machine('40p')
sparc/test_migration.py:13: self.set_machine('SS-4')
sparc/test_migration.py:17: self.set_machine('SS-5')
sparc/test_migration.py:21: self.set_machine('SS-4')
mips64el/test_replay.py:22: self.set_machine('malta')
mips64el/test_replay.py:45: self.set_machine('malta')
ppc/test_amiga.py:25: self.set_machine('amigaone')
avr/test_uno.py:21: self.set_machine('arduino-uno')
ppc/test_ppe42.py:68: self.set_machine('ppe42_machine')
avr/test_mega2560.py:38: self.set_machine('arduino-mega-2560-v3')
mips64el/test_fuloong2e.py:33: self.set_machine('fuloong2e')
mips64el/test_fuloong2e.py:54: self.set_machine('fuloong2e')
ppc/test_migration.py:13: self.set_machine('sam460ex')
ppc/test_migration.py:17: self.set_machine('sam460ex')
ppc/test_migration.py:21: self.set_machine('sam460ex')
ppc/test_mpc8544ds.py:27: self.set_machine('mpc8544ds')
ppc/test_sam460ex.py:19: self.set_machine('sam460ex')
ppc/test_mac.py:28: self.set_machine('g3beige')
ppc/test_mac.py:32: self.set_machine('mac99')
ppc/test_virtex_ml507.py:27: self.set_machine('virtex-ml507')
riscv32/test_migration.py:13: self.set_machine('spike')
riscv32/test_migration.py:21: self.set_machine('spike')
mips/test_malta.py:57: test.set_machine('malta')
mips/test_malta.py:108: self.set_machine('malta')
mips/test_malta.py:131: self.set_machine('malta')
sh4eb/test_r2d.py:18: self.set_machine('r2d')
or1k/test_sim.py:18: self.set_machine('or1k-sim')
alpha/test_replay.py:20: self.set_machine('clipper')
mips/test_tuxrun.py:27: self.set_machine('malta')
or1k/test_replay.py:19: self.set_machine('or1k-sim')
alpha/test_clipper.py:19: self.set_machine('clipper')
mips/test_replay.py:22: self.set_machine('malta')
mips/test_replay.py:43: self.set_machine('malta')
arm/test_tuxrun.py:30: self.set_machine('versatilepb')
alpha/test_migration.py:13: self.set_machine('clipper')
alpha/test_migration.py:17: self.set_machine('clipper')
alpha/test_migration.py:21: self.set_machine('clipper')
m68k/test_mcf5208evb.py:18: self.set_machine('mcf5208evb')
m68k/test_replay.py:21: self.set_machine('q800')
m68k/test_replay.py:35: self.set_machine('mcf5208evb')
arm/test_bpim2u.py:40: self.set_machine('bpim2u')
arm/test_bpim2u.py:61: self.set_machine('bpim2u')
arm/test_bpim2u.py:94: self.set_machine('bpim2u')
arm/test_bpim2u.py:142: self.set_machine('bpim2u')
rx/test_gdbsim.py:37: self.set_machine('gdbsim-r5f562n8')
rx/test_gdbsim.py:57: self.set_machine('gdbsim-r5f562n7')
m68k/test_nextcube.py:49: self.set_machine('next-cube')
m68k/test_nextcube.py:61: self.set_machine('next-cube')
s390x/test_reverse_debug.py:15: self.set_machine('s390-ccw-virtio')
arm/test_integratorcp.py:45: self.set_machine('integratorcp')
hppa/test_cdboot.py:19: self.set_machine('B160L')
m68k/test_q800.py:19: self.set_machine('q800')
hppa/test_seabios.py:27: self.set_machine('B160L')
hppa/test_seabios.py:31: self.set_machine('C3700')
arm/test_cubieboard.py:38: self.set_machine('cubieboard')
arm/test_cubieboard.py:69: self.set_machine('cubieboard')
arm/test_cubieboard.py:107: self.set_machine('cubieboard')
arm/test_realview.py:21: self.set_machine('realview-eb-mpcore')
s390x/test_pxelinux.py:56: self.set_machine('s390-ccw-virtio')
arm/test_sx1.py:40: self.set_machine('sx1')
arm/test_sx1.py:51: self.set_machine('sx1')
arm/test_sx1.py:62: self.set_machine('sx1')
arm/test_replay.py:38: self.set_machine('cubieboard')
arm/test_replay.py:60: self.set_machine('vexpress-a9')
arm/test_aspeed_ast2600_buildroot.py:25: self.set_machine('ast2600-evb')
arm/test_aspeed_ast2600_buildroot.py:99: self.set_machine('ast2600-evb')
arm/test_canona1100.py:27: self.set_machine('canon-a1100')
sh4/test_r2d.py:21: self.set_machine('r2d')
xtensa/test_replay.py:19: self.set_machine('lx60')
arm/test_max78000fthr.py:19: self.set_machine('max78000fthr')
arm/test_raspi2.py:42: self.set_machine('raspi2b')
arm/test_raspi2.py:68: self.set_machine('raspi2b')
microblaze/test_replay.py:20: self.set_machine('petalogix-s3adsp1800')
sh4/test_tuxrun.py:27: self.set_machine('r2d')
xtensa/test_lx60.py:18: self.set_machine('lx60')
arm/test_smdkc210.py:24: self.set_machine('smdkc210')
arm/test_microbit.py:19: self.set_machine('microbit')
arm/test_collie.py:22: self.set_machine('collie')
arm/test_quanta_gsj.py:35: self.set_machine('quanta-gsj')
arm/test_quanta_gsj.py:71: self.set_machine('quanta-gsj')
arm/test_vexpress.py:18: self.set_machine('vexpress-a9')
microblaze/test_s3adsp1800.py:29: self.set_machine('petalogix-s3adsp1800')
microblaze/test_s3adsp1800.py:46: self.set_machine('petalogix-s3adsp1800')
arm/test_emcraft_sf2.py:26: self.set_machine('emcraft-sf2')
arm/test_aspeed_ast1060.py:21: self.set_machine('ast1060-evb')
arm/test_aspeed_ast1060.py:35: self.vm.set_machine("ast1060-evb")
arm/test_aspeed_rainier.py:16: self.set_machine('rainier-bmc')
arm/test_aspeed_rainier.py:42: self.set_machine('rainier-bmc')
arm/test_aspeed_ast1030.py:21: self.set_machine('ast1030-evb')
arm/test_aspeed_ast1030.py:40: self.set_machine('ast1030-evb')
arm/test_aspeed_ast1030.py:72: self.vm.set_machine("ast1030-evb")
arm/test_aspeed_ast2600_sdk.py:35: self.set_machine('ast2600-evb')
arm/test_aspeed_ast2600_sdk.py:74: self.vm.set_machine("ast2600-evb")
arm/test_stellaris.py:18: self.set_machine('lm3s6965evb')
arm/test_stellaris.py:32: self.set_machine('lm3s811evb')
arm/test_migration.py:13: self.set_machine('npcm750-evb')
arm/test_migration.py:17: self.set_machine('npcm750-evb')
arm/test_migration.py:21: self.set_machine('npcm750-evb')
arm/test_aspeed_ast2500.py:19: self.set_machine('ast2500-evb')
arm/test_aspeed_ast2500.py:45: self.set_machine('ast2500-evb')
aarch64/test_raspi4.py:37: self.set_machine('raspi4b')
aarch64/test_raspi4.py:67: self.set_machine('raspi4b')
arm/test_orangepi.py:51: self.set_machine('orangepi-pc')
arm/test_orangepi.py:72: self.set_machine('orangepi-pc')
arm/test_orangepi.py:105: self.set_machine('orangepi-pc')
arm/test_orangepi.py:149: self.set_machine('orangepi-pc')
arm/test_orangepi.py:185: self.set_machine('orangepi-pc')
ppc64/test_reverse_debug.py:25: self.set_machine('pseries')
ppc64/test_reverse_debug.py:33: self.set_machine('powernv')
aarch64/test_imx8mp_evk.py:53: self.set_machine('imx8mp-evk')
ppc64/test_migration.py:14: self.set_machine('mac99')
ppc64/test_migration.py:18: self.set_machine('mac99')
ppc64/test_migration.py:22: self.set_machine('mac99')
ppc64/test_e500.py:22: self.set_machine('ppce500')
aarch64/test_raspi3.py:24: self.set_machine('raspi3b')
ppc64/test_mac99.py:23: self.set_machine('mac99')
ppc64/test_replay.py:21: self.set_machine('ppce500')
ppc64/test_replay.py:34: self.set_machine('pseries')
aarch64/test_aspeed_ast2700.py:151: self.set_machine('ast2700a1-evb')
aarch64/test_aspeed_ast2700.py:163: self.set_machine('ast2700a1-evb')
aarch64/test_aspeed_ast2700.py:174: self.set_machine('ast2700a1-evb')
aarch64/test_aspeed_ast2700.py:178: self.vm.set_machine('ast2700a1-evb,fmc-model=w25q512jv')
aarch64/test_migration.py:13: self.set_machine('quanta-gsj')
aarch64/test_migration.py:17: self.set_machine('quanta-gsj')
aarch64/test_migration.py:21: self.set_machine('quanta-gsj')
aarch64/test_aspeed_ast2700fc.py:152: self.set_machine('ast2700fc')
aarch64/test_aspeed_ast2700fc.py:164: self.set_machine('ast2700fc')
Thinking about it Arm's sbsa-ref would be an interesting one to check
for fidelity as the firmware stack targets an explicit HW platform.
>
>> >>> In contrast, QMP usage may end up even more complex and less
>> >>> maintainable for the task of full system modeling.
>> >>> To my understanding, this would need to generate too long configs,
>> >>> which may eventually require some intermediate format by itself.
>> >>>
>> >>> I am proposing to use FDT as a serialization format to describe a
>> >>> machine configuration. Theoretically we can use other formats, like
>> >>> XML, but in my opinion FDT perfectly matches the requirements.
>> >>
>> >> The QMP interface is self-documenting and introspectable and used for
>> >> the management of QEMU including things like hotplug. It is also QOM
>> >> aware so a natural fit for dealing with the underlying QOM machinery.
>> >
>> > QOM seems to be more about operating a machine rather than defining it
>> > for which fdt is an already established common way.
>> >
>> >> Previous discussions have entertained the idea of making the parsing of
>> >> hw-dtb an external script which would then translate into QMP commands
>> >> to build up the machine.
>> >
>> > That would be possible but it seems that directly parsing fdt in QEMU
>> > could be done with very few code and with some additional support from
>> > devices the latter of which is probably also needed when trying to
>> > interactively define a machine from command line, monitor or QMP.
>>
>> I agree QMP would need some new APIs to support this - and also have
>> some examples of how to replicate the existing machines over a pure QMP
>> interface.
>>
>> >
>> > Regards,
>> > BALATON Zoltan
>>
>> --
>> Alex Bennée
>> Virtualisation Tech Lead @ Linaro
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
^ permalink raw reply [flat|nested] 42+ messages in thread
end of thread, other threads:[~2026-02-06 18:35 UTC | newest]
Thread overview: 42+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-26 17:42 [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 01/27] system/device_tree: update qemu_fdt_getprop/_cell Ruslan Ruslichenko
2026-01-27 1:56 ` David Gibson
2026-01-26 17:42 ` [PATCH 02/27] system/device_tree: add few parsing and traversal helpers Ruslan Ruslichenko
2026-01-27 2:19 ` David Gibson
2026-01-27 21:22 ` Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 03/27] util/log: add log entry for fdt generic utils Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 04/27] hw/core: introduce generic FDT device model registry Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 05/27] hw/core/fdt_generic: implement FDT machine creation helpers Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 06/27] hw/core/fdt_generic: add cpu clusters management Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 07/27] hw/core/fdt_generic_util: implement main fdt parse routine Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 08/27] hw/core/fdt_generic_util: implement fdt_init_qdev Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 09/27] hw/core/fdt_generic_util: initilize qdev properties from fdt Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 10/27] hw/core/fdt_generic_util: actually realize device Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 11/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_MMAP Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 12/27] hw/core/fdt_generic_util: add TYPE_FDT_GENERIC_INTC Ruslan Ruslichenko
2026-01-26 17:42 ` [PATCH 13/27] hw/core/fdt_generic_util: implement fdt_get_irq/_info API Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 14/27] hw/core/fdt_generic_util: map device memory Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 15/27] hw/core/fdt_generic_util: Connect device irqs Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 16/27] hw/core/fdt_generic_util: realize cpu clusters Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 17/27] hw/core: add fdt_generic to the build Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 18/27] hw/core/machine: add '-hw-dtb' option for machine Ruslan Ruslichenko
2026-01-27 8:40 ` Zhao Liu
2026-01-27 20:12 ` Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 19/27] hw/arm: add generic ARM machine initialized by FDT Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 20/27] hw/core/sysbus: implement FDT_GENERIC_MMAP_CLASS interface Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 21/27] hw/intc/arm_gic: implement FDT_GENERIC_INTC and fdt support Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 22/27] target/arm/cpu: add fdt support for armv8-timer Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 23/27] qom/object: export object_resolve_link() Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 24/27] system/memory: add setters for MemoryRegion properties Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 25/27] system/memory: implement FDT_GENERIC_MMAP interface Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 26/27] hw/core/fdt_generic_util: initialize serial devices Ruslan Ruslichenko
2026-01-26 17:43 ` [PATCH 27/27] system/memory: add QOM aliases for fdt support Ruslan Ruslichenko
2026-01-27 10:02 ` [PATCH 00/27] hw/arm: Add generic FDT-based machine and infrastructure Peter Maydell
2026-01-27 14:29 ` BALATON Zoltan
2026-01-27 18:18 ` Ruslan Ruslichenko
2026-01-28 17:48 ` Alex Bennée
2026-01-28 21:41 ` BALATON Zoltan
2026-01-29 12:23 ` Alex Bennée
2026-01-29 15:39 ` Ruslan Ruslichenko
2026-02-06 18:34 ` Alex Bennée
2026-01-29 16:11 ` Edgar E. Iglesias
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.