Annotation of OSKit-Mach/kern/exception.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

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