File: /usr/src/linux/arch/sh/kernel/fpu.c
1 /* $Id: fpu.c,v 1.29 2000/03/22 13:42:10 gniibe Exp $
2 *
3 * linux/arch/sh/kernel/fpu.c
4 *
5 * Save/restore floating point context for signal handlers.
6 *
7 * This file is subject to the terms and conditions of the GNU General Public
8 * License. See the file "COPYING" in the main directory of this archive
9 * for more details.
10 *
11 * Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
12 *
13 * FIXME! These routines can be optimized in big endian case.
14 */
15
16 #include <linux/sched.h>
17 #include <linux/signal.h>
18 #include <asm/processor.h>
19 #include <asm/io.h>
20
21 void
22 save_fpu(struct task_struct *tsk)
23 {
24 asm volatile("sts.l fpul, @-%0\n\t"
25 "sts.l fpscr, @-%0\n\t"
26 "lds %1, fpscr\n\t"
27 "frchg\n\t"
28 "fmov.s fr15, @-%0\n\t"
29 "fmov.s fr14, @-%0\n\t"
30 "fmov.s fr13, @-%0\n\t"
31 "fmov.s fr12, @-%0\n\t"
32 "fmov.s fr11, @-%0\n\t"
33 "fmov.s fr10, @-%0\n\t"
34 "fmov.s fr9, @-%0\n\t"
35 "fmov.s fr8, @-%0\n\t"
36 "fmov.s fr7, @-%0\n\t"
37 "fmov.s fr6, @-%0\n\t"
38 "fmov.s fr5, @-%0\n\t"
39 "fmov.s fr4, @-%0\n\t"
40 "fmov.s fr3, @-%0\n\t"
41 "fmov.s fr2, @-%0\n\t"
42 "fmov.s fr1, @-%0\n\t"
43 "fmov.s fr0, @-%0\n\t"
44 "frchg\n\t"
45 "fmov.s fr15, @-%0\n\t"
46 "fmov.s fr14, @-%0\n\t"
47 "fmov.s fr13, @-%0\n\t"
48 "fmov.s fr12, @-%0\n\t"
49 "fmov.s fr11, @-%0\n\t"
50 "fmov.s fr10, @-%0\n\t"
51 "fmov.s fr9, @-%0\n\t"
52 "fmov.s fr8, @-%0\n\t"
53 "fmov.s fr7, @-%0\n\t"
54 "fmov.s fr6, @-%0\n\t"
55 "fmov.s fr5, @-%0\n\t"
56 "fmov.s fr4, @-%0\n\t"
57 "fmov.s fr3, @-%0\n\t"
58 "fmov.s fr2, @-%0\n\t"
59 "fmov.s fr1, @-%0\n\t"
60 "fmov.s fr0, @-%0"
61 : /* no output */
62 : "r" ((char *)(&tsk->thread.fpu.hard.status)),
63 "r" (FPSCR_INIT)
64 : "memory");
65
66 tsk->flags &= ~PF_USEDFPU;
67 release_fpu();
68 }
69
70 static void
71 restore_fpu(struct task_struct *tsk)
72 {
73 asm volatile("lds %1, fpscr\n\t"
74 "fmov.s @%0+, fr0\n\t"
75 "fmov.s @%0+, fr1\n\t"
76 "fmov.s @%0+, fr2\n\t"
77 "fmov.s @%0+, fr3\n\t"
78 "fmov.s @%0+, fr4\n\t"
79 "fmov.s @%0+, fr5\n\t"
80 "fmov.s @%0+, fr6\n\t"
81 "fmov.s @%0+, fr7\n\t"
82 "fmov.s @%0+, fr8\n\t"
83 "fmov.s @%0+, fr9\n\t"
84 "fmov.s @%0+, fr10\n\t"
85 "fmov.s @%0+, fr11\n\t"
86 "fmov.s @%0+, fr12\n\t"
87 "fmov.s @%0+, fr13\n\t"
88 "fmov.s @%0+, fr14\n\t"
89 "fmov.s @%0+, fr15\n\t"
90 "frchg\n\t"
91 "fmov.s @%0+, fr0\n\t"
92 "fmov.s @%0+, fr1\n\t"
93 "fmov.s @%0+, fr2\n\t"
94 "fmov.s @%0+, fr3\n\t"
95 "fmov.s @%0+, fr4\n\t"
96 "fmov.s @%0+, fr5\n\t"
97 "fmov.s @%0+, fr6\n\t"
98 "fmov.s @%0+, fr7\n\t"
99 "fmov.s @%0+, fr8\n\t"
100 "fmov.s @%0+, fr9\n\t"
101 "fmov.s @%0+, fr10\n\t"
102 "fmov.s @%0+, fr11\n\t"
103 "fmov.s @%0+, fr12\n\t"
104 "fmov.s @%0+, fr13\n\t"
105 "fmov.s @%0+, fr14\n\t"
106 "fmov.s @%0+, fr15\n\t"
107 "frchg\n\t"
108 "lds.l @%0+, fpscr\n\t"
109 "lds.l @%0+, fpul\n\t"
110 : /* no output */
111 : "r" (&tsk->thread.fpu), "r" (FPSCR_INIT)
112 : "memory");
113 }
114
115 /*
116 * Load the FPU with signalling NANS. This bit pattern we're using
117 * has the property that no matter wether considered as single or as
118 * double precission represents signaling NANS.
119 */
120
121 void fpu_init(void)
122 {
123 asm volatile("lds %0, fpul\n\t"
124 "lds %1, fpscr\n\t"
125 "fsts fpul, fr0\n\t"
126 "fsts fpul, fr1\n\t"
127 "fsts fpul, fr2\n\t"
128 "fsts fpul, fr3\n\t"
129 "fsts fpul, fr4\n\t"
130 "fsts fpul, fr5\n\t"
131 "fsts fpul, fr6\n\t"
132 "fsts fpul, fr7\n\t"
133 "fsts fpul, fr8\n\t"
134 "fsts fpul, fr9\n\t"
135 "fsts fpul, fr10\n\t"
136 "fsts fpul, fr11\n\t"
137 "fsts fpul, fr12\n\t"
138 "fsts fpul, fr13\n\t"
139 "fsts fpul, fr14\n\t"
140 "fsts fpul, fr15\n\t"
141 "frchg\n\t"
142 "fsts fpul, fr0\n\t"
143 "fsts fpul, fr1\n\t"
144 "fsts fpul, fr2\n\t"
145 "fsts fpul, fr3\n\t"
146 "fsts fpul, fr4\n\t"
147 "fsts fpul, fr5\n\t"
148 "fsts fpul, fr6\n\t"
149 "fsts fpul, fr7\n\t"
150 "fsts fpul, fr8\n\t"
151 "fsts fpul, fr9\n\t"
152 "fsts fpul, fr10\n\t"
153 "fsts fpul, fr11\n\t"
154 "fsts fpul, fr12\n\t"
155 "fsts fpul, fr13\n\t"
156 "fsts fpul, fr14\n\t"
157 "fsts fpul, fr15\n\t"
158 "frchg"
159 : /* no output */
160 : "r" (0), "r" (FPSCR_INIT));
161 }
162
163 asmlinkage void
164 do_fpu_error(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7,
165 struct pt_regs regs)
166 {
167 struct task_struct *tsk = current;
168
169 regs.pc += 2;
170
171 grab_fpu();
172 save_fpu(tsk);
173 tsk->thread.trap_no = 11;
174 tsk->thread.error_code = 0;
175 force_sig(SIGFPE, tsk);
176 }
177
178 asmlinkage void
179 do_fpu_state_restore(unsigned long r4, unsigned long r5, unsigned long r6,
180 unsigned long r7, struct pt_regs regs)
181 {
182 struct task_struct *tsk = current;
183
184 if (!user_mode(®s)) {
185 if (tsk != &init_task) {
186 unlazy_fpu(tsk);
187 }
188 tsk = &init_task;
189 if (tsk->flags & PF_USEDFPU) {
190 /*
191 * This weird situation can be occurred.
192 *
193 * There's race condition in __cli:
194 *
195 * (1) SR --> register
196 * (2) Set IMASK of register
197 * (3) SR <-- register
198 *
199 * Between (1) and (2), or (2) and (3) getting
200 * interrupt, and interrupt handler (or
201 * softirq) may use FPU.
202 *
203 * Then, SR.FD is overwritten by (3).
204 *
205 * This results init_task.PF_USEDFPU is on,
206 * with SR.FD == 1.
207 *
208 */
209 release_fpu();
210 return;
211 }
212 }
213
214 grab_fpu();
215 if (tsk->used_math) {
216 /* Using the FPU again. */
217 restore_fpu(tsk);
218 } else {
219 /* First time FPU user. */
220 fpu_init();
221 tsk->used_math = 1;
222 }
223 tsk->flags |= PF_USEDFPU;
224 release_fpu();
225 }
226
227 /*
228 * Change current FD flag to set FD flag back to exception
229 */
230 asmlinkage void
231 fpu_prepare_fd(unsigned long sr, unsigned long r5, unsigned long r6,
232 unsigned long r7, struct pt_regs regs)
233 {
234 __cli();
235 if (!user_mode(®s)) {
236 if (init_task.flags & PF_USEDFPU)
237 grab_fpu();
238 else {
239 if (!(sr & SR_FD)) {
240 BUG();
241 release_fpu();
242 }
243 }
244 return;
245 }
246
247 if (sr & SR_FD) { /* Kernel doesn't grab FPU */
248 if (current->flags & PF_USEDFPU)
249 grab_fpu();
250 else {
251 if (init_task.flags & PF_USEDFPU) {
252 /*
253 * This weird situation can be occurred.
254 * See the comment in do_fpu_state_restore.
255 */
256 grab_fpu();
257 save_fpu(&init_task);
258 }
259 }
260 } else {
261 if (init_task.flags & PF_USEDFPU)
262 save_fpu(&init_task);
263 else {
264 BUG();
265 release_fpu();
266 }
267 }
268 }
269
270 /* Short cut for the FPU exception */
271 asmlinkage void
272 enable_fpu_in_danger(void)
273 {
274 struct task_struct *tsk = current;
275
276 if (tsk != &init_task)
277 unlazy_fpu(tsk);
278
279 tsk = &init_task;
280 if (tsk->used_math) {
281 /* Using the FPU again. */
282 restore_fpu(tsk);
283 } else {
284 /* First time FPU user. */
285 fpu_init();
286 tsk->used_math = 1;
287 }
288 tsk->flags |= PF_USEDFPU;
289 }
290