From 3ba2c15147cc0c86342a443cd0cbfab3d2931c06 Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Tue, 25 Jul 2023 07:53:35 -0700 Subject: [PATCH 1/2] feat(handoff): introduce firmware handoff library Add transfer list APIs and firmware handoff build option. Change-Id: I68a0ace22c7e50fcdacd101eb76b271d7b76d8ff Signed-off-by: Raymond Mao --- Makefile | 6 + changelog.yaml | 3 + docs/getting_started/build-options.rst | 6 + include/lib/transfer_list.h | 114 ++++++ include/lib/utils_def.h | 37 +- lib/transfer_list/transfer_list.c | 474 +++++++++++++++++++++++++ lib/transfer_list/transfer_list.mk | 20 ++ make_helpers/defaults.mk | 3 + 8 files changed, 662 insertions(+), 1 deletion(-) create mode 100644 include/lib/transfer_list.h create mode 100644 lib/transfer_list/transfer_list.c create mode 100644 lib/transfer_list/transfer_list.mk diff --git a/Makefile b/Makefile index 1cce234a7..104d84dc5 100644 --- a/Makefile +++ b/Makefile @@ -1027,6 +1027,10 @@ ifeq ($(DRTM_SUPPORT),1) $(info DRTM_SUPPORT is an experimental feature) endif +ifeq (${TRANSFER_LIST},1) + $(info TRANSFER_LIST is an experimental feature) +endif + ifeq (${ENABLE_RME},1) ifneq (${SEPARATE_CODE_AND_RODATA},1) $(error `ENABLE_RME=1` requires `SEPARATE_CODE_AND_RODATA=1`) @@ -1191,6 +1195,7 @@ $(eval $(call assert_booleans,\ SPMC_AT_EL3 \ SPMD_SPM_AT_SEL2 \ ENABLE_SPMD_LP \ + TRANSFER_LIST \ TRUSTED_BOARD_BOOT \ USE_COHERENT_MEM \ USE_DEBUGFS \ @@ -1351,6 +1356,7 @@ $(eval $(call add_defines,\ SPM_MM \ SPMC_AT_EL3 \ SPMD_SPM_AT_SEL2 \ + TRANSFER_LIST \ TRUSTED_BOARD_BOOT \ CRYPTO_SUPPORT \ TRNG_SUPPORT \ diff --git a/changelog.yaml b/changelog.yaml index cdbedbb34..0d69fb383 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -784,6 +784,9 @@ subsections: - title: Semihosting scope: semihosting + - title: Firmware Handoff + scope: handoff + - title: Drivers subsections: diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst index 2c018c394..bc939e148 100644 --- a/docs/getting_started/build-options.rst +++ b/docs/getting_started/build-options.rst @@ -944,6 +944,11 @@ Common build options hardware will limit the effective VL to the maximum physically supported VL. +- ``TRANSFER_LIST``: Setting this to ``1`` enables support for Firmware + Handoff using Transfer List defined in `Firmware Handoff specification`_. + This defaults to ``0``. Please note that this is an experimental feature + based on Firmware Handoff specification v0.9. + - ``TRNG_SUPPORT``: Setting this to ``1`` enables support for True Random Number Generator Interface to BL31 image. This defaults to ``0``. @@ -1298,3 +1303,4 @@ Firmware update options .. _PSA DRTM specification: https://developer.arm.com/documentation/den0113/a .. _GCC: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html .. _Clang: https://clang.llvm.org/docs/DiagnosticsReference.html +.. _Firmware Handoff specification: https://github.com/FirmwareHandoff/firmware_handoff/releases/tag/v0.9 diff --git a/include/lib/transfer_list.h b/include/lib/transfer_list.h new file mode 100644 index 000000000..54c864347 --- /dev/null +++ b/include/lib/transfer_list.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __TRANSFER_LIST_H +#define __TRANSFER_LIST_H + +#include +#include + +#include + +#define TRANSFER_LIST_SIGNATURE U(0x006ed0ff) +#define TRANSFER_LIST_VERSION U(0x0001) + +// Init value of maximum alignment required by any TE data in the TL +// specified as a power of two +#define TRANSFER_LIST_INIT_MAX_ALIGN U(3) + +// alignment required by TE header start address, in bytes +#define TRANSFER_LIST_GRANULE U(8) + +// version of the register convention used. +// Set to 1 for both AArch64 and AArch32 according to fw handoff spec v0.9 +#define REGISTER_CONVENTION_VERSION_MASK (1 << 24) + +#ifndef __ASSEMBLER__ + +enum transfer_list_tag_id { + TL_TAG_EMPTY = 0, + TL_TAG_FDT = 1, + TL_TAG_HOB_BLOCK = 2, + TL_TAG_HOB_LIST = 3, + TL_TAG_ACPI_TABLE_AGGREGATE = 4, +}; + +enum transfer_list_ops { + TL_OPS_NON, // invalid for any operation + TL_OPS_ALL, // valid for all operations + TL_OPS_RO, // valid for read only + TL_OPS_CUS, // either abort or switch to special code to interpret +}; + +struct transfer_list_header { + uint32_t signature; + uint8_t checksum; + uint8_t version; + uint8_t hdr_size; + uint8_t alignment; // max alignment of TE data + uint32_t size; // TL header + all TEs + uint32_t max_size; + /* + * Commented out element used to visualize dynamic part of the + * data structure. + * + * Note that struct transfer_list_entry also is dynamic in size + * so the elements can't be indexed directly but instead must be + * traversed in order + * + * struct transfer_list_entry entries[]; + */ +}; + +struct transfer_list_entry { + uint16_t tag_id; + uint8_t reserved0; // place holder + uint8_t hdr_size; + uint32_t data_size; + /* + * Commented out element used to visualize dynamic part of the + * data structure. + * + * Note that padding is added at the end of @data to make to reach + * a 8-byte boundary. + * + * uint8_t data[ROUNDUP(data_size, 8)]; + */ +}; + +void transfer_list_dump(struct transfer_list_header *tl); +struct transfer_list_header *transfer_list_init(void *addr, size_t max_size); + +struct transfer_list_header *transfer_list_relocate(struct transfer_list_header *tl, + void *addr, size_t max_size); +enum transfer_list_ops transfer_list_check_header(const struct transfer_list_header *tl); + +void transfer_list_update_checksum(struct transfer_list_header *tl); +bool transfer_list_verify_checksum(const struct transfer_list_header *tl); + +bool transfer_list_set_data_size(struct transfer_list_header *tl, + struct transfer_list_entry *entry, + uint32_t new_data_size); + +void *transfer_list_entry_data(struct transfer_list_entry *entry); +bool transfer_list_rem(struct transfer_list_header *tl, struct transfer_list_entry *entry); + +struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl, + uint16_t tag_id, uint32_t data_size, + const void *data); + +struct transfer_list_entry *transfer_list_add_with_align(struct transfer_list_header *tl, + uint16_t tag_id, uint32_t data_size, + const void *data, uint8_t alignment); + +struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl, + struct transfer_list_entry *last); + +struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl, + uint16_t tag_id); + +#endif /*__ASSEMBLER__*/ +#endif /*__TRANSFER_LIST_H*/ diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h index ba52bc6c5..a170a09d2 100644 --- a/include/lib/utils_def.h +++ b/include/lib/utils_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2022, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved. * Copyright (c) 2020, NVIDIA Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause @@ -104,6 +104,41 @@ #define round_down(value, boundary) \ ((value) & ~round_boundary(value, boundary)) +/* add operation together with checking whether the operation overflowed + * The result is '*res', + * return 0 on success and 1 on overflow + */ +#define add_overflow(a, b, res) __builtin_add_overflow((a), (b), (res)) + +/* + * Round up a value to align with a given size and + * check whether overflow happens. + * The rounduped value is '*res', + * return 0 on success and 1 on overflow + */ +#define round_up_overflow(v, size, res) (__extension__({ \ + typeof(res) __res = res; \ + typeof(*(__res)) __roundup_tmp = 0; \ + typeof(v) __roundup_mask = (typeof(v))(size) - 1; \ + \ + add_overflow((v), __roundup_mask, &__roundup_tmp) ? 1 : \ + (void)(*(__res) = __roundup_tmp & ~__roundup_mask), 0; \ +})) + +/* + * Add a with b, then round up the result to align with a given size and + * check whether overflow happens. + * The rounduped value is '*res', + * return 0 on success and 1 on overflow + */ +#define add_with_round_up_overflow(a, b, size, res) (__extension__({ \ + typeof(a) __a = (a); \ + typeof(__a) __add_res = 0; \ + \ + add_overflow((__a), (b), &__add_res) ? 1 : \ + round_up_overflow(__add_res, (size), (res)) ? 1 : 0; \ +})) + /** * Helper macro to ensure a value lies on a given boundary. */ diff --git a/lib/transfer_list/transfer_list.c b/lib/transfer_list/transfer_list.c new file mode 100644 index 000000000..e38bf7426 --- /dev/null +++ b/lib/transfer_list/transfer_list.c @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include +#include + +void transfer_list_dump(struct transfer_list_header *tl) +{ + struct transfer_list_entry *te = NULL; + int i = 0; + + if (!tl) { + return; + } + NOTICE("Dump transfer list:\n"); + NOTICE("signature 0x%x\n", tl->signature); + NOTICE("checksum 0x%x\n", tl->checksum); + NOTICE("version 0x%x\n", tl->version); + NOTICE("hdr_size 0x%x\n", tl->hdr_size); + NOTICE("alignment 0x%x\n", tl->alignment); + NOTICE("size 0x%x\n", tl->size); + NOTICE("max_size 0x%x\n", tl->max_size); + while (true) { + te = transfer_list_next(tl, te); + if (!te) { + break; + } + NOTICE("Entry %d:\n", i++); + NOTICE("tag_id 0x%x\n", te->tag_id); + NOTICE("hdr_size 0x%x\n", te->hdr_size); + NOTICE("data_size 0x%x\n", te->data_size); + NOTICE("data_addr 0x%lx\n", + (unsigned long)transfer_list_entry_data(te)); + } +} + +/******************************************************************************* + * Creating a transfer list in a reserved memory region specified + * Compliant to 2.4.5 of Firmware handoff specification (v0.9) + * Return pointer to the created transfer list or NULL on error + ******************************************************************************/ +struct transfer_list_header *transfer_list_init(void *addr, size_t max_size) +{ + struct transfer_list_header *tl = addr; + + if (!addr || max_size == 0) { + return NULL; + } + + if (!is_aligned((uintptr_t)addr, 1 << TRANSFER_LIST_INIT_MAX_ALIGN) || + !is_aligned(max_size, 1 << TRANSFER_LIST_INIT_MAX_ALIGN) || + max_size < sizeof(*tl)) { + return NULL; + } + + memset(tl, 0, max_size); + tl->signature = TRANSFER_LIST_SIGNATURE; + tl->version = TRANSFER_LIST_VERSION; + tl->hdr_size = sizeof(*tl); + tl->alignment = TRANSFER_LIST_INIT_MAX_ALIGN; // initial max align + tl->size = sizeof(*tl); // initial size is the size of header + tl->max_size = max_size; + + transfer_list_update_checksum(tl); + + return tl; +} + +/******************************************************************************* + * Relocating a transfer list to a reserved memory region specified + * Compliant to 2.4.6 of Firmware handoff specification (v0.9) + * Return true on success or false on error + ******************************************************************************/ +struct transfer_list_header *transfer_list_relocate( + struct transfer_list_header *tl, + void *addr, size_t max_size) +{ + uintptr_t new_addr, align_mask, align_off; + struct transfer_list_header *new_tl; + uint32_t new_max_size; + + if (!tl || !addr || max_size == 0) { + return NULL; + } + + align_mask = (1 << tl->alignment) - 1; + align_off = (uintptr_t)tl & align_mask; + new_addr = ((uintptr_t)addr & ~align_mask) + align_off; + + if (new_addr < (uintptr_t)addr) { + new_addr += (1 << tl->alignment); + } + + new_max_size = max_size - (new_addr - (uintptr_t)addr); + + // the new space is not sufficient for the tl + if (tl->size > new_max_size) { + return NULL; + } + + new_tl = (struct transfer_list_header *)new_addr; + memmove(new_tl, tl, tl->size); + new_tl->max_size = new_max_size; + + transfer_list_update_checksum(new_tl); + + return new_tl; +} + +/******************************************************************************* + * Verifying the header of a transfer list + * Compliant to 2.4.1 of Firmware handoff specification (v0.9) + * Return transfer list operation status code + ******************************************************************************/ +enum transfer_list_ops transfer_list_check_header( + const struct transfer_list_header *tl) +{ + if (!tl) { + return TL_OPS_NON; + } + + if (tl->signature != TRANSFER_LIST_SIGNATURE) { + ERROR("Bad transfer list signature %#"PRIx32"\n", + tl->signature); + return TL_OPS_NON; + } + + if (!tl->max_size) { + ERROR("Bad transfer list max size %#"PRIx32"\n", + tl->max_size); + return TL_OPS_NON; + } + + if (tl->size > tl->max_size) { + ERROR("Bad transfer list size %#"PRIx32"\n", tl->size); + return TL_OPS_NON; + } + + if (tl->hdr_size != sizeof(struct transfer_list_header)) { + ERROR("Bad transfer list header size %#"PRIx32"\n", tl->hdr_size); + return TL_OPS_NON; + } + + if (!transfer_list_verify_checksum(tl)) { + ERROR("Bad transfer list checksum %#"PRIx32"\n", tl->checksum); + return TL_OPS_NON; + } + + if (tl->version == 0) { + ERROR("Transfer list version is invalid\n"); + return TL_OPS_NON; + } else if (tl->version == TRANSFER_LIST_VERSION) { + INFO("Transfer list version is valid for all operations\n"); + return TL_OPS_ALL; + } else if (tl->version > TRANSFER_LIST_VERSION) { + INFO("Transfer list version is valid for read-only\n"); + return TL_OPS_RO; + } + + INFO("Old transfer list version is detected\n"); + return TL_OPS_CUS; +} + +/******************************************************************************* + * Enumerate the next transfer entry + * Return pointer to the next transfer entry or NULL on error + ******************************************************************************/ +struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl, + struct transfer_list_entry *last) +{ + struct transfer_list_entry *te = NULL; + uintptr_t tl_ev = 0; + uintptr_t va = 0; + uintptr_t ev = 0; + size_t sz = 0; + + if (!tl) { + return NULL; + } + + tl_ev = (uintptr_t)tl + tl->size; + + if (last) { + va = (uintptr_t)last; + // check if the total size overflow + if (add_overflow(last->hdr_size, + last->data_size, &sz)) { + return NULL; + } + // roundup to the next entry + if (add_with_round_up_overflow(va, sz, + TRANSFER_LIST_GRANULE, &va)) { + return NULL; + } + } else { + va = (uintptr_t)tl + tl->hdr_size; + } + + te = (struct transfer_list_entry *)va; + + if (va + sizeof(*te) > tl_ev || te->hdr_size < sizeof(*te) || + add_overflow(te->hdr_size, te->data_size, &sz) || + add_overflow(va, sz, &ev) || + ev > tl_ev) { + return NULL; + } + + return te; +} + +/******************************************************************************* + * Calculate the byte sum of a transfer list + * Return byte sum of the transfer list + ******************************************************************************/ +static uint8_t calc_byte_sum(const struct transfer_list_header *tl) +{ + uint8_t *b = (uint8_t *)tl; + uint8_t cs = 0; + size_t n = 0; + + if (!tl) { + return 0; + } + + for (n = 0; n < tl->size; n++) { + cs += b[n]; + } + + return cs; +} + +/******************************************************************************* + * Update the checksum of a transfer list + * Return updated checksum of the transfer list + ******************************************************************************/ +void transfer_list_update_checksum(struct transfer_list_header *tl) +{ + uint8_t cs; + + if (!tl) { + return; + } + + cs = calc_byte_sum(tl); + cs -= tl->checksum; + cs = 256 - cs; + tl->checksum = cs; + assert(transfer_list_verify_checksum(tl)); +} + +/******************************************************************************* + * Verify the checksum of a transfer list + * Return true if verified or false if not + ******************************************************************************/ +bool transfer_list_verify_checksum(const struct transfer_list_header *tl) +{ + return !calc_byte_sum(tl); +} + +/******************************************************************************* + * Update the data size of a transfer entry + * Return true on success or false on error + ******************************************************************************/ +bool transfer_list_set_data_size(struct transfer_list_header *tl, + struct transfer_list_entry *te, + uint32_t new_data_size) +{ + uintptr_t tl_old_ev, new_ev = 0, old_ev = 0, ru_new_ev; + struct transfer_list_entry *dummy_te = NULL; + size_t gap = 0; + size_t mov_dis = 0; + size_t sz = 0; + + if (!tl || !te) { + return false; + } + tl_old_ev = (uintptr_t)tl + tl->size; + + // calculate the old and new end of TE + // both must be roundup to align with TRANSFER_LIST_GRANULE + if (add_overflow(te->hdr_size, te->data_size, &sz) || + add_with_round_up_overflow((uintptr_t)te, sz, + TRANSFER_LIST_GRANULE, &old_ev)) { + return false; + } + if (add_overflow(te->hdr_size, new_data_size, &sz) || + add_with_round_up_overflow((uintptr_t)te, sz, + TRANSFER_LIST_GRANULE, &new_ev)) { + return false; + } + + if (new_ev > old_ev) { + // move distance should be roundup + // to meet the requirement of TE data max alignment + // ensure that the increased size doesn't exceed + // the max size of TL + mov_dis = new_ev - old_ev; + if (round_up_overflow(mov_dis, 1 << tl->alignment, + &mov_dis) || tl->size + mov_dis > tl->max_size) { + return false; + } + ru_new_ev = old_ev + mov_dis; + memmove((void *)ru_new_ev, (void *)old_ev, tl_old_ev - old_ev); + tl->size += mov_dis; + gap = ru_new_ev - new_ev; + } else { + gap = old_ev - new_ev; + } + + if (gap >= sizeof(*dummy_te)) { + // create a dummy TE to fill up the gap + dummy_te = (struct transfer_list_entry *)new_ev; + dummy_te->tag_id = TL_TAG_EMPTY; + dummy_te->reserved0 = 0; + dummy_te->hdr_size = sizeof(*dummy_te); + dummy_te->data_size = gap - sizeof(*dummy_te); + } + + te->data_size = new_data_size; + + transfer_list_update_checksum(tl); + return true; +} + +/******************************************************************************* + * Remove a specified transfer entry from a transfer list + * Return true on success or false on error + ******************************************************************************/ +bool transfer_list_rem(struct transfer_list_header *tl, + struct transfer_list_entry *te) +{ + if (!tl || !te || (uintptr_t)te > (uintptr_t)tl + tl->size) { + return false; + } + te->tag_id = TL_TAG_EMPTY; + te->reserved0 = 0; + transfer_list_update_checksum(tl); + return true; +} + +/******************************************************************************* + * Add a new transfer entry into a transfer list + * Compliant to 2.4.3 of Firmware handoff specification (v0.9) + * Return pointer to the added transfer entry or NULL on error + ******************************************************************************/ +struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl, + uint16_t tag_id, + uint32_t data_size, + const void *data) +{ + uintptr_t max_tl_ev, tl_ev, ev; + struct transfer_list_entry *te = NULL; + uint8_t *te_data = NULL; + size_t sz = 0; + + if (!tl) { + return NULL; + } + + max_tl_ev = (uintptr_t)tl + tl->max_size; + tl_ev = (uintptr_t)tl + tl->size; + ev = tl_ev; + + // skip the step 1 (optional step) + // new TE will be added into the tail + if (add_overflow(sizeof(*te), data_size, &sz) || + add_with_round_up_overflow(ev, sz, + TRANSFER_LIST_GRANULE, &ev) || ev > max_tl_ev) { + return NULL; + } + + te = (struct transfer_list_entry *)tl_ev; + te->tag_id = tag_id; + te->reserved0 = 0; + te->hdr_size = sizeof(*te); + te->data_size = data_size; + tl->size += ev - tl_ev; + + if (data) { + // get TE data pointer + te_data = transfer_list_entry_data(te); + if (!te_data) { + return NULL; + } + memmove(te_data, data, data_size); + } + + transfer_list_update_checksum(tl); + + return te; +} + +/******************************************************************************* + * Add a new transfer entry into a transfer list with specified new data + * alignment requirement + * Compliant to 2.4.4 of Firmware handoff specification (v0.9) + * Return pointer to the added transfer entry or NULL on error + ******************************************************************************/ +struct transfer_list_entry *transfer_list_add_with_align( + struct transfer_list_header *tl, + uint16_t tag_id, uint32_t data_size, + const void *data, uint8_t alignment) +{ + struct transfer_list_entry *te = NULL; + uintptr_t tl_ev, ev, new_tl_ev; + size_t dummy_te_data_sz = 0; + + if (!tl) { + return NULL; + } + + tl_ev = (uintptr_t)tl + tl->size; + ev = tl_ev + sizeof(struct transfer_list_entry); + + if (!is_aligned(ev, 1 << alignment)) { + // TE data address is not aligned to the new alignment + // fill the gap with an empty TE as a placeholder before + // adding the desire TE + new_tl_ev = round_up(ev, 1 << alignment) - + sizeof(struct transfer_list_entry); + dummy_te_data_sz = new_tl_ev - tl_ev - + sizeof(struct transfer_list_entry); + if (!transfer_list_add(tl, TL_TAG_EMPTY, dummy_te_data_sz, + NULL)) { + return NULL; + } + } + + te = transfer_list_add(tl, tag_id, data_size, data); + + if (alignment > tl->alignment) { + tl->alignment = alignment; + transfer_list_update_checksum(tl); + } + + return te; +} + +/******************************************************************************* + * Search for an existing transfer entry with the specified tag id from a + * transfer list + * Return pointer to the found transfer entry or NULL on error + ******************************************************************************/ +struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl, + uint16_t tag_id) +{ + struct transfer_list_entry *te = NULL; + + do { + te = transfer_list_next(tl, te); + } while (te && (te->tag_id != tag_id || te->reserved0 != 0)); + + return te; +} + +/******************************************************************************* + * Retrieve the data pointer of a specified transfer entry + * Return pointer to the transfer entry data or NULL on error + ******************************************************************************/ +void *transfer_list_entry_data(struct transfer_list_entry *entry) +{ + if (!entry) { + return NULL; + } + return (uint8_t *)entry + entry->hdr_size; +} diff --git a/lib/transfer_list/transfer_list.mk b/lib/transfer_list/transfer_list.mk new file mode 100644 index 000000000..42574e85d --- /dev/null +++ b/lib/transfer_list/transfer_list.mk @@ -0,0 +1,20 @@ +# +# Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +ifeq (${TRANSFER_LIST},1) + +ifeq (${ARCH},aarch32) +$(eval $(call add_define,TRANSFER_LIST_AARCH32)) +endif + +TRANSFER_LIST_SOURCES += $(addprefix lib/transfer_list/, \ + transfer_list.c) + +BL31_SOURCES += $(TRANSFER_LIST_SOURCES) +BL2_SOURCES += $(TRANSFER_LIST_SOURCES) + +endif # TRANSFER_LIST + diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index aaabb2771..b7e6f9917 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -147,6 +147,9 @@ GICV2_G0_FOR_EL3 := 0 # by lower ELs. HANDLE_EA_EL3_FIRST_NS := 0 +# Enable Handoff protocol using transfer lists +TRANSFER_LIST := 0 + # Secure hash algorithm flag, accepts 3 values: sha256, sha384 and sha512. # The default value is sha256. HASH_ALG := sha256 From 322af23445fe7a86eaad335b8a0f2ed523f5c1df Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Wed, 28 Jun 2023 15:07:15 -0700 Subject: [PATCH 2/2] feat(qemu): implement firmware handoff on qemu Implement firmware handoff from BL2 to BL33 on qemu platform compliant to Firmware handoff specification v0.9. Change-Id: Id8d5206a71ef6ec97cf3c97995de328ebf0600cc Signed-off-by: Raymond Mao --- plat/qemu/common/qemu_bl2_setup.c | 82 ++++++++++++++++++++++++++- plat/qemu/common/qemu_common.c | 17 ++++++ plat/qemu/common/qemu_image_load.c | 5 +- plat/qemu/common/qemu_private.h | 4 +- plat/qemu/qemu/include/platform_def.h | 20 +++++-- plat/qemu/qemu/platform.mk | 4 ++ 6 files changed, 123 insertions(+), 9 deletions(-) diff --git a/plat/qemu/common/qemu_bl2_setup.c b/plat/qemu/common/qemu_bl2_setup.c index c4d235e0a..231f23a77 100644 --- a/plat/qemu/common/qemu_bl2_setup.c +++ b/plat/qemu/common/qemu_bl2_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -18,6 +18,9 @@ #include #include #include +#if TRANSFER_LIST +#include +#endif #include #include @@ -48,6 +51,9 @@ /* Data structure which holds the extents of the trusted SRAM for BL2 */ static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE); +#if TRANSFER_LIST +static struct transfer_list_header *bl2_tl; +#endif void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) @@ -73,6 +79,9 @@ static void security_setup(void) static void update_dt(void) { +#if TRANSFER_LIST + struct transfer_list_entry *te; +#endif int ret; void *fdt = (void *)(uintptr_t)ARM_PRELOADED_DTB_BASE; @@ -95,16 +104,40 @@ static void update_dt(void) ret = fdt_pack(fdt); if (ret < 0) ERROR("Failed to pack Device Tree at %p: error %d\n", fdt, ret); + +#if TRANSFER_LIST + // create a TE + te = transfer_list_add(bl2_tl, TL_TAG_FDT, fdt_totalsize(fdt), fdt); + if (!te) { + ERROR("Failed to add FDT entry to Transfer List\n"); + return; + } +#endif } void bl2_platform_setup(void) { +#if TRANSFER_LIST + bl2_tl = transfer_list_init((void *)(uintptr_t)FW_HANDOFF_BASE, + FW_HANDOFF_SIZE); + if (!bl2_tl) { + ERROR("Failed to initialize Transfer List at 0x%lx\n", + (unsigned long)FW_HANDOFF_BASE); + } +#endif security_setup(); update_dt(); /* TODO Initialize timer */ } +void qemu_bl2_sync_transfer_list(void) +{ +#if TRANSFER_LIST + transfer_list_update_checksum(bl2_tl); +#endif +} + void bl2_plat_arch_setup(void) { const mmap_region_t bl_regions[] = { @@ -221,6 +254,10 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) #if defined(SPD_spmd) bl_mem_params_node_t *bl32_mem_params = NULL; #endif +#if TRANSFER_LIST + struct transfer_list_header *ns_tl = NULL; + struct transfer_list_entry *te = NULL; +#endif assert(bl_mem_params); @@ -275,6 +312,8 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) pager_mem_params->ep_info.lr_svc = bl_mem_params->ep_info.pc; #endif + bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl33_entry(); + #if ARM_LINUX_KERNEL_AS_BL33 /* * According to the file ``Documentation/arm64/booting.txt`` of @@ -287,12 +326,49 @@ static int qemu_bl2_handle_post_image_load(unsigned int image_id) bl_mem_params->ep_info.args.arg1 = 0U; bl_mem_params->ep_info.args.arg2 = 0U; bl_mem_params->ep_info.args.arg3 = 0U; +#elif TRANSFER_LIST + if (bl2_tl) { + // relocate the tl to pre-allocate NS memory + ns_tl = transfer_list_relocate(bl2_tl, + (void *)(uintptr_t)FW_NS_HANDOFF_BASE, + bl2_tl->max_size); + if (!ns_tl) { + ERROR("Relocate TL to 0x%lx failed\n", + (unsigned long)FW_NS_HANDOFF_BASE); + return -1; + } + NOTICE("Transfer list handoff to BL33\n"); + transfer_list_dump(ns_tl); + + te = transfer_list_find(ns_tl, TL_TAG_FDT); + + bl_mem_params->ep_info.args.arg1 = + TRANSFER_LIST_SIGNATURE | + REGISTER_CONVENTION_VERSION_MASK; + bl_mem_params->ep_info.args.arg3 = (uintptr_t)ns_tl; + + if (GET_RW(bl_mem_params->ep_info.spsr) == MODE_RW_32) { + // aarch32 + bl_mem_params->ep_info.args.arg0 = 0; + bl_mem_params->ep_info.args.arg2 = te ? + (uintptr_t)transfer_list_entry_data(te) + : 0; + } else { + // aarch64 + bl_mem_params->ep_info.args.arg0 = te ? + (uintptr_t)transfer_list_entry_data(te) + : 0; + bl_mem_params->ep_info.args.arg2 = 0; + } + } else { + // Legacy handoff + bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr(); + } #else /* BL33 expects to receive the primary CPU MPID (through r0) */ bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr(); -#endif +#endif // ARM_LINUX_KERNEL_AS_BL33 - bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl33_entry(); break; #ifdef SPD_spmd #if SPMD_SPM_AT_SEL2 diff --git a/plat/qemu/common/qemu_common.c b/plat/qemu/common/qemu_common.c index 98be4910d..d4488a4bd 100644 --- a/plat/qemu/common/qemu_common.c +++ b/plat/qemu/common/qemu_common.c @@ -47,6 +47,14 @@ #define MAP_FLASH1 MAP_REGION_FLAT(QEMU_FLASH1_BASE, QEMU_FLASH1_SIZE, \ MT_MEMORY | MT_RO | MT_SECURE) +#ifdef FW_HANDOFF_BASE +#define MAP_FW_HANDOFF MAP_REGION_FLAT(FW_HANDOFF_BASE, FW_HANDOFF_SIZE, \ + MT_MEMORY | MT_RW | MT_SECURE) +#endif +#ifdef FW_NS_HANDOFF_BASE +#define MAP_FW_NS_HANDOFF MAP_REGION_FLAT(FW_NS_HANDOFF_BASE, FW_HANDOFF_SIZE, \ + MT_MEMORY | MT_RW | MT_NS) +#endif /* * Table of regions for various BL stages to map using the MMU. * This doesn't include TZRAM as the 'mem_layout' argument passed to @@ -84,6 +92,9 @@ static const mmap_region_t plat_qemu_mmap[] = { QEMU_SP_IMAGE_MMAP, #else MAP_BL32_MEM, +#endif +#ifdef MAP_FW_HANDOFF + MAP_FW_HANDOFF, #endif {0} }; @@ -98,6 +109,12 @@ static const mmap_region_t plat_qemu_mmap[] = { #ifdef MAP_DEVICE2 MAP_DEVICE2, #endif +#ifdef MAP_FW_HANDOFF + MAP_FW_HANDOFF, +#endif +#ifdef MAP_FW_NS_HANDOFF + MAP_FW_NS_HANDOFF, +#endif #if SPM_MM MAP_NS_DRAM0, QEMU_SPM_BUF_EL3_MMAP, diff --git a/plat/qemu/common/qemu_image_load.c b/plat/qemu/common/qemu_image_load.c index 9970d1de7..2b02a67ed 100644 --- a/plat/qemu/common/qemu_image_load.c +++ b/plat/qemu/common/qemu_image_load.c @@ -1,11 +1,13 @@ /* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include +#include "qemu_private.h" + /******************************************************************************* * This function is a wrapper of a common function which flushes the data * structures so that they are visible in memory for the next BL image. @@ -13,6 +15,7 @@ void plat_flush_next_bl_params(void) { flush_bl_params_desc(); + qemu_bl2_sync_transfer_list(); } /******************************************************************************* diff --git a/plat/qemu/common/qemu_private.h b/plat/qemu/common/qemu_private.h index e80a88d5f..c8912b22f 100644 --- a/plat/qemu/common/qemu_private.h +++ b/plat/qemu/common/qemu_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -40,4 +40,6 @@ int qemu_set_nt_fw_info( size_t log_size, uintptr_t *ns_log_addr); +void qemu_bl2_sync_transfer_list(void); + #endif /* QEMU_PRIVATE_H */ diff --git a/plat/qemu/qemu/include/platform_def.h b/plat/qemu/qemu/include/platform_def.h index 93a3ce842..903c809dd 100644 --- a/plat/qemu/qemu/include/platform_def.h +++ b/plat/qemu/qemu/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -151,9 +151,17 @@ * current BL3-1 debug size plus a little space for growth. */ #define BL31_BASE (BL31_LIMIT - 0x60000) -#define BL31_LIMIT (BL_RAM_BASE + BL_RAM_SIZE) +#define BL31_LIMIT (BL_RAM_BASE + BL_RAM_SIZE - FW_HANDOFF_SIZE) #define BL31_PROGBITS_LIMIT BL1_RW_BASE +#if TRANSFER_LIST +#define FW_HANDOFF_BASE BL31_LIMIT +#define FW_HANDOFF_LIMIT (FW_HANDOFF_BASE + FW_HANDOFF_SIZE) +#define FW_HANDOFF_SIZE 0x4000 +#else +#define FW_HANDOFF_SIZE 0 +#endif + /* * BL3-2 specific defines. @@ -172,16 +180,20 @@ # define BL32_MEM_BASE BL_RAM_BASE # define BL32_MEM_SIZE BL_RAM_SIZE # define BL32_BASE BL32_SRAM_BASE -# define BL32_LIMIT BL32_SRAM_LIMIT +# define BL32_LIMIT (BL32_SRAM_LIMIT - FW_HANDOFF_SIZE) #elif BL32_RAM_LOCATION_ID == SEC_DRAM_ID # define BL32_MEM_BASE SEC_DRAM_BASE # define BL32_MEM_SIZE SEC_DRAM_SIZE # define BL32_BASE BL32_DRAM_BASE -# define BL32_LIMIT BL32_DRAM_LIMIT +# define BL32_LIMIT (BL32_DRAM_LIMIT - FW_HANDOFF_SIZE) #else # error "Unsupported BL32_RAM_LOCATION_ID value" #endif +#if TRANSFER_LIST +#define FW_NS_HANDOFF_BASE (NS_IMAGE_OFFSET - FW_HANDOFF_SIZE) +#endif + #define NS_IMAGE_OFFSET (NS_DRAM0_BASE + 0x20000000) #define NS_IMAGE_MAX_SIZE (NS_DRAM0_SIZE - 0x20000000) diff --git a/plat/qemu/qemu/platform.mk b/plat/qemu/qemu/platform.mk index 16e89c1fe..e902c1214 100644 --- a/plat/qemu/qemu/platform.mk +++ b/plat/qemu/qemu/platform.mk @@ -39,6 +39,10 @@ $(eval $(call add_define,SPMC_OPTEE)) add-lib-optee := yes endif +ifeq (${TRANSFER_LIST},1) +include lib/transfer_list/transfer_list.mk +endif + ifeq ($(NEED_BL32),yes) $(eval $(call add_define,QEMU_LOAD_BL32)) endif