~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/x86/math-emu/fpu_entry.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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("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(&code_descriptor);
158                 code_limit = seg_get_limit(&code_descriptor) + 1;
159                 code_limit *= seg_get_granularity(&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(&byte1, (u_char __user **) & FPU_EIP,
168                           &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,
253                                                &data_sel_off, addr_modes);
254                 else
255                         data_address =
256                             FPU_get_address(FPU_modrm, &FPU_EIP, &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((float __user *)
284                                                             data_address,
285                                                             &loaded_data);
286                                         loaded_tag = unmasked & 0xff;
287                                         unmasked &= ~0xff;
288                                         break;
289                                 case 1:
290                                         loaded_tag =
291                                             FPU_load_int32((long __user *)
292                                                            data_address,
293                                                            &loaded_data);
294                                         break;
295                                 case 2:
296                                         unmasked =
297                                             FPU_load_double((double __user *)
298                                                             data_address,
299                                                             &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((short __user *)
307                                                            data_address,
308                                                            &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(st0_ptr))
319                                     || ((loaded_tag == TAG_Special)
320                                         && isNaN(&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                                                             (&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                                                             (&loaded_data,
344                                                              loaded_tag, 0,
345                                                              &loaded_data);
346                                                 else
347 #endif /* PECULIAR_486 */
348                                                         /* fadd, fdivr, fmul, or fsubr */
349                                                         real_2op_NaN
350                                                             (&loaded_data,
351                                                              loaded_tag, 0,
352                                                              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                                                             (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(&loaded_data, loaded_tag, 0,
397                                                 control_word);
398                                         break;
399                                 case 1: /* fmul */
400                                         clear_C1();
401                                         FPU_mul(&loaded_data, loaded_tag, 0,
402                                                 control_word);
403                                         break;
404                                 case 2: /* fcom */
405                                         FPU_compare_st_data(&loaded_data,
406                                                             loaded_tag);
407                                         break;
408                                 case 3: /* fcomp */
409                                         if (!FPU_compare_st_data
410                                             (&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                                                 (int)&loaded_data,
418                                                 control_word);
419                                         break;
420                                 case 5: /* fsubr */
421                                         clear_C1();
422                                         FPU_sub(REV | LOADED | loaded_tag,
423                                                 (int)&loaded_data,
424                                                 control_word);
425                                         break;
426                                 case 6: /* fdiv */
427                                         clear_C1();
428                                         FPU_div(LOADED | loaded_tag,
429                                                 (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                                                 (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(((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(&byte1, (u_char __user **) & FPU_EIP,
532                                  &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, &count, &kbuf, &ubuf, s387, 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, &count, &kbuf, &ubuf,
665                                  space + offset, 0, other);
666         if (!ret && offset)
667                 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
668                                          space, 0, 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((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(&to, s387, offsetof(struct swregs_state, st_space));
712         membuf_write(&to, space + offset, other);
713         membuf_write(&to, space, offset);
714 
715         RE_ENTRANT_CHECK_ON;
716 
717         return 0;
718 }
719 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php