xref: /xnu-8019.80.24/osfmk/ipc/flipc.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 /*
2  * Copyright (c) 2015-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
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.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
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.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /*	File:	ipc/flipc.h
29  *	Author:	Dean Reece
30  *	Date:	2016
31  *
32  *	Implementation of fast local ipc (flipc).
33  */
34 
35 
36 #include <mach/mach_types.h>
37 #include <mach/boolean.h>
38 #include <mach/kern_return.h>
39 
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>
45 
46 #include <ipc/port.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>
55 
56 #pragma pack(4)
57 
58 
59 /*** FLIPC Internal Implementation (private to flipc.c) ***/
60 
61 ZONE_DECLARE(flipc_port_zone, "flipc ports",
62     sizeof(struct flipc_port), ZC_ZFREE_CLEARMEM);
63 
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.
66  */
67 static inline mnl_name_t
mnl_name_from_port(ipc_port_t lport)68 mnl_name_from_port(ipc_port_t lport)
69 {
70 	mnl_name_t name = MNL_NAME_NULL;
71 
72 	if (IP_VALID(lport)) {
73 		flipc_port_t fport = lport->ip_messages.imq_fport;
74 		if (FPORT_VALID(fport)) {
75 			name = fport->obj.name;
76 		}
77 	}
78 	return name;
79 }
80 
81 
82 /*  Lookup the ipc_port associated with mnl_name <name>.
83  *  Returns IP_NULL if <name> is invalid or not a known mnl object.
84  */
85 static inline ipc_port_t
mnl_name_to_port(mnl_name_t name)86 mnl_name_to_port(mnl_name_t name)
87 {
88 	ipc_port_t lport = IP_NULL;
89 
90 	if (MNL_NAME_VALID(name)) {
91 		flipc_port_t fport = (flipc_port_t)mnl_obj_lookup(name);
92 		if (FPORT_VALID(fport)) {
93 			lport = fport->lport;
94 		}
95 	}
96 	return lport;
97 }
98 
99 
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.
103  */
104 static kern_return_t
flipc_port_create(ipc_port_t lport,mach_node_t node,mnl_name_t name)105 flipc_port_create(ipc_port_t lport, mach_node_t node, mnl_name_t name)
106 {
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));
112 
113 	/* Allocate and initialize a flipc port */
114 	flipc_port_t fport = zalloc_flags(flipc_port_zone, Z_WAITOK | Z_ZERO);
115 	if (!FPORT_VALID(fport)) {
116 		return KERN_RESOURCE_SHORTAGE;
117 	}
118 	fport->obj.name = name;
119 	fport->hostnode = node;
120 	if (node == localnode) {
121 		fport->state = FPORT_STATE_PRINCIPAL;
122 	} else {
123 		fport->state = FPORT_STATE_PROXY;
124 	}
125 
126 	/* Link co-structures (lport is locked) */
127 	fport->lport = lport;
128 	lport->ip_messages.imq_fport = fport;
129 
130 	/* Add fport to the name hash table; revert link if insert fails */
131 	kern_return_t kr =  mnl_obj_insert((mnl_obj_t)fport);
132 	if (kr != KERN_SUCCESS) {
133 		lport->ip_messages.imq_fport = FPORT_NULL;
134 		fport->lport = IP_NULL;
135 		zfree(flipc_port_zone, fport);
136 	}
137 
138 	return kr;
139 }
140 
141 
142 /*  flipc_port_destroy() is called to convert a flipc port back to a
143  *  local-only ipc port (i.e., the port has no remaining off-node rights).
144  *  This will dispose of any undelivered flipc messages, generating NAKs if
145  *  needed.  <lport> must be locked on entry and is not unlocked on return.
146  */
147 static void
flipc_port_destroy(ipc_port_t lport)148 flipc_port_destroy(ipc_port_t lport)
149 {
150 	/* Ensure parameter is valid, and linked to an fport with a valid name */
151 	assert(IP_VALID(lport));
152 	ipc_mqueue_t port_mq = &lport->ip_messages;
153 	flipc_port_t fport = port_mq->imq_fport;
154 	assert(FPORT_VALID(fport));
155 	assert(MNL_NAME_VALID(fport->obj.name));
156 
157 	/* Dispose of any undelivered messages */
158 	int m = port_mq->imq_msgcount;
159 	if (m > 0) {
160 		ipc_kmsg_t kmsg;
161 #if DEBUG
162 		printf("flipc: destroying %p with %d undelivered msgs\n", lport, m);
163 #endif
164 
165 		/* Logic was lifted from ipc_mqueue_select_on_thread() */
166 		while (m--) {
167 			kmsg = ipc_kmsg_queue_first(&port_mq->imq_messages);
168 			assert(kmsg != IKM_NULL);
169 			ipc_kmsg_rmqueue(&port_mq->imq_messages, kmsg);
170 			if (fport->state == FPORT_STATE_PRINCIPAL) {
171 				flipc_msg_ack(kmsg->ikm_node, port_mq, FALSE);
172 			}
173 			ipc_mqueue_release_msgcount(port_mq);
174 			port_mq->imq_seqno++;
175 		}
176 	}
177 
178 	/* Remove from name hash table, unlink co-structures, and free fport */
179 	mnl_obj_remove(fport->obj.name);
180 	lport->ip_messages.imq_fport = FPORT_NULL;
181 	fport->lport = IP_NULL;
182 	zfree(flipc_port_zone, fport);
183 }
184 
185 
186 /*
187  *	Routine:	flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)
188  *	Purpose:
189  *		Compute the size of the buffer needed to hold the translated flipc
190  *      message.  All identifiers are converted to flipc_names which are 64b.
191  *      If this node's pointers are a different size, we have to allow for
192  *      expansion of the descriptors as appropriate.
193  *	Conditions:
194  *		Nothing locked.
195  *	Returns:
196  *		size of the message as it would be sent over the flipc link.
197  */
198 static mach_msg_size_t
flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)199 flipc_msg_size_from_kmsg(ipc_kmsg_t kmsg)
200 {
201 	mach_msg_size_t fsize = kmsg->ikm_header->msgh_size;
202 
203 	if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
204 		PE_enter_debugger("flipc_msg_size_from_kmsg(): Complex messages not supported.");
205 	}
206 
207 	return fsize;
208 }
209 
210 
211 /*  Translate a kmsg into a flipc msg suitable to transmit over the mach node
212  *  link.  All in-line rights and objects are similarly processed.  If the msg
213  *  moves a receive right, then queued messages may need to be moved as a
214  *  result, causing this function to ultimately be recursive.
215  */
216 static kern_return_t
mnl_msg_from_kmsg(ipc_kmsg_t kmsg,mnl_msg_t * fmsgp)217 mnl_msg_from_kmsg(ipc_kmsg_t kmsg, mnl_msg_t *fmsgp)
218 {
219 	if (kmsg->ikm_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
220 		printf("mnl_msg_from_kmsg(): Complex messages not supported.");
221 		return KERN_FAILURE;
222 	}
223 
224 	mach_msg_size_t fsize = flipc_msg_size_from_kmsg(kmsg);
225 
226 	mnl_msg_t fmsg = mnl_msg_alloc(fsize, 0);
227 
228 	if (fmsg == MNL_MSG_NULL) {
229 		return KERN_RESOURCE_SHORTAGE;
230 	}
231 
232 	/* Setup flipc message header */
233 	fmsg->sub = MACH_NODE_SUB_FLIPC;
234 	fmsg->cmd = FLIPC_CMD_IPCMESSAGE;
235 	fmsg->node_id = localnode_id;   // Message is from us
236 	fmsg->qos = 0; // not used
237 	fmsg->size = fsize; // Payload size (does NOT include mnl_msg header)
238 	fmsg->object = kmsg->ikm_header->msgh_remote_port->ip_messages.imq_fport->obj.name;
239 
240 	/* Copy body of message */
241 	bcopy((const void*)kmsg->ikm_header, (void*)MNL_MSG_PAYLOAD(fmsg), fsize);
242 
243 	// Convert port fields
244 	mach_msg_header_t *mmsg = (mach_msg_header_t*)MNL_MSG_PAYLOAD(fmsg);
245 	mmsg->msgh_remote_port = (mach_port_t)fmsg->object;
246 	mmsg->msgh_local_port = (mach_port_t)
247 	    mnl_name_from_port(mmsg->msgh_local_port);
248 	mmsg->msgh_voucher_port = (mach_port_name_t)MNL_NAME_NULL;
249 
250 	*fmsgp = (mnl_msg_t)fmsg;
251 
252 	return KERN_SUCCESS;
253 }
254 
255 
256 /* lifted from ipc_mig.c:mach_msg_send_from_kernel_proper() */
257 static mach_msg_return_t
mach_msg_send_from_remote_kernel(mach_msg_header_t * msg,mach_msg_size_t send_size,mach_node_t node)258 mach_msg_send_from_remote_kernel(mach_msg_header_t      *msg,
259     mach_msg_size_t        send_size,
260     mach_node_t            node)
261 {
262 	ipc_kmsg_t kmsg;
263 	mach_msg_return_t mr;
264 
265 	mr = ipc_kmsg_get_from_kernel(msg, send_size, &kmsg);
266 	if (mr != MACH_MSG_SUCCESS) {
267 		return mr;
268 	}
269 
270 	mr = ipc_kmsg_copyin_from_kernel(kmsg);
271 	if (mr != MACH_MSG_SUCCESS) {
272 		ipc_kmsg_free(kmsg);
273 		return mr;
274 	}
275 
276 	kmsg->ikm_node = node;  // node that needs to receive message ack
277 	mr = ipc_kmsg_send(kmsg,
278 	    MACH_SEND_KERNEL_DEFAULT,
279 	    MACH_MSG_TIMEOUT_NONE);
280 	if (mr != MACH_MSG_SUCCESS) {
281 		ipc_kmsg_destroy(kmsg);
282 	}
283 
284 	return mr;
285 }
286 
287 
288 /*  Translate a flipc msg <fmsg> into a kmsg and post it to the appropriate
289  *	port.  <node> is the node that originated the message, not necessarily the
290  *	node we received it from.  This will block if the receiving port is full.
291  */
292 static mach_msg_return_t
flipc_cmd_ipc(mnl_msg_t fmsg,mach_node_t node,uint32_t flags __unused)293 flipc_cmd_ipc(mnl_msg_t     fmsg,
294     mach_node_t   node,
295     uint32_t      flags   __unused)
296 {
297 	mach_msg_header_t *mmsg;
298 
299 	// Convert flipc message into mach message in place to avoid alloc/copy
300 	mmsg = (mach_msg_header_t*)MNL_MSG_PAYLOAD(fmsg);
301 	mmsg->msgh_size = fmsg->size;
302 	mmsg->msgh_remote_port = mnl_name_to_port(fmsg->object);
303 	mmsg->msgh_local_port = mnl_name_to_port((mnl_name_t)mmsg->msgh_local_port);
304 	mmsg->msgh_voucher_port = (mach_port_name_t)MACH_PORT_NULL;
305 	mmsg->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
306 	// unchanged: msgh_id
307 
308 	return mach_msg_send_from_remote_kernel(mmsg, fmsg->size, node);
309 }
310 
311 
312 /*  Called when an ACKMESSAGE packet is received. <name> indicates
313  *	the flipc name of the port holding the messages to be acknowledged.
314  *	<msg_count> indicates the number of messages being acked for this node:port.
315  */
316 static void
flipc_cmd_ack(flipc_ack_msg_t fmsg,mach_node_t node __unused,uint32_t flags __unused)317 flipc_cmd_ack(flipc_ack_msg_t   fmsg,
318     mach_node_t       node    __unused,
319     uint32_t          flags   __unused)
320 {
321 	unsigned int msg_count = fmsg->msg_count;
322 	thread_t thread = current_thread();
323 	boolean_t kick = FALSE;
324 
325 	flipc_port_t fport = (flipc_port_t)mnl_obj_lookup(fmsg->mnl.object);
326 
327 	ipc_port_t lport = fport->lport;
328 	ip_mq_lock(lport); // Revisit the lock when enabling flipc
329 
330 	ipc_mqueue_t lport_mq = &lport->ip_messages;
331 
332 	assert(fport->peek_count >= msg_count); // Can't ack what we haven't peeked!
333 
334 	while (msg_count--) {
335 		ipc_mqueue_select_on_thread_locked(lport_mq, NULL, 0, thread);
336 		fport->peek_count--;
337 		kick |= ipc_kmsg_delayed_destroy(thread->ith_kmsg);
338 	}
339 
340 	ip_mq_unlock(lport);
341 
342 	if (kick) {
343 		ipc_kmsg_reap_delayed();
344 	}
345 }
346 
347 
348 
349 /*** FLIPC Node Managment Functions (called by mach node layer) ***/
350 
351 
352 /*  flipc_node_prepare() is called by mach node layer when a remote node is
353  *  registered by a link driver, or when the bootstrap port changes for the
354  *  local node.  This is the flipc layer's opportunity to initialize per-node
355  *  flipc state, and to convert the node's bootstrap port into a flipc port.
356  *  Note that the node is not yet in the mach node table.
357  *  Returns KERN_SUCCESS on success; otherwise node is not prepared.
358  */
359 kern_return_t
flipc_node_prepare(mach_node_t node)360 flipc_node_prepare(mach_node_t node)
361 {
362 	kern_return_t kr;
363 
364 	assert(MACH_NODE_VALID(node));
365 	ipc_port_t bs_port = node->bootstrap_port;
366 	assert(IP_VALID(bs_port));
367 
368 	ip_mq_lock(bs_port);
369 
370 	kr = flipc_port_create(bs_port,
371 	    node,
372 	    MNL_NAME_BOOTSTRAP(node->info.node_id));
373 	ip_mq_unlock(bs_port);
374 
375 	return kr;
376 }
377 
378 
379 /*  flipc_node_retire() is called by mach node layer when a remote node is
380  *  terminated by a link driver, or when the local node's bootstrap port
381  *  becomes invalid.  This is the flipc layer's opportunity to free per-node
382  *  flipc state, and to revert the node's bootstrap port to a local ipc port.
383  *  <node> must be locked by the caller.
384  *  Returns KERN_SUCCESS on success.
385  */
386 kern_return_t
flipc_node_retire(mach_node_t node)387 flipc_node_retire(mach_node_t node)
388 {
389 	if (!MACH_NODE_VALID(node)) {
390 		return KERN_NODE_DOWN;
391 	}
392 
393 	ipc_port_t bs_port = node->bootstrap_port;
394 	if (IP_VALID(bs_port)) {
395 		ip_mq_lock(bs_port);  // Revisit the lock when enabling flipc
396 		flipc_port_destroy(bs_port);
397 		ip_mq_unlock(bs_port);
398 	}
399 
400 	return KERN_SUCCESS;
401 }
402 
403 
404 /*** FLIPC Message Functions (called by mach node layer) ***/
405 
406 
407 /*  The node layer calls flipc_msg_to_remote_node() to fetch the next message
408  *  for <node>.  This function will block until a message is available or the
409  *  node is terminated, in which case it returns MNL_MSG_NULL.
410  */
411 mnl_msg_t
flipc_msg_to_remote_node(mach_node_t to_node,uint32_t flags __unused)412 flipc_msg_to_remote_node(mach_node_t  to_node,
413     uint32_t     flags __unused)
414 {
415 	mach_port_seqno_t msgoff;
416 	ipc_kmsg_t kmsg = IKM_NULL;
417 	mnl_msg_t fmsg = MNL_MSG_NULL;
418 
419 	assert(to_node != localnode);
420 	assert(get_preemption_level() == 0);
421 
422 	struct waitq *pset_waitq = &to_node->proxy_port_set->ips_wqset.wqset_q;
423 	ipc_mqueue_t port_mq = IMQ_NULL;
424 
425 	while (!to_node->dead) {
426 		/* Fetch next message from proxy port */
427 		ipc_mqueue_receive(pset_waitq, MACH_PEEK_MSG, 0, 0, THREAD_ABORTSAFE);
428 
429 		thread_t thread = current_thread();
430 		if (thread->ith_state == MACH_PEEK_READY) {
431 			port_mq = thread->ith_peekq;
432 			thread->ith_peekq = IMQ_NULL;
433 		} else {
434 			panic("Unexpected thread state %d after ipc_mqueue_receive()",
435 			    thread->ith_state);
436 		}
437 
438 		assert(get_preemption_level() == 0);
439 
440 		flipc_port_t fport = port_mq->imq_fport;
441 
442 		if (FPORT_VALID(fport)) {
443 			msgoff = port_mq->imq_fport->peek_count;
444 
445 			ipc_mqueue_peek_locked(port_mq, &msgoff, NULL, NULL, NULL, &kmsg);
446 			if (kmsg != IKM_NULL) {
447 				port_mq->imq_fport->peek_count++;
448 			}
449 
450 			/* Clean up outstanding prepost on port_mq.
451 			 * This also unlocks port_mq.
452 			 */
453 			ipc_mqueue_release_peek_ref(port_mq);
454 			assert(get_preemption_level() == 0);
455 
456 			/* DANGER:  The code below must be allowed to allocate so it can't
457 			 * run under the protection of the imq_lock, but that leaves mqueue
458 			 * open for business for a small window before we examine kmsg.
459 			 * This SHOULD be OK, since we are the only thread looking.
460 			 */
461 			if (kmsg != IKM_NULL) {
462 				mnl_msg_from_kmsg(kmsg, (mnl_msg_t*)&fmsg);
463 			}
464 		} else {
465 			/* Must be from the control_port, which is not a flipc port */
466 			assert(!FPORT_VALID(port_mq->imq_fport));
467 
468 			/* This is a simplified copy of ipc_mqueue_select_on_thread() */
469 			kmsg = ipc_kmsg_queue_first(&port_mq->imq_messages);
470 			assert(kmsg != IKM_NULL);
471 			ipc_kmsg_rmqueue(&port_mq->imq_messages, kmsg);
472 			ipc_mqueue_release_msgcount(port_mq);
473 			counter_inc(&current_task()->messages_received);
474 			ip_release(to_node->control_port); // Should derive ref from port_mq
475 
476 			/* We just pass the kmsg payload as the fmsg.
477 			 * flipc_msg_free() will notice and free the kmsg properly.
478 			 */
479 			mach_msg_header_t *hdr = kmsg->ikm_header;
480 			fmsg = (mnl_msg_t)(&hdr[1]);
481 			/* Stash kmsg pointer just before fmsg */
482 			*(ipc_kmsg_t*)((vm_offset_t)fmsg - sizeof(vm_offset_t)) = kmsg;
483 		}
484 
485 		if (MNL_MSG_VALID(fmsg)) {
486 			break;
487 		}
488 	}
489 	assert(MNL_MSG_VALID(fmsg));
490 	return fmsg;
491 }
492 
493 
494 /*  The mach node layer calls this to deliver an incoming message.  It is the
495  *  responsibility of the caller to release the received message buffer after
496  *  return.
497  */
498 void
flipc_msg_from_node(mach_node_t from_node __unused,mnl_msg_t msg,uint32_t flags)499 flipc_msg_from_node(mach_node_t from_node   __unused,
500     mnl_msg_t   msg,
501     uint32_t    flags)
502 {
503 	/*  Note that if flipc message forwarding is supported, the from_node arg
504 	 *  may not match fmsg->node_id.  The former is the node from which we
505 	 *	received the message; the latter is the node that originated the
506 	 *	message.  We use the originating node, which is where the ack goes.
507 	 */
508 	assert(msg->sub == MACH_NODE_SUB_FLIPC);
509 	mach_node_t node = mach_node_for_id_locked(msg->node_id, FALSE, FALSE);
510 	MACH_NODE_UNLOCK(node);
511 
512 	switch (msg->cmd) {
513 	case FLIPC_CMD_IPCMESSAGE:
514 		flipc_cmd_ipc(msg, node, flags);
515 		break;
516 
517 	case FLIPC_CMD_ACKMESSAGE:
518 	case FLIPC_CMD_NAKMESSAGE:
519 		flipc_cmd_ack((flipc_ack_msg_t)msg, node, flags);
520 		break;
521 
522 	default:
523 #if DEBUG
524 		PE_enter_debugger("flipc_incoming(): Invalid command");
525 #endif
526 		break;
527 	}
528 }
529 
530 
531 /*  The node layer calls flipc_msg_free() to dispose of sent messages that
532  *  originated in the FLIPC layer.  This allows us to repurpose the payload
533  *  of an ack or nak kmsg as a flipc message to avoid a copy - we detect
534  *  such messages here and free them appropriately.
535  */
536 void
flipc_msg_free(mnl_msg_t msg,uint32_t flags)537 flipc_msg_free(mnl_msg_t    msg,
538     uint32_t     flags)
539 {
540 	switch (msg->cmd) {
541 	case FLIPC_CMD_ACKMESSAGE:  // Flipc msg is a kmsg in disguise...
542 	case FLIPC_CMD_NAKMESSAGE:  // Convert back to kmsg for disposal
543 		ipc_kmsg_free(*(ipc_kmsg_t*)((vm_offset_t)msg - sizeof(vm_offset_t)));
544 		break;
545 
546 	default:    // Flipc msg is not a kmsg in disguise; dispose of normally
547 		mnl_msg_free(msg, flags);
548 		break;
549 	}
550 }
551 
552 
553 /*** FLIPC Message Functions (called by mach ipc subsystem) ***/
554 
555 /*	Ack's one message sent to <mqueue> from <node>.  A new kmsg is allocated
556  *  and filled in as an ack, then posted to the node's contol port.  This will
557  *  wake the link driver (if sleeping) and cause the ack to be included with
558  *  normal IPC traffic.
559  *
560  *  This function immediately returns if <fport> or <node> is invalid, so it
561  *  is safe & quick to call speculatively.
562  *
563  *	Called from mach ipc_mqueue.c when a flipc-originated message is consumed.
564  */
565 void
flipc_msg_ack(mach_node_t node,ipc_mqueue_t mqueue,boolean_t delivered)566 flipc_msg_ack(mach_node_t   node,
567     ipc_mqueue_t  mqueue,
568     boolean_t     delivered)
569 {
570 	flipc_port_t fport = mqueue->imq_fport;
571 
572 	assert(FPORT_VALID(fport));
573 	assert(MACH_NODE_VALID(node));
574 
575 	mnl_name_t name = MNL_NAME_NULL;
576 	mach_node_id_t nid = HOST_LOCAL_NODE;
577 	ipc_port_t ack_port = IP_NULL;
578 
579 	ip_mq_lock(fport->lport);
580 	name = fport->obj.name;
581 	ip_mq_unlock(fport->lport);
582 
583 	if (!MNL_NAME_VALID(name)) {
584 		return;
585 	}
586 
587 	MACH_NODE_LOCK(node);
588 	if (node->active) {
589 		nid = node->info.node_id;
590 		ack_port = node->control_port;
591 	}
592 	MACH_NODE_UNLOCK(node);
593 
594 	if (!IP_VALID(ack_port) || !MACH_NODE_ID_VALID(nid)) {
595 		return;
596 	}
597 
598 	/* We have a valid node id & obj name, and a port to send the ack to. */
599 	ipc_kmsg_t kmsg = ipc_kmsg_alloc(sizeof(struct flipc_ack_msg), IPC_KMSG_ALLOC_KERNEL);
600 	assert((unsigned long long)kmsg >= 4ULL);//!= IKM_NULL);
601 	mach_msg_header_t *msg = kmsg->ikm_header;
602 
603 	/* Fill in the mach_msg_header struct */
604 	msg->msgh_bits = MACH_MSGH_BITS_SET(0, 0, 0, 0);
605 	msg->msgh_size = sizeof(msg);
606 	msg->msgh_remote_port = ack_port;
607 	msg->msgh_local_port = MACH_PORT_NULL;
608 	msg->msgh_voucher_port = MACH_PORT_NULL;
609 	msg->msgh_id = FLIPC_CMD_ID;
610 
611 	/* Fill in the flipc_ack_msg struct */
612 	flipc_ack_msg_t fmsg = (flipc_ack_msg_t)(&msg[1]);
613 	fmsg->resend_to = HOST_LOCAL_NODE;
614 	fmsg->msg_count = 1; // Might want to coalesce acks to a node/name pair
615 
616 	/* Fill in the mnl_msg struct */
617 	fmsg->mnl.sub = MACH_NODE_SUB_FLIPC;
618 	fmsg->mnl.cmd = delivered ? FLIPC_CMD_ACKMESSAGE : FLIPC_CMD_NAKMESSAGE;
619 	fmsg->mnl.qos = 0;    // Doesn't do anything yet
620 	fmsg->mnl.flags = 0;
621 	fmsg->mnl.node_id = nid;
622 	fmsg->mnl.object = name;
623 	fmsg->mnl.options = 0;
624 	fmsg->mnl.size = sizeof(struct flipc_ack_msg) - sizeof(struct mnl_msg);
625 
626 #if (0)
627 	mach_msg_return_t mmr;
628 	ipc_mqueue_t ack_mqueue;
629 
630 	ip_mq_lock(ack_port); // Revisit the lock when enabling flipc
631 	ack_mqueue = &ack_port->ip_messages;
632 
633 	/* ipc_mqueue_send() unlocks ack_mqueue */
634 	mmr = ipc_mqueue_send_locked(ack_mqueue, kmsg, 0, 0);
635 #else
636 	kern_return_t kr;
637 	kr = ipc_kmsg_send(kmsg,
638 	    MACH_SEND_KERNEL_DEFAULT,
639 	    MACH_MSG_TIMEOUT_NONE);
640 #endif
641 }
642