2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
26 * Mach Operating System
27 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
28 * All Rights Reserved.
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.
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.
40 * Carnegie Mellon requests users of this software to return to
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
55 #include <mach/mach_types.h>
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>
61 #include <mach/task.h>
62 #include <mach/thread_status.h>
63 #include <mach/exception_types.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>
71 #include <kern/counters.h>
72 #include <kern/ipc_tt.h>
73 #include <kern/task.h>
74 #include <kern/thread.h>
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>
84 #include <ddb/db_trap.h>
89 #include <ddb/db_output.h>
91 #if iPSC386 || iPSC860
92 boolean_t debug_user_with_kdb
= TRUE
;
94 boolean_t debug_user_with_kdb
= FALSE
;
99 unsigned long c_thr_exc_raise
= 0;
100 unsigned long c_thr_exc_raise_state
= 0;
101 unsigned long c_thr_exc_raise_state_id
= 0;
102 unsigned long c_tsk_exc_raise
= 0;
103 unsigned long c_tsk_exc_raise_state
= 0;
104 unsigned long c_tsk_exc_raise_state_id
= 0;
106 /* forward declarations */
107 void exception_deliver(
108 exception_type_t exception
,
109 exception_data_t code
,
110 mach_msg_type_number_t codeCnt
,
111 struct exception_action
*excp
,
115 kern_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 */
122 * Routine: exception_deliver
124 * Make an upcall to the exception server provided.
126 * Nothing locked and no resources held.
127 * Called from an exception context, so
128 * thread_exception_return and thread_kdb_return
131 * If the exception was not handled by this handler
135 exception_type_t exception
,
136 exception_data_t code
,
137 mach_msg_type_number_t codeCnt
,
138 struct exception_action
*excp
,
141 thread_t self
= current_thread();
148 * Save work if we are terminating.
149 * Just go back to our AST handler.
152 thread_exception_return();
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.
162 exc_port
= excp
->port
;
163 if (!IP_VALID(exc_port
)) {
168 if (!ip_active(exc_port
)) {
173 ip_reference(exc_port
);
174 exc_port
->ip_srights
++;
177 flavor
= excp
->flavor
;
178 behavior
= excp
->behavior
;
182 case EXCEPTION_STATE
: {
183 mach_msg_type_number_t state_cnt
;
184 thread_state_data_t state
;
186 c_thr_exc_raise_state
++;
187 state_cnt
= _MachineStateCount
[flavor
];
188 kr
= thread_getstatus(self
, flavor
,
189 (thread_state_t
)state
,
191 if (kr
== KERN_SUCCESS
) {
192 kr
= exception_raise_state(exc_port
, exception
,
197 if (kr
== MACH_MSG_SUCCESS
)
198 kr
= thread_setstatus(self
, flavor
,
199 (thread_state_t
)state
,
203 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
204 thread_exception_return();
209 case EXCEPTION_DEFAULT
:
211 kr
= exception_raise(exc_port
,
212 retrieve_thread_self_fast(self
),
213 retrieve_task_self_fast(self
->task
),
217 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
218 thread_exception_return();
222 case EXCEPTION_STATE_IDENTITY
: {
223 mach_msg_type_number_t state_cnt
;
224 thread_state_data_t state
;
226 c_thr_exc_raise_state_id
++;
227 state_cnt
= _MachineStateCount
[flavor
];
228 kr
= thread_getstatus(self
, flavor
,
229 (thread_state_t
)state
,
231 if (kr
== KERN_SUCCESS
) {
232 kr
= exception_raise_state_identity(exc_port
,
233 retrieve_thread_self_fast(self
),
234 retrieve_task_self_fast(self
->task
),
240 if (kr
== MACH_MSG_SUCCESS
)
241 kr
= thread_setstatus(self
, flavor
,
242 (thread_state_t
)state
,
246 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
247 thread_exception_return();
253 panic ("bad exception behavior!");
260 * The current thread caught an exception.
261 * We make an up-call to the thread's exception server.
263 * Nothing locked and no resources held.
264 * Called from an exception context, so
265 * thread_exception_return and thread_kdb_return
272 exception_type_t exception
,
273 exception_data_t code
,
274 mach_msg_type_number_t codeCnt
)
278 host_priv_t host_priv
;
279 struct exception_action
*excp
;
282 assert(exception
!= EXC_RPC_ALERT
);
284 if (exception
== KERN_SUCCESS
)
288 * Try to raise the exception at the activation level.
290 thread
= current_thread();
291 mutex
= mutex_addr(thread
->mutex
);
292 excp
= &thread
->exc_actions
[exception
];
293 exception_deliver(exception
, code
, codeCnt
, excp
, mutex
);
296 * Maybe the task level will handle it.
298 task
= current_task();
299 mutex
= mutex_addr(task
->lock
);
300 excp
= &task
->exc_actions
[exception
];
301 exception_deliver(exception
, code
, codeCnt
, excp
, mutex
);
304 * How about at the host level?
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
);
312 * Nobody handled it, terminate the task.
316 if (debug_user_with_kdb
) {
318 * Debug the exception with kdb.
319 * If kdb handles the exception,
320 * then thread_kdb_return won't return.
322 db_printf("No exception server, calling kdb...\n");
325 #endif /* MACH_KDB */
327 (void) task_terminate(task
);
328 thread_exception_return();
334 exception_type_t exception
,
335 exception_data_t code
,
336 mach_msg_type_number_t codeCnt
)
339 struct exception_action
*excp
;
341 thread_t self
= current_thread();
348 * Maybe the task level will handle it.
350 task
= current_task();
351 mutex
= mutex_addr(task
->lock
);
352 excp
= &task
->exc_actions
[exception
];
355 * Save work if we are terminating.
356 * Just go back to our AST handler.
359 return(KERN_FAILURE
);
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.
370 exc_port
= excp
->port
;
371 if (!IP_VALID(exc_port
)) {
373 return(KERN_FAILURE
);
376 if (!ip_active(exc_port
)) {
379 return(KERN_FAILURE
);
381 ip_reference(exc_port
);
382 exc_port
->ip_srights
++;
385 flavor
= excp
->flavor
;
386 behavior
= excp
->behavior
;
390 case EXCEPTION_STATE
: {
391 mach_msg_type_number_t state_cnt
;
392 thread_state_data_t state
;
394 c_thr_exc_raise_state
++;
395 state_cnt
= _MachineStateCount
[flavor
];
396 kr
= thread_getstatus(self
, flavor
,
397 (thread_state_t
)state
,
399 if (kr
== KERN_SUCCESS
) {
400 kr
= exception_raise_state(exc_port
, exception
,
405 if (kr
== MACH_MSG_SUCCESS
)
406 kr
= thread_setstatus(self
, flavor
,
407 (thread_state_t
)state
,
411 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
412 return(KERN_SUCCESS
);
414 return(KERN_FAILURE
);
417 case EXCEPTION_DEFAULT
:
419 kr
= exception_raise(exc_port
,
420 retrieve_thread_self_fast(self
),
421 retrieve_task_self_fast(self
->task
),
425 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
426 return(KERN_SUCCESS
);
427 return(KERN_FAILURE
);
429 case EXCEPTION_STATE_IDENTITY
: {
430 mach_msg_type_number_t state_cnt
;
431 thread_state_data_t state
;
433 c_thr_exc_raise_state_id
++;
434 state_cnt
= _MachineStateCount
[flavor
];
435 kr
= thread_getstatus(self
, flavor
,
436 (thread_state_t
)state
,
438 if (kr
== KERN_SUCCESS
) {
439 kr
= exception_raise_state_identity(exc_port
,
440 retrieve_thread_self_fast(self
),
441 retrieve_task_self_fast(self
->task
),
447 if (kr
== MACH_MSG_SUCCESS
)
448 kr
= thread_setstatus(self
, flavor
,
449 (thread_state_t
)state
,
453 if (kr
== KERN_SUCCESS
|| kr
== MACH_RCV_PORT_DIED
)
454 return(KERN_SUCCESS
);
455 return(KERN_FAILURE
);
460 return(KERN_FAILURE
);
462 return(KERN_FAILURE
);
469 * Handle interface for special perfomance monitoring
470 * This is a special case of the host exception handler
473 kern_return_t
sys_perf_notify(struct task
*task
,
474 exception_data_t code
,
475 mach_msg_type_number_t codeCnt
)
478 struct exception_action
*excp
;
479 thread_t thread
= current_thread();
482 wait_interrupt_t wsave
;
484 hostp
= host_priv_self(); /* Get the host privileged ports */
485 excp
= &hostp
->exc_actions
[EXC_RPC_ALERT
]; /* Point to the RPC_ALERT action */
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... */
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... */
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... */
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 */
511 mutex_unlock(&hostp
->lock
); /* All done with the lock */
513 wsave
= thread_interrupt_level(THREAD_UNINT
); /* Make sure we aren't aborted here */
515 ret
= exception_raise(xport
, /* Send the exception to the perf handler */
516 retrieve_thread_self_fast(thread
), /* Not always the dying guy */
517 retrieve_task_self_fast(thread
->task
), /* Not always the dying guy */
518 EXC_RPC_ALERT
, /* Unused exception type until now */
521 (void)thread_interrupt_level(wsave
); /* Restore interrupt level */
523 return(ret
); /* Tell caller how it went */