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