From 59bd2ad83c13ed3c84bb9b841032c95927358890 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Tue, 12 Apr 2022 17:18:13 +0100 Subject: [PATCH 1/4] feat(spmc): add support for FF-A power mgmt. messages in the EL3 SPMC This patch adds support for forwarding the following PSCI messages received by the SPMC at EL3 to the S-EL1 SP if the SP has indicated that it wishes to receive the appropriate message via its manifest. 1. A PSCI CPU_OFF message in response to a cpu hot unplug request from the OS. 2. A message to indicate warm boot of a cpu in response to a cpu hot plug request from the OS. 3. A PSCI CPU_SUSPEND message in response to a cpu idle event initiated from the OS. 4. A message to indicate warm boot of a cpu from a shallow power state in response to a cpu resume power event. This patch also implements the FFA_SECONDARY_EP_REGISTER function to enable the SP specify its secondary entrypoint. Signed-off-by: Achin Gupta Signed-off-by: Marc Bonnici Change-Id: I375d0655b2c6fc27445facc39213d1d0678557f4 --- docs/components/ffa-manifest-binding.rst | 10 + include/services/ffa_svc.h | 13 + services/std_svc/spm/el3_spmc/spmc.h | 19 +- services/std_svc/spm/el3_spmc/spmc.mk | 3 +- services/std_svc/spm/el3_spmc/spmc_main.c | 147 ++++++++++- services/std_svc/spm/el3_spmc/spmc_pm.c | 283 ++++++++++++++++++++++ services/std_svc/spmd/spmd_main.c | 2 +- services/std_svc/spmd/spmd_pm.c | 2 +- services/std_svc/spmd/spmd_private.h | 2 - 9 files changed, 465 insertions(+), 16 deletions(-) create mode 100644 services/std_svc/spm/el3_spmc/spmc_pm.c 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..7f0136cb2 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) | \ 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..88c13c392 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -234,13 +234,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; } @@ -1032,6 +1039,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: @@ -1171,6 +1179,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 +1382,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; } @@ -1540,6 +1665,9 @@ int32_t spmc_setup(void) return ret; } + /* Register power management hooks with PSCI */ + psci_register_spd_pm_hook(&spmc_pm); + /* Register init function for deferred init. */ bl31_register_bl32_init(&sp_init); @@ -1575,6 +1703,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, 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..745c9499c 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; 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) From 729d7793f830781ff8ed44d144c3346c6e4251a3 Mon Sep 17 00:00:00 2001 From: Achin Gupta Date: Mon, 4 Oct 2021 20:17:45 +0100 Subject: [PATCH 2/4] feat(spmc): add support for forwarding a secure interrupt to the SP This patch adds support for forwarding a secure interrupt that preempts the normal world to a SP for top-half interrupt handling. Signed-off-by: Achin Gupta Signed-off-by: Marc Bonnici Change-Id: Iaa6e96f4cf8922ba5b6d128a19359df15e44158d --- services/std_svc/spm/el3_spmc/spmc_main.c | 76 +++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index 88c13c392..b4ca5ffa2 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. @@ -1015,6 +1021,7 @@ 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_ID_GET: case FFA_FEATURES: case FFA_VERSION: @@ -1639,6 +1646,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(); @@ -1668,6 +1676,21 @@ int32_t spmc_setup(void) /* 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); @@ -1753,3 +1776,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); +} From 46872e01f5efb555fef8367595b59e5d2f75cec0 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 25 Nov 2021 15:54:52 +0000 Subject: [PATCH 3/4] feat(spmc): add support for FFA_SPM_ID_GET Enable a Secure Partition to query the ID assigned to the SPMC. The SPMD will take care of any calls from the normal world therefore we should not need to handle this case in the SPMC. Signed-off-by: Marc Bonnici Change-Id: I97903e920e928df385addbb2d383f24e602bf2db --- services/std_svc/spm/el3_spmc/spmc_main.c | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/services/std_svc/spm/el3_spmc/spmc_main.c b/services/std_svc/spm/el3_spmc/spmc_main.c index b4ca5ffa2..342f55c0f 100644 --- a/services/std_svc/spm/el3_spmc/spmc_main.c +++ b/services/std_svc/spm/el3_spmc/spmc_main.c @@ -1022,6 +1022,7 @@ static uint64_t ffa_features_handler(uint32_t smc_fid, 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: @@ -1083,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, @@ -1718,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); From 642db9840712044b9c496e04a7acd60580e54117 Mon Sep 17 00:00:00 2001 From: Marc Bonnici Date: Thu, 23 Sep 2021 09:44:14 +0100 Subject: [PATCH 4/4] feat(spmd): allow forwarding of FFA_FRAG_RX/TX calls Enable the SPMD to forward FFA_FRAG_RX/TX calls between the normal world and the SPMC. Signed-off-by: Marc Bonnici Change-Id: I097a48552827a8527dd3efe1155bc601d7cbf887 --- include/services/ffa_svc.h | 4 ++++ services/std_svc/spmd/spmd_main.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/include/services/ffa_svc.h b/include/services/ffa_svc.h index 7f0136cb2..85844899d 100644 --- a/include/services/ffa_svc.h +++ b/include/services/ffa_svc.h @@ -101,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 */ @@ -156,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/spmd/spmd_main.c b/services/std_svc/spmd/spmd_main.c index 745c9499c..e38878432 100644 --- a/services/std_svc/spmd/spmd_main.c +++ b/services/std_svc/spmd/spmd_main.c @@ -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: /*