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