cp: allow prefixing all console messages with the guest name
[hvf.git] / cp / nucleus / sched.c
bloba8c25987b1d0f0252e1bd07db9a243f95de2ff12
1 /*
2 * (C) Copyright 2007-2011 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
4 * This file is released under the GPLv2. See the COPYING file for more
5 * details.
6 */
8 #include <sched.h>
9 #include <page.h>
10 #include <buddy.h>
11 #include <slab.h>
12 #include <magic.h>
14 /* list of all runnable processes */
15 static struct list_head runnable;
17 /* list of all processes in the system */
18 static struct list_head processes;
20 /* idle task */
21 static struct task idle_task;
23 /**
24 * start_task - helper used to start the task's code
25 * @f: function to execute
27 static void start_task(int (*f)(void*), void *data)
30 * Start executing the code
32 if (f)
33 (*f)(data);
35 ldep_no_locks();
37 exit();
39 /* unreachable */
40 BUG();
43 /**
44 * __init_task - helper to initialize the task structure
45 * @task: task struct to initialize
46 * @f: pointer to function to execute
47 * @stack: pointer to the page for stack
49 static void __init_task(struct task *task, void *f, void *data, void *stack)
51 memset(task, 0, sizeof(struct task));
53 task->regs.psw.io = 1;
54 task->regs.psw.ex = 1;
55 task->regs.psw.m = 1;
56 task->regs.psw.ea = 1;
57 task->regs.psw.ba = 1;
59 /* code to execute */
60 task->regs.psw.ptr = (u64) start_task;
62 task->regs.gpr[2] = (u64) f;
63 task->regs.gpr[3] = (u64) data;
64 task->regs.gpr[15] = ((u64) stack) + PAGE_SIZE - STACK_FRAME_SIZE;
66 task->state = TASK_SLEEPING;
68 task->stack = stack;
70 task->nr_locks = 0;
73 /**
74 * init_idle_task - initialize the idle task
76 * Since the initial thread of execution already has a stack, and the task
77 * struct is a global variable, all that is left for us to do is to
78 * associate the current stack with idle_task.
80 static void init_idle_task(void)
82 u64 stack;
84 stack = ((u64) &stack) & ~(PAGE_SIZE-1);
86 __init_task(&idle_task, NULL, NULL, (void*) stack);
87 set_task_ptr(&idle_task);
90 * NOTE: The idle task is _not_ supposed to be on either of the
91 * two process lists.
95 /**
96 * create_task - create a new task
97 * @name: name for the task
98 * @f: function pointer to where thread of execution should begin
99 * @data: arbitrary data to pass to the task
101 struct task* create_task(char *name, int (*f)(void *), void *data)
103 struct page *page;
104 struct task *task;
107 * Allocate a page for stack, and struct task itself
109 page = alloc_pages(0, ZONE_NORMAL);
110 task = malloc(sizeof(struct task), ZONE_NORMAL);
111 if (!page || !task)
112 goto out;
115 * Set up task's state
117 __init_task(task, f, data, page_to_addr(page));
119 strncpy(task->name, name, TASK_NAME_LEN);
122 * Add the task to the scheduler lists
124 list_add_tail(&task->proc_list, &processes);
125 list_add_tail(&task->run_queue, &runnable);
127 return task;
129 out:
130 free(task);
131 free_pages(page_to_addr(page), 0);
133 return ERR_PTR(-ENOMEM);
137 * __sched - core of the scheduler code. This decides which task to run
138 * next, and switches over to it
140 static void __sched(int force_idle)
142 struct task *next;
145 * If there are no runnable tasks, let's use the idle thread
147 if (force_idle || list_empty(&runnable)) {
148 next = &idle_task;
149 goto run;
152 next = list_first_entry(&runnable, struct task, run_queue);
153 BUG_ON(!next);
156 * Remove the new task from the run queue & mark it as running
158 list_del(&next->run_queue);
159 next->state = TASK_RUNNING;
161 run:
162 next->slice_end_time = (force_idle) ? force_idle : ticks + SCHED_TICKS_PER_SLICE;
165 * NOTE: Because we need to load the registers _BEFORE_ we issue
166 * lpswe, we have to place the new psw into PSA and use register 0
168 memcpy(PSA_TMP_PSW, &next->regs.psw, sizeof(struct psw));
169 set_task_ptr(next);
171 load_pasce(next->regs.cr1);
174 * Load the next task
176 * FIXME: fp? ar? cr?
178 asm volatile(
179 "lmg %%r0,%%r15,%0\n" /* load gpr */
180 "lpswe %1\n" /* load new psw */
181 : /* output */
182 : /* input */
183 "m" (next->regs.gpr[0]),
184 "m" (*PSA_TMP_PSW)
187 /* unreachable */
188 BUG();
192 * schedule_preempted - called to switch tasks
194 void __schedule(struct psw *old_psw, int newstate)
196 struct task *prev;
197 int force_idle = 0;
200 * Save last process's state
203 prev = current;
204 BUG_ON(!prev);
207 * Save the registers (gpr, psw, ...)
209 * FIXME: fp? ar? cr?
211 memcpy(prev->regs.gpr, PSA_INT_GPR, sizeof(u64)*16);
212 memcpy(&prev->regs.psw, old_psw, sizeof(struct psw));
215 * Idle task doesn't get added back to the queue
217 if (prev == &idle_task)
218 goto go;
220 store_pasce(&prev->regs.cr1);
223 * Add back on the queue
225 prev->state = newstate;
228 * If the previous task didn't use it's full slice, force idle_task
229 * to take over.
231 if (prev->slice_end_time >= (ticks + SCHED_TICKS_PER_SLICE))
232 force_idle = prev->slice_end_time;
234 switch(newstate) {
235 case TASK_ZOMBIE:
236 list_del(&prev->proc_list);
237 free_pages(prev->stack, 0);
238 free(prev);
239 break;
240 case TASK_LOCKED:
241 break;
242 default:
243 list_add_tail(&prev->run_queue, &runnable);
244 break;
249 * Run the rest of the scheduler that selects the next task and
250 * context switches
252 __sched(force_idle);
256 * __schedule_svc - wrapper for the supervisor-service call handler
258 void __schedule_svc(void)
260 __schedule(SVC_INT_OLD_PSW, TASK_SLEEPING);
264 * __schedule_blocked__svc - wrapper for the supervisor-service call handler
266 void __schedule_blocked_svc(void)
268 __schedule(SVC_INT_OLD_PSW, TASK_LOCKED);
272 * __schedule_blocked__svc - wrapper for the supervisor-service call handler
274 void __schedule_exit_svc(void)
276 __schedule(SVC_INT_OLD_PSW, TASK_ZOMBIE);
280 * Initialize the schduler, and all associated structures
282 void init_sched(void)
284 u64 cr0;
286 INIT_LIST_HEAD(&runnable);
287 INIT_LIST_HEAD(&processes);
289 init_idle_task();
291 /* enable cpu timer interrupt subclass */
292 asm volatile(
293 "stctg 0,0,%0\n" /* get cr0 */
294 "oi %1,0x04\n" /* enable cpu timer subclass */
295 "ni %1,0x7f\n" /* disable clock comp */
296 "lctlg 0,0,%0\n" /* reload cr0 */
297 : /* output */
298 : /* input */
299 "m" (cr0),
300 "m" (*(((u8*)&cr0) + 6))
303 set_timer();
306 void list_tasks(void (*f)(struct task*, void*), void *priv)
308 struct task *t;
310 list_for_each_entry(t, &processes, proc_list)
311 f(t, priv);
314 void make_runnable(struct task *task)
316 task->state = TASK_SLEEPING;
317 list_add_tail(&task->run_queue, &runnable);