Add support for structured tags and v18 dtb version
cover Add support for structured tags and v18 dtb version
Recently, I sent a RFC series related to support for metadata and addon device-trees [1].
During the discussion the concept of structured tags and "unknown" tags emerged as well as the need to have them handled as a prerequisite of support for metadata and addon.
The conclusion was the need for a new dtb version (v18) with support for:
- Structured tags and based on them, "unknown" tags.
Those structured tags allow to have an standardized definition of tags with the capability of skipping a tag and its related data when a "unknown" tag is incountered by a given version of libfdt, dtc and tools. Those "unknown" tags are tags defined in future versions. Even if they exact meaning is unknown for an 'old' version, they structure is understood and the 'old' version can skip them without any errors if allowed.
- Flags in the dtb header (dt_flags).
The goal of this field is to have a placeholder to specify the type of dtb we are dealing with. For instance, addons dtb will set a flag in this placeholder
- A last compatible version for writing purpose.
The goal of the new dtb header field (last_comp_version_w) is to disable globally any modification. It works similarly to last_comp_version but for modification. It can be used to avoid any modification that could be done by an 'old' version and could lead to inconsistencies between the modification itself and some "unknown" tags.
This RFC series implements those features and leads to the v18 dtb version.
First patches (patches from 1 to 8) are patches fixing issues or preparation patches. IMHO, those patches could be taken even if other patches in the series lead to discussions.
Patch 9 introduces the structured tags. The patch gives definitions needed to handle those tags.
Patches 10, 11 and 12 handles "unknown" tags in fdtdump, dtc and libfdt. This is the reading part implementation related to "unknown" tags in tools and lib.
Patch 13 is a preparation commit for patch 14 and patch 14 itself is handling modifications (writing part) when unknown tags are involved.
The last patch (patch 15) bumps the dtb version including changes that cannot be moved out of the version bump without having a v18 without all expected features.
Also, several tests are added as soon as the related feature is supported. Those tests are part of the last commit adding the feature.
As already said, this current RFC series is a prerequisite to the support for metadata and addons. The RFC series related to metadata and addons [1] will be rebased on top of this prerequisite. Please, keep that in mind for the review of this current RFC prerequisite series.
[1] https://lore.kernel.org/all/[email protected]/
Best regards,
Hervé
Herve Codina (15):
dtc: Use a consistent type for basenamelen
fdtdump: Remove dtb version check
fdtdump: Return an error code on wrong tag value
libfdt: fdt_rw: Introduce fdt_downgrade_version()
libfdt: Introduce fdt_first_node()
libfdt: Don't assume that a FDT_BEGIN_NODE tag is available at offset
0
libfdt: fdt_check_full: Handle FDT_NOP when FDT_END is expected
tests: asm: Introduce treehdr_vers macro
Introduce structured tag value definition
fdtdump: Handle unknown tags
flattree: Handle unknown tags
libfdt: Handle unknown tags in fdt_get_next()
libfdt: Introduce fdt_ptr_offset_
libfdt: Handle unknown tags on dtb modifications Introduce v18 dtb version
checks.c | 2 +- dtc.h | 4 +- fdtdump.c | 57 ++++++- flattree.c | 102 ++++++++++-- libfdt/fdt.c | 110 ++++++++++++- libfdt/fdt.h | 28 ++++ libfdt/fdt_check.c | 2 +- libfdt/fdt_ro.c | 16 +- libfdt/fdt_rw.c | 151 +++++++++++++++++- libfdt/fdt_sw.c | 3 + libfdt/libfdt.h | 7 +- libfdt/libfdt_internal.h | 9 ++ pylibfdt/libfdt.i | 18 +++ tests/dumptrees.c | 5 +- tests/pylibfdt_tests.py | 10 +- tests/run_tests.sh | 115 ++++++++++++- tests/testdata.h | 3 + tests/testutils.c | 2 +- tests/trees.S | 143 ++++++++++++++++- tests/unknown_tags_can_skip.dtb.dts.expect | 19 +++ tests/unknown_tags_can_skip.dtb.expect | 26 +++ ...own_tags_can_skip.fdtput.test.dtb.0.expect | 32 ++++ ...own_tags_can_skip.fdtput.test.dtb.1.expect | 36 +++++ ...own_tags_can_skip.fdtput.test.dtb.2.expect | 34 ++++ ...own_tags_can_skip.fdtput.test.dtb.3.expect | 36 +++++ ...own_tags_can_skip.fdtput.test.dtb.4.expect | 35 ++++ ...own_tags_can_skip.fdtput.test.dtb.5.expect | 33 ++++ ...own_tags_can_skip.fdtput.test.dtb.6.expect | 28 ++++ 28 files changed, 1017 insertions(+), 49 deletions(-) create mode 100644 tests/unknown_tags_can_skip.dtb.dts.expect create mode 100644 tests/unknown_tags_can_skip.dtb.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect
Signature · 1 line
2.52.0
RFC PATCH 1/15 dtc: Use a consistent type for basenamelen
The basenamelen member in the node structure is set in all cases to a positive value, the length of the basename string. Also it is used as parameters on function expecting a size_t type.
Further more an implicit cast of strspn() returned value from size_t to int is needed in checks.c to avoid a signed/unsigned compilation warning when this value is checked.
This member has no reason to be a signed integer and its obvious type is size_t.
Be consistent and fix its type.
--- checks.c | 2 +- dtc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
checks.c +1 -1
@@ -324,7 +324,7 @@ ERROR(node_name_chars, check_node_name_chars, NODECHARS);static void check_node_name_chars_strict (struct check *c,struct dt_info *dti,struct node *node) { -int n =strspn (node->name, c->data); +size_t n =strspn (node->name, c->data);if (n < node->basenamelen) FAIL(c, dti, node, "Character '%c' not recommended in node name",
dtc.h +1 -1
@@ -227,7 +227,7 @@ struct node {struct node *next_sibling;char *fullpath; -int basenamelen; +size_t basenamelen;cell_t phandle;int addr_cells, size_cells;
Signature · 1 line
2.52.0
David Gibson
AppliedRe: [RFC PATCH 01/15] dtc: Use a consistent type for basenamelen
Quoted 8 lines · Herve Codina
The basenamelen member in the node structure is set in all cases to a positive value, the length of the basename string. Also it is used as parameters on function expecting a
size_ttype.Further more an implicit cast of
strspn()returned value fromsize_tto int is needed inchecks.cto avoid a signed/unsigned compilation warning when this value is checked.
Quoted 6 lines
This member has no reason to be a signed integer and its obvious type is
size_t.Be consistent and fix its type.
Signed-off-by Herve Codina <[email protected]>
Applied, thanks.
Reviewing the rest of the series may still take a while.
Quoted 35 lines
--- checks.c | 2 +- dtc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)checks.c +1
-1@@ -324,7 +324,7 @@ ERROR(node_name_chars, check_node_name_chars, NODECHARS);static void check_node_name_chars_strict (struct check *c,struct dt_info *dti,struct node *node) { -int n =strspn (node->name, c->data); +size_t n =strspn (node->name, c->data);if (n < node->basenamelen) FAIL(c, dti, node, "Character '%c' not recommended in node name",dtc.h +1
-1@@ -227,7 +227,7 @@ struct node {struct node *next_sibling;char *fullpath; -int basenamelen; +size_t basenamelen;cell_t phandle;int addr_cells, size_cells;Signature · 1 line
2.52.0
Signature · 4 lines
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
RFC PATCH 2/15 fdtdump: Remove dtb version check
In reply to Herve Codinafdtdump checks the dtb version and simply failed if the dtb version is newer than the last version supported by fdtdump.
This check is not needed and too restrictive. Indeed, fdtdump does read-only operations on the dtb provided and should rely only the last_comp_version header field to know whether or not it can read the dtb.
The current check also avoid the use of fdtdump in tests checking for the libfdt behavior when an new (future) dtb version is used.
Relax fdtdump checks removing the check of the dtb version header field.
--- fdtdump.c | 1 - 1 file changed, 1 deletion(-)
fdtdump.c +0 -1
@@ -169,7 +169,6 @@ static bool valid_header(char *p, size_t len) { if (len < sizeof(struct fdt_header) || fdt_magic(p) != FDT_MAGIC || - fdt_version(p) > MAX_VERSION || fdt_last_comp_version(p) > MAX_VERSION || fdt_totalsize(p) >= len || fdt_off_dt_struct(p) >= len ||
Signature · 1 line
2.52.0
David Gibson
AppliedRe: [RFC PATCH 02/15] fdtdump: Remove dtb version check
Quoted 8 lines · Herve Codina
fdtdump checks the dtb version and simply failed if the dtb version is newer than the last version supported by fdtdump.
This check is not needed and too restrictive. Indeed, fdtdump does read-only operations on the dtb provided and should rely only the
last_comp_versionheader field to know whether or not it can read the dtb.
Quoted 6 lines
The current check also avoid the use of fdtdump in tests checking for the libfdt behavior when an new (future) dtb version is used.
Relax fdtdump checks removing the check of the dtb version header field.
Signed-off-by Herve Codina <[email protected]>
Applied, thanks.
Signature · 4 lines
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
RFC PATCH 3/15 fdtdump: Return an error code on wrong tag value
In reply to Herve Codinafdtdump prints a message on stderr when it encounters a wrong tag and stop its processing without returning an error code.
Having a wrong tag is really a failure. Indeed, the processing cannot continue.
Be more strict. Stop the processing, print a message and return an error code. In other words, call die().
--- fdtdump.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
fdtdump.c +1 -2
@@ -146,8 +146,7 @@ static void dump_blob(void *blob, bool debug)continue ; } -fprintf (stderr,"%*s ** Unknown tag 0x%08" PRIx32"\n" , depth * shift,"" , tag); -break ; +die ("** Unknown tag 0x%08" PRIx32"\n" , tag); } }
Signature · 1 line
2.52.0
Re: [RFC PATCH 03/15] fdtdump: Return an error code on wrong tag value
Quoted 10 lines · Herve Codina
fdtdump prints a message on stderr when it encounters a wrong tag and stop its processing without returning an error code.
Having a wrong tag is really a failure. Indeed, the processing cannot continue.
Be more strict. Stop the processing, print a message and return an error code. In other words, call
die().Signed-off-by Herve Codina <[email protected]>
The intention of fdtdump is that it's a fairly crude debugging tool - it will generally attempt to produce at least partial output even on a bad dtb file. If you want a polished tool for use on good dtbs, use :dtc -I dtb -O dts".
That's why this didn't die() initially - the idea is if there's some bogus stuff in the dtb, it might print a bunch of these warnings, but eventually resynchronize on another valid tag.
So, I'm disinclined to apply this without a stronger case.
Quoted 22 lines
--- fdtdump.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)fdtdump.c +1
-2@@ -146,8 +146,7 @@ static void dump_blob(void *blob, bool debug)continue ; } -fprintf (stderr,"%*s ** Unknown tag 0x%08" PRIx32"\n" , depth * shift,"" , tag); -break ; +die ("** Unknown tag 0x%08" PRIx32"\n" , tag); } }Signature · 1 line
2.52.0
Signature · 4 lines
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: [RFC PATCH 03/15] fdtdump: Return an error code on wrong tag value
Hi David,
Quoted 10 lines · On Mon, 23 Feb 2026 16:38:09 +1100 David Gibson <[email protected]>…
Quoted 9 lines · Herve Codina
fdtdump prints a message on stderr when it encounters a wrong tag and stop its processing without returning an error code.
Having a wrong tag is really a failure. Indeed, the processing cannot continue.
Be more strict. Stop the processing, print a message and return an error code. In other words, call
die().
Quoted 6 lines
Quoted 1 line
Signed-off-by Herve Codina <[email protected]>The intention of fdtdump is that it's a fairly crude debugging tool - it will generally attempt to produce at least partial output even on a bad dtb file. If you want a polished tool for use on good dtbs, use :dtc -I dtb -O dts".
fdtdump is also interesting for tests purpose.
I use it to check dtb outputs during tests. Those outputs are either generated by dtc or by libfdt.
Having an error code returned by fdtdump when it cannot parse the given dtb allows to have this test (patch 10):
--- 8< ---
+
+ base_run_test wrap_fdtdump unknown_tags_can_skip.dtb unknown_tags_can_skip.dtb.out + # Remove unneeded comments + sed -i '/^\/\/ /d' unknown_tags_can_skip.dtb.out + base_run_test check_diff unknown_tags_can_skip.dtb.out "$SRCDIR/unknown_tags_can_skip.dtb.expect"
+
+ run_wrap_error_test $FDTDUMP unknown_tags_no_skip.dtb
--- 8< ---
Quoted 4 lines
That's why this didn't
die()initially - the idea is if there's some bogus stuff in the dtb, it might print a bunch of these warnings, but eventually resynchronize on another valid tag.
Current implementation cannot resynchronize. The parsing loop is exited
https://git.kernel.org/pub/scm/utils/dtc/dtc.git/tree/fdtdump.c#n150
and the program just returns 0.
Instead of just print and exit the loop, the idea was to have a program error code set. Calling die() allows to print, exit the loop and exit the program with an error code.
With that in mind, I can do what you prefer. Either:
- keep the die() call
or
- discard this patch and update the test in patch 10 (i.e. remove the 'run_wrap_error_test $FDTDUMP unknown_tags_no_skip.dtb' line)
Let me know.
Best regards,
Hervé
Re: [RFC PATCH 03/15] fdtdump: Return an error code on wrong tag value
Quoted 17 lines · Herve Codina
Hi David,
Quoted 12 lines · On Mon, 23 Feb 2026 16:38:09 +1100 David Gibson <[email protected]>…
Quoted 10 lines · Herve Codina
fdtdump prints a message on stderr when it encounters a wrong tag and stop its processing without returning an error code.
Having a wrong tag is really a failure. Indeed, the processing cannot continue.
Be more strict. Stop the processing, print a message and return an error code. In other words, call
die().Signed-off-by Herve Codina <[email protected]>
Quoted 8 lines
Quoted 4 lines
The intention of fdtdump is that it's a fairly crude debugging tool - it will generally attempt to produce at least partial output even on a bad dtb file. If you want a polished tool for use on good dtbs, use :dtc -I dtb -O dts".
fdtdump is also interesting for tests purpose.
I use it to check dtb outputs during tests. Those outputs are either generated by dtc or by libfdt.
dtc -I dts should still be usable for that purpose, no? It seems like the better tool for this job.
Quoted 13 lines
Having an error code returned by fdtdump when it cannot parse the given dtb allows to have this test (patch 10):
--- 8< ---(quoted diff) +7
-1+ + base_run_test wrap_fdtdump unknown_tags_can_skip.dtb unknown_tags_can_skip.dtb.out + # Remove unneeded comments + sed -i '/^\/\/ /d' unknown_tags_can_skip.dtb.out + base_run_test check_diff unknown_tags_can_skip.dtb.out "$SRCDIR/unknown_tags_can_skip.dtb.expect" + + run_wrap_error_test $FDTDUMP unknown_tags_no_skip.dtb --- 8< ---Quoted 2 lines
Quoted 7 lines
Quoted 3 lines
That's why this didn't
die()initially - the idea is if there's some bogus stuff in the dtb, it might print a bunch of these warnings, but eventually resynchronize on another valid tag.Current implementation cannot resynchronize. The parsing loop is exited
https://git.kernel.org/pub/scm/utils/dtc/dtc.git/tree/fdtdump.c#n150
and the program just returns 0.
Oh, good point. In that case this change is unequivocally an improvement. Applied.
Quoted 9 lines
Instead of just print and exit the loop, the idea was to have a program error code set. Calling
die()allows to print, exit the loop and exit the program with an error code.With that in mind, I can do what you prefer. Either:
- keep thedie()call
or
- discard this patch and update the test in patch 10 (i.e. remove the 'run_wrap_error_test$FDTDUMP unknown_tags_no_skip.dtb' line)
So, I've applied the patch, so I guess option (1). However, I think the test should be altered to use dtc instead of fdtdump. I don't really encourage fdtdump to be used for anything except ad-hoc experimentation.
Signature · 4 lines
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
RFC PATCH 4/15 libfdt: fdt_rw: Introduce fdt_downgrade_version()
In reply to Herve CodinaCurrent code perform a version downgrade at one place only, the end of fdt_rw_probe_().
In order to offer a finer grain and choose to downgrade or not depending on the exact writes done, introduce fdt_downgrade_version() to perform the downgrade operation.
The modification doesn't introduce any functional changes.
--- libfdt/fdt_rw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
libfdt/fdt_rw.c +7 -2
@@ -22,6 +22,12 @@ static int fdt_blocks_misordered_(const void *fdt, (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); } +static void fdt_downgrade_version (void *fdt) +{ +if (!can_assume (LATEST ) &&fdt_version (fdt) >FDT_LAST_SUPPORTED_VERSION ) +fdt_set_version (fdt,FDT_LAST_SUPPORTED_VERSION ); +} +static int fdt_rw_probe_ (void *fdt) {if (can_assume (VALID_DTB ))
@@ -33,9 +39,8 @@ static int fdt_rw_probe_(void *fdt)if (fdt_blocks_misordered_ (fdt,sizeof (struct fdt_reserve_entry ),fdt_size_dt_struct (fdt)))return -FDT_ERR_BADLAYOUT ; -if (!can_assume (LATEST ) &&fdt_version (fdt) >17 ) -fdt_set_version (fdt,17 ); +fdt_downgrade_version (fdt);return 0 ; }
Signature · 1 line
2.52.0
David Gibson
AppliedRe: [RFC PATCH 04/15] libfdt: fdt_rw: Introduce fdt_downgrade_version()
Quoted 10 lines · Herve Codina
Current code perform a version downgrade at one place only, the end of
fdt_rw_probe_().In order to offer a finer grain and choose to downgrade or not depending on the exact writes done, introduce
fdt_downgrade_version()to perform the downgrade operation.The modification doesn't introduce any functional changes.
Signed-off-by Herve Codina <[email protected]>
Applied, thanks.
Quoted 36 lines
--- libfdt/fdt_rw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)libfdt/fdt_rw.c +7
-2@@ -22,6 +22,12 @@ static int fdt_blocks_misordered_(const void *fdt, (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); } +static void fdt_downgrade_version (void *fdt) +{ +if (!can_assume (LATEST ) &&fdt_version (fdt) >FDT_LAST_SUPPORTED_VERSION ) +fdt_set_version (fdt,FDT_LAST_SUPPORTED_VERSION ); +} +static int fdt_rw_probe_ (void *fdt) {if (can_assume (VALID_DTB ))@@ -33,9 +39,8 @@ static int fdt_rw_probe_(void *fdt)if (fdt_blocks_misordered_ (fdt,sizeof (struct fdt_reserve_entry ),fdt_size_dt_struct (fdt)))return -FDT_ERR_BADLAYOUT ; -if (!can_assume (LATEST ) &&fdt_version (fdt) >17 ) -fdt_set_version (fdt,17 ); +fdt_downgrade_version (fdt);return 0 ; }Signature · 1 line
2.52.0
Signature · 4 lines
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
RFC PATCH 5/15 libfdt: Introduce fdt_first_node()
In reply to Herve CodinaIn several places, libfdt assumes that a FDT_BEGIN_NODE tag is present at the offset 0 of the structure block.
This assumption is not correct. Indeed, a FDT_NOP can be present at the offset 0 and this is a legit case.
Introduce fdt_first_node() in order to get the offset of the first node (first FDT_BEGIN_NODE tag) available in a fdt blob.
--- libfdt/fdt.c | 25 +++++++++++++++++++++++++ libfdt/libfdt_internal.h | 1 + 2 files changed, 26 insertions(+)
libfdt/fdt.c +25 -0
@@ -252,6 +252,31 @@ int fdt_check_prop_offset_(const void *fdt, int offset)return offset; } +int fdt_first_node (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 ;
libfdt/libfdt_internal.h +1 -0
@@ -32,6 +32,7 @@ static inline const char *fdt_find_string_(const char *strtab, int tabsize, }int fdt_node_end_offset_ (void *fdt,int nodeoffset); +int fdt_first_node (const void *fdt);static inline const void *fdt_offset_ptr_ (const void *fdt,int offset) {
Signature · 1 line
2.52.0
Re: [RFC PATCH 05/15] libfdt: Introduce fdt_first_node()
Hi Hervé, David,
I'm trying to review the patches that have no feedback so far.
Being new to the dtc codebase I'm mostly pointing out things that are not clear from a newcomer point of view. I hope this helps anyway.
Quoted 5 lines · Herve Codina
In several places, libfdt assumes that a
FDT_BEGIN_NODEtag is present at the offset 0 of the structure block.This assumption is not correct. Indeed, a
FDT_NOPcan be present at the offset 0 and this is a legit case.
I wonder whether this can be proven by showing an example, or the specs, or whatever use case that makes sense.
Quoted 14 lines
Introduce
fdt_first_node()in order to get the offset of the first node (firstFDT_BEGIN_NODEtag) available in a fdt blob.Signed-off-by Herve Codina <[email protected]>--- libfdt/fdt.c | 25 +++++++++++++++++++++++++ libfdt/libfdt_internal.h | 1 + 2 files changed, 26 insertions(+)libfdt/fdt.c +0
-0
libfdt/fdt.c int fdt_check_prop_offset_(const void *fdt, int offset) · line 254
Quoted 3 lines
libfdt/fdt.c +0
-0@@ -252,6 +252,31 @@ int fdt_check_prop_offset_(const void *fdt, int offset)return offset; }
Even though this seems to be quite uncommon in this repository, I think documenting new functions would be helpful, especially preconditions, postconditions and parameter values when not obvious.
What about:
Find the initial node with content (FDT_BEGIN_NODE) in a fdt, skipping FDT_NOP [and <other tags> is applicable].
*return: pointer to the first node into the fdt or e negative error value
Quoted 15 lines
libfdt/fdt.c +15
-0+int fdt_first_node (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:
libfdt/fdt.c int fdt_check_prop_offset_(const void *fdt, int offset) · line 279
Quoted 10 lines
libfdt/fdt.c +10
-0+return offset; + +default : +break ; + } + }while (tag !=FDT_END ); + +return (nextoffset <0 ) ? nextoffset : -FDT_ERR_NOTFOUND ; +} +
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [RFC PATCH 05/15] libfdt: Introduce fdt_first_node()
Hi Luca, David,
Quoted 8 lines · On Wed, 01 Apr 2026 17:11:15 +0200 "Luca Ceresoli" <[email protected]>…
Hi Hervé, David,
I'm trying to review the patches that have no feedback so far.
Being new to the dtc codebase I'm mostly pointing out things that are not clear from a newcomer point of view. I hope this helps anyway.
On Tue Feb 10, 2026 at 6:33 PM CET, Herve Codina wrote:
Quoted 8 lines
Quoted 5 lines
In several places, libfdt assumes that a
FDT_BEGIN_NODEtag is present at the offset 0 of the structure block.This assumption is not correct. Indeed, a
FDT_NOPcan be present at the offset 0 and this is a legit case.I wonder whether this can be proven by showing an example, or the specs, or whatever use case that makes sense.
I can point the device-tree specification and add the following:
--- 8< ---
Indeed, the FDT_NOP definition according to the device-tree specification [0] is the following:
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.
[0] https://github.com/devicetree-org/devicetree-specification/blob/main/source/chapter5-flattened-format.rst?plain=1#L317
--- 8< ---
Quoted 23 lines
Quoted 15 lines
Introduce
fdt_first_node()in order to get the offset of the first node (firstFDT_BEGIN_NODEtag) available in a fdt blob.Signed-off-by Herve Codina <[email protected]>--- libfdt/fdt.c | 25 +++++++++++++++++++++++++ libfdt/libfdt_internal.h | 1 + 2 files changed, 26 insertions(+)libfdt/fdt.c +0
-0libfdt/fdt.c int fdt_check_prop_offset_(const void *fdt, int offset) · line 254
Quoted 3 lines
libfdt/fdt.c +0
-0@@ -252,6 +252,31 @@ int fdt_check_prop_offset_(const void *fdt, int offset)return offset; }Even though this seems to be quite uncommon in this repository, I think documenting new functions would be helpful, especially preconditions, postconditions and parameter values when not obvious.
Quoted 6 lines
What about:
Find the initial node with content (
FDT_BEGIN_NODE) in a fdt, skippingFDT_NOP[and <other tags> is applicable].*return: pointer to the first node into the fdt or e negative error value
As you already said, documentation is quite uncommon here.
I would prefer to keep the code consistent and avoid adding documentation for some functions and not others.
On the other side, adding some documentation could be beneficial.
David, what do you prefer?
Should I document new functions I introduce?
Best regards,
Hervé
RFC PATCH 6/15 libfdt: Don't assume that a FDT_BEGIN_NODE tag is available at offset 0
In reply to Herve CodinaIn several places, libfdt assumes that a FDT_BEGIN_NODE tag is present at the offset 0 of the structure block.
This assumption is not correct. Indeed, a FDT_NOP can be present at the offset 0 and this is a legit case.
fdt_first_node() has been introduce recently to get the offset of the first node (first FDT_BEGIN_NODE) in a fdt blob.
Use this function to get the first node offset instead of looking for this node at offset 0.
--- libfdt/fdt.c | 10 ++++++++-- libfdt/fdt_ro.c | 16 +++++++++++++--- libfdt/fdt_rw.c | 6 ++++++ 3 files changed, 27 insertions(+), 5 deletions(-)
libfdt/fdt.c +8 -2
@@ -279,11 +279,17 @@ int fdt_first_node(const void *fdt)int fdt_next_node (const void *fdt,int offset,int *depth) { -int nextoffset =0 ; +int nextoffset = offset;uint32_t tag; +if (offset <=0 ) { + nextoffset =fdt_first_node (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 {
libfdt/fdt_ro.c +13 -3
@@ -229,6 +229,12 @@ int fdt_subnode_offset_namelen(const void *fdt, int offset,FDT_RO_PROBE (fdt); +if (!offset) { + offset =fdt_first_node (fdt); +if (offset <0 ) +return offset; + } +for (depth =0 ; (offset >=0 ) && (depth >=0 ); offset =fdt_next_node (fdt, offset, &depth))
@@ -251,13 +257,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_first_node (fdt); +if (offset <0 ) +return offset; +/* see if we have an alias */ if (*path !='/' ) {const char *q =memchr (path,'/' , end - p);
@@ -579,7 +589,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_first_node (fdt), depth =0 ; (offset >=0 ) && (offset <= nodeoffset); offset =fdt_next_node (fdt, offset, &depth)) {while (pdepth > depth) {
@@ -617,7 +627,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_first_node() */ } int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
libfdt/fdt_rw.c +6 -0
@@ -354,6 +354,12 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,FDT_RW_PROBE (fdt); +if (!parentoffset) { + parentoffset =fdt_first_node (fdt); +if (parentoffset <0 ) +return parentoffset; + } + offset =fdt_subnode_offset_namelen (fdt, parentoffset, name, namelen);if (offset >=0 )return -FDT_ERR_EXISTS ;
Signature · 1 line
2.52.0
Re: [RFC PATCH 06/15] libfdt: Don't assume that a FDT_BEGIN_NODE tag is available at offset 0
Quoted 7 lines · Herve Codina
In several places, libfdt assumes that a
FDT_BEGIN_NODEtag is present at the offset 0 of the structure block.This assumption is not correct. Indeed, a
FDT_NOPcan be present at the offset 0 and this is a legit case.
fdt_first_node()has been introduce recently to get the offset of the
^ introduced
Quoted 17 lines
first node (first
FDT_BEGIN_NODE) in a fdt blob.Use this function to get the first node offset instead of looking for this node at offset 0.
Signed-off-by Herve Codina <[email protected]>--- libfdt/fdt.c | 10 ++++++++-- libfdt/fdt_ro.c | 16 +++++++++++++--- libfdt/fdt_rw.c | 6 ++++++ 3 files changed, 27 insertions(+), 5 deletions(-)libfdt/fdt.c +0
-0
libfdt/fdt.c int fdt_first_node(const void *fdt) · line 285
Quoted 8 lines
libfdt/fdt.c +2
-1@@ -279,11 +279,17 @@ int fdt_first_node(const void *fdt)int fdt_next_node (const void *fdt,int offset,int *depth) { -int nextoffset =0 ; +int nextoffset = offset;uint32_t tag; +if (offset <=0 ) {
What is the difference between 0 and a engative value?
This is where the parameter value is not obvious to the newcomer and I'd love seeing it concisely documented.
Otherwise this patch looks good to me.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [RFC PATCH 06/15] libfdt: Don't assume that a FDT_BEGIN_NODE tag is available at offset 0
Hi Luca, David,
Quoted 10 lines · On Wed, 01 Apr 2026 17:11:42 +0200 "Luca Ceresoli" <[email protected]>…
Quoted 7 lines · Herve Codina
In several places, libfdt assumes that a
FDT_BEGIN_NODEtag is present at the offset 0 of the structure block.This assumption is not correct. Indeed, a
FDT_NOPcan be present at the offset 0 and this is a legit case.
fdt_first_node()has been introduce recently to get the offset of the^ introduced
Will be fixed in the next iteration.
...
Quoted 9 lines
libfdt/fdt.c int fdt_first_node(const void *fdt) · line 285
Quoted 7 lines
int
fdt_next_node(const void *fdt, int offset, int *depth){libfdt/fdt.c +2
-1-int nextoffset =0 ; +int nextoffset = offset;uint32_t tag; +if (offset <=0 ) {What is the difference between 0 and a engative value?
I will add a comment in the code.
0 means that we want the next node from the first node.
The negative value is an invalid value. In that case we start from the first node. This was the behavior of the code without my modification and I kept the same behavior in that case.
Quoted 3 lines
This is where the parameter value is not obvious to the newcomer and I'd love seeing it concisely documented.
David, is functions documentation expected ?
I can add to this documentation if you want it.
Best regards,
Hervé
RFC PATCH 7/15 libfdt: fdt_check_full: Handle FDT_NOP when FDT_END is expected
In reply to Herve Codinafdt_check_full() makes the assumption that a FDT_END tag is present immediately after the FDT_END_NODE tag related to the root node.
This assumption is not correct. Indeed, FDT_NOP tags can be present between this FDT_END_NODE tag and the FDT_END tag.
Handle those possible FDT_NOP tags.
--- libfdt/fdt_check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
libfdt/fdt_check.c +1 -1
@@ -43,7 +43,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)return nextoffset;/* If we see two root nodes, something is wrong */ -if (expect_end && tag !=FDT_END ) +if (expect_end && tag !=FDT_END && tag !=FDT_NOP )return -FDT_ERR_BADSTRUCTURE ;switch (tag) {
Signature · 1 line
2.52.0
David Gibson
AppliedRe: [RFC PATCH 07/15] libfdt: fdt_check_full: Handle FDT_NOP when FDT_END is expected
Quoted 9 lines · Herve Codina
fdt_check_full()makes the assumption that aFDT_ENDtag is present immediately after theFDT_END_NODEtag related to the root node.This assumption is not correct. Indeed,
FDT_NOPtags can be present between thisFDT_END_NODEtag and theFDT_ENDtag.Handle those possible
FDT_NOPtags.Signed-off-by Herve Codina <[email protected]>
Merged, thanks.
Quoted 21 lines
--- libfdt/fdt_check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)libfdt/fdt_check.c +1
-1@@ -43,7 +43,7 @@ int fdt_check_full(const void *fdt, size_t bufsize)return nextoffset;/* If we see two root nodes, something is wrong */ -if (expect_end && tag !=FDT_END ) +if (expect_end && tag !=FDT_END && tag !=FDT_NOP )return -FDT_ERR_BADSTRUCTURE ;switch (tag) {Signature · 1 line
2.52.0
Signature · 4 lines
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
RFC PATCH 8/15 tests: asm: Introduce treehdr_vers macro
In reply to Herve Codinatree.S is used to generate custom dtbs. It uses the treehdr macro to build the header part.
The current definition of this macro doesn't allow to set custom settings related to version fields.
In order to easily generate some dtb with custom version values without duplicating the full header computation, introduce the treehdr_vers macro.
The modification doesn't introduce any functional changes.
--- tests/trees.S | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
tests/trees.S +7 -3
@@ -8,7 +8,7 @@ .byte (\val) & 0xff .endm - .macro treehdr tree + .macro treehdr_vers tree vers last_comp_vers .balign 8 .globl \tree \tree :
@@ -17,13 +17,17 @@ fdtlong (\tree\()_struct - \tree) fdtlong (\tree\()_strings - \tree) fdtlong (\tree\()_rsvmap - \tree) - fdtlong 0x11 - fdtlong 0x10 + fdtlong \vers + fdtlong \last_comp_vers fdtlong 0 fdtlong (\tree\()_strings_end - \tree\()_strings) fdtlong (\tree\()_struct_end - \tree\()_struct) .endm + .macro treehdr tree + treehdr_vers \tree 0x11 0x10 + .endm + .macro rsvmape addrh, addrl, lenh, lenl fdtlong \addrh fdtlong \addrl
Signature · 1 line
2.52.0
RFC PATCH 9/15 Introduce structured tag value definition
In reply to Herve CodinaThe goal of structured tag values is to ease the introduction of new tags in future releases with the capability for an already existing release to ignore those structured tags. In order to do that data length related to the unknown tag needs to be identify. Also a flag is present to tell an old release if this tag can be simply skipped or must lead to an error.
Structured tag value is defined on 32bit and is defined as follow:
Bits | 31 | 30 | 29 28 | 27 0|
------+----+----------+-------------------+--------+
Fields| 1 | CAN_SKIP | DATA_LNG_ENCODING | TAG_ID |
------+----+----------+-------------------+--------+
Bit 31 is always set to 1 to identified a structured tag value.
Bit 30 (CAN_SKIP) is set to 1 if the tag can be safely ignore when its TAG_ID value is not a known value (unknown tag). If the CAN_SKIP bit is set to 0 this tag must not be ignored and an error should be reported when its TAG_ID value is not a known value (unknown tag).
Bits 29..28 (DATA_LNG_ENCODING) indicates the length of the data related to the tag. Following values are possible:
- 0b00: No data.
The tag is followed by the next tag value.
- 0b01: 1 cell data
The tag is followed by a 1 cell (u32) data. The next tag is available after this cell.
- 0b10: 2 cells data
The tag is followed by a 2 cells (2 * u32) data. The next tag is available after those two cells.
- 0b11: Data length encoding
The tag is followed by a cell (u32) indicating the size of the data. This size is given in bytes. Data are available right after this cell.
The next tag is available after the data. Padding is present after the data in order to have the next tag aligned on 32bits. This padding is not included in the size of the data.
Bits 27..0 (TAG_ID) is the tag identifier defining a specific tag.
Introduce the structured tag values definition and some specific tags reserved for tests based on this structure definition.
--- libfdt/fdt.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
libfdt/fdt.h +23 -0
@@ -49,6 +49,7 @@ struct fdt_property {#define FDT_MAGIC 0xd00dfeed/* 4: version, 4: total size */ #define FDT_TAGSIZE sizeof(fdt32_t) +#define FDT_CELLSIZE sizeof(fdt32_t)#define FDT_BEGIN_NODE 0x1/* Start node: full name */ #define FDT_END_NODE 0x2/* End node */
@@ -57,6 +58,28 @@ struct fdt_property {#define FDT_NOP 0x4/* nop */ #define FDT_END 0x9 +/* Tag values flags */ +#define FDT_TAG_STRUCTURED (1<<31) +#define FDT_TAG_SKIP_SAFE (1<<30) +#define FDT_TAG_DATA_MASK (3<<28) +#define FDT_TAG_DATA_NONE (0<<28) +#define FDT_TAG_DATA_1CELL (1<<28) +#define FDT_TAG_DATA_2CELLS (2<<28) +#define FDT_TAG_DATA_LNG (3<<28) + +#define FDT_TAG_NO_SKIP (tag_data, tag_id) \ + (FDT_TAG_STRUCTURED | tag_data | tag_id) + +#define FDT_TAG_CAN_SKIP (tag_data, tag_id) \ + (FDT_TAG_STRUCTURED | FDT_TAG_SKIP_SAFE | tag_data | tag_id) + +/* Tests reserved tags */ +#define FDT_TEST_NONE_CAN_SKIP FDT_TAG_CAN_SKIP(FDT_TAG_DATA_NONE, 0) +#define FDT_TEST_1CELL_CAN_SKIP FDT_TAG_CAN_SKIP(FDT_TAG_DATA_1CELL, 0) +#define FDT_TEST_2CELLS_CAN_SKIP FDT_TAG_CAN_SKIP(FDT_TAG_DATA_2CELLS, 0) +#define FDT_TEST_LNG_CAN_SKIP FDT_TAG_CAN_SKIP(FDT_TAG_DATA_LNG, 0) +#define FDT_TEST_NONE_NO_SKIP FDT_TAG_NO_SKIP(FDT_TAG_DATA_NONE, 0) +#define FDT_V1_SIZE (7*sizeof(fdt32_t))#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
Signature · 1 line
2.52.0
Re: [RFC PATCH 09/15] Introduce structured tag value definition
Quoted 4 lines · Herve Codina
The goal of structured tag values is to ease the introduction of new tags in future releases with the capability for an already existing release to ignore those structured tags. In order to do that data length related to the unknown tag needs to be identify.
^ identified
Quoted 1 line
Also a flag is present
"Also add a flag"
Quoted 11 lines
to tell an old release if this tag can be simply skipped or must lead to an error.
Structured tag value is defined on 32bit and is defined as follow:
Bits | 31 | 30 | 29 28 | 27 0|
------+----+----------+-------------------+--------+
Fields| 1 |CAN_SKIP|DATA_LNG_ENCODING|TAG_ID|
------+----+----------+-------------------+--------+Bit 31 is always set to 1 to identified a structured tag value.
^ identify
Quoted 1 line
Bit 30 (
CAN_SKIP) is set to 1 if the tag can be safely ignore when its
^ ignored
Quoted 5 lines
TAG_IDvalue is not a known value (unknown tag). If theCAN_SKIPbit is set to 0 this tag must not be ignored and an error should be reported when itsTAG_IDvalue is not a known value (unknown tag).Bits 29..28 (
DATA_LNG_ENCODING) indicates the length of the data related
I think "LEN" is more common than "LNG".
Quoted 44 lines
to the tag. Following values are possible:
- 0b00: No data.
The tag is followed by the next tag value.- 0b01: 1 cell data
The tag is followed by a 1 cell (u32) data. The next tag is available after this cell.- 0b10: 2 cells data
The tag is followed by a 2 cells (2 * u32) data. The next tag is available after those two cells.- 0b11: Data length encoding
The tag is followed by a cell (u32) indicating the size of the data. This size is given in bytes. Data are available right after this cell.The next tag is available after the data. Padding is present after the data in order to have the next tag aligned on 32bits. This padding is not included in the size of the data.
Bits 27..0 (
TAG_ID) is the tag identifier defining a specific tag.Introduce the structured tag values definition and some specific tags reserved for tests based on this structure definition.
Signed-off-by Herve Codina <[email protected]>--- libfdt/fdt.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)libfdt/fdt.h +1
-0@@ -49,6 +49,7 @@ struct fdt_property {#define FDT_MAGIC 0xd00dfeed/* 4: version, 4: total size */ #define FDT_TAGSIZE sizeof(fdt32_t) +#define FDT_CELLSIZE sizeof(fdt32_t)#define FDT_BEGIN_NODE 0x1/* Start node: full name */ #define FDT_END_NODE 0x2/* End node */
libfdt/fdt.h struct fdt_property { · line 63
Quoted 6 lines
libfdt/fdt.h +3
-0@@ -57,6 +58,28 @@ struct fdt_property {#define FDT_NOP 0x4/* nop */ #define FDT_END 0x9 +/* Tag values flags */ +#define FDT_TAG_STRUCTURED (1<<31) +#define FDT_TAG_SKIP_SAFE (1<<30)
This is called CAN_SKIP in the commit message and SKIP_SAFE here. Using a consistent name would be better IMO.
libfdt/fdt.h struct fdt_property { · line 68
Quoted 5 lines
libfdt/fdt.h +5
-0+#define FDT_TAG_DATA_MASK (3<<28) +#define FDT_TAG_DATA_NONE (0<<28) +#define FDT_TAG_DATA_1CELL (1<<28) +#define FDT_TAG_DATA_2CELLS (2<<28) +#define FDT_TAG_DATA_LNG (3<<28)
I find _LNG (or _LEN) misleading: this is not the length, but rather an enum value telling you the length is stored in the next cell. What about FDT_TAG_DATA_VARLEN?
Otherwise looks good.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [RFC PATCH 09/15] Introduce structured tag value definition
Hi Luca,
Quoted 7 lines · On Wed, 01 Apr 2026 17:11:35 +0200 "Luca Ceresoli" <[email protected]>…
Quoted 4 lines · Herve Codina
The goal of structured tag values is to ease the introduction of new tags in future releases with the capability for an already existing release to ignore those structured tags. In order to do that data length related to the unknown tag needs to be identify.
^ identified
Will be fixed in the next iteration.
Quoted 3 lines
Quoted 2 lines
Also a flag is present
"Also add a flag"
Will be updated in the next iteration.
Quoted 11 lines
Quoted 11 lines
to tell an old release if this tag can be simply skipped or must lead to an error.
Structured tag value is defined on 32bit and is defined as follow:
Bits | 31 | 30 | 29 28 | 27 0|
------+----+----------+-------------------+--------+
Fields| 1 |CAN_SKIP|DATA_LNG_ENCODING|TAG_ID|
------+----+----------+-------------------+--------+
Quoted 7 lines
Quoted 1 line
Bit 31 is always set to 1 to identified a structured tag value.
^ identifyQuoted 1 line
Bit 30 (
CAN_SKIP) is set to 1 if the tag can be safely ignore when its^ ignored
Both will be fixed in the next iteration.
Quoted 9 lines
Quoted 7 lines
TAG_IDvalue is not a known value (unknown tag). If theCAN_SKIPbit is set to 0 this tag must not be ignored and an error should be reported when itsTAG_IDvalue is not a known value (unknown tag).Bits 29..28 (
DATA_LNG_ENCODING) indicates the length of the data relatedI think "LEN" is more common than "LNG".
Agree, will be changed.
...
Quoted 7 lines
libfdt/fdt.h struct fdt_property { · line 63
Quoted 4 lines
libfdt/fdt.h +3
-0+/* Tag values flags */ +#define FDT_TAG_STRUCTURED (1<<31) +#define FDT_TAG_SKIP_SAFE (1<<30)This is called
CAN_SKIPin the commit message andSKIP_SAFEhere. Using a consistent name would be better IMO.
I will use SKIP_SAFE and so update the commit message accordingly in the next iteration.
Quoted 10 lines
libfdt/fdt.h struct fdt_property { · line 68
Quoted 6 lines
libfdt/fdt.h +5
-0+#define FDT_TAG_DATA_MASK (3<<28) +#define FDT_TAG_DATA_NONE (0<<28) +#define FDT_TAG_DATA_1CELL (1<<28) +#define FDT_TAG_DATA_2CELLS (2<<28) +#define FDT_TAG_DATA_LNG (3<<28)I find
_LNG(or_LEN) misleading: this is not the length, but rather an enum value telling you the length is stored in the next cell. What aboutFDT_TAG_DATA_VARLEN?
Yes indeed, VARLEN is better.
I will use FDT_TAG_DATA_VARLEN in the next iteration.
Best regards,
Hervé
RFC PATCH 10/15 fdtdump: Handle unknown tags
In reply to Herve CodinaThe structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Handle those structured tag.
--- fdtdump.c | 45 +++++++++- tests/dumptrees.c | 4 +- tests/run_tests.sh | 41 +++++++++ tests/testdata.h | 2 + tests/trees.S | 110 +++++++++++++++++++++++++ tests/unknown_tags_can_skip.dtb.expect | 26 ++++++ 6 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 tests/unknown_tags_can_skip.dtb.expect
fdtdump.c +42 -3
@@ -44,7 +44,7 @@ static const char *tagname(uint32_t tag) #define dumpf(fmt, args...) \ do { if (debug) printf("// " fmt, ## args); } while (0) -static void dump_blob (void *blob,bool debug) +static void dump_blob (void *blob,bool debug,int dump_unknown) {uintptr_t blob_off = (uintptr_t )blob;struct fdt_header *bph = blob;
@@ -146,20 +146,55 @@ static void dump_blob(void *blob, bool debug)continue ; } +if ((tag &FDT_TAG_STRUCTURED ) && (tag &FDT_TAG_SKIP_SAFE )) { + sz =0 ; +switch (tag &FDT_TAG_DATA_MASK ) { +case FDT_TAG_DATA_NONE : +break ; +case FDT_TAG_DATA_1CELL : + sz =FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_2CELLS : + sz =2 *FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_LNG : +/* Get the length */ + sz =fdt32_to_cpu (GET_CELL (p)); +break ; + } + +if (dump_unknown) { +printf ("%*s// Unknown tag ignored: 0x%08" PRIx32", data lng %d" , + depth * shift,"" , tag, sz); +if (dump_unknown >1 && sz !=0 ) { +printf (" " ); +for (i =0 ; i < sz; i++) +printf ("%02hhx" , *(p + i)); + } +printf ("\n" ); + } + +/* Skip the data bytes */ + p =PALIGN (p + sz,4 ); +continue ; + } +die ("** Unknown tag 0x%08" PRIx32"\n" , tag); } }/* Usage related data. */ static const char usage_synopsis[] ="fdtdump [options] <file>" ; -static const char usage_short_opts[] ="ds" USAGE_COMMON_SHORT_OPTS ; +static const char usage_short_opts[] ="dus" USAGE_COMMON_SHORT_OPTS ;static struct option const usage_long_opts[] = { {"debug" , no_argument,NULL ,'d' }, + {"unknown" , no_argument,NULL ,'u' }, {"scan" , no_argument,NULL ,'s' },USAGE_COMMON_LONG_OPTS };static const char *const usage_opts_help[] = {"Dump debug information while decoding the file" , +"Dump unknown tags information while decoding the file (-uu to have data)" ,"Scan for an embedded fdt in file" ,USAGE_COMMON_OPTS_HELP };
@@ -183,6 +218,7 @@ int main(int argc, char *argv[])const char *file;char *buf;bool debug = false; +int dump_unknown =0 ;bool scan = false;size_t len;
@@ -198,6 +234,9 @@ int main(int argc, char *argv[])case 'd' : debug = true;break ; +case 'u' : + dump_unknown++; +break ;case 's' : scan = true;break ;
@@ -242,7 +281,7 @@ int main(int argc, char *argv[]) }else if (!valid_header (buf, len))die ("%s: header is not valid\n" , file); -dump_blob (buf, debug); +dump_blob (buf, debug, dump_unknown);return 0 ; }
tests/dumptrees.c +3 -1
@@ -25,7 +25,9 @@ static struct {TREE (truncated_property),TREE (truncated_string),TREE (truncated_memrsv),TREE (two_roots), - TREE(named_root) +TREE (named_root), +TREE (unknown_tags_can_skip), + TREE(unknown_tags_no_skip) };#define NUM_TREES (sizeof(trees) / sizeof(trees[0]))
tests/run_tests.sh +41 -0
53 lines not on this page · raw message
tests/testdata.h +2 -0
@@ -57,4 +57,6 @@ extern struct fdt_header truncated_string;extern struct fdt_header truncated_memrsv;extern struct fdt_header two_roots;extern struct fdt_header named_root; +extern struct fdt_header unknown_tags_can_skip; +extern struct fdt_header unknown_tags_no_skip;#endif /* ! __ASSEMBLER__ */
tests/trees.S +110 -0
113 lines not on this page · raw message
tests/unknown_tags_can_skip.dtb.expect new file +26 -0
@@ -0,0 +1,26 @@ +/dts-v1/; + +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + prop-str = "abcd"; + // Unknown tag ignored: 0xe0000000, data lng 8 0000001200000012 + // Unknown tag ignored: 0xf0000000, data lng 3 131313 + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + subsubnode { + // Unknown tag ignored: 0xd0000000, data lng 4 00000123 + prop-int = <0x00000001 0x00000002>; + }; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +};
Signature · 1 line
2.52.0
Re: [RFC PATCH 10/15] fdtdump: Handle unknown tags
Quoted 4 lines · Herve Codina
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Handle those structured tag.
How? This sentence is vague, what about:
Allow dumping the unknown tags or not based on a command line flag.
Quoted 24 lines
fdtdump.c +10
-1@@ -44,7 +44,7 @@ static const char *tagname(uint32_t tag) #define dumpf(fmt, args...) \ do { if (debug) printf("// " fmt, ## args); } while (0) -static void dump_blob (void *blob,bool debug) +static void dump_blob (void *blob,bool debug,int dump_unknown) {uintptr_t blob_off = (uintptr_t )blob;struct fdt_header *bph = blob;@@ -146,20 +146,55 @@ static void dump_blob(void *blob, bool debug)continue ; } +if ((tag &FDT_TAG_STRUCTURED ) && (tag &FDT_TAG_SKIP_SAFE )) { + sz =0 ; +switch (tag &FDT_TAG_DATA_MASK ) { +case FDT_TAG_DATA_NONE : +break ; +case FDT_TAG_DATA_1CELL : + sz =FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_2CELLS :
fdtdump.c static void dump_blob(void *blob, bool debug) · line 167
Quoted 10 lines
fdtdump.c +10
-0+ sz =2 *FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_LNG : +/* Get the length */ + sz =fdt32_to_cpu (GET_CELL (p)); +break ; + } + +if (dump_unknown) { + printf("%*s// Unknown tag ignored: 0x%08"PRIx32", data lng %d",
As before, I'd use "len" instead of "lng".
Quoted 21 lines
fdtdump.c +14
-1+ depth * shift, "", tag, sz); +if (dump_unknown >1 && sz !=0 ) { +printf (" " ); +for (i =0 ; i < sz; i++) +printf ("%02hhx" , *(p + i)); + } +printf ("\n" ); + } + +/* Skip the data bytes */ + p =PALIGN (p + sz,4 ); +continue ; + } +die ("** Unknown tag 0x%08" PRIx32"\n" , tag); } }/* Usage related data. */ static const char usage_synopsis[] ="fdtdump [options] <file>" ; -static const char usage_short_opts[] ="ds" USAGE_COMMON_SHORT_OPTS ;
fdtdump.c static void dump_blob(void *blob, bool debug) · line 197
Quoted 10 lines
fdtdump.c +3
-0+static const char usage_short_opts[] ="dus" USAGE_COMMON_SHORT_OPTS ;static struct option const usage_long_opts[] = { {"debug" , no_argument,NULL ,'d' }, + {"unknown" , no_argument,NULL ,'u' }, {"scan" , no_argument,NULL ,'s' },USAGE_COMMON_LONG_OPTS };static const char *const usage_opts_help[] = {"Dump debug information while decoding the file" , +"Dump unknown tags information while decoding the file (-uu to have data)" ,
^ dump
Quoted 22 lines
tests/trees.S +16
-0@@ -328,3 +328,113 @@ named_root_strings: named_root_strings_end: named_root_end: + + + /* Tree with "unknown" tags that can be skipped + * Use a really future dtb version to check version downgrade on + * modification. + */ + treehdr_vers unknown_tags_can_skip 0xffffffff 0x10 + empty_rsvmap unknown_tags_can_skip + +unknown_tags_can_skip_struct: + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x1 + + beginn "" + fdtlong FDT_TEST_NONE_CAN_SKIP +
tests/trees.S named_root_strings: · line 356
Quoted 10 lines
tests/trees.S +10
-0+ propu32 unknown_tags_can_skip, prop_int, 1 + + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x11 + + propstr unknown_tags_can_skip, prop_str, "abcd" + + fdtlong FDT_TEST_2CELLS_CAN_SKIP + fdtlong 0x12 + fdtlong 0x12
Can you use different values here, just to make the test slightly more robust? Just in case parsing ends up on the wrong cell, as unlikely as it can be.
Same in various places below.
Quoted 14 lines
tests/trees.S +14
-0+ + fdtlong FDT_TEST_LNG_CAN_SKIP + fdtlong 3 + .byte 0x13 + .byte 0x13 + .byte 0x13 + .byte 0 /* padding */ + + beginn "subnode1" + propu64 unknown_tags_can_skip, prop_int, 1, 2 + fdtlong FDT_TEST_NONE_CAN_SKIP + endn + + beginn "subnode2"
tests/trees.S named_root_strings: · line 380
Quoted 10 lines
tests/trees.S +10
-0+ fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x121 + propu64 unknown_tags_can_skip, prop_int1, 1, 2 + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x122 + propu64 unknown_tags_can_skip, prop_int2, 1, 2 + beginn "subsubnode" + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x123 + propu64 unknown_tags_can_skip, prop_int, 1, 2
As before, you are using values 1 and 2 for all the properties, I'd use different values, and possibly even with different amounts of cells in properties.
Other than these two minor nits, this patch looks very good to me.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [RFC PATCH 10/15] fdtdump: Handle unknown tags
Hi Luca,
Quoted 9 lines · On Wed, 01 Apr 2026 17:15:09 +0200 "Luca Ceresoli" <[email protected]>…
Quoted 4 lines · Herve Codina
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Handle those structured tag.
How? This sentence is vague, what about:
Allow dumping the unknown tags or not based on a command line flag.
Hum indeed but I don't fully agree with your proposal.
The patch adds support for structured tag in fdtdump and introduce the '-u' option to dump unknown tags which can be safely ignored.
What do you think about:
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Add support for those structured tags in fdtdump and introduce a command line option to dump unknown tags that should be ignored.
Quoted 29 lines
Quoted 29 lines
fdtdump.c +14
-1@@ -44,7 +44,7 @@ static const char *tagname(uint32_t tag) #define dumpf(fmt, args...) \ do { if (debug) printf("// " fmt, ## args); } while (0) -static void dump_blob (void *blob,bool debug) +static void dump_blob (void *blob,bool debug,int dump_unknown) {uintptr_t blob_off = (uintptr_t )blob;struct fdt_header *bph = blob;@@ -146,20 +146,55 @@ static void dump_blob(void *blob, bool debug)continue ; } +if ((tag &FDT_TAG_STRUCTURED ) && (tag &FDT_TAG_SKIP_SAFE )) { + sz =0 ; +switch (tag &FDT_TAG_DATA_MASK ) { +case FDT_TAG_DATA_NONE : +break ; +case FDT_TAG_DATA_1CELL : + sz =FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_2CELLS : + sz =2 *FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_LNG : +/* Get the length */
Quoted 8 lines
fdtdump.c static void dump_blob(void *blob, bool debug) · line 167
Quoted 6 lines
fdtdump.c +6
-0+ sz =fdt32_to_cpu (GET_CELL (p)); +break ; + } + +if (dump_unknown) { + printf("%*s// Unknown tag ignored: 0x%08"PRIx32", data lng %d",As before, I'd use "len" instead of "lng".
I will change in the next iteration.
Quoted 19 lines
Quoted 19 lines
fdtdump.c +14
-0+ depth * shift, "", tag, sz); +if (dump_unknown >1 && sz !=0 ) { +printf (" " ); +for (i =0 ; i < sz; i++) +printf ("%02hhx" , *(p + i)); + } +printf ("\n" ); + } + +/* Skip the data bytes */ + p =PALIGN (p + sz,4 ); +continue ; + } +die ("** Unknown tag 0x%08" PRIx32"\n" , tag); } }
Quoted 15 lines
fdtdump.c static void dump_blob(void *blob, bool debug) · line 197
Quoted 13 lines
fdtdump.c +3
-1/* Usage related data. */ static const char usage_synopsis[] ="fdtdump [options] <file>" ; -static const char usage_short_opts[] ="ds" USAGE_COMMON_SHORT_OPTS ; +static const char usage_short_opts[] ="dus" USAGE_COMMON_SHORT_OPTS ;static struct option const usage_long_opts[] = { {"debug" , no_argument,NULL ,'d' }, + {"unknown" , no_argument,NULL ,'u' }, {"scan" , no_argument,NULL ,'s' },USAGE_COMMON_LONG_OPTS };static const char *const usage_opts_help[] = {"Dump debug information while decoding the file" , +"Dump unknown tags information while decoding the file (-uu to have data)" ,^ dump
Will be fixed in the next iteration.
Quoted 29 lines
Quoted 29 lines
tests/trees.S +22
-0@@ -328,3 +328,113 @@ named_root_strings: named_root_strings_end: named_root_end: + + + /* Tree with "unknown" tags that can be skipped + * Use a really future dtb version to check version downgrade on + * modification. + */ + treehdr_vers unknown_tags_can_skip 0xffffffff 0x10 + empty_rsvmap unknown_tags_can_skip + +unknown_tags_can_skip_struct: + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x1 + + beginn "" + fdtlong FDT_TEST_NONE_CAN_SKIP + + propu32 unknown_tags_can_skip, prop_int, 1 + + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x11 + + propstr unknown_tags_can_skip, prop_str, "abcd"
Quoted 8 lines
tests/trees.S named_root_strings: · line 356
Quoted 4 lines
tests/trees.S +4
-0+ + fdtlong FDT_TEST_2CELLS_CAN_SKIP + fdtlong 0x12 + fdtlong 0x12Can you use different values here, just to make the test slightly more robust? Just in case parsing ends up on the wrong cell, as unlikely as it can be.
Yes indeed, good proposal. I will update patterns.
Quoted 23 lines
Same in various places below.
Quoted 20 lines
tests/trees.S +20
-0+ + fdtlong FDT_TEST_LNG_CAN_SKIP + fdtlong 3 + .byte 0x13 + .byte 0x13 + .byte 0x13 + .byte 0 /* padding */ + + beginn "subnode1" + propu64 unknown_tags_can_skip, prop_int, 1, 2 + fdtlong FDT_TEST_NONE_CAN_SKIP + endn + + beginn "subnode2" + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x121 + propu64 unknown_tags_can_skip, prop_int1, 1, 2 + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x122 + propu64 unknown_tags_can_skip, prop_int2, 1, 2
Quoted 8 lines
tests/trees.S named_root_strings: · line 380
Quoted 4 lines
tests/trees.S +4
-0+ beginn "subsubnode" + fdtlong FDT_TEST_1CELL_CAN_SKIP + fdtlong 0x123 + propu64 unknown_tags_can_skip, prop_int, 1, 2As before, you are using values 1 and 2 for all the properties, I'd use different values, and possibly even with different amounts of cells in properties.
I will update too.
Best regards,
Hervé
Re: [RFC PATCH 10/15] fdtdump: Handle unknown tags
Quoted 20 lines · Herve Codina
Hi Luca,
Quoted 9 lines · On Wed, 01 Apr 2026 17:15:09 +0200 "Luca Ceresoli" <[email protected]>…
Quoted 4 lines · Herve Codina
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Handle those structured tag.
How? This sentence is vague, what about:
Allow dumping the unknown tags or not based on a command line flag.
Hum indeed but I don't fully agree with your proposal.
The patch adds support for structured tag in fdtdump and introduce the '-u' option to dump unknown tags which can be safely ignored.
Quoted 7 lines
What do you think about:
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Add support for those structured tags in fdtdump and introduce a command line option to dump unknown tags that should be ignored.
Looks way better now, thanks!
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
RFC PATCH 11/15 flattree: Handle unknown tags
In reply to Herve CodinaThe structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Handle those structured tag.
--- flattree.c | 65 ++++++++++++++++++++-- tests/run_tests.sh | 5 ++ tests/unknown_tags_can_skip.dtb.dts.expect | 19 +++++++ 3 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 tests/unknown_tags_can_skip.dtb.dts.expect
flattree.c +60 -5
@@ -579,7 +579,8 @@ static void flat_read_chunk(struct inbuf *inb, void *p, int len)if ((inb->ptr + len) > inb->limit)die ("Premature end of data parsing flat device tree\n" ); -memcpy (p, inb->ptr, len); +if (p) +memcpy (p, inb->ptr, len); inb->ptr += len; }
@@ -604,6 +605,61 @@ static void flat_realign(struct inbuf *inb, int align)die ("Premature end of data parsing flat device tree\n" ); } +static bool flat_skip_unknown_tag (struct inbuf *inb,uint32_t tag) +{ +uint32_t lng; + +if (!(tag &FDT_TAG_STRUCTURED ) || !(tag &FDT_TAG_SKIP_SAFE )) +return false; + +switch (tag &FDT_TAG_DATA_MASK ) { +case FDT_TAG_DATA_NONE : +break ; + +case FDT_TAG_DATA_1CELL : +flat_read_word (inb); +break ; + +case FDT_TAG_DATA_2CELLS : +flat_read_word (inb); +flat_read_word (inb); +break ; + +case FDT_TAG_DATA_LNG : +/* Get the length */ + lng =flat_read_word (inb); + +/* Skip the following length bytes */ +flat_read_chunk (inb,NULL , lng); + +flat_realign (inb,sizeof (uint32_t )); +break ; + } + +return true; +} + +static uint32_t flat_read_tag (struct inbuf *inb) +{ +uint32_t tag; + +do { + tag =flat_read_word (inb); +switch (tag) { +case FDT_BEGIN_NODE : +case FDT_END_NODE : +case FDT_PROP : +case FDT_NOP : +case FDT_END : +return tag; +default : +break ; + } + }while (flat_skip_unknown_tag (inb, tag)); + +die ("Cannot skip unknown tag 0x%08x\n" , tag); +} +static const char *flat_read_string (struct inbuf *inb) {int len =0 ;
@@ -750,7 +806,7 @@ static struct node *unflatten_tree(struct inbuf *dtbuf,struct property *prop;struct node *child; - val =flat_read_word (dtbuf); + val =flat_read_tag (dtbuf);switch (val) {case FDT_PROP :if (node->children)
@@ -905,14 +961,13 @@ struct dt_info *dt_from_blob(const char *fname) reservelist =flat_read_mem_reserve (&memresvbuf); - val =flat_read_word (&dtbuf); - + val =flat_read_tag (&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); - val =flat_read_word (&dtbuf); + val =flat_read_tag (&dtbuf);if (val !=FDT_END )die ("Device tree blob doesn't end with FDT_END\n" );
tests/run_tests.sh +5 -0
@@ -881,6 +881,11 @@ dtc_tests () { # Tests for overlay/plugin generation dtc_overlay_tests + + # Tests with "unknown tags" + run_dtc_test -I dtb -O dts -o unknown_tags_can_skip.dtb.dts unknown_tags_can_skip.dtb + base_run_test check_diff unknown_tags_can_skip.dtb.dts "$SRCDIR/unknown_tags_can_skip.dtb.dts.expect" + run_wrap_error_test $DTC -I dtb -O dts -o unknown_tags_no_skip.dtb.dts unknown_tags_no_skip.dtb } cmp_tests () {
tests/unknown_tags_can_skip.dtb.dts.expect new file +19 -0
@@ -0,0 +1,19 @@ +/dts-v1/; + +/ { + prop-int = <0x01>; + prop-str = "abcd"; + + subnode1 { + prop-int = <0x01 0x02>; + }; + + subnode2 { + prop-int1 = <0x01 0x02>; + prop-int2 = <0x01 0x02>; + + subsubnode { + prop-int = <0x01 0x02>; + }; + }; +};
Signature · 1 line
2.52.0
Re: [RFC PATCH 11/15] flattree: Handle unknown tags
Quoted 18 lines · Herve Codina
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
Handle those structured tag.
Signed-off-by Herve Codina <[email protected]>+++ b/tests/unknown_tags_can_skip.dtb.dts.expect
tests/unknown_tags_can_skip.dtb.dts.expect new file +9
-0@@ -0,0 +1,19 @@ +/dts-v1/; + +/ { + prop-int = <0x01>; + prop-str = "abcd"; + + subnode1 { + prop-int = <0x01 0x02>; + };
tests/unknown_tags_can_skip.dtb.dts.expect line 19
Quoted 10 lines
tests/unknown_tags_can_skip.dtb.dts.expect new file +10
-0+ + subnode2 { + prop-int1 = <0x01 0x02>; + prop-int2 = <0x01 0x02>; + + subsubnode { + prop-int = <0x01 0x02>; + }; + }; +};
Of course this will need updating in case you adopt the changes to the tests I suggested in patch 10.
With that done:
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
RFC PATCH 12/15 libfdt: Handle unknown tags in fdt_get_next()
In reply to Herve CodinaThe structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
libfdt uses fdt_get_next() to get a tag.
Filtering out tags that should be ignored in fdt_get_next() allows to have the filtering done globally and allows, in future release, to have a central place to add new known tags that should not be filtered out.
An already known tag exists with the meaning of "just ignore". This tag is FDT_NOP. fdt_get_next() callers already handle the FDT_NOP tag.
Avoid unneeded modification at callers side and use a fake FDT_NOP tag when an unknown tag that should be ignored is encountered.
Add also fdt_get_next_() internal function for callers who need to know if the FDT_NOP tag returned is a real FDT_NOP or a fake FDT_NOP due to an unknown tag.
--- libfdt/fdt.c | 75 ++++++++++++++++++++++++++++++++++++++-- libfdt/libfdt_internal.h | 3 ++ tests/run_tests.sh | 9 +++-- 3 files changed, 83 insertions(+), 4 deletions(-)
libfdt/fdt.c +73 -2
@@ -167,7 +167,7 @@ const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)return fdt_offset_ptr_ (fdt, offset); } -uint32_t fdt_next_tag (const void *fdt,int startoffset,int *nextoffset) +static uint32_t fdt_next_tag_all (const void *fdt,int startoffset,int *nextoffset) {const fdt32_t *tagp, *lenp;uint32_t tag, len, sum;
@@ -218,7 +218,37 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)break ;default : -return FDT_END ; +if (!(tag &FDT_TAG_STRUCTURED ) || !(tag &FDT_TAG_SKIP_SAFE )) +return FDT_END ; + +switch (tag &FDT_TAG_DATA_MASK ) { +case FDT_TAG_DATA_NONE : +break ; +case FDT_TAG_DATA_1CELL : + offset +=FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_2CELLS : + offset +=2 *FDT_CELLSIZE ; +break ; +case FDT_TAG_DATA_LNG : +/* Get the length */ + lenp =fdt_offset_ptr (fdt, offset,sizeof (*lenp)); +if (!can_assume (VALID_DTB ) && !lenp) +return FDT_END ;/* premature end */ + len =fdt32_to_cpu (*lenp); +/* +* Skip the cell encoding the length and the +* following length bytes +*/ + len +=sizeof (*lenp); + sum = len + offset; +if (!can_assume (VALID_DTB ) && + (sum >=INT_MAX || sum < (uint32_t ) offset)) +return FDT_END ;/* premature end */ + + offset += len; +break ; + } }if (!fdt_offset_ptr (fdt, startoffset, offset - startoffset))
@@ -228,6 +258,47 @@ uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)return tag; } +static bool fdt_is_unknown_tag (uint32_t tag) +{ +switch (tag) { +case FDT_BEGIN_NODE : +case FDT_END_NODE : +case FDT_PROP : +case FDT_NOP : +case FDT_END : +return false; +default : +break ; + } +return true; +} + +uint32_t fdt_next_tag_ (const void *fdt,int startoffset,int *nextoffset,bool *is_unknown) +{ +uint32_t tag; +bool unknown = false; + +/* Retrieve next tag */ + tag =fdt_next_tag_all (fdt, startoffset, nextoffset); +if (tag ==FDT_END ) + goto end; + +if (fdt_is_unknown_tag (tag)) { + unknown = true; +/* Use a known tag that should be skipped by the caller */ + tag =FDT_NOP ; + } +end: +if (is_unknown) + *is_unknown = unknown; +return tag; +} + +uint32_t fdt_next_tag (const void *fdt,int startoffset,int *nextoffset) +{ +return fdt_next_tag_ (fdt, startoffset, nextoffset,NULL ); +} +int fdt_check_node_offset_ (const void *fdt,int offset) { if (!can_assume(VALID_INPUT)
libfdt/libfdt_internal.h +3 -0
@@ -20,6 +20,9 @@ int32_t fdt_ro_probe_(const void *fdt); } \ } +uint32_t fdt_next_tag_ (const void *fdt,int startoffset,int *nextoffset, +bool *is_unknown); +int fdt_check_node_offset_ (const void *fdt,int offset);int fdt_check_prop_offset_ (const void *fdt,int offset);
tests/run_tests.sh +7 -2
@@ -576,11 +576,12 @@ libfdt_tests () { run_test dtbs_equal_ordered cell-overflow.test.dtb cell-overflow-results.test.dtb # check full tests - for good in test_tree1.dtb; do + for good in test_tree1.dtb unknown_tags_can_skip.dtb; do run_test check_full $good done for bad in truncated_property.dtb truncated_string.dtb \ - truncated_memrsv.dtb two_roots.dtb named_root.dtb; do + truncated_memrsv.dtb two_roots.dtb named_root.dtb \ + unknown_tags_no_skip.dtb; do run_test check_full -n $bad done }
@@ -961,6 +962,10 @@ fdtget_tests () { run_fdtget_test "<the dead silence>" -tx \ -d "<the dead silence>" $dtb /randomnode doctor-who run_fdtget_test "<blink>" -tx -d "<blink>" $dtb /memory doctor-who + + # test with unknown tags involved + run_fdtget_test "1 2" unknown_tags_can_skip.dtb /subnode1 prop-int + run_wrap_error_test $DTGET unknown_tags_no_skip.dtb /subnode1 prop-int } fdtput_tests () {
Signature · 1 line
2.52.0
Re: [RFC PATCH 12/15] libfdt: Handle unknown tags in fdt_get_next()
Quoted 4 lines · Herve Codina
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
libfdt uses
fdt_get_next()to get a tag.
I think you mean fdt_next_tag(), here and elsewhere in the commit message.
Quoted 3 lines
Filtering out tags that should be ignored in
fdt_get_next()allows to have the filtering done globally and allows, in future release, to have
^ releases
Quoted 9 lines
a central place to add new known tags that should not be filtered out.
An already known tag exists with the meaning of "just ignore". This tag is
FDT_NOP.fdt_get_next()callers already handle theFDT_NOPtag.Avoid unneeded modification at callers side and use a fake
FDT_NOPtag when an unknown tag that should be ignored is encountered.Add also
fdt_get_next_()internal function for callers who need to know
And here fdt_next_tag_()?
Quoted 4 lines
if the
FDT_NOPtag returned is a realFDT_NOPor a fakeFDT_NOPdue to an unknown tag.Signed-off-by Herve Codina <[email protected]>
Looks good otherwise, so with those fixed:
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [RFC PATCH 12/15] libfdt: Handle unknown tags in fdt_get_next()
Hi Luca,
Quoted 19 lines · On Wed, 01 Apr 2026 17:17:46 +0200 "Luca Ceresoli" <[email protected]>…
Quoted 4 lines · Herve Codina
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error when they are read.
libfdt uses
fdt_get_next()to get a tag.I think you mean
fdt_next_tag(), here and elsewhere in the commit message.Quoted 3 lines
Filtering out tags that should be ignored in
fdt_get_next()allows to have the filtering done globally and allows, in future release, to have^ releasesQuoted 5 lines
a central place to add new known tags that should not be filtered out.
An already known tag exists with the meaning of "just ignore". This tag is
FDT_NOP.fdt_get_next()callers already handle theFDT_NOPtag.
Quoted 7 lines
Quoted 4 lines
Avoid unneeded modification at callers side and use a fake
FDT_NOPtag when an unknown tag that should be ignored is encountered.Add also
fdt_get_next_()internal function for callers who need to knowAnd here
fdt_next_tag_()?
You're perfectly right. fdt_next_tag() and fdt_next_tag_() are the correct functions.
This will be fixed in the next iteration as well as the commit title (same issue) and the 'release' typo.
Best regards,
Hervé
RFC PATCH 13/15 libfdt: Introduce fdt_ptr_offset_
In reply to Herve Codinalibfdt provides internal used helpers to convert an offset to a pointer but nothing to do the reverse operation.
Fill this lack and introduce the fdt_ptr_offset_() internal helper to convert a pointer to an offset.
--- libfdt/libfdt_internal.h | 5 +++++ 1 file changed, 5 insertions(+)
libfdt/libfdt_internal.h +5 -0
@@ -47,6 +47,11 @@ static inline void *fdt_offset_ptr_w_(void *fdt, int offset)return (void *)(uintptr_t )fdt_offset_ptr_ (fdt, offset); } +static inline int fdt_ptr_offset_ (const void *fdt,const void *ptr) +{ +return (const char *)ptr - (const char *)fdt_offset_ptr_ (fdt,0 ); +} +static inline const struct fdt_reserve_entry *fdt_mem_rsv_ (const void *fdt,int n) { const struct fdt_reserve_entry *rsv_table =
Signature · 1 line
2.52.0
Re: [RFC PATCH 13/15] libfdt: Introduce fdt_ptr_offset_
Quoted 1 line · Herve Codina
libfdt provides internal used helpers to convert an offset to a pointer
^^^^^^^^^^^^^^^^^^^^^
Just "internal helpers"
Quoted 3 lines
but nothing to do the reverse operation.
Fill this lack and introduce the
fdt_ptr_offset_()internal helper to
"Fill this gap"? Or just "Introduce the..." would be enough.
With those fixed:
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
RFC PATCH 14/15 libfdt: Handle unknown tags on dtb modifications
In reply to Herve CodinaThe structured tag value definition introduced recently gives the ability to ignore unknown tags without any error.
When the dtb is modified those unknown tags have to be taken into account.
First, depending on the unknown tag location, the item associated with the tag is identified:
- An unknown tag located just after a FDT_BEGIN_NODE is related to the node.
- An unknown tag located just after a FDT_PROP is related to the property.
- An unknown tag out of any node (i.e located before the first FDT_BEGIN_NODE or after the last FDT_END_NODE is a global tag related to the dtb itself.
Then, if we are allowed to write a dtb containing unknown tags, the following rules are used:
- When a property is modified, tags related to this property are removed and the dtb version is downgraded.
- When a property is removed, tags related to this property are obviously removed. The dtb version is kept unchanged.
- When a property or a node is added, obviously no unknown tags are added and the dtb version is kept unchanged.
- When a node is removed, tags related to this node are obviously removed. The dtb version is kept unchanged.
- Adding, removing or modifying a property is not considered as a node modification and so, those operations have no impacts on unknown tags related to the node. Those node related tags are kept unchanged.
- The only modification considered as a node modification is setting its name. We consider that this operation has no impact on tags related to the node. Here also, those node related tags and the dtb version are kept unchanged.
- Global (dtb related) unknown tags are kept unchanged regardless the modification done.
Implement those rules when a dtb is modified.
--- libfdt/fdt_rw.c | 118 +++++++++++++++++- tests/run_tests.sh | 50 ++++++++ ...own_tags_can_skip.fdtput.test.dtb.0.expect | 31 +++++ ...own_tags_can_skip.fdtput.test.dtb.1.expect | 35 ++++++ ...own_tags_can_skip.fdtput.test.dtb.2.expect | 33 +++++ ...own_tags_can_skip.fdtput.test.dtb.3.expect | 35 ++++++ ...own_tags_can_skip.fdtput.test.dtb.4.expect | 34 +++++ ...own_tags_can_skip.fdtput.test.dtb.5.expect | 32 +++++ ...own_tags_can_skip.fdtput.test.dtb.6.expect | 27 ++++ 9 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect create mode 100644 tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect
libfdt/fdt_rw.c +117 -1
@@ -188,6 +188,60 @@ int fdt_del_mem_rsv(void *fdt, int n)return fdt_splice_mem_rsv_ (fdt, re,1 ,0 ); } +static void fdt_nopify_area (void *fdt,int start_offset,int next_offset) +{ +int count = (next_offset - start_offset) /sizeof (fdt32_t); +fdt32_t fdt32_nop =cpu_to_fdt32 (FDT_NOP ); +fdt32_t *ptr; + + ptr =fdt_offset_ptr_w_ (fdt, start_offset); +while (count--) + *(ptr++) = fdt32_nop; +}; + +static int fdt_property_remove_unknown_tags (void *fdt, +const struct fdt_property *prop, +bool downgrade_version) +{ +int nextoffset, offset; +bool is_unknown; +uint32_t tag; + + tag =fdt_next_tag (fdt,fdt_ptr_offset_ (fdt, prop), &nextoffset); +if (tag ==FDT_END ) +return nextoffset; + +/* +* Look at all tags related to the current property. I.e. tags after the +* current property and before either the next property, a sub-node or +* the end of current node +*/ +do { + offset = nextoffset; + tag =fdt_next_tag_ (fdt, offset, &nextoffset, &is_unknown); +if (tag ==FDT_END ) +return nextoffset; + +/* +* Unknown tags are returned as NOP. Force FDT_NOP to be really +* present in the area to remove the unknown tag and its related +* data. Also, as a tag is removed, downgrade the dtb version +* if asked for. +*/ +if (tag ==FDT_NOP ) { +if (is_unknown) { +if (downgrade_version) +fdt_downgrade_version (fdt); +fdt_nopify_area (fdt, offset, nextoffset); + } + } + + }while ((tag !=FDT_PROP ) && (tag !=FDT_BEGIN_NODE ) && + (tag !=FDT_END_NODE )); + +return 0 ; +} +static int fdt_resize_property_ (void *fdt,int nodeoffset,const char *name,int namelen,int len,struct fdt_property **prop)
@@ -200,6 +254,14 @@ static int fdt_resize_property_(void *fdt, int nodeoffset,if (!*prop)return oldlen; +/* +* The property is resized. Remove possible unknown tags related to the +* property downgrading the dtb version. +*/ + err =fdt_property_remove_unknown_tags (fdt, *prop, true); +if (err) +return err; +if ((err =fdt_splice_struct_ (fdt, (*prop)->data,FDT_TAGALIGN (oldlen),FDT_TAGALIGN (len))))return err;
@@ -208,6 +270,29 @@ static int fdt_resize_property_(void *fdt, int nodeoffset,return 0 ; } +static int fdt_node_skip_unknown_tags (void *fdt,int next) +{ +int nextoffset = next; +int offset; +uint32_t tag; + +/* +* Skip all tags related to the current node. I.e. tags after the +* current node and before either the next property, a sub-node or the +* end of current node. +*/ +do { + offset = nextoffset; + tag =fdt_next_tag (fdt, offset, &nextoffset); +if (tag ==FDT_END ) +return nextoffset; + + }while ((tag !=FDT_PROP ) && (tag !=FDT_BEGIN_NODE ) && + (tag !=FDT_END_NODE )); + +return offset; +} +static int fdt_add_property_ (void *fdt,int nodeoffset,const char *name,int namelen,int len,struct fdt_property **prop) {
@@ -220,6 +305,15 @@ static int fdt_add_property_(void *fdt, int nodeoffset, const char *name,if ((nextoffset =fdt_check_node_offset_ (fdt, nodeoffset)) <0 )return nextoffset; +/* +* nextoffset it at the first tag after the node. +* Skip possible unknown tags related to the node in order to add the +* property after those tags. +*/ + nextoffset =fdt_node_skip_unknown_tags (fdt, nextoffset); +if (nextoffset <0 ) +return nextoffset; + namestroff =fdt_find_add_string_ (fdt, name, namelen, &allocated);if (namestroff <0 )return namestroff;
@@ -309,6 +403,14 @@ int fdt_appendprop(void *fdt, int nodeoffset, const char *name, prop =fdt_get_property_w (fdt, nodeoffset, name, &oldlen);if (prop) { +/* +* The property is going to be modified. Remove possible unknown +* tags related to this property downgrading the dtb version. +*/ + err =fdt_property_remove_unknown_tags (fdt, prop, true); +if (err) +return err; + newlen = len + oldlen; err = fdt_splice_struct_(fdt, prop->data, FDT_TAGALIGN(oldlen),
@@ -331,6 +433,7 @@ int fdt_delprop(void *fdt, int nodeoffset, const char *name) {struct fdt_property *prop;int len, proplen; +int err;FDT_RW_PROBE (fdt);
@@ -338,6 +441,14 @@ int fdt_delprop(void *fdt, int nodeoffset, const char *name)if (!prop)return len; +/* +* The property is going to be removed. Remove also possible unknown +* tags related to this property. Keep the dtb version unchanged. +*/ + err =fdt_property_remove_unknown_tags (fdt, prop, false); +if (err) +return err; + proplen =sizeof (*prop) +FDT_TAGALIGN (len);return fdt_splice_struct_ (fdt, prop, proplen,0 ); }
@@ -366,7 +477,12 @@ int fdt_add_subnode_namelen(void *fdt, int parentoffset,else if (offset != -FDT_ERR_NOTFOUND )return offset; -/* Try to place the new node after the parent's properties */ +/* +* Try to place the new node after the parent's properties and unknown +* tags related to those properties. +* Unknown tags are reported as FDT_NOP tags by fdt_next_tag. +* Skipping FDT_NOP tags will correctly skip unknown tags. +*/ tag =fdt_next_tag (fdt, parentoffset, &nextoffset);/* the fdt_subnode_offset_namelen() should ensure this never hits */ if (!can_assume (LIBFDT_FLAWLESS ) && (tag !=FDT_BEGIN_NODE ))
tests/run_tests.sh +50 -0
56 lines not on this page · raw message
tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect new file +31 -0
@@ -0,0 +1,31 @@ +/dts-v1/; +// version: 4294967295 +// last_comp_version: 16 + +// Unknown tag ignored: 0xd0000000, data lng 4 00000001 +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + prop-str = "abcd"; + // Unknown tag ignored: 0xe0000000, data lng 8 0000001200000012 + // Unknown tag ignored: 0xf0000000, data lng 3 131313 + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + subsubnode { + // Unknown tag ignored: 0xd0000000, data lng 4 00000123 + prop-int = <0x00000001 0x00000002>; + }; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +}; +// Unknown tag ignored: 0xd0000000, data lng 4 00000002 +// Unknown tag ignored: 0xf0000000, data lng 2 0303
tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect new file +35 -0
@@ -0,0 +1,35 @@ +/dts-v1/; +// version: 17 +// last_comp_version: 16 + +// Unknown tag ignored: 0xd0000000, data lng 4 00000001 +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + prop-str = "vwxyz"; + // [NOP] + // [NOP] + // [NOP] + // [NOP] + // [NOP] + // [NOP] + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + subsubnode { + // Unknown tag ignored: 0xd0000000, data lng 4 00000123 + prop-int = <0x00000001 0x00000002>; + }; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +}; +// Unknown tag ignored: 0xd0000000, data lng 4 00000002 +// Unknown tag ignored: 0xf0000000, data lng 2 0303
tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect new file +33 -0
@@ -0,0 +1,33 @@ +/dts-v1/; +// version: 17 +// last_comp_version: 16 + +// Unknown tag ignored: 0xd0000000, data lng 4 00000001 +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + prop-str = "abcd"; + // Unknown tag ignored: 0xe0000000, data lng 8 0000001200000012 + // Unknown tag ignored: 0xf0000000, data lng 3 131313 + tst-fdtput { + }; + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + subsubnode { + // Unknown tag ignored: 0xd0000000, data lng 4 00000123 + prop-int = <0x00000001 0x00000002>; + }; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +}; +// Unknown tag ignored: 0xd0000000, data lng 4 00000002 +// Unknown tag ignored: 0xf0000000, data lng 2 0303
tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect new file +35 -0
@@ -0,0 +1,35 @@ +/dts-v1/; +// version: 17 +// last_comp_version: 16 + +// Unknown tag ignored: 0xd0000000, data lng 4 00000001 +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + prop-str = "abcd"; + // Unknown tag ignored: 0xe0000000, data lng 8 0000001200000012 + // Unknown tag ignored: 0xf0000000, data lng 3 131313 + tst-fdtput { + n2 { + }; + }; + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + subsubnode { + // Unknown tag ignored: 0xd0000000, data lng 4 00000123 + prop-int = <0x00000001 0x00000002>; + }; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +}; +// Unknown tag ignored: 0xd0000000, data lng 4 00000002 +// Unknown tag ignored: 0xf0000000, data lng 2 0303
tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect new file +34 -0
@@ -0,0 +1,34 @@ +/dts-v1/; +// version: 17 +// last_comp_version: 16 + +// Unknown tag ignored: 0xd0000000, data lng 4 00000001 +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + // [NOP] + // [NOP] + // [NOP] + // [NOP] + // [NOP] + // [NOP] + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + subsubnode { + // Unknown tag ignored: 0xd0000000, data lng 4 00000123 + prop-int = <0x00000001 0x00000002>; + }; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +}; +// Unknown tag ignored: 0xd0000000, data lng 4 00000002 +// Unknown tag ignored: 0xf0000000, data lng 2 0303
tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect new file +32 -0
@@ -0,0 +1,32 @@ +/dts-v1/; +// version: 17 +// last_comp_version: 16 + +// Unknown tag ignored: 0xd0000000, data lng 4 00000001 +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + prop-str = "abcd"; + // Unknown tag ignored: 0xe0000000, data lng 8 0000001200000012 + // Unknown tag ignored: 0xf0000000, data lng 3 131313 + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-tst-fdtput = "Test fdtput"; + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + subsubnode { + // Unknown tag ignored: 0xd0000000, data lng 4 00000123 + prop-int = <0x00000001 0x00000002>; + }; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +}; +// Unknown tag ignored: 0xd0000000, data lng 4 00000002 +// Unknown tag ignored: 0xf0000000, data lng 2 0303
tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect new file +27 -0
@@ -0,0 +1,27 @@ +/dts-v1/; +// version: 17 +// last_comp_version: 16 + +// Unknown tag ignored: 0xd0000000, data lng 4 00000001 +/ { + // Unknown tag ignored: 0xc0000000, data lng 0 + prop-int = <0x00000001>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000011 + prop-str = "abcd"; + // Unknown tag ignored: 0xe0000000, data lng 8 0000001200000012 + // Unknown tag ignored: 0xf0000000, data lng 3 131313 + subnode1 { + prop-int = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xc0000000, data lng 0 + }; + subnode2 { + // Unknown tag ignored: 0xd0000000, data lng 4 00000121 + prop-int1 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000122 + prop-int2 = <0x00000001 0x00000002>; + // Unknown tag ignored: 0xd0000000, data lng 4 00000124 + }; + // Unknown tag ignored: 0xf0000000, data lng 5 1414141414 +}; +// Unknown tag ignored: 0xd0000000, data lng 4 00000002 +// Unknown tag ignored: 0xf0000000, data lng 2 0303
Signature · 1 line
2.52.0
Re: [RFC PATCH 14/15] libfdt: Handle unknown tags on dtb modifications
Quoted 11 lines · Herve Codina
The structured tag value definition introduced recently gives the ability to ignore unknown tags without any error.
When the dtb is modified those unknown tags have to be taken into account.
First, depending on the unknown tag location, the item associated with the tag is identified:
- An unknown tag located just after aFDT_BEGIN_NODEis related to the node.
Quoted 5 lines
- An unknown tag located just after a
FDT_PROPis related to the property.- An unknown tag out of any node (i.e located before the first
FDT_BEGIN_NODEor after the lastFDT_END_NODEis a global tag
^ missing ')'
Quoted 1 line
related to the dtb itself.
Out of curiosity, is there a real use case for global tags after FDT_END_NODE?
Quoted 23 lines
Then, if we are allowed to write a dtb containing unknown tags, the following rules are used:
- When a property is modified, tags related to this property are removed and the dtb version is downgraded.- When a property is removed, tags related to this property are obviously removed. The dtb version is kept unchanged.
- When a property or a node is added, obviously no unknown tags are added and the dtb version is kept unchanged.
- When a node is removed, tags related to this node are obviously removed. The dtb version is kept unchanged.
- Adding, removing or modifying a property is not considered as a node modification and so, those operations have no impacts on unknown tags related to the node. Those node related tags are kept unchanged.
- The only modification considered as a node modification is setting its name. We consider that this operation has no impact on tags related to the node. Here also, those node related tags and the dtb version are kept unchanged.
Quoted 6 lines
- Global (dtb related) unknown tags are kept unchanged regardless the modification done.
Implement those rules when a dtb is modified.
Signed-off-by Herve Codina <[email protected]>
With the tests updated as per comments in patch 10:
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [RFC PATCH 14/15] libfdt: Handle unknown tags on dtb modifications
Hi Luca,
On Wed, 01 Apr 2026 17:18:54 +0200
"Luca Ceresoli" <[email protected]> wrote:
...
Quoted 8 lines
Quoted 2 lines
- An unknown tag out of any node (i.e located before the first
FDT_BEGIN_NODEor after the lastFDT_END_NODEis a global tag^ missing ')'Quoted 1 line
related to the dtb itself.
Out of curiosity, is there a real use case for global tags after
FDT_END_NODE?
Well what could be use cases in the future?
We talk about unknown tag and nothing prevent an unknown tag to be after the last FDT_END_NODE tag in the future.
In my RFC series adding support for addons, I added FDT_IMPORT_SYM tags at the end of the addon dtb and so a global tags were available after a FDT_END_NODE tag.
In the end of the commit log introducing FDT_IMPORT_SYM tags [0], the location of those tags is mentioned:
--- 8< ---
If FDT_IMPORT_SYM tags are present in the dtb, they are present after the root node definition (i.e. after the FDT_END_NODE related to the first FDT_BEGIN_NODE).
--- 8< ---
Also in tests related to import symbols [0], you can have a look look at the tests/metadata_importsyms.dtb.expect file and you will find:
--- 8< ---
--- /dev/null
+++ b/tests/metadata_importsyms.dtb.expect
@@ -0,0 +1,8 @@
+/dts-v1/;
+/addon/;
+
+/ {
+ prop = <0x00000001>;
+};
+// [FDT_IMPORT_SYM] 'base_a' (foo,bar)
+// [FDT_IMPORT_SYM] 'base_b' (foo,baz)--- 8< ---
This is the expected result when the metadata_importsyms.dtb is dumped using fdtdump.
fdtdump dumps a dtb in a linear way starting from the beginning to the end of file.
The FDT_END_NODE tag is represented by the '};' sequence (end of node). FDT_IMPORT_SYM tags are present after the end of node and so between the FDT_END_NODE tag and the FDT_END tag.
Not sure I will keep those tags at the end of dtb when I rework the series on top of "structured tags" but well, this was a real use case.
[0] https://lore.kernel.org/devicetree-compiler/[email protected]/
[1] https://lore.kernel.org/devicetree-compiler/[email protected]/
Best regards,
Hervé
RFC PATCH 15/15 Introduce v18 dtb version
In reply to Herve CodinaThis v18 version will add support for
- Structured tags.
Those tags value definition will allow old libfdt, dtc and other tools to skip unknown tags if encountered in future dtb version.
- dt_flags header field.
For now this flag field is set to 0. It is a placeholder for future dtb version and could be used to store some dtb related information such as the kind of dtb.
- last_comp_version_w header field.
This field is similar to last_comp_version but for writing. It contains the lowest version of the devicetree data structure with which the version used can safely perform modifications (taking into account following rules related to unknown tags).
If this lowest version is greater than the last known supported version, modification are simply forbidden and lead to a FDT_ERR_BADVERSION error.
For modification, when an unknown tag that can be skipped is involved and last_comp_version_w allows modifications, the following rules applies:
- When a property is modified, tags related to this property are removed and the dtb version is downgraded.
- When a property is removed, tags related to this property are obviously removed. The dtb version is kept unchanged.
- When a property or a node is added, obviously no unknown tags are added and the dtb version is kept unchanged.
- When a node is removed, tags related to this node are obviously removed. The dtb version is kept unchanged.
- Adding, removing or modifying a property is not considered as a node modification and so, those operations have no impacts on unknown tags related to the node. Those node related tags are kept unchanged.
- The only modification considered as a node modification is setting its name. We consider that this operation has no impact on tags related to the node. Here also, those node related tags and the dtb version are kept unchanged.
- Global (dtb related) unknown tags are kept unchanged regardless the modification done.
In all cases, if unknown tags are not involved in a modification, the dtb version is not downgraded when the modification is node.
Compared to previous version, it is worth noting that the dtb is not downgrade for all modification but only when unknown tags are removed due a property modification.
--- dtc.h | 2 +- fdtdump.c | 8 +++- flattree.c | 37 +++++++++++++++---- libfdt/fdt.h | 5 +++ libfdt/fdt_rw.c | 20 +++++++--- libfdt/fdt_sw.c | 3 ++ libfdt/libfdt.h | 7 +++- pylibfdt/libfdt.i | 18 +++++++++ tests/dumptrees.c | 3 +- tests/pylibfdt_tests.py | 10 +++-- tests/run_tests.sh | 26 +++++++++---- tests/testdata.h | 1 + tests/testutils.c | 2 +- tests/trees.S | 29 +++++++++++++-- ...own_tags_can_skip.fdtput.test.dtb.0.expect | 1 + ...own_tags_can_skip.fdtput.test.dtb.1.expect | 3 +- ...own_tags_can_skip.fdtput.test.dtb.2.expect | 3 +- ...own_tags_can_skip.fdtput.test.dtb.3.expect | 3 +- ...own_tags_can_skip.fdtput.test.dtb.4.expect | 3 +- ...own_tags_can_skip.fdtput.test.dtb.5.expect | 3 +- ...own_tags_can_skip.fdtput.test.dtb.6.expect | 3 +- 21 files changed, 152 insertions(+), 38 deletions(-)
dtc.h +1 -1
@@ -29,7 +29,7 @@#define debug (...)#endif -#define DEFAULT_FDT_VERSION 17 +#define DEFAULT_FDT_VERSION 18/* * Command line options
fdtdump.c +7 -1
@@ -18,7 +18,7 @@#include "util.h" #define FDT_MAGIC_SIZE 4 -#define MAX_VERSION 17U +#define MAX_VERSION 18U#define ALIGN (x, a) (((x) + ((a) - 1)) & ~((a) - 1))#define PALIGN (p, a) ((void *)(ALIGN((uintptr_t)(p), (a))))
@@ -86,6 +86,12 @@ static void dump_blob(void *blob, bool debug, int dump_unknown)if (version >=17 )printf ("// size_dt_struct:\t0x%" PRIx32"\n" ,fdt32_to_cpu (bph->size_dt_struct)); +if (version >=18 ) { +printf ("// dt_flags:\t\t0x%" PRIx32"\n" , +fdt32_to_cpu (bph->dt_flags)); +printf ("// last_comp_version_w:\t%" PRIu32"\n" , +fdt32_to_cpu (bph->last_comp_version_w)); + }printf ("\n" );for (i =0 ; ; i++) {
flattree.c +30 -7
@@ -13,23 +13,29 @@#define FTF_STRTABSIZE 0x10#define FTF_STRUCTSIZE 0x20#define FTF_NOPS 0x40 +#define FTF_DTFLAGS 0x80 +#define FTF_LCVERSW 0x100static struct version_info {int version;int last_comp_version; +int last_comp_version_w;int hdr_size;int flags; } version_table[] = { - {1 ,1 ,FDT_V1_SIZE , + {1 ,1 ,0 ,FDT_V1_SIZE ,FTF_FULLPATH |FTF_VARALIGN |FTF_NAMEPROPS }, - {2 ,1 ,FDT_V2_SIZE , + {2 ,1 ,0 ,FDT_V2_SIZE ,FTF_FULLPATH |FTF_VARALIGN |FTF_NAMEPROPS |FTF_BOOTCPUID }, - {3 ,1 ,FDT_V3_SIZE , + {3 ,1 ,0 ,FDT_V3_SIZE ,FTF_FULLPATH |FTF_VARALIGN |FTF_NAMEPROPS |FTF_BOOTCPUID |FTF_STRTABSIZE }, - {16 ,16 ,FDT_V3_SIZE , + {16 ,16 ,0 ,FDT_V3_SIZE ,FTF_BOOTCPUID |FTF_STRTABSIZE |FTF_NOPS }, - {17 ,16 ,FDT_V17_SIZE , + {17 ,16 ,0 ,FDT_V17_SIZE ,FTF_BOOTCPUID |FTF_STRTABSIZE |FTF_STRUCTSIZE |FTF_NOPS }, + {18 ,16 ,17 ,FDT_V18_SIZE , +FTF_BOOTCPUID |FTF_STRTABSIZE |FTF_STRUCTSIZE |FTF_NOPS |FTF_DTFLAGS | +FTF_LCVERSW }, };struct emitter {
@@ -314,7 +320,7 @@ static struct data flatten_reserve_list(struct reserve_info *reservelist,static void make_fdt_header (struct fdt_header *fdt,struct version_info *vi,int reservesize,int dtsize,int strsize, -int boot_cpuid_phys) +int boot_cpuid_phys,uint32_t dt_flags) {int reserve_off;
@@ -341,6 +347,10 @@ static void make_fdt_header(struct fdt_header *fdt, fdt->size_dt_strings =cpu_to_fdt32 (strsize);if (vi->flags &FTF_STRUCTSIZE ) fdt->size_dt_struct =cpu_to_fdt32 (dtsize); +if (vi->flags &FTF_DTFLAGS ) + fdt->dt_flags =cpu_to_fdt32 (dt_flags); +if (vi->flags &FTF_LCVERSW ) + fdt->last_comp_version_w =cpu_to_fdt32 (vi->last_comp_version_w); }void dt_to_blob (FILE *f,struct dt_info *dti,int version)
@@ -368,7 +378,7 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version)/* Make header */ make_fdt_header (&fdt, vi, reservebuf.len, dtbuf.len, strbuf.len, - dti->boot_cpuid_phys); + dti->boot_cpuid_phys,0 );/* * If the user asked for more space than is used, adjust the totalsize.
@@ -499,6 +509,16 @@ void dt_to_asm(FILE *f, struct dt_info *dti, int version) symprefix, symprefix); } +if (vi->flags &FTF_DTFLAGS ) { +fprintf (f,"\t/* dt_flags */\n" ); +asm_emit_cell (f,0 ); + } + +if (vi->flags &FTF_LCVERSW ) { +fprintf (f,"\t/* last_comp_version_w */\n" ); +asm_emit_cell (f, vi->last_comp_version_w); + } +/* * Reserve map entries. * Align the reserve map to a doubleword boundary.
@@ -955,6 +975,9 @@ struct dt_info *dt_from_blob(const char *fname) flags |=FTF_NOPS ; } +if (version >=18 ) + flags |=FTF_DTFLAGS |FTF_LCVERSW ; +inbuf_init (&memresvbuf, blob + off_mem_rsvmap, blob + totalsize);inbuf_init (&dtbuf, blob + off_dt, blob + totalsize);
libfdt/fdt.h +5 -0
@@ -26,6 +26,10 @@ struct fdt_header {/* version 17 fields below */ fdt32_t size_dt_struct;/* size of the structure block */ + +/* version 18 fields below */ +fdt32_t dt_flags;/* Ored value of FDT_FLAG_XXXX */ +fdt32_t last_comp_version_w;/* last compatible version for writing */ };struct fdt_reserve_entry {
@@ -85,5 +89,6 @@ struct fdt_property {#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))#define FDT_V16_SIZE FDT_V3_SIZE#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) +#define FDT_V18_SIZE (FDT_V17_SIZE + 2 * sizeof(fdt32_t))#endif /* FDT_H */
libfdt/fdt_rw.c +15 -5
@@ -34,13 +34,17 @@ static int fdt_rw_probe_(void *fdt)return 0 ;FDT_RO_PROBE (fdt); -if (!can_assume (LATEST ) &&fdt_version (fdt) <17 ) -return -FDT_ERR_BADVERSION ; +if (!can_assume (LATEST )) { +if (fdt_version (fdt) <17 ) +return -FDT_ERR_BADVERSION ; +else if (fdt_version (fdt) >=18 && +fdt_last_comp_version_w (fdt) >FDT_LAST_SUPPORTED_VERSION ) +return -FDT_ERR_BADVERSION ; + }if (fdt_blocks_misordered_ (fdt,sizeof (struct fdt_reserve_entry ),fdt_size_dt_struct (fdt)))return -FDT_ERR_BADLAYOUT ; -fdt_downgrade_version (fdt);return 0 ; }
@@ -582,7 +586,11 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize) err =fdt_move (fdt, buf, bufsize);if (err)return err; -fdt_set_version (buf,17 ); +if (can_assume (LATEST ) ||fdt_version (fdt) <18 ) { +fdt_set_version (buf,18 ); +fdt_set_dt_flags (buf,0 ); +fdt_set_last_comp_version_w (buf,17 ); + }fdt_set_size_dt_struct (buf, struct_size);fdt_set_totalsize (buf, bufsize);return 0 ;
@@ -611,8 +619,10 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize)fdt_set_magic (buf,FDT_MAGIC );fdt_set_totalsize (buf, bufsize); -fdt_set_version (buf,17 ); +fdt_set_version (buf,18 );fdt_set_last_comp_version (buf,16 ); +fdt_set_dt_flags (buf,0 ); +fdt_set_last_comp_version_w (buf,17 );fdt_set_boot_cpuid_phys (buf,fdt_boot_cpuid_phys (fdt));return 0 ;
libfdt/fdt_sw.c +3 -0
@@ -137,6 +137,9 @@ int fdt_create_with_flags(void *buf, int bufsize, uint32_t flags)fdt_set_off_dt_struct (fdt,fdt_off_mem_rsvmap (fdt));fdt_set_off_dt_strings (fdt,0 ); +fdt_set_dt_flags (fdt,0 ); +fdt_set_last_comp_version_w (fdt,FDT_LAST_COMPATIBLE_VERSION_W ); +return 0 ; }
libfdt/libfdt.h +6 -1
@@ -15,7 +15,8 @@ extern "C" {#define FDT_FIRST_SUPPORTED_VERSION 0x02#define FDT_LAST_COMPATIBLE_VERSION 0x10 -#define FDT_LAST_SUPPORTED_VERSION 0x11 +#define FDT_LAST_COMPATIBLE_VERSION_W 0x11 +#define FDT_LAST_SUPPORTED_VERSION 0x12/* Error codes: informative error codes */ #define FDT_ERR_NOTFOUND 1
@@ -284,6 +285,8 @@ int fdt_next_subnode(const void *fdt, int offset);#define fdt_boot_cpuid_phys (fdt) (fdt_get_header(fdt, boot_cpuid_phys))#define fdt_size_dt_strings (fdt) (fdt_get_header(fdt, size_dt_strings))#define fdt_size_dt_struct (fdt) (fdt_get_header(fdt, size_dt_struct)) +#define fdt_dt_flags (fdt) (fdt_get_header(fdt, dt_flags)) +#define fdt_last_comp_version_w (fdt) (fdt_get_header(fdt, last_comp_version_w))#define fdt_set_hdr_ (name) \ static inline void fdt_set_##name(void *fdt, uint32_t val) \
@@ -301,6 +304,8 @@ fdt_set_hdr_(last_comp_version) fdt_set_hdr_(boot_cpuid_phys) fdt_set_hdr_(size_dt_strings) fdt_set_hdr_(size_dt_struct) +fdt_set_hdr_(dt_flags) +fdt_set_hdr_(last_comp_version_w) #undef fdt_set_hdr_/**
pylibfdt/libfdt.i +18 -0
@@ -281,6 +281,22 @@ class FdtRo(object): """ return fdt_size_dt_struct(self._fdt) + def dt_flags(self): + """Return flags from the header + + Returns: + flags value from the header + """ + return fdt_dt_flags(self._fdt) + + def last_comp_version_w(self): + """Return the last compatible version for writing of the device tree + + Returns: + Last compatible version number for writing of the device tree + """ + return fdt_last_comp_version_w(self._fdt) + def num_mem_rsv(self, quiet=()): """Return the number of memory reserve-map records
@@ -1215,6 +1231,8 @@ uint32_t fdt_last_comp_version(const void *fdt); uint32_t fdt_boot_cpuid_phys(const void *fdt); uint32_t fdt_size_dt_strings(const void *fdt); uint32_t fdt_size_dt_struct(const void *fdt); +uint32_t fdt_dt_flags(const void *fdt); +uint32_t fdt_last_comp_version_w(const void *fdt); int fdt_property_string(void *fdt, const char *name, const char *val); int fdt_property_cell(void *fdt, const char *name, uint32_t val);
tests/dumptrees.c +2 -1
@@ -27,7 +27,8 @@ static struct {TREE (two_roots),TREE (named_root),TREE (unknown_tags_can_skip), - TREE(unknown_tags_no_skip) +TREE (unknown_tags_no_skip), + TREE(last_comp_version_w_future) };#define NUM_TREES (sizeof(trees) / sizeof(trees[0]))
tests/pylibfdt_tests.py +6 -4
@@ -285,14 +285,16 @@ class PyLibfdtBasicTests(unittest.TestCase): """Test that we can access the header values""" self.assertEqual(self.fdt.magic(), 0xd00dfeed) self.assertEqual(self.fdt.totalsize(), len(self.fdt._fdt)) - self.assertEqual(self.fdt.off_dt_struct(), 88) - self.assertEqual(self.fdt.off_dt_strings(), 652) - self.assertEqual(self.fdt.off_mem_rsvmap(), 40) - self.assertEqual(self.fdt.version(), 17) + self.assertEqual(self.fdt.off_dt_struct(), 96) + self.assertEqual(self.fdt.off_dt_strings(), 660) + self.assertEqual(self.fdt.off_mem_rsvmap(), 48) + self.assertEqual(self.fdt.version(), 18) self.assertEqual(self.fdt.last_comp_version(), 16) self.assertEqual(self.fdt.boot_cpuid_phys(), 0) self.assertEqual(self.fdt.size_dt_strings(), 105) self.assertEqual(self.fdt.size_dt_struct(), 564) + self.assertEqual(self.fdt.dt_flags(), 0) + self.assertEqual(self.fdt.last_comp_version_w(), 17) def testPack(self): """Test that we can pack the tree after deleting something"""
tests/run_tests.sh +18 -8
@@ -576,7 +576,8 @@ libfdt_tests () { run_test dtbs_equal_ordered cell-overflow.test.dtb cell-overflow-results.test.dtb # check full tests - for good in test_tree1.dtb unknown_tags_can_skip.dtb; do + for good in test_tree1.dtb unknown_tags_can_skip.dtb \ + last_comp_version_w_future.dtb; do run_test check_full $good done for bad in truncated_property.dtb truncated_string.dtb \
@@ -1044,32 +1045,41 @@ fdtput_tests () { # TODO: Add tests for verbose mode? + # Not allowed to modify a dtb due to last_comp_version_w + # FDT_ERR_BADVERSION error code is returned + dtb=last_comp_version_w_future.fdtput.test.dtb + cp last_comp_version_w_future.dtb $dtb + run_wrap_error_test $DTPUT $dtb /subnode prop-int -tu 123 + run_wrap_error_test $DTPUT $dtb -d /subnode prop-int + run_wrap_error_test $DTPUT $dtb -c /new-node + run_wrap_error_test $DTPUT $dtb -r /subnode + # Modify a dtb containing some "unknown" tags that can be skipped dtb=unknown_tags_can_skip.fdtput.test.dtb cp unknown_tags_can_skip.dtb $dtb base_run_test wrap_fdtdump $dtb $dtb.0.out # Remove unneeded header fields (keep those related to versions) - sed -i '/^\/.*\(magic\|off\|size\|cpu\)/d' $dtb.0.out + sed -i '/^\/.*\(magic\|off\|size\|cpu\|flags\)/d' $dtb.0.out base_run_test check_diff $dtb.0.out "$SRCDIR/$dtb.0.expect" run_fdtput_test "vwxyz" $dtb / prop-str -ts "vwxyz" base_run_test wrap_fdtdump $dtb $dtb.1.out # Remove unneeded header fields (keep those related to versions) - sed -i '/^\/.*\(magic\|off\|size\|cpu\)/d' $dtb.1.out + sed -i '/^\/.*\(magic\|off\|size\|cpu\|flags\)/d' $dtb.1.out base_run_test check_diff $dtb.1.out "$SRCDIR/$dtb.1.expect" cp unknown_tags_can_skip.dtb $dtb run_wrap_test $DTPUT $dtb -c /tst-fdtput base_run_test wrap_fdtdump $dtb $dtb.2.out # Remove unneeded header fields (keep those related to versions) - sed -i '/^\/.*\(magic\|off\|size\|cpu\)/d' $dtb.2.out + sed -i '/^\/.*\(magic\|off\|size\|cpu\|flags\)/d' $dtb.2.out base_run_test check_diff $dtb.2.out "$SRCDIR/$dtb.2.expect" run_wrap_test $DTPUT $dtb -c /tst-fdtput/n1 /tst-fdtput/n2 /tst-fdtput/n3 run_wrap_test $DTPUT $dtb -r /tst-fdtput/n1 /tst-fdtput/n3 run_fdtget_test "n2" $dtb -l /tst-fdtput base_run_test wrap_fdtdump $dtb $dtb.3.out # Remove unneeded header fields (keep those related to versions) - sed -i '/^\/.*\(magic\|off\|size\|cpu\)/d' $dtb.3.out + sed -i '/^\/.*\(magic\|off\|size\|cpu\|flags\)/d' $dtb.3.out base_run_test check_diff $dtb.3.out "$SRCDIR/$dtb.3.expect" cp unknown_tags_can_skip.dtb $dtb
@@ -1077,21 +1087,21 @@ fdtput_tests () { run_fdtget_test "prop-int" $dtb -p / base_run_test wrap_fdtdump $dtb $dtb.4.out # Remove unneeded header fields (keep those related to versions) - sed -i '/^\/.*\(magic\|off\|size\|cpu\)/d' $dtb.4.out + sed -i '/^\/.*\(magic\|off\|size\|cpu\|flags\)/d' $dtb.4.out base_run_test check_diff $dtb.4.out "$SRCDIR/$dtb.4.expect" cp unknown_tags_can_skip.dtb $dtb run_wrap_test $DTPUT $dtb /subnode2 prop-tst-fdtput -ts "Test fdtput" base_run_test wrap_fdtdump $dtb $dtb.5.out # Remove unneeded header fields (keep those related to versions) - sed -i '/^\/.*\(magic\|off\|size\|cpu\)/d' $dtb.5.out + sed -i '/^\/.*\(magic\|off\|size\|cpu\|flags\)/d' $dtb.5.out base_run_test check_diff $dtb.5.out "$SRCDIR/$dtb.5.expect" cp unknown_tags_can_skip.dtb $dtb run_wrap_test $DTPUT $dtb -r /subnode2/subsubnode base_run_test wrap_fdtdump $dtb $dtb.6.out # Remove unneeded header fields (keep those related to versions) - sed -i '/^\/.*\(magic\|off\|size\|cpu\)/d' $dtb.6.out + sed -i '/^\/.*\(magic\|off\|size\|cpu\|flags\)/d' $dtb.6.out base_run_test check_diff $dtb.6.out "$SRCDIR/$dtb.6.expect" }
tests/testdata.h +1 -0
@@ -59,4 +59,5 @@ extern struct fdt_header two_roots;extern struct fdt_header named_root;extern struct fdt_header unknown_tags_can_skip;extern struct fdt_header unknown_tags_no_skip; +extern struct fdt_header last_comp_version_w_future;#endif /* ! __ASSEMBLER__ */
tests/testutils.c +1 -1
@@ -344,7 +344,7 @@ void *open_blob_rw(const void *blob) {int err;void *buf; -int newsize =fdt_totalsize (blob) +8 ; +int newsize =fdt_totalsize (blob) +8 +2 *4 ; buf =xmalloc (newsize); err =fdt_open_into (blob, buf, newsize);
tests/trees.S +26 -3
@@ -8,7 +8,7 @@ .byte (\val) & 0xff .endm - .macro treehdr_vers tree vers last_comp_vers + .macro treehdr_vers tree vers last_comp_vers last_comp_vers_w .balign 8 .globl \tree \tree :
@@ -22,10 +22,12 @@ fdtlong 0 fdtlong (\tree\()_strings_end - \tree\()_strings) fdtlong (\tree\()_struct_end - \tree\()_struct) + fdtlong 0 + fdtlong \last_comp_vers_w .endm .macro treehdr tree - treehdr_vers \tree 0x11 0x10 + treehdr_vers \tree 0x12 0x10 0x11 .endm .macro rsvmape addrh, addrl, lenh, lenl
@@ -334,7 +336,7 @@ named_root_end: * Use a really future dtb version to check version downgrade on * modification. */ - treehdr_vers unknown_tags_can_skip 0xffffffff 0x10 + treehdr_vers unknown_tags_can_skip 0xffffffff 0x10 0x11 empty_rsvmap unknown_tags_can_skip unknown_tags_can_skip_struct:
@@ -438,3 +440,24 @@ unknown_tags_no_skip_strings: unknown_tags_no_skip_strings_end: unknown_tags_no_skip_end: + + + /* Tree with last_comp_version_w set to avoid any modifications */ + treehdr_vers last_comp_version_w_future 0xffffffff 0x10 0xffffffff + empty_rsvmap last_comp_version_w_future + +last_comp_version_w_future_struct: + beginn "" + propu64 last_comp_version_w_future, prop_int, 1, 2 + beginn "subnode" + propu64 last_comp_version_w_future, prop_int, 1, 2 + endn + endn + fdtlong FDT_END +last_comp_version_w_future_struct_end: + +last_comp_version_w_future_strings: + string last_comp_version_w_future, prop_int, "prop-int" +last_comp_version_w_future_strings_end: + +last_comp_version_w_future_end:
tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect +1 -0
@@ -1,6 +1,7 @@ /dts-v1/; // version: 4294967295 // last_comp_version: 16 +// last_comp_version_w: 17 // Unknown tag ignored: 0xd0000000, data lng 4 00000001 / {
tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect +2 -1
@@ -1,6 +1,7 @@ /dts-v1/; -// version: 17 +// version: 18 // last_comp_version: 16 +// last_comp_version_w: 17 // Unknown tag ignored: 0xd0000000, data lng 4 00000001 / {
tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect +2 -1
@@ -1,6 +1,7 @@ /dts-v1/; -// version: 17 +// version: 4294967295 // last_comp_version: 16 +// last_comp_version_w: 17 // Unknown tag ignored: 0xd0000000, data lng 4 00000001 / {
tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect +2 -1
@@ -1,6 +1,7 @@ /dts-v1/; -// version: 17 +// version: 4294967295 // last_comp_version: 16 +// last_comp_version_w: 17 // Unknown tag ignored: 0xd0000000, data lng 4 00000001 / {
tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect +2 -1
@@ -1,6 +1,7 @@ /dts-v1/; -// version: 17 +// version: 4294967295 // last_comp_version: 16 +// last_comp_version_w: 17 // Unknown tag ignored: 0xd0000000, data lng 4 00000001 / {
tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect +2 -1
@@ -1,6 +1,7 @@ /dts-v1/; -// version: 17 +// version: 4294967295 // last_comp_version: 16 +// last_comp_version_w: 17 // Unknown tag ignored: 0xd0000000, data lng 4 00000001 / {
tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect +2 -1
@@ -1,6 +1,7 @@ /dts-v1/; -// version: 17 +// version: 4294967295 // last_comp_version: 16 +// last_comp_version_w: 17 // Unknown tag ignored: 0xd0000000, data lng 4 00000001 / {
Signature · 1 line
2.52.0
Re: [RFC PATCH 15/15] Introduce v18 dtb version
Quoted 4 lines · Herve Codina
This v18 version will add support for
- Structured tags.
Those tags value definition will allow old libfdt, dtc and other tools to skip unknown tags if encountered in future dtb version.
"old" seems to imply that versions released before today will be able to wkip unknown tags. I think this should be clarified along the lines of:
libfdt, dtc and other tools implementing version v18 will be able to wkip unknown tags in dtbs generated with later versions of dtc
Quoted 4 lines
-
dt_flagsheader field.
For now this flag field is set to 0. It is a placeholder for future dtb version and could be used to store some dtb related information such as the kind of dtb.
Is this intended for DT addons?
You may mention a realistiv use case here.
Quoted 10 lines
-
last_comp_version_wheader field.
This field is similar tolast_comp_versionbut for writing. It contains the lowest version of the devicetree data structure with which the version used can safely perform modifications (taking into account following rules related to unknown tags).
If this lowest version is greater than the last known supported version, modification are simply forbidden and lead to aFDT_ERR_BADVERSIONerror.
Quoted 3 lines
For modification, when an unknown tag that can be skipped is involved and
last_comp_version_wallows modifications, the following rules applies:
^
apply
Quoted 22 lines
- When a property is modified, tags related to this property are removed and the dtb version is downgraded.
- When a property is removed, tags related to this property are obviously removed. The dtb version is kept unchanged.
- When a property or a node is added, obviously no unknown tags are added and the dtb version is kept unchanged.
- When a node is removed, tags related to this node are obviously removed. The dtb version is kept unchanged.
- Adding, removing or modifying a property is not considered as a node modification and so, those operations have no impacts on unknown tags related to the node. Those node related tags are kept unchanged.
- The only modification considered as a node modification is setting its name. We consider that this operation has no impact on tags related to the node. Here also, those node related tags and the dtb version are kept unchanged.
Quoted 5 lines
- Global (dtb related) unknown tags are kept unchanged regardless the modification done.
In all cases, if unknown tags are not involved in a modification, the dtb version is not downgraded when the modification is node.
^ made?
Quoted 2 lines
Compared to previous version, it is worth noting that the dtb is not
^ "dtb version"
Quoted 1 line
downgrade for all modification but only when unknown tags are removed
^
downgraded for any
Quoted 1 line
due a property modification.
^
"due to a ..."
I'm not sure I got what you mean by the initial "Compared to previous version". Version(s) of what?
If I just remove those 4 words the sentence seems OK to me BTW.
Quoted 1 line
Signed-off-by Herve Codina <[email protected]>
I am unable to fully understand the implications of this patch, but the code seems OK to me as far as I can tell.
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Re: [RFC PATCH 15/15] Introduce v18 dtb version
Hi Luca,
Quoted 11 lines · On Wed, 01 Apr 2026 17:19:09 +0200 "Luca Ceresoli" <[email protected]>…
Quoted 4 lines · Herve Codina
This v18 version will add support for
- Structured tags.
Those tags value definition will allow old libfdt, dtc and other tools to skip unknown tags if encountered in future dtb version."old" seems to imply that versions released before today will be able to wkip unknown tags. I think this should be clarified along the lines of:
libfdt, dtc and other tools implementing version v18 will be able to wkip unknown tags in dtbs generated with later versions of dtc
Yes, I will add this clarification in the next iteration.
Quoted 9 lines
Quoted 5 lines
-
dt_flagsheader field.
For now this flag field is set to 0. It is a placeholder for future dtb version and could be used to store some dtb related information such as the kind of dtb.Is this intended for DT addons?
You may mention a realistiv use case here.
Intended, maybe not. Used by addons, yes, for sure.
What do you think if I add the following:
For instance, the future addons format will use this field to clearly identify that the dtb is an addon dtb.
...
Quoted 10 lines
Quoted 2 lines
Compared to previous version, it is worth noting that the dtb is not
^ "dtb version"Quoted 1 line
downgrade for all modification but only when unknown tags are removed
^
downgraded for any
Quoted 8 lines
Quoted 1 line
due a property modification.
^
"due to a ..."I'm not sure I got what you mean by the initial "Compared to previous version". Version(s) of what?
If I just remove those 4 words the sentence seems OK to me BTW.
Is the following clearer?
It is worth noting that with this v18 version, the dtb version is not downgraded for any modification but only when unknown tags are removed due to a property modification. In v17 or older version any modification led to a dtb version downgrade.
Best regards,
Hervé
Re: [RFC PATCH 15/15] Introduce v18 dtb version
Quoted 27 lines · Herve Codina
Hi Luca,
Quoted 11 lines · On Wed, 01 Apr 2026 17:19:09 +0200 "Luca Ceresoli" <[email protected]>…
Quoted 4 lines · Herve Codina
This v18 version will add support for
- Structured tags.
Those tags value definition will allow old libfdt, dtc and other tools to skip unknown tags if encountered in future dtb version."old" seems to imply that versions released before today will be able to wkip unknown tags. I think this should be clarified along the lines of:
libfdt, dtc and other tools implementing version v18 will be able to wkip unknown tags in dtbs generated with later versions of dtc
Yes, I will add this clarification in the next iteration.
Quoted 8 lines
Quoted 5 lines
-
dt_flagsheader field.
For now this flag field is set to 0. It is a placeholder for future dtb version and could be used to store some dtb related information such as the kind of dtb.Is this intended for DT addons?
Quoted 7 lines
Quoted 1 line
You may mention a realistiv use case here.
Intended, maybe not. Used by addons, yes, for sure.
What do you think if I add the following:
For instance, the future addons format will use this field to clearly identify that the dtb is an addon dtb.
This clarifies a lot to me, so I think it can be added to the commit message.
Quoted 16 lines
Quoted 16 lines
Quoted 1 line
Compared to previous version, it is worth noting that the dtb is not
^ "dtb version"Quoted 1 line
downgrade for all modification but only when unknown tags are removed
^
downgraded for anyQuoted 1 line
due a property modification.
^
"due to a ..."I'm not sure I got what you mean by the initial "Compared to previous version". Version(s) of what?
Quoted 8 lines
Quoted 1 line
If I just remove those 4 words the sentence seems OK to me BTW.
Is the following clearer?
It is worth noting that with this v18 version, the dtb version is not downgraded for any modification but only when unknown tags are removed due to a property modification. In v17 or older version any modification led to a dtb version downgrade.
Yes, totally clear now, thanks!
Luca
--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Hi David,
Quoted 31 lines · On Tue, 10 Feb 2026 18:33:28 +0100 Herve Codina <[email protected]>…
Recently, I sent a RFC series related to support for metadata and addon device-trees [1].
During the discussion the concept of structured tags and "unknown" tags emerged as well as the need to have them handled as a prerequisite of support for metadata and addon.
The conclusion was the need for a new dtb version (v18) with support for:
- Structured tags and based on them, "unknown" tags.
Those structured tags allow to have an standardized definition of tags with the capability of skipping a tag and its related data when a "unknown" tag is incountered by a given version of libfdt, dtc and tools. Those "unknown" tags are tags defined in future versions. Even if they exact meaning is unknown for an 'old' version, they structure is understood and the 'old' version can skip them without any errors if allowed.- Flags in the dtb header (
dt_flags).
The goal of this field is to have a placeholder to specify the type of dtb we are dealing with. For instance, addons dtb will set a flag in this placeholder- A last compatible version for writing purpose.
The goal of the new dtb header field (last_comp_version_w) is to disable globally any modification. It works similarly tolast_comp_versionbut for modification. It can be used to avoid any modification that could be done by an 'old' version and could lead to inconsistencies between the modification itself and some "unknown" tags.
Quoted 2 lines
This RFC series implements those features and leads to the v18 dtb version.
I sent this series a month ago.
I know some patches have been applied but what's the plan for the remaining ones?
Is there anything I can do to help move things forward?
Let me know if you want some rebase and/or modification and so a new version of the series or if you need anything else that can help in moving forward.
Best regards,
Hervé
Quoted 36 lines · Herve Codina
Hi David,
Quoted 31 lines · On Tue, 10 Feb 2026 18:33:28 +0100 Herve Codina <[email protected]>…
Recently, I sent a RFC series related to support for metadata and addon device-trees [1].
During the discussion the concept of structured tags and "unknown" tags emerged as well as the need to have them handled as a prerequisite of support for metadata and addon.
The conclusion was the need for a new dtb version (v18) with support for:
- Structured tags and based on them, "unknown" tags.
Those structured tags allow to have an standardized definition of tags with the capability of skipping a tag and its related data when a "unknown" tag is incountered by a given version of libfdt, dtc and tools. Those "unknown" tags are tags defined in future versions. Even if they exact meaning is unknown for an 'old' version, they structure is understood and the 'old' version can skip them without any errors if allowed.- Flags in the dtb header (
dt_flags).
The goal of this field is to have a placeholder to specify the type of dtb we are dealing with. For instance, addons dtb will set a flag in this placeholder- A last compatible version for writing purpose.
The goal of the new dtb header field (last_comp_version_w) is to disable globally any modification. It works similarly tolast_comp_versionbut for modification. It can be used to avoid any modification that could be done by an 'old' version and could lead to inconsistencies between the modification itself and some "unknown" tags.
Quoted 7 lines
Quoted 2 lines
This RFC series implements those features and leads to the v18 dtb version.
I sent this series a month ago.
I know some patches have been applied but what's the plan for the remaining ones?
Get to them as time permits, which I do not expect to be soon.
Quoted 1 line
Is there anything I can do to help move things forward?
Usher forth a world where small projects get sufficient support to have apprentice maintainers? Other than that, not really.
Quoted 2 lines
Let me know if you want some rebase and/or modification and so a new version of the series or if you need anything else that can help in moving forward.
I will when I can.
Signature · 4 lines
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
Hi David,
On Thu, 12 Mar 2026 21:21:45 +1100
David Gibson <[email protected]> wrote:
...
Quoted 8 lines
Quoted 3 lines
I know some patches have been applied but what's the plan for the remaining ones?
Get to them as time permits, which I do not expect to be soon.
Quoted 1 line
Is there anything I can do to help move things forward?
Quoted 8 lines
Usher forth a world where small projects get sufficient support to have apprentice maintainers? Other than that, not really.
Quoted 2 lines
Let me know if you want some rebase and/or modification and so a new version of the series or if you need anything else that can help in moving forward.
I will when I can.
I have subscribed to the devicetree-compiler mailing list. I hope I could help you reviewing new requests and series related to dtc and libfdt.
Also I have a look at any pending patches to review, but found none.
I can offer to help on other topics to spread the workload if you want. Let me know.
Best regards,
Hervé