version 1.1.1.1, 2008/06/03 10:38:46 |
version 1.1.1.1.2.1, 2008/08/13 17:12:32 |
|
|
* Kernel task. |
* Kernel task. |
* kern_task acts as a list head of all tasks in the system. |
* kern_task acts as a list head of all tasks in the system. |
*/ |
*/ |
struct task kern_task; |
struct task kern_task; |
|
|
/** |
/** |
* task_create - create a new task. |
* task_create - create a new task. |
|
|
* Task name No |
* Task name No |
* Object list No |
* Object list No |
* Threads No |
* Threads No |
* Memory map New/Duplicate/Share |
* Memory map New/Copy/Share |
* Suspend count No |
* Suspend count No |
* Exception handler Yes |
* Exception handler Yes |
* Capability Yes |
* Capability Yes |
* |
* |
* If vm_option is VM_COPY, the child task will have the same memory |
* vm_option: |
* image with the parent task. Especially, text region and read-only |
* VM_NEW: The child task will have clean memory image. |
* region are physically shared among them. VM_COPY is supported only |
* VM_SHARE: The child task will share whole memory image with parent. |
* with MMU system. |
* VM_COPY: The parent's memory image is copied to the child's one. |
* The child task initially contains no threads. |
* However, the text region and read-only region will be |
|
* physically shared among them. VM_COPY is supported only |
|
* with MMU system. |
|
* |
|
* Note: The child task initially contains no threads. |
*/ |
*/ |
int |
int |
task_create(task_t parent, int vm_option, task_t *child) |
task_create(task_t parent, int vm_option, task_t *child) |
|
|
goto out; |
goto out; |
} |
} |
if (cur_task() != &kern_task) { |
if (cur_task() != &kern_task) { |
if(!task_access(parent)) { |
if (!task_access(parent)) { |
err = EPERM; |
err = EPERM; |
goto out; |
goto out; |
} |
} |
/* |
/* |
* Set zero as child task id before copying parent's |
* Set zero as child task id before copying |
* memory space. So, the child task can identify |
* parent's memory space. So, the child task |
* whether it is a child. |
* can identify whether it is a child. |
*/ |
*/ |
task = 0; |
task = 0; |
if (umem_copyout(&task, child, sizeof(task_t))) { |
if (umem_copyout(&task, child, sizeof(task))) { |
err = EFAULT; |
err = EFAULT; |
goto out; |
goto out; |
} |
} |
|
|
case VM_COPY: |
case VM_COPY: |
map = vm_fork(parent->map); |
map = vm_fork(parent->map); |
break; |
break; |
|
default: |
|
/* DO NOTHING */ |
|
break; |
} |
} |
if (map == NULL) { |
if (map == NULL) { |
kmem_free(task); |
kmem_free(task); |
err = ENOMEM; |
err = ENOMEM; |
goto out; |
goto out; |
} |
} |
|
|
/* |
/* |
* Fill initial task data. |
* Fill initial task data. |
*/ |
*/ |
task->map = map; |
task->map = map; |
task->exc_handler = parent->exc_handler; |
task->handler = parent->handler; |
task->capability = parent->capability; |
task->capability = parent->capability; |
task->parent = parent; |
task->parent = parent; |
task->magic = TASK_MAGIC; |
task->magic = TASK_MAGIC; |
|
|
if (cur_task() == &kern_task) |
if (cur_task() == &kern_task) |
*child = task; |
*child = task; |
else |
else |
err = umem_copyout(&task, child, sizeof(task_t)); |
err = umem_copyout(&task, child, sizeof(task)); |
out: |
out: |
sched_unlock(); |
sched_unlock(); |
return err; |
return err; |
|
|
head = &task->objects; |
head = &task->objects; |
for (n = list_first(head); n != head; n = list_next(n)) { |
for (n = list_first(head); n != head; n = list_next(n)) { |
/* |
/* |
* A current task may not have the right to delete |
* A current task may not have the right to |
* target objects. So, we set the owner of the object |
* delete target objects. So, we set the owner |
* to the current task before deleting it. |
* of the object to the current task before |
|
* deleting it. |
*/ |
*/ |
obj = list_entry(n, struct object, task_link); |
obj = list_entry(n, struct object, task_link); |
obj->owner = cur_task(); |
obj->owner = cur_task(); |
|
|
/* |
/* |
* Release all other task related resources. |
* Release all other task related resources. |
*/ |
*/ |
|
timer_stop(&task->alarm); |
vm_terminate(task->map); |
vm_terminate(task->map); |
list_remove(&task->link); |
list_remove(&task->link); |
kmem_free(task); |
kmem_free(task); |
|
|
{ |
{ |
list_t head, n; |
list_t head, n; |
thread_t th; |
thread_t th; |
int err = 0; |
|
|
|
sched_lock(); |
sched_lock(); |
if (!task_valid(task)) { |
if (!task_valid(task)) { |
err = ESRCH; |
sched_unlock(); |
} else if (!task_access(task)) { |
return ESRCH; |
err = EPERM; |
} |
} else if (++task->suspend_count == 1) { |
if (!task_access(task)) { |
|
sched_unlock(); |
|
return EPERM; |
|
} |
|
|
|
if (++task->suscnt == 1) { |
/* |
/* |
* Suspend all threads within the task. |
* Suspend all threads within the task. |
*/ |
*/ |
|
|
} |
} |
} |
} |
sched_unlock(); |
sched_unlock(); |
return err; |
return 0; |
} |
} |
|
|
/* |
/* |
|
|
sched_lock(); |
sched_lock(); |
if (!task_valid(task)) { |
if (!task_valid(task)) { |
err = ESRCH; |
err = ESRCH; |
} else if (!task_access(task)) { |
goto out; |
|
} |
|
if (!task_access(task)) { |
err = EPERM; |
err = EPERM; |
} else if (task->suspend_count == 0) { |
goto out; |
|
} |
|
if (task->suscnt == 0) { |
err = EINVAL; |
err = EINVAL; |
} else if (--task->suspend_count == 0) { |
goto out; |
|
} |
|
if (--task->suscnt == 0) { |
/* |
/* |
* Resume all threads in the target task. |
* Resume all threads in the target task. |
*/ |
*/ |
|
|
thread_resume(th); |
thread_resume(th); |
} |
} |
} |
} |
|
out: |
sched_unlock(); |
sched_unlock(); |
return err; |
return err; |
} |
} |
|
|
sched_lock(); |
sched_lock(); |
if (!task_valid(task)) { |
if (!task_valid(task)) { |
err = ESRCH; |
err = ESRCH; |
} else if (!task_access(task)) { |
goto out; |
|
} |
|
if (!task_access(task)) { |
err = EPERM; |
err = EPERM; |
|
goto out; |
|
} |
|
if (cur_task() == &kern_task) { |
|
strlcpy(task->name, name, MAXTASKNAME); |
} else { |
} else { |
if (cur_task() == &kern_task) |
if (umem_strnlen(name, MAXTASKNAME, &len)) |
strlcpy(task->name, name, MAXTASKNAME); |
err = EFAULT; |
else { |
else if (len >= MAXTASKNAME) |
if (umem_strnlen(name, MAXTASKNAME, &len)) |
err = ENAMETOOLONG; |
err = EFAULT; |
else |
else if (len >= MAXTASKNAME) |
err = umem_copyin(name, task->name, len + 1); |
err = ENAMETOOLONG; |
|
else |
|
err = umem_copyin((void *)name, task->name, |
|
len + 1); |
|
} |
|
} |
} |
|
out: |
sched_unlock(); |
sched_unlock(); |
return err; |
return err; |
} |
} |
|
|
int |
int |
task_getcap(task_t task, cap_t *cap) |
task_getcap(task_t task, cap_t *cap) |
{ |
{ |
cap_t cur_cap; |
cap_t curcap; |
|
|
sched_lock(); |
sched_lock(); |
if (!task_valid(task)) { |
if (!task_valid(task)) { |
sched_unlock(); |
sched_unlock(); |
return ESRCH; |
return ESRCH; |
} |
} |
cur_cap = task->capability; |
curcap = task->capability; |
sched_unlock(); |
sched_unlock(); |
|
|
return umem_copyout(&cur_cap, cap, sizeof(cap_t)); |
return umem_copyout(&curcap, cap, sizeof(cap_t)); |
} |
} |
|
|
/* |
/* |
|
|
int |
int |
task_setcap(task_t task, cap_t *cap) |
task_setcap(task_t task, cap_t *cap) |
{ |
{ |
cap_t new_cap; |
cap_t newcap; |
int err = 0; |
|
|
|
if (!task_capable(CAP_SETPCAP)) |
if (!task_capable(CAP_SETPCAP)) |
return EPERM; |
return EPERM; |
|
|
sched_lock(); |
sched_lock(); |
if (!task_valid(task)) { |
if (!task_valid(task)) { |
err = ESRCH; |
sched_unlock(); |
} else if (umem_copyin(cap, &new_cap, sizeof(cap_t))) { |
return ESRCH; |
err = EFAULT; |
|
} else { |
|
task->capability = new_cap; |
|
} |
} |
|
if (umem_copyin(cap, &newcap, sizeof(cap_t))) { |
|
sched_unlock(); |
|
return EFAULT; |
|
} |
|
task->capability = newcap; |
sched_unlock(); |
sched_unlock(); |
return err; |
return 0; |
} |
} |
|
|
/* |
/* |
* Check if the current task can access the specified task. |
* Check if the current task can access the specified task. |
|
* Return true on success, or false on error. |
*/ |
*/ |
int |
int |
task_access(task_t task) |
task_access(task_t task) |
{ |
{ |
|
|
return (task != &kern_task && |
if (task == &kern_task) |
(task == cur_task() || task->parent == cur_task() || |
return 0; |
task_capable(CAP_TASK))); |
else { |
|
if (task == cur_task() || |
|
task->parent == cur_task() || |
|
task_capable(CAP_TASK)) |
|
return 1; |
|
} |
|
return 0; |
} |
} |
|
|
/* |
/* |
|
|
void |
void |
task_bootstrap(void) |
task_bootstrap(void) |
{ |
{ |
struct module *m; |
struct module *mod; |
task_t task; |
task_t task; |
thread_t th; |
thread_t th; |
void *stack; |
void *stack; |
|
vaddr_t sp; |
int i; |
int i; |
|
|
m = &boot_info->tasks[0]; |
mod = &boot_info->tasks[0]; |
for (i = 0; i < boot_info->nr_tasks; i++, m++) { |
for (i = 0; i < boot_info->nr_tasks; i++) { |
/* |
/* |
* Create a new task. |
* Create a new task. |
*/ |
*/ |
if (task_create(&kern_task, VM_NEW, &task)) |
if (task_create(&kern_task, VM_NEW, &task)) |
break; |
break; |
task_name(task, m->name); |
task_name(task, mod->name); |
if (vm_load(task->map, m, &stack)) |
if (vm_load(task->map, mod, &stack)) |
break; |
break; |
|
|
/* |
/* |
|
|
*/ |
*/ |
if (thread_create(task, &th)) |
if (thread_create(task, &th)) |
break; |
break; |
stack = (void *)((u_long)stack + USTACK_SIZE - sizeof(int)); |
sp = (vaddr_t)stack + USTACK_SIZE - (sizeof(int) * 3); |
if (thread_load(th, (void (*)(void))m->entry, stack)) |
if (thread_load(th, (void (*)(void))mod->entry, (void *)sp)) |
break; |
break; |
thread_resume(th); |
thread_resume(th); |
|
|
|
mod++; |
} |
} |
if (i != boot_info->nr_tasks) |
if (i != boot_info->nr_tasks) |
panic("task_boot"); |
panic("task_bootstrap: unable to load boot task"); |
} |
} |
|
|
#if defined(DEBUG) && defined(CONFIG_KDUMP) |
#ifdef DEBUG |
void |
void |
task_dump(void) |
task_dump(void) |
{ |
{ |
list_t i, j; |
list_t i, j; |
task_t task; |
task_t task; |
int nobjs, nthreads; |
int nthreads; |
|
|
printk("Task dump:\n"); |
printf("\nTask dump:\n"); |
printk(" mod task nobjs nthrds vm map susp exc hdlr " |
printf(" mod task nthrds susp exc hdlr cap name\n"); |
"cap name\n"); |
printf(" --- --------- ------ ---- -------- -------- ------------\n"); |
printk(" --- --------- ------ ------ -------- ---- -------- " |
|
"-------- ------------\n"); |
|
|
|
i = &kern_task.link; |
i = &kern_task.link; |
do { |
do { |
task = list_entry(i, struct task, link); |
task = list_entry(i, struct task, link); |
|
|
nthreads = 0; |
nthreads = 0; |
j = &task->threads; |
j = list_first(&task->threads); |
j = list_next(j); |
|
do { |
do { |
nthreads++; |
nthreads++; |
j = list_next(j); |
j = list_next(j); |
} while (j != &task->threads); |
} while (j != &task->threads); |
|
|
nobjs = 0; |
printf(" %s %08x%c %3d %4d %08x %08x %s\n", |
j = &task->objects; |
(task == &kern_task) ? "Knl" : "Usr", task, |
j = list_next(j); |
(task == cur_task()) ? '*' : ' ', nthreads, |
do { |
task->suscnt, task->handler, task->capability, |
nobjs++; |
task->name != NULL ? task->name : "no name"); |
j = list_next(j); |
|
} while (j != &task->objects); |
|
|
|
printk(" %s %08x%c %3d %3d %08x %4d %08x %08x %s\n", |
|
(task == &kern_task) ? "Knl" : "Usr", |
|
task, (task == cur_task()) ? '*' : ' ', nobjs, |
|
nthreads, task->map, task->suspend_count, |
|
task->exc_handler, task->capability, |
|
task->name ? task->name : "no name"); |
|
|
|
i = list_next(i); |
i = list_next(i); |
} while (i != &kern_task.link); |
} while (i != &kern_task.link); |
} |
} |
|
|
void |
|
boot_dump(void) |
|
{ |
|
struct module *m; |
|
int i; |
|
|
|
printk(" text base data base text size data size bss size " |
|
"task name\n"); |
|
printk(" --------- --------- --------- --------- ---------- " |
|
"----------\n"); |
|
|
|
m = &boot_info->tasks[0]; |
|
for (i = 0; i < boot_info->nr_tasks; i++, m++) { |
|
printk(" %8x %8x %8d %8d %8d %s\n", |
|
m->text, m->data, m->textsz, |
|
m->datasz, m->bsssz, m->name); |
|
} |
|
} |
|
#endif |
#endif |
|
|
|
/* |
|
* We assume the VM mapping of a kernel task has already |
|
* been initialized in vm_init(). |
|
*/ |
void |
void |
task_init(void) |
task_init(void) |
{ |
{ |
/* |
/* |
* Create a kernel task as a first task in the system. |
* Create a kernel task as the first task. |
* |
|
* Note: We assume the VM mapping for a kernel task has |
|
* already been initialized in vm_init(). |
|
*/ |
*/ |
strlcpy(kern_task.name, "kernel", MAXTASKNAME); |
strlcpy(kern_task.name, "kernel", MAXTASKNAME); |
list_init(&kern_task.link); |
list_init(&kern_task.link); |