Mon Aug 14 16:51:13 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
[glibc.git] / sysdeps / mach / hurd / mips / trampoline.c
blob03e3d1da6fc9b50251247daaa9e1268eb778d73d
1 /* Set thread_state for sighandler, and sigcontext to recover. MIPS version.
2 Copyright (C) 1994, 1995 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
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. */
20 #include <hurd/signal.h>
21 #include "thread_state.h"
24 struct mach_msg_trap_args
26 /* This is the order of arguments to mach_msg_trap. */
27 mach_msg_header_t *msg;
28 mach_msg_option_t option;
29 mach_msg_size_t send_size;
30 mach_msg_size_t rcv_size;
31 mach_port_t rcv_name;
32 mach_msg_timeout_t timeout;
33 mach_port_t notify;
37 struct sigcontext *
38 _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler,
39 int signo, long int sigcode,
40 int rpc_wait,
41 struct machine_thread_all_state *state)
44 __label__ trampoline, rpc_wait_trampoline;
45 void *sigsp;
46 struct sigcontext *scp;
48 if (ss->context)
50 /* We have a previous sigcontext that sigreturn was about
51 to restore when another signal arrived. We will just base
52 our setup on that. */
53 if (! setjmp (_hurd_sigthread_fault_env))
55 memcpy (&state->basic, &ss->context->sc_mips_thread_state,
56 sizeof (state->basic));
57 memcpy (&state->exc, &ss->context->sc_mips_exc_state,
58 sizeof (state->exc));
59 state->set = (1 << MIPS_THREAD_STATE) | (1 << MIPS_EXC_STATE);
60 if (state->exc.coproc_state & SC_COPROC_USE_FPU)
62 memcpy (&state->fpu, &ss->context->sc_mips_float_state,
63 sizeof (state->fpu));
64 state->set |= (1 << MIPS_FLOAT_STATE);
66 assert (! rpc_wait);
67 /* The intr_port slot was cleared before sigreturn sent us the
68 sig_post that made us notice this pending signal, so
69 _hurd_internal_post_signal wouldn't do interrupt_operation.
70 After we return, our caller will set SCP->sc_intr_port (in the
71 new context) from SS->intr_port and clear SS->intr_port. Now
72 that we are restoring this old context recorded by sigreturn,
73 we want to restore its intr_port too; so store it in
74 SS->intr_port now, so it will end up in SCP->sc_intr_port
75 later. */
76 ss->intr_port = ss->context->sc_intr_port;
78 /* If the sigreturn context was bogus, just ignore it. */
79 ss->context = NULL;
81 else if (! machine_get_basic_state (ss->thread, state))
82 return NULL;
84 if ((ss->actions[signo].sa_flags & SA_ONSTACK) &&
85 !(ss->sigaltstack.ss_flags & (SA_DISABLE|SA_ONSTACK)))
87 sigsp = ss->sigaltstack.ss_sp + ss->sigaltstack.ss_size;
88 ss->sigaltstack.ss_flags |= SA_ONSTACK;
89 /* XXX need to set up base of new stack for
90 per-thread variables, cthreads. */
92 else
93 sigsp = (char *) state->basic.r29;
95 /* Set up the sigcontext structure on the stack. This is all the stack
96 needs, since the args are passed in registers (below). */
97 sigsp -= sizeof (*scp);
98 scp = sigsp;
100 if (! setjmp (_hurd_sigthread_fault_env))
102 /* Set up the sigcontext from the current state of the thread. */
104 scp->sc_onstack = ss->sigaltstack.ss_flags & SA_ONSTACK ? 1 : 0;
106 /* struct sigcontext is laid out so that starting at sc_gpr
107 mimics a struct mips_thread_state. */
108 memcpy (&scp->sc_mips_thread_state,
109 &state->basic, sizeof (state->basic));
111 /* struct sigcontext is laid out so that starting at sc_cause
112 mimics a struct mips_exc_state. */
113 if (! machine_get_state (ss->thread, state, MIPS_EXC_STATE,
114 &state->exc, &scp->sc_cause,
115 sizeof (state->exc)))
116 return NULL;
117 if ((scp->sc_coproc_used & SC_COPROC_USE_FPU) &&
118 /* struct sigcontext is laid out so that starting at sc_fpr
119 mimics a struct mips_float_state. This state
120 is only meaningful if the coprocessor was used. */
121 ! machine_get_state (ss->thread, state, MIPS_FLOAT_STATE,
122 &state->fpu,
123 &scp->sc_mips_float_state, sizeof (state->fpu)))
124 return NULL;
126 else
127 /* We got a fault trying to write the stack frame.
128 We cannot set up the signal handler.
129 Returning NULL tells our caller, who will nuke us with a SIGILL. */
130 return NULL;
132 /* Modify the thread state to call the trampoline code on the new stack. */
133 if (rpc_wait)
135 /* The signalee thread was blocked in a mach_msg_trap system call,
136 still waiting for a reply. We will have it run the special
137 trampoline code which retries the message receive before running
138 the signal handler.
140 To do this we change the OPTION argument in its registers to
141 enable only message reception, since the request message has
142 already been sent. */
144 /* The system call arguments are stored in consecutive registers
145 starting with a0 ($4). */
146 struct mach_msg_trap_args *args = (void *) &state->basic.r4;
148 assert (args->option & MACH_RCV_MSG);
149 /* Disable the message-send, since it has already completed. The
150 calls we retry need only wait to receive the reply message. */
151 args->option &= ~MACH_SEND_MSG;
153 /* Limit the time to receive the reply message, in case the server
154 claimed that `interrupt_operation' succeeded but in fact the RPC
155 is hung. */
156 args->option |= MACH_RCV_TIMEOUT;
157 args->timeout = _hurd_interrupted_rpc_timeout;
159 state->basic.pc = (int) &&rpc_wait_trampoline;
160 state->basic.r29 = (int) sigsp; /* $29 is the stack pointer register. */
161 /* After doing the message receive, the trampoline code will need to
162 update the v0 ($2) value to be restored by sigreturn. To simplify
163 the assembly code, we pass the address of its slot in SCP to the
164 trampoline code in v1 ($3). */
165 state->basic.r3 = (int) &scp->sc_gpr[1];
166 /* We must preserve the mach_msg_trap args in a0..t2 ($4..$10).
167 Pass the handler args to the trampoline code in s1..s3 ($17..$19). */
168 state->basic.r17 = signo;
169 state->basic.r18 = sigcode;
170 state->basic.r19 = (int) scp;
172 else
174 state->basic.pc = (int) &&trampoline;
175 state->basic.r29 = (int) sigsp;
176 state->basic.r4 = signo;
177 state->basic.r5 = sigcode;
178 state->basic.r6 = (int) scp;
181 /* We pass the handler function to the trampoline code in at ($1). */
182 state->basic.r1 = (int) handler;
183 /* In the callee-saved register s0 ($16), we save the SCP value to pass
184 to __sigreturn after the handler returns. */
185 state->basic.r16 = (int) scp;
187 return scp;
189 /* The trampoline code follows. This is not actually executed as part of
190 this function, it is just convenient to write it that way. */
192 rpc_wait_trampoline:
193 /* This is the entry point when we have an RPC reply message to receive
194 before running the handler. The MACH_MSG_SEND bit has already been
195 cleared in the OPTION argument in our registers. For our convenience,
196 $3 points to the sc_gpr[1] member of the sigcontext (saved v0 ($2)). */
197 asm volatile
198 (".set noat; .set noreorder; .set nomacro\n"
199 /* Retry the interrupted mach_msg system call. */
200 "li $2, -25\n" /* mach_msg_trap */
201 "syscall\n"
202 /* When the sigcontext was saved, v0 was MACH_RCV_INTERRUPTED. But
203 now the message receive has completed and the original caller of
204 the RPC (i.e. the code running when the signal arrived) needs to
205 see the final return value of the message receive in v0. So
206 store the new v0 value into the sc_gpr[1] member of the sigcontext
207 (whose address is in v1 to make this code simpler). */
208 "sw $2, ($3)\n"
209 /* Since the argument registers needed to have the mach_msg_trap
210 arguments, we've stored the arguments to the handler function
211 in registers s1..s3 ($17..$19). */
212 "move $4, $17\n"
213 "move $5, $18\n"
214 "move $6, $19\n");
216 trampoline:
217 /* Entry point for running the handler normally. The arguments to the
218 handler function are already in the standard registers:
220 a0 SIGNO
221 a1 SIGCODE
222 a2 SCP
224 asm volatile
225 ("jal $1; nop\n" /* Call the handler function. */
226 /* Call __sigreturn (SCP); this cannot return. */
227 "j %0\n"
228 "move $4, $16" /* Set up arg from saved SCP in delay slot. */
229 : : "i" (&__sigreturn));
231 /* NOTREACHED */
232 asm volatile (".set reorder; .set at; .set macro");
234 return NULL;