Source to kern/exception.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,1989,1988,1987 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 <norma_ipc.h>
#include <mach_kdb.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/message.h>
#include <mach/port.h>
#include <mach/mig_errors.h>
#include <ipc/port.h>
#include <ipc/ipc_entry.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <ipc/mach_msg.h>
#include <ipc/ipc_machdep.h>
#include <kern/counters.h>
#include <kern/ipc_tt.h>
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/processor.h>
#include <kern/sched.h>
#include <kern/sched_prim.h>
extern void exception();
extern void exception_try_task();
extern void exception_no_server();
extern void exception_raise();
extern kern_return_t exception_parse_reply();
extern void exception_raise_continue();
extern void exception_raise_continue_slow();
extern void exception_raise_continue_fast();
#if MACH_KDB
extern void thread_kdb_return();
extern void db_printf();
boolean_t debug_user_with_kdb = FALSE;
#endif MACH_KDB
#ifdef KEEP_STACKS
/*
* Some obsolete architectures don't support kernel stack discarding
* or the thread_exception_return, thread_syscall_return continuations.
* For these architectures, the NOTREACHED comments below are incorrect.
* The exception function is expected to return.
* So the return statements along the slow paths are important.
*/
#endif KEEP_STACKS
/*
* Routine: exception
* Purpose:
* The current thread caught an exception.
* We make an up-call to the thread's exception server.
* Conditions:
* Nothing locked and no resources held.
* Called from an exception context, so
* thread_exception_return and thread_kdb_return
* are possible.
* Returns:
* Doesn't return.
*/
__inline__
void
exception_with_continuation(_exception, code, subcode, continuation)
int _exception, code, subcode;
void (*continuation)(void);
{
register ipc_thread_t self = current_thread();
register ipc_port_t exc_port;
if (_exception == KERN_SUCCESS)
panic("exception");
self->exc_func = continuation;
/*
* Optimized version of retrieve_thread_exception.
*/
ith_lock(self);
assert(self->ith_self != IP_NULL);
exc_port = self->ith_exception;
if (!IP_VALID(exc_port)) {
ith_unlock(self);
exception_try_task(_exception, code, subcode);
/*NOTREACHED*/
return;
}
ip_lock(exc_port);
ith_unlock(self);
if (!ip_active(exc_port)) {
ip_unlock(exc_port);
exception_try_task(_exception, code, subcode);
/*NOTREACHED*/
return;
}
/*
* Make a naked send right for the exception port.
*/
ip_reference(exc_port);
exc_port->ip_srights++;
ip_unlock(exc_port);
/*
* If this exception port doesn't work,
* we will want to try the task's exception port.
* Indicate this by saving the exception state.
*/
self->ith_exc = _exception;
self->ith_exc_code = code;
self->ith_exc_subcode = subcode;
exception_raise(exc_port,
retrieve_thread_self_fast(self),
retrieve_task_self_fast(self->task),
_exception, code, subcode);
/*NOTREACHED*/
}
void
exception(_exception, code, subcode)
int _exception, code, subcode;
{
exception_with_continuation(
_exception, code, subcode, thread_exception_return);
/* NOTREACHED */
}
void
exception_from_kernel(_exception, code, subcode)
int _exception, code, subcode;
{
ipc_thread_t self = current_thread();
void (*old_continuation)(void) = self->exc_func;
int old_exception = self->ith_exc,
old_code = self->ith_exc_code,
old_subcode = self->ith_exc_subcode;
exception_with_continuation(_exception, code, subcode, 0);
self->exc_func = old_continuation;
self->ith_exc = old_exception;
self->ith_exc_code = old_code;
self->ith_exc_subcode = old_subcode;
}
/*
* Routine: exception_try_task
* Purpose:
* The current thread caught an exception.
* We make an up-call to the task's exception server.
* Conditions:
* Nothing locked and no resources held.
* Called from an exception context, so
* thread_exception_return and thread_kdb_return
* are possible.
* Returns:
* Doesn't return.
*/
void
exception_try_task(_exception, code, subcode)
int _exception, code, subcode;
{
ipc_thread_t self = current_thread();
register task_t task = self->task;
register ipc_port_t exc_port;
/*
* Optimized version of retrieve_task_exception.
*/
itk_lock(task);
assert(task->itk_self != IP_NULL);
exc_port = task->itk_exception;
if (!IP_VALID(exc_port)) {
itk_unlock(task);
exception_no_server();
/*NOTREACHED*/
return;
}
ip_lock(exc_port);
itk_unlock(task);
if (!ip_active(exc_port)) {
ip_unlock(exc_port);
exception_no_server();
/*NOTREACHED*/
return;
}
/*
* Make a naked send right for the exception port.
*/
ip_reference(exc_port);
exc_port->ip_srights++;
ip_unlock(exc_port);
/*
* This is the thread's last chance.
* Clear the saved exception state.
*/
self->ith_exc = KERN_SUCCESS;
exception_raise(exc_port,
retrieve_thread_self_fast(self),
retrieve_task_self_fast(task),
_exception, code, subcode);
/*NOTREACHED*/
}
/*
* Routine: exception_no_server
* Purpose:
* The current thread took an exception,
* and no exception server took responsibility
* for the exception. So good bye, charlie.
* Conditions:
* Nothing locked and no resources held.
* Called from an exception context, so
* thread_kdb_return is possible.
* Returns:
* Doesn't return.
*/
void
exception_no_server()
{
register ipc_thread_t self = current_thread();
/*
* If this thread is being terminated, cooperate.
*/
while (thread_should_halt(self))
thread_halt_self();
#if MACH_KDB
if (debug_user_with_kdb) {
/*
* Debug the exception with kdb.
* If kdb handles the exception,
* then thread_kdb_return won't return.
*/
db_printf("No exception server, calling kdb...\n");
thread_kdb_return();
}
#endif MACH_KDB
/*
* Try to perform a clean exit.
*/
process_terminate_self();
/*
* All else failed; terminate task.
*/
(void) task_terminate(self->task);
thread_halt_self();
/*NOTREACHED*/
}
#define MACH_EXCEPTION_ID 2400 /* from mach/exc.defs */
#define MACH_EXCEPTION_REPLY_ID (MACH_EXCEPTION_ID + 100)
struct mach_exception {
mach_msg_header_t Head;
mach_msg_type_t threadType;
mach_port_t thread;
mach_msg_type_t taskType;
mach_port_t task;
mach_msg_type_t exceptionType;
int exception;
mach_msg_type_t codeType;
int code;
mach_msg_type_t subcodeType;
int subcode;
};
mach_msg_type_t exc_port_proto = {
/* msgt_name = */ MACH_MSG_TYPE_PORT_SEND,
/* msgt_size = */ PORT_T_SIZE_IN_BITS,
/* msgt_number = */ 1,
/* msgt_inline = */ TRUE,
/* msgt_longform = */ FALSE,
/* msgt_deallocate = */ FALSE,
/* msgt_unused = */ 0
};
mach_msg_type_t exc_code_proto = {
/* msgt_name = */ MACH_MSG_TYPE_INTEGER_32,
/* msgt_size = */ 32,
/* msgt_number = */ 1,
/* msgt_inline = */ TRUE,
/* msgt_longform = */ FALSE,
/* msgt_deallocate = */ FALSE,
/* msgt_unused = */ 0
};
/*
* Routine: exception_raise
* Purpose:
* Make an exception_raise up-call to an exception server.
*
* dest_port must be a valid naked send right.
* thread_port and task_port are naked send rights.
* All three are always consumed.
*
* self->ith_exc, self->ith_exc_code, self->ith_exc_subcode
* must be appropriately initialized.
* Conditions:
* Nothing locked. We are being called in an exception context,
* so thread_exception_return may be called.
* Returns:
* Doesn't return.
*/
int exception_raise_misses = 0;
void
exception_raise(dest_port, thread_port, task_port,
_exception, code, subcode)
ipc_port_t dest_port;
ipc_port_t thread_port;
ipc_port_t task_port;
int _exception, code, subcode;
{
ipc_thread_t self = current_thread();
ipc_thread_t receiver;
ipc_port_t reply_port;
ipc_mqueue_t dest_mqueue;
ipc_mqueue_t reply_mqueue;
ipc_kmsg_t kmsg;
mach_msg_return_t mr;
assert(IP_VALID(dest_port));
/*
* We will eventually need a message buffer.
* Grab the buffer now, while nothing is locked.
* This buffer will get handed to the exception server,
* and it will give the buffer back with its reply.
*/
kmsg = ikm_cache();
if (kmsg != IKM_NULL) {
ikm_cache() = IKM_NULL;
ikm_check_initialized(kmsg, IKM_SAVED_KMSG_SIZE);
} else {
kmsg = ikm_alloc(IKM_SAVED_MSG_SIZE);
if (kmsg == IKM_NULL)
panic("exception_raise");
ikm_init(kmsg, IKM_SAVED_MSG_SIZE);
}
kmsg->ikm_sender = KERNEL_SECURITY_ID_VALUE;
/*
* We need a reply port for the RPC.
* Check first for a cached port.
*/
ith_lock(self);
assert(self->ith_self != IP_NULL);
reply_port = self->ith_rpc_reply;
if (reply_port == IP_NULL) {
ith_unlock(self);
reply_port = ipc_port_alloc_reply();
ith_lock(self);
if ((reply_port == IP_NULL) ||
(self->ith_rpc_reply != IP_NULL))
panic("exception_raise");
self->ith_rpc_reply = reply_port;
}
ip_lock(reply_port);
assert(ip_active(reply_port));
ith_unlock(self);
/*
* Make a naked send-once right for the reply port,
* to hand to the exception server.
* Make an extra reference for the reply port,
* to receive on. This protects us against
* mach_msg_abort_rpc.
*/
reply_port->ip_sorights++;
ip_reference(reply_port);
ip_reference(reply_port);
self->ith_port = reply_port;
reply_mqueue = &reply_port->ip_messages;
imq_lock(reply_mqueue);
assert(ipc_kmsg_queue_empty(&reply_mqueue->imq_messages));
ip_unlock(reply_port);
#if 0
/*
* Make sure we can queue to the destination port.
*/
if (!ip_lock_try(dest_port)) {
imq_unlock(reply_mqueue);
goto slow_exception_raise;
}
if (!ip_active(dest_port) ||
#if NORMA_IPC
IP_NORMA_IS_PROXY(dest_port) ||
#endif NORMA_IPC
(dest_port->ip_receiver == ipc_space_kernel)) {
imq_unlock(reply_mqueue);
ip_unlock(dest_port);
goto slow_exception_raise;
}
/*
* Find the destination message queue.
*/
{
register 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)) {
imq_unlock(reply_mqueue);
ip_unlock(dest_port);
goto slow_exception_raise;
}
/*
* Safe to unlock dest_port, because we hold
* dest_mqueue locked. We never bother changing
* dest_port->ip_msgcount.
*/
ip_unlock(dest_port);
receiver = ipc_thread_queue_first(&dest_mqueue->imq_threads);
if ((receiver == ITH_NULL) ||
(self->exc_func == 0) ||
!((receiver->swap_func == (void (*)()) mach_msg_continue) ||
((receiver->swap_func ==
(void (*)()) mach_msg_receive_continue) &&
(sizeof(struct mach_exception) <= receiver->ith_msize) &&
((receiver->ith_option & MACH_RCV_NOTIFY) == 0))) ||
!thread_handoff(self, exception_raise_continue, receiver)) {
imq_unlock(reply_mqueue);
imq_unlock(dest_mqueue);
goto slow_exception_raise;
}
counter(c_exception_raise_block++);
assert(current_thread() == receiver);
/*
* We need to finish preparing self for its
* time asleep in reply_mqueue. self is left
* holding the extra ref for reply_port.
*/
ipc_thread_enqueue_macro(&reply_mqueue->imq_threads, self);
self->ith_state = MACH_RCV_IN_PROGRESS;
self->ith_msize = MACH_MSG_SIZE_MAX;
imq_unlock(reply_mqueue);
/*
* Finish extracting receiver from dest_mqueue.
*/
ipc_thread_rmqueue_first_macro(
&dest_mqueue->imq_threads, receiver);
imq_unlock(dest_mqueue);
/*
* Release the receiver's reference for his object.
*/
{
register ipc_object_t object = receiver->ith_object;
io_lock(object);
io_release(object);
io_check_unlock(object);
}
{
register struct mach_exception *exc =
(struct mach_exception *) &kmsg->ikm_header;
ipc_space_t space = receiver->task->itk_space;
/*
* We are running as the receiver now. We hold
* the following resources, which must be consumed:
* kmsg, send-once right for reply_port
* send rights for dest_port, thread_port, task_port
* Synthesize a kmsg for copyout to the receiver.
*/
exc->Head.msgh_bits = (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
MACH_MSG_TYPE_PORT_SEND) |
MACH_MSGH_BITS_COMPLEX);
exc->Head.msgh_size = sizeof *exc;
/* exc->Head.msgh_remote_port later */
/* exc->Head.msgh_local_port later */
exc->Head.msgh_reserved = 0;
exc->Head.msgh_id = MACH_EXCEPTION_ID;
exc->threadType = exc_port_proto;
/* exc->thread later */
exc->taskType = exc_port_proto;
/* exc->task later */
exc->exceptionType = exc_code_proto;
exc->exception = _exception;
exc->codeType = exc_code_proto;
exc->code = code;
exc->subcodeType = exc_code_proto;
exc->subcode = subcode;
/*
* Check that the receiver can handle the message.
*/
if (receiver->ith_rcv_size < sizeof(struct mach_exception)) {
/*
* ipc_kmsg_destroy is a handy way to consume
* the resources we hold, but it requires setup.
*/
exc->Head.msgh_bits =
(MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
MACH_MSG_TYPE_PORT_SEND_ONCE) |
MACH_MSGH_BITS_COMPLEX);
exc->Head.msgh_remote_port = (mach_port_t) dest_port;
exc->Head.msgh_local_port = (mach_port_t) reply_port;
exc->thread = (mach_port_t) thread_port;
exc->task = (mach_port_t) task_port;
ipc_kmsg_destroy(kmsg);
thread_syscall_return(MACH_RCV_TOO_LARGE);
/*NOTREACHED*/
}
is_write_lock(space);
assert(space->is_active);
/*
* To do an atomic copyout, need simultaneous
* locks on both ports and the space.
*/
ip_lock(dest_port);
if (!ip_active(dest_port) ||
!ip_lock_try(reply_port)) {
abort_copyout:
ip_unlock(dest_port);
is_write_unlock(space);
/*
* Oh well, we have to do the header the slow way.
* First make it look like it's in-transit.
*/
exc->Head.msgh_bits =
(MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
MACH_MSG_TYPE_PORT_SEND_ONCE) |
MACH_MSGH_BITS_COMPLEX);
exc->Head.msgh_remote_port = (mach_port_t) dest_port;
exc->Head.msgh_local_port = (mach_port_t) reply_port;
mr = ipc_kmsg_copyout_header(&exc->Head, space,
MACH_PORT_NULL);
if (mr == MACH_MSG_SUCCESS)
goto copyout_body;
/*
* Ack! Prepare for ipc_kmsg_copyout_dest.
* It will consume thread_port and task_port.
*/
exc->thread = (mach_port_t) thread_port;
exc->task = (mach_port_t) task_port;
ipc_kmsg_copyout_dest(kmsg, space);
(void) ipc_kmsg_put(receiver->ith_msg, kmsg,
sizeof(mach_msg_header_t));
thread_syscall_return(mr);
/*NOTREACHED*/
}
if (!ip_active(reply_port)) {
ip_unlock(reply_port);
goto abort_copyout;
}
assert(reply_port->ip_sorights > 0);
ip_unlock(reply_port);
{
register ipc_entry_t table;
register ipc_entry_t entry;
register mach_port_index_t index;
/* optimized ipc_entry_get */
table = space->is_table;
index = table->ie_next;
if (index == 0)
goto abort_copyout;
entry = &table[index];
table->ie_next = entry->ie_next;
entry->ie_request = 0;
{
register mach_port_gen_t gen;
assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0);
gen = entry->ie_bits + IE_BITS_GEN_ONE;
exc->Head.msgh_remote_port = MACH_PORT_MAKE(index, gen);
/* optimized ipc_right_copyout */
entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1);
}
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);
exc->Head.msgh_local_port =
((dest_port->ip_receiver == space) ?
dest_port->ip_receiver_name : MACH_PORT_NULL);
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);
copyout_body:
/*
* Optimized version of ipc_kmsg_copyout_body,
* to handle the two ports in the body.
*/
mr = (ipc_kmsg_copyout_object(space, (ipc_object_t) thread_port,
MACH_MSG_TYPE_PORT_SEND, &exc->thread) |
ipc_kmsg_copyout_object(space, (ipc_object_t) task_port,
MACH_MSG_TYPE_PORT_SEND, &exc->task));
if (mr != MACH_MSG_SUCCESS) {
(void) ipc_kmsg_put(receiver->ith_msg, kmsg,
kmsg->ikm_header.msgh_size);
thread_syscall_return(mr | MACH_RCV_BODY_ERROR);
/*NOTREACHED*/
}
}
/*
* Optimized version of ipc_kmsg_put.
* We must check ikm_cache after copyoutmsg.
*/
ikm_check_initialized(kmsg, kmsg->ikm_size);
assert(kmsg->ikm_size == IKM_SAVED_KMSG_SIZE);
if (copyoutmsg((vm_offset_t) &kmsg->ikm_header, (vm_offset_t)receiver->ith_msg,
sizeof(struct mach_exception)) ||
(ikm_cache() != IKM_NULL)) {
mr = ipc_kmsg_put(receiver->ith_msg, kmsg,
kmsg->ikm_header.msgh_size);
thread_syscall_return(mr);
/*NOTREACHED*/
}
ikm_cache() = kmsg;
thread_syscall_return(MACH_MSG_SUCCESS);
/*NOTREACHED*/
#ifndef __GNUC__
return; /* help for the compiler */
#endif
#else 0
imq_unlock(reply_mqueue);
#endif 0
slow_exception_raise: {
register struct mach_exception *exc =
(struct mach_exception *) &kmsg->ikm_header;
ipc_kmsg_t reply_kmsg;
mach_port_seqno_t reply_seqno;
exception_raise_misses++;
/*
* We hold the following resources, which must be consumed:
* kmsg, send-once right and ref for reply_port
* send rights for dest_port, thread_port, task_port
* Synthesize a kmsg to send.
*/
exc->Head.msgh_bits = (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
MACH_MSG_TYPE_PORT_SEND_ONCE) |
MACH_MSGH_BITS_OLD_FORMAT |
MACH_MSGH_BITS_COMPLEX);
exc->Head.msgh_size = sizeof *exc;
exc->Head.msgh_remote_port = (mach_port_t) dest_port;
exc->Head.msgh_local_port = (mach_port_t) reply_port;
exc->Head.msgh_reserved = 0;
exc->Head.msgh_id = MACH_EXCEPTION_ID;
exc->threadType = exc_port_proto;
exc->thread = (mach_port_t) thread_port;
exc->taskType = exc_port_proto;
exc->task = (mach_port_t) task_port;
exc->exceptionType = exc_code_proto;
exc->exception = _exception;
exc->codeType = exc_code_proto;
exc->code = code;
exc->subcodeType = exc_code_proto;
exc->subcode = subcode;
ipc_mqueue_send_always(kmsg);
/*
* We are left with a ref for reply_port,
* which we use to receive the reply message.
*/
ip_lock(reply_port);
if (!ip_active(reply_port)) {
ip_unlock(reply_port);
exception_raise_continue_slow(MACH_RCV_PORT_DIED, IKM_NULL, /*dummy*/0);
/*NOTREACHED*/
return;
}
imq_lock(reply_mqueue);
ip_unlock(reply_port);
mr = ipc_mqueue_receive(reply_mqueue, MACH_RCV_OLD_FORMAT,
MACH_MSG_SIZE_MAX,
MACH_MSG_TIMEOUT_NONE,
FALSE,
(self->exc_func)?
exception_raise_continue :
IMQ_NULL_CONTINUE,
&reply_kmsg, &reply_seqno, 0);
/* reply_mqueue is unlocked */
exception_raise_continue_slow(mr, reply_kmsg, reply_seqno);
/*NOTREACHED*/
}
}
/*
* Routine: exception_parse_reply
* Purpose:
* Parse and consume an exception reply message.
* Conditions:
* The destination port right has already been consumed.
* The message buffer and anything else in it is consumed.
* Returns:
* The reply return code.
*/
kern_return_t
exception_parse_reply(kmsg)
ipc_kmsg_t kmsg;
{
register mig_reply_header_t *msg =
(mig_reply_header_t *) &kmsg->ikm_header;
kern_return_t kr;
if ((msg->Head.msgh_bits != (MACH_MSGH_BITS_OLD_FORMAT |
MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0))) ||
(msg->Head.msgh_size != sizeof *msg) ||
(msg->Head.msgh_id != MACH_EXCEPTION_REPLY_ID) ||
(* (int *) &msg->RetCodeType != * (int *) &exc_code_proto)) {
/*
* Bozo user sent us a misformatted reply.
*/
kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
ipc_kmsg_destroy(kmsg);
return MIG_REPLY_MISMATCH;
}
kr = msg->RetCode;
if ((kmsg->ikm_size == IKM_SAVED_KMSG_SIZE) &&
(ikm_cache() == IKM_NULL))
ikm_cache() = kmsg;
else
ikm_free(kmsg);
return kr;
}
/*
* Routine: exception_raise_continue
* Purpose:
* Continue after blocking for an exception.
* Conditions:
* Nothing locked. We are running on a new kernel stack,
* with the exception state saved in the thread. From here
* control goes back to user space.
* Returns:
* Doesn't return.
*/
void
exception_raise_continue()
{
ipc_thread_t self = current_thread();
ipc_port_t reply_port = self->ith_port;
ipc_mqueue_t reply_mqueue = &reply_port->ip_messages;
ipc_kmsg_t kmsg;
mach_port_seqno_t seqno;
mach_msg_return_t mr;
mr = ipc_mqueue_receive(reply_mqueue, MACH_RCV_OLD_FORMAT,
MACH_MSG_SIZE_MAX,
MACH_MSG_TIMEOUT_NONE,
TRUE, exception_raise_continue,
&kmsg, &seqno, 0);
/* reply_mqueue is unlocked */
exception_raise_continue_slow(mr, kmsg, seqno);
/*NOTREACHED*/
}
/*
* Routine: exception_raise_continue_slow
* Purpose:
* Continue after finishing an ipc_mqueue_receive
* for an exception reply message.
* Conditions:
* Nothing locked. We hold a ref for reply_port.
* Returns:
* Doesn't return.
*/
void
exception_raise_continue_slow(mr, kmsg, seqno)
mach_msg_return_t mr;
ipc_kmsg_t kmsg;
mach_port_seqno_t seqno;
{
ipc_thread_t self = current_thread();
ipc_port_t reply_port = self->ith_port;
ipc_mqueue_t reply_mqueue = &reply_port->ip_messages;
while (mr == MACH_RCV_INTERRUPTED) {
/*
* Somebody is trying to force this thread
* to a clean point. We must cooperate
* and then resume the receive.
*/
while (thread_should_halt(self)) {
/* don't terminate while holding a reference */
if (IP_VALID(reply_port))
ipc_port_release(reply_port);
reply_port = self->ith_port = IP_NULL;
reply_mqueue = IMQ_NULL;
thread_halt_self_with_continuation((void (*)()) 0);
ith_lock(self);
reply_port = self->ith_port = self->ith_rpc_reply;
if (IP_VALID(reply_port)) {
ipc_port_reference(reply_port);
reply_mqueue = &reply_port->ip_messages;
}
ith_unlock(self);
}
if (!IP_VALID(reply_port)) {
mr = MACH_RCV_PORT_DIED;
break;
}
ip_lock(reply_port);
if (!ip_active(reply_port)) {
ip_unlock(reply_port);
mr = MACH_RCV_PORT_DIED;
break;
}
imq_lock(reply_mqueue);
ip_unlock(reply_port);
mr = ipc_mqueue_receive(reply_mqueue, MACH_RCV_OLD_FORMAT,
MACH_MSG_SIZE_MAX,
MACH_MSG_TIMEOUT_NONE,
FALSE,
(self->exc_func)?
exception_raise_continue :
IMQ_NULL_CONTINUE,
&kmsg, &seqno, 0);
/* reply_mqueue is unlocked */
}
if (IP_VALID(reply_port))
ipc_port_release(reply_port);
assert((mr == MACH_MSG_SUCCESS) ||
(mr == MACH_RCV_PORT_DIED));
if (mr == MACH_MSG_SUCCESS) {
/*
* Consume the reply message.
*/
ipc_port_release_sonce(reply_port);
mr = exception_parse_reply(kmsg);
}
if ((mr == KERN_SUCCESS) ||
(mr == MACH_RCV_PORT_DIED)) {
void (*continuation)(void) = self->exc_func;
if (continuation) {
call_continuation(continuation);
/*NOTREACHED*/
} else
return;
}
if (self->ith_exc != KERN_SUCCESS) {
exception_try_task(self->ith_exc,
self->ith_exc_code,
self->ith_exc_subcode);
/*NOTREACHED*/
return;
}
exception_no_server();
/*NOTREACHED*/
}
/*
* Routine: exception_raise_continue_fast
* Purpose:
* Special-purpose fast continuation for exceptions.
* Conditions:
* reply_port is locked and alive.
* kmsg is our reply message.
* Returns:
* Doesn't return.
*/
void
exception_raise_continue_fast(reply_port, kmsg)
ipc_port_t reply_port;
ipc_kmsg_t kmsg;
{
ipc_thread_t self = current_thread();
kern_return_t kr;
assert(ip_active(reply_port));
assert(reply_port == self->ith_port);
assert(reply_port == (ipc_port_t) kmsg->ikm_header.msgh_remote_port);
assert(MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
MACH_MSG_TYPE_PORT_SEND_ONCE);
/*
* Release the send-once right (from the message header)
* and the saved reference (from self->ith_port).
*/
reply_port->ip_sorights--;
ip_release(reply_port);
ip_release(reply_port);
ip_unlock(reply_port);
/*
* Consume the reply message.
*/
kr = exception_parse_reply(kmsg);
if (kr == KERN_SUCCESS) {
void (*continuation)(void) = self->exc_func;
if (continuation) {
call_continuation(continuation);
/*NOTREACHED*/
}
thread_exception_return();
/*NOTREACHED*/
return; /* help for the compiler */
}
if (self->ith_exc != KERN_SUCCESS) {
exception_try_task(self->ith_exc,
self->ith_exc_code,
self->ith_exc_subcode);
/*NOTREACHED*/
}
exception_no_server();
/*NOTREACHED*/
}