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