blob: f37e84ab49f38e335bde57880a6cbe8640fb2c4b [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>
Ingo Molnar952f07e2015-04-26 16:56:05 +020034#include <asm/fpu/internal.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;
Ingo Molnarc5bedc62015-04-23 12:49:20 +0200150 struct fpu *fpu = &current->thread.fpu;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
Ingo Molnarc4d72e22015-04-27 07:18:17 +0200152 fpu__activate_curr(fpu);
Suresh Siddhae8a496a2008-05-23 16:26:37 -0700153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154#ifdef RE_ENTRANT_CHECKING
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100155 if (emulating) {
156 printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
157 }
158 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159#endif /* RE_ENTRANT_CHECKING */
160
Tejun Heod3157602009-02-09 22:17:39 +0900161 FPU_info = info;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100162
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;
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530269 current->thread.trap_nr = X86_TRAP_MF;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100270 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;
Andrew Mortoncc7594e2008-03-18 18:46:06 -0700279 entry_sel_off.empty = 0;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100280
281 FPU_rm = FPU_modrm & 7;
282
283 if (FPU_modrm < 0300) {
284 /* All of these instructions use the mod/rm byte to get a data address */
285
286 if ((addr_modes.default_mode & SIXTEEN)
287 ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
288 data_address =
289 FPU_get_address_16(FPU_modrm, &FPU_EIP,
290 &data_sel_off, addr_modes);
291 else
292 data_address =
293 FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
294 addr_modes);
295
296 if (addr_modes.default_mode) {
297 if (FPU_EIP - 1 > code_limit)
298 math_abort(FPU_info, SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 }
300
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100301 if (!(byte1 & 1)) {
302 unsigned short status1 = partial_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100304 st0_ptr = &st(0);
305 st0_tag = FPU_gettag0();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100307 /* Stack underflow has priority */
308 if (NOT_EMPTY_ST0) {
309 if (addr_modes.default_mode & PROTECTED) {
310 /* This table works for 16 and 32 bit protected mode */
311 if (access_limit <
312 data_sizes_16[(byte1 >> 1) & 3])
313 math_abort(FPU_info, SIGSEGV);
314 }
315
316 unmasked = 0; /* Do this here to stop compiler warnings. */
317 switch ((byte1 >> 1) & 3) {
318 case 0:
319 unmasked =
320 FPU_load_single((float __user *)
321 data_address,
322 &loaded_data);
323 loaded_tag = unmasked & 0xff;
324 unmasked &= ~0xff;
325 break;
326 case 1:
327 loaded_tag =
328 FPU_load_int32((long __user *)
329 data_address,
330 &loaded_data);
331 break;
332 case 2:
333 unmasked =
334 FPU_load_double((double __user *)
335 data_address,
336 &loaded_data);
337 loaded_tag = unmasked & 0xff;
338 unmasked &= ~0xff;
339 break;
340 case 3:
341 default: /* Used here to suppress gcc warnings. */
342 loaded_tag =
343 FPU_load_int16((short __user *)
344 data_address,
345 &loaded_data);
346 break;
347 }
348
349 /* No more access to user memory, it is safe
350 to use static data now */
351
352 /* NaN operands have the next priority. */
353 /* We have to delay looking at st(0) until after
354 loading the data, because that data might contain an SNaN */
355 if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
356 || ((loaded_tag == TAG_Special)
357 && isNaN(&loaded_data))) {
358 /* Restore the status word; we might have loaded a
359 denormal. */
360 partial_status = status1;
361 if ((FPU_modrm & 0x30) == 0x10) {
362 /* fcom or fcomp */
363 EXCEPTION(EX_Invalid);
364 setcc(SW_C3 | SW_C2 | SW_C0);
365 if ((FPU_modrm & 0x08)
366 && (control_word &
367 CW_Invalid))
368 FPU_pop(); /* fcomp, masked, so we pop. */
369 } else {
370 if (loaded_tag == TAG_Special)
371 loaded_tag =
372 FPU_Special
373 (&loaded_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100375 /* This is not really needed, but gives behaviour
376 identical to an 80486 */
377 if ((FPU_modrm & 0x28) == 0x20)
378 /* fdiv or fsub */
379 real_2op_NaN
380 (&loaded_data,
381 loaded_tag, 0,
382 &loaded_data);
383 else
384#endif /* PECULIAR_486 */
385 /* fadd, fdivr, fmul, or fsubr */
386 real_2op_NaN
387 (&loaded_data,
388 loaded_tag, 0,
389 st0_ptr);
390 }
391 goto reg_mem_instr_done;
392 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100394 if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
395 /* Is not a comparison instruction. */
396 if ((FPU_modrm & 0x38) == 0x38) {
397 /* fdivr */
398 if ((st0_tag == TAG_Zero) &&
399 ((loaded_tag == TAG_Valid)
400 || (loaded_tag ==
401 TAG_Special
402 &&
403 isdenormal
404 (&loaded_data)))) {
405 if (FPU_divide_by_zero
406 (0,
407 getsign
408 (&loaded_data))
409 < 0) {
410 /* We use the fact here that the unmasked
411 exception in the loaded data was for a
412 denormal operand */
413 /* Restore the state of the denormal op bit */
414 partial_status
415 &=
416 ~SW_Denorm_Op;
417 partial_status
418 |=
419 status1 &
420 SW_Denorm_Op;
421 } else
422 setsign(st0_ptr,
423 getsign
424 (&loaded_data));
425 }
426 }
427 goto reg_mem_instr_done;
428 }
429
430 switch ((FPU_modrm >> 3) & 7) {
431 case 0: /* fadd */
432 clear_C1();
433 FPU_add(&loaded_data, loaded_tag, 0,
434 control_word);
435 break;
436 case 1: /* fmul */
437 clear_C1();
438 FPU_mul(&loaded_data, loaded_tag, 0,
439 control_word);
440 break;
441 case 2: /* fcom */
442 FPU_compare_st_data(&loaded_data,
443 loaded_tag);
444 break;
445 case 3: /* fcomp */
446 if (!FPU_compare_st_data
447 (&loaded_data, loaded_tag)
448 && !unmasked)
449 FPU_pop();
450 break;
451 case 4: /* fsub */
452 clear_C1();
453 FPU_sub(LOADED | loaded_tag,
454 (int)&loaded_data,
455 control_word);
456 break;
457 case 5: /* fsubr */
458 clear_C1();
459 FPU_sub(REV | LOADED | loaded_tag,
460 (int)&loaded_data,
461 control_word);
462 break;
463 case 6: /* fdiv */
464 clear_C1();
465 FPU_div(LOADED | loaded_tag,
466 (int)&loaded_data,
467 control_word);
468 break;
469 case 7: /* fdivr */
470 clear_C1();
471 if (st0_tag == TAG_Zero)
472 partial_status = status1; /* Undo any denorm tag,
473 zero-divide has priority. */
474 FPU_div(REV | LOADED | loaded_tag,
475 (int)&loaded_data,
476 control_word);
477 break;
478 }
479 } else {
480 if ((FPU_modrm & 0x30) == 0x10) {
481 /* The instruction is fcom or fcomp */
482 EXCEPTION(EX_StackUnder);
483 setcc(SW_C3 | SW_C2 | SW_C0);
484 if ((FPU_modrm & 0x08)
485 && (control_word & CW_Invalid))
486 FPU_pop(); /* fcomp */
487 } else
488 FPU_stack_underflow();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100490 reg_mem_instr_done:
491 operand_address = data_sel_off;
492 } else {
493 if (!(no_ip_update =
494 FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
495 >> 1, addr_modes, data_address))) {
496 operand_address = data_sel_off;
497 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 }
499
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100500 } else {
501 /* None of these instructions access user memory */
502 u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
504#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100505 /* This is supposed to be undefined, but a real 80486 seems
506 to do this: */
507 operand_address.offset = 0;
508 operand_address.selector = FPU_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509#endif /* PECULIAR_486 */
510
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100511 st0_ptr = &st(0);
512 st0_tag = FPU_gettag0();
513 switch (type_table[(int)instr_index]) {
514 case _NONE_: /* also _REGIc: _REGIn */
515 break;
516 case _REG0_:
517 if (!NOT_EMPTY_ST0) {
518 FPU_stack_underflow();
519 goto FPU_instruction_done;
520 }
521 break;
522 case _REGIi:
523 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
524 FPU_stack_underflow_i(FPU_rm);
525 goto FPU_instruction_done;
526 }
527 break;
528 case _REGIp:
529 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
530 FPU_stack_underflow_pop(FPU_rm);
531 goto FPU_instruction_done;
532 }
533 break;
534 case _REGI_:
535 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
536 FPU_stack_underflow();
537 goto FPU_instruction_done;
538 }
539 break;
540 case _PUSH_: /* Only used by the fld st(i) instruction */
541 break;
542 case _null_:
543 FPU_illegal();
544 goto FPU_instruction_done;
545 default:
546 EXCEPTION(EX_INTERNAL | 0x111);
547 goto FPU_instruction_done;
548 }
549 (*st_instr_table[(int)instr_index]) ();
550
551 FPU_instruction_done:
552 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100555 if (!no_ip_update)
556 instruction_address = entry_sel_off;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100558 FPU_fwait_done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560#ifdef DEBUG
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100561 RE_ENTRANT_CHECK_OFF;
562 FPU_printall();
563 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564#endif /* DEBUG */
565
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100566 if (FPU_lookahead && !need_resched()) {
567 FPU_ORIG_EIP = FPU_EIP - code_base;
568 if (valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
569 &addr_modes.override))
570 goto do_another_FPU_instruction;
571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100573 if (addr_modes.default_mode)
574 FPU_EIP -= code_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100576 RE_ENTRANT_CHECK_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577}
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579/* 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. */
Ingo Molnare8d591d2008-01-30 13:30:12 +0100583static int valid_prefix(u_char *Byte, u_char __user **fpu_eip,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100584 overrides * override)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100586 u_char byte;
587 u_char __user *ip = *fpu_eip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100589 *override = (overrides) {
590 0, 0, PREFIX_DEFAULT}; /* defaults */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100592 RE_ENTRANT_CHECK_OFF;
593 FPU_code_access_ok(1);
594 FPU_get_user(byte, ip);
595 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100597 while (1) {
598 switch (byte) {
599 case ADDR_SIZE_PREFIX:
600 override->address_size = ADDR_SIZE_PREFIX;
601 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100603 case OP_SIZE_PREFIX:
604 override->operand_size = OP_SIZE_PREFIX;
605 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100607 case PREFIX_CS:
608 override->segment = PREFIX_CS_;
609 goto do_next_byte;
610 case PREFIX_ES:
611 override->segment = PREFIX_ES_;
612 goto do_next_byte;
613 case PREFIX_SS:
614 override->segment = PREFIX_SS_;
615 goto do_next_byte;
616 case PREFIX_FS:
617 override->segment = PREFIX_FS_;
618 goto do_next_byte;
619 case PREFIX_GS:
620 override->segment = PREFIX_GS_;
621 goto do_next_byte;
622 case PREFIX_DS:
623 override->segment = PREFIX_DS_;
624 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
626/* lock is not a valid prefix for FPU instructions,
627 let the cpu handle it to generate a SIGILL. */
628/* case PREFIX_LOCK: */
629
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100630 /* rep.. prefixes have no meaning for FPU instructions */
631 case PREFIX_REPE:
632 case PREFIX_REPNE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100634 do_next_byte:
635 ip++;
636 RE_ENTRANT_CHECK_OFF;
637 FPU_code_access_ok(1);
638 FPU_get_user(byte, ip);
639 RE_ENTRANT_CHECK_ON;
640 break;
641 case FWAIT_OPCODE:
642 *Byte = byte;
643 return 1;
644 default:
645 if ((byte & 0xf8) == 0xd8) {
646 *Byte = byte;
647 *fpu_eip = ip;
648 return 1;
649 } else {
650 /* Not a valid sequence of prefix bytes followed by
651 an FPU instruction. */
652 *Byte = byte; /* Needed for error message. */
653 return 0;
654 }
655 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657}
658
Tejun Heoae6af412009-02-09 22:17:39 +0900659void math_abort(struct math_emu_info *info, unsigned int signal)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
661 FPU_EIP = FPU_ORIG_EIP;
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530662 current->thread.trap_nr = X86_TRAP_MF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 current->thread.error_code = 0;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100664 send_sig(signal, current, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 RE_ENTRANT_CHECK_OFF;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100666 __asm__("movl %0,%%esp ; ret": :"g"(((long)info) - 4));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100668 printk("ERROR: wm-FPU-emu math_abort failed!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669#endif /* PARANOID */
670}
671
Ingo Molnarc47ada32015-04-30 17:15:32 +0200672#define S387 ((struct swregs_state *)s387)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673#define sstatus_word() \
674 ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
675
Roland McGrathff0ebb22008-01-30 13:31:49 +0100676int fpregs_soft_set(struct task_struct *target,
677 const struct user_regset *regset,
678 unsigned int pos, unsigned int count,
679 const void *kbuf, const void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680{
Ingo Molnarc47ada32015-04-30 17:15:32 +0200681 struct swregs_state *s387 = &target->thread.fpu.state.soft;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100682 void *space = s387->st_space;
683 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100684 int offset, other, i, tags, regnr, tag, newtop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100686 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100687 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
Ingo Molnarc47ada32015-04-30 17:15:32 +0200688 offsetof(struct swregs_state, st_space));
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100689 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Roland McGrathff0ebb22008-01-30 13:31:49 +0100691 if (ret)
692 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100694 S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
695 offset = (S387->ftop & 7) * 10;
696 other = 80 - offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100698 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100699
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100700 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100701 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
702 space + offset, 0, other);
703 if (!ret && offset)
704 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
705 space, 0, offset);
706
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100707 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100709 /* The tags may need to be corrected now. */
710 tags = S387->twd;
711 newtop = S387->ftop;
712 for (i = 0; i < 8; i++) {
713 regnr = (i + newtop) & 7;
714 if (((tags >> ((regnr & 7) * 2)) & 3) != TAG_Empty) {
715 /* The loaded data over-rides all other cases. */
716 tag =
717 FPU_tagof((FPU_REG *) ((u_char *) S387->st_space +
718 10 * regnr));
719 tags &= ~(3 << (regnr * 2));
720 tags |= (tag & 3) << (regnr * 2);
721 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100723 S387->twd = tags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Roland McGrathff0ebb22008-01-30 13:31:49 +0100725 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726}
727
Roland McGrathff0ebb22008-01-30 13:31:49 +0100728int fpregs_soft_get(struct task_struct *target,
729 const struct user_regset *regset,
730 unsigned int pos, unsigned int count,
731 void *kbuf, void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732{
Ingo Molnarc47ada32015-04-30 17:15:32 +0200733 struct swregs_state *s387 = &target->thread.fpu.state.soft;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100734 const void *space = s387->st_space;
735 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100736 int offset = (S387->ftop & 7) * 10, other = 80 - offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100738 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100739
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100741 S387->cwd &= ~0xe080;
742 /* An 80486 sets nearly all of the reserved bits to 1. */
743 S387->cwd |= 0xffff0040;
744 S387->swd = sstatus_word() | 0xffff0000;
745 S387->twd |= 0xffff0000;
746 S387->fcs &= ~0xf8000000;
747 S387->fos |= 0xffff0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748#endif /* PECULIAR_486 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Roland McGrathff0ebb22008-01-30 13:31:49 +0100750 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0,
Ingo Molnarc47ada32015-04-30 17:15:32 +0200751 offsetof(struct swregs_state, st_space));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100753 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100754 if (!ret)
755 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
756 space + offset, 0, other);
757 if (!ret)
758 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
759 space, 0, offset);
760
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100761 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Roland McGrathff0ebb22008-01-30 13:31:49 +0100763 return ret;
764}