2 * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
32 * Implementation of fast local ipc (flipc).
36 #include <mach/mach_types.h>
37 #include <mach/boolean.h>
38 #include <mach/kern_return.h>
40 #include <kern/kern_types.h>
41 #include <kern/assert.h>
42 #include <kern/host.h>
43 #include <kern/kalloc.h>
44 #include <kern/mach_node.h>
47 #include <ipc/ipc_types.h>
48 #include <ipc/ipc_init.h>
49 #include <ipc/ipc_kmsg.h>
50 #include <ipc/ipc_port.h>
51 #include <ipc/ipc_pset.h>
52 #include <ipc/ipc_table.h>
53 #include <ipc/ipc_entry.h>
54 #include <ipc/flipc.h>
59 /*** FLIPC Internal Implementation (private to flipc.c) ***/
62 zone_t flipc_port_zone
;
65 /* Get the mnl_name associated with local ipc_port <lport>.
66 * Returns MNL_NAME_NULL if <lport> is invalid or not a flipc port.
68 static inline mnl_name_t
mnl_name_from_port(ipc_port_t lport
)
70 mnl_name_t name
= MNL_NAME_NULL
;
72 if (IP_VALID(lport
)) {
73 flipc_port_t fport
= lport
->ip_messages
.data
.port
.fport
;
74 if (FPORT_VALID(fport
))
75 name
= fport
->obj
.name
;
81 /* Lookup the ipc_port associated with mnl_name <name>.
82 * Returns IP_NULL if <name> is invalid or not a known mnl object.
84 static inline ipc_port_t
mnl_name_to_port(mnl_name_t name
)
86 ipc_port_t lport
= IP_NULL
;
88 if (MNL_NAME_VALID(name
)) {
89 flipc_port_t fport
= (flipc_port_t
)mnl_obj_lookup(name
);
90 if (FPORT_VALID(fport
))
97 /* flipc_port_create() is called to convert a regular mach port into a
98 * flipc port (i.e., the port has one or more rights off-node).
99 * <lport> must be locked on entry and is not unlocked on return.
102 flipc_port_create(ipc_port_t lport
, mach_node_t node
, mnl_name_t name
)
104 /* Ensure parameters are valid and not already linked */
105 assert(IP_VALID(lport
));
106 assert(MACH_NODE_VALID(node
));
107 assert(MNL_NAME_VALID(name
));
108 assert(!FPORT_VALID(lport
->ip_messages
.imq_fport
));
110 /* Allocate and initialize a flipc port */
111 flipc_port_t fport
= (flipc_port_t
) zalloc(flipc_port_zone
);
112 if (!FPORT_VALID(fport
))
113 return KERN_RESOURCE_SHORTAGE
;
114 bzero(fport
, sizeof(struct flipc_port
));
115 fport
->obj
.name
= name
;
116 fport
->hostnode
= node
;
117 if (node
== localnode
)
118 fport
->state
= FPORT_STATE_PRINCIPAL
;
120 fport
->state
= FPORT_STATE_PROXY
;
122 /* Link co-structures (lport is locked) */
123 fport
->lport
= lport
;
124 lport
->ip_messages
.imq_fport
= fport
;
126 /* Add fport to the name hash table; revert link if insert fails */
127 kern_return_t kr
= mnl_obj_insert((mnl_obj_t
)fport
);
128 if (kr
!= KERN_SUCCESS
) {
129 lport
->ip_messages
.imq_fport
= FPORT_NULL
;
130 fport
->lport
= IP_NULL
;
131 zfree(flipc_port_zone
, fport
);
138 /* flipc_port_destroy() is called to convert a flipc port back to a
139 * local-only ipc port (i.e., the port has no remaining off-node rights).
140 * This will dispose of any undelivered flipc messages, generating NAKs if
141 * needed. <lport> must be locked on entry and is not unlocked on return.
144 flipc_port_destroy(ipc_port_t lport
)
146 /* Ensure parameter is valid, and linked to an fport with a valid name */
147 assert(IP_VALID(lport
));
148 ipc_mqueue_t port_mq
= &lport
->ip_messages
;
149 flipc_port_t fport
= port_mq
->data
.port
.fport
;
150 assert(FPORT_VALID(fport
));
151 assert(MNL_NAME_VALID(fport
->obj
.name
));
153 /* Dispose of any undelivered messages */
154 int m
= port_mq
->data
.port
.msgcount
;
158 printf("flipc: destroying %p with %d undelivered msgs\n", lport
, m
);
161 /* Logic was lifted from ipc_mqueue_select_on_thread() */
163 kmsg
= ipc_kmsg_queue_first(&port_mq
->imq_messages
);
164 assert(kmsg
!= IKM_NULL
);
165 ipc_kmsg_rmqueue(&port_mq
->imq_messages
, kmsg
);
166 if (fport
->state
== FPORT_STATE_PRINCIPAL
)
167 flipc_msg_ack(kmsg
->ikm_node
, port_mq
, FALSE
);
168 ipc_mqueue_release_msgcount(port_mq
, NULL
);
169 port_mq
->imq_seqno
++;
173 /* Remove from name hash table, unlink co-structures, and free fport */
174 mnl_obj_remove(fport
->obj
.name
);
175 lport
->ip_messages
.data
.port
.fport
= FPORT_NULL
;
176 fport
->lport
= IP_NULL
;
177 zfree(flipc_port_zone
, fport
);
182 * Routine: flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)
184 * Compute the size of the buffer needed to hold the translated flipc
185 * message. All identifiers are converted to flipc_names which are 64b.
186 * If this node's pointers are a different size, we have to allow for
187 * expansion of the descriptors as appropriate.
191 * size of the message as it would be sent over the flipc link.
193 static mach_msg_size_t
flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg
)
195 mach_msg_size_t fsize
= kmsg
->ikm_header
->msgh_size
;
197 if (kmsg
->ikm_header
->msgh_bits
& MACH_MSGH_BITS_COMPLEX
)
198 PE_enter_debugger("flipc_msg_size_from_kmsg(): Complex messages not supported.");
204 /* Translate a kmsg into a flipc msg suitable to transmit over the mach node
205 * link. All in-line rights and objects are similarly processed. If the msg
206 * moves a receive right, then queued messages may need to be moved as a
207 * result, causing this function to ultimately be recursive.
209 static kern_return_t
mnl_msg_from_kmsg(ipc_kmsg_t kmsg
, mnl_msg_t
*fmsgp
)
211 if (kmsg
->ikm_header
->msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
212 printf("mnl_msg_from_kmsg(): Complex messages not supported.");
216 mach_msg_size_t fsize
= flipc_msg_size_from_kmsg(kmsg
);
218 mnl_msg_t fmsg
= mnl_msg_alloc(fsize
, 0);
220 if (fmsg
== MNL_MSG_NULL
)
221 return KERN_RESOURCE_SHORTAGE
;
223 /* Setup flipc message header */
224 fmsg
->sub
= MACH_NODE_SUB_FLIPC
;
225 fmsg
->cmd
= FLIPC_CMD_IPCMESSAGE
;
226 fmsg
->node_id
= localnode_id
; // Message is from us
227 fmsg
->qos
= 0; // not used
228 fmsg
->size
= fsize
; // Payload size (does NOT include mnl_msg header)
229 fmsg
->object
= kmsg
->ikm_header
->msgh_remote_port
->ip_messages
.data
.port
.fport
->obj
.name
;
231 /* Copy body of message */
232 bcopy((const void*)kmsg
->ikm_header
, (void*)MNL_MSG_PAYLOAD(fmsg
), fsize
);
234 // Convert port fields
235 mach_msg_header_t
*mmsg
= (mach_msg_header_t
*)MNL_MSG_PAYLOAD(fmsg
);
236 mmsg
->msgh_remote_port
= (mach_port_t
)fmsg
->object
;
237 mmsg
->msgh_local_port
= (mach_port_t
)
238 mnl_name_from_port(mmsg
->msgh_local_port
);
239 mmsg
->msgh_voucher_port
= (mach_port_name_t
)MNL_NAME_NULL
;
241 *fmsgp
= (mnl_msg_t
)fmsg
;
247 /* lifted from ipc_mig.c:mach_msg_send_from_kernel_proper() */
248 static mach_msg_return_t
249 mach_msg_send_from_remote_kernel(mach_msg_header_t
*msg
,
250 mach_msg_size_t send_size
,
254 mach_msg_return_t mr
;
256 mr
= ipc_kmsg_get_from_kernel(msg
, send_size
, &kmsg
);
257 if (mr
!= MACH_MSG_SUCCESS
)
260 mr
= ipc_kmsg_copyin_from_kernel(kmsg
);
261 if (mr
!= MACH_MSG_SUCCESS
) {
266 kmsg
->ikm_node
= node
; // node that needs to receive message ack
267 mr
= ipc_kmsg_send(kmsg
,
268 MACH_SEND_KERNEL_DEFAULT
,
269 MACH_MSG_TIMEOUT_NONE
);
270 if (mr
!= MACH_MSG_SUCCESS
) {
271 ipc_kmsg_destroy(kmsg
);
278 /* Translate a flipc msg <fmsg> into a kmsg and post it to the appropriate
279 * port. <node> is the node that originated the message, not necessarily the
280 * node we received it from. This will block if the receiving port is full.
282 static mach_msg_return_t
283 flipc_cmd_ipc(mnl_msg_t fmsg
,
285 uint32_t flags __unused
)
287 mach_msg_header_t
*mmsg
;
289 // Convert flipc message into mach message in place to avoid alloc/copy
290 mmsg
= (mach_msg_header_t
*)MNL_MSG_PAYLOAD(fmsg
);
291 mmsg
->msgh_size
= fmsg
->size
;
292 mmsg
->msgh_remote_port
= mnl_name_to_port(fmsg
->object
);
293 mmsg
->msgh_local_port
= mnl_name_to_port((mnl_name_t
)mmsg
->msgh_local_port
);
294 mmsg
->msgh_voucher_port
= (mach_port_name_t
)MACH_PORT_NULL
;
295 mmsg
->msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND
, 0);
296 // unchanged: msgh_id
298 return mach_msg_send_from_remote_kernel(mmsg
, fmsg
->size
, node
);
302 /* Called when an ACKMESSAGE packet is received. <name> indicates
303 * the flipc name of the port holding the messages to be acknowledged.
304 * <msg_count> indicates the number of messages being acked for this node:port.
307 flipc_cmd_ack(flipc_ack_msg_t fmsg
,
308 mach_node_t node __unused
,
309 uint32_t flags __unused
)
311 unsigned int msg_count
= fmsg
->msg_count
;
312 thread_t thread
= current_thread();
313 boolean_t kick
= FALSE
;
315 flipc_port_t fport
= (flipc_port_t
)mnl_obj_lookup(fmsg
->mnl
.object
);
317 ipc_port_t lport
= fport
->lport
;
320 ipc_mqueue_t lport_mq
= &lport
->ip_messages
;
323 assert(fport
->peek_count
>= msg_count
); // Can't ack what we haven't peeked!
325 while (msg_count
--) {
326 ipc_mqueue_select_on_thread(lport_mq
, IMQ_NULL
, 0, 0, thread
);
328 kick
|= ipc_kmsg_delayed_destroy(thread
->ith_kmsg
);
331 imq_unlock(lport_mq
);
335 ipc_kmsg_reap_delayed();
340 /*** FLIPC Node Managment Functions (called by mach node layer) ***/
343 /* The mach node layer calls flipc_init() once before it calls any other
344 * flipc entry points. Returns KERN_SUCCESS on success; otherwise flipc
345 * is not initialized and cannot be used.
350 /* Create zone for flipc ports.
351 * TODO: Pick a better max value than ipc_port_max>>4
353 flipc_port_zone
= zinit(sizeof(struct flipc_port
),
354 (ipc_port_max
>>4) * sizeof(struct flipc_port
),
355 sizeof(struct flipc_port
),
358 zone_change(flipc_port_zone
, Z_CALLERACCT
, FALSE
);
359 zone_change(flipc_port_zone
, Z_NOENCRYPT
, TRUE
);
364 /* flipc_node_prepare() is called by mach node layer when a remote node is
365 * registered by a link driver, or when the bootstrap port changes for the
366 * local node. This is the flipc layer's opportunity to initialize per-node
367 * flipc state, and to convert the node's bootstrap port into a flipc port.
368 * Note that the node is not yet in the mach node table.
369 * Returns KERN_SUCCESS on success; otherwise node is not prepared.
372 flipc_node_prepare(mach_node_t node
)
376 assert(MACH_NODE_VALID(node
));
377 ipc_port_t bs_port
= node
->bootstrap_port
;
378 assert(IP_VALID(bs_port
));
382 kr
= flipc_port_create(bs_port
,
384 MNL_NAME_BOOTSTRAP(node
->info
.node_id
));
391 /* flipc_node_retire() is called by mach node layer when a remote node is
392 * terminated by a link driver, or when the local node's bootstrap port
393 * becomes invalid. This is the flipc layer's opportunity to free per-node
394 * flipc state, and to revert the node's bootstrap port to a local ipc port.
395 * <node> must be locked by the caller.
396 * Returns KERN_SUCCESS on success.
399 flipc_node_retire(mach_node_t node
)
401 if (!MACH_NODE_VALID(node
))
402 return KERN_NODE_DOWN
;
404 ipc_port_t bs_port
= node
->bootstrap_port
;
405 if (IP_VALID(bs_port
)) {
407 flipc_port_destroy(bs_port
);
415 /*** FLIPC Message Functions (called by mach node layer) ***/
418 /* The node layer calls flipc_msg_to_remote_node() to fetch the next message
419 * for <node>. This function will block until a message is available or the
420 * node is terminated, in which case it returns MNL_MSG_NULL.
423 flipc_msg_to_remote_node(mach_node_t to_node
,
424 uint32_t flags __unused
)
426 mach_port_seqno_t msgoff
;
427 ipc_kmsg_t kmsg
= IKM_NULL
;
428 mnl_msg_t fmsg
= MNL_MSG_NULL
;
430 assert(to_node
!= localnode
);
431 assert(get_preemption_level()==0);
433 ipc_mqueue_t portset_mq
= &to_node
->proxy_port_set
->ips_messages
;
434 ipc_mqueue_t port_mq
= IMQ_NULL
;
436 while (!to_node
->dead
) {
437 /* Fetch next message from proxy port */
438 ipc_mqueue_receive(portset_mq
, MACH_PEEK_MSG
, 0, 0, THREAD_ABORTSAFE
);
440 thread_t thread
= current_thread();
441 if (thread
->ith_state
== MACH_PEEK_READY
) {
442 port_mq
= thread
->ith_peekq
;
443 thread
->ith_peekq
= IMQ_NULL
;
445 panic("Unexpected thread state %d after ipc_mqueue_receive()",
449 assert(get_preemption_level()==0);
452 flipc_port_t fport
= port_mq
->data
.port
.fport
;
454 if (FPORT_VALID(fport
)) {
455 msgoff
= port_mq
->data
.port
.fport
->peek_count
;
457 ipc_mqueue_peek_locked(port_mq
, &msgoff
, NULL
, NULL
, NULL
, &kmsg
);
458 if (kmsg
!= IKM_NULL
)
459 port_mq
->data
.port
.fport
->peek_count
++;
461 /* Clean up outstanding prepost on port_mq.
462 * This also unlocks port_mq.
464 ipc_mqueue_release_peek_ref(port_mq
);
465 assert(get_preemption_level()==0);
467 /* DANGER: The code below must be allowed to allocate so it can't
468 * run under the protection of the imq_lock, but that leaves mqueue
469 * open for business for a small window before we examine kmsg.
470 * This SHOULD be OK, since we are the only thread looking.
472 if (kmsg
!= IKM_NULL
)
473 mnl_msg_from_kmsg(kmsg
, (mnl_msg_t
*)&fmsg
);
475 /* Must be from the control_port, which is not a flipc port */
476 assert(!FPORT_VALID(port_mq
->data
.port
.fport
));
478 /* This is a simplified copy of ipc_mqueue_select_on_thread() */
479 kmsg
= ipc_kmsg_queue_first(&port_mq
->imq_messages
);
480 assert(kmsg
!= IKM_NULL
);
481 ipc_kmsg_rmqueue(&port_mq
->imq_messages
, kmsg
);
482 ipc_mqueue_release_msgcount(port_mq
, portset_mq
);
484 current_task()->messages_received
++;
485 ip_release(to_node
->control_port
); // Should derive ref from port_mq
487 /* We just pass the kmsg payload as the fmsg.
488 * flipc_msg_free() will notice and free the kmsg properly.
490 mach_msg_header_t
*hdr
= kmsg
->ikm_header
;
491 fmsg
= (mnl_msg_t
)(&hdr
[1]);
492 /* Stash kmsg pointer just before fmsg */
493 *(ipc_kmsg_t
*)((vm_offset_t
)fmsg
-sizeof(vm_offset_t
)) = kmsg
;
496 if (MNL_MSG_VALID(fmsg
))
499 assert(MNL_MSG_VALID(fmsg
));
504 /* The mach node layer calls this to deliver an incoming message. It is the
505 * responsibility of the caller to release the received message buffer after
509 flipc_msg_from_node(mach_node_t from_node __unused
,
513 /* Note that if flipc message forwarding is supported, the from_node arg
514 * may not match fmsg->node_id. The former is the node from which we
515 * received the message; the latter is the node that originated the
516 * message. We use the originating node, which is where the ack goes.
518 assert(msg
->sub
== MACH_NODE_SUB_FLIPC
);
519 mach_node_t node
= mach_node_for_id_locked(msg
->node_id
, FALSE
, FALSE
);
520 MACH_NODE_UNLOCK(node
);
523 case FLIPC_CMD_IPCMESSAGE
:
524 flipc_cmd_ipc(msg
, node
, flags
);
527 case FLIPC_CMD_ACKMESSAGE
:
528 case FLIPC_CMD_NAKMESSAGE
:
529 flipc_cmd_ack((flipc_ack_msg_t
)msg
, node
, flags
);
534 PE_enter_debugger("flipc_incoming(): Invalid command");
541 /* The node layer calls flipc_msg_free() to dispose of sent messages that
542 * originated in the FLIPC layer. This allows us to repurpose the payload
543 * of an ack or nak kmsg as a flipc message to avoid a copy - we detect
544 * such messages here and free them appropriately.
547 flipc_msg_free(mnl_msg_t msg
,
551 case FLIPC_CMD_ACKMESSAGE
: // Flipc msg is a kmsg in disguise...
552 case FLIPC_CMD_NAKMESSAGE
: // Convert back to kmsg for disposal
553 ipc_kmsg_free(*(ipc_kmsg_t
*)((vm_offset_t
)msg
-sizeof(vm_offset_t
)));
556 default: // Flipc msg is not a kmsg in disguise; dispose of normally
557 mnl_msg_free(msg
, flags
);
563 /*** FLIPC Message Functions (called by mach ipc subsystem) ***/
565 /* Ack's one message sent to <mqueue> from <node>. A new kmsg is allocated
566 * and filled in as an ack, then posted to the node's contol port. This will
567 * wake the link driver (if sleeping) and cause the ack to be included with
568 * normal IPC traffic.
570 * This function immediately returns if <fport> or <node> is invalid, so it
571 * is safe & quick to call speculatively.
573 * Called from mach ipc_mqueue.c when a flipc-originated message is consumed.
576 flipc_msg_ack(mach_node_t node
,
580 flipc_port_t fport
= mqueue
->imq_fport
;
582 assert(FPORT_VALID(fport
));
583 assert(MACH_NODE_VALID(node
));
585 mnl_name_t name
= MNL_NAME_NULL
;
586 mach_node_id_t nid
= HOST_LOCAL_NODE
;
587 ipc_port_t ack_port
= IP_NULL
;
589 ip_lock(fport
->lport
);
590 name
= fport
->obj
.name
;
591 ip_unlock(fport
->lport
);
593 if (!MNL_NAME_VALID(name
))
596 MACH_NODE_LOCK(node
);
598 nid
= node
->info
.node_id
;
599 ack_port
= node
->control_port
;
601 MACH_NODE_UNLOCK(node
);
603 if ( !IP_VALID(ack_port
) || !MACH_NODE_ID_VALID(nid
) )
606 /* We have a valid node id & obj name, and a port to send the ack to. */
607 ipc_kmsg_t kmsg
= ipc_kmsg_alloc(sizeof(struct flipc_ack_msg
) + MAX_TRAILER_SIZE
);
608 assert((unsigned long long)kmsg
>= 4ULL);//!= IKM_NULL);
609 mach_msg_header_t
*msg
= kmsg
->ikm_header
;
611 /* Fill in the mach_msg_header struct */
612 msg
->msgh_bits
= MACH_MSGH_BITS_SET(0, 0, 0, 0);
613 msg
->msgh_size
= sizeof(msg
);
614 msg
->msgh_remote_port
= ack_port
;
615 msg
->msgh_local_port
= MACH_PORT_NULL
;
616 msg
->msgh_voucher_port
= MACH_PORT_NULL
;
617 msg
->msgh_id
= FLIPC_CMD_ID
;
619 /* Fill in the flipc_ack_msg struct */
620 flipc_ack_msg_t fmsg
= (flipc_ack_msg_t
)(&msg
[1]);
621 fmsg
->resend_to
= HOST_LOCAL_NODE
;
622 fmsg
->msg_count
= 1; // Might want to coalesce acks to a node/name pair
624 /* Fill in the mnl_msg struct */
625 fmsg
->mnl
.sub
= MACH_NODE_SUB_FLIPC
;
626 fmsg
->mnl
.cmd
= delivered
? FLIPC_CMD_ACKMESSAGE
: FLIPC_CMD_NAKMESSAGE
;
627 fmsg
->mnl
.qos
= 0; // Doesn't do anything yet
629 fmsg
->mnl
.node_id
= nid
;
630 fmsg
->mnl
.object
= name
;
631 fmsg
->mnl
.options
= 0;
632 fmsg
->mnl
.size
= sizeof(struct flipc_ack_msg
) - sizeof(struct mnl_msg
);
635 mach_msg_return_t mmr
;
636 ipc_mqueue_t ack_mqueue
;
639 ack_mqueue
= &ack_port
->ip_messages
;
640 imq_lock(ack_mqueue
);
643 /* ipc_mqueue_send() unlocks ack_mqueue */
644 mmr
= ipc_mqueue_send(ack_mqueue
, kmsg
, 0, 0);
647 kr
= ipc_kmsg_send(kmsg
,
648 MACH_SEND_KERNEL_DEFAULT
,
649 MACH_MSG_TIMEOUT_NONE
);