1
2
3
4
5
6
7
8
9
10
11#include <asm/uaccess.h>
12#include <asm/fpu.h>
13#include <asm/elf.h>
14#include <asm/exceptions.h>
15#include <asm/system.h>
16
17#ifdef CONFIG_LAZY_SAVE_FPU
18struct task_struct *fpu_state_owner;
19#endif
20
21
22
23
24asmlinkage void fpu_disabled_in_kernel(struct pt_regs *regs)
25{
26 die_if_no_fixup("An FPU Disabled exception happened in kernel space\n",
27 regs, EXCEP_FPU_DISABLED);
28}
29
30
31
32
33
34
35asmlinkage void fpu_exception(struct pt_regs *regs, enum exception_code code)
36{
37 struct task_struct *tsk = current;
38 siginfo_t info;
39 u32 fpcr;
40
41 if (!user_mode(regs))
42 die_if_no_fixup("An FPU Operation exception happened in"
43 " kernel space\n",
44 regs, code);
45
46 if (!is_using_fpu(tsk))
47 die_if_no_fixup("An FPU Operation exception happened,"
48 " but the FPU is not in use",
49 regs, code);
50
51 info.si_signo = SIGFPE;
52 info.si_errno = 0;
53 info.si_addr = (void *) tsk->thread.uregs->pc;
54 info.si_code = FPE_FLTINV;
55
56 unlazy_fpu(tsk);
57
58 fpcr = tsk->thread.fpu_state.fpcr;
59
60 if (fpcr & FPCR_EC_Z)
61 info.si_code = FPE_FLTDIV;
62 else if (fpcr & FPCR_EC_O)
63 info.si_code = FPE_FLTOVF;
64 else if (fpcr & FPCR_EC_U)
65 info.si_code = FPE_FLTUND;
66 else if (fpcr & FPCR_EC_I)
67 info.si_code = FPE_FLTRES;
68
69 force_sig_info(SIGFPE, &info, tsk);
70}
71
72
73
74
75
76asmlinkage void fpu_invalid_op(struct pt_regs *regs, enum exception_code code)
77{
78 siginfo_t info;
79
80 if (!user_mode(regs))
81 die_if_no_fixup("FPU invalid opcode", regs, code);
82
83 info.si_signo = SIGILL;
84 info.si_errno = 0;
85 info.si_code = ILL_COPROC;
86 info.si_addr = (void *) regs->pc;
87 force_sig_info(info.si_signo, &info, current);
88}
89
90
91
92
93int fpu_setup_sigcontext(struct fpucontext *fpucontext)
94{
95 struct task_struct *tsk = current;
96
97 if (!is_using_fpu(tsk))
98 return 0;
99
100
101
102
103
104 preempt_disable();
105
106#ifndef CONFIG_LAZY_SAVE_FPU
107 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
108 fpu_save(&tsk->thread.fpu_state);
109 tsk->thread.uregs->epsw &= ~EPSW_FE;
110 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
111 }
112#else
113 if (fpu_state_owner == tsk) {
114 fpu_save(&tsk->thread.fpu_state);
115 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
116 fpu_state_owner = NULL;
117 }
118#endif
119
120 preempt_enable();
121
122
123 clear_using_fpu(tsk);
124
125
126 if (copy_to_user(fpucontext,
127 &tsk->thread.fpu_state,
128 min(sizeof(struct fpu_state_struct),
129 sizeof(struct fpucontext))))
130 return -1;
131
132 return 1;
133}
134
135
136
137
138void fpu_kill_state(struct task_struct *tsk)
139{
140
141 preempt_disable();
142
143#ifndef CONFIG_LAZY_SAVE_FPU
144 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
145 tsk->thread.uregs->epsw &= ~EPSW_FE;
146 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
147 }
148#else
149 if (fpu_state_owner == tsk) {
150 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
151 fpu_state_owner = NULL;
152 }
153#endif
154
155 preempt_enable();
156
157
158 clear_using_fpu(tsk);
159}
160
161
162
163
164int fpu_restore_sigcontext(struct fpucontext *fpucontext)
165{
166 struct task_struct *tsk = current;
167 int ret;
168
169
170 ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
171 min(sizeof(struct fpu_state_struct),
172 sizeof(struct fpucontext)));
173 if (!ret)
174 set_using_fpu(tsk);
175
176 return ret;
177}
178
179
180
181
182int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
183{
184 struct task_struct *tsk = current;
185 int fpvalid;
186
187 fpvalid = is_using_fpu(tsk);
188 if (fpvalid) {
189 unlazy_fpu(tsk);
190 memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
191 }
192
193 return fpvalid;
194}
195