blob: 2931ff355218041221033379c18aa7b6c4b9458c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*---------------------------------------------------------------------------+
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
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010029#define _NONE_ 0 /* st0_ptr etc not needed */
30#define _REG0_ 1 /* Will be storing st(0) */
31#define _PUSH_ 3 /* Need to check for space to push onto stack */
32#define _null_ 4 /* Function illegal or not implemented */
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34#define pop_0() { FPU_settag0(TAG_Empty); top++; }
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036static u_char const type_table[32] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010037 _PUSH_, _PUSH_, _PUSH_, _PUSH_,
38 _null_, _null_, _null_, _null_,
39 _REG0_, _REG0_, _REG0_, _REG0_,
40 _REG0_, _REG0_, _REG0_, _REG0_,
41 _NONE_, _null_, _NONE_, _PUSH_,
42 _NONE_, _PUSH_, _null_, _PUSH_,
43 _NONE_, _null_, _NONE_, _REG0_,
44 _NONE_, _REG0_, _NONE_, _REG0_
45};
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47u_char const data_sizes_16[32] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010048 4, 4, 8, 2, 0, 0, 0, 0,
49 4, 4, 8, 2, 4, 4, 8, 2,
50 14, 0, 94, 10, 2, 10, 0, 8,
51 14, 0, 94, 10, 2, 10, 2, 8
Linus Torvalds1da177e2005-04-16 15:20:36 -070052};
53
54static u_char const data_sizes_32[32] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010055 4, 4, 8, 2, 0, 0, 0, 0,
56 4, 4, 8, 2, 4, 4, 8, 2,
57 28, 0, 108, 10, 2, 10, 0, 8,
58 28, 0, 108, 10, 2, 10, 2, 8
Linus Torvalds1da177e2005-04-16 15:20:36 -070059};
60
61int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010062 void __user * data_address)
Linus Torvalds1da177e2005-04-16 15:20:36 -070063{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010064 FPU_REG loaded_data;
65 FPU_REG *st0_ptr;
66 u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */
67 u_char loaded_tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010069 st0_ptr = NULL; /* Initialized just to stop compiler warnings. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010071 if (addr_modes.default_mode & PROTECTED) {
72 if (addr_modes.default_mode == SEG32) {
73 if (access_limit < data_sizes_32[type])
74 math_abort(FPU_info, SIGSEGV);
75 } else if (addr_modes.default_mode == PM16) {
76 if (access_limit < data_sizes_16[type])
77 math_abort(FPU_info, SIGSEGV);
78 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070079#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010080 else
81 EXCEPTION(EX_INTERNAL | 0x140);
Linus Torvalds1da177e2005-04-16 15:20:36 -070082#endif /* PARANOID */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010083 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010085 switch (type_table[type]) {
86 case _NONE_:
87 break;
88 case _REG0_:
89 st0_ptr = &st(0); /* Some of these instructions pop after
90 storing */
91 st0_tag = FPU_gettag0();
92 break;
93 case _PUSH_:
94 {
95 if (FPU_gettagi(-1) != TAG_Empty) {
96 FPU_stack_overflow();
97 return 0;
98 }
99 top--;
100 st0_ptr = &st(0);
101 }
102 break;
103 case _null_:
104 FPU_illegal();
105 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100107 default:
108 EXCEPTION(EX_INTERNAL | 0x141);
109 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110#endif /* PARANOID */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100111 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100113 switch (type) {
114 case 000: /* fld m32real */
115 clear_C1();
116 loaded_tag =
117 FPU_load_single((float __user *)data_address, &loaded_data);
118 if ((loaded_tag == TAG_Special)
119 && isNaN(&loaded_data)
120 && (real_1op_NaN(&loaded_data) < 0)) {
121 top++;
122 break;
123 }
124 FPU_copy_to_reg0(&loaded_data, loaded_tag);
125 break;
126 case 001: /* fild m32int */
127 clear_C1();
128 loaded_tag =
129 FPU_load_int32((long __user *)data_address, &loaded_data);
130 FPU_copy_to_reg0(&loaded_data, loaded_tag);
131 break;
132 case 002: /* fld m64real */
133 clear_C1();
134 loaded_tag =
135 FPU_load_double((double __user *)data_address,
136 &loaded_data);
137 if ((loaded_tag == TAG_Special)
138 && isNaN(&loaded_data)
139 && (real_1op_NaN(&loaded_data) < 0)) {
140 top++;
141 break;
142 }
143 FPU_copy_to_reg0(&loaded_data, loaded_tag);
144 break;
145 case 003: /* fild m16int */
146 clear_C1();
147 loaded_tag =
148 FPU_load_int16((short __user *)data_address, &loaded_data);
149 FPU_copy_to_reg0(&loaded_data, loaded_tag);
150 break;
151 case 010: /* fst m32real */
152 clear_C1();
153 FPU_store_single(st0_ptr, st0_tag,
154 (float __user *)data_address);
155 break;
156 case 011: /* fist m32int */
157 clear_C1();
158 FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address);
159 break;
160 case 012: /* fst m64real */
161 clear_C1();
162 FPU_store_double(st0_ptr, st0_tag,
163 (double __user *)data_address);
164 break;
165 case 013: /* fist m16int */
166 clear_C1();
167 FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address);
168 break;
169 case 014: /* fstp m32real */
170 clear_C1();
171 if (FPU_store_single
172 (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
179 (st0_ptr, st0_tag, (long __user *)data_address))
180 pop_0(); /* pop only if the number was actually stored
181 (see the 80486 manual p16-28) */
182 break;
183 case 016: /* fstp m64real */
184 clear_C1();
185 if (FPU_store_double
186 (st0_ptr, st0_tag, (double __user *)data_address))
187 pop_0(); /* pop only if the number was actually stored
188 (see the 80486 manual p16-28) */
189 break;
190 case 017: /* fistp m16int */
191 clear_C1();
192 if (FPU_store_int16
193 (st0_ptr, st0_tag, (short __user *)data_address))
194 pop_0(); /* pop only if the number was actually stored
195 (see the 80486 manual p16-28) */
196 break;
197 case 020: /* fldenv m14/28byte */
198 fldenv(addr_modes, (u_char __user *) data_address);
199 /* Ensure that the values just loaded are not changed by
200 fix-up operations. */
201 return 1;
202 case 022: /* frstor m94/108byte */
203 frstor(addr_modes, (u_char __user *) data_address);
204 /* Ensure that the values just loaded are not changed by
205 fix-up operations. */
206 return 1;
207 case 023: /* fbld m80dec */
208 clear_C1();
209 loaded_tag = FPU_load_bcd((u_char __user *) data_address);
210 FPU_settag0(loaded_tag);
211 break;
212 case 024: /* fldcw */
213 RE_ENTRANT_CHECK_OFF;
214 FPU_access_ok(VERIFY_READ, data_address, 2);
215 FPU_get_user(control_word,
216 (unsigned short __user *)data_address);
217 RE_ENTRANT_CHECK_ON;
218 if (partial_status & ~control_word & CW_Exceptions)
219 partial_status |= (SW_Summary | SW_Backward);
220 else
221 partial_status &= ~(SW_Summary | SW_Backward);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100223 control_word |= 0x40; /* An 80486 appears to always set this bit */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224#endif /* PECULIAR_486 */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100225 return 1;
226 case 025: /* fld m80real */
227 clear_C1();
228 loaded_tag =
229 FPU_load_extended((long double __user *)data_address, 0);
230 FPU_settag0(loaded_tag);
231 break;
232 case 027: /* fild m64int */
233 clear_C1();
234 loaded_tag = FPU_load_int64((long long __user *)data_address);
235 if (loaded_tag == TAG_Error)
236 return 0;
237 FPU_settag0(loaded_tag);
238 break;
239 case 030: /* fstenv m14/28byte */
240 fstenv(addr_modes, (u_char __user *) data_address);
241 return 1;
242 case 032: /* fsave */
243 fsave(addr_modes, (u_char __user *) data_address);
244 return 1;
245 case 033: /* fbstp m80dec */
246 clear_C1();
247 if (FPU_store_bcd
248 (st0_ptr, st0_tag, (u_char __user *) data_address))
249 pop_0(); /* pop only if the number was actually stored
250 (see the 80486 manual p16-28) */
251 break;
252 case 034: /* fstcw m16int */
253 RE_ENTRANT_CHECK_OFF;
254 FPU_access_ok(VERIFY_WRITE, data_address, 2);
255 FPU_put_user(control_word,
256 (unsigned short __user *)data_address);
257 RE_ENTRANT_CHECK_ON;
258 return 1;
259 case 035: /* fstp m80real */
260 clear_C1();
261 if (FPU_store_extended
262 (st0_ptr, st0_tag, (long double __user *)data_address))
263 pop_0(); /* pop only if the number was actually stored
264 (see the 80486 manual p16-28) */
265 break;
266 case 036: /* fstsw m2byte */
267 RE_ENTRANT_CHECK_OFF;
268 FPU_access_ok(VERIFY_WRITE, data_address, 2);
269 FPU_put_user(status_word(),
270 (unsigned short __user *)data_address);
271 RE_ENTRANT_CHECK_ON;
272 return 1;
273 case 037: /* fistp m64int */
274 clear_C1();
275 if (FPU_store_int64
276 (st0_ptr, st0_tag, (long long __user *)data_address))
277 pop_0(); /* pop only if the number was actually stored
278 (see the 80486 manual p16-28) */
279 break;
280 }
Randy Dunlapd606f882006-12-07 02:14:00 +0100281 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282}