Annotation of prex/sys/kern/exception.c, Revision 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