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