diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/i386/math-emu/fpu_entry.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/i386/math-emu/fpu_entry.c')
-rw-r--r-- | arch/i386/math-emu/fpu_entry.c | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/arch/i386/math-emu/fpu_entry.c b/arch/i386/math-emu/fpu_entry.c new file mode 100644 index 000000000000..d93f16ef828f --- /dev/null +++ b/arch/i386/math-emu/fpu_entry.c | |||
@@ -0,0 +1,760 @@ | |||
1 | /*---------------------------------------------------------------------------+ | ||
2 | | fpu_entry.c | | ||
3 | | | | ||
4 | | The entry functions for wm-FPU-emu | | ||
5 | | | | ||
6 | | Copyright (C) 1992,1993,1994,1996,1997 | | ||
7 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | | ||
8 | | E-mail billm@suburbia.net | | ||
9 | | | | ||
10 | | See the files "README" and "COPYING" for further copyright and warranty | | ||
11 | | information. | | ||
12 | | | | ||
13 | +---------------------------------------------------------------------------*/ | ||
14 | |||
15 | /*---------------------------------------------------------------------------+ | ||
16 | | Note: | | ||
17 | | The file contains code which accesses user memory. | | ||
18 | | Emulator static data may change when user memory is accessed, due to | | ||
19 | | other processes using the emulator while swapping is in progress. | | ||
20 | +---------------------------------------------------------------------------*/ | ||
21 | |||
22 | /*---------------------------------------------------------------------------+ | ||
23 | | math_emulate(), restore_i387_soft() and save_i387_soft() are the only | | ||
24 | | entry points for wm-FPU-emu. | | ||
25 | +---------------------------------------------------------------------------*/ | ||
26 | |||
27 | #include <linux/signal.h> | ||
28 | #include <linux/ptrace.h> | ||
29 | |||
30 | #include <asm/uaccess.h> | ||
31 | #include <asm/desc.h> | ||
32 | |||
33 | #include "fpu_system.h" | ||
34 | #include "fpu_emu.h" | ||
35 | #include "exception.h" | ||
36 | #include "control_w.h" | ||
37 | #include "status_w.h" | ||
38 | |||
39 | #define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */ | ||
40 | |||
41 | #ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */ | ||
42 | |||
43 | /* WARNING: These codes are not documented by Intel in their 80486 manual | ||
44 | and may not work on FPU clones or later Intel FPUs. */ | ||
45 | |||
46 | /* Changes to support the un-doc codes provided by Linus Torvalds. */ | ||
47 | |||
48 | #define _d9_d8_ fstp_i /* unofficial code (19) */ | ||
49 | #define _dc_d0_ fcom_st /* unofficial code (14) */ | ||
50 | #define _dc_d8_ fcompst /* unofficial code (1c) */ | ||
51 | #define _dd_c8_ fxch_i /* unofficial code (0d) */ | ||
52 | #define _de_d0_ fcompst /* unofficial code (16) */ | ||
53 | #define _df_c0_ ffreep /* unofficial code (07) ffree + pop */ | ||
54 | #define _df_c8_ fxch_i /* unofficial code (0f) */ | ||
55 | #define _df_d0_ fstp_i /* unofficial code (17) */ | ||
56 | #define _df_d8_ fstp_i /* unofficial code (1f) */ | ||
57 | |||
58 | static FUNC const st_instr_table[64] = { | ||
59 | fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_, | ||
60 | fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_, | ||
61 | fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_, | ||
62 | fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_, | ||
63 | fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, | ||
64 | fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, | ||
65 | fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, | ||
66 | fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, | ||
67 | }; | ||
68 | |||
69 | #else /* Support only documented FPU op-codes */ | ||
70 | |||
71 | static FUNC const st_instr_table[64] = { | ||
72 | fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__, | ||
73 | fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__, | ||
74 | fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__, | ||
75 | fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__, | ||
76 | fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_, | ||
77 | fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__, | ||
78 | fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__, | ||
79 | fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__, | ||
80 | }; | ||
81 | |||
82 | #endif /* NO_UNDOC_CODE */ | ||
83 | |||
84 | |||
85 | #define _NONE_ 0 /* Take no special action */ | ||
86 | #define _REG0_ 1 /* Need to check for not empty st(0) */ | ||
87 | #define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */ | ||
88 | #define _REGi_ 0 /* Uses st(rm) */ | ||
89 | #define _PUSH_ 3 /* Need to check for space to push onto stack */ | ||
90 | #define _null_ 4 /* Function illegal or not implemented */ | ||
91 | #define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */ | ||
92 | #define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */ | ||
93 | #define _REGIc 0 /* Compare st(0) and st(rm) */ | ||
94 | #define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */ | ||
95 | |||
96 | #ifndef NO_UNDOC_CODE | ||
97 | |||
98 | /* Un-documented FPU op-codes supported by default. (see above) */ | ||
99 | |||
100 | static u_char const type_table[64] = { | ||
101 | _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, | ||
102 | _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, | ||
103 | _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, | ||
104 | _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, | ||
105 | _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, | ||
106 | _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, | ||
107 | _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, | ||
108 | _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ | ||
109 | }; | ||
110 | |||
111 | #else /* Support only documented FPU op-codes */ | ||
112 | |||
113 | static u_char const type_table[64] = { | ||
114 | _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_, | ||
115 | _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_, | ||
116 | _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_, | ||
117 | _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_, | ||
118 | _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, | ||
119 | _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, | ||
120 | _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, | ||
121 | _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ | ||
122 | }; | ||
123 | |||
124 | #endif /* NO_UNDOC_CODE */ | ||
125 | |||
126 | |||
127 | #ifdef RE_ENTRANT_CHECKING | ||
128 | u_char emulating=0; | ||
129 | #endif /* RE_ENTRANT_CHECKING */ | ||
130 | |||
131 | static int valid_prefix(u_char *Byte, u_char __user **fpu_eip, | ||
132 | overrides *override); | ||
133 | |||
134 | asmlinkage void math_emulate(long arg) | ||
135 | { | ||
136 | u_char FPU_modrm, byte1; | ||
137 | unsigned short code; | ||
138 | fpu_addr_modes addr_modes; | ||
139 | int unmasked; | ||
140 | FPU_REG loaded_data; | ||
141 | FPU_REG *st0_ptr; | ||
142 | u_char loaded_tag, st0_tag; | ||
143 | void __user *data_address; | ||
144 | struct address data_sel_off; | ||
145 | struct address entry_sel_off; | ||
146 | unsigned long code_base = 0; | ||
147 | unsigned long code_limit = 0; /* Initialized to stop compiler warnings */ | ||
148 | struct desc_struct code_descriptor; | ||
149 | |||
150 | #ifdef RE_ENTRANT_CHECKING | ||
151 | if ( emulating ) | ||
152 | { | ||
153 | printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n"); | ||
154 | } | ||
155 | RE_ENTRANT_CHECK_ON; | ||
156 | #endif /* RE_ENTRANT_CHECKING */ | ||
157 | |||
158 | if (!used_math()) | ||
159 | { | ||
160 | finit(); | ||
161 | set_used_math(); | ||
162 | } | ||
163 | |||
164 | SETUP_DATA_AREA(arg); | ||
165 | |||
166 | FPU_ORIG_EIP = FPU_EIP; | ||
167 | |||
168 | if ( (FPU_EFLAGS & 0x00020000) != 0 ) | ||
169 | { | ||
170 | /* Virtual 8086 mode */ | ||
171 | addr_modes.default_mode = VM86; | ||
172 | FPU_EIP += code_base = FPU_CS << 4; | ||
173 | code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */ | ||
174 | } | ||
175 | else if ( FPU_CS == __USER_CS && FPU_DS == __USER_DS ) | ||
176 | { | ||
177 | addr_modes.default_mode = 0; | ||
178 | } | ||
179 | else if ( FPU_CS == __KERNEL_CS ) | ||
180 | { | ||
181 | printk("math_emulate: %04x:%08lx\n",FPU_CS,FPU_EIP); | ||
182 | panic("Math emulation needed in kernel"); | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | |||
187 | if ( (FPU_CS & 4) != 4 ) /* Must be in the LDT */ | ||
188 | { | ||
189 | /* Can only handle segmented addressing via the LDT | ||
190 | for now, and it must be 16 bit */ | ||
191 | printk("FPU emulator: Unsupported addressing mode\n"); | ||
192 | math_abort(FPU_info, SIGILL); | ||
193 | } | ||
194 | |||
195 | code_descriptor = LDT_DESCRIPTOR(FPU_CS); | ||
196 | if ( SEG_D_SIZE(code_descriptor) ) | ||
197 | { | ||
198 | /* The above test may be wrong, the book is not clear */ | ||
199 | /* Segmented 32 bit protected mode */ | ||
200 | addr_modes.default_mode = SEG32; | ||
201 | } | ||
202 | else | ||
203 | { | ||
204 | /* 16 bit protected mode */ | ||
205 | addr_modes.default_mode = PM16; | ||
206 | } | ||
207 | FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor); | ||
208 | code_limit = code_base | ||
209 | + (SEG_LIMIT(code_descriptor)+1) * SEG_GRANULARITY(code_descriptor) | ||
210 | - 1; | ||
211 | if ( code_limit < code_base ) code_limit = 0xffffffff; | ||
212 | } | ||
213 | |||
214 | FPU_lookahead = 1; | ||
215 | if (current->ptrace & PT_PTRACED) | ||
216 | FPU_lookahead = 0; | ||
217 | |||
218 | if ( !valid_prefix(&byte1, (u_char __user **)&FPU_EIP, | ||
219 | &addr_modes.override) ) | ||
220 | { | ||
221 | RE_ENTRANT_CHECK_OFF; | ||
222 | printk("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n" | ||
223 | "FPU emulator: self-modifying code! (emulation impossible)\n", | ||
224 | byte1); | ||
225 | RE_ENTRANT_CHECK_ON; | ||
226 | EXCEPTION(EX_INTERNAL|0x126); | ||
227 | math_abort(FPU_info,SIGILL); | ||
228 | } | ||
229 | |||
230 | do_another_FPU_instruction: | ||
231 | |||
232 | no_ip_update = 0; | ||
233 | |||
234 | FPU_EIP++; /* We have fetched the prefix and first code bytes. */ | ||
235 | |||
236 | if ( addr_modes.default_mode ) | ||
237 | { | ||
238 | /* This checks for the minimum instruction bytes. | ||
239 | We also need to check any extra (address mode) code access. */ | ||
240 | if ( FPU_EIP > code_limit ) | ||
241 | math_abort(FPU_info,SIGSEGV); | ||
242 | } | ||
243 | |||
244 | if ( (byte1 & 0xf8) != 0xd8 ) | ||
245 | { | ||
246 | if ( byte1 == FWAIT_OPCODE ) | ||
247 | { | ||
248 | if (partial_status & SW_Summary) | ||
249 | goto do_the_FPU_interrupt; | ||
250 | else | ||
251 | goto FPU_fwait_done; | ||
252 | } | ||
253 | #ifdef PARANOID | ||
254 | EXCEPTION(EX_INTERNAL|0x128); | ||
255 | math_abort(FPU_info,SIGILL); | ||
256 | #endif /* PARANOID */ | ||
257 | } | ||
258 | |||
259 | RE_ENTRANT_CHECK_OFF; | ||
260 | FPU_code_access_ok(1); | ||
261 | FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP); | ||
262 | RE_ENTRANT_CHECK_ON; | ||
263 | FPU_EIP++; | ||
264 | |||
265 | if (partial_status & SW_Summary) | ||
266 | { | ||
267 | /* Ignore the error for now if the current instruction is a no-wait | ||
268 | control instruction */ | ||
269 | /* The 80486 manual contradicts itself on this topic, | ||
270 | but a real 80486 uses the following instructions: | ||
271 | fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex. | ||
272 | */ | ||
273 | code = (FPU_modrm << 8) | byte1; | ||
274 | if ( ! ( (((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */ | ||
275 | (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv, | ||
276 | fnstsw */ | ||
277 | ((code & 0xc000) != 0xc000))) ) ) | ||
278 | { | ||
279 | /* | ||
280 | * We need to simulate the action of the kernel to FPU | ||
281 | * interrupts here. | ||
282 | */ | ||
283 | do_the_FPU_interrupt: | ||
284 | |||
285 | FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */ | ||
286 | |||
287 | RE_ENTRANT_CHECK_OFF; | ||
288 | current->thread.trap_no = 16; | ||
289 | current->thread.error_code = 0; | ||
290 | send_sig(SIGFPE, current, 1); | ||
291 | return; | ||
292 | } | ||
293 | } | ||
294 | |||
295 | entry_sel_off.offset = FPU_ORIG_EIP; | ||
296 | entry_sel_off.selector = FPU_CS; | ||
297 | entry_sel_off.opcode = (byte1 << 8) | FPU_modrm; | ||
298 | |||
299 | FPU_rm = FPU_modrm & 7; | ||
300 | |||
301 | if ( FPU_modrm < 0300 ) | ||
302 | { | ||
303 | /* All of these instructions use the mod/rm byte to get a data address */ | ||
304 | |||
305 | if ( (addr_modes.default_mode & SIXTEEN) | ||
306 | ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX) ) | ||
307 | data_address = FPU_get_address_16(FPU_modrm, &FPU_EIP, &data_sel_off, | ||
308 | addr_modes); | ||
309 | else | ||
310 | data_address = FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off, | ||
311 | addr_modes); | ||
312 | |||
313 | if ( addr_modes.default_mode ) | ||
314 | { | ||
315 | if ( FPU_EIP-1 > code_limit ) | ||
316 | math_abort(FPU_info,SIGSEGV); | ||
317 | } | ||
318 | |||
319 | if ( !(byte1 & 1) ) | ||
320 | { | ||
321 | unsigned short status1 = partial_status; | ||
322 | |||
323 | st0_ptr = &st(0); | ||
324 | st0_tag = FPU_gettag0(); | ||
325 | |||
326 | /* Stack underflow has priority */ | ||
327 | if ( NOT_EMPTY_ST0 ) | ||
328 | { | ||
329 | if ( addr_modes.default_mode & PROTECTED ) | ||
330 | { | ||
331 | /* This table works for 16 and 32 bit protected mode */ | ||
332 | if ( access_limit < data_sizes_16[(byte1 >> 1) & 3] ) | ||
333 | math_abort(FPU_info,SIGSEGV); | ||
334 | } | ||
335 | |||
336 | unmasked = 0; /* Do this here to stop compiler warnings. */ | ||
337 | switch ( (byte1 >> 1) & 3 ) | ||
338 | { | ||
339 | case 0: | ||
340 | unmasked = FPU_load_single((float __user *)data_address, | ||
341 | &loaded_data); | ||
342 | loaded_tag = unmasked & 0xff; | ||
343 | unmasked &= ~0xff; | ||
344 | break; | ||
345 | case 1: | ||
346 | loaded_tag = FPU_load_int32((long __user *)data_address, &loaded_data); | ||
347 | break; | ||
348 | case 2: | ||
349 | unmasked = FPU_load_double((double __user *)data_address, | ||
350 | &loaded_data); | ||
351 | loaded_tag = unmasked & 0xff; | ||
352 | unmasked &= ~0xff; | ||
353 | break; | ||
354 | case 3: | ||
355 | default: /* Used here to suppress gcc warnings. */ | ||
356 | loaded_tag = FPU_load_int16((short __user *)data_address, &loaded_data); | ||
357 | break; | ||
358 | } | ||
359 | |||
360 | /* No more access to user memory, it is safe | ||
361 | to use static data now */ | ||
362 | |||
363 | /* NaN operands have the next priority. */ | ||
364 | /* We have to delay looking at st(0) until after | ||
365 | loading the data, because that data might contain an SNaN */ | ||
366 | if ( ((st0_tag == TAG_Special) && isNaN(st0_ptr)) || | ||
367 | ((loaded_tag == TAG_Special) && isNaN(&loaded_data)) ) | ||
368 | { | ||
369 | /* Restore the status word; we might have loaded a | ||
370 | denormal. */ | ||
371 | partial_status = status1; | ||
372 | if ( (FPU_modrm & 0x30) == 0x10 ) | ||
373 | { | ||
374 | /* fcom or fcomp */ | ||
375 | EXCEPTION(EX_Invalid); | ||
376 | setcc(SW_C3 | SW_C2 | SW_C0); | ||
377 | if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) | ||
378 | FPU_pop(); /* fcomp, masked, so we pop. */ | ||
379 | } | ||
380 | else | ||
381 | { | ||
382 | if ( loaded_tag == TAG_Special ) | ||
383 | loaded_tag = FPU_Special(&loaded_data); | ||
384 | #ifdef PECULIAR_486 | ||
385 | /* This is not really needed, but gives behaviour | ||
386 | identical to an 80486 */ | ||
387 | if ( (FPU_modrm & 0x28) == 0x20 ) | ||
388 | /* fdiv or fsub */ | ||
389 | real_2op_NaN(&loaded_data, loaded_tag, 0, &loaded_data); | ||
390 | else | ||
391 | #endif /* PECULIAR_486 */ | ||
392 | /* fadd, fdivr, fmul, or fsubr */ | ||
393 | real_2op_NaN(&loaded_data, loaded_tag, 0, st0_ptr); | ||
394 | } | ||
395 | goto reg_mem_instr_done; | ||
396 | } | ||
397 | |||
398 | if ( unmasked && !((FPU_modrm & 0x30) == 0x10) ) | ||
399 | { | ||
400 | /* Is not a comparison instruction. */ | ||
401 | if ( (FPU_modrm & 0x38) == 0x38 ) | ||
402 | { | ||
403 | /* fdivr */ | ||
404 | if ( (st0_tag == TAG_Zero) && | ||
405 | ((loaded_tag == TAG_Valid) | ||
406 | || (loaded_tag == TAG_Special | ||
407 | && isdenormal(&loaded_data))) ) | ||
408 | { | ||
409 | if ( FPU_divide_by_zero(0, getsign(&loaded_data)) | ||
410 | < 0 ) | ||
411 | { | ||
412 | /* We use the fact here that the unmasked | ||
413 | exception in the loaded data was for a | ||
414 | denormal operand */ | ||
415 | /* Restore the state of the denormal op bit */ | ||
416 | partial_status &= ~SW_Denorm_Op; | ||
417 | partial_status |= status1 & SW_Denorm_Op; | ||
418 | } | ||
419 | else | ||
420 | setsign(st0_ptr, getsign(&loaded_data)); | ||
421 | } | ||
422 | } | ||
423 | goto reg_mem_instr_done; | ||
424 | } | ||
425 | |||
426 | switch ( (FPU_modrm >> 3) & 7 ) | ||
427 | { | ||
428 | case 0: /* fadd */ | ||
429 | clear_C1(); | ||
430 | FPU_add(&loaded_data, loaded_tag, 0, control_word); | ||
431 | break; | ||
432 | case 1: /* fmul */ | ||
433 | clear_C1(); | ||
434 | FPU_mul(&loaded_data, loaded_tag, 0, control_word); | ||
435 | break; | ||
436 | case 2: /* fcom */ | ||
437 | FPU_compare_st_data(&loaded_data, loaded_tag); | ||
438 | break; | ||
439 | case 3: /* fcomp */ | ||
440 | if ( !FPU_compare_st_data(&loaded_data, loaded_tag) | ||
441 | && !unmasked ) | ||
442 | FPU_pop(); | ||
443 | break; | ||
444 | case 4: /* fsub */ | ||
445 | clear_C1(); | ||
446 | FPU_sub(LOADED|loaded_tag, (int)&loaded_data, control_word); | ||
447 | break; | ||
448 | case 5: /* fsubr */ | ||
449 | clear_C1(); | ||
450 | FPU_sub(REV|LOADED|loaded_tag, (int)&loaded_data, control_word); | ||
451 | break; | ||
452 | case 6: /* fdiv */ | ||
453 | clear_C1(); | ||
454 | FPU_div(LOADED|loaded_tag, (int)&loaded_data, control_word); | ||
455 | break; | ||
456 | case 7: /* fdivr */ | ||
457 | clear_C1(); | ||
458 | if ( st0_tag == TAG_Zero ) | ||
459 | partial_status = status1; /* Undo any denorm tag, | ||
460 | zero-divide has priority. */ | ||
461 | FPU_div(REV|LOADED|loaded_tag, (int)&loaded_data, control_word); | ||
462 | break; | ||
463 | } | ||
464 | } | ||
465 | else | ||
466 | { | ||
467 | if ( (FPU_modrm & 0x30) == 0x10 ) | ||
468 | { | ||
469 | /* The instruction is fcom or fcomp */ | ||
470 | EXCEPTION(EX_StackUnder); | ||
471 | setcc(SW_C3 | SW_C2 | SW_C0); | ||
472 | if ( (FPU_modrm & 0x08) && (control_word & CW_Invalid) ) | ||
473 | FPU_pop(); /* fcomp */ | ||
474 | } | ||
475 | else | ||
476 | FPU_stack_underflow(); | ||
477 | } | ||
478 | reg_mem_instr_done: | ||
479 | operand_address = data_sel_off; | ||
480 | } | ||
481 | else | ||
482 | { | ||
483 | if ( !(no_ip_update = | ||
484 | FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6)) >> 1, | ||
485 | addr_modes, data_address)) ) | ||
486 | { | ||
487 | operand_address = data_sel_off; | ||
488 | } | ||
489 | } | ||
490 | |||
491 | } | ||
492 | else | ||
493 | { | ||
494 | /* None of these instructions access user memory */ | ||
495 | u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7); | ||
496 | |||
497 | #ifdef PECULIAR_486 | ||
498 | /* This is supposed to be undefined, but a real 80486 seems | ||
499 | to do this: */ | ||
500 | operand_address.offset = 0; | ||
501 | operand_address.selector = FPU_DS; | ||
502 | #endif /* PECULIAR_486 */ | ||
503 | |||
504 | st0_ptr = &st(0); | ||
505 | st0_tag = FPU_gettag0(); | ||
506 | switch ( type_table[(int) instr_index] ) | ||
507 | { | ||
508 | case _NONE_: /* also _REGIc: _REGIn */ | ||
509 | break; | ||
510 | case _REG0_: | ||
511 | if ( !NOT_EMPTY_ST0 ) | ||
512 | { | ||
513 | FPU_stack_underflow(); | ||
514 | goto FPU_instruction_done; | ||
515 | } | ||
516 | break; | ||
517 | case _REGIi: | ||
518 | if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) | ||
519 | { | ||
520 | FPU_stack_underflow_i(FPU_rm); | ||
521 | goto FPU_instruction_done; | ||
522 | } | ||
523 | break; | ||
524 | case _REGIp: | ||
525 | if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) | ||
526 | { | ||
527 | FPU_stack_underflow_pop(FPU_rm); | ||
528 | goto FPU_instruction_done; | ||
529 | } | ||
530 | break; | ||
531 | case _REGI_: | ||
532 | if ( !NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm) ) | ||
533 | { | ||
534 | FPU_stack_underflow(); | ||
535 | goto FPU_instruction_done; | ||
536 | } | ||
537 | break; | ||
538 | case _PUSH_: /* Only used by the fld st(i) instruction */ | ||
539 | break; | ||
540 | case _null_: | ||
541 | FPU_illegal(); | ||
542 | goto FPU_instruction_done; | ||
543 | default: | ||
544 | EXCEPTION(EX_INTERNAL|0x111); | ||
545 | goto FPU_instruction_done; | ||
546 | } | ||
547 | (*st_instr_table[(int) instr_index])(); | ||
548 | |||
549 | FPU_instruction_done: | ||
550 | ; | ||
551 | } | ||
552 | |||
553 | if ( ! no_ip_update ) | ||
554 | instruction_address = entry_sel_off; | ||
555 | |||
556 | FPU_fwait_done: | ||
557 | |||
558 | #ifdef DEBUG | ||
559 | RE_ENTRANT_CHECK_OFF; | ||
560 | FPU_printall(); | ||
561 | RE_ENTRANT_CHECK_ON; | ||
562 | #endif /* DEBUG */ | ||
563 | |||
564 | if (FPU_lookahead && !need_resched()) | ||
565 | { | ||
566 | FPU_ORIG_EIP = FPU_EIP - code_base; | ||
567 | if ( valid_prefix(&byte1, (u_char __user **)&FPU_EIP, | ||
568 | &addr_modes.override) ) | ||
569 | goto do_another_FPU_instruction; | ||
570 | } | ||
571 | |||
572 | if ( addr_modes.default_mode ) | ||
573 | FPU_EIP -= code_base; | ||
574 | |||
575 | RE_ENTRANT_CHECK_OFF; | ||
576 | } | ||
577 | |||
578 | |||
579 | /* Support for prefix bytes is not yet complete. To properly handle | ||
580 | all prefix bytes, further changes are needed in the emulator code | ||
581 | which accesses user address space. Access to separate segments is | ||
582 | important for msdos emulation. */ | ||
583 | static int valid_prefix(u_char *Byte, u_char __user **fpu_eip, | ||
584 | overrides *override) | ||
585 | { | ||
586 | u_char byte; | ||
587 | u_char __user *ip = *fpu_eip; | ||
588 | |||
589 | *override = (overrides) { 0, 0, PREFIX_DEFAULT }; /* defaults */ | ||
590 | |||
591 | RE_ENTRANT_CHECK_OFF; | ||
592 | FPU_code_access_ok(1); | ||
593 | FPU_get_user(byte, ip); | ||
594 | RE_ENTRANT_CHECK_ON; | ||
595 | |||
596 | while ( 1 ) | ||
597 | { | ||
598 | switch ( byte ) | ||
599 | { | ||
600 | case ADDR_SIZE_PREFIX: | ||
601 | override->address_size = ADDR_SIZE_PREFIX; | ||
602 | goto do_next_byte; | ||
603 | |||
604 | case OP_SIZE_PREFIX: | ||
605 | override->operand_size = OP_SIZE_PREFIX; | ||
606 | goto do_next_byte; | ||
607 | |||
608 | case PREFIX_CS: | ||
609 | override->segment = PREFIX_CS_; | ||
610 | goto do_next_byte; | ||
611 | case PREFIX_ES: | ||
612 | override->segment = PREFIX_ES_; | ||
613 | goto do_next_byte; | ||
614 | case PREFIX_SS: | ||
615 | override->segment = PREFIX_SS_; | ||
616 | goto do_next_byte; | ||
617 | case PREFIX_FS: | ||
618 | override->segment = PREFIX_FS_; | ||
619 | goto do_next_byte; | ||
620 | case PREFIX_GS: | ||
621 | override->segment = PREFIX_GS_; | ||
622 | goto do_next_byte; | ||
623 | case PREFIX_DS: | ||
624 | override->segment = PREFIX_DS_; | ||
625 | goto do_next_byte; | ||
626 | |||
627 | /* lock is not a valid prefix for FPU instructions, | ||
628 | let the cpu handle it to generate a SIGILL. */ | ||
629 | /* case PREFIX_LOCK: */ | ||
630 | |||
631 | /* rep.. prefixes have no meaning for FPU instructions */ | ||
632 | case PREFIX_REPE: | ||
633 | case PREFIX_REPNE: | ||
634 | |||
635 | do_next_byte: | ||
636 | ip++; | ||
637 | RE_ENTRANT_CHECK_OFF; | ||
638 | FPU_code_access_ok(1); | ||
639 | FPU_get_user(byte, ip); | ||
640 | RE_ENTRANT_CHECK_ON; | ||
641 | break; | ||
642 | case FWAIT_OPCODE: | ||
643 | *Byte = byte; | ||
644 | return 1; | ||
645 | default: | ||
646 | if ( (byte & 0xf8) == 0xd8 ) | ||
647 | { | ||
648 | *Byte = byte; | ||
649 | *fpu_eip = ip; | ||
650 | return 1; | ||
651 | } | ||
652 | else | ||
653 | { | ||
654 | /* Not a valid sequence of prefix bytes followed by | ||
655 | an FPU instruction. */ | ||
656 | *Byte = byte; /* Needed for error message. */ | ||
657 | return 0; | ||
658 | } | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | |||
663 | |||
664 | void math_abort(struct info * info, unsigned int signal) | ||
665 | { | ||
666 | FPU_EIP = FPU_ORIG_EIP; | ||
667 | current->thread.trap_no = 16; | ||
668 | current->thread.error_code = 0; | ||
669 | send_sig(signal,current,1); | ||
670 | RE_ENTRANT_CHECK_OFF; | ||
671 | __asm__("movl %0,%%esp ; ret": :"g" (((long) info)-4)); | ||
672 | #ifdef PARANOID | ||
673 | printk("ERROR: wm-FPU-emu math_abort failed!\n"); | ||
674 | #endif /* PARANOID */ | ||
675 | } | ||
676 | |||
677 | |||
678 | |||
679 | #define S387 ((struct i387_soft_struct *)s387) | ||
680 | #define sstatus_word() \ | ||
681 | ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top)) | ||
682 | |||
683 | int restore_i387_soft(void *s387, struct _fpstate __user *buf) | ||
684 | { | ||
685 | u_char __user *d = (u_char __user *)buf; | ||
686 | int offset, other, i, tags, regnr, tag, newtop; | ||
687 | |||
688 | RE_ENTRANT_CHECK_OFF; | ||
689 | FPU_access_ok(VERIFY_READ, d, 7*4 + 8*10); | ||
690 | if (__copy_from_user(&S387->cwd, d, 7*4)) | ||
691 | return -1; | ||
692 | RE_ENTRANT_CHECK_ON; | ||
693 | |||
694 | d += 7*4; | ||
695 | |||
696 | S387->ftop = (S387->swd >> SW_Top_Shift) & 7; | ||
697 | offset = (S387->ftop & 7) * 10; | ||
698 | other = 80 - offset; | ||
699 | |||
700 | RE_ENTRANT_CHECK_OFF; | ||
701 | /* Copy all registers in stack order. */ | ||
702 | if (__copy_from_user(((u_char *)&S387->st_space)+offset, d, other)) | ||
703 | return -1; | ||
704 | if ( offset ) | ||
705 | if (__copy_from_user((u_char *)&S387->st_space, d+other, offset)) | ||
706 | return -1; | ||
707 | RE_ENTRANT_CHECK_ON; | ||
708 | |||
709 | /* The tags may need to be corrected now. */ | ||
710 | tags = S387->twd; | ||
711 | newtop = S387->ftop; | ||
712 | for ( i = 0; i < 8; i++ ) | ||
713 | { | ||
714 | regnr = (i+newtop) & 7; | ||
715 | if ( ((tags >> ((regnr & 7)*2)) & 3) != TAG_Empty ) | ||
716 | { | ||
717 | /* The loaded data over-rides all other cases. */ | ||
718 | tag = FPU_tagof((FPU_REG *)((u_char *)S387->st_space + 10*regnr)); | ||
719 | tags &= ~(3 << (regnr*2)); | ||
720 | tags |= (tag & 3) << (regnr*2); | ||
721 | } | ||
722 | } | ||
723 | S387->twd = tags; | ||
724 | |||
725 | return 0; | ||
726 | } | ||
727 | |||
728 | |||
729 | int save_i387_soft(void *s387, struct _fpstate __user * buf) | ||
730 | { | ||
731 | u_char __user *d = (u_char __user *)buf; | ||
732 | int offset = (S387->ftop & 7) * 10, other = 80 - offset; | ||
733 | |||
734 | RE_ENTRANT_CHECK_OFF; | ||
735 | FPU_access_ok(VERIFY_WRITE, d, 7*4 + 8*10); | ||
736 | #ifdef PECULIAR_486 | ||
737 | S387->cwd &= ~0xe080; | ||
738 | /* An 80486 sets nearly all of the reserved bits to 1. */ | ||
739 | S387->cwd |= 0xffff0040; | ||
740 | S387->swd = sstatus_word() | 0xffff0000; | ||
741 | S387->twd |= 0xffff0000; | ||
742 | S387->fcs &= ~0xf8000000; | ||
743 | S387->fos |= 0xffff0000; | ||
744 | #endif /* PECULIAR_486 */ | ||
745 | __copy_to_user(d, &S387->cwd, 7*4); | ||
746 | RE_ENTRANT_CHECK_ON; | ||
747 | |||
748 | d += 7*4; | ||
749 | |||
750 | RE_ENTRANT_CHECK_OFF; | ||
751 | /* Copy all registers in stack order. */ | ||
752 | if (__copy_to_user(d, ((u_char *)&S387->st_space)+offset, other)) | ||
753 | return -1; | ||
754 | if ( offset ) | ||
755 | if (__copy_to_user(d+other, (u_char *)&S387->st_space, offset)) | ||
756 | return -1 | ||
757 | RE_ENTRANT_CHECK_ON; | ||
758 | |||
759 | return 1; | ||
760 | } | ||