This source file includes following definitions.
- math_emulate
- valid_prefix
- math_abort
- fpregs_soft_set
- fpregs_soft_get
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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/internal.h>
35
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
42 #define __BAD__ FPU_illegal
43
44
45
46
47
48
49
50 static FUNC const st_instr_table[64] = {
51
52
53 fadd__, fld_i_, fcmovb, fcmovnb,
54 fadd_i, ffree_, faddp_, ffreep,
55 fmul__, fxch_i, fcmove, fcmovne,
56 fmul_i, fxch_i, fmulp_, fxch_i,
57 fcom_st, fp_nop, fcmovbe, fcmovnbe,
58 fcom_st, fst_i_, fcompst, fstp_i,
59 fcompst, fstp_i, fcmovu, fcmovnu,
60 fcompst, fstp_i, fcompp, fstp_i,
61 fsub__, FPU_etc, __BAD__, finit_,
62 fsubri, fucom_, fsubrp, fstsw_,
63 fsubr_, fconst, fucompp, fucomi_,
64 fsub_i, fucomp, fsubp_, fucomip,
65 fdiv__, FPU_triga, __BAD__, fcomi_,
66 fdivri, __BAD__, fdivrp, fcomip,
67 fdivr_, FPU_trigb, __BAD__, __BAD__,
68 fdiv_i, __BAD__, fdivp_, __BAD__,
69 };
70
71 #define _NONE_ 0
72 #define _REG0_ 1
73 #define _REGI_ 2
74 #define _REGi_ 0
75 #define _PUSH_ 3
76 #define _null_ 4
77 #define _REGIi 5
78 #define _REGIp 6
79 #define _REGIc 0
80 #define _REGIn 0
81
82 static u_char const type_table[64] = {
83
84 _REGI_, _NONE_, _REGIn, _REGIn, _REGIi, _REGi_, _REGIp, _REGi_,
85 _REGI_, _REGIn, _REGIn, _REGIn, _REGIi, _REGI_, _REGIp, _REGI_,
86 _REGIc, _NONE_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
87 _REGIc, _REG0_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
88 _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
89 _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
90 _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
91 _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
92 };
93
94 #ifdef RE_ENTRANT_CHECKING
95 u_char emulating = 0;
96 #endif
97
98 static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip,
99 overrides * override);
100
101 void math_emulate(struct math_emu_info *info)
102 {
103 u_char FPU_modrm, byte1;
104 unsigned short code;
105 fpu_addr_modes addr_modes;
106 int unmasked;
107 FPU_REG loaded_data;
108 FPU_REG *st0_ptr;
109 u_char loaded_tag, st0_tag;
110 void __user *data_address;
111 struct address data_sel_off;
112 struct address entry_sel_off;
113 unsigned long code_base = 0;
114 unsigned long code_limit = 0;
115 struct desc_struct code_descriptor;
116
117 #ifdef RE_ENTRANT_CHECKING
118 if (emulating) {
119 printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
120 }
121 RE_ENTRANT_CHECK_ON;
122 #endif
123
124 FPU_info = info;
125
126 FPU_ORIG_EIP = FPU_EIP;
127
128 if ((FPU_EFLAGS & 0x00020000) != 0) {
129
130 addr_modes.default_mode = VM86;
131 FPU_EIP += code_base = FPU_CS << 4;
132 code_limit = code_base + 0xffff;
133 } else if (FPU_CS == __USER_CS && FPU_DS == __USER_DS) {
134 addr_modes.default_mode = 0;
135 } else if (FPU_CS == __KERNEL_CS) {
136 printk("math_emulate: %04x:%08lx\n", FPU_CS, FPU_EIP);
137 panic("Math emulation needed in kernel");
138 } else {
139
140 if ((FPU_CS & 4) != 4) {
141
142
143 printk("FPU emulator: Unsupported addressing mode\n");
144 math_abort(FPU_info, SIGILL);
145 }
146
147 code_descriptor = FPU_get_ldt_descriptor(FPU_CS);
148 if (code_descriptor.d) {
149
150
151 addr_modes.default_mode = SEG32;
152 } else {
153
154 addr_modes.default_mode = PM16;
155 }
156 FPU_EIP += code_base = seg_get_base(&code_descriptor);
157 code_limit = seg_get_limit(&code_descriptor) + 1;
158 code_limit *= seg_get_granularity(&code_descriptor);
159 code_limit += code_base - 1;
160 if (code_limit < code_base)
161 code_limit = 0xffffffff;
162 }
163
164 FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);
165
166 if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
167 &addr_modes.override)) {
168 RE_ENTRANT_CHECK_OFF;
169 printk
170 ("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
171 "FPU emulator: self-modifying code! (emulation impossible)\n",
172 byte1);
173 RE_ENTRANT_CHECK_ON;
174 EXCEPTION(EX_INTERNAL | 0x126);
175 math_abort(FPU_info, SIGILL);
176 }
177
178 do_another_FPU_instruction:
179
180 no_ip_update = 0;
181
182 FPU_EIP++;
183
184 if (addr_modes.default_mode) {
185
186
187 if (FPU_EIP > code_limit)
188 math_abort(FPU_info, SIGSEGV);
189 }
190
191 if ((byte1 & 0xf8) != 0xd8) {
192 if (byte1 == FWAIT_OPCODE) {
193 if (partial_status & SW_Summary)
194 goto do_the_FPU_interrupt;
195 else
196 goto FPU_fwait_done;
197 }
198 #ifdef PARANOID
199 EXCEPTION(EX_INTERNAL | 0x128);
200 math_abort(FPU_info, SIGILL);
201 #endif
202 }
203
204 RE_ENTRANT_CHECK_OFF;
205 FPU_code_access_ok(1);
206 FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP);
207 RE_ENTRANT_CHECK_ON;
208 FPU_EIP++;
209
210 if (partial_status & SW_Summary) {
211
212
213
214
215
216
217 code = (FPU_modrm << 8) | byte1;
218 if (!((((code & 0xf803) == 0xe003) ||
219 (((code & 0x3003) == 0x3001) &&
220
221 ((code & 0xc000) != 0xc000))))) {
222
223
224
225
226 do_the_FPU_interrupt:
227
228 FPU_EIP = FPU_ORIG_EIP;
229
230 RE_ENTRANT_CHECK_OFF;
231 current->thread.trap_nr = X86_TRAP_MF;
232 current->thread.error_code = 0;
233 send_sig(SIGFPE, current, 1);
234 return;
235 }
236 }
237
238 entry_sel_off.offset = FPU_ORIG_EIP;
239 entry_sel_off.selector = FPU_CS;
240 entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
241 entry_sel_off.empty = 0;
242
243 FPU_rm = FPU_modrm & 7;
244
245 if (FPU_modrm < 0300) {
246
247
248 if ((addr_modes.default_mode & SIXTEEN)
249 ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
250 data_address =
251 FPU_get_address_16(FPU_modrm, &FPU_EIP,
252 &data_sel_off, addr_modes);
253 else
254 data_address =
255 FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
256 addr_modes);
257
258 if (addr_modes.default_mode) {
259 if (FPU_EIP - 1 > code_limit)
260 math_abort(FPU_info, SIGSEGV);
261 }
262
263 if (!(byte1 & 1)) {
264 unsigned short status1 = partial_status;
265
266 st0_ptr = &st(0);
267 st0_tag = FPU_gettag0();
268
269
270 if (NOT_EMPTY_ST0) {
271 if (addr_modes.default_mode & PROTECTED) {
272
273 if (access_limit <
274 data_sizes_16[(byte1 >> 1) & 3])
275 math_abort(FPU_info, SIGSEGV);
276 }
277
278 unmasked = 0;
279 switch ((byte1 >> 1) & 3) {
280 case 0:
281 unmasked =
282 FPU_load_single((float __user *)
283 data_address,
284 &loaded_data);
285 loaded_tag = unmasked & 0xff;
286 unmasked &= ~0xff;
287 break;
288 case 1:
289 loaded_tag =
290 FPU_load_int32((long __user *)
291 data_address,
292 &loaded_data);
293 break;
294 case 2:
295 unmasked =
296 FPU_load_double((double __user *)
297 data_address,
298 &loaded_data);
299 loaded_tag = unmasked & 0xff;
300 unmasked &= ~0xff;
301 break;
302 case 3:
303 default:
304 loaded_tag =
305 FPU_load_int16((short __user *)
306 data_address,
307 &loaded_data);
308 break;
309 }
310
311
312
313
314
315
316
317 if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
318 || ((loaded_tag == TAG_Special)
319 && isNaN(&loaded_data))) {
320
321
322 partial_status = status1;
323 if ((FPU_modrm & 0x30) == 0x10) {
324
325 EXCEPTION(EX_Invalid);
326 setcc(SW_C3 | SW_C2 | SW_C0);
327 if ((FPU_modrm & 0x08)
328 && (control_word &
329 CW_Invalid))
330 FPU_pop();
331 } else {
332 if (loaded_tag == TAG_Special)
333 loaded_tag =
334 FPU_Special
335 (&loaded_data);
336 #ifdef PECULIAR_486
337
338
339 if ((FPU_modrm & 0x28) == 0x20)
340
341 real_2op_NaN
342 (&loaded_data,
343 loaded_tag, 0,
344 &loaded_data);
345 else
346 #endif
347
348 real_2op_NaN
349 (&loaded_data,
350 loaded_tag, 0,
351 st0_ptr);
352 }
353 goto reg_mem_instr_done;
354 }
355
356 if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
357
358 if ((FPU_modrm & 0x38) == 0x38) {
359
360 if ((st0_tag == TAG_Zero) &&
361 ((loaded_tag == TAG_Valid)
362 || (loaded_tag ==
363 TAG_Special
364 &&
365 isdenormal
366 (&loaded_data)))) {
367 if (FPU_divide_by_zero
368 (0,
369 getsign
370 (&loaded_data))
371 < 0) {
372
373
374
375
376 partial_status
377 &=
378 ~SW_Denorm_Op;
379 partial_status
380 |=
381 status1 &
382 SW_Denorm_Op;
383 } else
384 setsign(st0_ptr,
385 getsign
386 (&loaded_data));
387 }
388 }
389 goto reg_mem_instr_done;
390 }
391
392 switch ((FPU_modrm >> 3) & 7) {
393 case 0:
394 clear_C1();
395 FPU_add(&loaded_data, loaded_tag, 0,
396 control_word);
397 break;
398 case 1:
399 clear_C1();
400 FPU_mul(&loaded_data, loaded_tag, 0,
401 control_word);
402 break;
403 case 2:
404 FPU_compare_st_data(&loaded_data,
405 loaded_tag);
406 break;
407 case 3:
408 if (!FPU_compare_st_data
409 (&loaded_data, loaded_tag)
410 && !unmasked)
411 FPU_pop();
412 break;
413 case 4:
414 clear_C1();
415 FPU_sub(LOADED | loaded_tag,
416 (int)&loaded_data,
417 control_word);
418 break;
419 case 5:
420 clear_C1();
421 FPU_sub(REV | LOADED | loaded_tag,
422 (int)&loaded_data,
423 control_word);
424 break;
425 case 6:
426 clear_C1();
427 FPU_div(LOADED | loaded_tag,
428 (int)&loaded_data,
429 control_word);
430 break;
431 case 7:
432 clear_C1();
433 if (st0_tag == TAG_Zero)
434 partial_status = status1;
435
436 FPU_div(REV | LOADED | loaded_tag,
437 (int)&loaded_data,
438 control_word);
439 break;
440 }
441 } else {
442 if ((FPU_modrm & 0x30) == 0x10) {
443
444 EXCEPTION(EX_StackUnder);
445 setcc(SW_C3 | SW_C2 | SW_C0);
446 if ((FPU_modrm & 0x08)
447 && (control_word & CW_Invalid))
448 FPU_pop();
449 } else
450 FPU_stack_underflow();
451 }
452 reg_mem_instr_done:
453 operand_address = data_sel_off;
454 } else {
455 if (!(no_ip_update =
456 FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
457 >> 1, addr_modes, data_address))) {
458 operand_address = data_sel_off;
459 }
460 }
461
462 } else {
463
464 u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
465
466 #ifdef PECULIAR_486
467
468
469 operand_address.offset = 0;
470 operand_address.selector = FPU_DS;
471 #endif
472
473 st0_ptr = &st(0);
474 st0_tag = FPU_gettag0();
475 switch (type_table[(int)instr_index]) {
476 case _NONE_:
477 break;
478 case _REG0_:
479 if (!NOT_EMPTY_ST0) {
480 FPU_stack_underflow();
481 goto FPU_instruction_done;
482 }
483 break;
484 case _REGIi:
485 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
486 FPU_stack_underflow_i(FPU_rm);
487 goto FPU_instruction_done;
488 }
489 break;
490 case _REGIp:
491 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
492 FPU_stack_underflow_pop(FPU_rm);
493 goto FPU_instruction_done;
494 }
495 break;
496 case _REGI_:
497 if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
498 FPU_stack_underflow();
499 goto FPU_instruction_done;
500 }
501 break;
502 case _PUSH_:
503 break;
504 case _null_:
505 FPU_illegal();
506 goto FPU_instruction_done;
507 default:
508 EXCEPTION(EX_INTERNAL | 0x111);
509 goto FPU_instruction_done;
510 }
511 (*st_instr_table[(int)instr_index]) ();
512
513 FPU_instruction_done:
514 ;
515 }
516
517 if (!no_ip_update)
518 instruction_address = entry_sel_off;
519
520 FPU_fwait_done:
521
522 #ifdef DEBUG
523 RE_ENTRANT_CHECK_OFF;
524 FPU_printall();
525 RE_ENTRANT_CHECK_ON;
526 #endif
527
528 if (FPU_lookahead && !need_resched()) {
529 FPU_ORIG_EIP = FPU_EIP - code_base;
530 if (valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
531 &addr_modes.override))
532 goto do_another_FPU_instruction;
533 }
534
535 if (addr_modes.default_mode)
536 FPU_EIP -= code_base;
537
538 RE_ENTRANT_CHECK_OFF;
539 }
540
541
542
543
544
545 static int valid_prefix(u_char *Byte, u_char __user **fpu_eip,
546 overrides * override)
547 {
548 u_char byte;
549 u_char __user *ip = *fpu_eip;
550
551 *override = (overrides) {
552 0, 0, PREFIX_DEFAULT};
553
554 RE_ENTRANT_CHECK_OFF;
555 FPU_code_access_ok(1);
556 FPU_get_user(byte, ip);
557 RE_ENTRANT_CHECK_ON;
558
559 while (1) {
560 switch (byte) {
561 case ADDR_SIZE_PREFIX:
562 override->address_size = ADDR_SIZE_PREFIX;
563 goto do_next_byte;
564
565 case OP_SIZE_PREFIX:
566 override->operand_size = OP_SIZE_PREFIX;
567 goto do_next_byte;
568
569 case PREFIX_CS:
570 override->segment = PREFIX_CS_;
571 goto do_next_byte;
572 case PREFIX_ES:
573 override->segment = PREFIX_ES_;
574 goto do_next_byte;
575 case PREFIX_SS:
576 override->segment = PREFIX_SS_;
577 goto do_next_byte;
578 case PREFIX_FS:
579 override->segment = PREFIX_FS_;
580 goto do_next_byte;
581 case PREFIX_GS:
582 override->segment = PREFIX_GS_;
583 goto do_next_byte;
584 case PREFIX_DS:
585 override->segment = PREFIX_DS_;
586 goto do_next_byte;
587
588
589
590
591
592
593 case PREFIX_REPE:
594 case PREFIX_REPNE:
595
596 do_next_byte:
597 ip++;
598 RE_ENTRANT_CHECK_OFF;
599 FPU_code_access_ok(1);
600 FPU_get_user(byte, ip);
601 RE_ENTRANT_CHECK_ON;
602 break;
603 case FWAIT_OPCODE:
604 *Byte = byte;
605 return 1;
606 default:
607 if ((byte & 0xf8) == 0xd8) {
608 *Byte = byte;
609 *fpu_eip = ip;
610 return 1;
611 } else {
612
613
614 *Byte = byte;
615 return 0;
616 }
617 }
618 }
619 }
620
621 void math_abort(struct math_emu_info *info, unsigned int signal)
622 {
623 FPU_EIP = FPU_ORIG_EIP;
624 current->thread.trap_nr = X86_TRAP_MF;
625 current->thread.error_code = 0;
626 send_sig(signal, current, 1);
627 RE_ENTRANT_CHECK_OFF;
628 __asm__("movl %0,%%esp ; ret": :"g"(((long)info) - 4));
629 #ifdef PARANOID
630 printk("ERROR: wm-FPU-emu math_abort failed!\n");
631 #endif
632 }
633
634 #define S387 ((struct swregs_state *)s387)
635 #define sstatus_word() \
636 ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
637
638 int fpregs_soft_set(struct task_struct *target,
639 const struct user_regset *regset,
640 unsigned int pos, unsigned int count,
641 const void *kbuf, const void __user *ubuf)
642 {
643 struct swregs_state *s387 = &target->thread.fpu.state.soft;
644 void *space = s387->st_space;
645 int ret;
646 int offset, other, i, tags, regnr, tag, newtop;
647
648 RE_ENTRANT_CHECK_OFF;
649 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
650 offsetof(struct swregs_state, st_space));
651 RE_ENTRANT_CHECK_ON;
652
653 if (ret)
654 return ret;
655
656 S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
657 offset = (S387->ftop & 7) * 10;
658 other = 80 - offset;
659
660 RE_ENTRANT_CHECK_OFF;
661
662
663 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
664 space + offset, 0, other);
665 if (!ret && offset)
666 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
667 space, 0, offset);
668
669 RE_ENTRANT_CHECK_ON;
670
671
672 tags = S387->twd;
673 newtop = S387->ftop;
674 for (i = 0; i < 8; i++) {
675 regnr = (i + newtop) & 7;
676 if (((tags >> ((regnr & 7) * 2)) & 3) != TAG_Empty) {
677
678 tag =
679 FPU_tagof((FPU_REG *) ((u_char *) S387->st_space +
680 10 * regnr));
681 tags &= ~(3 << (regnr * 2));
682 tags |= (tag & 3) << (regnr * 2);
683 }
684 }
685 S387->twd = tags;
686
687 return ret;
688 }
689
690 int fpregs_soft_get(struct task_struct *target,
691 const struct user_regset *regset,
692 unsigned int pos, unsigned int count,
693 void *kbuf, void __user *ubuf)
694 {
695 struct swregs_state *s387 = &target->thread.fpu.state.soft;
696 const void *space = s387->st_space;
697 int ret;
698 int offset = (S387->ftop & 7) * 10, other = 80 - offset;
699
700 RE_ENTRANT_CHECK_OFF;
701
702 #ifdef PECULIAR_486
703 S387->cwd &= ~0xe080;
704
705 S387->cwd |= 0xffff0040;
706 S387->swd = sstatus_word() | 0xffff0000;
707 S387->twd |= 0xffff0000;
708 S387->fcs &= ~0xf8000000;
709 S387->fos |= 0xffff0000;
710 #endif
711
712 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, s387, 0,
713 offsetof(struct swregs_state, st_space));
714
715
716 if (!ret)
717 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
718 space + offset, 0, other);
719 if (!ret)
720 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
721 space, 0, offset);
722
723 RE_ENTRANT_CHECK_ON;
724
725 return ret;
726 }