On Mon, Mar 2, 2026 at 2:41 AM David Gibson <[email protected]> wrote:
>
> On Thu, Feb 19, 2026 at 03:33:01PM +0100, Ruslan Ruslichenko wrote:
> > From: Ruslan Ruslichenko <[email protected]>
> >
> > 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 <[email protected]>
> > ---
> > include/system/device_tree.h | 30 +++++
> > system/device_tree.c | 221 +++++++++++++++++++++++++++++++++++
> > 2 files changed, 251 insertions(+)
> >
> > diff --git a/include/system/device_tree.h b/include/system/device_tree.h
> > index 5667ff9538..9d6b570bdb 100644
> > --- a/include/system/device_tree.h
> > +++ b/include/system/device_tree.h
> > @@ -116,6 +116,11 @@ const void *qemu_fdt_getprop(void *fdt, const char
> > *node_path,
> > uint32_t qemu_fdt_getprop_cell(void *fdt, const char *node_path,
> > const char *property, int cell_id,
> > Error **errp);
> > +
> > +const void *qemu_fdt_getprop_inherited(void *fdt, const char *node_path,
> > + const char *property, int *lenp, Error
> > **errp);
> > +uint32_t qemu_fdt_getprop_cell_inherited(void *fdt, const char *node_path,
> > + const char *property, int cell_id, 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);
> > @@ -191,6 +196,31 @@ int qemu_fdt_setprop_sized_cells_from_array(void *fdt,
> > qdt_tmp); \
> > })
> >
> > +typedef struct QEMUDevtreeProp {
> > + const char *name;
> > + int len;
> > + const void *value;
> > +} QEMUDevtreeProp;
>
> If you need this, it really starts to seem to me that keeping the data
> structure in fdt is not the right choice any more. I've thought for a
> while that with increasing handling of the device tree by qemu, we
> should maybe have a "live" device tree representation (with pointers)
> that we use for manipulation. Right before boot, that can be
> serialized into fdt.
>
> fdt is, fundamentally, a serialization format. Direct fdt
> manipulation with libfdt was really only intended as something to
> allow firmwares and similarly minimal programs to make small tweaks.
> Once manipulation becomes extensive, fdt is no longer a good choice of
> data structure.
>
For this series, the generic machine is strictly read-only regarding the FDT
(we only parse it to instantiate devices, we don't manipulate or
rewrite the tree).
So to simplify things, I can drop the QEMUDevtreeProp wrapper and just
use standard
libfdt iteration macros (like fdt_for_each_property_offset) to keep
the footprint
as small as possible.
Would this approach be acceptable for now? I can prepare this modification along
with other review comments for v3 patch series.
> > +
> > +/* node queries */
> > +
> > +int qemu_devtree_get_num_children(void *fdt, const char *node_path);
> > +int qemu_devtree_get_children(void *fdt, const char *node_path,
> > + int max_paths, char **returned_paths);
> > +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_phandle(void *fdt, char *node_path, int
> > phandle);
> > +int qemu_devtree_getparent(void *fdt, char *node_path,
> > + const char *current);
> > +
> > +/* misc */
> > +
> > +int devtree_get_num_nodes(void *fdt);
> > +
> > +#define DT_PATH_LENGTH 1024
> >
> > /**
> > * qemu_fdt_randomize_seeds:
> > diff --git a/system/device_tree.c b/system/device_tree.c
> > index d2db7bd355..fc34c660db 100644
> > --- a/system/device_tree.c
> > +++ b/system/device_tree.c
> > @@ -464,6 +464,103 @@ uint32_t qemu_fdt_getprop_cell(void *fdt, const char
> > *node_path,
> > return be32_to_cpu(p[cell_id]);
> > }
> >
> > +const void *qemu_fdt_getprop_inherited(void *fdt, const char *node_path,
> > + const char *property, int *lenp, Error **errp)
> > +{
> > + const void *found_val = NULL;
> > + int found_len = 0;
> > + int curr_offset = 0;
> > + int temp_len;
> > + char **tokens;
> > + char **iter;
> > +
> > + found_val = fdt_getprop(fdt, 0, property, &found_len);
> > +
> > + tokens = g_strsplit(node_path + 1, "/", -1);
> > +
> > + for (iter = tokens; *iter != NULL; iter++) {
> > + if (**iter == '\0') {
> > + continue;
> > + }
> > +
> > + curr_offset = fdt_subnode_offset(fdt, curr_offset, *iter);
> > + if (curr_offset < 0) {
> > + error_setg(errp, "%s: Path '%s' not found",
> > + __func__, *iter);
> > + g_strfreev(tokens);
> > + return NULL;
> > + }
> > +
> > + const void *val = fdt_getprop(fdt, curr_offset, property,
> > &temp_len);
> > + if (val) {
> > + found_val = val;
> > + found_len = temp_len;
> > + }
> > + }
> > + g_strfreev(tokens);
> > +
> > + if (!found_val) {
> > + error_setg(errp, "%s: Property '%s' not found",
> > + __func__, property);
> > + return NULL;
> > + }
> > +
> > + if (lenp) {
> > + *lenp = found_len;
> > + }
> > +
> > + return found_val;
> > +}
> > +
> > +uint32_t qemu_fdt_getprop_cell_inherited(void *fdt, const char *node_path,
> > + const char *property, int cell_id, Error
> > **errp)
> > +{
> > + int len;
> > + const uint32_t *p;
> > +
> > + p = qemu_fdt_getprop_inherited(fdt, node_path, property, &len, errp);
> > + if (!p) {
> > + return 0;
> > + }
> > + if (len < (cell_id + 1) * 4) {
> > + error_setg(errp,
> > + "%s: %s/%s is too short, need %d bytes for cell ind %d",
> > + __func__, node_path, property, (cell_id + 1) * 4,
> > cell_id);
> > + return 0;
> > + }
> > + return be32_to_cpu(p[cell_id]);
> > +}
> > +
> > +int qemu_devtree_getparent(void *fdt, char *node_path, const char *current)
> > +{
> > + const char *sep;
> > + int len;
> > +
> > + if (!current || !node_path) {
> > + return -FDT_ERR_NOTFOUND;
> > + }
> > +
> > + if (current[0] == '/' && current[1] == 0) {
>
> strcmp() against "/" is maybe marginally more costly, but clearer in
> meaning.
>
> > + return -FDT_ERR_NOTFOUND;
> > + }
> > +
> > + sep = strrchr(current, '/');
> > + if (!sep) {
> > + return -FDT_ERR_BADPATH;
> > + }
> > +
> > + if (sep == current) {
> > + len = 1;
> > + } else {
> > + len = sep - current;
> > + }
> > +
> > + memcpy(node_path, current, len);
> > + node_path[len] = '\0';
>
> This assumes node_path points to a long enough buffer. Returning a
> g_strndup()ed copy of the parent path might be a nicer interface?
>
> > +
> > + return 0;
> > +}
> > +
> > uint32_t qemu_fdt_get_phandle(void *fdt, const char *path)
> > {
> > uint32_t r;
> > @@ -631,6 +728,130 @@ out:
> > return ret;
> > }
> >
> > +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 = propname;
> > + ret[i].value = val;
> > + i++;
> > + }
> > + return ret;
> > +}
> > +
> > +int qemu_devtree_get_children(void *fdt, const char *node_path,
> > + int max_paths, char **returned_paths)
> > {
> > + int count = 0;
> > + int subnode;
> > + const char *name;
> > + int offset = fdt_path_offset(fdt, node_path);
> > +
> > + if (offset < 0) {
> > + return offset;
> > + }
> > +
> > + bool is_root = (strcmp(node_path, "/") == 0);
> > +
> > + fdt_for_each_subnode(subnode, fdt, offset) {
> > + if (count >= max_paths) {
> > + break;
> > + }
> > + name = fdt_get_name(fdt, subnode, NULL);
> > + if (returned_paths) {
> > + returned_paths[count] = g_strdup_printf("%s/%s",
> > + is_root ? "" : node_path,
> > name);
> > + }
> > +
> > + ++count;
> > + }
> > +
> > + return count;
> > +}
> > +
> > +int qemu_devtree_get_num_children(void *fdt, const char *node_path)
> > +{
> > + int count = 0;
> > + int subnode;
> > + int offset = fdt_path_offset(fdt, node_path);
> > +
> > + if (offset < 0) {
> > + return offset;
> > + }
> > +
> > + fdt_for_each_subnode(subnode, fdt, offset) {
> > + ++count;
> > + }
> > +
> > + return count;
> > +}
> > +
> > +int qemu_devtree_get_node_by_phandle(void *fdt, char *node_path, int
> > phandle)
> > +{
> > + int offset = 0, cur_depth = 0;
> > + int path_lens[64] = { 0 };
> > +
> > + for (offset = 0; offset >= 0; offset = fdt_next_node(fdt, offset,
> > + &cur_depth)) {
> > + if (cur_depth > 64) {
> > + break;
> > + }
> > + const char *name = fdt_get_name(fdt, offset, NULL);
> > +
> > + int parent_len = (cur_depth > 0) ? path_lens[cur_depth - 1] : 0;
> > + int len = snprintf(node_path + parent_len,
> > + DT_PATH_LENGTH - parent_len,
> > + "%s%s",
> > + (parent_len > 1) ? "/" : "",
> > + (cur_depth == 0) ? "/" : name);
> > +
> > + path_lens[cur_depth] = parent_len + len;
> > +
> > + if (fdt_get_phandle(fdt, offset) == phandle) {
> > + return 0;
> > + }
> > + }
> > +
> > + return -FDT_ERR_NOTFOUND;
> > +}
> > +
> > +int devtree_get_num_nodes(void *fdt)
> > +{
> > + int num_nodes = 0;
> > + int depth = 0, offset = 0;
> > +
> > + for (;;) {
> > + offset = fdt_next_node(fdt, offset, &depth);
> > + num_nodes++;
> > + if (offset <= 0 || depth <= 0) {
> > + break;
> > + }
> > + }
> > + return num_nodes;
> > +}
> > +
> > 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
--
BR,
Ruslan Ruslichenko