diff options
author | Pasha <pasha@member.fsf.org> | 2024-02-20 18:49:50 +0000 |
---|---|---|
committer | Pasha <pasha@member.fsf.org> | 2024-02-20 18:49:50 +0000 |
commit | 5e0b8d508ed51004bd836384293be00950ee62c9 (patch) | |
tree | e3f16b1aa8b7177032ce3ec429fbad2b1d92a876 /ipc | |
download | gnumach-riscv-5e0b8d508ed51004bd836384293be00950ee62c9.tar.gz gnumach-riscv-5e0b8d508ed51004bd836384293be00950ee62c9.tar.bz2 |
init gnumach copy
Diffstat (limited to 'ipc')
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 = ¤t_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, ©, FALSE); + } else { + kr = vm_map_copyin(map, addr, length, + dealloc, ©); + } + 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, ©); + 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_ */ |