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

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

unix.superglobalmegacorp.com

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