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