]> Git Repo - qemu.git/blame - linux-user/mips/signal.c
linux-user: Use target_restore_altstack in all sigreturn
[qemu.git] / linux-user / mips / signal.c
CommitLineData
befb7447
LV
1/*
2 * Emulation of Linux signals
3 *
4 * Copyright (c) 2003 Fabrice Bellard
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
8949bef1
LV
19#include "qemu/osdep.h"
20#include "qemu.h"
8949bef1
LV
21#include "signal-common.h"
22#include "linux-user/trace.h"
23
24# if defined(TARGET_ABI_MIPSO32)
25struct target_sigcontext {
26 uint32_t sc_regmask; /* Unused */
27 uint32_t sc_status;
28 uint64_t sc_pc;
29 uint64_t sc_regs[32];
30 uint64_t sc_fpregs[32];
31 uint32_t sc_ownedfp; /* Unused */
32 uint32_t sc_fpc_csr;
33 uint32_t sc_fpc_eir; /* Unused */
34 uint32_t sc_used_math;
35 uint32_t sc_dsp; /* dsp status, was sc_ssflags */
36 uint32_t pad0;
37 uint64_t sc_mdhi;
38 uint64_t sc_mdlo;
39 target_ulong sc_hi1; /* Was sc_cause */
40 target_ulong sc_lo1; /* Was sc_badvaddr */
41 target_ulong sc_hi2; /* Was sc_sigset[4] */
42 target_ulong sc_lo2;
43 target_ulong sc_hi3;
44 target_ulong sc_lo3;
45};
46# else /* N32 || N64 */
47struct target_sigcontext {
48 uint64_t sc_regs[32];
49 uint64_t sc_fpregs[32];
50 uint64_t sc_mdhi;
51 uint64_t sc_hi1;
52 uint64_t sc_hi2;
53 uint64_t sc_hi3;
54 uint64_t sc_mdlo;
55 uint64_t sc_lo1;
56 uint64_t sc_lo2;
57 uint64_t sc_lo3;
58 uint64_t sc_pc;
59 uint32_t sc_fpc_csr;
60 uint32_t sc_used_math;
61 uint32_t sc_dsp;
62 uint32_t sc_reserved;
63};
64# endif /* O32 */
65
66struct sigframe {
67 uint32_t sf_ass[4]; /* argument save space for o32 */
68 uint32_t sf_code[2]; /* signal trampoline */
69 struct target_sigcontext sf_sc;
70 target_sigset_t sf_mask;
71};
72
73struct target_ucontext {
4ced996f
AM
74 abi_ulong tuc_flags;
75 abi_ulong tuc_link;
8949bef1 76 target_stack_t tuc_stack;
8949bef1
LV
77 struct target_sigcontext tuc_mcontext;
78 target_sigset_t tuc_sigmask;
79};
80
81struct target_rt_sigframe {
82 uint32_t rs_ass[4]; /* argument save space for o32 */
83 uint32_t rs_code[2]; /* signal trampoline */
84 struct target_siginfo rs_info;
85 struct target_ucontext rs_uc;
86};
87
88/* Install trampoline to jump back from signal handler */
89static inline int install_sigtramp(unsigned int *tramp, unsigned int syscall)
90{
91 int err = 0;
92
93 /*
94 * Set up the return code ...
95 *
96 * li v0, __NR__foo_sigreturn
97 * syscall
98 */
99
100 __put_user(0x24020000 + syscall, tramp + 0);
101 __put_user(0x0000000c , tramp + 1);
102 return err;
103}
104
105static inline void setup_sigcontext(CPUMIPSState *regs,
106 struct target_sigcontext *sc)
107{
108 int i;
109
110 __put_user(exception_resume_pc(regs), &sc->sc_pc);
111 regs->hflags &= ~MIPS_HFLAG_BMASK;
112
113 __put_user(0, &sc->sc_regs[0]);
114 for (i = 1; i < 32; ++i) {
115 __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
116 }
117
118 __put_user(regs->active_tc.HI[0], &sc->sc_mdhi);
119 __put_user(regs->active_tc.LO[0], &sc->sc_mdlo);
120
121 /* Rather than checking for dsp existence, always copy. The storage
122 would just be garbage otherwise. */
123 __put_user(regs->active_tc.HI[1], &sc->sc_hi1);
124 __put_user(regs->active_tc.HI[2], &sc->sc_hi2);
125 __put_user(regs->active_tc.HI[3], &sc->sc_hi3);
126 __put_user(regs->active_tc.LO[1], &sc->sc_lo1);
127 __put_user(regs->active_tc.LO[2], &sc->sc_lo2);
128 __put_user(regs->active_tc.LO[3], &sc->sc_lo3);
129 {
130 uint32_t dsp = cpu_rddsp(0x3ff, regs);
131 __put_user(dsp, &sc->sc_dsp);
132 }
133
134 __put_user(1, &sc->sc_used_math);
135
136 for (i = 0; i < 32; ++i) {
137 __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
138 }
139}
140
141static inline void
142restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc)
143{
144 int i;
145
146 __get_user(regs->CP0_EPC, &sc->sc_pc);
147
148 __get_user(regs->active_tc.HI[0], &sc->sc_mdhi);
149 __get_user(regs->active_tc.LO[0], &sc->sc_mdlo);
150
151 for (i = 1; i < 32; ++i) {
152 __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
153 }
154
155 __get_user(regs->active_tc.HI[1], &sc->sc_hi1);
156 __get_user(regs->active_tc.HI[2], &sc->sc_hi2);
157 __get_user(regs->active_tc.HI[3], &sc->sc_hi3);
158 __get_user(regs->active_tc.LO[1], &sc->sc_lo1);
159 __get_user(regs->active_tc.LO[2], &sc->sc_lo2);
160 __get_user(regs->active_tc.LO[3], &sc->sc_lo3);
161 {
162 uint32_t dsp;
163 __get_user(dsp, &sc->sc_dsp);
164 cpu_wrdsp(dsp, 0x3ff, regs);
165 }
166
167 for (i = 0; i < 32; ++i) {
168 __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
169 }
170}
171
172/*
173 * Determine which stack to use..
174 */
175static inline abi_ulong
176get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size)
177{
178 unsigned long sp;
179
8949bef1
LV
180 /*
181 * FPU emulator may have its own trampoline active just
182 * above the user stack, 16-bytes before the next lowest
183 * 16 byte boundary. Try to avoid trashing it.
184 */
465e237b 185 sp = target_sigsp(get_sp_from_cpustate(regs) - 32, ka);
8949bef1
LV
186
187 return (sp - frame_size) & ~7;
188}
189
190static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env)
191{
192 if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) {
193 env->hflags &= ~MIPS_HFLAG_M16;
194 env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT;
195 env->active_tc.PC &= ~(target_ulong) 1;
196 }
197}
198
199# if defined(TARGET_ABI_MIPSO32)
200/* compare linux/arch/mips/kernel/signal.c:setup_frame() */
201void setup_frame(int sig, struct target_sigaction * ka,
202 target_sigset_t *set, CPUMIPSState *regs)
203{
204 struct sigframe *frame;
205 abi_ulong frame_addr;
206 int i;
207
208 frame_addr = get_sigframe(ka, regs, sizeof(*frame));
209 trace_user_setup_frame(regs, frame_addr);
210 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
211 goto give_sigsegv;
212 }
213
214 install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
215
216 setup_sigcontext(regs, &frame->sf_sc);
217
218 for(i = 0; i < TARGET_NSIG_WORDS; i++) {
219 __put_user(set->sig[i], &frame->sf_mask.sig[i]);
220 }
221
222 /*
223 * Arguments to signal handler:
224 *
225 * a0 = signal number
226 * a1 = 0 (should be cause)
227 * a2 = pointer to struct sigcontext
228 *
229 * $25 and PC point to the signal handler, $29 points to the
230 * struct sigframe.
231 */
232 regs->active_tc.gpr[ 4] = sig;
233 regs->active_tc.gpr[ 5] = 0;
234 regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc);
235 regs->active_tc.gpr[29] = frame_addr;
236 regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code);
237 /* The original kernel code sets CP0_EPC to the handler
238 * since it returns to userland using eret
239 * we cannot do this here, and we must set PC directly */
240 regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler;
241 mips_set_hflags_isa_mode_from_pc(regs);
242 unlock_user_struct(frame, frame_addr, 1);
243 return;
244
245give_sigsegv:
246 force_sigsegv(sig);
247}
248
249long do_sigreturn(CPUMIPSState *regs)
250{
251 struct sigframe *frame;
252 abi_ulong frame_addr;
253 sigset_t blocked;
254 target_sigset_t target_set;
255 int i;
256
257 frame_addr = regs->active_tc.gpr[29];
258 trace_user_do_sigreturn(regs, frame_addr);
259 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
260 goto badframe;
261
262 for(i = 0; i < TARGET_NSIG_WORDS; i++) {
263 __get_user(target_set.sig[i], &frame->sf_mask.sig[i]);
264 }
265
266 target_to_host_sigset_internal(&blocked, &target_set);
267 set_sigmask(&blocked);
268
269 restore_sigcontext(regs, &frame->sf_sc);
270
271#if 0
272 /*
273 * Don't let your children do this ...
274 */
275 __asm__ __volatile__(
276 "move\t$29, %0\n\t"
277 "j\tsyscall_exit"
278 :/* no outputs */
279 :"r" (&regs));
280 /* Unreached */
281#endif
282
283 regs->active_tc.PC = regs->CP0_EPC;
284 mips_set_hflags_isa_mode_from_pc(regs);
285 /* I am not sure this is right, but it seems to work
286 * maybe a problem with nested signals ? */
287 regs->CP0_EPC = 0;
288 return -TARGET_QEMU_ESIGRETURN;
289
290badframe:
291 force_sig(TARGET_SIGSEGV);
292 return -TARGET_QEMU_ESIGRETURN;
293}
294# endif /* O32 */
295
296void setup_rt_frame(int sig, struct target_sigaction *ka,
297 target_siginfo_t *info,
298 target_sigset_t *set, CPUMIPSState *env)
299{
300 struct target_rt_sigframe *frame;
301 abi_ulong frame_addr;
302 int i;
303
304 frame_addr = get_sigframe(ka, env, sizeof(*frame));
305 trace_user_setup_rt_frame(env, frame_addr);
306 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
307 goto give_sigsegv;
308 }
309
310 install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn);
311
312 tswap_siginfo(&frame->rs_info, info);
313
314 __put_user(0, &frame->rs_uc.tuc_flags);
315 __put_user(0, &frame->rs_uc.tuc_link);
465e237b 316 target_save_altstack(&frame->rs_uc.tuc_stack, env);
8949bef1
LV
317
318 setup_sigcontext(env, &frame->rs_uc.tuc_mcontext);
319
320 for(i = 0; i < TARGET_NSIG_WORDS; i++) {
321 __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
322 }
323
324 /*
325 * Arguments to signal handler:
326 *
327 * a0 = signal number
328 * a1 = pointer to siginfo_t
329 * a2 = pointer to ucontext_t
330 *
331 * $25 and PC point to the signal handler, $29 points to the
332 * struct sigframe.
333 */
334 env->active_tc.gpr[ 4] = sig;
335 env->active_tc.gpr[ 5] = frame_addr
336 + offsetof(struct target_rt_sigframe, rs_info);
337 env->active_tc.gpr[ 6] = frame_addr
338 + offsetof(struct target_rt_sigframe, rs_uc);
339 env->active_tc.gpr[29] = frame_addr;
340 env->active_tc.gpr[31] = frame_addr
341 + offsetof(struct target_rt_sigframe, rs_code);
342 /* The original kernel code sets CP0_EPC to the handler
343 * since it returns to userland using eret
344 * we cannot do this here, and we must set PC directly */
345 env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
346 mips_set_hflags_isa_mode_from_pc(env);
347 unlock_user_struct(frame, frame_addr, 1);
348 return;
349
350give_sigsegv:
351 unlock_user_struct(frame, frame_addr, 1);
352 force_sigsegv(sig);
353}
354
355long do_rt_sigreturn(CPUMIPSState *env)
356{
357 struct target_rt_sigframe *frame;
358 abi_ulong frame_addr;
359 sigset_t blocked;
360
361 frame_addr = env->active_tc.gpr[29];
362 trace_user_do_rt_sigreturn(env, frame_addr);
363 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
364 goto badframe;
365 }
366
367 target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
368 set_sigmask(&blocked);
369
370 restore_sigcontext(env, &frame->rs_uc.tuc_mcontext);
56384cf3 371 target_restore_altstack(&frame->rs_uc.tuc_stack, get_sp_from_cpustate(env));
8949bef1
LV
372
373 env->active_tc.PC = env->CP0_EPC;
374 mips_set_hflags_isa_mode_from_pc(env);
375 /* I am not sure this is right, but it seems to work
376 * maybe a problem with nested signals ? */
377 env->CP0_EPC = 0;
378 return -TARGET_QEMU_ESIGRETURN;
379
380badframe:
381 force_sig(TARGET_SIGSEGV);
382 return -TARGET_QEMU_ESIGRETURN;
383}
This page took 0.213249 seconds and 4 git commands to generate.