From 8d15a63e84ff62fd31e8278088fa1176f2735eef Mon Sep 17 00:00:00 2001 From: David Gibson Date: Wed, 28 Jan 2026 12:03:52 +1100 Subject: [PATCH 01/35] libfdt: Verify alignment of sub-blocks in dtb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A dtb is considered malformed if its structural elements (not things within property values) are not naturally aligned. This means that the structure block must be aligned to a 32-bit boundary, the reserve map must be aligned to 64-bit boundary and the whole thing must be loaded at a 64-bit aligned address. We currently verify that lasat condition in fdt_check_header() but not the other cases. Reported-by: Owen Sanzas (Ze Sheng) 盛泽 Link: https://github.com/dgibson/dtc/issues/178 Signed-off-by: David Gibson --- libfdt/fdt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libfdt/fdt.c b/libfdt/fdt.c index 95f644c..56d4dcb 100644 --- a/libfdt/fdt.c +++ b/libfdt/fdt.c @@ -110,6 +110,14 @@ int fdt_check_header(const void *fdt) || (fdt_totalsize(fdt) > INT_MAX)) return -FDT_ERR_TRUNCATED; + /* memrsv block must be 8 byte aligned */ + if (fdt_off_mem_rsvmap(fdt) % sizeof(uint64_t)) + return -FDT_ERR_ALIGNMENT; + + /* Structure block must be 4 byte aligned */ + if (fdt_off_dt_struct(fdt) % FDT_TAGSIZE) + return -FDT_ERR_ALIGNMENT; + /* Bounds check memrsv block */ if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) From adba02caf554a213ed900e5eebb4141c6a7a830a Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 10 Feb 2026 18:33:29 +0100 Subject: [PATCH 02/35] 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. Signed-off-by: Herve Codina Message-ID: <20260210173349.636766-2-herve.codina@bootlin.com> Signed-off-by: David Gibson --- checks.c | 2 +- dtc.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/checks.c b/checks.c index 45d0213..946c142 100644 --- a/checks.c +++ b/checks.c @@ -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", diff --git a/dtc.h b/dtc.h index 7231200..473552e 100644 --- a/dtc.h +++ b/dtc.h @@ -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; From 68b960e299f7a31c986d6e5c6244a31f3dd4b8fd Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 10 Feb 2026 18:33:30 +0100 Subject: [PATCH 03/35] fdtdump: Remove dtb version check 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_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. Signed-off-by: Herve Codina Message-ID: <20260210173349.636766-3-herve.codina@bootlin.com> Signed-off-by: David Gibson --- fdtdump.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fdtdump.c b/fdtdump.c index 0260609..6c9ad90 100644 --- a/fdtdump.c +++ b/fdtdump.c @@ -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 || From 5bb5bedd347dee5d3928d71e568405b2d449f02d Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 10 Feb 2026 18:33:31 +0100 Subject: [PATCH 04/35] fdtdump: Return an error code on wrong tag value 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 Message-ID: <20260210173349.636766-4-herve.codina@bootlin.com> Signed-off-by: David Gibson --- fdtdump.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fdtdump.c b/fdtdump.c index 6c9ad90..0e7a265 100644 --- a/fdtdump.c +++ b/fdtdump.c @@ -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); } } From 5976c4a6609829861093af7c56ffd90fb8925cae Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 10 Feb 2026 18:33:32 +0100 Subject: [PATCH 05/35] libfdt: fdt_rw: Introduce fdt_downgrade_version() 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 Message-ID: <20260210173349.636766-5-herve.codina@bootlin.com> Signed-off-by: David Gibson --- libfdt/fdt_rw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c index 7475caf..90ea14e 100644 --- a/libfdt/fdt_rw.c +++ b/libfdt/fdt_rw.c @@ -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; } From caf7465c5d60a56e00ea8c6b6eeb4e443371f815 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Tue, 10 Feb 2026 18:33:35 +0100 Subject: [PATCH 06/35] libfdt: fdt_check_full: Handle FDT_NOP when FDT_END is expected fdt_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. Signed-off-by: Herve Codina Message-ID: <20260210173349.636766-8-herve.codina@bootlin.com> Signed-off-by: David Gibson --- libfdt/fdt_check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c index a21ebbc..cca0523 100644 --- a/libfdt/fdt_check.c +++ b/libfdt/fdt_check.c @@ -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) { From 53373d135579b05ba255b072539f25f0fe1edee9 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 25 Mar 2026 17:13:44 -0700 Subject: [PATCH 07/35] dtc: Remove unused dts_version in dtc-lexer.l A recent strengthening of -Wunused-but-set-variable (enabled with -Wall) in clang under a new subwarning, -Wunused-but-set-global, points out an unused static global variable in dtc-lexer.l: ../dtc-lexer.l:42:12: error: variable 'dts_version' set but not used [-Werror,-Wunused-but-set-global] 42 | static int dts_version = 1; | ^ This variable has been unused since commit 4e1a0a0 ("Remove support for the legacy DTS source file format."). Remove it to clear up the warning. Signed-off-by: Nathan Chancellor Message-ID: <20260325-dtc-lexer-remove-dts_version-v1-1-0b5d64903bbb@kernel.org> Signed-off-by: David Gibson --- dtc-lexer.l | 3 --- 1 file changed, 3 deletions(-) diff --git a/dtc-lexer.l b/dtc-lexer.l index 15d585c..1b129b1 100644 --- a/dtc-lexer.l +++ b/dtc-lexer.l @@ -39,8 +39,6 @@ extern bool treesource_error; #define DPRINT(fmt, ...) do { } while (0) #endif -static int dts_version = 1; - #define BEGIN_DEFAULT() DPRINT("\n"); \ BEGIN(V1); \ @@ -101,7 +99,6 @@ static void PRINTF(1, 2) lexical_error(const char *fmt, ...); <*>"/dts-v1/" { DPRINT("Keyword: /dts-v1/\n"); - dts_version = 1; BEGIN_DEFAULT(); return DT_V1; } From f551be7b39c83d6c0515906bf3fc44f4c0cf16ce Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 17 Apr 2026 16:23:35 +1000 Subject: [PATCH 08/35] libfdt: Standardise returns annotation in function documentation libfdt.h is divided in when it uses "Return:" and when it uses "returns:" to describe the return value in function documentation comments. Standardise on "returns:" since it is more prevalent (74 vs 19 occurrences). Signed-off-by: David Gibson --- libfdt/libfdt.h | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index 7a10f66..fd7637c 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -225,7 +225,7 @@ int fdt_next_node(const void *fdt, int offset, int *depth); * @fdt: FDT blob * @offset: Offset of node to check * - * Return: offset of first subnode, or -FDT_ERR_NOTFOUND if there is none + * returns: offset of first subnode, or -FDT_ERR_NOTFOUND if there is none */ int fdt_first_subnode(const void *fdt, int offset); @@ -237,8 +237,8 @@ int fdt_first_subnode(const void *fdt, int offset); * After first calling fdt_first_subnode(), call this function repeatedly to * get direct subnodes of a parent node. * - * Return: offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more - * subnodes + * returns: offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more + * subnodes */ int fdt_next_subnode(const void *fdt, int offset); @@ -307,7 +307,7 @@ fdt_set_hdr_(size_dt_struct) * fdt_header_size - return the size of the tree's header * @fdt: pointer to a flattened device tree * - * Return: size of DTB header in bytes + * returns: size of DTB header in bytes */ size_t fdt_header_size(const void *fdt); @@ -315,7 +315,7 @@ size_t fdt_header_size(const void *fdt); * fdt_header_size_ - internal function to get header size from a version number * @version: device tree version number * - * Return: size of DTB header in bytes + * returns: size of DTB header in bytes */ size_t fdt_header_size_(uint32_t version); @@ -462,7 +462,7 @@ static inline uint32_t fdt_get_max_phandle(const void *fdt) * highest phandle value in the device tree blob) will be returned in the * @phandle parameter. * - * Return: 0 on success or a negative error-code on failure + * returns: 0 on success or a negative error-code on failure */ int fdt_generate_phandle(const void *fdt, uint32_t *phandle); @@ -510,7 +510,7 @@ int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); * useful for finding subnodes based on a portion of a larger string, * such as a full path. * - * Return: offset of the subnode or -FDT_ERR_NOTFOUND if name not found. + * returns: offset of the subnode or -FDT_ERR_NOTFOUND if name not found. */ #ifndef SWIG /* Not available in Python */ int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, @@ -551,7 +551,7 @@ int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); * Identical to fdt_path_offset(), but only consider the first namelen * characters of path as the path name. * - * Return: offset of the node or negative libfdt error value otherwise + * returns: offset of the node or negative libfdt error value otherwise */ #ifndef SWIG /* Not available in Python */ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); @@ -749,7 +749,7 @@ static inline struct fdt_property *fdt_get_property_by_offset_w(void *fdt, * Identical to fdt_get_property(), but only examine the first namelen * characters of name for matching the property name. * - * Return: pointer to the structure representing the property, or NULL + * returns: pointer to the structure representing the property, or NULL * if not found */ #ifndef SWIG /* Not available in Python */ @@ -851,7 +851,7 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset, * Identical to fdt_getprop(), but only examine the first namelen * characters of name for matching the property name. * - * Return: pointer to the property's value or NULL on error + * returns: pointer to the property's value or NULL on error */ #ifndef SWIG /* Not available in Python */ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, @@ -924,8 +924,8 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); * Identical to fdt_get_alias(), but only examine the first @namelen * characters of @name for matching the alias name. * - * Return: a pointer to the expansion of the alias named @name, if it exists, - * NULL otherwise + * returns: a pointer to the expansion of the alias named @name, if it exists, + * NULL otherwise */ #ifndef SWIG /* Not available in Python */ const char *fdt_get_alias_namelen(const void *fdt, @@ -955,8 +955,8 @@ const char *fdt_get_alias(const void *fdt, const char *name); * Identical to fdt_get_symbol(), but only examine the first @namelen * characters of @name for matching the symbol name. * - * Return: a pointer to the expansion of the symbol named @name, if it exists, - * NULL otherwise + * returns: a pointer to the expansion of the symbol named @name, if it exists, + * NULL otherwise */ #ifndef SWIG /* Not available in Python */ const char *fdt_get_symbol_namelen(const void *fdt, @@ -1220,7 +1220,7 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, * one or more strings, each terminated by \0, as is found in a device tree * "compatible" property. * - * Return: 1 if the string is found in the list, 0 not found, or invalid list + * returns: 1 if the string is found in the list, 0 not found, or invalid list */ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); @@ -1230,7 +1230,7 @@ int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); * @nodeoffset: offset of a tree node * @property: name of the property containing the string list * - * Return: + * returns: * the number of strings in the given property * -FDT_ERR_BADVALUE if the property value is not NUL-terminated * -FDT_ERR_NOTFOUND if the property does not exist @@ -1274,7 +1274,7 @@ int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, * If non-NULL, the length of the string (on success) or a negative error-code * (on failure) will be stored in the integer pointer to by lenp. * - * Return: + * returns: * A pointer to the string at the given index in the string list or NULL on * failure. On success the length of the string will be stored in the memory * location pointed to by the lenp parameter, if non-NULL. On failure one of @@ -1364,7 +1364,7 @@ int fdt_size_cells(const void *fdt, int nodeoffset); * of the name. It is useful when you want to manipulate only one value of * an array and you have a string that doesn't end with \0. * - * Return: 0 on success, negative libfdt error value otherwise + * returns: 0 on success, negative libfdt error value otherwise */ #ifndef SWIG /* Not available in Python */ int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, @@ -1484,7 +1484,7 @@ static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, * @val: new value of the 32-bit cell * * This is an alternative name for fdt_setprop_inplace_u32() - * Return: 0 on success, negative libfdt error number otherwise. + * returns: 0 on success, negative libfdt error number otherwise. */ static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) @@ -2084,7 +2084,7 @@ static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, * * This is an alternative name for fdt_setprop_u32() * - * Return: 0 on success, negative libfdt error value otherwise. + * returns: 0 on success, negative libfdt error value otherwise. */ static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) @@ -2294,7 +2294,7 @@ static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, * * This is an alternative name for fdt_appendprop_u32() * - * Return: 0 on success, negative libfdt error value otherwise. + * returns: 0 on success, negative libfdt error value otherwise. */ static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, const char *name, uint32_t val) @@ -2405,8 +2405,8 @@ int fdt_delprop(void *fdt, int nodeoffset, const char *name); * creating subnodes based on a portion of a larger string, such as a * full path. * - * Return: structure block offset of the created subnode (>=0), - * negative libfdt error value otherwise + * returns: structure block offset of the created subnode (>=0), + * negative libfdt error value otherwise */ #ifndef SWIG /* Not available in Python */ int fdt_add_subnode_namelen(void *fdt, int parentoffset, From 47d7c01ba8a1241e919ab56dd01ba245b38fef8e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 17 Apr 2026 20:36:42 +1000 Subject: [PATCH 09/35] libfdt: Fix bugs with unchecked usage of fdt_num_mem_rsv() fdt_num_mem_rsv() can return an error if the memory reservation block is not properly terminated with a (0, 0) entry. However several other places in libfdt called it without checking for error returns, and could therefore return strange results, or in the case of fdt_open_into() crash. Fix this by always checking the return value. Add some addition tests to catch this bug. Reported-by: Moshe Strauss Signed-off-by: David Gibson --- libfdt/fdt_ro.c | 2 ++ libfdt/fdt_rw.c | 27 +++++++++++---- libfdt/libfdt.h | 7 +++- tests/.gitignore | 1 + tests/Makefile.tests | 3 +- tests/meson.build | 1 + tests/run_tests.sh | 1 + tests/testdata.h | 1 + tests/trees.S | 17 ++++++++++ tests/truncated_memrsv.c | 9 +++++ tests/unterminated_memrsv.c | 67 +++++++++++++++++++++++++++++++++++++ 11 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 tests/unterminated_memrsv.c diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c index 63494fb..11f2e2e 100644 --- a/libfdt/fdt_ro.c +++ b/libfdt/fdt_ro.c @@ -191,6 +191,8 @@ int fdt_num_mem_rsv(const void *fdt) int i; const struct fdt_reserve_entry *re; + FDT_RO_PROBE(fdt); + for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { if (fdt64_ld_(&re->size) == 0) return i; diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c index 90ea14e..bea45ed 100644 --- a/libfdt/fdt_rw.c +++ b/libfdt/fdt_rw.c @@ -166,7 +166,11 @@ int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) FDT_RW_PROBE(fdt); - re = fdt_mem_rsv_w_(fdt, fdt_num_mem_rsv(fdt)); + err = fdt_num_mem_rsv(fdt); + if (err < 0) + return err; + + re = fdt_mem_rsv_w_(fdt, err); err = fdt_splice_mem_rsv_(fdt, re, 0, 1); if (err) return err; @@ -179,10 +183,15 @@ int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) int fdt_del_mem_rsv(void *fdt, int n) { struct fdt_reserve_entry *re = fdt_mem_rsv_w_(fdt, n); + int num; FDT_RW_PROBE(fdt); - if (n >= fdt_num_mem_rsv(fdt)) + num = fdt_num_mem_rsv(fdt); + if (num < 0) + return num; + + if (n >= num) return -FDT_ERR_NOTFOUND; return fdt_splice_mem_rsv_(fdt, re, 1, 0); @@ -439,8 +448,10 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize) FDT_RO_PROBE(fdt); - mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) - * sizeof(struct fdt_reserve_entry); + err = fdt_num_mem_rsv(fdt); + if (err < 0) + return err; + mem_rsv_size = (err + 1) * sizeof(struct fdt_reserve_entry); if (can_assume(LATEST) || fdt_version(fdt) >= 17) { struct_size = fdt_size_dt_struct(fdt); @@ -498,12 +509,14 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize) int fdt_pack(void *fdt) { - int mem_rsv_size; + int err, mem_rsv_size; FDT_RW_PROBE(fdt); - mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) - * sizeof(struct fdt_reserve_entry); + err = fdt_num_mem_rsv(fdt); + if (err < 0) + return err; + mem_rsv_size = (err+1) * sizeof(struct fdt_reserve_entry); fdt_packblocks_(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt), fdt_size_dt_strings(fdt)); fdt_set_totalsize(fdt, fdt_data_size_(fdt)); diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index fd7637c..8c7770e 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -475,7 +475,12 @@ int fdt_generate_phandle(const void *fdt, uint32_t *phandle); * or any other (0,0) entries reserved for expansion. * * returns: - * the number of entries + * the number of entries, on success + * -FDT_ERR_ALIGNMENT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings */ int fdt_num_mem_rsv(const void *fdt); diff --git a/tests/.gitignore b/tests/.gitignore index 3376ed9..a1f53dd 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -72,6 +72,7 @@ tmp.* /truncated_property /truncated_string /truncated_memrsv +/unterminated_memrsv /utilfdt_test /value-labels /get_next_tag_invalid_prop_len diff --git a/tests/Makefile.tests b/tests/Makefile.tests index 05bb3b2..94b3cf9 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -32,7 +32,8 @@ LIB_TESTS_L = get_mem_rsv \ fs_tree1 LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) -LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv +LIBTREE_TESTS_L = truncated_property truncated_string \ + truncated_memrsv unterminated_memrsv LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%) diff --git a/tests/meson.build b/tests/meson.build index 37bfd47..4e669ce 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -96,6 +96,7 @@ tests += [ 'truncated_memrsv', 'truncated_property', 'truncated_string', + 'unterminated_memrsv', ] test_deps = [testutil_dep, util_dep, libfdt_dep] diff --git a/tests/run_tests.sh b/tests/run_tests.sh index f07092b..34110c1 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -495,6 +495,7 @@ libfdt_tests () { run_test truncated_property run_test truncated_string run_test truncated_memrsv + run_test unterminated_memrsv # Check aliases support in fdt_path_offset run_dtc_test -I dts -O dtb -o aliases.dtb "$SRCDIR/aliases.dts" diff --git a/tests/testdata.h b/tests/testdata.h index fcebc2c..b29a759 100644 --- a/tests/testdata.h +++ b/tests/testdata.h @@ -55,6 +55,7 @@ extern struct fdt_header bad_prop_char; extern struct fdt_header ovf_size_strings; extern struct fdt_header truncated_string; extern struct fdt_header truncated_memrsv; +extern struct fdt_header unterminated_memrsv; extern struct fdt_header two_roots; extern struct fdt_header named_root; #endif /* ! __ASSEMBLER__ */ diff --git a/tests/trees.S b/tests/trees.S index d69f7f1..3de95fa 100644 --- a/tests/trees.S +++ b/tests/trees.S @@ -291,6 +291,23 @@ truncated_memrsv_rsvmap_end: truncated_memrsv_end: + /* unterminated_memrsv */ + treehdr unterminated_memrsv + +unterminated_memrsv_rsvmap: + rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L +unterminated_memrsv_rsvmap_end: + +unterminated_memrsv_struct: + beginn "" + endn + fdtlong FDT_END +unterminated_memrsv_struct_end: + +unterminated_memrsv_strings: +unterminated_memrsv_strings_end: + +unterminated_memrsv_end: /* two root nodes */ treehdr two_roots diff --git a/tests/truncated_memrsv.c b/tests/truncated_memrsv.c index d78036c..b7ab4e7 100644 --- a/tests/truncated_memrsv.c +++ b/tests/truncated_memrsv.c @@ -15,9 +15,12 @@ #include "tests.h" #include "testdata.h" +#define SPACE 65536 + int main(int argc, char *argv[]) { void *fdt = &truncated_memrsv; + void *buf; int err; uint64_t addr, size; @@ -46,5 +49,11 @@ int main(int argc, char *argv[]) FAIL("fdt_get_mem_rsv(1) returned %d instead of -FDT_ERR_BADOFFSET", err); + buf = xmalloc(SPACE); + err = fdt_open_into(fdt, buf, SPACE); + if (err != -FDT_ERR_TRUNCATED) + FAIL("fdt_open_into() returned %d instead of -FDT_ERR_TRUNCATED", + err); + PASS(); } diff --git a/tests/unterminated_memrsv.c b/tests/unterminated_memrsv.c new file mode 100644 index 0000000..441b8d7 --- /dev/null +++ b/tests/unterminated_memrsv.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * libfdt - Flat Device Tree manipulation + * Testcase for misbehaviour on an unterminated memrsv map + * Copyright Red Hat + * + * Based on a proof of concept report from: + * Moshe Strauss + */ + +#include +#include +#include +#include + +#include + +#include "tests.h" +#include "testdata.h" + +#define SPACE 65536 + +int main(int argc, char *argv[]) +{ + void *fdt = &unterminated_memrsv; + void *buf; + int err; + uint64_t addr, size; + + test_init(argc, argv); + + err = fdt_check_header(fdt); + if (err != 0) + FAIL("Bad header: %s", fdt_strerror(err)); + + err = fdt_num_mem_rsv(fdt); + if (err != -FDT_ERR_TRUNCATED) + FAIL("fdt_num_mem_rsv() returned %d instead of -FDT_ERR_TRUNCATED", + err); + + err = fdt_get_mem_rsv(fdt, 0, &addr, &size); + if (err != 0) + FAIL("fdt_get_mem_rsv() failed on first entry: %s", + fdt_strerror(err)); + if ((addr != TEST_ADDR_1) || (size != TEST_SIZE_1)) + FAIL("Entry doesn't match: (0x%llx, 0x%llx) != (0x%llx, 0x%llx)", + (unsigned long long)addr, (unsigned long long)size, + TEST_ADDR_1, TEST_SIZE_1); + + err = fdt_add_mem_rsv(fdt, TEST_ADDR_2, TEST_SIZE_2); + if (err != -FDT_ERR_TRUNCATED) + FAIL("fdt_add_mem_rsv() returned %d instead of -FDT_ERR_TRUNCATED", + err); + + err = fdt_del_mem_rsv(fdt, 0); + if (err != -FDT_ERR_TRUNCATED) + FAIL("fdt_del_mem_rsv() returned %d instead of -FDT_ERR_TRUNCATED", + err); + + buf = xmalloc(SPACE); + err = fdt_open_into(fdt, buf, SPACE); + if (err != -FDT_ERR_TRUNCATED) + FAIL("fdt_open_into() returned %d instead of -FDT_ERR_TRUNCATED", + err); + + PASS(); +} From 2164019f84832099643acc2ff7d296daacbee236 Mon Sep 17 00:00:00 2001 From: Aristo Chen Date: Tue, 12 May 2026 15:23:14 +0200 Subject: [PATCH 10/35] checks: Add missing phandle_references prereq to interrupts_property The WARNING macro signature is (name, fn, data, ...prereqs), but interrupts_property was registered with &phandle_references in the data slot instead of as a prereq. Since c->data is const void *, the misuse compiled silently and the check has been running with num_prereqs == 0. The bug is normally masked: many other checks (interrupt_map, gpios_property, omit_unused_nodes, and the *_property checks generated by WARNING_PROPERTY_PHANDLE_CELLS) correctly list phandle_references as a prereq, and run_check recurses into prereqs unconditionally. As long as any one of them is enabled, fixup_phandle_references is pulled in transitively and the parser's 0xffffffff placeholder is replaced with the real phandle before check_interrupts_property reads it. The bug becomes visible when every other consumer of phandle_references is silenced (-Eno-phandle_references plus -Wno- for the *_property, gpios_property and interrupt_map checks). In that case the placeholder remains and interrupts_property falsely reports "Invalid phandle" / "Missing interrupt-parent" on a valid tree. Reordering check_table[] could also expose it under default flags. Add the missing NULL so &phandle_references lands in the prereq slot. Signed-off-by: Aristo Chen Signed-off-by: David Gibson --- checks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/checks.c b/checks.c index 946c142..a6037ed 100644 --- a/checks.c +++ b/checks.c @@ -1769,7 +1769,7 @@ static void check_interrupts_property(struct check *c, irq_prop->val.len, (int)(irq_cells * sizeof(cell_t))); } } -WARNING(interrupts_property, check_interrupts_property, &phandle_references); +WARNING(interrupts_property, check_interrupts_property, NULL, &phandle_references); static const struct bus_type graph_port_bus = { .name = "graph-port", From f57e7df35df4a301961cbbf9433ba4e85c2ee5ed Mon Sep 17 00:00:00 2001 From: Oleksii Kurochko Date: Tue, 19 May 2026 17:45:43 +0200 Subject: [PATCH 11/35] libfdt: fix UBSAN null pointer in fdt_property() fdt_property() unconditionally calls memcpy(ptr, val, len) even when len is zero and val is NULL. This is a legitimate calling convention for adding empty FDT properties such as "interrupt-controller", which carry no payload. However, compilers that treat memcpy as nonnull on its pointer arguments will fire UBSAN before observing that len is zero. Guard the memcpy() with a check on len so it is skipped entirely when there is no payload to copy, bringing the code in line with the nonnull contract. Signed-off-by: Oleksii Kurochko Signed-off-by: David Gibson --- libfdt/fdt_sw.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c index 4c569ee..96d4cf5 100644 --- a/libfdt/fdt_sw.c +++ b/libfdt/fdt_sw.c @@ -330,7 +330,8 @@ int fdt_property(void *fdt, const char *name, const void *val, int len) ret = fdt_property_placeholder(fdt, name, len, &ptr); if (ret) return ret; - memcpy(ptr, val, len); + if (len) + memcpy(ptr, val, len); return 0; } From 5c35036b2c83f08121001a9f54a85585161dc5c0 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 1 Aug 2025 16:03:08 +1000 Subject: [PATCH 12/35] docs: Add AI contribution guidelines Add AI coding assistant policy to CONTRIBUTING.md, modelled on the Linux kernel's coding-assistants.rst. Covers DCO/Signed-off-by restrictions, licensing requirements, and the Assisted-by attribution format. Add AGENTS.md with codebase guidance for AI coding assistants. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- AGENTS.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 48 +++++++++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1b8ba76 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,96 @@ +# AGENTS.md + +This file provides guidance to AI coding assistants when working with +code in this repository. + +## Build and Test + +The build system is Meson (the legacy Makefile still works but is +deprecated). + +```sh +# Configure and build +meson setup build +meson compile -C build + +# Run all tests +meson test -C build + +# Run a specific test suite (libfdt, dtc, fdtget, fdtput, fdtdump, fdtoverlay, pylibfdt, utilfdt, dtbs_equal) +meson test -C build dtc +meson test -C build libfdt + +# Legacy make (deprecated, still functional) +make +make check # all tests +make checkm # tests under valgrind +``` + +Optional build dependencies: libyaml (>= 0.2.3) for YAML output, +valgrind for memory checking, swig + python3-dev for pylibfdt. + +## Architecture + +The repo contains three main components: + +### dtc (Device Tree Compiler) + +Compiles device tree source (.dts) to binary (.dtb) and vice versa. The pipeline is: parse source → live tree → flatten to blob (or reverse). + +- **Parsing**: `dtc-lexer.l` (flex) + `dtc-parser.y` (bison) produce a live tree from .dts source. `flattree.c` reads .dtb blobs. `fstree.c` reads /proc/device-tree style filesystem trees. `yamltree.c` writes YAML output. + +- **Live tree** (`livetree.c`, `dtc.h`): In-memory representation as `struct node` / `struct property` trees with labels, phandles, and source position tracking. The `struct data` type carries property values with type markers and cross-reference markers. +- **Checks** (`checks.c`): ~50 semantic checks registered via `WARNING()`, `ERROR()`, and `CHECK()` macros into a `check_table[]`. Each check declares prerequisite checks, forming a DAG. Checks validate DT conventions (node naming, property types, interrupt structures, etc.). Use `-W`/`-E` flags to promote/demote. +- **Output**: `flattree.c` writes .dtb blobs and assembler output. `treesource.c` writes .dts source. + +### libfdt (Flat Device Tree library) + +C library for reading/writing .dtb blobs in-place, dual-licensed +GPL-2.0-or-later OR BSD-2-Clause. Used in bootloaders, kernels, and +hypervisors where the full compiler isn't available. + +- `fdt_ro.c` — read-only access (property lookup, node traversal) +- `fdt_rw.c` — read-write modification of existing blobs +- `fdt_sw.c` — sequential-write creation of new blobs +- `fdt_wip.c` — "write in place" operations (in-place modification) +- `fdt_overlay.c` — device tree overlay application +- `fdt_check.c` — blob validation (`fdt_check_full`) +- `fdt_addresses.c` — address/size cell helpers +- `version.lds` — exported symbol list; new public functions must be added here + +libfdt is designed to be embeddable: `Makefile.libfdt` can be included +by external build systems. The `FDT_ASSUME_MASK` controls safety +vs. performance tradeoffs (see `libfdt_internal.h`). + +### pylibfdt + +SWIG-generated Python bindings for libfdt +(`pylibfdt/libfdt.i`). Functions not supportable by SWIG should be +behind `#ifndef SWIG` in `libfdt.h`. + +## Tests + +Tests live in `tests/`. The test runner is `tests/run_tests.sh` which +defines test groups: `libfdt_tests`, `dtc_tests`, `fdtget_tests`, +`fdtput_tests`, `fdtoverlay_tests`, `pylibfdt_tests`, etc. + +Individual C test programs link against libfdt and use helpers from +`tests/testutils.c`. Binary test trees are built from assembler macros +in `tests/trees.S` via `tests/dumptrees.c` — if you modify +`tests/test_tree1.dts`, you must also update `tests/trees.S`. + +## AI Contribution Policy + +See the "AI Coding Assistants" section in CONTRIBUTING.md. Key rules: + +- **Do not** add `Signed-off-by` tags — only humans can certify the DCO +- Use `Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]` for attribution in commit messages +- The human submitter is responsible for reviewing all AI-generated code and ensuring license compliance + +## Coding Conventions + +- License: GPL-2.0-or-later for dtc tools; (GPL-2.0-or-later OR BSD-2-Clause) for libfdt +- SPDX identifiers on every file +- C style follows kernel conventions: tabs for indentation, `lower_case` names +- Compiler warnings are errors (`-Werror`) +- libfdt functions return negative `FDT_ERR_*` codes on failure (never errno) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48c8efd..9742dc3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,3 +77,51 @@ from people handling and transporting the patch, but were not involved in its development. SoB chains should reflect the **real** route a patch took as it was propagated to the maintainers, with the first SoB entry signalling primary authorship of a single author. + +## AI Coding Assistants + +AI tools helping with dtc/libfdt development must follow the standard +contribution process described in this document. + +### Licensing and Legal Requirements + +All contributions must comply with the project's licensing requirements: + +* All code must be compatible with GPL-2.0-or-later + * All libfdt code must also be compatible with BSD-2-Clause +* Use appropriate SPDX license identifiers + +### Signed-off-by and Developer Certificate of Origin + +AI agents MUST NOT add Signed-off-by tags. Only humans can legally +certify the Developer Certificate of Origin (DCO). The human submitter +is responsible for: + +* Reviewing all AI-generated code +* Ensuring compliance with licensing requirements +* Adding their own Signed-off-by tag to certify the DCO +* Taking full responsibility for the contribution + +### Attribution + +When AI tools contribute to development, proper attribution helps track +the evolving role of AI in the development process. Contributions +should include an `Assisted-by` tag in the following format: + +``` +Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2] +``` + +Where: + +* `AGENT_NAME` is the name of the AI tool or framework +* `MODEL_VERSION` is the specific model version used +* `[TOOL1] [TOOL2]` are optional specialized analysis tools used + (e.g., coccinelle, sparse, smatch, clang-tidy) + +Basic development tools (git, gcc, make, editors) should not be listed. + +Example: +``` +Assisted-by: Claude:claude-3-opus coccinelle sparse +``` From 7fdf750154e900c1c8d8dcf6a49b102c86fc3edf Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 15:20:10 +1000 Subject: [PATCH 13/35] docs: Add release tagging procedure and finalize-release script Document the release workflow in AGENTS.md, splitting it into agent-preparable draft steps and human-only finalization. Add scripts/finalize-release which amends the version bump commit with Signed-off-by, creates a signed tag, and optionally invokes kup-dtc for upload. Remove the now-redundant dist and kup targets from the Makefile. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- AGENTS.md | 45 +++++++++++++++++++++++++ Makefile | 23 ------------- scripts/finalize-release | 71 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 23 deletions(-) create mode 100755 scripts/finalize-release diff --git a/AGENTS.md b/AGENTS.md index 1b8ba76..77fa0f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -87,6 +87,51 @@ See the "AI Coding Assistants" section in CONTRIBUTING.md. Key rules: - Use `Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]` for attribution in commit messages - The human submitter is responsible for reviewing all AI-generated code and ensuring license compliance +## Tagging a New Release + +Releases use the `vMAJOR.MINOR.PATCH` tag format (e.g., `v1.7.2`). + +An AI agent can prepare the release; the human maintainer reviews, +adds `Signed-off-by`, and signs the tag. + +1. **Update `VERSION.txt`** — change the single line to the new version + number (e.g., `1.7.2`). `meson.build` reads its version from this + file. No other version files need editing. + +2. **Draft the version bump commit** — create the commit *without* + `Signed-off-by` (agents must not add S-o-b per the AI contribution + policy). Use the message format: + ``` + Bump version to vX.Y.Z + + Prepare for a new release. + ``` + +3. **Draft the tag message** — write it to a temporary file (e.g., + `tag-message.txt`) for the maintainer to review. The format is: + ``` + DTC X.Y.Z + + Changes since vPREV include: + * Component + - Change description + - ... + ``` + Group changes by component (dtc, libfdt, pylibfdt, fdtget, + fdtoverlay, Build, General, etc.) with a bullet per notable change. + Generate the changelog from `git log vPREV..HEAD`. + +4. **Human review** — the maintainer reviews the commit and tag + message, then runs the finalize script which amends the commit to + add `Signed-off-by` and creates the signed annotated tag: + ```sh + scripts/finalize-release tag-message.txt + ``` + +Agents must **never** run `scripts/finalize-release` or +`scripts/kup-dtc`. These perform signing and upload operations that +only a human maintainer may execute. + ## Coding Conventions - License: GPL-2.0-or-later for dtc tools; (GPL-2.0-or-later OR BSD-2-Clause) for libfdt diff --git a/Makefile b/Makefile index 83d8220..4b8909a 100644 --- a/Makefile +++ b/Makefile @@ -255,29 +255,6 @@ fdtput: $(FDTPUT_OBJS) $(LIBFDT_dep) fdtoverlay: $(FDTOVERLAY_OBJS) $(LIBFDT_dep) -dist: - git archive --format=tar --prefix=dtc-$(dtc_version)/ HEAD \ - > ../dtc-$(dtc_version).tar - cat ../dtc-$(dtc_version).tar | \ - gzip -9 > ../dtc-$(dtc_version).tar.gz - - -# -# Release signing and uploading -# This is for maintainer convenience, don't try this at home. -# -ifeq ($(MAINTAINER),y) -GPG = gpg2 -KUP = kup -KUPDIR = /pub/software/utils/dtc - -kup: dist - $(GPG) --detach-sign --armor -o ../dtc-$(dtc_version).tar.sign \ - ../dtc-$(dtc_version).tar - $(KUP) put ../dtc-$(dtc_version).tar.gz ../dtc-$(dtc_version).tar.sign \ - $(KUPDIR)/dtc-$(dtc_version).tar.gz -endif - tags: FORCE rm -f tags find . \( -name tests -type d -prune \) -o \ diff --git a/scripts/finalize-release b/scripts/finalize-release new file mode 100755 index 0000000..8b6fba3 --- /dev/null +++ b/scripts/finalize-release @@ -0,0 +1,71 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Finalize a release (possibly prepared with AI agent assistance) +# Amends the version bump commit to add Signed-off-by, then creates a +# signed annotated tag from the drafted tag message. + +set -e + +usage() { + echo "Usage: $0 " >&2 + echo "" >&2 + echo "Run this from the repository root with VERSION.txt updated" >&2 + echo "and a draft tag message in given file." >&2 + exit 1 +} + +TAG_MSG_FILE="$1" +if [ -z "$TAG_MSG_FILE" ] || [ ! -f "$TAG_MSG_FILE" ]; then + usage +fi + +VERSION=$(cat VERSION.txt) +TAG="v${VERSION}" + +if git rev-parse "$TAG" >/dev/null 2>&1; then + echo "Error: tag $TAG already exists" >&2 + exit 1 +fi + +HEAD_SUBJECT=$(git log -1 --format=%s) +EXPECTED="Bump version to $TAG" +if [ "$HEAD_SUBJECT" != "$EXPECTED" ]; then + echo "Error: HEAD commit subject is:" >&2 + echo " $HEAD_SUBJECT" >&2 + echo "Expected:" >&2 + echo " $EXPECTED" >&2 + exit 1 +fi + +echo "Version: $VERSION" +echo "Tag: $TAG" +echo "" +echo "--- Tag message ---" +cat "$TAG_MSG_FILE" +echo "--- End tag message ---" +echo "" + +printf "Proceed? [y/N] " +read answer +case "$answer" in + y|Y) ;; + *) echo "Aborted."; exit 1 ;; +esac + +git commit --amend -s --no-edit +git tag -s "$TAG" -F "$TAG_MSG_FILE" + +echo "" +echo "Created signed tag $TAG." +echo "Review with: git show $TAG" +echo "" + +SCRIPTDIR=$(dirname "$0") + +printf "Upload to kernel.org via kup? [y/N] " +read answer +case "$answer" in + y|Y) "$SCRIPTDIR/kup-dtc" "$VERSION" ;; + *) echo "Skipping kup upload." ;; +esac From dd1c7f1a919ee014fb836ba2c7e6958999fdced3 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 15:45:28 +1000 Subject: [PATCH 14/35] docs: Add CLAUDE.md referencing AGENTS.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Code auto-loads CLAUDE.md at session start but does not look for AGENTS.md. Without this file, a fresh session misses the project's AI contribution policy (attribution format, DCO rules, build instructions) unless the user explicitly points it there. Include a note pinning the Assisted-by agent name to "Claude" for consistency — previous sessions have used varying formats (claude-code, Claude Code, Claude). Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- CLAUDE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2b87faa --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,17 @@ +# CLAUDE.md + +Read and follow AGENTS.md — it has build instructions, architecture +notes, coding conventions, and the AI contribution policy for this +project. + +## Claude Code Specifics + +When writing `Assisted-by` commit trailers, use `Claude` as the agent +name and the model's short ID as the version. For example: + +``` +Assisted-by: Claude:claude-opus-4-6 +``` + +Do not use `claude-code`, `Claude Code`, or other variations as the +agent name. From 1aaa44849b9a695ed107e6e8a88c1348db68f7d0 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 15:53:57 +1000 Subject: [PATCH 15/35] ci: Skip swig install for mingw32 Windows build MSYS2 has dropped the mingw-w64-i686-swig package, causing the mingw32 CI job to fail during setup. Since the Windows build doesn't enable Python bindings anyway, conditionally install swig and python-setuptools-scm only for the 64-bit subsystems. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- .github/workflows/build.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6e0a9c..ad9ce17 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,10 +77,10 @@ jobs: fail-fast: false matrix: include: - - { sys: mingw32 } - - { sys: mingw64 } - - { sys: ucrt64 } - - { sys: clang64 } + - { sys: mingw32, swig: false } + - { sys: mingw64, swig: true } + - { sys: ucrt64, swig: true } + - { sys: clang64, swig: true } name: ${{ matrix.sys }} defaults: run: @@ -105,8 +105,7 @@ jobs: meson:p ninja:p libyaml:p - swig:p - python-setuptools-scm:p + ${{ matrix.swig && 'swig:p python-setuptools-scm:p' || '' }} - name: '🚧 Build' run: | From 83cdc93ca900e61b203e07198740668cb07a5f06 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 15:41:25 +1000 Subject: [PATCH 16/35] ci: Add macOS build to GitHub Actions The codebase already has macOS support (dylib flags in the Makefile, macOS linker handling in meson.build), but CI only tests Linux, Windows, and FreeBSD. This means macOS regressions go unnoticed until a user reports them. Add a build-macos job that tests both Make and Meson builds on macos-latest. The runner ships with clang, flex, bison, python3, and git; only libyaml, swig, meson, ninja, and pkg-config are installed via Homebrew. Apple Clang uses -Wsign-compare by default, which trips on some lex generated code. Suppress the warning for the two flex-generated objects. We skip tests for now, because trees.S doesn't work with the MacOS assembler. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- .github/workflows/build.yml | 31 +++++++++++++++++++++++++++++++ Makefile | 5 +++++ 2 files changed, 36 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad9ce17..294ebb2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,37 @@ jobs: - name: Run check run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi + build-macos: + runs-on: macos-latest + + strategy: + fail-fast: false + matrix: + build: [ "make", "meson" ] + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Dependencies + run: | + brew install libyaml swig meson ninja pkg-config + + - name: Build (Make) + if: matrix.build == 'make' + run: | + make + + - name: Setup (Meson) + if: matrix.build == 'meson' + run: meson setup -D python=enabled -D yaml=enabled -D tests=false build + + - name: Build (Meson) + if: matrix.build == 'meson' + run: meson compile -C build + build-windows: runs-on: windows-latest strategy: diff --git a/Makefile b/Makefile index 4b8909a..9110d17 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,11 @@ include Makefile.convert-dtsv0 include Makefile.dtc include Makefile.utils +# Flex-generated source triggers -Wsign-compare in its templates +dtc-lexer.lex.o: CFLAGS += -Wno-sign-compare +convert-dtsv0-lexer.lex.o: CFLAGS += -Wno-sign-compare + + BIN += convert-dtsv0 BIN += dtc BIN += fdtdump From b1337df19598b27efd8f90023b071e0aae7933db Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 18:03:58 +1000 Subject: [PATCH 17/35] Bump version to v1.8.0 Prepare for a new release. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index f8a696c..27f9cd3 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.7.2 +1.8.0 From 040c1f7e29e25785d2d84044b1a766aa819d527a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 28 May 2026 11:20:07 +1000 Subject: [PATCH 18/35] libfdt: Revert accidental ABI breakage Commit bdca8612 introduced the new fdt_setprop_namelen() and fdt_setprop_placeholder_namelen() functions, making the existing fdt_setprop() and fdt_setprop_placeholder() trivial inline wrappers around them. I failed to realise that replacing the existing functions with (static) inlines breaks the library ABI. Theoretically it's possible to have the functions available as both an inline and a "real" symbol. However, because they do need to call strlen() before the lower level fdt function, these wrappers aren't quite as trivial as they appear (inlining may not be zero cost). So, instead revert and keep the old functions as true functions. Reported-by: David Runge Fixes: bdca8612 ("libfdt: Add fdt_setprop_namelen()") Link: https://github.com/dgibson/dtc/issues/184 Signed-off-by: David Gibson --- libfdt/fdt_rw.c | 14 ++++++++++++++ libfdt/libfdt.h | 17 ++++------------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c index bea45ed..850aafe 100644 --- a/libfdt/fdt_rw.c +++ b/libfdt/fdt_rw.c @@ -292,6 +292,13 @@ int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name, return 0; } +int fdt_setprop_placeholder(void *fdt, int nodeoffset, + const char *name, int len, void **prop_data) +{ + return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, + strlen(name), len, prop_data); +} + int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, int namelen, const void *val, int len) { @@ -308,6 +315,13 @@ int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, return 0; } +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val, + len); +} + int fdt_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index 8c7770e..c69a18e 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -1935,12 +1935,8 @@ int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ -static inline int fdt_setprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len) -{ - return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val, - len); -} +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); /** * fdt_setprop_placeholder_namelen - allocate space for a property @@ -2002,13 +1998,8 @@ int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ -static inline int fdt_setprop_placeholder(void *fdt, int nodeoffset, - const char *name, int len, - void **prop_data) -{ - return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, - strlen(name), len, prop_data); -} +int fdt_setprop_placeholder(void *fdt, int nodeoffset, + const char *name, int len, void **prop_data); /** * fdt_setprop_u32 - set a property to a 32-bit integer From 25a368100a98213119a06085f9c7f535521721da Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 19:32:39 +1000 Subject: [PATCH 19/35] docs: Improvements to release creation tooling Introduce prepare-release for the deterministic parts of preparing a release. Update AGENTS.md to use that, rather than using an LLM for deterministic steps. finalize-release now pushes the release commit and tag after signing, and prepares a release announcement mail. Assisted-by: Claude:claude-opus-4-6i Signed-off-by: David Gibson --- AGENTS.md | 28 +++++++--------- scripts/finalize-release | 27 ++++++++++++++++ scripts/prepare-release | 70 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 16 deletions(-) create mode 100755 scripts/prepare-release diff --git a/AGENTS.md b/AGENTS.md index 77fa0f6..82d5996 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,20 +94,14 @@ Releases use the `vMAJOR.MINOR.PATCH` tag format (e.g., `v1.7.2`). An AI agent can prepare the release; the human maintainer reviews, adds `Signed-off-by`, and signs the tag. -1. **Update `VERSION.txt`** — change the single line to the new version - number (e.g., `1.7.2`). `meson.build` reads its version from this - file. No other version files need editing. - -2. **Draft the version bump commit** — create the commit *without* - `Signed-off-by` (agents must not add S-o-b per the AI contribution - policy). Use the message format: +1. **Update `VERSION.txt` and commit** — run the prepare script: + ```sh + scripts/prepare-release X.Y.Z ``` - Bump version to vX.Y.Z + This updates `VERSION.txt` and creates the version bump commit + (without `Signed-off-by`, per the AI contribution policy). - Prepare for a new release. - ``` - -3. **Draft the tag message** — write it to a temporary file (e.g., +2. **Draft the tag message** — write it to a temporary file (e.g., `tag-message.txt`) for the maintainer to review. The format is: ``` DTC X.Y.Z @@ -121,16 +115,18 @@ adds `Signed-off-by`, and signs the tag. fdtoverlay, Build, General, etc.) with a bullet per notable change. Generate the changelog from `git log vPREV..HEAD`. -4. **Human review** — the maintainer reviews the commit and tag +3. **Human review** — the maintainer reviews the commit and tag message, then runs the finalize script which amends the commit to - add `Signed-off-by` and creates the signed annotated tag: + add `Signed-off-by`, creates the signed annotated tag, pushes the + release commit and tag to origin, and optionally uploads to + kernel.org via kup: ```sh scripts/finalize-release tag-message.txt ``` Agents must **never** run `scripts/finalize-release` or -`scripts/kup-dtc`. These perform signing and upload operations that -only a human maintainer may execute. +`scripts/kup-dtc`. These perform signing, push, and upload operations +that only a human maintainer may execute. ## Coding Conventions diff --git a/scripts/finalize-release b/scripts/finalize-release index 8b6fba3..89008f2 100755 --- a/scripts/finalize-release +++ b/scripts/finalize-release @@ -61,6 +61,10 @@ echo "Created signed tag $TAG." echo "Review with: git show $TAG" echo "" +git push origin main "$TAG" +git push github main "$TAG" +git push gitlab main "$TAG" + SCRIPTDIR=$(dirname "$0") printf "Upload to kernel.org via kup? [y/N] " @@ -69,3 +73,26 @@ case "$answer" in y|Y) "$SCRIPTDIR/kup-dtc" "$VERSION" ;; *) echo "Skipping kup upload." ;; esac + +TAG_BODY=$(git tag -l --format='%(contents:body)' "$TAG") + +ANNOUNCE="announce-${TAG}.txt" +cat > "$ANNOUNCE" <" >&2 + echo "" >&2 + echo " version X.Y.Z or vX.Y.Z" >&2 + echo "" >&2 + echo "Example: $0 1.8.1" >&2 + exit 1 +} + +RAW="$1" +if [ -z "$RAW" ]; then + usage +fi + +VERSION="${RAW#v}" + +case "$VERSION" in + [0-9]*.[0-9]*.[0-9]*) + ;; + *) + echo "Error: version '$VERSION' does not look like X.Y.Z" >&2 + exit 1 + ;; +esac + +TAG="v${VERSION}" + +if git rev-parse "$TAG" >/dev/null 2>&1; then + echo "Error: tag $TAG already exists" >&2 + exit 1 +fi + +if ! git diff-index --quiet HEAD; then + echo "Error: working tree is dirty; commit or stash first" >&2 + exit 1 +fi + +echo "$VERSION" > VERSION.txt +git add VERSION.txt +git commit -m "$(cat < Date: Thu, 28 May 2026 12:13:02 +1000 Subject: [PATCH 20/35] Bump version to v1.8.1 Prepare for a new release. Signed-off-by: David Gibson --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 27f9cd3..a8fdfda 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.8.0 +1.8.1 From 84d13508d59db724770e18af89a9033643a7f991 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 20:47:02 +1000 Subject: [PATCH 21/35] tests: Remove unused cleanup() mechanism from tests The cleanup() function is defined as a weak symbol in testutils.c, and called from the various helpers which exit a test. The idea is that each test can override it to perform test specific cleanup. However, nothing actually uses it. Furthermore the use of ((weak)) means we can't run the tests on Windows / mingw. Just remove it for now. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- tests/tests.h | 9 --------- tests/testutils.c | 5 ----- 2 files changed, 14 deletions(-) diff --git a/tests/tests.h b/tests/tests.h index 7c1b6c0..578d8c1 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -25,9 +25,6 @@ void test_init(int argc, char *argv[]); #define streq(s1, s2) (strcmp((s1),(s2)) == 0) -/* Each test case must define this function */ -void cleanup(void); - #define verbose_printf(...) \ if (verbose_test) { \ printf(__VA_ARGS__); \ @@ -39,21 +36,18 @@ void cleanup(void); #define PASS() \ do { \ - cleanup(); \ printf("PASS\n"); \ exit(RC_PASS); \ } while (0) #define PASS_INCONCLUSIVE() \ do { \ - cleanup(); \ printf("PASS (inconclusive)\n"); \ exit(RC_PASS); \ } while (0) #define IRRELEVANT() \ do { \ - cleanup(); \ printf("PASS (irrelevant)\n"); \ exit(RC_PASS); \ } while (0) @@ -61,21 +55,18 @@ void cleanup(void); /* Look out, gcc extension below... */ #define FAIL(fmt, ...) \ do { \ - cleanup(); \ printf("FAIL\t" fmt "\n", ##__VA_ARGS__); \ exit(RC_FAIL); \ } while (0) #define CONFIG(fmt, ...) \ do { \ - cleanup(); \ printf("Bad configuration: " fmt "\n", ##__VA_ARGS__); \ exit(RC_CONFIG); \ } while (0) #define TEST_BUG(fmt, ...) \ do { \ - cleanup(); \ printf("BUG in testsuite: " fmt "\n", ##__VA_ARGS__); \ exit(RC_BUG); \ } while (0) diff --git a/tests/testutils.c b/tests/testutils.c index 54da2e4..5ab4324 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -42,13 +42,8 @@ static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len) int verbose_test = 1; char *test_name; -void __attribute__((weak)) cleanup(void) -{ -} - static void sigint_handler(int signum, siginfo_t *si, void *uc) { - cleanup(); fprintf(stderr, "%s: %s (pid=%d)\n", test_name, strsignal(signum), getpid()); exit(RC_BUG); From d320877b07d14b479eb45f6ebba80105d888231e Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 19:57:14 +1000 Subject: [PATCH 22/35] tests: More portable installation of SIGINT handler Our tests install SIGINT handlers using sigaction(), but that isn't available on Windows / mingw, blocking use of the tests there. Use the more portable signal() instead - it's good enough for our needs. Avoid strsignal() for the same reason. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- AGENTS.md | 1 + tests/testutils.c | 17 +++++++---------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 82d5996..2f0264f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,6 +86,7 @@ See the "AI Coding Assistants" section in CONTRIBUTING.md. Key rules: - **Do not** add `Signed-off-by` tags — only humans can certify the DCO - Use `Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]` for attribution in commit messages - The human submitter is responsible for reviewing all AI-generated code and ensuring license compliance +- Do not add `Co-authored-by` tags. ## Tagging a New Release diff --git a/tests/testutils.c b/tests/testutils.c index 5ab4324..6d69e87 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -42,24 +42,21 @@ static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len) int verbose_test = 1; char *test_name; -static void sigint_handler(int signum, siginfo_t *si, void *uc) +static void sigint_handler(int signum) { - fprintf(stderr, "%s: %s (pid=%d)\n", test_name, - strsignal(signum), getpid()); + fprintf(stderr, "%s: signal %d (pid=%d)\n", test_name, + signum, getpid()); exit(RC_BUG); } void test_init(int argc, char *argv[]) { - int err; - struct sigaction sa_int = { - .sa_sigaction = sigint_handler, - }; - test_name = argv[0]; - err = sigaction(SIGINT, &sa_int, NULL); - if (err) + /* Use signal(), not sigaction() because it also works on the Windows / + * mingw environments we support. + */ + if (signal(SIGINT, sigint_handler) == SIG_ERR) FAIL("Can't install SIGINT handler"); if (getenv("QUIET_TEST")) From a74b952f94c85a1b522f93e4cefc4e385f879061 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Tue, 26 May 2026 20:13:33 +1000 Subject: [PATCH 23/35] tests: Fix mkdir() call for Windows compatibility Windows mkdir() takes only one argument (no mode parameter). Guard the two-argument POSIX form with #ifndef _WIN32. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- tests/fs_tree1.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/fs_tree1.c b/tests/fs_tree1.c index 978f6a3..27eea55 100644 --- a/tests/fs_tree1.c +++ b/tests/fs_tree1.c @@ -24,7 +24,11 @@ static void start_dir(const char *name) { int rc; +#ifndef _WIN32 rc = mkdir(name, 0777); +#else + rc = mkdir(name); +#endif if (rc != 0) FAIL("mkdir(\"%s\"): %s", name, strerror(errno)); From d245851f0629ab5cbf3e6caad7d2c3ce753ac23c Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 28 May 2026 15:31:00 +1000 Subject: [PATCH 24/35] tests: Avoid -Wconstant-logical-operand warnings in integer-expressions.c Our examples trip this warning new in gcc 17, but we still want them. Reported-by: Ahmad Fatoum Signed-off-by: David Gibson --- tests/integer-expressions.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/integer-expressions.c b/tests/integer-expressions.c index 2f164d9..fe12eb3 100644 --- a/tests/integer-expressions.c +++ b/tests/integer-expressions.c @@ -17,6 +17,14 @@ #include "tests.h" #include "testdata.h" +/* + * Some of these trip the -Wconstant-logical-operand warning introduced in gcc + * 17. Disable it temporarily, we also have to disable -Wpragmas for the + * benefit of older compiler versions that don't know about that warning. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wconstant-logical-operand" static struct test_expr { const char *expr; uint32_t result; @@ -51,6 +59,7 @@ static struct test_expr { TE(11 * 257 * 1321517ULL), TE(123456790 - 4/2 + 17%4), }; +#pragma GCC diagnostic pop #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) From c0e1d0fd58d4524a3667e29a51d2aa7dead57527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 8 Jun 2026 18:33:05 +0200 Subject: [PATCH 25/35] livetree: Fix a comparison of integers with different signedness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some architectures we have: livetree.c: In function 'fixup_phandles': livetree.c:1237:54: error: comparison of integer expressions of different signedness: 'long int' and 'unsigned int' [-Werror=sign-compare] 1237 | if (offset < 0 || offset + 4 > p->val.len) { | ^ cc1: all warnings being treated as errors make: *** [Makefile:307: livetree.o] Error 1 Fixes: a26ef6400bd8 ("Restore phandle references from __fixups__ node") Closes: https://github.com/dgibson/dtc/issues/186 Signed-off-by: Uwe Kleine-König Reported-by: Michael Olbrich Signed-off-by: David Gibson --- livetree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/livetree.c b/livetree.c index 5d72abc..8baed1f 100644 --- a/livetree.c +++ b/livetree.c @@ -1234,7 +1234,7 @@ void fixup_phandles(struct dt_info *dti, const char *name) continue; offset = strtol(soffset, NULL, 0); - if (offset < 0 || offset + 4 > p->val.len) { + if (offset < 0 || (unsigned int)offset + 4 > p->val.len) { if (quiet < 1) fprintf(stderr, "Warning: Label %s contains invalid offset for property %s in node %s\n", From 4ad496846a1214ee55428ea39731bbc4055ac551 Mon Sep 17 00:00:00 2001 From: Yedaya Katsman Date: Wed, 27 May 2026 17:24:33 +0300 Subject: [PATCH 26/35] Set default permissions for workflows Old github repositories created before 2023 run jobs with a GITHUB_TOKEN that has write permissions by default for PRs from inside the repo (not a fork), unless set otherwise in the repository.[0][1] [0] https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/ [1] https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/ Set it explicitly so there won't be a chance that a compromise of an action will be able to modify anything in the repository. Pointed out by zizmor[2] [2] https://github.com/zizmorcore/zizmor Signed-off-by: Yedaya Katsman Signed-off-by: David Gibson --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 294ebb2..c477b03 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,8 @@ name: Build test pull_request: branches: - main +permissions: + contents: read # ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel # and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency From 3ad5ebbdd9d525dc5b10d86dcd3532cfab8b6200 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 13 Jun 2026 15:10:46 +1000 Subject: [PATCH 27/35] ci: Upgrade actions/checkout v4 to v6 for Node.js 24 support Node.js 20 actions are deprecated and will be forced to Node.js 24 on June 16, 2026. actions/checkout@v6 has native Node.js 24 support. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c477b03..3b68821 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Dependencies run: | @@ -58,7 +58,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Install Dependencies run: | @@ -83,7 +83,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -120,7 +120,7 @@ jobs: shell: msys2 {0} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 From 23fd89bc8ed875467bcdc7901a545f155a088270 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 13 Jun 2026 14:56:12 +1000 Subject: [PATCH 28/35] ci: Move FreeBSD CI from Cirrus CI to GitHub Actions Cirrus CI shut down on 2026-06-01. Replace it with cross-platform-actions/action which runs FreeBSD in a QEMU VM on GitHub Actions runners. This preserves the same test matrix: FreeBSD 13.5 and 14.3 with both make and meson build systems. Explicitly set shell to sh, since FreeBSD 13.x defaults to csh which doesn't understand POSIX if/then/fi syntax. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- .cirrus.yml | 39 --------------------- .github/workflows/build.yml | 70 +++++++++++++++++++++++++++++++++++++ scripts/install-deps.sh | 7 ++++ 3 files changed, 77 insertions(+), 39 deletions(-) delete mode 100644 .cirrus.yml diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 825aeb7..0000000 --- a/.cirrus.yml +++ /dev/null @@ -1,39 +0,0 @@ -# FreeBSD build with multiple versions -freebsd_versions_task: - name: FreeBSD $FREEBSD_VERSION make build - freebsd_instance: - image_family: $FREEBSD_IMAGE - matrix: - - env: - FREEBSD_VERSION: "13.5" - FREEBSD_IMAGE: freebsd-13-5 - - env: - FREEBSD_VERSION: "14.3" - FREEBSD_IMAGE: freebsd-14-3 - install_script: - - pkg install -y git gmake flex bison python3 py312-setuptools swig libyaml pkgconf - build_script: - - gmake - check_script: - - gmake check - -# FreeBSD meson builds with multiple versions -freebsd_meson_versions_task: - name: FreeBSD $FREEBSD_VERSION meson build - freebsd_instance: - image_family: $FREEBSD_IMAGE - matrix: - - env: - FREEBSD_VERSION: "13.5" - FREEBSD_IMAGE: freebsd-13-5 - - env: - FREEBSD_VERSION: "14.3" - FREEBSD_IMAGE: freebsd-14-3 - install_script: - - pkg install -y git meson ninja flex bison python3 py312-setuptools swig libyaml pkgconf - setup_script: - - meson setup -D python=enabled -D yaml=enabled build - build_script: - - meson compile -C build - test_script: - - if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3b68821..4d2cb42 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -144,3 +144,73 @@ jobs: run: | meson setup -Dtools=true -Dtests=false build meson compile -C build + + build-freebsd-make: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + version: [ "13.5", "14.3" ] + + name: FreeBSD ${{ matrix.version }} make build + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Start VM + uses: cross-platform-actions/action@v1 + with: + operating_system: freebsd + version: ${{ matrix.version }} + shell: sh + + - name: Install Dependencies + shell: cpa.sh {0} + run: sudo ./scripts/install-deps.sh + + - name: Build + shell: cpa.sh {0} + run: gmake + + - name: Run check + shell: cpa.sh {0} + run: gmake check + + build-freebsd-meson: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + version: [ "13.5", "14.3" ] + + name: FreeBSD ${{ matrix.version }} meson build + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Start VM + uses: cross-platform-actions/action@v1 + with: + operating_system: freebsd + version: ${{ matrix.version }} + shell: sh + + - name: Install Dependencies + shell: cpa.sh {0} + run: sudo ./scripts/install-deps.sh + + - name: Setup + shell: cpa.sh {0} + run: meson setup -D python=enabled -D yaml=enabled build + + - name: Build + shell: cpa.sh {0} + run: meson compile -C build + + - name: Run check + shell: cpa.sh {0} + run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi diff --git a/scripts/install-deps.sh b/scripts/install-deps.sh index d243504..e66cbdf 100755 --- a/scripts/install-deps.sh +++ b/scripts/install-deps.sh @@ -1,6 +1,13 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later +if [ "$(uname -s)" = "FreeBSD" ] +then + pkg install -y git gmake flex bison meson ninja \ + python3 py312-setuptools swig libyaml pkgconf + exit 0 +fi + if [ -f /etc/os-release ] then . /etc/os-release From 6c1426dfa963374f6134b4e63b1330320c8f1a54 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 13 Jun 2026 15:29:35 +1000 Subject: [PATCH 29/35] ci: Add macOS support to install-deps.sh Use brew install --quiet to suppress warnings about already-installed packages on the GitHub Actions runner. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- .github/workflows/build.yml | 2 +- scripts/install-deps.sh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4d2cb42..1b3d924 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,7 +89,7 @@ jobs: - name: Install Dependencies run: | - brew install libyaml swig meson ninja pkg-config + ./scripts/install-deps.sh - name: Build (Make) if: matrix.build == 'make' diff --git a/scripts/install-deps.sh b/scripts/install-deps.sh index e66cbdf..01f2874 100755 --- a/scripts/install-deps.sh +++ b/scripts/install-deps.sh @@ -1,6 +1,12 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0-or-later +if [ "$(uname -s)" = "Darwin" ] +then + brew install --quiet libyaml swig meson ninja pkg-config + exit 0 +fi + if [ "$(uname -s)" = "FreeBSD" ] then pkg install -y git gmake flex bison meson ninja \ From 5f919a25d71b3fe066020685a4ce15fc4309553b Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 13 Jun 2026 15:38:00 +1000 Subject: [PATCH 30/35] ci: Merge make and meson jobs using build matrix Combine build-make/build-meson into build-linux and build-freebsd-make/build-freebsd-meson into build-freebsd, each with a build: [make, meson] matrix dimension. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- .github/workflows/build.yml | 93 ++++++++++++------------------------- 1 file changed, 29 insertions(+), 64 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b3d924..b788558 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,13 +18,14 @@ concurrency: cancel-in-progress: true jobs: - build-make: + build-linux: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ "alpine", "archlinux", "fedora", "ubuntu" ] + os: [ "alpine", "archlinux", "fedora", "ubuntu" ] + build: [ "make", "meson" ] container: image: ${{ matrix.os }} @@ -34,43 +35,26 @@ jobs: uses: actions/checkout@v6 - name: Install Dependencies - run: | - ./scripts/install-deps.sh + run: ./scripts/install-deps.sh - - name: Build - run: | - make + - name: Build (Make) + if: matrix.build == 'make' + run: make - - name: Run check - run: | - make check + - name: Run check (Make) + if: matrix.build == 'make' + run: make check - build-meson: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - os: [ "alpine", "archlinux", "fedora", "ubuntu" ] - - container: - image: ${{ matrix.os }} - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Install Dependencies - run: | - ./scripts/install-deps.sh - - - name: Setup + - name: Setup (Meson) + if: matrix.build == 'meson' run: meson setup -D python=enabled -D yaml=enabled build - - name: Build + - name: Build (Meson) + if: matrix.build == 'meson' run: meson compile -C build - - name: Run check + - name: Run check (Meson) + if: matrix.build == 'meson' run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi build-macos: @@ -145,15 +129,16 @@ jobs: meson setup -Dtools=true -Dtests=false build meson compile -C build - build-freebsd-make: + build-freebsd: runs-on: ubuntu-latest strategy: fail-fast: false matrix: version: [ "13.5", "14.3" ] + build: [ "make", "meson" ] - name: FreeBSD ${{ matrix.version }} make build + name: FreeBSD ${{ matrix.version }} ${{ matrix.build }} build steps: - name: Checkout @@ -170,47 +155,27 @@ jobs: shell: cpa.sh {0} run: sudo ./scripts/install-deps.sh - - name: Build + - name: Build (Make) + if: matrix.build == 'make' shell: cpa.sh {0} run: gmake - - name: Run check + - name: Run check (Make) + if: matrix.build == 'make' shell: cpa.sh {0} run: gmake check - build-freebsd-meson: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - version: [ "13.5", "14.3" ] - - name: FreeBSD ${{ matrix.version }} meson build - - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Start VM - uses: cross-platform-actions/action@v1 - with: - operating_system: freebsd - version: ${{ matrix.version }} - shell: sh - - - name: Install Dependencies - shell: cpa.sh {0} - run: sudo ./scripts/install-deps.sh - - - name: Setup + - name: Setup (Meson) + if: matrix.build == 'meson' shell: cpa.sh {0} run: meson setup -D python=enabled -D yaml=enabled build - - name: Build + - name: Build (Meson) + if: matrix.build == 'meson' shell: cpa.sh {0} run: meson compile -C build - - name: Run check + - name: Run check (Meson) + if: matrix.build == 'meson' shell: cpa.sh {0} run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi From 9b53b9a4c2f953a37d8f68d72f5910b20cf5aee0 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 13 Jun 2026 19:58:25 +1000 Subject: [PATCH 31/35] tests: Add missing unterminated_memrsv to dumptrees unterminated_memrsv is defined in trees.S and has a test program, but was never included in the dumptrees table, so no .dtb file was generated for it. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- tests/Makefile.tests | 3 ++- tests/dumptrees.c | 1 + tests/meson.build | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Makefile.tests b/tests/Makefile.tests index 94b3cf9..c8f22be 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -46,7 +46,8 @@ TESTS = $(LIB_TESTS) $(LIBTREE_TESTS) $(DL_LIB_TESTS) TESTS_TREES_L = test_tree1.dtb bad_node_char.dtb bad_node_format.dtb \ bad_prop_char.dtb ovf_size_strings.dtb truncated_property.dtb \ - truncated_string.dtb truncated_memrsv.dtb two_roots.dtb named_root.dtb + truncated_string.dtb truncated_memrsv.dtb unterminated_memrsv.dtb \ + two_roots.dtb named_root.dtb TESTS_TREES = $(TESTS_TREES_L:%=$(TESTS_PREFIX)%) TESTS_TARGETS = $(TESTS) $(TESTS_TREES) diff --git a/tests/dumptrees.c b/tests/dumptrees.c index 08967b3..f42f68a 100644 --- a/tests/dumptrees.c +++ b/tests/dumptrees.c @@ -24,6 +24,7 @@ static struct { TREE(ovf_size_strings), TREE(truncated_property), TREE(truncated_string), TREE(truncated_memrsv), + TREE(unterminated_memrsv), TREE(two_roots), TREE(named_root) }; diff --git a/tests/meson.build b/tests/meson.build index 4e669ce..4c68eee 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -18,6 +18,7 @@ dumptrees_dtb = custom_target( 'truncated_property.dtb', 'truncated_string.dtb', 'truncated_memrsv.dtb', + 'unterminated_memrsv.dtb', ] ) From f116f4800edce6cd58f680a36fbaed656018d528 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 13 Jun 2026 20:07:37 +1000 Subject: [PATCH 32/35] tests: Dont have test cases link directly against example dtb data Several tests (truncated_property, truncated_string, truncated_memrsv and unterminated_memrsv), rather than loading their example dtb from a file, instead link directly to trees.o and get the data from directly in their own data segment. This was a clever trick once, but it's kind of awkward, and makes it harder to generalise how we generate those trees. Change them to load their example data from file, like most of the tests already do. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- tests/Makefile.tests | 14 ++++---------- tests/meson.build | 7 ++----- tests/run_tests.sh | 8 ++++---- tests/truncated_memrsv.c | 3 ++- tests/truncated_property.c | 6 ++---- tests/truncated_string.c | 6 ++---- tests/unterminated_memrsv.c | 3 ++- 7 files changed, 18 insertions(+), 29 deletions(-) diff --git a/tests/Makefile.tests b/tests/Makefile.tests index c8f22be..bed2f36 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -29,20 +29,17 @@ LIB_TESTS_L = get_mem_rsv \ subnode_iterate \ overlay overlay_bad_fixup \ check_path check_header check_full \ - fs_tree1 -LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) - -LIBTREE_TESTS_L = truncated_property truncated_string \ + fs_tree1 \ + truncated_property truncated_string \ truncated_memrsv unterminated_memrsv - -LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%) +LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) ifneq ($(STATIC_BUILD),1) DL_LIB_TESTS_L = asm_tree_dump value-labels DL_LIB_TESTS = $(DL_LIB_TESTS_L:%=$(TESTS_PREFIX)%) endif -TESTS = $(LIB_TESTS) $(LIBTREE_TESTS) $(DL_LIB_TESTS) +TESTS = $(LIB_TESTS) $(DL_LIB_TESTS) TESTS_TREES_L = test_tree1.dtb bad_node_char.dtb bad_node_format.dtb \ bad_prop_char.dtb ovf_size_strings.dtb truncated_property.dtb \ @@ -74,9 +71,6 @@ $(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep) @$(VECHO) LD [libdl] $@ $(LINK.c) -o $@ $^ $(LIBDL) -$(LIBTREE_TESTS): %: $(TESTS_PREFIX)testutils.o $(TESTS_PREFIX)trees.o \ - util.o $(LIBFDT_dep) - $(TESTS_PREFIX)dumptrees: $(TESTS_PREFIX)trees.o $(TESTS_TREES): $(TESTS_PREFIX)dumptrees diff --git a/tests/meson.build b/tests/meson.build index 4c68eee..571c42f 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -22,7 +22,7 @@ dumptrees_dtb = custom_target( ] ) -testutil_dep = declare_dependency(sources: ['testutils.c'], link_with: trees) +testutil_dep = declare_dependency(sources: ['testutils.c']) tests = [ 'add_subnode_with_nops', @@ -90,14 +90,11 @@ tests = [ 'supernode_atdepth_offset', 'sw_states', 'sw_tree1', - 'utilfdt_test', -] - -tests += [ 'truncated_memrsv', 'truncated_property', 'truncated_string', 'unterminated_memrsv', + 'utilfdt_test', ] test_deps = [testutil_dep, util_dep, libfdt_dep] diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 34110c1..842b543 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -492,10 +492,10 @@ libfdt_tests () { run_test appendprop_addrrange unit-addr-without-reg.dtb 2 1 3 # Tests for behaviour on various sorts of corrupted trees - run_test truncated_property - run_test truncated_string - run_test truncated_memrsv - run_test unterminated_memrsv + run_test truncated_property truncated_property.dtb + run_test truncated_string truncated_string.dtb + run_test truncated_memrsv truncated_memrsv.dtb + run_test unterminated_memrsv unterminated_memrsv.dtb # Check aliases support in fdt_path_offset run_dtc_test -I dts -O dtb -o aliases.dtb "$SRCDIR/aliases.dts" diff --git a/tests/truncated_memrsv.c b/tests/truncated_memrsv.c index b7ab4e7..e9b18cc 100644 --- a/tests/truncated_memrsv.c +++ b/tests/truncated_memrsv.c @@ -19,12 +19,13 @@ int main(int argc, char *argv[]) { - void *fdt = &truncated_memrsv; + void *fdt; void *buf; int err; uint64_t addr, size; test_init(argc, argv); + fdt = load_blob_arg(argc, argv); err = fdt_check_header(fdt); if (err != 0) diff --git a/tests/truncated_property.c b/tests/truncated_property.c index d9d52b2..eb09aa4 100644 --- a/tests/truncated_property.c +++ b/tests/truncated_property.c @@ -13,17 +13,15 @@ #include #include "tests.h" -#include "testdata.h" int main(int argc, char *argv[]) { - void *fdt = &truncated_property; + void *fdt; const void *prop; int len; test_init(argc, argv); - - vg_prepare_blob(fdt, fdt_totalsize(fdt)); + fdt = load_blob_arg(argc, argv); prop = fdt_getprop(fdt, 0, "truncated", &len); if (prop) diff --git a/tests/truncated_string.c b/tests/truncated_string.c index d745414..45e6cd3 100644 --- a/tests/truncated_string.c +++ b/tests/truncated_string.c @@ -13,18 +13,16 @@ #include #include "tests.h" -#include "testdata.h" int main(int argc, char *argv[]) { - void *fdt = &truncated_string; + void *fdt; const struct fdt_property *good, *bad; int off, len; const char *name; test_init(argc, argv); - - vg_prepare_blob(fdt, fdt_totalsize(fdt)); + fdt = load_blob_arg(argc, argv); off = fdt_first_property_offset(fdt, 0); good = fdt_get_property_by_offset(fdt, off, NULL); diff --git a/tests/unterminated_memrsv.c b/tests/unterminated_memrsv.c index 441b8d7..a19dc2d 100644 --- a/tests/unterminated_memrsv.c +++ b/tests/unterminated_memrsv.c @@ -22,12 +22,13 @@ int main(int argc, char *argv[]) { - void *fdt = &unterminated_memrsv; + void *fdt; void *buf; int err; uint64_t addr, size; test_init(argc, argv); + fdt = load_blob_arg(argc, argv); err = fdt_check_header(fdt); if (err != 0) From 4df9ca4c8ddb83c40900bf13916b42914a25caf4 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sat, 13 Jun 2026 20:47:36 +1000 Subject: [PATCH 33/35] tests: Add treegen, a pure C replacement for trees.S + dumptrees For tests, we need some example dtbs that are generated without using dtc or libfdt. This is for two reasons: * We want to check exact byte-for-byte properties independently of the main code - we don't want bugs in one to mask bugs in the other * We need to generate some deliberately malformed trees to test error handling Currently, these are generated (somewhat oddly) using the assembler - we use assembler macros in trees.S which constructs them "in memory", then dumptrees.c links against that and dumps them out as files. This is arguably an abuse of the assembler, and it's fairly fragile. Platforms with unusual symbol naming conventions can cause it to break, and some assembler versions (e.g. the one on MacOS) don't work with it. This replaces it with a bespoke C code, with a structure to at least somewhat maintain the readability of the examples. Output is byte for byte identical with the previous version. For now we add tests that check the new code generates (byte for byte) identical output, but still use the old version in our other tests. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- tests/.gitignore | 1 + tests/Makefile.tests | 8 +- tests/meson.build | 9 +- tests/run_tests.sh | 15 +- tests/treegen.c | 756 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 783 insertions(+), 6 deletions(-) create mode 100644 tests/treegen.c diff --git a/tests/.gitignore b/tests/.gitignore index a1f53dd..6969312 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -69,6 +69,7 @@ tmp.* /supernode_atdepth_offset /sw_tree1 /sw_states +/treegen /truncated_property /truncated_string /truncated_memrsv diff --git a/tests/Makefile.tests b/tests/Makefile.tests index bed2f36..de12f6e 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -54,13 +54,13 @@ TESTS_DEPFILES = $(TESTS:%=%.d) \ TESTS_CLEANFILES_L = $(STD_CLEANFILES) \ *.dtb *.test.dts *.test.dt.yaml *.dtsv1 tmp.* *.bak \ - dumptrees + dumptrees treegen TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%) -TESTS_CLEANDIRS_L = fs +TESTS_CLEANDIRS_L = fs treegen-out TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%) .PHONY: tests -tests: $(TESTS) $(TESTS_TREES) +tests: $(TESTS) $(TESTS_TREES) $(TESTS_PREFIX)treegen $(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep) @@ -71,6 +71,8 @@ $(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep) @$(VECHO) LD [libdl] $@ $(LINK.c) -o $@ $^ $(LIBDL) +$(TESTS_PREFIX)treegen: + $(TESTS_PREFIX)dumptrees: $(TESTS_PREFIX)trees.o $(TESTS_TREES): $(TESTS_PREFIX)dumptrees diff --git a/tests/meson.build b/tests/meson.build index 571c42f..32297c0 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -6,6 +6,10 @@ dumptrees = executable('dumptrees', files('dumptrees.c'), build_by_default: false, link_with: trees, dependencies: libfdt_dep) +treegen = executable('treegen', files('treegen.c'), + build_by_default: false, + dependencies: [util_dep]) + dumptrees_dtb = custom_target( 'dumptrees', command: [dumptrees, meson.current_build_dir()], @@ -139,10 +143,11 @@ run_test_types = [ 'fdtget', 'fdtput', 'fdtdump', - 'fdtoverlay' + 'fdtoverlay', + 'treegen' ] run_test_deps = [ - dtc_tools, dumptrees_dtb, tests_exe + dtc_tools, dumptrees_dtb, tests_exe, treegen ] if pylibfdt_enabled run_test_types += 'pylibfdt' diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 842b543..df63a18 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -1091,6 +1091,16 @@ fdtoverlay_tests() { run_wrap_test test "$bd" = "$pd" } +treegen_tests () { + mkdir -p treegen-out + run_wrap_test ./treegen treegen-out + for tree in test_tree1 bad_node_char bad_node_format bad_prop_char \ + ovf_size_strings truncated_property truncated_string \ + truncated_memrsv unterminated_memrsv two_roots named_root; do + run_wrap_test cmp $tree.dtb treegen-out/$tree.dtb + done +} + pylibfdt_tests () { run_dtc_test -I dts -O dtb -o test_props.dtb "$SRCDIR/test_props.dts" TMP=/tmp/tests.stderr.$$ @@ -1130,7 +1140,7 @@ while getopts "vt:me" ARG ; do done if [ -z "$TESTSETS" ]; then - TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay" + TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay treegen" # Test pylibfdt if the libfdt Python module is available. if ! $no_python; then @@ -1170,6 +1180,9 @@ for set in $TESTSETS; do "fdtoverlay") fdtoverlay_tests ;; + "treegen") + treegen_tests + ;; esac done diff --git a/tests/treegen.c b/tests/treegen.c new file mode 100644 index 0000000..6891bfd --- /dev/null +++ b/tests/treegen.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * treegen - generate test device tree blobs + * + * Build example device tree blobs for test. Note that some of the generated + * blobs are deliberately malformed, in order to test error conditions. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tests.h" +#include "testdata.h" + +/* + * Buffer with write cursor. Emit helpers write at the cursor, advance it, and + * return the offset at which data was written. + */ +struct buf { + char *data; + size_t size, cursor; +}; + +#define CHUNKSIZE 1024 + +static struct buf buf_init(void) +{ + struct buf b = { NULL, 0, 0 }; + + return b; +} + +/* Low-level helpers */ + +static size_t emit_bytes(struct buf *b, const void *data, int len) +{ + size_t off = b->cursor; + + if (!len) + return off; + + if (b->cursor + len > b->size) { + size_t newsize = b->cursor + len + CHUNKSIZE; + + b->data = xrealloc(b->data, newsize); + memset(b->data + b->size, 0, newsize - b->size); + b->size = newsize; + } + memcpy(b->data + b->cursor, data, len); + b->cursor += len; + return off; +} + +static size_t emit_u32(struct buf *b, uint32_t val) +{ + fdt32_t beval = cpu_to_fdt32(val); + + return emit_bytes(b, &beval, sizeof(beval)); +} + +static size_t emit_u64(struct buf *b, uint64_t val) +{ + fdt64_t beval = cpu_to_fdt64(val); + + return emit_bytes(b, &beval, sizeof(beval)); +} + +static size_t emit_string(struct buf *b, const char *str) +{ + return emit_bytes(b, str, strlen(str) + 1); +} + +#define MAX_ALIGNMENT 8 + +static size_t emit_align(struct buf *b, size_t alignment) +{ + size_t pad = ((b->cursor + alignment - 1) & ~(alignment - 1)) - b->cursor; + static const char zeros[MAX_ALIGNMENT]; + + assert(alignment <= MAX_ALIGNMENT); + assert(pad < alignment); + + return emit_bytes(b, zeros, pad); +} + +static size_t start_block(struct buf *b) +{ + return b->cursor; +} + +static void fill_prop_name(struct buf *b, size_t strs, size_t prop, size_t stroff) +{ + struct fdt_property *fp = (struct fdt_property *)(b->data + prop); + + fp->nameoff = cpu_to_fdt32(stroff - strs); +} + +/* FDT structure helpers */ + +static size_t emit_fdt_header(struct buf *b) +{ + size_t off = emit_u32(b, FDT_MAGIC); + + emit_u32(b, 0); /* totalsize */ + emit_u32(b, 0); /* off_dt_struct */ + emit_u32(b, 0); /* off_dt_strings */ + emit_u32(b, 0); /* off_mem_rsvmap */ + emit_u32(b, 0x11); /* version */ + emit_u32(b, 0x10); /* last_comp_version */ + emit_u32(b, 0); /* boot_cpuid_phys */ + emit_u32(b, 0); /* size_dt_strings */ + emit_u32(b, 0); /* size_dt_struct */ + return off; +} + +static void finish_rsvmap(struct buf *b, size_t hdr, size_t rsvmap) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->off_mem_rsvmap = cpu_to_fdt32(rsvmap - hdr); +} + +static void finish_struct_block(struct buf *b, size_t hdr, size_t start) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->off_dt_struct = cpu_to_fdt32(start - hdr); + fh->size_dt_struct = cpu_to_fdt32(b->cursor - start); +} + +static void finish_strings_block(struct buf *b, size_t hdr, size_t start) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->off_dt_strings = cpu_to_fdt32(start - hdr); + fh->size_dt_strings = cpu_to_fdt32(b->cursor - start); +} + +static void finish_totalsize(struct buf *b, size_t hdr) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->totalsize = cpu_to_fdt32(b->cursor - hdr); +} + +static size_t emit_rsvmap_entry(struct buf *b, uint64_t addr, uint64_t size) +{ + size_t off = emit_u64(b, addr); + + emit_u64(b, size); + return off; +} + +static size_t emit_rsvmap_term(struct buf *b) +{ + return emit_rsvmap_entry(b, 0, 0); +} + +static void emit_rsvmap_empty(struct buf *b, size_t hdr) +{ + size_t rsvmap = start_block(b); + + emit_rsvmap_term(b); + finish_rsvmap(b, hdr, rsvmap); +} + +static size_t emit_begin_node(struct buf *b, const char *name) +{ + size_t off = emit_u32(b, FDT_BEGIN_NODE); + + emit_string(b, name); + emit_align(b, FDT_TAGSIZE); + return off; +} + +static size_t emit_end_node(struct buf *b) +{ + return emit_u32(b, FDT_END_NODE); +} + +static size_t emit_fdt_end(struct buf *b) +{ + return emit_u32(b, FDT_END); +} + +static size_t emit_prop_header(struct buf *b, int nameoff, int len) +{ + size_t off = emit_u32(b, FDT_PROP); + + emit_u32(b, len); + emit_u32(b, nameoff); + return off; +} + +static size_t emit_prop_nil(struct buf *b, int nameoff) +{ + return emit_prop_header(b, nameoff, 0); +} + +static size_t emit_prop_u32(struct buf *b, int nameoff, uint32_t val) +{ + size_t off = emit_prop_header(b, nameoff, 4); + + emit_u32(b, val); + return off; +} + +static size_t emit_prop_u64(struct buf *b, int nameoff, uint64_t val) +{ + size_t off = emit_prop_header(b, nameoff, 8); + + emit_u64(b, val); + return off; +} + +static size_t emit_prop_str(struct buf *b, int nameoff, const char *str) +{ + int len = strlen(str) + 1; + size_t off = emit_prop_header(b, nameoff, len); + + emit_bytes(b, str, len); + emit_align(b, FDT_TAGSIZE); + return off; +} + +#define emit_prop_strings(b, nameoff, ...) \ + emit_prop_strings_(b, nameoff, __VA_ARGS__, NULL) + +static size_t emit_prop_strings_(struct buf *b, int nameoff, ...) +{ + size_t off = emit_prop_header(b, nameoff, 0); + size_t start = b->cursor; + va_list ap; + const char *s; + + va_start(ap, nameoff); + while ((s = va_arg(ap, const char *)) != NULL) + emit_string(b, s); + va_end(ap); + + ((struct fdt_property *)(b->data + off))->len = + cpu_to_fdt32(b->cursor - start); + emit_align(b, FDT_TAGSIZE); + return off; +} + +/* + * Tree generators + * + * Each function builds a complete DTB in a buffer. + */ + +static struct buf make_test_tree1(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p_compatible, p_prop_int, p_prop_int64, p_prop_str, + p_address_cells, p_size_cells; + size_t p_s1_compatible, p_s1_reg, p_s1_prop_int; + size_t p_s1ss_compatible, p_s1ss_placeholder, p_s1ss_prop_int; + size_t p_s2_reg, p_s2_linux_phandle, p_s2_prop_int, + p_s2_address_cells, p_s2_size_cells; + size_t p_s2ss0_reg, p_s2ss0_phandle, p_s2ss0_compatible, + p_s2ss0_prop_int; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + { + size_t rsvmap = start_block(&b); + + emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1); + emit_rsvmap_entry(&b, TEST_ADDR_2, TEST_SIZE_2); + emit_rsvmap_term(&b); + finish_rsvmap(&b, hdr, rsvmap); + } + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p_compatible = emit_prop_str(&b, 0, "test_tree1"); + p_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1); + p_prop_int64 = emit_prop_u64(&b, 0, TEST_VALUE64_1); + p_prop_str = emit_prop_str(&b, 0, TEST_STRING_1); + p_address_cells = emit_prop_u32(&b, 0, 1); + p_size_cells = emit_prop_u32(&b, 0, 0); + + emit_begin_node(&b, "subnode@1"); + p_s1_compatible = emit_prop_str(&b, 0, "subnode1"); + p_s1_reg = emit_prop_u32(&b, 0, 1); + p_s1_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1); + + emit_begin_node(&b, "subsubnode"); + p_s1ss_compatible = emit_prop_strings(&b, 0, + "subsubnode1", "subsubnode"); + p_s1ss_placeholder = emit_prop_strings(&b, 0, + "this is a placeholder string", + "string2"); + p_s1ss_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1); + emit_end_node(&b); + + emit_begin_node(&b, "ss1"); + emit_end_node(&b); + + emit_end_node(&b); + + emit_begin_node(&b, "subnode@2"); + p_s2_reg = emit_prop_u32(&b, 0, 2); + p_s2_linux_phandle = emit_prop_u32(&b, 0, PHANDLE_1); + p_s2_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_2); + p_s2_address_cells = emit_prop_u32(&b, 0, 1); + p_s2_size_cells = emit_prop_u32(&b, 0, 0); + + emit_begin_node(&b, "subsubnode@0"); + p_s2ss0_reg = emit_prop_u32(&b, 0, 0); + p_s2ss0_phandle = emit_prop_u32(&b, 0, PHANDLE_2); + p_s2ss0_compatible = emit_prop_strings(&b, 0, + "subsubnode2", "subsubnode"); + p_s2ss0_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_2); + emit_end_node(&b); + + emit_begin_node(&b, "ss2"); + emit_end_node(&b); + + emit_end_node(&b); + + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + size_t s; + + s = emit_string(&b, "compatible"); + fill_prop_name(&b, strs, p_compatible, s); + fill_prop_name(&b, strs, p_s1_compatible, s); + fill_prop_name(&b, strs, p_s1ss_compatible, s); + fill_prop_name(&b, strs, p_s2ss0_compatible, s); + + s = emit_string(&b, "prop-int"); + fill_prop_name(&b, strs, p_prop_int, s); + fill_prop_name(&b, strs, p_s1_prop_int, s); + fill_prop_name(&b, strs, p_s1ss_prop_int, s); + fill_prop_name(&b, strs, p_s2_prop_int, s); + fill_prop_name(&b, strs, p_s2ss0_prop_int, s); + + fill_prop_name(&b, strs, p_prop_int64, + emit_string(&b, "prop-int64")); + + fill_prop_name(&b, strs, p_prop_str, + emit_string(&b, "prop-str")); + + fill_prop_name(&b, strs, p_s2_linux_phandle, + emit_string(&b, "linux,phandle")); + + fill_prop_name(&b, strs, p_s2ss0_phandle, + emit_string(&b, "phandle")); + + s = emit_string(&b, "reg"); + fill_prop_name(&b, strs, p_s1_reg, s); + fill_prop_name(&b, strs, p_s2_reg, s); + fill_prop_name(&b, strs, p_s2ss0_reg, s); + + fill_prop_name(&b, strs, p_s1ss_placeholder, + emit_string(&b, "placeholder")); + + s = emit_string(&b, "#address-cells"); + fill_prop_name(&b, strs, p_address_cells, s); + fill_prop_name(&b, strs, p_s2_address_cells, s); + + s = emit_string(&b, "#size-cells"); + fill_prop_name(&b, strs, p_size_cells, s); + fill_prop_name(&b, strs, p_s2_size_cells, s); + + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_truncated_property(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p = emit_prop_header(&b, 0, 4); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + emit_string(&b, "truncated"); + fill_prop_name(&b, strs, p, strs); + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_bad_node_char(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_begin_node(&b, "sub$node"); + emit_end_node(&b); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_bad_node_format(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_begin_node(&b, "subnode@1@2"); + emit_end_node(&b); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_bad_prop_char(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p = emit_prop_u32(&b, 0, TEST_VALUE_1); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + fill_prop_name(&b, strs, p, + emit_string(&b, "prop$erty")); + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_ovf_size_strings(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_prop_u32(&b, 0x10000000, 0); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + emit_string(&b, "x"); + finish_strings_block(&b, hdr, strs); + } + + ((struct fdt_header *)(b.data + hdr))->size_dt_strings = + cpu_to_fdt32(0xffffffff); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_truncated_string(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p_good, p_bad; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p_good = emit_prop_nil(&b, 0); + p_bad = emit_prop_nil(&b, 0); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + fill_prop_name(&b, strs, p_good, emit_string(&b, "good")); + fill_prop_name(&b, strs, p_bad, emit_bytes(&b, "bad", 3)); + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_truncated_memrsv(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + { + size_t rsvmap; + + emit_align(&b, 8); + rsvmap = start_block(&b); + emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1); + finish_rsvmap(&b, hdr, rsvmap); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_unterminated_memrsv(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + + { + size_t rsvmap; + + emit_align(&b, 8); + rsvmap = start_block(&b); + emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1); + finish_rsvmap(&b, hdr, rsvmap); + } + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_two_roots(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_named_root(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, "fake"); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +/* Tree table and main */ + +static struct { + struct buf (*generator)(void); + const char *filename; +} trees[] = { +#define TREE(name) { make_##name, #name ".dtb" } + TREE(test_tree1), + TREE(bad_node_char), TREE(bad_node_format), TREE(bad_prop_char), + TREE(ovf_size_strings), + TREE(truncated_property), TREE(truncated_string), + TREE(truncated_memrsv), + TREE(unterminated_memrsv), + TREE(two_roots), + TREE(named_root), +}; + +int main(int argc, char *argv[]) +{ + unsigned int i; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + if (chdir(argv[1]) != 0) { + perror("chdir()"); + return 1; + } + + for (i = 0; i < ARRAY_SIZE(trees); i++) { + struct buf b = trees[i].generator(); + const char *filename = trees[i].filename; + int fd; + ssize_t ret; + + printf("Tree \"%s\", %zu bytes\n", filename, b.cursor); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) + perror("open()"); + + ret = write(fd, b.data, b.cursor); + if (ret < 0 || (size_t)ret != b.cursor) + perror("write()"); + + close(fd); + free(b.data); + } + + return 0; +} From 214372a27398121f8266cf9bb9592561508c4c0f Mon Sep 17 00:00:00 2001 From: David Gibson Date: Sun, 14 Jun 2026 16:30:42 +1000 Subject: [PATCH 34/35] tests: Replace trees.S/dumptrees with treegen for making test dtbs Switch the build system to use treegen instead of trees.S + dumptrees for generating the test DTB files. treegen produces byte-identical output without requiring a GNU assembler. Remove the old tree.S, dumptrees.c and associated pieces. This also removes the need for the ASM_CONST_LL macro and separated values for the high and low halves of 64-bit values: those only existed so the same constants could be embedded in both C and assembly. Remove those from testdata.h, and the copies in pylibfdt_tests.py. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: David Gibson --- tests/Makefile.tests | 18 +-- tests/dumptrees.c | 70 -------- tests/meson.build | 19 +-- tests/pylibfdt_tests.py | 19 +-- tests/run_tests.sh | 15 +- tests/testdata.h | 40 +---- tests/trees.S | 343 ---------------------------------------- 7 files changed, 23 insertions(+), 501 deletions(-) delete mode 100644 tests/dumptrees.c delete mode 100644 tests/trees.S diff --git a/tests/Makefile.tests b/tests/Makefile.tests index de12f6e..af5d309 100644 --- a/tests/Makefile.tests +++ b/tests/Makefile.tests @@ -50,17 +50,17 @@ TESTS_TREES = $(TESTS_TREES_L:%=$(TESTS_PREFIX)%) TESTS_TARGETS = $(TESTS) $(TESTS_TREES) TESTS_DEPFILES = $(TESTS:%=%.d) \ - $(addprefix $(TESTS_PREFIX),testutils.d trees.d dumptrees.d) + $(addprefix $(TESTS_PREFIX),testutils.d) TESTS_CLEANFILES_L = $(STD_CLEANFILES) \ *.dtb *.test.dts *.test.dt.yaml *.dtsv1 tmp.* *.bak \ - dumptrees treegen + treegen TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%) -TESTS_CLEANDIRS_L = fs treegen-out +TESTS_CLEANDIRS_L = fs TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%) .PHONY: tests -tests: $(TESTS) $(TESTS_TREES) $(TESTS_PREFIX)treegen +tests: $(TESTS) $(TESTS_TREES) $(LIB_TESTS): %: $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep) @@ -71,13 +71,9 @@ $(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep) @$(VECHO) LD [libdl] $@ $(LINK.c) -o $@ $^ $(LIBDL) -$(TESTS_PREFIX)treegen: - -$(TESTS_PREFIX)dumptrees: $(TESTS_PREFIX)trees.o - -$(TESTS_TREES): $(TESTS_PREFIX)dumptrees - @$(VECHO) DUMPTREES - cd $(TESTS_PREFIX); ./dumptrees . >/dev/null +$(TESTS_TREES): $(TESTS_PREFIX)treegen + @$(VECHO) TREEGEN + cd $(TESTS_PREFIX); ./treegen . >/dev/null tests_clean: @$(VECHO) CLEAN "(tests)" diff --git a/tests/dumptrees.c b/tests/dumptrees.c deleted file mode 100644 index f42f68a..0000000 --- a/tests/dumptrees.c +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dumptrees - utility for libfdt testing - * - * (C) Copyright David Gibson , IBM Corporation. 2006. - */ -#include -#include -#include -#include -#include - -#include - -#include "testdata.h" - -static struct { - void *blob; - const char *filename; -} trees[] = { -#define TREE(name) { &name, #name ".dtb" } - TREE(test_tree1), - TREE(bad_node_char), TREE(bad_node_format), TREE(bad_prop_char), - TREE(ovf_size_strings), - TREE(truncated_property), TREE(truncated_string), - TREE(truncated_memrsv), - TREE(unterminated_memrsv), - TREE(two_roots), - TREE(named_root) -}; - -#define NUM_TREES (sizeof(trees) / sizeof(trees[0])) - -int main(int argc, char *argv[]) -{ - unsigned int i; - - if (argc != 2) { - fprintf(stderr, "Missing output directory argument\n"); - return 1; - } - - if (chdir(argv[1]) != 0) { - perror("chdir()"); - return 1; - } - - for (i = 0; i < NUM_TREES; i++) { - void *blob = trees[i].blob; - const char *filename = trees[i].filename; - int size; - int fd; - int ret; - - size = fdt_totalsize(blob); - - printf("Tree \"%s\", %d bytes\n", filename, size); - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) - perror("open()"); - - ret = write(fd, blob, size); - if (ret != size) - perror("write()"); - - close(fd); - } - exit(0); -} diff --git a/tests/meson.build b/tests/meson.build index 32297c0..c6ac2d7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,18 +1,10 @@ -trees = static_library('trees', files('trees.S'), - build_by_default: false, - include_directories: libfdt_inc) - -dumptrees = executable('dumptrees', files('dumptrees.c'), - build_by_default: false, - link_with: trees, dependencies: libfdt_dep) - treegen = executable('treegen', files('treegen.c'), build_by_default: false, dependencies: [util_dep]) -dumptrees_dtb = custom_target( - 'dumptrees', - command: [dumptrees, meson.current_build_dir()], +treegen_dtb = custom_target( + 'treegen_dtb', + command: [treegen, meson.current_build_dir()], output: [ 'test_tree1.dtb', 'bad_node_char.dtb', @@ -23,6 +15,8 @@ dumptrees_dtb = custom_target( 'truncated_string.dtb', 'truncated_memrsv.dtb', 'unterminated_memrsv.dtb', + 'two_roots.dtb', + 'named_root.dtb', ] ) @@ -144,10 +138,9 @@ run_test_types = [ 'fdtput', 'fdtdump', 'fdtoverlay', - 'treegen' ] run_test_deps = [ - dtc_tools, dumptrees_dtb, tests_exe, treegen + dtc_tools, treegen_dtb, tests_exe, ] if pylibfdt_enabled run_test_types += 'pylibfdt' diff --git a/tests/pylibfdt_tests.py b/tests/pylibfdt_tests.py index a4f73ed..6af11f2 100644 --- a/tests/pylibfdt_tests.py +++ b/tests/pylibfdt_tests.py @@ -13,26 +13,15 @@ sys.path.insert(0, '../pylibfdt') import libfdt from libfdt import Fdt, FdtSw, FdtException, QUIET_NOTFOUND, QUIET_ALL -TEST_ADDR_1H = 0xdeadbeef -TEST_ADDR_1L = 0x00000000 -TEST_ADDR_1 = (TEST_ADDR_1H << 32) | TEST_ADDR_1L TEST_ADDR_1 = 0x8000000000000000 -TEST_SIZE_1H = 0x00000000 -TEST_SIZE_1L = 0x00100000 -TEST_SIZE_1 = (TEST_SIZE_1H << 32) | TEST_SIZE_1L -TEST_ADDR_2H = 0 -TEST_ADDR_2L = 123456789 -TEST_ADDR_2 = (TEST_ADDR_2H << 32) | TEST_ADDR_2L -TEST_SIZE_2H = 0 -TEST_SIZE_2L = 0o10000 -TEST_SIZE_2 = (TEST_SIZE_2H << 32) | TEST_SIZE_2L +TEST_SIZE_1 = 0x0000000000100000 +TEST_ADDR_2 = 123456789 +TEST_SIZE_2 = 0o10000 TEST_VALUE_1 = 0xdeadbeef TEST_VALUE_2 = 123456789 -TEST_VALUE64_1H = 0xdeadbeef -TEST_VALUE64_1L = 0x01abcdef -TEST_VALUE64_1 = (TEST_VALUE64_1H << 32) | TEST_VALUE64_1L +TEST_VALUE64_1 = 0xdeadbeef01abcdef PHANDLE_1 = 0x2000 PHANDLE_2 = 0x2001 diff --git a/tests/run_tests.sh b/tests/run_tests.sh index df63a18..842b543 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -1091,16 +1091,6 @@ fdtoverlay_tests() { run_wrap_test test "$bd" = "$pd" } -treegen_tests () { - mkdir -p treegen-out - run_wrap_test ./treegen treegen-out - for tree in test_tree1 bad_node_char bad_node_format bad_prop_char \ - ovf_size_strings truncated_property truncated_string \ - truncated_memrsv unterminated_memrsv two_roots named_root; do - run_wrap_test cmp $tree.dtb treegen-out/$tree.dtb - done -} - pylibfdt_tests () { run_dtc_test -I dts -O dtb -o test_props.dtb "$SRCDIR/test_props.dts" TMP=/tmp/tests.stderr.$$ @@ -1140,7 +1130,7 @@ while getopts "vt:me" ARG ; do done if [ -z "$TESTSETS" ]; then - TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay treegen" + TESTSETS="libfdt utilfdt dtc dtbs_equal fdtget fdtput fdtdump fdtoverlay" # Test pylibfdt if the libfdt Python module is available. if ! $no_python; then @@ -1180,9 +1170,6 @@ for set in $TESTSETS; do "fdtoverlay") fdtoverlay_tests ;; - "treegen") - treegen_tests - ;; esac done diff --git a/tests/testdata.h b/tests/testdata.h index b29a759..c1eac10 100644 --- a/tests/testdata.h +++ b/tests/testdata.h @@ -1,28 +1,12 @@ -#ifdef __ASSEMBLER__ -#define ASM_CONST_LL(x) (x) -#else -#define ASM_CONST_LL(x) (x##ULL) -#endif - -#define TEST_ADDR_1H ASM_CONST_LL(0xdeadbeef) -#define TEST_ADDR_1L ASM_CONST_LL(0x00000000) -#define TEST_ADDR_1 ((TEST_ADDR_1H << 32) | TEST_ADDR_1L) -#define TEST_SIZE_1H ASM_CONST_LL(0x00000000) -#define TEST_SIZE_1L ASM_CONST_LL(0x00100000) -#define TEST_SIZE_1 ((TEST_SIZE_1H << 32) | TEST_SIZE_1L) -#define TEST_ADDR_2H ASM_CONST_LL(0) -#define TEST_ADDR_2L ASM_CONST_LL(123456789) -#define TEST_ADDR_2 ((TEST_ADDR_2H << 32) | TEST_ADDR_2L) -#define TEST_SIZE_2H ASM_CONST_LL(0) -#define TEST_SIZE_2L ASM_CONST_LL(010000) -#define TEST_SIZE_2 ((TEST_SIZE_2H << 32) | TEST_SIZE_2L) +#define TEST_ADDR_1 0xdeadbeef00000000ULL +#define TEST_SIZE_1 0x0000000000100000ULL +#define TEST_ADDR_2 123456789ULL +#define TEST_SIZE_2 010000ULL #define TEST_VALUE_1 0xdeadbeef #define TEST_VALUE_2 123456789 -#define TEST_VALUE64_1H ASM_CONST_LL(0xdeadbeef) -#define TEST_VALUE64_1L ASM_CONST_LL(0x01abcdef) -#define TEST_VALUE64_1 ((TEST_VALUE64_1H << 32) | TEST_VALUE64_1L) +#define TEST_VALUE64_1 0xdeadbeef01abcdefULL #define PHANDLE_1 0x2000 #define PHANDLE_2 0x2001 @@ -45,17 +29,3 @@ #define TEST_MEMREGION_SIZE 0x9abcdef0 #define TEST_MEMREGION_SIZE_HI 0x0fedcba900000000 #define TEST_MEMREGION_SIZE_INC 0x1000 - -#ifndef __ASSEMBLER__ -extern struct fdt_header test_tree1; -extern struct fdt_header truncated_property; -extern struct fdt_header bad_node_char; -extern struct fdt_header bad_node_format; -extern struct fdt_header bad_prop_char; -extern struct fdt_header ovf_size_strings; -extern struct fdt_header truncated_string; -extern struct fdt_header truncated_memrsv; -extern struct fdt_header unterminated_memrsv; -extern struct fdt_header two_roots; -extern struct fdt_header named_root; -#endif /* ! __ASSEMBLER__ */ diff --git a/tests/trees.S b/tests/trees.S deleted file mode 100644 index 3de95fa..0000000 --- a/tests/trees.S +++ /dev/null @@ -1,343 +0,0 @@ -#include -#include "testdata.h" - - .macro fdtlong val - .byte ((\val) >> 24) & 0xff - .byte ((\val) >> 16) & 0xff - .byte ((\val) >> 8) & 0xff - .byte (\val) & 0xff - .endm - - .macro treehdr tree - .balign 8 - .globl \tree -\tree : - fdtlong FDT_MAGIC - fdtlong (\tree\()_end - \tree) - fdtlong (\tree\()_struct - \tree) - fdtlong (\tree\()_strings - \tree) - fdtlong (\tree\()_rsvmap - \tree) - fdtlong 0x11 - fdtlong 0x10 - fdtlong 0 - fdtlong (\tree\()_strings_end - \tree\()_strings) - fdtlong (\tree\()_struct_end - \tree\()_struct) - .endm - - .macro rsvmape addrh, addrl, lenh, lenl - fdtlong \addrh - fdtlong \addrl - fdtlong \lenh - fdtlong \lenl - .endm - - .macro empty_rsvmap tree - .balign 8 -\tree\()_rsvmap: - rsvmape 0, 0, 0, 0 -\tree\()_rsvmap_end: - .endm - - .macro prophdr tree, name, len - fdtlong FDT_PROP - fdtlong \len - fdtlong (\tree\()_\name - \tree\()_strings) - .endm - - .macro propnil tree, name - prophdr \tree, \name, 0 - .endm - - .macro propu32 tree, name, val - prophdr \tree, \name, 4 - fdtlong \val - .endm - - .macro propu64 tree, name, valh, vall - prophdr \tree, \name, 8 - fdtlong \valh - fdtlong \vall - .endm - - .macro propstr tree, name, str:vararg - prophdr \tree, \name, (55f - 54f) -54: - .asciz \str -55: - .balign 4 - .endm - - .macro beginn name:vararg - fdtlong FDT_BEGIN_NODE - .asciz \name - .balign 4 - .endm - - .macro endn - fdtlong FDT_END_NODE - .endm - - .macro string tree, name, str:vararg -\tree\()_\name : - .asciz \str - .endm - - - .data - - treehdr test_tree1 - - .balign 8 -test_tree1_rsvmap: - rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L - rsvmape TEST_ADDR_2H, TEST_ADDR_2L, TEST_SIZE_2H, TEST_SIZE_2L - rsvmape 0, 0, 0, 0 -test_tree1_rsvmap_end: - -test_tree1_struct: - beginn "" - propstr test_tree1, compatible, "test_tree1" - propu32 test_tree1, prop_int, TEST_VALUE_1 - propu64 test_tree1, prop_int64, TEST_VALUE64_1H, TEST_VALUE64_1L - propstr test_tree1, prop_str, TEST_STRING_1 - propu32 test_tree1, address_cells, 1 - propu32 test_tree1, size_cells, 0 - - beginn "subnode@1" - propstr test_tree1, compatible, "subnode1" - propu32 test_tree1, reg, 1 - propu32 test_tree1, prop_int, TEST_VALUE_1 - - beginn "subsubnode" - propstr test_tree1, compatible, "subsubnode1\0subsubnode" - propstr test_tree1, placeholder, "this is a placeholder string\0string2" - propu32 test_tree1, prop_int, TEST_VALUE_1 - endn - - beginn "ss1" - endn - - endn - - beginn "subnode@2" - propu32 test_tree1, reg, 2 - propu32 test_tree1, linux_phandle, PHANDLE_1 - propu32 test_tree1, prop_int, TEST_VALUE_2 - propu32 test_tree1, address_cells, 1 - propu32 test_tree1, size_cells, 0 - - beginn "subsubnode@0" - propu32 test_tree1, reg, 0 - propu32 test_tree1, phandle, PHANDLE_2 - propstr test_tree1, compatible, "subsubnode2\0subsubnode" - propu32 test_tree1, prop_int, TEST_VALUE_2 - endn - - beginn "ss2" - endn - - endn - - endn - fdtlong FDT_END -test_tree1_struct_end: - -test_tree1_strings: - string test_tree1, compatible, "compatible" - string test_tree1, prop_int, "prop-int" - string test_tree1, prop_int64, "prop-int64" - string test_tree1, prop_str, "prop-str" - string test_tree1, linux_phandle, "linux,phandle" - string test_tree1, phandle, "phandle" - string test_tree1, reg, "reg" - string test_tree1, placeholder, "placeholder" - string test_tree1, address_cells, "#address-cells" - string test_tree1, size_cells, "#size-cells" -test_tree1_strings_end: -test_tree1_end: - - - treehdr truncated_property - empty_rsvmap truncated_property - -truncated_property_struct: - beginn "" - prophdr truncated_property, prop_truncated, 4 - /* Oops, no actual property data here */ -truncated_property_struct_end: - -truncated_property_strings: - string truncated_property, prop_truncated, "truncated" -truncated_property_strings_end: - -truncated_property_end: - - - treehdr bad_node_char - empty_rsvmap bad_node_char - -bad_node_char_struct: - beginn "" - beginn "sub$node" - endn - endn - fdtlong FDT_END -bad_node_char_struct_end: - -bad_node_char_strings: -bad_node_char_strings_end: -bad_node_char_end: - - - treehdr bad_node_format - empty_rsvmap bad_node_format - -bad_node_format_struct: - beginn "" - beginn "subnode@1@2" - endn - endn - fdtlong FDT_END -bad_node_format_struct_end: - -bad_node_format_strings: -bad_node_format_strings_end: -bad_node_format_end: - - - treehdr bad_prop_char - empty_rsvmap bad_prop_char - -bad_prop_char_struct: - beginn "" - propu32 bad_prop_char, prop, TEST_VALUE_1 - endn - fdtlong FDT_END -bad_prop_char_struct_end: - -bad_prop_char_strings: - string bad_prop_char, prop, "prop$erty" -bad_prop_char_strings_end: -bad_prop_char_end: - - - /* overflow_size_strings */ - .balign 8 - .globl ovf_size_strings -ovf_size_strings: - fdtlong FDT_MAGIC - fdtlong (ovf_size_strings_end - ovf_size_strings) - fdtlong (ovf_size_strings_struct - ovf_size_strings) - fdtlong (ovf_size_strings_strings - ovf_size_strings) - fdtlong (ovf_size_strings_rsvmap - ovf_size_strings) - fdtlong 0x11 - fdtlong 0x10 - fdtlong 0 - fdtlong 0xffffffff - fdtlong (ovf_size_strings_struct_end - ovf_size_strings_struct) - empty_rsvmap ovf_size_strings - -ovf_size_strings_struct: - beginn "" - propu32 ovf_size_strings, bad_string, 0 - endn - fdtlong FDT_END -ovf_size_strings_struct_end: - -ovf_size_strings_strings: - string ovf_size_strings, x, "x" - ovf_size_strings_bad_string = ovf_size_strings_strings + 0x10000000 -ovf_size_strings_strings_end: -ovf_size_strings_end: - - - /* truncated_string */ - treehdr truncated_string - empty_rsvmap truncated_string - -truncated_string_struct: - beginn "" - propnil truncated_string, good_string - propnil truncated_string, bad_string - endn - fdtlong FDT_END -truncated_string_struct_end: - -truncated_string_strings: - string truncated_string, good_string, "good" -truncated_string_bad_string: - .ascii "bad" - /* NOTE: terminating \0 deliberately missing */ -truncated_string_strings_end: -truncated_string_end: - - - /* truncated_memrsv */ - treehdr truncated_memrsv - -truncated_memrsv_struct: - beginn "" - endn - fdtlong FDT_END -truncated_memrsv_struct_end: - -truncated_memrsv_strings: -truncated_memrsv_strings_end: - - .balign 8 -truncated_memrsv_rsvmap: - rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L -truncated_memrsv_rsvmap_end: - -truncated_memrsv_end: - - /* unterminated_memrsv */ - treehdr unterminated_memrsv - -unterminated_memrsv_rsvmap: - rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L -unterminated_memrsv_rsvmap_end: - -unterminated_memrsv_struct: - beginn "" - endn - fdtlong FDT_END -unterminated_memrsv_struct_end: - -unterminated_memrsv_strings: -unterminated_memrsv_strings_end: - -unterminated_memrsv_end: - - /* two root nodes */ - treehdr two_roots - empty_rsvmap two_roots - -two_roots_struct: - beginn "" - endn - beginn "" - endn - fdtlong FDT_END -two_roots_struct_end: - -two_roots_strings: -two_roots_strings_end: - -two_roots_end: - - - /* root node with a non-empty name */ - treehdr named_root - empty_rsvmap named_root - -named_root_struct: - beginn "fake" - endn - fdtlong FDT_END -named_root_struct_end: - -named_root_strings: -named_root_strings_end: - -named_root_end: From 66e1201c3775716607c28afd2bbb2b3afb08b695 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Tue, 26 May 2026 14:30:22 -0600 Subject: [PATCH 35/35] libfdt: fdt_check_full: Add can_assume(PERFECT) check In this function from fdt_check.c we have (reasonably and as the name implies) a number of checks on the DTB. However, there are cases where we may wish to assume that we have been given a perfect DTB already and do nothing here. Add a test for can_assume(PERFECT) as the first check in this function and if true, perform no checks. Signed-off-by: Tom Rini Message-ID: <20260526203022.4006434-1-trini@konsulko.com> Reviewed-by: Simon Glass Signed-off-by: David Gibson --- libfdt/fdt_check.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c index cca0523..2fd5b61 100644 --- a/libfdt/fdt_check.c +++ b/libfdt/fdt_check.c @@ -21,6 +21,8 @@ int fdt_check_full(const void *fdt, size_t bufsize) const char *propname; bool expect_end = false; + if (can_assume(PERFECT)) + return 0; if (bufsize < FDT_V1_SIZE) return -FDT_ERR_TRUNCATED; if (bufsize < fdt_header_size(fdt))