]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/exception.c
xnu-344.49.tar.gz
[apple/xnu.git] / osfmk / kern / exception.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * @OSF_COPYRIGHT@
27 */
28 /*
29 * Mach Operating System
30 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
31 * All Rights Reserved.
32 *
33 * Permission to use, copy, modify and distribute this software and its
34 * documentation is hereby granted, provided that both the copyright
35 * notice and this permission notice appear in all copies of the
36 * software, derivative works or modified versions, and any portions
37 * thereof, and that both notices appear in supporting documentation.
38 *
39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
42 *
43 * Carnegie Mellon requests users of this software to return to
44 *
45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
46 * School of Computer Science
47 * Carnegie Mellon University
48 * Pittsburgh PA 15213-3890
49 *
50 * any improvements or extensions that they make and grant Carnegie Mellon
51 * the rights to redistribute these changes.
52 */
53 /*
54 */
55
56 #include <mach_kdb.h>
57
58 #include <mach/boolean.h>
59 #include <mach/kern_return.h>
60 #include <mach/message.h>
61 #include <mach/port.h>
62 #include <mach/mig_errors.h>
63 #include <mach/thread_status.h>
64 #include <mach/exception_types.h>
65 #include <ipc/port.h>
66 #include <ipc/ipc_entry.h>
67 #include <ipc/ipc_object.h>
68 #include <ipc/ipc_notify.h>
69 #include <ipc/ipc_space.h>
70 #include <ipc/ipc_pset.h>
71 #include <ipc/ipc_machdep.h>
72 #include <kern/etap_macros.h>
73 #include <kern/counters.h>
74 #include <kern/ipc_tt.h>
75 #include <kern/task.h>
76 #include <kern/thread.h>
77 #include <kern/thread_swap.h>
78 #include <kern/processor.h>
79 #include <kern/sched.h>
80 #include <kern/sched_prim.h>
81 #include <kern/host.h>
82 #include <kern/misc_protos.h>
83 #include <string.h>
84 #include <mach/exc.h>
85
86 #if MACH_KDB
87 #include <ddb/db_trap.h>
88 #endif /* MACH_KDB */
89
90 #if MACH_KDB
91
92 #include <ddb/db_output.h>
93
94 #if iPSC386 || iPSC860
95 boolean_t debug_user_with_kdb = TRUE;
96 #else
97 boolean_t debug_user_with_kdb = FALSE;
98 #endif
99
100 #endif /* MACH_KDB */
101
102 unsigned long c_thr_exc_raise = 0;
103 unsigned long c_thr_exc_raise_state = 0;
104 unsigned long c_thr_exc_raise_state_id = 0;
105 unsigned long c_tsk_exc_raise = 0;
106 unsigned long c_tsk_exc_raise_state = 0;
107 unsigned long c_tsk_exc_raise_state_id = 0;
108
109
110 /*
111 * Routine: exception_deliver
112 * Purpose:
113 * Make an upcall to the exception server provided.
114 * Conditions:
115 * Nothing locked and no resources held.
116 * Called from an exception context, so
117 * thread_exception_return and thread_kdb_return
118 * are possible.
119 * Returns:
120 * If the exception was not handled by this handler
121 */
122 void
123 exception_deliver(
124 exception_type_t exception,
125 exception_data_t code,
126 mach_msg_type_number_t codeCnt,
127 struct exception_action *excp,
128 mutex_t *mutex)
129 {
130 thread_act_t a_self = current_act();
131 ipc_port_t exc_port;
132 int behavior;
133 int flavor;
134 kern_return_t kr;
135
136 /*
137 * Save work if we are terminating.
138 * Just go back to our AST handler.
139 */
140 if (!a_self->active)
141 thread_exception_return();
142
143 /*
144 * Snapshot the exception action data under lock for consistency.
145 * Hold a reference to the port over the exception_raise_* calls
146 * so it can't be destroyed. This seems like overkill, but keeps
147 * the port from disappearing between now and when
148 * ipc_object_copyin_from_kernel is finally called.
149 */
150 mutex_lock(mutex);
151 exc_port = excp->port;
152 if (!IP_VALID(exc_port)) {
153 mutex_unlock(mutex);
154 return;
155 }
156 ip_lock(exc_port);
157 if (!ip_active(exc_port)) {
158 ip_unlock(exc_port);
159 mutex_unlock(mutex);
160 return;
161 }
162 ip_reference(exc_port);
163 exc_port->ip_srights++;
164 ip_unlock(exc_port);
165
166 flavor = excp->flavor;
167 behavior = excp->behavior;
168 mutex_unlock(mutex);
169
170 switch (behavior) {
171 case EXCEPTION_STATE: {
172 mach_msg_type_number_t state_cnt;
173 natural_t state[ THREAD_MACHINE_STATE_MAX ];
174
175 c_thr_exc_raise_state++;
176 state_cnt = state_count[flavor];
177 kr = thread_getstatus(a_self, flavor,
178 (thread_state_t)state,
179 &state_cnt);
180 if (kr == KERN_SUCCESS) {
181 kr = exception_raise_state(exc_port, exception,
182 code, codeCnt,
183 &flavor,
184 state, state_cnt,
185 state, &state_cnt);
186 if (kr == MACH_MSG_SUCCESS)
187 kr = thread_setstatus(a_self, flavor,
188 (thread_state_t)state,
189 state_cnt);
190 }
191
192 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
193 thread_exception_return();
194 /*NOTREACHED*/
195 return;
196 }
197
198 case EXCEPTION_DEFAULT:
199 c_thr_exc_raise++;
200 kr = exception_raise(exc_port,
201 retrieve_act_self_fast(a_self),
202 retrieve_task_self_fast(a_self->task),
203 exception,
204 code, codeCnt);
205
206 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
207 thread_exception_return();
208 /*NOTREACHED*/
209 return;
210
211 case EXCEPTION_STATE_IDENTITY: {
212 mach_msg_type_number_t state_cnt;
213 natural_t state[ THREAD_MACHINE_STATE_MAX ];
214
215 c_thr_exc_raise_state_id++;
216 state_cnt = state_count[flavor];
217 kr = thread_getstatus(a_self, flavor,
218 (thread_state_t)state,
219 &state_cnt);
220 if (kr == KERN_SUCCESS) {
221 kr = exception_raise_state_identity(exc_port,
222 retrieve_act_self_fast(a_self),
223 retrieve_task_self_fast(a_self->task),
224 exception,
225 code, codeCnt,
226 &flavor,
227 state, state_cnt,
228 state, &state_cnt);
229 if (kr == MACH_MSG_SUCCESS)
230 kr = thread_setstatus(a_self, flavor,
231 (thread_state_t)state,
232 state_cnt);
233 }
234
235 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
236 thread_exception_return();
237 /*NOTREACHED*/
238 return;
239 }
240
241 default:
242 panic ("bad exception behavior!");
243 }/* switch */
244 }
245
246 /*
247 * Routine: exception
248 * Purpose:
249 * The current thread caught an exception.
250 * We make an up-call to the thread's exception server.
251 * Conditions:
252 * Nothing locked and no resources held.
253 * Called from an exception context, so
254 * thread_exception_return and thread_kdb_return
255 * are possible.
256 * Returns:
257 * Doesn't return.
258 */
259 void
260 exception(
261 exception_type_t exception,
262 exception_data_t code,
263 mach_msg_type_number_t codeCnt)
264 {
265 thread_act_t thr_act;
266 task_t task;
267 host_priv_t host_priv;
268 struct exception_action *excp;
269 mutex_t *mutex;
270
271 assert(exception != EXC_RPC_ALERT);
272
273 if (exception == KERN_SUCCESS)
274 panic("exception");
275
276 /*
277 * Try to raise the exception at the activation level.
278 */
279 thr_act = current_act();
280 mutex = mutex_addr(thr_act->lock);
281 excp = &thr_act->exc_actions[exception];
282 exception_deliver(exception, code, codeCnt, excp, mutex);
283
284 /*
285 * Maybe the task level will handle it.
286 */
287 task = current_task();
288 mutex = mutex_addr(task->lock);
289 excp = &task->exc_actions[exception];
290 exception_deliver(exception, code, codeCnt, excp, mutex);
291
292 /*
293 * How about at the host level?
294 */
295 host_priv = host_priv_self();
296 mutex = mutex_addr(host_priv->lock);
297 excp = &host_priv->exc_actions[exception];
298 exception_deliver(exception, code, codeCnt, excp, mutex);
299
300 /*
301 * Nobody handled it, terminate the task.
302 */
303
304 #if MACH_KDB
305 if (debug_user_with_kdb) {
306 /*
307 * Debug the exception with kdb.
308 * If kdb handles the exception,
309 * then thread_kdb_return won't return.
310 */
311 db_printf("No exception server, calling kdb...\n");
312 thread_kdb_return();
313 }
314 #endif /* MACH_KDB */
315
316 (void) task_terminate(task);
317 thread_exception_return();
318 /*NOTREACHED*/
319 }
320
321 kern_return_t
322 bsd_exception(
323 exception_type_t exception,
324 exception_data_t code,
325 mach_msg_type_number_t codeCnt)
326 {
327 task_t task;
328 host_priv_t host_priv;
329 struct exception_action *excp;
330 mutex_t *mutex;
331 thread_act_t a_self = current_act();
332 ipc_port_t exc_port;
333 int behavior;
334 int flavor;
335 kern_return_t kr;
336
337 /*
338 * Maybe the task level will handle it.
339 */
340 task = current_task();
341 mutex = mutex_addr(task->lock);
342 excp = &task->exc_actions[exception];
343
344 /*
345 * Save work if we are terminating.
346 * Just go back to our AST handler.
347 */
348 if (!a_self->active) {
349 return(KERN_FAILURE);
350 }
351
352 /*
353 * Snapshot the exception action data under lock for consistency.
354 * Hold a reference to the port over the exception_raise_* calls
355 * so it can't be destroyed. This seems like overkill, but keeps
356 * the port from disappearing between now and when
357 * ipc_object_copyin_from_kernel is finally called.
358 */
359 mutex_lock(mutex);
360 exc_port = excp->port;
361 if (!IP_VALID(exc_port)) {
362 mutex_unlock(mutex);
363 return(KERN_FAILURE);
364 }
365 ip_lock(exc_port);
366 if (!ip_active(exc_port)) {
367 ip_unlock(exc_port);
368 mutex_unlock(mutex);
369 return(KERN_FAILURE);
370 }
371 ip_reference(exc_port);
372 exc_port->ip_srights++;
373 ip_unlock(exc_port);
374
375 flavor = excp->flavor;
376 behavior = excp->behavior;
377 mutex_unlock(mutex);
378
379 switch (behavior) {
380 case EXCEPTION_STATE: {
381 mach_msg_type_number_t state_cnt;
382 natural_t state[ THREAD_MACHINE_STATE_MAX ];
383
384 c_thr_exc_raise_state++;
385 state_cnt = state_count[flavor];
386 kr = thread_getstatus(a_self, flavor,
387 (thread_state_t)state,
388 &state_cnt);
389 if (kr == KERN_SUCCESS) {
390 kr = exception_raise_state(exc_port, exception,
391 code, codeCnt,
392 &flavor,
393 state, state_cnt,
394 state, &state_cnt);
395 if (kr == MACH_MSG_SUCCESS)
396 kr = thread_setstatus(a_self, flavor,
397 (thread_state_t)state,
398 state_cnt);
399 }
400
401 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
402 return(KERN_SUCCESS);
403
404 return(KERN_FAILURE);
405 }
406
407 case EXCEPTION_DEFAULT:
408 c_thr_exc_raise++;
409 kr = exception_raise(exc_port,
410 retrieve_act_self_fast(a_self),
411 retrieve_task_self_fast(a_self->task),
412 exception,
413 code, codeCnt);
414
415 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
416 return(KERN_SUCCESS);
417 return(KERN_FAILURE);
418
419 case EXCEPTION_STATE_IDENTITY: {
420 mach_msg_type_number_t state_cnt;
421 natural_t state[ THREAD_MACHINE_STATE_MAX ];
422
423 c_thr_exc_raise_state_id++;
424 state_cnt = state_count[flavor];
425 kr = thread_getstatus(a_self, flavor,
426 (thread_state_t)state,
427 &state_cnt);
428 if (kr == KERN_SUCCESS) {
429 kr = exception_raise_state_identity(exc_port,
430 retrieve_act_self_fast(a_self),
431 retrieve_task_self_fast(a_self->task),
432 exception,
433 code, codeCnt,
434 &flavor,
435 state, state_cnt,
436 state, &state_cnt);
437 if (kr == MACH_MSG_SUCCESS)
438 kr = thread_setstatus(a_self, flavor,
439 (thread_state_t)state,
440 state_cnt);
441 }
442
443 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
444 return(KERN_SUCCESS);
445 return(KERN_FAILURE);
446 }
447
448 default:
449
450 return(KERN_FAILURE);
451 }/* switch */
452 return(KERN_FAILURE);
453 }
454