Compare commits

..

No commits in common. "66e1201c3775716607c28afd2bbb2b3afb08b695" and "47d7c01ba8a1241e919ab56dd01ba245b38fef8e" have entirely different histories.

33 changed files with 654 additions and 1343 deletions

39
.cirrus.yml Normal file
View file

@ -0,0 +1,39 @@
# 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,8 +8,6 @@ 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
@ -18,93 +16,78 @@ concurrency:
cancel-in-progress: true
jobs:
build-linux:
build-make:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [ "alpine", "archlinux", "fedora", "ubuntu" ]
build: [ "make", "meson" ]
os: [ "alpine", "archlinux", "fedora", "ubuntu" ]
container:
image: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install Dependencies
run: ./scripts/install-deps.sh
- name: 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
uses: actions/checkout@v4
- name: Install Dependencies
run: |
./scripts/install-deps.sh
- name: Build (Make)
if: matrix.build == 'make'
- name: Build
run: |
make
- name: Setup (Meson)
if: matrix.build == 'meson'
run: meson setup -D python=enabled -D yaml=enabled -D tests=false build
- name: Run check
run: |
make check
- name: Build (Meson)
if: matrix.build == 'meson'
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
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, swig: false }
- { sys: mingw64, swig: true }
- { sys: ucrt64, swig: true }
- { sys: clang64, swig: true }
- { sys: mingw32 }
- { sys: mingw64 }
- { sys: ucrt64 }
- { sys: clang64 }
name: ${{ matrix.sys }}
defaults:
run:
shell: msys2 {0}
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
fetch-depth: 0
@ -122,60 +105,10 @@ jobs:
meson:p
ninja:p
libyaml:p
${{ matrix.swig && 'swig:p python-setuptools-scm:p' || '' }}
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
View file

@ -1,138 +0,0 @@
# 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)

View file

@ -1,17 +0,0 @@
# 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,51 +77,3 @@ 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,11 +153,6 @@ 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
@ -260,6 +255,29 @@ 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.8.1
1.7.2

View file

@ -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, NULL, &phandle_references);
WARNING(interrupts_property, check_interrupts_property, &phandle_references);
static const struct bus_type graph_port_bus = {
.name = "graph-port",

View file

@ -21,8 +21,6 @@ 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))

View file

@ -292,13 +292,6 @@ 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)
{
@ -315,13 +308,6 @@ 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)
{

View file

@ -330,8 +330,7 @@ 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;
if (len)
memcpy(ptr, val, len);
memcpy(ptr, val, len);
return 0;
}

View file

@ -1935,8 +1935,12 @@ int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name,
* -FDT_ERR_BADLAYOUT,
* -FDT_ERR_TRUNCATED, standard meanings
*/
int fdt_setprop(void *fdt, int nodeoffset, const char *name,
const void *val, int len);
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);
}
/**
* fdt_setprop_placeholder_namelen - allocate space for a property
@ -1998,8 +2002,13 @@ int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name,
* -FDT_ERR_BADLAYOUT,
* -FDT_ERR_TRUNCATED, standard meanings
*/
int fdt_setprop_placeholder(void *fdt, int nodeoffset,
const char *name, int len, void **prop_data);
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);
}
/**
* 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;
offset = strtol(soffset, NULL, 0);
if (offset < 0 || (unsigned int)offset + 4 > p->val.len) {
if (offset < 0 || offset + 4 > p->val.len) {
if (quiet < 1)
fprintf(stderr,
"Warning: Label %s contains invalid offset for property %s in node %s\n",

View file

@ -1,98 +0,0 @@
#!/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,19 +1,6 @@
#!/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

View file

@ -1,70 +0,0 @@
#!/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,7 +69,6 @@ tmp.*
/supernode_atdepth_offset
/sw_tree1
/sw_states
/treegen
/truncated_property
/truncated_string
/truncated_memrsv

View file

@ -29,32 +29,34 @@ LIB_TESTS_L = get_mem_rsv \
subnode_iterate \
overlay overlay_bad_fixup \
check_path check_header check_full \
fs_tree1 \
truncated_property truncated_string \
truncated_memrsv unterminated_memrsv
fs_tree1
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
LIBTREE_TESTS_L = truncated_property truncated_string \
truncated_memrsv unterminated_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) $(DL_LIB_TESTS)
TESTS = $(LIB_TESTS) $(LIBTREE_TESTS) $(DL_LIB_TESTS)
TESTS_TREES_L = test_tree1.dtb bad_node_char.dtb bad_node_format.dtb \
bad_prop_char.dtb ovf_size_strings.dtb truncated_property.dtb \
truncated_string.dtb truncated_memrsv.dtb unterminated_memrsv.dtb \
two_roots.dtb named_root.dtb
truncated_string.dtb truncated_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)
$(addprefix $(TESTS_PREFIX),testutils.d trees.d dumptrees.d)
TESTS_CLEANFILES_L = $(STD_CLEANFILES) \
*.dtb *.test.dts *.test.dt.yaml *.dtsv1 tmp.* *.bak \
treegen
dumptrees
TESTS_CLEANFILES = $(TESTS) $(TESTS_CLEANFILES_L:%=$(TESTS_PREFIX)%)
TESTS_CLEANDIRS_L = fs
TESTS_CLEANDIRS = $(TESTS_CLEANDIRS_L:%=$(TESTS_PREFIX)%)
@ -71,9 +73,14 @@ $(DL_LIB_TESTS): %: %.o $(TESTS_PREFIX)testutils.o util.o $(LIBFDT_dep)
@$(VECHO) LD [libdl] $@
$(LINK.c) -o $@ $^ $(LIBDL)
$(TESTS_TREES): $(TESTS_PREFIX)treegen
@$(VECHO) TREEGEN
cd $(TESTS_PREFIX); ./treegen . >/dev/null
$(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_clean:
@$(VECHO) CLEAN "(tests)"

69
tests/dumptrees.c Normal file
View file

@ -0,0 +1,69 @@
// 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,11 +24,7 @@ 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,14 +17,6 @@
#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;
@ -59,7 +51,6 @@ 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,10 +1,14 @@
treegen = executable('treegen', files('treegen.c'),
build_by_default: false,
dependencies: [util_dep])
trees = static_library('trees', files('trees.S'),
build_by_default: false,
include_directories: libfdt_inc)
treegen_dtb = custom_target(
'treegen_dtb',
command: [treegen, meson.current_build_dir()],
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()],
output: [
'test_tree1.dtb',
'bad_node_char.dtb',
@ -14,13 +18,10 @@ treegen_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'])
testutil_dep = declare_dependency(sources: ['testutils.c'], link_with: trees)
tests = [
'add_subnode_with_nops',
@ -88,11 +89,14 @@ 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]
@ -137,10 +141,10 @@ run_test_types = [
'fdtget',
'fdtput',
'fdtdump',
'fdtoverlay',
'fdtoverlay'
]
run_test_deps = [
dtc_tools, treegen_dtb, tests_exe,
dtc_tools, dumptrees_dtb, tests_exe
]
if pylibfdt_enabled
run_test_types += 'pylibfdt'

View file

@ -13,15 +13,26 @@ 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_1 = 0x0000000000100000
TEST_ADDR_2 = 123456789
TEST_SIZE_2 = 0o10000
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_VALUE_1 = 0xdeadbeef
TEST_VALUE_2 = 123456789
TEST_VALUE64_1 = 0xdeadbeef01abcdef
TEST_VALUE64_1H = 0xdeadbeef
TEST_VALUE64_1L = 0x01abcdef
TEST_VALUE64_1 = (TEST_VALUE64_1H << 32) | TEST_VALUE64_1L
PHANDLE_1 = 0x2000
PHANDLE_2 = 0x2001

View file

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

View file

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

View file

@ -25,6 +25,9 @@ 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__); \
@ -36,18 +39,21 @@ void test_init(int argc, char *argv[]);
#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)
@ -55,18 +61,21 @@ void test_init(int argc, char *argv[]);
/* 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,21 +42,29 @@ static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len)
int verbose_test = 1;
char *test_name;
static void sigint_handler(int signum)
void __attribute__((weak)) cleanup(void)
{
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);
}
void test_init(int argc, char *argv[])
{
int err;
struct sigaction sa_int = {
.sa_sigaction = sigint_handler,
};
test_name = argv[0];
/* Use signal(), not sigaction() because it also works on the Windows /
* mingw environments we support.
*/
if (signal(SIGINT, sigint_handler) == SIG_ERR)
err = sigaction(SIGINT, &sa_int, NULL);
if (err)
FAIL("Can't install SIGINT handler");
if (getenv("QUIET_TEST"))

View file

@ -1,756 +0,0 @@
// 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;
}

343
tests/trees.S Normal file
View file

@ -0,0 +1,343 @@
#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,13 +19,12 @@
int main(int argc, char *argv[])
{
void *fdt;
void *fdt = &truncated_memrsv;
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)

View file

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

View file

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

View file

@ -22,13 +22,12 @@
int main(int argc, char *argv[])
{
void *fdt;
void *fdt = &unterminated_memrsv;
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)