mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Johannes Schneider <johannes.schneider@leica-geosystems.com>
To: barebox@lists.infradead.org
Cc: Marco Felsch <m.felsch@pengutronix.de>,
	Johannes Schneider <johannes.schneider@leica-geosystems.com>
Subject: [PATCH v1 04/14] efi: loader: provide EFI_DT_FIXUP_PROTOCOL
Date: Sun,  4 Oct 2026 01:19:37 +0000	[thread overview]
Message-ID: <20261004011958.3255011-5-johannes.schneider@leica-geosystems.com> (raw)
In-Reply-To: <20261004011958.3255011-1-johannes.schneider@leica-geosystems.com>

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@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




  parent reply	other threads:[~2026-10-04  1:25 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-04  1:19 [PATCH v1 00/14] efi: loader: boot Authenticode-signed UKIs Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 01/14] mfd: hgs-efi: do not claim the name of the EFI loader's device Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 02/14] efi: loader: file: report EFI_UNSUPPORTED for volumes without a filesystem Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 03/14] efi: loader: bootm: free the devicetree after installing it Johannes Schneider
2026-10-04  1:19 ` Johannes Schneider [this message]
2026-10-04  1:19 ` [PATCH v1 05/14] efi: loader: pe: add helpers for the image size and a named section Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 06/14] efi: loader: bootm: load only the PE image, not the whole file Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 07/14] efi: loader: bootm: pass the kernel command line to UKIs Johannes Schneider
2026-10-05 17:18   ` Ahmad Fatoum
2026-10-04  1:19 ` [PATCH v1 08/14] efi: loader: verify Authenticode signatures against built-in keys Johannes Schneider
2026-10-05  5:33   ` Ahmad Fatoum
2026-10-05  5:45     ` SCHNEIDER Johannes
2026-10-09  0:07       ` SCHNEIDER Johannes
2026-10-04  1:19 ` [PATCH v1 09/14] efi: loader: authenticode: add a fuzz test Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 10/14] efi: loader: authenticate LoadImage() images when signing is forced Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 11/14] efi: loader: file: expose no filesystem when signed images are forced Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 12/14] bootm: efi: boot signed EFI images " Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 13/14] efi: loader: bootm: install a devicetree for matching UKI devicetrees Johannes Schneider
2026-10-04  1:19 ` [PATCH v1 14/14] efi: loader: bootm: apply overlays carried by a UKI Johannes Schneider

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261004011958.3255011-5-johannes.schneider@leica-geosystems.com \
    --to=johannes.schneider@leica-geosystems.com \
    --cc=barebox@lists.infradead.org \
    --cc=m.felsch@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox