| /* Handle faults in the signal thread. |
| Copyright (C) 1994,1995,1996,1997,2002,2005 |
| Free Software Foundation, Inc. |
| This file is part of the GNU C Library. |
| |
| The GNU C Library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| The GNU C Library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with the GNU C Library; if not, write to the Free |
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307 USA. */ |
| |
| #include <hurd.h> |
| #include <hurd/signal.h> |
| #include "hurdfault.h" |
| #include <errno.h> |
| #include <string.h> |
| #include <setjmp.h> |
| #include <stdio.h> |
| #include <thread_state.h> |
| #include "faultexc_server.h" /* mig-generated header for our exc server. */ |
| #include <assert.h> |
| |
| jmp_buf _hurdsig_fault_env; |
| struct hurd_signal_preemptor _hurdsig_fault_preemptor = {0}; |
| |
| /* XXX temporary to deal with spelling fix */ |
| weak_alias (_hurdsig_fault_preemptor, _hurdsig_fault_preempter) |
| |
| static mach_port_t forward_sigexc; |
| |
| kern_return_t |
| _hurdsig_fault_catch_exception_raise (mach_port_t port, |
| thread_t thread, |
| task_t task, |
| #ifdef EXC_MASK_ALL /* New interface flavor. */ |
| exception_type_t exception, |
| exception_data_t code, |
| mach_msg_type_number_t codeCnt |
| #else /* Vanilla Mach 3.0 interface. */ |
| integer_t exception, |
| integer_t code, integer_t subcode |
| #endif |
| ) |
| { |
| int signo; |
| struct hurd_signal_detail d; |
| |
| if (port != forward_sigexc || |
| thread != _hurd_msgport_thread || task != __mach_task_self ()) |
| return EPERM; /* Strange bogosity. */ |
| |
| d.exc = exception; |
| #ifdef EXC_MASK_ALL |
| assert (codeCnt >= 2); |
| d.exc_code = code[0]; |
| d.exc_subcode = code[1]; |
| #else |
| d.exc_code = code; |
| d.exc_subcode = subcode; |
| #endif |
| |
| /* Call the machine-dependent function to translate the Mach exception |
| codes into a signal number and subcode. */ |
| _hurd_exception2signal (&d, &signo); |
| |
| return HURD_PREEMPT_SIGNAL_P (&_hurdsig_fault_preemptor, signo, d.code) |
| ? 0 : EGREGIOUS; |
| } |
| |
| #ifdef EXC_MASK_ALL |
| /* XXX New interface flavor has additional RPCs that we could be using |
| instead. These RPCs roll a thread_get_state/thread_set_state into |
| the message, so the signal thread ought to use these to save some calls. |
| */ |
| kern_return_t |
| _hurdsig_fault_catch_exception_raise_state |
| (mach_port_t port, |
| exception_type_t exception, |
| exception_data_t code, |
| mach_msg_type_number_t codeCnt, |
| int *flavor, |
| thread_state_t old_state, |
| mach_msg_type_number_t old_stateCnt, |
| thread_state_t new_state, |
| mach_msg_type_number_t *new_stateCnt) |
| { |
| abort (); |
| return KERN_FAILURE; |
| } |
| |
| kern_return_t |
| _hurdsig_fault_catch_exception_raise_state_identity |
| (mach_port_t exception_port, |
| thread_t thread, |
| task_t task, |
| exception_type_t exception, |
| exception_data_t code, |
| mach_msg_type_number_t codeCnt, |
| int *flavor, |
| thread_state_t old_state, |
| mach_msg_type_number_t old_stateCnt, |
| thread_state_t new_state, |
| mach_msg_type_number_t *new_stateCnt) |
| { |
| abort (); |
| return KERN_FAILURE; |
| } |
| #endif |
| |
| |
| #ifdef NDR_CHAR_ASCII /* OSF Mach flavors have different names. */ |
| # define mig_reply_header_t mig_reply_error_t |
| #endif |
| |
| static void |
| faulted (void) |
| { |
| struct |
| { |
| mach_msg_header_t head; |
| char buf[64]; |
| } request; |
| mig_reply_header_t reply; |
| extern int _hurdsig_fault_exc_server (mach_msg_header_t *, |
| mach_msg_header_t *); |
| |
| /* Wait for the exception_raise message forwarded by the proc server. */ |
| |
| if (__mach_msg (&request.head, MACH_RCV_MSG, 0, |
| sizeof request, forward_sigexc, |
| MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL) |
| != MACH_MSG_SUCCESS) |
| __libc_fatal ("msg receive failed on signal thread exc\n"); |
| |
| /* Run the exc demuxer which should call the server function above. |
| That function returns 0 if the exception was expected. */ |
| _hurdsig_fault_exc_server (&request.head, &reply.Head); |
| if (reply.Head.msgh_remote_port != MACH_PORT_NULL) |
| __mach_msg (&reply.Head, MACH_SEND_MSG, reply.Head.msgh_size, |
| 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
| if (reply.RetCode == MIG_BAD_ID) |
| __mach_msg_destroy (&request.head); |
| |
| if (reply.RetCode) |
| __libc_fatal ("BUG: unexpected fault in signal thread\n"); |
| |
| _hurdsig_fault_preemptor.signals = 0; |
| longjmp (_hurdsig_fault_env, 1); |
| } |
| |
| static char faultstack[1024]; |
| |
| /* Send exceptions for the signal thread to the proc server. |
| It will forward the message on to our message port, |
| and then restore the thread's state to code which |
| does `longjmp (_hurd_sigthread_fault_env, 1)'. */ |
| |
| void |
| _hurdsig_fault_init (void) |
| { |
| error_t err; |
| struct machine_thread_state state; |
| mach_port_t sigexc; |
| |
| /* Allocate a port to receive signal thread exceptions. |
| We will move this receive right to the proc server. */ |
| err = __mach_port_allocate (__mach_task_self (), |
| MACH_PORT_RIGHT_RECEIVE, &sigexc); |
| assert_perror (err); |
| err = __mach_port_allocate (__mach_task_self (), |
| MACH_PORT_RIGHT_RECEIVE, &forward_sigexc); |
| assert_perror (err); |
| |
| /* Allocate a port to receive the exception msgs forwarded |
| from the proc server. */ |
| err = __mach_port_insert_right (__mach_task_self (), sigexc, |
| sigexc, MACH_MSG_TYPE_MAKE_SEND); |
| assert_perror (err); |
| |
| /* Set the queue limit for this port to just one. The proc server will |
| notice if we ever get a second exception while one remains queued and |
| unreceived, and decide we are hopelessly buggy. */ |
| #ifdef MACH_PORT_RECEIVE_STATUS_COUNT |
| { |
| const mach_port_limits_t lim = { mpl_qlimit: 1 }; |
| assert (MACH_PORT_RECEIVE_STATUS_COUNT == sizeof lim / sizeof (natural_t)); |
| err = __mach_port_set_attributes (__mach_task_self (), forward_sigexc, |
| MACH_PORT_RECEIVE_STATUS, |
| (mach_port_info_t) &lim, |
| MACH_PORT_RECEIVE_STATUS_COUNT); |
| } |
| #else |
| err = __mach_port_set_qlimit (__mach_task_self (), forward_sigexc, 1); |
| #endif |
| assert_perror (err); |
| |
| /* This state will be restored when we fault. |
| It runs the function above. */ |
| memset (&state, 0, sizeof state); |
| MACHINE_THREAD_STATE_SET_PC (&state, faulted); |
| MACHINE_THREAD_STATE_SET_SP (&state, faultstack, sizeof faultstack); |
| |
| err = __USEPORT |
| (PROC, |
| __proc_handle_exceptions (port, |
| sigexc, |
| forward_sigexc, MACH_MSG_TYPE_MAKE_SEND, |
| MACHINE_THREAD_STATE_FLAVOR, |
| (natural_t *) &state, |
| MACHINE_THREAD_STATE_COUNT)); |
| assert_perror (err); |
| |
| /* Direct signal thread exceptions to the proc server. */ |
| #ifdef THREAD_EXCEPTION_PORT |
| err = __thread_set_special_port (_hurd_msgport_thread, |
| THREAD_EXCEPTION_PORT, sigexc); |
| #elif defined (EXC_MASK_ALL) |
| __thread_set_exception_ports (_hurd_msgport_thread, |
| EXC_MASK_ALL & ~(EXC_MASK_SYSCALL |
| | EXC_MASK_MACH_SYSCALL |
| | EXC_MASK_RPC_ALERT), |
| sigexc, |
| EXCEPTION_STATE_IDENTITY, |
| MACHINE_THREAD_STATE); |
| #else |
| # error thread_set_exception_ports? |
| #endif |
| __mach_port_deallocate (__mach_task_self (), sigexc); |
| assert_perror (err); |
| } |