Annotation of kernel/kern/exception.c, revision 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.