Prex Home / Browse Source - Prex Version: 0.9.0

root/sys/kern/exception.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. exception_setup
  2. exception_raise
  3. exception_post
  4. exception_wait
  5. exception_mark
  6. exception_deliver
  7. exception_return
  8. exception_init

   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 set 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 <hal.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  * EXC_DFL can be specified as handler to remove the current handler.
  88  * If handler is removed, all pending exceptions are discarded
  89  * immediately. At this time, all threads blocked in exception_wait()
  90  * are automatically unblocked.
  91  *
  92  * We allow only one exception handler per task. If the handler
  93  * has already been set in task, exception_setup() just override
  94  * the previous handler.
  95  */
  96 int
  97 exception_setup(void (*handler)(int))
  98 {
  99         task_t self = curtask;
 100         list_t head, n;
 101         thread_t t;
 102         int s;
 103 
 104         if (handler != EXC_DFL && !user_area(handler))
 105                 return EFAULT;
 106         if (handler == NULL)
 107                 return EINVAL;
 108 
 109         sched_lock();
 110         if (self->handler != EXC_DFL && handler == EXC_DFL) {
 111                 /*
 112                  * Remove existing exception handler. Do clean up
 113                  * job for all threads in the target task.
 114                  */
 115                 head = &self->threads;
 116                 for (n = list_first(head); n != head; n = list_next(n)) {
 117 
 118                         /*
 119                          * Clear pending exceptions.
 120                          */
 121                         s = splhigh();
 122                         t = list_entry(n, struct thread, task_link);
 123                         t->excbits = 0;
 124                         splx(s);
 125 
 126                         /*
 127                          * If the thread is waiting for an exception,
 128                          * cancel it.
 129                          */
 130                         if (t->slpevt == &exception_event) {
 131                                 DPRINTF(("Exception cancelled task=%s\n",
 132                                          self->name));
 133                                 sched_unsleep(t, SLP_BREAK);
 134                         }
 135                 }
 136         }
 137         self->handler = handler;
 138         sched_unlock();
 139         return 0;
 140 }
 141 
 142 /*
 143  * exception_raise - system call to raise an exception.
 144  *
 145  * The exception pending flag is marked here, and it is
 146  * processed by exception_deliver() later. The task must have
 147  * CAP_KILL capability to raise an exception to another task.
 148  */
 149 int
 150 exception_raise(task_t task, int excno)
 151 {
 152         int error;
 153 
 154         sched_lock();
 155         if (!task_valid(task)) {
 156                 DPRINTF(("Bad exception task=%lx\n", (long)task));
 157                 sched_unlock();
 158                 return ESRCH;
 159         }
 160         if (task != curtask && !task_capable(CAP_KILL)) {
 161                 sched_unlock();
 162                 return EPERM;
 163         }
 164         error = exception_post(task, excno);
 165         sched_unlock();
 166         return error;
 167 }
 168 
 169 /*
 170  * exception_post-- the internal version of exception_raise().
 171  */
 172 int
 173 exception_post(task_t task, int excno)
 174 {
 175         list_t head, n;
 176         thread_t t = NULL;
 177         int s, found = 0;
 178 
 179         sched_lock();
 180         if (task->flags & TF_SYSTEM) {
 181                 sched_unlock();
 182                 return EPERM;
 183         }
 184 
 185         if ((task->handler == EXC_DFL) ||
 186             (task->nthreads == 0) ||
 187             (excno < 0) || (excno >= NEXC)) {
 188                 sched_unlock();
 189                 return EINVAL;
 190         }
 191 
 192         /*
 193          * Determine which thread should we send an exception.
 194          * First, search the thread that is currently waiting
 195          * an exception by calling exception_wait().
 196          */
 197         head = &task->threads;
 198         for (n = list_first(head); n != head; n = list_next(n)) {
 199                 t = list_entry(n, struct thread, task_link);
 200                 if (t->slpevt == &exception_event) {
 201                         found = 1;
 202                         break;
 203                 }
 204         }
 205 
 206         /*
 207          * If no thread is waiting exceptions, we send it to
 208          * the master thread in the task.
 209          */
 210         if (!found) {
 211                 if (!list_empty(&task->threads)) {
 212                         n = list_first(&task->threads);
 213                         t = list_entry(n, struct thread, task_link);
 214                 }
 215         }
 216 
 217         /*
 218          * Mark pending bit for this exception.
 219          */
 220         s = splhigh();
 221         t->excbits |= (1 << excno);
 222         splx(s);
 223 
 224         /*
 225          * Wakeup the target thread regardless of its
 226          * waiting event.
 227          */
 228         sched_unsleep(t, SLP_INTR);
 229 
 230         sched_unlock();
 231         return 0;
 232 }
 233 
 234 /*
 235  * exception_wait - block a current thread until some
 236  * exceptions are raised to the current thread.
 237  *
 238  * The routine returns EINTR on success.
 239  */
 240 int
 241 exception_wait(int *excno)
 242 {
 243         int i, rc, s;
 244 
 245         if (curtask->handler == EXC_DFL)
 246                 return EINVAL;
 247 
 248         /* Check fault before sleeping. */
 249         i = 0;
 250         if (copyout(&i, excno, sizeof(i)))
 251                 return EFAULT;
 252 
 253         sched_lock();
 254 
 255         /*
 256          * Sleep until some exceptions occur.
 257          */
 258         rc = sched_sleep(&exception_event);
 259         if (rc == SLP_BREAK) {
 260                 sched_unlock();
 261                 return EINVAL;
 262         }
 263         s = splhigh();
 264         for (i = 0; i < NEXC; i++) {
 265                 if (curthread->excbits & (1 << i))
 266                         break;
 267         }
 268         splx(s);
 269         ASSERT(i != NEXC);
 270         sched_unlock();
 271 
 272         if (copyout(&i, excno, sizeof(i)))
 273                 return EFAULT;
 274         return EINTR;
 275 }
 276 
 277 /*
 278  * Mark an exception flag for the current thread.
 279  *
 280  * This is called by HAL code when H/W trap is occurred. If a
 281  * current task does not have exception handler, then the
 282  * current task will be terminated. This routine can be called
 283  * at interrupt level.
 284  */
 285 void
 286 exception_mark(int excno)
 287 {
 288         int s;
 289 
 290         ASSERT(excno > 0 && excno < NEXC);
 291 
 292         /* Mark pending bit */
 293         s = splhigh();
 294         curthread->excbits |= (1 << excno);
 295         splx(s);
 296 }
 297 
 298 /*
 299  * exception_deliver - deliver pending exception to the task.
 300  *
 301  * Check if pending exception exists for the current task, and
 302  * deliver it to the exception handler if needed. All
 303  * exceptions are delivered at the time when the control goes
 304  * back to the user mode.  Some application may use longjmp()
 305  * during its signal handler. So, current context must be
 306  * saved to the user mode stack.
 307  */
 308 void
 309 exception_deliver(void)
 310 {
 311         task_t self = curtask;
 312         void (*handler)(int);
 313         uint32_t bitmap;
 314         int s, excno;
 315 
 316         ASSERT(curthread->state != TS_EXIT);
 317         sched_lock();
 318 
 319         s = splhigh();
 320         bitmap = curthread->excbits;
 321         splx(s);
 322 
 323         if (bitmap != 0) {
 324                 /*
 325                  * Find a pending exception.
 326                  */
 327                 for (excno = 0; excno < NEXC; excno++) {
 328                         if (bitmap & (1 << excno))
 329                                 break;
 330                 }
 331                 handler = self->handler;
 332                 if (handler == EXC_DFL) {
 333                         DPRINTF(("Exception #%d is not handled by task.\n",
 334                                 excno));
 335                         DPRINTF(("Terminate task:%s (id:%lx)\n",
 336                                  self->name, (long)self));
 337 
 338                         task_terminate(self);
 339                         /* NOTREACHED */
 340                 }
 341 
 342                 /*
 343                  * Transfer control to an exception handler.
 344                  */
 345                 s = splhigh();
 346                 context_save(&curthread->ctx);
 347                 context_set(&curthread->ctx, CTX_UENTRY, (register_t)handler);
 348                 context_set(&curthread->ctx, CTX_UARG, (register_t)excno);
 349                 curthread->excbits &= ~(1 << excno);
 350                 splx(s);
 351         }
 352         sched_unlock();
 353 }
 354 
 355 /*
 356  * exception_return() is called from exception handler to
 357  * restore the original context.
 358  */
 359 void
 360 exception_return(void)
 361 {
 362         int s;
 363 
 364         s = splhigh();
 365         context_restore(&curthread->ctx);
 366         splx(s);
 367 }
 368 
 369 void
 370 exception_init(void)
 371 {
 372 
 373         event_init(&exception_event, "exception");
 374 }

/* [<][>][^][v][top][bottom][index][help] */