Annotation of Gnu-Mach/kern/exception.c, revision 1.1.1.6

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.