qemu-devel
[Top][All Lists]
Advanced

[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(&current->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(&current->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
 

Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]