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