]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/exception.c
xnu-201.42.3.tar.gz
[apple/xnu.git] / osfmk / kern / exception.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * @OSF_COPYRIGHT@
24 */
25 /*
26 * Mach Operating System
27 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
28 * All Rights Reserved.
29 *
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.
35 *
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.
39 *
40 * Carnegie Mellon requests users of this software to return to
41 *
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
46 *
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
49 */
50 /*
51 */
52
53 #include <mach_kdb.h>
54
55 #include <mach/boolean.h>
56 #include <mach/kern_return.h>
57 #include <mach/message.h>
58 #include <mach/port.h>
59 #include <mach/mig_errors.h>
60 #include <mach/thread_status.h>
61 #include <mach/exception_types.h>
62 #include <ipc/port.h>
63 #include <ipc/ipc_entry.h>
64 #include <ipc/ipc_object.h>
65 #include <ipc/ipc_notify.h>
66 #include <ipc/ipc_space.h>
67 #include <ipc/ipc_pset.h>
68 #include <ipc/ipc_machdep.h>
69 #include <kern/etap_macros.h>
70 #include <kern/counters.h>
71 #include <kern/ipc_tt.h>
72 #include <kern/task.h>
73 #include <kern/thread.h>
74 #include <kern/thread_swap.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>
80 #include <string.h>
81 #include <mach/exc.h> /* JMM - will become exception.h */
82 #include <machine/machine_rpc.h>
83
84 #if MACH_KDB
85 #include <ddb/db_trap.h>
86 #endif /* MACH_KDB */
87
88 #if MACH_KDB
89
90 #include <ddb/db_output.h>
91
92 #if iPSC386 || iPSC860
93 boolean_t debug_user_with_kdb = TRUE;
94 #else
95 boolean_t debug_user_with_kdb = FALSE;
96 #endif
97
98 #endif /* MACH_KDB */
99
100 unsigned long c_thr_exc_raise = 0;
101 unsigned long c_thr_exc_raise_state = 0;
102 unsigned long c_thr_exc_raise_state_id = 0;
103 unsigned long c_tsk_exc_raise = 0;
104 unsigned long c_tsk_exc_raise_state = 0;
105 unsigned long c_tsk_exc_raise_state_id = 0;
106
107
108 /*
109 * Routine: exception_deliver
110 * Purpose:
111 * Make an upcall to the exception server provided.
112 * Conditions:
113 * Nothing locked and no resources held.
114 * Called from an exception context, so
115 * thread_exception_return and thread_kdb_return
116 * are possible.
117 * Returns:
118 * If the exception was not handled by this handler
119 */
120 void
121 exception_deliver(
122 exception_type_t exception,
123 exception_data_t code,
124 mach_msg_type_number_t codeCnt,
125 struct exception_action *excp,
126 mutex_t *mutex)
127 {
128 thread_act_t a_self = current_act();
129 ipc_port_t exc_port;
130 int behavior;
131 int flavor;
132 kern_return_t kr;
133
134 /*
135 * Save work if we are terminating.
136 * Just go back to our AST handler.
137 */
138 if (!a_self->active)
139 thread_exception_return();
140
141 /*
142 * Snapshot the exception action data under lock for consistency.
143 * Hold a reference to the port over the exception_raise_* calls
144 * so it can't be destroyed. This seems like overkill, but keeps
145 * the port from disappearing between now and when
146 * ipc_object_copyin_from_kernel is finally called.
147 */
148 mutex_lock(mutex);
149 exc_port = excp->port;
150 if (!IP_VALID(exc_port)) {
151 mutex_unlock(mutex);
152 return;
153 }
154 ip_lock(exc_port);
155 if (!ip_active(exc_port)) {
156 ip_unlock(exc_port);
157 mutex_unlock(mutex);
158 return;
159 }
160 ip_reference(exc_port);
161 exc_port->ip_srights++;
162 ip_unlock(exc_port);
163
164 flavor = excp->flavor;
165 behavior = excp->behavior;
166 mutex_unlock(mutex);
167
168 switch (behavior) {
169 case EXCEPTION_STATE: {
170 mach_msg_type_number_t state_cnt;
171 natural_t state[ THREAD_MACHINE_STATE_MAX ];
172
173 c_thr_exc_raise_state++;
174 state_cnt = state_count[flavor];
175 kr = thread_getstatus(a_self, flavor,
176 (thread_state_t)state,
177 &state_cnt);
178 if (kr == KERN_SUCCESS) {
179 kr = exception_raise_state(exc_port, exception,
180 code, codeCnt,
181 &flavor,
182 state, state_cnt,
183 state, &state_cnt);
184 if (kr == MACH_MSG_SUCCESS)
185 kr = thread_setstatus(a_self, flavor,
186 (thread_state_t)state,
187 state_cnt);
188 }
189
190 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
191 thread_exception_return();
192 /*NOTREACHED*/
193 return;
194 }
195
196 case EXCEPTION_DEFAULT:
197 c_thr_exc_raise++;
198 kr = exception_raise(exc_port,
199 retrieve_act_self_fast(a_self),
200 retrieve_task_self_fast(a_self->task),
201 exception,
202 code, codeCnt);
203
204 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
205 thread_exception_return();
206 /*NOTREACHED*/
207 return;
208
209 case EXCEPTION_STATE_IDENTITY: {
210 mach_msg_type_number_t state_cnt;
211 natural_t state[ THREAD_MACHINE_STATE_MAX ];
212
213 c_thr_exc_raise_state_id++;
214 state_cnt = state_count[flavor];
215 kr = thread_getstatus(a_self, flavor,
216 (thread_state_t)state,
217 &state_cnt);
218 if (kr == KERN_SUCCESS) {
219 kr = exception_raise_state_identity(exc_port,
220 retrieve_act_self_fast(a_self),
221 retrieve_task_self_fast(a_self->task),
222 exception,
223 code, codeCnt,
224 &flavor,
225 state, state_cnt,
226 state, &state_cnt);
227 if (kr == MACH_MSG_SUCCESS)
228 kr = thread_setstatus(a_self, flavor,
229 (thread_state_t)state,
230 state_cnt);
231 }
232
233 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
234 thread_exception_return();
235 /*NOTREACHED*/
236 return;
237 }
238
239 default:
240 panic ("bad exception behavior!");
241 }/* switch */
242 }
243
244 /*
245 * Routine: exception
246 * Purpose:
247 * The current thread caught an exception.
248 * We make an up-call to the thread's exception server.
249 * Conditions:
250 * Nothing locked and no resources held.
251 * Called from an exception context, so
252 * thread_exception_return and thread_kdb_return
253 * are possible.
254 * Returns:
255 * Doesn't return.
256 */
257 void
258 exception(
259 exception_type_t exception,
260 exception_data_t code,
261 mach_msg_type_number_t codeCnt)
262 {
263 thread_act_t thr_act;
264 task_t task;
265 host_priv_t host_priv;
266 struct exception_action *excp;
267 mutex_t *mutex;
268
269 assert(exception != EXC_RPC_ALERT);
270
271 if (exception == KERN_SUCCESS)
272 panic("exception");
273
274 /*
275 * Try to raise the exception at the activation level.
276 */
277 thr_act = current_act();
278 mutex = mutex_addr(thr_act->lock);
279 excp = &thr_act->exc_actions[exception];
280 exception_deliver(exception, code, codeCnt, excp, mutex);
281
282 /*
283 * Maybe the task level will handle it.
284 */
285 task = current_task();
286 mutex = mutex_addr(task->lock);
287 excp = &task->exc_actions[exception];
288 exception_deliver(exception, code, codeCnt, excp, mutex);
289
290 /*
291 * How about at the host level?
292 */
293 host_priv = host_priv_self();
294 mutex = mutex_addr(host_priv->lock);
295 excp = &host_priv->exc_actions[exception];
296 exception_deliver(exception, code, codeCnt, excp, mutex);
297
298 /*
299 * Nobody handled it, terminate the task.
300 */
301
302 #if MACH_KDB
303 if (debug_user_with_kdb) {
304 /*
305 * Debug the exception with kdb.
306 * If kdb handles the exception,
307 * then thread_kdb_return won't return.
308 */
309 db_printf("No exception server, calling kdb...\n");
310 thread_kdb_return();
311 }
312 #endif /* MACH_KDB */
313
314 (void) task_terminate(task);
315 thread_exception_return();
316 /*NOTREACHED*/
317 }