devicetree-compiler.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Herve Codina <herve.codina@bootlin.com>
To: David Gibson <david@gibson.dropbear.id.au>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	David Lechner <dlechner@baylibre.com>,
	Ayush Singh <ayush@beagleboard.org>,
	Geert Uytterhoeven <geert@linux-m68k.org>,
	devicetree-compiler@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org, devicetree-spec@vger.kernel.org,
	Hui Pu <hui.pu@gehealthcare.com>,
	Ian Ray <ian.ray@gehealthcare.com>,
	Luca Ceresoli <luca.ceresoli@bootlin.com>,
	Thomas Petazzoni <thomas.petazzoni@bootlin.com>,
	Herve Codina <herve.codina@bootlin.com>,
	Frank Li <Frank.Li@nxp.com>
Subject: [PATCH v3 02/15] libfdt: Don't assume the root node is available at offset 0
Date: Wed, 26 Aug 2026 10:31:33 +0200	[thread overview]
Message-ID: <20260826083146.304291-3-herve.codina@bootlin.com> (raw)
In-Reply-To: <20260826083146.304291-1-herve.codina@bootlin.com>

In several places, libfdt assumes that the root offset (i.e. the first
FDT_BEGIN_NODE tag) is present at the offset 0 of the structure block.

This assumption is not correct. A FDT_NOP can be present at the offset 0
and this is a legit case. Indeed, the device-tree specification [0]
defines the FDT_NOP tag as follow:

   The FDT_NOP token will be ignored by any program parsing the device
   tree. This token has no extra data; so it is followed immediately by
   the next token, which can be any valid token. A property or node
   definition in the tree can be overwritten with FDT_NOP tokens to
   remove it from the tree without needing to move other sections of
   the tree’s representation in the devicetree blob.

Nothing refers to any location for this tag and it has to be simply
ignored. Having this tag at offset 0 doesn't make an exception, the tag
has to be ignored.

Introduce fdt_root_offset() in order to get the offset of the root
node (first FDT_BEGIN_NODE tag) available in a fdt blob taking care of
FDT_NOP tags.

Use this function to get the root node offset instead of looking for
this node at offset 0.

[0] https://github.com/devicetree-org/devicetree-specification/blob/main/source/chapter5-flattened-format.rst?plain=1#L317

Signed-off-by: Herve Codina <herve.codina@bootlin.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
 libfdt/fdt.c       | 39 +++++++++++++++++++++++++++++--
 libfdt/fdt_ro.c    | 57 ++++++++++++++++++++++++++++++++++++++--------
 libfdt/fdt_rw.c    | 12 ++++++++++
 libfdt/libfdt.h    | 15 +++++++++++-
 libfdt/version.lds |  1 +
 5 files changed, 111 insertions(+), 13 deletions(-)

diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 56d4dcb2..eb803e8a 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -252,13 +252,48 @@ int fdt_check_prop_offset_(const void *fdt, int offset)
 	return offset;
 }
 
-int fdt_next_node(const void *fdt, int offset, int *depth)
+int fdt_root_offset(const void *fdt)
 {
 	int nextoffset = 0;
+	int offset;
+	uint32_t tag;
+
+	do {
+		offset = nextoffset;
+		tag = fdt_next_tag(fdt, offset, &nextoffset);
+		switch (tag) {
+		case FDT_END_NODE:
+		case FDT_PROP:
+			return -FDT_ERR_BADSTRUCTURE;
+
+		case FDT_BEGIN_NODE:
+			return offset;
+
+		default:
+			break;
+		}
+	} while (tag != FDT_END);
+
+	return (nextoffset < 0) ? nextoffset : -FDT_ERR_NOTFOUND;
+}
+
+int fdt_next_node(const void *fdt, int offset, int *depth)
+{
+	int nextoffset = offset;
 	uint32_t tag;
 
+	/*
+	 * Get the root node if asked for next node from the root node
+	 * (offset == 0) or if the given offset is not valid (negative).
+	 */
+	if (offset <= 0) {
+		nextoffset = fdt_root_offset(fdt);
+		if (nextoffset < 0)
+			return nextoffset;
+	}
+
 	if (offset >= 0)
-		if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
+		if ((nextoffset = fdt_check_node_offset_(fdt, nextoffset)) < 0)
 			return nextoffset;
 
 	do {
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
index 11f2e2ee..856c62f1 100644
--- a/libfdt/fdt_ro.c
+++ b/libfdt/fdt_ro.c
@@ -231,6 +231,12 @@ int fdt_subnode_offset_namelen(const void *fdt, int offset,
 
 	FDT_RO_PROBE(fdt);
 
+	if (!offset) {
+		offset = fdt_root_offset(fdt);
+		if (offset < 0)
+			return offset;
+	}
+
 	for (depth = 0;
 	     (offset >= 0) && (depth >= 0);
 	     offset = fdt_next_node(fdt, offset, &depth))
@@ -253,13 +259,17 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
 {
 	const char *end = path + namelen;
 	const char *p = path;
-	int offset = 0;
+	int offset;
 
 	FDT_RO_PROBE(fdt);
 
 	if (!can_assume(VALID_INPUT) && namelen <= 0)
 		return -FDT_ERR_BADPATH;
 
+	offset = fdt_root_offset(fdt);
+	if (offset < 0)
+		return offset;
+
 	/* see if we have an alias */
 	if (*path != '/') {
 		const char *q = memchr(path, '/', end - p);
@@ -304,14 +314,24 @@ int fdt_path_offset(const void *fdt, const char *path)
 
 const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
 {
-	const struct fdt_node_header *nh = fdt_offset_ptr_(fdt, nodeoffset);
+	const struct fdt_node_header *nh;
 	const char *nameptr;
 	int err;
 
+	if (!nodeoffset) {
+		nodeoffset = fdt_root_offset(fdt);
+		if (nodeoffset < 0) {
+			err = nodeoffset;
+			goto fail;
+		}
+	}
+
+
 	if (!can_assume(VALID_DTB) && (((err = fdt_ro_probe_(fdt)) < 0)
 	    || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
 			goto fail;
 
+	nh = fdt_offset_ptr_(fdt, nodeoffset);
 	nameptr = nh->name;
 
 	if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
@@ -344,6 +364,12 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset)
 {
 	int offset;
 
+	if (!nodeoffset) {
+		nodeoffset = fdt_root_offset(fdt);
+		if (nodeoffset < 0)
+			return nodeoffset;
+	}
+
 	if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
 		return offset;
 
@@ -581,7 +607,7 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
 	if (buflen < 2)
 		return -FDT_ERR_NOSPACE;
 
-	for (offset = 0, depth = 0;
+	for (offset = fdt_root_offset(fdt), depth = 0;
 	     (offset >= 0) && (offset <= nodeoffset);
 	     offset = fdt_next_node(fdt, offset, &depth)) {
 		while (pdepth > depth) {
@@ -619,7 +645,7 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
 	else if (offset == -FDT_ERR_BADOFFSET)
 		return -FDT_ERR_BADSTRUCTURE;
 
-	return offset; /* error from fdt_next_node() */
+	return offset; /* error from fdt_next_node() or fdt_root_offset() */
 }
 
 int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
@@ -627,13 +653,21 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
 {
 	int offset, depth;
 	int supernodeoffset = -FDT_ERR_INTERNAL;
+	int root_offset;
 
 	FDT_RO_PROBE(fdt);
 
 	if (supernodedepth < 0)
 		return -FDT_ERR_NOTFOUND;
 
-	for (offset = 0, depth = 0;
+	root_offset = fdt_root_offset(fdt);
+	if (root_offset < 0)
+		return root_offset;
+
+	if (!nodeoffset)
+		nodeoffset = root_offset;
+
+	for (offset = root_offset, depth = 0;
 	     (offset >= 0) && (offset <= nodeoffset);
 	     offset = fdt_next_node(fdt, offset, &depth)) {
 		if (depth == supernodedepth)
@@ -663,12 +697,15 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
 int fdt_node_depth(const void *fdt, int nodeoffset)
 {
 	int nodedepth;
-	int err;
+	int offset;
+
+	offset = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
+	if (offset < 0)
+		return offset;
+
+	if (!can_assume(LIBFDT_FLAWLESS) && offset != fdt_root_offset(fdt))
+		return -FDT_ERR_INTERNAL;
 
-	err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
-	if (err)
-		return (can_assume(LIBFDT_FLAWLESS) || err < 0) ? err :
-			-FDT_ERR_INTERNAL;
 	return nodedepth;
 }
 
diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
index 850aafe4..ceef49b8 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -226,6 +226,12 @@ static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
 	int err;
 	int allocated;
 
+	if (!nodeoffset) {
+		nodeoffset = fdt_root_offset(fdt);
+		if (nodeoffset < 0)
+			return nodeoffset;
+	}
+
 	if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
 		return nextoffset;
 
@@ -377,6 +383,12 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,
 
 	FDT_RW_PROBE(fdt);
 
+	if (!parentoffset) {
+		parentoffset = fdt_root_offset(fdt);
+		if (parentoffset < 0)
+			return parentoffset;
+	}
+
 	offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
 	if (offset >= 0)
 		return -FDT_ERR_EXISTS;
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index c69a18ed..7a1915a5 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -503,6 +503,19 @@ int fdt_num_mem_rsv(const void *fdt);
  */
 int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
 
+/**
+ * fdt_root_offset - Get the offset of the root node
+ * @fdt: pointer to the device tree blob
+ *
+ * The root node can be located after the offset 0. Indeed FDT_NOP tags can be
+ * present at offset 0. fdt_root_offset() takes care of those possible FDT_NOP
+ * tags.
+ *
+ * returns: offset of the root node or negative libfdt error value otherwise
+ */
+int fdt_root_offset(const void *fdt);
+
+
 /**
  * fdt_subnode_offset_namelen - find a subnode based on substring
  * @fdt: pointer to the device tree blob
@@ -1025,7 +1038,7 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
  * at a specific depth from the root (where the root itself has depth
  * 0, its immediate subnodes depth 1 and so forth).  So
  *	fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
- * will always return 0, the offset of the root node.  If the node at
+ * will always return the offset of the root node.  If the node at
  * nodeoffset has depth D, then:
  *	fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
  * will return nodeoffset itself.
diff --git a/libfdt/version.lds b/libfdt/version.lds
index cbfef546..d0b71669 100644
--- a/libfdt/version.lds
+++ b/libfdt/version.lds
@@ -7,6 +7,7 @@ LIBFDT_1.2 {
 		fdt_string;
 		fdt_num_mem_rsv;
 		fdt_get_mem_rsv;
+		fdt_root_offset;
 		fdt_subnode_offset_namelen;
 		fdt_subnode_offset;
 		fdt_path_offset_namelen;
-- 
2.55.0


  parent reply	other threads:[~2026-08-26  8:32 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-26  8:31 [PATCH v3 00/15] Add support for structured tags and v18 dtb version Herve Codina
2026-08-26  8:31 ` [PATCH v3 01/15] fdtget: Use libfdt iterators instead of open coded loops Herve Codina
2026-08-26  8:31 ` Herve Codina [this message]
2026-08-26  8:31 ` [PATCH v3 03/15] tests: Don't assume the root node is available at offset 0 Herve Codina
2026-08-26  8:31 ` [PATCH v3 04/15] tests/nopulate: Add a FDT_NOP before the root node Herve Codina
2026-08-26  8:31 ` [PATCH v3 05/15] tests: treegen: Introduce emit_fdt_header_vers() Herve Codina
2026-08-26  8:31 ` [PATCH v3 06/15] Introduce structured tag value definition Herve Codina
2026-08-26  8:31 ` [PATCH v3 07/15] fdtdump: Handle unknown tags Herve Codina
2026-08-26  8:31 ` [PATCH v3 08/15] flattree: " Herve Codina
2026-08-26  8:31 ` [PATCH v3 09/15] libfdt: Handle unknown tags in fdt_next_tag() Herve Codina
2026-08-26  8:31 ` [PATCH v3 10/15] libfdt: Introduce fdt_ptr_offset_() Herve Codina
2026-08-26  8:31 ` [PATCH v3 11/15] libfdt: Introduce fdt_getprop_by_offset_w() Herve Codina
2026-08-26  8:31 ` [PATCH v3 12/15] libfdt: Introduce fdt_getprop_offset_namelen() Herve Codina
2026-08-26  8:31 ` [PATCH v3 13/15] tests: Add wip_func utility Herve Codina
2026-08-26  8:31 ` [PATCH v3 14/15] libfdt: Handle unknown tags on dtb modifications Herve Codina
2026-08-26  8:31 ` [PATCH v3 15/15] Introduce v18 dtb version Herve Codina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260826083146.304291-3-herve.codina@bootlin.com \
    --to=herve.codina@bootlin.com \
    --cc=Frank.Li@nxp.com \
    --cc=ayush@beagleboard.org \
    --cc=conor+dt@kernel.org \
    --cc=david@gibson.dropbear.id.au \
    --cc=devicetree-compiler@vger.kernel.org \
    --cc=devicetree-spec@vger.kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=geert@linux-m68k.org \
    --cc=hui.pu@gehealthcare.com \
    --cc=ian.ray@gehealthcare.com \
    --cc=krzk@kernel.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luca.ceresoli@bootlin.com \
    --cc=robh@kernel.org \
    --cc=thomas.petazzoni@bootlin.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).