]> rtime.felk.cvut.cz Git - arc.git/blob - system/kernel/task.c
8ec8f822612a00f91e17075a110aa24372af4d6e
[arc.git] / system / kernel / task.c
1 /* -------------------------------- Arctic Core ------------------------------
2  * Arctic Core - the open source AUTOSAR platform http://arccore.com
3  *
4  * Copyright (C) 2009  ArcCore AB <contact@arccore.com>
5  *
6  * This source code is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 as published by the
8  * Free Software Foundation; See <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * for more details.
14  * -------------------------------- Arctic Core ------------------------------*/
15
16 #include <stdlib.h>\r
17 #include "Os.h"
18 #include "internal.h"
19 #include "arc.h"
20 #include "arch.h"
21
22 /** @req OS067 */
23
24 _Bool os_pcb_pid_valid( OsPcbType *restrict pcb ) {
25         return ( pcb->pid > Oil_GetTaskCnt() ) ? 0 : 1;
26 }
27 /**
28  * Start an extended task.
29  * Tasks done:
30  * - Grab the internal resource for the process
31  * - Set it running state.
32  * - Start to execute the process
33  *
34  */
35 void Os_TaskStartExtended( void ) {
36         OsPcbType *pcb;
37
38         PRETASKHOOK();
39
40         pcb = Os_TaskGetCurrent();
41         Os_ResourceGetInternal();
42         Os_TaskMakeRunning(pcb);
43
44         Os_ArchFirstCall();
45
46         /** @req OS070 */
47         Os_ResourceCheckAndRelease(pcb);
48
49         /** @req OS239 */
50         Irq_Disable();
51         if( Os_IrqAnyDisabled() ) {
52                 Os_IrqClearAll();
53         }
54
55 #warning Dont I have to check this at terminate task also?
56
57         /** @req OS069 */
58         ERRORHOOK(E_OS_MISSINGEND);
59
60         /** @req OS052 */
61         TerminateTask();
62 }
63
64 /**
65  * Start an basic task.
66  * See extended task.
67  */
68
69 void Os_TaskStartBasic( void ) {
70         OsPcbType *pcb;
71
72         PRETASKHOOK();
73
74         pcb = Os_TaskGetCurrent();
75         Os_ResourceGetInternal();
76         Os_TaskMakeRunning(pcb);
77         Os_ArchFirstCall();
78
79         /** @req OS070 */
80         Os_ResourceCheckAndRelease(pcb);
81
82         /** @req OS239 */
83         Irq_Disable();
84         if( Os_IrqAnyDisabled() ) {
85                 Os_IrqClearAll();
86         }
87
88         /** @req OS069 */
89         ERRORHOOK(E_OS_MISSINGEND);
90
91         /** @req OS052 */
92         TerminateTask();
93 }
94
95
96 static void Os_StackSetup( OsPcbType *pcbPtr ) {
97         uint8_t *bottom;
98
99         /* Find bottom of the stack so that we can place the
100          * context there.
101          *
102          * stack bottom = high address. stack top = low address
103          */
104         bottom = (uint8_t *)pcbPtr->stack.top + pcbPtr->stack.size;
105         pcbPtr->stack.curr = bottom;
106         // TODO: alignments here..
107         // TODO :use function os_arch_get_call_size() ??
108
109         // Make some space for back-chain.
110         bottom -= 16;
111         // Set the current stack so that it points to the context
112         pcbPtr->stack.curr = bottom - Os_ArchGetScSize();
113
114         Os_StackSetEndmark(pcbPtr);
115 }
116
117 /**
118  * Fill the stack with a predefined pattern
119  *
120  * @param pcbPtr Pointer to the pcb to fill with pattern
121  */
122 static void Os_StackFill(OsPcbType *pcbPtr) {
123         uint8_t *p = pcbPtr->stack.curr;
124
125         assert(pcbPtr->stack.curr > pcbPtr->stack.top);
126
127         while (p > (uint8_t *) pcbPtr->stack.top) {
128                 --p;
129                 *p = STACK_PATTERN;
130         }
131 }
132
133 #if 0
134 /**
135  *
136  * @param pcbPtr
137  */
138 static void Os_TaskSetEntry(OsPcbType *pcbPtr ) {
139
140 }
141 #endif
142
143
144 /**
145  * Setup the context for a pcb. The context differs for different arch's
146  * so we call the arch dependent functions also.
147  * The context at setup is always a small context.
148  *
149  * @param pcb Ptr to the pcb to setup context for.
150  */
151 void Os_ContextInit( OsPcbType *pcb ) {
152
153         if( pcb->autostart ) {
154                 Os_TaskMakeReady(pcb);
155         } else {
156                 pcb->state = ST_SUSPENDED;
157         }
158
159         Os_StackSetup(pcb);
160         Os_StackFill(pcb);
161         OsArch_SetTaskEntry(pcb);
162
163         Os_ArchSetupContext(pcb);
164 }
165
166 /**
167  *
168  * @param pcb
169  */
170 void Os_ContextReInit( OsPcbType *pcbPtr ) {
171         Os_StackSetup(pcbPtr);
172 }
173
174 /**
175  * Search for a specific task in the pcb list.
176  *
177  * @param tid The task id to search for
178  * @return Ptr to the found pcb or NULL
179  */
180 OsPcbType *os_find_task( TaskType tid ) {
181         OsPcbType *i_pcb;
182
183         /* TODO: Implement this as an array */
184         TAILQ_FOREACH(i_pcb,& os_sys.pcb_head,pcb_list) {
185                 if(i_pcb->pid == tid ) {
186                         return i_pcb;
187                 }
188         }
189         assert(0);
190         return NULL;
191 }
192
193 /**
194  * Adds a pcb to the list of pcb's
195  * @param pcb
196  */
197 TaskType Os_AddTask( OsPcbType *pcb ) {
198         long msr;
199
200         Irq_Save(msr);  // Save irq status and disable interrupts
201
202         pcb->pid = os_sys.task_cnt;
203         // Add to list of PCB's
204         TAILQ_INSERT_TAIL(& os_sys.pcb_head,pcb,pcb_list);
205         os_sys.task_cnt++;
206
207         Irq_Restore(msr);  // Restore interrupts
208         return pcb->pid;
209 }
210
211
212 #define PRIO_ILLEGAL    -100
213 // TODO: we can't have O(n) search here.. hash on prio instead
214
215 /**
216  * Find the top priority task. Even the running task is included.
217  *
218  * @return
219  */
220
221 OsPcbType *Os_TaskGetTop( void ){
222         OsPcbType *i_pcb;
223         OsPcbType *top_prio_pcb = NULL;
224         OsPriorityType top_prio = PRIO_ILLEGAL;
225
226         os_isr_printf(D_TASK,"os_find_top_prio_proc\n");
227
228         TAILQ_FOREACH(i_pcb,& os_sys.ready_head,ready_list) {
229                 // all ready task are canidates
230                 if( i_pcb->state & (ST_READY|ST_RUNNING)) {
231                         if( top_prio != PRIO_ILLEGAL ) {
232                                 if( i_pcb->prio > top_prio ) {
233                                         top_prio = i_pcb->prio;
234                                         top_prio_pcb = i_pcb;
235                                 }
236                         } else {
237                                 top_prio = i_pcb->prio;
238                                 top_prio_pcb = i_pcb;
239                         }
240                 } else {
241                         assert(0);
242                 }
243         }
244
245         assert(top_prio_pcb!=NULL);
246
247         os_isr_printf(D_TASK,"Found %s\n",top_prio_pcb->name);
248
249         return top_prio_pcb;
250 }
251
252
253 #define USE_DEBUG
254 #include "Trace.h"
255
256 // we come here from
257 // - WaitEvent()
258 //   old_pcb -> WAITING
259 //   new_pcb -> READY(RUNNING)
260 // - Schedule(),
261 //   old_pcb -> READY
262 //   new_pcb -> READY/RUNNING
263
264 /*
265  * two strategies
266  * 1. When running ->
267  *    - remove from ready queue
268  *    - set state == ST_RUNNING
269  *
270  * 2. When running ->
271  *    * leave in ready queue
272  *    * set state == ST_RUNNING
273  *    - ready queue and ST_READY not the same
274  *    + No need to remove the running process from ready queue
275  */
276
277 OsPcbType *Os_FindTopPrioTask( void ) {
278
279
280         return NULL;
281 }
282
283 /**
284  * Tries to Dispatch.
285  *
286  * Used by:
287  *   ActivateTask()
288  *   WaitEvent()
289  *   TerminateTask()
290  *
291  * @param force Force a re-scheduling
292  *
293  */
294 void Os_Dispatch( _Bool force ) {
295         OsPcbType *pcbPtr;
296         OsPcbType *currPcbPtr;
297
298         assert(os_sys.int_nest_cnt == 0);
299         assert(os_sys.scheduler_lock == 0 );
300
301         pcbPtr = Os_TaskGetTop();
302         currPcbPtr = Os_TaskGetCurrent();
303         /* Swap if we found any process or are forced (multiple activations)*/
304         if( pcbPtr != currPcbPtr ) {
305                 /* Add us to the ready list */
306                 if( currPcbPtr->state & ST_RUNNING ) {
307                         Os_TaskRunningToReady(currPcbPtr);
308                 }
309
310                 /*
311                  * Swap context
312                  */
313                 /** @req OS052 */
314                 POSTTASKHOOK();
315                 assert(pcbPtr!=NULL);
316
317                 Os_ResourceReleaseInternal();
318
319 #if (OS_STACK_MONITORING == 1)
320                 if( !Os_StackIsEndmarkOk(currPcbPtr) ) {
321 #if (  OS_SC1 == 1) || (  OS_SC2 == 1)
322                         /** @req OS068 */
323                         ShutdownOS(E_OS_STACKFAULT);
324 #else
325 #error SC3 or SC4 not supported. Protection hook should be called here
326 #endif
327                 }
328 #endif
329
330 #if 0
331                 // Make a simple stack check for prio procs...
332                 // See OS068, Autosar SWS
333                 {
334                         uint32_t stackp = (uint32_t)Os_ArchGetStackPtr();
335                         uint32_t smallc_size = Os_ArchGetScSize();
336
337                         // enough size to place a small context on the stack
338                         // top( low address ) + small context > current stackpointer
339                         if( (uint32_t)(currPcbPtr->stack.top + smallc_size) > stackp ) {
340                                 ShutdownOS(E_OS_STACKFAULT);
341                         }
342                 }
343 #endif
344
345                 /* Cases:
346                  * 1. Re-Activate the same basic task again -> Os_ArchSwapContextToW()
347                  * 2. Swap out a terminated task -> Os_ArchSwapContextToW()
348                  * 3. Normal swap -> Os_ArchSwapContext()
349                  */
350
351 #if 0
352                 /* Force is ONLY used from TerminateTask() */
353                 if( force ) {
354                         Os_StackSetup(pcbPtr);
355                         OsArch_SetTaskEntry(pcbPtr);
356                         // TODO: This really need to be done
357                         Os_ArchSetupContext(pcbPtr);
358                         // Os_ArchSetSpAndCall(pcbPtr->stack.curr,Os_TaskStartBasic);
359                 }
360 #endif
361
362                 Os_ArchSwapContext(currPcbPtr,pcbPtr);
363
364                 pcbPtr = Os_TaskGetCurrent();
365                 Os_TaskMakeRunning(pcbPtr);
366
367                 Os_ResourceGetInternal();
368
369                 PRETASKHOOK();
370
371         } else {
372                 Os_StackSetup(pcbPtr);
373                 Os_ArchSetSpAndCall(pcbPtr->stack.curr,Os_TaskStartBasic);
374 #if 0
375                 /* We haven't removed ourselves from the ready list? */
376                 assert(currPcbPtr->state != ST_WAITING);
377                 /* We have terminated and found us in the ready list? */
378                 assert(currPcbPtr->state != ST_SUSPENDED);
379 #endif
380         }
381 }
382
383 // We come here from
384 // - os_init
385
386 /**
387  * Called when a task is to be run for the first time.
388  */
389 void Os_TaskSwapContextTo(OsPcbType *old_pcb, OsPcbType *new_pcb ) {
390
391
392         Os_ArchSwapContextTo(old_pcb,new_pcb);
393         /* TODO: When do we return here ?? */
394 }
395
396
397 void Os_Arc_GetStackInfo( TaskType task, StackInfoType *s) {
398         OsPcbType *pcb  = os_get_pcb(task);
399
400         s->curr         = Os_ArchGetStackPtr();
401         s->top          = pcb->stack.top;
402         s->at_swap      = pcb->stack.curr;
403         s->size         = pcb->stack.size;
404         s->usage        = (void *)Os_StackGetUsage(pcb);
405 }
406
407
408 #if 0
409 OsPcbType *os_find_higher_priority_task( OsPriorityType prio ) {
410         OsPcbType *i_pcb;
411         OsPcbType *h_prio_pcb = NULL;
412         OsPriorityType t_prio = prio;
413
414         TAILQ_FOREACH(i_pcb,& os_sys.ready_head,ready_list) {
415                 if( i_pcb->prio > t_prio ) {
416                         t_prio = i_pcb->prio;
417                         h_prio_pcb = i_pcb;
418                 }
419         }
420         return h_prio_pcb;
421 }
422 #endif
423
424 \r
425 StatusType GetTaskState(TaskType TaskId, TaskStateRefType State) {\r
426         state_t curr_state = os_pcb_get_state(os_get_pcb(TaskId));\r
427         StatusType rv = E_OK;\r
428 \r
429         // TODO: Lazy impl. for now */\r
430         switch(curr_state) {\r
431         case ST_RUNNING:        *State = TASK_STATE_RUNNING;    break;\r
432         case ST_WAITING:        *State = TASK_STATE_WAITING;    break;\r
433         case ST_SUSPENDED:      *State = TASK_STATE_SUSPENDED;  break;\r
434         case ST_READY:          *State = TASK_STATE_READY;      break;\r
435         }\r
436 \r
437         // Prevent label warning. Remove when proper error handling is implemented.\r
438         if (0) goto err;\r
439 \r
440         OS_STD_END_2(OSServiceId_GetTaskState,TaskId, State);\r
441 }\r
442 \r
443 StatusType GetTaskID( TaskRefType task_id ) {\r
444         *task_id = os_sys.curr_pcb->pid;\r
445         return E_OK;\r
446 }\r
447
448 ISRType GetISRID( void ) {
449
450         /** @req OS264 */
451         if(os_sys.int_nest_cnt == 0 ) {
452                 return INVALID_ISR;
453         }
454
455         /** @req OS263 */
456         return (ISRType)Os_TaskGetCurrent()->pid;
457 }
458
459 #define TASK_CHECK_ID(x)                                \
460         if( (x) > Oil_GetTaskCnt()) { \
461                 rv = E_OS_ID;                                   \
462                 goto err;                                               \
463         }
464
465 \r
466 /**\r
467  * The task <TaskID> is transferred from the suspended state into\r
468  * the  ready state. The operating system ensures that the task\r
469  * code is being executed from the first statement.\r
470  *\r
471  * The service may be called from interrupt  level and from task\r
472  * level (see Figure 12-1).\r
473  * Rescheduling after the call to  ActivateTask depends on the\r
474  * place it is called from (ISR, non preemptable task, preemptable\r
475  * task).\r
476  *\r
477  * If E_OS_LIMIT is returned the activation is ignored.\r
478  * When an extended task is transferred from suspended state\r
479  * into ready state all its events are cleared.\r
480  *
481  * Note!
482  * ActivateTask will not immediately change the state of the task
483  * in case of multiple activation requests. If the task is not
484  * suspended, the activation will only be recorded and performed later.
485  *
486  * @param pid
487  * @return
488  */\r
489 \r
490 StatusType ActivateTask( TaskType TaskID ) {\r
491         long msr;\r
492         OsPcbType *pcb = os_get_pcb(TaskID);\r
493         StatusType rv = E_OK;\r
494 \r
495         os_isr_printf(D_TASK,"ActivateTask %s\n",pcb->name);\r
496
497 #if (OS_STATUS_EXTENDED == STD_ON )
498         TASK_CHECK_ID(TaskID);
499
500         /* @req OS093 ActivateTask */
501         if( Os_IrqAnyDisabled() ) {
502                 rv = E_OS_DISABLEDINT;
503                 goto err;
504         }
505 #endif
506
507         Irq_Save(msr);
508
509         if( os_pcb_get_state(pcb) == ST_SUSPENDED ) {
510                 pcb->activations++;
511                 if (pcb->proc_type == PROC_EXTENDED) {
512                         /** @req OSEK ActivateTask Cleanup events
513                          * OSEK,ActivateTask, When an extended task is transferred from suspended
514                          * state into ready state all its events are cleared.*/
515                         pcb->ev_set = 0;
516                         pcb->ev_wait = 0;
517                 } else {
518                         Os_StackSetup(pcb);
519                         OsArch_SetTaskEntry(pcb);
520                         Os_ArchSetupContext(pcb);
521                 }
522                 Os_TaskMakeReady(pcb);
523         } else {
524
525                 if( pcb->proc_type == PROC_EXTENDED ) {
526                         /** @req OSEK Activate task.
527                          * An extended task be activated once. See Chapter 4.3 in OSEK
528                          */
529                         rv = E_OS_LIMIT;
530                         goto err;
531                 }
532
533                 /** @req OSEK_? Too many task activations */
534                 if( pcb->activations == pcb->activationLimit ) {
535                         rv=E_OS_LIMIT;
536                         goto err;
537                 } else {
538                         pcb->activations++;
539                 }
540         }
541
542         Irq_Restore(msr);\r
543
544         /* Preempt only if higher prio than us */\r
545         if(     (pcb->scheduling == FULL) &&
546                 (os_sys.int_nest_cnt == 0) && (pcb->prio > Os_TaskGetCurrent()->prio) )
547         {\r
548                 Os_Dispatch(0);\r
549         }\r
550 \r
551         OS_STD_END_1(OSServiceId_ActivateTask,TaskID);\r
552 }\r
553
554 /**
555  * This  service  causes  the  termination  of  the  calling  task.  The
556  * calling  task  is  transferred  from  the  running  state  into  the
557  * suspended state.
558  *
559  * An internal resource assigned to the calling task is automatically
560  * released. Other resources occupied by the task shall have been
561  * released before the call to TerminateTask. If a resource is still
562  * occupied in standard status the behaviour is undefined.
563  *
564  *   If the call was successful, TerminateTask does not return to the
565  * call level and the status can not be evaluated.
566  *
567  *   If the version with extended status is used, the service returns
568  * in case of error, and provides a status which can be evaluated
569  * in the application.
570  *
571  *   If the service TerminateTask is called successfully, it enforces a
572  * rescheduling.
573  *
574  *  [ Ending   a   task   function   without   call   to   TerminateTask
575  *    or ChainTask is strictly forbidden and may leave the system in an
576  *    undefined state. ]
577  *
578  * [] is an OSEK requirement and is overridden by OS052
579  *
580  * @return
581  */
582 \r
583 StatusType TerminateTask( void ) {\r
584         OsPcbType *curr_pcb = Os_TaskGetCurrent();\r
585         StatusType rv = E_OK;
586         uint32_t flags;\r
587 \r
588         os_std_printf(D_TASK,"TerminateTask %s\n",curr_pcb->name);\r
589
590 #if (OS_STATUS_EXTENDED == STD_ON )
591         if( os_sys.int_nest_cnt != 0 ) {
592                 rv =  E_OS_CALLEVEL;
593                 goto err;
594         }
595 #endif
596 \r
597         Irq_Save(flags);
598
599         --curr_pcb->activations;
600 //      assert(curr_pcb->activations>=0);
601
602         /*@req OSEK TerminateTask
603          * In case of tasks with multiple activation requests,
604          * terminating the current instance of the task automatically puts the next
605          * instance of the same task into the ready state
606          */
607         if( curr_pcb->activations == 0 ) {
608                 Os_TaskMakeSuspended(curr_pcb);
609         } else {
610                 /* We need to add ourselves to the ready list again,
611                  * with a startup context. */
612         }
613
614 //      Os_ContextReInit(curr_pcb);
615
616         /* Force the dispatcher to find something, even if its us */
617         Os_Dispatch(1);
618 \r
619         Irq_Restore(flags);\r
620         // It must find something here...otherwise something is very wrong..\r
621         assert(0);\r
622 \r
623         rv = E_NOT_OK;\r
624         goto err;\r
625 \r
626 \r
627         OS_STD_END(OSServiceId_TerminateTask);\r
628 }\r
629 \r
630 StatusType ChainTask( TaskType TaskId ) {\r
631         StatusType rv;
632         uint32_t flags;
633
634 #if (OS_STATUS_EXTENDED == STD_ON )
635         TASK_CHECK_ID(TaskId);
636
637         if( os_sys.int_nest_cnt != 0 ) {
638                 rv =  E_OS_CALLEVEL;
639                 goto err;
640         }
641 #endif
642 \r
643         Irq_Save(flags);\r
644         rv = ActivateTask(TaskId);\r
645         /* TODO: more more here..*/\r
646         TerminateTask();\r
647         Irq_Restore(flags);\r
648 \r
649         if (rv != E_OK) goto err;\r
650 \r
651         OS_STD_END_1(OSServiceId_ChainTask,TaskId);\r
652 }\r
653 \r
654 /**\r
655  * If a higher-priority task is ready, the internal resource of the task\r
656  * is released, the current task is put into the  ready state, its\r
657  * context is saved and the higher-priority task is executed.\r
658  * Otherwise the calling task is continued.\r
659  *\r
660  * TODO: The OSEK spec says a lot of strange things under "particulareties"\r
661  * that I don't understand
662  *
663  * See OSEK/VDX 13.2.3.4\r
664  *
665  */\r
666 StatusType Schedule( void ) {\r
667 //      OsPcbType *pcb;\r
668 //      OsPcbType *curr_pcb = get_curr_pcb();\r
669         StatusType rv = E_OK;
670         uint32_t flags;\r
671
672         /* We need to figure out if we have an internal resource,
673          * otherwise no re-scheduling.
674          * NON  - Have internal resource prio OS_RES_SCHEDULER_PRIO (32+)
675          * FULL - Assigned internal resource OR
676          *        No assigned internal resource.
677          * */
678         if( Os_TaskGetCurrent()->scheduling != NON ) {
679                 return E_OK;
680         }
681
682 #if 0
683         if( os_get_resource_int_p() == NULL ) {
684                 /* We do nothing */
685                 return E_OK;
686         }
687 #endif
688
689         /* Check that we are not calling from interrupt context */
690         if( os_sys.int_nest_cnt != 0 ) {
691                 rv =  E_OS_CALLEVEL;
692                 goto err;
693         }
694 \r
695         Irq_Save(flags);
696         Os_Dispatch(0);\r
697         Irq_Restore(flags);\r
698 \r
699         // Prevent label warning. Remove this when proper error handling is implemented.\r
700         if (0) goto err;\r
701 \r
702         OS_STD_END(OSServiceId_Schedule);\r
703 }\r
704 \r