From 1c4751fd1ac7bda72ab72ce352dc9b2131df2807 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:20 -0800 Subject: bloblist: add API to check the register conventions Add bloblist_check_reg_conv() to check whether the bloblist is compliant to the register conventions defined in Firmware Handoff specification. This API can be used for all Arm platforms. Signed-off-by: Raymond Mao --- common/bloblist.c | 11 +++++++++++ include/bloblist.h | 20 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/common/bloblist.c b/common/bloblist.c index 2d373910b6d..980b1ddbcb4 100644 --- a/common/bloblist.c +++ b/common/bloblist.c @@ -542,3 +542,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/include/bloblist.h b/include/bloblist.h index 84fc9438191..f7e800f6812 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, @@ -461,4 +468,17 @@ 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); + #endif /* __BLOBLIST_H */ -- cgit v1.2.3 From 67254214930cd2cb52279b01690c1f820a7f83db Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:21 -0800 Subject: bloblist: check bloblist with specified buffer size Instead of expecting the bloblist total size to be the same as the pre-allocated buffer size, practically we are more interested in whether the pre-allocated buffer size is bigger than the bloblist total size. Signed-off-by: Raymond Mao Reviewed-by: Ilias Apalodimas --- common/bloblist.c | 2 +- include/bloblist.h | 9 +++++---- test/bloblist.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/bloblist.c b/common/bloblist.c index 980b1ddbcb4..26b0ba33b11 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); diff --git a/include/bloblist.h b/include/bloblist.h index f7e800f6812..cc78259e5a9 100644 --- a/include/bloblist.h +++ b/include/bloblist.h @@ -348,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); diff --git a/test/bloblist.c b/test/bloblist.c index 17d9dd03d07..7dab9addf82 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++; -- cgit v1.2.3 From 1ef43f3bf20a4a9508c299a738d01faa3dc3ce95 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:22 -0800 Subject: bloblist: refactor of bloblist_reloc() The current bloblist pointer and size can be retrieved from global data, so we don't need to pass them from the function arguments. This change also help to remove all external access of gd->bloblist outside of bloblist module. Signed-off-by: Raymond Mao Reviewed-by: Ilias Apalodimas --- common/bloblist.c | 10 ++++++++-- common/board_f.c | 9 +++------ include/bloblist.h | 8 ++++---- test/bloblist.c | 6 ++---- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/common/bloblist.c b/common/bloblist.c index 26b0ba33b11..c2fd07575fb 100644 --- a/common/bloblist.c +++ b/common/bloblist.c @@ -472,13 +472,19 @@ 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; } int bloblist_init(void) 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/include/bloblist.h b/include/bloblist.h index cc78259e5a9..0ae079d62aa 100644 --- a/include/bloblist.h +++ b/include/bloblist.h @@ -426,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 diff --git a/test/bloblist.c b/test/bloblist.c index 7dab9addf82..1c60bbac36c 100644 --- a/test/bloblist.c +++ b/test/bloblist.c @@ -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)); -- cgit v1.2.3 From 5103e69344d60eb8c8d2cf804ec00d0ef3975124 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:23 -0800 Subject: arm: armv7: save boot arguments Save boot arguments r[0-3] into an array for handover of bloblist from previous boot stage. Signed-off-by: Raymond Mao Suggested-by: Ard Biesheuvel Reviewed-by: Ilias Apalodimas --- arch/arm/cpu/armv7/start.S | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) 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 -- cgit v1.2.3 From 11f3171256337dfb81ead526bb2dd78503839882 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:24 -0800 Subject: arm: armv8: save boot arguments Save boot arguments x[0-3] into an array for handover of bloblist from previous boot stage. Signed-off-by: Raymond Mao Suggested-by: Ard Biesheuvel Reviewed-by: Ilias Apalodimas --- arch/arm/cpu/armv8/start.S | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) 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 -- cgit v1.2.3 From fc61de3ff66053e517946b2aee8ada9f2d510edc Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:25 -0800 Subject: arm: Get bloblist from boot arguments Add arch custom function to get bloblist from boot arguments. Check whether boot arguments aligns with the register conventions defined in FW Handoff spec v0.9. Signed-off-by: Raymond Mao --- arch/arm/lib/Makefile | 2 ++ arch/arm/lib/xferlist.c | 25 +++++++++++++++++++++++++ arch/arm/lib/xferlist.h | 19 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 arch/arm/lib/xferlist.c create mode 100644 arch/arm/lib/xferlist.h 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 + */ +#include +#include +#include +#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 + */ + +#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_ */ -- cgit v1.2.3 From 66131310d8ff1ba228f989b41bd8812f43be41c3 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:26 -0800 Subject: bloblist: Load the bloblist from the previous loader During bloblist initialization, load the bloblist via boot arguments from the previous loader. If a valid bloblist exists in boot arguments, relocate it into the fixed bloblist memory region. If not, fallback to support BLOBLIST_ADDR or BLOBLIST_ALLOC. Signed-off-by: Raymond Mao --- common/bloblist.c | 62 ++++++++++++++++++++++++++++++++++++++---------------- include/bloblist.h | 10 +++++++++ 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/common/bloblist.c b/common/bloblist.c index c2fd07575fb..ad06d7a1795 100644 --- a/common/bloblist.c +++ b/common/bloblist.c @@ -487,37 +487,57 @@ int bloblist_reloc(void *to, uint to_size) 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) { 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); @@ -525,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); @@ -538,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; } diff --git a/include/bloblist.h b/include/bloblist.h index 0ae079d62aa..7fbdd622bcf 100644 --- a/include/bloblist.h +++ b/include/bloblist.h @@ -482,4 +482,14 @@ static inline int bloblist_maybe_init(void) */ 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 */ -- cgit v1.2.3 From 2b71470628c035ce3ccc76d12e50bdc453bdbc93 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Sat, 3 Feb 2024 08:36:27 -0800 Subject: dts: OF_HAS_PRIOR_STAGE should depend on !BLOBLIST When BLOBLIST is enabled, FDT is expected to be from bloblist carried from previous stage, instead of from OF_BOARD, therefore only enable OF_HAS_PRIOR_STAGE when BLOBLIST is disabled. Signed-off-by: Raymond Mao --- dts/Kconfig | 1 + 1 file changed, 1 insertion(+) 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 -- cgit v1.2.3