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