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