version 1.1.1.1, 2008/06/03 10:38:46 |
version 1.1.1.1.2.1, 2008/08/13 17:12:31 |
|
|
|
|
/* |
/* |
* Install an exception handler for the current task. |
* Install an exception handler for the current task. |
|
* |
* NULL can be specified as handler to remove current handler. |
* NULL can be specified as handler to remove current handler. |
* If handler is removed, all pending exceptions are discarded |
* If handler is removed, all pending exceptions are discarded |
* immediately. In this case, all threads blocked in exception_wait() |
* immediately. In this case, all threads blocked in |
* are unblocked. |
* exception_wait() are unblocked. |
* |
* |
* Only one exception handler can be set per task. If the previous |
* Only one exception handler can be set per task. If the |
* handler exists in task, exception_setup() just override that |
* previous handler exists in task, exception_setup() just |
* handler. |
* override that handler. |
*/ |
*/ |
int |
int |
exception_setup(void (*handler)(int, u_long)) |
exception_setup(void (*handler)(int)) |
{ |
{ |
task_t self; |
task_t self = cur_task(); |
list_t head, n; |
list_t head, n; |
thread_t th; |
thread_t th; |
|
|
|
|
return EFAULT; |
return EFAULT; |
|
|
sched_lock(); |
sched_lock(); |
self = cur_task(); |
if (self->handler && handler == NULL) { |
if (self->exc_handler && handler == NULL) { |
|
/* |
/* |
* Remove existing exception handler. Do clean up |
* Remove existing exception handler. Do clean up |
* job for all threads in the target task. |
* job for all threads in the target task. |
|
|
*/ |
*/ |
th = list_entry(n, struct thread, task_link); |
th = list_entry(n, struct thread, task_link); |
irq_lock(); |
irq_lock(); |
th->exc_bitmap = 0; |
th->excbits = 0; |
irq_unlock(); |
irq_unlock(); |
|
|
/* |
/* |
* If the thread is waiting for an exception, |
* If the thread is waiting for an exception, |
* cancel it. |
* cancel it. |
*/ |
*/ |
if (th->sleep_event == &exception_event) |
if (th->slpevt == &exception_event) |
sched_unsleep(th, SLP_BREAK); |
sched_unsleep(th, SLP_BREAK); |
} |
} |
} |
} |
self->exc_handler = handler; |
self->handler = handler; |
sched_unlock(); |
sched_unlock(); |
return 0; |
return 0; |
} |
} |
|
|
/* |
/* |
* exception_raise - raise an exception for specified task. |
* exception_raise - system call to raise an exception. |
* @task: task id |
|
* @exc: exception code |
|
* |
* |
* The exception pending flag is marked here, and it is processed |
* The exception pending flag is marked here, and it is |
* by exception_deliver() later. If the task want to raise an |
* processed by exception_deliver() later. If the task |
* exception to another task, the caller task must have CAP_KILL |
* want to raise an exception to another task, the caller |
* capability. If the exception is sent to the kernel task, this |
* task must have CAP_KILL capability. If the exception |
* routine just returns error. |
* is sent to the kernel task, this routine just returns |
|
* error. |
*/ |
*/ |
int |
int |
exception_raise(task_t task, int exc) |
exception_raise(task_t task, int exc) |
|
|
|
|
if (!task_valid(task)) { |
if (!task_valid(task)) { |
err = ESRCH; |
err = ESRCH; |
} else if (task != cur_task() && !task_capable(CAP_KILL)) { |
goto out; |
|
} |
|
if (task != cur_task() && !task_capable(CAP_KILL)) { |
err = EPERM; |
err = EPERM; |
} else if (task == &kern_task || task->exc_handler == NULL || |
goto out; |
list_empty(&task->threads)) { |
} |
|
if (task == &kern_task || task->handler == NULL || |
|
list_empty(&task->threads)) { |
err = EPERM; |
err = EPERM; |
} else { |
goto out; |
err = exception_post(task, exc); |
|
} |
} |
|
err = exception_post(task, exc); |
|
out: |
sched_unlock(); |
sched_unlock(); |
return err; |
return err; |
} |
} |
|
|
/* |
/* |
* Post an exception to the specified task. |
* exception_post-- the internal version of exception_raise(). |
*/ |
*/ |
int |
int |
exception_post(task_t task, int exc) |
exception_post(task_t task, int exc) |
|
|
list_t head, n; |
list_t head, n; |
thread_t th; |
thread_t th; |
|
|
if (exc < 0 || exc >= NR_EXCS) |
if (exc < 0 || exc >= NEXC) |
return EINVAL; |
return EINVAL; |
|
|
/* |
/* |
* Determine which thread should we send an exception. |
* Determine which thread should we send an exception. |
* First, search the thread that is waiting an exception by |
* First, search the thread that is waiting an exception |
* calling exception_wait(). Then, if no thread is waiting |
* by calling exception_wait(). Then, if no thread is |
* exceptions, it is sent to the master thread in task. |
* waiting exceptions, it is sent to the master thread in |
|
* task. |
*/ |
*/ |
head = &task->threads; |
head = &task->threads; |
for (n = list_first(head); n != head; n = list_next(n)) { |
for (n = list_first(head); n != head; n = list_next(n)) { |
th = list_entry(n, struct thread, task_link); |
th = list_entry(n, struct thread, task_link); |
if (th->sleep_event == &exception_event) |
if (th->slpevt == &exception_event) |
break; |
break; |
} |
} |
if (n == head) { |
if (n == head) { |
|
|
* Mark pending bit for this exception. |
* Mark pending bit for this exception. |
*/ |
*/ |
irq_lock(); |
irq_lock(); |
th->exc_bitmap |= (1 << exc); |
th->excbits |= (1 << exc); |
irq_unlock(); |
irq_unlock(); |
|
|
/* |
/* |
* Wakeup the target thread regardless of its waiting |
* Wakeup the target thread regardless of its |
* event. |
* waiting event. |
*/ |
*/ |
sched_unsleep(th, SLP_INTR); |
sched_unsleep(th, SLP_INTR); |
|
|
|
|
} |
} |
|
|
/* |
/* |
* exception_wait - block a current thread until some exceptions are |
* exception_wait - block a current thread until some exceptions |
* raised to the current thread. |
* are raised to the current thread. |
* @exc: exception code returned. |
|
* |
* |
* The routine returns EINTR on success. |
* The routine returns EINTR on success. |
*/ |
*/ |
int |
int |
exception_wait(int *exc) |
exception_wait(int *exc) |
{ |
{ |
|
task_t self = cur_task(); |
int i, rc; |
int i, rc; |
|
|
if (cur_task()->exc_handler == NULL) |
self = cur_task(); |
|
if (self->handler == NULL) |
return EINVAL; |
return EINVAL; |
if (!user_area(exc)) |
if (!user_area(exc)) |
return EFAULT; |
return EFAULT; |
|
|
return EINVAL; |
return EINVAL; |
} |
} |
irq_lock(); |
irq_lock(); |
for (i = 0; i < NR_EXCS; i++) { |
for (i = 0; i < NEXC; i++) { |
if (cur_thread->exc_bitmap & (1 << i)) |
if (cur_thread->excbits & (1 << i)) |
break; |
break; |
} |
} |
irq_unlock(); |
irq_unlock(); |
ASSERT(i != NR_EXCS); |
ASSERT(i != NEXC); |
sched_unlock(); |
sched_unlock(); |
|
|
if (umem_copyout(&i, exc, sizeof(int))) |
if (umem_copyout(&i, exc, sizeof(i))) |
return EFAULT; |
return EFAULT; |
return EINTR; |
return EINTR; |
} |
} |
|
|
/* |
/* |
* Mark an exception flag for the current thread. |
* Mark an exception flag for the current thread. |
* |
* |
* This is called from architecture dependent code when H/W trap is |
* This is called from architecture dependent code when H/W |
* occurred. If current task does not have exception handler, then |
* trap is occurred. If current task does not have exception |
* current task will be terminated. |
* handler, then current task will be terminated. This routine |
* This routine may be called at interrupt level. |
* may be called at interrupt level. |
*/ |
*/ |
void |
void |
exception_mark(int exc) |
exception_mark(int exc) |
{ |
{ |
ASSERT(exc > 0 && exc < NR_EXCS); |
ASSERT(exc > 0 && exc < NEXC); |
|
|
/* Mark pending bit */ |
/* Mark pending bit */ |
irq_lock(); |
irq_lock(); |
cur_thread->exc_bitmap |= (1 << exc); |
cur_thread->excbits |= (1 << exc); |
irq_unlock(); |
irq_unlock(); |
} |
} |
|
|
/* |
/* |
* Check if pending exception exists for current task, and deliver |
* exception_deliver - deliver pending exception to the task. |
* it to the exception handler if needed. |
* |
* All exception is delivered at the time when the control goes back |
* Check if pending exception exists for current task, and |
* to the user mode. |
* deliver it to the exception handler if needed. All |
* This routine is called from architecture dependent code. |
* exception is delivered at the time when the control goes |
* Some application may use longjmp() during its signal handler. |
* back to the user mode. This routine is called from |
* So, current context must be saved to user mode stack. |
* architecture dependent code. Some application may use |
|
* longjmp() during its signal handler. So, current context |
|
* must be saved to user mode stack. |
*/ |
*/ |
void |
void |
exception_deliver(void) |
exception_deliver(void) |
{ |
{ |
thread_t th = cur_thread; |
thread_t th = cur_thread; |
task_t self = cur_task(); |
task_t self = cur_task(); |
void (*handler)(int, u_long); |
void (*handler)(int); |
uint32_t bitmap; |
uint32_t bitmap; |
int exc; |
int exc; |
|
|
sched_lock(); |
sched_lock(); |
irq_lock(); |
irq_lock(); |
bitmap = th->exc_bitmap; |
bitmap = th->excbits; |
irq_unlock(); |
irq_unlock(); |
|
|
if (bitmap != 0) { |
if (bitmap != 0) { |
/* |
/* |
* Find a pending exception. |
* Find a pending exception. |
*/ |
*/ |
for (exc = 0; exc < NR_EXCS; exc++) { |
for (exc = 0; exc < NEXC; exc++) { |
if (bitmap & (1 << exc)) |
if (bitmap & (1 << exc)) |
break; |
break; |
} |
} |
handler = self->exc_handler; |
handler = self->handler; |
if (handler == NULL) { |
if (handler == NULL) { |
printk("Exception #%d is not handled by task.\n", exc); |
DPRINTF(("Exception #%d is not handled by task.\n", |
printk("Terminate task:%s (id:%x)\n", |
exc)); |
self->name ? self->name : "no name", self); |
DPRINTF(("Terminate task:%s (id:%x)\n", |
|
self->name != NULL ? self->name : "no name", |
|
self)); |
|
|
task_terminate(self); |
task_terminate(self); |
goto out; |
goto out; |
} |
} |
/* |
/* |
* Transfer control to an exception handler. |
* Transfer control to an exception handler. |
*/ |
*/ |
context_save(&th->context, exc); |
context_save(&th->ctx); |
context_set(&th->context, CTX_UENTRY, (u_long)handler); |
context_set(&th->ctx, CTX_UENTRY, (vaddr_t)handler); |
|
context_set(&th->ctx, CTX_UARG, (vaddr_t)exc); |
|
|
irq_lock(); |
irq_lock(); |
th->exc_bitmap &= ~(1 << exc); |
th->excbits &= ~(1 << exc); |
irq_unlock(); |
irq_unlock(); |
} |
} |
out: |
out: |
|
|
} |
} |
|
|
/* |
/* |
* exception_return() is called from exception handler to restore |
* exception_return() is called from exception handler to |
* the original context. |
* restore the original context. |
* @regs: context pointer which is passed to exception handler. |
|
* |
|
* TODO: should validate passed data area. |
|
*/ |
*/ |
int |
int |
exception_return(void *regs) |
exception_return(void) |
{ |
{ |
|
|
if ((regs == NULL) || !user_area(regs)) |
context_restore(&cur_thread->ctx); |
return EFAULT; |
|
|
|
context_restore(&cur_thread->context, regs); |
|
return 0; |
return 0; |
} |
} |
|
|