Ken Liu | 5d73c87 | 2021-08-19 19:23:17 +0800 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2018-2021, Arm Limited. All rights reserved. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | * |
| 6 | */ |
| 7 | |
| 8 | #include <stdint.h> |
| 9 | #include "thread.h" |
| 10 | #include "tfm_arch.h" |
| 11 | #include "utilities.h" |
| 12 | |
| 13 | /* Force ZERO in case ZI(bss) clear is missing. */ |
| 14 | static struct thread_t *p_thrd_head = NULL; /* Point to the first thread. */ |
| 15 | static struct thread_t *p_rnbl_head = NULL; /* Point to the first runnable. */ |
| 16 | |
| 17 | /* Define Macro to fetch global to support future expansion (PERCPU e.g.) */ |
| 18 | #define LIST_HEAD p_thrd_head |
| 19 | #define RNBL_HEAD p_rnbl_head |
| 20 | |
| 21 | struct thread_t *thrd_next(void) |
| 22 | { |
| 23 | struct thread_t *p_thrd = RNBL_HEAD; |
| 24 | /* |
| 25 | * First runnable thread has highest priority since threads are |
| 26 | * sorted by priority. |
| 27 | */ |
| 28 | while (p_thrd && p_thrd->state != THRD_STATE_RUNNABLE) { |
| 29 | p_thrd = p_thrd->next; |
| 30 | } |
| 31 | |
| 32 | return p_thrd; |
| 33 | } |
| 34 | |
| 35 | static void insert_by_prior(struct thread_t **head, struct thread_t *node) |
| 36 | { |
| 37 | if (*head == NULL || (node->priority <= (*head)->priority)) { |
| 38 | node->next = *head; |
| 39 | *head = node; |
| 40 | } else { |
| 41 | struct thread_t *iter = *head; |
| 42 | |
| 43 | while (iter->next && (node->priority > iter->next->priority)) { |
| 44 | iter = iter->next; |
| 45 | } |
| 46 | |
| 47 | node->next = iter->next; |
| 48 | iter->next = node; |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | void thrd_start(struct thread_t *p_thrd, |
| 53 | thrd_fn_t fn, void *param, |
| 54 | uintptr_t sp_limit, uintptr_t sp) |
| 55 | { |
| 56 | TFM_CORE_ASSERT(p_thrd != NULL); |
| 57 | |
| 58 | /* Insert a new thread with priority */ |
| 59 | insert_by_prior(&LIST_HEAD, p_thrd); |
| 60 | |
| 61 | /* Mark it as RUNNABLE after insertion */ |
| 62 | thrd_set_state(p_thrd, THRD_STATE_RUNNABLE); |
| 63 | |
| 64 | tfm_arch_init_context(p_thrd->p_context_ctrl, (uintptr_t)fn, param, |
| 65 | (uintptr_t)fn&~1UL, sp_limit, sp); |
| 66 | } |
| 67 | |
| 68 | void thrd_set_state(struct thread_t *p_thrd, uint32_t new_state) |
| 69 | { |
| 70 | TFM_CORE_ASSERT(p_thrd != NULL); |
| 71 | |
| 72 | p_thrd->state = new_state; |
| 73 | |
| 74 | /* |
| 75 | * Set first runnable thread as head to reduce enumerate |
| 76 | * depth while searching for a first runnable thread. |
| 77 | */ |
| 78 | if ((p_thrd->state == THRD_STATE_RUNNABLE) && |
| 79 | ((RNBL_HEAD == NULL) || (p_thrd->priority < RNBL_HEAD->priority))) { |
| 80 | RNBL_HEAD = p_thrd; |
| 81 | } else { |
| 82 | RNBL_HEAD = LIST_HEAD; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | uint32_t thrd_start_scheduler(struct thread_t **ppth) |
| 87 | { |
| 88 | struct thread_t *pth = thrd_next(); |
| 89 | |
| 90 | tfm_arch_trigger_pendsv(); |
| 91 | |
| 92 | if (ppth) { |
| 93 | *ppth = pth; |
| 94 | } |
| 95 | |
| 96 | return tfm_arch_refresh_hardware_context(pth->p_context_ctrl); |
| 97 | } |
| 98 | |
| 99 | void thrd_wait_on(struct sync_obj_t *p_sync_obj, struct thread_t *pth) |
| 100 | { |
| 101 | TFM_CORE_ASSERT(p_sync_obj && p_sync_obj->magic == THRD_SYNC_MAGIC); |
| 102 | |
| 103 | p_sync_obj->owner = pth; |
| 104 | thrd_set_state(pth, THRD_STATE_BLOCK); |
| 105 | tfm_arch_trigger_pendsv(); |
| 106 | } |
| 107 | |
| 108 | void thrd_wake_up(struct sync_obj_t *p_sync_obj, uint32_t ret_val) |
| 109 | { |
| 110 | TFM_CORE_ASSERT(p_sync_obj && p_sync_obj->magic == THRD_SYNC_MAGIC); |
| 111 | |
| 112 | if (p_sync_obj->owner && p_sync_obj->owner->state == THRD_STATE_BLOCK) { |
| 113 | thrd_set_state(p_sync_obj->owner, THRD_STATE_RUNNABLE); |
| 114 | tfm_arch_set_context_ret_code(p_sync_obj->owner->p_context_ctrl, |
| 115 | ret_val); |
| 116 | p_sync_obj->owner = NULL; |
| 117 | tfm_arch_trigger_pendsv(); |
| 118 | } |
| 119 | } |