2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
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
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
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.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
34 * Mach Operating System
35 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
36 * All Rights Reserved.
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.
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.
48 * Carnegie Mellon requests users of this software to return to
50 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
51 * School of Computer Science
52 * Carnegie Mellon University
53 * Pittsburgh PA 15213-3890
55 * any improvements or extensions that they make and grant Carnegie Mellon
56 * the rights to redistribute these changes.
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>
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>
92 #include <ddb/db_trap.h>
97 #include <ddb/db_output.h>
99 #if iPSC386 || iPSC860
100 boolean_t debug_user_with_kdb
= TRUE
;
102 boolean_t debug_user_with_kdb
= FALSE
;
105 #endif /* MACH_KDB */
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;
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
,
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 */
130 * Routine: exception_deliver
132 * Make an upcall to the exception server provided.
134 * Nothing locked and no resources held.
135 * Called from an exception context, so
136 * thread_exception_return and thread_kdb_return
139 * If the exception was not handled by this handler
143 exception_type_t exception
,
144 exception_data_t code
,
145 mach_msg_type_number_t codeCnt
,
146 struct exception_action
*excp
,
149 thread_t self
= current_thread();
156 * Save work if we are terminating.
157 * Just go back to our AST handler.
160 thread_exception_return();
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.
170 exc_port
= excp
->port
;
171 if (!IP_VALID(exc_port
)) {
176 if (!ip_active(exc_port
)) {
181 ip_reference(exc_port
);
182 exc_port
->ip_srights
++;
185 flavor
= excp
->flavor
;
186 behavior
= excp
->behavior
;
190 case EXCEPTION_STATE
: {
191 mach_msg_type_number_t state_cnt
;
192 thread_state_data_t state
;
194 c_thr_exc_raise_state
++;
195 state_cnt
= _MachineStateCount
[flavor
];
196 kr
= thread_getstatus(self
, flavor
,
197 (thread_state_t
)state
,
199 if (kr
== KERN_SUCCESS
) {
200 kr
= exception_raise_state(exc_port
, exception
,
205 if (kr
== MACH_MSG_SUCCESS
)
206 kr
= thread_setstatus(self
, flavor
,
207 (thread_state_t
)state
,
211 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
212 thread_exception_return();
217 case EXCEPTION_DEFAULT
:
219 kr
= exception_raise(exc_port
,
220 retrieve_thread_self_fast(self
),
221 retrieve_task_self_fast(self
->task
),
225 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
226 thread_exception_return();
230 case EXCEPTION_STATE_IDENTITY
: {
231 mach_msg_type_number_t state_cnt
;
232 thread_state_data_t state
;
234 c_thr_exc_raise_state_id
++;
235 state_cnt
= _MachineStateCount
[flavor
];
236 kr
= thread_getstatus(self
, flavor
,
237 (thread_state_t
)state
,
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
),
248 if (kr
== MACH_MSG_SUCCESS
)
249 kr
= thread_setstatus(self
, flavor
,
250 (thread_state_t
)state
,
254 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
255 thread_exception_return();
261 panic ("bad exception behavior!");
268 * The current thread caught an exception.
269 * We make an up-call to the thread's exception server.
271 * Nothing locked and no resources held.
272 * Called from an exception context, so
273 * thread_exception_return and thread_kdb_return
280 exception_type_t exception
,
281 exception_data_t code
,
282 mach_msg_type_number_t codeCnt
)
286 host_priv_t host_priv
;
287 struct exception_action
*excp
;
290 assert(exception
!= EXC_RPC_ALERT
);
292 if (exception
== KERN_SUCCESS
)
296 * Try to raise the exception at the activation level.
298 thread
= current_thread();
299 mutex
= mutex_addr(thread
->mutex
);
300 excp
= &thread
->exc_actions
[exception
];
301 exception_deliver(exception
, code
, codeCnt
, excp
, mutex
);
304 * Maybe the task level will handle it.
306 task
= current_task();
307 mutex
= mutex_addr(task
->lock
);
308 excp
= &task
->exc_actions
[exception
];
309 exception_deliver(exception
, code
, codeCnt
, excp
, mutex
);
312 * How about at the host level?
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
);
320 * Nobody handled it, terminate the task.
324 if (debug_user_with_kdb
) {
326 * Debug the exception with kdb.
327 * If kdb handles the exception,
328 * then thread_kdb_return won't return.
330 db_printf("No exception server, calling kdb...\n");
333 #endif /* MACH_KDB */
335 (void) task_terminate(task
);
336 thread_exception_return();
342 exception_type_t exception
,
343 exception_data_t code
,
344 mach_msg_type_number_t codeCnt
)
347 struct exception_action
*excp
;
349 thread_t self
= current_thread();
356 * Maybe the task level will handle it.
358 task
= current_task();
359 mutex
= mutex_addr(task
->lock
);
360 excp
= &task
->exc_actions
[exception
];
363 * Save work if we are terminating.
364 * Just go back to our AST handler.
367 return(KERN_FAILURE
);
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.
378 exc_port
= excp
->port
;
379 if (!IP_VALID(exc_port
)) {
381 return(KERN_FAILURE
);
384 if (!ip_active(exc_port
)) {
387 return(KERN_FAILURE
);
389 ip_reference(exc_port
);
390 exc_port
->ip_srights
++;
393 flavor
= excp
->flavor
;
394 behavior
= excp
->behavior
;
398 case EXCEPTION_STATE
: {
399 mach_msg_type_number_t state_cnt
;
400 thread_state_data_t state
;
402 c_thr_exc_raise_state
++;
403 state_cnt
= _MachineStateCount
[flavor
];
404 kr
= thread_getstatus(self
, flavor
,
405 (thread_state_t
)state
,
407 if (kr
== KERN_SUCCESS
) {
408 kr
= exception_raise_state(exc_port
, exception
,
413 if (kr
== MACH_MSG_SUCCESS
)
414 kr
= thread_setstatus(self
, flavor
,
415 (thread_state_t
)state
,
419 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
420 return(KERN_SUCCESS
);
422 return(KERN_FAILURE
);
425 case EXCEPTION_DEFAULT
:
427 kr
= exception_raise(exc_port
,
428 retrieve_thread_self_fast(self
),
429 retrieve_task_self_fast(self
->task
),
433 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
434 return(KERN_SUCCESS
);
435 return(KERN_FAILURE
);
437 case EXCEPTION_STATE_IDENTITY
: {
438 mach_msg_type_number_t state_cnt
;
439 thread_state_data_t state
;
441 c_thr_exc_raise_state_id
++;
442 state_cnt
= _MachineStateCount
[flavor
];
443 kr
= thread_getstatus(self
, flavor
,
444 (thread_state_t
)state
,
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
),
455 if (kr
== MACH_MSG_SUCCESS
)
456 kr
= thread_setstatus(self
, flavor
,
457 (thread_state_t
)state
,
461 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
462 return(KERN_SUCCESS
);
463 return(KERN_FAILURE
);
468 return(KERN_FAILURE
);
470 return(KERN_FAILURE
);
477 * Handle interface for special perfomance monitoring
478 * This is a special case of the host exception handler
481 kern_return_t
sys_perf_notify(struct task
*task
,
482 exception_data_t code
,
483 mach_msg_type_number_t codeCnt
)
486 struct exception_action
*excp
;
487 thread_t thread
= current_thread();
490 wait_interrupt_t wsave
;
492 hostp
= host_priv_self(); /* Get the host privileged ports */
493 excp
= &hostp
->exc_actions
[EXC_RPC_ALERT
]; /* Point to the RPC_ALERT action */
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... */
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... */
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... */
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 */
519 mutex_unlock(&hostp
->lock
); /* All done with the lock */
521 wsave
= thread_interrupt_level(THREAD_UNINT
); /* Make sure we aren't aborted here */
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 */
529 (void)thread_interrupt_level(wsave
); /* Restore interrupt level */
531 return(ret
); /* Tell caller how it went */