diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h index 85844899d..da016fd46 100644 --- a/include/services/ffa_svc.h +++ b/include/services/ffa_svc.h @@ -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 */ diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h index 0e08d2e38..22a745e96 100644 --- a/services/std_svc/spm/el3_spmc/spmc.h +++ b/services/std_svc/spm/el3_spmc/spmc.h @@ -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, diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index 74c00da0e..c7071abf9 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -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); diff --git a/services/std_svc/spm/el3_spmc/spmc_setup.c b/services/std_svc/spm/el3_spmc/spmc_setup.c index af5219d02..8ebae2852 100644 --- a/services/std_svc/spm/el3_spmc/spmc_setup.c +++ b/services/std_svc/spm/el3_spmc/spmc_setup.c @@ -10,18 +10,138 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include "spm_common.h" #include "spmc.h" +#include #include +/* + * 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)); + } } /*