Skip to content

Commit

Permalink
core: add support for transfer list
Browse files Browse the repository at this point in the history
Add supports for Transfer List on both aarch32/64.
Fetch arguments from {x,r}{0-3} and check if a valid Transfer List
exists, which compliant to the Firmware Handoff specification.
The Transfer List will be mapped during early initialization and
unmapped before exiting to next boot stage.
DTB and pagable address will be parsed from the Transfer List if
they exist as Transfer Entries.
If Transfer List does not exist or is invalid, legacy argument
handoff is backwards compatible.

Signed-off-by: Raymond Mao <raymond.mao@linaro.org>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
Acked-by: Etienne Carriere <etienne.carriere@foss.st.com>
  • Loading branch information
raymo200915 authored and jforissier committed Nov 9, 2023
1 parent a122250 commit 6676372
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 12 deletions.
115 changes: 110 additions & 5 deletions core/arch/arm/kernel/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <ffa.h>
#include <initcall.h>
#include <inttypes.h>
#include <io.h>
#include <keep.h>
#include <kernel/asan.h>
#include <kernel/boot.h>
Expand All @@ -25,6 +26,7 @@
#include <kernel/tee_misc.h>
#include <kernel/thread.h>
#include <kernel/tpm.h>
#include <kernel/transfer_list.h>
#include <libfdt.h>
#include <malloc.h>
#include <memtag.h>
Expand Down Expand Up @@ -82,6 +84,8 @@ static void *manifest_dt __nex_bss;
static unsigned long boot_arg_fdt __nex_bss;
static unsigned long boot_arg_nsec_entry __nex_bss;
static unsigned long boot_arg_pageable_part __nex_bss;
static unsigned long boot_arg_transfer_list __nex_bss;
static struct transfer_list_header *mapped_tl __nex_bss;

#ifdef CFG_SECONDARY_INIT_CNTFRQ
static uint32_t cntfrq;
Expand Down Expand Up @@ -1252,15 +1256,54 @@ void __weak boot_init_primary_early(void)
{
unsigned long pageable_part = 0;
unsigned long e = PADDR_INVALID;
struct transfer_list_entry *tl_e = NULL;

if (!IS_ENABLED(CFG_WITH_ARM_TRUSTED_FW))
e = boot_arg_nsec_entry;
if (IS_ENABLED(CFG_WITH_PAGER))
pageable_part = boot_arg_pageable_part;

if (IS_ENABLED(CFG_TRANSFER_LIST) && boot_arg_transfer_list) {
/* map and save the TL */
mapped_tl = transfer_list_map(boot_arg_transfer_list);
if (!mapped_tl)
panic("Failed to map transfer list");
tl_e = transfer_list_find(mapped_tl, TL_TAG_OPTEE_PAGABLE_PART);
}

if (IS_ENABLED(CFG_WITH_PAGER)) {
if (IS_ENABLED(CFG_TRANSFER_LIST) && tl_e)
pageable_part =
get_le64(transfer_list_entry_data(tl_e));
else
pageable_part = boot_arg_pageable_part;
}

init_primary(pageable_part, e);
}

static void boot_save_transfer_list(unsigned long zero_reg,
unsigned long transfer_list,
unsigned long fdt)
{
struct transfer_list_header *tl = (void *)transfer_list;
struct transfer_list_entry *tl_e = NULL;

if (zero_reg != 0)
panic("Incorrect transfer list register convention");

if (!IS_ALIGNED_WITH_TYPE(transfer_list, struct transfer_list_header) ||
!IS_ALIGNED(transfer_list, TL_ALIGNMENT_FROM_ORDER(tl->alignment)))
panic("Transfer list base address is not aligned");

if (transfer_list_check_header(tl) == TL_OPS_NONE)
panic("Invalid transfer list");

tl_e = transfer_list_find(tl, TL_TAG_FDT);
if (fdt != (unsigned long)transfer_list_entry_data(tl_e))
panic("DT does not match to the DT entry of the TL");

boot_arg_transfer_list = transfer_list;
}

#if defined(CFG_WITH_ARM_TRUSTED_FW)
unsigned long boot_cpu_on_handler(unsigned long a0 __maybe_unused,
unsigned long a1 __unused)
Expand Down Expand Up @@ -1441,13 +1484,14 @@ static void get_sec_mem_from_manifest(void *fdt, paddr_t *base, size_t *size)
*size = num;
}

void __weak boot_save_args(unsigned long a0, unsigned long a1 __unused,
unsigned long a2 __maybe_unused,
unsigned long a3 __unused,
void __weak boot_save_args(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3,
unsigned long a4 __maybe_unused)
{
/*
* Register use:
*
* Scenario A: Default arguments
* a0 - CFG_CORE_FFA=y && CFG_CORE_SEL2_SPMC=n:
* if non-NULL holds the TOS FW config [1] address
* - CFG_CORE_FFA=y &&
Expand All @@ -1471,8 +1515,31 @@ void __weak boot_save_args(unsigned long a0, unsigned long a1 __unused,
* here. This is also called Manifest DT, related to the Manifest DT
* passed in the FF-A Boot Information Blob, but with a different
* compatible string.
* Scenario B: FW Handoff via Transfer List
* Note: FF-A and non-secure entry are not yet supported with
* Transfer List
* a0 - DTB address or 0 (AArch64)
* - must be 0 (AArch32)
* a1 - TRANSFER_LIST_SIGNATURE | REG_CONVENTION_VER_MASK
* a2 - must be 0 (AArch64)
* - DTB address or 0 (AArch32)
* a3 - Transfer list base address
* a4 - Not used
*/

if (IS_ENABLED(CFG_TRANSFER_LIST) &&
a1 == (TRANSFER_LIST_SIGNATURE | REG_CONVENTION_VER_MASK)) {
if (IS_ENABLED(CFG_ARM64_core)) {
boot_save_transfer_list(a2, a3, a0);
boot_arg_fdt = a0;
} else {
boot_save_transfer_list(a0, a3, a2);
boot_arg_fdt = a2;
}
return;
}

if (!IS_ENABLED(CFG_CORE_SEL2_SPMC)) {
#if defined(CFG_DT_ADDR)
boot_arg_fdt = CFG_DT_ADDR;
Expand Down Expand Up @@ -1512,3 +1579,41 @@ void __weak boot_save_args(unsigned long a0, unsigned long a1 __unused,
}
}
}

#if defined(CFG_TRANSFER_LIST)
static TEE_Result release_transfer_list(void)
{
struct dt_descriptor *dt = get_external_dt_desc();

if (!mapped_tl)
return TEE_SUCCESS;

if (dt) {
int ret = 0;
struct transfer_list_entry *tl_e = NULL;

/*
* Pack the DTB and update the transfer list before un-mapping
*/
ret = fdt_pack(dt->blob);
if (ret < 0) {
EMSG("Failed to pack Device Tree at 0x%" PRIxPA
": error %d", virt_to_phys(dt->blob), ret);
panic();
}

tl_e = transfer_list_find(mapped_tl, TL_TAG_FDT);
assert(dt->blob == transfer_list_entry_data(tl_e));
transfer_list_set_data_size(mapped_tl, tl_e,
fdt_totalsize(dt->blob));
dt->blob = NULL;
}

transfer_list_unmap_sync(mapped_tl);
mapped_tl = NULL;

return TEE_SUCCESS;
}

boot_final(release_transfer_list);
#endif
34 changes: 27 additions & 7 deletions core/kernel/dt.c
Original file line number Diff line number Diff line change
Expand Up @@ -547,8 +547,8 @@ struct dt_descriptor *get_external_dt_desc(void)
void init_external_dt(unsigned long phys_dt)
{
struct dt_descriptor *dt = &external_dt;
void *fdt = NULL;
int ret = 0;
enum teecore_memtypes mtype = MEM_AREA_MAXTYPE;

if (!IS_ENABLED(CFG_EXTERNAL_DT))
return;
Expand All @@ -566,11 +566,22 @@ void init_external_dt(unsigned long phys_dt)
return;
}

fdt = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt, CFG_DTB_MAX_SIZE);
if (!fdt)
panic("Failed to map external DTB");

dt->blob = fdt;
mtype = core_mmu_get_type_by_pa(phys_dt);
if (mtype == MEM_AREA_MAXTYPE) {
/* Map the DTB if it is not yet mapped */
dt->blob = core_mmu_add_mapping(MEM_AREA_EXT_DT, phys_dt,
CFG_DTB_MAX_SIZE);
if (!dt->blob)
panic("Failed to map external DTB");
} else {
/* Get the DTB address if already mapped in a memory area */
dt->blob = phys_to_virt(phys_dt, mtype, CFG_DTB_MAX_SIZE);
if (!dt->blob) {
EMSG("Failed to get a mapped external DTB for PA %#lx",
phys_dt);
panic();
}
}

ret = init_dt_overlay(dt, CFG_DTB_MAX_SIZE);
if (ret < 0) {
Expand All @@ -579,7 +590,7 @@ void init_external_dt(unsigned long phys_dt)
panic();
}

ret = fdt_open_into(fdt, fdt, CFG_DTB_MAX_SIZE);
ret = fdt_open_into(dt->blob, dt->blob, CFG_DTB_MAX_SIZE);
if (ret < 0) {
EMSG("Invalid Device Tree at %#lx: error %d", phys_dt, ret);
panic();
Expand All @@ -600,13 +611,22 @@ void *get_external_dt(void)
static TEE_Result release_external_dt(void)
{
int ret = 0;
paddr_t pa_dt = 0;

if (!IS_ENABLED(CFG_EXTERNAL_DT))
return TEE_SUCCESS;

if (!external_dt.blob)
return TEE_SUCCESS;

pa_dt = virt_to_phys(external_dt.blob);
/*
* Skip packing and un-mapping operations if the external DTB is mapped
* in a different memory area
*/
if (core_mmu_get_type_by_pa(pa_dt) != MEM_AREA_EXT_DT)
return TEE_SUCCESS;

ret = fdt_pack(external_dt.blob);
if (ret < 0) {
EMSG("Failed to pack Device Tree at 0x%" PRIxPA ": error %d",
Expand Down

0 comments on commit 6676372

Please sign in to comment.