mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-25 22:35:42 +00:00
Merge changes from topic "ffa_el3_spmc" into integration
* changes: feat(spmd): allow forwarding of FFA_FRAG_RX/TX calls feat(spmc): add support for FFA_SPM_ID_GET feat(spmc): add support for forwarding a secure interrupt to the SP feat(spmc): add support for FF-A power mgmt. messages in the EL3 SPMC
This commit is contained in:
commit
b1470ccc92
9 changed files with 576 additions and 16 deletions
docs/components
include/services
services/std_svc
|
@ -151,6 +151,16 @@ Partition Properties
|
|||
- List of <u32> tuples, identifying the IDs this partition is acting as
|
||||
proxy for.
|
||||
|
||||
- power-management-messages
|
||||
- value type: <u32>
|
||||
- Specifies which power management messages a partition subscribes to.
|
||||
A set bit means the partition should be informed of the power event, clear
|
||||
bit - should not be informed of event:
|
||||
|
||||
- Bit[0]: CPU_OFF
|
||||
- Bit[1]: CPU_SUSPEND
|
||||
- Bit[2]: CPU_SUSPEND_RESUME
|
||||
|
||||
Memory Regions
|
||||
--------------
|
||||
|
||||
|
|
|
@ -56,6 +56,19 @@
|
|||
(((blk) & FFA_MSG_SEND_ATTRS_BLK_MASK) \
|
||||
<< FFA_MSG_SEND_ATTRS_BLK_SHIFT)
|
||||
|
||||
/* Defines for FF-A framework messages exchanged using direct messages. */
|
||||
#define FFA_FWK_MSG_BIT BIT(31)
|
||||
#define FFA_FWK_MSG_MASK 0xFF
|
||||
#define FFA_FWK_MSG_PSCI U(0x0)
|
||||
|
||||
/* Defines for FF-A power management messages framework messages. */
|
||||
#define FFA_PM_MSG_WB_REQ U(0x1) /* Warm boot request. */
|
||||
#define FFA_PM_MSG_PM_RESP U(0x2) /* Response to PSCI or warmboot req. */
|
||||
|
||||
/* FF-A warm boot types. */
|
||||
#define FFA_WB_TYPE_S2RAM 0x0
|
||||
#define FFA_WB_TYPE_NOTS2RAM 0x1
|
||||
|
||||
/* Get FFA fastcall std FID from function number */
|
||||
#define FFA_FID(smc_cc, func_num) \
|
||||
((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT) | \
|
||||
|
@ -88,6 +101,8 @@
|
|||
#define FFA_FNUM_MEM_RETRIEVE_RESP U(0x75)
|
||||
#define FFA_FNUM_MEM_RELINQUISH U(0x76)
|
||||
#define FFA_FNUM_MEM_RECLAIM U(0x77)
|
||||
#define FFA_FNUM_MEM_FRAG_RX U(0x7A)
|
||||
#define FFA_FNUM_MEM_FRAG_TX U(0x7B)
|
||||
#define FFA_FNUM_NORMAL_WORLD_RESUME U(0x7C)
|
||||
|
||||
/* FF-A v1.1 */
|
||||
|
@ -143,6 +158,8 @@
|
|||
#define FFA_NOTIFICATION_GET FFA_FID(SMC_32, FFA_FNUM_NOTIFICATION_GET)
|
||||
#define FFA_NOTIFICATION_INFO_GET \
|
||||
FFA_FID(SMC_32, FFA_FNUM_NOTIFICATION_INFO_GET)
|
||||
#define FFA_MEM_FRAG_RX FFA_FID(SMC_32, FFA_FNUM_MEM_FRAG_RX)
|
||||
#define FFA_MEM_FRAG_TX FFA_FID(SMC_32, FFA_FNUM_MEM_FRAG_TX)
|
||||
#define FFA_SPM_ID_GET FFA_FID(SMC_32, FFA_FNUM_SPM_ID_GET)
|
||||
#define FFA_NORMAL_WORLD_RESUME FFA_FID(SMC_32, FFA_FNUM_NORMAL_WORLD_RESUME)
|
||||
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
/* 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
|
||||
|
||||
/* FF-A Related helper macros. */
|
||||
#define FFA_ID_MASK U(0xFFFF)
|
||||
#define FFA_PARTITION_ID_SHIFT U(16)
|
||||
|
@ -53,6 +49,13 @@
|
|||
/* Ensure that the page size used by TF-A is 4k aligned. */
|
||||
CASSERT((PAGE_SIZE % FFA_PAGE_SIZE) == 0, assert_aligned_page_size);
|
||||
|
||||
/*
|
||||
* Defines to allow an SP to subscribe for power management messages
|
||||
*/
|
||||
#define FFA_PM_MSG_SUB_CPU_OFF U(1 << 0)
|
||||
#define FFA_PM_MSG_SUB_CPU_SUSPEND U(1 << 1)
|
||||
#define FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME U(1 << 2)
|
||||
|
||||
/*
|
||||
* Runtime states of an execution context as per the FF-A v1.1 specification.
|
||||
*/
|
||||
|
@ -162,6 +165,11 @@ struct secure_partition_desc {
|
|||
|
||||
/* Secondary entrypoint. Only valid for a S-EL1 SP. */
|
||||
uintptr_t secondary_ep;
|
||||
|
||||
/*
|
||||
* Store whether the SP has subscribed to any power management messages.
|
||||
*/
|
||||
uint16_t pwr_mgmt_msgs;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -212,6 +220,9 @@ struct ffa_partition_info_v1_1 {
|
|||
uint32_t uuid[4];
|
||||
};
|
||||
|
||||
/* Reference to power management hooks */
|
||||
extern const spd_pm_ops_t spmc_pm;
|
||||
|
||||
/* Setup Function for different SP types. */
|
||||
void spmc_sp_common_setup(struct secure_partition_desc *sp,
|
||||
entry_point_info_t *ep_info);
|
||||
|
|
|
@ -11,7 +11,8 @@ endif
|
|||
SPMC_SOURCES := $(addprefix services/std_svc/spm/el3_spmc/, \
|
||||
spmc_main.c \
|
||||
spmc_setup.c \
|
||||
logical_sp.c)
|
||||
logical_sp.c \
|
||||
spmc_pm.c)
|
||||
|
||||
# Specify platform specific logical partition implementation.
|
||||
SPMC_LP_SOURCES := $(addprefix ${PLAT_DIR}/, \
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <arch_helpers.h>
|
||||
#include <bl31/bl31.h>
|
||||
#include <bl31/ehf.h>
|
||||
#include <bl31/interrupt_mgmt.h>
|
||||
#include <common/debug.h>
|
||||
#include <common/fdt_wrappers.h>
|
||||
#include <common/runtime_svc.h>
|
||||
|
@ -45,6 +46,11 @@ static struct secure_partition_desc sp_desc[SECURE_PARTITION_COUNT];
|
|||
*/
|
||||
static struct ns_endpoint_desc ns_ep_desc[NS_PARTITION_COUNT];
|
||||
|
||||
static uint64_t spmc_sp_interrupt_handler(uint32_t id,
|
||||
uint32_t flags,
|
||||
void *handle,
|
||||
void *cookie);
|
||||
|
||||
/*
|
||||
* Helper function to obtain the array storing the EL3
|
||||
* Logical Partition descriptors.
|
||||
|
@ -234,13 +240,20 @@ static uint64_t spmc_smc_return(uint32_t smc_fid,
|
|||
******************************************************************************/
|
||||
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;
|
||||
/* Check message type. */
|
||||
if (x2 & FFA_FWK_MSG_BIT) {
|
||||
/* We have a framework message, ensure it is a known message. */
|
||||
if (x2 & ~(FFA_FWK_MSG_MASK | FFA_FWK_MSG_BIT)) {
|
||||
VERBOSE("Invalid message format 0x%lx.\n", x2);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* We have a partition messages, ensure x2 is not set. */
|
||||
if (x2 != (uint64_t) 0) {
|
||||
VERBOSE("Arg2 MBZ for partition messages. (0x%lx).\n",
|
||||
x2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1008,6 +1021,8 @@ static uint64_t ffa_features_handler(uint32_t smc_fid,
|
|||
/* Supported features from both worlds. */
|
||||
case FFA_ERROR:
|
||||
case FFA_SUCCESS_SMC32:
|
||||
case FFA_INTERRUPT:
|
||||
case FFA_SPM_ID_GET:
|
||||
case FFA_ID_GET:
|
||||
case FFA_FEATURES:
|
||||
case FFA_VERSION:
|
||||
|
@ -1032,6 +1047,7 @@ static uint64_t ffa_features_handler(uint32_t smc_fid,
|
|||
/* Execution stops here. */
|
||||
|
||||
/* Supported ABIs only from the secure world. */
|
||||
case FFA_SECONDARY_EP_REGISTER_SMC64:
|
||||
case FFA_MSG_SEND_DIRECT_RESP_SMC32:
|
||||
case FFA_MSG_SEND_DIRECT_RESP_SMC64:
|
||||
case FFA_MSG_WAIT:
|
||||
|
@ -1068,6 +1084,30 @@ static uint64_t ffa_id_get_handler(uint32_t smc_fid,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable an SP to query the ID assigned to the SPMC.
|
||||
*/
|
||||
static uint64_t ffa_spm_id_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)
|
||||
{
|
||||
assert(x1 == 0UL);
|
||||
assert(x2 == 0UL);
|
||||
assert(x3 == 0UL);
|
||||
assert(x4 == 0UL);
|
||||
assert(SMC_GET_GP(handle, CTX_GPREG_X5) == 0UL);
|
||||
assert(SMC_GET_GP(handle, CTX_GPREG_X6) == 0UL);
|
||||
assert(SMC_GET_GP(handle, CTX_GPREG_X7) == 0UL);
|
||||
|
||||
SMC_RET3(handle, FFA_SUCCESS_SMC32, 0x0, FFA_SPMC_ID);
|
||||
}
|
||||
|
||||
static uint64_t ffa_run_handler(uint32_t smc_fid,
|
||||
bool secure_origin,
|
||||
uint64_t x1,
|
||||
|
@ -1171,6 +1211,104 @@ static uint64_t rx_release_handler(uint32_t smc_fid,
|
|||
SMC_RET1(handle, FFA_SUCCESS_SMC32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform initial validation on the provided secondary entry point.
|
||||
* For now ensure it does not lie within the BL31 Image or the SP's
|
||||
* RX/TX buffers as these are mapped within EL3.
|
||||
* TODO: perform validation for additional invalid memory regions.
|
||||
*/
|
||||
static int validate_secondary_ep(uintptr_t ep, struct secure_partition_desc *sp)
|
||||
{
|
||||
struct mailbox *mb;
|
||||
uintptr_t buffer_size;
|
||||
uintptr_t sp_rx_buffer;
|
||||
uintptr_t sp_tx_buffer;
|
||||
uintptr_t sp_rx_buffer_limit;
|
||||
uintptr_t sp_tx_buffer_limit;
|
||||
|
||||
mb = &sp->mailbox;
|
||||
buffer_size = (uintptr_t) (mb->rxtx_page_count * FFA_PAGE_SIZE);
|
||||
sp_rx_buffer = (uintptr_t) mb->rx_buffer;
|
||||
sp_tx_buffer = (uintptr_t) mb->tx_buffer;
|
||||
sp_rx_buffer_limit = sp_rx_buffer + buffer_size;
|
||||
sp_tx_buffer_limit = sp_tx_buffer + buffer_size;
|
||||
|
||||
/*
|
||||
* Check if the entry point lies within BL31, or the
|
||||
* SP's RX or TX buffer.
|
||||
*/
|
||||
if ((ep >= BL31_BASE && ep < BL31_LIMIT) ||
|
||||
(ep >= sp_rx_buffer && ep < sp_rx_buffer_limit) ||
|
||||
(ep >= sp_tx_buffer && ep < sp_tx_buffer_limit)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* This function handles the FFA_SECONDARY_EP_REGISTER SMC to allow an SP to
|
||||
* register an entry point for initialization during a secondary cold boot.
|
||||
******************************************************************************/
|
||||
static uint64_t ffa_sec_ep_register_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;
|
||||
struct sp_exec_ctx *sp_ctx;
|
||||
|
||||
/* This request cannot originate from the Normal world. */
|
||||
if (!secure_origin) {
|
||||
WARN("%s: Can only be called from SWd.\n", __func__);
|
||||
return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
/* Get the context of the current SP. */
|
||||
sp = spmc_get_current_sp_ctx();
|
||||
if (sp == NULL) {
|
||||
WARN("%s: Cannot find SP context.\n", __func__);
|
||||
return spmc_ffa_error_return(handle,
|
||||
FFA_ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
/* Only an S-EL1 SP should be invoking this ABI. */
|
||||
if (sp->runtime_el != S_EL1) {
|
||||
WARN("%s: Can only be called for a S-EL1 SP.\n", __func__);
|
||||
return spmc_ffa_error_return(handle, FFA_ERROR_DENIED);
|
||||
}
|
||||
|
||||
/* Ensure the SP is in its initialization state. */
|
||||
sp_ctx = spmc_get_sp_ec(sp);
|
||||
if (sp_ctx->rt_model != RT_MODEL_INIT) {
|
||||
WARN("%s: Can only be called during SP initialization.\n",
|
||||
__func__);
|
||||
return spmc_ffa_error_return(handle, FFA_ERROR_DENIED);
|
||||
}
|
||||
|
||||
/* Perform initial validation of the secondary entry point. */
|
||||
if (validate_secondary_ep(x1, sp)) {
|
||||
WARN("%s: Invalid entry point provided (0x%lx).\n",
|
||||
__func__, x1);
|
||||
return spmc_ffa_error_return(handle,
|
||||
FFA_ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the secondary entrypoint in SP context.
|
||||
* We don't need a lock here as during partition initialization there
|
||||
* will only be a single core online.
|
||||
*/
|
||||
sp->secondary_ep = x1;
|
||||
VERBOSE("%s: 0x%lx\n", __func__, sp->secondary_ep);
|
||||
|
||||
SMC_RET1(handle, FFA_SUCCESS_SMC32);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* This function will parse the Secure Partition Manifest. From manifest, it
|
||||
* will fetch details for preparing Secure partition image context and secure
|
||||
|
@ -1276,6 +1414,25 @@ static int sp_manifest_parse(void *sp_manifest, int offset,
|
|||
sp->sp_id = config_32;
|
||||
}
|
||||
|
||||
ret = fdt_read_uint32(sp_manifest, node,
|
||||
"power-management-messages", &config_32);
|
||||
if (ret != 0) {
|
||||
WARN("Missing Power Management Messages entry.\n");
|
||||
} else {
|
||||
/*
|
||||
* Ensure only the currently supported power messages have
|
||||
* been requested.
|
||||
*/
|
||||
if (config_32 & ~(FFA_PM_MSG_SUB_CPU_OFF |
|
||||
FFA_PM_MSG_SUB_CPU_SUSPEND |
|
||||
FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME)) {
|
||||
ERROR("Requested unsupported PM messages (%x)\n",
|
||||
config_32);
|
||||
return -EINVAL;
|
||||
}
|
||||
sp->pwr_mgmt_msgs = config_32;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1514,6 +1671,7 @@ void spmc_populate_attrs(spmc_manifest_attribute_t *spmc_attrs)
|
|||
int32_t spmc_setup(void)
|
||||
{
|
||||
int32_t ret;
|
||||
uint32_t flags;
|
||||
|
||||
/* Initialize endpoint descriptors */
|
||||
initalize_sp_descs();
|
||||
|
@ -1540,6 +1698,24 @@ int32_t spmc_setup(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Register power management hooks with PSCI */
|
||||
psci_register_spd_pm_hook(&spmc_pm);
|
||||
|
||||
/*
|
||||
* Register an interrupt handler for S-EL1 interrupts
|
||||
* when generated during code executing in the
|
||||
* non-secure state.
|
||||
*/
|
||||
flags = 0;
|
||||
set_interrupt_rm_flag(flags, NON_SECURE);
|
||||
ret = register_interrupt_type_handler(INTR_TYPE_S_EL1,
|
||||
spmc_sp_interrupt_handler,
|
||||
flags);
|
||||
if (ret != 0) {
|
||||
ERROR("Failed to register interrupt handler! (%d)\n", ret);
|
||||
panic();
|
||||
}
|
||||
|
||||
/* Register init function for deferred init. */
|
||||
bl31_register_bl32_init(&sp_init);
|
||||
|
||||
|
@ -1567,6 +1743,10 @@ uint64_t spmc_smc_handler(uint32_t smc_fid,
|
|||
return ffa_version_handler(smc_fid, secure_origin, x1, x2, x3,
|
||||
x4, cookie, handle, flags);
|
||||
|
||||
case FFA_SPM_ID_GET:
|
||||
return ffa_spm_id_get_handler(smc_fid, secure_origin, x1, x2,
|
||||
x3, x4, cookie, handle, flags);
|
||||
|
||||
case FFA_ID_GET:
|
||||
return ffa_id_get_handler(smc_fid, secure_origin, x1, x2, x3,
|
||||
x4, cookie, handle, flags);
|
||||
|
@ -1575,6 +1755,11 @@ uint64_t spmc_smc_handler(uint32_t smc_fid,
|
|||
return ffa_features_handler(smc_fid, secure_origin, x1, x2, x3,
|
||||
x4, cookie, handle, flags);
|
||||
|
||||
case FFA_SECONDARY_EP_REGISTER_SMC64:
|
||||
return ffa_sec_ep_register_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,
|
||||
|
@ -1620,3 +1805,56 @@ uint64_t spmc_smc_handler(uint32_t smc_fid,
|
|||
}
|
||||
return spmc_ffa_error_return(handle, FFA_ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* This function is the handler registered for S-EL1 interrupts by the SPMC. It
|
||||
* validates the interrupt and upon success arranges entry into the SP for
|
||||
* handling the interrupt.
|
||||
******************************************************************************/
|
||||
static uint64_t spmc_sp_interrupt_handler(uint32_t id,
|
||||
uint32_t flags,
|
||||
void *handle,
|
||||
void *cookie)
|
||||
{
|
||||
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
|
||||
struct sp_exec_ctx *ec;
|
||||
uint32_t linear_id = plat_my_core_pos();
|
||||
|
||||
/* Sanity check for a NULL pointer dereference. */
|
||||
assert(sp != NULL);
|
||||
|
||||
/* Check the security state when the exception was generated. */
|
||||
assert(get_interrupt_src_ss(flags) == NON_SECURE);
|
||||
|
||||
/* Panic if not an S-EL1 Partition. */
|
||||
if (sp->runtime_el != S_EL1) {
|
||||
ERROR("Interrupt received for a non S-EL1 SP on core%u.\n",
|
||||
linear_id);
|
||||
panic();
|
||||
}
|
||||
|
||||
/* Obtain a reference to the SP execution context. */
|
||||
ec = spmc_get_sp_ec(sp);
|
||||
|
||||
/* Ensure that the execution context is in waiting state else panic. */
|
||||
if (ec->rt_state != RT_STATE_WAITING) {
|
||||
ERROR("SP EC on core%u is not waiting (%u), it is (%u).\n",
|
||||
linear_id, RT_STATE_WAITING, ec->rt_state);
|
||||
panic();
|
||||
}
|
||||
|
||||
/* Update the runtime model and state of the partition. */
|
||||
ec->rt_model = RT_MODEL_INTR;
|
||||
ec->rt_state = RT_STATE_RUNNING;
|
||||
|
||||
VERBOSE("SP (0x%x) interrupt start on core%u.\n", sp->sp_id, linear_id);
|
||||
|
||||
/*
|
||||
* Forward the interrupt to the S-EL1 SP. The interrupt ID is not
|
||||
* populated as the SP can determine this by itself.
|
||||
*/
|
||||
return spmd_smc_switch_state(FFA_INTERRUPT, false,
|
||||
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
||||
FFA_PARAM_MBZ, FFA_PARAM_MBZ,
|
||||
handle);
|
||||
}
|
||||
|
|
283
services/std_svc/spm/el3_spmc/spmc_pm.c
Normal file
283
services/std_svc/spm/el3_spmc/spmc_pm.c
Normal file
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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-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);
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
};
|
|
@ -684,7 +684,7 @@ uint64_t spmd_smc_handler(uint32_t smc_fid,
|
|||
(SMC_GET_GP(gpregs, CTX_GPREG_X0) !=
|
||||
FFA_MSG_SEND_DIRECT_RESP_SMC32) ||
|
||||
(SMC_GET_GP(gpregs, CTX_GPREG_X2) !=
|
||||
(SPMD_FWK_MSG_BIT |
|
||||
(FFA_FWK_MSG_BIT |
|
||||
SPMD_FWK_MSG_FFA_VERSION_RESP))) {
|
||||
ERROR("Failed to forward FFA_VERSION\n");
|
||||
ret = FFA_ERROR_NOT_SUPPORTED;
|
||||
|
@ -875,6 +875,8 @@ uint64_t spmd_smc_handler(uint32_t smc_fid,
|
|||
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:
|
||||
/*
|
||||
|
|
|
@ -123,7 +123,7 @@ static int32_t spmd_cpu_off_handler(u_register_t unused)
|
|||
|
||||
/* Build an SPMD to SPMC direct message request. */
|
||||
spmd_build_spmc_message(get_gpregs_ctx(&ctx->cpu_ctx),
|
||||
SPMD_FWK_MSG_PSCI, PSCI_CPU_OFF);
|
||||
FFA_FWK_MSG_PSCI, PSCI_CPU_OFF);
|
||||
|
||||
rc = spmd_spm_core_sync_entry(ctx);
|
||||
if (rc != 0ULL) {
|
||||
|
|
|
@ -59,8 +59,6 @@ typedef struct spmd_spm_core_context {
|
|||
#define FFA_NS_ENDPOINT_ID U(0)
|
||||
|
||||
/* 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)
|
||||
#define SPMD_FWK_MSG_FFA_VERSION_REQ U(0x8)
|
||||
#define SPMD_FWK_MSG_FFA_VERSION_RESP U(0x9)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue