mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-17 10:04:26 +00:00

Enable the SPMC to handle calls to FFA_PARTITION_INFO_GET. This allows the normal world to discover which partitions are running in the secure world including logical partitions in EL3. This implementation supports both the v1.0 and v1.1 implementations of the Partition Info Get Descriptor. The SPMC populates the appropriate descriptor in the partitions RX buffer, if requested, according to the version of FF-A that the caller is using. Additionally rename the common/uuid UUID_H include guard due to a conflict with another header file. Signed-off-by: Marc Bonnici <marc.bonnici@arm.com> Change-Id: I0a85f1dae50fae1fe47a3cafb765fbe9f40619e1
1412 lines
40 KiB
C
1412 lines
40 KiB
C
/*
|
|
* 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 <common/uuid.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/el3_spmc_logical_sp.h>
|
|
#include <services/ffa_svc.h>
|
|
#include <services/spmc_svc.h>
|
|
#include <services/spmd_svc.h>
|
|
#include "spmc.h"
|
|
|
|
#include <platform_def.h>
|
|
|
|
/* Declare the maximum number of SPs and El3 LPs. */
|
|
#define MAX_SP_LP_PARTITIONS SECURE_PARTITION_COUNT + MAX_EL3_LP_DESCS_COUNT
|
|
|
|
/*
|
|
* 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 array storing the EL3
|
|
* Logical Partition descriptors.
|
|
*/
|
|
struct el3_lp_desc *get_el3_lp_array(void)
|
|
{
|
|
return (struct el3_lp_desc *) EL3_LP_DESCS_START;
|
|
}
|
|
|
|
/*
|
|
* 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 Secure World Partitions. */
|
|
for (unsigned int i = 0U; i < SECURE_PARTITION_COUNT; i++) {
|
|
if (sp_desc[i].sp_id == id) {
|
|
return &(sp_desc[i]);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Helper function to obtain the descriptor of the Hypervisor or OS kernel.
|
|
* We assume that the first descriptor is reserved for this entity.
|
|
*/
|
|
struct ns_endpoint_desc *spmc_get_hyp_ctx(void)
|
|
{
|
|
return &(ns_ep_desc[0]);
|
|
}
|
|
|
|
/*
|
|
* Helper function to obtain the RX/TX buffer pair descriptor of the Hypervisor
|
|
* or OS kernel in the normal world or the last SP that was run.
|
|
*/
|
|
struct mailbox *spmc_get_mbox_desc(bool secure_origin)
|
|
{
|
|
/* Obtain the RX/TX buffer pair descriptor. */
|
|
if (secure_origin) {
|
|
return &(spmc_get_current_sp_ctx()->mailbox);
|
|
} else {
|
|
return &(spmc_get_hyp_ctx()->mailbox);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* 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)
|
|
{
|
|
struct el3_lp_desc *el3_lp_descs = get_el3_lp_array();
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* Ensure we don't clash with any Logical SP's. */
|
|
for (unsigned int i = 0U; i < EL3_LP_DESCS_COUNT; i++) {
|
|
if (el3_lp_descs[i].sp_id == partition_id) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* This function either forwards the request to the other world or returns
|
|
* with an ERET depending on the source of the call.
|
|
* We can assume that the destination is for an entity at a lower exception
|
|
* level as any messages destined for a logical SP resident in EL3 will have
|
|
* already been taken care of by the SPMC before entering this function.
|
|
******************************************************************************/
|
|
static uint64_t spmc_smc_return(uint32_t smc_fid,
|
|
bool secure_origin,
|
|
uint64_t x1,
|
|
uint64_t x2,
|
|
uint64_t x3,
|
|
uint64_t x4,
|
|
void *handle,
|
|
void *cookie,
|
|
uint64_t flags,
|
|
uint16_t dst_id)
|
|
{
|
|
/* If the destination is in the normal world always go via the SPMD. */
|
|
if (ffa_is_normal_world_id(dst_id)) {
|
|
return spmd_smc_handler(smc_fid, x1, x2, x3, x4,
|
|
cookie, handle, flags);
|
|
}
|
|
/*
|
|
* If the caller is secure and we want to return to the secure world,
|
|
* ERET directly.
|
|
*/
|
|
else if (secure_origin && ffa_is_secure_world_id(dst_id)) {
|
|
SMC_RET5(handle, smc_fid, x1, x2, x3, x4);
|
|
}
|
|
/* If we originated in the normal world then switch contexts. */
|
|
else if (!secure_origin && ffa_is_secure_world_id(dst_id)) {
|
|
return spmd_smc_switch_state(smc_fid, secure_origin, x1, x2,
|
|
x3, x4, handle);
|
|
} else {
|
|
/* Unknown State. */
|
|
panic();
|
|
}
|
|
|
|
/* Shouldn't be Reached. */
|
|
return 0;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* FF-A ABI Handlers.
|
|
******************************************************************************/
|
|
|
|
/*******************************************************************************
|
|
* Helper function to validate arg2 as part of a direct message.
|
|
******************************************************************************/
|
|
static inline bool direct_msg_validate_arg2(uint64_t x2)
|
|
{
|
|
/*
|
|
* We currently only support partition messages, therefore ensure x2 is
|
|
* not set.
|
|
*/
|
|
if (x2 != (uint64_t) 0) {
|
|
VERBOSE("Arg2 MBZ for partition messages (0x%lx).\n", x2);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Handle direct request messages and route to the appropriate destination.
|
|
******************************************************************************/
|
|
static uint64_t direct_req_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)
|
|
{
|
|
uint16_t dst_id = ffa_endpoint_destination(x1);
|
|
struct el3_lp_desc *el3_lp_descs;
|
|
struct secure_partition_desc *sp;
|
|
unsigned int idx;
|
|
|
|
/* Check if arg2 has been populated correctly based on message type. */
|
|
if (!direct_msg_validate_arg2(x2)) {
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
el3_lp_descs = get_el3_lp_array();
|
|
|
|
/* Check if the request is destined for a Logical Partition. */
|
|
for (unsigned int i = 0U; i < MAX_EL3_LP_DESCS_COUNT; i++) {
|
|
if (el3_lp_descs[i].sp_id == dst_id) {
|
|
return el3_lp_descs[i].direct_req(
|
|
smc_fid, secure_origin, x1, x2, x3, x4,
|
|
cookie, handle, flags);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the request was not targeted to a LSP and from the secure world
|
|
* then it is invalid since a SP cannot call into the Normal world and
|
|
* there is no other SP to call into. If there are other SPs in future
|
|
* then the partition runtime model would need to be validated as well.
|
|
*/
|
|
if (secure_origin) {
|
|
VERBOSE("Direct request not supported to the Normal World.\n");
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Check if the SP ID is valid. */
|
|
sp = spmc_get_sp_ctx(dst_id);
|
|
if (sp == NULL) {
|
|
VERBOSE("Direct request to unknown partition ID (0x%x).\n",
|
|
dst_id);
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/*
|
|
* Check that the target execution context is in a waiting state before
|
|
* forwarding the direct request to it.
|
|
*/
|
|
idx = get_ec_index(sp);
|
|
if (sp->ec[idx].rt_state != RT_STATE_WAITING) {
|
|
VERBOSE("SP context on core%u is not waiting (%u).\n",
|
|
idx, sp->ec[idx].rt_model);
|
|
return spmc_ffa_error_return(handle, FFA_ERROR_BUSY);
|
|
}
|
|
|
|
/*
|
|
* Everything checks out so forward the request to the SP after updating
|
|
* its state and runtime model.
|
|
*/
|
|
sp->ec[idx].rt_state = RT_STATE_RUNNING;
|
|
sp->ec[idx].rt_model = RT_MODEL_DIR_REQ;
|
|
return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
handle, cookie, flags, dst_id);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Handle direct response messages and route to the appropriate destination.
|
|
******************************************************************************/
|
|
static uint64_t direct_resp_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)
|
|
{
|
|
uint16_t dst_id = ffa_endpoint_destination(x1);
|
|
struct secure_partition_desc *sp;
|
|
unsigned int idx;
|
|
|
|
/* Check if arg2 has been populated correctly based on message type. */
|
|
if (!direct_msg_validate_arg2(x2)) {
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Check that the response did not originate from the Normal world. */
|
|
if (!secure_origin) {
|
|
VERBOSE("Direct Response not supported from Normal World.\n");
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/*
|
|
* Check that the response is either targeted to the Normal world or the
|
|
* SPMC e.g. a PM response.
|
|
*/
|
|
if ((dst_id != FFA_SPMC_ID) && ffa_is_secure_world_id(dst_id)) {
|
|
VERBOSE("Direct response to invalid partition ID (0x%x).\n",
|
|
dst_id);
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Obtain the SP descriptor and update its runtime state. */
|
|
sp = spmc_get_sp_ctx(ffa_endpoint_source(x1));
|
|
if (sp == NULL) {
|
|
VERBOSE("Direct response to unknown partition ID (0x%x).\n",
|
|
dst_id);
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Sanity check state is being tracked correctly in the SPMC. */
|
|
idx = get_ec_index(sp);
|
|
assert(sp->ec[idx].rt_state == RT_STATE_RUNNING);
|
|
|
|
/* Ensure SP execution context was in the right runtime model. */
|
|
if (sp->ec[idx].rt_model != RT_MODEL_DIR_REQ) {
|
|
VERBOSE("SP context on core%u not handling direct req (%u).\n",
|
|
idx, sp->ec[idx].rt_model);
|
|
return spmc_ffa_error_return(handle, FFA_ERROR_DENIED);
|
|
}
|
|
|
|
/* Update the state of the SP execution context. */
|
|
sp->ec[idx].rt_state = RT_STATE_WAITING;
|
|
|
|
/*
|
|
* If the receiver is not the SPMC then forward the response to the
|
|
* Normal world.
|
|
*/
|
|
if (dst_id == FFA_SPMC_ID) {
|
|
spmc_sp_synchronous_exit(&sp->ec[idx], x4);
|
|
/* Should not get here. */
|
|
panic();
|
|
}
|
|
|
|
return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
handle, cookie, flags, dst_id);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* This function handles the FFA_MSG_WAIT SMC to allow an SP to relinquish its
|
|
* cycles.
|
|
******************************************************************************/
|
|
static uint64_t msg_wait_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)
|
|
{
|
|
struct secure_partition_desc *sp;
|
|
unsigned int idx;
|
|
|
|
/*
|
|
* Check that the response did not originate from the Normal world as
|
|
* only the secure world can call this ABI.
|
|
*/
|
|
if (!secure_origin) {
|
|
VERBOSE("Normal world cannot call FFA_MSG_WAIT.\n");
|
|
return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
/* Get the descriptor of the SP that invoked FFA_MSG_WAIT. */
|
|
sp = spmc_get_current_sp_ctx();
|
|
if (sp == NULL) {
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/*
|
|
* Get the execution context of the SP that invoked FFA_MSG_WAIT.
|
|
*/
|
|
idx = get_ec_index(sp);
|
|
|
|
/* Ensure SP execution context was in the right runtime model. */
|
|
if (sp->ec[idx].rt_model == RT_MODEL_DIR_REQ) {
|
|
return spmc_ffa_error_return(handle, FFA_ERROR_DENIED);
|
|
}
|
|
|
|
/* Sanity check the state is being tracked correctly in the SPMC. */
|
|
assert(sp->ec[idx].rt_state == RT_STATE_RUNNING);
|
|
|
|
/*
|
|
* Perform a synchronous exit if the partition was initialising. The
|
|
* state is updated after the exit.
|
|
*/
|
|
if (sp->ec[idx].rt_model == RT_MODEL_INIT) {
|
|
spmc_sp_synchronous_exit(&sp->ec[idx], x4);
|
|
/* Should not get here */
|
|
panic();
|
|
}
|
|
|
|
/* Update the state of the SP execution context. */
|
|
sp->ec[idx].rt_state = RT_STATE_WAITING;
|
|
|
|
/* Resume normal world if a secure interrupt was handled. */
|
|
if (sp->ec[idx].rt_model == RT_MODEL_INTR) {
|
|
/* FFA_MSG_WAIT can only be called from the secure world. */
|
|
unsigned int secure_state_in = SECURE;
|
|
unsigned int secure_state_out = NON_SECURE;
|
|
|
|
cm_el1_sysregs_context_save(secure_state_in);
|
|
cm_el1_sysregs_context_restore(secure_state_out);
|
|
cm_set_next_eret_context(secure_state_out);
|
|
SMC_RET0(cm_get_context(secure_state_out));
|
|
}
|
|
|
|
/* Forward the response to the Normal world. */
|
|
return spmc_smc_return(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
handle, cookie, flags, FFA_NWD_ID);
|
|
}
|
|
|
|
static uint64_t ffa_error_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)
|
|
{
|
|
struct secure_partition_desc *sp;
|
|
unsigned int idx;
|
|
|
|
/* Check that the response did not originate from the Normal world. */
|
|
if (!secure_origin) {
|
|
return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
/* Get the descriptor of the SP that invoked FFA_ERROR. */
|
|
sp = spmc_get_current_sp_ctx();
|
|
if (sp == NULL) {
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Get the execution context of the SP that invoked FFA_ERROR. */
|
|
idx = get_ec_index(sp);
|
|
|
|
/*
|
|
* We only expect FFA_ERROR to be received during SP initialisation
|
|
* otherwise this is an invalid call.
|
|
*/
|
|
if (sp->ec[idx].rt_model == RT_MODEL_INIT) {
|
|
ERROR("SP 0x%x failed to initialize.\n", sp->sp_id);
|
|
spmc_sp_synchronous_exit(&sp->ec[idx], x2);
|
|
/* Should not get here. */
|
|
panic();
|
|
}
|
|
|
|
return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
static uint64_t ffa_version_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)
|
|
{
|
|
uint32_t requested_version = x1 & FFA_VERSION_MASK;
|
|
|
|
if (requested_version & FFA_VERSION_BIT31_MASK) {
|
|
/* Invalid encoding, return an error. */
|
|
SMC_RET1(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
/* Execution stops here. */
|
|
}
|
|
|
|
/* Determine the caller to store the requested version. */
|
|
if (secure_origin) {
|
|
/*
|
|
* Ensure that the SP is reporting the same version as
|
|
* specified in its manifest. If these do not match there is
|
|
* something wrong with the SP.
|
|
* TODO: Should we abort the SP? For now assert this is not
|
|
* case.
|
|
*/
|
|
assert(requested_version ==
|
|
spmc_get_current_sp_ctx()->ffa_version);
|
|
} else {
|
|
/*
|
|
* If this is called by the normal world, record this
|
|
* information in its descriptor.
|
|
*/
|
|
spmc_get_hyp_ctx()->ffa_version = requested_version;
|
|
}
|
|
|
|
SMC_RET1(handle, MAKE_FFA_VERSION(FFA_VERSION_MAJOR,
|
|
FFA_VERSION_MINOR));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Helper function to obtain the FF-A version of the calling partition.
|
|
******************************************************************************/
|
|
uint32_t get_partition_ffa_version(bool secure_origin)
|
|
{
|
|
if (secure_origin) {
|
|
return spmc_get_current_sp_ctx()->ffa_version;
|
|
} else {
|
|
return spmc_get_hyp_ctx()->ffa_version;
|
|
}
|
|
}
|
|
|
|
static uint64_t rxtx_map_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)
|
|
{
|
|
int ret;
|
|
uint32_t error_code;
|
|
uint32_t mem_atts = secure_origin ? MT_SECURE : MT_NS;
|
|
struct mailbox *mbox;
|
|
uintptr_t tx_address = x1;
|
|
uintptr_t rx_address = x2;
|
|
uint32_t page_count = x3 & FFA_RXTX_PAGE_COUNT_MASK; /* Bits [5:0] */
|
|
uint32_t buf_size = page_count * FFA_PAGE_SIZE;
|
|
|
|
/*
|
|
* The SPMC does not support mapping of VM RX/TX pairs to facilitate
|
|
* indirect messaging with SPs. Check if the Hypervisor has invoked this
|
|
* ABI on behalf of a VM and reject it if this is the case.
|
|
*/
|
|
if (tx_address == 0 || rx_address == 0) {
|
|
WARN("Mapping RX/TX Buffers on behalf of VM not supported.\n");
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Ensure the specified buffers are not the same. */
|
|
if (tx_address == rx_address) {
|
|
WARN("TX Buffer must not be the same as RX Buffer.\n");
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Ensure the buffer size is not 0. */
|
|
if (buf_size == 0U) {
|
|
WARN("Buffer size must not be 0\n");
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/*
|
|
* Ensure the buffer size is a multiple of the translation granule size
|
|
* in TF-A.
|
|
*/
|
|
if (buf_size % PAGE_SIZE != 0U) {
|
|
WARN("Buffer size must be aligned to translation granule.\n");
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Obtain the RX/TX buffer pair descriptor. */
|
|
mbox = spmc_get_mbox_desc(secure_origin);
|
|
|
|
spin_lock(&mbox->lock);
|
|
|
|
/* Check if buffers have already been mapped. */
|
|
if (mbox->rx_buffer != 0 || mbox->tx_buffer != 0) {
|
|
WARN("RX/TX Buffers already mapped (%p/%p)\n",
|
|
(void *) mbox->rx_buffer, (void *)mbox->tx_buffer);
|
|
error_code = FFA_ERROR_DENIED;
|
|
goto err;
|
|
}
|
|
|
|
/* memmap the TX buffer as read only. */
|
|
ret = mmap_add_dynamic_region(tx_address, /* PA */
|
|
tx_address, /* VA */
|
|
buf_size, /* size */
|
|
mem_atts | MT_RO_DATA); /* attrs */
|
|
if (ret != 0) {
|
|
/* Return the correct error code. */
|
|
error_code = (ret == -ENOMEM) ? FFA_ERROR_NO_MEMORY :
|
|
FFA_ERROR_INVALID_PARAMETER;
|
|
WARN("Unable to map TX buffer: %d\n", error_code);
|
|
goto err;
|
|
}
|
|
|
|
/* memmap the RX buffer as read write. */
|
|
ret = mmap_add_dynamic_region(rx_address, /* PA */
|
|
rx_address, /* VA */
|
|
buf_size, /* size */
|
|
mem_atts | MT_RW_DATA); /* attrs */
|
|
|
|
if (ret != 0) {
|
|
error_code = (ret == -ENOMEM) ? FFA_ERROR_NO_MEMORY :
|
|
FFA_ERROR_INVALID_PARAMETER;
|
|
WARN("Unable to map RX buffer: %d\n", error_code);
|
|
/* Unmap the TX buffer again. */
|
|
mmap_remove_dynamic_region(tx_address, buf_size);
|
|
goto err;
|
|
}
|
|
|
|
mbox->tx_buffer = (void *) tx_address;
|
|
mbox->rx_buffer = (void *) rx_address;
|
|
mbox->rxtx_page_count = page_count;
|
|
spin_unlock(&mbox->lock);
|
|
|
|
SMC_RET1(handle, FFA_SUCCESS_SMC32);
|
|
/* Execution stops here. */
|
|
err:
|
|
spin_unlock(&mbox->lock);
|
|
return spmc_ffa_error_return(handle, error_code);
|
|
}
|
|
|
|
static uint64_t rxtx_unmap_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)
|
|
{
|
|
struct mailbox *mbox = spmc_get_mbox_desc(secure_origin);
|
|
uint32_t buf_size = mbox->rxtx_page_count * FFA_PAGE_SIZE;
|
|
|
|
/*
|
|
* The SPMC does not support mapping of VM RX/TX pairs to facilitate
|
|
* indirect messaging with SPs. Check if the Hypervisor has invoked this
|
|
* ABI on behalf of a VM and reject it if this is the case.
|
|
*/
|
|
if (x1 != 0UL) {
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
spin_lock(&mbox->lock);
|
|
|
|
/* Check if buffers are currently mapped. */
|
|
if (mbox->rx_buffer == 0 || mbox->tx_buffer == 0) {
|
|
spin_unlock(&mbox->lock);
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
|
|
/* Unmap RX Buffer */
|
|
if (mmap_remove_dynamic_region((uintptr_t) mbox->rx_buffer,
|
|
buf_size) != 0) {
|
|
WARN("Unable to unmap RX buffer!\n");
|
|
}
|
|
|
|
mbox->rx_buffer = 0;
|
|
|
|
/* Unmap TX Buffer */
|
|
if (mmap_remove_dynamic_region((uintptr_t) mbox->tx_buffer,
|
|
buf_size) != 0) {
|
|
WARN("Unable to unmap TX buffer!\n");
|
|
}
|
|
|
|
mbox->tx_buffer = 0;
|
|
mbox->rxtx_page_count = 0;
|
|
|
|
spin_unlock(&mbox->lock);
|
|
SMC_RET1(handle, FFA_SUCCESS_SMC32);
|
|
}
|
|
|
|
/*
|
|
* Collate the partition information in a v1.1 partition information
|
|
* descriptor format, this will be converter later if required.
|
|
*/
|
|
static int partition_info_get_handler_v1_1(uint32_t *uuid,
|
|
struct ffa_partition_info_v1_1
|
|
*partitions,
|
|
uint32_t max_partitions,
|
|
uint32_t *partition_count)
|
|
{
|
|
uint32_t index;
|
|
struct ffa_partition_info_v1_1 *desc;
|
|
bool null_uuid = is_null_uuid(uuid);
|
|
struct el3_lp_desc *el3_lp_descs = get_el3_lp_array();
|
|
|
|
/* Deal with Logical Partitions. */
|
|
for (index = 0U; index < EL3_LP_DESCS_COUNT; index++) {
|
|
if (null_uuid || uuid_match(uuid, el3_lp_descs[index].uuid)) {
|
|
/* Found a matching UUID, populate appropriately. */
|
|
if (*partition_count >= max_partitions) {
|
|
return FFA_ERROR_NO_MEMORY;
|
|
}
|
|
|
|
desc = &partitions[*partition_count];
|
|
desc->ep_id = el3_lp_descs[index].sp_id;
|
|
desc->execution_ctx_count = PLATFORM_CORE_COUNT;
|
|
desc->properties = el3_lp_descs[index].properties;
|
|
if (null_uuid) {
|
|
copy_uuid(desc->uuid, el3_lp_descs[index].uuid);
|
|
}
|
|
(*partition_count)++;
|
|
}
|
|
}
|
|
|
|
/* Deal with physical SP's. */
|
|
for (index = 0U; index < SECURE_PARTITION_COUNT; index++) {
|
|
if (null_uuid || uuid_match(uuid, sp_desc[index].uuid)) {
|
|
/* Found a matching UUID, populate appropriately. */
|
|
if (*partition_count >= max_partitions) {
|
|
return FFA_ERROR_NO_MEMORY;
|
|
}
|
|
|
|
desc = &partitions[*partition_count];
|
|
desc->ep_id = sp_desc[index].sp_id;
|
|
/*
|
|
* Execution context count must match No. cores for
|
|
* S-EL1 SPs.
|
|
*/
|
|
desc->execution_ctx_count = PLATFORM_CORE_COUNT;
|
|
desc->properties = sp_desc[index].properties;
|
|
if (null_uuid) {
|
|
copy_uuid(desc->uuid, sp_desc[index].uuid);
|
|
}
|
|
(*partition_count)++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Handle the case where that caller only wants the count of partitions
|
|
* matching a given UUID and does not want the corresponding descriptors
|
|
* populated.
|
|
*/
|
|
static uint32_t partition_info_get_handler_count_only(uint32_t *uuid)
|
|
{
|
|
uint32_t index = 0;
|
|
uint32_t partition_count = 0;
|
|
bool null_uuid = is_null_uuid(uuid);
|
|
struct el3_lp_desc *el3_lp_descs = get_el3_lp_array();
|
|
|
|
/* Deal with Logical Partitions. */
|
|
for (index = 0U; index < EL3_LP_DESCS_COUNT; index++) {
|
|
if (null_uuid ||
|
|
uuid_match(uuid, el3_lp_descs[index].uuid)) {
|
|
(partition_count)++;
|
|
}
|
|
}
|
|
|
|
/* Deal with physical SP's. */
|
|
for (index = 0U; index < SECURE_PARTITION_COUNT; index++) {
|
|
if (null_uuid || uuid_match(uuid, sp_desc[index].uuid)) {
|
|
(partition_count)++;
|
|
}
|
|
}
|
|
return partition_count;
|
|
}
|
|
|
|
/*
|
|
* If the caller of the PARTITION_INFO_GET ABI was a v1.0 caller, populate
|
|
* the coresponding descriptor format from the v1.1 descriptor array.
|
|
*/
|
|
static uint64_t partition_info_populate_v1_0(struct ffa_partition_info_v1_1
|
|
*partitions,
|
|
struct mailbox *mbox,
|
|
int partition_count)
|
|
{
|
|
uint32_t index;
|
|
uint32_t buf_size;
|
|
uint32_t descriptor_size;
|
|
struct ffa_partition_info_v1_0 *v1_0_partitions =
|
|
(struct ffa_partition_info_v1_0 *) mbox->rx_buffer;
|
|
|
|
buf_size = mbox->rxtx_page_count * FFA_PAGE_SIZE;
|
|
descriptor_size = partition_count *
|
|
sizeof(struct ffa_partition_info_v1_0);
|
|
|
|
if (descriptor_size > buf_size) {
|
|
return FFA_ERROR_NO_MEMORY;
|
|
}
|
|
|
|
for (index = 0U; index < partition_count; index++) {
|
|
v1_0_partitions[index].ep_id = partitions[index].ep_id;
|
|
v1_0_partitions[index].execution_ctx_count =
|
|
partitions[index].execution_ctx_count;
|
|
v1_0_partitions[index].properties =
|
|
partitions[index].properties;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Main handler for FFA_PARTITION_INFO_GET which supports both FF-A v1.1 and
|
|
* v1.0 implementations.
|
|
*/
|
|
static uint64_t partition_info_get_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)
|
|
{
|
|
int ret;
|
|
uint32_t partition_count = 0;
|
|
uint32_t size = 0;
|
|
uint32_t ffa_version = get_partition_ffa_version(secure_origin);
|
|
struct mailbox *mbox;
|
|
uint64_t info_get_flags;
|
|
bool count_only;
|
|
uint32_t uuid[4];
|
|
|
|
uuid[0] = x1;
|
|
uuid[1] = x2;
|
|
uuid[2] = x3;
|
|
uuid[3] = x4;
|
|
|
|
/* Determine if the Partition descriptors should be populated. */
|
|
info_get_flags = SMC_GET_GP(handle, CTX_GPREG_X5);
|
|
count_only = (info_get_flags & FFA_PARTITION_INFO_GET_COUNT_FLAG_MASK);
|
|
|
|
/* Handle the case where we don't need to populate the descriptors. */
|
|
if (count_only) {
|
|
partition_count = partition_info_get_handler_count_only(uuid);
|
|
if (partition_count == 0) {
|
|
return spmc_ffa_error_return(handle,
|
|
FFA_ERROR_INVALID_PARAMETER);
|
|
}
|
|
} else {
|
|
struct ffa_partition_info_v1_1 partitions[MAX_SP_LP_PARTITIONS];
|
|
|
|
/*
|
|
* Handle the case where the partition descriptors are required,
|
|
* check we have the buffers available and populate the
|
|
* appropriate structure version.
|
|
*/
|
|
|
|
/* Obtain the v1.1 format of the descriptors. */
|
|
ret = partition_info_get_handler_v1_1(uuid, partitions,
|
|
MAX_SP_LP_PARTITIONS,
|
|
&partition_count);
|
|
|
|
/* Check if an error occurred during discovery. */
|
|
if (ret != 0) {
|
|
goto err;
|
|
}
|
|
|
|
/* If we didn't find any matches the UUID is unknown. */
|
|
if (partition_count == 0) {
|
|
ret = FFA_ERROR_INVALID_PARAMETER;
|
|
goto err;
|
|
}
|
|
|
|
/* Obtain the partition mailbox RX/TX buffer pair descriptor. */
|
|
mbox = spmc_get_mbox_desc(secure_origin);
|
|
|
|
/*
|
|
* If the caller has not bothered registering its RX/TX pair
|
|
* then return an error code.
|
|
*/
|
|
spin_lock(&mbox->lock);
|
|
if (mbox->rx_buffer == NULL) {
|
|
ret = FFA_ERROR_BUSY;
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Ensure the RX buffer is currently free. */
|
|
if (mbox->state != MAILBOX_STATE_EMPTY) {
|
|
ret = FFA_ERROR_BUSY;
|
|
goto err_unlock;
|
|
}
|
|
|
|
/* Zero the RX buffer before populating. */
|
|
(void)memset(mbox->rx_buffer, 0,
|
|
mbox->rxtx_page_count * FFA_PAGE_SIZE);
|
|
|
|
/*
|
|
* Depending on the FF-A version of the requesting partition
|
|
* we may need to convert to a v1.0 format otherwise we can copy
|
|
* directly.
|
|
*/
|
|
if (ffa_version == MAKE_FFA_VERSION(U(1), U(0))) {
|
|
ret = partition_info_populate_v1_0(partitions,
|
|
mbox,
|
|
partition_count);
|
|
if (ret != 0) {
|
|
goto err_unlock;
|
|
}
|
|
} else {
|
|
uint32_t buf_size = mbox->rxtx_page_count *
|
|
FFA_PAGE_SIZE;
|
|
|
|
/* Ensure the descriptor will fit in the buffer. */
|
|
size = sizeof(struct ffa_partition_info_v1_1);
|
|
if (partition_count * size > buf_size) {
|
|
ret = FFA_ERROR_NO_MEMORY;
|
|
goto err_unlock;
|
|
}
|
|
memcpy(mbox->rx_buffer, partitions,
|
|
partition_count * size);
|
|
}
|
|
|
|
mbox->state = MAILBOX_STATE_FULL;
|
|
spin_unlock(&mbox->lock);
|
|
}
|
|
SMC_RET4(handle, FFA_SUCCESS_SMC32, 0, partition_count, size);
|
|
|
|
err_unlock:
|
|
spin_unlock(&mbox->lock);
|
|
err:
|
|
return spmc_ffa_error_return(handle, ret);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* 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_array(sp_manifest, node, "uuid",
|
|
ARRAY_SIZE(sp->uuid), sp->uuid);
|
|
if (ret != 0) {
|
|
ERROR("Missing Secure Partition UUID.\n");
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
ret = fdt_read_uint32(sp_manifest, node,
|
|
"messaging-method", &config_32);
|
|
if (ret != 0) {
|
|
ERROR("Missing Secure Partition messaging method.\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Validate this entry, we currently only support direct messaging. */
|
|
if ((config_32 & ~(FFA_PARTITION_DIRECT_REQ_RECV |
|
|
FFA_PARTITION_DIRECT_REQ_SEND)) != 0U) {
|
|
WARN("Invalid Secure Partition messaging method (0x%x)\n",
|
|
config_32);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sp->properties = config_32;
|
|
|
|
ret = fdt_read_uint32(sp_manifest, node,
|
|
"execution-ctx-count", &config_32);
|
|
|
|
if (ret != 0) {
|
|
ERROR("Missing SP Execution Context Count.\n");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Ensure this field is set correctly in the manifest however
|
|
* since this is currently a hardcoded value for S-EL1 partitions
|
|
* we don't need to save it here, just validate.
|
|
*/
|
|
if (config_32 != PLATFORM_CORE_COUNT) {
|
|
ERROR("SP Execution Context Count (%u) must be %u.\n",
|
|
config_32, PLATFORM_CORE_COUNT);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
******************************************************************************/
|
|
static int32_t logical_sp_init(void)
|
|
{
|
|
int32_t rc = 0;
|
|
struct el3_lp_desc *el3_lp_descs;
|
|
|
|
/* Perform initial validation of the Logical Partitions. */
|
|
rc = el3_sp_desc_validate();
|
|
if (rc != 0) {
|
|
ERROR("Logical Partition validation failed!\n");
|
|
return rc;
|
|
}
|
|
|
|
el3_lp_descs = get_el3_lp_array();
|
|
|
|
INFO("Logical Secure Partition init start.\n");
|
|
for (unsigned int i = 0U; i < EL3_LP_DESCS_COUNT; i++) {
|
|
rc = el3_lp_descs[i].init();
|
|
if (rc != 0) {
|
|
ERROR("Logical SP (0x%x) Failed to Initialize\n",
|
|
el3_lp_descs[i].sp_id);
|
|
return rc;
|
|
}
|
|
VERBOSE("Logical SP (0x%x) Initialized\n",
|
|
el3_lp_descs[i].sp_id);
|
|
}
|
|
|
|
INFO("Logical Secure Partition init completed.\n");
|
|
|
|
return rc;
|
|
}
|
|
|
|
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->mailbox.rx_buffer = NULL;
|
|
sp->mailbox.tx_buffer = NULL;
|
|
sp->mailbox.state = MAILBOX_STATE_EMPTY;
|
|
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;
|
|
ns_ep->mailbox.rx_buffer = NULL;
|
|
ns_ep->mailbox.tx_buffer = NULL;
|
|
ns_ep->mailbox.state = MAILBOX_STATE_EMPTY;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Initialize SPMC attributes for the SPMD.
|
|
******************************************************************************/
|
|
void spmc_populate_attrs(spmc_manifest_attribute_t *spmc_attrs)
|
|
{
|
|
spmc_attrs->major_version = FFA_VERSION_MAJOR;
|
|
spmc_attrs->minor_version = FFA_VERSION_MINOR;
|
|
spmc_attrs->exec_state = MODE_RW_64;
|
|
spmc_attrs->spmc_id = FFA_SPMC_ID;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Initialize contexts of all Secure Partitions.
|
|
******************************************************************************/
|
|
int32_t spmc_setup(void)
|
|
{
|
|
int32_t ret;
|
|
|
|
/* Initialize endpoint descriptors */
|
|
initalize_sp_descs();
|
|
initalize_ns_ep_descs();
|
|
|
|
/* Setup logical SPs. */
|
|
ret = logical_sp_init();
|
|
if (ret != 0) {
|
|
ERROR("Failed to initialize Logical Partitions.\n");
|
|
return ret;
|
|
}
|
|
|
|
/* 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) {
|
|
|
|
case FFA_VERSION:
|
|
return ffa_version_handler(smc_fid, secure_origin, x1, x2, x3,
|
|
x4, cookie, handle, flags);
|
|
|
|
case FFA_MSG_SEND_DIRECT_REQ_SMC32:
|
|
case FFA_MSG_SEND_DIRECT_REQ_SMC64:
|
|
return direct_req_smc_handler(smc_fid, secure_origin, x1, x2,
|
|
x3, x4, cookie, handle, flags);
|
|
|
|
case FFA_MSG_SEND_DIRECT_RESP_SMC32:
|
|
case FFA_MSG_SEND_DIRECT_RESP_SMC64:
|
|
return direct_resp_smc_handler(smc_fid, secure_origin, x1, x2,
|
|
x3, x4, cookie, handle, flags);
|
|
|
|
case FFA_RXTX_MAP_SMC32:
|
|
case FFA_RXTX_MAP_SMC64:
|
|
return rxtx_map_handler(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
cookie, handle, flags);
|
|
|
|
case FFA_RXTX_UNMAP:
|
|
return rxtx_unmap_handler(smc_fid, secure_origin, x1, x2, x3,
|
|
x4, cookie, handle, flags);
|
|
|
|
case FFA_PARTITION_INFO_GET:
|
|
return partition_info_get_handler(smc_fid, secure_origin, x1,
|
|
x2, x3, x4, cookie, handle,
|
|
flags);
|
|
|
|
case FFA_MSG_WAIT:
|
|
return msg_wait_handler(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
cookie, handle, flags);
|
|
|
|
case FFA_ERROR:
|
|
return ffa_error_handler(smc_fid, secure_origin, x1, x2, x3, x4,
|
|
cookie, handle, flags);
|
|
|
|
default:
|
|
WARN("Unsupported FF-A call 0x%08x.\n", smc_fid);
|
|
break;
|
|
}
|
|
return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
|
}
|