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(¤t_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