Source to ipc/mach_msg.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * Copyright (c) 1995, 1994, 1993, 1992, 1991, 1990  
 * Open Software Foundation, Inc. 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation, and that the name of ("OSF") or Open Software 
 * Foundation not be used in advertising or publicity pertaining to 
 * distribution of the software without specific, written prior permission. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OSF BE LIABLE FOR ANY 
 * SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
 * ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING 
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE 
 */
/*
 * OSF Research Institute MK6.1 (unencumbered) 1/31/1995
 */
/* 
 * Mach Operating System
 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  [email protected]
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/*
 *	File:	ipc/mach_msg.c
 *	Author:	Rich Draves
 *	Date:	1989
 *
 *	Exported message traps.  See mach/message.h.
 */

#import <mach/features.h>

#include <mach/kern_return.h>
#include <mach/port.h>
#include <mach/message.h>
#include <kern/assert.h>
#include <kern/counters.h>
#include <kern/lock.h>
#include <kern/sched_prim.h>
#include <kern/ipc_sched.h>
#include <vm/vm_map.h>
#include <ipc/ipc_kmsg.h>
#include <ipc/ipc_marequest.h>
#include <ipc/ipc_mqueue.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_notify.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_thread.h>
#include <ipc/ipc_entry.h>
#include <ipc/mach_msg.h>

/*
 * Forward declarations
 */

mach_msg_return_t mach_msg_send(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		send_size,
	mach_msg_timeout_t	timeout,
	mach_port_t		notify);

mach_msg_return_t mach_msg_receive(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		rcv_size,
	mach_port_t		rcv_name,
	mach_msg_timeout_t	timeout,
	mach_port_t		notify,
	mach_msg_size_t		list_size);

mach_msg_return_t mach_msg_receive_error(
	ipc_kmsg_t		kmsg,
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_port_seqno_t	seqno,
	ipc_space_t		space);

/* the size of each trailer has to be listed here for copyout purposes */
vm_size_t trailer_size[] = {
          sizeof (mach_msg_trailer_t), 
	  sizeof (mach_msg_seqno_trailer_t),
	  sizeof (mach_msg_security_trailer_t)
};

/*
 *	Routine:	mach_msg_send
 *	Purpose:
 *		Send a message.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_MSG_SUCCESS	Sent the message.
 *		MACH_SEND_MSG_TOO_SMALL	Message smaller than a header.
 *		MACH_SEND_NO_BUFFER	Couldn't allocate buffer.
 *		MACH_SEND_INVALID_DATA	Couldn't copy message data.
 *		MACH_SEND_INVALID_HEADER
 *			Illegal value in the message header bits.
 *		MACH_SEND_INVALID_DEST	The space is dead.
 *		MACH_SEND_INVALID_NOTIFY	Bad notify port.
 *		MACH_SEND_INVALID_DEST	Can't copyin destination port.
 *		MACH_SEND_INVALID_REPLY	Can't copyin reply port.
 *		MACH_SEND_TIMED_OUT	Timeout expired without delivery.
 *		MACH_SEND_INTERRUPTED	Delivery interrupted.
 *		MACH_SEND_NO_NOTIFY	Can't allocate a msg-accepted request.
 *		MACH_SEND_WILL_NOTIFY	Msg-accepted notif. requested.
 *		MACH_SEND_NOTIFY_IN_PROGRESS
 *			This space has already forced a message to this port.
 */

mach_msg_return_t
mach_msg_send(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		send_size,
	mach_msg_timeout_t	timeout,
	mach_port_t		notify)
{
	ipc_space_t space = current_space();
	vm_map_t map = current_map();
	ipc_kmsg_t kmsg;
	mach_msg_return_t mr;

	mr = ipc_kmsg_get(msg, option, send_size, MAX_TRAILER_SIZE, &kmsg);
	if (mr != MACH_MSG_SUCCESS)
		return mr;

	if (option & MACH_SEND_CANCEL) {
		if (notify == MACH_PORT_NULL)
			mr = MACH_SEND_INVALID_NOTIFY;
		else
			mr = ipc_kmsg_copyin(kmsg, space, map, notify);
	} else
		mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL);
	if (mr != MACH_MSG_SUCCESS) {
		ikm_free(kmsg);
		return mr;
	}

	mr = ipc_mqueue_send(kmsg, option & MACH_SEND_TIMEOUT,
						timeout, IMQ_NULL_CONTINUE);
	if (mr != MACH_MSG_SUCCESS) {
		mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map, IKM_NULL);

		assert(kmsg->ikm_marequest == IMAR_NULL);
		(void) ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
	}

	return mr;
}

#define FREE_SCATTER_LIST(l)	 			\
MACRO_BEGIN						\
	if((l) != IKM_NULL) {		 		\
		ikm_free((l));				\
	}						\
MACRO_END

/*
 *	Routine:	mach_msg_receive
 *	Purpose:
 *		Receive a message.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_MSG_SUCCESS	Received a message.
 *		MACH_RCV_INVALID_NAME	The name doesn't denote a right,
 *			or the denoted right is not receive or port set.
 *		MACH_RCV_IN_SET		Receive right is a member of a set.
 *		MACH_RCV_TOO_LARGE	Message wouldn't fit into buffer.
 *		MACH_RCV_TIMED_OUT	Timeout expired without a message.
 *		MACH_RCV_INTERRUPTED	Reception interrupted.
 *		MACH_RCV_PORT_DIED	Port/set died while receiving.
 *		MACH_RCV_PORT_CHANGED	Port moved into set while receiving.
 *		MACH_RCV_INVALID_DATA	Couldn't copy to user buffer.
 *		MACH_RCV_INVALID_NOTIFY	Bad notify port.
 *		MACH_RCV_HEADER_ERROR
 */

mach_msg_return_t
mach_msg_receive(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		rcv_size,
	mach_port_t		rcv_name,
	mach_msg_timeout_t	timeout,
	mach_port_t		notify,
	mach_msg_size_t		list_size)
{
	ipc_thread_t self = current_thread();
	ipc_space_t space = current_space();
	vm_map_t map = current_map();
	ipc_object_t object;
	ipc_mqueue_t mqueue;
	ipc_kmsg_t kmsg;
	mach_port_seqno_t seqno;
	ipc_kmsg_t list;
	mach_msg_return_t mr;
	mach_msg_trailer_t *trailer;

	/*
 	 * If MACH_RCV_OVERWRITE was specified, both receive_msg (msg)
	 * and receive_msg_size (list_size) need to be non NULL.
	 */
	if (option & MACH_RCV_OVERWRITE) {
		if (list_size < sizeof(mach_msg_base_t)) 
			return MACH_RCV_SCATTER_SMALL;
		else 
		if (ipc_kmsg_get(msg, MACH_MSG_OPTION_NONE,
				list_size, 0, &list) != MACH_MSG_SUCCESS)
			return MACH_RCV_INVALID_DATA;

		else
		if ((((mach_msg_base_t *)
			&kmsg->ikm_header)->body.msgh_descriptor_count *
					sizeof (mach_msg_descriptor_t)) + 
					sizeof (mach_msg_base_t) > list_size) {
			FREE_SCATTER_LIST(list);
			return MACH_RCV_INVALID_TYPE;
		}
	}
	else
		list = IKM_NULL;

	mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
	if (mr != MACH_MSG_SUCCESS) {
		FREE_SCATTER_LIST(list);
		return mr;
	}
	/* hold ref for object; mqueue is locked */

	/*
	 *	ipc_mqueue_receive may not return, because if we block
	 *	then our kernel stack may be discarded.  So we save
	 *	state here for mach_msg_receive_continue to pick up.
	 */

	self->ith_msg = msg;
	self->ith_option = option;
	self->ith_rcv_size = rcv_size;
	self->ith_timeout = timeout;
	self->ith_notify = notify;
	self->ith_object = object;
	self->ith_mqueue = mqueue;

	mr = ipc_mqueue_receive(mqueue,
				option & (MACH_RCV_TRAILER_MASK|
						MACH_RCV_TIMEOUT|
						MACH_RCV_LARGE),
				rcv_size, timeout,
				FALSE, mach_msg_receive_continue,
				&kmsg, &seqno, &list);
	/* mqueue is unlocked */
	ipc_object_release(object);

	if (mr != MACH_MSG_SUCCESS) {
		if (mr == MACH_RCV_HEADER_ERROR) {
			mach_msg_return_t	pr;

			pr = mach_msg_receive_error(kmsg, msg,
						    option, seqno, space);
			if (pr != MACH_MSG_SUCCESS)
				mr = pr;
		}
		else
		if (mr == MACH_RCV_TOO_LARGE || mr == MACH_RCV_SCATTER_SMALL) {
			if (option & MACH_RCV_LARGE) {
				if (list != IKM_NULL) {
					mach_msg_return_t	pr;

					list_size = list->ikm_header.msgh_size;
					list->ikm_header.msgh_size =
						(mach_msg_size_t)kmsg;
					pr = ipc_kmsg_put(
						msg, list, list_size);
					if (pr != MACH_MSG_SUCCESS)
						mr = pr;
				}
				else					
				if (copyout((char *) &kmsg,
					       (char *) &msg->msgh_size,
					       sizeof(mach_msg_size_t)))
					mr = MACH_RCV_INVALID_DATA;

				return mr;
			}
			else {
				mach_msg_return_t	pr;

				pr = mach_msg_receive_error(kmsg, msg,
							option, seqno, space);
				if (pr != MACH_MSG_SUCCESS)
					mr = pr;
			}
		}

		FREE_SCATTER_LIST(list);
		return mr;
	}

	/*
	 * Generate requested trailer.
	 */
	trailer = (mach_msg_trailer_t *)
			((vm_offset_t)&kmsg->ikm_header +
					kmsg->ikm_header.msgh_size);

	if (option & MACH_RCV_NO_TRAILER) {
		trailer->msgh_trailer_size = 
				trailer->msgh_trailer_type = 0;
		kmsg->ikm_header.msgh_bits |= MACH_MSGH_BITS_NO_TRAILER;
	}
	else
	if (kmsg->ikm_delta > 0) {
		/* MACH_SEND_TRAILER */;
	}
	else {
		int	elements = GET_RCV_ELEMENTS(option);
#define trailer0	((mach_msg_format_0_trailer_t *)trailer)
		
		if (elements == MACH_RCV_TRAILER_SENDER) {
			trailer0->msgh_sender = kmsg->ikm_sender;
			trailer0->msgh_seqno = seqno;
		}
		else
		if (elements == MACH_RCV_TRAILER_SEQNO)
			trailer0->msgh_seqno = seqno;

		trailer0->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
		trailer0->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
#undef trailer0
	}

	if (option & MACH_RCV_NOTIFY) {
		if (notify == MACH_PORT_NULL)
			mr = MACH_RCV_INVALID_NOTIFY;
		else
			mr = ipc_kmsg_copyout(kmsg, space, map, notify, list);
	} else
		mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL, list);
	if (mr != MACH_MSG_SUCCESS) {
		mach_msg_return_t	pr;

		if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
			pr = ipc_kmsg_put(msg, kmsg,
					kmsg->ikm_header.msgh_size +
						trailer->msgh_trailer_size);
			if (pr != MACH_MSG_SUCCESS)
				mr = pr;
					
		} else {
			pr = mach_msg_receive_error(kmsg, msg,
						option, seqno, space);
			if (pr != MACH_MSG_SUCCESS)
				mr = pr;
		}

		FREE_SCATTER_LIST(list);
		return mr;
	}

	FREE_SCATTER_LIST(list);
	return ipc_kmsg_put(msg, kmsg,
			kmsg->ikm_header.msgh_size +
				trailer->msgh_trailer_size);
}

/*
 *	Routine:	mach_msg_receive_continue
 *	Purpose:
 *		Continue after blocking for a message.
 *	Conditions:
 *		Nothing locked.  We are running on a new kernel stack,
 *		with the receive state saved in the thread.  From here
 *		control goes back to user space.
 */

void
mach_msg_receive_continue(void)
{
	ipc_thread_t self = current_thread();
	ipc_space_t space = current_space();
	vm_map_t map = current_map();
	mach_msg_header_t *msg = self->ith_msg;
	mach_msg_option_t option = self->ith_option;
	mach_msg_trailer_t *trailer;
	ipc_kmsg_t kmsg;
	mach_port_seqno_t seqno;
	ipc_kmsg_t list;
	mach_msg_return_t mr;

	mr = ipc_mqueue_receive(self->ith_mqueue,
				option & (MACH_RCV_TRAILER_MASK|
						MACH_RCV_TIMEOUT|
						MACH_RCV_LARGE),
				self->ith_rcv_size, self->ith_timeout,
				TRUE, mach_msg_receive_continue,
				&kmsg, &seqno, &list);
	/* mqueue is unlocked */
	ipc_object_release(self->ith_object);

	if (mr != MACH_MSG_SUCCESS) {
		if (mr == MACH_RCV_HEADER_ERROR) {
			mach_msg_return_t	pr;

			pr = mach_msg_receive_error(kmsg, msg,
						    option, seqno, space);
			if (pr != MACH_MSG_SUCCESS)
				mr = pr;
		}
		else
		if (mr == MACH_RCV_TOO_LARGE || mr == MACH_RCV_SCATTER_SMALL) {
			if (option & MACH_RCV_LARGE) {
				if (list != IKM_NULL) {
					mach_msg_return_t	pr;
					mach_msg_size_t		list_size;

					list_size = list->ikm_header.msgh_size;
					list->ikm_header.msgh_size =
						(mach_msg_size_t)kmsg;
					pr = ipc_kmsg_put(
						msg, list, list_size);
					if (pr != MACH_MSG_SUCCESS)
						mr = pr;
				}
				else					
				if (copyout((char *) &kmsg,
					       (char *) &msg->msgh_size,
					       sizeof(mach_msg_size_t)))
					mr = MACH_RCV_INVALID_DATA;

				thread_syscall_return(mr);
				/*NOTREACHED*/
			}
			else {
				mach_msg_return_t	pr;

				pr = mach_msg_receive_error(kmsg, msg,
							option, seqno, space);
				if (pr != MACH_MSG_SUCCESS)
					mr = pr;
			}
		}

		FREE_SCATTER_LIST(list);
		thread_syscall_return(mr);
		/*NOTREACHED*/
	}

	/*
	 * Generate requested trailer.
	 */
	trailer = (mach_msg_trailer_t *)
			((vm_offset_t)&kmsg->ikm_header +
					kmsg->ikm_header.msgh_size);

	if (option & MACH_RCV_NO_TRAILER) {
		trailer->msgh_trailer_size = 
				trailer->msgh_trailer_type = 0;
		kmsg->ikm_header.msgh_bits |= MACH_MSGH_BITS_NO_TRAILER;
	}
	else
	if (kmsg->ikm_delta > 0) {
		/* MACH_SEND_TRAILER */;
	}
	else {
		int	elements = GET_RCV_ELEMENTS(option);
#define trailer0	((mach_msg_format_0_trailer_t *)trailer)
		
		if (elements == MACH_RCV_TRAILER_SENDER) {
			trailer0->msgh_sender = kmsg->ikm_sender;
			trailer0->msgh_seqno = seqno;
		}
		else
		if (elements == MACH_RCV_TRAILER_SEQNO)
			trailer0->msgh_seqno = seqno;

		trailer0->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
		trailer0->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
#undef trailer0
	}

	if (option & MACH_RCV_NOTIFY) {
		if (self->ith_notify == MACH_PORT_NULL)
			mr = MACH_RCV_INVALID_NOTIFY;
		else
			mr = ipc_kmsg_copyout(
					kmsg, space, map,
					self->ith_notify, 
					self->ith_list);
	} else
		mr = ipc_kmsg_copyout(
				kmsg, space, map,
				MACH_PORT_NULL,
				self->ith_list);
	if (mr != MACH_MSG_SUCCESS) {
		mach_msg_return_t	pr;

		if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
			pr = ipc_kmsg_put(msg, kmsg,
					kmsg->ikm_header.msgh_size +
						trailer->msgh_trailer_size);
			if (pr != MACH_MSG_SUCCESS)
				mr = pr;
					
		} else {
			pr = mach_msg_receive_error(kmsg, msg,
						option, seqno, space);
			if (pr != MACH_MSG_SUCCESS)
				mr = pr;
		}

		FREE_SCATTER_LIST(list);
		thread_syscall_return(mr);
		/*NOTREACHED*/
	}

	FREE_SCATTER_LIST(list);
	mr = ipc_kmsg_put(msg, kmsg,
			kmsg->ikm_header.msgh_size +
				trailer->msgh_trailer_size);
	thread_syscall_return(mr);
	/*NOTREACHED*/
}

/*
 *	Routine:	mach_msg_receive_error	[internal]
 *	Purpose:
 *		Builds a minimal header/trailer and copies it to
 *		the user message buffer.  Invoked when in the case of a
 *		MACH_RCV_TOO_LARGE or MACH_RCV_HEADER_ERROR error.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_MSG_SUCCESS	minimal headere/trailer copied
 *		MACH_RCV_INVALID_DATA	copyout to user buffer failed
 */
	
mach_msg_return_t
mach_msg_receive_error(
	ipc_kmsg_t		kmsg,
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_port_seqno_t	seqno,
	ipc_space_t		space)
{
	mach_msg_trailer_t *trailer;

	/*
	 * Copy out the destination port in the message.
 	 * Destroy all other rights and memory in the message.
	 */
	ipc_kmsg_copyout_dest(kmsg, space);

	/*
	 * Build a minimal message with a null trailer.
	 */
	kmsg->ikm_header.msgh_size = sizeof(mach_msg_header_t);
	trailer = (mach_msg_trailer_t *)
				((vm_offset_t)&kmsg->ikm_header +
						kmsg->ikm_header.msgh_size);

	if (option & MACH_RCV_NO_TRAILER) {
	    trailer->msgh_trailer_size = trailer->msgh_trailer_type = 0;
	    kmsg->ikm_header.msgh_bits |= MACH_MSGH_BITS_NO_TRAILER;
	}
	else {
	    trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
	    trailer->msgh_trailer_size = sizeof (mach_msg_trailer_t);
	}

	/*
	 * Copy the message to user space
	 */
	return ipc_kmsg_put(msg, kmsg,
			kmsg->ikm_header.msgh_size +
				trailer->msgh_trailer_size);
}

/*
 *	Routine:	mach_msg_overwrite_trap [mach trap]
 *	Purpose:
 *		Possibly send a message; possibly receive a message.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		All of mach_msg_send and mach_msg_receive error codes.
 */
static __inline__
mach_msg_return_t
_mach_msg_overwrite(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		send_size,
	mach_msg_size_t		rcv_size,
	mach_port_t		rcv_name,
	mach_msg_timeout_t	timeout,
	mach_port_t		notify,
	mach_msg_header_t	*rcv_msg,
        mach_msg_size_t		list_size)
{
	mach_msg_return_t mr;

	if (option & MACH_SEND_MSG) {
		mr = mach_msg_send(msg, option, send_size,
				   timeout, notify);
		if (mr != MACH_MSG_SUCCESS)
			return mr;
	}

	if (option & MACH_RCV_MSG) {
		mach_msg_header_t *rcv;

		/*
		 * 1. MACH_RCV_OVERWRITE is on, and rcv_msg is our scatter list
		 *    and receive buffer
		 * 2. MACH_RCV_OVERWRITE is off, and rcv_msg might be the
		 *    alternate receive buffer (separate send and receive buffers).
		 */
		if (option & MACH_RCV_OVERWRITE) 
		    rcv = rcv_msg;
		else if (rcv_msg != MACH_MSG_NULL)
		    rcv = rcv_msg;
		else
		    rcv = msg;
		mr = mach_msg_receive(rcv, option, rcv_size, rcv_name, 
				      timeout, notify, list_size);
		if (mr != MACH_MSG_SUCCESS)
			return mr;
	}

	return MACH_MSG_SUCCESS;
}

mach_msg_return_t
mach_msg_overwrite_trap(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		sndsiz,
	mach_msg_size_t		rcvsiz,
	mach_port_t		rcvnam,
	mach_msg_timeout_t	tout,
	mach_port_t		notify,
	mach_msg_header_t	*rmsg,
	mach_msg_size_t		rmsgsiz)
{
	return
	(
		_mach_msg_overwrite(
				msg, option,
				sndsiz,
				rcvsiz, rcvnam,
				tout, notify,
				rmsg, rmsgsiz));
}

mach_msg_return_t
mach_msg_trap(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		sndsiz,
	mach_msg_size_t		rcvsiz,
	mach_port_t		rcvnam,
	mach_msg_timeout_t	tout,
	mach_port_t		notify)
{
	return
	(
		_mach_msg_overwrite(
				msg, option,
				sndsiz,
				rcvsiz, rcvnam,
				tout, notify,
				MACH_MSG_NULL,  (mach_msg_size_t) 0));
}

mach_msg_return_t
mach_msg_simple_trap(
	mach_msg_header_t	*msg,
	mach_msg_option_t	option,
	mach_msg_size_t		sndsiz,
	mach_msg_size_t		rcvsiz,
	mach_port_t		rcvnam)
{
	return
	(
		_mach_msg_overwrite(
				msg, option,
				sndsiz,
				rcvsiz, rcvnam,
				MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
				MACH_MSG_NULL,  (mach_msg_size_t) 0));
}

/*
 *	Routine:	mach_msg_interrupt
 *	Purpose:
 *		Attempts to force a thread waiting at mach_msg_continue or
 *		mach_msg_receive_continue into a clean point.  Returns TRUE
 *		if this was possible.
 *	Conditions:
 *		Nothing locked.  The thread must NOT be runnable.
 */

boolean_t
mach_msg_interrupt(
	thread_t thread)
{
	ipc_mqueue_t mqueue;

	assert((thread->swap_func == (void (*)()) mach_msg_continue) ||
	       (thread->swap_func == (void (*)()) mach_msg_receive_continue));

	mqueue = thread->ith_mqueue;
	imq_lock(mqueue);
	if (thread->ith_state != MACH_RCV_IN_PROGRESS) {
		/*
		 *	The thread is no longer waiting for a message.
		 *	It may have a message sitting in ith_kmsg.
		 *	We can't clean this up.
		 */

		imq_unlock(mqueue);
		return FALSE;
	}
	ipc_thread_rmqueue(&mqueue->imq_threads, thread);
	imq_unlock(mqueue);

	ipc_object_release(thread->ith_object);

	thread_set_syscall_return(thread, MACH_RCV_INTERRUPTED);
	thread->swap_func = thread_exception_return;
	return TRUE;
}

#if	MACH_IPC_COMPAT

/*
 *	Routine:	msg_return_translate
 *	Purpose:
 *		Translate from new error code to old error code.
 */

msg_return_t
msg_return_translate(
	mach_msg_return_t mr)
{
	switch (mr &~ MACH_MSG_MASK) {
	    case MACH_MSG_SUCCESS:
		return 0;	/* SEND_SUCCESS/RCV_SUCCESS/RPC_SUCCESS */

	    case MACH_SEND_NO_BUFFER:
	    case MACH_SEND_NO_NOTIFY:
		printf("msg_return_translate: %x -> interrupted\n", mr);
		return SEND_INTERRUPTED;

	    case MACH_SEND_MSG_TOO_SMALL:
		return SEND_MSG_TOO_SMALL;
	    case MACH_SEND_INVALID_DATA:
	    case MACH_SEND_INVALID_MEMORY:
		return SEND_INVALID_MEMORY;
	    case MACH_SEND_TIMED_OUT:
		return SEND_TIMED_OUT;
	    case MACH_SEND_INTERRUPTED:
		return SEND_INTERRUPTED;
	    case MACH_SEND_INVALID_DEST:
	    case MACH_SEND_INVALID_REPLY:
	    case MACH_SEND_INVALID_RIGHT:
	    case MACH_SEND_INVALID_TYPE:
		return SEND_INVALID_PORT;
	    case MACH_SEND_WILL_NOTIFY:
		return SEND_WILL_NOTIFY;
	    case MACH_SEND_NOTIFY_IN_PROGRESS:
		return SEND_NOTIFY_IN_PROGRESS;

	    case MACH_RCV_INVALID_NAME:
	    case MACH_RCV_IN_SET:
	    case MACH_RCV_PORT_DIED:
		return RCV_INVALID_PORT;
	    case MACH_RCV_TOO_LARGE:
		return RCV_TOO_LARGE;
	    case MACH_RCV_TIMED_OUT:
		return RCV_TIMED_OUT;
	    case MACH_RCV_INTERRUPTED:
		return RCV_INTERRUPTED;
	    case MACH_RCV_PORT_CHANGED:
		return RCV_PORT_CHANGE;
	    case MACH_RCV_INVALID_DATA:
		return RCV_INVALID_MEMORY;

	    case MACH_SEND_IN_PROGRESS:
	    case MACH_SEND_INVALID_NOTIFY:
	    case MACH_SEND_INVALID_HEADER:
	    case MACH_RCV_IN_PROGRESS:
	    case MACH_RCV_INVALID_NOTIFY:
	    case MACH_RCV_HEADER_ERROR:
	    case MACH_RCV_BODY_ERROR:
	    default:
		panic("msg_return_translate");
	}
}

/*
 *	Routine:	msg_send_trap [mach trap]
 *	Purpose:
 *		Send a message.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 */

msg_return_t
msg_send_trap(
	msg_header_t *msg,
	msg_option_t option,
	msg_size_t send_size,
	msg_timeout_t timeout)
{
	ipc_space_t space = current_space();
	vm_map_t map = current_map();
	ipc_kmsg_t kmsg;
	integer_t send_delta = send_size;
	mach_msg_return_t mr;

	send_size = (send_size + 3) & ~3; /* round up */
	send_delta -= send_size;

	if (send_size > MSG_SIZE_MAX)
		return SEND_MSG_TOO_LARGE;

	mr = ipc_kmsg_get((mach_msg_header_t *) msg, MACH_MSG_OPTION_NONE,
			  (mach_msg_size_t) send_size, send_delta,
			  &kmsg);
	if (mr != MACH_MSG_SUCCESS)
		return msg_return_translate(mr);

	mr = ipc_kmsg_copyin_compat(kmsg, space, map);
	if (mr != MACH_MSG_SUCCESS) {
		ikm_free(kmsg);
		return msg_return_translate(mr);
	}

	if (option & SEND_NOTIFY) {
		mr = ipc_mqueue_send(kmsg, 
				     ((option & SEND_SWITCH) ?
				      MACH_SEND_SWITCH :
				      MACH_MSG_OPTION_NONE) |
				      MACH_SEND_TIMEOUT,
				     ((option & SEND_TIMEOUT) ?
				      (mach_msg_timeout_t) timeout :
				      MACH_MSG_TIMEOUT_NONE),
				     IMQ_NULL_CONTINUE);
		if (mr == MACH_SEND_TIMED_OUT) {
			ipc_port_t dest = (ipc_port_t)
				kmsg->ikm_header.msgh_remote_port;

			mr = ipc_marequest_create(space, dest, MACH_PORT_NULL,
						  &kmsg->ikm_marequest);
			if (mr == MACH_MSG_SUCCESS) {
				ipc_mqueue_send_always(kmsg);
				return SEND_WILL_NOTIFY;
			}
		}
	}
	else if (option & SEND_SWITCH)
		mr = ipc_mqueue_send(kmsg,
				     ((option & SEND_TIMEOUT) ?
				      MACH_SEND_TIMEOUT :
				      MACH_MSG_OPTION_NONE) |
				      MACH_SEND_SWITCH,
				     (mach_msg_timeout_t) timeout,
				     msg_send_switch_continue);
	else
		mr = ipc_mqueue_send(kmsg,
				     ((option & SEND_TIMEOUT) ?
				      MACH_SEND_TIMEOUT :
				      MACH_MSG_OPTION_NONE),
				     (mach_msg_timeout_t) timeout,
				     IMQ_NULL_CONTINUE);

	if (mr != MACH_MSG_SUCCESS)
		ipc_kmsg_destroy(kmsg);

	return msg_return_translate(mr);
}

void
msg_send_switch_continue(void)
{
	thread_syscall_return(SEND_SUCCESS);
	/*NOTREACHED*/
}

mach_msg_return_t
msg_receive_error(
	ipc_kmsg_t	kmsg,
	msg_header_t	*msg,
	ipc_space_t	space);

/*
 *	Routine:	msg_receive_trap [mach trap]
 *	Purpose:
 *		Receive a message.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 */

msg_return_t
msg_receive_trap(
	msg_header_t *msg,
	msg_option_t option,
	msg_size_t rcv_size,
	port_name_t rcv_name,
	msg_timeout_t timeout)
{
	ipc_thread_t self;
	ipc_space_t space = current_space();
	vm_map_t map = current_map();
	ipc_object_t object;
	ipc_mqueue_t mqueue;
	ipc_kmsg_t kmsg;
	mach_port_seqno_t seqno;
	mach_msg_return_t mr;

	mr = ipc_mqueue_copyin(space, (mach_port_t) rcv_name,
			       &mqueue, &object);
	if (mr != MACH_MSG_SUCCESS)
		return msg_return_translate(mr);
	/* hold ref for object; mqueue is locked */

	/*
	 *	ipc_mqueue_receive may not return, because if we block
	 *	then our kernel stack may be discarded.  So we save
	 *	state here for msg_receive_continue to pick up.
	 */

	self = current_thread();
	self->ith_msg = (mach_msg_header_t *) msg;
	self->ith_option = (mach_msg_option_t) option;
	self->ith_rcv_size = (mach_msg_size_t) rcv_size;
	self->ith_timeout = (mach_msg_timeout_t) timeout;
	self->ith_object = object;
	self->ith_mqueue = mqueue;

	mr = ipc_mqueue_receive(mqueue,
				((option & RCV_TIMEOUT) ?
				MACH_RCV_TIMEOUT :
				MACH_MSG_OPTION_NONE) |
				MACH_RCV_OLD_FORMAT,
				(option & RCV_LARGE) ?
				(mach_msg_size_t) rcv_size :
				MACH_MSG_SIZE_MAX,
				(mach_msg_timeout_t) timeout,
				FALSE, msg_receive_continue,
				&kmsg, &seqno, 0);
	/* mqueue is unlocked */
	ipc_object_release(object);

	if (mr != MACH_MSG_SUCCESS) {
		if (mr == MACH_RCV_HEADER_ERROR)
			mr = msg_receive_error(kmsg, msg, space);
		else
		if (mr == MACH_RCV_TOO_LARGE) {
			msg_size_t real_size =
				(msg_size_t) (mach_msg_size_t) kmsg;

			assert(real_size > rcv_size);

			(void) copyout((vm_offset_t) &real_size,
				       (vm_offset_t) &msg->msg_size,
				       sizeof(msg_size_t));
		}

		return msg_return_translate(mr);
	}
	
	if (kmsg->ikm_header.msgh_size > (mach_msg_size_t) rcv_size) {
		assert(!(option & RCV_LARGE));
		
		ipc_kmsg_destroy(kmsg);
		return (RCV_TOO_LARGE);
	}

	assert(kmsg->ikm_header.msgh_size <= (mach_msg_size_t) rcv_size);

	mr = ipc_kmsg_copyout_compat(kmsg, space, map);
	assert(mr == MACH_MSG_SUCCESS);

	kmsg->ikm_header.msgh_size += kmsg->ikm_delta;
	mr = ipc_kmsg_put((mach_msg_header_t *) msg, kmsg,
			  kmsg->ikm_header.msgh_size);
	return msg_return_translate(mr);
}

/*
 *	Routine:	msg_rpc_trap [mach trap]
 *	Purpose:
 *		Send and receive a message.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 */

msg_return_t
msg_rpc_trap(
	msg_header_t *msg,
	msg_option_t option,
	msg_size_t send_size,
	msg_size_t rcv_size,
	msg_timeout_t send_timeout,
	msg_timeout_t rcv_timeout)
{
	ipc_thread_t self;
	ipc_space_t space = current_space();
	vm_map_t map = current_map();
	ipc_port_t reply;
	ipc_pset_t pset;
	ipc_mqueue_t mqueue;
	ipc_kmsg_t kmsg;
	integer_t send_delta = send_size;
	mach_port_seqno_t seqno;
	mach_msg_return_t mr;

	/*
	 *	Instead of using msg_send_trap and msg_receive_trap,
	 *	we implement msg_rpc_trap directly.  The difference
	 *	is how the reply port is handled.  Instead of using
	 *	ipc_mqueue_copyin, we save a reference for the reply
	 *	port carried in the sent message.  For example,
	 *	consider a rename kernel call which changes the name
	 *	of the call's own reply port.  This is the behaviour
	 *	of the Mach 2.5 msg_rpc_trap.
	 */

	send_size = (send_size + 3) & ~3; /* round up */
	send_delta -= send_size;

	if (send_size > MSG_SIZE_MAX)
		return SEND_MSG_TOO_LARGE;

	mr = ipc_kmsg_get((mach_msg_header_t *) msg, MACH_MSG_OPTION_NONE,
			  (mach_msg_size_t) send_size, send_delta,
			  &kmsg);
	if (mr != MACH_MSG_SUCCESS)
		return msg_return_translate(mr);

	mr = ipc_kmsg_copyin_compat(kmsg, space, map);
	if (mr != MACH_MSG_SUCCESS) {
		ikm_free(kmsg);
		return msg_return_translate(mr);
	}

	reply = (ipc_port_t) kmsg->ikm_header.msgh_local_port;
	if (IP_VALID(reply)) {
		ipc_port_t	dest = (ipc_port_t)
					kmsg->ikm_header.msgh_remote_port;
		
		ipc_port_reference(reply);
		assert(IP_VALID(dest));
		
		ip_lock(dest);
		
		if (dest->ip_receiver == ipc_space_kernel) {
			ipc_mqueue_t	mqueue;

			assert(ip_active(dest));
			ip_unlock(dest);
			
			kmsg = ipc_kobject_server(kmsg);
			if (kmsg == IKM_NULL)
				goto receive_reply;
			
			ip_lock(reply);
			
			if ((!ip_active(reply)) ||
			    (reply->ip_receiver != space) ||
			    (reply->ip_pset != IPS_NULL) ||
			    (rcv_size < (kmsg->ikm_header.msgh_size + 
			    				kmsg->ikm_delta)))
			{
				ip_unlock(reply);
				ipc_mqueue_send_always(kmsg);
				goto receive_reply;
			}

			mqueue = &reply->ip_messages;
			imq_lock(mqueue);
			
			if ((ipc_thread_queue_first(&mqueue->imq_threads)
				!= ITH_NULL) ||
			    (ipc_kmsg_queue_first(&mqueue->imq_messages)
				!= IKM_NULL))
			{
				imq_unlock(mqueue);
				ip_unlock(reply);
				ipc_mqueue_send_always(kmsg);
				goto receive_reply;
			}

			assert(kmsg->ikm_marequest == IMAR_NULL);
			assert(ipc_thread_queue_first(&reply->ip_blocked)
				== ITH_NULL);

			reply->ip_seqno++;
			imq_unlock(mqueue);

			ip_release(reply);
			ip_unlock(reply);
			
			goto copyout_reply;
		}
		
		ip_unlock(dest);
	}

	if (option & SEND_NOTIFY) {
		mr = ipc_mqueue_send(kmsg,
				     ((option & SEND_SWITCH) ?
				      MACH_SEND_SWITCH :
				      MACH_MSG_OPTION_NONE) |
				      MACH_SEND_TIMEOUT,
				     ((option & SEND_TIMEOUT) ?
				      (mach_msg_timeout_t) send_timeout :
				      MACH_MSG_TIMEOUT_NONE),
				     IMQ_NULL_CONTINUE);
		if (mr == MACH_SEND_TIMED_OUT) {
			ipc_port_t dest = (ipc_port_t)
				kmsg->ikm_header.msgh_remote_port;

			mr = ipc_marequest_create(space, dest, MACH_PORT_NULL,
						  &kmsg->ikm_marequest);
			if (mr == MACH_MSG_SUCCESS) {
				ipc_mqueue_send_always(kmsg);
				if (IP_VALID(reply))
					ipc_port_release(reply);
				return SEND_WILL_NOTIFY;
			}
		}
	}
	else if (option & SEND_SWITCH)
		mr = ipc_mqueue_send(kmsg,
				     ((option & SEND_TIMEOUT) ?
				      MACH_SEND_TIMEOUT :
				      MACH_MSG_OPTION_NONE) |
				      MACH_SEND_SWITCH,
				     (mach_msg_timeout_t) send_timeout,
				     IMQ_NULL_CONTINUE);
	else
		mr = ipc_mqueue_send(kmsg,
				     ((option & SEND_TIMEOUT) ?
				      MACH_SEND_TIMEOUT :
				      MACH_MSG_OPTION_NONE),
				     (mach_msg_timeout_t) send_timeout,
				     IMQ_NULL_CONTINUE);

	if (mr != MACH_MSG_SUCCESS) {
		ipc_kmsg_destroy(kmsg);
		if (IP_VALID(reply))
			ipc_port_release(reply);
		return msg_return_translate(mr);
	}

receive_reply:
	if (!IP_VALID(reply))
		return RCV_INVALID_PORT;

	ip_lock(reply);
	if (reply->ip_receiver != space) {
		ip_release(reply);
		ip_check_unlock(reply);
		return RCV_INVALID_PORT;
	}

	assert(ip_active(reply));
	pset = reply->ip_pset;

	if (pset != IPS_NULL) {
		ips_lock(pset);
		if (ips_active(pset)) {
			ips_unlock(pset);
			ip_release(reply);
			ip_unlock(reply);
			return RCV_INVALID_PORT;
		}

		ipc_pset_remove(pset, reply);
		ips_check_unlock(pset);
		assert(reply->ip_pset == IPS_NULL);
	}

	mqueue = &reply->ip_messages;
	imq_lock(mqueue);
	ip_unlock(reply);

	/*
	 *	ipc_mqueue_receive may not return, because if we block
	 *	then our kernel stack may be discarded.  So we save
	 *	state here for msg_receive_continue to pick up.
	 */

	self = current_thread();
	self->ith_msg = (mach_msg_header_t *) msg;
	self->ith_option = (mach_msg_option_t) option;
	self->ith_rcv_size = (mach_msg_size_t) rcv_size;
	self->ith_timeout = (mach_msg_timeout_t) rcv_timeout;
	self->ith_object = (ipc_object_t) reply;
	self->ith_mqueue = mqueue;

	mr = ipc_mqueue_receive(mqueue,
				((option & RCV_TIMEOUT) ?
				MACH_RCV_TIMEOUT :
				MACH_MSG_OPTION_NONE) |
				MACH_RCV_OLD_FORMAT,
				(option & RCV_LARGE) ?
				(mach_msg_size_t) rcv_size :
				MACH_MSG_SIZE_MAX,
				(mach_msg_timeout_t) rcv_timeout,
				FALSE, msg_receive_continue,
				&kmsg, &seqno, 0);
	/* mqueue is unlocked */
	ipc_port_release(reply);

	if (mr != MACH_MSG_SUCCESS) {
		if (mr == MACH_RCV_HEADER_ERROR)
			mr = msg_receive_error(kmsg, msg, space);
		else
		if (mr == MACH_RCV_TOO_LARGE) {
			msg_size_t real_size =
				(msg_size_t) (mach_msg_size_t) kmsg;

			assert(real_size > rcv_size);

			(void) copyout((vm_offset_t) &real_size,
				       (vm_offset_t) &msg->msg_size,
				       sizeof(msg_size_t));
		}

		return msg_return_translate(mr);
	}
	
	if (kmsg->ikm_header.msgh_size > (mach_msg_size_t) rcv_size) {
		assert(!(option & RCV_LARGE));
		
		ipc_kmsg_destroy(kmsg);
		return (RCV_TOO_LARGE);
	}

copyout_reply:
	assert(kmsg->ikm_header.msgh_size <= (mach_msg_size_t) rcv_size);

	mr = ipc_kmsg_copyout_compat(kmsg, space, map);
	assert(mr == MACH_MSG_SUCCESS);

	kmsg->ikm_header.msgh_size += kmsg->ikm_delta;
	mr = ipc_kmsg_put((mach_msg_header_t *) msg,
			  kmsg, kmsg->ikm_header.msgh_size);
	return msg_return_translate(mr);
}

/*
 *	Routine:	msg_receive_continue
 *	Purpose:
 *		Continue after blocking for a message.
 *	Conditions:
 *		Nothing locked.  We are running on a new kernel stack,
 *		with the receive state saved in the thread.  From here
 *		control goes back to user space.
 */

void
msg_receive_continue(void)
{
	ipc_thread_t self = current_thread();
	msg_header_t *msg = (msg_header_t *) self->ith_msg;
	msg_option_t option = (msg_option_t) self->ith_option;
	ipc_kmsg_t kmsg;
	mach_port_seqno_t seqno;
	mach_msg_return_t mr;

	mr = ipc_mqueue_receive(self->ith_mqueue,
				((option & RCV_TIMEOUT) ?
				MACH_RCV_TIMEOUT :
				MACH_MSG_OPTION_NONE) |
				MACH_RCV_OLD_FORMAT,
				(option & RCV_LARGE) ?
				self->ith_rcv_size :
				MACH_MSG_SIZE_MAX,
				self->ith_timeout,
				TRUE, msg_receive_continue,
				&kmsg, &seqno, 0);
	/* mqueue is unlocked */
	ipc_object_release(self->ith_object);

	if (mr != MACH_MSG_SUCCESS) {
		if (mr == MACH_RCV_HEADER_ERROR)
			mr = msg_receive_error(kmsg, msg, current_space());
		else
		if (mr == MACH_RCV_TOO_LARGE) {
			msg_size_t real_size =
				(msg_size_t) (mach_msg_size_t) kmsg;

			assert(real_size > rcv_size);

			(void) copyout((vm_offset_t) &real_size,
				       (vm_offset_t) &msg->msg_size,
				       sizeof(msg_size_t));
		}

		thread_syscall_return(msg_return_translate(mr));
		/*NOTREACHED*/
	}
	
	if (kmsg->ikm_header.msgh_size > self->ith_rcv_size) {
		assert(!(option & RCV_LARGE));
		
		ipc_kmsg_destroy(kmsg);
		thread_syscall_return(RCV_TOO_LARGE);
		/*NOTREACHED*/
	}

	assert(kmsg->ikm_header.msgh_size <= self->ith_rcv_size);

	mr = ipc_kmsg_copyout_compat(kmsg, current_space(), current_map());
	assert(mr == MACH_MSG_SUCCESS);

	kmsg->ikm_header.msgh_size += kmsg->ikm_delta;
	mr = ipc_kmsg_put((mach_msg_header_t *) msg, kmsg,
			  kmsg->ikm_header.msgh_size);
	thread_syscall_return(msg_return_translate(mr));
	/*NOTREACHED*/
}

mach_msg_return_t
msg_receive_error(
	ipc_kmsg_t	kmsg,
	msg_header_t	*msg,
	ipc_space_t	space)
{
	mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits;
	ipc_object_t reply = (ipc_object_t) kmsg->ikm_header.msgh_local_port;
	mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits);
	mach_msg_return_t mr;

	if (IO_VALID(reply))
		ipc_object_destroy(reply, reply_type);
	(ipc_object_t)kmsg->ikm_header.msgh_local_port = IO_NULL;

	assert(!(mbits & MACH_MSGH_BITS_OLD_FORMAT));
	if (mbits & MACH_MSGH_BITS_COMPLEX) {
		mach_msg_body_t *body;

		body = (mach_msg_body_t *) (&kmsg->ikm_header + 1);
		ipc_kmsg_clean_body(kmsg, body->msgh_descriptor_count);
	}

	kmsg->ikm_header.msgh_size = sizeof (mach_msg_header_t);
	kmsg->ikm_header.msgh_reserved = 0;	/* copied to msg_type */
	mr = ipc_kmsg_copyout_compat(kmsg, space, VM_MAP_NULL);
	assert(mr == MACH_MSG_SUCCESS);

	return ipc_kmsg_put((mach_msg_header_t *)msg, kmsg,
					kmsg->ikm_header.msgh_size);
}

#endif	MACH_IPC_COMPAT