Source to kernserv/kern_server.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) 1989 NeXT, Inc.
 *
 * HISTORY
 *  4-Jun-91  Gregg Kellogg (gk) at NeXT
 *	Abort server_thread before calling thread_dowait when trying to
 *	service callouts.
 *
 * 27-Aug-90  Gregg Kellogg (gk) at NeXT
 *	Fixed bugs in kern_serv_callout.
 *	Remove compatibility for servers < version 2.
 *
 * 12-Jul-90  Gregg Kellogg (gk) at NeXT
 *	Don't allocate MSG_SIZE_MAX for in and out messages.  Only allocate
 *	a minimal amount fo input message and use RCV_TOO_LARGE logic
 *	to allocate more.  Output message is only allocated for servers
 *	and older style handlers.
 *
 * 23-May-90  Gregg kellogg (gk) at NeXT
 *	Changed to use thread_reply port instead of bootstrap port.
 *	Checks to see if it's running in the kernel_task and uses
 *	kernel_ipc_space accordingly.
 *	Terminates on shutdown, doesn't suspend.
 *	Doesn't depend on being in a separate task now.
 *
 * 06-Feb-89  Gregg Kellogg (gk) at NeXT
 *	Created.
 *
 */ 

/*
 * Generic Kernel Server for loaded server tasks.
 *
 * This code is run by dynamically loaded Mach kernel servers.  It recieves
 * instructions from it's boot port server to set up it's local environment
 * and enter the server loop to do per-server processing.
 *
 * The server basically exists to dispatch messages recieved on ports
 * to procedures.  Any un-handled messages are either thrown away or handled
 * directly by the server.  Handled messages are either acked, or not acked.
 * If the return status is > 0, then any resources passed (other than ports)
 * are deallocated from the message as well.
 */

#import <mach/mig_errors.h>
#import <sys/callout.h>
#import <mach/notify.h>
#import <kern/kern_port.h>
#import <kern/sched_prim.h>
#import <mach/mach_traps.h>
#import <kern/kalloc.h>
#import <kernserv/kern_notify.h>
#import <mach/vm_param.h>
#import "kernobjc.h"
#import <kern/thread_call.h>

#import <machine/spl.h>

#import <kernserv/kern_server_handler.h>
#import <kernserv/kern_server_reply.h>

#define panic(s) (curipl() == 0 ? kern_serv_panic(ksp->bootstrap_port, s) \
				: printf("can't panic: %s\n", s))

/* Now a per server static port_t kernel_port; */

/*
 * Use internal version of thread_terminate.
 */
#undef	thread_terminate

/*
 * Locally used functions
 */
static kern_return_t kern_serv_dispatch (
	msg_header_t *in_msg,
	kern_server_t ksp);

static void kern_serv_send_log(void *arg);
static void kern_serv_log_init (	// initialize server's log buffer
	log_t	*log,			// uninitialized log struct
	int	num_entries);		// size of log

static void kern_serv_log_free (log_t *log); // deallocate log structure
static void kern_serv_interrupt_server(thread_t thread);

const kern_serv_t kern_serv_proto = {
	0,		// arg to be filled in
	0,		// don't wait for message to be sent
	kern_serv_instance_loc,
	kern_serv_boot_port,
	kern_serv_wire_range,
	kern_serv_unwire_range,
	kern_serv_port_proc,
	kern_serv_port_death_proc,
	kern_serv_call_proc,
	kern_serv_shutdown,
	kern_serv_log_level,
	kern_serv_get_log,
	kern_serv_port_serv,
	kern_serv_version,
	kern_serv_load_objc};

/*
 * Prototypes for kernel functions not yet prototyped.
 */
int bcopy(void *to, void *from, int size);
int curipl(void);
kern_return_t vm_map_pageable(
	vm_map_t	map,
	vm_offset_t	start,
	vm_offset_t	end,
	boolean_t	new_pageable);
#if	DEBUG
void xpr(char *msg, ...);
#endif	DEBUG

#if	DEBUG
static boolean_t kserv_debug = TRUE;
#define ks_log(s) {if (kserv_debug) XPR(XPR_LDD, s); }
#define ks_log2(s) ks_log(s)
void kern_server_main1(void);
void kern_server_main2(void);
#else	DEBUG
#define ks_log(s)
#define ks_log2(s)
#endif	DEBUG

/*
 * Entry point to kernel server, set up task environment.
 */
void kern_server_main(void)
{
#if	DEBUG
	int foo[10];
	kern_server_main1();
}
void kern_server_main1(void)
{
	int foo[10];
	kern_server_main2();
}
void kern_server_main2(void)
{
#endif	DEBUG
	kern_return_t	r;
	kern_server_t	ksp = (kern_server_t)kalloc(sizeof *ksp);
	port_t		boot_listener_port;
	port_t		reply_port;
	port_set_name_t	port_set;
	msg_header_t	*in_msg;
	int		error;
	register int	i, s;
	kern_serv_t kern_serv = kern_serv_proto;

#if	0
	volatile int instr;

	/*
	 * Write to the loaded text so that the debugger can set break-points.
	 */
	instr = *(int *)&kern_server_main2;
	*(int *)&kern_server_main2 = instr;
#endif	0

	kern_serv.arg = (void *)&ksp;

	/*
	 *	Find out who we are.
	 */

	if (current_task() != kernel_task) {
		current_task()->kernel_vm_space = TRUE;
	}

	bzero((char *)ksp, sizeof(*ksp));
	ksp->version = -1;

	/*
	 * Our task port.
	 */
	ksp->task_port = task_self();

	/*
	 * Our thread structure.
	 */
	ksp->server_thread = current_thread();
	simple_lock_init(&ksp->slock);

	/*
	 * Initialize message send queue.
	 */
	queue_init(&ksp->msg_callout_q);
	queue_init(&ksp->msg_callout_fq);
	queue_init(&ksp->notify_q);
	for (  i = sizeof(ksp->msg_send_array)/sizeof(ksp->msg_send_array[0])-1
	     ; i >= 0
	     ; i--)
	{
		queue_enter(&ksp->msg_callout_fq, &ksp->msg_send_array[i],
			struct msg_send_entry *, link);
	}

	/*
	 * Use our thread_reply port as the bootstrap port.  This is
	 * claimed by the kern_loader before we startup, so there's no
	 * race condition.
	 */
	r = thread_get_special_port(thread_self(), THREAD_REPLY_PORT,
		&boot_listener_port);
	if (r != KERN_SUCCESS || boot_listener_port == PORT_NULL) {
		printf("k_server: can't find listener port..terminating\n");
		thread_terminate(current_thread());
		thread_halt_self();
	}
	ksp->boot_listener_port = boot_listener_port;

	/*
	 * Allocate a new reply port.
	 */
	r = port_allocate(task_self(), &reply_port);
	if (r != KERN_SUCCESS) {
		printf("k_server: can't allocate reply port..terminating\n");
		thread_terminate(current_thread());
		thread_halt_self();
	}
	
	r = thread_set_special_port(thread_self(), THREAD_REPLY_PORT,
		reply_port);
	if (r != KERN_SUCCESS) {
		printf("k_server: can't set reply port..terminating\n");
		thread_terminate(current_thread());
		thread_halt_self();
	}

	/*
	 * Forget the reply_port we were using so that it isn't
	 * cached anymore.
	 */

	/*
	 * Allocate the basic set of ports we need for communication
	 * (notify port).
	 */
	r = port_set_allocate(task_self(), &port_set);
	if (r != KERN_SUCCESS) {
		printf("k_server: can't allocate port set..terminating\n");
		thread_terminate(current_thread());
		thread_halt_self();
	}
	ksp->port_set = port_set;

	/*
	 * Add boot listener port to the port set.
	 */
	if (   port_set_add(task_self(), port_set, boot_listener_port)
	    != KERN_SUCCESS)
	{
		printf("k_server: can't add listener port\n");
		thread_terminate(current_thread());
		thread_halt_self();
	}

	/*
	 * Allocate and set up the notify port.
	 */
	r = port_allocate(ksp->task_port, &ksp->notify_port);
	if (r == KERN_SUCCESS)
		(void) port_set_add(ksp->task_port, ksp->port_set,
			ksp->notify_port);
	else
		kern_serv_panic(ksp->bootstrap_port,
			"k_server: can't get notify port");

	if (current_task() != kernel_task) {
		/*
		 * Set this port as our task's notify port.
		 */
		(void) task_set_special_port(task_self(),
			TASK_NOTIFY_PORT, ksp->notify_port);
	}

	kern_serv_notify(&ksp, ksp->notify_port, ksp->bootstrap_port);

	/*
	 * The kernel's task port.
	 */
	ksp->kernel_port = kern_serv_kernel_task_port();

	/*
	 *	Service loop... receive messages and process them.
	 */

	in_msg = (msg_header_t *)kalloc(kern_servMaxRequestSize);
	ksp->msg = in_msg;
	ksp->msg_size = kern_servMaxRequestSize;
	for (;;) {
		/*
		 * Look for things to do that need to be done
		 * from this task/thread.
		 * Send messages.
		 */
		s = splhigh();
		simple_lock(&ksp->slock);

		while (!queue_empty(&ksp->msg_callout_q)) {
			struct msg_send_entry *msep;

			queue_remove_first(&ksp->msg_callout_q,
				msep, struct msg_send_entry *, link);

			simple_unlock(&ksp->slock);
			splx(s);

			ks_log(("kern_server_main: qed callout func 0x%x\n",
				msep->func));
			(*msep->func)(msep->arg);

			s = splhigh();
			simple_lock(&ksp->slock);

			queue_enter(&ksp->msg_callout_fq, msep,
				struct msg_send_entry *, link);
		}

		simple_unlock(&ksp->slock);
		splx(s);

		/*
		 * Wait for the next request.
		 */
	    receive:
		in_msg->msg_local_port = port_set;
		in_msg->msg_size = ksp->msg_size;
		r = msg_receive(in_msg, RCV_TIMEOUT|RCV_INTERRUPT|RCV_LARGE,
				1000);

		/*
		 * If our receive was interrupted, go back to it.
		 */
		switch (r) {
		case RCV_INTERRUPTED:
			break;
		case RCV_TIMED_OUT:
			continue;
		case RCV_TOO_LARGE:
		{
			vm_size_t	new_msg_size = ksp->msg->msg_size;
			/*
			 * We need to allocate a larger message to return.
			 */
			kfree(ksp->msg, ksp->msg_size);
			ksp->msg_size = new_msg_size;
			ksp->msg = (void *)kalloc(ksp->msg_size);
			in_msg = ksp->msg;
			goto receive;
		}

		case KERN_SUCCESS:
			break;
		default:
			kern_serv_panic(ksp->bootstrap_port,
				"kern_server_main: received return bad return"
				" from msg_receive");
			break;
		}

		ks_log(("kern_server_main: received msgid %d "
			"on port %d\n", in_msg->msg_id,
			in_msg->msg_local_port));

		/*
		 * Dispatch the message based on it's port.
		 */
		if (in_msg->msg_local_port == ksp->notify_port) {
			notification_t	*n = (notification_t *) in_msg;
			switch(in_msg->msg_id) {
			case NOTIFY_PORT_DELETED:
				/*
				 * Give loaded code a chance to handle it
				 */
				if (ksp->pd_proc) {
					if ((*ksp->pd_proc)(n->notify_port))
						break;
				} else if (ksp->pn_proc)
					(*ksp->pn_proc)(n->notify_port,
						in_msg->msg_id);
				kern_serv_port_gone(&ksp, n->notify_port);
				break;
			default:
				if (ksp->pn_proc)
					(*ksp->pn_proc)(n->notify_port,
				    			in_msg->msg_id);
				break;
			}
			continue;
		} else if (   in_msg->msg_id >= NOTIFY_FIRST
			   && in_msg->msg_id < NOTIFY_LAST)
		{
			notification_t	*n = (notification_t *) in_msg;
			ks_notify_t *np;

			/*
			 * If we have a notify request on this port, forward
			 * the message to the requested port.
			 */
			for (  np = (ks_notify_t *)queue_first(&ksp->notify_q)
			     ; !queue_end(&ksp->notify_q, (queue_entry_t)np)
			     ; np = (ks_notify_t *)queue_next(&np->link))
				if (np->req_port == n->notify_port) {
					in_msg->msg_remote_port =
						np->reply_port;
					ks_log2(("kern_server_main: "
						 "forwarding notification "
						 "request to port %d\n",
						 np->reply_port));
					msg_send(in_msg, MSG_OPTION_NONE, 0);
					queue_remove(&ksp->notify_q, np,
						ks_notify_t *, link);
					kfree(np, sizeof(*np));
					continue;
				}
		}

		ksp->local_port = in_msg->msg_local_port;

		error = kern_serv_dispatch(in_msg, ksp);
		ks_log2(("kern_server_main: return from ks_disp %d\n",
			error));

		if (   error == MIG_BAD_ID
		    && in_msg->msg_local_port == boot_listener_port)
		{
			error = kern_serv_handler(in_msg, &kern_serv);
			ks_log2(("kern_server_main: "
				"return from ks_server %d\n",
				error));
		}
	}
}

typedef struct {
	msg_header_t Head;
	msg_type_t RetCodeType;
	kern_return_t RetCode;
} Reply;

static kern_return_t kern_serv_port_proc_call (
	msg_header_t *in_msg,
	port_proc_map_t *pp)
{
	Reply *out_msg;
	kern_return_t ret_code;
	port_t local_port;

	if (pp->type == PP_handler)
		return (*pp->proc)(in_msg, pp->uarg);

	out_msg = (Reply *)kalloc(MSG_SIZE_MAX);
	local_port = in_msg->msg_local_port;
	in_msg->msg_local_port = (port_t)pp->uarg;

	(*((port_map_serv_t)pp->proc))(in_msg, (msg_header_t *)out_msg);
	ret_code = out_msg->RetCode;

	if (out_msg->RetCode == MIG_NO_REPLY)
		ret_code = KERN_SUCCESS;
	else
		ret_code = msg_send(&out_msg->Head, MSG_OPTION_NONE, 0);

	kfree(out_msg, MSG_SIZE_MAX);

	return ret_code;
}

#define RCV_IN_PROGRESS		RCV_ERRORS_START
/*
 * Call a proc indirectly based on the port.
 */
static kern_return_t kern_serv_dispatch (
	msg_header_t *in_msg,
	kern_server_t ksp)
{
	int i = ksp->last_rec_index;
	kern_return_t r = RCV_IN_PROGRESS;

	if (!ksp)
		return MIG_BAD_ID;

	if (in_msg->msg_local_port == ksp->last_unrec_port) {
		ks_log2(("kern_serv_dispatch: port %d was unrec\n",
			in_msg->msg_local_port));
		r = MIG_BAD_ID;
	} else if (in_msg->msg_local_port == ksp->last_rec_port) {
		ks_log2(("kern_serv_dispatch: port %d was rec(%d), "
			"proc 0x%x(%d)\n",
			in_msg->msg_local_port, i, ksp->port_proc[i].proc,
			ksp->port_proc[i].uarg));
		r = kern_serv_port_proc_call(in_msg, &ksp->port_proc[i]);
	} else {
		for (   i = 0
		     ;    i < KERN_SERVER_NPORTPROC
		       && ksp->port_proc[i].port != in_msg->msg_local_port
		     ; i++)
			;

		if (i != KERN_SERVER_NPORTPROC) {
			ksp->last_rec_port = in_msg->msg_local_port;
			ksp->last_rec_index = i;
			ks_log2(("kern_serv_dispatch: port %d now rec(%d), "
				"proc 0x%x(%d)\n",
				in_msg->msg_local_port, i,
				ksp->port_proc[i].proc,
				ksp->port_proc[i].uarg));
			r = kern_serv_port_proc_call(in_msg,
				&ksp->port_proc[i]);
		}
	}

	if (r == RCV_IN_PROGRESS) {
		ks_log2(("kern_serv_dispatch: port %d now unrec\n",
			in_msg->msg_local_port));
		ksp->last_unrec_port = in_msg->msg_local_port;
		r = MIG_BAD_ID;
	}

	return r;
}

/*
 * Got notification that port we have send rights on has gone away, clean up.
 */
void kern_serv_port_gone (
	kern_server_t	*kspp,
	port_name_t	port)
{
	kern_server_t	ksp = *kspp;
	register int i;

	if (ksp == 0)
		return;

	if (ksp->last_rec_port == port)
		ksp->last_rec_port = PORT_NULL;

	/*
	 * If this is the log port, don't try to send anything to it.
	 */
	if (ksp->log_port && port == ksp->log_port)
		ksp->log_port == PORT_NULL;
		
	/*
	 * Free up port/proc pairs
	 */
	for (i = 0; i < KERN_SERVER_NPORTPROC; i++)
		if (ksp->port_proc[i].port == port) {
			ksp->port_proc[i].port = PORT_NULL;
			ksp->port_proc[i].proc = 0;
			break;
		}
	return;
}

kern_return_t kern_serv_instance_loc (
	void		*arg,
	vm_address_t	instance_loc)
{
	kern_server_t ksp = *((kern_server_t *)arg);
	kern_server_t *kspp = (kern_server_t *)instance_loc;

	ks_log(("kern_serv_instance_loc: 0x%x\n", instance_loc));
	*kspp = ksp;

	return KERN_SUCCESS;
}

/*
 * Specify user's version of system user compiled with.
 */
kern_return_t kern_serv_version (
	void		*arg,
	int		version)
{
	kern_server_t ksp = *((kern_server_t *)arg);

	if (version < KS_COMPAT)
		return KERN_SERVER_BAD_VERSION;
	ksp->version = version;

	return KERN_SUCCESS;
}
kern_return_t kern_serv_load_objc(
	void		*arg,
	vm_address_t	header)
{
	kern_server_t ksp = *((kern_server_t *)arg);
	int		error;

#if	KERNOBJC
	ksp->mach_header = (struct mach_header *) header;
	error = objc_registerModule (ksp->mach_header, 0);
#endif	KERNOBJC
	return KERN_SUCCESS;
}

				  
kern_return_t kern_serv_boot_port (	// how to talk to loader
	void		*arg,
	port_t		boot_port)
{
	kern_server_t ksp = *((kern_server_t *)arg);

	ksp->bootstrap_port = boot_port;
	return KERN_SUCCESS;
}

/*
 * Kernel version of kern_serv_notify, doesn't contact kern_loader, uses
 * internal port_request_notification facility.
 */
kern_return_t kern_serv_notify (
	kern_server_t	*kspp,
	port_t		reply_port,
	port_t		req_port)
{
	kern_server_t ksp;
	port_t	bootstrap_port;
	kern_return_t r;
	kern_port_t rp, np;

	ksp = *kspp;
	bootstrap_port = ksp->bootstrap_port;

	if (current_task() != kernel_task) {
		ks_notify_t *np;

		if (reply_port == ksp->notify_port)
			/*
			 * Notification will happen automatically.
			 */
			return KERN_SUCCESS;

		/*
		 * Make sure that an entry doesn't already exist for this
		 * pair.
		 */
		for (  np = (ks_notify_t *)queue_first(&ksp->notify_q)
		     ; !queue_end(&ksp->notify_q, (queue_entry_t)np)
		     ; np = (ks_notify_t *)queue_next(&np->link))
			if (   np->reply_port == reply_port
			    && np->req_port == req_port)
			    	return KERN_FAILURE;

		np = (kern_port_t)kalloc(sizeof *np);
		np->reply_port = reply_port;
		np->req_port = req_port;
		queue_enter(&ksp->notify_q, np, ks_notify_t *, link);

		/*
		 * Notification will be forwarded from kern_server.
		 */
		return KERN_SUCCESS;
	}

	r = get_kern_port(current_task(), req_port, &rp);
	if (r != KERN_SUCCESS)
		return r;
	r = get_kern_port(current_task(), reply_port, &np);
	if (r != KERN_SUCCESS)
		return r;
	port_request_notification(rp, np);
	return KERN_SUCCESS;
}

kern_return_t kern_serv_wire_range (	// wire the specified range or memory
	void		*arg,
	vm_address_t	addr,
	vm_size_t	size)
{
	kern_server_t ksp = *((kern_server_t *)arg);

	ks_log(("kern_serv_wire_range: addr 0x%x for 0x%x bytes\n",
		addr, size));
#if	DIAGNOSTIC
	printf("kern_server: wiring 0x%x to 0x%x\n", trunc_page(addr),
		round_page(addr+size));
#endif
	return vm_map_pageable(kernel_task->map, trunc_page(addr),
		round_page(addr+size), FALSE);
}

kern_return_t kern_serv_unwire_range (	// unwire the specified range or memory
	void		*arg,
	vm_address_t	addr,
	vm_size_t	size)
{
	kern_server_t ksp = *((kern_server_t *)arg);

	ks_log(("kern_serv_unwire_range: addr 0x%x for 0x%x bytes\n",
		addr, size));
#if	DIAGNOSTIC
	printf("kern_server: unwiring 0x%x to 0x%x\n", trunc_page(addr),
		round_page(addr+size));
#endif
	return vm_map_pageable(kernel_task->map, trunc_page(addr),
		round_page(addr+size), TRUE);
}

/*
 * Map messages coming on on the specified port to call the specified proc.
 * If this mapping can't be made FALSE is returned.
 * The port is added to the portset for this server.
 */
kern_return_t kern_serv_port_proc (	// map a message on port to proc/arg
	void		*arg,		// record into structure
	port_all_t	port,		// port to map (all rights passed)
	port_map_proc_t	proc,		// proc to call
	int		uarg)		// replace local_port with uarg
{
	register int i;
	kern_return_t r;
	kern_server_t ksp = *((kern_server_t *)arg);

	ks_log(("kern_serv_port_proc: port %d proc 0x%x uarg %d\n",
		port, proc, uarg));

	if (ksp->last_unrec_port == port)
		ksp->last_unrec_port = PORT_NULL;

	for (i = 0; i < KERN_SERVER_NPORTPROC; i++)
		if (ksp->port_proc[i].port == port)
			ksp->port_proc[i].port = PORT_NULL;

	for (i = 0; i < KERN_SERVER_NPORTPROC; i++)
		if (ksp->port_proc[i].port == PORT_NULL)
			break;

	r = KERN_RESOURCE_SHORTAGE;
	if (   i == KERN_SERVER_NPORTPROC
	    || (r = port_set_add(ksp->task_port, ksp->port_set, port))
		!= KERN_SUCCESS)
		return r;

	ksp->port_proc[i].port = port;
	ksp->port_proc[i].proc = proc;
	ksp->port_proc[i].uarg = (void *)uarg;
	ksp->port_proc[i].type = PP_handler;

	return KERN_SUCCESS;
}

/*
 * Map messages coming on on the specified port to call the specified proc.
 * If this mapping can't be made FALSE is returned.
 * The port is added to the portset for this server.
 */
kern_return_t kern_serv_port_serv (	// map a message on port to proc/arg
	void		*arg,		// record into structure
	port_all_t	port,		// port to map (all rights passed)
	port_map_proc_t	proc,		// proc to call
	int		uarg)		// replace local_port with uarg
{
	register int i;
	kern_return_t r;
	kern_server_t ksp = *((kern_server_t *)arg);

	ks_log(("kern_serv_port_serv: port %d proc 0x%x uarg %d\n",
		port, proc, uarg));

	if (ksp->last_unrec_port == port)
		ksp->last_unrec_port = PORT_NULL;

	for (i = 0; i < KERN_SERVER_NPORTPROC; i++)
		if (ksp->port_proc[i].port == port)
			ksp->port_proc[i].port = PORT_NULL;

	for (i = 0; i < KERN_SERVER_NPORTPROC; i++)
		if (ksp->port_proc[i].port == PORT_NULL)
			break;

	r = KERN_RESOURCE_SHORTAGE;
	if (   i == KERN_SERVER_NPORTPROC
	    || (r = port_set_add(ksp->task_port, ksp->port_set, port))
		!= KERN_SUCCESS)
		return r;

	ksp->port_proc[i].port = port;
	ksp->port_proc[i].proc = proc;
	ksp->port_proc[i].uarg = (void *)uarg;
	ksp->port_proc[i].type = PP_server;

	return KERN_SUCCESS;
}

kern_return_t kern_serv_port_death_proc ( // specify port death handler
	void			*arg,	// record into structure
	port_death_proc_t	proc)	// record into structure
{
	kern_server_t ksp = *((kern_server_t *)arg);

	ksp->pd_proc = proc;
	return KERN_SUCCESS;
}

kern_return_t kern_serv_call_proc (	// call procedure with argument
	void		*arg,		// record into structure
	call_proc_t	proc,		// record into structure
	int		uarg)		// arg to supply
{
	kern_server_t ksp = *((kern_server_t *)arg);

	ks_log(("kern_serv_call_proc: proc 0x%x uarg %d\n",
		proc, uarg));

	if (proc) {
		(*proc)(uarg);
		return KERN_SUCCESS;
	}
	return KERN_SERVER_ERROR;
}

kern_return_t kern_serv_shutdown (
	void		*arg)
{
	kern_server_t ksp = *((kern_server_t *)arg);
	int i;

	ks_log(("kern_serv_shutdown\n"));

#if	KERNOBJC
	/*
	 * Detach from Objective-C runtime.
	 */
	if (ksp->mach_header)
		objc_unregisterModule (ksp->mach_header, 0);
#endif	KERNOBJC

	/*
	 * Stop logging and free the data
	 * if we were logging.
	 */
	if (ksp->log.level) {
		kern_serv_log_free(&ksp->log);
		ksp->log.level = 0;
	}

	/*
	 * Deallocate all the port mappings we have.
	 */
	for (i = 0; i < KERN_SERVER_NPORTPROC; i++)
		if (ksp->port_proc[i].port != PORT_NULL) {
			port_deallocate(ksp->task_port,
				ksp->port_proc[i].port);
			ksp->port_proc[i].port = PORT_NULL;
			ksp->port_proc[i].proc = 0;
		}

	/*
	 * Deallocate our boot_listener and notify ports.
	 */
	port_deallocate(ksp->task_port, ksp->boot_listener_port);
	port_deallocate(ksp->task_port, ksp->notify_port);

	/*
	 * Deallocate our port_set
	 */
	port_set_deallocate(ksp->task_port, ksp->port_set);

	/*
	 * Deallocate our message frame.
	 */
	kfree(ksp->msg, ksp->msg_size);
	kfree(ksp, sizeof *ksp);
	thread_terminate(current_thread());
	while (1)
		/*
		 * Wait to be killed.
		 */
		thread_halt_self();

	return KERN_SUCCESS;
}

// log level from message on port
kern_return_t kern_serv_log_level (
	void		*arg,
	int		log_level)
{
	kern_server_t ksp = *((kern_server_t *)arg);
	int old_level = ksp->log.level;

	ksp->log.level = log_level;

	/*
	 * Only keep space around for the log
	 * if we need it.
	 */
	if (old_level == 0 && ksp->log.level)
		kern_serv_log_init(&ksp->log,
			KERN_SERVER_LOG_SIZE);
	else if (ksp->log.level == 0 && old_level)
		kern_serv_log_free(&ksp->log);

	return KERN_SUCCESS;
}

kern_return_t kern_serv_get_log (
	void		*arg,
	port_t		reply_port)	// port to send log information to
{
	int s, n;
	kern_server_t ksp = *((kern_server_t *)arg);

	if (ksp->log.level == 0) {
		port_deallocate(ksp->task_port, reply_port);
		return KERN_SERVER_NOTLOGGING;
	}

	if (ksp->log.ptr == ksp->log.base) {
		ksp->log_port = reply_port;
		return KERN_SUCCESS;
	} else {
		vm_size_t size =
		    round_page((char *)ksp->log.ptr - (char *)ksp->log.base);
		pointer_t ldata;
		unsigned int ldata_count;
		kern_return_t r;

		ASSERT(    (void *)ksp->log.base
			== (void *)round_page(ksp->log.base));
		r = vm_read(ksp->kernel_port,
			(vm_address_t)ksp->log.base, size, &ldata,
			&ldata_count);
		ASSERT(r == KERN_SUCCESS);
		kern_serv_log_data(reply_port,
			(log_entry_t *)ldata,
			ksp->log.ptr - ksp->log.base);

		port_deallocate(ksp->task_port, reply_port);
		vm_deallocate(ksp->task_port, ldata, size);
	}

	s = splhigh();
	simple_lock(&ksp->slock);
	ksp->log.ptr = ksp->log.base;
	simple_unlock(&ksp->slock);
	splx(s);

	return KERN_SUCCESS;
}

void kern_serv_log (			// log a message
	kern_server_t	*kspp,		// kern_server instance vars
	int		log_level,	// level to log at
	char		*msg,		// what to log (followed by args)
	int		arg1,
	int		arg2,
	int		arg3,
	int		arg4,
	int		arg5)
{
	kern_server_t ksp = *kspp;
	register int s;
	log_entry_t *x;

	if (log_level > ksp->log.level || ksp->log.base == 0)
		return;

	s = splhigh();
	simple_lock(&ksp->slock);

	x = ksp->log.ptr++;
	if (ksp->log.ptr == ksp->log.last) {
		ksp->log.ptr--;
		simple_unlock(&ksp->slock);
		splx(s);
		return;
	}
	simple_unlock(&ksp->slock);
	splx(s);

	x->msg = msg;
	x->arg1 = arg1;
	x->arg2 = arg2;
	x->arg3 = arg3;
	x->arg4 = arg4;
	x->arg5 = arg5;
	x->timestamp = XPR_TIMESTAMP;
	x->cpuinfo = log_level;

	if (ksp->log_port)
		kern_serv_callout(kspp, kern_serv_send_log, (void *)ksp);
}

/*
 * Send the log back to the user.  Have to copy the log into our address
 * space.
 */
static void kern_serv_send_log(void *arg)
{
	kern_server_t ksp = (kern_server_t)arg;
	port_name_t log_port;
	int s;
	vm_size_t size =
		round_page((char *)ksp->log.ptr - (char *)ksp->log.base);
	pointer_t ldata;
	unsigned int ldata_count;
	kern_return_t r;

	s = splhigh();
	simple_lock(&ksp->slock);

	log_port = ksp->log_port;
	ksp->log_port = PORT_NULL;

	simple_unlock(&ksp->slock);
	splx(s);
	if (log_port == PORT_NULL)
		return;

	ASSERT((void *)ksp->log.base == (void *)round_page(ksp->log.base));
	r = vm_read(ksp->kernel_port, (vm_address_t)ksp->log.base,
		size, &ldata, &ldata_count);
	ASSERT(r == KERN_SUCCESS);
	kern_serv_log_data(log_port, (log_entry_t *)ldata,
		ksp->log.ptr - ksp->log.base);

	port_deallocate(ksp->task_port, log_port);
	vm_deallocate(ksp->task_port, ldata, size);

	s = splhigh();
	simple_lock(&ksp->slock);

	ksp->log.ptr = ksp->log.base;

	simple_unlock(&ksp->slock);
	splx(s);

}

/*
 * Ensure that the number of entries is rounded up to a page boundary.
 */
static void kern_serv_log_init (	// initialize server's log buffer
	log_t	*log,			// uninitialized log struct
	int	num_entries)		// size of log
{
	vm_size_t size = round_page(sizeof(log_entry_t)*num_entries);
	num_entries = size / sizeof(log_entry_t);

	log->base = (log_entry_t *)kalloc(size);

	log->last = log->base + num_entries;
	log->ptr = log->base;
#if	DIAGNOSTIC
	printf("kern_serv_log_init: log 0x%x log.last 0x%x, log.base 0x%x\n",
		log, log->last, log->base);
#endif 
}

static void kern_serv_log_free (log_t *log) // deallocate log structure
{
	kfree(log->base, round_page((char *)log->last - (char *)log->base));
	log->base = log->last = log->ptr = 0;
}

kern_return_t kern_serv_callout (
	kern_server_t	*kspp,
	void		(*proc)(void *arg),
	void *		arg)
{
	kern_server_t ksp = *kspp;
	struct msg_send_entry *msep;
	int s;

	/*
	 * Just call this guy directly if the conditions are right.
	 */
	if (
	    curipl() == 0
	    && task_self() == ksp->task_port) {
		(*proc)(arg);
		return KERN_SUCCESS;
	}

	s = splhigh();
	simple_lock(&ksp->slock);

	if (queue_empty(&ksp->msg_callout_fq)) {
#if	DIAGNOSTIC
		printf("kern_serv_callout: no free messages");
#endif
		simple_unlock(&ksp->slock);
		splx(s);
		return KERN_RESOURCE_SHORTAGE;
	}

	queue_remove_first(&ksp->msg_callout_fq, msep, struct msg_send_entry *,
		link);
	ks_log(("kern_serv_send: (queue) proc 0x%x\n", proc));

	msep->func = proc;
	msep->arg = arg;
	queue_enter(&ksp->msg_callout_q, msep, struct msg_send_entry *, link);

	simple_unlock(&ksp->slock);
	splx(s);

	/*
	 * Interrupt the server thread to that it'll look at this sucker.
	 */
	thread_call_func(
		(thread_call_func_t)kern_serv_interrupt_server,
		(thread_call_spec_t)ksp->server_thread, TRUE);

	return KERN_SUCCESS;
}

static void kern_serv_interrupt_server(thread_t thread)
{
	/*
	 * Sending the guy a message sounds like the best way to ensure
	 * that he see's that there's something to do.
	 */
	send_notification(thread->task, NOTIFY_MSG_ACCEPTED, PORT_NULL);
}

/*
 * Structure access functions.
 */
port_t kern_serv_local_port(kern_server_t *ksp)
{
	return (*ksp)->local_port;
}

port_t kern_serv_bootstrap_port(kern_server_t *ksp)
{
	return (*ksp)->bootstrap_port;
}

port_t kern_serv_notify_port(kern_server_t *ksp)
{
	return (*ksp)->notify_port;
}

port_set_name_t kern_serv_port_set(kern_server_t *ksp)
{
	return (*ksp)->port_set;
}

/* Get the kernel task port. */
port_t kern_serv_kernel_task_port(void)
{
	extern task_t kernel_task;	// global kernel variable.
	port_t my_kernel_port;

	task_reference(kernel_task);
	my_kernel_port = convert_task_to_port(kernel_task);
	if (my_kernel_port == PORT_NULL)
		return PORT_NULL;

	object_copyout(current_task(), my_kernel_port, MSG_TYPE_PORT,
		&my_kernel_port);
	return my_kernel_port;
}