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