NetBSD/sys/compat/mach/mach_notify.c
2003-11-18 11:20:34 +00:00

541 lines
15 KiB
C

/* $NetBSD: mach_notify.c,v 1.6 2003/11/18 11:20:34 manu Exp $ */
/*-
* Copyright (c) 2003 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Emmanuel Dreyfus
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mach_notify.c,v 1.6 2003/11/18 11:20:34 manu Exp $");
#include "opt_ktrace.h"
#include "opt_compat_mach.h" /* For COMPAT_MACH in <sys/ktrace.h> */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
#include <compat/mach/mach_types.h>
#include <compat/mach/mach_exec.h>
#include <compat/mach/mach_thread.h>
#include <compat/mach/mach_notify.h>
#include <compat/mach/mach_message.h>
#include <compat/mach/mach_services.h>
static void mach_siginfo_to_exception(const struct ksiginfo *, int *);
void
mach_notify_port_destroyed(l, mr)
struct lwp *l;
struct mach_right *mr;
{
struct mach_port *mp;
mach_notify_port_destroyed_request_t *req;
if (mr->mr_notify_destroyed == NULL)
return;
mp = mr->mr_notify_destroyed->mr_port;
#ifdef DEBUG_MACH
if (mp == NULL) {
printf("Warning: notification right without a port\n");
return;
}
#endif
req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
req->req_msgh.msgh_bits =
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
req->req_msgh.msgh_size = sizeof(*req) - sizeof(req->req_trailer);
req->req_msgh.msgh_local_port = mr->mr_notify_destroyed->mr_name;
req->req_msgh.msgh_id = MACH_NOTIFY_DESTROYED_MSGID;
req->req_body.msgh_descriptor_count = 1;
req->req_rights.name = mr->mr_name;
req->req_trailer.msgh_trailer_size = 8;
(void)mach_message_get((mach_msg_header_t *)req, sizeof(*req), mp, l);
#ifdef DEBUG_MACH_MSG
printf("pid %d: message queued on port %p (%d) [%p]\n",
l->l_proc->p_pid, mp, req->req_msgh.msgh_id,
mp->mp_recv->mr_sethead);
#endif
wakeup(mp->mp_recv->mr_sethead);
return;
}
void
mach_notify_port_no_senders(l, mr)
struct lwp *l;
struct mach_right *mr;
{
struct mach_port *mp;
mach_notify_port_no_senders_request_t *req;
if ((mr->mr_notify_no_senders == NULL) ||
(mr->mr_notify_no_senders->mr_port == NULL))
return;
mp = mr->mr_notify_no_senders->mr_port;
#ifdef DEBUG_MACH
if ((mp == NULL) || (mp->mp_datatype != MACH_MP_NOTIFY_SYNC)) {
printf("Warning: notification right without a port\n");
return;
}
#endif
if ((int)mp->mp_data >= mr->mr_refcount)
return;
req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
req->req_msgh.msgh_bits =
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
req->req_msgh.msgh_size = sizeof(*req) - sizeof(req->req_trailer);
req->req_msgh.msgh_local_port = mr->mr_notify_no_senders->mr_name;
req->req_msgh.msgh_id = MACH_NOTIFY_NO_SENDERS_MSGID;
req->req_mscount = mr->mr_refcount;
req->req_trailer.msgh_trailer_size = 8;
(void)mach_message_get((mach_msg_header_t *)req, sizeof(*req), mp, l);
#ifdef DEBUG_MACH_MSG
printf("pid %d: message queued on port %p (%d) [%p]\n",
l->l_proc->p_pid, mp, req->req_msgh.msgh_id,
mp->mp_recv->mr_sethead);
#endif
wakeup(mp->mp_recv->mr_sethead);
return;
}
void
mach_notify_port_dead_name(l, mr)
struct lwp *l;
struct mach_right *mr;
{
struct mach_port *mp;
mach_notify_port_dead_name_request_t *req;
if ((mr->mr_notify_dead_name == NULL) ||
(mr->mr_notify_dead_name->mr_port == NULL))
return;
mp = mr->mr_notify_no_senders->mr_port;
#ifdef DEBUG_MACH
if (mp == NULL) {
printf("Warning: notification right without a port\n");
return;
}
#endif
req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
req->req_msgh.msgh_bits =
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
req->req_msgh.msgh_size = sizeof(*req) - sizeof(req->req_trailer);
req->req_msgh.msgh_local_port = mr->mr_notify_dead_name->mr_name;
req->req_msgh.msgh_id = MACH_NOTIFY_DEAD_NAME_MSGID;
req->req_name = mr->mr_name;
req->req_trailer.msgh_trailer_size = 8;
mr->mr_refcount++;
mp = mr->mr_notify_dead_name->mr_port;
(void)mach_message_get((mach_msg_header_t *)req, sizeof(*req), mp, l);
#ifdef DEBUG_MACH_MSG
printf("pid %d: message queued on port %p (%d) [%p]\n",
l->l_proc->p_pid, mp, req->req_msgh.msgh_id,
mp->mp_recv->mr_sethead);
#endif
wakeup(mp->mp_recv->mr_sethead);
return;
}
/*
* Exception handler
* Mach does not use signals, so mach_trapsignal will not try to send
* any signal. But systems based on Mach (e.g.: Darwin), can use both
* Mach exceptions and UNIX signals. In order to allow the Mach layer
* to intercept the exception and inhiubit UNIX signals, we have
* mach_trapsignal1 returning an error. If it returns 0, then the
* exception was intercepted at the Mach level, and no signal should
* be produced. Else, a signal might be sent. darwin_trapinfo calls
* mach_trapinfo1 and handle signls if it gets a non zero return value.
*/
void
mach_trapsignal(l, ksi)
struct lwp *l;
const struct ksiginfo *ksi;
{
(void)mach_trapsignal1(l, ksi);
return;
}
int
mach_trapsignal1(l, ksi)
struct lwp *l;
const struct ksiginfo *ksi;
{
struct proc *p = l->l_proc;
struct mach_emuldata *med;
struct mach_port *exc_port;
struct mach_right *task;
struct mach_right *thread;
int exc_no;
med = (struct mach_emuldata *)p->p_emuldata;
/* XXX Thread and task should have different ports */
task = mach_right_get(med->med_kernel, l, MACH_PORT_TYPE_SEND, 0);
thread = mach_right_get(med->med_kernel, l, MACH_PORT_TYPE_SEND, 0);
switch (ksi->ksi_signo) {
case SIGILL:
exc_no = MACH_EXC_BAD_INSTRUCTION;
break;
case SIGFPE:
exc_no = MACH_EXC_ARITHMETIC;
break;
case SIGSEGV:
case SIGBUS:
exc_no = MACH_EXC_BAD_ACCESS;
break;
case SIGTRAP:
exc_no = MACH_EXC_BREAKPOINT;
break;
default: /* SIGCHLD, SIGPOLL */
return EINVAL;
break;
}
if ((exc_port = med->med_exc[exc_no]) == NULL)
return EINVAL;
else
return mach_exception(l, ksi, exc_port, exc_no, task, thread);
}
int
mach_exception(l, ksi, exc_port, exc, task, thread)
struct lwp *l;
const struct ksiginfo *ksi;
struct mach_port *exc_port;
int exc;
struct mach_right *task;
struct mach_right *thread;
{
int behavior, flavor;
mach_msg_header_t *msgh;
size_t msglen;
struct mach_right *exc_mr;
struct mach_emuldata *med;
struct mach_right *kernel_mr;
struct lwp *catcher_lwp;
int error;
#ifdef DIAGNOSTIC
if (exc_port->mp_datatype != MACH_MP_EXC_FLAGS)
printf("mach_exception: unexpected datatype");
#endif
behavior = (int)exc_port->mp_data >> 16;
flavor = (int)exc_port->mp_data & 0xffff;
/*
* We want the port names in the target process, that is,
* the process with receive right for exc_port.
*/
catcher_lwp = exc_port->mp_recv->mr_lwp;
med = catcher_lwp->l_proc->p_emuldata;
exc_mr = mach_right_get(exc_port, catcher_lwp, MACH_PORT_TYPE_SEND, 0);
kernel_mr = mach_right_get(med->med_kernel,
catcher_lwp, MACH_PORT_TYPE_SEND, 0);
switch (behavior) {
case MACH_EXCEPTION_DEFAULT: {
mach_exception_raise_request_t *req;
req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
msglen = sizeof(*req);
msgh = (mach_msg_header_t *)req;
req->req_msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
req->req_msgh.msgh_size =
sizeof(*req) - sizeof(req->req_trailer);
req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
req->req_msgh.msgh_local_port = exc_mr->mr_name;
req->req_msgh.msgh_id = MACH_EXC_RAISE_MSGID;
req->req_body.msgh_descriptor_count = 2;
req->req_thread.name = thread->mr_name;
req->req_thread.disposition = 0x11; /* XXX */
req->req_thread.type = 0; /* XXX */
req->req_task.name = task->mr_name;
req->req_task.disposition = 0x11; /* XXX */
req->req_task.type = 0; /* XXX */
req->req_exc = exc;
req->req_codecount = 2;
mach_siginfo_to_exception(ksi, &req->req_code[0]);
req->req_trailer.msgh_trailer_size = 8;
break;
}
case MACH_EXCEPTION_STATE: {
mach_exception_raise_state_request_t *req;
int dc;
req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
msglen = sizeof(*req);
msgh = (mach_msg_header_t *)req;
req->req_msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
req->req_msgh.msgh_size =
sizeof(*req) - sizeof(req->req_trailer);
req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
req->req_msgh.msgh_local_port = exc_mr->mr_name;
req->req_msgh.msgh_id = MACH_EXCEPTION_STATE;
req->req_exc = exc;
req->req_codecount = 2;
mach_siginfo_to_exception(ksi, &req->req_code[0]);
req->req_flavor = flavor;
mach_thread_get_state_machdep(l, flavor, req->req_state, &dc);
/* Trailer */
req->req_state[(dc / sizeof(req->req_state[0])) + 1] = 8;
msglen = msglen -
sizeof(req->req_state) +
(dc * sizeof(req->req_state[0]));
break;
}
case MACH_EXCEPTION_STATE_IDENTITY: {
mach_exception_raise_state_identity_request_t *req;
int dc;
req = malloc(sizeof(*req), M_EMULDATA, M_WAITOK | M_ZERO);
msglen = sizeof(*req);
msgh = (mach_msg_header_t *)req;
req->req_msgh.msgh_bits = MACH_MSGH_BITS_COMPLEX |
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND) |
MACH_MSGH_REMOTE_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
req->req_msgh.msgh_size =
sizeof(*req) - sizeof(req->req_trailer);
req->req_msgh.msgh_remote_port = kernel_mr->mr_name;
req->req_msgh.msgh_local_port = exc_mr->mr_name;
req->req_msgh.msgh_id = MACH_EXC_RAISE_STATE_IDENTITY_MSGID;
req->req_body.msgh_descriptor_count = 2;
req->req_thread.name = thread->mr_name;
req->req_thread.disposition = 0x11; /* XXX */
req->req_thread.type = 0; /* XXX */
req->req_task.name = task->mr_name;
req->req_task.disposition = 0x11; /* XXX */
req->req_task.type = 0; /* XXX */
req->req_exc = exc;
req->req_codecount = 2;
mach_siginfo_to_exception(ksi, &req->req_code[0]);
req->req_flavor = flavor;
mach_thread_get_state_machdep(l, flavor, req->req_state, &dc);
/* Trailer */
req->req_state[(dc / sizeof(req->req_state[0])) + 1] = 8;
msglen = msglen -
sizeof(req->req_state) +
(dc * sizeof(req->req_state[0]));
break;
}
default:
printf("unknown exception bevahior %d\n", behavior);
return EINVAL;
break;
}
(void)mach_message_get(msgh, msglen, exc_port, NULL);
wakeup(exc_port->mp_recv->mr_sethead);
/*
* The thread that caused the exception is now
* supposed to wait for a reply to its message.
* This is to avoid an endless loop on the same
* exception.
*/
error = tsleep(kernel_mr->mr_port, PZERO|PCATCH, "mach_exc", 0);
if ((error == ERESTART) || (error == EINTR))
return error;
return 0;
}
static void
mach_siginfo_to_exception(ksi, code)
const struct ksiginfo *ksi;
int *code;
{
switch (ksi->ksi_signo) {
case SIGBUS:
switch (ksi->ksi_code) {
case BUS_ADRALN:
code[0] = MACH_BUS_ADRALN;
code[1] = (long)ksi->ksi_addr;
break;
default:
printf("untranslated siginfo signo %d, code %d\n",
ksi->ksi_signo, ksi->ksi_code);
break;
}
break;
case SIGSEGV:
switch (ksi->ksi_code) {
case SEGV_MAPERR:
code[0] = MACH_SEGV_MAPERR;
code[1] = (long)ksi->ksi_addr;
break;
default:
printf("untranslated siginfo signo %d, code %d\n",
ksi->ksi_signo, ksi->ksi_code);
break;
}
break;
case SIGTRAP:
switch (ksi->ksi_code) {
case TRAP_BRKPT:
code[0] = MACH_TRAP_BRKPT;
code[1] = (long)ksi->ksi_addr;
break;
default:
printf("untranslated siginfo signo %d, code %d\n",
ksi->ksi_signo, ksi->ksi_code);
break;
}
break;
case SIGILL:
switch (ksi->ksi_code) {
case ILL_ILLOPC:
case ILL_ILLOPN:
case ILL_ILLADR:
case ILL_ILLTRP:
code[0] = MACH_ILL_ILLOPC;
code[1] = (long)ksi->ksi_addr;
break;
case ILL_PRVOPC:
case ILL_PRVREG:
code[0] = MACH_ILL_PRVOPC;
code[1] = (long)ksi->ksi_addr;
break;
default:
printf("untranslated siginfo signo %d, code %d\n",
ksi->ksi_signo, ksi->ksi_code);
break;
}
break;
default:
printf("untranslated siginfo signo %d, code %d\n",
ksi->ksi_signo, ksi->ksi_code);
break;
}
return;
}
int
mach_exception_raise(args)
struct mach_trap_args *args;
{
mach_exception_raise_reply_t *rep;
struct lwp *l = args->l;
mach_port_t mn;
struct mach_right *mr;
struct mach_emuldata *med;
/*
* No typo here: the reply is in the sent message.
* The kernel is acting as a client that gets the
* reply message to its exception message.
*/
rep = args->smsg;
/*
* Sanity check the remote port: it should be a
* right to the process' kernel port
*/
mn = rep->rep_msgh.msgh_remote_port;
if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == 0)
return MACH_SEND_INVALID_RIGHT;
med = (struct mach_emuldata *)l->l_proc->p_emuldata;
if (med->med_kernel != mr->mr_port) {
#ifdef DEBUG_MACH
printf("mach_exception_raise: remote port not kernel port\n");
#endif
return 0;
}
/*
* This message is sent by the process catching the
* exception to release the process that raised the exception.
* We wake it up if the return value is 0 (no error), else
* we should ignore this message.
*/
if (rep->rep_retval == 0)
wakeup(mr->mr_port);
return 0;
}
int
mach_exception_raise_state(args)
struct mach_trap_args *args;
{
return mach_exception_raise(args);
}
int
mach_exception_raise_state_identity(args)
struct mach_trap_args *args;
{
return mach_exception_raise(args);
}