diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 825aeb7..0000000 --- a/.cirrus.yml +++ /dev/null @@ -1,39 +0,0 @@ -# FreeBSD build with multiple versions -freebsd_versions_task: - name: FreeBSD $FREEBSD_VERSION make build - freebsd_instance: - image_family: $FREEBSD_IMAGE - matrix: - - env: - FREEBSD_VERSION: "13.5" - FREEBSD_IMAGE: freebsd-13-5 - - env: - FREEBSD_VERSION: "14.3" - FREEBSD_IMAGE: freebsd-14-3 - install_script: - - pkg install -y git gmake flex bison python3 py312-setuptools swig libyaml pkgconf - build_script: - - gmake - check_script: - - gmake check - -# FreeBSD meson builds with multiple versions -freebsd_meson_versions_task: - name: FreeBSD $FREEBSD_VERSION meson build - freebsd_instance: - image_family: $FREEBSD_IMAGE - matrix: - - env: - FREEBSD_VERSION: "13.5" - FREEBSD_IMAGE: freebsd-13-5 - - env: - FREEBSD_VERSION: "14.3" - FREEBSD_IMAGE: freebsd-14-3 - install_script: - - pkg install -y git meson ninja flex bison python3 py312-setuptools swig libyaml pkgconf - setup_script: - - meson setup -D python=enabled -D yaml=enabled build - build_script: - - meson compile -C build - test_script: - - if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b6e0a9c..b788558 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,8 @@ name: Build test pull_request: branches: - main +permissions: + contents: read # ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel # and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency @@ -16,78 +18,93 @@ concurrency: cancel-in-progress: true jobs: - build-make: + build-linux: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ "alpine", "archlinux", "fedora", "ubuntu" ] + os: [ "alpine", "archlinux", "fedora", "ubuntu" ] + build: [ "make", "meson" ] container: image: ${{ matrix.os }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 + + - name: Install Dependencies + run: ./scripts/install-deps.sh + + - name: Build (Make) + if: matrix.build == 'make' + run: make + + - name: Run check (Make) + if: matrix.build == 'make' + run: make check + + - name: Setup (Meson) + if: matrix.build == 'meson' + run: meson setup -D python=enabled -D yaml=enabled build + + - name: Build (Meson) + if: matrix.build == 'meson' + run: meson compile -C build + + - name: Run check (Meson) + if: matrix.build == 'meson' + run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi + + build-macos: + runs-on: macos-latest + + strategy: + fail-fast: false + matrix: + build: [ "make", "meson" ] + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 - name: Install Dependencies run: | ./scripts/install-deps.sh - - name: Build + - name: Build (Make) + if: matrix.build == 'make' run: | make - - name: Run check - run: | - make check + - name: Setup (Meson) + if: matrix.build == 'meson' + run: meson setup -D python=enabled -D yaml=enabled -D tests=false build - build-meson: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - os: [ "alpine", "archlinux", "fedora", "ubuntu" ] - - container: - image: ${{ matrix.os }} - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install Dependencies - run: | - ./scripts/install-deps.sh - - - name: Setup - run: meson setup -D python=enabled -D yaml=enabled build - - - name: Build + - name: Build (Meson) + if: matrix.build == 'meson' run: meson compile -C build - - name: Run check - run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi - build-windows: runs-on: windows-latest strategy: fail-fast: false matrix: include: - - { sys: mingw32 } - - { sys: mingw64 } - - { sys: ucrt64 } - - { sys: clang64 } + - { sys: mingw32, swig: false } + - { sys: mingw64, swig: true } + - { sys: ucrt64, swig: true } + - { sys: clang64, swig: true } name: ${{ matrix.sys }} defaults: run: shell: msys2 {0} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 @@ -105,10 +122,60 @@ jobs: meson:p ninja:p libyaml:p - swig:p - python-setuptools-scm:p + ${{ matrix.swig && 'swig:p python-setuptools-scm:p' || '' }} - name: '🚧 Build' run: | meson setup -Dtools=true -Dtests=false build meson compile -C build + + build-freebsd: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + version: [ "13.5", "14.3" ] + build: [ "make", "meson" ] + + name: FreeBSD ${{ matrix.version }} ${{ matrix.build }} build + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Start VM + uses: cross-platform-actions/action@v1 + with: + operating_system: freebsd + version: ${{ matrix.version }} + shell: sh + + - name: Install Dependencies + shell: cpa.sh {0} + run: sudo ./scripts/install-deps.sh + + - name: Build (Make) + if: matrix.build == 'make' + shell: cpa.sh {0} + run: gmake + + - name: Run check (Make) + if: matrix.build == 'make' + shell: cpa.sh {0} + run: gmake check + + - name: Setup (Meson) + if: matrix.build == 'meson' + shell: cpa.sh {0} + run: meson setup -D python=enabled -D yaml=enabled build + + - name: Build (Meson) + if: matrix.build == 'meson' + shell: cpa.sh {0} + run: meson compile -C build + + - name: Run check (Meson) + if: matrix.build == 'meson' + shell: cpa.sh {0} + run: if ! meson test -C build; then cat build/meson-logs/testlog.txt; false; fi diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2f0264f --- /dev/null +++ b/AGENTS.md @@ -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) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..2b87faa --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,17 @@ +# CLAUDE.md + +Read and follow AGENTS.md — it has build instructions, architecture +notes, coding conventions, and the AI contribution policy for this +project. + +## Claude Code Specifics + +When writing `Assisted-by` commit trailers, use `Claude` as the agent +name and the model's short ID as the version. For example: + +``` +Assisted-by: Claude:claude-opus-4-6 +``` + +Do not use `claude-code`, `Claude Code`, or other variations as the +agent name. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48c8efd..9742dc3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,3 +77,51 @@ from people handling and transporting the patch, but were not involved in its development. SoB chains should reflect the **real** route a patch took as it was propagated to the maintainers, with the first SoB entry signalling primary authorship of a single author. + +## AI Coding Assistants + +AI tools helping with dtc/libfdt development must follow the standard +contribution process described in this document. + +### Licensing and Legal Requirements + +All contributions must comply with the project's licensing requirements: + +* All code must be compatible with GPL-2.0-or-later + * All libfdt code must also be compatible with BSD-2-Clause +* Use appropriate SPDX license identifiers + +### Signed-off-by and Developer Certificate of Origin + +AI agents MUST NOT add Signed-off-by tags. Only humans can legally +certify the Developer Certificate of Origin (DCO). The human submitter +is responsible for: + +* Reviewing all AI-generated code +* Ensuring compliance with licensing requirements +* Adding their own Signed-off-by tag to certify the DCO +* Taking full responsibility for the contribution + +### Attribution + +When AI tools contribute to development, proper attribution helps track +the evolving role of AI in the development process. Contributions +should include an `Assisted-by` tag in the following format: + +``` +Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2] +``` + +Where: + +* `AGENT_NAME` is the name of the AI tool or framework +* `MODEL_VERSION` is the specific model version used +* `[TOOL1] [TOOL2]` are optional specialized analysis tools used + (e.g., coccinelle, sparse, smatch, clang-tidy) + +Basic development tools (git, gcc, make, editors) should not be listed. + +Example: +``` +Assisted-by: Claude:claude-3-opus coccinelle sparse +``` diff --git a/Makefile b/Makefile index 83d8220..9110d17 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,11 @@ include Makefile.convert-dtsv0 include Makefile.dtc include Makefile.utils +# Flex-generated source triggers -Wsign-compare in its templates +dtc-lexer.lex.o: CFLAGS += -Wno-sign-compare +convert-dtsv0-lexer.lex.o: CFLAGS += -Wno-sign-compare + + BIN += convert-dtsv0 BIN += dtc BIN += fdtdump @@ -255,29 +260,6 @@ fdtput: $(FDTPUT_OBJS) $(LIBFDT_dep) fdtoverlay: $(FDTOVERLAY_OBJS) $(LIBFDT_dep) -dist: - git archive --format=tar --prefix=dtc-$(dtc_version)/ HEAD \ - > ../dtc-$(dtc_version).tar - cat ../dtc-$(dtc_version).tar | \ - gzip -9 > ../dtc-$(dtc_version).tar.gz - - -# -# Release signing and uploading -# This is for maintainer convenience, don't try this at home. -# -ifeq ($(MAINTAINER),y) -GPG = gpg2 -KUP = kup -KUPDIR = /pub/software/utils/dtc - -kup: dist - $(GPG) --detach-sign --armor -o ../dtc-$(dtc_version).tar.sign \ - ../dtc-$(dtc_version).tar - $(KUP) put ../dtc-$(dtc_version).tar.gz ../dtc-$(dtc_version).tar.sign \ - $(KUPDIR)/dtc-$(dtc_version).tar.gz -endif - tags: FORCE rm -f tags find . \( -name tests -type d -prune \) -o \ diff --git a/VERSION.txt b/VERSION.txt index f8a696c..a8fdfda 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.7.2 +1.8.1 diff --git a/checks.c b/checks.c index 946c142..a6037ed 100644 --- a/checks.c +++ b/checks.c @@ -1769,7 +1769,7 @@ static void check_interrupts_property(struct check *c, irq_prop->val.len, (int)(irq_cells * sizeof(cell_t))); } } -WARNING(interrupts_property, check_interrupts_property, &phandle_references); +WARNING(interrupts_property, check_interrupts_property, NULL, &phandle_references); static const struct bus_type graph_port_bus = { .name = "graph-port", diff --git a/libfdt/fdt_check.c b/libfdt/fdt_check.c index cca0523..2fd5b61 100644 --- a/libfdt/fdt_check.c +++ b/libfdt/fdt_check.c @@ -21,6 +21,8 @@ int fdt_check_full(const void *fdt, size_t bufsize) const char *propname; bool expect_end = false; + if (can_assume(PERFECT)) + return 0; if (bufsize < FDT_V1_SIZE) return -FDT_ERR_TRUNCATED; if (bufsize < fdt_header_size(fdt)) diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c index bea45ed..850aafe 100644 --- a/libfdt/fdt_rw.c +++ b/libfdt/fdt_rw.c @@ -292,6 +292,13 @@ int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name, return 0; } +int fdt_setprop_placeholder(void *fdt, int nodeoffset, + const char *name, int len, void **prop_data) +{ + return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, + strlen(name), len, prop_data); +} + int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, int namelen, const void *val, int len) { @@ -308,6 +315,13 @@ int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, return 0; } +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val, + len); +} + int fdt_appendprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) { diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c index 4c569ee..96d4cf5 100644 --- a/libfdt/fdt_sw.c +++ b/libfdt/fdt_sw.c @@ -330,7 +330,8 @@ int fdt_property(void *fdt, const char *name, const void *val, int len) ret = fdt_property_placeholder(fdt, name, len, &ptr); if (ret) return ret; - memcpy(ptr, val, len); + if (len) + memcpy(ptr, val, len); return 0; } diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h index 8c7770e..c69a18e 100644 --- a/libfdt/libfdt.h +++ b/libfdt/libfdt.h @@ -1935,12 +1935,8 @@ int fdt_setprop_namelen(void *fdt, int nodeoffset, const char *name, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ -static inline int fdt_setprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len) -{ - return fdt_setprop_namelen(fdt, nodeoffset, name, strlen(name), val, - len); -} +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); /** * fdt_setprop_placeholder_namelen - allocate space for a property @@ -2002,13 +1998,8 @@ int fdt_setprop_placeholder_namelen(void *fdt, int nodeoffset, const char *name, * -FDT_ERR_BADLAYOUT, * -FDT_ERR_TRUNCATED, standard meanings */ -static inline int fdt_setprop_placeholder(void *fdt, int nodeoffset, - const char *name, int len, - void **prop_data) -{ - return fdt_setprop_placeholder_namelen(fdt, nodeoffset, name, - strlen(name), len, prop_data); -} +int fdt_setprop_placeholder(void *fdt, int nodeoffset, + const char *name, int len, void **prop_data); /** * fdt_setprop_u32 - set a property to a 32-bit integer diff --git a/livetree.c b/livetree.c index 5d72abc..8baed1f 100644 --- a/livetree.c +++ b/livetree.c @@ -1234,7 +1234,7 @@ void fixup_phandles(struct dt_info *dti, const char *name) continue; offset = strtol(soffset, NULL, 0); - if (offset < 0 || offset + 4 > p->val.len) { + if (offset < 0 || (unsigned int)offset + 4 > p->val.len) { if (quiet < 1) fprintf(stderr, "Warning: Label %s contains invalid offset for property %s in node %s\n", diff --git a/scripts/finalize-release b/scripts/finalize-release new file mode 100755 index 0000000..89008f2 --- /dev/null +++ b/scripts/finalize-release @@ -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 " >&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" <" >&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 </dev/null +$(TESTS_TREES): $(TESTS_PREFIX)treegen + @$(VECHO) TREEGEN + cd $(TESTS_PREFIX); ./treegen . >/dev/null tests_clean: @$(VECHO) CLEAN "(tests)" diff --git a/tests/dumptrees.c b/tests/dumptrees.c deleted file mode 100644 index 08967b3..0000000 --- a/tests/dumptrees.c +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * dumptrees - utility for libfdt testing - * - * (C) Copyright David Gibson , IBM Corporation. 2006. - */ -#include -#include -#include -#include -#include - -#include - -#include "testdata.h" - -static struct { - void *blob; - const char *filename; -} trees[] = { -#define TREE(name) { &name, #name ".dtb" } - TREE(test_tree1), - TREE(bad_node_char), TREE(bad_node_format), TREE(bad_prop_char), - TREE(ovf_size_strings), - TREE(truncated_property), TREE(truncated_string), - TREE(truncated_memrsv), - TREE(two_roots), - TREE(named_root) -}; - -#define NUM_TREES (sizeof(trees) / sizeof(trees[0])) - -int main(int argc, char *argv[]) -{ - unsigned int i; - - if (argc != 2) { - fprintf(stderr, "Missing output directory argument\n"); - return 1; - } - - if (chdir(argv[1]) != 0) { - perror("chdir()"); - return 1; - } - - for (i = 0; i < NUM_TREES; i++) { - void *blob = trees[i].blob; - const char *filename = trees[i].filename; - int size; - int fd; - int ret; - - size = fdt_totalsize(blob); - - printf("Tree \"%s\", %d bytes\n", filename, size); - - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) - perror("open()"); - - ret = write(fd, blob, size); - if (ret != size) - perror("write()"); - - close(fd); - } - exit(0); -} diff --git a/tests/fs_tree1.c b/tests/fs_tree1.c index 978f6a3..27eea55 100644 --- a/tests/fs_tree1.c +++ b/tests/fs_tree1.c @@ -24,7 +24,11 @@ static void start_dir(const char *name) { int rc; +#ifndef _WIN32 rc = mkdir(name, 0777); +#else + rc = mkdir(name); +#endif if (rc != 0) FAIL("mkdir(\"%s\"): %s", name, strerror(errno)); diff --git a/tests/integer-expressions.c b/tests/integer-expressions.c index 2f164d9..fe12eb3 100644 --- a/tests/integer-expressions.c +++ b/tests/integer-expressions.c @@ -17,6 +17,14 @@ #include "tests.h" #include "testdata.h" +/* + * Some of these trip the -Wconstant-logical-operand warning introduced in gcc + * 17. Disable it temporarily, we also have to disable -Wpragmas for the + * benefit of older compiler versions that don't know about that warning. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wconstant-logical-operand" static struct test_expr { const char *expr; uint32_t result; @@ -51,6 +59,7 @@ static struct test_expr { TE(11 * 257 * 1321517ULL), TE(123456790 - 4/2 + 17%4), }; +#pragma GCC diagnostic pop #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) diff --git a/tests/meson.build b/tests/meson.build index 4e669ce..c6ac2d7 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,14 +1,10 @@ -trees = static_library('trees', files('trees.S'), - build_by_default: false, - include_directories: libfdt_inc) +treegen = executable('treegen', files('treegen.c'), + build_by_default: false, + dependencies: [util_dep]) -dumptrees = executable('dumptrees', files('dumptrees.c'), - build_by_default: false, - link_with: trees, dependencies: libfdt_dep) - -dumptrees_dtb = custom_target( - 'dumptrees', - command: [dumptrees, meson.current_build_dir()], +treegen_dtb = custom_target( + 'treegen_dtb', + command: [treegen, meson.current_build_dir()], output: [ 'test_tree1.dtb', 'bad_node_char.dtb', @@ -18,10 +14,13 @@ dumptrees_dtb = custom_target( 'truncated_property.dtb', 'truncated_string.dtb', 'truncated_memrsv.dtb', + 'unterminated_memrsv.dtb', + 'two_roots.dtb', + 'named_root.dtb', ] ) -testutil_dep = declare_dependency(sources: ['testutils.c'], link_with: trees) +testutil_dep = declare_dependency(sources: ['testutils.c']) tests = [ 'add_subnode_with_nops', @@ -89,14 +88,11 @@ tests = [ 'supernode_atdepth_offset', 'sw_states', 'sw_tree1', - 'utilfdt_test', -] - -tests += [ 'truncated_memrsv', 'truncated_property', 'truncated_string', 'unterminated_memrsv', + 'utilfdt_test', ] test_deps = [testutil_dep, util_dep, libfdt_dep] @@ -141,10 +137,10 @@ run_test_types = [ 'fdtget', 'fdtput', 'fdtdump', - 'fdtoverlay' + 'fdtoverlay', ] run_test_deps = [ - dtc_tools, dumptrees_dtb, tests_exe + dtc_tools, treegen_dtb, tests_exe, ] if pylibfdt_enabled run_test_types += 'pylibfdt' diff --git a/tests/pylibfdt_tests.py b/tests/pylibfdt_tests.py index a4f73ed..6af11f2 100644 --- a/tests/pylibfdt_tests.py +++ b/tests/pylibfdt_tests.py @@ -13,26 +13,15 @@ sys.path.insert(0, '../pylibfdt') import libfdt from libfdt import Fdt, FdtSw, FdtException, QUIET_NOTFOUND, QUIET_ALL -TEST_ADDR_1H = 0xdeadbeef -TEST_ADDR_1L = 0x00000000 -TEST_ADDR_1 = (TEST_ADDR_1H << 32) | TEST_ADDR_1L TEST_ADDR_1 = 0x8000000000000000 -TEST_SIZE_1H = 0x00000000 -TEST_SIZE_1L = 0x00100000 -TEST_SIZE_1 = (TEST_SIZE_1H << 32) | TEST_SIZE_1L -TEST_ADDR_2H = 0 -TEST_ADDR_2L = 123456789 -TEST_ADDR_2 = (TEST_ADDR_2H << 32) | TEST_ADDR_2L -TEST_SIZE_2H = 0 -TEST_SIZE_2L = 0o10000 -TEST_SIZE_2 = (TEST_SIZE_2H << 32) | TEST_SIZE_2L +TEST_SIZE_1 = 0x0000000000100000 +TEST_ADDR_2 = 123456789 +TEST_SIZE_2 = 0o10000 TEST_VALUE_1 = 0xdeadbeef TEST_VALUE_2 = 123456789 -TEST_VALUE64_1H = 0xdeadbeef -TEST_VALUE64_1L = 0x01abcdef -TEST_VALUE64_1 = (TEST_VALUE64_1H << 32) | TEST_VALUE64_1L +TEST_VALUE64_1 = 0xdeadbeef01abcdef PHANDLE_1 = 0x2000 PHANDLE_2 = 0x2001 diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 34110c1..842b543 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -492,10 +492,10 @@ libfdt_tests () { run_test appendprop_addrrange unit-addr-without-reg.dtb 2 1 3 # Tests for behaviour on various sorts of corrupted trees - run_test truncated_property - run_test truncated_string - run_test truncated_memrsv - run_test unterminated_memrsv + run_test truncated_property truncated_property.dtb + run_test truncated_string truncated_string.dtb + run_test truncated_memrsv truncated_memrsv.dtb + run_test unterminated_memrsv unterminated_memrsv.dtb # Check aliases support in fdt_path_offset run_dtc_test -I dts -O dtb -o aliases.dtb "$SRCDIR/aliases.dts" diff --git a/tests/testdata.h b/tests/testdata.h index b29a759..c1eac10 100644 --- a/tests/testdata.h +++ b/tests/testdata.h @@ -1,28 +1,12 @@ -#ifdef __ASSEMBLER__ -#define ASM_CONST_LL(x) (x) -#else -#define ASM_CONST_LL(x) (x##ULL) -#endif - -#define TEST_ADDR_1H ASM_CONST_LL(0xdeadbeef) -#define TEST_ADDR_1L ASM_CONST_LL(0x00000000) -#define TEST_ADDR_1 ((TEST_ADDR_1H << 32) | TEST_ADDR_1L) -#define TEST_SIZE_1H ASM_CONST_LL(0x00000000) -#define TEST_SIZE_1L ASM_CONST_LL(0x00100000) -#define TEST_SIZE_1 ((TEST_SIZE_1H << 32) | TEST_SIZE_1L) -#define TEST_ADDR_2H ASM_CONST_LL(0) -#define TEST_ADDR_2L ASM_CONST_LL(123456789) -#define TEST_ADDR_2 ((TEST_ADDR_2H << 32) | TEST_ADDR_2L) -#define TEST_SIZE_2H ASM_CONST_LL(0) -#define TEST_SIZE_2L ASM_CONST_LL(010000) -#define TEST_SIZE_2 ((TEST_SIZE_2H << 32) | TEST_SIZE_2L) +#define TEST_ADDR_1 0xdeadbeef00000000ULL +#define TEST_SIZE_1 0x0000000000100000ULL +#define TEST_ADDR_2 123456789ULL +#define TEST_SIZE_2 010000ULL #define TEST_VALUE_1 0xdeadbeef #define TEST_VALUE_2 123456789 -#define TEST_VALUE64_1H ASM_CONST_LL(0xdeadbeef) -#define TEST_VALUE64_1L ASM_CONST_LL(0x01abcdef) -#define TEST_VALUE64_1 ((TEST_VALUE64_1H << 32) | TEST_VALUE64_1L) +#define TEST_VALUE64_1 0xdeadbeef01abcdefULL #define PHANDLE_1 0x2000 #define PHANDLE_2 0x2001 @@ -45,17 +29,3 @@ #define TEST_MEMREGION_SIZE 0x9abcdef0 #define TEST_MEMREGION_SIZE_HI 0x0fedcba900000000 #define TEST_MEMREGION_SIZE_INC 0x1000 - -#ifndef __ASSEMBLER__ -extern struct fdt_header test_tree1; -extern struct fdt_header truncated_property; -extern struct fdt_header bad_node_char; -extern struct fdt_header bad_node_format; -extern struct fdt_header bad_prop_char; -extern struct fdt_header ovf_size_strings; -extern struct fdt_header truncated_string; -extern struct fdt_header truncated_memrsv; -extern struct fdt_header unterminated_memrsv; -extern struct fdt_header two_roots; -extern struct fdt_header named_root; -#endif /* ! __ASSEMBLER__ */ diff --git a/tests/tests.h b/tests/tests.h index 7c1b6c0..578d8c1 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -25,9 +25,6 @@ void test_init(int argc, char *argv[]); #define streq(s1, s2) (strcmp((s1),(s2)) == 0) -/* Each test case must define this function */ -void cleanup(void); - #define verbose_printf(...) \ if (verbose_test) { \ printf(__VA_ARGS__); \ @@ -39,21 +36,18 @@ void cleanup(void); #define PASS() \ do { \ - cleanup(); \ printf("PASS\n"); \ exit(RC_PASS); \ } while (0) #define PASS_INCONCLUSIVE() \ do { \ - cleanup(); \ printf("PASS (inconclusive)\n"); \ exit(RC_PASS); \ } while (0) #define IRRELEVANT() \ do { \ - cleanup(); \ printf("PASS (irrelevant)\n"); \ exit(RC_PASS); \ } while (0) @@ -61,21 +55,18 @@ void cleanup(void); /* Look out, gcc extension below... */ #define FAIL(fmt, ...) \ do { \ - cleanup(); \ printf("FAIL\t" fmt "\n", ##__VA_ARGS__); \ exit(RC_FAIL); \ } while (0) #define CONFIG(fmt, ...) \ do { \ - cleanup(); \ printf("Bad configuration: " fmt "\n", ##__VA_ARGS__); \ exit(RC_CONFIG); \ } while (0) #define TEST_BUG(fmt, ...) \ do { \ - cleanup(); \ printf("BUG in testsuite: " fmt "\n", ##__VA_ARGS__); \ exit(RC_BUG); \ } while (0) diff --git a/tests/testutils.c b/tests/testutils.c index 54da2e4..6d69e87 100644 --- a/tests/testutils.c +++ b/tests/testutils.c @@ -42,29 +42,21 @@ static inline void VALGRIND_MAKE_MEM_DEFINED(void *p, size_t len) int verbose_test = 1; char *test_name; -void __attribute__((weak)) cleanup(void) +static void sigint_handler(int signum) { -} - -static void sigint_handler(int signum, siginfo_t *si, void *uc) -{ - cleanup(); - fprintf(stderr, "%s: %s (pid=%d)\n", test_name, - strsignal(signum), getpid()); + fprintf(stderr, "%s: signal %d (pid=%d)\n", test_name, + signum, getpid()); exit(RC_BUG); } void test_init(int argc, char *argv[]) { - int err; - struct sigaction sa_int = { - .sa_sigaction = sigint_handler, - }; - test_name = argv[0]; - err = sigaction(SIGINT, &sa_int, NULL); - if (err) + /* Use signal(), not sigaction() because it also works on the Windows / + * mingw environments we support. + */ + if (signal(SIGINT, sigint_handler) == SIG_ERR) FAIL("Can't install SIGINT handler"); if (getenv("QUIET_TEST")) diff --git a/tests/treegen.c b/tests/treegen.c new file mode 100644 index 0000000..6891bfd --- /dev/null +++ b/tests/treegen.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * treegen - generate test device tree blobs + * + * Build example device tree blobs for test. Note that some of the generated + * blobs are deliberately malformed, in order to test error conditions. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "tests.h" +#include "testdata.h" + +/* + * Buffer with write cursor. Emit helpers write at the cursor, advance it, and + * return the offset at which data was written. + */ +struct buf { + char *data; + size_t size, cursor; +}; + +#define CHUNKSIZE 1024 + +static struct buf buf_init(void) +{ + struct buf b = { NULL, 0, 0 }; + + return b; +} + +/* Low-level helpers */ + +static size_t emit_bytes(struct buf *b, const void *data, int len) +{ + size_t off = b->cursor; + + if (!len) + return off; + + if (b->cursor + len > b->size) { + size_t newsize = b->cursor + len + CHUNKSIZE; + + b->data = xrealloc(b->data, newsize); + memset(b->data + b->size, 0, newsize - b->size); + b->size = newsize; + } + memcpy(b->data + b->cursor, data, len); + b->cursor += len; + return off; +} + +static size_t emit_u32(struct buf *b, uint32_t val) +{ + fdt32_t beval = cpu_to_fdt32(val); + + return emit_bytes(b, &beval, sizeof(beval)); +} + +static size_t emit_u64(struct buf *b, uint64_t val) +{ + fdt64_t beval = cpu_to_fdt64(val); + + return emit_bytes(b, &beval, sizeof(beval)); +} + +static size_t emit_string(struct buf *b, const char *str) +{ + return emit_bytes(b, str, strlen(str) + 1); +} + +#define MAX_ALIGNMENT 8 + +static size_t emit_align(struct buf *b, size_t alignment) +{ + size_t pad = ((b->cursor + alignment - 1) & ~(alignment - 1)) - b->cursor; + static const char zeros[MAX_ALIGNMENT]; + + assert(alignment <= MAX_ALIGNMENT); + assert(pad < alignment); + + return emit_bytes(b, zeros, pad); +} + +static size_t start_block(struct buf *b) +{ + return b->cursor; +} + +static void fill_prop_name(struct buf *b, size_t strs, size_t prop, size_t stroff) +{ + struct fdt_property *fp = (struct fdt_property *)(b->data + prop); + + fp->nameoff = cpu_to_fdt32(stroff - strs); +} + +/* FDT structure helpers */ + +static size_t emit_fdt_header(struct buf *b) +{ + size_t off = emit_u32(b, FDT_MAGIC); + + emit_u32(b, 0); /* totalsize */ + emit_u32(b, 0); /* off_dt_struct */ + emit_u32(b, 0); /* off_dt_strings */ + emit_u32(b, 0); /* off_mem_rsvmap */ + emit_u32(b, 0x11); /* version */ + emit_u32(b, 0x10); /* last_comp_version */ + emit_u32(b, 0); /* boot_cpuid_phys */ + emit_u32(b, 0); /* size_dt_strings */ + emit_u32(b, 0); /* size_dt_struct */ + return off; +} + +static void finish_rsvmap(struct buf *b, size_t hdr, size_t rsvmap) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->off_mem_rsvmap = cpu_to_fdt32(rsvmap - hdr); +} + +static void finish_struct_block(struct buf *b, size_t hdr, size_t start) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->off_dt_struct = cpu_to_fdt32(start - hdr); + fh->size_dt_struct = cpu_to_fdt32(b->cursor - start); +} + +static void finish_strings_block(struct buf *b, size_t hdr, size_t start) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->off_dt_strings = cpu_to_fdt32(start - hdr); + fh->size_dt_strings = cpu_to_fdt32(b->cursor - start); +} + +static void finish_totalsize(struct buf *b, size_t hdr) +{ + struct fdt_header *fh = (struct fdt_header *)(b->data + hdr); + + fh->totalsize = cpu_to_fdt32(b->cursor - hdr); +} + +static size_t emit_rsvmap_entry(struct buf *b, uint64_t addr, uint64_t size) +{ + size_t off = emit_u64(b, addr); + + emit_u64(b, size); + return off; +} + +static size_t emit_rsvmap_term(struct buf *b) +{ + return emit_rsvmap_entry(b, 0, 0); +} + +static void emit_rsvmap_empty(struct buf *b, size_t hdr) +{ + size_t rsvmap = start_block(b); + + emit_rsvmap_term(b); + finish_rsvmap(b, hdr, rsvmap); +} + +static size_t emit_begin_node(struct buf *b, const char *name) +{ + size_t off = emit_u32(b, FDT_BEGIN_NODE); + + emit_string(b, name); + emit_align(b, FDT_TAGSIZE); + return off; +} + +static size_t emit_end_node(struct buf *b) +{ + return emit_u32(b, FDT_END_NODE); +} + +static size_t emit_fdt_end(struct buf *b) +{ + return emit_u32(b, FDT_END); +} + +static size_t emit_prop_header(struct buf *b, int nameoff, int len) +{ + size_t off = emit_u32(b, FDT_PROP); + + emit_u32(b, len); + emit_u32(b, nameoff); + return off; +} + +static size_t emit_prop_nil(struct buf *b, int nameoff) +{ + return emit_prop_header(b, nameoff, 0); +} + +static size_t emit_prop_u32(struct buf *b, int nameoff, uint32_t val) +{ + size_t off = emit_prop_header(b, nameoff, 4); + + emit_u32(b, val); + return off; +} + +static size_t emit_prop_u64(struct buf *b, int nameoff, uint64_t val) +{ + size_t off = emit_prop_header(b, nameoff, 8); + + emit_u64(b, val); + return off; +} + +static size_t emit_prop_str(struct buf *b, int nameoff, const char *str) +{ + int len = strlen(str) + 1; + size_t off = emit_prop_header(b, nameoff, len); + + emit_bytes(b, str, len); + emit_align(b, FDT_TAGSIZE); + return off; +} + +#define emit_prop_strings(b, nameoff, ...) \ + emit_prop_strings_(b, nameoff, __VA_ARGS__, NULL) + +static size_t emit_prop_strings_(struct buf *b, int nameoff, ...) +{ + size_t off = emit_prop_header(b, nameoff, 0); + size_t start = b->cursor; + va_list ap; + const char *s; + + va_start(ap, nameoff); + while ((s = va_arg(ap, const char *)) != NULL) + emit_string(b, s); + va_end(ap); + + ((struct fdt_property *)(b->data + off))->len = + cpu_to_fdt32(b->cursor - start); + emit_align(b, FDT_TAGSIZE); + return off; +} + +/* + * Tree generators + * + * Each function builds a complete DTB in a buffer. + */ + +static struct buf make_test_tree1(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p_compatible, p_prop_int, p_prop_int64, p_prop_str, + p_address_cells, p_size_cells; + size_t p_s1_compatible, p_s1_reg, p_s1_prop_int; + size_t p_s1ss_compatible, p_s1ss_placeholder, p_s1ss_prop_int; + size_t p_s2_reg, p_s2_linux_phandle, p_s2_prop_int, + p_s2_address_cells, p_s2_size_cells; + size_t p_s2ss0_reg, p_s2ss0_phandle, p_s2ss0_compatible, + p_s2ss0_prop_int; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + { + size_t rsvmap = start_block(&b); + + emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1); + emit_rsvmap_entry(&b, TEST_ADDR_2, TEST_SIZE_2); + emit_rsvmap_term(&b); + finish_rsvmap(&b, hdr, rsvmap); + } + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p_compatible = emit_prop_str(&b, 0, "test_tree1"); + p_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1); + p_prop_int64 = emit_prop_u64(&b, 0, TEST_VALUE64_1); + p_prop_str = emit_prop_str(&b, 0, TEST_STRING_1); + p_address_cells = emit_prop_u32(&b, 0, 1); + p_size_cells = emit_prop_u32(&b, 0, 0); + + emit_begin_node(&b, "subnode@1"); + p_s1_compatible = emit_prop_str(&b, 0, "subnode1"); + p_s1_reg = emit_prop_u32(&b, 0, 1); + p_s1_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1); + + emit_begin_node(&b, "subsubnode"); + p_s1ss_compatible = emit_prop_strings(&b, 0, + "subsubnode1", "subsubnode"); + p_s1ss_placeholder = emit_prop_strings(&b, 0, + "this is a placeholder string", + "string2"); + p_s1ss_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_1); + emit_end_node(&b); + + emit_begin_node(&b, "ss1"); + emit_end_node(&b); + + emit_end_node(&b); + + emit_begin_node(&b, "subnode@2"); + p_s2_reg = emit_prop_u32(&b, 0, 2); + p_s2_linux_phandle = emit_prop_u32(&b, 0, PHANDLE_1); + p_s2_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_2); + p_s2_address_cells = emit_prop_u32(&b, 0, 1); + p_s2_size_cells = emit_prop_u32(&b, 0, 0); + + emit_begin_node(&b, "subsubnode@0"); + p_s2ss0_reg = emit_prop_u32(&b, 0, 0); + p_s2ss0_phandle = emit_prop_u32(&b, 0, PHANDLE_2); + p_s2ss0_compatible = emit_prop_strings(&b, 0, + "subsubnode2", "subsubnode"); + p_s2ss0_prop_int = emit_prop_u32(&b, 0, TEST_VALUE_2); + emit_end_node(&b); + + emit_begin_node(&b, "ss2"); + emit_end_node(&b); + + emit_end_node(&b); + + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + size_t s; + + s = emit_string(&b, "compatible"); + fill_prop_name(&b, strs, p_compatible, s); + fill_prop_name(&b, strs, p_s1_compatible, s); + fill_prop_name(&b, strs, p_s1ss_compatible, s); + fill_prop_name(&b, strs, p_s2ss0_compatible, s); + + s = emit_string(&b, "prop-int"); + fill_prop_name(&b, strs, p_prop_int, s); + fill_prop_name(&b, strs, p_s1_prop_int, s); + fill_prop_name(&b, strs, p_s1ss_prop_int, s); + fill_prop_name(&b, strs, p_s2_prop_int, s); + fill_prop_name(&b, strs, p_s2ss0_prop_int, s); + + fill_prop_name(&b, strs, p_prop_int64, + emit_string(&b, "prop-int64")); + + fill_prop_name(&b, strs, p_prop_str, + emit_string(&b, "prop-str")); + + fill_prop_name(&b, strs, p_s2_linux_phandle, + emit_string(&b, "linux,phandle")); + + fill_prop_name(&b, strs, p_s2ss0_phandle, + emit_string(&b, "phandle")); + + s = emit_string(&b, "reg"); + fill_prop_name(&b, strs, p_s1_reg, s); + fill_prop_name(&b, strs, p_s2_reg, s); + fill_prop_name(&b, strs, p_s2ss0_reg, s); + + fill_prop_name(&b, strs, p_s1ss_placeholder, + emit_string(&b, "placeholder")); + + s = emit_string(&b, "#address-cells"); + fill_prop_name(&b, strs, p_address_cells, s); + fill_prop_name(&b, strs, p_s2_address_cells, s); + + s = emit_string(&b, "#size-cells"); + fill_prop_name(&b, strs, p_size_cells, s); + fill_prop_name(&b, strs, p_s2_size_cells, s); + + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_truncated_property(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p = emit_prop_header(&b, 0, 4); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + emit_string(&b, "truncated"); + fill_prop_name(&b, strs, p, strs); + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_bad_node_char(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_begin_node(&b, "sub$node"); + emit_end_node(&b); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_bad_node_format(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_begin_node(&b, "subnode@1@2"); + emit_end_node(&b); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_bad_prop_char(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p = emit_prop_u32(&b, 0, TEST_VALUE_1); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + fill_prop_name(&b, strs, p, + emit_string(&b, "prop$erty")); + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_ovf_size_strings(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_prop_u32(&b, 0x10000000, 0); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + emit_string(&b, "x"); + finish_strings_block(&b, hdr, strs); + } + + ((struct fdt_header *)(b.data + hdr))->size_dt_strings = + cpu_to_fdt32(0xffffffff); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_truncated_string(void) +{ + struct buf b = buf_init(); + size_t hdr; + size_t p_good, p_bad; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + p_good = emit_prop_nil(&b, 0); + p_bad = emit_prop_nil(&b, 0); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + { + size_t strs = start_block(&b); + + fill_prop_name(&b, strs, p_good, emit_string(&b, "good")); + fill_prop_name(&b, strs, p_bad, emit_bytes(&b, "bad", 3)); + finish_strings_block(&b, hdr, strs); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_truncated_memrsv(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + { + size_t rsvmap; + + emit_align(&b, 8); + rsvmap = start_block(&b); + emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1); + finish_rsvmap(&b, hdr, rsvmap); + } + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_unterminated_memrsv(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + + { + size_t rsvmap; + + emit_align(&b, 8); + rsvmap = start_block(&b); + emit_rsvmap_entry(&b, TEST_ADDR_1, TEST_SIZE_1); + finish_rsvmap(&b, hdr, rsvmap); + } + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_two_roots(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_begin_node(&b, ""); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +static struct buf make_named_root(void) +{ + struct buf b = buf_init(); + size_t hdr; + + hdr = emit_fdt_header(&b); + emit_align(&b, 8); + + emit_rsvmap_empty(&b, hdr); + + { + size_t ss = start_block(&b); + + emit_begin_node(&b, "fake"); + emit_end_node(&b); + emit_fdt_end(&b); + finish_struct_block(&b, hdr, ss); + } + + finish_strings_block(&b, hdr, start_block(&b)); + + finish_totalsize(&b, hdr); + + return b; +} + +/* Tree table and main */ + +static struct { + struct buf (*generator)(void); + const char *filename; +} trees[] = { +#define TREE(name) { make_##name, #name ".dtb" } + TREE(test_tree1), + TREE(bad_node_char), TREE(bad_node_format), TREE(bad_prop_char), + TREE(ovf_size_strings), + TREE(truncated_property), TREE(truncated_string), + TREE(truncated_memrsv), + TREE(unterminated_memrsv), + TREE(two_roots), + TREE(named_root), +}; + +int main(int argc, char *argv[]) +{ + unsigned int i; + + if (argc != 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + if (chdir(argv[1]) != 0) { + perror("chdir()"); + return 1; + } + + for (i = 0; i < ARRAY_SIZE(trees); i++) { + struct buf b = trees[i].generator(); + const char *filename = trees[i].filename; + int fd; + ssize_t ret; + + printf("Tree \"%s\", %zu bytes\n", filename, b.cursor); + + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd < 0) + perror("open()"); + + ret = write(fd, b.data, b.cursor); + if (ret < 0 || (size_t)ret != b.cursor) + perror("write()"); + + close(fd); + free(b.data); + } + + return 0; +} diff --git a/tests/trees.S b/tests/trees.S deleted file mode 100644 index 3de95fa..0000000 --- a/tests/trees.S +++ /dev/null @@ -1,343 +0,0 @@ -#include -#include "testdata.h" - - .macro fdtlong val - .byte ((\val) >> 24) & 0xff - .byte ((\val) >> 16) & 0xff - .byte ((\val) >> 8) & 0xff - .byte (\val) & 0xff - .endm - - .macro treehdr tree - .balign 8 - .globl \tree -\tree : - fdtlong FDT_MAGIC - fdtlong (\tree\()_end - \tree) - fdtlong (\tree\()_struct - \tree) - fdtlong (\tree\()_strings - \tree) - fdtlong (\tree\()_rsvmap - \tree) - fdtlong 0x11 - fdtlong 0x10 - fdtlong 0 - fdtlong (\tree\()_strings_end - \tree\()_strings) - fdtlong (\tree\()_struct_end - \tree\()_struct) - .endm - - .macro rsvmape addrh, addrl, lenh, lenl - fdtlong \addrh - fdtlong \addrl - fdtlong \lenh - fdtlong \lenl - .endm - - .macro empty_rsvmap tree - .balign 8 -\tree\()_rsvmap: - rsvmape 0, 0, 0, 0 -\tree\()_rsvmap_end: - .endm - - .macro prophdr tree, name, len - fdtlong FDT_PROP - fdtlong \len - fdtlong (\tree\()_\name - \tree\()_strings) - .endm - - .macro propnil tree, name - prophdr \tree, \name, 0 - .endm - - .macro propu32 tree, name, val - prophdr \tree, \name, 4 - fdtlong \val - .endm - - .macro propu64 tree, name, valh, vall - prophdr \tree, \name, 8 - fdtlong \valh - fdtlong \vall - .endm - - .macro propstr tree, name, str:vararg - prophdr \tree, \name, (55f - 54f) -54: - .asciz \str -55: - .balign 4 - .endm - - .macro beginn name:vararg - fdtlong FDT_BEGIN_NODE - .asciz \name - .balign 4 - .endm - - .macro endn - fdtlong FDT_END_NODE - .endm - - .macro string tree, name, str:vararg -\tree\()_\name : - .asciz \str - .endm - - - .data - - treehdr test_tree1 - - .balign 8 -test_tree1_rsvmap: - rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L - rsvmape TEST_ADDR_2H, TEST_ADDR_2L, TEST_SIZE_2H, TEST_SIZE_2L - rsvmape 0, 0, 0, 0 -test_tree1_rsvmap_end: - -test_tree1_struct: - beginn "" - propstr test_tree1, compatible, "test_tree1" - propu32 test_tree1, prop_int, TEST_VALUE_1 - propu64 test_tree1, prop_int64, TEST_VALUE64_1H, TEST_VALUE64_1L - propstr test_tree1, prop_str, TEST_STRING_1 - propu32 test_tree1, address_cells, 1 - propu32 test_tree1, size_cells, 0 - - beginn "subnode@1" - propstr test_tree1, compatible, "subnode1" - propu32 test_tree1, reg, 1 - propu32 test_tree1, prop_int, TEST_VALUE_1 - - beginn "subsubnode" - propstr test_tree1, compatible, "subsubnode1\0subsubnode" - propstr test_tree1, placeholder, "this is a placeholder string\0string2" - propu32 test_tree1, prop_int, TEST_VALUE_1 - endn - - beginn "ss1" - endn - - endn - - beginn "subnode@2" - propu32 test_tree1, reg, 2 - propu32 test_tree1, linux_phandle, PHANDLE_1 - propu32 test_tree1, prop_int, TEST_VALUE_2 - propu32 test_tree1, address_cells, 1 - propu32 test_tree1, size_cells, 0 - - beginn "subsubnode@0" - propu32 test_tree1, reg, 0 - propu32 test_tree1, phandle, PHANDLE_2 - propstr test_tree1, compatible, "subsubnode2\0subsubnode" - propu32 test_tree1, prop_int, TEST_VALUE_2 - endn - - beginn "ss2" - endn - - endn - - endn - fdtlong FDT_END -test_tree1_struct_end: - -test_tree1_strings: - string test_tree1, compatible, "compatible" - string test_tree1, prop_int, "prop-int" - string test_tree1, prop_int64, "prop-int64" - string test_tree1, prop_str, "prop-str" - string test_tree1, linux_phandle, "linux,phandle" - string test_tree1, phandle, "phandle" - string test_tree1, reg, "reg" - string test_tree1, placeholder, "placeholder" - string test_tree1, address_cells, "#address-cells" - string test_tree1, size_cells, "#size-cells" -test_tree1_strings_end: -test_tree1_end: - - - treehdr truncated_property - empty_rsvmap truncated_property - -truncated_property_struct: - beginn "" - prophdr truncated_property, prop_truncated, 4 - /* Oops, no actual property data here */ -truncated_property_struct_end: - -truncated_property_strings: - string truncated_property, prop_truncated, "truncated" -truncated_property_strings_end: - -truncated_property_end: - - - treehdr bad_node_char - empty_rsvmap bad_node_char - -bad_node_char_struct: - beginn "" - beginn "sub$node" - endn - endn - fdtlong FDT_END -bad_node_char_struct_end: - -bad_node_char_strings: -bad_node_char_strings_end: -bad_node_char_end: - - - treehdr bad_node_format - empty_rsvmap bad_node_format - -bad_node_format_struct: - beginn "" - beginn "subnode@1@2" - endn - endn - fdtlong FDT_END -bad_node_format_struct_end: - -bad_node_format_strings: -bad_node_format_strings_end: -bad_node_format_end: - - - treehdr bad_prop_char - empty_rsvmap bad_prop_char - -bad_prop_char_struct: - beginn "" - propu32 bad_prop_char, prop, TEST_VALUE_1 - endn - fdtlong FDT_END -bad_prop_char_struct_end: - -bad_prop_char_strings: - string bad_prop_char, prop, "prop$erty" -bad_prop_char_strings_end: -bad_prop_char_end: - - - /* overflow_size_strings */ - .balign 8 - .globl ovf_size_strings -ovf_size_strings: - fdtlong FDT_MAGIC - fdtlong (ovf_size_strings_end - ovf_size_strings) - fdtlong (ovf_size_strings_struct - ovf_size_strings) - fdtlong (ovf_size_strings_strings - ovf_size_strings) - fdtlong (ovf_size_strings_rsvmap - ovf_size_strings) - fdtlong 0x11 - fdtlong 0x10 - fdtlong 0 - fdtlong 0xffffffff - fdtlong (ovf_size_strings_struct_end - ovf_size_strings_struct) - empty_rsvmap ovf_size_strings - -ovf_size_strings_struct: - beginn "" - propu32 ovf_size_strings, bad_string, 0 - endn - fdtlong FDT_END -ovf_size_strings_struct_end: - -ovf_size_strings_strings: - string ovf_size_strings, x, "x" - ovf_size_strings_bad_string = ovf_size_strings_strings + 0x10000000 -ovf_size_strings_strings_end: -ovf_size_strings_end: - - - /* truncated_string */ - treehdr truncated_string - empty_rsvmap truncated_string - -truncated_string_struct: - beginn "" - propnil truncated_string, good_string - propnil truncated_string, bad_string - endn - fdtlong FDT_END -truncated_string_struct_end: - -truncated_string_strings: - string truncated_string, good_string, "good" -truncated_string_bad_string: - .ascii "bad" - /* NOTE: terminating \0 deliberately missing */ -truncated_string_strings_end: -truncated_string_end: - - - /* truncated_memrsv */ - treehdr truncated_memrsv - -truncated_memrsv_struct: - beginn "" - endn - fdtlong FDT_END -truncated_memrsv_struct_end: - -truncated_memrsv_strings: -truncated_memrsv_strings_end: - - .balign 8 -truncated_memrsv_rsvmap: - rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L -truncated_memrsv_rsvmap_end: - -truncated_memrsv_end: - - /* unterminated_memrsv */ - treehdr unterminated_memrsv - -unterminated_memrsv_rsvmap: - rsvmape TEST_ADDR_1H, TEST_ADDR_1L, TEST_SIZE_1H, TEST_SIZE_1L -unterminated_memrsv_rsvmap_end: - -unterminated_memrsv_struct: - beginn "" - endn - fdtlong FDT_END -unterminated_memrsv_struct_end: - -unterminated_memrsv_strings: -unterminated_memrsv_strings_end: - -unterminated_memrsv_end: - - /* two root nodes */ - treehdr two_roots - empty_rsvmap two_roots - -two_roots_struct: - beginn "" - endn - beginn "" - endn - fdtlong FDT_END -two_roots_struct_end: - -two_roots_strings: -two_roots_strings_end: - -two_roots_end: - - - /* root node with a non-empty name */ - treehdr named_root - empty_rsvmap named_root - -named_root_struct: - beginn "fake" - endn - fdtlong FDT_END -named_root_struct_end: - -named_root_strings: -named_root_strings_end: - -named_root_end: diff --git a/tests/truncated_memrsv.c b/tests/truncated_memrsv.c index b7ab4e7..e9b18cc 100644 --- a/tests/truncated_memrsv.c +++ b/tests/truncated_memrsv.c @@ -19,12 +19,13 @@ int main(int argc, char *argv[]) { - void *fdt = &truncated_memrsv; + void *fdt; void *buf; int err; uint64_t addr, size; test_init(argc, argv); + fdt = load_blob_arg(argc, argv); err = fdt_check_header(fdt); if (err != 0) diff --git a/tests/truncated_property.c b/tests/truncated_property.c index d9d52b2..eb09aa4 100644 --- a/tests/truncated_property.c +++ b/tests/truncated_property.c @@ -13,17 +13,15 @@ #include #include "tests.h" -#include "testdata.h" int main(int argc, char *argv[]) { - void *fdt = &truncated_property; + void *fdt; const void *prop; int len; test_init(argc, argv); - - vg_prepare_blob(fdt, fdt_totalsize(fdt)); + fdt = load_blob_arg(argc, argv); prop = fdt_getprop(fdt, 0, "truncated", &len); if (prop) diff --git a/tests/truncated_string.c b/tests/truncated_string.c index d745414..45e6cd3 100644 --- a/tests/truncated_string.c +++ b/tests/truncated_string.c @@ -13,18 +13,16 @@ #include #include "tests.h" -#include "testdata.h" int main(int argc, char *argv[]) { - void *fdt = &truncated_string; + void *fdt; const struct fdt_property *good, *bad; int off, len; const char *name; test_init(argc, argv); - - vg_prepare_blob(fdt, fdt_totalsize(fdt)); + fdt = load_blob_arg(argc, argv); off = fdt_first_property_offset(fdt, 0); good = fdt_get_property_by_offset(fdt, off, NULL); diff --git a/tests/unterminated_memrsv.c b/tests/unterminated_memrsv.c index 441b8d7..a19dc2d 100644 --- a/tests/unterminated_memrsv.c +++ b/tests/unterminated_memrsv.c @@ -22,12 +22,13 @@ int main(int argc, char *argv[]) { - void *fdt = &unterminated_memrsv; + void *fdt; void *buf; int err; uint64_t addr, size; test_init(argc, argv); + fdt = load_blob_arg(argc, argv); err = fdt_check_header(fdt); if (err != 0)