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