lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260112142009.1006236-59-herve.codina@bootlin.com>
Date: Mon, 12 Jan 2026 15:19:48 +0100
From: Herve Codina <herve.codina@...tlin.com>
To: David Gibson <david@...son.dropbear.id.au>,
	Rob Herring <robh@...nel.org>,
	Krzysztof Kozlowski <krzk@...nel.org>,
	Conor Dooley <conor+dt@...nel.org>
Cc: Ayush Singh <ayush@...gleboard.org>,
	Geert Uytterhoeven <geert@...ux-m68k.org>,
	devicetree-compiler@...r.kernel.org,
	devicetree@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	devicetree-spec@...r.kernel.org,
	Hui Pu <hui.pu@...ealthcare.com>,
	Ian Ray <ian.ray@...ealthcare.com>,
	Luca Ceresoli <luca.ceresoli@...tlin.com>,
	Thomas Petazzoni <thomas.petazzoni@...tlin.com>,
	Herve Codina <herve.codina@...tlin.com>
Subject: [RFC PATCH 58/77] dtc: Add support for missing root node in addon device-tree

Addon can have orphan nodes fully described (dts) and encoded (dtb)
without the need for a root device tree.

Contrary to plugin (overlay), addon doesn't need any specific root tree
structure to described the orphans. Indeed, /fragment@..._overlay__
doesn't exist for addons. Orphans are encoded in dtb using the
FDT_BEGIN_NODE_REF tag without any impact on the root tree.

The following snippet is fully legit for addon:
   --- 8< ---
   /addon/;

   &foo {
      subnode {
        prop = <1>;
      };
   }
   --- 8< ---

This snippet doesn't contains a root device tree, and the related dtb
doesn't need any root device tree to encode the snippet.

The root device is no more mandatory for addon.

Add support for addon without a root device-tree.

Signed-off-by: Herve Codina <herve.codina@...tlin.com>
---
 checks.c     | 28 ++++++++++++++++++++++++++--
 dtc-parser.y | 18 ++++++++++++++----
 dtc.c        | 36 +++++++++++++++++++++++-------------
 flattree.c   | 42 +++++++++++++++++++++++++++++-------------
 livetree.c   | 22 +++++++++++++++++-----
 treesource.c |  3 ++-
 6 files changed, 111 insertions(+), 38 deletions(-)

diff --git a/checks.c b/checks.c
index 83a0f6e..f98f153 100644
--- a/checks.c
+++ b/checks.c
@@ -154,7 +154,6 @@ static bool is_multiple_of(int multiple, int divisor)
 
 static bool run_check(struct check *c, struct dt_info *dti)
 {
-	struct node *dt = dti->dt;
 	struct node *orphan;
 	bool error = false;
 	int i;
@@ -179,7 +178,8 @@ static bool run_check(struct check *c, struct dt_info *dti)
 	if (c->status != UNCHECKED)
 		goto out;
 
-	check_nodes_props(c, dti, dt);
+	if (dti->dt)
+		check_nodes_props(c, dti, dti->dt);
 
 	for_each_orphan(dti->orphanlist, orphan)
 		check_nodes_props(c, dti, orphan);
@@ -2079,6 +2079,30 @@ void process_checks(bool force, struct dt_info *dti)
 	unsigned int i;
 	int error = 0;
 
+	if (!dti->dt) {
+		/* No root node is only allowed for addons */
+		if (dti->dtsflags & DTSF_ADDON) {
+			if (!dti->orphanlist) {
+				/*
+				 * but addons without a root node and without
+				 * orphan nodes is really incorrect
+				 */
+				fprintf(stderr,
+					"ERROR: Input tree has no root or orphan nodes, aborting\n");
+				exit(2);
+			}
+		} else {
+			fprintf(stderr, "ERROR: Input tree has no root node, aborting\n");
+			exit(2);
+		}
+	}
+	if (!(dti->dtsflags & DTSF_ADDON)) {
+		if (dti->orphanlist) {
+			fprintf(stderr, "ERROR: Input tree has orphan nodes, aborting\n");
+			exit(2);
+		}
+	}
+
 	for (i = 0; i < ARRAY_SIZE(check_table); i++) {
 		struct check *c = check_table[i];
 
diff --git a/dtc-parser.y b/dtc-parser.y
index 10ca6d4..cf5447a 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -133,12 +133,18 @@ static struct node *parser_get_node_by_ref(struct node *dt, struct node *orphanl
 sourcefile:
 	  headers memreserves devicetree
 		{
+			if (!($1 & DTSF_ADDON) && !$3)
+				ERROR(&@3, "Only addon support missing root node");
+
 			parser_output = build_dt_info($1, $2, $3,
-			                              guess_boot_cpuid($3),
+			                              $3 ? guess_boot_cpuid($3) : 0,
 						      NULL, parser_orphanlist);
 		}
 	|  headers memreserves importlist devicetree
 		{
+			if (!($1 & DTSF_ADDON) && !$4)
+				ERROR(&@4, "Only addon support missing root node");
+
 			/*
 			 * importlist is created with chain_symbol() and so it
 			 * is created in reverse order. Reverse it now to have
@@ -241,7 +247,10 @@ devicetree:
 		}
 	| devicetree '/' nodedef
 		{
-			$$ = merge_nodes($1, $3);
+			if ($1)
+				$$ = merge_nodes($1, $3);
+			else
+				$$ = name_node($3, "");
 		}
 	| dt_ref nodedef
 		{
@@ -256,8 +265,9 @@ devicetree:
 							  ""),
 						$2, $1);
 			} else {
-				ERROR(&@2, "Orphan node %s without a root node not yet supported", $1);
-				YYERROR;
+				addon_add_orphan_node(&parser_orphanlist,
+						      orphan_node($2, $1));
+				$$ = NULL;
 			}
 		}
 	| devicetree DT_LABEL dt_ref nodedef
diff --git a/dtc.c b/dtc.c
index 5cf8f31..63725bf 100644
--- a/dtc.c
+++ b/dtc.c
@@ -351,27 +351,37 @@ int main(int argc, char *argv[])
 	update_exports_ref(dti);
 	mark_local_exports(dti);
 
-	/*
-	 * With FDT_REF_PHANDLE added in dtbs, we need to identified
-	 * if some unresolved phandle references are allowed in the dtb
-	 * we have parsed (needed for process_check() to run properly).
-	 *
-	 * Identify plugin device-trees (overlays) based on specific node
-	 * presence.
-	 */
-	if (get_subnode(dti->dt, "__fixups__") ||
-	    get_subnode(dti->dt, "__local_fixups__"))
-		dti->dtsflags |= DTSF_PLUGIN;
+	if (dti->dt) {
+		/*
+		 * With FDT_REF_PHANDLE added in dtbs, we need to identified
+		 * if some unresolved phandle references are allowed in the dtb
+		 * we have parsed (needed for process_check() to run properly).
+		 *
+		 * Identify plugin device-trees (overlays) based on specific node
+		 * presence.
+		 */
+		if (get_subnode(dti->dt, "__fixups__") ||
+		    get_subnode(dti->dt, "__local_fixups__"))
+			dti->dtsflags |= DTSF_PLUGIN;
+	}
 
 	process_checks(force, dti);
 
-	if (auto_label_aliases)
+	if (auto_label_aliases) {
+		if (!dti->dt)
+			die("auto-alias not supported without a root node\n");
 		generate_label_tree(dti, "aliases", false);
+	}
 
-	if (generate_symbols)
+	if (generate_symbols) {
+		if (!dti->dt)
+			die("generation of symbols not supported without a root node\n");
 		generate_label_tree(dti, "__symbols__", true);
+	}
 
 	if (generate_fixups) {
+		if (!dti->dt)
+			die("generation of fixups not supported without a root node\n");
 		generate_fixups_tree(dti, "__fixups__");
 		generate_local_fixups_tree(dti, "__local_fixups__");
 	}
diff --git a/flattree.c b/flattree.c
index 27f7608..11447b1 100644
--- a/flattree.c
+++ b/flattree.c
@@ -561,7 +561,9 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version)
 
 	dt_flags |= dti->dtsflags & DTSF_ADDON ? FDT_FLAG_ADDON : 0;
 
-	flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
+	if (dti->dt)
+		flatten_tree(dti->dt, &bin_emitter, &dtbuf, &strbuf, vi);
+
 	flatten_imports(dti->importsymlist, &bin_emitter, &dtbuf, vi, dt_flags);
 	flatten_orphans(dti->orphanlist, &bin_emitter, &dtbuf, &strbuf, vi,
 			dt_flags);
@@ -752,7 +754,9 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version)
 	fprintf(f, "\t.long\t0, 0\n\t.long\t0, 0\n");
 
 	emit_label(f, symprefix, "struct_start");
-	flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi);
+
+	if (dti->dt)
+		flatten_tree(dti->dt, &asm_emitter, f, &strbuf, vi);
 
 	fprintf(f, "\t/* FDT_END */\n");
 	asm_emit_cell(f, FDT_END);
@@ -1092,7 +1096,7 @@ struct dt_info *dt_from_blob(const char *fname)
 	struct inbuf memresvbuf;
 	int sizeleft;
 	struct reserve_info *reservelist;
-	struct node *tree;
+	struct node *tree = NULL;
 	struct symbol *importsymlist = NULL;
 	struct symbol *importsym;
 	struct node *orphanlist = NULL;
@@ -1203,16 +1207,24 @@ struct dt_info *dt_from_blob(const char *fname)
 
 	reservelist = flat_read_mem_reserve(&memresvbuf);
 
-	val = flat_read_word(&dtbuf);
-
-	if (val != FDT_BEGIN_NODE)
-		die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n", val);
+	if (!(dtsflags & DTSF_ADDON)) {
+		val = flat_read_word(&dtbuf);
+		if (val != FDT_BEGIN_NODE)
+			die("Device tree blob doesn't begin with FDT_BEGIN_NODE (begins with 0x%08x)\n",
+			    val);
 
-	tree = unflatten_tree(&dtbuf, &strbuf, "", flags, false);
+		tree = unflatten_tree(&dtbuf, &strbuf, "", flags, false);
 
-	val = flat_read_word(&dtbuf);
+		val = flat_read_word(&dtbuf);
+		if (val != FDT_END)
+			die("Device tree blob doesn't end with FDT_END\n");
+	} else {
+		val = flat_read_word(&dtbuf);
+		if (val == FDT_BEGIN_NODE) {
+			tree = unflatten_tree(&dtbuf, &strbuf, "", flags, false);
+			val = flat_read_word(&dtbuf);
+		}
 
-	if (dtsflags & DTSF_ADDON) {
 		if (flags & FTF_EXPORT_IMPORT_SYM) {
 			while (val == FDT_IMPORT_SYM) {
 				importsym = flat_read_importsym(&dtbuf);
@@ -1229,10 +1241,14 @@ struct dt_info *dt_from_blob(const char *fname)
 				val = flat_read_word(&dtbuf);
 			}
 		}
-	}
 
-	if (val != FDT_END)
-		die("Device tree blob doesn't end with FDT_END\n");
+		if (!tree && !orphanlist)
+			die("Device tree blob has 0x%08x tag instead of FDT_BEGIN_NODE or FDT_BEGIN_NODE_REF\n",
+			    val);
+
+		if (val != FDT_END)
+			die("Device tree blob doesn't end with FDT_END\n");
+	}
 
 	free(blob);
 
diff --git a/livetree.c b/livetree.c
index eaface5..00274c5 100644
--- a/livetree.c
+++ b/livetree.c
@@ -1203,7 +1203,9 @@ void sort_tree(struct dt_info *dti)
 {
 	sort_reserve_entries(dti);
 	sort_importsyms(dti);
-	sort_node(dti->dt);
+
+	if (dti->dt)
+		sort_node(dti->dt);
 }
 
 /* utility helper to avoid code duplication */
@@ -1451,6 +1453,8 @@ static int generate_local_fixups_tree_internal(struct dt_info *dti,
 
 void generate_label_tree(struct dt_info *dti, const char *name, bool allocph)
 {
+	assert(dti->dt);
+
 	if (!any_label_tree(dti, dti->dt))
 		return;
 	generate_label_tree_internal(dti, build_root_node(dti->dt, name),
@@ -1459,6 +1463,8 @@ void generate_label_tree(struct dt_info *dti, const char *name, bool allocph)
 
 void generate_fixups_tree(struct dt_info *dti, const char *name)
 {
+	assert(dti->dt);
+
 	if (!any_fixup_tree(dti, dti->dt))
 		return;
 	if (generate_fixups_tree_internal(dti, build_root_node(dti->dt, name), dti->dt))
@@ -1469,6 +1475,8 @@ void generate_fixups_tree(struct dt_info *dti, const char *name)
 
 void generate_local_fixups_tree(struct dt_info *dti, const char *name)
 {
+	assert(dti->dt);
+
 	if (!any_local_fixup_tree(dti, dti->dt))
 		return;
 	if (generate_local_fixups_tree_internal(dti, build_root_node(dti->dt, name), dti->dt))
@@ -1514,7 +1522,8 @@ void update_phandles_ref(struct dt_info *dti)
 {
 	struct node *orphan;
 
-	update_phandles_ref_internal(dti, dti->dt);
+	if (dti->dt)
+		update_phandles_ref_internal(dti, dti->dt);
 
 	for_each_orphan(dti->orphanlist, orphan)
 		update_phandles_ref_internal(dti, orphan);
@@ -1545,7 +1554,8 @@ void mark_local_phandles(struct dt_info *dti)
 {
 	struct node *orphan;
 
-	mark_local_phandles_internal(dti, dti->dt);
+	if (dti->dt)
+		mark_local_phandles_internal(dti, dti->dt);
 
 	for_each_orphan(dti->orphanlist, orphan)
 		mark_local_phandles_internal(dti, orphan);
@@ -1580,7 +1590,8 @@ void update_exports_ref(struct dt_info *dti)
 {
 	struct node *orphan;
 
-	update_exports_ref_internal(dti, dti->dt);
+	if (dti->dt)
+		update_exports_ref_internal(dti, dti->dt);
 
 	for_each_orphan(dti->orphanlist, orphan)
 		update_exports_ref_internal(dti, orphan);
@@ -1607,7 +1618,8 @@ void mark_local_exports(struct dt_info *dti)
 {
 	struct node *orphan;
 
-	mark_local_exports_internal(dti, dti->dt);
+	if (dti->dt)
+		mark_local_exports_internal(dti, dti->dt);
 
 	for_each_orphan(dti->orphanlist, orphan)
 		mark_local_exports_internal(dti, orphan);
diff --git a/treesource.c b/treesource.c
index bc5d847..71dbd5f 100644
--- a/treesource.c
+++ b/treesource.c
@@ -410,7 +410,8 @@ void dt_to_source(FILE *f, struct dt_info *dti)
 		fprintf(f, "\n");
 	}
 
-	write_tree_source_node(f, dti->dt, 0);
+	if (dti->dt)
+		write_tree_source_node(f, dti->dt, 0);
 
 	for_each_orphan(dti->orphanlist, orphan) {
 		fprintf(f, "\n");
-- 
2.52.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ