uml: floating point signal delivery fixes
Handle floating point state in across signals correctly. UML/i386 needs to
know whether the host does PTRACE_[GS]ETFPXREGS, so an arch_init_registers
hook is added, which on x86_64 does nothing.
UML doesn't save and restore floating point registers on kernel entry and
exit, so they need to be copied between the host process and the sigcontext.
save_fpx_registers and restore_fpx_registers are added for this purpose.
save_fp_registers and restore_fp_registers already exist.
There was a bunch of floating point state conversion code in
arch/um/sys-i386/ptrace.c which isn't needed there, but is needed in signal.c,
so it is moved over.
The i386 code now distinguishes between fp and fpx state and handles them
correctly. The x86_64 code just needs to copy state as-is between the host
process and the stack. There are also some fixes there to pass the correct
address of the floating point state around.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
diff --git a/arch/um/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c
index c98dd7f..a8e5fd7 100644
--- a/arch/um/sys-x86_64/signal.c
+++ b/arch/um/sys-x86_64/signal.c
@@ -42,8 +42,10 @@
}
static int copy_sc_from_user(struct pt_regs *regs,
- struct sigcontext __user *from)
+ struct sigcontext __user *from,
+ struct _fpstate __user *fpp)
{
+ struct user_i387_struct fp;
int err = 0;
#define GETREG(regs, regno, sc, regname) \
@@ -69,10 +71,25 @@
err |= GETREG(regs, RIP, from, rip);
err |= GETREG(regs, EFLAGS, from, eflags);
err |= GETREG(regs, CS, from, cs);
+ if (err)
+ return 1;
#undef GETREG
- return err;
+ err = copy_from_user(&fp, fpp, sizeof(struct user_i387_struct));
+ if (err)
+ return 1;
+
+ err = restore_fp_registers(userspace_pid[current_thread->cpu],
+ (unsigned long *) &fp);
+ if (err < 0) {
+ printk(KERN_ERR "copy_sc_from_user - "
+ "restore_fp_registers failed, errno = %d\n",
+ -err);
+ return 1;
+ }
+
+ return 0;
}
static int copy_sc_to_user(struct sigcontext __user *to,
@@ -80,6 +97,7 @@
unsigned long mask, unsigned long sp)
{
struct faultinfo * fi = ¤t->thread.arch.faultinfo;
+ struct user_i387_struct fp;
int err = 0;
err |= __put_user(0, &to->gs);
@@ -120,6 +138,19 @@
#undef PUTREG
err |= __put_user(mask, &to->oldmask);
+ if (err)
+ return 1;
+
+ err = save_fp_registers(userspace_pid[current_thread->cpu],
+ (unsigned long *) &fp);
+ if (err < 0) {
+ printk(KERN_ERR "copy_sc_from_user - restore_fp_registers "
+ "failed, errno = %d\n", -err);
+ return 1;
+ }
+
+ if (copy_to_user(to_fp, &fp, sizeof(struct user_i387_struct)))
+ return 1;
return(err);
}
@@ -129,6 +160,7 @@
char __user *pretcode;
struct ucontext uc;
struct siginfo info;
+ struct _fpstate fpstate;
};
#define round_down(m, n) (((m) / (n)) * (n))
@@ -138,7 +170,6 @@
siginfo_t *info, sigset_t *set)
{
struct rt_sigframe __user *frame;
- struct _fpstate __user *fp = NULL;
unsigned long save_sp = PT_REGS_RSP(regs);
int err = 0;
struct task_struct *me = current;
@@ -148,13 +179,6 @@
/* Subtract 128 for a red zone and 8 for proper alignment */
frame = (struct rt_sigframe __user *) ((unsigned long) frame - 128 - 8);
- if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate)))
- goto out;
-
-#if 0 /* XXX */
- if (save_i387(fp) < 0)
- err |= -1;
-#endif
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
goto out;
@@ -181,9 +205,9 @@
err |= __put_user(sas_ss_flags(save_sp),
&frame->uc.uc_stack.ss_flags);
err |= __put_user(me->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= copy_sc_to_user(&frame->uc.uc_mcontext, fp, regs, set->sig[0],
- save_sp);
- err |= __put_user(fp, &frame->uc.uc_mcontext.fpstate);
+ err |= copy_sc_to_user(&frame->uc.uc_mcontext, &frame->fpstate, regs,
+ set->sig[0], save_sp);
+ err |= __put_user(&frame->fpstate, &frame->uc.uc_mcontext.fpstate);
if (sizeof(*set) == 16) {
__put_user(set->sig[0], &frame->uc.uc_sigmask.sig[0]);
__put_user(set->sig[1], &frame->uc.uc_sigmask.sig[1]);
@@ -246,7 +270,8 @@
recalc_sigpending();
spin_unlock_irq(¤t->sighand->siglock);
- if (copy_sc_from_user(¤t->thread.regs, &uc->uc_mcontext))
+ if (copy_sc_from_user(¤t->thread.regs, &uc->uc_mcontext,
+ &frame->fpstate))
goto segfault;
/* Avoid ERESTART handling */