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