void tfm_thrd_context_switch(struct tfm_state_context_ext *ctxb, struct tfm_thrd_ctx *prev, struct tfm_thrd_ctx *next) { /* Update latest context into the current thread context */ tfm_memcpy(&prev->state_ctx.ctxb, ctxb, sizeof(*ctxb)); /* Update background context with next thread's context */ tfm_memcpy(ctxb, &next->state_ctx.ctxb, sizeof(next->state_ctx.ctxb)); /* Set current thread indicator with next thread */ CURR_THRD = next; }
/** * \brief SVC handler for \ref psa_write. * * \param[in] args Include all input arguments: * msg_handle, outvec_idx, buffer, num_bytes. * * \retval void Success * \retval "Does not return" The call is invalid, one or more of the * following are true: * \arg msg_handle is invalid. * \arg msg_handle does not refer to a * \ref PSA_IPC_CALL message. * \arg outvec_idx is equal to or greater than * \ref PSA_MAX_IOVEC. * \arg The memory reference for buffer is invalid. * \arg The call attempts to write data past the end * of the client output vector. */ static void tfm_svcall_psa_write(uint32_t *args) { psa_handle_t msg_handle; uint32_t outvec_idx; void *buffer = NULL; size_t num_bytes; struct tfm_msg_body_t *msg = NULL; TFM_ASSERT(args != NULL); msg_handle = (psa_handle_t)args[0]; outvec_idx = args[1]; buffer = (void *)args[2]; num_bytes = (size_t)args[3]; /* It is a fatal error if message handle is invalid */ msg = tfm_spm_get_msg_from_handle(msg_handle); if (!msg) { tfm_panic(); } /* * It is a fatal error if message handle does not refer to a PSA_IPC_CALL * message */ if (msg->msg.type != PSA_IPC_CALL) { tfm_panic(); } /* * It is a fatal error if outvec_idx is equal to or greater than * PSA_MAX_IOVEC */ if (outvec_idx >= PSA_MAX_IOVEC) { tfm_panic(); } /* * It is a fatal error if the call attempts to write data past the end of * the client output vector */ if (num_bytes > msg->msg.out_size[outvec_idx] - msg->outvec[outvec_idx].len) { tfm_panic(); } /* * Copy the service buffer to client outvecs. It is a fatal error * if the memory reference for buffer is invalid or not readable. */ if (tfm_memory_check(buffer, num_bytes, false, TFM_MEMORY_ACCESS_RO) != IPC_SUCCESS) { tfm_panic(); } tfm_memcpy(msg->outvec[outvec_idx].base + msg->outvec[outvec_idx].len, buffer, num_bytes); /* Update the write number */ msg->outvec[outvec_idx].len += num_bytes; }
/** * \brief SVC handler for \ref psa_get. * * \param[in] args Include all input arguments: signal, msg. * * \retval PSA_SUCCESS Success, *msg will contain the delivered * message. * \retval PSA_ERR_NOMSG Message could not be delivered. * \retval "Does not return" The call is invalid because one or more of the * following are true: * \arg signal has more than a single bit set. * \arg signal does not correspond to a RoT Service. * \arg The RoT Service signal is not currently * asserted. * \arg The msg pointer provided is not a valid memory * reference. */ static psa_status_t tfm_svcall_psa_get(uint32_t *args) { psa_signal_t signal; psa_msg_t *msg = NULL; struct tfm_spm_service_t *service = NULL; struct tfm_msg_body_t *tmp_msg = NULL; struct tfm_spm_ipc_partition_t *partition = NULL; TFM_ASSERT(args != NULL); signal = (psa_signal_t)args[0]; msg = (psa_msg_t *)args[1]; /* * Only one message could be retrieved every time for psa_get(). It is a * fatal error if the input signal has more than a signal bit set. */ if (tfm_bitcount(signal) != 1) { tfm_panic(); } /* * Write the message to the service buffer. It is a fatal error if the * input msg pointer is not a valid memory reference or not read-write. */ if (tfm_memory_check((void *)msg, sizeof(psa_msg_t), false, TFM_MEMORY_ACCESS_RW) != IPC_SUCCESS) { tfm_panic(); } partition = tfm_spm_get_running_partition(); if (!partition) { tfm_panic(); } /* * It is a fatal error if the caller call psa_get() when no message has * been set. The caller must call this function after a RoT Service signal * is returned by psa_wait(). */ if (partition->signals == 0) { tfm_panic(); } /* * It is a fatal error if the RoT Service signal is not currently asserted. */ if ((partition->signals & signal) == 0) { tfm_panic(); } /* * Get Rot service by signal from partition. It is a fatal error if geting * failed which mean the input signal is not correspond to a RoT service. */ service = tfm_spm_get_service_by_signal(partition, signal); if (!service) { tfm_panic(); } tmp_msg = tfm_msg_dequeue(&service->msg_queue); if (!tmp_msg) { return PSA_ERR_NOMSG; } tfm_memcpy(msg, &tmp_msg->msg, sizeof(psa_msg_t)); /* * There may be mutiple messages for this RoT Service signal, do not clear * its mask until no remaining message. */ if (tfm_msg_queue_is_empty(&service->msg_queue)) { partition->signals &= ~signal; } return PSA_SUCCESS; }
/** * \brief SVC handler for \ref psa_read. * * \param[in] args Include all input arguments: * msg_handle, invec_idx, buffer, num_bytes. * * \retval >0 Number of bytes copied. * \retval 0 There was no remaining data in this input * vector. * \retval "Does not return" The call is invalid, one or more of the * following are true: * \arg msg_handle is invalid. * \arg msg_handle does not refer to a * \ref PSA_IPC_CALL message. * \arg invec_idx is equal to or greater than * \ref PSA_MAX_IOVEC. * \arg the memory reference for buffer is invalid or * not writable. */ static size_t tfm_svcall_psa_read(uint32_t *args) { psa_handle_t msg_handle; uint32_t invec_idx; void *buffer = NULL; size_t num_bytes; size_t bytes; struct tfm_msg_body_t *msg = NULL; TFM_ASSERT(args != NULL); msg_handle = (psa_handle_t)args[0]; invec_idx = args[1]; buffer = (void *)args[2]; num_bytes = (size_t)args[3]; /* It is a fatal error if message handle is invalid */ msg = tfm_spm_get_msg_from_handle(msg_handle); if (!msg) { tfm_panic(); } /* * It is a fatal error if message handle does not refer to a PSA_IPC_CALL * message */ if (msg->msg.type != PSA_IPC_CALL) { tfm_panic(); } /* * It is a fatal error if invec_idx is equal to or greater than * PSA_MAX_IOVEC */ if (invec_idx >= PSA_MAX_IOVEC) { tfm_panic(); } /* There was no remaining data in this input vector */ if (msg->msg.in_size[invec_idx] == 0) { return 0; } /* * Copy the client data to the service buffer. It is a fatal error * if the memory reference for buffer is invalid or not read-write. */ if (tfm_memory_check(buffer, num_bytes, false, TFM_MEMORY_ACCESS_RW) != IPC_SUCCESS) { tfm_panic(); } bytes = num_bytes > msg->msg.in_size[invec_idx] ? msg->msg.in_size[invec_idx] : num_bytes; tfm_memcpy(buffer, msg->invec[invec_idx].base, bytes); /* There maybe some remaining data */ msg->invec[invec_idx].base += bytes; msg->msg.in_size[invec_idx] -= bytes; return bytes; }
psa_status_t tfm_svcall_psa_call(uint32_t *args, int32_t ns_caller, uint32_t lr) { psa_handle_t handle; psa_invec *inptr, invecs[PSA_MAX_IOVEC]; psa_outvec *outptr, outvecs[PSA_MAX_IOVEC]; size_t in_num, out_num; struct tfm_spm_service_t *service; struct tfm_msg_body_t *msg; int i; TFM_ASSERT(args != NULL); handle = (psa_handle_t)args[0]; if (!ns_caller) { inptr = (psa_invec *)args[1]; in_num = (size_t)args[2]; outptr = (psa_outvec *)args[3]; /* * 5th parameter is pushed at stack top before SVC, then PE hardware * stacks the execution context. The size of the context depends on * various settings: * - if FP is not used, 5th parameter is at 8th position counting * from SP; * - if FP is used and FPCCR_S.TS is 0, 5th parameter is at 26th * position counting from SP; * - if FP is used and FPCCR_S.TS is 1, 5th parameter is at 42th * position counting from SP. */ if (lr & EXC_RETURN_FPU_FRAME_BASIC) { out_num = (size_t)args[8]; #if defined (__FPU_USED) && (__FPU_USED == 1U) } else if (FPU->FPCCR & FPU_FPCCR_TS_Msk) { out_num = (size_t)args[42]; #endif } else { out_num = (size_t)args[26]; } } else { /* * FixMe: From non-secure caller, vec and len are composed into a new * struct parameter. Need to extract them. */ /* * Read parameters from the arguments. It is a fatal error if the * memory reference for buffer is invalid or not readable. */ if (tfm_memory_check((void *)args[1], sizeof(uint32_t), ns_caller, TFM_MEMORY_ACCESS_RO) != IPC_SUCCESS) { tfm_panic(); } if (tfm_memory_check((void *)args[2], sizeof(uint32_t), ns_caller, TFM_MEMORY_ACCESS_RO) != IPC_SUCCESS) { tfm_panic(); } inptr = (psa_invec *)((psa_invec *)args[1])->base; in_num = ((psa_invec *)args[1])->len; outptr = (psa_outvec *)((psa_invec *)args[2])->base; out_num = ((psa_invec *)args[2])->len; } /* It is a fatal error if in_len + out_len > PSA_MAX_IOVEC. */ if (in_num + out_num > PSA_MAX_IOVEC) { tfm_panic(); } /* It is a fatal error if an invalid handle was passed. */ service = tfm_spm_get_service_by_handle(handle); if (!service) { /* FixMe: Need to implement one mechanism to resolve this failure. */ tfm_panic(); } /* * Read client invecs from the wrap input vector. It is a fatal error * if the memory reference for the wrap input vector is invalid or not * readable. */ if (tfm_memory_check((void *)inptr, in_num * sizeof(psa_invec), ns_caller, TFM_MEMORY_ACCESS_RO) != IPC_SUCCESS) { tfm_panic(); } /* * Read client outvecs from the wrap output vector and will update the * actual length later. It is a fatal error if the memory reference for * the wrap output vector is invalid or not read-write. */ if (tfm_memory_check((void *)outptr, out_num * sizeof(psa_outvec), ns_caller, TFM_MEMORY_ACCESS_RW) != IPC_SUCCESS) { tfm_panic(); } tfm_memset(invecs, 0, sizeof(invecs)); tfm_memset(outvecs, 0, sizeof(outvecs)); /* Copy the address out to avoid TOCTOU attacks. */ tfm_memcpy(invecs, inptr, in_num * sizeof(psa_invec)); tfm_memcpy(outvecs, outptr, out_num * sizeof(psa_outvec)); /* * For client input vector, it is a fatal error if the provided payload * memory reference was invalid or not readable. */ for (i = 0; i < in_num; i++) { if (tfm_memory_check((void *)invecs[i].base, invecs[i].len, ns_caller, TFM_MEMORY_ACCESS_RO) != IPC_SUCCESS) { tfm_panic(); } } /* * For client output vector, it is a fatal error if the provided payload * memory reference was invalid or not read-write. */ for (i = 0; i < out_num; i++) { if (tfm_memory_check(outvecs[i].base, outvecs[i].len, ns_caller, TFM_MEMORY_ACCESS_RW) != IPC_SUCCESS) { tfm_panic(); } } /* * FixMe: Need to check if the message is unrecognized by the RoT * Service or incorrectly formatted. */ msg = tfm_spm_create_msg(service, handle, PSA_IPC_CALL, ns_caller, invecs, in_num, outvecs, out_num, outptr); if (!msg) { /* FixMe: Need to implement one mechanism to resolve this failure. */ tfm_panic(); } /* * Send message and wake up the SP who is waiting on message queue, * and scheduler triggered */ if (tfm_spm_send_event(service, msg) != IPC_SUCCESS) { /* FixMe: Need to refine failure process here. */ tfm_panic(); } return PSA_SUCCESS; }