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