/* * Emulation of Linux signals * * Copyright (c) 2003 Fabrice Bellard * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "qemu/osdep.h" #include "qemu.h" #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" struct target_sigcontext { struct target_pt_regs regs; /* needs to be first */ uint32_t oldmask; uint32_t usp; /* usp before stacking this gunk on it */ }; /* Signal frames. */ struct target_signal_frame { struct target_sigcontext sc; uint32_t extramask[TARGET_NSIG_WORDS - 1]; uint16_t retcode[4]; /* Trampoline code. */ }; static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) { __put_user(env->regs[0], &sc->regs.r0); __put_user(env->regs[1], &sc->regs.r1); __put_user(env->regs[2], &sc->regs.r2); __put_user(env->regs[3], &sc->regs.r3); __put_user(env->regs[4], &sc->regs.r4); __put_user(env->regs[5], &sc->regs.r5); __put_user(env->regs[6], &sc->regs.r6); __put_user(env->regs[7], &sc->regs.r7); __put_user(env->regs[8], &sc->regs.r8); __put_user(env->regs[9], &sc->regs.r9); __put_user(env->regs[10], &sc->regs.r10); __put_user(env->regs[11], &sc->regs.r11); __put_user(env->regs[12], &sc->regs.r12); __put_user(env->regs[13], &sc->regs.r13); __put_user(env->regs[14], &sc->usp); __put_user(env->regs[15], &sc->regs.acr); __put_user(env->pregs[PR_MOF], &sc->regs.mof); __put_user(env->pregs[PR_SRP], &sc->regs.srp); __put_user(env->pc, &sc->regs.erp); } static void restore_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) { __get_user(env->regs[0], &sc->regs.r0); __get_user(env->regs[1], &sc->regs.r1); __get_user(env->regs[2], &sc->regs.r2); __get_user(env->regs[3], &sc->regs.r3); __get_user(env->regs[4], &sc->regs.r4); __get_user(env->regs[5], &sc->regs.r5); __get_user(env->regs[6], &sc->regs.r6); __get_user(env->regs[7], &sc->regs.r7); __get_user(env->regs[8], &sc->regs.r8); __get_user(env->regs[9], &sc->regs.r9); __get_user(env->regs[10], &sc->regs.r10); __get_user(env->regs[11], &sc->regs.r11); __get_user(env->regs[12], &sc->regs.r12); __get_user(env->regs[13], &sc->regs.r13); __get_user(env->regs[14], &sc->usp); __get_user(env->regs[15], &sc->regs.acr); __get_user(env->pregs[PR_MOF], &sc->regs.mof); __get_user(env->pregs[PR_SRP], &sc->regs.srp); __get_user(env->pc, &sc->regs.erp); } static abi_ulong get_sigframe(CPUCRISState *env, int framesize) { abi_ulong sp; /* Align the stack downwards to 4. */ sp = (env->regs[R_SP] & ~3); return sp - framesize; } static void setup_sigreturn(uint16_t *retcode) { /* This is movu.w __NR_sigreturn, r9; break 13; */ __put_user(0x9c5f, retcode + 0); __put_user(TARGET_NR_sigreturn, retcode + 1); __put_user(0xe93d, retcode + 2); } void setup_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUCRISState *env) { struct target_signal_frame *frame; abi_ulong frame_addr; int i; frame_addr = get_sigframe(env, sizeof *frame); trace_user_setup_frame(env, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) goto badframe; /* * The CRIS signal return trampoline. A real linux/CRIS kernel doesn't * use this trampoline anymore but it sets it up for GDB. */ setup_sigreturn(frame->retcode); /* Save the mask. */ __put_user(set->sig[0], &frame->sc.oldmask); for(i = 1; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &frame->extramask[i - 1]); } setup_sigcontext(&frame->sc, env); /* Move the stack and setup the arguments for the handler. */ env->regs[R_SP] = frame_addr; env->regs[10] = sig; env->pc = (unsigned long) ka->_sa_handler; /* Link SRP so the guest returns through the trampoline. */ env->pregs[PR_SRP] = default_sigreturn; unlock_user_struct(frame, frame_addr, 1); return; badframe: force_sigsegv(sig); } void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUCRISState *env) { qemu_log_mask(LOG_UNIMP, "setup_rt_frame: not implemented\n"); } long do_sigreturn(CPUCRISState *env) { struct target_signal_frame *frame; abi_ulong frame_addr; target_sigset_t target_set; sigset_t set; int i; frame_addr = env->regs[R_SP]; trace_user_do_sigreturn(env, frame_addr); /* Make sure the guest isn't playing games. */ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) { goto badframe; } /* Restore blocked signals */ __get_user(target_set.sig[0], &frame->sc.oldmask); for(i = 1; i < TARGET_NSIG_WORDS; i++) { __get_user(target_set.sig[i], &frame->extramask[i - 1]); } target_to_host_sigset_internal(&set, &target_set); set_sigmask(&set); restore_sigcontext(&frame->sc, env); unlock_user_struct(frame, frame_addr, 0); return -QEMU_ESIGRETURN; badframe: force_sig(TARGET_SIGSEGV); return -QEMU_ESIGRETURN; } long do_rt_sigreturn(CPUCRISState *env) { trace_user_do_rt_sigreturn(env, 0); qemu_log_mask(LOG_UNIMP, "do_rt_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } void setup_sigtramp(abi_ulong sigtramp_page) { uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 6, 0); assert(tramp != NULL); default_sigreturn = sigtramp_page; setup_sigreturn(tramp); unlock_user(tramp, sigtramp_page, 6); }