X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/7e41aa883dd258f888d0470250eead40a53ef1f5..3903760236c30e3b5ace7a4eefac3a269d68957c:/osfmk/kern/mach_node.c diff --git a/osfmk/kern/mach_node.c b/osfmk/kern/mach_node.c new file mode 100644 index 000000000..4a0d96dc8 --- /dev/null +++ b/osfmk/kern/mach_node.c @@ -0,0 +1,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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include // mach_msg_send_from_kernel_proper() + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include // OSAddAtomic64(), OSCompareAndSwap() +#include // 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; npublished = 0; + if (np->active == 1) + mach_nodes_to_publish++; + } + + mach_node_publish(localnode); // Always publish local node first + + for (n=0; ninfo.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 */ + 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 . + * The current node is locked and returned if it is not dead, or if it is dead + * and is false. A new node struct is allocated, locked and + * returned if the node is dead and is true, or if the node + * is absent and is true. MACH_NODE_NULL is returned if + * the node is absent and 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; ilinks); + 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 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 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