[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] Add signal-handling support for PowerPC user-mode emulation
From: |
Josh Triplett |
Subject: |
[Qemu-devel] Add signal-handling support for PowerPC user-mode emulation |
Date: |
Mon, 07 Nov 2005 15:01:40 -0800 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.12) Gecko/20051007 Debian/1.7.12-1 |
The attached patch adds support for signal handling to the PowerPC
user-mode emulation.
With this patch, I can successfully run cross-compiled PowerPC programs
that make use of signals, such as the PSAS flight computer software.
Note, however, that the patch does not handle real-time signals or
Altivec registers.
Some code from this patch was taken from the Linux kernel, heavily
adapted for qemu.
- Josh Triplett
diff -Naur qemu-0.7.2.orig/linux-user/signal.c qemu-0.7.2/linux-user/signal.c
--- qemu-0.7.2.orig/linux-user/signal.c 2005-09-04 10:11:31.000000000 -0700
+++ qemu-0.7.2/linux-user/signal.c 2005-11-06 22:01:23.000000000 -0800
@@ -2,6 +2,7 @@
* Emulation of Linux signals
*
* Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2005 Josh Triplett <address@hidden>
*
* 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 (address@hidden)
+ * 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 <stdlib.h>
#include <stdio.h>
@@ -1614,6 +1621,369 @@
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(__copy_to_user(&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(__copy_to_user(&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 (__copy_to_user(&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 (__copy_to_user(&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(__copy_from_user(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(__copy_from_user(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 (__copy_from_user(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 (__copy_from_user(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 (copy_from_user(&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
signature.asc
Description: OpenPGP digital signature
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] Add signal-handling support for PowerPC user-mode emulation,
Josh Triplett <=