Compare commits

...

26 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
33 changed files with 1343 additions and 654 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: pull_request:
branches: branches:
- main - main
permissions:
contents: read
# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel # 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 # and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
@ -16,78 +18,93 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
build-make: build-linux:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ "alpine", "archlinux", "fedora", "ubuntu" ] os: [ "alpine", "archlinux", "fedora", "ubuntu" ]
build: [ "make", "meson" ]
container: container:
image: ${{ matrix.os }} image: ${{ matrix.os }}
steps: steps:
- name: Checkout - 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 - name: Install Dependencies
run: | run: |
./scripts/install-deps.sh ./scripts/install-deps.sh
- name: Build - name: Build (Make)
if: matrix.build == 'make'
run: | run: |
make make
- name: Run check - name: Setup (Meson)
run: | if: matrix.build == 'meson'
make check run: meson setup -D python=enabled -D yaml=enabled -D tests=false build
build-meson: - name: Build (Meson)
runs-on: ubuntu-latest if: matrix.build == 'meson'
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
run: meson compile -C build 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: build-windows:
runs-on: windows-latest runs-on: windows-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- { sys: mingw32 } - { sys: mingw32, swig: false }
- { sys: mingw64 } - { sys: mingw64, swig: true }
- { sys: ucrt64 } - { sys: ucrt64, swig: true }
- { sys: clang64 } - { sys: clang64, swig: true }
name: ${{ matrix.sys }} name: ${{ matrix.sys }}
defaults: defaults:
run: run:
shell: msys2 {0} shell: msys2 {0}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v6
with: with:
fetch-depth: 0 fetch-depth: 0
@ -105,10 +122,60 @@ jobs:
meson:p meson:p
ninja:p ninja:p
libyaml:p libyaml:p
swig:p ${{ matrix.swig && 'swig:p python-setuptools-scm:p' || '' }}
python-setuptools-scm:p
- name: '🚧 Build' - name: '🚧 Build'
run: | run: |
meson setup -Dtools=true -Dtests=false build meson setup -Dtools=true -Dtests=false build
meson compile -C 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 in its development. SoB chains should reflect the **real** route a
patch took as it was propagated to the maintainers, with the first SoB patch took as it was propagated to the maintainers, with the first SoB
entry signalling primary authorship of a single author. 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.dtc
include Makefile.utils 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 += convert-dtsv0
BIN += dtc BIN += dtc
BIN += fdtdump BIN += fdtdump
@ -255,29 +260,6 @@ fdtput: $(FDTPUT_OBJS) $(LIBFDT_dep)
fdtoverlay: $(FDTOVERLAY_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 tags: FORCE
rm -f tags rm -f tags
find . \( -name tests -type d -prune \) -o \ find . \( -name tests -type d -prune \) -o \

View file

@ -1 +1 @@
1.7.2 1.8.1

View file

@ -1769,7 +1769,7 @@ static void check_interrupts_property(struct check *c,
irq_prop->val.len, (int)(irq_cells * sizeof(cell_t))); 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 = { static const struct bus_type graph_port_bus = {
.name = "graph-port", .name = "graph-port",

View file

@ -21,6 +21,8 @@ int fdt_check_full(const void *fdt, size_t bufsize)
const char *propname; const char *propname;
bool expect_end = false; bool expect_end = false;
if (can_assume(PERFECT))
return 0;
if (bufsize < FDT_V1_SIZE) if (bufsize < FDT_V1_SIZE)
return -FDT_ERR_TRUNCATED; return -FDT_ERR_TRUNCATED;
if (bufsize < fdt_header_size(fdt)) if (bufsize < fdt_header_size(fdt))

View file

@ -292,6 +292,13 @@ int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
return 0; 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 fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
int namelen, const void *val, int len) int namelen, const void *val, int len)
{ {
@ -308,6 +315,13 @@ int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
return 0; 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, int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
const void *val, int len) const void *val, int len)
{ {

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); ret = fdt_property_placeholder(fdt, name, len, &ptr);
if (ret) if (ret)
return ret; return ret;
memcpy(ptr, val, len); if (len)
memcpy(ptr, val, len);
return 0; return 0;
} }

View file

@ -1935,12 +1935,8 @@ int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
* -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADLAYOUT,
* -FDT_ERR_TRUNCATED, standard meanings * -FDT_ERR_TRUNCATED, standard meanings
*/ */
static inline int fdt_setprop(void *fdt, int nodeoffset, const char *name, int fdt_setprop(void *fdt, int nodeoffset, const char *name,
const void *val, int len) const void *val, int len);
{
return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val,
len);
}
/** /**
* fdt_setprop_placeholder_namelen - allocate space for a property * fdt_setprop_placeholder_namelen - allocate space for a property
@ -2002,13 +1998,8 @@ int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
* -FDT_ERR_BADLAYOUT, * -FDT_ERR_BADLAYOUT,
* -FDT_ERR_TRUNCATED, standard meanings * -FDT_ERR_TRUNCATED, standard meanings
*/ */
static inline int fdt_setprop_placeholder(void *fdt, int nodeoffset, int fdt_setprop_placeholder(void *fdt, int nodeoffset,
const char *name, int len, const char *name, int len, void **prop_data);
void **prop_data)
{
return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name,
strlen(name), len, prop_data);
}
/** /**
* fdt_setprop_u32 - set a property to a 32-bit integer * fdt_setprop_u32 - set a property to a 32-bit integer

View file

@ -1234,7 +1234,7 @@ void fixup_phandles(struct dt_info *dti, const char *name)
continue; continue;
offset = strtol(soffset, NULL, 0); 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) if (quiet < 1)
fprintf(stderr, fprintf(stderr,
"Warning: Label %s contains invalid offset for property %s in node %s\n", "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 #!/bin/sh
# SPDX-License-Identifier: GPL-2.0-or-later # 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 ] if [ -f /etc/os-release ]
then then
. /etc/os-release . /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"

1
tests/.gitignore vendored
View file

@ -69,6 +69,7 @@ tmp.*
/supernode_atdepth_offset /supernode_atdepth_offset
/sw_tree1 /sw_tree1
/sw_states /sw_states
/treegen
/truncated_property /truncated_property
/truncated_string /truncated_string
/truncated_memrsv /truncated_memrsv

View file

@ -29,34 +29,32 @@ LIB_TESTS_L = get_mem_rsv \
subnode_iterate \ subnode_iterate \
overlay overlay_bad_fixup \ overlay overlay_bad_fixup \
check_path check_header check_full \ check_path check_header check_full \
fs_tree1 fs_tree1 \
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%) truncated_property truncated_string \
LIBTREE_TESTS_L = truncated_property truncated_string \
truncated_memrsv unterminated_memrsv truncated_memrsv unterminated_memrsv
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
ifneq ($(STATIC_BUILD),1) ifneq ($(STATIC_BUILD),1)
DL_LIB_TESTS_L = asm_tree_dump value-labels DL_LIB_TESTS_L = asm_tree_dump value-labels
DL_LIB_TESTS = $(DL_LIB_TESTS_L:%=$(TESTS_PREFIX)%) DL_LIB_TESTS = $(DL_LIB_TESTS_L:%=$(TESTS_PREFIX)%)
endif 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 \ 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 \ 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_TREES = $(TESTS_TREES_L:%=$(TESTS_PREFIX)%)
TESTS_TARGETS = $(TESTS) $(TESTS_TREES) TESTS_TARGETS = $(TESTS) $(TESTS_TREES)
TESTS_DEPFILES = $(TESTS:%=%.d) \ TESTS_DEPFILES = $(TESTS:%=%.d) \
$(addprefix $(TESTS_PREFIX),testutils.d trees.d dumptrees.d) $(addprefix $(TESTS_PREFIX),testutils.d)
TESTS_CLEANFILES_L = $(STD_CLEANFILES) \ TESTS_CLEANFILES_L = $(STD_CLEANFILES) \
*.dtb *.test.dts *.test.dt.yaml *.dtsv1 tmp.* *.bak \ *.dtb *.test.dts *.test.dt.yaml *.dtsv1 tmp.* *.bak \
dumptrees treegen
TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%) TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%)
TESTS_CLEANDIRS_L = fs TESTS_CLEANDIRS_L = fs
TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%) TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%)
@ -73,14 +71,9 @@ $(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep)
@$(VECHO) LD [libdl] $@ @$(VECHO) LD [libdl] $@
$(LINK.c) -o $@ $^ $(LIBDL) $(LINK.c) -o $@ $^ $(LIBDL)
$(LIBTREE_TESTS): %: $(TESTS_PREFIX)testutils.o $(TESTS_PREFIX)trees.o \ $(TESTS_TREES): $(TESTS_PREFIX)treegen
util.o $(LIBFDT_dep) @$(VECHO) TREEGEN
cd $(TESTS_PREFIX); ./treegen . >/dev/null
$(TESTS_PREFIX)dumptrees: $(TESTS_PREFIX)trees.o
$(TESTS_TREES): $(TESTS_PREFIX)dumptrees
@$(VECHO) DUMPTREES
cd $(TESTS_PREFIX); ./dumptrees . >/dev/null
tests_clean: tests_clean:
@$(VECHO) CLEAN "(tests)" @$(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; int rc;
#ifndef _WIN32
rc = mkdir(name, 0777); rc = mkdir(name, 0777);
#else
rc = mkdir(name);
#endif
if (rc != 0) if (rc != 0)
FAIL("mkdir(\"%s\"): %s", name, strerror(errno)); FAIL("mkdir(\"%s\"): %s", name, strerror(errno));

View file

@ -17,6 +17,14 @@
#include "tests.h" #include "tests.h"
#include "testdata.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 { static struct test_expr {
const char *expr; const char *expr;
uint32_t result; uint32_t result;
@ -51,6 +59,7 @@ static struct test_expr {
TE(11 * 257 * 1321517ULL), TE(11 * 257 * 1321517ULL),
TE(123456790 - 4/2 + 17%4), TE(123456790 - 4/2 + 17%4),
}; };
#pragma GCC diagnostic pop
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

View file

@ -1,14 +1,10 @@
trees = static_library('trees', files('trees.S'), treegen = executable('treegen', files('treegen.c'),
build_by_default: false, build_by_default: false,
include_directories: libfdt_inc) dependencies: [util_dep])
dumptrees = executable('dumptrees', files('dumptrees.c'), treegen_dtb = custom_target(
build_by_default: false, 'treegen_dtb',
link_with: trees, dependencies: libfdt_dep) command: [treegen, meson.current_build_dir()],
dumptrees_dtb = custom_target(
'dumptrees',
command: [dumptrees, meson.current_build_dir()],
output: [ output: [
'test_tree1.dtb', 'test_tree1.dtb',
'bad_node_char.dtb', 'bad_node_char.dtb',
@ -18,10 +14,13 @@ dumptrees_dtb = custom_target(
'truncated_property.dtb', 'truncated_property.dtb',
'truncated_string.dtb', 'truncated_string.dtb',
'truncated_memrsv.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 = [ tests = [
'add_subnode_with_nops', 'add_subnode_with_nops',
@ -89,14 +88,11 @@ tests = [
'supernode_atdepth_offset', 'supernode_atdepth_offset',
'sw_states', 'sw_states',
'sw_tree1', 'sw_tree1',
'utilfdt_test',
]
tests += [
'truncated_memrsv', 'truncated_memrsv',
'truncated_property', 'truncated_property',
'truncated_string', 'truncated_string',
'unterminated_memrsv', 'unterminated_memrsv',
'utilfdt_test',
] ]
test_deps = [testutil_dep, util_dep, libfdt_dep] test_deps = [testutil_dep, util_dep, libfdt_dep]
@ -141,10 +137,10 @@ run_test_types = [
'fdtget', 'fdtget',
'fdtput', 'fdtput',
'fdtdump', 'fdtdump',
'fdtoverlay' 'fdtoverlay',
] ]
run_test_deps = [ run_test_deps = [
dtc_tools, dumptrees_dtb, tests_exe dtc_tools, treegen_dtb, tests_exe,
] ]
if pylibfdt_enabled if pylibfdt_enabled
run_test_types += 'pylibfdt' run_test_types += 'pylibfdt'

View file

@ -13,26 +13,15 @@ sys.path.insert(0, '../pylibfdt')
import libfdt import libfdt
from libfdt import Fdt, FdtSw, FdtException, QUIET_NOTFOUND, QUIET_ALL 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_ADDR_1 = 0x8000000000000000
TEST_SIZE_1H = 0x00000000 TEST_SIZE_1 = 0x0000000000100000
TEST_SIZE_1L = 0x00100000 TEST_ADDR_2 = 123456789
TEST_SIZE_1 = (TEST_SIZE_1H << 32) | TEST_SIZE_1L TEST_SIZE_2 = 0o10000
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_VALUE_1 = 0xdeadbeef TEST_VALUE_1 = 0xdeadbeef
TEST_VALUE_2 = 123456789 TEST_VALUE_2 = 123456789
TEST_VALUE64_1H = 0xdeadbeef TEST_VALUE64_1 = 0xdeadbeef01abcdef
TEST_VALUE64_1L = 0x01abcdef
TEST_VALUE64_1 = (TEST_VALUE64_1H << 32) | TEST_VALUE64_1L
PHANDLE_1 = 0x2000 PHANDLE_1 = 0x2000
PHANDLE_2 = 0x2001 PHANDLE_2 = 0x2001

View file

@ -492,10 +492,10 @@ libfdt_tests () {
run_test appendprop_addrrange unit-addr-without-reg.dtb 2 1 3 run_test appendprop_addrrange unit-addr-without-reg.dtb 2 1 3
# Tests for behaviour on various sorts of corrupted trees # Tests for behaviour on various sorts of corrupted trees
run_test truncated_property run_test truncated_property truncated_property.dtb
run_test truncated_string run_test truncated_string truncated_string.dtb
run_test truncated_memrsv run_test truncated_memrsv truncated_memrsv.dtb
run_test unterminated_memrsv run_test unterminated_memrsv unterminated_memrsv.dtb
# Check aliases support in fdt_path_offset # Check aliases support in fdt_path_offset
run_dtc_test -I dts -O dtb -o aliases.dtb "$SRCDIR/aliases.dts" run_dtc_test -I dts -O dtb -o aliases.dtb "$SRCDIR/aliases.dts"

View file

@ -1,28 +1,12 @@
#ifdef __ASSEMBLER__ #define TEST_ADDR_1 0xdeadbeef00000000ULL
#define ASM_CONST_LL(x) (x) #define TEST_SIZE_1 0x0000000000100000ULL
#else #define TEST_ADDR_2 123456789ULL
#define ASM_CONST_LL(x) (x##ULL) #define TEST_SIZE_2 010000ULL
#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_VALUE_1 0xdeadbeef #define TEST_VALUE_1 0xdeadbeef
#define TEST_VALUE_2 123456789 #define TEST_VALUE_2 123456789
#define TEST_VALUE64_1H ASM_CONST_LL(0xdeadbeef) #define TEST_VALUE64_1 0xdeadbeef01abcdefULL
#define TEST_VALUE64_1L ASM_CONST_LL(0x01abcdef)
#define TEST_VALUE64_1 ((TEST_VALUE64_1H << 32) | TEST_VALUE64_1L)
#define PHANDLE_1 0x2000 #define PHANDLE_1 0x2000
#define PHANDLE_2 0x2001 #define PHANDLE_2 0x2001
@ -45,17 +29,3 @@
#define TEST_MEMREGION_SIZE 0x9abcdef0 #define TEST_MEMREGION_SIZE 0x9abcdef0
#define TEST_MEMREGION_SIZE_HI 0x0fedcba900000000 #define TEST_MEMREGION_SIZE_HI 0x0fedcba900000000
#define TEST_MEMREGION_SIZE_INC 0x1000 #define TEST_MEMREGION_SIZE_INC 0x1000
#ifndef __ASSEMBLER__
extern struct fdt_header test_tree1;
extern struct fdt_header truncated_property;
extern struct fdt_header bad_node_char;
extern struct fdt_header bad_node_format;
extern struct fdt_header bad_prop_char;
extern struct fdt_header ovf_size_strings;
extern struct fdt_header truncated_string;
extern struct fdt_header truncated_memrsv;
extern struct fdt_header unterminated_memrsv;
extern struct fdt_header two_roots;
extern struct fdt_header named_root;
#endif /* ! __ASSEMBLER__ */

View file

@ -25,9 +25,6 @@ void test_init(int argc, char *argv[]);
#define streq(s1, s2) (strcmp((s1),(s2)) == 0) #define streq(s1, s2) (strcmp((s1),(s2)) == 0)
/* Each test case must define this function */
void cleanup(void);
#define verbose_printf(...) \ #define verbose_printf(...) \
if (verbose_test) { \ if (verbose_test) { \
printf(__VA_ARGS__); \ printf(__VA_ARGS__); \
@ -39,21 +36,18 @@ void cleanup(void);
#define PASS() \ #define PASS() \
do { \ do { \
cleanup(); \
printf("PASS\n"); \ printf("PASS\n"); \
exit(RC_PASS); \ exit(RC_PASS); \
} while (0) } while (0)
#define PASS_INCONCLUSIVE() \ #define PASS_INCONCLUSIVE() \
do { \ do { \
cleanup(); \
printf("PASS (inconclusive)\n"); \ printf("PASS (inconclusive)\n"); \
exit(RC_PASS); \ exit(RC_PASS); \
} while (0) } while (0)
#define IRRELEVANT() \ #define IRRELEVANT() \
do { \ do { \
cleanup(); \
printf("PASS (irrelevant)\n"); \ printf("PASS (irrelevant)\n"); \
exit(RC_PASS); \ exit(RC_PASS); \
} while (0) } while (0)
@ -61,21 +55,18 @@ void cleanup(void);
/* Look out, gcc extension below... */ /* Look out, gcc extension below... */
#define FAIL(fmt, ...) \ #define FAIL(fmt, ...) \
do { \ do { \
cleanup(); \
printf("FAIL\t" fmt "\n", ##__VA_ARGS__); \ printf("FAIL\t" fmt "\n", ##__VA_ARGS__); \
exit(RC_FAIL); \ exit(RC_FAIL); \
} while (0) } while (0)
#define CONFIG(fmt, ...) \ #define CONFIG(fmt, ...) \
do { \ do { \
cleanup(); \
printf("Bad configuration: " fmt "\n", ##__VA_ARGS__); \ printf("Bad configuration: " fmt "\n", ##__VA_ARGS__); \
exit(RC_CONFIG); \ exit(RC_CONFIG); \
} while (0) } while (0)
#define TEST_BUG(fmt, ...) \ #define TEST_BUG(fmt, ...) \
do { \ do { \
cleanup(); \
printf("BUG in testsuite: " fmt "\n", ##__VA_ARGS__); \ printf("BUG in testsuite: " fmt "\n", ##__VA_ARGS__); \
exit(RC_BUG); \ exit(RC_BUG); \
} while (0) } while (0)

View file

@ -42,29 +42,21 @@ static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len)
int verbose_test = 1; int verbose_test = 1;
char *test_name; char *test_name;
void __attribute__((weak)) cleanup(void) static void sigint_handler(int signum)
{ {
} fprintf(stderr, "%s: signal %d (pid=%d)\n", test_name,
signum, getpid());
static void sigint_handler(int signum, siginfo_t *si, void *uc)
{
cleanup();
fprintf(stderr, "%s: %s (pid=%d)\n", test_name,
strsignal(signum), getpid());
exit(RC_BUG); exit(RC_BUG);
} }
void test_init(int argc, char *argv[]) void test_init(int argc, char *argv[])
{ {
int err;
struct sigaction sa_int = {
.sa_sigaction = sigint_handler,
};
test_name = argv[0]; test_name = argv[0];
err = sigaction(SIGINT, &sa_int, NULL); /* Use signal(), not sigaction() because it also works on the Windows /
if (err) * mingw environments we support.
*/
if (signal(SIGINT, sigint_handler) == SIG_ERR)
FAIL("Can't install SIGINT handler"); FAIL("Can't install SIGINT handler");
if (getenv("QUIET_TEST")) 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,343 +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:
/* unterminated_memrsv */
treehdr unterminated_memrsv
unterminated_memrsv_rsvmap:
rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L
unterminated_memrsv_rsvmap_end:
unterminated_memrsv_struct:
beginn ""
endn
fdtlong FDT_END
unterminated_memrsv_struct_end:
unterminated_memrsv_strings:
unterminated_memrsv_strings_end:
unterminated_memrsv_end:
/* two root nodes */
treehdr two_roots
empty_rsvmap two_roots
two_roots_struct:
beginn ""
endn
beginn ""
endn
fdtlong FDT_END
two_roots_struct_end:
two_roots_strings:
two_roots_strings_end:
two_roots_end:
/* root node with a non-empty name */
treehdr named_root
empty_rsvmap named_root
named_root_struct:
beginn "fake"
endn
fdtlong FDT_END
named_root_struct_end:
named_root_strings:
named_root_strings_end:
named_root_end:

View file

@ -19,12 +19,13 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
void *fdt = &truncated_memrsv; void *fdt;
void *buf; void *buf;
int err; int err;
uint64_t addr, size; uint64_t addr, size;
test_init(argc, argv); test_init(argc, argv);
fdt = load_blob_arg(argc, argv);
err = fdt_check_header(fdt); err = fdt_check_header(fdt);
if (err != 0) if (err != 0)

View file

@ -13,17 +13,15 @@
#include <libfdt.h> #include <libfdt.h>
#include "tests.h" #include "tests.h"
#include "testdata.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
void *fdt = &truncated_property; void *fdt;
const void *prop; const void *prop;
int len; int len;
test_init(argc, argv); test_init(argc, argv);
fdt = load_blob_arg(argc, argv);
vg_prepare_blob(fdt, fdt_totalsize(fdt));
prop = fdt_getprop(fdt, 0, "truncated", &len); prop = fdt_getprop(fdt, 0, "truncated", &len);
if (prop) if (prop)

View file

@ -13,18 +13,16 @@
#include <libfdt.h> #include <libfdt.h>
#include "tests.h" #include "tests.h"
#include "testdata.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
void *fdt = &truncated_string; void *fdt;
const struct fdt_property *good, *bad; const struct fdt_property *good, *bad;
int off, len; int off, len;
const char *name; const char *name;
test_init(argc, argv); test_init(argc, argv);
fdt = load_blob_arg(argc, argv);
vg_prepare_blob(fdt, fdt_totalsize(fdt));
off = fdt_first_property_offset(fdt, 0); off = fdt_first_property_offset(fdt, 0);
good = fdt_get_property_by_offset(fdt, off, NULL); good = fdt_get_property_by_offset(fdt, off, NULL);

View file

@ -22,12 +22,13 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
void *fdt = &unterminated_memrsv; void *fdt;
void *buf; void *buf;
int err; int err;
uint64_t addr, size; uint64_t addr, size;
test_init(argc, argv); test_init(argc, argv);
fdt = load_blob_arg(argc, argv);
err = fdt_check_header(fdt); err = fdt_check_header(fdt);
if (err != 0) if (err != 0)