scoutos / prex-0.9.0 / sys / kern / task.c @ 03e9c04a
History | View | Annotate | Download (10.8 KB)
1 |
/*-
|
---|---|
2 |
* Copyright (c) 2005-2009, Kohsuke Ohtani
|
3 |
* All rights reserved.
|
4 |
*
|
5 |
* Redistribution and use in source and binary forms, with or without
|
6 |
* modification, are permitted provided that the following conditions
|
7 |
* are met:
|
8 |
* 1. Redistributions of source code must retain the above copyright
|
9 |
* notice, this list of conditions and the following disclaimer.
|
10 |
* 2. Redistributions in binary form must reproduce the above copyright
|
11 |
* notice, this list of conditions and the following disclaimer in the
|
12 |
* documentation and/or other materials provided with the distribution.
|
13 |
* 3. Neither the name of the author nor the names of any co-contributors
|
14 |
* may be used to endorse or promote products derived from this software
|
15 |
* without specific prior written permission.
|
16 |
*
|
17 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
18 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
20 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
21 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
23 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
24 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
26 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
27 |
* SUCH DAMAGE.
|
28 |
*/
|
29 |
|
30 |
/*
|
31 |
* task.c - task management routines.
|
32 |
*/
|
33 |
|
34 |
#include <kernel.h> |
35 |
#include <kmem.h> |
36 |
#include <sched.h> |
37 |
#include <thread.h> |
38 |
#include <ipc.h> |
39 |
#include <sync.h> |
40 |
#include <vm.h> |
41 |
#include <exception.h> |
42 |
#include <task.h> |
43 |
#include <hal.h> |
44 |
#include <sys/bootinfo.h> |
45 |
|
46 |
struct task kernel_task; /* kernel task */ |
47 |
static struct list task_list; /* list for all tasks */ |
48 |
static int ntasks; /* number of tasks in system */ |
49 |
|
50 |
/**
|
51 |
* task_create - create a new task.
|
52 |
*
|
53 |
* vm_option:
|
54 |
* VM_NEW: The child task will have fresh memory image.
|
55 |
* VM_SHARE: The child task will share whole memory image with parent.
|
56 |
* VM_COPY: The parent's memory image is copied to the child's one.
|
57 |
* VM_COPY is supported only with MMU system.
|
58 |
*
|
59 |
* Note: The child task initially contains no threads.
|
60 |
*/
|
61 |
int
|
62 |
task_create(task_t parent, int vm_option, task_t *childp)
|
63 |
{ |
64 |
struct task *task;
|
65 |
vm_map_t map = NULL;
|
66 |
|
67 |
ASSERT(parent != NULL);
|
68 |
|
69 |
switch (vm_option) {
|
70 |
case VM_NEW:
|
71 |
case VM_SHARE:
|
72 |
#ifdef CONFIG_MMU
|
73 |
case VM_COPY:
|
74 |
#endif
|
75 |
break;
|
76 |
default:
|
77 |
return EINVAL;
|
78 |
} |
79 |
if (ntasks >= MAXTASKS)
|
80 |
return EAGAIN;
|
81 |
|
82 |
sched_lock(); |
83 |
if (!task_valid(parent)) {
|
84 |
sched_unlock(); |
85 |
return ESRCH;
|
86 |
} |
87 |
if ((curtask->flags & TF_SYSTEM) == 0) { |
88 |
if (!task_access(parent)) {
|
89 |
sched_unlock(); |
90 |
return EPERM;
|
91 |
} |
92 |
/*
|
93 |
* It's important to set zero as task id before
|
94 |
* copying parent's memory space. Otherwise, we
|
95 |
* have to switch VM space to copy it.
|
96 |
*/
|
97 |
task = 0;
|
98 |
if (copyout(&task, childp, sizeof(task))) { |
99 |
sched_unlock(); |
100 |
return EFAULT;
|
101 |
} |
102 |
} |
103 |
|
104 |
if ((task = kmem_alloc(sizeof(*task))) == NULL) { |
105 |
sched_unlock(); |
106 |
return ENOMEM;
|
107 |
} |
108 |
memset(task, 0, sizeof(*task)); |
109 |
|
110 |
/*
|
111 |
* Setup VM mapping.
|
112 |
*/
|
113 |
switch (vm_option) {
|
114 |
case VM_NEW:
|
115 |
map = vm_create(); |
116 |
break;
|
117 |
case VM_SHARE:
|
118 |
vm_reference(parent->map); |
119 |
map = parent->map; |
120 |
break;
|
121 |
case VM_COPY:
|
122 |
map = vm_dup(parent->map); |
123 |
break;
|
124 |
} |
125 |
if (map == NULL) { |
126 |
kmem_free(task); |
127 |
sched_unlock(); |
128 |
return ENOMEM;
|
129 |
} |
130 |
|
131 |
/*
|
132 |
* Fill initial task data.
|
133 |
*/
|
134 |
task->map = map; |
135 |
task->handler = parent->handler; |
136 |
task->capability = parent->capability; |
137 |
task->parent = parent; |
138 |
task->flags = TF_DEFAULT; |
139 |
strlcpy(task->name, "*noname", MAXTASKNAME);
|
140 |
list_init(&task->threads); |
141 |
list_init(&task->objects); |
142 |
list_init(&task->mutexes); |
143 |
list_init(&task->conds); |
144 |
list_init(&task->sems); |
145 |
list_insert(&task_list, &task->link); |
146 |
ntasks++; |
147 |
|
148 |
if (curtask->flags & TF_SYSTEM)
|
149 |
*childp = task; |
150 |
else {
|
151 |
/*
|
152 |
* No page fault here because we have already
|
153 |
* checked it.
|
154 |
*/
|
155 |
copyout(&task, childp, sizeof(task));
|
156 |
} |
157 |
|
158 |
sched_unlock(); |
159 |
return 0; |
160 |
} |
161 |
|
162 |
/*
|
163 |
* Terminate the specified task.
|
164 |
*/
|
165 |
int
|
166 |
task_terminate(task_t task) |
167 |
{ |
168 |
list_t head, n; |
169 |
thread_t t; |
170 |
|
171 |
sched_lock(); |
172 |
if (!task_valid(task)) {
|
173 |
sched_unlock(); |
174 |
return ESRCH;
|
175 |
} |
176 |
if (!task_access(task)) {
|
177 |
sched_unlock(); |
178 |
return EPERM;
|
179 |
} |
180 |
|
181 |
list_remove(&task->link); |
182 |
task->handler = EXC_DFL; |
183 |
|
184 |
/*
|
185 |
* Clean up all resources owned by the target task.
|
186 |
*/
|
187 |
timer_stop(&task->alarm); |
188 |
object_cleanup(task); |
189 |
mutex_cleanup(task); |
190 |
cond_cleanup(task); |
191 |
sem_cleanup(task); |
192 |
|
193 |
/*
|
194 |
* Terminate each thread in the task.
|
195 |
*/
|
196 |
head = &task->threads; |
197 |
for (n = list_first(head); n != head; n = list_next(n)) {
|
198 |
t = list_entry(n, struct thread, task_link);
|
199 |
if (t != curthread)
|
200 |
thread_destroy(t); |
201 |
} |
202 |
if (task == curtask)
|
203 |
thread_destroy(curthread); |
204 |
|
205 |
vm_terminate(task->map); |
206 |
task->map = NULL;
|
207 |
kmem_free(task); |
208 |
ntasks--; |
209 |
sched_unlock(); |
210 |
return 0; |
211 |
} |
212 |
|
213 |
/*
|
214 |
* Return the current task.
|
215 |
*/
|
216 |
task_t |
217 |
task_self(void)
|
218 |
{ |
219 |
|
220 |
return curthread->task;
|
221 |
} |
222 |
|
223 |
/*
|
224 |
* Suspend a task.
|
225 |
*/
|
226 |
int
|
227 |
task_suspend(task_t task) |
228 |
{ |
229 |
list_t head, n; |
230 |
thread_t t; |
231 |
|
232 |
sched_lock(); |
233 |
if (!task_valid(task)) {
|
234 |
sched_unlock(); |
235 |
return ESRCH;
|
236 |
} |
237 |
if (!task_access(task)) {
|
238 |
sched_unlock(); |
239 |
return EPERM;
|
240 |
} |
241 |
|
242 |
if (++task->suscnt == 1) { |
243 |
/*
|
244 |
* Suspend all threads within the task.
|
245 |
*/
|
246 |
head = &task->threads; |
247 |
for (n = list_first(head); n != head; n = list_next(n)) {
|
248 |
t = list_entry(n, struct thread, task_link);
|
249 |
thread_suspend(t); |
250 |
} |
251 |
} |
252 |
sched_unlock(); |
253 |
return 0; |
254 |
} |
255 |
|
256 |
/*
|
257 |
* Resume a task.
|
258 |
*
|
259 |
* A thread in the task will begin to run only when both
|
260 |
* thread suspend count and task suspend count become 0.
|
261 |
*/
|
262 |
int
|
263 |
task_resume(task_t task) |
264 |
{ |
265 |
list_t head, n; |
266 |
thread_t t; |
267 |
|
268 |
ASSERT(task != curtask); |
269 |
|
270 |
sched_lock(); |
271 |
if (!task_valid(task)) {
|
272 |
sched_unlock(); |
273 |
return ESRCH;
|
274 |
} |
275 |
if (!task_access(task)) {
|
276 |
sched_unlock(); |
277 |
return EPERM;
|
278 |
} |
279 |
if (task->suscnt == 0) { |
280 |
sched_unlock(); |
281 |
return EINVAL;
|
282 |
} |
283 |
|
284 |
if (--task->suscnt == 0) { |
285 |
/*
|
286 |
* Resume all threads in the target task.
|
287 |
*/
|
288 |
head = &task->threads; |
289 |
for (n = list_first(head); n != head; n = list_next(n)) {
|
290 |
t = list_entry(n, struct thread, task_link);
|
291 |
thread_resume(t); |
292 |
} |
293 |
} |
294 |
sched_unlock(); |
295 |
return 0; |
296 |
} |
297 |
|
298 |
/*
|
299 |
* Set task name.
|
300 |
*
|
301 |
* The naming service is separated from task_create() because
|
302 |
* the task name can be changed at anytime by exec().
|
303 |
*/
|
304 |
int
|
305 |
task_setname(task_t task, const char *name) |
306 |
{ |
307 |
char str[MAXTASKNAME];
|
308 |
int error;
|
309 |
|
310 |
sched_lock(); |
311 |
if (!task_valid(task)) {
|
312 |
sched_unlock(); |
313 |
return ESRCH;
|
314 |
} |
315 |
if (!task_access(task)) {
|
316 |
sched_unlock(); |
317 |
return EPERM;
|
318 |
} |
319 |
|
320 |
if (curtask->flags & TF_SYSTEM)
|
321 |
strlcpy(task->name, name, MAXTASKNAME); |
322 |
else {
|
323 |
error = copyinstr(name, str, MAXTASKNAME); |
324 |
if (error) {
|
325 |
sched_unlock(); |
326 |
return error;
|
327 |
} |
328 |
strlcpy(task->name, str, MAXTASKNAME); |
329 |
} |
330 |
sched_unlock(); |
331 |
return 0; |
332 |
} |
333 |
|
334 |
/*
|
335 |
* Set the capability of the specified task.
|
336 |
*/
|
337 |
int
|
338 |
task_setcap(task_t task, cap_t cap) |
339 |
{ |
340 |
|
341 |
if (!task_capable(CAP_SETPCAP))
|
342 |
return EPERM;
|
343 |
|
344 |
sched_lock(); |
345 |
if (!task_valid(task)) {
|
346 |
sched_unlock(); |
347 |
return ESRCH;
|
348 |
} |
349 |
if (!task_access(task)) {
|
350 |
sched_unlock(); |
351 |
return EPERM;
|
352 |
} |
353 |
task->capability = cap; |
354 |
sched_unlock(); |
355 |
return 0; |
356 |
} |
357 |
|
358 |
/*
|
359 |
* task_chkcap - system call to check task capability.
|
360 |
*/
|
361 |
int
|
362 |
task_chkcap(task_t task, cap_t cap) |
363 |
{ |
364 |
int error = 0; |
365 |
|
366 |
sched_lock(); |
367 |
if (!task_valid(task)) {
|
368 |
sched_unlock(); |
369 |
return ESRCH;
|
370 |
} |
371 |
if ((task->capability & cap) == 0) { |
372 |
DPRINTF(("Denying capability by %s: task=%s cap=%08x\n",
|
373 |
curtask->name, task->name, cap)); |
374 |
if (task->flags & TF_AUDIT)
|
375 |
panic("audit failed");
|
376 |
error = EPERM; |
377 |
} |
378 |
sched_unlock(); |
379 |
return error;
|
380 |
} |
381 |
|
382 |
/*
|
383 |
* Check if the current task has specified capability.
|
384 |
* Returns true on success, or false on error.
|
385 |
*/
|
386 |
int
|
387 |
task_capable(cap_t cap) |
388 |
{ |
389 |
int capable = 1; |
390 |
|
391 |
if ((curtask->capability & cap) == 0) { |
392 |
DPRINTF(("Denying capability by kernel: task=%s cap=%08x\n",
|
393 |
curtask->name, cap)); |
394 |
if (curtask->flags & TF_AUDIT)
|
395 |
panic("audit failed");
|
396 |
capable = 0;
|
397 |
} |
398 |
return capable;
|
399 |
} |
400 |
|
401 |
/*
|
402 |
* Return true if the specified task is valid.
|
403 |
*/
|
404 |
int
|
405 |
task_valid(task_t task) |
406 |
{ |
407 |
task_t tmp; |
408 |
list_t n; |
409 |
|
410 |
for (n = list_first(&task_list); n != &task_list; n = list_next(n)) {
|
411 |
tmp = list_entry(n, struct task, link);
|
412 |
if (tmp == task)
|
413 |
return 1; |
414 |
} |
415 |
return 0; |
416 |
} |
417 |
|
418 |
/*
|
419 |
* Check if the current task can access the specified task.
|
420 |
* Return true on success, or false on error.
|
421 |
*/
|
422 |
int
|
423 |
task_access(task_t task) |
424 |
{ |
425 |
|
426 |
if (task->flags & TF_SYSTEM) {
|
427 |
/* Do not access the kernel task. */
|
428 |
return 0; |
429 |
} else {
|
430 |
if (task == curtask || task->parent == curtask ||
|
431 |
task == curtask->parent || /* XXX: fork on nommu */
|
432 |
task_capable(CAP_TASKCTRL)) |
433 |
return 1; |
434 |
} |
435 |
return 0; |
436 |
} |
437 |
|
438 |
int
|
439 |
task_info(struct taskinfo *info)
|
440 |
{ |
441 |
u_long target = info->cookie; |
442 |
u_long i = 0;
|
443 |
task_t task; |
444 |
list_t n; |
445 |
|
446 |
sched_lock(); |
447 |
n = list_first(&task_list); |
448 |
do {
|
449 |
if (i++ == target) {
|
450 |
task = list_entry(n, struct task, link);
|
451 |
info->cookie = i; |
452 |
info->id = task; |
453 |
info->flags = task->flags; |
454 |
info->suscnt = task->suscnt; |
455 |
info->capability = task->capability; |
456 |
info->vmsize = task->map->total; |
457 |
info->nthreads = task->nthreads; |
458 |
info->active = (task == curtask) ? 1 : 0; |
459 |
strlcpy(info->taskname, task->name, MAXTASKNAME); |
460 |
sched_unlock(); |
461 |
return 0; |
462 |
} |
463 |
n = list_next(n); |
464 |
} while (n != &task_list);
|
465 |
sched_unlock(); |
466 |
return ESRCH;
|
467 |
} |
468 |
|
469 |
/*
|
470 |
* Create and setup boot tasks.
|
471 |
*/
|
472 |
void
|
473 |
task_bootstrap(void)
|
474 |
{ |
475 |
struct module *mod;
|
476 |
struct bootinfo *bi;
|
477 |
task_t task; |
478 |
thread_t t; |
479 |
void *stack, *sp;
|
480 |
int i, error = 0; |
481 |
|
482 |
machine_bootinfo(&bi); |
483 |
mod = &bi->tasks[0];
|
484 |
|
485 |
for (i = 0; i < bi->nr_tasks; i++) { |
486 |
/*
|
487 |
* Create a new task.
|
488 |
*/
|
489 |
if ((error = task_create(&kernel_task, VM_NEW, &task)) != 0) |
490 |
break;
|
491 |
if ((error = vm_load(task->map, mod, &stack)) != 0) |
492 |
break;
|
493 |
task_setname(task, mod->name); |
494 |
|
495 |
/*
|
496 |
* Set the default capability.
|
497 |
* We give CAP_SETPCAP to the exec server.
|
498 |
*/
|
499 |
task->capability = CAPSET_BOOT; |
500 |
if (!strncmp(task->name, "exec", MAXTASKNAME)) |
501 |
task->capability |= CAP_SETPCAP; |
502 |
|
503 |
/*
|
504 |
* Create and start a new thread.
|
505 |
*/
|
506 |
if ((error = thread_create(task, &t)) != 0) |
507 |
break;
|
508 |
sp = (char *)stack + DFLSTKSZ - (sizeof(int) * 3); |
509 |
error = thread_load(t, (void (*)(void))mod->entry, sp); |
510 |
if (error)
|
511 |
break;
|
512 |
t->priority = PRI_REALTIME; |
513 |
t->basepri = PRI_REALTIME; |
514 |
thread_resume(t); |
515 |
|
516 |
mod++; |
517 |
} |
518 |
if (error) {
|
519 |
DPRINTF(("task_bootstrap: error=%d\n", error));
|
520 |
panic("unable to load boot task");
|
521 |
} |
522 |
} |
523 |
|
524 |
/*
|
525 |
* Initialize task.
|
526 |
*/
|
527 |
void
|
528 |
task_init(void)
|
529 |
{ |
530 |
|
531 |
list_init(&task_list); |
532 |
|
533 |
/*
|
534 |
* Create a kernel task as first task.
|
535 |
*/
|
536 |
strlcpy(kernel_task.name, "kernel", MAXTASKNAME);
|
537 |
kernel_task.flags = TF_SYSTEM; |
538 |
kernel_task.nthreads = 0;
|
539 |
list_init(&kernel_task.threads); |
540 |
list_init(&kernel_task.objects); |
541 |
list_init(&kernel_task.mutexes); |
542 |
list_init(&kernel_task.conds); |
543 |
list_init(&kernel_task.sems); |
544 |
|
545 |
list_insert(&task_list, &kernel_task.link); |
546 |
ntasks = 1;
|
547 |
} |