summaryrefslogtreecommitdiff
path: root/ports/cortex_m7/ghs/src/tx_ghs.c
blob: 30b8054e425de592a27be8b7f67603f2498b9c1d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
/*
 *                ThreadX C/C++ Library Support
 *
 *      Copyright 1983-2019 Green Hills Software LLC.
 *
 *  This program is the property of Green Hills Software LLC.,
 *  its contents are proprietary information and no part of it
 *  is to be disclosed to anyone except employees of Green Hills
 *  Software LLC., or as agreed in writing signed by the President
 *  of Green Hills Software LLC.
 */

#include "tx_ghs.h"
#ifndef TX_DISABLE_ERROR_CHECKING
#define TX_DISABLE_ERROR_CHECKING
#endif
#include "tx_api.h"
#include <setjmp.h>
#include <string.h>

/* Allow these routines to access the following ThreadX global variables.  */
extern ULONG      _tx_thread_created_count;
extern TX_THREAD *_tx_thread_created_ptr;
extern TX_THREAD *_tx_thread_current_ptr;

#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
/* Thread-local storage routines for Green Hills releases 5.x and above. */
/*
  Thread-Local (Per-Thread) Library Data Retrieval
  ================================================

  __ghs_ThreadLocalStorage_specifier defines all library data items
  that the Green Hills libraries allow to be allocated per-thread.

  An implementation can choose which of these data items to allocate
  for each thread. For example, an implementation may choose to
  allocate an errno value for each thread, but not the strtok_saved_pos
  pointer. The application could then use strtok_r instead of strtok for
  correct operation.

  To add per-thread library data, define one of the
  TX_THREAD_EXTENSION_* macros in tx_port.h to include the data item
  or items in each thread control block TX_THREAD.

  If C++ with exceptions is being used, the __eh_globals entry must be
  allocated for each thread. This is typically done by default using
  TX_THREAD_EXTENSION_1 in tx_port.h.

  If __ghs_GetThreadLocalStorageItem is customized to return a
  per-thread errno value, you should also:

  * Customize the System Library for your project
  * Define the preprocessor symbol USE_THREAD_LOCAL_ERRNO in
    src/libsys/ind_errn.c

  If you customize the System Library, you should remove ind_thrd.c
  from the libsys.gpj subproject.

 */

/* Provide global __eh_globals value to support C++ exception handling
   outside a thread context. This name also forces this module to be
   included in the linked program instead of the ind_thrd.o module from
   the System Library libsys.a.
 */
static void *__eh_globals;

#pragma ghs startnomisra
void *__ghs_GetThreadLocalStorageItem(int specifier)
{
    void *ptlsitem = (void *)0;
    switch (specifier) {
	case (int)__ghs_TLS_Errno:
	    /* Set ptslsitem to the address of the per-thread errno value.
	       The per-thread errno value should have the type int.

	       If returning a per-thread errno value, follow the steps
	       above.

	       This item is used by numerous library functions.
	    */
	    break;
	case (int)__ghs_TLS_SignalHandlers:
	    /* Set ptslsitem to the address of the per-thread SignalHandlers
	       array. The per-thread SignalHandlers array should have the
	       array type as in the following declaration:
	       SignalHandler SignalHandlers[_SIGMAX];
	       The SignalHandler type and _SIGMAX constant are defined in
	       ind_thrd.h.

	       This item is used by the library functions signal() and
	       raise().
	    */
	    break;
	case (int)__ghs_TLS_asctime_buff:
	    /* Set ptslsitem to the address of the per-thread asctime_buff
	       array. The per-thread asctime_buff array should have the
	       array type as in the following declaration:
	       char asctime_buff[30];

	       This item is used by the library functions asctime() and
	       ctime(). The library provides asctime_r() and ctime_r(),
	       inherently thread-safe versions of these functions.
	    */
	    break;
	case (int)__ghs_TLS_tmpnam_space:
	    /* Set ptslsitem to the address of the per-thread tmpnam_space
	       array. The per-thread tmpnam_space array should have the
	       array type as in the following declaration:
	       char tmpnam_space[L_tmpnam];
	       The constant is defined in <stdio.h>

	       This item is used by the library function tmpnam() when
	       passed NULL. The library provides tmpnam_r(), an
	       inherently thread-safe version of tmpnam().
	    */
	    break;
	case (int)__ghs_TLS_strtok_saved_pos:
	    /* Set ptslsitem to the address of the per-thread
	       strtok_saved_pos pointer. The per-thread strtok_saved_pos
	       pointer should have the type "char *".

	       This item is used by the library function strtok().
	       The library provides strtok_r(), an inherently thread-safe
	       version of strtok().
	    */
	    break;
	case (int)__ghs_TLS_gmtime_temp:
	    /* Set ptslsitem to the address of the per-thread gmtime_temp
	       value. The per-thread gmtime_temp value should have the
	       type "struct tm" defined in time.h, included by indos.h.

	       This item is used by the library functions gmtime() and
	       localtime(). The library provides gmtime_r() and
	       localtime_r(), inherently thread-safe versions of these
	       functions.
	    */
	    break;
	case (int)__ghs_TLS___eh_globals:
	    /* Set ptslsitem to the address of the per-thread __eh_globals
	       value. The per-thread __eh_globals value should have the
	       type "void *".

	       This item is used by C++ exception handling.
	    */
	    if (_tx_thread_current_ptr)
		ptlsitem = (void *)&(_tx_thread_current_ptr->tx_thread_eh_globals);
	    else
		/* Use the global __eh_globals pointer.  */
		ptlsitem = (void *)&__eh_globals;
	    break;
    }
    return ptlsitem;
}
#pragma ghs endnomisra
#else
/* Thread-local storage routines for Green Hills releases 4.x and 3.x . */

/*
 * ThreadX C and C++ thread-safe library support routines.
 *
 * This implementation merely tries to guarantee thread safety within
 * individual C library calls such as malloc() and free(), but it does
 * not attempt to solve the problems associated with the following
 * multithreaded issues:
 *
 * 1. Use of errno.  This can be made thread-safe by adding errno
 *    to TX_THREAD_PORT_EXTENSION and using that within a modified
 *    version of libsys/ind_errno.c.
 *
 * 2. Thread safety ACROSS library calls.  Certain C library calls either
 *    return pointers to statically-allocated data structures or maintain
 *    state across calls.  These include strtok(), asctime(), gmtime(),
 *    tmpnam(NULL), signal().  To make such C library routines thread-safe
 *    would require adding a ThreadLocalStorage struct to the thread control
 *    block TX_THREAD.  Since relatively few applications make use of these
 *    library routines, the implementation provided here uses a single, global
 *    ThreadLocalStorage data structure rather than greatly increasing the size
 *    of the thread control block TX_THREAD.
 *
 *    The ThreadX global variable _tx_thread_current_ptr points to the
 *    current thread's control block TX_THREAD. If a ThreadLocalStorage struct
 *    called tx_tls is placed in TX_THREAD, the function GetThreadLocalStorage
 *    should be modified to return &(_tx_thread_current_ptr->tx_tls).
 */

static ThreadLocalStorage GlobalTLS;

ThreadLocalStorage *GetThreadLocalStorage()
{
    return &GlobalTLS;
}
#endif

/*
 * Use a global ThreadX mutex to implement thread safety within C and C++
 * library routines.
 *
 */
TX_MUTEX __ghLockMutex;

/*
 * Acquire general lock.  Blocks until the lock becomes available.
 * Use tx_mutex_get to implement __ghsLock
 */
void __ghsLock(void)
{
    tx_mutex_get(&__ghLockMutex, TX_WAIT_FOREVER);
}

/*
 * Release general lock
 * Use tx_mutex_put to implement __ghsUnlock
 */
void __ghsUnlock(void)
{
    tx_mutex_put(&__ghLockMutex);
}

/* ThreadX Initialization function prototype.  */
void _tx_initialize_kernel_setup(void);

void __gh_lock_init(void)
{
    /* Initialize the low-level portions of ThreadX. */
    _tx_initialize_kernel_setup();

    /* Create the global thread lock mutex.  */
    tx_mutex_create(&__ghLockMutex, "__ghLockMutex", TX_NO_INHERIT);
}

/*
  Saving State Across setjmp() Calls
  ==================================

  These routines can be used to save and restore arbitrary state
  across calls to setjmp() and longjmp().
*/
int __ghs_SaveSignalContext(jmp_buf jmpbuf)
{
    return 0;
}

/* Restore arbitrary state across a longjmp() */
void __ghs_RestoreSignalContext(jmp_buf jmpbuf)
{
}

#if defined(__GHS_VERSION_NUMBER) && (__GHS_VERSION_NUMBER < 560)
/*
  C++ Exception Handling
  ======================

  These routines allow C++ exceptions to be used in multiple threads.
  The default implementation uses __ghs_GetThreadLocalStorageItem
  to return a thread-specific __eh_globals pointer.

*/

/* Must be called after __cpp_exception_init() is called to allocate
 * and initialize the per-thread exception handling structure */
void *__get_eh_globals(void)
{
#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
    return *(void **)__ghs_GetThreadLocalStorageItem(__ghs_TLS___eh_globals);
#else
    if (_tx_thread_current_ptr)

        /* Return thread-specific __eh_globals pointer.  */
        return _tx_thread_current_ptr->tx_thread_eh_globals;
    else
        /* Return the global __eh_globals pointer.  */
        return GlobalTLS.__eh_globals;
#endif
}
#endif

#if defined(__ghs) && (__GHS_VERSION_NUMBER >= 500)
#pragma weak __cpp_exception_init
extern void __cpp_exception_init(void **);
#pragma weak __cpp_exception_cleanup
extern void __cpp_exception_cleanup(void **);

/* __tx_cpp_exception_init retrieves the eh_globals field from
   thread-local storage and calls __cpp_exception_init.
 */
void __tx_cpp_exception_init(TX_THREAD *thread_ptr) {
    void **peh_globals;
    if(__cpp_exception_init) {
	if (thread_ptr)
	    peh_globals = &(thread_ptr->tx_thread_eh_globals);
	else
	    /* Use the global __eh_globals pointer.  */
	    peh_globals = &__eh_globals;
	__cpp_exception_init(peh_globals);
    }
}

/* __tx_cpp_exception_cleanup retrieves the eh_globals field from
   thread-local storage and calls __cpp_exception_cleanup.
 */
void __tx_cpp_exception_cleanup(TX_THREAD *thread_ptr) {
    void **peh_globals;
    if(__cpp_exception_cleanup) {
	if (thread_ptr)
	    peh_globals = &(thread_ptr->tx_thread_eh_globals);
	else
	    /* Use the global __eh_globals pointer.  */
	    peh_globals = &__eh_globals;
	__cpp_exception_cleanup(peh_globals);
    }
}

/* __ghs_cpp_exception_init is called from ind_crt1.o to initialize
   exceptions for the global context.
 */
void __ghs_cpp_exception_init() {
    __tx_cpp_exception_init((void *)0);
}

/* __ghs_cpp_exception_cleanup is called from ind_exit.o to clean up
   exceptions for the global context.
 */
void __ghs_cpp_exception_cleanup(TX_THREAD *thread_ptr) {
    __tx_cpp_exception_cleanup((void *)0);
}
#endif


/*
  File Locks
  ======================

  These routines can be customized to implement per-file locks to allow
  thread-safe I/O.

*/

/* Acquire lock for FILE *addr */
void __ghs_flock_file(void *addr)
{
    tx_mutex_get((TX_MUTEX *)addr, TX_WAIT_FOREVER);
}

/* Release lock for FILE *addr */
void __ghs_funlock_file(void *addr)
{
    tx_mutex_put((TX_MUTEX *)addr);
}

/* Non blocking acquire lock for FILE *addr.  May return -1 if */
/* not implemented. Returns 0 on success and nonzero otherwise. */
int __ghs_ftrylock_file(void *addr)
{
    return -1;
}

/* Calls to initialize local lock data structures before they */
/* are used. */
void __ghs_flock_create(void **addr)
{
    *addr = (void *)(&__ghLockMutex);
}
void __ghs_flock_destroy(void *addr) {}


/*
 * ThreadX Peak Stack Checking support routines.
 *
 * All of these routines are called by MULTI's ThreadX-aware debugging
 * package to determine the peak stack use for one thread or for all threads.
 *
 * These routines are included in this file in order to guarantee that they will
 * be available while debugging with MULTI.  These routines are not referenced by
 * any other part of the ThreadX system.
 *
 * _txs_thread_stack_check:         return the peak stack usage for a thread.
 *
 * _txs_thread_stack_check_2:       store the peak stack usage for all threads
 *                                  in the tx_thread_stack_size field of each thread
 *                                  control block, TX_THREAD.  This routine takes
 *                                  advantage of the redundancy within the TX_THREAD
 *                                  structure since tx_thread_stack_size can be computed
 *                                  from the tx_thread_stack_start and tx_thread_stack_end
 *                                  fields of TX_THREAD.
 *
 * _txs_thread_stack_check_2_fixup: clean up from the _txs_thread_stack_check_2
 *                                  call by computing the stack size for each
 *                                  thread and storing the result in the
 *                                  tx_thread_stack_size field of each thread control
 *                                  block TX_THREAD.
 *
 * These three routines do not support architectures such as i960 or StarCore
 * where the stack grows up instead of down.
 *
 */
#ifndef TX_DISABLE_STACK_CHECKING

ULONG _txs_thread_stack_check(TX_THREAD *thread_ptr)
{
    CHAR      *cp;                  /* Pointer inside thread's stack.  */

    /* Search through the thread's stack to find the highest address modified.  */
    for ( cp = (CHAR *)thread_ptr->tx_thread_stack_start;
        cp <= (CHAR *)thread_ptr->tx_thread_stack_end; ++cp ) {

        /* Check if this byte in the stack contains something other than TX_STACK_FILL.  */
        if (*cp != (char)TX_STACK_FILL) {

            /* Assume cp points to the locating marking the peak stack use.
               Return the number of bytes from cp up to and including the
               end of the stack.  */
            return (((ULONG)thread_ptr->tx_thread_stack_end) - (ULONG)cp + 1);
        }
    }
    return thread_ptr->tx_thread_stack_size;
}


int _txs_thread_stack_check_2(void) {
    CHAR *      cp;                  /* Pointer inside thread's stack.  */
    TX_THREAD * tp;                  /* Pointer to each thread.  */

    /* If no threads are created, return immediately.  */
    if (!_tx_thread_created_count)
        return 0;

    /* Start iterating through the threads in the system.  Assume that we always
       have at least one thread (the system timer thread) in the system.  */
    tp = _tx_thread_created_ptr;

    do {

        /* Search through the thread's stack to find the highest address modified.  */
        for ( cp = (CHAR *)tp->tx_thread_stack_start; cp <= (CHAR *)tp->tx_thread_stack_end;
        ++cp ) {

            /* Check if this byte in the stack contains something other than TX_STACK_FILL.  */
            if (*cp != (char)TX_STACK_FILL) {

                /* Assume cp points to the locating marking the peak stack use.
                   Store the number of bytes from cp up to and including the
                   end of the stack in the tx_thread_stack_size field.  */
                tp->tx_thread_stack_size = ((ULONG)tp->tx_thread_stack_end) - (ULONG)cp + 1;
                break;
            }

        }

        /* Continue with the next thread.  */
        tp = tp->tx_thread_created_next;

    /* Loop until we point to the first thread again.  */
    } while ( tp != _tx_thread_created_ptr );

    return 0;
}

int _txs_thread_stack_check_2_fixup(void) {
    TX_THREAD * tp;                  /* Pointer to each thread.  */

    /* If no threads are created, return immediately.  */
    if (!_tx_thread_created_count)
        return 0;

    /* Start iterating through the threads in the system.  Assume that we always
       have at least one thread (the system timer thread) in the system.  */
    tp = _tx_thread_created_ptr;

    do {

        /* Compute the tx_thread_stack_size field by using the tx_thread_stack_end and
           tx_thread_stack_start fields.  */
        tp->tx_thread_stack_size = (ULONG)tp->tx_thread_stack_end-(ULONG)tp->tx_thread_stack_start+1;

        /* Continue with the next thread.  */
        tp = tp->tx_thread_created_next;

    /* Loop until we point to the first thread again.  */
    } while ( tp != _tx_thread_created_ptr );

    return 0;
}

#endif /* TX_DISABLE_STACK_CHECKING  */