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