162 lines
5.0 KiB
C
162 lines
5.0 KiB
C
/*
|
|
* Copyright (c) 2016 Cadence Design Systems, Inc.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifdef CONFIG_INIT_STACKS
|
|
#include <string.h>
|
|
#endif /* CONFIG_INIT_STACKS */
|
|
#ifdef CONFIG_DEBUG
|
|
#include <misc/printk.h>
|
|
#endif
|
|
#include <kernel_structs.h>
|
|
#include <wait_q.h>
|
|
#include <xtensa_config.h>
|
|
|
|
extern void _xt_user_exit(void);
|
|
#if CONFIG_MICROKERNEL
|
|
extern FUNC_NORETURN void _TaskAbort(void);
|
|
#endif
|
|
extern void fiber_abort(void);
|
|
|
|
#if defined(CONFIG_THREAD_MONITOR)
|
|
#define THREAD_MONITOR_INIT(tcs) _thread_monitor_init(tcs)
|
|
#else
|
|
#define THREAD_MONITOR_INIT(tcs) \
|
|
do {/* do nothing */ \
|
|
} while ((0))
|
|
#endif
|
|
|
|
#if defined(CONFIG_THREAD_MONITOR)
|
|
/**
|
|
*
|
|
* @brief Initialize thread monitoring support
|
|
*
|
|
* Currently only inserts the new thread in the list of active threads.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
static inline void _thread_monitor_init(struct tcs *tcs)
|
|
{
|
|
unsigned int key;
|
|
|
|
/*
|
|
* Add the newly initialized thread to head of the list of threads.
|
|
* This singly linked list of threads maintains ALL the threads in the
|
|
* system:
|
|
* both tasks and fibers regardless of whether they are runnable.
|
|
*/
|
|
|
|
key = irq_lock();
|
|
tcs->next_thread = _nanokernel.threads;
|
|
_nanokernel.threads = tcs;
|
|
irq_unlock(key);
|
|
}
|
|
#endif /* CONFIG_THREAD_MONITOR */
|
|
|
|
/*
|
|
* @brief Initialize a new thread from its stack space
|
|
*
|
|
* The control structure (TCS) is put at the lower address of the stack. An
|
|
* initial context, to be "restored" by __return_from_coop(), is put at
|
|
* the other end of the stack, and thus reusable by the stack when not
|
|
* needed anymore.
|
|
*
|
|
* The initial context is a basic stack frame that contains arguments for
|
|
* _thread_entry() return address, that points at _thread_entry()
|
|
* and status register.
|
|
*
|
|
* <options> is currently unused.
|
|
*
|
|
* @param pStackmem the pointer to aligned stack memory
|
|
* @param stackSize the stack size in bytes
|
|
* @param pEntry thread entry point routine
|
|
* @param p1 first param to entry point
|
|
* @param p2 second param to entry point
|
|
* @param p3 third param to entry point
|
|
* @param fiber prio, -1 for task
|
|
* @param options is unused (saved for future expansion)
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void _new_thread(char *pStack, size_t stackSize,
|
|
void (*pEntry)(void *, void *, void *),
|
|
void *p1, void *p2, void *p3,
|
|
int prio, unsigned int options)
|
|
{
|
|
/* Align stack end to maximum alignment requirement. */
|
|
char *stackEnd = (char *)ROUND_DOWN(pStack + stackSize,
|
|
(XCHAL_TOTAL_SA_ALIGN < 16 ? 16 : XCHAL_TOTAL_SA_ALIGN));
|
|
/* TCS is located at top of stack while frames are located at end of it */
|
|
struct tcs *tcs = (struct tcs *)(pStack);
|
|
uint32_t *cpSA;
|
|
|
|
#ifdef CONFIG_DEBUG
|
|
printk("\nstackPtr = %p, stackSize = %d\n", pStack, stackSize);
|
|
printk("stackEnd = %p\n", stackEnd);
|
|
#endif
|
|
#ifdef CONFIG_INIT_STACKS
|
|
memset(pStack, 0xaa, stackSize);
|
|
#endif
|
|
#if XCHAL_CP_NUM > 0
|
|
/* Coprocessor's stack is allocated just after the TCS */
|
|
tcs->arch.preempCoprocReg.cpStack = pStack + sizeof(struct k_thread);
|
|
cpSA = (uint32_t *)(tcs->arch.preempCoprocReg.cpStack + XT_CP_ASA);
|
|
/* Coprocessor's save area alignment is at leat 16 bytes */
|
|
*cpSA = ROUND_UP(cpSA + 1,
|
|
(XCHAL_TOTAL_SA_ALIGN < 16 ? 16 : XCHAL_TOTAL_SA_ALIGN));
|
|
#ifdef CONFIG_DEBUG
|
|
printk("cpStack = %p\n", tcs->arch.preempCoprocReg.cpStack);
|
|
printk("cpAsa = %p\n", *(void **)(tcs->arch.preempCoprocReg.cpStack + XT_CP_ASA));
|
|
#endif
|
|
#endif
|
|
/* Thread's first frame alignment is granted as both operands are aligned */
|
|
XtExcFrame *pInitCtx = (XtExcFrame *)(stackEnd - (XT_XTRA_SIZE - XT_CP_SIZE));
|
|
#ifdef CONFIG_DEBUG
|
|
printk("pInitCtx = %p\n", pInitCtx);
|
|
#endif
|
|
/* Explicitly initialize certain saved registers */
|
|
pInitCtx->pc = (uint32_t)_thread_entry; /* task entrypoint */
|
|
pInitCtx->a1 = (uint32_t)pInitCtx + XT_STK_FRMSZ; /* physical top of stack frame */
|
|
pInitCtx->exit = (uint32_t)_xt_user_exit; /* user exception exit dispatcher */
|
|
/* Set initial PS to int level 0, EXCM disabled, user mode. */
|
|
/* Also set entry point argument arg. */
|
|
#ifdef __XTENSA_CALL0_ABI__
|
|
pInitCtx->a2 = (uint32_t)pEntry;
|
|
pInitCtx->a3 = (uint32_t)p1;
|
|
pInitCtx->a4 = (uint32_t)p2;
|
|
pInitCtx->a5 = (uint32_t)p3;
|
|
pInitCtx->ps = PS_UM | PS_EXCM;
|
|
#else
|
|
/* For windowed ABI set also WOE and CALLINC (pretend task is 'call4'). */
|
|
pInitCtx->a6 = (uint32_t)pEntry;
|
|
pInitCtx->a7 = (uint32_t)p1;
|
|
pInitCtx->a8 = (uint32_t)p2;
|
|
pInitCtx->a9 = (uint32_t)p3;
|
|
pInitCtx->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1);
|
|
#endif
|
|
tcs->callee_saved.topOfStack = pInitCtx;
|
|
tcs->arch.flags = 0;
|
|
_init_thread_base(&tcs->base, prio, _THREAD_PRESTART, options);
|
|
/* static threads overwrite it afterwards with real value */
|
|
tcs->init_data = NULL;
|
|
tcs->fn_abort = NULL;
|
|
#ifdef CONFIG_THREAD_CUSTOM_DATA
|
|
/* Initialize custom data field (value is opaque to kernel) */
|
|
tcs->custom_data = NULL;
|
|
#endif
|
|
#ifdef CONFIG_THREAD_MONITOR
|
|
/*
|
|
* In debug mode tcs->entry give direct access to the thread entry
|
|
* and the corresponding parameters.
|
|
*/
|
|
tcs->entry = (struct __thread_entry *)(pInitCtx);
|
|
#endif
|
|
/* initial values in all other registers/TCS entries are irrelevant */
|
|
|
|
THREAD_MONITOR_INIT(tcs);
|
|
}
|
|
|