proc.c

Go to the documentation of this file.
00001 
00040 #include "proc_p.h"
00041 #include "proc.h"
00042 
00043 #include "cfg/cfg_arch.h"  // ARCH_EMUL
00044 #include "cfg/cfg_kern.h"
00045 #include <cfg/macros.h>    // ROUND_UP2
00046 #include <cfg/module.h>
00047 #include <cfg/depend.h>    // CONFIG_DEPEND()
00048 
00049 #include <cpu/irq.h>
00050 #include <cpu/types.h>
00051 #include <cpu/attr.h>
00052 #include <cpu/frame.h>
00053 
00054 #if CONFIG_KERN_HEAP
00055     #include <struct/heap.h>
00056 #endif
00057 
00058 #include <string.h>           /* memset() */
00059 
00060 // Check config dependencies
00061 CONFIG_DEPEND(CONFIG_KERN_SIGNALS,    CONFIG_KERN_SCHED);
00062 CONFIG_DEPEND(CONFIG_KERN_SEMAPHORES, CONFIG_KERN_SIGNALS);
00063 CONFIG_DEPEND(CONFIG_KERN_MONITOR,    CONFIG_KERN_SCHED);
00064 
00065 
00066 /*
00067  * The scheduer tracks ready processes by enqueuing them in the
00068  * ready list.
00069  *
00070  * \note Access to the list must occur while interrupts are disabled.
00071  */
00072 REGISTER List ProcReadyList;
00073 
00074 /*
00075  * Holds a pointer to the TCB of the currently running process.
00076  *
00077  * \note User applications should use proc_current() to retrieve this value.
00078  */
00079 REGISTER Process *CurrentProcess;
00080 
00081 #if (ARCH & ARCH_EMUL)
00082 /*
00083  * In some hosted environments, we must emulate the stack on the real
00084  * process stack to satisfy consistency checks in system libraries and
00085  * because some ABIs place trampolines on the stack.
00086  *
00087  * Access to this list must be protected by PROC_ATOMIC().
00088  */
00089 List StackFreeList;
00090 
00091 #define NPROC 8
00092 cpu_stack_t proc_stacks[NPROC][(64 * 1024) / sizeof(cpu_stack_t)];
00093 #endif
00094 
00096 struct Process MainProcess;
00097 
00098 
00099 static void proc_init_struct(Process *proc)
00100 {
00101     /* Avoid warning for unused argument. */
00102     (void)proc;
00103 
00104 #if CONFIG_KERN_SIGNALS
00105     proc->sig_recv = 0;
00106 #endif
00107 
00108 #if CONFIG_KERN_HEAP
00109     proc->flags = 0;
00110 #endif
00111 
00112 #if CONFIG_KERN_PRI
00113     proc->link.pri = 0;
00114 #endif
00115 }
00116 
00117 MOD_DEFINE(proc);
00118 
00119 void proc_init(void)
00120 {
00121     LIST_INIT(&ProcReadyList);
00122 
00123 #if ARCH & ARCH_EMUL
00124     LIST_INIT(&StackFreeList);
00125     for (int i = 0; i < NPROC; i++)
00126         ADDTAIL(&StackFreeList, (Node *)proc_stacks[i]);
00127 #endif
00128 
00129     /*
00130      * We "promote" the current context into a real process. The only thing we have
00131      * to do is create a PCB and make it current. We don't need to setup the stack
00132      * pointer because it will be written the first time we switch to another process.
00133      */
00134     proc_init_struct(&MainProcess);
00135     CurrentProcess = &MainProcess;
00136 
00137 #if CONFIG_KERN_MONITOR
00138     monitor_init();
00139     monitor_add(CurrentProcess, "main");
00140 #endif
00141 
00142 #if CONFIG_KERN_PREEMPT
00143     preempt_init();
00144 #endif
00145 
00146     MOD_INIT(proc);
00147 }
00148 
00155 struct Process *proc_new_with_name(UNUSED_ARG(const char *, name), void (*entry)(void), iptr_t data, size_t stack_size, cpu_stack_t *stack_base)
00156 {
00157     Process *proc;
00158     const size_t PROC_SIZE_WORDS = ROUND_UP2(sizeof(Process), sizeof(cpu_stack_t)) / sizeof(cpu_stack_t);
00159 #if CONFIG_KERN_HEAP
00160     bool free_stack = false;
00161 #endif
00162     TRACEMSG("name=%s", name);
00163 
00164 #if (ARCH & ARCH_EMUL)
00165     /* Ignore stack provided by caller and use the large enough default instead. */
00166     PROC_ATOMIC(stack_base = (cpu_stack_t *)list_remHead(&StackFreeList));
00167 
00168     stack_size = CONFIG_KERN_MINSTACKSIZE;
00169 #elif CONFIG_KERN_HEAP
00170     /* Did the caller provide a stack for us? */
00171     if (!stack_base)
00172     {
00173         /* Did the caller specify the desired stack size? */
00174         if (!stack_size)
00175             stack_size = CONFIG_KERN_MINSTACKSIZE;
00176 
00177         /* Allocate stack dinamically */
00178         if (!(stack_base = heap_alloc(stack_size)))
00179             return NULL;
00180 
00181         free_stack = true;
00182     }
00183 
00184 #else // !ARCH_EMUL && !CONFIG_KERN_HEAP
00185 
00186     /* Stack must have been provided by the user */
00187     ASSERT_VALID_PTR(stack_base);
00188     ASSERT(stack_size);
00189 
00190 #endif // !ARCH_EMUL && !CONFIG_KERN_HEAP
00191 
00192 #if CONFIG_KERN_MONITOR
00193     /*
00194      * Fill-in the stack with a special marker to help debugging.
00195      * On 64bit platforms, CONFIG_KERN_STACKFILLCODE is larger
00196      * than an int, so the (int) cast is required to silence the
00197      * warning for truncating its size.
00198      */
00199     memset(stack_base, (int)CONFIG_KERN_STACKFILLCODE, stack_size);
00200 #endif
00201 
00202     /* Initialize the process control block */
00203     if (CPU_STACK_GROWS_UPWARD)
00204     {
00205         proc = (Process *)stack_base;
00206         proc->stack = stack_base + PROC_SIZE_WORDS;
00207         if (CPU_SP_ON_EMPTY_SLOT)
00208             proc->stack++;
00209     }
00210     else
00211     {
00212         proc = (Process *)(stack_base + stack_size / sizeof(cpu_stack_t) - PROC_SIZE_WORDS);
00213         proc->stack = (cpu_stack_t *)proc;
00214         if (CPU_SP_ON_EMPTY_SLOT)
00215             proc->stack--;
00216     }
00217 
00218     proc_init_struct(proc);
00219     proc->user_data = data;
00220 
00221 #if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR | (ARCH & ARCH_EMUL)
00222     proc->stack_base = stack_base;
00223     proc->stack_size = stack_size;
00224     #if CONFIG_KERN_HEAP
00225     if (free_stack)
00226         proc->flags |= PF_FREESTACK;
00227     #endif
00228 #endif
00229 
00230     #if CONFIG_KERN_PREEMPT
00231 
00232         getcontext(&proc->context);
00233         proc->context.uc_stack.ss_sp = proc->stack;
00234         proc->context.uc_stack.ss_size = stack_size - PROC_SIZE_WORDS - 1;
00235         proc->context.uc_link = NULL;
00236         makecontext(&proc->context, (void (*)(void))proc_entry, 1, entry);
00237 
00238     #else // !CONFIG_KERN_PREEMPT
00239     {
00240         size_t i;
00241 
00242         /* Initialize process stack frame */
00243         CPU_PUSH_CALL_FRAME(proc->stack, proc_exit);
00244         CPU_PUSH_CALL_FRAME(proc->stack, entry);
00245 
00246         /* Push a clean set of CPU registers for asm_switch_context() */
00247         for (i = 0; i < CPU_SAVED_REGS_CNT; i++)
00248             CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i));
00249     }
00250     #endif // CONFIG_KERN_PREEMPT
00251 
00252     #if CONFIG_KERN_MONITOR
00253         monitor_add(proc, name);
00254     #endif
00255 
00256     /* Add to ready list */
00257     ATOMIC(SCHED_ENQUEUE(proc));
00258 
00259     return proc;
00260 }
00261 
00267 const char *proc_name(struct Process *proc)
00268 {
00269     #if CONFIG_KERN_MONITOR
00270         return proc ? proc->monitor.name : "<NULL>";
00271     #else
00272         (void)proc;
00273         return "---";
00274     #endif
00275 }
00276 
00278 const char *proc_currentName(void)
00279 {
00280     return proc_name(proc_current());
00281 }
00282 
00284 void proc_rename(struct Process *proc, const char *name)
00285 {
00286 #if CONFIG_KERN_MONITOR
00287     monitor_rename(proc, name);
00288 #else
00289     (void)proc; (void)name;
00290 #endif
00291 }
00292 
00293 
00294 #if CONFIG_KERN_PRI
00295 
00314 void proc_setPri(struct Process *proc, int pri)
00315 {
00316         if (proc->link.pri == pri)
00317             return;
00318 
00319         proc->link.pri = pri;
00320 
00321         if (proc != CurrentProcess)
00322         {
00323                 //proc_forbid();
00324                 //TODO: re-enqueue process
00325                 //pric_permit();
00326         }
00327 }
00328 #endif // CONFIG_KERN_PRI
00329 
00333 void proc_exit(void)
00334 {
00335     TRACEMSG("%p:%s", CurrentProcess, proc_currentName());
00336 
00337 #if CONFIG_KERN_MONITOR
00338     monitor_remove(CurrentProcess);
00339 #endif
00340 
00341 #if CONFIG_KERN_HEAP
00342     /*
00343      * The following code is BROKEN.
00344      * We are freeing our own stack before entering proc_schedule()
00345      * BAJO: A correct fix would be to rearrange the scheduler with
00346      *  an additional parameter which frees the old stack/process
00347      *  after a context switch.
00348      */
00349     if (CurrentProcess->flags & PF_FREESTACK)
00350         heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size);
00351     heap_free(CurrentProcess);
00352 #endif
00353 
00354 #if (ARCH & ARCH_EMUL)
00355     /* Reinsert process stack in free list */
00356     PROC_ATOMIC(ADDHEAD(&StackFreeList, (Node *)CurrentProcess->stack_base));
00357 
00358     /*
00359      * NOTE: At this point the first two words of what used
00360      * to be our stack contain a list node. From now on, we
00361      * rely on the compiler not reading/writing the stack.
00362      */
00363 #endif /* ARCH_EMUL */
00364 
00365     CurrentProcess = NULL;
00366     proc_switch();
00367     /* not reached */
00368 }
00369 
00370 
00374 iptr_t proc_currentUserData(void)
00375 {
00376     return CurrentProcess->user_data;
00377 }