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