aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/cpu/armv7/start.S29
-rw-r--r--arch/arm/cpu/armv8/start.S23
-rw-r--r--arch/arm/lib/Makefile2
-rw-r--r--arch/arm/lib/xferlist.c25
-rw-r--r--arch/arm/lib/xferlist.h19
-rw-r--r--common/bloblist.c85
-rw-r--r--common/board_f.c9
-rw-r--r--dts/Kconfig1
-rw-r--r--include/bloblist.h47
-rw-r--r--test/bloblist.c8
10 files changed, 208 insertions, 40 deletions
diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S
index 69e281b086a..7730a16e512 100644
--- a/arch/arm/cpu/armv7/start.S
+++ b/arch/arm/cpu/armv7/start.S
@@ -152,9 +152,38 @@ ENDPROC(c_runtime_cpu_setup)
*
*************************************************************************/
WEAK(save_boot_params)
+#if (IS_ENABLED(CONFIG_BLOBLIST))
+ /* Calculate the PC-relative address of saved_args */
+ adr r12, saved_args_offset
+ ldr r13, saved_args_offset
+ add r12, r12, r13
+
+ /*
+ * Intentionally swapping r0 with r2 in order to simplify the C
+ * function we use later.
+ */
+ str r2, [r12]
+ str r1, [r12, #4]
+ str r0, [r12, #8]
+ str r3, [r12, #12]
+#endif
b save_boot_params_ret @ back to my caller
ENDPROC(save_boot_params)
+#if (IS_ENABLED(CONFIG_BLOBLIST))
+saved_args_offset:
+ .long saved_args - . /* offset from current code to save_args */
+
+ .section .data
+ .align 2
+ .global saved_args
+saved_args:
+ .rept 4
+ .word 0
+ .endr
+END(saved_args)
+#endif
+
#ifdef CONFIG_ARMV7_LPAE
WEAK(switch_to_hypervisor)
b switch_to_hypervisor_ret
diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S
index 6cc1d26e5e2..74612802617 100644
--- a/arch/arm/cpu/armv8/start.S
+++ b/arch/arm/cpu/armv8/start.S
@@ -370,5 +370,28 @@ ENTRY(c_runtime_cpu_setup)
ENDPROC(c_runtime_cpu_setup)
WEAK(save_boot_params)
+#if (IS_ENABLED(CONFIG_BLOBLIST))
+ /* Calculate the PC-relative address of saved_args */
+ adr x9, saved_args_offset
+ ldr w10, saved_args_offset
+ add x9, x9, w10, sxtw
+
+ stp x0, x1, [x9]
+ stp x2, x3, [x9, #16]
+#endif
b save_boot_params_ret /* back to my caller */
ENDPROC(save_boot_params)
+
+#if (IS_ENABLED(CONFIG_BLOBLIST))
+saved_args_offset:
+ .long saved_args - . /* offset from current code to save_args */
+
+ .section .data
+ .align 2
+ .global saved_args
+saved_args:
+ .rept 4
+ .dword 0
+ .endr
+END(saved_args)
+#endif
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index b1bcd374662..67275fba616 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -85,6 +85,8 @@ obj-y += psci-dt.o
obj-$(CONFIG_DEBUG_LL) += debug.o
+obj-$(CONFIG_BLOBLIST) += xferlist.o
+
# For EABI conformant tool chains, provide eabi_compat()
ifneq (,$(findstring -mabi=aapcs-linux,$(PLATFORM_CPPFLAGS)))
extra-y += eabi_compat.o
diff --git a/arch/arm/lib/xferlist.c b/arch/arm/lib/xferlist.c
new file mode 100644
index 00000000000..f9c5d88bd47
--- /dev/null
+++ b/arch/arm/lib/xferlist.c
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Linaro Limited
+ * Author: Raymond Mao <raymond.mao@linaro.org>
+ */
+#include <linux/types.h>
+#include <errno.h>
+#include <bloblist.h>
+#include "xferlist.h"
+
+int xferlist_from_boot_arg(ulong addr, ulong size)
+{
+ int ret;
+
+ ret = bloblist_check(saved_args[3], size);
+ if (ret)
+ return ret;
+
+ ret = bloblist_check_reg_conv(saved_args[0], saved_args[2],
+ saved_args[1]);
+ if (ret)
+ return ret;
+
+ return bloblist_reloc((void *)addr, size);
+}
diff --git a/arch/arm/lib/xferlist.h b/arch/arm/lib/xferlist.h
new file mode 100644
index 00000000000..60d79c1a8eb
--- /dev/null
+++ b/arch/arm/lib/xferlist.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0+ BSD-3-Clause */
+/*
+ * Copyright (C) 2023 Linaro Limited
+ * Author: Raymond Mao <raymond.mao@linaro.org>
+ */
+
+#ifndef _XFERLIST_H_
+#define _XFERLIST_H_
+
+/*
+ * Boot parameters saved from start.S
+ * saved_args[0]: FDT base address
+ * saved_args[1]: Bloblist signature
+ * saved_args[2]: must be 0
+ * saved_args[3]: Bloblist base address
+ */
+extern unsigned long saved_args[];
+
+#endif /* _XFERLIST_H_ */
diff --git a/common/bloblist.c b/common/bloblist.c
index 2d373910b6d..ad06d7a1795 100644
--- a/common/bloblist.c
+++ b/common/bloblist.c
@@ -384,7 +384,7 @@ int bloblist_check(ulong addr, uint size)
return log_msg_ret("Bad magic", -ENOENT);
if (hdr->version != BLOBLIST_VERSION)
return log_msg_ret("Bad version", -EPROTONOSUPPORT);
- if (!hdr->total_size || (size && hdr->total_size != size))
+ if (!hdr->total_size || (size && hdr->total_size > size))
return log_msg_ret("Bad total size", -EFBIG);
if (hdr->used_size > hdr->total_size)
return log_msg_ret("Bad used size", -ENOENT);
@@ -472,13 +472,28 @@ void bloblist_show_list(void)
}
}
-void bloblist_reloc(void *to, uint to_size, void *from, uint from_size)
+int bloblist_reloc(void *to, uint to_size)
{
struct bloblist_hdr *hdr;
- memcpy(to, from, from_size);
+ if (to_size < gd->bloblist->total_size)
+ return -ENOSPC;
+
+ memcpy(to, gd->bloblist, gd->bloblist->total_size);
hdr = to;
hdr->total_size = to_size;
+ gd->bloblist = to;
+
+ return 0;
+}
+
+/*
+ * Weak default function for getting bloblist from boot args.
+ */
+int __weak xferlist_from_boot_arg(ulong __always_unused addr,
+ ulong __always_unused size)
+{
+ return -ENOENT;
}
int bloblist_init(void)
@@ -486,32 +501,43 @@ int bloblist_init(void)
bool fixed = IS_ENABLED(CONFIG_BLOBLIST_FIXED);
int ret = -ENOENT;
ulong addr, size;
- bool expected;
-
- /**
- * We don't expect to find an existing bloblist in the first phase of
- * U-Boot that runs. Also we have no way to receive the address of an
- * allocated bloblist from a previous stage, so it must be at a fixed
+ /*
+ * If U-Boot is not in the first phase, an existing bloblist must be
+ * at a fixed address.
+ */
+ bool from_addr = fixed && !u_boot_first_phase();
+ /*
+ * If U-Boot is in the first phase that an arch custom routine should
+ * install the bloblist passed from previous loader to this fixed
* address.
*/
- expected = fixed && !u_boot_first_phase();
+ bool from_boot_arg = fixed && u_boot_first_phase();
+
if (spl_prev_phase() == PHASE_TPL && !IS_ENABLED(CONFIG_TPL_BLOBLIST))
- expected = false;
+ from_addr = false;
if (fixed)
addr = IF_ENABLED_INT(CONFIG_BLOBLIST_FIXED,
CONFIG_BLOBLIST_ADDR);
size = CONFIG_BLOBLIST_SIZE;
- if (expected) {
+
+ if (from_boot_arg)
+ ret = xferlist_from_boot_arg(addr, size);
+ else if (from_addr)
ret = bloblist_check(addr, size);
- if (ret) {
- log_warning("Expected bloblist at %lx not found (err=%d)\n",
- addr, ret);
- } else {
- /* Get the real size, if it is not what we expected */
- size = gd->bloblist->total_size;
- }
- }
+
+ if (ret)
+ log_warning("Bloblist at %lx not found (err=%d)\n",
+ addr, ret);
+ else
+ /* Get the real size */
+ size = gd->bloblist->total_size;
+
if (ret) {
+ /*
+ * If we don't have a bloblist from a fixed address, or the one
+ * in the fixed address is not valid. we must allocate the
+ * memory for it now.
+ */
if (CONFIG_IS_ENABLED(BLOBLIST_ALLOC)) {
void *ptr = memalign(BLOBLIST_ALIGN, size);
@@ -519,7 +545,8 @@ int bloblist_init(void)
return log_msg_ret("alloc", -ENOMEM);
addr = map_to_sysmem(ptr);
} else if (!fixed) {
- return log_msg_ret("!fixed", ret);
+ return log_msg_ret("BLOBLIST_FIXED is not enabled",
+ ret);
}
log_debug("Creating new bloblist size %lx at %lx\n", size,
addr);
@@ -532,6 +559,11 @@ int bloblist_init(void)
return log_msg_ret("ini", ret);
gd->flags |= GD_FLG_BLOBLIST_READY;
+#ifdef DEBUG
+ bloblist_show_stats();
+ bloblist_show_list();
+#endif
+
return 0;
}
@@ -542,3 +574,14 @@ int bloblist_maybe_init(void)
return 0;
}
+
+int bloblist_check_reg_conv(ulong rfdt, ulong rzero, ulong rsig)
+{
+ if (rzero || rsig != (BLOBLIST_MAGIC | BLOBLIST_REGCONV_VER) ||
+ rfdt != (ulong)bloblist_find(BLOBLISTT_CONTROL_FDT, 0)) {
+ gd->bloblist = NULL; /* Reset the gd bloblist pointer */
+ return -EIO;
+ }
+
+ return 0;
+}
diff --git a/common/board_f.c b/common/board_f.c
index 442b8349d08..7e313691028 100644
--- a/common/board_f.c
+++ b/common/board_f.c
@@ -706,13 +706,10 @@ static int reloc_bloblist(void)
return 0;
}
if (gd->new_bloblist) {
- int size = CONFIG_BLOBLIST_SIZE;
-
debug("Copying bloblist from %p to %p, size %x\n",
- gd->bloblist, gd->new_bloblist, size);
- bloblist_reloc(gd->new_bloblist, CONFIG_BLOBLIST_SIZE_RELOC,
- gd->bloblist, size);
- gd->bloblist = gd->new_bloblist;
+ gd->bloblist, gd->new_bloblist, gd->bloblist->total_size);
+ return bloblist_reloc(gd->new_bloblist,
+ CONFIG_BLOBLIST_SIZE_RELOC);
}
#endif
diff --git a/dts/Kconfig b/dts/Kconfig
index 00c0aeff893..def0e172412 100644
--- a/dts/Kconfig
+++ b/dts/Kconfig
@@ -121,6 +121,7 @@ config OF_BOARD
config OF_HAS_PRIOR_STAGE
bool
+ depends on !BLOBLIST
help
Indicates that a prior stage of the firmware (before U-Boot proper)
makes use of device tree and this board normally boots with that prior
diff --git a/include/bloblist.h b/include/bloblist.h
index 84fc9438191..7fbdd622bcf 100644
--- a/include/bloblist.h
+++ b/include/bloblist.h
@@ -78,6 +78,13 @@ enum {
BLOBLIST_VERSION = 1,
BLOBLIST_MAGIC = 0x4a0fb10b,
+ /*
+ * FIXME:
+ * Register convention version should be placed into a higher byte
+ * https://github.com/FirmwareHandoff/firmware_handoff/issues/32
+ */
+ BLOBLIST_REGCONV_VER = 1 << 24,
+
BLOBLIST_BLOB_ALIGN_LOG2 = 3,
BLOBLIST_BLOB_ALIGN = 1 << BLOBLIST_BLOB_ALIGN_LOG2,
@@ -341,12 +348,13 @@ int bloblist_new(ulong addr, uint size, uint flags, uint align_log2);
* bloblist_check() - Check if a bloblist exists
*
* @addr: Address of bloblist
- * @size: Expected size of blobsize, or 0 to detect the size
+ * @size: Reserved space size for blobsize, or 0 to use the total size
* Return: 0 if OK, -ENOENT if the magic number doesn't match (indicating that
- * there problem is no bloblist at the given address), -EPROTONOSUPPORT
+ * there problem is no bloblist at the given address) or any fields for header
+ * size, used size and total size do not match, -EPROTONOSUPPORT
* if the version does not match, -EIO if the checksum does not match,
- * -EFBIG if the expected size does not match the detected size, -ENOSPC
- * if the size is not large enough to hold the headers
+ * -EFBIG if the reserved space size is small than the total size or total size
+ * is 0
*/
int bloblist_check(ulong addr, uint size);
@@ -418,11 +426,11 @@ const char *bloblist_tag_name(enum bloblist_tag_t tag);
* bloblist_reloc() - Relocate the bloblist and optionally resize it
*
* @to: Pointer to new bloblist location (must not overlap old location)
- * @to_size: New size for bloblist (must be larger than from_size)
- * @from: Pointer to bloblist to relocate
- * @from_size: Size of bloblist to relocate
+ * @to_size: New size for bloblist
+ * Return: 0 if OK, -ENOSPC if the new size is small than the bloblist total
+ * size.
*/
-void bloblist_reloc(void *to, uint to_size, void *from, uint from_size);
+int bloblist_reloc(void *to, uint to_size);
/**
* bloblist_init() - Init the bloblist system with a single bloblist
@@ -461,4 +469,27 @@ static inline int bloblist_maybe_init(void)
}
#endif /* BLOBLIST */
+/**
+ * bloblist_check_reg_conv() - Check whether the bloblist is compliant to
+ * the register conventions according to the
+ * Firmware Handoff spec.
+ *
+ * @rfdt: Register that holds the FDT base address.
+ * @rzero: Register that must be zero.
+ * @rsig: Register that holds signature and register conventions version.
+ * Return: 0 if OK, -EIO if the bloblist is not compliant to the register
+ * conventions.
+ */
+int bloblist_check_reg_conv(ulong rfdt, ulong rzero, ulong rsig);
+
+/**
+ * xferlist_from_boot_arg() - Get bloblist from the boot args and relocate it
+ * to the specified address.
+ *
+ * @addr: Address for the bloblist
+ * @size: Size of space reserved for the bloblist
+ * Return: 0 if OK, else on error
+ */
+int xferlist_from_boot_arg(ulong addr, ulong size);
+
#endif /* __BLOBLIST_H */
diff --git a/test/bloblist.c b/test/bloblist.c
index 17d9dd03d07..1c60bbac36c 100644
--- a/test/bloblist.c
+++ b/test/bloblist.c
@@ -207,7 +207,7 @@ static int bloblist_test_checksum(struct unit_test_state *uts)
hdr->flags++;
hdr->total_size--;
- ut_asserteq(-EFBIG, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
hdr->total_size++;
hdr->spare++;
@@ -376,13 +376,12 @@ static int bloblist_test_reloc(struct unit_test_state *uts)
{
const uint large_size = TEST_BLOBLIST_SIZE;
const uint small_size = 0x20;
- void *old_ptr, *new_ptr;
+ void *new_ptr;
void *blob1, *blob2;
ulong new_addr;
ulong new_size;
ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
- old_ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
/* Add one blob and then one that won't fit */
blob1 = bloblist_add(TEST_TAG, small_size, 0);
@@ -394,8 +393,7 @@ static int bloblist_test_reloc(struct unit_test_state *uts)
new_addr = TEST_ADDR + TEST_BLOBLIST_SIZE;
new_size = TEST_BLOBLIST_SIZE + 0x100;
new_ptr = map_sysmem(new_addr, TEST_BLOBLIST_SIZE);
- bloblist_reloc(new_ptr, new_size, old_ptr, TEST_BLOBLIST_SIZE);
- gd->bloblist = new_ptr;
+ ut_assertok(bloblist_reloc(new_ptr, new_size));
/* Check the old blob is there and that we can now add the bigger one */
ut_assertnonnull(bloblist_find(TEST_TAG, small_size));