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 <[EMAIL PROTECTED]>
  *
  *  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 ([EMAIL PROTECTED])
+ *    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

_______________________________________________
Qemu-devel mailing list
Qemu-devel@nongnu.org
http://lists.nongnu.org/mailman/listinfo/qemu-devel

Reply via email to