blob: cfbdaa1532ce98af8997198d7f71f8a744814ad1 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*---------------------------------------------------------------------------+
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>
Roland McGrathff0ebb22008-01-30 13:31:49 +010028#include <linux/regset.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
30#include <asm/uaccess.h>
31#include <asm/desc.h>
Roland McGrathff0ebb22008-01-30 13:31:49 +010032#include <asm/user.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34#include "fpu_system.h"
35#include "fpu_emu.h"
36#include "exception.h"
37#include "control_w.h"
38#include "status_w.h"
39
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010040#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010042#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44/* WARNING: These codes are not documented by Intel in their 80486 manual
45 and may not work on FPU clones or later Intel FPUs. */
46
47/* Changes to support the un-doc codes provided by Linus Torvalds. */
48
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010049#define _d9_d8_ fstp_i /* unofficial code (19) */
50#define _dc_d0_ fcom_st /* unofficial code (14) */
51#define _dc_d8_ fcompst /* unofficial code (1c) */
52#define _dd_c8_ fxch_i /* unofficial code (0d) */
53#define _de_d0_ fcompst /* unofficial code (16) */
54#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
55#define _df_c8_ fxch_i /* unofficial code (0f) */
56#define _df_d0_ fstp_i /* unofficial code (17) */
57#define _df_d8_ fstp_i /* unofficial code (1f) */
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59static FUNC const st_instr_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010060 fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
61 fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
62 fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
63 fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
64 fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
65 fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
66 fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
67 fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
Linus Torvalds1da177e2005-04-16 15:20:36 -070068};
69
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010070#else /* Support only documented FPU op-codes */
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
72static FUNC const st_instr_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010073 fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
74 fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
75 fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
76 fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
77 fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
78 fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
79 fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
80 fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
Linus Torvalds1da177e2005-04-16 15:20:36 -070081};
82
83#endif /* NO_UNDOC_CODE */
84
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010085#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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
96#ifndef NO_UNDOC_CODE
97
98/* Un-documented FPU op-codes supported by default. (see above) */
99
100static u_char const type_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100101 _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_
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109};
110
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100111#else /* Support only documented FPU op-codes */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
113static u_char const type_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100114 _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_
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122};
123
124#endif /* NO_UNDOC_CODE */
125
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126#ifdef RE_ENTRANT_CHECKING
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100127u_char emulating = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128#endif /* RE_ENTRANT_CHECKING */
129
Ingo Molnare8d591d2008-01-30 13:30:12 +0100130static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100131 overrides * override);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133asmlinkage void math_emulate(long arg)
134{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100135 u_char FPU_modrm, byte1;
136 unsigned short code;
137 fpu_addr_modes addr_modes;
138 int unmasked;
139 FPU_REG loaded_data;
140 FPU_REG *st0_ptr;
141 u_char loaded_tag, st0_tag;
142 void __user *data_address;
143 struct address data_sel_off;
144 struct address entry_sel_off;
145 unsigned long code_base = 0;
146 unsigned long code_limit = 0; /* Initialized to stop compiler warnings */
147 struct desc_struct code_descriptor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
149#ifdef RE_ENTRANT_CHECKING
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100150 if (emulating) {
151 printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
152 }
153 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154#endif /* RE_ENTRANT_CHECKING */
155
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100156 if (!used_math()) {
157 finit();
158 set_used_math();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 }
160
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100161 SETUP_DATA_AREA(arg);
162
163 FPU_ORIG_EIP = FPU_EIP;
164
165 if ((FPU_EFLAGS & 0x00020000) != 0) {
166 /* Virtual 8086 mode */
167 addr_modes.default_mode = VM86;
168 FPU_EIP += code_base = FPU_CS << 4;
169 code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */
170 } else if (FPU_CS == __USER_CS && FPU_DS == __USER_DS) {
171 addr_modes.default_mode = 0;
172 } else if (FPU_CS == __KERNEL_CS) {
173 printk("math_emulate: %04x:%08lx\n", FPU_CS, FPU_EIP);
174 panic("Math emulation needed in kernel");
175 } else {
176
177 if ((FPU_CS & 4) != 4) { /* Must be in the LDT */
178 /* Can only handle segmented addressing via the LDT
179 for now, and it must be 16 bit */
180 printk("FPU emulator: Unsupported addressing mode\n");
181 math_abort(FPU_info, SIGILL);
182 }
183
184 code_descriptor = LDT_DESCRIPTOR(FPU_CS);
185 if (SEG_D_SIZE(code_descriptor)) {
186 /* The above test may be wrong, the book is not clear */
187 /* Segmented 32 bit protected mode */
188 addr_modes.default_mode = SEG32;
189 } else {
190 /* 16 bit protected mode */
191 addr_modes.default_mode = PM16;
192 }
193 FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor);
194 code_limit = code_base
195 + (SEG_LIMIT(code_descriptor) +
196 1) * SEG_GRANULARITY(code_descriptor)
197 - 1;
198 if (code_limit < code_base)
199 code_limit = 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100201
Roland McGrathff0ebb22008-01-30 13:31:49 +0100202 FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100203
204 if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
205 &addr_modes.override)) {
206 RE_ENTRANT_CHECK_OFF;
207 printk
208 ("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
209 "FPU emulator: self-modifying code! (emulation impossible)\n",
210 byte1);
211 RE_ENTRANT_CHECK_ON;
212 EXCEPTION(EX_INTERNAL | 0x126);
213 math_abort(FPU_info, SIGILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100216 do_another_FPU_instruction:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100218 no_ip_update = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100220 FPU_EIP++; /* We have fetched the prefix and first code bytes. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100222 if (addr_modes.default_mode) {
223 /* This checks for the minimum instruction bytes.
224 We also need to check any extra (address mode) code access. */
225 if (FPU_EIP > code_limit)
226 math_abort(FPU_info, SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100228
229 if ((byte1 & 0xf8) != 0xd8) {
230 if (byte1 == FWAIT_OPCODE) {
231 if (partial_status & SW_Summary)
232 goto do_the_FPU_interrupt;
233 else
234 goto FPU_fwait_done;
235 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100237 EXCEPTION(EX_INTERNAL | 0x128);
238 math_abort(FPU_info, SIGILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239#endif /* PARANOID */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 }
241
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100242 RE_ENTRANT_CHECK_OFF;
243 FPU_code_access_ok(1);
244 FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP);
245 RE_ENTRANT_CHECK_ON;
246 FPU_EIP++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100248 if (partial_status & SW_Summary) {
249 /* Ignore the error for now if the current instruction is a no-wait
250 control instruction */
251 /* The 80486 manual contradicts itself on this topic,
252 but a real 80486 uses the following instructions:
253 fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
254 */
255 code = (FPU_modrm << 8) | byte1;
256 if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
257 (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
258 fnstsw */
259 ((code & 0xc000) != 0xc000))))) {
260 /*
261 * We need to simulate the action of the kernel to FPU
262 * interrupts here.
263 */
264 do_the_FPU_interrupt:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100266 FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */
267
268 RE_ENTRANT_CHECK_OFF;
269 current->thread.trap_no = 16;
270 current->thread.error_code = 0;
271 send_sig(SIGFPE, current, 1);
272 return;
273 }
274 }
275
276 entry_sel_off.offset = FPU_ORIG_EIP;
277 entry_sel_off.selector = FPU_CS;
278 entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
279
280 FPU_rm = FPU_modrm & 7;
281
282 if (FPU_modrm < 0300) {
283 /* All of these instructions use the mod/rm byte to get a data address */
284
285 if ((addr_modes.default_mode & SIXTEEN)
286 ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
287 data_address =
288 FPU_get_address_16(FPU_modrm, &FPU_EIP,
289 &data_sel_off, addr_modes);
290 else
291 data_address =
292 FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
293 addr_modes);
294
295 if (addr_modes.default_mode) {
296 if (FPU_EIP - 1 > code_limit)
297 math_abort(FPU_info, SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 }
299
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100300 if (!(byte1 & 1)) {
301 unsigned short status1 = partial_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100303 st0_ptr = &st(0);
304 st0_tag = FPU_gettag0();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100306 /* Stack underflow has priority */
307 if (NOT_EMPTY_ST0) {
308 if (addr_modes.default_mode & PROTECTED) {
309 /* This table works for 16 and 32 bit protected mode */
310 if (access_limit <
311 data_sizes_16[(byte1 >> 1) & 3])
312 math_abort(FPU_info, SIGSEGV);
313 }
314
315 unmasked = 0; /* Do this here to stop compiler warnings. */
316 switch ((byte1 >> 1) & 3) {
317 case 0:
318 unmasked =
319 FPU_load_single((float __user *)
320 data_address,
321 &loaded_data);
322 loaded_tag = unmasked & 0xff;
323 unmasked &= ~0xff;
324 break;
325 case 1:
326 loaded_tag =
327 FPU_load_int32((long __user *)
328 data_address,
329 &loaded_data);
330 break;
331 case 2:
332 unmasked =
333 FPU_load_double((double __user *)
334 data_address,
335 &loaded_data);
336 loaded_tag = unmasked & 0xff;
337 unmasked &= ~0xff;
338 break;
339 case 3:
340 default: /* Used here to suppress gcc warnings. */
341 loaded_tag =
342 FPU_load_int16((short __user *)
343 data_address,
344 &loaded_data);
345 break;
346 }
347
348 /* No more access to user memory, it is safe
349 to use static data now */
350
351 /* NaN operands have the next priority. */
352 /* We have to delay looking at st(0) until after
353 loading the data, because that data might contain an SNaN */
354 if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
355 || ((loaded_tag == TAG_Special)
356 && isNaN(&loaded_data))) {
357 /* Restore the status word; we might have loaded a
358 denormal. */
359 partial_status = status1;
360 if ((FPU_modrm & 0x30) == 0x10) {
361 /* fcom or fcomp */
362 EXCEPTION(EX_Invalid);
363 setcc(SW_C3 | SW_C2 | SW_C0);
364 if ((FPU_modrm & 0x08)
365 && (control_word &
366 CW_Invalid))
367 FPU_pop(); /* fcomp, masked, so we pop. */
368 } else {
369 if (loaded_tag == TAG_Special)
370 loaded_tag =
371 FPU_Special
372 (&loaded_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100374 /* This is not really needed, but gives behaviour
375 identical to an 80486 */
376 if ((FPU_modrm & 0x28) == 0x20)
377 /* fdiv or fsub */
378 real_2op_NaN
379 (&loaded_data,
380 loaded_tag, 0,
381 &loaded_data);
382 else
383#endif /* PECULIAR_486 */
384 /* fadd, fdivr, fmul, or fsubr */
385 real_2op_NaN
386 (&loaded_data,
387 loaded_tag, 0,
388 st0_ptr);
389 }
390 goto reg_mem_instr_done;
391 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100393 if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
394 /* Is not a comparison instruction. */
395 if ((FPU_modrm & 0x38) == 0x38) {
396 /* fdivr */
397 if ((st0_tag == TAG_Zero) &&
398 ((loaded_tag == TAG_Valid)
399 || (loaded_tag ==
400 TAG_Special
401 &&
402 isdenormal
403 (&loaded_data)))) {
404 if (FPU_divide_by_zero
405 (0,
406 getsign
407 (&loaded_data))
408 < 0) {
409 /* We use the fact here that the unmasked
410 exception in the loaded data was for a
411 denormal operand */
412 /* Restore the state of the denormal op bit */
413 partial_status
414 &=
415 ~SW_Denorm_Op;
416 partial_status
417 |=
418 status1 &
419 SW_Denorm_Op;
420 } else
421 setsign(st0_ptr,
422 getsign
423 (&loaded_data));
424 }
425 }
426 goto reg_mem_instr_done;
427 }
428
429 switch ((FPU_modrm >> 3) & 7) {
430 case 0: /* fadd */
431 clear_C1();
432 FPU_add(&loaded_data, loaded_tag, 0,
433 control_word);
434 break;
435 case 1: /* fmul */
436 clear_C1();
437 FPU_mul(&loaded_data, loaded_tag, 0,
438 control_word);
439 break;
440 case 2: /* fcom */
441 FPU_compare_st_data(&loaded_data,
442 loaded_tag);
443 break;
444 case 3: /* fcomp */
445 if (!FPU_compare_st_data
446 (&loaded_data, loaded_tag)
447 && !unmasked)
448 FPU_pop();
449 break;
450 case 4: /* fsub */
451 clear_C1();
452 FPU_sub(LOADED | loaded_tag,
453 (int)&loaded_data,
454 control_word);
455 break;
456 case 5: /* fsubr */
457 clear_C1();
458 FPU_sub(REV | LOADED | loaded_tag,
459 (int)&loaded_data,
460 control_word);
461 break;
462 case 6: /* fdiv */
463 clear_C1();
464 FPU_div(LOADED | loaded_tag,
465 (int)&loaded_data,
466 control_word);
467 break;
468 case 7: /* fdivr */
469 clear_C1();
470 if (st0_tag == TAG_Zero)
471 partial_status = status1; /* Undo any denorm tag,
472 zero-divide has priority. */
473 FPU_div(REV | LOADED | loaded_tag,
474 (int)&loaded_data,
475 control_word);
476 break;
477 }
478 } else {
479 if ((FPU_modrm & 0x30) == 0x10) {
480 /* The instruction is fcom or fcomp */
481 EXCEPTION(EX_StackUnder);
482 setcc(SW_C3 | SW_C2 | SW_C0);
483 if ((FPU_modrm & 0x08)
484 && (control_word & CW_Invalid))
485 FPU_pop(); /* fcomp */
486 } else
487 FPU_stack_underflow();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100489 reg_mem_instr_done:
490 operand_address = data_sel_off;
491 } else {
492 if (!(no_ip_update =
493 FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
494 >> 1, addr_modes, data_address))) {
495 operand_address = data_sel_off;
496 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 }
498
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100499 } else {
500 /* None of these instructions access user memory */
501 u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100504 /* This is supposed to be undefined, but a real 80486 seems
505 to do this: */
506 operand_address.offset = 0;
507 operand_address.selector = FPU_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508#endif /* PECULIAR_486 */
509
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100510 st0_ptr = &st(0);
511 st0_tag = FPU_gettag0();
512 switch (type_table[(int)instr_index]) {
513 case _NONE_: /* also _REGIc: _REGIn */
514 break;
515 case _REG0_:
516 if (!NOT_EMPTY_ST0) {
517 FPU_stack_underflow();
518 goto FPU_instruction_done;
519 }
520 break;
521 case _REGIi:
522 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
523 FPU_stack_underflow_i(FPU_rm);
524 goto FPU_instruction_done;
525 }
526 break;
527 case _REGIp:
528 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
529 FPU_stack_underflow_pop(FPU_rm);
530 goto FPU_instruction_done;
531 }
532 break;
533 case _REGI_:
534 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
535 FPU_stack_underflow();
536 goto FPU_instruction_done;
537 }
538 break;
539 case _PUSH_: /* Only used by the fld st(i) instruction */
540 break;
541 case _null_:
542 FPU_illegal();
543 goto FPU_instruction_done;
544 default:
545 EXCEPTION(EX_INTERNAL | 0x111);
546 goto FPU_instruction_done;
547 }
548 (*st_instr_table[(int)instr_index]) ();
549
550 FPU_instruction_done:
551 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100554 if (!no_ip_update)
555 instruction_address = entry_sel_off;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100557 FPU_fwait_done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
559#ifdef DEBUG
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100560 RE_ENTRANT_CHECK_OFF;
561 FPU_printall();
562 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563#endif /* DEBUG */
564
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100565 if (FPU_lookahead && !need_resched()) {
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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100572 if (addr_modes.default_mode)
573 FPU_EIP -= code_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100575 RE_ENTRANT_CHECK_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576}
577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578/* Support for prefix bytes is not yet complete. To properly handle
579 all prefix bytes, further changes are needed in the emulator code
580 which accesses user address space. Access to separate segments is
581 important for msdos emulation. */
Ingo Molnare8d591d2008-01-30 13:30:12 +0100582static int valid_prefix(u_char *Byte, u_char __user **fpu_eip,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100583 overrides * override)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100585 u_char byte;
586 u_char __user *ip = *fpu_eip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100588 *override = (overrides) {
589 0, 0, PREFIX_DEFAULT}; /* defaults */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100591 RE_ENTRANT_CHECK_OFF;
592 FPU_code_access_ok(1);
593 FPU_get_user(byte, ip);
594 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100596 while (1) {
597 switch (byte) {
598 case ADDR_SIZE_PREFIX:
599 override->address_size = ADDR_SIZE_PREFIX;
600 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100602 case OP_SIZE_PREFIX:
603 override->operand_size = OP_SIZE_PREFIX;
604 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100606 case PREFIX_CS:
607 override->segment = PREFIX_CS_;
608 goto do_next_byte;
609 case PREFIX_ES:
610 override->segment = PREFIX_ES_;
611 goto do_next_byte;
612 case PREFIX_SS:
613 override->segment = PREFIX_SS_;
614 goto do_next_byte;
615 case PREFIX_FS:
616 override->segment = PREFIX_FS_;
617 goto do_next_byte;
618 case PREFIX_GS:
619 override->segment = PREFIX_GS_;
620 goto do_next_byte;
621 case PREFIX_DS:
622 override->segment = PREFIX_DS_;
623 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625/* lock is not a valid prefix for FPU instructions,
626 let the cpu handle it to generate a SIGILL. */
627/* case PREFIX_LOCK: */
628
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100629 /* rep.. prefixes have no meaning for FPU instructions */
630 case PREFIX_REPE:
631 case PREFIX_REPNE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100633 do_next_byte:
634 ip++;
635 RE_ENTRANT_CHECK_OFF;
636 FPU_code_access_ok(1);
637 FPU_get_user(byte, ip);
638 RE_ENTRANT_CHECK_ON;
639 break;
640 case FWAIT_OPCODE:
641 *Byte = byte;
642 return 1;
643 default:
644 if ((byte & 0xf8) == 0xd8) {
645 *Byte = byte;
646 *fpu_eip = ip;
647 return 1;
648 } else {
649 /* Not a valid sequence of prefix bytes followed by
650 an FPU instruction. */
651 *Byte = byte; /* Needed for error message. */
652 return 0;
653 }
654 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656}
657
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100658void math_abort(struct info *info, unsigned int signal)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
660 FPU_EIP = FPU_ORIG_EIP;
661 current->thread.trap_no = 16;
662 current->thread.error_code = 0;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100663 send_sig(signal, current, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 RE_ENTRANT_CHECK_OFF;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100665 __asm__("movl %0,%%esp ; ret": :"g"(((long)info) - 4));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100667 printk("ERROR: wm-FPU-emu math_abort failed!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668#endif /* PARANOID */
669}
670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671#define S387 ((struct i387_soft_struct *)s387)
672#define sstatus_word() \
673 ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
674
Roland McGrathff0ebb22008-01-30 13:31:49 +0100675int fpregs_soft_set(struct task_struct *target,
676 const struct user_regset *regset,
677 unsigned int pos, unsigned int count,
678 const void *kbuf, const void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679{
Roland McGrathff0ebb22008-01-30 13:31:49 +0100680 struct i387_soft_struct *s387 = &target->thread.i387.soft;
681 void *space = s387->st_space;
682 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100683 int offset, other, i, tags, regnr, tag, newtop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100685 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100686 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
687 offsetof(struct i387_soft_struct, st_space));
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100688 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
Roland McGrathff0ebb22008-01-30 13:31:49 +0100690 if (ret)
691 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100693 S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
694 offset = (S387->ftop & 7) * 10;
695 other = 80 - offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100697 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100698
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100699 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100700 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
701 space + offset, 0, other);
702 if (!ret && offset)
703 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
704 space, 0, offset);
705
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100706 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100708 /* The tags may need to be corrected now. */
709 tags = S387->twd;
710 newtop = S387->ftop;
711 for (i = 0; i < 8; i++) {
712 regnr = (i + newtop) & 7;
713 if (((tags >> ((regnr & 7) * 2)) & 3) != TAG_Empty) {
714 /* The loaded data over-rides all other cases. */
715 tag =
716 FPU_tagof((FPU_REG *) ((u_char *) S387->st_space +
717 10 * regnr));
718 tags &= ~(3 << (regnr * 2));
719 tags |= (tag & 3) << (regnr * 2);
720 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100722 S387->twd = tags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
Roland McGrathff0ebb22008-01-30 13:31:49 +0100724 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725}
726
Roland McGrathff0ebb22008-01-30 13:31:49 +0100727int fpregs_soft_get(struct task_struct *target,
728 const struct user_regset *regset,
729 unsigned int pos, unsigned int count,
730 void *kbuf, void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731{
Roland McGrathff0ebb22008-01-30 13:31:49 +0100732 struct i387_soft_struct *s387 = &target->thread.i387.soft;
733 const void *space = s387->st_space;
734 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100735 int offset = (S387->ftop & 7) * 10, other = 80 - offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100737 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100740 S387->cwd &= ~0xe080;
741 /* An 80486 sets nearly all of the reserved bits to 1. */
742 S387->cwd |= 0xffff0040;
743 S387->swd = sstatus_word() | 0xffff0000;
744 S387->twd |= 0xffff0000;
745 S387->fcs &= ~0xf8000000;
746 S387->fos |= 0xffff0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747#endif /* PECULIAR_486 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Roland McGrathff0ebb22008-01-30 13:31:49 +0100749 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0,
750 offsetof(struct i387_soft_struct, st_space));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100752 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100753 if (!ret)
754 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
755 space + offset, 0, other);
756 if (!ret)
757 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
758 space, 0, offset);
759
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100760 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Roland McGrathff0ebb22008-01-30 13:31:49 +0100762 return ret;
763}
764
765int save_i387_soft(void *s387, struct _fpstate __user *buf)
766{
767 return fpregs_soft_get(current, NULL,
768 0, sizeof(struct user_i387_struct),
769 NULL, buf) ? -1 : 1;
770}
771
772int restore_i387_soft(void *s387, struct _fpstate __user *buf)
773{
774 return fpregs_soft_set(current, NULL,
775 0, sizeof(struct user_i387_struct),
776 NULL, buf) ? -1 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777}