1aa5da461SJens Wiklander /*
2*04c39e46SBoyan Karatotev * Copyright (c) 2013-2025, Arm Limited and Contributors. All rights reserved.
3aa5da461SJens Wiklander *
482cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause
5aa5da461SJens Wiklander */
6aa5da461SJens Wiklander
7aa5da461SJens Wiklander #include <assert.h>
809d40e0eSAntonio Nino Diaz
909d40e0eSAntonio Nino Diaz #include <arch_helpers.h>
1009d40e0eSAntonio Nino Diaz #include <common/bl_common.h>
1109d40e0eSAntonio Nino Diaz #include <common/debug.h>
1209d40e0eSAntonio Nino Diaz #include <lib/el3_runtime/context_mgmt.h>
1309d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
1409d40e0eSAntonio Nino Diaz
15aa5da461SJens Wiklander #include "opteed_private.h"
16aa5da461SJens Wiklander
17aa5da461SJens Wiklander /*******************************************************************************
18aa5da461SJens Wiklander * The target cpu is being turned on. Allow the OPTEED/OPTEE to perform any
19aa5da461SJens Wiklander * actions needed. Nothing at the moment.
20aa5da461SJens Wiklander ******************************************************************************/
opteed_cpu_on_handler(u_register_t target_cpu)2157d1e5faSMasahiro Yamada static void opteed_cpu_on_handler(u_register_t target_cpu)
22aa5da461SJens Wiklander {
23aa5da461SJens Wiklander }
24aa5da461SJens Wiklander
25aa5da461SJens Wiklander /*******************************************************************************
26aa5da461SJens Wiklander * This cpu is being turned off. Allow the OPTEED/OPTEE to perform any actions
27aa5da461SJens Wiklander * needed
28aa5da461SJens Wiklander ******************************************************************************/
opteed_cpu_off_handler(u_register_t unused)2957d1e5faSMasahiro Yamada static int32_t opteed_cpu_off_handler(u_register_t unused)
30aa5da461SJens Wiklander {
31aa5da461SJens Wiklander int32_t rc = 0;
32fd650ff6SSoby Mathew uint32_t linear_id = plat_my_core_pos();
33aa5da461SJens Wiklander optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
34aa5da461SJens Wiklander
3505c69cf7SJeffrey Kardatzke if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
3605c69cf7SJeffrey Kardatzke return 0;
3705c69cf7SJeffrey Kardatzke }
3805c69cf7SJeffrey Kardatzke
39776ff52aSDaniel Boulby assert(optee_vector_table);
40aa5da461SJens Wiklander assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
41aa5da461SJens Wiklander
42aa5da461SJens Wiklander /* Program the entry point and enter OPTEE */
43776ff52aSDaniel Boulby cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_off_entry);
44aa5da461SJens Wiklander rc = opteed_synchronous_sp_entry(optee_ctx);
45aa5da461SJens Wiklander
46aa5da461SJens Wiklander /*
47aa5da461SJens Wiklander * Read the response from OPTEE. A non-zero return means that
48aa5da461SJens Wiklander * something went wrong while communicating with OPTEE.
49aa5da461SJens Wiklander */
50aa5da461SJens Wiklander if (rc != 0)
51aa5da461SJens Wiklander panic();
52aa5da461SJens Wiklander
53aa5da461SJens Wiklander /*
54aa5da461SJens Wiklander * Reset OPTEE's context for a fresh start when this cpu is turned on
55aa5da461SJens Wiklander * subsequently.
56aa5da461SJens Wiklander */
57aa5da461SJens Wiklander set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_OFF);
58aa5da461SJens Wiklander
59aa5da461SJens Wiklander return 0;
60aa5da461SJens Wiklander }
61aa5da461SJens Wiklander
62aa5da461SJens Wiklander /*******************************************************************************
63aa5da461SJens Wiklander * This cpu is being suspended. S-EL1 state must have been saved in the
64aa5da461SJens Wiklander * resident cpu (mpidr format) if it is a UP/UP migratable OPTEE.
65aa5da461SJens Wiklander ******************************************************************************/
opteed_cpu_suspend_handler(u_register_t max_off_pwrlvl)6657d1e5faSMasahiro Yamada static void opteed_cpu_suspend_handler(u_register_t max_off_pwrlvl)
67aa5da461SJens Wiklander {
68aa5da461SJens Wiklander int32_t rc = 0;
69fd650ff6SSoby Mathew uint32_t linear_id = plat_my_core_pos();
70aa5da461SJens Wiklander optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
71aa5da461SJens Wiklander
7205c69cf7SJeffrey Kardatzke if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
7305c69cf7SJeffrey Kardatzke return;
7405c69cf7SJeffrey Kardatzke }
7505c69cf7SJeffrey Kardatzke
76776ff52aSDaniel Boulby assert(optee_vector_table);
77aa5da461SJens Wiklander assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
78aa5da461SJens Wiklander
79820540bfSJorge Ramirez-Ortiz write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx), CTX_GPREG_X0,
80820540bfSJorge Ramirez-Ortiz max_off_pwrlvl);
81820540bfSJorge Ramirez-Ortiz
8231244d74SSoby Mathew /* Program the entry point and enter OPTEE */
83776ff52aSDaniel Boulby cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_suspend_entry);
84aa5da461SJens Wiklander rc = opteed_synchronous_sp_entry(optee_ctx);
85aa5da461SJens Wiklander
86aa5da461SJens Wiklander /*
87aa5da461SJens Wiklander * Read the response from OPTEE. A non-zero return means that
88aa5da461SJens Wiklander * something went wrong while communicating with OPTEE.
89aa5da461SJens Wiklander */
90aa5da461SJens Wiklander if (rc != 0)
91aa5da461SJens Wiklander panic();
92aa5da461SJens Wiklander
93aa5da461SJens Wiklander /* Update its context to reflect the state OPTEE is in */
94aa5da461SJens Wiklander set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_SUSPEND);
95aa5da461SJens Wiklander }
96aa5da461SJens Wiklander
97aa5da461SJens Wiklander /*******************************************************************************
98aa5da461SJens Wiklander * This cpu has been turned on. Enter OPTEE to initialise S-EL1 and other bits
99aa5da461SJens Wiklander * before passing control back to the Secure Monitor. Entry in S-El1 is done
100aa5da461SJens Wiklander * after initialising minimal architectural state that guarantees safe
101aa5da461SJens Wiklander * execution.
102aa5da461SJens Wiklander ******************************************************************************/
opteed_cpu_on_finish_handler(u_register_t unused)10305c69cf7SJeffrey Kardatzke void opteed_cpu_on_finish_handler(u_register_t unused)
104aa5da461SJens Wiklander {
105aa5da461SJens Wiklander int32_t rc = 0;
106fd650ff6SSoby Mathew uint32_t linear_id = plat_my_core_pos();
107aa5da461SJens Wiklander optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
108aa5da461SJens Wiklander entry_point_info_t optee_on_entrypoint;
109aa5da461SJens Wiklander
110776ff52aSDaniel Boulby assert(optee_vector_table);
11105c69cf7SJeffrey Kardatzke assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_OFF ||
11205c69cf7SJeffrey Kardatzke get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN);
113aa5da461SJens Wiklander
114aa5da461SJens Wiklander opteed_init_optee_ep_state(&optee_on_entrypoint, opteed_rw,
115776ff52aSDaniel Boulby (uint64_t)&optee_vector_table->cpu_on_entry,
1160e8def99SRaymond Mao 0, 0, 0, 0, optee_ctx);
117aa5da461SJens Wiklander
118aa5da461SJens Wiklander /* Initialise this cpu's secure context */
119fd650ff6SSoby Mathew cm_init_my_context(&optee_on_entrypoint);
120aa5da461SJens Wiklander
121aa5da461SJens Wiklander /* Enter OPTEE */
122aa5da461SJens Wiklander rc = opteed_synchronous_sp_entry(optee_ctx);
123aa5da461SJens Wiklander
124aa5da461SJens Wiklander /*
125aa5da461SJens Wiklander * Read the response from OPTEE. A non-zero return means that
126aa5da461SJens Wiklander * something went wrong while communicating with OPTEE.
127aa5da461SJens Wiklander */
128aa5da461SJens Wiklander if (rc != 0)
129aa5da461SJens Wiklander panic();
130aa5da461SJens Wiklander
131aa5da461SJens Wiklander /* Update its context to reflect the state OPTEE is in */
132aa5da461SJens Wiklander set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
133aa5da461SJens Wiklander }
134aa5da461SJens Wiklander
135aa5da461SJens Wiklander /*******************************************************************************
136aa5da461SJens Wiklander * This cpu has resumed from suspend. The OPTEED saved the OPTEE context when it
137aa5da461SJens Wiklander * completed the preceding suspend call. Use that context to program an entry
138aa5da461SJens Wiklander * into OPTEE to allow it to do any remaining book keeping
139aa5da461SJens Wiklander ******************************************************************************/
opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl,bool abandon)140*04c39e46SBoyan Karatotev static void opteed_cpu_suspend_finish_handler(u_register_t max_off_pwrlvl, bool abandon)
141aa5da461SJens Wiklander {
142aa5da461SJens Wiklander int32_t rc = 0;
143fd650ff6SSoby Mathew uint32_t linear_id = plat_my_core_pos();
144aa5da461SJens Wiklander optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
145aa5da461SJens Wiklander
146*04c39e46SBoyan Karatotev /* opteed is not expected to be used on platforms with pabandon */
147*04c39e46SBoyan Karatotev assert(!abandon);
148*04c39e46SBoyan Karatotev
14905c69cf7SJeffrey Kardatzke if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
15005c69cf7SJeffrey Kardatzke return;
15105c69cf7SJeffrey Kardatzke }
15205c69cf7SJeffrey Kardatzke
153776ff52aSDaniel Boulby assert(optee_vector_table);
154aa5da461SJens Wiklander assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_SUSPEND);
155aa5da461SJens Wiklander
156f1054c93SAchin Gupta /* Program the entry point, max_off_pwrlvl and enter the SP */
157aa5da461SJens Wiklander write_ctx_reg(get_gpregs_ctx(&optee_ctx->cpu_ctx),
158aa5da461SJens Wiklander CTX_GPREG_X0,
159f1054c93SAchin Gupta max_off_pwrlvl);
160776ff52aSDaniel Boulby cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->cpu_resume_entry);
161aa5da461SJens Wiklander rc = opteed_synchronous_sp_entry(optee_ctx);
162aa5da461SJens Wiklander
163aa5da461SJens Wiklander /*
164aa5da461SJens Wiklander * Read the response from OPTEE. A non-zero return means that
165aa5da461SJens Wiklander * something went wrong while communicating with OPTEE.
166aa5da461SJens Wiklander */
167aa5da461SJens Wiklander if (rc != 0)
168aa5da461SJens Wiklander panic();
169aa5da461SJens Wiklander
170aa5da461SJens Wiklander /* Update its context to reflect the state OPTEE is in */
171aa5da461SJens Wiklander set_optee_pstate(optee_ctx->state, OPTEE_PSTATE_ON);
172aa5da461SJens Wiklander }
173aa5da461SJens Wiklander
174aa5da461SJens Wiklander /*******************************************************************************
175aa5da461SJens Wiklander * Return the type of OPTEE the OPTEED is dealing with. Report the current
176aa5da461SJens Wiklander * resident cpu (mpidr format) if it is a UP/UP migratable OPTEE.
177aa5da461SJens Wiklander ******************************************************************************/
opteed_cpu_migrate_info(u_register_t * resident_cpu)17857d1e5faSMasahiro Yamada static int32_t opteed_cpu_migrate_info(u_register_t *resident_cpu)
179aa5da461SJens Wiklander {
180aa5da461SJens Wiklander return OPTEE_MIGRATE_INFO;
181aa5da461SJens Wiklander }
182aa5da461SJens Wiklander
183aa5da461SJens Wiklander /*******************************************************************************
184aa5da461SJens Wiklander * System is about to be switched off. Allow the OPTEED/OPTEE to perform
185aa5da461SJens Wiklander * any actions needed.
186aa5da461SJens Wiklander ******************************************************************************/
opteed_system_off(void)187aa5da461SJens Wiklander static void opteed_system_off(void)
188aa5da461SJens Wiklander {
189fd650ff6SSoby Mathew uint32_t linear_id = plat_my_core_pos();
190aa5da461SJens Wiklander optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
191aa5da461SJens Wiklander
19205c69cf7SJeffrey Kardatzke /*
19305c69cf7SJeffrey Kardatzke * OP-TEE must have been initialized in order to reach this location so
19405c69cf7SJeffrey Kardatzke * it is safe to init the CPU context if not already done for this core.
19505c69cf7SJeffrey Kardatzke */
19605c69cf7SJeffrey Kardatzke if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
19705c69cf7SJeffrey Kardatzke opteed_cpu_on_finish_handler(0);
19805c69cf7SJeffrey Kardatzke }
19905c69cf7SJeffrey Kardatzke
200776ff52aSDaniel Boulby assert(optee_vector_table);
201aa5da461SJens Wiklander assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
202aa5da461SJens Wiklander
203aa5da461SJens Wiklander /* Program the entry point */
204776ff52aSDaniel Boulby cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_off_entry);
205aa5da461SJens Wiklander
206aa5da461SJens Wiklander /* Enter OPTEE. We do not care about the return value because we
207aa5da461SJens Wiklander * must continue the shutdown anyway */
208aa5da461SJens Wiklander opteed_synchronous_sp_entry(optee_ctx);
209aa5da461SJens Wiklander }
210aa5da461SJens Wiklander
211aa5da461SJens Wiklander /*******************************************************************************
212aa5da461SJens Wiklander * System is about to be reset. Allow the OPTEED/OPTEE to perform
213aa5da461SJens Wiklander * any actions needed.
214aa5da461SJens Wiklander ******************************************************************************/
opteed_system_reset(void)215aa5da461SJens Wiklander static void opteed_system_reset(void)
216aa5da461SJens Wiklander {
217fd650ff6SSoby Mathew uint32_t linear_id = plat_my_core_pos();
218aa5da461SJens Wiklander optee_context_t *optee_ctx = &opteed_sp_context[linear_id];
219aa5da461SJens Wiklander
22005c69cf7SJeffrey Kardatzke /*
22105c69cf7SJeffrey Kardatzke * OP-TEE must have been initialized in order to reach this location so
22205c69cf7SJeffrey Kardatzke * it is safe to init the CPU context if not already done for this core.
22305c69cf7SJeffrey Kardatzke */
22405c69cf7SJeffrey Kardatzke if (get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_UNKNOWN) {
22505c69cf7SJeffrey Kardatzke opteed_cpu_on_finish_handler(0);
22605c69cf7SJeffrey Kardatzke }
22705c69cf7SJeffrey Kardatzke
228776ff52aSDaniel Boulby assert(optee_vector_table);
229aa5da461SJens Wiklander assert(get_optee_pstate(optee_ctx->state) == OPTEE_PSTATE_ON);
230aa5da461SJens Wiklander
231aa5da461SJens Wiklander /* Program the entry point */
232776ff52aSDaniel Boulby cm_set_elr_el3(SECURE, (uint64_t) &optee_vector_table->system_reset_entry);
233aa5da461SJens Wiklander
234aa5da461SJens Wiklander /* Enter OPTEE. We do not care about the return value because we
235aa5da461SJens Wiklander * must continue the reset anyway */
236aa5da461SJens Wiklander opteed_synchronous_sp_entry(optee_ctx);
237aa5da461SJens Wiklander }
238aa5da461SJens Wiklander
239aa5da461SJens Wiklander
240aa5da461SJens Wiklander /*******************************************************************************
241aa5da461SJens Wiklander * Structure populated by the OPTEE Dispatcher to be given a chance to
242aa5da461SJens Wiklander * perform any OPTEE bookkeeping before PSCI executes a power mgmt.
243aa5da461SJens Wiklander * operation.
244aa5da461SJens Wiklander ******************************************************************************/
245aa5da461SJens Wiklander const spd_pm_ops_t opteed_pm = {
246aa5da461SJens Wiklander .svc_on = opteed_cpu_on_handler,
247aa5da461SJens Wiklander .svc_off = opteed_cpu_off_handler,
248aa5da461SJens Wiklander .svc_suspend = opteed_cpu_suspend_handler,
249aa5da461SJens Wiklander .svc_on_finish = opteed_cpu_on_finish_handler,
250aa5da461SJens Wiklander .svc_suspend_finish = opteed_cpu_suspend_finish_handler,
251aa5da461SJens Wiklander .svc_migrate = NULL,
252aa5da461SJens Wiklander .svc_migrate_info = opteed_cpu_migrate_info,
253aa5da461SJens Wiklander .svc_system_off = opteed_system_off,
254aa5da461SJens Wiklander .svc_system_reset = opteed_system_reset,
255aa5da461SJens Wiklander };
256