The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/exception.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* 
    2  * Mach Operating System
    3  * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
    4  * All Rights Reserved.
    5  * 
    6  * Permission to use, copy, modify and distribute this software and its
    7  * documentation is hereby granted, provided that both the copyright
    8  * notice and this permission notice appear in all copies of the
    9  * software, derivative works or modified versions, and any portions
   10  * thereof, and that both notices appear in supporting documentation.
   11  * 
   12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
   13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   15  * 
   16  * Carnegie Mellon requests users of this software to return to
   17  * 
   18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   19  *  School of Computer Science
   20  *  Carnegie Mellon University
   21  *  Pittsburgh PA 15213-3890
   22  * 
   23  * any improvements or extensions that they make and grant Carnegie Mellon
   24  * the rights to redistribute these changes.
   25  */
   26 /*
   27  * HISTORY
   28  * $Log:        exception.c,v $
   29  * Revision 2.16  93/03/09  10:55:01  danner
   30  *      Removed duplicated decl for thread_syscall_return().
   31  *      Added a tiny GNUism to shutup GCC.
   32  *      [93/03/06            af]
   33  * 
   34  * Revision 2.14.1.1  92/12/10  18:02:42  af
   35  *      64bit cleanup.
   36  *      [92/12/01            af]
   37  * 
   38  * Revision 2.14  92/08/03  17:37:00  jfriedl
   39  *      removed silly prototypes
   40  *      [92/08/02            jfriedl]
   41  * 
   42  * Revision 2.13  92/05/21  17:13:19  jfriedl
   43  *      tried prototypes.
   44  *      [92/05/20            jfriedl]
   45  * 
   46  * 16-May-92  Jeffrey Friedl (jfriedl) at Carnegie-Mellon University
   47  *      Cleanup to quiet gcc warnings. Renamed arguments 'exception'
   48  *      to '_exception' to avoid name conflict.
   49  * Revision 2.12  92/03/10  16:26:24  jsb
   50  *      Don't convert exception kmsg to network format.
   51  *      [92/02/21  09:02:59  jsb]
   52  * 
   53  * Revision 2.11  92/03/03  00:44:45  rpd
   54  *      Changed debug_user_with_kdb to FALSE.
   55  *      [92/03/02            rpd]
   56  * 
   57  * Revision 2.10  91/12/14  14:18:23  jsb
   58  *      Removed ipc_fields.h hack.
   59  * 
   60  * Revision 2.9  91/12/13  13:41:38  jsb
   61  *      Added NORMA_IPC support.
   62  * 
   63  * Revision 2.8  91/08/28  11:14:26  jsb
   64  *      Added seqno arguments to ipc_mqueue_receive
   65  *      and exception_raise_continue_slow.
   66  *      [91/08/10            rpd]
   67  * 
   68  *      Changed msgh_kind to msgh_seqno.
   69  *      [91/08/10            rpd]
   70  * 
   71  *      Changed exception_no_server to print an informative message
   72  *      before invoking kdb, so this doesn't look like a kernel bug.
   73  *      [91/08/09            rpd]
   74  * 
   75  * Revision 2.7  91/06/25  10:28:06  rpd
   76  *      Fixed ikm_cache critical sections to avoid blocking operations.
   77  *      [91/05/23            rpd]
   78  * 
   79  * Revision 2.6  91/05/14  16:41:02  mrt
   80  *      Correcting copyright
   81  * 
   82  * Revision 2.5  91/03/16  14:49:47  rpd
   83  *      Fixed assertion typo.
   84  *      [91/03/08            rpd]
   85  *      Replaced ipc_thread_switch with thread_handoff.
   86  *      Replaced ith_saved with ikm_cache.
   87  *      [91/02/16            rpd]
   88  * 
   89  * Revision 2.4  91/02/05  17:26:05  mrt
   90  *      Changed to new Mach copyright
   91  *      [91/02/01  16:11:53  mrt]
   92  * 
   93  * Revision 2.3  91/01/08  15:15:36  rpd
   94  *      Added KEEP_STACKS support.
   95  *      [91/01/08  14:11:40  rpd]
   96  * 
   97  *      Replaced thread_doexception with new, optimized exception path.
   98  *      [90/12/22            rpd]
   99  * 
  100  * Revision 2.2  90/06/02  14:53:44  rpd
  101  *      Converted to new IPC.
  102  *      [90/03/26  22:04:29  rpd]
  103  * 
  104  *
  105  * Condensed history:
  106  *      Changed thread_doexception to return boolean (dbg).
  107  *      Removed non-MACH code (dbg).
  108  *      Added thread_exception_abort (dlb).
  109  *      Use port_alloc, object_copyout (rpd).
  110  *      Removed exception-port routines (dbg).
  111  *      Check for thread halt condition if exception rpc fails (dlb).
  112  *      Switch to master before calling uprintf and exit (dbg).
  113  *      If rpc fails in thread_doexception, leave ports alone (dlb).
  114  *      Translate global port names to local port names (dlb).
  115  *      Rewrote for exc interface (dlb).
  116  *      Created (dlb).
  117  */
  118 
  119 #include <norma_ipc.h>
  120 #include <mach_kdb.h>
  121 
  122 #include <mach/boolean.h>
  123 #include <mach/kern_return.h>
  124 #include <mach/message.h>
  125 #include <mach/port.h>
  126 #include <mach/mig_errors.h>
  127 #include <ipc/port.h>
  128 #include <ipc/ipc_entry.h>
  129 #include <ipc/ipc_object.h>
  130 #include <ipc/ipc_space.h>
  131 #include <ipc/ipc_port.h>
  132 #include <ipc/ipc_pset.h>
  133 #include <ipc/mach_msg.h>
  134 #include <ipc/ipc_machdep.h>
  135 #include <kern/counters.h>
  136 #include <kern/ipc_tt.h>
  137 #include <kern/task.h>
  138 #include <kern/thread.h>
  139 #include <kern/processor.h>
  140 #include <kern/sched.h>
  141 #include <kern/sched_prim.h>
  142 
  143 
  144 
  145 extern void exception();
  146 extern void exception_try_task();
  147 extern void exception_no_server();
  148 
  149 extern void exception_raise();
  150 extern kern_return_t exception_parse_reply();
  151 extern void exception_raise_continue();
  152 extern void exception_raise_continue_slow();
  153 extern void exception_raise_continue_fast();
  154 
  155 #if     MACH_KDB
  156 extern void thread_kdb_return();
  157 extern void db_printf();
  158 
  159 boolean_t debug_user_with_kdb = FALSE;
  160 #endif  MACH_KDB
  161 
  162 #ifdef  KEEP_STACKS
  163 /*
  164  *      Some obsolete architectures don't support kernel stack discarding
  165  *      or the thread_exception_return, thread_syscall_return continuations.
  166  *      For these architectures, the NOTREACHED comments below are incorrect.
  167  *      The exception function is expected to return.
  168  *      So the return statements along the slow paths are important.
  169  */
  170 #endif  KEEP_STACKS
  171 
  172 /*
  173  *      Routine:        exception
  174  *      Purpose:
  175  *              The current thread caught an exception.
  176  *              We make an up-call to the thread's exception server.
  177  *      Conditions:
  178  *              Nothing locked and no resources held.
  179  *              Called from an exception context, so
  180  *              thread_exception_return and thread_kdb_return
  181  *              are possible.
  182  *      Returns:
  183  *              Doesn't return.
  184  */
  185 
  186 void
  187 exception(_exception, code, subcode)
  188         int _exception, code, subcode;
  189 {
  190         register ipc_thread_t self = current_thread();
  191         register ipc_port_t exc_port;
  192 
  193         if (_exception == KERN_SUCCESS)
  194                 panic("exception");
  195 
  196         /*
  197          *      Optimized version of retrieve_thread_exception.
  198          */
  199 
  200         ith_lock(self);
  201         assert(self->ith_self != IP_NULL);
  202         exc_port = self->ith_exception;
  203         if (!IP_VALID(exc_port)) {
  204                 ith_unlock(self);
  205                 exception_try_task(_exception, code, subcode);
  206                 /*NOTREACHED*/
  207                 return;
  208         }
  209 
  210         ip_lock(exc_port);
  211         ith_unlock(self);
  212         if (!ip_active(exc_port)) {
  213                 ip_unlock(exc_port);
  214                 exception_try_task(_exception, code, subcode);
  215                 /*NOTREACHED*/
  216                 return;
  217         }
  218 
  219         /*
  220          *      Make a naked send right for the exception port.
  221          */
  222 
  223         ip_reference(exc_port);
  224         exc_port->ip_srights++;
  225         ip_unlock(exc_port);
  226 
  227         /*
  228          *      If this exception port doesn't work,
  229          *      we will want to try the task's exception port.
  230          *      Indicate this by saving the exception state.
  231          */
  232 
  233         self->ith_exc = _exception;
  234         self->ith_exc_code = code;
  235         self->ith_exc_subcode = subcode;
  236 
  237         exception_raise(exc_port,
  238                         retrieve_thread_self_fast(self),
  239                         retrieve_task_self_fast(self->task),
  240                         _exception, code, subcode);
  241         /*NOTREACHED*/
  242 }
  243 
  244 /*
  245  *      Routine:        exception_try_task
  246  *      Purpose:
  247  *              The current thread caught an exception.
  248  *              We make an up-call to the task's exception server.
  249  *      Conditions:
  250  *              Nothing locked and no resources held.
  251  *              Called from an exception context, so
  252  *              thread_exception_return and thread_kdb_return
  253  *              are possible.
  254  *      Returns:
  255  *              Doesn't return.
  256  */
  257 
  258 void
  259 exception_try_task(_exception, code, subcode)
  260         int _exception, code, subcode;
  261 {
  262         ipc_thread_t self = current_thread();
  263         register task_t task = self->task;
  264         register ipc_port_t exc_port;
  265 
  266         /*
  267          *      Optimized version of retrieve_task_exception.
  268          */
  269 
  270         itk_lock(task);
  271         assert(task->itk_self != IP_NULL);
  272         exc_port = task->itk_exception;
  273         if (!IP_VALID(exc_port)) {
  274                 itk_unlock(task);
  275                 exception_no_server();
  276                 /*NOTREACHED*/
  277                 return;
  278         }
  279 
  280         ip_lock(exc_port);
  281         itk_unlock(task);
  282         if (!ip_active(exc_port)) {
  283                 ip_unlock(exc_port);
  284                 exception_no_server();
  285                 /*NOTREACHED*/
  286                 return;
  287         }
  288 
  289         /*
  290          *      Make a naked send right for the exception port.
  291          */
  292 
  293         ip_reference(exc_port);
  294         exc_port->ip_srights++;
  295         ip_unlock(exc_port);
  296 
  297         /*
  298          *      This is the thread's last chance.
  299          *      Clear the saved exception state.
  300          */
  301 
  302         self->ith_exc = KERN_SUCCESS;
  303 
  304         exception_raise(exc_port,
  305                         retrieve_thread_self_fast(self),
  306                         retrieve_task_self_fast(task),
  307                         _exception, code, subcode);
  308         /*NOTREACHED*/
  309 }
  310 
  311 /*
  312  *      Routine:        exception_no_server
  313  *      Purpose:
  314  *              The current thread took an exception,
  315  *              and no exception server took responsibility
  316  *              for the exception.  So good bye, charlie.
  317  *      Conditions:
  318  *              Nothing locked and no resources held.
  319  *              Called from an exception context, so
  320  *              thread_kdb_return is possible.
  321  *      Returns:
  322  *              Doesn't return.
  323  */
  324 
  325 void
  326 exception_no_server()
  327 {
  328         register ipc_thread_t self = current_thread();
  329 
  330         /*
  331          *      If this thread is being terminated, cooperate.
  332          */
  333 
  334         while (thread_should_halt(self))
  335                 thread_halt_self();
  336 
  337 #if     MACH_KDB
  338         if (debug_user_with_kdb) {
  339                 /*
  340                  *      Debug the exception with kdb.
  341                  *      If kdb handles the exception,
  342                  *      then thread_kdb_return won't return.
  343                  */
  344 
  345                 db_printf("No exception server, calling kdb...\n");
  346                 thread_kdb_return();
  347         }
  348 #endif  MACH_KDB
  349 
  350         /*
  351          *      All else failed; terminate task.
  352          */
  353 
  354         (void) task_terminate(self->task);
  355         thread_halt_self();
  356         /*NOTREACHED*/
  357 }
  358 
  359 #define MACH_EXCEPTION_ID               2400    /* from mach/exc.defs */
  360 #define MACH_EXCEPTION_REPLY_ID         (MACH_EXCEPTION_ID + 100)
  361 
  362 struct mach_exception {
  363         mach_msg_header_t Head;
  364         mach_msg_type_t threadType;
  365         mach_port_t thread;
  366         mach_msg_type_t taskType;
  367         mach_port_t task;
  368         mach_msg_type_t exceptionType;
  369         int exception;
  370         mach_msg_type_t codeType;
  371         int code;
  372         mach_msg_type_t subcodeType;
  373         int subcode;
  374 };
  375 
  376 mach_msg_type_t exc_port_proto = {
  377         /* msgt_name = */               MACH_MSG_TYPE_PORT_SEND,
  378         /* msgt_size = */               PORT_T_SIZE_IN_BITS,
  379         /* msgt_number = */             1,
  380         /* msgt_inline = */             TRUE,
  381         /* msgt_longform = */           FALSE,
  382         /* msgt_deallocate = */         FALSE,
  383         /* msgt_unused = */             0
  384 };
  385 
  386 mach_msg_type_t exc_code_proto = {
  387         /* msgt_name = */               MACH_MSG_TYPE_INTEGER_32,
  388         /* msgt_size = */               32,
  389         /* msgt_number = */             1,
  390         /* msgt_inline = */             TRUE,
  391         /* msgt_longform = */           FALSE,
  392         /* msgt_deallocate = */         FALSE,
  393         /* msgt_unused = */             0
  394 };
  395 
  396 /*
  397  *      Routine:        exception_raise
  398  *      Purpose:
  399  *              Make an exception_raise up-call to an exception server.
  400  *
  401  *              dest_port must be a valid naked send right.
  402  *              thread_port and task_port are naked send rights.
  403  *              All three are always consumed.
  404  *
  405  *              self->ith_exc, self->ith_exc_code, self->ith_exc_subcode
  406  *              must be appropriately initialized.
  407  *      Conditions:
  408  *              Nothing locked.  We are being called in an exception context,
  409  *              so thread_exception_return may be called.
  410  *      Returns:
  411  *              Doesn't return.
  412  */
  413 
  414 int exception_raise_misses = 0;
  415 
  416 void
  417 exception_raise(dest_port, thread_port, task_port,
  418                 _exception, code, subcode)
  419         ipc_port_t dest_port;
  420         ipc_port_t thread_port;
  421         ipc_port_t task_port;
  422         int _exception, code, subcode;
  423 {
  424         ipc_thread_t self = current_thread();
  425         ipc_thread_t receiver;
  426         ipc_port_t reply_port;
  427         ipc_mqueue_t dest_mqueue;
  428         ipc_mqueue_t reply_mqueue;
  429         ipc_kmsg_t kmsg;
  430         mach_msg_return_t mr;
  431 
  432         assert(IP_VALID(dest_port));
  433 
  434         /*
  435          *      We will eventually need a message buffer.
  436          *      Grab the buffer now, while nothing is locked.
  437          *      This buffer will get handed to the exception server,
  438          *      and it will give the buffer back with its reply.
  439          */
  440 
  441         kmsg = ikm_cache();
  442         if (kmsg != IKM_NULL) {
  443                 ikm_cache() = IKM_NULL;
  444                 ikm_check_initialized(kmsg, IKM_SAVED_KMSG_SIZE);
  445         } else {
  446                 kmsg = ikm_alloc(IKM_SAVED_MSG_SIZE);
  447                 if (kmsg == IKM_NULL)
  448                         panic("exception_raise");
  449                 ikm_init(kmsg, IKM_SAVED_MSG_SIZE);
  450         }
  451 
  452         /*
  453          *      We need a reply port for the RPC.
  454          *      Check first for a cached port.
  455          */
  456 
  457         ith_lock(self);
  458         assert(self->ith_self != IP_NULL);
  459 
  460         reply_port = self->ith_rpc_reply;
  461         if (reply_port == IP_NULL) {
  462                 ith_unlock(self);
  463                 reply_port = ipc_port_alloc_reply();
  464                 ith_lock(self);
  465                 if ((reply_port == IP_NULL) ||
  466                     (self->ith_rpc_reply != IP_NULL))
  467                         panic("exception_raise");
  468                 self->ith_rpc_reply = reply_port;
  469         }
  470 
  471         ip_lock(reply_port);
  472         assert(ip_active(reply_port));
  473         ith_unlock(self);
  474 
  475         /*
  476          *      Make a naked send-once right for the reply port,
  477          *      to hand to the exception server.
  478          *      Make an extra reference for the reply port,
  479          *      to receive on.  This protects us against
  480          *      mach_msg_abort_rpc.
  481          */
  482 
  483         reply_port->ip_sorights++;
  484         ip_reference(reply_port);
  485 
  486         ip_reference(reply_port);
  487         self->ith_port = reply_port;
  488 
  489         reply_mqueue = &reply_port->ip_messages;
  490         imq_lock(reply_mqueue);
  491         assert(ipc_kmsg_queue_empty(&reply_mqueue->imq_messages));
  492         ip_unlock(reply_port);
  493 
  494         /*
  495          *      Make sure we can queue to the destination port.
  496          */
  497 
  498         if (!ip_lock_try(dest_port)) {
  499                 imq_unlock(reply_mqueue);
  500                 goto slow_exception_raise;
  501         }
  502 
  503         if (!ip_active(dest_port) ||
  504 #if     NORMA_IPC
  505             IP_NORMA_IS_PROXY(dest_port) ||
  506 #endif  NORMA_IPC
  507             (dest_port->ip_receiver == ipc_space_kernel)) {
  508                 imq_unlock(reply_mqueue);
  509                 ip_unlock(dest_port);
  510                 goto slow_exception_raise;
  511         }
  512 
  513         /*
  514          *      Find the destination message queue.
  515          */
  516 
  517     {
  518         register ipc_pset_t dest_pset;
  519 
  520         dest_pset = dest_port->ip_pset;
  521         if (dest_pset == IPS_NULL)
  522                 dest_mqueue = &dest_port->ip_messages;
  523         else
  524                 dest_mqueue = &dest_pset->ips_messages;
  525     }
  526 
  527         if (!imq_lock_try(dest_mqueue)) {
  528                 imq_unlock(reply_mqueue);
  529                 ip_unlock(dest_port);
  530                 goto slow_exception_raise;
  531         }
  532 
  533         /*
  534          *      Safe to unlock dest_port, because we hold
  535          *      dest_mqueue locked.  We never bother changing
  536          *      dest_port->ip_msgcount.
  537          */
  538 
  539         ip_unlock(dest_port);
  540 
  541         receiver = ipc_thread_queue_first(&dest_mqueue->imq_threads);
  542         if ((receiver == ITH_NULL) ||
  543             !((receiver->swap_func == (void (*)()) mach_msg_continue) ||
  544               ((receiver->swap_func ==
  545                                 (void (*)()) mach_msg_receive_continue) &&
  546                (sizeof(struct mach_exception) <= receiver->ith_msize) &&
  547                ((receiver->ith_option & MACH_RCV_NOTIFY) == 0))) ||
  548             !thread_handoff(self, exception_raise_continue, receiver)) {
  549                 imq_unlock(reply_mqueue);
  550                 imq_unlock(dest_mqueue);
  551                 goto slow_exception_raise;
  552         }
  553         counter(c_exception_raise_block++);
  554 
  555         assert(current_thread() == receiver);
  556 
  557         /*
  558          *      We need to finish preparing self for its
  559          *      time asleep in reply_mqueue.  self is left
  560          *      holding the extra ref for reply_port.
  561          */
  562 
  563         ipc_thread_enqueue_macro(&reply_mqueue->imq_threads, self);
  564         self->ith_state = MACH_RCV_IN_PROGRESS;
  565         self->ith_msize = MACH_MSG_SIZE_MAX;
  566         imq_unlock(reply_mqueue);
  567 
  568         /*
  569          *      Finish extracting receiver from dest_mqueue.
  570          */
  571 
  572         ipc_thread_rmqueue_first_macro(
  573                 &dest_mqueue->imq_threads, receiver);
  574         imq_unlock(dest_mqueue);
  575 
  576         /*
  577          *      Release the receiver's reference for his object.
  578          */
  579     {
  580         register ipc_object_t object = receiver->ith_object;
  581 
  582         io_lock(object);
  583         io_release(object);
  584         io_check_unlock(object);
  585     }
  586 
  587     {
  588         register struct mach_exception *exc =
  589                         (struct mach_exception *) &kmsg->ikm_header;
  590         ipc_space_t space = receiver->task->itk_space;
  591 
  592         /*
  593          *      We are running as the receiver now.  We hold
  594          *      the following resources, which must be consumed:
  595          *              kmsg, send-once right for reply_port
  596          *              send rights for dest_port, thread_port, task_port
  597          *      Synthesize a kmsg for copyout to the receiver.
  598          */
  599 
  600         exc->Head.msgh_bits = (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
  601                                               MACH_MSG_TYPE_PORT_SEND) |
  602                                MACH_MSGH_BITS_COMPLEX);
  603         exc->Head.msgh_size = sizeof *exc;
  604      /* exc->Head.msgh_remote_port later */
  605      /* exc->Head.msgh_local_port later */
  606         exc->Head.msgh_seqno = 0;
  607         exc->Head.msgh_id = MACH_EXCEPTION_ID;
  608         exc->threadType = exc_port_proto;
  609      /* exc->thread later */
  610         exc->taskType = exc_port_proto;
  611      /* exc->task later */
  612         exc->exceptionType = exc_code_proto;
  613         exc->exception = _exception;
  614         exc->codeType = exc_code_proto;
  615         exc->code = code;
  616         exc->subcodeType = exc_code_proto;
  617         exc->subcode = subcode;
  618 
  619         /*
  620          *      Check that the receiver can handle the message.
  621          */
  622 
  623         if (receiver->ith_rcv_size < sizeof(struct mach_exception)) {
  624                 /*
  625                  *      ipc_kmsg_destroy is a handy way to consume
  626                  *      the resources we hold, but it requires setup.
  627                  */
  628 
  629                 exc->Head.msgh_bits =
  630                         (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
  631                                         MACH_MSG_TYPE_PORT_SEND_ONCE) |
  632                          MACH_MSGH_BITS_COMPLEX);
  633                 exc->Head.msgh_remote_port = (mach_port_t) dest_port;
  634                 exc->Head.msgh_local_port = (mach_port_t) reply_port;
  635                 exc->thread = (mach_port_t) thread_port;
  636                 exc->task = (mach_port_t) task_port;
  637 
  638                 ipc_kmsg_destroy(kmsg);
  639                 thread_syscall_return(MACH_RCV_TOO_LARGE);
  640                 /*NOTREACHED*/
  641         }
  642 
  643         is_write_lock(space);
  644         assert(space->is_active);
  645 
  646         /*
  647          *      To do an atomic copyout, need simultaneous
  648          *      locks on both ports and the space.
  649          */
  650 
  651         ip_lock(dest_port);
  652         if (!ip_active(dest_port) ||
  653             !ip_lock_try(reply_port)) {
  654             abort_copyout:
  655                 ip_unlock(dest_port);
  656                 is_write_unlock(space);
  657 
  658                 /*
  659                  *      Oh well, we have to do the header the slow way.
  660                  *      First make it look like it's in-transit.
  661                  */
  662 
  663                 exc->Head.msgh_bits =
  664                         (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
  665                                         MACH_MSG_TYPE_PORT_SEND_ONCE) |
  666                          MACH_MSGH_BITS_COMPLEX);
  667                 exc->Head.msgh_remote_port = (mach_port_t) dest_port;
  668                 exc->Head.msgh_local_port = (mach_port_t) reply_port;
  669 
  670                 mr = ipc_kmsg_copyout_header(&exc->Head, space,
  671                                              MACH_PORT_NULL);
  672                 if (mr == MACH_MSG_SUCCESS)
  673                         goto copyout_body;
  674 
  675                 /*
  676                  *      Ack!  Prepare for ipc_kmsg_copyout_dest.
  677                  *      It will consume thread_port and task_port.
  678                  */
  679 
  680                 exc->thread = (mach_port_t) thread_port;
  681                 exc->task = (mach_port_t) task_port;
  682 
  683                 ipc_kmsg_copyout_dest(kmsg, space);
  684                 (void) ipc_kmsg_put(receiver->ith_msg, kmsg,
  685                                     sizeof(mach_msg_header_t));
  686                 thread_syscall_return(mr);
  687                 /*NOTREACHED*/
  688         }
  689 
  690         if (!ip_active(reply_port)) {
  691                 ip_unlock(reply_port);
  692                 goto abort_copyout;
  693         }
  694 
  695         assert(reply_port->ip_sorights > 0);
  696         ip_unlock(reply_port);
  697 
  698     {
  699         register ipc_entry_t table;
  700         register ipc_entry_t entry;
  701         register mach_port_index_t index;
  702 
  703         /* optimized ipc_entry_get */
  704 
  705         table = space->is_table;
  706         index = table->ie_next;
  707 
  708         if (index == 0)
  709                 goto abort_copyout;
  710 
  711         entry = &table[index];
  712         table->ie_next = entry->ie_next;
  713         entry->ie_request = 0;
  714 
  715     {
  716         register mach_port_gen_t gen;
  717 
  718         assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0);
  719         gen = entry->ie_bits + IE_BITS_GEN_ONE;
  720 
  721         exc->Head.msgh_remote_port = MACH_PORT_MAKE(index, gen);
  722 
  723         /* optimized ipc_right_copyout */
  724 
  725         entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1);
  726     }
  727 
  728         entry->ie_object = (ipc_object_t) reply_port;
  729         is_write_unlock(space);
  730     }
  731 
  732         /* optimized ipc_object_copyout_dest */
  733 
  734         assert(dest_port->ip_srights > 0);
  735         ip_release(dest_port);
  736 
  737         exc->Head.msgh_local_port =
  738                 ((dest_port->ip_receiver == space) ?
  739                  dest_port->ip_receiver_name : MACH_PORT_NULL);
  740 
  741         if ((--dest_port->ip_srights == 0) &&
  742             (dest_port->ip_nsrequest != IP_NULL)) {
  743                 ipc_port_t nsrequest;
  744                 mach_port_mscount_t mscount;
  745 
  746                 /* a rather rare case */
  747 
  748                 nsrequest = dest_port->ip_nsrequest;
  749                 mscount = dest_port->ip_mscount;
  750                 dest_port->ip_nsrequest = IP_NULL;
  751                 ip_unlock(dest_port);
  752 
  753                 ipc_notify_no_senders(nsrequest, mscount);
  754         } else
  755                 ip_unlock(dest_port);
  756 
  757     copyout_body:
  758         /*
  759          *      Optimized version of ipc_kmsg_copyout_body,
  760          *      to handle the two ports in the body.
  761          */
  762 
  763         mr = (ipc_kmsg_copyout_object(space, (ipc_object_t) thread_port,
  764                                       MACH_MSG_TYPE_PORT_SEND, &exc->thread) |
  765               ipc_kmsg_copyout_object(space, (ipc_object_t) task_port,
  766                                       MACH_MSG_TYPE_PORT_SEND, &exc->task));
  767         if (mr != MACH_MSG_SUCCESS) {
  768                 (void) ipc_kmsg_put(receiver->ith_msg, kmsg,
  769                                     kmsg->ikm_header.msgh_size);
  770                 thread_syscall_return(mr | MACH_RCV_BODY_ERROR);
  771                 /*NOTREACHED*/
  772         }
  773     }
  774 
  775         /*
  776          *      Optimized version of ipc_kmsg_put.
  777          *      We must check ikm_cache after copyoutmsg.
  778          */
  779 
  780         ikm_check_initialized(kmsg, kmsg->ikm_size);
  781         assert(kmsg->ikm_size == IKM_SAVED_KMSG_SIZE);
  782 
  783         if (copyoutmsg((vm_offset_t) &kmsg->ikm_header, (vm_offset_t)receiver->ith_msg,
  784                        sizeof(struct mach_exception)) ||
  785             (ikm_cache() != IKM_NULL)) {
  786                 mr = ipc_kmsg_put(receiver->ith_msg, kmsg,
  787                                   kmsg->ikm_header.msgh_size);
  788                 thread_syscall_return(mr);
  789                 /*NOTREACHED*/
  790         }
  791 
  792         ikm_cache() = kmsg;
  793         thread_syscall_return(MACH_MSG_SUCCESS);
  794         /*NOTREACHED*/
  795 #ifndef __GNUC__
  796         return; /* help for the compiler */
  797 #endif
  798 
  799     slow_exception_raise: {
  800         register struct mach_exception *exc =
  801                         (struct mach_exception *) &kmsg->ikm_header;
  802         ipc_kmsg_t reply_kmsg;
  803         mach_port_seqno_t reply_seqno;
  804 
  805         exception_raise_misses++;
  806 
  807         /*
  808          *      We hold the following resources, which must be consumed:
  809          *              kmsg, send-once right and ref for reply_port
  810          *              send rights for dest_port, thread_port, task_port
  811          *      Synthesize a kmsg to send.
  812          */
  813 
  814         exc->Head.msgh_bits = (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
  815                                               MACH_MSG_TYPE_PORT_SEND_ONCE) |
  816                                MACH_MSGH_BITS_COMPLEX);
  817         exc->Head.msgh_size = sizeof *exc;
  818         exc->Head.msgh_remote_port = (mach_port_t) dest_port;
  819         exc->Head.msgh_local_port = (mach_port_t) reply_port;
  820         exc->Head.msgh_seqno = 0;
  821         exc->Head.msgh_id = MACH_EXCEPTION_ID;
  822         exc->threadType = exc_port_proto;
  823         exc->thread = (mach_port_t) thread_port;
  824         exc->taskType = exc_port_proto;
  825         exc->task = (mach_port_t) task_port;
  826         exc->exceptionType = exc_code_proto;
  827         exc->exception = _exception;
  828         exc->codeType = exc_code_proto;
  829         exc->code = code;
  830         exc->subcodeType = exc_code_proto;
  831         exc->subcode = subcode;
  832 
  833         ipc_mqueue_send_always(kmsg);
  834 
  835         /*
  836          *      We are left with a ref for reply_port,
  837          *      which we use to receive the reply message.
  838          */
  839 
  840         ip_lock(reply_port);
  841         if (!ip_active(reply_port)) {
  842                 ip_unlock(reply_port);
  843                 exception_raise_continue_slow(MACH_RCV_PORT_DIED, IKM_NULL, /*dummy*/0);
  844                 /*NOTREACHED*/
  845                 return;
  846         }
  847 
  848         imq_lock(reply_mqueue);
  849         ip_unlock(reply_port);
  850 
  851         mr = ipc_mqueue_receive(reply_mqueue, MACH_MSG_OPTION_NONE,
  852                                 MACH_MSG_SIZE_MAX,
  853                                 MACH_MSG_TIMEOUT_NONE,
  854                                 FALSE, exception_raise_continue,
  855                                 &reply_kmsg, &reply_seqno);
  856         /* reply_mqueue is unlocked */
  857 
  858         exception_raise_continue_slow(mr, reply_kmsg, reply_seqno);
  859         /*NOTREACHED*/
  860     }
  861 }
  862 
  863 /*
  864  *      Routine:        exception_parse_reply
  865  *      Purpose:
  866  *              Parse and consume an exception reply message.
  867  *      Conditions:
  868  *              The destination port right has already been consumed.
  869  *              The message buffer and anything else in it is consumed.
  870  *      Returns:
  871  *              The reply return code.
  872  */
  873 
  874 kern_return_t
  875 exception_parse_reply(kmsg)
  876         ipc_kmsg_t kmsg;
  877 {
  878         register mig_reply_header_t *msg =
  879                         (mig_reply_header_t *) &kmsg->ikm_header;
  880         kern_return_t kr;
  881 
  882         if ((msg->Head.msgh_bits !=
  883                         MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0)) ||
  884             (msg->Head.msgh_size != sizeof *msg) ||
  885             (msg->Head.msgh_id != MACH_EXCEPTION_REPLY_ID) ||
  886             (* (int *) &msg->RetCodeType != * (int *) &exc_code_proto)) {
  887                 /*
  888                  *      Bozo user sent us a misformatted reply.
  889                  */
  890 
  891                 kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
  892                 ipc_kmsg_destroy(kmsg);
  893                 return MIG_REPLY_MISMATCH;
  894         }
  895 
  896         kr = msg->RetCode;
  897 
  898         if ((kmsg->ikm_size == IKM_SAVED_KMSG_SIZE) &&
  899             (ikm_cache() == IKM_NULL))
  900                 ikm_cache() = kmsg;
  901         else
  902                 ikm_free(kmsg);
  903 
  904         return kr;
  905 }
  906 
  907 /*
  908  *      Routine:        exception_raise_continue
  909  *      Purpose:
  910  *              Continue after blocking for an exception.
  911  *      Conditions:
  912  *              Nothing locked.  We are running on a new kernel stack,
  913  *              with the exception state saved in the thread.  From here
  914  *              control goes back to user space.
  915  *      Returns:
  916  *              Doesn't return.
  917  */
  918 
  919 void
  920 exception_raise_continue()
  921 {
  922         ipc_thread_t self = current_thread();
  923         ipc_port_t reply_port = self->ith_port;
  924         ipc_mqueue_t reply_mqueue = &reply_port->ip_messages;
  925         ipc_kmsg_t kmsg;
  926         mach_port_seqno_t seqno;
  927         mach_msg_return_t mr;
  928 
  929         mr = ipc_mqueue_receive(reply_mqueue, MACH_MSG_OPTION_NONE,
  930                                 MACH_MSG_SIZE_MAX,
  931                                 MACH_MSG_TIMEOUT_NONE,
  932                                 TRUE, exception_raise_continue,
  933                                 &kmsg, &seqno);
  934         /* reply_mqueue is unlocked */
  935 
  936         exception_raise_continue_slow(mr, kmsg, seqno);
  937         /*NOTREACHED*/
  938 }
  939 
  940 /*
  941  *      Routine:        exception_raise_continue_slow
  942  *      Purpose:
  943  *              Continue after finishing an ipc_mqueue_receive
  944  *              for an exception reply message.
  945  *      Conditions:
  946  *              Nothing locked.  We hold a ref for reply_port.
  947  *      Returns:
  948  *              Doesn't return.
  949  */
  950 
  951 void
  952 exception_raise_continue_slow(mr, kmsg, seqno)
  953         mach_msg_return_t mr;
  954         ipc_kmsg_t kmsg;
  955         mach_port_seqno_t seqno;
  956 {
  957         ipc_thread_t self = current_thread();
  958         ipc_port_t reply_port = self->ith_port;
  959         ipc_mqueue_t reply_mqueue = &reply_port->ip_messages;
  960 
  961         while (mr == MACH_RCV_INTERRUPTED) {
  962                 /*
  963                  *      Somebody is trying to force this thread
  964                  *      to a clean point.  We must cooperate
  965                  *      and then resume the receive.
  966                  */
  967 
  968                 while (thread_should_halt(self)) {
  969                         /* don't terminate while holding a reference */
  970                         if (self->ast & AST_TERMINATE)
  971                                 ipc_port_release(reply_port);
  972                         thread_halt_self();
  973                 }
  974 
  975                 ip_lock(reply_port);
  976                 if (!ip_active(reply_port)) {
  977                         ip_unlock(reply_port);
  978                         mr = MACH_RCV_PORT_DIED;
  979                         break;
  980                 }
  981 
  982                 imq_lock(reply_mqueue);
  983                 ip_unlock(reply_port);
  984 
  985                 mr = ipc_mqueue_receive(reply_mqueue, MACH_MSG_OPTION_NONE,
  986                                         MACH_MSG_SIZE_MAX,
  987                                         MACH_MSG_TIMEOUT_NONE,
  988                                         FALSE, exception_raise_continue,
  989                                         &kmsg, &seqno);
  990                 /* reply_mqueue is unlocked */
  991         }
  992         ipc_port_release(reply_port);
  993 
  994         assert((mr == MACH_MSG_SUCCESS) ||
  995                (mr == MACH_RCV_PORT_DIED));
  996 
  997         if (mr == MACH_MSG_SUCCESS) {
  998                 /*
  999                  *      Consume the reply message.
 1000                  */
 1001 
 1002                 ipc_port_release_sonce(reply_port);
 1003                 mr = exception_parse_reply(kmsg);
 1004         }
 1005 
 1006         if ((mr == KERN_SUCCESS) ||
 1007             (mr == MACH_RCV_PORT_DIED)) {
 1008                 thread_exception_return();
 1009                 /*NOTREACHED*/
 1010                 return;
 1011         }
 1012 
 1013         if (self->ith_exc != KERN_SUCCESS) {
 1014                 exception_try_task(self->ith_exc,
 1015                                    self->ith_exc_code,
 1016                                    self->ith_exc_subcode);
 1017                 /*NOTREACHED*/
 1018                 return;
 1019         }
 1020 
 1021         exception_no_server();
 1022         /*NOTREACHED*/
 1023 }
 1024 
 1025 /*
 1026  *      Routine:        exception_raise_continue_fast
 1027  *      Purpose:
 1028  *              Special-purpose fast continuation for exceptions.
 1029  *      Conditions:
 1030  *              reply_port is locked and alive.
 1031  *              kmsg is our reply message.
 1032  *      Returns:
 1033  *              Doesn't return.
 1034  */
 1035 
 1036 void
 1037 exception_raise_continue_fast(reply_port, kmsg)
 1038         ipc_port_t reply_port;
 1039         ipc_kmsg_t kmsg;
 1040 {
 1041         ipc_thread_t self = current_thread();
 1042         kern_return_t kr;
 1043 
 1044         assert(ip_active(reply_port));
 1045         assert(reply_port == self->ith_port);
 1046         assert(reply_port == (ipc_port_t) kmsg->ikm_header.msgh_remote_port);
 1047         assert(MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
 1048                                                 MACH_MSG_TYPE_PORT_SEND_ONCE);
 1049 
 1050         /*
 1051          *      Release the send-once right (from the message header)
 1052          *      and the saved reference (from self->ith_port).
 1053          */
 1054 
 1055         reply_port->ip_sorights--;
 1056         ip_release(reply_port);
 1057         ip_release(reply_port);
 1058         ip_unlock(reply_port);
 1059 
 1060         /*
 1061          *      Consume the reply message.
 1062          */
 1063 
 1064         kr = exception_parse_reply(kmsg);
 1065         if (kr == KERN_SUCCESS) {
 1066                 thread_exception_return();
 1067                 /*NOTREACHED*/
 1068                 return; /* help for the compiler */
 1069         }
 1070 
 1071         if (self->ith_exc != KERN_SUCCESS) {
 1072                 exception_try_task(self->ith_exc,
 1073                                    self->ith_exc_code,
 1074                                    self->ith_exc_subcode);
 1075                 /*NOTREACHED*/
 1076         }
 1077 
 1078         exception_no_server();
 1079         /*NOTREACHED*/
 1080 }

Cache object: 42a231ef37a7dc46842433423d12ff87


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.