* [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR
@ 2026-07-28 13:20 Carlo Caione
2026-07-28 13:20 ` [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree Carlo Caione
` (6 more replies)
0 siblings, 7 replies; 17+ messages in thread
From: Carlo Caione @ 2026-07-28 13:20 UTC (permalink / raw)
To: u-boot
Cc: Ahmad Fatoum, David Lechner, Julien Masson, Vitor Sato Eschholz,
Heinrich Schuchardt, Ilias Apalodimas, Tom Rini, Carlo Caione,
Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
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. As a result,
platforms carry downstream commands to provide it.
This series adds a generic loader for a firmware-owned devicetree FIT on
a dedicated GPT partition:
- one FIT ("fdt.itb" by default) carries the base devicetree and its
overlays. FIT configurations describe the valid combinations, so a
signed configuration authenticates the base, overlay set and ordering;
- a standalone 'u-boot,firmware-fdt-block' control-DT node points to the
media device through a 'firmware-fdt-store' phandle. The node selects
a partition by type UUID and/or name;
- 'fw_fdt_part' can pin an A/B partition and 'fw_fdt_config' can select
an explicit configuration. Otherwise compatible best-match against the
control devicetree is used, falling back to the FIT default. Configuration
chaining is rejected so each combination remains one authenticated unit;
- one staging helper owns the EFI policy: use fdt_addr_r, enforce the
size cap and distinguish an absent source from a configured source
which failed. Both the EFI bootmeth and EFI boot manager use it;
- once a source is configured, any failure to assemble the devicetree is
fatal rather than silently falling back to an unverified source. Every
image is verified up front so a corrupt overlay cannot be silently skipped.
The loader itself does not depend on standard boot. EFI is its first
consumer, but the source lookup, FIT selection, verification and overlay
assembly are generic.
Patch 1 makes an FDT passed to efi_bootmgr_run() outrank a Boot#### FDT,
as an independent behaviour fix. Patch 2 adds the loader, binding,
documentation and shared EFI staging policy. Patches 3 and 4 integrate
the EFI bootmeth and boot manager. Patch 5 adds sandbox coverage.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
Changes in v2:
- Make the loader independent of bootstd and describe EFI as its first
consumer.
- Describe the source with a standalone compatible node which points to
the backing media through a phandle.
- Rename 'boot_dtb' to 'fw_fdt_part' and document both environment
variables in doc/usage/environment.rst.
- Factor EFI staging and fail-closed policy into one helper used by all
three call sites.
- Track the assembled FDT's ownership explicitly and own the FIT filename.
- Select FIT_BEST_MATCH and add explicit compatible-selection coverage.
- Split the passed-FDT precedence change into its own first patch.
- Build the entire sandbox source topology at runtime, store mmc11.img in
persistent_data_dir and add staging, configuration-chaining and corrupt
base/overlay fail-closed coverage.
- Link to v1: https://patch.msgid.link/20260706-ccaione-upstream-ebbr-v1-0-e742cdd6abe5@baylibre.com
---
Carlo Caione (5):
efi_loader: bootmgr: preserve a passed devicetree
boot: add a firmware-owned devicetree source
bootmeth: efi: use the firmware-owned devicetree
efi_loader: bootmgr: install the firmware-owned devicetree
test: boot: add firmware-FDT source tests
MAINTAINERS | 3 +
boot/Kconfig | 24 ++
boot/Makefile | 1 +
boot/bootmeth_efi.c | 45 +++-
boot/firmware_fdt.c | 420 ++++++++++++++++++++++++++++++
boot/image-fdt.c | 3 +-
boot/image-fit.c | 9 +-
configs/sandbox_defconfig | 1 +
doc/develop/uefi/firmware_fdt.rst | 112 ++++++++
doc/develop/uefi/index.rst | 1 +
doc/device-tree-bindings/firmware-fdt.txt | 150 +++++++++++
doc/usage/environment.rst | 11 +
include/firmware_fdt.h | 95 +++++++
include/image.h | 4 +-
lib/efi_loader/efi_bootmgr.c | 39 ++-
test/boot/Makefile | 1 +
test/boot/firmware_fdt.c | 411 +++++++++++++++++++++++++++++
test/py/tests/test_ut.py | 181 +++++++++++++
18 files changed, 1492 insertions(+), 19 deletions(-)
---
base-commit: b635d43bca429500cb8ef20aa151cb5773b9a8a5
change-id: 20260706-ccaione-upstream-ebbr-206c30be3a3a
Best regards,
--
Carlo Caione <ccaione@baylibre.com>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
@ 2026-07-28 13:20 ` Carlo Caione
2026-08-04 8:51 ` Ilias Apalodimas
2026-08-09 0:34 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 2/5] boot: add a firmware-owned devicetree source Carlo Caione
` (5 subsequent siblings)
6 siblings, 2 replies; 17+ messages in thread
From: Carlo Caione @ 2026-07-28 13:20 UTC (permalink / raw)
To: u-boot
Cc: Ahmad Fatoum, David Lechner, Julien Masson, Vitor Sato Eschholz,
Heinrich Schuchardt, Ilias Apalodimas, Tom Rini, Carlo Caione,
Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
An FDT explicitly passed to efi_bootmgr_run() is currently replaced by
the FDT referenced by the selected Boot#### load option. This makes an
operator-supplied devicetree ineffective whenever the boot option carries
one.
Only consult the load-option FDT when no FDT was passed. Document the
resulting precedence so the explicit call argument remains the
highest-priority source.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
lib/efi_loader/efi_bootmgr.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 8c9a9b5eb56..785484abf23 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -1294,9 +1294,9 @@ out:
* efi_bootmgr_run() - execute EFI boot manager
* @fdt: Flat device tree
*
- * 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.
+ * Invoke the EFI boot manager and execute a binary according to its boot
+ * options. The devicetree precedence, from highest to lowest, is an FDT
+ * passed in @fdt, the Boot#### load-option FDT, then the distro/ESP FDT.
*
* Return: status code
*/
@@ -1305,7 +1305,7 @@ 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;
+ void *fdt_lo = NULL, *fdt_distro = NULL;
efi_uintn_t fdt_size;
/* Initialize EFI drivers */
@@ -1320,11 +1320,13 @@ 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;
+ 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;
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 2/5] boot: add a firmware-owned devicetree source
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
2026-07-28 13:20 ` [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree Carlo Caione
@ 2026-07-28 13:20 ` Carlo Caione
2026-08-04 8:58 ` Ilias Apalodimas
2026-08-15 18:44 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 3/5] bootmeth: efi: use the firmware-owned devicetree Carlo Caione
` (4 subsequent siblings)
6 siblings, 2 replies; 17+ messages in thread
From: Carlo Caione @ 2026-07-28 13:20 UTC (permalink / raw)
To: u-boot
Cc: Ahmad Fatoum, David Lechner, Julien Masson, Vitor Sato Eschholz,
Heinrich Schuchardt, Ilias Apalodimas, Tom Rini, Carlo Caione,
Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
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. U-Boot has no generic way to
source and assemble a devicetree from such a partition.
Add firmware_fdt_load(), a loader for a FIT from a firmware-owned
devicetree source. The FIT images hold the base DTB and its overlays,
while each configuration names one valid combination through its standard
'fdt' property. Select an explicit 'fw_fdt_config' when set, otherwise
use compatible best-match against the control devicetree and fall back to
the FIT default.
Describe the source with a standalone control-DT node. This patch
implements the first source backend, 'u-boot,firmware-fdt-block', which
reads the FIT from a filesystem on a GPT partition of a block device:
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";
};
The 'firmware-fdt-store' phandle points to the media device, and the GPT
type UUID and/or name select the partition. The 'fw_fdt_part' environment
variable can pin an A/B partition.
The compatible suffix identifies the storage backend. Future backends can
load the same fdt.itb from other firmware storage, such as UBI on MTD,
without changing the FIT contract, common verification and assembly, or
its consumers.
The loader is independent of bootstd. Add efi_stage_firmware_fdt() as the
shared policy adapter for its initial EFI consumers: it stages at the
caller's single fdt_addr_r value, enforces the common size limit and
preserves fail-closed semantics.
Make -ENOENT mean only "no source configured". Once a source exists, a
missing partition, FIT or configuration is fatal. Reject load addresses
and external data, require every image to be a flat devicetree and verify
every image before assembly. Reject configuration chaining so the base,
overlay set and ordering remain one authenticated unit.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
MAINTAINERS | 3 +
boot/Kconfig | 24 ++
boot/Makefile | 1 +
boot/firmware_fdt.c | 420 ++++++++++++++++++++++++++++++
boot/image-fdt.c | 3 +-
boot/image-fit.c | 9 +-
doc/develop/uefi/firmware_fdt.rst | 112 ++++++++
doc/develop/uefi/index.rst | 1 +
doc/device-tree-bindings/firmware-fdt.txt | 150 +++++++++++
doc/usage/environment.rst | 11 +
include/firmware_fdt.h | 95 +++++++
include/image.h | 4 +-
12 files changed, 829 insertions(+), 4 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 53034b703df..c8ac95e76f9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -905,8 +905,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
@@ -916,6 +918,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 c67dc0ba493..764c67a63cb 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -194,6 +194,30 @@ config FIT_BEST_MATCH
If several configurations match equally well, the one named by
the configurations node 'default' property is preferred.
+config FIRMWARE_FDT
+ bool "Source the devicetree from a firmware-owned partition"
+ depends on BLK && FIT
+ select EFI_PARTITION
+ select FIT_BEST_MATCH
+ select PARTITION_TYPE_GUID
+ select OF_LIBFDT
+ select OF_LIBFDT_OVERLAY
+ help
+ Source the devicetree from a firmware-owned partition rather than
+ from the operating-system image. The partition carries a FIT whose
+ images hold the base DTB and its overlays, and whose configurations
+ name the bootable combinations. The assembled devicetree can be
+ handed to the OS, so it can be updated as part of the firmware,
+ independently of the operating system.
+
+ For secure boot, sign the FIT configurations and enable
+ FIT_SIGNATURE with a required key in the control devicetree; the
+ standard verified-boot policy then rejects unsigned FITs.
+
+ EFI consumers use the shared staging helper, but the loader itself is
+ generic. This is intended for platforms following EBBR / Arm
+ SystemReady IR. Say N unless you are booting such a platform.
+
config FIT_IMAGE_POST_PROCESS
bool "Enable post-processing of FIT artifacts after loading by U-Boot"
depends on SOCFPGA_SECURE_VAB_AUTH
diff --git a/boot/Makefile b/boot/Makefile
index 7fb56e7ef37..23c1de85eb1 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_)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..4e033d55163
--- /dev/null
+++ b/boot/firmware_fdt.c
@@ -0,0 +1,420 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Firmware-owned devicetree source.
+ *
+ * Load a firmware-owned FIT, select and verify one configuration, and
+ * assemble its base devicetree and overlays for consumers.
+ */
+
+#define LOG_CATEGORY LOGC_BOOT
+
+#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/string.h>
+
+/* The FIT lives in a filesystem on a GPT partition of a block device */
+#define FW_FDT_COMPAT_BLOCK "u-boot,firmware-fdt-block"
+
+/* Default FIT filename on the firmware partition */
+#define FW_FDT_FILENAME "fdt.itb"
+
+/**
+ * fw_fdt_get_source() - find the configured firmware-FDT source node
+ *
+ * Scans the control devicetree for an enabled node with the
+ * "u-boot,firmware-fdt-block" compatible. Disabled nodes are skipped, so a
+ * devicetree may ship the node with status "disabled" and a variant (or a
+ * test) enable it.
+ *
+ * @srcp: returns the source ofnode on success
+ * Return: 0 on success, -ENOENT if no source is configured
+ */
+static int fw_fdt_get_source(ofnode *srcp)
+{
+ ofnode node;
+
+ node = ofnode_by_compatible(ofnode_null(), FW_FDT_COMPAT_BLOCK);
+ while (ofnode_valid(node) && !ofnode_is_enabled(node))
+ node = ofnode_by_compatible(node, FW_FDT_COMPAT_BLOCK);
+
+ if (!ofnode_valid(node))
+ return -ENOENT;
+
+ *srcp = node;
+
+ return 0;
+}
+
+/**
+ * fw_fdt_get_blk() - resolve the block device holding the FIT
+ *
+ * The source node points at its media device (e.g. &mmc0) through the
+ * 'firmware-fdt-store' phandle. The lookup also probes the device, 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, -EINVAL if 'firmware-fdt-store' is missing or its
+ * phandle does not resolve, other negative on error
+ */
+static int fw_fdt_get_blk(ofnode src, struct blk_desc **descp)
+{
+ struct udevice *media, *blk;
+ ofnode store;
+ int ret;
+
+ store = ofnode_parse_phandle(src, "firmware-fdt-store", 0);
+ if (!ofnode_valid(store))
+ return log_msg_ret("store", -EINVAL);
+
+ /* a source is configured: remap -ENOENT to -ENODEV to fail closed */
+ ret = device_get_global_by_ofnode(store, &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
+ *
+ * 'fw_fdt_part', 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 'fw_fdt_part' 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("fw_fdt_part");
+ 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 into an allocated buffer
+ *
+ * @desc: block device holding the firmware partition
+ * @part: partition number
+ * @fname: FIT filename
+ * @bufp: returns the allocated buffer holding the FIT
+ * @sizep: returns the FIT 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 > FIRMWARE_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 FITs that are not self-contained
+ *
+ * @fit: the FIT
+ * 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 (!fit_image_check_type(fit, node, IH_TYPE_FLATDT))
+ return log_msg_ret("type", -EINVAL);
+
+ 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 FIT 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;
+
+ /*
+ * boot_get_fdt_fit() verifies the base image and the selected
+ * configuration, but historically skips an overlay which fails to
+ * load. Verify every image up front so a bad overlay cannot silently
+ * turn a signed base-plus-overlay configuration into the base alone.
+ */
+ if (!fit_all_image_verify(out->fit))
+ return log_msg_ret("verify", -EACCES);
+
+ conf = env_get("fw_fdt_config");
+ if (conf && !*conf)
+ conf = NULL;
+ /*
+ * boot_get_fdt_fit() accepts '#' to compose several configurations.
+ * A firmware-owned devicetree must use one configuration so its base,
+ * overlay set and ordering remain one authenticated unit.
+ */
+ if (conf && strchr(conf, '#'))
+ return log_msg_ret("chain", -EINVAL);
+
+ ret = boot_get_fdt_fit(&images, map_to_sysmem(out->fit), NULL, &conf,
+ IH_ARCH_DEFAULT, &data, &len,
+ &out->fdt_owned);
+ if (ret < 0)
+ return log_msg_ret("conf", ret);
+
+ fdt = map_sysmem(data, len);
+
+ out->fdt = fdt;
+ out->size = len;
+
+ if (len > FIRMWARE_FDT_MAX_SIZE)
+ return log_msg_ret("bigfdt", -E2BIG);
+
+ ret = fdt_check_full(fdt, len);
+ if (ret)
+ return log_msg_ret("chk", -EINVAL);
+
+ out->name = strdup(fname);
+ if (!out->name)
+ return log_msg_ret("name", -ENOMEM);
+
+ return 0;
+}
+
+static int fw_fdt_load_source(struct firmware_fdt *out, ofnode src)
+{
+ int ret;
+
+ memset(out, '\0', sizeof(*out));
+
+ 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 FIT,
+ * missing FIT configuration, ...) to -ENODEV to keep the
+ * failure fatal (fail closed).
+ */
+ if (ret == -ENOENT)
+ ret = -ENODEV;
+ }
+
+ return ret;
+}
+
+int firmware_fdt_load(struct firmware_fdt *out)
+{
+ ofnode src;
+ int ret;
+
+ memset(out, '\0', sizeof(*out));
+
+ ret = fw_fdt_get_source(&src);
+ if (ret)
+ return ret;
+
+ return fw_fdt_load_source(out, src);
+}
+
+int efi_stage_firmware_fdt(ulong fdt_addr, ulong *fdt_sizep, char **namep)
+{
+ struct firmware_fdt fw;
+ ofnode src;
+ char *name = NULL;
+ int ret;
+
+ if (namep)
+ *namep = NULL;
+
+ /*
+ * Check for a configured source before validating the staging address:
+ * an absent source must remain -ENOENT even on systems which do not
+ * provide fdt_addr_r.
+ */
+ ret = fw_fdt_get_source(&src);
+ if (ret)
+ return ret;
+
+ if (!fdt_addr)
+ return log_msg_ret("addr", -EINVAL);
+
+ ret = fw_fdt_load_source(&fw, src);
+ if (ret) {
+ log_err("Failed to assemble the firmware devicetree (err %d)\n",
+ ret);
+ return ret;
+ }
+
+ if (fw.size > FIRMWARE_FDT_MAX_SIZE) {
+ ret = -E2BIG;
+ goto out;
+ }
+
+ if (namep) {
+ name = strdup(fw.name);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ memcpy(map_sysmem(fdt_addr, fw.size), fw.fdt, fw.size);
+ *fdt_sizep = fw.size;
+ if (namep)
+ *namep = name;
+ log_debug("Using firmware-owned devicetree\n");
+
+out:
+ firmware_fdt_free(&fw);
+
+ return ret;
+}
+
+void firmware_fdt_free(struct firmware_fdt *fw)
+{
+ if (fw->fdt_owned)
+ free(fw->fdt);
+
+ free(fw->name);
+ free(fw->fit);
+ memset(fw, '\0', sizeof(*fw));
+}
diff --git a/boot/image-fdt.c b/boot/image-fdt.c
index 9e0e0f93edd..1b6b5725873 100644
--- a/boot/image-fdt.c
+++ b/boot/image-fdt.c
@@ -468,7 +468,8 @@ static int select_fdt(struct bootm_headers *images, const char *select, u8 arch,
fdt_noffset = boot_get_fdt_fit(images, fdt_addr,
&fit_uname_fdt,
&fit_uname_config,
- arch, &load, &len);
+ arch, &load, &len,
+ NULL);
if (fdt_noffset < 0)
return -ENOENT;
diff --git a/boot/image-fit.c b/boot/image-fit.c
index ef90c5abd18..fa22a2e08b2 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -2513,7 +2513,7 @@ out:
int boot_get_fdt_fit(struct bootm_headers *images, ulong addr,
const char **fit_unamep, const char **fit_uname_configp,
- int arch, ulong *datap, ulong *lenp)
+ int arch, ulong *datap, ulong *lenp, bool *ownedp)
{
int fdt_noffset, cfg_noffset, count;
const void *fit;
@@ -2533,6 +2533,8 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong addr,
#endif
fit_uname = fit_unamep ? *fit_unamep : NULL;
+ if (ownedp)
+ *ownedp = false;
if (fit_uname_configp && *fit_uname_configp) {
fit_uname_config_copy = strdup(*fit_uname_configp);
@@ -2691,8 +2693,11 @@ int boot_get_fdt_fit(struct bootm_headers *images, ulong addr,
out:
#ifdef CONFIG_OF_LIBFDT_OVERLAY
- if (fdt_noffset >= 0 && base_buf)
+ if (fdt_noffset >= 0 && base_buf) {
load = map_to_sysmem(base_buf);
+ if (ownedp)
+ *ownedp = true;
+ }
#endif
if (datap)
*datap = load;
diff --git a/doc/develop/uefi/firmware_fdt.rst b/doc/develop/uefi/firmware_fdt.rst
new file mode 100644
index 00000000000..465b0c74b3d
--- /dev/null
+++ b/doc/develop/uefi/firmware_fdt.rst
@@ -0,0 +1,112 @@
+.. 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_FIRMWARE_FDT``) provides a
+consumer-facing interface independently of standard boot. The source
+compatible identifies the storage backend. The first implemented backend,
+``u-boot,firmware-fdt-block``, reads the FIT from a filesystem on a GPT
+partition of a block device.
+
+Additional source backends may load the same FIT from other firmware storage,
+such as UBI on MTD. They reuse the common FIT configuration selection,
+verification and assembly, as well as the consumers below.
+
+The first consumers are the two 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 replaces vendor-specific firmware-devicetree commands while keeping
+storage discovery behind the source backend.
+
+The FIT
+-------
+
+The firmware partition carries a FIT (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 FIT describes and carries the devicetree
+as one artefact, updated atomically with it.
+
+Images in the FIT must be self-contained flat devicetrees: images that carry
+a ``load`` address and FITs using external data are rejected. With
+``FIT_SIGNATURE`` enabled, node and configuration names must not contain
+``@``.
+
+Configuration
+-------------
+
+The FIT source is described in the control devicetree (see
+``doc/device-tree-bindings/firmware-fdt.txt``). Each source compatible
+defines one storage backend and its locator properties. The currently
+implemented ``u-boot,firmware-fdt-block`` backend points at the media device
+through the ``firmware-fdt-store`` phandle and identifies a GPT partition by
+type UUID and/or name, with an optional ``filename`` for the FIT path. Its
+store phandle follows the FWU metadata (``u-boot,fwu-mdata-*``) pattern.
+
+A future backend may use different locator properties, for example an MTD
+device and UBI volume, while preserving the same FIT contents and the common
+selection, verification, assembly and fail-closed behavior.
+
+Two optional environment variables select among what the FIT ships:
+``fw_fdt_part`` pins a partition number (A/B firmware partitions) and
+``fw_fdt_config`` names the FIT configuration to use. Without an explicit
+configuration, compatible best-match against the control devicetree is used;
+if there is no match, the FIT's ``default`` configuration is used. The
+``fw_fdt_config`` value must name one configuration; configuration chaining
+with ``#`` is rejected so the selected base, overlay set and ordering remain
+one authenticated unit.
+
+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 FIT
+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
+FIT 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 FIT 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/uefi/index.rst b/doc/develop/uefi/index.rst
index e26b1fbe05c..67b100691bd 100644
--- a/doc/develop/uefi/index.rst
+++ b/doc/develop/uefi/index.rst
@@ -14,3 +14,4 @@ can be run an UEFI payload.
u-boot_on_efi.rst
iscsi.rst
fwu_updates.rst
+ firmware_fdt.rst
diff --git a/doc/device-tree-bindings/firmware-fdt.txt b/doc/device-tree-bindings/firmware-fdt.txt
new file mode 100644
index 00000000000..9baf9665430
--- /dev/null
+++ b/doc/device-tree-bindings/firmware-fdt.txt
@@ -0,0 +1,150 @@
+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: 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.
+
+The source node's compatible selects the storage backend and its locator
+properties. This document defines the first backend,
+"u-boot,firmware-fdt-block", which reads the FIT from a filesystem on a GPT
+partition of a block device. Additional backends may load the same FIT from
+other firmware storage, for example a UBI volume on MTD, without changing
+FIT selection, verification, assembly or consumers.
+
+For the block backend, a standalone node points at the media device that owns
+the partition by phandle. The node may live anywhere in the control devicetree
+(it is found by compatible); a node with status "disabled" is ignored.
+
+
+firmware-fdt source node
+------------------------
+
+Required properties:
+
+compatible:
+ "u-boot,firmware-fdt-block" - the FIT lives in a filesystem on a
+ GPT partition of a block device. The suffix names the first implemented
+ backend. Sibling compatibles may define other source backends and their
+ storage-specific locator properties later.
+
+firmware-fdt-store:
+ phandle to the media device (UCLASS_MMC, ...) that owns the firmware
+ partition
+
+The partition is selected by the 'fw_fdt_part' 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 on the partition (default: "fdt.itb").
+
+
+Environment
+-----------
+
+Two optional environment variables select among what the FIT ships:
+
+ fw_fdt_part pin a specific partition number (A/B firmware partitions)
+ fw_fdt_config name of the FIT configuration to use; when unset the
+ best compatible match against the control devicetree is
+ used, falling back to the FIT's default configuration
+
+``fw_fdt_config`` selects exactly one configuration; U-Boot's ``#`` syntax
+for composing several configurations is rejected so the base, overlay set and
+ordering remain one authenticated unit. Both values only choose among
+combinations the firmware author shipped; with signed configurations a
+tampered value cannot select an unsigned combination.
+
+
+The FIT
+-------
+
+This 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
+-------
+
+ 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";
+ };
diff --git a/doc/usage/environment.rst b/doc/usage/environment.rst
index 0143f81f2c0..6515fa47e58 100644
--- a/doc/usage/environment.rst
+++ b/doc/usage/environment.rst
@@ -243,6 +243,17 @@ fdtcontroladdr
device tree used by U-Boot when CONFIG_OF_CONTROL is
defined.
+fw_fdt_config
+ Name of the configuration to select from a firmware-owned devicetree
+ FIT. If unset, U-Boot uses compatible best-match against the control
+ devicetree, falling back to the FIT's default configuration.
+ Configuration chaining with ``#`` is not supported.
+
+fw_fdt_part
+ Partition number containing the firmware-owned devicetree FIT. This can
+ pin one side of an A/B firmware layout. If unset, U-Boot uses the
+ partition selectors in the ``u-boot,firmware-fdt-block`` control-DT node.
+
initrd_high
restrict positioning of initrd images:
If this variable is not set, initrd images will be
diff --git a/include/firmware_fdt.h b/include/firmware_fdt.h
new file mode 100644
index 00000000000..b6afb3b150e
--- /dev/null
+++ b/include/firmware_fdt.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __FIRMWARE_FDT_H
+#define __FIRMWARE_FDT_H
+
+#include <linux/errno.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+/* Maximum size of both the firmware FIT and the assembled devicetree */
+#define FIRMWARE_FDT_MAX_SIZE SZ_4M
+
+/**
+ * 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: owned FIT filename (for diagnostics)
+ * @fit: internal: buffer holding the FIT
+ * @fit_size: internal: size of the FIT, in bytes
+ * @fdt_owned: internal: true if @fdt is a separate allocation
+ *
+ * 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;
+ char *name;
+ void *fit;
+ ulong fit_size;
+ bool fdt_owned;
+};
+
+#if CONFIG_IS_ENABLED(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 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);
+
+/**
+ * efi_stage_firmware_fdt() - stage a firmware-owned devicetree for EFI
+ *
+ * This is the common policy adapter for EFI consumers. It first checks
+ * whether a source is configured, then assembles and copies its devicetree
+ * to @fdt_addr. Only -ENOENT permits a caller to try another source.
+ *
+ * Callers must read ``fdt_addr_r`` once and pass that value as @fdt_addr.
+ * The same value must be used for any fallback source, so an environment
+ * change cannot make the two paths disagree.
+ *
+ * @fdt_addr: destination address, normally the caller's ``fdt_addr_r`` value
+ * @fdt_sizep: returns the staged devicetree size
+ * @namep: if non-NULL, returns an allocated copy of the FIT filename
+ * Return: 0 if staged; -ENOENT if no source is configured; another negative
+ * errno if a configured source cannot be staged
+ */
+int efi_stage_firmware_fdt(ulong fdt_addr, ulong *fdt_sizep, char **namep);
+#else
+static inline int firmware_fdt_load(struct firmware_fdt *out)
+{
+ return -ENOENT;
+}
+
+static inline void firmware_fdt_free(struct firmware_fdt *fw)
+{
+}
+
+static inline int efi_stage_firmware_fdt(ulong fdt_addr, ulong *fdt_sizep,
+ char **namep)
+{
+ return -ENOENT;
+}
+#endif
+
+#endif /* __FIRMWARE_FDT_H */
diff --git a/include/image.h b/include/image.h
index 4149ebbcce9..0f297ee5d97 100644
--- a/include/image.h
+++ b/include/image.h
@@ -719,12 +719,14 @@ int boot_get_setup_fit(struct bootm_headers *images, uint8_t arch,
* @param arch Expected architecture (IH_ARCH_...)
* @param datap Returns address of loaded image
* @param lenp Returns length of loaded image
+ * @param ownedp Returns true if the loaded image is separately allocated
+ * and must be freed by the caller, or NULL
*
* Return: node offset of base image, or -ve error code on error
*/
int boot_get_fdt_fit(struct bootm_headers *images, ulong addr,
const char **fit_unamep, const char **fit_uname_configp,
- int arch, ulong *datap, ulong *lenp);
+ int arch, ulong *datap, ulong *lenp, bool *ownedp);
/**
* fit_image_load() - load an image from a FIT
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 3/5] bootmeth: efi: use the firmware-owned devicetree
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
2026-07-28 13:20 ` [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree Carlo Caione
2026-07-28 13:20 ` [PATCH v2 2/5] boot: add a firmware-owned devicetree source Carlo Caione
@ 2026-07-28 13:20 ` Carlo Caione
2026-08-15 18:44 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 4/5] efi_loader: bootmgr: install " Carlo Caione
` (3 subsequent siblings)
6 siblings, 1 reply; 17+ messages in thread
From: Carlo Caione @ 2026-07-28 13:20 UTC (permalink / raw)
To: u-boot
Cc: Ahmad Fatoum, David Lechner, Julien Masson, Vitor Sato Eschholz,
Heinrich Schuchardt, Ilias Apalodimas, Tom Rini, Carlo Caione,
Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
When a firmware-owned devicetree source is configured, stage it at the
same fdt_addr_r value used by the normal EFI bootmeth fallback and pass it
through bflow->fdt_addr to efi_install_fdt().
Use the shared helper in both disk and network paths. Only -ENOENT, meaning
that no source is configured, permits the existing ESP, network or
prior-stage fallback. Any failure after a source is configured ends that
bootflow.
The firmware-owned devicetree is complete and authoritative, so do not
apply extension-board overlays on top of it; those combinations belong in
the FIT.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
boot/bootmeth_efi.c | 45 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 39 insertions(+), 6 deletions(-)
diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
index e187dc39912..469a07e9540 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>
@@ -100,7 +101,7 @@ static int distro_efi_check(struct udevice *dev, struct bootflow_iter *iter)
static int distro_efi_try_bootflow_files(struct udevice *dev,
struct bootflow *bflow)
{
- ulong fdt_addr, size, overlay_addr;
+ ulong fdt_addr, fw_fdt_size, size, overlay_addr;
const struct extension *extension;
struct fdt_header *working_fdt;
struct blk_desc *desc = NULL;
@@ -130,6 +131,22 @@ 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 FIT as configurations.
+ */
+ ret = efi_stage_firmware_fdt(fdt_addr, &fw_fdt_size,
+ &bflow->fdt_fname);
+ if (!ret) {
+ bflow->fdt_size = fw_fdt_size;
+ bflow->fdt_addr = fdt_addr;
+ return 0;
+ }
+ if (ret != -ENOENT)
+ return log_msg_ret("fwf", ret);
+
/* try the various available names */
ret = -ENOENT;
*fname = '\0';
@@ -222,9 +239,9 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
char file_addr[17], fname[256];
char *tftp_argv[] = {"tftp", file_addr, fname, NULL};
struct cmd_tbl cmdtp = {}; /* dummy */
- const char *addr_str, *fdt_addr_str, *bootfile_name;
+ const char *addr_str, *bootfile_name;
int ret, arch, size;
- ulong addr, fdt_addr;
+ ulong addr, fdt_addr, fw_fdt_size;
char str[36];
ret = get_efi_pxe_vci(str, sizeof(str));
@@ -268,6 +285,24 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
if (!bflow->fname)
return log_msg_ret("fi0", -ENOMEM);
+ /*
+ * Read fdt_addr_r once so the firmware-FDT source and network fallback
+ * below stage at the same address. A configured firmware-owned
+ * devicetree outranks the network-provided one (and the prior-stage /
+ * built-in devicetree below), so a DHCP/TFTP server cannot replace it.
+ */
+ fdt_addr = env_get_hex("fdt_addr_r", 0);
+ ret = efi_stage_firmware_fdt(fdt_addr, &fw_fdt_size,
+ &bflow->fdt_fname);
+ if (!ret) {
+ bflow->fdt_size = fw_fdt_size;
+ bflow->fdt_addr = fdt_addr;
+ 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) {
@@ -279,10 +314,8 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
return log_msg_ret("nam", ret);
}
- fdt_addr_str = env_get("fdt_addr_r");
- if (!fdt_addr_str)
+ if (!fdt_addr)
return log_msg_ret("fdt", -EINVAL);
- fdt_addr = hextoul(fdt_addr_str, NULL);
sprintf(file_addr, "%lx", fdt_addr);
bflow->fdt_fname = strdup(fname);
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 4/5] efi_loader: bootmgr: install the firmware-owned devicetree
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
` (2 preceding siblings ...)
2026-07-28 13:20 ` [PATCH v2 3/5] bootmeth: efi: use the firmware-owned devicetree Carlo Caione
@ 2026-07-28 13:20 ` Carlo Caione
2026-08-15 18:44 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 5/5] test: boot: add firmware-FDT source tests Carlo Caione
` (2 subsequent siblings)
6 siblings, 1 reply; 17+ messages in thread
From: Carlo Caione @ 2026-07-28 13:20 UTC (permalink / raw)
To: u-boot
Cc: Ahmad Fatoum, David Lechner, Julien Masson, Vitor Sato Eschholz,
Heinrich Schuchardt, Ilias Apalodimas, Tom Rini, Carlo Caione,
Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
SystemReady IR can boot through the UEFI boot manager without going
through the per-device EFI bootmeth. Use the shared staging helper here
as well so both EFI launch paths install the same firmware-owned
devicetree.
The precedence is an explicitly passed FDT, firmware-owned FDT, Boot####
load-option FDT, then distro/ESP FDT. A configured source fails closed,
and assembly failures are reported as EFI_DEVICE_ERROR.
Route load-option errors through the common cleanup path while adding it.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
lib/efi_loader/efi_bootmgr.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
index 785484abf23..cb817116692 100644
--- a/lib/efi_loader/efi_bootmgr.c
+++ b/lib/efi_loader/efi_bootmgr.c
@@ -14,8 +14,10 @@
#include <efi.h>
#include <efi_device_path.h>
#include <env.h>
+#include <firmware_fdt.h>
#include <log.h>
#include <malloc.h>
+#include <mapmem.h>
#include <net.h>
#include <part.h>
#include <efi_loader.h>
@@ -1296,7 +1298,8 @@ out:
*
* Invoke the EFI boot manager and execute a binary according to its boot
* options. The devicetree precedence, from highest to lowest, is an FDT
- * passed in @fdt, the Boot#### load-option FDT, then the distro/ESP FDT.
+ * passed in @fdt, a configured firmware-owned FDT, the Boot#### load-option
+ * FDT, then the distro/ESP FDT.
*
* Return: status code
*/
@@ -1320,13 +1323,27 @@ efi_status_t efi_bootmgr_run(void *fdt)
}
if (!IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
+ if (!fdt) {
+ ulong fdt_addr = env_get_hex("fdt_addr_r", 0);
+ int err;
+
+ err = efi_stage_firmware_fdt(fdt_addr, &fdt_size, NULL);
+ if (!err) {
+ fdt = map_sysmem(fdt_addr, fdt_size);
+ } else if (err != -ENOENT) {
+ ret = EFI_DEVICE_ERROR;
+ goto out;
+ }
+ }
+
if (!fdt) {
ret = load_fdt_from_load_option(&fdt_lo);
if (ret != EFI_SUCCESS)
- return ret;
+ goto out;
if (fdt_lo)
fdt = fdt_lo;
}
+
if (!fdt) {
efi_load_distro_fdt(handle, &fdt_distro, &fdt_size);
fdt = fdt_distro;
@@ -1339,7 +1356,9 @@ 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 */
free(fdt_lo);
if (fdt_distro)
efi_free_pages((uintptr_t)fdt_distro,
--
2.55.0
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v2 5/5] test: boot: add firmware-FDT source tests
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
` (3 preceding siblings ...)
2026-07-28 13:20 ` [PATCH v2 4/5] efi_loader: bootmgr: install " Carlo Caione
@ 2026-07-28 13:20 ` Carlo Caione
2026-08-15 18:45 ` Simon Glass
2026-07-29 7:10 ` [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Peter Robinson
2026-08-15 18:45 ` [v2,0/5] " Simon Glass
6 siblings, 1 reply; 17+ messages in thread
From: Carlo Caione @ 2026-07-28 13:20 UTC (permalink / raw)
To: u-boot
Cc: Ahmad Fatoum, David Lechner, Julien Masson, Vitor Sato Eschholz,
Heinrich Schuchardt, Ilias Apalodimas, Tom Rini, Carlo Caione,
Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
Add sandbox coverage for firmware_fdt_load(). The Python fixture creates
mmc11.img in persistent_data_dir with A/B firmware partitions. Its first
FAT partition carries FITs for a base-plus-overlay configuration,
compatible best-match, external-data rejection and corrupt-hash rejection.
Construct the complete MMC provider and firmware-FDT source topology in
each flat-tree test, including the provider phandle. Nothing is added to
the shared sandbox control devicetree, and the sandbox test framework
restores its FDT snapshot even when an assertion fails.
Cover default and explicit configuration selection, compatible best-match,
owned and borrowed assembled-FDT storage, EFI staging and its returned
filename, and the fail-closed cases: corrupt base or overlay data,
configuration chaining, a missing configuration or partition, an invalid
source phandle, external data and mismatched partition selectors. Verify
that only a genuinely absent source returns -ENOENT.
Signed-off-by: Carlo Caione <ccaione@baylibre.com>
---
configs/sandbox_defconfig | 1 +
test/boot/Makefile | 1 +
test/boot/firmware_fdt.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++
test/py/tests/test_ut.py | 181 ++++++++++++++++++++
4 files changed, 594 insertions(+)
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 79f46317e45..b2affd96831 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -27,6 +27,7 @@ CONFIG_FIT_VERITY=y
CONFIG_FIT_VERBOSE=y
CONFIG_BOOTMETH_ANDROID=y
CONFIG_BOOTMETH_RAUC=y
+CONFIG_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 59a87028704..681c8c4c94f 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_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..098aeea4873
--- /dev/null
+++ b/test/boot/firmware_fdt.c
@@ -0,0 +1,411 @@
+// 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 (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 tests create the complete source topology at runtime, including the
+ * mmc11 provider and its phandle. The shared test.dts stays unconfigured,
+ * while the sandbox DM test framework restores its FDT snapshot after every
+ * test, including a failed one.
+ */
+
+#include <dm.h>
+#include <env.h>
+#include <firmware_fdt.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <os.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_NODE_PATH "/fw-fdt"
+#define FWFDT_STORE_PROP "firmware-fdt-store"
+#define FWFDT_STORE_PHANDLE 0x10000
+#define FWFDT_TYPE_UUID "384e979b-eb76-435a-a3a6-1a071dbad91d"
+#define FWFDT_TEST_FLAGS (UTF_DM | UTF_SCAN_FDT | UTF_FLAT_TREE)
+
+static ofnode fwfdt_source_node(void)
+{
+ return ofnode_path(FWFDT_NODE_PATH);
+}
+
+/* Bind the runtime-created mmc node that owns the firmware-FDT image */
+static int fwfdt_bind_mmc(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ofnode node;
+
+ node = ofnode_path("/mmc11");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false));
+
+ return 0;
+}
+
+/*
+ * Create the full firmware-FDT topology. Set @with_source to false for the
+ * no-source test, which still needs the media device.
+ */
+static int fwfdt_configure(struct unit_test_state *uts, bool with_source)
+{
+ char fname[256];
+ ofnode root, mmc, src;
+
+ ut_assertok(os_persistent_file(fname, sizeof(fname), "mmc11.img"));
+ root = oftree_root(oftree_default());
+ ut_assertok(ofnode_add_subnode(root, "mmc11", &mmc));
+ ut_assertok(ofnode_write_string(mmc, "compatible", "sandbox,mmc"));
+ ut_assertok(ofnode_write_string(mmc, "filename", fname));
+ ut_assertok(ofnode_write_u32(mmc, "phandle", FWFDT_STORE_PHANDLE));
+
+ if (!with_source)
+ return 0;
+
+ ut_assertok(ofnode_add_subnode(root, "fw-fdt", &src));
+ ut_assertok(ofnode_write_string(src, "compatible",
+ "u-boot,firmware-fdt-block"));
+ ut_assertok(ofnode_write_u32(src, FWFDT_STORE_PROP,
+ FWFDT_STORE_PHANDLE));
+ ut_assertok(ofnode_write_string(src, "partition-type-uuid",
+ FWFDT_TYPE_UUID));
+ ut_assertok(ofnode_write_string(src, "partition-name", "firmware"));
+ ut_assertok(ofnode_write_string(src, "filename", "fdt.itb"));
+
+ return 0;
+}
+
+/* Clear the environment values used by the tests */
+static int fwfdt_clear_env(struct unit_test_state *uts)
+{
+ env_set("fw_fdt_part", NULL);
+ env_set("fw_fdt_config", NULL);
+
+ return 0;
+}
+
+/* Happy path: the FIT's default configuration applies base + overlay */
+static int firmware_fdt_test_load(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ void *fdt;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(firmware_fdt_load(&fw));
+
+ ut_asserteq_str("fdt.itb", fw.name);
+ ut_assert(fw.size > 0);
+ ut_assert(fw.fdt_owned);
+
+ 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_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_load, FWFDT_TEST_FLAGS);
+
+/* 'fw_fdt_config' selects another configuration the FIT ships */
+static int firmware_fdt_test_select(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ void *fdt;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ 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_assert(!fw.fdt_owned);
+ 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_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_select, FWFDT_TEST_FLAGS);
+
+/*
+ * Configuration chaining could assemble a combination which was never signed
+ * as one unit. Only one FIT configuration may be selected.
+ */
+static int firmware_fdt_test_config_chain(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(env_set("fw_fdt_config", "conf-base#conf-overlay"));
+ ut_asserteq(-EINVAL, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_config_chain, FWFDT_TEST_FLAGS);
+
+/* The shared EFI helper stages the result and owns its returned filename */
+static int firmware_fdt_test_stage(struct unit_test_state *uts)
+{
+ char *name;
+ ulong size;
+ void *buf;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ buf = malloc(FIRMWARE_FDT_MAX_SIZE);
+ ut_assertnonnull(buf);
+ ut_assertok(efi_stage_firmware_fdt(map_to_sysmem(buf), &size, &name));
+ ut_asserteq_str("fdt.itb", name);
+ ut_asserteq(size, fdt_totalsize(buf));
+ ut_assertnonnull(fdt_getprop(buf, 0, "fw-base-prop", NULL));
+ ut_assertnonnull(fdt_getprop(buf, 0, "fw-overlay-prop", NULL));
+ free(name);
+ free(buf);
+
+ ut_assertok(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_stage, FWFDT_TEST_FLAGS);
+
+/* Compatible best-match against the control DT selects conf-sandbox */
+static int firmware_fdt_test_best_match(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ const char *value;
+ ofnode node;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ node = fwfdt_source_node();
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_write_string(node, "filename", "fdt-best.itb"));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(firmware_fdt_load(&fw));
+ value = fdt_getprop(fw.fdt, 0, "fw-best-prop", NULL);
+ ut_assertnonnull(value);
+ ut_asserteq_str("sandbox", value);
+ firmware_fdt_free(&fw);
+
+ ut_assertok(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_best_match, FWFDT_TEST_FLAGS);
+
+/* A corrupted base fails hash verification and cannot fall back */
+static int firmware_fdt_test_corrupt(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ ofnode node;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ node = fwfdt_source_node();
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_write_string(node, "filename", "fdt-corrupt.itb"));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_asserteq(-EACCES, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_corrupt, FWFDT_TEST_FLAGS);
+
+/* A corrupted overlay is fatal too; it must never be silently skipped */
+static int firmware_fdt_test_corrupt_overlay(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+ ofnode node;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ node = fwfdt_source_node();
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_write_string(node, "filename",
+ "fdt-corrupt-overlay.itb"));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_asserteq(-EACCES, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_corrupt_overlay, FWFDT_TEST_FLAGS);
+
+/*
+ * A selector naming a configuration the FIT 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, true));
+ 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_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_bad_config, FWFDT_TEST_FLAGS);
+
+/* 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, true));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_assertok(env_set("fw_fdt_part", "9"));
+ ut_asserteq(-ENODEV, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_no_part, FWFDT_TEST_FLAGS);
+
+/*
+ * Without a source node, -ENOENT is the only result which lets callers fall
+ * back. This also proves the provider alone does not configure the feature.
+ */
+static int firmware_fdt_test_no_source(struct unit_test_state *uts)
+{
+ struct firmware_fdt fw;
+
+ ut_assertok(fwfdt_configure(uts, false));
+ ut_assertok(fwfdt_bind_mmc(uts));
+
+ ut_asserteq(-ENOENT, firmware_fdt_load(&fw));
+ /* Source detection precedes address validation in the EFI helper */
+ ut_asserteq(-ENOENT, efi_stage_firmware_fdt(0, NULL, NULL));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_no_source, FWFDT_TEST_FLAGS);
+
+/*
+ * A 'firmware-fdt-store' phandle that 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;
+ fdt32_t bad;
+ ofnode node;
+
+ ut_assertok(fwfdt_configure(uts, true));
+ node = fwfdt_source_node();
+ ut_assert(ofnode_valid(node));
+
+ bad = cpu_to_fdt32(0x7fffffff);
+ ut_assertok(ofnode_write_prop(node, FWFDT_STORE_PROP, &bad,
+ sizeof(bad), true));
+ ut_asserteq(-EINVAL, firmware_fdt_load(&fw));
+
+ ut_assertok(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_bad_source, FWFDT_TEST_FLAGS);
+
+/* A FIT 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, true));
+
+ node = fwfdt_source_node();
+ 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(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_external, FWFDT_TEST_FLAGS);
+
+/*
+ * 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, true));
+
+ node = fwfdt_source_node();
+ 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(fwfdt_clear_env(uts));
+
+ return 0;
+}
+
+BOOTSTD_TEST(firmware_fdt_test_part_mismatch, FWFDT_TEST_FLAGS);
diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
index fa50c8008a5..7678795dd0f 100644
--- a/test/py/tests/test_ut.py
+++ b/test/py/tests/test_ut.py
@@ -614,6 +614,186 @@ 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 (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.persistent_data_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 FIT
+ src = os.path.join(ubman.config.persistent_data_dir, 'fwfdt')
+ mkdir_cond(src)
+ base_dtb = os.path.join(src, 'base.dtb')
+ match_dtb = os.path.join(src, 'match.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"; };')
+ utils.run_and_log(
+ ubman, f'dtc -O dtb -o {match_dtb}',
+ stdin=b'/dts-v1/; / { compatible = "sandbox"; '
+ b'fw-best-prop = "sandbox"; };')
+
+ 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')
+ fit = os.path.join(fs_dir, 'fdt.itb')
+ utils.run_and_log(ubman, f'{mkimage} -f {its} {fit}')
+
+ # 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")}')
+
+ # A FIT with two distinct compatibles for CONFIG_FIT_BEST_MATCH coverage
+ best_its = os.path.join(src, 'fdt-best.its')
+ with open(best_its, 'w', encoding='ascii') as outf:
+ outf.write(f'''
+/dts-v1/;
+/ {{
+\tdescription = "Firmware-owned compatible selection test";
+\t#address-cells = <1>;
+
+\timages {{
+\t\tfdt-generic {{
+\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-sandbox {{
+\t\t\tdata = /incbin/("{match_dtb}");
+\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-generic";
+\t\tconf-generic {{
+\t\t\tfdt = "fdt-generic";
+\t\t}};
+\t\tconf-sandbox {{
+\t\t\tfdt = "fdt-sandbox";
+\t\t}};
+\t}};
+}};
+''')
+ utils.run_and_log(
+ ubman,
+ f'{mkimage} -f {best_its} {os.path.join(fs_dir, "fdt-best.itb")}')
+
+ # Corrupt base data without updating its hash: loading must fail closed
+ with open(fit, 'rb') as inf:
+ corrupt_data = bytearray(inf.read())
+ with open(base_dtb, 'rb') as inf:
+ base_data = inf.read()
+ data_offset = corrupt_data.find(base_data)
+ if data_offset < 0:
+ raise ValueError('Cannot locate base DTB in firmware-FDT FIT')
+ corrupt_data[data_offset + len(base_data) - 1] ^= 1
+ with open(os.path.join(fs_dir, 'fdt-corrupt.itb'), 'wb') as outf:
+ outf.write(corrupt_data)
+
+ # A corrupt overlay must be fatal too, never silently skipped
+ with open(fit, 'rb') as inf:
+ corrupt_data = bytearray(inf.read())
+ with open(ovl_dtbo, 'rb') as inf:
+ overlay_data = inf.read()
+ data_offset = corrupt_data.find(overlay_data)
+ if data_offset < 0:
+ raise ValueError('Cannot locate overlay DTBO in firmware-FDT FIT')
+ corrupt_data[data_offset + len(overlay_data) - 1] ^= 1
+ with open(os.path.join(fs_dir, 'fdt-corrupt-overlay.itb'), 'wb') as outf:
+ outf.write(corrupt_data)
+
+ 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 +806,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] 17+ messages in thread
* Re: [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
` (4 preceding siblings ...)
2026-07-28 13:20 ` [PATCH v2 5/5] test: boot: add firmware-FDT source tests Carlo Caione
@ 2026-07-29 7:10 ` Peter Robinson
2026-08-15 18:45 ` [v2,0/5] " Simon Glass
6 siblings, 0 replies; 17+ messages in thread
From: Peter Robinson @ 2026-07-29 7:10 UTC (permalink / raw)
To: Carlo Caione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Ilias Apalodimas,
Tom Rini, Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
On Tue, 28 Jul 2026 at 14:21, Carlo Caione <ccaione@baylibre.com> wrote:
>
> EBBR-style firmware (SystemReady IR) owns the devicetree: the OS is
For reference it's no long SystemReady IR, but SystemReady DT [1] or
SystemReady DeviceTree.
[1] https://www.arm.com/architecture/system-architectures/systemready-compliance-program
> 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. As a result,
> platforms carry downstream commands to provide it.
>
> This series adds a generic loader for a firmware-owned devicetree FIT on
> a dedicated GPT partition:
>
> - one FIT ("fdt.itb" by default) carries the base devicetree and its
> overlays. FIT configurations describe the valid combinations, so a
> signed configuration authenticates the base, overlay set and ordering;
>
> - a standalone 'u-boot,firmware-fdt-block' control-DT node points to the
> media device through a 'firmware-fdt-store' phandle. The node selects
> a partition by type UUID and/or name;
>
> - 'fw_fdt_part' can pin an A/B partition and 'fw_fdt_config' can select
> an explicit configuration. Otherwise compatible best-match against the
> control devicetree is used, falling back to the FIT default. Configuration
> chaining is rejected so each combination remains one authenticated unit;
>
> - one staging helper owns the EFI policy: use fdt_addr_r, enforce the
> size cap and distinguish an absent source from a configured source
> which failed. Both the EFI bootmeth and EFI boot manager use it;
>
> - once a source is configured, any failure to assemble the devicetree is
> fatal rather than silently falling back to an unverified source. Every
> image is verified up front so a corrupt overlay cannot be silently skipped.
>
> The loader itself does not depend on standard boot. EFI is its first
> consumer, but the source lookup, FIT selection, verification and overlay
> assembly are generic.
>
> Patch 1 makes an FDT passed to efi_bootmgr_run() outrank a Boot#### FDT,
> as an independent behaviour fix. Patch 2 adds the loader, binding,
> documentation and shared EFI staging policy. Patches 3 and 4 integrate
> the EFI bootmeth and boot manager. Patch 5 adds sandbox coverage.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
> ---
> Changes in v2:
> - Make the loader independent of bootstd and describe EFI as its first
> consumer.
> - Describe the source with a standalone compatible node which points to
> the backing media through a phandle.
> - Rename 'boot_dtb' to 'fw_fdt_part' and document both environment
> variables in doc/usage/environment.rst.
> - Factor EFI staging and fail-closed policy into one helper used by all
> three call sites.
> - Track the assembled FDT's ownership explicitly and own the FIT filename.
> - Select FIT_BEST_MATCH and add explicit compatible-selection coverage.
> - Split the passed-FDT precedence change into its own first patch.
> - Build the entire sandbox source topology at runtime, store mmc11.img in
> persistent_data_dir and add staging, configuration-chaining and corrupt
> base/overlay fail-closed coverage.
> - Link to v1: https://patch.msgid.link/20260706-ccaione-upstream-ebbr-v1-0-e742cdd6abe5@baylibre.com
>
> ---
> Carlo Caione (5):
> efi_loader: bootmgr: preserve a passed devicetree
> boot: add a firmware-owned devicetree source
> bootmeth: efi: use the firmware-owned devicetree
> efi_loader: bootmgr: install the firmware-owned devicetree
> test: boot: add firmware-FDT source tests
>
> MAINTAINERS | 3 +
> boot/Kconfig | 24 ++
> boot/Makefile | 1 +
> boot/bootmeth_efi.c | 45 +++-
> boot/firmware_fdt.c | 420 ++++++++++++++++++++++++++++++
> boot/image-fdt.c | 3 +-
> boot/image-fit.c | 9 +-
> configs/sandbox_defconfig | 1 +
> doc/develop/uefi/firmware_fdt.rst | 112 ++++++++
> doc/develop/uefi/index.rst | 1 +
> doc/device-tree-bindings/firmware-fdt.txt | 150 +++++++++++
> doc/usage/environment.rst | 11 +
> include/firmware_fdt.h | 95 +++++++
> include/image.h | 4 +-
> lib/efi_loader/efi_bootmgr.c | 39 ++-
> test/boot/Makefile | 1 +
> test/boot/firmware_fdt.c | 411 +++++++++++++++++++++++++++++
> test/py/tests/test_ut.py | 181 +++++++++++++
> 18 files changed, 1492 insertions(+), 19 deletions(-)
> ---
> base-commit: b635d43bca429500cb8ef20aa151cb5773b9a8a5
> change-id: 20260706-ccaione-upstream-ebbr-206c30be3a3a
>
> Best regards,
> --
> Carlo Caione <ccaione@baylibre.com>
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree
2026-07-28 13:20 ` [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree Carlo Caione
@ 2026-08-04 8:51 ` Ilias Apalodimas
2026-08-09 0:34 ` Simon Glass
1 sibling, 0 replies; 17+ messages in thread
From: Ilias Apalodimas @ 2026-08-04 8:51 UTC (permalink / raw)
To: Carlo Caione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Tom Rini, Simon Glass,
Quentin Schulz, Marek Vasut, Johan Jonker, Randolph Sapp,
Daniel Golle, James Hilliard, Aristo Chen, Peng Fan,
Vincent Jardin
On Tue, 28 Jul 2026 at 16:20, Carlo Caione <ccaione@baylibre.com> wrote:
>
> An FDT explicitly passed to efi_bootmgr_run() is currently replaced by
> the FDT referenced by the selected Boot#### load option. This makes an
> operator-supplied devicetree ineffective whenever the boot option carries
> one.
>
> Only consult the load-option FDT when no FDT was passed. Document the
> resulting precedence so the explicit call argument remains the
> highest-priority source.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
> ---
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> lib/efi_loader/efi_bootmgr.c | 20 +++++++++++---------
> 1 file changed, 11 insertions(+), 9 deletions(-)
>
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> index 8c9a9b5eb56..785484abf23 100644
> --- a/lib/efi_loader/efi_bootmgr.c
> +++ b/lib/efi_loader/efi_bootmgr.c
> @@ -1294,9 +1294,9 @@ out:
> * efi_bootmgr_run() - execute EFI boot manager
> * @fdt: Flat device tree
> *
> - * 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.
> + * Invoke the EFI boot manager and execute a binary according to its boot
> + * options. The devicetree precedence, from highest to lowest, is an FDT
> + * passed in @fdt, the Boot#### load-option FDT, then the distro/ESP FDT.
> *
> * Return: status code
> */
> @@ -1305,7 +1305,7 @@ 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;
> + void *fdt_lo = NULL, *fdt_distro = NULL;
> efi_uintn_t fdt_size;
>
> /* Initialize EFI drivers */
> @@ -1320,11 +1320,13 @@ 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;
> + 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;
>
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/5] boot: add a firmware-owned devicetree source
2026-07-28 13:20 ` [PATCH v2 2/5] boot: add a firmware-owned devicetree source Carlo Caione
@ 2026-08-04 8:58 ` Ilias Apalodimas
2026-08-15 18:44 ` Simon Glass
1 sibling, 0 replies; 17+ messages in thread
From: Ilias Apalodimas @ 2026-08-04 8:58 UTC (permalink / raw)
To: Carlo Caione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Tom Rini, Simon Glass,
Quentin Schulz, Marek Vasut, Johan Jonker, Randolph Sapp,
Daniel Golle, James Hilliard, Aristo Chen, Peng Fan,
Vincent Jardin
Hi Carlo,
[...]
> +
> +int efi_stage_firmware_fdt(ulong fdt_addr, ulong *fdt_sizep, char **namep)
Is this really limited to efi only? This seems a pretty generic way of
loading the DT that's defined somewhere in the firmware. So perhaps
remove the efi prefix? Or find a more suitable name for it.
[...]
Cheers
/Ilias
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree
2026-07-28 13:20 ` [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree Carlo Caione
2026-08-04 8:51 ` Ilias Apalodimas
@ 2026-08-09 0:34 ` Simon Glass
1 sibling, 0 replies; 17+ messages in thread
From: Simon Glass @ 2026-08-09 0:34 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Ilias Apalodimas,
Tom Rini, Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
> efi_loader: bootmgr: preserve a passed devicetree
>
> An FDT explicitly passed to efi_bootmgr_run() is currently replaced by
> the FDT referenced by the selected Boot#### load option. This makes an
> operator-supplied devicetree ineffective whenever the boot option carries
> one.
>
> Only consult the load-option FDT when no FDT was passed. Document the
> resulting precedence so the explicit call argument remains the
> highest-priority source.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
> Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
>
> lib/efi_loader/efi_bootmgr.c | 20 +++++++++++---------
> 1 file changed, 11 insertions(+), 9 deletions(-)
Reviewed-by: Simon Glass <sjg@chromium.org>
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 3/5] bootmeth: efi: use the firmware-owned devicetree
2026-07-28 13:20 ` [PATCH v2 3/5] bootmeth: efi: use the firmware-owned devicetree Carlo Caione
@ 2026-08-15 18:44 ` Simon Glass
0 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2026-08-15 18:44 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Ilias Apalodimas,
Tom Rini, Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
Hi Carlo,
On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
> bootmeth: efi: use the firmware-owned devicetree
>
> When a firmware-owned devicetree source is configured, stage it at the
> same fdt_addr_r value used by the normal EFI bootmeth fallback and pass it
> through bflow->fdt_addr to efi_install_fdt().
>
> Use the shared helper in both disk and network paths. Only -ENOENT, meaning
> that no source is configured, permits the existing ESP, network or
> prior-stage fallback. Any failure after a source is configured ends that
> bootflow.
>
> The firmware-owned devicetree is complete and authoritative, so do not
> apply extension-board overlays on top of it; those combinations belong in
> the FIT.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
>
> boot/bootmeth_efi.c | 45 +++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 39 insertions(+), 6 deletions(-)
> diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
> @@ -130,6 +131,22 @@ static int distro_efi_try_bootflow_files(struct udevice *dev,
> + ret = efi_stage_firmware_fdt(fdt_addr, &fw_fdt_size,
> + &bflow->fdt_fname);
> + if (!ret) {
> + bflow->fdt_size = fw_fdt_size;
> + bflow->fdt_addr = fdt_addr;
> + return 0;
> + }
Just to check: distro_efi_try_bootflow_files() runs per candidate
bootflow, so on a machine with several block devices/partitions the
firmware FIT will be re-read, re-verified and re-assembled for every
scan. Since the source is fixed by the control DT, is there any reason
not to cache the assembled result across bootflows? It would also make
the fail-closed policy cheaper to enforce.
> diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c
> @@ -279,10 +314,8 @@ static int distro_efi_read_bootflow_net(struct bootflow *bflow)
> - fdt_addr_str = env_get("fdt_addr_r");
> - if (!fdt_addr_str)
> + if (!fdt_addr)
> return log_msg_ret("fdt", -EINVAL);
> - fdt_addr = hextoul(fdt_addr_str, NULL);
BTW this will reject a 0 address, but that seems fine as it should not
occur in practice.
Regards,
Simon
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 4/5] efi_loader: bootmgr: install the firmware-owned devicetree
2026-07-28 13:20 ` [PATCH v2 4/5] efi_loader: bootmgr: install " Carlo Caione
@ 2026-08-15 18:44 ` Simon Glass
0 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2026-08-15 18:44 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Ilias Apalodimas,
Tom Rini, Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
Hi Carlo,
On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
> efi_loader: bootmgr: install the firmware-owned devicetree
>
> SystemReady IR can boot through the UEFI boot manager without going
> through the per-device EFI bootmeth. Use the shared staging helper here
> as well so both EFI launch paths install the same firmware-owned
> devicetree.
>
> The precedence is an explicitly passed FDT, firmware-owned FDT, Boot####
> load-option FDT, then distro/ESP FDT. A configured source fails closed,
> and assembly failures are reported as EFI_DEVICE_ERROR.
>
> Route load-option errors through the common cleanup path while adding it.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
>
> lib/efi_loader/efi_bootmgr.c | 23 +++++++++++++++++++++--
> 1 file changed, 21 insertions(+), 2 deletions(-)
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> @@ -1320,13 +1323,27 @@ efi_status_t efi_bootmgr_run(void *fdt)
> + if (!fdt) {
> + ulong fdt_addr = env_get_hex("fdt_addr_r", 0);
> + int err;
> +
> + err = efi_stage_firmware_fdt(fdt_addr, &fdt_size, NULL);
fdt_size is efi_uintn_t (size_t) but efi_stage_firmware_fdt() takes
ulong * -- on builds where size_t and unsigned long are distinct this
will warn, and is arguably a strict-aliasing issue. Patch 3 declares a
separate ulong fw_fdt_size in bootmeth_efi.c for exactly this reason -
please do the same here rather than aliasing the distro-FDT size
variable. That also decouples the cleanup at 'out:', where the size
passed to efi_free_pages() only happens to be correct because
fdt_distro is NULL on the firmware-FDT path.
> diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c
> @@ -1320,13 +1323,27 @@ efi_status_t efi_bootmgr_run(void *fdt)
> + err = efi_stage_firmware_fdt(fdt_addr, &fdt_size, NULL);
Please pass a non-NULL namep so a failure to stage the firmware-owned
FDT can be logged with the FIT filename, matching what bootmeth_efi
does via bflow->fdt_fname
Regards,
Simon
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 2/5] boot: add a firmware-owned devicetree source
2026-07-28 13:20 ` [PATCH v2 2/5] boot: add a firmware-owned devicetree source Carlo Caione
2026-08-04 8:58 ` Ilias Apalodimas
@ 2026-08-15 18:44 ` Simon Glass
1 sibling, 0 replies; 17+ messages in thread
From: Simon Glass @ 2026-08-15 18:44 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Ilias Apalodimas,
Tom Rini, Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
Hi Carlo,
On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
> boot: 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. U-Boot has no generic way to
> source and assemble a devicetree from such a partition.
>
> Add firmware_fdt_load(), a loader for a FIT from a firmware-owned
> devicetree source. The FIT images hold the base DTB and its overlays,
> while each configuration names one valid combination through its standard
> 'fdt' property. Select an explicit 'fw_fdt_config' when set, otherwise
> use compatible best-match against the control devicetree and fall back to
> the FIT default.
>
> Describe the source with a standalone control-DT node. This patch
> implements the first source backend, 'u-boot,firmware-fdt-block', which
> reads the FIT from a filesystem on a GPT partition of a block device:
>
> firmware-fdt {
> compatible = "u-boot,firmware-fdt-block";
> [...]
>
> MAINTAINERS | 3 +
> boot/Kconfig | 24 ++
> boot/Makefile | 1 +
> boot/firmware_fdt.c | 420 ++++++++++++++++++++++++++++++
> boot/image-fdt.c | 3 +-
> boot/image-fit.c | 9 +-
> doc/develop/uefi/firmware_fdt.rst | 112 ++++++++
> doc/develop/uefi/index.rst | 1 +
> doc/device-tree-bindings/firmware-fdt.txt | 150 +++++++++++
> doc/usage/environment.rst | 11 +
> include/firmware_fdt.h | 95 +++++++
> include/image.h | 4 +-
> 12 files changed, 829 insertions(+), 4 deletions(-)
> diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
> @@ -0,0 +1,420 @@
> +static int fw_fdt_get_source(ofnode *srcp)
> +{
> + ofnode node;
> +
> + node = ofnode_by_compatible(ofnode_null(), FW_FDT_COMPAT_BLOCK);
> + while (ofnode_valid(node) && !ofnode_is_enabled(node))
> + node = ofnode_by_compatible(node, FW_FDT_COMPAT_BLOCK);
> +
> + if (!ofnode_valid(node))
> + return -ENOENT;
> +
> + *srcp = node;
> +
> + return 0;
> +}
If a control DT has two enabled u-boot,firmware-fdt-block nodes this
silently picks the first, which is traversal-order dependent and hard
to debug. Is your intention that only one is permitted?
Also, wouldn't it be better to make this a driver so that driver model
can handle discovery, binding, etc.?
> diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
> @@ -0,0 +1,420 @@
> + fdt = map_sysmem(data, len);
> +
> + out->fdt = fdt;
> + out->size = len;
> +
> + if (len > FIRMWARE_FDT_MAX_SIZE)
> + return log_msg_ret("bigfdt", -E2BIG);
> +
> + ret = fdt_check_full(fdt, len);
> + if (ret)
> + return log_msg_ret("chk", -EINVAL);
> +
> + out->name = strdup(fname);
Size and integrity checks belong before the assignments to out->fdt / out->size
> diff --git a/include/firmware_fdt.h b/include/firmware_fdt.h
> @@ -0,0 +1,95 @@
> +/* Maximum size of both the firmware FIT and the assembled devicetree */
> +#define FIRMWARE_FDT_MAX_SIZE SZ_4M
4 MiB is a policy decision - some platforms will want a smaller cap,
others may need more once overlays multiply. Please make this a
Kconfig knob (default SZ_4M) so boards can tune it without patching a
header. The name also implies an FDT-only limit while the comment says
it covers the FIT too; FIRMWARE_FDT_FIT_MAX_SIZE would be less
surprising.
> diff --git a/boot/firmware_fdt.c b/boot/firmware_fdt.c
> @@ -0,0 +1,420 @@
> + if (namep) {
> + name = strdup(fw.name);
> + if (!name) {
> + ret = -ENOMEM;
> + goto out;
> + }
> + }
fw.name is itself a strdup() of the FIT filename made inside
fw_fdt_assemble(); duplicating it a second time here just so
firmware_fdt_free() can free the original is wasteful. Consider
transferring ownership - pass fw.name out and clear it in the struct
so firmware_fdt_free() leaves it alone - or don't allocate fw.name
until it is needed.
> diff --git a/include/image.h b/include/image.h
> @@ -719,12 +719,14 @@ int boot_get_setup_fit(struct bootm_headers *images, uint8_t arch,
> + * @param ownedp Returns true if the loaded image is separately allocated
> + * and must be freed by the caller, or NULL
The "or NULL" trails 'must be freed by the caller' and reads as if the
returned image can be NULL. It refers to the parameter itself being
optional - please reword as a separate sentence, e.g. 'may be NULL if
the caller does not need this information'.
Regards,
Simon
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v2 5/5] test: boot: add firmware-FDT source tests
2026-07-28 13:20 ` [PATCH v2 5/5] test: boot: add firmware-FDT source tests Carlo Caione
@ 2026-08-15 18:45 ` Simon Glass
0 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2026-08-15 18:45 UTC (permalink / raw)
To: ccaione
Cc: u-boot, Ahmad Fatoum, David Lechner, Julien Masson,
Vitor Sato Eschholz, Heinrich Schuchardt, Ilias Apalodimas,
Tom Rini, Simon Glass, Quentin Schulz, Marek Vasut, Johan Jonker,
Randolph Sapp, Daniel Golle, James Hilliard, Aristo Chen,
Peng Fan, Vincent Jardin
Hi Carlo,
On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
> test: boot: add firmware-FDT source tests
>
> Add sandbox coverage for firmware_fdt_load(). The Python fixture creates
> mmc11.img in persistent_data_dir with A/B firmware partitions. Its first
> FAT partition carries FITs for a base-plus-overlay configuration,
> compatible best-match, external-data rejection and corrupt-hash rejection.
>
> Construct the complete MMC provider and firmware-FDT source topology in
> each flat-tree test, including the provider phandle. Nothing is added to
> the shared sandbox control devicetree, and the sandbox test framework
> restores its FDT snapshot even when an assertion fails.
>
> Cover default and explicit configuration selection, compatible best-match,
> owned and borrowed assembled-FDT storage, EFI staging and its returned
> filename, and the fail-closed cases: corrupt base or overlay data,
> configuration chaining, a missing configuration or partition, an invalid
> source phandle, external data and mismatched partition selectors. Verify
> that only a genuinely absent source returns -ENOENT.
>
> Signed-off-by: Carlo Caione <ccaione@baylibre.com>
>
> configs/sandbox_defconfig | 1 +
> test/boot/Makefile | 1 +
> test/boot/firmware_fdt.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++
> test/py/tests/test_ut.py | 181 ++++++++++++++++++++
> 4 files changed, 594 insertions(+)
> diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
> @@ -614,6 +614,186 @@ def setup_rauc_image(ubman):
> +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 (fdt.itb) carrying a base DTB and an overlay, with two
> + configurations: the default applies the overlay, 'conf-base' does not.
> + """
The docstring only mentions fdt.itb, but the fixture also builds
fdt-best.itb, fdt-ext.itb, fdt-corrupt.itb and
fdt-corrupt-overlay.itb. Please extend it so a reader knows the FAT
holds five FITs without digging through the body.
> diff --git a/test/boot/firmware_fdt.c b/test/boot/firmware_fdt.c
> @@ -0,0 +1,411 @@
> +/* A corrupted base fails hash verification and cannot fall back */
> +static int firmware_fdt_test_corrupt(struct unit_test_state *uts)
There is no coverage for the case where the source is configured, the
partition is found, but the named FIT file does not exist on the
filesystem. That drops through fs_size() in fw_fdt_read_fit() and is
remapped to -ENODEV by fw_fdt_load_source(); worth pinning that
behaviour so a future refactor cannot silently turn "FIT missing" back
into a fall-back -ENOENT. Please add one, e.g. by setting filename to
something not present on the FAT.
> diff --git a/test/boot/firmware_fdt.c b/test/boot/firmware_fdt.c
> @@ -0,0 +1,411 @@
> +/*
> + * Without a source node, -ENOENT is the only result which lets callers fall
> + * back. This also proves the provider alone does not configure the feature.
> + */
> +static int firmware_fdt_test_no_source(struct unit_test_state *uts)
> +{
> + struct firmware_fdt fw;
> +
> + ut_assertok(fwfdt_configure(uts, false));
> + ut_assertok(fwfdt_bind_mmc(uts));
> +
> + ut_asserteq(-ENOENT, firmware_fdt_load(&fw));
> + /* Source detection precedes address validation in the EFI helper */
> + ut_asserteq(-ENOENT, efi_stage_firmware_fdt(0, NULL, NULL));
> +
> + return 0;
> +}
This is the only test which omits the trailing fwfdt_clear_env() call.
Harmless here since nothing was set, but worth keeping the pattern
uniform so a future change that starts poking at the environment does
not leak between tests.
Regards,
Simon
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [v2,0/5] firmware-owned devicetree for EBBR / SystemReady IR
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
` (5 preceding siblings ...)
2026-07-29 7:10 ` [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Peter Robinson
@ 2026-08-15 18:45 ` Simon Glass
2026-08-24 14:48 ` Carlo Caione
6 siblings, 1 reply; 17+ messages in thread
From: Simon Glass @ 2026-08-15 18:45 UTC (permalink / raw)
To: ccaione; +Cc: u-boot
Hi Carlo,
On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
> Patch 1 makes an FDT passed to efi_bootmgr_run() outrank a Boot#### FDT,
> as an independent behaviour fix. Patch 2 adds the loader, binding,
> documentation and shared EFI staging policy. Patches 3 and 4 integrate
> the EFI bootmeth and boot manager. Patch 5 adds sandbox coverage.
[..]
> - 'fw_fdt_part' can pin an A/B partition and 'fw_fdt_config' can select
> an explicit configuration.
Just to check - is exposing these as environment variables really the
right long-term interface? U-Boot has been moving configuration into
the control devicetree, and both of these look like properties that
would sit naturally on the u-boot,firmware-fdt-block node, with the
env var overriding for A/B slot selection only. What do you think?
Regards,
Simon
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [v2,0/5] firmware-owned devicetree for EBBR / SystemReady IR
2026-08-15 18:45 ` [v2,0/5] " Simon Glass
@ 2026-08-24 14:48 ` Carlo Caione
2026-08-25 12:44 ` Simon Glass
0 siblings, 1 reply; 17+ messages in thread
From: Carlo Caione @ 2026-08-24 14:48 UTC (permalink / raw)
To: Simon Glass, ccaione; +Cc: u-boot
On Sat Aug 15, 2026 at 8:45 PM CEST, Simon Glass wrote:
> Hi Carlo,
>
> On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
>
>> Patch 1 makes an FDT passed to efi_bootmgr_run() outrank a Boot#### FDT,
>> as an independent behaviour fix. Patch 2 adds the loader, binding,
>> documentation and shared EFI staging policy. Patches 3 and 4 integrate
>> the EFI bootmeth and boot manager. Patch 5 adds sandbox coverage.
> [..]
>
>> - 'fw_fdt_part' can pin an A/B partition and 'fw_fdt_config' can select
>> an explicit configuration.
>
> Just to check - is exposing these as environment variables really the
> right long-term interface? U-Boot has been moving configuration into
> the control devicetree, and both of these look like properties that
> would sit naturally on the u-boot,firmware-fdt-block node, with the
> env var overriding for A/B slot selection only. What do you think?
Hi Simon,
Replying only to this point because all the other comments have my ACK.
Agreed regarding fw_fdt_part: it is indeed intended as the runtime A/B
slot override, while the stable storage location remains described by
the provider phandle and partition UUID/name in the control devicetree.
Now, fw_fdt_config is slightly different though. We need configuration
selection to depend on the selected boot target. For example in our
usecase, the same firmware FIT may provide a base-only configuration
for a generic distribution and an overlay configuration for a platform
image. The control devicetree is identical in both cases, so a fixed
property there cannot express that choice.
Static selection is already covered by compatible best-match and
the FIT default. I would retain fw_fdt_config only as an explicit
runtime override above those mechanisms and clarify that role in the
documentation if that is ok with you.
Cheers,
--
Carlo Caione
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [v2,0/5] firmware-owned devicetree for EBBR / SystemReady IR
2026-08-24 14:48 ` Carlo Caione
@ 2026-08-25 12:44 ` Simon Glass
0 siblings, 0 replies; 17+ messages in thread
From: Simon Glass @ 2026-08-25 12:44 UTC (permalink / raw)
To: Carlo Caione; +Cc: u-boot
Hi Carlo,
On Mon, 24 Aug 2026 at 08:48, Carlo Caione <ccaione@baylibre.com> wrote:
>
> On Sat Aug 15, 2026 at 8:45 PM CEST, Simon Glass wrote:
> > Hi Carlo,
> >
> > On 2026-07-28T13:20:42, Carlo Caione <ccaione@baylibre.com> wrote:
> >
> >> Patch 1 makes an FDT passed to efi_bootmgr_run() outrank a Boot#### FDT,
> >> as an independent behaviour fix. Patch 2 adds the loader, binding,
> >> documentation and shared EFI staging policy. Patches 3 and 4 integrate
> >> the EFI bootmeth and boot manager. Patch 5 adds sandbox coverage.
> > [..]
> >
> >> - 'fw_fdt_part' can pin an A/B partition and 'fw_fdt_config' can select
> >> an explicit configuration.
> >
> > Just to check - is exposing these as environment variables really the
> > right long-term interface? U-Boot has been moving configuration into
> > the control devicetree, and both of these look like properties that
> > would sit naturally on the u-boot,firmware-fdt-block node, with the
> > env var overriding for A/B slot selection only. What do you think?
>
> Hi Simon,
> Replying only to this point because all the other comments have my ACK.
>
> Agreed regarding fw_fdt_part: it is indeed intended as the runtime A/B
> slot override, while the stable storage location remains described by
> the provider phandle and partition UUID/name in the control devicetree.
>
> Now, fw_fdt_config is slightly different though. We need configuration
> selection to depend on the selected boot target. For example in our
> usecase, the same firmware FIT may provide a base-only configuration
> for a generic distribution and an overlay configuration for a platform
> image. The control devicetree is identical in both cases, so a fixed
> property there cannot express that choice.
>
> Static selection is already covered by compatible best-match and
> the FIT default. I would retain fw_fdt_config only as an explicit
> runtime override above those mechanisms and clarify that role in the
> documentation if that is ok with you.
Yes that seems reasonable to me, thanks for explaining.
Regards,
Simon
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-08-25 12:45 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-28 13:20 [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Carlo Caione
2026-07-28 13:20 ` [PATCH v2 1/5] efi_loader: bootmgr: preserve a passed devicetree Carlo Caione
2026-08-04 8:51 ` Ilias Apalodimas
2026-08-09 0:34 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 2/5] boot: add a firmware-owned devicetree source Carlo Caione
2026-08-04 8:58 ` Ilias Apalodimas
2026-08-15 18:44 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 3/5] bootmeth: efi: use the firmware-owned devicetree Carlo Caione
2026-08-15 18:44 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 4/5] efi_loader: bootmgr: install " Carlo Caione
2026-08-15 18:44 ` Simon Glass
2026-07-28 13:20 ` [PATCH v2 5/5] test: boot: add firmware-FDT source tests Carlo Caione
2026-08-15 18:45 ` Simon Glass
2026-07-29 7:10 ` [PATCH v2 0/5] firmware-owned devicetree for EBBR / SystemReady IR Peter Robinson
2026-08-15 18:45 ` [v2,0/5] " Simon Glass
2026-08-24 14:48 ` Carlo Caione
2026-08-25 12:44 ` Simon Glass
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox