Package: qemu
Version: 0.7.0-4
Tags: patch upstream

The attached patch adds signal-handling support for the PowerPC target.

This patch applies to 0.7.0; it needs some minor changes to apply to
0.7.2 upstream, due to some recent changes to the register load/store
helper functions.

- Josh Triplett

diff -Naur qemu-0.7.0.orig/linux-user/signal.c qemu-0.7.0/linux-user/signal.c
--- qemu-0.7.0.orig/linux-user/signal.c	2005-10-21 19:55:59.000000000 -0700
+++ qemu-0.7.0/linux-user/signal.c	2005-10-24 02:21:46.000000000 -0700
@@ -1612,6 +1612,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(_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(_load_xer(env), &frame->mc_gregs[37])
+	   || __put_user(_load_cr(env), &frame->mc_gregs[38])
+	   || __put_user(env->spr[MQ], &frame->mc_gregs[39])
+	   /* FIXME: || __put_user(trap, &frame->mc_gregs[40]) */
+	   || __put_user(env->spr[DAR], &frame->mc_gregs[41])
+	   || __put_user(env->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(_load_fpscr(env), &frame->mc_fregs[32]))
+
+	_store_fpscr(env, 0);	/* 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[MQ], &sr->mc_gregs[39])
+	   /* FIXME: || __get_user(trap, &sr->mc_gregs[40]) */
+	   || __get_user(env->spr[DAR], &sr->mc_gregs[41])
+	   || __get_user(env->spr[DSISR], &sr->mc_gregs[42])
+	   /* FIXME: || __get_user(result, &sr->mc_gregs[43]) */)
+		return 1;
+	_store_xer(env, saved_xer);
+	_store_cr(env, saved_cr);
+
+	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;
+	_store_fpscr(env, saved_fpscr);
+
+#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((struct target_pt_regs *)frame, &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(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
 
diff -Naur qemu-0.7.0.orig/target-ppc/cpu.h qemu-0.7.0/target-ppc/cpu.h
--- qemu-0.7.0.orig/target-ppc/cpu.h	2005-04-27 13:52:05.000000000 -0700
+++ qemu-0.7.0/target-ppc/cpu.h	2005-10-23 20:46:57.000000000 -0700
@@ -182,6 +182,10 @@
 void _store_xer (CPUPPCState *env, uint32_t value);
 uint32_t _load_msr (CPUPPCState *env);
 void _store_msr (CPUPPCState *env, uint32_t value);
+uint32_t _load_cr (CPUPPCState *env);
+void _store_cr (CPUPPCState *env, uint32_t value);
+double _load_fpscr(CPUPPCState *env);
+void _store_fpscr(CPUPPCState *env, double value);
 
 int cpu_ppc_register (CPUPPCState *env, uint32_t pvr);
 
diff -Naur qemu-0.7.0.orig/target-ppc/helper.c qemu-0.7.0/target-ppc/helper.c
--- qemu-0.7.0.orig/target-ppc/helper.c	2005-04-27 13:52:05.000000000 -0700
+++ qemu-0.7.0/target-ppc/helper.c	2005-10-23 20:47:41.000000000 -0700
@@ -654,6 +654,79 @@
     /* XXX: should enter PM state if msr_pow has been set */
 }
 
+uint32_t _load_cr(CPUState *env)
+{
+    return (env->crf[0] << 28) |
+           (env->crf[1] << 24) |
+           (env->crf[2] << 20) |
+           (env->crf[3] << 16) |
+           (env->crf[4] << 12) |
+           (env->crf[5] << 8) |
+           (env->crf[6] << 4) |
+           (env->crf[7] << 0);
+}
+
+void _store_cr(CPUState *env, uint32_t value)
+{
+    env->crf[0] = (value >> 28) & 0xF;
+    env->crf[1] = (value >> 24) & 0xF;
+    env->crf[2] = (value >> 20) & 0xF;
+    env->crf[3] = (value >> 16) & 0xF;
+    env->crf[4] = (value >> 12) & 0xF;
+    env->crf[5] = (value >>  8) & 0xF;
+    env->crf[6] = (value >>  4) & 0xF;
+    env->crf[7] = (value >>  0) & 0xF;
+}
+
+double _load_fpscr(CPUState *env)
+{
+    /* The 32 MSB of the target fpr are undefined.
+     * They'll be zero...
+     */
+    union {
+        double d;
+        struct {
+            uint32_t u[2];
+        } s;
+    } u;
+    int i;
+
+#ifdef WORDS_BIGENDIAN
+#define WORD0 0
+#define WORD1 1
+#else
+#define WORD0 1
+#define WORD1 0
+#endif
+    u.s.u[WORD0] = 0;
+    u.s.u[WORD1] = 0;
+    for (i = 0; i < 8; i++)
+        u.s.u[WORD1] |= env->fpscr[i] << (4 * i);
+    return u.d;
+}
+
+void _store_fpscr(CPUState *env, double value)
+{
+    union {
+        double d;
+        struct {
+            uint32_t u[2];
+        } s;
+    } u;
+    int i;
+
+#ifdef WORDS_BIGENDIAN
+#define WORD0 0
+#define WORD1 1
+#else
+#define WORD0 1
+#define WORD1 0
+#endif
+    u.d = value;
+    for (i = 0; i < 8; i++)
+        env->fpscr[i] = (u.s.u[WORD1] >> (4 * i)) & 0xF;
+}
+
 #if defined (CONFIG_USER_ONLY)
 void do_interrupt (CPUState *env)
 {

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to