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