From nobody Sun Feb 8 12:18:56 2026 Received: from smtpout-03.galae.net (smtpout-03.galae.net [185.246.85.4]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4EA722DA760; Mon, 12 Jan 2026 14:22:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.246.85.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768227735; cv=none; b=LeY3e2RewuklPkC76HQvjs82NJ84IkDaaAxtN91Qfd+QvQXGHbxQB159pPERLqetMxBF3qDWsK6ixGDOw3Z/UWp2i3MK5xWpqKUbSHA+ZTVdtMsnILgI/kuhyvtZRiE265jhFPu4WK9Z7zLtX/zTdB/rJD2Hj0rV1hhptznF71w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768227735; c=relaxed/simple; bh=Sucyfoy8+Xz2IsznHku9kGLHK88fT8w0tMw6/VCHFIo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=j3WhrJwRMMPXTAQ3HbN5lwuBamzbPzoluClbL+yOKX7n67m2tw4g2UbXn8P9W9i3ocMe1Fbl74i+BW3KAsWYSMDOnkRL2WJXOG1SlQUJ2jwR3t/cBpWM6Jb1rb8wvp7IpbPKRk/gK9Rjsv6a3TfNRnLvuUETgwTCwO3HBdnx0Ws= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com; spf=pass smtp.mailfrom=bootlin.com; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b=hby+yWLM; arc=none smtp.client-ip=185.246.85.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=bootlin.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=bootlin.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=bootlin.com header.i=@bootlin.com header.b="hby+yWLM" Received: from smtpout-01.galae.net (smtpout-01.galae.net [212.83.139.233]) by smtpout-03.galae.net (Postfix) with ESMTPS id 27D714E42094; Mon, 12 Jan 2026 14:22:11 +0000 (UTC) Received: from mail.galae.net (mail.galae.net [212.83.136.155]) by smtpout-01.galae.net (Postfix) with ESMTPS id F067A606FA; Mon, 12 Jan 2026 14:22:10 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) by localhost (Mailerdaemon) with ESMTPSA id DAAEC103C8CD8; Mon, 12 Jan 2026 15:22:08 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=dkim; t=1768227730; h=from:subject:date:message-id:to:cc:mime-version: content-transfer-encoding:in-reply-to:references; bh=jy5RTTiCQ7Wx69xXqSkW5jY8TKudtZArBpa46lgNVqQ=; b=hby+yWLMwgNZN73qBjc4reFcDEPQrTpFFGWw7tuOAEO+rqawlUqpqDgZkJ4Z417182JDoi dj3Z5ZwV2O9Fg0S5wS+FqzK27QVWJS/BPGuAP6RoEy7ZqEHkJGBujOs08b5L/TD/s2ipXc ti1vxVQH8IbguCOzjxXD/HrHnGowubT2PsTR4e7tzNVvf9Wu9D22aVp6eLJLfI/EoWp57U O/geVXMH/l3Y9RBaEG9rstY3bkGNc3tNKYVCEv7J5onnW0t3hAUWmE9VWMmSO/6sxN7cTv vdpbfxQX2RHc3W/T2wED832TbJy+kpikAWTjfwnDh+YkdU6oelYBhpDXPIpHgg== From: Herve Codina To: David Gibson , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: Ayush Singh , Geert Uytterhoeven , devicetree-compiler@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, devicetree-spec@vger.kernel.org, Hui Pu , Ian Ray , Luca Ceresoli , Thomas Petazzoni , Herve Codina Subject: [RFC PATCH 71/77] Add fdtaddon tool to apply an addon Date: Mon, 12 Jan 2026 15:20:01 +0100 Message-ID: <20260112142009.1006236-72-herve.codina@bootlin.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260112142009.1006236-1-herve.codina@bootlin.com> References: <20260112142009.1006236-1-herve.codina@bootlin.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Last-TLS-Session-Version: TLSv1.3 Content-Type: text/plain; charset="utf-8" libfdt has support for applying an addon on top of a base device-tree. This is provided by the libfdt fdt_addon_apply() function. The fdtaddon tool is command line tool which allows to apply addon dtb file to a base device-tree dtb file. It relies on fdt_addon_apply(). Signed-off-by: Herve Codina --- Makefile | 5 ++ fdtaddon.c | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++ meson.build | 2 +- 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 fdtaddon.c diff --git a/Makefile b/Makefile index 83d8220..1137cee 100644 --- a/Makefile +++ b/Makefile @@ -159,6 +159,7 @@ BIN +=3D fdtdump BIN +=3D fdtget BIN +=3D fdtput BIN +=3D fdtoverlay +BIN +=3D fdtaddon =20 SCRIPTS =3D dtdiff =20 @@ -172,6 +173,7 @@ ifneq ($(MAKECMDGOALS),libfdt) -include $(FDTGET_OBJS:%.o=3D%.d) -include $(FDTPUT_OBJS:%.o=3D%.d) -include $(FDTOVERLAY_OBJS:%.o=3D%.d) +-include $(FDTADDON_OBJS:%.o=3D%.d) endif endif =20 @@ -255,6 +257,8 @@ fdtput: $(FDTPUT_OBJS) $(LIBFDT_dep) =20 fdtoverlay: $(FDTOVERLAY_OBJS) $(LIBFDT_dep) =20 +fdtaddon: $(FDTADDON_OBJS) $(LIBFDT_dep) + dist: git archive --format=3Dtar --prefix=3Ddtc-$(dtc_version)/ HEAD \ > ../dtc-$(dtc_version).tar @@ -295,6 +299,7 @@ TESTS_BIN +=3D fdtput TESTS_BIN +=3D fdtget TESTS_BIN +=3D fdtdump TESTS_BIN +=3D fdtoverlay +TESTS_BIN +=3D fdtaddon =20 ifneq ($(MAKECMDGOALS),libfdt) include tests/Makefile.tests diff --git a/fdtaddon.c b/fdtaddon.c new file mode 100644 index 0000000..c2fefa3 --- /dev/null +++ b/fdtaddon.c @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2025 Bootlin. All rights reserved. + * + * Author: + * Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" + +/* Usage related data. */ +static const char usage_synopsis[] =3D + "apply an addon to a base blob\n" + " fdtaddon "; +static const char usage_short_opts[] =3D "i:o:t:v" USAGE_COMMON_SHORT_OPTS; +static const struct option usage_long_opts[] =3D { + { "input", required_argument, NULL, 'i' }, + { "output", required_argument, NULL, 'o' }, + { "target", required_argument, NULL, 't' }, + { "verbose", no_argument, NULL, 'v' }, + USAGE_COMMON_LONG_OPTS, +}; +static const char *const usage_opts_help[] =3D { "Input base DT blob", + "Output DT blob", "Target node", + "Verbose messages", + USAGE_COMMON_OPTS_HELP }; + +int verbose; + +static void *do_apply(void *base, const void *addon, const char *target) +{ + void *tmp_merged; + void *tmp_addon; + size_t max_merged_size; + int ret; + + /* + * We take copies first, because a failed apply can trash + * both the base blob and the overlay. + */ + + /* + * The merged size should not be greater than the sum of the size of + * individual items. + */ + max_merged_size =3D fdt_totalsize(base) + fdt_totalsize(addon); + + tmp_merged =3D xmalloc(max_merged_size); + ret =3D fdt_open_into(base, tmp_merged, max_merged_size); + if (ret) { + fprintf(stderr, + "\nFailed to make temporary copy: %s\n", + fdt_strerror(ret)); + goto fail; + } + + tmp_addon =3D xmalloc(fdt_totalsize(addon)); + memcpy(tmp_addon, addon, fdt_totalsize(addon)); + + if (!(fdt_dt_flags(tmp_addon) & FDT_FLAG_ADDON)) { + fprintf(stderr, + "\nAddon dtb is not an 'addon'\n"); + goto fail; + } + + ret =3D fdt_addon_apply(tmp_merged, tmp_addon, target); + if (ret) { + fprintf(stderr, "\nFailed to apply %s\n", fdt_strerror(ret)); + goto fail; + } + + free(tmp_addon); + return tmp_merged; + +fail: + free(tmp_merged); + free(tmp_addon); + return NULL; +} + +static int do_fdtaddon(const char *input_filename, const char *output_file= name, + const char *addon_filename, const char *target) +{ + void *base_blob =3D NULL; + void *addon_blob =3D NULL; + void *merged_blob =3D NULL; + size_t base_buflen; + size_t addon_buflen; + int ret =3D -1; + + base_blob =3D utilfdt_read(input_filename, &base_buflen); + if (!base_blob) { + fprintf(stderr, "\nFailed to read '%s'\n", input_filename); + goto out_err; + } + if (fdt_totalsize(base_blob) > base_buflen) { + fprintf(stderr, + "\nBase blob is incomplete (%zu / %"PRIu32" bytes read)\n", + base_buflen, fdt_totalsize(base_blob)); + goto out_err; + } + + addon_blob =3D utilfdt_read(addon_filename, &addon_buflen); + if (!addon_blob) { + fprintf(stderr, "\nFailed to read '%s'\n", addon_filename); + goto out_err; + } + if (fdt_totalsize(addon_blob) > addon_buflen) { + fprintf(stderr, + "\nAddon blob is incomplete (%zu / %"PRIu32" bytes read)\n", + addon_buflen, fdt_totalsize(addon_blob)); + goto out_err; + } + + /* apply the addon */ + merged_blob =3D do_apply(base_blob, addon_blob, target); + if (!merged_blob) + goto out_err; + + fdt_pack(merged_blob); + ret =3D utilfdt_write(output_filename, merged_blob); + if (ret) + fprintf(stderr, "\nFailed to write '%s'\n", output_filename); + +out_err: + free(merged_blob); + free(addon_blob); + free(base_blob); + + return ret; +} + +int main(int argc, char *argv[]) +{ + char *input_filename =3D NULL; + char *output_filename =3D NULL; + char *addon_filename =3D NULL; + const char *target =3D NULL; + int opt; + + while ((opt =3D util_getopt_long()) !=3D EOF) { + switch (opt) { + case_USAGE_COMMON_FLAGS + + case 'i': + input_filename =3D optarg; + break; + case 'o': + output_filename =3D optarg; + break; + case 'v': + verbose =3D 1; + break; + case 't': + target =3D optarg; + break; + } + } + + if (!input_filename) + usage("missing input file"); + + if (!output_filename) + usage("missing output file"); + + if (!target) + usage("missing target"); + + argv +=3D optind; + argc -=3D optind; + + if (argc !=3D 1) + usage("missing addon file"); + + addon_filename =3D argv[0]; + + if (verbose) { + printf("input =3D %s\n", input_filename); + printf("output =3D %s\n", output_filename); + printf("addon =3D %s\n", addon_filename); + } + + if (do_fdtaddon(input_filename, output_filename, addon_filename, target)) + return 1; + + return 0; +} diff --git a/meson.build b/meson.build index 66b44e8..c108514 100644 --- a/meson.build +++ b/meson.build @@ -107,7 +107,7 @@ if get_option('tools') and not wheel_only install: true, ) =20 - foreach e: ['fdtdump', 'fdtget', 'fdtput', 'fdtoverlay'] + foreach e: ['fdtdump', 'fdtget', 'fdtput', 'fdtoverlay', 'fdtaddon'] dtc_tools +=3D executable(e, files(e + '.c'), dependencies: util_dep, = install: true) endforeach =20 --=20 2.52.0