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