1 /**************************************************************************/
2 /*                                                                        */
3 /*       Copyright (c) Microsoft Corporation. All rights reserved.        */
4 /*                                                                        */
5 /*       This software is licensed under the Microsoft Software License   */
6 /*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
7 /*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
8 /*       and in the root directory of this software.                      */
9 /*                                                                        */
10 /**************************************************************************/
11 
12 
13 /**************************************************************************/
14 /**************************************************************************/
15 /**                                                                       */
16 /** ThreadX Component                                                     */
17 /**                                                                       */
18 /**   Initialize                                                          */
19 /**                                                                       */
20 /**************************************************************************/
21 /**************************************************************************/
22 
23 
24 #define TX_SOURCE_CODE
25 
26 
27 /* Include necessary system files.  */
28 
29 #include "tx_api.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <signal.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <sys/sysinfo.h>
36 
37 
38 /* Define various Linux objects used by the ThreadX port.  */
39 
40 pthread_mutex_t     _tx_linux_mutex;
41 sem_t               _tx_linux_semaphore;
42 sem_t               _tx_linux_semaphore_no_idle;
43 ULONG               _tx_linux_global_int_disabled_flag;
44 struct timespec     _tx_linux_time_stamp;
45 __thread int        _tx_linux_threadx_thread = 0;
46 
47 /* Define signals for linux thread. */
48 #define SUSPEND_SIG SIGUSR1
49 #define RESUME_SIG  SIGUSR2
50 
51 static sigset_t     _tx_linux_thread_wait_mask;
52 static __thread int _tx_linux_thread_suspended;
53 static sem_t        _tx_linux_thread_timer_wait;
54 static sem_t        _tx_linux_thread_other_wait;
55 
56 /* Define simulated timer interrupt.  This is done inside a thread, which is
57    how other interrupts may be defined as well.  See code below for an
58    example.  */
59 
60 pthread_t           _tx_linux_timer_id;
61 sem_t               _tx_linux_timer_semaphore;
62 sem_t               _tx_linux_isr_semaphore;
63 void               *_tx_linux_timer_interrupt(void *p);
64 
65 void    _tx_linux_thread_resume_handler(int sig);
66 void    _tx_linux_thread_suspend_handler(int sig);
67 void    _tx_linux_thread_suspend(pthread_t thread_id);
68 
69 #ifdef TX_LINUX_DEBUG_ENABLE
70 
71 extern ULONG        _tx_thread_system_state;
72 extern UINT         _tx_thread_preempt_disable;
73 extern TX_THREAD    *_tx_thread_current_ptr;
74 extern TX_THREAD    *_tx_thread_execute_ptr;
75 
76 
77 /* Define debug log in order to debug Linux issues with this port.  */
78 
79 typedef struct TX_LINUX_DEBUG_ENTRY_STRUCT
80 {
81     char                *tx_linux_debug_entry_action;
82     struct timespec     tx_linux_debug_entry_timestamp;
83     char                *tx_linux_debug_entry_file;
84     unsigned long       tx_linux_debug_entry_line;
85     pthread_mutex_t     tx_linux_debug_entry_mutex;
86     unsigned long       tx_linux_debug_entry_int_disabled_flag;
87     ULONG               tx_linux_debug_entry_system_state;
88     UINT                tx_linux_debug_entry_preempt_disable;
89     TX_THREAD           *tx_linux_debug_entry_current_thread;
90     TX_THREAD           *tx_linux_debug_entry_execute_thread;
91 } TX_LINUX_DEBUG_ENTRY;
92 
93 
94 /* Define the maximum size of the Linux debug array.  */
95 
96 #ifndef TX_LINUX_DEBUG_EVENT_SIZE
97 #define TX_LINUX_DEBUG_EVENT_SIZE       400
98 #endif
99 
100 
101 /* Define the circular array of Linux debug entries.  */
102 
103 TX_LINUX_DEBUG_ENTRY    _tx_linux_debug_entry_array[TX_LINUX_DEBUG_EVENT_SIZE];
104 
105 
106 /* Define the Linux debug index.  */
107 
108 unsigned long           _tx_linux_debug_entry_index =  0;
109 
110 
111 /* Now define the debug entry function.  */
_tx_linux_debug_entry_insert(char * action,char * file,unsigned long line)112 void    _tx_linux_debug_entry_insert(char *action, char *file, unsigned long line)
113 {
114 
115 pthread_mutex_t        temp_copy;
116 
117     /* Save the current critical section value.  */
118     temp_copy =  _tx_linux_mutex;
119 
120     /* Lock mutex.  */
121     tx_linux_mutex_lock(_tx_linux_mutex);
122 
123     /* Get the time stamp.  */
124     clock_gettime(CLOCK_REALTIME, &_tx_linux_time_stamp);
125 
126     /* Setup the debub entry.  */
127     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_action =             action;
128     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_timestamp =          _tx_linux_time_stamp;
129     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_file =               file;
130     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_line =               line;
131     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_mutex =              temp_copy;
132     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_int_disabled_flag =  _tx_linux_global_int_disabled_flag;
133     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_system_state =       _tx_thread_system_state;
134     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_preempt_disable =    _tx_thread_preempt_disable;
135     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_current_thread =     _tx_thread_current_ptr;
136     _tx_linux_debug_entry_array[_tx_linux_debug_entry_index].tx_linux_debug_entry_execute_thread =     _tx_thread_execute_ptr;
137 
138     /* Now move to the next entry.  */
139     _tx_linux_debug_entry_index++;
140 
141     /* Determine if we need to wrap the list.  */
142     if (_tx_linux_debug_entry_index >= TX_LINUX_DEBUG_EVENT_SIZE)
143     {
144 
145         /* Yes, wrap the list!  */
146         _tx_linux_debug_entry_index =  0;
147     }
148 
149     /* Unlock mutex.  */
150     tx_linux_mutex_unlock(_tx_linux_mutex);
151 }
152 
153 #endif
154 
155 
156 /* Define the ThreadX timer interrupt handler.  */
157 
158 void    _tx_timer_interrupt(void);
159 
160 
161 /* Define other external function references.  */
162 
163 VOID    _tx_initialize_low_level(VOID);
164 VOID    _tx_thread_context_save(VOID);
165 VOID    _tx_thread_context_restore(VOID);
166 
167 
168 /* Define other external variable references.  */
169 
170 extern VOID     *_tx_initialize_unused_memory;
171 
172 
173 /**************************************************************************/
174 /*                                                                        */
175 /*  FUNCTION                                               RELEASE        */
176 /*                                                                        */
177 /*    _tx_initialize_low_level                            Linux/GNU       */
178 /*                                                           6.1          */
179 /*  AUTHOR                                                                */
180 /*                                                                        */
181 /*    William E. Lamie, Microsoft Corporation                             */
182 /*                                                                        */
183 /*  DESCRIPTION                                                           */
184 /*                                                                        */
185 /*    This function is responsible for any low-level processor            */
186 /*    initialization, including setting up interrupt vectors, setting     */
187 /*    up a periodic timer interrupt source, saving the system stack       */
188 /*    pointer for use in ISR processing later, and finding the first      */
189 /*    available RAM memory address for tx_application_define.             */
190 /*                                                                        */
191 /*  INPUT                                                                 */
192 /*                                                                        */
193 /*    None                                                                */
194 /*                                                                        */
195 /*  OUTPUT                                                                */
196 /*                                                                        */
197 /*    None                                                                */
198 /*                                                                        */
199 /*  CALLS                                                                 */
200 /*                                                                        */
201 /*    sched_setaffinity                                                   */
202 /*    getpid                                                              */
203 /*    _tx_linux_thread_init                                               */
204 /*    pthread_setschedparam                                               */
205 /*    pthread_mutexattr_init                                              */
206 /*    pthread_mutex_init                                                  */
207 /*    _tx_linux_thread_suspend                                            */
208 /*    sem_init                                                            */
209 /*    pthread_create                                                      */
210 /*    printf                                                              */
211 /*                                                                        */
212 /*  CALLED BY                                                             */
213 /*                                                                        */
214 /*    _tx_initialize_kernel_enter           ThreadX entry function        */
215 /*                                                                        */
216 /*  RELEASE HISTORY                                                       */
217 /*                                                                        */
218 /*    DATE              NAME                      DESCRIPTION             */
219 /*                                                                        */
220 /*  09-30-2020     William E. Lamie         Initial Version 6.1           */
221 /*                                                                        */
222 /**************************************************************************/
_tx_initialize_low_level(VOID)223 VOID   _tx_initialize_low_level(VOID)
224 {
225 struct sched_param sp;
226 pthread_mutexattr_t attr;
227 
228 #ifdef TX_LINUX_MULTI_CORE
229 cpu_set_t mask;
230 
231     sched_getaffinity(getpid(), sizeof(mask), &mask);
232     if (CPU_COUNT(&mask) > 1)
233     {
234 
235         srand((ULONG)pthread_self());
236 
237         /* Limit this ThreadX simulation on Linux to a single core.  */
238         CPU_ZERO(&mask);
239         CPU_SET(rand() % get_nprocs(), &mask);
240         if (sched_setaffinity(getpid(), sizeof(mask), &mask) != 0)
241         {
242 
243             /* Error restricting the process to one core.  */
244             printf("ThreadX Linux error restricting the process to one core!\n");
245             while(1)
246             {
247             }
248         }
249     }
250 #endif
251 
252     /* Pickup the first available memory address.  */
253 
254     /* Save the first available memory address.  */
255     _tx_initialize_unused_memory =  malloc(TX_LINUX_MEMORY_SIZE);
256 
257     /* Init Linux thread. */
258     _tx_linux_thread_init();
259 
260     /* Set priority and schedual of main thread. */
261     sp.sched_priority = TX_LINUX_PRIORITY_SCHEDULE;
262     pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp);
263 
264     /* Create the system critical section.  This is used by the
265        scheduler thread (which is the main thread) to block all
266        other stuff out.  */
267     pthread_mutexattr_init(&attr);
268     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
269     pthread_mutex_init(&_tx_linux_mutex, &attr);
270     sem_init(&_tx_linux_semaphore, 0, 0);
271 #ifdef TX_LINUX_NO_IDLE_ENABLE
272     sem_init(&_tx_linux_semaphore_no_idle, 0, 0);
273 #endif /* TX_LINUX_NO_IDLE_ENABLE */
274 
275     /* Initialize the global interrupt disabled flag.  */
276     _tx_linux_global_int_disabled_flag =  TX_FALSE;
277 
278     /* Create semaphore for timer thread. */
279     sem_init(&_tx_linux_timer_semaphore, 0, 0);
280 
281     /* Create semaphore for ISR thread. */
282     sem_init(&_tx_linux_isr_semaphore, 0, 0);
283 
284     /* Setup periodic timer interrupt.  */
285     if(pthread_create(&_tx_linux_timer_id, NULL, _tx_linux_timer_interrupt, NULL))
286     {
287 
288         /* Error creating the timer interrupt.  */
289         printf("ThreadX Linux error creating timer interrupt thread!\n");
290         while(1)
291         {
292         }
293     }
294 
295     /* Otherwise, we have a good thread create.  Now set the priority to
296        a level lower than the system thread but higher than the application
297        threads.  */
298     sp.sched_priority = TX_LINUX_PRIORITY_ISR;
299     pthread_setschedparam(_tx_linux_timer_id, SCHED_FIFO, &sp);
300 
301     /* Done, return to caller.  */
302 }
303 
304 
305 /* This routine is called after initialization is complete in order to start
306    all interrupt threads.  Interrupt threads in addition to the timer may
307    be added to this routine as well.  */
308 
_tx_initialize_start_interrupts(void)309 void    _tx_initialize_start_interrupts(void)
310 {
311 
312     /* Kick the timer thread off to generate the ThreadX periodic interrupt
313        source.  */
314     tx_linux_sem_post(&_tx_linux_timer_semaphore);
315 }
316 
317 
318 /* Define the ThreadX system timer interrupt.  Other interrupts may be simulated
319    in a similar way.  */
320 
_tx_linux_timer_interrupt(void * p)321 void    *_tx_linux_timer_interrupt(void *p)
322 {
323 struct timespec ts;
324 long timer_periodic_nsec;
325 int err;
326 
327     (VOID)p;
328 
329     /* Calculate periodic timer. */
330     timer_periodic_nsec = 1000000000 / TX_TIMER_TICKS_PER_SECOND;
331     nice(10);
332 
333     /* Wait startup semaphore. */
334     tx_linux_sem_wait(&_tx_linux_timer_semaphore);
335 
336     while(1)
337     {
338 
339         clock_gettime(CLOCK_REALTIME, &ts);
340         ts.tv_nsec += timer_periodic_nsec;
341         if (ts.tv_nsec > 1000000000)
342         {
343             ts.tv_nsec -= 1000000000;
344             ts.tv_sec++;
345         }
346         do
347         {
348             if (sem_timedwait(&_tx_linux_timer_semaphore, &ts) == 0)
349             {
350                 break;
351             }
352             err = errno;
353         } while (err != ETIMEDOUT);
354 
355         /* Call ThreadX context save for interrupt preparation.  */
356         _tx_thread_context_save();
357 
358         /* Call trace ISR enter event insert.  */
359         _tx_trace_isr_enter_insert(0);
360 
361         /* Call the ThreadX system timer interrupt processing.  */
362         _tx_timer_interrupt();
363 
364         /* Call trace ISR exit event insert.  */
365         _tx_trace_isr_exit_insert(0);
366 
367         /* Call ThreadX context restore for interrupt completion.  */
368         _tx_thread_context_restore();
369 
370 #ifdef TX_LINUX_NO_IDLE_ENABLE
371         tx_linux_mutex_lock(_tx_linux_mutex);
372 
373         /* Make sure semaphore is 0. */
374         while(!sem_trywait(&_tx_linux_semaphore_no_idle));
375 
376         /* Wakeup the system thread by setting the system semaphore.  */
377         tx_linux_sem_post(&_tx_linux_semaphore_no_idle);
378 
379         tx_linux_mutex_unlock(_tx_linux_mutex);
380 #endif /* TX_LINUX_NO_IDLE_ENABLE */
381     }
382 }
383 
384 /* Define functions for linux thread. */
_tx_linux_thread_resume_handler(int sig)385 void    _tx_linux_thread_resume_handler(int sig)
386 {
387     (VOID)sig;
388 }
389 
_tx_linux_thread_suspend_handler(int sig)390 void    _tx_linux_thread_suspend_handler(int sig)
391 {
392     (VOID)sig;
393 
394     if(pthread_equal(pthread_self(), _tx_linux_timer_id))
395         tx_linux_sem_post_nolock(&_tx_linux_thread_timer_wait);
396     else
397         tx_linux_sem_post_nolock(&_tx_linux_thread_other_wait);
398 
399     if(_tx_linux_thread_suspended)
400         return;
401 
402     _tx_linux_thread_suspended = 1;
403     sigsuspend(&_tx_linux_thread_wait_mask);
404     _tx_linux_thread_suspended = 0;
405 }
406 
_tx_linux_thread_suspend(pthread_t thread_id)407 void    _tx_linux_thread_suspend(pthread_t thread_id)
408 {
409 
410     /* Send signal. */
411     tx_linux_mutex_lock(_tx_linux_mutex);
412     pthread_kill(thread_id, SUSPEND_SIG);
413     tx_linux_mutex_unlock(_tx_linux_mutex);
414 
415     /* Wait until signal is received. */
416     if(pthread_equal(thread_id, _tx_linux_timer_id))
417         tx_linux_sem_wait(&_tx_linux_thread_timer_wait);
418     else
419         tx_linux_sem_wait(&_tx_linux_thread_other_wait);
420 }
421 
_tx_linux_thread_resume(pthread_t thread_id)422 void    _tx_linux_thread_resume(pthread_t thread_id)
423 {
424 
425     /* Send signal. */
426     tx_linux_mutex_lock(_tx_linux_mutex);
427     pthread_kill(thread_id, RESUME_SIG);
428     tx_linux_mutex_unlock(_tx_linux_mutex);
429 }
430 
_tx_linux_thread_init()431 void    _tx_linux_thread_init()
432 {
433 struct sigaction sa;
434 
435     /* Create semaphore for linux thread. */
436     sem_init(&_tx_linux_thread_timer_wait, 0, 0);
437     sem_init(&_tx_linux_thread_other_wait, 0, 0);
438 
439     sigfillset(&_tx_linux_thread_wait_mask);
440     sigdelset(&_tx_linux_thread_wait_mask, RESUME_SIG);
441 
442     sigfillset(&sa.sa_mask);
443     sa.sa_flags = 0;
444     sa.sa_handler = _tx_linux_thread_resume_handler;
445     sigaction(RESUME_SIG, &sa, NULL);
446 
447     sa.sa_handler = _tx_linux_thread_suspend_handler;
448     sigaction(SUSPEND_SIG, &sa, NULL);
449 }
450 
451 
452