This source file includes following definitions.
- math_emulate
- __math_abort
- math_emulate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 #include <linux/config.h>
26
27 #ifdef CONFIG_MATH_EMULATION
28
29 #include <linux/signal.h>
30
31 #include "fpu_system.h"
32 #include "fpu_emu.h"
33 #include "exception.h"
34
35 #include <asm/segment.h>
36
37
38 #define __BAD__ Un_impl
39
40 static FUNC st_instr_table[64] = {
41 fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
42 fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
43 fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
44 fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
45 fsub__, fp_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
46 fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
47 fdiv__, trig_a, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
48 fdivr_, trig_b, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
49 };
50
51 #define _NONE_ 0
52 #define _REG0_ 1
53 #define _REGI_ 2
54 #define _REGi_ 0
55 #define _PUSH_ 3
56 #define _null_ 4
57
58 static unsigned char type_table[64] = {
59 _REGI_, _NONE_, _null_, _null_, _REGI_, _REGi_, _REGI_, _null_,
60 _REGI_, _REGI_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
61 _REGI_, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
62 _REGI_, _null_, _null_, _null_, _null_, _REG0_, _REGI_, _null_,
63 _REGI_, _NONE_, _null_, _NONE_, _REGI_, _REGI_, _REGI_, _NONE_,
64 _REGI_, _NONE_, _REGI_, _null_, _REGI_, _REGI_, _REGI_, _null_,
65 _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_,
66 _REGI_, _NONE_, _null_, _null_, _REGI_, _null_, _REGI_, _null_
67 };
68
69
70
71
72 unsigned char FPU_rm;
73 char FPU_st0_tag;
74 FPU_REG *FPU_st0_ptr;
75
76 #ifdef PARANOID
77 char emulating=0;
78 #endif PARANOID
79
80 #define bswapw(x) __asm__("xchgb %%al,%%ah":"=a" (x):"0" ((short)x))
81
82
83 void math_emulate(long arg)
84 {
85 unsigned char FPU_modrm;
86 unsigned short code;
87
88 #ifdef PARANOID
89 if ( emulating )
90 {
91 printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\r\n");
92 }
93 RE_ENTRANT_CHECK_ON
94 #endif PARANOID
95
96 if (!current->used_math)
97 {
98 finit();
99 current->used_math = 1;
100 control_word = 0x037f;
101 status_word = 0x0000;
102 }
103
104 FPU_info = (struct info *) &arg;
105
106
107 if (FPU_EFLAGS & 0x00020000)
108 math_abort(FPU_info,SIGILL);
109
110
111 if (FPU_CS != 0x000f)
112 {
113 printk("math_emulate: %04x:%08x\n\r",FPU_CS,FPU_EIP);
114 panic("Math emulation needed in kernel");
115 }
116
117 FPU_lookahead = 1;
118 if (current->flags & PF_PTRACED)
119 FPU_lookahead = 0;
120 do_another:
121
122 FPU_entry_eip = FPU_ORIG_EIP = FPU_EIP;
123
124 RE_ENTRANT_CHECK_OFF
125 code = get_fs_word((unsigned short *) FPU_EIP);
126 RE_ENTRANT_CHECK_ON
127
128 if ( (code & 0xff) == 0x66 )
129 {
130 FPU_EIP++;
131 RE_ENTRANT_CHECK_OFF
132 code = get_fs_word((unsigned short *) FPU_EIP);
133 RE_ENTRANT_CHECK_ON
134 }
135 FPU_EIP += 2;
136
137 FPU_modrm = code >> 8;
138 FPU_rm = FPU_modrm & 7;
139
140 if ( FPU_modrm < 0300 )
141 {
142
143 get_address(FPU_modrm);
144 if ( !(code & 1) )
145 {
146 switch ( (code >> 1) & 3 )
147 {
148 case 0:
149 reg_load_single();
150 break;
151 case 1:
152 reg_load_int32();
153 break;
154 case 2:
155 reg_load_double();
156 break;
157 case 3:
158 reg_load_int16();
159 break;
160 }
161
162
163
164 FPU_st0_ptr = &st(0);
165 FPU_st0_tag = FPU_st0_ptr->tag;
166 if ( NOT_EMPTY_0 )
167 {
168 switch ( (FPU_modrm >> 3) & 7 )
169 {
170 case 0:
171 reg_add(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
172 break;
173 case 1:
174 reg_mul(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
175 break;
176 case 2:
177 compare_st_data();
178 break;
179 case 3:
180 compare_st_data();
181 pop();
182 break;
183 case 4:
184 reg_sub(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
185 break;
186 case 5:
187 reg_sub(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr);
188 break;
189 case 6:
190 reg_div(FPU_st0_ptr, &FPU_loaded_data, FPU_st0_ptr);
191 break;
192 case 7:
193 reg_div(&FPU_loaded_data, FPU_st0_ptr, FPU_st0_ptr);
194 break;
195 }
196 }
197 else
198 stack_underflow();
199 }
200 else
201 {
202 load_store_instr(((FPU_modrm & 0x38) | (code & 6)) >> 1);
203 }
204
205 data_operand_offset = (unsigned long)FPU_data_address;
206 }
207 else
208 {
209
210 unsigned char instr_index = (FPU_modrm & 0x38) | (code & 7);
211 FPU_st0_ptr = &st(0);
212 FPU_st0_tag = FPU_st0_ptr->tag;
213 switch ( type_table[(int) instr_index] )
214 {
215 case _NONE_:
216 break;
217 case _REG0_:
218 if ( !NOT_EMPTY_0 )
219 {
220 stack_underflow();
221 goto instruction_done;
222 }
223 break;
224 case _REGI_:
225 if ( !NOT_EMPTY_0 || !NOT_EMPTY(FPU_rm) )
226 {
227 stack_underflow();
228 goto instruction_done;
229 }
230 break;
231 case _PUSH_:
232 break;
233 case _null_:
234 Un_impl();
235 goto instruction_done;
236 default:
237 EXCEPTION(EX_INTERNAL|0x111);
238 goto instruction_done;
239 }
240 (*st_instr_table[(int) instr_index])();
241 }
242
243 instruction_done:
244
245 ip_offset = FPU_entry_eip;
246 bswapw(code);
247 *(1 + (unsigned short *)&cs_selector) = code & 0x7ff;
248
249 if (FPU_lookahead && !need_resched)
250 {
251 unsigned char next;
252 skip_fwait:
253 RE_ENTRANT_CHECK_OFF
254 next = get_fs_byte((unsigned char *) FPU_EIP);
255 RE_ENTRANT_CHECK_ON
256 test_for_fp:
257 if ( (next & 0xf8) == 0xd8 )
258 {
259 goto do_another;
260 }
261 if ( next == 0x9b )
262 { FPU_EIP++; goto skip_fwait; }
263 if ( next == 0x66 )
264 {
265 RE_ENTRANT_CHECK_OFF
266 next = get_fs_byte((unsigned char *) (FPU_EIP+1));
267 RE_ENTRANT_CHECK_ON
268 if ( (next & 0xf8) == 0xd8 )
269 goto test_for_fp;
270 }
271 }
272
273 RE_ENTRANT_CHECK_OFF
274 }
275
276
277 void __math_abort(struct info * info, unsigned int signal)
278 {
279 FPU_EIP = FPU_ORIG_EIP;
280 send_sig(signal,current,1);
281 __asm__("movl %0,%%esp ; ret"::"g" (((long) info)-4));
282 }
283
284 #else
285
286 #include <linux/signal.h>
287 #include <linux/sched.h>
288
289 void math_emulate(long arg)
290 {
291 printk("math-meulation not enabled and no coprocessor found.\n");
292 printk("killing %s.\n",current->comm);
293 send_sig(SIGFPE,current,1);
294 schedule();
295 }
296
297 #endif