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