mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-18 02:24:18 +00:00

Add header file to help with creation of SPMD logical partitions. Also update linker files to create sections to record SPMD logical partitions declared. This follows the same pattern as the EL3 SPMC's logical partitions. This patch also adds initialization of SPMD logical partitions when the SPMD comes up. ENABLE_SPMD_LP is a build flag that is used to enable support for SPMD logical partitions. Note that the approach chosen is to keep SPMD and SPMC logical partition support separate, as opposed to extend the existing SPMC logical partition support since the code would need to have a number of ifdefs and the interactions with various build options such as SPMC_AT_EL3 needs to be accounted for, which would make code more complicated. Signed-off-by: Raghu Krishnamurthy <raghu.ncstate@gmail.com> Change-Id: I9642ddbf6ea26dd3f4a283baec598d61c07e3661
1209 lines
36 KiB
C
1209 lines
36 KiB
C
/*
|
|
* Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include <arch_helpers.h>
|
|
#include <arch/aarch64/arch_features.h>
|
|
#include <bl31/bl31.h>
|
|
#include <bl31/interrupt_mgmt.h>
|
|
#include <common/debug.h>
|
|
#include <common/runtime_svc.h>
|
|
#include <common/tbbr/tbbr_img_def.h>
|
|
#include <lib/el3_runtime/context_mgmt.h>
|
|
#include <lib/fconf/fconf.h>
|
|
#include <lib/fconf/fconf_dyn_cfg_getter.h>
|
|
#include <lib/smccc.h>
|
|
#include <lib/spinlock.h>
|
|
#include <lib/utils.h>
|
|
#include <lib/xlat_tables/xlat_tables_v2.h>
|
|
#include <plat/common/common_def.h>
|
|
#include <plat/common/platform.h>
|
|
#include <platform_def.h>
|
|
#include <services/el3_spmd_logical_sp.h>
|
|
#include <services/ffa_svc.h>
|
|
#include <services/spmc_svc.h>
|
|
#include <services/spmd_svc.h>
|
|
#include <smccc_helpers.h>
|
|
#include "spmd_private.h"
|
|
|
|
/*******************************************************************************
|
|
* SPM Core context information.
|
|
******************************************************************************/
|
|
static spmd_spm_core_context_t spm_core_context[PLATFORM_CORE_COUNT];
|
|
|
|
/*******************************************************************************
|
|
* SPM Core attribute information is read from its manifest if the SPMC is not
|
|
* at EL3. Else, it is populated from the SPMC directly.
|
|
******************************************************************************/
|
|
static spmc_manifest_attribute_t spmc_attrs;
|
|
|
|
/*******************************************************************************
|
|
* SPM Core entry point information. Discovered on the primary core and reused
|
|
* on secondary cores.
|
|
******************************************************************************/
|
|
static entry_point_info_t *spmc_ep_info;
|
|
|
|
/*******************************************************************************
|
|
* SPM Core context on CPU based on mpidr.
|
|
******************************************************************************/
|
|
spmd_spm_core_context_t *spmd_get_context_by_mpidr(uint64_t mpidr)
|
|
{
|
|
int core_idx = plat_core_pos_by_mpidr(mpidr);
|
|
|
|
if (core_idx < 0) {
|
|
ERROR("Invalid mpidr: %" PRIx64 ", returned ID: %d\n", mpidr, core_idx);
|
|
panic();
|
|
}
|
|
|
|
return &spm_core_context[core_idx];
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* SPM Core context on current CPU get helper.
|
|
******************************************************************************/
|
|
spmd_spm_core_context_t *spmd_get_context(void)
|
|
{
|
|
return spmd_get_context_by_mpidr(read_mpidr());
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* SPM Core ID getter.
|
|
******************************************************************************/
|
|
uint16_t spmd_spmc_id_get(void)
|
|
{
|
|
return spmc_attrs.spmc_id;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Static function declaration.
|
|
******************************************************************************/
|
|
static int32_t spmd_init(void);
|
|
static int spmd_spmc_init(void *pm_addr);
|
|
static uint64_t spmd_ffa_error_return(void *handle,
|
|
int error_code);
|
|
static uint64_t spmd_smc_forward(uint32_t smc_fid,
|
|
bool secure_origin,
|
|
uint64_t x1,
|
|
uint64_t x2,
|
|
uint64_t x3,
|
|
uint64_t x4,
|
|
void *cookie,
|
|
void *handle,
|
|
uint64_t flags);
|
|
|
|
/******************************************************************************
|
|
* Builds an SPMD to SPMC direct message request.
|
|
*****************************************************************************/
|
|
void spmd_build_spmc_message(gp_regs_t *gpregs, uint8_t target_func,
|
|
unsigned long long message)
|
|
{
|
|
write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X1,
|
|
(SPMD_DIRECT_MSG_ENDPOINT_ID << FFA_DIRECT_MSG_SOURCE_SHIFT) |
|
|
spmd_spmc_id_get());
|
|
write_ctx_reg(gpregs, CTX_GPREG_X2, BIT(31) | target_func);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X3, message);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* This function takes an SPMC context pointer and performs a synchronous
|
|
* SPMC entry.
|
|
******************************************************************************/
|
|
uint64_t spmd_spm_core_sync_entry(spmd_spm_core_context_t *spmc_ctx)
|
|
{
|
|
uint64_t rc;
|
|
|
|
assert(spmc_ctx != NULL);
|
|
|
|
cm_set_context(&(spmc_ctx->cpu_ctx), SECURE);
|
|
|
|
/* Restore the context assigned above */
|
|
#if SPMD_SPM_AT_SEL2
|
|
cm_el2_sysregs_context_restore(SECURE);
|
|
#else
|
|
cm_el1_sysregs_context_restore(SECURE);
|
|
#endif
|
|
cm_set_next_eret_context(SECURE);
|
|
|
|
/* Enter SPMC */
|
|
rc = spmd_spm_core_enter(&spmc_ctx->c_rt_ctx);
|
|
|
|
/* Save secure state */
|
|
#if SPMD_SPM_AT_SEL2
|
|
cm_el2_sysregs_context_save(SECURE);
|
|
#else
|
|
cm_el1_sysregs_context_save(SECURE);
|
|
#endif
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* This function returns to the place where spmd_spm_core_sync_entry() was
|
|
* called originally.
|
|
******************************************************************************/
|
|
__dead2 void spmd_spm_core_sync_exit(uint64_t rc)
|
|
{
|
|
spmd_spm_core_context_t *ctx = spmd_get_context();
|
|
|
|
/* Get current CPU context from SPMC context */
|
|
assert(cm_get_context(SECURE) == &(ctx->cpu_ctx));
|
|
|
|
/*
|
|
* The SPMD must have initiated the original request through a
|
|
* synchronous entry into SPMC. Jump back to the original C runtime
|
|
* context with the value of rc in x0;
|
|
*/
|
|
spmd_spm_core_exit(ctx->c_rt_ctx, rc);
|
|
|
|
panic();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Jump to the SPM Core for the first time.
|
|
******************************************************************************/
|
|
static int32_t spmd_init(void)
|
|
{
|
|
spmd_spm_core_context_t *ctx = spmd_get_context();
|
|
uint64_t rc;
|
|
|
|
VERBOSE("SPM Core init start.\n");
|
|
|
|
/* Primary boot core enters the SPMC for initialization. */
|
|
ctx->state = SPMC_STATE_ON_PENDING;
|
|
|
|
rc = spmd_spm_core_sync_entry(ctx);
|
|
if (rc != 0ULL) {
|
|
ERROR("SPMC initialisation failed 0x%" PRIx64 "\n", rc);
|
|
return 0;
|
|
}
|
|
|
|
ctx->state = SPMC_STATE_ON;
|
|
|
|
VERBOSE("SPM Core init end.\n");
|
|
|
|
spmd_logical_sp_set_spmc_initialized();
|
|
rc = spmd_logical_sp_init();
|
|
if (rc != 0) {
|
|
WARN("SPMD Logical partitions failed init.\n");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* spmd_secure_interrupt_handler
|
|
* Enter the SPMC for further handling of the secure interrupt by the SPMC
|
|
* itself or a Secure Partition.
|
|
******************************************************************************/
|
|
static uint64_t spmd_secure_interrupt_handler(uint32_t id,
|
|
uint32_t flags,
|
|
void *handle,
|
|
void *cookie)
|
|
{
|
|
spmd_spm_core_context_t *ctx = spmd_get_context();
|
|
gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx);
|
|
unsigned int linear_id = plat_my_core_pos();
|
|
int64_t rc;
|
|
|
|
/* Sanity check the security state when the exception was generated */
|
|
assert(get_interrupt_src_ss(flags) == NON_SECURE);
|
|
|
|
/* Sanity check the pointer to this cpu's context */
|
|
assert(handle == cm_get_context(NON_SECURE));
|
|
|
|
/* Save the non-secure context before entering SPMC */
|
|
cm_el1_sysregs_context_save(NON_SECURE);
|
|
#if SPMD_SPM_AT_SEL2
|
|
cm_el2_sysregs_context_save(NON_SECURE);
|
|
#endif
|
|
|
|
/* Convey the event to the SPMC through the FFA_INTERRUPT interface. */
|
|
write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_INTERRUPT);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X1, 0);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X2, 0);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X3, 0);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X4, 0);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X5, 0);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X6, 0);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X7, 0);
|
|
|
|
/* Mark current core as handling a secure interrupt. */
|
|
ctx->secure_interrupt_ongoing = true;
|
|
|
|
rc = spmd_spm_core_sync_entry(ctx);
|
|
if (rc != 0ULL) {
|
|
ERROR("%s failed (%" PRId64 ") on CPU%u\n", __func__, rc, linear_id);
|
|
}
|
|
|
|
ctx->secure_interrupt_ongoing = false;
|
|
|
|
cm_el1_sysregs_context_restore(NON_SECURE);
|
|
#if SPMD_SPM_AT_SEL2
|
|
cm_el2_sysregs_context_restore(NON_SECURE);
|
|
#endif
|
|
cm_set_next_eret_context(NON_SECURE);
|
|
|
|
SMC_RET0(&ctx->cpu_ctx);
|
|
}
|
|
|
|
#if (EL3_EXCEPTION_HANDLING == 0)
|
|
/*******************************************************************************
|
|
* spmd_group0_interrupt_handler_nwd
|
|
* Group0 secure interrupt in the normal world are trapped to EL3. Delegate the
|
|
* handling of the interrupt to the platform handler, and return only upon
|
|
* successfully handling the Group0 interrupt.
|
|
******************************************************************************/
|
|
static uint64_t spmd_group0_interrupt_handler_nwd(uint32_t id,
|
|
uint32_t flags,
|
|
void *handle,
|
|
void *cookie)
|
|
{
|
|
uint32_t intid;
|
|
|
|
/* Sanity check the security state when the exception was generated. */
|
|
assert(get_interrupt_src_ss(flags) == NON_SECURE);
|
|
|
|
/* Sanity check the pointer to this cpu's context. */
|
|
assert(handle == cm_get_context(NON_SECURE));
|
|
|
|
assert(id == INTR_ID_UNAVAILABLE);
|
|
|
|
assert(plat_ic_get_pending_interrupt_type() == INTR_TYPE_EL3);
|
|
|
|
intid = plat_ic_acknowledge_interrupt();
|
|
|
|
if (plat_spmd_handle_group0_interrupt(intid) < 0) {
|
|
ERROR("Group0 interrupt %u not handled\n", intid);
|
|
panic();
|
|
}
|
|
|
|
/* Deactivate the corresponding Group0 interrupt. */
|
|
plat_ic_end_of_interrupt(intid);
|
|
|
|
return 0U;
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* spmd_handle_group0_intr_swd
|
|
* SPMC delegates handling of Group0 secure interrupt to EL3 firmware using
|
|
* FFA_EL3_INTR_HANDLE SMC call. Further, SPMD delegates the handling of the
|
|
* interrupt to the platform handler, and returns only upon successfully
|
|
* handling the Group0 interrupt.
|
|
******************************************************************************/
|
|
static uint64_t spmd_handle_group0_intr_swd(void *handle)
|
|
{
|
|
uint32_t intid;
|
|
|
|
/* Sanity check the pointer to this cpu's context */
|
|
assert(handle == cm_get_context(SECURE));
|
|
|
|
assert(plat_ic_get_pending_interrupt_type() == INTR_TYPE_EL3);
|
|
|
|
intid = plat_ic_acknowledge_interrupt();
|
|
|
|
/*
|
|
* TODO: Currently due to a limitation in SPMD implementation, the
|
|
* platform handler is expected to not delegate handling to NWd while
|
|
* processing Group0 secure interrupt.
|
|
*/
|
|
if (plat_spmd_handle_group0_interrupt(intid) < 0) {
|
|
/* Group0 interrupt was not handled by the platform. */
|
|
ERROR("Group0 interrupt %u not handled\n", intid);
|
|
panic();
|
|
}
|
|
|
|
/* Deactivate the corresponding Group0 interrupt. */
|
|
plat_ic_end_of_interrupt(intid);
|
|
|
|
/* Return success. */
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC32, FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
}
|
|
|
|
#if ENABLE_RME && SPMD_SPM_AT_SEL2 && !RESET_TO_BL31
|
|
static int spmd_dynamic_map_mem(uintptr_t base_addr, size_t size,
|
|
unsigned int attr, uintptr_t *align_addr,
|
|
size_t *align_size)
|
|
{
|
|
uintptr_t base_addr_align;
|
|
size_t mapped_size_align;
|
|
int rc;
|
|
|
|
/* Page aligned address and size if necessary */
|
|
base_addr_align = page_align(base_addr, DOWN);
|
|
mapped_size_align = page_align(size, UP);
|
|
|
|
if ((base_addr != base_addr_align) &&
|
|
(size == mapped_size_align)) {
|
|
mapped_size_align += PAGE_SIZE;
|
|
}
|
|
|
|
/*
|
|
* Map dynamically given region with its aligned base address and
|
|
* size
|
|
*/
|
|
rc = mmap_add_dynamic_region((unsigned long long)base_addr_align,
|
|
base_addr_align,
|
|
mapped_size_align,
|
|
attr);
|
|
if (rc == 0) {
|
|
*align_addr = base_addr_align;
|
|
*align_size = mapped_size_align;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void spmd_do_sec_cpy(uintptr_t root_base_addr, uintptr_t sec_base_addr,
|
|
size_t size)
|
|
{
|
|
uintptr_t root_base_addr_align, sec_base_addr_align;
|
|
size_t root_mapped_size_align, sec_mapped_size_align;
|
|
int rc;
|
|
|
|
assert(root_base_addr != 0UL);
|
|
assert(sec_base_addr != 0UL);
|
|
assert(size != 0UL);
|
|
|
|
/* Map the memory with required attributes */
|
|
rc = spmd_dynamic_map_mem(root_base_addr, size, MT_RO_DATA | MT_ROOT,
|
|
&root_base_addr_align,
|
|
&root_mapped_size_align);
|
|
if (rc != 0) {
|
|
ERROR("%s %s %lu (%d)\n", "Error while mapping", "root region",
|
|
root_base_addr, rc);
|
|
panic();
|
|
}
|
|
|
|
rc = spmd_dynamic_map_mem(sec_base_addr, size, MT_RW_DATA | MT_SECURE,
|
|
&sec_base_addr_align, &sec_mapped_size_align);
|
|
if (rc != 0) {
|
|
ERROR("%s %s %lu (%d)\n", "Error while mapping",
|
|
"secure region", sec_base_addr, rc);
|
|
panic();
|
|
}
|
|
|
|
/* Do copy operation */
|
|
(void)memcpy((void *)sec_base_addr, (void *)root_base_addr, size);
|
|
|
|
/* Unmap root memory region */
|
|
rc = mmap_remove_dynamic_region(root_base_addr_align,
|
|
root_mapped_size_align);
|
|
if (rc != 0) {
|
|
ERROR("%s %s %lu (%d)\n", "Error while unmapping",
|
|
"root region", root_base_addr_align, rc);
|
|
panic();
|
|
}
|
|
|
|
/* Unmap secure memory region */
|
|
rc = mmap_remove_dynamic_region(sec_base_addr_align,
|
|
sec_mapped_size_align);
|
|
if (rc != 0) {
|
|
ERROR("%s %s %lu (%d)\n", "Error while unmapping",
|
|
"secure region", sec_base_addr_align, rc);
|
|
panic();
|
|
}
|
|
}
|
|
#endif /* ENABLE_RME && SPMD_SPM_AT_SEL2 && !RESET_TO_BL31 */
|
|
|
|
/*******************************************************************************
|
|
* Loads SPMC manifest and inits SPMC.
|
|
******************************************************************************/
|
|
static int spmd_spmc_init(void *pm_addr)
|
|
{
|
|
cpu_context_t *cpu_ctx;
|
|
unsigned int core_id;
|
|
uint32_t ep_attr, flags;
|
|
int rc;
|
|
const struct dyn_cfg_dtb_info_t *image_info __unused;
|
|
|
|
/* Load the SPM Core manifest */
|
|
rc = plat_spm_core_manifest_load(&spmc_attrs, pm_addr);
|
|
if (rc != 0) {
|
|
WARN("No or invalid SPM Core manifest image provided by BL2\n");
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Ensure that the SPM Core version is compatible with the SPM
|
|
* Dispatcher version.
|
|
*/
|
|
if ((spmc_attrs.major_version != FFA_VERSION_MAJOR) ||
|
|
(spmc_attrs.minor_version > FFA_VERSION_MINOR)) {
|
|
WARN("Unsupported FFA version (%u.%u)\n",
|
|
spmc_attrs.major_version, spmc_attrs.minor_version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
VERBOSE("FFA version (%u.%u)\n", spmc_attrs.major_version,
|
|
spmc_attrs.minor_version);
|
|
|
|
VERBOSE("SPM Core run time EL%x.\n",
|
|
SPMD_SPM_AT_SEL2 ? MODE_EL2 : MODE_EL1);
|
|
|
|
/* Validate the SPMC ID, Ensure high bit is set */
|
|
if (((spmc_attrs.spmc_id >> SPMC_SECURE_ID_SHIFT) &
|
|
SPMC_SECURE_ID_MASK) == 0U) {
|
|
WARN("Invalid ID (0x%x) for SPMC.\n", spmc_attrs.spmc_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Validate the SPM Core execution state */
|
|
if ((spmc_attrs.exec_state != MODE_RW_64) &&
|
|
(spmc_attrs.exec_state != MODE_RW_32)) {
|
|
WARN("Unsupported %s%x.\n", "SPM Core execution state 0x",
|
|
spmc_attrs.exec_state);
|
|
return -EINVAL;
|
|
}
|
|
|
|
VERBOSE("%s%x.\n", "SPM Core execution state 0x",
|
|
spmc_attrs.exec_state);
|
|
|
|
#if SPMD_SPM_AT_SEL2
|
|
/* Ensure manifest has not requested AArch32 state in S-EL2 */
|
|
if (spmc_attrs.exec_state == MODE_RW_32) {
|
|
WARN("AArch32 state at S-EL2 is not supported.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Check if S-EL2 is supported on this system if S-EL2
|
|
* is required for SPM
|
|
*/
|
|
if (!is_feat_sel2_supported()) {
|
|
WARN("SPM Core run time S-EL2 is not supported.\n");
|
|
return -EINVAL;
|
|
}
|
|
#endif /* SPMD_SPM_AT_SEL2 */
|
|
|
|
/* Initialise an entrypoint to set up the CPU context */
|
|
ep_attr = SECURE | EP_ST_ENABLE;
|
|
if ((read_sctlr_el3() & SCTLR_EE_BIT) != 0ULL) {
|
|
ep_attr |= EP_EE_BIG;
|
|
}
|
|
|
|
SET_PARAM_HEAD(spmc_ep_info, PARAM_EP, VERSION_1, ep_attr);
|
|
|
|
/*
|
|
* Populate SPSR for SPM Core based upon validated parameters from the
|
|
* manifest.
|
|
*/
|
|
if (spmc_attrs.exec_state == MODE_RW_32) {
|
|
spmc_ep_info->spsr = SPSR_MODE32(MODE32_svc, SPSR_T_ARM,
|
|
SPSR_E_LITTLE,
|
|
DAIF_FIQ_BIT |
|
|
DAIF_IRQ_BIT |
|
|
DAIF_ABT_BIT);
|
|
} else {
|
|
|
|
#if SPMD_SPM_AT_SEL2
|
|
static const uint32_t runtime_el = MODE_EL2;
|
|
#else
|
|
static const uint32_t runtime_el = MODE_EL1;
|
|
#endif
|
|
spmc_ep_info->spsr = SPSR_64(runtime_el,
|
|
MODE_SP_ELX,
|
|
DISABLE_ALL_EXCEPTIONS);
|
|
}
|
|
|
|
#if ENABLE_RME && SPMD_SPM_AT_SEL2 && !RESET_TO_BL31
|
|
image_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TOS_FW_CONFIG_ID);
|
|
assert(image_info != NULL);
|
|
|
|
if ((image_info->config_addr == 0UL) ||
|
|
(image_info->secondary_config_addr == 0UL) ||
|
|
(image_info->config_max_size == 0UL)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Copy manifest from root->secure region */
|
|
spmd_do_sec_cpy(image_info->config_addr,
|
|
image_info->secondary_config_addr,
|
|
image_info->config_max_size);
|
|
|
|
/* Update ep info of BL32 */
|
|
assert(spmc_ep_info != NULL);
|
|
spmc_ep_info->args.arg0 = image_info->secondary_config_addr;
|
|
#endif /* ENABLE_RME && SPMD_SPM_AT_SEL2 && !RESET_TO_BL31 */
|
|
|
|
/* Set an initial SPMC context state for all cores. */
|
|
for (core_id = 0U; core_id < PLATFORM_CORE_COUNT; core_id++) {
|
|
spm_core_context[core_id].state = SPMC_STATE_OFF;
|
|
|
|
/* Setup an initial cpu context for the SPMC. */
|
|
cpu_ctx = &spm_core_context[core_id].cpu_ctx;
|
|
cm_setup_context(cpu_ctx, spmc_ep_info);
|
|
|
|
/*
|
|
* Pass the core linear ID to the SPMC through x4.
|
|
* (TF-A implementation defined behavior helping
|
|
* a legacy TOS migration to adopt FF-A).
|
|
*/
|
|
write_ctx_reg(get_gpregs_ctx(cpu_ctx), CTX_GPREG_X4, core_id);
|
|
}
|
|
|
|
/* Register power management hooks with PSCI */
|
|
psci_register_spd_pm_hook(&spmd_pm);
|
|
|
|
/* Register init function for deferred init. */
|
|
bl31_register_bl32_init(&spmd_init);
|
|
|
|
INFO("SPM Core setup done.\n");
|
|
|
|
/*
|
|
* Register an interrupt handler routing secure interrupts to SPMD
|
|
* while the NWd is running.
|
|
*/
|
|
flags = 0;
|
|
set_interrupt_rm_flag(flags, NON_SECURE);
|
|
rc = register_interrupt_type_handler(INTR_TYPE_S_EL1,
|
|
spmd_secure_interrupt_handler,
|
|
flags);
|
|
if (rc != 0) {
|
|
panic();
|
|
}
|
|
|
|
/*
|
|
* Permit configurations where the SPM resides at S-EL1/2 and upon a
|
|
* Group0 interrupt triggering while the normal world runs, the
|
|
* interrupt is routed either through the EHF or directly to the SPMD:
|
|
*
|
|
* EL3_EXCEPTION_HANDLING=0: the Group0 interrupt is routed to the SPMD
|
|
* for handling by spmd_group0_interrupt_handler_nwd.
|
|
*
|
|
* EL3_EXCEPTION_HANDLING=1: the Group0 interrupt is routed to the EHF.
|
|
*
|
|
*/
|
|
#if (EL3_EXCEPTION_HANDLING == 0)
|
|
/*
|
|
* Register an interrupt handler routing Group0 interrupts to SPMD
|
|
* while the NWd is running.
|
|
*/
|
|
rc = register_interrupt_type_handler(INTR_TYPE_EL3,
|
|
spmd_group0_interrupt_handler_nwd,
|
|
flags);
|
|
if (rc != 0) {
|
|
panic();
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Initialize context of SPM Core.
|
|
******************************************************************************/
|
|
int spmd_setup(void)
|
|
{
|
|
int rc;
|
|
void *spmc_manifest;
|
|
|
|
/*
|
|
* If the SPMC is at EL3, then just initialise it directly. The
|
|
* shenanigans of when it is at a lower EL are not needed.
|
|
*/
|
|
if (is_spmc_at_el3()) {
|
|
/* Allow the SPMC to populate its attributes directly. */
|
|
spmc_populate_attrs(&spmc_attrs);
|
|
|
|
rc = spmc_setup();
|
|
if (rc != 0) {
|
|
WARN("SPMC initialisation failed 0x%x.\n", rc);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
spmc_ep_info = bl31_plat_get_next_image_ep_info(SECURE);
|
|
if (spmc_ep_info == NULL) {
|
|
WARN("No SPM Core image provided by BL2 boot loader.\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Under no circumstances will this parameter be 0 */
|
|
assert(spmc_ep_info->pc != 0ULL);
|
|
|
|
/*
|
|
* Check if BL32 ep_info has a reference to 'tos_fw_config'. This will
|
|
* be used as a manifest for the SPM Core at the next lower EL/mode.
|
|
*/
|
|
spmc_manifest = (void *)spmc_ep_info->args.arg0;
|
|
if (spmc_manifest == NULL) {
|
|
WARN("Invalid or absent SPM Core manifest.\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Load manifest, init SPMC */
|
|
rc = spmd_spmc_init(spmc_manifest);
|
|
if (rc != 0) {
|
|
WARN("Booting device without SPM initialization.\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Forward FF-A SMCs to the other security state.
|
|
******************************************************************************/
|
|
uint64_t spmd_smc_switch_state(uint32_t smc_fid,
|
|
bool secure_origin,
|
|
uint64_t x1,
|
|
uint64_t x2,
|
|
uint64_t x3,
|
|
uint64_t x4,
|
|
void *handle)
|
|
{
|
|
unsigned int secure_state_in = (secure_origin) ? SECURE : NON_SECURE;
|
|
unsigned int secure_state_out = (!secure_origin) ? SECURE : NON_SECURE;
|
|
|
|
/* Save incoming security state */
|
|
#if SPMD_SPM_AT_SEL2
|
|
if (secure_state_in == NON_SECURE) {
|
|
cm_el1_sysregs_context_save(secure_state_in);
|
|
}
|
|
cm_el2_sysregs_context_save(secure_state_in);
|
|
#else
|
|
cm_el1_sysregs_context_save(secure_state_in);
|
|
#endif
|
|
|
|
/* Restore outgoing security state */
|
|
#if SPMD_SPM_AT_SEL2
|
|
if (secure_state_out == NON_SECURE) {
|
|
cm_el1_sysregs_context_restore(secure_state_out);
|
|
}
|
|
cm_el2_sysregs_context_restore(secure_state_out);
|
|
#else
|
|
cm_el1_sysregs_context_restore(secure_state_out);
|
|
#endif
|
|
cm_set_next_eret_context(secure_state_out);
|
|
|
|
#if SPMD_SPM_AT_SEL2
|
|
/*
|
|
* If SPMC is at SEL2, save additional registers x8-x17, which may
|
|
* be used in FF-A calls such as FFA_PARTITION_INFO_GET_REGS.
|
|
* Note that technically, all SPMCs can support this, but this code is
|
|
* under ifdef to minimize breakage in case other SPMCs do not save
|
|
* and restore x8-x17.
|
|
* We also need to pass through these registers since not all FF-A ABIs
|
|
* modify x8-x17, in which case, SMCCC requires that these registers be
|
|
* preserved, so the SPMD passes through these registers and expects the
|
|
* SPMC to save and restore (potentially also modify) them.
|
|
*/
|
|
SMC_RET18(cm_get_context(secure_state_out), smc_fid, x1, x2, x3, x4,
|
|
SMC_GET_GP(handle, CTX_GPREG_X5),
|
|
SMC_GET_GP(handle, CTX_GPREG_X6),
|
|
SMC_GET_GP(handle, CTX_GPREG_X7),
|
|
SMC_GET_GP(handle, CTX_GPREG_X8),
|
|
SMC_GET_GP(handle, CTX_GPREG_X9),
|
|
SMC_GET_GP(handle, CTX_GPREG_X10),
|
|
SMC_GET_GP(handle, CTX_GPREG_X11),
|
|
SMC_GET_GP(handle, CTX_GPREG_X12),
|
|
SMC_GET_GP(handle, CTX_GPREG_X13),
|
|
SMC_GET_GP(handle, CTX_GPREG_X14),
|
|
SMC_GET_GP(handle, CTX_GPREG_X15),
|
|
SMC_GET_GP(handle, CTX_GPREG_X16),
|
|
SMC_GET_GP(handle, CTX_GPREG_X17)
|
|
);
|
|
|
|
#else
|
|
SMC_RET8(cm_get_context(secure_state_out), smc_fid, x1, x2, x3, x4,
|
|
SMC_GET_GP(handle, CTX_GPREG_X5),
|
|
SMC_GET_GP(handle, CTX_GPREG_X6),
|
|
SMC_GET_GP(handle, CTX_GPREG_X7));
|
|
#endif
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Forward SMCs to the other security state.
|
|
******************************************************************************/
|
|
static uint64_t spmd_smc_forward(uint32_t smc_fid,
|
|
bool secure_origin,
|
|
uint64_t x1,
|
|
uint64_t x2,
|
|
uint64_t x3,
|
|
uint64_t x4,
|
|
void *cookie,
|
|
void *handle,
|
|
uint64_t flags)
|
|
{
|
|
if (is_spmc_at_el3() && !secure_origin) {
|
|
return spmc_smc_handler(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
cookie, handle, flags);
|
|
}
|
|
return spmd_smc_switch_state(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
handle);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Return FFA_ERROR with specified error code
|
|
******************************************************************************/
|
|
static uint64_t spmd_ffa_error_return(void *handle, int error_code)
|
|
{
|
|
SMC_RET8(handle, (uint32_t) FFA_ERROR,
|
|
FFA_TARGET_INFO_MBZ, (uint32_t)error_code,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* spmd_check_address_in_binary_image
|
|
******************************************************************************/
|
|
bool spmd_check_address_in_binary_image(uint64_t address)
|
|
{
|
|
assert(!check_uptr_overflow(spmc_attrs.load_address, spmc_attrs.binary_size));
|
|
|
|
return ((address >= spmc_attrs.load_address) &&
|
|
(address < (spmc_attrs.load_address + spmc_attrs.binary_size)));
|
|
}
|
|
|
|
/******************************************************************************
|
|
* spmd_is_spmc_message
|
|
*****************************************************************************/
|
|
static bool spmd_is_spmc_message(unsigned int ep)
|
|
{
|
|
if (is_spmc_at_el3()) {
|
|
return false;
|
|
}
|
|
|
|
return ((ffa_endpoint_destination(ep) == SPMD_DIRECT_MSG_ENDPOINT_ID)
|
|
&& (ffa_endpoint_source(ep) == spmc_attrs.spmc_id));
|
|
}
|
|
|
|
/******************************************************************************
|
|
* spmd_handle_spmc_message
|
|
*****************************************************************************/
|
|
static int spmd_handle_spmc_message(unsigned long long msg,
|
|
unsigned long long parm1, unsigned long long parm2,
|
|
unsigned long long parm3, unsigned long long parm4)
|
|
{
|
|
VERBOSE("%s %llx %llx %llx %llx %llx\n", __func__,
|
|
msg, parm1, parm2, parm3, parm4);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* This function forwards FF-A SMCs to either the main SPMD handler or the
|
|
* SPMC at EL3, depending on the origin security state, if enabled.
|
|
******************************************************************************/
|
|
uint64_t spmd_ffa_smc_handler(uint32_t smc_fid,
|
|
uint64_t x1,
|
|
uint64_t x2,
|
|
uint64_t x3,
|
|
uint64_t x4,
|
|
void *cookie,
|
|
void *handle,
|
|
uint64_t flags)
|
|
{
|
|
if (is_spmc_at_el3()) {
|
|
/*
|
|
* If we have an SPMC at EL3 allow handling of the SMC first.
|
|
* The SPMC will call back through to SPMD handler if required.
|
|
*/
|
|
if (is_caller_secure(flags)) {
|
|
return spmc_smc_handler(smc_fid,
|
|
is_caller_secure(flags),
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
}
|
|
}
|
|
return spmd_smc_handler(smc_fid, x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* This function handles all SMCs in the range reserved for FFA. Each call is
|
|
* either forwarded to the other security state or handled by the SPM dispatcher
|
|
******************************************************************************/
|
|
uint64_t spmd_smc_handler(uint32_t smc_fid,
|
|
uint64_t x1,
|
|
uint64_t x2,
|
|
uint64_t x3,
|
|
uint64_t x4,
|
|
void *cookie,
|
|
void *handle,
|
|
uint64_t flags)
|
|
{
|
|
unsigned int linear_id = plat_my_core_pos();
|
|
spmd_spm_core_context_t *ctx = spmd_get_context();
|
|
bool secure_origin;
|
|
int32_t ret;
|
|
uint32_t input_version;
|
|
|
|
/* Determine which security state this SMC originated from */
|
|
secure_origin = is_caller_secure(flags);
|
|
|
|
VERBOSE("SPM(%u): 0x%x 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64
|
|
" 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 "\n",
|
|
linear_id, smc_fid, x1, x2, x3, x4,
|
|
SMC_GET_GP(handle, CTX_GPREG_X5),
|
|
SMC_GET_GP(handle, CTX_GPREG_X6),
|
|
SMC_GET_GP(handle, CTX_GPREG_X7));
|
|
|
|
switch (smc_fid) {
|
|
case FFA_ERROR:
|
|
/*
|
|
* Check if this is the first invocation of this interface on
|
|
* this CPU. If so, then indicate that the SPM Core initialised
|
|
* unsuccessfully.
|
|
*/
|
|
if (secure_origin && (ctx->state == SPMC_STATE_ON_PENDING)) {
|
|
spmd_spm_core_sync_exit(x2);
|
|
}
|
|
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
break; /* not reached */
|
|
|
|
case FFA_VERSION:
|
|
input_version = (uint32_t)(0xFFFFFFFF & x1);
|
|
/*
|
|
* If caller is secure and SPMC was initialized,
|
|
* return FFA_VERSION of SPMD.
|
|
* If caller is non secure and SPMC was initialized,
|
|
* forward to the EL3 SPMC if enabled, otherwise return
|
|
* the SPMC version if implemented at a lower EL.
|
|
* Sanity check to "input_version".
|
|
* If the EL3 SPMC is enabled, ignore the SPMC state as
|
|
* this is not used.
|
|
*/
|
|
if ((input_version & FFA_VERSION_BIT31_MASK) ||
|
|
(!is_spmc_at_el3() && (ctx->state == SPMC_STATE_RESET))) {
|
|
ret = FFA_ERROR_NOT_SUPPORTED;
|
|
} else if (!secure_origin) {
|
|
if (is_spmc_at_el3()) {
|
|
/*
|
|
* Forward the call directly to the EL3 SPMC, if
|
|
* enabled, as we don't need to wrap the call in
|
|
* a direct request.
|
|
*/
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
}
|
|
|
|
gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx);
|
|
uint64_t rc;
|
|
|
|
if (spmc_attrs.major_version == 1 &&
|
|
spmc_attrs.minor_version == 0) {
|
|
ret = MAKE_FFA_VERSION(spmc_attrs.major_version,
|
|
spmc_attrs.minor_version);
|
|
SMC_RET8(handle, (uint32_t)ret,
|
|
FFA_TARGET_INFO_MBZ,
|
|
FFA_TARGET_INFO_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
break;
|
|
}
|
|
/* Save non-secure system registers context */
|
|
cm_el1_sysregs_context_save(NON_SECURE);
|
|
#if SPMD_SPM_AT_SEL2
|
|
cm_el2_sysregs_context_save(NON_SECURE);
|
|
#endif
|
|
|
|
/*
|
|
* The incoming request has FFA_VERSION as X0 smc_fid
|
|
* and requested version in x1. Prepare a direct request
|
|
* from SPMD to SPMC with FFA_VERSION framework function
|
|
* identifier in X2 and requested version in X3.
|
|
*/
|
|
spmd_build_spmc_message(gpregs,
|
|
SPMD_FWK_MSG_FFA_VERSION_REQ,
|
|
input_version);
|
|
|
|
rc = spmd_spm_core_sync_entry(ctx);
|
|
|
|
if ((rc != 0ULL) ||
|
|
(SMC_GET_GP(gpregs, CTX_GPREG_X0) !=
|
|
FFA_MSG_SEND_DIRECT_RESP_SMC32) ||
|
|
(SMC_GET_GP(gpregs, CTX_GPREG_X2) !=
|
|
(FFA_FWK_MSG_BIT |
|
|
SPMD_FWK_MSG_FFA_VERSION_RESP))) {
|
|
ERROR("Failed to forward FFA_VERSION\n");
|
|
ret = FFA_ERROR_NOT_SUPPORTED;
|
|
} else {
|
|
ret = SMC_GET_GP(gpregs, CTX_GPREG_X3);
|
|
}
|
|
|
|
/*
|
|
* Return here after SPMC has handled FFA_VERSION.
|
|
* The returned SPMC version is held in X3.
|
|
* Forward this version in X0 to the non-secure caller.
|
|
*/
|
|
return spmd_smc_forward(ret, true, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, cookie, gpregs,
|
|
flags);
|
|
} else {
|
|
ret = MAKE_FFA_VERSION(FFA_VERSION_MAJOR,
|
|
FFA_VERSION_MINOR);
|
|
}
|
|
|
|
SMC_RET8(handle, (uint32_t)ret, FFA_TARGET_INFO_MBZ,
|
|
FFA_TARGET_INFO_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ);
|
|
break; /* not reached */
|
|
|
|
case FFA_FEATURES:
|
|
/*
|
|
* This is an optional interface. Do the minimal checks and
|
|
* forward to SPM Core which will handle it if implemented.
|
|
*/
|
|
|
|
/* Forward SMC from Normal world to the SPM Core */
|
|
if (!secure_origin) {
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
}
|
|
|
|
/*
|
|
* Return success if call was from secure world i.e. all
|
|
* FFA functions are supported. This is essentially a
|
|
* nop.
|
|
*/
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC32, x1, x2, x3, x4,
|
|
SMC_GET_GP(handle, CTX_GPREG_X5),
|
|
SMC_GET_GP(handle, CTX_GPREG_X6),
|
|
SMC_GET_GP(handle, CTX_GPREG_X7));
|
|
|
|
break; /* not reached */
|
|
|
|
case FFA_ID_GET:
|
|
/*
|
|
* Returns the ID of the calling FFA component.
|
|
*/
|
|
if (!secure_origin) {
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC32,
|
|
FFA_TARGET_INFO_MBZ, FFA_NS_ENDPOINT_ID,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
}
|
|
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC32,
|
|
FFA_TARGET_INFO_MBZ, spmc_attrs.spmc_id,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
|
|
break; /* not reached */
|
|
|
|
case FFA_SECONDARY_EP_REGISTER_SMC64:
|
|
if (secure_origin) {
|
|
ret = spmd_pm_secondary_ep_register(x1);
|
|
|
|
if (ret < 0) {
|
|
SMC_RET8(handle, FFA_ERROR_SMC64,
|
|
FFA_TARGET_INFO_MBZ, ret,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
} else {
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC64,
|
|
FFA_TARGET_INFO_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
}
|
|
}
|
|
|
|
return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
break; /* Not reached */
|
|
|
|
case FFA_SPM_ID_GET:
|
|
if (MAKE_FFA_VERSION(1, 1) > FFA_VERSION_COMPILED) {
|
|
return spmd_ffa_error_return(handle,
|
|
FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
/*
|
|
* Returns the ID of the SPMC or SPMD depending on the FF-A
|
|
* instance where this function is invoked
|
|
*/
|
|
if (!secure_origin) {
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC32,
|
|
FFA_TARGET_INFO_MBZ, spmc_attrs.spmc_id,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
}
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC32,
|
|
FFA_TARGET_INFO_MBZ, SPMD_DIRECT_MSG_ENDPOINT_ID,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
|
|
break; /* not reached */
|
|
|
|
case FFA_MSG_SEND_DIRECT_REQ_SMC32:
|
|
case FFA_MSG_SEND_DIRECT_REQ_SMC64:
|
|
if (!secure_origin) {
|
|
/* Validate source endpoint is non-secure for non-secure caller. */
|
|
if (ffa_is_secure_world_id(ffa_endpoint_source(x1))) {
|
|
return spmd_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
if (secure_origin && spmd_is_spmc_message(x1)) {
|
|
ret = spmd_handle_spmc_message(x3, x4,
|
|
SMC_GET_GP(handle, CTX_GPREG_X5),
|
|
SMC_GET_GP(handle, CTX_GPREG_X6),
|
|
SMC_GET_GP(handle, CTX_GPREG_X7));
|
|
|
|
SMC_RET8(handle, FFA_SUCCESS_SMC32,
|
|
FFA_TARGET_INFO_MBZ, ret,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
|
FFA_PARAM_MBZ);
|
|
} else {
|
|
/* Forward direct message to the other world */
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
}
|
|
break; /* Not reached */
|
|
|
|
case FFA_MSG_SEND_DIRECT_RESP_SMC32:
|
|
if (secure_origin && spmd_is_spmc_message(x1)) {
|
|
spmd_spm_core_sync_exit(0ULL);
|
|
} else {
|
|
/* Forward direct message to the other world */
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
}
|
|
break; /* Not reached */
|
|
|
|
case FFA_RX_RELEASE:
|
|
case FFA_RXTX_MAP_SMC32:
|
|
case FFA_RXTX_MAP_SMC64:
|
|
case FFA_RXTX_UNMAP:
|
|
case FFA_PARTITION_INFO_GET:
|
|
#if MAKE_FFA_VERSION(1, 1) <= FFA_VERSION_COMPILED
|
|
case FFA_NOTIFICATION_BITMAP_CREATE:
|
|
case FFA_NOTIFICATION_BITMAP_DESTROY:
|
|
case FFA_NOTIFICATION_BIND:
|
|
case FFA_NOTIFICATION_UNBIND:
|
|
case FFA_NOTIFICATION_SET:
|
|
case FFA_NOTIFICATION_GET:
|
|
case FFA_NOTIFICATION_INFO_GET:
|
|
case FFA_NOTIFICATION_INFO_GET_SMC64:
|
|
case FFA_MSG_SEND2:
|
|
case FFA_RX_ACQUIRE:
|
|
#endif
|
|
case FFA_MSG_RUN:
|
|
/*
|
|
* Above calls should be invoked only by the Normal world and
|
|
* must not be forwarded from Secure world to Normal world.
|
|
*/
|
|
if (secure_origin) {
|
|
return spmd_ffa_error_return(handle,
|
|
FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
/* Forward the call to the other world */
|
|
/* fallthrough */
|
|
case FFA_MSG_SEND:
|
|
case FFA_MSG_SEND_DIRECT_RESP_SMC64:
|
|
case FFA_MEM_DONATE_SMC32:
|
|
case FFA_MEM_DONATE_SMC64:
|
|
case FFA_MEM_LEND_SMC32:
|
|
case FFA_MEM_LEND_SMC64:
|
|
case FFA_MEM_SHARE_SMC32:
|
|
case FFA_MEM_SHARE_SMC64:
|
|
case FFA_MEM_RETRIEVE_REQ_SMC32:
|
|
case FFA_MEM_RETRIEVE_REQ_SMC64:
|
|
case FFA_MEM_RETRIEVE_RESP:
|
|
case FFA_MEM_RELINQUISH:
|
|
case FFA_MEM_RECLAIM:
|
|
case FFA_MEM_FRAG_TX:
|
|
case FFA_MEM_FRAG_RX:
|
|
case FFA_SUCCESS_SMC32:
|
|
case FFA_SUCCESS_SMC64:
|
|
/*
|
|
* TODO: Assume that no requests originate from EL3 at the
|
|
* moment. This will change if a SP service is required in
|
|
* response to secure interrupts targeted to EL3. Until then
|
|
* simply forward the call to the Normal world.
|
|
*/
|
|
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
break; /* not reached */
|
|
|
|
case FFA_MSG_WAIT:
|
|
/*
|
|
* Check if this is the first invocation of this interface on
|
|
* this CPU from the Secure world. If so, then indicate that the
|
|
* SPM Core initialised successfully.
|
|
*/
|
|
if (secure_origin && (ctx->state == SPMC_STATE_ON_PENDING)) {
|
|
spmd_spm_core_sync_exit(0ULL);
|
|
}
|
|
|
|
/* Forward the call to the other world */
|
|
/* fallthrough */
|
|
case FFA_INTERRUPT:
|
|
case FFA_MSG_YIELD:
|
|
/* This interface must be invoked only by the Secure world */
|
|
if (!secure_origin) {
|
|
return spmd_ffa_error_return(handle,
|
|
FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
break; /* not reached */
|
|
|
|
case FFA_NORMAL_WORLD_RESUME:
|
|
if (secure_origin && ctx->secure_interrupt_ongoing) {
|
|
spmd_spm_core_sync_exit(0ULL);
|
|
} else {
|
|
return spmd_ffa_error_return(handle, FFA_ERROR_DENIED);
|
|
}
|
|
break; /* Not reached */
|
|
#if MAKE_FFA_VERSION(1, 1) <= FFA_VERSION_COMPILED
|
|
case FFA_PARTITION_INFO_GET_REGS_SMC64:
|
|
if (secure_origin) {
|
|
/* TODO: Future patches to enable support for this */
|
|
return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
/* Call only supported with SMCCC 1.2+ */
|
|
if (MAKE_SMCCC_VERSION(SMCCC_MAJOR_VERSION, SMCCC_MINOR_VERSION) < 0x10002) {
|
|
return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
return spmd_smc_forward(smc_fid, secure_origin,
|
|
x1, x2, x3, x4, cookie,
|
|
handle, flags);
|
|
break; /* Not reached */
|
|
#endif
|
|
case FFA_EL3_INTR_HANDLE:
|
|
if (secure_origin) {
|
|
return spmd_handle_group0_intr_swd(handle);
|
|
} else {
|
|
return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
default:
|
|
WARN("SPM: Unsupported call 0x%08x\n", smc_fid);
|
|
return spmd_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
}
|