[PATCH v1 04/14] efi: loader: provide EFI_DT_FIXUP_PROTOCOL

Johannes Schneider johannes.schneider at leica-geosystems.com
Sat Oct 3 18:19:37 PDT 2026


A payload that installs its own devicetree, like systemd-stub with a
UKI's .dtb or .dtbauto sections, replaces the one barebox installed and
with it every barebox fixup: machine compatible, serial number, OP-TEE
reservations, global overlays. systemd-stub asks the firmware to apply
them through EFI_DT_FIXUP_PROTOCOL, of which barebox only has the GUID.

Implement it on the root node:

- APPLY_FIXUPS runs of_fix_tree() on the payload's devicetree and
  returns EFI_BUFFER_TOO_SMALL with the required size if the result
  does not fit. The result is kept for the retry, so fixups and overlays
  run only once.
- RESERVE_MEMORY keeps boot-services allocations out of the memreserve
  entries and /reserved-memory nodes. The regions are requested as
  boot-services data, as the EFI loader cannot allocate
  EFI_RESERVED_TYPE; that suffices, because the kernel honours the
  devicetree reservations after ExitBootServices(). Regions barebox
  already holds, such as OP-TEE's, are skipped.
- INSTALL_TABLE installs the result as configuration table.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Johannes Schneider <johannes.schneider at leica-geosystems.com>
---
 efi/loader/protocols/Kconfig    |   9 ++
 efi/loader/protocols/Makefile   |   1 +
 efi/loader/protocols/dt_fixup.c | 185 ++++++++++++++++++++++++++++++++
 include/efi/protocol/dt_fixup.h |  21 ++++
 4 files changed, 216 insertions(+)
 create mode 100644 efi/loader/protocols/dt_fixup.c
 create mode 100644 include/efi/protocol/dt_fixup.h

diff --git a/efi/loader/protocols/Kconfig b/efi/loader/protocols/Kconfig
index ad7896065a..bc0d3adc8f 100644
--- a/efi/loader/protocols/Kconfig
+++ b/efi/loader/protocols/Kconfig
@@ -71,4 +71,13 @@ config EFI_LOADER_DEVICE_PATH_UTIL
 	  The device path utilities protocol creates and manipulates device
 	  paths and device nodes. It is required to run the EFI Shell.
 
+config EFI_LOADER_DT_FIXUP
+	bool "EFI_DT_FIXUP_PROTOCOL support"
+	depends on OFTREE
+	default y
+	help
+	  Provide EFI_DT_FIXUP_PROTOCOL, so that an EFI payload installing its
+	  own devicetree (like systemd-stub from a UKI's .dtb/.dtbauto section)
+	  gets the barebox devicetree fixups and overlays applied to it.
+
 endmenu
diff --git a/efi/loader/protocols/Makefile b/efi/loader/protocols/Makefile
index d0b55bde46..ffc86ce84d 100644
--- a/efi/loader/protocols/Makefile
+++ b/efi/loader/protocols/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_EFI_LOADER_UNICODE_COLLATION_PROTOCOL2) += unicode_collation.o
 obj-$(CONFIG_EFI_LOADER_RNG) += rng.o
 obj-$(CONFIG_EFI_LOADER_DEVICE_PATH_UTIL) += device_path_utilities.o
 obj-$(CONFIG_EFI_LOADER_DEVICE_PATH_TO_TEXT) += device_path_to_text.o
+obj-$(CONFIG_EFI_LOADER_DT_FIXUP) += dt_fixup.o
diff --git a/efi/loader/protocols/dt_fixup.c b/efi/loader/protocols/dt_fixup.c
new file mode 100644
index 0000000000..fe48d4a160
--- /dev/null
+++ b/efi/loader/protocols/dt_fixup.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EFI_DT_FIXUP_PROTOCOL: apply barebox's fixups and overlays to a devicetree
+ * an EFI payload brings itself
+ */
+
+#define pr_fmt(fmt) "efi-loader: dt-fixup: " fmt
+
+#include <common.h>
+#include <init.h>
+#include <malloc.h>
+#include <memory.h>
+#include <of.h>
+#include <linux/libfdt.h>
+#include <efi/loader.h>
+#include <efi/memory.h>
+#include <crc.h>
+#include <linux/err.h>
+#include <efi/protocol/dt_fixup.h>
+#include <efi/loader/table.h>
+#include <efi/loader/trace.h>
+#include <efi/guid.h>
+#include <efi/error.h>
+
+static void dt_fixup_reserve(u64 addr, u64 size)
+{
+	u64 start = ALIGN_DOWN(addr, EFI_PAGE_SIZE);
+	u64 len = ALIGN(addr + size, EFI_PAGE_SIZE) - start;
+
+	/* only needs to hold until ExitBootServices() */
+	if (!request_sdram_region_silent("dt-reserved", start, len,
+					 efi_memory_type_to_resource_type(EFI_BOOT_SERVICES_DATA),
+					 MEMATTRS_RW))
+		pr_debug("0x%llx+0x%llx already in use\n", addr, size);
+}
+
+/* Keep boot-services allocations out of what the devicetree reserves */
+static void dt_fixup_reserve_memory(const void *fdt)
+{
+	int i, node, sub, len, na, ns, entry;
+	u64 addr, size;
+
+	for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
+		if (!fdt_get_mem_rsv(fdt, i, &addr, &size) && size)
+			dt_fixup_reserve(addr, size);
+	}
+
+	node = fdt_path_offset(fdt, "/reserved-memory");
+	if (node < 0)
+		return;
+
+	na = fdt_address_cells(fdt, node);
+	ns = fdt_size_cells(fdt, node);
+	if (na < 1 || na > 2 || ns < 1 || ns > 2) {
+		pr_warn("/reserved-memory: unsupported #address-cells/#size-cells\n");
+		return;
+	}
+	entry = (na + ns) * sizeof(fdt32_t);
+
+	fdt_for_each_subnode(sub, fdt, node) {
+		const fdt32_t *reg = fdt_getprop(fdt, sub, "reg", &len);
+
+		if (!reg)
+			continue;
+
+		for (; len >= entry; len -= entry) {
+			addr = of_read_number(reg, na);
+			size = of_read_number(reg + na, ns);
+			reg += na + ns;
+			if (size)
+				dt_fixup_reserve(addr, size);
+		}
+	}
+}
+
+/* Kept for a caller retrying with a larger buffer, so fixups run only once */
+static struct {
+	void *fixed;
+	size_t in_len;
+	u32 in_crc;
+} dt_fixup_pending;
+
+static void *dt_fixup_apply(const void *dtb)
+{
+	size_t len = fdt_totalsize(dtb);
+	u32 crc = crc32(0, dtb, len);
+	struct device_node *root;
+	void *fixed;
+
+	if (dt_fixup_pending.fixed && dt_fixup_pending.in_len == len &&
+	    dt_fixup_pending.in_crc == crc) {
+		fixed = dt_fixup_pending.fixed;
+		dt_fixup_pending.fixed = NULL;
+		return fixed;
+	}
+
+	root = of_unflatten_dtb(dtb, len);
+	if (IS_ERR(root))
+		return ERR_CAST(root);
+
+	of_fix_tree(root);
+
+	fixed = of_flatten_dtb(root);
+	of_delete_node(root);
+	if (!fixed)
+		return ERR_PTR(-ENOMEM);
+
+	fdt_add_reserve_map(fixed);
+
+	free(dt_fixup_pending.fixed);
+	dt_fixup_pending.fixed = NULL;
+	dt_fixup_pending.in_len = len;
+	dt_fixup_pending.in_crc = crc;
+
+	return fixed;
+}
+
+static efi_status_t EFIAPI dt_fixup(struct efi_dt_fixup_protocol *this,
+				    void *dtb, size_t *buffer_size, u32 flags)
+{
+	efi_status_t ret = EFI_SUCCESS;
+	void *fixed = NULL;
+	size_t size;
+
+	EFI_ENTRY("%p, %p, %p, %u", this, dtb, buffer_size, flags);
+
+	if (!this || !dtb || !buffer_size || !flags || (flags & ~EFI_DT_ALL)) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	if (fdt_check_header(dtb)) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	if (flags & EFI_DT_APPLY_FIXUPS) {
+		fixed = dt_fixup_apply(dtb);
+		if (IS_ERR(fixed)) {
+			ret = PTR_ERR(fixed) == -ENOMEM ? EFI_OUT_OF_RESOURCES :
+							  EFI_INVALID_PARAMETER;
+			fixed = NULL;
+			goto out;
+		}
+
+		size = fdt_totalsize(fixed);
+		if (size > *buffer_size) {
+			/* the caller retries with a buffer of this size */
+			*buffer_size = size;
+			dt_fixup_pending.fixed = fixed;
+			fixed = NULL;
+			ret = EFI_BUFFER_TOO_SMALL;
+			goto out;
+		}
+
+		memcpy(dtb, fixed, size);
+	} else if (fdt_totalsize(dtb) > *buffer_size) {
+		*buffer_size = fdt_totalsize(dtb);
+		ret = EFI_BUFFER_TOO_SMALL;
+		goto out;
+	}
+
+	if (flags & EFI_DT_RESERVE_MEMORY)
+		dt_fixup_reserve_memory(dtb);
+
+	if (flags & EFI_DT_INSTALL_TABLE)
+		ret = efi_install_configuration_table(&efi_fdt_guid, dtb);
+
+out:
+	free(fixed);
+	return EFI_EXIT(ret);
+}
+
+static struct efi_dt_fixup_protocol efi_dt_fixup_prot = {
+	.revision = EFI_DT_FIXUP_PROTOCOL_REVISION,
+	.fixup = dt_fixup,
+};
+
+static int efi_dt_fixup_init(void)
+{
+	efi_add_root_node_protocol_deferred(&efi_dt_fixup_protocol_guid,
+					    &efi_dt_fixup_prot);
+	return 0;
+}
+device_initcall(efi_dt_fixup_init);
diff --git a/include/efi/protocol/dt_fixup.h b/include/efi/protocol/dt_fixup.h
new file mode 100644
index 0000000000..fd418793f6
--- /dev/null
+++ b/include/efi/protocol/dt_fixup.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __EFI_PROTOCOL_DT_FIXUP_H_
+#define __EFI_PROTOCOL_DT_FIXUP_H_
+
+#include <efi/types.h>
+
+#define EFI_DT_FIXUP_PROTOCOL_REVISION 0x00010000
+
+#define EFI_DT_APPLY_FIXUPS	0x00000001
+#define EFI_DT_RESERVE_MEMORY	0x00000002
+#define EFI_DT_INSTALL_TABLE	0x00000004
+#define EFI_DT_ALL		(EFI_DT_APPLY_FIXUPS | EFI_DT_RESERVE_MEMORY | \
+				 EFI_DT_INSTALL_TABLE)
+
+struct efi_dt_fixup_protocol {
+	u64 revision;
+	efi_status_t (EFIAPI *fixup)(struct efi_dt_fixup_protocol *this,
+				     void *dtb, size_t *buffer_size, u32 flags);
+};
+
+#endif
-- 
2.43.0




More information about the barebox mailing list