signal.c (11574B)
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) 26struct 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 */ 48struct 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 67struct 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 74struct 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 82struct 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 */ 90static 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 103static 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} 138 139static inline void 140restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) 141{ 142 int i; 143 144 __get_user(regs->CP0_EPC, &sc->sc_pc); 145 146 __get_user(regs->active_tc.HI[0], &sc->sc_mdhi); 147 __get_user(regs->active_tc.LO[0], &sc->sc_mdlo); 148 149 for (i = 1; i < 32; ++i) { 150 __get_user(regs->active_tc.gpr[i], &sc->sc_regs[i]); 151 } 152 153 __get_user(regs->active_tc.HI[1], &sc->sc_hi1); 154 __get_user(regs->active_tc.HI[2], &sc->sc_hi2); 155 __get_user(regs->active_tc.HI[3], &sc->sc_hi3); 156 __get_user(regs->active_tc.LO[1], &sc->sc_lo1); 157 __get_user(regs->active_tc.LO[2], &sc->sc_lo2); 158 __get_user(regs->active_tc.LO[3], &sc->sc_lo3); 159 { 160 uint32_t dsp; 161 __get_user(dsp, &sc->sc_dsp); 162 cpu_wrdsp(dsp, 0x3ff, regs); 163 } 164 165 for (i = 0; i < 32; ++i) { 166 __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); 167 } 168} 169 170/* 171 * Determine which stack to use.. 172 */ 173static inline abi_ulong 174get_sigframe(struct target_sigaction *ka, CPUMIPSState *regs, size_t frame_size) 175{ 176 unsigned long sp; 177 178 /* 179 * FPU emulator may have its own trampoline active just 180 * above the user stack, 16-bytes before the next lowest 181 * 16 byte boundary. Try to avoid trashing it. 182 */ 183 sp = target_sigsp(get_sp_from_cpustate(regs) - 32, ka); 184 185 return (sp - frame_size) & ~7; 186} 187 188static void mips_set_hflags_isa_mode_from_pc(CPUMIPSState *env) 189{ 190 if (env->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { 191 env->hflags &= ~MIPS_HFLAG_M16; 192 env->hflags |= (env->active_tc.PC & 1) << MIPS_HFLAG_M16_SHIFT; 193 env->active_tc.PC &= ~(target_ulong) 1; 194 } 195} 196 197# if defined(TARGET_ABI_MIPSO32) 198/* compare linux/arch/mips/kernel/signal.c:setup_frame() */ 199void setup_frame(int sig, struct target_sigaction * ka, 200 target_sigset_t *set, CPUMIPSState *regs) 201{ 202 struct sigframe *frame; 203 abi_ulong frame_addr; 204 int i; 205 206 frame_addr = get_sigframe(ka, regs, sizeof(*frame)); 207 trace_user_setup_frame(regs, frame_addr); 208 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { 209 goto give_sigsegv; 210 } 211 212 setup_sigcontext(regs, &frame->sf_sc); 213 214 for(i = 0; i < TARGET_NSIG_WORDS; i++) { 215 __put_user(set->sig[i], &frame->sf_mask.sig[i]); 216 } 217 218 /* 219 * Arguments to signal handler: 220 * 221 * a0 = signal number 222 * a1 = 0 (should be cause) 223 * a2 = pointer to struct sigcontext 224 * 225 * $25 and PC point to the signal handler, $29 points to the 226 * struct sigframe. 227 */ 228 regs->active_tc.gpr[ 4] = sig; 229 regs->active_tc.gpr[ 5] = 0; 230 regs->active_tc.gpr[ 6] = frame_addr + offsetof(struct sigframe, sf_sc); 231 regs->active_tc.gpr[29] = frame_addr; 232 regs->active_tc.gpr[31] = default_sigreturn; 233 /* The original kernel code sets CP0_EPC to the handler 234 * since it returns to userland using eret 235 * we cannot do this here, and we must set PC directly */ 236 regs->active_tc.PC = regs->active_tc.gpr[25] = ka->_sa_handler; 237 mips_set_hflags_isa_mode_from_pc(regs); 238 unlock_user_struct(frame, frame_addr, 1); 239 return; 240 241give_sigsegv: 242 force_sigsegv(sig); 243} 244 245long do_sigreturn(CPUMIPSState *regs) 246{ 247 struct sigframe *frame; 248 abi_ulong frame_addr; 249 sigset_t blocked; 250 target_sigset_t target_set; 251 int i; 252 253 frame_addr = regs->active_tc.gpr[29]; 254 trace_user_do_sigreturn(regs, frame_addr); 255 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) 256 goto badframe; 257 258 for(i = 0; i < TARGET_NSIG_WORDS; i++) { 259 __get_user(target_set.sig[i], &frame->sf_mask.sig[i]); 260 } 261 262 target_to_host_sigset_internal(&blocked, &target_set); 263 set_sigmask(&blocked); 264 265 restore_sigcontext(regs, &frame->sf_sc); 266 267#if 0 268 /* 269 * Don't let your children do this ... 270 */ 271 __asm__ __volatile__( 272 "move\t$29, %0\n\t" 273 "j\tsyscall_exit" 274 :/* no outputs */ 275 :"r" (®s)); 276 /* Unreached */ 277#endif 278 279 regs->active_tc.PC = regs->CP0_EPC; 280 mips_set_hflags_isa_mode_from_pc(regs); 281 /* I am not sure this is right, but it seems to work 282 * maybe a problem with nested signals ? */ 283 regs->CP0_EPC = 0; 284 return -TARGET_QEMU_ESIGRETURN; 285 286badframe: 287 force_sig(TARGET_SIGSEGV); 288 return -TARGET_QEMU_ESIGRETURN; 289} 290# endif /* O32 */ 291 292void setup_rt_frame(int sig, struct target_sigaction *ka, 293 target_siginfo_t *info, 294 target_sigset_t *set, CPUMIPSState *env) 295{ 296 struct target_rt_sigframe *frame; 297 abi_ulong frame_addr; 298 int i; 299 300 frame_addr = get_sigframe(ka, env, sizeof(*frame)); 301 trace_user_setup_rt_frame(env, frame_addr); 302 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { 303 goto give_sigsegv; 304 } 305 306 tswap_siginfo(&frame->rs_info, info); 307 308 __put_user(0, &frame->rs_uc.tuc_flags); 309 __put_user(0, &frame->rs_uc.tuc_link); 310 target_save_altstack(&frame->rs_uc.tuc_stack, env); 311 312 setup_sigcontext(env, &frame->rs_uc.tuc_mcontext); 313 314 for(i = 0; i < TARGET_NSIG_WORDS; i++) { 315 __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]); 316 } 317 318 /* 319 * Arguments to signal handler: 320 * 321 * a0 = signal number 322 * a1 = pointer to siginfo_t 323 * a2 = pointer to ucontext_t 324 * 325 * $25 and PC point to the signal handler, $29 points to the 326 * struct sigframe. 327 */ 328 env->active_tc.gpr[ 4] = sig; 329 env->active_tc.gpr[ 5] = frame_addr 330 + offsetof(struct target_rt_sigframe, rs_info); 331 env->active_tc.gpr[ 6] = frame_addr 332 + offsetof(struct target_rt_sigframe, rs_uc); 333 env->active_tc.gpr[29] = frame_addr; 334 env->active_tc.gpr[31] = default_rt_sigreturn; 335 336 /* 337 * The original kernel code sets CP0_EPC to the handler 338 * since it returns to userland using eret 339 * we cannot do this here, and we must set PC directly 340 */ 341 env->active_tc.PC = env->active_tc.gpr[25] = ka->_sa_handler; 342 mips_set_hflags_isa_mode_from_pc(env); 343 unlock_user_struct(frame, frame_addr, 1); 344 return; 345 346give_sigsegv: 347 unlock_user_struct(frame, frame_addr, 1); 348 force_sigsegv(sig); 349} 350 351long do_rt_sigreturn(CPUMIPSState *env) 352{ 353 struct target_rt_sigframe *frame; 354 abi_ulong frame_addr; 355 sigset_t blocked; 356 357 frame_addr = env->active_tc.gpr[29]; 358 trace_user_do_rt_sigreturn(env, frame_addr); 359 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { 360 goto badframe; 361 } 362 363 target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask); 364 set_sigmask(&blocked); 365 366 restore_sigcontext(env, &frame->rs_uc.tuc_mcontext); 367 target_restore_altstack(&frame->rs_uc.tuc_stack, env); 368 369 env->active_tc.PC = env->CP0_EPC; 370 mips_set_hflags_isa_mode_from_pc(env); 371 /* I am not sure this is right, but it seems to work 372 * maybe a problem with nested signals ? */ 373 env->CP0_EPC = 0; 374 return -TARGET_QEMU_ESIGRETURN; 375 376badframe: 377 force_sig(TARGET_SIGSEGV); 378 return -TARGET_QEMU_ESIGRETURN; 379} 380 381void setup_sigtramp(abi_ulong sigtramp_page) 382{ 383 uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 8, 0); 384 assert(tramp != NULL); 385 386#ifdef TARGET_ARCH_HAS_SETUP_FRAME 387 default_sigreturn = sigtramp_page; 388 install_sigtramp(tramp, TARGET_NR_sigreturn); 389#endif 390 391 default_rt_sigreturn = sigtramp_page + 8; 392 install_sigtramp(tramp + 2, TARGET_NR_rt_sigreturn); 393 394 unlock_user(tramp, sigtramp_page, 2 * 8); 395}