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