blob: 1d66e256f5ca69ae76014ca88962655575097b07 [file] [log] [blame]
/*
* Copyright (c) 2018-2020, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <inttypes.h>
#include "tfm_arch.h"
#include "tfm_thread.h"
#include "tfm_utils.h"
#include "tfm_memory_utils.h"
#include "tfm/tfm_core_svc.h"
#include "tfm/spm_api.h"
#include "tfm_core_utils.h"
/* Force ZERO in case ZI(bss) clear is missing */
static struct tfm_core_thread_t *p_thrd_head = NULL;
static struct tfm_core_thread_t *p_runn_head = NULL;
static struct tfm_core_thread_t *p_curr_thrd = NULL;
/* Define Macro to fetch global to support future expansion (PERCPU e.g.) */
#define LIST_HEAD p_thrd_head
#define RUNN_HEAD p_runn_head
#define CURR_THRD p_curr_thrd
static struct tfm_core_thread_t *find_next_running_thread(
struct tfm_core_thread_t *pth)
{
while (pth && pth->state != THRD_STATE_RUNNING) {
pth = pth->next;
}
return pth;
}
/* To get next running thread for scheduler */
struct tfm_core_thread_t *tfm_core_thrd_get_next_thread(void)
{
/*
* First RUNNING thread has highest priority since threads are sorted with
* priority.
*/
return find_next_running_thread(RUNN_HEAD);
}
/* To get current thread for caller */
struct tfm_core_thread_t *tfm_core_thrd_get_curr_thread(void)
{
return CURR_THRD;
}
/* Insert a new thread into list by descending priority (Highest at head) */
static void insert_by_prior(struct tfm_core_thread_t **head,
struct tfm_core_thread_t *node)
{
if (*head == NULL || (node->prior <= (*head)->prior)) {
node->next = *head;
*head = node;
} else {
struct tfm_core_thread_t *iter = *head;
while (iter->next && (node->prior > iter->next->prior)) {
iter = iter->next;
}
node->next = iter->next;
iter->next = node;
}
}
/*
* Set first running thread as head to reduce enumerate
* depth while searching for a first running thread.
*/
static void update_running_head(struct tfm_core_thread_t **runn,
struct tfm_core_thread_t *node)
{
if ((node->state == THRD_STATE_RUNNING) &&
(*runn == NULL || (node->prior < (*runn)->prior))) {
*runn = node;
} else {
*runn = LIST_HEAD;
}
}
/* Set context members only. No validation here */
void tfm_core_thrd_init(struct tfm_core_thread_t *pth,
tfm_core_thrd_entry_t pfn, void *param,
uintptr_t stk_top, uintptr_t stk_btm)
{
pth->prior = THRD_PRIOR_MEDIUM;
pth->state = THRD_STATE_CREATING;
pth->pfn = pfn;
pth->param = param;
pth->stk_btm = stk_btm;
pth->stk_top = stk_top;
}
uint32_t tfm_core_thrd_start(struct tfm_core_thread_t *pth)
{
/* Validate parameters before really start */
if ((pth->state != THRD_STATE_CREATING) ||
(pth->pfn == NULL) ||
(pth->stk_btm == 0) ||
(pth->stk_top == 0)) {
return THRD_ERR_INVALID_PARAM;
}
/* Thread management runs in handler mode; set context for thread mode. */
tfm_arch_init_context(&pth->arch_ctx, pth->param, (uintptr_t)pth->pfn,
pth->stk_btm, pth->stk_top);
/* Insert a new thread with priority */
insert_by_prior(&LIST_HEAD, pth);
/* Mark it as RUNNING after insertion */
tfm_core_thrd_set_state(pth, THRD_STATE_RUNNING);
return THRD_SUCCESS;
}
void tfm_core_thrd_set_state(struct tfm_core_thread_t *pth, uint32_t new_state)
{
TFM_CORE_ASSERT(pth != NULL && new_state < THRD_STATE_INVALID);
pth->state = new_state;
update_running_head(&RUNN_HEAD, pth);
}
/* Scheduling won't happen immediately but after the exception returns */
void tfm_core_thrd_activate_schedule(void)
{
tfm_arch_trigger_pendsv();
}
void tfm_core_thrd_start_scheduler(struct tfm_core_thread_t *pth)
{
/*
* There is no selected thread before scheduler start, assign the caller
* provided thread as the current thread. Update the hardware PSP/PSPLIM
* with the value in thread context to ensure they are identical.
* This function can be called only ONCE; further calling triggers assert.
*/
TFM_CORE_ASSERT(CURR_THRD == NULL);
TFM_CORE_ASSERT(pth != NULL);
TFM_CORE_ASSERT(pth->arch_ctx.sp != 0);
tfm_arch_update_ctx(&pth->arch_ctx);
CURR_THRD = pth;
tfm_core_thrd_activate_schedule();
}
void tfm_core_thrd_switch_context(struct tfm_arch_ctx_t *p_actx,
struct tfm_core_thread_t *prev,
struct tfm_core_thread_t *next)
{
TFM_CORE_ASSERT(prev != NULL);
TFM_CORE_ASSERT(next != NULL);
/*
* First, update latest context into the current thread context.
* Then, update background context with next thread's context.
*/
tfm_core_util_memcpy(&prev->arch_ctx, p_actx, sizeof(*p_actx));
tfm_core_util_memcpy(p_actx, &next->arch_ctx, sizeof(next->arch_ctx));
/* Update current thread indicator */
CURR_THRD = next;
}