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
75int fpu_setup_sigcontext(struct fpucontext *fpucontext)
76{
77 struct task_struct *tsk = current;
78
79 if (!is_using_fpu(tsk))
80 return 0;
81
82
83
84
85
86 preempt_disable();
87
88#ifndef CONFIG_LAZY_SAVE_FPU
89 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
90 fpu_save(&tsk->thread.fpu_state);
91 tsk->thread.uregs->epsw &= ~EPSW_FE;
92 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
93 }
94#else
95 if (fpu_state_owner == tsk) {
96 fpu_save(&tsk->thread.fpu_state);
97 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
98 fpu_state_owner = NULL;
99 }
100#endif
101
102 preempt_enable();
103
104
105 clear_using_fpu(tsk);
106
107
108 if (copy_to_user(fpucontext,
109 &tsk->thread.fpu_state,
110 min(sizeof(struct fpu_state_struct),
111 sizeof(struct fpucontext))))
112 return -1;
113
114 return 1;
115}
116
117
118
119
120void fpu_kill_state(struct task_struct *tsk)
121{
122
123 preempt_disable();
124
125#ifndef CONFIG_LAZY_SAVE_FPU
126 if (tsk->thread.fpu_flags & THREAD_HAS_FPU) {
127 tsk->thread.uregs->epsw &= ~EPSW_FE;
128 tsk->thread.fpu_flags &= ~THREAD_HAS_FPU;
129 }
130#else
131 if (fpu_state_owner == tsk) {
132 fpu_state_owner->thread.uregs->epsw &= ~EPSW_FE;
133 fpu_state_owner = NULL;
134 }
135#endif
136
137 preempt_enable();
138
139
140 clear_using_fpu(tsk);
141}
142
143
144
145
146int fpu_restore_sigcontext(struct fpucontext *fpucontext)
147{
148 struct task_struct *tsk = current;
149 int ret;
150
151
152 ret = copy_from_user(&tsk->thread.fpu_state, fpucontext,
153 min(sizeof(struct fpu_state_struct),
154 sizeof(struct fpucontext)));
155 if (!ret)
156 set_using_fpu(tsk);
157
158 return ret;
159}
160
161
162
163
164int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpreg)
165{
166 struct task_struct *tsk = current;
167 int fpvalid;
168
169 fpvalid = is_using_fpu(tsk);
170 if (fpvalid) {
171 unlazy_fpu(tsk);
172 memcpy(fpreg, &tsk->thread.fpu_state, sizeof(*fpreg));
173 }
174
175 return fpvalid;
176}
177