]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
91447636 | 2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
2d21ac55 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. 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. | |
8f6c56a5 | 14 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
8f6c56a5 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
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 | ||
55e303ae | 61 | #include <mach/mach_types.h> |
1c79356b A |
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> | |
91447636 | 67 | #include <mach/task.h> |
1c79356b A |
68 | #include <mach/thread_status.h> |
69 | #include <mach/exception_types.h> | |
2d21ac55 A |
70 | #include <mach/exc.h> |
71 | #include <mach/mach_exc.h> | |
1c79356b A |
72 | #include <ipc/port.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> | |
1c79356b A |
79 | #include <kern/counters.h> |
80 | #include <kern/ipc_tt.h> | |
81 | #include <kern/task.h> | |
82 | #include <kern/thread.h> | |
1c79356b A |
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> | |
88 | #include <string.h> | |
1c79356b A |
89 | |
90 | #if MACH_KDB | |
91 | #include <ddb/db_trap.h> | |
92 | #endif /* MACH_KDB */ | |
93 | ||
94 | #if MACH_KDB | |
95 | ||
96 | #include <ddb/db_output.h> | |
97 | ||
98 | #if iPSC386 || iPSC860 | |
99 | boolean_t debug_user_with_kdb = TRUE; | |
100 | #else | |
101 | boolean_t debug_user_with_kdb = FALSE; | |
102 | #endif | |
103 | ||
104 | #endif /* MACH_KDB */ | |
105 | ||
106 | unsigned long c_thr_exc_raise = 0; | |
107 | unsigned long c_thr_exc_raise_state = 0; | |
108 | unsigned long c_thr_exc_raise_state_id = 0; | |
109 | unsigned long c_tsk_exc_raise = 0; | |
110 | unsigned long c_tsk_exc_raise_state = 0; | |
111 | unsigned long c_tsk_exc_raise_state_id = 0; | |
112 | ||
91447636 | 113 | /* forward declarations */ |
2d21ac55 A |
114 | kern_return_t exception_deliver( |
115 | thread_t thread, | |
91447636 | 116 | exception_type_t exception, |
2d21ac55 | 117 | mach_exception_data_t code, |
91447636 A |
118 | mach_msg_type_number_t codeCnt, |
119 | struct exception_action *excp, | |
b0d623f7 | 120 | lck_mtx_t *mutex); |
91447636 A |
121 | |
122 | #ifdef MACH_BSD | |
123 | kern_return_t bsd_exception( | |
124 | exception_type_t exception, | |
2d21ac55 | 125 | mach_exception_data_t code, |
91447636 A |
126 | mach_msg_type_number_t codeCnt); |
127 | #endif /* MACH_BSD */ | |
1c79356b A |
128 | |
129 | /* | |
130 | * Routine: exception_deliver | |
131 | * Purpose: | |
132 | * Make an upcall to the exception server provided. | |
133 | * Conditions: | |
134 | * Nothing locked and no resources held. | |
135 | * Called from an exception context, so | |
136 | * thread_exception_return and thread_kdb_return | |
137 | * are possible. | |
138 | * Returns: | |
2d21ac55 | 139 | * KERN_SUCCESS if the exception was handled |
1c79356b | 140 | */ |
2d21ac55 | 141 | kern_return_t |
1c79356b | 142 | exception_deliver( |
2d21ac55 | 143 | thread_t thread, |
1c79356b | 144 | exception_type_t exception, |
2d21ac55 | 145 | mach_exception_data_t code, |
1c79356b A |
146 | mach_msg_type_number_t codeCnt, |
147 | struct exception_action *excp, | |
b0d623f7 | 148 | lck_mtx_t *mutex) |
1c79356b | 149 | { |
1c79356b | 150 | ipc_port_t exc_port; |
2d21ac55 A |
151 | exception_data_type_t small_code[EXCEPTION_CODE_MAX]; |
152 | int code64; | |
1c79356b A |
153 | int behavior; |
154 | int flavor; | |
155 | kern_return_t kr; | |
156 | ||
157 | /* | |
158 | * Save work if we are terminating. | |
159 | * Just go back to our AST handler. | |
160 | */ | |
2d21ac55 A |
161 | if (!thread->active) |
162 | return KERN_SUCCESS; | |
1c79356b A |
163 | |
164 | /* | |
165 | * Snapshot the exception action data under lock for consistency. | |
166 | * Hold a reference to the port over the exception_raise_* calls | |
167 | * so it can't be destroyed. This seems like overkill, but keeps | |
168 | * the port from disappearing between now and when | |
169 | * ipc_object_copyin_from_kernel is finally called. | |
170 | */ | |
b0d623f7 | 171 | lck_mtx_lock(mutex); |
1c79356b A |
172 | exc_port = excp->port; |
173 | if (!IP_VALID(exc_port)) { | |
b0d623f7 | 174 | lck_mtx_unlock(mutex); |
2d21ac55 | 175 | return KERN_FAILURE; |
1c79356b A |
176 | } |
177 | ip_lock(exc_port); | |
178 | if (!ip_active(exc_port)) { | |
179 | ip_unlock(exc_port); | |
b0d623f7 | 180 | lck_mtx_unlock(mutex); |
2d21ac55 | 181 | return KERN_FAILURE; |
1c79356b A |
182 | } |
183 | ip_reference(exc_port); | |
184 | exc_port->ip_srights++; | |
185 | ip_unlock(exc_port); | |
186 | ||
187 | flavor = excp->flavor; | |
188 | behavior = excp->behavior; | |
b0d623f7 | 189 | lck_mtx_unlock(mutex); |
1c79356b | 190 | |
2d21ac55 A |
191 | code64 = (behavior & MACH_EXCEPTION_CODES); |
192 | behavior &= ~MACH_EXCEPTION_CODES; | |
193 | ||
194 | if (!code64) { | |
b0d623f7 A |
195 | small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]); |
196 | small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]); | |
2d21ac55 A |
197 | } |
198 | ||
199 | ||
1c79356b A |
200 | switch (behavior) { |
201 | case EXCEPTION_STATE: { | |
202 | mach_msg_type_number_t state_cnt; | |
55e303ae | 203 | thread_state_data_t state; |
1c79356b A |
204 | |
205 | c_thr_exc_raise_state++; | |
91447636 | 206 | state_cnt = _MachineStateCount[flavor]; |
2d21ac55 | 207 | kr = thread_getstatus(thread, flavor, |
1c79356b A |
208 | (thread_state_t)state, |
209 | &state_cnt); | |
210 | if (kr == KERN_SUCCESS) { | |
2d21ac55 A |
211 | if (code64) { |
212 | kr = mach_exception_raise_state(exc_port, | |
213 | exception, | |
214 | code, | |
215 | codeCnt, | |
216 | &flavor, | |
217 | state, state_cnt, | |
218 | state, &state_cnt); | |
219 | } else { | |
220 | kr = exception_raise_state(exc_port, exception, | |
221 | small_code, | |
222 | codeCnt, | |
223 | &flavor, | |
224 | state, state_cnt, | |
225 | state, &state_cnt); | |
226 | } | |
1c79356b | 227 | if (kr == MACH_MSG_SUCCESS) |
2d21ac55 A |
228 | kr = thread_setstatus(thread, flavor, |
229 | (thread_state_t)state, | |
230 | state_cnt); | |
1c79356b A |
231 | } |
232 | ||
2d21ac55 | 233 | return kr; |
1c79356b A |
234 | } |
235 | ||
236 | case EXCEPTION_DEFAULT: | |
237 | c_thr_exc_raise++; | |
2d21ac55 A |
238 | if (code64) { |
239 | kr = mach_exception_raise(exc_port, | |
240 | retrieve_thread_self_fast(thread), | |
241 | retrieve_task_self_fast(thread->task), | |
242 | exception, | |
243 | code, | |
244 | codeCnt); | |
245 | } else { | |
246 | kr = exception_raise(exc_port, | |
247 | retrieve_thread_self_fast(thread), | |
248 | retrieve_task_self_fast(thread->task), | |
249 | exception, | |
250 | small_code, | |
251 | codeCnt); | |
252 | } | |
1c79356b | 253 | |
2d21ac55 | 254 | return kr; |
1c79356b A |
255 | |
256 | case EXCEPTION_STATE_IDENTITY: { | |
257 | mach_msg_type_number_t state_cnt; | |
55e303ae | 258 | thread_state_data_t state; |
1c79356b A |
259 | |
260 | c_thr_exc_raise_state_id++; | |
91447636 | 261 | state_cnt = _MachineStateCount[flavor]; |
2d21ac55 | 262 | kr = thread_getstatus(thread, flavor, |
1c79356b A |
263 | (thread_state_t)state, |
264 | &state_cnt); | |
265 | if (kr == KERN_SUCCESS) { | |
2d21ac55 A |
266 | if (code64) { |
267 | kr = mach_exception_raise_state_identity( | |
268 | exc_port, | |
269 | retrieve_thread_self_fast(thread), | |
270 | retrieve_task_self_fast(thread->task), | |
271 | exception, | |
272 | code, | |
273 | codeCnt, | |
274 | &flavor, | |
275 | state, state_cnt, | |
276 | state, &state_cnt); | |
277 | } else { | |
278 | kr = exception_raise_state_identity(exc_port, | |
279 | retrieve_thread_self_fast(thread), | |
280 | retrieve_task_self_fast(thread->task), | |
281 | exception, | |
282 | small_code, | |
283 | codeCnt, | |
284 | &flavor, | |
285 | state, state_cnt, | |
286 | state, &state_cnt); | |
287 | } | |
288 | if (kr == MACH_MSG_SUCCESS) | |
289 | kr = thread_setstatus(thread, flavor, | |
290 | (thread_state_t)state, | |
291 | state_cnt); | |
1c79356b A |
292 | } |
293 | ||
2d21ac55 | 294 | return kr; |
1c79356b | 295 | } |
2d21ac55 | 296 | |
1c79356b | 297 | default: |
2d21ac55 A |
298 | panic ("bad exception behavior!"); |
299 | return KERN_FAILURE; | |
1c79356b A |
300 | }/* switch */ |
301 | } | |
302 | ||
303 | /* | |
304 | * Routine: exception | |
305 | * Purpose: | |
306 | * The current thread caught an exception. | |
307 | * We make an up-call to the thread's exception server. | |
308 | * Conditions: | |
309 | * Nothing locked and no resources held. | |
310 | * Called from an exception context, so | |
311 | * thread_exception_return and thread_kdb_return | |
312 | * are possible. | |
313 | * Returns: | |
314 | * Doesn't return. | |
315 | */ | |
316 | void | |
91447636 | 317 | exception_triage( |
1c79356b | 318 | exception_type_t exception, |
2d21ac55 | 319 | mach_exception_data_t code, |
1c79356b A |
320 | mach_msg_type_number_t codeCnt) |
321 | { | |
91447636 | 322 | thread_t thread; |
1c79356b A |
323 | task_t task; |
324 | host_priv_t host_priv; | |
325 | struct exception_action *excp; | |
b0d623f7 | 326 | lck_mtx_t *mutex; |
2d21ac55 | 327 | kern_return_t kr; |
1c79356b A |
328 | |
329 | assert(exception != EXC_RPC_ALERT); | |
330 | ||
331 | if (exception == KERN_SUCCESS) | |
332 | panic("exception"); | |
333 | ||
334 | /* | |
335 | * Try to raise the exception at the activation level. | |
336 | */ | |
91447636 | 337 | thread = current_thread(); |
b0d623f7 | 338 | mutex = &thread->mutex; |
91447636 | 339 | excp = &thread->exc_actions[exception]; |
2d21ac55 A |
340 | kr = exception_deliver(thread, exception, code, codeCnt, excp, mutex); |
341 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
342 | goto out; | |
1c79356b A |
343 | |
344 | /* | |
345 | * Maybe the task level will handle it. | |
346 | */ | |
347 | task = current_task(); | |
b0d623f7 | 348 | mutex = &task->lock; |
1c79356b | 349 | excp = &task->exc_actions[exception]; |
2d21ac55 A |
350 | kr = exception_deliver(thread, exception, code, codeCnt, excp, mutex); |
351 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
352 | goto out; | |
1c79356b A |
353 | |
354 | /* | |
355 | * How about at the host level? | |
356 | */ | |
357 | host_priv = host_priv_self(); | |
b0d623f7 | 358 | mutex = &host_priv->lock; |
1c79356b | 359 | excp = &host_priv->exc_actions[exception]; |
2d21ac55 A |
360 | kr = exception_deliver(thread, exception, code, codeCnt, excp, mutex); |
361 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
362 | goto out; | |
1c79356b A |
363 | |
364 | /* | |
365 | * Nobody handled it, terminate the task. | |
366 | */ | |
367 | ||
368 | #if MACH_KDB | |
369 | if (debug_user_with_kdb) { | |
370 | /* | |
371 | * Debug the exception with kdb. | |
372 | * If kdb handles the exception, | |
373 | * then thread_kdb_return won't return. | |
374 | */ | |
375 | db_printf("No exception server, calling kdb...\n"); | |
376 | thread_kdb_return(); | |
377 | } | |
378 | #endif /* MACH_KDB */ | |
379 | ||
380 | (void) task_terminate(task); | |
2d21ac55 A |
381 | |
382 | out: | |
383 | if (exception != EXC_CRASH) | |
384 | thread_exception_return(); | |
385 | return; | |
1c79356b | 386 | } |
9bccf70c A |
387 | |
388 | kern_return_t | |
389 | bsd_exception( | |
390 | exception_type_t exception, | |
2d21ac55 | 391 | mach_exception_data_t code, |
9bccf70c A |
392 | mach_msg_type_number_t codeCnt) |
393 | { | |
394 | task_t task; | |
9bccf70c | 395 | struct exception_action *excp; |
b0d623f7 | 396 | lck_mtx_t *mutex; |
91447636 | 397 | thread_t self = current_thread(); |
9bccf70c A |
398 | kern_return_t kr; |
399 | ||
400 | /* | |
401 | * Maybe the task level will handle it. | |
402 | */ | |
403 | task = current_task(); | |
b0d623f7 | 404 | mutex = &task->lock; |
9bccf70c A |
405 | excp = &task->exc_actions[exception]; |
406 | ||
2d21ac55 | 407 | kr = exception_deliver(self, exception, code, codeCnt, excp, mutex); |
9bccf70c | 408 | |
2d21ac55 A |
409 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) |
410 | return(KERN_SUCCESS); | |
411 | return(KERN_FAILURE); | |
412 | } | |
9bccf70c | 413 | |
9bccf70c | 414 | |
2d21ac55 A |
415 | /* |
416 | * Raise an EXC_CRASH exception on the dying task. | |
417 | * This should tell launchd to launch Crash Reporter for this task. | |
418 | */ | |
419 | kern_return_t abnormal_exit_notify(mach_exception_data_type_t exccode, | |
420 | mach_exception_data_type_t excsubcode) | |
421 | { | |
422 | mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; | |
423 | wait_interrupt_t wsave; | |
9bccf70c | 424 | |
2d21ac55 A |
425 | code[0] = exccode; |
426 | code[1] = excsubcode; | |
9bccf70c | 427 | |
2d21ac55 A |
428 | wsave = thread_interrupt_level(THREAD_UNINT); |
429 | exception_triage(EXC_CRASH, code, EXCEPTION_CODE_MAX); | |
430 | (void) thread_interrupt_level(wsave); | |
431 | return (KERN_SUCCESS); | |
9bccf70c A |
432 | } |
433 | ||
55e303ae | 434 | |
55e303ae | 435 | /* |
2d21ac55 | 436 | * Handle interface for special performance monitoring |
55e303ae A |
437 | * This is a special case of the host exception handler |
438 | */ | |
2d21ac55 | 439 | kern_return_t sys_perf_notify(thread_t thread, int pid) |
55e303ae | 440 | { |
b0d623f7 | 441 | |
55e303ae A |
442 | host_priv_t hostp; |
443 | struct exception_action *excp; | |
55e303ae | 444 | ipc_port_t xport; |
55e303ae | 445 | wait_interrupt_t wsave; |
2d21ac55 | 446 | kern_return_t ret; |
55e303ae | 447 | |
2d21ac55 A |
448 | hostp = host_priv_self(); /* Get the host privileged ports */ |
449 | mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; | |
450 | code[0] = 0xFF000001; /* Set terminate code */ | |
451 | code[1] = pid; /* Pass out the pid */ | |
55e303ae | 452 | |
2d21ac55 A |
453 | struct task *task = thread->task; |
454 | excp = &hostp->exc_actions[EXC_RPC_ALERT]; | |
455 | xport = excp->port; | |
55e303ae | 456 | |
2d21ac55 A |
457 | /* Make sure we're not catching our own exception */ |
458 | if (!IP_VALID(xport) || | |
459 | !ip_active(xport) || | |
460 | task->itk_space == xport->data.receiver) { | |
55e303ae | 461 | |
2d21ac55 | 462 | return(KERN_FAILURE); |
55e303ae | 463 | } |
2d21ac55 A |
464 | |
465 | wsave = thread_interrupt_level(THREAD_UNINT); | |
466 | ret = exception_deliver( | |
467 | thread, | |
468 | EXC_RPC_ALERT, | |
469 | code, | |
470 | 2, | |
471 | excp, | |
472 | &hostp->lock); | |
473 | (void)thread_interrupt_level(wsave); | |
474 | ||
475 | return(ret); | |
55e303ae | 476 | } |
2d21ac55 | 477 |