aboutsummaryrefslogtreecommitdiff
path: root/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'ipc')
-rw-r--r--ipc/.gitignore2
-rw-r--r--ipc/ipc_entry.c214
-rw-r--r--ipc/ipc_entry.h110
-rw-r--r--ipc/ipc_init.c117
-rw-r--r--ipc/ipc_init.h50
-rw-r--r--ipc/ipc_kmsg.c2904
-rw-r--r--ipc/ipc_kmsg.h345
-rw-r--r--ipc/ipc_kmsg_queue.h31
-rwxr-xr-xipc/ipc_machdep.h39
-rw-r--r--ipc/ipc_marequest.c437
-rw-r--r--ipc/ipc_marequest.h99
-rw-r--r--ipc/ipc_mqueue.c695
-rw-r--r--ipc/ipc_mqueue.h112
-rw-r--r--ipc/ipc_notify.c449
-rw-r--r--ipc/ipc_notify.h58
-rw-r--r--ipc/ipc_object.c969
-rw-r--r--ipc/ipc_object.h169
-rw-r--r--ipc/ipc_port.c1290
-rw-r--r--ipc/ipc_port.h354
-rw-r--r--ipc/ipc_print.h39
-rw-r--r--ipc/ipc_pset.c350
-rw-r--r--ipc/ipc_pset.h92
-rw-r--r--ipc/ipc_right.c2115
-rw-r--r--ipc/ipc_right.h112
-rw-r--r--ipc/ipc_space.c215
-rw-r--r--ipc/ipc_space.h324
-rw-r--r--ipc/ipc_table.c135
-rw-r--r--ipc/ipc_table.h101
-rw-r--r--ipc/ipc_target.c78
-rw-r--r--ipc/ipc_target.h67
-rw-r--r--ipc/ipc_thread.c107
-rw-r--r--ipc/ipc_thread.h129
-rw-r--r--ipc/ipc_types.h31
-rw-r--r--ipc/mach_debug.c288
-rw-r--r--ipc/mach_msg.c1709
-rw-r--r--ipc/mach_msg.h60
-rw-r--r--ipc/mach_port.c1578
-rw-r--r--ipc/mach_port.h37
-rw-r--r--ipc/mach_port.srv27
-rw-r--r--ipc/notify.defs22
-rw-r--r--ipc/port.h106
41 files changed, 16166 insertions, 0 deletions
diff --git a/ipc/.gitignore b/ipc/.gitignore
new file mode 100644
index 0000000..b750932
--- /dev/null
+++ b/ipc/.gitignore
@@ -0,0 +1,2 @@
+notify.none.defs.c
+notify.none.msgids
diff --git a/ipc/ipc_entry.c b/ipc/ipc_entry.c
new file mode 100644
index 0000000..f13c442
--- /dev/null
+++ b/ipc/ipc_entry.c
@@ -0,0 +1,214 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_entry.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Primitive functions to manipulate translation entries.
+ */
+
+#include <kern/printf.h>
+#include <string.h>
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <kern/assert.h>
+#include <kern/sched_prim.h>
+#include <kern/slab.h>
+#include <ipc/port.h>
+#include <ipc/ipc_types.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_table.h>
+#include <ipc/ipc_object.h>
+
+struct kmem_cache ipc_entry_cache;
+
+/*
+ * Routine: ipc_entry_alloc
+ * Purpose:
+ * Allocate an entry out of the space.
+ * Conditions:
+ * The space must be write-locked. May allocate memory.
+ * Returns:
+ * KERN_SUCCESS An entry was allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory for an entry.
+ */
+
+kern_return_t
+ipc_entry_alloc(
+ ipc_space_t space,
+ mach_port_name_t *namep,
+ ipc_entry_t *entryp)
+{
+ kern_return_t kr;
+ ipc_entry_t entry;
+ rdxtree_key_t key;
+
+ if (!space->is_active) {
+ return KERN_INVALID_TASK;
+ }
+
+ kr = ipc_entry_get(space, namep, entryp);
+ if (kr == KERN_SUCCESS)
+ return kr;
+
+ entry = ie_alloc();
+ if (entry == IE_NULL) {
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ kr = rdxtree_insert_alloc(&space->is_map, entry, &key);
+ if (kr) {
+ ie_free(entry);
+ return kr;
+ }
+ space->is_size += 1;
+
+ entry->ie_bits = 0;
+ entry->ie_object = IO_NULL;
+ entry->ie_request = 0;
+ entry->ie_name = (mach_port_name_t) key;
+
+ *entryp = entry;
+ *namep = (mach_port_name_t) key;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_entry_alloc_name
+ * Purpose:
+ * Allocates/finds an entry with a specific name.
+ * If an existing entry is returned, its type will be nonzero.
+ * Conditions:
+ * The space must be write-locked. May allocate memory.
+ * Returns:
+ * KERN_SUCCESS Found existing entry with same name.
+ * KERN_SUCCESS Allocated a new entry.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_entry_alloc_name(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t *entryp)
+{
+ kern_return_t kr;
+ ipc_entry_t entry, e, *prevp;
+ void **slot;
+ assert(MACH_PORT_NAME_VALID(name));
+
+ if (!space->is_active) {
+ return KERN_INVALID_TASK;
+ }
+
+ slot = rdxtree_lookup_slot(&space->is_map, (rdxtree_key_t) name);
+ if (slot != NULL)
+ entry = *(ipc_entry_t *) slot;
+
+ if (slot == NULL || entry == IE_NULL) {
+ entry = ie_alloc();
+ if (entry == IE_NULL) {
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ entry->ie_bits = 0;
+ entry->ie_object = IO_NULL;
+ entry->ie_request = 0;
+ entry->ie_name = name;
+
+ if (slot != NULL)
+ rdxtree_replace_slot(slot, entry);
+ else {
+ kr = rdxtree_insert(&space->is_map,
+ (rdxtree_key_t) name, entry);
+ if (kr != KERN_SUCCESS) {
+ ie_free(entry);
+ return kr;
+ }
+ }
+ space->is_size += 1;
+
+ *entryp = entry;
+ return KERN_SUCCESS;
+ }
+
+ if (IE_BITS_TYPE(entry->ie_bits)) {
+ /* Used entry. */
+ *entryp = entry;
+ return KERN_SUCCESS;
+ }
+
+ /* Free entry. Rip the entry out of the free list. */
+ for (prevp = &space->is_free_list, e = space->is_free_list;
+ e != entry;
+ ({ prevp = &e->ie_next_free; e = e->ie_next_free; }))
+ continue;
+
+ *prevp = entry->ie_next_free;
+ space->is_free_list_size -= 1;
+
+ entry->ie_bits = 0;
+ assert(entry->ie_object == IO_NULL);
+ assert(entry->ie_name == name);
+ entry->ie_request = 0;
+
+ space->is_size += 1;
+ *entryp = entry;
+ return KERN_SUCCESS;
+}
+
+#if MACH_KDB
+#include <ddb/db_output.h>
+#include <kern/task.h>
+
+#define printf kdbprintf
+
+ipc_entry_t
+db_ipc_object_by_name(
+ const task_t task,
+ mach_port_name_t name)
+{
+ ipc_space_t space = task->itk_space;
+ ipc_entry_t entry;
+
+ entry = ipc_entry_lookup(space, name);
+ if(entry != IE_NULL) {
+ iprintf("(task 0x%x, name 0x%x) ==> object 0x%x",
+ entry->ie_object);
+ return (ipc_entry_t) entry->ie_object;
+ }
+ return entry;
+}
+#endif /* MACH_KDB */
diff --git a/ipc/ipc_entry.h b/ipc/ipc_entry.h
new file mode 100644
index 0000000..9f7b593
--- /dev/null
+++ b/ipc/ipc_entry.h
@@ -0,0 +1,110 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_entry.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for translation entries, which represent
+ * tasks' capabilities for ports and port sets.
+ */
+
+#ifndef _IPC_IPC_ENTRY_H_
+#define _IPC_IPC_ENTRY_H_
+
+#include <mach/mach_types.h>
+#include <mach/port.h>
+#include <mach/kern_return.h>
+#include <kern/slab.h>
+#include <ipc/port.h>
+#include <ipc/ipc_table.h>
+#include <ipc/ipc_types.h>
+
+/*
+ * Spaces hold capabilities for ipc_object_t's (ports and port sets).
+ * Each ipc_entry_t records a capability.
+ */
+
+typedef unsigned int ipc_entry_bits_t;
+typedef ipc_table_elems_t ipc_entry_num_t; /* number of entries */
+
+typedef struct ipc_entry {
+ mach_port_name_t ie_name;
+ ipc_entry_bits_t ie_bits;
+ struct ipc_object *ie_object;
+ union {
+ struct ipc_entry *next_free;
+ /*XXX ipc_port_request_index_t request;*/
+ unsigned int request;
+ } index;
+} *ipc_entry_t;
+
+#define IE_NULL ((ipc_entry_t) 0)
+
+#define ie_request index.request
+#define ie_next_free index.next_free
+
+#define IE_BITS_UREFS_MASK 0x0000ffff /* 16 bits of user-reference */
+#define IE_BITS_UREFS(bits) ((bits) & IE_BITS_UREFS_MASK)
+
+#define IE_BITS_TYPE_MASK 0x001f0000 /* 5 bits of capability type */
+#define IE_BITS_TYPE(bits) ((bits) & IE_BITS_TYPE_MASK)
+
+#define IE_BITS_MAREQUEST 0x00200000 /* 1 bit for msg-accepted */
+
+#define IE_BITS_RIGHT_MASK 0x003fffff /* relevant to the right */
+
+#if PORT_GENERATIONS
+#error "not supported"
+#define IE_BITS_GEN_MASK 0xff000000U /* 8 bits for generation */
+#define IE_BITS_GEN(bits) ((bits) & IE_BITS_GEN_MASK)
+#define IE_BITS_GEN_ONE 0x01000000 /* low bit of generation */
+#else
+#define IE_BITS_GEN_MASK 0
+#define IE_BITS_GEN(bits) 0
+#define IE_BITS_GEN_ONE 0
+#endif
+
+
+extern struct kmem_cache ipc_entry_cache;
+#define ie_alloc() ((ipc_entry_t) kmem_cache_alloc(&ipc_entry_cache))
+#define ie_free(e) kmem_cache_free(&ipc_entry_cache, (vm_offset_t) (e))
+
+extern kern_return_t
+ipc_entry_alloc(ipc_space_t space, mach_port_name_t *namep, ipc_entry_t *entryp);
+
+extern kern_return_t
+ipc_entry_alloc_name(ipc_space_t space, mach_port_name_t name, ipc_entry_t *entryp);
+
+ipc_entry_t
+db_ipc_object_by_name(
+ task_t task,
+ mach_port_name_t name);
+
+#endif /* _IPC_IPC_ENTRY_H_ */
diff --git a/ipc/ipc_init.c b/ipc/ipc_init.c
new file mode 100644
index 0000000..8e628ad
--- /dev/null
+++ b/ipc/ipc_init.c
@@ -0,0 +1,117 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_init.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to initialize the IPC system.
+ */
+
+#include <mach/kern_return.h>
+#include <kern/ipc_host.h>
+#include <kern/slab.h>
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_marequest.h>
+#include <ipc/ipc_notify.h>
+#include <ipc/ipc_kmsg.h>
+#include <ipc/ipc_init.h>
+
+
+
+static struct vm_map ipc_kernel_map_store;
+vm_map_t ipc_kernel_map = &ipc_kernel_map_store;
+const vm_size_t ipc_kernel_map_size = 8 * 1024 * 1024;
+
+/*
+ * Routine: ipc_bootstrap
+ * Purpose:
+ * Initialization needed before the kernel task
+ * can be created.
+ */
+
+void
+ipc_bootstrap(void)
+{
+ kern_return_t kr;
+
+ ipc_port_multiple_lock_init();
+
+ ipc_port_timestamp_lock_init();
+ ipc_port_timestamp_data = 0;
+
+ kmem_cache_init(&ipc_space_cache, "ipc_space",
+ sizeof(struct ipc_space), 0, NULL, 0);
+
+ kmem_cache_init(&ipc_entry_cache, "ipc_entry",
+ sizeof(struct ipc_entry), 0, NULL, 0);
+
+ kmem_cache_init(&ipc_object_caches[IOT_PORT], "ipc_port",
+ sizeof(struct ipc_port), 0, NULL, 0);
+
+ kmem_cache_init(&ipc_object_caches[IOT_PORT_SET], "ipc_pset",
+ sizeof(struct ipc_pset), 0, NULL, 0);
+
+ /* create special spaces */
+
+ kr = ipc_space_create_special(&ipc_space_kernel);
+ assert(kr == KERN_SUCCESS);
+
+ kr = ipc_space_create_special(&ipc_space_reply);
+ assert(kr == KERN_SUCCESS);
+
+ /* initialize modules with hidden data structures */
+
+ ipc_table_init();
+ ipc_notify_init();
+ ipc_marequest_init();
+}
+
+/*
+ * Routine: ipc_init
+ * Purpose:
+ * Final initialization of the IPC system.
+ */
+
+void
+ipc_init(void)
+{
+ vm_offset_t min, max;
+
+ kmem_submap(ipc_kernel_map, kernel_map, &min, &max,
+ ipc_kernel_map_size);
+
+ ipc_host_init();
+}
diff --git a/ipc/ipc_init.h b/ipc/ipc_init.h
new file mode 100644
index 0000000..8dd64bb
--- /dev/null
+++ b/ipc/ipc_init.h
@@ -0,0 +1,50 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_init.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Declarations of functions to initialize the IPC system.
+ */
+
+#ifndef _IPC_IPC_INIT_H_
+#define _IPC_IPC_INIT_H_
+
+/*
+ * Exported interfaces
+ */
+
+/* IPC initialization needed before creation of kernel task */
+extern void ipc_bootstrap(void);
+
+/* Remaining IPC initialization */
+extern void ipc_init(void);
+
+#endif /* _IPC_IPC_INIT_H_ */
diff --git a/ipc/ipc_kmsg.c b/ipc/ipc_kmsg.c
new file mode 100644
index 0000000..bd84380
--- /dev/null
+++ b/ipc/ipc_kmsg.c
@@ -0,0 +1,2904 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_kmsg.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Operations on kernel messages.
+ */
+
+#include <kern/printf.h>
+#include <string.h>
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/message.h>
+#include <mach/port.h>
+#include <machine/locore.h>
+#include <machine/copy_user.h>
+#include <kern/assert.h>
+#include <kern/debug.h>
+#include <kern/kalloc.h>
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_user.h>
+#include <ipc/port.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_kmsg.h>
+#include <ipc/ipc_thread.h>
+#include <ipc/ipc_marequest.h>
+#include <ipc/ipc_notify.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_right.h>
+
+#include <ipc/ipc_machdep.h>
+
+#include <device/net_io.h>
+
+#if MACH_KDB
+#include <ddb/db_output.h>
+#include <ipc/ipc_print.h>
+#endif
+
+
+ipc_kmsg_t ipc_kmsg_cache[NCPUS];
+
+/*
+ * Routine: ipc_kmsg_enqueue
+ * Purpose:
+ * Enqueue a kmsg.
+ */
+
+void
+ipc_kmsg_enqueue(
+ ipc_kmsg_queue_t queue,
+ ipc_kmsg_t kmsg)
+{
+ ipc_kmsg_enqueue_macro(queue, kmsg);
+}
+
+/*
+ * Routine: ipc_kmsg_dequeue
+ * Purpose:
+ * Dequeue and return a kmsg.
+ */
+
+ipc_kmsg_t
+ipc_kmsg_dequeue(
+ ipc_kmsg_queue_t queue)
+{
+ ipc_kmsg_t first;
+
+ first = ipc_kmsg_queue_first(queue);
+
+ if (first != IKM_NULL)
+ ipc_kmsg_rmqueue_first_macro(queue, first);
+
+ return first;
+}
+
+/*
+ * Routine: ipc_kmsg_rmqueue
+ * Purpose:
+ * Pull a kmsg out of a queue.
+ */
+
+void
+ipc_kmsg_rmqueue(
+ ipc_kmsg_queue_t queue,
+ ipc_kmsg_t kmsg)
+{
+ ipc_kmsg_t next, prev;
+
+ assert(queue->ikmq_base != IKM_NULL);
+
+ next = kmsg->ikm_next;
+ prev = kmsg->ikm_prev;
+
+ if (next == kmsg) {
+ assert(prev == kmsg);
+ assert(queue->ikmq_base == kmsg);
+
+ queue->ikmq_base = IKM_NULL;
+ } else {
+ if (queue->ikmq_base == kmsg)
+ queue->ikmq_base = next;
+
+ next->ikm_prev = prev;
+ prev->ikm_next = next;
+ }
+ ikm_mark_bogus (kmsg);
+}
+
+/*
+ * Routine: ipc_kmsg_queue_next
+ * Purpose:
+ * Return the kmsg following the given kmsg.
+ * (Or IKM_NULL if it is the last one in the queue.)
+ */
+
+ipc_kmsg_t
+ipc_kmsg_queue_next(
+ ipc_kmsg_queue_t queue,
+ ipc_kmsg_t kmsg)
+{
+ ipc_kmsg_t next;
+
+ assert(queue->ikmq_base != IKM_NULL);
+
+ next = kmsg->ikm_next;
+ if (queue->ikmq_base == next)
+ next = IKM_NULL;
+
+ return next;
+}
+
+/*
+ * Routine: ipc_kmsg_destroy
+ * Purpose:
+ * Destroys a kernel message. Releases all rights,
+ * references, and memory held by the message.
+ * Frees the message.
+ * Conditions:
+ * No locks held.
+ */
+
+void
+ipc_kmsg_destroy(
+ ipc_kmsg_t kmsg)
+{
+ ipc_kmsg_queue_t queue;
+ boolean_t empty;
+
+ /*
+ * ipc_kmsg_clean can cause more messages to be destroyed.
+ * Curtail recursion by queueing messages. If a message
+ * is already queued, then this is a recursive call.
+ */
+
+ queue = &current_thread()->ith_messages;
+ empty = ipc_kmsg_queue_empty(queue);
+ ipc_kmsg_enqueue(queue, kmsg);
+
+ if (empty) {
+ /* must leave kmsg in queue while cleaning it */
+
+ while ((kmsg = ipc_kmsg_queue_first(queue)) != IKM_NULL) {
+ ipc_kmsg_clean(kmsg);
+ ipc_kmsg_rmqueue(queue, kmsg);
+ ikm_free(kmsg);
+ }
+ }
+}
+
+/*
+ * Routine: ipc_kmsg_clean_body
+ * Purpose:
+ * Cleans the body of a kernel message.
+ * Releases all rights, references, and memory.
+ *
+ * The last type/data pair might stretch past eaddr.
+ * (See the usage in ipc_kmsg_copyout.)
+ * Conditions:
+ * No locks held.
+ */
+
+static void
+ipc_kmsg_clean_body(
+ vm_offset_t saddr,
+ vm_offset_t eaddr)
+{
+ while (saddr < eaddr) {
+ mach_msg_type_long_t *type;
+ mach_msg_type_name_t name;
+ mach_msg_type_size_t size;
+ mach_msg_type_number_t number;
+ boolean_t is_inline, is_port;
+ vm_size_t length;
+
+ type = (mach_msg_type_long_t *) saddr;
+ is_inline = ((mach_msg_type_t*)type)->msgt_inline;
+ if (((mach_msg_type_t*)type)->msgt_longform) {
+ name = type->msgtl_name;
+ size = type->msgtl_size;
+ number = type->msgtl_number;
+ saddr += sizeof(mach_msg_type_long_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ } else {
+ name = ((mach_msg_type_t*)type)->msgt_name;
+ size = ((mach_msg_type_t*)type)->msgt_size;
+ number = ((mach_msg_type_t*)type)->msgt_number;
+ saddr += sizeof(mach_msg_type_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ }
+
+ /* calculate length of data in bytes, rounding up */
+
+ length = ((number * size) + 7) >> 3;
+
+ is_port = MACH_MSG_TYPE_PORT_ANY(name);
+
+ if (is_port) {
+ ipc_object_t *objects;
+ mach_msg_type_number_t i;
+
+ if (is_inline) {
+ objects = (ipc_object_t *) saddr;
+ /* sanity check */
+ while (eaddr < (vm_offset_t)&objects[number]) number--;
+ } else {
+ objects = (ipc_object_t *)
+ * (vm_offset_t *) saddr;
+ }
+
+ /* destroy port rights carried in the message */
+
+ for (i = 0; i < number; i++) {
+ ipc_object_t object = objects[i];
+
+ if (!IO_VALID(object))
+ continue;
+
+ ipc_object_destroy(object, name);
+ }
+ }
+
+ if (is_inline) {
+ saddr += length;
+ } else {
+ vm_offset_t data = * (vm_offset_t *) saddr;
+
+ /* destroy memory carried in the message */
+
+ if (length == 0)
+ assert(data == 0);
+ else if (is_port)
+ kfree(data, length);
+ else
+ vm_map_copy_discard((vm_map_copy_t) data);
+
+ saddr += sizeof(vm_offset_t);
+ }
+ saddr = mach_msg_kernel_align(saddr);
+ }
+}
+
+/*
+ * Routine: ipc_kmsg_clean
+ * Purpose:
+ * Cleans a kernel message. Releases all rights,
+ * references, and memory held by the message.
+ * Conditions:
+ * No locks held.
+ */
+
+void
+ipc_kmsg_clean(ipc_kmsg_t kmsg)
+{
+ ipc_marequest_t marequest;
+ ipc_object_t object;
+ mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits;
+
+ marequest = kmsg->ikm_marequest;
+ if (marequest != IMAR_NULL)
+ ipc_marequest_destroy(marequest);
+
+ object = (ipc_object_t) kmsg->ikm_header.msgh_remote_port;
+ if (IO_VALID(object))
+ ipc_object_destroy(object, MACH_MSGH_BITS_REMOTE(mbits));
+
+ object = (ipc_object_t) kmsg->ikm_header.msgh_local_port;
+ if (IO_VALID(object))
+ ipc_object_destroy(object, MACH_MSGH_BITS_LOCAL(mbits));
+
+ if (mbits & MACH_MSGH_BITS_COMPLEX) {
+ vm_offset_t saddr, eaddr;
+
+ saddr = (vm_offset_t) (&kmsg->ikm_header + 1);
+ eaddr = (vm_offset_t) &kmsg->ikm_header +
+ kmsg->ikm_header.msgh_size;
+
+ ipc_kmsg_clean_body(saddr, eaddr);
+ }
+}
+
+/*
+ * Routine: ipc_kmsg_clean_partial
+ * Purpose:
+ * Cleans a partially-acquired kernel message.
+ * eaddr is the address of the type specification
+ * in the body of the message that contained the error.
+ * If dolast, the memory and port rights in this last
+ * type spec are also cleaned. In that case, number
+ * specifies the number of port rights to clean.
+ * Conditions:
+ * Nothing locked.
+ */
+
+static void
+ipc_kmsg_clean_partial(
+ ipc_kmsg_t kmsg,
+ vm_offset_t eaddr,
+ boolean_t dolast,
+ mach_msg_type_number_t number)
+{
+ ipc_object_t object;
+ mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits;
+ vm_offset_t saddr;
+
+ assert(kmsg->ikm_marequest == IMAR_NULL);
+
+ object = (ipc_object_t) kmsg->ikm_header.msgh_remote_port;
+ assert(IO_VALID(object));
+ ipc_object_destroy(object, MACH_MSGH_BITS_REMOTE(mbits));
+
+ object = (ipc_object_t) kmsg->ikm_header.msgh_local_port;
+ if (IO_VALID(object))
+ ipc_object_destroy(object, MACH_MSGH_BITS_LOCAL(mbits));
+
+ saddr = (vm_offset_t) (&kmsg->ikm_header + 1);
+ ipc_kmsg_clean_body(saddr, eaddr);
+
+ if (dolast) {
+ mach_msg_type_long_t *type;
+ mach_msg_type_name_t name;
+ mach_msg_type_size_t size;
+ mach_msg_type_number_t rnumber;
+ boolean_t is_inline, is_port;
+ vm_size_t length;
+
+ type = (mach_msg_type_long_t *) eaddr;
+ is_inline = ((mach_msg_type_t*)type)->msgt_inline;
+ if (((mach_msg_type_t*)type)->msgt_longform) {
+ name = type->msgtl_name;
+ size = type->msgtl_size;
+ rnumber = type->msgtl_number;
+ eaddr += sizeof(mach_msg_type_long_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) {
+ eaddr = mach_msg_kernel_align(eaddr);
+ }
+ } else {
+ name = ((mach_msg_type_t*)type)->msgt_name;
+ size = ((mach_msg_type_t*)type)->msgt_size;
+ rnumber = ((mach_msg_type_t*)type)->msgt_number;
+ eaddr += sizeof(mach_msg_type_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) {
+ eaddr = mach_msg_kernel_align(eaddr);
+ }
+ }
+
+ /* calculate length of data in bytes, rounding up */
+
+ length = ((rnumber * size) + 7) >> 3;
+
+ is_port = MACH_MSG_TYPE_PORT_ANY(name);
+
+ if (is_port) {
+ ipc_object_t *objects;
+ mach_msg_type_number_t i;
+
+ objects = (ipc_object_t *)
+ (is_inline ? eaddr : * (vm_offset_t *) eaddr);
+
+ /* destroy port rights carried in the message */
+
+ for (i = 0; i < number; i++) {
+ ipc_object_t obj = objects[i];
+
+ if (!IO_VALID(obj))
+ continue;
+
+ ipc_object_destroy(obj, name);
+ }
+ }
+
+ if (!is_inline) {
+ vm_offset_t data = * (vm_offset_t *) eaddr;
+
+ /* destroy memory carried in the message */
+
+ if (length == 0)
+ assert(data == 0);
+ else if (is_port)
+ kfree(data, length);
+ else
+ vm_map_copy_discard((vm_map_copy_t) data);
+ }
+ }
+}
+
+/*
+ * Routine: ipc_kmsg_free
+ * Purpose:
+ * Free a kernel message buffer.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_kmsg_free(ipc_kmsg_t kmsg)
+{
+ vm_size_t size = kmsg->ikm_size;
+
+ switch (size) {
+
+ case IKM_SIZE_NETWORK:
+ /* return it to the network code */
+ net_kmsg_put(kmsg);
+ break;
+
+ default:
+ kfree((vm_offset_t) kmsg, size);
+ break;
+ }
+}
+
+/*
+ * Routine: ipc_kmsg_get
+ * Purpose:
+ * Allocates a kernel message buffer.
+ * Copies a user message to the message buffer.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Acquired a message buffer.
+ * MACH_SEND_MSG_TOO_SMALL Message smaller than a header.
+ * MACH_SEND_MSG_TOO_SMALL Message size not long-word multiple.
+ * MACH_SEND_NO_BUFFER Couldn't allocate a message buffer.
+ * MACH_SEND_INVALID_DATA Couldn't copy message data.
+ */
+
+mach_msg_return_t
+ipc_kmsg_get(
+ mach_msg_user_header_t *msg,
+ mach_msg_size_t size,
+ ipc_kmsg_t *kmsgp)
+{
+ ipc_kmsg_t kmsg;
+ mach_msg_size_t ksize = size * IKM_EXPAND_FACTOR;
+
+ if ((size < sizeof(mach_msg_user_header_t)) || mach_msg_user_is_misaligned(size))
+ return MACH_SEND_MSG_TOO_SMALL;
+
+ if (ksize <= IKM_SAVED_MSG_SIZE) {
+ kmsg = ikm_cache_alloc();
+ if (kmsg == IKM_NULL)
+ return MACH_SEND_NO_BUFFER;
+ } else {
+ kmsg = ikm_alloc(ksize);
+ if (kmsg == IKM_NULL)
+ return MACH_SEND_NO_BUFFER;
+ ikm_init(kmsg, ksize);
+ }
+
+ if (copyinmsg(msg, &kmsg->ikm_header, size, kmsg->ikm_size)) {
+ ikm_free(kmsg);
+ return MACH_SEND_INVALID_DATA;
+ }
+
+ *kmsgp = kmsg;
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_kmsg_get_from_kernel
+ * Purpose:
+ * Allocates a kernel message buffer.
+ * Copies a kernel message to the message buffer.
+ * Only resource errors are allowed.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Acquired a message buffer.
+ * MACH_SEND_NO_BUFFER Couldn't allocate a message buffer.
+ */
+
+extern mach_msg_return_t
+ipc_kmsg_get_from_kernel(
+ mach_msg_header_t *msg,
+ mach_msg_size_t size,
+ ipc_kmsg_t *kmsgp)
+{
+ ipc_kmsg_t kmsg;
+
+ assert(size >= sizeof(mach_msg_header_t));
+ assert(!mach_msg_kernel_is_misaligned(size));
+
+ kmsg = ikm_alloc(size);
+ if (kmsg == IKM_NULL)
+ return MACH_SEND_NO_BUFFER;
+ ikm_init(kmsg, size);
+
+ memcpy(&kmsg->ikm_header, msg, size);
+
+ kmsg->ikm_header.msgh_size = size;
+ *kmsgp = kmsg;
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_kmsg_put
+ * Purpose:
+ * Copies a message buffer to a user message.
+ * Copies only the specified number of bytes.
+ * Frees the message buffer.
+ * Conditions:
+ * Nothing locked. The message buffer must have clean
+ * header (ikm_marequest) fields.
+ * Returns:
+ * MACH_MSG_SUCCESS Copied data out of message buffer.
+ * MACH_RCV_INVALID_DATA Couldn't copy to user message.
+ */
+
+mach_msg_return_t
+ipc_kmsg_put(
+ mach_msg_user_header_t *msg,
+ ipc_kmsg_t kmsg,
+ mach_msg_size_t size)
+{
+ mach_msg_return_t mr;
+
+ ikm_check_initialized(kmsg, kmsg->ikm_size);
+
+ if (copyoutmsg(&kmsg->ikm_header, msg, size))
+ mr = MACH_RCV_INVALID_DATA;
+ else
+ mr = MACH_MSG_SUCCESS;
+
+ ikm_cache_free(kmsg);
+
+ return mr;
+}
+
+/*
+ * Routine: ipc_kmsg_put_to_kernel
+ * Purpose:
+ * Copies a message buffer to a kernel message.
+ * Frees the message buffer.
+ * No errors allowed.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_kmsg_put_to_kernel(
+ mach_msg_header_t *msg,
+ ipc_kmsg_t kmsg,
+ mach_msg_size_t size)
+{
+#if DIPC
+ assert(!KMSG_IN_DIPC(kmsg));
+#endif /* DIPC */
+
+ memcpy(msg, &kmsg->ikm_header, size);
+
+ ikm_free(kmsg);
+}
+
+/*
+ * Routine: ipc_kmsg_copyin_header
+ * Purpose:
+ * "Copy-in" port rights in the header of a message.
+ * Operates atomically; if it doesn't succeed the
+ * message header and the space are left untouched.
+ * If it does succeed the remote/local port fields
+ * contain object pointers instead of port names,
+ * and the bits field is updated. The destination port
+ * will be a valid port pointer.
+ *
+ * The notify argument implements the MACH_SEND_CANCEL option.
+ * If it is not MACH_PORT_NULL, it should name a receive right.
+ * If the processing of the destination port would generate
+ * a port-deleted notification (because the right for the
+ * destination port is destroyed and it had a request for
+ * a dead-name notification registered), and the port-deleted
+ * notification would be sent to the named receive right,
+ * then it isn't sent and the send-once right for the notify
+ * port is quietly destroyed.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Successful copyin.
+ * MACH_SEND_INVALID_HEADER
+ * Illegal value in the message header bits.
+ * MACH_SEND_INVALID_DEST The space is dead.
+ * MACH_SEND_INVALID_NOTIFY
+ * Notify is non-null and doesn't name a receive right.
+ * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.)
+ * MACH_SEND_INVALID_DEST Can't copyin destination port.
+ * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.)
+ * MACH_SEND_INVALID_REPLY Can't copyin reply port.
+ * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.)
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyin_header(
+ mach_msg_header_t *msg,
+ ipc_space_t space,
+ mach_port_name_t notify)
+{
+ mach_msg_bits_t mbits = msg->msgh_bits &~ MACH_MSGH_BITS_CIRCULAR;
+ /*
+ * TODO: For 64 bits, msgh_remote_port as written by user space
+ * is 4 bytes long but here we assume it is the same size as a pointer.
+ * When copying the message to the kernel, we need to perform the
+ * conversion so that port names are parsed correctly.
+ *
+ * When copying the message out of the kernel to user space, we also need
+ * to be careful with the reverse translation.
+ */
+
+ mach_port_name_t dest_name = (mach_port_name_t)msg->msgh_remote_port;
+ mach_port_name_t reply_name = (mach_port_name_t)msg->msgh_local_port;
+ kern_return_t kr;
+
+#ifndef MIGRATING_THREADS
+ /* first check for common cases */
+
+ if (notify == MACH_PORT_NULL) switch (MACH_MSGH_BITS_PORTS(mbits)) {
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0): {
+ ipc_entry_t entry;
+ ipc_entry_bits_t bits;
+ ipc_port_t dest_port;
+
+ /* sending an asynchronous message */
+
+ if (reply_name != MACH_PORT_NULL)
+ break;
+
+ is_read_lock(space);
+ if (!space->is_active)
+ goto abort_async;
+
+ entry = ipc_entry_lookup (space, dest_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, dest_name);
+ goto abort_async;
+ }
+ bits = entry->ie_bits;
+
+ /* check type bits */
+ if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND)
+ goto abort_async;
+
+ /* optimized ipc_right_copyin */
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ dest_port = (ipc_port_t) entry->ie_object;
+ assert(dest_port != IP_NULL);
+
+ ip_lock(dest_port);
+ /* can unlock space now without compromising atomicity */
+ is_read_unlock(space);
+
+ if (!ip_active(dest_port)) {
+ ip_unlock(dest_port);
+ break;
+ }
+
+ assert(dest_port->ip_srights > 0);
+ dest_port->ip_srights++;
+ ip_reference(dest_port);
+ ip_unlock(dest_port);
+
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0));
+ msg->msgh_remote_port = (mach_port_t) dest_port;
+ return MACH_MSG_SUCCESS;
+
+ abort_async:
+ is_read_unlock(space);
+ break;
+ }
+
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE): {
+ ipc_entry_t entry;
+ ipc_entry_bits_t bits;
+ ipc_port_t dest_port, reply_port;
+
+ /* sending a request message */
+
+ is_read_lock(space);
+ if (!space->is_active)
+ goto abort_request;
+
+ entry = ipc_entry_lookup (space, dest_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, dest_name);
+ goto abort_request;
+ }
+ bits = entry->ie_bits;
+
+ /* check type bits */
+ if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND)
+ goto abort_request;
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ dest_port = (ipc_port_t) entry->ie_object;
+ assert(dest_port != IP_NULL);
+
+ entry = ipc_entry_lookup (space, reply_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, reply_name);
+ goto abort_request;
+ }
+ bits = entry->ie_bits;
+
+ /* check type bits */
+ if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_RECEIVE)
+ goto abort_request;
+
+ reply_port = (ipc_port_t) entry->ie_object;
+ assert(reply_port != IP_NULL);
+
+ /*
+ * To do an atomic copyin, need simultaneous
+ * locks on both ports and the space. If
+ * dest_port == reply_port, and simple locking is
+ * enabled, then we will abort. Otherwise it's
+ * OK to unlock twice.
+ */
+
+ ip_lock(dest_port);
+ if (!ip_active(dest_port) || !ip_lock_try(reply_port)) {
+ ip_unlock(dest_port);
+ goto abort_request;
+ }
+ /* can unlock space now without compromising atomicity */
+ is_read_unlock(space);
+
+ assert(dest_port->ip_srights > 0);
+ dest_port->ip_srights++;
+ ip_reference(dest_port);
+ ip_unlock(dest_port);
+
+ assert(ip_active(reply_port));
+ assert(reply_port->ip_receiver_name == reply_name);
+ assert(reply_port->ip_receiver == space);
+
+ reply_port->ip_sorights++;
+ ip_reference(reply_port);
+ ip_unlock(reply_port);
+
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
+ MACH_MSG_TYPE_PORT_SEND_ONCE));
+ msg->msgh_remote_port = (mach_port_t) dest_port;
+ msg->msgh_local_port = (mach_port_t) reply_port;
+ return MACH_MSG_SUCCESS;
+
+ abort_request:
+ is_read_unlock(space);
+ break;
+ }
+
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0): {
+ ipc_entry_t entry;
+ ipc_entry_bits_t bits;
+ ipc_port_t dest_port;
+
+ /* sending a reply message */
+
+ if (reply_name != MACH_PORT_NULL)
+ break;
+
+ is_write_lock(space);
+ if (!space->is_active)
+ goto abort_reply;
+
+ entry = ipc_entry_lookup (space, dest_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, dest_name);
+ goto abort_reply;
+ }
+ bits = entry->ie_bits;
+
+ /* check and type bits */
+ if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND_ONCE)
+ goto abort_reply;
+
+ /* optimized ipc_right_copyin */
+
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+ assert(IE_BITS_UREFS(bits) == 1);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+
+ if (entry->ie_request != 0)
+ goto abort_reply;
+
+ dest_port = (ipc_port_t) entry->ie_object;
+ assert(dest_port != IP_NULL);
+
+ ip_lock(dest_port);
+ if (!ip_active(dest_port)) {
+ ip_unlock(dest_port);
+ goto abort_reply;
+ }
+
+ assert(dest_port->ip_sorights > 0);
+ ip_unlock(dest_port);
+
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc (space, dest_name, entry);
+ is_write_unlock(space);
+
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
+ 0));
+ msg->msgh_remote_port = (mach_port_t) dest_port;
+ return MACH_MSG_SUCCESS;
+
+ abort_reply:
+ is_write_unlock(space);
+ break;
+ }
+
+ default:
+ /* don't bother optimizing */
+ break;
+ }
+#endif /* MIGRATING_THREADS */
+
+ {
+ mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits);
+ mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits);
+ ipc_object_t dest_port, reply_port;
+ ipc_port_t dest_soright, reply_soright;
+ ipc_port_t notify_port = 0; /* '=0' to quiet gcc warnings */
+
+ if (!MACH_MSG_TYPE_PORT_ANY_SEND(dest_type))
+ return MACH_SEND_INVALID_HEADER;
+
+ if ((reply_type == 0) ?
+ (reply_name != MACH_PORT_NULL) :
+ !MACH_MSG_TYPE_PORT_ANY_SEND(reply_type))
+ return MACH_SEND_INVALID_HEADER;
+
+ is_write_lock(space);
+ if (!space->is_active)
+ goto invalid_dest;
+
+ if (notify != MACH_PORT_NULL) {
+ ipc_entry_t entry;
+
+ if (((entry = ipc_entry_lookup(space, notify)) == IE_NULL) ||
+ ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)) {
+ if (entry == IE_NULL)
+ ipc_entry_lookup_failed (msg, notify);
+ is_write_unlock(space);
+ return MACH_SEND_INVALID_NOTIFY;
+ }
+
+ notify_port = (ipc_port_t) entry->ie_object;
+ }
+
+ if (dest_name == reply_name) {
+ ipc_entry_t entry;
+ mach_port_name_t name = dest_name;
+
+ /*
+ * Destination and reply ports are the same!
+ * This is a little tedious to make atomic, because
+ * there are 25 combinations of dest_type/reply_type.
+ * However, most are easy. If either is move-sonce,
+ * then there must be an error. If either are
+ * make-send or make-sonce, then we must be looking
+ * at a receive right so the port can't die.
+ * The hard cases are the combinations of
+ * copy-send and make-send.
+ */
+
+ entry = ipc_entry_lookup(space, name);
+ if (entry == IE_NULL) {
+ ipc_entry_lookup_failed (msg, name);
+ goto invalid_dest;
+ }
+
+ assert(reply_type != 0); /* because name not null */
+
+ if (!ipc_right_copyin_check(space, name, entry, reply_type))
+ goto invalid_reply;
+
+ if ((dest_type == MACH_MSG_TYPE_MOVE_SEND_ONCE) ||
+ (reply_type == MACH_MSG_TYPE_MOVE_SEND_ONCE)) {
+ /*
+ * Why must there be an error? To get a valid
+ * destination, this entry must name a live
+ * port (not a dead name or dead port). However
+ * a successful move-sonce will destroy a
+ * live entry. Therefore the other copyin,
+ * whatever it is, would fail. We've already
+ * checked for reply port errors above,
+ * so report a destination error.
+ */
+
+ goto invalid_dest;
+ } else if ((dest_type == MACH_MSG_TYPE_MAKE_SEND) ||
+ (dest_type == MACH_MSG_TYPE_MAKE_SEND_ONCE) ||
+ (reply_type == MACH_MSG_TYPE_MAKE_SEND) ||
+ (reply_type == MACH_MSG_TYPE_MAKE_SEND_ONCE)) {
+ kr = ipc_right_copyin(space, name, entry,
+ dest_type, FALSE,
+ &dest_port, &dest_soright);
+ if (kr != KERN_SUCCESS)
+ goto invalid_dest;
+
+ /*
+ * Either dest or reply needs a receive right.
+ * We know the receive right is there, because
+ * of the copyin_check and copyin calls. Hence
+ * the port is not in danger of dying. If dest
+ * used the receive right, then the right needed
+ * by reply (and verified by copyin_check) will
+ * still be there.
+ */
+
+ assert(IO_VALID(dest_port));
+ assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
+ assert(dest_soright == IP_NULL);
+
+ kr = ipc_right_copyin(space, name, entry,
+ reply_type, TRUE,
+ &reply_port, &reply_soright);
+
+ assert(kr == KERN_SUCCESS);
+ assert(reply_port == dest_port);
+ assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
+ assert(reply_soright == IP_NULL);
+ } else if ((dest_type == MACH_MSG_TYPE_COPY_SEND) &&
+ (reply_type == MACH_MSG_TYPE_COPY_SEND)) {
+ /*
+ * To make this atomic, just do one copy-send,
+ * and dup the send right we get out.
+ */
+
+ kr = ipc_right_copyin(space, name, entry,
+ dest_type, FALSE,
+ &dest_port, &dest_soright);
+ if (kr != KERN_SUCCESS)
+ goto invalid_dest;
+
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND);
+ assert(dest_soright == IP_NULL);
+
+ /*
+ * It's OK if the port we got is dead now,
+ * so reply_port is IP_DEAD, because the msg
+ * won't go anywhere anyway.
+ */
+
+ reply_port = (ipc_object_t)
+ ipc_port_copy_send((ipc_port_t) dest_port);
+ reply_soright = IP_NULL;
+ } else if ((dest_type == MACH_MSG_TYPE_MOVE_SEND) &&
+ (reply_type == MACH_MSG_TYPE_MOVE_SEND)) {
+ /*
+ * This is an easy case. Just use our
+ * handy-dandy special-purpose copyin call
+ * to get two send rights for the price of one.
+ */
+
+ kr = ipc_right_copyin_two(space, name, entry,
+ &dest_port, &dest_soright);
+ if (kr != KERN_SUCCESS)
+ goto invalid_dest;
+
+ /* the entry might need to be deallocated */
+
+ if (IE_BITS_TYPE(entry->ie_bits)
+ == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, name, entry);
+
+ reply_port = dest_port;
+ reply_soright = IP_NULL;
+ } else {
+ ipc_port_t soright;
+
+ assert(((dest_type == MACH_MSG_TYPE_COPY_SEND) &&
+ (reply_type == MACH_MSG_TYPE_MOVE_SEND)) ||
+ ((dest_type == MACH_MSG_TYPE_MOVE_SEND) &&
+ (reply_type == MACH_MSG_TYPE_COPY_SEND)));
+
+ /*
+ * To make this atomic, just do a move-send,
+ * and dup the send right we get out.
+ */
+
+ kr = ipc_right_copyin(space, name, entry,
+ MACH_MSG_TYPE_MOVE_SEND, FALSE,
+ &dest_port, &soright);
+ if (kr != KERN_SUCCESS)
+ goto invalid_dest;
+
+ /* the entry might need to be deallocated */
+
+ if (IE_BITS_TYPE(entry->ie_bits)
+ == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, name, entry);
+
+ /*
+ * It's OK if the port we got is dead now,
+ * so reply_port is IP_DEAD, because the msg
+ * won't go anywhere anyway.
+ */
+
+ reply_port = (ipc_object_t)
+ ipc_port_copy_send((ipc_port_t) dest_port);
+
+ if (dest_type == MACH_MSG_TYPE_MOVE_SEND) {
+ dest_soright = soright;
+ reply_soright = IP_NULL;
+ } else {
+ dest_soright = IP_NULL;
+ reply_soright = soright;
+ }
+ }
+ } else if (!MACH_PORT_NAME_VALID(reply_name)) {
+ ipc_entry_t entry;
+
+ /*
+ * No reply port! This is an easy case
+ * to make atomic. Just copyin the destination.
+ */
+
+ entry = ipc_entry_lookup(space, dest_name);
+ if (entry == IE_NULL) {
+ ipc_entry_lookup_failed (msg, dest_name);
+ goto invalid_dest;
+ }
+
+ kr = ipc_right_copyin(space, dest_name, entry,
+ dest_type, FALSE,
+ &dest_port, &dest_soright);
+ if (kr != KERN_SUCCESS)
+ goto invalid_dest;
+
+ /* the entry might need to be deallocated */
+
+ if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, dest_name, entry);
+
+ reply_port = (ipc_object_t) invalid_name_to_port(reply_name);
+ reply_soright = IP_NULL;
+ } else {
+ ipc_entry_t dest_entry, reply_entry;
+ ipc_port_t saved_reply;
+
+ /*
+ * This is the tough case to make atomic.
+ * The difficult problem is serializing with port death.
+ * At the time we copyin dest_port, it must be alive.
+ * If reply_port is alive when we copyin it, then
+ * we are OK, because we serialize before the death
+ * of both ports. Assume reply_port is dead at copyin.
+ * Then if dest_port dies/died after reply_port died,
+ * we are OK, because we serialize between the death
+ * of the two ports. So the bad case is when dest_port
+ * dies after its copyin, reply_port dies before its
+ * copyin, and dest_port dies before reply_port. Then
+ * the copyins operated as if dest_port was alive
+ * and reply_port was dead, which shouldn't have happened
+ * because they died in the other order.
+ *
+ * We handle the bad case by undoing the copyins
+ * (which is only possible because the ports are dead)
+ * and failing with MACH_SEND_INVALID_DEST, serializing
+ * after the death of the ports.
+ *
+ * Note that it is easy for a user task to tell if
+ * a copyin happened before or after a port died.
+ * For example, suppose both dest and reply are
+ * send-once rights (types are both move-sonce) and
+ * both rights have dead-name requests registered.
+ * If a port dies before copyin, a dead-name notification
+ * is generated and the dead name's urefs are incremented,
+ * and if the copyin happens first, a port-deleted
+ * notification is generated.
+ *
+ * Note that although the entries are different,
+ * dest_port and reply_port might still be the same.
+ */
+
+ dest_entry = ipc_entry_lookup(space, dest_name);
+ if (dest_entry == IE_NULL) {
+ ipc_entry_lookup_failed (msg, dest_name);
+ goto invalid_dest;
+ }
+
+ reply_entry = ipc_entry_lookup(space, reply_name);
+ if (reply_entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, reply_name);
+ goto invalid_reply;
+ }
+
+ assert(dest_entry != reply_entry); /* names are not equal */
+ assert(reply_type != 0); /* because reply_name not null */
+
+ if (!ipc_right_copyin_check(space, reply_name, reply_entry,
+ reply_type))
+ goto invalid_reply;
+
+ kr = ipc_right_copyin(space, dest_name, dest_entry,
+ dest_type, FALSE,
+ &dest_port, &dest_soright);
+ if (kr != KERN_SUCCESS)
+ goto invalid_dest;
+
+ assert(IO_VALID(dest_port));
+
+ saved_reply = (ipc_port_t) reply_entry->ie_object;
+ /* might be IP_NULL, if this is a dead name */
+ if (saved_reply != IP_NULL)
+ ipc_port_reference(saved_reply);
+
+ kr = ipc_right_copyin(space, reply_name, reply_entry,
+ reply_type, TRUE,
+ &reply_port, &reply_soright);
+ assert(kr == KERN_SUCCESS);
+
+ if ((saved_reply != IP_NULL) && (reply_port == IO_DEAD)) {
+ ipc_port_t dest = (ipc_port_t) dest_port;
+ ipc_port_timestamp_t timestamp;
+ boolean_t must_undo;
+
+ /*
+ * The reply port died before copyin.
+ * Check if dest port died before reply.
+ */
+
+ ip_lock(saved_reply);
+ assert(!ip_active(saved_reply));
+ timestamp = saved_reply->ip_timestamp;
+ ip_unlock(saved_reply);
+
+ ip_lock(dest);
+ must_undo = (!ip_active(dest) &&
+ IP_TIMESTAMP_ORDER(dest->ip_timestamp,
+ timestamp));
+ ip_unlock(dest);
+
+ if (must_undo) {
+ /*
+ * Our worst nightmares are realized.
+ * Both destination and reply ports
+ * are dead, but in the wrong order,
+ * so we must undo the copyins and
+ * possibly generate a dead-name notif.
+ */
+
+ ipc_right_copyin_undo(
+ space, dest_name, dest_entry,
+ dest_type, dest_port,
+ dest_soright);
+ /* dest_entry may be deallocated now */
+
+ ipc_right_copyin_undo(
+ space, reply_name, reply_entry,
+ reply_type, reply_port,
+ reply_soright);
+ /* reply_entry may be deallocated now */
+
+ is_write_unlock(space);
+
+ if (dest_soright != IP_NULL)
+ ipc_notify_dead_name(dest_soright,
+ dest_name);
+ assert(reply_soright == IP_NULL);
+
+ ipc_port_release(saved_reply);
+ return MACH_SEND_INVALID_DEST;
+ }
+ }
+
+ /* the entries might need to be deallocated */
+
+ if (IE_BITS_TYPE(reply_entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, reply_name, reply_entry);
+
+ if (IE_BITS_TYPE(dest_entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, dest_name, dest_entry);
+
+ if (saved_reply != IP_NULL)
+ ipc_port_release(saved_reply);
+ }
+
+ /*
+ * At this point, dest_port, reply_port,
+ * dest_soright, reply_soright are all initialized.
+ * Any defunct entries have been deallocated.
+ * The space is still write-locked, and we need to
+ * make the MACH_SEND_CANCEL check. The notify_port pointer
+ * is still usable, because the copyin code above won't ever
+ * deallocate a receive right, so its entry still exists
+ * and holds a ref. Note notify_port might even equal
+ * dest_port or reply_port.
+ */
+
+ if ((notify != MACH_PORT_NULL) &&
+ (dest_soright == notify_port)) {
+ ipc_port_release_sonce(dest_soright);
+ dest_soright = IP_NULL;
+ }
+
+ is_write_unlock(space);
+
+ if (dest_soright != IP_NULL)
+ ipc_notify_port_deleted(dest_soright, dest_name);
+
+ if (reply_soright != IP_NULL)
+ ipc_notify_port_deleted(reply_soright, reply_name);
+
+ dest_type = ipc_object_copyin_type(dest_type);
+ reply_type = ipc_object_copyin_type(reply_type);
+
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(dest_type, reply_type));
+ msg->msgh_remote_port = (mach_port_t) dest_port;
+ msg->msgh_local_port = (mach_port_t) reply_port;
+ }
+
+ return MACH_MSG_SUCCESS;
+
+ invalid_dest:
+ is_write_unlock(space);
+ return MACH_SEND_INVALID_DEST;
+
+ invalid_reply:
+ is_write_unlock(space);
+ return MACH_SEND_INVALID_REPLY;
+}
+
+static mach_msg_return_t
+ipc_kmsg_copyin_body(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space,
+ vm_map_t map)
+{
+ ipc_object_t dest;
+ vm_offset_t saddr, eaddr;
+ boolean_t complex;
+ boolean_t use_page_lists, steal_pages;
+
+ dest = (ipc_object_t) kmsg->ikm_header.msgh_remote_port;
+ complex = FALSE;
+ use_page_lists = ipc_kobject_vm_page_list(ip_kotype((ipc_port_t)dest));
+ steal_pages = ipc_kobject_vm_page_steal(ip_kotype((ipc_port_t)dest));
+
+ saddr = (vm_offset_t) (&kmsg->ikm_header + 1);
+ eaddr = (vm_offset_t) &kmsg->ikm_header + kmsg->ikm_header.msgh_size;
+
+ // We make assumptions about the alignment of the header.
+ _Static_assert(!mach_msg_kernel_is_misaligned(sizeof(mach_msg_header_t)),
+ "mach_msg_header_t needs to be MACH_MSG_KERNEL_ALIGNMENT aligned.");
+
+ while (saddr < eaddr) {
+ vm_offset_t taddr = saddr;
+ mach_msg_type_long_t *type;
+ mach_msg_type_name_t name;
+ mach_msg_type_size_t size;
+ mach_msg_type_number_t number;
+ boolean_t is_inline, longform, dealloc, is_port;
+ vm_offset_t data;
+ uint64_t length;
+ kern_return_t kr;
+
+ type = (mach_msg_type_long_t *) saddr;
+
+ if (((eaddr - saddr) < sizeof(mach_msg_type_t)) ||
+ ((longform = ((mach_msg_type_t*)type)->msgt_longform) &&
+ ((eaddr - saddr) < sizeof(mach_msg_type_long_t)))) {
+ ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0);
+ return MACH_SEND_MSG_TOO_SMALL;
+ }
+
+ is_inline = ((mach_msg_type_t*)type)->msgt_inline;
+ dealloc = ((mach_msg_type_t*)type)->msgt_deallocate;
+ if (longform) {
+ name = type->msgtl_name;
+ size = type->msgtl_size;
+ number = type->msgtl_number;
+ saddr += sizeof(mach_msg_type_long_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ } else {
+ name = ((mach_msg_type_t*)type)->msgt_name;
+ size = ((mach_msg_type_t*)type)->msgt_size;
+ number = ((mach_msg_type_t*)type)->msgt_number;
+ saddr += sizeof(mach_msg_type_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ }
+
+ is_port = MACH_MSG_TYPE_PORT_ANY(name);
+
+ if ((is_port && (size != PORT_T_SIZE_IN_BITS)) ||
+#ifndef __x86_64__
+ (longform && ((type->msgtl_header.msgt_name != 0) ||
+ (type->msgtl_header.msgt_size != 0) ||
+ (type->msgtl_header.msgt_number != 0))) ||
+#endif
+ (((mach_msg_type_t*)type)->msgt_unused != 0) ||
+ (dealloc && is_inline)) {
+ ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0);
+ return MACH_SEND_INVALID_TYPE;
+ }
+
+ /* calculate length of data in bytes, rounding up */
+
+ length = (((uint64_t) number * size) + 7) >> 3;
+
+ if (is_inline) {
+ vm_size_t amount = length;
+
+ if ((eaddr - saddr) < amount) {
+ ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0);
+ return MACH_SEND_MSG_TOO_SMALL;
+ }
+
+ data = saddr;
+ saddr += amount;
+ } else {
+ vm_offset_t addr;
+
+ if ((eaddr - saddr) < sizeof(vm_offset_t)) {
+ ipc_kmsg_clean_partial(kmsg, taddr, FALSE, 0);
+ return MACH_SEND_MSG_TOO_SMALL;
+ }
+
+ /* grab the out-of-line data */
+
+ addr = * (vm_offset_t *) saddr;
+
+ if (length == 0)
+ data = 0;
+ else if (is_port) {
+ data = kalloc(length);
+ if (data == 0)
+ goto invalid_memory;
+
+ if (sizeof(mach_port_name_t) != sizeof(mach_port_t))
+ {
+ mach_port_name_t *src = (mach_port_name_t*)addr;
+ mach_port_t *dst = (mach_port_t*)data;
+ for (int i=0; i<number; i++) {
+ if (copyin_port(src + i, dst + i)) {
+ kfree(data, length);
+ goto invalid_memory;
+ }
+ }
+ } else if (copyinmap(map, (char *) addr,
+ (char *) data, length)) {
+ kfree(data, length);
+ goto invalid_memory;
+ }
+ if (dealloc &&
+ (vm_deallocate(map, addr, length) != KERN_SUCCESS)) {
+ kfree(data, length);
+ goto invalid_memory;
+ }
+
+ } else {
+ vm_map_copy_t copy;
+
+ if (use_page_lists) {
+ kr = vm_map_copyin_page_list(map,
+ addr, length, dealloc,
+ steal_pages, &copy, FALSE);
+ } else {
+ kr = vm_map_copyin(map, addr, length,
+ dealloc, &copy);
+ }
+ if (kr != KERN_SUCCESS) {
+ invalid_memory:
+ ipc_kmsg_clean_partial(kmsg, taddr,
+ FALSE, 0);
+ return MACH_SEND_INVALID_MEMORY;
+ }
+
+ data = (vm_offset_t) copy;
+ }
+
+ * (vm_offset_t *) saddr = data;
+ saddr += sizeof(vm_offset_t);
+ complex = TRUE;
+ }
+
+ if (is_port) {
+ mach_msg_type_name_t newname =
+ ipc_object_copyin_type(name);
+ ipc_object_t *objects = (ipc_object_t *) data;
+ mach_msg_type_number_t i;
+
+ if (longform)
+ type->msgtl_name = newname;
+ else
+ ((mach_msg_type_t*)type)->msgt_name = newname;
+
+ for (i = 0; i < number; i++) {
+ mach_port_name_t port = ((mach_port_t*)data)[i];
+ ipc_object_t object;
+
+ if (!MACH_PORT_NAME_VALID(port)) {
+ objects[i] = (ipc_object_t)invalid_name_to_port(port);
+ continue;
+ }
+
+ kr = ipc_object_copyin(space, port,
+ name, &object);
+ if (kr != KERN_SUCCESS) {
+ ipc_kmsg_clean_partial(kmsg, taddr,
+ TRUE, i);
+ return MACH_SEND_INVALID_RIGHT;
+ }
+
+ if ((newname == MACH_MSG_TYPE_PORT_RECEIVE) &&
+ ipc_port_check_circularity(
+ (ipc_port_t) object,
+ (ipc_port_t) dest))
+ kmsg->ikm_header.msgh_bits |=
+ MACH_MSGH_BITS_CIRCULAR;
+
+ objects[i] = object;
+ }
+
+ complex = TRUE;
+ }
+ saddr = mach_msg_kernel_align(saddr);
+ }
+
+ if (!complex)
+ kmsg->ikm_header.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX;
+
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_kmsg_copyin
+ * Purpose:
+ * "Copy-in" port rights and out-of-line memory
+ * in the message.
+ *
+ * In all failure cases, the message is left holding
+ * no rights or memory. However, the message buffer
+ * is not deallocated. If successful, the message
+ * contains a valid destination port.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Successful copyin.
+ * MACH_SEND_INVALID_HEADER
+ * Illegal value in the message header bits.
+ * MACH_SEND_INVALID_NOTIFY Bad notify port.
+ * MACH_SEND_INVALID_DEST Can't copyin destination port.
+ * MACH_SEND_INVALID_REPLY Can't copyin reply port.
+ * MACH_SEND_INVALID_MEMORY Can't grab out-of-line memory.
+ * MACH_SEND_INVALID_RIGHT Can't copyin port right in body.
+ * MACH_SEND_INVALID_TYPE Bad type specification.
+ * MACH_SEND_MSG_TOO_SMALL Body is too small for types/data.
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyin(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space,
+ vm_map_t map,
+ mach_port_name_t notify)
+{
+ mach_msg_return_t mr;
+
+ mr = ipc_kmsg_copyin_header(&kmsg->ikm_header, space, notify);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+
+ if ((kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_COMPLEX) == 0)
+ return MACH_MSG_SUCCESS;
+
+ return ipc_kmsg_copyin_body(kmsg, space, map);
+}
+
+/*
+ * Routine: ipc_kmsg_copyin_from_kernel
+ * Purpose:
+ * "Copy-in" port rights and out-of-line memory
+ * in a message sent from the kernel.
+ *
+ * Because the message comes from the kernel,
+ * the implementation assumes there are no errors
+ * or peculiarities in the message.
+ *
+ * Returns TRUE if queueing the message
+ * would result in a circularity.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_kmsg_copyin_from_kernel(ipc_kmsg_t kmsg)
+{
+ mach_msg_bits_t bits = kmsg->ikm_header.msgh_bits;
+ mach_msg_type_name_t rname = MACH_MSGH_BITS_REMOTE(bits);
+ mach_msg_type_name_t lname = MACH_MSGH_BITS_LOCAL(bits);
+ ipc_object_t remote = (ipc_object_t) kmsg->ikm_header.msgh_remote_port;
+ ipc_object_t local = (ipc_object_t) kmsg->ikm_header.msgh_local_port;
+ vm_offset_t saddr, eaddr;
+
+ /* translate the destination and reply ports */
+
+ ipc_object_copyin_from_kernel(remote, rname);
+ if (IO_VALID(local))
+ ipc_object_copyin_from_kernel(local, lname);
+
+ /*
+ * The common case is a complex message with no reply port,
+ * because that is what the memory_object interface uses.
+ */
+
+ if (bits == (MACH_MSGH_BITS_COMPLEX |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0))) {
+ bits = (MACH_MSGH_BITS_COMPLEX |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0));
+
+ kmsg->ikm_header.msgh_bits = bits;
+ } else {
+ bits = (MACH_MSGH_BITS_OTHER(bits) |
+ MACH_MSGH_BITS(ipc_object_copyin_type(rname),
+ ipc_object_copyin_type(lname)));
+
+ kmsg->ikm_header.msgh_bits = bits;
+ if ((bits & MACH_MSGH_BITS_COMPLEX) == 0)
+ return;
+ }
+
+ saddr = (vm_offset_t) (&kmsg->ikm_header + 1);
+ eaddr = (vm_offset_t) &kmsg->ikm_header + kmsg->ikm_header.msgh_size;
+
+ while (saddr < eaddr) {
+ mach_msg_type_long_t *type;
+ mach_msg_type_name_t name;
+ mach_msg_type_size_t size;
+ mach_msg_type_number_t number;
+ boolean_t is_inline, longform, is_port;
+ vm_offset_t data;
+ vm_size_t length;
+
+ type = (mach_msg_type_long_t *) saddr;
+ is_inline = ((mach_msg_type_t*)type)->msgt_inline;
+ longform = ((mach_msg_type_t*)type)->msgt_longform;
+ /* type->msgtl_header.msgt_deallocate not used */
+ if (longform) {
+ name = type->msgtl_name;
+ size = type->msgtl_size;
+ number = type->msgtl_number;
+ saddr += sizeof(mach_msg_type_long_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ } else {
+ name = ((mach_msg_type_t*)type)->msgt_name;
+ size = ((mach_msg_type_t*)type)->msgt_size;
+ number = ((mach_msg_type_t*)type)->msgt_number;
+ saddr += sizeof(mach_msg_type_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ }
+
+ /* calculate length of data in bytes, rounding up */
+
+ length = ((number * size) + 7) >> 3;
+
+ is_port = MACH_MSG_TYPE_PORT_ANY(name);
+
+ if (is_inline) {
+ data = saddr;
+ saddr += length;
+ } else {
+ /*
+ * The sender should supply ready-made memory
+ * for us, so we don't need to do anything.
+ */
+
+ data = * (vm_offset_t *) saddr;
+ saddr += sizeof(vm_offset_t);
+ }
+
+ if (is_port) {
+ mach_msg_type_name_t newname =
+ ipc_object_copyin_type(name);
+ ipc_object_t *objects = (ipc_object_t *) data;
+ mach_msg_type_number_t i;
+
+ if (longform)
+ type->msgtl_name = newname;
+ else
+ ((mach_msg_type_t*)type)->msgt_name = newname;
+ for (i = 0; i < number; i++) {
+ ipc_object_t object = objects[i];
+
+ if (!IO_VALID(object))
+ continue;
+
+ ipc_object_copyin_from_kernel(object, name);
+
+ if ((newname == MACH_MSG_TYPE_PORT_RECEIVE) &&
+ ipc_port_check_circularity(
+ (ipc_port_t) object,
+ (ipc_port_t) remote))
+ kmsg->ikm_header.msgh_bits |=
+ MACH_MSGH_BITS_CIRCULAR;
+ }
+ }
+ saddr = mach_msg_kernel_align(saddr);
+ }
+}
+
+/*
+ * Routine: ipc_kmsg_copyout_header
+ * Purpose:
+ * "Copy-out" port rights in the header of a message.
+ * Operates atomically; if it doesn't succeed the
+ * message header and the space are left untouched.
+ * If it does succeed the remote/local port fields
+ * contain port names instead of object pointers,
+ * and the bits field is updated.
+ *
+ * The notify argument implements the MACH_RCV_NOTIFY option.
+ * If it is not MACH_PORT_NULL, it should name a receive right.
+ * If the process of receiving the reply port creates a
+ * new right in the receiving task, then the new right is
+ * automatically registered for a dead-name notification,
+ * with the notify port supplying the send-once right.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Copied out port rights.
+ * MACH_RCV_INVALID_NOTIFY
+ * Notify is non-null and doesn't name a receive right.
+ * (Either KERN_INVALID_NAME or KERN_INVALID_RIGHT.)
+ * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE
+ * The space is dead.
+ * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE
+ * No room in space for another name.
+ * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_KERNEL
+ * Couldn't allocate memory for the reply port.
+ * MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_KERNEL
+ * Couldn't allocate memory for the dead-name request.
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyout_header(
+ mach_msg_header_t *msg,
+ ipc_space_t space,
+ mach_port_name_t notify)
+{
+ mach_msg_bits_t mbits = msg->msgh_bits;
+ ipc_port_t dest = (ipc_port_t) msg->msgh_remote_port;
+
+ assert(IP_VALID(dest));
+
+#ifndef MIGRATING_THREADS
+ /* first check for common cases */
+
+ if (notify == MACH_PORT_NULL) switch (MACH_MSGH_BITS_PORTS(mbits)) {
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0): {
+ mach_port_name_t dest_name;
+ ipc_port_t nsrequest;
+ rpc_uintptr_t payload;
+
+ /* receiving an asynchronous message */
+
+ ip_lock(dest);
+ if (!ip_active(dest)) {
+ ip_unlock(dest);
+ break;
+ }
+
+ /* optimized ipc_object_copyout_dest */
+
+ assert(dest->ip_srights > 0);
+ ip_release(dest);
+
+ if (dest->ip_receiver == space)
+ dest_name = dest->ip_receiver_name;
+ else
+ dest_name = MACH_PORT_NULL;
+ payload = dest->ip_protected_payload;
+
+ if ((--dest->ip_srights == 0) &&
+ ((nsrequest = dest->ip_nsrequest) != IP_NULL)) {
+ mach_port_mscount_t mscount;
+
+ dest->ip_nsrequest = IP_NULL;
+ mscount = dest->ip_mscount;
+ ip_unlock(dest);
+
+ ipc_notify_no_senders(nsrequest, mscount);
+ } else
+ ip_unlock(dest);
+
+ if (! ipc_port_flag_protected_payload(dest)) {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(0, MACH_MSG_TYPE_PORT_SEND));
+ msg->msgh_local_port = dest_name;
+ } else {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(
+ 0, MACH_MSG_TYPE_PROTECTED_PAYLOAD));
+ msg->msgh_protected_payload = payload;
+ }
+ msg->msgh_remote_port = MACH_PORT_NULL;
+ return MACH_MSG_SUCCESS;
+ }
+
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
+ MACH_MSG_TYPE_PORT_SEND_ONCE): {
+ ipc_entry_t entry;
+ ipc_port_t reply = (ipc_port_t) msg->msgh_local_port;
+ mach_port_name_t dest_name, reply_name;
+ ipc_port_t nsrequest;
+ rpc_uintptr_t payload;
+
+ /* receiving a request message */
+
+ if (!IP_VALID(reply))
+ break;
+
+ is_write_lock(space);
+ if (!space->is_active || space->is_free_list == NULL) {
+ is_write_unlock(space);
+ break;
+ }
+
+ /*
+ * To do an atomic copyout, need simultaneous
+ * locks on both ports and the space. If
+ * dest == reply, and simple locking is
+ * enabled, then we will abort. Otherwise it's
+ * OK to unlock twice.
+ */
+
+ ip_lock(dest);
+ if (!ip_active(dest) || !ip_lock_try(reply)) {
+ ip_unlock(dest);
+ is_write_unlock(space);
+ break;
+ }
+
+ if (!ip_active(reply)) {
+ ip_unlock(reply);
+ ip_unlock(dest);
+ is_write_unlock(space);
+ break;
+ }
+
+ assert(reply->ip_sorights > 0);
+ ip_unlock(reply);
+
+ kern_return_t kr;
+ kr = ipc_entry_get (space, &reply_name, &entry);
+ if (kr) {
+ ip_unlock(reply);
+ ip_unlock(dest);
+ is_write_unlock(space);
+ break;
+ }
+
+ {
+ mach_port_gen_t gen;
+
+ assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0);
+ gen = entry->ie_bits + IE_BITS_GEN_ONE;
+
+ /* optimized ipc_right_copyout */
+
+ entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1);
+ }
+
+ assert(MACH_PORT_NAME_VALID(reply_name));
+ entry->ie_object = (ipc_object_t) reply;
+ is_write_unlock(space);
+
+ /* optimized ipc_object_copyout_dest */
+
+ assert(dest->ip_srights > 0);
+ ip_release(dest);
+
+ if (dest->ip_receiver == space)
+ dest_name = dest->ip_receiver_name;
+ else
+ dest_name = MACH_PORT_NULL;
+ payload = dest->ip_protected_payload;
+
+ if ((--dest->ip_srights == 0) &&
+ ((nsrequest = dest->ip_nsrequest) != IP_NULL)) {
+ mach_port_mscount_t mscount;
+
+ dest->ip_nsrequest = IP_NULL;
+ mscount = dest->ip_mscount;
+ ip_unlock(dest);
+
+ ipc_notify_no_senders(nsrequest, mscount);
+ } else
+ ip_unlock(dest);
+
+ if (! ipc_port_flag_protected_payload(dest)) {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
+ MACH_MSG_TYPE_PORT_SEND));
+ msg->msgh_local_port = dest_name;
+ } else {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
+ MACH_MSG_TYPE_PROTECTED_PAYLOAD));
+ msg->msgh_protected_payload = payload;
+ }
+ msg->msgh_remote_port = reply_name;
+ return MACH_MSG_SUCCESS;
+ }
+
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): {
+ mach_port_name_t dest_name;
+ rpc_uintptr_t payload;
+
+ /* receiving a reply message */
+
+ ip_lock(dest);
+ if (!ip_active(dest)) {
+ ip_unlock(dest);
+ break;
+ }
+
+ /* optimized ipc_object_copyout_dest */
+
+ assert(dest->ip_sorights > 0);
+
+ payload = dest->ip_protected_payload;
+
+ if (dest->ip_receiver == space) {
+ ip_release(dest);
+ dest->ip_sorights--;
+ dest_name = dest->ip_receiver_name;
+ ip_unlock(dest);
+ } else {
+ ip_unlock(dest);
+
+ ipc_notify_send_once(dest);
+ dest_name = MACH_PORT_NULL;
+ }
+
+ if (! ipc_port_flag_protected_payload(dest)) {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(0,
+ MACH_MSG_TYPE_PORT_SEND_ONCE));
+ msg->msgh_local_port = dest_name;
+ } else {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(0,
+ MACH_MSG_TYPE_PROTECTED_PAYLOAD));
+ msg->msgh_protected_payload = payload;
+ }
+ msg->msgh_remote_port = MACH_PORT_NULL;
+ return MACH_MSG_SUCCESS;
+ }
+
+ default:
+ /* don't bother optimizing */
+ break;
+ }
+#endif /* MIGRATING_THREADS */
+
+ {
+ mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits);
+ mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits);
+ ipc_port_t reply = (ipc_port_t) msg->msgh_local_port;
+ mach_port_name_t dest_name, reply_name;
+ rpc_uintptr_t payload;
+
+ if (IP_VALID(reply)) {
+ ipc_port_t notify_port;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ /*
+ * Handling notify (for MACH_RCV_NOTIFY) is tricky.
+ * The problem is atomically making a send-once right
+ * from the notify port and installing it for a
+ * dead-name request in the new entry, because this
+ * requires two port locks (on the notify port and
+ * the reply port). However, we can safely make
+ * and consume send-once rights for the notify port
+ * as long as we hold the space locked. This isn't
+ * an atomicity problem, because the only way
+ * to detect that a send-once right has been created
+ * and then consumed if it wasn't needed is by getting
+ * at the receive right to look at ip_sorights, and
+ * because the space is write-locked status calls can't
+ * lookup the notify port receive right. When we make
+ * the send-once right, we lock the notify port,
+ * so any status calls in progress will be done.
+ */
+
+ is_write_lock(space);
+
+ for (;;) {
+ ipc_port_request_index_t request;
+
+ if (!space->is_active) {
+ is_write_unlock(space);
+ return (MACH_RCV_HEADER_ERROR|
+ MACH_MSG_IPC_SPACE);
+ }
+
+ if (notify != MACH_PORT_NULL) {
+ notify_port = ipc_port_lookup_notify(space,
+ notify);
+ if (notify_port == IP_NULL) {
+ is_write_unlock(space);
+ return MACH_RCV_INVALID_NOTIFY;
+ }
+ } else
+ notify_port = IP_NULL;
+
+ if ((reply_type != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
+ ipc_right_reverse(space, (ipc_object_t) reply,
+ &reply_name, &entry)) {
+ /* reply port is locked and active */
+
+ /*
+ * We don't need the notify_port
+ * send-once right, but we can't release
+ * it here because reply port is locked.
+ * Wait until after the copyout to
+ * release the notify port right.
+ */
+
+ assert(entry->ie_bits &
+ MACH_PORT_TYPE_SEND_RECEIVE);
+ break;
+ }
+
+ ip_lock(reply);
+ if (!ip_active(reply)) {
+ ip_release(reply);
+ ip_check_unlock(reply);
+
+ if (notify_port != IP_NULL)
+ ipc_port_release_sonce(notify_port);
+
+ ip_lock(dest);
+ is_write_unlock(space);
+
+ reply = IP_DEAD;
+ reply_name = MACH_PORT_NAME_DEAD;
+ goto copyout_dest;
+ }
+
+ kr = ipc_entry_alloc(space, &reply_name, &entry);
+ if (kr != KERN_SUCCESS) {
+ ip_unlock(reply);
+
+ if (notify_port != IP_NULL)
+ ipc_port_release_sonce(notify_port);
+
+ is_write_unlock(space);
+ if (kr == KERN_RESOURCE_SHORTAGE)
+ return (MACH_RCV_HEADER_ERROR|
+ MACH_MSG_IPC_KERNEL);
+ else
+ return (MACH_RCV_HEADER_ERROR|
+ MACH_MSG_IPC_SPACE);
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits)
+ == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ if (notify_port == IP_NULL) {
+ /* not making a dead-name request */
+
+ entry->ie_object = (ipc_object_t) reply;
+ break;
+ }
+
+ kr = ipc_port_dnrequest(reply, reply_name,
+ notify_port, &request);
+ if (kr != KERN_SUCCESS) {
+ ip_unlock(reply);
+
+ ipc_port_release_sonce(notify_port);
+
+ ipc_entry_dealloc(space, reply_name, entry);
+ is_write_unlock(space);
+
+ ip_lock(reply);
+ if (!ip_active(reply)) {
+ /* will fail next time around loop */
+
+ ip_unlock(reply);
+ is_write_lock(space);
+ continue;
+ }
+
+ kr = ipc_port_dngrow(reply);
+ /* port is unlocked */
+ if (kr != KERN_SUCCESS)
+ return (MACH_RCV_HEADER_ERROR|
+ MACH_MSG_IPC_KERNEL);
+
+ is_write_lock(space);
+ continue;
+ }
+
+ notify_port = IP_NULL; /* don't release right below */
+
+ entry->ie_object = (ipc_object_t) reply;
+ entry->ie_request = request;
+ break;
+ }
+
+ /* space and reply port are locked and active */
+
+ ip_reference(reply); /* hold onto the reply port */
+
+ kr = ipc_right_copyout(space, reply_name, entry,
+ reply_type, TRUE, (ipc_object_t) reply);
+ /* reply port is unlocked */
+ assert(kr == KERN_SUCCESS);
+
+ if (notify_port != IP_NULL)
+ ipc_port_release_sonce(notify_port);
+
+ ip_lock(dest);
+ is_write_unlock(space);
+ } else {
+ /*
+ * No reply port! This is an easy case.
+ * We only need to have the space locked
+ * when checking notify and when locking
+ * the destination (to ensure atomicity).
+ */
+
+ is_read_lock(space);
+ if (!space->is_active) {
+ is_read_unlock(space);
+ return MACH_RCV_HEADER_ERROR|MACH_MSG_IPC_SPACE;
+ }
+
+ if (notify != MACH_PORT_NULL) {
+ ipc_entry_t entry;
+
+ /* must check notify even though it won't be used */
+
+ if (((entry = ipc_entry_lookup(space, notify))
+ == IE_NULL) ||
+ ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)) {
+ if (entry == IE_NULL)
+ ipc_entry_lookup_failed (msg, notify);
+ is_read_unlock(space);
+ return MACH_RCV_INVALID_NOTIFY;
+ }
+ }
+
+ ip_lock(dest);
+ is_read_unlock(space);
+
+ reply_name = invalid_port_to_name(msg->msgh_local_port);
+ }
+
+ /*
+ * At this point, the space is unlocked and the destination
+ * port is locked. (Lock taken while space was locked.)
+ * reply_name is taken care of; we still need dest_name.
+ * We still hold a ref for reply (if it is valid).
+ *
+ * If the space holds receive rights for the destination,
+ * we return its name for the right. Otherwise the task
+ * managed to destroy or give away the receive right between
+ * receiving the message and this copyout. If the destination
+ * is dead, return MACH_PORT_DEAD, and if the receive right
+ * exists somewhere else (another space, in transit)
+ * return MACH_PORT_NULL.
+ *
+ * Making this copyout operation atomic with the previous
+ * copyout of the reply port is a bit tricky. If there was
+ * no real reply port (it wasn't IP_VALID) then this isn't
+ * an issue. If the reply port was dead at copyout time,
+ * then we are OK, because if dest is dead we serialize
+ * after the death of both ports and if dest is alive
+ * we serialize after reply died but before dest's (later) death.
+ * So assume reply was alive when we copied it out. If dest
+ * is alive, then we are OK because we serialize before
+ * the ports' deaths. So assume dest is dead when we look at it.
+ * If reply dies/died after dest, then we are OK because
+ * we serialize after dest died but before reply dies.
+ * So the hard case is when reply is alive at copyout,
+ * dest is dead at copyout, and reply died before dest died.
+ * In this case pretend that dest is still alive, so
+ * we serialize while both ports are alive.
+ *
+ * Because the space lock is held across the copyout of reply
+ * and locking dest, the receive right for dest can't move
+ * in or out of the space while the copyouts happen, so
+ * that isn't an atomicity problem. In the last hard case
+ * above, this implies that when dest is dead that the
+ * space couldn't have had receive rights for dest at
+ * the time reply was copied-out, so when we pretend
+ * that dest is still alive, we can return MACH_PORT_NULL.
+ *
+ * If dest == reply, then we have to make it look like
+ * either both copyouts happened before the port died,
+ * or both happened after the port died. This special
+ * case works naturally if the timestamp comparison
+ * is done correctly.
+ */
+
+ copyout_dest:
+ payload = dest->ip_protected_payload;
+
+ if (ip_active(dest)) {
+ ipc_object_copyout_dest(space, (ipc_object_t) dest,
+ dest_type, &dest_name);
+ /* dest is unlocked */
+ } else {
+ ipc_port_timestamp_t timestamp;
+
+ timestamp = dest->ip_timestamp;
+ ip_release(dest);
+ ip_check_unlock(dest);
+
+ if (IP_VALID(reply)) {
+ ip_lock(reply);
+ if (ip_active(reply) ||
+ IP_TIMESTAMP_ORDER(timestamp,
+ reply->ip_timestamp))
+ dest_name = MACH_PORT_NAME_DEAD;
+ else
+ dest_name = MACH_PORT_NAME_NULL;
+ ip_unlock(reply);
+ } else
+ dest_name = MACH_PORT_NAME_DEAD;
+ }
+
+ if (IP_VALID(reply))
+ ipc_port_release(reply);
+
+ if (! ipc_port_flag_protected_payload(dest)) {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(reply_type, dest_type));
+ msg->msgh_local_port = dest_name;
+ } else {
+ msg->msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(reply_type,
+ MACH_MSG_TYPE_PROTECTED_PAYLOAD));
+ msg->msgh_protected_payload = payload;
+ }
+
+ msg->msgh_remote_port = reply_name;
+ }
+
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_kmsg_copyout_object
+ * Purpose:
+ * Copy-out a port right. Always returns a name,
+ * even for unsuccessful return codes. Always
+ * consumes the supplied object.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS The space acquired the right
+ * (name is valid) or the object is dead (MACH_PORT_DEAD).
+ * MACH_MSG_IPC_SPACE No room in space for the right,
+ * or the space is dead. (Name is MACH_PORT_NULL.)
+ * MACH_MSG_IPC_KERNEL Kernel resource shortage.
+ * (Name is MACH_PORT_NULL.)
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyout_object(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ mach_port_name_t *namep)
+{
+ if (!IO_VALID(object)) {
+ *namep = invalid_port_to_name((mach_port_t)object);
+ return MACH_MSG_SUCCESS;
+ }
+
+#ifndef MIGRATING_THREADS
+ /*
+ * Attempt quick copyout of send rights. We optimize for a
+ * live port for which the receiver holds send (and not
+ * receive) rights in his local table.
+ */
+
+ if (msgt_name != MACH_MSG_TYPE_PORT_SEND)
+ goto slow_copyout;
+
+ {
+ ipc_port_t port = (ipc_port_t) object;
+ ipc_entry_t entry;
+
+ is_write_lock(space);
+ if (!space->is_active) {
+ is_write_unlock(space);
+ goto slow_copyout;
+ }
+
+ ip_lock(port);
+ if (!ip_active(port) ||
+ (entry = ipc_reverse_lookup(space,
+ (ipc_object_t) port)) == NULL) {
+ ip_unlock(port);
+ is_write_unlock(space);
+ goto slow_copyout;
+ }
+ *namep = entry->ie_name;
+
+ /*
+ * Copyout the send right, incrementing urefs
+ * unless it would overflow, and consume the right.
+ */
+
+ assert(port->ip_srights > 1);
+ port->ip_srights--;
+ ip_release(port);
+ ip_unlock(port);
+
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND);
+ assert(IE_BITS_UREFS(entry->ie_bits) > 0);
+ assert(IE_BITS_UREFS(entry->ie_bits) < MACH_PORT_UREFS_MAX);
+
+ {
+ ipc_entry_bits_t bits = entry->ie_bits + 1;
+
+ if (IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX)
+ entry->ie_bits = bits;
+ }
+
+ is_write_unlock(space);
+ return MACH_MSG_SUCCESS;
+ }
+
+ slow_copyout:
+#endif /* MIGRATING_THREADS */
+
+ {
+ kern_return_t kr;
+
+ kr = ipc_object_copyout(space, object, msgt_name, TRUE, namep);
+ if (kr != KERN_SUCCESS) {
+ ipc_object_destroy(object, msgt_name);
+
+ if (kr == KERN_INVALID_CAPABILITY)
+ *namep = MACH_PORT_NAME_DEAD;
+ else {
+ *namep = MACH_PORT_NAME_NULL;
+
+ if (kr == KERN_RESOURCE_SHORTAGE)
+ return MACH_MSG_IPC_KERNEL;
+ else
+ return MACH_MSG_IPC_SPACE;
+ }
+ }
+
+ return MACH_MSG_SUCCESS;
+ }
+}
+
+/*
+ * Routine: ipc_kmsg_copyout_body
+ * Purpose:
+ * "Copy-out" port rights and out-of-line memory
+ * in the body of a message.
+ *
+ * The error codes are a combination of special bits.
+ * The copyout proceeds despite errors.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Successful copyout.
+ * MACH_MSG_IPC_SPACE No room for port right in name space.
+ * MACH_MSG_VM_SPACE No room for memory in address space.
+ * MACH_MSG_IPC_KERNEL Resource shortage handling port right.
+ * MACH_MSG_VM_KERNEL Resource shortage handling memory.
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyout_body(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space,
+ vm_map_t map)
+{
+ mach_msg_return_t mr = MACH_MSG_SUCCESS;
+ kern_return_t kr;
+ vm_offset_t saddr, eaddr;
+
+ saddr = (vm_offset_t) (&kmsg->ikm_header + 1);
+ eaddr = (vm_offset_t) &kmsg->ikm_header +
+ kmsg->ikm_header.msgh_size;
+
+ while (saddr < eaddr) {
+ vm_offset_t taddr = saddr;
+ mach_msg_type_long_t *type;
+ mach_msg_type_name_t name;
+ mach_msg_type_size_t size;
+ mach_msg_type_number_t number;
+ boolean_t is_inline, longform, is_port;
+ uint64_t length;
+ vm_offset_t addr;
+
+ type = (mach_msg_type_long_t *) saddr;
+ is_inline = ((mach_msg_type_t*)type)->msgt_inline;
+ longform = ((mach_msg_type_t*)type)->msgt_longform;
+ if (longform) {
+ name = type->msgtl_name;
+ size = type->msgtl_size;
+ number = type->msgtl_number;
+ saddr += sizeof(mach_msg_type_long_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ } else {
+ name = ((mach_msg_type_t*)type)->msgt_name;
+ size = ((mach_msg_type_t*)type)->msgt_size;
+ number = ((mach_msg_type_t*)type)->msgt_number;
+ saddr += sizeof(mach_msg_type_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ }
+
+ /* calculate length of data in bytes, rounding up */
+
+ length = (((uint64_t) number * size) + 7) >> 3;
+
+ is_port = MACH_MSG_TYPE_PORT_ANY(name);
+
+ if (is_port) {
+ ipc_object_t *objects;
+ mach_msg_type_number_t i;
+
+ if (!is_inline && (length != 0)) {
+ /* first allocate memory in the map */
+ uint64_t allocated = length;
+
+ _Static_assert(sizeof(mach_port_name_t) <= sizeof(mach_port_t),
+ "Size of mach_port_t should be equal or larger than mach_port_name_t.");
+ allocated -= (sizeof(mach_port_t) - sizeof(mach_port_name_t)) * number;
+
+ kr = vm_allocate(map, &addr, allocated, TRUE);
+ if (kr != KERN_SUCCESS) {
+ ipc_kmsg_clean_body(taddr, saddr);
+ goto vm_copyout_failure;
+ }
+ }
+
+ objects = (ipc_object_t *)
+ (is_inline ? saddr : * (vm_offset_t *) saddr);
+
+ /* copyout port rights carried in the message */
+
+ for (i = 0; i < number; i++) {
+ ipc_object_t object = objects[i];
+
+ mr |= ipc_kmsg_copyout_object_to_port(space, object,
+ name, (mach_port_t *)&objects[i]);
+ }
+ }
+
+ if (is_inline) {
+ ((mach_msg_type_t*)type)->msgt_deallocate = FALSE;
+ saddr += length;
+ } else {
+ vm_offset_t data;
+
+ data = * (vm_offset_t *) saddr;
+
+ /* copyout memory carried in the message */
+
+ if (length == 0) {
+ assert(data == 0);
+ addr = 0;
+ } else if (is_port) {
+ /* copyout to memory allocated above */
+
+ if (sizeof(mach_port_name_t) != sizeof(mach_port_t)) {
+ mach_port_t *src = (mach_port_t*)data;
+ mach_port_name_t *dst = (mach_port_name_t*)addr;
+ for (int i=0; i<number; i++) {
+ if (copyout_port(src + i, dst + i)) {
+ kr = KERN_FAILURE;
+ goto vm_copyout_failure;
+ }
+ }
+ } else {
+ (void) copyoutmap(map, (char *) data,
+ (char *) addr, length);
+ }
+ kfree(data, length);
+ } else {
+ vm_map_copy_t copy = (vm_map_copy_t) data;
+
+ kr = vm_map_copyout(map, &addr, copy);
+ if (kr != KERN_SUCCESS) {
+ vm_map_copy_discard(copy);
+
+ vm_copyout_failure:
+
+ addr = 0;
+ if (longform)
+ type->msgtl_size = 0;
+ else
+ ((mach_msg_type_t*)type)->msgt_size = 0;
+
+ if (kr == KERN_RESOURCE_SHORTAGE)
+ mr |= MACH_MSG_VM_KERNEL;
+ else
+ mr |= MACH_MSG_VM_SPACE;
+ }
+ }
+
+ ((mach_msg_type_t*)type)->msgt_deallocate = TRUE;
+ * (vm_offset_t *) saddr = addr;
+ saddr += sizeof(vm_offset_t);
+ }
+
+ /* Next element is always correctly aligned */
+ saddr = mach_msg_kernel_align(saddr);
+ }
+
+ return mr;
+}
+
+/*
+ * Routine: ipc_kmsg_copyout
+ * Purpose:
+ * "Copy-out" port rights and out-of-line memory
+ * in the message.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Copied out all rights and memory.
+ * MACH_RCV_INVALID_NOTIFY Bad notify port.
+ * Rights and memory in the message are intact.
+ * MACH_RCV_HEADER_ERROR + special bits
+ * Rights and memory in the message are intact.
+ * MACH_RCV_BODY_ERROR + special bits
+ * The message header was successfully copied out.
+ * As much of the body was handled as possible.
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyout(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space,
+ vm_map_t map,
+ mach_port_name_t notify)
+{
+ mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits;
+ mach_msg_return_t mr;
+
+ mr = ipc_kmsg_copyout_header(&kmsg->ikm_header, space, notify);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+
+ if (mbits & MACH_MSGH_BITS_COMPLEX) {
+ mr = ipc_kmsg_copyout_body(kmsg, space, map);
+ if (mr != MACH_MSG_SUCCESS)
+ mr |= MACH_RCV_BODY_ERROR;
+ }
+
+ return mr;
+}
+
+/*
+ * Routine: ipc_kmsg_copyout_pseudo
+ * Purpose:
+ * Does a pseudo-copyout of the message.
+ * This is like a regular copyout, except
+ * that the ports in the header are handled
+ * as if they are in the body. They aren't reversed.
+ *
+ * The error codes are a combination of special bits.
+ * The copyout proceeds despite errors.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Successful copyout.
+ * MACH_MSG_IPC_SPACE No room for port right in name space.
+ * MACH_MSG_VM_SPACE No room for memory in address space.
+ * MACH_MSG_IPC_KERNEL Resource shortage handling port right.
+ * MACH_MSG_VM_KERNEL Resource shortage handling memory.
+ */
+
+mach_msg_return_t
+ipc_kmsg_copyout_pseudo(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space,
+ vm_map_t map)
+{
+ mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits;
+ ipc_object_t dest = (ipc_object_t) kmsg->ikm_header.msgh_remote_port;
+ ipc_object_t reply = (ipc_object_t) kmsg->ikm_header.msgh_local_port;
+ mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits);
+ mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits);
+ mach_port_name_t dest_name, reply_name;
+ mach_msg_return_t mr;
+
+ assert(IO_VALID(dest));
+
+ mr = (ipc_kmsg_copyout_object(space, dest, dest_type, &dest_name) |
+ ipc_kmsg_copyout_object(space, reply, reply_type, &reply_name));
+
+ kmsg->ikm_header.msgh_bits = mbits &~ MACH_MSGH_BITS_CIRCULAR;
+ kmsg->ikm_header.msgh_remote_port = dest_name;
+ kmsg->ikm_header.msgh_local_port = reply_name;
+
+ if (mbits & MACH_MSGH_BITS_COMPLEX) {
+ mr |= ipc_kmsg_copyout_body(kmsg, space, map);
+ }
+
+ return mr;
+}
+
+/*
+ * Routine: ipc_kmsg_copyout_dest
+ * Purpose:
+ * Copies out the destination port in the message.
+ * Destroys all other rights and memory in the message.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_kmsg_copyout_dest(
+ ipc_kmsg_t kmsg,
+ ipc_space_t space)
+{
+ mach_msg_bits_t mbits = kmsg->ikm_header.msgh_bits;
+ ipc_object_t dest = (ipc_object_t) kmsg->ikm_header.msgh_remote_port;
+ ipc_object_t reply = (ipc_object_t) kmsg->ikm_header.msgh_local_port;
+ mach_msg_type_name_t dest_type = MACH_MSGH_BITS_REMOTE(mbits);
+ mach_msg_type_name_t reply_type = MACH_MSGH_BITS_LOCAL(mbits);
+ mach_port_name_t dest_name, reply_name;
+
+ assert(IO_VALID(dest));
+
+ io_lock(dest);
+ if (io_active(dest)) {
+ ipc_object_copyout_dest(space, dest, dest_type, &dest_name);
+ /* dest is unlocked */
+ } else {
+ io_release(dest);
+ io_check_unlock(dest);
+ dest_name = MACH_PORT_NAME_DEAD;
+ }
+
+ if (IO_VALID(reply)) {
+ ipc_object_destroy(reply, reply_type);
+ reply_name = MACH_PORT_NAME_NULL;
+ } else
+ reply_name = invalid_port_to_name((mach_port_t)reply);
+
+ kmsg->ikm_header.msgh_bits = (MACH_MSGH_BITS_OTHER(mbits) |
+ MACH_MSGH_BITS(reply_type, dest_type));
+ kmsg->ikm_header.msgh_local_port = dest_name;
+ kmsg->ikm_header.msgh_remote_port = reply_name;
+
+ if (mbits & MACH_MSGH_BITS_COMPLEX) {
+ vm_offset_t saddr, eaddr;
+
+ saddr = (vm_offset_t) (&kmsg->ikm_header + 1);
+ eaddr = (vm_offset_t) &kmsg->ikm_header +
+ kmsg->ikm_header.msgh_size;
+
+ ipc_kmsg_clean_body(saddr, eaddr);
+ }
+}
+
+#if MACH_KDB
+
+static char *
+ipc_type_name(
+ int type_name,
+ boolean_t received)
+{
+ switch (type_name) {
+ case MACH_MSG_TYPE_BOOLEAN:
+ return "boolean";
+
+ case MACH_MSG_TYPE_INTEGER_16:
+ return "short";
+
+ case MACH_MSG_TYPE_INTEGER_32:
+ return "int32";
+
+ case MACH_MSG_TYPE_INTEGER_64:
+ return "int64";
+
+ case MACH_MSG_TYPE_CHAR:
+ return "char";
+
+ case MACH_MSG_TYPE_BYTE:
+ return "byte";
+
+ case MACH_MSG_TYPE_REAL:
+ return "real";
+
+ case MACH_MSG_TYPE_STRING:
+ return "string";
+
+ case MACH_MSG_TYPE_PORT_NAME:
+ return "port_name";
+
+ case MACH_MSG_TYPE_MOVE_RECEIVE:
+ if (received) {
+ return "port_receive";
+ } else {
+ return "move_receive";
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND:
+ if (received) {
+ return "port_send";
+ } else {
+ return "move_send";
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+ if (received) {
+ return "port_send_once";
+ } else {
+ return "move_send_once";
+ }
+
+ case MACH_MSG_TYPE_COPY_SEND:
+ return "copy_send";
+
+ case MACH_MSG_TYPE_MAKE_SEND:
+ return "make_send";
+
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+ return "make_send_once";
+
+ default:
+ return (char *) 0;
+ }
+}
+
+static void
+ipc_print_type_name(
+ int type_name)
+{
+ char *name = ipc_type_name(type_name, TRUE);
+ if (name) {
+ printf("%s", name);
+ } else {
+ printf("type%d", type_name);
+ }
+}
+
+/*
+ * ipc_kmsg_print [ debug ]
+ */
+void
+ipc_kmsg_print(ipc_kmsg_t kmsg)
+{
+ db_printf("kmsg=0x%x\n", kmsg);
+ db_printf("ikm_next=0x%x,prev=0x%x,size=%d,marequest=0x%x",
+ kmsg->ikm_next,
+ kmsg->ikm_prev,
+ kmsg->ikm_size,
+ kmsg->ikm_marequest);
+ db_printf("\n");
+ ipc_msg_print(&kmsg->ikm_header);
+}
+
+/*
+ * ipc_msg_print [ debug ]
+ */
+void
+ipc_msg_print(mach_msg_header_t *msgh)
+{
+ vm_offset_t saddr, eaddr;
+
+ db_printf("msgh_bits=0x%x: ", msgh->msgh_bits);
+ if (msgh->msgh_bits & MACH_MSGH_BITS_COMPLEX) {
+ db_printf("complex,");
+ }
+ if (msgh->msgh_bits & MACH_MSGH_BITS_CIRCULAR) {
+ db_printf("circular,");
+ }
+ if (msgh->msgh_bits & MACH_MSGH_BITS_COMPLEX_PORTS) {
+ db_printf("complex_ports,");
+ }
+ if (msgh->msgh_bits & MACH_MSGH_BITS_COMPLEX_DATA) {
+ db_printf("complex_data,");
+ }
+ if (msgh->msgh_bits & MACH_MSGH_BITS_MIGRATED) {
+ db_printf("migrated,");
+ }
+ if (msgh->msgh_bits & MACH_MSGH_BITS_UNUSED) {
+ db_printf("unused=0x%x,",
+ msgh->msgh_bits & MACH_MSGH_BITS_UNUSED);
+ }
+ db_printf("l=0x%x,r=0x%x\n",
+ MACH_MSGH_BITS_LOCAL(msgh->msgh_bits),
+ MACH_MSGH_BITS_REMOTE(msgh->msgh_bits));
+
+ db_printf("msgh_id=%d,size=%d,seqno=%d,",
+ msgh->msgh_id,
+ msgh->msgh_size,
+ msgh->msgh_seqno);
+
+ if (msgh->msgh_remote_port) {
+ db_printf("remote=0x%x(", msgh->msgh_remote_port);
+ ipc_print_type_name(MACH_MSGH_BITS_REMOTE(msgh->msgh_bits));
+ db_printf("),");
+ } else {
+ db_printf("remote=null,\n");
+ }
+
+ if (msgh->msgh_local_port) {
+ db_printf("local=0x%x(", msgh->msgh_local_port);
+ ipc_print_type_name(MACH_MSGH_BITS_LOCAL(msgh->msgh_bits));
+ db_printf(")\n");
+ } else {
+ db_printf("local=null\n");
+ }
+
+ saddr = (vm_offset_t) (msgh + 1);
+ eaddr = (vm_offset_t) msgh + msgh->msgh_size;
+
+ while (saddr < eaddr) {
+ mach_msg_type_long_t *type;
+ mach_msg_type_name_t name;
+ mach_msg_type_size_t size;
+ mach_msg_type_number_t number;
+ boolean_t is_inline, longform, dealloc, is_port;
+ vm_size_t length;
+
+ type = (mach_msg_type_long_t *) saddr;
+
+ if (((eaddr - saddr) < sizeof(mach_msg_type_t)) ||
+ ((longform = ((mach_msg_type_t*)type)->msgt_longform) &&
+ ((eaddr - saddr) < sizeof(mach_msg_type_long_t)))) {
+ db_printf("*** msg too small\n");
+ return;
+ }
+
+ is_inline = ((mach_msg_type_t*)type)->msgt_inline;
+ dealloc = ((mach_msg_type_t*)type)->msgt_deallocate;
+ if (longform) {
+ name = type->msgtl_name;
+ size = type->msgtl_size;
+ number = type->msgtl_number;
+ saddr += sizeof(mach_msg_type_long_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_long_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ } else {
+ name = ((mach_msg_type_t*)type)->msgt_name;
+ size = ((mach_msg_type_t*)type)->msgt_size;
+ number = ((mach_msg_type_t*)type)->msgt_number;
+ saddr += sizeof(mach_msg_type_t);
+ if (mach_msg_kernel_is_misaligned(sizeof(mach_msg_type_t))) {
+ saddr = mach_msg_kernel_align(saddr);
+ }
+ }
+
+ db_printf("-- type=");
+ ipc_print_type_name(name);
+ if (! is_inline) {
+ db_printf(",ool");
+ }
+ if (dealloc) {
+ db_printf(",dealloc");
+ }
+ if (longform) {
+ db_printf(",longform");
+ }
+ db_printf(",size=%d,number=%d,addr=0x%x\n",
+ size,
+ number,
+ saddr);
+
+ is_port = MACH_MSG_TYPE_PORT_ANY(name);
+
+ if ((is_port && (size != PORT_T_SIZE_IN_BITS)) ||
+#ifndef __x86_64__
+ (longform && ((type->msgtl_header.msgt_name != 0) ||
+ (type->msgtl_header.msgt_size != 0) ||
+ (type->msgtl_header.msgt_number != 0))) ||
+#endif
+ (((mach_msg_type_t*)type)->msgt_unused != 0) ||
+ (dealloc && is_inline)) {
+ db_printf("*** invalid type\n");
+ return;
+ }
+
+ /* calculate length of data in bytes, rounding up */
+
+ length = ((number * size) + 7) >> 3;
+
+ if (is_inline) {
+ vm_size_t amount;
+ unsigned i, numwords;
+
+ /* round up to int boundaries for printing */
+ amount = (length + 3) &~ 3;
+ if ((eaddr - saddr) < amount) {
+ db_printf("*** too small\n");
+ return;
+ }
+ numwords = amount / sizeof(int);
+ if (numwords > 8) {
+ numwords = 8;
+ }
+ for (i = 0; i < numwords; i++) {
+ db_printf("0x%x\n", ((int *) saddr)[i]);
+ }
+ if (numwords < amount / sizeof(int)) {
+ db_printf("...\n");
+ }
+ saddr += amount;
+ } else {
+ if ((eaddr - saddr) < sizeof(vm_offset_t)) {
+ db_printf("*** too small\n");
+ return;
+ }
+ db_printf("0x%x\n", * (vm_offset_t *) saddr);
+ saddr += sizeof(vm_offset_t);
+ }
+ saddr = mach_msg_kernel_align(saddr);
+ }
+}
+#endif /* MACH_KDB */
diff --git a/ipc/ipc_kmsg.h b/ipc/ipc_kmsg.h
new file mode 100644
index 0000000..9ee1aa4
--- /dev/null
+++ b/ipc/ipc_kmsg.h
@@ -0,0 +1,345 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_kmsg.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for kernel messages.
+ */
+
+#ifndef _IPC_IPC_KMSG_H_
+#define _IPC_IPC_KMSG_H_
+
+#include <mach/machine/vm_types.h>
+#include <mach/message.h>
+#include <kern/assert.h>
+#include <kern/cpu_number.h>
+#include <kern/macros.h>
+#include <kern/kalloc.h>
+#include <ipc/ipc_marequest.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_types.h>
+#include <vm/vm_map.h>
+
+/*
+ * This structure is only the header for a kmsg buffer;
+ * the actual buffer is normally larger. The rest of the buffer
+ * holds the body of the message.
+ *
+ * In a kmsg, the port fields hold pointers to ports instead
+ * of port names. These pointers hold references.
+ *
+ * The ikm_header.msgh_remote_port field is the destination
+ * of the message.
+ */
+
+typedef struct ipc_kmsg {
+ struct ipc_kmsg *ikm_next, *ikm_prev;
+ vm_size_t ikm_size;
+ ipc_marequest_t ikm_marequest;
+ mach_msg_header_t ikm_header;
+} *ipc_kmsg_t;
+
+#define IKM_NULL ((ipc_kmsg_t) 0)
+
+#define IKM_OVERHEAD \
+ (sizeof(struct ipc_kmsg) - sizeof(mach_msg_header_t))
+
+#define ikm_plus_overhead(size) ((vm_size_t)((size) + IKM_OVERHEAD))
+#define ikm_less_overhead(size) ((mach_msg_size_t)((size) - IKM_OVERHEAD))
+
+#if MACH_IPC_TEST
+/*
+ * For debugging.
+ */
+#define IKM_BOGUS ((ipc_kmsg_t) 0xffffff10)
+
+#define ikm_mark_bogus(kmsg) \
+MACRO_BEGIN \
+ (kmsg)->ikm_next = IKM_BOGUS; \
+ (kmsg)->ikm_prev = IKM_BOGUS; \
+MACRO_END
+
+#else /* MACH_IPC_TEST */
+
+#define ikm_mark_bogus(kmsg) ;
+
+#endif /* MACH_IPC_TEST */
+
+/*
+ * We keep a per-processor cache of kernel message buffers.
+ * The cache saves the overhead/locking of using kalloc/kfree.
+ * The per-processor cache seems to miss less than a per-thread cache,
+ * and it also uses less memory. Access to the cache doesn't
+ * require locking.
+ */
+
+extern ipc_kmsg_t ipc_kmsg_cache[NCPUS];
+
+#define ikm_cache() ipc_kmsg_cache[cpu_number()]
+
+#define ikm_cache_alloc_try() \
+MACRO_BEGIN \
+ ipc_kmsg_t __kmsg = ikm_cache(); \
+ if (__kmsg != IKM_NULL) { \
+ ikm_cache() = IKM_NULL; \
+ ikm_check_initialized(__kmsg, IKM_SAVED_KMSG_SIZE); \
+ } \
+ __kmsg; \
+MACRO_END
+
+#define ikm_cache_alloc() \
+MACRO_BEGIN \
+ ipc_kmsg_t __kmsg = ikm_cache_alloc_try(); \
+ if (!__kmsg) { \
+ __kmsg = ikm_alloc(IKM_SAVED_MSG_SIZE); \
+ if (__kmsg != IKM_NULL) \
+ ikm_init(__kmsg, IKM_SAVED_MSG_SIZE); \
+ } \
+ __kmsg; \
+MACRO_END
+
+#define ikm_cache_free_try(kmsg) \
+MACRO_BEGIN \
+ int __success = 0; \
+ if (ikm_cache() == IKM_NULL) { \
+ ikm_cache() = (kmsg); \
+ __success = 1; \
+ } \
+ __success; \
+MACRO_END
+
+#define ikm_cache_free(kmsg) \
+MACRO_BEGIN \
+ if (((kmsg)->ikm_size == IKM_SAVED_KMSG_SIZE) && \
+ (ikm_cache() == IKM_NULL)) \
+ ikm_cache() = (kmsg); \
+ else \
+ ikm_free(kmsg); \
+MACRO_END
+
+/*
+ * The size of the kernel message buffers that will be cached.
+ * IKM_SAVED_KMSG_SIZE includes overhead; IKM_SAVED_MSG_SIZE doesn't.
+ *
+ * We use the page size for IKM_SAVED_KMSG_SIZE to make sure the
+ * page is pinned to a single processor.
+ */
+
+#define IKM_SAVED_KMSG_SIZE PAGE_SIZE
+#define IKM_SAVED_MSG_SIZE ikm_less_overhead(IKM_SAVED_KMSG_SIZE)
+
+#define ikm_alloc(size) \
+ ((ipc_kmsg_t) kalloc(ikm_plus_overhead(size)))
+
+/*
+ * The conversion between userland and kernel-land has to convert from port
+ * names to ports. This may increase the size that needs to be allocated
+ * on the kernel size. At worse the message is full of port names to be
+ * converted.
+ */
+#define IKM_EXPAND_FACTOR ((sizeof(mach_port_t) + sizeof(mach_port_name_t) - 1) / sizeof(mach_port_name_t))
+/* But make sure it's not the converse. */
+_Static_assert(sizeof(mach_port_t) >= sizeof(mach_port_name_t));
+
+#define ikm_init(kmsg, size) \
+MACRO_BEGIN \
+ ikm_init_special((kmsg), ikm_plus_overhead(size)); \
+MACRO_END
+
+#define ikm_init_special(kmsg, size) \
+MACRO_BEGIN \
+ (kmsg)->ikm_size = (size); \
+ (kmsg)->ikm_marequest = IMAR_NULL; \
+MACRO_END
+
+#define ikm_check_initialized(kmsg, size) \
+MACRO_BEGIN \
+ assert((kmsg)->ikm_size == (size)); \
+ assert((kmsg)->ikm_marequest == IMAR_NULL); \
+MACRO_END
+
+/*
+ * Non-positive message sizes are special. They indicate that
+ * the message buffer doesn't come from ikm_alloc and
+ * requires some special handling to free.
+ *
+ * ipc_kmsg_free is the non-macro form of ikm_free.
+ * It frees kmsgs of all varieties.
+ */
+
+#define IKM_SIZE_NORMA 0
+#define IKM_SIZE_NETWORK -1
+
+#define ikm_free(kmsg) \
+MACRO_BEGIN \
+ vm_size_t _size = (kmsg)->ikm_size; \
+ \
+ if ((integer_t)_size > 0) \
+ kfree((vm_offset_t) (kmsg), _size); \
+ else \
+ ipc_kmsg_free(kmsg); \
+MACRO_END
+
+/*
+ * struct ipc_kmsg_queue is defined in ipc/ipc_kmsg_queue.h
+ */
+
+#include <ipc/ipc_kmsg_queue.h>
+
+typedef struct ipc_kmsg_queue *ipc_kmsg_queue_t;
+
+#define IKMQ_NULL ((ipc_kmsg_queue_t) 0)
+
+
+#define ipc_kmsg_queue_init(queue) \
+MACRO_BEGIN \
+ (queue)->ikmq_base = IKM_NULL; \
+MACRO_END
+
+#define ipc_kmsg_queue_empty(queue) ((queue)->ikmq_base == IKM_NULL)
+
+/* Enqueue a kmsg */
+extern void ipc_kmsg_enqueue(
+ ipc_kmsg_queue_t queue,
+ ipc_kmsg_t kmsg);
+
+/* Dequeue and return a kmsg */
+extern ipc_kmsg_t ipc_kmsg_dequeue(
+ ipc_kmsg_queue_t queue);
+
+/* Pull a kmsg out of a queue */
+extern void ipc_kmsg_rmqueue(
+ ipc_kmsg_queue_t queue,
+ ipc_kmsg_t kmsg);
+
+#define ipc_kmsg_queue_first(queue) ((queue)->ikmq_base)
+
+/* Return the kmsg following the given kmsg */
+extern ipc_kmsg_t ipc_kmsg_queue_next(
+ ipc_kmsg_queue_t queue,
+ ipc_kmsg_t kmsg);
+
+#define ipc_kmsg_rmqueue_first_macro(queue, kmsg) \
+MACRO_BEGIN \
+ ipc_kmsg_t _next; \
+ \
+ assert((queue)->ikmq_base == (kmsg)); \
+ \
+ _next = (kmsg)->ikm_next; \
+ if (_next == (kmsg)) { \
+ assert((kmsg)->ikm_prev == (kmsg)); \
+ (queue)->ikmq_base = IKM_NULL; \
+ } else { \
+ ipc_kmsg_t _prev = (kmsg)->ikm_prev; \
+ \
+ (queue)->ikmq_base = _next; \
+ _next->ikm_prev = _prev; \
+ _prev->ikm_next = _next; \
+ } \
+ ikm_mark_bogus (kmsg); \
+MACRO_END
+
+#define ipc_kmsg_enqueue_macro(queue, kmsg) \
+MACRO_BEGIN \
+ ipc_kmsg_t _first = (queue)->ikmq_base; \
+ \
+ if (_first == IKM_NULL) { \
+ (queue)->ikmq_base = (kmsg); \
+ (kmsg)->ikm_next = (kmsg); \
+ (kmsg)->ikm_prev = (kmsg); \
+ } else { \
+ ipc_kmsg_t _last = _first->ikm_prev; \
+ \
+ (kmsg)->ikm_next = _first; \
+ (kmsg)->ikm_prev = _last; \
+ _first->ikm_prev = (kmsg); \
+ _last->ikm_next = (kmsg); \
+ } \
+MACRO_END
+
+extern void
+ipc_kmsg_destroy(ipc_kmsg_t);
+
+extern void
+ipc_kmsg_clean(ipc_kmsg_t);
+
+extern void
+ipc_kmsg_free(ipc_kmsg_t);
+
+extern mach_msg_return_t
+ipc_kmsg_get(mach_msg_user_header_t *, mach_msg_size_t, ipc_kmsg_t *);
+
+extern mach_msg_return_t
+ipc_kmsg_get_from_kernel(mach_msg_header_t *, mach_msg_size_t, ipc_kmsg_t *);
+
+extern mach_msg_return_t
+ipc_kmsg_put(mach_msg_user_header_t *, ipc_kmsg_t, mach_msg_size_t);
+
+extern void
+ipc_kmsg_put_to_kernel(mach_msg_header_t *, ipc_kmsg_t, mach_msg_size_t);
+
+extern mach_msg_return_t
+ipc_kmsg_copyin_header(mach_msg_header_t *, ipc_space_t, mach_port_name_t);
+
+extern mach_msg_return_t
+ipc_kmsg_copyin(ipc_kmsg_t, ipc_space_t, vm_map_t, mach_port_name_t);
+
+extern void
+ipc_kmsg_copyin_from_kernel(ipc_kmsg_t);
+
+extern mach_msg_return_t
+ipc_kmsg_copyout_header(mach_msg_header_t *, ipc_space_t, mach_port_name_t);
+
+extern mach_msg_return_t
+ipc_kmsg_copyout_object(ipc_space_t, ipc_object_t,
+ mach_msg_type_name_t, mach_port_name_t *);
+
+static inline mach_msg_return_t
+ipc_kmsg_copyout_object_to_port(ipc_space_t space, ipc_object_t object,
+ mach_msg_type_name_t msgt_name, mach_port_t *portp)
+{
+ mach_port_name_t name;;
+ mach_msg_return_t mr;
+ mr = ipc_kmsg_copyout_object(space, object, msgt_name, &name);
+ *portp = (mach_port_t)name;
+ return mr;
+}
+
+extern mach_msg_return_t
+ipc_kmsg_copyout_body(ipc_kmsg_t, ipc_space_t, vm_map_t);
+
+extern mach_msg_return_t
+ipc_kmsg_copyout(ipc_kmsg_t, ipc_space_t, vm_map_t, mach_port_name_t);
+
+extern mach_msg_return_t
+ipc_kmsg_copyout_pseudo(ipc_kmsg_t, ipc_space_t, vm_map_t);
+
+extern void
+ipc_kmsg_copyout_dest(ipc_kmsg_t, ipc_space_t);
+
+#endif /* _IPC_IPC_KMSG_H_ */
diff --git a/ipc/ipc_kmsg_queue.h b/ipc/ipc_kmsg_queue.h
new file mode 100644
index 0000000..b4b3df1
--- /dev/null
+++ b/ipc/ipc_kmsg_queue.h
@@ -0,0 +1,31 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+#ifndef _IPC_KMSG_QUEUE_H_
+#define _IPC_KMSG_QUEUE_H_
+struct ipc_kmsg_queue {
+ struct ipc_kmsg *ikmq_base; };
+#endif /* _IPC_KMSG_QUEUE_H_ */
+
diff --git a/ipc/ipc_machdep.h b/ipc/ipc_machdep.h
new file mode 100755
index 0000000..2871fc3
--- /dev/null
+++ b/ipc/ipc_machdep.h
@@ -0,0 +1,39 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#ifndef _IPC_IPC_MACHDEP_H_
+#define _IPC_IPC_MACHDEP_H_
+
+#include <mach/message.h>
+
+/*
+ * At times, we need to know the size of a port in bits
+ */
+
+#define PORT_T_SIZE_IN_BITS (sizeof(mach_port_t)*8)
+#define PORT_NAME_T_SIZE_IN_BITS (sizeof(mach_port_name_t)*8)
+
+#endif /* _IPC_IPC_MACHDEP_H_ */
diff --git a/ipc/ipc_marequest.c b/ipc/ipc_marequest.c
new file mode 100644
index 0000000..c096fe2
--- /dev/null
+++ b/ipc/ipc_marequest.c
@@ -0,0 +1,437 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_marequest.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to handle msg-accepted requests.
+ */
+
+#include <mach/message.h>
+#include <mach/port.h>
+#include <kern/lock.h>
+#include <kern/kalloc.h>
+#include <kern/slab.h>
+#include <ipc/port.h>
+#include <ipc/ipc_init.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_right.h>
+#include <ipc/ipc_marequest.h>
+#include <ipc/ipc_notify.h>
+
+#if MACH_IPC_DEBUG
+#include <mach/kern_return.h>
+#include <mach_debug/hash_info.h>
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_user.h>
+#endif
+
+
+struct kmem_cache ipc_marequest_cache;
+
+#define imar_alloc() ((ipc_marequest_t) kmem_cache_alloc(&ipc_marequest_cache))
+#define imar_free(imar) kmem_cache_free(&ipc_marequest_cache, (vm_offset_t) (imar))
+
+typedef unsigned int ipc_marequest_index_t;
+
+ipc_marequest_index_t ipc_marequest_size;
+ipc_marequest_index_t ipc_marequest_mask;
+
+#define IMAR_HASH(space, name) \
+ ((((ipc_marequest_index_t)((vm_offset_t)space) >> 4) + \
+ MACH_PORT_INDEX(name) + MACH_PORT_NGEN(name)) & \
+ ipc_marequest_mask)
+
+typedef struct ipc_marequest_bucket {
+ decl_simple_lock_data(, imarb_lock_data)
+ ipc_marequest_t imarb_head;
+} *ipc_marequest_bucket_t;
+
+#define IMARB_NULL ((ipc_marequest_bucket_t) 0)
+
+#define imarb_lock_init(imarb) simple_lock_init(&(imarb)->imarb_lock_data)
+#define imarb_lock(imarb) simple_lock(&(imarb)->imarb_lock_data)
+#define imarb_unlock(imarb) simple_unlock(&(imarb)->imarb_lock_data)
+
+ipc_marequest_bucket_t ipc_marequest_table;
+
+
+
+/*
+ * Routine: ipc_marequest_init
+ * Purpose:
+ * Initialize the msg-accepted request module.
+ */
+
+void
+ipc_marequest_init(void)
+{
+ ipc_marequest_index_t i;
+
+ /* initialize ipc_marequest_size */
+
+ ipc_marequest_size = IPC_MAREQUEST_SIZE;
+
+ /* make sure it is a power of two */
+
+ ipc_marequest_mask = ipc_marequest_size - 1;
+ if ((ipc_marequest_size & ipc_marequest_mask) != 0) {
+ unsigned int bit;
+
+ /* round up to closest power of two */
+
+ for (bit = 1;; bit <<= 1) {
+ ipc_marequest_mask |= bit;
+ ipc_marequest_size = ipc_marequest_mask + 1;
+
+ if ((ipc_marequest_size & ipc_marequest_mask) == 0)
+ break;
+ }
+ }
+
+ /* allocate ipc_marequest_table */
+
+ ipc_marequest_table = (ipc_marequest_bucket_t)
+ kalloc((vm_size_t) (ipc_marequest_size *
+ sizeof(struct ipc_marequest_bucket)));
+ assert(ipc_marequest_table != IMARB_NULL);
+
+ /* and initialize it */
+
+ for (i = 0; i < ipc_marequest_size; i++) {
+ ipc_marequest_bucket_t bucket;
+
+ bucket = &ipc_marequest_table[i];
+ imarb_lock_init(bucket);
+ bucket->imarb_head = IMAR_NULL;
+ }
+
+ kmem_cache_init(&ipc_marequest_cache, "ipc_marequest",
+ sizeof(struct ipc_marequest), 0, NULL, 0);
+}
+
+/*
+ * Routine: ipc_marequest_create
+ * Purpose:
+ * Create a msg-accepted request, because
+ * a sender is forcing a message with MACH_SEND_NOTIFY.
+ *
+ * The "notify" argument should name a receive right
+ * that is used to create the send-once notify port.
+ * Conditions:
+ * Nothing locked; refs held for space and port.
+ * Returns:
+ * MACH_MSG_SUCCESS Msg-accepted request created.
+ * MACH_SEND_INVALID_NOTIFY The space is dead.
+ * MACH_SEND_INVALID_NOTIFY The notify port is bad.
+ * MACH_SEND_NOTIFY_IN_PROGRESS
+ * This space has already forced a message to this port.
+ * MACH_SEND_NO_NOTIFY Can't allocate a msg-accepted request.
+ */
+
+mach_msg_return_t
+ipc_marequest_create(
+ ipc_space_t space,
+ ipc_port_t port,
+ mach_port_name_t notify,
+ ipc_marequest_t *marequestp)
+{
+ mach_port_name_t name;
+ ipc_entry_t entry;
+ ipc_port_t soright;
+ ipc_marequest_t marequest;
+ ipc_marequest_bucket_t bucket;
+
+ marequest = imar_alloc();
+ if (marequest == IMAR_NULL)
+ return MACH_SEND_NO_NOTIFY;
+
+ /*
+ * Delay creating the send-once right until
+ * we know there will be no errors. Otherwise,
+ * we would have to worry about disposing of it
+ * when it turned out it wasn't needed.
+ */
+
+ is_write_lock(space);
+ if (!space->is_active) {
+ is_write_unlock(space);
+ imar_free(marequest);
+ return MACH_SEND_INVALID_NOTIFY;
+ }
+
+ if (ipc_right_reverse(space, (ipc_object_t) port, &name, &entry)) {
+ ipc_entry_bits_t bits;
+
+ /* port is locked and active */
+ ip_unlock(port);
+ bits = entry->ie_bits;
+
+ assert(port == (ipc_port_t) entry->ie_object);
+ assert(bits & MACH_PORT_TYPE_SEND_RECEIVE);
+
+ if (bits & IE_BITS_MAREQUEST) {
+ is_write_unlock(space);
+ imar_free(marequest);
+ return MACH_SEND_NOTIFY_IN_PROGRESS;
+ }
+
+ if ((soright = ipc_port_lookup_notify(space, notify))
+ == IP_NULL) {
+ is_write_unlock(space);
+ imar_free(marequest);
+ return MACH_SEND_INVALID_NOTIFY;
+ }
+
+ entry->ie_bits = bits | IE_BITS_MAREQUEST;
+
+ is_reference(space);
+ marequest->imar_space = space;
+ marequest->imar_name = name;
+ marequest->imar_soright = soright;
+
+ bucket = &ipc_marequest_table[IMAR_HASH(space, name)];
+ imarb_lock(bucket);
+
+ marequest->imar_next = bucket->imarb_head;
+ bucket->imarb_head = marequest;
+
+ imarb_unlock(bucket);
+ } else {
+ if ((soright = ipc_port_lookup_notify(space, notify))
+ == IP_NULL) {
+ is_write_unlock(space);
+ imar_free(marequest);
+ return MACH_SEND_INVALID_NOTIFY;
+ }
+
+ is_reference(space);
+ marequest->imar_space = space;
+ marequest->imar_name = MACH_PORT_NULL;
+ marequest->imar_soright = soright;
+ }
+
+ is_write_unlock(space);
+ *marequestp = marequest;
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_marequest_cancel
+ * Purpose:
+ * Cancel a msg-accepted request, because
+ * the space's entry is being destroyed.
+ * Conditions:
+ * The space is write-locked and active.
+ */
+
+void
+ipc_marequest_cancel(
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ ipc_marequest_bucket_t bucket;
+ ipc_marequest_t marequest, *last;
+
+ assert(space->is_active);
+
+ bucket = &ipc_marequest_table[IMAR_HASH(space, name)];
+ imarb_lock(bucket);
+
+ for (last = &bucket->imarb_head;
+ (marequest = *last) != IMAR_NULL;
+ last = &marequest->imar_next)
+ if ((marequest->imar_space == space) &&
+ (marequest->imar_name == name))
+ break;
+
+ assert(marequest != IMAR_NULL);
+ *last = marequest->imar_next;
+ imarb_unlock(bucket);
+
+ marequest->imar_name = MACH_PORT_NAME_NULL;
+}
+
+/*
+ * Routine: ipc_marequest_rename
+ * Purpose:
+ * Rename a msg-accepted request, because the entry
+ * in the space is being renamed.
+ * Conditions:
+ * The space is write-locked and active.
+ */
+
+void
+ipc_marequest_rename(
+ ipc_space_t space,
+ mach_port_name_t old,
+ mach_port_name_t new)
+{
+ ipc_marequest_bucket_t bucket;
+ ipc_marequest_t marequest, *last;
+
+ assert(space->is_active);
+
+ bucket = &ipc_marequest_table[IMAR_HASH(space, old)];
+ imarb_lock(bucket);
+
+ for (last = &bucket->imarb_head;
+ (marequest = *last) != IMAR_NULL;
+ last = &marequest->imar_next)
+ if ((marequest->imar_space == space) &&
+ (marequest->imar_name == old))
+ break;
+
+ assert(marequest != IMAR_NULL);
+ *last = marequest->imar_next;
+ imarb_unlock(bucket);
+
+ marequest->imar_name = new;
+
+ bucket = &ipc_marequest_table[IMAR_HASH(space, new)];
+ imarb_lock(bucket);
+
+ marequest->imar_next = bucket->imarb_head;
+ bucket->imarb_head = marequest;
+
+ imarb_unlock(bucket);
+}
+
+/*
+ * Routine: ipc_marequest_destroy
+ * Purpose:
+ * Destroy a msg-accepted request, because
+ * the kernel message is being received/destroyed.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_marequest_destroy(ipc_marequest_t marequest)
+{
+ ipc_space_t space = marequest->imar_space;
+ mach_port_name_t name;
+ ipc_port_t soright;
+
+ is_write_lock(space);
+
+ name = marequest->imar_name;
+ soright = marequest->imar_soright;
+
+ if (name != MACH_PORT_NULL) {
+ ipc_marequest_bucket_t bucket;
+ ipc_marequest_t this, *last;
+
+ bucket = &ipc_marequest_table[IMAR_HASH(space, name)];
+ imarb_lock(bucket);
+
+ for (last = &bucket->imarb_head;
+ (this = *last) != IMAR_NULL;
+ last = &this->imar_next)
+ if ((this->imar_space == space) &&
+ (this->imar_name == name))
+ break;
+
+ assert(this == marequest);
+ *last = this->imar_next;
+ imarb_unlock(bucket);
+
+ if (space->is_active) {
+ ipc_entry_t entry;
+
+ entry = ipc_entry_lookup(space, name);
+ assert(entry != IE_NULL);
+ assert(entry->ie_bits & IE_BITS_MAREQUEST);
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
+
+ entry->ie_bits &= ~IE_BITS_MAREQUEST;
+
+ } else
+ name = MACH_PORT_NAME_NULL;
+ }
+
+ is_write_unlock(space);
+ is_release(space);
+
+ imar_free(marequest);
+
+ assert(soright != IP_NULL);
+ ipc_notify_msg_accepted(soright, name);
+}
+
+#if MACH_IPC_DEBUG
+
+
+/*
+ * Routine: ipc_marequest_info
+ * Purpose:
+ * Return information about the marequest hash table.
+ * Fills the buffer with as much information as possible
+ * and returns the desired size of the buffer.
+ * Conditions:
+ * Nothing locked. The caller should provide
+ * possibly-pageable memory.
+ */
+
+unsigned int
+ipc_marequest_info(
+ unsigned int *maxp,
+ hash_info_bucket_t *info,
+ unsigned int count)
+{
+ ipc_marequest_index_t i;
+
+ if (ipc_marequest_size < count)
+ count = ipc_marequest_size;
+
+ for (i = 0; i < count; i++) {
+ ipc_marequest_bucket_t bucket = &ipc_marequest_table[i];
+ unsigned int bucket_count = 0;
+ ipc_marequest_t marequest;
+
+ imarb_lock(bucket);
+ for (marequest = bucket->imarb_head;
+ marequest != IMAR_NULL;
+ marequest = marequest->imar_next)
+ bucket_count++;
+ imarb_unlock(bucket);
+
+ /* don't touch pageable memory while holding locks */
+ info[i].hib_count = bucket_count;
+ }
+
+ *maxp = (unsigned int)-1;
+ return ipc_marequest_size;
+}
+
+#endif /* MACH_IPC_DEBUG */
diff --git a/ipc/ipc_marequest.h b/ipc/ipc_marequest.h
new file mode 100644
index 0000000..a55d4e2
--- /dev/null
+++ b/ipc/ipc_marequest.h
@@ -0,0 +1,99 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_marequest.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for msg-accepted requests.
+ */
+
+#ifndef _IPC_IPC_MAREQUEST_H_
+#define _IPC_IPC_MAREQUEST_H_
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach_debug/hash_info.h>
+#include <ipc/ipc_types.h>
+
+/*
+ * A msg-accepted request is made when MACH_SEND_NOTIFY is used
+ * to force a message to a send right. The IE_BITS_MAREQUEST bit
+ * in an entry indicates the entry is blocked because MACH_SEND_NOTIFY
+ * has already been used to force a message. The kmsg holds
+ * a pointer to the marequest; it is destroyed when the kmsg
+ * is received/destroyed. (If the send right is destroyed,
+ * this just changes imar_name. If the space is destroyed,
+ * the marequest is left unchanged.)
+ *
+ * Locking considerations: The imar_space field is read-only and
+ * points to the space which locks the imar_name field. imar_soright
+ * is read-only. Normally it is a non-null send-once right for
+ * the msg-accepted notification, but in compat mode it is null
+ * and the notification goes to the space's notify port. Normally
+ * imar_name is non-null, but if the send right is destroyed then
+ * it is changed to be null. imar_next is locked by a bucket lock;
+ * imar_name is read-only when the request is in a bucket. (So lookups
+ * in the bucket can safely check imar_space and imar_name.)
+ * imar_space and imar_soright both hold references.
+ */
+
+typedef struct ipc_marequest {
+ struct ipc_space *imar_space;
+ mach_port_name_t imar_name;
+ struct ipc_port *imar_soright;
+ struct ipc_marequest *imar_next;
+} *ipc_marequest_t;
+
+#define IMAR_NULL ((ipc_marequest_t) 0)
+
+#define IPC_MAREQUEST_SIZE 16
+
+extern void
+ipc_marequest_init(void);
+
+#if MACH_IPC_DEBUG
+
+extern unsigned int
+ipc_marequest_info(unsigned int *, hash_info_bucket_t *, unsigned int);
+
+#endif /* MACH_IPC_DEBUG */
+
+extern mach_msg_return_t
+ipc_marequest_create(ipc_space_t space, ipc_port_t port,
+ mach_port_name_t notify, ipc_marequest_t *marequestp);
+
+extern void
+ipc_marequest_cancel(ipc_space_t space, mach_port_name_t name);
+
+extern void
+ipc_marequest_rename(ipc_space_t space,
+ mach_port_name_t old, mach_port_name_t new);
+
+extern void
+ipc_marequest_destroy(ipc_marequest_t marequest);
+
+#endif /* _IPC_IPC_MAREQUEST_H_ */
diff --git a/ipc/ipc_mqueue.c b/ipc/ipc_mqueue.c
new file mode 100644
index 0000000..44e1eb9
--- /dev/null
+++ b/ipc/ipc_mqueue.c
@@ -0,0 +1,695 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_mqueue.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC message queues.
+ */
+
+#include <mach/port.h>
+#include <mach/message.h>
+#include <machine/copy_user.h>
+#include <kern/assert.h>
+#include <kern/counters.h>
+#include <kern/debug.h>
+#include <kern/sched_prim.h>
+#include <kern/ipc_sched.h>
+#include <kern/ipc_kobject.h>
+#include <ipc/ipc_mqueue.h>
+#include <ipc/ipc_thread.h>
+#include <ipc/ipc_kmsg.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_marequest.h>
+
+
+
+/*
+ * Routine: ipc_mqueue_init
+ * Purpose:
+ * Initialize a newly-allocated message queue.
+ */
+
+void
+ipc_mqueue_init(
+ ipc_mqueue_t mqueue)
+{
+ imq_lock_init(mqueue);
+ ipc_kmsg_queue_init(&mqueue->imq_messages);
+ ipc_thread_queue_init(&mqueue->imq_threads);
+}
+
+/*
+ * Routine: ipc_mqueue_move
+ * Purpose:
+ * Move messages from one queue (source) to another (dest).
+ * Only moves messages sent to the specified port.
+ * Conditions:
+ * Both queues must be locked.
+ * (This is sufficient to manipulate port->ip_seqno.)
+ */
+
+void
+ipc_mqueue_move(
+ ipc_mqueue_t dest,
+ ipc_mqueue_t source,
+ const ipc_port_t port)
+{
+ ipc_kmsg_queue_t oldq, newq;
+ ipc_thread_queue_t blockedq;
+ ipc_kmsg_t kmsg, next;
+ ipc_thread_t th;
+
+ oldq = &source->imq_messages;
+ newq = &dest->imq_messages;
+ blockedq = &dest->imq_threads;
+
+ for (kmsg = ipc_kmsg_queue_first(oldq);
+ kmsg != IKM_NULL; kmsg = next) {
+ next = ipc_kmsg_queue_next(oldq, kmsg);
+
+ /* only move messages sent to port */
+
+ if (kmsg->ikm_header.msgh_remote_port != (mach_port_t) port)
+ continue;
+
+ ipc_kmsg_rmqueue(oldq, kmsg);
+
+ /* before adding kmsg to newq, check for a blocked receiver */
+
+ while ((th = ipc_thread_dequeue(blockedq)) != ITH_NULL) {
+ assert(ipc_kmsg_queue_empty(newq));
+
+ thread_go(th);
+
+ /* check if the receiver can handle the message */
+
+ if (kmsg->ikm_header.msgh_size <= th->ith_msize) {
+ th->ith_state = MACH_MSG_SUCCESS;
+ th->ith_kmsg = kmsg;
+ th->ith_seqno = port->ip_seqno++;
+
+ goto next_kmsg;
+ }
+
+ th->ith_state = MACH_RCV_TOO_LARGE;
+ th->ith_msize = kmsg->ikm_header.msgh_size;
+ }
+
+ /* didn't find a receiver to handle the message */
+
+ ipc_kmsg_enqueue(newq, kmsg);
+ next_kmsg:;
+ }
+}
+
+/*
+ * Routine: ipc_mqueue_changed
+ * Purpose:
+ * Wake up receivers waiting in a message queue.
+ * Conditions:
+ * The message queue is locked.
+ */
+
+void
+ipc_mqueue_changed(
+ ipc_mqueue_t mqueue,
+ mach_msg_return_t mr)
+{
+ ipc_thread_t th;
+
+ while ((th = ipc_thread_dequeue(&mqueue->imq_threads)) != ITH_NULL) {
+ th->ith_state = mr;
+ thread_go(th);
+ }
+}
+
+/*
+ * Routine: ipc_mqueue_send
+ * Purpose:
+ * Send a message to a port. The message holds a reference
+ * for the destination port in the msgh_remote_port field.
+ *
+ * If unsuccessful, the caller still has possession of
+ * the message and must do something with it. If successful,
+ * the message is queued, given to a receiver, destroyed,
+ * or handled directly by the kernel via mach_msg.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS The message was accepted.
+ * MACH_SEND_TIMED_OUT Caller still has message.
+ * MACH_SEND_INTERRUPTED Caller still has message.
+ */
+
+mach_msg_return_t
+ipc_mqueue_send(
+ ipc_kmsg_t kmsg,
+ mach_msg_option_t option,
+ mach_msg_timeout_t time_out)
+{
+ ipc_port_t port;
+
+ port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
+ assert(IP_VALID(port));
+
+ ip_lock(port);
+
+ if (port->ip_receiver == ipc_space_kernel) {
+ ipc_kmsg_t reply;
+
+ /*
+ * We can check ip_receiver == ipc_space_kernel
+ * before checking that the port is active because
+ * ipc_port_dealloc_kernel clears ip_receiver
+ * before destroying a kernel port.
+ */
+
+ assert(ip_active(port));
+ ip_unlock(port);
+
+ reply = ipc_kobject_server(kmsg);
+ if (reply != IKM_NULL)
+ ipc_mqueue_send_always(reply);
+
+ return MACH_MSG_SUCCESS;
+ }
+
+ for (;;) {
+ ipc_thread_t self;
+
+ /*
+ * Can't deliver to a dead port.
+ * However, we can pretend it got sent
+ * and was then immediately destroyed.
+ */
+
+ if (!ip_active(port)) {
+ /*
+ * We can't let ipc_kmsg_destroy deallocate
+ * the port right, because we might end up
+ * in an infinite loop trying to deliver
+ * a send-once notification.
+ */
+
+ ip_release(port);
+ ip_check_unlock(port);
+ kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
+ ipc_kmsg_destroy(kmsg);
+ return MACH_MSG_SUCCESS;
+ }
+
+ /*
+ * Don't block if:
+ * 1) We're under the queue limit.
+ * 2) Caller used the MACH_SEND_ALWAYS internal option.
+ * 3) Message is sent to a send-once right.
+ */
+
+ if ((port->ip_msgcount < port->ip_qlimit) ||
+ (option & MACH_SEND_ALWAYS) ||
+ (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
+ MACH_MSG_TYPE_PORT_SEND_ONCE))
+ break;
+
+ /* must block waiting for queue to clear */
+
+ self = current_thread();
+
+ if (option & MACH_SEND_TIMEOUT) {
+ if (time_out == 0) {
+ ip_unlock(port);
+ return MACH_SEND_TIMED_OUT;
+ }
+
+ thread_will_wait_with_timeout(self, time_out);
+ } else
+ thread_will_wait(self);
+
+ ipc_thread_enqueue(&port->ip_blocked, self);
+ self->ith_state = MACH_SEND_IN_PROGRESS;
+
+ ip_unlock(port);
+ counter(c_ipc_mqueue_send_block++);
+ thread_block(thread_no_continuation);
+ ip_lock(port);
+
+ /* why did we wake up? */
+
+ if (self->ith_state == MACH_MSG_SUCCESS)
+ continue;
+ assert(self->ith_state == MACH_SEND_IN_PROGRESS);
+
+ /* take ourselves off blocked queue */
+
+ ipc_thread_rmqueue(&port->ip_blocked, self);
+
+ /*
+ * Thread wakeup-reason field tells us why
+ * the wait was interrupted.
+ */
+
+ switch (self->ith_wait_result) {
+ case THREAD_INTERRUPTED:
+ /* send was interrupted - give up */
+
+ ip_unlock(port);
+ return MACH_SEND_INTERRUPTED;
+
+ case THREAD_TIMED_OUT:
+ /* timeout expired */
+
+ assert(option & MACH_SEND_TIMEOUT);
+ time_out = 0;
+ break;
+
+ case THREAD_RESTART:
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_mqueue_send");
+#else
+ panic("ipc_mqueue_send");
+#endif
+ }
+ }
+
+ if (kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) {
+ ip_unlock(port);
+
+ /* don't allow the creation of a circular loop */
+
+ ipc_kmsg_destroy(kmsg);
+ return MACH_MSG_SUCCESS;
+ }
+
+ {
+ ipc_mqueue_t mqueue;
+ ipc_pset_t pset;
+ ipc_thread_t receiver;
+ ipc_thread_queue_t receivers;
+
+ port->ip_msgcount++;
+ assert(port->ip_msgcount > 0);
+
+ pset = port->ip_pset;
+ if (pset == IPS_NULL)
+ mqueue = &port->ip_messages;
+ else
+ mqueue = &pset->ips_messages;
+
+ imq_lock(mqueue);
+ receivers = &mqueue->imq_threads;
+
+ /*
+ * Can unlock the port now that the msg queue is locked
+ * and we know the port is active. While the msg queue
+ * is locked, we have control of the kmsg, so the ref in
+ * it for the port is still good. If the msg queue is in
+ * a set (dead or alive), then we're OK because the port
+ * is still a member of the set and the set won't go away
+ * until the port is taken out, which tries to lock the
+ * set's msg queue to remove the port's msgs.
+ */
+
+ ip_unlock(port);
+
+ /* check for a receiver for the message */
+
+ for (;;) {
+ receiver = ipc_thread_queue_first(receivers);
+ if (receiver == ITH_NULL) {
+ /* no receivers; queue kmsg */
+
+ ipc_kmsg_enqueue_macro(&mqueue->imq_messages, kmsg);
+ imq_unlock(mqueue);
+ break;
+ }
+
+ ipc_thread_rmqueue_first_macro(receivers, receiver);
+ assert(ipc_kmsg_queue_empty(&mqueue->imq_messages));
+
+ if (kmsg->ikm_header.msgh_size <= receiver->ith_msize) {
+ /* got a successful receiver */
+
+ receiver->ith_state = MACH_MSG_SUCCESS;
+ receiver->ith_kmsg = kmsg;
+ receiver->ith_seqno = port->ip_seqno++;
+ imq_unlock(mqueue);
+
+ thread_go(receiver);
+ break;
+ }
+
+ receiver->ith_state = MACH_RCV_TOO_LARGE;
+ receiver->ith_msize = kmsg->ikm_header.msgh_size;
+ thread_go(receiver);
+ }
+ }
+
+ current_task()->messages_sent++;
+
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_mqueue_copyin
+ * Purpose:
+ * Convert a name in a space to a message queue.
+ * Conditions:
+ * Nothing locked. If successful, the message queue
+ * is returned locked and caller gets a ref for the object.
+ * This ref ensures the continued existence of the queue.
+ * Returns:
+ * MACH_MSG_SUCCESS Found a message queue.
+ * MACH_RCV_INVALID_NAME The space is dead.
+ * MACH_RCV_INVALID_NAME The name doesn't denote a right.
+ * MACH_RCV_INVALID_NAME
+ * The denoted right is not receive or port set.
+ * MACH_RCV_IN_SET Receive right is a member of a set.
+ */
+
+mach_msg_return_t
+ipc_mqueue_copyin(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_mqueue_t *mqueuep,
+ ipc_object_t *objectp)
+{
+ ipc_entry_t entry;
+ ipc_entry_bits_t bits;
+ ipc_object_t object;
+ ipc_mqueue_t mqueue;
+
+ is_read_lock(space);
+ if (!space->is_active) {
+ is_read_unlock(space);
+ return MACH_RCV_INVALID_NAME;
+ }
+
+ entry = ipc_entry_lookup(space, name);
+ if (entry == IE_NULL) {
+ is_read_unlock(space);
+ return MACH_RCV_INVALID_NAME;
+ }
+
+ bits = entry->ie_bits;
+ object = entry->ie_object;
+
+ if (bits & MACH_PORT_TYPE_RECEIVE) {
+ ipc_port_t port;
+ ipc_pset_t pset;
+
+ port = (ipc_port_t) object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+ is_read_unlock(space);
+
+ pset = port->ip_pset;
+ if (pset != IPS_NULL) {
+ ips_lock(pset);
+ if (ips_active(pset)) {
+ ips_unlock(pset);
+ ip_unlock(port);
+ return MACH_RCV_IN_SET;
+ }
+
+ ipc_pset_remove(pset, port);
+ ips_check_unlock(pset);
+ assert(port->ip_pset == IPS_NULL);
+ }
+
+ mqueue = &port->ip_messages;
+ } else if (bits & MACH_PORT_TYPE_PORT_SET) {
+ ipc_pset_t pset;
+
+ pset = (ipc_pset_t) object;
+ assert(pset != IPS_NULL);
+
+ ips_lock(pset);
+ assert(ips_active(pset));
+ assert(pset->ips_local_name == name);
+ is_read_unlock(space);
+
+ mqueue = &pset->ips_messages;
+ } else {
+ is_read_unlock(space);
+ return MACH_RCV_INVALID_NAME;
+ }
+
+ /*
+ * At this point, the object is locked and active,
+ * the space is unlocked, and mqueue is initialized.
+ */
+
+ io_reference(object);
+ imq_lock(mqueue);
+ io_unlock(object);
+
+ *objectp = object;
+ *mqueuep = mqueue;
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: ipc_mqueue_receive
+ * Purpose:
+ * Receive a message from a message queue.
+ *
+ * If continuation is non-zero, then we might discard
+ * our kernel stack when we block. We will continue
+ * after unblocking by executing continuation.
+ *
+ * If resume is true, then we are resuming a receive
+ * operation after a blocked receive discarded our stack.
+ * Conditions:
+ * The message queue is locked; it will be returned unlocked.
+ *
+ * Our caller must hold a reference for the port or port set
+ * to which this queue belongs, to keep the queue
+ * from being deallocated. Furthermore, the port or set
+ * must have been active when the queue was locked.
+ *
+ * The kmsg is returned with clean header fields
+ * and with the circular bit turned off.
+ * Returns:
+ * MACH_MSG_SUCCESS Message returned in kmsgp.
+ * MACH_RCV_TOO_LARGE Message size returned in kmsgp.
+ * MACH_RCV_TIMED_OUT No message obtained.
+ * MACH_RCV_INTERRUPTED No message obtained.
+ * MACH_RCV_PORT_DIED Port/set died; no message.
+ * MACH_RCV_PORT_CHANGED Port moved into set; no msg.
+ *
+ */
+
+mach_msg_return_t
+ipc_mqueue_receive(
+ ipc_mqueue_t mqueue,
+ mach_msg_option_t option,
+ mach_msg_size_t max_size,
+ mach_msg_timeout_t time_out,
+ boolean_t resume,
+ continuation_t continuation,
+ ipc_kmsg_t *kmsgp,
+ mach_port_seqno_t *seqnop)
+{
+ ipc_port_t port;
+ ipc_kmsg_t kmsg;
+ mach_port_seqno_t seqno;
+
+ {
+ ipc_kmsg_queue_t kmsgs = &mqueue->imq_messages;
+ ipc_thread_t self = current_thread();
+
+ if (resume)
+ goto after_thread_block;
+
+ for (;;) {
+ kmsg = ipc_kmsg_queue_first(kmsgs);
+ if (kmsg != IKM_NULL) {
+ /* check space requirements */
+
+ if (msg_usize(&kmsg->ikm_header) > max_size) {
+ * (mach_msg_size_t *) kmsgp =
+ kmsg->ikm_header.msgh_size;
+ imq_unlock(mqueue);
+ return MACH_RCV_TOO_LARGE;
+ }
+
+ ipc_kmsg_rmqueue_first_macro(kmsgs, kmsg);
+ port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
+ seqno = port->ip_seqno++;
+ break;
+ }
+
+ /* must block waiting for a message */
+
+ if (option & MACH_RCV_TIMEOUT) {
+ if (time_out == 0) {
+ imq_unlock(mqueue);
+ return MACH_RCV_TIMED_OUT;
+ }
+
+ thread_will_wait_with_timeout(self, time_out);
+ } else
+ thread_will_wait(self);
+
+ ipc_thread_enqueue_macro(&mqueue->imq_threads, self);
+ self->ith_state = MACH_RCV_IN_PROGRESS;
+ self->ith_msize = max_size;
+
+ imq_unlock(mqueue);
+ if (continuation != (void (*)(void)) 0) {
+ counter(c_ipc_mqueue_receive_block_user++);
+ } else {
+ counter(c_ipc_mqueue_receive_block_kernel++);
+ }
+ thread_block(continuation);
+ after_thread_block:
+ imq_lock(mqueue);
+
+ /* why did we wake up? */
+
+ if (self->ith_state == MACH_MSG_SUCCESS) {
+ /* pick up the message that was handed to us */
+
+ kmsg = self->ith_kmsg;
+ seqno = self->ith_seqno;
+ port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
+ break;
+ }
+
+ switch (self->ith_state) {
+ case MACH_RCV_TOO_LARGE:
+ /* pick up size of the too-large message */
+
+ * (mach_msg_size_t *) kmsgp = self->ith_msize;
+ /* fall-through */
+
+ case MACH_RCV_PORT_DIED:
+ case MACH_RCV_PORT_CHANGED:
+ /* something bad happened to the port/set */
+
+ imq_unlock(mqueue);
+ return self->ith_state;
+
+ case MACH_RCV_IN_PROGRESS:
+ /*
+ * Awakened for other than IPC completion.
+ * Remove ourselves from the waiting queue,
+ * then check the wakeup cause.
+ */
+
+ ipc_thread_rmqueue(&mqueue->imq_threads, self);
+
+ switch (self->ith_wait_result) {
+ case THREAD_INTERRUPTED:
+ /* receive was interrupted - give up */
+
+ imq_unlock(mqueue);
+ return MACH_RCV_INTERRUPTED;
+
+ case THREAD_TIMED_OUT:
+ /* timeout expired */
+
+ assert(option & MACH_RCV_TIMEOUT);
+ time_out = 0;
+ break;
+
+ case THREAD_RESTART:
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_mqueue_receive");
+#else
+ panic("ipc_mqueue_receive");
+#endif
+ }
+ break;
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_mqueue_receive: strange ith_state");
+#else
+ panic("ipc_mqueue_receive: strange ith_state");
+#endif
+ }
+ }
+
+ /* we have a kmsg; unlock the msg queue */
+
+ imq_unlock(mqueue);
+ assert(msg_usize(&kmsg->ikm_header) <= max_size);
+ }
+
+ {
+ ipc_marequest_t marequest;
+
+ marequest = kmsg->ikm_marequest;
+ if (marequest != IMAR_NULL) {
+ ipc_marequest_destroy(marequest);
+ kmsg->ikm_marequest = IMAR_NULL;
+ }
+ assert((kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR) == 0);
+
+ assert(port == (ipc_port_t) kmsg->ikm_header.msgh_remote_port);
+ ip_lock(port);
+
+ if (ip_active(port)) {
+ ipc_thread_queue_t senders;
+ ipc_thread_t sender;
+
+ assert(port->ip_msgcount > 0);
+ port->ip_msgcount--;
+
+ senders = &port->ip_blocked;
+ sender = ipc_thread_queue_first(senders);
+
+ if ((sender != ITH_NULL) &&
+ (port->ip_msgcount < port->ip_qlimit)) {
+ ipc_thread_rmqueue(senders, sender);
+ sender->ith_state = MACH_MSG_SUCCESS;
+ thread_go(sender);
+ }
+ }
+
+ ip_unlock(port);
+ }
+
+ current_task()->messages_received++;
+
+ *kmsgp = kmsg;
+ *seqnop = seqno;
+ return MACH_MSG_SUCCESS;
+}
diff --git a/ipc/ipc_mqueue.h b/ipc/ipc_mqueue.h
new file mode 100644
index 0000000..dfac745
--- /dev/null
+++ b/ipc/ipc_mqueue.h
@@ -0,0 +1,112 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_mqueue.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for message queues.
+ */
+
+#ifndef _IPC_IPC_MQUEUE_H_
+#define _IPC_IPC_MQUEUE_H_
+
+#include <mach/message.h>
+#include <kern/assert.h>
+#include <kern/lock.h>
+#include <kern/macros.h>
+#include <ipc/ipc_kmsg_queue.h>
+#include <ipc/ipc_kmsg.h>
+#include <ipc/ipc_thread.h>
+
+typedef struct ipc_mqueue {
+ decl_simple_lock_data(, imq_lock_data)
+ struct ipc_kmsg_queue imq_messages;
+ struct ipc_thread_queue imq_threads;
+} *ipc_mqueue_t;
+
+#define IMQ_NULL ((ipc_mqueue_t) 0)
+
+#define imq_lock_init(mq) simple_lock_init(&(mq)->imq_lock_data)
+#define imq_lock(mq) simple_lock(&(mq)->imq_lock_data)
+#define imq_lock_try(mq) simple_lock_try(&(mq)->imq_lock_data)
+#define imq_unlock(mq) simple_unlock(&(mq)->imq_lock_data)
+
+extern void
+ipc_mqueue_init(ipc_mqueue_t);
+
+extern void
+ipc_mqueue_move(ipc_mqueue_t, ipc_mqueue_t, ipc_port_t);
+
+extern void
+ipc_mqueue_changed(ipc_mqueue_t, mach_msg_return_t);
+
+extern mach_msg_return_t
+ipc_mqueue_send(ipc_kmsg_t, mach_msg_option_t, mach_msg_timeout_t);
+
+extern mach_msg_return_t
+ipc_mqueue_copyin(ipc_space_t, mach_port_name_t, ipc_mqueue_t *, ipc_object_t *);
+
+#define IMQ_NULL_CONTINUE ((void (*)()) 0)
+
+extern mach_msg_return_t
+ipc_mqueue_receive(ipc_mqueue_t, mach_msg_option_t,
+ mach_msg_size_t, mach_msg_timeout_t,
+ boolean_t, continuation_t,
+ ipc_kmsg_t *, mach_port_seqno_t *);
+
+/*
+ * extern void
+ * ipc_mqueue_send_always(ipc_kmsg_t);
+ *
+ * Unfortunately, to avoid warnings/lint about unused variables
+ * when assertions are turned off, we need two versions of this.
+ */
+
+#include <kern/assert.h>
+
+#if MACH_ASSERT
+
+#define ipc_mqueue_send_always(kmsg) \
+MACRO_BEGIN \
+ mach_msg_return_t mr; \
+ \
+ mr = ipc_mqueue_send((kmsg), MACH_SEND_ALWAYS, \
+ MACH_MSG_TIMEOUT_NONE); \
+ assert(mr == MACH_MSG_SUCCESS); \
+MACRO_END
+
+#else /* MACH_ASSERT */
+
+#define ipc_mqueue_send_always(kmsg) \
+MACRO_BEGIN \
+ (void) ipc_mqueue_send((kmsg), MACH_SEND_ALWAYS, \
+ MACH_MSG_TIMEOUT_NONE); \
+MACRO_END
+
+#endif /* MACH_ASSERT */
+
+#endif /* _IPC_IPC_MQUEUE_H_ */
diff --git a/ipc/ipc_notify.c b/ipc/ipc_notify.c
new file mode 100644
index 0000000..d0b71cf
--- /dev/null
+++ b/ipc/ipc_notify.c
@@ -0,0 +1,449 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_notify.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Notification-sending functions.
+ */
+
+#include <kern/printf.h>
+#include <mach/port.h>
+#include <mach/message.h>
+#include <mach/notify.h>
+#include <kern/assert.h>
+#include <ipc/ipc_kmsg.h>
+#include <ipc/ipc_mqueue.h>
+#include <ipc/ipc_notify.h>
+#include <ipc/ipc_port.h>
+
+#include <ipc/ipc_machdep.h>
+
+mach_port_deleted_notification_t ipc_notify_port_deleted_template;
+mach_msg_accepted_notification_t ipc_notify_msg_accepted_template;
+mach_port_destroyed_notification_t ipc_notify_port_destroyed_template;
+mach_no_senders_notification_t ipc_notify_no_senders_template;
+mach_send_once_notification_t ipc_notify_send_once_template;
+mach_dead_name_notification_t ipc_notify_dead_name_template;
+
+#define NOTIFY_MSGH_SEQNO 0
+
+/*
+ * Routine: ipc_notify_init_port_deleted
+ * Purpose:
+ * Initialize a template for port-deleted notifications.
+ */
+
+static void
+ipc_notify_init_port_deleted(mach_port_deleted_notification_t *n)
+{
+ mach_msg_header_t *m = &n->not_header;
+ mach_msg_type_t *t = &n->not_type;
+
+ m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_NOTIFY_PORT_DELETED;
+
+ t->msgt_name = MACH_MSG_TYPE_PORT_NAME;
+ t->msgt_size = PORT_NAME_T_SIZE_IN_BITS;
+ t->msgt_number = 1;
+ t->msgt_inline = TRUE;
+ t->msgt_longform = FALSE;
+ t->msgt_deallocate = FALSE;
+ t->msgt_unused = 0;
+
+ n->not_port = MACH_PORT_NULL;
+}
+
+/*
+ * Routine: ipc_notify_init_msg_accepted
+ * Purpose:
+ * Initialize a template for msg-accepted notifications.
+ */
+
+static void
+ipc_notify_init_msg_accepted(mach_msg_accepted_notification_t *n)
+{
+ mach_msg_header_t *m = &n->not_header;
+ mach_msg_type_t *t = &n->not_type;
+
+ m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_NOTIFY_MSG_ACCEPTED;
+
+ t->msgt_name = MACH_MSG_TYPE_PORT_NAME;
+ t->msgt_size = PORT_NAME_T_SIZE_IN_BITS;
+ t->msgt_number = 1;
+ t->msgt_inline = TRUE;
+ t->msgt_longform = FALSE;
+ t->msgt_deallocate = FALSE;
+ t->msgt_unused = 0;
+
+ n->not_port = MACH_PORT_NULL;
+}
+
+/*
+ * Routine: ipc_notify_init_port_destroyed
+ * Purpose:
+ * Initialize a template for port-destroyed notifications.
+ */
+
+static void
+ipc_notify_init_port_destroyed(mach_port_destroyed_notification_t *n)
+{
+ mach_msg_header_t *m = &n->not_header;
+ mach_msg_type_t *t = &n->not_type;
+
+ m->msgh_bits = MACH_MSGH_BITS_COMPLEX |
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_NOTIFY_PORT_DESTROYED;
+
+ t->msgt_name = MACH_MSG_TYPE_PORT_RECEIVE;
+ t->msgt_size = PORT_T_SIZE_IN_BITS;
+ t->msgt_number = 1;
+ t->msgt_inline = TRUE;
+ t->msgt_longform = FALSE;
+ t->msgt_deallocate = FALSE;
+ t->msgt_unused = 0;
+
+ n->not_port = MACH_PORT_NULL;
+}
+
+/*
+ * Routine: ipc_notify_init_no_senders
+ * Purpose:
+ * Initialize a template for no-senders notifications.
+ */
+
+static void
+ipc_notify_init_no_senders(
+ mach_no_senders_notification_t *n)
+{
+ mach_msg_header_t *m = &n->not_header;
+ mach_msg_type_t *t = &n->not_type;
+
+ m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_NOTIFY_NO_SENDERS;
+
+ t->msgt_name = MACH_MSG_TYPE_INTEGER_32;
+ t->msgt_size = 32;
+ t->msgt_number = 1;
+ t->msgt_inline = TRUE;
+ t->msgt_longform = FALSE;
+ t->msgt_deallocate = FALSE;
+ t->msgt_unused = 0;
+
+ n->not_count = 0;
+}
+
+/*
+ * Routine: ipc_notify_init_send_once
+ * Purpose:
+ * Initialize a template for send-once notifications.
+ */
+
+static void
+ipc_notify_init_send_once(
+ mach_send_once_notification_t *n)
+{
+ mach_msg_header_t *m = &n->not_header;
+
+ m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_NOTIFY_SEND_ONCE;
+}
+
+/*
+ * Routine: ipc_notify_init_dead_name
+ * Purpose:
+ * Initialize a template for dead-name notifications.
+ */
+
+static void
+ipc_notify_init_dead_name(
+ mach_dead_name_notification_t *n)
+{
+ mach_msg_header_t *m = &n->not_header;
+ mach_msg_type_t *t = &n->not_type;
+
+ m->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0);
+ m->msgh_size = sizeof *n;
+ m->msgh_seqno = NOTIFY_MSGH_SEQNO;
+ m->msgh_local_port = MACH_PORT_NULL;
+ m->msgh_remote_port = MACH_PORT_NULL;
+ m->msgh_id = MACH_NOTIFY_DEAD_NAME;
+
+ t->msgt_name = MACH_MSG_TYPE_PORT_NAME;
+ t->msgt_size = PORT_NAME_T_SIZE_IN_BITS;
+ t->msgt_number = 1;
+ t->msgt_inline = TRUE;
+ t->msgt_longform = FALSE;
+ t->msgt_deallocate = FALSE;
+ t->msgt_unused = 0;
+
+ n->not_port = MACH_PORT_NULL;
+}
+
+/*
+ * Routine: ipc_notify_init
+ * Purpose:
+ * Initialize the notification subsystem.
+ */
+
+void
+ipc_notify_init(void)
+{
+ ipc_notify_init_port_deleted(&ipc_notify_port_deleted_template);
+ ipc_notify_init_msg_accepted(&ipc_notify_msg_accepted_template);
+ ipc_notify_init_port_destroyed(&ipc_notify_port_destroyed_template);
+ ipc_notify_init_no_senders(&ipc_notify_no_senders_template);
+ ipc_notify_init_send_once(&ipc_notify_send_once_template);
+ ipc_notify_init_dead_name(&ipc_notify_dead_name_template);
+}
+
+/*
+ * Routine: ipc_notify_port_deleted
+ * Purpose:
+ * Send a port-deleted notification.
+ * Conditions:
+ * Nothing locked.
+ * Consumes a ref/soright for port.
+ */
+
+void
+ipc_notify_port_deleted(
+ ipc_port_t port,
+ mach_port_name_t name)
+{
+ ipc_kmsg_t kmsg;
+ mach_port_deleted_notification_t *n;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL) {
+ printf("dropped port-deleted (0x%p, 0x%x)\n", port, name);
+ ipc_port_release_sonce(port);
+ return;
+ }
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_port_deleted_notification_t *) &kmsg->ikm_header;
+ *n = ipc_notify_port_deleted_template;
+
+ n->not_header.msgh_remote_port = (mach_port_t) port;
+ n->not_port = name;
+
+ ipc_mqueue_send_always(kmsg);
+}
+
+/*
+ * Routine: ipc_notify_msg_accepted
+ * Purpose:
+ * Send a msg-accepted notification.
+ * Conditions:
+ * Nothing locked.
+ * Consumes a ref/soright for port.
+ */
+
+void
+ipc_notify_msg_accepted(
+ ipc_port_t port,
+ mach_port_name_t name)
+{
+ ipc_kmsg_t kmsg;
+ mach_msg_accepted_notification_t *n;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL) {
+ printf("dropped msg-accepted (0x%p, 0x%x)\n", port, name);
+ ipc_port_release_sonce(port);
+ return;
+ }
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_msg_accepted_notification_t *) &kmsg->ikm_header;
+ *n = ipc_notify_msg_accepted_template;
+
+ n->not_header.msgh_remote_port = (mach_port_t) port;
+ n->not_port = name;
+
+ ipc_mqueue_send_always(kmsg);
+}
+
+/*
+ * Routine: ipc_notify_port_destroyed
+ * Purpose:
+ * Send a port-destroyed notification.
+ * Conditions:
+ * Nothing locked.
+ * Consumes a ref/soright for port.
+ * Consumes a ref for right, which should be a receive right
+ * prepped for placement into a message. (In-transit,
+ * or in-limbo if a circularity was detected.)
+ */
+
+void
+ipc_notify_port_destroyed(
+ ipc_port_t port,
+ ipc_port_t right)
+{
+ ipc_kmsg_t kmsg;
+ mach_port_destroyed_notification_t *n;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL) {
+ printf("dropped port-destroyed (0x%p, 0x%p)\n",
+ port, right);
+ ipc_port_release_sonce(port);
+ ipc_port_release_receive(right);
+ return;
+ }
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_port_destroyed_notification_t *) &kmsg->ikm_header;
+ *n = ipc_notify_port_destroyed_template;
+
+ n->not_header.msgh_remote_port = (mach_port_t) port;
+ n->not_port = (mach_port_t) right;
+
+ ipc_mqueue_send_always(kmsg);
+}
+
+/*
+ * Routine: ipc_notify_no_senders
+ * Purpose:
+ * Send a no-senders notification.
+ * Conditions:
+ * Nothing locked.
+ * Consumes a ref/soright for port.
+ */
+
+void
+ipc_notify_no_senders(
+ ipc_port_t port,
+ mach_port_mscount_t mscount)
+{
+ ipc_kmsg_t kmsg;
+ mach_no_senders_notification_t *n;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL) {
+ printf("dropped no-senders (0x%p, %u)\n", port, mscount);
+ ipc_port_release_sonce(port);
+ return;
+ }
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_no_senders_notification_t *) &kmsg->ikm_header;
+ *n = ipc_notify_no_senders_template;
+
+ n->not_header.msgh_remote_port = (mach_port_t) port;
+ n->not_count = mscount;
+
+ ipc_mqueue_send_always(kmsg);
+}
+
+/*
+ * Routine: ipc_notify_send_once
+ * Purpose:
+ * Send a send-once notification.
+ * Conditions:
+ * Nothing locked.
+ * Consumes a ref/soright for port.
+ */
+
+void
+ipc_notify_send_once(ipc_port_t port)
+{
+ ipc_kmsg_t kmsg;
+ mach_send_once_notification_t *n;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL) {
+ printf("dropped send-once (0x%p)\n", port);
+ ipc_port_release_sonce(port);
+ return;
+ }
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_send_once_notification_t *) &kmsg->ikm_header;
+ *n = ipc_notify_send_once_template;
+
+ n->not_header.msgh_remote_port = (mach_port_t) port;
+
+ ipc_mqueue_send_always(kmsg);
+}
+
+/*
+ * Routine: ipc_notify_dead_name
+ * Purpose:
+ * Send a dead-name notification.
+ * Conditions:
+ * Nothing locked.
+ * Consumes a ref/soright for port.
+ */
+
+void
+ipc_notify_dead_name(
+ ipc_port_t port,
+ mach_port_name_t name)
+{
+ ipc_kmsg_t kmsg;
+ mach_dead_name_notification_t *n;
+
+ kmsg = ikm_alloc(sizeof *n);
+ if (kmsg == IKM_NULL) {
+ printf("dropped dead-name (0x%p, 0x%x)\n", port, name);
+ ipc_port_release_sonce(port);
+ return;
+ }
+
+ ikm_init(kmsg, sizeof *n);
+ n = (mach_dead_name_notification_t *) &kmsg->ikm_header;
+ *n = ipc_notify_dead_name_template;
+
+ n->not_header.msgh_remote_port = (mach_port_t) port;
+ n->not_port = name;
+
+ ipc_mqueue_send_always(kmsg);
+}
diff --git a/ipc/ipc_notify.h b/ipc/ipc_notify.h
new file mode 100644
index 0000000..8940f38
--- /dev/null
+++ b/ipc/ipc_notify.h
@@ -0,0 +1,58 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_notify.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Declarations of notification-sending functions.
+ */
+
+#ifndef _IPC_IPC_NOTIFY_H_
+#define _IPC_IPC_NOTIFY_H_
+
+extern void
+ipc_notify_init(void);
+
+extern void
+ipc_notify_port_deleted(ipc_port_t, mach_port_name_t);
+
+extern void
+ipc_notify_msg_accepted(ipc_port_t, mach_port_name_t);
+
+extern void
+ipc_notify_port_destroyed(ipc_port_t, ipc_port_t);
+
+extern void
+ipc_notify_no_senders(ipc_port_t, mach_port_mscount_t);
+
+extern void
+ipc_notify_send_once(ipc_port_t);
+
+extern void
+ipc_notify_dead_name(ipc_port_t, mach_port_name_t);
+
+#endif /* _IPC_IPC_NOTIFY_H_ */
diff --git a/ipc/ipc_object.c b/ipc/ipc_object.c
new file mode 100644
index 0000000..1074fb2
--- /dev/null
+++ b/ipc/ipc_object.c
@@ -0,0 +1,969 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_object.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC objects.
+ */
+
+#include <string.h>
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/message.h>
+#include <ipc/port.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_right.h>
+#include <ipc/ipc_notify.h>
+#include <ipc/ipc_pset.h>
+#include <kern/debug.h>
+#include <kern/printf.h>
+#include <kern/slab.h>
+
+#if MACH_KDB
+#include <ddb/db_output.h>
+#endif /* MACH_KDB */
+
+
+struct kmem_cache ipc_object_caches[IOT_NUMBER];
+
+
+
+/*
+ * Routine: ipc_object_reference
+ * Purpose:
+ * Take a reference to an object.
+ */
+
+void
+ipc_object_reference(
+ ipc_object_t object)
+{
+ io_lock(object);
+ assert(object->io_references > 0);
+ io_reference(object);
+ io_unlock(object);
+}
+
+/*
+ * Routine: ipc_object_release
+ * Purpose:
+ * Release a reference to an object.
+ */
+
+void
+ipc_object_release(
+ ipc_object_t object)
+{
+ io_lock(object);
+ assert(object->io_references > 0);
+ io_release(object);
+ io_check_unlock(object);
+}
+
+/*
+ * Routine: ipc_object_translate
+ * Purpose:
+ * Look up an object in a space.
+ * Conditions:
+ * Nothing locked before. If successful, the object
+ * is returned locked. The caller doesn't get a ref.
+ * Returns:
+ * KERN_SUCCESS Objected returned locked.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote the correct right.
+ */
+
+kern_return_t
+ipc_object_translate(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_right_t right,
+ ipc_object_t *objectp)
+{
+ ipc_entry_t entry;
+ ipc_object_t object;
+ kern_return_t kr;
+
+ kr = ipc_right_lookup_read(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is read-locked and active */
+
+ if ((entry->ie_bits & MACH_PORT_TYPE(right)) == (mach_port_right_t) 0) {
+ is_read_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ object = entry->ie_object;
+ assert(object != IO_NULL);
+
+ io_lock(object);
+ is_read_unlock(space);
+
+ *objectp = object;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc_dead
+ * Purpose:
+ * Allocate a dead-name entry.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The dead name is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc_dead(
+ ipc_space_t space,
+ mach_port_name_t *namep)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ is_write_lock(space);
+ kr = ipc_entry_alloc(space, namep, &entry);
+ if (kr != KERN_SUCCESS) {
+ is_write_unlock(space);
+ return kr;
+ }
+
+ /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
+
+ assert(entry->ie_object == IO_NULL);
+ entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1;
+
+ is_write_unlock(space);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc_dead_name
+ * Purpose:
+ * Allocate a dead-name entry, with a specific name.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The dead name is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc_dead_name(
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ is_write_lock(space);
+ kr = ipc_entry_alloc_name(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ is_write_unlock(space);
+ return kr;
+ }
+
+ if (ipc_right_inuse(space, name, entry))
+ return KERN_NAME_EXISTS;
+
+ /* null object, MACH_PORT_TYPE_DEAD_NAME, 1 uref */
+
+ assert(entry->ie_object == IO_NULL);
+ entry->ie_bits |= MACH_PORT_TYPE_DEAD_NAME | 1;
+
+ is_write_unlock(space);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc
+ * Purpose:
+ * Allocate an object.
+ * Conditions:
+ * Nothing locked. If successful, the object is returned locked.
+ * The caller doesn't get a reference for the object.
+ * Returns:
+ * KERN_SUCCESS The object is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc(
+ ipc_space_t space,
+ ipc_object_type_t otype,
+ mach_port_type_t type,
+ mach_port_urefs_t urefs,
+ mach_port_name_t *namep,
+ ipc_object_t *objectp)
+{
+ ipc_object_t object;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(otype < IOT_NUMBER);
+ assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type);
+ assert(type != MACH_PORT_TYPE_NONE);
+ assert(urefs <= MACH_PORT_UREFS_MAX);
+
+ object = io_alloc(otype);
+ if (object == IO_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ if (otype == IOT_PORT) {
+ ipc_port_t port = (ipc_port_t)object;
+
+ memset(port, 0, sizeof(*port));
+ } else if (otype == IOT_PORT_SET) {
+ ipc_pset_t pset = (ipc_pset_t)object;
+
+ memset(pset, 0, sizeof(*pset));
+ }
+ is_write_lock(space);
+ kr = ipc_entry_alloc(space, namep, &entry);
+ if (kr != KERN_SUCCESS) {
+ is_write_unlock(space);
+ io_free(otype, object);
+ return kr;
+ }
+
+ entry->ie_bits |= type | urefs;
+ entry->ie_object = object;
+
+ io_lock_init(object);
+ io_lock(object);
+ is_write_unlock(space);
+
+ object->io_references = 1; /* for entry, not caller */
+ object->io_bits = io_makebits(TRUE, otype, 0);
+
+ *objectp = object;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_alloc_name
+ * Purpose:
+ * Allocate an object, with a specific name.
+ * Conditions:
+ * Nothing locked. If successful, the object is returned locked.
+ * The caller doesn't get a reference for the object.
+ * Returns:
+ * KERN_SUCCESS The object is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_object_alloc_name(
+ ipc_space_t space,
+ ipc_object_type_t otype,
+ mach_port_type_t type,
+ mach_port_urefs_t urefs,
+ mach_port_name_t name,
+ ipc_object_t *objectp)
+{
+ ipc_object_t object;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(otype < IOT_NUMBER);
+ assert((type & MACH_PORT_TYPE_ALL_RIGHTS) == type);
+ assert(type != MACH_PORT_TYPE_NONE);
+ assert(urefs <= MACH_PORT_UREFS_MAX);
+
+ object = io_alloc(otype);
+ if (object == IO_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ if (otype == IOT_PORT) {
+ ipc_port_t port = (ipc_port_t)object;
+
+ memset(port, 0, sizeof(*port));
+ } else if (otype == IOT_PORT_SET) {
+ ipc_pset_t pset = (ipc_pset_t)object;
+
+ memset(pset, 0, sizeof(*pset));
+ }
+
+ is_write_lock(space);
+ kr = ipc_entry_alloc_name(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ is_write_unlock(space);
+ io_free(otype, object);
+ return kr;
+ }
+
+ if (ipc_right_inuse(space, name, entry)) {
+ io_free(otype, object);
+ return KERN_NAME_EXISTS;
+ }
+
+ entry->ie_bits |= type | urefs;
+ entry->ie_object = object;
+
+ io_lock_init(object);
+ io_lock(object);
+ is_write_unlock(space);
+
+ object->io_references = 1; /* for entry, not caller */
+ object->io_bits = io_makebits(TRUE, otype, 0);
+
+ *objectp = object;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_object_copyin_type
+ * Purpose:
+ * Convert a send type name to a received type name.
+ */
+
+mach_msg_type_name_t
+ipc_object_copyin_type(
+ mach_msg_type_name_t msgt_name)
+{
+ switch (msgt_name) {
+ case 0:
+ return 0;
+
+ case MACH_MSG_TYPE_MOVE_RECEIVE:
+ return MACH_MSG_TYPE_PORT_RECEIVE;
+
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+ return MACH_MSG_TYPE_PORT_SEND_ONCE;
+
+ case MACH_MSG_TYPE_MOVE_SEND:
+ case MACH_MSG_TYPE_MAKE_SEND:
+ case MACH_MSG_TYPE_COPY_SEND:
+ return MACH_MSG_TYPE_PORT_SEND;
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_object_copyin_type: strange rights");
+#else
+ panic("ipc_object_copyin_type: strange rights");
+#endif
+ return 0; /* in case assert/panic returns */
+ }
+}
+
+/*
+ * Routine: ipc_object_copyin
+ * Purpose:
+ * Copyin a capability from a space.
+ * If successful, the caller gets a ref
+ * for the resulting object, unless it is IO_DEAD.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Acquired an object, possibly IO_DEAD.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Name doesn't exist in space.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right.
+ */
+
+kern_return_t
+ipc_object_copyin(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_msg_type_name_t msgt_name,
+ ipc_object_t *objectp)
+{
+ ipc_entry_t entry;
+ ipc_port_t soright;
+ kern_return_t kr;
+
+ /*
+ * Could first try a read lock when doing
+ * MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND,
+ * and MACH_MSG_TYPE_MAKE_SEND_ONCE.
+ */
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ kr = ipc_right_copyin(space, name, entry,
+ msgt_name, TRUE,
+ objectp, &soright);
+ if (IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ if ((kr == KERN_SUCCESS) && (soright != IP_NULL))
+ ipc_notify_port_deleted(soright, name);
+
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyin_from_kernel
+ * Purpose:
+ * Copyin a naked capability from the kernel.
+ *
+ * MACH_MSG_TYPE_MOVE_RECEIVE
+ * The receiver must be ipc_space_kernel.
+ * Consumes the naked receive right.
+ * MACH_MSG_TYPE_COPY_SEND
+ * A naked send right must be supplied.
+ * The port gains a reference, and a send right
+ * if the port is still active.
+ * MACH_MSG_TYPE_MAKE_SEND
+ * The receiver must be ipc_space_kernel.
+ * The port gains a reference and a send right.
+ * MACH_MSG_TYPE_MOVE_SEND
+ * Consumes a naked send right.
+ * MACH_MSG_TYPE_MAKE_SEND_ONCE
+ * The receiver must be ipc_space_kernel.
+ * The port gains a reference and a send-once right.
+ * MACH_MSG_TYPE_MOVE_SEND_ONCE
+ * Consumes a naked send-once right.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_object_copyin_from_kernel(
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name)
+{
+ assert(IO_VALID(object));
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_MOVE_RECEIVE: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert(port->ip_receiver == ipc_space_kernel);
+
+ /* relevant part of ipc_port_clear_receiver */
+ ipc_port_set_mscount(port, 0);
+
+ port->ip_receiver_name = MACH_PORT_NULL;
+ port->ip_destination = IP_NULL;
+ ipc_port_flag_protected_payload_clear(port);
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_COPY_SEND: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ if (ip_active(port)) {
+ assert(port->ip_srights > 0);
+ port->ip_srights++;
+ }
+ ip_reference(port);
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_MAKE_SEND: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert(port->ip_receiver == ipc_space_kernel);
+
+ ip_reference(port);
+ port->ip_mscount++;
+ port->ip_srights++;
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND:
+ /* move naked send right into the message */
+ break;
+
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert(port->ip_receiver == ipc_space_kernel);
+
+ ip_reference(port);
+ port->ip_sorights++;
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE:
+ /* move naked send-once right into the message */
+ break;
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_object_copyin_from_kernel: strange rights");
+#else
+ panic("ipc_object_copyin_from_kernel: strange rights");
+#endif
+ }
+}
+
+/*
+ * Routine: ipc_object_destroy
+ * Purpose:
+ * Destroys a naked capability.
+ * Consumes a ref for the object.
+ *
+ * A receive right should be in limbo or in transit.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_object_destroy(
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name)
+{
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_PORT_SEND:
+ ipc_port_release_send((ipc_port_t) object);
+ break;
+
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ ipc_notify_send_once((ipc_port_t) object);
+ break;
+
+ case MACH_MSG_TYPE_PORT_RECEIVE:
+ ipc_port_release_receive((ipc_port_t) object);
+ break;
+
+ default:
+ panic("ipc_object_destroy: strange rights");
+ }
+}
+
+/*
+ * Routine: ipc_object_copyout
+ * Purpose:
+ * Copyout a capability, placing it into a space.
+ * If successful, consumes a ref for the object.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Copied out object, consumed ref.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_CAPABILITY The object is dead.
+ * KERN_NO_SPACE No room in space for another right.
+ * KERN_RESOURCE_SHORTAGE No memory available.
+ * KERN_UREFS_OVERFLOW Urefs limit exceeded
+ * and overflow wasn't specified.
+ */
+
+kern_return_t
+ipc_object_copyout(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ boolean_t overflow,
+ mach_port_name_t *namep)
+{
+ mach_port_name_t name;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+
+ is_write_lock(space);
+
+ for (;;) {
+ if (!space->is_active) {
+ is_write_unlock(space);
+ return KERN_INVALID_TASK;
+ }
+
+ if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
+ ipc_right_reverse(space, object, &name, &entry)) {
+ /* object is locked and active */
+
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
+ break;
+ }
+
+ kr = ipc_entry_alloc(space, &name, &entry);
+ if (kr != KERN_SUCCESS) {
+ is_write_unlock(space);
+ return kr;
+ }
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ io_lock(object);
+ if (!io_active(object)) {
+ io_unlock(object);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ entry->ie_object = object;
+ break;
+ }
+
+ /* space is write-locked and active, object is locked and active */
+
+ kr = ipc_right_copyout(space, name, entry,
+ msgt_name, overflow, object);
+ /* object is unlocked */
+ is_write_unlock(space);
+
+ if (kr == KERN_SUCCESS)
+ *namep = name;
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyout_name
+ * Purpose:
+ * Copyout a capability, placing it into a space.
+ * The specified name is used for the capability.
+ * If successful, consumes a ref for the object.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Copied out object, consumed ref.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_CAPABILITY The object is dead.
+ * KERN_RESOURCE_SHORTAGE No memory available.
+ * KERN_UREFS_OVERFLOW Urefs limit exceeded
+ * and overflow wasn't specified.
+ * KERN_RIGHT_EXISTS Space has rights under another name.
+ * KERN_NAME_EXISTS Name is already used.
+ */
+
+kern_return_t
+ipc_object_copyout_name(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ boolean_t overflow,
+ mach_port_name_t name)
+{
+ mach_port_name_t oname;
+ ipc_entry_t oentry;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+
+ is_write_lock(space);
+ kr = ipc_entry_alloc_name(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ is_write_unlock(space);
+ return kr;
+ }
+
+ if ((msgt_name != MACH_MSG_TYPE_PORT_SEND_ONCE) &&
+ ipc_right_reverse(space, object, &oname, &oentry)) {
+ /* object is locked and active */
+
+ if (name != oname) {
+ io_unlock(object);
+
+ if (IE_BITS_TYPE(entry->ie_bits)
+ == MACH_PORT_TYPE_NONE)
+ ipc_entry_dealloc(space, name, entry);
+
+ is_write_unlock(space);
+ return KERN_RIGHT_EXISTS;
+ }
+
+ assert(entry == oentry);
+ assert(entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE);
+ } else {
+ if (ipc_right_inuse(space, name, entry))
+ return KERN_NAME_EXISTS;
+
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+
+ io_lock(object);
+ if (!io_active(object)) {
+ io_unlock(object);
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ return KERN_INVALID_CAPABILITY;
+ }
+
+ entry->ie_object = object;
+ }
+
+ /* space is write-locked and active, object is locked and active */
+
+ kr = ipc_right_copyout(space, name, entry,
+ msgt_name, overflow, object);
+ /* object is unlocked */
+ is_write_unlock(space);
+ return kr;
+}
+
+/*
+ * Routine: ipc_object_copyout_dest
+ * Purpose:
+ * Translates/consumes the destination right of a message.
+ * This is unlike normal copyout because the right is consumed
+ * in a funny way instead of being given to the receiving space.
+ * The receiver gets his name for the port, if he has receive
+ * rights, otherwise MACH_PORT_NULL.
+ * Conditions:
+ * The object is locked and active. Nothing else locked.
+ * The object is unlocked and loses a reference.
+ */
+
+void
+ipc_object_copyout_dest(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_msg_type_name_t msgt_name,
+ mach_port_name_t *namep)
+{
+ mach_port_name_t name;
+
+ assert(IO_VALID(object));
+ assert(io_active(object));
+
+ io_release(object);
+
+ /*
+ * If the space is the receiver/owner of the object,
+ * then we quietly consume the right and return
+ * the space's name for the object. Otherwise
+ * we destroy the right and return MACH_PORT_NULL.
+ */
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_PORT_SEND: {
+ ipc_port_t port = (ipc_port_t) object;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */
+
+ assert(port->ip_srights > 0);
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+
+ if (port->ip_receiver == space)
+ name = port->ip_receiver_name;
+ else
+ name = MACH_PORT_NAME_NULL;
+
+ ip_unlock(port);
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+
+ break;
+ }
+
+ case MACH_MSG_TYPE_PORT_SEND_ONCE: {
+ ipc_port_t port = (ipc_port_t) object;
+
+ assert(port->ip_sorights > 0);
+
+ if (port->ip_receiver == space) {
+ /* quietly consume the send-once right */
+
+ port->ip_sorights--;
+ name = port->ip_receiver_name;
+ ip_unlock(port);
+ } else {
+ /*
+ * A very bizarre case. The message
+ * was received, but before this copyout
+ * happened the space lost receive rights.
+ * We can't quietly consume the soright
+ * out from underneath some other task,
+ * so generate a send-once notification.
+ */
+
+ ip_reference(port); /* restore ref */
+ ip_unlock(port);
+
+ ipc_notify_send_once(port);
+ name = MACH_PORT_NAME_NULL;
+ }
+
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_object_copyout_dest: strange rights");
+#else
+ panic("ipc_object_copyout_dest: strange rights");
+#endif
+
+ }
+
+ *namep = name;
+}
+
+/*
+ * Routine: ipc_object_rename
+ * Purpose:
+ * Rename an entry in a space.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Renamed the entry.
+ * KERN_INVALID_TASK The space was dead.
+ * KERN_INVALID_NAME oname didn't denote an entry.
+ * KERN_NAME_EXISTS nname already denoted an entry.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate new entry.
+ */
+
+kern_return_t
+ipc_object_rename(
+ ipc_space_t space,
+ mach_port_name_t oname,
+ mach_port_name_t nname)
+{
+ ipc_entry_t oentry, nentry;
+ kern_return_t kr;
+
+ is_write_lock(space);
+ kr = ipc_entry_alloc_name(space, nname, &nentry);
+ if (kr != KERN_SUCCESS) {
+ is_write_unlock(space);
+ return kr;
+ }
+
+ if (ipc_right_inuse(space, nname, nentry)) {
+ /* space is unlocked */
+ return KERN_NAME_EXISTS;
+ }
+
+ /* don't let ipc_entry_lookup see the uninitialized new entry */
+
+ if ((oname == nname) ||
+ ((oentry = ipc_entry_lookup(space, oname)) == IE_NULL)) {
+ ipc_entry_dealloc(space, nname, nentry);
+ is_write_unlock(space);
+ return KERN_INVALID_NAME;
+ }
+
+ kr = ipc_right_rename(space, oname, oentry, nname, nentry);
+ /* space is unlocked */
+ return kr;
+}
+
+#if MACH_KDB
+#define printf kdbprintf
+
+/*
+ * Routine: ipc_object_print
+ * Purpose:
+ * Pretty-print an object for kdb.
+ */
+
+char *ikot_print_array[IKOT_MAX_TYPE] = {
+ "(NONE) ",
+ "(THREAD) ",
+ "(TASK) ",
+ "(HOST) ",
+ "(HOST_PRIV) ",
+ "(PROCESSOR) ",
+ "(PSET) ",
+ "(PSET_NAME) ",
+ "(PAGER) ",
+ "(PAGER_REQUEST) ",
+ "(DEVICE) ", /* 10 */
+ "(XMM_OBJECT) ",
+ "(XMM_PAGER) ",
+ "(XMM_KERNEL) ",
+ "(XMM_REPLY) ",
+ "(PAGER_TERMINATING)",
+ "(PAGING_NAME) ",
+ "(HOST_SECURITY) ",
+ "(LEDGER) ",
+ "(MASTER_DEVICE) ",
+ "(ACTIVATION) ", /* 20 */
+ "(SUBSYSTEM) ",
+ "(IO_DONE_QUEUE) ",
+ "(SEMAPHORE) ",
+ "(LOCK_SET) ",
+ "(CLOCK) ",
+ "(CLOCK_CTRL) ",
+ "(PAGER_PROXY) ", /* 27 */
+ /* << new entries here */
+ "(UNKNOWN) " /* magic catchall */
+}; /* Please keep in sync with kern/ipc_kobject.h */
+
+void
+ipc_object_print(
+ const ipc_object_t object)
+{
+ int kotype;
+
+ iprintf("%s", io_active(object) ? "active" : "dead");
+ printf(", refs=%d", object->io_references);
+ printf(", otype=%d", io_otype(object));
+ kotype = io_kotype(object);
+ if (kotype >= 0 && kotype < IKOT_MAX_TYPE)
+ printf(", kotype=%d %s\n", io_kotype(object),
+ ikot_print_array[kotype]);
+ else
+ printf(", kotype=0x%x %s\n", io_kotype(object),
+ ikot_print_array[IKOT_UNKNOWN]);
+}
+
+#endif /* MACH_KDB */
diff --git a/ipc/ipc_object.h b/ipc/ipc_object.h
new file mode 100644
index 0000000..209fae1
--- /dev/null
+++ b/ipc/ipc_object.h
@@ -0,0 +1,169 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_object.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for IPC objects, for which tasks have capabilities.
+ */
+
+#ifndef _IPC_IPC_OBJECT_H_
+#define _IPC_IPC_OBJECT_H_
+
+#include <mach/kern_return.h>
+#include <mach/message.h>
+#include <ipc/ipc_types.h>
+#include <kern/lock.h>
+#include <kern/macros.h>
+#include <kern/slab.h>
+
+typedef unsigned int ipc_object_refs_t;
+typedef unsigned int ipc_object_bits_t;
+typedef unsigned int ipc_object_type_t;
+
+typedef struct ipc_object {
+ decl_simple_lock_data(,io_lock_data)
+ ipc_object_refs_t io_references;
+ ipc_object_bits_t io_bits;
+} *ipc_object_t;
+
+#define IO_NULL ((ipc_object_t) 0)
+#define IO_DEAD ((ipc_object_t) -1)
+
+#define IO_VALID(io) (((io) != IO_NULL) && ((io) != IO_DEAD))
+
+#define IO_BITS_KOTYPE 0x0000ffff /* used by the object */
+#define IO_BITS_OTYPE 0x3fff0000 /* determines a cache */
+/* The following masks are used to store attributes of ipc ports. */
+#define IO_BITS_PROTECTED_PAYLOAD 0x40000000 /* pp set? */
+#define IO_BITS_ACTIVE 0x80000000U /* is object alive? */
+
+#define io_active(io) ((int)(io)->io_bits < 0) /* hack */
+
+#define io_otype(io) (((io)->io_bits & IO_BITS_OTYPE) >> 16)
+#define io_kotype(io) ((io)->io_bits & IO_BITS_KOTYPE)
+
+#define io_makebits(active, otype, kotype) \
+ (((active) ? IO_BITS_ACTIVE : 0) | ((otype) << 16) | (kotype))
+
+/*
+ * Object types: ports, port sets, kernel-loaded ports
+ */
+#define IOT_PORT 0
+#define IOT_PORT_SET 1
+#define IOT_NUMBER 2 /* number of types used */
+
+extern struct kmem_cache ipc_object_caches[IOT_NUMBER];
+
+#define io_alloc(otype) \
+ ((ipc_object_t) kmem_cache_alloc(&ipc_object_caches[(otype)]))
+
+#define io_free(otype, io) \
+ kmem_cache_free(&ipc_object_caches[(otype)], (vm_offset_t) (io))
+
+#define io_lock_init(io) simple_lock_init(&(io)->io_lock_data)
+#define io_lock(io) simple_lock(&(io)->io_lock_data)
+#define io_lock_try(io) simple_lock_try(&(io)->io_lock_data)
+#define io_unlock(io) simple_unlock(&(io)->io_lock_data)
+
+#define io_check_unlock(io) \
+MACRO_BEGIN \
+ ipc_object_refs_t _refs = (io)->io_references; \
+ \
+ io_unlock(io); \
+ if (_refs == 0) \
+ io_free(io_otype(io), io); \
+MACRO_END
+
+#define io_reference(io) \
+MACRO_BEGIN \
+ (io)->io_references++; \
+MACRO_END
+
+#define io_release(io) \
+MACRO_BEGIN \
+ (io)->io_references--; \
+MACRO_END
+
+extern void
+ipc_object_reference(ipc_object_t);
+
+extern void
+ipc_object_release(ipc_object_t);
+
+extern kern_return_t
+ipc_object_translate(ipc_space_t, mach_port_name_t,
+ mach_port_right_t, ipc_object_t *);
+
+extern kern_return_t
+ipc_object_alloc_dead(ipc_space_t, mach_port_name_t *);
+
+extern kern_return_t
+ipc_object_alloc_dead_name(ipc_space_t, mach_port_name_t);
+
+extern kern_return_t
+ipc_object_alloc(ipc_space_t, ipc_object_type_t,
+ mach_port_type_t, mach_port_urefs_t,
+ mach_port_name_t *, ipc_object_t *);
+
+extern kern_return_t
+ipc_object_alloc_name(ipc_space_t, ipc_object_type_t,
+ mach_port_type_t, mach_port_urefs_t,
+ mach_port_name_t, ipc_object_t *);
+
+extern mach_msg_type_name_t
+ipc_object_copyin_type(mach_msg_type_name_t);
+
+extern kern_return_t
+ipc_object_copyin(ipc_space_t, mach_port_name_t,
+ mach_msg_type_name_t, ipc_object_t *);
+
+extern void
+ipc_object_copyin_from_kernel(ipc_object_t, mach_msg_type_name_t);
+
+extern void
+ipc_object_destroy(ipc_object_t, mach_msg_type_name_t);
+
+extern kern_return_t
+ipc_object_copyout(ipc_space_t, ipc_object_t,
+ mach_msg_type_name_t, boolean_t, mach_port_name_t *);
+
+extern kern_return_t
+ipc_object_copyout_name(ipc_space_t, ipc_object_t,
+ mach_msg_type_name_t, boolean_t, mach_port_name_t);
+
+extern void
+ipc_object_copyout_dest(ipc_space_t, ipc_object_t,
+ mach_msg_type_name_t, mach_port_name_t *);
+
+extern kern_return_t
+ipc_object_rename(ipc_space_t, mach_port_name_t, mach_port_name_t);
+
+extern void
+ipc_object_print(ipc_object_t);
+
+#endif /* _IPC_IPC_OBJECT_H_ */
diff --git a/ipc/ipc_port.c b/ipc/ipc_port.c
new file mode 100644
index 0000000..e959f67
--- /dev/null
+++ b/ipc/ipc_port.c
@@ -0,0 +1,1290 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: ipc/ipc_port.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC ports.
+ */
+
+#include <kern/printf.h>
+#include <string.h>
+
+#include <mach/port.h>
+#include <mach/kern_return.h>
+#include <kern/lock.h>
+#include <kern/ipc_sched.h>
+#include <kern/ipc_kobject.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_thread.h>
+#include <ipc/ipc_mqueue.h>
+#include <ipc/ipc_notify.h>
+
+#if MACH_KDB
+#include <ddb/db_output.h>
+#include <ipc/ipc_print.h>
+#endif /* MACH_KDB */
+
+
+def_simple_lock_data(, ipc_port_multiple_lock_data)
+
+def_simple_lock_data(, ipc_port_timestamp_lock_data)
+ipc_port_timestamp_t ipc_port_timestamp_data;
+
+/*
+ * Routine: ipc_port_timestamp
+ * Purpose:
+ * Retrieve a timestamp value.
+ */
+
+ipc_port_timestamp_t
+ipc_port_timestamp(void)
+{
+ ipc_port_timestamp_t timestamp;
+
+ ipc_port_timestamp_lock();
+ timestamp = ipc_port_timestamp_data++;
+ ipc_port_timestamp_unlock();
+
+ return timestamp;
+}
+
+/*
+ * Routine: ipc_port_dnrequest
+ * Purpose:
+ * Try to allocate a dead-name request slot.
+ * If successful, returns the request index.
+ * Otherwise returns zero.
+ * Conditions:
+ * The port is locked and active.
+ * Returns:
+ * KERN_SUCCESS A request index was found.
+ * KERN_NO_SPACE No index allocated.
+ */
+
+kern_return_t
+ipc_port_dnrequest(
+ ipc_port_t port,
+ mach_port_name_t name,
+ ipc_port_t soright,
+ ipc_port_request_index_t *indexp)
+{
+ ipc_port_request_t ipr, table;
+ ipc_port_request_index_t index;
+
+ assert(ip_active(port));
+ assert(name != MACH_PORT_NULL);
+ assert(soright != IP_NULL);
+
+ table = port->ip_dnrequests;
+ if (table == IPR_NULL)
+ return KERN_NO_SPACE;
+
+ index = table->ipr_next;
+ if (index == 0)
+ return KERN_NO_SPACE;
+
+ ipr = &table[index];
+ assert(ipr->ipr_name == MACH_PORT_NULL);
+
+ table->ipr_next = ipr->ipr_next;
+ ipr->ipr_name = name;
+ ipr->ipr_soright = soright;
+
+ *indexp = index;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_port_dngrow
+ * Purpose:
+ * Grow a port's table of dead-name requests.
+ * Conditions:
+ * The port must be locked and active.
+ * Nothing else locked; will allocate memory.
+ * Upon return the port is unlocked.
+ * Returns:
+ * KERN_SUCCESS Grew the table.
+ * KERN_SUCCESS Somebody else grew the table.
+ * KERN_SUCCESS The port died.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate new table.
+ */
+
+kern_return_t
+ipc_port_dngrow(ipc_port_t port)
+{
+ ipc_table_size_t its;
+ ipc_port_request_t otable, ntable;
+
+ assert(ip_active(port));
+
+ otable = port->ip_dnrequests;
+ if (otable == IPR_NULL)
+ its = &ipc_table_dnrequests[0];
+ else
+ its = otable->ipr_size + 1;
+
+ ip_reference(port);
+ ip_unlock(port);
+
+ if ((its->its_size == 0) ||
+ ((ntable = it_dnrequests_alloc(its)) == IPR_NULL)) {
+ ipc_port_release(port);
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ ip_lock(port);
+ ip_release(port);
+
+ /*
+ * Check that port is still active and that nobody else
+ * has slipped in and grown the table on us. Note that
+ * just checking port->ip_dnrequests == otable isn't
+ * sufficient; must check ipr_size.
+ */
+
+ if (ip_active(port) &&
+ (port->ip_dnrequests == otable) &&
+ ((otable == IPR_NULL) || (otable->ipr_size+1 == its))) {
+ ipc_table_size_t oits = 0; /* '=0' to shut up lint */
+ ipc_table_elems_t osize, nsize;
+ ipc_port_request_index_t free, i;
+
+ /* copy old table to new table */
+
+ if (otable != IPR_NULL) {
+ oits = otable->ipr_size;
+ osize = oits->its_size;
+ free = otable->ipr_next;
+
+ memcpy((ntable + 1), (otable + 1),
+ (osize - 1) * sizeof(struct ipc_port_request));
+ } else {
+ osize = 1;
+ free = 0;
+ }
+
+ nsize = its->its_size;
+ assert(nsize > osize);
+
+ /* add new elements to the new table's free list */
+
+ for (i = osize; i < nsize; i++) {
+ ipc_port_request_t ipr = &ntable[i];
+
+ ipr->ipr_name = MACH_PORT_NULL;
+ ipr->ipr_next = free;
+ free = i;
+ }
+
+ ntable->ipr_next = free;
+ ntable->ipr_size = its;
+ port->ip_dnrequests = ntable;
+ ip_unlock(port);
+
+ if (otable != IPR_NULL)
+ it_dnrequests_free(oits, otable);
+ } else {
+ ip_check_unlock(port);
+ it_dnrequests_free(its, ntable);
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_port_dncancel
+ * Purpose:
+ * Cancel a dead-name request and return the send-once right.
+ * Conditions:
+ * The port must locked and active.
+ */
+
+ipc_port_t
+ipc_port_dncancel(
+ ipc_port_t port,
+ mach_port_name_t name,
+ ipc_port_request_index_t index)
+{
+ ipc_port_request_t ipr, table;
+ ipc_port_t dnrequest;
+
+ assert(ip_active(port));
+ assert(name != MACH_PORT_NULL);
+ assert(index != 0);
+
+ table = port->ip_dnrequests;
+ assert(table != IPR_NULL);
+
+ ipr = &table[index];
+ dnrequest = ipr->ipr_soright;
+ assert(ipr->ipr_name == name);
+
+ /* return ipr to the free list inside the table */
+
+ ipr->ipr_name = MACH_PORT_NULL;
+ ipr->ipr_next = table->ipr_next;
+ table->ipr_next = index;
+
+ return dnrequest;
+}
+
+/*
+ * Routine: ipc_port_pdrequest
+ * Purpose:
+ * Make a port-deleted request, returning the
+ * previously registered send-once right.
+ * Just cancels the previous request if notify is IP_NULL.
+ * Conditions:
+ * The port is locked and active. It is unlocked.
+ * Consumes a ref for notify (if non-null), and
+ * returns previous with a ref (if non-null).
+ */
+
+void
+ipc_port_pdrequest(
+ ipc_port_t port,
+ const ipc_port_t notify,
+ ipc_port_t *previousp)
+{
+ ipc_port_t previous;
+
+ assert(ip_active(port));
+
+ previous = port->ip_pdrequest;
+ port->ip_pdrequest = notify;
+ ip_unlock(port);
+
+ *previousp = previous;
+}
+
+/*
+ * Routine: ipc_port_nsrequest
+ * Purpose:
+ * Make a no-senders request, returning the
+ * previously registered send-once right.
+ * Just cancels the previous request if notify is IP_NULL.
+ * Conditions:
+ * The port is locked and active. It is unlocked.
+ * Consumes a ref for notify (if non-null), and
+ * returns previous with a ref (if non-null).
+ */
+
+void
+ipc_port_nsrequest(
+ ipc_port_t port,
+ mach_port_mscount_t sync,
+ ipc_port_t notify,
+ ipc_port_t *previousp)
+{
+ ipc_port_t previous;
+ mach_port_mscount_t mscount;
+
+ assert(ip_active(port));
+
+ previous = port->ip_nsrequest;
+ mscount = port->ip_mscount;
+
+ if ((port->ip_srights == 0) &&
+ (sync <= mscount) &&
+ (notify != IP_NULL)) {
+ port->ip_nsrequest = IP_NULL;
+ ip_unlock(port);
+ ipc_notify_no_senders(notify, mscount);
+ } else {
+ port->ip_nsrequest = notify;
+ ip_unlock(port);
+ }
+
+ *previousp = previous;
+}
+
+/*
+ * Routine: ipc_port_set_qlimit
+ * Purpose:
+ * Changes a port's queue limit; the maximum number
+ * of messages which may be queued to the port.
+ * Conditions:
+ * The port is locked and active.
+ */
+
+void
+ipc_port_set_qlimit(
+ ipc_port_t port,
+ mach_port_msgcount_t qlimit)
+{
+ assert(ip_active(port));
+
+ /* wake up senders allowed by the new qlimit */
+
+ if (qlimit > port->ip_qlimit) {
+ mach_port_msgcount_t i, wakeup;
+
+ /* caution: wakeup, qlimit are unsigned */
+
+ wakeup = qlimit - port->ip_qlimit;
+
+ for (i = 0; i < wakeup; i++) {
+ ipc_thread_t th;
+
+ th = ipc_thread_dequeue(&port->ip_blocked);
+ if (th == ITH_NULL)
+ break;
+
+ th->ith_state = MACH_MSG_SUCCESS;
+ thread_go(th);
+ }
+ }
+
+ port->ip_qlimit = qlimit;
+}
+
+/*
+ * Routine: ipc_port_lock_mqueue
+ * Purpose:
+ * Locks and returns the message queue that the port is using.
+ * The message queue may be in the port or in its port set.
+ * Conditions:
+ * The port is locked and active.
+ * Port set, message queue locks may be taken.
+ */
+
+ipc_mqueue_t
+ipc_port_lock_mqueue(ipc_port_t port)
+{
+ if (port->ip_pset != IPS_NULL) {
+ ipc_pset_t pset = port->ip_pset;
+
+ ips_lock(pset);
+ if (ips_active(pset)) {
+ imq_lock(&pset->ips_messages);
+ ips_unlock(pset);
+ return &pset->ips_messages;
+ }
+
+ ipc_pset_remove(pset, port);
+ ips_check_unlock(pset);
+ }
+
+ imq_lock(&port->ip_messages);
+ return &port->ip_messages;
+}
+
+/*
+ * Routine: ipc_port_set_seqno
+ * Purpose:
+ * Changes a port's sequence number.
+ * Conditions:
+ * The port is locked and active.
+ * Port set, message queue locks may be taken.
+ */
+
+void
+ipc_port_set_seqno(
+ ipc_port_t port,
+ mach_port_seqno_t seqno)
+{
+ ipc_mqueue_t mqueue;
+
+ mqueue = ipc_port_lock_mqueue(port);
+ port->ip_seqno = seqno;
+ imq_unlock(mqueue);
+}
+
+/*
+ * Routine: ipc_port_set_protected_payload
+ * Purpose:
+ * Changes a port's protected payload.
+ * Conditions:
+ * The port is locked and active.
+ */
+
+void
+ipc_port_set_protected_payload(ipc_port_t port, rpc_uintptr_t payload)
+{
+ ipc_mqueue_t mqueue;
+
+ mqueue = ipc_port_lock_mqueue(port);
+ port->ip_protected_payload = payload;
+ ipc_port_flag_protected_payload_set(port);
+ imq_unlock(mqueue);
+}
+
+/*
+ * Routine: ipc_port_clear_protected_payload
+ * Purpose:
+ * Clear a port's protected payload.
+ * Conditions:
+ * The port is locked and active.
+ */
+
+void
+ipc_port_clear_protected_payload(ipc_port_t port)
+{
+ ipc_mqueue_t mqueue;
+
+ mqueue = ipc_port_lock_mqueue(port);
+ ipc_port_flag_protected_payload_clear(port);
+ imq_unlock(mqueue);
+}
+
+
+/*
+ * Routine: ipc_port_clear_receiver
+ * Purpose:
+ * Prepares a receive right for transmission/destruction.
+ * Conditions:
+ * The port is locked and active.
+ */
+
+void
+ipc_port_clear_receiver(
+ ipc_port_t port)
+{
+ ipc_pset_t pset;
+
+ assert(ip_active(port));
+
+ pset = port->ip_pset;
+ if (pset != IPS_NULL) {
+ /* No threads receiving from port, but must remove from set. */
+
+ ips_lock(pset);
+ ipc_pset_remove(pset, port);
+ ips_check_unlock(pset);
+ } else {
+ /* Else, wake up all receivers, indicating why. */
+
+ imq_lock(&port->ip_messages);
+ ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_DIED);
+ imq_unlock(&port->ip_messages);
+ }
+
+ ipc_port_set_mscount(port, 0);
+ imq_lock(&port->ip_messages);
+ port->ip_seqno = 0;
+ imq_unlock(&port->ip_messages);
+}
+
+/*
+ * Routine: ipc_port_init
+ * Purpose:
+ * Initializes a newly-allocated port.
+ * Doesn't touch the ip_object fields.
+ */
+
+void
+ipc_port_init(
+ ipc_port_t port,
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ /* port->ip_kobject doesn't have to be initialized */
+
+ ipc_target_init(&port->ip_target, name);
+
+ port->ip_receiver = space;
+
+ port->ip_mscount = 0;
+ port->ip_srights = 0;
+ port->ip_sorights = 0;
+
+ port->ip_nsrequest = IP_NULL;
+ port->ip_pdrequest = IP_NULL;
+ port->ip_dnrequests = IPR_NULL;
+
+ port->ip_pset = IPS_NULL;
+ port->ip_cur_target = &port->ip_target;
+ port->ip_seqno = 0;
+ port->ip_msgcount = 0;
+ port->ip_qlimit = MACH_PORT_QLIMIT_DEFAULT;
+ ipc_port_flag_protected_payload_clear(port);
+ port->ip_protected_payload = 0;
+
+ ipc_mqueue_init(&port->ip_messages);
+ ipc_thread_queue_init(&port->ip_blocked);
+}
+
+/*
+ * Routine: ipc_port_alloc
+ * Purpose:
+ * Allocate a port.
+ * Conditions:
+ * Nothing locked. If successful, the port is returned
+ * locked. (The caller doesn't have a reference.)
+ * Returns:
+ * KERN_SUCCESS The port is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_port_alloc(
+ ipc_space_t space,
+ mach_port_name_t *namep,
+ ipc_port_t *portp)
+{
+ ipc_port_t port;
+ mach_port_name_t name;
+ kern_return_t kr;
+
+ kr = ipc_object_alloc(space, IOT_PORT,
+ MACH_PORT_TYPE_RECEIVE, 0,
+ &name, (ipc_object_t *) &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ /* port is locked */
+
+ ipc_port_init(port, space, name);
+
+ *namep = name;
+ *portp = port;
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_port_alloc_name
+ * Purpose:
+ * Allocate a port, with a specific name.
+ * Conditions:
+ * Nothing locked. If successful, the port is returned
+ * locked. (The caller doesn't have a reference.)
+ * Returns:
+ * KERN_SUCCESS The port is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_port_alloc_name(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_port_t *portp)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+
+ kr = ipc_object_alloc_name(space, IOT_PORT,
+ MACH_PORT_TYPE_RECEIVE, 0,
+ name, (ipc_object_t *) &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked */
+
+ ipc_port_init(port, space, name);
+
+ *portp = port;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_port_destroy
+ * Purpose:
+ * Destroys a port. Cleans up queued messages.
+ *
+ * If the port has a backup, it doesn't get destroyed,
+ * but is sent in a port-destroyed notification to the backup.
+ * Conditions:
+ * The port is locked and alive; nothing else locked.
+ * The caller has a reference, which is consumed.
+ * Afterwards, the port is unlocked and dead.
+ */
+
+void
+ipc_port_destroy(
+ ipc_port_t port)
+{
+ ipc_port_t pdrequest, nsrequest;
+ ipc_mqueue_t mqueue;
+ ipc_kmsg_queue_t kmqueue;
+ ipc_kmsg_t kmsg;
+ ipc_thread_t sender;
+ ipc_port_request_t dnrequests;
+
+ assert(ip_active(port));
+ /* port->ip_receiver_name is garbage */
+ /* port->ip_receiver/port->ip_destination is garbage */
+ assert(port->ip_pset == IPS_NULL);
+ assert(port->ip_mscount == 0);
+ assert(port->ip_seqno == 0);
+
+ /* first check for a backup port */
+
+ pdrequest = port->ip_pdrequest;
+ if (pdrequest != IP_NULL) {
+ /* we assume the ref for pdrequest */
+ port->ip_pdrequest = IP_NULL;
+
+ /* make port be in limbo */
+ port->ip_receiver_name = MACH_PORT_NULL;
+ port->ip_destination = IP_NULL;
+ ipc_port_flag_protected_payload_clear(port);
+ ip_unlock(port);
+
+ if (!ipc_port_check_circularity(port, pdrequest)) {
+ /* consumes our refs for port and pdrequest */
+ ipc_notify_port_destroyed(pdrequest, port);
+ return;
+ } else {
+ /* consume pdrequest and destroy port */
+ ipc_port_release_sonce(pdrequest);
+ }
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_pset == IPS_NULL);
+ assert(port->ip_mscount == 0);
+ assert(port->ip_seqno == 0);
+ assert(port->ip_pdrequest == IP_NULL);
+ assert(port->ip_receiver_name == MACH_PORT_NULL);
+ assert(port->ip_destination == IP_NULL);
+
+ /* fall through and destroy the port */
+ }
+
+ /*
+ * rouse all blocked senders
+ *
+ * This must be done with the port locked, because
+ * ipc_mqueue_send can play with the ip_blocked queue
+ * of a dead port.
+ */
+
+ while ((sender = ipc_thread_dequeue(&port->ip_blocked)) != ITH_NULL) {
+ sender->ith_state = MACH_MSG_SUCCESS;
+ thread_go(sender);
+ }
+
+ /* once port is dead, we don't need to keep it locked */
+
+ port->ip_object.io_bits &= ~IO_BITS_ACTIVE;
+ port->ip_timestamp = ipc_port_timestamp();
+ ip_unlock(port);
+
+ /* throw away no-senders request */
+
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL)
+ ipc_notify_send_once(nsrequest); /* consumes ref */
+
+ /* destroy any queued messages */
+
+ mqueue = &port->ip_messages;
+ imq_lock(mqueue);
+ assert(ipc_thread_queue_empty(&mqueue->imq_threads));
+ kmqueue = &mqueue->imq_messages;
+
+ while ((kmsg = ipc_kmsg_dequeue(kmqueue)) != IKM_NULL) {
+ imq_unlock(mqueue);
+
+ assert(kmsg->ikm_header.msgh_remote_port ==
+ (mach_port_t) port);
+
+ ipc_port_release(port);
+ kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
+ ipc_kmsg_destroy(kmsg);
+
+ imq_lock(mqueue);
+ }
+
+ imq_unlock(mqueue);
+
+ /* generate dead-name notifications */
+
+ dnrequests = port->ip_dnrequests;
+ if (dnrequests != IPR_NULL) {
+ ipc_table_size_t its = dnrequests->ipr_size;
+ ipc_table_elems_t size = its->its_size;
+ ipc_port_request_index_t index;
+
+ for (index = 1; index < size; index++) {
+ ipc_port_request_t ipr = &dnrequests[index];
+ mach_port_name_t name = ipr->ipr_name;
+ ipc_port_t soright;
+
+ if (name == MACH_PORT_NULL)
+ continue;
+
+ soright = ipr->ipr_soright;
+ assert(soright != IP_NULL);
+
+ ipc_notify_dead_name(soright, name);
+ }
+
+ it_dnrequests_free(its, dnrequests);
+ }
+
+ if (ip_kotype(port) != IKOT_NONE)
+ ipc_kobject_destroy(port);
+
+ /* Common destruction for the IPC target. */
+ ipc_target_terminate(&port->ip_target);
+
+ ipc_port_release(port); /* consume caller's ref */
+}
+
+/*
+ * Routine: ipc_port_check_circularity
+ * Purpose:
+ * Check if queueing "port" in a message for "dest"
+ * would create a circular group of ports and messages.
+ *
+ * If no circularity (FALSE returned), then "port"
+ * is changed from "in limbo" to "in transit".
+ *
+ * That is, we want to set port->ip_destination == dest,
+ * but guaranteeing that this doesn't create a circle
+ * port->ip_destination->ip_destination->... == port
+ * Conditions:
+ * No ports locked. References held for "port" and "dest".
+ */
+
+boolean_t
+ipc_port_check_circularity(
+ ipc_port_t port,
+ ipc_port_t dest)
+{
+ ipc_port_t base;
+
+ assert(port != IP_NULL);
+ assert(dest != IP_NULL);
+
+ if (port == dest)
+ return TRUE;
+ base = dest;
+
+ /*
+ * First try a quick check that can run in parallel.
+ * No circularity if dest is not in transit.
+ */
+
+ ip_lock(port);
+ if (ip_lock_try(dest)) {
+ if (!ip_active(dest) ||
+ (dest->ip_receiver_name != MACH_PORT_NULL) ||
+ (dest->ip_destination == IP_NULL))
+ goto not_circular;
+
+ /* dest is in transit; further checking necessary */
+
+ ip_unlock(dest);
+ }
+ ip_unlock(port);
+
+ ipc_port_multiple_lock(); /* massive serialization */
+
+ /*
+ * Search for the end of the chain (a port not in transit),
+ * acquiring locks along the way.
+ */
+
+ for (;;) {
+ ip_lock(base);
+
+ if (!ip_active(base) ||
+ (base->ip_receiver_name != MACH_PORT_NULL) ||
+ (base->ip_destination == IP_NULL))
+ break;
+
+ base = base->ip_destination;
+ }
+
+ /* all ports in chain from dest to base, inclusive, are locked */
+
+ if (port == base) {
+ /* circularity detected! */
+
+ ipc_port_multiple_unlock();
+
+ /* port (== base) is in limbo */
+
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == MACH_PORT_NULL);
+ assert(port->ip_destination == IP_NULL);
+
+ while (dest != IP_NULL) {
+ ipc_port_t next;
+
+ /* dest is in transit or in limbo */
+
+ assert(ip_active(dest));
+ assert(dest->ip_receiver_name == MACH_PORT_NULL);
+
+ next = dest->ip_destination;
+ ip_unlock(dest);
+ dest = next;
+ }
+
+ return TRUE;
+ }
+
+ /*
+ * The guarantee: lock port while the entire chain is locked.
+ * Once port is locked, we can take a reference to dest,
+ * add port to the chain, and unlock everything.
+ */
+
+ ip_lock(port);
+ ipc_port_multiple_unlock();
+
+ not_circular:
+
+ /* port is in limbo */
+
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == MACH_PORT_NULL);
+ assert(port->ip_destination == IP_NULL);
+
+ ip_reference(dest);
+ port->ip_destination = dest;
+
+ /* now unlock chain */
+
+ while (port != base) {
+ ipc_port_t next;
+
+ /* port is in transit */
+
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == MACH_PORT_NULL);
+ assert(port->ip_destination != IP_NULL);
+
+ next = port->ip_destination;
+ ip_unlock(port);
+ port = next;
+ }
+
+ /* base is not in transit */
+
+ assert(!ip_active(base) ||
+ (base->ip_receiver_name != MACH_PORT_NULL) ||
+ (base->ip_destination == IP_NULL));
+ ip_unlock(base);
+
+ return FALSE;
+}
+
+/*
+ * Routine: ipc_port_lookup_notify
+ * Purpose:
+ * Make a send-once notify port from a receive right.
+ * Returns IP_NULL if name doesn't denote a receive right.
+ * Conditions:
+ * The space must be locked (read or write) and active.
+ */
+
+ipc_port_t
+ipc_port_lookup_notify(
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ ipc_port_t port;
+ ipc_entry_t entry;
+
+ assert(space->is_active);
+
+ entry = ipc_entry_lookup(space, name);
+ if (entry == IE_NULL)
+ return IP_NULL;
+
+ if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0)
+ return IP_NULL;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+
+ ip_reference(port);
+ port->ip_sorights++;
+ ip_unlock(port);
+
+ return port;
+}
+
+/*
+ * Routine: ipc_port_make_send
+ * Purpose:
+ * Make a naked send right from a receive right.
+ * Conditions:
+ * The port is not locked but it is active.
+ */
+
+ipc_port_t
+ipc_port_make_send(
+ ipc_port_t port)
+{
+ assert(IP_VALID(port));
+
+ ip_lock(port);
+ assert(ip_active(port));
+ port->ip_mscount++;
+ port->ip_srights++;
+ ip_reference(port);
+ ip_unlock(port);
+
+ return port;
+}
+
+/*
+ * Routine: ipc_port_copy_send
+ * Purpose:
+ * Make a naked send right from another naked send right.
+ * IP_NULL -> IP_NULL
+ * IP_DEAD -> IP_DEAD
+ * dead port -> IP_DEAD
+ * live port -> port + ref
+ * Conditions:
+ * Nothing locked except possibly a space.
+ */
+
+ipc_port_t
+ipc_port_copy_send(
+ ipc_port_t port)
+{
+ ipc_port_t sright;
+
+ if (!IP_VALID(port))
+ return port;
+
+ ip_lock(port);
+ if (ip_active(port)) {
+ assert(port->ip_srights > 0);
+
+ ip_reference(port);
+ port->ip_srights++;
+ sright = port;
+ } else
+ sright = IP_DEAD;
+ ip_unlock(port);
+
+ return sright;
+}
+
+/*
+ * Routine: ipc_port_copyout_send
+ * Purpose:
+ * Copyout a naked send right (possibly null/dead),
+ * or if that fails, destroy the right.
+ * Conditions:
+ * Nothing locked.
+ */
+
+mach_port_name_t
+ipc_port_copyout_send(
+ ipc_port_t sright,
+ ipc_space_t space)
+{
+ mach_port_name_t name;
+
+ if (IP_VALID(sright)) {
+ kern_return_t kr;
+
+ kr = ipc_object_copyout(space, (ipc_object_t) sright,
+ MACH_MSG_TYPE_PORT_SEND, TRUE, &name);
+ if (kr != KERN_SUCCESS) {
+ ipc_port_release_send(sright);
+
+ if (kr == KERN_INVALID_CAPABILITY)
+ name = MACH_PORT_NAME_DEAD;
+ else
+ name = MACH_PORT_NAME_NULL;
+ }
+ } else
+ name = invalid_port_to_name((mach_port_t)sright);
+
+ return name;
+}
+
+/*
+ * Routine: ipc_port_release_send
+ * Purpose:
+ * Release a (valid) naked send right.
+ * Consumes a ref for the port.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_port_release_send(
+ ipc_port_t port)
+{
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount;
+
+ assert(IP_VALID(port));
+
+ ip_lock(port);
+ ip_release(port);
+
+ if (!ip_active(port)) {
+ ip_check_unlock(port);
+ return;
+ }
+
+ assert(port->ip_srights > 0);
+
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+
+ ip_unlock(port);
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+}
+
+/*
+ * Routine: ipc_port_make_sonce
+ * Purpose:
+ * Make a naked send-once right from a receive right.
+ * Conditions:
+ * The port is not locked but it is active.
+ */
+
+ipc_port_t
+ipc_port_make_sonce(
+ ipc_port_t port)
+{
+ assert(IP_VALID(port));
+
+ ip_lock(port);
+ assert(ip_active(port));
+ port->ip_sorights++;
+ ip_reference(port);
+ ip_unlock(port);
+
+ return port;
+}
+
+/*
+ * Routine: ipc_port_release_sonce
+ * Purpose:
+ * Release a naked send-once right.
+ * Consumes a ref for the port.
+ *
+ * In normal situations, this is never used.
+ * Send-once rights are only consumed when
+ * a message (possibly a send-once notification)
+ * is sent to them.
+ * Conditions:
+ * Nothing locked except possibly a space.
+ */
+
+void
+ipc_port_release_sonce(
+ ipc_port_t port)
+{
+ assert(IP_VALID(port));
+
+ ip_lock(port);
+
+ assert(port->ip_sorights > 0);
+
+ port->ip_sorights--;
+
+ ip_release(port);
+
+ if (!ip_active(port)) {
+ ip_check_unlock(port);
+ return;
+ }
+
+ ip_unlock(port);
+}
+
+/*
+ * Routine: ipc_port_release_receive
+ * Purpose:
+ * Release a naked (in limbo or in transit) receive right.
+ * Consumes a ref for the port; destroys the port.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_port_release_receive(
+ ipc_port_t port)
+{
+ ipc_port_t dest;
+
+ assert(IP_VALID(port));
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == MACH_PORT_NULL);
+ dest = port->ip_destination;
+
+ ipc_port_destroy(port); /* consumes ref, unlocks */
+
+ if (dest != IP_NULL)
+ ipc_port_release(dest);
+}
+
+/*
+ * Routine: ipc_port_alloc_special
+ * Purpose:
+ * Allocate a port in a special space.
+ * The new port is returned with one ref.
+ * If unsuccessful, IP_NULL is returned.
+ * Conditions:
+ * Nothing locked.
+ */
+
+ipc_port_t
+ipc_port_alloc_special(ipc_space_t space)
+{
+ ipc_port_t port;
+
+ port = ip_alloc();
+ if (port == IP_NULL)
+ return IP_NULL;
+
+ ip_lock_init(port);
+ port->ip_references = 1;
+ port->ip_object.io_bits = io_makebits(TRUE, IOT_PORT, 0);
+
+ /*
+ * The actual values of ip_receiver_name aren't important,
+ * as long as they are valid (not null/dead).
+ *
+ * Mach4: we set it to the internal port structure address
+ * so we can always just pass on ip_receiver_name during
+ * an rpc regardless of whether the destination is user or
+ * kernel (i.e. no special-casing code for the kernel along
+ * the fast rpc path).
+ */
+
+ ipc_port_init(port, space, (mach_port_name_t)port);
+
+ return port;
+}
+
+/*
+ * Routine: ipc_port_dealloc_special
+ * Purpose:
+ * Deallocate a port in a special space.
+ * Consumes one ref for the port.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_port_dealloc_special(
+ ipc_port_t port,
+ ipc_space_t space)
+{
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name != MACH_PORT_NULL);
+ assert(port->ip_receiver == space);
+
+ /*
+ * We clear ip_receiver_name and ip_receiver to simplify
+ * the ipc_space_kernel check in ipc_mqueue_send.
+ */
+
+ port->ip_receiver_name = MACH_PORT_NULL;
+ port->ip_receiver = IS_NULL;
+
+ /*
+ * For ipc_space_kernel, all ipc_port_clear_receiver does
+ * is clean things up for the assertions in ipc_port_destroy.
+ * For ipc_space_reply, there might be a waiting receiver.
+ */
+
+ ipc_port_clear_receiver(port);
+ ipc_port_destroy(port);
+}
+
+#if MACH_KDB
+#define printf kdbprintf
+
+/*
+ * Routine: ipc_port_print
+ * Purpose:
+ * Pretty-print a port for kdb.
+ */
+
+void
+ipc_port_print(const ipc_port_t port)
+{
+ printf("port 0x%x\n", port);
+
+ indent += 2;
+
+ iprintf("flags ");
+ printf("has_protected_payload=%d",
+ ipc_port_flag_protected_payload(port));
+ printf("\n");
+
+ ipc_object_print(&port->ip_object);
+ iprintf("receiver=0x%x", port->ip_receiver);
+ printf(", receiver_name=0x%x\n", port->ip_receiver_name);
+
+ iprintf("mscount=%d", port->ip_mscount);
+ printf(", srights=%d", port->ip_srights);
+ printf(", sorights=%d\n", port->ip_sorights);
+
+ iprintf("nsrequest=0x%x", port->ip_nsrequest);
+ printf(", pdrequest=0x%x", port->ip_pdrequest);
+ printf(", dnrequests=0x%x\n", port->ip_dnrequests);
+
+ iprintf("pset=0x%x", port->ip_pset);
+ printf(", seqno=%d", port->ip_seqno);
+ printf(", msgcount=%d", port->ip_msgcount);
+ printf(", qlimit=%d\n", port->ip_qlimit);
+
+ iprintf("kmsgs=0x%x", port->ip_messages.imq_messages.ikmq_base);
+ printf(", rcvrs=0x%x", port->ip_messages.imq_threads.ithq_base);
+ printf(", sndrs=0x%x", port->ip_blocked.ithq_base);
+ printf(", kobj=0x%x\n", port->ip_kobject);
+
+ iprintf("protected_payload=%p\n", (void *) (vm_offset_t) port->ip_protected_payload);
+
+ indent -= 2;
+}
+
+#endif /* MACH_KDB */
diff --git a/ipc/ipc_port.h b/ipc/ipc_port.h
new file mode 100644
index 0000000..192d880
--- /dev/null
+++ b/ipc/ipc_port.h
@@ -0,0 +1,354 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_port.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for ports.
+ */
+
+#ifndef _IPC_IPC_PORT_H_
+#define _IPC_IPC_PORT_H_
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <kern/lock.h>
+#include <kern/macros.h>
+#include <kern/ipc_kobject.h>
+#include <ipc/ipc_mqueue.h>
+#include <ipc/ipc_table.h>
+#include <ipc/ipc_thread.h>
+#include <ipc/ipc_object.h>
+#include "ipc_target.h"
+
+/*
+ * A receive right (port) can be in four states:
+ * 1) dead (not active, ip_timestamp has death time)
+ * 2) in a space (ip_receiver_name != 0, ip_receiver points
+ * to the space but doesn't hold a ref for it)
+ * 3) in transit (ip_receiver_name == 0, ip_destination points
+ * to the destination port and holds a ref for it)
+ * 4) in limbo (ip_receiver_name == 0, ip_destination == IP_NULL)
+ *
+ * If the port is active, and ip_receiver points to some space,
+ * then ip_receiver_name != 0, and that space holds receive rights.
+ * If the port is not active, then ip_timestamp contains a timestamp
+ * taken when the port was destroyed.
+ */
+
+typedef unsigned int ipc_port_timestamp_t;
+
+struct ipc_port {
+ struct ipc_target ip_target;
+
+ /* This points to the ip_target above if this port isn't on a port set;
+ otherwise it points to the port set's ips_target. */
+ struct ipc_target *ip_cur_target;
+
+ union {
+ struct ipc_space *receiver;
+ struct ipc_port *destination;
+ ipc_port_timestamp_t timestamp;
+ } data;
+
+ ipc_kobject_t ip_kobject;
+
+ mach_port_mscount_t ip_mscount;
+ mach_port_rights_t ip_srights;
+ mach_port_rights_t ip_sorights;
+
+ struct ipc_port *ip_nsrequest;
+ struct ipc_port *ip_pdrequest;
+ struct ipc_port_request *ip_dnrequests;
+
+ struct ipc_pset *ip_pset;
+ mach_port_seqno_t ip_seqno; /* locked by message queue */
+ mach_port_msgcount_t ip_msgcount;
+ mach_port_msgcount_t ip_qlimit;
+ struct ipc_thread_queue ip_blocked;
+ rpc_uintptr_t ip_protected_payload;
+};
+
+#define ip_object ip_target.ipt_object
+#define ip_receiver_name ip_target.ipt_name
+#define ip_messages ip_target.ipt_messages
+#define ip_references ip_object.io_references
+#define ip_bits ip_object.io_bits
+#define ip_receiver data.receiver
+#define ip_destination data.destination
+#define ip_timestamp data.timestamp
+
+#define IP_NULL ((ipc_port_t) IO_NULL)
+#define IP_DEAD ((ipc_port_t) IO_DEAD)
+
+#define IP_VALID(port) IO_VALID(&(port)->ip_object)
+
+#define ip_active(port) io_active(&(port)->ip_object)
+#define ip_lock_init(port) io_lock_init(&(port)->ip_object)
+#define ip_lock(port) io_lock(&(port)->ip_object)
+#define ip_lock_try(port) io_lock_try(&(port)->ip_object)
+#define ip_unlock(port) io_unlock(&(port)->ip_object)
+#define ip_check_unlock(port) io_check_unlock(&(port)->ip_object)
+#define ip_reference(port) io_reference(&(port)->ip_object)
+#define ip_release(port) io_release(&(port)->ip_object)
+
+#define ip_alloc() ((ipc_port_t) io_alloc(IOT_PORT))
+#define ip_free(port) io_free(IOT_PORT, &(port)->ip_object)
+
+#define ip_kotype(port) io_kotype(&(port)->ip_object)
+
+typedef ipc_table_index_t ipc_port_request_index_t;
+
+typedef struct ipc_port_request {
+ union {
+ struct ipc_port *port;
+ ipc_port_request_index_t index;
+ } notify;
+
+ union {
+ mach_port_name_t name;
+ struct ipc_table_size *size;
+ } name;
+} *ipc_port_request_t;
+
+#define ipr_next notify.index
+#define ipr_size name.size
+
+#define ipr_soright notify.port
+#define ipr_name name.name
+
+#define IPR_NULL ((ipc_port_request_t) 0)
+
+/*
+ * Taking the ipc_port_multiple lock grants the privilege
+ * to lock multiple ports at once. No ports must locked
+ * when it is taken.
+ */
+
+decl_simple_lock_data(extern, ipc_port_multiple_lock_data)
+
+#define ipc_port_multiple_lock_init() \
+ simple_lock_init(&ipc_port_multiple_lock_data)
+
+#define ipc_port_multiple_lock() \
+ simple_lock(&ipc_port_multiple_lock_data)
+
+#define ipc_port_multiple_unlock() \
+ simple_unlock(&ipc_port_multiple_lock_data)
+
+/*
+ * The port timestamp facility provides timestamps
+ * for port destruction. It is used to serialize
+ * mach_port_names with port death.
+ */
+
+decl_simple_lock_data(extern, ipc_port_timestamp_lock_data)
+extern ipc_port_timestamp_t ipc_port_timestamp_data;
+
+#define ipc_port_timestamp_lock_init() \
+ simple_lock_init(&ipc_port_timestamp_lock_data)
+
+#define ipc_port_timestamp_lock() \
+ simple_lock(&ipc_port_timestamp_lock_data)
+
+#define ipc_port_timestamp_unlock() \
+ simple_unlock(&ipc_port_timestamp_lock_data)
+
+extern ipc_port_timestamp_t
+ipc_port_timestamp(void);
+
+/*
+ * Compares two timestamps, and returns TRUE if one
+ * happened before two. Note that this formulation
+ * works when the timestamp wraps around at 2^32,
+ * as long as one and two aren't too far apart.
+ */
+
+#define IP_TIMESTAMP_ORDER(one, two) ((int) ((one) - (two)) < 0)
+
+#define ipc_port_translate_receive(space, name, portp) \
+ ipc_object_translate((space), (name), \
+ MACH_PORT_RIGHT_RECEIVE, \
+ (ipc_object_t *) (portp))
+
+#define ipc_port_translate_send(space, name, portp) \
+ ipc_object_translate((space), (name), \
+ MACH_PORT_RIGHT_SEND, \
+ (ipc_object_t *) (portp))
+
+extern kern_return_t
+ipc_port_dnrequest(ipc_port_t, mach_port_name_t, ipc_port_t,
+ ipc_port_request_index_t *);
+
+extern kern_return_t
+ipc_port_dngrow(ipc_port_t);
+
+extern ipc_port_t
+ipc_port_dncancel(ipc_port_t, mach_port_name_t, ipc_port_request_index_t);
+
+#define ipc_port_dnrename(port, index, oname, nname) \
+MACRO_BEGIN \
+ ipc_port_request_t ipr, table; \
+ \
+ assert(ip_active(port)); \
+ \
+ table = port->ip_dnrequests; \
+ assert(table != IPR_NULL); \
+ \
+ ipr = &table[index]; \
+ assert(ipr->ipr_name == oname); \
+ \
+ ipr->ipr_name = nname; \
+MACRO_END
+
+/* Make a port-deleted request */
+extern void ipc_port_pdrequest(
+ ipc_port_t port,
+ ipc_port_t notify,
+ ipc_port_t *previousp);
+
+/* Make a no-senders request */
+extern void ipc_port_nsrequest(
+ ipc_port_t port,
+ mach_port_mscount_t sync,
+ ipc_port_t notify,
+ ipc_port_t *previousp);
+
+/* Change a port's queue limit */
+extern void ipc_port_set_qlimit(
+ ipc_port_t port,
+ mach_port_msgcount_t qlimit);
+
+#define ipc_port_set_mscount(port, mscount) \
+MACRO_BEGIN \
+ assert(ip_active(port)); \
+ \
+ (port)->ip_mscount = (mscount); \
+MACRO_END
+
+extern struct ipc_mqueue *
+ipc_port_lock_mqueue(ipc_port_t);
+
+extern void
+ipc_port_set_seqno(ipc_port_t, mach_port_seqno_t);
+
+extern void
+ipc_port_set_protected_payload(ipc_port_t, rpc_uintptr_t);
+
+extern void
+ipc_port_clear_protected_payload(ipc_port_t);
+
+extern void
+ipc_port_clear_receiver(ipc_port_t);
+
+extern void
+ipc_port_init(ipc_port_t, ipc_space_t, mach_port_name_t);
+
+extern kern_return_t
+ipc_port_alloc(ipc_space_t, mach_port_name_t *, ipc_port_t *);
+
+extern kern_return_t
+ipc_port_alloc_name(ipc_space_t, mach_port_name_t, ipc_port_t *);
+
+extern void
+ipc_port_destroy(ipc_port_t);
+
+extern boolean_t
+ipc_port_check_circularity(ipc_port_t, ipc_port_t);
+
+extern ipc_port_t
+ipc_port_lookup_notify(ipc_space_t, mach_port_name_t);
+
+extern ipc_port_t
+ipc_port_make_send(ipc_port_t);
+
+extern ipc_port_t
+ipc_port_copy_send(ipc_port_t);
+
+extern mach_port_name_t
+ipc_port_copyout_send(ipc_port_t, ipc_space_t);
+
+extern void
+ipc_port_release_send(ipc_port_t);
+
+extern ipc_port_t
+ipc_port_make_sonce(ipc_port_t);
+
+extern void
+ipc_port_release_sonce(ipc_port_t);
+
+extern void
+ipc_port_release_receive(ipc_port_t);
+
+extern ipc_port_t
+ipc_port_alloc_special(ipc_space_t);
+
+extern void
+ipc_port_dealloc_special(ipc_port_t, ipc_space_t);
+
+#define ipc_port_alloc_kernel() \
+ ipc_port_alloc_special(ipc_space_kernel)
+#define ipc_port_dealloc_kernel(port) \
+ ipc_port_dealloc_special((port), ipc_space_kernel)
+
+#define ipc_port_alloc_reply() \
+ ipc_port_alloc_special(ipc_space_reply)
+#define ipc_port_dealloc_reply(port) \
+ ipc_port_dealloc_special((port), ipc_space_reply)
+
+#define ipc_port_reference(port) \
+ ipc_object_reference(&(port)->ip_object)
+
+#define ipc_port_release(port) \
+ ipc_object_release(&(port)->ip_object)
+
+static inline boolean_t
+ipc_port_flag_protected_payload(const struct ipc_port *port)
+{
+ return !! (port->ip_target.ipt_object.io_bits
+ & IO_BITS_PROTECTED_PAYLOAD);
+}
+
+static inline void
+ipc_port_flag_protected_payload_set(struct ipc_port *port)
+{
+ port->ip_target.ipt_object.io_bits |= IO_BITS_PROTECTED_PAYLOAD;
+}
+
+static inline void
+ipc_port_flag_protected_payload_clear(struct ipc_port *port)
+{
+ port->ip_target.ipt_object.io_bits &= ~IO_BITS_PROTECTED_PAYLOAD;
+}
+
+#endif /* _IPC_IPC_PORT_H_ */
diff --git a/ipc/ipc_print.h b/ipc/ipc_print.h
new file mode 100644
index 0000000..5e8e4f3
--- /dev/null
+++ b/ipc/ipc_print.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013 Free Software Foundation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _IPC_PRINT_H_
+#define _IPC_PRINT_H_
+
+#if MACH_KDB
+
+#include <mach/mach_types.h>
+#include <mach/message.h>
+#include <ipc/ipc_types.h>
+#include <ipc/ipc_pset.h>
+
+extern void ipc_port_print(const ipc_port_t);
+
+extern void ipc_pset_print(const ipc_pset_t);
+
+extern void ipc_kmsg_print(const ipc_kmsg_t);
+
+extern void ipc_msg_print(mach_msg_header_t*);
+
+#endif /* MACH_KDB */
+
+#endif /* IPC_PRINT_H */
diff --git a/ipc/ipc_pset.c b/ipc/ipc_pset.c
new file mode 100644
index 0000000..30c12a2
--- /dev/null
+++ b/ipc/ipc_pset.c
@@ -0,0 +1,350 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_pset.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC port sets.
+ */
+
+#include <kern/printf.h>
+#include <mach/port.h>
+#include <mach/kern_return.h>
+#include <mach/message.h>
+#include <ipc/ipc_mqueue.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_right.h>
+#include <ipc/ipc_space.h>
+
+#if MACH_KDB
+#include <ddb/db_output.h>
+#include <ipc/ipc_print.h>
+#endif /* MACH_KDB */
+
+
+/*
+ * Routine: ipc_pset_alloc
+ * Purpose:
+ * Allocate a port set.
+ * Conditions:
+ * Nothing locked. If successful, the port set is returned
+ * locked. (The caller doesn't have a reference.)
+ * Returns:
+ * KERN_SUCCESS The port set is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NO_SPACE No room for an entry in the space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_pset_alloc(
+ ipc_space_t space,
+ mach_port_name_t *namep,
+ ipc_pset_t *psetp)
+{
+ ipc_pset_t pset;
+ mach_port_name_t name;
+ kern_return_t kr;
+
+ kr = ipc_object_alloc(space, IOT_PORT_SET,
+ MACH_PORT_TYPE_PORT_SET, 0,
+ &name, (ipc_object_t *) &pset);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* pset is locked */
+
+ ipc_target_init(&pset->ips_target, name);
+
+ *namep = name;
+ *psetp = pset;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_pset_alloc_name
+ * Purpose:
+ * Allocate a port set, with a specific name.
+ * Conditions:
+ * Nothing locked. If successful, the port set is returned
+ * locked. (The caller doesn't have a reference.)
+ * Returns:
+ * KERN_SUCCESS The port set is allocated.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_pset_alloc_name(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_pset_t *psetp)
+{
+ ipc_pset_t pset;
+ kern_return_t kr;
+
+ kr = ipc_object_alloc_name(space, IOT_PORT_SET,
+ MACH_PORT_TYPE_PORT_SET, 0,
+ name, (ipc_object_t *) &pset);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* pset is locked */
+
+ ipc_target_init(&pset->ips_target, name);
+
+ *psetp = pset;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_pset_add
+ * Purpose:
+ * Puts a port into a port set.
+ * The port set gains a reference.
+ * Conditions:
+ * Both port and port set are locked and active.
+ * The port isn't already in a set.
+ * The owner of the port set is also receiver for the port.
+ */
+
+void
+ipc_pset_add(
+ ipc_pset_t pset,
+ ipc_port_t port)
+{
+ assert(ips_active(pset));
+ assert(ip_active(port));
+ assert(port->ip_pset == IPS_NULL);
+
+ port->ip_pset = pset;
+ port->ip_cur_target = &pset->ips_target;
+ ips_reference(pset);
+
+ imq_lock(&port->ip_messages);
+ imq_lock(&pset->ips_messages);
+
+ /* move messages from port's queue to the port set's queue */
+
+ ipc_mqueue_move(&pset->ips_messages, &port->ip_messages, port);
+ imq_unlock(&pset->ips_messages);
+ assert(ipc_kmsg_queue_empty(&port->ip_messages.imq_messages));
+
+ /* wake up threads waiting to receive from the port */
+
+ ipc_mqueue_changed(&port->ip_messages, MACH_RCV_PORT_CHANGED);
+ assert(ipc_thread_queue_empty(&port->ip_messages.imq_threads));
+ imq_unlock(&port->ip_messages);
+}
+
+/*
+ * Routine: ipc_pset_remove
+ * Purpose:
+ * Removes a port from a port set.
+ * The port set loses a reference.
+ * Conditions:
+ * Both port and port set are locked.
+ * The port must be active.
+ */
+
+void
+ipc_pset_remove(
+ ipc_pset_t pset,
+ ipc_port_t port)
+{
+ assert(ip_active(port));
+ assert(port->ip_pset == pset);
+
+ port->ip_pset = IPS_NULL;
+ port->ip_cur_target = &port->ip_target;
+ ips_release(pset);
+
+ imq_lock(&port->ip_messages);
+ imq_lock(&pset->ips_messages);
+
+ /* move messages from port set's queue to the port's queue */
+
+ ipc_mqueue_move(&port->ip_messages, &pset->ips_messages, port);
+
+ imq_unlock(&pset->ips_messages);
+ imq_unlock(&port->ip_messages);
+}
+
+/*
+ * Routine: ipc_pset_move
+ * Purpose:
+ * If nset is IPS_NULL, removes port
+ * from the port set it is in. Otherwise, adds
+ * port to nset, removing it from any set
+ * it might already be in.
+ * Conditions:
+ * The space is read-locked.
+ * Returns:
+ * KERN_SUCCESS Moved the port.
+ * KERN_NOT_IN_SET nset is null and port isn't in a set.
+ */
+
+kern_return_t
+ipc_pset_move(
+ ipc_space_t space,
+ ipc_port_t port,
+ ipc_pset_t nset)
+{
+ ipc_pset_t oset;
+
+ /*
+ * While we've got the space locked, it holds refs for
+ * the port and nset (because of the entries). Also,
+ * they must be alive. While we've got port locked, it
+ * holds a ref for oset, which might not be alive.
+ */
+
+ ip_lock(port);
+ assert(ip_active(port));
+
+ oset = port->ip_pset;
+
+ if (oset == nset) {
+ /* the port is already in the new set: a noop */
+
+ is_read_unlock(space);
+ } else if (oset == IPS_NULL) {
+ /* just add port to the new set */
+
+ ips_lock(nset);
+ assert(ips_active(nset));
+ is_read_unlock(space);
+
+ ipc_pset_add(nset, port);
+
+ ips_unlock(nset);
+ } else if (nset == IPS_NULL) {
+ /* just remove port from the old set */
+
+ is_read_unlock(space);
+ ips_lock(oset);
+
+ ipc_pset_remove(oset, port);
+
+ if (ips_active(oset))
+ ips_unlock(oset);
+ else {
+ ips_check_unlock(oset);
+ oset = IPS_NULL; /* trigger KERN_NOT_IN_SET */
+ }
+ } else {
+ /* atomically move port from oset to nset */
+
+ if (oset < nset) {
+ ips_lock(oset);
+ ips_lock(nset);
+ } else {
+ ips_lock(nset);
+ ips_lock(oset);
+ }
+
+ is_read_unlock(space);
+ assert(ips_active(nset));
+
+ ipc_pset_remove(oset, port);
+ ipc_pset_add(nset, port);
+
+ ips_unlock(nset);
+ ips_check_unlock(oset); /* KERN_NOT_IN_SET not a possibility */
+ }
+
+ ip_unlock(port);
+
+ return (((nset == IPS_NULL) && (oset == IPS_NULL)) ?
+ KERN_NOT_IN_SET : KERN_SUCCESS);
+}
+
+/*
+ * Routine: ipc_pset_destroy
+ * Purpose:
+ * Destroys a port_set.
+ *
+ * Doesn't remove members from the port set;
+ * that happens lazily. As members are removed,
+ * their messages are removed from the queue.
+ * Conditions:
+ * The port_set is locked and alive.
+ * The caller has a reference, which is consumed.
+ * Afterwards, the port_set is unlocked and dead.
+ */
+
+void
+ipc_pset_destroy(
+ ipc_pset_t pset)
+{
+ assert(ips_active(pset));
+
+ pset->ips_object.io_bits &= ~IO_BITS_ACTIVE;
+
+ imq_lock(&pset->ips_messages);
+ ipc_mqueue_changed(&pset->ips_messages, MACH_RCV_PORT_DIED);
+ imq_unlock(&pset->ips_messages);
+
+ /* Common destruction for the IPC target. */
+ ipc_target_terminate(&pset->ips_target);
+
+ ips_release(pset); /* consume the ref our caller gave us */
+ ips_check_unlock(pset);
+}
+
+
+#if MACH_KDB
+#define printf kdbprintf
+
+/*
+ * Routine: ipc_pset_print
+ * Purpose:
+ * Pretty-print a port set for kdb.
+ */
+
+void
+ipc_pset_print(
+ const ipc_pset_t pset)
+{
+ printf("pset 0x%x\n", pset);
+
+ indent += 2;
+
+ ipc_object_print(&pset->ips_object);
+ iprintf("local_name = 0x%x\n", pset->ips_local_name);
+ iprintf("kmsgs = 0x%x", pset->ips_messages.imq_messages.ikmq_base);
+ printf(",rcvrs = 0x%x\n", pset->ips_messages.imq_threads.ithq_base);
+
+ indent -= 2;
+}
+
+#endif /* MACH_KDB */
diff --git a/ipc/ipc_pset.h b/ipc/ipc_pset.h
new file mode 100644
index 0000000..3f94be5
--- /dev/null
+++ b/ipc/ipc_pset.h
@@ -0,0 +1,92 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_pset.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for port sets.
+ */
+
+#ifndef _IPC_IPC_PSET_H_
+#define _IPC_IPC_PSET_H_
+
+#include <mach/port.h>
+#include <mach/kern_return.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_mqueue.h>
+#include "ipc_target.h"
+
+typedef struct ipc_pset {
+ struct ipc_target ips_target;
+
+} *ipc_pset_t;
+
+#define ips_object ips_target.ipt_object
+#define ips_local_name ips_target.ipt_name
+#define ips_messages ips_target.ipt_messages
+#define ips_references ips_object.io_references
+
+#define IPS_NULL ((ipc_pset_t) IO_NULL)
+
+#define ips_active(pset) io_active(&(pset)->ips_object)
+#define ips_lock(pset) io_lock(&(pset)->ips_object)
+#define ips_lock_try(pset) io_lock_try(&(pset)->ips_object)
+#define ips_unlock(pset) io_unlock(&(pset)->ips_object)
+#define ips_check_unlock(pset) io_check_unlock(&(pset)->ips_object)
+#define ips_reference(pset) io_reference(&(pset)->ips_object)
+#define ips_release(pset) io_release(&(pset)->ips_object)
+
+extern kern_return_t
+ipc_pset_alloc(ipc_space_t, mach_port_name_t *, ipc_pset_t *);
+
+extern kern_return_t
+ipc_pset_alloc_name(ipc_space_t, mach_port_name_t, ipc_pset_t *);
+
+extern void
+ipc_pset_add(ipc_pset_t, ipc_port_t);
+
+extern void
+ipc_pset_remove(ipc_pset_t, ipc_port_t);
+
+extern kern_return_t
+ipc_pset_move(ipc_space_t, ipc_port_t, ipc_pset_t);
+
+extern void
+ipc_pset_destroy(ipc_pset_t);
+
+#define ipc_pset_reference(pset) \
+ ipc_object_reference(&(pset)->ips_object)
+
+#define ipc_pset_release(pset) \
+ ipc_object_release(&(pset)->ips_object)
+
+#endif /* _IPC_IPC_PSET_H_ */
diff --git a/ipc/ipc_right.c b/ipc/ipc_right.c
new file mode 100644
index 0000000..79f70c3
--- /dev/null
+++ b/ipc/ipc_right.c
@@ -0,0 +1,2115 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_right.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC capabilities.
+ */
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/message.h>
+#include <kern/assert.h>
+#include <kern/debug.h>
+#include <ipc/port.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_marequest.h>
+#include <ipc/ipc_right.h>
+#include <ipc/ipc_notify.h>
+
+
+
+/*
+ * Routine: ipc_right_lookup_write
+ * Purpose:
+ * Finds an entry in a space, given the name.
+ * Conditions:
+ * Nothing locked. If successful, the space is write-locked.
+ * Returns:
+ * KERN_SUCCESS Found an entry.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Name doesn't exist in space.
+ */
+
+kern_return_t
+ipc_right_lookup_write(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t *entryp)
+{
+ ipc_entry_t entry;
+
+ assert(space != IS_NULL);
+
+ is_write_lock(space);
+
+ if (!space->is_active) {
+ is_write_unlock(space);
+ return KERN_INVALID_TASK;
+ }
+
+ if ((entry = ipc_entry_lookup(space, name)) == IE_NULL) {
+ is_write_unlock(space);
+ return KERN_INVALID_NAME;
+ }
+
+ *entryp = entry;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_right_reverse
+ * Purpose:
+ * Translate (space, object) -> (name, entry).
+ * Only finds send/receive rights.
+ * Returns TRUE if an entry is found; if so,
+ * the object is locked and active.
+ * Conditions:
+ * The space must be locked (read or write) and active.
+ * Nothing else locked.
+ */
+
+boolean_t
+ipc_right_reverse(
+ ipc_space_t space,
+ ipc_object_t object,
+ mach_port_name_t *namep,
+ ipc_entry_t *entryp)
+{
+ ipc_port_t port;
+ mach_port_name_t name;
+ ipc_entry_t entry;
+
+ /* would switch on io_otype to handle multiple types of object */
+
+ assert(space->is_active);
+ assert(io_otype(object) == IOT_PORT);
+
+ port = (ipc_port_t) object;
+
+ ip_lock(port);
+ if (!ip_active(port)) {
+ ip_unlock(port);
+
+ return FALSE;
+ }
+
+ if (port->ip_receiver == space) {
+ name = port->ip_receiver_name;
+ assert(name != MACH_PORT_NULL);
+
+ entry = ipc_entry_lookup(space, name);
+
+ assert(entry != IE_NULL);
+ assert(entry->ie_bits & MACH_PORT_TYPE_RECEIVE);
+ assert(port == (ipc_port_t) entry->ie_object);
+
+ *namep = name;
+ *entryp = entry;
+ return TRUE;
+ }
+
+ if ((*entryp = ipc_reverse_lookup(space, (ipc_object_t) port))) {
+ *namep = (*entryp)->ie_name;
+ assert((entry = *entryp) != IE_NULL);
+ assert(IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_SEND);
+ assert(port == (ipc_port_t) entry->ie_object);
+
+ return TRUE;
+ }
+
+ ip_unlock(port);
+ return FALSE;
+}
+
+/*
+ * Routine: ipc_right_dnrequest
+ * Purpose:
+ * Make a dead-name request, returning the previously
+ * registered send-once right. If notify is IP_NULL,
+ * just cancels the previously registered request.
+ *
+ * This interacts with the IE_BITS_COMPAT, because they
+ * both use ie_request. If this is a compat entry, then
+ * previous always gets IP_NULL. If notify is IP_NULL,
+ * then the entry remains a compat entry. Otherwise
+ * the real dead-name request is registered and the entry
+ * is no longer a compat entry.
+ * Conditions:
+ * Nothing locked. May allocate memory.
+ * Only consumes/returns refs if successful.
+ * Returns:
+ * KERN_SUCCESS Made/canceled dead-name request.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Name doesn't exist in space.
+ * KERN_INVALID_RIGHT Name doesn't denote port/dead rights.
+ * KERN_INVALID_ARGUMENT Name denotes dead name, but
+ * immediate is FALSE or notify is IP_NULL.
+ * KERN_UREFS_OVERFLOW Name denotes dead name, but
+ * generating immediate notif. would overflow urefs.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_right_dnrequest(
+ ipc_space_t space,
+ mach_port_name_t name,
+ boolean_t immediate,
+ ipc_port_t notify,
+ ipc_port_t *previousp)
+{
+ ipc_port_t previous;
+
+ for (;;) {
+ ipc_entry_t entry;
+ ipc_entry_bits_t bits;
+ kern_return_t kr;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ bits = entry->ie_bits;
+ if (bits & MACH_PORT_TYPE_PORT_RIGHTS) {
+ ipc_port_t port;
+ ipc_port_request_index_t request;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (!ipc_right_check(space, port, name, entry)) {
+ /* port is locked and active */
+
+ if (notify == IP_NULL) {
+ previous = ipc_right_dncancel_macro(
+ space, port, name, entry);
+
+ ip_unlock(port);
+ is_write_unlock(space);
+ break;
+ }
+
+ /*
+ * If a registered soright exists,
+ * want to atomically switch with it.
+ * If ipc_port_dncancel finds us a
+ * soright, then the following
+ * ipc_port_dnrequest will reuse
+ * that slot, so we are guaranteed
+ * not to unlock and retry.
+ */
+
+ previous = ipc_right_dncancel_macro(space,
+ port, name, entry);
+
+ kr = ipc_port_dnrequest(port, name, notify,
+ &request);
+ if (kr != KERN_SUCCESS) {
+ assert(previous == IP_NULL);
+ is_write_unlock(space);
+
+ kr = ipc_port_dngrow(port);
+ /* port is unlocked */
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ continue;
+ }
+
+ assert(request != 0);
+ ip_unlock(port);
+
+ entry->ie_request = request;
+ is_write_unlock(space);
+ break;
+ }
+
+ bits = entry->ie_bits;
+ assert(bits & MACH_PORT_TYPE_DEAD_NAME);
+ }
+
+ if ((bits & MACH_PORT_TYPE_DEAD_NAME) &&
+ immediate && (notify != IP_NULL)) {
+ mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
+
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ assert(urefs > 0);
+
+ if (MACH_PORT_UREFS_OVERFLOW(urefs, 1)) {
+ is_write_unlock(space);
+ return KERN_UREFS_OVERFLOW;
+ }
+
+ entry->ie_bits = bits + 1; /* increment urefs */
+ is_write_unlock(space);
+
+ ipc_notify_dead_name(notify, name);
+ previous = IP_NULL;
+ break;
+ }
+
+ is_write_unlock(space);
+ if (bits & MACH_PORT_TYPE_PORT_OR_DEAD)
+ return KERN_INVALID_ARGUMENT;
+ else
+ return KERN_INVALID_RIGHT;
+ }
+
+ *previousp = previous;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_right_dncancel
+ * Purpose:
+ * Cancel a dead-name request and return the send-once right.
+ * Afterwards, entry->ie_request == 0.
+ * Conditions:
+ * The space must be write-locked; the port must be locked.
+ * The port must be active; the space doesn't have to be.
+ */
+
+ipc_port_t
+ipc_right_dncancel(
+ ipc_space_t space,
+ ipc_port_t port,
+ mach_port_name_t name,
+ ipc_entry_t entry)
+{
+ ipc_port_t dnrequest;
+
+ assert(ip_active(port));
+ assert(port == (ipc_port_t) entry->ie_object);
+
+ dnrequest = ipc_port_dncancel(port, name, entry->ie_request);
+ entry->ie_request = 0;
+
+ return dnrequest;
+}
+
+/*
+ * Routine: ipc_right_inuse
+ * Purpose:
+ * Check if an entry is being used.
+ * Returns TRUE if it is.
+ * Conditions:
+ * The space is write-locked and active.
+ * It is unlocked if the entry is inuse.
+ */
+
+boolean_t
+ipc_right_inuse(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+
+ if (IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE) {
+ is_write_unlock(space);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Routine: ipc_right_check
+ * Purpose:
+ * Check if the port has died. If it has,
+ * clean up the entry and return TRUE.
+ * Conditions:
+ * The space is write-locked; the port is not locked.
+ * If returns FALSE, the port is also locked and active.
+ * Otherwise, entry is converted to a dead name, freeing
+ * a reference to port.
+ */
+
+boolean_t
+ipc_right_check(
+ ipc_space_t space,
+ ipc_port_t port,
+ mach_port_name_t name,
+ ipc_entry_t entry)
+{
+ ipc_entry_bits_t bits;
+
+ assert(space->is_active);
+ assert(port == (ipc_port_t) entry->ie_object);
+
+ ip_lock(port);
+ if (ip_active(port))
+ return FALSE;
+ ip_unlock(port);
+
+ /* this was either a pure send right or a send-once right */
+
+ bits = entry->ie_bits;
+ assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ if (bits & MACH_PORT_TYPE_SEND) {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
+
+ /* clean up msg-accepted request */
+
+ if (bits & IE_BITS_MAREQUEST) {
+ bits &= ~IE_BITS_MAREQUEST;
+
+ ipc_marequest_cancel(space, name);
+ }
+
+ ipc_reverse_remove(space, (ipc_object_t) port);
+ } else {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+ assert(IE_BITS_UREFS(bits) == 1);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ }
+
+ ipc_port_release(port);
+
+ /* convert entry to dead name */
+
+ bits = (bits &~ IE_BITS_TYPE_MASK) | MACH_PORT_TYPE_DEAD_NAME;
+
+ if (entry->ie_request != 0) {
+ assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
+
+ entry->ie_request = 0;
+ bits++; /* increment urefs */
+ }
+
+ entry->ie_bits = bits;
+ entry->ie_object = IO_NULL;
+
+ return TRUE;
+}
+
+/*
+ * Routine: ipc_right_clean
+ * Purpose:
+ * Cleans up an entry in a dead space.
+ * The entry isn't deallocated or removed
+ * from the reverse mappings.
+ * Conditions:
+ * The space is dead and unlocked.
+ */
+
+void
+ipc_right_clean(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+ mach_port_type_t type = IE_BITS_TYPE(bits);
+
+ assert(!space->is_active);
+
+ /*
+ * We can't clean up IE_BITS_MAREQUEST when the space is dead.
+ * This is because ipc_marequest_destroy can't turn off
+ * the bit if the space is dead. Hence, it might be on
+ * even though the marequest has been destroyed. It's OK
+ * not to cancel the marequest, because ipc_marequest_destroy
+ * cancels for us if the space is dead.
+ *
+ * IE_BITS_COMPAT/ipc_right_dncancel doesn't have this
+ * problem, because we check that the port is active. If
+ * we didn't cancel IE_BITS_COMPAT, ipc_port_destroy
+ * would still work, but dead space refs would accumulate
+ * in ip_dnrequests. They would use up slots in
+ * ip_dnrequests and keep the spaces from being freed.
+ */
+
+ switch (type) {
+ case MACH_PORT_TYPE_DEAD_NAME:
+ assert(entry->ie_request == 0);
+ assert(entry->ie_object == IO_NULL);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ break;
+
+ case MACH_PORT_TYPE_PORT_SET: {
+ ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
+
+ assert(entry->ie_request == 0);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ assert(pset != IPS_NULL);
+
+ ips_lock(pset);
+ assert(ips_active(pset));
+
+ ipc_pset_destroy(pset); /* consumes ref, unlocks */
+ break;
+ }
+
+ case MACH_PORT_TYPE_SEND:
+ case MACH_PORT_TYPE_RECEIVE:
+ case MACH_PORT_TYPE_SEND_RECEIVE:
+ case MACH_PORT_TYPE_SEND_ONCE: {
+ ipc_port_t port = (ipc_port_t) entry->ie_object;
+ ipc_port_t dnrequest;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */
+
+ assert(port != IP_NULL);
+ ip_lock(port);
+
+ if (!ip_active(port)) {
+ ip_release(port);
+ ip_check_unlock(port);
+ break;
+ }
+
+ dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+
+ if (type & MACH_PORT_TYPE_SEND) {
+ assert(port->ip_srights > 0);
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+ }
+
+ if (type & MACH_PORT_TYPE_RECEIVE) {
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+
+ ipc_port_clear_receiver(port);
+ ipc_port_destroy(port); /* consumes our ref, unlocks */
+ } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
+ assert(port->ip_sorights > 0);
+ ip_unlock(port);
+
+ ipc_notify_send_once(port); /* consumes our ref */
+ } else {
+ assert(port->ip_receiver != space);
+
+ ip_release(port);
+ ip_unlock(port); /* port is active */
+ }
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+
+ if (dnrequest != IP_NULL)
+ ipc_notify_port_deleted(dnrequest, name);
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_right_clean: strange type");
+#else
+ panic("ipc_right_clean: strange type");
+#endif
+ }
+}
+
+/*
+ * Routine: ipc_right_destroy
+ * Purpose:
+ * Destroys an entry in a space.
+ * Conditions:
+ * The space is write-locked, and is unlocked upon return.
+ * The space must be active.
+ * Returns:
+ * KERN_SUCCESS The entry was destroyed.
+ */
+
+kern_return_t
+ipc_right_destroy(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+ mach_port_type_t type = IE_BITS_TYPE(bits);
+
+ assert(space->is_active);
+
+ switch (type) {
+ case MACH_PORT_TYPE_DEAD_NAME:
+ assert(entry->ie_request == 0);
+ assert(entry->ie_object == IO_NULL);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+ break;
+
+ case MACH_PORT_TYPE_PORT_SET: {
+ ipc_pset_t pset = (ipc_pset_t) entry->ie_object;
+
+ assert(entry->ie_request == 0);
+ assert(pset != IPS_NULL);
+
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+
+ ips_lock(pset);
+ assert(ips_active(pset));
+ is_write_unlock(space);
+
+ ipc_pset_destroy(pset); /* consumes ref, unlocks */
+ break;
+ }
+
+ case MACH_PORT_TYPE_SEND:
+ case MACH_PORT_TYPE_RECEIVE:
+ case MACH_PORT_TYPE_SEND_RECEIVE:
+ case MACH_PORT_TYPE_SEND_ONCE: {
+ ipc_port_t port = (ipc_port_t) entry->ie_object;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */
+ ipc_port_t dnrequest;
+
+ assert(port != IP_NULL);
+
+ if (bits & IE_BITS_MAREQUEST) {
+ assert(type & MACH_PORT_TYPE_SEND_RECEIVE);
+
+ ipc_marequest_cancel(space, name);
+ }
+
+ if (type == MACH_PORT_TYPE_SEND)
+ ipc_reverse_remove(space, (ipc_object_t) port);
+
+ ip_lock(port);
+
+ if (!ip_active(port)) {
+ assert((type & MACH_PORT_TYPE_RECEIVE) == 0);
+
+ ip_release(port);
+ ip_check_unlock(port);
+
+ entry->ie_request = 0;
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ break;
+ }
+
+ dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ if (type & MACH_PORT_TYPE_SEND) {
+ assert(port->ip_srights > 0);
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+ }
+
+ if (type & MACH_PORT_TYPE_RECEIVE) {
+ assert(ip_active(port));
+ assert(port->ip_receiver == space);
+
+ ipc_port_clear_receiver(port);
+ ipc_port_destroy(port); /* consumes our ref, unlocks */
+ } else if (type & MACH_PORT_TYPE_SEND_ONCE) {
+ assert(port->ip_sorights > 0);
+ ip_unlock(port);
+
+ ipc_notify_send_once(port); /* consumes our ref */
+ } else {
+ assert(port->ip_receiver != space);
+
+ ip_release(port);
+ ip_unlock(port);
+ }
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+
+ if (dnrequest != IP_NULL)
+ ipc_notify_port_deleted(dnrequest, name);
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_right_destroy: strange type");
+#else
+ panic("ipc_right_destroy: strange type");
+#endif
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_right_dealloc
+ * Purpose:
+ * Releases a send/send-once/dead-name user ref.
+ * Like ipc_right_delta with a delta of -1,
+ * but looks at the entry to determine the right.
+ * Conditions:
+ * The space is write-locked, and is unlocked upon return.
+ * The space must be active.
+ * Returns:
+ * KERN_SUCCESS A user ref was released.
+ * KERN_INVALID_RIGHT Entry has wrong type.
+ */
+
+kern_return_t
+ipc_right_dealloc(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+ mach_port_type_t type = IE_BITS_TYPE(bits);
+
+ assert(space->is_active);
+
+ switch (type) {
+ case MACH_PORT_TYPE_DEAD_NAME: {
+ dead_name:
+
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert(entry->ie_request == 0);
+ assert(entry->ie_object == IO_NULL);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+
+ if (IE_BITS_UREFS(bits) == 1)
+ ipc_entry_dealloc(space, name, entry);
+ else
+ entry->ie_bits = bits-1; /* decrement urefs */
+
+ is_write_unlock(space);
+ break;
+ }
+
+ case MACH_PORT_TYPE_SEND_ONCE: {
+ ipc_port_t port, dnrequest;
+
+ assert(IE_BITS_UREFS(bits) == 1);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ bits = entry->ie_bits;
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ goto dead_name;
+ }
+ /* port is locked and active */
+
+ assert(port->ip_sorights > 0);
+
+ dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+ ip_unlock(port);
+
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ ipc_notify_send_once(port);
+
+ if (dnrequest != IP_NULL)
+ ipc_notify_port_deleted(dnrequest, name);
+ break;
+ }
+
+ case MACH_PORT_TYPE_SEND: {
+ ipc_port_t port;
+ ipc_port_t dnrequest = IP_NULL;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ bits = entry->ie_bits;
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ goto dead_name;
+ }
+ /* port is locked and active */
+
+ assert(port->ip_srights > 0);
+
+ if (IE_BITS_UREFS(bits) == 1) {
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+
+ dnrequest = ipc_right_dncancel_macro(space, port,
+ name, entry);
+
+ ipc_reverse_remove(space, (ipc_object_t) port);
+
+ if (bits & IE_BITS_MAREQUEST)
+ ipc_marequest_cancel(space, name);
+
+ ip_release(port);
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ } else
+ entry->ie_bits = bits-1; /* decrement urefs */
+
+ ip_unlock(port); /* even if dropped a ref, port is active */
+ is_write_unlock(space);
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+
+ if (dnrequest != IP_NULL)
+ ipc_notify_port_deleted(dnrequest, name);
+ break;
+ }
+
+ case MACH_PORT_TYPE_SEND_RECEIVE: {
+ ipc_port_t port;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+ assert(port->ip_srights > 0);
+
+ if (IE_BITS_UREFS(bits) == 1) {
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+
+ entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|
+ MACH_PORT_TYPE_SEND);
+ } else
+ entry->ie_bits = bits-1; /* decrement urefs */
+
+ ip_unlock(port);
+ is_write_unlock(space);
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+ break;
+ }
+
+ default:
+ is_write_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_right_delta
+ * Purpose:
+ * Modifies the user-reference count for a right.
+ * May deallocate the right, if the count goes to zero.
+ * Conditions:
+ * The space is write-locked, and is unlocked upon return.
+ * The space must be active.
+ * Returns:
+ * KERN_SUCCESS Count was modified.
+ * KERN_INVALID_RIGHT Entry has wrong type.
+ * KERN_INVALID_VALUE Bad delta for the right.
+ * KERN_UREFS_OVERFLOW OK delta, except would overflow.
+ */
+
+kern_return_t
+ipc_right_delta(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_port_right_t right,
+ mach_port_delta_t delta)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+
+ assert(space->is_active);
+ assert(right < MACH_PORT_RIGHT_NUMBER);
+
+ /* Rights-specific restrictions and operations. */
+
+ switch (right) {
+ case MACH_PORT_RIGHT_PORT_SET: {
+ ipc_pset_t pset;
+
+ if ((bits & MACH_PORT_TYPE_PORT_SET) == 0)
+ goto invalid_right;
+
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_PORT_SET);
+ assert(IE_BITS_UREFS(bits) == 0);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ assert(entry->ie_request == 0);
+
+ if (delta == 0)
+ goto success;
+
+ if (delta != -1)
+ goto invalid_value;
+
+ pset = (ipc_pset_t) entry->ie_object;
+ assert(pset != IPS_NULL);
+
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+
+ ips_lock(pset);
+ assert(ips_active(pset));
+ is_write_unlock(space);
+
+ ipc_pset_destroy(pset); /* consumes ref, unlocks */
+ break;
+ }
+
+ case MACH_PORT_RIGHT_RECEIVE: {
+ ipc_port_t port;
+ ipc_port_t dnrequest = IP_NULL;
+
+ if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+ goto invalid_right;
+
+ if (delta == 0)
+ goto success;
+
+ if (delta != -1)
+ goto invalid_value;
+
+ if (bits & IE_BITS_MAREQUEST) {
+ bits &= ~IE_BITS_MAREQUEST;
+
+ ipc_marequest_cancel(space, name);
+ }
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ /*
+ * The port lock is needed for ipc_right_dncancel;
+ * otherwise, we wouldn't have to take the lock
+ * until just before dropping the space lock.
+ */
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+
+ if (bits & MACH_PORT_TYPE_SEND) {
+ assert(IE_BITS_TYPE(bits) ==
+ MACH_PORT_TYPE_SEND_RECEIVE);
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
+ assert(port->ip_srights > 0);
+
+ /*
+ * The remaining send right turns into a
+ * dead name. Notice we don't decrement
+ * ip_srights, generate a no-senders notif,
+ * or use ipc_right_dncancel, because the
+ * port is destroyed "first".
+ */
+
+ bits &= ~IE_BITS_TYPE_MASK;
+ bits |= MACH_PORT_TYPE_DEAD_NAME;
+
+ if (entry->ie_request != 0) {
+ entry->ie_request = 0;
+ bits++; /* increment urefs */
+ }
+
+ entry->ie_bits = bits;
+ entry->ie_object = IO_NULL;
+ } else {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
+ assert(IE_BITS_UREFS(bits) == 0);
+
+ dnrequest = ipc_right_dncancel_macro(space, port,
+ name, entry);
+
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ }
+ is_write_unlock(space);
+
+ ipc_port_clear_receiver(port);
+ ipc_port_destroy(port); /* consumes ref, unlocks */
+
+ if (dnrequest != IP_NULL)
+ ipc_notify_port_deleted(dnrequest, name);
+ break;
+ }
+
+ case MACH_PORT_RIGHT_SEND_ONCE: {
+ ipc_port_t port, dnrequest;
+
+ if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0)
+ goto invalid_right;
+
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+ assert(IE_BITS_UREFS(bits) == 1);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+
+ if ((delta > 0) || (delta < -1))
+ goto invalid_value;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ assert(!(entry->ie_bits & MACH_PORT_TYPE_SEND_ONCE));
+ goto invalid_right;
+ }
+ /* port is locked and active */
+
+ assert(port->ip_sorights > 0);
+
+ if (delta == 0) {
+ ip_unlock(port);
+ goto success;
+ }
+
+ dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+ ip_unlock(port);
+
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ is_write_unlock(space);
+
+ ipc_notify_send_once(port);
+
+ if (dnrequest != IP_NULL)
+ ipc_notify_port_deleted(dnrequest, name);
+ break;
+ }
+
+ case MACH_PORT_RIGHT_DEAD_NAME: {
+ mach_port_urefs_t urefs;
+
+ if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
+ ipc_port_t port;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (!ipc_right_check(space, port, name, entry)) {
+ /* port is locked and active */
+ ip_unlock(port);
+ goto invalid_right;
+ }
+
+ bits = entry->ie_bits;
+ } else if ((bits & MACH_PORT_TYPE_DEAD_NAME) == 0)
+ goto invalid_right;
+
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ assert(entry->ie_object == IO_NULL);
+ assert(entry->ie_request == 0);
+
+ urefs = IE_BITS_UREFS(bits);
+ if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta))
+ goto invalid_value;
+ if (MACH_PORT_UREFS_OVERFLOW(urefs, delta))
+ goto urefs_overflow;
+
+ if ((urefs + delta) == 0)
+ ipc_entry_dealloc(space, name, entry);
+ else
+ entry->ie_bits = bits + delta;
+
+ is_write_unlock(space);
+ break;
+ }
+
+ case MACH_PORT_RIGHT_SEND: {
+ mach_port_urefs_t urefs;
+ ipc_port_t port;
+ ipc_port_t dnrequest = IP_NULL;
+ ipc_port_t nsrequest = IP_NULL;
+ mach_port_mscount_t mscount = 0; /* '=0' to shut up lint */
+
+ if ((bits & MACH_PORT_TYPE_SEND) == 0)
+ goto invalid_right;
+
+ /* maximum urefs for send is MACH_PORT_UREFS_MAX-1 */
+
+ urefs = IE_BITS_UREFS(bits);
+ if (MACH_PORT_UREFS_UNDERFLOW(urefs, delta))
+ goto invalid_value;
+ if (MACH_PORT_UREFS_OVERFLOW(urefs+1, delta))
+ goto urefs_overflow;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ assert((entry->ie_bits & MACH_PORT_TYPE_SEND) == 0);
+ goto invalid_right;
+ }
+ /* port is locked and active */
+
+ assert(port->ip_srights > 0);
+
+ if ((urefs + delta) == 0) {
+ if (--port->ip_srights == 0) {
+ nsrequest = port->ip_nsrequest;
+ if (nsrequest != IP_NULL) {
+ port->ip_nsrequest = IP_NULL;
+ mscount = port->ip_mscount;
+ }
+ }
+
+ if (bits & MACH_PORT_TYPE_RECEIVE) {
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+ assert(IE_BITS_TYPE(bits) ==
+ MACH_PORT_TYPE_SEND_RECEIVE);
+
+ entry->ie_bits = bits &~ (IE_BITS_UREFS_MASK|
+ MACH_PORT_TYPE_SEND);
+ } else {
+ assert(IE_BITS_TYPE(bits) ==
+ MACH_PORT_TYPE_SEND);
+
+ dnrequest = ipc_right_dncancel_macro(
+ space, port, name, entry);
+
+ ipc_reverse_remove(space, (ipc_object_t) port);
+
+ if (bits & IE_BITS_MAREQUEST)
+ ipc_marequest_cancel(space, name);
+
+ ip_release(port);
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, name, entry);
+ }
+ } else
+ entry->ie_bits = bits + delta;
+
+ ip_unlock(port); /* even if dropped a ref, port is active */
+ is_write_unlock(space);
+
+ if (nsrequest != IP_NULL)
+ ipc_notify_no_senders(nsrequest, mscount);
+
+ if (dnrequest != IP_NULL)
+ ipc_notify_port_deleted(dnrequest, name);
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_right_delta: strange right");
+#else
+ panic("ipc_right_delta: strange right");
+#endif
+ }
+
+ return KERN_SUCCESS;
+
+ success:
+ is_write_unlock(space);
+ return KERN_SUCCESS;
+
+ invalid_right:
+ is_write_unlock(space);
+ return KERN_INVALID_RIGHT;
+
+ invalid_value:
+ is_write_unlock(space);
+ return KERN_INVALID_VALUE;
+
+ urefs_overflow:
+ is_write_unlock(space);
+ return KERN_UREFS_OVERFLOW;
+}
+
+/*
+ * Routine: ipc_right_info
+ * Purpose:
+ * Retrieves information about the right.
+ * Conditions:
+ * The space is write-locked, and is unlocked upon return
+ * if the call is unsuccessful. The space must be active.
+ * Returns:
+ * KERN_SUCCESS Retrieved info; space still locked.
+ */
+
+kern_return_t
+ipc_right_info(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_port_type_t *typep,
+ mach_port_urefs_t *urefsp)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+ ipc_port_request_index_t request;
+ mach_port_type_t type;
+
+ if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
+ ipc_port_t port = (ipc_port_t) entry->ie_object;
+
+ if (ipc_right_check(space, port, name, entry)) {
+ bits = entry->ie_bits;
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ } else
+ ip_unlock(port);
+ }
+
+ type = IE_BITS_TYPE(bits);
+ request = entry->ie_request;
+
+ if (request != 0)
+ type |= MACH_PORT_TYPE_DNREQUEST;
+ if (bits & IE_BITS_MAREQUEST)
+ type |= MACH_PORT_TYPE_MAREQUEST;
+
+ *typep = type;
+ *urefsp = IE_BITS_UREFS(bits);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_right_copyin_check
+ * Purpose:
+ * Check if a subsequent ipc_right_copyin would succeed.
+ * Conditions:
+ * The space is locked (read or write) and active.
+ */
+
+boolean_t
+ipc_right_copyin_check(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_msg_type_name_t msgt_name)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+
+ assert(space->is_active);
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_MAKE_SEND:
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+ case MACH_MSG_TYPE_MOVE_RECEIVE:
+ if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+ return FALSE;
+
+ break;
+
+ case MACH_MSG_TYPE_COPY_SEND:
+ case MACH_MSG_TYPE_MOVE_SEND:
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
+ ipc_port_t port;
+ boolean_t active;
+
+ if (bits & MACH_PORT_TYPE_DEAD_NAME)
+ break;
+
+ if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+ return FALSE;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ active = ip_active(port);
+ ip_unlock(port);
+
+ if (!active) {
+ break;
+ }
+
+ if (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE) {
+ if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0)
+ return FALSE;
+ } else {
+ if ((bits & MACH_PORT_TYPE_SEND) == 0)
+ return FALSE;
+ }
+
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_right_copyin_check: strange rights");
+#else
+ panic("ipc_right_copyin_check: strange rights");
+#endif
+ }
+
+ return TRUE;
+}
+
+/*
+ * Routine: ipc_right_copyin
+ * Purpose:
+ * Copyin a capability from a space.
+ * If successful, the caller gets a ref
+ * for the resulting object, unless it is IO_DEAD,
+ * and possibly a send-once right which should
+ * be used in a port-deleted notification.
+ *
+ * If deadok is not TRUE, the copyin operation
+ * will fail instead of producing IO_DEAD.
+ *
+ * The entry is never deallocated (except
+ * when KERN_INVALID_NAME), so the caller
+ * should deallocate the entry if its type
+ * is MACH_PORT_TYPE_NONE.
+ * Conditions:
+ * The space is write-locked and active.
+ * Returns:
+ * KERN_SUCCESS Acquired an object, possibly IO_DEAD.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right.
+ */
+
+kern_return_t
+ipc_right_copyin(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_msg_type_name_t msgt_name,
+ boolean_t deadok,
+ ipc_object_t *objectp,
+ ipc_port_t *sorightp)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+
+ assert(space->is_active);
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_MAKE_SEND: {
+ ipc_port_t port;
+
+ if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+ goto invalid_right;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+
+ port->ip_mscount++;
+ port->ip_srights++;
+ ip_reference(port);
+ ip_unlock(port);
+
+ *objectp = (ipc_object_t) port;
+ *sorightp = IP_NULL;
+ break;
+ }
+
+ case MACH_MSG_TYPE_MAKE_SEND_ONCE: {
+ ipc_port_t port;
+
+ if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+ goto invalid_right;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+
+ port->ip_sorights++;
+ ip_reference(port);
+ ip_unlock(port);
+
+ *objectp = (ipc_object_t) port;
+ *sorightp = IP_NULL;
+ break;
+ }
+
+ case MACH_MSG_TYPE_MOVE_RECEIVE: {
+ ipc_port_t port;
+ ipc_port_t dnrequest = IP_NULL;
+
+ if ((bits & MACH_PORT_TYPE_RECEIVE) == 0)
+ goto invalid_right;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+
+ if (bits & MACH_PORT_TYPE_SEND) {
+ assert(IE_BITS_TYPE(bits) ==
+ MACH_PORT_TYPE_SEND_RECEIVE);
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert(port->ip_srights > 0);
+
+ entry->ie_name = name;
+ ipc_reverse_insert(space, (ipc_object_t) port, entry);
+
+ ip_reference(port);
+ } else {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
+ assert(IE_BITS_UREFS(bits) == 0);
+
+ dnrequest = ipc_right_dncancel_macro(space, port,
+ name, entry);
+
+ if (bits & IE_BITS_MAREQUEST)
+ ipc_marequest_cancel(space, name);
+
+ entry->ie_object = IO_NULL;
+ }
+ entry->ie_bits = bits &~ MACH_PORT_TYPE_RECEIVE;
+
+ ipc_port_clear_receiver(port);
+
+ port->ip_receiver_name = MACH_PORT_NULL;
+ port->ip_destination = IP_NULL;
+
+ /*
+ * Clear the protected payload field to retain
+ * the behavior of mach_msg.
+ */
+ ipc_port_flag_protected_payload_clear(port);
+ ip_unlock(port);
+
+ *objectp = (ipc_object_t) port;
+ *sorightp = dnrequest;
+ break;
+ }
+
+ case MACH_MSG_TYPE_COPY_SEND: {
+ ipc_port_t port;
+
+ if (bits & MACH_PORT_TYPE_DEAD_NAME)
+ goto copy_dead;
+
+ /* allow for dead send-once rights */
+
+ if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+ goto invalid_right;
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ bits = entry->ie_bits;
+ goto copy_dead;
+ }
+ /* port is locked and active */
+
+ if ((bits & MACH_PORT_TYPE_SEND) == 0) {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+ assert(port->ip_sorights > 0);
+
+ ip_unlock(port);
+ goto invalid_right;
+ }
+
+ assert(port->ip_srights > 0);
+
+ port->ip_srights++;
+ ip_reference(port);
+ ip_unlock(port);
+
+ *objectp = (ipc_object_t) port;
+ *sorightp = IP_NULL;
+ break;
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND: {
+ ipc_port_t port;
+ ipc_port_t dnrequest = IP_NULL;
+
+ if (bits & MACH_PORT_TYPE_DEAD_NAME)
+ goto move_dead;
+
+ /* allow for dead send-once rights */
+
+ if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+ goto invalid_right;
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ bits = entry->ie_bits;
+ goto move_dead;
+ }
+ /* port is locked and active */
+
+ if ((bits & MACH_PORT_TYPE_SEND) == 0) {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+ assert(port->ip_sorights > 0);
+
+ ip_unlock(port);
+ goto invalid_right;
+ }
+
+ assert(port->ip_srights > 0);
+
+ if (IE_BITS_UREFS(bits) == 1) {
+ if (bits & MACH_PORT_TYPE_RECEIVE) {
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+ assert(IE_BITS_TYPE(bits) ==
+ MACH_PORT_TYPE_SEND_RECEIVE);
+
+ ip_reference(port);
+ } else {
+ assert(IE_BITS_TYPE(bits) ==
+ MACH_PORT_TYPE_SEND);
+
+ dnrequest = ipc_right_dncancel_macro(
+ space, port, name, entry);
+
+ ipc_reverse_remove(space, (ipc_object_t) port);
+
+ if (bits & IE_BITS_MAREQUEST)
+ ipc_marequest_cancel(space, name);
+
+ entry->ie_object = IO_NULL;
+ }
+ entry->ie_bits = bits &~
+ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND);
+ } else {
+ port->ip_srights++;
+ ip_reference(port);
+ entry->ie_bits = bits-1; /* decrement urefs */
+ }
+
+ ip_unlock(port);
+
+ *objectp = (ipc_object_t) port;
+ *sorightp = dnrequest;
+ break;
+ }
+
+ case MACH_MSG_TYPE_MOVE_SEND_ONCE: {
+ ipc_port_t port;
+ ipc_port_t dnrequest;
+
+ if (bits & MACH_PORT_TYPE_DEAD_NAME)
+ goto move_dead;
+
+ /* allow for dead send rights */
+
+ if ((bits & MACH_PORT_TYPE_SEND_RIGHTS) == 0)
+ goto invalid_right;
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ bits = entry->ie_bits;
+ goto move_dead;
+ }
+ /* port is locked and active */
+
+ if ((bits & MACH_PORT_TYPE_SEND_ONCE) == 0) {
+ assert(bits & MACH_PORT_TYPE_SEND);
+ assert(port->ip_srights > 0);
+
+ ip_unlock(port);
+ goto invalid_right;
+ }
+
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND_ONCE);
+ assert(IE_BITS_UREFS(bits) == 1);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ assert(port->ip_sorights > 0);
+
+ dnrequest = ipc_right_dncancel_macro(space, port, name, entry);
+ ip_unlock(port);
+
+ entry->ie_object = IO_NULL;
+ entry->ie_bits = bits &~ MACH_PORT_TYPE_SEND_ONCE;
+
+ *objectp = (ipc_object_t) port;
+ *sorightp = dnrequest;
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_right_copyin: strange rights");
+#else
+ panic("ipc_right_copyin: strange rights");
+#endif
+ }
+
+ return KERN_SUCCESS;
+
+ copy_dead:
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ assert(entry->ie_request == 0);
+ assert(entry->ie_object == 0);
+
+ if (!deadok)
+ goto invalid_right;
+
+ *objectp = IO_DEAD;
+ *sorightp = IP_NULL;
+ return KERN_SUCCESS;
+
+ move_dead:
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert((bits & IE_BITS_MAREQUEST) == 0);
+ assert(entry->ie_request == 0);
+ assert(entry->ie_object == 0);
+
+ if (!deadok)
+ goto invalid_right;
+
+ if (IE_BITS_UREFS(bits) == 1)
+ entry->ie_bits = bits &~ MACH_PORT_TYPE_DEAD_NAME;
+ else
+ entry->ie_bits = bits-1; /* decrement urefs */
+
+ *objectp = IO_DEAD;
+ *sorightp = IP_NULL;
+ return KERN_SUCCESS;
+
+ invalid_right:
+ return KERN_INVALID_RIGHT;
+}
+
+/*
+ * Routine: ipc_right_copyin_undo
+ * Purpose:
+ * Undoes the effects of an ipc_right_copyin
+ * of a send/send-once right that is dead.
+ * (Object is either IO_DEAD or a dead port.)
+ * Conditions:
+ * The space is write-locked and active.
+ */
+
+void
+ipc_right_copyin_undo(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_msg_type_name_t msgt_name,
+ ipc_object_t object,
+ ipc_port_t soright)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+
+ assert(space->is_active);
+
+ assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
+ (msgt_name == MACH_MSG_TYPE_COPY_SEND) ||
+ (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
+
+ if (soright != IP_NULL) {
+ assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
+ (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
+ assert(entry->ie_object == IO_NULL);
+ assert(object != IO_DEAD);
+
+ entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) |
+ MACH_PORT_TYPE_DEAD_NAME | 2);
+ } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE) {
+ assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
+ (msgt_name == MACH_MSG_TYPE_MOVE_SEND_ONCE));
+ assert(entry->ie_object == IO_NULL);
+
+ entry->ie_bits = ((bits &~ IE_BITS_RIGHT_MASK) |
+ MACH_PORT_TYPE_DEAD_NAME | 1);
+ } else if (IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME) {
+ assert(entry->ie_object == IO_NULL);
+ assert(object == IO_DEAD);
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
+ assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX);
+
+ entry->ie_bits = bits+1; /* increment urefs */
+ }
+ } else {
+ assert((msgt_name == MACH_MSG_TYPE_MOVE_SEND) ||
+ (msgt_name == MACH_MSG_TYPE_COPY_SEND));
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
+ assert(object != IO_DEAD);
+ assert(entry->ie_object == object);
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ if (msgt_name != MACH_MSG_TYPE_COPY_SEND) {
+ assert(IE_BITS_UREFS(bits) < MACH_PORT_UREFS_MAX-1);
+
+ entry->ie_bits = bits+1; /* increment urefs */
+ }
+
+ /*
+ * May as well convert the entry to a dead name.
+ * (Or if it is a compat entry, destroy it.)
+ */
+
+ (void) ipc_right_check(space, (ipc_port_t) object,
+ name, entry);
+ /* object is dead so it is not locked */
+ }
+
+ /* release the reference acquired by copyin */
+
+ if (object != IO_DEAD)
+ ipc_object_release(object);
+}
+
+/*
+ * Routine: ipc_right_copyin_two
+ * Purpose:
+ * Like ipc_right_copyin with MACH_MSG_TYPE_MOVE_SEND
+ * and deadok == FALSE, except that this moves two
+ * send rights at once.
+ * Conditions:
+ * The space is write-locked and active.
+ * The object is returned with two refs/send rights.
+ * Returns:
+ * KERN_SUCCESS Acquired an object.
+ * KERN_INVALID_RIGHT Name doesn't denote correct right.
+ */
+
+kern_return_t
+ipc_right_copyin_two(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ ipc_object_t *objectp,
+ ipc_port_t *sorightp)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+ mach_port_urefs_t urefs;
+ ipc_port_t port;
+ ipc_port_t dnrequest = IP_NULL;
+
+ assert(space->is_active);
+
+ if ((bits & MACH_PORT_TYPE_SEND) == 0)
+ goto invalid_right;
+
+ urefs = IE_BITS_UREFS(bits);
+ if (urefs < 2)
+ goto invalid_right;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, name, entry)) {
+ goto invalid_right;
+ }
+ /* port is locked and active */
+
+ assert(port->ip_srights > 0);
+
+ if (urefs == 2) {
+ if (bits & MACH_PORT_TYPE_RECEIVE) {
+ assert(port->ip_receiver_name == name);
+ assert(port->ip_receiver == space);
+ assert(IE_BITS_TYPE(bits) ==
+ MACH_PORT_TYPE_SEND_RECEIVE);
+
+ port->ip_srights++;
+ ip_reference(port);
+ ip_reference(port);
+ } else {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
+
+ dnrequest = ipc_right_dncancel_macro(space, port,
+ name, entry);
+
+ ipc_reverse_remove(space, (ipc_object_t) port);
+
+ if (bits & IE_BITS_MAREQUEST)
+ ipc_marequest_cancel(space, name);
+
+ port->ip_srights++;
+ ip_reference(port);
+ entry->ie_object = IO_NULL;
+ }
+ entry->ie_bits = bits &~
+ (IE_BITS_UREFS_MASK|MACH_PORT_TYPE_SEND);
+ } else {
+ port->ip_srights += 2;
+ ip_reference(port);
+ ip_reference(port);
+ entry->ie_bits = bits-2; /* decrement urefs */
+ }
+ ip_unlock(port);
+
+ *objectp = (ipc_object_t) port;
+ *sorightp = dnrequest;
+ return KERN_SUCCESS;
+
+ invalid_right:
+ return KERN_INVALID_RIGHT;
+}
+
+/*
+ * Routine: ipc_right_copyout
+ * Purpose:
+ * Copyout a capability to a space.
+ * If successful, consumes a ref for the object.
+ *
+ * Always succeeds when given a newly-allocated entry,
+ * because user-reference overflow isn't a possibility.
+ *
+ * If copying out the object would cause the user-reference
+ * count in the entry to overflow, and overflow is TRUE,
+ * then instead the user-reference count is left pegged
+ * to its maximum value and the copyout succeeds anyway.
+ * Conditions:
+ * The space is write-locked and active.
+ * The object is locked and active.
+ * The object is unlocked; the space isn't.
+ * Returns:
+ * KERN_SUCCESS Copied out capability.
+ * KERN_UREFS_OVERFLOW User-refs would overflow;
+ * guaranteed not to happen with a fresh entry
+ * or if overflow=TRUE was specified.
+ */
+
+kern_return_t
+ipc_right_copyout(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry,
+ mach_msg_type_name_t msgt_name,
+ boolean_t overflow,
+ ipc_object_t object)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+ ipc_port_t port;
+
+ assert(IO_VALID(object));
+ assert(io_otype(object) == IOT_PORT);
+ assert(io_active(object));
+ assert(entry->ie_object == object);
+
+ port = (ipc_port_t) object;
+
+ switch (msgt_name) {
+ case MACH_MSG_TYPE_PORT_SEND_ONCE:
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
+ assert(port->ip_sorights > 0);
+
+ /* transfer send-once right and ref to entry */
+ ip_unlock(port);
+
+ entry->ie_bits = bits | (MACH_PORT_TYPE_SEND_ONCE | 1);
+ break;
+
+ case MACH_MSG_TYPE_PORT_SEND:
+ assert(port->ip_srights > 0);
+
+ if (bits & MACH_PORT_TYPE_SEND) {
+ mach_port_urefs_t urefs = IE_BITS_UREFS(bits);
+
+ assert(port->ip_srights > 1);
+ assert(urefs > 0);
+ assert(urefs < MACH_PORT_UREFS_MAX);
+
+ if (urefs+1 == MACH_PORT_UREFS_MAX) {
+ if (overflow) {
+ /* leave urefs pegged to maximum */
+
+ port->ip_srights--;
+ ip_release(port);
+ ip_unlock(port);
+ return KERN_SUCCESS;
+ }
+
+ ip_unlock(port);
+ return KERN_UREFS_OVERFLOW;
+ }
+
+ port->ip_srights--;
+ ip_release(port);
+ ip_unlock(port);
+ } else if (bits & MACH_PORT_TYPE_RECEIVE) {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_RECEIVE);
+ assert(IE_BITS_UREFS(bits) == 0);
+
+ /* transfer send right to entry */
+ ip_release(port);
+ ip_unlock(port);
+ } else {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
+ assert(IE_BITS_UREFS(bits) == 0);
+
+ /* transfer send right and ref to entry */
+ ip_unlock(port);
+
+ /* entry is locked holding ref, so can use port */
+
+ entry->ie_name = name;
+ ipc_reverse_insert(space, (ipc_object_t) port, entry);
+ }
+
+ entry->ie_bits = (bits | MACH_PORT_TYPE_SEND) + 1;
+ break;
+
+ case MACH_MSG_TYPE_PORT_RECEIVE: {
+ ipc_port_t dest;
+
+ assert(port->ip_mscount == 0);
+ assert(port->ip_receiver_name == MACH_PORT_NULL);
+ dest = port->ip_destination;
+
+ port->ip_receiver_name = name;
+ port->ip_receiver = space;
+
+ /*
+ * Clear the protected payload field to retain
+ * the behavior of mach_msg.
+ */
+ ipc_port_flag_protected_payload_clear(port);
+
+ assert((bits & MACH_PORT_TYPE_RECEIVE) == 0);
+
+ if (bits & MACH_PORT_TYPE_SEND) {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_SEND);
+ assert(IE_BITS_UREFS(bits) > 0);
+ assert(port->ip_srights > 0);
+
+ ip_release(port);
+ ip_unlock(port);
+
+ /* entry is locked holding ref, so can use port */
+
+ ipc_reverse_remove(space, (ipc_object_t) port);
+ } else {
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_NONE);
+ assert(IE_BITS_UREFS(bits) == 0);
+
+ /* transfer ref to entry */
+ ip_unlock(port);
+ }
+
+ entry->ie_bits = bits | MACH_PORT_TYPE_RECEIVE;
+
+ if (dest != IP_NULL)
+ ipc_port_release(dest);
+ break;
+ }
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_right_copyout: strange rights");
+#else
+ panic("ipc_right_copyout: strange rights");
+#endif
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_right_rename
+ * Purpose:
+ * Transfer an entry from one name to another.
+ * The old entry is deallocated.
+ * Conditions:
+ * The space is write-locked and active.
+ * The new entry is unused. Upon return,
+ * the space is unlocked.
+ * Returns:
+ * KERN_SUCCESS Moved entry to new name.
+ */
+
+kern_return_t
+ipc_right_rename(
+ ipc_space_t space,
+ mach_port_name_t oname,
+ ipc_entry_t oentry,
+ mach_port_name_t nname,
+ ipc_entry_t nentry)
+{
+ ipc_entry_bits_t bits = oentry->ie_bits;
+ ipc_port_request_index_t request = oentry->ie_request;
+ ipc_object_t object = oentry->ie_object;
+
+ assert(space->is_active);
+ assert(oname != nname);
+
+ /*
+ * If IE_BITS_COMPAT, we can't allow the entry to be renamed
+ * if the port is dead. (This would foil ipc_port_destroy.)
+ * Instead we should fail because oentry shouldn't exist.
+ * Note IE_BITS_COMPAT implies ie_request != 0.
+ */
+
+ if (request != 0) {
+ ipc_port_t port;
+
+ assert(bits & MACH_PORT_TYPE_PORT_RIGHTS);
+ port = (ipc_port_t) object;
+ assert(port != IP_NULL);
+
+ if (ipc_right_check(space, port, oname, oentry)) {
+ bits = oentry->ie_bits;
+ assert(IE_BITS_TYPE(bits) == MACH_PORT_TYPE_DEAD_NAME);
+ assert(oentry->ie_request == 0);
+ request = 0;
+ assert(oentry->ie_object == IO_NULL);
+ object = IO_NULL;
+ } else {
+ /* port is locked and active */
+
+ ipc_port_dnrename(port, request, oname, nname);
+ ip_unlock(port);
+ oentry->ie_request = 0;
+ }
+ }
+
+ if (bits & IE_BITS_MAREQUEST) {
+ assert(bits & MACH_PORT_TYPE_SEND_RECEIVE);
+
+ ipc_marequest_rename(space, oname, nname);
+ }
+
+ /* initialize nentry before letting ipc_reverse_insert see it */
+
+ assert((nentry->ie_bits & IE_BITS_RIGHT_MASK) == 0);
+ nentry->ie_bits |= bits & IE_BITS_RIGHT_MASK;
+ nentry->ie_request = request;
+ nentry->ie_object = object;
+
+ switch (IE_BITS_TYPE(bits)) {
+ case MACH_PORT_TYPE_SEND: {
+ ipc_port_t port;
+
+ port = (ipc_port_t) object;
+ assert(port != IP_NULL);
+
+ ipc_reverse_remove(space, (ipc_object_t) port);
+ nentry->ie_name = nname;
+ ipc_reverse_insert(space, (ipc_object_t) port, nentry);
+ break;
+ }
+
+ case MACH_PORT_TYPE_RECEIVE:
+ case MACH_PORT_TYPE_SEND_RECEIVE: {
+ ipc_port_t port;
+
+ port = (ipc_port_t) object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+ assert(port->ip_receiver_name == oname);
+ assert(port->ip_receiver == space);
+
+ port->ip_receiver_name = nname;
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_PORT_TYPE_PORT_SET: {
+ ipc_pset_t pset;
+
+ pset = (ipc_pset_t) object;
+ assert(pset != IPS_NULL);
+
+ ips_lock(pset);
+ assert(ips_active(pset));
+ assert(pset->ips_local_name == oname);
+
+ pset->ips_local_name = nname;
+ ips_unlock(pset);
+ break;
+ }
+
+ case MACH_PORT_TYPE_SEND_ONCE:
+ case MACH_PORT_TYPE_DEAD_NAME:
+ break;
+
+ default:
+#if MACH_ASSERT
+ assert(!"ipc_right_rename: strange rights");
+#else
+ panic("ipc_right_rename: strange rights");
+#endif
+ }
+
+ assert(oentry->ie_request == 0);
+ oentry->ie_object = IO_NULL;
+ ipc_entry_dealloc(space, oname, oentry);
+ is_write_unlock(space);
+
+ return KERN_SUCCESS;
+}
diff --git a/ipc/ipc_right.h b/ipc/ipc_right.h
new file mode 100644
index 0000000..6802abb
--- /dev/null
+++ b/ipc/ipc_right.h
@@ -0,0 +1,112 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_right.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Declarations of functions to manipulate IPC capabilities.
+ */
+
+#ifndef _IPC_IPC_RIGHT_H_
+#define _IPC_IPC_RIGHT_H_
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_port.h>
+
+#define ipc_right_lookup_read ipc_right_lookup_write
+
+extern kern_return_t
+ipc_right_lookup_write(ipc_space_t, mach_port_name_t, ipc_entry_t *);
+
+extern boolean_t
+ipc_right_reverse(ipc_space_t, ipc_object_t,
+ mach_port_name_t *, ipc_entry_t *);
+
+extern kern_return_t
+ipc_right_dnrequest(ipc_space_t, mach_port_name_t, boolean_t,
+ ipc_port_t, ipc_port_t *);
+
+extern ipc_port_t
+ipc_right_dncancel(ipc_space_t, ipc_port_t, mach_port_name_t, ipc_entry_t);
+
+#define ipc_right_dncancel_macro(space, port, name, entry) \
+ (((entry)->ie_request == 0) ? IP_NULL : \
+ ipc_right_dncancel((space), (port), (name), (entry)))
+
+extern boolean_t
+ipc_right_inuse(ipc_space_t, mach_port_name_t, ipc_entry_t);
+
+extern boolean_t
+ipc_right_check(ipc_space_t, ipc_port_t, mach_port_name_t, ipc_entry_t);
+
+extern void
+ipc_right_clean(ipc_space_t, mach_port_name_t, ipc_entry_t);
+
+extern kern_return_t
+ipc_right_destroy(ipc_space_t, mach_port_name_t, ipc_entry_t);
+
+extern kern_return_t
+ipc_right_dealloc(ipc_space_t, mach_port_name_t, ipc_entry_t);
+
+extern kern_return_t
+ipc_right_delta(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ mach_port_right_t, mach_port_delta_t);
+
+extern kern_return_t
+ipc_right_info(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ mach_port_type_t *, mach_port_urefs_t *);
+
+extern boolean_t
+ipc_right_copyin_check(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ mach_msg_type_name_t);
+
+extern kern_return_t
+ipc_right_copyin(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ mach_msg_type_name_t, boolean_t,
+ ipc_object_t *, ipc_port_t *);
+
+extern void
+ipc_right_copyin_undo(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ mach_msg_type_name_t, ipc_object_t, ipc_port_t);
+
+extern kern_return_t
+ipc_right_copyin_two(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ ipc_object_t *, ipc_port_t *);
+
+extern kern_return_t
+ipc_right_copyout(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ mach_msg_type_name_t, boolean_t, ipc_object_t);
+
+extern kern_return_t
+ipc_right_rename(ipc_space_t, mach_port_name_t, ipc_entry_t,
+ mach_port_name_t, ipc_entry_t);
+
+#endif /* _IPC_IPC_RIGHT_H_ */
diff --git a/ipc/ipc_space.c b/ipc/ipc_space.c
new file mode 100644
index 0000000..77040d1
--- /dev/null
+++ b/ipc/ipc_space.c
@@ -0,0 +1,215 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_space.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate IPC capability spaces.
+ */
+
+#include <string.h>
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <kern/assert.h>
+#include <kern/sched_prim.h>
+#include <kern/slab.h>
+#include <ipc/port.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_table.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_right.h>
+
+
+
+struct kmem_cache ipc_space_cache;
+ipc_space_t ipc_space_kernel;
+ipc_space_t ipc_space_reply;
+
+/*
+ * Routine: ipc_space_reference
+ * Routine: ipc_space_release
+ * Purpose:
+ * Function versions of the IPC space macros.
+ * The "is_" cover macros can be defined to use the
+ * macros or the functions, as desired.
+ */
+
+void
+ipc_space_reference(
+ ipc_space_t space)
+{
+ ipc_space_reference_macro(space);
+}
+
+void
+ipc_space_release(
+ ipc_space_t space)
+{
+ ipc_space_release_macro(space);
+}
+
+/* A place-holder object for the zeroth entry. */
+struct ipc_entry zero_entry;
+
+/*
+ * Routine: ipc_space_create
+ * Purpose:
+ * Creates a new IPC space.
+ *
+ * The new space has two references, one for the caller
+ * and one because it is active.
+ * Conditions:
+ * Nothing locked. Allocates memory.
+ * Returns:
+ * KERN_SUCCESS Created a space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_space_create(
+ ipc_space_t *spacep)
+{
+ ipc_space_t space;
+
+ space = is_alloc();
+ if (space == IS_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ is_ref_lock_init(space);
+ space->is_references = 2;
+
+ is_lock_init(space);
+ space->is_active = TRUE;
+
+ rdxtree_init(&space->is_map);
+ rdxtree_init(&space->is_reverse_map);
+ /* The zeroth entry is reserved. */
+ rdxtree_insert(&space->is_map, 0, &zero_entry);
+ space->is_size = 1;
+ space->is_free_list = NULL;
+ space->is_free_list_size = 0;
+
+ *spacep = space;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_space_create_special
+ * Purpose:
+ * Create a special space. A special space
+ * doesn't hold rights in the normal way.
+ * Instead it is place-holder for holding
+ * disembodied (naked) receive rights.
+ * See ipc_port_alloc_special/ipc_port_dealloc_special.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Created a space.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+ipc_space_create_special(
+ ipc_space_t *spacep)
+{
+ ipc_space_t space;
+
+ space = is_alloc();
+ if (space == IS_NULL)
+ return KERN_RESOURCE_SHORTAGE;
+
+ is_ref_lock_init(space);
+ space->is_references = 1;
+
+ is_lock_init(space);
+ space->is_active = FALSE;
+
+ *spacep = space;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_space_destroy
+ * Purpose:
+ * Marks the space as dead and cleans up the entries.
+ * Does nothing if the space is already dead.
+ * Conditions:
+ * Nothing locked.
+ */
+
+void
+ipc_space_destroy(
+ ipc_space_t space)
+{
+ boolean_t active;
+
+ assert(space != IS_NULL);
+
+ is_write_lock(space);
+ active = space->is_active;
+ space->is_active = FALSE;
+ is_write_unlock(space);
+
+ if (!active)
+ return;
+
+ ipc_entry_t entry;
+ struct rdxtree_iter iter;
+ rdxtree_for_each(&space->is_map, &iter, entry) {
+ if (entry->ie_name == MACH_PORT_NULL)
+ continue;
+
+ mach_port_type_t type = IE_BITS_TYPE(entry->ie_bits);
+
+ if (type != MACH_PORT_TYPE_NONE) {
+ mach_port_name_t name =
+ MACH_PORT_MAKEB(entry->ie_name, entry->ie_bits);
+
+ ipc_right_clean(space, name, entry);
+ }
+
+ ie_free(entry);
+ }
+ rdxtree_remove_all(&space->is_map);
+ rdxtree_remove_all(&space->is_reverse_map);
+
+ /*
+ * Because the space is now dead,
+ * we must release the "active" reference for it.
+ * Our caller still has his reference.
+ */
+
+ is_release(space);
+}
diff --git a/ipc/ipc_space.h b/ipc/ipc_space.h
new file mode 100644
index 0000000..96d5894
--- /dev/null
+++ b/ipc/ipc_space.h
@@ -0,0 +1,324 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_space.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for IPC spaces of capabilities.
+ */
+
+#ifndef _IPC_IPC_SPACE_H_
+#define _IPC_IPC_SPACE_H_
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/mach_types.h>
+#include <machine/vm_param.h>
+#include <kern/macros.h>
+#include <kern/lock.h>
+#include <kern/rdxtree.h>
+#include <kern/slab.h>
+#include <kern/printf.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_types.h>
+
+/*
+ * Every task has a space of IPC capabilities.
+ * IPC operations like send and receive use this space.
+ * IPC kernel calls manipulate the space of the target task.
+ */
+
+typedef unsigned int ipc_space_refs_t;
+
+struct ipc_space {
+ decl_simple_lock_data(,is_ref_lock_data)
+ ipc_space_refs_t is_references;
+
+ struct lock is_lock_data;
+ boolean_t is_active; /* is the space alive? */
+ struct rdxtree is_map; /* a map of entries */
+ size_t is_size; /* number of entries */
+ struct rdxtree is_reverse_map; /* maps objects to entries */
+ ipc_entry_t is_free_list; /* a linked list of free entries */
+ size_t is_free_list_size; /* number of free entries */
+#define IS_FREE_LIST_SIZE_LIMIT 64 /* maximum number of entries
+ in the free list */
+};
+
+
+#define IS_NULL ((ipc_space_t) 0)
+
+extern struct kmem_cache ipc_space_cache;
+
+#define is_alloc() ((ipc_space_t) kmem_cache_alloc(&ipc_space_cache))
+#define is_free(is) kmem_cache_free(&ipc_space_cache, (vm_offset_t) (is))
+
+extern struct ipc_space *ipc_space_kernel;
+extern struct ipc_space *ipc_space_reply;
+
+#define is_ref_lock_init(is) simple_lock_init(&(is)->is_ref_lock_data)
+
+#define ipc_space_reference_macro(is) \
+MACRO_BEGIN \
+ simple_lock(&(is)->is_ref_lock_data); \
+ assert((is)->is_references > 0); \
+ (is)->is_references++; \
+ simple_unlock(&(is)->is_ref_lock_data); \
+MACRO_END
+
+#define ipc_space_release_macro(is) \
+MACRO_BEGIN \
+ ipc_space_refs_t _refs; \
+ \
+ simple_lock(&(is)->is_ref_lock_data); \
+ assert((is)->is_references > 0); \
+ _refs = --(is)->is_references; \
+ simple_unlock(&(is)->is_ref_lock_data); \
+ \
+ if (_refs == 0) \
+ is_free(is); \
+MACRO_END
+
+#define is_lock_init(is) lock_init(&(is)->is_lock_data, TRUE)
+
+#define is_read_lock(is) lock_read(&(is)->is_lock_data)
+#define is_read_unlock(is) lock_done(&(is)->is_lock_data)
+
+#define is_write_lock(is) lock_write(&(is)->is_lock_data)
+#define is_write_lock_try(is) lock_try_write(&(is)->is_lock_data)
+#define is_write_unlock(is) lock_done(&(is)->is_lock_data)
+
+#define is_write_to_read_lock(is) lock_write_to_read(&(is)->is_lock_data)
+
+extern void ipc_space_reference(struct ipc_space *space);
+extern void ipc_space_release(struct ipc_space *space);
+
+#define is_reference(is) ipc_space_reference_macro(is)
+#define is_release(is) ipc_space_release_macro(is)
+
+kern_return_t ipc_space_create(ipc_space_t *);
+kern_return_t ipc_space_create_special(struct ipc_space **);
+void ipc_space_destroy(struct ipc_space *);
+
+/* IPC entry lookups. */
+
+/*
+ * Routine: ipc_entry_lookup
+ * Purpose:
+ * Searches for an entry, given its name.
+ * Conditions:
+ * The space must be read or write locked throughout.
+ * The space must be active.
+ */
+
+static inline ipc_entry_t
+ipc_entry_lookup(
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ ipc_entry_t entry;
+
+ assert(space->is_active);
+ entry = rdxtree_lookup(&space->is_map, (rdxtree_key_t) name);
+ if (entry != IE_NULL
+ && IE_BITS_TYPE(entry->ie_bits) == MACH_PORT_TYPE_NONE)
+ entry = NULL;
+ assert((entry == IE_NULL) || IE_BITS_TYPE(entry->ie_bits));
+ return entry;
+}
+
+extern volatile boolean_t mach_port_deallocate_debug;
+
+static inline void
+ipc_entry_lookup_failed(mach_msg_header_t *msg, mach_port_name_t name)
+{
+ if (name == MACH_PORT_NAME_NULL || name == MACH_PORT_NAME_DEAD)
+ return;
+ printf("task %.*s looked up a bogus port %lu for %d, most probably a bug.\n", (int) sizeof current_task()->name, current_task()->name, (unsigned long) name, msg->msgh_id);
+ if (mach_port_deallocate_debug)
+ SoftDebugger("ipc_entry_lookup");
+}
+
+/*
+ * Routine: ipc_entry_get
+ * Purpose:
+ * Tries to allocate an entry out of the space.
+ * Conditions:
+ * The space is write-locked and active throughout.
+ * An object may be locked. Will not allocate memory.
+ * Returns:
+ * KERN_SUCCESS A free entry was found.
+ * KERN_NO_SPACE No entry allocated.
+ */
+
+static inline kern_return_t
+ipc_entry_get(
+ ipc_space_t space,
+ mach_port_name_t *namep,
+ ipc_entry_t *entryp)
+{
+ mach_port_name_t new_name;
+ ipc_entry_t free_entry;
+
+ assert(space->is_active);
+
+ /* Get entry from the free list. */
+ free_entry = space->is_free_list;
+ if (free_entry == IE_NULL)
+ return KERN_NO_SPACE;
+
+ space->is_free_list = free_entry->ie_next_free;
+ space->is_free_list_size -= 1;
+
+ /*
+ * Initialize the new entry. We need only
+ * increment the generation number and clear ie_request.
+ */
+
+ {
+ mach_port_gen_t gen;
+
+ assert((free_entry->ie_bits &~ IE_BITS_GEN_MASK) == 0);
+ gen = free_entry->ie_bits + IE_BITS_GEN_ONE;
+ free_entry->ie_bits = gen;
+ free_entry->ie_request = 0;
+ new_name = MACH_PORT_MAKE(free_entry->ie_name, gen);
+ }
+
+ /*
+ * The new name can't be MACH_PORT_NULL because index
+ * is non-zero. It can't be MACH_PORT_DEAD because
+ * the table isn't allowed to grow big enough.
+ * (See comment in ipc/ipc_table.h.)
+ */
+
+ assert(MACH_PORT_NAME_VALID(new_name));
+ assert(free_entry->ie_object == IO_NULL);
+
+ space->is_size += 1;
+ *namep = new_name;
+ *entryp = free_entry;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: ipc_entry_dealloc
+ * Purpose:
+ * Deallocates an entry from a space.
+ * Conditions:
+ * The space must be write-locked throughout.
+ * The space must be active.
+ */
+
+static inline void
+ipc_entry_dealloc(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_entry_t entry)
+{
+ assert(space->is_active);
+ assert(entry->ie_object == IO_NULL);
+ assert(entry->ie_request == 0);
+
+ if (space->is_free_list_size < IS_FREE_LIST_SIZE_LIMIT) {
+ space->is_free_list_size += 1;
+ entry->ie_bits &= IE_BITS_GEN_MASK;
+ entry->ie_next_free = space->is_free_list;
+ space->is_free_list = entry;
+ } else {
+ rdxtree_remove(&space->is_map, (rdxtree_key_t) name);
+ ie_free(entry);
+ }
+ space->is_size -= 1;
+}
+
+/* Reverse lookups. */
+
+/* Cast a pointer to a suitable key. */
+#define KEY(X) \
+ ({ \
+ assert((((unsigned long) (X)) & 0x07) == 0); \
+ ((unsigned long long) \
+ (((unsigned long) (X) - VM_MIN_KERNEL_ADDRESS) >> 3)); \
+ })
+
+/* Insert (OBJ, ENTRY) pair into the reverse mapping. SPACE must
+ be write-locked. */
+static inline kern_return_t
+ipc_reverse_insert(ipc_space_t space,
+ ipc_object_t obj,
+ ipc_entry_t entry)
+{
+ assert(space != IS_NULL);
+ assert(obj != IO_NULL);
+ return (kern_return_t) rdxtree_insert(&space->is_reverse_map,
+ KEY(obj), entry);
+}
+
+/* Remove OBJ from the reverse mapping. SPACE must be
+ write-locked. */
+static inline ipc_entry_t
+ipc_reverse_remove(ipc_space_t space,
+ ipc_object_t obj)
+{
+ assert(space != IS_NULL);
+ assert(obj != IO_NULL);
+ return rdxtree_remove(&space->is_reverse_map, KEY(obj));
+}
+
+/* Remove all entries from the reverse mapping. SPACE must be
+ write-locked. */
+static inline void
+ipc_reverse_remove_all(ipc_space_t space)
+{
+ assert(space != IS_NULL);
+ rdxtree_remove_all(&space->is_reverse_map);
+ assert(space->is_reverse_map.height == 0);
+ assert(space->is_reverse_map.root == NULL);
+}
+
+/* Return ENTRY related to OBJ, or NULL if no such entry is found in
+ the reverse mapping. SPACE must be read-locked or
+ write-locked. */
+static inline ipc_entry_t
+ipc_reverse_lookup(ipc_space_t space,
+ ipc_object_t obj)
+{
+ assert(space != IS_NULL);
+ assert(obj != IO_NULL);
+ return rdxtree_lookup(&space->is_reverse_map, KEY(obj));
+}
+
+#undef KEY
+
+#endif /* _IPC_IPC_SPACE_H_ */
diff --git a/ipc/ipc_table.c b/ipc/ipc_table.c
new file mode 100644
index 0000000..0f8592a
--- /dev/null
+++ b/ipc/ipc_table.c
@@ -0,0 +1,135 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_table.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Functions to manipulate tables of IPC capabilities.
+ */
+
+#include <mach/kern_return.h>
+#include <mach/vm_param.h>
+#include <ipc/ipc_table.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_entry.h>
+#include <kern/kalloc.h>
+#include <kern/slab.h>
+#include <vm/vm_kern.h>
+
+ipc_table_size_t ipc_table_dnrequests;
+const unsigned int ipc_table_dnrequests_size = 64;
+
+void
+ipc_table_fill(
+ ipc_table_size_t its, /* array to fill */
+ unsigned int num, /* size of array */
+ unsigned int min, /* at least this many elements */
+ vm_size_t elemsize) /* size of elements */
+{
+ unsigned int index;
+ vm_size_t minsize = min * elemsize;
+ vm_size_t size;
+ vm_size_t incrsize;
+
+ /* first use powers of two, up to the page size */
+
+ for (index = 0, size = 1;
+ (index < num) && (size < PAGE_SIZE);
+ size <<= 1) {
+ if (size >= minsize) {
+ its[index].its_size = size / elemsize;
+ index++;
+ }
+ }
+
+ /* then increments of a page, then two pages, etc. */
+
+ for (incrsize = PAGE_SIZE; index < num;) {
+ unsigned int period;
+
+ for (period = 0;
+ (period < 15) && (index < num);
+ period++, size += incrsize) {
+ if (size >= minsize) {
+ its[index].its_size = size / elemsize;
+ index++;
+ }
+ }
+ if (incrsize < (PAGE_SIZE << 3))
+ incrsize <<= 1;
+ }
+}
+
+void
+ipc_table_init(void)
+{
+ ipc_table_dnrequests = (ipc_table_size_t)
+ kalloc(sizeof(struct ipc_table_size) *
+ ipc_table_dnrequests_size);
+ assert(ipc_table_dnrequests != ITS_NULL);
+
+ ipc_table_fill(ipc_table_dnrequests, ipc_table_dnrequests_size - 1,
+ 2, sizeof(struct ipc_port_request));
+
+ /* the last element should have zero size */
+
+ ipc_table_dnrequests[ipc_table_dnrequests_size - 1].its_size = 0;
+}
+
+/*
+ * Routine: ipc_table_alloc
+ * Purpose:
+ * Allocate a table.
+ * Conditions:
+ * May block.
+ */
+
+vm_offset_t
+ipc_table_alloc(
+ vm_size_t size)
+{
+ return kalloc(size);
+}
+
+/*
+ * Routine: ipc_table_free
+ * Purpose:
+ * Free a table allocated with ipc_table_alloc or
+ * ipc_table_realloc.
+ * Conditions:
+ * May block.
+ */
+
+void
+ipc_table_free(
+ vm_size_t size,
+ vm_offset_t table)
+{
+ kfree(table, size);
+}
diff --git a/ipc/ipc_table.h b/ipc/ipc_table.h
new file mode 100644
index 0000000..7968e6b
--- /dev/null
+++ b/ipc/ipc_table.h
@@ -0,0 +1,101 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_table.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for tables, used for dead-name requests
+ * (ipc_port_request_t).
+ */
+
+#ifndef _IPC_IPC_TABLE_H_
+#define _IPC_IPC_TABLE_H_
+
+#include <mach/boolean.h>
+#include <mach/vm_param.h>
+
+/*
+ * Every its_size value must must be a power of two.
+ *
+ * The ipr_size field of the first element in a table of
+ * dead-name requests (ipc_port_request_t) points to the
+ * ipc_table_size structure. The structures must be elements
+ * of ipc_table_dnrequests. ipc_table_dnrequests must end
+ * with an element with zero its_size, and except for this last
+ * element, the its_size values must be strictly increasing.
+ *
+ * The ipr_size field points to the currently used ipc_table_size.
+ */
+
+typedef unsigned int ipc_table_index_t; /* index into tables */
+typedef unsigned int ipc_table_elems_t; /* size of tables */
+
+typedef struct ipc_table_size {
+ ipc_table_elems_t its_size; /* number of elements in table */
+} *ipc_table_size_t;
+
+#define ITS_NULL ((ipc_table_size_t) 0)
+
+extern ipc_table_size_t ipc_table_dnrequests;
+
+extern void
+ipc_table_init(void);
+
+/*
+ * Note that ipc_table_alloc, and ipc_table_free all potentially
+ * use the VM system. Hence simple locks can't be held across
+ * them.
+ */
+
+/* Allocate a table */
+extern vm_offset_t ipc_table_alloc(
+ vm_size_t size);
+
+/* Free a table */
+extern void ipc_table_free(
+ vm_size_t size,
+ vm_offset_t table);
+
+void ipc_table_fill(
+ ipc_table_size_t its,
+ unsigned int num,
+ unsigned int min,
+ vm_size_t elemsize);
+
+#define it_dnrequests_alloc(its) \
+ ((ipc_port_request_t) \
+ ipc_table_alloc((its)->its_size * \
+ sizeof(struct ipc_port_request)))
+
+#define it_dnrequests_free(its, table) \
+ ipc_table_free((its)->its_size * \
+ sizeof(struct ipc_port_request), \
+ (vm_offset_t)(table))
+
+#endif /* _IPC_IPC_TABLE_H_ */
diff --git a/ipc/ipc_target.c b/ipc/ipc_target.c
new file mode 100644
index 0000000..94c5d40
--- /dev/null
+++ b/ipc/ipc_target.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL). All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ */
+/*
+ * File: ipc_target.c
+ *
+ * Implementation for common part of IPC ports and port sets
+ * representing a target of messages and migrating RPCs.
+ */
+
+#include <kern/sched_prim.h>
+#include "ipc_target.h"
+
+void
+ipc_target_init(struct ipc_target *ipt, mach_port_name_t name)
+{
+ ipt->ipt_name = name;
+ ipc_mqueue_init(&ipt->ipt_messages);
+
+#ifdef MIGRATING_THREADS
+ ipt->ipt_type = IPT_TYPE_MESSAGE_RPC;
+ ipt->ipt_acts = 0;
+
+ ipc_target_machine_init(ipt);
+#endif
+}
+
+void
+ipc_target_terminate(struct ipc_target *ipt)
+{
+}
+
+#ifdef MIGRATING_THREADS
+struct Act *
+ipc_target_block(struct ipc_target *ipt)
+{
+ struct Act *act;
+
+ ipt_lock(ipt);
+ while ((act = ipt->ipt_acts) == 0) {
+ /* XXX mp unsafe */
+ ipt->ipt_waiting = 1;
+ ipt_unlock(ipt);
+ thread_wait((int)&ipt->ipt_acts, FALSE);
+ ipt_lock(ipt);
+ }
+ ipt->ipt_acts = act->ipt_next;
+ ipt_unlock(ipt);
+
+ return act;
+}
+
+void
+ipc_target_wakeup(struct ipc_target *ipt)
+{
+ ipt_lock(ipt);
+ if (ipt->ipt_waiting) {
+ thread_wakeup((int)&ipt->ipt_acts);
+ ipt->ipt_waiting = 0;
+ }
+ ipt_unlock(ipt);
+}
+#endif /* MIGRATING_THREADS */
+
diff --git a/ipc/ipc_target.h b/ipc/ipc_target.h
new file mode 100644
index 0000000..c2cc924
--- /dev/null
+++ b/ipc/ipc_target.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL). All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ */
+/*
+ * File: ipc_target.h
+ *
+ * Common part of IPC ports and port sets
+ * representing a target of messages and migrating RPCs.
+ */
+
+#ifndef _IPC_IPC_RECEIVER_H_
+#define _IPC_IPC_RECEIVER_H_
+
+#include "ipc_mqueue.h"
+#include "ipc_object.h"
+
+typedef struct ipc_target {
+
+ struct ipc_object ipt_object;
+
+ mach_port_name_t ipt_name;
+ struct ipc_mqueue ipt_messages;
+
+#ifdef MIGRATING_THREADS
+ /*** Migrating RPC stuff ***/
+
+ int ipt_type;
+
+ /* User entry info for migrating RPC */
+ rpc_info_t ipt_rpcinfo;
+
+ /* List of available activations, all active but not in use. */
+ struct Act *ipt_acts;
+
+ /* TRUE if someone is waiting for an activation from this pool. */
+ int ipt_waiting;
+#endif /* MIGRATING_THREADS */
+
+} *ipc_target_t;
+
+#define IPT_TYPE_MESSAGE_RPC 1
+#define IPT_TYPE_MIGRATE_RPC 2
+
+void ipc_target_init(struct ipc_target *ipt, mach_port_name_t name);
+void ipc_target_terminate(struct ipc_target *ipt);
+
+#define ipt_lock(ipt) io_lock(&(ipt)->ipt_object)
+#define ipt_unlock(ipt) io_unlock(&(ipt)->ipt_object)
+#define ipt_reference(ipt) io_reference(&(ipt)->ipt_object)
+#define ipt_release(ipt) io_release(&(ipt)->ipt_object)
+#define ipt_check_unlock(ipt) io_check_unlock(&(ipt)->ipt_object)
+
+#endif /* _IPC_IPC_RECEIVER_H_ */
diff --git a/ipc/ipc_thread.c b/ipc/ipc_thread.c
new file mode 100644
index 0000000..1e738a5
--- /dev/null
+++ b/ipc/ipc_thread.c
@@ -0,0 +1,107 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_thread.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * IPC operations on threads.
+ */
+
+#include <kern/assert.h>
+#include <ipc/ipc_thread.h>
+
+/*
+ * Routine: ipc_thread_enqueue
+ * Purpose:
+ * Enqueue a thread.
+ */
+
+void
+ipc_thread_enqueue(
+ ipc_thread_queue_t queue,
+ ipc_thread_t thread)
+{
+ ipc_thread_enqueue_macro(queue, thread);
+}
+
+/*
+ * Routine: ipc_thread_dequeue
+ * Purpose:
+ * Dequeue and return a thread.
+ */
+
+ipc_thread_t
+ipc_thread_dequeue(
+ ipc_thread_queue_t queue)
+{
+ ipc_thread_t first;
+
+ first = ipc_thread_queue_first(queue);
+
+ if (first != ITH_NULL)
+ ipc_thread_rmqueue_first_macro(queue, first);
+
+ return first;
+}
+
+/*
+ * Routine: ipc_thread_rmqueue
+ * Purpose:
+ * Pull a thread out of a queue.
+ */
+
+void
+ipc_thread_rmqueue(
+ ipc_thread_queue_t queue,
+ ipc_thread_t thread)
+{
+ ipc_thread_t next, prev;
+
+ assert(queue->ithq_base != ITH_NULL);
+
+ next = thread->ith_next;
+ prev = thread->ith_prev;
+
+ if (next == thread) {
+ assert(prev == thread);
+ assert(queue->ithq_base == thread);
+
+ queue->ithq_base = ITH_NULL;
+ } else {
+ if (queue->ithq_base == thread)
+ queue->ithq_base = next;
+
+ next->ith_prev = prev;
+ prev->ith_next = next;
+ ipc_thread_links_init(thread);
+ }
+}
diff --git a/ipc/ipc_thread.h b/ipc/ipc_thread.h
new file mode 100644
index 0000000..008ab4a
--- /dev/null
+++ b/ipc/ipc_thread.h
@@ -0,0 +1,129 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/ipc_thread.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Definitions for the IPC component of threads.
+ */
+
+#ifndef _IPC_IPC_THREAD_H_
+#define _IPC_IPC_THREAD_H_
+
+#include <kern/thread.h>
+
+typedef thread_t ipc_thread_t;
+
+#define ITH_NULL THREAD_NULL
+
+#define ith_lock_init(thread) simple_lock_init(&(thread)->ith_lock_data)
+#define ith_lock(thread) simple_lock(&(thread)->ith_lock_data)
+#define ith_unlock(thread) simple_unlock(&(thread)->ith_lock_data)
+
+/*
+ * Note that this isn't a queue, but rather a stack. This causes
+ * threads that were recently running to be reused earlier, which
+ * helps improve locality of reference.
+ */
+typedef struct ipc_thread_queue {
+ ipc_thread_t ithq_base;
+} *ipc_thread_queue_t;
+
+#define ITHQ_NULL ((ipc_thread_queue_t) 0)
+
+
+#define ipc_thread_links_init(thread) \
+MACRO_BEGIN \
+ (thread)->ith_next = (thread); \
+ (thread)->ith_prev = (thread); \
+MACRO_END
+
+#define ipc_thread_queue_init(queue) \
+MACRO_BEGIN \
+ (queue)->ithq_base = ITH_NULL; \
+MACRO_END
+
+#define ipc_thread_queue_empty(queue) ((queue)->ithq_base == ITH_NULL)
+
+#define ipc_thread_queue_first(queue) ((queue)->ithq_base)
+
+#define ipc_thread_rmqueue_first_macro(queue, thread) \
+MACRO_BEGIN \
+ ipc_thread_t _next; \
+ \
+ assert((queue)->ithq_base == (thread)); \
+ \
+ _next = (thread)->ith_next; \
+ if (_next == (thread)) { \
+ assert((thread)->ith_prev == (thread)); \
+ (queue)->ithq_base = ITH_NULL; \
+ } else { \
+ ipc_thread_t _prev = (thread)->ith_prev; \
+ \
+ (queue)->ithq_base = _next; \
+ _next->ith_prev = _prev; \
+ _prev->ith_next = _next; \
+ ipc_thread_links_init(thread); \
+ } \
+MACRO_END
+
+#define ipc_thread_enqueue_macro(queue, thread) \
+MACRO_BEGIN \
+ ipc_thread_t _first = (queue)->ithq_base; \
+ \
+ if (_first == ITH_NULL) { \
+ (queue)->ithq_base = (thread); \
+ assert((thread)->ith_next == (thread)); \
+ assert((thread)->ith_prev == (thread)); \
+ } else { \
+ ipc_thread_t _last = _first->ith_prev; \
+ \
+ (thread)->ith_next = _first; \
+ (thread)->ith_prev = _last; \
+ _first->ith_prev = (thread); \
+ _last->ith_next = (thread); \
+ (queue)->ithq_base = (thread); \
+ } \
+MACRO_END
+
+/* Enqueue a thread on a message queue */
+extern void ipc_thread_enqueue(
+ ipc_thread_queue_t queue,
+ ipc_thread_t thread);
+
+/* Dequeue a thread from a message queue */
+extern ipc_thread_t ipc_thread_dequeue(
+ ipc_thread_queue_t queue);
+
+/* Remove a thread from a message queue */
+extern void ipc_thread_rmqueue(
+ ipc_thread_queue_t queue,
+ ipc_thread_t thread);
+
+#endif /* _IPC_IPC_THREAD_H_ */
diff --git a/ipc/ipc_types.h b/ipc/ipc_types.h
new file mode 100644
index 0000000..c8f0d0b
--- /dev/null
+++ b/ipc/ipc_types.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 1995, 1994, 1993, 1992, 1991, 1990
+ * Open Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of ("OSF") or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OSF BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * ACTION OF CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE
+ */
+/*
+ * OSF Research Institute MK6.1 (unencumbered) 1/31/1995
+ */
+
+#ifndef _IPC_TYPES_H_
+#define _IPC_TYPES_H_
+
+typedef struct ipc_space *ipc_space_t;
+typedef struct ipc_port *ipc_port_t;
+
+#endif /* _IPC_TYPES_H_ */
diff --git a/ipc/mach_debug.c b/ipc/mach_debug.c
new file mode 100644
index 0000000..7dca4b6
--- /dev/null
+++ b/ipc/mach_debug.c
@@ -0,0 +1,288 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/mach_debug.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Exported kernel calls. See mach_debug/mach_debug.defs.
+ */
+
+#include <string.h>
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/machine/vm_types.h>
+#include <mach/vm_param.h>
+#include <mach_debug/hash_info.h>
+#include <kern/host.h>
+#include <kern/mach_debug.server.h>
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_marequest.h>
+#include <ipc/ipc_table.h>
+#include <ipc/ipc_right.h>
+
+
+
+/*
+ * Routine: mach_port_get_srights [kernel call]
+ * Purpose:
+ * Retrieve the number of extant send rights
+ * that a receive right has.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Retrieved number of send rights.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ */
+
+kern_return_t
+mach_port_get_srights(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_rights_t *srightsp)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+ mach_port_rights_t srights;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ srights = port->ip_srights;
+ ip_unlock(port);
+
+ *srightsp = srights;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: host_ipc_marequest_info
+ * Purpose:
+ * Return information about the marequest hash table.
+ * Conditions:
+ * Nothing locked. Obeys CountInOut protocol.
+ * Returns:
+ * KERN_SUCCESS Returned information.
+ * KERN_INVALID_HOST The host is null.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+host_ipc_marequest_info(
+ host_t host,
+ unsigned int *maxp,
+ hash_info_bucket_array_t *infop,
+ unsigned int *countp)
+{
+ vm_offset_t addr;
+ vm_size_t size = 0; /* '=0' to shut up lint */
+ hash_info_bucket_t *info;
+ unsigned int potential, actual;
+ kern_return_t kr;
+
+ if (host == HOST_NULL)
+ return KERN_INVALID_HOST;
+
+ /* start with in-line data */
+
+ info = *infop;
+ potential = *countp;
+
+ for (;;) {
+ actual = ipc_marequest_info(maxp, info, potential);
+ if (actual <= potential)
+ break;
+
+ /* allocate more memory */
+
+ if (info != *infop)
+ kmem_free(ipc_kernel_map, addr, size);
+
+ size = round_page(actual * sizeof *info);
+ kr = kmem_alloc_pageable(ipc_kernel_map, &addr, size);
+ if (kr != KERN_SUCCESS)
+ return KERN_RESOURCE_SHORTAGE;
+
+ info = (hash_info_bucket_t *) addr;
+ potential = size/sizeof *info;
+ }
+
+ if (info == *infop) {
+ /* data fit in-line; nothing to deallocate */
+
+ *countp = actual;
+ } else if (actual == 0) {
+ kmem_free(ipc_kernel_map, addr, size);
+
+ *countp = 0;
+ } else {
+ vm_map_copy_t copy;
+ vm_size_t used;
+
+ used = round_page(actual * sizeof *info);
+
+ if (used != size)
+ kmem_free(ipc_kernel_map, addr + used, size - used);
+
+ kr = vm_map_copyin(ipc_kernel_map, addr, used,
+ TRUE, &copy);
+ assert(kr == KERN_SUCCESS);
+
+ *infop = (hash_info_bucket_t *) copy;
+ *countp = actual;
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_dnrequest_info
+ * Purpose:
+ * Returns information about the dead-name requests
+ * registered with the named receive right.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Retrieved information.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ */
+
+kern_return_t
+mach_port_dnrequest_info(
+ ipc_space_t space,
+ mach_port_name_t name,
+ unsigned int *totalp,
+ unsigned int *usedp)
+{
+ unsigned int total, used;
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ if (port->ip_dnrequests == IPR_NULL) {
+ total = 0;
+ used = 0;
+ } else {
+ ipc_port_request_t dnrequests = port->ip_dnrequests;
+ ipc_port_request_index_t index;
+
+ total = dnrequests->ipr_size->its_size;
+
+ for (index = 1, used = 0;
+ index < total; index++) {
+ ipc_port_request_t ipr = &dnrequests[index];
+
+ if (ipr->ipr_name != MACH_PORT_NULL)
+ used++;
+ }
+ }
+ ip_unlock(port);
+
+ *totalp = total;
+ *usedp = used;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_kernel_object [kernel call]
+ * Purpose:
+ * Retrieve the type and address of the kernel object
+ * represented by a send or receive right.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Retrieved kernel object info.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote
+ * send or receive rights.
+ */
+
+kern_return_t
+mach_port_kernel_object(
+ ipc_space_t space,
+ mach_port_name_t name,
+ unsigned int *typep,
+ vm_offset_t *addrp)
+{
+ ipc_entry_t entry;
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_right_lookup_read(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is read-locked and active */
+
+ if ((entry->ie_bits & MACH_PORT_TYPE_SEND_RECEIVE) == 0) {
+ is_read_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ is_read_unlock(space);
+
+ if (!ip_active(port)) {
+ ip_unlock(port);
+ return KERN_INVALID_RIGHT;
+ }
+
+ *typep = ip_kotype(port);
+ *addrp = port->ip_kobject;
+ ip_unlock(port);
+ return KERN_SUCCESS;
+}
diff --git a/ipc/mach_msg.c b/ipc/mach_msg.c
new file mode 100644
index 0000000..6194ef7
--- /dev/null
+++ b/ipc/mach_msg.c
@@ -0,0 +1,1709 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/mach_msg.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Exported message traps. See mach/message.h.
+ */
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
+#include <mach/message.h>
+#include <machine/copy_user.h>
+#include <kern/assert.h>
+#include <kern/counters.h>
+#include <kern/debug.h>
+#include <kern/lock.h>
+#include <kern/printf.h>
+#include <kern/sched_prim.h>
+#include <kern/ipc_sched.h>
+#include <kern/exception.h>
+#include <vm/vm_map.h>
+#include <ipc/ipc_kmsg.h>
+#include <ipc/ipc_marequest.h>
+#include <ipc/ipc_mqueue.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_notify.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_thread.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/mach_msg.h>
+#include <machine/locore.h>
+#include <machine/pcb.h>
+
+/*
+ * Routine: mach_msg_send
+ * Purpose:
+ * Send a message.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Sent the message.
+ * MACH_SEND_MSG_TOO_SMALL Message smaller than a header.
+ * MACH_SEND_NO_BUFFER Couldn't allocate buffer.
+ * MACH_SEND_INVALID_DATA Couldn't copy message data.
+ * MACH_SEND_INVALID_HEADER
+ * Illegal value in the message header bits.
+ * MACH_SEND_INVALID_DEST The space is dead.
+ * MACH_SEND_INVALID_NOTIFY Bad notify port.
+ * MACH_SEND_INVALID_DEST Can't copyin destination port.
+ * MACH_SEND_INVALID_REPLY Can't copyin reply port.
+ * MACH_SEND_TIMED_OUT Timeout expired without delivery.
+ * MACH_SEND_INTERRUPTED Delivery interrupted.
+ * MACH_SEND_NO_NOTIFY Can't allocate a msg-accepted request.
+ * MACH_SEND_WILL_NOTIFY Msg-accepted notif. requested.
+ * MACH_SEND_NOTIFY_IN_PROGRESS
+ * This space has already forced a message to this port.
+ */
+
+mach_msg_return_t
+mach_msg_send(
+ mach_msg_user_header_t *msg,
+ mach_msg_option_t option,
+ mach_msg_size_t send_size,
+ mach_msg_timeout_t time_out,
+ mach_port_name_t notify)
+{
+ ipc_space_t space = current_space();
+ vm_map_t map = current_map();
+ ipc_kmsg_t kmsg;
+ mach_msg_return_t mr;
+
+ mr = ipc_kmsg_get(msg, send_size, &kmsg);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+
+ if (option & MACH_SEND_CANCEL) {
+ if (notify == MACH_PORT_NULL)
+ mr = MACH_SEND_INVALID_NOTIFY;
+ else
+ mr = ipc_kmsg_copyin(kmsg, space, map, notify);
+ } else
+ mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ ikm_free(kmsg);
+ return mr;
+ }
+
+ if (option & MACH_SEND_NOTIFY) {
+ mr = ipc_mqueue_send(kmsg, MACH_SEND_TIMEOUT,
+ ((option & MACH_SEND_TIMEOUT) ?
+ time_out : MACH_MSG_TIMEOUT_NONE));
+ if (mr == MACH_SEND_TIMED_OUT) {
+ ipc_port_t dest = (ipc_port_t)
+ kmsg->ikm_header.msgh_remote_port;
+
+ if (notify == MACH_PORT_NULL)
+ mr = MACH_SEND_INVALID_NOTIFY;
+ else
+ mr = ipc_marequest_create(space, dest,
+ notify, &kmsg->ikm_marequest);
+ if (mr == MACH_MSG_SUCCESS) {
+ ipc_mqueue_send_always(kmsg);
+ return MACH_SEND_WILL_NOTIFY;
+ }
+ }
+ } else
+ mr = ipc_mqueue_send(kmsg, option & MACH_SEND_TIMEOUT,
+ time_out);
+
+ if (mr != MACH_MSG_SUCCESS) {
+ mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map);
+
+ assert(kmsg->ikm_marequest == IMAR_NULL);
+ (void) ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
+ }
+
+ return mr;
+}
+
+/*
+ * Routine: mach_msg_receive
+ * Purpose:
+ * Receive a message.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * MACH_MSG_SUCCESS Received a message.
+ * MACH_RCV_INVALID_NAME The name doesn't denote a right,
+ * or the denoted right is not receive or port set.
+ * MACH_RCV_IN_SET Receive right is a member of a set.
+ * MACH_RCV_TOO_LARGE Message wouldn't fit into buffer.
+ * MACH_RCV_TIMED_OUT Timeout expired without a message.
+ * MACH_RCV_INTERRUPTED Reception interrupted.
+ * MACH_RCV_PORT_DIED Port/set died while receiving.
+ * MACH_RCV_PORT_CHANGED Port moved into set while receiving.
+ * MACH_RCV_INVALID_DATA Couldn't copy to user buffer.
+ * MACH_RCV_INVALID_NOTIFY Bad notify port.
+ * MACH_RCV_HEADER_ERROR
+ */
+
+mach_msg_return_t
+mach_msg_receive(
+ mach_msg_user_header_t *msg,
+ mach_msg_option_t option,
+ mach_msg_size_t rcv_size,
+ mach_port_name_t rcv_name,
+ mach_msg_timeout_t time_out,
+ mach_port_name_t notify)
+{
+ ipc_thread_t self = current_thread();
+ ipc_space_t space = current_space();
+ vm_map_t map = current_map();
+ ipc_object_t object;
+ ipc_mqueue_t mqueue;
+ ipc_kmsg_t kmsg;
+ mach_port_seqno_t seqno;
+ mach_msg_return_t mr;
+
+ mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+ /* hold ref for object; mqueue is locked */
+
+ /*
+ * ipc_mqueue_receive may not return, because if we block
+ * then our kernel stack may be discarded. So we save
+ * state here for mach_msg_receive_continue to pick up.
+ */
+
+ self->ith_msg = msg;
+ self->ith_option = option;
+ self->ith_rcv_size = rcv_size;
+ self->ith_timeout = time_out;
+ self->ith_notify = notify;
+ self->ith_object = object;
+ self->ith_mqueue = mqueue;
+
+ if (option & MACH_RCV_LARGE) {
+ mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
+ rcv_size, time_out,
+ FALSE, mach_msg_receive_continue,
+ &kmsg, &seqno);
+ /* mqueue is unlocked */
+ ipc_object_release(object);
+ if (mr != MACH_MSG_SUCCESS) {
+ if (mr == MACH_RCV_TOO_LARGE) {
+ mach_msg_size_t real_size =
+ (mach_msg_size_t) (vm_offset_t) kmsg;
+
+ assert(real_size > rcv_size);
+
+ (void) copyout(&real_size,
+ &msg->msgh_size,
+ sizeof(mach_msg_size_t));
+ }
+
+ return mr;
+ }
+
+ kmsg->ikm_header.msgh_seqno = seqno;
+ assert(kmsg->ikm_header.msgh_size <= rcv_size);
+ } else {
+ mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
+ MACH_MSG_SIZE_MAX, time_out,
+ FALSE, mach_msg_receive_continue,
+ &kmsg, &seqno);
+ /* mqueue is unlocked */
+ ipc_object_release(object);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+
+ kmsg->ikm_header.msgh_seqno = seqno;
+ if (msg_usize(&kmsg->ikm_header) > rcv_size) {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ return MACH_RCV_TOO_LARGE;
+ }
+ }
+
+ if (option & MACH_RCV_NOTIFY) {
+ if (notify == MACH_PORT_NULL)
+ mr = MACH_RCV_INVALID_NOTIFY;
+ else
+ mr = ipc_kmsg_copyout(kmsg, space, map, notify);
+ } else
+ mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ } else {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ }
+
+ return mr;
+ }
+
+ return ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
+}
+
+/*
+ * Routine: mach_msg_receive_continue
+ * Purpose:
+ * Continue after blocking for a message.
+ * Conditions:
+ * Nothing locked. We are running on a new kernel stack,
+ * with the receive state saved in the thread. From here
+ * control goes back to user space.
+ */
+
+void
+mach_msg_receive_continue(void)
+{
+ ipc_thread_t self = current_thread();
+ ipc_space_t space = current_space();
+ vm_map_t map = current_map();
+ mach_msg_user_header_t *msg = self->ith_msg;
+ mach_msg_option_t option = self->ith_option;
+ mach_msg_size_t rcv_size = self->ith_rcv_size;
+ mach_msg_timeout_t time_out = self->ith_timeout;
+ mach_port_name_t notify = self->ith_notify;
+ ipc_object_t object = self->ith_object;
+ ipc_mqueue_t mqueue = self->ith_mqueue;
+ ipc_kmsg_t kmsg;
+ mach_port_seqno_t seqno;
+ mach_msg_return_t mr;
+
+ if (option & MACH_RCV_LARGE) {
+ mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
+ rcv_size, time_out,
+ TRUE, mach_msg_receive_continue,
+ &kmsg, &seqno);
+ /* mqueue is unlocked */
+ ipc_object_release(object);
+ if (mr != MACH_MSG_SUCCESS) {
+ if (mr == MACH_RCV_TOO_LARGE) {
+ mach_msg_size_t real_size =
+ (mach_msg_size_t) (vm_offset_t) kmsg;
+
+ assert(real_size > rcv_size);
+
+ (void) copyout(&real_size,
+ &msg->msgh_size,
+ sizeof(mach_msg_size_t));
+ }
+
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ kmsg->ikm_header.msgh_seqno = seqno;
+ assert(msg_usize(&kmsg->ikm_header) <= rcv_size);
+ } else {
+ mr = ipc_mqueue_receive(mqueue, option & MACH_RCV_TIMEOUT,
+ MACH_MSG_SIZE_MAX, time_out,
+ TRUE, mach_msg_receive_continue,
+ &kmsg, &seqno);
+ /* mqueue is unlocked */
+ ipc_object_release(object);
+ if (mr != MACH_MSG_SUCCESS) {
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ kmsg->ikm_header.msgh_seqno = seqno;
+ if (msg_usize(&kmsg->ikm_header) > rcv_size) {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ thread_syscall_return(MACH_RCV_TOO_LARGE);
+ /*NOTREACHED*/
+ }
+ }
+
+ if (option & MACH_RCV_NOTIFY) {
+ if (notify == MACH_PORT_NULL)
+ mr = MACH_RCV_INVALID_NOTIFY;
+ else
+ mr = ipc_kmsg_copyout(kmsg, space, map, notify);
+ } else
+ mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ } else {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ }
+
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ mr = ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+}
+
+/*
+ * Routine: mach_msg_trap [mach trap]
+ * Purpose:
+ * Possibly send a message; possibly receive a message.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * All of mach_msg_send and mach_msg_receive error codes.
+ */
+
+mach_msg_return_t
+mach_msg_trap(
+ mach_msg_user_header_t *msg,
+ mach_msg_option_t option,
+ mach_msg_size_t send_size,
+ mach_msg_size_t rcv_size,
+ mach_port_name_t rcv_name,
+ mach_msg_timeout_t time_out,
+ mach_port_name_t notify)
+{
+ mach_msg_return_t mr;
+
+ /* first check for common cases */
+
+ if (option == (MACH_SEND_MSG|MACH_RCV_MSG)) {
+ ipc_thread_t self = current_thread();
+ ipc_space_t space = self->task->itk_space;
+ ipc_kmsg_t kmsg;
+ ipc_port_t dest_port;
+ ipc_object_t rcv_object;
+ ipc_mqueue_t rcv_mqueue;
+ mach_msg_size_t reply_size;
+
+ /*
+ * This case is divided into ten sections, each
+ * with a label. There are five optimized
+ * sections and six unoptimized sections, which
+ * do the same thing but handle all possible
+ * cases and are slower.
+ *
+ * The five sections for an RPC are
+ * 1) Get request message into a buffer.
+ * (fast_get or slow_get)
+ * 2) Copyin request message and rcv_name.
+ * (fast_copyin or slow_copyin)
+ * 3) Enqueue request and dequeue reply.
+ * (fast_send_receive or
+ * slow_send and slow_receive)
+ * 4) Copyout reply message.
+ * (fast_copyout or slow_copyout)
+ * 5) Put reply message to user's buffer.
+ * (fast_put or slow_put)
+ *
+ * Keep the locking hierarchy firmly in mind.
+ * (First spaces, then ports, then port sets,
+ * then message queues.) Only a non-blocking
+ * attempt can be made to acquire locks out of
+ * order, or acquire two locks on the same level.
+ * Acquiring two locks on the same level will
+ * fail if the objects are really the same,
+ * unless simple locking is disabled. This is OK,
+ * because then the extra unlock does nothing.
+ *
+ * There are two major reasons these RPCs can't use
+ * ipc_thread_switch, and use slow_send/slow_receive:
+ * 1) Kernel RPCs.
+ * 2) Servers fall behind clients, so
+ * client doesn't find a blocked server thread and
+ * server finds waiting messages and can't block.
+ */
+
+ /*
+ fast_get:
+ */
+ /*
+ * optimized ipc_kmsg_get
+ *
+ * No locks, references, or messages held.
+ * We must clear ikm_cache before copyinmsg.
+ */
+
+ if (((send_size * IKM_EXPAND_FACTOR) > IKM_SAVED_MSG_SIZE) ||
+ (send_size < sizeof(mach_msg_user_header_t)) ||
+ (send_size & 3))
+ goto slow_get;
+
+ kmsg = ikm_cache_alloc_try();
+ if (kmsg == IKM_NULL)
+ goto slow_get;
+
+ if (copyinmsg(msg, &kmsg->ikm_header,
+ send_size, kmsg->ikm_size)) {
+ ikm_free(kmsg);
+ goto slow_get;
+ }
+
+ fast_copyin:
+ /*
+ * optimized ipc_kmsg_copyin/ipc_mqueue_copyin
+ *
+ * We have the request message data in kmsg.
+ * Must still do copyin, send, receive, etc.
+ *
+ * If the message isn't simple, we can't combine
+ * ipc_kmsg_copyin_header and ipc_mqueue_copyin,
+ * because copyin of the message body might
+ * affect rcv_name.
+ */
+
+ switch (kmsg->ikm_header.msgh_bits) {
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,
+ MACH_MSG_TYPE_MAKE_SEND_ONCE): {
+ ipc_port_t reply_port;
+ {
+ mach_port_name_t reply_name =
+ kmsg->ikm_header.msgh_local_port;
+
+ if (reply_name != rcv_name)
+ goto slow_copyin;
+
+ is_read_lock(space);
+ assert(space->is_active);
+
+ ipc_entry_t entry;
+ entry = ipc_entry_lookup (space, reply_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, reply_name);
+ goto abort_request_copyin;
+ }
+ reply_port = (ipc_port_t) entry->ie_object;
+ assert(reply_port != IP_NULL);
+ }
+
+ {
+ mach_port_name_t dest_name =
+ kmsg->ikm_header.msgh_remote_port;
+
+ ipc_entry_t entry;
+ ipc_entry_bits_t bits;
+ entry = ipc_entry_lookup (space, dest_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, dest_name);
+ goto abort_request_copyin;
+ }
+ bits = entry->ie_bits;
+
+ /* check type bits */
+ if (IE_BITS_TYPE (bits) != MACH_PORT_TYPE_SEND)
+ goto abort_request_copyin;
+
+ assert(IE_BITS_UREFS(bits) > 0);
+
+ dest_port = (ipc_port_t) entry->ie_object;
+ assert(dest_port != IP_NULL);
+ }
+
+ /*
+ * To do an atomic copyin, need simultaneous
+ * locks on both ports and the space. If
+ * dest_port == reply_port, and simple locking is
+ * enabled, then we will abort. Otherwise it's
+ * OK to unlock twice.
+ */
+
+ ip_lock(dest_port);
+ if (!ip_active(dest_port) ||
+ !ip_lock_try(reply_port)) {
+ ip_unlock(dest_port);
+ goto abort_request_copyin;
+ }
+ is_read_unlock(space);
+
+ assert(dest_port->ip_srights > 0);
+ dest_port->ip_srights++;
+ ip_reference(dest_port);
+
+ assert(ip_active(reply_port));
+ assert(reply_port->ip_receiver_name ==
+ kmsg->ikm_header.msgh_local_port);
+ assert(reply_port->ip_receiver == space);
+
+ reply_port->ip_sorights++;
+ ip_reference(reply_port);
+
+ kmsg->ikm_header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
+ MACH_MSG_TYPE_PORT_SEND_ONCE);
+ kmsg->ikm_header.msgh_remote_port =
+ (mach_port_t) dest_port;
+ kmsg->ikm_header.msgh_local_port =
+ (mach_port_t) reply_port;
+
+ /* make sure we can queue to the destination */
+
+ if (dest_port->ip_receiver == ipc_space_kernel) {
+ /*
+ * The kernel server has a reference to
+ * the reply port, which it hands back
+ * to us in the reply message. We do
+ * not need to keep another reference to
+ * it.
+ */
+ ip_unlock(reply_port);
+
+ assert(ip_active(dest_port));
+ ip_unlock(dest_port);
+ goto kernel_send;
+ }
+
+ if (dest_port->ip_msgcount >= dest_port->ip_qlimit)
+ goto abort_request_send_receive;
+
+ /* optimized ipc_mqueue_copyin */
+
+ if (reply_port->ip_pset != IPS_NULL)
+ goto abort_request_send_receive;
+
+ rcv_object = (ipc_object_t) reply_port;
+ io_reference(rcv_object);
+ rcv_mqueue = &reply_port->ip_messages;
+ imq_lock(rcv_mqueue);
+ io_unlock(rcv_object);
+ goto fast_send_receive;
+
+ abort_request_copyin:
+ is_read_unlock(space);
+ goto slow_copyin;
+
+ abort_request_send_receive:
+ ip_unlock(dest_port);
+ ip_unlock(reply_port);
+ goto slow_send;
+ }
+
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 0): {
+ /* sending a reply message */
+
+ {
+ mach_port_name_t reply_name =
+ kmsg->ikm_header.msgh_local_port;
+
+ if (reply_name != MACH_PORT_NULL)
+ goto slow_copyin;
+ }
+
+ is_write_lock(space);
+ assert(space->is_active);
+
+ {
+ ipc_entry_t entry;
+ mach_port_name_t dest_name =
+ kmsg->ikm_header.msgh_remote_port;
+
+ entry = ipc_entry_lookup (space, dest_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, dest_name);
+ goto abort_reply_dest_copyin;
+ }
+
+ /* check type bits */
+ if (IE_BITS_TYPE (entry->ie_bits) !=
+ MACH_PORT_TYPE_SEND_ONCE)
+ goto abort_reply_dest_copyin;
+
+ /* optimized ipc_right_copyin */
+
+ assert(IE_BITS_TYPE(entry->ie_bits) ==
+ MACH_PORT_TYPE_SEND_ONCE);
+ assert(IE_BITS_UREFS(entry->ie_bits) == 1);
+ assert((entry->ie_bits & IE_BITS_MAREQUEST) == 0);
+
+ if (entry->ie_request != 0)
+ goto abort_reply_dest_copyin;
+
+ dest_port = (ipc_port_t) entry->ie_object;
+ assert(dest_port != IP_NULL);
+
+ ip_lock(dest_port);
+ if (!ip_active(dest_port)) {
+ ip_unlock(dest_port);
+ goto abort_reply_dest_copyin;
+ }
+
+ assert(dest_port->ip_sorights > 0);
+ entry->ie_object = IO_NULL;
+ ipc_entry_dealloc (space, dest_name, entry);
+ }
+
+ kmsg->ikm_header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
+ 0);
+ kmsg->ikm_header.msgh_remote_port =
+ (mach_port_t) dest_port;
+
+ /* make sure we can queue to the destination */
+
+ assert(dest_port->ip_receiver != ipc_space_kernel);
+
+ /* optimized ipc_mqueue_copyin */
+
+ {
+ ipc_entry_t entry;
+ ipc_entry_bits_t bits;
+ entry = ipc_entry_lookup (space, rcv_name);
+ if (entry == IE_NULL)
+ {
+ ipc_entry_lookup_failed (msg, rcv_name);
+ goto abort_reply_rcv_copyin;
+ }
+ bits = entry->ie_bits;
+
+ /* check type bits; looking for receive or set */
+
+ if (bits & MACH_PORT_TYPE_PORT_SET) {
+ ipc_pset_t rcv_pset;
+
+ rcv_pset = (ipc_pset_t) entry->ie_object;
+ assert(rcv_pset != IPS_NULL);
+
+ ips_lock(rcv_pset);
+ assert(ips_active(rcv_pset));
+
+ rcv_object = (ipc_object_t) rcv_pset;
+ rcv_mqueue = &rcv_pset->ips_messages;
+ } else if (bits & MACH_PORT_TYPE_RECEIVE) {
+ ipc_port_t rcv_port;
+
+ rcv_port = (ipc_port_t) entry->ie_object;
+ assert(rcv_port != IP_NULL);
+
+ if (!ip_lock_try(rcv_port))
+ goto abort_reply_rcv_copyin;
+ assert(ip_active(rcv_port));
+
+ if (rcv_port->ip_pset != IPS_NULL) {
+ ip_unlock(rcv_port);
+ goto abort_reply_rcv_copyin;
+ }
+
+ rcv_object = (ipc_object_t) rcv_port;
+ rcv_mqueue = &rcv_port->ip_messages;
+ } else
+ goto abort_reply_rcv_copyin;
+ }
+
+ is_write_unlock(space);
+ io_reference(rcv_object);
+ imq_lock(rcv_mqueue);
+ io_unlock(rcv_object);
+ goto fast_send_receive;
+
+ abort_reply_dest_copyin:
+ is_write_unlock(space);
+ goto slow_copyin;
+
+ abort_reply_rcv_copyin:
+ ip_unlock(dest_port);
+ is_write_unlock(space);
+ goto slow_send;
+ }
+
+ default:
+ goto slow_copyin;
+ }
+ /*NOTREACHED*/
+
+ fast_send_receive:
+ /*
+ * optimized ipc_mqueue_send/ipc_mqueue_receive
+ *
+ * Finished get/copyin of kmsg and copyin of rcv_name.
+ * space is unlocked, dest_port is locked,
+ * we can queue kmsg to dest_port,
+ * rcv_mqueue is locked, rcv_object holds a ref,
+ * if rcv_object is a port it isn't in a port set
+ *
+ * Note that if simple locking is turned off,
+ * then we could have dest_mqueue == rcv_mqueue
+ * and not abort when we try to lock dest_mqueue.
+ */
+
+ assert(ip_active(dest_port));
+ assert(dest_port->ip_receiver != ipc_space_kernel);
+ assert((dest_port->ip_msgcount < dest_port->ip_qlimit) ||
+ (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
+ MACH_MSG_TYPE_PORT_SEND_ONCE));
+ assert((kmsg->ikm_header.msgh_bits &
+ MACH_MSGH_BITS_CIRCULAR) == 0);
+
+ {
+ ipc_mqueue_t dest_mqueue;
+ ipc_thread_t receiver;
+
+ {
+ ipc_pset_t dest_pset;
+
+ dest_pset = dest_port->ip_pset;
+ if (dest_pset == IPS_NULL)
+ dest_mqueue = &dest_port->ip_messages;
+ else
+ dest_mqueue = &dest_pset->ips_messages;
+ }
+
+ if (!imq_lock_try(dest_mqueue)) {
+ abort_send_receive:
+ ip_unlock(dest_port);
+ imq_unlock(rcv_mqueue);
+ ipc_object_release(rcv_object);
+ goto slow_send;
+ }
+
+ receiver = ipc_thread_queue_first(&dest_mqueue->imq_threads);
+ if ((receiver == ITH_NULL) ||
+ (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages)
+ != IKM_NULL)) {
+ imq_unlock(dest_mqueue);
+ goto abort_send_receive;
+ }
+
+ /*
+ * There is a receiver thread waiting, and
+ * there is no reply message for us to pick up.
+ * We have hope of hand-off, so save state.
+ */
+
+ self->ith_msg = msg;
+ self->ith_rcv_size = rcv_size;
+ self->ith_object = rcv_object;
+ self->ith_mqueue = rcv_mqueue;
+
+ if ((receiver->swap_func == mach_msg_continue) &&
+ thread_handoff(self, mach_msg_continue, receiver)) {
+ assert(current_thread() == receiver);
+
+ /*
+ * We can use the optimized receive code,
+ * because the receiver is using no options.
+ */
+ } else if ((receiver->swap_func ==
+ exception_raise_continue) &&
+ thread_handoff(self, mach_msg_continue, receiver)) {
+ counter(c_mach_msg_trap_block_exc++);
+ assert(current_thread() == receiver);
+
+ /*
+ * We are a reply message coming back through
+ * the optimized exception-handling path.
+ * Finish with rcv_mqueue and dest_mqueue,
+ * and then jump to exception code with
+ * dest_port still locked. We don't bother
+ * with a sequence number in this case.
+ */
+
+ ipc_thread_enqueue_macro(
+ &rcv_mqueue->imq_threads, self);
+ self->ith_state = MACH_RCV_IN_PROGRESS;
+ self->ith_msize = MACH_MSG_SIZE_MAX;
+ imq_unlock(rcv_mqueue);
+
+ ipc_thread_rmqueue_first_macro(
+ &dest_mqueue->imq_threads, receiver);
+ imq_unlock(dest_mqueue);
+
+ exception_raise_continue_fast(dest_port, kmsg);
+ /*NOTREACHED*/
+ return MACH_MSG_SUCCESS;
+ } else if ((send_size <= receiver->ith_msize) &&
+ thread_handoff(self, mach_msg_continue, receiver)) {
+ assert(current_thread() == receiver);
+
+ if ((receiver->swap_func ==
+ mach_msg_receive_continue) &&
+ ((receiver->ith_option & MACH_RCV_NOTIFY) == 0)) {
+ /*
+ * We can still use the optimized code.
+ */
+ } else {
+ counter(c_mach_msg_trap_block_slow++);
+ /*
+ * We are running as the receiver,
+ * but we can't use the optimized code.
+ * Finish send/receive processing.
+ */
+
+ dest_port->ip_msgcount++;
+ ip_unlock(dest_port);
+
+ ipc_thread_enqueue_macro(
+ &rcv_mqueue->imq_threads, self);
+ self->ith_state = MACH_RCV_IN_PROGRESS;
+ self->ith_msize = MACH_MSG_SIZE_MAX;
+ imq_unlock(rcv_mqueue);
+
+ ipc_thread_rmqueue_first_macro(
+ &dest_mqueue->imq_threads, receiver);
+ receiver->ith_state = MACH_MSG_SUCCESS;
+ receiver->ith_kmsg = kmsg;
+ receiver->ith_seqno = dest_port->ip_seqno++;
+ imq_unlock(dest_mqueue);
+
+ /*
+ * Call the receiver's continuation.
+ */
+
+ receiver->wait_result = THREAD_AWAKENED;
+ (*receiver->swap_func)();
+ /*NOTREACHED*/
+ return MACH_MSG_SUCCESS;
+ }
+ } else {
+ /*
+ * The receiver can't accept the message,
+ * or we can't switch to the receiver.
+ */
+
+ imq_unlock(dest_mqueue);
+ goto abort_send_receive;
+ }
+ counter(c_mach_msg_trap_block_fast++);
+
+ /*
+ * Safe to unlock dest_port now that we are
+ * committed to this path, because we hold
+ * dest_mqueue locked. We never bother changing
+ * dest_port->ip_msgcount.
+ */
+
+ ip_unlock(dest_port);
+
+ /*
+ * We need to finish preparing self for its
+ * time asleep in rcv_mqueue.
+ */
+
+ ipc_thread_enqueue_macro(&rcv_mqueue->imq_threads, self);
+ self->ith_state = MACH_RCV_IN_PROGRESS;
+ self->ith_msize = MACH_MSG_SIZE_MAX;
+ imq_unlock(rcv_mqueue);
+
+ /*
+ * Finish extracting receiver from dest_mqueue.
+ */
+
+ ipc_thread_rmqueue_first_macro(
+ &dest_mqueue->imq_threads, receiver);
+ kmsg->ikm_header.msgh_seqno = dest_port->ip_seqno++;
+ imq_unlock(dest_mqueue);
+
+ /*
+ * We don't have to do any post-dequeue processing of
+ * the message. We never incremented ip_msgcount, we
+ * know it has no msg-accepted request, and blocked
+ * senders aren't a worry because we found the port
+ * with a receiver waiting.
+ */
+
+ self = receiver;
+ space = self->task->itk_space;
+
+ msg = self->ith_msg;
+ rcv_size = self->ith_rcv_size;
+ rcv_object = self->ith_object;
+
+ /* inline ipc_object_release */
+ io_lock(rcv_object);
+ io_release(rcv_object);
+ io_check_unlock(rcv_object);
+ }
+
+ fast_copyout:
+ /*
+ * Nothing locked and no references held, except
+ * we have kmsg with msgh_seqno filled in. Must
+ * still check against rcv_size and do
+ * ipc_kmsg_copyout/ipc_kmsg_put.
+ */
+
+ assert((ipc_port_t) kmsg->ikm_header.msgh_remote_port
+ == dest_port);
+
+ reply_size = kmsg->ikm_header.msgh_size;
+ if (rcv_size < msg_usize(&kmsg->ikm_header))
+ goto slow_copyout;
+
+ /* optimized ipc_kmsg_copyout/ipc_kmsg_copyout_header */
+
+ switch (kmsg->ikm_header.msgh_bits) {
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
+ MACH_MSG_TYPE_PORT_SEND_ONCE): {
+ ipc_port_t reply_port =
+ (ipc_port_t) kmsg->ikm_header.msgh_local_port;
+ mach_port_name_t dest_name, reply_name;
+ rpc_uintptr_t payload;
+
+ /* receiving a request message */
+
+ if (!IP_VALID(reply_port))
+ goto slow_copyout;
+
+ is_write_lock(space);
+ assert(space->is_active);
+
+ /*
+ * To do an atomic copyout, need simultaneous
+ * locks on both ports and the space. If
+ * dest_port == reply_port, and simple locking is
+ * enabled, then we will abort. Otherwise it's
+ * OK to unlock twice.
+ */
+
+ ip_lock(dest_port);
+ if (!ip_active(dest_port) ||
+ !ip_lock_try(reply_port))
+ goto abort_request_copyout;
+
+ if (!ip_active(reply_port)) {
+ ip_unlock(reply_port);
+ goto abort_request_copyout;
+ }
+
+ assert(reply_port->ip_sorights > 0);
+ ip_unlock(reply_port);
+
+ {
+ ipc_entry_t entry;
+ kern_return_t kr;
+ kr = ipc_entry_get (space, &reply_name, &entry);
+ if (kr)
+ goto abort_request_copyout;
+ assert (entry != NULL);
+
+ {
+ mach_port_gen_t gen;
+
+ assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0);
+ gen = entry->ie_bits + IE_BITS_GEN_ONE;
+
+ /* optimized ipc_right_copyout */
+
+ entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1);
+ }
+
+ assert(MACH_PORT_NAME_VALID(reply_name));
+ entry->ie_object = (ipc_object_t) reply_port;
+ is_write_unlock(space);
+ }
+
+ /* optimized ipc_object_copyout_dest */
+
+ assert(dest_port->ip_srights > 0);
+ ip_release(dest_port);
+
+ if (dest_port->ip_receiver == space)
+ dest_name = dest_port->ip_receiver_name;
+ else
+ dest_name = MACH_PORT_NULL;
+ payload = dest_port->ip_protected_payload;
+
+ if ((--dest_port->ip_srights == 0) &&
+ (dest_port->ip_nsrequest != IP_NULL)) {
+ ipc_port_t nsrequest;
+ mach_port_mscount_t mscount;
+
+ /* a rather rare case */
+
+ nsrequest = dest_port->ip_nsrequest;
+ mscount = dest_port->ip_mscount;
+ dest_port->ip_nsrequest = IP_NULL;
+ ip_unlock(dest_port);
+
+ ipc_notify_no_senders(nsrequest, mscount);
+ } else
+ ip_unlock(dest_port);
+
+ if (! ipc_port_flag_protected_payload(dest_port)) {
+ kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS(
+ MACH_MSG_TYPE_PORT_SEND_ONCE,
+ MACH_MSG_TYPE_PORT_SEND);
+ kmsg->ikm_header.msgh_local_port = dest_name;
+ } else {
+ kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS(
+ MACH_MSG_TYPE_PORT_SEND_ONCE,
+ MACH_MSG_TYPE_PROTECTED_PAYLOAD);
+ kmsg->ikm_header.msgh_protected_payload =
+ payload;
+ }
+ kmsg->ikm_header.msgh_remote_port = reply_name;
+ goto fast_put;
+
+ abort_request_copyout:
+ ip_unlock(dest_port);
+ is_write_unlock(space);
+ goto slow_copyout;
+ }
+
+ case MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): {
+ mach_port_name_t dest_name;
+ rpc_uintptr_t payload;
+
+ /* receiving a reply message */
+
+ ip_lock(dest_port);
+ if (!ip_active(dest_port))
+ goto slow_copyout;
+
+ /* optimized ipc_object_copyout_dest */
+
+ assert(dest_port->ip_sorights > 0);
+
+ payload = dest_port->ip_protected_payload;
+
+ if (dest_port->ip_receiver == space) {
+ ip_release(dest_port);
+ dest_port->ip_sorights--;
+ dest_name = dest_port->ip_receiver_name;
+ ip_unlock(dest_port);
+ } else {
+ ip_unlock(dest_port);
+
+ ipc_notify_send_once(dest_port);
+ dest_name = MACH_PORT_NULL;
+ }
+
+ if (! ipc_port_flag_protected_payload(dest_port)) {
+ kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS(
+ 0,
+ MACH_MSG_TYPE_PORT_SEND_ONCE);
+ kmsg->ikm_header.msgh_local_port = dest_name;
+ } else {
+ kmsg->ikm_header.msgh_bits = MACH_MSGH_BITS(
+ 0,
+ MACH_MSG_TYPE_PROTECTED_PAYLOAD);
+ kmsg->ikm_header.msgh_protected_payload =
+ payload;
+ }
+ kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
+ goto fast_put;
+ }
+
+ case MACH_MSGH_BITS_COMPLEX|
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0): {
+ mach_port_name_t dest_name;
+ rpc_uintptr_t payload;
+
+ /* receiving a complex reply message */
+
+ ip_lock(dest_port);
+ if (!ip_active(dest_port))
+ goto slow_copyout;
+
+ /* optimized ipc_object_copyout_dest */
+
+ assert(dest_port->ip_sorights > 0);
+
+ payload = dest_port->ip_protected_payload;
+
+ if (dest_port->ip_receiver == space) {
+ ip_release(dest_port);
+ dest_port->ip_sorights--;
+ dest_name = dest_port->ip_receiver_name;
+ ip_unlock(dest_port);
+ } else {
+ ip_unlock(dest_port);
+
+ ipc_notify_send_once(dest_port);
+ dest_name = MACH_PORT_NULL;
+ }
+
+ if (! ipc_port_flag_protected_payload(dest_port)) {
+ kmsg->ikm_header.msgh_bits =
+ MACH_MSGH_BITS_COMPLEX
+ | MACH_MSGH_BITS(
+ 0,
+ MACH_MSG_TYPE_PORT_SEND_ONCE);
+ kmsg->ikm_header.msgh_local_port = dest_name;
+ } else {
+ kmsg->ikm_header.msgh_bits =
+ MACH_MSGH_BITS_COMPLEX
+ | MACH_MSGH_BITS(
+ 0,
+ MACH_MSG_TYPE_PROTECTED_PAYLOAD);
+ kmsg->ikm_header.msgh_protected_payload =
+ payload;
+ }
+ kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
+
+ mr = ipc_kmsg_copyout_body(
+ kmsg,
+ space,
+ current_map());
+
+ if (mr != MACH_MSG_SUCCESS) {
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ return mr | MACH_RCV_BODY_ERROR;
+ }
+ goto fast_put;
+ }
+
+ default:
+ goto slow_copyout;
+ }
+ /*NOTREACHED*/
+
+ fast_put:
+ /*
+ * We have the reply message data in kmsg,
+ * and the reply message size in reply_size.
+ * Just need to copy it out to the user and free kmsg.
+ * We must check ikm_cache after copyoutmsg.
+ */
+
+ ikm_check_initialized(kmsg, kmsg->ikm_size);
+
+ if ((kmsg->ikm_size != IKM_SAVED_KMSG_SIZE) ||
+ copyoutmsg(&kmsg->ikm_header, msg,
+ reply_size))
+ goto slow_put;
+
+ if (!ikm_cache_free_try(kmsg))
+ goto slow_put;
+
+ thread_syscall_return(MACH_MSG_SUCCESS);
+ /*NOTREACHED*/
+ return MACH_MSG_SUCCESS; /* help for the compiler */
+
+ /*
+ * The slow path has a few non-register temporary
+ * variables used only for call-by-reference.
+ */
+
+ {
+ ipc_kmsg_t temp_kmsg;
+ mach_port_seqno_t temp_seqno;
+ ipc_object_t temp_rcv_object;
+ ipc_mqueue_t temp_rcv_mqueue;
+
+ slow_get:
+ /*
+ * No locks, references, or messages held.
+ * Still have to get the request, send it,
+ * receive reply, etc.
+ */
+
+ mr = ipc_kmsg_get(msg, send_size, &temp_kmsg);
+ if (mr != MACH_MSG_SUCCESS) {
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+ kmsg = temp_kmsg;
+
+ /* try to get back on optimized path */
+ goto fast_copyin;
+
+ slow_copyin:
+ /*
+ * We have the message data in kmsg, but
+ * we still need to copyin, send it,
+ * receive a reply, and do copyout.
+ */
+
+ mr = ipc_kmsg_copyin(kmsg, space, current_map(),
+ MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ ikm_free(kmsg);
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ /* try to get back on optimized path */
+
+ if (kmsg->ikm_header.msgh_bits & MACH_MSGH_BITS_CIRCULAR)
+ goto slow_send;
+
+ dest_port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
+ assert(IP_VALID(dest_port));
+
+ ip_lock(dest_port);
+ if (dest_port->ip_receiver == ipc_space_kernel) {
+ assert(ip_active(dest_port));
+ ip_unlock(dest_port);
+ goto kernel_send;
+ }
+
+ if (ip_active(dest_port) &&
+ ((dest_port->ip_msgcount < dest_port->ip_qlimit) ||
+ (MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
+ MACH_MSG_TYPE_PORT_SEND_ONCE)))
+ {
+ /*
+ * Try an optimized ipc_mqueue_copyin.
+ * It will work if this is a request message.
+ */
+
+ ipc_port_t reply_port;
+
+ reply_port = (ipc_port_t)
+ kmsg->ikm_header.msgh_local_port;
+ if (IP_VALID(reply_port)) {
+ if (ip_lock_try(reply_port)) {
+ if (ip_active(reply_port) &&
+ reply_port->ip_receiver == space &&
+ reply_port->ip_receiver_name == rcv_name &&
+ reply_port->ip_pset == IPS_NULL)
+ {
+ /* Grab a reference to the reply port. */
+ rcv_object = (ipc_object_t) reply_port;
+ io_reference(rcv_object);
+ rcv_mqueue = &reply_port->ip_messages;
+ imq_lock(rcv_mqueue);
+ io_unlock(rcv_object);
+ goto fast_send_receive;
+ }
+ ip_unlock(reply_port);
+ }
+ }
+ }
+
+ ip_unlock(dest_port);
+ goto slow_send;
+
+ kernel_send:
+ /*
+ * Special case: send message to kernel services.
+ * The request message has been copied into the
+ * kmsg. Nothing is locked.
+ */
+
+ {
+ ipc_port_t reply_port;
+
+ /*
+ * Perform the kernel function.
+ */
+
+ kmsg = ipc_kobject_server(kmsg);
+ if (kmsg == IKM_NULL) {
+ /*
+ * No reply. Take the
+ * slow receive path.
+ */
+ goto slow_get_rcv_port;
+ }
+
+ /*
+ * Check that:
+ * the reply port is alive
+ * we hold the receive right
+ * the name has not changed.
+ * the port is not in a set
+ * If any of these are not true,
+ * we cannot directly receive the reply
+ * message.
+ */
+ reply_port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
+ ip_lock(reply_port);
+
+ if ((!ip_active(reply_port)) ||
+ (reply_port->ip_receiver != space) ||
+ (reply_port->ip_receiver_name != rcv_name) ||
+ (reply_port->ip_pset != IPS_NULL))
+ {
+ ip_unlock(reply_port);
+ ipc_mqueue_send_always(kmsg);
+ goto slow_get_rcv_port;
+ }
+
+ rcv_mqueue = &reply_port->ip_messages;
+ imq_lock(rcv_mqueue);
+ /* keep port locked, and don`t change ref count yet */
+
+ /*
+ * If there are messages on the port
+ * or other threads waiting for a message,
+ * we cannot directly receive the reply.
+ */
+ if ((ipc_thread_queue_first(&rcv_mqueue->imq_threads)
+ != ITH_NULL) ||
+ (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages)
+ != IKM_NULL))
+ {
+ imq_unlock(rcv_mqueue);
+ ip_unlock(reply_port);
+ ipc_mqueue_send_always(kmsg);
+ goto slow_get_rcv_port;
+ }
+
+ /*
+ * We can directly receive this reply.
+ * Since the kernel reply never blocks,
+ * it holds no message_accepted request.
+ * Since there were no messages queued
+ * on the reply port, there should be
+ * no threads blocked waiting to send.
+ */
+
+ assert(kmsg->ikm_marequest == IMAR_NULL);
+ assert(ipc_thread_queue_first(&reply_port->ip_blocked)
+ == ITH_NULL);
+
+ dest_port = reply_port;
+ kmsg->ikm_header.msgh_seqno = dest_port->ip_seqno++;
+ imq_unlock(rcv_mqueue);
+
+ /*
+ * inline ipc_object_release.
+ * Port is still locked.
+ * Reference count was not incremented.
+ */
+ ip_check_unlock(reply_port);
+
+ /* copy out the kernel reply */
+ goto fast_copyout;
+ }
+
+ slow_send:
+ /*
+ * Nothing is locked. We have acquired kmsg, but
+ * we still need to send it and receive a reply.
+ */
+
+ mr = ipc_mqueue_send(kmsg, MACH_MSG_OPTION_NONE,
+ MACH_MSG_TIMEOUT_NONE);
+ if (mr != MACH_MSG_SUCCESS) {
+ mr |= ipc_kmsg_copyout_pseudo(kmsg, space,
+ current_map());
+
+ assert(kmsg->ikm_marequest == IMAR_NULL);
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ slow_get_rcv_port:
+ /*
+ * We have sent the message. Copy in the receive port.
+ */
+ mr = ipc_mqueue_copyin(space, rcv_name,
+ &temp_rcv_mqueue, &temp_rcv_object);
+ if (mr != MACH_MSG_SUCCESS) {
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+ rcv_mqueue = temp_rcv_mqueue;
+ rcv_object = temp_rcv_object;
+ /* hold ref for rcv_object; rcv_mqueue is locked */
+
+ /*
+ slow_receive:
+ */
+ /*
+ * Now we have sent the request and copied in rcv_name,
+ * so rcv_mqueue is locked and hold ref for rcv_object.
+ * Just receive a reply and try to get back to fast path.
+ *
+ * ipc_mqueue_receive may not return, because if we block
+ * then our kernel stack may be discarded. So we save
+ * state here for mach_msg_continue to pick up.
+ */
+
+ self->ith_msg = msg;
+ self->ith_rcv_size = rcv_size;
+ self->ith_object = rcv_object;
+ self->ith_mqueue = rcv_mqueue;
+
+ mr = ipc_mqueue_receive(rcv_mqueue,
+ MACH_MSG_OPTION_NONE,
+ MACH_MSG_SIZE_MAX,
+ MACH_MSG_TIMEOUT_NONE,
+ FALSE, mach_msg_continue,
+ &temp_kmsg, &temp_seqno);
+ /* rcv_mqueue is unlocked */
+ ipc_object_release(rcv_object);
+ if (mr != MACH_MSG_SUCCESS) {
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ (kmsg = temp_kmsg)->ikm_header.msgh_seqno = temp_seqno;
+ dest_port = (ipc_port_t) kmsg->ikm_header.msgh_remote_port;
+ goto fast_copyout;
+
+ slow_copyout:
+ /*
+ * Nothing locked and no references held, except
+ * we have kmsg with msgh_seqno filled in. Must
+ * still check against rcv_size and do
+ * ipc_kmsg_copyout/ipc_kmsg_put.
+ */
+
+ reply_size = kmsg->ikm_header.msgh_size;
+ if (rcv_size < msg_usize(&kmsg->ikm_header)) {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ thread_syscall_return(MACH_RCV_TOO_LARGE);
+ /*NOTREACHED*/
+ }
+
+ mr = ipc_kmsg_copyout(kmsg, space, current_map(),
+ MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ } else {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ }
+
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ /* try to get back on optimized path */
+
+ goto fast_put;
+
+ slow_put:
+ mr = ipc_kmsg_put(msg, kmsg, reply_size);
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+ } else if (option == MACH_SEND_MSG) {
+ ipc_space_t space = current_space();
+ vm_map_t map = current_map();
+ ipc_kmsg_t kmsg;
+
+ mr = ipc_kmsg_get(msg, send_size, &kmsg);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+
+ mr = ipc_kmsg_copyin(kmsg, space, map, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ ikm_free(kmsg);
+ return mr;
+ }
+
+ mr = ipc_mqueue_send(kmsg, MACH_MSG_OPTION_NONE,
+ MACH_MSG_TIMEOUT_NONE);
+ if (mr != MACH_MSG_SUCCESS) {
+ mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map);
+
+ assert(kmsg->ikm_marequest == IMAR_NULL);
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ }
+
+ return mr;
+ } else if (option == MACH_RCV_MSG) {
+ ipc_thread_t self = current_thread();
+ ipc_space_t space = current_space();
+ vm_map_t map = current_map();
+ ipc_object_t object;
+ ipc_mqueue_t mqueue;
+ ipc_kmsg_t kmsg;
+ mach_port_seqno_t seqno;
+
+ mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+ /* hold ref for object; mqueue is locked */
+
+ /*
+ * ipc_mqueue_receive may not return, because if we block
+ * then our kernel stack may be discarded. So we save
+ * state here for mach_msg_continue to pick up.
+ */
+
+ self->ith_msg = msg;
+ self->ith_rcv_size = rcv_size;
+ self->ith_object = object;
+ self->ith_mqueue = mqueue;
+
+ mr = ipc_mqueue_receive(mqueue,
+ MACH_MSG_OPTION_NONE,
+ MACH_MSG_SIZE_MAX,
+ MACH_MSG_TIMEOUT_NONE,
+ FALSE, mach_msg_continue,
+ &kmsg, &seqno);
+ /* mqueue is unlocked */
+ ipc_object_release(object);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+
+ kmsg->ikm_header.msgh_seqno = seqno;
+ if (rcv_size < msg_usize(&kmsg->ikm_header)) {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ return MACH_RCV_TOO_LARGE;
+ }
+
+ mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ } else {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ }
+
+ return mr;
+ }
+
+ return ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
+ } else if (option == MACH_MSG_OPTION_NONE) {
+ /*
+ * We can measure the "null mach_msg_trap"
+ * (syscall entry and thread_syscall_return exit)
+ * with this path.
+ */
+
+ thread_syscall_return(MACH_MSG_SUCCESS);
+ /*NOTREACHED*/
+ }
+
+ if (option & MACH_SEND_MSG) {
+ mr = mach_msg_send(msg, option, send_size,
+ time_out, notify);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+ }
+
+ if (option & MACH_RCV_MSG) {
+ mr = mach_msg_receive(msg, option, rcv_size, rcv_name,
+ time_out, notify);
+ if (mr != MACH_MSG_SUCCESS)
+ return mr;
+ }
+
+ return MACH_MSG_SUCCESS;
+}
+
+/*
+ * Routine: mach_msg_continue
+ * Purpose:
+ * Continue after blocking for a message.
+ * Conditions:
+ * Nothing locked. We are running on a new kernel stack,
+ * with the receive state saved in the thread. From here
+ * control goes back to user space.
+ */
+
+void
+mach_msg_continue(void)
+{
+ ipc_thread_t thread = current_thread();
+ task_t task = thread->task;
+ ipc_space_t space = task->itk_space;
+ vm_map_t map = task->map;
+ mach_msg_user_header_t *msg = thread->ith_msg;
+ mach_msg_size_t rcv_size = thread->ith_rcv_size;
+ ipc_object_t object = thread->ith_object;
+ ipc_mqueue_t mqueue = thread->ith_mqueue;
+ ipc_kmsg_t kmsg;
+ mach_port_seqno_t seqno;
+ mach_msg_return_t mr;
+
+ mr = ipc_mqueue_receive(mqueue, MACH_MSG_OPTION_NONE,
+ MACH_MSG_SIZE_MAX, MACH_MSG_TIMEOUT_NONE,
+ TRUE, mach_msg_continue, &kmsg, &seqno);
+ /* mqueue is unlocked */
+ ipc_object_release(object);
+ if (mr != MACH_MSG_SUCCESS) {
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ kmsg->ikm_header.msgh_seqno = seqno;
+ if (msg_usize(&kmsg->ikm_header) > rcv_size) {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ thread_syscall_return(MACH_RCV_TOO_LARGE);
+ /*NOTREACHED*/
+ }
+
+ mr = ipc_kmsg_copyout(kmsg, space, map, MACH_PORT_NULL);
+ if (mr != MACH_MSG_SUCCESS) {
+ if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
+ (void) ipc_kmsg_put(msg, kmsg,
+ kmsg->ikm_header.msgh_size);
+ } else {
+ ipc_kmsg_copyout_dest(kmsg, space);
+ (void) ipc_kmsg_put(msg, kmsg, sizeof *msg);
+ }
+
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+ }
+
+ mr = ipc_kmsg_put(msg, kmsg, kmsg->ikm_header.msgh_size);
+ thread_syscall_return(mr);
+ /*NOTREACHED*/
+}
+
+/*
+ * Routine: mach_msg_interrupt
+ * Purpose:
+ * Attempts to force a thread waiting at mach_msg_continue or
+ * mach_msg_receive_continue into a clean point. Returns TRUE
+ * if this was possible.
+ * Conditions:
+ * Nothing locked. The thread must NOT be runnable.
+ */
+
+boolean_t
+mach_msg_interrupt(thread_t thread)
+{
+ ipc_mqueue_t mqueue;
+
+ assert((thread->swap_func == mach_msg_continue) ||
+ (thread->swap_func == mach_msg_receive_continue));
+
+ mqueue = thread->ith_mqueue;
+ imq_lock(mqueue);
+ if (thread->ith_state != MACH_RCV_IN_PROGRESS) {
+ /*
+ * The thread is no longer waiting for a message.
+ * It may have a message sitting in ith_kmsg.
+ * We can't clean this up.
+ */
+
+ imq_unlock(mqueue);
+ return FALSE;
+ }
+ ipc_thread_rmqueue(&mqueue->imq_threads, thread);
+ imq_unlock(mqueue);
+
+ ipc_object_release(thread->ith_object);
+
+ thread_set_syscall_return(thread, MACH_RCV_INTERRUPTED);
+ thread->swap_func = thread_exception_return;
+ return TRUE;
+}
diff --git a/ipc/mach_msg.h b/ipc/mach_msg.h
new file mode 100644
index 0000000..2951bce
--- /dev/null
+++ b/ipc/mach_msg.h
@@ -0,0 +1,60 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/mach_msg.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Declarations of internal messaging primitives.
+ */
+
+#ifndef _IPC_MACH_MSG_H_
+#define _IPC_MACH_MSG_H_
+
+#include <mach/boolean.h>
+#include <mach/message.h>
+
+extern mach_msg_return_t
+mach_msg_send(mach_msg_user_header_t *, mach_msg_option_t,
+ mach_msg_size_t, mach_msg_timeout_t, mach_port_name_t);
+
+extern mach_msg_return_t
+mach_msg_receive(mach_msg_user_header_t *, mach_msg_option_t,
+ mach_msg_size_t, mach_port_name_t,
+ mach_msg_timeout_t, mach_port_name_t);
+
+extern void
+mach_msg_receive_continue(void);
+
+extern void
+mach_msg_continue(void);
+
+extern boolean_t
+mach_msg_interrupt(thread_t);
+
+#endif /* _IPC_MACH_MSG_H_ */
diff --git a/ipc/mach_port.c b/ipc/mach_port.c
new file mode 100644
index 0000000..d8696e2
--- /dev/null
+++ b/ipc/mach_port.c
@@ -0,0 +1,1578 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/mach_port.c
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Exported kernel calls. See mach/mach_port.defs.
+ */
+
+#include <kern/debug.h>
+#include <kern/printf.h>
+#include <mach/port.h>
+#include <mach/kern_return.h>
+#include <mach/notify.h>
+#include <mach/mach_param.h>
+#include <mach/vm_param.h>
+#include <mach/vm_prot.h>
+#ifdef MIGRATING_THREADS
+#include <kern/task.h>
+#include <kern/act.h>
+#endif /* MIGRATING_THREADS */
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_user.h>
+#include <ipc/ipc_entry.h>
+#include <ipc/ipc_space.h>
+#include <ipc/ipc_object.h>
+#include <ipc/ipc_notify.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_pset.h>
+#include <ipc/ipc_right.h>
+#include <ipc/mach_port.h>
+#include <ipc/mach_port.server.h>
+
+
+/*
+ * Routine: mach_port_names_helper
+ * Purpose:
+ * A helper function for mach_port_names.
+ */
+
+static void
+mach_port_names_helper(
+ ipc_port_timestamp_t timestamp,
+ ipc_entry_t entry,
+ mach_port_name_t name,
+ mach_port_name_t *names,
+ mach_port_type_t *types,
+ ipc_entry_num_t *actualp)
+{
+ ipc_entry_bits_t bits = entry->ie_bits;
+ ipc_port_request_index_t request = entry->ie_request;
+ mach_port_type_t type;
+ ipc_entry_num_t actual;
+
+ if (bits & MACH_PORT_TYPE_SEND_RIGHTS) {
+ ipc_port_t port;
+ boolean_t died;
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ /*
+ * The timestamp serializes mach_port_names
+ * with ipc_port_destroy. If the port died,
+ * but after mach_port_names started, pretend
+ * that it isn't dead.
+ */
+
+ ip_lock(port);
+ died = (!ip_active(port) &&
+ IP_TIMESTAMP_ORDER(port->ip_timestamp, timestamp));
+ ip_unlock(port);
+
+ if (died) {
+ /* pretend this is a dead-name entry */
+
+ bits &= ~(IE_BITS_TYPE_MASK|IE_BITS_MAREQUEST);
+ bits |= MACH_PORT_TYPE_DEAD_NAME;
+ if (request != 0)
+ bits++;
+ request = 0;
+ }
+ }
+
+ type = IE_BITS_TYPE(bits);
+ if (request != 0)
+ type |= MACH_PORT_TYPE_DNREQUEST;
+ if (bits & IE_BITS_MAREQUEST)
+ type |= MACH_PORT_TYPE_MAREQUEST;
+
+ actual = *actualp;
+ names[actual] = name;
+ types[actual] = type;
+ *actualp = actual+1;
+}
+
+/*
+ * Routine: mach_port_names [kernel call]
+ * Purpose:
+ * Retrieves a list of the rights present in the space,
+ * along with type information. (Same as returned
+ * by mach_port_type.) The names are returned in
+ * no particular order, but they (and the type info)
+ * are an accurate snapshot of the space.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Arrays of names and types returned.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+mach_port_names(
+ ipc_space_t space,
+ mach_port_name_t **namesp,
+ mach_msg_type_number_t *namesCnt,
+ mach_port_type_t **typesp,
+ mach_msg_type_number_t *typesCnt)
+{
+ ipc_entry_num_t actual; /* this many names */
+ ipc_port_timestamp_t timestamp; /* logical time of this operation */
+ mach_port_name_t *names;
+ mach_port_type_t *types;
+ kern_return_t kr;
+
+ vm_size_t size; /* size of allocated memory */
+ vm_offset_t addr1; /* allocated memory, for names */
+ vm_offset_t addr2; /* allocated memory, for types */
+ vm_map_copy_t memory1; /* copied-in memory, for names */
+ vm_map_copy_t memory2; /* copied-in memory, for types */
+ ipc_entry_num_t bound;
+
+ /* safe simplifying assumption */
+ assert_static(sizeof(mach_port_name_t) == sizeof(mach_port_type_t));
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ size = 0;
+
+ for (;;) {
+ vm_size_t size_needed;
+
+ is_read_lock(space);
+ if (!space->is_active) {
+ is_read_unlock(space);
+ if (size != 0) {
+ kmem_free(ipc_kernel_map, addr1, size);
+ kmem_free(ipc_kernel_map, addr2, size);
+ }
+ return KERN_INVALID_TASK;
+ }
+
+ /* upper bound on number of names in the space */
+
+ bound = space->is_size;
+ size_needed = round_page(bound * sizeof(mach_port_name_t));
+
+ if (size_needed <= size)
+ break;
+
+ is_read_unlock(space);
+
+ if (size != 0) {
+ kmem_free(ipc_kernel_map, addr1, size);
+ kmem_free(ipc_kernel_map, addr2, size);
+ }
+ size = size_needed;
+
+ kr = vm_allocate(ipc_kernel_map, &addr1, size, TRUE);
+ if (kr != KERN_SUCCESS) {
+ printf_once("no more room in ipc_kernel_map\n");
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ kr = vm_allocate(ipc_kernel_map, &addr2, size, TRUE);
+ if (kr != KERN_SUCCESS) {
+ printf_once("no more room in ipc_kernel_map\n");
+ kmem_free(ipc_kernel_map, addr1, size);
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ /* can't fault while we hold locks */
+
+ kr = vm_map_pageable(ipc_kernel_map, addr1, addr1 + size,
+ VM_PROT_READ|VM_PROT_WRITE, TRUE, TRUE);
+ assert(kr == KERN_SUCCESS);
+
+ kr = vm_map_pageable(ipc_kernel_map, addr2, addr2 + size,
+ VM_PROT_READ|VM_PROT_WRITE, TRUE, TRUE);
+ assert(kr == KERN_SUCCESS);
+ }
+ /* space is read-locked and active */
+
+ names = (mach_port_name_t *) addr1;
+ types = (mach_port_type_t *) addr2;
+ actual = 0;
+
+ timestamp = ipc_port_timestamp();
+
+ ipc_entry_t entry;
+ struct rdxtree_iter iter;
+ rdxtree_for_each(&space->is_map, &iter, entry) {
+ ipc_entry_bits_t bits = entry->ie_bits;
+
+ if (IE_BITS_TYPE(bits) != MACH_PORT_TYPE_NONE) {
+ mach_port_names_helper(timestamp, entry, entry->ie_name,
+ names, types, &actual);
+ }
+ }
+ assert(actual < bound);
+ is_read_unlock(space);
+
+ if (actual == 0) {
+ memory1 = VM_MAP_COPY_NULL;
+ memory2 = VM_MAP_COPY_NULL;
+
+ if (size != 0) {
+ kmem_free(ipc_kernel_map, addr1, size);
+ kmem_free(ipc_kernel_map, addr2, size);
+ }
+ } else {
+ vm_size_t size_used;
+
+ size_used = round_page(actual * sizeof(mach_port_name_t));
+
+ /*
+ * Make used memory pageable and get it into
+ * copied-in form. Free any unused memory.
+ */
+
+ kr = vm_map_pageable(ipc_kernel_map,
+ addr1, addr1 + size_used,
+ VM_PROT_NONE, TRUE, TRUE);
+ assert(kr == KERN_SUCCESS);
+
+ kr = vm_map_pageable(ipc_kernel_map,
+ addr2, addr2 + size_used,
+ VM_PROT_NONE, TRUE, TRUE);
+ assert(kr == KERN_SUCCESS);
+
+ kr = vm_map_copyin(ipc_kernel_map, addr1, size_used,
+ TRUE, &memory1);
+ assert(kr == KERN_SUCCESS);
+
+ kr = vm_map_copyin(ipc_kernel_map, addr2, size_used,
+ TRUE, &memory2);
+ assert(kr == KERN_SUCCESS);
+
+ if (size_used != size) {
+ kmem_free(ipc_kernel_map,
+ addr1 + size_used, size - size_used);
+ kmem_free(ipc_kernel_map,
+ addr2 + size_used, size - size_used);
+ }
+ }
+
+ *namesp = (mach_port_name_t *) memory1;
+ *namesCnt = actual;
+ *typesp = (mach_port_type_t *) memory2;
+ *typesCnt = actual;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_type [kernel call]
+ * Purpose:
+ * Retrieves the type of a right in the space.
+ * The type is a bitwise combination of one or more
+ * of the following type bits:
+ * MACH_PORT_TYPE_SEND
+ * MACH_PORT_TYPE_RECEIVE
+ * MACH_PORT_TYPE_SEND_ONCE
+ * MACH_PORT_TYPE_PORT_SET
+ * MACH_PORT_TYPE_DEAD_NAME
+ * In addition, the following pseudo-type bits may be present:
+ * MACH_PORT_TYPE_DNREQUEST
+ * A dead-name notification is requested.
+ * MACH_PORT_TYPE_MAREQUEST
+ * The send/receive right is blocked;
+ * a msg-accepted notification is outstanding.
+ * MACH_PORT_TYPE_COMPAT
+ * This is a compatibility-mode right;
+ * when the port dies, it will disappear
+ * instead of turning into a dead-name.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Type is returned.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ */
+
+kern_return_t
+mach_port_type(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_type_t *typep)
+{
+ mach_port_urefs_t urefs;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ kr = ipc_right_info(space, name, entry, typep, &urefs);
+ if (kr == KERN_SUCCESS)
+ is_write_unlock(space);
+ /* space is unlocked */
+ return kr;
+}
+
+/*
+ * Routine: mach_port_rename [kernel call]
+ * Purpose:
+ * Changes the name denoting a right,
+ * from oname to nname.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The right is renamed.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The oname doesn't denote a right.
+ * KERN_INVALID_VALUE The nname isn't a legal name.
+ * KERN_NAME_EXISTS The nname already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+mach_port_rename(
+ ipc_space_t space,
+ mach_port_name_t oname,
+ mach_port_name_t nname)
+{
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (!MACH_PORT_NAME_VALID(nname))
+ return KERN_INVALID_VALUE;
+
+ return ipc_object_rename(space, oname, nname);
+}
+
+/*
+ * Routine: mach_port_allocate_name [kernel call]
+ * Purpose:
+ * Allocates a right in a space, using a specific name
+ * for the new right. Possible rights:
+ * MACH_PORT_RIGHT_RECEIVE
+ * MACH_PORT_RIGHT_PORT_SET
+ * MACH_PORT_RIGHT_DEAD_NAME
+ *
+ * A new port (allocated with MACH_PORT_RIGHT_RECEIVE)
+ * has no extant send or send-once rights and no queued
+ * messages. Its queue limit is MACH_PORT_QLIMIT_DEFAULT
+ * and its make-send count is 0. It is not a member of
+ * a port set. It has no registered no-senders or
+ * port-destroyed notification requests.
+ *
+ * A new port set has no members.
+ *
+ * A new dead name has one user reference.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The right is allocated.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_VALUE The name isn't a legal name.
+ * KERN_INVALID_VALUE "right" isn't a legal kind of right.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+mach_port_allocate_name(
+ ipc_space_t space,
+ mach_port_right_t right,
+ mach_port_name_t name)
+{
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (!MACH_PORT_NAME_VALID(name))
+ return KERN_INVALID_VALUE;
+
+ switch (right) {
+ case MACH_PORT_RIGHT_RECEIVE: {
+ ipc_port_t port;
+
+ kr = ipc_port_alloc_name(space, name, &port);
+ if (kr == KERN_SUCCESS)
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_PORT_RIGHT_PORT_SET: {
+ ipc_pset_t pset;
+
+ kr = ipc_pset_alloc_name(space, name, &pset);
+ if (kr == KERN_SUCCESS)
+ ips_unlock(pset);
+ break;
+ }
+
+ case MACH_PORT_RIGHT_DEAD_NAME:
+ kr = ipc_object_alloc_dead_name(space, name);
+ break;
+
+ default:
+ kr = KERN_INVALID_VALUE;
+ break;
+ }
+
+ return kr;
+}
+
+/*
+ * Routine: mach_port_allocate [kernel call]
+ * Purpose:
+ * Allocates a right in a space. Like mach_port_allocate_name,
+ * except that the implementation picks a name for the right.
+ * The name may be any legal name in the space that doesn't
+ * currently denote a right.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The right is allocated.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_VALUE "right" isn't a legal kind of right.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ * KERN_NO_SPACE No room in space for another right.
+ */
+
+kern_return_t
+mach_port_allocate(
+ ipc_space_t space,
+ mach_port_right_t right,
+ mach_port_name_t *namep)
+{
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ switch (right) {
+ case MACH_PORT_RIGHT_RECEIVE: {
+ ipc_port_t port;
+
+ kr = ipc_port_alloc(space, namep, &port);
+ if (kr == KERN_SUCCESS)
+ ip_unlock(port);
+ break;
+ }
+
+ case MACH_PORT_RIGHT_PORT_SET: {
+ ipc_pset_t pset;
+
+ kr = ipc_pset_alloc(space, namep, &pset);
+ if (kr == KERN_SUCCESS)
+ ips_unlock(pset);
+ break;
+ }
+
+ case MACH_PORT_RIGHT_DEAD_NAME:
+ kr = ipc_object_alloc_dead(space, namep);
+ break;
+
+ default:
+ kr = KERN_INVALID_VALUE;
+ break;
+ }
+
+ return (kr);
+}
+
+/*
+ * Routine: mach_port_destroy [kernel call]
+ * Purpose:
+ * Cleans up and destroys all rights denoted by a name
+ * in a space. The destruction of a receive right
+ * destroys the port, unless a port-destroyed request
+ * has been made for it; the destruction of a port-set right
+ * destroys the port set.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The name is destroyed.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ */
+
+volatile boolean_t mach_port_deallocate_debug = FALSE;
+
+kern_return_t
+mach_port_destroy(
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ if (MACH_PORT_NAME_VALID (name) && space == current_space()) {
+ printf("task %.*s destroying a bogus port %lu, most probably a bug.\n", (int) sizeof current_task()->name, current_task()->name, (unsigned long) name);
+ if (mach_port_deallocate_debug)
+ SoftDebugger("mach_port_deallocate");
+ }
+ return kr;
+ }
+ /* space is write-locked and active */
+
+ kr = ipc_right_destroy(space, name, entry); /* unlocks space */
+ return kr;
+}
+
+/*
+ * Routine: mach_port_deallocate [kernel call]
+ * Purpose:
+ * Deallocates a user reference from a send right,
+ * send-once right, or a dead-name right. May
+ * deallocate the right, if this is the last uref,
+ * and destroy the name, if it doesn't denote
+ * other rights.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS The uref is deallocated.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT The right isn't correct.
+ */
+
+kern_return_t
+mach_port_deallocate(
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ if (MACH_PORT_NAME_VALID (name) && space == current_space()) {
+ printf("task %.*s deallocating a bogus port %lu, most probably a bug.\n", (int) sizeof current_task()->name, current_task()->name, (unsigned long) name);
+ if (mach_port_deallocate_debug)
+ SoftDebugger("mach_port_deallocate");
+ }
+ return kr;
+ }
+ /* space is write-locked */
+
+ kr = ipc_right_dealloc(space, name, entry); /* unlocks space */
+ return kr;
+}
+
+/*
+ * Routine: mach_port_get_refs [kernel call]
+ * Purpose:
+ * Retrieves the number of user references held by a right.
+ * Receive rights, port-set rights, and send-once rights
+ * always have one user reference. Returns zero if the
+ * name denotes a right, but not the queried right.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Number of urefs returned.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_VALUE "right" isn't a legal value.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ */
+
+kern_return_t
+mach_port_get_refs(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_right_t right,
+ mach_port_urefs_t *urefsp)
+{
+ mach_port_type_t type;
+ mach_port_urefs_t urefs;
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (right >= MACH_PORT_RIGHT_NUMBER)
+ return KERN_INVALID_VALUE;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is write-locked and active */
+
+ kr = ipc_right_info(space, name, entry, &type, &urefs); /* unlocks */
+ if (kr != KERN_SUCCESS)
+ return kr; /* space is unlocked */
+ is_write_unlock(space);
+
+ if (type & MACH_PORT_TYPE(right))
+ switch (right) {
+ case MACH_PORT_RIGHT_SEND_ONCE:
+ assert(urefs == 1);
+ /* fall-through */
+
+ case MACH_PORT_RIGHT_PORT_SET:
+ case MACH_PORT_RIGHT_RECEIVE:
+ *urefsp = 1;
+ break;
+
+ case MACH_PORT_RIGHT_DEAD_NAME:
+ case MACH_PORT_RIGHT_SEND:
+ assert(urefs > 0);
+ *urefsp = urefs;
+ break;
+
+ default:
+ panic("mach_port_get_refs: strange rights");
+ }
+ else
+ *urefsp = 0;
+
+ return kr;
+}
+
+/*
+ * Routine: mach_port_mod_refs
+ * Purpose:
+ * Modifies the number of user references held by a right.
+ * The resulting number of user references must be non-negative.
+ * If it is zero, the right is deallocated. If the name
+ * doesn't denote other rights, it is destroyed.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Modified number of urefs.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_VALUE "right" isn't a legal value.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote specified right.
+ * KERN_INVALID_VALUE Impossible modification to urefs.
+ * KERN_UREFS_OVERFLOW Urefs would overflow.
+ */
+
+kern_return_t
+mach_port_mod_refs(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_right_t right,
+ mach_port_delta_t delta)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (right >= MACH_PORT_RIGHT_NUMBER)
+ return KERN_INVALID_VALUE;
+
+ kr = ipc_right_lookup_write(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ if (MACH_PORT_NAME_VALID (name) && space == current_space()) {
+ printf("task %.*s %screasing a bogus port "
+ "%u by %d, most probably a bug.\n",
+ (int) (sizeof current_task()->name),
+ current_task()->name,
+ delta < 0 ? "de" : "in", name,
+ delta < 0 ? -delta : delta);
+ if (mach_port_deallocate_debug)
+ SoftDebugger("mach_port_mod_refs");
+ }
+ return kr;
+ }
+ /* space is write-locked and active */
+
+ kr = ipc_right_delta(space, name, entry, right, delta); /* unlocks */
+ return kr;
+}
+
+/*
+ * Routine: mach_port_set_qlimit [kernel call]
+ * Purpose:
+ * Changes a receive right's queue limit.
+ * The new queue limit must be between 0 and
+ * MACH_PORT_QLIMIT_MAX, inclusive.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Set queue limit.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ * KERN_INVALID_VALUE Illegal queue limit.
+ */
+
+kern_return_t
+mach_port_set_qlimit(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_msgcount_t qlimit)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (qlimit > MACH_PORT_QLIMIT_MAX)
+ return KERN_INVALID_VALUE;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ ipc_port_set_qlimit(port, qlimit);
+
+ ip_unlock(port);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_set_mscount [kernel call]
+ * Purpose:
+ * Changes a receive right's make-send count.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Set make-send count.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ */
+
+kern_return_t
+mach_port_set_mscount(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_mscount_t mscount)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ ipc_port_set_mscount(port, mscount);
+
+ ip_unlock(port);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_set_seqno [kernel call]
+ * Purpose:
+ * Changes a receive right's sequence number.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Set sequence number.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ */
+
+kern_return_t
+mach_port_set_seqno(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_seqno_t seqno)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ ipc_port_set_seqno(port, seqno);
+
+ ip_unlock(port);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_gst_helper
+ * Purpose:
+ * A helper function for mach_port_get_set_status.
+ */
+
+static void
+mach_port_gst_helper(
+ ipc_pset_t pset,
+ ipc_port_t port,
+ ipc_entry_num_t maxnames,
+ mach_port_name_t *names,
+ ipc_entry_num_t *actualp)
+{
+ ipc_pset_t ip_pset;
+ mach_port_name_t name;
+
+ assert(port != IP_NULL);
+
+ ip_lock(port);
+ assert(ip_active(port));
+
+ name = port->ip_receiver_name;
+ assert(name != MACH_PORT_NULL);
+ ip_pset = port->ip_pset;
+
+ ip_unlock(port);
+
+ if (pset == ip_pset) {
+ ipc_entry_num_t actual = *actualp;
+
+ if (actual < maxnames)
+ names[actual] = name;
+
+ *actualp = actual+1;
+ }
+}
+
+/*
+ * Routine: mach_port_get_set_status [kernel call]
+ * Purpose:
+ * Retrieves a list of members in a port set.
+ * Returns the space's name for each receive right member.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Retrieved list of members.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote a port set.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+mach_port_get_set_status(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_name_t **members,
+ mach_msg_type_number_t *membersCnt)
+{
+ ipc_entry_num_t actual; /* this many members */
+ ipc_entry_num_t maxnames; /* space for this many members */
+ kern_return_t kr;
+
+ vm_size_t size; /* size of allocated memory */
+ vm_offset_t addr; /* allocated memory */
+ vm_map_copy_t memory; /* copied-in memory */
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ size = PAGE_SIZE; /* initial guess */
+
+ for (;;) {
+ ipc_entry_t entry;
+ mach_port_name_t *names;
+ ipc_pset_t pset;
+
+ kr = vm_allocate(ipc_kernel_map, &addr, size, TRUE);
+ if (kr != KERN_SUCCESS) {
+ printf_once("no more room in ipc_kernel_map\n");
+ return KERN_RESOURCE_SHORTAGE;
+ }
+
+ /* can't fault while we hold locks */
+
+ kr = vm_map_pageable(ipc_kernel_map, addr, addr + size,
+ VM_PROT_READ|VM_PROT_WRITE, TRUE, TRUE);
+ assert(kr == KERN_SUCCESS);
+
+ kr = ipc_right_lookup_read(space, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ kmem_free(ipc_kernel_map, addr, size);
+ return kr;
+ }
+ /* space is read-locked and active */
+
+ if (IE_BITS_TYPE(entry->ie_bits) != MACH_PORT_TYPE_PORT_SET) {
+ is_read_unlock(space);
+ kmem_free(ipc_kernel_map, addr, size);
+ return KERN_INVALID_RIGHT;
+ }
+
+ pset = (ipc_pset_t) entry->ie_object;
+ assert(pset != IPS_NULL);
+ /* the port set must be active */
+
+ names = (mach_port_name_t *) addr;
+ maxnames = size / sizeof(mach_port_name_t);
+ actual = 0;
+
+ ipc_entry_t ientry;
+ struct rdxtree_iter iter;
+ rdxtree_for_each(&space->is_map, &iter, ientry) {
+ ipc_entry_bits_t bits = ientry->ie_bits;
+
+ if (bits & MACH_PORT_TYPE_RECEIVE) {
+ ipc_port_t port =
+ (ipc_port_t) ientry->ie_object;
+
+ mach_port_gst_helper(pset, port, maxnames,
+ names, &actual);
+ }
+ }
+
+ is_read_unlock(space);
+
+ if (actual <= maxnames)
+ break;
+
+ /* didn't have enough memory; allocate more */
+
+ kmem_free(ipc_kernel_map, addr, size);
+ size = round_page(actual * sizeof(mach_port_name_t)) + PAGE_SIZE;
+ }
+
+ if (actual == 0) {
+ memory = VM_MAP_COPY_NULL;
+
+ kmem_free(ipc_kernel_map, addr, size);
+ } else {
+ vm_size_t size_used;
+
+ size_used = round_page(actual * sizeof(mach_port_name_t));
+
+ /*
+ * Make used memory pageable and get it into
+ * copied-in form. Free any unused memory.
+ */
+
+ kr = vm_map_pageable(ipc_kernel_map,
+ addr, addr + size_used,
+ VM_PROT_NONE, TRUE, TRUE);
+ assert(kr == KERN_SUCCESS);
+
+ kr = vm_map_copyin(ipc_kernel_map, addr, size_used,
+ TRUE, &memory);
+ assert(kr == KERN_SUCCESS);
+
+ if (size_used != size)
+ kmem_free(ipc_kernel_map,
+ addr + size_used, size - size_used);
+ }
+
+ *members = (mach_port_name_t *) memory;
+ *membersCnt = actual;
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_move_member [kernel call]
+ * Purpose:
+ * If after is MACH_PORT_NULL, removes member
+ * from the port set it is in. Otherwise, adds
+ * member to after, removing it from any set
+ * it might already be in.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Moved the port.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME Member didn't denote a right.
+ * KERN_INVALID_RIGHT Member didn't denote a receive right.
+ * KERN_INVALID_NAME After didn't denote a right.
+ * KERN_INVALID_RIGHT After didn't denote a port set right.
+ * KERN_NOT_IN_SET
+ * After is MACH_PORT_NULL and Member isn't in a port set.
+ */
+
+kern_return_t
+mach_port_move_member(
+ ipc_space_t space,
+ mach_port_name_t member,
+ mach_port_name_t after)
+{
+ ipc_entry_t entry;
+ ipc_port_t port;
+ ipc_pset_t nset;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_right_lookup_read(space, member, &entry);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* space is read-locked and active */
+
+ if ((entry->ie_bits & MACH_PORT_TYPE_RECEIVE) == 0) {
+ is_read_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ port = (ipc_port_t) entry->ie_object;
+ assert(port != IP_NULL);
+
+ if (after == MACH_PORT_NULL)
+ nset = IPS_NULL;
+ else {
+ entry = ipc_entry_lookup(space, after);
+ if (entry == IE_NULL) {
+ is_read_unlock(space);
+ return KERN_INVALID_NAME;
+ }
+
+ if ((entry->ie_bits & MACH_PORT_TYPE_PORT_SET) == 0) {
+ is_read_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ nset = (ipc_pset_t) entry->ie_object;
+ assert(nset != IPS_NULL);
+ }
+
+ kr = ipc_pset_move(space, port, nset);
+ /* space is unlocked */
+ return kr;
+}
+
+/*
+ * Routine: mach_port_request_notification [kernel call]
+ * Purpose:
+ * Requests a notification. The caller supplies
+ * a send-once right for the notification to use,
+ * and the call returns the previously registered
+ * send-once right, if any. Possible types:
+ *
+ * MACH_NOTIFY_PORT_DESTROYED
+ * Requests a port-destroyed notification
+ * for a receive right. Sync should be zero.
+ * MACH_NOTIFY_NO_SENDERS
+ * Requests a no-senders notification for a
+ * receive right. If there are currently no
+ * senders, sync is less than or equal to the
+ * current make-send count, and a send-once right
+ * is supplied, then an immediate no-senders
+ * notification is generated.
+ * MACH_NOTIFY_DEAD_NAME
+ * Requests a dead-name notification for a send
+ * or receive right. If the name is already a
+ * dead name, sync is non-zero, and a send-once
+ * right is supplied, then an immediate dead-name
+ * notification is generated.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Requested a notification.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_VALUE Bad id value.
+ * KERN_INVALID_NAME Name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote appropriate right.
+ * KERN_INVALID_CAPABILITY The notify port is dead.
+ * MACH_NOTIFY_PORT_DESTROYED:
+ * KERN_INVALID_VALUE Sync isn't zero.
+ * MACH_NOTIFY_DEAD_NAME:
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ * KERN_INVALID_ARGUMENT Name denotes dead name, but
+ * sync is zero or notify is IP_NULL.
+ * KERN_UREFS_OVERFLOW Name denotes dead name, but
+ * generating immediate notif. would overflow urefs.
+ */
+
+kern_return_t
+mach_port_request_notification(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_msg_id_t id,
+ mach_port_mscount_t sync,
+ ipc_port_t notify,
+ ipc_port_t *previousp)
+{
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (notify == IP_DEAD)
+ return KERN_INVALID_CAPABILITY;
+
+ switch (id) {
+ case MACH_NOTIFY_PORT_DESTROYED: {
+ ipc_port_t port, previous;
+
+ if (sync != 0)
+ return KERN_INVALID_VALUE;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ ipc_port_pdrequest(port, notify, &previous);
+ /* port is unlocked */
+
+ *previousp = previous;
+ break;
+ }
+
+ case MACH_NOTIFY_NO_SENDERS: {
+ ipc_port_t port;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ ipc_port_nsrequest(port, sync, notify, previousp);
+ /* port is unlocked */
+ break;
+ }
+
+ case MACH_NOTIFY_DEAD_NAME:
+ kr = ipc_right_dnrequest(space, name, sync != 0,
+ notify, previousp);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ break;
+
+ default:
+ return KERN_INVALID_VALUE;
+ }
+
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_insert_right [kernel call]
+ * Purpose:
+ * Inserts a right into a space, as if the space
+ * voluntarily received the right in a message,
+ * except that the right gets the specified name.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Inserted the right.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_VALUE The name isn't a legal name.
+ * KERN_NAME_EXISTS The name already denotes a right.
+ * KERN_INVALID_VALUE Message doesn't carry a port right.
+ * KERN_INVALID_CAPABILITY Port is null or dead.
+ * KERN_UREFS_OVERFLOW Urefs limit would be exceeded.
+ * KERN_RIGHT_EXISTS Space has rights under another name.
+ * KERN_RESOURCE_SHORTAGE Couldn't allocate memory.
+ */
+
+kern_return_t
+mach_port_insert_right(
+ ipc_space_t space,
+ mach_port_name_t name,
+ ipc_port_t poly,
+ mach_msg_type_name_t polyPoly)
+{
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (!MACH_PORT_NAME_VALID(name) ||
+ !MACH_MSG_TYPE_PORT_ANY_RIGHT(polyPoly))
+ return KERN_INVALID_VALUE;
+
+ if (!IO_VALID((ipc_object_t)poly))
+ return KERN_INVALID_CAPABILITY;
+
+ return ipc_object_copyout_name(space, (ipc_object_t)poly,
+ polyPoly, FALSE, name);
+}
+
+/*
+ * Routine: mach_port_extract_right [kernel call]
+ * Purpose:
+ * Extracts a right from a space, as if the space
+ * voluntarily sent the right to the caller.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Extracted the right.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_VALUE Requested type isn't a port right.
+ * KERN_INVALID_NAME Name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote appropriate right.
+ */
+
+kern_return_t
+mach_port_extract_right(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_msg_type_name_t msgt_name,
+ ipc_port_t *poly,
+ mach_msg_type_name_t *polyPoly)
+{
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ if (!MACH_MSG_TYPE_PORT_ANY(msgt_name))
+ return KERN_INVALID_VALUE;
+
+ kr = ipc_object_copyin(space, name, msgt_name, (ipc_object_t *) poly);
+
+ if (kr == KERN_SUCCESS)
+ *polyPoly = ipc_object_copyin_type(msgt_name);
+ return kr;
+}
+
+/*
+ * Routine: mach_port_get_receive_status [kernel call]
+ * Purpose:
+ * Retrieves mucho info about a receive right.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Retrieved status.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ */
+
+kern_return_t
+mach_port_get_receive_status(
+ ipc_space_t space,
+ mach_port_name_t name,
+ mach_port_status_t *statusp)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ if (port->ip_pset != IPS_NULL) {
+ ipc_pset_t pset = port->ip_pset;
+
+ ips_lock(pset);
+ if (!ips_active(pset)) {
+ ipc_pset_remove(pset, port);
+ ips_check_unlock(pset);
+ goto no_port_set;
+ } else {
+ statusp->mps_pset = pset->ips_local_name;
+ imq_lock(&pset->ips_messages);
+ statusp->mps_seqno = port->ip_seqno;
+ imq_unlock(&pset->ips_messages);
+ ips_unlock(pset);
+ assert(MACH_PORT_NAME_VALID(statusp->mps_pset));
+ }
+ } else {
+ no_port_set:
+ statusp->mps_pset = MACH_PORT_NULL;
+ imq_lock(&port->ip_messages);
+ statusp->mps_seqno = port->ip_seqno;
+ imq_unlock(&port->ip_messages);
+ }
+
+ statusp->mps_mscount = port->ip_mscount;
+ statusp->mps_qlimit = port->ip_qlimit;
+ statusp->mps_msgcount = port->ip_msgcount;
+ statusp->mps_sorights = port->ip_sorights;
+ statusp->mps_srights = port->ip_srights > 0;
+ statusp->mps_pdrequest = port->ip_pdrequest != IP_NULL;
+ statusp->mps_nsrequest = port->ip_nsrequest != IP_NULL;
+ ip_unlock(port);
+
+ return KERN_SUCCESS;
+}
+
+#ifdef MIGRATING_THREADS
+kern_return_t
+mach_port_set_rpcinfo(
+ ipc_space_t space,
+ mach_port_name_t name,
+ void *rpc_info,
+ unsigned int rpc_info_count)
+{
+ ipc_target_t target;
+ ipc_object_t object;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_object_translate(space, name,
+ MACH_PORT_RIGHT_PORT_SET, &object);
+ if (kr == KERN_SUCCESS)
+ target = &((ipc_pset_t)object)->ips_target;
+ else {
+ kr = ipc_object_translate(space, name,
+ MACH_PORT_RIGHT_RECEIVE, &object);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ target = &((ipc_port_t)object)->ip_target;
+ }
+
+ /* port/pset is locked and active */
+
+ kr = port_machine_set_rpcinfo(target, rpc_info, rpc_info_count);
+
+ io_unlock(object);
+
+ return kr;
+}
+
+#if 1
+int sacts, maxsacts;
+#endif
+
+void sact_count(void)
+{
+ printf("%d server activations in use, %d max\n", sacts, maxsacts);
+}
+
+kern_return_t
+mach_port_create_act(
+ task_t task,
+ mach_port_name_t name,
+ vm_offset_t user_stack,
+ vm_offset_t user_rbuf,
+ vm_size_t user_rbuf_size,
+ Act **out_act)
+{
+ ipc_target_t target;
+ ipc_space_t space;
+ ipc_object_t object;
+ kern_return_t kr;
+ Act *act;
+
+ if (task == 0)
+ return KERN_INVALID_TASK;
+
+ /* First create the new activation. */
+ kr = act_create(task, user_stack, user_rbuf, user_rbuf_size, &act);
+ if (kr != KERN_SUCCESS)
+ return kr;
+
+ space = task->itk_space;
+
+ kr = ipc_object_translate(space, name,
+ MACH_PORT_RIGHT_PORT_SET, &object);
+ if (kr == KERN_SUCCESS)
+ target = &((ipc_pset_t)object)->ips_target;
+ else {
+ kr = ipc_object_translate(space, name,
+ MACH_PORT_RIGHT_RECEIVE, &object);
+ if (kr != KERN_SUCCESS) {
+ act_terminate(act);
+ act_deallocate(act);
+ return kr;
+ }
+ target = &((ipc_port_t)object)->ip_target;
+ }
+
+ /* port/pset is locked and active */
+#if 0
+ printf("act port/pset %08x ipc_target %08x stack %08x act %08x\n",
+ object, target, user_stack, act);
+#endif
+
+ /* Assign the activation to the port's actpool. */
+ kr = act_set_target(act, target);
+ if (kr != KERN_SUCCESS) {
+ io_unlock(object);
+ act_terminate(act);
+ act_deallocate(act);
+ return kr;
+ }
+#if 0
+ printf(" actpool %08x act %08x\n", target->ip_actpool, act);
+#endif
+
+ io_unlock(object);
+
+ /* Pass our reference to the activation back to the user. */
+ *out_act = act;
+
+#if 1
+ sacts++;
+ if (sacts > maxsacts)
+ maxsacts = sacts;
+ act->mact.pcb->ss.mpsfu_high = 0x69;
+#endif
+ return KERN_SUCCESS;
+}
+
+#ifdef RPCKERNELSIG
+kern_return_t
+mach_port_set_syscall_right(
+ task_t task,
+ mach_port_name_t name)
+{
+ ipc_entry_t entry;
+ kern_return_t kr;
+
+ if (task == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_right_lookup_write(task, name, &entry);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ if (!(entry->ie_bits & MACH_PORT_TYPE(MACH_PORT_RIGHT_SEND))) {
+ is_write_unlock(space);
+ return KERN_INVALID_RIGHT;
+ }
+
+ task->syscall_ipc_entry = *entry;
+
+ is_write_unlock(space);
+
+ return KERN_SUCCESS;
+}
+#endif
+#endif /* MIGRATING_THREADS */
+
+/*
+ * Routine: mach_port_set_protected_payload [kernel call]
+ * Purpose:
+ * Changes a receive right's protected payload.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Set protected payload.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ */
+
+kern_return_t
+mach_port_set_protected_payload(
+ ipc_space_t space,
+ mach_port_name_t name,
+ rpc_uintptr_t payload)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ ipc_port_set_protected_payload(port, payload);
+
+ ip_unlock(port);
+ return KERN_SUCCESS;
+}
+
+/*
+ * Routine: mach_port_clear_protected_payload [kernel call]
+ * Purpose:
+ * Clears a receive right's protected payload.
+ * Conditions:
+ * Nothing locked.
+ * Returns:
+ * KERN_SUCCESS Clear protected payload.
+ * KERN_INVALID_TASK The space is null.
+ * KERN_INVALID_TASK The space is dead.
+ * KERN_INVALID_NAME The name doesn't denote a right.
+ * KERN_INVALID_RIGHT Name doesn't denote receive rights.
+ */
+
+kern_return_t
+mach_port_clear_protected_payload(
+ ipc_space_t space,
+ mach_port_name_t name)
+{
+ ipc_port_t port;
+ kern_return_t kr;
+
+ if (space == IS_NULL)
+ return KERN_INVALID_TASK;
+
+ kr = ipc_port_translate_receive(space, name, &port);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ /* port is locked and active */
+
+ ipc_port_clear_protected_payload(port);
+
+ ip_unlock(port);
+ return KERN_SUCCESS;
+}
+
+#if MACH_KDB
+
+void
+db_debug_port_references (boolean_t enable)
+{
+ mach_port_deallocate_debug = enable;
+}
+
+#endif /* MACH_KDB */
diff --git a/ipc/mach_port.h b/ipc/mach_port.h
new file mode 100644
index 0000000..e91e495
--- /dev/null
+++ b/ipc/mach_port.h
@@ -0,0 +1,37 @@
+/*
+ * Mach Port Functions.
+ * Copyright (C) 2008 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Barry deFreese.
+ */
+/*
+ * Mach port functions.
+ *
+ */
+
+#ifndef _IPC_MACH_PORT_H_
+#define _IPC_MACH_PORT_H_
+
+#include <sys/types.h>
+#include <ipc/ipc_types.h>
+#include <ipc/ipc_entry.h>
+
+#if MACH_KDB
+void db_debug_port_references (boolean_t enable);
+#endif /* MACH_KDB */
+
+#endif /* _IPC_MACH_PORT_H_ */
diff --git a/ipc/mach_port.srv b/ipc/mach_port.srv
new file mode 100644
index 0000000..c4f8536
--- /dev/null
+++ b/ipc/mach_port.srv
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+/* This is a server presentation file. */
+
+#define KERNEL_SERVER 1
+
+#include <mach/mach_port.defs>
diff --git a/ipc/notify.defs b/ipc/notify.defs
new file mode 100644
index 0000000..db059b8
--- /dev/null
+++ b/ipc/notify.defs
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2015 Free Software Foundation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* We use custom functions to send notifications. These functions can
+ be found in `ipc_notify.c'. We use this file merely to produce the
+ list of message ids. */
+
+#include <mach/notify.defs>
diff --git a/ipc/port.h b/ipc/port.h
new file mode 100644
index 0000000..c85685d
--- /dev/null
+++ b/ipc/port.h
@@ -0,0 +1,106 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ */
+/*
+ * File: ipc/port.h
+ * Author: Rich Draves
+ * Date: 1989
+ *
+ * Implementation specific complement to mach/port.h.
+ */
+
+#ifndef _IPC_PORT_H_
+#define _IPC_PORT_H_
+
+#include <kern/debug.h>
+#include <mach/port.h>
+
+/*
+ * mach_port_name_t must be an unsigned type. Port values
+ * have two parts, a generation number and an index.
+ * These macros encapsulate all knowledge of how
+ * a mach_port_name_t is laid out.
+ *
+ * If the size of generation numbers changes,
+ * be sure to update IE_BITS_GEN_MASK and friends
+ * in ipc/ipc_entry.h.
+ */
+
+#if PORT_GENERATIONS
+#define MACH_PORT_INDEX(name) ((name) >> 8)
+#define MACH_PORT_GEN(name) (((name) & 0xff) << 24)
+#define MACH_PORT_MAKE(index, gen) (((index) << 8) | ((gen) >> 24))
+#else
+#define MACH_PORT_INDEX(name) (name)
+#define MACH_PORT_GEN(name) 0
+#define MACH_PORT_MAKE(index, gen) (index)
+#endif
+
+#define MACH_PORT_NGEN(name) MACH_PORT_MAKE(0, MACH_PORT_GEN(name))
+#define MACH_PORT_MAKEB(index, bits) MACH_PORT_MAKE(index, IE_BITS_GEN(bits))
+
+/*
+ * Typedefs for code cleanliness. These must all have
+ * the same (unsigned) type as mach_port_name_t.
+ */
+
+typedef mach_port_name_t mach_port_gen_t; /* generation numbers */
+
+
+#define MACH_PORT_UREFS_MAX ((mach_port_urefs_t) ((1 << 16) - 1))
+
+#define MACH_PORT_UREFS_OVERFLOW(urefs, delta) \
+ (((delta) > 0) && \
+ ((((urefs) + (delta)) <= (urefs)) || \
+ (((urefs) + (delta)) > MACH_PORT_UREFS_MAX)))
+
+#define MACH_PORT_UREFS_UNDERFLOW(urefs, delta) \
+ (((delta) < 0) && (-(delta) > (urefs)))
+
+
+static inline mach_port_t invalid_name_to_port(mach_port_name_t name)
+{
+ if (name == MACH_PORT_NAME_NULL)
+ return MACH_PORT_NULL;
+ if (name == MACH_PORT_NAME_DEAD)
+ return MACH_PORT_DEAD;
+ panic("invalid_name_to_port() called with a valid port");
+}
+
+static inline mach_port_name_t invalid_port_to_name(mach_port_t port)
+{
+ if (port == MACH_PORT_NULL)
+ return MACH_PORT_NAME_NULL;
+ if (port == MACH_PORT_DEAD)
+ return MACH_PORT_NAME_DEAD;
+ panic("invalid_port_to_name() called with a valid name");
+}
+
+#endif /* _IPC_PORT_H_ */