zephyr/arch/xtensa/core/thread.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);
}