arch/xtensa: Support __thread and thread_local keywords

Enable architecture-specific support for those keywords. This is a
"gcc-based" thread local storage implementation.
This commit is contained in:
Tiago Medicci Serrano 2024-11-27 13:30:26 -03:00 committed by Xiang Xiao
parent 02828921db
commit 7548db1980
15 changed files with 291 additions and 2 deletions

View file

@ -158,6 +158,7 @@ config ARCH_XTENSA
select ARCH_HAVE_INTERRUPTSTACK
select ARCH_HAVE_STACKCHECK
select ARCH_HAVE_CUSTOMOPT
select ARCH_HAVE_THREAD_LOCAL
select ARCH_HAVE_TCBINFO
select ARCH_HAVE_STDARG_H
select ARCH_HAVE_SETJMP if ARCH_TOOLCHAIN_GNU

View file

@ -94,8 +94,9 @@
#define REG_SAR (18)
#define REG_EXCCAUSE (19)
#define REG_EXCVADDR (20)
#define REG_THREADPTR (21)
#define _REG_EXTRA_START (21)
#define _REG_EXTRA_START (22)
#if XCHAL_HAVE_S32C1I != 0
# define REG_SCOMPARE1 (_REG_EXTRA_START + 0)

View file

@ -128,13 +128,14 @@
/* Tensilica-defined user registers: */
#define THREADPTR 231 /* threadptr option */
#if 0
#if 0
#define ... 21..24 /* (545CK) */
#define ... 140..143 /* (545CK) */
#endif
#define EXPSTATE 230 /* Diamond */
#define THREADPTR 231 /* threadptr option */
#define FCR 232 /* FPU */
#define FSR 233 /* FPU */
#define AE_OVF_SAR 240 /* HiFi2 */

View file

@ -54,6 +54,10 @@ ifeq ($(CONFIG_SCHED_BACKTRACE),y)
CMN_CSRCS += xtensa_backtrace.c
endif
ifeq ($(CONFIG_SCHED_THREAD_LOCAL),y)
CMN_CSRCS += xtensa_tls.c
endif
ifeq ($(CONFIG_SMP),y)
CMN_CSRCS += xtensa_smpcall.c
endif

View file

@ -144,6 +144,18 @@
#define INTSTACK_COLOR 0xdeadbeef
#define HEAP_COLOR 'h'
#define _START_TEXT _stext
#define _END_TEXT _etext
#define _START_BSS _sbss
#define _END_BSS _ebss
#define _DATA_INIT _eronly
#define _START_DATA _sdata
#define _END_DATA _edata
#define _START_TDATA _stdata
#define _END_TDATA _etdata
#define _START_TBSS _stbss
#define _END_TBSS _etbss
/****************************************************************************
* Public Types
****************************************************************************/
@ -177,6 +189,10 @@ extern uint8_t _sbss[]; /* Start of .bss */
extern uint8_t _ebss[]; /* End+1 of .bss */
extern uint8_t _sheap[]; /* Start of heap */
extern uint8_t _eheap[]; /* End+1 of heap */
extern uint8_t _stdata[]; /* Start of .tdata */
extern uint8_t _etdata[]; /* End+1 of .tdata */
extern uint8_t _stbss[]; /* Start of .tbss */
extern uint8_t _etbss[]; /* End+1 of .tbss */
extern uint8_t _sbss_extmem[]; /* start of external memory bss */
extern uint8_t _ebss_extmem[]; /* End+1 of external memory bss */

View file

@ -131,6 +131,9 @@ _xtensa_context_save:
s32i a15, sp, (4 * REG_A15)
#endif
rur a3, THREADPTR
s32i a3, sp, (4 * REG_THREADPTR)
rsr a3, SAR
s32i a3, sp, (4 * REG_SAR)
@ -276,6 +279,9 @@ _xtensa_context_restore:
wsr a3, SCOMPARE1
#endif
l32i a3, a2, (4 * REG_THREADPTR)
wur a3, THREADPTR
l32i a3, a2, (4 * REG_SAR)
wsr a3, SAR

View file

@ -31,6 +31,7 @@
#include <string.h>
#include <nuttx/arch.h>
#include <nuttx/nuttx.h>
#include <arch/irq.h>
#include <arch/xtensa/core.h>
#include <arch/chip/core-isa.h>
@ -39,6 +40,23 @@
#include "xtensa.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_SCHED_THREAD_LOCAL
# define TCB_SIZE 8
#endif
/****************************************************************************
* Public Data
****************************************************************************/
extern int _thread_local_start;
extern int _thread_local_end;
extern int _rodata_reserved_start;
extern int _rodata_reserved_align;
/****************************************************************************
* Public Functions
****************************************************************************/
@ -59,6 +77,10 @@
void up_initial_state(struct tcb_s *tcb)
{
struct xcptcontext *xcp = &tcb->xcp;
#ifdef CONFIG_SCHED_THREAD_LOCAL
const uint32_t base = ALIGN_UP((uint32_t)&_rodata_reserved_align,
TCB_SIZE);
#endif
/* Initialize the initial exception register context structure */
@ -100,6 +122,116 @@ void up_initial_state(struct tcb_s *tcb)
xcp->regs[REG_A1] = (uint32_t)tcb->stack_base_ptr + /* Physical top of stack frame */
tcb->adj_stack_size;
/* Each task access the TLS variables using the THREADPTR register plus an
* offset to obtain the address of the variable.
*
* TLS layout at link-time (on flash), where 0xNNN is the offset that the
* linker calculates to a particular TLS variable:
*
* LOW ADDRESS
* |---------------------------| Linker Symbols
* | Section | --------------
* | .flash.rodata |
* 0x0|---------------------------| <- _rodata_reserved_start
* ^ | Other Data |
* | |---------------------------| <- _thread_local_start
* | | .tdata | ^ ^
* | | | | |
* | | | | Offset from |
* | | | | TLS start |
* | | | | | tls_area_size
* V | | V |
* 0xNNN | int example; | |
* | | |
* | | |
* | .tbss | V
* |---------------------------| <- _thread_local_end
* | Other data |
* | ... |
* |---------------------------|
* HIGH ADDRESS
*
* Consider the TLS variable `example`. Its location is calculated as:
*
* &example = &_rodata_reserved_start + 0xNNN
*
* And the offset 0xNNN can be calculated as:
*
* 0xNNN = (&_thread_local_start - &_rodata_reserved_start) +
* Offset from TLS start
*
* Consider the following diagram for the stack layout:
*
* Note: the following diagram shows the stack layout for a particular task
* and the memory grows towards the stack base (lower memory addresses).
*
* HIGH ADDRESS
* |---------------------------| <- Top of the stack
* | |
* | |
* |---------------------------| <- Base of the stack
* | .tbss (*) |
* | |
* | |
* | int example; |
* ^ | | ^
* | | | | Offset from TLS start
* | | .tdata (*) | V
* | |---------------------------| <- Start of the TLS area
* 0xNNN | | | ^
* | | | |
* | | ... | | (_thread_local_start -
* | | | | _rodata_reserved_start)
* | | | | + align_up(TCB_SIZE,
* | | | | tls_section_align)
* | | | |
* | | | V
* V | | <- threadptr register's value
*
* LOW ADDRESS
*
* At run-time, each task accesses the TLS variables using the THREADPTR
* register plus an offset to obtain the address of the variable.
*
* &example = THREADPTR + 0xNNN (calculated at link-time)
*
* Similarly, example can be accessed as:
*
* &example = Start of the TLS area + Offset from TLS start
*
* The start of the TLS area is given by (check xtensa_tls.c):
*
* Start of the TLS area = tcb->stack_alloc_ptr +
* sizeof(struct tls_info_s)
*
* Calculate the THREADPTR register's initialization value based on the
* link-time offset and the TLS area allocated on the stack.
*
* THREADPTR = &example - 0xNNN
* THREADPTR = (tcb->stack_alloc_ptr + sizeof(struct tls_info_s)) +
* Offset from TLS start - 0xNNN
*
* And, finally, based on 0xNNN calculated at link-time:
*
* THREADPTR = (tcb->stack_alloc_ptr + sizeof(struct tls_info_s)) -
* (&_thread_local_start - &_rodata_reserved_start) - base
*
* Note: Xtensa is slightly different compared to the RISC-V port as there
* is an implicit aligned TCB_SIZE added to the offset.
* - "offset = address - tls_section_vma +
* align_up(TCB_SIZE, tls_section_align)"
* - TCB_SIZE is hardcoded to 8
* Refer to https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=bfd/
* elf32-xtensa.c;h=f078cbde7146675fd2ed82eb5102ae2596c20775;hb=HEAD#l1830
*/
#ifdef CONFIG_SCHED_THREAD_LOCAL
xcp->regs[REG_THREADPTR] = (uintptr_t)tcb->stack_alloc_ptr +
sizeof(struct tls_info_s) -
((uint32_t)&_thread_local_start -
(uint32_t)&_rodata_reserved_start) - base;
#endif
/* Set initial PS to int level 0, user mode. */
#ifdef __XTENSA_CALL0_ABI__

View file

@ -79,6 +79,8 @@ int up_saveusercontext(void *saveregs)
" s32i a14, %0, (4 * " STRINGIFY(REG_A14) ")\n"
" s32i a15, %0, (4 * " STRINGIFY(REG_A15) ")\n"
#endif
" rur a2, THREADPTR\n"
" s32i a2, %0, (4 * " STRINGIFY(THREADPTR) ")\n"
" rsr a2, SAR\n"
" s32i a2, %0, (4 * " STRINGIFY(REG_SAR) ")\n"
#if XCHAL_HAVE_S32C1I != 0

View file

@ -0,0 +1,74 @@
/****************************************************************************
* arch/xtensa/src/common/xtensa_tls.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/arch.h>
#include <nuttx/tls.h>
#include "xtensa.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_tls_size
*
* Description:
* Get TLS (sizeof(struct tls_info_s) + tdata + tbss) section size.
*
* Returned Value:
* Size of (sizeof(struct tls_info_s) + tdata + tbss).
*
****************************************************************************/
int up_tls_size(void)
{
size_t size_tls_info_s = sizeof(struct tls_info_s);
size_t size_tsec = (_END_TBSS - _START_TDATA);
return size_tls_info_s + size_tsec;
}
/****************************************************************************
* Name: up_tls_initialize
*
* Description:
* Initialize thread local region.
*
* Input Parameters:
* info - The TLS structure to initialize.
*
****************************************************************************/
void up_tls_initialize(struct tls_info_s *info)
{
uint8_t *tls_data = (uint8_t *)(info + 1);
uint32_t tdata_len = (_END_TDATA - _START_TDATA);
uint32_t tbss_len = (_END_TBSS - _START_TBSS);
memcpy(tls_data, _START_TDATA, tdata_len);
memset(tls_data + tdata_len, 0, tbss_len);
}

View file

@ -403,6 +403,7 @@ SECTIONS
.flash.rodata :
{
_rodata_reserved_start = ABSOLUTE(.);
. = ALIGN(4);
_srodata = ABSOLUTE(.);
*(EXCLUDE_FILE (*libarch.a:esp32_spiflash.* *libarch.a:esp32_spicache.*
@ -469,8 +470,24 @@ SECTIONS
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
/* TLS data. */
. = ALIGN(4);
_thread_local_start = ABSOLUTE(.);
_stdata = ABSOLUTE(.);
*(.tdata .tdata.* .gnu.linkonce.td.*);
_etdata = ABSOLUTE(.);
_stbss = ABSOLUTE(.);
*(.tbss .tbss.* .gnu.linkonce.tb.* .tcommon);
_etbss = ABSOLUTE(.);
_thread_local_end = ABSOLUTE(.);
_rodata_reserved_end = ABSOLUTE(.);
} >default_rodata_seg AT>ROM
_rodata_reserved_align = ALIGNOF(.flash.rodata);
_image_irom_vma = ADDR(.flash.text);
_image_irom_lma = LOADADDR(.flash.text);

View file

@ -38,6 +38,7 @@ CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=114688
CONFIG_RAM_START=0x20000000
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_THREAD_LOCAL=y
CONFIG_SCHED_WAITPID=y
CONFIG_SPI=y
CONFIG_START_DAY=6

View file

@ -315,6 +315,8 @@ SECTIONS
.flash.rodata :
{
_rodata_reserved_start = ABSOLUTE(.);
_srodata = ABSOLUTE(.);
*(EXCLUDE_FILE (esp32s2_start.*) .rodata)
*(EXCLUDE_FILE (esp32s2_start.*) .rodata.*)
@ -374,8 +376,23 @@ SECTIONS
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
/* TLS data. */
. = ALIGN(4);
_thread_local_start = ABSOLUTE(.);
_stdata = ABSOLUTE(.);
*(.tdata .tdata.* .gnu.linkonce.td.*);
_etdata = ABSOLUTE(.);
_stbss = ABSOLUTE(.);
*(.tbss .tbss.* .gnu.linkonce.tb.* .tcommon);
_etbss = ABSOLUTE(.);
_thread_local_end = ABSOLUTE(.);
_rodata_reserved_end = ABSOLUTE(.);
} >default_rodata_seg AT>ROM
_rodata_reserved_align = ALIGNOF(.flash.rodata);
_image_irom_vma = ADDR(.flash.text);
_image_irom_lma = LOADADDR(.flash.text);

View file

@ -37,6 +37,7 @@ CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=114688
CONFIG_RAM_START=0x20000000
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_THREAD_LOCAL=y
CONFIG_SCHED_WAITPID=y
CONFIG_START_DAY=6
CONFIG_START_MONTH=12

View file

@ -523,9 +523,24 @@ SECTIONS
*(.lit4.*)
*(.gnu.linkonce.lit4.*)
_lit4_end = ABSOLUTE(.);
/* TLS data. */
. = ALIGN(4);
_thread_local_start = ABSOLUTE(.);
_stdata = ABSOLUTE(.);
*(.tdata .tdata.* .gnu.linkonce.td.*);
_etdata = ABSOLUTE(.);
_stbss = ABSOLUTE(.);
*(.tbss .tbss.* .gnu.linkonce.tb.* .tcommon);
_etbss = ABSOLUTE(.);
_thread_local_end = ABSOLUTE(.);
_rodata_reserved_end = ABSOLUTE(.);
} >drom0_0_seg AT>ROM
_rodata_reserved_align = ALIGNOF(.flash.rodata);
_image_irom_vma = ADDR(.flash.text);
_image_irom_lma = LOADADDR(.flash.text);

View file

@ -40,6 +40,7 @@ CONFIG_PREALLOC_TIMERS=4
CONFIG_RAM_SIZE=114688
CONFIG_RAM_START=0x20000000
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_THREAD_LOCAL=y
CONFIG_SCHED_WAITPID=y
CONFIG_SMP=y
CONFIG_SMP_NCPUS=2