15598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include <linux/kernel.h>
25598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include <linux/types.h>
35598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include <linux/thread_info.h>
45598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include <linux/uaccess.h>
59ff03b392fa34f6d549fbb56bf05d8a0483aa818Ben Hutchings#include <linux/errno.h>
65598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
75598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include <asm/sigcontext.h>
85598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include <asm/fpumacro.h>
95598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include <asm/ptrace.h>
10d550bbd40c0e10aefa05103dadbe0ae42e683707David Howells#include <asm/switch_to.h>
115598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
125598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller#include "sigutil.h"
135598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
145598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Millerint save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
155598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller{
165598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	unsigned long *fpregs = current_thread_info()->fpregs;
175598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	unsigned long fprs;
185598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	int err = 0;
195598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
205598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	fprs = current_thread_info()->fpsaved[0];
215598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	if (fprs & FPRS_DL)
225598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= copy_to_user(&fpu->si_float_regs[0], fpregs,
235598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller				    (sizeof(unsigned int) * 32));
245598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	if (fprs & FPRS_DU)
255598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= copy_to_user(&fpu->si_float_regs[32], fpregs+16,
265598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller				    (sizeof(unsigned int) * 32));
275598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	err |= __put_user(current_thread_info()->xfsr[0], &fpu->si_fsr);
285598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	err |= __put_user(current_thread_info()->gsr[0], &fpu->si_gsr);
295598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	err |= __put_user(fprs, &fpu->si_fprs);
305598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
315598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	return err;
325598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller}
335598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
345598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Millerint restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t __user *fpu)
355598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller{
365598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	unsigned long *fpregs = current_thread_info()->fpregs;
375598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	unsigned long fprs;
385598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	int err;
395598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
405598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	err = __get_user(fprs, &fpu->si_fprs);
415598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	fprs_write(0);
425598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	regs->tstate &= ~TSTATE_PEF;
435598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	if (fprs & FPRS_DL)
445598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= copy_from_user(fpregs, &fpu->si_float_regs[0],
455598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		       	       (sizeof(unsigned int) * 32));
465598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	if (fprs & FPRS_DU)
475598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= copy_from_user(fpregs+16, &fpu->si_float_regs[32],
485598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		       	       (sizeof(unsigned int) * 32));
495598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	err |= __get_user(current_thread_info()->xfsr[0], &fpu->si_fsr);
505598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	err |= __get_user(current_thread_info()->gsr[0], &fpu->si_gsr);
515598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	current_thread_info()->fpsaved[0] |= fprs;
525598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	return err;
535598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller}
545598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
555598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Millerint save_rwin_state(int wsaved, __siginfo_rwin_t __user *rwin)
565598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller{
575598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	int i, err = __put_user(wsaved, &rwin->wsaved);
585598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
595598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	for (i = 0; i < wsaved; i++) {
605598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		struct reg_window *rp = &current_thread_info()->reg_window[i];
615598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		unsigned long fp = current_thread_info()->rwbuf_stkptrs[i];
625598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
635598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= copy_to_user(&rwin->reg_window[i], rp,
645598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller				    sizeof(struct reg_window));
655598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= __put_user(fp, &rwin->rwbuf_stkptrs[i]);
665598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	}
675598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	return err;
685598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller}
695598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
705598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Millerint restore_rwin_state(__siginfo_rwin_t __user *rp)
715598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller{
725598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	struct thread_info *t = current_thread_info();
735598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	int i, wsaved, err;
745598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
755598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	__get_user(wsaved, &rp->wsaved);
765598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	if (wsaved > NSWINS)
775598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		return -EFAULT;
785598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
795598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	err = 0;
805598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	for (i = 0; i < wsaved; i++) {
815598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= copy_from_user(&t->reg_window[i],
825598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller				      &rp->reg_window[i],
835598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller				      sizeof(struct reg_window));
845598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		err |= __get_user(t->rwbuf_stkptrs[i],
855598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller				  &rp->rwbuf_stkptrs[i]);
865598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	}
875598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	if (err)
885598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		return err;
895598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller
905598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	set_thread_wsaved(wsaved);
915598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	synchronize_user_stack();
925598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	if (get_thread_wsaved())
935598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller		return -EFAULT;
945598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller	return 0;
955598473a5b40c47a8c5349dd2c2630797169cf1aDavid S. Miller}
96