1/* 2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * @OSF_COPYRIGHT@ 30 */ 31/* 32 * Mach Operating System 33 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University 34 * All Rights Reserved. 35 * 36 * Permission to use, copy, modify and distribute this software and its 37 * documentation is hereby granted, provided that both the copyright 38 * notice and this permission notice appear in all copies of the 39 * software, derivative works or modified versions, and any portions 40 * thereof, and that both notices appear in supporting documentation. 41 * 42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 45 * 46 * Carnegie Mellon requests users of this software to return to 47 * 48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 49 * School of Computer Science 50 * Carnegie Mellon University 51 * Pittsburgh PA 15213-3890 52 * 53 * any improvements or extensions that they make and grant Carnegie Mellon 54 * the rights to redistribute these changes. 55 */ 56/* 57 */ 58 59#include <mach/mach_types.h> 60#include <mach/boolean.h> 61#include <mach/kern_return.h> 62#include <mach/message.h> 63#include <mach/port.h> 64#include <mach/mig_errors.h> 65#include <mach/task.h> 66#include <mach/thread_status.h> 67#include <mach/exception_types.h> 68#include <mach/exc.h> 69#include <mach/mach_exc.h> 70#include <ipc/port.h> 71#include <ipc/ipc_entry.h> 72#include <ipc/ipc_object.h> 73#include <ipc/ipc_notify.h> 74#include <ipc/ipc_space.h> 75#include <ipc/ipc_pset.h> 76#include <ipc/ipc_machdep.h> 77#include <kern/counters.h> 78#include <kern/ipc_tt.h> 79#include <kern/task.h> 80#include <kern/thread.h> 81#include <kern/processor.h> 82#include <kern/sched.h> 83#include <kern/sched_prim.h> 84#include <kern/host.h> 85#include <kern/misc_protos.h> 86#include <string.h> 87#include <pexpert/pexpert.h> 88 89unsigned long c_thr_exc_raise = 0; 90unsigned long c_thr_exc_raise_state = 0; 91unsigned long c_thr_exc_raise_state_id = 0; 92unsigned long c_tsk_exc_raise = 0; 93unsigned long c_tsk_exc_raise_state = 0; 94unsigned long c_tsk_exc_raise_state_id = 0; 95 96/* forward declarations */ 97kern_return_t exception_deliver( 98 thread_t thread, 99 exception_type_t exception, 100 mach_exception_data_t code, 101 mach_msg_type_number_t codeCnt, 102 struct exception_action *excp, 103 lck_mtx_t *mutex); 104 105#ifdef MACH_BSD 106kern_return_t bsd_exception( 107 exception_type_t exception, 108 mach_exception_data_t code, 109 mach_msg_type_number_t codeCnt); 110#endif /* MACH_BSD */ 111 112/* 113 * Routine: exception_deliver 114 * Purpose: 115 * Make an upcall to the exception server provided. 116 * Conditions: 117 * Nothing locked and no resources held. 118 * Called from an exception context, so 119 * thread_exception_return and thread_kdb_return 120 * are possible. 121 * Returns: 122 * KERN_SUCCESS if the exception was handled 123 */ 124kern_return_t 125exception_deliver( 126 thread_t thread, 127 exception_type_t exception, 128 mach_exception_data_t code, 129 mach_msg_type_number_t codeCnt, 130 struct exception_action *excp, 131 lck_mtx_t *mutex) 132{ 133 ipc_port_t exc_port; 134 exception_data_type_t small_code[EXCEPTION_CODE_MAX]; 135 int code64; 136 int behavior; 137 int flavor; 138 kern_return_t kr; 139 140 /* 141 * Save work if we are terminating. 142 * Just go back to our AST handler. 143 */ 144 if (!thread->active) 145 return KERN_SUCCESS; 146 147 /* 148 * If there are no exception actions defined for this entity, 149 * we can't deliver here. 150 */ 151 if (excp == NULL) 152 return KERN_FAILURE; 153 154 assert(exception < EXC_TYPES_COUNT); 155 if (exception >= EXC_TYPES_COUNT) 156 return KERN_FAILURE; 157 158 excp = &excp[exception]; 159 160 /* 161 * Snapshot the exception action data under lock for consistency. 162 * Hold a reference to the port over the exception_raise_* calls 163 * so it can't be destroyed. This seems like overkill, but keeps 164 * the port from disappearing between now and when 165 * ipc_object_copyin_from_kernel is finally called. 166 */ 167 lck_mtx_lock(mutex); 168 exc_port = excp->port; 169 if (!IP_VALID(exc_port)) { 170 lck_mtx_unlock(mutex); 171 return KERN_FAILURE; 172 } 173 ip_lock(exc_port); 174 if (!ip_active(exc_port)) { 175 ip_unlock(exc_port); 176 lck_mtx_unlock(mutex); 177 return KERN_FAILURE; 178 } 179 ip_reference(exc_port); 180 exc_port->ip_srights++; 181 ip_unlock(exc_port); 182 183 flavor = excp->flavor; 184 behavior = excp->behavior; 185 lck_mtx_unlock(mutex); 186 187 code64 = (behavior & MACH_EXCEPTION_CODES); 188 behavior &= ~MACH_EXCEPTION_CODES; 189 190 if (!code64) { 191 small_code[0] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[0]); 192 small_code[1] = CAST_DOWN_EXPLICIT(exception_data_type_t, code[1]); 193 } 194 195 196 switch (behavior) { 197 case EXCEPTION_STATE: { 198 mach_msg_type_number_t state_cnt; 199 thread_state_data_t state; 200 201 c_thr_exc_raise_state++; 202 state_cnt = _MachineStateCount[flavor]; 203 kr = thread_getstatus(thread, flavor, 204 (thread_state_t)state, 205 &state_cnt); 206 if (kr == KERN_SUCCESS) { 207 if (code64) { 208 kr = mach_exception_raise_state(exc_port, 209 exception, 210 code, 211 codeCnt, 212 &flavor, 213 state, state_cnt, 214 state, &state_cnt); 215 } else { 216 kr = exception_raise_state(exc_port, exception, 217 small_code, 218 codeCnt, 219 &flavor, 220 state, state_cnt, 221 state, &state_cnt); 222 } 223 if (kr == MACH_MSG_SUCCESS) 224 kr = thread_setstatus(thread, flavor, 225 (thread_state_t)state, 226 state_cnt); 227 } 228 229 return kr; 230 } 231 232 case EXCEPTION_DEFAULT: 233 c_thr_exc_raise++; 234 if (code64) { 235 kr = mach_exception_raise(exc_port, 236 retrieve_thread_self_fast(thread), 237 retrieve_task_self_fast(thread->task), 238 exception, 239 code, 240 codeCnt); 241 } else { 242 kr = exception_raise(exc_port, 243 retrieve_thread_self_fast(thread), 244 retrieve_task_self_fast(thread->task), 245 exception, 246 small_code, 247 codeCnt); 248 } 249 250 return kr; 251 252 case EXCEPTION_STATE_IDENTITY: { 253 mach_msg_type_number_t state_cnt; 254 thread_state_data_t state; 255 256 c_thr_exc_raise_state_id++; 257 state_cnt = _MachineStateCount[flavor]; 258 kr = thread_getstatus(thread, flavor, 259 (thread_state_t)state, 260 &state_cnt); 261 if (kr == KERN_SUCCESS) { 262 if (code64) { 263 kr = mach_exception_raise_state_identity( 264 exc_port, 265 retrieve_thread_self_fast(thread), 266 retrieve_task_self_fast(thread->task), 267 exception, 268 code, 269 codeCnt, 270 &flavor, 271 state, state_cnt, 272 state, &state_cnt); 273 } else { 274 kr = exception_raise_state_identity(exc_port, 275 retrieve_thread_self_fast(thread), 276 retrieve_task_self_fast(thread->task), 277 exception, 278 small_code, 279 codeCnt, 280 &flavor, 281 state, state_cnt, 282 state, &state_cnt); 283 } 284 if (kr == MACH_MSG_SUCCESS) 285 kr = thread_setstatus(thread, flavor, 286 (thread_state_t)state, 287 state_cnt); 288 } 289 290 return kr; 291 } 292 293 default: 294 panic ("bad exception behavior!"); 295 return KERN_FAILURE; 296 }/* switch */ 297} 298 299/* 300 * Routine: exception 301 * Purpose: 302 * The current thread caught an exception. 303 * We make an up-call to the thread's exception server. 304 * Conditions: 305 * Nothing locked and no resources held. 306 * Called from an exception context, so 307 * thread_exception_return and thread_kdb_return 308 * are possible. 309 * Returns: 310 * Doesn't return. 311 */ 312void 313exception_triage( 314 exception_type_t exception, 315 mach_exception_data_t code, 316 mach_msg_type_number_t codeCnt) 317{ 318 thread_t thread; 319 task_t task; 320 host_priv_t host_priv; 321 lck_mtx_t *mutex; 322 kern_return_t kr; 323 324 assert(exception != EXC_RPC_ALERT); 325 326 thread = current_thread(); 327 328 /* 329 * Try to raise the exception at the activation level. 330 */ 331 mutex = &thread->mutex; 332 kr = exception_deliver(thread, exception, code, codeCnt, thread->exc_actions, mutex); 333 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) 334 goto out; 335 336 /* 337 * Maybe the task level will handle it. 338 */ 339 task = current_task(); 340 mutex = &task->lock; 341 kr = exception_deliver(thread, exception, code, codeCnt, task->exc_actions, mutex); 342 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) 343 goto out; 344 345 /* 346 * How about at the host level? 347 */ 348 host_priv = host_priv_self(); 349 mutex = &host_priv->lock; 350 kr = exception_deliver(thread, exception, code, codeCnt, host_priv->exc_actions, mutex); 351 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) 352 goto out; 353 354 /* 355 * Nobody handled it, terminate the task. 356 */ 357 358 (void) task_terminate(task); 359 360out: 361 if ((exception != EXC_CRASH) && (exception != EXC_RESOURCE) && 362 (exception != EXC_GUARD)) 363 thread_exception_return(); 364 return; 365} 366 367kern_return_t 368bsd_exception( 369 exception_type_t exception, 370 mach_exception_data_t code, 371 mach_msg_type_number_t codeCnt) 372{ 373 task_t task; 374 lck_mtx_t *mutex; 375 thread_t self = current_thread(); 376 kern_return_t kr; 377 378 /* 379 * Maybe the task level will handle it. 380 */ 381 task = current_task(); 382 mutex = &task->lock; 383 384 kr = exception_deliver(self, exception, code, codeCnt, task->exc_actions, mutex); 385 386 if (kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED) 387 return(KERN_SUCCESS); 388 return(KERN_FAILURE); 389} 390 391 392/* 393 * Raise an exception on a task. 394 * This should tell launchd to launch Crash Reporter for this task. 395 */ 396kern_return_t task_exception_notify(exception_type_t exception, 397 mach_exception_data_type_t exccode, mach_exception_data_type_t excsubcode) 398{ 399 mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; 400 wait_interrupt_t wsave; 401 402 code[0] = exccode; 403 code[1] = excsubcode; 404 405 wsave = thread_interrupt_level(THREAD_UNINT); 406 exception_triage(exception, code, EXCEPTION_CODE_MAX); 407 (void) thread_interrupt_level(wsave); 408 return (KERN_SUCCESS); 409} 410 411 412/* 413 * Handle interface for special performance monitoring 414 * This is a special case of the host exception handler 415 */ 416kern_return_t sys_perf_notify(thread_t thread, int pid) 417{ 418 host_priv_t hostp; 419 ipc_port_t xport; 420 wait_interrupt_t wsave; 421 kern_return_t ret; 422 423 hostp = host_priv_self(); /* Get the host privileged ports */ 424 mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; 425 code[0] = 0xFF000001; /* Set terminate code */ 426 code[1] = pid; /* Pass out the pid */ 427 428 struct task *task = thread->task; 429 xport = hostp->exc_actions[EXC_RPC_ALERT].port; 430 431 /* Make sure we're not catching our own exception */ 432 if (!IP_VALID(xport) || 433 !ip_active(xport) || 434 task->itk_space == xport->data.receiver) { 435 436 return(KERN_FAILURE); 437 } 438 439 wsave = thread_interrupt_level(THREAD_UNINT); 440 ret = exception_deliver( 441 thread, 442 EXC_RPC_ALERT, 443 code, 444 2, 445 hostp->exc_actions, 446 &hostp->lock); 447 (void)thread_interrupt_level(wsave); 448 449 return(ret); 450} 451 452