Browse Source

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
pull/1986/head
Olivier Deprez 3 years ago
committed by TrustedFirmware Code Review
parent
commit
b1470ccc92
  1. 10
      docs/components/ffa-manifest-binding.rst
  2. 17
      include/services/ffa_svc.h
  3. 19
      services/std_svc/spm/el3_spmc/spmc.h
  4. 3
      services/std_svc/spm/el3_spmc/spmc.mk
  5. 252
      services/std_svc/spm/el3_spmc/spmc_main.c
  6. 283
      services/std_svc/spm/el3_spmc/spmc_pm.c
  7. 4
      services/std_svc/spmd/spmd_main.c
  8. 2
      services/std_svc/spmd/spmd_pm.c
  9. 2
      services/std_svc/spmd/spmd_private.h

10
docs/components/ffa-manifest-binding.rst

@ -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
--------------

17
include/services/ffa_svc.h

@ -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)

19
services/std_svc/spm/el3_spmc/spmc.h

@ -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);

3
services/std_svc/spm/el3_spmc/spmc.mk

@ -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}/, \

252
services/std_svc/spm/el3_spmc/spmc_main.c

@ -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

@ -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
};

4
services/std_svc/spmd/spmd_main.c

@ -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:
/*

2
services/std_svc/spmd/spmd_pm.c

@ -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) {

2
services/std_svc/spmd/spmd_private.h

@ -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…
Cancel
Save