2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Mach Operating System
33 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
34 * All Rights Reserved.
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.
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.
46 * Carnegie Mellon requests users of this software to return to
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
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>
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>
90 #include <ddb/db_trap.h>
95 #include <ddb/db_output.h>
97 #if iPSC386 || iPSC860
98 boolean_t debug_user_with_kdb
= TRUE
;
100 boolean_t debug_user_with_kdb
= FALSE
;
103 #endif /* MACH_KDB */
105 unsigned long c_thr_exc_raise
= 0;
106 unsigned long c_thr_exc_raise_state
= 0;
107 unsigned long c_thr_exc_raise_state_id
= 0;
108 unsigned long c_tsk_exc_raise
= 0;
109 unsigned long c_tsk_exc_raise_state
= 0;
110 unsigned long c_tsk_exc_raise_state_id
= 0;
112 /* forward declarations */
113 void exception_deliver(
114 exception_type_t exception
,
115 exception_data_t code
,
116 mach_msg_type_number_t codeCnt
,
117 struct exception_action
*excp
,
121 kern_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 */
128 * Routine: exception_deliver
130 * Make an upcall to the exception server provided.
132 * Nothing locked and no resources held.
133 * Called from an exception context, so
134 * thread_exception_return and thread_kdb_return
137 * If the exception was not handled by this handler
141 exception_type_t exception
,
142 exception_data_t code
,
143 mach_msg_type_number_t codeCnt
,
144 struct exception_action
*excp
,
147 thread_t self
= current_thread();
154 * Save work if we are terminating.
155 * Just go back to our AST handler.
158 thread_exception_return();
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.
168 exc_port
= excp
->port
;
169 if (!IP_VALID(exc_port
)) {
174 if (!ip_active(exc_port
)) {
179 ip_reference(exc_port
);
180 exc_port
->ip_srights
++;
183 flavor
= excp
->flavor
;
184 behavior
= excp
->behavior
;
188 case EXCEPTION_STATE
: {
189 mach_msg_type_number_t state_cnt
;
190 thread_state_data_t state
;
192 c_thr_exc_raise_state
++;
193 state_cnt
= _MachineStateCount
[flavor
];
194 kr
= thread_getstatus(self
, flavor
,
195 (thread_state_t
)state
,
197 if (kr
== KERN_SUCCESS
) {
198 kr
= exception_raise_state(exc_port
, exception
,
203 if (kr
== MACH_MSG_SUCCESS
)
204 kr
= thread_setstatus(self
, flavor
,
205 (thread_state_t
)state
,
209 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
210 thread_exception_return();
215 case EXCEPTION_DEFAULT
:
217 kr
= exception_raise(exc_port
,
218 retrieve_thread_self_fast(self
),
219 retrieve_task_self_fast(self
->task
),
223 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
224 thread_exception_return();
228 case EXCEPTION_STATE_IDENTITY
: {
229 mach_msg_type_number_t state_cnt
;
230 thread_state_data_t state
;
232 c_thr_exc_raise_state_id
++;
233 state_cnt
= _MachineStateCount
[flavor
];
234 kr
= thread_getstatus(self
, flavor
,
235 (thread_state_t
)state
,
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
),
246 if (kr
== MACH_MSG_SUCCESS
)
247 kr
= thread_setstatus(self
, flavor
,
248 (thread_state_t
)state
,
252 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
253 thread_exception_return();
259 panic ("bad exception behavior!");
266 * The current thread caught an exception.
267 * We make an up-call to the thread's exception server.
269 * Nothing locked and no resources held.
270 * Called from an exception context, so
271 * thread_exception_return and thread_kdb_return
278 exception_type_t exception
,
279 exception_data_t code
,
280 mach_msg_type_number_t codeCnt
)
284 host_priv_t host_priv
;
285 struct exception_action
*excp
;
288 assert(exception
!= EXC_RPC_ALERT
);
290 if (exception
== KERN_SUCCESS
)
294 * Try to raise the exception at the activation level.
296 thread
= current_thread();
297 mutex
= mutex_addr(thread
->mutex
);
298 excp
= &thread
->exc_actions
[exception
];
299 exception_deliver(exception
, code
, codeCnt
, excp
, mutex
);
302 * Maybe the task level will handle it.
304 task
= current_task();
305 mutex
= mutex_addr(task
->lock
);
306 excp
= &task
->exc_actions
[exception
];
307 exception_deliver(exception
, code
, codeCnt
, excp
, mutex
);
310 * How about at the host level?
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
);
318 * Nobody handled it, terminate the task.
322 if (debug_user_with_kdb
) {
324 * Debug the exception with kdb.
325 * If kdb handles the exception,
326 * then thread_kdb_return won't return.
328 db_printf("No exception server, calling kdb...\n");
331 #endif /* MACH_KDB */
333 (void) task_terminate(task
);
334 thread_exception_return();
340 exception_type_t exception
,
341 exception_data_t code
,
342 mach_msg_type_number_t codeCnt
)
345 struct exception_action
*excp
;
347 thread_t self
= current_thread();
354 * Maybe the task level will handle it.
356 task
= current_task();
357 mutex
= mutex_addr(task
->lock
);
358 excp
= &task
->exc_actions
[exception
];
361 * Save work if we are terminating.
362 * Just go back to our AST handler.
365 return(KERN_FAILURE
);
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.
376 exc_port
= excp
->port
;
377 if (!IP_VALID(exc_port
)) {
379 return(KERN_FAILURE
);
382 if (!ip_active(exc_port
)) {
385 return(KERN_FAILURE
);
387 ip_reference(exc_port
);
388 exc_port
->ip_srights
++;
391 flavor
= excp
->flavor
;
392 behavior
= excp
->behavior
;
396 case EXCEPTION_STATE
: {
397 mach_msg_type_number_t state_cnt
;
398 thread_state_data_t state
;
400 c_thr_exc_raise_state
++;
401 state_cnt
= _MachineStateCount
[flavor
];
402 kr
= thread_getstatus(self
, flavor
,
403 (thread_state_t
)state
,
405 if (kr
== KERN_SUCCESS
) {
406 kr
= exception_raise_state(exc_port
, exception
,
411 if (kr
== MACH_MSG_SUCCESS
)
412 kr
= thread_setstatus(self
, flavor
,
413 (thread_state_t
)state
,
417 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
418 return(KERN_SUCCESS
);
420 return(KERN_FAILURE
);
423 case EXCEPTION_DEFAULT
:
425 kr
= exception_raise(exc_port
,
426 retrieve_thread_self_fast(self
),
427 retrieve_task_self_fast(self
->task
),
431 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
432 return(KERN_SUCCESS
);
433 return(KERN_FAILURE
);
435 case EXCEPTION_STATE_IDENTITY
: {
436 mach_msg_type_number_t state_cnt
;
437 thread_state_data_t state
;
439 c_thr_exc_raise_state_id
++;
440 state_cnt
= _MachineStateCount
[flavor
];
441 kr
= thread_getstatus(self
, flavor
,
442 (thread_state_t
)state
,
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
),
453 if (kr
== MACH_MSG_SUCCESS
)
454 kr
= thread_setstatus(self
, flavor
,
455 (thread_state_t
)state
,
459 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
460 return(KERN_SUCCESS
);
461 return(KERN_FAILURE
);
466 return(KERN_FAILURE
);
468 return(KERN_FAILURE
);
475 * Handle interface for special perfomance monitoring
476 * This is a special case of the host exception handler
479 kern_return_t
sys_perf_notify(struct task
*task
,
480 exception_data_t code
,
481 mach_msg_type_number_t codeCnt
)
484 struct exception_action
*excp
;
485 thread_t thread
= current_thread();
488 wait_interrupt_t wsave
;
490 hostp
= host_priv_self(); /* Get the host privileged ports */
491 excp
= &hostp
->exc_actions
[EXC_RPC_ALERT
]; /* Point to the RPC_ALERT action */
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... */
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... */
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... */
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 */
517 mutex_unlock(&hostp
->lock
); /* All done with the lock */
519 wsave
= thread_interrupt_level(THREAD_UNINT
); /* Make sure we aren't aborted here */
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 */
527 (void)thread_interrupt_level(wsave
); /* Restore interrupt level */
529 return(ret
); /* Tell caller how it went */