1 /*
2 * Copyright (c) 2013-2025, 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, 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,bool abandon)140 static void opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl, bool abandon)
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 /* opteed is not expected to be used on platforms with pabandon */
147 assert(!abandon);
148
149 if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
150 return;
151 }
152
153 assert(optee_vector_table);
154 assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_SUSPEND);
155
156 /* Program the entry point, max_off_pwrlvl and enter the SP */
157 write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx),
158 CTX_GPREG_X0,
159 max_off_pwrlvl);
160 cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_resume_entry);
161 rc = opteed_synchronous_sp_entry(optee_ctx);
162
163 /*
164 * Read the response from OPTEE. A non-zero return means that
165 * something went wrong while communicating with OPTEE.
166 */
167 if (rc != 0)
168 panic();
169
170 /* Update its context to reflect the state OPTEE is in */
171 set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
172 }
173
174 /*******************************************************************************
175 * Return the type of OPTEE the OPTEED is dealing with. Report the current
176 * resident cpu (mpidr format) if it is a UP/UP migratable OPTEE.
177 ******************************************************************************/
opteed_cpu_migrate_info(u_register_t * resident_cpu)178 static int32_t opteed_cpu_migrate_info(u_register_t *resident_cpu)
179 {
180 return OPTEE_MIGRATE_INFO;
181 }
182
183 /*******************************************************************************
184 * System is about to be switched off. Allow the OPTEED/OPTEE to perform
185 * any actions needed.
186 ******************************************************************************/
opteed_system_off(void)187 static void opteed_system_off(void)
188 {
189 uint32_t linear_id = plat_my_core_pos();
190 optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
191
192 /*
193 * OP-TEE must have been initialized in order to reach this location so
194 * it is safe to init the CPU context if not already done for this core.
195 */
196 if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
197 opteed_cpu_on_finish_handler(0);
198 }
199
200 assert(optee_vector_table);
201 assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
202
203 /* Program the entry point */
204 cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_off_entry);
205
206 /* Enter OPTEE. We do not care about the return value because we
207 * must continue the shutdown anyway */
208 opteed_synchronous_sp_entry(optee_ctx);
209 }
210
211 /*******************************************************************************
212 * System is about to be reset. Allow the OPTEED/OPTEE to perform
213 * any actions needed.
214 ******************************************************************************/
opteed_system_reset(void)215 static void opteed_system_reset(void)
216 {
217 uint32_t linear_id = plat_my_core_pos();
218 optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
219
220 /*
221 * OP-TEE must have been initialized in order to reach this location so
222 * it is safe to init the CPU context if not already done for this core.
223 */
224 if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
225 opteed_cpu_on_finish_handler(0);
226 }
227
228 assert(optee_vector_table);
229 assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
230
231 /* Program the entry point */
232 cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_reset_entry);
233
234 /* Enter OPTEE. We do not care about the return value because we
235 * must continue the reset anyway */
236 opteed_synchronous_sp_entry(optee_ctx);
237 }
238
239
240 /*******************************************************************************
241 * Structure populated by the OPTEE Dispatcher to be given a chance to
242 * perform any OPTEE bookkeeping before PSCI executes a power mgmt.
243 * operation.
244 ******************************************************************************/
245 const spd_pm_ops_t opteed_pm = {
246 .svc_on = opteed_cpu_on_handler,
247 .svc_off = opteed_cpu_off_handler,
248 .svc_suspend = opteed_cpu_suspend_handler,
249 .svc_on_finish = opteed_cpu_on_finish_handler,
250 .svc_suspend_finish = opteed_cpu_suspend_finish_handler,
251 .svc_migrate = NULL,
252 .svc_migrate_info = opteed_cpu_migrate_info,
253 .svc_system_off = opteed_system_off,
254 .svc_system_reset = opteed_system_reset,
255 };
256