ARM: 6051/1: VFP: preserve the HW context when calling signal handlers

From: Imre Deak <imre.deak@nokia.com>

Signal handlers can use floating point, so prevent them to corrupt
the main thread's VFP context. So far there were two signal stack
frame formats defined based on the VFP implementation, but the user
struct used for ptrace covers all posibilities, so use it for the
signal stack too.

Introduce also a new user struct for VFP exception registers. In
this too fields not relevant to the current VFP architecture are
ignored.

Support to save / restore the exception registers was added by
Will Deacon.

Signed-off-by: Imre Deak <imre.deak@nokia.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
Imre Deak
2010-04-11 15:58:27 +01:00
committed by Russell King
parent 5c5cac6385
commit 82c6f5a5b3
3 changed files with 111 additions and 17 deletions

View File

@@ -18,6 +18,7 @@
#include <asm/cacheflush.h>
#include <asm/ucontext.h>
#include <asm/unistd.h>
#include <asm/vfp.h>
#include "ptrace.h"
#include "signal.h"
@@ -175,6 +176,90 @@ static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
#endif
#ifdef CONFIG_VFP
static int preserve_vfp_context(struct vfp_sigframe __user *frame)
{
struct thread_info *thread = current_thread_info();
struct vfp_hard_struct *h = &thread->vfpstate.hard;
const unsigned long magic = VFP_MAGIC;
const unsigned long size = VFP_STORAGE_SIZE;
int err = 0;
vfp_sync_hwstate(thread);
__put_user_error(magic, &frame->magic, err);
__put_user_error(size, &frame->size, err);
/*
* Copy the floating point registers. There can be unused
* registers see asm/hwcap.h for details.
*/
err |= __copy_to_user(&frame->ufp.fpregs, &h->fpregs,
sizeof(h->fpregs));
/*
* Copy the status and control register.
*/
__put_user_error(h->fpscr, &frame->ufp.fpscr, err);
/*
* Copy the exception registers.
*/
__put_user_error(h->fpexc, &frame->ufp_exc.fpexc, err);
__put_user_error(h->fpinst, &frame->ufp_exc.fpinst, err);
__put_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err);
return err ? -EFAULT : 0;
}
static int restore_vfp_context(struct vfp_sigframe __user *frame)
{
struct thread_info *thread = current_thread_info();
struct vfp_hard_struct *h = &thread->vfpstate.hard;
unsigned long magic;
unsigned long size;
unsigned long fpexc;
int err = 0;
__get_user_error(magic, &frame->magic, err);
__get_user_error(size, &frame->size, err);
if (err)
return -EFAULT;
if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE)
return -EINVAL;
/*
* Copy the floating point registers. There can be unused
* registers see asm/hwcap.h for details.
*/
err |= __copy_from_user(&h->fpregs, &frame->ufp.fpregs,
sizeof(h->fpregs));
/*
* Copy the status and control register.
*/
__get_user_error(h->fpscr, &frame->ufp.fpscr, err);
/*
* Sanitise and restore the exception registers.
*/
__get_user_error(fpexc, &frame->ufp_exc.fpexc, err);
/* Ensure the VFP is enabled. */
fpexc |= FPEXC_EN;
/* Ensure FPINST2 is invalid and the exception flag is cleared. */
fpexc &= ~(FPEXC_EX | FPEXC_FP2V);
h->fpexc = fpexc;
__get_user_error(h->fpinst, &frame->ufp_exc.fpinst, err);
__get_user_error(h->fpinst2, &frame->ufp_exc.fpinst2, err);
if (!err)
vfp_flush_hwstate(thread);
return err ? -EFAULT : 0;
}
#endif
/*
* Do a signal return; undo the signal stack. These are aligned to 64-bit.
*/
@@ -233,8 +318,8 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
err |= restore_iwmmxt_context(&aux->iwmmxt);
#endif
#ifdef CONFIG_VFP
// if (err == 0)
// err |= vfp_restore_state(&sf->aux.vfp);
if (err == 0)
err |= restore_vfp_context(&aux->vfp);
#endif
return err;
@@ -348,8 +433,8 @@ setup_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set)
err |= preserve_iwmmxt_context(&aux->iwmmxt);
#endif
#ifdef CONFIG_VFP
// if (err == 0)
// err |= vfp_save_state(&sf->aux.vfp);
if (err == 0)
err |= preserve_vfp_context(&aux->vfp);
#endif
__put_user_error(0, &aux->end_magic, err);