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