Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * 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);
#if XCHAL_CP_NUM > 0
	uint32_t *cpSA;
#endif

#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 */

	 /* task entrypoint */
	pInitCtx->pc   = (uint32_t)_thread_entry;

	/* physical top of stack frame */
	pInitCtx->a1   = (uint32_t)pInitCtx + XT_STK_FRMSZ;

	/* user exception exit dispatcher */
	pInitCtx->exit = (uint32_t)_xt_user_exit;

	/* 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);
}