xref: /openbmc/linux/arch/x86/math-emu/reg_ld_str.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
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