|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.