]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/exception.c
xnu-2422.115.4.tar.gz
[apple/xnu.git] / osfmk / kern / exception.c
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/mach_types.h>
60 #include <mach/boolean.h>
61 #include <mach/kern_return.h>
62 #include <mach/message.h>
63 #include <mach/port.h>
64 #include <mach/mig_errors.h>
65 #include <mach/task.h>
66 #include <mach/thread_status.h>
67 #include <mach/exception_types.h>
68 #include <mach/exc.h>
69 #include <mach/mach_exc.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 <pexpert/pexpert.h>
88
89 unsigned long c_thr_exc_raise = 0;
90 unsigned long c_thr_exc_raise_state = 0;
91 unsigned long c_thr_exc_raise_state_id = 0;
92 unsigned long c_tsk_exc_raise = 0;
93 unsigned long c_tsk_exc_raise_state = 0;
94 unsigned long c_tsk_exc_raise_state_id = 0;
95
96 /* forward declarations */
97 kern_return_t exception_deliver(
98 thread_t thread,
99 exception_type_t exception,
100 mach_exception_data_t code,
101 mach_msg_type_number_t codeCnt,
102 struct exception_action *excp,
103 lck_mtx_t *mutex);
104
105 #ifdef MACH_BSD
106 kern_return_t bsd_exception(
107 exception_type_t exception,
108 mach_exception_data_t code,
109 mach_msg_type_number_t codeCnt);
110 #endif /* MACH_BSD */
111
112 /*
113 * Routine: exception_deliver
114 * Purpose:
115 * Make an upcall to the exception server provided.
116 * Conditions:
117 * Nothing locked and no resources held.
118 * Called from an exception context, so
119 * thread_exception_return and thread_kdb_return
120 * are possible.
121 * Returns:
122 * KERN_SUCCESS if the exception was handled
123 */
124 kern_return_t
125 exception_deliver(
126 thread_t thread,
127 exception_type_t exception,
128 mach_exception_data_t code,
129 mach_msg_type_number_t codeCnt,
130 struct exception_action *excp,
131 lck_mtx_t *mutex)
132 {
133 ipc_port_t exc_port;
134 exception_data_type_t small_code[EXCEPTION_CODE_MAX];
135 int code64;
136 int behavior;
137 int flavor;
138 kern_return_t kr;
139
140 /*
141 * Save work if we are terminating.
142 * Just go back to our AST handler.
143 */
144 if (!thread->active)
145 return KERN_SUCCESS;
146
147 /*
148 * If there are no exception actions defined for this entity,
149 * we can't deliver here.
150 */
151 if (excp == NULL)
152 return KERN_FAILURE;
153
154 assert(exception < EXC_TYPES_COUNT);
155 if (exception >= EXC_TYPES_COUNT)
156 return KERN_FAILURE;
157
158 excp = &excp[exception];
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 lck_mtx_lock(mutex);
168 exc_port = excp->port;
169 if (!IP_VALID(exc_port)) {
170 lck_mtx_unlock(mutex);
171 return KERN_FAILURE;
172 }
173 ip_lock(exc_port);
174 if (!ip_active(exc_port)) {
175 ip_unlock(exc_port);
176 lck_mtx_unlock(mutex);
177 return KERN_FAILURE;
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 lck_mtx_unlock(mutex);
186
187 code64 = (behavior & MACH_EXCEPTION_CODES);
188 behavior &= ~MACH_EXCEPTION_CODES;
189
190 if (!code64) {
191 small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]);
192 small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]);
193 }
194
195
196 switch (behavior) {
197 case EXCEPTION_STATE: {
198 mach_msg_type_number_t state_cnt;
199 thread_state_data_t state;
200
201 c_thr_exc_raise_state++;
202 state_cnt = _MachineStateCount[flavor];
203 kr = thread_getstatus(thread, flavor,
204 (thread_state_t)state,
205 &state_cnt);
206 if (kr == KERN_SUCCESS) {
207 if (code64) {
208 kr = mach_exception_raise_state(exc_port,
209 exception,
210 code,
211 codeCnt,
212 &flavor,
213 state, state_cnt,
214 state, &state_cnt);
215 } else {
216 kr = exception_raise_state(exc_port, exception,
217 small_code,
218 codeCnt,
219 &flavor,
220 state, state_cnt,
221 state, &state_cnt);
222 }
223 if (kr == MACH_MSG_SUCCESS)
224 kr = thread_setstatus(thread, flavor,
225 (thread_state_t)state,
226 state_cnt);
227 }
228
229 return kr;
230 }
231
232 case EXCEPTION_DEFAULT:
233 c_thr_exc_raise++;
234 if (code64) {
235 kr = mach_exception_raise(exc_port,
236 retrieve_thread_self_fast(thread),
237 retrieve_task_self_fast(thread->task),
238 exception,
239 code,
240 codeCnt);
241 } else {
242 kr = exception_raise(exc_port,
243 retrieve_thread_self_fast(thread),
244 retrieve_task_self_fast(thread->task),
245 exception,
246 small_code,
247 codeCnt);
248 }
249
250 return kr;
251
252 case EXCEPTION_STATE_IDENTITY: {
253 mach_msg_type_number_t state_cnt;
254 thread_state_data_t state;
255
256 c_thr_exc_raise_state_id++;
257 state_cnt = _MachineStateCount[flavor];
258 kr = thread_getstatus(thread, flavor,
259 (thread_state_t)state,
260 &state_cnt);
261 if (kr == KERN_SUCCESS) {
262 if (code64) {
263 kr = mach_exception_raise_state_identity(
264 exc_port,
265 retrieve_thread_self_fast(thread),
266 retrieve_task_self_fast(thread->task),
267 exception,
268 code,
269 codeCnt,
270 &flavor,
271 state, state_cnt,
272 state, &state_cnt);
273 } else {
274 kr = exception_raise_state_identity(exc_port,
275 retrieve_thread_self_fast(thread),
276 retrieve_task_self_fast(thread->task),
277 exception,
278 small_code,
279 codeCnt,
280 &flavor,
281 state, state_cnt,
282 state, &state_cnt);
283 }
284 if (kr == MACH_MSG_SUCCESS)
285 kr = thread_setstatus(thread, flavor,
286 (thread_state_t)state,
287 state_cnt);
288 }
289
290 return kr;
291 }
292
293 default:
294 panic ("bad exception behavior!");
295 return KERN_FAILURE;
296 }/* switch */
297 }
298
299 /*
300 * Routine: exception
301 * Purpose:
302 * The current thread caught an exception.
303 * We make an up-call to the thread's exception server.
304 * Conditions:
305 * Nothing locked and no resources held.
306 * Called from an exception context, so
307 * thread_exception_return and thread_kdb_return
308 * are possible.
309 * Returns:
310 * Doesn't return.
311 */
312 void
313 exception_triage(
314 exception_type_t exception,
315 mach_exception_data_t code,
316 mach_msg_type_number_t codeCnt)
317 {
318 thread_t thread;
319 task_t task;
320 host_priv_t host_priv;
321 lck_mtx_t *mutex;
322 kern_return_t kr;
323
324 assert(exception != EXC_RPC_ALERT);
325
326 thread = current_thread();
327
328 /*
329 * Try to raise the exception at the activation level.
330 */
331 mutex = &thread->mutex;
332 kr = exception_deliver(thread, exception, code, codeCnt, thread->exc_actions, mutex);
333 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
334 goto out;
335
336 /*
337 * Maybe the task level will handle it.
338 */
339 task = current_task();
340 mutex = &task->lock;
341 kr = exception_deliver(thread, exception, code, codeCnt, task->exc_actions, mutex);
342 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
343 goto out;
344
345 /*
346 * How about at the host level?
347 */
348 host_priv = host_priv_self();
349 mutex = &host_priv->lock;
350 kr = exception_deliver(thread, exception, code, codeCnt, host_priv->exc_actions, mutex);
351 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
352 goto out;
353
354 /*
355 * Nobody handled it, terminate the task.
356 */
357
358 (void) task_terminate(task);
359
360 out:
361 if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE) &&
362 (exception != EXC_GUARD))
363 thread_exception_return();
364 return;
365 }
366
367 kern_return_t
368 bsd_exception(
369 exception_type_t exception,
370 mach_exception_data_t code,
371 mach_msg_type_number_t codeCnt)
372 {
373 task_t task;
374 lck_mtx_t *mutex;
375 thread_t self = current_thread();
376 kern_return_t kr;
377
378 /*
379 * Maybe the task level will handle it.
380 */
381 task = current_task();
382 mutex = &task->lock;
383
384 kr = exception_deliver(self, exception, code, codeCnt, task->exc_actions, mutex);
385
386 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
387 return(KERN_SUCCESS);
388 return(KERN_FAILURE);
389 }
390
391
392 /*
393 * Raise an exception on a task.
394 * This should tell launchd to launch Crash Reporter for this task.
395 */
396 kern_return_t task_exception_notify(exception_type_t exception,
397 mach_exception_data_type_t exccode, mach_exception_data_type_t excsubcode)
398 {
399 mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
400 wait_interrupt_t wsave;
401
402 code[0] = exccode;
403 code[1] = excsubcode;
404
405 wsave = thread_interrupt_level(THREAD_UNINT);
406 exception_triage(exception, code, EXCEPTION_CODE_MAX);
407 (void) thread_interrupt_level(wsave);
408 return (KERN_SUCCESS);
409 }
410
411
412 /*
413 * Handle interface for special performance monitoring
414 * This is a special case of the host exception handler
415 */
416 kern_return_t sys_perf_notify(thread_t thread, int pid)
417 {
418 host_priv_t hostp;
419 ipc_port_t xport;
420 wait_interrupt_t wsave;
421 kern_return_t ret;
422
423 hostp = host_priv_self(); /* Get the host privileged ports */
424 mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
425 code[0] = 0xFF000001; /* Set terminate code */
426 code[1] = pid; /* Pass out the pid */
427
428 struct task *task = thread->task;
429 xport = hostp->exc_actions[EXC_RPC_ALERT].port;
430
431 /* Make sure we're not catching our own exception */
432 if (!IP_VALID(xport) ||
433 !ip_active(xport) ||
434 task->itk_space == xport->data.receiver) {
435
436 return(KERN_FAILURE);
437 }
438
439 wsave = thread_interrupt_level(THREAD_UNINT);
440 ret = exception_deliver(
441 thread,
442 EXC_RPC_ALERT,
443 code,
444 2,
445 hostp->exc_actions,
446 &hostp->lock);
447 (void)thread_interrupt_level(wsave);
448
449 return(ret);
450 }
451