Statistics
| Branch: | Revision:

root / prex-0.9.0 / sys / kern / exception.c @ 03e9c04a

History | View | Annotate | Download (9.02 KB)

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
}