mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-17 10:04:26 +00:00
Merge changes from topic "rm/handoff" into integration
* changes: feat(qemu): implement firmware handoff on qemu feat(handoff): introduce firmware handoff library
This commit is contained in:
commit
a1377a89a7
14 changed files with 785 additions and 10 deletions
6
Makefile
6
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 \
|
||||
|
|
|
@ -787,6 +787,9 @@ subsections:
|
|||
- title: Semihosting
|
||||
scope: semihosting
|
||||
|
||||
- title: Firmware Handoff
|
||||
scope: handoff
|
||||
|
||||
- title: Drivers
|
||||
|
||||
subsections:
|
||||
|
|
|
@ -945,6 +945,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``.
|
||||
|
||||
|
@ -1300,3 +1305,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
|
||||
|
|
114
include/lib/transfer_list.h
Normal file
114
include/lib/transfer_list.h
Normal file
|
@ -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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lib/utils_def.h>
|
||||
|
||||
#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*/
|
|
@ -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.
|
||||
*/
|
||||
|
|
474
lib/transfer_list/transfer_list.c
Normal file
474
lib/transfer_list/transfer_list.c
Normal file
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <lib/transfer_list.h>
|
||||
#include <lib/utils_def.h>
|
||||
|
||||
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;
|
||||
}
|
20
lib/transfer_list/transfer_list.mk
Normal file
20
lib/transfer_list/transfer_list.mk
Normal file
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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 <common/fdt_fixup.h>
|
||||
#include <common/fdt_wrappers.h>
|
||||
#include <lib/optee_utils.h>
|
||||
#if TRANSFER_LIST
|
||||
#include <lib/transfer_list.h>
|
||||
#endif
|
||||
#include <lib/utils.h>
|
||||
#include <plat/common/platform.h>
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 <common/desc_image_load.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue