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