mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Herve Codina <herve.codina@bootlin.com>
To: David Gibson <david@gibson.dropbear.id.au>
Cc: Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	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>,
	Frank Li <Frank.Li@nxp.com>
Subject: Re: [PATCH v3 02/15] libfdt: Don't assume the root node is available at offset 0
Date: Mon, 7 Sep 2026 18:46:41 +0200	[thread overview]
Message-ID: <20260907184641.537cfa49@bootlin.com> (raw)
In-Reply-To: <apfKx_u3v3GBfzOz@gractus.seuss>

Hi David,

On Wed, 2 Sep 2026 17:06:03 +1000
David Gibson <david@gibson.dropbear.id.au> wrote:

...
> > 
> > Ok, I will update fdt_check_node_offset_() to have it updating its offset
> > parameter to the real offset of the root node when its value is 0.
> > 
> > Based on this update, will see where it goes. I mean, impacts on callers, if
> > it simplifies things or not, if the offset update needs also to be propagate
> > to caller's parameter or any other similar point that we can see during the
> > implementation.
> > 
> > Having something implemented and available in a patch will be the best to
> > compare changes and impacts related to fdt_check_node_offset_() update.
> > 
> > Here we have a version of handling offset 0 vs real root node without any
> > offset update done in fdt_check_node_offset_(). In the next iteration we will
> > have the version with update done in fdt_check_node_offset_().
> > 
> > I think the golden rules to follow on this point is "keep it as simple as
> > possible".  
> 
> Agreed.  Feel free to repost just the NOP before root patches on their
> own.  At this time, frequent small series is easier for me to tackle
> than occasional large series.
> 

I've moved forward on the fdt_check_node_offset_() update.

The new fdt_check_node_offset_() looks like this:
--- 8< ---
int fdt_check_node_offset_(const void *fdt, int *offset)
{
	int nextoffset;

	if (!can_assume(VALID_INPUT)
	    && ((*offset < 0) || (*offset % FDT_TAGSIZE)))
		return -FDT_ERR_BADOFFSET;

	if (*offset == 0) {
		*offset = fdt_root_offset(fdt);
		if (*offset < 0)
			return *offset;
	}

	if (fdt_next_tag(fdt, *offset, &nextoffset) != FDT_BEGIN_NODE)
		return -FDT_ERR_BADOFFSET;

	return nextoffset;
}
--- 8< ---

If the given offset is 0, fdt_check_node_offset_() considers we want to check
the root node and so update offset to the real root node offset.

Ok, I still need some fdt_root_offset() calls from some other parts but that's
not my main issue.

My main issue comes with orphan nodes in addons. fdt_check_node_offset_() is
called with offset pointing to an orphan node and this offset can be 0.

The offset 0 seen by fdt_check_node_offset_() can be the "fake" offset of a root
node and in that case fdt_check_node_offset_() should update offset to the real
root node offset but it can also be the offset of the orphan node we want to check
and in that case the offset should not be updated.

fdt_check_node_offset_() cannot determine whether or not the offset should be
updated.

With orphan nodes in the loop, fdt_check_node_offset_() becomes:
--- 8< ---
int fdt_check_node_offset_(const void *fdt, int *offset)
{
        int nextoffset;
        uint32_t tag;

        if (!can_assume(VALID_INPUT)
            && ((*offset < 0) || (*offset % FDT_TAGSIZE)))
                return -FDT_ERR_BADOFFSET;

        if (*offset == 0) {
#pragma message "We have a problem!"
                /*
                 * An orphan node can be present at offset 0.
                 * In that case, looking for the root node may or may not be
                 * correct.
                 * Indeed is offset = 0 requested because we want the root
                 * node and sadly an orphan node is available at offset 0 or
                 * is it requested because we want to really check the orphan
                 * node available at offset 0. How to determine the correct
                 * case?
                 */
                tag = fdt_next_tag(fdt, *offset, &nextoffset);
                if (tag == FDT_BEGIN_NODE || tag == FDT_BEGIN_NODE_REF)
                        return nextoffset;

                *offset = fdt_root_offset(fdt);
                if (*offset < 0)
                        return *offset;
        }

        tag = fdt_next_tag(fdt, *offset, &nextoffset);
        if (tag != FDT_BEGIN_NODE && tag != FDT_BEGIN_NODE_REF)
                return -FDT_ERR_BADOFFSET;

        return nextoffset;
}
--- 8< ---

Of course, extra complexity could be added such as an additional parameter but
I don't think it would make sense.

The simplest way to handle the case is to not update the offset in
fdt_check_node_offset_() and let callers to pass the offset pointing to the
real node offset expected to be checked. This is my initial proposal.

Of course, I am still open to other ideas.

For information, without addons and orphan nodes, just to compare against my
original proposal, the full patch ("libfdt: Don't assume the root node is
available at offset 0") with the offset update done in fdt_check_node_offset_()
becomes:
--- 8< ---
diff --git a/libfdt/fdt.c b/libfdt/fdt.c
index 56d4dcb2..d4d4b64b 100644
--- a/libfdt/fdt.c
+++ b/libfdt/fdt.c
@@ -228,16 +228,24 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
 	return tag;
 }
 
-int fdt_check_node_offset_(const void *fdt, int offset)
+int fdt_check_node_offset_(const void *fdt, int *offset)
 {
+	int nextoffset;
+
 	if (!can_assume(VALID_INPUT)
-	    && ((offset < 0) || (offset % FDT_TAGSIZE)))
+	    && ((*offset < 0) || (*offset % FDT_TAGSIZE)))
 		return -FDT_ERR_BADOFFSET;
 
-	if (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)
+	if (*offset == 0) {
+		*offset = fdt_root_offset(fdt);
+		if (*offset < 0)
+			return *offset;
+	}
+
+	if (fdt_next_tag(fdt, *offset, &nextoffset) != FDT_BEGIN_NODE)
 		return -FDT_ERR_BADOFFSET;
 
-	return offset;
+	return nextoffset;
 }
 
 int fdt_check_prop_offset_(const void *fdt, int offset)
@@ -252,13 +260,38 @@ int fdt_check_prop_offset_(const void *fdt, int offset)
 	return offset;
 }
 
+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 = 0;
 	uint32_t tag;
 
 	if (offset >= 0)
-		if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
+		if ((nextoffset = fdt_check_node_offset_(fdt, &offset)) < 0)
 			return nextoffset;
 
 	do {
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
index 11f2e2ee..ec8af835 100644
--- a/libfdt/fdt_ro.c
+++ b/libfdt/fdt_ro.c
@@ -281,7 +281,7 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
 		while (*p == '/') {
 			p++;
 			if (p == end)
-				return offset;
+				goto terminate;
 		}
 		q = memchr(p, '/', end - p);
 		if (! q)
@@ -294,7 +294,13 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
 		p = q;
 	}
 
-	return offset;
+terminate:
+        /*
+         * Avoid returning offset 0 or the real root offset.
+         * Be sure to return one and only one offset for the root node, its
+         * real offset.
+         */
+	return offset ? offset : fdt_root_offset(fdt);
 }
 
 int fdt_path_offset(const void *fdt, const char *path)
@@ -304,14 +310,17 @@ 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 (!can_assume(VALID_DTB) && (((err = fdt_ro_probe_(fdt)) < 0)
-	    || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)))
-			goto fail;
+	if (!can_assume(VALID_DTB) && (err = fdt_ro_probe_(fdt)) < 0)
+		goto fail;
+
+	if ((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,7 +353,7 @@ int fdt_first_property_offset(const void *fdt, int nodeoffset)
 {
 	int offset;
 
-	if ((offset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
+	if ((offset = fdt_check_node_offset_(fdt, &nodeoffset)) < 0)
 		return offset;
 
 	return nextprop_(fdt, offset);
@@ -574,6 +583,7 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
 {
 	int pdepth = 0, p = 0;
 	int offset, depth, namelen;
+	int root_offset;
 	const char *name;
 
 	FDT_RO_PROBE(fdt);
@@ -581,7 +591,14 @@ 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;
+	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)) {
 		while (pdepth > depth) {
@@ -627,13 +644,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 +688,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..a1a07f01 100644
--- a/libfdt/fdt_rw.c
+++ b/libfdt/fdt_rw.c
@@ -226,7 +226,7 @@ static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,
 	int err;
 	int allocated;
 
-	if ((nextoffset = fdt_check_node_offset_(fdt, nodeoffset)) < 0)
+	if ((nextoffset = fdt_check_node_offset_(fdt, &nodeoffset)) < 0)
 		return nextoffset;
 
 	namestroff = fdt_find_add_string_(fdt, name, namelen, &allocated);
@@ -377,6 +377,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/libfdt_internal.h b/libfdt/libfdt_internal.h
index 0e103caf..d5072279 100644
--- a/libfdt/libfdt_internal.h
+++ b/libfdt/libfdt_internal.h
@@ -20,7 +20,7 @@ int32_t fdt_ro_probe_(const void *fdt);
 		}							\
 	}
 
-int fdt_check_node_offset_(const void *fdt, int offset);
+int fdt_check_node_offset_(const void *fdt, int *offset);
 int fdt_check_prop_offset_(const void *fdt, int offset);
 
 const char *fdt_find_string_len_(const char *strtab, int tabsize, const char *s,
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;
--- 8< ---


I hope all of those details will help to move forward.

Best regards,
Hervé

  reply	other threads:[~2026-09-07 16:46 UTC|newest]

Thread overview: 44+ 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-27  3:55   ` David Gibson
2026-08-26  8:31 ` [PATCH v3 02/15] libfdt: Don't assume the root node is available at offset 0 Herve Codina
2026-08-30  3:21   ` David Gibson
2026-08-31 12:01     ` Herve Codina
2026-09-01  7:42       ` David Gibson
2026-09-01 12:18         ` Herve Codina
2026-09-02  7:06           ` David Gibson
2026-09-07 16:46             ` Herve Codina [this message]
2026-09-08  6:41               ` David Gibson
2026-09-08  8:08                 ` Herve Codina
2026-09-09  6:18                   ` David Gibson
2026-09-09  6:58                     ` Herve Codina
2026-09-09  7:02                       ` David Gibson
2026-08-26  8:31 ` [PATCH v3 03/15] tests: " Herve Codina
2026-09-01  8:03   ` David Gibson
2026-09-01 13:36     ` Herve Codina
2026-09-02  8:56       ` David Gibson
2026-08-26  8:31 ` [PATCH v3 04/15] tests/nopulate: Add a FDT_NOP before the root node Herve Codina
2026-09-01  8:05   ` David Gibson
2026-08-26  8:31 ` [PATCH v3 05/15] tests: treegen: Introduce emit_fdt_header_vers() Herve Codina
2026-09-09  6:38   ` David Gibson
2026-08-26  8:31 ` [PATCH v3 06/15] Introduce structured tag value definition Herve Codina
2026-09-10  4:51   ` David Gibson
2026-09-10  7:41     ` Herve Codina
2026-09-10  9:32       ` David Gibson
2026-09-11  7:16         ` Herve Codina
2026-09-12  2:34           ` David Gibson
2026-09-10  5:33   ` David Gibson
2026-09-10  7:58     ` Herve Codina
2026-09-10  9:41       ` David Gibson
2026-09-11  7:53         ` Herve Codina
2026-09-12  2:35           ` David Gibson
2026-08-26  8:31 ` [PATCH v3 07/15] fdtdump: Handle unknown tags Herve Codina
2026-09-10  5:25   ` David Gibson
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=20260907184641.537cfa49@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

all inboxes | Powered by JetHome®