1 /*
2  *                ThreadX C/C++ Library Support
3  *
4  *      Copyright 1983-2019 Green Hills Software LLC.
5  *
6  *  This program is the property of Green Hills Software LLC.,
7  *  its contents are proprietary information and no part of it
8  *  is to be disclosed to anyone except employees of Green Hills
9  *  Software LLC., or as agreed in writing signed by the President
10  *  of Green Hills Software LLC.
11  */
12 
13 #include "tx_ghs.h"
14 #ifndef TX_DISABLE_ERROR_CHECKING
15 #define TX_DISABLE_ERROR_CHECKING
16 #endif
17 #include "tx_api.h"
18 #include <setjmp.h>
19 #include <string.h>
20 
21 /* Allow these routines to access the following ThreadX global variables.  */
22 extern ULONG      _tx_thread_created_count;
23 extern TX_THREAD *_tx_thread_created_ptr;
24 extern TX_THREAD *_tx_thread_current_ptr;
25 
26 #if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
27 /* Thread-local storage routines for Green Hills releases 5.x and above. */
28 /*
29   Thread-Local (Per-Thread) Library Data Retrieval
30   ================================================
31 
32   __ghs_ThreadLocalStorage_specifier defines all library data items
33   that the Green Hills libraries allow to be allocated per-thread.
34 
35   An implementation can choose which of these data items to allocate
36   for each thread. For example, an implementation may choose to
37   allocate an errno value for each thread, but not the strtok_saved_pos
38   pointer. The application could then use strtok_r instead of strtok for
39   correct operation.
40 
41   To add per-thread library data, define one of the
42   TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item
43   or items in each thread control block TX_THREAD.
44 
45   If C++ with exceptions is being used, the __eh_globals entry must be
46   allocated for each thread. This is typically done by default using
47   TX_THREAD_EXTENSION_1 in tx_port.h.
48 
49   If __ghs_GetThreadLocalStorageItem is customized to return a
50   per-thread errno value, you should also:
51 
52   * Customize the System Library for your project
53   * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in
54     src/libsys/ind_errn.c
55 
56   If you customize the System Library, you should remove ind_thrd.c
57   from the libsys.gpj subproject.
58 
59  */
60 
61 /* Provide global __eh_globals value to support C++ exception handling
62    outside a thread context. This name also forces this module to be
63    included in the linked program instead of the ind_thrd.o module from
64    the System Library libsys.a.
65  */
66 static void *__eh_globals;
67 
68 #pragma ghs startnomisra
__ghs_GetThreadLocalStorageItem(int specifier)69 void *__ghs_GetThreadLocalStorageItem(int specifier)
70 {
71     void *ptlsitem = (void *)0;
72     switch (specifier) {
73 	case (int)__ghs_TLS_Errno:
74 	    /* Set ptslsitem to the address of the per-thread errno value.
75 	       The per-thread errno value should have the type int.
76 
77 	       If returning a per-thread errno value, follow the steps
78 	       above.
79 
80 	       This item is used by numerous library functions.
81 	    */
82 	    break;
83 	case (int)__ghs_TLS_SignalHandlers:
84 	    /* Set ptslsitem to the address of the per-thread SignalHandlers
85 	       array. The per-thread SignalHandlers array should have the
86 	       array type as in the following declaration:
87 	       SignalHandler SignalHandlers[_SIGMAX];
88 	       The SignalHandler type and _SIGMAX constant are defined in
89 	       ind_thrd.h.
90 
91 	       This item is used by the library functions signal() and
92 	       raise().
93 	    */
94 	    break;
95 	case (int)__ghs_TLS_asctime_buff:
96 	    /* Set ptslsitem to the address of the per-thread asctime_buff
97 	       array. The per-thread asctime_buff array should have the
98 	       array type as in the following declaration:
99 	       char asctime_buff[30];
100 
101 	       This item is used by the library functions asctime() and
102 	       ctime(). The library provides asctime_r() and ctime_r(),
103 	       inherently thread-safe versions of these functions.
104 	    */
105 	    break;
106 	case (int)__ghs_TLS_tmpnam_space:
107 	    /* Set ptslsitem to the address of the per-thread tmpnam_space
108 	       array. The per-thread tmpnam_space array should have the
109 	       array type as in the following declaration:
110 	       char tmpnam_space[L_tmpnam];
111 	       The constant is defined in <stdio.h>
112 
113 	       This item is used by the library function tmpnam() when
114 	       passed NULL. The library provides tmpnam_r(), an
115 	       inherently thread-safe version of tmpnam().
116 	    */
117 	    break;
118 	case (int)__ghs_TLS_strtok_saved_pos:
119 	    /* Set ptslsitem to the address of the per-thread
120 	       strtok_saved_pos pointer. The per-thread strtok_saved_pos
121 	       pointer should have the type "char *".
122 
123 	       This item is used by the library function strtok().
124 	       The library provides strtok_r(), an inherently thread-safe
125 	       version of strtok().
126 	    */
127 	    break;
128 	case (int)__ghs_TLS_gmtime_temp:
129 	    /* Set ptslsitem to the address of the per-thread gmtime_temp
130 	       value. The per-thread gmtime_temp value should have the
131 	       type "struct tm" defined in time.h, included by indos.h.
132 
133 	       This item is used by the library functions gmtime() and
134 	       localtime(). The library provides gmtime_r() and
135 	       localtime_r(), inherently thread-safe versions of these
136 	       functions.
137 	    */
138 	    break;
139 	case (int)__ghs_TLS___eh_globals:
140 	    /* Set ptslsitem to the address of the per-thread __eh_globals
141 	       value. The per-thread __eh_globals value should have the
142 	       type "void *".
143 
144 	       This item is used by C++ exception handling.
145 	    */
146 	    if (_tx_thread_current_ptr)
147 		ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals);
148 	    else
149 		/* Use the global __eh_globals pointer.  */
150 		ptlsitem = (void *)&__eh_globals;
151 	    break;
152     }
153     return ptlsitem;
154 }
155 #pragma ghs endnomisra
156 #else
157 /* Thread-local storage routines for Green Hills releases 4.x and 3.x . */
158 
159 /*
160  * ThreadX C and C++ thread-safe library support routines.
161  *
162  * This implementation merely tries to guarantee thread safety within
163  * individual C library calls such as malloc() and free(), but it does
164  * not attempt to solve the problems associated with the following
165  * multithreaded issues:
166  *
167  * 1. Use of errno.  This can be made thread-safe by adding errno
168  *    to TX_THREAD_PORT_EXTENSION and using that within a modified
169  *    version of libsys/ind_errno.c.
170  *
171  * 2. Thread safety ACROSS library calls.  Certain C library calls either
172  *    return pointers to statically-allocated data structures or maintain
173  *    state across calls.  These include strtok(), asctime(), gmtime(),
174  *    tmpnam(NULL), signal().  To make such C library routines thread-safe
175  *    would require adding a ThreadLocalStorage struct to the thread control
176  *    block TX_THREAD.  Since relatively few applications make use of these
177  *    library routines, the implementation provided here uses a single, global
178  *    ThreadLocalStorage data structure rather than greatly increasing the size
179  *    of the thread control block TX_THREAD.
180  *
181  *    The ThreadX global variable _tx_thread_current_ptr points to the
182  *    current thread's control block TX_THREAD. If a ThreadLocalStorage struct
183  *    called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage
184  *    should be modified to return &(_tx_thread_current_ptr->tx_tls).
185  */
186 
187 static ThreadLocalStorage GlobalTLS;
188 
GetThreadLocalStorage()189 ThreadLocalStorage *GetThreadLocalStorage()
190 {
191     return &GlobalTLS;
192 }
193 #endif
194 
195 /*
196  * Use a global ThreadX mutex to implement thread safety within C and C++
197  * library routines.
198  *
199  */
200 TX_MUTEX __ghLockMutex;
201 
202 /*
203  * Acquire general lock.  Blocks until the lock becomes available.
204  * Use tx_mutex_get to implement __ghsLock
205  */
__ghsLock(void)206 void __ghsLock(void)
207 {
208     tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER);
209 }
210 
211 /*
212  * Release general lock
213  * Use tx_mutex_put to implement __ghsUnlock
214  */
__ghsUnlock(void)215 void __ghsUnlock(void)
216 {
217     tx_mutex_put(&__ghLockMutex);
218 }
219 
220 /* ThreadX Initialization function prototype.  */
221 void _tx_initialize_kernel_setup(void);
222 
__gh_lock_init(void)223 void __gh_lock_init(void)
224 {
225     /* Initialize the low-level portions of ThreadX. */
226     _tx_initialize_kernel_setup();
227 
228     /* Create the global thread lock mutex.  */
229     tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT);
230 }
231 
232 /*
233   Saving State Across setjmp() Calls
234   ==================================
235 
236   These routines can be used to save and restore arbitrary state
237   across calls to setjmp() and longjmp().
238 */
__ghs_SaveSignalContext(jmp_buf jmpbuf)239 int __ghs_SaveSignalContext(jmp_buf jmpbuf)
240 {
241     return 0;
242 }
243 
244 /* Restore arbitrary state across a longjmp() */
__ghs_RestoreSignalContext(jmp_buf jmpbuf)245 void __ghs_RestoreSignalContext(jmp_buf jmpbuf)
246 {
247 }
248 
249 #if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560)
250 /*
251   C++ Exception Handling
252   ======================
253 
254   These routines allow C++ exceptions to be used in multiple threads.
255   The default implementation uses __ghs_GetThreadLocalStorageItem
256   to return a thread-specific __eh_globals pointer.
257 
258 */
259 
260 /* Must be called after __cpp_exception_init() is called to allocate
261  * and initialize the per-thread exception handling structure */
__get_eh_globals(void)262 void *__get_eh_globals(void)
263 {
264 #if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
265     return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals);
266 #else
267     if (_tx_thread_current_ptr)
268 
269         /* Return thread-specific __eh_globals pointer.  */
270         return _tx_thread_current_ptr->tx_thread_eh_globals;
271     else
272         /* Return the global __eh_globals pointer.  */
273         return GlobalTLS.__eh_globals;
274 #endif
275 }
276 #endif
277 
278 #if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
279 #pragma weak __cpp_exception_init
280 extern void __cpp_exception_init(void **);
281 #pragma weak __cpp_exception_cleanup
282 extern void __cpp_exception_cleanup(void **);
283 
284 /* __tx_cpp_exception_init retrieves the eh_globals field from
285    thread-local storage and calls __cpp_exception_init.
286  */
__tx_cpp_exception_init(TX_THREAD * thread_ptr)287 void __tx_cpp_exception_init(TX_THREAD *thread_ptr) {
288     void **peh_globals;
289     if(__cpp_exception_init) {
290 	if (thread_ptr)
291 	    peh_globals = &(thread_ptr->tx_thread_eh_globals);
292 	else
293 	    /* Use the global __eh_globals pointer.  */
294 	    peh_globals = &__eh_globals;
295 	__cpp_exception_init(peh_globals);
296     }
297 }
298 
299 /* __tx_cpp_exception_cleanup retrieves the eh_globals field from
300    thread-local storage and calls __cpp_exception_cleanup.
301  */
__tx_cpp_exception_cleanup(TX_THREAD * thread_ptr)302 void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) {
303     void **peh_globals;
304     if(__cpp_exception_cleanup) {
305 	if (thread_ptr)
306 	    peh_globals = &(thread_ptr->tx_thread_eh_globals);
307 	else
308 	    /* Use the global __eh_globals pointer.  */
309 	    peh_globals = &__eh_globals;
310 	__cpp_exception_cleanup(peh_globals);
311     }
312 }
313 
314 /* __ghs_cpp_exception_init is called from ind_crt1.o to initialize
315    exceptions for the global context.
316  */
__ghs_cpp_exception_init()317 void __ghs_cpp_exception_init() {
318     __tx_cpp_exception_init((void *)0);
319 }
320 
321 /* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up
322    exceptions for the global context.
323  */
__ghs_cpp_exception_cleanup(TX_THREAD * thread_ptr)324 void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) {
325     __tx_cpp_exception_cleanup((void *)0);
326 }
327 #endif
328 
329 
330 /*
331   File Locks
332   ======================
333 
334   These routines can be customized to implement per-file locks to allow
335   thread-safe I/O.
336 
337 */
338 
339 /* Acquire lock for FILE *addr */
__ghs_flock_file(void * addr)340 void __ghs_flock_file(void *addr)
341 {
342     tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER);
343 }
344 
345 /* Release lock for FILE *addr */
__ghs_funlock_file(void * addr)346 void __ghs_funlock_file(void *addr)
347 {
348     tx_mutex_put((TX_MUTEX *)addr);
349 }
350 
351 /* Non blocking acquire lock for FILE *addr.  May return -1 if */
352 /* not implemented. Returns 0 on success and nonzero otherwise. */
__ghs_ftrylock_file(void * addr)353 int __ghs_ftrylock_file(void *addr)
354 {
355     return -1;
356 }
357 
358 /* Calls to initialize local lock data structures before they */
359 /* are used. */
__ghs_flock_create(void ** addr)360 void __ghs_flock_create(void **addr)
361 {
362     *addr = (void *)(&__ghLockMutex);
363 }
__ghs_flock_destroy(void * addr)364 void __ghs_flock_destroy(void *addr) {}
365 
366 
367 /*
368  * ThreadX Peak Stack Checking support routines.
369  *
370  * All of these routines are called by MULTI's ThreadX-aware debugging
371  * package to determine the peak stack use for one thread or for all threads.
372  *
373  * These routines are included in this file in order to guarantee that they will
374  * be available while debugging with MULTI.  These routines are not referenced by
375  * any other part of the ThreadX system.
376  *
377  * _txs_thread_stack_check:         return the peak stack usage for a thread.
378  *
379  * _txs_thread_stack_check_2:       store the peak stack usage for all threads
380  *                                  in the tx_thread_stack_size field of each thread
381  *                                  control block, TX_THREAD.  This routine takes
382  *                                  advantage of the redundancy within the TX_THREAD
383  *                                  structure since tx_thread_stack_size can be computed
384  *                                  from the tx_thread_stack_start and tx_thread_stack_end
385  *                                  fields of TX_THREAD.
386  *
387  * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2
388  *                                  call by computing the stack size for each
389  *                                  thread and storing the result in the
390  *                                  tx_thread_stack_size field of each thread control
391  *                                  block TX_THREAD.
392  *
393  * These three routines do not support architectures such as i960 or StarCore
394  * where the stack grows up instead of down.
395  *
396  */
397 #ifndef TX_DISABLE_STACK_CHECKING
398 
_txs_thread_stack_check(TX_THREAD * thread_ptr)399 ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr)
400 {
401     CHAR      *cp;                  /* Pointer inside thread's stack.  */
402 
403     /* Search through the thread's stack to find the highest address modified.  */
404     for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start;
405         cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) {
406 
407         /* Check if this byte in the stack contains something other than TX_STACK_FILL.  */
408         if (*cp != (char)TX_STACK_FILL) {
409 
410             /* Assume cp points to the locating marking the peak stack use.
411                Return the number of bytes from cp up to and including the
412                end of the stack.  */
413             return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1);
414         }
415     }
416     return thread_ptr->tx_thread_stack_size;
417 }
418 
419 
_txs_thread_stack_check_2(void)420 int _txs_thread_stack_check_2(void) {
421     CHAR *      cp;                  /* Pointer inside thread's stack.  */
422     TX_THREAD * tp;                  /* Pointer to each thread.  */
423 
424     /* If no threads are created, return immediately.  */
425     if (!_tx_thread_created_count)
426         return 0;
427 
428     /* Start iterating through the threads in the system.  Assume that we always
429        have at least one thread (the system timer thread) in the system.  */
430     tp = _tx_thread_created_ptr;
431 
432     do {
433 
434         /* Search through the thread's stack to find the highest address modified.  */
435         for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end;
436         ++cp ) {
437 
438             /* Check if this byte in the stack contains something other than TX_STACK_FILL.  */
439             if (*cp != (char)TX_STACK_FILL) {
440 
441                 /* Assume cp points to the locating marking the peak stack use.
442                    Store the number of bytes from cp up to and including the
443                    end of the stack in the tx_thread_stack_size field.  */
444                 tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1;
445                 break;
446             }
447 
448         }
449 
450         /* Continue with the next thread.  */
451         tp = tp->tx_thread_created_next;
452 
453     /* Loop until we point to the first thread again.  */
454     } while ( tp != _tx_thread_created_ptr );
455 
456     return 0;
457 }
458 
_txs_thread_stack_check_2_fixup(void)459 int _txs_thread_stack_check_2_fixup(void) {
460     TX_THREAD * tp;                  /* Pointer to each thread.  */
461 
462     /* If no threads are created, return immediately.  */
463     if (!_tx_thread_created_count)
464         return 0;
465 
466     /* Start iterating through the threads in the system.  Assume that we always
467        have at least one thread (the system timer thread) in the system.  */
468     tp = _tx_thread_created_ptr;
469 
470     do {
471 
472         /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and
473            tx_thread_stack_start fields.  */
474         tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1;
475 
476         /* Continue with the next thread.  */
477         tp = tp->tx_thread_created_next;
478 
479     /* Loop until we point to the first thread again.  */
480     } while ( tp != _tx_thread_created_ptr );
481 
482     return 0;
483 }
484 
485 #endif /* TX_DISABLE_STACK_CHECKING  */
486