/* * Copyright (c) 2017, 2023 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL); #ifdef CONFIG_USERSPACE #ifdef CONFIG_THREAD_LOCAL_STORAGE /* * Per-thread (TLS) variable indicating whether execution is in user mode. */ __thread uint32_t is_user_mode; #endif #endif /* CONFIG_USERSPACE */ /** * Initializes a stack area such that it can be "restored" later and * begin running with the specified function and three arguments. The * entry function takes three arguments to match the signature of * Zephyr's k_thread_entry_t. Thread will start with EXCM clear and * INTLEVEL set to zero (i.e. it's a user thread, we don't start with * anything masked, so don't assume that!). */ static void *init_stack(struct k_thread *thread, int *stack_top, void (*entry)(void *, void *, void *), void *arg1, void *arg2, void *arg3) { void *ret; _xtensa_irq_stack_frame_a11_t *frame; #ifdef CONFIG_USERSPACE struct xtensa_thread_stack_header *header = (struct xtensa_thread_stack_header *)thread->stack_obj; thread->arch.psp = header->privilege_stack + sizeof(header->privilege_stack); #endif /* Not-a-cpu ID Ensures that the first time this is run, the * stack will be invalidated. That covers the edge case of * restarting a thread on a stack that had previously been run * on one CPU, but then initialized on this one, and * potentially run THERE and not HERE. */ thread->arch.last_cpu = -1; /* We cheat and shave 16 bytes off, the top four words are the * A0-A3 spill area for the caller of the entry function, * which doesn't exist. It will never be touched, so we * arrange to enter the function with a CALLINC of 1 and a * stack pointer 16 bytes above the top, so its ENTRY at the * start will decrement the stack pointer by 16. */ const int bsasz = sizeof(*frame) - 16; frame = (void *)(((char *) stack_top) - bsasz); (void)memset(frame, 0, bsasz); frame->bsa.ps = PS_WOE | PS_UM | PS_CALLINC(1); #ifdef CONFIG_USERSPACE if ((thread->base.user_options & K_USER) == K_USER) { frame->bsa.pc = (uintptr_t)arch_user_mode_enter; } else { frame->bsa.pc = (uintptr_t)z_thread_entry; } #else frame->bsa.pc = (uintptr_t)z_thread_entry; #endif #if XCHAL_HAVE_THREADPTR #ifdef CONFIG_THREAD_LOCAL_STORAGE frame->bsa.threadptr = thread->tls; #elif CONFIG_USERSPACE frame->bsa.threadptr = (uintptr_t)((thread->base.user_options & K_USER) ? thread : NULL); #endif #endif /* Arguments to z_thread_entry(). Remember these start at A6, * which will be rotated into A2 by the ENTRY instruction that * begins the C function. And A4-A7 and A8-A11 are optional * quads that live below the BSA! */ frame->a7 = (uintptr_t)arg1; /* a7 */ frame->a6 = (uintptr_t)entry; /* a6 */ frame->a5 = 0; /* a5 */ frame->a4 = 0; /* a4 */ frame->a11 = 0; /* a11 */ frame->a10 = 0; /* a10 */ frame->a9 = (uintptr_t)arg3; /* a9 */ frame->a8 = (uintptr_t)arg2; /* a8 */ /* Finally push the BSA pointer and return the stack pointer * as the handle */ frame->ptr_to_bsa = (void *)&frame->bsa; ret = &frame->ptr_to_bsa; return ret; } void arch_new_thread(struct k_thread *thread, k_thread_stack_t *stack, char *stack_ptr, k_thread_entry_t entry, void *p1, void *p2, void *p3) { thread->switch_handle = init_stack(thread, (int *)stack_ptr, entry, p1, p2, p3); #ifdef CONFIG_KERNEL_COHERENCE __ASSERT((((size_t)stack) % XCHAL_DCACHE_LINESIZE) == 0, ""); __ASSERT((((size_t)stack_ptr) % XCHAL_DCACHE_LINESIZE) == 0, ""); sys_cache_data_flush_and_invd_range(stack, (char *)stack_ptr - (char *)stack); #endif } #if defined(CONFIG_FPU) && defined(CONFIG_FPU_SHARING) int arch_float_disable(struct k_thread *thread) { /* xtensa always has FPU enabled so cannot be disabled */ return -ENOTSUP; } int arch_float_enable(struct k_thread *thread, unsigned int options) { /* xtensa always has FPU enabled so nothing to do here */ return 0; } #endif /* CONFIG_FPU && CONFIG_FPU_SHARING */ #ifdef CONFIG_USERSPACE FUNC_NORETURN void arch_user_mode_enter(k_thread_entry_t user_entry, void *p1, void *p2, void *p3) { struct k_thread *current = _current; size_t stack_end; /* Transition will reset stack pointer to initial, discarding * any old context since this is a one-way operation */ stack_end = Z_STACK_PTR_ALIGN(current->stack_info.start + current->stack_info.size - current->stack_info.delta); xtensa_userspace_enter(user_entry, p1, p2, p3, stack_end, current->stack_info.start); CODE_UNREACHABLE; } #endif /* CONFIG_USERSPACE */