/** \brief Test case: TC_CoreFunc_PSPLIM \details - Check if __get_PSPLIM and __set_PSPLIM intrinsic can be used to manipulate process stack pointer limit. */ void TC_CoreFunc_PSPLIM (void) { // don't use stack for this variables static uint32_t orig; static uint32_t psplim; static uint32_t result; orig = __get_PSPLIM(); psplim = orig + 0x12345678U; __set_PSPLIM(psplim); result = __get_PSPLIM(); __set_PSPLIM(orig); #if (!(defined (__ARM_ARCH_8M_MAIN__ ) && (__ARM_ARCH_8M_MAIN__ == 1)) && \ (!defined (__ARM_FEATURE_CMSE ) || (__ARM_FEATURE_CMSE < 3))) // without main extensions, the non-secure PSPLIM is RAZ/WI ASSERT_TRUE(result == 0U); #else ASSERT_TRUE(result == psplim); #endif }
static int32_t tfm_return_from_partition(uint32_t *excReturn) { uint32_t current_partition_idx = tfm_spm_partition_get_running_partition_idx(); const struct spm_partition_runtime_data_t *curr_part_data, *ret_part_data; uint32_t current_partition_flags; uint32_t return_partition_idx; uint32_t return_partition_flags; uint32_t psp = __get_PSP(); size_t i; struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp; struct iovec_args_t *iovec_args; if (current_partition_idx == SPM_INVALID_PARTITION_IDX) { return TFM_SECURE_UNLOCK_FAILED; } curr_part_data = tfm_spm_partition_get_runtime_data(current_partition_idx); return_partition_idx = curr_part_data->caller_partition_idx; if (return_partition_idx == SPM_INVALID_PARTITION_IDX) { return TFM_SECURE_UNLOCK_FAILED; } ret_part_data = tfm_spm_partition_get_runtime_data(return_partition_idx); return_partition_flags = tfm_spm_partition_get_flags(return_partition_idx); current_partition_flags = tfm_spm_partition_get_flags( current_partition_idx); tfm_secure_lock--; if((return_partition_flags & SPM_PART_FLAG_APP_ROT) == 0) { /* Re-enable NS exceptions when secure service returns to NS client. * FixMe: * To be removed when pre-emption and context management issues have * been analysed and resolved. */ TFM_NS_EXC_ENABLE(); } #if (TFM_LVL != 1) && (TFM_LVL != 2) /* Deconfigure completed partition environment */ tfm_spm_partition_sandbox_deconfig(current_partition_idx); if (tfm_secure_api_initializing) { /* Restore privilege for thread mode during TF-M init. This is only * have to be done if the partition is not trusted. */ if ((current_partition_flags & SPM_PART_FLAG_PSA_ROT) == 0) { CONTROL_Type ctrl; ctrl.w = __get_CONTROL(); ctrl.b.nPRIV = 0; __set_CONTROL(ctrl.w); __DSB(); __ISB(); } } else { /* Configure the caller partition environment in case this was a * partition to partition call and returning to untrusted partition */ if (tfm_spm_partition_sandbox_config(return_partition_idx) != SPM_ERR_OK) { ERROR_MSG("Failed to configure sandbox for partition!"); tfm_secure_api_error_handler(); } if (return_partition_flags & SPM_PART_FLAG_APP_ROT) { /* Restore share status */ tfm_spm_partition_set_share( return_partition_idx, tfm_spm_partition_get_runtime_data( return_partition_idx)->share); } } #endif #if TFM_LVL == 1 if (!(return_partition_flags & SPM_PART_FLAG_APP_ROT) || (tfm_secure_api_initializing)) { /* In TFM level 1 context restore is only done when * returning to NS or after initialization */ /* Restore caller context */ restore_caller_ctx(svc_ctx, (struct tfm_exc_stack_t *)ret_part_data->stack_ptr); *excReturn = ret_part_data->lr; __set_PSP(ret_part_data->stack_ptr); extern uint32_t Image$$ARM_LIB_STACK$$ZI$$Base[]; uint32_t psp_stack_bottom = (uint32_t)Image$$ARM_LIB_STACK$$ZI$$Base; __set_PSPLIM(psp_stack_bottom); /* FIXME: The condition should be removed once all the secure service * calls are done via the iovec veneers */ if (curr_part_data->iovec_api) { iovec_args = (struct iovec_args_t *) ((uint32_t)®ION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit)- sizeof(struct iovec_args_t)); for (i = 0; i < curr_part_data->iovec_args.out_len; ++i) { curr_part_data->orig_outvec[i].len = iovec_args->out_vec[i].len; } tfm_clear_iovec_parameters(iovec_args); } } #else /* Restore caller context */ restore_caller_ctx(svc_ctx, (struct tfm_exc_stack_t *)ret_part_data->stack_ptr); *excReturn = ret_part_data->lr; __set_PSP(ret_part_data->stack_ptr); __set_PSPLIM(tfm_spm_partition_get_stack_bottom(return_partition_idx)); /* Clear the context entry before returning */ tfm_spm_partition_set_stack( current_partition_idx, psp + sizeof(struct tfm_exc_stack_t)); /* FIXME: The condition should be removed once all the secure service * calls are done via the iovec veneers */ if (curr_part_data->iovec_api) { iovec_args = (struct iovec_args_t *) (tfm_spm_partition_get_stack_top(current_partition_idx) - sizeof(struct iovec_args_t)); for (i = 0; i < curr_part_data->iovec_args.out_len; ++i) { curr_part_data->orig_outvec[i].len = iovec_args->out_vec[i].len; } tfm_clear_iovec_parameters(iovec_args); } #endif tfm_spm_partition_cleanup_context(current_partition_idx); tfm_spm_partition_set_state(current_partition_idx, SPM_PARTITION_STATE_IDLE); tfm_spm_partition_set_state(return_partition_idx, SPM_PARTITION_STATE_RUNNING); return TFM_SUCCESS; }
static int32_t tfm_start_partition(const struct tfm_sfn_req_s *desc_ptr, uint32_t excReturn) { uint32_t caller_partition_idx = desc_ptr->caller_part_idx; const struct spm_partition_runtime_data_t *curr_part_data; uint32_t caller_flags; register uint32_t partition_idx; uint32_t psp = __get_PSP(); uint32_t partition_psp, partition_psplim; uint32_t partition_state; uint32_t partition_flags; struct tfm_exc_stack_t *svc_ctx = (struct tfm_exc_stack_t *)psp; uint32_t caller_partition_id; int32_t client_id; struct iovec_args_t *iovec_args; caller_flags = tfm_spm_partition_get_flags(caller_partition_idx); /* Check partition state consistency */ if (((caller_flags & SPM_PART_FLAG_APP_ROT) != 0) != (!desc_ptr->ns_caller)) { /* Partition state inconsistency detected */ return TFM_SECURE_LOCK_FAILED; } if((caller_flags & SPM_PART_FLAG_APP_ROT) == 0) { /* Disable NS exception handling while secure service is running. * FixMe: * This restriction is applied to limit the number of possible attack * vectors. * To be removed when pre-emption and context management issues have * been analysed and resolved. */ TFM_NS_EXC_DISABLE(); } partition_idx = get_partition_idx(desc_ptr->sp_id); curr_part_data = tfm_spm_partition_get_runtime_data(partition_idx); partition_state = curr_part_data->partition_state; partition_flags = tfm_spm_partition_get_flags(partition_idx); caller_partition_id = tfm_spm_partition_get_partition_id( caller_partition_idx); if (tfm_secure_api_initializing) { #if TFM_LVL != 1 /* Make thread mode unprivileged while untrusted partition init is * executed */ if ((partition_flags & SPM_PART_FLAG_PSA_ROT) == 0) { CONTROL_Type ctrl; ctrl.w = __get_CONTROL(); ctrl.b.nPRIV = 1; __set_CONTROL(ctrl.w); __DSB(); __ISB(); } #endif } else if (partition_state == SPM_PARTITION_STATE_RUNNING || partition_state == SPM_PARTITION_STATE_SUSPENDED || partition_state == SPM_PARTITION_STATE_BLOCKED) { /* Recursion is not permitted! */ return TFM_ERROR_PARTITION_NON_REENTRANT; } else if (partition_state != SPM_PARTITION_STATE_IDLE) { /* The partition to be called is not in a proper state */ return TFM_SECURE_LOCK_FAILED; } #if TFM_LVL == 1 /* Prepare switch to shared secure partition stack */ /* In case the call is coming from the non-secure world, we save the iovecs * on the stop of the stack. So the memory area, that can actually be used * as stack by the partitions starts at a lower address */ partition_psp = (uint32_t)®ION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit)- sizeof(struct iovec_args_t); partition_psplim = (uint32_t)®ION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Base); #else partition_psp = curr_part_data->stack_ptr; partition_psplim = tfm_spm_partition_get_stack_bottom(partition_idx); #endif /* Store the context for the partition call */ tfm_spm_partition_set_caller_partition_idx(partition_idx, caller_partition_idx); tfm_spm_partition_store_context(caller_partition_idx, psp, excReturn); if ((caller_flags & SPM_PART_FLAG_APP_ROT)) { tfm_spm_partition_set_caller_client_id(partition_idx, caller_partition_id); } else { client_id = tfm_nspm_get_current_client_id(); if (client_id >= 0) { return TFM_SECURE_LOCK_FAILED; } tfm_spm_partition_set_caller_client_id(partition_idx, client_id); } #if (TFM_LVL != 1) && (TFM_LVL != 2) /* Dynamic partitioning is only done is TFM level 3 */ tfm_spm_partition_sandbox_deconfig(caller_partition_idx); /* Configure partition execution environment */ if (tfm_spm_partition_sandbox_config(partition_idx) != SPM_ERR_OK) { ERROR_MSG("Failed to configure sandbox for partition!"); tfm_secure_api_error_handler(); } #endif /* Default share to scratch area in case of partition to partition calls * this way partitions always get default access to input buffers */ /* FixMe: return value/error handling TBD */ tfm_spm_partition_set_share(partition_idx, desc_ptr->ns_caller ? TFM_BUFFER_SHARE_NS_CODE : TFM_BUFFER_SHARE_SCRATCH); #if TFM_LVL == 1 /* In level one, only switch context and return from exception if in * handler mode */ if ((desc_ptr->ns_caller) || (tfm_secure_api_initializing)) { if (desc_ptr->iovec_api == TFM_SFN_API_IOVEC) { /* Save the iovecs on the common stack. The vectors had been sanity * checked already, and since then the interrupts have been kept * disabled. So we can be sure that the vectors haven't been * tampered with since the check. */ iovec_args = (struct iovec_args_t *) ((uint32_t)®ION_NAME(Image$$, TFM_SECURE_STACK, $$ZI$$Limit)- sizeof(struct iovec_args_t)); if (tfm_spm_partition_set_iovec(partition_idx, desc_ptr->args) != SPM_ERR_OK) { return TFM_ERROR_GENERIC; } tfm_copy_iovec_parameters(iovec_args, &(curr_part_data->iovec_args)); /* Prepare the partition context, update stack ptr */ psp = (uint32_t)prepare_partition_iovec_ctx(svc_ctx, desc_ptr, iovec_args, (uint32_t *)partition_psp); } else { /* Prepare the partition context, update stack ptr */ psp = (uint32_t)prepare_partition_ctx(svc_ctx, desc_ptr, (uint32_t *)partition_psp); } __set_PSP(psp); __set_PSPLIM(partition_psplim); } #else if (desc_ptr->iovec_api == TFM_SFN_API_IOVEC) { /* Save the iovecs on the stack of the partition. The vectors had been * sanity checked already, and since then the interrupts have been kept * disabled. So we can be sure that the vectors haven't been tampered * with since the check. */ iovec_args = (struct iovec_args_t *)(tfm_spm_partition_get_stack_top(partition_idx) - sizeof(struct iovec_args_t)); if (tfm_spm_partition_set_iovec(partition_idx, desc_ptr->args) != SPM_ERR_OK) { return TFM_ERROR_GENERIC; } tfm_copy_iovec_parameters(iovec_args, &(curr_part_data->iovec_args)); /* Prepare the partition context, update stack ptr */ psp = (uint32_t)prepare_partition_iovec_ctx(svc_ctx, desc_ptr, iovec_args, (uint32_t *)partition_psp); } else { /* Prepare the partition context, update stack ptr */ psp = (uint32_t)prepare_partition_ctx(svc_ctx, desc_ptr, (uint32_t *)partition_psp); } __set_PSP(psp); __set_PSPLIM(partition_psplim); #endif tfm_spm_partition_set_state(caller_partition_idx, SPM_PARTITION_STATE_BLOCKED); tfm_spm_partition_set_state(partition_idx, SPM_PARTITION_STATE_RUNNING); tfm_secure_lock++; return TFM_SUCCESS; }