1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2da957e11SThomas Gleixner /*---------------------------------------------------------------------------+
3da957e11SThomas Gleixner | reg_ld_str.c |
4da957e11SThomas Gleixner | |
5da957e11SThomas Gleixner | All of the functions which transfer data between user memory and FPU_REGs.|
6da957e11SThomas Gleixner | |
7da957e11SThomas Gleixner | Copyright (C) 1992,1993,1994,1996,1997 |
8da957e11SThomas Gleixner | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9da957e11SThomas Gleixner | E-mail billm@suburbia.net |
10da957e11SThomas Gleixner | |
11da957e11SThomas Gleixner | |
12da957e11SThomas Gleixner +---------------------------------------------------------------------------*/
13da957e11SThomas Gleixner
14da957e11SThomas Gleixner /*---------------------------------------------------------------------------+
15da957e11SThomas Gleixner | Note: |
16da957e11SThomas Gleixner | The file contains code which accesses user memory. |
17da957e11SThomas Gleixner | Emulator static data may change when user memory is accessed, due to |
18da957e11SThomas Gleixner | other processes using the emulator while swapping is in progress. |
19da957e11SThomas Gleixner +---------------------------------------------------------------------------*/
20da957e11SThomas Gleixner
21da957e11SThomas Gleixner #include "fpu_emu.h"
22da957e11SThomas Gleixner
237c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
24da957e11SThomas Gleixner
25da957e11SThomas Gleixner #include "fpu_system.h"
26da957e11SThomas Gleixner #include "exception.h"
27da957e11SThomas Gleixner #include "reg_constant.h"
28da957e11SThomas Gleixner #include "control_w.h"
29da957e11SThomas Gleixner #include "status_w.h"
30da957e11SThomas Gleixner
31da957e11SThomas Gleixner #define DOUBLE_Emax 1023 /* largest valid exponent */
32da957e11SThomas Gleixner #define DOUBLE_Ebias 1023
33da957e11SThomas Gleixner #define DOUBLE_Emin (-1022) /* smallest valid exponent */
34da957e11SThomas Gleixner
35da957e11SThomas Gleixner #define SINGLE_Emax 127 /* largest valid exponent */
36da957e11SThomas Gleixner #define SINGLE_Ebias 127
37da957e11SThomas Gleixner #define SINGLE_Emin (-126) /* smallest valid exponent */
38da957e11SThomas Gleixner
normalize_no_excep(FPU_REG * r,int exp,int sign)39da957e11SThomas Gleixner static u_char normalize_no_excep(FPU_REG *r, int exp, int sign)
40da957e11SThomas Gleixner {
41da957e11SThomas Gleixner u_char tag;
42da957e11SThomas Gleixner
43da957e11SThomas Gleixner setexponent16(r, exp);
44da957e11SThomas Gleixner
45da957e11SThomas Gleixner tag = FPU_normalize_nuo(r);
46da957e11SThomas Gleixner stdexp(r);
47da957e11SThomas Gleixner if (sign)
48da957e11SThomas Gleixner setnegative(r);
49da957e11SThomas Gleixner
50da957e11SThomas Gleixner return tag;
51da957e11SThomas Gleixner }
52da957e11SThomas Gleixner
FPU_tagof(FPU_REG * ptr)53da957e11SThomas Gleixner int FPU_tagof(FPU_REG *ptr)
54da957e11SThomas Gleixner {
55da957e11SThomas Gleixner int exp;
56da957e11SThomas Gleixner
57da957e11SThomas Gleixner exp = exponent16(ptr) & 0x7fff;
583d0d14f9SIngo Molnar if (exp == 0) {
593d0d14f9SIngo Molnar if (!(ptr->sigh | ptr->sigl)) {
60da957e11SThomas Gleixner return TAG_Zero;
61da957e11SThomas Gleixner }
62da957e11SThomas Gleixner /* The number is a de-normal or pseudodenormal. */
63da957e11SThomas Gleixner return TAG_Special;
64da957e11SThomas Gleixner }
65da957e11SThomas Gleixner
663d0d14f9SIngo Molnar if (exp == 0x7fff) {
67da957e11SThomas Gleixner /* Is an Infinity, a NaN, or an unsupported data type. */
68da957e11SThomas Gleixner return TAG_Special;
69da957e11SThomas Gleixner }
70da957e11SThomas Gleixner
713d0d14f9SIngo Molnar if (!(ptr->sigh & 0x80000000)) {
72da957e11SThomas Gleixner /* Unsupported data type. */
73da957e11SThomas Gleixner /* Valid numbers have the ms bit set to 1. */
74da957e11SThomas Gleixner /* Unnormal. */
75da957e11SThomas Gleixner return TAG_Special;
76da957e11SThomas Gleixner }
77da957e11SThomas Gleixner
78da957e11SThomas Gleixner return TAG_Valid;
79da957e11SThomas Gleixner }
80da957e11SThomas Gleixner
81da957e11SThomas Gleixner /* Get a long double from user memory */
FPU_load_extended(long double __user * s,int stnr)82da957e11SThomas Gleixner int FPU_load_extended(long double __user *s, int stnr)
83da957e11SThomas Gleixner {
84da957e11SThomas Gleixner FPU_REG *sti_ptr = &st(stnr);
85da957e11SThomas Gleixner
86da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
8796d4f267SLinus Torvalds FPU_access_ok(s, 10);
88e6b44ce1SArnd Bergmann FPU_copy_from_user(sti_ptr, s, 10);
89da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
90da957e11SThomas Gleixner
91da957e11SThomas Gleixner return FPU_tagof(sti_ptr);
92da957e11SThomas Gleixner }
93da957e11SThomas Gleixner
94da957e11SThomas Gleixner /* Get a double from user memory */
FPU_load_double(double __user * dfloat,FPU_REG * loaded_data)95da957e11SThomas Gleixner int FPU_load_double(double __user *dfloat, FPU_REG *loaded_data)
96da957e11SThomas Gleixner {
97da957e11SThomas Gleixner int exp, tag, negative;
98da957e11SThomas Gleixner unsigned m64, l64;
99da957e11SThomas Gleixner
100da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
10196d4f267SLinus Torvalds FPU_access_ok(dfloat, 8);
102da957e11SThomas Gleixner FPU_get_user(m64, 1 + (unsigned long __user *)dfloat);
103da957e11SThomas Gleixner FPU_get_user(l64, (unsigned long __user *)dfloat);
104da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
105da957e11SThomas Gleixner
106da957e11SThomas Gleixner negative = (m64 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
107da957e11SThomas Gleixner exp = ((m64 & 0x7ff00000) >> 20) - DOUBLE_Ebias + EXTENDED_Ebias;
108da957e11SThomas Gleixner m64 &= 0xfffff;
1093d0d14f9SIngo Molnar if (exp > DOUBLE_Emax + EXTENDED_Ebias) {
110da957e11SThomas Gleixner /* Infinity or NaN */
1113d0d14f9SIngo Molnar if ((m64 == 0) && (l64 == 0)) {
112da957e11SThomas Gleixner /* +- infinity */
113da957e11SThomas Gleixner loaded_data->sigh = 0x80000000;
114da957e11SThomas Gleixner loaded_data->sigl = 0x00000000;
115da957e11SThomas Gleixner exp = EXP_Infinity + EXTENDED_Ebias;
116da957e11SThomas Gleixner tag = TAG_Special;
1173d0d14f9SIngo Molnar } else {
118da957e11SThomas Gleixner /* Must be a signaling or quiet NaN */
119da957e11SThomas Gleixner exp = EXP_NaN + EXTENDED_Ebias;
120da957e11SThomas Gleixner loaded_data->sigh = (m64 << 11) | 0x80000000;
121da957e11SThomas Gleixner loaded_data->sigh |= l64 >> 21;
122da957e11SThomas Gleixner loaded_data->sigl = l64 << 11;
123da957e11SThomas Gleixner tag = TAG_Special; /* The calling function must look for NaNs */
124da957e11SThomas Gleixner }
1253d0d14f9SIngo Molnar } else if (exp < DOUBLE_Emin + EXTENDED_Ebias) {
126da957e11SThomas Gleixner /* Zero or de-normal */
1273d0d14f9SIngo Molnar if ((m64 == 0) && (l64 == 0)) {
128da957e11SThomas Gleixner /* Zero */
129da957e11SThomas Gleixner reg_copy(&CONST_Z, loaded_data);
130da957e11SThomas Gleixner exp = 0;
131da957e11SThomas Gleixner tag = TAG_Zero;
1323d0d14f9SIngo Molnar } else {
133da957e11SThomas Gleixner /* De-normal */
134da957e11SThomas Gleixner loaded_data->sigh = m64 << 11;
135da957e11SThomas Gleixner loaded_data->sigh |= l64 >> 21;
136da957e11SThomas Gleixner loaded_data->sigl = l64 << 11;
137da957e11SThomas Gleixner
1383d0d14f9SIngo Molnar return normalize_no_excep(loaded_data, DOUBLE_Emin,
1393d0d14f9SIngo Molnar negative)
140da957e11SThomas Gleixner | (denormal_operand() < 0 ? FPU_Exception : 0);
141da957e11SThomas Gleixner }
1423d0d14f9SIngo Molnar } else {
143da957e11SThomas Gleixner loaded_data->sigh = (m64 << 11) | 0x80000000;
144da957e11SThomas Gleixner loaded_data->sigh |= l64 >> 21;
145da957e11SThomas Gleixner loaded_data->sigl = l64 << 11;
146da957e11SThomas Gleixner
147da957e11SThomas Gleixner tag = TAG_Valid;
148da957e11SThomas Gleixner }
149da957e11SThomas Gleixner
150da957e11SThomas Gleixner setexponent16(loaded_data, exp | negative);
151da957e11SThomas Gleixner
152da957e11SThomas Gleixner return tag;
153da957e11SThomas Gleixner }
154da957e11SThomas Gleixner
155da957e11SThomas Gleixner /* Get a float from user memory */
FPU_load_single(float __user * single,FPU_REG * loaded_data)156da957e11SThomas Gleixner int FPU_load_single(float __user *single, FPU_REG *loaded_data)
157da957e11SThomas Gleixner {
158da957e11SThomas Gleixner unsigned m32;
159da957e11SThomas Gleixner int exp, tag, negative;
160da957e11SThomas Gleixner
161da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
16296d4f267SLinus Torvalds FPU_access_ok(single, 4);
163da957e11SThomas Gleixner FPU_get_user(m32, (unsigned long __user *)single);
164da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
165da957e11SThomas Gleixner
166da957e11SThomas Gleixner negative = (m32 & 0x80000000) ? SIGN_Negative : SIGN_Positive;
167da957e11SThomas Gleixner
1683d0d14f9SIngo Molnar if (!(m32 & 0x7fffffff)) {
169da957e11SThomas Gleixner /* Zero */
170da957e11SThomas Gleixner reg_copy(&CONST_Z, loaded_data);
171da957e11SThomas Gleixner addexponent(loaded_data, negative);
172da957e11SThomas Gleixner return TAG_Zero;
173da957e11SThomas Gleixner }
174da957e11SThomas Gleixner exp = ((m32 & 0x7f800000) >> 23) - SINGLE_Ebias + EXTENDED_Ebias;
175da957e11SThomas Gleixner m32 = (m32 & 0x7fffff) << 8;
1763d0d14f9SIngo Molnar if (exp < SINGLE_Emin + EXTENDED_Ebias) {
177da957e11SThomas Gleixner /* De-normals */
178da957e11SThomas Gleixner loaded_data->sigh = m32;
179da957e11SThomas Gleixner loaded_data->sigl = 0;
180da957e11SThomas Gleixner
181da957e11SThomas Gleixner return normalize_no_excep(loaded_data, SINGLE_Emin, negative)
182da957e11SThomas Gleixner | (denormal_operand() < 0 ? FPU_Exception : 0);
1833d0d14f9SIngo Molnar } else if (exp > SINGLE_Emax + EXTENDED_Ebias) {
184da957e11SThomas Gleixner /* Infinity or NaN */
1853d0d14f9SIngo Molnar if (m32 == 0) {
186da957e11SThomas Gleixner /* +- infinity */
187da957e11SThomas Gleixner loaded_data->sigh = 0x80000000;
188da957e11SThomas Gleixner loaded_data->sigl = 0x00000000;
189da957e11SThomas Gleixner exp = EXP_Infinity + EXTENDED_Ebias;
190da957e11SThomas Gleixner tag = TAG_Special;
1913d0d14f9SIngo Molnar } else {
192da957e11SThomas Gleixner /* Must be a signaling or quiet NaN */
193da957e11SThomas Gleixner exp = EXP_NaN + EXTENDED_Ebias;
194da957e11SThomas Gleixner loaded_data->sigh = m32 | 0x80000000;
195da957e11SThomas Gleixner loaded_data->sigl = 0;
196da957e11SThomas Gleixner tag = TAG_Special; /* The calling function must look for NaNs */
197da957e11SThomas Gleixner }
1983d0d14f9SIngo Molnar } else {
199da957e11SThomas Gleixner loaded_data->sigh = m32 | 0x80000000;
200da957e11SThomas Gleixner loaded_data->sigl = 0;
201da957e11SThomas Gleixner tag = TAG_Valid;
202da957e11SThomas Gleixner }
203da957e11SThomas Gleixner
204da957e11SThomas Gleixner setexponent16(loaded_data, exp | negative); /* Set the sign. */
205da957e11SThomas Gleixner
206da957e11SThomas Gleixner return tag;
207da957e11SThomas Gleixner }
208da957e11SThomas Gleixner
209da957e11SThomas Gleixner /* Get a long long from user memory */
FPU_load_int64(long long __user * _s)210da957e11SThomas Gleixner int FPU_load_int64(long long __user *_s)
211da957e11SThomas Gleixner {
212da957e11SThomas Gleixner long long s;
213da957e11SThomas Gleixner int sign;
214da957e11SThomas Gleixner FPU_REG *st0_ptr = &st(0);
215da957e11SThomas Gleixner
216da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
21796d4f267SLinus Torvalds FPU_access_ok(_s, 8);
218da957e11SThomas Gleixner if (copy_from_user(&s, _s, 8))
219da957e11SThomas Gleixner FPU_abort;
220da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
221da957e11SThomas Gleixner
2223d0d14f9SIngo Molnar if (s == 0) {
223da957e11SThomas Gleixner reg_copy(&CONST_Z, st0_ptr);
224da957e11SThomas Gleixner return TAG_Zero;
225da957e11SThomas Gleixner }
226da957e11SThomas Gleixner
227da957e11SThomas Gleixner if (s > 0)
228da957e11SThomas Gleixner sign = SIGN_Positive;
2293d0d14f9SIngo Molnar else {
230da957e11SThomas Gleixner s = -s;
231da957e11SThomas Gleixner sign = SIGN_Negative;
232da957e11SThomas Gleixner }
233da957e11SThomas Gleixner
234da957e11SThomas Gleixner significand(st0_ptr) = s;
235da957e11SThomas Gleixner
236da957e11SThomas Gleixner return normalize_no_excep(st0_ptr, 63, sign);
237da957e11SThomas Gleixner }
238da957e11SThomas Gleixner
239da957e11SThomas Gleixner /* Get a long from user memory */
FPU_load_int32(long __user * _s,FPU_REG * loaded_data)240da957e11SThomas Gleixner int FPU_load_int32(long __user *_s, FPU_REG *loaded_data)
241da957e11SThomas Gleixner {
242da957e11SThomas Gleixner long s;
243da957e11SThomas Gleixner int negative;
244da957e11SThomas Gleixner
245da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
24696d4f267SLinus Torvalds FPU_access_ok(_s, 4);
247da957e11SThomas Gleixner FPU_get_user(s, _s);
248da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
249da957e11SThomas Gleixner
2503d0d14f9SIngo Molnar if (s == 0) {
2513d0d14f9SIngo Molnar reg_copy(&CONST_Z, loaded_data);
2523d0d14f9SIngo Molnar return TAG_Zero;
2533d0d14f9SIngo Molnar }
254da957e11SThomas Gleixner
255da957e11SThomas Gleixner if (s > 0)
256da957e11SThomas Gleixner negative = SIGN_Positive;
2573d0d14f9SIngo Molnar else {
258da957e11SThomas Gleixner s = -s;
259da957e11SThomas Gleixner negative = SIGN_Negative;
260da957e11SThomas Gleixner }
261da957e11SThomas Gleixner
262da957e11SThomas Gleixner loaded_data->sigh = s;
263da957e11SThomas Gleixner loaded_data->sigl = 0;
264da957e11SThomas Gleixner
265da957e11SThomas Gleixner return normalize_no_excep(loaded_data, 31, negative);
266da957e11SThomas Gleixner }
267da957e11SThomas Gleixner
268da957e11SThomas Gleixner /* Get a short from user memory */
FPU_load_int16(short __user * _s,FPU_REG * loaded_data)269da957e11SThomas Gleixner int FPU_load_int16(short __user *_s, FPU_REG *loaded_data)
270da957e11SThomas Gleixner {
271da957e11SThomas Gleixner int s, negative;
272da957e11SThomas Gleixner
273da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
27496d4f267SLinus Torvalds FPU_access_ok(_s, 2);
275da957e11SThomas Gleixner /* Cast as short to get the sign extended. */
276da957e11SThomas Gleixner FPU_get_user(s, _s);
277da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
278da957e11SThomas Gleixner
2793d0d14f9SIngo Molnar if (s == 0) {
2803d0d14f9SIngo Molnar reg_copy(&CONST_Z, loaded_data);
2813d0d14f9SIngo Molnar return TAG_Zero;
2823d0d14f9SIngo Molnar }
283da957e11SThomas Gleixner
284da957e11SThomas Gleixner if (s > 0)
285da957e11SThomas Gleixner negative = SIGN_Positive;
2863d0d14f9SIngo Molnar else {
287da957e11SThomas Gleixner s = -s;
288da957e11SThomas Gleixner negative = SIGN_Negative;
289da957e11SThomas Gleixner }
290da957e11SThomas Gleixner
291da957e11SThomas Gleixner loaded_data->sigh = s << 16;
292da957e11SThomas Gleixner loaded_data->sigl = 0;
293da957e11SThomas Gleixner
294da957e11SThomas Gleixner return normalize_no_excep(loaded_data, 15, negative);
295da957e11SThomas Gleixner }
296da957e11SThomas Gleixner
297da957e11SThomas Gleixner /* Get a packed bcd array from user memory */
FPU_load_bcd(u_char __user * s)298da957e11SThomas Gleixner int FPU_load_bcd(u_char __user *s)
299da957e11SThomas Gleixner {
300da957e11SThomas Gleixner FPU_REG *st0_ptr = &st(0);
301da957e11SThomas Gleixner int pos;
302da957e11SThomas Gleixner u_char bcd;
303da957e11SThomas Gleixner long long l = 0;
304da957e11SThomas Gleixner int sign;
305da957e11SThomas Gleixner
306da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
30796d4f267SLinus Torvalds FPU_access_ok(s, 10);
308da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
3093d0d14f9SIngo Molnar for (pos = 8; pos >= 0; pos--) {
310da957e11SThomas Gleixner l *= 10;
311da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
312da957e11SThomas Gleixner FPU_get_user(bcd, s + pos);
313da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
314da957e11SThomas Gleixner l += bcd >> 4;
315da957e11SThomas Gleixner l *= 10;
316da957e11SThomas Gleixner l += bcd & 0x0f;
317da957e11SThomas Gleixner }
318da957e11SThomas Gleixner
319da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
320da957e11SThomas Gleixner FPU_get_user(sign, s + 9);
321da957e11SThomas Gleixner sign = sign & 0x80 ? SIGN_Negative : SIGN_Positive;
322da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
323da957e11SThomas Gleixner
3243d0d14f9SIngo Molnar if (l == 0) {
325da957e11SThomas Gleixner reg_copy(&CONST_Z, st0_ptr);
326da957e11SThomas Gleixner addexponent(st0_ptr, sign); /* Set the sign. */
327da957e11SThomas Gleixner return TAG_Zero;
3283d0d14f9SIngo Molnar } else {
329da957e11SThomas Gleixner significand(st0_ptr) = l;
330da957e11SThomas Gleixner return normalize_no_excep(st0_ptr, 63, sign);
331da957e11SThomas Gleixner }
332da957e11SThomas Gleixner }
333da957e11SThomas Gleixner
334da957e11SThomas Gleixner /*===========================================================================*/
335da957e11SThomas Gleixner
336da957e11SThomas Gleixner /* Put a long double into user memory */
FPU_store_extended(FPU_REG * st0_ptr,u_char st0_tag,long double __user * d)3373d0d14f9SIngo Molnar int FPU_store_extended(FPU_REG *st0_ptr, u_char st0_tag,
3383d0d14f9SIngo Molnar long double __user * d)
339da957e11SThomas Gleixner {
340da957e11SThomas Gleixner /*
341da957e11SThomas Gleixner The only exception raised by an attempt to store to an
342da957e11SThomas Gleixner extended format is the Invalid Stack exception, i.e.
343da957e11SThomas Gleixner attempting to store from an empty register.
344da957e11SThomas Gleixner */
345da957e11SThomas Gleixner
3463d0d14f9SIngo Molnar if (st0_tag != TAG_Empty) {
347da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
34896d4f267SLinus Torvalds FPU_access_ok(d, 10);
349da957e11SThomas Gleixner
350da957e11SThomas Gleixner FPU_put_user(st0_ptr->sigl, (unsigned long __user *)d);
3513d0d14f9SIngo Molnar FPU_put_user(st0_ptr->sigh,
3523d0d14f9SIngo Molnar (unsigned long __user *)((u_char __user *) d + 4));
3533d0d14f9SIngo Molnar FPU_put_user(exponent16(st0_ptr),
3543d0d14f9SIngo Molnar (unsigned short __user *)((u_char __user *) d +
3553d0d14f9SIngo Molnar 8));
356da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
357da957e11SThomas Gleixner
358da957e11SThomas Gleixner return 1;
359da957e11SThomas Gleixner }
360da957e11SThomas Gleixner
361da957e11SThomas Gleixner /* Empty register (stack underflow) */
362da957e11SThomas Gleixner EXCEPTION(EX_StackUnder);
3633d0d14f9SIngo Molnar if (control_word & CW_Invalid) {
364da957e11SThomas Gleixner /* The masked response */
365da957e11SThomas Gleixner /* Put out the QNaN indefinite */
366da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
36796d4f267SLinus Torvalds FPU_access_ok(d, 10);
368da957e11SThomas Gleixner FPU_put_user(0, (unsigned long __user *)d);
369da957e11SThomas Gleixner FPU_put_user(0xc0000000, 1 + (unsigned long __user *)d);
370da957e11SThomas Gleixner FPU_put_user(0xffff, 4 + (short __user *)d);
371da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
372da957e11SThomas Gleixner return 1;
3733d0d14f9SIngo Molnar } else
374da957e11SThomas Gleixner return 0;
375da957e11SThomas Gleixner
376da957e11SThomas Gleixner }
377da957e11SThomas Gleixner
378da957e11SThomas Gleixner /* Put a double into user memory */
FPU_store_double(FPU_REG * st0_ptr,u_char st0_tag,double __user * dfloat)379da957e11SThomas Gleixner int FPU_store_double(FPU_REG *st0_ptr, u_char st0_tag, double __user *dfloat)
380da957e11SThomas Gleixner {
381da957e11SThomas Gleixner unsigned long l[2];
382da957e11SThomas Gleixner unsigned long increment = 0; /* avoid gcc warnings */
383da957e11SThomas Gleixner int precision_loss;
384da957e11SThomas Gleixner int exp;
385da957e11SThomas Gleixner FPU_REG tmp;
386da957e11SThomas Gleixner
387f2e576b8SAndrew Morton l[0] = 0;
388f2e576b8SAndrew Morton l[1] = 0;
3893d0d14f9SIngo Molnar if (st0_tag == TAG_Valid) {
390da957e11SThomas Gleixner reg_copy(st0_ptr, &tmp);
391da957e11SThomas Gleixner exp = exponent(&tmp);
392da957e11SThomas Gleixner
3933d0d14f9SIngo Molnar if (exp < DOUBLE_Emin) { /* It may be a denormal */
394da957e11SThomas Gleixner addexponent(&tmp, -DOUBLE_Emin + 52); /* largest exp to be 51 */
395da957e11SThomas Gleixner denormal_arg:
3963d0d14f9SIngo Molnar if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
397da957e11SThomas Gleixner #ifdef PECULIAR_486
398da957e11SThomas Gleixner /* Did it round to a non-denormal ? */
399da957e11SThomas Gleixner /* This behaviour might be regarded as peculiar, it appears
400da957e11SThomas Gleixner that the 80486 rounds to the dest precision, then
401da957e11SThomas Gleixner converts to decide underflow. */
4023d0d14f9SIngo Molnar if (!
4033d0d14f9SIngo Molnar ((tmp.sigh == 0x00100000) && (tmp.sigl == 0)
4043d0d14f9SIngo Molnar && (st0_ptr->sigl & 0x000007ff)))
405da957e11SThomas Gleixner #endif /* PECULIAR_486 */
406da957e11SThomas Gleixner {
407da957e11SThomas Gleixner EXCEPTION(EX_Underflow);
408da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of
409da957e11SThomas Gleixner the 80486 book */
410da957e11SThomas Gleixner if (!(control_word & CW_Underflow))
411da957e11SThomas Gleixner return 0;
412da957e11SThomas Gleixner }
413da957e11SThomas Gleixner EXCEPTION(precision_loss);
414da957e11SThomas Gleixner if (!(control_word & CW_Precision))
415da957e11SThomas Gleixner return 0;
416da957e11SThomas Gleixner }
417da957e11SThomas Gleixner l[0] = tmp.sigl;
418da957e11SThomas Gleixner l[1] = tmp.sigh;
4193d0d14f9SIngo Molnar } else {
4203d0d14f9SIngo Molnar if (tmp.sigl & 0x000007ff) {
421da957e11SThomas Gleixner precision_loss = 1;
4223d0d14f9SIngo Molnar switch (control_word & CW_RC) {
423da957e11SThomas Gleixner case RC_RND:
424da957e11SThomas Gleixner /* Rounding can get a little messy.. */
425da957e11SThomas Gleixner increment = ((tmp.sigl & 0x7ff) > 0x400) | /* nearest */
426da957e11SThomas Gleixner ((tmp.sigl & 0xc00) == 0xc00); /* odd -> even */
427da957e11SThomas Gleixner break;
428da957e11SThomas Gleixner case RC_DOWN: /* towards -infinity */
4293d0d14f9SIngo Molnar increment =
4303d0d14f9SIngo Molnar signpositive(&tmp) ? 0 : tmp.
4313d0d14f9SIngo Molnar sigl & 0x7ff;
432da957e11SThomas Gleixner break;
433da957e11SThomas Gleixner case RC_UP: /* towards +infinity */
4343d0d14f9SIngo Molnar increment =
4353d0d14f9SIngo Molnar signpositive(&tmp) ? tmp.
4363d0d14f9SIngo Molnar sigl & 0x7ff : 0;
437da957e11SThomas Gleixner break;
438da957e11SThomas Gleixner case RC_CHOP:
439da957e11SThomas Gleixner increment = 0;
440da957e11SThomas Gleixner break;
441da957e11SThomas Gleixner }
442da957e11SThomas Gleixner
443da957e11SThomas Gleixner /* Truncate the mantissa */
444da957e11SThomas Gleixner tmp.sigl &= 0xfffff800;
445da957e11SThomas Gleixner
4463d0d14f9SIngo Molnar if (increment) {
4473d0d14f9SIngo Molnar if (tmp.sigl >= 0xfffff800) {
448da957e11SThomas Gleixner /* the sigl part overflows */
4493d0d14f9SIngo Molnar if (tmp.sigh == 0xffffffff) {
450da957e11SThomas Gleixner /* The sigh part overflows */
451da957e11SThomas Gleixner tmp.sigh = 0x80000000;
452da957e11SThomas Gleixner exp++;
453da957e11SThomas Gleixner if (exp >= EXP_OVER)
454da957e11SThomas Gleixner goto overflow;
4553d0d14f9SIngo Molnar } else {
456da957e11SThomas Gleixner tmp.sigh++;
457da957e11SThomas Gleixner }
458da957e11SThomas Gleixner tmp.sigl = 0x00000000;
4593d0d14f9SIngo Molnar } else {
460da957e11SThomas Gleixner /* We only need to increment sigl */
461da957e11SThomas Gleixner tmp.sigl += 0x00000800;
462da957e11SThomas Gleixner }
463da957e11SThomas Gleixner }
4643d0d14f9SIngo Molnar } else
465da957e11SThomas Gleixner precision_loss = 0;
466da957e11SThomas Gleixner
467da957e11SThomas Gleixner l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
468da957e11SThomas Gleixner l[1] = ((tmp.sigh >> 11) & 0xfffff);
469da957e11SThomas Gleixner
4703d0d14f9SIngo Molnar if (exp > DOUBLE_Emax) {
471da957e11SThomas Gleixner overflow:
472da957e11SThomas Gleixner EXCEPTION(EX_Overflow);
473da957e11SThomas Gleixner if (!(control_word & CW_Overflow))
474da957e11SThomas Gleixner return 0;
475da957e11SThomas Gleixner set_precision_flag_up();
476da957e11SThomas Gleixner if (!(control_word & CW_Precision))
477da957e11SThomas Gleixner return 0;
478da957e11SThomas Gleixner
479da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of the 80486 book */
480da957e11SThomas Gleixner /* Overflow to infinity */
481f2e576b8SAndrew Morton l[1] = 0x7ff00000; /* Set to + INF */
4823d0d14f9SIngo Molnar } else {
4833d0d14f9SIngo Molnar if (precision_loss) {
484da957e11SThomas Gleixner if (increment)
485da957e11SThomas Gleixner set_precision_flag_up();
486da957e11SThomas Gleixner else
487da957e11SThomas Gleixner set_precision_flag_down();
488da957e11SThomas Gleixner }
489da957e11SThomas Gleixner /* Add the exponent */
490da957e11SThomas Gleixner l[1] |= (((exp + DOUBLE_Ebias) & 0x7ff) << 20);
491da957e11SThomas Gleixner }
492da957e11SThomas Gleixner }
4933d0d14f9SIngo Molnar } else if (st0_tag == TAG_Zero) {
494da957e11SThomas Gleixner /* Number is zero */
4953d0d14f9SIngo Molnar } else if (st0_tag == TAG_Special) {
496da957e11SThomas Gleixner st0_tag = FPU_Special(st0_ptr);
4973d0d14f9SIngo Molnar if (st0_tag == TW_Denormal) {
498da957e11SThomas Gleixner /* A denormal will always underflow. */
499da957e11SThomas Gleixner #ifndef PECULIAR_486
500da957e11SThomas Gleixner /* An 80486 is supposed to be able to generate
501da957e11SThomas Gleixner a denormal exception here, but... */
502da957e11SThomas Gleixner /* Underflow has priority. */
503da957e11SThomas Gleixner if (control_word & CW_Underflow)
504da957e11SThomas Gleixner denormal_operand();
505da957e11SThomas Gleixner #endif /* PECULIAR_486 */
506da957e11SThomas Gleixner reg_copy(st0_ptr, &tmp);
507da957e11SThomas Gleixner goto denormal_arg;
5083d0d14f9SIngo Molnar } else if (st0_tag == TW_Infinity) {
509da957e11SThomas Gleixner l[1] = 0x7ff00000;
5103d0d14f9SIngo Molnar } else if (st0_tag == TW_NaN) {
511da957e11SThomas Gleixner /* Is it really a NaN ? */
512da957e11SThomas Gleixner if ((exponent(st0_ptr) == EXP_OVER)
5133d0d14f9SIngo Molnar && (st0_ptr->sigh & 0x80000000)) {
514da957e11SThomas Gleixner /* See if we can get a valid NaN from the FPU_REG */
5153d0d14f9SIngo Molnar l[0] =
5163d0d14f9SIngo Molnar (st0_ptr->sigl >> 11) | (st0_ptr->
5173d0d14f9SIngo Molnar sigh << 21);
518da957e11SThomas Gleixner l[1] = ((st0_ptr->sigh >> 11) & 0xfffff);
5193d0d14f9SIngo Molnar if (!(st0_ptr->sigh & 0x40000000)) {
520da957e11SThomas Gleixner /* It is a signalling NaN */
521da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
522da957e11SThomas Gleixner if (!(control_word & CW_Invalid))
523da957e11SThomas Gleixner return 0;
524da957e11SThomas Gleixner l[1] |= (0x40000000 >> 11);
525da957e11SThomas Gleixner }
526da957e11SThomas Gleixner l[1] |= 0x7ff00000;
5273d0d14f9SIngo Molnar } else {
528da957e11SThomas Gleixner /* It is an unsupported data type */
529da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
530da957e11SThomas Gleixner if (!(control_word & CW_Invalid))
531da957e11SThomas Gleixner return 0;
532da957e11SThomas Gleixner l[1] = 0xfff80000;
533da957e11SThomas Gleixner }
534da957e11SThomas Gleixner }
5353d0d14f9SIngo Molnar } else if (st0_tag == TAG_Empty) {
536da957e11SThomas Gleixner /* Empty register (stack underflow) */
537da957e11SThomas Gleixner EXCEPTION(EX_StackUnder);
5383d0d14f9SIngo Molnar if (control_word & CW_Invalid) {
539da957e11SThomas Gleixner /* The masked response */
540da957e11SThomas Gleixner /* Put out the QNaN indefinite */
541da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
54296d4f267SLinus Torvalds FPU_access_ok(dfloat, 8);
543da957e11SThomas Gleixner FPU_put_user(0, (unsigned long __user *)dfloat);
5443d0d14f9SIngo Molnar FPU_put_user(0xfff80000,
5453d0d14f9SIngo Molnar 1 + (unsigned long __user *)dfloat);
546da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
547da957e11SThomas Gleixner return 1;
5483d0d14f9SIngo Molnar } else
549da957e11SThomas Gleixner return 0;
550da957e11SThomas Gleixner }
551da957e11SThomas Gleixner if (getsign(st0_ptr))
552da957e11SThomas Gleixner l[1] |= 0x80000000;
553da957e11SThomas Gleixner
554da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
55596d4f267SLinus Torvalds FPU_access_ok(dfloat, 8);
556da957e11SThomas Gleixner FPU_put_user(l[0], (unsigned long __user *)dfloat);
557da957e11SThomas Gleixner FPU_put_user(l[1], 1 + (unsigned long __user *)dfloat);
558da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
559da957e11SThomas Gleixner
560da957e11SThomas Gleixner return 1;
561da957e11SThomas Gleixner }
562da957e11SThomas Gleixner
563da957e11SThomas Gleixner /* Put a float into user memory */
FPU_store_single(FPU_REG * st0_ptr,u_char st0_tag,float __user * single)564da957e11SThomas Gleixner int FPU_store_single(FPU_REG *st0_ptr, u_char st0_tag, float __user *single)
565da957e11SThomas Gleixner {
566da957e11SThomas Gleixner long templ = 0;
567da957e11SThomas Gleixner unsigned long increment = 0; /* avoid gcc warnings */
568da957e11SThomas Gleixner int precision_loss;
569da957e11SThomas Gleixner int exp;
570da957e11SThomas Gleixner FPU_REG tmp;
571da957e11SThomas Gleixner
5723d0d14f9SIngo Molnar if (st0_tag == TAG_Valid) {
573da957e11SThomas Gleixner
574da957e11SThomas Gleixner reg_copy(st0_ptr, &tmp);
575da957e11SThomas Gleixner exp = exponent(&tmp);
576da957e11SThomas Gleixner
5773d0d14f9SIngo Molnar if (exp < SINGLE_Emin) {
578da957e11SThomas Gleixner addexponent(&tmp, -SINGLE_Emin + 23); /* largest exp to be 22 */
579da957e11SThomas Gleixner
580da957e11SThomas Gleixner denormal_arg:
581da957e11SThomas Gleixner
5823d0d14f9SIngo Molnar if ((precision_loss = FPU_round_to_int(&tmp, st0_tag))) {
583da957e11SThomas Gleixner #ifdef PECULIAR_486
584da957e11SThomas Gleixner /* Did it round to a non-denormal ? */
585da957e11SThomas Gleixner /* This behaviour might be regarded as peculiar, it appears
586da957e11SThomas Gleixner that the 80486 rounds to the dest precision, then
587da957e11SThomas Gleixner converts to decide underflow. */
588da957e11SThomas Gleixner if (!((tmp.sigl == 0x00800000) &&
5893d0d14f9SIngo Molnar ((st0_ptr->sigh & 0x000000ff)
5903d0d14f9SIngo Molnar || st0_ptr->sigl)))
591da957e11SThomas Gleixner #endif /* PECULIAR_486 */
592da957e11SThomas Gleixner {
593da957e11SThomas Gleixner EXCEPTION(EX_Underflow);
594da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of
595da957e11SThomas Gleixner the 80486 book */
596da957e11SThomas Gleixner if (!(control_word & CW_Underflow))
597da957e11SThomas Gleixner return 0;
598da957e11SThomas Gleixner }
599da957e11SThomas Gleixner EXCEPTION(precision_loss);
600da957e11SThomas Gleixner if (!(control_word & CW_Precision))
601da957e11SThomas Gleixner return 0;
602da957e11SThomas Gleixner }
603da957e11SThomas Gleixner templ = tmp.sigl;
6043d0d14f9SIngo Molnar } else {
6053d0d14f9SIngo Molnar if (tmp.sigl | (tmp.sigh & 0x000000ff)) {
606da957e11SThomas Gleixner unsigned long sigh = tmp.sigh;
607da957e11SThomas Gleixner unsigned long sigl = tmp.sigl;
608da957e11SThomas Gleixner
609da957e11SThomas Gleixner precision_loss = 1;
6103d0d14f9SIngo Molnar switch (control_word & CW_RC) {
611da957e11SThomas Gleixner case RC_RND:
612da957e11SThomas Gleixner increment = ((sigh & 0xff) > 0x80) /* more than half */
613da957e11SThomas Gleixner ||(((sigh & 0xff) == 0x80) && sigl) /* more than half */
614da957e11SThomas Gleixner ||((sigh & 0x180) == 0x180); /* round to even */
615da957e11SThomas Gleixner break;
616da957e11SThomas Gleixner case RC_DOWN: /* towards -infinity */
617da957e11SThomas Gleixner increment = signpositive(&tmp)
618da957e11SThomas Gleixner ? 0 : (sigl | (sigh & 0xff));
619da957e11SThomas Gleixner break;
620da957e11SThomas Gleixner case RC_UP: /* towards +infinity */
621da957e11SThomas Gleixner increment = signpositive(&tmp)
622da957e11SThomas Gleixner ? (sigl | (sigh & 0xff)) : 0;
623da957e11SThomas Gleixner break;
624da957e11SThomas Gleixner case RC_CHOP:
625da957e11SThomas Gleixner increment = 0;
626da957e11SThomas Gleixner break;
627da957e11SThomas Gleixner }
628da957e11SThomas Gleixner
629da957e11SThomas Gleixner /* Truncate part of the mantissa */
630da957e11SThomas Gleixner tmp.sigl = 0;
631da957e11SThomas Gleixner
6323d0d14f9SIngo Molnar if (increment) {
6333d0d14f9SIngo Molnar if (sigh >= 0xffffff00) {
634da957e11SThomas Gleixner /* The sigh part overflows */
635da957e11SThomas Gleixner tmp.sigh = 0x80000000;
636da957e11SThomas Gleixner exp++;
637da957e11SThomas Gleixner if (exp >= EXP_OVER)
638da957e11SThomas Gleixner goto overflow;
6393d0d14f9SIngo Molnar } else {
640da957e11SThomas Gleixner tmp.sigh &= 0xffffff00;
641da957e11SThomas Gleixner tmp.sigh += 0x100;
642da957e11SThomas Gleixner }
6433d0d14f9SIngo Molnar } else {
644da957e11SThomas Gleixner tmp.sigh &= 0xffffff00; /* Finish the truncation */
645da957e11SThomas Gleixner }
6463d0d14f9SIngo Molnar } else
647da957e11SThomas Gleixner precision_loss = 0;
648da957e11SThomas Gleixner
649da957e11SThomas Gleixner templ = (tmp.sigh >> 8) & 0x007fffff;
650da957e11SThomas Gleixner
6513d0d14f9SIngo Molnar if (exp > SINGLE_Emax) {
652da957e11SThomas Gleixner overflow:
653da957e11SThomas Gleixner EXCEPTION(EX_Overflow);
654da957e11SThomas Gleixner if (!(control_word & CW_Overflow))
655da957e11SThomas Gleixner return 0;
656da957e11SThomas Gleixner set_precision_flag_up();
657da957e11SThomas Gleixner if (!(control_word & CW_Precision))
658da957e11SThomas Gleixner return 0;
659da957e11SThomas Gleixner
660da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of the 80486 book. */
661da957e11SThomas Gleixner /* Masked response is overflow to infinity. */
662da957e11SThomas Gleixner templ = 0x7f800000;
6633d0d14f9SIngo Molnar } else {
6643d0d14f9SIngo Molnar if (precision_loss) {
665da957e11SThomas Gleixner if (increment)
666da957e11SThomas Gleixner set_precision_flag_up();
667da957e11SThomas Gleixner else
668da957e11SThomas Gleixner set_precision_flag_down();
669da957e11SThomas Gleixner }
670da957e11SThomas Gleixner /* Add the exponent */
671da957e11SThomas Gleixner templ |= ((exp + SINGLE_Ebias) & 0xff) << 23;
672da957e11SThomas Gleixner }
673da957e11SThomas Gleixner }
6743d0d14f9SIngo Molnar } else if (st0_tag == TAG_Zero) {
675da957e11SThomas Gleixner templ = 0;
6763d0d14f9SIngo Molnar } else if (st0_tag == TAG_Special) {
677da957e11SThomas Gleixner st0_tag = FPU_Special(st0_ptr);
6783d0d14f9SIngo Molnar if (st0_tag == TW_Denormal) {
679da957e11SThomas Gleixner reg_copy(st0_ptr, &tmp);
680da957e11SThomas Gleixner
681da957e11SThomas Gleixner /* A denormal will always underflow. */
682da957e11SThomas Gleixner #ifndef PECULIAR_486
683da957e11SThomas Gleixner /* An 80486 is supposed to be able to generate
684da957e11SThomas Gleixner a denormal exception here, but... */
685da957e11SThomas Gleixner /* Underflow has priority. */
686da957e11SThomas Gleixner if (control_word & CW_Underflow)
687da957e11SThomas Gleixner denormal_operand();
688da957e11SThomas Gleixner #endif /* PECULIAR_486 */
689da957e11SThomas Gleixner goto denormal_arg;
6903d0d14f9SIngo Molnar } else if (st0_tag == TW_Infinity) {
691da957e11SThomas Gleixner templ = 0x7f800000;
6923d0d14f9SIngo Molnar } else if (st0_tag == TW_NaN) {
693da957e11SThomas Gleixner /* Is it really a NaN ? */
6943d0d14f9SIngo Molnar if ((exponent(st0_ptr) == EXP_OVER)
6953d0d14f9SIngo Molnar && (st0_ptr->sigh & 0x80000000)) {
696da957e11SThomas Gleixner /* See if we can get a valid NaN from the FPU_REG */
697da957e11SThomas Gleixner templ = st0_ptr->sigh >> 8;
6983d0d14f9SIngo Molnar if (!(st0_ptr->sigh & 0x40000000)) {
699da957e11SThomas Gleixner /* It is a signalling NaN */
700da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
701da957e11SThomas Gleixner if (!(control_word & CW_Invalid))
702da957e11SThomas Gleixner return 0;
703da957e11SThomas Gleixner templ |= (0x40000000 >> 8);
704da957e11SThomas Gleixner }
705da957e11SThomas Gleixner templ |= 0x7f800000;
7063d0d14f9SIngo Molnar } else {
707da957e11SThomas Gleixner /* It is an unsupported data type */
708da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
709da957e11SThomas Gleixner if (!(control_word & CW_Invalid))
710da957e11SThomas Gleixner return 0;
711da957e11SThomas Gleixner templ = 0xffc00000;
712da957e11SThomas Gleixner }
713da957e11SThomas Gleixner }
714da957e11SThomas Gleixner #ifdef PARANOID
7153d0d14f9SIngo Molnar else {
716da957e11SThomas Gleixner EXCEPTION(EX_INTERNAL | 0x164);
717da957e11SThomas Gleixner return 0;
718da957e11SThomas Gleixner }
719da957e11SThomas Gleixner #endif
7203d0d14f9SIngo Molnar } else if (st0_tag == TAG_Empty) {
721da957e11SThomas Gleixner /* Empty register (stack underflow) */
722da957e11SThomas Gleixner EXCEPTION(EX_StackUnder);
7233d0d14f9SIngo Molnar if (control_word & EX_Invalid) {
724da957e11SThomas Gleixner /* The masked response */
725da957e11SThomas Gleixner /* Put out the QNaN indefinite */
726da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
72796d4f267SLinus Torvalds FPU_access_ok(single, 4);
7283d0d14f9SIngo Molnar FPU_put_user(0xffc00000,
7293d0d14f9SIngo Molnar (unsigned long __user *)single);
730da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
731da957e11SThomas Gleixner return 1;
7323d0d14f9SIngo Molnar } else
733da957e11SThomas Gleixner return 0;
734da957e11SThomas Gleixner }
735da957e11SThomas Gleixner #ifdef PARANOID
7363d0d14f9SIngo Molnar else {
737da957e11SThomas Gleixner EXCEPTION(EX_INTERNAL | 0x163);
738da957e11SThomas Gleixner return 0;
739da957e11SThomas Gleixner }
740da957e11SThomas Gleixner #endif
741da957e11SThomas Gleixner if (getsign(st0_ptr))
742da957e11SThomas Gleixner templ |= 0x80000000;
743da957e11SThomas Gleixner
744da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
74596d4f267SLinus Torvalds FPU_access_ok(single, 4);
746da957e11SThomas Gleixner FPU_put_user(templ, (unsigned long __user *)single);
747da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
748da957e11SThomas Gleixner
749da957e11SThomas Gleixner return 1;
750da957e11SThomas Gleixner }
751da957e11SThomas Gleixner
752da957e11SThomas Gleixner /* Put a long long into user memory */
FPU_store_int64(FPU_REG * st0_ptr,u_char st0_tag,long long __user * d)753da957e11SThomas Gleixner int FPU_store_int64(FPU_REG *st0_ptr, u_char st0_tag, long long __user *d)
754da957e11SThomas Gleixner {
755da957e11SThomas Gleixner FPU_REG t;
756da957e11SThomas Gleixner long long tll;
757da957e11SThomas Gleixner int precision_loss;
758da957e11SThomas Gleixner
7593d0d14f9SIngo Molnar if (st0_tag == TAG_Empty) {
760da957e11SThomas Gleixner /* Empty register (stack underflow) */
761da957e11SThomas Gleixner EXCEPTION(EX_StackUnder);
762da957e11SThomas Gleixner goto invalid_operand;
7633d0d14f9SIngo Molnar } else if (st0_tag == TAG_Special) {
764da957e11SThomas Gleixner st0_tag = FPU_Special(st0_ptr);
7653d0d14f9SIngo Molnar if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
766da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
767da957e11SThomas Gleixner goto invalid_operand;
768da957e11SThomas Gleixner }
769da957e11SThomas Gleixner }
770da957e11SThomas Gleixner
771da957e11SThomas Gleixner reg_copy(st0_ptr, &t);
772da957e11SThomas Gleixner precision_loss = FPU_round_to_int(&t, st0_tag);
773da957e11SThomas Gleixner ((long *)&tll)[0] = t.sigl;
774da957e11SThomas Gleixner ((long *)&tll)[1] = t.sigh;
775da957e11SThomas Gleixner if ((precision_loss == 1) ||
776da957e11SThomas Gleixner ((t.sigh & 0x80000000) &&
7773d0d14f9SIngo Molnar !((t.sigh == 0x80000000) && (t.sigl == 0) && signnegative(&t)))) {
778da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
779da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of the 80486 book */
780da957e11SThomas Gleixner invalid_operand:
7813d0d14f9SIngo Molnar if (control_word & EX_Invalid) {
782da957e11SThomas Gleixner /* Produce something like QNaN "indefinite" */
783da957e11SThomas Gleixner tll = 0x8000000000000000LL;
7843d0d14f9SIngo Molnar } else
785da957e11SThomas Gleixner return 0;
7863d0d14f9SIngo Molnar } else {
787da957e11SThomas Gleixner if (precision_loss)
788da957e11SThomas Gleixner set_precision_flag(precision_loss);
789da957e11SThomas Gleixner if (signnegative(&t))
790da957e11SThomas Gleixner tll = -tll;
791da957e11SThomas Gleixner }
792da957e11SThomas Gleixner
793da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
79496d4f267SLinus Torvalds FPU_access_ok(d, 8);
795da957e11SThomas Gleixner if (copy_to_user(d, &tll, 8))
796da957e11SThomas Gleixner FPU_abort;
797da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
798da957e11SThomas Gleixner
799da957e11SThomas Gleixner return 1;
800da957e11SThomas Gleixner }
801da957e11SThomas Gleixner
802da957e11SThomas Gleixner /* Put a long into user memory */
FPU_store_int32(FPU_REG * st0_ptr,u_char st0_tag,long __user * d)803da957e11SThomas Gleixner int FPU_store_int32(FPU_REG *st0_ptr, u_char st0_tag, long __user *d)
804da957e11SThomas Gleixner {
805da957e11SThomas Gleixner FPU_REG t;
806da957e11SThomas Gleixner int precision_loss;
807da957e11SThomas Gleixner
8083d0d14f9SIngo Molnar if (st0_tag == TAG_Empty) {
809da957e11SThomas Gleixner /* Empty register (stack underflow) */
810da957e11SThomas Gleixner EXCEPTION(EX_StackUnder);
811da957e11SThomas Gleixner goto invalid_operand;
8123d0d14f9SIngo Molnar } else if (st0_tag == TAG_Special) {
813da957e11SThomas Gleixner st0_tag = FPU_Special(st0_ptr);
8143d0d14f9SIngo Molnar if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
815da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
816da957e11SThomas Gleixner goto invalid_operand;
817da957e11SThomas Gleixner }
818da957e11SThomas Gleixner }
819da957e11SThomas Gleixner
820da957e11SThomas Gleixner reg_copy(st0_ptr, &t);
821da957e11SThomas Gleixner precision_loss = FPU_round_to_int(&t, st0_tag);
822da957e11SThomas Gleixner if (t.sigh ||
823da957e11SThomas Gleixner ((t.sigl & 0x80000000) &&
8243d0d14f9SIngo Molnar !((t.sigl == 0x80000000) && signnegative(&t)))) {
825da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
826da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of the 80486 book */
827da957e11SThomas Gleixner invalid_operand:
8283d0d14f9SIngo Molnar if (control_word & EX_Invalid) {
829da957e11SThomas Gleixner /* Produce something like QNaN "indefinite" */
830da957e11SThomas Gleixner t.sigl = 0x80000000;
8313d0d14f9SIngo Molnar } else
832da957e11SThomas Gleixner return 0;
8333d0d14f9SIngo Molnar } else {
834da957e11SThomas Gleixner if (precision_loss)
835da957e11SThomas Gleixner set_precision_flag(precision_loss);
836da957e11SThomas Gleixner if (signnegative(&t))
837da957e11SThomas Gleixner t.sigl = -(long)t.sigl;
838da957e11SThomas Gleixner }
839da957e11SThomas Gleixner
840da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
84196d4f267SLinus Torvalds FPU_access_ok(d, 4);
842da957e11SThomas Gleixner FPU_put_user(t.sigl, (unsigned long __user *)d);
843da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
844da957e11SThomas Gleixner
845da957e11SThomas Gleixner return 1;
846da957e11SThomas Gleixner }
847da957e11SThomas Gleixner
848da957e11SThomas Gleixner /* Put a short into user memory */
FPU_store_int16(FPU_REG * st0_ptr,u_char st0_tag,short __user * d)849da957e11SThomas Gleixner int FPU_store_int16(FPU_REG *st0_ptr, u_char st0_tag, short __user *d)
850da957e11SThomas Gleixner {
851da957e11SThomas Gleixner FPU_REG t;
852da957e11SThomas Gleixner int precision_loss;
853da957e11SThomas Gleixner
8543d0d14f9SIngo Molnar if (st0_tag == TAG_Empty) {
855da957e11SThomas Gleixner /* Empty register (stack underflow) */
856da957e11SThomas Gleixner EXCEPTION(EX_StackUnder);
857da957e11SThomas Gleixner goto invalid_operand;
8583d0d14f9SIngo Molnar } else if (st0_tag == TAG_Special) {
859da957e11SThomas Gleixner st0_tag = FPU_Special(st0_ptr);
8603d0d14f9SIngo Molnar if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
861da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
862da957e11SThomas Gleixner goto invalid_operand;
863da957e11SThomas Gleixner }
864da957e11SThomas Gleixner }
865da957e11SThomas Gleixner
866da957e11SThomas Gleixner reg_copy(st0_ptr, &t);
867da957e11SThomas Gleixner precision_loss = FPU_round_to_int(&t, st0_tag);
868da957e11SThomas Gleixner if (t.sigh ||
869da957e11SThomas Gleixner ((t.sigl & 0xffff8000) &&
8703d0d14f9SIngo Molnar !((t.sigl == 0x8000) && signnegative(&t)))) {
871da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
872da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of the 80486 book */
873da957e11SThomas Gleixner invalid_operand:
8743d0d14f9SIngo Molnar if (control_word & EX_Invalid) {
875da957e11SThomas Gleixner /* Produce something like QNaN "indefinite" */
876da957e11SThomas Gleixner t.sigl = 0x8000;
8773d0d14f9SIngo Molnar } else
878da957e11SThomas Gleixner return 0;
8793d0d14f9SIngo Molnar } else {
880da957e11SThomas Gleixner if (precision_loss)
881da957e11SThomas Gleixner set_precision_flag(precision_loss);
882da957e11SThomas Gleixner if (signnegative(&t))
883da957e11SThomas Gleixner t.sigl = -t.sigl;
884da957e11SThomas Gleixner }
885da957e11SThomas Gleixner
886da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
88796d4f267SLinus Torvalds FPU_access_ok(d, 2);
888da957e11SThomas Gleixner FPU_put_user((short)t.sigl, d);
889da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
890da957e11SThomas Gleixner
891da957e11SThomas Gleixner return 1;
892da957e11SThomas Gleixner }
893da957e11SThomas Gleixner
894da957e11SThomas Gleixner /* Put a packed bcd array into user memory */
FPU_store_bcd(FPU_REG * st0_ptr,u_char st0_tag,u_char __user * d)895da957e11SThomas Gleixner int FPU_store_bcd(FPU_REG *st0_ptr, u_char st0_tag, u_char __user *d)
896da957e11SThomas Gleixner {
897da957e11SThomas Gleixner FPU_REG t;
898da957e11SThomas Gleixner unsigned long long ll;
899da957e11SThomas Gleixner u_char b;
900da957e11SThomas Gleixner int i, precision_loss;
901da957e11SThomas Gleixner u_char sign = (getsign(st0_ptr) == SIGN_NEG) ? 0x80 : 0;
902da957e11SThomas Gleixner
9033d0d14f9SIngo Molnar if (st0_tag == TAG_Empty) {
904da957e11SThomas Gleixner /* Empty register (stack underflow) */
905da957e11SThomas Gleixner EXCEPTION(EX_StackUnder);
906da957e11SThomas Gleixner goto invalid_operand;
9073d0d14f9SIngo Molnar } else if (st0_tag == TAG_Special) {
908da957e11SThomas Gleixner st0_tag = FPU_Special(st0_ptr);
9093d0d14f9SIngo Molnar if ((st0_tag == TW_Infinity) || (st0_tag == TW_NaN)) {
910da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
911da957e11SThomas Gleixner goto invalid_operand;
912da957e11SThomas Gleixner }
913da957e11SThomas Gleixner }
914da957e11SThomas Gleixner
915da957e11SThomas Gleixner reg_copy(st0_ptr, &t);
916da957e11SThomas Gleixner precision_loss = FPU_round_to_int(&t, st0_tag);
917da957e11SThomas Gleixner ll = significand(&t);
918da957e11SThomas Gleixner
919da957e11SThomas Gleixner /* Check for overflow, by comparing with 999999999999999999 decimal. */
920da957e11SThomas Gleixner if ((t.sigh > 0x0de0b6b3) ||
9213d0d14f9SIngo Molnar ((t.sigh == 0x0de0b6b3) && (t.sigl > 0xa763ffff))) {
922da957e11SThomas Gleixner EXCEPTION(EX_Invalid);
923da957e11SThomas Gleixner /* This is a special case: see sec 16.2.5.1 of the 80486 book */
924da957e11SThomas Gleixner invalid_operand:
9253d0d14f9SIngo Molnar if (control_word & CW_Invalid) {
926da957e11SThomas Gleixner /* Produce the QNaN "indefinite" */
927da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
92896d4f267SLinus Torvalds FPU_access_ok(d, 10);
929da957e11SThomas Gleixner for (i = 0; i < 7; i++)
930da957e11SThomas Gleixner FPU_put_user(0, d + i); /* These bytes "undefined" */
931da957e11SThomas Gleixner FPU_put_user(0xc0, d + 7); /* This byte "undefined" */
932da957e11SThomas Gleixner FPU_put_user(0xff, d + 8);
933da957e11SThomas Gleixner FPU_put_user(0xff, d + 9);
934da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
935da957e11SThomas Gleixner return 1;
9363d0d14f9SIngo Molnar } else
937da957e11SThomas Gleixner return 0;
9383d0d14f9SIngo Molnar } else if (precision_loss) {
939da957e11SThomas Gleixner /* Precision loss doesn't stop the data transfer */
940da957e11SThomas Gleixner set_precision_flag(precision_loss);
941da957e11SThomas Gleixner }
942da957e11SThomas Gleixner
943da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
94496d4f267SLinus Torvalds FPU_access_ok(d, 10);
945da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
9463d0d14f9SIngo Molnar for (i = 0; i < 9; i++) {
947da957e11SThomas Gleixner b = FPU_div_small(&ll, 10);
948da957e11SThomas Gleixner b |= (FPU_div_small(&ll, 10)) << 4;
949da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
950da957e11SThomas Gleixner FPU_put_user(b, d + i);
951da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
952da957e11SThomas Gleixner }
953da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
954da957e11SThomas Gleixner FPU_put_user(sign, d + 9);
955da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
956da957e11SThomas Gleixner
957da957e11SThomas Gleixner return 1;
958da957e11SThomas Gleixner }
959da957e11SThomas Gleixner
960da957e11SThomas Gleixner /*===========================================================================*/
961da957e11SThomas Gleixner
962da957e11SThomas Gleixner /* r gets mangled such that sig is int, sign:
963da957e11SThomas Gleixner it is NOT normalized */
964da957e11SThomas Gleixner /* The return value (in eax) is zero if the result is exact,
965da957e11SThomas Gleixner if bits are changed due to rounding, truncation, etc, then
966da957e11SThomas Gleixner a non-zero value is returned */
967163b0991SIngo Molnar /* Overflow is signaled by a non-zero return value (in eax).
968da957e11SThomas Gleixner In the case of overflow, the returned significand always has the
969da957e11SThomas Gleixner largest possible value */
FPU_round_to_int(FPU_REG * r,u_char tag)970da957e11SThomas Gleixner int FPU_round_to_int(FPU_REG *r, u_char tag)
971da957e11SThomas Gleixner {
972da957e11SThomas Gleixner u_char very_big;
973da957e11SThomas Gleixner unsigned eax;
974da957e11SThomas Gleixner
9753d0d14f9SIngo Molnar if (tag == TAG_Zero) {
976da957e11SThomas Gleixner /* Make sure that zero is returned */
977da957e11SThomas Gleixner significand(r) = 0;
978da957e11SThomas Gleixner return 0; /* o.k. */
979da957e11SThomas Gleixner }
980da957e11SThomas Gleixner
9813d0d14f9SIngo Molnar if (exponent(r) > 63) {
982da957e11SThomas Gleixner r->sigl = r->sigh = ~0; /* The largest representable number */
983da957e11SThomas Gleixner return 1; /* overflow */
984da957e11SThomas Gleixner }
985da957e11SThomas Gleixner
986da957e11SThomas Gleixner eax = FPU_shrxs(&r->sigl, 63 - exponent(r));
987da957e11SThomas Gleixner very_big = !(~(r->sigh) | ~(r->sigl)); /* test for 0xfff...fff */
988da957e11SThomas Gleixner #define half_or_more (eax & 0x80000000)
989da957e11SThomas Gleixner #define frac_part (eax)
990da957e11SThomas Gleixner #define more_than_half ((eax & 0x80000001) == 0x80000001)
9913d0d14f9SIngo Molnar switch (control_word & CW_RC) {
992da957e11SThomas Gleixner case RC_RND:
993da957e11SThomas Gleixner if (more_than_half /* nearest */
9943d0d14f9SIngo Molnar || (half_or_more && (r->sigl & 1))) { /* odd -> even */
9953d0d14f9SIngo Molnar if (very_big)
9963d0d14f9SIngo Molnar return 1; /* overflow */
997da957e11SThomas Gleixner significand(r)++;
998da957e11SThomas Gleixner return PRECISION_LOST_UP;
999da957e11SThomas Gleixner }
1000da957e11SThomas Gleixner break;
1001da957e11SThomas Gleixner case RC_DOWN:
10023d0d14f9SIngo Molnar if (frac_part && getsign(r)) {
10033d0d14f9SIngo Molnar if (very_big)
10043d0d14f9SIngo Molnar return 1; /* overflow */
1005da957e11SThomas Gleixner significand(r)++;
1006da957e11SThomas Gleixner return PRECISION_LOST_UP;
1007da957e11SThomas Gleixner }
1008da957e11SThomas Gleixner break;
1009da957e11SThomas Gleixner case RC_UP:
10103d0d14f9SIngo Molnar if (frac_part && !getsign(r)) {
10113d0d14f9SIngo Molnar if (very_big)
10123d0d14f9SIngo Molnar return 1; /* overflow */
1013da957e11SThomas Gleixner significand(r)++;
1014da957e11SThomas Gleixner return PRECISION_LOST_UP;
1015da957e11SThomas Gleixner }
1016da957e11SThomas Gleixner break;
1017da957e11SThomas Gleixner case RC_CHOP:
1018da957e11SThomas Gleixner break;
1019da957e11SThomas Gleixner }
1020da957e11SThomas Gleixner
1021da957e11SThomas Gleixner return eax ? PRECISION_LOST_DOWN : 0;
1022da957e11SThomas Gleixner
1023da957e11SThomas Gleixner }
1024da957e11SThomas Gleixner
1025da957e11SThomas Gleixner /*===========================================================================*/
1026da957e11SThomas Gleixner
fldenv(fpu_addr_modes addr_modes,u_char __user * s)1027da957e11SThomas Gleixner u_char __user *fldenv(fpu_addr_modes addr_modes, u_char __user *s)
1028da957e11SThomas Gleixner {
1029da957e11SThomas Gleixner unsigned short tag_word = 0;
1030da957e11SThomas Gleixner u_char tag;
1031da957e11SThomas Gleixner int i;
1032da957e11SThomas Gleixner
1033da957e11SThomas Gleixner if ((addr_modes.default_mode == VM86) ||
1034da957e11SThomas Gleixner ((addr_modes.default_mode == PM16)
10353d0d14f9SIngo Molnar ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1036da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
103796d4f267SLinus Torvalds FPU_access_ok(s, 0x0e);
1038da957e11SThomas Gleixner FPU_get_user(control_word, (unsigned short __user *)s);
1039da957e11SThomas Gleixner FPU_get_user(partial_status, (unsigned short __user *)(s + 2));
1040da957e11SThomas Gleixner FPU_get_user(tag_word, (unsigned short __user *)(s + 4));
10413d0d14f9SIngo Molnar FPU_get_user(instruction_address.offset,
10423d0d14f9SIngo Molnar (unsigned short __user *)(s + 6));
10433d0d14f9SIngo Molnar FPU_get_user(instruction_address.selector,
10443d0d14f9SIngo Molnar (unsigned short __user *)(s + 8));
10453d0d14f9SIngo Molnar FPU_get_user(operand_address.offset,
10463d0d14f9SIngo Molnar (unsigned short __user *)(s + 0x0a));
10473d0d14f9SIngo Molnar FPU_get_user(operand_address.selector,
10483d0d14f9SIngo Molnar (unsigned short __user *)(s + 0x0c));
1049da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
1050da957e11SThomas Gleixner s += 0x0e;
10513d0d14f9SIngo Molnar if (addr_modes.default_mode == VM86) {
1052da957e11SThomas Gleixner instruction_address.offset
1053da957e11SThomas Gleixner += (instruction_address.selector & 0xf000) << 4;
10543d0d14f9SIngo Molnar operand_address.offset +=
10553d0d14f9SIngo Molnar (operand_address.selector & 0xf000) << 4;
1056da957e11SThomas Gleixner }
10573d0d14f9SIngo Molnar } else {
1058da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
105996d4f267SLinus Torvalds FPU_access_ok(s, 0x1c);
1060da957e11SThomas Gleixner FPU_get_user(control_word, (unsigned short __user *)s);
1061da957e11SThomas Gleixner FPU_get_user(partial_status, (unsigned short __user *)(s + 4));
1062da957e11SThomas Gleixner FPU_get_user(tag_word, (unsigned short __user *)(s + 8));
10633d0d14f9SIngo Molnar FPU_get_user(instruction_address.offset,
10643d0d14f9SIngo Molnar (unsigned long __user *)(s + 0x0c));
10653d0d14f9SIngo Molnar FPU_get_user(instruction_address.selector,
10663d0d14f9SIngo Molnar (unsigned short __user *)(s + 0x10));
10673d0d14f9SIngo Molnar FPU_get_user(instruction_address.opcode,
10683d0d14f9SIngo Molnar (unsigned short __user *)(s + 0x12));
10693d0d14f9SIngo Molnar FPU_get_user(operand_address.offset,
10703d0d14f9SIngo Molnar (unsigned long __user *)(s + 0x14));
10713d0d14f9SIngo Molnar FPU_get_user(operand_address.selector,
10723d0d14f9SIngo Molnar (unsigned long __user *)(s + 0x18));
1073da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
1074da957e11SThomas Gleixner s += 0x1c;
1075da957e11SThomas Gleixner }
1076da957e11SThomas Gleixner
1077da957e11SThomas Gleixner #ifdef PECULIAR_486
1078da957e11SThomas Gleixner control_word &= ~0xe080;
1079da957e11SThomas Gleixner #endif /* PECULIAR_486 */
1080da957e11SThomas Gleixner
1081da957e11SThomas Gleixner top = (partial_status >> SW_Top_Shift) & 7;
1082da957e11SThomas Gleixner
1083da957e11SThomas Gleixner if (partial_status & ~control_word & CW_Exceptions)
1084da957e11SThomas Gleixner partial_status |= (SW_Summary | SW_Backward);
1085da957e11SThomas Gleixner else
1086da957e11SThomas Gleixner partial_status &= ~(SW_Summary | SW_Backward);
1087da957e11SThomas Gleixner
10883d0d14f9SIngo Molnar for (i = 0; i < 8; i++) {
1089da957e11SThomas Gleixner tag = tag_word & 3;
1090da957e11SThomas Gleixner tag_word >>= 2;
1091da957e11SThomas Gleixner
1092da957e11SThomas Gleixner if (tag == TAG_Empty)
1093da957e11SThomas Gleixner /* New tag is empty. Accept it */
1094da957e11SThomas Gleixner FPU_settag(i, TAG_Empty);
10953d0d14f9SIngo Molnar else if (FPU_gettag(i) == TAG_Empty) {
1096da957e11SThomas Gleixner /* Old tag is empty and new tag is not empty. New tag is determined
1097da957e11SThomas Gleixner by old reg contents */
10983d0d14f9SIngo Molnar if (exponent(&fpu_register(i)) == -EXTENDED_Ebias) {
10993d0d14f9SIngo Molnar if (!
11003d0d14f9SIngo Molnar (fpu_register(i).sigl | fpu_register(i).
11013d0d14f9SIngo Molnar sigh))
1102da957e11SThomas Gleixner FPU_settag(i, TAG_Zero);
1103da957e11SThomas Gleixner else
1104da957e11SThomas Gleixner FPU_settag(i, TAG_Special);
11053d0d14f9SIngo Molnar } else if (exponent(&fpu_register(i)) ==
11063d0d14f9SIngo Molnar 0x7fff - EXTENDED_Ebias) {
1107da957e11SThomas Gleixner FPU_settag(i, TAG_Special);
11083d0d14f9SIngo Molnar } else if (fpu_register(i).sigh & 0x80000000)
1109da957e11SThomas Gleixner FPU_settag(i, TAG_Valid);
1110da957e11SThomas Gleixner else
1111da957e11SThomas Gleixner FPU_settag(i, TAG_Special); /* An Un-normal */
1112da957e11SThomas Gleixner }
1113da957e11SThomas Gleixner /* Else old tag is not empty and new tag is not empty. Old tag
1114da957e11SThomas Gleixner remains correct */
1115da957e11SThomas Gleixner }
1116da957e11SThomas Gleixner
1117da957e11SThomas Gleixner return s;
1118da957e11SThomas Gleixner }
1119da957e11SThomas Gleixner
FPU_frstor(fpu_addr_modes addr_modes,u_char __user * data_address)1120*872c65dbSThomas Gleixner void FPU_frstor(fpu_addr_modes addr_modes, u_char __user *data_address)
1121da957e11SThomas Gleixner {
1122da957e11SThomas Gleixner int i, regnr;
1123da957e11SThomas Gleixner u_char __user *s = fldenv(addr_modes, data_address);
1124da957e11SThomas Gleixner int offset = (top & 7) * 10, other = 80 - offset;
1125da957e11SThomas Gleixner
1126da957e11SThomas Gleixner /* Copy all registers in stack order. */
1127da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
112896d4f267SLinus Torvalds FPU_access_ok(s, 80);
1129e6b44ce1SArnd Bergmann FPU_copy_from_user(register_base + offset, s, other);
1130da957e11SThomas Gleixner if (offset)
1131e6b44ce1SArnd Bergmann FPU_copy_from_user(register_base, s + other, offset);
1132da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
1133da957e11SThomas Gleixner
11343d0d14f9SIngo Molnar for (i = 0; i < 8; i++) {
1135da957e11SThomas Gleixner regnr = (i + top) & 7;
1136da957e11SThomas Gleixner if (FPU_gettag(regnr) != TAG_Empty)
1137da957e11SThomas Gleixner /* The loaded data over-rides all other cases. */
1138da957e11SThomas Gleixner FPU_settag(regnr, FPU_tagof(&st(i)));
1139da957e11SThomas Gleixner }
1140da957e11SThomas Gleixner
1141da957e11SThomas Gleixner }
1142da957e11SThomas Gleixner
fstenv(fpu_addr_modes addr_modes,u_char __user * d)1143da957e11SThomas Gleixner u_char __user *fstenv(fpu_addr_modes addr_modes, u_char __user *d)
1144da957e11SThomas Gleixner {
1145da957e11SThomas Gleixner if ((addr_modes.default_mode == VM86) ||
1146da957e11SThomas Gleixner ((addr_modes.default_mode == PM16)
11473d0d14f9SIngo Molnar ^ (addr_modes.override.operand_size == OP_SIZE_PREFIX))) {
1148da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
114996d4f267SLinus Torvalds FPU_access_ok(d, 14);
1150da957e11SThomas Gleixner #ifdef PECULIAR_486
1151da957e11SThomas Gleixner FPU_put_user(control_word & ~0xe080, (unsigned long __user *)d);
1152da957e11SThomas Gleixner #else
1153da957e11SThomas Gleixner FPU_put_user(control_word, (unsigned short __user *)d);
1154da957e11SThomas Gleixner #endif /* PECULIAR_486 */
1155da957e11SThomas Gleixner FPU_put_user(status_word(), (unsigned short __user *)(d + 2));
1156da957e11SThomas Gleixner FPU_put_user(fpu_tag_word, (unsigned short __user *)(d + 4));
11573d0d14f9SIngo Molnar FPU_put_user(instruction_address.offset,
11583d0d14f9SIngo Molnar (unsigned short __user *)(d + 6));
11593d0d14f9SIngo Molnar FPU_put_user(operand_address.offset,
11603d0d14f9SIngo Molnar (unsigned short __user *)(d + 0x0a));
11613d0d14f9SIngo Molnar if (addr_modes.default_mode == VM86) {
11623d0d14f9SIngo Molnar FPU_put_user((instruction_address.
11633d0d14f9SIngo Molnar offset & 0xf0000) >> 4,
1164da957e11SThomas Gleixner (unsigned short __user *)(d + 8));
1165da957e11SThomas Gleixner FPU_put_user((operand_address.offset & 0xf0000) >> 4,
1166da957e11SThomas Gleixner (unsigned short __user *)(d + 0x0c));
11673d0d14f9SIngo Molnar } else {
11683d0d14f9SIngo Molnar FPU_put_user(instruction_address.selector,
11693d0d14f9SIngo Molnar (unsigned short __user *)(d + 8));
11703d0d14f9SIngo Molnar FPU_put_user(operand_address.selector,
11713d0d14f9SIngo Molnar (unsigned short __user *)(d + 0x0c));
1172da957e11SThomas Gleixner }
1173da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
1174da957e11SThomas Gleixner d += 0x0e;
11753d0d14f9SIngo Molnar } else {
1176da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
117796d4f267SLinus Torvalds FPU_access_ok(d, 7 * 4);
1178da957e11SThomas Gleixner #ifdef PECULIAR_486
1179da957e11SThomas Gleixner control_word &= ~0xe080;
1180da957e11SThomas Gleixner /* An 80486 sets nearly all of the reserved bits to 1. */
1181da957e11SThomas Gleixner control_word |= 0xffff0040;
1182da957e11SThomas Gleixner partial_status = status_word() | 0xffff0000;
1183da957e11SThomas Gleixner fpu_tag_word |= 0xffff0000;
118461c4628bSSuresh Siddha I387->soft.fcs &= ~0xf8000000;
118561c4628bSSuresh Siddha I387->soft.fos |= 0xffff0000;
1186da957e11SThomas Gleixner #endif /* PECULIAR_486 */
1187da957e11SThomas Gleixner if (__copy_to_user(d, &control_word, 7 * 4))
1188da957e11SThomas Gleixner FPU_abort;
1189da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
1190da957e11SThomas Gleixner d += 0x1c;
1191da957e11SThomas Gleixner }
1192da957e11SThomas Gleixner
1193da957e11SThomas Gleixner control_word |= CW_Exceptions;
1194da957e11SThomas Gleixner partial_status &= ~(SW_Summary | SW_Backward);
1195da957e11SThomas Gleixner
1196da957e11SThomas Gleixner return d;
1197da957e11SThomas Gleixner }
1198da957e11SThomas Gleixner
fsave(fpu_addr_modes addr_modes,u_char __user * data_address)1199da957e11SThomas Gleixner void fsave(fpu_addr_modes addr_modes, u_char __user *data_address)
1200da957e11SThomas Gleixner {
1201da957e11SThomas Gleixner u_char __user *d;
1202da957e11SThomas Gleixner int offset = (top & 7) * 10, other = 80 - offset;
1203da957e11SThomas Gleixner
1204da957e11SThomas Gleixner d = fstenv(addr_modes, data_address);
1205da957e11SThomas Gleixner
1206da957e11SThomas Gleixner RE_ENTRANT_CHECK_OFF;
120796d4f267SLinus Torvalds FPU_access_ok(d, 80);
1208da957e11SThomas Gleixner
1209da957e11SThomas Gleixner /* Copy all registers in stack order. */
1210da957e11SThomas Gleixner if (__copy_to_user(d, register_base + offset, other))
1211da957e11SThomas Gleixner FPU_abort;
1212da957e11SThomas Gleixner if (offset)
1213da957e11SThomas Gleixner if (__copy_to_user(d + other, register_base, offset))
1214da957e11SThomas Gleixner FPU_abort;
1215da957e11SThomas Gleixner RE_ENTRANT_CHECK_ON;
1216da957e11SThomas Gleixner
1217da957e11SThomas Gleixner finit();
1218da957e11SThomas Gleixner }
1219da957e11SThomas Gleixner
1220da957e11SThomas Gleixner /*===========================================================================*/
1221