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