diff options
-rw-r--r-- | arch/arm/cpu/armv7/start.S | 29 | ||||
-rw-r--r-- | arch/arm/cpu/armv8/start.S | 23 | ||||
-rw-r--r-- | arch/arm/lib/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/lib/xferlist.c | 25 | ||||
-rw-r--r-- | arch/arm/lib/xferlist.h | 19 | ||||
-rw-r--r-- | common/bloblist.c | 85 | ||||
-rw-r--r-- | common/board_f.c | 9 | ||||
-rw-r--r-- | dts/Kconfig | 1 | ||||
-rw-r--r-- | include/bloblist.h | 47 | ||||
-rw-r--r-- | test/bloblist.c | 8 |
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)); |