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