/* * 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 "target_signal.h" #include "signal-common.h" #include "linux-user/trace.h" #define MCONTEXT_VERSION 2 struct target_sigcontext { int version; unsigned long gregs[32]; }; struct target_ucontext { abi_ulong tuc_flags; abi_ulong tuc_link; target_stack_t tuc_stack; struct target_sigcontext tuc_mcontext; target_sigset_t tuc_sigmask; /* mask last for extensibility */ }; struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; }; static int rt_setup_ucontext(struct target_ucontext *uc, CPUNios2State *env) { unsigned long *gregs = uc->tuc_mcontext.gregs; __put_user(MCONTEXT_VERSION, &uc->tuc_mcontext.version); __put_user(env->regs[1], &gregs[0]); __put_user(env->regs[2], &gregs[1]); __put_user(env->regs[3], &gregs[2]); __put_user(env->regs[4], &gregs[3]); __put_user(env->regs[5], &gregs[4]); __put_user(env->regs[6], &gregs[5]); __put_user(env->regs[7], &gregs[6]); __put_user(env->regs[8], &gregs[7]); __put_user(env->regs[9], &gregs[8]); __put_user(env->regs[10], &gregs[9]); __put_user(env->regs[11], &gregs[10]); __put_user(env->regs[12], &gregs[11]); __put_user(env->regs[13], &gregs[12]); __put_user(env->regs[14], &gregs[13]); __put_user(env->regs[15], &gregs[14]); __put_user(env->regs[16], &gregs[15]); __put_user(env->regs[17], &gregs[16]); __put_user(env->regs[18], &gregs[17]); __put_user(env->regs[19], &gregs[18]); __put_user(env->regs[20], &gregs[19]); __put_user(env->regs[21], &gregs[20]); __put_user(env->regs[22], &gregs[21]); __put_user(env->regs[23], &gregs[22]); __put_user(env->regs[R_RA], &gregs[23]); __put_user(env->regs[R_FP], &gregs[24]); __put_user(env->regs[R_GP], &gregs[25]); __put_user(env->regs[R_EA], &gregs[27]); __put_user(env->regs[R_SP], &gregs[28]); return 0; } static int rt_restore_ucontext(CPUNios2State *env, struct target_ucontext *uc, int *pr2) { int temp; abi_ulong off, frame_addr = env->regs[R_SP]; unsigned long *gregs = uc->tuc_mcontext.gregs; int err; /* Always make any pending restarted system calls return -EINTR */ /* current->restart_block.fn = do_no_restart_syscall; */ __get_user(temp, &uc->tuc_mcontext.version); if (temp != MCONTEXT_VERSION) { return 1; } /* restore passed registers */ __get_user(env->regs[1], &gregs[0]); __get_user(env->regs[2], &gregs[1]); __get_user(env->regs[3], &gregs[2]); __get_user(env->regs[4], &gregs[3]); __get_user(env->regs[5], &gregs[4]); __get_user(env->regs[6], &gregs[5]); __get_user(env->regs[7], &gregs[6]); __get_user(env->regs[8], &gregs[7]); __get_user(env->regs[9], &gregs[8]); __get_user(env->regs[10], &gregs[9]); __get_user(env->regs[11], &gregs[10]); __get_user(env->regs[12], &gregs[11]); __get_user(env->regs[13], &gregs[12]); __get_user(env->regs[14], &gregs[13]); __get_user(env->regs[15], &gregs[14]); __get_user(env->regs[16], &gregs[15]); __get_user(env->regs[17], &gregs[16]); __get_user(env->regs[18], &gregs[17]); __get_user(env->regs[19], &gregs[18]); __get_user(env->regs[20], &gregs[19]); __get_user(env->regs[21], &gregs[20]); __get_user(env->regs[22], &gregs[21]); __get_user(env->regs[23], &gregs[22]); /* gregs[23] is handled below */ /* Verify, should this be settable */ __get_user(env->regs[R_FP], &gregs[24]); /* Verify, should this be settable */ __get_user(env->regs[R_GP], &gregs[25]); /* Not really necessary no user settable bits */ __get_user(temp, &gregs[26]); __get_user(env->regs[R_EA], &gregs[27]); __get_user(env->regs[R_RA], &gregs[23]); __get_user(env->regs[R_SP], &gregs[28]); off = offsetof(struct target_rt_sigframe, uc.tuc_stack); err = do_sigaltstack(frame_addr + off, 0, get_sp_from_cpustate(env)); if (err == -EFAULT) { return 1; } *pr2 = env->regs[2]; return 0; } static void *get_sigframe(struct target_sigaction *ka, CPUNios2State *env, size_t frame_size) { unsigned long usp; /* This is the X/Open sanctioned signal stack switching. */ usp = target_sigsp(get_sp_from_cpustate(env), ka); /* Verify, is it 32 or 64 bit aligned */ return (void *)((usp - frame_size) & -8UL); } void setup_rt_frame(int sig, struct target_sigaction *ka, target_siginfo_t *info, target_sigset_t *set, CPUNios2State *env) { struct target_rt_sigframe *frame; int i, err = 0; frame = get_sigframe(ka, env, sizeof(*frame)); if (ka->sa_flags & SA_SIGINFO) { tswap_siginfo(&frame->info, info); } /* Create the ucontext. */ __put_user(0, &frame->uc.tuc_flags); __put_user(0, &frame->uc.tuc_link); target_save_altstack(&frame->uc.tuc_stack, env); err |= rt_setup_ucontext(&frame->uc, env); for (i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user((abi_ulong)set->sig[i], (abi_ulong *)&frame->uc.tuc_sigmask.sig[i]); } if (err) { goto give_sigsegv; } /* Set up to return from userspace; jump to fixed address sigreturn trampoline on kuser page. */ env->regs[R_RA] = (unsigned long) (0x1044); /* Set up registers for signal handler */ env->regs[R_SP] = (unsigned long) frame; env->regs[4] = (unsigned long) sig; env->regs[5] = (unsigned long) &frame->info; env->regs[6] = (unsigned long) &frame->uc; env->regs[R_EA] = (unsigned long) ka->_sa_handler; return; give_sigsegv: if (sig == TARGET_SIGSEGV) { ka->_sa_handler = TARGET_SIG_DFL; } force_sigsegv(sig); return; } long do_sigreturn(CPUNios2State *env) { trace_user_do_sigreturn(env, 0); fprintf(stderr, "do_sigreturn: not implemented\n"); return -TARGET_ENOSYS; } long do_rt_sigreturn(CPUNios2State *env) { /* Verify, can we follow the stack back */ abi_ulong frame_addr = env->regs[R_SP]; struct target_rt_sigframe *frame; sigset_t set; int rval; if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { goto badframe; } target_to_host_sigset(&set, &frame->uc.tuc_sigmask); do_sigprocmask(SIG_SETMASK, &set, NULL); if (rt_restore_ucontext(env, &frame->uc, &rval)) { goto badframe; } unlock_user_struct(frame, frame_addr, 0); return rval; badframe: unlock_user_struct(frame, frame_addr, 0); force_sig(TARGET_SIGSEGV); return 0; }