]> git.proxmox.com Git - mirror_qemu.git/blob - linux-user/mips/signal.c
qemu-img: rebase: Reuse in-chain BlockDriverState
[mirror_qemu.git] / linux-user / mips / signal.c
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 "signal-common.h"
22 #include "linux-user/trace.h"
23
24 # if defined(TARGET_ABI_MIPSO32)
25 struct 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 */
47 struct 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
66 struct 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
73 struct target_ucontext {
74 target_ulong tuc_flags;
75 target_ulong tuc_link;
76 target_stack_t tuc_stack;
77 target_ulong pad0;
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 inline int install_sigtramp(unsigned int *tramp, unsigned int syscall)
91 {
92 int err = 0;
93
94 /*
95 * Set up the return code ...
96 *
97 * li v0, __NR__foo_sigreturn
98 * syscall
99 */
100
101 __put_user(0x24020000 + syscall, tramp + 0);
102 __put_user(0x0000000c , tramp + 1);
103 return err;
104 }
105
106 static inline void setup_sigcontext(CPUMIPSState *regs,
107 struct target_sigcontext *sc)
108 {
109 int i;
110
111 __put_user(exception_resume_pc(regs), &sc->sc_pc);
112 regs->hflags &= ~MIPS_HFLAG_BMASK;
113
114 __put_user(0, &sc->sc_regs[0]);
115 for (i = 1; i < 32; ++i) {
116 __put_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
117 }
118
119 __put_user(regs->active_tc.HI[0], &sc->sc_mdhi);
120 __put_user(regs->active_tc.LO[0], &sc->sc_mdlo);
121
122 /* Rather than checking for dsp existence, always copy. The storage
123 would just be garbage otherwise. */
124 __put_user(regs->active_tc.HI[1], &sc->sc_hi1);
125 __put_user(regs->active_tc.HI[2], &sc->sc_hi2);
126 __put_user(regs->active_tc.HI[3], &sc->sc_hi3);
127 __put_user(regs->active_tc.LO[1], &sc->sc_lo1);
128 __put_user(regs->active_tc.LO[2], &sc->sc_lo2);
129 __put_user(regs->active_tc.LO[3], &sc->sc_lo3);
130 {
131 uint32_t dsp = cpu_rddsp(0x3ff, regs);
132 __put_user(dsp, &sc->sc_dsp);
133 }
134
135 __put_user(1, &sc->sc_used_math);
136
137 for (i = 0; i < 32; ++i) {
138 __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
139 }
140 }
141
142 static inline void
143 restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc)
144 {
145 int i;
146
147 __get_user(regs->CP0_EPC, &sc->sc_pc);
148
149 __get_user(regs->active_tc.HI[0], &sc->sc_mdhi);
150 __get_user(regs->active_tc.LO[0], &sc->sc_mdlo);
151
152 for (i = 1; i < 32; ++i) {
153 __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]);
154 }
155
156 __get_user(regs->active_tc.HI[1], &sc->sc_hi1);
157 __get_user(regs->active_tc.HI[2], &sc->sc_hi2);
158 __get_user(regs->active_tc.HI[3], &sc->sc_hi3);
159 __get_user(regs->active_tc.LO[1], &sc->sc_lo1);
160 __get_user(regs->active_tc.LO[2], &sc->sc_lo2);
161 __get_user(regs->active_tc.LO[3], &sc->sc_lo3);
162 {
163 uint32_t dsp;
164 __get_user(dsp, &sc->sc_dsp);
165 cpu_wrdsp(dsp, 0x3ff, regs);
166 }
167
168 for (i = 0; i < 32; ++i) {
169 __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]);
170 }
171 }
172
173 /*
174 * Determine which stack to use..
175 */
176 static inline abi_ulong
177 get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size)
178 {
179 unsigned long sp;
180
181 /*
182 * FPU emulator may have its own trampoline active just
183 * above the user stack, 16-bytes before the next lowest
184 * 16 byte boundary. Try to avoid trashing it.
185 */
186 sp = target_sigsp(get_sp_from_cpustate(regs) - 32, ka);
187
188 return (sp - frame_size) & ~7;
189 }
190
191 static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env)
192 {
193 if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) {
194 env->hflags &= ~MIPS_HFLAG_M16;
195 env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT;
196 env->active_tc.PC &= ~(target_ulong) 1;
197 }
198 }
199
200 # if defined(TARGET_ABI_MIPSO32)
201 /* compare linux/arch/mips/kernel/signal.c:setup_frame() */
202 void setup_frame(int sig, struct target_sigaction * ka,
203 target_sigset_t *set, CPUMIPSState *regs)
204 {
205 struct sigframe *frame;
206 abi_ulong frame_addr;
207 int i;
208
209 frame_addr = get_sigframe(ka, regs, sizeof(*frame));
210 trace_user_setup_frame(regs, frame_addr);
211 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
212 goto give_sigsegv;
213 }
214
215 install_sigtramp(frame->sf_code, TARGET_NR_sigreturn);
216
217 setup_sigcontext(regs, &frame->sf_sc);
218
219 for(i = 0; i < TARGET_NSIG_WORDS; i++) {
220 __put_user(set->sig[i], &frame->sf_mask.sig[i]);
221 }
222
223 /*
224 * Arguments to signal handler:
225 *
226 * a0 = signal number
227 * a1 = 0 (should be cause)
228 * a2 = pointer to struct sigcontext
229 *
230 * $25 and PC point to the signal handler, $29 points to the
231 * struct sigframe.
232 */
233 regs->active_tc.gpr[ 4] = sig;
234 regs->active_tc.gpr[ 5] = 0;
235 regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc);
236 regs->active_tc.gpr[29] = frame_addr;
237 regs->active_tc.gpr[31] = frame_addr + offsetof(struct sigframe, sf_code);
238 /* The original kernel code sets CP0_EPC to the handler
239 * since it returns to userland using eret
240 * we cannot do this here, and we must set PC directly */
241 regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler;
242 mips_set_hflags_isa_mode_from_pc(regs);
243 unlock_user_struct(frame, frame_addr, 1);
244 return;
245
246 give_sigsegv:
247 force_sigsegv(sig);
248 }
249
250 long do_sigreturn(CPUMIPSState *regs)
251 {
252 struct sigframe *frame;
253 abi_ulong frame_addr;
254 sigset_t blocked;
255 target_sigset_t target_set;
256 int i;
257
258 frame_addr = regs->active_tc.gpr[29];
259 trace_user_do_sigreturn(regs, frame_addr);
260 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
261 goto badframe;
262
263 for(i = 0; i < TARGET_NSIG_WORDS; i++) {
264 __get_user(target_set.sig[i], &frame->sf_mask.sig[i]);
265 }
266
267 target_to_host_sigset_internal(&blocked, &target_set);
268 set_sigmask(&blocked);
269
270 restore_sigcontext(regs, &frame->sf_sc);
271
272 #if 0
273 /*
274 * Don't let your children do this ...
275 */
276 __asm__ __volatile__(
277 "move\t$29, %0\n\t"
278 "j\tsyscall_exit"
279 :/* no outputs */
280 :"r" (&regs));
281 /* Unreached */
282 #endif
283
284 regs->active_tc.PC = regs->CP0_EPC;
285 mips_set_hflags_isa_mode_from_pc(regs);
286 /* I am not sure this is right, but it seems to work
287 * maybe a problem with nested signals ? */
288 regs->CP0_EPC = 0;
289 return -TARGET_QEMU_ESIGRETURN;
290
291 badframe:
292 force_sig(TARGET_SIGSEGV);
293 return -TARGET_QEMU_ESIGRETURN;
294 }
295 # endif /* O32 */
296
297 void setup_rt_frame(int sig, struct target_sigaction *ka,
298 target_siginfo_t *info,
299 target_sigset_t *set, CPUMIPSState *env)
300 {
301 struct target_rt_sigframe *frame;
302 abi_ulong frame_addr;
303 int i;
304
305 frame_addr = get_sigframe(ka, env, sizeof(*frame));
306 trace_user_setup_rt_frame(env, frame_addr);
307 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
308 goto give_sigsegv;
309 }
310
311 install_sigtramp(frame->rs_code, TARGET_NR_rt_sigreturn);
312
313 tswap_siginfo(&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] = frame_addr
342 + offsetof(struct target_rt_sigframe, rs_code);
343 /* The original kernel code sets CP0_EPC to the handler
344 * since it returns to userland using eret
345 * we cannot do this here, and we must set PC directly */
346 env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler;
347 mips_set_hflags_isa_mode_from_pc(env);
348 unlock_user_struct(frame, frame_addr, 1);
349 return;
350
351 give_sigsegv:
352 unlock_user_struct(frame, frame_addr, 1);
353 force_sigsegv(sig);
354 }
355
356 long do_rt_sigreturn(CPUMIPSState *env)
357 {
358 struct target_rt_sigframe *frame;
359 abi_ulong frame_addr;
360 sigset_t blocked;
361
362 frame_addr = env->active_tc.gpr[29];
363 trace_user_do_rt_sigreturn(env, frame_addr);
364 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
365 goto badframe;
366 }
367
368 target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
369 set_sigmask(&blocked);
370
371 restore_sigcontext(env, &frame->rs_uc.tuc_mcontext);
372
373 if (do_sigaltstack(frame_addr +
374 offsetof(struct target_rt_sigframe, rs_uc.tuc_stack),
375 0, get_sp_from_cpustate(env)) == -EFAULT)
376 goto badframe;
377
378 env->active_tc.PC = env->CP0_EPC;
379 mips_set_hflags_isa_mode_from_pc(env);
380 /* I am not sure this is right, but it seems to work
381 * maybe a problem with nested signals ? */
382 env->CP0_EPC = 0;
383 return -TARGET_QEMU_ESIGRETURN;
384
385 badframe:
386 force_sig(TARGET_SIGSEGV);
387 return -TARGET_QEMU_ESIGRETURN;
388 }