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