diff --git a/arch/xtensa/core/CMakeLists.txt b/arch/xtensa/core/CMakeLists.txt index 9e85b2fffdc..0de05a13a25 100644 --- a/arch/xtensa/core/CMakeLists.txt +++ b/arch/xtensa/core/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_library_sources( xtensa_asm2_util.S xtensa_asm2.c irq_manage.c + thread.c ) zephyr_library_sources_ifdef(CONFIG_XTENSA_USE_CORE_CRT1 crt1.S) diff --git a/arch/xtensa/core/thread.c b/arch/xtensa/core/thread.c new file mode 100644 index 00000000000..53a4126d6ce --- /dev/null +++ b/arch/xtensa/core/thread.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017, 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#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 z_xtensa_thread_stack_header *header = + (struct z_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); + + z_xtensa_userspace_enter(user_entry, p1, p2, p3, + stack_end, current->stack_info.start); + + CODE_UNREACHABLE; +} +#endif /* CONFIG_USERSPACE */ diff --git a/arch/xtensa/core/xtensa_asm2.c b/arch/xtensa/core/xtensa_asm2.c index 1b6089820c1..0b2eb3117ec 100644 --- a/arch/xtensa/core/xtensa_asm2.c +++ b/arch/xtensa/core/xtensa_asm2.c @@ -28,115 +28,8 @@ Z_EXC_DECLARE(z_xtensa_user_string_nlen); static const struct z_exc_handle exceptions[] = { Z_EXC_HANDLE(z_xtensa_user_string_nlen) }; - -#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 z_xtensa_thread_stack_header *header = - (struct z_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 -} - void z_irq_spurious(const void *arg) { int irqs, ie; @@ -277,20 +170,6 @@ static inline void *return_to(void *interrupted) return z_arch_get_next_switch_handle(interrupted); } -#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 */ - /* The wrapper code lives here instead of in the python script that * generates _xtensa_handle_one_int*(). Seems cleaner, still kind of * ugly. @@ -528,24 +407,3 @@ int z_xtensa_irq_is_enabled(unsigned int irq) return (ie & (1 << irq)) != 0U; } - -#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); - - z_xtensa_userspace_enter(user_entry, p1, p2, p3, - stack_end, current->stack_info.start); - - CODE_UNREACHABLE; -} -#endif /* CONFIG_USERSPACE */