Re: [PATCH v2 10/10] Introduce v18 dtb version
From: Frank Li
Date: Thu Jun 04 2026 - 17:37:19 EST
On Thu, Apr 09, 2026 at 01:54:26PM +0200, Herve Codina wrote:
> 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.
>
> libfdt, dtc and other tools implementing version v18 will be able to
> skip unknown tags available in dtbd generated with later version of
> dtc.
>
> - 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. For instance, the future addons format will
> use this field to clearly identify that the dtb is an addon 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
> apply:
> - 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 made.
>
> 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.
>
> Signed-off-by: Herve Codina <herve.codina@xxxxxxxxxxx>
> ---
Reviewed-by: Frank Li <Frank.Li@xxxxxxx>
> 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(-)
>
> diff --git a/dtc.h b/dtc.h
> index 473552e..f0c2cde 100644
> --- a/dtc.h
> +++ b/dtc.h
> @@ -29,7 +29,7 @@
> #define debug(...)
> #endif
>
> -#define DEFAULT_FDT_VERSION 17
> +#define DEFAULT_FDT_VERSION 18
>
> /*
> * Command line options
> diff --git a/fdtdump.c b/fdtdump.c
> index 7a8b278..5994e1d 100644
> --- a/fdtdump.c
> +++ b/fdtdump.c
> @@ -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++) {
> diff --git a/flattree.c b/flattree.c
> index 88dbfa7..ff57fc5 100644
> --- a/flattree.c
> +++ b/flattree.c
> @@ -13,23 +13,29 @@
> #define FTF_STRTABSIZE 0x10
> #define FTF_STRUCTSIZE 0x20
> #define FTF_NOPS 0x40
> +#define FTF_DTFLAGS 0x80
> +#define FTF_LCVERSW 0x100
>
> static 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);
> diff --git a/libfdt/fdt.h b/libfdt/fdt.h
> index e6f75e7..12d358d 100644
> --- a/libfdt/fdt.h
> +++ b/libfdt/fdt.h
> @@ -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 */
> diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c
> index a8f53b4..0a4a03f 100644
> --- a/libfdt/fdt_rw.c
> +++ b/libfdt/fdt_rw.c
> @@ -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;
> diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c
> index 4c569ee..10da0d6 100644
> --- a/libfdt/fdt_sw.c
> +++ b/libfdt/fdt_sw.c
> @@ -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;
> }
>
> diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
> index 7a10f66..71c7de5 100644
> --- a/libfdt/libfdt.h
> +++ b/libfdt/libfdt.h
> @@ -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_
>
> /**
> diff --git a/pylibfdt/libfdt.i b/pylibfdt/libfdt.i
> index 1f9c047..90966bd 100644
> --- a/pylibfdt/libfdt.i
> +++ b/pylibfdt/libfdt.i
> @@ -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);
> diff --git a/tests/dumptrees.c b/tests/dumptrees.c
> index 4732fff..c05d216 100644
> --- a/tests/dumptrees.c
> +++ b/tests/dumptrees.c
> @@ -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]))
> diff --git a/tests/pylibfdt_tests.py b/tests/pylibfdt_tests.py
> index a4f73ed..72c98c5 100644
> --- a/tests/pylibfdt_tests.py
> +++ b/tests/pylibfdt_tests.py
> @@ -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"""
> diff --git a/tests/run_tests.sh b/tests/run_tests.sh
> index 1ba937d..90ba558 100755
> --- a/tests/run_tests.sh
> +++ b/tests/run_tests.sh
> @@ -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"
> }
>
> diff --git a/tests/testdata.h b/tests/testdata.h
> index aef04ab..26c7c18 100644
> --- a/tests/testdata.h
> +++ b/tests/testdata.h
> @@ -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__ */
> diff --git a/tests/testutils.c b/tests/testutils.c
> index 54da2e4..728d89c 100644
> --- a/tests/testutils.c
> +++ b/tests/testutils.c
> @@ -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);
> diff --git a/tests/trees.S b/tests/trees.S
> index ef9a175..9215f3e 100644
> --- a/tests/trees.S
> +++ b/tests/trees.S
> @@ -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:
> @@ -444,3 +446,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:
> diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect
> index 3cdf448..0d72e1f 100644
> --- a/tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect
> +++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.0.expect
> @@ -1,6 +1,7 @@
> /dts-v1/;
> // version: 4294967295
> // last_comp_version: 16
> +// last_comp_version_w: 17
>
> // Unknown tag ignored: 0xd0000000, data len 4 00000001
> / {
> diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect
> index 71f2d1c..d355d5a 100644
> --- a/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect
> +++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.1.expect
> @@ -1,6 +1,7 @@
> /dts-v1/;
> -// version: 17
> +// version: 18
> // last_comp_version: 16
> +// last_comp_version_w: 17
>
> // Unknown tag ignored: 0xd0000000, data len 4 00000001
> / {
> diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect
> index bd3a13b..69b4465 100644
> --- a/tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect
> +++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.2.expect
> @@ -1,6 +1,7 @@
> /dts-v1/;
> -// version: 17
> +// version: 4294967295
> // last_comp_version: 16
> +// last_comp_version_w: 17
>
> // Unknown tag ignored: 0xd0000000, data len 4 00000001
> / {
> diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect
> index 237eb95..bbdd1c6 100644
> --- a/tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect
> +++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.3.expect
> @@ -1,6 +1,7 @@
> /dts-v1/;
> -// version: 17
> +// version: 4294967295
> // last_comp_version: 16
> +// last_comp_version_w: 17
>
> // Unknown tag ignored: 0xd0000000, data len 4 00000001
> / {
> diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect
> index 8473040..98b0252 100644
> --- a/tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect
> +++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.4.expect
> @@ -1,6 +1,7 @@
> /dts-v1/;
> -// version: 17
> +// version: 4294967295
> // last_comp_version: 16
> +// last_comp_version_w: 17
>
> // Unknown tag ignored: 0xd0000000, data len 4 00000001
> / {
> diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect
> index f7806f4..9738793 100644
> --- a/tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect
> +++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.5.expect
> @@ -1,6 +1,7 @@
> /dts-v1/;
> -// version: 17
> +// version: 4294967295
> // last_comp_version: 16
> +// last_comp_version_w: 17
>
> // Unknown tag ignored: 0xd0000000, data len 4 00000001
> / {
> diff --git a/tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect b/tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect
> index 029f3b4..22449ae 100644
> --- a/tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect
> +++ b/tests/unknown_tags_can_skip.fdtput.test.dtb.6.expect
> @@ -1,6 +1,7 @@
> /dts-v1/;
> -// version: 17
> +// version: 4294967295
> // last_comp_version: 16
> +// last_comp_version_w: 17
>
> // Unknown tag ignored: 0xd0000000, data len 4 00000001
> / {
> --
> 2.53.0
>