xref: /xnu-8020.140.41/osfmk/kern/mach_node.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc) !
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