[PATCH 5/5] Add thread-local storage (TLS) support

Sebastian Huber sebastian.huber at embedded-brains.de
Wed Jan 29 10:17:52 UTC 2014


Tested and implemented on ARM, m68k, PowerPC and SPARC.  Other
architectures need more work.
---
 c/src/lib/libcpu/powerpc/new-exceptions/cpu.c     |   16 +-
 cpukit/score/Makefile.am                          |    1 +
 cpukit/score/cpu/arm/Makefile.am                  |    1 +
 cpukit/score/cpu/arm/__aeabi_read_tp.c            |   44 ++++
 cpukit/score/cpu/arm/armv7m-context-initialize.c  |    3 +-
 cpukit/score/cpu/arm/cpu.c                        |   20 ++-
 cpukit/score/cpu/arm/cpu_asm.S                    |   10 +
 cpukit/score/cpu/arm/rtems/score/arm.h            |    5 +
 cpukit/score/cpu/arm/rtems/score/cpu.h            |   10 +-
 cpukit/score/cpu/avr/rtems/score/cpu.h            |    3 +-
 cpukit/score/cpu/bfin/cpu.c                       |    3 +-
 cpukit/score/cpu/bfin/rtems/score/cpu.h           |    4 +-
 cpukit/score/cpu/h8300/rtems/score/cpu.h          |    2 +-
 cpukit/score/cpu/i386/rtems/score/cpu.h           |    2 +-
 cpukit/score/cpu/lm32/rtems/score/cpu.h           |    2 +-
 cpukit/score/cpu/m32c/context_init.c              |    3 +-
 cpukit/score/cpu/m32c/rtems/score/cpu.h           |    4 +-
 cpukit/score/cpu/m32r/context_init.c              |    3 +-
 cpukit/score/cpu/m32r/rtems/score/cpu.h           |    4 +-
 cpukit/score/cpu/m68k/Makefile.am                 |    1 +
 cpukit/score/cpu/m68k/__m68k_read_tp.c            |   36 +++
 cpukit/score/cpu/m68k/cpu.c                       |   27 +++
 cpukit/score/cpu/m68k/rtems/score/cpu.h           |   33 +--
 cpukit/score/cpu/mips/cpu.c                       |    3 +-
 cpukit/score/cpu/mips/rtems/score/cpu.h           |    3 +-
 cpukit/score/cpu/moxie/rtems/score/cpu.h          |    2 +-
 cpukit/score/cpu/nios2/nios2-context-initialize.c |    3 +-
 cpukit/score/cpu/nios2/rtems/score/cpu.h          |    4 +-
 cpukit/score/cpu/no_cpu/rtems/score/cpu.h         |    3 +-
 cpukit/score/cpu/powerpc/rtems/score/cpu.h        |    3 +-
 cpukit/score/cpu/sh/cpu.c                         |    3 +-
 cpukit/score/cpu/sh/rtems/score/cpu.h             |    3 +-
 cpukit/score/cpu/sparc/cpu.c                      |   10 +-
 cpukit/score/cpu/sparc/rtems/score/cpu.h          |    4 +-
 cpukit/score/cpu/sparc64/cpu.c                    |   10 +-
 cpukit/score/cpu/sparc64/rtems/score/cpu.h        |    3 +-
 cpukit/score/cpu/v850/cpu.c                       |    3 +-
 cpukit/score/cpu/v850/rtems/score/cpu.h           |    4 +-
 cpukit/score/include/rtems/score/context.h        |    7 +-
 cpukit/score/include/rtems/score/thread.h         |    2 +
 cpukit/score/include/rtems/score/tls.h            |  169 ++++++++++++++
 cpukit/score/preinstall.am                        |    4 +
 cpukit/score/src/threadclose.c                    |    2 +
 cpukit/score/src/threadinitialize.c               |   18 ++
 cpukit/score/src/threadloadenv.c                  |    3 +-
 cpukit/score/src/wkspace.c                        |   30 +++
 testsuites/sptests/Makefile.am                    |    4 +
 testsuites/sptests/configure.ac                   |    7 +
 testsuites/sptests/sptls01/Makefile.am            |   19 ++
 testsuites/sptests/sptls01/init.c                 |   98 ++++++++
 testsuites/sptests/sptls01/sptls01.doc            |   11 +
 testsuites/sptests/sptls01/sptls01.scn            |    5 +
 testsuites/sptests/sptls02/Makefile.am            |   20 ++
 testsuites/sptests/sptls02/init.cc                |  257 +++++++++++++++++++++
 testsuites/sptests/sptls02/sptls02.doc            |   12 +
 testsuites/sptests/sptls02/sptls02.scn            |    4 +
 56 files changed, 912 insertions(+), 58 deletions(-)
 create mode 100644 cpukit/score/cpu/arm/__aeabi_read_tp.c
 create mode 100644 cpukit/score/cpu/m68k/__m68k_read_tp.c
 create mode 100644 cpukit/score/include/rtems/score/tls.h
 create mode 100644 testsuites/sptests/sptls01/Makefile.am
 create mode 100644 testsuites/sptests/sptls01/init.c
 create mode 100644 testsuites/sptests/sptls01/sptls01.doc
 create mode 100644 testsuites/sptests/sptls01/sptls01.scn
 create mode 100644 testsuites/sptests/sptls02/Makefile.am
 create mode 100644 testsuites/sptests/sptls02/init.cc
 create mode 100644 testsuites/sptests/sptls02/sptls02.doc
 create mode 100644 testsuites/sptests/sptls02/sptls02.scn

diff --git a/c/src/lib/libcpu/powerpc/new-exceptions/cpu.c b/c/src/lib/libcpu/powerpc/new-exceptions/cpu.c
index d6a883a..a339f70 100644
--- a/c/src/lib/libcpu/powerpc/new-exceptions/cpu.c
+++ b/c/src/lib/libcpu/powerpc/new-exceptions/cpu.c
@@ -34,6 +34,7 @@
 #include <rtems/score/thread.h>
 #include <rtems/score/interr.h>
 #include <rtems/score/cpu.h>
+#include <rtems/score/tls.h>
 #include <rtems/powerpc/powerpc.h>
 
 /*  _CPU_Initialize
@@ -62,13 +63,13 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
   ppc_context *the_ppc_context;
   uint32_t   msr_value;
   uint32_t   sp;
-  register uint32_t gpr2 __asm__("2");
 
   sp = (uint32_t)stack_base + size - PPC_MINIMUM_STACK_FRAME_SIZE;
 
@@ -128,9 +129,18 @@ void _CPU_Context_Initialize(
   the_ppc_context->gpr1 = sp;
   the_ppc_context->msr = msr_value;
   the_ppc_context->lr = (uint32_t) entry_point;
-  the_ppc_context->gpr2 = gpr2;
 
 #ifdef __ALTIVEC__
   _CPU_Context_initialize_altivec( the_ppc_context );
 #endif
+
+  if ( tls_area != NULL ) {
+    void *tls_block = _TLS_TCB_before_tls_block_initialize( tls_area );
+
+    the_ppc_context->gpr2 = (uint32_t) tls_block + 0x7000;
+  } else {
+    register uint32_t gpr2 __asm__("2");
+
+    the_ppc_context->gpr2 = gpr2;
+  }
 }
diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am
index 4b22129..871d44e 100644
--- a/cpukit/score/Makefile.am
+++ b/cpukit/score/Makefile.am
@@ -74,6 +74,7 @@ include_rtems_score_HEADERS += include/rtems/score/threadsync.h
 include_rtems_score_HEADERS += include/rtems/score/timespec.h
 include_rtems_score_HEADERS += include/rtems/score/timestamp.h
 include_rtems_score_HEADERS += include/rtems/score/timestamp64.h
+include_rtems_score_HEADERS += include/rtems/score/tls.h
 include_rtems_score_HEADERS += include/rtems/score/tod.h
 include_rtems_score_HEADERS += include/rtems/score/todimpl.h
 include_rtems_score_HEADERS += include/rtems/score/userext.h
diff --git a/cpukit/score/cpu/arm/Makefile.am b/cpukit/score/cpu/arm/Makefile.am
index edd7248..9fc0781 100644
--- a/cpukit/score/cpu/arm/Makefile.am
+++ b/cpukit/score/cpu/arm/Makefile.am
@@ -15,6 +15,7 @@ include_rtems_score_HEADERS += rtems/score/cpusmplock.h
 noinst_LIBRARIES = libscorecpu.a
 libscorecpu_a_CPPFLAGS = $(AM_CPPFLAGS)
 libscorecpu_a_SOURCES =
+libscorecpu_a_SOURCES += __aeabi_read_tp.c
 libscorecpu_a_SOURCES += cpu.c
 libscorecpu_a_SOURCES += cpu_asm.S
 libscorecpu_a_SOURCES += arm-context-validate.S
diff --git a/cpukit/score/cpu/arm/__aeabi_read_tp.c b/cpukit/score/cpu/arm/__aeabi_read_tp.c
new file mode 100644
index 0000000..afdfd99
--- /dev/null
+++ b/cpukit/score/cpu/arm/__aeabi_read_tp.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Obere Lagerstr. 30
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <rtems/score/thread.h>
+#include <rtems/score/percpu.h>
+
+#ifndef RTEMS_SMP
+
+void __attribute__((naked)) __aeabi_read_tp(void);
+
+void __attribute__((naked)) __aeabi_read_tp(void)
+{
+  __asm__ volatile (
+    "ldr r0, =_Per_CPU_Information\n"
+    "ldr r0, [r0, %[executingoff]]\n"
+#if defined(__thumb__) && !defined(__thumb2__)
+    "add r0, %[tlsareaoff]\n"
+    "ldr r0, [r0]\n"
+#else
+    "ldr r0, [r0, %[tlsareaoff]]\n"
+#endif
+    "bx lr\n"
+    :
+    : [executingoff] "I" (offsetof(Per_CPU_Control, executing)),
+      [tlsareaoff] "I" (offsetof(Thread_Control, Start.tls_area))
+  );
+}
+
+#endif /* RTEMS_SMP */
diff --git a/cpukit/score/cpu/arm/armv7m-context-initialize.c b/cpukit/score/cpu/arm/armv7m-context-initialize.c
index 892df4d..8d91c67 100644
--- a/cpukit/score/cpu/arm/armv7m-context-initialize.c
+++ b/cpukit/score/cpu/arm/armv7m-context-initialize.c
@@ -35,7 +35,8 @@ void _CPU_Context_Initialize(
   size_t stack_area_size,
   uint32_t new_level,
   void (*entry_point)( void ),
-  bool is_fp
+  bool is_fp,
+  void *tls_area_begin
 )
 {
   char *stack_area_end = (char *) stack_area_begin + stack_area_size;
diff --git a/cpukit/score/cpu/arm/cpu.c b/cpukit/score/cpu/arm/cpu.c
index 1c1fe5d..c3f071d 100644
--- a/cpukit/score/cpu/arm/cpu.c
+++ b/cpukit/score/cpu/arm/cpu.c
@@ -32,6 +32,7 @@
 #include <rtems/score/isr.h>
 #include <rtems/score/wkspace.h>
 #include <rtems/score/thread.h>
+#include <rtems/score/tls.h>
 #include <rtems/score/cpu.h>
 
 #ifdef ARM_MULTILIB_VFP_D32
@@ -41,6 +42,14 @@
   );
 #endif
 
+#ifdef ARM_MULTILIB_HAS_THREAD_ID_REGISTER
+  RTEMS_STATIC_ASSERT(
+    offsetof( Context_Control, thread_id )
+      == ARM_CONTEXT_CONTROL_THREAD_ID_OFFSET,
+    ARM_CONTEXT_CONTROL_THREAD_ID_OFFSET
+  );
+#endif
+
 RTEMS_STATIC_ASSERT(
   sizeof( CPU_Exception_frame ) == ARM_EXCEPTION_FRAME_SIZE,
   ARM_EXCEPTION_FRAME_SIZE
@@ -71,13 +80,22 @@ void _CPU_Context_Initialize(
   size_t stack_area_size,
   uint32_t new_level,
   void (*entry_point)( void ),
-  bool is_fp
+  bool is_fp,
+  void *tls_area
 )
 {
   the_context->register_sp = (uint32_t) stack_area_begin + stack_area_size;
   the_context->register_lr = (uint32_t) entry_point;
   the_context->register_cpsr = ( ( new_level != 0 ) ? ARM_PSR_I : 0 )
     | arm_cpu_mode;
+
+#ifdef ARM_MULTILIB_HAS_THREAD_ID_REGISTER
+  the_context->thread_id = (uint32_t) tls_area;
+#endif
+
+  if ( tls_area != NULL ) {
+    _TLS_TCB_at_area_begin_initialize( tls_area );
+  }
 }
 
 /* Preprocessor magic for stringification of x */
diff --git a/cpukit/score/cpu/arm/cpu_asm.S b/cpukit/score/cpu/arm/cpu_asm.S
index 7fb4062..1771ddd 100644
--- a/cpukit/score/cpu/arm/cpu_asm.S
+++ b/cpukit/score/cpu/arm/cpu_asm.S
@@ -62,12 +62,22 @@ DEFINE_FUNCTION_ARM(_CPU_Context_switch)
 	vstm	r3, {d8-d15}
 #endif
 
+#ifdef ARM_MULTILIB_HAS_THREAD_ID_REGISTER
+	mrc	p15, 0, r3, c13, c0, 3
+	str	r3, [r0, #ARM_CONTEXT_CONTROL_THREAD_ID_OFFSET]
+#endif
+
 /* Start restoring context */
 _restore:
 #ifdef ARM_MULTILIB_HAS_LOAD_STORE_EXCLUSIVE
 	clrex
 #endif
 
+#ifdef ARM_MULTILIB_HAS_THREAD_ID_REGISTER
+	ldr	r3, [r1, #ARM_CONTEXT_CONTROL_THREAD_ID_OFFSET]
+	mcr	p15, 0, r3, c13, c0, 3
+#endif
+
 #ifdef ARM_MULTILIB_VFP_D32
 	add	r3, r1, #ARM_CONTEXT_CONTROL_D8_OFFSET
 	vldm	r3, {d8-d15}
diff --git a/cpukit/score/cpu/arm/rtems/score/arm.h b/cpukit/score/cpu/arm/rtems/score/arm.h
index 608f753..3e428f9 100644
--- a/cpukit/score/cpu/arm/rtems/score/arm.h
+++ b/cpukit/score/cpu/arm/rtems/score/arm.h
@@ -44,6 +44,11 @@ extern "C" {
   #define ARM_MULTILIB_HAS_LOAD_STORE_EXCLUSIVE
 #endif
 
+#if defined(__ARM_ARCH_7A__) \
+  || defined(__ARM_ARCH_7R__)
+  #define ARM_MULTILIB_HAS_THREAD_ID_REGISTER
+#endif
+
 #if defined(__ARM_NEON__)
   #define ARM_MULTILIB_VFP_D32
 #elif !defined(__SOFTFP__)
diff --git a/cpukit/score/cpu/arm/rtems/score/cpu.h b/cpukit/score/cpu/arm/rtems/score/cpu.h
index 2d9b5d5..216f39f 100644
--- a/cpukit/score/cpu/arm/rtems/score/cpu.h
+++ b/cpukit/score/cpu/arm/rtems/score/cpu.h
@@ -212,6 +212,10 @@
 
 /** @} */
 
+#ifdef ARM_MULTILIB_HAS_THREAD_ID_REGISTER
+  #define ARM_CONTEXT_CONTROL_THREAD_ID_OFFSET 44
+#endif
+
 #ifdef ARM_MULTILIB_VFP_D32
   #define ARM_CONTEXT_CONTROL_D8_OFFSET 48
 #endif
@@ -267,6 +271,9 @@ typedef struct {
 #else
   void *register_sp;
 #endif
+#ifdef ARM_MULTILIB_HAS_THREAD_ID_REGISTER
+  uint32_t thread_id;
+#endif
 #ifdef ARM_MULTILIB_VFP_D32
   uint64_t register_d8;
   uint64_t register_d9;
@@ -400,7 +407,8 @@ void _CPU_Context_Initialize(
   size_t stack_area_size,
   uint32_t new_level,
   void (*entry_point)( void ),
-  bool is_fp
+  bool is_fp,
+  void *tls_area
 );
 
 #define _CPU_Context_Get_SP( _context ) \
diff --git a/cpukit/score/cpu/avr/rtems/score/cpu.h b/cpukit/score/cpu/avr/rtems/score/cpu.h
index b67b241..f82b763 100644
--- a/cpukit/score/cpu/avr/rtems/score/cpu.h
+++ b/cpukit/score/cpu/avr/rtems/score/cpu.h
@@ -963,7 +963,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /*
diff --git a/cpukit/score/cpu/bfin/cpu.c b/cpukit/score/cpu/bfin/cpu.c
index 05b9243..268848e 100644
--- a/cpukit/score/cpu/bfin/cpu.c
+++ b/cpukit/score/cpu/bfin/cpu.c
@@ -186,7 +186,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
     uint32_t     stack_high;  /* highest "stack aligned" address */
diff --git a/cpukit/score/cpu/bfin/rtems/score/cpu.h b/cpukit/score/cpu/bfin/rtems/score/cpu.h
index 1af3f81..a8c60a5 100644
--- a/cpukit/score/cpu/bfin/rtems/score/cpu.h
+++ b/cpukit/score/cpu/bfin/rtems/score/cpu.h
@@ -817,6 +817,7 @@ uint32_t   _CPU_ISR_Get_level( void );
  *       point thread.  This is typically only used on CPUs where the
  *       FPU may be easily disabled by software such as on the SPARC
  *       where the PSR contains an enable FPU bit.
+ * @param[in] tls_area is the thread-local storage (TLS) area
  *
  * Port Specific Information:
  *
@@ -828,7 +829,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /**
diff --git a/cpukit/score/cpu/h8300/rtems/score/cpu.h b/cpukit/score/cpu/h8300/rtems/score/cpu.h
index f75f2e5..66774d4 100644
--- a/cpukit/score/cpu/h8300/rtems/score/cpu.h
+++ b/cpukit/score/cpu/h8300/rtems/score/cpu.h
@@ -756,7 +756,7 @@ uint32_t   _CPU_ISR_Get_level( void );
 #define CPU_CCR_INTERRUPTS_OFF 0x00
 
 #define _CPU_Context_Initialize( _the_context, _stack_base, _size, \
-                                   _isr, _entry_point, _is_fp ) \
+                                   _isr, _entry_point, _is_fp, _tls_area ) \
   /* Locate Me */ \
   do { \
     uintptr_t   _stack; \
diff --git a/cpukit/score/cpu/i386/rtems/score/cpu.h b/cpukit/score/cpu/i386/rtems/score/cpu.h
index 43422ed..dd06059 100644
--- a/cpukit/score/cpu/i386/rtems/score/cpu.h
+++ b/cpukit/score/cpu/i386/rtems/score/cpu.h
@@ -443,7 +443,7 @@ uint32_t   _CPU_ISR_Get_level( void );
 
 
 #define _CPU_Context_Initialize( _the_context, _stack_base, _size, \
-                                   _isr, _entry_point, _is_fp ) \
+                                   _isr, _entry_point, _is_fp, _tls_area ) \
   do { \
     uint32_t   _stack; \
     \
diff --git a/cpukit/score/cpu/lm32/rtems/score/cpu.h b/cpukit/score/cpu/lm32/rtems/score/cpu.h
index 95553ef..4699c1a 100644
--- a/cpukit/score/cpu/lm32/rtems/score/cpu.h
+++ b/cpukit/score/cpu/lm32/rtems/score/cpu.h
@@ -823,7 +823,7 @@ uint32_t   _CPU_ISR_Get_level( void );
 extern char _gp[];
 
 #define _CPU_Context_Initialize( _the_context, _stack_base, _size, \
-                                 _isr, _entry_point, _is_fp ) \
+                                 _isr, _entry_point, _is_fp, _tls_area ) \
    do { \
      uint32_t _stack = (uint32_t)(_stack_base) + (_size) - 4; \
      (_the_context)->gp = (uint32_t)_gp; \
diff --git a/cpukit/score/cpu/m32c/context_init.c b/cpukit/score/cpu/m32c/context_init.c
index d7c1c5d..b2edcf8 100644
--- a/cpukit/score/cpu/m32c/context_init.c
+++ b/cpukit/score/cpu/m32c/context_init.c
@@ -50,7 +50,8 @@ void _CPU_Context_Initialize(
   size_t            size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
   void *stackEnd = stack_base;
diff --git a/cpukit/score/cpu/m32c/rtems/score/cpu.h b/cpukit/score/cpu/m32c/rtems/score/cpu.h
index 5841885..681fb4b 100644
--- a/cpukit/score/cpu/m32c/rtems/score/cpu.h
+++ b/cpukit/score/cpu/m32c/rtems/score/cpu.h
@@ -809,6 +809,7 @@ uint32_t   _CPU_ISR_Get_level( void );
  *       point thread.  This is typically only used on CPUs where the
  *       FPU may be easily disabled by software such as on the SPARC
  *       where the PSR contains an enable FPU bit.
+ * @param[in] tls_area is the thread-local storage (TLS) area
  *
  * Port Specific Information:
  *
@@ -820,7 +821,8 @@ void _CPU_Context_Initialize(
   size_t            size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /**
diff --git a/cpukit/score/cpu/m32r/context_init.c b/cpukit/score/cpu/m32r/context_init.c
index bdfd758..d8ecc53 100644
--- a/cpukit/score/cpu/m32r/context_init.c
+++ b/cpukit/score/cpu/m32r/context_init.c
@@ -33,7 +33,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
   void *stackEnd = stack_base;
diff --git a/cpukit/score/cpu/m32r/rtems/score/cpu.h b/cpukit/score/cpu/m32r/rtems/score/cpu.h
index 0fea7bd..95a897a 100644
--- a/cpukit/score/cpu/m32r/rtems/score/cpu.h
+++ b/cpukit/score/cpu/m32r/rtems/score/cpu.h
@@ -828,6 +828,7 @@ uint32_t   _CPU_ISR_Get_level( void );
  *       point thread.  This is typically only used on CPUs where the
  *       FPU may be easily disabled by software such as on the SPARC
  *       where the PSR contains an enable FPU bit.
+ * @param[in] tls_area is the thread-local storage (TLS) area
  *
  * Port Specific Information:
  *
@@ -839,7 +840,8 @@ void _CPU_Context_Initialize(
   size_t            size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /**
diff --git a/cpukit/score/cpu/m68k/Makefile.am b/cpukit/score/cpu/m68k/Makefile.am
index 863a071..9d8d333 100644
--- a/cpukit/score/cpu/m68k/Makefile.am
+++ b/cpukit/score/cpu/m68k/Makefile.am
@@ -20,6 +20,7 @@ include_rtems_score_HEADERS += rtems/score/cpuatomic.h
 
 libscorecpu_a_SOURCES = cpu.c cpu_asm.S
 libscorecpu_a_SOURCES += m68k-exception-frame-print.c
+libscorecpu_a_SOURCES += __m68k_read_tp.c
 
 include $(srcdir)/preinstall.am
 include $(top_srcdir)/automake/local.am
diff --git a/cpukit/score/cpu/m68k/__m68k_read_tp.c b/cpukit/score/cpu/m68k/__m68k_read_tp.c
new file mode 100644
index 0000000..3e024a8
--- /dev/null
+++ b/cpukit/score/cpu/m68k/__m68k_read_tp.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Obere Lagerstr. 30
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <rtems/score/thread.h>
+#include <rtems/score/tls.h>
+
+void __m68k_read_tp(void);
+
+void __m68k_read_tp(void)
+{
+  const Thread_Control *executing = _Thread_Get_executing();
+  void *tp = (char *) executing->Start.tls_area +
+    _TLS_Get_thread_control_block_area_size((uintptr_t) _TLS_Alignment)
+    + 0x7000;
+
+  __asm__ volatile (
+    "move.l %0, %%a0"
+    :
+    : "d" (tp)
+  );
+}
diff --git a/cpukit/score/cpu/m68k/cpu.c b/cpukit/score/cpu/m68k/cpu.c
index 3776345..589d099 100644
--- a/cpukit/score/cpu/m68k/cpu.c
+++ b/cpukit/score/cpu/m68k/cpu.c
@@ -19,6 +19,7 @@
 
 #include <rtems/system.h>
 #include <rtems/score/isr.h>
+#include <rtems/score/tls.h>
 
 #if defined( __mcoldfire__ ) && ( M68K_HAS_FPU == 1 )
   uint32_t _CPU_cacr_shadow;
@@ -181,3 +182,29 @@ void _CPU_Context_restore_fp (Context_Control_fp **fp_context_ptr)
   _fpCCR = *fp;
 }
 #endif
+
+void _CPU_Context_Initialize(
+  Context_Control *the_context,
+  void *stack_area_begin,
+  size_t stack_area_size,
+  uint32_t new_level,
+  void (*entry_point)( void ),
+  bool is_fp,
+  void *tls_area
+)
+{
+  uint32_t stack;
+
+  the_context->sr      = 0x3000 | (new_level << 8);
+  stack                = (uint32_t)stack_area_begin + stack_area_size - 4;
+  the_context->a7_msp  = (void *)stack;
+  *(void **)stack      = (void *)entry_point;
+
+#if (defined(__mcoldfire__) && ( M68K_HAS_FPU == 1 ))
+  the_context->fpu_dis = is_fp ? 0x00 : 0x10;
+#endif
+
+  if ( tls_area != NULL ) {
+    _TLS_TCB_before_tls_block_initialize( tls_area );
+  }
+}
diff --git a/cpukit/score/cpu/m68k/rtems/score/cpu.h b/cpukit/score/cpu/m68k/rtems/score/cpu.h
index ccec3a6..9981f53 100644
--- a/cpukit/score/cpu/m68k/rtems/score/cpu.h
+++ b/cpukit/score/cpu/m68k/rtems/score/cpu.h
@@ -448,30 +448,15 @@ uint32_t   _CPU_ISR_Get_level( void );
  *     + initialize an FP context area
  */
 
-#if (defined(__mcoldfire__) && ( M68K_HAS_FPU == 1 ))
-#define _CPU_Context_Initialize( _the_context, _stack_base, _size, \
-                                 _isr, _entry_point, _is_fp ) \
-   do { \
-     uint32_t   _stack; \
-     \
-     (_the_context)->sr      = 0x3000 | ((_isr) << 8); \
-     _stack                  = (uint32_t)(_stack_base) + (_size) - 4; \
-     (_the_context)->a7_msp  = (void *)_stack; \
-     *(void **)_stack        = (void *)(_entry_point); \
-     (_the_context)->fpu_dis = (_is_fp == TRUE) ? 0x00 : 0x10;          \
-   } while ( 0 )
-#else
-#define _CPU_Context_Initialize( _the_context, _stack_base, _size,      \
-                                 _isr, _entry_point, _is_fp )           \
-   do {                                                                 \
-     uint32_t   _stack;                                                 \
-                                                                        \
-     (_the_context)->sr      = 0x3000 | ((_isr) << 8);                  \
-     _stack                  = (uint32_t)(_stack_base) + (_size) - 4;   \
-     (_the_context)->a7_msp  = (void *)_stack;                          \
-     *(void **)_stack        = (void *)(_entry_point);                  \
-   } while ( 0 )
-#endif
+void _CPU_Context_Initialize(
+  Context_Control *the_context,
+  void *stack_area_begin,
+  size_t stack_area_size,
+  uint32_t new_level,
+  void (*entry_point)( void ),
+  bool is_fp,
+  void *tls_area
+);
 
 /* end of Context handler macros */
 
diff --git a/cpukit/score/cpu/mips/cpu.c b/cpukit/score/cpu/mips/cpu.c
index 5f7abeb..3be5df3 100644
--- a/cpukit/score/cpu/mips/cpu.c
+++ b/cpukit/score/cpu/mips/cpu.c
@@ -173,7 +173,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
   uintptr_t             stack_tmp;
diff --git a/cpukit/score/cpu/mips/rtems/score/cpu.h b/cpukit/score/cpu/mips/rtems/score/cpu.h
index 1d1449c..6299961 100644
--- a/cpukit/score/cpu/mips/rtems/score/cpu.h
+++ b/cpukit/score/cpu/mips/rtems/score/cpu.h
@@ -858,7 +858,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 
diff --git a/cpukit/score/cpu/moxie/rtems/score/cpu.h b/cpukit/score/cpu/moxie/rtems/score/cpu.h
index 309110a..36a956c 100644
--- a/cpukit/score/cpu/moxie/rtems/score/cpu.h
+++ b/cpukit/score/cpu/moxie/rtems/score/cpu.h
@@ -651,7 +651,7 @@ uint32_t   _CPU_ISR_Get_level( void );
 #define CPU_CCR_INTERRUPTS_OFF 0x00
 
 #define _CPU_Context_Initialize( _the_context, _stack_base, _size, \
-                                 _isr, _entry_point, _is_fp )      \
+                                 _isr, _entry_point, _is_fp, _tls_area ) \
   /* Locate Me */                                                  \
   do {                                                             \
     uintptr_t   _stack;                                            \
diff --git a/cpukit/score/cpu/nios2/nios2-context-initialize.c b/cpukit/score/cpu/nios2/nios2-context-initialize.c
index e74e04f..6b884d0 100644
--- a/cpukit/score/cpu/nios2/nios2-context-initialize.c
+++ b/cpukit/score/cpu/nios2/nios2-context-initialize.c
@@ -27,7 +27,8 @@ void _CPU_Context_Initialize(
   size_t stack_area_size,
   uint32_t new_level,
   void (*entry_point)( void ),
-  bool is_fp
+  bool is_fp,
+  void *tls_area
 )
 {
   const Nios2_MPU_Configuration *mpu_config = _Nios2_MPU_Get_configuration();
diff --git a/cpukit/score/cpu/nios2/rtems/score/cpu.h b/cpukit/score/cpu/nios2/rtems/score/cpu.h
index e0dfd9f..bdd8f57 100644
--- a/cpukit/score/cpu/nios2/rtems/score/cpu.h
+++ b/cpukit/score/cpu/nios2/rtems/score/cpu.h
@@ -295,6 +295,7 @@ uint32_t _CPU_ISR_Get_level( void );
  * @param[in] new_level is the interrupt level for the task
  * @param[in] entry_point is the task's entry point
  * @param[in] is_fp is set to @c true if the task is a floating point task
+ * @param[in] tls_area is the thread-local storage (TLS) area
  */
 void _CPU_Context_Initialize(
   Context_Control *context,
@@ -302,7 +303,8 @@ void _CPU_Context_Initialize(
   size_t stack_area_size,
   uint32_t new_level,
   void (*entry_point)( void ),
-  bool is_fp
+  bool is_fp,
+  void *tls_area
 );
 
 #define _CPU_Context_Restart_self( _the_context ) \
diff --git a/cpukit/score/cpu/no_cpu/rtems/score/cpu.h b/cpukit/score/cpu/no_cpu/rtems/score/cpu.h
index f1f02ed..783da76 100644
--- a/cpukit/score/cpu/no_cpu/rtems/score/cpu.h
+++ b/cpukit/score/cpu/no_cpu/rtems/score/cpu.h
@@ -932,13 +932,14 @@ uint32_t   _CPU_ISR_Get_level( void );
  *       point thread.  This is typically only used on CPUs where the
  *       FPU may be easily disabled by software such as on the SPARC
  *       where the PSR contains an enable FPU bit.
+ * @param[in] _tls_area The thread-local storage (TLS) area.
  *
  * Port Specific Information:
  *
  * XXX document implementation including references if appropriate
  */
 #define _CPU_Context_Initialize( _the_context, _stack_base, _size, \
-                                 _isr, _entry_point, _is_fp ) \
+                                 _isr, _entry_point, _is_fp, _tls_area ) \
   { \
   }
 
diff --git a/cpukit/score/cpu/powerpc/rtems/score/cpu.h b/cpukit/score/cpu/powerpc/rtems/score/cpu.h
index 6263d34..8eb2327 100644
--- a/cpukit/score/cpu/powerpc/rtems/score/cpu.h
+++ b/cpukit/score/cpu/powerpc/rtems/score/cpu.h
@@ -797,7 +797,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /*
diff --git a/cpukit/score/cpu/sh/cpu.c b/cpukit/score/cpu/sh/cpu.c
index 57d6ffc..be3fedd 100644
--- a/cpukit/score/cpu/sh/cpu.c
+++ b/cpukit/score/cpu/sh/cpu.c
@@ -211,7 +211,8 @@ void _CPU_Context_Initialize(
   uint32_t  		_size,
   uint32_t  		_isr,
   void 	(*_entry_point)(void),
-  int			_is_fp )
+  int			_is_fp,
+  void			*_tls_base)
 {
   _the_context->r15 = (uint32_t *) ((uint32_t) (_stack_base) + (_size) );
 #if defined(__sh1__) || defined(__sh2__) || defined(__SH2E__)
diff --git a/cpukit/score/cpu/sh/rtems/score/cpu.h b/cpukit/score/cpu/sh/rtems/score/cpu.h
index 41d9638..ff9ad55 100644
--- a/cpukit/score/cpu/sh/rtems/score/cpu.h
+++ b/cpukit/score/cpu/sh/rtems/score/cpu.h
@@ -603,7 +603,8 @@ SCORE_EXTERN void _CPU_Context_Initialize(
   uint32_t              _size,
   uint32_t              _isr,
   void    (*_entry_point)(void),
-  int                   _is_fp );
+  int                   _is_fp,
+  void                  *_tls_area );
 
 /*
  *  This routine is responsible for somehow restarting the currently
diff --git a/cpukit/score/cpu/sparc/cpu.c b/cpukit/score/cpu/sparc/cpu.c
index 1865a21..11e31f9 100644
--- a/cpukit/score/cpu/sparc/cpu.c
+++ b/cpukit/score/cpu/sparc/cpu.c
@@ -20,6 +20,7 @@
 #include <rtems/system.h>
 #include <rtems/score/isr.h>
 #include <rtems/score/percpu.h>
+#include <rtems/score/tls.h>
 #include <rtems/rtems/cache.h>
 
 RTEMS_STATIC_ASSERT(
@@ -232,7 +233,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
     uint32_t     stack_high;  /* highest "stack aligned" address */
@@ -285,4 +287,10 @@ void _CPU_Context_Initialize(
    *  thread can have an _ISR_Dispatch stack frame on its stack.
    */
     the_context->isr_dispatch_disable = 0;
+
+  if ( tls_area != NULL ) {
+    void *tcb = _TLS_TCB_after_tls_block_initialize( tls_area );
+
+    the_context->g7 = (uintptr_t) tcb;
+  }
 }
diff --git a/cpukit/score/cpu/sparc/rtems/score/cpu.h b/cpukit/score/cpu/sparc/rtems/score/cpu.h
index 690ddcf..ae89311 100644
--- a/cpukit/score/cpu/sparc/rtems/score/cpu.h
+++ b/cpukit/score/cpu/sparc/rtems/score/cpu.h
@@ -1019,6 +1019,7 @@ uint32_t   _CPU_ISR_Get_level( void );
  * @param[in] new_level is the interrupt level for the task
  * @param[in] entry_point is the task's entry point
  * @param[in] is_fp is set to TRUE if the task is a floating point task
+ * @param[in] tls_area is the thread-local storage (TLS) area
  *
  * NOTE:  Implemented as a subroutine for the SPARC port.
  */
@@ -1028,7 +1029,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /**
diff --git a/cpukit/score/cpu/sparc64/cpu.c b/cpukit/score/cpu/sparc64/cpu.c
index 94f9340..d7c2f47 100644
--- a/cpukit/score/cpu/sparc64/cpu.c
+++ b/cpukit/score/cpu/sparc64/cpu.c
@@ -20,6 +20,7 @@
 #include <rtems/system.h>
 #include <rtems/asm.h>
 #include <rtems/score/isr.h>
+#include <rtems/score/tls.h>
 #include <rtems/rtems/cache.h>
 
 /*
@@ -65,7 +66,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
     uint64_t     stack_high;  /* highest "stack aligned" address */
@@ -99,4 +101,10 @@ void _CPU_Context_Initialize(
    *  thread can have an _ISR_Dispatch stack frame on its stack.
    */
     the_context->isr_dispatch_disable = 0;
+
+  if ( tls_area != NULL ) {
+    void *tcb = _TLS_TCB_after_tls_block_initialize( tls_area );
+
+    the_context->g7 = (uintptr_t) tcb;
+  }
 }
diff --git a/cpukit/score/cpu/sparc64/rtems/score/cpu.h b/cpukit/score/cpu/sparc64/rtems/score/cpu.h
index 8d3e273..22ec97d 100644
--- a/cpukit/score/cpu/sparc64/rtems/score/cpu.h
+++ b/cpukit/score/cpu/sparc64/rtems/score/cpu.h
@@ -838,7 +838,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /*
diff --git a/cpukit/score/cpu/v850/cpu.c b/cpukit/score/cpu/v850/cpu.c
index 50065b6..d4022f3 100644
--- a/cpukit/score/cpu/v850/cpu.c
+++ b/cpukit/score/cpu/v850/cpu.c
@@ -61,7 +61,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 )
 {
   uint32_t  stack_high;  /* highest "stack aligned" address */
diff --git a/cpukit/score/cpu/v850/rtems/score/cpu.h b/cpukit/score/cpu/v850/rtems/score/cpu.h
index 3e110b5..f41bde0 100644
--- a/cpukit/score/cpu/v850/rtems/score/cpu.h
+++ b/cpukit/score/cpu/v850/rtems/score/cpu.h
@@ -790,6 +790,7 @@ uint32_t   _CPU_ISR_Get_level( void );
  *       point thread.  This is typically only used on CPUs where the
  *       FPU may be easily disabled by software such as on the SPARC
  *       where the PSR contains an enable FPU bit.
+ * @param[in] tls_area is the thread-local storage (TLS) area
  *
  * Port Specific Information:
  *
@@ -801,7 +802,8 @@ void _CPU_Context_Initialize(
   uint32_t          size,
   uint32_t          new_level,
   void             *entry_point,
-  bool              is_fp
+  bool              is_fp,
+  void             *tls_area
 );
 
 /**
diff --git a/cpukit/score/include/rtems/score/context.h b/cpukit/score/include/rtems/score/context.h
index 02bcf15..ffffe9b 100644
--- a/cpukit/score/include/rtems/score/context.h
+++ b/cpukit/score/include/rtems/score/context.h
@@ -60,9 +60,12 @@ extern "C" {
  *  @param[in] _entry is this thread's entry point
  *  @param[in] _is_fp is set to true if this thread has floating point
  *         enabled
+ *  @param[in] _tls_area The thread-local storage (TLS) area begin.
  */
-#define _Context_Initialize(_the_context, _stack, _size, _isr, _entry, _is_fp) \
-   _CPU_Context_Initialize( _the_context, _stack, _size, _isr, _entry, _is_fp )
+#define _Context_Initialize( _the_context, _stack, _size, _isr, _entry, \
+  _is_fp, _tls_area ) \
+    _CPU_Context_Initialize( _the_context, _stack, _size, _isr, _entry, \
+      _is_fp, _tls_area )
 
 /**
  *  This macro is invoked from _Thread_Handler to do whatever CPU
diff --git a/cpukit/score/include/rtems/score/thread.h b/cpukit/score/include/rtems/score/thread.h
index edb2f67..3b09007 100644
--- a/cpukit/score/include/rtems/score/thread.h
+++ b/cpukit/score/include/rtems/score/thread.h
@@ -229,6 +229,8 @@ typedef struct {
   #endif
   /** This field is the initial stack area address. */
   void                                *stack;
+  /** The thread-local storage (TLS) area */
+  void                                *tls_area;
 } Thread_Start_information;
 
 /**
diff --git a/cpukit/score/include/rtems/score/tls.h b/cpukit/score/include/rtems/score/tls.h
new file mode 100644
index 0000000..858a65c
--- /dev/null
+++ b/cpukit/score/include/rtems/score/tls.h
@@ -0,0 +1,169 @@
+/**
+ * @file
+ *
+ * @ingroup ScoreTLS
+ *
+ * @brief Thread-Local Storage (TLS)
+ */
+
+/*
+ * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#ifndef _RTEMS_SCORE_TLS_H
+#define _RTEMS_SCORE_TLS_H
+
+#include <rtems/score/basedefs.h>
+
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup ScoreTLS Thread-Local Storage (TLS)
+ *
+ * @ingroup Score
+ *
+ * @brief Thread-local storage (TLS) support.
+ *
+ * Variants I and II are according to Ulrich Drepper, "ELF Handling For
+ * Thread-Local Storage".
+ *
+ * @{
+ */
+
+extern char _TLS_Data_begin[];
+
+extern char _TLS_Data_end[];
+
+extern char _TLS_Data_size[];
+
+extern char _TLS_BSS_begin[];
+
+extern char _TLS_BSS_end[];
+
+extern char _TLS_BSS_size[];
+
+extern char _TLS_Size[];
+
+extern char _TLS_Alignment[];
+
+typedef struct {
+  /*
+   * FIXME: Not sure if the generation number type is correct for all
+   * architectures.
+  */
+  uint32_t generation_number;
+
+  void *tls_blocks[1];
+} TLS_Dynamic_thread_vector;
+
+typedef struct {
+  TLS_Dynamic_thread_vector *dtv;
+  uintptr_t reserved;
+} TLS_Thread_control_block;
+
+static inline uintptr_t _TLS_Get_thread_control_block_area_size(
+  uintptr_t alignment
+)
+{
+  return alignment <= sizeof(TLS_Thread_control_block) ?
+    sizeof(TLS_Thread_control_block) : alignment;
+}
+
+static inline uintptr_t _TLS_Get_allocation_size(
+  uintptr_t size,
+  uintptr_t alignment
+)
+{
+  return _TLS_Get_thread_control_block_area_size(alignment)
+    + size + sizeof(TLS_Dynamic_thread_vector);
+}
+
+static inline void *_TLS_Copy_and_clear(void *tls_area)
+{
+  tls_area = memcpy(tls_area, _TLS_Data_begin, (size_t) _TLS_Data_size);
+
+  memset(
+    (char *) tls_area + (size_t) _TLS_BSS_begin - (size_t) _TLS_Data_begin,
+    0,
+    (size_t) _TLS_BSS_size
+  );
+
+  return tls_area;
+}
+
+static inline void *_TLS_Initialize(
+  void *tls_block,
+  TLS_Thread_control_block *tcb,
+  TLS_Dynamic_thread_vector *dtv
+)
+{
+  tcb->dtv = dtv;
+  dtv->generation_number = 1;
+  dtv->tls_blocks[0] = tls_block;
+
+  return _TLS_Copy_and_clear(tls_block);
+}
+
+/* Use Variant I, TLS offsets emitted by linker takes the TCB into account */
+static inline void *_TLS_TCB_at_area_begin_initialize(void *tls_area)
+{
+  void *tls_block = (char *) tls_area
+    + _TLS_Get_thread_control_block_area_size( (uintptr_t) _TLS_Alignment );
+  TLS_Thread_control_block *tcb = tls_area;
+  TLS_Dynamic_thread_vector *dtv = (TLS_Dynamic_thread_vector *)
+    ((char *) tls_block + (uintptr_t) _TLS_Size);
+
+  return _TLS_Initialize( tls_block, tcb, dtv );
+}
+
+/* Use Variant I, TLS offsets emitted by linker neglects the TCB */
+static inline void *_TLS_TCB_before_tls_block_initialize(void *tls_area)
+{
+  void *tls_block = (char *) tls_area
+    + _TLS_Get_thread_control_block_area_size( (uintptr_t) _TLS_Alignment );
+  TLS_Thread_control_block *tcb = (TLS_Thread_control_block *)
+    ((char *) tls_block - sizeof(*tcb));
+  TLS_Dynamic_thread_vector *dtv = (TLS_Dynamic_thread_vector *)
+    ((char *) tls_block + (uintptr_t) _TLS_Size);
+
+  return _TLS_Initialize( tls_block, tcb, dtv );
+}
+
+/* Use Variant II */
+static inline void *_TLS_TCB_after_tls_block_initialize(void *tls_area)
+{
+  void *tls_block = (char *) tls_area;
+  uintptr_t tls_size = (uintptr_t) _TLS_Size;
+  uintptr_t tls_align = (uintptr_t) _TLS_Alignment;
+  uintptr_t tls_mask = tls_align - 1;
+  TLS_Thread_control_block *tcb = (TLS_Thread_control_block *)
+    ((char *) tls_block + ((tls_size + tls_mask) & ~tls_mask));
+  TLS_Dynamic_thread_vector *dtv = (TLS_Dynamic_thread_vector *)
+    ((char *) tcb + sizeof(*tcb));
+
+  _TLS_Initialize( tls_block, tcb, dtv );
+
+  return tcb;
+}
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* _RTEMS_SCORE_TLS_H */
diff --git a/cpukit/score/preinstall.am b/cpukit/score/preinstall.am
index 79a18b5..f43fc9d 100644
--- a/cpukit/score/preinstall.am
+++ b/cpukit/score/preinstall.am
@@ -279,6 +279,10 @@ $(PROJECT_INCLUDE)/rtems/score/timestamp64.h: include/rtems/score/timestamp64.h
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/timestamp64.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/timestamp64.h
 
+$(PROJECT_INCLUDE)/rtems/score/tls.h: include/rtems/score/tls.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/tls.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/tls.h
+
 $(PROJECT_INCLUDE)/rtems/score/tod.h: include/rtems/score/tod.h $(PROJECT_INCLUDE)/rtems/score/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/rtems/score/tod.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/rtems/score/tod.h
diff --git a/cpukit/score/src/threadclose.c b/cpukit/score/src/threadclose.c
index df70f50..82c4ab4 100644
--- a/cpukit/score/src/threadclose.c
+++ b/cpukit/score/src/threadclose.c
@@ -96,4 +96,6 @@ void _Thread_Close(
 
   _Workspace_Free( the_thread->extensions );
   the_thread->extensions = NULL;
+
+  _Workspace_Free( the_thread->Start.tls_area );
 }
diff --git a/cpukit/score/src/threadinitialize.c b/cpukit/score/src/threadinitialize.c
index 34198ca..6345ae7 100644
--- a/cpukit/score/src/threadinitialize.c
+++ b/cpukit/score/src/threadinitialize.c
@@ -20,6 +20,7 @@
 #include <rtems/score/threadimpl.h>
 #include <rtems/score/schedulerimpl.h>
 #include <rtems/score/stackimpl.h>
+#include <rtems/score/tls.h>
 #include <rtems/score/userextimpl.h>
 #include <rtems/score/watchdogimpl.h>
 #include <rtems/score/wkspace.h>
@@ -48,6 +49,7 @@ bool _Thread_Initialize(
   void                *extensions_area;
   bool                 extension_status;
   int                  i;
+  uintptr_t            tls_size = (uintptr_t) _TLS_Size;
 
 #if defined( RTEMS_SMP )
   if ( rtems_configuration_is_smp_enabled() && !is_preemptible ) {
@@ -70,6 +72,7 @@ bool _Thread_Initialize(
 
   extensions_area = NULL;
   the_thread->libc_reent = NULL;
+  the_thread->Start.tls_area = NULL;
 
   #if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
     fp_area = NULL;
@@ -105,6 +108,19 @@ bool _Thread_Initialize(
      actual_stack_size
   );
 
+  /* Thread-local storage (TLS) area allocation */
+  if ( tls_size > 0 ) {
+    uintptr_t tls_alignment = (uintptr_t) _TLS_Alignment;
+    uintptr_t tls_alloc = _TLS_Get_allocation_size( tls_size, tls_alignment );
+
+    the_thread->Start.tls_area =
+      _Workspace_Allocate_aligned( tls_alloc, tls_alignment );
+
+    if ( the_thread->Start.tls_area == NULL ) {
+      goto failed;
+    }
+  }
+
   /*
    *  Allocate the floating point area for this thread
    */
@@ -223,6 +239,8 @@ bool _Thread_Initialize(
     return true;
 
 failed:
+  _Workspace_Free( the_thread->Start.tls_area );
+
   _Workspace_Free( the_thread->libc_reent );
 
   for ( i=0 ; i <= THREAD_API_LAST ; i++ )
diff --git a/cpukit/score/src/threadloadenv.c b/cpukit/score/src/threadloadenv.c
index eb3cc63..49821b0 100644
--- a/cpukit/score/src/threadloadenv.c
+++ b/cpukit/score/src/threadloadenv.c
@@ -58,7 +58,8 @@ void _Thread_Load_environment(
     the_thread->Start.Initial_stack.size,
     isr_level,
     _Thread_Handler,
-    is_fp
+    is_fp,
+    the_thread->Start.tls_area
   );
 
 }
diff --git a/cpukit/score/src/wkspace.c b/cpukit/score/src/wkspace.c
index 6580686..f437b88 100644
--- a/cpukit/score/src/wkspace.c
+++ b/cpukit/score/src/wkspace.c
@@ -21,6 +21,8 @@
 #include <rtems/score/wkspace.h>
 #include <rtems/score/heapimpl.h>
 #include <rtems/score/interr.h>
+#include <rtems/score/threadimpl.h>
+#include <rtems/score/tls.h>
 #include <rtems/config.h>
 
 #include <string.h>  /* for memset */
@@ -30,6 +32,25 @@
   #include <rtems/bspIo.h>
 #endif
 
+static uint32_t _Get_maximum_thread_count(void)
+{
+  uint32_t thread_count = 0;
+
+  thread_count += _Thread_Get_maximum_internal_threads();
+
+  thread_count += rtems_resource_maximum_per_allocation(
+    Configuration_RTEMS_API.maximum_tasks
+  );
+
+#if defined(RTEMS_POSIX_API)
+  thread_count += rtems_resource_maximum_per_allocation(
+    Configuration_POSIX_API.maximum_threads
+  );
+#endif
+
+  return thread_count;
+}
+
 void _Workspace_Handler_initialization(
   Heap_Area *areas,
   size_t area_count,
@@ -42,8 +63,17 @@ void _Workspace_Handler_initialization(
   bool unified = rtems_configuration_get_unified_work_area();
   uintptr_t page_size = CPU_HEAP_ALIGNMENT;
   uintptr_t overhead = _Heap_Area_overhead( page_size );
+  uintptr_t tls_size = (uintptr_t) _TLS_Size;
   size_t i;
 
+  if ( tls_size > 0 ) {
+    uintptr_t tls_alignment = (uintptr_t) _TLS_Alignment;
+    uintptr_t tls_alloc = _TLS_Get_allocation_size( tls_size, tls_alignment );
+
+    remaining += _Get_maximum_thread_count()
+      * _Heap_Size_with_overhead( page_size, tls_alloc, tls_alignment );
+  }
+
   for (i = 0; i < area_count; ++i) {
     Heap_Area *area = &areas [i];
 
diff --git a/testsuites/sptests/Makefile.am b/testsuites/sptests/Makefile.am
index 7eab621..1eacf5c 100644
--- a/testsuites/sptests/Makefile.am
+++ b/testsuites/sptests/Makefile.am
@@ -30,6 +30,10 @@ SUBDIRS = \
     spsimplesched03 spnsext01 spedfsched01 spedfsched02 spedfsched03 \
     spcbssched01 spcbssched02 spcbssched03 spqreslib sptimespec01 \
     spregion_err01 sppartition_err01
+if HAS_CPLUSPLUS
+SUBDIRS += sptls02
+endif
+SUBDIRS += sptls01
 SUBDIRS += spintrcritical20
 SUBDIRS += spintrcritical19
 SUBDIRS += spcontext01
diff --git a/testsuites/sptests/configure.ac b/testsuites/sptests/configure.ac
index 6b14543..623e784 100644
--- a/testsuites/sptests/configure.ac
+++ b/testsuites/sptests/configure.ac
@@ -11,16 +11,21 @@ RTEMS_CANONICAL_TARGET_CPU
 AM_INIT_AUTOMAKE([no-define foreign 1.12.2])
 AM_MAINTAINER_MODE
 
+RTEMS_ENABLE_CXX
 RTEMS_ENV_RTEMSBSP
 RTEMS_CHECK_RTEMS_TEST_NO_PAUSE
 
 RTEMS_PROJECT_ROOT
 
 RTEMS_PROG_CC_FOR_TARGET
+RTEMS_PROG_CXX_FOR_TARGET
 
 RTEMS_CANONICALIZE_TOOLS
 
 RTEMS_CHECK_CUSTOM_BSP(RTEMS_BSP)
+RTEMS_CHECK_CXX(RTEMS_BSP)
+
+AM_CONDITIONAL([HAS_CPLUSPLUS],[test $HAS_CPLUSPLUS = "yes"])
 
 # FIXME: We should get rid of this. It's a cludge.
 AC_CHECK_SIZEOF([time_t])
@@ -31,6 +36,8 @@ AM_CONDITIONAL(HAS_CPUSET,test x"${ac_cv_header_sys_cpuset_h}" = x"yes")
 
 # Explicitly list all Makefiles here
 AC_CONFIG_FILES([Makefile
+sptls02/Makefile
+sptls01/Makefile
 spintrcritical20/Makefile
 spintrcritical19/Makefile
 spcontext01/Makefile
diff --git a/testsuites/sptests/sptls01/Makefile.am b/testsuites/sptests/sptls01/Makefile.am
new file mode 100644
index 0000000..5512b8a
--- /dev/null
+++ b/testsuites/sptests/sptls01/Makefile.am
@@ -0,0 +1,19 @@
+rtems_tests_PROGRAMS = sptls01
+sptls01_SOURCES = init.c
+
+dist_rtems_tests_DATA = sptls01.scn sptls01.doc
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP at .cfg
+include $(top_srcdir)/../automake/compile.am
+include $(top_srcdir)/../automake/leaf.am
+
+AM_CPPFLAGS += -I$(top_srcdir)/../support/include
+
+LINK_OBJS = $(sptls01_OBJECTS)
+LINK_LIBS = $(sptls01_LDLIBS)
+
+sptls01$(EXEEXT): $(sptls01_OBJECTS) $(sptls01_DEPENDENCIES)
+	@rm -f sptls01$(EXEEXT)
+	$(make-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/sptests/sptls01/init.c b/testsuites/sptests/sptls01/init.c
new file mode 100644
index 0000000..57a3434
--- /dev/null
+++ b/testsuites/sptests/sptls01/init.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include "tmacros.h"
+
+static rtems_id master_task;
+
+static __thread volatile char tls_item = 123;
+
+static void check_tls_item(int expected)
+{
+  printf("TLS item = %i\n", tls_item);
+  rtems_test_assert(tls_item == expected);
+}
+
+static void task(rtems_task_argument arg)
+{
+  rtems_status_code sc;
+
+  check_tls_item(123);
+
+	sc = rtems_event_transient_send(master_task);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	sc = rtems_task_suspend(RTEMS_SELF);
+	assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void test(void)
+{
+	rtems_id id;
+	rtems_status_code sc;
+
+	master_task = rtems_task_self();
+
+  check_tls_item(123);
+  tls_item = 5;
+
+  sc = rtems_task_create(
+		rtems_build_name('T', 'A', 'S', 'K'),
+		RTEMS_MINIMUM_PRIORITY,
+		RTEMS_MINIMUM_STACK_SIZE,
+		RTEMS_DEFAULT_MODES,
+		RTEMS_DEFAULT_ATTRIBUTES,
+		&id
+	);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	sc = rtems_task_start(id, task, 0);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	sc = rtems_task_delete(id);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+  check_tls_item(5);
+}
+
+static void Init(rtems_task_argument arg)
+{
+  puts("\n\n*** TEST SPTLS 1 ***");
+
+  test();
+
+  puts("*** END OF TEST SPTLS 1 ***");
+
+  rtems_test_exit(0);
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+
+#define CONFIGURE_MAXIMUM_TASKS 2
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/sptests/sptls01/sptls01.doc b/testsuites/sptests/sptls01/sptls01.doc
new file mode 100644
index 0000000..a6cc38b
--- /dev/null
+++ b/testsuites/sptests/sptls01/sptls01.doc
@@ -0,0 +1,11 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: sptls01
+
+directives:
+
+  - None
+
+concepts:
+
+  - Ensure that thread-local storage (TLS) works minimum alignment requirements.
diff --git a/testsuites/sptests/sptls01/sptls01.scn b/testsuites/sptests/sptls01/sptls01.scn
new file mode 100644
index 0000000..eaeb11e
--- /dev/null
+++ b/testsuites/sptests/sptls01/sptls01.scn
@@ -0,0 +1,5 @@
+*** TEST SPTLS 1 ***
+TLS item = 123
+TLS item = 123
+TLS item = 5
+*** END OF TEST SPTLS 1 ***
diff --git a/testsuites/sptests/sptls02/Makefile.am b/testsuites/sptests/sptls02/Makefile.am
new file mode 100644
index 0000000..d577b5b
--- /dev/null
+++ b/testsuites/sptests/sptls02/Makefile.am
@@ -0,0 +1,20 @@
+rtems_tests_PROGRAMS = sptls02
+sptls02_SOURCES = init.cc
+
+dist_rtems_tests_DATA = sptls02.scn sptls02.doc
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP at .cfg
+include $(top_srcdir)/../automake/compile.am
+include $(top_srcdir)/../automake/leaf.am
+
+AM_CPPFLAGS += -I$(top_srcdir)/../support/include
+AM_CXXFLAGS += -std=c++11 -ftls-model=local-exec
+
+LINK_OBJS = $(sptls02_OBJECTS)
+LINK_LIBS = $(sptls02_LDLIBS)
+
+sptls02$(EXEEXT): $(sptls02_OBJECTS) $(sptls02_DEPENDENCIES)
+	@rm -f sptls02$(EXEEXT)
+	$(make-cxx-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/sptests/sptls02/init.cc b/testsuites/sptests/sptls02/init.cc
new file mode 100644
index 0000000..b1b2e8a
--- /dev/null
+++ b/testsuites/sptests/sptls02/init.cc
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.com/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <rtems.h>
+#include <rtems/libcsupport.h>
+
+#include "tmacros.h"
+
+static thread_local long i123 = 123;
+
+alignas(256) static thread_local long a256 = 256;
+
+static thread_local long i0;
+
+alignas(512) static thread_local long a512;
+
+static void clobber()
+{
+	i123 = 0xdead0001;
+	a256 = 0xdead0002;
+	i0 = 0xdead0003;
+	a512 = 0xdead0004;
+}
+
+static long f456(bool clobber)
+{
+	static thread_local long fi456 = 456;
+
+	if (clobber) {
+		fi456 = 0xdead0003;
+	}
+
+	return fi456;
+}
+
+static long f0(bool clobber)
+{
+	static thread_local long fi0;
+
+	if (clobber) {
+		fi0 = 0xdead0004;
+	}
+
+	return fi0;
+}
+
+class C {
+	public:
+		static long c789()
+		{
+			return ci789;
+		}
+
+		static long c0()
+		{
+			return ci0;
+		}
+
+		static void clobber()
+		{
+			ci789 = 0xdead0005;
+			ci0 = 0xdead0006;
+		}
+
+	private:
+		static thread_local long ci789;
+
+		static thread_local long ci0;
+};
+
+thread_local long C::ci789 = 789;
+
+thread_local long C::ci0;
+
+class A {
+	public:
+		A(long i)
+			: ii(i), c(gc)
+		{
+			++gc;
+		}
+
+		~A()
+		{
+			--gc;
+		}
+
+		long i() const
+		{
+			return ii;
+		}
+
+		void clobber()
+		{
+			ii = ~ii;
+			c = ~c;
+		}
+
+		long counter() const
+		{
+			return c;
+		}
+
+		static long globalCounter()
+		{
+			return gc;
+		}
+
+	private:
+		static long gc;
+
+		long ii;
+
+		long c;
+};
+
+long A::gc;
+
+static volatile long mc;
+
+static thread_local A a1(mc + 1);
+static thread_local A a2(mc + 2);
+static thread_local A a3(mc + 3);
+
+static void checkTLSValues()
+{
+	assert(i123 == 123);
+	assert(a256 == 256);
+	assert((a256 & 255) == 0);
+	assert(i0 == 0);
+	assert(a512 == 0);
+	assert((a512 & 511) == 0);
+	assert(f456(false) == 456);
+	assert(f0(false) == 0);
+	assert(C::c789() == 789);
+	assert(C::c0() == 0);
+	assert(a1.i() == 1);
+	assert(a2.i() == 2);
+	assert(a3.i() == 3);
+}
+
+static rtems_id masterTask;
+
+static void task(rtems_task_argument arg)
+{
+	checkTLSValues();
+
+	const long gc = static_cast<long>(arg);
+
+	assert(A::globalCounter() == gc + 3);
+
+	assert(a1.counter() == gc + 0);
+	assert(a2.counter() == gc + 1);
+	assert(a3.counter() == gc + 2);
+
+	clobber();
+	f456(true);
+	f0(true);
+	C::clobber();
+	a1.clobber();
+	a2.clobber();
+	a3.clobber();
+
+	rtems_status_code sc = rtems_event_transient_send(masterTask);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	sc = rtems_task_suspend(RTEMS_SELF);
+	assert(sc == RTEMS_SUCCESSFUL);
+}
+
+static void testTask()
+{
+	checkTLSValues();
+
+	rtems_id id;
+	rtems_status_code sc = rtems_task_create(
+		rtems_build_name('T', 'A', 'S', 'K'),
+		RTEMS_MINIMUM_PRIORITY,
+		RTEMS_MINIMUM_STACK_SIZE,
+		RTEMS_DEFAULT_MODES,
+		RTEMS_DEFAULT_ATTRIBUTES,
+		&id
+	);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	const long gc = A::globalCounter();
+
+	sc = rtems_task_start(id, task, gc);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	sc = rtems_task_delete(id);
+	assert(sc == RTEMS_SUCCESSFUL);
+
+	assert(A::globalCounter() == gc);
+
+	checkTLSValues();
+}
+
+extern "C" void Init(rtems_task_argument arg)
+{
+	puts("*** TEST SPTLS 2 ***");
+
+	printf("A::globalCounter() = %li\n", A::globalCounter());
+
+	checkTLSValues();
+
+	printf("A::globalCounter() = %li\n", A::globalCounter());
+
+	masterTask = rtems_task_self();
+
+	testTask();
+
+	rtems_resource_snapshot snapshot;
+	rtems_resource_snapshot_take(&snapshot);
+
+	testTask();
+
+	assert(rtems_resource_snapshot_check(&snapshot));
+
+	puts("*** END OF TEST SPTLS 2 ***");
+
+	exit(0);
+}
+
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
+#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
+
+#define CONFIGURE_MAXIMUM_TASKS 2
+#define CONFIGURE_MAXIMUM_SEMAPHORES 2
+
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
+
+#define CONFIGURE_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/sptests/sptls02/sptls02.doc b/testsuites/sptests/sptls02/sptls02.doc
new file mode 100644
index 0000000..4417004
--- /dev/null
+++ b/testsuites/sptests/sptls02/sptls02.doc
@@ -0,0 +1,12 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: sptls02
+
+directives:
+
+  - None
+
+concepts:
+
+  - Ensure that thread-local storage (TLS) works alignment requirements.
+  - Ensure that thread-local storage (TLS) works objects.
diff --git a/testsuites/sptests/sptls02/sptls02.scn b/testsuites/sptests/sptls02/sptls02.scn
new file mode 100644
index 0000000..e4c92f5
--- /dev/null
+++ b/testsuites/sptests/sptls02/sptls02.scn
@@ -0,0 +1,4 @@
+*** TEST SPTLS 2 ***
+A::globalCounter() = 0
+A::globalCounter() = 3
+*** END OF TEST SPTLS 2 ***
-- 
1.7.7




More information about the devel mailing list