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

StandaloneMm which is S-EL0 partition uses FFA_MSG_SEND_DIRECT_REQ2/RESP2 to handle multiple services. For this, add support for FFA_MSG_SEND_DIRECT_REQ2/RESP2 in el3_spmc restrictly up to use 8 registers. although FF-A v1.2 defines FFA_MSG_SEND_DIRECT_REQ2/RESP2 with ability to pass/return up to 18 registers. Signed-off-by: Levi Yun <yeoreum.yun@arm.com> Change-Id: I8ab1c332d269d9d131330bb2debd10d75bdba1ee
300 lines
9 KiB
C
300 lines
9 KiB
C
/*
|
|
* Copyright (c) 2022-2023, ARM Limited and Contributors. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
|
|
#include <lib/el3_runtime/context_mgmt.h>
|
|
#include <lib/spinlock.h>
|
|
#include <plat/common/common_def.h>
|
|
#include <plat/common/platform.h>
|
|
#include <services/ffa_svc.h>
|
|
#include "spmc.h"
|
|
|
|
#include <platform_def.h>
|
|
|
|
/*******************************************************************************
|
|
* spmc_build_pm_message
|
|
*
|
|
* Builds an SPMC to SP direct message request.
|
|
******************************************************************************/
|
|
static void spmc_build_pm_message(gp_regs_t *gpregs,
|
|
unsigned long long message,
|
|
uint8_t pm_msg_type,
|
|
uint16_t sp_id)
|
|
{
|
|
write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X1,
|
|
(FFA_SPMC_ID << FFA_DIRECT_MSG_SOURCE_SHIFT) |
|
|
sp_id);
|
|
write_ctx_reg(gpregs, CTX_GPREG_X2, FFA_FWK_MSG_BIT |
|
|
(pm_msg_type & FFA_FWK_MSG_MASK));
|
|
write_ctx_reg(gpregs, CTX_GPREG_X3, message);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* This CPU has been turned on. Enter the SP to initialise S-EL0 or S-EL1.
|
|
******************************************************************************/
|
|
static void spmc_cpu_on_finish_handler(u_register_t unused)
|
|
{
|
|
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
|
|
struct sp_exec_ctx *ec;
|
|
unsigned int linear_id = plat_my_core_pos();
|
|
entry_point_info_t sec_ec_ep_info = {0};
|
|
uint64_t rc;
|
|
|
|
/* Sanity check for a NULL pointer dereference. */
|
|
assert(sp != NULL);
|
|
|
|
/* Obtain a reference to the SP execution context */
|
|
ec = &sp->ec[get_ec_index(sp)];
|
|
|
|
/*
|
|
* In case of a S-EL0 SP, only initialise the context data structure for
|
|
* the secure world on this cpu and return.
|
|
*/
|
|
if (sp->runtime_el == S_EL0) {
|
|
/* Assign the context of the SP to this CPU */
|
|
cm_set_context(&(ec->cpu_ctx), SECURE);
|
|
return;
|
|
}
|
|
|
|
/* Initialize entry point information for the SP. */
|
|
SET_PARAM_HEAD(&sec_ec_ep_info, PARAM_EP, VERSION_1,
|
|
SECURE | EP_ST_ENABLE);
|
|
|
|
/*
|
|
* Check if the primary execution context registered an entry point else
|
|
* bail out early.
|
|
* TODO: Add support for boot reason in manifest to allow jumping to
|
|
* entrypoint into the primary execution context.
|
|
*/
|
|
if (sp->secondary_ep == 0) {
|
|
WARN("%s: No secondary ep on core%u\n", __func__, linear_id);
|
|
return;
|
|
}
|
|
|
|
sec_ec_ep_info.pc = sp->secondary_ep;
|
|
|
|
/*
|
|
* Setup and initialise the SP execution context on this physical cpu.
|
|
*/
|
|
spmc_el1_sp_setup(sp, &sec_ec_ep_info);
|
|
spmc_sp_common_ep_commit(sp, &sec_ec_ep_info);
|
|
|
|
/* Obtain a reference to the SP execution context. */
|
|
ec = spmc_get_sp_ec(sp);
|
|
|
|
/*
|
|
* TODO: Should we do some PM related state tracking of the SP execution
|
|
* context here?
|
|
*/
|
|
|
|
/* Update the runtime model and state of the partition. */
|
|
ec->rt_model = RT_MODEL_INIT;
|
|
ec->rt_state = RT_STATE_RUNNING;
|
|
ec->dir_req_origin_id = INV_SP_ID;
|
|
|
|
INFO("SP (0x%x) init start on core%u.\n", sp->sp_id, linear_id);
|
|
|
|
rc = spmc_sp_synchronous_entry(ec);
|
|
if (rc != 0ULL) {
|
|
ERROR("%s failed (%lu) on CPU%u\n", __func__, rc, linear_id);
|
|
}
|
|
|
|
/* Update the runtime state of the partition. */
|
|
ec->rt_state = RT_STATE_WAITING;
|
|
|
|
VERBOSE("CPU %u on!\n", linear_id);
|
|
}
|
|
/*******************************************************************************
|
|
* Helper function to send a FF-A power management message to an SP.
|
|
******************************************************************************/
|
|
static int32_t spmc_send_pm_msg(uint8_t pm_msg_type,
|
|
unsigned long long psci_event)
|
|
{
|
|
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
|
|
struct sp_exec_ctx *ec;
|
|
gp_regs_t *gpregs_ctx;
|
|
unsigned int linear_id = plat_my_core_pos();
|
|
u_register_t resp;
|
|
uint64_t rc;
|
|
|
|
/* Obtain a reference to the SP execution context. */
|
|
ec = spmc_get_sp_ec(sp);
|
|
|
|
/*
|
|
* TODO: Should we do some PM related state tracking of the SP execution
|
|
* context here?
|
|
*/
|
|
|
|
/*
|
|
* Build an SPMC to SP direct message request.
|
|
* Note that x4-x6 should be populated with the original PSCI arguments.
|
|
*/
|
|
spmc_build_pm_message(get_gpregs_ctx(&ec->cpu_ctx),
|
|
psci_event,
|
|
pm_msg_type,
|
|
sp->sp_id);
|
|
|
|
/* Sanity check partition state. */
|
|
assert(ec->rt_state == RT_STATE_WAITING);
|
|
|
|
/* Update the runtime model and state of the partition. */
|
|
ec->rt_model = RT_MODEL_DIR_REQ;
|
|
ec->rt_state = RT_STATE_RUNNING;
|
|
ec->dir_req_origin_id = FFA_SPMC_ID;
|
|
/* Expect a direct message response from the SP. */
|
|
ec->dir_req_funcid = FFA_FNUM_MSG_SEND_DIRECT_REQ;
|
|
|
|
rc = spmc_sp_synchronous_entry(ec);
|
|
if (rc != 0ULL) {
|
|
ERROR("%s failed (%lu) on CPU%u.\n", __func__, rc, linear_id);
|
|
assert(false);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Validate we receive an expected response from the SP.
|
|
* TODO: We don't currently support aborting an SP in the scenario
|
|
* where it is misbehaving so assert these conditions are not
|
|
* met for now.
|
|
*/
|
|
gpregs_ctx = get_gpregs_ctx(&ec->cpu_ctx);
|
|
|
|
/* Expect a direct message response from the SP. */
|
|
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X0);
|
|
if (resp != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
|
|
ERROR("%s invalid SP response (%lx).\n", __func__, resp);
|
|
assert(false);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Ensure the sender and receiver are populated correctly. */
|
|
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X1);
|
|
if (!(ffa_endpoint_source(resp) == sp->sp_id &&
|
|
ffa_endpoint_destination(resp) == FFA_SPMC_ID)) {
|
|
ERROR("%s invalid src/dst response (%lx).\n", __func__, resp);
|
|
assert(false);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Expect a PM message response from the SP. */
|
|
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X2);
|
|
if ((resp & FFA_FWK_MSG_BIT) == 0U ||
|
|
((resp & FFA_FWK_MSG_MASK) != FFA_PM_MSG_PM_RESP)) {
|
|
ERROR("%s invalid PM response (%lx).\n", __func__, resp);
|
|
assert(false);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Update the runtime state of the partition. */
|
|
ec->rt_state = RT_STATE_WAITING;
|
|
|
|
/* Return the status code returned by the SP */
|
|
return read_ctx_reg(gpregs_ctx, CTX_GPREG_X3);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* spmc_cpu_suspend_finish_handler
|
|
******************************************************************************/
|
|
static void spmc_cpu_suspend_finish_handler(u_register_t unused)
|
|
{
|
|
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
|
|
unsigned int linear_id = plat_my_core_pos();
|
|
int32_t rc;
|
|
|
|
/* Sanity check for a NULL pointer dereference. */
|
|
assert(sp != NULL);
|
|
|
|
/*
|
|
* Check if the SP has subscribed for this power management message.
|
|
* If not then we don't have anything else to do here.
|
|
*/
|
|
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME) == 0U) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = spmc_send_pm_msg(FFA_PM_MSG_WB_REQ, FFA_WB_TYPE_NOTS2RAM);
|
|
if (rc < 0) {
|
|
ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
|
|
return;
|
|
}
|
|
|
|
exit:
|
|
VERBOSE("CPU %u resumed!\n", linear_id);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* spmc_cpu_suspend_handler
|
|
******************************************************************************/
|
|
static void spmc_cpu_suspend_handler(u_register_t unused)
|
|
{
|
|
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
|
|
unsigned int linear_id = plat_my_core_pos();
|
|
int32_t rc;
|
|
|
|
/* Sanity check for a NULL pointer dereference. */
|
|
assert(sp != NULL);
|
|
|
|
/*
|
|
* Check if the SP has subscribed for this power management message.
|
|
* If not then we don't have anything else to do here.
|
|
*/
|
|
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND) == 0U) {
|
|
goto exit;
|
|
}
|
|
|
|
rc = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_SUSPEND_AARCH64);
|
|
if (rc < 0) {
|
|
ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
|
|
return;
|
|
}
|
|
exit:
|
|
VERBOSE("CPU %u suspend!\n", linear_id);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* spmc_cpu_off_handler
|
|
******************************************************************************/
|
|
static int32_t spmc_cpu_off_handler(u_register_t unused)
|
|
{
|
|
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
|
|
unsigned int linear_id = plat_my_core_pos();
|
|
int32_t ret = 0;
|
|
|
|
/* Sanity check for a NULL pointer dereference. */
|
|
assert(sp != NULL);
|
|
|
|
/*
|
|
* Check if the SP has subscribed for this power management message.
|
|
* If not then we don't have anything else to do here.
|
|
*/
|
|
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_OFF) == 0U) {
|
|
goto exit;
|
|
}
|
|
|
|
ret = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_OFF);
|
|
if (ret < 0) {
|
|
ERROR("%s failed (%d) on CPU%u\n", __func__, ret, linear_id);
|
|
return ret;
|
|
}
|
|
|
|
exit:
|
|
VERBOSE("CPU %u off!\n", linear_id);
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Structure populated by the SPM Core to perform any bookkeeping before
|
|
* PSCI executes a power mgmt. operation.
|
|
******************************************************************************/
|
|
const spd_pm_ops_t spmc_pm = {
|
|
.svc_on_finish = spmc_cpu_on_finish_handler,
|
|
.svc_off = spmc_cpu_off_handler,
|
|
.svc_suspend = spmc_cpu_suspend_handler,
|
|
.svc_suspend_finish = spmc_cpu_suspend_finish_handler
|
|
};
|