diff options
Diffstat (limited to 'arch/x86_64/kernel/i387.c')
-rw-r--r-- | arch/x86_64/kernel/i387.c | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/arch/x86_64/kernel/i387.c b/arch/x86_64/kernel/i387.c new file mode 100644 index 000000000000..ba139cac57ce --- /dev/null +++ b/arch/x86_64/kernel/i387.c | |||
@@ -0,0 +1,155 @@ | |||
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/config.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <asm/processor.h> | ||
21 | #include <asm/i387.h> | ||
22 | #include <asm/sigcontext.h> | ||
23 | #include <asm/user.h> | ||
24 | #include <asm/ptrace.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | |||
27 | unsigned int mxcsr_feature_mask = 0xffffffff; | ||
28 | |||
29 | void mxcsr_feature_mask_init(void) | ||
30 | { | ||
31 | unsigned int mask; | ||
32 | clts(); | ||
33 | memset(¤t->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct)); | ||
34 | asm volatile("fxsave %0" : : "m" (current->thread.i387.fxsave)); | ||
35 | mask = current->thread.i387.fxsave.mxcsr_mask; | ||
36 | if (mask == 0) mask = 0x0000ffbf; | ||
37 | mxcsr_feature_mask &= mask; | ||
38 | stts(); | ||
39 | } | ||
40 | |||
41 | /* | ||
42 | * Called at bootup to set up the initial FPU state that is later cloned | ||
43 | * into all processes. | ||
44 | */ | ||
45 | void __init fpu_init(void) | ||
46 | { | ||
47 | unsigned long oldcr0 = read_cr0(); | ||
48 | extern void __bad_fxsave_alignment(void); | ||
49 | |||
50 | if (offsetof(struct task_struct, thread.i387.fxsave) & 15) | ||
51 | __bad_fxsave_alignment(); | ||
52 | set_in_cr4(X86_CR4_OSFXSR); | ||
53 | set_in_cr4(X86_CR4_OSXMMEXCPT); | ||
54 | |||
55 | write_cr0(oldcr0 & ~((1UL<<3)|(1UL<<2))); /* clear TS and EM */ | ||
56 | |||
57 | mxcsr_feature_mask_init(); | ||
58 | /* clean state in init */ | ||
59 | current_thread_info()->status = 0; | ||
60 | clear_used_math(); | ||
61 | } | ||
62 | |||
63 | void init_fpu(struct task_struct *child) | ||
64 | { | ||
65 | if (tsk_used_math(child)) { | ||
66 | if (child == current) | ||
67 | unlazy_fpu(child); | ||
68 | return; | ||
69 | } | ||
70 | memset(&child->thread.i387.fxsave, 0, sizeof(struct i387_fxsave_struct)); | ||
71 | child->thread.i387.fxsave.cwd = 0x37f; | ||
72 | child->thread.i387.fxsave.mxcsr = 0x1f80; | ||
73 | /* only the device not available exception or ptrace can call init_fpu */ | ||
74 | set_stopped_child_used_math(child); | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Signal frame handlers. | ||
79 | */ | ||
80 | |||
81 | int save_i387(struct _fpstate __user *buf) | ||
82 | { | ||
83 | struct task_struct *tsk = current; | ||
84 | int err = 0; | ||
85 | |||
86 | { | ||
87 | extern void bad_user_i387_struct(void); | ||
88 | if (sizeof(struct user_i387_struct) != sizeof(tsk->thread.i387.fxsave)) | ||
89 | bad_user_i387_struct(); | ||
90 | } | ||
91 | |||
92 | if ((unsigned long)buf % 16) | ||
93 | printk("save_i387: bad fpstate %p\n",buf); | ||
94 | |||
95 | if (!used_math()) | ||
96 | return 0; | ||
97 | clear_used_math(); /* trigger finit */ | ||
98 | if (tsk->thread_info->status & TS_USEDFPU) { | ||
99 | err = save_i387_checking((struct i387_fxsave_struct __user *)buf); | ||
100 | if (err) return err; | ||
101 | stts(); | ||
102 | } else { | ||
103 | if (__copy_to_user(buf, &tsk->thread.i387.fxsave, | ||
104 | sizeof(struct i387_fxsave_struct))) | ||
105 | return -1; | ||
106 | } | ||
107 | return 1; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * ptrace request handlers. | ||
112 | */ | ||
113 | |||
114 | int get_fpregs(struct user_i387_struct __user *buf, struct task_struct *tsk) | ||
115 | { | ||
116 | init_fpu(tsk); | ||
117 | return __copy_to_user(buf, &tsk->thread.i387.fxsave, | ||
118 | sizeof(struct user_i387_struct)) ? -EFAULT : 0; | ||
119 | } | ||
120 | |||
121 | int set_fpregs(struct task_struct *tsk, struct user_i387_struct __user *buf) | ||
122 | { | ||
123 | if (__copy_from_user(&tsk->thread.i387.fxsave, buf, | ||
124 | sizeof(struct user_i387_struct))) | ||
125 | return -EFAULT; | ||
126 | return 0; | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * FPU state for core dumps. | ||
131 | */ | ||
132 | |||
133 | int dump_fpu( struct pt_regs *regs, struct user_i387_struct *fpu ) | ||
134 | { | ||
135 | struct task_struct *tsk = current; | ||
136 | |||
137 | if (!used_math()) | ||
138 | return 0; | ||
139 | |||
140 | unlazy_fpu(tsk); | ||
141 | memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct)); | ||
142 | return 1; | ||
143 | } | ||
144 | |||
145 | int dump_task_fpu(struct task_struct *tsk, struct user_i387_struct *fpu) | ||
146 | { | ||
147 | int fpvalid = !!tsk_used_math(tsk); | ||
148 | |||
149 | if (fpvalid) { | ||
150 | if (tsk == current) | ||
151 | unlazy_fpu(tsk); | ||
152 | memcpy(fpu, &tsk->thread.i387.fxsave, sizeof(struct user_i387_struct)); | ||
153 | } | ||
154 | return fpvalid; | ||
155 | } | ||