summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-04-27 09:44:40 -0600
committerTom Rini <[email protected]>2026-04-27 09:44:40 -0600
commitd7b23b2477ee6e966b0f9b8ae304b0bf9df58297 (patch)
treee4c28ee4b77dc3182cda06cc3f66b2a196298dbd
parente0991f42aa7f973e09f38bbe60cd12b28a46e491 (diff)
parentd3935134c9d597b57cebb954615cf3b80eaf85d7 (diff)
Merge patch series "Add support for DT overlays handoff"
Raymond Mao <[email protected]> says: The series include refactoring on bloblist and fdtdec to support handoff of multiple DT overlays and applying them into the DT base during setup. All changes are aligned to the spec update for supporting DT overlay handoff[1]. Notes for testing: Currently DT overlay is not yet enabled in TF-A, but with the test patches I provided for TF-A and OP-TEE build, importing a DT overlay blob file from QEMU to TF-A reserved memory is supported. Follow below instructions to build and run for test: $ repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml Replace your local qemu_v8.xml with [2], which contains all necessary changes in both TF-A and OP-TEE build. $ repo sync $ cd build $ make toolchains $ make ARM_FIRMWARE_HANDOFF=y all Copy and rename your DT overlay blob as 'qemu_v8.dtb' into out/bin $ make ARM_FIRMWARE_HANDOFF=y run-only [1] Add Transfer Entry for Devicetree Overlay https://github.com/FirmwareHandoff/firmware_handoff/pull/74 [2] https://github.com/raymo200915/optee_manifest/blob/dt_overlay_handoff/qemu_v8.xml Link: https://lore.kernel.org/r/[email protected]
-rw-r--r--common/bloblist.c67
-rw-r--r--configs/qemu_arm64_defconfig1
-rw-r--r--configs/vexpress_fvp_bloblist_defconfig1
-rw-r--r--include/bloblist.h34
-rw-r--r--lib/fdtdec.c80
5 files changed, 181 insertions, 2 deletions
diff --git a/common/bloblist.c b/common/bloblist.c
index d5fa62249a9..afed968ce01 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -43,6 +43,7 @@ static struct tag_name {
{ BLOBLISTT_ACPI_TABLES, "ACPI tables for x86" },
{ BLOBLISTT_TPM_EVLOG, "TPM event log defined by TCG EFI" },
{ BLOBLISTT_TPM_CRB_BASE, "TPM Command Response Buffer address" },
+ { BLOBLISTT_FDT_OVERLAY, "DT overlay" },
/* BLOBLISTT_AREA_FIRMWARE */
{ BLOBLISTT_TPM2_TCG_LOG, "TPM v2 log space" },
@@ -96,6 +97,19 @@ static inline uint rec_tag(struct bloblist_rec *rec)
BLOBLISTR_TAG_SHIFT;
}
+static inline void void_blob(struct bloblist_rec *rec)
+{
+ if (rec_tag(rec) == BLOBLISTT_VOID)
+ return;
+ rec->tag_and_hdr_size = BLOBLISTT_VOID |
+ sizeof(*rec) << BLOBLISTR_HDR_SIZE_SHIFT;
+}
+
+static inline struct bloblist_rec *rec_from_blob(void *blob)
+{
+ return (blob - sizeof(struct bloblist_rec));
+}
+
static ulong bloblist_blob_end_ofs(struct bloblist_hdr *hdr,
struct bloblist_rec *rec)
{
@@ -222,6 +236,19 @@ static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size,
return 0;
}
+static int bloblist_get_blob_data_offset(uint tag)
+{
+ switch (tag) {
+ case BLOBLISTT_FDT_OVERLAY:
+ return sizeof(struct dto_blob_hdr);
+ /*
+ * return the data offset if it is not following the blob
+ * header immediately.
+ */
+ }
+ return 0;
+}
+
void *bloblist_find(uint tag, int size)
{
void *blob = NULL;
@@ -248,6 +275,44 @@ void *bloblist_get_blob(uint tag, int *sizep)
return (void *)rec + rec_hdr_size(rec);
}
+int bloblist_apply_blobs(uint tag, int (*func)(void **data, int size))
+{
+ struct bloblist_hdr *hdr = gd->bloblist;
+ struct bloblist_rec *rec;
+
+ if (!func || !hdr)
+ return -ENOENT;
+
+ foreach_rec(rec, hdr) {
+ /* Apply all blobs with the specified tag */
+ if (rec_tag(rec) == tag) {
+ int ret;
+ int tag = rec_tag(rec);
+ void *blob = (void *)rec + rec_hdr_size(rec);
+ int dat_off = bloblist_get_blob_data_offset(tag);
+
+ blob += dat_off;
+ ret = func(&blob, rec->size - dat_off);
+ if (ret) {
+ log_err("Failed to apply blob with tag %d\n",
+ tag);
+ return ret;
+ }
+
+ rec = rec_from_blob(blob - dat_off);
+ if (rec <= 0) {
+ log_err("Blob corrupted\n");
+ return -ENOENT;
+ }
+
+ /* Mark applied blob record as void */
+ void_blob(rec);
+ }
+ }
+
+ return 0;
+}
+
void *bloblist_add(uint tag, int size, int align_log2)
{
struct bloblist_rec *rec;
@@ -322,7 +387,7 @@ static int bloblist_resize_rec(struct bloblist_hdr *hdr,
next_ofs = bloblist_blob_end_ofs(hdr, rec);
if (next_ofs != hdr->used_size) {
memmove((void *)hdr + next_ofs + expand_by,
- (void *)hdr + next_ofs, new_alloced - next_ofs);
+ (void *)hdr + next_ofs, hdr->used_size - next_ofs);
}
hdr->used_size = new_alloced;
diff --git a/configs/qemu_arm64_defconfig b/configs/qemu_arm64_defconfig
index fd0bdad2d62..68b80e28746 100644
--- a/configs/qemu_arm64_defconfig
+++ b/configs/qemu_arm64_defconfig
@@ -6,6 +6,7 @@ CONFIG_CUSTOM_SYS_INIT_SP_ADDR=0x40200000
CONFIG_ENV_SIZE=0x40000
CONFIG_ENV_SECT_SIZE=0x40000
CONFIG_DEFAULT_DEVICE_TREE="qemu-arm64"
+CONFIG_OF_LIBFDT_OVERLAY=y
CONFIG_SYS_LOAD_ADDR=0x40200000
CONFIG_DEBUG_UART_BASE=0x9000000
CONFIG_DEBUG_UART_CLOCK=0
diff --git a/configs/vexpress_fvp_bloblist_defconfig b/configs/vexpress_fvp_bloblist_defconfig
index 4d52b96202b..6cd0365ec68 100644
--- a/configs/vexpress_fvp_bloblist_defconfig
+++ b/configs/vexpress_fvp_bloblist_defconfig
@@ -1,5 +1,6 @@
#include <configs/vexpress_fvp_defconfig>
+CONFIG_OF_LIBFDT_OVERLAY=y
CONFIG_BLOBLIST=y
CONFIG_BLOBLIST_PASSAGE_MANDATORY=y
CONFIG_BLOBLIST_SIZE_RELOC=0x10000
diff --git a/include/bloblist.h b/include/bloblist.h
index f32faf78560..e67b2a76358 100644
--- a/include/bloblist.h
+++ b/include/bloblist.h
@@ -73,6 +73,7 @@
#define __BLOBLIST_H
#include <mapmem.h>
+#include <linux/errno.h>
enum {
BLOBLIST_VERSION = 1,
@@ -110,7 +111,8 @@ enum bloblist_tag_t {
BLOBLISTT_ACPI_TABLES = 4,
BLOBLISTT_TPM_EVLOG = 5,
BLOBLISTT_TPM_CRB_BASE = 6,
- BLOBLISTT_ACPI_PP = 7,
+ BLOBLISTT_FDT_OVERLAY = 7,
+ BLOBLISTT_ACPI_PP = 8,
/* Standard area to allocate blobs used across firmware components */
BLOBLISTT_AREA_FIRMWARE = 0x10,
@@ -231,6 +233,16 @@ enum {
BLOBLIST_REC_HDR_SIZE = sizeof(struct bloblist_rec),
};
+/*
+ * struct dto_blob_hdr - Blob inline header for BLOBLISTT_FDT_OVERLAY
+ *
+ * @subtype: IMP-DEF per the agreement between the DT overlay producer and
+ * consumer. Default value is 0.
+ */
+struct dto_blob_hdr {
+ u64 subtype;
+};
+
/**
* bloblist_check_magic() - return a bloblist if the magic matches
*
@@ -268,6 +280,26 @@ static inline void *bloblist_get_blob(uint tag, int *sizep)
}
#endif
+#if CONFIG_IS_ENABLED(BLOBLIST)
+/**
+ * bloblist_apply_blobs() - Apply the data of blobs by tag
+ *
+ * Scan the bloblist, find the blobs with the matching tag and apply the data
+ * of blobs
+ *
+ * @tag: Tag to search for (enum bloblist_tag_t)
+ * @func: Function to apply the data of blobs
+ * Return: 0 if OK, otherwise error.
+ */
+int bloblist_apply_blobs(uint tag, int (*func)(void **data, int size));
+#else
+static inline int bloblist_apply_blobs(uint tag,
+ int (*func)(void **data, int size))
+{
+ return -EPERM;
+}
+#endif
+
/**
* bloblist_find() - Find a blob
*
diff --git a/lib/fdtdec.c b/lib/fdtdec.c
index 90d34ae105f..c6e13b6abef 100644
--- a/lib/fdtdec.c
+++ b/lib/fdtdec.c
@@ -1709,6 +1709,83 @@ void fdtdec_setup_embed(void)
gd->fdt_src = FDTSRC_EMBED;
}
+static int fdtdec_match_dto_compatible(const void *base, const void *dto)
+{
+ const char *compat_base;
+ const char *compat_dto;
+ int len;
+
+ compat_base = (const char *)fdt_getprop(base, 0, "compatible", &len);
+ if (!compat_base || len <= 0)
+ return -ENOENT;
+
+ compat_dto = (const char *)fdt_getprop(dto, 0, "compatible", &len);
+ if (!compat_dto || len <= 0)
+ return -ENOENT;
+
+ if (strcmp(compat_base, compat_dto))
+ return -EPERM;
+
+ return 0;
+}
+
+static int fdtdec_apply_dto_blob(void **blob, __maybe_unused int size)
+{
+ int ret;
+
+ ret = fdt_check_header(*blob);
+ if (ret)
+ return ret;
+
+ ret = fdtdec_match_dto_compatible(gd->fdt_blob, *blob);
+ if (ret)
+ return ret;
+
+ return fdt_overlay_apply_verbose((void *)gd->fdt_blob, *blob);
+}
+
+static int fdtdec_apply_bloblist_dtos(void)
+{
+ int ret;
+ struct fdt_header *live_fdt;
+ int blob_size;
+ size_t padded_size, max_size;
+
+ if (!CONFIG_IS_ENABLED(OF_LIBFDT_OVERLAY) ||
+ !CONFIG_IS_ENABLED(BLOBLIST))
+ return 0;
+
+ /* Get the total space reserved for FDT in blob */
+ live_fdt = bloblist_get_blob(BLOBLISTT_CONTROL_FDT, &blob_size);
+ if (live_fdt != gd->fdt_blob)
+ return -ENOENT;
+
+ /* Calculate the allowed padded size */
+ padded_size = fdt_totalsize(live_fdt) + CONFIG_SYS_FDT_PAD;
+ max_size = bloblist_get_total_size() - bloblist_get_size() + blob_size;
+ if (padded_size > max_size)
+ padded_size = max_size;
+
+ /* Resize if the current space is not sufficient */
+ if (blob_size < padded_size) {
+ ret = bloblist_resize(BLOBLISTT_CONTROL_FDT, padded_size);
+ if (ret)
+ return ret;
+
+ ret = fdt_open_into(live_fdt, live_fdt, padded_size);
+ if (ret)
+ return ret;
+ }
+
+ ret = bloblist_apply_blobs(BLOBLISTT_FDT_OVERLAY, fdtdec_apply_dto_blob);
+ if (ret)
+ return ret;
+
+ /* Shink the blob to the actual FDT size */
+ fdt_pack(live_fdt);
+ return bloblist_resize(BLOBLISTT_CONTROL_FDT, fdt_totalsize(live_fdt));
+}
+
int fdtdec_setup(void)
{
int ret = -ENOENT;
@@ -1730,6 +1807,9 @@ int fdtdec_setup(void)
gd->fdt_src = FDTSRC_BLOBLIST;
log_debug("Devicetree is in bloblist at %p\n",
gd->fdt_blob);
+ ret = fdtdec_apply_bloblist_dtos();
+ if (ret)
+ return ret;
goto setup_fdt;
} else {
log_debug("No FDT found in bloblist\n");