No empty .Rs/.Re
[netbsd-mini2440.git] / sys / compat / mach / mach_exception.c
blobab384580a6e589c61567dd77c2294b0776d6583f
1 /* $NetBSD: mach_exception.c,v 1.13 2008/04/28 20:23:44 martin Exp $ */
3 /*-
4 * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 * All rights reserved.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Emmanuel Dreyfus
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: mach_exception.c,v 1.13 2008/04/28 20:23:44 martin Exp $");
35 #include "opt_compat_darwin.h"
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/signal.h>
40 #include <sys/proc.h>
41 #include <sys/malloc.h>
43 #ifdef COMPAT_DARWIN
44 #include <compat/darwin/darwin_exec.h>
45 #endif
47 #include <compat/mach/mach_types.h>
48 #include <compat/mach/mach_exec.h>
49 #include <compat/mach/mach_errno.h>
50 #include <compat/mach/mach_thread.h>
51 #include <compat/mach/mach_exception.h>
52 #include <compat/mach/mach_message.h>
53 #include <compat/mach/mach_services.h>
54 #include <compat/mach/mach_sysctl.h>
56 #include <machine/mach_machdep.h>
58 static void mach_siginfo_to_exception(const struct ksiginfo *, int *);
61 * Exception handler
62 * Mach does not use signals. But systems based on Mach (e.g.: Darwin),
63 * can use both Mach exceptions and UNIX signals. In order to allow the
64 * Mach layer to intercept the exception and inhibit UNIX signals, we have
65 * mach_trapsignal1 returning an error. If it returns 0, then the
66 * exception was intercepted at the Mach level, and no signal should
67 * be produced. Else, a signal might be sent. darwin_trapinfo calls
68 * mach_trapinfo1 and handle signals if it gets a non zero return value.
70 void
71 mach_trapsignal(struct lwp *l, struct ksiginfo *ksi)
73 if (mach_trapsignal1(l, ksi) != 0)
74 trapsignal(l, ksi);
75 return;
78 int
79 mach_trapsignal1(struct lwp *l, struct ksiginfo *ksi)
81 struct proc *p = l->l_proc;
82 struct mach_emuldata *med;
83 int exc_no;
84 int code[2];
86 /* Don't inhinbit non maskable signals */
87 if (sigprop[ksi->ksi_signo] & SA_CANTMASK)
88 return EINVAL;
90 med = (struct mach_emuldata *)p->p_emuldata;
92 switch (ksi->ksi_signo) {
93 case SIGILL:
94 exc_no = MACH_EXC_BAD_INSTRUCTION;
95 break;
96 case SIGFPE:
97 exc_no = MACH_EXC_ARITHMETIC;
98 break;
99 case SIGSEGV:
100 case SIGBUS:
101 exc_no = MACH_EXC_BAD_ACCESS;
102 break;
103 case SIGTRAP:
104 exc_no = MACH_EXC_BREAKPOINT;
105 break;
106 default: /* SIGCHLD, SIGPOLL */
107 return EINVAL;
108 break;
111 mach_siginfo_to_exception(ksi, code);
113 return mach_exception(l, exc_no, code);
117 mach_exception(struct lwp *exc_l, int exc, int *code)
118 /* exc_l: currently running lwp */
120 int behavior, flavor;
121 mach_msg_header_t *msgh;
122 size_t msglen;
123 struct mach_right *exc_mr;
124 struct mach_emuldata *exc_med;
125 struct mach_lwp_emuldata *exc_mle;
126 struct mach_emuldata *catcher_med;
127 struct mach_right *kernel_mr;
128 struct lwp *catcher_l; /* The lwp catching the exception */
129 struct mach_right *exc_task;
130 struct mach_right *exc_thread;
131 struct mach_port *exc_port;
132 struct mach_exc_info *mei;
133 int error = 0;
135 #ifdef DIAGNOSTIC
136 if (exc_l == NULL) {
137 printf("mach_exception: exc_l = %p\n", exc_l);
138 return ESRCH;
140 #endif
141 #ifdef DEBUG_MACH
142 printf("mach_exception: %d.%d, exc %d, code (%d, %d)\n",
143 exc_l->l_proc->p_pid, exc_l->l_lid, exc, code[0], code[1]);
144 #endif
147 * It's extremely useful to have the ability of catching
148 * the process at the time it dies.
150 if (mach_exception_hang) {
151 struct proc *p = exc_l->l_proc;
153 sigminusset(&contsigmask, &exc_l->l_sigpendset->sp_set);
154 lwp_lock(exc_l);
155 p->p_pptr->p_nstopchild++;
156 p->p_stat = SSTOP;
157 exc_l->l_stat = LSSTOP;
158 p->p_nrlwps--;
159 KERNEL_UNLOCK_ALL(exc_l, &exc_l->l_biglocks);
160 mi_switch(exc_l);
161 KERNEL_LOCK(exc_l->l_biglocks, exc_l);
165 * No exception if there is no exception port or if it has no receiver
167 exc_mle = exc_l->l_emuldata;
168 exc_med = exc_l->l_proc->p_emuldata;
169 if ((exc_port = exc_med->med_exc[exc]) == NULL)
170 return EINVAL;
172 MACH_PORT_REF(exc_port);
173 if (exc_port->mp_recv == NULL) {
174 error = EINVAL;
175 goto out;
178 #ifdef DEBUG_MACH
179 printf("catcher is %d.%d, state %d\n",
180 exc_port->mp_recv->mr_lwp->l_proc->p_pid,
181 exc_port->mp_recv->mr_lwp->l_lid,
182 exc_port->mp_recv->mr_lwp->l_proc->p_stat);
183 #endif
185 * Don't send exceptions to dying processes
187 if (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc)) {
188 error = ESRCH;
189 goto out;
193 * XXX Avoid a nasty deadlock because process in TX state
194 * (traced and suspended) are invulnerable to kill -9.
196 * The scenario:
197 * - the parent gets Child's signals though Mach exceptions
198 * - the parent is killed. Before calling the emulation hook
199 * mach_exit(), it will wait for the child
200 * - the child receives SIGHUP, which is turned into a Mach
201 * exception. The child sleeps awaiting for the parent
202 * to tell it to continue.
203 * For some reason I do not understand, it goes in the
204 * suspended state instead of the sleeping state.
205 * - Parents waits for the child, child is suspended, we
206 * are stuck.
208 * By preventing exception to traced processes with
209 * a dying parent, a signal is sent instead of the
210 * notification, this fixes the problem.
212 if ((exc_l->l_proc->p_slflag & PSL_TRACED) &&
213 (exc_l->l_proc->p_pptr->p_sflag & PS_WEXIT)) {
214 #ifdef DEBUG_MACH
215 printf("mach_exception: deadlock avoided\n");
216 #endif
217 error = EINVAL;
218 goto out;
221 if (exc_port->mp_datatype != MACH_MP_EXC_INFO) {
222 #ifdef DIAGNOSTIC
223 printf("mach_exception: unexpected datatype");
224 #endif
225 error = EINVAL;
226 goto out;
228 mei = exc_port->mp_data;
229 behavior = mei->mei_behavior;
230 flavor = mei->mei_flavor;
233 * We want the port names in the target process, that is,
234 * the process with receive right for exc_port.
236 catcher_l = exc_port->mp_recv->mr_lwp;
237 catcher_med = catcher_l->l_proc->p_emuldata;
238 exc_mr = mach_right_get(exc_port, catcher_l, MACH_PORT_TYPE_SEND, 0);
239 kernel_mr = mach_right_get(catcher_med->med_kernel,
240 catcher_l, MACH_PORT_TYPE_SEND, 0);
242 exc_task = mach_right_get(exc_med->med_kernel,
243 catcher_l, MACH_PORT_TYPE_SEND, 0);
244 exc_thread = mach_right_get(exc_mle->mle_kernel,
245 catcher_l, MACH_PORT_TYPE_SEND, 0);
247 switch (behavior) {
248 case MACH_EXCEPTION_DEFAULT: {
249 mach_exception_raise_request_t *req;
251 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
252 msglen = sizeof(*req);
253 msgh = (mach_msg_header_t *)req;
255 req->req_msgh.msgh_bits =
256 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
257 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
258 req->req_msgh.msgh_size =
259 sizeof(*req) - sizeof(req->req_trailer);
260 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
261 req->req_msgh.msgh_local_port = exc_mr->mr_name;
262 req->req_msgh.msgh_id = MACH_EXC_RAISE_MSGID;
264 mach_add_port_desc(req, exc_thread->mr_name);
265 mach_add_port_desc(req, exc_task->mr_name);
267 req->req_exc = exc;
268 req->req_codecount = 2;
269 memcpy(&req->req_code[0], code, sizeof(req->req_code));
271 mach_set_trailer(req, msglen);
273 break;
276 case MACH_EXCEPTION_STATE: {
277 mach_exception_raise_state_request_t *req;
278 int dc;
280 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
281 msglen = sizeof(*req);
282 msgh = (mach_msg_header_t *)req;
284 req->req_msgh.msgh_bits =
285 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
286 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
287 req->req_msgh.msgh_size =
288 sizeof(*req) - sizeof(req->req_trailer);
289 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
290 req->req_msgh.msgh_local_port = exc_mr->mr_name;
291 req->req_msgh.msgh_id = MACH_EXCEPTION_STATE;
292 req->req_exc = exc;
293 req->req_codecount = 2;
294 memcpy(&req->req_code[0], code, sizeof(req->req_code));
295 req->req_flavor = flavor;
296 mach_thread_get_state_machdep(exc_l,
297 flavor, req->req_state, &dc);
299 msglen = msglen -
300 sizeof(req->req_state) +
301 (dc * sizeof(req->req_state[0]));
302 mach_set_trailer(req, msglen);
304 break;
307 case MACH_EXCEPTION_STATE_IDENTITY: {
308 mach_exception_raise_state_identity_request_t *req;
309 int dc;
311 req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
312 msglen = sizeof(*req);
313 msgh = (mach_msg_header_t *)req;
315 req->req_msgh.msgh_bits =
316 MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
317 MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
318 req->req_msgh.msgh_size =
319 sizeof(*req) - sizeof(req->req_trailer);
320 req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
321 req->req_msgh.msgh_local_port = exc_mr->mr_name;
322 req->req_msgh.msgh_id = MACH_EXC_RAISE_STATE_IDENTITY_MSGID;
323 req->req_body.msgh_descriptor_count = 2;
325 mach_add_port_desc(req, exc_thread->mr_name);
326 mach_add_port_desc(req, exc_task->mr_name);
328 req->req_exc = exc;
329 req->req_codecount = 2;
330 memcpy(&req->req_code[0], code, sizeof(req->req_code));
331 req->req_flavor = flavor;
332 mach_thread_get_state_machdep(exc_l,
333 flavor, req->req_state, &dc);
335 msglen = msglen -
336 sizeof(req->req_state) +
337 (dc * sizeof(req->req_state[0]));
339 mach_set_trailer(req, msglen);
341 break;
344 default:
345 printf("unknown exception bevahior %d\n", behavior);
346 error = EINVAL;
347 goto out;
348 break;
351 mach_set_trailer(msgh, msglen);
354 * Once an exception is sent on the exception port,
355 * no new exception will be taken until the catcher
356 * acknowledge the first one.
358 rw_enter(&catcher_med->med_exclock, RW_WRITER);
361 * If the catcher died, we are done.
363 if (((exc_port = exc_med->med_exc[exc]) == NULL) ||
364 (exc_port->mp_recv == NULL) ||
365 (P_ZOMBIE(exc_port->mp_recv->mr_lwp->l_proc))) {
366 error = ESRCH;
367 goto out;
370 (void)mach_message_get(msgh, msglen, exc_port, NULL);
371 wakeup(exc_port->mp_recv->mr_sethead);
374 * The thread that caused the exception is now
375 * supposed to wait for a reply to its message.
377 #ifdef DEBUG_MACH
378 printf("mach_exception: %d.%d sleep on catcher_med->med_exclock = %p\n",
379 exc_l->l_proc->p_pid, exc_l->l_lid, &catcher_med->med_exclock);
380 #endif
381 error = tsleep(&catcher_med->med_exclock, PZERO, "mach_exc", 0);
382 #ifdef DEBUG_MACH
383 printf("mach_exception: %d.%d resumed, error = %d\n",
384 exc_l->l_proc->p_pid, exc_l->l_lid, error);
385 #endif
388 * Unlock the catcher's exception handler
390 rw_exit(&catcher_med->med_exclock);
392 out:
393 MACH_PORT_UNREF(exc_port);
394 return error;
397 static void
398 mach_siginfo_to_exception(const struct ksiginfo *ksi, int *code)
400 code[1] = (long)ksi->ksi_addr;
401 switch (ksi->ksi_signo) {
402 case SIGBUS:
403 switch (ksi->ksi_code) {
404 case BUS_ADRALN:
405 code[0] = MACH_BUS_ADRALN;
406 break;
407 default:
408 printf("untranslated siginfo signo %d, code %d\n",
409 ksi->ksi_signo, ksi->ksi_code);
410 break;
412 break;
414 case SIGSEGV:
415 switch (ksi->ksi_code) {
416 case SEGV_MAPERR:
417 code[0] = MACH_SEGV_MAPERR;
418 break;
419 case SEGV_ACCERR:
420 code[0] = MACH_SEGV_ACCERR;
421 break;
422 default:
423 printf("untranslated siginfo signo %d, code %d\n",
424 ksi->ksi_signo, ksi->ksi_code);
425 break;
427 break;
429 case SIGTRAP:
430 switch (ksi->ksi_code) {
431 case TRAP_BRKPT:
432 code[0] = MACH_TRAP_BRKPT;
433 code[1] = (long)ksi->ksi_addr;
434 break;
435 default:
436 printf("untranslated siginfo signo %d, code %d\n",
437 ksi->ksi_signo, ksi->ksi_code);
438 break;
440 break;
442 case SIGILL:
443 switch (ksi->ksi_code) {
444 case ILL_ILLOPC:
445 case ILL_ILLOPN:
446 case ILL_ILLADR:
447 code[0] = MACH_ILL_ILLOPC;
448 break;
449 case ILL_PRVOPC:
450 case ILL_PRVREG:
451 code[0] = MACH_ILL_PRVOPC;
452 break;
453 case ILL_ILLTRP:
454 code[0] = MACH_ILL_ILLTRP;
455 break;
456 default:
457 printf("untranslated siginfo signo %d, code %d\n",
458 ksi->ksi_signo, ksi->ksi_code);
459 break;
461 break;
463 default:
464 printf("untranslated siginfo signo %d, code %d\n",
465 ksi->ksi_signo, ksi->ksi_code);
466 break;
471 mach_exception_raise(struct mach_trap_args *args)
473 struct lwp *l = args->l;
474 mach_exception_raise_reply_t *rep;
475 struct mach_emuldata *med;
478 * No typo here: the reply is in the sent message.
479 * The kernel is acting as a client that gets the
480 * reply message to its exception message.
482 rep = args->smsg;
485 * This message is sent by the process catching the
486 * exception to release the process that raised the exception.
487 * We wake it up if the return value is 0 (no error), else
488 * we should ignore this message.
490 #ifdef DEBUG_MACH
491 printf("mach_excpetion_raise: retval = %ld\n", (long)rep->rep_retval);
492 #endif
493 if (rep->rep_retval != 0)
494 return 0;
496 med = l->l_proc->p_emuldata;
499 * Check for unexpected exception acknowledge, whereas
500 * the kernel sent no exception message.
502 if (!rw_lock_held(&med->med_exclock)) {
503 #ifdef DEBUG_MACH
504 printf("spurious mach_exception_raise\n");
505 #endif
506 return mach_msg_error(args, EINVAL);
510 * Wakeup the thread that raised the exception.
512 #ifdef DEBUG_MACH
513 printf("mach_exception_raise: wakeup at %p\n", &med->med_exclock);
514 #endif
515 wakeup(&med->med_exclock);
517 return 0;
521 mach_exception_raise_state(struct mach_trap_args *args)
523 return mach_exception_raise(args);
527 mach_exception_raise_state_identity(struct mach_trap_args *args)
529 return mach_exception_raise(args);