75f11457cdb0d3a466a2a3741f3b9cbfa2c1f4ee
[kopensolaris-gnu/glibc.git] / sysdeps / mach / hurd / i386 / trampoline.c
1 /* Set thread_state for sighandler, and sigcontext to recover.  i386 version.
2 Copyright (C) 1994, 1995 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB.  If
17 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
18 Cambridge, MA 02139, USA.  */
19
20 #include <hurd/signal.h>
21 #include "thread_state.h"
22 #include <assert.h>
23 #include <errno.h>
24 #include "hurdfault.h"
25
26      
27 struct mach_msg_trap_args
28   {
29     void *retaddr;              /* Address mach_msg_trap will return to.  */
30     /* This is the order of arguments to mach_msg_trap.  */
31     mach_msg_header_t *msg;
32     mach_msg_option_t option;
33     mach_msg_size_t send_size;
34     mach_msg_size_t rcv_size;
35     mach_port_t rcv_name;
36     mach_msg_timeout_t timeout;
37     mach_port_t notify;
38   };
39
40 struct sigcontext *
41 _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler,
42                         int signo, long int sigcode,
43                         volatile int rpc_wait,
44                         struct machine_thread_all_state *state)
45 {
46   __label__ trampoline, rpc_wait_trampoline;
47   void *volatile sigsp;
48   struct sigcontext *scp;
49   struct 
50     {
51       int signo;
52       long int sigcode;
53       struct sigcontext *scp;   /* Points to ctx, below.  */
54       void *sigreturn_addr;
55       void *sigreturn_returns_here;
56       struct sigcontext *return_scp; /* Same; arg to sigreturn.  */
57       struct sigcontext ctx;
58     } *stackframe;
59
60   if (ss->context)
61     {
62       /* We have a previous sigcontext that sigreturn was about
63          to restore when another signal arrived.  We will just base
64          our setup on that.  */
65       if (_hurdsig_catch_fault (SIGSEGV))
66         assert (_hurdsig_fault_sigcode >= (long int) ss->context &&
67                 _hurdsig_fault_sigcode < (long int) (ss->context + 1));
68       else
69         {
70           memcpy (&state->basic, &ss->context->sc_i386_thread_state,
71                   sizeof (state->basic));
72           memcpy (&state->fpu, &ss->context->sc_i386_float_state,
73                   sizeof (state->fpu));
74           state->set = (1 << i386_THREAD_STATE) | (1 << i386_FLOAT_STATE);
75           assert (! rpc_wait);
76           /* The intr_port slot was cleared before sigreturn sent us the
77              sig_post that made us notice this pending signal, so
78              _hurd_internal_post_signal wouldn't do interrupt_operation.
79              After we return, our caller will set SCP->sc_intr_port (in the
80              new context) from SS->intr_port and clear SS->intr_port.  Now
81              that we are restoring this old context recorded by sigreturn,
82              we want to restore its intr_port too; so store it in
83              SS->intr_port now, so it will end up in SCP->sc_intr_port
84              later.  */
85           ss->intr_port = ss->context->sc_intr_port;
86         }
87       /* If the sigreturn context was bogus, just ignore it.  */
88       ss->context = NULL;
89     }
90   else if (! machine_get_basic_state (ss->thread, state))
91     return NULL;
92
93   if ((ss->actions[signo].sa_flags & SA_ONSTACK) &&
94       !(ss->sigaltstack.ss_flags & (SA_DISABLE|SA_ONSTACK)))
95     {
96       sigsp = ss->sigaltstack.ss_sp + ss->sigaltstack.ss_size;
97       ss->sigaltstack.ss_flags |= SA_ONSTACK;
98       /* XXX need to set up base of new stack for
99          per-thread variables, cthreads.  */
100     }
101   else
102     sigsp = (char *) state->basic.uesp;
103
104   /* Push the arguments to call `trampoline' on the stack.  */
105   sigsp -= sizeof (*stackframe);
106   stackframe = sigsp;
107
108   if (_hurdsig_catch_fault (SIGSEGV))
109     {
110       assert (_hurdsig_fault_sigcode >= (long int) stackframe &&
111               _hurdsig_fault_sigcode <= (long int) (stackframe + 1));
112       /* We got a fault trying to write the stack frame.
113          We cannot set up the signal handler.
114          Returning NULL tells our caller, who will nuke us with a SIGILL.  */
115       return NULL;
116     }
117   else
118     {
119       int ok;
120
121       /* Set up the arguments for the signal handler.  */
122       stackframe->signo = signo;
123       stackframe->sigcode = sigcode;
124       stackframe->scp = stackframe->return_scp = scp = &stackframe->ctx;
125       stackframe->sigreturn_addr = &__sigreturn;
126
127       /* Set up the sigcontext from the current state of the thread.  */
128
129       scp->sc_onstack = ss->sigaltstack.ss_flags & SA_ONSTACK ? 1 : 0;
130
131       /* struct sigcontext is laid out so that starting at sc_gs mimics a
132          struct i386_thread_state.  */
133       memcpy (&scp->sc_i386_thread_state,
134               &state->basic, sizeof (state->basic));
135
136       /* struct sigcontext is laid out so that starting at sc_fpkind mimics
137          a struct i386_float_state.  */
138       ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE,
139                               &state->fpu, &scp->sc_i386_float_state,
140                               sizeof (state->fpu));
141
142       _hurdsig_end_catch_fault ();
143
144       if (! ok)
145         return NULL;
146     }
147
148   /* Modify the thread state to call the trampoline code on the new stack.  */
149   if (rpc_wait)
150     {
151       /* The signalee thread was blocked in a mach_msg_trap system call,
152          still waiting for a reply.  We will have it run the special
153          trampoline code which retries the message receive before running
154          the signal handler.
155          
156          To do this we change the OPTION argument on its stack to enable only
157          message reception, since the request message has already been
158          sent.  */
159
160       struct mach_msg_trap_args *args = (void *) state->basic.uesp;
161
162       if (_hurdsig_catch_fault (SIGSEGV))
163         {
164           assert (_hurdsig_fault_sigcode >= (long int) args &&
165                   _hurdsig_fault_sigcode < (long int) (args + 1));
166           /* Faulted accessing ARGS.  Bomb.  */
167           return NULL;
168         }
169
170       assert (args->option & MACH_RCV_MSG);
171       /* Disable the message-send, since it has already completed.  The
172          calls we retry need only wait to receive the reply message.  */
173       args->option &= ~MACH_SEND_MSG;
174
175       _hurdsig_end_catch_fault ();
176
177       state->basic.eip = (int) &&rpc_wait_trampoline;
178       /* The reply-receiving trampoline code runs initially on the original
179          user stack.  We pass it the signal stack pointer in %ebx.  */
180       state->basic.ebx = (int) sigsp;
181       /* After doing the message receive, the trampoline code will need to
182          update the %eax value to be restored by sigreturn.  To simplify
183          the assembly code, we pass the address of its slot in SCP to the
184          trampoline code in %ecx.  */
185       state->basic.ecx = (int) &scp->sc_eax;
186     }
187   else
188     {
189       state->basic.eip = (int) &&trampoline;
190       state->basic.uesp = (int) sigsp;
191     }
192   /* We pass the handler function to the trampoline code in %edx.  */
193   state->basic.edx = (int) handler;
194
195   return scp;
196
197   /* The trampoline code follows.  This is not actually executed as part of
198      this function, it is just convenient to write it that way.  */
199
200  rpc_wait_trampoline:
201   /* This is the entry point when we have an RPC reply message to receive
202      before running the handler.  The MACH_MSG_SEND bit has already been
203      cleared in the OPTION argument on our stack.  The interrupted user
204      stack pointer has not been changed, so the system call can find its
205      arguments; the signal stack pointer is in %ebx.  For our convenience,
206      %ecx points to the sc_eax member of the sigcontext.  */
207   asm volatile
208     (/* Retry the interrupted mach_msg system call.  */
209      "movl $-25, %eax\n"        /* mach_msg_trap */
210      "lcall $7, $0\n"
211      /* When the sigcontext was saved, %eax was MACH_RCV_INTERRUPTED.  But
212         now the message receive has completed and the original caller of
213         the RPC (i.e. the code running when the signal arrived) needs to
214         see the final return value of the message receive in %eax.  So
215         store the new %eax value into the sc_eax member of the sigcontext
216         (whose address is in %ecx to make this code simpler).  */
217      "movl %eax, (%ecx)\n"
218      /* Switch to the signal stack.  */
219      "movl %ebx, %esp\n");
220
221  trampoline:
222   /* Entry point for running the handler normally.  The arguments to the
223      handler function are already on the top of the stack:
224
225        0(%esp)  SIGNO
226        4(%esp)  SIGCODE
227        8(%esp)  SCP
228      */
229   asm volatile
230     ("call *%edx\n"             /* Call the handler function.  */
231      "addl $12, %esp\n"         /* Pop its args.  */
232      /* The word at the top of stack is &__sigreturn; following are a dummy
233         word to fill the slot for the address for __sigreturn to return to,
234         and a copy of SCP for __sigreturn's argument.  "Return" to calling
235         __sigreturn (SCP); this call never returns.  */
236      "ret");
237
238   /* NOTREACHED */
239   return NULL;
240 }
241 \f
242 /* STATE describes a thread that had intr_port set (meaning it was inside
243    HURD_EINTR_RPC), after it has been thread_abort'd.  It it looks to have
244    just completed a mach_msg_trap system call that returned
245    MACH_RCV_INTERRUPTED, return nonzero and set *PORT to the receive right
246    being waited on.  */
247 int
248 _hurdsig_rcv_interrupted_p (struct machine_thread_all_state *state,
249                             mach_port_t *port)
250 {
251   static const unsigned char syscall[] = { 0x9a, 0, 0, 0, 0, 7, 0 };
252   const unsigned char *volatile pc
253     = (void *) state->basic.eip - sizeof syscall;
254
255   if (_hurdsig_catch_fault (SIGSEGV))
256     assert (_hurdsig_fault_sigcode >= (long int) pc &&
257             _hurdsig_fault_sigcode < (long int) pc + sizeof syscall);
258   else
259     {
260       int rcving = (state->basic.eax == MACH_RCV_INTERRUPTED &&
261                     !memcmp (pc, &syscall, sizeof syscall));
262       _hurdsig_end_catch_fault ();
263       if (rcving)
264         {
265           /* We did just return from a mach_msg_trap system call
266              doing a message receive that was interrupted.
267              Examine the parameters to find the receive right.  */
268           struct mach_msg_trap_args *args = (void *) state->basic.uesp;
269
270           *port = args->rcv_name;
271           return 1;
272         }
273     }
274
275   return 0;
276 }