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