Compare commits

..

35 commits

Author SHA1 Message Date
Tom Rini
66e1201c37 libfdt: fdt_check_full: Add can_assume(PERFECT) check
Some checks are pending
Build test / build-linux (make, alpine) (push) Waiting to run
Build test / build-linux (make, archlinux) (push) Waiting to run
Build test / build-linux (make, fedora) (push) Waiting to run
Build test / build-linux (make, ubuntu) (push) Waiting to run
Build test / build-linux (meson, alpine) (push) Waiting to run
Build test / build-linux (meson, archlinux) (push) Waiting to run
Build test / build-linux (meson, fedora) (push) Waiting to run
Build test / build-linux (meson, ubuntu) (push) Waiting to run
Build test / build-macos (make) (push) Waiting to run
Build test / build-macos (meson) (push) Waiting to run
Build test / mingw32 (push) Waiting to run
Build test / clang64 (push) Waiting to run
Build test / mingw64 (push) Waiting to run
Build test / ucrt64 (push) Waiting to run
Build test / FreeBSD 13.5 make build (push) Waiting to run
Build test / FreeBSD 14.3 make build (push) Waiting to run
Build test / FreeBSD 13.5 meson build (push) Waiting to run
Build test / FreeBSD 14.3 meson build (push) Waiting to run
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 <trini@konsulko.com>
Message-ID: <20260526203022.4006434-1-trini@konsulko.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-06-17 15:31:29 +10:00
David Gibson
214372a273 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 <david@gibson.dropbear.id.au>
2026-06-14 17:43:39 +10:00
David Gibson
4df9ca4c8d 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 <david@gibson.dropbear.id.au>
2026-06-14 15:59:18 +10:00
David Gibson
f116f4800e 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 <david@gibson.dropbear.id.au>
2026-06-13 20:55:07 +10:00
David Gibson
9b53b9a4c2 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 <david@gibson.dropbear.id.au>
2026-06-13 20:49:01 +10:00
David Gibson
5f919a25d7 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 <david@gibson.dropbear.id.au>
2026-06-13 15:49:40 +10:00
David Gibson
6c1426dfa9 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 <david@gibson.dropbear.id.au>
2026-06-13 15:48:54 +10:00
David Gibson
23fd89bc8e 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 <david@gibson.dropbear.id.au>
2026-06-13 15:48:29 +10:00
David Gibson
3ad5ebbdd9 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 <david@gibson.dropbear.id.au>
2026-06-13 15:47:11 +10:00
Yedaya Katsman
4ad496846a 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 <yedaya.ka@gmail.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-06-13 14:29:32 +10:00
Uwe Kleine-König
c0e1d0fd58 livetree: Fix a comparison of integers with different signedness
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: a26ef6400b ("Restore phandle references from __fixups__ node")
Closes: https://github.com/dgibson/dtc/issues/186
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
Reported-by: Michael Olbrich <m.olbrich@pengutronix.de>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-06-13 14:29:32 +10:00
David Gibson
d245851f06 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 <david@gibson.dropbear.id.au>
2026-05-28 15:42:51 +10:00
David Gibson
a74b952f94 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 <david@gibson.dropbear.id.au>
2026-05-28 14:04:59 +10:00
David Gibson
d320877b07 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 <david@gibson.dropbear.id.au>
2026-05-28 13:55:51 +10:00
David Gibson
84d13508d5 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 <david@gibson.dropbear.id.au>
2026-05-28 13:52:55 +10:00
David Gibson
8f48565e5c Bump version to v1.8.1
Prepare for a new release.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-05-28 12:40:45 +10:00
David Gibson
25a368100a 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 <david@gibson.dropbear.id.au>
2026-05-28 12:12:38 +10:00
David Gibson
040c1f7e29 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 <dave@sleepmap.de>
Fixes: bdca8612 ("libfdt: Add fdt_setprop_namelen()")
Link: https://github.com/dgibson/dtc/issues/184
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-05-28 11:52:59 +10:00
David Gibson
b1337df195 Bump version to v1.8.0
Prepare for a new release.

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-05-26 18:50:56 +10:00
David Gibson
83cdc93ca9 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 <david@gibson.dropbear.id.au>
2026-05-26 17:52:08 +10:00
David Gibson
1aaa44849b 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 <david@gibson.dropbear.id.au>
2026-05-26 17:50:54 +10:00
David Gibson
dd1c7f1a91 docs: Add CLAUDE.md referencing AGENTS.md
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 <david@gibson.dropbear.id.au>
2026-05-26 17:50:29 +10:00
David Gibson
7fdf750154 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 <david@gibson.dropbear.id.au>
2026-05-26 15:21:32 +10:00
David Gibson
5c35036b2c 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 <david@gibson.dropbear.id.au>
2026-05-26 13:23:55 +10:00
Oleksii Kurochko
f57e7df35d 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 <oleksii.kurochko@gmail.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-05-20 11:35:03 +10:00
Aristo Chen
2164019f84 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 <aristo.chen@canonical.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-05-16 16:31:39 +10:00
David Gibson
47d7c01ba8 libfdt: Fix bugs with unchecked usage of fdt_num_mem_rsv()
Some checks failed
Build test / build-make (alpine) (push) Has been cancelled
Build test / build-make (archlinux) (push) Has been cancelled
Build test / build-make (fedora) (push) Has been cancelled
Build test / build-make (ubuntu) (push) Has been cancelled
Build test / build-meson (alpine) (push) Has been cancelled
Build test / build-meson (archlinux) (push) Has been cancelled
Build test / build-meson (fedora) (push) Has been cancelled
Build test / build-meson (ubuntu) (push) Has been cancelled
Build test / clang64 (push) Has been cancelled
Build test / mingw32 (push) Has been cancelled
Build test / mingw64 (push) Has been cancelled
Build test / ucrt64 (push) Has been cancelled
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 <moshestrauss10@gmail.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-04-17 21:21:16 +10:00
David Gibson
f551be7b39 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 <david@gibson.dropbear.id.au>
2026-04-17 16:23:35 +10:00
Nathan Chancellor
53373d1355 dtc: Remove unused dts_version in dtc-lexer.l
Some checks failed
Build test / build-make (alpine) (push) Has been cancelled
Build test / build-make (archlinux) (push) Has been cancelled
Build test / build-make (fedora) (push) Has been cancelled
Build test / build-make (ubuntu) (push) Has been cancelled
Build test / build-meson (alpine) (push) Has been cancelled
Build test / build-meson (archlinux) (push) Has been cancelled
Build test / build-meson (fedora) (push) Has been cancelled
Build test / build-meson (ubuntu) (push) Has been cancelled
Build test / clang64 (push) Has been cancelled
Build test / mingw32 (push) Has been cancelled
Build test / mingw64 (push) Has been cancelled
Build test / ucrt64 (push) Has been cancelled
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 <nathan@kernel.org>
Message-ID: <20260325-dtc-lexer-remove-dts_version-v1-1-0b5d64903bbb@kernel.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-03-27 20:20:54 +11:00
Herve Codina
caf7465c5d libfdt: fdt_check_full: Handle FDT_NOP when FDT_END is expected
Some checks failed
Build test / build-make (alpine) (push) Has been cancelled
Build test / build-make (archlinux) (push) Has been cancelled
Build test / build-make (fedora) (push) Has been cancelled
Build test / build-make (ubuntu) (push) Has been cancelled
Build test / build-meson (alpine) (push) Has been cancelled
Build test / build-meson (archlinux) (push) Has been cancelled
Build test / build-meson (fedora) (push) Has been cancelled
Build test / build-meson (ubuntu) (push) Has been cancelled
Build test / clang64 (push) Has been cancelled
Build test / mingw32 (push) Has been cancelled
Build test / mingw64 (push) Has been cancelled
Build test / ucrt64 (push) Has been cancelled
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 <herve.codina@bootlin.com>
Message-ID: <20260210173349.636766-8-herve.codina@bootlin.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-03-04 19:26:24 +11:00
Herve Codina
5976c4a660 libfdt: fdt_rw: Introduce fdt_downgrade_version()
Some checks failed
Build test / build-make (alpine) (push) Has been cancelled
Build test / build-make (archlinux) (push) Has been cancelled
Build test / build-make (fedora) (push) Has been cancelled
Build test / build-make (ubuntu) (push) Has been cancelled
Build test / build-meson (alpine) (push) Has been cancelled
Build test / build-meson (archlinux) (push) Has been cancelled
Build test / build-meson (fedora) (push) Has been cancelled
Build test / build-meson (ubuntu) (push) Has been cancelled
Build test / clang64 (push) Has been cancelled
Build test / mingw32 (push) Has been cancelled
Build test / mingw64 (push) Has been cancelled
Build test / ucrt64 (push) Has been cancelled
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 <herve.codina@bootlin.com>
Message-ID: <20260210173349.636766-5-herve.codina@bootlin.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-02-24 17:00:38 +11:00
Herve Codina
5bb5bedd34 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 <herve.codina@bootlin.com>
Message-ID: <20260210173349.636766-4-herve.codina@bootlin.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-02-24 16:52:47 +11:00
Herve Codina
68b960e299 fdtdump: Remove dtb version check
Some checks failed
Build test / build-make (alpine) (push) Has been cancelled
Build test / build-make (archlinux) (push) Has been cancelled
Build test / build-make (fedora) (push) Has been cancelled
Build test / build-make (ubuntu) (push) Has been cancelled
Build test / build-meson (alpine) (push) Has been cancelled
Build test / build-meson (archlinux) (push) Has been cancelled
Build test / build-meson (fedora) (push) Has been cancelled
Build test / build-meson (ubuntu) (push) Has been cancelled
Build test / clang64 (push) Has been cancelled
Build test / mingw32 (push) Has been cancelled
Build test / mingw64 (push) Has been cancelled
Build test / ucrt64 (push) Has been cancelled
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 <herve.codina@bootlin.com>
Message-ID: <20260210173349.636766-3-herve.codina@bootlin.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-02-14 13:03:18 +11:00
Herve Codina
adba02caf5 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 <herve.codina@bootlin.com>
Message-ID: <20260210173349.636766-2-herve.codina@bootlin.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-02-13 16:27:22 +11:00
David Gibson
8d15a63e84 libfdt: Verify alignment of sub-blocks in dtb
Some checks failed
Build test / build-make (alpine) (push) Has been cancelled
Build test / build-make (archlinux) (push) Has been cancelled
Build test / build-make (fedora) (push) Has been cancelled
Build test / build-make (ubuntu) (push) Has been cancelled
Build test / build-meson (alpine) (push) Has been cancelled
Build test / build-meson (archlinux) (push) Has been cancelled
Build test / build-meson (fedora) (push) Has been cancelled
Build test / build-meson (ubuntu) (push) Has been cancelled
Build test / clang64 (push) Has been cancelled
Build test / mingw32 (push) Has been cancelled
Build test / mingw64 (push) Has been cancelled
Build test / ucrt64 (push) Has been cancelled
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) 盛泽 <zesheng@tamu.edu>
Link: https://github.com/dgibson/dtc/issues/178
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2026-01-28 12:14:09 +11:00
38 changed files with 1490 additions and 675 deletions

View file

@ -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

View file

@ -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
@ -16,78 +18,93 @@ 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 }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Install Dependencies
run: ./scripts/install-deps.sh
- name: Build (Make)
if: matrix.build == 'make'
run: make
- name: Run check (Make)
if: matrix.build == 'make'
run: make check
- name: Setup (Meson)
if: matrix.build == 'meson'
run: meson setup -D python=enabled -D yaml=enabled build
- name: Build (Meson)
if: matrix.build == 'meson'
run: meson compile -C build
- 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:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
build: [ "make", "meson" ]
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Dependencies
run: |
./scripts/install-deps.sh
- name: Build
- name: Build (Make)
if: matrix.build == 'make'
run: |
make
- name: Run check
run: |
make check
- name: Setup (Meson)
if: matrix.build == 'meson'
run: meson setup -D python=enabled -D yaml=enabled -D tests=false build
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@v4
- name: Install Dependencies
run: |
./scripts/install-deps.sh
- name: Setup
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
run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi
build-windows:
runs-on: windows-latest
strategy:
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:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@ -105,10 +122,60 @@ jobs:
meson:p
ninja:p
libyaml:p
swig:p
python-setuptools-scm:p
${{ matrix.swig && 'swig:p python-setuptools-scm:p' || '' }}
- name: '🚧 Build'
run: |
meson setup -Dtools=true -Dtests=false build
meson compile -C build
build-freebsd:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version: [ "13.5", "14.3" ]
build: [ "make", "meson" ]
name: FreeBSD ${{ matrix.version }} ${{ matrix.build }} 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 (Make)
if: matrix.build == 'make'
shell: cpa.sh {0}
run: gmake
- name: Run check (Make)
if: matrix.build == 'make'
shell: cpa.sh {0}
run: gmake check
- name: Setup (Meson)
if: matrix.build == 'meson'
shell: cpa.sh {0}
run: meson setup -D python=enabled -D yaml=enabled build
- name: Build (Meson)
if: matrix.build == 'meson'
shell: cpa.sh {0}
run: meson compile -C build
- 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

138
AGENTS.md Normal file
View file

@ -0,0 +1,138 @@
# 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
- Do not add `Co-authored-by` tags.
## 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` and commit** — run the prepare script:
```sh
scripts/prepare-release X.Y.Z
```
This updates `VERSION.txt` and creates the version bump commit
(without `Signed-off-by`, per the AI contribution policy).
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
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`.
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`, 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, push, 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
- 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)

17
CLAUDE.md Normal file
View file

@ -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.

View file

@ -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
```

View file

@ -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
@ -255,29 +260,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 \

View file

@ -1 +1 @@
1.7.2
1.8.1

View file

@ -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",
@ -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",

View file

@ -39,8 +39,6 @@ extern bool treesource_error;
#define DPRINT(fmt, ...) do { } while (0)
#endif
static int dts_version = 1;
#define BEGIN_DEFAULT() DPRINT("<V1>\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;
}

2
dtc.h
View file

@ -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;

View file

@ -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);
}
}
@ -169,7 +168,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 ||

View file

@ -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)))

View file

@ -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))
@ -43,7 +45,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) {

View file

@ -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;

View file

@ -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;
}
@ -161,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;
@ -174,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);
@ -278,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)
{
@ -294,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)
{
@ -434,8 +462,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);
@ -493,12 +523,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));

View file

@ -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;
}

View file

@ -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);
@ -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);
@ -510,7 +515,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 +556,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 +754,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 +856,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 +929,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 +960,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 +1225,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 +1235,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 +1279,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 +1369,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 +1489,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)
@ -1930,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
@ -1997,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
@ -2084,7 +2080,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 +2290,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 +2401,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,

View file

@ -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",

98
scripts/finalize-release Executable file
View file

@ -0,0 +1,98 @@
#!/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 <tag-message-file>" >&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 ""
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] "
read answer
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" <<MAIL
To: devicetree-compiler@vger.kernel.org
Subject: DTC $VERSION released
DTC $VERSION is now available.
Git tree:
https://git.kernel.org/pub/scm/utils/dtc/dtc.git
Tagged release ($TAG):
https://git.kernel.org/pub/scm/utils/dtc/dtc.git/tag/?h=$TAG
Tarball:
https://kernel.org/pub/software/utils/dtc/dtc-${VERSION}.tar.gz
$TAG_BODY
MAIL
echo "Announcement draft written to $ANNOUNCE"

View file

@ -1,6 +1,19 @@
#!/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 \
python3 py312-setuptools swig libyaml pkgconf
exit 0
fi
if [ -f /etc/os-release ]
then
. /etc/os-release

70
scripts/prepare-release Executable file
View file

@ -0,0 +1,70 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Prepare a release: update VERSION.txt and create the version bump
# commit. This handles steps 1 and 2 of the release procedure in
# AGENTS.md. Step 3 (drafting the tag message) requires judgement to
# categorise and summarise changes, so is left to the caller.
set -e
usage() {
echo "Usage: $0 <version>" >&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 <<EOF
Bump version to $TAG
Prepare for a new release.
EOF
)"
echo "Building with make..."
git clean -fdx
make check
echo ""
echo "Building with meson..."
git clean -fdx
meson setup build
meson test -C build
echo ""
echo "All builds and tests passed."
echo "Next: draft the tag message to tag-message.txt, then run:"
echo " scripts/finalize-release tag-message.txt"

2
tests/.gitignore vendored
View file

@ -69,9 +69,11 @@ tmp.*
/supernode_atdepth_offset
/sw_tree1
/sw_states
/treegen
/truncated_property
/truncated_string
/truncated_memrsv
/unterminated_memrsv
/utilfdt_test
/value-labels
/get_next_tag_invalid_prop_len

View file

@ -29,33 +29,32 @@ LIB_TESTS_L = get_mem_rsv \
subnode_iterate \
overlay overlay_bad_fixup \
check_path check_header check_full \
fs_tree1
fs_tree1 \
truncated_property truncated_string \
truncated_memrsv unterminated_memrsv
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
LIBTREE_TESTS_L = truncated_property truncated_string truncated_memrsv
LIBTREE_TESTS = $(LIBTREE_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 \
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)
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
TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%)
TESTS_CLEANDIRS_L = fs
TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%)
@ -72,14 +71,9 @@ $(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
@$(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)"

View file

@ -1,69 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* dumptrees - utility for libfdt testing
*
* (C) Copyright David Gibson <dwg@au1.ibm.com>, IBM Corporation. 2006.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
#include <libfdt.h>
#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(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);
}

View file

@ -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));

View file

@ -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]))

View file

@ -1,14 +1,10 @@
trees = static_library('trees', files('trees.S'),
build_by_default: false,
include_directories: libfdt_inc)
treegen = executable('treegen', files('treegen.c'),
build_by_default: false,
dependencies: [util_dep])
dumptrees = executable('dumptrees', files('dumptrees.c'),
build_by_default: false,
link_with: trees, dependencies: libfdt_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',
@ -18,10 +14,13 @@ dumptrees_dtb = custom_target(
'truncated_property.dtb',
'truncated_string.dtb',
'truncated_memrsv.dtb',
'unterminated_memrsv.dtb',
'two_roots.dtb',
'named_root.dtb',
]
)
testutil_dep = declare_dependency(sources: ['testutils.c'], link_with: trees)
testutil_dep = declare_dependency(sources: ['testutils.c'])
tests = [
'add_subnode_with_nops',
@ -89,13 +88,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]
@ -140,10 +137,10 @@ run_test_types = [
'fdtget',
'fdtput',
'fdtdump',
'fdtoverlay'
'fdtoverlay',
]
run_test_deps = [
dtc_tools, dumptrees_dtb, tests_exe
dtc_tools, treegen_dtb, tests_exe,
]
if pylibfdt_enabled
run_test_types += 'pylibfdt'

View file

@ -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

View file

@ -492,9 +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 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"

View file

@ -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,16 +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 two_roots;
extern struct fdt_header named_root;
#endif /* ! __ASSEMBLER__ */

View file

@ -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)

View file

@ -42,29 +42,21 @@ 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)
{
}
static void sigint_handler(int signum, siginfo_t *si, void *uc)
{
cleanup();
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"))

756
tests/treegen.c Normal file
View file

@ -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 <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <libfdt_env.h>
#include <fdt.h>
#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 <output-dir>\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;
}

View file

@ -1,326 +0,0 @@
#include <fdt.h>
#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:
/* 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:

View file

@ -15,13 +15,17 @@
#include "tests.h"
#include "testdata.h"
#define SPACE 65536
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)
@ -46,5 +50,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();
}

View file

@ -13,17 +13,15 @@
#include <libfdt.h>
#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)

View file

@ -13,18 +13,16 @@
#include <libfdt.h>
#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);

View file

@ -0,0 +1,68 @@
// 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 <moshestrauss10@gmail.com>
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <libfdt.h>
#include "tests.h"
#include "testdata.h"
#define SPACE 65536
int main(int argc, char *argv[])
{
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)
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();
}