blob: d4a7df2205b8de2576bc7cdbf8455f2fa9987b66 [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
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080030#include <linux/uaccess.h>
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +053031#include <asm/traps.h>
Roland McGrathff0ebb22008-01-30 13:31:49 +010032#include <asm/user.h>
Ingo Molnar952f07e2015-04-26 16:56:05 +020033#include <asm/fpu/internal.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
35#include "fpu_system.h"
36#include "fpu_emu.h"
37#include "exception.h"
38#include "control_w.h"
39#include "status_w.h"
40
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010041#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Denys Vlasenko9a9d8642015-09-18 16:53:30 +020043/* fcmovCC and f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */
Denys Vlasenkob8e4a912015-09-18 16:53:29 +020044
Denys Vlasenko4aef3632015-09-18 16:53:28 +020045/* WARNING: "u" entries are not documented by Intel in their 80486 manual
46 and may not work on FPU clones or later Intel FPUs.
47 Changes to support them provided by Linus Torvalds. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49static FUNC const st_instr_table[64] = {
Denys Vlasenko4aef3632015-09-18 16:53:28 +020050/* Opcode: d8 d9 da db */
51/* dc dd de df */
Denys Vlasenko9a9d8642015-09-18 16:53:30 +020052/* c0..7 */ fadd__, fld_i_, fcmovb, fcmovnb,
Denys Vlasenko4aef3632015-09-18 16:53:28 +020053/* c0..7 */ fadd_i, ffree_, faddp_, ffreep,/*u*/
Denys Vlasenko9a9d8642015-09-18 16:53:30 +020054/* c8..f */ fmul__, fxch_i, fcmove, fcmovne,
Denys Vlasenko4aef3632015-09-18 16:53:28 +020055/* c8..f */ fmul_i, fxch_i,/*u*/ fmulp_, fxch_i,/*u*/
Denys Vlasenko9a9d8642015-09-18 16:53:30 +020056/* d0..7 */ fcom_st, fp_nop, fcmovbe, fcmovnbe,
Denys Vlasenko4aef3632015-09-18 16:53:28 +020057/* d0..7 */ fcom_st,/*u*/ fst_i_, fcompst,/*u*/ fstp_i,/*u*/
Denys Vlasenko9a9d8642015-09-18 16:53:30 +020058/* d8..f */ fcompst, fstp_i,/*u*/ fcmovu, fcmovnu,
Denys Vlasenko4aef3632015-09-18 16:53:28 +020059/* d8..f */ fcompst,/*u*/ fstp_i, fcompp, fstp_i,/*u*/
60/* e0..7 */ fsub__, FPU_etc, __BAD__, finit_,
61/* e0..7 */ fsubri, fucom_, fsubrp, fstsw_,
Denys Vlasenkob8e4a912015-09-18 16:53:29 +020062/* e8..f */ fsubr_, fconst, fucompp, fucomi_,
63/* e8..f */ fsub_i, fucomp, fsubp_, fucomip,
64/* f0..7 */ fdiv__, FPU_triga, __BAD__, fcomi_,
65/* f0..7 */ fdivri, __BAD__, fdivrp, fcomip,
Denys Vlasenko4aef3632015-09-18 16:53:28 +020066/* f8..f */ fdivr_, FPU_trigb, __BAD__, __BAD__,
67/* f8..f */ fdiv_i, __BAD__, fdivp_, __BAD__,
Linus Torvalds1da177e2005-04-16 15:20:36 -070068};
69
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010070#define _NONE_ 0 /* Take no special action */
71#define _REG0_ 1 /* Need to check for not empty st(0) */
72#define _REGI_ 2 /* Need to check for not empty st(0) and st(rm) */
73#define _REGi_ 0 /* Uses st(rm) */
74#define _PUSH_ 3 /* Need to check for space to push onto stack */
75#define _null_ 4 /* Function illegal or not implemented */
76#define _REGIi 5 /* Uses st(0) and st(rm), result to st(rm) */
77#define _REGIp 6 /* Uses st(0) and st(rm), result to st(rm) then pop */
78#define _REGIc 0 /* Compare st(0) and st(rm) */
79#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Linus Torvalds1da177e2005-04-16 15:20:36 -070081static u_char const type_table[64] = {
Denys Vlasenkob8e4a912015-09-18 16:53:29 +020082/* Opcode: d8 d9 da db dc dd de df */
Denys Vlasenko9a9d8642015-09-18 16:53:30 +020083/* c0..7 */ _REGI_, _NONE_, _REGIn, _REGIn, _REGIi, _REGi_, _REGIp, _REGi_,
84/* c8..f */ _REGI_, _REGIn, _REGIn, _REGIn, _REGIi, _REGI_, _REGIp, _REGI_,
85/* d0..7 */ _REGIc, _NONE_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
86/* d8..f */ _REGIc, _REG0_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
Denys Vlasenkob8e4a912015-09-18 16:53:29 +020087/* e0..7 */ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
88/* e8..f */ _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
89/* f0..7 */ _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
90/* f8..f */ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
Linus Torvalds1da177e2005-04-16 15:20:36 -070091};
92
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#ifdef RE_ENTRANT_CHECKING
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010094u_char emulating = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095#endif /* RE_ENTRANT_CHECKING */
96
Ingo Molnare8d591d2008-01-30 13:30:12 +010097static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010098 overrides * override);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
Tejun Heod3157602009-02-09 22:17:39 +0900100void math_emulate(struct math_emu_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100102 u_char FPU_modrm, byte1;
103 unsigned short code;
104 fpu_addr_modes addr_modes;
105 int unmasked;
106 FPU_REG loaded_data;
107 FPU_REG *st0_ptr;
108 u_char loaded_tag, st0_tag;
109 void __user *data_address;
110 struct address data_sel_off;
111 struct address entry_sel_off;
112 unsigned long code_base = 0;
113 unsigned long code_limit = 0; /* Initialized to stop compiler warnings */
114 struct desc_struct code_descriptor;
Ingo Molnarc5bedc62015-04-23 12:49:20 +0200115 struct fpu *fpu = &current->thread.fpu;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116
Ingo Molnarc4d72e22015-04-27 07:18:17 +0200117 fpu__activate_curr(fpu);
Suresh Siddhae8a496a2008-05-23 16:26:37 -0700118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119#ifdef RE_ENTRANT_CHECKING
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100120 if (emulating) {
121 printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
122 }
123 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124#endif /* RE_ENTRANT_CHECKING */
125
Tejun Heod3157602009-02-09 22:17:39 +0900126 FPU_info = info;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100127
128 FPU_ORIG_EIP = FPU_EIP;
129
130 if ((FPU_EFLAGS & 0x00020000) != 0) {
131 /* Virtual 8086 mode */
132 addr_modes.default_mode = VM86;
133 FPU_EIP += code_base = FPU_CS << 4;
134 code_limit = code_base + 0xffff; /* Assumes code_base <= 0xffff0000 */
135 } else if (FPU_CS == __USER_CS && FPU_DS == __USER_DS) {
136 addr_modes.default_mode = 0;
137 } else if (FPU_CS == __KERNEL_CS) {
138 printk("math_emulate: %04x:%08lx\n", FPU_CS, FPU_EIP);
139 panic("Math emulation needed in kernel");
140 } else {
141
142 if ((FPU_CS & 4) != 4) { /* Must be in the LDT */
143 /* Can only handle segmented addressing via the LDT
144 for now, and it must be 16 bit */
145 printk("FPU emulator: Unsupported addressing mode\n");
146 math_abort(FPU_info, SIGILL);
147 }
148
Juergen Gross48091462015-08-06 19:54:34 +0200149 code_descriptor = FPU_get_ldt_descriptor(FPU_CS);
Thomas Gleixner718f5d02017-08-28 08:47:39 +0200150 if (code_descriptor.d) {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100151 /* The above test may be wrong, the book is not clear */
152 /* Segmented 32 bit protected mode */
153 addr_modes.default_mode = SEG32;
154 } else {
155 /* 16 bit protected mode */
156 addr_modes.default_mode = PM16;
157 }
Thomas Gleixner718f5d02017-08-28 08:47:39 +0200158 FPU_EIP += code_base = seg_get_base(&code_descriptor);
159 code_limit = seg_get_limit(&code_descriptor) + 1;
160 code_limit *= seg_get_granularity(&code_descriptor);
161 code_limit += code_base - 1;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100162 if (code_limit < code_base)
163 code_limit = 0xffffffff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100165
Roland McGrathff0ebb22008-01-30 13:31:49 +0100166 FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100167
168 if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
169 &addr_modes.override)) {
170 RE_ENTRANT_CHECK_OFF;
171 printk
172 ("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
173 "FPU emulator: self-modifying code! (emulation impossible)\n",
174 byte1);
175 RE_ENTRANT_CHECK_ON;
176 EXCEPTION(EX_INTERNAL | 0x126);
177 math_abort(FPU_info, SIGILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100180 do_another_FPU_instruction:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100182 no_ip_update = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100184 FPU_EIP++; /* We have fetched the prefix and first code bytes. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100186 if (addr_modes.default_mode) {
187 /* This checks for the minimum instruction bytes.
188 We also need to check any extra (address mode) code access. */
189 if (FPU_EIP > code_limit)
190 math_abort(FPU_info, SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100192
193 if ((byte1 & 0xf8) != 0xd8) {
194 if (byte1 == FWAIT_OPCODE) {
195 if (partial_status & SW_Summary)
196 goto do_the_FPU_interrupt;
197 else
198 goto FPU_fwait_done;
199 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100201 EXCEPTION(EX_INTERNAL | 0x128);
202 math_abort(FPU_info, SIGILL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203#endif /* PARANOID */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 }
205
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100206 RE_ENTRANT_CHECK_OFF;
207 FPU_code_access_ok(1);
208 FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP);
209 RE_ENTRANT_CHECK_ON;
210 FPU_EIP++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100212 if (partial_status & SW_Summary) {
213 /* Ignore the error for now if the current instruction is a no-wait
214 control instruction */
215 /* The 80486 manual contradicts itself on this topic,
216 but a real 80486 uses the following instructions:
217 fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
218 */
219 code = (FPU_modrm << 8) | byte1;
220 if (!((((code & 0xf803) == 0xe003) || /* fnclex, fninit, fnstsw */
221 (((code & 0x3003) == 0x3001) && /* fnsave, fnstcw, fnstenv,
222 fnstsw */
223 ((code & 0xc000) != 0xc000))))) {
224 /*
225 * We need to simulate the action of the kernel to FPU
226 * interrupts here.
227 */
228 do_the_FPU_interrupt:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100230 FPU_EIP = FPU_ORIG_EIP; /* Point to current FPU instruction. */
231
232 RE_ENTRANT_CHECK_OFF;
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530233 current->thread.trap_nr = X86_TRAP_MF;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100234 current->thread.error_code = 0;
235 send_sig(SIGFPE, current, 1);
236 return;
237 }
238 }
239
240 entry_sel_off.offset = FPU_ORIG_EIP;
241 entry_sel_off.selector = FPU_CS;
242 entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
Andrew Mortoncc7594e2008-03-18 18:46:06 -0700243 entry_sel_off.empty = 0;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100244
245 FPU_rm = FPU_modrm & 7;
246
247 if (FPU_modrm < 0300) {
248 /* All of these instructions use the mod/rm byte to get a data address */
249
250 if ((addr_modes.default_mode & SIXTEEN)
251 ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
252 data_address =
253 FPU_get_address_16(FPU_modrm, &FPU_EIP,
254 &data_sel_off, addr_modes);
255 else
256 data_address =
257 FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
258 addr_modes);
259
260 if (addr_modes.default_mode) {
261 if (FPU_EIP - 1 > code_limit)
262 math_abort(FPU_info, SIGSEGV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 }
264
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100265 if (!(byte1 & 1)) {
266 unsigned short status1 = partial_status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100268 st0_ptr = &st(0);
269 st0_tag = FPU_gettag0();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100271 /* Stack underflow has priority */
272 if (NOT_EMPTY_ST0) {
273 if (addr_modes.default_mode & PROTECTED) {
274 /* This table works for 16 and 32 bit protected mode */
275 if (access_limit <
276 data_sizes_16[(byte1 >> 1) & 3])
277 math_abort(FPU_info, SIGSEGV);
278 }
279
280 unmasked = 0; /* Do this here to stop compiler warnings. */
281 switch ((byte1 >> 1) & 3) {
282 case 0:
283 unmasked =
284 FPU_load_single((float __user *)
285 data_address,
286 &loaded_data);
287 loaded_tag = unmasked & 0xff;
288 unmasked &= ~0xff;
289 break;
290 case 1:
291 loaded_tag =
292 FPU_load_int32((long __user *)
293 data_address,
294 &loaded_data);
295 break;
296 case 2:
297 unmasked =
298 FPU_load_double((double __user *)
299 data_address,
300 &loaded_data);
301 loaded_tag = unmasked & 0xff;
302 unmasked &= ~0xff;
303 break;
304 case 3:
305 default: /* Used here to suppress gcc warnings. */
306 loaded_tag =
307 FPU_load_int16((short __user *)
308 data_address,
309 &loaded_data);
310 break;
311 }
312
313 /* No more access to user memory, it is safe
314 to use static data now */
315
316 /* NaN operands have the next priority. */
317 /* We have to delay looking at st(0) until after
318 loading the data, because that data might contain an SNaN */
319 if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
320 || ((loaded_tag == TAG_Special)
321 && isNaN(&loaded_data))) {
322 /* Restore the status word; we might have loaded a
323 denormal. */
324 partial_status = status1;
325 if ((FPU_modrm & 0x30) == 0x10) {
326 /* fcom or fcomp */
327 EXCEPTION(EX_Invalid);
328 setcc(SW_C3 | SW_C2 | SW_C0);
329 if ((FPU_modrm & 0x08)
330 && (control_word &
331 CW_Invalid))
332 FPU_pop(); /* fcomp, masked, so we pop. */
333 } else {
334 if (loaded_tag == TAG_Special)
335 loaded_tag =
336 FPU_Special
337 (&loaded_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100339 /* This is not really needed, but gives behaviour
340 identical to an 80486 */
341 if ((FPU_modrm & 0x28) == 0x20)
342 /* fdiv or fsub */
343 real_2op_NaN
344 (&loaded_data,
345 loaded_tag, 0,
346 &loaded_data);
347 else
348#endif /* PECULIAR_486 */
349 /* fadd, fdivr, fmul, or fsubr */
350 real_2op_NaN
351 (&loaded_data,
352 loaded_tag, 0,
353 st0_ptr);
354 }
355 goto reg_mem_instr_done;
356 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100358 if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
359 /* Is not a comparison instruction. */
360 if ((FPU_modrm & 0x38) == 0x38) {
361 /* fdivr */
362 if ((st0_tag == TAG_Zero) &&
363 ((loaded_tag == TAG_Valid)
364 || (loaded_tag ==
365 TAG_Special
366 &&
367 isdenormal
368 (&loaded_data)))) {
369 if (FPU_divide_by_zero
370 (0,
371 getsign
372 (&loaded_data))
373 < 0) {
374 /* We use the fact here that the unmasked
375 exception in the loaded data was for a
376 denormal operand */
377 /* Restore the state of the denormal op bit */
378 partial_status
379 &=
380 ~SW_Denorm_Op;
381 partial_status
382 |=
383 status1 &
384 SW_Denorm_Op;
385 } else
386 setsign(st0_ptr,
387 getsign
388 (&loaded_data));
389 }
390 }
391 goto reg_mem_instr_done;
392 }
393
394 switch ((FPU_modrm >> 3) & 7) {
395 case 0: /* fadd */
396 clear_C1();
397 FPU_add(&loaded_data, loaded_tag, 0,
398 control_word);
399 break;
400 case 1: /* fmul */
401 clear_C1();
402 FPU_mul(&loaded_data, loaded_tag, 0,
403 control_word);
404 break;
405 case 2: /* fcom */
406 FPU_compare_st_data(&loaded_data,
407 loaded_tag);
408 break;
409 case 3: /* fcomp */
410 if (!FPU_compare_st_data
411 (&loaded_data, loaded_tag)
412 && !unmasked)
413 FPU_pop();
414 break;
415 case 4: /* fsub */
416 clear_C1();
417 FPU_sub(LOADED | loaded_tag,
418 (int)&loaded_data,
419 control_word);
420 break;
421 case 5: /* fsubr */
422 clear_C1();
423 FPU_sub(REV | LOADED | loaded_tag,
424 (int)&loaded_data,
425 control_word);
426 break;
427 case 6: /* fdiv */
428 clear_C1();
429 FPU_div(LOADED | loaded_tag,
430 (int)&loaded_data,
431 control_word);
432 break;
433 case 7: /* fdivr */
434 clear_C1();
435 if (st0_tag == TAG_Zero)
436 partial_status = status1; /* Undo any denorm tag,
437 zero-divide has priority. */
438 FPU_div(REV | LOADED | loaded_tag,
439 (int)&loaded_data,
440 control_word);
441 break;
442 }
443 } else {
444 if ((FPU_modrm & 0x30) == 0x10) {
445 /* The instruction is fcom or fcomp */
446 EXCEPTION(EX_StackUnder);
447 setcc(SW_C3 | SW_C2 | SW_C0);
448 if ((FPU_modrm & 0x08)
449 && (control_word & CW_Invalid))
450 FPU_pop(); /* fcomp */
451 } else
452 FPU_stack_underflow();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100454 reg_mem_instr_done:
455 operand_address = data_sel_off;
456 } else {
457 if (!(no_ip_update =
458 FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
459 >> 1, addr_modes, data_address))) {
460 operand_address = data_sel_off;
461 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 }
463
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100464 } else {
465 /* None of these instructions access user memory */
466 u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
468#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100469 /* This is supposed to be undefined, but a real 80486 seems
470 to do this: */
471 operand_address.offset = 0;
472 operand_address.selector = FPU_DS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473#endif /* PECULIAR_486 */
474
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100475 st0_ptr = &st(0);
476 st0_tag = FPU_gettag0();
477 switch (type_table[(int)instr_index]) {
478 case _NONE_: /* also _REGIc: _REGIn */
479 break;
480 case _REG0_:
481 if (!NOT_EMPTY_ST0) {
482 FPU_stack_underflow();
483 goto FPU_instruction_done;
484 }
485 break;
486 case _REGIi:
487 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
488 FPU_stack_underflow_i(FPU_rm);
489 goto FPU_instruction_done;
490 }
491 break;
492 case _REGIp:
493 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
494 FPU_stack_underflow_pop(FPU_rm);
495 goto FPU_instruction_done;
496 }
497 break;
498 case _REGI_:
499 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
500 FPU_stack_underflow();
501 goto FPU_instruction_done;
502 }
503 break;
504 case _PUSH_: /* Only used by the fld st(i) instruction */
505 break;
506 case _null_:
507 FPU_illegal();
508 goto FPU_instruction_done;
509 default:
510 EXCEPTION(EX_INTERNAL | 0x111);
511 goto FPU_instruction_done;
512 }
513 (*st_instr_table[(int)instr_index]) ();
514
515 FPU_instruction_done:
516 ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100519 if (!no_ip_update)
520 instruction_address = entry_sel_off;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100522 FPU_fwait_done:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523
524#ifdef DEBUG
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100525 RE_ENTRANT_CHECK_OFF;
526 FPU_printall();
527 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528#endif /* DEBUG */
529
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100530 if (FPU_lookahead && !need_resched()) {
531 FPU_ORIG_EIP = FPU_EIP - code_base;
532 if (valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
533 &addr_modes.override))
534 goto do_another_FPU_instruction;
535 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100537 if (addr_modes.default_mode)
538 FPU_EIP -= code_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100540 RE_ENTRANT_CHECK_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541}
542
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543/* Support for prefix bytes is not yet complete. To properly handle
544 all prefix bytes, further changes are needed in the emulator code
545 which accesses user address space. Access to separate segments is
546 important for msdos emulation. */
Ingo Molnare8d591d2008-01-30 13:30:12 +0100547static int valid_prefix(u_char *Byte, u_char __user **fpu_eip,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100548 overrides * override)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100550 u_char byte;
551 u_char __user *ip = *fpu_eip;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100553 *override = (overrides) {
554 0, 0, PREFIX_DEFAULT}; /* defaults */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100556 RE_ENTRANT_CHECK_OFF;
557 FPU_code_access_ok(1);
558 FPU_get_user(byte, ip);
559 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100561 while (1) {
562 switch (byte) {
563 case ADDR_SIZE_PREFIX:
564 override->address_size = ADDR_SIZE_PREFIX;
565 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100567 case OP_SIZE_PREFIX:
568 override->operand_size = OP_SIZE_PREFIX;
569 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100571 case PREFIX_CS:
572 override->segment = PREFIX_CS_;
573 goto do_next_byte;
574 case PREFIX_ES:
575 override->segment = PREFIX_ES_;
576 goto do_next_byte;
577 case PREFIX_SS:
578 override->segment = PREFIX_SS_;
579 goto do_next_byte;
580 case PREFIX_FS:
581 override->segment = PREFIX_FS_;
582 goto do_next_byte;
583 case PREFIX_GS:
584 override->segment = PREFIX_GS_;
585 goto do_next_byte;
586 case PREFIX_DS:
587 override->segment = PREFIX_DS_;
588 goto do_next_byte;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590/* lock is not a valid prefix for FPU instructions,
591 let the cpu handle it to generate a SIGILL. */
592/* case PREFIX_LOCK: */
593
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100594 /* rep.. prefixes have no meaning for FPU instructions */
595 case PREFIX_REPE:
596 case PREFIX_REPNE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100598 do_next_byte:
599 ip++;
600 RE_ENTRANT_CHECK_OFF;
601 FPU_code_access_ok(1);
602 FPU_get_user(byte, ip);
603 RE_ENTRANT_CHECK_ON;
604 break;
605 case FWAIT_OPCODE:
606 *Byte = byte;
607 return 1;
608 default:
609 if ((byte & 0xf8) == 0xd8) {
610 *Byte = byte;
611 *fpu_eip = ip;
612 return 1;
613 } else {
614 /* Not a valid sequence of prefix bytes followed by
615 an FPU instruction. */
616 *Byte = byte; /* Needed for error message. */
617 return 0;
618 }
619 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621}
622
Tejun Heoae6af412009-02-09 22:17:39 +0900623void math_abort(struct math_emu_info *info, unsigned int signal)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624{
625 FPU_EIP = FPU_ORIG_EIP;
Srikar Dronamraju51e7dc72012-03-12 14:55:55 +0530626 current->thread.trap_nr = X86_TRAP_MF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 current->thread.error_code = 0;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100628 send_sig(signal, current, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 RE_ENTRANT_CHECK_OFF;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100630 __asm__("movl %0,%%esp ; ret": :"g"(((long)info) - 4));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100632 printk("ERROR: wm-FPU-emu math_abort failed!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633#endif /* PARANOID */
634}
635
Ingo Molnarc47ada32015-04-30 17:15:32 +0200636#define S387 ((struct swregs_state *)s387)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637#define sstatus_word() \
638 ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
639
Roland McGrathff0ebb22008-01-30 13:31:49 +0100640int fpregs_soft_set(struct task_struct *target,
641 const struct user_regset *regset,
642 unsigned int pos, unsigned int count,
643 const void *kbuf, const void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644{
Ingo Molnarc47ada32015-04-30 17:15:32 +0200645 struct swregs_state *s387 = &target->thread.fpu.state.soft;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100646 void *space = s387->st_space;
647 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100648 int offset, other, i, tags, regnr, tag, newtop;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100650 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100651 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
Ingo Molnarc47ada32015-04-30 17:15:32 +0200652 offsetof(struct swregs_state, st_space));
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100653 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Roland McGrathff0ebb22008-01-30 13:31:49 +0100655 if (ret)
656 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100658 S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
659 offset = (S387->ftop & 7) * 10;
660 other = 80 - offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100662 RE_ENTRANT_CHECK_OFF;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100663
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100664 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100665 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
666 space + offset, 0, other);
667 if (!ret && offset)
668 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
669 space, 0, offset);
670
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100671 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100673 /* The tags may need to be corrected now. */
674 tags = S387->twd;
675 newtop = S387->ftop;
676 for (i = 0; i < 8; i++) {
677 regnr = (i + newtop) & 7;
678 if (((tags >> ((regnr & 7) * 2)) & 3) != TAG_Empty) {
679 /* The loaded data over-rides all other cases. */
680 tag =
681 FPU_tagof((FPU_REG *) ((u_char *) S387->st_space +
682 10 * regnr));
683 tags &= ~(3 << (regnr * 2));
684 tags |= (tag & 3) << (regnr * 2);
685 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100687 S387->twd = tags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688
Roland McGrathff0ebb22008-01-30 13:31:49 +0100689 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690}
691
Roland McGrathff0ebb22008-01-30 13:31:49 +0100692int fpregs_soft_get(struct task_struct *target,
693 const struct user_regset *regset,
694 unsigned int pos, unsigned int count,
695 void *kbuf, void __user *ubuf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696{
Ingo Molnarc47ada32015-04-30 17:15:32 +0200697 struct swregs_state *s387 = &target->thread.fpu.state.soft;
Roland McGrathff0ebb22008-01-30 13:31:49 +0100698 const void *space = s387->st_space;
699 int ret;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100700 int offset = (S387->ftop & 7) * 10, 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100705 S387->cwd &= ~0xe080;
706 /* An 80486 sets nearly all of the reserved bits to 1. */
707 S387->cwd |= 0xffff0040;
708 S387->swd = sstatus_word() | 0xffff0000;
709 S387->twd |= 0xffff0000;
710 S387->fcs &= ~0xf8000000;
711 S387->fos |= 0xffff0000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712#endif /* PECULIAR_486 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
Roland McGrathff0ebb22008-01-30 13:31:49 +0100714 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0,
Ingo Molnarc47ada32015-04-30 17:15:32 +0200715 offsetof(struct swregs_state, st_space));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100717 /* Copy all registers in stack order. */
Roland McGrathff0ebb22008-01-30 13:31:49 +0100718 if (!ret)
719 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
720 space + offset, 0, other);
721 if (!ret)
722 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
723 space, 0, offset);
724
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100725 RE_ENTRANT_CHECK_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
Roland McGrathff0ebb22008-01-30 13:31:49 +0100727 return ret;
728}