blob: 61ee0c6a306d7730d7fa57dfe0e780d76be01ec6 [file] [log] [blame]
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#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 "spm_shim_private.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);
/*
* We need to choose one execution context from all those available for a S-EL0
* SP. This execution context will be used subsequently irrespective of which
* physical CPU the SP runs on.
*/
#define SEL0_SP_EC_INDEX 0
#define SP_MEM_READ 0x1
#define SP_MEM_WRITE 0x2
#define SP_MEM_EXECUTE 0x4
#define SP_MEM_NON_SECURE 0x8
#define SP_MEM_READ_ONLY SP_MEM_READ
#define SP_MEM_READ_WRITE (SP_MEM_READ | SP_MEM_WRITE)
/*
* 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 corresponding 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);
}
/*
* S-EL1 partitions can be assigned with multiple execution contexts, each
* pinned to the physical CPU. Each execution context index corresponds to the
* respective liner core position.
* S-EL0 partitions execute in a single execution context (index 0).
*/
unsigned int get_ec_index(struct secure_partition_desc *sp)
{
return (sp->runtime_el == S_EL0) ?
SEL0_SP_EC_INDEX : plat_my_core_pos();
}
#if SPMC_AT_EL3_SEL0_SP
/* Setup spsr in entry point info for common context management code to use. */
void spmc_el0_sp_spsr_setup(entry_point_info_t *ep_info)
{
/* Setup Secure Partition SPSR for S-EL0 SP. */
ep_info->spsr = SPSR_64(MODE_EL0, MODE_SP_EL0, DISABLE_ALL_EXCEPTIONS);
}
static void read_optional_string(void *manifest, int32_t offset,
char *property, char *out, size_t len)
{
const fdt32_t *prop;
int lenp;
prop = fdt_getprop(manifest, offset, property, &lenp);
if (prop == NULL) {
out[0] = '\0';
} else {
memcpy(out, prop, MIN(lenp, (int)len));
}
}
/*******************************************************************************
* This function will parse the Secure Partition Manifest for fetching secure
* partition specific memory region details. It will find base address, size,
* memory attributes for each memory region and then add the respective region
* into secure parition's translation context.
******************************************************************************/
static void populate_sp_mem_regions(struct secure_partition_desc *sp,
void *sp_manifest,
int node)
{
uintptr_t base_address;
uint32_t mem_attr, mem_region, size;
struct mmap_region sp_mem_regions;
int32_t offset, ret;
char description[10];
char *property;
if (fdt_node_check_compatible(sp_manifest, node,
"arm,ffa-manifest-memory-regions") != 0) {
WARN("Incompatible memory region node in manifest\n");
return;
}
INFO("Mapping SP's memory regions\n");
for (offset = fdt_first_subnode(sp_manifest, node), mem_region = 0;
offset >= 0;
offset = fdt_next_subnode(sp_manifest, offset), mem_region++) {
read_optional_string(sp_manifest, offset, "description",
description, sizeof(description));
INFO("Mapping: region: %d, %s\n", mem_region, description);
property = "base-address";
ret = fdt_read_uint64(sp_manifest, offset, property,
&base_address);
if (ret < 0) {
WARN("Missing:%s for %s.\n", property, description);
continue;
}
property = "pages-count";
ret = fdt_read_uint32(sp_manifest, offset, property, &size);
if (ret < 0) {
WARN("Missing: %s for %s.\n", property, description);
continue;
}
size *= PAGE_SIZE;
property = "attributes";
ret = fdt_read_uint32(sp_manifest, offset, property, &mem_attr);
if (ret < 0) {
WARN("Missing: %s for %s.\n", property, description);
continue;
}
sp_mem_regions.attr = MT_USER;
if ((mem_attr & SP_MEM_EXECUTE) == SP_MEM_EXECUTE) {
sp_mem_regions.attr |= MT_CODE;
} else if ((mem_attr & SP_MEM_READ_ONLY) == SP_MEM_READ_ONLY) {
sp_mem_regions.attr |= MT_RO_DATA;
} else if ((mem_attr & SP_MEM_READ_WRITE) ==
SP_MEM_READ_WRITE) {
sp_mem_regions.attr |= MT_RW_DATA;
}
if ((mem_attr & SP_MEM_NON_SECURE) == SP_MEM_NON_SECURE) {
sp_mem_regions.attr |= MT_NS;
} else {
sp_mem_regions.attr |= MT_SECURE;
}
sp_mem_regions.base_pa = base_address;
sp_mem_regions.base_va = base_address;
sp_mem_regions.size = size;
sp_mem_regions.granularity = XLAT_BLOCK_SIZE(3);
mmap_add_region_ctx(sp->xlat_ctx_handle, &sp_mem_regions);
}
}
static void spmc_el0_sp_setup_mmu(struct secure_partition_desc *sp,
cpu_context_t *ctx)
{
xlat_ctx_t *xlat_ctx;
uint64_t mmu_cfg_params[MMU_CFG_PARAM_MAX];
xlat_ctx = sp->xlat_ctx_handle;
init_xlat_tables_ctx(sp->xlat_ctx_handle);
setup_mmu_cfg((uint64_t *)&mmu_cfg_params, 0, xlat_ctx->base_table,
xlat_ctx->pa_max_address, xlat_ctx->va_max_address,
EL1_EL0_REGIME);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_MAIR_EL1,
mmu_cfg_params[MMU_CFG_MAIR]);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_TCR_EL1,
mmu_cfg_params[MMU_CFG_TCR]);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_TTBR0_EL1,
mmu_cfg_params[MMU_CFG_TTBR0]);
}
static void spmc_el0_sp_setup_sctlr_el1(cpu_context_t *ctx)
{
u_register_t sctlr_el1;
/* Setup SCTLR_EL1 */
sctlr_el1 = read_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1);
sctlr_el1 |=
/*SCTLR_EL1_RES1 |*/
/* Don't trap DC CVAU, DC CIVAC, DC CVAC, DC CVAP, or IC IVAU */
SCTLR_UCI_BIT |
/* RW regions at xlat regime EL1&0 are forced to be XN. */
SCTLR_WXN_BIT |
/* Don't trap to EL1 execution of WFI or WFE at EL0. */
SCTLR_NTWI_BIT | SCTLR_NTWE_BIT |
/* Don't trap to EL1 accesses to CTR_EL0 from EL0. */
SCTLR_UCT_BIT |
/* Don't trap to EL1 execution of DZ ZVA at EL0. */
SCTLR_DZE_BIT |
/* Enable SP Alignment check for EL0 */
SCTLR_SA0_BIT |
/* Don't change PSTATE.PAN on taking an exception to EL1 */
SCTLR_SPAN_BIT |
/* Allow cacheable data and instr. accesses to normal memory. */
SCTLR_C_BIT | SCTLR_I_BIT |
/* Enable MMU. */
SCTLR_M_BIT;
sctlr_el1 &= ~(
/* Explicit data accesses at EL0 are little-endian. */
SCTLR_E0E_BIT |
/*
* Alignment fault checking disabled when at EL1 and EL0 as
* the UEFI spec permits unaligned accesses.
*/
SCTLR_A_BIT |
/* Accesses to DAIF from EL0 are trapped to EL1. */
SCTLR_UMA_BIT
);
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_SCTLR_EL1, sctlr_el1);
}
static void spmc_el0_sp_setup_system_registers(struct secure_partition_desc *sp,
cpu_context_t *ctx)
{
spmc_el0_sp_setup_mmu(sp, ctx);
spmc_el0_sp_setup_sctlr_el1(ctx);
/* Setup other system registers. */
/* Shim Exception Vector Base Address */
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_VBAR_EL1,
SPM_SHIM_EXCEPTIONS_PTR);
#if NS_TIMER_SWITCH
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_CNTKCTL_EL1,
EL0PTEN_BIT | EL0VTEN_BIT | EL0PCTEN_BIT | EL0VCTEN_BIT);
#endif
/*
* FPEN: Allow the Secure Partition to access FP/SIMD registers.
* Note that SPM will not do any saving/restoring of these registers on
* behalf of the SP. This falls under the SP's responsibility.
* TTA: Enable access to trace registers.
* ZEN (v8.2): Trap SVE instructions and access to SVE registers.
*/
write_ctx_reg(get_el1_sysregs_ctx(ctx), CTX_CPACR_EL1,
CPACR_EL1_FPEN(CPACR_EL1_FP_TRAP_NONE));
}
/* Setup context of an EL0 Secure Partition. */
void spmc_el0_sp_setup(struct secure_partition_desc *sp,
int32_t boot_info_reg,
void *sp_manifest)
{
mmap_region_t sel1_exception_vectors =
MAP_REGION_FLAT(SPM_SHIM_EXCEPTIONS_START,
SPM_SHIM_EXCEPTIONS_SIZE,
MT_CODE | MT_SECURE | MT_PRIVILEGED);
cpu_context_t *ctx;
int node;
int offset = 0;
ctx = &sp->ec[SEL0_SP_EC_INDEX].cpu_ctx;
sp->xlat_ctx_handle->xlat_regime = EL1_EL0_REGIME;
/* This region contains the exception vectors used at S-EL1. */
mmap_add_region_ctx(sp->xlat_ctx_handle,
&sel1_exception_vectors);
/*
* If the SP manifest specified the register to pass the address of the
* boot information, then map the memory region to pass boot
* information.
*/
if (boot_info_reg >= 0) {
mmap_region_t ffa_boot_info_region = MAP_REGION_FLAT(
(uintptr_t) ffa_boot_info_mem,
PAGE_SIZE,
MT_RO_DATA | MT_SECURE | MT_USER);
mmap_add_region_ctx(sp->xlat_ctx_handle, &ffa_boot_info_region);
}
/*
* Parse the manifest for any memory regions that the SP wants to be
* mapped in its translation regime.
*/
node = fdt_subnode_offset_namelen(sp_manifest, offset,
"memory-regions",
sizeof("memory-regions") - 1);
if (node < 0) {
WARN("Not found memory-region configuration for SP.\n");
} else {
populate_sp_mem_regions(sp, sp_manifest, node);
}
spmc_el0_sp_setup_system_registers(sp, ctx);
}
#endif /* SPMC_AT_EL3_SEL0_SP */
/* S-EL1 partition specific initialisation. */
void spmc_el1_sp_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
{
/* Sanity check input arguments. */
assert(sp != NULL);
assert(ep_info != NULL);
/* Initialise the SPSR for S-EL1 SPs. */
ep_info->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS);
/*
* TF-A Implementation defined behaviour to provide the linear
* core ID in the x4 register.
*/
ep_info->args.arg4 = (uintptr_t) plat_my_core_pos();
/*
* Check whether setup is being performed for the primary or a secondary
* execution context. In the latter case, indicate to the SP that this
* is a warm boot.
* TODO: This check would need to be reworked if the same entry point is
* used for both primary and secondary initialisation.
*/
if (sp->secondary_ep != 0U) {
/*
* Sanity check that the secondary entry point is still what was
* originally set.
*/
assert(sp->secondary_ep == ep_info->pc);
ep_info->args.arg0 = FFA_WB_TYPE_S2RAM;
}
}
/* Common initialisation for all SPs. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info,
int32_t boot_info_reg)
{
uint16_t sp_id;
/* Assign FF-A Partition ID if not already assigned. */
if (sp->sp_id == INV_SP_ID) {
sp_id = FFA_SP_ID_BASE + ACTIVE_SP_DESC_INDEX;
/*
* Ensure we don't clash with previously assigned partition
* IDs.
*/
while (!is_ffa_secure_id_valid(sp_id)) {
sp_id++;
if (sp_id == FFA_SWD_ID_LIMIT) {
ERROR("Unable to determine valid SP ID.\n");
panic();
}
}
sp->sp_id = sp_id;
}
/* 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));
}
}
/*
* Initialise the SP context now we have populated the common and EL specific
* entrypoint information.
*/
void spmc_sp_common_ep_commit(struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
{
cpu_context_t *cpu_ctx;
cpu_ctx = &(spmc_get_sp_ec(sp)->cpu_ctx);
print_entry_point_info(ep_info);
cm_setup_context(cpu_ctx, ep_info);
}