1 /* 2 * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <errno.h> 9 #include <string.h> 10 #include "spmd_private.h" 11 12 #include <common/debug.h> 13 #include <lib/el3_runtime/context_mgmt.h> 14 #include <services/el3_spmd_logical_sp.h> 15 #include <services/spmc_svc.h> 16 17 18 #if ENABLE_SPMD_LP 19 static bool is_spmd_lp_inited; 20 static bool is_spmc_inited; 21 22 /* 23 * Helper function to obtain the array storing the EL3 24 * SPMD Logical Partition descriptors. 25 */ 26 static struct spmd_lp_desc *get_spmd_el3_lp_array(void) 27 { 28 return (struct spmd_lp_desc *) SPMD_LP_DESCS_START; 29 } 30 31 /******************************************************************************* 32 * Validate any logical partition descriptors before we initialize. 33 * Initialization of said partitions will be taken care of during SPMD boot. 34 ******************************************************************************/ 35 static int el3_spmd_sp_desc_validate(struct spmd_lp_desc *lp_array) 36 { 37 /* Check the array bounds are valid. */ 38 assert(SPMD_LP_DESCS_END > SPMD_LP_DESCS_START); 39 40 /* 41 * No support for SPMD logical partitions when SPMC is at EL3. 42 */ 43 assert(!is_spmc_at_el3()); 44 45 /* If no SPMD logical partitions are implemented then simply bail out. */ 46 if (SPMD_LP_DESCS_COUNT == 0U) { 47 return -1; 48 } 49 50 for (uint32_t index = 0U; index < SPMD_LP_DESCS_COUNT; index++) { 51 struct spmd_lp_desc *lp_desc = &lp_array[index]; 52 53 /* Validate our logical partition descriptors. */ 54 if (lp_desc == NULL) { 55 ERROR("Invalid SPMD Logical SP Descriptor\n"); 56 return -EINVAL; 57 } 58 59 /* 60 * Ensure the ID follows the convention to indicate it resides 61 * in the secure world. 62 */ 63 if (!ffa_is_secure_world_id(lp_desc->sp_id)) { 64 ERROR("Invalid SPMD Logical SP ID (0x%x)\n", 65 lp_desc->sp_id); 66 return -EINVAL; 67 } 68 69 /* Ensure SPMD logical partition is in valid range. */ 70 if (!is_spmd_lp_id(lp_desc->sp_id)) { 71 ERROR("Invalid SPMD Logical Partition ID (0x%x)\n", 72 lp_desc->sp_id); 73 return -EINVAL; 74 } 75 76 /* Ensure the UUID is not the NULL UUID. */ 77 if (lp_desc->uuid[0] == 0 && lp_desc->uuid[1] == 0 && 78 lp_desc->uuid[2] == 0 && lp_desc->uuid[3] == 0) { 79 ERROR("Invalid UUID for SPMD Logical SP (0x%x)\n", 80 lp_desc->sp_id); 81 return -EINVAL; 82 } 83 84 /* Ensure init function callback is registered. */ 85 if (lp_desc->init == NULL) { 86 ERROR("Missing init function for Logical SP(0x%x)\n", 87 lp_desc->sp_id); 88 return -EINVAL; 89 } 90 91 /* Ensure that SPMD LP only supports sending direct requests. */ 92 if (lp_desc->properties != FFA_PARTITION_DIRECT_REQ_SEND) { 93 ERROR("Invalid SPMD logical partition properties (0x%x)\n", 94 lp_desc->properties); 95 return -EINVAL; 96 } 97 98 /* Ensure that all partition IDs are unique. */ 99 for (uint32_t inner_idx = index + 1; 100 inner_idx < SPMD_LP_DESCS_COUNT; inner_idx++) { 101 if (lp_desc->sp_id == lp_array[inner_idx].sp_id) { 102 ERROR("Duplicate SPMD logical SP ID Detected (0x%x)\n", 103 lp_desc->sp_id); 104 return -EINVAL; 105 } 106 } 107 } 108 return 0; 109 } 110 111 static void spmd_encode_ffa_error(struct ffa_value *retval, int32_t error_code) 112 { 113 retval->func = FFA_ERROR; 114 retval->arg1 = FFA_TARGET_INFO_MBZ; 115 retval->arg2 = (uint32_t)error_code; 116 retval->arg3 = FFA_TARGET_INFO_MBZ; 117 retval->arg4 = FFA_TARGET_INFO_MBZ; 118 retval->arg5 = FFA_TARGET_INFO_MBZ; 119 retval->arg6 = FFA_TARGET_INFO_MBZ; 120 retval->arg7 = FFA_TARGET_INFO_MBZ; 121 } 122 123 static void spmd_build_direct_message_req(spmd_spm_core_context_t *ctx, 124 uint64_t x1, uint64_t x2, 125 uint64_t x3, uint64_t x4) 126 { 127 gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); 128 129 write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32); 130 write_ctx_reg(gpregs, CTX_GPREG_X1, x1); 131 write_ctx_reg(gpregs, CTX_GPREG_X2, x2); 132 write_ctx_reg(gpregs, CTX_GPREG_X3, x3); 133 write_ctx_reg(gpregs, CTX_GPREG_X4, x4); 134 write_ctx_reg(gpregs, CTX_GPREG_X5, 0U); 135 write_ctx_reg(gpregs, CTX_GPREG_X6, 0U); 136 write_ctx_reg(gpregs, CTX_GPREG_X7, 0U); 137 } 138 139 static void spmd_encode_ctx_to_ffa_value(spmd_spm_core_context_t *ctx, 140 struct ffa_value *retval) 141 { 142 gp_regs_t *gpregs = get_gpregs_ctx(&ctx->cpu_ctx); 143 144 retval->func = read_ctx_reg(gpregs, CTX_GPREG_X0); 145 retval->arg1 = read_ctx_reg(gpregs, CTX_GPREG_X1); 146 retval->arg2 = read_ctx_reg(gpregs, CTX_GPREG_X2); 147 retval->arg3 = read_ctx_reg(gpregs, CTX_GPREG_X3); 148 retval->arg4 = read_ctx_reg(gpregs, CTX_GPREG_X4); 149 retval->arg5 = read_ctx_reg(gpregs, CTX_GPREG_X5); 150 retval->arg6 = read_ctx_reg(gpregs, CTX_GPREG_X6); 151 retval->arg7 = read_ctx_reg(gpregs, CTX_GPREG_X7); 152 } 153 154 static void spmd_logical_sp_set_dir_req_ongoing(spmd_spm_core_context_t *ctx) 155 { 156 ctx->spmd_lp_sync_req_ongoing |= SPMD_LP_FFA_DIR_REQ_ONGOING; 157 } 158 159 static void spmd_logical_sp_reset_dir_req_ongoing(spmd_spm_core_context_t *ctx) 160 { 161 ctx->spmd_lp_sync_req_ongoing &= ~SPMD_LP_FFA_DIR_REQ_ONGOING; 162 } 163 164 #endif 165 166 /* 167 * Initialize SPMD logical partitions. This function assumes that it is called 168 * only after the SPMC has successfully initialized. 169 */ 170 int32_t spmd_logical_sp_init(void) 171 { 172 #if ENABLE_SPMD_LP 173 int32_t rc = 0; 174 struct spmd_lp_desc *spmd_lp_descs; 175 176 if (is_spmd_lp_inited == true) { 177 return 0; 178 } 179 180 if (is_spmc_inited == false) { 181 return -1; 182 } 183 184 spmd_lp_descs = get_spmd_el3_lp_array(); 185 186 /* Perform initial validation of the SPMD Logical Partitions. */ 187 rc = el3_spmd_sp_desc_validate(spmd_lp_descs); 188 if (rc != 0) { 189 ERROR("Logical SPMD Partition validation failed!\n"); 190 return rc; 191 } 192 193 VERBOSE("SPMD Logical Secure Partition init start.\n"); 194 for (unsigned int i = 0U; i < SPMD_LP_DESCS_COUNT; i++) { 195 rc = spmd_lp_descs[i].init(); 196 if (rc != 0) { 197 ERROR("SPMD Logical SP (0x%x) failed to initialize\n", 198 spmd_lp_descs[i].sp_id); 199 return rc; 200 } 201 VERBOSE("SPMD Logical SP (0x%x) Initialized\n", 202 spmd_lp_descs[i].sp_id); 203 } 204 205 INFO("SPMD Logical Secure Partition init completed.\n"); 206 if (rc == 0) { 207 is_spmd_lp_inited = true; 208 } 209 return rc; 210 #else 211 return 0; 212 #endif 213 } 214 215 void spmd_logical_sp_set_spmc_initialized(void) 216 { 217 #if ENABLE_SPMD_LP 218 is_spmc_inited = true; 219 #endif 220 } 221 222 void spmd_logical_sp_set_spmc_failure(void) 223 { 224 #if ENABLE_SPMD_LP 225 is_spmc_inited = false; 226 #endif 227 } 228 229 /******************************************************************************* 230 * This function sends an FF-A Direct Request from a partition in EL3 to a 231 * partition that may reside under an SPMC (only lower ELs supported). The main 232 * use of this API is for SPMD logical partitions. 233 * The API is expected to be used when there are platform specific SMCs that 234 * need to be routed to a secure partition that is FF-A compliant or when 235 * there are group 0 interrupts that need to be handled first in EL3 and then 236 * forwarded to an FF-A compliant secure partition. Therefore, it is expected 237 * that the handle to the context provided belongs to the non-secure context. 238 * This also means that interrupts/SMCs that trap to EL3 during secure execution 239 * cannot use this API. 240 * x1, x2, x3 and x4 are encoded as specified in the FF-A specification. 241 * retval is used to pass the direct response values to the caller. 242 * The function returns true if retval has valid values, and false otherwise. 243 ******************************************************************************/ 244 bool spmd_el3_ffa_msg_direct_req(uint64_t x1, 245 uint64_t x2, 246 uint64_t x3, 247 uint64_t x4, 248 void *handle, 249 struct ffa_value *retval) 250 { 251 #if ENABLE_SPMD_LP 252 253 uint64_t rc = UINT64_MAX; 254 spmd_spm_core_context_t *ctx = spmd_get_context(); 255 256 if (retval == NULL) { 257 return false; 258 } 259 260 memset(retval, 0, sizeof(*retval)); 261 262 if (!is_spmd_lp_inited || !is_spmc_inited) { 263 VERBOSE("Cannot send SPMD logical partition direct message," 264 " Partitions not initialized or SPMC not initialized.\n"); 265 spmd_encode_ffa_error(retval, FFA_ERROR_DENIED); 266 return true; 267 } 268 269 /* 270 * x2 must be zero, since there is no support for framework message via 271 * an SPMD logical partition. This is sort of a useless check and it is 272 * possible to not take parameter. However, as the framework extends it 273 * may be useful to have x2 and extend this function later with 274 * functionality based on x2. 275 */ 276 if (x2 != 0) { 277 VERBOSE("x2 must be zero. Cannot send framework message.\n"); 278 spmd_encode_ffa_error(retval, FFA_ERROR_DENIED); 279 return true; 280 } 281 282 /* 283 * Current context must be non-secure. API is expected to be used 284 * when entry into EL3 and the SPMD logical partition is via an 285 * interrupt that occurs when execution is in normal world and 286 * SMCs from normal world. FF-A compliant SPMCs are expected to 287 * trap interrupts during secure execution in lower ELs since they 288 * are usually not re-entrant and SMCs from secure world can be 289 * handled synchronously. There is no known use case for an SPMD 290 * logical partition to send a direct message to another partition 291 * in response to a secure interrupt or SMCs from secure world. 292 */ 293 if (handle != cm_get_context(NON_SECURE)) { 294 VERBOSE("Handle must be for the non-secure context.\n"); 295 spmd_encode_ffa_error(retval, FFA_ERROR_DENIED); 296 return true; 297 } 298 299 if (!is_spmd_lp_id(ffa_endpoint_source(x1))) { 300 VERBOSE("Source ID must be valid SPMD logical partition" 301 " ID.\n"); 302 spmd_encode_ffa_error(retval, 303 FFA_ERROR_INVALID_PARAMETER); 304 return true; 305 } 306 307 if (is_spmd_lp_id(ffa_endpoint_destination(x1))) { 308 VERBOSE("Destination ID must not be SPMD logical partition" 309 " ID.\n"); 310 spmd_encode_ffa_error(retval, 311 FFA_ERROR_INVALID_PARAMETER); 312 return true; 313 } 314 315 if (!ffa_is_secure_world_id(ffa_endpoint_destination(x1))) { 316 VERBOSE("Destination ID must be secure world ID.\n"); 317 spmd_encode_ffa_error(retval, 318 FFA_ERROR_INVALID_PARAMETER); 319 return true; 320 } 321 322 if (ffa_endpoint_destination(x1) == SPMD_DIRECT_MSG_ENDPOINT_ID) { 323 VERBOSE("Destination ID must not be SPMD ID.\n"); 324 spmd_encode_ffa_error(retval, 325 FFA_ERROR_INVALID_PARAMETER); 326 return true; 327 } 328 329 if (ffa_endpoint_destination(x1) == spmd_spmc_id_get()) { 330 VERBOSE("Destination ID must not be SPMC ID.\n"); 331 spmd_encode_ffa_error(retval, 332 FFA_ERROR_INVALID_PARAMETER); 333 return true; 334 } 335 336 /* Save the non-secure context before entering SPMC */ 337 cm_el1_sysregs_context_save(NON_SECURE); 338 #if SPMD_SPM_AT_SEL2 339 cm_el2_sysregs_context_save(NON_SECURE); 340 #endif 341 342 /* 343 * Perform synchronous entry into the SPMC. Synchronous entry is 344 * required because the spec requires that a direct message request 345 * from an SPMD LP look like a function call from it's perspective. 346 */ 347 spmd_build_direct_message_req(ctx, x1, x2, x3, x4); 348 spmd_logical_sp_set_dir_req_ongoing(ctx); 349 350 rc = spmd_spm_core_sync_entry(ctx); 351 352 spmd_logical_sp_reset_dir_req_ongoing(ctx); 353 354 if (rc != 0ULL) { 355 ERROR("%s failed (%lx) on CPU%u\n", __func__, rc, 356 plat_my_core_pos()); 357 panic(); 358 } else { 359 spmd_encode_ctx_to_ffa_value(ctx, retval); 360 361 /* 362 * Only expect error or direct response, 363 * spmd_spm_core_sync_exit should not be called on other paths. 364 * Checks are asserts since the LSP can fail gracefully if the 365 * source or destination ids are not the same. Panic'ing would 366 * not provide any benefit. 367 */ 368 assert(is_ffa_error(retval) || is_ffa_direct_msg_resp(retval)); 369 assert(is_ffa_error(retval) || 370 (ffa_endpoint_destination(retval->arg1) == 371 ffa_endpoint_source(x1))); 372 assert(is_ffa_error(retval) || 373 (ffa_endpoint_source(retval->arg1) == 374 ffa_endpoint_destination(x1))); 375 } 376 377 cm_el1_sysregs_context_restore(NON_SECURE); 378 #if SPMD_SPM_AT_SEL2 379 cm_el2_sysregs_context_restore(NON_SECURE); 380 #endif 381 cm_set_next_eret_context(NON_SECURE); 382 383 return true; 384 #else 385 return false; 386 #endif 387 } 388 389 bool is_spmd_logical_sp_dir_req_in_progress( 390 spmd_spm_core_context_t *ctx) 391 { 392 #if ENABLE_SPMD_LP 393 return ((ctx->spmd_lp_sync_req_ongoing & SPMD_LP_FFA_DIR_REQ_ONGOING) 394 == SPMD_LP_FFA_DIR_REQ_ONGOING); 395 #else 396 return false; 397 #endif 398 } 399