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