xref: /rk3399_ARM-atf/services/std_svc/spm/el3_spmc/spmc_pm.c (revision 35b2bbf4942689fd52fa741ac7d93bc7f1d4c230)
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