diff options
Diffstat (limited to 'kern/exception.c')
-rw-r--r-- | kern/exception.c | 1003 |
1 files changed, 1003 insertions, 0 deletions
diff --git a/kern/exception.c b/kern/exception.c new file mode 100644 index 0000000..ebd9e5b --- /dev/null +++ b/kern/exception.c @@ -0,0 +1,1003 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,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 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. + */ + +#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> +#include <mach/machine/vm_types.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. + */ + +void +exception(_exception, code, subcode) + integer_t _exception, code, subcode; +{ + register ipc_thread_t self = current_thread(); + register ipc_port_t exc_port; + + if (_exception == KERN_SUCCESS) + panic("exception"); + + /* + * 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*/ +} + +/* + * 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) + integer_t _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 + + /* + * 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; + integer_t exception; + mach_msg_type_t codeType; + integer_t code; + mach_msg_type_t subcodeType; + integer_t subcode; +}; + +#define INTEGER_T_SIZE_IN_BITS (8 * sizeof(integer_t)) +#define INTEGER_T_TYPE MACH_MSG_TYPE_INTEGER_T + /* in mach/machine/vm_types.h */ + +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 = */ INTEGER_T_TYPE, + /* msgt_size = */ INTEGER_T_SIZE_IN_BITS, + /* 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; + integer_t _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); + } + + /* + * 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); + + /* + * 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) || + !((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_seqno = 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 + + 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_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_seqno = 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_MSG_OPTION_NONE, + MACH_MSG_SIZE_MAX, + MACH_MSG_TIMEOUT_NONE, + FALSE, exception_raise_continue, + &reply_kmsg, &reply_seqno); + /* reply_mqueue is unlocked */ + + exception_raise_continue_slow(mr, reply_kmsg, reply_seqno); + /*NOTREACHED*/ + } +} + +mach_msg_type_t exc_RetCode_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_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(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_RetCode_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_MSG_OPTION_NONE, + MACH_MSG_SIZE_MAX, + MACH_MSG_TIMEOUT_NONE, + TRUE, exception_raise_continue, + &kmsg, &seqno); + /* 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 (self->ast & AST_TERMINATE) + ipc_port_release(reply_port); + thread_halt_self(); + } + + 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_MSG_OPTION_NONE, + MACH_MSG_SIZE_MAX, + MACH_MSG_TIMEOUT_NONE, + FALSE, exception_raise_continue, + &kmsg, &seqno); + /* reply_mqueue is unlocked */ + } + 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)) { + thread_exception_return(); + /*NOTREACHED*/ + 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) { + 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*/ +} |