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