Project

General

Profile

Statistics
| Branch: | Revision:

root / prex-0.9.0 / sys / kern / task.c @ 03e9c04a

History | View | Annotate | Download (10.8 KB)

1 03e9c04a Brad Neuman
/*-
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
}