1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1991,1990 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
49 *  School of Computer Science
50 *  Carnegie Mellon University
51 *  Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58
59#include <mach/boolean.h>
60#include <mach/port.h>
61#include <mach/mig.h>
62#include <mach/mig_errors.h>
63#include <mach/mach_types.h>
64#include <mach/mach_traps.h>
65
66#include <kern/ipc_tt.h>
67#include <kern/ipc_mig.h>
68#include <kern/kalloc.h>
69#include <kern/task.h>
70#include <kern/thread.h>
71#include <kern/ipc_kobject.h>
72#include <kern/misc_protos.h>
73
74#include <ipc/port.h>
75#include <ipc/ipc_kmsg.h>
76#include <ipc/ipc_entry.h>
77#include <ipc/ipc_object.h>
78#include <ipc/ipc_mqueue.h>
79#include <ipc/ipc_space.h>
80#include <ipc/ipc_port.h>
81#include <ipc/ipc_pset.h>
82#include <ipc/ipc_notify.h>
83#include <vm/vm_map.h>
84
85#include <libkern/OSAtomic.h>
86
87/*
88 *	Routine:	mach_msg_send_from_kernel
89 *	Purpose:
90 *		Send a message from the kernel.
91 *
92 *		This is used by the client side of KernelUser interfaces
93 *		to implement SimpleRoutines.  Currently, this includes
94 *		memory_object messages.
95 *	Conditions:
96 *		Nothing locked.
97 *	Returns:
98 *		MACH_MSG_SUCCESS	Sent the message.
99 *		MACH_SEND_INVALID_DEST	Bad destination port.
100 *		MACH_MSG_SEND_NO_BUFFER Destination port had inuse fixed bufer
101 *		                        or destination is above kernel limit
102 */
103
104#if IKM_SUPPORT_LEGACY
105
106#undef mach_msg_send_from_kernel
107mach_msg_return_t mach_msg_send_from_kernel(
108	mach_msg_header_t	*msg,
109	mach_msg_size_t		send_size);
110
111mach_msg_return_t
112mach_msg_send_from_kernel(
113	mach_msg_header_t	*msg,
114	mach_msg_size_t		send_size)
115{
116	ipc_kmsg_t kmsg;
117	mach_msg_return_t mr;
118
119	mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
120	if (mr != MACH_MSG_SUCCESS)
121		return mr;
122
123	mr = ipc_kmsg_copyin_from_kernel_legacy(kmsg);
124	if (mr != MACH_MSG_SUCCESS) {
125		ipc_kmsg_free(kmsg);
126		return mr;
127	}
128
129	mr = ipc_kmsg_send_always(kmsg);
130	if (mr != MACH_MSG_SUCCESS) {
131		ipc_kmsg_destroy(kmsg);
132	}
133
134	return mr;
135}
136
137#endif /* IKM_SUPPORT_LEGACY */
138
139mach_msg_return_t
140mach_msg_send_from_kernel_proper(
141	mach_msg_header_t	*msg,
142	mach_msg_size_t		send_size)
143{
144	ipc_kmsg_t kmsg;
145	mach_msg_return_t mr;
146
147	mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
148	if (mr != MACH_MSG_SUCCESS)
149		return mr;
150
151	mr = ipc_kmsg_copyin_from_kernel(kmsg);
152	if (mr != MACH_MSG_SUCCESS) {
153		ipc_kmsg_free(kmsg);
154		return mr;
155	}
156
157	mr = ipc_kmsg_send_always(kmsg);
158	if (mr != MACH_MSG_SUCCESS) {
159		ipc_kmsg_destroy(kmsg);
160	}
161
162	return mr;
163}
164
165#if IKM_SUPPORT_LEGACY
166
167mach_msg_return_t
168mach_msg_send_from_kernel_with_options(
169	mach_msg_header_t	*msg,
170	mach_msg_size_t		send_size,
171	mach_msg_option_t	option,
172	mach_msg_timeout_t	timeout_val)
173{
174	ipc_kmsg_t kmsg;
175	mach_msg_return_t mr;
176
177	mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
178	if (mr != MACH_MSG_SUCCESS)
179		return mr;
180
181	mr = ipc_kmsg_copyin_from_kernel_legacy(kmsg);
182	if (mr != MACH_MSG_SUCCESS) {
183		ipc_kmsg_free(kmsg);
184		return mr;
185	}
186
187	mr = ipc_kmsg_send(kmsg, option, timeout_val);
188	if (mr != MACH_MSG_SUCCESS) {
189		ipc_kmsg_destroy(kmsg);
190	}
191
192	return mr;
193}
194
195#endif /* IKM_SUPPORT_LEGACY */
196
197/*
198 *	Routine:	mach_msg_rpc_from_kernel
199 *	Purpose:
200 *		Send a message from the kernel and receive a reply.
201 *		Uses ith_rpc_reply for the reply port.
202 *
203 *		This is used by the client side of KernelUser interfaces
204 *		to implement Routines.
205 *	Conditions:
206 *		Nothing locked.
207 *	Returns:
208 *		MACH_MSG_SUCCESS	Sent the message.
209 *		MACH_RCV_PORT_DIED	The reply port was deallocated.
210 */
211
212mach_msg_return_t mach_msg_rpc_from_kernel_body(mach_msg_header_t *msg,
213        mach_msg_size_t send_size, mach_msg_size_t rcv_size, boolean_t legacy);
214
215#if IKM_SUPPORT_LEGACY
216
217#undef mach_msg_rpc_from_kernel
218mach_msg_return_t
219mach_msg_rpc_from_kernel(
220	mach_msg_header_t	*msg,
221	mach_msg_size_t		send_size,
222	mach_msg_size_t		rcv_size);
223
224mach_msg_return_t
225mach_msg_rpc_from_kernel(
226	mach_msg_header_t	*msg,
227	mach_msg_size_t		send_size,
228	mach_msg_size_t		rcv_size)
229{
230    return mach_msg_rpc_from_kernel_body(msg, send_size, rcv_size, TRUE);
231}
232
233#endif /* IKM_SUPPORT_LEGACY */
234
235mach_msg_return_t
236mach_msg_rpc_from_kernel_proper(
237	mach_msg_header_t	*msg,
238	mach_msg_size_t		send_size,
239	mach_msg_size_t		rcv_size)
240{
241    return mach_msg_rpc_from_kernel_body(msg, send_size, rcv_size, FALSE);
242}
243
244mach_msg_return_t
245mach_msg_rpc_from_kernel_body(
246	mach_msg_header_t	*msg,
247	mach_msg_size_t		send_size,
248	mach_msg_size_t		rcv_size,
249#if !IKM_SUPPORT_LEGACY
250	__unused
251#endif
252    boolean_t           legacy)
253{
254	thread_t self = current_thread();
255	ipc_port_t reply;
256	ipc_kmsg_t kmsg;
257	mach_port_seqno_t seqno;
258	mach_msg_return_t mr;
259
260	assert(msg->msgh_local_port == MACH_PORT_NULL);
261
262	mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
263	if (mr != MACH_MSG_SUCCESS)
264		return mr;
265
266	reply = self->ith_rpc_reply;
267	if (reply == IP_NULL) {
268		reply = ipc_port_alloc_reply();
269		if ((reply == IP_NULL) ||
270		    (self->ith_rpc_reply != IP_NULL))
271			panic("mach_msg_rpc_from_kernel");
272		self->ith_rpc_reply = reply;
273	}
274
275	/* insert send-once right for the reply port */
276	kmsg->ikm_header->msgh_local_port = reply;
277	kmsg->ikm_header->msgh_bits |=
278		MACH_MSGH_BITS(0, MACH_MSG_TYPE_MAKE_SEND_ONCE);
279
280	ip_reference(reply);
281
282#if IKM_SUPPORT_LEGACY
283    if(legacy)
284        mr = ipc_kmsg_copyin_from_kernel_legacy(kmsg);
285    else
286        mr = ipc_kmsg_copyin_from_kernel(kmsg);
287#else
288    mr = ipc_kmsg_copyin_from_kernel(kmsg);
289#endif
290    if (mr != MACH_MSG_SUCCESS) {
291	    ipc_kmsg_free(kmsg);
292	    return mr;
293    }
294	mr = ipc_kmsg_send_always(kmsg);
295	if (mr != MACH_MSG_SUCCESS) {
296		ipc_kmsg_destroy(kmsg);
297		return mr;
298	}
299
300	for (;;) {
301		ipc_mqueue_t mqueue;
302
303		ip_lock(reply);
304		if ( !ip_active(reply)) {
305			ip_unlock(reply);
306			ip_release(reply);
307			return MACH_RCV_PORT_DIED;
308		}
309		if (!self->active) {
310			ip_unlock(reply);
311			ip_release(reply);
312			return MACH_RCV_INTERRUPTED;
313		}
314
315		assert(reply->ip_pset_count == 0);
316		mqueue = &reply->ip_messages;
317		ip_unlock(reply);
318
319		self->ith_continuation = (void (*)(mach_msg_return_t))0;
320
321		ipc_mqueue_receive(mqueue,
322				   MACH_MSG_OPTION_NONE,
323				   MACH_MSG_SIZE_MAX,
324				   MACH_MSG_TIMEOUT_NONE,
325				   THREAD_INTERRUPTIBLE);
326
327		mr = self->ith_state;
328		kmsg = self->ith_kmsg;
329		seqno = self->ith_seqno;
330
331		if (mr == MACH_MSG_SUCCESS)
332		  {
333			break;
334		  }
335
336		assert(mr == MACH_RCV_INTERRUPTED);
337
338		if (self->handlers) {
339			ip_release(reply);
340			return(mr);
341		}
342	}
343	ip_release(reply);
344
345	/*
346	 * Check to see how much of the message/trailer can be received.
347	 * We chose the maximum trailer that will fit, since we don't
348	 * have options telling us which trailer elements the caller needed.
349	 */
350	if (rcv_size >= kmsg->ikm_header->msgh_size) {
351		mach_msg_format_0_trailer_t *trailer =  (mach_msg_format_0_trailer_t *)
352			((vm_offset_t)kmsg->ikm_header + kmsg->ikm_header->msgh_size);
353
354		if (rcv_size >= kmsg->ikm_header->msgh_size + MAX_TRAILER_SIZE) {
355			/* Enough room for a maximum trailer */
356			trailer->msgh_trailer_size = MAX_TRAILER_SIZE;
357		}
358		else if (rcv_size < kmsg->ikm_header->msgh_size +
359			   trailer->msgh_trailer_size) {
360			/* no room for even the basic (default) trailer */
361			trailer->msgh_trailer_size = 0;
362		}
363		assert(trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0);
364		rcv_size = kmsg->ikm_header->msgh_size + trailer->msgh_trailer_size;
365		mr = MACH_MSG_SUCCESS;
366	} else {
367		mr = MACH_RCV_TOO_LARGE;
368	}
369
370
371	/*
372	 *	We want to preserve rights and memory in reply!
373	 *	We don't have to put them anywhere; just leave them
374	 *	as they are.
375	 */
376#if IKM_SUPPORT_LEGACY
377    if(legacy)
378        ipc_kmsg_copyout_to_kernel_legacy(kmsg, ipc_space_reply);
379    else
380        ipc_kmsg_copyout_to_kernel(kmsg, ipc_space_reply);
381#else
382    ipc_kmsg_copyout_to_kernel(kmsg, ipc_space_reply);
383#endif
384	ipc_kmsg_put_to_kernel(msg, kmsg, rcv_size);
385	return mr;
386}
387
388
389/************** These Calls are set up for kernel-loaded tasks/threads **************/
390
391/*
392 *	Routine:	mach_msg_overwrite
393 *	Purpose:
394 *		Like mach_msg_overwrite_trap except that message buffers
395 *		live in kernel space.  Doesn't handle any options.
396 *
397 *		This is used by in-kernel server threads to make
398 *		kernel calls, to receive request messages, and
399 *		to send reply messages.
400 *	Conditions:
401 *		Nothing locked.
402 *	Returns:
403 */
404
405mach_msg_return_t
406mach_msg_overwrite(
407	mach_msg_header_t		*msg,
408	mach_msg_option_t		option,
409	mach_msg_size_t		send_size,
410	mach_msg_size_t		rcv_size,
411	mach_port_name_t		rcv_name,
412	__unused mach_msg_timeout_t	msg_timeout,
413	__unused mach_port_name_t	notify,
414	__unused mach_msg_header_t	*rcv_msg,
415       __unused mach_msg_size_t	rcv_msg_size)
416{
417	ipc_space_t space = current_space();
418	vm_map_t map = current_map();
419	ipc_kmsg_t kmsg;
420	mach_port_seqno_t seqno;
421	mach_msg_return_t mr;
422	mach_msg_trailer_size_t trailer_size;
423
424	if (option & MACH_SEND_MSG) {
425		mach_msg_size_t	msg_and_trailer_size;
426		mach_msg_max_trailer_t	*max_trailer;
427
428		if ((send_size < sizeof(mach_msg_header_t)) || (send_size & 3))
429			return MACH_SEND_MSG_TOO_SMALL;
430
431		if (send_size > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE)
432			return MACH_SEND_TOO_LARGE;
433
434		msg_and_trailer_size = send_size + MAX_TRAILER_SIZE;
435		kmsg = ipc_kmsg_alloc(msg_and_trailer_size);
436
437		if (kmsg == IKM_NULL)
438			return MACH_SEND_NO_BUFFER;
439
440		(void) memcpy((void *) kmsg->ikm_header, (const void *) msg, send_size);
441
442		kmsg->ikm_header->msgh_size = send_size;
443
444		/*
445		 * Reserve for the trailer the largest space (MAX_TRAILER_SIZE)
446		 * However, the internal size field of the trailer (msgh_trailer_size)
447		 * is initialized to the minimum (sizeof(mach_msg_trailer_t)), to optimize
448		 * the cases where no implicit data is requested.
449		 */
450		max_trailer = (mach_msg_max_trailer_t *) ((vm_offset_t)kmsg->ikm_header + send_size);
451		max_trailer->msgh_sender = current_thread()->task->sec_token;
452		max_trailer->msgh_audit = current_thread()->task->audit_token;
453		max_trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
454		max_trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;
455
456		mr = ipc_kmsg_copyin(kmsg, space, map, FALSE);
457		if (mr != MACH_MSG_SUCCESS) {
458			ipc_kmsg_free(kmsg);
459			return mr;
460		}
461
462		do
463			mr = ipc_kmsg_send(kmsg, MACH_MSG_OPTION_NONE,
464					     MACH_MSG_TIMEOUT_NONE);
465		while (mr == MACH_SEND_INTERRUPTED);
466		assert(mr == MACH_MSG_SUCCESS);
467	}
468
469	if (option & MACH_RCV_MSG) {
470		thread_t self = current_thread();
471
472		do {
473			ipc_object_t object;
474			ipc_mqueue_t mqueue;
475
476			mr = ipc_mqueue_copyin(space, rcv_name,
477					       &mqueue, &object);
478			if (mr != MACH_MSG_SUCCESS)
479				return mr;
480			/* hold ref for object */
481
482			self->ith_continuation = (void (*)(mach_msg_return_t))0;
483			ipc_mqueue_receive(mqueue,
484					   MACH_MSG_OPTION_NONE,
485					   MACH_MSG_SIZE_MAX,
486					   MACH_MSG_TIMEOUT_NONE,
487					   THREAD_ABORTSAFE);
488			mr = self->ith_state;
489			kmsg = self->ith_kmsg;
490			seqno = self->ith_seqno;
491
492			io_release(object);
493
494		} while (mr == MACH_RCV_INTERRUPTED);
495		if (mr != MACH_MSG_SUCCESS)
496			return mr;
497
498
499		trailer_size = ipc_kmsg_add_trailer(kmsg, space, option, current_thread(), seqno, TRUE,
500				kmsg->ikm_header->msgh_remote_port->ip_context);
501
502		if (rcv_size < (kmsg->ikm_header->msgh_size + trailer_size)) {
503			ipc_kmsg_copyout_dest(kmsg, space);
504			(void) memcpy((void *) msg, (const void *) kmsg->ikm_header, sizeof *msg);
505			ipc_kmsg_free(kmsg);
506			return MACH_RCV_TOO_LARGE;
507		}
508
509		mr = ipc_kmsg_copyout(kmsg, space, map, MACH_MSG_BODY_NULL);
510		if (mr != MACH_MSG_SUCCESS) {
511			if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
512				ipc_kmsg_put_to_kernel(msg, kmsg,
513						kmsg->ikm_header->msgh_size + trailer_size);
514			} else {
515				ipc_kmsg_copyout_dest(kmsg, space);
516				(void) memcpy((void *) msg, (const void *) kmsg->ikm_header, sizeof *msg);
517				ipc_kmsg_free(kmsg);
518			}
519
520			return mr;
521		}
522
523		(void) memcpy((void *) msg, (const void *) kmsg->ikm_header,
524			      kmsg->ikm_header->msgh_size + trailer_size);
525		ipc_kmsg_free(kmsg);
526	}
527
528	return MACH_MSG_SUCCESS;
529}
530
531/*
532 *	Routine:	mig_get_reply_port
533 *	Purpose:
534 *		Called by client side interfaces living in the kernel
535 *		to get a reply port.
536 */
537mach_port_t
538mig_get_reply_port(void)
539{
540	return (MACH_PORT_NULL);
541}
542
543/*
544 *	Routine:	mig_dealloc_reply_port
545 *	Purpose:
546 *		Called by client side interfaces to get rid of a reply port.
547 */
548
549void
550mig_dealloc_reply_port(
551	__unused mach_port_t reply_port)
552{
553}
554
555/*
556 *	Routine:	mig_put_reply_port
557 *	Purpose:
558 *		Called by client side interfaces after each RPC to
559 *		let the client recycle the reply port if it wishes.
560 */
561void
562mig_put_reply_port(
563	__unused mach_port_t reply_port)
564{
565}
566
567/*
568 * mig_strncpy.c - by Joshua Block
569 *
570 * mig_strncp -- Bounded string copy.  Does what the library routine strncpy
571 * OUGHT to do:  Copies the (null terminated) string in src into dest, a
572 * buffer of length len.  Assures that the copy is still null terminated
573 * and doesn't overflow the buffer, truncating the copy if necessary.
574 *
575 * Parameters:
576 *
577 *     dest - Pointer to destination buffer.
578 *
579 *     src - Pointer to source string.
580 *
581 *     len - Length of destination buffer.
582 */
583int
584mig_strncpy(
585	char		*dest,
586	const char	*src,
587	int		len)
588{
589    int i = 0;
590
591    if (len > 0)
592	if (dest != NULL) {
593	    if (src != NULL)
594		   for (i=1; i<len; i++)
595			if (! (*dest++ = *src++))
596			    return i;
597	        *dest = '\0';
598	}
599    return i;
600}
601
602char *
603mig_user_allocate(
604	vm_size_t	size)
605{
606	return (char *)kalloc(size);
607}
608
609void
610mig_user_deallocate(
611	char		*data,
612	vm_size_t	size)
613{
614	kfree(data, size);
615}
616
617/*
618 *	Routine:	mig_object_init
619 *	Purpose:
620 *		Initialize the base class portion of a MIG object.  We
621 *		will lazy init the port, so just clear it for now.
622 */
623kern_return_t
624mig_object_init(
625	mig_object_t		mig_object,
626	const IMIGObject	*interface)
627{
628	if (mig_object == MIG_OBJECT_NULL)
629		return KERN_INVALID_ARGUMENT;
630	mig_object->pVtbl = (const IMIGObjectVtbl *)interface;
631	mig_object->port = MACH_PORT_NULL;
632	return KERN_SUCCESS;
633}
634
635/*
636 *	Routine:	mig_object_destroy
637 *	Purpose:
638 *		The object is being freed.  This call lets us clean
639 *		up any state we have have built up over the object's
640 *		lifetime.
641 *	Conditions:
642 *		Since notifications and the port hold references on
643 *		on the object, neither can exist when this is called.
644 *		This is a good place to assert() that condition.
645 */
646void
647mig_object_destroy(
648	__assert_only mig_object_t	mig_object)
649{
650	assert(mig_object->port == MACH_PORT_NULL);
651	return;
652}
653
654/*
655 *	Routine:	mig_object_reference
656 *	Purpose:
657 *		Pure virtual helper to invoke the MIG object's AddRef
658 *		method.
659 *	Conditions:
660 *		MIG object port may be locked.
661 */
662void
663mig_object_reference(
664	mig_object_t	mig_object)
665{
666	assert(mig_object != MIG_OBJECT_NULL);
667	mig_object->pVtbl->AddRef((IMIGObject *)mig_object);
668}
669
670/*
671 *	Routine:	mig_object_deallocate
672 *	Purpose:
673 *		Pure virtual helper to invoke the MIG object's Release
674 *		method.
675 *	Conditions:
676 *		Nothing locked.
677 */
678void
679mig_object_deallocate(
680	mig_object_t	mig_object)
681{
682	assert(mig_object != MIG_OBJECT_NULL);
683	mig_object->pVtbl->Release((IMIGObject *)mig_object);
684}
685
686/*
687 *	Routine:	convert_mig_object_to_port [interface]
688 *	Purpose:
689 *		Base implementation of MIG outtrans routine to convert from
690 *		a mig object reference to a new send right on the object's
691 *		port.  The object reference is consumed.
692 *	Returns:
693 *		IP_NULL - Null MIG object supplied
694 *		Otherwise, a newly made send right for the port
695 *	Conditions:
696 *		Nothing locked.
697 */
698ipc_port_t
699convert_mig_object_to_port(
700	mig_object_t	mig_object)
701{
702	ipc_port_t	port;
703	boolean_t	deallocate = TRUE;
704
705	if (mig_object == MIG_OBJECT_NULL)
706		return IP_NULL;
707
708	port = mig_object->port;
709	while ((port == IP_NULL) ||
710	       ((port = ipc_port_make_send(port)) == IP_NULL)) {
711		ipc_port_t	previous;
712
713		/*
714		 * Either the port was never set up, or it was just
715		 * deallocated out from under us by the no-senders
716		 * processing.  In either case, we must:
717		 *	Attempt to make one
718		 * 	Arrange for no senders
719		 *	Try to atomically register it with the object
720		 *		Destroy it if we are raced.
721		 */
722		port = ipc_port_alloc_kernel();
723		ip_lock(port);
724		ipc_kobject_set_atomically(port,
725					   (ipc_kobject_t) mig_object,
726					   IKOT_MIG);
727
728		/* make a sonce right for the notification */
729		port->ip_sorights++;
730		ip_reference(port);
731
732		ipc_port_nsrequest(port, 1, port, &previous);
733		/* port unlocked */
734
735		assert(previous == IP_NULL);
736
737		if (OSCompareAndSwapPtr((void *)IP_NULL, (void *)port,
738											(void * volatile *)&mig_object->port)) {
739			deallocate = FALSE;
740		} else {
741			ipc_port_dealloc_kernel(port);
742			port = mig_object->port;
743		}
744	}
745
746	if (deallocate)
747		mig_object->pVtbl->Release((IMIGObject *)mig_object);
748
749	return (port);
750}
751
752
753/*
754 *	Routine:	convert_port_to_mig_object [interface]
755 *	Purpose:
756 *		Base implementation of MIG intrans routine to convert from
757 *		an incoming port reference to a new reference on the
758 *		underlying object. A new reference must be created, because
759 *		the port's reference could go away asynchronously.
760 *	Returns:
761 *		NULL - Not an active MIG object port or iid not supported
762 *		Otherwise, a reference to the underlying MIG interface
763 *	Conditions:
764 *		Nothing locked.
765 */
766mig_object_t
767convert_port_to_mig_object(
768	ipc_port_t	port,
769	const MIGIID	*iid)
770{
771	mig_object_t	mig_object;
772	void 		*ppv;
773
774	if (!IP_VALID(port))
775		return NULL;
776
777	ip_lock(port);
778	if (!ip_active(port) || (ip_kotype(port) != IKOT_MIG)) {
779		ip_unlock(port);
780		return NULL;
781	}
782
783	/*
784	 * Our port points to some MIG object interface.  Now
785	 * query it to get a reference to the desired interface.
786	 */
787	ppv = NULL;
788	mig_object = (mig_object_t)port->ip_kobject;
789	mig_object->pVtbl->QueryInterface((IMIGObject *)mig_object, iid, &ppv);
790	ip_unlock(port);
791	return (mig_object_t)ppv;
792}
793
794/*
795 *	Routine:	mig_object_no_senders [interface]
796 *	Purpose:
797 *		Base implementation of a no-senders notification handler
798 *		for MIG objects. If there truly are no more senders, must
799 *		destroy the port and drop its reference on the object.
800 *	Returns:
801 *		TRUE  - port deallocate and reference dropped
802 *		FALSE - more senders arrived, re-registered for notification
803 *	Conditions:
804 *		Nothing locked.
805 */
806
807boolean_t
808mig_object_no_senders(
809	ipc_port_t		port,
810	mach_port_mscount_t	mscount)
811{
812	mig_object_t		mig_object;
813
814	ip_lock(port);
815	if (port->ip_mscount > mscount) {
816		ipc_port_t 	previous;
817
818		/*
819		 * Somebody created new send rights while the
820		 * notification was in-flight.  Just create a
821		 * new send-once right and re-register with
822		 * the new (higher) mscount threshold.
823		 */
824		/* make a sonce right for the notification */
825		port->ip_sorights++;
826		ip_reference(port);
827		ipc_port_nsrequest(port, mscount, port, &previous);
828		/* port unlocked */
829
830		assert(previous == IP_NULL);
831		return (FALSE);
832	}
833
834	/*
835	 * Clear the port pointer while we have it locked.
836	 */
837	mig_object = (mig_object_t)port->ip_kobject;
838	mig_object->port = IP_NULL;
839
840	/*
841	 * Bring the sequence number and mscount in
842	 * line with ipc_port_destroy assertion.
843	 */
844	port->ip_mscount = 0;
845	port->ip_messages.imq_seqno = 0;
846	ipc_port_destroy(port); /* releases lock */
847
848	/*
849	 * Release the port's reference on the object.
850	 */
851	mig_object->pVtbl->Release((IMIGObject *)mig_object);
852	return (TRUE);
853}
854
855/*
856 * Kernel implementation of the notification chain for MIG object
857 * is kept separate from the actual objects, since there are expected
858 * to be much fewer of them than actual objects.
859 *
860 * The implementation of this part of MIG objects is coming
861 * "Real Soon Now"(TM).
862 */
863