2 * Copyright (c) 2015-2020 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) ***/
61 ZONE_DECLARE(flipc_port_zone
, "flipc ports",
62 sizeof(struct flipc_port
), ZC_NOENCRYPT
);
64 /* Get the mnl_name associated with local ipc_port <lport>.
65 * Returns MNL_NAME_NULL if <lport> is invalid or not a flipc port.
67 static inline mnl_name_t
68 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
;
82 /* Lookup the ipc_port associated with mnl_name <name>.
83 * Returns IP_NULL if <name> is invalid or not a known mnl object.
85 static inline ipc_port_t
86 mnl_name_to_port(mnl_name_t name
)
88 ipc_port_t lport
= IP_NULL
;
90 if (MNL_NAME_VALID(name
)) {
91 flipc_port_t fport
= (flipc_port_t
)mnl_obj_lookup(name
);
92 if (FPORT_VALID(fport
)) {
100 /* flipc_port_create() is called to convert a regular mach port into a
101 * flipc port (i.e., the port has one or more rights off-node).
102 * <lport> must be locked on entry and is not unlocked on return.
105 flipc_port_create(ipc_port_t lport
, mach_node_t node
, mnl_name_t name
)
107 /* Ensure parameters are valid and not already linked */
108 assert(IP_VALID(lport
));
109 assert(MACH_NODE_VALID(node
));
110 assert(MNL_NAME_VALID(name
));
111 assert(!FPORT_VALID(lport
->ip_messages
.imq_fport
));
113 /* Allocate and initialize a flipc port */
114 flipc_port_t fport
= (flipc_port_t
) zalloc(flipc_port_zone
);
115 if (!FPORT_VALID(fport
)) {
116 return KERN_RESOURCE_SHORTAGE
;
118 bzero(fport
, sizeof(struct flipc_port
));
119 fport
->obj
.name
= name
;
120 fport
->hostnode
= node
;
121 if (node
== localnode
) {
122 fport
->state
= FPORT_STATE_PRINCIPAL
;
124 fport
->state
= FPORT_STATE_PROXY
;
127 /* Link co-structures (lport is locked) */
128 fport
->lport
= lport
;
129 lport
->ip_messages
.imq_fport
= fport
;
131 /* Add fport to the name hash table; revert link if insert fails */
132 kern_return_t kr
= mnl_obj_insert((mnl_obj_t
)fport
);
133 if (kr
!= KERN_SUCCESS
) {
134 lport
->ip_messages
.imq_fport
= FPORT_NULL
;
135 fport
->lport
= IP_NULL
;
136 zfree(flipc_port_zone
, fport
);
143 /* flipc_port_destroy() is called to convert a flipc port back to a
144 * local-only ipc port (i.e., the port has no remaining off-node rights).
145 * This will dispose of any undelivered flipc messages, generating NAKs if
146 * needed. <lport> must be locked on entry and is not unlocked on return.
149 flipc_port_destroy(ipc_port_t lport
)
151 /* Ensure parameter is valid, and linked to an fport with a valid name */
152 assert(IP_VALID(lport
));
153 ipc_mqueue_t port_mq
= &lport
->ip_messages
;
154 flipc_port_t fport
= port_mq
->data
.port
.fport
;
155 assert(FPORT_VALID(fport
));
156 assert(MNL_NAME_VALID(fport
->obj
.name
));
158 /* Dispose of any undelivered messages */
159 int m
= port_mq
->data
.port
.msgcount
;
163 printf("flipc: destroying %p with %d undelivered msgs\n", lport
, m
);
166 /* Logic was lifted from ipc_mqueue_select_on_thread() */
168 kmsg
= ipc_kmsg_queue_first(&port_mq
->imq_messages
);
169 assert(kmsg
!= IKM_NULL
);
170 ipc_kmsg_rmqueue(&port_mq
->imq_messages
, kmsg
);
171 if (fport
->state
== FPORT_STATE_PRINCIPAL
) {
172 flipc_msg_ack(kmsg
->ikm_node
, port_mq
, FALSE
);
174 ipc_mqueue_release_msgcount(port_mq
, NULL
);
175 port_mq
->imq_seqno
++;
179 /* Remove from name hash table, unlink co-structures, and free fport */
180 mnl_obj_remove(fport
->obj
.name
);
181 lport
->ip_messages
.data
.port
.fport
= FPORT_NULL
;
182 fport
->lport
= IP_NULL
;
183 zfree(flipc_port_zone
, fport
);
188 * Routine: flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)
190 * Compute the size of the buffer needed to hold the translated flipc
191 * message. All identifiers are converted to flipc_names which are 64b.
192 * If this node's pointers are a different size, we have to allow for
193 * expansion of the descriptors as appropriate.
197 * size of the message as it would be sent over the flipc link.
199 static mach_msg_size_t
200 flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg
)
202 mach_msg_size_t fsize
= kmsg
->ikm_header
->msgh_size
;
204 if (kmsg
->ikm_header
->msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
205 PE_enter_debugger("flipc_msg_size_from_kmsg(): Complex messages not supported.");
212 /* Translate a kmsg into a flipc msg suitable to transmit over the mach node
213 * link. All in-line rights and objects are similarly processed. If the msg
214 * moves a receive right, then queued messages may need to be moved as a
215 * result, causing this function to ultimately be recursive.
218 mnl_msg_from_kmsg(ipc_kmsg_t kmsg
, mnl_msg_t
*fmsgp
)
220 if (kmsg
->ikm_header
->msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
221 printf("mnl_msg_from_kmsg(): Complex messages not supported.");
225 mach_msg_size_t fsize
= flipc_msg_size_from_kmsg(kmsg
);
227 mnl_msg_t fmsg
= mnl_msg_alloc(fsize
, 0);
229 if (fmsg
== MNL_MSG_NULL
) {
230 return KERN_RESOURCE_SHORTAGE
;
233 /* Setup flipc message header */
234 fmsg
->sub
= MACH_NODE_SUB_FLIPC
;
235 fmsg
->cmd
= FLIPC_CMD_IPCMESSAGE
;
236 fmsg
->node_id
= localnode_id
; // Message is from us
237 fmsg
->qos
= 0; // not used
238 fmsg
->size
= fsize
; // Payload size (does NOT include mnl_msg header)
239 fmsg
->object
= kmsg
->ikm_header
->msgh_remote_port
->ip_messages
.data
.port
.fport
->obj
.name
;
241 /* Copy body of message */
242 bcopy((const void*)kmsg
->ikm_header
, (void*)MNL_MSG_PAYLOAD(fmsg
), fsize
);
244 // Convert port fields
245 mach_msg_header_t
*mmsg
= (mach_msg_header_t
*)MNL_MSG_PAYLOAD(fmsg
);
246 mmsg
->msgh_remote_port
= (mach_port_t
)fmsg
->object
;
247 mmsg
->msgh_local_port
= (mach_port_t
)
248 mnl_name_from_port(mmsg
->msgh_local_port
);
249 mmsg
->msgh_voucher_port
= (mach_port_name_t
)MNL_NAME_NULL
;
251 *fmsgp
= (mnl_msg_t
)fmsg
;
257 /* lifted from ipc_mig.c:mach_msg_send_from_kernel_proper() */
258 static mach_msg_return_t
259 mach_msg_send_from_remote_kernel(mach_msg_header_t
*msg
,
260 mach_msg_size_t send_size
,
264 mach_msg_return_t mr
;
266 mr
= ipc_kmsg_get_from_kernel(msg
, send_size
, &kmsg
);
267 if (mr
!= MACH_MSG_SUCCESS
) {
271 mr
= ipc_kmsg_copyin_from_kernel(kmsg
);
272 if (mr
!= MACH_MSG_SUCCESS
) {
277 kmsg
->ikm_node
= node
; // node that needs to receive message ack
278 mr
= ipc_kmsg_send(kmsg
,
279 MACH_SEND_KERNEL_DEFAULT
,
280 MACH_MSG_TIMEOUT_NONE
);
281 if (mr
!= MACH_MSG_SUCCESS
) {
282 ipc_kmsg_destroy(kmsg
);
289 /* Translate a flipc msg <fmsg> into a kmsg and post it to the appropriate
290 * port. <node> is the node that originated the message, not necessarily the
291 * node we received it from. This will block if the receiving port is full.
293 static mach_msg_return_t
294 flipc_cmd_ipc(mnl_msg_t fmsg
,
296 uint32_t flags __unused
)
298 mach_msg_header_t
*mmsg
;
300 // Convert flipc message into mach message in place to avoid alloc/copy
301 mmsg
= (mach_msg_header_t
*)MNL_MSG_PAYLOAD(fmsg
);
302 mmsg
->msgh_size
= fmsg
->size
;
303 mmsg
->msgh_remote_port
= mnl_name_to_port(fmsg
->object
);
304 mmsg
->msgh_local_port
= mnl_name_to_port((mnl_name_t
)mmsg
->msgh_local_port
);
305 mmsg
->msgh_voucher_port
= (mach_port_name_t
)MACH_PORT_NULL
;
306 mmsg
->msgh_bits
= MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND
, 0);
307 // unchanged: msgh_id
309 return mach_msg_send_from_remote_kernel(mmsg
, fmsg
->size
, node
);
313 /* Called when an ACKMESSAGE packet is received. <name> indicates
314 * the flipc name of the port holding the messages to be acknowledged.
315 * <msg_count> indicates the number of messages being acked for this node:port.
318 flipc_cmd_ack(flipc_ack_msg_t fmsg
,
319 mach_node_t node __unused
,
320 uint32_t flags __unused
)
322 unsigned int msg_count
= fmsg
->msg_count
;
323 thread_t thread
= current_thread();
324 boolean_t kick
= FALSE
;
326 flipc_port_t fport
= (flipc_port_t
)mnl_obj_lookup(fmsg
->mnl
.object
);
328 ipc_port_t lport
= fport
->lport
;
331 ipc_mqueue_t lport_mq
= &lport
->ip_messages
;
334 assert(fport
->peek_count
>= msg_count
); // Can't ack what we haven't peeked!
336 while (msg_count
--) {
337 ipc_mqueue_select_on_thread(lport_mq
, IMQ_NULL
, 0, 0, thread
);
339 kick
|= ipc_kmsg_delayed_destroy(thread
->ith_kmsg
);
342 imq_unlock(lport_mq
);
346 ipc_kmsg_reap_delayed();
352 /*** FLIPC Node Managment Functions (called by mach node layer) ***/
355 /* flipc_node_prepare() is called by mach node layer when a remote node is
356 * registered by a link driver, or when the bootstrap port changes for the
357 * local node. This is the flipc layer's opportunity to initialize per-node
358 * flipc state, and to convert the node's bootstrap port into a flipc port.
359 * Note that the node is not yet in the mach node table.
360 * Returns KERN_SUCCESS on success; otherwise node is not prepared.
363 flipc_node_prepare(mach_node_t node
)
367 assert(MACH_NODE_VALID(node
));
368 ipc_port_t bs_port
= node
->bootstrap_port
;
369 assert(IP_VALID(bs_port
));
373 kr
= flipc_port_create(bs_port
,
375 MNL_NAME_BOOTSTRAP(node
->info
.node_id
));
382 /* flipc_node_retire() is called by mach node layer when a remote node is
383 * terminated by a link driver, or when the local node's bootstrap port
384 * becomes invalid. This is the flipc layer's opportunity to free per-node
385 * flipc state, and to revert the node's bootstrap port to a local ipc port.
386 * <node> must be locked by the caller.
387 * Returns KERN_SUCCESS on success.
390 flipc_node_retire(mach_node_t node
)
392 if (!MACH_NODE_VALID(node
)) {
393 return KERN_NODE_DOWN
;
396 ipc_port_t bs_port
= node
->bootstrap_port
;
397 if (IP_VALID(bs_port
)) {
399 flipc_port_destroy(bs_port
);
407 /*** FLIPC Message Functions (called by mach node layer) ***/
410 /* The node layer calls flipc_msg_to_remote_node() to fetch the next message
411 * for <node>. This function will block until a message is available or the
412 * node is terminated, in which case it returns MNL_MSG_NULL.
415 flipc_msg_to_remote_node(mach_node_t to_node
,
416 uint32_t flags __unused
)
418 mach_port_seqno_t msgoff
;
419 ipc_kmsg_t kmsg
= IKM_NULL
;
420 mnl_msg_t fmsg
= MNL_MSG_NULL
;
422 assert(to_node
!= localnode
);
423 assert(get_preemption_level() == 0);
425 ipc_mqueue_t portset_mq
= &to_node
->proxy_port_set
->ips_messages
;
426 ipc_mqueue_t port_mq
= IMQ_NULL
;
428 while (!to_node
->dead
) {
429 /* Fetch next message from proxy port */
430 ipc_mqueue_receive(portset_mq
, MACH_PEEK_MSG
, 0, 0, THREAD_ABORTSAFE
);
432 thread_t thread
= current_thread();
433 if (thread
->ith_state
== MACH_PEEK_READY
) {
434 port_mq
= thread
->ith_peekq
;
435 thread
->ith_peekq
= IMQ_NULL
;
437 panic("Unexpected thread state %d after ipc_mqueue_receive()",
441 assert(get_preemption_level() == 0);
444 flipc_port_t fport
= port_mq
->data
.port
.fport
;
446 if (FPORT_VALID(fport
)) {
447 msgoff
= port_mq
->data
.port
.fport
->peek_count
;
449 ipc_mqueue_peek_locked(port_mq
, &msgoff
, NULL
, NULL
, NULL
, &kmsg
);
450 if (kmsg
!= IKM_NULL
) {
451 port_mq
->data
.port
.fport
->peek_count
++;
454 /* Clean up outstanding prepost on port_mq.
455 * This also unlocks port_mq.
457 ipc_mqueue_release_peek_ref(port_mq
);
458 assert(get_preemption_level() == 0);
460 /* DANGER: The code below must be allowed to allocate so it can't
461 * run under the protection of the imq_lock, but that leaves mqueue
462 * open for business for a small window before we examine kmsg.
463 * This SHOULD be OK, since we are the only thread looking.
465 if (kmsg
!= IKM_NULL
) {
466 mnl_msg_from_kmsg(kmsg
, (mnl_msg_t
*)&fmsg
);
469 /* Must be from the control_port, which is not a flipc port */
470 assert(!FPORT_VALID(port_mq
->data
.port
.fport
));
472 /* This is a simplified copy of ipc_mqueue_select_on_thread() */
473 kmsg
= ipc_kmsg_queue_first(&port_mq
->imq_messages
);
474 assert(kmsg
!= IKM_NULL
);
475 ipc_kmsg_rmqueue(&port_mq
->imq_messages
, kmsg
);
476 ipc_mqueue_release_msgcount(port_mq
, portset_mq
);
478 current_task()->messages_received
++;
479 ip_release(to_node
->control_port
); // Should derive ref from port_mq
481 /* We just pass the kmsg payload as the fmsg.
482 * flipc_msg_free() will notice and free the kmsg properly.
484 mach_msg_header_t
*hdr
= kmsg
->ikm_header
;
485 fmsg
= (mnl_msg_t
)(&hdr
[1]);
486 /* Stash kmsg pointer just before fmsg */
487 *(ipc_kmsg_t
*)((vm_offset_t
)fmsg
- sizeof(vm_offset_t
)) = kmsg
;
490 if (MNL_MSG_VALID(fmsg
)) {
494 assert(MNL_MSG_VALID(fmsg
));
499 /* The mach node layer calls this to deliver an incoming message. It is the
500 * responsibility of the caller to release the received message buffer after
504 flipc_msg_from_node(mach_node_t from_node __unused
,
508 /* Note that if flipc message forwarding is supported, the from_node arg
509 * may not match fmsg->node_id. The former is the node from which we
510 * received the message; the latter is the node that originated the
511 * message. We use the originating node, which is where the ack goes.
513 assert(msg
->sub
== MACH_NODE_SUB_FLIPC
);
514 mach_node_t node
= mach_node_for_id_locked(msg
->node_id
, FALSE
, FALSE
);
515 MACH_NODE_UNLOCK(node
);
518 case FLIPC_CMD_IPCMESSAGE
:
519 flipc_cmd_ipc(msg
, node
, flags
);
522 case FLIPC_CMD_ACKMESSAGE
:
523 case FLIPC_CMD_NAKMESSAGE
:
524 flipc_cmd_ack((flipc_ack_msg_t
)msg
, node
, flags
);
529 PE_enter_debugger("flipc_incoming(): Invalid command");
536 /* The node layer calls flipc_msg_free() to dispose of sent messages that
537 * originated in the FLIPC layer. This allows us to repurpose the payload
538 * of an ack or nak kmsg as a flipc message to avoid a copy - we detect
539 * such messages here and free them appropriately.
542 flipc_msg_free(mnl_msg_t msg
,
546 case FLIPC_CMD_ACKMESSAGE
: // Flipc msg is a kmsg in disguise...
547 case FLIPC_CMD_NAKMESSAGE
: // Convert back to kmsg for disposal
548 ipc_kmsg_free(*(ipc_kmsg_t
*)((vm_offset_t
)msg
- sizeof(vm_offset_t
)));
551 default: // Flipc msg is not a kmsg in disguise; dispose of normally
552 mnl_msg_free(msg
, flags
);
558 /*** FLIPC Message Functions (called by mach ipc subsystem) ***/
560 /* Ack's one message sent to <mqueue> from <node>. A new kmsg is allocated
561 * and filled in as an ack, then posted to the node's contol port. This will
562 * wake the link driver (if sleeping) and cause the ack to be included with
563 * normal IPC traffic.
565 * This function immediately returns if <fport> or <node> is invalid, so it
566 * is safe & quick to call speculatively.
568 * Called from mach ipc_mqueue.c when a flipc-originated message is consumed.
571 flipc_msg_ack(mach_node_t node
,
575 flipc_port_t fport
= mqueue
->imq_fport
;
577 assert(FPORT_VALID(fport
));
578 assert(MACH_NODE_VALID(node
));
580 mnl_name_t name
= MNL_NAME_NULL
;
581 mach_node_id_t nid
= HOST_LOCAL_NODE
;
582 ipc_port_t ack_port
= IP_NULL
;
584 ip_lock(fport
->lport
);
585 name
= fport
->obj
.name
;
586 ip_unlock(fport
->lport
);
588 if (!MNL_NAME_VALID(name
)) {
592 MACH_NODE_LOCK(node
);
594 nid
= node
->info
.node_id
;
595 ack_port
= node
->control_port
;
597 MACH_NODE_UNLOCK(node
);
599 if (!IP_VALID(ack_port
) || !MACH_NODE_ID_VALID(nid
)) {
603 /* We have a valid node id & obj name, and a port to send the ack to. */
604 ipc_kmsg_t kmsg
= ipc_kmsg_alloc(sizeof(struct flipc_ack_msg
) + MAX_TRAILER_SIZE
);
605 assert((unsigned long long)kmsg
>= 4ULL);//!= IKM_NULL);
606 mach_msg_header_t
*msg
= kmsg
->ikm_header
;
608 /* Fill in the mach_msg_header struct */
609 msg
->msgh_bits
= MACH_MSGH_BITS_SET(0, 0, 0, 0);
610 msg
->msgh_size
= sizeof(msg
);
611 msg
->msgh_remote_port
= ack_port
;
612 msg
->msgh_local_port
= MACH_PORT_NULL
;
613 msg
->msgh_voucher_port
= MACH_PORT_NULL
;
614 msg
->msgh_id
= FLIPC_CMD_ID
;
616 /* Fill in the flipc_ack_msg struct */
617 flipc_ack_msg_t fmsg
= (flipc_ack_msg_t
)(&msg
[1]);
618 fmsg
->resend_to
= HOST_LOCAL_NODE
;
619 fmsg
->msg_count
= 1; // Might want to coalesce acks to a node/name pair
621 /* Fill in the mnl_msg struct */
622 fmsg
->mnl
.sub
= MACH_NODE_SUB_FLIPC
;
623 fmsg
->mnl
.cmd
= delivered
? FLIPC_CMD_ACKMESSAGE
: FLIPC_CMD_NAKMESSAGE
;
624 fmsg
->mnl
.qos
= 0; // Doesn't do anything yet
626 fmsg
->mnl
.node_id
= nid
;
627 fmsg
->mnl
.object
= name
;
628 fmsg
->mnl
.options
= 0;
629 fmsg
->mnl
.size
= sizeof(struct flipc_ack_msg
) - sizeof(struct mnl_msg
);
632 mach_msg_return_t mmr
;
633 ipc_mqueue_t ack_mqueue
;
636 ack_mqueue
= &ack_port
->ip_messages
;
637 imq_lock(ack_mqueue
);
640 /* ipc_mqueue_send() unlocks ack_mqueue */
641 mmr
= ipc_mqueue_send(ack_mqueue
, kmsg
, 0, 0);
644 kr
= ipc_kmsg_send(kmsg
,
645 MACH_SEND_KERNEL_DEFAULT
,
646 MACH_MSG_TIMEOUT_NONE
);