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 ******************************************************************************/ 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 ******************************************************************************/ 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 ******************************************************************************/ 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 ******************************************************************************/ 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 ******************************************************************************/ 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 ******************************************************************************/ 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 ******************************************************************************/ 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 ******************************************************************************/ 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