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