* [PATCH 0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR
@ 2026-07-06 15:06 Carlo Caione
2026-07-06 15:06 ` [PATCH 1/4] bootstd: add a firmware-owned devicetree source Carlo Caione
` (4 more replies)
0 siblings, 5 replies; 14+ messages in thread
From: Carlo Caione @ 2026-07-06 15:06 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen, Carlo Caione
EBBR-style firmware (SystemReady IR) owns the devicetree: the OS is
booted via UEFI and receives the devicetree from the firmware through
the EFI configuration table instead of shipping its own. U-Boot has
the handoff mechanism (efi_install_fdt()), but no generic way to say
which devicetree the firmware owns or where it lives, so vendors carry
downstream commands for it (e.g. MediaTek's dtbprobe) and every board
reinvents discovery, selection and authentication.
This series adds a bootstd-level facility for sourcing the
firmware-owned devicetree from a FIT manifest on a dedicated GPT
partition:
- one FIT image ("fdt.itb" by default) on a partition identified by
type UUID and/or name describes and carries the base devicetree and
its overlays. FIT configurations name the bootable combinations, so
signing a configuration authenticates the base, the overlay set and
their ordering in one go;
- the control devicetree points at the source: a 'firmware-fdt-source'
phandle under /bootstd referencing a 'u-boot,firmware-fdt-block' node
under the owning media device;
- configuration selection: compatible best-match against the control
devicetree (CONFIG_FIT_BEST_MATCH), then the manifest default, with
environment overrides for explicit choice (one manifest can serve
both a vendor OS wanting the full overlay stack and a generic
distro wanting the plain base tree);
- both EFI launch paths consume it, the EFI bootmeth (disk and network)
and the EFI boot manager, all funneling into efi_install_fdt();
- fail closed: once a source is configured, any failure to produce
the devicetree ends the boot rather than silently falling back to an
unverified one.
Patch 1 adds the core helper (source binding, partition lookup,
manifest assembly and selection); patch 2 hooks the EFI bootmeth,
disk and network paths; patch 3 the EFI boot manager; patch 4 adds
the sandbox tests.
For more details please check the RFC at [1] (or the commits).
[1] https://lore.kernel.org/u-boot/140504E6-5BCB-4566-A03F-2A94B0F5BA01@baylibre.com/
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
Carlo Caione (4):
bootstd: add a firmware-owned devicetree source
bootmeth: efi: source the devicetree from a firmware partition
efi_loader: bootmgr: install the firmware-owned devicetree
test: bootstd: add firmware-FDT source tests
MAINTAINERS | 3 +
arch/sandbox/dts/test.dts | 24 ++
boot/Kconfig | 23 ++
boot/Makefile | 1 +
boot/bootmeth_efi.c | 81 +++++++
boot/firmware_fdt.c | 382 ++++++++++++++++++++++++++++++
configs/sandbox_defconfig | 1 +
doc/develop/bootstd/firmware_fdt.rst | 94 ++++++++
doc/develop/bootstd/index.rst | 1 +
doc/device-tree-bindings/firmware-fdt.txt | 149 ++++++++++++
include/firmware_fdt.h | 78 ++++++
lib/efi_loader/efi_bootmgr.c | 50 +++-
test/boot/Makefile | 1 +
test/boot/firmware_fdt.c | 292 +++++++++++++++++++++++
test/py/tests/test_ut.py | 110 +++++++++
15 files changed, 1283 insertions(+), 7 deletions(-)
---
base-commit: a3f393c02cf10f44310c6ddf3dedf354c2e877a0
change-id: 20260706-ccaione-upstream-ebbr-206c30be3a3a
Best regards,
--
Carlo Caione <ccaione@baylibre.com>
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/4] bootstd: add a firmware-owned devicetree source
2026-07-06 15:06 [PATCH 0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
@ 2026-07-06 15:06 ` Carlo Caione
2026-07-08 17:18 ` Tom Rini
` (2 more replies)
2026-07-06 15:06 ` [PATCH 2/4] bootmeth: efi: source the devicetree from a firmware partition Carlo Caione
` (3 subsequent siblings)
4 siblings, 3 replies; 14+ messages in thread
From: Carlo Caione @ 2026-07-06 15:06 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen, Carlo Caione
Platforms following EBBR / Arm SystemReady IR keep the devicetree on
a firmware-owned partition, updated independently of the operating
system, rather than shipping it in the OS image or on the EFI System
Partition. U-Boot has no generic way to source the devicetree from such
a partition, so vendors carry out-of-tree machinery for it.
Add a bootstd helper, firmware_fdt_load(), that assembles the OS
devicetree from a FIT manifest carried on that partition: the FIT images
hold the base DTB and its overlays, and each FIT configuration names one
bootable combination through its 'fdt' property.
The partition is described in the control devicetree: the bootstd node
carries a 'firmware-fdt-source' phandle to a source node, a child of
the media device owning the partition, which identifies it by GPT type
UUID and/or name. The 'boot_dtb' environment variable may pin an A/B
partition number.
The error semantics are fail closed: -ENOENT strictly means "no source
configured" (the only case where callers may fall back to their normal
devicetree); once a source is configured, any assembly failure is
fatal, and downstream -ENOENT codes (missing manifest, missing FIT
configuration) are remapped so a missing or bad firmware devicetree is
never silently replaced by an unverified one. Manifest images carrying
a 'load' property are rejected so a misbuilt manifest cannot overwrite
arbitrary memory.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
MAINTAINERS | 3 +
boot/Kconfig | 23 ++
boot/Makefile | 1 +
boot/firmware_fdt.c | 382 ++++++++++++++++++++++++++++++
doc/develop/bootstd/firmware_fdt.rst | 94 ++++++++
doc/develop/bootstd/index.rst | 1 +
doc/device-tree-bindings/firmware-fdt.txt | 149 ++++++++++++
include/firmware_fdt.h | 78 ++++++
8 files changed, 731 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 571af196465..00078f93a95 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1002,8 +1002,10 @@ F: boot/bootdev*.c
F: boot/bootflow.c
F: boot/bootmeth*.c
F: boot/bootstd.c
+F: boot/firmware_fdt.c
F: cmd/bootdev.c
F: cmd/bootflow.c
+F: doc/device-tree-bindings/firmware-fdt.txt
F: doc/develop/bootstd/
F: doc/usage/bootdev.rst
F: doc/usage/bootflow.rst
@@ -1013,6 +1015,7 @@ F: include/bootdev.h
F: include/bootflow.h
F: include/bootmeth.h
F: include/bootstd.h
+F: include/firmware_fdt.h
F: net/eth_bootdevice.c
F: test/boot/
diff --git a/boot/Kconfig b/boot/Kconfig
index ae6f09a6ede..a93cc3e0c3c 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -638,6 +638,29 @@ config BOOTMETH_EFILOADER
This provides a way to try out standard boot on an existing boot flow.
+config BOOTSTD_FIRMWARE_FDT
+ bool "Source the devicetree from a firmware-owned partition"
+ depends on BOOTSTD && BLK && FIT
+ select EFI_PARTITION
+ select PARTITION_TYPE_GUID
+ select OF_LIBFDT
+ select OF_LIBFDT_OVERLAY
+ help
+ Source the devicetree from a firmware-owned partition rather than
+ from the EFI System Partition or U-Boot's built-in control
+ devicetree. The partition carries a FIT manifest whose images hold
+ the base DTB and its overlays, and whose configurations name the
+ bootable combinations. The assembled devicetree is handed to the OS,
+ so it can be updated as part of the firmware, independently of the
+ operating system.
+
+ For secure boot, sign the manifest configurations and enable
+ FIT_SIGNATURE with a required key in the control devicetree; the
+ standard verified-boot policy then rejects unsigned manifests.
+
+ This is intended for platforms following EBBR / Arm SystemReady IR.
+ Say N unless you are booting such a platform.
+
config BOOTMETH_EFI_BOOTMGR
bool "Bootdev support for EFI boot manager"
depends on EFI_BOOTMGR
diff --git a/boot/Makefile b/boot/Makefile
index 7fb56e7ef37..8e6f0dfc8d5 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_$(PHASE_)BOOTSTD) += bootstd-uclass.o
obj-$(CONFIG_$(PHASE_)BOOTSTD_MENU) += bootflow_menu.o
obj-$(CONFIG_$(PHASE_)BOOTSTD_PROG) += prog_boot.o
+obj-$(CONFIG_$(PHASE_)BOOTSTD_FIRMWARE_FDT) += firmware_fdt.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX) += bootmeth_extlinux.o
obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX_PXE) += bootmeth_pxe.o
diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
new file mode 100644
index 00000000000..952299f1e1b
--- /dev/null
+++ b/boot/firmware_fdt.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Firmware-owned devicetree (FDT) source.
+ *
+ * Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a
+ * firmware-owned partition rather than in the OS image or the EFI System
+ * Partition. The devicetree is carried by a FIT manifest on that partition:
+ * the FIT images hold the base DTB and its overlays, and each FIT
+ * configuration names one bootable combination through its 'fdt' property.
+ *
+ * The partition is described by the control devicetree: the bootstd node
+ * carries a 'firmware-fdt-source' phandle to a source node, a child of the
+ * media device that owns the partition, e.g.:
+ *
+ * bootstd {
+ * compatible = "u-boot,boot-std";
+ * firmware-fdt-source = <&fw_fdt>;
+ * };
+ * &mmc0 {
+ * fw_fdt: firmware-fdt {
+ * compatible = "u-boot,firmware-fdt-block";
+ * partition-type-uuid = "...."; (GPT type UUID)
+ * partition-name = "firmware"; (optional fallback)
+ * filename = "fdt.itb"; (optional, the default)
+ * };
+ * };
+ *
+ * At most two runtime values come from the environment: 'boot_dtb' may pin
+ * an A/B firmware partition and 'fw_fdt_config' may select a FIT
+ * configuration. Both only choose among combinations the firmware author
+ * shipped (and, in secure mode, signed).
+ */
+
+#define LOG_CATEGORY UCLASS_BOOTSTD
+
+#include <blk.h>
+#include <dm.h>
+#include <env.h>
+#include <firmware_fdt.h>
+#include <fs.h>
+#include <image.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <part.h>
+#include <vsprintf.h>
+#include <dm/ofnode.h>
+#include <linux/libfdt.h>
+#include <linux/sizes.h>
+#include <linux/string.h>
+
+/* The manifest lives in a filesystem on a GPT partition of a block device */
+#define FW_FDT_COMPAT_BLOCK "u-boot,firmware-fdt-block"
+
+/* Default manifest filename on the firmware partition */
+#define FW_FDT_FILENAME "fdt.itb"
+
+/* Sanity cap on the manifest size */
+#define FW_FDT_MAX_SIZE SZ_4M
+
+/**
+ * fw_fdt_get_source() - find the configured firmware-FDT source node
+ *
+ * Reads the 'firmware-fdt-source' phandle from the bootstd node.
+ *
+ * @srcp: returns the source ofnode on success
+ * Return: 0 on success, -ENOENT if no source is configured, -EINVAL if the
+ * property is present but its phandle does not resolve
+ */
+static int fw_fdt_get_source(ofnode *srcp)
+{
+ ofnode bootstd, src;
+ const void *prop;
+
+ bootstd = ofnode_by_compatible(ofnode_null(), "u-boot,boot-std");
+ if (!ofnode_valid(bootstd))
+ return -ENOENT;
+
+ prop = ofnode_get_property(bootstd, "firmware-fdt-source", NULL);
+ if (!prop)
+ return -ENOENT;
+
+ src = ofnode_parse_phandle(bootstd, "firmware-fdt-source", 0);
+ if (!ofnode_valid(src))
+ return log_msg_ret("phandle", -EINVAL);
+
+ *srcp = src;
+
+ return 0;
+}
+
+static int fw_fdt_get_checked_source(ofnode *srcp)
+{
+ int ret;
+
+ ret = fw_fdt_get_source(srcp);
+ if (ret)
+ return ret;
+
+ if (!ofnode_device_is_compatible(*srcp, FW_FDT_COMPAT_BLOCK))
+ return log_msg_ret("compat", -EOPNOTSUPP);
+
+ return 0;
+}
+
+/**
+ * fw_fdt_get_blk() - resolve the block device that owns the source node
+ *
+ * The source node is a child of its media device (e.g. &mmc0); resolve that
+ * parent to a udevice. The lookup also probes it, which the EFI boot-manager
+ * path relies on.
+ *
+ * @src: the firmware-FDT source node
+ * @descp: returns the block descriptor on success
+ * Return: 0 on success, negative on error
+ */
+static int fw_fdt_get_blk(ofnode src, struct blk_desc **descp)
+{
+ struct udevice *media, *blk;
+ ofnode parent;
+ int ret;
+
+ parent = ofnode_get_parent(src);
+ if (!ofnode_valid(parent))
+ return log_msg_ret("par", -EINVAL);
+
+ /* a source is configured: remap -ENOENT to -ENODEV to fail closed */
+ ret = device_get_global_by_ofnode(parent, &media);
+ if (ret)
+ return log_msg_ret("media", ret == -ENOENT ? -ENODEV : ret);
+
+ ret = blk_get_from_parent(media, &blk);
+ if (ret)
+ return log_msg_ret("blk", ret == -ENOENT ? -ENODEV : ret);
+
+ *descp = dev_get_uclass_plat(blk);
+
+ return 0;
+}
+
+/**
+ * fw_fdt_find_part() - find the firmware partition on @desc
+ *
+ * 'boot_dtb', if set, pins an explicit partition number. Otherwise the
+ * first partition matching every configured selector is used: when both
+ * @type_uuid and @name are configured, both must match, so a misprovisioned
+ * disk fails instead of silently selecting whichever same-type (e.g. A/B)
+ * partition comes first. At least one selector must be configured.
+ *
+ * @desc: block device to scan
+ * @type_uuid: GPT type UUID to match, or NULL
+ * @name: partition name to match, or NULL
+ * Return: partition number (>= 1), -EINVAL if 'boot_dtb' is set to an
+ * invalid partition number or no selector is configured, or -ENODEV if no
+ * partition matched
+ */
+static int fw_fdt_find_part(struct blk_desc *desc, const char *type_uuid,
+ const char *name)
+{
+ const char *sel;
+ struct disk_partition info;
+ bool want_type = type_uuid && *type_uuid;
+ bool want_name = name && *name;
+ int p;
+
+ sel = env_get("boot_dtb");
+ if (sel && *sel) {
+ char *end;
+ ulong pin;
+
+ pin = dectoul(sel, &end);
+ if (*end || !pin || pin > MAX_SEARCH_PARTITIONS)
+ return log_msg_ret("pin", -EINVAL);
+
+ if (part_get_info(desc, pin, &info))
+ return log_msg_ret("pin", -ENODEV);
+
+ return pin;
+ }
+
+ if (!want_type && !want_name)
+ return log_msg_ret("sel", -EINVAL);
+
+ for (p = 1; p <= MAX_SEARCH_PARTITIONS; p++) {
+ bool type_match, name_match;
+
+ if (part_get_info(desc, p, &info))
+ continue;
+
+ /* UUID text is case-insensitive (RFC 4122) */
+ type_match = !want_type ||
+ !strncasecmp(disk_partition_type_guid(&info), type_uuid,
+ UUID_STR_LEN);
+ name_match = !want_name ||
+ !strcmp((const char *)info.name, name);
+
+ if (type_match && name_match)
+ return p;
+ }
+
+ return -ENODEV;
+}
+
+/**
+ * fw_fdt_read_fit() - read the FIT manifest into an allocated buffer
+ *
+ * @desc: block device holding the firmware partition
+ * @part: partition number
+ * @fname: manifest filename
+ * @bufp: returns the allocated buffer holding the manifest
+ * @sizep: returns the manifest size in bytes
+ * Return: 0 on success, negative on error
+ */
+static int fw_fdt_read_fit(struct blk_desc *desc, int part, const char *fname,
+ void **bufp, ulong *sizep)
+{
+ loff_t size;
+ int ret;
+
+ ret = fs_set_blk_dev_with_part(desc, part);
+ if (ret)
+ return log_msg_ret("fs", -EIO);
+
+ ret = fs_size(fname, &size);
+ if (ret)
+ return log_msg_ret("size", -EIO);
+
+ if (!size || size > FW_FDT_MAX_SIZE)
+ return log_msg_ret("big", -E2BIG);
+
+ /* fs_size() consumed the mount */
+ ret = fs_set_blk_dev_with_part(desc, part);
+ if (ret)
+ return log_msg_ret("fs2", -EIO);
+
+ ret = fs_read_alloc(fname, size, 0, bufp);
+ if (ret)
+ return log_msg_ret("read", ret);
+
+ *sizep = size;
+
+ return 0;
+}
+
+/**
+ * fw_fdt_check_images() - reject manifests that are not self-contained
+ *
+ * @fit: the FIT manifest
+ * Return: 0 if every image is embedded and has no load address, -EINVAL
+ * otherwise
+ */
+static int fw_fdt_check_images(const void *fit)
+{
+ int images, node;
+
+ images = fdt_path_offset(fit, FIT_IMAGES_PATH);
+ if (images < 0)
+ return log_msg_ret("img", -EINVAL);
+
+ fdt_for_each_subnode(node, fit, images) {
+ if (fdt_getprop(fit, node, FIT_LOAD_PROP, NULL))
+ return log_msg_ret("load", -EINVAL);
+
+ if (fdt_getprop(fit, node, FIT_DATA_OFFSET_PROP, NULL) ||
+ fdt_getprop(fit, node, FIT_DATA_POSITION_PROP, NULL))
+ return log_msg_ret("ext", -EINVAL);
+ }
+
+ return 0;
+}
+
+/**
+ * fw_fdt_assemble() - load the manifest and assemble the devicetree
+ *
+ * @out: returns the assembled devicetree and its backing buffers
+ * @src: the (validated) firmware-FDT source node
+ * Return: 0 on success, negative on error
+ */
+static int fw_fdt_assemble(struct firmware_fdt *out, ofnode src)
+{
+ struct bootm_headers images;
+ const char *type_uuid, *part_name, *fname, *conf;
+ struct blk_desc *desc;
+ ulong data, len;
+ void *fdt;
+ int part, ret;
+
+ memset(&images, '\0', sizeof(images));
+ images.verify = 1;
+
+ fname = ofnode_read_string(src, "filename");
+ if (!fname)
+ fname = FW_FDT_FILENAME;
+
+ type_uuid = ofnode_read_string(src, "partition-type-uuid");
+ part_name = ofnode_read_string(src, "partition-name");
+
+ ret = fw_fdt_get_blk(src, &desc);
+ if (ret)
+ return ret;
+
+ part = fw_fdt_find_part(desc, type_uuid, part_name);
+ if (part < 0)
+ return log_msg_ret("part", part);
+
+ ret = fw_fdt_read_fit(desc, part, fname, &out->fit, &out->fit_size);
+ if (ret)
+ return ret;
+
+ ret = fit_check_format(out->fit, out->fit_size);
+ if (ret)
+ return log_msg_ret("fit", -EINVAL);
+
+ ret = fw_fdt_check_images(out->fit);
+ if (ret)
+ return ret;
+
+ conf = env_get("fw_fdt_config");
+
+ ret = boot_get_fdt_fit(&images, map_to_sysmem(out->fit), NULL, &conf,
+ IH_ARCH_DEFAULT, &data, &len);
+ if (ret < 0)
+ return log_msg_ret("conf", ret);
+
+ fdt = map_sysmem(data, len);
+
+ ret = fdt_check_full(fdt, len);
+ if (ret)
+ return log_msg_ret("chk", -EINVAL);
+
+ out->fdt = fdt;
+ out->size = len;
+ out->name = fname;
+
+ return 0;
+}
+
+int firmware_fdt_load(struct firmware_fdt *out)
+{
+ ofnode src;
+ int ret;
+
+ memset(out, '\0', sizeof(*out));
+
+ ret = fw_fdt_get_checked_source(&src);
+ if (ret)
+ return ret;
+
+ ret = fw_fdt_assemble(out, src);
+ if (ret) {
+ firmware_fdt_free(out);
+
+ /*
+ * Callers treat -ENOENT as "no source configured" and fall
+ * back to their normal devicetree. A source IS configured
+ * here, so remap any downstream -ENOENT (missing manifest,
+ * missing FIT configuration, ...) to -ENODEV to keep the
+ * failure fatal (fail closed).
+ */
+ if (ret == -ENOENT)
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+void firmware_fdt_free(struct firmware_fdt *fw)
+{
+ u8 *fit = fw->fit;
+
+ /*
+ * The assembled devicetree either points into the manifest buffer or
+ * is a separate allocation (made when applying overlays, or when
+ * aligning the devicetree); free it only in the latter case.
+ */
+ if (fw->fdt &&
+ ((u8 *)fw->fdt < fit || (u8 *)fw->fdt >= fit + fw->fit_size))
+ free(fw->fdt);
+
+ free(fw->fit);
+ memset(fw, '\0', sizeof(*fw));
+}
diff --git a/doc/develop/bootstd/firmware_fdt.rst b/doc/develop/bootstd/firmware_fdt.rst
new file mode 100644
index 00000000000..d849e66b5db
--- /dev/null
+++ b/doc/develop/bootstd/firmware_fdt.rst
@@ -0,0 +1,94 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+Firmware-owned devicetree
+=========================
+
+Some platforms following EBBR / Arm SystemReady IR treat the devicetree as
+part of the firmware: it lives on a firmware-owned partition and is updated
+independently of the operating system, instead of being shipped in the OS
+image or on the EFI System Partition. U-Boot must read that devicetree,
+assemble it (base plus overlays) and hand it to the OS.
+
+The :c:func:`firmware_fdt_load` helper (``CONFIG_BOOTSTD_FIRMWARE_FDT``)
+provides this. It is consumed by both EFI launch paths so the firmware-owned
+devicetree is installed regardless of how the EFI application is started:
+
+ - the per-device EFI bootmeth (``bootmeth_efi``), and
+ - the EFI boot manager (``efi_bootmgr_run()``).
+
+In each case the assembled devicetree is installed into the EFI configuration
+table via :c:func:`efi_install_fdt`, exactly like any other source, so the OS
+cannot tell where it came from.
+
+This is a generic replacement for vendor-specific firmware-devicetree
+commands.
+
+The manifest
+------------
+
+The firmware partition carries a FIT manifest (by default ``fdt.itb``). Its
+images hold the base DTB and any overlays, and each of its configurations
+names one bootable combination through the standard ``fdt`` property::
+
+ configurations {
+ default = "conf-panel";
+ conf-panel {
+ fdt = "fdt-base", "fdt-panel";
+ };
+ };
+
+The helper selects a configuration, verifies it, loads the base devicetree
+and applies the listed overlays in order (via :c:func:`boot_get_fdt_fit`, the
+same code path ``bootm`` uses). The manifest describes and carries the
+devicetree as one artefact, updated atomically with it.
+
+Images in the manifest must be self-contained flat devicetrees: images that
+carry a ``load`` address and manifests using external data are rejected. With
+``FIT_SIGNATURE`` enabled, node and configuration names must not contain
+``@``.
+
+Configuration
+-------------
+
+The manifest location is described in the control devicetree (see
+``doc/device-tree-bindings/firmware-fdt.txt``): the ``bootstd`` node carries
+a ``firmware-fdt-source`` phandle to a ``u-boot,firmware-fdt-block`` node,
+which is a child of the media device that owns the partition and identifies
+it by GPT type UUID and/or name, with an optional ``filename`` for the
+manifest path.
+
+Two optional environment variables select among what the manifest ships:
+``boot_dtb`` pins a partition number (A/B firmware partitions) and
+``fw_fdt_config`` names the FIT configuration to use (defaulting to the
+manifest's ``default`` configuration).
+
+If no source is configured, the helper returns ``-ENOENT`` and the caller
+falls back to its normal devicetree source (ESP / built-in control FDT). If a
+source is configured but cannot be assembled, the error is fatal for that EFI
+launch path; this prevents a bad or unauthenticated firmware devicetree from
+being silently replaced by another devicetree source.
+
+A firmware-owned devicetree is the complete, authoritative devicetree: no
+other devicetree source is layered on top of it. In particular,
+extension-board overlays (``extension_scan()``) are intentionally not
+applied, since modifying the assembled (and, in secure mode, signed)
+devicetree would defeat the authenticated-combination model. Boards using
+extension boards should ship each supported combination as a manifest
+configuration and select it with ``fw_fdt_config``.
+
+Secure boot
+-----------
+
+Signing a FIT configuration authenticates the whole combination: the base,
+the overlay set and its ordering are the signed unit, and a tampered selector
+can only pick among combinations the firmware author pre-signed. Sign the
+manifest and inject the public key into U-Boot's control devicetree as
+usual::
+
+ mkimage -f fdt.its -k keys -K u-boot.dtb -r fdt.itb
+
+With ``CONFIG_FIT_SIGNATURE`` enabled and a required key in the control
+devicetree, verification is enforced by the standard verified-boot policy:
+an unsigned or tampered manifest is rejected and, because a configured source
+never falls back, the boot fails closed rather than booting an unverified
+devicetree.
diff --git a/doc/develop/bootstd/index.rst b/doc/develop/bootstd/index.rst
index ec74fc2fb9d..a7cd4c47261 100644
--- a/doc/develop/bootstd/index.rst
+++ b/doc/develop/bootstd/index.rst
@@ -14,4 +14,5 @@ Standard Boot
cros
rauc
script
+ firmware_fdt
sandbox
diff --git a/doc/device-tree-bindings/firmware-fdt.txt b/doc/device-tree-bindings/firmware-fdt.txt
new file mode 100644
index 00000000000..9b7cf2aff59
--- /dev/null
+++ b/doc/device-tree-bindings/firmware-fdt.txt
@@ -0,0 +1,149 @@
+U-Boot firmware-owned devicetree source (firmware-fdt)
+======================================================
+
+Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a
+firmware-owned partition, updated independently of the operating system,
+rather than shipping it in the OS image or the EFI System Partition. The
+partition carries a FIT manifest: its images hold the base DTB and any
+overlays, and each of its configurations names one bootable combination
+through the standard 'fdt' property. U-Boot selects a configuration,
+verifies it per the usual verified-boot policy, assembles the devicetree
+(base plus overlays, in order) and installs it via the EFI configuration
+table.
+
+This binding describes where that manifest lives. The source is referenced
+from the bootstd node and described by a node that is a child of the media
+device (UCLASS_MMC, ...) that owns the partition.
+
+
+bootstd node
+------------
+
+Optional property:
+
+firmware-fdt-source:
+ phandle to the firmware-fdt source node to use
+
+
+firmware-fdt source node
+------------------------
+
+Required properties:
+
+compatible:
+ "u-boot,firmware-fdt-block" - the manifest lives in a filesystem on a
+ GPT partition of a block device (the parent media device)
+
+The partition is selected by the 'boot_dtb' environment variable, if set,
+which pins a partition number (for A/B firmware partitions). Otherwise the
+first partition matching every configured selector is used: when both
+'partition-type-uuid' and 'partition-name' are present, both must match (so
+a misprovisioned disk fails closed instead of silently selecting whichever
+same-type partition comes first).
+
+At least one of 'partition-type-uuid' or 'partition-name' must be present.
+
+Optional properties:
+
+partition-type-uuid:
+ GPT partition type UUID (string, case-insensitive) identifying the
+ firmware partition. When A/B firmware partitions share a type UUID,
+ 'partition-name' disambiguates between them.
+
+partition-name:
+ GPT partition name (string) identifying the firmware partition. Used to
+ disambiguate, or as a fallback when 'partition-type-uuid' is absent.
+
+filename:
+ Path of the FIT manifest on the partition (default: "fdt.itb").
+
+
+Environment
+-----------
+
+Two optional environment variables select among what the manifest ships:
+
+ boot_dtb pin a specific partition number (A/B firmware partitions)
+ fw_fdt_config name of the FIT configuration to use; when unset the
+ manifest's default configuration (or the best compatible
+ match, with FIT_BEST_MATCH) is used
+
+Both values only choose among combinations the firmware author shipped; with
+signed configurations a tampered value cannot select an unsigned combination.
+
+
+The manifest
+------------
+
+The manifest is a standard FIT image. Every image must be a flat devicetree
+('type = "flat_dt"') without a load address (images carrying a 'load'
+property are rejected), self-contained (no external data). For secure boot,
+sign the configurations and enable FIT_SIGNATURE with a required key in the
+control devicetree. Example source (.its):
+
+ /dts-v1/;
+ / {
+ description = "Firmware-owned devicetree";
+ #address-cells = <1>;
+
+ images {
+ fdt-base {
+ description = "base board devicetree";
+ data = /incbin/("board.dtb");
+ type = "flat_dt";
+ arch = "arm64";
+ compression = "none";
+ hash-1 { algo = "sha256"; };
+ };
+ fdt-panel {
+ description = "panel overlay";
+ data = /incbin/("panel.dtbo");
+ type = "flat_dt";
+ arch = "arm64";
+ compression = "none";
+ hash-1 { algo = "sha256"; };
+ };
+ };
+
+ configurations {
+ default = "conf-panel";
+ conf-panel {
+ fdt = "fdt-base", "fdt-panel";
+ signature-1 {
+ algo = "sha256,rsa2048";
+ key-name-hint = "fw";
+ sign-images = "fdt";
+ };
+ };
+ conf-base {
+ fdt = "fdt-base";
+ signature-1 {
+ algo = "sha256,rsa2048";
+ key-name-hint = "fw";
+ sign-images = "fdt";
+ };
+ };
+ };
+ };
+
+Note: with FIT_SIGNATURE enabled, node and configuration names must not
+contain the '@' character.
+
+
+Example
+-------
+
+ bootstd {
+ compatible = "u-boot,boot-std";
+
+ firmware-fdt-source = <&fw_fdt>;
+ };
+
+ &mmc0 {
+ fw_fdt: firmware-fdt {
+ compatible = "u-boot,firmware-fdt-block";
+ partition-type-uuid =
+ "384e979b-eb76-435a-a3a6-1a071dbad91d";
+ partition-name = "firmware";
+ };
+ };
diff --git a/include/firmware_fdt.h b/include/firmware_fdt.h
new file mode 100644
index 00000000000..269e6e741f2
--- /dev/null
+++ b/include/firmware_fdt.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Firmware-owned devicetree (FDT) source for EFI boot.
+ *
+ * Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a
+ * firmware-owned partition rather than in the OS image or the EFI System
+ * Partition. This provides a single helper that locates that partition,
+ * assembles the devicetree from the FIT manifest it carries, and returns the
+ * result so the caller can install it via the EFI configuration table
+ * (efi_install_fdt()).
+ *
+ * The same helper is intended to be consumed by both the per-device EFI
+ * bootmeth and the EFI boot manager, so firmware-owned DT provenance is
+ * honoured regardless of which path launches the EFI application.
+ */
+
+#ifndef __FIRMWARE_FDT_H
+#define __FIRMWARE_FDT_H
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+/**
+ * struct firmware_fdt - an assembled, firmware-owned devicetree
+ *
+ * @fdt: pointer to the assembled devicetree in memory
+ * @size: size of the assembled devicetree, in bytes
+ * @name: manifest filename it was assembled from (for diagnostics)
+ * @fit: internal: buffer holding the FIT manifest
+ * @fit_size: internal: size of the FIT manifest, in bytes
+ *
+ * All memory is owned by the helper: release it with firmware_fdt_free()
+ * once the devicetree has been consumed (installed or copied).
+ */
+struct firmware_fdt {
+ void *fdt;
+ ulong size;
+ const char *name;
+ void *fit;
+ ulong fit_size;
+};
+
+#if CONFIG_IS_ENABLED(BOOTSTD_FIRMWARE_FDT)
+/**
+ * firmware_fdt_load() - assemble the devicetree from a firmware partition
+ *
+ * Assemble the devicetree (the base DTB with its overlays applied, as
+ * described by the FIT manifest on the configured firmware partition) and
+ * return it in @out, ready to hand to the OS.
+ *
+ * @out: returns the assembled devicetree on success
+ * Return: 0 on success; -ENOENT if no source is configured (the caller may
+ * fall back to its normal devicetree); another negative errno if a
+ * configured source fails to assemble (the caller must fail, never
+ * fall back)
+ */
+int firmware_fdt_load(struct firmware_fdt *out);
+
+/**
+ * firmware_fdt_free() - release the memory behind an assembled devicetree
+ *
+ * Safe to call on a zeroed or already-freed @fw.
+ *
+ * @fw: the assembled devicetree to release
+ */
+void firmware_fdt_free(struct firmware_fdt *fw);
+#else
+static inline int firmware_fdt_load(struct firmware_fdt *out)
+{
+ return -ENOSYS;
+}
+
+static inline void firmware_fdt_free(struct firmware_fdt *fw)
+{
+}
+#endif
+
+#endif /* __FIRMWARE_FDT_H */
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/4] bootmeth: efi: source the devicetree from a firmware partition
2026-07-06 15:06 [PATCH 0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
2026-07-06 15:06 ` [PATCH 1/4] bootstd: add a firmware-owned devicetree source Carlo Caione
@ 2026-07-06 15:06 ` Carlo Caione
2026-07-09 20:18 ` Simon Glass
2026-07-06 15:06 ` [PATCH 3/4] efi_loader: bootmgr: install the firmware-owned devicetree Carlo Caione
` (2 subsequent siblings)
4 siblings, 1 reply; 14+ messages in thread
From: Carlo Caione @ 2026-07-06 15:06 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen, Carlo Caione
When a firmware-owned devicetree source is configured (EBBR / Arm
SystemReady IR), assemble the devicetree from the FIT manifest on
the firmware partition instead of reading a DTB from the EFI System
Partition, and hand it to the EFI application through the normal
bflow->fdt_addr path (efi_install_fdt()).
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
boot/bootmeth_efi.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
index e187dc39912..ae3f1c9b91e 100644
--- a/boot/bootmeth_efi.c
+++ b/boot/bootmeth_efi.c
@@ -17,6 +17,7 @@
#include <efi_loader.h>
#include <env.h>
#include <extension_board.h>
+#include <firmware_fdt.h>
#include <fs.h>
#include <malloc.h>
#include <mapmem.h>
@@ -73,6 +74,61 @@ static int efiload_read_file(struct bootflow *bflow, ulong addr)
return 0;
}
+/**
+ * distro_efi_firmware_fdt() - try to stage the firmware-owned devicetree
+ *
+ * Prefer a firmware-owned devicetree if one is configured: assemble it
+ * from the FIT manifest on the firmware partition, stage it at 'fdt_addr_r'
+ * and fill the bootflow, so it reaches the EFI app through the normal
+ * bflow->fdt_addr path (efi_install_fdt()).
+ *
+ * The semantics are fail closed: only -ENOENT (no firmware-FDT source
+ * configured) lets the caller fall back to its normal devicetree source, so
+ * a missing or broken firmware devicetree is never silently replaced.
+ *
+ * @bflow: bootflow to update
+ * Return: 0 if the firmware devicetree was staged (the bootflow devicetree
+ * fields are set), -ENOENT if no source is configured, other -ve error if a
+ * configured source failed to assemble (the bootflow must fail)
+ */
+static int distro_efi_firmware_fdt(struct bootflow *bflow)
+{
+ struct firmware_fdt fw;
+ ulong fdt_addr;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(BOOTSTD_FIRMWARE_FDT))
+ return -ENOENT;
+
+ ret = firmware_fdt_load(&fw);
+ if (ret) {
+ if (ret != -ENOENT)
+ log_err("Failed to assemble the firmware devicetree (err %d)\n",
+ ret);
+ return ret;
+ }
+
+ /* stage it at fdt_addr_r, like any other devicetree */
+ fdt_addr = env_get_hex("fdt_addr_r", 0);
+ if (!fdt_addr || fw.size > SZ_4M) {
+ firmware_fdt_free(&fw);
+ return log_msg_ret("fwa", -EINVAL);
+ }
+
+ memcpy(map_sysmem(fdt_addr, fw.size), fw.fdt, fw.size);
+
+ bflow->fdt_fname = strdup(fw.name);
+ bflow->fdt_size = fw.size;
+ bflow->fdt_addr = fdt_addr;
+ firmware_fdt_free(&fw);
+ if (!bflow->fdt_fname)
+ return log_msg_ret("fwn", -ENOMEM);
+
+ log_debug("Using firmware-owned devicetree\n");
+
+ return 0;
+}
+
static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter)
{
/* This only works on block and network devices */
@@ -130,6 +186,18 @@ static int distro_efi_try_bootflow_files(struct udevice *dev,
fdt_addr = env_get_hex("fdt_addr_r", 0);
+ /*
+ * A staged firmware-owned devicetree is complete and authoritative,
+ * so return without considering any other devicetree source. The
+ * extension overlays below are deliberately not applied on top:
+ * such combinations belong in the manifest as configurations.
+ */
+ ret = distro_efi_firmware_fdt(bflow);
+ if (!ret)
+ return 0;
+ if (ret != -ENOENT)
+ return log_msg_ret("fwf", ret);
+
/* try the various available names */
ret = -ENOENT;
*fname = '\0';
@@ -268,6 +336,19 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
if (!bflow->fname)
return log_msg_ret("fi0", -ENOMEM);
+ /*
+ * A configured firmware-owned devicetree outranks the network-provided
+ * one (and the prior-stage / built-in devicetree below): a DHCP/TFTP
+ * server must not be able to replace it.
+ */
+ ret = distro_efi_firmware_fdt(bflow);
+ if (!ret) {
+ bflow->state = BOOTFLOWST_READY;
+ return 0;
+ }
+ if (ret != -ENOENT)
+ return log_msg_ret("fwf", ret);
+
/* read the DT file also */
ret = efi_get_distro_fdt_name(fname, sizeof(fname), 0);
if (ret == -EALREADY) {
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 3/4] efi_loader: bootmgr: install the firmware-owned devicetree
2026-07-06 15:06 [PATCH 0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
2026-07-06 15:06 ` [PATCH 1/4] bootstd: add a firmware-owned devicetree source Carlo Caione
2026-07-06 15:06 ` [PATCH 2/4] bootmeth: efi: source the devicetree from a firmware partition Carlo Caione
@ 2026-07-06 15:06 ` Carlo Caione
2026-07-09 20:19 ` Simon Glass
2026-07-06 15:06 ` [PATCH 4/4] test: bootstd: add firmware-FDT source tests Carlo Caione
2026-07-09 20:20 ` [0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Simon Glass
4 siblings, 1 reply; 14+ messages in thread
From: Carlo Caione @ 2026-07-06 15:06 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen, Carlo Caione
SystemReady IR boots through the UEFI boot manager, which does not go
through the per-device EFI bootmeth, so hooking the bootmeth alone
would leave the boot-manager path (including autoboot) on the control
devicetree.
Consume the firmware-FDT source in efi_bootmgr_run() too, so the
firmware-owned devicetree is installed via efi_install_fdt() regardless
of which EFI launch path is taken.
The firmware-owned devicetree is tried before the Boot#### load-option
FDT, so a stale or broken load option can neither override nor block a
configured (and possibly signed) firmware devicetree. As a side effect,
a devicetree passed directly to efi_bootmgr_run() now also outranks the
load-option FDT, which previously overrode it: an explicitly supplied
devicetree is the operator's immediate intent, and stored boot options
must not replace it.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
lib/efi_loader/efi_bootmgr.c | 50 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 43 insertions(+), 7 deletions(-)
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 8c9a9b5eb56..8835891c003 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -14,6 +14,7 @@
#include <efi.h>
#include <efi_device_path.h>
#include <env.h>
+#include <firmware_fdt.h>
#include <log.h>
#include <malloc.h>
#include <net.h>
@@ -1296,7 +1297,9 @@ out:
*
* Invoke EFI boot manager and execute a binary depending on
* boot options. If @fdt is not NULL, it will be passed to
- * the executed binary.
+ * the executed binary. Otherwise a configured firmware-owned
+ * devicetree source takes precedence over both a Boot#### load
+ * option FDT and the distro one.
*
* Return: status code
*/
@@ -1305,7 +1308,8 @@ efi_status_t efi_bootmgr_run(void *fdt)
efi_handle_t handle;
void *load_options;
efi_status_t ret;
- void *fdt_lo, *fdt_distro = NULL;
+ struct firmware_fdt fw = { 0 };
+ void *fdt_lo = NULL, *fdt_distro = NULL;
efi_uintn_t fdt_size;
/* Initialize EFI drivers */
@@ -1320,11 +1324,40 @@ efi_status_t efi_bootmgr_run(void *fdt)
}
if (!IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
- ret = load_fdt_from_load_option(&fdt_lo);
- if (ret != EFI_SUCCESS)
- return ret;
- if (fdt_lo)
- fdt = fdt_lo;
+ /*
+ * Prefer a firmware-owned devicetree (EBBR) over both the
+ * Boot#### load-option FDT and the distro/ESP one, and try it
+ * first, so a configured (and, in secure mode, signed)
+ * firmware DT can neither be overridden nor blocked by a
+ * stale or broken load-option FDT. An FDT explicitly passed
+ * to efi_bootmgr_run() (e.g. "bootefi bootmgr <addr>") still
+ * wins. Fail closed: a configured source that cannot be
+ * assembled ends the boot rather than falling back to an
+ * unverified devicetree.
+ */
+ if (!fdt && CONFIG_IS_ENABLED(BOOTSTD_FIRMWARE_FDT)) {
+ int err;
+
+ err = firmware_fdt_load(&fw);
+ if (!err) {
+ fdt = fw.fdt;
+ log_debug("Using firmware-owned devicetree\n");
+ } else if (err != -ENOENT) {
+ log_err("Failed to assemble the firmware devicetree (err %d)\n",
+ err);
+ ret = EFI_LOAD_ERROR;
+ goto out;
+ }
+ }
+
+ if (!fdt) {
+ ret = load_fdt_from_load_option(&fdt_lo);
+ if (ret != EFI_SUCCESS)
+ return ret;
+ if (fdt_lo)
+ fdt = fdt_lo;
+ }
+
if (!fdt) {
efi_load_distro_fdt(handle, &fdt_distro, &fdt_size);
fdt = fdt_distro;
@@ -1337,7 +1370,10 @@ efi_status_t efi_bootmgr_run(void *fdt)
*/
ret = efi_install_fdt(fdt);
+out:
if (!IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
+ /* efi_install_fdt() has copied the devicetree */
+ firmware_fdt_free(&fw);
free(fdt_lo);
if (fdt_distro)
efi_free_pages((uintptr_t)fdt_distro,
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 4/4] test: bootstd: add firmware-FDT source tests
2026-07-06 15:06 [PATCH 0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
` (2 preceding siblings ...)
2026-07-06 15:06 ` [PATCH 3/4] efi_loader: bootmgr: install the firmware-owned devicetree Carlo Caione
@ 2026-07-06 15:06 ` Carlo Caione
2026-07-09 20:19 ` Simon Glass
2026-07-09 20:20 ` [0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Simon Glass
4 siblings, 1 reply; 14+ messages in thread
From: Carlo Caione @ 2026-07-06 15:06 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen, Carlo Caione
Add sandbox coverage for firmware_fdt_load(): a python fixture builds
mmc11.img, a GPT disk with A/B 'firmware' partitions whose first
partition carries a FAT filesystem holding the FIT manifest (fdt.itb:
a base DTB plus one overlay, with a default configuration that applies
the overlay and a base-only 'conf-base' configuration).
The tests cover the happy path (default configuration, packed size,
both fixture properties visible), configuration selection via
'fw_fdt_config', and the fail-closed semantics: a missing pinned
partition and a selector naming a configuration the manifest does not
ship are fatal errors rather than -ENOENT, an unresolvable
'firmware-fdt-source' phandle is -EINVAL, and only a genuinely absent
source returns -ENOENT.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
arch/sandbox/dts/test.dts | 24 ++++
configs/sandbox_defconfig | 1 +
test/boot/Makefile | 1 +
test/boot/firmware_fdt.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++
test/py/tests/test_ut.py | 110 +++++++++++++++++
5 files changed, 428 insertions(+)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 0887de4333b..97199d1812e 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -49,6 +49,7 @@
mmc8 = "/mmc8";
mmc9 = "/mmc9";
mmc10 = "/mmc10";
+ mmc11 = "/mmc11";
pci0 = &pci0;
pci1 = &pci1;
pci2 = &pci2;
@@ -122,6 +123,14 @@
filename-prefixes = "/", "/boot/";
bootdev-order = "mmc2", "mmc1";
+ /*
+ * Not read by U-Boot: the firmware_fdt tests copy this into a
+ * live 'firmware-fdt-source' property at runtime (and delete
+ * it afterwards), so the source stays unconfigured for every
+ * other test. It also keeps a phandle emitted for &fw_fdt.
+ */
+ test-fw-fdt-source = <&fw_fdt>;
+
extlinux {
compatible = "u-boot,extlinux";
};
@@ -1348,6 +1357,21 @@
filename = "mmc10.img";
};
+ /* This is used for firmware-FDT (firmware_fdt) tests */
+ mmc11 {
+ status = "disabled";
+ compatible = "sandbox,mmc";
+ filename = "mmc11.img";
+
+ fw_fdt: firmware-fdt {
+ compatible = "u-boot,firmware-fdt-block";
+ partition-type-uuid =
+ "384e979b-eb76-435a-a3a6-1a071dbad91d";
+ partition-name = "firmware";
+ filename = "fdt.itb";
+ };
+ };
+
pch {
compatible = "sandbox,pch";
};
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ba800f7d19d..2d54de1c746 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -25,6 +25,7 @@ CONFIG_FIT_CIPHER=y
CONFIG_FIT_VERBOSE=y
CONFIG_BOOTMETH_ANDROID=y
CONFIG_BOOTMETH_RAUC=y
+CONFIG_BOOTSTD_FIRMWARE_FDT=y
CONFIG_UPL=y
CONFIG_LEGACY_IMAGE_FORMAT=y
CONFIG_MEASURED_BOOT=y
diff --git a/test/boot/Makefile b/test/boot/Makefile
index 89538d4f0a6..90704e6cbbc 100644
--- a/test/boot/Makefile
+++ b/test/boot/Makefile
@@ -4,6 +4,7 @@
ifdef CONFIG_UT_BOOTSTD
obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
+obj-$(CONFIG_BOOTSTD_FIRMWARE_FDT) += firmware_fdt.o
obj-$(CONFIG_FIT) += image.o
ifdef CONFIG_VIDEO_SANDBOX_SDL
diff --git a/test/boot/firmware_fdt.c b/test/boot/firmware_fdt.c
new file mode 100644
index 00000000000..ef137f82648
--- /dev/null
+++ b/test/boot/firmware_fdt.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the firmware-owned devicetree source (firmware_fdt_load()).
+ *
+ * Uses a sandbox mmc image (mmc11) carrying a GPT 'firmware' partition with
+ * a FAT filesystem holding the FIT manifest (fdt.itb: a base DTB and one
+ * overlay, with two configurations). The image is built by
+ * setup_firmware_fdt_image() in test/py/tests/test_ut.py.
+ *
+ * The shared test.dts deliberately does NOT configure a firmware-FDT source:
+ * a configured source turns the fail-closed semantics on for every EFI
+ * launch path, which would break the other sandbox EFI tests. Instead the
+ * bootstd node carries an inert 'test-fw-fdt-source' phandle, which these
+ * tests copy into a live 'firmware-fdt-source' property for their duration
+ * and delete again afterwards.
+ */
+
+#include <dm.h>
+#include <env.h>
+#include <firmware_fdt.h>
+#include <asm/global_data.h>
+#include <dm/lists.h>
+#include <dm/ofnode.h>
+#include <dm/root.h>
+#include <linux/libfdt.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define FWFDT_PROP "firmware-fdt-source"
+#define FWFDT_TEST_PROP "test-fw-fdt-source"
+
+static ofnode fwfdt_bootstd_node(void)
+{
+ return ofnode_by_compatible(ofnode_null(), "u-boot,boot-std");
+}
+
+/* Bind the (disabled) mmc node that owns the firmware-FDT image */
+static int fwfdt_bind_mmc(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ofnode root, node;
+
+ root = oftree_root(oftree_default());
+ node = ofnode_find_subnode(root, "mmc11");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false));
+
+ return 0;
+}
+
+/*
+ * Configure the firmware-FDT source for this test: copy the inert
+ * 'test-fw-fdt-source' phandle into the property firmware_fdt_load() reads.
+ */
+static int fwfdt_configure(struct unit_test_state *uts)
+{
+ const void *prop;
+ ofnode bootstd;
+ fdt32_t val;
+ int len;
+
+ bootstd = fwfdt_bootstd_node();
+ ut_assert(ofnode_valid(bootstd));
+
+ prop = ofnode_get_property(bootstd, FWFDT_TEST_PROP, &len);
+ ut_assertnonnull(prop);
+ ut_asserteq(sizeof(val), len);
+
+ /*
+ * Copy the value out first: on the flat tree, inserting the new
+ * property shifts the blob region the old property value lives in,
+ * so passing the in-blob pointer straight to ofnode_write_prop()
+ * would copy from the stale location.
+ */
+ memcpy(&val, prop, sizeof(val));
+ ut_assertok(ofnode_write_prop(bootstd, FWFDT_PROP, &val, sizeof(val),
+ true));
+
+ return 0;
+}
+
+/* Undo fwfdt_configure() and clear any environment the tests use */
+static int fwfdt_deconfigure(struct unit_test_state *uts)
+{
+ ofnode bootstd;
+
+ bootstd = fwfdt_bootstd_node();
+ ut_assert(ofnode_valid(bootstd));
+ if (ofnode_get_property(bootstd, FWFDT_PROP, NULL))
+ ut_assertok(ofnode_delete_prop(bootstd, FWFDT_PROP));
+
+ env_set("boot_dtb", NULL);
+ env_set("fw_fdt_config", NULL);
+
+ return 0;
+}
+
+/* Happy path: the manifest's default configuration applies base + overlay */
+static int firmware_fdt_test_load(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ void *fdt;
+
+ /*
+ * Configure before binding: on the flat tree, inserting the property
+ * shifts every later node offset, so a device bound beforehand would
+ * no longer match its (shifted) devicetree node.
+ */
+ ut_assertok(fwfdt_configure(uts));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(firmware_fdt_load(&fw));
+
+ ut_asserteq_str("fdt.itb", fw.name);
+ ut_assert(fw.size > 0);
+
+ fdt = fw.fdt;
+ ut_assertok(fdt_check_header(fdt));
+ /* the size reports the packed devicetree, not a padded buffer */
+ ut_asserteq(fw.size, fdt_totalsize(fdt));
+ /* the base property is present... */
+ ut_assertnonnull(fdt_getprop(fdt, 0, "fw-base-prop", NULL));
+ /* ...and the overlay was applied on top */
+ ut_assertnonnull(fdt_getprop(fdt, 0, "fw-overlay-prop", NULL));
+
+ firmware_fdt_free(&fw);
+ ut_assertnull(fw.fdt);
+
+ ut_assertok(fwfdt_deconfigure(uts));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_load, UTF_DM | UTF_SCAN_FDT);
+
+/* 'fw_fdt_config' selects another configuration the manifest ships */
+static int firmware_fdt_test_select(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ void *fdt;
+
+ ut_assertok(fwfdt_configure(uts));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(env_set("fw_fdt_config", "conf-base"));
+ ut_assertok(firmware_fdt_load(&fw));
+
+ fdt = fw.fdt;
+ ut_assertnonnull(fdt_getprop(fdt, 0, "fw-base-prop", NULL));
+ /* the base-only configuration applies no overlay */
+ ut_assertnull(fdt_getprop(fdt, 0, "fw-overlay-prop", NULL));
+
+ firmware_fdt_free(&fw);
+
+ ut_assertok(fwfdt_deconfigure(uts));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_select, UTF_DM | UTF_SCAN_FDT);
+
+/*
+ * A selector naming a configuration the manifest does not ship must be
+ * fatal: -ENOENT strictly means "no source configured", so the inner miss
+ * must not leak out and let the caller fall back (fail closed).
+ */
+static int firmware_fdt_test_bad_config(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+
+ ut_assertok(fwfdt_configure(uts));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(env_set("fw_fdt_config", "conf-nonexistent"));
+ ut_asserteq(-ENODEV, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_deconfigure(uts));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_bad_config, UTF_DM | UTF_SCAN_FDT);
+
+/* Pinning a partition that does not exist is a hard error (fail closed) */
+static int firmware_fdt_test_no_part(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+
+ ut_assertok(fwfdt_configure(uts));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(env_set("boot_dtb", "9"));
+ ut_asserteq(-ENODEV, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_deconfigure(uts));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_no_part, UTF_DM | UTF_SCAN_FDT);
+
+/*
+ * Without the runtime injection no source is configured: -ENOENT, the only
+ * case where callers may fall back. This also proves the shared test.dts
+ * stays inert for every other sandbox test.
+ */
+static int firmware_fdt_test_no_source(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_asserteq(-ENOENT, firmware_fdt_load(&fw));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_no_source, UTF_DM | UTF_SCAN_FDT);
+
+/*
+ * A 'firmware-fdt-source' property whose phandle does not resolve is a
+ * broken configuration and must be fatal, not mistaken for "no source".
+ */
+static int firmware_fdt_test_bad_source(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ ofnode bootstd;
+ fdt32_t bad;
+
+ bootstd = fwfdt_bootstd_node();
+ ut_assert(ofnode_valid(bootstd));
+
+ bad = cpu_to_fdt32(0x7fffffff);
+ ut_assertok(ofnode_write_prop(bootstd, FWFDT_PROP, &bad, sizeof(bad),
+ true));
+
+ ut_asserteq(-EINVAL, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_deconfigure(uts));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_bad_source, UTF_DM | UTF_SCAN_FDT);
+
+/* A manifest using external data is refused: it must be self-contained */
+static int firmware_fdt_test_external(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ ofnode node;
+
+ ut_assertok(fwfdt_configure(uts));
+
+ node = ofnode_path("/mmc11/firmware-fdt");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_write_string(node, "filename", "fdt-ext.itb"));
+
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_asserteq(-EINVAL, firmware_fdt_load(&fw));
+
+ ut_assertok(ofnode_write_string(node, "filename", "fdt.itb"));
+ ut_assertok(fwfdt_deconfigure(uts));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_external, UTF_DM | UTF_SCAN_FDT);
+
+/*
+ * When both partition-type-uuid and partition-name are configured, both
+ * must match: a name matching nothing must not fall back to whichever
+ * same-type (A/B) partition comes first.
+ */
+static int firmware_fdt_test_part_mismatch(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ ofnode node;
+
+ ut_assertok(fwfdt_configure(uts));
+
+ node = ofnode_path("/mmc11/firmware-fdt");
+ ut_assert(ofnode_valid(node));
+ /* the type UUID matches both A/B partitions; this name matches none */
+ ut_assertok(ofnode_write_string(node, "partition-name", "nomatch"));
+
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_asserteq(-ENODEV, firmware_fdt_load(&fw));
+
+ ut_assertok(ofnode_write_string(node, "partition-name", "firmware"));
+ ut_assertok(fwfdt_deconfigure(uts));
+
+ return 0;
+}
+BOOTSTD_TEST(firmware_fdt_test_part_mismatch, UTF_DM | UTF_SCAN_FDT);
diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
index dce5a37dd35..15c36fecafb 100644
--- a/test/py/tests/test_ut.py
+++ b/test/py/tests/test_ut.py
@@ -614,6 +614,115 @@ def setup_rauc_image(ubman):
boot.cleanup()
root.cleanup()
+def setup_firmware_fdt_image(ubman):
+ """Create mmc11.img for the firmware_fdt tests
+
+ A GPT disk with two firmware partitions (A/B) sharing a firmware type
+ UUID; partition 1 (label 'firmware') holds a FAT filesystem with the
+ FIT manifest (fdt.itb) carrying a base DTB and an overlay, with two
+ configurations: the default applies the overlay, 'conf-base' does not.
+ """
+ Partition = collections.namedtuple('part', 'start,size,name')
+ parts = {}
+
+ mmc_dev = 11
+ fname = os.path.join(ubman.config.source_dir, f'mmc{mmc_dev}.img')
+ fw_type = '384e979b-eb76-435a-a3a6-1a071dbad91d'
+ sect_size = 512
+
+ # Compile a tiny base DTB and an overlay, then wrap them in the manifest
+ src = os.path.join(ubman.config.persistent_data_dir, 'fwfdt')
+ mkdir_cond(src)
+ base_dtb = os.path.join(src, 'base.dtb')
+ ovl_dtbo = os.path.join(src, 'overlay.dtbo')
+ utils.run_and_log(
+ ubman, f'dtc -O dtb -o {base_dtb}',
+ stdin=b'/dts-v1/; / { compatible = "test,fw-fdt-base"; '
+ b'fw-base-prop = "base"; };')
+ utils.run_and_log(
+ ubman, f'dtc -O dtb -o {ovl_dtbo}',
+ stdin=b'/dts-v1/; /plugin/; &{/} { fw-overlay-prop = "applied"; };')
+
+ its = os.path.join(src, 'fdt.its')
+ with open(its, 'w', encoding='ascii') as outf:
+ outf.write(f'''
+/dts-v1/;
+/ {{
+\tdescription = "Firmware-owned OS devicetree";
+\t#address-cells = <1>;
+
+\timages {{
+\t\tfdt-base {{
+\t\t\tdata = /incbin/("{base_dtb}");
+\t\t\ttype = "flat_dt";
+\t\t\tarch = "sandbox";
+\t\t\tcompression = "none";
+\t\t\thash-1 {{ algo = "sha256"; }};
+\t\t}};
+\t\tfdt-overlay {{
+\t\t\tdata = /incbin/("{ovl_dtbo}");
+\t\t\ttype = "flat_dt";
+\t\t\tarch = "sandbox";
+\t\t\tcompression = "none";
+\t\t\thash-1 {{ algo = "sha256"; }};
+\t\t}};
+\t}};
+
+\tconfigurations {{
+\t\tdefault = "conf-overlay";
+\t\tconf-overlay {{
+\t\t\tfdt = "fdt-base", "fdt-overlay";
+\t\t}};
+\t\tconf-base {{
+\t\t\tfdt = "fdt-base";
+\t\t}};
+\t}};
+}};
+''')
+
+ fs_dir = os.path.join(src, 'fs')
+ mkdir_cond(fs_dir)
+ mkimage = os.path.join(ubman.config.build_dir, 'tools/mkimage')
+ utils.run_and_log(
+ ubman, f'{mkimage} -f {its} {os.path.join(fs_dir, "fdt.itb")}')
+
+ # An external-data variant, which firmware_fdt_load() must refuse
+ utils.run_and_log(
+ ubman, f'{mkimage} -E -f {its} {os.path.join(fs_dir, "fdt-ext.itb")}')
+
+ fat_img = fs_helper.mk_fs(ubman.config, 'vfat', 1 << 20, 'fwfdt',
+ src_dir=fs_dir)
+ with open(fat_img, 'rb') as inf:
+ fat_data = inf.read()
+
+ # GPT with two same-type firmware partitions; the FAT goes in partition 1
+ fat_sects = (len(fat_data) + sect_size - 1) // sect_size
+ utils.run_and_log(ubman, f'qemu-img create {fname} 8M')
+ utils.run_and_log(ubman, f'cgpt create {fname}')
+ ptr = 40
+ for num, label in ((1, 'firmware'), (2, 'firmware_b')):
+ utils.run_and_log(
+ ubman,
+ f'cgpt add -i {num} -b {ptr} -s {fat_sects} -t {fw_type} '
+ f'-l {label} {fname}')
+ ptr += fat_sects
+ utils.run_and_log(ubman, f'cgpt boot -p {fname}')
+ out = utils.run_and_log(ubman, f'cgpt show -q {fname}')
+ for line in out.splitlines():
+ start, size, num, name = line.split(maxsplit=3)
+ parts[int(num)] = Partition(int(start), int(size), name)
+
+ # Splice the FAT image into partition 1
+ with open(fname, 'rb') as inf:
+ disk_data = inf.read()
+ start = parts[1].start * sect_size
+ disk_data = disk_data[:start] + fat_data + disk_data[start + len(fat_data):]
+ with open(fname, 'wb') as outf:
+ outf.write(disk_data)
+
+ return fname
+
+
@pytest.mark.buildconfigspec('cmd_bootflow')
@pytest.mark.buildconfigspec('sandbox')
def test_ut_dm_init_bootstd(ubman):
@@ -626,6 +735,7 @@ def test_ut_dm_init_bootstd(ubman):
setup_android_image(ubman)
setup_efi_image(ubman)
setup_rauc_image(ubman)
+ setup_firmware_fdt_image(ubman)
# Restart so that the new mmc1.img is picked up
ubman.restart_uboot()
--
2.55.0
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] bootstd: add a firmware-owned devicetree source
2026-07-06 15:06 ` [PATCH 1/4] bootstd: add a firmware-owned devicetree source Carlo Caione
@ 2026-07-08 17:18 ` Tom Rini
2026-07-09 8:36 ` Carlo Caione
2026-07-08 19:22 ` David Lechner
2026-07-09 20:18 ` Simon Glass
2 siblings, 1 reply; 14+ messages in thread
From: Tom Rini @ 2026-07-08 17:18 UTC (permalink / raw)
To: Carlo Caione
Cc: u-boot, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
[-- Attachment #1: Type: text/plain, Size: 2310 bytes --]
On Mon, Jul 06, 2026 at 05:06:42PM +0200, Carlo Caione wrote:
> Platforms following EBBR / Arm SystemReady IR keep the devicetree on
> a firmware-owned partition, updated independently of the operating
> system, rather than shipping it in the OS image or on the EFI System
> Partition. U-Boot has no generic way to source the devicetree from such
> a partition, so vendors carry out-of-tree machinery for it.
>
> Add a bootstd helper, firmware_fdt_load(), that assembles the OS
> devicetree from a FIT manifest carried on that partition: the FIT images
> hold the base DTB and its overlays, and each FIT configuration names one
> bootable combination through its 'fdt' property.
>
> The partition is described in the control devicetree: the bootstd node
> carries a 'firmware-fdt-source' phandle to a source node, a child of
> the media device owning the partition, which identifies it by GPT type
> UUID and/or name. The 'boot_dtb' environment variable may pin an A/B
> partition number.
>
> The error semantics are fail closed: -ENOENT strictly means "no source
> configured" (the only case where callers may fall back to their normal
> devicetree); once a source is configured, any assembly failure is
> fatal, and downstream -ENOENT codes (missing manifest, missing FIT
> configuration) are remapped so a missing or bad firmware devicetree is
> never silently replaced by an unverified one. Manifest images carrying
> a 'load' property are rejected so a misbuilt manifest cannot overwrite
> arbitrary memory.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
> ---
> MAINTAINERS | 3 +
> boot/Kconfig | 23 ++
> boot/Makefile | 1 +
> boot/firmware_fdt.c | 382 ++++++++++++++++++++++++++++++
> doc/develop/bootstd/firmware_fdt.rst | 94 ++++++++
> doc/develop/bootstd/index.rst | 1 +
> doc/device-tree-bindings/firmware-fdt.txt | 149 ++++++++++++
I think I wasn't clear enough on the RFC, sorry. We need to get this
binding approved over in https://github.com/devicetree-org/dt-schema/
and I strongly suspect that barebox would be able to make use of this
too, so a u-boot prefix would be inappropriate.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] bootstd: add a firmware-owned devicetree source
2026-07-06 15:06 ` [PATCH 1/4] bootstd: add a firmware-owned devicetree source Carlo Caione
2026-07-08 17:18 ` Tom Rini
@ 2026-07-08 19:22 ` David Lechner
2026-07-09 20:18 ` Simon Glass
2 siblings, 0 replies; 14+ messages in thread
From: David Lechner @ 2026-07-08 19:22 UTC (permalink / raw)
To: Carlo Caione, u-boot
Cc: Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
On 7/6/26 10:06 AM, Carlo Caione wrote:
> Platforms following EBBR / Arm SystemReady IR keep the devicetree on
> a firmware-owned partition, updated independently of the operating
> system, rather than shipping it in the OS image or on the EFI System
> Partition. U-Boot has no generic way to source the devicetree from such
> a partition, so vendors carry out-of-tree machinery for it.
>
> Add a bootstd helper, firmware_fdt_load(), that assembles the OS
> devicetree from a FIT manifest carried on that partition: the FIT images
> hold the base DTB and its overlays, and each FIT configuration names one
> bootable combination through its 'fdt' property.
>
> The partition is described in the control devicetree: the bootstd node
> carries a 'firmware-fdt-source' phandle to a source node, a child of
> the media device owning the partition, which identifies it by GPT type
> UUID and/or name. The 'boot_dtb' environment variable may pin an A/B
> partition number.
>
> The error semantics are fail closed: -ENOENT strictly means "no source
> configured" (the only case where callers may fall back to their normal
> devicetree); once a source is configured, any assembly failure is
> fatal, and downstream -ENOENT codes (missing manifest, missing FIT
> configuration) are remapped so a missing or bad firmware devicetree is
> never silently replaced by an unverified one. Manifest images carrying
> a 'load' property are rejected so a misbuilt manifest cannot overwrite
> arbitrary memory.
>
...
> diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
> new file mode 100644
> index 00000000000..952299f1e1b
> --- /dev/null
> +++ b/boot/firmware_fdt.c
> @@ -0,0 +1,382 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Firmware-owned devicetree (FDT) source.
> + *
> + * Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a
> + * firmware-owned partition rather than in the OS image or the EFI System
> + * Partition. The devicetree is carried by a FIT manifest on that partition:
> + * the FIT images hold the base DTB and its overlays, and each FIT
> + * configuration names one bootable combination through its 'fdt' property.
> + *
> + * The partition is described by the control devicetree: the bootstd node
> + * carries a 'firmware-fdt-source' phandle to a source node, a child of the
> + * media device that owns the partition, e.g.:
> + *
> + * bootstd {
> + * compatible = "u-boot,boot-std";
> + * firmware-fdt-source = <&fw_fdt>;
> + * };
> + * &mmc0 {
> + * fw_fdt: firmware-fdt {
> + * compatible = "u-boot,firmware-fdt-block";
> + * partition-type-uuid = "...."; (GPT type UUID)
> + * partition-name = "firmware"; (optional fallback)
> + * filename = "fdt.itb"; (optional, the default)
> + * };
> + * };
I have some doubts that it is safe to add arbitrary child nodes to
arbitrary storage controller devices like this. Many of these devices
already define child nodes in their bindings, so this could break
drivers that expect a certain type of child node already. (And also
break validation against the binding schemas.)
So it seems like it should go under /bootstd if this is u-boot-only
or /options [1] if we want to try to make it generic for other bootloaders.
And it could just have a phandle to reference the target block device
provider or just reference it by name (to match using alias).
[1]: https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/options.yaml
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] bootstd: add a firmware-owned devicetree source
2026-07-08 17:18 ` Tom Rini
@ 2026-07-09 8:36 ` Carlo Caione
2026-07-20 19:14 ` Tom Rini via U-Boot
0 siblings, 1 reply; 14+ messages in thread
From: Carlo Caione @ 2026-07-09 8:36 UTC (permalink / raw)
To: Tom Rini
Cc: u-boot, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
On Wed, Jul 8, 2026 at 7:18 PM Tom Rini <trini@konsulko.com> wrote:
> I think I wasn't clear enough on the RFC, sorry. We need to get this
> binding approved over in https://github.com/devicetree-org/dt-schema/
> and I strongly suspect that barebox would be able to make use of this
> too, so a u-boot prefix would be inappropriate.
Addressing also the comment from David:
> I have some doubts that it is safe to add arbitrary child nodes to
> arbitrary storage controller devices like this. Many of these devices
> already define child nodes in their bindings, so this could break
> drivers that expect a certain type of child node already. (And also
> break validation against the binding schemas.)
>
> So it seems like it should go under /bootstd if this is u-boot-only
> or /options [1] if we want to try to make it generic for other bootloaders.
> And it could just have a phandle to reference the target block device
> provider or just reference it by name (to match using alias).
Thank you both for the review.
I would like to split this into two parts:
1. The on-media contract: a firmware-owned devicetree FIT manifest
where configurations describe the valid base+overlay combinations and,
when signed, authenticate the whole combination.
2. The bootloader-local locator: how a particular bootloader finds
that FIT container on its platform.
The first part is the piece that looks useful beyond U-Boot. The
second part is bootloader policy (IMO).
For the U-Boot-local locator, I would like to follow the existing FWU
metadata precedent. FWU metadata solves a similar problem:
firmware-owned data whose storage location the bootloader must find,
using GPT and MTD backends. It uses a standalone U-Boot control-DT
node, not a child of the storage controller, and points to the storage
provider by phandle:
fwu-mdata {
compatible = "u-boot,fwu-mdata-gpt";
fwu-mdata-store = <&sdmmc1>;
};
Modeled the same way, the firmware-devicetree source would become a
standalone U-Boot control-DT node, per backend:
/* eMMC / SD (GPT) */
firmware-fdt {
compatible = "u-boot,firmware-fdt-block";
firmware-fdt-store = <&mmc0>;
partition-type-uuid = "384e979b-eb76-435a-a3a6-1a071dbad91d";
partition-name = "firmware";
filename = "fdt.itb";
};
/* SPI-NOR / UBI */
firmware-fdt {
compatible = "u-boot,firmware-fdt-mtd";
firmware-fdt-store = <&spi_nor>;
ubi-volume = "firmware";
filename = "fdt.itb";
};
(please note that for the sake of simplification in the patchset we
are only implementing the `u-boot,firmware-fdt-block` backend, `-mtd`
variant to follow).
This fixes the child-node problem David raised: the controller node is
untouched and only referenced by a phandle. It also keeps this as
U-Boot control-DT policy, matching the existing FWU metadata pattern,
rather than defining a new cross-bootloader locator ABI in this series
(so exonerating us from dealing with dt-schema at all, considering
also that we would deal with that for each different
`u-boot,firmware-fdt-*` backend that we would introduce).
What I think is genuinely shareable with barebox is the on-media
contract: the `fdt.itb` FIT layout, configuration semantics, signing
model, and whatever partition or volume naming convention the platform
or specification defines. That seems like the right thing to document
at the EBBR level, where barebox could adopt the same container
convention independently of how each bootloader locates it. So we
share and define the 'what' but how then barebox (or any other
bootloader) would source `ftd.itb` (the what) is not part of the ABI
and very much bootloader specific.
Does following the existing U-Boot FWU-metadata precedent work if we
scope this binding explicitly as U-Boot control-DT policy?
Cheers,
--
Carlo Caione
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] bootstd: add a firmware-owned devicetree source
2026-07-06 15:06 ` [PATCH 1/4] bootstd: add a firmware-owned devicetree source Carlo Caione
2026-07-08 17:18 ` Tom Rini
2026-07-08 19:22 ` David Lechner
@ 2026-07-09 20:18 ` Simon Glass
2 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-07-09 20:18 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent,
Peng Fan, Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
Hi Carlo,
On 2026-07-06T15:06:41, Carlo Caione <ccaione@baylibre.com> wrote:
> bootstd: add a firmware-owned devicetree source
>
> Platforms following EBBR / Arm SystemReady IR keep the devicetree on
> a firmware-owned partition, updated independently of the operating
> system, rather than shipping it in the OS image or on the EFI System
> Partition. U-Boot has no generic way to source the devicetree from such
> a partition, so vendors carry out-of-tree machinery for it.
>
> Add a bootstd helper, firmware_fdt_load(), that assembles the OS
> devicetree from a FIT manifest carried on that partition: the FIT images
> hold the base DTB and its overlays, and each FIT configuration names one
> bootable combination through its 'fdt' property.
>
> The partition is described in the control devicetree: the bootstd node
> carries a 'firmware-fdt-source' phandle to a source node, a child of
> the media device owning the partition, which identifies it by GPT type
> UUID and/or name. The 'boot_dtb' environment variable may pin an A/B
> partition number.
>
> The error semantics are fail closed: -ENOENT strictly means "no source
> [...]
>
> MAINTAINERS | 3 +
> boot/Kconfig | 23 ++
> boot/Makefile | 1 +
> boot/firmware_fdt.c | 382 ++++++++++++++++++++++++++++++
> doc/develop/bootstd/firmware_fdt.rst | 94 ++++++++
> doc/develop/bootstd/index.rst | 1 +
> doc/device-tree-bindings/firmware-fdt.txt | 149 ++++++++++++
> include/firmware_fdt.h | 78 ++++++
> 8 files changed, 731 insertions(+)
(See naming comment on the cover letter)
diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
new file mode 100644
index 00000000000..952299f1e1b
--- /dev/null
+++ b/boot/firmware_fdt.c
@@ -0,0 +1,382 @@
> diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
> @@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Firmware-owned devicetree (FDT) source.
+ *
+ * Some platforms (EBBR / Arm SystemReady IR) keep the devicetree on a
+ * firmware-owned partition rather than in the OS image or the EFI System
+ * Partition. The devicetree is carried by a FIT manifest on that partition:
+ * the FIT images hold the base DTB and its overlays, and each FIT
+ * configuration names one bootable combination through its 'fdt' property.
+ *
By FIT manifest I think you mean a FIT?
> +static int fw_fdt_get_source(ofnode *srcp)
> +{
> + ofnode bootstd, src;
> + const void *prop;
> +
> + bootstd = ofnode_by_compatible(ofnode_null(), 'u-boot,boot-std');
> + if (!ofnode_valid(bootstd))
> + return -ENOENT;
Please look the node up via the bootstd device rather than the raw
compatible string - every other bootstd consumer uses
uclass_first_device_err(UCLASS_BOOTSTD, &dev) and dev_ofnode(dev),
which also ensures the device is probed. Open-coding the compatible
walk bypasses the uclass and duplicates knowledge that lives in
bootstd-uclass.c
> diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
> @@ -0,0 +1,382 @@
> +void firmware_fdt_free(struct firmware_fdt *fw)
> +{
> + u8 *fit = fw->fit;
> +
> + /*
> + * The assembled devicetree either points into the manifest buffer or
> + * is a separate allocation (made when applying overlays, or when
> + * aligning the devicetree); free it only in the latter case.
> + */
> + if (fw->fdt &&
> + ((u8 *)fw->fdt < fit || (u8 *)fw->fdt >= fit + fw->fit_size))
> + free(fw->fdt);
Comparing pointers from two different allocations is undefined in
strict C, and the "is fdt inside fit?" heuristic depends on assembly
internals that could change. Please track ownership explicitly, e.g.
a bool fdt_owned set at assemble time, and free based on that.
I am nervous about mentioning it, but an abuf could help here, since
it handles things which are allocated or not.
> diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
> @@ -0,0 +1,382 @@
> + fname = ofnode_read_string(src, 'filename');
> + if (!fname)
> + fname = FW_FDT_FILENAME;
> ...
> + out->fdt = fdt;
> + out->size = len;
> + out->name = fname;
out->name either points into the live devicetree or at a static
string; the caller (bootmeth_efi in patch 2) strdup()s it, but
nothing in the API contract says the caller must copy before the
tree is modified. Please strdup() here, or spell out the lifetime
rule in the header.
> diff --git a/boot/Kconfig b/boot/Kconfig
> @@ -671,6 +671,29 @@ config BOOTMETH_EFILOADER
> +config BOOTSTD_FIRMWARE_FDT
> + bool "Source the devicetree from a firmware-owned partition"
> + depends on BOOTSTD && BLK && FIT
> + select EFI_PARTITION
> + select PARTITION_TYPE_GUID
> + select OF_LIBFDT
> + select OF_LIBFDT_OVERLAY
The cover letter advertises best-match against the control DT
(CONFIG_FIT_BEST_MATCH), but nothing here selects or depends on
FIT_BEST_MATCH - with the default config selection silently falls
through to the manifest default. Either select FIT_BEST_MATCH or
note in the help text and doc that best-match applies only when the
user turns it on.
Selecting it might have code-size implementation, though...
Regards,
Simon
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 2/4] bootmeth: efi: source the devicetree from a firmware partition
2026-07-06 15:06 ` [PATCH 2/4] bootmeth: efi: source the devicetree from a firmware partition Carlo Caione
@ 2026-07-09 20:18 ` Simon Glass
0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-07-09 20:18 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent,
Peng Fan, Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
Hi Carlo,
On 2026-07-06T15:06:41, Carlo Caione <ccaione@baylibre.com> wrote:
> bootmeth: efi: source the devicetree from a firmware partition
>
> When a firmware-owned devicetree source is configured (EBBR / Arm
> SystemReady IR), assemble the devicetree from the FIT manifest on
> the firmware partition instead of reading a DTB from the EFI System
> Partition, and hand it to the EFI application through the normal
> bflow->fdt_addr path (efi_install_fdt()).
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
>
> boot/bootmeth_efi.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
> diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
> @@ -73,6 +74,61 @@ static int efiload_read_file(struct bootflow *bflow, ulong addr)
> + ret = firmware_fdt_load(&fw);
> + if (ret) {
> + if (ret != -ENOENT)
> + log_err("Failed to assemble the firmware devicetree (err %d)\n",
> + ret);
> + return ret;
> + }
> +
> + /* stage it at fdt_addr_r, like any other devicetree */
> + fdt_addr = env_get_hex('fdt_addr_r', 0);
> + if (!fdt_addr || fw.size > SZ_4M) {
> + firmware_fdt_free(&fw);
> + return log_msg_ret('fwa', -EINVAL);
> + }
Please move the fdt_addr_r lookup above firmware_fdt_load():
assembling the manifest, applying overlays and running
fdt_check_full() only to discover we have nowhere to put the result
is wasted work. The SZ_4M cap also duplicates FW_FDT_MAX_SIZE inside
firmware_fdt.c - please share the constant via firmware_fdt.h
> diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
> @@ -130,6 +186,18 @@ static int distro_efi_try_bootflow_files(struct udevice *dev,
> + /*
> + * A staged firmware-owned devicetree is complete and authoritative,
> + * so return without considering any other devicetree source. The
> + * extension overlays below are deliberately not applied on top:
> + * such combinations belong in the manifest as configurations.
> + */
> + ret = distro_efi_firmware_fdt(bflow);
> + if (!ret)
> + return 0;
> + if (ret != -ENOENT)
> + return log_msg_ret('fwf', ret);
Just to check - once you move the env lookup into the helper, the
outer fdt_addr = env_get_hex(...) still needs to run for the ESP
fall-back path. Please make sure the two lookups cannot disagree and
state the contract in the doc-comment.
> diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
> @@ -268,6 +336,19 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
> + /*
> + * A configured firmware-owned devicetree outranks the network-provided
> + * one (and the prior-stage / built-in devicetree below): a DHCP/TFTP
> + * server must not be able to replace it.
> + */
> + ret = distro_efi_firmware_fdt(bflow);
> + if (!ret) {
> + bflow->state = BOOTFLOWST_READY;
> + return 0;
> + }
> + if (ret != -ENOENT)
> + return log_msg_ret('fwf', ret);
The two call sites are essentially identical except for the extra
bflow->state = BOOTFLOWST_READY here. Please factor the "try
firmware fdt, return on success, fail on hard error, otherwise
continue" logic into a small helper - patch 3 adds a third caller
in efi_bootmgr.c and it would be easy to forget one of the checks.
> diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
> @@ -73,6 +74,61 @@ static int efiload_read_file(struct bootflow *bflow, ulong addr)
> + if (!CONFIG_IS_ENABLED(BOOTSTD_FIRMWARE_FDT))
> + return -ENOENT;
BTW the stub in firmware_fdt.h returns -ENOSYS when the option is
disabled, so this early-return exists purely to remap that to
-ENOENT. Cleaner to have the stub return -ENOENT directly and drop
this guard.
Regards,
Simon
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 3/4] efi_loader: bootmgr: install the firmware-owned devicetree
2026-07-06 15:06 ` [PATCH 3/4] efi_loader: bootmgr: install the firmware-owned devicetree Carlo Caione
@ 2026-07-09 20:19 ` Simon Glass
0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-07-09 20:19 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent,
Peng Fan, Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
Hi Carlo,
On 2026-07-06T15:06:41, Carlo Caione <ccaione@baylibre.com> wrote:
> efi_loader: bootmgr: install the firmware-owned devicetree
>
> SystemReady IR boots through the UEFI boot manager, which does not go
> through the per-device EFI bootmeth, so hooking the bootmeth alone
> would leave the boot-manager path (including autoboot) on the control
> devicetree.
>
> Consume the firmware-FDT source in efi_bootmgr_run() too, so the
> firmware-owned devicetree is installed via efi_install_fdt() regardless
> of which EFI launch path is taken.
>
> The firmware-owned devicetree is tried before the Boot#### load-option
> FDT, so a stale or broken load option can neither override nor block a
> configured (and possibly signed) firmware devicetree. As a side effect,
> a devicetree passed directly to efi_bootmgr_run() now also outranks the
> load-option FDT, which previously overrode it: an explicitly supplied
> devicetree is the operator's immediate intent, and stored boot options
> must not replace it.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
>
> lib/efi_loader/efi_bootmgr.c | 50 +++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 43 insertions(+), 7 deletions(-)
> As a side effect,
> a devicetree passed directly to efi_bootmgr_run() now also outranks the
> load-option FDT, which previously overrode it: an explicitly supplied
> devicetree is the operator's immediate intent, and stored boot options
> must not replace it.
This behaviour change is independent of the firmware-FDT work -
please split it into its own patch so the two can be reviewed (and,
if needed, reverted) separately. That patch can go first, then this
one becomes purely additive.
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> @@ -1305,7 +1308,8 @@ efi_status_t efi_bootmgr_run(void *fdt)
> if (!fdt) {
> + ret = load_fdt_from_load_option(&fdt_lo);
> + if (ret != EFI_SUCCESS)
> + return ret;
> + if (fdt_lo)
> + fdt = fdt_lo;
> + }
Now that you have an out: label with cleanup for fw, this early
return ret; is inconsistent - it skips firmware_fdt_free(&fw) and
the load_options / handle cleanup below. In practice fw is zeroed on
this path so nothing leaks today, but please route through goto out;
so the function has a single exit for error paths.
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> @@ -1320,11 +1324,40 @@ efi_status_t efi_bootmgr_run(void *fdt)
> + } else if (err != -ENOENT) {
> + log_err("Failed to assemble the firmware devicetree (err %d)\n",
> + err);
> + ret = EFI_LOAD_ERROR;
> + goto out;
> + }
EFI_LOAD_ERROR is really about failing to load an image; for a
devicetree that couldn't be assembled, EFI_DEVICE_ERROR (or
EFI_NOT_FOUND, when appropriate) reads more accurately.
You are adding quite a bit of code here, so please split it into a
separate function if you can.
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> @@ -1296,7 +1297,9 @@ out:
> *
> * Invoke EFI boot manager and execute a binary depending on
> * boot options. If @fdt is not NULL, it will be passed to
> - * the executed binary.
> + * the executed binary. Otherwise a configured firmware-owned
> + * devicetree source takes precedence over both a Boot#### load
> + * option FDT and the distro one.
> *
> * Return: status code
> */
Please spell out the full precedence order here (passed @fdt >
firmware-owned > Boot#### load option > distro/ESP) - that is now
the interesting contract of the function and 'Otherwise' hides half
of it.
Regards,
Simon
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/4] test: bootstd: add firmware-FDT source tests
2026-07-06 15:06 ` [PATCH 4/4] test: bootstd: add firmware-FDT source tests Carlo Caione
@ 2026-07-09 20:19 ` Simon Glass
0 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-07-09 20:19 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Tom Rini, Simon Glass, Neil Armstrong, Kory Maincent,
Peng Fan, Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
Hi Carlo,
On 2026-07-06T15:06:41, Carlo Caione <ccaione@baylibre.com> wrote:
> test: bootstd: add firmware-FDT source tests
>
> Add sandbox coverage for firmware_fdt_load(): a python fixture builds
> mmc11.img, a GPT disk with A/B 'firmware' partitions whose first
> partition carries a FAT filesystem holding the FIT manifest (fdt.itb:
> a base DTB plus one overlay, with a default configuration that applies
> the overlay and a base-only 'conf-base' configuration).
>
> The tests cover the happy path (default configuration, packed size,
> both fixture properties visible), configuration selection via
> 'fw_fdt_config', and the fail-closed semantics: a missing pinned
> partition and a selector naming a configuration the manifest does not
> ship are fatal errors rather than -ENOENT, an unresolvable
> 'firmware-fdt-source' phandle is -EINVAL, and only a genuinely absent
> source returns -ENOENT.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
>
> arch/sandbox/dts/test.dts | 24 ++++
> configs/sandbox_defconfig | 1 +
> test/boot/Makefile | 1 +
> test/boot/firmware_fdt.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++
> test/py/tests/test_ut.py | 110 +++++++++++++++++
> 5 files changed, 428 insertions(+)
> diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
> @@ -614,6 +614,115 @@ def setup_rauc_image(ubman):
> + mmc_dev = 11
> + fname = os.path.join(ubman.config.source_dir, f'mmc{mmc_dev}.img')
Every other setup_*_image() puts its image under persistent_data_dir
(or lets DiskHelper do it). Please put mmc11.img in the same place.
I have a pending series to move some of this around...I'll see if I
can send a new version.
> diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
> @@ -122,6 +123,14 @@
> + /*
> + * Not read by U-Boot: the firmware_fdt tests copy this into a
> + * live 'firmware-fdt-source' property at runtime (and delete
> + * it afterwards), so the source stays unconfigured for every
> + * other test. It also keeps a phandle emitted for &fw_fdt.
> + */
> + test-fw-fdt-source = <&fw_fdt>;
I don't like this very much. Poking an inert marker into the shared
bootstd node just to keep a phandle alive, then having every test
mutate the live tree to graft the real property in and out, is
fragile - a failed ut_assert() in the middle of a test leaves the
tree half-configured for whatever runs next.
How about creating the whole thing (including adding a phandle) in the test?
> diff --git a/test/boot/firmware_fdt.c b/test/boot/firmware_fdt.c
> @@ -0,0 +1,292 @@
> +BOOTSTD_TEST(firmware_fdt_test_part_mismatch, UTF_DM | UTF_SCAN_FDT);
No coverage for the CONFIG_FIT_BEST_MATCH path advertised in the
cover letter (compatible-based configuration selection against the
control DT). Please add a test - a manifest with two configurations
carrying different compatible strings, where the base compatible
from the control DT picks one of them.
Regards,
Simon
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR
2026-07-06 15:06 [PATCH 0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
` (3 preceding siblings ...)
2026-07-06 15:06 ` [PATCH 4/4] test: bootstd: add firmware-FDT source tests Carlo Caione
@ 2026-07-09 20:20 ` Simon Glass
4 siblings, 0 replies; 14+ messages in thread
From: Simon Glass @ 2026-07-09 20:20 UTC (permalink / raw)
To: ccaione; +Cc: u-boot
Hi Carlo,
On 2026-07-06T15:06:41, Carlo Caione <ccaione@baylibre.com> wrote:
> This series adds a bootstd-level facility for sourcing the
> firmware-owned devicetree from a FIT manifest on a dedicated GPT
> partition
Thanks for working on this - a generic replacement for the vendor
dtbprobe-style commands is very welcome! Herewith a few general
comments.
The "bootstd-level facility" framing doesn't match what the code does.
Nothing plugs into the bootflow iterator: no bootdev, no bootmeth, no
bootflow, and the only consumers are the EFI bootmeth and
efi_bootmgr_run(). Shouldn't this feature be more general than just
EFI?
> the control devicetree points at the source: a 'firmware-fdt-source'
> phandle under /bootstd referencing a 'u-boot,firmware-fdt-block' node
> under the owning media device
I'm a bit nervous about adding this into a media node, but if that is
what your binding ends up as, OK. The other way to do this would be
like VBE, which has a node in bootstd which points to the media
(although it does not include the full path, nor a phandle...
> - both EFI launch paths consume it, the EFI bootmeth (disk and network)
> and the EFI boot manager, all funneling into efi_install_fdt();
The two consumers (distro_efi_firmware_fdt() in bootmeth_efi.c and
the new block in efi_bootmgr_run()) carry near-identical staging
logic and log strings. Please factor into a single helper alongside
firmware_fdt_load() - e.g. efi_stage_firmware_fdt() - so one place
owns the staging policy (fdt_addr_r, size cap, fail-closed vs
-ENOENT).
> The 'boot_dtb' environment variable may pin an A/B partition number.
boot_dtb seems quite generic - it reads like an address or filename,
How about something like fw_fdt_part so it lines up with
fw_fdt_config. While you're there, both names deserve to appear in
doc/usage/environment.rst
Regards,
Simon
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] bootstd: add a firmware-owned devicetree source
2026-07-09 8:36 ` Carlo Caione
@ 2026-07-20 19:14 ` Tom Rini via U-Boot
0 siblings, 0 replies; 14+ messages in thread
From: Tom Rini via U-Boot @ 2026-07-20 19:14 UTC (permalink / raw)
To: Carlo Caione
Cc: u-boot, Simon Glass, Neil Armstrong, Kory Maincent, Peng Fan,
Kuan-Wei Chiu, Jerome Forissier, Alif Zakuan Yuslaimi,
Raymond Mao, Quentin Schulz, Stefan Roese, Philip Molloy,
Anshul Dalal, Mattijs Korpershoek, João Paulo Gonçalves,
Heinrich Schuchardt, Ilias Apalodimas, Javier Tia, Marek Vasut,
Heiko Schocher, Dinesh Maniyam,
Markus Schneider-Pargmann (TI.com), Lucien.Jheng, Martin Schwan,
Michal Simek, Pieter Van Trappen
[-- Attachment #1: Type: text/plain, Size: 4426 bytes --]
On Thu, Jul 09, 2026 at 10:36:06AM +0200, Carlo Caione wrote:
> On Wed, Jul 8, 2026 at 7:18 PM Tom Rini <trini@konsulko.com> wrote:
>
> > I think I wasn't clear enough on the RFC, sorry. We need to get this
> > binding approved over in https://github.com/devicetree-org/dt-schema/
> > and I strongly suspect that barebox would be able to make use of this
> > too, so a u-boot prefix would be inappropriate.
>
> Addressing also the comment from David:
>
> > I have some doubts that it is safe to add arbitrary child nodes to
> > arbitrary storage controller devices like this. Many of these devices
> > already define child nodes in their bindings, so this could break
> > drivers that expect a certain type of child node already. (And also
> > break validation against the binding schemas.)
> >
> > So it seems like it should go under /bootstd if this is u-boot-only
> > or /options [1] if we want to try to make it generic for other bootloaders.
> > And it could just have a phandle to reference the target block device
> > provider or just reference it by name (to match using alias).
>
> Thank you both for the review.
>
> I would like to split this into two parts:
>
> 1. The on-media contract: a firmware-owned devicetree FIT manifest
> where configurations describe the valid base+overlay combinations and,
> when signed, authenticate the whole combination.
>
> 2. The bootloader-local locator: how a particular bootloader finds
> that FIT container on its platform.
>
> The first part is the piece that looks useful beyond U-Boot. The
> second part is bootloader policy (IMO).
>
> For the U-Boot-local locator, I would like to follow the existing FWU
> metadata precedent. FWU metadata solves a similar problem:
> firmware-owned data whose storage location the bootloader must find,
> using GPT and MTD backends. It uses a standalone U-Boot control-DT
> node, not a child of the storage controller, and points to the storage
> provider by phandle:
>
> fwu-mdata {
> compatible = "u-boot,fwu-mdata-gpt";
> fwu-mdata-store = <&sdmmc1>;
> };
>
> Modeled the same way, the firmware-devicetree source would become a
> standalone U-Boot control-DT node, per backend:
>
> /* eMMC / SD (GPT) */
> firmware-fdt {
> compatible = "u-boot,firmware-fdt-block";
> firmware-fdt-store = <&mmc0>;
> partition-type-uuid = "384e979b-eb76-435a-a3a6-1a071dbad91d";
> partition-name = "firmware";
> filename = "fdt.itb";
> };
>
> /* SPI-NOR / UBI */
> firmware-fdt {
> compatible = "u-boot,firmware-fdt-mtd";
> firmware-fdt-store = <&spi_nor>;
> ubi-volume = "firmware";
> filename = "fdt.itb";
> };
>
> (please note that for the sake of simplification in the patchset we
> are only implementing the `u-boot,firmware-fdt-block` backend, `-mtd`
> variant to follow).
>
> This fixes the child-node problem David raised: the controller node is
> untouched and only referenced by a phandle. It also keeps this as
> U-Boot control-DT policy, matching the existing FWU metadata pattern,
> rather than defining a new cross-bootloader locator ABI in this series
> (so exonerating us from dealing with dt-schema at all, considering
> also that we would deal with that for each different
> `u-boot,firmware-fdt-*` backend that we would introduce).
>
> What I think is genuinely shareable with barebox is the on-media
> contract: the `fdt.itb` FIT layout, configuration semantics, signing
> model, and whatever partition or volume naming convention the platform
> or specification defines. That seems like the right thing to document
> at the EBBR level, where barebox could adopt the same container
> convention independently of how each bootloader locates it. So we
> share and define the 'what' but how then barebox (or any other
> bootloader) would source `ftd.itb` (the what) is not part of the ABI
> and very much bootloader specific.
>
> Does following the existing U-Boot FWU-metadata precedent work if we
> scope this binding explicitly as U-Boot control-DT policy?
I was hoping that someone on the FWU side of things would chime in by
now, but I assume people have been too busy. This is I suppose a
reasonable starting path and will give people time to chime in if they
see a problem with it.
--
Tom
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-07-20 19:14 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 15:06 [PATCH 0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
2026-07-06 15:06 ` [PATCH 1/4] bootstd: add a firmware-owned devicetree source Carlo Caione
2026-07-08 17:18 ` Tom Rini
2026-07-09 8:36 ` Carlo Caione
2026-07-20 19:14 ` Tom Rini via U-Boot
2026-07-08 19:22 ` David Lechner
2026-07-09 20:18 ` Simon Glass
2026-07-06 15:06 ` [PATCH 2/4] bootmeth: efi: source the devicetree from a firmware partition Carlo Caione
2026-07-09 20:18 ` Simon Glass
2026-07-06 15:06 ` [PATCH 3/4] efi_loader: bootmgr: install the firmware-owned devicetree Carlo Caione
2026-07-09 20:19 ` Simon Glass
2026-07-06 15:06 ` [PATCH 4/4] test: bootstd: add firmware-FDT source tests Carlo Caione
2026-07-09 20:19 ` Simon Glass
2026-07-09 20:20 ` [0/4] bootstd: firmware-owned devicetree for EBBR / SystemReady IR Simon Glass
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox