xtensa: add support for thread local storage

Adds the necessary bits to initialize TLS in the stack
area and sets up CPU registers during context switch.

Note that this does not enable TLS for all Xtensa SoC.
This is because Xtensa SoCs are highly configurable
so that each SoC can be considered a whole architecture.
So TLS needs to be enabled on the SoC level, instead of
at the arch level.

Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
Daniel Leung 2020-10-02 14:55:34 -07:00 committed by Andrew Boie
commit f8a909dad1
8 changed files with 88 additions and 6 deletions

View file

@ -16,5 +16,6 @@ zephyr_library_sources(
zephyr_library_sources_ifndef(CONFIG_ATOMIC_OPERATIONS_C atomic.S)
zephyr_library_sources_ifdef(CONFIG_XTENSA_USE_CORE_CRT1 crt1.S)
zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD irq_offload.c)
zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c)
add_subdirectory(startup)

47
arch/xtensa/core/tls.c Normal file
View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <kernel_structs.h>
#include <kernel_internal.h>
#include <kernel_tls.h>
#include <app_memory/app_memdomain.h>
#include <sys/util.h>
#if XCHAL_HAVE_THREADPTR == 0
#error SoC does not support THREADPTR for thread local storage.
#endif
size_t arch_tls_stack_setup(struct k_thread *new_thread, char *stack_ptr)
{
/*
* TLS area has some data fields following by
* thread data and bss. These fields are supposed to be
* used by toolchain and OS TLS code to aid in locating
* the TLS data/bss. Zephyr currently has no use for
* this so we can simply skip these. However, since GCC
* is generating code assuming these fields are there,
* we simply skip them when setting the TLS pointer.
*/
/*
* Since we are populating things backwards,
* setup the TLS data/bss area first.
*/
stack_ptr -= z_tls_data_size();
z_tls_copy(stack_ptr);
/* Skip two pointers due to toolchain */
stack_ptr -= sizeof(uintptr_t) * 2;
/*
* Set thread TLS pointer which is used in
* context switch to point to TLS area.
*/
new_thread->tls = POINTER_TO_UINT(stack_ptr);
return (z_tls_data_size() + (sizeof(uintptr_t) * 2));
}

View file

@ -167,6 +167,10 @@ _restore_context:
#if XCHAL_HAVE_S32C1I
l32i a0, a1, BSA_SCOMPARE1_OFF
wsr.SCOMPARE1 a0
#endif
#if XCHAL_HAVE_THREADPTR && defined(CONFIG_THREAD_LOCAL_STORAGE)
l32i a0, a1, BSA_THREADPTR_OFF
wur.THREADPTR a0
#endif
rsync

View file

@ -15,7 +15,7 @@
LOG_MODULE_DECLARE(os);
void *xtensa_init_stack(int *stack_top,
void *xtensa_init_stack(struct k_thread *thread, int *stack_top,
void (*entry)(void *, void *, void *),
void *arg1, void *arg2, void *arg3)
{
@ -34,6 +34,10 @@ void *xtensa_init_stack(int *stack_top,
bsa[BSA_PC_OFF/4] = z_thread_entry;
bsa[BSA_PS_OFF/4] = (void *)(PS_WOE | PS_UM | PS_CALLINC(1));
#if XCHAL_HAVE_THREADPTR && defined(CONFIG_THREAD_LOCAL_STORAGE)
bsa[BSA_THREADPTR_OFF/4] = UINT_TO_POINTER(thread->tls);
#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
@ -65,7 +69,8 @@ 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 = xtensa_init_stack((int *)stack_ptr, entry,
thread->switch_handle = xtensa_init_stack(thread,
(int *)stack_ptr, entry,
p1, p2, p3);
}

View file

@ -78,10 +78,17 @@
#define BASE_SAVE_AREA_SIZE_SCOMPARE 0
#endif
#if XCHAL_HAVE_THREADPTR && defined(CONFIG_THREAD_LOCAL_STORAGE)
#define BASE_SAVE_AREA_SIZE_THREADPTR 4
#else
#define BASE_SAVE_AREA_SIZE_THREADPTR 0
#endif
#define BASE_SAVE_AREA_SIZE \
(BASE_SAVE_AREA_SIZE_COMMON + \
BASE_SAVE_AREA_SIZE_LOOPS + \
BASE_SAVE_AREA_SIZE_SCOMPARE)
BASE_SAVE_AREA_SIZE_SCOMPARE + \
BASE_SAVE_AREA_SIZE_THREADPTR)
#define BSA_A3_OFF (BASE_SAVE_AREA_SIZE - 20)
#define BSA_A2_OFF (BASE_SAVE_AREA_SIZE - 24)
@ -94,9 +101,21 @@
#define BSA_LEND_OFF (BASE_SAVE_AREA_SIZE - 52)
#define BSA_LCOUNT_OFF (BASE_SAVE_AREA_SIZE - 56)
#if XCHAL_HAVE_S32C1I
#define BSA_SCOMPARE1_OFF \
(BASE_SAVE_AREA_SIZE - \
(BASE_SAVE_AREA_SIZE_COMMON + \
BASE_SAVE_AREA_SIZE_LOOPS + 4))
BASE_SAVE_AREA_SIZE_LOOPS + \
BASE_SAVE_AREA_SIZE_SCOMPARE))
#endif
#if XCHAL_HAVE_THREADPTR && defined(CONFIG_THREAD_LOCAL_STORAGE)
#define BSA_THREADPTR_OFF \
(BASE_SAVE_AREA_SIZE - \
(BASE_SAVE_AREA_SIZE_COMMON + \
BASE_SAVE_AREA_SIZE_LOOPS + \
BASE_SAVE_AREA_SIZE_SCOMPARE + \
BASE_SAVE_AREA_SIZE_THREADPTR))
#endif
#endif /* ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_ASM2_CONTEXT_H_ */

View file

@ -114,6 +114,10 @@
rsr.SCOMPARE1 a0
s32i a0, a1, BSA_SCOMPARE1_OFF
#endif
#if XCHAL_HAVE_THREADPTR && defined(CONFIG_THREAD_LOCAL_STORAGE)
rur.THREADPTR a0
s32i a0, a1, BSA_THREADPTR_OFF
#endif
.endm
/*

View file

@ -6,6 +6,7 @@
#ifndef ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_ASM2_H_
#define ZEPHYR_ARCH_XTENSA_INCLUDE_XTENSA_ASM2_H_
#include <kernel_structs.h>
#include "xtensa-asm2-context.h"
/**
@ -16,7 +17,7 @@
* INTLEVEL set to zero (i.e. it's a user thread, we don't start with
* anything masked, so don't assume that!).
*/
void *xtensa_init_stack(int *stack_top,
void *xtensa_init_stack(struct k_thread *thread, int *stack_top,
void (*entry)(void *, void *, void *),
void *arg1, void *arg2, void *arg3);

View file

@ -260,7 +260,8 @@ int test_switch(void)
(void)memset(stack2, 0, sizeof(stack2));
int *sp = xtensa_init_stack(&stack2[ARRAY_SIZE(stack2)],
int *sp = xtensa_init_stack(k_current_get(),
&stack2[ARRAY_SIZE(stack2)],
(void *)test_switch_bounce,
0, 0, 0);