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