diff --git a/docs/components/ffa-manifest-binding.rst b/docs/components/ffa-manifest-binding.rst index df2985ccc..59996cc27 100644 --- a/docs/components/ffa-manifest-binding.rst +++ b/docs/components/ffa-manifest-binding.rst @@ -151,6 +151,16 @@ Partition Properties - List of tuples, identifying the IDs this partition is acting as proxy for. +- power-management-messages + - value type: + - 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 -------------- diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h index 153d206b9..85844899d 100644 --- a/include/services/ffa_svc.h +++ b/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) diff --git a/services/std_svc/spm/el3_spmc/spmc.h b/services/std_svc/spm/el3_spmc/spmc.h index 5f397338f..0e08d2e38 100644 --- a/services/std_svc/spm/el3_spmc/spmc.h +++ b/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); diff --git a/services/std_svc/spm/el3_spmc/spmc.mk b/services/std_svc/spm/el3_spmc/spmc.mk index 8067c74eb..9f82ccbf3 100644 --- a/services/std_svc/spm/el3_spmc/spmc.mk +++ b/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}/, \ diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index 5dbc48d67..342f55c0f 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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); +} diff --git a/services/std_svc/spm/el3_spmc/spmc_pm.c b/services/std_svc/spm/el3_spmc/spmc_pm.c new file mode 100644 index 000000000..d25344cb9 --- /dev/null +++ b/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 +#include + +#include +#include +#include +#include +#include +#include "spmc.h" + +#include + +/******************************************************************************* + * 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 +}; diff --git a/services/std_svc/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index 777a96203..e38878432 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/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: /* diff --git a/services/std_svc/spmd/spmd_pm.c b/services/std_svc/spmd/spmd_pm.c index b71916130..a2704dd81 100644 --- a/services/std_svc/spmd/spmd_pm.c +++ b/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) { diff --git a/services/std_svc/spmd/spmd_private.h b/services/std_svc/spmd/spmd_private.h index 4c298c9e8..07fecb66c 100644 --- a/services/std_svc/spmd/spmd_private.h +++ b/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)