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
69 mnl_name_from_port(ipc_port_t lport
)
71 mnl_name_t name
= MNL_NAME_NULL
;
73 if (IP_VALID(lport
)) {
74 flipc_port_t fport
= lport
->ip_messages
.data
.port
.fport
;
75 if (FPORT_VALID(fport
)) {
76 name
= fport
->obj
.name
;
83 /* Lookup the ipc_port associated with mnl_name <name>.
84 * Returns IP_NULL if <name> is invalid or not a known mnl object.
86 static inline ipc_port_t
87 mnl_name_to_port(mnl_name_t name
)
89 ipc_port_t lport
= IP_NULL
;
91 if (MNL_NAME_VALID(name
)) {
92 flipc_port_t fport
= (flipc_port_t
)mnl_obj_lookup(name
);
93 if (FPORT_VALID(fport
)) {
101 /* flipc_port_create() is called to convert a regular mach port into a
102 * flipc port (i.e., the port has one or more rights off-node).
103 * <lport> must be locked on entry and is not unlocked on return.
106 flipc_port_create(ipc_port_t lport
, mach_node_t node
, mnl_name_t name
)
108 /* Ensure parameters are valid and not already linked */
109 assert(IP_VALID(lport
));
110 assert(MACH_NODE_VALID(node
));
111 assert(MNL_NAME_VALID(name
));
112 assert(!FPORT_VALID(lport
->ip_messages
.imq_fport
));
114 /* Allocate and initialize a flipc port */
115 flipc_port_t fport
= (flipc_port_t
) zalloc(flipc_port_zone
);
116 if (!FPORT_VALID(fport
)) {
117 return KERN_RESOURCE_SHORTAGE
;
119 bzero(fport
, sizeof(struct flipc_port
));
120 fport
->obj
.name
= name
;
121 fport
->hostnode
= node
;
122 if (node
== localnode
) {
123 fport
->state
= FPORT_STATE_PRINCIPAL
;
125 fport
->state
= FPORT_STATE_PROXY
;
128 /* Link co-structures (lport is locked) */
129 fport
->lport
= lport
;
130 lport
->ip_messages
.imq_fport
= fport
;
132 /* Add fport to the name hash table; revert link if insert fails */
133 kern_return_t kr
= mnl_obj_insert((mnl_obj_t
)fport
);
134 if (kr
!= KERN_SUCCESS
) {
135 lport
->ip_messages
.imq_fport
= FPORT_NULL
;
136 fport
->lport
= IP_NULL
;
137 zfree(flipc_port_zone
, fport
);
144 /* flipc_port_destroy() is called to convert a flipc port back to a
145 * local-only ipc port (i.e., the port has no remaining off-node rights).
146 * This will dispose of any undelivered flipc messages, generating NAKs if
147 * needed. <lport> must be locked on entry and is not unlocked on return.
150 flipc_port_destroy(ipc_port_t lport
)
152 /* Ensure parameter is valid, and linked to an fport with a valid name */
153 assert(IP_VALID(lport
));
154 ipc_mqueue_t port_mq
= &lport
->ip_messages
;
155 flipc_port_t fport
= port_mq
->data
.port
.fport
;
156 assert(FPORT_VALID(fport
));
157 assert(MNL_NAME_VALID(fport
->obj
.name
));
159 /* Dispose of any undelivered messages */
160 int m
= port_mq
->data
.port
.msgcount
;
164 printf("flipc: destroying %p with %d undelivered msgs\n", lport
, m
);
167 /* Logic was lifted from ipc_mqueue_select_on_thread() */
169 kmsg
= ipc_kmsg_queue_first(&port_mq
->imq_messages
);
170 assert(kmsg
!= IKM_NULL
);
171 ipc_kmsg_rmqueue(&port_mq
->imq_messages
, kmsg
);
172 if (fport
->state
== FPORT_STATE_PRINCIPAL
) {
173 flipc_msg_ack(kmsg
->ikm_node
, port_mq
, FALSE
);
175 ipc_mqueue_release_msgcount(port_mq
, NULL
);
176 port_mq
->imq_seqno
++;
180 /* Remove from name hash table, unlink co-structures, and free fport */
181 mnl_obj_remove(fport
->obj
.name
);
182 lport
->ip_messages
.data
.port
.fport
= FPORT_NULL
;
183 fport
->lport
= IP_NULL
;
184 zfree(flipc_port_zone
, fport
);
189 * Routine: flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)
191 * Compute the size of the buffer needed to hold the translated flipc
192 * message. All identifiers are converted to flipc_names which are 64b.
193 * If this node's pointers are a different size, we have to allow for
194 * expansion of the descriptors as appropriate.
198 * size of the message as it would be sent over the flipc link.
200 static mach_msg_size_t
201 flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg
)
203 mach_msg_size_t fsize
= kmsg
->ikm_header
->msgh_size
;
205 if (kmsg
->ikm_header
->msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
206 PE_enter_debugger("flipc_msg_size_from_kmsg(): Complex messages not supported.");
213 /* Translate a kmsg into a flipc msg suitable to transmit over the mach node
214 * link. All in-line rights and objects are similarly processed. If the msg
215 * moves a receive right, then queued messages may need to be moved as a
216 * result, causing this function to ultimately be recursive.
219 mnl_msg_from_kmsg(ipc_kmsg_t kmsg
, mnl_msg_t
*fmsgp
)
221 if (kmsg
->ikm_header
->msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
222 printf("mnl_msg_from_kmsg(): Complex messages not supported.");
226 mach_msg_size_t fsize
= flipc_msg_size_from_kmsg(kmsg
);
228 mnl_msg_t fmsg
= mnl_msg_alloc(fsize
, 0);
230 if (fmsg
== MNL_MSG_NULL
) {
231 return KERN_RESOURCE_SHORTAGE
;
234 /* Setup flipc message header */
235 fmsg
->sub
= MACH_NODE_SUB_FLIPC
;
236 fmsg
->cmd
= FLIPC_CMD_IPCMESSAGE
;
237 fmsg
->node_id
= localnode_id
; // Message is from us
238 fmsg
->qos
= 0; // not used
239 fmsg
->size
= fsize
; // Payload size (does NOT include mnl_msg header)
240 fmsg
->object
= kmsg
->ikm_header
->msgh_remote_port
->ip_messages
.data
.port
.fport
->obj
.name
;
242 /* Copy body of message */
243 bcopy((const void*)kmsg
->ikm_header
, (void*)MNL_MSG_PAYLOAD(fmsg
), fsize
);
245 // Convert port fields
246 mach_msg_header_t
*mmsg
= (mach_msg_header_t
*)MNL_MSG_PAYLOAD(fmsg
);
247 mmsg
->msgh_remote_port
= (mach_port_t
)fmsg
->object
;
248 mmsg
->msgh_local_port
= (mach_port_t
)
249 mnl_name_from_port(mmsg
->msgh_local_port
);
250 mmsg
->msgh_voucher_port
= (mach_port_name_t
)MNL_NAME_NULL
;
252 *fmsgp
= (mnl_msg_t
)fmsg
;
258 /* lifted from ipc_mig.c:mach_msg_send_from_kernel_proper() */
259 static mach_msg_return_t
260 mach_msg_send_from_remote_kernel(mach_msg_header_t
*msg
,
261 mach_msg_size_t send_size
,
265 mach_msg_return_t mr
;
267 mr
= ipc_kmsg_get_from_kernel(msg
, send_size
, &kmsg
);
268 if (mr
!= MACH_MSG_SUCCESS
) {
272 mr
= ipc_kmsg_copyin_from_kernel(kmsg
);
273 if (mr
!= MACH_MSG_SUCCESS
) {
278 kmsg
->ikm_node
= node
; // node that needs to receive message ack
279 mr
= ipc_kmsg_send(kmsg
,
280 MACH_SEND_KERNEL_DEFAULT
,
281 MACH_MSG_TIMEOUT_NONE
);
282 if (mr
!= MACH_MSG_SUCCESS
) {
283 ipc_kmsg_destroy(kmsg
);
290 /* Translate a flipc msg <fmsg> into a kmsg and post it to the appropriate
291 * port. <node> is the node that originated the message, not necessarily the
292 * node we received it from. This will block if the receiving port is full.
294 static mach_msg_return_t
295 flipc_cmd_ipc(mnl_msg_t fmsg
,
297 uint32_t flags __unused
)
299 mach_msg_header_t
*mmsg
;
301 // Convert flipc message into mach message in place to avoid alloc/copy
302 mmsg
= (mach_msg_header_t
*)MNL_MSG_PAYLOAD(fmsg
);
303 mmsg
->msgh_size
= fmsg
->size
;
304 mmsg
->msgh_remote_port
= mnl_name_to_port(fmsg
->object
);
305 mmsg
->msgh_local_port
= mnl_name_to_port((mnl_name_t
)mmsg
->msgh_local_port
);
306 mmsg
->msgh_voucher_port
= (mach_port_name_t
)MACH_PORT_NULL
;
307 mmsg
->msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND
, 0);
308 // unchanged: msgh_id
310 return mach_msg_send_from_remote_kernel(mmsg
, fmsg
->size
, node
);
314 /* Called when an ACKMESSAGE packet is received. <name> indicates
315 * the flipc name of the port holding the messages to be acknowledged.
316 * <msg_count> indicates the number of messages being acked for this node:port.
319 flipc_cmd_ack(flipc_ack_msg_t fmsg
,
320 mach_node_t node __unused
,
321 uint32_t flags __unused
)
323 unsigned int msg_count
= fmsg
->msg_count
;
324 thread_t thread
= current_thread();
325 boolean_t kick
= FALSE
;
327 flipc_port_t fport
= (flipc_port_t
)mnl_obj_lookup(fmsg
->mnl
.object
);
329 ipc_port_t lport
= fport
->lport
;
332 ipc_mqueue_t lport_mq
= &lport
->ip_messages
;
335 assert(fport
->peek_count
>= msg_count
); // Can't ack what we haven't peeked!
337 while (msg_count
--) {
338 ipc_mqueue_select_on_thread(lport_mq
, IMQ_NULL
, 0, 0, thread
);
340 kick
|= ipc_kmsg_delayed_destroy(thread
->ith_kmsg
);
343 imq_unlock(lport_mq
);
347 ipc_kmsg_reap_delayed();
353 /*** FLIPC Node Managment Functions (called by mach node layer) ***/
356 /* The mach node layer calls flipc_init() once before it calls any other
357 * flipc entry points. Returns KERN_SUCCESS on success; otherwise flipc
358 * is not initialized and cannot be used.
363 /* Create zone for flipc ports.
364 * TODO: Pick a better max value than ipc_port_max>>4
366 flipc_port_zone
= zinit(sizeof(struct flipc_port
),
367 (ipc_port_max
>> 4) * sizeof(struct flipc_port
),
368 sizeof(struct flipc_port
),
371 zone_change(flipc_port_zone
, Z_CALLERACCT
, FALSE
);
372 zone_change(flipc_port_zone
, Z_NOENCRYPT
, TRUE
);
377 /* flipc_node_prepare() is called by mach node layer when a remote node is
378 * registered by a link driver, or when the bootstrap port changes for the
379 * local node. This is the flipc layer's opportunity to initialize per-node
380 * flipc state, and to convert the node's bootstrap port into a flipc port.
381 * Note that the node is not yet in the mach node table.
382 * Returns KERN_SUCCESS on success; otherwise node is not prepared.
385 flipc_node_prepare(mach_node_t node
)
389 assert(MACH_NODE_VALID(node
));
390 ipc_port_t bs_port
= node
->bootstrap_port
;
391 assert(IP_VALID(bs_port
));
395 kr
= flipc_port_create(bs_port
,
397 MNL_NAME_BOOTSTRAP(node
->info
.node_id
));
404 /* flipc_node_retire() is called by mach node layer when a remote node is
405 * terminated by a link driver, or when the local node's bootstrap port
406 * becomes invalid. This is the flipc layer's opportunity to free per-node
407 * flipc state, and to revert the node's bootstrap port to a local ipc port.
408 * <node> must be locked by the caller.
409 * Returns KERN_SUCCESS on success.
412 flipc_node_retire(mach_node_t node
)
414 if (!MACH_NODE_VALID(node
)) {
415 return KERN_NODE_DOWN
;
418 ipc_port_t bs_port
= node
->bootstrap_port
;
419 if (IP_VALID(bs_port
)) {
421 flipc_port_destroy(bs_port
);
429 /*** FLIPC Message Functions (called by mach node layer) ***/
432 /* The node layer calls flipc_msg_to_remote_node() to fetch the next message
433 * for <node>. This function will block until a message is available or the
434 * node is terminated, in which case it returns MNL_MSG_NULL.
437 flipc_msg_to_remote_node(mach_node_t to_node
,
438 uint32_t flags __unused
)
440 mach_port_seqno_t msgoff
;
441 ipc_kmsg_t kmsg
= IKM_NULL
;
442 mnl_msg_t fmsg
= MNL_MSG_NULL
;
444 assert(to_node
!= localnode
);
445 assert(get_preemption_level() == 0);
447 ipc_mqueue_t portset_mq
= &to_node
->proxy_port_set
->ips_messages
;
448 ipc_mqueue_t port_mq
= IMQ_NULL
;
450 while (!to_node
->dead
) {
451 /* Fetch next message from proxy port */
452 ipc_mqueue_receive(portset_mq
, MACH_PEEK_MSG
, 0, 0, THREAD_ABORTSAFE
);
454 thread_t thread
= current_thread();
455 if (thread
->ith_state
== MACH_PEEK_READY
) {
456 port_mq
= thread
->ith_peekq
;
457 thread
->ith_peekq
= IMQ_NULL
;
459 panic("Unexpected thread state %d after ipc_mqueue_receive()",
463 assert(get_preemption_level() == 0);
466 flipc_port_t fport
= port_mq
->data
.port
.fport
;
468 if (FPORT_VALID(fport
)) {
469 msgoff
= port_mq
->data
.port
.fport
->peek_count
;
471 ipc_mqueue_peek_locked(port_mq
, &msgoff
, NULL
, NULL
, NULL
, &kmsg
);
472 if (kmsg
!= IKM_NULL
) {
473 port_mq
->data
.port
.fport
->peek_count
++;
476 /* Clean up outstanding prepost on port_mq.
477 * This also unlocks port_mq.
479 ipc_mqueue_release_peek_ref(port_mq
);
480 assert(get_preemption_level() == 0);
482 /* DANGER: The code below must be allowed to allocate so it can't
483 * run under the protection of the imq_lock, but that leaves mqueue
484 * open for business for a small window before we examine kmsg.
485 * This SHOULD be OK, since we are the only thread looking.
487 if (kmsg
!= IKM_NULL
) {
488 mnl_msg_from_kmsg(kmsg
, (mnl_msg_t
*)&fmsg
);
491 /* Must be from the control_port, which is not a flipc port */
492 assert(!FPORT_VALID(port_mq
->data
.port
.fport
));
494 /* This is a simplified copy of ipc_mqueue_select_on_thread() */
495 kmsg
= ipc_kmsg_queue_first(&port_mq
->imq_messages
);
496 assert(kmsg
!= IKM_NULL
);
497 ipc_kmsg_rmqueue(&port_mq
->imq_messages
, kmsg
);
498 ipc_mqueue_release_msgcount(port_mq
, portset_mq
);
500 current_task()->messages_received
++;
501 ip_release(to_node
->control_port
); // Should derive ref from port_mq
503 /* We just pass the kmsg payload as the fmsg.
504 * flipc_msg_free() will notice and free the kmsg properly.
506 mach_msg_header_t
*hdr
= kmsg
->ikm_header
;
507 fmsg
= (mnl_msg_t
)(&hdr
[1]);
508 /* Stash kmsg pointer just before fmsg */
509 *(ipc_kmsg_t
*)((vm_offset_t
)fmsg
- sizeof(vm_offset_t
)) = kmsg
;
512 if (MNL_MSG_VALID(fmsg
)) {
516 assert(MNL_MSG_VALID(fmsg
));
521 /* The mach node layer calls this to deliver an incoming message. It is the
522 * responsibility of the caller to release the received message buffer after
526 flipc_msg_from_node(mach_node_t from_node __unused
,
530 /* Note that if flipc message forwarding is supported, the from_node arg
531 * may not match fmsg->node_id. The former is the node from which we
532 * received the message; the latter is the node that originated the
533 * message. We use the originating node, which is where the ack goes.
535 assert(msg
->sub
== MACH_NODE_SUB_FLIPC
);
536 mach_node_t node
= mach_node_for_id_locked(msg
->node_id
, FALSE
, FALSE
);
537 MACH_NODE_UNLOCK(node
);
540 case FLIPC_CMD_IPCMESSAGE
:
541 flipc_cmd_ipc(msg
, node
, flags
);
544 case FLIPC_CMD_ACKMESSAGE
:
545 case FLIPC_CMD_NAKMESSAGE
:
546 flipc_cmd_ack((flipc_ack_msg_t
)msg
, node
, flags
);
551 PE_enter_debugger("flipc_incoming(): Invalid command");
558 /* The node layer calls flipc_msg_free() to dispose of sent messages that
559 * originated in the FLIPC layer. This allows us to repurpose the payload
560 * of an ack or nak kmsg as a flipc message to avoid a copy - we detect
561 * such messages here and free them appropriately.
564 flipc_msg_free(mnl_msg_t msg
,
568 case FLIPC_CMD_ACKMESSAGE
: // Flipc msg is a kmsg in disguise...
569 case FLIPC_CMD_NAKMESSAGE
: // Convert back to kmsg for disposal
570 ipc_kmsg_free(*(ipc_kmsg_t
*)((vm_offset_t
)msg
- sizeof(vm_offset_t
)));
573 default: // Flipc msg is not a kmsg in disguise; dispose of normally
574 mnl_msg_free(msg
, flags
);
580 /*** FLIPC Message Functions (called by mach ipc subsystem) ***/
582 /* Ack's one message sent to <mqueue> from <node>. A new kmsg is allocated
583 * and filled in as an ack, then posted to the node's contol port. This will
584 * wake the link driver (if sleeping) and cause the ack to be included with
585 * normal IPC traffic.
587 * This function immediately returns if <fport> or <node> is invalid, so it
588 * is safe & quick to call speculatively.
590 * Called from mach ipc_mqueue.c when a flipc-originated message is consumed.
593 flipc_msg_ack(mach_node_t node
,
597 flipc_port_t fport
= mqueue
->imq_fport
;
599 assert(FPORT_VALID(fport
));
600 assert(MACH_NODE_VALID(node
));
602 mnl_name_t name
= MNL_NAME_NULL
;
603 mach_node_id_t nid
= HOST_LOCAL_NODE
;
604 ipc_port_t ack_port
= IP_NULL
;
606 ip_lock(fport
->lport
);
607 name
= fport
->obj
.name
;
608 ip_unlock(fport
->lport
);
610 if (!MNL_NAME_VALID(name
)) {
614 MACH_NODE_LOCK(node
);
616 nid
= node
->info
.node_id
;
617 ack_port
= node
->control_port
;
619 MACH_NODE_UNLOCK(node
);
621 if (!IP_VALID(ack_port
) || !MACH_NODE_ID_VALID(nid
)) {
625 /* We have a valid node id & obj name, and a port to send the ack to. */
626 ipc_kmsg_t kmsg
= ipc_kmsg_alloc(sizeof(struct flipc_ack_msg
) + MAX_TRAILER_SIZE
);
627 assert((unsigned long long)kmsg
>= 4ULL);//!= IKM_NULL);
628 mach_msg_header_t
*msg
= kmsg
->ikm_header
;
630 /* Fill in the mach_msg_header struct */
631 msg
->msgh_bits
= MACH_MSGH_BITS_SET(0, 0, 0, 0);
632 msg
->msgh_size
= sizeof(msg
);
633 msg
->msgh_remote_port
= ack_port
;
634 msg
->msgh_local_port
= MACH_PORT_NULL
;
635 msg
->msgh_voucher_port
= MACH_PORT_NULL
;
636 msg
->msgh_id
= FLIPC_CMD_ID
;
638 /* Fill in the flipc_ack_msg struct */
639 flipc_ack_msg_t fmsg
= (flipc_ack_msg_t
)(&msg
[1]);
640 fmsg
->resend_to
= HOST_LOCAL_NODE
;
641 fmsg
->msg_count
= 1; // Might want to coalesce acks to a node/name pair
643 /* Fill in the mnl_msg struct */
644 fmsg
->mnl
.sub
= MACH_NODE_SUB_FLIPC
;
645 fmsg
->mnl
.cmd
= delivered
? FLIPC_CMD_ACKMESSAGE
: FLIPC_CMD_NAKMESSAGE
;
646 fmsg
->mnl
.qos
= 0; // Doesn't do anything yet
648 fmsg
->mnl
.node_id
= nid
;
649 fmsg
->mnl
.object
= name
;
650 fmsg
->mnl
.options
= 0;
651 fmsg
->mnl
.size
= sizeof(struct flipc_ack_msg
) - sizeof(struct mnl_msg
);
654 mach_msg_return_t mmr
;
655 ipc_mqueue_t ack_mqueue
;
658 ack_mqueue
= &ack_port
->ip_messages
;
659 imq_lock(ack_mqueue
);
662 /* ipc_mqueue_send() unlocks ack_mqueue */
663 mmr
= ipc_mqueue_send(ack_mqueue
, kmsg
, 0, 0);
666 kr
= ipc_kmsg_send(kmsg
,
667 MACH_SEND_KERNEL_DEFAULT
,
668 MACH_MSG_TIMEOUT_NONE
);