1/*
2 *  linux/arch/x86_64/kernel/i387.c
3 *
4 *  Copyright (C) 1994 Linus Torvalds
5 *  Copyright (C) 2002 Andi Kleen, SuSE Labs
6 *
7 *  Pentium III FXSR, SSE support
8 *  General FPU state handling cleanups
9 *	Gareth Hughes <gareth@valinux.com>, May 2000
10 *
11 *  x86-64 rework 2002 Andi Kleen.
12 *  Does direct fxsave in and out of user space now for signal handlers.
13 *  All the FSAVE<->FXSAVE conversion code has been moved to the 32bit emulation,
14 *  the 64bit user space sees a FXSAVE frame directly.
15 */
16
17#include <linux/sched.h>
18#include <linux/init.h>
19#include <asm/processor.h>
20#include <asm/i387.h>
21#include <asm/sigcontext.h>
22#include <asm/user.h>
23#include <asm/ptrace.h>
24#include <asm/uaccess.h>
25
26unsigned int mxcsr_feature_mask __read_mostly = 0xffffffff;
27
28void mxcsr_feature_mask_init(void)
29{
30	unsigned int mask;
31	clts();
32	memset(&current->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct));
33	asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave));
34	mask = current->thread.i387.fxsave.mxcsr_mask;
35	if (mask == 0) mask = 0x0000ffbf;
36	mxcsr_feature_mask &= mask;
37	stts();
38}
39
40/*
41 * Called at bootup to set up the initial FPU state that is later cloned
42 * into all processes.
43 */
44void __cpuinit fpu_init(void)
45{
46	unsigned long oldcr0 = read_cr0();
47	extern void __bad_fxsave_alignment(void);
48
49	if (offsetof(struct task_struct, thread.i387.fxsave) & 15)
50		__bad_fxsave_alignment();
51	set_in_cr4(X86_CR4_OSFXSR);
52	set_in_cr4(X86_CR4_OSXMMEXCPT);
53
54	write_cr0(oldcr0 & ~((1UL<<3)|(1UL<<2))); /* clear TS and EM */
55
56	mxcsr_feature_mask_init();
57	/* clean state in init */
58	current_thread_info()->status = 0;
59	clear_used_math();
60}
61
62void init_fpu(struct task_struct *child)
63{
64	if (tsk_used_math(child)) {
65		if (child == current)
66			unlazy_fpu(child);
67		return;
68	}
69	memset(&child->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct));
70	child->thread.i387.fxsave.cwd = 0x37f;
71	child->thread.i387.fxsave.mxcsr = 0x1f80;
72	/* only the device not available exception or ptrace can call init_fpu */
73	set_stopped_child_used_math(child);
74}
75
76/*
77 * Signal frame handlers.
78 */
79
80int save_i387(struct _fpstate __user *buf)
81{
82	struct task_struct *tsk = current;
83	int err = 0;
84
85	BUILD_BUG_ON(sizeof(struct user_i387_struct) !=
86			sizeof(tsk->thread.i387.fxsave));
87
88	if ((unsigned long)buf % 16)
89		printk("save_i387: bad fpstate %p\n",buf);
90
91	if (!used_math())
92		return 0;
93	clear_used_math(); /* trigger finit */
94	if (task_thread_info(tsk)->status & TS_USEDFPU) {
95		err = save_i387_checking((struct i387_fxsave_struct __user *)buf);
96		if (err) return err;
97		stts();
98		} else {
99		if (__copy_to_user(buf, &tsk->thread.i387.fxsave,
100				   sizeof(struct i387_fxsave_struct)))
101			return -1;
102	}
103		return 1;
104}
105
106/*
107 * ptrace request handlers.
108 */
109
110int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *tsk)
111{
112	init_fpu(tsk);
113	return __copy_to_user(buf, &tsk->thread.i387.fxsave,
114			       sizeof(struct user_i387_struct)) ? -EFAULT : 0;
115}
116
117int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf)
118{
119	if (__copy_from_user(&tsk->thread.i387.fxsave, buf,
120			     sizeof(struct user_i387_struct)))
121		return -EFAULT;
122		return 0;
123}
124
125/*
126 * FPU state for core dumps.
127 */
128
129int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu )
130{
131	struct task_struct *tsk = current;
132
133	if (!used_math())
134		return 0;
135
136	unlazy_fpu(tsk);
137	memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct));
138	return 1;
139}
140
141int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu)
142{
143	int fpvalid = !!tsk_used_math(tsk);
144
145	if (fpvalid) {
146		if (tsk == current)
147			unlazy_fpu(tsk);
148		memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct));
149}
150	return fpvalid;
151}
152