+ ipc_special_reply_port_bits_reset(port);
+}
+
+/*
+ * Routine: ipc_port_unbind_special_reply_port
+ * Purpose:
+ * Unbind the thread's special reply port.
+ * If the special port has threads waiting on turnstile,
+ * update it's inheritor.
+ * Condition:
+ * Nothing locked.
+ * Returns:
+ * None.
+ */
+static kern_return_t
+ipc_port_unbind_special_reply_port(
+ thread_t thread,
+ boolean_t unbind_active_port)
+{
+ ipc_port_t special_reply_port = thread->ith_special_reply_port;
+
+ ip_lock(special_reply_port);
+
+ /* Return error if port active and unbind_active_port set to FALSE */
+ if (unbind_active_port == FALSE && ip_active(special_reply_port)) {
+ ip_unlock(special_reply_port);
+ return KERN_FAILURE;
+ }
+
+ thread->ith_special_reply_port = NULL;
+ ipc_port_adjust_special_reply_port_locked(special_reply_port, NULL,
+ IPC_PORT_ADJUST_UNLINK_THREAD, FALSE);
+ /* port unlocked */
+
+ ip_release(special_reply_port);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: thread_get_special_port [kernel call]
+ * Purpose:
+ * Clones a send right for one of the thread's
+ * special ports.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Extracted a send right.
+ * KERN_INVALID_ARGUMENT The thread is null.
+ * KERN_FAILURE The thread is dead.
+ * KERN_INVALID_ARGUMENT Invalid special port.
+ */
+
+kern_return_t
+thread_get_special_port(
+ thread_inspect_t thread,
+ int which,
+ ipc_port_t *portp);
+
+kern_return_t
+static
+thread_get_special_port_internal(
+ thread_inspect_t thread,
+ int which,
+ ipc_port_t *portp,
+ mach_thread_flavor_t flavor)
+{
+ kern_return_t kr;
+ ipc_port_t port;
+
+ if (thread == THREAD_NULL) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ if ((kr = port_allowed_with_thread_flavor(which, flavor)) != KERN_SUCCESS) {
+ return kr;
+ }
+
+ thread_mtx_lock(thread);
+ if (!thread->active) {
+ thread_mtx_unlock(thread);
+ return KERN_FAILURE;
+ }
+
+ switch (which) {
+ case THREAD_KERNEL_PORT:
+ port = ipc_port_copy_send(thread->ith_settable_self);
+ thread_mtx_unlock(thread);
+ break;
+
+ case THREAD_READ_PORT:
+ case THREAD_INSPECT_PORT:
+ thread_mtx_unlock(thread);
+ mach_thread_flavor_t current_flavor = (which == THREAD_READ_PORT) ?
+ THREAD_FLAVOR_READ : THREAD_FLAVOR_INSPECT;
+ /* convert_thread_to_port_with_flavor consumes a thread reference */
+ thread_reference(thread);
+ port = convert_thread_to_port_with_flavor(thread, current_flavor);
+ break;
+
+ default:
+ thread_mtx_unlock(thread);
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ *portp = port;
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+thread_get_special_port(
+ thread_inspect_t thread,
+ int which,
+ ipc_port_t *portp)
+{
+ return thread_get_special_port_internal(thread, which, portp, THREAD_FLAVOR_CONTROL);
+}
+
+kern_return_t
+thread_get_special_port_from_user(
+ mach_port_t port,
+ int which,
+ ipc_port_t *portp)
+{
+ ipc_kobject_type_t kotype;
+ kern_return_t kr;
+
+ thread_t thread = convert_port_to_thread_check_type(port, &kotype, THREAD_FLAVOR_INSPECT, FALSE);
+
+ if (thread == THREAD_NULL) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ switch (kotype) {
+ case IKOT_THREAD_CONTROL:
+ kr = thread_get_special_port_internal(thread, which, portp, THREAD_FLAVOR_CONTROL);
+ break;
+ case IKOT_THREAD_READ:
+ kr = thread_get_special_port_internal(thread, which, portp, THREAD_FLAVOR_READ);
+ break;
+ case IKOT_THREAD_INSPECT:
+ kr = thread_get_special_port_internal(thread, which, portp, THREAD_FLAVOR_INSPECT);
+ break;
+ default:
+ panic("strange kobject type");
+ break;
+ }
+
+ thread_deallocate(thread);
+ return kr;
+}
+
+static kern_return_t
+port_allowed_with_thread_flavor(
+ int which,
+ mach_thread_flavor_t flavor)
+{
+ switch (flavor) {
+ case THREAD_FLAVOR_CONTROL:
+ return KERN_SUCCESS;
+
+ case THREAD_FLAVOR_READ:
+
+ switch (which) {
+ case THREAD_READ_PORT:
+ case THREAD_INSPECT_PORT:
+ return KERN_SUCCESS;
+ default:
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ case THREAD_FLAVOR_INSPECT:
+
+ switch (which) {
+ case THREAD_INSPECT_PORT:
+ return KERN_SUCCESS;
+ default:
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ default:
+ return KERN_INVALID_CAPABILITY;
+ }
+}
+
+/*
+ * Routine: thread_set_special_port [kernel call]
+ * Purpose:
+ * Changes one of the thread's special ports,
+ * setting it to the supplied send right.
+ * Conditions:
+ * Nothing locked. If successful, consumes
+ * the supplied send right.
+ * Returns:
+ * KERN_SUCCESS Changed the special port.
+ * KERN_INVALID_ARGUMENT The thread is null.
+ * KERN_FAILURE The thread is dead.
+ * KERN_INVALID_ARGUMENT Invalid special port.
+ * KERN_NO_ACCESS Restricted access to set port.
+ */
+
+kern_return_t
+thread_set_special_port(
+ thread_t thread,
+ int which,
+ ipc_port_t port)
+{
+ kern_return_t result = KERN_SUCCESS;
+ ipc_port_t *whichp, old = IP_NULL;
+
+ if (thread == THREAD_NULL) {
+ return KERN_INVALID_ARGUMENT;
+ }
+
+ switch (which) {
+ case THREAD_KERNEL_PORT:
+#if CONFIG_CSR
+ if (csr_check(CSR_ALLOW_KERNEL_DEBUGGER) != 0) {
+ /*
+ * Only allow setting of thread-self
+ * special port from user-space when SIP is
+ * disabled (for Mach-on-Mach emulation).
+ */
+ return KERN_NO_ACCESS;
+ }
+#endif
+ whichp = &thread->ith_settable_self;
+ break;