darwin-xnu/osfmk/kern/exception.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
   3 *
   4 * @APPLE_LICENSE_HEADER_START@
   5 * 
   6 * The contents of this file constitute Original Code as defined in and
   7 * are subject to the Apple Public Source License Version 1.1 (the
   8 * "License").  You may not use this file except in compliance with the
   9 * License.  Please obtain a copy of the License at
  10 * http://www.apple.com/publicsource and read it before using this file.
  11 * 
  12 * This Original Code and all software distributed under the License are
  13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
  17 * License for the specific language governing rights and limitations
  18 * under the License.
  19 * 
  20 * @APPLE_LICENSE_HEADER_END@
  21 */
  22/*
  23 * @OSF_COPYRIGHT@
  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  Software.Distribution@CS.CMU.EDU
  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 */
  52
  53#include <mach_kdb.h>
  54
  55#include <mach/mach_types.h>
  56#include <mach/boolean.h>
  57#include <mach/kern_return.h>
  58#include <mach/message.h>
  59#include <mach/port.h>
  60#include <mach/mig_errors.h>
  61#include <mach/task.h>
  62#include <mach/thread_status.h>
  63#include <mach/exception_types.h>
  64#include <ipc/port.h>
  65#include <ipc/ipc_entry.h>
  66#include <ipc/ipc_object.h>
  67#include <ipc/ipc_notify.h>
  68#include <ipc/ipc_space.h>
  69#include <ipc/ipc_pset.h>
  70#include <ipc/ipc_machdep.h>
  71#include <kern/counters.h>
  72#include <kern/ipc_tt.h>
  73#include <kern/task.h>
  74#include <kern/thread.h>
  75#include <kern/processor.h>
  76#include <kern/sched.h>
  77#include <kern/sched_prim.h>
  78#include <kern/host.h>
  79#include <kern/misc_protos.h>
  80#include <string.h>
  81#include <mach/exc.h>
  82
  83#if     MACH_KDB
  84#include <ddb/db_trap.h>
  85#endif  /* MACH_KDB */
  86
  87#if     MACH_KDB
  88
  89#include <ddb/db_output.h>
  90
  91#if iPSC386 || iPSC860
  92boolean_t debug_user_with_kdb = TRUE;
  93#else
  94boolean_t debug_user_with_kdb = FALSE;
  95#endif
  96
  97#endif  /* MACH_KDB */
  98
  99unsigned long c_thr_exc_raise = 0;
 100unsigned long c_thr_exc_raise_state = 0;
 101unsigned long c_thr_exc_raise_state_id = 0;
 102unsigned long c_tsk_exc_raise = 0;
 103unsigned long c_tsk_exc_raise_state = 0;
 104unsigned long c_tsk_exc_raise_state_id = 0;
 105
 106/* forward declarations */
 107void exception_deliver(
 108        exception_type_t        exception,
 109        exception_data_t        code,
 110        mach_msg_type_number_t  codeCnt,
 111        struct exception_action *excp,
 112        mutex_t                 *mutex);
 113
 114#ifdef MACH_BSD
 115kern_return_t bsd_exception(
 116        exception_type_t        exception,
 117        exception_data_t        code,
 118        mach_msg_type_number_t  codeCnt);
 119#endif /* MACH_BSD */
 120
 121/*
 122 *      Routine:        exception_deliver
 123 *      Purpose:
 124 *              Make an upcall to the exception server provided.
 125 *      Conditions:
 126 *              Nothing locked and no resources held.
 127 *              Called from an exception context, so
 128 *              thread_exception_return and thread_kdb_return
 129 *              are possible.
 130 *      Returns:
 131 *              If the exception was not handled by this handler
 132 */
 133void
 134exception_deliver(
 135        exception_type_t        exception,
 136        exception_data_t        code,
 137        mach_msg_type_number_t  codeCnt,
 138        struct exception_action *excp,
 139        mutex_t                 *mutex)
 140{
 141        thread_t                self = current_thread();
 142        ipc_port_t              exc_port;
 143        int                     behavior;
 144        int                     flavor;
 145        kern_return_t           kr;
 146
 147        /*
 148         *  Save work if we are terminating.
 149         *  Just go back to our AST handler.
 150         */
 151        if (!self->active)
 152                thread_exception_return();
 153
 154        /*
 155         * Snapshot the exception action data under lock for consistency.
 156         * Hold a reference to the port over the exception_raise_* calls
 157         * so it can't be destroyed.  This seems like overkill, but keeps
 158         * the port from disappearing between now and when
 159         * ipc_object_copyin_from_kernel is finally called.
 160         */
 161        mutex_lock(mutex);
 162        exc_port = excp->port;
 163        if (!IP_VALID(exc_port)) {
 164                mutex_unlock(mutex);
 165                return;
 166        }
 167        ip_lock(exc_port);
 168        if (!ip_active(exc_port)) {
 169                ip_unlock(exc_port);
 170                mutex_unlock(mutex);
 171                return;
 172        }
 173        ip_reference(exc_port); 
 174        exc_port->ip_srights++;
 175        ip_unlock(exc_port);
 176
 177        flavor = excp->flavor;
 178        behavior = excp->behavior;
 179        mutex_unlock(mutex);
 180
 181        switch (behavior) {
 182        case EXCEPTION_STATE: {
 183                mach_msg_type_number_t state_cnt;
 184                thread_state_data_t state;
 185
 186                c_thr_exc_raise_state++;
 187                state_cnt = _MachineStateCount[flavor];
 188                kr = thread_getstatus(self, flavor, 
 189                                      (thread_state_t)state,
 190                                      &state_cnt);
 191                if (kr == KERN_SUCCESS) {
 192                        kr = exception_raise_state(exc_port, exception,
 193                                                   code, codeCnt,
 194                                                   &flavor,
 195                                                   state, state_cnt,
 196                                                   state, &state_cnt);
 197                        if (kr == MACH_MSG_SUCCESS)
 198                                kr = thread_setstatus(self, flavor, 
 199                                                      (thread_state_t)state,
 200                                                      state_cnt);
 201                }
 202
 203                if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
 204                        thread_exception_return();
 205                        /*NOTREACHED*/
 206                return;
 207        }
 208
 209        case EXCEPTION_DEFAULT:
 210                c_thr_exc_raise++;
 211                kr = exception_raise(exc_port,
 212                                retrieve_thread_self_fast(self),
 213                                retrieve_task_self_fast(self->task),
 214                                exception,
 215                                code, codeCnt);
 216
 217                if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
 218                        thread_exception_return();
 219                        /*NOTREACHED*/
 220                return;
 221
 222        case EXCEPTION_STATE_IDENTITY: {
 223                mach_msg_type_number_t state_cnt;
 224                thread_state_data_t state;
 225
 226                c_thr_exc_raise_state_id++;
 227                state_cnt = _MachineStateCount[flavor];
 228                kr = thread_getstatus(self, flavor,
 229                                      (thread_state_t)state,
 230                                      &state_cnt);
 231                if (kr == KERN_SUCCESS) {
 232                    kr = exception_raise_state_identity(exc_port,
 233                                retrieve_thread_self_fast(self),
 234                                retrieve_task_self_fast(self->task),
 235                                exception,
 236                                code, codeCnt,
 237                                &flavor,
 238                                state, state_cnt,
 239                                state, &state_cnt);
 240                    if (kr == MACH_MSG_SUCCESS)
 241                        kr = thread_setstatus(self, flavor,
 242                                              (thread_state_t)state,
 243                                              state_cnt);
 244                }
 245
 246                if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
 247                        thread_exception_return();
 248                        /*NOTREACHED*/
 249                return;
 250        }
 251        
 252        default:
 253                panic ("bad exception behavior!");
 254        }/* switch */
 255}
 256
 257/*
 258 *      Routine:        exception
 259 *      Purpose:
 260 *              The current thread caught an exception.
 261 *              We make an up-call to the thread's exception server.
 262 *      Conditions:
 263 *              Nothing locked and no resources held.
 264 *              Called from an exception context, so
 265 *              thread_exception_return and thread_kdb_return
 266 *              are possible.
 267 *      Returns:
 268 *              Doesn't return.
 269 */
 270void
 271exception_triage(
 272        exception_type_t        exception,
 273        exception_data_t        code,
 274        mach_msg_type_number_t  codeCnt)
 275{
 276        thread_t                thread;
 277        task_t                  task;
 278        host_priv_t             host_priv;
 279        struct exception_action *excp;
 280        mutex_t                 *mutex;
 281
 282        assert(exception != EXC_RPC_ALERT);
 283
 284        if (exception == KERN_SUCCESS)
 285                panic("exception");
 286
 287        /*
 288         * Try to raise the exception at the activation level.
 289         */
 290        thread = current_thread();
 291        mutex = mutex_addr(thread->mutex);
 292        excp = &thread->exc_actions[exception];
 293        exception_deliver(exception, code, codeCnt, excp, mutex);
 294
 295        /*
 296         * Maybe the task level will handle it.
 297         */
 298        task = current_task();
 299        mutex = mutex_addr(task->lock);
 300        excp = &task->exc_actions[exception];
 301        exception_deliver(exception, code, codeCnt, excp, mutex);
 302
 303        /*
 304         * How about at the host level?
 305         */
 306        host_priv = host_priv_self();
 307        mutex = mutex_addr(host_priv->lock);
 308        excp = &host_priv->exc_actions[exception];
 309        exception_deliver(exception, code, codeCnt, excp, mutex);
 310
 311        /*
 312         * Nobody handled it, terminate the task.
 313         */
 314
 315#if     MACH_KDB
 316        if (debug_user_with_kdb) {
 317                /*
 318                 *      Debug the exception with kdb.
 319                 *      If kdb handles the exception,
 320                 *      then thread_kdb_return won't return.
 321                 */
 322                db_printf("No exception server, calling kdb...\n");
 323                thread_kdb_return();
 324        }
 325#endif  /* MACH_KDB */
 326
 327        (void) task_terminate(task);
 328        thread_exception_return();
 329        /*NOTREACHED*/
 330}
 331
 332kern_return_t
 333bsd_exception(
 334        exception_type_t        exception,
 335        exception_data_t        code,
 336        mach_msg_type_number_t  codeCnt)
 337{
 338        task_t                  task;
 339        struct exception_action *excp;
 340        mutex_t                 *mutex;
 341        thread_t                self = current_thread();
 342        ipc_port_t              exc_port;
 343        int                     behavior;
 344        int                     flavor;
 345        kern_return_t           kr;
 346
 347        /*
 348         * Maybe the task level will handle it.
 349         */
 350        task = current_task();
 351        mutex = mutex_addr(task->lock);
 352        excp = &task->exc_actions[exception];
 353
 354        /*
 355         *  Save work if we are terminating.
 356         *  Just go back to our AST handler.
 357         */
 358        if (!self->active) {
 359                return(KERN_FAILURE);
 360        }
 361
 362        /*
 363         * Snapshot the exception action data under lock for consistency.
 364         * Hold a reference to the port over the exception_raise_* calls
 365         * so it can't be destroyed.  This seems like overkill, but keeps
 366         * the port from disappearing between now and when
 367         * ipc_object_copyin_from_kernel is finally called.
 368         */
 369        mutex_lock(mutex);
 370        exc_port = excp->port;
 371        if (!IP_VALID(exc_port)) {
 372                mutex_unlock(mutex);
 373                return(KERN_FAILURE);
 374        }
 375        ip_lock(exc_port);
 376        if (!ip_active(exc_port)) {
 377                ip_unlock(exc_port);
 378                mutex_unlock(mutex);
 379                return(KERN_FAILURE);
 380        }
 381        ip_reference(exc_port); 
 382        exc_port->ip_srights++;
 383        ip_unlock(exc_port);
 384
 385        flavor = excp->flavor;
 386        behavior = excp->behavior;
 387        mutex_unlock(mutex);
 388
 389        switch (behavior) {
 390        case EXCEPTION_STATE: {
 391                mach_msg_type_number_t state_cnt;
 392                thread_state_data_t state;
 393
 394                c_thr_exc_raise_state++;
 395                state_cnt = _MachineStateCount[flavor];
 396                kr = thread_getstatus(self, flavor, 
 397                                      (thread_state_t)state,
 398                                      &state_cnt);
 399                if (kr == KERN_SUCCESS) {
 400                        kr = exception_raise_state(exc_port, exception,
 401                                                   code, codeCnt,
 402                                                   &flavor,
 403                                                   state, state_cnt,
 404                                                   state, &state_cnt);
 405                        if (kr == MACH_MSG_SUCCESS)
 406                                kr = thread_setstatus(self, flavor, 
 407                                                      (thread_state_t)state,
 408                                                      state_cnt);
 409                }
 410
 411                if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
 412                        return(KERN_SUCCESS);
 413
 414                return(KERN_FAILURE);
 415        }
 416
 417        case EXCEPTION_DEFAULT:
 418                c_thr_exc_raise++;
 419                kr = exception_raise(exc_port,
 420                                retrieve_thread_self_fast(self),
 421                                retrieve_task_self_fast(self->task),
 422                                exception,
 423                                code, codeCnt);
 424
 425                if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
 426                        return(KERN_SUCCESS);
 427                return(KERN_FAILURE);
 428
 429        case EXCEPTION_STATE_IDENTITY: {
 430                mach_msg_type_number_t state_cnt;
 431                thread_state_data_t state;
 432
 433                c_thr_exc_raise_state_id++;
 434                state_cnt = _MachineStateCount[flavor];
 435                kr = thread_getstatus(self, flavor,
 436                                      (thread_state_t)state,
 437                                      &state_cnt);
 438                if (kr == KERN_SUCCESS) {
 439                    kr = exception_raise_state_identity(exc_port,
 440                                retrieve_thread_self_fast(self),
 441                                retrieve_task_self_fast(self->task),
 442                                exception,
 443                                code, codeCnt,
 444                                &flavor,
 445                                state, state_cnt,
 446                                state, &state_cnt);
 447                    if (kr == MACH_MSG_SUCCESS)
 448                        kr = thread_setstatus(self, flavor,
 449                                              (thread_state_t)state,
 450                                              state_cnt);
 451                }
 452
 453                if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED)
 454                        return(KERN_SUCCESS);
 455                return(KERN_FAILURE);
 456        }
 457        
 458        default:
 459                
 460                return(KERN_FAILURE);
 461        }/* switch */
 462        return(KERN_FAILURE);
 463}
 464
 465
 466
 467
 468/*
 469 *      Handle interface for special perfomance monitoring
 470 *      This is a special case of the host exception handler
 471 */
 472
 473kern_return_t sys_perf_notify(struct task *task,
 474        exception_data_t code,
 475        mach_msg_type_number_t codeCnt)
 476{
 477        host_priv_t             hostp;
 478        struct exception_action *excp;
 479        thread_t                thread = current_thread();
 480        ipc_port_t              xport;
 481        kern_return_t   ret;
 482        wait_interrupt_t        wsave;
 483
 484        hostp = host_priv_self();                               /* Get the host privileged ports */
 485        excp = &hostp->exc_actions[EXC_RPC_ALERT];      /* Point to the RPC_ALERT action */
 486
 487        mutex_lock(&hostp->lock);                               /* Lock the priv port */
 488        xport = excp->port;                                             /* Get the port for this exception */
 489        if (!IP_VALID(xport)) {                                 /* Is it valid? */
 490                mutex_unlock(&hostp->lock);                     /* Unlock */
 491                return(KERN_FAILURE);                           /* Go away... */
 492        }
 493
 494        ip_lock(xport);                                                 /* Lock the exception port */
 495        if (!ip_active(xport)) {                                /* and is it active? */
 496                ip_unlock(xport);                                       /* Nope, fail */
 497                mutex_unlock(&hostp->lock);                     /* Unlock */
 498                return(KERN_FAILURE);                           /* Go away... */
 499        }
 500
 501        if (task->itk_space == xport->data.receiver) {  /* Are we trying to send to ourselves? */
 502                ip_unlock(xport);                                       /* Yes, fail */
 503                mutex_unlock(&hostp->lock);                     /* Unlock */
 504                return(KERN_FAILURE);                           /* Go away... */
 505        }
 506        
 507        ip_reference(xport);                                    /* Bump reference so it doesn't go away */
 508        xport->ip_srights++;                                    /* Bump send rights */
 509        ip_unlock(xport);                                               /* We can unlock it now */
 510
 511        mutex_unlock(&hostp->lock);                             /* All done with the lock */
 512
 513        wsave = thread_interrupt_level(THREAD_UNINT);   /* Make sure we aren't aborted here */
 514        
 515        ret = exception_raise(xport,                    /* Send the exception to the perf handler */
 516                retrieve_thread_self_fast(thread),              /* Not always the dying guy */
 517                retrieve_task_self_fast(thread->task),  /* Not always the dying guy */
 518                EXC_RPC_ALERT,                                          /* Unused exception type until now */
 519                code, codeCnt); 
 520                
 521        (void)thread_interrupt_level(wsave);    /* Restore interrupt level */                   
 522
 523        return(ret);                                                    /* Tell caller how it went */
 524}
 525
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.