Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/math-emu/load_store.c
26442 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*---------------------------------------------------------------------------+
3
| load_store.c |
4
| |
5
| This file contains most of the code to interpret the FPU instructions |
6
| which load and store from user memory. |
7
| |
8
| Copyright (C) 1992,1993,1994,1997 |
9
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
10
| Australia. E-mail [email protected] |
11
| |
12
| |
13
+---------------------------------------------------------------------------*/
14
15
/*---------------------------------------------------------------------------+
16
| Note: |
17
| The file contains code which accesses user memory. |
18
| Emulator static data may change when user memory is accessed, due to |
19
| other processes using the emulator while swapping is in progress. |
20
+---------------------------------------------------------------------------*/
21
22
#include <linux/uaccess.h>
23
24
#include "fpu_system.h"
25
#include "exception.h"
26
#include "fpu_emu.h"
27
#include "status_w.h"
28
#include "control_w.h"
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
/* index is a 5-bit value: (3-bit FPU_modrm.reg field | opcode[2,1]) */
38
static u_char const type_table[32] = {
39
_PUSH_, _PUSH_, _PUSH_, _PUSH_, /* /0: d9:fld f32, db:fild m32, dd:fld f64, df:fild m16 */
40
_null_, _REG0_, _REG0_, _REG0_, /* /1: d9:undef, db,dd,df:fisttp m32/64/16 */
41
_REG0_, _REG0_, _REG0_, _REG0_, /* /2: d9:fst f32, db:fist m32, dd:fst f64, df:fist m16 */
42
_REG0_, _REG0_, _REG0_, _REG0_, /* /3: d9:fstp f32, db:fistp m32, dd:fstp f64, df:fistp m16 */
43
_NONE_, _null_, _NONE_, _PUSH_,
44
_NONE_, _PUSH_, _null_, _PUSH_,
45
_NONE_, _null_, _NONE_, _REG0_,
46
_NONE_, _REG0_, _NONE_, _REG0_
47
};
48
49
u_char const data_sizes_16[32] = {
50
4, 4, 8, 2,
51
0, 4, 8, 2, /* /1: d9:undef, db,dd,df:fisttp */
52
4, 4, 8, 2,
53
4, 4, 8, 2,
54
14, 0, 94, 10, 2, 10, 0, 8,
55
14, 0, 94, 10, 2, 10, 2, 8
56
};
57
58
static u_char const data_sizes_32[32] = {
59
4, 4, 8, 2,
60
0, 4, 8, 2, /* /1: d9:undef, db,dd,df:fisttp */
61
4, 4, 8, 2,
62
4, 4, 8, 2,
63
28, 0, 108, 10, 2, 10, 0, 8,
64
28, 0, 108, 10, 2, 10, 2, 8
65
};
66
67
int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
68
void __user * data_address)
69
{
70
FPU_REG loaded_data;
71
FPU_REG *st0_ptr;
72
u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */
73
u_char loaded_tag;
74
int sv_cw;
75
76
st0_ptr = NULL; /* Initialized just to stop compiler warnings. */
77
78
if (addr_modes.default_mode & PROTECTED) {
79
if (addr_modes.default_mode == SEG32) {
80
if (access_limit < data_sizes_32[type])
81
math_abort(FPU_info, SIGSEGV);
82
} else if (addr_modes.default_mode == PM16) {
83
if (access_limit < data_sizes_16[type])
84
math_abort(FPU_info, SIGSEGV);
85
}
86
#ifdef PARANOID
87
else
88
EXCEPTION(EX_INTERNAL | 0x140);
89
#endif /* PARANOID */
90
}
91
92
switch (type_table[type]) {
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();
104
return 0;
105
}
106
top--;
107
st0_ptr = &st(0);
108
}
109
break;
110
case _null_:
111
FPU_illegal();
112
return 0;
113
#ifdef PARANOID
114
default:
115
EXCEPTION(EX_INTERNAL | 0x141);
116
return 0;
117
#endif /* PARANOID */
118
}
119
120
switch (type) {
121
/* type is a 5-bit value: (3-bit FPU_modrm.reg field | opcode[2,1]) */
122
case 000: /* fld m32real (d9 /0) */
123
clear_C1();
124
loaded_tag =
125
FPU_load_single((float __user *)data_address, &loaded_data);
126
if ((loaded_tag == TAG_Special)
127
&& isNaN(&loaded_data)
128
&& (real_1op_NaN(&loaded_data) < 0)) {
129
top++;
130
break;
131
}
132
FPU_copy_to_reg0(&loaded_data, loaded_tag);
133
break;
134
case 001: /* fild m32int (db /0) */
135
clear_C1();
136
loaded_tag =
137
FPU_load_int32((long __user *)data_address, &loaded_data);
138
FPU_copy_to_reg0(&loaded_data, loaded_tag);
139
break;
140
case 002: /* fld m64real (dd /0) */
141
clear_C1();
142
loaded_tag =
143
FPU_load_double((double __user *)data_address,
144
&loaded_data);
145
if ((loaded_tag == TAG_Special)
146
&& isNaN(&loaded_data)
147
&& (real_1op_NaN(&loaded_data) < 0)) {
148
top++;
149
break;
150
}
151
FPU_copy_to_reg0(&loaded_data, loaded_tag);
152
break;
153
case 003: /* fild m16int (df /0) */
154
clear_C1();
155
loaded_tag =
156
FPU_load_int16((short __user *)data_address, &loaded_data);
157
FPU_copy_to_reg0(&loaded_data, loaded_tag);
158
break;
159
/* case 004: undefined (d9 /1) */
160
/* fisttp are enabled if CPUID(1).ECX(0) "sse3" is set */
161
case 005: /* fisttp m32int (db /1) */
162
clear_C1();
163
sv_cw = control_word;
164
control_word |= RC_CHOP;
165
if (FPU_store_int32
166
(st0_ptr, st0_tag, (long __user *)data_address))
167
pop_0(); /* pop only if the number was actually stored
168
(see the 80486 manual p16-28) */
169
control_word = sv_cw;
170
break;
171
case 006: /* fisttp m64int (dd /1) */
172
clear_C1();
173
sv_cw = control_word;
174
control_word |= RC_CHOP;
175
if (FPU_store_int64
176
(st0_ptr, st0_tag, (long long __user *)data_address))
177
pop_0(); /* pop only if the number was actually stored
178
(see the 80486 manual p16-28) */
179
control_word = sv_cw;
180
break;
181
case 007: /* fisttp m16int (df /1) */
182
clear_C1();
183
sv_cw = control_word;
184
control_word |= RC_CHOP;
185
if (FPU_store_int16
186
(st0_ptr, st0_tag, (short __user *)data_address))
187
pop_0(); /* pop only if the number was actually stored
188
(see the 80486 manual p16-28) */
189
control_word = sv_cw;
190
break;
191
case 010: /* fst m32real */
192
clear_C1();
193
FPU_store_single(st0_ptr, st0_tag,
194
(float __user *)data_address);
195
break;
196
case 011: /* fist m32int */
197
clear_C1();
198
FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address);
199
break;
200
case 012: /* fst m64real */
201
clear_C1();
202
FPU_store_double(st0_ptr, st0_tag,
203
(double __user *)data_address);
204
break;
205
case 013: /* fist m16int */
206
clear_C1();
207
FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address);
208
break;
209
case 014: /* fstp m32real */
210
clear_C1();
211
if (FPU_store_single
212
(st0_ptr, st0_tag, (float __user *)data_address))
213
pop_0(); /* pop only if the number was actually stored
214
(see the 80486 manual p16-28) */
215
break;
216
case 015: /* fistp m32int */
217
clear_C1();
218
if (FPU_store_int32
219
(st0_ptr, st0_tag, (long __user *)data_address))
220
pop_0(); /* pop only if the number was actually stored
221
(see the 80486 manual p16-28) */
222
break;
223
case 016: /* fstp m64real */
224
clear_C1();
225
if (FPU_store_double
226
(st0_ptr, st0_tag, (double __user *)data_address))
227
pop_0(); /* pop only if the number was actually stored
228
(see the 80486 manual p16-28) */
229
break;
230
case 017: /* fistp m16int */
231
clear_C1();
232
if (FPU_store_int16
233
(st0_ptr, st0_tag, (short __user *)data_address))
234
pop_0(); /* pop only if the number was actually stored
235
(see the 80486 manual p16-28) */
236
break;
237
case 020: /* fldenv m14/28byte */
238
fldenv(addr_modes, (u_char __user *) data_address);
239
/* Ensure that the values just loaded are not changed by
240
fix-up operations. */
241
return 1;
242
case 022: /* frstor m94/108byte */
243
FPU_frstor(addr_modes, (u_char __user *) data_address);
244
/* Ensure that the values just loaded are not changed by
245
fix-up operations. */
246
return 1;
247
case 023: /* fbld m80dec */
248
clear_C1();
249
loaded_tag = FPU_load_bcd((u_char __user *) data_address);
250
FPU_settag0(loaded_tag);
251
break;
252
case 024: /* fldcw */
253
RE_ENTRANT_CHECK_OFF;
254
FPU_access_ok(data_address, 2);
255
FPU_get_user(control_word,
256
(unsigned short __user *)data_address);
257
RE_ENTRANT_CHECK_ON;
258
if (partial_status & ~control_word & CW_Exceptions)
259
partial_status |= (SW_Summary | SW_Backward);
260
else
261
partial_status &= ~(SW_Summary | SW_Backward);
262
#ifdef PECULIAR_486
263
control_word |= 0x40; /* An 80486 appears to always set this bit */
264
#endif /* PECULIAR_486 */
265
return 1;
266
case 025: /* fld m80real */
267
clear_C1();
268
loaded_tag =
269
FPU_load_extended((long double __user *)data_address, 0);
270
FPU_settag0(loaded_tag);
271
break;
272
case 027: /* fild m64int */
273
clear_C1();
274
loaded_tag = FPU_load_int64((long long __user *)data_address);
275
if (loaded_tag == TAG_Error)
276
return 0;
277
FPU_settag0(loaded_tag);
278
break;
279
case 030: /* fstenv m14/28byte */
280
fstenv(addr_modes, (u_char __user *) data_address);
281
return 1;
282
case 032: /* fsave */
283
fsave(addr_modes, (u_char __user *) data_address);
284
return 1;
285
case 033: /* fbstp m80dec */
286
clear_C1();
287
if (FPU_store_bcd
288
(st0_ptr, st0_tag, (u_char __user *) data_address))
289
pop_0(); /* pop only if the number was actually stored
290
(see the 80486 manual p16-28) */
291
break;
292
case 034: /* fstcw m16int */
293
RE_ENTRANT_CHECK_OFF;
294
FPU_access_ok(data_address, 2);
295
FPU_put_user(control_word,
296
(unsigned short __user *)data_address);
297
RE_ENTRANT_CHECK_ON;
298
return 1;
299
case 035: /* fstp m80real */
300
clear_C1();
301
if (FPU_store_extended
302
(st0_ptr, st0_tag, (long double __user *)data_address))
303
pop_0(); /* pop only if the number was actually stored
304
(see the 80486 manual p16-28) */
305
break;
306
case 036: /* fstsw m2byte */
307
RE_ENTRANT_CHECK_OFF;
308
FPU_access_ok(data_address, 2);
309
FPU_put_user(status_word(),
310
(unsigned short __user *)data_address);
311
RE_ENTRANT_CHECK_ON;
312
return 1;
313
case 037: /* fistp m64int */
314
clear_C1();
315
if (FPU_store_int64
316
(st0_ptr, st0_tag, (long long __user *)data_address))
317
pop_0(); /* pop only if the number was actually stored
318
(see the 80486 manual p16-28) */
319
break;
320
}
321
return 0;
322
}
323
324