[PATCH 02/27] system/device_tree: add few parsing and traversal helpers

Ruslan Ruslichenko posted 27 patches 1 week, 4 days ago
Maintainers: Peter Maydell <peter.maydell@linaro.org>, Alistair Francis <alistair@alistair23.me>, "Edgar E. Iglesias" <edgar.iglesias@gmail.com>, Eduardo Habkost <eduardo@habkost.net>, Marcel Apfelbaum <marcel.apfelbaum@gmail.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Yanan Wang <wangyanan55@huawei.com>, Zhao Liu <zhao1.liu@intel.com>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, David Gibson <david@gibson.dropbear.id.au>, Peter Xu <peterx@redhat.com>
[PATCH 02/27] system/device_tree: add few parsing and traversal helpers
Posted by Ruslan Ruslichenko 1 week, 4 days ago
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
Re: [PATCH 02/27] system/device_tree: add few parsing and traversal helpers
Posted by David Gibson 1 week, 4 days ago
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
Re: [PATCH 02/27] system/device_tree: add few parsing and traversal helpers
Posted by Ruslan Ruslichenko 1 week, 3 days ago
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