Drop ABI versioning for asm_switch_context(), as it could be easily done by renaming...
[bertos.git] / bertos / kern / proc.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2001,2004 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 1999,2000,2001 Bernie Innocenti <bernie@codewiz.org>
31  *
32  * -->
33  *
34  * \brief Simple realtime multitasking scheduler.
35  *        Context switching is only done cooperatively.
36  *
37  * \version $Id$
38  *
39  * \author Bernie Innocenti <bernie@codewiz.org>
40  * \author Stefano Fedrigo <aleph@develer.com>
41  */
42
43
44 #include "proc_p.h"
45 #include "proc.h"
46
47 #include "cfg/cfg_arch.h"  /* ARCH_EMUL */
48 #include <cfg/debug.h>
49 #include <cfg/module.h>
50 #include <cfg/macros.h>       /* ABS() */
51
52 #include <cpu/irq.h>
53 #include <cpu/types.h>
54 #include <cpu/attr.h>
55
56 #include <mware/event.h>
57
58 #include <string.h>           /* memset() */
59
60 /**
61  * CPU dependent context switching routines.
62  *
63  * \note This function *MUST* preserve also the status of the interrupts.
64  */
65 EXTERN_C void asm_switch_context(cpustack_t **new_sp, cpustack_t **save_sp);
66
67 /*
68  * The scheduer tracks ready and waiting processes
69  * by enqueuing them in these lists. A pointer to the currently
70  * running process is stored in the CurrentProcess pointer.
71  *
72  * NOTE: these variables are protected by DI/EI locking
73  */
74 REGISTER Process *CurrentProcess;
75 REGISTER List     ProcReadyList;
76
77
78 #if CONFIG_KERN_PREEMPTIVE
79 /*
80  * The time sharing scheduler forces a task switch when
81  * the current process has consumed its quantum.
82  */
83 uint16_t Quantum;
84 #endif
85
86
87 /* In Win32 we must emulate stack on the real process stack */
88 #if (ARCH & ARCH_EMUL)
89 extern List StackFreeList;
90 #endif
91
92 /** The main process (the one that executes main()). */
93 struct Process MainProcess;
94
95
96 static void proc_init_struct(Process *proc)
97 {
98         /* Avoid warning for unused argument. */
99         (void)proc;
100
101 #if CONFIG_KERN_SIGNALS
102         proc->sig_recv = 0;
103 #endif
104
105 #if CONFIG_KERN_PREEMPTIVE
106         proc->forbid_cnt = 0;
107 #endif
108
109 #if CONFIG_KERN_HEAP
110         proc->flags = 0;
111 #endif
112 }
113
114 MOD_DEFINE(proc);
115
116 void proc_init(void)
117 {
118         LIST_INIT(&ProcReadyList);
119
120 #if CONFIG_KERN_MONITOR
121         monitor_init();
122 #endif
123
124         /* We "promote" the current context into a real process. The only thing we have
125          * to do is create a PCB and make it current. We don't need to setup the stack
126          * pointer because it will be written the first time we switch to another process.
127          */
128         proc_init_struct(&MainProcess);
129         CurrentProcess = &MainProcess;
130
131         MOD_INIT(proc);
132 }
133
134
135 /**
136  * Create a new process, starting at the provided entry point.
137  *
138  * \return Process structure of new created process
139  *         if successful, NULL otherwise.
140  */
141 struct Process *proc_new_with_name(UNUSED(const char *, name), void (*entry)(void), iptr_t data, size_t stack_size, cpustack_t *stack_base)
142 {
143         Process *proc;
144         size_t i;
145         const size_t PROC_SIZE_WORDS = ROUND2(sizeof(Process), sizeof(cpustack_t)) / sizeof(cpustack_t);
146 #if CONFIG_KERN_HEAP
147         bool free_stack = false;
148 #endif
149
150 #if (ARCH & ARCH_EMUL)
151         /* Ignore stack provided by caller and use the large enough default instead. */
152         stack_base = (cpustack_t *)list_remHead(&StackFreeList);
153
154         stack_size = CONFIG_PROC_DEFSTACKSIZE;
155 #elif CONFIG_KERN_HEAP
156         /* Did the caller provide a stack for us? */
157         if (!stack_base)
158         {
159                 /* Did the caller specify the desired stack size? */
160                 if (!stack_size)
161                         stack_size = CONFIG_PROC_DEFSTACKSIZE + sizeof(Process);
162
163                 /* Allocate stack dinamically */
164                 if (!(stack_base = heap_alloc(stack_size)))
165                         return NULL;
166
167                 free_stack = true;
168         }
169 #else
170         /* Stack must have been provided by the user */
171         ASSERT(stack_base);
172         ASSERT(stack_size);
173 #endif
174
175 #if CONFIG_KERN_MONITOR
176         /* Fill-in the stack with a special marker to help debugging */
177         memset(stack_base, CONFIG_KERN_STACKFILLCODE, stack_size / sizeof(cpustack_t));
178 #endif
179
180         /* Initialize the process control block */
181         if (CPU_STACK_GROWS_UPWARD)
182         {
183                 proc = (Process*)stack_base;
184                 proc->stack = stack_base + PROC_SIZE_WORDS;
185                 if (CPU_SP_ON_EMPTY_SLOT)
186                         proc->stack++;
187         }
188         else
189         {
190                 proc = (Process*)(stack_base + stack_size / sizeof(cpustack_t) - PROC_SIZE_WORDS);
191                 proc->stack = (cpustack_t*)proc;
192                 if (CPU_SP_ON_EMPTY_SLOT)
193                         proc->stack--;
194         }
195
196         proc_init_struct(proc);
197         proc->user_data = data;
198
199 #if CONFIG_KERN_HEAP | CONFIG_KERN_MONITOR | (ARCH & ARCH_EMUL)
200         proc->stack_base = stack_base;
201         proc->stack_size = stack_size;
202         #if CONFIG_KERN_HEAP
203         if (free_stack)
204                 proc->flags |= PF_FREESTACK;
205         #endif
206 #endif
207
208         /* Initialize process stack frame */
209         CPU_PUSH_CALL_CONTEXT(proc->stack, proc_exit);
210         CPU_PUSH_CALL_CONTEXT(proc->stack, entry);
211
212         /* Push a clean set of CPU registers for asm_switch_context() */
213         for (i = 0; i < CPU_SAVED_REGS_CNT; i++)
214                 CPU_PUSH_WORD(proc->stack, CPU_REG_INIT_VALUE(i));
215
216         /* Add to ready list */
217         ATOMIC(SCHED_ENQUEUE(proc));
218
219 #if CONFIG_KERN_MONITOR
220         monitor_add(proc, name);
221 #endif
222
223         return proc;
224 }
225
226 /** Rename a process */
227 void proc_rename(struct Process *proc, const char *name)
228 {
229 #if CONFIG_KERN_MONITOR
230         monitor_rename(proc, name);
231 #else
232         (void)proc; (void)name;
233 #endif
234 }
235
236
237 /**
238  * System scheduler: pass CPU control to the next process in
239  * the ready queue.
240  *
241  * Saving and restoring the context on the stack is done
242  * by a CPU-dependent support routine which must usually be
243  * written in assembly.
244  */
245 void proc_schedule(void)
246 {
247         struct Process *old_process;
248         cpuflags_t flags;
249
250         /* Remember old process to save its context later */
251         old_process = CurrentProcess;
252
253 #ifdef IRQ_RUNNING
254         /* Scheduling in interrupts is a nono. */
255         ASSERT(!IRQ_RUNNING());
256 #endif
257
258         /* Poll on the ready queue for the first ready process */
259         IRQ_SAVE_DISABLE(flags);
260         while (!(CurrentProcess = (struct Process *)list_remHead(&ProcReadyList)))
261         {
262                 /*
263                  * Make sure we physically reenable interrupts here, no matter what
264                  * the current task status is. This is important because if we
265                  * are idle-spinning, we must allow interrupts, otherwise no
266                  * process will ever wake up.
267                  *
268                  * During idle-spinning, an interrupt can occur and it may
269                  * modify \p ProcReadyList. To ensure that compiler reload this
270                  * variable every while cycle we call CPU_MEMORY_BARRIER.
271                  * The memory barrier ensure that all variables used in this context
272                  * are reloaded.
273                  * \todo If there was a way to write sig_wait() so that it does not
274                  * disable interrupts while waiting, there would not be any
275                  * reason to do this.
276                  */
277                 IRQ_ENABLE;
278                 CPU_IDLE;
279                 MEMORY_BARRIER;
280                 IRQ_DISABLE;
281         }
282         IRQ_RESTORE(flags);
283
284         /*
285          * Optimization: don't switch contexts when the active
286          * process has not changed.
287          */
288         if (CurrentProcess != old_process)
289         {
290                 cpustack_t *dummy;
291
292 #if CONFIG_KERN_PREEMPTIVE
293                 /* Reset quantum for this process */
294                 Quantum = CONFIG_KERN_QUANTUM;
295 #endif
296
297                 /* Save context of old process and switch to new process. If there is no
298                  * old process, we save the old stack pointer into a dummy variable that
299                  * we ignore. In fact, this happens only when the old process has just
300                  * exited.
301                  * TODO: Instead of physically clearing the process at exit time, a zombie
302                  * list should be created.
303                  */
304                 asm_switch_context(&CurrentProcess->stack, old_process ? &old_process->stack : &dummy);
305         }
306
307         /* This RET resumes the execution on the new process */
308 }
309
310
311 /**
312  * Terminate the current process
313  */
314 void proc_exit(void)
315 {
316 #if CONFIG_KERN_MONITOR
317         monitor_remove(CurrentProcess);
318 #endif
319
320 #if CONFIG_KERN_HEAP
321         /*
322          * The following code is BROKEN.
323          * We are freeing our own stack before entering proc_schedule()
324          * BAJO: A correct fix would be to rearrange the scheduler with
325          *  an additional parameter which frees the old stack/process
326          *  after a context switch.
327          */
328         if (CurrentProcess->flags & PF_FREESTACK)
329                 heap_free(CurrentProcess->stack_base, CurrentProcess->stack_size);
330         heap_free(CurrentProcess);
331 #endif
332
333 #if (ARCH & ARCH_EMUL)
334 #warning This is wrong
335         /* Reinsert process stack in free list */
336         ADDHEAD(&StackFreeList, (Node *)(CurrentProcess->stack
337                 - (CONFIG_PROC_DEFSTACKSIZE / sizeof(cpustack_t))));
338
339         /*
340          * NOTE: At this point the first two words of what used
341          * to be our stack contain a list node. From now on, we
342          * rely on the compiler not reading/writing the stack.
343          */
344 #endif /* ARCH_EMUL */
345
346         CurrentProcess = NULL;
347         proc_schedule();
348         /* not reached */
349 }
350
351
352 /**
353  * Co-operative context switch
354  */
355 void proc_switch(void)
356 {
357         cpuflags_t flags;
358
359         IRQ_SAVE_DISABLE(flags);
360         SCHED_ENQUEUE(CurrentProcess);
361         IRQ_RESTORE(flags);
362
363         proc_schedule();
364 }
365
366
367 /**
368  * Get the pointer to the current process
369  */
370 struct Process *proc_current(void)
371 {
372         return CurrentProcess;
373 }
374
375 /**
376  * Get the pointer to the user data of the current process
377  */
378 iptr_t proc_current_user_data(void)
379 {
380         return CurrentProcess->user_data;
381 }
382
383
384 #if CONFIG_KERN_PREEMPTIVE
385
386 /**
387  * Disable preemptive task switching.
388  *
389  * The scheduler maintains a per-process nesting counter.  Task switching is
390  * effectively re-enabled only when the number of calls to proc_permit()
391  * matches the number of calls to proc_forbid().
392  *
393  * Calling functions that could sleep while task switching is disabled
394  * is dangerous, although supported.  Preemptive task switching is
395  * resumed while the process is sleeping and disabled again as soon as
396  * it wakes up again.
397  *
398  * \sa proc_permit()
399  */
400 void proc_forbid(void)
401 {
402         /* No need to protect against interrupts here. */
403         ++CurrentProcess->forbid_cnt;
404 }
405
406 /**
407  * Re-enable preemptive task switching.
408  *
409  * \sa proc_forbid()
410  */
411 void proc_permit(void)
412 {
413         /* No need to protect against interrupts here. */
414         --CurrentProcess->forbid_cnt;
415 }
416
417 #endif /* CONFIG_KERN_PREEMPTIVE */