This is xnu-11215.1.10. See this file in:
/*
 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * 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 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */
/*
 * @OSF_COPYRIGHT@
 */
/*
 * Mach Operating System
 * Copyright (c) 1991,1990 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  Software.Distribution@CS.CMU.EDU
 *  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.
 */
/*
 */

#include <mach/boolean.h>
#include <mach/port.h>
#include <mach/mig.h>
#include <mach/mig_errors.h>
#include <mach/mach_types.h>
#include <mach/mach_traps.h>

#include <kern/ipc_tt.h>
#include <kern/ipc_mig.h>
#include <kern/kalloc.h>
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/ipc_kobject.h>
#include <kern/misc_protos.h>

#include <ipc/port.h>
#include <ipc/ipc_kmsg.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_mqueue.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <ipc/ipc_notify.h>
#include <vm/vm_map.h>
#include <mach/thread_act.h>

#include <libkern/OSAtomic.h>

#define KERNEL_DESC_SIZE             sizeof(mach_msg_kdescriptor_t)

void
mach_msg_receive_results_complete(ipc_object_t object);

/*
 *	Routine:	mach_msg_send_from_kernel
 *	Purpose:
 *		Send a message from the kernel.
 *
 *		This is used by the client side of KernelUser interfaces
 *		to implement SimpleRoutines.  Currently, this includes
 *		memory_object messages.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_MSG_SUCCESS	Sent the message.
 *		MACH_SEND_INVALID_DEST	Bad destination port.
 *		MACH_MSG_SEND_NO_BUFFER Destination port had inuse fixed bufer
 *		                        or destination is above kernel limit
 */

mach_msg_return_t
mach_msg_send_from_kernel(
	mach_msg_header_t      *msg,
	mach_msg_size_t         send_size)
{
	mach_msg_option64_t option = MACH_SEND_KERNEL_DEFAULT;
	mach_msg_timeout_t  timeout_val = MACH_MSG_TIMEOUT_NONE;
	return kernel_mach_msg_send(msg, send_size, option, timeout_val, NULL);
}

mach_msg_return_t
mach_msg_send_from_kernel_with_options(
	mach_msg_header_t      *msg,
	mach_msg_size_t         send_size,
	mach_msg_option64_t     option,
	mach_msg_timeout_t      timeout_val)
{
	return kernel_mach_msg_send(msg, send_size, option, timeout_val, NULL);
}

static mach_msg_return_t
kernel_mach_msg_send_common(
	ipc_kmsg_t              kmsg,
	mach_msg_option64_t     option,
	mach_msg_timeout_t      timeout_val,
	boolean_t              *message_moved)
{
	mach_msg_return_t mr;

	mr = ipc_kmsg_copyin_from_kernel(kmsg);
	if (mr != MACH_MSG_SUCCESS) {
		ipc_kmsg_free(kmsg);
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
		return mr;
	}

	if (message_moved) {
		*message_moved = TRUE;
	}

	/*
	 * Until we are sure of its effects, we are disabling
	 * importance donation from the kernel-side of user
	 * threads in importance-donating tasks - unless the
	 * option to force importance donation is passed in,
	 * or the thread's SEND_IMPORTANCE option has been set.
	 * (11938665 & 23925818)
	 */
	if (current_thread()->options & TH_OPT_SEND_IMPORTANCE) {
		option &= ~MACH_SEND_NOIMPORTANCE;
	} else if ((option & MACH_SEND_IMPORTANCE) == 0) {
		option |= MACH_SEND_NOIMPORTANCE;
	}

	mr = ipc_kmsg_send(kmsg, option, timeout_val);

	if (mr != MACH_MSG_SUCCESS) {
		ipc_kmsg_destroy(kmsg, IPC_KMSG_DESTROY_ALL);
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
	}

	return mr;
}

mach_msg_return_t
kernel_mach_msg_send(
	mach_msg_header_t      *msg,
	mach_msg_size_t         send_size,
	mach_msg_option64_t     option,
	mach_msg_timeout_t      timeout_val,
	boolean_t              *message_moved)
{
	ipc_kmsg_t kmsg;
	mach_msg_return_t mr;

	KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);

	if (message_moved) {
		*message_moved = FALSE;
	}

	option &= ~MACH64_POLICY_KERNEL_EXTENSION;
	mr = ipc_kmsg_get_from_kernel(msg, send_size, option, &kmsg);
	if (mr != MACH_MSG_SUCCESS) {
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
		return mr;
	}

	return kernel_mach_msg_send_common(kmsg, option, timeout_val, message_moved);
}

extern typeof(mach_msg_send_from_kernel) mach_msg_send_from_kernel_proper;
mach_msg_return_t
mach_msg_send_from_kernel_proper(
	mach_msg_header_t      *msg,
	mach_msg_size_t         send_size)
{
	mach_msg_option64_t option = MACH_SEND_KERNEL_DEFAULT;
	mach_msg_timeout_t  timeout_val = MACH_MSG_TIMEOUT_NONE;
	ipc_kmsg_t          kmsg;
	mach_msg_return_t   mr;

	KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);

	option |= MACH64_POLICY_KERNEL_EXTENSION;
	mr = ipc_kmsg_get_from_kernel(msg, send_size, option, &kmsg);
	if (mr != MACH_MSG_SUCCESS) {
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
		return mr;
	}

	return kernel_mach_msg_send_common(kmsg, option, timeout_val, NULL);
}

mach_msg_return_t
kernel_mach_msg_send_kmsg(
	ipc_kmsg_t              kmsg)
{
	mach_msg_option_t   option = MACH_SEND_KERNEL_DEFAULT;
	mach_msg_timeout_t  timeout_val = MACH_MSG_TIMEOUT_NONE;

	KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);

	return kernel_mach_msg_send_common(kmsg, option, timeout_val, NULL);
}

mach_msg_return_t
kernel_mach_msg_send_with_builder_internal(
	mach_msg_size_t         desc_count,
	mach_msg_size_t         payload_size, /* Not total send size */
	mach_msg_option64_t     option,
	mach_msg_timeout_t      timeout_val,
	boolean_t              *message_moved,
	void                  (^builder)(mach_msg_header_t *, mach_msg_descriptor_t *, void *))
{
	ipc_kmsg_t kmsg;
	mach_msg_return_t mr;
	mach_msg_header_t *hdr;
	void *udata;
	bool complex;
	mach_msg_size_t send_size;

	KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);

	/*
	 * If message has descriptors it must be complex and vice versa. We assume
	 * this for messages originated from kernel. The two are not equivalent for
	 * user messages for bin-compat reasons.
	 */
	complex = (desc_count > 0);
	send_size = sizeof(mach_msg_header_t) + payload_size;

	if (complex) {
		send_size += sizeof(mach_msg_body_t) + desc_count * KERNEL_DESC_SIZE;
	}
	if (message_moved) {
		*message_moved = FALSE;
	}

	kmsg = ipc_kmsg_alloc(send_size, 0, desc_count,
	    IPC_KMSG_ALLOC_KERNEL | IPC_KMSG_ALLOC_ZERO);
	/* kmsg can be non-linear */

	if (kmsg == IKM_NULL) {
		mr = MACH_SEND_NO_BUFFER;
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
		return mr;
	}

	hdr   = ikm_header(kmsg);
	udata = (payload_size > 0) ? ikm_udata(kmsg, desc_count, complex) : NULL;

	/* Allow the caller to build the message, and sanity check it */
	if (complex) {
		mach_msg_kbase_t *kbase = mach_msg_header_to_kbase(hdr);

		builder(hdr, (mach_msg_descriptor_t *)kbase->msgb_dsc_array, udata);

		assert(hdr->msgh_bits & MACH_MSGH_BITS_COMPLEX);
		hdr->msgh_bits |= MACH_MSGH_BITS_COMPLEX;

		/* Set the correct descriptor count */
		kbase->msgb_dsc_count = desc_count;
	} else {
		builder(hdr, NULL, udata);

		assert(!(hdr->msgh_bits & MACH_MSGH_BITS_COMPLEX));
		hdr->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
	}
	assert(hdr->msgh_size == send_size);

	return kernel_mach_msg_send_common(kmsg, option, timeout_val, NULL);
}

mach_msg_return_t
kernel_mach_msg_send_with_builder(
	mach_msg_size_t         desc_count,
	mach_msg_size_t         udata_size,
	void                    (^builder)(mach_msg_header_t *,
	mach_msg_descriptor_t *, void *))
{
	mach_msg_option64_t options;

	options = MACH_SEND_KERNEL_DEFAULT | MACH64_POLICY_KERNEL_EXTENSION;
	return kernel_mach_msg_send_with_builder_internal(desc_count, udata_size,
	           options, MACH_MSG_TIMEOUT_NONE, NULL, builder);
}

/*
 *	Routine:	mach_msg_rpc_from_kernel
 *	Purpose:
 *		Send a message from the kernel and receive a reply.
 *		Uses ith_rpc_reply for the reply port.
 *
 *		This is used by the client side of KernelUser interfaces
 *		to implement Routines.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_MSG_SUCCESS	Sent the message.
 *		MACH_RCV_PORT_DIED	The reply port was deallocated.
 */

static mach_msg_return_t
kernel_mach_msg_rpc_common(
	mach_msg_header_t       *msg,
	mach_msg_size_t         send_size,
	mach_msg_size_t         rcv_size,
	boolean_t               interruptible,
	mach_msg_option64_t     options,
	boolean_t              *message_moved)
{
	thread_t self = current_thread();
	ipc_port_t dest = IPC_PORT_NULL;
	/* Sync IPC from kernel should pass adopted voucher and importance */
	mach_msg_option64_t option = MACH_SEND_KERNEL_DEFAULT & ~MACH_SEND_NOIMPORTANCE;
	ipc_port_t reply;
	ipc_kmsg_t kmsg;
	mach_msg_header_t *hdr;
	mach_port_seqno_t seqno;
	mach_msg_return_t mr;

	assert(msg->msgh_local_port == MACH_PORT_NULL);

	KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_START);

	if (message_moved) {
		*message_moved = FALSE;
	}

	if (!IP_VALID(msg->msgh_remote_port)) {
		return MACH_SEND_INVALID_DEST;
	}

	mr = ipc_kmsg_get_from_kernel(msg, send_size, options, &kmsg);
	/* kmsg can be non-linear */

	if (mr != MACH_MSG_SUCCESS) {
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
		return mr;
	}
	hdr = ikm_header(kmsg);

	reply = self->ith_kernel_reply_port;
	if (reply == IP_NULL) {
		thread_get_kernel_special_reply_port();
		reply = self->ith_kernel_reply_port;
		if (reply == IP_NULL) {
			panic("mach_msg_rpc_from_kernel");
		}
	}

	/* Get voucher port for the current thread's voucher */
	ipc_voucher_t voucher = IPC_VOUCHER_NULL;
	ipc_port_t voucher_port = IP_NULL;

	/* Kernel server routines do not need voucher */
	bool has_voucher = !ip_is_kobject(hdr->msgh_remote_port);

	if (has_voucher && thread_get_mach_voucher(self, 0, &voucher) == KERN_SUCCESS) {
		/* If thread does not have a voucher, get the default voucher of the process */
		if (voucher == IPC_VOUCHER_NULL) {
			voucher = ipc_voucher_get_default_voucher();
		}
		voucher_port = convert_voucher_to_port(voucher);
		ipc_kmsg_set_voucher_port(kmsg, voucher_port, MACH_MSG_TYPE_MOVE_SEND);
	}

	/* insert send-once right for the reply port and send right for the adopted voucher */
	hdr->msgh_local_port = reply;
	hdr->msgh_bits |=
	    MACH_MSGH_BITS_SET_PORTS(
		0,
		MACH_MSG_TYPE_MAKE_SEND_ONCE,
		has_voucher ? MACH_MSG_TYPE_MOVE_SEND : 0);

	mr = ipc_kmsg_copyin_from_kernel(kmsg);
	if (mr != MACH_MSG_SUCCESS) {
		/* Remove the voucher from the kmsg */
		if (has_voucher) {
			voucher_port = ipc_kmsg_get_voucher_port(kmsg);
			ipc_kmsg_clear_voucher_port(kmsg);
			ipc_port_release_send(voucher_port);
		}

		ipc_kmsg_free(kmsg);
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
		return mr;
	}

	if (message_moved) {
		*message_moved = TRUE;
	}

	/*
	 * Destination port would be needed during receive for creating
	 * Sync IPC linkage with kernel special reply port, grab a reference
	 * of the destination port before it gets donated to mqueue in ipc_kmsg_send.
	 */
	dest = hdr->msgh_remote_port;
	ip_reference(dest);

	mr = ipc_kmsg_send(kmsg, option, MACH_MSG_TIMEOUT_NONE);
	if (mr != MACH_MSG_SUCCESS) {
		ip_release(dest);
		ipc_kmsg_destroy(kmsg, IPC_KMSG_DESTROY_ALL);
		KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_INFO) | DBG_FUNC_END, mr);
		return mr;
	}

	for (;;) {
		assert(!ip_in_pset(reply));
		require_ip_active(reply);

		/* JMM - why this check? */
		if (interruptible && !self->active && !self->inspection) {
			ip_release(dest);
			thread_dealloc_kernel_special_reply_port(current_thread());
			return MACH_RCV_INTERRUPTED;
		}

		/* Setup the sync IPC linkage for the special reply port */
		ipc_port_link_special_reply_port(reply,
		    dest, FALSE);

		bzero(&self->ith_receive, sizeof(self->ith_receive));
		self->ith_recv_bufs = (mach_msg_recv_bufs_t){
			.recv_msg_size = MACH_MSG_SIZE_MAX,
		};
		self->ith_object = IO_NULL;
		self->ith_option = MACH64_MSG_OPTION_NONE;
		self->ith_knote  = ITH_KNOTE_NULL; /* not part of ith_receive */

		ipc_mqueue_receive(&reply->ip_waitq, MACH_MSG_TIMEOUT_NONE,
		    interruptible ? THREAD_INTERRUPTIBLE : THREAD_UNINT,
		    self, /* continuation ? */ false);

		mr = self->ith_state;
		kmsg = self->ith_kmsg;
		seqno = self->ith_seqno;

		mach_msg_receive_results_complete(ip_to_object(reply));

		if (mr == MACH_MSG_SUCCESS) {
			break;
		}

		assert(mr == MACH_RCV_INTERRUPTED);
		assert(interruptible);
		assert(reply == self->ith_kernel_reply_port);

		if (thread_ast_peek(self, AST_APC)) {
			ip_release(dest);
			thread_dealloc_kernel_special_reply_port(current_thread());
			return mr;
		}
	}

	/* release the destination port ref acquired above */
	ip_release(dest);
	dest = IPC_PORT_NULL;

	/* reload hdr from reply kmsg got above */
	hdr = ikm_header(kmsg);

	mach_msg_size_t kmsg_size = hdr->msgh_size;
	mach_msg_size_t kmsg_and_max_trailer_size;

	/*
	 * The amount of trailer to receive is flexible (see below),
	 * but the kmsg header must have a size that allows for a maximum
	 * trailer to follow as that's how IPC works (otherwise it might be corrupt).
	 */
	if (os_add_overflow(kmsg_size, MAX_TRAILER_SIZE, &kmsg_and_max_trailer_size)) {
		panic("kernel_mach_msg_rpc");
	}

	/* The message header and body itself must be receivable */
	if (rcv_size < kmsg_size) {
		ipc_kmsg_destroy(kmsg, IPC_KMSG_DESTROY_ALL);
		return MACH_RCV_TOO_LARGE;
	}

	/*
	 *	We want to preserve rights and memory in reply!
	 *	We don't have to put them anywhere; just leave them
	 *	as they are.
	 */
	ipc_kmsg_copyout_dest_to_kernel(kmsg, ipc_space_reply);

	mach_msg_format_0_trailer_t *trailer =  (mach_msg_format_0_trailer_t *)
	    ipc_kmsg_get_trailer(kmsg);

	/* Determine what trailer bits we can receive (as no option specified) */
	if (rcv_size < kmsg_size + MACH_MSG_TRAILER_MINIMUM_SIZE) {
		rcv_size = kmsg_size;
	} else {
		if (rcv_size >= kmsg_and_max_trailer_size) {
			/*
			 * Enough room for a maximum trailer.
			 * JMM - we really should set the expected receiver-set fields:
			 *       (seqno, context, filterid, etc...) but nothing currently
			 *       expects them anyway.
			 */
			trailer->msgh_trailer_size = MAX_TRAILER_SIZE;
			rcv_size = kmsg_and_max_trailer_size;
		} else {
			assert(trailer->msgh_trailer_size == MACH_MSG_TRAILER_MINIMUM_SIZE);
			rcv_size = kmsg_size + MACH_MSG_TRAILER_MINIMUM_SIZE;
		}
	}
	assert(trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0);
	mr = MACH_MSG_SUCCESS;

	ipc_kmsg_put_to_kernel(msg, options, kmsg, rcv_size);
	return mr;
}

mach_msg_return_t
kernel_mach_msg_rpc(
	mach_msg_header_t       *msg,
	mach_msg_size_t         send_size,
	mach_msg_size_t         rcv_size,
	boolean_t               interruptible,
	boolean_t              *message_moved)
{
	mach_msg_option64_t options = MACH64_MSG_OPTION_NONE;

	return kernel_mach_msg_rpc_common(msg, send_size, rcv_size,
	           interruptible, options, message_moved);
}

mach_msg_return_t
mach_msg_rpc_from_kernel(
	mach_msg_header_t       *msg,
	mach_msg_size_t         send_size,
	mach_msg_size_t         rcv_size)
{
	mach_msg_option64_t options = MACH64_MSG_OPTION_NONE;

	return kernel_mach_msg_rpc_common(msg, send_size, rcv_size, TRUE,
	           options, NULL);
}

/* same as mach_msg_rpc_from_kernel but for kexts */
extern typeof(mach_msg_rpc_from_kernel) mach_msg_rpc_from_kernel_proper;
mach_msg_return_t
mach_msg_rpc_from_kernel_proper(
	mach_msg_header_t       *msg,
	mach_msg_size_t         send_size,
	mach_msg_size_t         rcv_size)
{
	mach_msg_option64_t options = MACH64_POLICY_KERNEL_EXTENSION;

	return kernel_mach_msg_rpc_common(msg, send_size, rcv_size, TRUE,
	           options, NULL);
}

/*
 *	Routine:	mach_msg_destroy_from_kernel
 *	Purpose:
 *		mach_msg_destroy_from_kernel is used to destroy
 *		an unwanted/unexpected reply message from a MIG
 *		kernel-specific user-side stub.	It is like ipc_kmsg_destroy(),
 *		except we no longer have the kmsg - just the contents.
 */
void
mach_msg_destroy_from_kernel(mach_msg_header_t *msg)
{
	mach_msg_bits_t mbits = msg->msgh_bits;
	ipc_port_t      port = msg->msgh_remote_port;

	if (IP_VALID(port)) {
		ipc_object_destroy(ip_to_object(port),
		    MACH_MSGH_BITS_REMOTE(mbits));
		msg->msgh_remote_port = IP_NULL;
	}

	/*
	 * The destination (now in msg->msgh_local_port via
	 * ipc_kmsg_copyout_dest_to_kernel) has been consumed with
	 * ipc_object_copyout_dest.
	 */

	/* MIG kernel users don't receive vouchers */
	assert(!MACH_MSGH_BITS_VOUCHER(mbits));

	/* For simple messages, we're done */
	if (mbits & MACH_MSGH_BITS_COMPLEX) {
		mach_msg_kbase_t *kbase = mach_msg_header_to_kbase(msg);

		ipc_kmsg_clean_descriptors(kbase->msgb_dsc_array,
		    kbase->msgb_dsc_count);
	}
}

/* same as mach_msg_destroy_from_kernel but for kexts */
extern typeof(mach_msg_destroy_from_kernel) mach_msg_destroy_from_kernel_proper;
void
mach_msg_destroy_from_kernel_proper(mach_msg_header_t *msg)
{
	if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
		mach_msg_kbase_t *kbase = mach_msg_header_to_kbase(msg);

		ipc_kmsg_sign_descriptors(kbase->msgb_dsc_array,
		    kbase->msgb_dsc_count);
	}
	mach_msg_destroy_from_kernel(msg);
}


/************** These Calls are set up for kernel-loaded tasks/threads **************/

/*
 *	Routine:	mig_get_reply_port
 *	Purpose:
 *		Called by client side interfaces living in the kernel
 *		to get a reply port.
 */
mach_port_t
mig_get_reply_port(void)
{
	return MACH_PORT_NULL;
}

/*
 *	Routine:	mig_dealloc_reply_port
 *	Purpose:
 *		Called by client side interfaces to get rid of a reply port.
 */

void
mig_dealloc_reply_port(
	__unused mach_port_t reply_port)
{
}

/*
 *	Routine:	mig_put_reply_port
 *	Purpose:
 *		Called by client side interfaces after each RPC to
 *		let the client recycle the reply port if it wishes.
 */
void
mig_put_reply_port(
	__unused mach_port_t reply_port)
{
}

/*
 * mig_strncpy.c - by Joshua Block
 *
 * mig_strncp -- Bounded string copy.  Does what the library routine strncpy
 * OUGHT to do:  Copies the (null terminated) string in src into dest, a
 * buffer of length len.  Assures that the copy is still null terminated
 * and doesn't overflow the buffer, truncating the copy if necessary.
 *
 * Parameters:
 *
 *     dest - Pointer to destination buffer.
 *
 *     src - Pointer to source string.
 *
 *     len - Length of destination buffer.
 */
int
mig_strncpy(
	char            *dest,
	const char      *src,
	int             len)
{
	int i = 0;

	if (len > 0) {
		if (dest != NULL) {
			if (src != NULL) {
				for (i = 1; i < len; i++) {
					if (!(*dest++ = *src++)) {
						return i;
					}
				}
			}
			*dest = '\0';
		}
	}
	return i;
}

/*
 * mig_strncpy_zerofill -- Bounded string copy.  Does what the
 * library routine strncpy OUGHT to do:  Copies the (null terminated)
 * string in src into dest, a buffer of length len.  Assures that
 * the copy is still null terminated and doesn't overflow the buffer,
 * truncating the copy if necessary. If the string in src is smaller
 * than given length len, it will zero fill the remaining bytes in dest.
 *
 * Parameters:
 *
 *     dest - Pointer to destination buffer.
 *
 *     src - Pointer to source string.
 *
 *     len - Length of destination buffer.
 */
int
mig_strncpy_zerofill(
	char            *dest,
	const char      *src,
	int             len)
{
	int i = 0;
	boolean_t terminated = FALSE;
	int retval = 0;

	if (len <= 0 || dest == NULL) {
		return 0;
	}

	if (src == NULL) {
		terminated = TRUE;
	}

	for (i = 1; i < len; i++) {
		if (!terminated) {
			if (!(*dest++ = *src++)) {
				retval = i;
				terminated = TRUE;
			}
		} else {
			*dest++ = '\0';
		}
	}

	*dest = '\0';
	if (!terminated) {
		retval = i;
	}

	return retval;
}

void *
mig_user_allocate(
	vm_size_t       size)
{
	return kalloc_type_var_impl(KT_IPC_KMSG_KDATA_OOL,
	           size, Z_WAITOK, NULL);
}

void
mig_user_deallocate(
	char            *data,
	vm_size_t       size)
{
	kfree_type_var_impl(KT_IPC_KMSG_KDATA_OOL, data, size);
}