Annotation of kernel/kern/exception.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

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