]>
Commit | Line | Data |
---|---|---|
1 | /* Set thread_state for sighandler, and sigcontext to recover. MIPS version. | |
2 | Copyright (C) 1996, 1997, 1998, 2005 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 Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the 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 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library; if not, write to the Free | |
17 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
18 | 02111-1307 USA. */ | |
19 | ||
20 | #include <hurd/signal.h> | |
21 | #include <hurd/userlink.h> | |
22 | #include <thread_state.h> | |
23 | #include <assert.h> | |
24 | #include <errno.h> | |
25 | #include "hurdfault.h" | |
26 | #include <intr-msg.h> | |
27 | ||
28 | ||
29 | struct sigcontext * | |
30 | _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler, | |
31 | int signo, struct hurd_signal_detail *detail, | |
32 | volatile int rpc_wait, | |
33 | struct machine_thread_all_state *state) | |
34 | { | |
35 | __label__ trampoline, rpc_wait_trampoline, firewall; | |
36 | void *volatile sigsp; | |
37 | struct sigcontext *scp; | |
38 | struct | |
39 | { | |
40 | int signo; | |
41 | long int sigcode; | |
42 | struct sigcontext *scp; /* Points to ctx, below. */ | |
43 | void *sigreturn_addr; | |
44 | void *sigreturn_returns_here; | |
45 | struct sigcontext *return_scp; /* Same; arg to sigreturn. */ | |
46 | struct sigcontext ctx; | |
47 | struct hurd_userlink link; | |
48 | } *stackframe; | |
49 | ||
50 | if (ss->context) | |
51 | { | |
52 | /* We have a previous sigcontext that sigreturn was about | |
53 | to restore when another signal arrived. We will just base | |
54 | our setup on that. */ | |
55 | if (! _hurdsig_catch_memory_fault (ss->context)) | |
56 | { | |
57 | memcpy (&state->basic, &ss->context->sc_mips_thread_state, | |
58 | sizeof (state->basic)); | |
59 | memcpy (&state->exc, &ss->context->sc_mips_exc_state, | |
60 | sizeof (state->exc)); | |
61 | state->set = (1 << MIPS_THREAD_STATE) | (1 << MIPS_EXC_STATE); | |
62 | if (state->exc.coproc_state & SC_COPROC_USE_FPU) | |
63 | { | |
64 | memcpy (&state->fpu, &ss->context->sc_mips_float_state, | |
65 | sizeof (state->fpu)); | |
66 | state->set |= (1 << MIPS_FLOAT_STATE); | |
67 | } | |
68 | } | |
69 | } | |
70 | ||
71 | if (! machine_get_basic_state (ss->thread, state)) | |
72 | return NULL; | |
73 | ||
74 | /* Save the original SP in the gratuitous s0 ($16) slot. | |
75 | We may need to reset the SP (the `r29' slot) to avoid clobbering an | |
76 | interrupted RPC frame. */ | |
77 | state->basic.r16 = state->basic.r29; | |
78 | ||
79 | if ((ss->actions[signo].sa_flags & SA_ONSTACK) && | |
80 | !(ss->sigaltstack.ss_flags & (SS_DISABLE|SS_ONSTACK))) | |
81 | { | |
82 | sigsp = ss->sigaltstack.ss_sp + ss->sigaltstack.ss_size; | |
83 | ss->sigaltstack.ss_flags |= SS_ONSTACK; | |
84 | /* XXX need to set up base of new stack for | |
85 | per-thread variables, cthreads. */ | |
86 | } | |
87 | else | |
88 | sigsp = (char *) state->basic.r29; | |
89 | ||
90 | /* Push the arguments to call `trampoline' on the stack. */ | |
91 | sigsp -= sizeof (*stackframe); | |
92 | stackframe = sigsp; | |
93 | ||
94 | if (_hurdsig_catch_memory_fault (stackframe)) | |
95 | { | |
96 | /* We got a fault trying to write the stack frame. | |
97 | We cannot set up the signal handler. | |
98 | Returning NULL tells our caller, who will nuke us with a SIGILL. */ | |
99 | return NULL; | |
100 | } | |
101 | else | |
102 | { | |
103 | int ok; | |
104 | ||
105 | extern void _hurdsig_longjmp_from_handler (void *, jmp_buf, int); | |
106 | ||
107 | /* Add a link to the thread's active-resources list. We mark this as | |
108 | the only user of the "resource", so the cleanup function will be | |
109 | called by any longjmp which is unwinding past the signal frame. | |
110 | The cleanup function (in sigunwind.c) will make sure that all the | |
111 | appropriate cleanups done by sigreturn are taken care of. */ | |
112 | stackframe->link.cleanup = &_hurdsig_longjmp_from_handler; | |
113 | stackframe->link.cleanup_data = &stackframe->ctx; | |
114 | stackframe->link.resource.next = NULL; | |
115 | stackframe->link.resource.prevp = NULL; | |
116 | stackframe->link.thread.next = ss->active_resources; | |
117 | stackframe->link.thread.prevp = &ss->active_resources; | |
118 | if (stackframe->link.thread.next) | |
119 | stackframe->link.thread.next->thread.prevp | |
120 | = &stackframe->link.thread.next; | |
121 | ss->active_resources = &stackframe->link; | |
122 | ||
123 | /* Set up the arguments for the signal handler. */ | |
124 | stackframe->signo = signo; | |
125 | stackframe->sigcode = detail->code; | |
126 | stackframe->scp = stackframe->return_scp = scp = &stackframe->ctx; | |
127 | stackframe->sigreturn_addr = &__sigreturn; | |
128 | stackframe->sigreturn_returns_here = &&firewall; /* Crash on return. */ | |
129 | ||
130 | /* Set up the sigcontext from the current state of the thread. */ | |
131 | ||
132 | scp->sc_onstack = ss->sigaltstack.ss_flags & SS_ONSTACK ? 1 : 0; | |
133 | ||
134 | /* struct sigcontext is laid out so that starting at sc_gpr | |
135 | mimics a struct mips_thread_state. */ | |
136 | memcpy (&scp->sc_mips_thread_state, | |
137 | &state->basic, sizeof (state->basic)); | |
138 | ||
139 | /* struct sigcontext is laid out so that starting at sc_cause | |
140 | mimics a struct mips_exc_state. */ | |
141 | ok = machine_get_state (ss->thread, state, MIPS_EXC_STATE, | |
142 | &state->exc, &scp->sc_cause, | |
143 | sizeof (state->exc)); | |
144 | ||
145 | if (ok && (scp->sc_coproc_used & SC_COPROC_USE_FPU)) | |
146 | /* struct sigcontext is laid out so that starting at sc_fpr | |
147 | mimics a struct mips_float_state. This state | |
148 | is only meaningful if the coprocessor was used. */ | |
149 | ok = machine_get_state (ss->thread, state, MIPS_FLOAT_STATE, | |
150 | &state->fpu, &scp->sc_mips_float_state, | |
151 | sizeof (state->fpu)); | |
152 | ||
153 | _hurdsig_end_catch_fault (); | |
154 | ||
155 | if (! ok) | |
156 | return NULL; | |
157 | } | |
158 | ||
159 | /* Modify the thread state to call the trampoline code on the new stack. */ | |
160 | if (rpc_wait) | |
161 | { | |
162 | /* The signalee thread was blocked in a mach_msg_trap system call, | |
163 | still waiting for a reply. We will have it run the special | |
164 | trampoline code which retries the message receive before running | |
165 | the signal handler. | |
166 | ||
167 | To do this we change the OPTION argument in its registers to | |
168 | enable only message reception, since the request message has | |
169 | already been sent. */ | |
170 | ||
171 | /* The system call arguments are stored in consecutive registers | |
172 | starting with a0 ($4). */ | |
173 | struct mach_msg_trap_args *args = (void *) &state->basic.r4; | |
174 | ||
175 | if (_hurdsig_catch_memory_fault (args)) | |
176 | { | |
177 | /* Faulted accessing ARGS. Bomb. */ | |
178 | return NULL; | |
179 | } | |
180 | ||
181 | assert (args->option & MACH_RCV_MSG); | |
182 | /* Disable the message-send, since it has already completed. The | |
183 | calls we retry need only wait to receive the reply message. */ | |
184 | args->option &= ~MACH_SEND_MSG; | |
185 | ||
186 | /* Limit the time to receive the reply message, in case the server | |
187 | claimed that `interrupt_operation' succeeded but in fact the RPC | |
188 | is hung. */ | |
189 | args->option |= MACH_RCV_TIMEOUT; | |
190 | args->timeout = _hurd_interrupted_rpc_timeout; | |
191 | ||
192 | _hurdsig_end_catch_fault (); | |
193 | ||
194 | state->basic.pc = (int) &&rpc_wait_trampoline; | |
195 | /* The reply-receiving trampoline code runs initially on the original | |
196 | user stack. We pass it the signal stack pointer in s4 ($20). */ | |
197 | state->basic.r29 = state->basic.r16; /* Restore mach_msg syscall SP. */ | |
198 | state->basic.r20 = (int) sigsp; | |
199 | /* After doing the message receive, the trampoline code will need to | |
200 | update the v0 ($2) value to be restored by sigreturn. To simplify | |
201 | the assembly code, we pass the address of its slot in SCP to the | |
202 | trampoline code in s5 ($21). */ | |
203 | state->basic.r21 = (int) &scp->sc_gpr[1]; | |
204 | /* We must preserve the mach_msg_trap args in a0..t2 ($4..$10). | |
205 | Pass the handler args to the trampoline code in s1..s3 ($17..$19). */ | |
206 | state->basic.r17 = signo; | |
207 | state->basic.r18 = detail->code; | |
208 | state->basic.r19 = (int) scp; | |
209 | } | |
210 | else | |
211 | { | |
212 | state->basic.pc = (int) &&trampoline; | |
213 | state->basic.r29 = (int) sigsp; | |
214 | state->basic.r4 = signo; | |
215 | state->basic.r5 = detail->code; | |
216 | state->basic.r6 = (int) scp; | |
217 | } | |
218 | ||
219 | /* We pass the handler function to the trampoline code in s6 ($22). */ | |
220 | state->basic.r22 = (int) handler; | |
221 | /* In the callee-saved register s0 ($16), we save the SCP value to pass | |
222 | to __sigreturn after the handler returns. */ | |
223 | state->basic.r16 = (int) scp; | |
224 | ||
225 | return scp; | |
226 | ||
227 | /* The trampoline code follows. This is not actually executed as part of | |
228 | this function, it is just convenient to write it that way. */ | |
229 | ||
230 | rpc_wait_trampoline: | |
231 | /* This is the entry point when we have an RPC reply message to receive | |
232 | before running the handler. The MACH_MSG_SEND bit has already been | |
233 | cleared in the OPTION argument in our registers. For our convenience, | |
234 | $3 points to the sc_gpr[1] member of the sigcontext (saved v0 ($2)). */ | |
235 | asm volatile | |
236 | (".set noat; .set noreorder; .set nomacro\n" | |
237 | /* Retry the interrupted mach_msg system call. */ | |
238 | #ifdef __mips64 | |
239 | "dli $2, -25\n" /* mach_msg_trap */ | |
240 | #else | |
241 | "li $2, -25\n" /* mach_msg_trap */ | |
242 | #endif | |
243 | "syscall\n" | |
244 | /* When the sigcontext was saved, v0 was MACH_RCV_INTERRUPTED. But | |
245 | now the message receive has completed and the original caller of | |
246 | the RPC (i.e. the code running when the signal arrived) needs to | |
247 | see the final return value of the message receive in v0. So | |
248 | store the new v0 value into the sc_gpr[1] member of the sigcontext | |
249 | (whose address is in s5 to make this code simpler). */ | |
250 | #ifdef __mips64 | |
251 | "sd $2, ($21)\n" | |
252 | #else | |
253 | "sw $2, ($21)\n" | |
254 | #endif | |
255 | /* Since the argument registers needed to have the mach_msg_trap | |
256 | arguments, we've stored the arguments to the handler function | |
257 | in registers s1..s3 ($17..$19). */ | |
258 | "move $4, $17\n" | |
259 | "move $5, $18\n" | |
260 | "move $6, $19\n" | |
261 | /* Switch to the signal stack. */ | |
262 | "move $29, $20\n"); | |
263 | ||
264 | trampoline: | |
265 | /* Entry point for running the handler normally. The arguments to the | |
266 | handler function are already in the standard registers: | |
267 | ||
268 | a0 SIGNO | |
269 | a1 SIGCODE | |
270 | a2 SCP | |
271 | */ | |
272 | asm volatile | |
273 | ("move $25, $22\n" /* Copy s6 to t9 for MIPS ABI. */ | |
274 | "jal $25; nop\n" /* Call the handler function. */ | |
275 | /* Call __sigreturn (SCP); this cannot return. */ | |
276 | #ifdef __mips64 | |
277 | "dla $1,%0\n" | |
278 | #else | |
279 | "la $1,%0\n" | |
280 | #endif | |
281 | "j $1\n" | |
282 | "move $4, $16" /* Set up arg from saved SCP in delay slot. */ | |
283 | : : "i" (&__sigreturn)); | |
284 | ||
285 | /* NOTREACHED */ | |
286 | asm volatile (".set reorder; .set at; .set macro"); | |
287 | ||
288 | firewall: | |
289 | asm volatile ("hlt: j hlt"); | |
290 | ||
291 | return NULL; | |
292 | } |