Source to kern/ipc_mig.c
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
* Reserved. This file contains Original Code and/or Modifications of
* Original Code as defined in and that are subject to the Apple Public
* Source License Version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. Please obtain a copy of the
* License at http://www.apple.com/publicsource and read it before using
* this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License."
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* 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 [email protected]
* 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.
*/
#include <mach/boolean.h>
#include <mach/port.h>
#include <mach/message.h>
#include <mach/thread_status.h>
#include <kern/ast.h>
#include <kern/ipc_tt.h>
#include <kern/thread.h>
#include <kern/task.h>
#include <kern/ipc_kobject.h>
#include <vm/vm_map.h>
#include <vm/vm_user.h>
#include <ipc/port.h>
#include <ipc/ipc_kmsg.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_mqueue.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <ipc/ipc_thread.h>
/*
* Routine: mach_msg_send_from_kernel
* Purpose:
* Send a message from the kernel.
*
* This is used by the client side of KernelUser interfaces
* to implement SimpleRoutines. Currently, this includes
* device_reply and memory_object messages.
* Conditions:
* Nothing locked.
* Returns:
* MACH_MSG_SUCCESS Sent the message.
* MACH_SEND_INVALID_DATA Bad destination port.
*/
mach_msg_return_t
mach_msg_send_from_kernel(msg, send_size)
mach_msg_header_t *msg;
mach_msg_size_t send_size;
{
ipc_kmsg_t kmsg;
mach_msg_return_t mr;
if (!MACH_PORT_VALID(msg->msgh_remote_port))
return MACH_SEND_INVALID_DEST;
mr = ipc_kmsg_get_from_kernel(msg, MACH_MSG_OPTION_NONE,
send_size, MAX_TRAILER_SIZE,
&kmsg);
if (mr != MACH_MSG_SUCCESS)
panic("mach_msg_send_from_kernel");
ipc_kmsg_copyin_from_kernel(kmsg);
ipc_mqueue_send_always(kmsg);
return MACH_MSG_SUCCESS;
}
/*
* Routine: mach_msg_abort_rpc
* Purpose:
* Destroy the thread's ith_rpc_reply port.
* This will interrupt a mach_msg_rpc_from_kernel
* with a MACH_RCV_PORT_DIED return code.
* Conditions:
* Nothing locked.
*/
void
mach_msg_abort_rpc(thread)
ipc_thread_t thread;
{
ipc_port_t reply = IP_NULL;
ith_lock(thread);
if (thread->ith_self != IP_NULL) {
reply = thread->ith_rpc_reply;
thread->ith_rpc_reply = IP_NULL;
}
ith_unlock(thread);
if (reply != IP_NULL)
ipc_port_dealloc_reply(reply);
}
msg_return_t
msg_send_from_kernel(msg, option, time_out)
msg_header_t *msg;
msg_option_t option;
msg_timeout_t time_out;
{
msg_size_t send_size = msg->msg_size;
ipc_kmsg_t kmsg;
integer_t send_delta = send_size;
mach_msg_return_t mr;
send_size = (send_size + 3) & ~3; /* round up */
send_delta -= send_size;
mr = ipc_kmsg_get_from_kernel((mach_msg_header_t *) msg,
MACH_MSG_OPTION_NONE,
(mach_msg_size_t) send_size,
send_delta,
&kmsg);
if (mr != MACH_MSG_SUCCESS)
return msg_return_translate(mr);
ipc_kmsg_copyin_compat_from_kernel(kmsg);
if (option & SEND_NOTIFY) {
panic("msg_send_from_kernel");
} else
mr = ipc_mqueue_send(kmsg,
((option & SEND_TIMEOUT) ?
MACH_SEND_TIMEOUT :
MACH_MSG_OPTION_NONE) |
MACH_SEND_ALWAYS | MACH_SEND_SWITCH,
(mach_msg_timeout_t) time_out,
IMQ_NULL_CONTINUE);
if (mr != MACH_MSG_SUCCESS)
ipc_kmsg_destroy(kmsg);
return msg_return_translate(mr);
}
msg_return_t
msg_send(msg, option, time_out)
msg_header_t *msg;
msg_option_t option;
msg_timeout_t time_out;
{
ipc_space_t space = current_space();
vm_map_t map = current_map();
msg_size_t send_size = msg->msg_size;
ipc_kmsg_t kmsg;
integer_t send_delta = send_size;
mach_msg_return_t mr;
send_size = (send_size + 3) & ~3; /* round up */
send_delta -= send_size;
if (send_size > MSG_SIZE_MAX)
return SEND_MSG_TOO_LARGE;
mr = ipc_kmsg_get_from_kernel((mach_msg_header_t *) msg,
MACH_MSG_OPTION_NONE,
(mach_msg_size_t) send_size,
send_delta,
&kmsg);
if (mr != MACH_MSG_SUCCESS)
return msg_return_translate(mr);
mr = ipc_kmsg_copyin_compat(kmsg, space, map);
if (mr != MACH_MSG_SUCCESS) {
ikm_free(kmsg);
return msg_return_translate(mr);
}
if (option & SEND_NOTIFY) {
panic("msg_send notify");
} else
do {
if (option & SEND_SWITCH)
mr = ipc_mqueue_send(kmsg,
((option & SEND_TIMEOUT) ?
MACH_SEND_TIMEOUT :
MACH_MSG_OPTION_NONE) |
MACH_SEND_SWITCH,
(mach_msg_timeout_t) time_out,
IMQ_NULL_CONTINUE);
else
mr = ipc_mqueue_send(kmsg,
((option & SEND_TIMEOUT) ?
MACH_SEND_TIMEOUT :
MACH_MSG_OPTION_NONE),
(mach_msg_timeout_t) time_out,
IMQ_NULL_CONTINUE);
if (mr == MACH_SEND_INTERRUPTED) {
while (thread_should_halt(current_thread()))
thread_halt_self_with_continuation(
(void (*)(void)) 0);
if (option & SEND_INTERRUPT)
break;
}
} while (mr == MACH_SEND_INTERRUPTED);
if (mr != MACH_MSG_SUCCESS)
ipc_kmsg_destroy(kmsg);
return msg_return_translate(mr);
}
msg_return_t
msg_receive(msg, option, time_out)
msg_header_t *msg;
msg_option_t option;
msg_timeout_t time_out;
{
ipc_space_t space = current_space();
vm_map_t map = current_map();
port_name_t rcv_name = msg->msg_local_port;
msg_size_t rcv_size = msg->msg_size;
ipc_object_t object;
ipc_mqueue_t mqueue;
ipc_kmsg_t kmsg;
mach_port_seqno_t seqno;
mach_msg_return_t mr;
do {
mr = ipc_mqueue_copyin(space, (mach_port_t) rcv_name,
&mqueue, &object);
if (mr != MACH_MSG_SUCCESS)
return msg_return_translate(mr);
/* hold ref for object; mqueue is locked */
mr = ipc_mqueue_receive(mqueue,
((option & RCV_TIMEOUT) ?
MACH_RCV_TIMEOUT :
MACH_MSG_OPTION_NONE) |
MACH_RCV_OLD_FORMAT,
(option & RCV_LARGE) ?
(mach_msg_size_t) rcv_size :
MACH_MSG_SIZE_MAX,
(mach_msg_timeout_t) time_out,
FALSE, IMQ_NULL_CONTINUE,
&kmsg, &seqno, 0);
/* mqueue is unlocked */
ipc_object_release(object);
if (mr == MACH_RCV_INTERRUPTED) {
while (thread_should_halt(current_thread()))
thread_halt_self_with_continuation(
(void (*)(void)) 0);
if (option & RCV_INTERRUPT)
break;
}
} while (mr == MACH_RCV_INTERRUPTED);
if (mr != MACH_MSG_SUCCESS) {
if (mr == MACH_RCV_TOO_LARGE) {
msg_size_t real_size =
(msg_size_t) (mach_msg_size_t) kmsg;
assert(real_size > rcv_size);
msg->msg_size = real_size;
}
return msg_return_translate(mr);
}
if (kmsg->ikm_header.msgh_size > (mach_msg_size_t) rcv_size) {
assert(!(option & RCV_LARGE));
ipc_kmsg_destroy(kmsg);
return msg_return_translate(MACH_RCV_TOO_LARGE);
}
assert(kmsg->ikm_header.msgh_size <= (mach_msg_size_t) rcv_size);
mr = ipc_kmsg_copyout_compat(kmsg, space, map);
assert(mr == MACH_MSG_SUCCESS);
kmsg->ikm_header.msgh_size += kmsg->ikm_delta;
ipc_kmsg_put_to_kernel((mach_msg_header_t *) msg, kmsg,
kmsg->ikm_header.msgh_size);
return msg_return_translate(mr);
}
msg_return_t
msg_rpc(msg, option, rcv_size, send_timeout, rcv_timeout)
msg_header_t *msg; /* in/out */
msg_option_t option;
msg_size_t rcv_size;
msg_timeout_t send_timeout;
msg_timeout_t rcv_timeout;
{
ipc_space_t space = current_space();
vm_map_t map = current_map();
msg_size_t send_size = msg->msg_size;
ipc_port_t reply;
ipc_pset_t pset;
ipc_mqueue_t mqueue;
ipc_kmsg_t kmsg;
integer_t send_delta = send_size;
mach_port_seqno_t seqno;
mach_msg_return_t mr;
/*
* Instead of using msg_send and msg_receive,
* we implement msg_rpc directly. The difference
* is how the reply port is handled. Instead of using
* ipc_mqueue_copyin, we save a reference for the reply
* port carried in the sent message. For example,
* consider a rename kernel call which changes the name
* of the call's own reply port. This is the behaviour
* of the Mach 2.5 msg_rpc_trap.
*/
send_size = (send_size + 3) & ~3; /* round up */
send_delta -= send_size;
if (send_size > MSG_SIZE_MAX)
return SEND_MSG_TOO_LARGE;
mr = ipc_kmsg_get_from_kernel((mach_msg_header_t *) msg,
MACH_MSG_OPTION_NONE,
(mach_msg_size_t) send_size,
send_delta,
&kmsg);
if (mr != MACH_MSG_SUCCESS)
return msg_return_translate(mr);
mr = ipc_kmsg_copyin_compat(kmsg, space, map);
if (mr != MACH_MSG_SUCCESS) {
ikm_free(kmsg);
return msg_return_translate(mr);
}
reply = (ipc_port_t)kmsg->ikm_header.msgh_local_port;
if (IP_VALID(reply)) {
ipc_port_t dest = (ipc_port_t)
kmsg->ikm_header.msgh_remote_port;
ipc_port_reference(reply);
assert(IP_VALID(dest));
ip_lock(dest);
if (dest->ip_receiver == ipc_space_kernel) {
ipc_mqueue_t mqueue;
assert(ip_active(dest));
ip_unlock(dest);
kmsg = ipc_kobject_server(kmsg);
if (kmsg == IKM_NULL)
goto receive_reply;
ip_lock(reply);
if ((!ip_active(reply)) ||
(reply->ip_receiver != space) ||
(reply->ip_pset != IPS_NULL) ||
(rcv_size < (kmsg->ikm_header.msgh_size +
kmsg->ikm_delta)))
{
ip_unlock(reply);
ipc_mqueue_send_always(kmsg);
goto receive_reply;
}
mqueue = &reply->ip_messages;
imq_lock(mqueue);
if ((ipc_thread_queue_first(&mqueue->imq_threads)
!= ITH_NULL) ||
(ipc_kmsg_queue_first(&mqueue->imq_messages)
!= IKM_NULL))
{
imq_unlock(mqueue);
ip_unlock(reply);
ipc_mqueue_send_always(kmsg);
goto receive_reply;
}
assert(kmsg->ikm_marequest == IMAR_NULL);
assert(ipc_thread_queue_first(&reply->ip_blocked)
== ITH_NULL);
reply->ip_seqno++;
imq_unlock(mqueue);
ip_release(reply);
ip_unlock(reply);
goto copyout_reply;
}
ip_unlock(dest);
}
if (option & SEND_NOTIFY) {
panic("msg_rpc notify");
} else
do {
if (option & SEND_SWITCH)
mr = ipc_mqueue_send(kmsg,
((option & SEND_TIMEOUT) ?
MACH_SEND_TIMEOUT :
MACH_MSG_OPTION_NONE) |
MACH_SEND_SWITCH,
(mach_msg_timeout_t)
send_timeout,
IMQ_NULL_CONTINUE);
else
mr = ipc_mqueue_send(kmsg,
((option & SEND_TIMEOUT) ?
MACH_SEND_TIMEOUT :
MACH_MSG_OPTION_NONE),
(mach_msg_timeout_t)
send_timeout,
IMQ_NULL_CONTINUE);
if (mr == MACH_SEND_INTERRUPTED) {
while (thread_should_halt(current_thread()))
thread_halt_self_with_continuation(
(void (*)(void)) 0);
if (option & SEND_INTERRUPT)
break;
}
} while (mr == MACH_SEND_INTERRUPTED);
if (mr != MACH_MSG_SUCCESS) {
ipc_kmsg_destroy(kmsg);
if (IP_VALID(reply))
ipc_port_release(reply);
return msg_return_translate(mr);
}
receive_reply:
if (!IP_VALID(reply))
return RCV_INVALID_PORT;
ip_lock(reply);
if (reply->ip_receiver != space) {
ip_release(reply);
ip_check_unlock(reply);
return RCV_INVALID_PORT;
}
assert(ip_active(reply));
pset = reply->ip_pset;
if (pset != IPS_NULL) {
ips_lock(pset);
if (ips_active(pset)) {
ips_unlock(pset);
ip_release(reply);
ip_unlock(reply);
return RCV_INVALID_PORT;
}
ipc_pset_remove(pset, reply);
ips_check_unlock(pset);
assert(reply->ip_pset == IPS_NULL);
}
mqueue = &reply->ip_messages;
imq_lock(mqueue);
ip_unlock(reply);
mr = ipc_mqueue_receive(mqueue,
((option & RCV_TIMEOUT) ?
MACH_RCV_TIMEOUT :
MACH_MSG_OPTION_NONE) |
MACH_RCV_OLD_FORMAT,
(option & RCV_LARGE) ?
(mach_msg_size_t) rcv_size :
MACH_MSG_SIZE_MAX,
(mach_msg_timeout_t) rcv_timeout,
FALSE, IMQ_NULL_CONTINUE,
&kmsg, &seqno, 0);
/* mqueue is unlocked */
ipc_port_release(reply);
if (mr != MACH_MSG_SUCCESS) {
if (mr == MACH_RCV_INTERRUPTED) {
while (thread_should_halt(current_thread()))
thread_halt_self_with_continuation(
(void (*)(void)) 0);
msg->msg_size = rcv_size; /* XXX */
if (!(option & RCV_INTERRUPT))
return msg_receive(msg, option, rcv_timeout);
} else if (mr == MACH_RCV_TOO_LARGE) {
msg_size_t real_size =
(msg_size_t) (mach_msg_size_t) kmsg;
assert(real_size > rcv_size);
msg->msg_size = real_size;
}
return msg_return_translate(mr);
}
if (kmsg->ikm_header.msgh_size > (mach_msg_size_t) rcv_size) {
assert(!(option & RCV_LARGE));
ipc_kmsg_destroy(kmsg);
return msg_return_translate(MACH_RCV_TOO_LARGE);
}
copyout_reply:
assert(kmsg->ikm_header.msgh_size <= (mach_msg_size_t) rcv_size);
mr = ipc_kmsg_copyout_compat(kmsg, space, map);
assert(mr == MACH_MSG_SUCCESS);
kmsg->ikm_header.msgh_size += kmsg->ikm_delta;
ipc_kmsg_put_to_kernel((mach_msg_header_t *) msg, kmsg,
kmsg->ikm_header.msgh_size);
return msg_return_translate(mr);
}
/*
* Routine: mig_get_reply_port
* Purpose:
* Called by client side interfaces living in the kernel
* to get a reply port. This port is used for
* mach_msg() calls which are kernel calls.
*/
mach_port_t
mig_get_reply_port()
{
ipc_thread_t self = current_thread();
if (self->ith_mig_reply == MACH_PORT_NULL)
self->ith_mig_reply = mach_reply_port();
return self->ith_mig_reply;
}
/*
* Routine: mig_dealloc_reply_port
* Purpose:
* Called by client side interfaces to get rid of a reply port.
* Shouldn't ever be called inside the kernel, because
* kernel calls shouldn't prompt Mig to call it.
*/
void
mig_dealloc_reply_port()
{
panic("mig_dealloc_reply_port");
}
/*
* mig_strncpy.c - by Joshua Block
*
* mig_strncp -- Bounded string copy. Does what the library routine strncpy
* OUGHT to do: Copies the (null terminated) string in src into dest, a
* buffer of length len. Assures that the copy is still null terminated
* and doesn't overflow the buffer, truncating the copy if necessary.
*
* Parameters:
*
* dest - Pointer to destination buffer.
*
* src - Pointer to source string.
*
* len - Length of destination buffer.
*/
vm_size_t mig_strncpy(
char *dest,
const char *src,
vm_size_t len)
{
vm_size_t i;
if (len <= 0)
return 0;
for (i=1; i<len; i++)
if (! (*dest++ = *src++))
return i;
*dest = '\0';
return i;
}
#if 0
#define fast_send_right_lookup(name, port, abort) \
MACRO_BEGIN \
register ipc_space_t space = current_space(); \
register ipc_entry_t entry; \
register mach_port_index_t index = MACH_PORT_INDEX(name); \
\
is_read_lock(space); \
assert(space->is_active); \
\
if ((index >= space->is_table_size) || \
(((entry = &space->is_table[index])->ie_bits & \
(IE_BITS_GEN_MASK|MACH_PORT_TYPE_SEND)) != \
(MACH_PORT_GEN(name) | MACH_PORT_TYPE_SEND))) { \
is_read_unlock(space); \
abort; \
} \
\
port = (ipc_port_t) entry->ie_object; \
assert(port != IP_NULL); \
\
ip_lock(port); \
/* can safely unlock space now that port is locked */ \
is_read_unlock(space); \
MACRO_END
thread_t
port_name_to_thread(name)
mach_port_t name;
{
register ipc_port_t port;
fast_send_right_lookup(name, port, goto abort);
/* port is locked */
if (ip_active(port) &&
(ip_kotype(port) == IKOT_THREAD)) {
register thread_t thread;
thread = (thread_t) port->ip_kobject;
assert(thread != THREAD_NULL);
/* thread referencing is a bit complicated,
so don't bother to expand inline */
thread_reference(thread);
ip_unlock(port);
return thread;
}
ip_unlock(port);
return THREAD_NULL;
abort: {
thread_t thread;
ipc_port_t kern_port;
kern_return_t kr;
kr = ipc_object_copyin(current_space(), name,
MACH_MSG_TYPE_COPY_SEND,
(ipc_object_t *) &kern_port);
if (kr != KERN_SUCCESS)
return THREAD_NULL;
thread = convert_port_to_thread(kern_port);
if (IP_VALID(kern_port))
ipc_port_release_send(kern_port);
return thread;
}
}
task_t
port_name_to_task(name)
mach_port_t name;
{
register ipc_port_t port;
fast_send_right_lookup(name, port, goto abort);
/* port is locked */
if (ip_active(port) &&
(ip_kotype(port) == IKOT_TASK)) {
register task_t task;
task = (task_t) port->ip_kobject;
assert(task != TASK_NULL);
task_lock(task);
/* can safely unlock port now that task is locked */
ip_unlock(port);
task->ref_count++;
task_unlock(task);
return task;
}
ip_unlock(port);
return TASK_NULL;
abort: {
task_t task;
ipc_port_t kern_port;
kern_return_t kr;
kr = ipc_object_copyin(current_space(), name,
MACH_MSG_TYPE_COPY_SEND,
(ipc_object_t *) &kern_port);
if (kr != KERN_SUCCESS)
return TASK_NULL;
task = convert_port_to_task(kern_port);
if (IP_VALID(kern_port))
ipc_port_release_send(kern_port);
return task;
}
}
vm_map_t
port_name_to_map(name)
mach_port_t name;
{
register ipc_port_t port;
fast_send_right_lookup(name, port, goto abort);
/* port is locked */
if (ip_active(port) &&
(ip_kotype(port) == IKOT_TASK)) {
register vm_map_t map;
map = ((task_t) port->ip_kobject)->map;
assert(map != VM_MAP_NULL);
simple_lock(&map->ref_lock);
/* can safely unlock port now that map is locked */
ip_unlock(port);
map->ref_count++;
simple_unlock(&map->ref_lock);
return map;
}
ip_unlock(port);
return VM_MAP_NULL;
abort: {
vm_map_t map;
ipc_port_t kern_port;
kern_return_t kr;
kr = ipc_object_copyin(current_space(), name,
MACH_MSG_TYPE_COPY_SEND,
(ipc_object_t *) &kern_port);
if (kr != KERN_SUCCESS)
return VM_MAP_NULL;
map = convert_port_to_map(kern_port);
if (IP_VALID(kern_port))
ipc_port_release_send(kern_port);
return map;
}
}
ipc_space_t
port_name_to_space(name)
mach_port_t name;
{
register ipc_port_t port;
fast_send_right_lookup(name, port, goto abort);
/* port is locked */
if (ip_active(port) &&
(ip_kotype(port) == IKOT_TASK)) {
register ipc_space_t space;
space = ((task_t) port->ip_kobject)->itk_space;
assert(space != IS_NULL);
simple_lock(&space->is_ref_lock_data);
/* can safely unlock port now that space is locked */
ip_unlock(port);
space->is_references++;
simple_unlock(&space->is_ref_lock_data);
return space;
}
ip_unlock(port);
return IS_NULL;
abort: {
ipc_space_t space;
ipc_port_t kern_port;
kern_return_t kr;
kr = ipc_object_copyin(current_space(), name,
MACH_MSG_TYPE_COPY_SEND,
(ipc_object_t *) &kern_port);
if (kr != KERN_SUCCESS)
return IS_NULL;
space = convert_port_to_space(kern_port);
if (IP_VALID(kern_port))
ipc_port_release_send(kern_port);
return space;
}
}
/*
* Hack to translate a thread port to a thread pointer for calling
* thread_get_state and thread_set_state. This is only necessary
* because the IPC message for these two operations overflows the
* kernel stack.
*
* AARGH!
*/
kern_return_t thread_get_state_KERNEL(thread_port, flavor,
old_state, old_state_count)
mach_port_t thread_port; /* port right for thread */
int flavor;
thread_state_t old_state; /* pointer to OUT array */
natural_t *old_state_count; /* IN/OUT */
{
thread_t thread;
kern_return_t result;
thread = port_name_to_thread(thread_port);
result = thread_get_state(thread, flavor, old_state, old_state_count);
thread_deallocate(thread);
return result;
}
kern_return_t thread_set_state_KERNEL(thread_port, flavor,
new_state, new_state_count)
mach_port_t thread_port; /* port right for thread */
int flavor;
thread_state_t new_state;
natural_t new_state_count;
{
thread_t thread;
kern_return_t result;
thread = port_name_to_thread(thread_port);
result = thread_set_state(thread, flavor, new_state, new_state_count);
thread_deallocate(thread);
return result;
}
/*
* Things to keep in mind:
*
* The idea here is to duplicate the semantics of the true kernel RPC.
* The destination port/object should be checked first, before anything
* that the user might notice (like ipc_object_copyin). Return
* MACH_SEND_INTERRUPTED if it isn't correct, so that the user stub
* knows to fall back on an RPC. For other return values, it won't
* retry with an RPC. The retry might get a different (incorrect) rc.
* Return values are only set (and should only be set, with copyout)
* on successfull calls.
*/
kern_return_t syscall_vm_map(
target_map,
address, size, mask, anywhere,
memory_object, offset,
copy,
cur_protection, max_protection, inheritance)
mach_port_t target_map;
vm_offset_t *address;
vm_size_t size;
vm_offset_t mask;
boolean_t anywhere;
mach_port_t memory_object;
vm_offset_t offset;
boolean_t copy;
vm_prot_t cur_protection;
vm_prot_t max_protection;
vm_inherit_t inheritance;
{
vm_map_t map;
ipc_port_t port;
vm_offset_t addr;
kern_return_t result;
map = port_name_to_map(target_map);
if (map == VM_MAP_NULL)
return MACH_SEND_INTERRUPTED;
if (MACH_PORT_VALID(memory_object)) {
result = ipc_object_copyin(current_space(), memory_object,
MACH_MSG_TYPE_COPY_SEND,
(ipc_object_t *) &port);
if (result != KERN_SUCCESS) {
vm_map_deallocate(map);
return result;
}
} else
port = (ipc_port_t) memory_object;
copyin((char *)address, (char *)&addr, sizeof(vm_offset_t));
result = vm_map(map, &addr, size, mask, anywhere,
port, offset, copy,
cur_protection, max_protection, inheritance);
if (result == KERN_SUCCESS)
copyout((char *)&addr, (char *)address, sizeof(vm_offset_t));
if (IP_VALID(port))
ipc_port_release_send(port);
vm_map_deallocate(map);
return result;
}
kern_return_t syscall_vm_allocate(target_map, address, size, anywhere)
mach_port_t target_map;
vm_offset_t *address;
vm_size_t size;
boolean_t anywhere;
{
vm_map_t map;
vm_offset_t addr;
kern_return_t result;
map = port_name_to_map(target_map);
if (map == VM_MAP_NULL)
return MACH_SEND_INTERRUPTED;
copyin((char *)address, (char *)&addr, sizeof(vm_offset_t));
result = vm_allocate(map, &addr, size, anywhere);
if (result == KERN_SUCCESS)
copyout((char *)&addr, (char *)address, sizeof(vm_offset_t));
vm_map_deallocate(map);
return result;
}
kern_return_t syscall_vm_deallocate(target_map, start, size)
mach_port_t target_map;
vm_offset_t start;
vm_size_t size;
{
vm_map_t map;
kern_return_t result;
map = port_name_to_map(target_map);
if (map == VM_MAP_NULL)
return MACH_SEND_INTERRUPTED;
result = vm_deallocate(map, start, size);
vm_map_deallocate(map);
return result;
}
kern_return_t syscall_task_create(parent_task, inherit_memory, child_task)
mach_port_t parent_task;
boolean_t inherit_memory;
mach_port_t *child_task; /* OUT */
{
task_t t, c;
ipc_port_t port;
mach_port_t name;
kern_return_t result;
t = port_name_to_task(parent_task);
if (t == TASK_NULL)
return MACH_SEND_INTERRUPTED;
result = task_create(t, inherit_memory, &c);
if (result == KERN_SUCCESS) {
port = (ipc_port_t) convert_task_to_port(c);
/* always returns a name, even for non-success return codes */
(void) ipc_kmsg_copyout_object(current_space(),
(ipc_object_t) port,
MACH_MSG_TYPE_PORT_SEND, &name);
copyout((char *)&name, (char *)child_task,
sizeof(mach_port_t));
}
task_deallocate(t);
return result;
}
kern_return_t syscall_task_terminate(task)
mach_port_t task;
{
task_t t;
kern_return_t result;
t = port_name_to_task(task);
if (t == TASK_NULL)
return MACH_SEND_INTERRUPTED;
result = task_terminate(t);
task_deallocate(t);
return result;
}
kern_return_t syscall_task_suspend(task)
mach_port_t task;
{
task_t t;
kern_return_t result;
t = port_name_to_task(task);
if (t == TASK_NULL)
return MACH_SEND_INTERRUPTED;
result = task_suspend(t);
task_deallocate(t);
return result;
}
kern_return_t syscall_task_set_special_port(task, which_port, port_name)
mach_port_t task;
int which_port;
mach_port_t port_name;
{
task_t t;
ipc_port_t port;
kern_return_t result;
t = port_name_to_task(task);
if (t == TASK_NULL)
return MACH_SEND_INTERRUPTED;
if (MACH_PORT_VALID(port_name)) {
result = ipc_object_copyin(current_space(), port_name,
MACH_MSG_TYPE_COPY_SEND,
(ipc_object_t *) &port);
if (result != KERN_SUCCESS) {
task_deallocate(t);
return result;
}
} else
port = (ipc_port_t) port_name;
result = task_set_special_port(t, which_port, port);
if ((result != KERN_SUCCESS) && IP_VALID(port))
ipc_port_release_send(port);
task_deallocate(t);
return result;
}
kern_return_t
syscall_mach_port_allocate(task, right, namep)
mach_port_t task;
mach_port_right_t right;
mach_port_t *namep;
{
ipc_space_t space;
mach_port_t name;
kern_return_t kr;
space = port_name_to_space(task);
if (space == IS_NULL)
return MACH_SEND_INTERRUPTED;
kr = mach_port_allocate(space, right, &name);
if (kr == KERN_SUCCESS)
copyout((char *)&name, (char *)namep, sizeof(mach_port_t));
is_release(space);
return kr;
}
kern_return_t
syscall_mach_port_allocate_name(task, right, name)
mach_port_t task;
mach_port_right_t right;
mach_port_t name;
{
ipc_space_t space;
kern_return_t kr;
space = port_name_to_space(task);
if (space == IS_NULL)
return MACH_SEND_INTERRUPTED;
kr = mach_port_allocate_name(space, right, name);
is_release(space);
return kr;
}
kern_return_t
syscall_mach_port_deallocate(task, name)
mach_port_t task;
mach_port_t name;
{
ipc_space_t space;
kern_return_t kr;
space = port_name_to_space(task);
if (space == IS_NULL)
return MACH_SEND_INTERRUPTED;
kr = mach_port_deallocate(space, name);
is_release(space);
return kr;
}
kern_return_t
syscall_mach_port_insert_right(task, name, right, rightType)
mach_port_t task;
mach_port_t name;
mach_port_t right;
mach_msg_type_name_t rightType;
{
ipc_space_t space;
ipc_object_t object;
mach_msg_type_name_t newtype;
kern_return_t kr;
space = port_name_to_space(task);
if (space == IS_NULL)
return MACH_SEND_INTERRUPTED;
if (!MACH_MSG_TYPE_PORT_ANY(rightType)) {
is_release(space);
return KERN_INVALID_VALUE;
}
if (MACH_PORT_VALID(right)) {
kr = ipc_object_copyin(current_space(), right, rightType,
&object);
if (kr != KERN_SUCCESS) {
is_release(space);
return kr;
}
} else
object = (ipc_object_t) right;
newtype = ipc_object_copyin_type(rightType);
kr = mach_port_insert_right(space, name, object, newtype);
if ((kr != KERN_SUCCESS) && IO_VALID(object))
ipc_object_destroy(object, newtype);
is_release(space);
return kr;
}
kern_return_t syscall_thread_depress_abort(thread)
mach_port_t thread;
{
thread_t t;
kern_return_t result;
t = port_name_to_thread(thread);
if (t == THREAD_NULL)
return MACH_SEND_INTERRUPTED;
result = thread_depress_abort(t);
thread_deallocate(t);
return result;
}
#endif