]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/mach_node.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / kern / mach_node.c
CommitLineData
39037602 1/*
f427ee49 2 * Copyright (c) 2015-2020 Apple Inc. All rights reserved.
39037602
A
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
0a7de745 61#include <libkern/OSAtomic.h> // OSAddAtomic64(), OSCompareAndSwap()
39037602
A
62#include <libkern/OSByteOrder.h> // OSHostByteOrder()
63
64#pragma pack(4)
65
0a7de745
A
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)
39037602
A
68
69/*** Visible outside mach_node layer ***/
0a7de745 70mach_node_id_t localnode_id = -1; // This node's FLIPC id.
39037602 71#if MACH_FLIPC
0a7de745 72mach_node_t localnode; // This node's mach_node_t struct
39037602
A
73
74
75/*** Private to mach_node layer ***/
f427ee49
A
76static int mach_nodes_to_publish;
77static mach_node_t mach_node_table[MACH_NODES_MAX];
78static LCK_SPIN_DECLARE_ATTR(mach_node_table_lock_data,
79 &ipc_lck_grp, &ipc_lck_attr);
39037602
A
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)
39037602 82
0a7de745 83static volatile SInt64 mnl_name_next;
f427ee49
A
84static queue_head_t mnl_name_table[MNL_NAME_TABLE_SIZE];
85static LCK_SPIN_DECLARE_ATTR(mnl_name_table_lock_data,
86 &ipc_lck_grp, &ipc_lck_attr);
39037602
A
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)
39037602
A
89
90static void mach_node_init(void);
91static void mnl_name_table_init(void);
92static void mach_node_table_init(void);
93static void mach_node_publish(mach_node_t node);
94
95static mach_node_t mach_node_alloc_init(mach_node_id_t node_id);
96static 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 */
103void
104mach_node_init(void)
105{
0a7de745 106 mach_node_id_t node_id = 0; // TODO: Read from device tree?
39037602 107 if (OSCompareAndSwap((UInt32)(HOST_LOCAL_NODE),
0a7de745
A
108 (UInt32)node_id,
109 &localnode_id)) {
39037602 110 printf("mach_node_init(): localnode_id=%d of %d\n",
0a7de745 111 localnode_id, MACH_NODES_MAX);
39037602
A
112 mach_node_table_init();
113 mnl_name_table_init();
0a7de745 114 } // TODO: else block until init is finished (init completion race)
39037602
A
115}
116
117void
118mach_node_table_init(void)
119{
0a7de745 120 MACH_NODE_TABLE_LOCK();
39037602 121
0a7de745
A
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;
39037602 125
0a7de745
A
126 /* Allocate localnode's struct */
127 localnode = mach_node_for_id_locked(localnode_id, 1, 1);
128 assert(MACH_NODE_VALID(localnode));
39037602 129
0a7de745 130 MACH_NODE_TABLE_UNLOCK();
39037602 131
0a7de745 132 /* Set up localnode's struct */
f427ee49 133 bzero(localnode, sizeof(*localnode));
0a7de745
A
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;
39037602
A
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
152void
153mach_node_publish(mach_node_t node)
154{
0a7de745
A
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);
39037602
A
195}
196
197/* Called whenever the node special port changes */
198void
199mach_node_port_changed(void)
200{
201 ipc_port_t bs_port;
0a7de745 202
39037602 203 mach_node_init(); // Lazy init of mach_node layer
0a7de745 204
39037602 205 /* Cleanup previous bootstrap port if necessary */
0a7de745
A
206 MACH_NODE_LOCK(localnode);
207 flipc_node_retire(localnode);
39037602
A
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 }
0a7de745 213
39037602
A
214 kernel_get_special_port(host_priv_self(), HOST_NODE_PORT, &bs_port);
215 assert(IP_VALID(bs_port));
0a7de745
A
216 localnode->bootstrap_port = bs_port;
217 flipc_node_prepare(localnode);
218 MACH_NODE_UNLOCK(localnode);
39037602
A
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;
0a7de745 224
39037602 225 mach_nodes_to_publish = 0;
0a7de745 226
39037602 227 int n;
0a7de745
A
228 for (n = 0; n < MACH_NODES_MAX; n++) {
229 mach_node_t np = mach_node_table[n];
39037602 230 // Publish all active nodes (except the local node)
0a7de745 231 if (!MACH_NODE_VALID(np)) {
39037602 232 continue;
0a7de745 233 }
39037602 234 np->published = 0;
0a7de745 235 if (np->active == 1) {
39037602 236 mach_nodes_to_publish++;
0a7de745 237 }
39037602 238 }
0a7de745 239
39037602 240 mach_node_publish(localnode); // Always publish local node first
39037602 241
0a7de745
A
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
39037602
A
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 */
254mach_node_t
255mach_node_alloc_init(mach_node_id_t node_id)
256{
0a7de745
A
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;
39037602
A
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 */
270kern_return_t
0a7de745
A
271mach_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 */
94ff46dc 301 bs_port = ipc_port_alloc_special(proxy_space, IPC_PORT_INIT_MESSAGE_QUEUE);
0a7de745
A
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 */
94ff46dc 308 ack_port = ipc_port_alloc_special(proxy_space, IPC_PORT_INIT_MESSAGE_QUEUE);
0a7de745
A
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);
322 /* Add the bootstrap port to the proxy port set */
323 uint64_t wq_link_id = waitq_link_reserve(NULL);
324 uint64_t wq_reserved_prepost = waitq_prepost_reserve(NULL, 10,
325 WAITQ_DONT_LOCK);
326 ips_lock(pp_set);
327 ip_lock(bs_port);
328 ipc_pset_add(pp_set,
329 bs_port,
330 &wq_link_id,
331 &wq_reserved_prepost);
332 ip_unlock(bs_port);
333 ips_unlock(pp_set);
334
335 waitq_link_release(wq_link_id);
336 waitq_prepost_release_reserve(wq_reserved_prepost);
337
338 /* Add the control port to the proxy port set */
339 wq_link_id = waitq_link_reserve(NULL);
340 wq_reserved_prepost = waitq_prepost_reserve(NULL, 10,
341 WAITQ_DONT_LOCK);
342 ips_lock(pp_set);
343 ip_lock(ack_port);
344 ipc_pset_add(pp_set,
345 ack_port,
346 &wq_link_id,
347 &wq_reserved_prepost);
348 ip_unlock(ack_port);
349 ips_unlock(pp_set);
350
351 waitq_link_release(wq_link_id);
352 waitq_prepost_release_reserve(wq_reserved_prepost);
353
354 // Setup mach_node struct
355 node->published = 0;
356 node->active = 1;
357 node->proxy_space = proxy_space;
358 node->proxy_port_set = pp_set;
359 node->bootstrap_port = bs_port;
360 node->proto_vers = node->info.proto_vers_max;
361 node->control_port = ack_port;
362
363 // Place new mach_node struct into node table
364 MACH_NODE_TABLE_LOCK();
365
366 mach_node_t old_node = mach_node_table[nid];
367 if (!MACH_NODE_VALID(old_node) || (old_node->dead)) {
368 node->antecedent = old_node;
369 flipc_node_prepare(node);
370 mach_node_table[nid] = node;
371 mach_nodes_to_publish++;
372 mach_node_publish(node);
373 kr = KERN_SUCCESS;
374 } else {
375 printf("mach_node_register: id %d already active!", nid);
376 kr = KERN_FAILURE;
377 }
378 MACH_NODE_TABLE_UNLOCK();
39037602
A
379
380out:
0a7de745
A
381 if (kr != KERN_SUCCESS) { // Dispose of whatever we allocated
382 if (pp_set) {
383 ips_lock(pp_set);
384 ipc_pset_destroy(proxy_space, pp_set);
385 }
39037602 386
0a7de745
A
387 if (bs_port) {
388 ipc_port_dealloc_special(bs_port, proxy_space);
389 }
39037602 390
0a7de745
A
391 if (ack_port) {
392 ipc_port_dealloc_special(ack_port, proxy_space);
393 }
39037602 394
0a7de745
A
395 if (proxy_space) {
396 ipc_space_terminate(proxy_space);
397 }
398 }
39037602 399
0a7de745 400 return kr;
39037602
A
401}
402
403
404/* Gets or allocates a locked mach_node struct for the specified <node_id>.
405 * The current node is locked and returned if it is not dead, or if it is dead
406 * and <alloc_if_dead> is false. A new node struct is allocated, locked and
407 * returned if the node is dead and <alloc_if_dead> is true, or if the node
408 * is absent and <alloc_if_absent> is true. MACH_NODE_NULL is returned if
409 * the node is absent and <alloc_if_absent> is false. MACH_NODE_NULL is also
410 * returned if a new node structure was not able to be allocated.
411 *
412 * Note: This function must be called with the node table lock held!
413 */
414mach_node_t
0a7de745
A
415mach_node_for_id_locked(mach_node_id_t node_id,
416 boolean_t alloc_if_dead,
417 boolean_t alloc_if_absent)
39037602 418{
0a7de745 419 if ((node_id < 0) || (node_id >= MACH_NODES_MAX)) {
39037602 420 return MACH_NODE_NULL;
0a7de745 421 }
39037602
A
422
423 mach_node_t node = mach_node_table[node_id];
0a7de745
A
424
425 if ((!MACH_NODE_VALID(node) && alloc_if_absent) ||
426 (MACH_NODE_VALID(node) && node->dead && alloc_if_dead)) {
39037602
A
427 node = mach_node_alloc_init(node_id);
428 if (MACH_NODE_VALID(node)) {
429 node->antecedent = mach_node_table[node_id];
430 mach_node_table[node_id] = node;
431 }
432 }
0a7de745
A
433
434 if (MACH_NODE_VALID(node)) {
39037602 435 MACH_NODE_LOCK(node);
0a7de745
A
436 }
437
39037602
A
438 return node;
439}
440
441
442
443/*** Mach Node Link Name and Hash Table Implementation ***/
444
445/* Allocate a new unique name and return it.
446 * Dispose of this with mnl_name_free().
447 * Returns MNL_NAME_NULL on failure.
448 */
449mnl_name_t
450mnl_name_alloc(void)
451{
452 return (mnl_name_t)OSAddAtomic64(MACH_NODES_MAX, &mnl_name_next);
453}
454
455
456/* Deallocate a unique name that was allocated via mnl_name_alloc().
457 */
458void
459mnl_name_free(mnl_name_t name __unused)
460{
0a7de745 461 ; // Nothing to do for now since we don't recycle mnl names.
39037602
A
462}
463
464
465/* Called once from mach_node_init(), this sets up the hash table structures.
466 */
467void
468mnl_name_table_init(void)
469{
0a7de745
A
470 MNL_NAME_TABLE_LOCK();
471
39037602
A
472 // Set the first name to this node's bootstrap name
473 mnl_name_next = localnode_id + MACH_NODES_MAX;
0a7de745
A
474
475 for (int i = 0; i < MNL_NAME_TABLE_SIZE; i++) {
39037602 476 queue_head_init(mnl_name_table[i]);
0a7de745
A
477 }
478
39037602
A
479 MNL_NAME_TABLE_UNLOCK();
480}
481
482
483/* Initialize the data structures in the mnl_obj structure at the head of the
484 * provided object. This should be called on an object before it is passed to
485 * any other mnl_obj* routine.
486 */
487void
488mnl_obj_init(mnl_obj_t obj)
489{
490 queue_chain_init(obj->links);
491 obj->name = MNL_NAME_NULL;
492}
493
494
495/* Search the local node's hash table for the object associated with a
496 * mnl_name_t and return it. Returns MNL_NAME_NULL on failure.
497 */
498mnl_obj_t
499mnl_obj_lookup(mnl_name_t name)
500{
501 mnl_obj_t obj = MNL_OBJ_NULL;
0a7de745 502
39037602
A
503 if (name != MNL_NAME_NULL) {
504 qe_foreach_element(obj, &mnl_name_table[MNL_NAME_HASH(name)], links) {
0a7de745 505 if (obj->name == name) {
39037602 506 break;
0a7de745 507 }
39037602
A
508 }
509 }
510 return obj;
511}
512
513
514/* Search the local node's hash table for the object associated with a
515 * mnl_name_t and remove it. The pointer to the removed object is returned so
516 * that the caller can appropriately dispose of the object.
517 * Returns MNL_NAME_NULL on failure.
518 */
519mnl_obj_t
520mnl_obj_remove(mnl_name_t name)
521{
522 mnl_obj_t obj = MNL_OBJ_NULL;
0a7de745 523
39037602
A
524 if (name != MNL_NAME_NULL) {
525 qe_foreach_element_safe(obj, &mnl_name_table[MNL_NAME_HASH(name)], links) {
0a7de745 526 if (obj->name == name) {
39037602 527 remqueue(&obj->links);
0a7de745 528 }
39037602
A
529 }
530 }
531 return obj;
532}
533
534
535/* Insert an object into the local node's hash table. If the name of the
536 * provided object is MNL_NAME_NULL then a new mnl_name is allocated and
537 * assigned to the object.
0a7de745
A
538 * Returns KERN_SUCCESS if obj was added to hash table
539 * Returns KERN_INVALID_ARGUMENT if obj is invalid
540 * Returns KERN_NAME_EXISTS if obj's name already exists in hash table
39037602
A
541 */
542kern_return_t
543mnl_obj_insert(mnl_obj_t obj)
544{
0a7de745 545 if (!MNL_OBJ_VALID(obj)) {
39037602 546 return KERN_INVALID_ARGUMENT;
0a7de745
A
547 }
548
39037602 549 MNL_NAME_TABLE_LOCK();
0a7de745 550
39037602
A
551 if (!MNL_NAME_VALID(obj->name)) {
552 // obj is unnammed, so lets allocate a fresh one
553 obj->name = mnl_name_alloc();
554 }
0a7de745 555
39037602
A
556 enqueue(&mnl_name_table[MNL_NAME_HASH(obj->name)], &obj->links);
557 MNL_NAME_TABLE_UNLOCK();
558
0a7de745 559 if (obj->name >= (MACH_NODES_MAX << 1)) {
39037602 560 panic("Unexpected MNL_NAME %lld in obj %p", obj->name, obj);
0a7de745 561 }
39037602
A
562
563 return KERN_SUCCESS;
564}
565
566
567/*** Mach Node Link Driver Interface Implementation ***/
568
569/* Allocate a mnl_msg struct plus additional payload. Link drivers are not
570 * required to use this to allocate messages; any wired and mapped kernel
571 * memory is acceptable.
572 *
573 * Arguments:
574 * payload Number of additional bytes to allocate for message payload
575 * flags Currently unused; 0 should be passed
576 *
577 * Return values:
578 * MNL_MSG_NULL: Allocation failed
579 * *: Pointer to new mnl_msg struct of requested size
580 */
581mnl_msg_t
582mnl_msg_alloc(int payload,
0a7de745 583 uint32_t flags __unused)
39037602
A
584{
585 mnl_msg_t msg = kalloc(MNL_MSG_SIZE + payload);
586
587 if (MNL_MSG_VALID(msg)) {
588 bzero(msg, MNL_MSG_SIZE); // Only zero the header
589 msg->size = payload;
590 }
591
592 return msg;
593}
594
595
596/* Free a mnl_msg struct allocated by mnl_msg_alloc().
597 *
598 * Arguments:
599 * msg Pointer to the message buffer to be freed
600 * flags Currently unused; 0 should be passed
601 */
602void
603mnl_msg_free(mnl_msg_t msg,
0a7de745 604 uint32_t flags __unused)
39037602 605{
0a7de745 606 if (MNL_MSG_VALID(msg)) {
39037602 607 kfree(msg, MNL_MSG_SIZE + msg->size);
0a7de745 608 }
39037602
A
609}
610
611
612/* The link driver calls this to setup a new (or restarted) node, and to get
613 * an mnl_node_info struct for use as a parameter to other mnl functions.
614 * If MNL_NODE_NULL is returned, the operation failed. Otherwise, a pointer
615 * to a new mnl_node struct is returned. The caller should set all fields
616 * in the structure, then call mnl_register() to complete node registration.
617 *
618 * Arguments:
619 * nid The id of the node to be instantiated
620 * flags Currently unused; 0 should be passed
621 *
622 * Return values:
623 * MNL_NODE_NULL: Operation failed
624 * *: Pointer to a new mnl_node struct
625 */
626mnl_node_info_t
627mnl_instantiate(mach_node_id_t nid,
0a7de745 628 uint32_t flags __unused)
39037602 629{
0a7de745 630 mach_node_init(); // Lazy init of mach_node layer
39037602 631
0a7de745
A
632 if ((nid == localnode_id) || !MACH_NODE_ID_VALID(nid)) {
633 return MNL_NODE_NULL;
634 }
39037602 635
0a7de745 636 return (mnl_node_info_t)mach_node_alloc_init(nid);
39037602
A
637}
638
639/* The link driver calls mnl_register() to complete the node registration
640 * process. KERN_SUCCESS is returned if registration succeeded, otherwise
641 * an error is returned.
642 *
643 * Arguments:
644 * node Pointer to the node's mnl_node structure
645 * flags Currently unused; 0 should be passed
646 *
647 * Return values:
648 * KERN_SUCCESS: Registration succeeded
649 * KERN_INVALID_ARGUMENT: Field(s) in <node> contained unacceptable values
650 * KERN_*: Values returned from underlying functions
651 */
652kern_return_t
653mnl_register(mnl_node_info_t node,
0a7de745 654 uint32_t flags __unused)
39037602 655{
0a7de745
A
656 if (MNL_NODE_VALID(node) && (node->node_id != localnode_id)) {
657 return mach_node_register((mach_node_t)node);
658 }
39037602 659
0a7de745 660 return KERN_INVALID_ARGUMENT;
39037602
A
661}
662
663
664/* The link driver calls this to report that the link has been raised in one
665 * or both directions. If the link is two uni-directional channels, each link
666 * driver will independently call this function, each only raising the link
667 * they are responsible for. The mach_node layer will not communicate with
668 * the remote node until both rx and tx links are up.
669 *
670 * Arguments:
671 * node Pointer to the node's mnl_node structure
672 * link Indicates which link(s) are up (see MNL_LINK_* defines)
673 * flags Currently unused; 0 should be passed
674 *
675 * Return values:
676 * KERN_SUCCESS: Link state changed successfully.
677 * KERN_INVALID_ARGUMENT: An argument value was not allowed.
678 * KERN_*: Values returned from underlying functions.
679 */
680kern_return_t
681mnl_set_link_state(mnl_node_info_t node,
0a7de745
A
682 int link,
683 uint32_t flags __unused)
39037602 684{
0a7de745 685 kern_return_t kr;
39037602
A
686 mach_node_t mnode = (mach_node_t)node;
687
0a7de745
A
688 if (!MACH_NODE_VALID(mnode) || !(link & MNL_LINK_UP) || (link & mnode->link)) {
689 return KERN_INVALID_ARGUMENT; // bad node, or bad link argument
690 }
691 MACH_NODE_LOCK(mnode);
39037602 692
0a7de745 693 if (mnode->dead) {
39037602 694 kr = KERN_NODE_DOWN;
0a7de745
A
695 } else {
696 mnode->link |= link;
697 kr = KERN_SUCCESS;
698 }
39037602 699
0a7de745 700 MACH_NODE_UNLOCK(mnode);
39037602
A
701
702 return kr;
703}
704
705/* The link driver calls this to indicate a node has terminated and is no
706 * longer available for messaging. This may be due to a crash or an orderly
707 * shutdown, but either way the remote node no longer retains any state about
708 * the remaining nodes. References held on behalf of the terminated node
709 * will be cleaned up. After this is called, both the rx and tx links are
710 * marked as down. If the remote node restarts, the link driver can bring
711 * up the link using mnl_instantiate() again.
712 *
713 * Arguments:
714 * node Pointer to the node's mnl_node structure
715 * flags Currently unused; 0 should be passed
716 *
717 * Return values:
718 * KERN_SUCCESS: Node was terminated.
719 * KERN_INVALID_ARGUMENT: Node id was invalid or non-existant.
720 * KERN_*: Values returned from underlying functions.
721 */
722kern_return_t
723mnl_terminate(mnl_node_info_t node,
0a7de745 724 uint32_t flags __unused)
39037602
A
725{
726 kern_return_t kr = KERN_SUCCESS;
727 mach_node_t mnode = (mach_node_t)node;
728
0a7de745
A
729 if (!MACH_NODE_VALID(mnode)) {
730 return KERN_INVALID_ARGUMENT; // bad node
731 }
39037602
A
732 MACH_NODE_LOCK(mnode);
733 if (mnode->dead) {
0a7de745 734 kr = KERN_NODE_DOWN; // node is already terminated
39037602
A
735 goto unlock;
736 }
737
738 mnode->link = MNL_LINK_DOWN;
739 mnode->active = 0;
740 mnode->suspended = 0;
741 mnode->dead = 1;
742
743 flipc_node_retire(mnode);
744
0a7de745
A
745 // Wake any threads sleeping on the proxy port set
746 if (mnode->proxy_port_set != IPS_NULL) {
747 ips_lock(mnode->proxy_port_set);
748 ipc_pset_destroy(mnode->proxy_space, mnode->proxy_port_set);
749 mnode->proxy_port_set = IPS_NULL;
750 }
39037602
A
751
752 // TODO: Inform node name server (if registered) of termination
753
754unlock:
755 MACH_NODE_UNLOCK(mnode);
756 return kr;
757}
758
759
760/* The link driver calls this to deliver an incoming message. Note that the
761 * link driver must dispose of the memory pointed to by <msg> after the
762 * function call returns.
763 *
764 * Arguments:
765 * node Pointer to the node's mnl_node structure
766 * msg Pointer to the message buffer
767 * flags Currently unused; 0 should be passed
768 */
769void
770mnl_msg_from_node(mnl_node_info_t node __unused,
0a7de745
A
771 mnl_msg_t msg,
772 uint32_t flags __unused)
39037602
A
773{
774 assert(MNL_MSG_VALID(msg));
775 assert(MACH_NODE_ID_VALID(msg->node_id));
776 assert(MNL_NODE_VALID(node));
777
778 /* If node message forwarding is supported, the from_node_id arg may not
779 * match fmsg->info.node_id. The former is the node from which we received
780 * the message; the latter is the node that generated the message originally.
781 * We always use fmsg->info.node_id, which is where the ack needs to go.
782 */
783
784 switch (msg->sub) {
0a7de745
A
785 case MACH_NODE_SUB_FLIPC:
786 flipc_msg_from_node((mach_node_t)node, msg, flags);
787 break;
39037602 788
0a7de745 789 default:
39037602 790#if DEBUG
0a7de745 791 PE_enter_debugger("mnl_msg_from_node(): Invalid subsystem");
39037602 792#endif
0a7de745 793 break;
39037602
A
794 }
795}
796
797
798/* The link driver calls this to fetch the next message to transmit.
799 * This function will block until a message is available, or will return
800 * FLIPC_MSG_NULL if the link is to be terminated. After the caller has
801 * completed the transmission and no longer needs the msg buffer, it should
802 * call mnl_msg_complete().
803 *
804 * Arguments:
805 * node Pointer to the node's mnl_node structure
806 * flags Currently unused; 0 should be passed
807 */
808mnl_msg_t
809mnl_msg_to_node(mnl_node_info_t node __unused,
0a7de745 810 uint32_t flags __unused)
39037602
A
811{
812 assert(MNL_NODE_VALID(node));
813
814#if DEBUG
0a7de745 815 thread_set_thread_name(current_thread(), "MNL_Link");
39037602
A
816#endif
817
818 return flipc_msg_to_remote_node((mach_node_t)node, 0);
819}
820
821
822/* The link driver calls this to indicate that the specified msg buffer has
823 * been sent over the link and can be deallocated.
824 *
825 * Arguments:
826 * node Pointer to the node's mnl_node structure
827 * msg Pointer to the message buffer
828 * flags Currently unused; 0 should be passed
829 */
830void
831mnl_msg_complete(mnl_node_info_t node __unused,
0a7de745
A
832 mnl_msg_t msg,
833 uint32_t flags)
39037602 834{
0a7de745
A
835 switch (msg->sub) {
836 case MACH_NODE_SUB_NODE:
837 mnl_msg_free(msg, flags);
838 break;
39037602 839
0a7de745
A
840 case MACH_NODE_SUB_FLIPC:
841 flipc_msg_free(msg, flags);
842 break;
39037602 843
0a7de745 844 default:
39037602 845#if DEBUG
0a7de745 846 PE_enter_debugger("mnl_msg_complete(): Invalid subsystem");
39037602 847#endif
0a7de745
A
848 break;
849 }
39037602
A
850}
851
852#else // MACH_FLIPC not configured, so provide KPI stubs
853
854mnl_msg_t
855mnl_msg_alloc(int payload __unused, uint32_t flags __unused)
856{
0a7de745 857 return MNL_MSG_NULL;
39037602
A
858}
859
860void
861mnl_msg_free(mnl_msg_t msg __unused, uint32_t flags __unused)
862{
0a7de745 863 return;
39037602
A
864}
865
866mnl_node_info_t
867mnl_instantiate(mach_node_id_t nid __unused, uint32_t flags __unused)
868{
0a7de745 869 return MNL_NODE_NULL;
39037602
A
870}
871
872kern_return_t
873mnl_register(mnl_node_info_t node __unused, uint32_t flags __unused)
874{
0a7de745 875 return KERN_FAILURE;
39037602
A
876}
877
878kern_return_t
879mnl_set_link_state(mnl_node_info_t node __unused,
0a7de745
A
880 int link __unused,
881 uint32_t flags __unused)
39037602 882{
0a7de745 883 return KERN_FAILURE;
39037602
A
884}
885
886kern_return_t
887mnl_terminate(mnl_node_info_t node __unused, uint32_t flags __unused)
888{
0a7de745 889 return KERN_FAILURE;
39037602
A
890}
891
892void
893mnl_msg_from_node(mnl_node_info_t node __unused,
0a7de745
A
894 mnl_msg_t msg __unused,
895 uint32_t flags __unused)
39037602 896{
0a7de745 897 return;
39037602
A
898}
899
900mnl_msg_t
901mnl_msg_to_node(mnl_node_info_t node __unused, uint32_t flags __unused)
902{
0a7de745 903 return MNL_MSG_NULL;
39037602
A
904}
905
906void
907mnl_msg_complete(mnl_node_info_t node __unused,
0a7de745
A
908 mnl_msg_t msg __unused,
909 uint32_t flags __unused)
39037602 910{
0a7de745 911 return;
39037602
A
912}
913
914#endif // MACH_FLIPC