feat(spmc): add support for v1.1 FF-A boot protocol

A partition can request the use of the FF-A boot protocol via
an entry in its manifest along with the register (0-3)
that should be populated with a pointer to a data structure
containing boot related information. Currently the boot
information consists of an allocated memory region
containing the SP's manifest, allowing it to map and parse
any extra information as required.

This implementation only supports the v1.1 data structures
and will return an error if a v1.0 client requests the usage
of the protocol.

Signed-off-by: Achin Gupta <achin.gupta@arm.com>
Signed-off-by: Marc Bonnici <marc.bonnici@arm.com>
Change-Id: I67692553a90a7e7d94c64fe275edd247b512efca
This commit is contained in:
Achin Gupta 2021-10-19 12:21:16 +01:00 committed by Marc Bonnici
parent 6a0788bc0e
commit 2e21921502
4 changed files with 255 additions and 11 deletions

View File

@ -271,4 +271,71 @@ static inline bool ffa_is_normal_world_id(uint16_t id)
return !ffa_is_secure_world_id(id);
}
/******************************************************************************
* Boot information protocol as per the FF-A v1.1 spec.
*****************************************************************************/
#define FFA_INIT_DESC_SIGNATURE 0x00000FFA
/* Boot information type. */
#define FFA_BOOT_INFO_TYPE_STD U(0x0)
#define FFA_BOOT_INFO_TYPE_IMPL U(0x1)
#define FFA_BOOT_INFO_TYPE_MASK U(0x1)
#define FFA_BOOT_INFO_TYPE_SHIFT U(0x7)
#define FFA_BOOT_INFO_TYPE(type) \
(((type) & FFA_BOOT_INFO_TYPE_MASK) \
<< FFA_BOOT_INFO_TYPE_SHIFT)
/* Boot information identifier. */
#define FFA_BOOT_INFO_TYPE_ID_FDT U(0x0)
#define FFA_BOOT_INFO_TYPE_ID_HOB U(0x1)
#define FFA_BOOT_INFO_TYPE_ID_MASK U(0x3F)
#define FFA_BOOT_INFO_TYPE_ID_SHIFT U(0x0)
#define FFA_BOOT_INFO_TYPE_ID(type) \
(((type) & FFA_BOOT_INFO_TYPE_ID_MASK) \
<< FFA_BOOT_INFO_TYPE_ID_SHIFT)
/* Format of Flags Name field. */
#define FFA_BOOT_INFO_FLAG_NAME_STRING U(0x0)
#define FFA_BOOT_INFO_FLAG_NAME_UUID U(0x1)
#define FFA_BOOT_INFO_FLAG_NAME_MASK U(0x3)
#define FFA_BOOT_INFO_FLAG_NAME_SHIFT U(0x0)
#define FFA_BOOT_INFO_FLAG_NAME(type) \
(((type) & FFA_BOOT_INFO_FLAG_NAME_MASK)\
<< FFA_BOOT_INFO_FLAG_NAME_SHIFT)
/* Format of Flags Contents field. */
#define FFA_BOOT_INFO_FLAG_CONTENT_ADR U(0x0)
#define FFA_BOOT_INFO_FLAG_CONTENT_VAL U(0x1)
#define FFA_BOOT_INFO_FLAG_CONTENT_MASK U(0x1)
#define FFA_BOOT_INFO_FLAG_CONTENT_SHIFT U(0x2)
#define FFA_BOOT_INFO_FLAG_CONTENT(content) \
(((content) & FFA_BOOT_INFO_FLAG_CONTENT_MASK) \
<< FFA_BOOT_INFO_FLAG_CONTENT_SHIFT)
/* Boot information descriptor. */
struct ffa_boot_info_desc {
uint8_t name[16];
uint8_t type;
uint8_t reserved;
uint16_t flags;
uint32_t size_boot_info;
uint64_t content;
};
/* Boot information header. */
struct ffa_boot_info_header {
uint32_t signature; /* 0xFFA */
uint32_t version;
uint32_t size_boot_info_blob;
uint32_t size_boot_info_desc;
uint32_t count_boot_info_desc;
uint32_t offset_boot_info_desc;
uint64_t reserved;
};
#endif /* FFA_SVC_H */

View File

@ -225,7 +225,8 @@ extern const spd_pm_ops_t spmc_pm;
/* Setup Function for different SP types. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);
entry_point_info_t *ep_info,
int32_t boot_info_reg);
void spmc_el1_sp_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);
void spmc_sp_common_ep_commit(struct secure_partition_desc *sp,

View File

@ -1336,7 +1336,8 @@ static uint64_t ffa_sec_ep_register_handler(uint32_t smc_fid,
******************************************************************************/
static int sp_manifest_parse(void *sp_manifest, int offset,
struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
entry_point_info_t *ep_info,
int32_t *boot_info_reg)
{
int32_t ret, node;
uint32_t config_32;
@ -1453,6 +1454,20 @@ static int sp_manifest_parse(void *sp_manifest, int offset,
sp->pwr_mgmt_msgs = config_32;
}
ret = fdt_read_uint32(sp_manifest, node,
"gp-register-num", &config_32);
if (ret != 0) {
WARN("Missing boot information register.\n");
} else {
/* Check if a register number between 0-3 is specified. */
if (config_32 < 4) {
*boot_info_reg = config_32;
} else {
WARN("Incorrect boot information register (%u).\n",
config_32);
}
}
return 0;
}
@ -1468,7 +1483,7 @@ static int find_and_prepare_sp_context(void)
uintptr_t manifest_base;
uintptr_t manifest_base_align;
entry_point_info_t *next_image_ep_info;
int32_t ret;
int32_t ret, boot_info_reg = -1;
struct secure_partition_desc *sp;
next_image_ep_info = bl31_plat_get_next_image_ep_info(SECURE);
@ -1527,7 +1542,8 @@ static int find_and_prepare_sp_context(void)
SECURE | EP_ST_ENABLE);
/* Parse the SP manifest. */
ret = sp_manifest_parse(sp_manifest, ret, sp, next_image_ep_info);
ret = sp_manifest_parse(sp_manifest, ret, sp, next_image_ep_info,
&boot_info_reg);
if (ret != 0) {
ERROR("Error in Secure Partition manifest parsing.\n");
return ret;
@ -1540,7 +1556,7 @@ static int find_and_prepare_sp_context(void)
}
/* Perform any common initialisation. */
spmc_sp_common_setup(sp, next_image_ep_info);
spmc_sp_common_setup(sp, next_image_ep_info, boot_info_reg);
/* Perform any initialisation specific to S-EL1 SPs. */
spmc_el1_sp_setup(sp, next_image_ep_info);

View File

@ -10,18 +10,138 @@
#include <arch.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <context.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <libfdt.h>
#include <plat/common/common_def.h>
#include <plat/common/platform.h>
#include <services/ffa_svc.h>
#include "spm_common.h"
#include "spmc.h"
#include <tools_share/firmware_image_package.h>
#include <platform_def.h>
/*
* Statically allocate a page of memory for passing boot information to an SP.
*/
static uint8_t ffa_boot_info_mem[PAGE_SIZE] __aligned(PAGE_SIZE);
/*
* This function creates a initialization descriptor in the memory reserved
* for passing boot information to an SP. It then copies the partition manifest
* into this region and ensures that its reference in the initialization
* descriptor is updated.
*/
static void spmc_create_boot_info(entry_point_info_t *ep_info,
struct secure_partition_desc *sp)
{
struct ffa_boot_info_header *boot_header;
struct ffa_boot_info_desc *boot_descriptor;
uintptr_t manifest_addr;
/*
* Calculate the maximum size of the manifest that can be accommodated
* in the boot information memory region.
*/
const unsigned int
max_manifest_sz = sizeof(ffa_boot_info_mem) -
(sizeof(struct ffa_boot_info_header) +
sizeof(struct ffa_boot_info_desc));
/*
* The current implementation only supports the FF-A v1.1
* implementation of the boot protocol, therefore check
* that a v1.0 SP has not requested use of the protocol.
*/
if (sp->ffa_version == MAKE_FFA_VERSION(1, 0)) {
ERROR("FF-A boot protocol not supported for v1.0 clients\n");
return;
}
/*
* Check if the manifest will fit into the boot info memory region else
* bail.
*/
if (ep_info->args.arg1 > max_manifest_sz) {
WARN("Unable to copy manifest into boot information. ");
WARN("Max sz = %u bytes. Manifest sz = %lu bytes\n",
max_manifest_sz, ep_info->args.arg1);
return;
}
/* Zero the memory region before populating. */
memset(ffa_boot_info_mem, 0, PAGE_SIZE);
/*
* Populate the ffa_boot_info_header at the start of the boot info
* region.
*/
boot_header = (struct ffa_boot_info_header *) ffa_boot_info_mem;
/* Position the ffa_boot_info_desc after the ffa_boot_info_header. */
boot_header->offset_boot_info_desc =
sizeof(struct ffa_boot_info_header);
boot_descriptor = (struct ffa_boot_info_desc *)
(ffa_boot_info_mem +
boot_header->offset_boot_info_desc);
/*
* We must use the FF-A version coresponding to the version implemented
* by the SP. Currently this can only be v1.1.
*/
boot_header->version = sp->ffa_version;
/* Populate the boot information header. */
boot_header->size_boot_info_desc = sizeof(struct ffa_boot_info_desc);
/* Set the signature "0xFFA". */
boot_header->signature = FFA_INIT_DESC_SIGNATURE;
/* Set the count. Currently 1 since only the manifest is specified. */
boot_header->count_boot_info_desc = 1;
/* Populate the boot information descriptor for the manifest. */
boot_descriptor->type =
FFA_BOOT_INFO_TYPE(FFA_BOOT_INFO_TYPE_STD) |
FFA_BOOT_INFO_TYPE_ID(FFA_BOOT_INFO_TYPE_ID_FDT);
boot_descriptor->flags =
FFA_BOOT_INFO_FLAG_NAME(FFA_BOOT_INFO_FLAG_NAME_UUID) |
FFA_BOOT_INFO_FLAG_CONTENT(FFA_BOOT_INFO_FLAG_CONTENT_ADR);
/*
* Copy the manifest into boot info region after the boot information
* descriptor.
*/
boot_descriptor->size_boot_info = (uint32_t) ep_info->args.arg1;
manifest_addr = (uintptr_t) (ffa_boot_info_mem +
boot_header->offset_boot_info_desc +
boot_header->size_boot_info_desc);
memcpy((void *) manifest_addr, (void *) ep_info->args.arg0,
boot_descriptor->size_boot_info);
boot_descriptor->content = manifest_addr;
/* Calculate the size of the total boot info blob. */
boot_header->size_boot_info_blob = boot_header->offset_boot_info_desc +
boot_descriptor->size_boot_info +
(boot_header->count_boot_info_desc *
boot_header->size_boot_info_desc);
INFO("SP boot info @ 0x%lx, size: %u bytes.\n",
(uintptr_t) ffa_boot_info_mem,
boot_header->size_boot_info_blob);
INFO("SP manifest @ 0x%lx, size: %u bytes.\n",
boot_descriptor->content,
boot_descriptor->size_boot_info);
}
/*
* We are assuming that the index of the execution
* context used is the linear index of the current physical cpu.
@ -68,7 +188,8 @@ void spmc_el1_sp_setup(struct secure_partition_desc *sp,
/* Common initialisation for all SPs. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
entry_point_info_t *ep_info,
int32_t boot_info_reg)
{
uint16_t sp_id;
@ -96,11 +217,50 @@ void spmc_sp_common_setup(struct secure_partition_desc *sp,
*/
assert(sp->runtime_el == S_EL1);
/*
* Clear the general purpose registers. These should be populated as
* required.
*/
zeromem(&ep_info->args, sizeof(ep_info->args));
/* Check if the SP wants to use the FF-A boot protocol. */
if (boot_info_reg >= 0) {
/*
* Create a boot information descriptor and copy the partition
* manifest into the reserved memory region for consumption by
* the SP.
*/
spmc_create_boot_info(ep_info, sp);
/*
* We have consumed what we need from ep args so we can now
* zero them before we start populating with new information
* specifically for the SP.
*/
zeromem(&ep_info->args, sizeof(ep_info->args));
/*
* Pass the address of the boot information in the
* boot_info_reg.
*/
switch (boot_info_reg) {
case 0:
ep_info->args.arg0 = (uintptr_t) ffa_boot_info_mem;
break;
case 1:
ep_info->args.arg1 = (uintptr_t) ffa_boot_info_mem;
break;
case 2:
ep_info->args.arg2 = (uintptr_t) ffa_boot_info_mem;
break;
case 3:
ep_info->args.arg3 = (uintptr_t) ffa_boot_info_mem;
break;
default:
ERROR("Invalid value for \"gp-register-num\" %d.\n",
boot_info_reg);
}
} else {
/*
* We don't need any of the information that was populated
* in ep_args so we can clear them.
*/
zeromem(&ep_info->args, sizeof(ep_info->args));
}
}
/*