]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
91447636 | 2 | * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. |
1c79356b | 3 | * |
8f6c56a5 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
1c79356b | 5 | * |
8f6c56a5 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. | |
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 | |
8ad349bb | 24 | * limitations under the License. |
8f6c56a5 A |
25 | * |
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> | |
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> | |
1c79356b A |
77 | #include <kern/counters.h> |
78 | #include <kern/ipc_tt.h> | |
79 | #include <kern/task.h> | |
80 | #include <kern/thread.h> | |
1c79356b A |
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> | |
9bccf70c | 87 | #include <mach/exc.h> |
1c79356b A |
88 | |
89 | #if MACH_KDB | |
90 | #include <ddb/db_trap.h> | |
91 | #endif /* MACH_KDB */ | |
92 | ||
93 | #if MACH_KDB | |
94 | ||
95 | #include <ddb/db_output.h> | |
96 | ||
97 | #if iPSC386 || iPSC860 | |
98 | boolean_t debug_user_with_kdb = TRUE; | |
99 | #else | |
100 | boolean_t debug_user_with_kdb = FALSE; | |
101 | #endif | |
102 | ||
103 | #endif /* MACH_KDB */ | |
104 | ||
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; | |
111 | ||
91447636 A |
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, | |
118 | mutex_t *mutex); | |
119 | ||
120 | #ifdef MACH_BSD | |
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 */ | |
1c79356b A |
126 | |
127 | /* | |
128 | * Routine: exception_deliver | |
129 | * Purpose: | |
130 | * Make an upcall to the exception server provided. | |
131 | * Conditions: | |
132 | * Nothing locked and no resources held. | |
133 | * Called from an exception context, so | |
134 | * thread_exception_return and thread_kdb_return | |
135 | * are possible. | |
136 | * Returns: | |
137 | * If the exception was not handled by this handler | |
138 | */ | |
139 | void | |
140 | exception_deliver( | |
141 | exception_type_t exception, | |
142 | exception_data_t code, | |
143 | mach_msg_type_number_t codeCnt, | |
144 | struct exception_action *excp, | |
145 | mutex_t *mutex) | |
146 | { | |
91447636 | 147 | thread_t self = current_thread(); |
1c79356b A |
148 | ipc_port_t exc_port; |
149 | int behavior; | |
150 | int flavor; | |
151 | kern_return_t kr; | |
152 | ||
153 | /* | |
154 | * Save work if we are terminating. | |
155 | * Just go back to our AST handler. | |
156 | */ | |
91447636 | 157 | if (!self->active) |
1c79356b A |
158 | thread_exception_return(); |
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 | mutex_lock(mutex); | |
168 | exc_port = excp->port; | |
169 | if (!IP_VALID(exc_port)) { | |
170 | mutex_unlock(mutex); | |
171 | return; | |
172 | } | |
173 | ip_lock(exc_port); | |
174 | if (!ip_active(exc_port)) { | |
175 | ip_unlock(exc_port); | |
176 | mutex_unlock(mutex); | |
177 | return; | |
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 | mutex_unlock(mutex); | |
186 | ||
187 | switch (behavior) { | |
188 | case EXCEPTION_STATE: { | |
189 | mach_msg_type_number_t state_cnt; | |
55e303ae | 190 | thread_state_data_t state; |
1c79356b A |
191 | |
192 | c_thr_exc_raise_state++; | |
91447636 A |
193 | state_cnt = _MachineStateCount[flavor]; |
194 | kr = thread_getstatus(self, flavor, | |
1c79356b A |
195 | (thread_state_t)state, |
196 | &state_cnt); | |
197 | if (kr == KERN_SUCCESS) { | |
198 | kr = exception_raise_state(exc_port, exception, | |
199 | code, codeCnt, | |
200 | &flavor, | |
201 | state, state_cnt, | |
202 | state, &state_cnt); | |
203 | if (kr == MACH_MSG_SUCCESS) | |
91447636 | 204 | kr = thread_setstatus(self, flavor, |
1c79356b A |
205 | (thread_state_t)state, |
206 | state_cnt); | |
207 | } | |
208 | ||
209 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
210 | thread_exception_return(); | |
211 | /*NOTREACHED*/ | |
212 | return; | |
213 | } | |
214 | ||
215 | case EXCEPTION_DEFAULT: | |
216 | c_thr_exc_raise++; | |
217 | kr = exception_raise(exc_port, | |
91447636 A |
218 | retrieve_thread_self_fast(self), |
219 | retrieve_task_self_fast(self->task), | |
1c79356b A |
220 | exception, |
221 | code, codeCnt); | |
222 | ||
223 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
224 | thread_exception_return(); | |
225 | /*NOTREACHED*/ | |
226 | return; | |
227 | ||
228 | case EXCEPTION_STATE_IDENTITY: { | |
229 | mach_msg_type_number_t state_cnt; | |
55e303ae | 230 | thread_state_data_t state; |
1c79356b A |
231 | |
232 | c_thr_exc_raise_state_id++; | |
91447636 A |
233 | state_cnt = _MachineStateCount[flavor]; |
234 | kr = thread_getstatus(self, flavor, | |
1c79356b A |
235 | (thread_state_t)state, |
236 | &state_cnt); | |
237 | if (kr == KERN_SUCCESS) { | |
238 | kr = exception_raise_state_identity(exc_port, | |
91447636 A |
239 | retrieve_thread_self_fast(self), |
240 | retrieve_task_self_fast(self->task), | |
1c79356b A |
241 | exception, |
242 | code, codeCnt, | |
243 | &flavor, | |
244 | state, state_cnt, | |
245 | state, &state_cnt); | |
246 | if (kr == MACH_MSG_SUCCESS) | |
91447636 | 247 | kr = thread_setstatus(self, flavor, |
1c79356b A |
248 | (thread_state_t)state, |
249 | state_cnt); | |
250 | } | |
251 | ||
252 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
253 | thread_exception_return(); | |
254 | /*NOTREACHED*/ | |
255 | return; | |
256 | } | |
257 | ||
258 | default: | |
259 | panic ("bad exception behavior!"); | |
260 | }/* switch */ | |
261 | } | |
262 | ||
263 | /* | |
264 | * Routine: exception | |
265 | * Purpose: | |
266 | * The current thread caught an exception. | |
267 | * We make an up-call to the thread's exception server. | |
268 | * Conditions: | |
269 | * Nothing locked and no resources held. | |
270 | * Called from an exception context, so | |
271 | * thread_exception_return and thread_kdb_return | |
272 | * are possible. | |
273 | * Returns: | |
274 | * Doesn't return. | |
275 | */ | |
276 | void | |
91447636 | 277 | exception_triage( |
1c79356b A |
278 | exception_type_t exception, |
279 | exception_data_t code, | |
280 | mach_msg_type_number_t codeCnt) | |
281 | { | |
91447636 | 282 | thread_t thread; |
1c79356b A |
283 | task_t task; |
284 | host_priv_t host_priv; | |
285 | struct exception_action *excp; | |
286 | mutex_t *mutex; | |
287 | ||
288 | assert(exception != EXC_RPC_ALERT); | |
289 | ||
290 | if (exception == KERN_SUCCESS) | |
291 | panic("exception"); | |
292 | ||
293 | /* | |
294 | * Try to raise the exception at the activation level. | |
295 | */ | |
91447636 A |
296 | thread = current_thread(); |
297 | mutex = mutex_addr(thread->mutex); | |
298 | excp = &thread->exc_actions[exception]; | |
1c79356b A |
299 | exception_deliver(exception, code, codeCnt, excp, mutex); |
300 | ||
301 | /* | |
302 | * Maybe the task level will handle it. | |
303 | */ | |
304 | task = current_task(); | |
305 | mutex = mutex_addr(task->lock); | |
306 | excp = &task->exc_actions[exception]; | |
307 | exception_deliver(exception, code, codeCnt, excp, mutex); | |
308 | ||
309 | /* | |
310 | * How about at the host level? | |
311 | */ | |
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); | |
316 | ||
317 | /* | |
318 | * Nobody handled it, terminate the task. | |
319 | */ | |
320 | ||
321 | #if MACH_KDB | |
322 | if (debug_user_with_kdb) { | |
323 | /* | |
324 | * Debug the exception with kdb. | |
325 | * If kdb handles the exception, | |
326 | * then thread_kdb_return won't return. | |
327 | */ | |
328 | db_printf("No exception server, calling kdb...\n"); | |
329 | thread_kdb_return(); | |
330 | } | |
331 | #endif /* MACH_KDB */ | |
332 | ||
333 | (void) task_terminate(task); | |
334 | thread_exception_return(); | |
335 | /*NOTREACHED*/ | |
336 | } | |
9bccf70c A |
337 | |
338 | kern_return_t | |
339 | bsd_exception( | |
340 | exception_type_t exception, | |
341 | exception_data_t code, | |
342 | mach_msg_type_number_t codeCnt) | |
343 | { | |
344 | task_t task; | |
9bccf70c A |
345 | struct exception_action *excp; |
346 | mutex_t *mutex; | |
91447636 | 347 | thread_t self = current_thread(); |
9bccf70c A |
348 | ipc_port_t exc_port; |
349 | int behavior; | |
350 | int flavor; | |
351 | kern_return_t kr; | |
352 | ||
353 | /* | |
354 | * Maybe the task level will handle it. | |
355 | */ | |
356 | task = current_task(); | |
357 | mutex = mutex_addr(task->lock); | |
358 | excp = &task->exc_actions[exception]; | |
359 | ||
360 | /* | |
361 | * Save work if we are terminating. | |
362 | * Just go back to our AST handler. | |
363 | */ | |
91447636 | 364 | if (!self->active) { |
9bccf70c A |
365 | return(KERN_FAILURE); |
366 | } | |
367 | ||
368 | /* | |
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. | |
374 | */ | |
375 | mutex_lock(mutex); | |
376 | exc_port = excp->port; | |
377 | if (!IP_VALID(exc_port)) { | |
378 | mutex_unlock(mutex); | |
379 | return(KERN_FAILURE); | |
380 | } | |
381 | ip_lock(exc_port); | |
382 | if (!ip_active(exc_port)) { | |
383 | ip_unlock(exc_port); | |
384 | mutex_unlock(mutex); | |
385 | return(KERN_FAILURE); | |
386 | } | |
387 | ip_reference(exc_port); | |
388 | exc_port->ip_srights++; | |
389 | ip_unlock(exc_port); | |
390 | ||
391 | flavor = excp->flavor; | |
392 | behavior = excp->behavior; | |
393 | mutex_unlock(mutex); | |
394 | ||
395 | switch (behavior) { | |
396 | case EXCEPTION_STATE: { | |
397 | mach_msg_type_number_t state_cnt; | |
55e303ae | 398 | thread_state_data_t state; |
9bccf70c A |
399 | |
400 | c_thr_exc_raise_state++; | |
91447636 A |
401 | state_cnt = _MachineStateCount[flavor]; |
402 | kr = thread_getstatus(self, flavor, | |
9bccf70c A |
403 | (thread_state_t)state, |
404 | &state_cnt); | |
405 | if (kr == KERN_SUCCESS) { | |
406 | kr = exception_raise_state(exc_port, exception, | |
407 | code, codeCnt, | |
408 | &flavor, | |
409 | state, state_cnt, | |
410 | state, &state_cnt); | |
411 | if (kr == MACH_MSG_SUCCESS) | |
91447636 | 412 | kr = thread_setstatus(self, flavor, |
9bccf70c A |
413 | (thread_state_t)state, |
414 | state_cnt); | |
415 | } | |
416 | ||
417 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
418 | return(KERN_SUCCESS); | |
419 | ||
420 | return(KERN_FAILURE); | |
421 | } | |
422 | ||
423 | case EXCEPTION_DEFAULT: | |
424 | c_thr_exc_raise++; | |
425 | kr = exception_raise(exc_port, | |
91447636 A |
426 | retrieve_thread_self_fast(self), |
427 | retrieve_task_self_fast(self->task), | |
9bccf70c A |
428 | exception, |
429 | code, codeCnt); | |
430 | ||
431 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
432 | return(KERN_SUCCESS); | |
433 | return(KERN_FAILURE); | |
434 | ||
435 | case EXCEPTION_STATE_IDENTITY: { | |
436 | mach_msg_type_number_t state_cnt; | |
55e303ae | 437 | thread_state_data_t state; |
9bccf70c A |
438 | |
439 | c_thr_exc_raise_state_id++; | |
91447636 A |
440 | state_cnt = _MachineStateCount[flavor]; |
441 | kr = thread_getstatus(self, flavor, | |
9bccf70c A |
442 | (thread_state_t)state, |
443 | &state_cnt); | |
444 | if (kr == KERN_SUCCESS) { | |
445 | kr = exception_raise_state_identity(exc_port, | |
91447636 A |
446 | retrieve_thread_self_fast(self), |
447 | retrieve_task_self_fast(self->task), | |
9bccf70c A |
448 | exception, |
449 | code, codeCnt, | |
450 | &flavor, | |
451 | state, state_cnt, | |
452 | state, &state_cnt); | |
453 | if (kr == MACH_MSG_SUCCESS) | |
91447636 | 454 | kr = thread_setstatus(self, flavor, |
9bccf70c A |
455 | (thread_state_t)state, |
456 | state_cnt); | |
457 | } | |
458 | ||
459 | if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) | |
460 | return(KERN_SUCCESS); | |
461 | return(KERN_FAILURE); | |
462 | } | |
463 | ||
464 | default: | |
465 | ||
466 | return(KERN_FAILURE); | |
467 | }/* switch */ | |
468 | return(KERN_FAILURE); | |
469 | } | |
470 | ||
55e303ae A |
471 | |
472 | ||
473 | ||
474 | /* | |
475 | * Handle interface for special perfomance monitoring | |
476 | * This is a special case of the host exception handler | |
477 | */ | |
478 | ||
479 | kern_return_t sys_perf_notify(struct task *task, | |
480 | exception_data_t code, | |
481 | mach_msg_type_number_t codeCnt) | |
482 | { | |
483 | host_priv_t hostp; | |
484 | struct exception_action *excp; | |
91447636 | 485 | thread_t thread = current_thread(); |
55e303ae A |
486 | ipc_port_t xport; |
487 | kern_return_t ret; | |
55e303ae A |
488 | wait_interrupt_t wsave; |
489 | ||
490 | hostp = host_priv_self(); /* Get the host privileged ports */ | |
491 | excp = &hostp->exc_actions[EXC_RPC_ALERT]; /* Point to the RPC_ALERT action */ | |
492 | ||
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... */ | |
498 | } | |
499 | ||
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... */ | |
505 | } | |
506 | ||
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... */ | |
511 | } | |
512 | ||
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 */ | |
516 | ||
517 | mutex_unlock(&hostp->lock); /* All done with the lock */ | |
518 | ||
519 | wsave = thread_interrupt_level(THREAD_UNINT); /* Make sure we aren't aborted here */ | |
520 | ||
521 | ret = exception_raise(xport, /* Send the exception to the perf handler */ | |
91447636 A |
522 | retrieve_thread_self_fast(thread), /* Not always the dying guy */ |
523 | retrieve_task_self_fast(thread->task), /* Not always the dying guy */ | |
55e303ae A |
524 | EXC_RPC_ALERT, /* Unused exception type until now */ |
525 | code, codeCnt); | |
526 | ||
527 | (void)thread_interrupt_level(wsave); /* Restore interrupt level */ | |
528 | ||
529 | return(ret); /* Tell caller how it went */ | |
530 | } |