1 /*
2  * Copyright (c) 2013-2023, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 
9 #include <arch_helpers.h>
10 #include <common/bl_common.h>
11 #include <common/debug.h>
12 #include <lib/el3_runtime/context_mgmt.h>
13 #include <plat/common/platform.h>
14 
15 #include "opteed_private.h"
16 
17 /*******************************************************************************
18  * The target cpu is being turned on. Allow the OPTEED/OPTEE to perform any
19  * actions needed. Nothing at the moment.
20  ******************************************************************************/
opteed_cpu_on_handler(u_register_t target_cpu)21 static void opteed_cpu_on_handler(u_register_t target_cpu)
22 {
23 }
24 
25 /*******************************************************************************
26  * This cpu is being turned off. Allow the OPTEED/OPTEE to perform any actions
27  * needed
28  ******************************************************************************/
opteed_cpu_off_handler(u_register_t unused)29 static int32_t opteed_cpu_off_handler(u_register_t unused)
30 {
31 	int32_t rc = 0;
32 	uint32_t linear_id = plat_my_core_pos();
33 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
34 
35 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
36 		return 0;
37 	}
38 
39 	assert(optee_vector_table);
40 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
41 
42 	/* Program the entry point and enter OPTEE */
43 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_off_entry);
44 	rc = opteed_synchronous_sp_entry(optee_ctx);
45 
46 	/*
47 	 * Read the response from OPTEE. A non-zero return means that
48 	 * something went wrong while communicating with OPTEE.
49 	 */
50 	if (rc != 0)
51 		panic();
52 
53 	/*
54 	 * Reset OPTEE's context for a fresh start when this cpu is turned on
55 	 * subsequently.
56 	 */
57 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_OFF);
58 
59 	 return 0;
60 }
61 
62 /*******************************************************************************
63  * This cpu is being suspended. S-EL1 state must have been saved in the
64  * resident cpu (mpidr format) if it is a UP/UP migratable OPTEE.
65  ******************************************************************************/
opteed_cpu_suspend_handler(u_register_t max_off_pwrlvl)66 static void opteed_cpu_suspend_handler(u_register_t max_off_pwrlvl)
67 {
68 	int32_t rc = 0;
69 	uint32_t linear_id = plat_my_core_pos();
70 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
71 
72 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
73 		return;
74 	}
75 
76 	assert(optee_vector_table);
77 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
78 
79 	write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx), CTX_GPREG_X0,
80 		      max_off_pwrlvl);
81 
82 	/* Program the entry point and enter OPTEE */
83 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_suspend_entry);
84 	rc = opteed_synchronous_sp_entry(optee_ctx);
85 
86 	/*
87 	 * Read the response from OPTEE. A non-zero return means that
88 	 * something went wrong while communicating with OPTEE.
89 	 */
90 	if (rc != 0)
91 		panic();
92 
93 	/* Update its context to reflect the state OPTEE is in */
94 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_SUSPEND);
95 }
96 
97 /*******************************************************************************
98  * This cpu has been turned on. Enter OPTEE to initialise S-EL1 and other bits
99  * before passing control back to the Secure Monitor. Entry in S-El1 is done
100  * after initialising minimal architectural state that guarantees safe
101  * execution.
102  ******************************************************************************/
opteed_cpu_on_finish_handler(u_register_t unused)103 void opteed_cpu_on_finish_handler(u_register_t unused)
104 {
105 	int32_t rc = 0;
106 	uint32_t linear_id = plat_my_core_pos();
107 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
108 	entry_point_info_t optee_on_entrypoint;
109 
110 	assert(optee_vector_table);
111 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_OFF ||
112 	       get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN);
113 
114 	opteed_init_optee_ep_state(&optee_on_entrypoint, opteed_rw,
115 				(uint64_t)&optee_vector_table->cpu_on_entry,
116 				0, 0, 0, optee_ctx);
117 
118 	/* Initialise this cpu's secure context */
119 	cm_init_my_context(&optee_on_entrypoint);
120 
121 	/* Enter OPTEE */
122 	rc = opteed_synchronous_sp_entry(optee_ctx);
123 
124 	/*
125 	 * Read the response from OPTEE. A non-zero return means that
126 	 * something went wrong while communicating with OPTEE.
127 	 */
128 	if (rc != 0)
129 		panic();
130 
131 	/* Update its context to reflect the state OPTEE is in */
132 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
133 }
134 
135 /*******************************************************************************
136  * This cpu has resumed from suspend. The OPTEED saved the OPTEE context when it
137  * completed the preceding suspend call. Use that context to program an entry
138  * into OPTEE to allow it to do any remaining book keeping
139  ******************************************************************************/
opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl)140 static void opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl)
141 {
142 	int32_t rc = 0;
143 	uint32_t linear_id = plat_my_core_pos();
144 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
145 
146 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
147 		return;
148 	}
149 
150 	assert(optee_vector_table);
151 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_SUSPEND);
152 
153 	/* Program the entry point, max_off_pwrlvl and enter the SP */
154 	write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx),
155 		      CTX_GPREG_X0,
156 		      max_off_pwrlvl);
157 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_resume_entry);
158 	rc = opteed_synchronous_sp_entry(optee_ctx);
159 
160 	/*
161 	 * Read the response from OPTEE. A non-zero return means that
162 	 * something went wrong while communicating with OPTEE.
163 	 */
164 	if (rc != 0)
165 		panic();
166 
167 	/* Update its context to reflect the state OPTEE is in */
168 	set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
169 }
170 
171 /*******************************************************************************
172  * Return the type of OPTEE the OPTEED is dealing with. Report the current
173  * resident cpu (mpidr format) if it is a UP/UP migratable OPTEE.
174  ******************************************************************************/
opteed_cpu_migrate_info(u_register_t * resident_cpu)175 static int32_t opteed_cpu_migrate_info(u_register_t *resident_cpu)
176 {
177 	return OPTEE_MIGRATE_INFO;
178 }
179 
180 /*******************************************************************************
181  * System is about to be switched off. Allow the OPTEED/OPTEE to perform
182  * any actions needed.
183  ******************************************************************************/
opteed_system_off(void)184 static void opteed_system_off(void)
185 {
186 	uint32_t linear_id = plat_my_core_pos();
187 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
188 
189 	/*
190 	 * OP-TEE must have been initialized in order to reach this location so
191 	 * it is safe to init the CPU context if not already done for this core.
192 	 */
193 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
194 		opteed_cpu_on_finish_handler(0);
195 	}
196 
197 	assert(optee_vector_table);
198 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
199 
200 	/* Program the entry point */
201 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_off_entry);
202 
203 	/* Enter OPTEE. We do not care about the return value because we
204 	 * must continue the shutdown anyway */
205 	opteed_synchronous_sp_entry(optee_ctx);
206 }
207 
208 /*******************************************************************************
209  * System is about to be reset. Allow the OPTEED/OPTEE to perform
210  * any actions needed.
211  ******************************************************************************/
opteed_system_reset(void)212 static void opteed_system_reset(void)
213 {
214 	uint32_t linear_id = plat_my_core_pos();
215 	optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
216 
217 	/*
218 	 * OP-TEE must have been initialized in order to reach this location so
219 	 * it is safe to init the CPU context if not already done for this core.
220 	 */
221 	if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
222 		opteed_cpu_on_finish_handler(0);
223 	}
224 
225 	assert(optee_vector_table);
226 	assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
227 
228 	/* Program the entry point */
229 	cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_reset_entry);
230 
231 	/* Enter OPTEE. We do not care about the return value because we
232 	 * must continue the reset anyway */
233 	opteed_synchronous_sp_entry(optee_ctx);
234 }
235 
236 
237 /*******************************************************************************
238  * Structure populated by the OPTEE Dispatcher to be given a chance to
239  * perform any OPTEE bookkeeping before PSCI executes a power mgmt.
240  * operation.
241  ******************************************************************************/
242 const spd_pm_ops_t opteed_pm = {
243 	.svc_on = opteed_cpu_on_handler,
244 	.svc_off = opteed_cpu_off_handler,
245 	.svc_suspend = opteed_cpu_suspend_handler,
246 	.svc_on_finish = opteed_cpu_on_finish_handler,
247 	.svc_suspend_finish = opteed_cpu_suspend_finish_handler,
248 	.svc_migrate = NULL,
249 	.svc_migrate_info = opteed_cpu_migrate_info,
250 	.svc_system_off = opteed_system_off,
251 	.svc_system_reset = opteed_system_reset,
252 };
253