|
|
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.