#DPATCHLEVEL=1 --- # linux-user/signal.c | 371 ++++++++++++++++++++++++++++++++++++++++++++++++++++ # 1 file changed, 371 insertions(+) # Index: qemu/linux-user/signal.c =================================================================== --- qemu.orig/linux-user/signal.c 2007-06-13 11:51:54.000000000 +0100 +++ qemu/linux-user/signal.c 2007-06-13 11:51:54.000000000 +0100 @@ -2,6 +2,7 @@ * Emulation of Linux signals * * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2005 Josh Triplett * * 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 @@ -16,6 +17,12 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Various portions adapted from the Linux kernel: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Derived from "arch/i386/kernel/signal.c" + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson */ #include #include @@ -1964,6 +1971,370 @@ long do_rt_sigreturn(CPUState *env) return -ENOSYS; } +#elif defined(TARGET_PPC) +/* Adapted from the Linux kernel: + * arch/ppc/kernel/signal.c + * include/asm-ppc/elf.h + * include/asm-ppc/ptrace.h + * include/asm-ppc/sigcontext.h + * include/asm-ppc/ucontext.h + */ + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * a sigcontext struct + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + */ + +#define TARGET_ELF_NGREG 48 /* includes nip, msr, lr, etc. */ +#define TARGET_ELF_NFPREG 33 /* includes fpscr */ +#define TARGET_ELF_NVRREG 33 /* includes vscr */ + +/* General registers */ +typedef unsigned long target_elf_greg_t; +typedef target_elf_greg_t target_elf_gregset_t[TARGET_ELF_NGREG]; + +/* Floating point registers */ +typedef double target_elf_fpreg_t; +typedef target_elf_fpreg_t target_elf_fpregset_t[TARGET_ELF_NFPREG]; + +/* Altivec registers */ +/* FIXME: Altivec not supported yet. */ +/* typedef __vector128 elf_vrreg_t; */ +typedef uint64_t target_elf_vrreg_t[2]; +typedef target_elf_vrreg_t target_elf_vrregset_t[TARGET_ELF_NVRREG]; + +struct target_mcontext { + target_elf_gregset_t mc_gregs; + target_elf_fpregset_t mc_fregs; + /* The kernel calls this mc_pad, but does #define tramp mc_pad */ + target_ulong tramp[2]; + target_elf_vrregset_t mc_vregs __attribute__((__aligned__(16))); +}; + +struct target_sigregs { + struct target_mcontext mctx; /* all the register values */ + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; + +struct target_sigcontext { + target_ulong _unused[4]; + uint32_t signal; + target_ulong handler; + target_ulong oldmask; + struct target_pt_regs *regs; +}; + +#define __SIGNAL_FRAMESIZE 64 + +static int +save_user_regs(CPUState *env, struct target_mcontext *frame, int sigret) +{ + /* save general and floating-point registers */ +#if 0 /* FIXME: handle floating-point, Altivec, SPE */ + CHECK_FULL_REGS(regs); + preempt_disable(); + if (regs->msr & MSR_FP) + giveup_fpu(current); +#ifdef CONFIG_ALTIVEC + if (current->thread.used_vr && (regs->msr & MSR_VEC)) + giveup_altivec(current); +#endif /* CONFIG_ALTIVEC */ +#ifdef CONFIG_SPE + if (current->thread.used_spe && (regs->msr & MSR_SPE)) + giveup_spe(current); +#endif /* CONFIG_ALTIVEC */ + preempt_enable(); +#endif /* 0 */ + + /* Note: this needs to be in the same order as target_pt_regs */ + if(!memcpy(&frame->mc_gregs, env->gpr, + 32*sizeof(target_elf_greg_t)) + || __put_user(env->nip, &frame->mc_gregs[32]) + || __put_user(do_load_msr(env), &frame->mc_gregs[33]) + /* FIXME: || __put_user(orig_gpr3, &frame->mc_gregs[34]) */ + || __put_user(env->ctr, &frame->mc_gregs[35]) + || __put_user(env->lr, &frame->mc_gregs[36]) + || __put_user(do_load_xer(env), &frame->mc_gregs[37]) + || __put_user(do_load_cr(env), &frame->mc_gregs[38]) + || __put_user(env->spr[SPR_MQ], &frame->mc_gregs[39]) + /* FIXME: || __put_user(trap, &frame->mc_gregs[40]) */ + || __put_user(env->spr[SPR_DAR], &frame->mc_gregs[41]) + || __put_user(env->spr[SPR_DSISR], &frame->mc_gregs[42]) + /* FIXME: || __put_user(result, &frame->mc_gregs[43]) */) + return 1; + + if(!memcpy(&frame->mc_fregs, env->fpr, + 32*sizeof(target_elf_fpreg_t)) + || __put_user(do_load_fpscr(env), &frame->mc_fregs[32])) + + do_store_fpscr(env, 0, 0xFF); /* turn off all fp exceptions */ + +#if 0 /* FIXME: handle Altivec, SPE */ +#ifdef CONFIG_ALTIVEC + /* save altivec registers */ + if (current->thread.used_vr) { + if (!memcpy(&frame->mc_vregs, current->thread.vr, + ELF_NVRREG * sizeof(vector128))) + return 1; + /* set MSR_VEC in the saved MSR value to indicate that + frame->mc_vregs contains valid data */ + if (__put_user(regs->msr | MSR_VEC, &frame->mc_gregs[PT_MSR])) + return 1; + } + /* else assert((regs->msr & MSR_VEC) == 0) */ + + /* We always copy to/from vrsave, it's 0 if we don't have or don't + * use altivec. Since VSCR only contains 32 bits saved in the least + * significant bits of a vector, we "cheat" and stuff VRSAVE in the + * most significant bits of that same vector. --BenH + */ + if (__put_user(current->thread.vrsave, (u32 __user *)&frame->mc_vregs[32])) + return 1; +#endif /* CONFIG_ALTIVEC */ + +#ifdef CONFIG_SPE + /* save spe registers */ + if (current->thread.used_spe) { + if (!memcpy(&frame->mc_vregs, current->thread.evr, + ELF_NEVRREG * sizeof(u32))) + return 1; + /* set MSR_SPE in the saved MSR value to indicate that + frame->mc_vregs contains valid data */ + if (__put_user(regs->msr | MSR_SPE, &frame->mc_gregs[PT_MSR])) + return 1; + } + /* else assert((regs->msr & MSR_SPE) == 0) */ + + /* We always copy to/from spefscr */ + if (__put_user(current->thread.spefscr, (u32 *)&frame->mc_vregs + ELF_NEVRREG)) + return 1; +#endif /* CONFIG_SPE */ +#endif /* 0 */ + + if (sigret) { + /* Set up the sigreturn trampoline: li r0,sigret; sc */ + if (__put_user(0x38000000UL + sigret, &frame->tramp[0]) + || __put_user(0x44000002UL, &frame->tramp[1])) + return 1; +#if 0 + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); +#endif + } + + return 0; +} + +static int +restore_user_regs(CPUState *env, struct target_mcontext *sr, int sig) +{ + target_ulong save_r2 = 0; + target_ulong saved_xer; + target_ulong saved_cr; + double saved_fpscr; + +#if 0 /* FIXME: handle Altivec, SPE */ +#if defined(CONFIG_ALTIVEC) || defined(CONFIG_SPE) + unsigned long msr; +#endif +#endif /* 0 */ + + /* backup/restore the TLS as we don't want it to be modified */ + if (!sig) + save_r2 = env->gpr[2]; + + /* Copy all registers except MSR */ + /* Note: this needs to be in the same order as target_pt_regs */ + if(!memcpy(env->gpr, &sr->mc_gregs, + 32*sizeof(target_elf_greg_t)) + || __get_user(env->nip, &sr->mc_gregs[32]) + /* FIXME: || __get_user(orig_gpr3, &sr->mc_gregs[34]) */ + || __get_user(env->ctr, &sr->mc_gregs[35]) + || __get_user(env->lr, &sr->mc_gregs[36]) + || __get_user(saved_xer, &sr->mc_gregs[37]) + || __get_user(saved_cr, &sr->mc_gregs[38]) + || __get_user(env->spr[SPR_MQ], &sr->mc_gregs[39]) + /* FIXME: || __get_user(trap, &sr->mc_gregs[40]) */ + || __get_user(env->spr[SPR_DAR], &sr->mc_gregs[41]) + || __get_user(env->spr[SPR_DSISR], &sr->mc_gregs[42]) + /* FIXME: || __get_user(result, &sr->mc_gregs[43]) */) + return 1; + do_store_xer(env, saved_xer); + do_store_cr(env, saved_cr, 0xFF); + + if (!sig) + env->gpr[2] = save_r2; + + /* The kernel delays restoring the floating-point registers until the + * thread uses floating-point again. For simplicity, just restore the + * registers now. */ + if(!memcpy(env->fpr, &sr->mc_fregs, + 32*sizeof(target_elf_fpreg_t)) + || __get_user(saved_fpscr, &sr->mc_fregs[32])) + return 1; + do_store_fpscr(env, saved_fpscr, 0xFF); + +#if 0 /* FIXME: handle Altivec, SPE */ +#ifdef CONFIG_ALTIVEC + /* force the process to reload the altivec registers from + current->thread when it next does altivec instructions */ + regs->msr &= ~MSR_VEC; + if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_VEC) != 0) { + /* restore altivec registers from the stack */ + if (!memcpy(current->thread.vr, &sr->mc_vregs, + sizeof(sr->mc_vregs))) + return 1; + } else if (current->thread.used_vr) + memset(¤t->thread.vr, 0, ELF_NVRREG * sizeof(vector128)); + + /* Always get VRSAVE back */ + if (__get_user(current->thread.vrsave, (u32 __user *)&sr->mc_vregs[32])) + return 1; +#endif /* CONFIG_ALTIVEC */ + +#ifdef CONFIG_SPE + /* force the process to reload the spe registers from + current->thread when it next does spe instructions */ + regs->msr &= ~MSR_SPE; + if (!__get_user(msr, &sr->mc_gregs[PT_MSR]) && (msr & MSR_SPE) != 0) { + /* restore spe registers from the stack */ + if (!memcpy(current->thread.evr, &sr->mc_vregs, + ELF_NEVRREG * sizeof(u32))) + return 1; + } else if (current->thread.used_spe) + memset(¤t->thread.evr, 0, ELF_NEVRREG * sizeof(u32)); + + /* Always get SPEFSCR back */ + if (__get_user(current->thread.spefscr, (u32 *)&sr->mc_vregs + ELF_NEVRREG)) + return 1; +#endif /* CONFIG_SPE */ +#endif /* 0 */ + +#if 0 /* FIXME: handle floating-point, Altivec, SPE */ +#ifndef CONFIG_SMP + preempt_disable(); + if (last_task_used_math == current) + last_task_used_math = NULL; + if (last_task_used_altivec == current) + last_task_used_altivec = NULL; + if (last_task_used_spe == current) + last_task_used_spe = NULL; + preempt_enable(); +#endif +#endif /* 0 */ + return 0; +} + +static void setup_frame(int sig, struct emulated_sigaction *ka, + target_sigset_t *oldset, CPUState *env) +{ + struct target_sigcontext *sc; + struct target_sigregs *frame; + target_ulong origsp = env->gpr[1]; + target_ulong newsp = origsp; + + /* Set up Signal Frame */ + newsp -= sizeof(struct target_sigregs); + frame = (struct target_sigregs *) newsp; + + /* Put a sigcontext on the stack */ + newsp -= sizeof(*sc); + sc = (struct target_sigcontext *) newsp; + + /* create a stack frame for the caller of the handler */ + newsp -= __SIGNAL_FRAMESIZE; + + if (!access_ok(VERIFY_WRITE, (void *) newsp, origsp - newsp)) + goto badframe; + +#if TARGET_NSIG != 64 +#error "Please adjust handle_signal()" +#endif + if (__put_user((target_ulong) ka->sa._sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) + || __put_user(oldset->sig[1], &sc->_unused[3]) + || __put_user(frame, (target_ulong *)&sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + + if (save_user_regs(env, &frame->mctx, TARGET_NR_sigreturn)) + goto badframe; + + if (put_user(env->gpr[1], (unsigned long *)newsp)) + goto badframe; + env->gpr[1] = newsp; + env->gpr[3] = sig; + env->gpr[4] = (unsigned long) sc; + env->nip = (unsigned long) ka->sa._sa_handler; + env->lr = (unsigned long) frame->mctx.tramp; + /* FIXME: env->trap = 0; */ + + return; + +badframe: +#ifdef DEBUG_SIGNAL + fprintf(stderr, + "badframe in handle_signal, frame=%p newsp=%lx\n", + frame, newsp); +#endif + force_sig(TARGET_SIGSEGV); +} + +static void setup_rt_frame(int sig, struct emulated_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUState *env) +{ + fprintf(stderr, "setup_rt_frame: not implemented\n"); +} + +long do_sigreturn(CPUState *env) +{ + struct target_sigcontext *sc; + struct target_sigcontext sigctx; + struct target_mcontext *sr; + target_sigset_t set; + sigset_t host_set; + + /* Always make any pending restarted system calls return -EINTR */ +#if 0 /* FIXME */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; +#endif + + sc = (struct target_sigcontext *)(env->gpr[1] + __SIGNAL_FRAMESIZE); + if (!memcpy(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + set.sig[0] = sigctx.oldmask; + set.sig[1] = sigctx._unused[3]; + target_to_host_sigset_internal(&host_set, &set); + sigprocmask(SIG_SETMASK, &host_set, NULL); + + sr = (struct target_mcontext *) tswapl((target_ulong)sigctx.regs); + if (!access_ok(VERIFY_READ, sr, sizeof(*sr)) + || restore_user_regs(env, sr, 1)) + goto badframe; + + return 0; + +badframe: + force_sig(TARGET_SIGSEGV); + return 0; +} + +long do_rt_sigreturn(CPUState *env) +{ + fprintf(stderr, "do_rt_sigreturn: not implemented\n"); + return -ENOSYS; +} + #else static void setup_frame(int sig, struct emulated_sigaction *ka,