scoutos / 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 |
} |