feat(spmc): add FF-A secure partition manager core
This patch introduces the core support for enabling an SPMC in EL3 as per the FF-A spec. The current implemented functionality is targeted to enable initialization of the SPMC itself and initial support for bringing up a single S-EL1 SP. This includes initialization of the SPMC's internal state, parsing of an SP's manifest, preparing the cpu contexts and appropriate system registers for the Secure Partition. The spmc_smc_handler is the main handler for all incoming SMCs to the SPMC, FF-A ABI handlers and functionality will be implemented in subsequent patches. Signed-off-by: Marc Bonnici <marc.bonnici@arm.com> Change-Id: Ib33c240b91e54cbd018a69fec880d02adfbe12b9
This commit is contained in:
parent
70d986ddbb
commit
5096aeb2ba
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, Arm Limited. All rights reserved.
|
||||
* Copyright (c) 2020-2022, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
@ -7,6 +7,8 @@
|
|||
#ifndef FFA_SVC_H
|
||||
#define FFA_SVC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <lib/smccc.h>
|
||||
#include <lib/utils_def.h>
|
||||
#include <tools_share/uuid.h>
|
||||
|
@ -175,6 +177,15 @@
|
|||
*/
|
||||
#define FFA_ENDPOINT_ID_MAX U(1 << 16)
|
||||
|
||||
/*
|
||||
* Reserve endpoint id for the SPMD.
|
||||
*/
|
||||
#define SPMD_DIRECT_MSG_ENDPOINT_ID U(FFA_ENDPOINT_ID_MAX - 1)
|
||||
|
||||
/* Mask and shift to check valid secure FF-A Endpoint ID. */
|
||||
#define SPMC_SECURE_ID_MASK U(1)
|
||||
#define SPMC_SECURE_ID_SHIFT U(15)
|
||||
|
||||
/*
|
||||
* Mask for source and destination endpoint id in
|
||||
* a direct message request/response.
|
||||
|
@ -209,4 +220,24 @@ static inline uint16_t ffa_endpoint_source(unsigned int ep)
|
|||
FFA_DIRECT_MSG_ENDPOINT_ID_MASK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* FF-A helper functions to determine partition ID world.
|
||||
*****************************************************************************/
|
||||
|
||||
/*
|
||||
* Determine if provided ID is in the secure world.
|
||||
*/
|
||||
static inline bool ffa_is_secure_world_id(uint16_t id)
|
||||
{
|
||||
return ((id >> SPMC_SECURE_ID_SHIFT) & SPMC_SECURE_ID_MASK) == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine if provided ID is in the normal world.
|
||||
*/
|
||||
static inline bool ffa_is_normal_world_id(uint16_t id)
|
||||
{
|
||||
return !ffa_is_secure_world_id(id);
|
||||
}
|
||||
|
||||
#endif /* FFA_SVC_H */
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef SPMC_SVC_H
|
||||
#define SPMC_SVC_H
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lib/utils_def.h>
|
||||
#include <services/ffa_svc.h>
|
||||
|
||||
int spmc_setup(void);
|
||||
void *spmc_get_config_addr(void);
|
||||
|
||||
void spmc_set_config_addr(uintptr_t soc_fw_config);
|
||||
|
||||
uint64_t spmc_smc_handler(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);
|
||||
|
||||
static inline bool is_spmc_at_el3(void)
|
||||
{
|
||||
return SPMC_AT_EL3 == 1;
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
#endif /* SPMC_SVC_H */
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef SPMC_H
|
||||
#define SPMC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <lib/psci/psci.h>
|
||||
#include <lib/spinlock.h>
|
||||
#include "spm_common.h"
|
||||
|
||||
/*
|
||||
* Ranges of FF-A IDs for Normal world and Secure world components. The
|
||||
* convention matches that used by other SPMCs i.e. Hafnium and OP-TEE.
|
||||
*/
|
||||
#define FFA_NWD_ID_BASE 0x0
|
||||
#define FFA_NWD_ID_LIMIT 0x7FFF
|
||||
#define FFA_SWD_ID_BASE 0x8000
|
||||
#define FFA_SWD_ID_LIMIT SPMD_DIRECT_MSG_ENDPOINT_ID - 1
|
||||
#define FFA_SWD_ID_MASK 0x8000
|
||||
|
||||
/* First ID is reserved for the SPMC */
|
||||
#define FFA_SPMC_ID U(FFA_SWD_ID_BASE)
|
||||
/* SP IDs are allocated after the SPMC ID */
|
||||
#define FFA_SP_ID_BASE (FFA_SPMC_ID + 1)
|
||||
/* Align with Hafnium implementation */
|
||||
#define INV_SP_ID 0x7FFF
|
||||
|
||||
/* FF-A warm boot types. */
|
||||
#define FFA_WB_TYPE_S2RAM 0
|
||||
#define FFA_WB_TYPE_NOTS2RAM 1
|
||||
|
||||
/*
|
||||
* Runtime states of an execution context as per the FF-A v1.1 specification.
|
||||
*/
|
||||
enum sp_runtime_states {
|
||||
RT_STATE_WAITING,
|
||||
RT_STATE_RUNNING,
|
||||
RT_STATE_PREEMPTED,
|
||||
RT_STATE_BLOCKED
|
||||
};
|
||||
|
||||
/*
|
||||
* Runtime model of an execution context as per the FF-A v1.1 specification. Its
|
||||
* value is valid only if the execution context is not in the waiting state.
|
||||
*/
|
||||
enum sp_runtime_model {
|
||||
RT_MODEL_DIR_REQ,
|
||||
RT_MODEL_RUN,
|
||||
RT_MODEL_INIT,
|
||||
RT_MODEL_INTR
|
||||
};
|
||||
|
||||
enum sp_runtime_el {
|
||||
EL1 = 0,
|
||||
S_EL0,
|
||||
S_EL1
|
||||
};
|
||||
|
||||
enum sp_execution_state {
|
||||
SP_STATE_AARCH64 = 0,
|
||||
SP_STATE_AARCH32
|
||||
};
|
||||
|
||||
/*
|
||||
* Execution context members for an SP. This is a bit like struct
|
||||
* vcpu in a hypervisor.
|
||||
*/
|
||||
struct sp_exec_ctx {
|
||||
/*
|
||||
* Store the stack address to restore C runtime context from after
|
||||
* returning from a synchronous entry into the SP.
|
||||
*/
|
||||
uint64_t c_rt_ctx;
|
||||
|
||||
/* Space to maintain the architectural state of an SP. */
|
||||
cpu_context_t cpu_ctx;
|
||||
|
||||
/* Track the current runtime state of the SP. */
|
||||
enum sp_runtime_states rt_state;
|
||||
|
||||
/* Track the current runtime model of the SP. */
|
||||
enum sp_runtime_model rt_model;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure to describe the cumulative properties of an SP.
|
||||
*/
|
||||
struct secure_partition_desc {
|
||||
/*
|
||||
* Execution contexts allocated to this endpoint. Ideally,
|
||||
* we need as many contexts as there are physical cpus only
|
||||
* for a S-EL1 SP which is MP-pinned.
|
||||
*/
|
||||
struct sp_exec_ctx ec[PLATFORM_CORE_COUNT];
|
||||
|
||||
/* ID of the Secure Partition. */
|
||||
uint16_t sp_id;
|
||||
|
||||
/* Runtime EL. */
|
||||
enum sp_runtime_el runtime_el;
|
||||
|
||||
/* Partition UUID. */
|
||||
uint32_t uuid[4];
|
||||
|
||||
/* Partition Properties. */
|
||||
uint32_t properties;
|
||||
|
||||
/* Supported FF-A Version. */
|
||||
uint32_t ffa_version;
|
||||
|
||||
/* Execution State. */
|
||||
enum sp_execution_state execution_state;
|
||||
|
||||
/* Secondary entrypoint. Only valid for a S-EL1 SP. */
|
||||
uintptr_t secondary_ep;
|
||||
};
|
||||
|
||||
/*
|
||||
* This define identifies the only SP that will be initialised and participate
|
||||
* in FF-A communication. The implementation leaves the door open for more SPs
|
||||
* to be managed in future but for now it is reasonable to assume that either a
|
||||
* single S-EL0 or a single S-EL1 SP will be supported. This define will be used
|
||||
* to identify which SP descriptor to initialise and manage during SP runtime.
|
||||
*/
|
||||
#define ACTIVE_SP_DESC_INDEX 0
|
||||
|
||||
/*
|
||||
* Structure to describe the cumulative properties of the Hypervisor and
|
||||
* NS-Endpoints.
|
||||
*/
|
||||
struct ns_endpoint_desc {
|
||||
/*
|
||||
* ID of the NS-Endpoint or Hypervisor.
|
||||
*/
|
||||
uint16_t ns_ep_id;
|
||||
|
||||
/*
|
||||
* Supported FF-A Version.
|
||||
*/
|
||||
uint32_t ffa_version;
|
||||
};
|
||||
|
||||
/* Setup Function for different SP types. */
|
||||
void spmc_sp_common_setup(struct secure_partition_desc *sp,
|
||||
entry_point_info_t *ep_info);
|
||||
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,
|
||||
entry_point_info_t *ep_info);
|
||||
|
||||
/*
|
||||
* Helper function to perform a synchronous entry into a SP.
|
||||
*/
|
||||
uint64_t spmc_sp_synchronous_entry(struct sp_exec_ctx *ec);
|
||||
|
||||
/*
|
||||
* Helper function to obtain the descriptor of the current SP on a physical cpu.
|
||||
*/
|
||||
struct secure_partition_desc *spmc_get_current_sp_ctx(void);
|
||||
|
||||
/*
|
||||
* Helper function to obtain the execution context of an SP on a
|
||||
* physical cpu.
|
||||
*/
|
||||
struct sp_exec_ctx *spmc_get_sp_ec(struct secure_partition_desc *sp);
|
||||
|
||||
/*
|
||||
* Helper function to obtain the index of the execution context of an SP on a
|
||||
* physical cpu.
|
||||
*/
|
||||
unsigned int get_ec_index(struct secure_partition_desc *sp);
|
||||
|
||||
uint64_t spmc_ffa_error_return(void *handle, int error_code);
|
||||
|
||||
/*
|
||||
* Ensure a partition ID does not clash and follows the secure world convention.
|
||||
*/
|
||||
bool is_ffa_secure_id_valid(uint16_t partition_id);
|
||||
|
||||
#endif /* SPMC_H */
|
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
|
||||
ifneq (${ARCH},aarch64)
|
||||
$(error "Error: SPMC is only supported on aarch64.")
|
||||
endif
|
||||
|
||||
SPMC_SOURCES := $(addprefix services/std_svc/spm/el3_spmc/, \
|
||||
spmc_main.c \
|
||||
spmc_setup.c)
|
||||
|
||||
|
||||
# Let the top-level Makefile know that we intend to include a BL32 image
|
||||
NEED_BL32 := yes
|
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <bl31/bl31.h>
|
||||
#include <bl31/ehf.h>
|
||||
#include <common/debug.h>
|
||||
#include <common/fdt_wrappers.h>
|
||||
#include <common/runtime_svc.h>
|
||||
#include <lib/el3_runtime/context_mgmt.h>
|
||||
#include <lib/smccc.h>
|
||||
#include <lib/utils.h>
|
||||
#include <lib/xlat_tables/xlat_tables_v2.h>
|
||||
#include <libfdt.h>
|
||||
#include <plat/common/platform.h>
|
||||
#include <services/ffa_svc.h>
|
||||
#include <services/spmc_svc.h>
|
||||
#include <services/spmd_svc.h>
|
||||
#include "spmc.h"
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
/*
|
||||
* Allocate a secure partition descriptor to describe each SP in the system that
|
||||
* does not reside at EL3.
|
||||
*/
|
||||
static struct secure_partition_desc sp_desc[SECURE_PARTITION_COUNT];
|
||||
|
||||
/*
|
||||
* Allocate an NS endpoint descriptor to describe each VM and the Hypervisor in
|
||||
* the system that interacts with a SP. It is used to track the Hypervisor
|
||||
* buffer pair, version and ID for now. It could be extended to track VM
|
||||
* properties when the SPMC supports indirect messaging.
|
||||
*/
|
||||
static struct ns_endpoint_desc ns_ep_desc[NS_PARTITION_COUNT];
|
||||
|
||||
/*
|
||||
* Helper function to obtain the descriptor of the last SP to whom control was
|
||||
* handed to on this physical cpu. Currently, we assume there is only one SP.
|
||||
* TODO: Expand to track multiple partitions when required.
|
||||
*/
|
||||
struct secure_partition_desc *spmc_get_current_sp_ctx(void)
|
||||
{
|
||||
return &(sp_desc[ACTIVE_SP_DESC_INDEX]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to obtain the execution context of an SP on the
|
||||
* current physical cpu.
|
||||
*/
|
||||
struct sp_exec_ctx *spmc_get_sp_ec(struct secure_partition_desc *sp)
|
||||
{
|
||||
return &(sp->ec[get_ec_index(sp)]);
|
||||
}
|
||||
|
||||
/* Helper function to get pointer to SP context from its ID. */
|
||||
struct secure_partition_desc *spmc_get_sp_ctx(uint16_t id)
|
||||
{
|
||||
/* Check for SWd Partitions. */
|
||||
for (unsigned int i = 0U; i < SECURE_PARTITION_COUNT; i++) {
|
||||
if (sp_desc[i].sp_id == id) {
|
||||
return &(sp_desc[i]);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* This function returns to the place where spmc_sp_synchronous_entry() was
|
||||
* called originally.
|
||||
******************************************************************************/
|
||||
__dead2 void spmc_sp_synchronous_exit(struct sp_exec_ctx *ec, uint64_t rc)
|
||||
{
|
||||
/*
|
||||
* The SPM must have initiated the original request through a
|
||||
* synchronous entry into the secure partition. Jump back to the
|
||||
* original C runtime context with the value of rc in x0;
|
||||
*/
|
||||
spm_secure_partition_exit(ec->c_rt_ctx, rc);
|
||||
|
||||
panic();
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Return FFA_ERROR with specified error code.
|
||||
******************************************************************************/
|
||||
uint64_t spmc_ffa_error_return(void *handle, int error_code)
|
||||
{
|
||||
SMC_RET8(handle, FFA_ERROR,
|
||||
FFA_TARGET_INFO_MBZ, error_code,
|
||||
FFA_PARAM_MBZ, FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
||||
FFA_PARAM_MBZ, FFA_PARAM_MBZ);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Helper function to validate a secure partition ID to ensure it does not
|
||||
* conflict with any other FF-A component and follows the convention to
|
||||
* indicate it resides within the secure world.
|
||||
******************************************************************************/
|
||||
bool is_ffa_secure_id_valid(uint16_t partition_id)
|
||||
{
|
||||
/* Ensure the ID is not the invalid partition ID. */
|
||||
if (partition_id == INV_SP_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure the ID is not the SPMD ID. */
|
||||
if (partition_id == SPMD_DIRECT_MSG_ENDPOINT_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure the ID follows the convention to indicate it resides
|
||||
* in the secure world.
|
||||
*/
|
||||
if (!ffa_is_secure_world_id(partition_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure we don't conflict with the SPMC partition ID. */
|
||||
if (partition_id == FFA_SPMC_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure we do not already have an SP context with this ID. */
|
||||
if (spmc_get_sp_ctx(partition_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* This function will parse the Secure Partition Manifest. From manifest, it
|
||||
* will fetch details for preparing Secure partition image context and secure
|
||||
* partition image boot arguments if any.
|
||||
******************************************************************************/
|
||||
static int sp_manifest_parse(void *sp_manifest, int offset,
|
||||
struct secure_partition_desc *sp,
|
||||
entry_point_info_t *ep_info)
|
||||
{
|
||||
int32_t ret, node;
|
||||
uint32_t config_32;
|
||||
|
||||
/*
|
||||
* Look for the mandatory fields that are expected to be present in
|
||||
* the SP manifests.
|
||||
*/
|
||||
node = fdt_path_offset(sp_manifest, "/");
|
||||
if (node < 0) {
|
||||
ERROR("Did not find root node.\n");
|
||||
return node;
|
||||
}
|
||||
|
||||
ret = fdt_read_uint32(sp_manifest, node, "exception-level", &config_32);
|
||||
if (ret != 0) {
|
||||
ERROR("Missing SP Exception Level information.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sp->runtime_el = config_32;
|
||||
|
||||
ret = fdt_read_uint32(sp_manifest, node, "ffa-version", &config_32);
|
||||
if (ret != 0) {
|
||||
ERROR("Missing Secure Partition FF-A Version.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sp->ffa_version = config_32;
|
||||
|
||||
ret = fdt_read_uint32(sp_manifest, node, "execution-state", &config_32);
|
||||
if (ret != 0) {
|
||||
ERROR("Missing Secure Partition Execution State.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sp->execution_state = config_32;
|
||||
|
||||
/*
|
||||
* Look for the optional fields that are expected to be present in
|
||||
* an SP manifest.
|
||||
*/
|
||||
ret = fdt_read_uint32(sp_manifest, node, "id", &config_32);
|
||||
if (ret != 0) {
|
||||
WARN("Missing Secure Partition ID.\n");
|
||||
} else {
|
||||
if (!is_ffa_secure_id_valid(config_32)) {
|
||||
ERROR("Invalid Secure Partition ID (0x%x).\n",
|
||||
config_32);
|
||||
return -EINVAL;
|
||||
}
|
||||
sp->sp_id = config_32;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* This function gets the Secure Partition Manifest base and maps the manifest
|
||||
* region.
|
||||
* Currently only one Secure Partition manifest is considered which is used to
|
||||
* prepare the context for the single Secure Partition.
|
||||
******************************************************************************/
|
||||
static int find_and_prepare_sp_context(void)
|
||||
{
|
||||
void *sp_manifest;
|
||||
uintptr_t manifest_base;
|
||||
uintptr_t manifest_base_align;
|
||||
entry_point_info_t *next_image_ep_info;
|
||||
int32_t ret;
|
||||
struct secure_partition_desc *sp;
|
||||
|
||||
next_image_ep_info = bl31_plat_get_next_image_ep_info(SECURE);
|
||||
if (next_image_ep_info == NULL) {
|
||||
WARN("No Secure Partition image provided by BL2.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
sp_manifest = (void *)next_image_ep_info->args.arg0;
|
||||
if (sp_manifest == NULL) {
|
||||
WARN("Secure Partition manifest absent.\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
manifest_base = (uintptr_t)sp_manifest;
|
||||
manifest_base_align = page_align(manifest_base, DOWN);
|
||||
|
||||
/*
|
||||
* Map the secure partition manifest region in the EL3 translation
|
||||
* regime.
|
||||
* Map an area equal to (2 * PAGE_SIZE) for now. During manifest base
|
||||
* alignment the region of 1 PAGE_SIZE from manifest align base may
|
||||
* not completely accommodate the secure partition manifest region.
|
||||
*/
|
||||
ret = mmap_add_dynamic_region((unsigned long long)manifest_base_align,
|
||||
manifest_base_align,
|
||||
PAGE_SIZE * 2,
|
||||
MT_RO_DATA);
|
||||
if (ret != 0) {
|
||||
ERROR("Error while mapping SP manifest (%d).\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = fdt_node_offset_by_compatible(sp_manifest, -1,
|
||||
"arm,ffa-manifest-1.0");
|
||||
if (ret < 0) {
|
||||
ERROR("Error happened in SP manifest reading.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the size of the manifest so that it can be used later to pass
|
||||
* the manifest as boot information later.
|
||||
*/
|
||||
next_image_ep_info->args.arg1 = fdt_totalsize(sp_manifest);
|
||||
INFO("Manifest size = %lu bytes.\n", next_image_ep_info->args.arg1);
|
||||
|
||||
/*
|
||||
* Select an SP descriptor for initialising the partition's execution
|
||||
* context on the primary CPU.
|
||||
*/
|
||||
sp = spmc_get_current_sp_ctx();
|
||||
|
||||
/* Initialize entry point information for the SP */
|
||||
SET_PARAM_HEAD(next_image_ep_info, PARAM_EP, VERSION_1,
|
||||
SECURE | EP_ST_ENABLE);
|
||||
|
||||
/* Parse the SP manifest. */
|
||||
ret = sp_manifest_parse(sp_manifest, ret, sp, next_image_ep_info);
|
||||
if (ret != 0) {
|
||||
ERROR("Error in Secure Partition manifest parsing.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check that the runtime EL in the manifest was correct. */
|
||||
if (sp->runtime_el != S_EL1) {
|
||||
ERROR("Unexpected runtime EL: %d\n", sp->runtime_el);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Perform any common initialisation. */
|
||||
spmc_sp_common_setup(sp, next_image_ep_info);
|
||||
|
||||
/* Perform any initialisation specific to S-EL1 SPs. */
|
||||
spmc_el1_sp_setup(sp, next_image_ep_info);
|
||||
|
||||
/* Initialize the SP context with the required ep info. */
|
||||
spmc_sp_common_ep_commit(sp, next_image_ep_info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* This function takes an SP context pointer and performs a synchronous entry
|
||||
* into it.
|
||||
******************************************************************************/
|
||||
uint64_t spmc_sp_synchronous_entry(struct sp_exec_ctx *ec)
|
||||
{
|
||||
uint64_t rc;
|
||||
|
||||
assert(ec != NULL);
|
||||
|
||||
/* Assign the context of the SP to this CPU */
|
||||
cm_set_context(&(ec->cpu_ctx), SECURE);
|
||||
|
||||
/* Restore the context assigned above */
|
||||
cm_el1_sysregs_context_restore(SECURE);
|
||||
cm_set_next_eret_context(SECURE);
|
||||
|
||||
/* Invalidate TLBs at EL1. */
|
||||
tlbivmalle1();
|
||||
dsbish();
|
||||
|
||||
/* Enter Secure Partition */
|
||||
rc = spm_secure_partition_enter(&ec->c_rt_ctx);
|
||||
|
||||
/* Save secure state */
|
||||
cm_el1_sysregs_context_save(SECURE);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* SPMC Helper Functions.
|
||||
******************************************************************************/
|
||||
static int32_t sp_init(void)
|
||||
{
|
||||
uint64_t rc;
|
||||
struct secure_partition_desc *sp;
|
||||
struct sp_exec_ctx *ec;
|
||||
|
||||
sp = spmc_get_current_sp_ctx();
|
||||
ec = spmc_get_sp_ec(sp);
|
||||
ec->rt_model = RT_MODEL_INIT;
|
||||
ec->rt_state = RT_STATE_RUNNING;
|
||||
|
||||
INFO("Secure Partition (0x%x) init start.\n", sp->sp_id);
|
||||
|
||||
rc = spmc_sp_synchronous_entry(ec);
|
||||
if (rc != 0) {
|
||||
/* Indicate SP init was not successful. */
|
||||
ERROR("SP (0x%x) failed to initialize (%lu).\n",
|
||||
sp->sp_id, rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ec->rt_state = RT_STATE_WAITING;
|
||||
INFO("Secure Partition initialized.\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void initalize_sp_descs(void)
|
||||
{
|
||||
struct secure_partition_desc *sp;
|
||||
|
||||
for (unsigned int i = 0U; i < SECURE_PARTITION_COUNT; i++) {
|
||||
sp = &sp_desc[i];
|
||||
sp->sp_id = INV_SP_ID;
|
||||
sp->secondary_ep = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void initalize_ns_ep_descs(void)
|
||||
{
|
||||
struct ns_endpoint_desc *ns_ep;
|
||||
|
||||
for (unsigned int i = 0U; i < NS_PARTITION_COUNT; i++) {
|
||||
ns_ep = &ns_ep_desc[i];
|
||||
/*
|
||||
* Clashes with the Hypervisor ID but will not be a
|
||||
* problem in practice.
|
||||
*/
|
||||
ns_ep->ns_ep_id = 0;
|
||||
ns_ep->ffa_version = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Initialize contexts of all Secure Partitions.
|
||||
******************************************************************************/
|
||||
int32_t spmc_setup(void)
|
||||
{
|
||||
int32_t ret;
|
||||
|
||||
/* Initialize endpoint descriptors */
|
||||
initalize_sp_descs();
|
||||
initalize_ns_ep_descs();
|
||||
|
||||
/* Perform physical SP setup. */
|
||||
|
||||
/* Disable MMU at EL1 (initialized by BL2) */
|
||||
disable_mmu_icache_el1();
|
||||
|
||||
/* Initialize context of the SP */
|
||||
INFO("Secure Partition context setup start.\n");
|
||||
|
||||
ret = find_and_prepare_sp_context();
|
||||
if (ret != 0) {
|
||||
ERROR("Error in SP finding and context preparation.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register init function for deferred init. */
|
||||
bl31_register_bl32_init(&sp_init);
|
||||
|
||||
INFO("Secure Partition setup done.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Secure Partition Manager SMC handler.
|
||||
******************************************************************************/
|
||||
uint64_t spmc_smc_handler(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)
|
||||
{
|
||||
switch (smc_fid) {
|
||||
|
||||
default:
|
||||
WARN("Unsupported FF-A call 0x%08x.\n", smc_fid);
|
||||
break;
|
||||
}
|
||||
return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* 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 <context.h>
|
||||
#include <lib/el3_runtime/context_mgmt.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 <services/ffa_svc.h>
|
||||
#include "spm_common.h"
|
||||
#include "spmc.h"
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
/*
|
||||
* We are assuming that the index of the execution
|
||||
* context used is the linear index of the current physical cpu.
|
||||
*/
|
||||
unsigned int get_ec_index(struct secure_partition_desc *sp)
|
||||
{
|
||||
return plat_my_core_pos();
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* We currently only support S-EL1 partitions so ensure this is the
|
||||
* case.
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
|
@ -58,12 +58,6 @@ typedef struct spmd_spm_core_context {
|
|||
*/
|
||||
#define FFA_NS_ENDPOINT_ID U(0)
|
||||
|
||||
/* Mask and shift to check valid secure FF-A Endpoint ID. */
|
||||
#define SPMC_SECURE_ID_MASK U(1)
|
||||
#define SPMC_SECURE_ID_SHIFT U(15)
|
||||
|
||||
#define SPMD_DIRECT_MSG_ENDPOINT_ID U(FFA_ENDPOINT_ID_MAX - 1)
|
||||
|
||||
/* Define SPMD target function IDs for framework messages to the SPMC */
|
||||
#define SPMD_FWK_MSG_BIT BIT(31)
|
||||
#define SPMD_FWK_MSG_PSCI U(0)
|
||||
|
|
Loading…
Reference in New Issue