1 /* -------------------------------- Arctic Core ------------------------------
\r
2 * Arctic Core - the open source AUTOSAR platform http://arccore.com
\r
4 * Copyright (C) 2009 ArcCore AB <contact@arccore.com>
\r
6 * This source code is free software; you can redistribute it and/or modify it
\r
7 * under the terms of the GNU General Public License version 2 as published by the
\r
8 * Free Software Foundation; See <http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt>.
\r
10 * This program is distributed in the hope that it will be useful, but
\r
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
\r
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
\r
14 * -------------------------------- Arctic Core ------------------------------*/
\r
19 #include "internal.h"
\r
25 _Bool os_pcb_pid_valid( OsPcbType *restrict pcb ) {
\r
26 return ( pcb->pid > OS_TASK_CNT ) ? 0 : 1;
\r
29 * Start an extended task.
\r
31 * - Grab the internal resource for the process
\r
32 * - Set it running state.
\r
33 * - Start to execute the process
\r
36 void Os_TaskStartExtended( void ) {
\r
40 pcb = Os_TaskGetCurrent();
\r
42 Os_ResourceGetInternal();
\r
43 Os_TaskMakeRunning(pcb);
\r
50 /* We got back without any TerminateTask() or ChainTask()
\r
53 * Each task shall terminate itself at the end of its code.
\r
54 * Ending the task without a call to TerminateTask or ChainTask
\r
55 * is strictly forbidden and causes undefined behaviour.
\r
58 * OS052, OS069, OS070 and OS239
\r
63 if( Os_IrqAnyDisabled() ) {
\r
68 if( Os_TaskOccupiesResources(pcb) ) {
\r
69 Os_ResourceFreeAll(pcb);
\r
73 ERRORHOOK(E_OS_MISSINGEND);
\r
80 * Start an basic task.
\r
81 * See extended task.
\r
84 void Os_TaskStartBasic( void ) {
\r
88 pcb = Os_TaskGetCurrent();
\r
90 Os_ResourceGetInternal();
\r
91 Os_TaskMakeRunning(pcb);
\r
100 if( Os_IrqAnyDisabled() ) {
\r
105 if( Os_TaskOccupiesResources(pcb) ) {
\r
106 Os_ResourceFreeAll(pcb);
\r
110 ERRORHOOK(E_OS_MISSINGEND);
\r
117 static void Os_StackSetup( OsPcbType *pcbPtr ) {
\r
120 /* Find bottom of the stack so that we can place the
\r
123 * stack bottom = high address. stack top = low address
\r
125 bottom = (uint8_t *)pcbPtr->stack.top + pcbPtr->stack.size;
\r
126 pcbPtr->stack.curr = bottom;
\r
127 // TODO: alignments here..
\r
128 // TODO :use function os_arch_get_call_size() ??
\r
130 // Make some space for back-chain.
\r
132 // Set the current stack so that it points to the context
\r
133 pcbPtr->stack.curr = bottom - Os_ArchGetScSize();
\r
135 Os_StackSetEndmark(pcbPtr);
\r
139 * Fill the stack with a predefined pattern
\r
141 * @param pcbPtr Pointer to the pcb to fill with pattern
\r
143 static void Os_StackFill(OsPcbType *pcbPtr) {
\r
144 uint8_t *p = pcbPtr->stack.curr;
\r
146 assert(pcbPtr->stack.curr > pcbPtr->stack.top);
\r
148 while (p > (uint8_t *) pcbPtr->stack.top) {
\r
150 *p = STACK_PATTERN;
\r
159 static void Os_TaskSetEntry(OsPcbType *pcbPtr ) {
\r
166 * Setup the context for a pcb. The context differs for different arch's
\r
167 * so we call the arch dependent functions also.
\r
168 * The context at setup is always a small context.
\r
170 * @param pcb Ptr to the pcb to setup context for.
\r
172 void Os_ContextInit( OsPcbType *pcb ) {
\r
174 if( pcb->autostart ) {
\r
175 Os_TaskMakeReady(pcb);
\r
177 pcb->state = ST_SUSPENDED;
\r
180 Os_StackSetup(pcb);
\r
182 Os_ArchSetTaskEntry(pcb);
\r
184 Os_ArchSetupContext(pcb);
\r
191 void Os_ContextReInit( OsPcbType *pcbPtr ) {
\r
192 Os_StackSetup(pcbPtr);
\r
196 * Search for a specific task in the pcb list.
\r
198 * @param tid The task id to search for
\r
199 * @return Ptr to the found pcb or NULL
\r
201 OsPcbType *os_find_task( TaskType tid ) {
\r
204 /* TODO: Implement this as an array */
\r
205 TAILQ_FOREACH(i_pcb,& os_sys.pcb_head,pcb_list) {
\r
206 if(i_pcb->pid == tid ) {
\r
215 * Adds a pcb to the list of pcb's
\r
218 TaskType Os_AddTask( OsPcbType *pcb ) {
\r
221 Irq_Save(msr); // Save irq status and disable interrupts
\r
223 pcb->pid = os_sys.task_cnt;
\r
224 // Add to list of PCB's
\r
225 TAILQ_INSERT_TAIL(& os_sys.pcb_head,pcb,pcb_list);
\r
228 Irq_Restore(msr); // Restore interrupts
\r
233 #define PRIO_ILLEGAL -100
\r
236 * Find the top priority task. Even the running task is included.
\r
237 * TODO: There should be a priority queue (using a heap?) here instead.
\r
238 * giving O(log n) for instertions and (1) for getting the top
\r
239 * prio task. The curerent implementation is ehhhh bad.
\r
243 OsPcbType *Os_TaskGetTop( void ){
\r
245 OsPcbType *top_prio_pcb = NULL;
\r
246 OsPriorityType top_prio = PRIO_ILLEGAL;
\r
248 // OS_DEBUG(D_TASK,"os_find_top_prio_proc\n");
\r
250 TAILQ_FOREACH(i_pcb,& os_sys.ready_head,ready_list) {
\r
251 // all ready task are canidates
\r
252 if( i_pcb->state & (ST_READY|ST_RUNNING)) {
\r
253 if( top_prio != PRIO_ILLEGAL ) {
\r
254 if( i_pcb->prio > top_prio ) {
\r
255 top_prio = i_pcb->prio;
\r
256 top_prio_pcb = i_pcb;
\r
259 top_prio = i_pcb->prio;
\r
260 top_prio_pcb = i_pcb;
\r
267 assert(top_prio_pcb!=NULL);
\r
269 OS_DEBUG(D_TASK,"Found %s\n",top_prio_pcb->name);
\r
271 return top_prio_pcb;
\r
275 #define USE_LDEBUG_PRINTF
\r
278 // we come here from
\r
280 // old_pcb -> WAITING
\r
281 // new_pcb -> READY(RUNNING)
\r
283 // old_pcb -> READY
\r
284 // new_pcb -> READY/RUNNING
\r
288 * 1. When running ->
\r
289 * - remove from ready queue
\r
290 * - set state == ST_RUNNING
\r
292 * 2. When running ->
\r
293 * * leave in ready queue
\r
294 * * set state == ST_RUNNING
\r
295 * - ready queue and ST_READY not the same
\r
296 * + No need to remove the running process from ready queue
\r
299 OsPcbType *Os_FindTopPrioTask( void ) {
\r
306 * Tries to Dispatch.
\r
314 * @param force Force a re-scheduling
\r
317 void Os_Dispatch( uint32_t op ) {
\r
319 OsPcbType *currPcbPtr = Os_TaskGetCurrent();
\r
321 assert(os_sys.int_nest_cnt == 0);
\r
322 assert(Os_SchedulerResourceIsFree());
\r
324 /* When calling post hook we must still be in ST_RUNNING */
\r
325 assert( currPcbPtr->state & ST_RUNNING );
\r
328 /* Go the correct state for running task */
\r
329 if( op & ( OP_SET_EVENT | OP_SCHEDULE | OP_RELEASE_RESOURCE )) {
\r
330 Os_TaskRunningToReady(currPcbPtr);
\r
331 } else if( op & (OP_WAIT_EVENT )) {
\r
332 Os_TaskMakeWaiting(currPcbPtr);
\r
333 } else if( op & (OP_SLEEP )) {
\r
334 Os_TaskMakeSleeping(currPcbPtr);
\r
335 } else if( op & OP_ACTIVATE_TASK ) {
\r
336 Os_TaskMakeReady(currPcbPtr);
\r
337 } else if( op & OP_CHAIN_TASK ) {
\r
338 assert( os_sys.chainedPcbPtr != NULL );
\r
340 /* # from chain top
\r
341 * ----------------------------------------------------------
\r
342 * 1 1 1 1 1->RUNNING
\r
343 * 2 1 1 2 1->READY, 2->RUNNING
\r
344 * 3 1 2 2 1->SUSPENDED/READY*, 2->RUNNING
\r
345 * 4 1 2 3 1->SUSPENDED/READY*, 2->READY , 3-RUNNING
\r
347 * *) Depends on the number of activations.
\r
349 * - Chained task is always READY when coming from ChainTask()
\r
351 if( currPcbPtr != os_sys.chainedPcbPtr ) {
\r
353 --currPcbPtr->activations;
\r
354 if( currPcbPtr->activations <= 0 ) {
\r
355 currPcbPtr->activations = 0;
\r
356 Os_TaskMakeSuspended(currPcbPtr);
\r
358 Os_TaskRunningToReady(currPcbPtr);
\r
360 /* Chained task is already in READY */
\r
362 os_sys.chainedPcbPtr = NULL;
\r
364 } else if( op & OP_TERMINATE_TASK ) {
\r
365 /*@req OSEK TerminateTask
\r
366 * In case of tasks with multiple activation requests,
\r
367 * terminating the current instance of the task automatically puts the next
\r
368 * instance of the same task into the ready state
\r
370 --currPcbPtr->activations;
\r
372 if( currPcbPtr->activations <= 0 ) {
\r
373 currPcbPtr->activations = 0;
\r
374 Os_TaskMakeSuspended(currPcbPtr);
\r
380 pcbPtr = Os_TaskGetTop();
\r
384 /* Swap if we found any process or are forced (multiple activations)*/
\r
385 if( pcbPtr != currPcbPtr ) {
\r
387 if( (op & OP_CHAIN_TASK) && ( currPcbPtr == os_sys.chainedPcbPtr ) ) {
\r
389 Os_TaskRunningToReady(currPcbPtr);
\r
394 assert(pcbPtr!=NULL);
\r
396 Os_ResourceReleaseInternal();
\r
398 #if (OS_STACK_MONITORING == 1)
\r
399 if( !Os_StackIsEndmarkOk(currPcbPtr) ) {
\r
400 #if ( OS_SC1 == 1) || ( OS_SC2 == 1)
\r
402 ShutdownOS(E_OS_STACKFAULT);
\r
404 #error SC3 or SC4 not supported. Protection hook should be called here
\r
408 OS_DEBUG(D_TASK,"Swapping to: %s\n",pcbPtr->name);
\r
409 Os_TaskSwapContext(currPcbPtr,pcbPtr);
\r
411 /* ActivateTask, SetEvent, Schedule, .. */
\r
412 // pcbPtr = Os_TaskGetCurrent();
\r
413 // Os_TaskMakeRunning(pcbPtr);
\r
416 // Os_ResourceGetInternal();
\r
419 OS_DEBUG(D_TASK,"Continuing task %s\n",pcbPtr->name);
\r
420 /* Setup the stack again, and just call the basic task */
\r
421 Os_StackSetup(pcbPtr);
\r
422 /* TODO: release and get the internal resource ? */
\r
423 Os_TaskMakeRunning(pcbPtr);
\r
425 Os_ArchSetSpAndCall(pcbPtr->stack.curr,Os_TaskStartBasic);
\r
432 * Thoughts on task switching and memory protection
\r
434 * If we would have had memory protection:
\r
435 * - Applications have their own MMU settings.
\r
436 * - Swapping between tasks in same Application does NOT involve the MMU.
\r
437 * - When running a non-trusted Application I need will have to:
\r
438 * - Run kernel in supervisor mode.
\r
439 * - Trap the start of each task
\r
440 * - All calls to the kernel will have a trap interface, i.e. Os_ResourceGetInternal(ActivateTask(TASK_ID_foo);
\r
442 * - The interupt is taken, the kernel runs in supervisor mode
\r
443 * - If the ISR2 activates
\r
446 * ALT1: 1 kernel stack...
\r
449 * Do we need virtual memory??
\r
452 void Os_TaskSwapContext(OsPcbType *old_pcb, OsPcbType *new_pcb ) {
\r
453 set_curr_pcb(new_pcb);
\r
454 Os_ResourceGetInternal();
\r
455 Os_TaskMakeRunning(new_pcb);
\r
456 /* TODO: The pretask hook is not called with the right stack
\r
457 * (it's called on the old task stack, not the new ) */
\r
459 Os_ArchSwapContext(old_pcb,new_pcb);
\r
462 void Os_TaskSwapContextTo(OsPcbType *old_pcb, OsPcbType *new_pcb ) {
\r
463 set_curr_pcb(new_pcb);
\r
464 Os_ResourceGetInternal();
\r
465 Os_TaskMakeRunning(new_pcb);
\r
467 Os_ArchSwapContextTo(old_pcb,new_pcb);
\r
472 void Os_Arc_GetStackInfo( TaskType task, StackInfoType *s) {
\r
473 OsPcbType *pcb = os_get_pcb(task);
\r
475 s->curr = Os_ArchGetStackPtr();
\r
476 s->top = pcb->stack.top;
\r
477 s->at_swap = pcb->stack.curr;
\r
478 s->size = pcb->stack.size;
\r
479 s->usage = (void *)Os_StackGetUsage(pcb);
\r
483 #define TASK_CHECK_ID(x) \
\r
484 if( (x) > OS_TASK_CNT) { \
\r
491 * Returns the state of a task (running, ready, waiting, suspended)
\r
492 * at the time of calling GetTaskState.
\r
494 * @param TaskId Task reference
\r
495 * @param State Reference to the state of the task
\r
498 StatusType GetTaskState(TaskType TaskId, TaskStateRefType State) {
\r
499 state_t curr_state;
\r
500 StatusType rv = E_OK;
\r
502 TASK_CHECK_ID(TaskId);
\r
504 curr_state = os_pcb_get_state(os_get_pcb(TaskId));
\r
506 // TODO: Lazy impl. for now */
\r
507 switch(curr_state) {
\r
508 case ST_RUNNING: *State = TASK_STATE_RUNNING; break;
\r
509 case ST_WAITING: *State = TASK_STATE_WAITING; break;
\r
510 case ST_SUSPENDED: *State = TASK_STATE_SUSPENDED; break;
\r
511 case ST_READY: *State = TASK_STATE_READY; break;
\r
514 // Prevent label warning. Remove when proper error handling is implemented.
\r
517 OS_STD_END_2(OSServiceId_GetTaskState,TaskId, State);
\r
522 * GetTaskID returns the information about the TaskID of the task
\r
523 * which is currently running.
\r
525 * @param task_id Reference to the task which is currently running
\r
528 StatusType GetTaskID( TaskRefType TaskID ) {
\r
529 StatusType rv = E_OK;
\r
530 *TaskID = INVALID_TASK;
\r
532 /* Test specification say return CALLEVEL if in ISR
\r
533 * but impl. spec says otherwise */
\r
534 if( os_sys.int_nest_cnt == 0 ) {
\r
535 if( os_sys.curr_pcb->state & ST_RUNNING ) {
\r
536 *TaskID = os_sys.curr_pcb->pid;
\r
538 /* This is not a real error since this could
\r
539 * be the case when called from ErrorHook */
547 ISRType GetISRID( void ) {
\r
550 if(os_sys.int_nest_cnt == 0 ) {
\r
551 return INVALID_ISR;
\r
555 return (ISRType)Os_TaskGetCurrent()->pid;
\r
558 static inline void Os_Arc_SetCleanContext( OsPcbType *pcb ) {
\r
559 if (pcb->proc_type == PROC_EXTENDED) {
\r
560 /** @req OSEK ActivateTask Cleanup events
\r
561 * OSEK,ActivateTask, When an extended task is transferred from suspended
\r
562 * state into ready state all its events are cleared.*/
\r
566 Os_StackSetup(pcb);
\r
567 Os_ArchSetTaskEntry(pcb);
\r
568 Os_ArchSetupContext(pcb);
\r
572 * The task <TaskID> is transferred from the suspended state into
\r
573 * the ready state. The operating system ensures that the task
\r
574 * code is being executed from the first statement.
\r
576 * The service may be called from interrupt level and from task
\r
577 * level (see Figure 12-1).
\r
578 * Rescheduling after the call to ActivateTask depends on the
\r
579 * place it is called from (ISR, non preemptable task, preemptable
\r
582 * If E_OS_LIMIT is returned the activation is ignored.
\r
583 * When an extended task is transferred from suspended state
\r
584 * into ready state all its events are cleared.
\r
587 * ActivateTask will not immediately change the state of the task
\r
588 * in case of multiple activation requests. If the task is not
\r
589 * suspended, the activation will only be recorded and performed later.
\r
595 StatusType ActivateTask( TaskType TaskID ) {
\r
597 OsPcbType *pcb = os_get_pcb(TaskID);
\r
598 StatusType rv = E_OK;
\r
600 OS_DEBUG(D_TASK,"# ActivateTask %s\n",pcb->name);
\r
602 #if (OS_STATUS_EXTENDED == STD_ON )
\r
604 TASK_CHECK_ID(TaskID);
\r
608 /* @req OS093 ActivateTask */
\r
609 if( Os_IrqAnyDisabled() ) {
\r
611 rv = E_OS_DISABLEDINT;
\r
615 pcb->activations++;
\r
616 if( os_pcb_get_state(pcb) != ST_SUSPENDED ) {
\r
617 /** @req OSEK_? Too many task activations */
\r
618 if( pcb->activations >= (pcb->activationLimit + 1) ) {
\r
622 --pcb->activations;
\r
626 /* We have a suspended task, make it ready for use */
\r
627 assert( pcb->activations == 1 );
\r
628 Os_Arc_SetCleanContext(pcb);
\r
629 Os_TaskMakeReady(pcb);
\r
632 /* Preempt only if we are preemptable and target has higher prio than us */
\r
633 if( (Os_TaskGetCurrent()->scheduling == FULL) &&
\r
634 (os_sys.int_nest_cnt == 0) &&
\r
635 (pcb->prio > Os_TaskGetCurrent()->prio) &&
\r
636 (Os_SchedulerResourceIsFree()))
\r
638 Os_Dispatch(OP_ACTIVATE_TASK);
\r
643 OS_STD_END_1(OSServiceId_ActivateTask,TaskID);
\r
647 * This service causes the termination of the calling task. The
\r
648 * calling task is transferred from the running state into the
\r
651 * An internal resource assigned to the calling task is automatically
\r
652 * released. Other resources occupied by the task shall have been
\r
653 * released before the call to TerminateTask. If a resource is still
\r
654 * occupied in standard status the behaviour is undefined.
\r
656 * If the call was successful, TerminateTask does not return to the
\r
657 * call level and the status can not be evaluated.
\r
659 * If the version with extended status is used, the service returns
\r
660 * in case of error, and provides a status which can be evaluated
\r
661 * in the application.
\r
663 * If the service TerminateTask is called successfully, it enforces a
\r
666 * [ Ending a task function without call to TerminateTask
\r
667 * or ChainTask is strictly forbidden and may leave the system in an
\r
668 * undefined state. ]
\r
670 * [] is an OSEK requirement and is overridden by OS052
\r
675 StatusType TerminateTask( void ) {
\r
676 OsPcbType *curr_pcb = Os_TaskGetCurrent();
\r
677 StatusType rv = E_OK;
\r
680 OS_DEBUG(D_TASK,"# TerminateTask %s\n",curr_pcb->name);
\r
682 #if (OS_STATUS_EXTENDED == STD_ON )
\r
685 if( os_sys.int_nest_cnt != 0 ) {
\r
686 rv = E_OS_CALLEVEL;
\r
691 if ( Os_SchedulerResourceIsOccupied() ) {
\r
692 rv = E_OS_RESOURCE;
\r
697 if( Os_TaskOccupiesResources(curr_pcb) ) {
\r
698 /* Note! Do NOT release the resource here */
\r
699 rv = E_OS_RESOURCE;
\r
706 /* Force the dispatcher to find something, even if its us */
\r
707 Os_Dispatch(OP_TERMINATE_TASK);
\r
711 OS_STD_END(OSServiceId_TerminateTask);
\r
714 StatusType ChainTask( TaskType TaskId ) {
\r
715 OsPcbType *curr_pcb = Os_TaskGetCurrent();
\r
716 StatusType rv = E_OK;
\r
718 OsPcbType *pcb = os_get_pcb(TaskId);
\r
721 OS_DEBUG(D_TASK,"# ChainTask %s\n",curr_pcb->name);
\r
723 #if (OS_STATUS_EXTENDED == STD_ON )
\r
725 TASK_CHECK_ID(TaskId);
\r
727 if( os_sys.int_nest_cnt != 0 ) {
\r
729 rv = E_OS_CALLEVEL;
\r
737 #if (OS_STATUS_EXTENDED == STD_ON )
\r
739 if ( Os_SchedulerResourceIsOccupied() ) {
\r
741 rv = E_OS_RESOURCE;
\r
742 Irq_Restore(flags);
\r
747 if( Os_TaskOccupiesResources(curr_pcb) ) {
\r
749 rv = E_OS_RESOURCE;
\r
750 Irq_Restore(flags);
\r
755 // if( os_pcb_get_state(pcb) != ST_SUSPENDED ) {
\r
756 if (curr_pcb != pcb) {
\r
757 /** @req OSEK_? Too many task activations */
\r
758 if( (pcb->activations + 1) > pcb->activationLimit ) {
\r
761 Irq_Restore(flags);
\r
765 if( os_pcb_get_state(pcb) == ST_SUSPENDED ) {
\r
766 assert( pcb->activations == 0 );
\r
767 Os_Arc_SetCleanContext(pcb);
\r
768 Os_TaskMakeReady(pcb);
\r
771 pcb->activations++;
\r
775 os_sys.chainedPcbPtr = pcb;
\r
777 Os_Dispatch(OP_CHAIN_TASK);
\r
781 OS_STD_END_1(OSServiceId_ChainTask,TaskId);
\r
785 * If a higher-priority task is ready, the internal resource of the task
\r
786 * is released, the current task is put into the ready state, its
\r
787 * context is saved and the higher-priority task is executed.
\r
788 * Otherwise the calling task is continued.
\r
790 * TODO: The OSEK spec says a lot of strange things under "particulareties"
\r
791 * that I don't understand
\r
793 * See OSEK/VDX 13.2.3.4
\r
796 StatusType Schedule( void ) {
\r
797 StatusType rv = E_OK;
\r
799 OsPcbType *curr_pcb = get_curr_pcb();
\r
801 OS_DEBUG(D_TASK,"# Schedule %s\n",Os_TaskGetCurrent()->name);
\r
803 /* Check that we are not calling from interrupt context */
\r
804 if( os_sys.int_nest_cnt != 0 ) {
\r
805 rv = E_OS_CALLEVEL;
\r
809 if ( Os_TaskOccupiesResources(curr_pcb) ) {
\r
810 rv = E_OS_RESOURCE;
\r
814 assert( Os_TaskGetCurrent()->state & ST_RUNNING );
\r
816 /* We need to figure out if we have an internal resource,
\r
817 * otherwise no re-scheduling.
\r
818 * NON - Have internal resource prio OS_RES_SCHEDULER_PRIO (32+)
\r
819 * FULL - Assigned internal resource OR
\r
820 * No assigned internal resource.
\r
822 if( Os_TaskGetCurrent()->scheduling != NON ) {
\r
827 OsPcbType* top_pcb = Os_TaskGetTop();
\r
828 /* only dispatch if some other ready task has higher prio */
\r
829 if (top_pcb->prio > Os_TaskGetCurrent()->prio) {
\r
830 Os_Dispatch(OP_SCHEDULE);
\r
833 Irq_Restore(flags);
\r
834 // Prevent label warning. Remove this when proper error handling is implemented.
\r
837 OS_STD_END(OSServiceId_Schedule);
\r