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