blob: 9b868124128d79699d6325dc579908bc575c3f4a [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>
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +053031#include <asm/traps.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <asm/desc.h>
Roland McGrathff0ebb22008-01-30 13:31:49 +010033#include <asm/user.h>
Suresh Siddhae8a496a2008-05-23 16:26:37 -070034#include <asm/i387.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
36#include "fpu_system.h"
37#include "fpu_emu.h"
38#include "exception.h"
39#include "control_w.h"
40#include "status_w.h"
41
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010042#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010044#ifndef NO_UNDOC_CODE /* Un-documented FPU op-codes supported by default. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
46/* WARNING: These codes are not documented by Intel in their 80486 manual
47 and may not work on FPU clones or later Intel FPUs. */
48
49/* Changes to support the un-doc codes provided by Linus Torvalds. */
50
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010051#define _d9_d8_ fstp_i /* unofficial code (19) */
52#define _dc_d0_ fcom_st /* unofficial code (14) */
53#define _dc_d8_ fcompst /* unofficial code (1c) */
54#define _dd_c8_ fxch_i /* unofficial code (0d) */
55#define _de_d0_ fcompst /* unofficial code (16) */
56#define _df_c0_ ffreep /* unofficial code (07) ffree + pop */
57#define _df_c8_ fxch_i /* unofficial code (0f) */
58#define _df_d0_ fstp_i /* unofficial code (17) */
59#define _df_d8_ fstp_i /* unofficial code (1f) */
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
61static FUNC const st_instr_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010062 fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
63 fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
64 fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
65 fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
66 fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
67 fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
68 fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
69 fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
Linus Torvalds1da177e2005-04-16 15:20:36 -070070};
71
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010072#else /* Support only documented FPU op-codes */
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74static FUNC const st_instr_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010075 fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, __BAD__,
76 fmul__, fxch_i, __BAD__, __BAD__, fmul_i, __BAD__, fmulp_, __BAD__,
77 fcom_st, fp_nop, __BAD__, __BAD__, __BAD__, fst_i_, __BAD__, __BAD__,
78 fcompst, __BAD__, __BAD__, __BAD__, __BAD__, fstp_i, fcompp, __BAD__,
79 fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
80 fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
81 fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
82 fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
Linus Torvalds1da177e2005-04-16 15:20:36 -070083};
84
85#endif /* NO_UNDOC_CODE */
86
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010087#define _NONE_ 0 /* Take no special action */
88#define _REG0_ 1 /* Need to check for not empty st(0) */
89#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */
90#define _REGi_ 0 /* Uses st(rm) */
91#define _PUSH_ 3 /* Need to check for space to push onto stack */
92#define _null_ 4 /* Function illegal or not implemented */
93#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
94#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
95#define _REGIc 0 /* Compare st(0) and st(rm) */
96#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
98#ifndef NO_UNDOC_CODE
99
100/* Un-documented FPU op-codes supported by default. (see above) */
101
102static u_char const type_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100103 _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
104 _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
105 _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
106 _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
107 _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
108 _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
109 _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
110 _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111};
112
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100113#else /* Support only documented FPU op-codes */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
115static u_char const type_table[64] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100116 _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _null_,
117 _REGI_, _REGIn, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
118 _REGIc, _NONE_, _null_, _null_, _null_, _REG0_, _null_, _null_,
119 _REGIc, _null_, _null_, _null_, _null_, _REG0_, _REGIc, _null_,
120 _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
121 _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
122 _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
123 _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124};
125
126#endif /* NO_UNDOC_CODE */
127
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128#ifdef RE_ENTRANT_CHECKING
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100129u_char emulating = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130#endif /* RE_ENTRANT_CHECKING */
131
Ingo Molnare8d591d2008-01-30 13:30:12 +0100132static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100133 overrides * override);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Tejun Heod3157602009-02-09 22:17:39 +0900135void math_emulate(struct math_emu_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100137 u_char FPU_modrm, byte1;
138 unsigned short code;
139 fpu_addr_modes addr_modes;
140 int unmasked;
141 FPU_REG loaded_data;
142 FPU_REG *st0_ptr;
143 u_char loaded_tag, st0_tag;
144 void __user *data_address;
145 struct address data_sel_off;
146 struct address entry_sel_off;
147 unsigned long code_base = 0;
148 unsigned long code_limit = 0; /* Initialized to stop compiler warnings */
149 struct desc_struct code_descriptor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
Suresh Siddhae8a496a2008-05-23 16:26:37 -0700151 if (!used_math()) {
152 if (init_fpu(current)) {
153 do_group_exit(SIGKILL);
154 return;
155 }
156 }
157
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158#ifdef RE_ENTRANT_CHECKING
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100159 if (emulating) {
160 printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
161 }
162 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163#endif /* RE_ENTRANT_CHECKING */
164
Tejun Heod3157602009-02-09 22:17:39 +0900165 FPU_info = info;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100166
167 FPU_ORIG_EIP = FPU_EIP;
168
169 if ((FPU_EFLAGS & 0x00020000) != 0) {
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 } else if (FPU_CS == __USER_CS && FPU_DS == __USER_DS) {
175 addr_modes.default_mode = 0;
176 } else if (FPU_CS == __KERNEL_CS) {
177 printk("math_emulate: %04x:%08lx\n", FPU_CS, FPU_EIP);
178 panic("Math emulation needed in kernel");
179 } else {
180
181 if ((FPU_CS & 4) != 4) { /* Must be in the LDT */
182 /* Can only handle segmented addressing via the LDT
183 for now, and it must be 16 bit */
184 printk("FPU emulator: Unsupported addressing mode\n");
185 math_abort(FPU_info, SIGILL);
186 }
187
188 code_descriptor = LDT_DESCRIPTOR(FPU_CS);
189 if (SEG_D_SIZE(code_descriptor)) {
190 /* The above test may be wrong, the book is not clear */
191 /* Segmented 32 bit protected mode */
192 addr_modes.default_mode = SEG32;
193 } else {
194 /* 16 bit protected mode */
195 addr_modes.default_mode = PM16;
196 }
197 FPU_EIP += code_base = SEG_BASE_ADDR(code_descriptor);
198 code_limit = code_base
199 + (SEG_LIMIT(code_descriptor) +
200 1) * SEG_GRANULARITY(code_descriptor)
201 - 1;
202 if (code_limit < code_base)
203 code_limit = 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100205
Roland McGrathff0ebb22008-01-30 13:31:49 +0100206 FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100207
208 if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
209 &addr_modes.override)) {
210 RE_ENTRANT_CHECK_OFF;
211 printk
212 ("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
213 "FPU emulator: self-modifying code! (emulation impossible)\n",
214 byte1);
215 RE_ENTRANT_CHECK_ON;
216 EXCEPTION(EX_INTERNAL | 0x126);
217 math_abort(FPU_info, SIGILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100220 do_another_FPU_instruction:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100222 no_ip_update = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100224 FPU_EIP++; /* We have fetched the prefix and first code bytes. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100226 if (addr_modes.default_mode) {
227 /* This checks for the minimum instruction bytes.
228 We also need to check any extra (address mode) code access. */
229 if (FPU_EIP > code_limit)
230 math_abort(FPU_info, SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100232
233 if ((byte1 & 0xf8) != 0xd8) {
234 if (byte1 == FWAIT_OPCODE) {
235 if (partial_status & SW_Summary)
236 goto do_the_FPU_interrupt;
237 else
238 goto FPU_fwait_done;
239 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100241 EXCEPTION(EX_INTERNAL | 0x128);
242 math_abort(FPU_info, SIGILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243#endif /* PARANOID */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 }
245
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100246 RE_ENTRANT_CHECK_OFF;
247 FPU_code_access_ok(1);
248 FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP);
249 RE_ENTRANT_CHECK_ON;
250 FPU_EIP++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100252 if (partial_status & SW_Summary) {
253 /* Ignore the error for now if the current instruction is a no-wait
254 control instruction */
255 /* The 80486 manual contradicts itself on this topic,
256 but a real 80486 uses the following instructions:
257 fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
258 */
259 code = (FPU_modrm << 8) | byte1;
260 if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
261 (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
262 fnstsw */
263 ((code & 0xc000) != 0xc000))))) {
264 /*
265 * We need to simulate the action of the kernel to FPU
266 * interrupts here.
267 */
268 do_the_FPU_interrupt:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100270 FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */
271
272 RE_ENTRANT_CHECK_OFF;
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530273 current->thread.trap_nr = X86_TRAP_MF;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100274 current->thread.error_code = 0;
275 send_sig(SIGFPE, current, 1);
276 return;
277 }
278 }
279
280 entry_sel_off.offset = FPU_ORIG_EIP;
281 entry_sel_off.selector = FPU_CS;
282 entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
Andrew Mortoncc7594e2008-03-18 18:46:06 -0700283 entry_sel_off.empty = 0;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100284
285 FPU_rm = FPU_modrm & 7;
286
287 if (FPU_modrm < 0300) {
288 /* All of these instructions use the mod/rm byte to get a data address */
289
290 if ((addr_modes.default_mode & SIXTEEN)
291 ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
292 data_address =
293 FPU_get_address_16(FPU_modrm, &FPU_EIP,
294 &data_sel_off, addr_modes);
295 else
296 data_address =
297 FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
298 addr_modes);
299
300 if (addr_modes.default_mode) {
301 if (FPU_EIP - 1 > code_limit)
302 math_abort(FPU_info, SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
304
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100305 if (!(byte1 & 1)) {
306 unsigned short status1 = partial_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100308 st0_ptr = &st(0);
309 st0_tag = FPU_gettag0();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100311 /* Stack underflow has priority */
312 if (NOT_EMPTY_ST0) {
313 if (addr_modes.default_mode & PROTECTED) {
314 /* This table works for 16 and 32 bit protected mode */
315 if (access_limit <
316 data_sizes_16[(byte1 >> 1) & 3])
317 math_abort(FPU_info, SIGSEGV);
318 }
319
320 unmasked = 0; /* Do this here to stop compiler warnings. */
321 switch ((byte1 >> 1) & 3) {
322 case 0:
323 unmasked =
324 FPU_load_single((float __user *)
325 data_address,
326 &loaded_data);
327 loaded_tag = unmasked & 0xff;
328 unmasked &= ~0xff;
329 break;
330 case 1:
331 loaded_tag =
332 FPU_load_int32((long __user *)
333 data_address,
334 &loaded_data);
335 break;
336 case 2:
337 unmasked =
338 FPU_load_double((double __user *)
339 data_address,
340 &loaded_data);
341 loaded_tag = unmasked & 0xff;
342 unmasked &= ~0xff;
343 break;
344 case 3:
345 default: /* Used here to suppress gcc warnings. */
346 loaded_tag =
347 FPU_load_int16((short __user *)
348 data_address,
349 &loaded_data);
350 break;
351 }
352
353 /* No more access to user memory, it is safe
354 to use static data now */
355
356 /* NaN operands have the next priority. */
357 /* We have to delay looking at st(0) until after
358 loading the data, because that data might contain an SNaN */
359 if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
360 || ((loaded_tag == TAG_Special)
361 && isNaN(&loaded_data))) {
362 /* Restore the status word; we might have loaded a
363 denormal. */
364 partial_status = status1;
365 if ((FPU_modrm & 0x30) == 0x10) {
366 /* fcom or fcomp */
367 EXCEPTION(EX_Invalid);
368 setcc(SW_C3 | SW_C2 | SW_C0);
369 if ((FPU_modrm & 0x08)
370 && (control_word &
371 CW_Invalid))
372 FPU_pop(); /* fcomp, masked, so we pop. */
373 } else {
374 if (loaded_tag == TAG_Special)
375 loaded_tag =
376 FPU_Special
377 (&loaded_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100379 /* This is not really needed, but gives behaviour
380 identical to an 80486 */
381 if ((FPU_modrm & 0x28) == 0x20)
382 /* fdiv or fsub */
383 real_2op_NaN
384 (&loaded_data,
385 loaded_tag, 0,
386 &loaded_data);
387 else
388#endif /* PECULIAR_486 */
389 /* fadd, fdivr, fmul, or fsubr */
390 real_2op_NaN
391 (&loaded_data,
392 loaded_tag, 0,
393 st0_ptr);
394 }
395 goto reg_mem_instr_done;
396 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100398 if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
399 /* Is not a comparison instruction. */
400 if ((FPU_modrm & 0x38) == 0x38) {
401 /* fdivr */
402 if ((st0_tag == TAG_Zero) &&
403 ((loaded_tag == TAG_Valid)
404 || (loaded_tag ==
405 TAG_Special
406 &&
407 isdenormal
408 (&loaded_data)))) {
409 if (FPU_divide_by_zero
410 (0,
411 getsign
412 (&loaded_data))
413 < 0) {
414 /* We use the fact here that the unmasked
415 exception in the loaded data was for a
416 denormal operand */
417 /* Restore the state of the denormal op bit */
418 partial_status
419 &=
420 ~SW_Denorm_Op;
421 partial_status
422 |=
423 status1 &
424 SW_Denorm_Op;
425 } else
426 setsign(st0_ptr,
427 getsign
428 (&loaded_data));
429 }
430 }
431 goto reg_mem_instr_done;
432 }
433
434 switch ((FPU_modrm >> 3) & 7) {
435 case 0: /* fadd */
436 clear_C1();
437 FPU_add(&loaded_data, loaded_tag, 0,
438 control_word);
439 break;
440 case 1: /* fmul */
441 clear_C1();
442 FPU_mul(&loaded_data, loaded_tag, 0,
443 control_word);
444 break;
445 case 2: /* fcom */
446 FPU_compare_st_data(&loaded_data,
447 loaded_tag);
448 break;
449 case 3: /* fcomp */
450 if (!FPU_compare_st_data
451 (&loaded_data, loaded_tag)
452 && !unmasked)
453 FPU_pop();
454 break;
455 case 4: /* fsub */
456 clear_C1();
457 FPU_sub(LOADED | loaded_tag,
458 (int)&loaded_data,
459 control_word);
460 break;
461 case 5: /* fsubr */
462 clear_C1();
463 FPU_sub(REV | LOADED | loaded_tag,
464 (int)&loaded_data,
465 control_word);
466 break;
467 case 6: /* fdiv */
468 clear_C1();
469 FPU_div(LOADED | loaded_tag,
470 (int)&loaded_data,
471 control_word);
472 break;
473 case 7: /* fdivr */
474 clear_C1();
475 if (st0_tag == TAG_Zero)
476 partial_status = status1; /* Undo any denorm tag,
477 zero-divide has priority. */
478 FPU_div(REV | LOADED | loaded_tag,
479 (int)&loaded_data,
480 control_word);
481 break;
482 }
483 } else {
484 if ((FPU_modrm & 0x30) == 0x10) {
485 /* The instruction is fcom or fcomp */
486 EXCEPTION(EX_StackUnder);
487 setcc(SW_C3 | SW_C2 | SW_C0);
488 if ((FPU_modrm & 0x08)
489 && (control_word & CW_Invalid))
490 FPU_pop(); /* fcomp */
491 } else
492 FPU_stack_underflow();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100494 reg_mem_instr_done:
495 operand_address = data_sel_off;
496 } else {
497 if (!(no_ip_update =
498 FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
499 >> 1, addr_modes, data_address))) {
500 operand_address = data_sel_off;
501 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 }
503
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100504 } else {
505 /* None of these instructions access user memory */
506 u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100509 /* This is supposed to be undefined, but a real 80486 seems
510 to do this: */
511 operand_address.offset = 0;
512 operand_address.selector = FPU_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513#endif /* PECULIAR_486 */
514
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100515 st0_ptr = &st(0);
516 st0_tag = FPU_gettag0();
517 switch (type_table[(int)instr_index]) {
518 case _NONE_: /* also _REGIc: _REGIn */
519 break;
520 case _REG0_:
521 if (!NOT_EMPTY_ST0) {
522 FPU_stack_underflow();
523 goto FPU_instruction_done;
524 }
525 break;
526 case _REGIi:
527 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
528 FPU_stack_underflow_i(FPU_rm);
529 goto FPU_instruction_done;
530 }
531 break;
532 case _REGIp:
533 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
534 FPU_stack_underflow_pop(FPU_rm);
535 goto FPU_instruction_done;
536 }
537 break;
538 case _REGI_:
539 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
540 FPU_stack_underflow();
541 goto FPU_instruction_done;
542 }
543 break;
544 case _PUSH_: /* Only used by the fld st(i) instruction */
545 break;
546 case _null_:
547 FPU_illegal();
548 goto FPU_instruction_done;
549 default:
550 EXCEPTION(EX_INTERNAL | 0x111);
551 goto FPU_instruction_done;
552 }
553 (*st_instr_table[(int)instr_index]) ();
554
555 FPU_instruction_done:
556 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100559 if (!no_ip_update)
560 instruction_address = entry_sel_off;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100562 FPU_fwait_done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
564#ifdef DEBUG
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100565 RE_ENTRANT_CHECK_OFF;
566 FPU_printall();
567 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568#endif /* DEBUG */
569
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100570 if (FPU_lookahead && !need_resched()) {
571 FPU_ORIG_EIP = FPU_EIP - code_base;
572 if (valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
573 &addr_modes.override))
574 goto do_another_FPU_instruction;
575 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100577 if (addr_modes.default_mode)
578 FPU_EIP -= code_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100580 RE_ENTRANT_CHECK_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581}
582
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583/* Support for prefix bytes is not yet complete. To properly handle
584 all prefix bytes, further changes are needed in the emulator code
585 which accesses user address space. Access to separate segments is
586 important for msdos emulation. */
Ingo Molnare8d591d2008-01-30 13:30:12 +0100587static int valid_prefix(u_char *Byte, u_char __user **fpu_eip,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100588 overrides * override)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100590 u_char byte;
591 u_char __user *ip = *fpu_eip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100593 *override = (overrides) {
594 0, 0, PREFIX_DEFAULT}; /* defaults */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100596 RE_ENTRANT_CHECK_OFF;
597 FPU_code_access_ok(1);
598 FPU_get_user(byte, ip);
599 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100601 while (1) {
602 switch (byte) {
603 case ADDR_SIZE_PREFIX:
604 override->address_size = ADDR_SIZE_PREFIX;
605 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100607 case OP_SIZE_PREFIX:
608 override->operand_size = OP_SIZE_PREFIX;
609 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100611 case PREFIX_CS:
612 override->segment = PREFIX_CS_;
613 goto do_next_byte;
614 case PREFIX_ES:
615 override->segment = PREFIX_ES_;
616 goto do_next_byte;
617 case PREFIX_SS:
618 override->segment = PREFIX_SS_;
619 goto do_next_byte;
620 case PREFIX_FS:
621 override->segment = PREFIX_FS_;
622 goto do_next_byte;
623 case PREFIX_GS:
624 override->segment = PREFIX_GS_;
625 goto do_next_byte;
626 case PREFIX_DS:
627 override->segment = PREFIX_DS_;
628 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
630/* lock is not a valid prefix for FPU instructions,
631 let the cpu handle it to generate a SIGILL. */
632/* case PREFIX_LOCK: */
633
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100634 /* rep.. prefixes have no meaning for FPU instructions */
635 case PREFIX_REPE:
636 case PREFIX_REPNE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100638 do_next_byte:
639 ip++;
640 RE_ENTRANT_CHECK_OFF;
641 FPU_code_access_ok(1);
642 FPU_get_user(byte, ip);
643 RE_ENTRANT_CHECK_ON;
644 break;
645 case FWAIT_OPCODE:
646 *Byte = byte;
647 return 1;
648 default:
649 if ((byte & 0xf8) == 0xd8) {
650 *Byte = byte;
651 *fpu_eip = ip;
652 return 1;
653 } else {
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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661}
662
Tejun Heoae6af412009-02-09 22:17:39 +0900663void math_abort(struct math_emu_info *info, unsigned int signal)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
665 FPU_EIP = FPU_ORIG_EIP;
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530666 current->thread.trap_nr = X86_TRAP_MF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 current->thread.error_code = 0;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100668 send_sig(signal, current, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 RE_ENTRANT_CHECK_OFF;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100670 __asm__("movl %0,%%esp ; ret": :"g"(((long)info) - 4));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100672 printk("ERROR: wm-FPU-emu math_abort failed!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673#endif /* PARANOID */
674}
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676#define S387 ((struct i387_soft_struct *)s387)
677#define sstatus_word() \
678 ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
679
Roland McGrathff0ebb22008-01-30 13:31:49 +0100680int fpregs_soft_set(struct task_struct *target,
681 const struct user_regset *regset,
682 unsigned int pos, unsigned int count,
683 const void *kbuf, const void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684{
H. Peter Anvinc3f89782010-05-10 13:37:16 -0700685 struct i387_soft_struct *s387 = &target->thread.fpu.state->soft;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100686 void *space = s387->st_space;
687 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100688 int offset, other, i, tags, regnr, tag, newtop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100690 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100691 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
692 offsetof(struct i387_soft_struct, st_space));
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100693 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Roland McGrathff0ebb22008-01-30 13:31:49 +0100695 if (ret)
696 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100698 S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
699 offset = (S387->ftop & 7) * 10;
700 other = 80 - offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100702 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100703
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100704 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100705 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
706 space + offset, 0, other);
707 if (!ret && offset)
708 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
709 space, 0, offset);
710
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100711 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100713 /* The tags may need to be corrected now. */
714 tags = S387->twd;
715 newtop = S387->ftop;
716 for (i = 0; i < 8; i++) {
717 regnr = (i + newtop) & 7;
718 if (((tags >> ((regnr & 7) * 2)) & 3) != TAG_Empty) {
719 /* The loaded data over-rides all other cases. */
720 tag =
721 FPU_tagof((FPU_REG *) ((u_char *) S387->st_space +
722 10 * regnr));
723 tags &= ~(3 << (regnr * 2));
724 tags |= (tag & 3) << (regnr * 2);
725 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100727 S387->twd = tags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
Roland McGrathff0ebb22008-01-30 13:31:49 +0100729 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730}
731
Roland McGrathff0ebb22008-01-30 13:31:49 +0100732int fpregs_soft_get(struct task_struct *target,
733 const struct user_regset *regset,
734 unsigned int pos, unsigned int count,
735 void *kbuf, void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
H. Peter Anvinc3f89782010-05-10 13:37:16 -0700737 struct i387_soft_struct *s387 = &target->thread.fpu.state->soft;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100738 const void *space = s387->st_space;
739 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100740 int offset = (S387->ftop & 7) * 10, other = 80 - offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100742 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100743
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100745 S387->cwd &= ~0xe080;
746 /* An 80486 sets nearly all of the reserved bits to 1. */
747 S387->cwd |= 0xffff0040;
748 S387->swd = sstatus_word() | 0xffff0000;
749 S387->twd |= 0xffff0000;
750 S387->fcs &= ~0xf8000000;
751 S387->fos |= 0xffff0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752#endif /* PECULIAR_486 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Roland McGrathff0ebb22008-01-30 13:31:49 +0100754 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0,
755 offsetof(struct i387_soft_struct, st_space));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100757 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100758 if (!ret)
759 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
760 space + offset, 0, other);
761 if (!ret)
762 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
763 space, 0, offset);
764
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100765 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Roland McGrathff0ebb22008-01-30 13:31:49 +0100767 return ret;
768}