|
|
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 (thread_suspend (self) == KERN_SUCCESS)
248: thread_exception_return ();
249:
250: #if MACH_KDB
251: if (debug_user_with_kdb) {
252: /*
253: * Debug the exception with kdb.
254: * If kdb handles the exception,
255: * then thread_kdb_return won't return.
256: */
257:
258: db_printf("No exception server, calling kdb...\n");
259: thread_kdb_return();
260: }
261: #endif MACH_KDB
262:
263: /*
264: * All else failed; terminate task.
265: */
266:
267: (void) task_terminate(self->task);
268: thread_halt_self();
269: /*NOTREACHED*/
270: }
271:
272: #define MACH_EXCEPTION_ID 2400 /* from mach/exc.defs */
273: #define MACH_EXCEPTION_REPLY_ID (MACH_EXCEPTION_ID + 100)
274:
275: struct mach_exception {
276: mach_msg_header_t Head;
277: mach_msg_type_t threadType;
278: mach_port_t thread;
279: mach_msg_type_t taskType;
280: mach_port_t task;
281: mach_msg_type_t exceptionType;
282: integer_t exception;
283: mach_msg_type_t codeType;
284: integer_t code;
285: mach_msg_type_t subcodeType;
286: integer_t subcode;
287: };
288:
289: #define INTEGER_T_SIZE_IN_BITS (8 * sizeof(integer_t))
290: #define INTEGER_T_TYPE MACH_MSG_TYPE_INTEGER_T
291: /* in mach/machine/vm_types.h */
292:
293: mach_msg_type_t exc_port_proto = {
294: /* msgt_name = */ MACH_MSG_TYPE_PORT_SEND,
295: /* msgt_size = */ PORT_T_SIZE_IN_BITS,
296: /* msgt_number = */ 1,
297: /* msgt_inline = */ TRUE,
298: /* msgt_longform = */ FALSE,
299: /* msgt_deallocate = */ FALSE,
300: /* msgt_unused = */ 0
301: };
302:
303: mach_msg_type_t exc_code_proto = {
304: /* msgt_name = */ INTEGER_T_TYPE,
305: /* msgt_size = */ INTEGER_T_SIZE_IN_BITS,
306: /* msgt_number = */ 1,
307: /* msgt_inline = */ TRUE,
308: /* msgt_longform = */ FALSE,
309: /* msgt_deallocate = */ FALSE,
310: /* msgt_unused = */ 0
311: };
312:
313: /*
314: * Routine: exception_raise
315: * Purpose:
316: * Make an exception_raise up-call to an exception server.
317: *
318: * dest_port must be a valid naked send right.
319: * thread_port and task_port are naked send rights.
320: * All three are always consumed.
321: *
322: * self->ith_exc, self->ith_exc_code, self->ith_exc_subcode
323: * must be appropriately initialized.
324: * Conditions:
325: * Nothing locked. We are being called in an exception context,
326: * so thread_exception_return may be called.
327: * Returns:
328: * Doesn't return.
329: */
330:
331: int exception_raise_misses = 0;
332:
333: void
334: exception_raise(dest_port, thread_port, task_port,
335: _exception, code, subcode)
336: ipc_port_t dest_port;
337: ipc_port_t thread_port;
338: ipc_port_t task_port;
339: integer_t _exception, code, subcode;
340: {
341: ipc_thread_t self = current_thread();
342: ipc_thread_t receiver;
343: ipc_port_t reply_port;
344: ipc_mqueue_t dest_mqueue;
345: ipc_mqueue_t reply_mqueue;
346: ipc_kmsg_t kmsg;
347: mach_msg_return_t mr;
348:
349: assert(IP_VALID(dest_port));
350:
351: /*
352: * We will eventually need a message buffer.
353: * Grab the buffer now, while nothing is locked.
354: * This buffer will get handed to the exception server,
355: * and it will give the buffer back with its reply.
356: */
357:
358: kmsg = ikm_cache();
359: if (kmsg != IKM_NULL) {
360: ikm_cache() = IKM_NULL;
361: ikm_check_initialized(kmsg, IKM_SAVED_KMSG_SIZE);
362: } else {
363: kmsg = ikm_alloc(IKM_SAVED_MSG_SIZE);
364: if (kmsg == IKM_NULL)
365: panic("exception_raise");
366: ikm_init(kmsg, IKM_SAVED_MSG_SIZE);
367: }
368:
369: /*
370: * We need a reply port for the RPC.
371: * Check first for a cached port.
372: */
373:
374: ith_lock(self);
375: assert(self->ith_self != IP_NULL);
376:
377: reply_port = self->ith_rpc_reply;
378: if (reply_port == IP_NULL) {
379: ith_unlock(self);
380: reply_port = ipc_port_alloc_reply();
381: ith_lock(self);
382: if ((reply_port == IP_NULL) ||
383: (self->ith_rpc_reply != IP_NULL))
384: panic("exception_raise");
385: self->ith_rpc_reply = reply_port;
386: }
387:
388: ip_lock(reply_port);
389: assert(ip_active(reply_port));
390: ith_unlock(self);
391:
392: /*
393: * Make a naked send-once right for the reply port,
394: * to hand to the exception server.
395: * Make an extra reference for the reply port,
396: * to receive on. This protects us against
397: * mach_msg_abort_rpc.
398: */
399:
400: reply_port->ip_sorights++;
401: ip_reference(reply_port);
402:
403: ip_reference(reply_port);
404: self->ith_port = reply_port;
405:
406: reply_mqueue = &reply_port->ip_messages;
407: imq_lock(reply_mqueue);
408: assert(ipc_kmsg_queue_empty(&reply_mqueue->imq_messages));
409: ip_unlock(reply_port);
410:
411: /*
412: * Make sure we can queue to the destination port.
413: */
414:
415: if (!ip_lock_try(dest_port)) {
416: imq_unlock(reply_mqueue);
417: goto slow_exception_raise;
418: }
419:
420: if (!ip_active(dest_port) ||
421: #if NORMA_IPC
422: IP_NORMA_IS_PROXY(dest_port) ||
423: #endif NORMA_IPC
424: (dest_port->ip_receiver == ipc_space_kernel)) {
425: imq_unlock(reply_mqueue);
426: ip_unlock(dest_port);
427: goto slow_exception_raise;
428: }
429:
430: /*
431: * Find the destination message queue.
432: */
433:
434: {
435: register ipc_pset_t dest_pset;
436:
437: dest_pset = dest_port->ip_pset;
438: if (dest_pset == IPS_NULL)
439: dest_mqueue = &dest_port->ip_messages;
440: else
441: dest_mqueue = &dest_pset->ips_messages;
442: }
443:
444: if (!imq_lock_try(dest_mqueue)) {
445: imq_unlock(reply_mqueue);
446: ip_unlock(dest_port);
447: goto slow_exception_raise;
448: }
449:
450: /*
451: * Safe to unlock dest_port, because we hold
452: * dest_mqueue locked. We never bother changing
453: * dest_port->ip_msgcount.
454: */
455:
456: ip_unlock(dest_port);
457:
458: receiver = ipc_thread_queue_first(&dest_mqueue->imq_threads);
459: if ((receiver == ITH_NULL) ||
460: !((receiver->swap_func == (void (*)()) mach_msg_continue) ||
461: ((receiver->swap_func ==
462: (void (*)()) mach_msg_receive_continue) &&
463: (sizeof(struct mach_exception) <= receiver->ith_msize) &&
464: ((receiver->ith_option & MACH_RCV_NOTIFY) == 0))) ||
465: !thread_handoff(self, exception_raise_continue, receiver)) {
466: imq_unlock(reply_mqueue);
467: imq_unlock(dest_mqueue);
468: goto slow_exception_raise;
469: }
470: counter(c_exception_raise_block++);
471:
472: assert(current_thread() == receiver);
473:
474: /*
475: * We need to finish preparing self for its
476: * time asleep in reply_mqueue. self is left
477: * holding the extra ref for reply_port.
478: */
479:
480: ipc_thread_enqueue_macro(&reply_mqueue->imq_threads, self);
481: self->ith_state = MACH_RCV_IN_PROGRESS;
482: self->ith_msize = MACH_MSG_SIZE_MAX;
483: imq_unlock(reply_mqueue);
484:
485: /*
486: * Finish extracting receiver from dest_mqueue.
487: */
488:
489: ipc_thread_rmqueue_first_macro(
490: &dest_mqueue->imq_threads, receiver);
491: imq_unlock(dest_mqueue);
492:
493: /*
494: * Release the receiver's reference for his object.
495: */
496: {
497: register ipc_object_t object = receiver->ith_object;
498:
499: io_lock(object);
500: io_release(object);
501: io_check_unlock(object);
502: }
503:
504: {
505: register struct mach_exception *exc =
506: (struct mach_exception *) &kmsg->ikm_header;
507: ipc_space_t space = receiver->task->itk_space;
508:
509: /*
510: * We are running as the receiver now. We hold
511: * the following resources, which must be consumed:
512: * kmsg, send-once right for reply_port
513: * send rights for dest_port, thread_port, task_port
514: * Synthesize a kmsg for copyout to the receiver.
515: */
516:
517: exc->Head.msgh_bits = (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE,
518: MACH_MSG_TYPE_PORT_SEND) |
519: MACH_MSGH_BITS_COMPLEX);
520: exc->Head.msgh_size = sizeof *exc;
521: /* exc->Head.msgh_remote_port later */
522: /* exc->Head.msgh_local_port later */
523: exc->Head.msgh_seqno = 0;
524: exc->Head.msgh_id = MACH_EXCEPTION_ID;
525: exc->threadType = exc_port_proto;
526: /* exc->thread later */
527: exc->taskType = exc_port_proto;
528: /* exc->task later */
529: exc->exceptionType = exc_code_proto;
530: exc->exception = _exception;
531: exc->codeType = exc_code_proto;
532: exc->code = code;
533: exc->subcodeType = exc_code_proto;
534: exc->subcode = subcode;
535:
536: /*
537: * Check that the receiver can handle the message.
538: */
539:
540: if (receiver->ith_rcv_size < sizeof(struct mach_exception)) {
541: /*
542: * ipc_kmsg_destroy is a handy way to consume
543: * the resources we hold, but it requires setup.
544: */
545:
546: exc->Head.msgh_bits =
547: (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
548: MACH_MSG_TYPE_PORT_SEND_ONCE) |
549: MACH_MSGH_BITS_COMPLEX);
550: exc->Head.msgh_remote_port = (mach_port_t) dest_port;
551: exc->Head.msgh_local_port = (mach_port_t) reply_port;
552: exc->thread = (mach_port_t) thread_port;
553: exc->task = (mach_port_t) task_port;
554:
555: ipc_kmsg_destroy(kmsg);
556: thread_syscall_return(MACH_RCV_TOO_LARGE);
557: /*NOTREACHED*/
558: }
559:
560: is_write_lock(space);
561: assert(space->is_active);
562:
563: /*
564: * To do an atomic copyout, need simultaneous
565: * locks on both ports and the space.
566: */
567:
568: ip_lock(dest_port);
569: if (!ip_active(dest_port) ||
570: !ip_lock_try(reply_port)) {
571: abort_copyout:
572: ip_unlock(dest_port);
573: is_write_unlock(space);
574:
575: /*
576: * Oh well, we have to do the header the slow way.
577: * First make it look like it's in-transit.
578: */
579:
580: exc->Head.msgh_bits =
581: (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
582: MACH_MSG_TYPE_PORT_SEND_ONCE) |
583: MACH_MSGH_BITS_COMPLEX);
584: exc->Head.msgh_remote_port = (mach_port_t) dest_port;
585: exc->Head.msgh_local_port = (mach_port_t) reply_port;
586:
587: mr = ipc_kmsg_copyout_header(&exc->Head, space,
588: MACH_PORT_NULL);
589: if (mr == MACH_MSG_SUCCESS)
590: goto copyout_body;
591:
592: /*
593: * Ack! Prepare for ipc_kmsg_copyout_dest.
594: * It will consume thread_port and task_port.
595: */
596:
597: exc->thread = (mach_port_t) thread_port;
598: exc->task = (mach_port_t) task_port;
599:
600: ipc_kmsg_copyout_dest(kmsg, space);
601: (void) ipc_kmsg_put(receiver->ith_msg, kmsg,
602: sizeof(mach_msg_header_t));
603: thread_syscall_return(mr);
604: /*NOTREACHED*/
605: }
606:
607: if (!ip_active(reply_port)) {
608: ip_unlock(reply_port);
609: goto abort_copyout;
610: }
611:
612: assert(reply_port->ip_sorights > 0);
613: ip_unlock(reply_port);
614:
615: {
616: register ipc_entry_t table;
617: register ipc_entry_t entry;
618: register mach_port_index_t index;
619:
620: /* optimized ipc_entry_get */
621:
622: table = space->is_table;
623: index = table->ie_next;
624:
625: if (index == 0)
626: goto abort_copyout;
627:
628: entry = &table[index];
629: table->ie_next = entry->ie_next;
630: entry->ie_request = 0;
631:
632: {
633: register mach_port_gen_t gen;
634:
635: assert((entry->ie_bits &~ IE_BITS_GEN_MASK) == 0);
636: gen = entry->ie_bits + IE_BITS_GEN_ONE;
637:
638: exc->Head.msgh_remote_port = MACH_PORT_MAKE(index, gen);
639:
640: /* optimized ipc_right_copyout */
641:
642: entry->ie_bits = gen | (MACH_PORT_TYPE_SEND_ONCE | 1);
643: }
644:
645: entry->ie_object = (ipc_object_t) reply_port;
646: is_write_unlock(space);
647: }
648:
649: /* optimized ipc_object_copyout_dest */
650:
651: assert(dest_port->ip_srights > 0);
652: ip_release(dest_port);
653:
654: exc->Head.msgh_local_port =
655: ((dest_port->ip_receiver == space) ?
656: dest_port->ip_receiver_name : MACH_PORT_NULL);
657:
658: if ((--dest_port->ip_srights == 0) &&
659: (dest_port->ip_nsrequest != IP_NULL)) {
660: ipc_port_t nsrequest;
661: mach_port_mscount_t mscount;
662:
663: /* a rather rare case */
664:
665: nsrequest = dest_port->ip_nsrequest;
666: mscount = dest_port->ip_mscount;
667: dest_port->ip_nsrequest = IP_NULL;
668: ip_unlock(dest_port);
669:
670: ipc_notify_no_senders(nsrequest, mscount);
671: } else
672: ip_unlock(dest_port);
673:
674: copyout_body:
675: /*
676: * Optimized version of ipc_kmsg_copyout_body,
677: * to handle the two ports in the body.
678: */
679:
680: mr = (ipc_kmsg_copyout_object(space, (ipc_object_t) thread_port,
681: MACH_MSG_TYPE_PORT_SEND, &exc->thread) |
682: ipc_kmsg_copyout_object(space, (ipc_object_t) task_port,
683: MACH_MSG_TYPE_PORT_SEND, &exc->task));
684: if (mr != MACH_MSG_SUCCESS) {
685: (void) ipc_kmsg_put(receiver->ith_msg, kmsg,
686: kmsg->ikm_header.msgh_size);
687: thread_syscall_return(mr | MACH_RCV_BODY_ERROR);
688: /*NOTREACHED*/
689: }
690: }
691:
692: /*
693: * Optimized version of ipc_kmsg_put.
694: * We must check ikm_cache after copyoutmsg.
695: */
696:
697: ikm_check_initialized(kmsg, kmsg->ikm_size);
698: assert(kmsg->ikm_size == IKM_SAVED_KMSG_SIZE);
699:
700: if (copyoutmsg((vm_offset_t) &kmsg->ikm_header, (vm_offset_t)receiver->ith_msg,
701: sizeof(struct mach_exception)) ||
702: (ikm_cache() != IKM_NULL)) {
703: mr = ipc_kmsg_put(receiver->ith_msg, kmsg,
704: kmsg->ikm_header.msgh_size);
705: thread_syscall_return(mr);
706: /*NOTREACHED*/
707: }
708:
709: ikm_cache() = kmsg;
710: thread_syscall_return(MACH_MSG_SUCCESS);
711: /*NOTREACHED*/
712: #ifndef __GNUC__
713: return; /* help for the compiler */
714: #endif
715:
716: slow_exception_raise: {
717: register struct mach_exception *exc =
718: (struct mach_exception *) &kmsg->ikm_header;
719: ipc_kmsg_t reply_kmsg;
720: mach_port_seqno_t reply_seqno;
721:
722: exception_raise_misses++;
723:
724: /*
725: * We hold the following resources, which must be consumed:
726: * kmsg, send-once right and ref for reply_port
727: * send rights for dest_port, thread_port, task_port
728: * Synthesize a kmsg to send.
729: */
730:
731: exc->Head.msgh_bits = (MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND,
732: MACH_MSG_TYPE_PORT_SEND_ONCE) |
733: MACH_MSGH_BITS_COMPLEX);
734: exc->Head.msgh_size = sizeof *exc;
735: exc->Head.msgh_remote_port = (mach_port_t) dest_port;
736: exc->Head.msgh_local_port = (mach_port_t) reply_port;
737: exc->Head.msgh_seqno = 0;
738: exc->Head.msgh_id = MACH_EXCEPTION_ID;
739: exc->threadType = exc_port_proto;
740: exc->thread = (mach_port_t) thread_port;
741: exc->taskType = exc_port_proto;
742: exc->task = (mach_port_t) task_port;
743: exc->exceptionType = exc_code_proto;
744: exc->exception = _exception;
745: exc->codeType = exc_code_proto;
746: exc->code = code;
747: exc->subcodeType = exc_code_proto;
748: exc->subcode = subcode;
749:
750: ipc_mqueue_send_always(kmsg);
751:
752: /*
753: * We are left with a ref for reply_port,
754: * which we use to receive the reply message.
755: */
756:
757: ip_lock(reply_port);
758: if (!ip_active(reply_port)) {
759: ip_unlock(reply_port);
760: exception_raise_continue_slow(MACH_RCV_PORT_DIED, IKM_NULL, /*dummy*/0);
761: /*NOTREACHED*/
762: return;
763: }
764:
765: imq_lock(reply_mqueue);
766: ip_unlock(reply_port);
767:
768: mr = ipc_mqueue_receive(reply_mqueue, MACH_MSG_OPTION_NONE,
769: MACH_MSG_SIZE_MAX,
770: MACH_MSG_TIMEOUT_NONE,
771: FALSE, exception_raise_continue,
772: &reply_kmsg, &reply_seqno);
773: /* reply_mqueue is unlocked */
774:
775: exception_raise_continue_slow(mr, reply_kmsg, reply_seqno);
776: /*NOTREACHED*/
777: }
778: }
779:
780: mach_msg_type_t exc_RetCode_proto = {
781: /* msgt_name = */ MACH_MSG_TYPE_INTEGER_32,
782: /* msgt_size = */ 32,
783: /* msgt_number = */ 1,
784: /* msgt_inline = */ TRUE,
785: /* msgt_longform = */ FALSE,
786: /* msgt_deallocate = */ FALSE,
787: /* msgt_unused = */ 0
788: };
789:
790: /*
791: * Routine: exception_parse_reply
792: * Purpose:
793: * Parse and consume an exception reply message.
794: * Conditions:
795: * The destination port right has already been consumed.
796: * The message buffer and anything else in it is consumed.
797: * Returns:
798: * The reply return code.
799: */
800:
801: kern_return_t
802: exception_parse_reply(kmsg)
803: ipc_kmsg_t kmsg;
804: {
805: register mig_reply_header_t *msg =
806: (mig_reply_header_t *) &kmsg->ikm_header;
807: kern_return_t kr;
808:
809: if ((msg->Head.msgh_bits !=
810: MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND_ONCE, 0)) ||
811: (msg->Head.msgh_size != sizeof *msg) ||
812: (msg->Head.msgh_id != MACH_EXCEPTION_REPLY_ID) ||
813: (* (int *) &msg->RetCodeType != * (int *) &exc_RetCode_proto)) {
814: /*
815: * Bozo user sent us a misformatted reply.
816: */
817:
818: kmsg->ikm_header.msgh_remote_port = MACH_PORT_NULL;
819: ipc_kmsg_destroy(kmsg);
820: return MIG_REPLY_MISMATCH;
821: }
822:
823: kr = msg->RetCode;
824:
825: if ((kmsg->ikm_size == IKM_SAVED_KMSG_SIZE) &&
826: (ikm_cache() == IKM_NULL))
827: ikm_cache() = kmsg;
828: else
829: ikm_free(kmsg);
830:
831: return kr;
832: }
833:
834: /*
835: * Routine: exception_raise_continue
836: * Purpose:
837: * Continue after blocking for an exception.
838: * Conditions:
839: * Nothing locked. We are running on a new kernel stack,
840: * with the exception state saved in the thread. From here
841: * control goes back to user space.
842: * Returns:
843: * Doesn't return.
844: */
845:
846: void
847: exception_raise_continue()
848: {
849: ipc_thread_t self = current_thread();
850: ipc_port_t reply_port = self->ith_port;
851: ipc_mqueue_t reply_mqueue = &reply_port->ip_messages;
852: ipc_kmsg_t kmsg;
853: mach_port_seqno_t seqno;
854: mach_msg_return_t mr;
855:
856: mr = ipc_mqueue_receive(reply_mqueue, MACH_MSG_OPTION_NONE,
857: MACH_MSG_SIZE_MAX,
858: MACH_MSG_TIMEOUT_NONE,
859: TRUE, exception_raise_continue,
860: &kmsg, &seqno);
861: /* reply_mqueue is unlocked */
862:
863: exception_raise_continue_slow(mr, kmsg, seqno);
864: /*NOTREACHED*/
865: }
866:
867: /*
868: * Routine: exception_raise_continue_slow
869: * Purpose:
870: * Continue after finishing an ipc_mqueue_receive
871: * for an exception reply message.
872: * Conditions:
873: * Nothing locked. We hold a ref for reply_port.
874: * Returns:
875: * Doesn't return.
876: */
877:
878: void
879: exception_raise_continue_slow(mr, kmsg, seqno)
880: mach_msg_return_t mr;
881: ipc_kmsg_t kmsg;
882: mach_port_seqno_t seqno;
883: {
884: ipc_thread_t self = current_thread();
885: ipc_port_t reply_port = self->ith_port;
886: ipc_mqueue_t reply_mqueue = &reply_port->ip_messages;
887:
888: while (mr == MACH_RCV_INTERRUPTED) {
889: /*
890: * Somebody is trying to force this thread
891: * to a clean point. We must cooperate
892: * and then resume the receive.
893: */
894:
895: while (thread_should_halt(self)) {
896: /* don't terminate while holding a reference */
897: if (self->ast & AST_TERMINATE)
898: ipc_port_release(reply_port);
899: thread_halt_self();
900: }
901:
902: ip_lock(reply_port);
903: if (!ip_active(reply_port)) {
904: ip_unlock(reply_port);
905: mr = MACH_RCV_PORT_DIED;
906: break;
907: }
908:
909: imq_lock(reply_mqueue);
910: ip_unlock(reply_port);
911:
912: mr = ipc_mqueue_receive(reply_mqueue, MACH_MSG_OPTION_NONE,
913: MACH_MSG_SIZE_MAX,
914: MACH_MSG_TIMEOUT_NONE,
915: FALSE, exception_raise_continue,
916: &kmsg, &seqno);
917: /* reply_mqueue is unlocked */
918: }
919: ipc_port_release(reply_port);
920:
921: assert((mr == MACH_MSG_SUCCESS) ||
922: (mr == MACH_RCV_PORT_DIED));
923:
924: if (mr == MACH_MSG_SUCCESS) {
925: /*
926: * Consume the reply message.
927: */
928:
929: ipc_port_release_sonce(reply_port);
930: mr = exception_parse_reply(kmsg);
931: }
932:
933: if ((mr == KERN_SUCCESS) ||
934: (mr == MACH_RCV_PORT_DIED)) {
935: thread_exception_return();
936: /*NOTREACHED*/
937: return;
938: }
939:
940: if (self->ith_exc != KERN_SUCCESS) {
941: exception_try_task(self->ith_exc,
942: self->ith_exc_code,
943: self->ith_exc_subcode);
944: /*NOTREACHED*/
945: return;
946: }
947:
948: exception_no_server();
949: /*NOTREACHED*/
950: }
951:
952: /*
953: * Routine: exception_raise_continue_fast
954: * Purpose:
955: * Special-purpose fast continuation for exceptions.
956: * Conditions:
957: * reply_port is locked and alive.
958: * kmsg is our reply message.
959: * Returns:
960: * Doesn't return.
961: */
962:
963: void
964: exception_raise_continue_fast(reply_port, kmsg)
965: ipc_port_t reply_port;
966: ipc_kmsg_t kmsg;
967: {
968: ipc_thread_t self = current_thread();
969: kern_return_t kr;
970:
971: assert(ip_active(reply_port));
972: assert(reply_port == self->ith_port);
973: assert(reply_port == (ipc_port_t) kmsg->ikm_header.msgh_remote_port);
974: assert(MACH_MSGH_BITS_REMOTE(kmsg->ikm_header.msgh_bits) ==
975: MACH_MSG_TYPE_PORT_SEND_ONCE);
976:
977: /*
978: * Release the send-once right (from the message header)
979: * and the saved reference (from self->ith_port).
980: */
981:
982: reply_port->ip_sorights--;
983: ip_release(reply_port);
984: ip_release(reply_port);
985: ip_unlock(reply_port);
986:
987: /*
988: * Consume the reply message.
989: */
990:
991: kr = exception_parse_reply(kmsg);
992: if (kr == KERN_SUCCESS) {
993: thread_exception_return();
994: /*NOTREACHED*/
995: return; /* help for the compiler */
996: }
997:
998: if (self->ith_exc != KERN_SUCCESS) {
999: exception_try_task(self->ith_exc,
1000: self->ith_exc_code,
1001: self->ith_exc_subcode);
1002: /*NOTREACHED*/
1003: }
1004:
1005: exception_no_server();
1006: /*NOTREACHED*/
1007: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.