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: kern/mach_node.h
29 * Author: Dean Reece
30 * Date: 2016
31 *
32 * Implementation of mach node support.
33 * This is the basis for flipc, which provides inter-node communication.
34 */
35
36
37 #include <mach/mach_types.h>
38 #include <mach/boolean.h>
39 #include <mach/kern_return.h>
40
41 #include <kern/kern_types.h>
42 #include <kern/assert.h>
43
44 #include <kern/host.h>
45 #include <kern/kalloc.h>
46 #include <kern/mach_node_link.h>
47 #include <kern/mach_node.h>
48 #include <kern/ipc_mig.h> // mach_msg_send_from_kernel_proper()
49
50 #include <ipc/port.h>
51 #include <ipc/ipc_types.h>
52 #include <ipc/ipc_init.h>
53 #include <ipc/ipc_kmsg.h>
54 #include <ipc/ipc_port.h>
55 #include <ipc/ipc_pset.h>
56 #include <ipc/ipc_table.h>
57 #include <ipc/ipc_entry.h>
58
59 #include <ipc/flipc.h>
60
61 #include <libkern/OSAtomic.h> // OSAddAtomic64(), OSCompareAndSwap()
62 #include <libkern/OSByteOrder.h> // OSHostByteOrder()
63
64 #pragma pack(4)
65
66 #define MNL_NAME_TABLE_SIZE (256) // Hash is evenly distributed, so ^2 is ok
67 #define MNL_NAME_HASH(name) (name % MNL_NAME_TABLE_SIZE)
68
69 /*** Visible outside mach_node layer ***/
70 mach_node_id_t localnode_id = -1; // This node's FLIPC id.
71 #if MACH_FLIPC
72 mach_node_t localnode; // This node's mach_node_t struct
73
74
75 /*** Private to mach_node layer ***/
76 static int mach_nodes_to_publish;
77 static mach_node_t mach_node_table[MACH_NODES_MAX];
78 static LCK_SPIN_DECLARE_ATTR(mach_node_table_lock_data,
79 &ipc_lck_grp, &ipc_lck_attr);
80 #define MACH_NODE_TABLE_LOCK() lck_spin_lock(&mach_node_table_lock_data)
81 #define MACH_NODE_TABLE_UNLOCK() lck_spin_unlock(&mach_node_table_lock_data)
82
83 static volatile SInt64 mnl_name_next;
84 static queue_head_t mnl_name_table[MNL_NAME_TABLE_SIZE];
85 static LCK_SPIN_DECLARE_ATTR(mnl_name_table_lock_data,
86 &ipc_lck_grp, &ipc_lck_attr);
87 #define MNL_NAME_TABLE_LOCK() lck_spin_lock(&mnl_name_table_lock_data)
88 #define MNL_NAME_TABLE_UNLOCK() lck_spin_unlock(&mnl_name_table_lock_data)
89
90 static void mach_node_init(void);
91 static void mnl_name_table_init(void);
92 static void mach_node_table_init(void);
93 static void mach_node_publish(mach_node_t node);
94
95 static mach_node_t mach_node_alloc_init(mach_node_id_t node_id);
96 static kern_return_t mach_node_register(mach_node_t node);
97
98
99 /* mach_node_init() is run lazily when a node link driver registers
100 * or the node special port is set.
101 * The variable localnode_id is used to determine if init has already run.
102 */
103 void
mach_node_init(void)104 mach_node_init(void)
105 {
106 mach_node_id_t node_id = 0; // TODO: Read from device tree?
107 if (OSCompareAndSwap((UInt32)(HOST_LOCAL_NODE),
108 (UInt32)node_id,
109 &localnode_id)) {
110 printf("mach_node_init(): localnode_id=%d of %d\n",
111 localnode_id, MACH_NODES_MAX);
112 mach_node_table_init();
113 mnl_name_table_init();
114 } // TODO: else block until init is finished (init completion race)
115 }
116
117 void
mach_node_table_init(void)118 mach_node_table_init(void)
119 {
120 MACH_NODE_TABLE_LOCK();
121
122 /* Start with an enpty node table. */
123 bzero(mach_node_table, sizeof(mach_node_t) * MACH_NODES_MAX);
124 mach_nodes_to_publish = 0;
125
126 /* Allocate localnode's struct */
127 localnode = mach_node_for_id_locked(localnode_id, 1, 1);
128 assert(MACH_NODE_VALID(localnode));
129
130 MACH_NODE_TABLE_UNLOCK();
131
132 /* Set up localnode's struct */
133 bzero(localnode, sizeof(*localnode));
134 localnode->info.datamodel = LOCAL_DATA_MODEL;
135 localnode->info.byteorder = OSHostByteOrder();
136 localnode->info.proto_vers_min = MNL_PROTOCOL_V1;
137 localnode->info.proto_vers_max = MNL_PROTOCOL_V1;
138 localnode->proto_vers = MNL_PROTOCOL_V1;
139 localnode->published = 0;
140 localnode->active = 1;
141
142 MACH_NODE_UNLOCK(localnode);
143 }
144
145 /* Sends a publication message to the local node's bootstrap server.
146 * This function is smart and will only send a notification if one as really
147 * needed - it can be called speculatively on any node at any time.
148 *
149 * Note: MUST be called with the node table lock held.
150 */
151
152 void
mach_node_publish(mach_node_t node)153 mach_node_publish(mach_node_t node)
154 {
155 kern_return_t kr;
156
157 if (!MACH_NODE_VALID(node) || (!node->active) || (node->published)) {
158 return; // node is invalid or not suitable for publication
159 }
160 ipc_port_t bs_port = localnode->bootstrap_port;
161 if (!IP_VALID(bs_port)) {
162 return; // No bootstrap server to notify!
163 }
164 /* Node is suitable and server is present, so make registration message */
165 struct mach_node_server_register_msg msg;
166
167 msg.node_header.header.msgh_remote_port = bs_port;
168 msg.node_header.header.msgh_size = sizeof(msg);
169 msg.node_header.header.msgh_local_port = MACH_PORT_NULL;
170 msg.node_header.header.msgh_voucher_port = MACH_PORT_NULL;
171 msg.node_header.header.msgh_id = MACH_NODE_SERVER_MSG_ID;
172 msg.node_header.node_id = node->info.node_id;
173 msg.node_header.options = 0;
174 msg.datamodel = node->info.datamodel;
175 msg.byteorder = node->info.byteorder;
176
177 if (node == localnode) {
178 msg.node_header.identifier = MACH_NODE_SM_REG_LOCAL;
179 msg.node_header.header.msgh_bits =
180 MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
181 } else {
182 msg.node_header.identifier = MACH_NODE_SM_REG_REMOTE;
183 msg.node_header.header.msgh_local_port = node->bootstrap_port;
184 msg.node_header.header.msgh_bits = MACH_MSGH_BITS_SET
185 (MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND, 0, 0);
186 }
187
188 kr = mach_msg_send_from_kernel_proper(&msg.node_header.header,
189 sizeof(msg));
190 if (kr == KERN_SUCCESS) {
191 node->published = 1;
192 mach_nodes_to_publish--;
193 }
194 printf("mach_node_publish(%d)=%d\n", node->info.node_id, kr);
195 }
196
197 /* Called whenever the node special port changes */
198 void
mach_node_port_changed(void)199 mach_node_port_changed(void)
200 {
201 ipc_port_t bs_port;
202
203 mach_node_init(); // Lazy init of mach_node layer
204
205 /* Cleanup previous bootstrap port if necessary */
206 MACH_NODE_LOCK(localnode);
207 flipc_node_retire(localnode);
208 bs_port = localnode->bootstrap_port;
209 if (IP_VALID(bs_port)) {
210 localnode->bootstrap_port = IP_NULL;
211 // TODO: destroy send right to outgoing bs_port
212 }
213
214 kernel_get_special_port(host_priv_self(), HOST_NODE_PORT, &bs_port);
215 assert(IP_VALID(bs_port));
216 localnode->bootstrap_port = bs_port;
217 flipc_node_prepare(localnode);
218 MACH_NODE_UNLOCK(localnode);
219
220 /* Cleanup the publication state of all nodes in the table */
221 MACH_NODE_TABLE_LOCK();
222 // TODO: Signup for bootstrap port death notifications
223 localnode->active = 1;
224
225 mach_nodes_to_publish = 0;
226
227 int n;
228 for (n = 0; n < MACH_NODES_MAX; n++) {
229 mach_node_t np = mach_node_table[n];
230 // Publish all active nodes (except the local node)
231 if (!MACH_NODE_VALID(np)) {
232 continue;
233 }
234 np->published = 0;
235 if (np->active == 1) {
236 mach_nodes_to_publish++;
237 }
238 }
239
240 mach_node_publish(localnode); // Always publish local node first
241
242 for (n = 0; n < MACH_NODES_MAX; n++) {
243 mach_node_publish(mach_node_table[n]);
244 }
245
246 MACH_NODE_TABLE_UNLOCK();
247
248 // TODO: notify all active nodes we are bootstrapped
249 }
250
251 /* Allocate/init a mach_node struct and fill in the node_id field.
252 * This does NOT insert the node struct into the node table.
253 */
254 mach_node_t
mach_node_alloc_init(mach_node_id_t node_id)255 mach_node_alloc_init(mach_node_id_t node_id)
256 {
257 mach_node_t node = MACH_NODE_ALLOC();
258 if (MACH_NODE_VALID(node)) {
259 bzero(node, sizeof(struct mach_node));
260 MACH_NODE_LOCK_INIT(node);
261 node->info.node_id = node_id;
262 }
263 return node;
264 }
265
266
267 /* This function takes a mach_node struct with a completed info field and
268 * registers it with the mach_node and flipc (if flipc is enabled) layers.
269 */
270 kern_return_t
mach_node_register(mach_node_t node)271 mach_node_register(mach_node_t node)
272 {
273 assert(MACH_NODE_VALID(node));
274 mach_node_id_t nid = node->info.node_id;
275 assert(MACH_NODE_ID_VALID(nid));
276
277 kern_return_t kr;
278 ipc_space_t proxy_space = IS_NULL;
279 ipc_pset_t pp_set = IPS_NULL; // pset for proxy ports
280 ipc_port_t bs_port = MACH_PORT_NULL;
281 ipc_port_t ack_port = MACH_PORT_NULL;
282
283 printf("mach_node_register(%d)\n", nid);
284
285 /* TODO: Support non-native byte order and data models */
286 if ((node->info.byteorder != OSHostByteOrder()) ||
287 (node->info.datamodel != LOCAL_DATA_MODEL)) {
288 printf("mach_node_register: unsupported byte order (%d) or width (%d)",
289 node->info.byteorder, node->info.datamodel);
290 return KERN_INVALID_ARGUMENT;
291 }
292
293 /* Create the space that holds all local rights assigned to <nid> */
294 kr = ipc_space_create_special(&proxy_space);
295 if (kr != KERN_SUCCESS) {
296 goto out;
297 }
298 proxy_space->is_node_id = nid;
299
300 /* Create the bootstrap proxy port for this remote node */
301 bs_port = ipc_port_alloc_special(proxy_space, IPC_PORT_INIT_MESSAGE_QUEUE);
302 if (bs_port == MACH_PORT_NULL) {
303 kr = KERN_RESOURCE_SHORTAGE;
304 goto out;
305 }
306
307 /* Create the control (ack) port for this remote node */
308 ack_port = ipc_port_alloc_special(proxy_space, IPC_PORT_INIT_MESSAGE_QUEUE);
309 if (ack_port == MACH_PORT_NULL) {
310 kr = KERN_RESOURCE_SHORTAGE;
311 goto out;
312 }
313
314 /* Create the set that holds all proxy ports for this remote node */
315 pp_set = ipc_pset_alloc_special(proxy_space);
316 if (pp_set == IPS_NULL) {
317 kr = KERN_RESOURCE_SHORTAGE;
318 goto out;
319 }
320
321 waitq_set_lazy_init_link(&pp_set->ips_wqset);
322 /* Add the bootstrap port to the proxy port set */
323 waitq_link_t link = waitq_link_alloc(WQT_PORT_SET);
324 ip_mq_lock(bs_port);
325 ips_mq_lock(pp_set); // Revisit the lock when enabling flipc
326 ipc_mqueue_add_locked(bs_port, pp_set, &link);
327 ips_mq_unlock(pp_set);
328 ip_mq_unlock(bs_port);
329
330 /* Add the control port to the proxy port set */
331 if (link.wqlh == NULL) {
332 link = waitq_link_alloc(WQT_PORT_SET);
333 }
334 ip_mq_lock(ack_port);
335 ips_mq_lock(pp_set); // Revisit the lock when enabling flipc
336 ipc_mqueue_add_locked(ack_port, pp_set, &link);
337 ips_mq_unlock(pp_set);
338 ips_mq_unlock(ack_port);
339
340 if (link.wqlh) {
341 waitq_link_free(WQT_PORT_SET, link);
342 }
343
344 // Setup mach_node struct
345 node->published = 0;
346 node->active = 1;
347 node->proxy_space = proxy_space;
348 node->proxy_port_set = pp_set;
349 node->bootstrap_port = bs_port;
350 node->proto_vers = node->info.proto_vers_max;
351 node->control_port = ack_port;
352
353 // Place new mach_node struct into node table
354 MACH_NODE_TABLE_LOCK();
355
356 mach_node_t old_node = mach_node_table[nid];
357 if (!MACH_NODE_VALID(old_node) || (old_node->dead)) {
358 node->antecedent = old_node;
359 flipc_node_prepare(node);
360 mach_node_table[nid] = node;
361 mach_nodes_to_publish++;
362 mach_node_publish(node);
363 kr = KERN_SUCCESS;
364 } else {
365 printf("mach_node_register: id %d already active!", nid);
366 kr = KERN_FAILURE;
367 }
368 MACH_NODE_TABLE_UNLOCK();
369
370 out:
371 if (kr != KERN_SUCCESS) { // Dispose of whatever we allocated
372 if (pp_set) {
373 ips_mq_lock(pp_set);
374 ipc_pset_destroy(proxy_space, pp_set);
375 }
376
377 if (bs_port) {
378 ipc_port_dealloc_special(bs_port, proxy_space);
379 }
380
381 if (ack_port) {
382 ipc_port_dealloc_special(ack_port, proxy_space);
383 }
384
385 if (proxy_space) {
386 ipc_space_terminate(proxy_space);
387 }
388 }
389
390 return kr;
391 }
392
393
394 /* Gets or allocates a locked mach_node struct for the specified <node_id>.
395 * The current node is locked and returned if it is not dead, or if it is dead
396 * and <alloc_if_dead> is false. A new node struct is allocated, locked and
397 * returned if the node is dead and <alloc_if_dead> is true, or if the node
398 * is absent and <alloc_if_absent> is true. MACH_NODE_NULL is returned if
399 * the node is absent and <alloc_if_absent> is false. MACH_NODE_NULL is also
400 * returned if a new node structure was not able to be allocated.
401 *
402 * Note: This function must be called with the node table lock held!
403 */
404 mach_node_t
mach_node_for_id_locked(mach_node_id_t node_id,boolean_t alloc_if_dead,boolean_t alloc_if_absent)405 mach_node_for_id_locked(mach_node_id_t node_id,
406 boolean_t alloc_if_dead,
407 boolean_t alloc_if_absent)
408 {
409 if ((node_id < 0) || (node_id >= MACH_NODES_MAX)) {
410 return MACH_NODE_NULL;
411 }
412
413 mach_node_t node = mach_node_table[node_id];
414
415 if ((!MACH_NODE_VALID(node) && alloc_if_absent) ||
416 (MACH_NODE_VALID(node) && node->dead && alloc_if_dead)) {
417 node = mach_node_alloc_init(node_id);
418 if (MACH_NODE_VALID(node)) {
419 node->antecedent = mach_node_table[node_id];
420 mach_node_table[node_id] = node;
421 }
422 }
423
424 if (MACH_NODE_VALID(node)) {
425 MACH_NODE_LOCK(node);
426 }
427
428 return node;
429 }
430
431
432
433 /*** Mach Node Link Name and Hash Table Implementation ***/
434
435 /* Allocate a new unique name and return it.
436 * Dispose of this with mnl_name_free().
437 * Returns MNL_NAME_NULL on failure.
438 */
439 mnl_name_t
mnl_name_alloc(void)440 mnl_name_alloc(void)
441 {
442 return (mnl_name_t)OSAddAtomic64(MACH_NODES_MAX, &mnl_name_next);
443 }
444
445
446 /* Deallocate a unique name that was allocated via mnl_name_alloc().
447 */
448 void
mnl_name_free(mnl_name_t name __unused)449 mnl_name_free(mnl_name_t name __unused)
450 {
451 ; // Nothing to do for now since we don't recycle mnl names.
452 }
453
454
455 /* Called once from mach_node_init(), this sets up the hash table structures.
456 */
457 void
mnl_name_table_init(void)458 mnl_name_table_init(void)
459 {
460 MNL_NAME_TABLE_LOCK();
461
462 // Set the first name to this node's bootstrap name
463 mnl_name_next = localnode_id + MACH_NODES_MAX;
464
465 for (int i = 0; i < MNL_NAME_TABLE_SIZE; i++) {
466 queue_head_init(mnl_name_table[i]);
467 }
468
469 MNL_NAME_TABLE_UNLOCK();
470 }
471
472
473 /* Initialize the data structures in the mnl_obj structure at the head of the
474 * provided object. This should be called on an object before it is passed to
475 * any other mnl_obj* routine.
476 */
477 void
mnl_obj_init(mnl_obj_t obj)478 mnl_obj_init(mnl_obj_t obj)
479 {
480 queue_chain_init(obj->links);
481 obj->name = MNL_NAME_NULL;
482 }
483
484
485 /* Search the local node's hash table for the object associated with a
486 * mnl_name_t and return it. Returns MNL_NAME_NULL on failure.
487 */
488 mnl_obj_t
mnl_obj_lookup(mnl_name_t name)489 mnl_obj_lookup(mnl_name_t name)
490 {
491 mnl_obj_t obj = MNL_OBJ_NULL;
492
493 if (name != MNL_NAME_NULL) {
494 qe_foreach_element(obj, &mnl_name_table[MNL_NAME_HASH(name)], links) {
495 if (obj->name == name) {
496 break;
497 }
498 }
499 }
500 return obj;
501 }
502
503
504 /* Search the local node's hash table for the object associated with a
505 * mnl_name_t and remove it. The pointer to the removed object is returned so
506 * that the caller can appropriately dispose of the object.
507 * Returns MNL_NAME_NULL on failure.
508 */
509 mnl_obj_t
mnl_obj_remove(mnl_name_t name)510 mnl_obj_remove(mnl_name_t name)
511 {
512 mnl_obj_t obj = MNL_OBJ_NULL;
513
514 if (name != MNL_NAME_NULL) {
515 qe_foreach_element_safe(obj, &mnl_name_table[MNL_NAME_HASH(name)], links) {
516 if (obj->name == name) {
517 remqueue(&obj->links);
518 }
519 }
520 }
521 return obj;
522 }
523
524
525 /* Insert an object into the local node's hash table. If the name of the
526 * provided object is MNL_NAME_NULL then a new mnl_name is allocated and
527 * assigned to the object.
528 * Returns KERN_SUCCESS if obj was added to hash table
529 * Returns KERN_INVALID_ARGUMENT if obj is invalid
530 * Returns KERN_NAME_EXISTS if obj's name already exists in hash table
531 */
532 kern_return_t
mnl_obj_insert(mnl_obj_t obj)533 mnl_obj_insert(mnl_obj_t obj)
534 {
535 if (!MNL_OBJ_VALID(obj)) {
536 return KERN_INVALID_ARGUMENT;
537 }
538
539 MNL_NAME_TABLE_LOCK();
540
541 if (!MNL_NAME_VALID(obj->name)) {
542 // obj is unnammed, so lets allocate a fresh one
543 obj->name = mnl_name_alloc();
544 }
545
546 enqueue(&mnl_name_table[MNL_NAME_HASH(obj->name)], &obj->links);
547 MNL_NAME_TABLE_UNLOCK();
548
549 if (obj->name >= (MACH_NODES_MAX << 1)) {
550 panic("Unexpected MNL_NAME %lld in obj %p", obj->name, obj);
551 }
552
553 return KERN_SUCCESS;
554 }
555
556
557 /*** Mach Node Link Driver Interface Implementation ***/
558
559 /* Allocate a mnl_msg struct plus additional payload. Link drivers are not
560 * required to use this to allocate messages; any wired and mapped kernel
561 * memory is acceptable.
562 *
563 * Arguments:
564 * payload Number of additional bytes to allocate for message payload
565 * flags Currently unused; 0 should be passed
566 *
567 * Return values:
568 * MNL_MSG_NULL: Allocation failed
569 * *: Pointer to new mnl_msg struct of requested size
570 */
571 mnl_msg_t
mnl_msg_alloc(int payload,uint32_t flags __unused)572 mnl_msg_alloc(int payload,
573 uint32_t flags __unused)
574 {
575 mnl_msg_t msg = kalloc(MNL_MSG_SIZE + payload);
576
577 if (MNL_MSG_VALID(msg)) {
578 bzero(msg, MNL_MSG_SIZE); // Only zero the header
579 msg->size = payload;
580 }
581
582 return msg;
583 }
584
585
586 /* Free a mnl_msg struct allocated by mnl_msg_alloc().
587 *
588 * Arguments:
589 * msg Pointer to the message buffer to be freed
590 * flags Currently unused; 0 should be passed
591 */
592 void
mnl_msg_free(mnl_msg_t msg,uint32_t flags __unused)593 mnl_msg_free(mnl_msg_t msg,
594 uint32_t flags __unused)
595 {
596 if (MNL_MSG_VALID(msg)) {
597 kfree(msg, MNL_MSG_SIZE + msg->size);
598 }
599 }
600
601
602 /* The link driver calls this to setup a new (or restarted) node, and to get
603 * an mnl_node_info struct for use as a parameter to other mnl functions.
604 * If MNL_NODE_NULL is returned, the operation failed. Otherwise, a pointer
605 * to a new mnl_node struct is returned. The caller should set all fields
606 * in the structure, then call mnl_register() to complete node registration.
607 *
608 * Arguments:
609 * nid The id of the node to be instantiated
610 * flags Currently unused; 0 should be passed
611 *
612 * Return values:
613 * MNL_NODE_NULL: Operation failed
614 * *: Pointer to a new mnl_node struct
615 */
616 mnl_node_info_t
mnl_instantiate(mach_node_id_t nid,uint32_t flags __unused)617 mnl_instantiate(mach_node_id_t nid,
618 uint32_t flags __unused)
619 {
620 mach_node_init(); // Lazy init of mach_node layer
621
622 if ((nid == localnode_id) || !MACH_NODE_ID_VALID(nid)) {
623 return MNL_NODE_NULL;
624 }
625
626 return (mnl_node_info_t)mach_node_alloc_init(nid);
627 }
628
629 /* The link driver calls mnl_register() to complete the node registration
630 * process. KERN_SUCCESS is returned if registration succeeded, otherwise
631 * an error is returned.
632 *
633 * Arguments:
634 * node Pointer to the node's mnl_node structure
635 * flags Currently unused; 0 should be passed
636 *
637 * Return values:
638 * KERN_SUCCESS: Registration succeeded
639 * KERN_INVALID_ARGUMENT: Field(s) in <node> contained unacceptable values
640 * KERN_*: Values returned from underlying functions
641 */
642 kern_return_t
mnl_register(mnl_node_info_t node,uint32_t flags __unused)643 mnl_register(mnl_node_info_t node,
644 uint32_t flags __unused)
645 {
646 if (MNL_NODE_VALID(node) && (node->node_id != localnode_id)) {
647 return mach_node_register((mach_node_t)node);
648 }
649
650 return KERN_INVALID_ARGUMENT;
651 }
652
653
654 /* The link driver calls this to report that the link has been raised in one
655 * or both directions. If the link is two uni-directional channels, each link
656 * driver will independently call this function, each only raising the link
657 * they are responsible for. The mach_node layer will not communicate with
658 * the remote node until both rx and tx links are up.
659 *
660 * Arguments:
661 * node Pointer to the node's mnl_node structure
662 * link Indicates which link(s) are up (see MNL_LINK_* defines)
663 * flags Currently unused; 0 should be passed
664 *
665 * Return values:
666 * KERN_SUCCESS: Link state changed successfully.
667 * KERN_INVALID_ARGUMENT: An argument value was not allowed.
668 * KERN_*: Values returned from underlying functions.
669 */
670 kern_return_t
mnl_set_link_state(mnl_node_info_t node,int link,uint32_t flags __unused)671 mnl_set_link_state(mnl_node_info_t node,
672 int link,
673 uint32_t flags __unused)
674 {
675 kern_return_t kr;
676 mach_node_t mnode = (mach_node_t)node;
677
678 if (!MACH_NODE_VALID(mnode) || !(link & MNL_LINK_UP) || (link & mnode->link)) {
679 return KERN_INVALID_ARGUMENT; // bad node, or bad link argument
680 }
681 MACH_NODE_LOCK(mnode);
682
683 if (mnode->dead) {
684 kr = KERN_NODE_DOWN;
685 } else {
686 mnode->link |= link;
687 kr = KERN_SUCCESS;
688 }
689
690 MACH_NODE_UNLOCK(mnode);
691
692 return kr;
693 }
694
695 /* The link driver calls this to indicate a node has terminated and is no
696 * longer available for messaging. This may be due to a crash or an orderly
697 * shutdown, but either way the remote node no longer retains any state about
698 * the remaining nodes. References held on behalf of the terminated node
699 * will be cleaned up. After this is called, both the rx and tx links are
700 * marked as down. If the remote node restarts, the link driver can bring
701 * up the link using mnl_instantiate() again.
702 *
703 * Arguments:
704 * node Pointer to the node's mnl_node structure
705 * flags Currently unused; 0 should be passed
706 *
707 * Return values:
708 * KERN_SUCCESS: Node was terminated.
709 * KERN_INVALID_ARGUMENT: Node id was invalid or non-existant.
710 * KERN_*: Values returned from underlying functions.
711 */
712 kern_return_t
mnl_terminate(mnl_node_info_t node,uint32_t flags __unused)713 mnl_terminate(mnl_node_info_t node,
714 uint32_t flags __unused)
715 {
716 kern_return_t kr = KERN_SUCCESS;
717 mach_node_t mnode = (mach_node_t)node;
718
719 if (!MACH_NODE_VALID(mnode)) {
720 return KERN_INVALID_ARGUMENT; // bad node
721 }
722 MACH_NODE_LOCK(mnode);
723 if (mnode->dead) {
724 kr = KERN_NODE_DOWN; // node is already terminated
725 goto unlock;
726 }
727
728 mnode->link = MNL_LINK_DOWN;
729 mnode->active = 0;
730 mnode->suspended = 0;
731 mnode->dead = 1;
732
733 flipc_node_retire(mnode);
734
735 // Wake any threads sleeping on the proxy port set
736 if (mnode->proxy_port_set != IPS_NULL) {
737 ips_mq_lock(mnode->proxy_port_set);
738 ipc_pset_destroy(mnode->proxy_space, mnode->proxy_port_set);
739 mnode->proxy_port_set = IPS_NULL;
740 }
741
742 // TODO: Inform node name server (if registered) of termination
743
744 unlock:
745 MACH_NODE_UNLOCK(mnode);
746 return kr;
747 }
748
749
750 /* The link driver calls this to deliver an incoming message. Note that the
751 * link driver must dispose of the memory pointed to by <msg> after the
752 * function call returns.
753 *
754 * Arguments:
755 * node Pointer to the node's mnl_node structure
756 * msg Pointer to the message buffer
757 * flags Currently unused; 0 should be passed
758 */
759 void
mnl_msg_from_node(mnl_node_info_t node __unused,mnl_msg_t msg,uint32_t flags __unused)760 mnl_msg_from_node(mnl_node_info_t node __unused,
761 mnl_msg_t msg,
762 uint32_t flags __unused)
763 {
764 assert(MNL_MSG_VALID(msg));
765 assert(MACH_NODE_ID_VALID(msg->node_id));
766 assert(MNL_NODE_VALID(node));
767
768 /* If node message forwarding is supported, the from_node_id arg may not
769 * match fmsg->info.node_id. The former is the node from which we received
770 * the message; the latter is the node that generated the message originally.
771 * We always use fmsg->info.node_id, which is where the ack needs to go.
772 */
773
774 switch (msg->sub) {
775 case MACH_NODE_SUB_FLIPC:
776 flipc_msg_from_node((mach_node_t)node, msg, flags);
777 break;
778
779 default:
780 #if DEBUG
781 PE_enter_debugger("mnl_msg_from_node(): Invalid subsystem");
782 #endif
783 break;
784 }
785 }
786
787
788 /* The link driver calls this to fetch the next message to transmit.
789 * This function will block until a message is available, or will return
790 * FLIPC_MSG_NULL if the link is to be terminated. After the caller has
791 * completed the transmission and no longer needs the msg buffer, it should
792 * call mnl_msg_complete().
793 *
794 * Arguments:
795 * node Pointer to the node's mnl_node structure
796 * flags Currently unused; 0 should be passed
797 */
798 mnl_msg_t
mnl_msg_to_node(mnl_node_info_t node __unused,uint32_t flags __unused)799 mnl_msg_to_node(mnl_node_info_t node __unused,
800 uint32_t flags __unused)
801 {
802 assert(MNL_NODE_VALID(node));
803
804 #if DEBUG
805 thread_set_thread_name(current_thread(), "MNL_Link");
806 #endif
807
808 return flipc_msg_to_remote_node((mach_node_t)node, 0);
809 }
810
811
812 /* The link driver calls this to indicate that the specified msg buffer has
813 * been sent over the link and can be deallocated.
814 *
815 * Arguments:
816 * node Pointer to the node's mnl_node structure
817 * msg Pointer to the message buffer
818 * flags Currently unused; 0 should be passed
819 */
820 void
mnl_msg_complete(mnl_node_info_t node __unused,mnl_msg_t msg,uint32_t flags)821 mnl_msg_complete(mnl_node_info_t node __unused,
822 mnl_msg_t msg,
823 uint32_t flags)
824 {
825 switch (msg->sub) {
826 case MACH_NODE_SUB_NODE:
827 mnl_msg_free(msg, flags);
828 break;
829
830 case MACH_NODE_SUB_FLIPC:
831 flipc_msg_free(msg, flags);
832 break;
833
834 default:
835 #if DEBUG
836 PE_enter_debugger("mnl_msg_complete(): Invalid subsystem");
837 #endif
838 break;
839 }
840 }
841
842 #else // MACH_FLIPC not configured, so provide KPI stubs
843
844 mnl_msg_t
mnl_msg_alloc(int payload __unused,uint32_t flags __unused)845 mnl_msg_alloc(int payload __unused, uint32_t flags __unused)
846 {
847 return MNL_MSG_NULL;
848 }
849
850 void
mnl_msg_free(mnl_msg_t msg __unused,uint32_t flags __unused)851 mnl_msg_free(mnl_msg_t msg __unused, uint32_t flags __unused)
852 {
853 return;
854 }
855
856 mnl_node_info_t
mnl_instantiate(mach_node_id_t nid __unused,uint32_t flags __unused)857 mnl_instantiate(mach_node_id_t nid __unused, uint32_t flags __unused)
858 {
859 return MNL_NODE_NULL;
860 }
861
862 kern_return_t
mnl_register(mnl_node_info_t node __unused,uint32_t flags __unused)863 mnl_register(mnl_node_info_t node __unused, uint32_t flags __unused)
864 {
865 return KERN_FAILURE;
866 }
867
868 kern_return_t
mnl_set_link_state(mnl_node_info_t node __unused,int link __unused,uint32_t flags __unused)869 mnl_set_link_state(mnl_node_info_t node __unused,
870 int link __unused,
871 uint32_t flags __unused)
872 {
873 return KERN_FAILURE;
874 }
875
876 kern_return_t
mnl_terminate(mnl_node_info_t node __unused,uint32_t flags __unused)877 mnl_terminate(mnl_node_info_t node __unused, uint32_t flags __unused)
878 {
879 return KERN_FAILURE;
880 }
881
882 void
mnl_msg_from_node(mnl_node_info_t node __unused,mnl_msg_t msg __unused,uint32_t flags __unused)883 mnl_msg_from_node(mnl_node_info_t node __unused,
884 mnl_msg_t msg __unused,
885 uint32_t flags __unused)
886 {
887 return;
888 }
889
890 mnl_msg_t
mnl_msg_to_node(mnl_node_info_t node __unused,uint32_t flags __unused)891 mnl_msg_to_node(mnl_node_info_t node __unused, uint32_t flags __unused)
892 {
893 return MNL_MSG_NULL;
894 }
895
896 void
mnl_msg_complete(mnl_node_info_t node __unused,mnl_msg_t msg __unused,uint32_t flags __unused)897 mnl_msg_complete(mnl_node_info_t node __unused,
898 mnl_msg_t msg __unused,
899 uint32_t flags __unused)
900 {
901 return;
902 }
903
904 #endif // MACH_FLIPC
905