[BACK]Return to exception.c CVS log [TXT][DIR] Up to [local] / prex / sys / kern

Annotation of prex/sys/kern/exception.c, Revision 1.1.1.1

1.1       nbrk        1: /*-
                      2:  * Copyright (c) 2005-2007, 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:  * exception.c - exception handling routines
                     32:  */
                     33:
                     34: /**
                     35:  * An user mode task can specify its own exception handler with
                     36:  * exception_setup() system call.
                     37:  *
                     38:  * There are two different types of exceptions in a system - H/W and
                     39:  * S/W exception. The kernel determines to which thread it delivers
                     40:  * depending on the exception type.
                     41:  *
                     42:  *  - H/W exception
                     43:  *
                     44:  *   This type of exception is caused by H/W trap & fault. The
                     45:  *   exception will be sent to the thread which caused the trap.
                     46:  *   If no handler is specified by the task, it will be terminated
                     47:  *   by the kernel immediately.
                     48:  *
                     49:  *  - S/W exception
                     50:  *
                     51:  *   The user mode task can send S/W exception to another task by
                     52:  *   exception_raise() system call.
                     53:  *   The exception  will be sent to the thread that is sleeping with
                     54:  *   exception_wait() call. If no thread is waiting for the exception,
                     55:  *   the exception is sent to the first thread in the target task.
                     56:  *
                     57:  * Kernel supports 32 types of exceptions. The following pre-defined
                     58:  * exceptions are raised by kernel itself.
                     59:  *
                     60:  *   Exception Type Reason
                     61:  *   --------- ---- -----------------------
                     62:  *   SIGILL    h/w  illegal instruction
                     63:  *   SIGTRAP   h/w  break point
                     64:  *   SIGFPE    h/w  math error
                     65:  *   SIGSEGV   h/w  invalid memory access
                     66:  *   SIGALRM   s/w  alarm event
                     67:  *
                     68:  * The POSIX emulation library will setup own exception handler to
                     69:  * convert the Prex exceptions into UNIX signals. It will maintain its
                     70:  * own signal mask, and transfer control to the POSIX signal handler.
                     71:  */
                     72:
                     73: #include <kernel.h>
                     74: #include <event.h>
                     75: #include <task.h>
                     76: #include <thread.h>
                     77: #include <sched.h>
                     78: #include <task.h>
                     79: #include <irq.h>
                     80: #include <exception.h>
                     81:
                     82: static struct event exception_event;
                     83:
                     84: /*
                     85:  * Install an exception handler for the current task.
                     86:  *
                     87:  * NULL can be specified as handler to remove current handler.
                     88:  * If handler is removed, all pending exceptions are discarded
                     89:  * immediately. In this case, all threads blocked in
                     90:  * exception_wait() are unblocked.
                     91:  *
                     92:  * Only one exception handler can be set per task. If the
                     93:  * previous handler exists in task, exception_setup() just
                     94:  * override that handler.
                     95:  */
                     96: int
                     97: exception_setup(void (*handler)(int))
                     98: {
                     99:        task_t self = cur_task();
                    100:        list_t head, n;
                    101:        thread_t th;
                    102:
                    103:        if (handler != NULL && !user_area(handler))
                    104:                return EFAULT;
                    105:
                    106:        sched_lock();
                    107:        if (self->handler && handler == NULL) {
                    108:                /*
                    109:                 * Remove existing exception handler. Do clean up
                    110:                 * job for all threads in the target task.
                    111:                 */
                    112:                head = &self->threads;
                    113:                for (n = list_first(head); n != head; n = list_next(n)) {
                    114:                        /*
                    115:                         * Clear pending exceptions.
                    116:                         */
                    117:                        th = list_entry(n, struct thread, task_link);
                    118:                        irq_lock();
                    119:                        th->excbits = 0;
                    120:                        irq_unlock();
                    121:
                    122:                        /*
                    123:                         * If the thread is waiting for an exception,
                    124:                         * cancel it.
                    125:                         */
                    126:                        if (th->slpevt == &exception_event)
                    127:                                sched_unsleep(th, SLP_BREAK);
                    128:                }
                    129:        }
                    130:        self->handler = handler;
                    131:        sched_unlock();
                    132:        return 0;
                    133: }
                    134:
                    135: /*
                    136:  * exception_raise - system call to raise an exception.
                    137:  *
                    138:  * The exception pending flag is marked here, and it is
                    139:  * processed by exception_deliver() later. If the task
                    140:  * want to raise an exception to another task, the caller
                    141:  * task must have CAP_KILL capability. If the exception
                    142:  * is sent to the kernel task, this routine just returns
                    143:  * error.
                    144:  */
                    145: int
                    146: exception_raise(task_t task, int exc)
                    147: {
                    148:        int err;
                    149:
                    150:        sched_lock();
                    151:
                    152:        if (!task_valid(task)) {
                    153:                err = ESRCH;
                    154:                goto out;
                    155:        }
                    156:        if (task != cur_task() && !task_capable(CAP_KILL)) {
                    157:                err = EPERM;
                    158:                goto out;
                    159:        }
                    160:        if (task == &kern_task || task->handler == NULL ||
                    161:            list_empty(&task->threads)) {
                    162:                err = EPERM;
                    163:                goto out;
                    164:        }
                    165:        err = exception_post(task, exc);
                    166:  out:
                    167:        sched_unlock();
                    168:        return err;
                    169: }
                    170:
                    171: /*
                    172:  * exception_post-- the internal version of exception_raise().
                    173:  */
                    174: int
                    175: exception_post(task_t task, int exc)
                    176: {
                    177:        list_t head, n;
                    178:        thread_t th;
                    179:
                    180:        if (exc < 0 || exc >= NEXC)
                    181:                return EINVAL;
                    182:
                    183:        /*
                    184:         * Determine which thread should we send an exception.
                    185:         * First, search the thread that is waiting an exception
                    186:         * by calling exception_wait(). Then, if no thread is
                    187:         * waiting exceptions, it is sent to the master thread in
                    188:         * task.
                    189:         */
                    190:        head = &task->threads;
                    191:        for (n = list_first(head); n != head; n = list_next(n)) {
                    192:                th = list_entry(n, struct thread, task_link);
                    193:                if (th->slpevt == &exception_event)
                    194:                        break;
                    195:        }
                    196:        if (n == head) {
                    197:                n = list_first(head);
                    198:                th = list_entry(n, struct thread, task_link);
                    199:        }
                    200:        /*
                    201:         * Mark pending bit for this exception.
                    202:         */
                    203:        irq_lock();
                    204:        th->excbits |= (1 << exc);
                    205:        irq_unlock();
                    206:
                    207:        /*
                    208:         * Wakeup the target thread regardless of its
                    209:         * waiting event.
                    210:         */
                    211:        sched_unsleep(th, SLP_INTR);
                    212:
                    213:        return 0;
                    214: }
                    215:
                    216: /*
                    217:  * exception_wait - block a current thread until some exceptions
                    218:  * are raised to the current thread.
                    219:  *
                    220:  * The routine returns EINTR on success.
                    221:  */
                    222: int
                    223: exception_wait(int *exc)
                    224: {
                    225:        task_t self = cur_task();
                    226:        int i, rc;
                    227:
                    228:        self = cur_task();
                    229:        if (self->handler == NULL)
                    230:                return EINVAL;
                    231:        if (!user_area(exc))
                    232:                return EFAULT;
                    233:
                    234:        sched_lock();
                    235:
                    236:        /*
                    237:         * Sleep until some exceptions occur.
                    238:         */
                    239:        rc = sched_sleep(&exception_event);
                    240:        if (rc == SLP_BREAK) {
                    241:                sched_unlock();
                    242:                return EINVAL;
                    243:        }
                    244:        irq_lock();
                    245:        for (i = 0; i < NEXC; i++) {
                    246:                if (cur_thread->excbits & (1 << i))
                    247:                        break;
                    248:        }
                    249:        irq_unlock();
                    250:        ASSERT(i != NEXC);
                    251:        sched_unlock();
                    252:
                    253:        if (umem_copyout(&i, exc, sizeof(i)))
                    254:                return EFAULT;
                    255:        return EINTR;
                    256: }
                    257:
                    258: /*
                    259:  * Mark an exception flag for the current thread.
                    260:  *
                    261:  * This is called from architecture dependent code when H/W
                    262:  * trap is occurred. If current task does not have exception
                    263:  * handler, then current task will be terminated. This routine
                    264:  * may be called at interrupt level.
                    265:  */
                    266: void
                    267: exception_mark(int exc)
                    268: {
                    269:        ASSERT(exc > 0 && exc < NEXC);
                    270:
                    271:        /* Mark pending bit */
                    272:        irq_lock();
                    273:        cur_thread->excbits |= (1 << exc);
                    274:        irq_unlock();
                    275: }
                    276:
                    277: /*
                    278:  * exception_deliver - deliver pending exception to the task.
                    279:  *
                    280:  * Check if pending exception exists for current task, and
                    281:  * deliver it to the exception handler if needed. All
                    282:  * exception is delivered at the time when the control goes
                    283:  * back to the user mode. This routine is called from
                    284:  * architecture dependent code. Some application may use
                    285:  * longjmp() during its signal handler. So, current context
                    286:  * must be saved to user mode stack.
                    287:  */
                    288: void
                    289: exception_deliver(void)
                    290: {
                    291:        thread_t th = cur_thread;
                    292:        task_t self = cur_task();
                    293:        void (*handler)(int);
                    294:        uint32_t bitmap;
                    295:        int exc;
                    296:
                    297:        sched_lock();
                    298:        irq_lock();
                    299:        bitmap = th->excbits;
                    300:        irq_unlock();
                    301:
                    302:        if (bitmap != 0) {
                    303:                /*
                    304:                 * Find a pending exception.
                    305:                 */
                    306:                for (exc = 0; exc < NEXC; exc++) {
                    307:                        if (bitmap & (1 << exc))
                    308:                                break;
                    309:                }
                    310:                handler = self->handler;
                    311:                if (handler == NULL) {
                    312:                        DPRINTF(("Exception #%d is not handled by task.\n",
                    313:                                exc));
                    314:                        DPRINTF(("Terminate task:%s (id:%x)\n",
                    315:                                 self->name != NULL ? self->name : "no name",
                    316:                                 self));
                    317:
                    318:                        task_terminate(self);
                    319:                        goto out;
                    320:                }
                    321:                /*
                    322:                 * Transfer control to an exception handler.
                    323:                 */
                    324:                context_save(&th->ctx);
                    325:                context_set(&th->ctx, CTX_UENTRY, (vaddr_t)handler);
                    326:                context_set(&th->ctx, CTX_UARG, (vaddr_t)exc);
                    327:
                    328:                irq_lock();
                    329:                th->excbits &= ~(1 << exc);
                    330:                irq_unlock();
                    331:        }
                    332:  out:
                    333:        sched_unlock();
                    334: }
                    335:
                    336: /*
                    337:  * exception_return() is called from exception handler to
                    338:  * restore the original context.
                    339:  */
                    340: int
                    341: exception_return(void)
                    342: {
                    343:
                    344:        context_restore(&cur_thread->ctx);
                    345:        return 0;
                    346: }
                    347:
                    348: void
                    349: exception_init(void)
                    350: {
                    351:
                    352:        event_init(&exception_event, "exception");
                    353: }

CVSweb