Source to kern/ipc_tt.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@
 */

/* 
 * Mach Operating System
 * Copyright (c) 1991,1990,1989,1988,1987 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.
 */

/*
 * File:	ipc_tt.c
 * Purpose:
 *	Task and thread related IPC functions.
 */

#include <mach_ipc_compat.h>

#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/mach_param.h>
#include <mach/task_special_ports.h>
#include <mach/thread_special_ports.h>
#include <vm/vm_kern.h>
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/ipc_kobject.h>
#include <kern/ipc_tt.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_table.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_right.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_object.h>



/*
 *	Routine:	ipc_task_init
 *	Purpose:
 *		Initialize a task's IPC state.
 *
 *		If non-null, some state will be inherited from the parent.
 *		The parent must be appropriately initialized.
 *	Conditions:
 *		Nothing locked.
 */

void
ipc_task_init(task, parent)
	task_t task;
	task_t parent;
{
	ipc_space_t space;
	ipc_port_t kport;
	kern_return_t kr;
	int i;

	kr = ipc_space_create(&ipc_table_entries[0], &space);
	if (kr != KERN_SUCCESS)
		panic("ipc_task_init");

	kport = ipc_port_alloc_kernel();
	if (kport == IP_NULL)
		panic("ipc_task_init");

	itk_lock_init(task);
	task->itk_self = kport;
	task->itk_sself = ipc_port_make_send(kport);
	task->itk_space = space;

	if (parent == TASK_NULL) {
		task->itk_exception = IP_NULL;
		task->itk_bootstrap = IP_NULL;
		for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
			task->itk_registered[i] = IP_NULL;
	} else {
		itk_lock(parent);
		assert(parent->itk_self != IP_NULL);

		/* inherit registered ports */

		for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
			task->itk_registered[i] =
				ipc_port_copy_send(parent->itk_registered[i]);

		/* inherit exception and bootstrap ports */

		task->itk_exception =
			ipc_port_copy_send(parent->itk_exception);
		task->itk_bootstrap =
			ipc_port_copy_send(parent->itk_bootstrap);

		itk_unlock(parent);
	}
}

/*
 *	Routine:	ipc_task_enable
 *	Purpose:
 *		Enable a task for IPC access.
 *	Conditions:
 *		Nothing locked.
 */

void
ipc_task_enable(task)
	task_t task;
{
	ipc_port_t kport;

	itk_lock(task);
	kport = task->itk_self;
	if (kport != IP_NULL)
		ipc_kobject_set(kport, (ipc_kobject_t) task, IKOT_TASK);
	itk_unlock(task);
}

/*
 *	Routine:	ipc_task_disable
 *	Purpose:
 *		Disable IPC access to a task.
 *	Conditions:
 *		Nothing locked.
 */

void
ipc_task_disable(task)
	task_t task;
{
	ipc_port_t kport;

	itk_lock(task);
	kport = task->itk_self;
	if (kport != IP_NULL)
		ipc_kobject_set(kport, IKO_NULL, IKOT_NONE);
	itk_unlock(task);
}

/*
 *	Routine:	ipc_task_terminate
 *	Purpose:
 *		Clean up and destroy a task's IPC state.
 *	Conditions:
 *		Nothing locked.  The task must be suspended.
 *		(Or the current thread must be in the task.)
 */

void
ipc_task_terminate(task)
	task_t task;
{
	ipc_port_t kport;
	int i;

	itk_lock(task);
	kport = task->itk_self;

	if (kport == IP_NULL) {
		/* the task is already terminated (can this happen?) */
		itk_unlock(task);
		return;
	}

	task->itk_self = IP_NULL;
	itk_unlock(task);

	/* release the naked send rights */

	if (IP_VALID(task->itk_sself))
		ipc_port_release_send(task->itk_sself);
	if (IP_VALID(task->itk_exception))
		ipc_port_release_send(task->itk_exception);
	if (IP_VALID(task->itk_bootstrap))
		ipc_port_release_send(task->itk_bootstrap);

	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
		if (IP_VALID(task->itk_registered[i]))
			ipc_port_release_send(task->itk_registered[i]);

	/* destroy the space, leaving just a reference for it */

	ipc_space_destroy(task->itk_space);

	/* destroy the kernel port */

	ipc_port_dealloc_kernel(kport);
}

/*
 *	Routine:	ipc_thread_init
 *	Purpose:
 *		Initialize a thread's IPC state.
 *	Conditions:
 *		Nothing locked.
 */

void
ipc_thread_init(thread)
	thread_t thread;
{
	ipc_port_t kport;

	kport = ipc_port_alloc_kernel();
	if (kport == IP_NULL)
		panic("ipc_thread_init");

	ipc_thread_links_init(thread);
	ipc_kmsg_queue_init(&thread->ith_messages);

	ith_lock_init(thread);
	thread->ith_self = kport;
	thread->ith_sself = ipc_port_make_send(kport);
	thread->ith_exception = IP_NULL;

	thread->ith_mig_reply = MACH_PORT_NULL;
	thread->ith_rpc_reply = IP_NULL;

#if	MACH_IPC_COMPAT
    {
	ipc_space_t space = thread->task->itk_space;
	ipc_port_t port;
	mach_port_t name;
	kern_return_t kr;

	kr = ipc_port_alloc_compat(space, &name, &port);
	if (kr != KERN_SUCCESS)
		panic("ipc_thread_init");
	/* port is locked and active */

	/*
	 *	Now we have a reply port.  We need to make a naked
	 *	send right to stash in ith_reply.  We can't use
	 *	ipc_port_make_send, because we can't unlock the port
	 *	before making the right.  Also we don't want to
	 *	increment ip_mscount.  The net effect of all this
	 *	is the same as doing
	 *		ipc_port_alloc_kernel		get the port
	 *		ipc_port_make_send		make the send right
	 *		ipc_object_copyin_from_kernel	grab receive right
	 *		ipc_object_copyout_compat	and give to user
	 */

	port->ip_srights++;
	ip_reference(port);
	ip_unlock(port);

	thread->ith_reply = port;
    }
#endif	MACH_IPC_COMPAT
}

/*
 *	Routine:	ipc_thread_enable
 *	Purpose:
 *		Enable a thread for IPC access.
 *	Conditions:
 *		Nothing locked.
 */

void
ipc_thread_enable(thread)
	thread_t thread;
{
	ipc_port_t kport;

	ith_lock(thread);
	kport = thread->ith_self;
	if (kport != IP_NULL)
		ipc_kobject_set(kport, (ipc_kobject_t) thread, IKOT_THREAD);
	ith_unlock(thread);
}

/*
 *	Routine:	ipc_thread_disable
 *	Purpose:
 *		Disable IPC access to a thread.
 *	Conditions:
 *		Nothing locked.
 */

void
ipc_thread_disable(thread)
	thread_t thread;
{
	ipc_port_t kport;

	ith_lock(thread);
	kport = thread->ith_self;
	if (kport != IP_NULL)
		ipc_kobject_set(kport, IKO_NULL, IKOT_NONE);
	ith_unlock(thread);
}

/*
 *	Routine:	ipc_thread_terminate
 *	Purpose:
 *		Clean up and destroy a thread's IPC state.
 *	Conditions:
 *		Nothing locked.  The thread must be suspended.
 *		(Or be the current thread.)
 */

void
ipc_thread_terminate(thread)
	thread_t thread;
{
	ipc_port_t kport;

	ith_lock(thread);
	kport = thread->ith_self;

	if (kport == IP_NULL) {
		/* the thread is already terminated (can this happen?) */
		ith_unlock(thread);
		return;
	}

	thread->ith_self = IP_NULL;
	ith_unlock(thread);

	assert(ipc_kmsg_queue_empty(&thread->ith_messages));

	/* release the naked send rights */

	if (IP_VALID(thread->ith_sself))
		ipc_port_release_send(thread->ith_sself);
	if (IP_VALID(thread->ith_exception))
		ipc_port_release_send(thread->ith_exception);

	/* nuke the outbound rpc reply port, if one exists */

	if (IP_VALID(thread->ith_rpc_reply))
		ipc_port_dealloc_reply(thread->ith_rpc_reply);

#if	MACH_IPC_COMPAT
	if (IP_VALID(thread->ith_reply)) {
		ipc_space_t space = thread->task->itk_space;
		ipc_port_t port = thread->ith_reply;
		ipc_entry_t entry;
		mach_port_t name;

		/* destroy any rights the task may have for the port */

		is_write_lock(space);
		if (space->is_active &&
		    ipc_right_reverse(space, (ipc_object_t) port,
				      &name, &entry)) {
			/* reply port is locked and active */
			ip_unlock(port);

			(void) ipc_right_destroy(space, name, entry);
			/* space is unlocked */
		} else
			is_write_unlock(space);

		ipc_port_release_send(port);
	}

	/*
	 *	Note we do *not* destroy any rights the space may have
	 *	for the thread's kernel port.  The old IPC code did this,
	 *	to avoid generating a notification when the port is
	 *	destroyed.  However, this isn't a good idea when
	 *	the kernel port is interposed, because then it doesn't
	 *	happen, exposing the interposition to the task.
	 *	Because we don't need the efficiency hack, I flushed
	 *	this behaviour, introducing a small incompatibility
	 *	with the old IPC code.
	 */
#endif	MACH_IPC_COMPAT

	/* destroy the kernel port */

	ipc_port_dealloc_kernel(kport);
}

/*
 *	Routine:	retrieve_task_self
 *	Purpose:
 *		Return a send right (possibly null/dead)
 *		for the task's user-visible self port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_task_self(task)
	task_t task;
{
	ipc_port_t port;

	assert(task != TASK_NULL);

	itk_lock(task);
	if (task->itk_self != IP_NULL)
		port = ipc_port_copy_send(task->itk_sself);
	else
		port = IP_NULL;
	itk_unlock(task);

	return port;
}

/*
 *	Routine:	retrieve_thread_self
 *	Purpose:
 *		Return a send right (possibly null/dead)
 *		for the thread's user-visible self port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_thread_self(thread)
	thread_t thread;
{
	ipc_port_t port;

	assert(thread != ITH_NULL);

	ith_lock(thread);
	if (thread->ith_self != IP_NULL)
		port = ipc_port_copy_send(thread->ith_sself);
	else
		port = IP_NULL;
	ith_unlock(thread);

	return port;
}

/*
 *	Routine:	retrieve_task_self_fast
 *	Purpose:
 *		Optimized version of retrieve_task_self,
 *		that only works for the current task.
 *
 *		Return a send right (possibly null/dead)
 *		for the task's user-visible self port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_task_self_fast(task)
	register task_t task;
{
	register ipc_port_t port;

	assert(task == current_task());

	itk_lock(task);
	assert(task->itk_self != IP_NULL);

	if ((port = task->itk_sself) == task->itk_self) {
		/* no interposing */

		ip_lock(port);
		assert(ip_active(port));
		ip_reference(port);
		port->ip_srights++;
		ip_unlock(port);
	} else
		port = ipc_port_copy_send(port);
	itk_unlock(task);

	return port;
}

/*
 *	Routine:	retrieve_thread_self_fast
 *	Purpose:
 *		Optimized version of retrieve_thread_self,
 *		that only works for the current thread.
 *
 *		Return a send right (possibly null/dead)
 *		for the thread's user-visible self port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_thread_self_fast(thread)
	register thread_t thread;
{
	register ipc_port_t port;

	assert(thread == current_thread());

	ith_lock(thread);
	assert(thread->ith_self != IP_NULL);

	if ((port = thread->ith_sself) == thread->ith_self) {
		/* no interposing */

		ip_lock(port);
		assert(ip_active(port));
		ip_reference(port);
		port->ip_srights++;
		ip_unlock(port);
	} else
		port = ipc_port_copy_send(port);
	ith_unlock(thread);

	return port;
}

#if	0
/*
 *	Routine:	retrieve_task_exception
 *	Purpose:
 *		Return a send right (possibly null/dead)
 *		for the task's exception port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_task_exception(task)
	task_t task;
{
	ipc_port_t port;

	assert(task != TASK_NULL);

	itk_lock(task);
	if (task->itk_self != IP_NULL)
		port = ipc_port_copy_send(task->itk_exception);
	else
		port = IP_NULL;
	itk_unlock(task);

	return port;
}

/*
 *	Routine:	retrieve_thread_exception
 *	Purpose:
 *		Return a send right (possibly null/dead)
 *		for the thread's exception port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_thread_exception(thread)
	thread_t thread;
{
	ipc_port_t port;

	assert(thread != ITH_NULL);

	ith_lock(thread);
	if (thread->ith_self != IP_NULL)
		port = ipc_port_copy_send(thread->ith_exception);
	else
		port = IP_NULL;
	ith_unlock(thread);

	return port;
}
#endif	0

/*
 *	Routine:	mach_task_self [mach trap]
 *	Purpose:
 *		Give the caller send rights for his own task port.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_PORT_NULL if there are any resource failures
 *		or other errors.
 */

mach_port_t
mach_task_self()
{
	task_t task = current_task();
	ipc_port_t sright;

	sright = retrieve_task_self_fast(task);
	return ipc_port_copyout_send(sright, task->itk_space);
}

/*
 *	Routine:	mach_thread_self [mach trap]
 *	Purpose:
 *		Give the caller send rights for his own thread port.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_PORT_NULL if there are any resource failures
 *		or other errors.
 */

mach_port_t
mach_thread_self()
{
	thread_t thread = current_thread();
	task_t task = thread->task;
	ipc_port_t sright;

	sright = retrieve_thread_self_fast(thread);
	return ipc_port_copyout_send(sright, task->itk_space);
}

/*
 *	Routine:	mach_reply_port [mach trap]
 *	Purpose:
 *		Allocate a port for the caller.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_PORT_NULL if there are any resource failures
 *		or other errors.
 */

mach_port_t
mach_reply_port()
{
	ipc_port_t port;
	mach_port_t name;
	kern_return_t kr;

	kr = ipc_port_alloc(current_task()->itk_space, &name, &port);
	if (kr == KERN_SUCCESS)
		ip_unlock(port);
	else
		name = MACH_PORT_NULL;

	return name;
}

#if	MACH_IPC_COMPAT

/*
 *	Routine:	retrieve_task_notify
 *	Purpose:
 *		Return a reference (or null) for
 *		the task's notify port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_task_notify(task)
	task_t task;
{
	ipc_space_t space = task->itk_space;
	ipc_port_t port;

	is_read_lock(space);
	if (space->is_active) {
		port = space->is_notify;
		if (IP_VALID(port))
			ipc_port_reference(port);
	} else
		port = IP_NULL;
	is_read_unlock(space);

	return port;
}

/*
 *	Routine:	retrieve_thread_reply
 *	Purpose:
 *		Return a reference (or null) for
 *		the thread's reply port.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
retrieve_thread_reply(thread)
	thread_t thread;
{
	ipc_port_t port;

	ith_lock(thread);
	if (thread->ith_self != IP_NULL) {
		port = thread->ith_reply;
		if (IP_VALID(port))
			ipc_port_reference(port);
	} else
		port = IP_NULL;
	ith_unlock(thread);

	return port;
}

/*
 *	Routine:	task_self [mach trap]
 *	Purpose:
 *		Give the caller send rights for his task port.
 *		If new, the send right is marked with IE_BITS_COMPAT.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_PORT_NULL if there are any resource failures
 *		or other errors.
 */

port_name_t
task_self()
{
	task_t task = current_task();
	ipc_port_t sright;
	mach_port_t name;

	sright = retrieve_task_self_fast(task);
	name = ipc_port_copyout_send_compat(sright, task->itk_space);
	return (port_name_t) name;
}

/*
 *	Routine:	task_notify [mach trap]
 *	Purpose:
 *		Give the caller the name of his own notify port.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_PORT_NULL if there isn't a notify port,
 *		if it is dead, or if the caller doesn't hold
 *		receive rights for it.
 */

port_name_t
task_notify()
{
	task_t task = current_task();
	ipc_port_t notify;
	mach_port_t name;

	notify = retrieve_task_notify(task);
	name = ipc_port_copyout_receiver(notify, task->itk_space);
	return (port_name_t) name;
}

/*
 *	Routine:	thread_self [mach trap]
 *	Purpose:
 *		Give the caller send rights for his own thread port.
 *		If new, the send right is marked with IE_BITS_COMPAT.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_PORT_NULL if there are any resource failures
 *		or other errors.
 */

port_name_t
thread_self()
{
	thread_t thread = current_thread();
	task_t task = thread->task;
	ipc_port_t sright;
	mach_port_t name;

	sright = retrieve_thread_self_fast(thread);
	name = ipc_port_copyout_send_compat(sright, task->itk_space);
	return (port_name_t) name;
}

/*
 *	Routine:	thread_reply [mach trap]
 *	Purpose:
 *		Give the caller the name of his own reply port.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		MACH_PORT_NULL if there isn't a reply port,
 *		if it is dead, or if the caller doesn't hold
 *		receive rights for it.
 */

port_name_t
thread_reply()
{
	task_t task = current_task();
	thread_t thread = current_thread();
	ipc_port_t reply;
	mach_port_t name;

	reply = retrieve_thread_reply(thread);
	name = ipc_port_copyout_receiver(reply, task->itk_space);
	return (port_name_t) name;
}

#endif	MACH_IPC_COMPAT

/*
 *	Routine:	task_get_special_port [kernel call]
 *	Purpose:
 *		Clones a send right for one of the task's
 *		special ports.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		KERN_SUCCESS		Extracted a send right.
 *		KERN_INVALID_ARGUMENT	The task is null.
 *		KERN_FAILURE		The task/space is dead.
 *		KERN_INVALID_ARGUMENT	Invalid special port.
 */

kern_return_t
task_get_special_port(task, which, portp)
	task_t task;
	int which;
	ipc_port_t *portp;
{
	ipc_port_t *whichp;
	ipc_port_t port;

	if (task == TASK_NULL)
		return KERN_INVALID_ARGUMENT;

	switch (which) {
#if	MACH_IPC_COMPAT
	    case TASK_NOTIFY_PORT: {
		ipc_space_t space = task->itk_space;

		is_read_lock(space);
		if (!space->is_active) {
			is_read_unlock(space);
			return KERN_FAILURE;
		}

		port = ipc_port_copy_send(space->is_notify);
		is_read_unlock(space);

		*portp = port;
		return KERN_SUCCESS;
	    }
#endif	MACH_IPC_COMPAT

	    case TASK_KERNEL_PORT:
		whichp = &task->itk_sself;
		break;

	    case TASK_EXCEPTION_PORT:
		whichp = &task->itk_exception;
		break;

	    case TASK_BOOTSTRAP_PORT:
		whichp = &task->itk_bootstrap;
		break;

	    default:
		return KERN_INVALID_ARGUMENT;
	}

	itk_lock(task);
	if (task->itk_self == IP_NULL) {
		itk_unlock(task);
		return KERN_FAILURE;
	}

	port = ipc_port_copy_send(*whichp);
	itk_unlock(task);

	*portp = port;
	return KERN_SUCCESS;
}

/*
 *	Routine:	task_set_special_port [kernel call]
 *	Purpose:
 *		Changes one of the task's special ports,
 *		setting it to the supplied send right.
 *	Conditions:
 *		Nothing locked.  If successful, consumes
 *		the supplied send right.
 *	Returns:
 *		KERN_SUCCESS		Changed the special port.
 *		KERN_INVALID_ARGUMENT	The task is null.
 *		KERN_FAILURE		The task/space is dead.
 *		KERN_INVALID_ARGUMENT	Invalid special port.
 */

kern_return_t
task_set_special_port(task, which, port)
	task_t task;
	int which;
	ipc_port_t port;
{
	ipc_port_t *whichp;
	ipc_port_t old;

	if (task == TASK_NULL)
		return KERN_INVALID_ARGUMENT;

	switch (which) {
#if	MACH_IPC_COMPAT
	    case TASK_NOTIFY_PORT: {
		ipc_space_t space = task->itk_space;

		is_write_lock(space);
		if (!space->is_active) {
			is_write_unlock(space);
			return KERN_FAILURE;
		}

		old = space->is_notify;
		space->is_notify = port;
		is_write_unlock(space);

		if (IP_VALID(old))
			ipc_port_release_send(old);
		return KERN_SUCCESS;
	    }
#endif	MACH_IPC_COMPAT

	    case TASK_KERNEL_PORT:
		whichp = &task->itk_sself;
		break;

	    case TASK_EXCEPTION_PORT:
		whichp = &task->itk_exception;
		break;

	    case TASK_BOOTSTRAP_PORT:
		whichp = &task->itk_bootstrap;
		break;

	    default:
		return KERN_INVALID_ARGUMENT;
	}

	itk_lock(task);
	if (task->itk_self == IP_NULL) {
		itk_unlock(task);
		return KERN_FAILURE;
	}

	old = *whichp;
	*whichp = port;
	itk_unlock(task);

	if (IP_VALID(old))
		ipc_port_release_send(old);
	return KERN_SUCCESS;
}

/*
 *	Routine:	thread_get_special_port [kernel call]
 *	Purpose:
 *		Clones a send right for one of the thread's
 *		special ports.
 *	Conditions:
 *		Nothing locked.
 *	Returns:
 *		KERN_SUCCESS		Extracted a send right.
 *		KERN_INVALID_ARGUMENT	The thread is null.
 *		KERN_FAILURE		The thread is dead.
 *		KERN_INVALID_ARGUMENT	Invalid special port.
 */

kern_return_t
thread_get_special_port(thread, which, portp)
	thread_t thread;
	int which;
	ipc_port_t *portp;
{
	ipc_port_t *whichp;
	ipc_port_t port;

	if (thread == ITH_NULL)
		return KERN_INVALID_ARGUMENT;

	switch (which) {
#if	MACH_IPC_COMPAT
	    case THREAD_REPLY_PORT:
		whichp = &thread->ith_reply;
		break;
#endif	MACH_IPC_COMPAT

	    case THREAD_KERNEL_PORT:
		whichp = &thread->ith_sself;
		break;

	    case THREAD_EXCEPTION_PORT:
		whichp = &thread->ith_exception;
		break;

	    default:
		return KERN_INVALID_ARGUMENT;
	}

	ith_lock(thread);
	if (thread->ith_self == IP_NULL) {
		ith_unlock(thread);
		return KERN_FAILURE;
	}

	port = ipc_port_copy_send(*whichp);
	ith_unlock(thread);

	*portp = port;
	return KERN_SUCCESS;
}

/*
 *	Routine:	thread_set_special_port [kernel call]
 *	Purpose:
 *		Changes one of the thread's special ports,
 *		setting it to the supplied send right.
 *	Conditions:
 *		Nothing locked.  If successful, consumes
 *		the supplied send right.
 *	Returns:
 *		KERN_SUCCESS		Changed the special port.
 *		KERN_INVALID_ARGUMENT	The thread is null.
 *		KERN_FAILURE		The thread is dead.
 *		KERN_INVALID_ARGUMENT	Invalid special port.
 */

kern_return_t
thread_set_special_port(thread, which, port)
	thread_t thread;
	int which;
	ipc_port_t port;
{
	ipc_port_t *whichp;
	ipc_port_t old;

	if (thread == ITH_NULL)
		return KERN_INVALID_ARGUMENT;

	switch (which) {
#if	MACH_IPC_COMPAT
	    case THREAD_REPLY_PORT:
		whichp = &thread->ith_reply;
		break;
#endif	MACH_IPC_COMPAT

	    case THREAD_KERNEL_PORT:
		whichp = &thread->ith_sself;
		break;

	    case THREAD_EXCEPTION_PORT:
		whichp = &thread->ith_exception;
		break;

	    default:
		return KERN_INVALID_ARGUMENT;
	}

	ith_lock(thread);
	if (thread->ith_self == IP_NULL) {
		ith_unlock(thread);
		return KERN_FAILURE;
	}

	old = *whichp;
	*whichp = port;
	ith_unlock(thread);

	if (IP_VALID(old))
		ipc_port_release_send(old);
	return KERN_SUCCESS;
}

/*
 *	Routine:	mach_ports_register [kernel call]
 *	Purpose:
 *		Stash a handful of port send rights in the task.
 *		Child tasks will inherit these rights, but they
 *		must use mach_ports_lookup to acquire them.
 *
 *		The rights are supplied in a (wired) kalloc'd segment.
 *		Rights which aren't supplied are assumed to be null.
 *	Conditions:
 *		Nothing locked.  If successful, consumes
 *		the supplied rights and memory.
 *	Returns:
 *		KERN_SUCCESS		Stashed the port rights.
 *		KERN_INVALID_ARGUMENT	The task is null.
 *		KERN_INVALID_ARGUMENT	The task is dead.
 *		KERN_INVALID_ARGUMENT	Too many port rights supplied.
 */

kern_return_t
mach_ports_register(task, memory, portsCnt)
	task_t task;
	ipc_port_t *memory;
	mach_msg_type_number_t portsCnt;
{
	ipc_port_t ports[TASK_PORT_REGISTER_MAX];
	int i;

	if ((task == TASK_NULL) ||
	    (portsCnt > TASK_PORT_REGISTER_MAX))
		return KERN_INVALID_ARGUMENT;

	/*
	 *	Pad the port rights with nulls.
	 */

	for (i = 0; i < portsCnt; i++)
		ports[i] = memory[i];
	for (; i < TASK_PORT_REGISTER_MAX; i++)
		ports[i] = IP_NULL;

	itk_lock(task);
	if (task->itk_self == IP_NULL) {
		itk_unlock(task);
		return KERN_INVALID_ARGUMENT;
	}

	/*
	 *	Replace the old send rights with the new.
	 *	Release the old rights after unlocking.
	 */

	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++) {
		ipc_port_t old;

		old = task->itk_registered[i];
		task->itk_registered[i] = ports[i];
		ports[i] = old;
	}

	itk_unlock(task);

	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
		if (IP_VALID(ports[i]))
			ipc_port_release_send(ports[i]);

	/*
	 *	Now that the operation is known to be successful,
	 *	we can free the memory.
	 */

	if (portsCnt != 0)
		kfree((vm_offset_t) memory,
		      (vm_size_t) (portsCnt * sizeof(mach_port_t)));

	return KERN_SUCCESS;
}

/*
 *	Routine:	mach_ports_lookup [kernel call]
 *	Purpose:
 *		Retrieves (clones) the stashed port send rights.
 *	Conditions:
 *		Nothing locked.  If successful, the caller gets
 *		rights and memory.
 *	Returns:
 *		KERN_SUCCESS		Retrieved the send rights.
 *		KERN_INVALID_ARGUMENT	The task is null.
 *		KERN_INVALID_ARGUMENT	The task is dead.
 *		KERN_RESOURCE_SHORTAGE	Couldn't allocate memory.
 */

kern_return_t
mach_ports_lookup(task, portsp, portsCnt)
	task_t task;
	ipc_port_t **portsp;
	mach_msg_type_number_t *portsCnt;
{
	vm_offset_t memory;
	vm_size_t size;
	ipc_port_t *ports;
	int i;

	if (task == TASK_NULL)
		return KERN_INVALID_ARGUMENT;

	size = (vm_size_t) (TASK_PORT_REGISTER_MAX * sizeof(ipc_port_t));

	memory = kalloc(size);
	if (memory == 0)
		return KERN_RESOURCE_SHORTAGE;

	itk_lock(task);
	if (task->itk_self == IP_NULL) {
		itk_unlock(task);

		kfree(memory, size);
		return KERN_INVALID_ARGUMENT;
	}

	ports = (ipc_port_t *) memory;

	/*
	 *	Clone port rights.  Because kalloc'd memory
	 *	is wired, we won't fault while holding the task lock.
	 */

	for (i = 0; i < TASK_PORT_REGISTER_MAX; i++)
		ports[i] = ipc_port_copy_send(task->itk_registered[i]);

	itk_unlock(task);

	*portsp = ports;
	*portsCnt = TASK_PORT_REGISTER_MAX;
	return KERN_SUCCESS;
}

/*
 *	Routine:	convert_port_to_task
 *	Purpose:
 *		Convert from a port to a task.
 *		Doesn't consume the port ref; produces a task ref,
 *		which may be null.
 *	Conditions:
 *		Nothing locked.
 */

task_t
convert_port_to_task(port)
	ipc_port_t port;
{
	task_t task = TASK_NULL;

	if (IP_VALID(port)) {
		ip_lock(port);
		if (ip_active(port) &&
		    (ip_kotype(port) == IKOT_TASK)) {
			task = (task_t) port->ip_kobject;
			task_reference(task);
		}
		ip_unlock(port);
	}

	return task;
}

/*
 *	Routine:	convert_port_to_space
 *	Purpose:
 *		Convert from a port to a space.
 *		Doesn't consume the port ref; produces a space ref,
 *		which may be null.
 *	Conditions:
 *		Nothing locked.
 */

ipc_space_t
convert_port_to_space(port)
	ipc_port_t port;
{
	ipc_space_t space = IS_NULL;

	if (IP_VALID(port)) {
		ip_lock(port);
		if (ip_active(port) &&
		    (ip_kotype(port) == IKOT_TASK)) {
			space = ((task_t) port->ip_kobject)->itk_space;
			is_reference(space);
		}
		ip_unlock(port);
	}

	return space;
}

/*
 *	Routine:	convert_port_to_map
 *	Purpose:
 *		Convert from a port to a map.
 *		Doesn't consume the port ref; produces a map ref,
 *		which may be null.
 *	Conditions:
 *		Nothing locked.
 */

vm_map_t
convert_port_to_map(port)
	ipc_port_t port;
{
	vm_map_t map = VM_MAP_NULL;

	if (IP_VALID(port)) {
		ip_lock(port);
		if (ip_active(port) &&
		    (ip_kotype(port) == IKOT_TASK)) {
			map = ((task_t) port->ip_kobject)->map;
			vm_map_reference(map);
		}
		ip_unlock(port);
	}

	return map;
}

/*
 *	Routine:	convert_port_to_thread
 *	Purpose:
 *		Convert from a port to a thread.
 *		Doesn't consume the port ref; produces a thread ref,
 *		which may be null.
 *	Conditions:
 *		Nothing locked.
 */

thread_t
convert_port_to_thread(port)
	ipc_port_t port;
{
	thread_t thread = THREAD_NULL;

	if (IP_VALID(port)) {
		ip_lock(port);
		if (ip_active(port) &&
		    (ip_kotype(port) == IKOT_THREAD)) {
			thread = (thread_t) port->ip_kobject;
			thread_reference(thread);
		}
		ip_unlock(port);
	}

	return thread;
}

/*
 *	Routine:	convert_task_to_port
 *	Purpose:
 *		Convert from a task to a port.
 *		Consumes a task ref; produces a naked send right
 *		which may be invalid.  
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
convert_task_to_port(task)
	task_t task;
{
	ipc_port_t port;

	itk_lock(task);
	if (task->itk_self != IP_NULL)
		port = ipc_port_make_send(task->itk_self);
	else
		port = IP_NULL;
	itk_unlock(task);

	task_deallocate(task);
	return port;
}

/*
 *	Routine:	convert_thread_to_port
 *	Purpose:
 *		Convert from a thread to a port.
 *		Consumes a thread ref; produces a naked send right
 *		which may be invalid.
 *	Conditions:
 *		Nothing locked.
 */

ipc_port_t
convert_thread_to_port(thread)
	thread_t thread;
{
	ipc_port_t port;

	ith_lock(thread);
	if (thread->ith_self != IP_NULL)
		port = ipc_port_make_send(thread->ith_self);
	else
		port = IP_NULL;
	ith_unlock(thread);

	thread_deallocate(thread);
	return port;
}

/*
 *	Routine:	space_deallocate
 *	Purpose:
 *		Deallocate a space ref produced by convert_port_to_space.
 *	Conditions:
 *		Nothing locked.
 */

void
space_deallocate(space)
	ipc_space_t space;
{
	if (space != IS_NULL)
		is_release(space);
}