xref: /arm-trusted-firmware/services/std_svc/spmd/spmd_pm.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2020-2022, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu #include <errno.h>
9*91f16700Schasinglulu #include <inttypes.h>
10*91f16700Schasinglulu #include <stdint.h>
11*91f16700Schasinglulu 
12*91f16700Schasinglulu #include <lib/el3_runtime/context_mgmt.h>
13*91f16700Schasinglulu #include <lib/spinlock.h>
14*91f16700Schasinglulu #include "spmd_private.h"
15*91f16700Schasinglulu 
16*91f16700Schasinglulu static struct {
17*91f16700Schasinglulu 	bool secondary_ep_locked;
18*91f16700Schasinglulu 	uintptr_t secondary_ep;
19*91f16700Schasinglulu 	spinlock_t lock;
20*91f16700Schasinglulu } g_spmd_pm;
21*91f16700Schasinglulu 
22*91f16700Schasinglulu /*******************************************************************************
23*91f16700Schasinglulu  * spmd_pm_secondary_ep_register
24*91f16700Schasinglulu  ******************************************************************************/
25*91f16700Schasinglulu int spmd_pm_secondary_ep_register(uintptr_t entry_point)
26*91f16700Schasinglulu {
27*91f16700Schasinglulu 	int ret = FFA_ERROR_INVALID_PARAMETER;
28*91f16700Schasinglulu 
29*91f16700Schasinglulu 	spin_lock(&g_spmd_pm.lock);
30*91f16700Schasinglulu 
31*91f16700Schasinglulu 	if (g_spmd_pm.secondary_ep_locked == true) {
32*91f16700Schasinglulu 		goto out;
33*91f16700Schasinglulu 	}
34*91f16700Schasinglulu 
35*91f16700Schasinglulu 	/*
36*91f16700Schasinglulu 	 * Check entry_point address is a PA within
37*91f16700Schasinglulu 	 * load_address <= entry_point < load_address + binary_size
38*91f16700Schasinglulu 	 */
39*91f16700Schasinglulu 	if (!spmd_check_address_in_binary_image(entry_point)) {
40*91f16700Schasinglulu 		ERROR("%s entry point is not within image boundaries\n",
41*91f16700Schasinglulu 			__func__);
42*91f16700Schasinglulu 		goto out;
43*91f16700Schasinglulu 	}
44*91f16700Schasinglulu 
45*91f16700Schasinglulu 	g_spmd_pm.secondary_ep = entry_point;
46*91f16700Schasinglulu 	g_spmd_pm.secondary_ep_locked = true;
47*91f16700Schasinglulu 
48*91f16700Schasinglulu 	VERBOSE("%s %lx\n", __func__, entry_point);
49*91f16700Schasinglulu 
50*91f16700Schasinglulu 	ret = 0;
51*91f16700Schasinglulu 
52*91f16700Schasinglulu out:
53*91f16700Schasinglulu 	spin_unlock(&g_spmd_pm.lock);
54*91f16700Schasinglulu 
55*91f16700Schasinglulu 	return ret;
56*91f16700Schasinglulu }
57*91f16700Schasinglulu 
58*91f16700Schasinglulu /*******************************************************************************
59*91f16700Schasinglulu  * This CPU has been turned on. Enter SPMC to initialise S-EL1 or S-EL2. As part
60*91f16700Schasinglulu  * of the SPMC initialization path, they will initialize any SPs that they
61*91f16700Schasinglulu  * manage. Entry into SPMC is done after initialising minimal architectural
62*91f16700Schasinglulu  * state that guarantees safe execution.
63*91f16700Schasinglulu  ******************************************************************************/
64*91f16700Schasinglulu static void spmd_cpu_on_finish_handler(u_register_t unused)
65*91f16700Schasinglulu {
66*91f16700Schasinglulu 	spmd_spm_core_context_t *ctx = spmd_get_context();
67*91f16700Schasinglulu 	unsigned int linear_id = plat_my_core_pos();
68*91f16700Schasinglulu 	el3_state_t *el3_state;
69*91f16700Schasinglulu 	uintptr_t entry_point;
70*91f16700Schasinglulu 	uint64_t rc;
71*91f16700Schasinglulu 
72*91f16700Schasinglulu 	assert(ctx != NULL);
73*91f16700Schasinglulu 	assert(ctx->state != SPMC_STATE_ON);
74*91f16700Schasinglulu 
75*91f16700Schasinglulu 	spin_lock(&g_spmd_pm.lock);
76*91f16700Schasinglulu 
77*91f16700Schasinglulu 	/*
78*91f16700Schasinglulu 	 * Leave the possibility that the SPMC does not call
79*91f16700Schasinglulu 	 * FFA_SECONDARY_EP_REGISTER in which case re-use the
80*91f16700Schasinglulu 	 * primary core address for booting secondary cores.
81*91f16700Schasinglulu 	 */
82*91f16700Schasinglulu 	if (g_spmd_pm.secondary_ep_locked == true) {
83*91f16700Schasinglulu 		/*
84*91f16700Schasinglulu 		 * The CPU context has already been initialized at boot time
85*91f16700Schasinglulu 		 * (in spmd_spmc_init by a call to cm_setup_context). Adjust
86*91f16700Schasinglulu 		 * below the target core entry point based on the address
87*91f16700Schasinglulu 		 * passed to by FFA_SECONDARY_EP_REGISTER.
88*91f16700Schasinglulu 		 */
89*91f16700Schasinglulu 		entry_point = g_spmd_pm.secondary_ep;
90*91f16700Schasinglulu 		el3_state = get_el3state_ctx(&ctx->cpu_ctx);
91*91f16700Schasinglulu 		write_ctx_reg(el3_state, CTX_ELR_EL3, entry_point);
92*91f16700Schasinglulu 	}
93*91f16700Schasinglulu 
94*91f16700Schasinglulu 	spin_unlock(&g_spmd_pm.lock);
95*91f16700Schasinglulu 
96*91f16700Schasinglulu 	/* Mark CPU as initiating ON operation. */
97*91f16700Schasinglulu 	ctx->state = SPMC_STATE_ON_PENDING;
98*91f16700Schasinglulu 
99*91f16700Schasinglulu 	rc = spmd_spm_core_sync_entry(ctx);
100*91f16700Schasinglulu 	if (rc != 0ULL) {
101*91f16700Schasinglulu 		ERROR("%s failed (%" PRIu64 ") on CPU%u\n", __func__, rc,
102*91f16700Schasinglulu 			linear_id);
103*91f16700Schasinglulu 		ctx->state = SPMC_STATE_OFF;
104*91f16700Schasinglulu 		return;
105*91f16700Schasinglulu 	}
106*91f16700Schasinglulu 
107*91f16700Schasinglulu 	ctx->state = SPMC_STATE_ON;
108*91f16700Schasinglulu 
109*91f16700Schasinglulu 	VERBOSE("CPU %u on!\n", linear_id);
110*91f16700Schasinglulu }
111*91f16700Schasinglulu 
112*91f16700Schasinglulu /*******************************************************************************
113*91f16700Schasinglulu  * spmd_cpu_off_handler
114*91f16700Schasinglulu  ******************************************************************************/
115*91f16700Schasinglulu static int32_t spmd_cpu_off_handler(u_register_t unused)
116*91f16700Schasinglulu {
117*91f16700Schasinglulu 	spmd_spm_core_context_t *ctx = spmd_get_context();
118*91f16700Schasinglulu 	unsigned int linear_id = plat_my_core_pos();
119*91f16700Schasinglulu 	int64_t rc;
120*91f16700Schasinglulu 
121*91f16700Schasinglulu 	assert(ctx != NULL);
122*91f16700Schasinglulu 	assert(ctx->state != SPMC_STATE_OFF);
123*91f16700Schasinglulu 
124*91f16700Schasinglulu 	/* Build an SPMD to SPMC direct message request. */
125*91f16700Schasinglulu 	gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx);
126*91f16700Schasinglulu 	spmd_build_spmc_message(gpregs, FFA_FWK_MSG_PSCI, PSCI_CPU_OFF);
127*91f16700Schasinglulu 
128*91f16700Schasinglulu 	/* Clear remaining x8 - x17 at EL3/SEL2 or EL3/SEL1 boundary. */
129*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X8, 0);
130*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X9, 0);
131*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X10, 0);
132*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X11, 0);
133*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X12, 0);
134*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X13, 0);
135*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X14, 0);
136*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X15, 0);
137*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X16, 0);
138*91f16700Schasinglulu 	write_ctx_reg(gpregs, CTX_GPREG_X17, 0);
139*91f16700Schasinglulu 
140*91f16700Schasinglulu 	rc = spmd_spm_core_sync_entry(ctx);
141*91f16700Schasinglulu 	if (rc != 0ULL) {
142*91f16700Schasinglulu 		ERROR("%s failed (%" PRIu64 ") on CPU%u\n", __func__, rc, linear_id);
143*91f16700Schasinglulu 	}
144*91f16700Schasinglulu 
145*91f16700Schasinglulu 	/* Expect a direct message response from the SPMC. */
146*91f16700Schasinglulu 	u_register_t ffa_resp_func = read_ctx_reg(get_gpregs_ctx(&ctx->cpu_ctx),
147*91f16700Schasinglulu 						  CTX_GPREG_X0);
148*91f16700Schasinglulu 	if (ffa_resp_func != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
149*91f16700Schasinglulu 		ERROR("%s invalid SPMC response (%lx).\n",
150*91f16700Schasinglulu 			__func__, ffa_resp_func);
151*91f16700Schasinglulu 		return -EINVAL;
152*91f16700Schasinglulu 	}
153*91f16700Schasinglulu 
154*91f16700Schasinglulu 	ctx->state = SPMC_STATE_OFF;
155*91f16700Schasinglulu 
156*91f16700Schasinglulu 	VERBOSE("CPU %u off!\n", linear_id);
157*91f16700Schasinglulu 
158*91f16700Schasinglulu 	return 0;
159*91f16700Schasinglulu }
160*91f16700Schasinglulu 
161*91f16700Schasinglulu /*******************************************************************************
162*91f16700Schasinglulu  * Structure populated by the SPM Dispatcher to perform any bookkeeping before
163*91f16700Schasinglulu  * PSCI executes a power mgmt. operation.
164*91f16700Schasinglulu  ******************************************************************************/
165*91f16700Schasinglulu const spd_pm_ops_t spmd_pm = {
166*91f16700Schasinglulu 	.svc_on_finish = spmd_cpu_on_finish_handler,
167*91f16700Schasinglulu 	.svc_off = spmd_cpu_off_handler
168*91f16700Schasinglulu };
169