Commit ab513701 authored by Suresh Siddha's avatar Suresh Siddha Committed by Ingo Molnar

x86, xsave: reorganization of signal save/restore fpstate code layout

move 64bit routines that saves/restores fpstate in/from user stack from
signal_64.c to xsave.c

restore_i387_xstate() now handles the condition when user passes
NULL fpstate.

Other misc changes for prepartion of xsave/xrstor sigcontext support.
Signed-off-by: default avatarSuresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: default avatarH. Peter Anvin <hpa@zytor.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 3c1c7f10
...@@ -216,7 +216,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, ...@@ -216,7 +216,7 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
unsigned int *peax) unsigned int *peax)
{ {
unsigned int tmpflags, gs, oldgs, err = 0; unsigned int tmpflags, gs, oldgs, err = 0;
struct _fpstate_ia32 __user *buf; void __user *buf;
u32 tmp; u32 tmp;
/* Always make any pending restarted system calls return -EINTR */ /* Always make any pending restarted system calls return -EINTR */
...@@ -260,26 +260,12 @@ static int ia32_restore_sigcontext(struct pt_regs *regs, ...@@ -260,26 +260,12 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
err |= __get_user(tmp, &sc->fpstate); err |= __get_user(tmp, &sc->fpstate);
buf = compat_ptr(tmp); buf = compat_ptr(tmp);
if (buf) { err |= restore_i387_xstate_ia32(buf);
if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
goto badframe;
err |= restore_i387_ia32(buf);
} else {
struct task_struct *me = current;
if (used_math()) {
clear_fpu(me);
clear_used_math();
}
}
err |= __get_user(tmp, &sc->ax); err |= __get_user(tmp, &sc->ax);
*peax = tmp; *peax = tmp;
return err; return err;
badframe:
return 1;
} }
asmlinkage long sys32_sigreturn(struct pt_regs *regs) asmlinkage long sys32_sigreturn(struct pt_regs *regs)
...@@ -351,7 +337,7 @@ asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs) ...@@ -351,7 +337,7 @@ asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
*/ */
static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
struct _fpstate_ia32 __user *fpstate, void __user *fpstate,
struct pt_regs *regs, unsigned int mask) struct pt_regs *regs, unsigned int mask)
{ {
int tmp, err = 0; int tmp, err = 0;
...@@ -382,7 +368,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, ...@@ -382,7 +368,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
err |= __put_user((u32)regs->flags, &sc->flags); err |= __put_user((u32)regs->flags, &sc->flags);
err |= __put_user((u32)regs->sp, &sc->sp_at_signal); err |= __put_user((u32)regs->sp, &sc->sp_at_signal);
tmp = save_i387_ia32(fpstate); tmp = save_i387_xstate_ia32(fpstate);
if (tmp < 0) if (tmp < 0)
err = -EFAULT; err = -EFAULT;
else { else {
...@@ -404,7 +390,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, ...@@ -404,7 +390,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
*/ */
static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
size_t frame_size, size_t frame_size,
struct _fpstate_ia32 **fpstate) void **fpstate)
{ {
unsigned long sp; unsigned long sp;
...@@ -441,7 +427,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, ...@@ -441,7 +427,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
struct sigframe __user *frame; struct sigframe __user *frame;
void __user *restorer; void __user *restorer;
int err = 0; int err = 0;
struct _fpstate_ia32 __user *fpstate = NULL; void __user *fpstate = NULL;
/* copy_to_user optimizes that into a single 8 byte store */ /* copy_to_user optimizes that into a single 8 byte store */
static const struct { static const struct {
...@@ -529,7 +515,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -529,7 +515,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
struct rt_sigframe __user *frame; struct rt_sigframe __user *frame;
void __user *restorer; void __user *restorer;
int err = 0; int err = 0;
struct _fpstate_ia32 __user *fpstate = NULL; void __user *fpstate = NULL;
/* __copy_to_user optimizes that into a single 8 byte store */ /* __copy_to_user optimizes that into a single 8 byte store */
static const struct { static const struct {
......
...@@ -21,9 +21,10 @@ ...@@ -21,9 +21,10 @@
# include <asm/sigcontext32.h> # include <asm/sigcontext32.h>
# include <asm/user32.h> # include <asm/user32.h>
#else #else
# define save_i387_ia32 save_i387 # define save_i387_xstate_ia32 save_i387_xstate
# define restore_i387_ia32 restore_i387 # define restore_i387_xstate_ia32 restore_i387_xstate
# define _fpstate_ia32 _fpstate # define _fpstate_ia32 _fpstate
# define _xstate_ia32 _xstate
# define sig_xstate_ia32_size sig_xstate_size # define sig_xstate_ia32_size sig_xstate_size
# define user_i387_ia32_struct user_i387_struct # define user_i387_ia32_struct user_i387_struct
# define user32_fxsr_struct user_fxsr_struct # define user32_fxsr_struct user_fxsr_struct
...@@ -424,7 +425,6 @@ static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf) ...@@ -424,7 +425,6 @@ static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
struct task_struct *tsk = current; struct task_struct *tsk = current;
struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave; struct i387_fsave_struct *fp = &tsk->thread.xstate->fsave;
unlazy_fpu(tsk);
fp->status = fp->swd; fp->status = fp->swd;
if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct))) if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
return -1; return -1;
...@@ -438,8 +438,6 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) ...@@ -438,8 +438,6 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
struct user_i387_ia32_struct env; struct user_i387_ia32_struct env;
int err = 0; int err = 0;
unlazy_fpu(tsk);
convert_from_fxsr(&env, tsk); convert_from_fxsr(&env, tsk);
if (__copy_to_user(buf, &env, sizeof(env))) if (__copy_to_user(buf, &env, sizeof(env)))
return -1; return -1;
...@@ -455,10 +453,16 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) ...@@ -455,10 +453,16 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
return 1; return 1;
} }
int save_i387_ia32(struct _fpstate_ia32 __user *buf) int save_i387_xstate_ia32(void __user *buf)
{ {
struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
struct task_struct *tsk = current;
if (!used_math()) if (!used_math())
return 0; return 0;
if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
return -EACCES;
/* /*
* This will cause a "finit" to be triggered by the next * This will cause a "finit" to be triggered by the next
* attempted FPU operation by the 'current' process. * attempted FPU operation by the 'current' process.
...@@ -468,13 +472,15 @@ int save_i387_ia32(struct _fpstate_ia32 __user *buf) ...@@ -468,13 +472,15 @@ int save_i387_ia32(struct _fpstate_ia32 __user *buf)
if (!HAVE_HWFP) { if (!HAVE_HWFP) {
return fpregs_soft_get(current, NULL, return fpregs_soft_get(current, NULL,
0, sizeof(struct user_i387_ia32_struct), 0, sizeof(struct user_i387_ia32_struct),
NULL, buf) ? -1 : 1; NULL, fp) ? -1 : 1;
} }
unlazy_fpu(tsk);
if (cpu_has_fxsr) if (cpu_has_fxsr)
return save_i387_fxsave(buf); return save_i387_fxsave(fp);
else else
return save_i387_fsave(buf); return save_i387_fsave(fp);
} }
static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf) static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
...@@ -502,14 +508,26 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf) ...@@ -502,14 +508,26 @@ static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf)
return 0; return 0;
} }
int restore_i387_ia32(struct _fpstate_ia32 __user *buf) int restore_i387_xstate_ia32(void __user *buf)
{ {
int err; int err;
struct task_struct *tsk = current; struct task_struct *tsk = current;
struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
if (HAVE_HWFP) if (HAVE_HWFP)
clear_fpu(tsk); clear_fpu(tsk);
if (!buf) {
if (used_math()) {
clear_fpu(tsk);
clear_used_math();
}
return 0;
} else
if (!access_ok(VERIFY_READ, buf, sig_xstate_ia32_size))
return -EACCES;
if (!used_math()) { if (!used_math()) {
err = init_fpu(tsk); err = init_fpu(tsk);
if (err) if (err)
...@@ -518,13 +536,13 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf) ...@@ -518,13 +536,13 @@ int restore_i387_ia32(struct _fpstate_ia32 __user *buf)
if (HAVE_HWFP) { if (HAVE_HWFP) {
if (cpu_has_fxsr) if (cpu_has_fxsr)
err = restore_i387_fxsave(buf); err = restore_i387_fxsave(fp);
else else
err = restore_i387_fsave(buf); err = restore_i387_fsave(fp);
} else { } else {
err = fpregs_soft_set(current, NULL, err = fpregs_soft_set(current, NULL,
0, sizeof(struct user_i387_ia32_struct), 0, sizeof(struct user_i387_ia32_struct),
NULL, buf) != 0; NULL, fp) != 0;
} }
set_used_math(); set_used_math();
......
...@@ -159,28 +159,14 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, ...@@ -159,28 +159,14 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
} }
{ {
struct _fpstate __user *buf; void __user *buf;
err |= __get_user(buf, &sc->fpstate); err |= __get_user(buf, &sc->fpstate);
if (buf) { err |= restore_i387_xstate(buf);
if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
goto badframe;
err |= restore_i387(buf);
} else {
struct task_struct *me = current;
if (used_math()) {
clear_fpu(me);
clear_used_math();
}
}
} }
err |= __get_user(*pax, &sc->ax); err |= __get_user(*pax, &sc->ax);
return err; return err;
badframe:
return 1;
} }
asmlinkage unsigned long sys_sigreturn(unsigned long __unused) asmlinkage unsigned long sys_sigreturn(unsigned long __unused)
...@@ -262,7 +248,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused) ...@@ -262,7 +248,7 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused)
* Set up a signal frame. * Set up a signal frame.
*/ */
static int static int
setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate,
struct pt_regs *regs, unsigned long mask) struct pt_regs *regs, unsigned long mask)
{ {
int tmp, err = 0; int tmp, err = 0;
...@@ -289,7 +275,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, ...@@ -289,7 +275,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
err |= __put_user(regs->sp, &sc->sp_at_signal); err |= __put_user(regs->sp, &sc->sp_at_signal);
err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss); err |= __put_user(regs->ss, (unsigned int __user *)&sc->ss);
tmp = save_i387(fpstate); tmp = save_i387_xstate(fpstate);
if (tmp < 0) if (tmp < 0)
err = 1; err = 1;
else else
...@@ -307,7 +293,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate, ...@@ -307,7 +293,7 @@ setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
*/ */
static inline void __user * static inline void __user *
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
struct _fpstate **fpstate) void **fpstate)
{ {
unsigned long sp; unsigned long sp;
...@@ -356,7 +342,7 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, ...@@ -356,7 +342,7 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set,
void __user *restorer; void __user *restorer;
int err = 0; int err = 0;
int usig; int usig;
struct _fpstate __user *fpstate = NULL; void __user *fpstate = NULL;
frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
...@@ -434,7 +420,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -434,7 +420,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
void __user *restorer; void __user *restorer;
int err = 0; int err = 0;
int usig; int usig;
struct _fpstate __user *fpstate = NULL; void __user *fpstate = NULL;
frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
......
...@@ -53,69 +53,6 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, ...@@ -53,69 +53,6 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
return do_sigaltstack(uss, uoss, regs->sp); return do_sigaltstack(uss, uoss, regs->sp);
} }
/*
* Signal frame handlers.
*/
static inline int save_i387(struct _fpstate __user *buf)
{
struct task_struct *tsk = current;
int err = 0;
BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
sizeof(tsk->thread.xstate->fxsave));
if ((unsigned long)buf % 16)
printk("save_i387: bad fpstate %p\n", buf);
if (!used_math())
return 0;
clear_used_math(); /* trigger finit */
if (task_thread_info(tsk)->status & TS_USEDFPU) {
err = save_i387_checking((struct i387_fxsave_struct __user *)
buf);
if (err)
return err;
task_thread_info(tsk)->status &= ~TS_USEDFPU;
stts();
} else {
if (__copy_to_user(buf, &tsk->thread.xstate->fxsave,
sizeof(struct i387_fxsave_struct)))
return -1;
}
return 1;
}
/*
* This restores directly out of user space. Exceptions are handled.
*/
static inline int restore_i387(struct _fpstate __user *buf)
{
struct task_struct *tsk = current;
int err;
if (!used_math()) {
err = init_fpu(tsk);
if (err)
return err;
}
if (!(task_thread_info(current)->status & TS_USEDFPU)) {
clts();
task_thread_info(current)->status |= TS_USEDFPU;
}
err = restore_fpu_checking((__force struct i387_fxsave_struct *)buf);
if (unlikely(err)) {
/*
* Encountered an error while doing the restore from the
* user buffer, clear the fpu state.
*/
clear_fpu(tsk);
clear_used_math();
}
return err;
}
/* /*
* Do a signal return; undo the signal stack. * Do a signal return; undo the signal stack.
*/ */
...@@ -160,25 +97,11 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, ...@@ -160,25 +97,11 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
{ {
struct _fpstate __user * buf; struct _fpstate __user * buf;
err |= __get_user(buf, &sc->fpstate); err |= __get_user(buf, &sc->fpstate);
err |= restore_i387_xstate(buf);
if (buf) {
if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
goto badframe;
err |= restore_i387(buf);
} else {
struct task_struct *me = current;
if (used_math()) {
clear_fpu(me);
clear_used_math();
}
}
} }
err |= __get_user(*pax, &sc->ax); err |= __get_user(*pax, &sc->ax);
return err; return err;
badframe:
return 1;
} }
asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
...@@ -276,7 +199,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -276,7 +199,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
sigset_t *set, struct pt_regs * regs) sigset_t *set, struct pt_regs * regs)
{ {
struct rt_sigframe __user *frame; struct rt_sigframe __user *frame;
struct _fpstate __user *fp = NULL; void __user *fp = NULL;
int err = 0; int err = 0;
struct task_struct *me = current; struct task_struct *me = current;
...@@ -288,7 +211,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, ...@@ -288,7 +211,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate))) if (!access_ok(VERIFY_WRITE, fp, sizeof(struct _fpstate)))
goto give_sigsegv; goto give_sigsegv;
if (save_i387(fp) < 0) if (save_i387_xstate(fp) < 0)
err |= -1; err |= -1;
} else } else
frame = get_stack(ka, regs, sizeof(struct rt_sigframe)) - 8; frame = get_stack(ka, regs, sizeof(struct rt_sigframe)) - 8;
......
...@@ -12,6 +12,85 @@ ...@@ -12,6 +12,85 @@
*/ */
unsigned int pcntxt_hmask, pcntxt_lmask; unsigned int pcntxt_hmask, pcntxt_lmask;
#ifdef CONFIG_X86_64
/*
* Signal frame handlers.
*/
int save_i387_xstate(void __user *buf)
{
struct task_struct *tsk = current;
int err = 0;
if (!access_ok(VERIFY_WRITE, buf, sig_xstate_size))
return -EACCES;
BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
sizeof(tsk->thread.xstate->fxsave));
if ((unsigned long)buf % 16)
printk("save_i387_xstate: bad fpstate %p\n", buf);
if (!used_math())
return 0;
clear_used_math(); /* trigger finit */
if (task_thread_info(tsk)->status & TS_USEDFPU) {
err = save_i387_checking((struct i387_fxsave_struct __user *)
buf);
if (err)
return err;
task_thread_info(tsk)->status &= ~TS_USEDFPU;
stts();
} else {
if (__copy_to_user(buf, &tsk->thread.xstate->fxsave,
xstate_size))
return -1;
}
return 1;
}
/*
* This restores directly out of user space. Exceptions are handled.
*/
int restore_i387_xstate(void __user *buf)
{
struct task_struct *tsk = current;
int err;
if (!buf) {
if (used_math()) {
clear_fpu(tsk);
clear_used_math();
}
return 0;
} else
if (!access_ok(VERIFY_READ, buf, sig_xstate_size))
return -EACCES;
if (!used_math()) {
err = init_fpu(tsk);
if (err)
return err;
}
if (!(task_thread_info(current)->status & TS_USEDFPU)) {
clts();
task_thread_info(current)->status |= TS_USEDFPU;
}
err = fxrstor_checking((__force struct i387_fxsave_struct *)buf);
if (unlikely(err)) {
/*
* Encountered an error while doing the restore from the
* user buffer, clear the fpu state.
*/
clear_fpu(tsk);
clear_used_math();
}
return err;
}
#endif
/* /*
* Represents init state for the supported extended state. * Represents init state for the supported extended state.
*/ */
......
...@@ -34,8 +34,9 @@ extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set; ...@@ -34,8 +34,9 @@ extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set;
#ifdef CONFIG_IA32_EMULATION #ifdef CONFIG_IA32_EMULATION
extern unsigned int sig_xstate_ia32_size; extern unsigned int sig_xstate_ia32_size;
struct _fpstate_ia32; struct _fpstate_ia32;
extern int save_i387_ia32(struct _fpstate_ia32 __user *buf); struct _xstate_ia32;
extern int restore_i387_ia32(struct _fpstate_ia32 __user *buf); extern int save_i387_xstate_ia32(void __user *buf);
extern int restore_i387_xstate_ia32(void __user *buf);
#endif #endif
#define X87_FSW_ES (1 << 7) /* Exception Summary */ #define X87_FSW_ES (1 << 7) /* Exception Summary */
...@@ -249,13 +250,13 @@ static inline void __save_init_fpu(struct task_struct *tsk) ...@@ -249,13 +250,13 @@ static inline void __save_init_fpu(struct task_struct *tsk)
task_thread_info(tsk)->status &= ~TS_USEDFPU; task_thread_info(tsk)->status &= ~TS_USEDFPU;
} }
#endif /* CONFIG_X86_64 */
/* /*
* Signal frame handlers... * Signal frame handlers...
*/ */
extern int save_i387(struct _fpstate __user *buf); extern int save_i387_xstate(void __user *buf);
extern int restore_i387(struct _fpstate __user *buf); extern int restore_i387_xstate(void __user *buf);
#endif /* CONFIG_X86_64 */
static inline void __unlazy_fpu(struct task_struct *tsk) static inline void __unlazy_fpu(struct task_struct *tsk)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment