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