+ if (type != IKOT_NONE) {
+ /* Once set, this bit can never be unset */
+ port->ip_object.io_bits |= IO_BITS_KOBJECT;
+ }
+}
+
+/*
+ * Routine: ipc_kobject_alloc_port
+ * Purpose:
+ * Allocate a kobject port in the kernel space of the specified type.
+ *
+ * This function never fails.
+ *
+ * Conditions:
+ * No locks held (memory is allocated)
+ */
+ipc_port_t
+ipc_kobject_alloc_port(
+ ipc_kobject_t kobject,
+ ipc_kobject_type_t type,
+ ipc_kobject_alloc_options_t options)
+{
+ ipc_port_init_flags_t flags;
+ ipc_space_t space;
+ ipc_port_t port;
+
+ if (options & IPC_KOBJECT_ALLOC_IN_TRANSIT) {
+ /* kobject port intended to be copied out to user-space */
+ flags = IPC_PORT_INIT_MESSAGE_QUEUE;
+ space = IS_NULL;
+ } else {
+ /* true kernel-bound kobject port */
+ flags = IPC_PORT_INIT_NONE;
+ space = ipc_space_kernel;
+ }
+ port = ipc_port_alloc_special(space, flags);
+ if (port == IP_NULL) {
+ panic("ipc_kobject_alloc_port(): failed to allocate port");
+ }
+
+ ipc_kobject_set_atomically(port, kobject, type);
+
+ if (options & IPC_KOBJECT_ALLOC_MAKE_SEND) {
+ ipc_port_make_send_locked(port);
+ }
+
+ if (options & IPC_KOBJECT_ALLOC_IN_TRANSIT) {
+ /* reset the port like it has been copied in circularity checked */
+ if (options & IPC_KOBJECT_ALLOC_NSREQUEST) {
+ panic("ipc_kobject_alloc_port(): invalid option for user-space port");
+ }
+ port->ip_mscount = 0;
+ assert(port->ip_tempowner == 0);
+ assert(port->ip_receiver == IS_NULL);
+ port->ip_receiver = IS_NULL;
+ port->ip_receiver_name = MACH_PORT_NULL;
+ } else {
+ if (options & IPC_KOBJECT_ALLOC_NSREQUEST) {
+ ipc_port_make_sonce_locked(port);
+ port->ip_nsrequest = port;
+ }
+ }
+ if (options & IPC_KOBJECT_ALLOC_IMMOVABLE_SEND) {
+ port->ip_immovable_send = 1;
+ }
+ if (options & IPC_KOBJECT_ALLOC_NO_GRANT) {
+ port->ip_no_grant = 1;
+ }
+
+ return port;
+}
+
+/*
+ * Routine: ipc_kobject_make_send_lazy_alloc_port
+ * Purpose:
+ * Make a send once for a kobject port.
+ *
+ * A location owning this port is passed in port_store.
+ * If no port exists, a port is made lazily.
+ *
+ * A send right is made for the port, and if this is the first one
+ * (possibly not for the first time), then the no-more-senders
+ * notification is rearmed.
+ *
+ * When a notification is armed, the kobject must donate
+ * one of its references to the port. It is expected
+ * the no-more-senders notification will consume this reference.
+ *
+ * Returns:
+ * TRUE if a notification was armed
+ * FALSE else
+ *
+ * Conditions:
+ * Nothing is locked, memory can be allocated.
+ * The caller must be able to donate a kobject reference to the port.
+ */
+boolean_t
+ipc_kobject_make_send_lazy_alloc_port(
+ ipc_port_t *port_store,
+ ipc_kobject_t kobject,
+ ipc_kobject_type_t type)
+{
+ ipc_port_t port, previous;
+ boolean_t rc = FALSE;
+
+ port = os_atomic_load(port_store, dependency);
+
+ if (!IP_VALID(port)) {
+ port = ipc_kobject_alloc_port(kobject, type,
+ IPC_KOBJECT_ALLOC_MAKE_SEND | IPC_KOBJECT_ALLOC_NSREQUEST);
+ if (os_atomic_cmpxchgv(port_store, IP_NULL, port, &previous, release)) {
+ return TRUE;
+ }
+
+ // undo what ipc_kobject_alloc_port() did above
+ port->ip_nsrequest = IP_NULL;
+ port->ip_mscount = 0;
+ port->ip_sorights = 0;
+ port->ip_srights = 0;
+ ip_release(port);
+ ip_release(port);
+ ipc_port_dealloc_kernel(port);
+
+ port = previous;
+ }
+
+ ip_lock(port);
+ ipc_port_make_send_locked(port);
+ if (port->ip_srights == 1) {
+ ipc_port_make_sonce_locked(port);
+ assert(port->ip_nsrequest == IP_NULL);
+ port->ip_nsrequest = port;
+ rc = TRUE;
+ }
+ ip_unlock(port);
+
+ return rc;