159bd2ad8SMarc Bonnici /*
2*04c39e46SBoyan Karatotev * Copyright (c) 2022-2025, Arm Limited and Contributors. All rights reserved.
359bd2ad8SMarc Bonnici *
459bd2ad8SMarc Bonnici * SPDX-License-Identifier: BSD-3-Clause
559bd2ad8SMarc Bonnici */
659bd2ad8SMarc Bonnici
759bd2ad8SMarc Bonnici #include <assert.h>
859bd2ad8SMarc Bonnici #include <errno.h>
959bd2ad8SMarc Bonnici
1059bd2ad8SMarc Bonnici #include <lib/el3_runtime/context_mgmt.h>
1159bd2ad8SMarc Bonnici #include <lib/spinlock.h>
1259bd2ad8SMarc Bonnici #include <plat/common/common_def.h>
1359bd2ad8SMarc Bonnici #include <plat/common/platform.h>
1459bd2ad8SMarc Bonnici #include <services/ffa_svc.h>
1559bd2ad8SMarc Bonnici #include "spmc.h"
1659bd2ad8SMarc Bonnici
1759bd2ad8SMarc Bonnici #include <platform_def.h>
1859bd2ad8SMarc Bonnici
1959bd2ad8SMarc Bonnici /*******************************************************************************
2059bd2ad8SMarc Bonnici * spmc_build_pm_message
2159bd2ad8SMarc Bonnici *
2259bd2ad8SMarc Bonnici * Builds an SPMC to SP direct message request.
2359bd2ad8SMarc Bonnici ******************************************************************************/
spmc_build_pm_message(gp_regs_t * gpregs,unsigned long long message,uint8_t pm_msg_type,uint16_t sp_id)2459bd2ad8SMarc Bonnici static void spmc_build_pm_message(gp_regs_t *gpregs,
2559bd2ad8SMarc Bonnici unsigned long long message,
2659bd2ad8SMarc Bonnici uint8_t pm_msg_type,
2759bd2ad8SMarc Bonnici uint16_t sp_id)
2859bd2ad8SMarc Bonnici {
2959bd2ad8SMarc Bonnici write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32);
3059bd2ad8SMarc Bonnici write_ctx_reg(gpregs, CTX_GPREG_X1,
3159bd2ad8SMarc Bonnici (FFA_SPMC_ID << FFA_DIRECT_MSG_SOURCE_SHIFT) |
3259bd2ad8SMarc Bonnici sp_id);
3359bd2ad8SMarc Bonnici write_ctx_reg(gpregs, CTX_GPREG_X2, FFA_FWK_MSG_BIT |
3459bd2ad8SMarc Bonnici (pm_msg_type & FFA_FWK_MSG_MASK));
3559bd2ad8SMarc Bonnici write_ctx_reg(gpregs, CTX_GPREG_X3, message);
3659bd2ad8SMarc Bonnici }
3759bd2ad8SMarc Bonnici
3859bd2ad8SMarc Bonnici /*******************************************************************************
3959173793SNishant Sharma * This CPU has been turned on. Enter the SP to initialise S-EL0 or S-EL1.
4059bd2ad8SMarc Bonnici ******************************************************************************/
spmc_cpu_on_finish_handler(u_register_t unused)4159bd2ad8SMarc Bonnici static void spmc_cpu_on_finish_handler(u_register_t unused)
4259bd2ad8SMarc Bonnici {
4359bd2ad8SMarc Bonnici struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
4459bd2ad8SMarc Bonnici struct sp_exec_ctx *ec;
4559bd2ad8SMarc Bonnici unsigned int linear_id = plat_my_core_pos();
4659bd2ad8SMarc Bonnici entry_point_info_t sec_ec_ep_info = {0};
4759bd2ad8SMarc Bonnici uint64_t rc;
4859bd2ad8SMarc Bonnici
4959bd2ad8SMarc Bonnici /* Sanity check for a NULL pointer dereference. */
5059bd2ad8SMarc Bonnici assert(sp != NULL);
5159bd2ad8SMarc Bonnici
5259173793SNishant Sharma /* Obtain a reference to the SP execution context */
5359173793SNishant Sharma ec = &sp->ec[get_ec_index(sp)];
5459173793SNishant Sharma
5559173793SNishant Sharma /*
5659173793SNishant Sharma * In case of a S-EL0 SP, only initialise the context data structure for
5759173793SNishant Sharma * the secure world on this cpu and return.
5859173793SNishant Sharma */
5959173793SNishant Sharma if (sp->runtime_el == S_EL0) {
6059173793SNishant Sharma /* Assign the context of the SP to this CPU */
6159173793SNishant Sharma cm_set_context(&(ec->cpu_ctx), SECURE);
6259173793SNishant Sharma return;
6359173793SNishant Sharma }
6459173793SNishant Sharma
6559bd2ad8SMarc Bonnici /* Initialize entry point information for the SP. */
6659bd2ad8SMarc Bonnici SET_PARAM_HEAD(&sec_ec_ep_info, PARAM_EP, VERSION_1,
6759bd2ad8SMarc Bonnici SECURE | EP_ST_ENABLE);
6859bd2ad8SMarc Bonnici
6959bd2ad8SMarc Bonnici /*
7059bd2ad8SMarc Bonnici * Check if the primary execution context registered an entry point else
7159bd2ad8SMarc Bonnici * bail out early.
7259bd2ad8SMarc Bonnici * TODO: Add support for boot reason in manifest to allow jumping to
7359bd2ad8SMarc Bonnici * entrypoint into the primary execution context.
7459bd2ad8SMarc Bonnici */
7559bd2ad8SMarc Bonnici if (sp->secondary_ep == 0) {
7659bd2ad8SMarc Bonnici WARN("%s: No secondary ep on core%u\n", __func__, linear_id);
7759bd2ad8SMarc Bonnici return;
7859bd2ad8SMarc Bonnici }
7959bd2ad8SMarc Bonnici
8059bd2ad8SMarc Bonnici sec_ec_ep_info.pc = sp->secondary_ep;
8159bd2ad8SMarc Bonnici
8259bd2ad8SMarc Bonnici /*
8359bd2ad8SMarc Bonnici * Setup and initialise the SP execution context on this physical cpu.
8459bd2ad8SMarc Bonnici */
8559bd2ad8SMarc Bonnici spmc_el1_sp_setup(sp, &sec_ec_ep_info);
8659bd2ad8SMarc Bonnici spmc_sp_common_ep_commit(sp, &sec_ec_ep_info);
8759bd2ad8SMarc Bonnici
8859bd2ad8SMarc Bonnici /* Obtain a reference to the SP execution context. */
8959bd2ad8SMarc Bonnici ec = spmc_get_sp_ec(sp);
9059bd2ad8SMarc Bonnici
9159bd2ad8SMarc Bonnici /*
9259bd2ad8SMarc Bonnici * TODO: Should we do some PM related state tracking of the SP execution
9359bd2ad8SMarc Bonnici * context here?
9459bd2ad8SMarc Bonnici */
9559bd2ad8SMarc Bonnici
9659bd2ad8SMarc Bonnici /* Update the runtime model and state of the partition. */
9759bd2ad8SMarc Bonnici ec->rt_model = RT_MODEL_INIT;
9859bd2ad8SMarc Bonnici ec->rt_state = RT_STATE_RUNNING;
9948fe24c5SMarc Bonnici ec->dir_req_origin_id = INV_SP_ID;
10059bd2ad8SMarc Bonnici
10159bd2ad8SMarc Bonnici INFO("SP (0x%x) init start on core%u.\n", sp->sp_id, linear_id);
10259bd2ad8SMarc Bonnici
10359bd2ad8SMarc Bonnici rc = spmc_sp_synchronous_entry(ec);
10459bd2ad8SMarc Bonnici if (rc != 0ULL) {
10559bd2ad8SMarc Bonnici ERROR("%s failed (%lu) on CPU%u\n", __func__, rc, linear_id);
10659bd2ad8SMarc Bonnici }
10759bd2ad8SMarc Bonnici
10859bd2ad8SMarc Bonnici /* Update the runtime state of the partition. */
10959bd2ad8SMarc Bonnici ec->rt_state = RT_STATE_WAITING;
11059bd2ad8SMarc Bonnici
11159bd2ad8SMarc Bonnici VERBOSE("CPU %u on!\n", linear_id);
11259bd2ad8SMarc Bonnici }
11359bd2ad8SMarc Bonnici /*******************************************************************************
11459bd2ad8SMarc Bonnici * Helper function to send a FF-A power management message to an SP.
11559bd2ad8SMarc Bonnici ******************************************************************************/
spmc_send_pm_msg(uint8_t pm_msg_type,unsigned long long psci_event)11659bd2ad8SMarc Bonnici static int32_t spmc_send_pm_msg(uint8_t pm_msg_type,
11759bd2ad8SMarc Bonnici unsigned long long psci_event)
11859bd2ad8SMarc Bonnici {
11959bd2ad8SMarc Bonnici struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
12059bd2ad8SMarc Bonnici struct sp_exec_ctx *ec;
12159bd2ad8SMarc Bonnici gp_regs_t *gpregs_ctx;
12259bd2ad8SMarc Bonnici unsigned int linear_id = plat_my_core_pos();
12359bd2ad8SMarc Bonnici u_register_t resp;
12459bd2ad8SMarc Bonnici uint64_t rc;
12559bd2ad8SMarc Bonnici
12659bd2ad8SMarc Bonnici /* Obtain a reference to the SP execution context. */
12759bd2ad8SMarc Bonnici ec = spmc_get_sp_ec(sp);
12859bd2ad8SMarc Bonnici
12959bd2ad8SMarc Bonnici /*
13059bd2ad8SMarc Bonnici * TODO: Should we do some PM related state tracking of the SP execution
13159bd2ad8SMarc Bonnici * context here?
13259bd2ad8SMarc Bonnici */
13359bd2ad8SMarc Bonnici
13459bd2ad8SMarc Bonnici /*
13559bd2ad8SMarc Bonnici * Build an SPMC to SP direct message request.
13659bd2ad8SMarc Bonnici * Note that x4-x6 should be populated with the original PSCI arguments.
13759bd2ad8SMarc Bonnici */
13859bd2ad8SMarc Bonnici spmc_build_pm_message(get_gpregs_ctx(&ec->cpu_ctx),
13959bd2ad8SMarc Bonnici psci_event,
14059bd2ad8SMarc Bonnici pm_msg_type,
14159bd2ad8SMarc Bonnici sp->sp_id);
14259bd2ad8SMarc Bonnici
14359bd2ad8SMarc Bonnici /* Sanity check partition state. */
14459bd2ad8SMarc Bonnici assert(ec->rt_state == RT_STATE_WAITING);
14559bd2ad8SMarc Bonnici
14659bd2ad8SMarc Bonnici /* Update the runtime model and state of the partition. */
14759bd2ad8SMarc Bonnici ec->rt_model = RT_MODEL_DIR_REQ;
14859bd2ad8SMarc Bonnici ec->rt_state = RT_STATE_RUNNING;
14948fe24c5SMarc Bonnici ec->dir_req_origin_id = FFA_SPMC_ID;
15009a580b7SLevi Yun /* Expect a direct message response from the SP. */
15109a580b7SLevi Yun ec->dir_req_funcid = FFA_FNUM_MSG_SEND_DIRECT_REQ;
15259bd2ad8SMarc Bonnici
15359bd2ad8SMarc Bonnici rc = spmc_sp_synchronous_entry(ec);
15459bd2ad8SMarc Bonnici if (rc != 0ULL) {
15559bd2ad8SMarc Bonnici ERROR("%s failed (%lu) on CPU%u.\n", __func__, rc, linear_id);
15659bd2ad8SMarc Bonnici assert(false);
15759bd2ad8SMarc Bonnici return -EINVAL;
15859bd2ad8SMarc Bonnici }
15959bd2ad8SMarc Bonnici
16059bd2ad8SMarc Bonnici /*
16159bd2ad8SMarc Bonnici * Validate we receive an expected response from the SP.
16259bd2ad8SMarc Bonnici * TODO: We don't currently support aborting an SP in the scenario
16359bd2ad8SMarc Bonnici * where it is misbehaving so assert these conditions are not
16459bd2ad8SMarc Bonnici * met for now.
16559bd2ad8SMarc Bonnici */
16659bd2ad8SMarc Bonnici gpregs_ctx = get_gpregs_ctx(&ec->cpu_ctx);
16759bd2ad8SMarc Bonnici
16859bd2ad8SMarc Bonnici /* Expect a direct message response from the SP. */
16959bd2ad8SMarc Bonnici resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X0);
17059bd2ad8SMarc Bonnici if (resp != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
17159bd2ad8SMarc Bonnici ERROR("%s invalid SP response (%lx).\n", __func__, resp);
17259bd2ad8SMarc Bonnici assert(false);
17359bd2ad8SMarc Bonnici return -EINVAL;
17459bd2ad8SMarc Bonnici }
17559bd2ad8SMarc Bonnici
17659bd2ad8SMarc Bonnici /* Ensure the sender and receiver are populated correctly. */
17759bd2ad8SMarc Bonnici resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X1);
17859bd2ad8SMarc Bonnici if (!(ffa_endpoint_source(resp) == sp->sp_id &&
17959bd2ad8SMarc Bonnici ffa_endpoint_destination(resp) == FFA_SPMC_ID)) {
18059bd2ad8SMarc Bonnici ERROR("%s invalid src/dst response (%lx).\n", __func__, resp);
18159bd2ad8SMarc Bonnici assert(false);
18259bd2ad8SMarc Bonnici return -EINVAL;
18359bd2ad8SMarc Bonnici }
18459bd2ad8SMarc Bonnici
18559bd2ad8SMarc Bonnici /* Expect a PM message response from the SP. */
18659bd2ad8SMarc Bonnici resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X2);
18759bd2ad8SMarc Bonnici if ((resp & FFA_FWK_MSG_BIT) == 0U ||
18859bd2ad8SMarc Bonnici ((resp & FFA_FWK_MSG_MASK) != FFA_PM_MSG_PM_RESP)) {
18959bd2ad8SMarc Bonnici ERROR("%s invalid PM response (%lx).\n", __func__, resp);
19059bd2ad8SMarc Bonnici assert(false);
19159bd2ad8SMarc Bonnici return -EINVAL;
19259bd2ad8SMarc Bonnici }
19359bd2ad8SMarc Bonnici
19459bd2ad8SMarc Bonnici /* Update the runtime state of the partition. */
19559bd2ad8SMarc Bonnici ec->rt_state = RT_STATE_WAITING;
19659bd2ad8SMarc Bonnici
19759bd2ad8SMarc Bonnici /* Return the status code returned by the SP */
19859bd2ad8SMarc Bonnici return read_ctx_reg(gpregs_ctx, CTX_GPREG_X3);
19959bd2ad8SMarc Bonnici }
20059bd2ad8SMarc Bonnici
20159bd2ad8SMarc Bonnici /*******************************************************************************
20259bd2ad8SMarc Bonnici * spmc_cpu_suspend_finish_handler
20359bd2ad8SMarc Bonnici ******************************************************************************/
spmc_cpu_suspend_finish_handler(u_register_t unused,bool abandon)204*04c39e46SBoyan Karatotev static void spmc_cpu_suspend_finish_handler(u_register_t unused, bool abandon)
20559bd2ad8SMarc Bonnici {
20659bd2ad8SMarc Bonnici struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
20759bd2ad8SMarc Bonnici unsigned int linear_id = plat_my_core_pos();
20859bd2ad8SMarc Bonnici int32_t rc;
20959bd2ad8SMarc Bonnici
21059bd2ad8SMarc Bonnici /* Sanity check for a NULL pointer dereference. */
21159bd2ad8SMarc Bonnici assert(sp != NULL);
21259bd2ad8SMarc Bonnici
213*04c39e46SBoyan Karatotev /* EL3 SPMC is not expected to be used on platforms with pabandon */
214*04c39e46SBoyan Karatotev assert(!abandon);
215*04c39e46SBoyan Karatotev
21659bd2ad8SMarc Bonnici /*
21759bd2ad8SMarc Bonnici * Check if the SP has subscribed for this power management message.
21859bd2ad8SMarc Bonnici * If not then we don't have anything else to do here.
21959bd2ad8SMarc Bonnici */
22059bd2ad8SMarc Bonnici if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME) == 0U) {
22159bd2ad8SMarc Bonnici goto exit;
22259bd2ad8SMarc Bonnici }
22359bd2ad8SMarc Bonnici
22459bd2ad8SMarc Bonnici rc = spmc_send_pm_msg(FFA_PM_MSG_WB_REQ, FFA_WB_TYPE_NOTS2RAM);
22559bd2ad8SMarc Bonnici if (rc < 0) {
22659bd2ad8SMarc Bonnici ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
22759bd2ad8SMarc Bonnici return;
22859bd2ad8SMarc Bonnici }
22959bd2ad8SMarc Bonnici
23059bd2ad8SMarc Bonnici exit:
23159bd2ad8SMarc Bonnici VERBOSE("CPU %u resumed!\n", linear_id);
23259bd2ad8SMarc Bonnici }
23359bd2ad8SMarc Bonnici
23459bd2ad8SMarc Bonnici /*******************************************************************************
23559bd2ad8SMarc Bonnici * spmc_cpu_suspend_handler
23659bd2ad8SMarc Bonnici ******************************************************************************/
spmc_cpu_suspend_handler(u_register_t unused)23759bd2ad8SMarc Bonnici static void spmc_cpu_suspend_handler(u_register_t unused)
23859bd2ad8SMarc Bonnici {
23959bd2ad8SMarc Bonnici struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
24059bd2ad8SMarc Bonnici unsigned int linear_id = plat_my_core_pos();
24159bd2ad8SMarc Bonnici int32_t rc;
24259bd2ad8SMarc Bonnici
24359bd2ad8SMarc Bonnici /* Sanity check for a NULL pointer dereference. */
24459bd2ad8SMarc Bonnici assert(sp != NULL);
24559bd2ad8SMarc Bonnici
24659bd2ad8SMarc Bonnici /*
24759bd2ad8SMarc Bonnici * Check if the SP has subscribed for this power management message.
24859bd2ad8SMarc Bonnici * If not then we don't have anything else to do here.
24959bd2ad8SMarc Bonnici */
25059bd2ad8SMarc Bonnici if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND) == 0U) {
25159bd2ad8SMarc Bonnici goto exit;
25259bd2ad8SMarc Bonnici }
25359bd2ad8SMarc Bonnici
25459bd2ad8SMarc Bonnici rc = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_SUSPEND_AARCH64);
25559bd2ad8SMarc Bonnici if (rc < 0) {
25659bd2ad8SMarc Bonnici ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
25759bd2ad8SMarc Bonnici return;
25859bd2ad8SMarc Bonnici }
25959bd2ad8SMarc Bonnici exit:
26059bd2ad8SMarc Bonnici VERBOSE("CPU %u suspend!\n", linear_id);
26159bd2ad8SMarc Bonnici }
26259bd2ad8SMarc Bonnici
26359bd2ad8SMarc Bonnici /*******************************************************************************
26459bd2ad8SMarc Bonnici * spmc_cpu_off_handler
26559bd2ad8SMarc Bonnici ******************************************************************************/
spmc_cpu_off_handler(u_register_t unused)26659bd2ad8SMarc Bonnici static int32_t spmc_cpu_off_handler(u_register_t unused)
26759bd2ad8SMarc Bonnici {
26859bd2ad8SMarc Bonnici struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
26959bd2ad8SMarc Bonnici unsigned int linear_id = plat_my_core_pos();
27059bd2ad8SMarc Bonnici int32_t ret = 0;
27159bd2ad8SMarc Bonnici
27259bd2ad8SMarc Bonnici /* Sanity check for a NULL pointer dereference. */
27359bd2ad8SMarc Bonnici assert(sp != NULL);
27459bd2ad8SMarc Bonnici
27559bd2ad8SMarc Bonnici /*
27659bd2ad8SMarc Bonnici * Check if the SP has subscribed for this power management message.
27759bd2ad8SMarc Bonnici * If not then we don't have anything else to do here.
27859bd2ad8SMarc Bonnici */
27959bd2ad8SMarc Bonnici if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_OFF) == 0U) {
28059bd2ad8SMarc Bonnici goto exit;
28159bd2ad8SMarc Bonnici }
28259bd2ad8SMarc Bonnici
28359bd2ad8SMarc Bonnici ret = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_OFF);
28459bd2ad8SMarc Bonnici if (ret < 0) {
28559bd2ad8SMarc Bonnici ERROR("%s failed (%d) on CPU%u\n", __func__, ret, linear_id);
28659bd2ad8SMarc Bonnici return ret;
28759bd2ad8SMarc Bonnici }
28859bd2ad8SMarc Bonnici
28959bd2ad8SMarc Bonnici exit:
29059bd2ad8SMarc Bonnici VERBOSE("CPU %u off!\n", linear_id);
29159bd2ad8SMarc Bonnici return ret;
29259bd2ad8SMarc Bonnici }
29359bd2ad8SMarc Bonnici
29459bd2ad8SMarc Bonnici /*******************************************************************************
29559bd2ad8SMarc Bonnici * Structure populated by the SPM Core to perform any bookkeeping before
29659bd2ad8SMarc Bonnici * PSCI executes a power mgmt. operation.
29759bd2ad8SMarc Bonnici ******************************************************************************/
29859bd2ad8SMarc Bonnici const spd_pm_ops_t spmc_pm = {
29959bd2ad8SMarc Bonnici .svc_on_finish = spmc_cpu_on_finish_handler,
30059bd2ad8SMarc Bonnici .svc_off = spmc_cpu_off_handler,
30159bd2ad8SMarc Bonnici .svc_suspend = spmc_cpu_suspend_handler,
30259bd2ad8SMarc Bonnici .svc_suspend_finish = spmc_cpu_suspend_finish_handler
30359bd2ad8SMarc Bonnici };
304