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