1/*---------------------------------------------------------------------------+
2 |  load_store.c                                                             |
3 |                                                                           |
4 | This file contains most of the code to interpret the FPU instructions     |
5 | which load and store from user memory.                                    |
6 |                                                                           |
7 | Copyright (C) 1992,1993,1994,1997                                         |
8 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
9 |                       Australia.  E-mail   billm@suburbia.net             |
10 |                                                                           |
11 |                                                                           |
12 +---------------------------------------------------------------------------*/
13
14/*---------------------------------------------------------------------------+
15 | Note:                                                                     |
16 |    The file contains code which accesses user memory.                     |
17 |    Emulator static data may change when user memory is accessed, due to   |
18 |    other processes using the emulator while swapping is in progress.      |
19 +---------------------------------------------------------------------------*/
20
21#include <asm/uaccess.h>
22
23#include "fpu_system.h"
24#include "exception.h"
25#include "fpu_emu.h"
26#include "status_w.h"
27#include "control_w.h"
28
29
30#define _NONE_ 0   /* st0_ptr etc not needed */
31#define _REG0_ 1   /* Will be storing st(0) */
32#define _PUSH_ 3   /* Need to check for space to push onto stack */
33#define _null_ 4   /* Function illegal or not implemented */
34
35#define pop_0()	{ FPU_settag0(TAG_Empty); top++; }
36
37
38static u_char const type_table[32] = {
39  _PUSH_, _PUSH_, _PUSH_, _PUSH_,
40  _null_, _null_, _null_, _null_,
41  _REG0_, _REG0_, _REG0_, _REG0_,
42  _REG0_, _REG0_, _REG0_, _REG0_,
43  _NONE_, _null_, _NONE_, _PUSH_,
44  _NONE_, _PUSH_, _null_, _PUSH_,
45  _NONE_, _null_, _NONE_, _REG0_,
46  _NONE_, _REG0_, _NONE_, _REG0_
47  };
48
49u_char const data_sizes_16[32] = {
50  4,  4,  8,  2,  0,  0,  0,  0,
51  4,  4,  8,  2,  4,  4,  8,  2,
52  14, 0, 94, 10,  2, 10,  0,  8,
53  14, 0, 94, 10,  2, 10,  2,  8
54};
55
56static u_char const data_sizes_32[32] = {
57  4,  4,  8,  2,  0,  0,  0,  0,
58  4,  4,  8,  2,  4,  4,  8,  2,
59  28, 0,108, 10,  2, 10,  0,  8,
60  28, 0,108, 10,  2, 10,  2,  8
61};
62
63int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
64		     void __user *data_address)
65{
66  FPU_REG loaded_data;
67  FPU_REG *st0_ptr;
68  u_char st0_tag = TAG_Empty;  /* This is just to stop a gcc warning. */
69  u_char loaded_tag;
70
71  st0_ptr = NULL;    /* Initialized just to stop compiler warnings. */
72
73  if ( addr_modes.default_mode & PROTECTED )
74    {
75      if ( addr_modes.default_mode == SEG32 )
76	{
77	  if ( access_limit < data_sizes_32[type] )
78	    math_abort(FPU_info,SIGSEGV);
79	}
80      else if ( addr_modes.default_mode == PM16 )
81	{
82	  if ( access_limit < data_sizes_16[type] )
83	    math_abort(FPU_info,SIGSEGV);
84	}
85#ifdef PARANOID
86      else
87	EXCEPTION(EX_INTERNAL|0x140);
88#endif /* PARANOID */
89    }
90
91  switch ( type_table[type] )
92    {
93    case _NONE_:
94      break;
95    case _REG0_:
96      st0_ptr = &st(0);       /* Some of these instructions pop after
97				 storing */
98      st0_tag = FPU_gettag0();
99      break;
100    case _PUSH_:
101      {
102	if ( FPU_gettagi(-1) != TAG_Empty )
103	  { FPU_stack_overflow(); return 0; }
104	top--;
105	st0_ptr = &st(0);
106      }
107      break;
108    case _null_:
109      FPU_illegal();
110      return 0;
111#ifdef PARANOID
112    default:
113      EXCEPTION(EX_INTERNAL|0x141);
114      return 0;
115#endif /* PARANOID */
116    }
117
118  switch ( type )
119    {
120    case 000:       /* fld m32real */
121      clear_C1();
122      loaded_tag = FPU_load_single((float __user *)data_address, &loaded_data);
123      if ( (loaded_tag == TAG_Special)
124	   && isNaN(&loaded_data)
125	   && (real_1op_NaN(&loaded_data) < 0) )
126	{
127	  top++;
128	  break;
129	}
130      FPU_copy_to_reg0(&loaded_data, loaded_tag);
131      break;
132    case 001:      /* fild m32int */
133      clear_C1();
134      loaded_tag = FPU_load_int32((long __user *)data_address, &loaded_data);
135      FPU_copy_to_reg0(&loaded_data, loaded_tag);
136      break;
137    case 002:      /* fld m64real */
138      clear_C1();
139      loaded_tag = FPU_load_double((double __user *)data_address, &loaded_data);
140      if ( (loaded_tag == TAG_Special)
141	   && isNaN(&loaded_data)
142	   && (real_1op_NaN(&loaded_data) < 0) )
143	{
144	  top++;
145	  break;
146	}
147      FPU_copy_to_reg0(&loaded_data, loaded_tag);
148      break;
149    case 003:      /* fild m16int */
150      clear_C1();
151      loaded_tag = FPU_load_int16((short __user *)data_address, &loaded_data);
152      FPU_copy_to_reg0(&loaded_data, loaded_tag);
153      break;
154    case 010:      /* fst m32real */
155      clear_C1();
156      FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address);
157      break;
158    case 011:      /* fist m32int */
159      clear_C1();
160      FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address);
161      break;
162    case 012:     /* fst m64real */
163      clear_C1();
164      FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address);
165      break;
166    case 013:     /* fist m16int */
167      clear_C1();
168      FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address);
169      break;
170    case 014:     /* fstp m32real */
171      clear_C1();
172      if ( FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address) )
173	pop_0();  /* pop only if the number was actually stored
174		     (see the 80486 manual p16-28) */
175      break;
176    case 015:     /* fistp m32int */
177      clear_C1();
178      if ( FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address) )
179	pop_0();  /* pop only if the number was actually stored
180		     (see the 80486 manual p16-28) */
181      break;
182    case 016:     /* fstp m64real */
183      clear_C1();
184      if ( FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address) )
185	pop_0();  /* pop only if the number was actually stored
186		     (see the 80486 manual p16-28) */
187      break;
188    case 017:     /* fistp m16int */
189      clear_C1();
190      if ( FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address) )
191	pop_0();  /* pop only if the number was actually stored
192		     (see the 80486 manual p16-28) */
193      break;
194    case 020:     /* fldenv  m14/28byte */
195      fldenv(addr_modes, (u_char __user *)data_address);
196      /* Ensure that the values just loaded are not changed by
197	 fix-up operations. */
198      return 1;
199    case 022:     /* frstor m94/108byte */
200      frstor(addr_modes, (u_char __user *)data_address);
201      /* Ensure that the values just loaded are not changed by
202	 fix-up operations. */
203      return 1;
204    case 023:     /* fbld m80dec */
205      clear_C1();
206      loaded_tag = FPU_load_bcd((u_char __user *)data_address);
207      FPU_settag0(loaded_tag);
208      break;
209    case 024:     /* fldcw */
210      RE_ENTRANT_CHECK_OFF;
211      FPU_access_ok(VERIFY_READ, data_address, 2);
212      FPU_get_user(control_word, (unsigned short __user *) data_address);
213      RE_ENTRANT_CHECK_ON;
214      if ( partial_status & ~control_word & CW_Exceptions )
215	partial_status |= (SW_Summary | SW_Backward);
216      else
217	partial_status &= ~(SW_Summary | SW_Backward);
218#ifdef PECULIAR_486
219      control_word |= 0x40;  /* An 80486 appears to always set this bit */
220#endif /* PECULIAR_486 */
221      return 1;
222    case 025:      /* fld m80real */
223      clear_C1();
224      loaded_tag = FPU_load_extended((long double __user *)data_address, 0);
225      FPU_settag0(loaded_tag);
226      break;
227    case 027:      /* fild m64int */
228      clear_C1();
229      loaded_tag = FPU_load_int64((long long __user *)data_address);
230      if (loaded_tag == TAG_Error)
231	return 0;
232      FPU_settag0(loaded_tag);
233      break;
234    case 030:     /* fstenv  m14/28byte */
235      fstenv(addr_modes, (u_char __user *)data_address);
236      return 1;
237    case 032:      /* fsave */
238      fsave(addr_modes, (u_char __user *)data_address);
239      return 1;
240    case 033:      /* fbstp m80dec */
241      clear_C1();
242      if ( FPU_store_bcd(st0_ptr, st0_tag, (u_char __user *)data_address) )
243	pop_0();  /* pop only if the number was actually stored
244		     (see the 80486 manual p16-28) */
245      break;
246    case 034:      /* fstcw m16int */
247      RE_ENTRANT_CHECK_OFF;
248      FPU_access_ok(VERIFY_WRITE,data_address,2);
249      FPU_put_user(control_word, (unsigned short __user *) data_address);
250      RE_ENTRANT_CHECK_ON;
251      return 1;
252    case 035:      /* fstp m80real */
253      clear_C1();
254      if ( FPU_store_extended(st0_ptr, st0_tag, (long double __user *)data_address) )
255	pop_0();  /* pop only if the number was actually stored
256		     (see the 80486 manual p16-28) */
257      break;
258    case 036:      /* fstsw m2byte */
259      RE_ENTRANT_CHECK_OFF;
260      FPU_access_ok(VERIFY_WRITE,data_address,2);
261      FPU_put_user(status_word(),(unsigned short __user *) data_address);
262      RE_ENTRANT_CHECK_ON;
263      return 1;
264    case 037:      /* fistp m64int */
265      clear_C1();
266      if ( FPU_store_int64(st0_ptr, st0_tag, (long long __user *)data_address) )
267	pop_0();  /* pop only if the number was actually stored
268		     (see the 80486 manual p16-28) */
269      break;
270    }
271  return 0;
272}
273