xref: /OK3568_Linux_fs/kernel/arch/x86/math-emu/fpu_entry.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*---------------------------------------------------------------------------+
3*4882a593Smuzhiyun  |  fpu_entry.c                                                              |
4*4882a593Smuzhiyun  |                                                                           |
5*4882a593Smuzhiyun  | The entry functions for wm-FPU-emu                                        |
6*4882a593Smuzhiyun  |                                                                           |
7*4882a593Smuzhiyun  | Copyright (C) 1992,1993,1994,1996,1997                                    |
8*4882a593Smuzhiyun  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9*4882a593Smuzhiyun  |                  E-mail   billm@suburbia.net                              |
10*4882a593Smuzhiyun  |                                                                           |
11*4882a593Smuzhiyun  | See the files "README" and "COPYING" for further copyright and warranty   |
12*4882a593Smuzhiyun  | information.                                                              |
13*4882a593Smuzhiyun  |                                                                           |
14*4882a593Smuzhiyun  +---------------------------------------------------------------------------*/
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun /*---------------------------------------------------------------------------+
17*4882a593Smuzhiyun  | Note:                                                                     |
18*4882a593Smuzhiyun  |    The file contains code which accesses user memory.                     |
19*4882a593Smuzhiyun  |    Emulator static data may change when user memory is accessed, due to   |
20*4882a593Smuzhiyun  |    other processes using the emulator while swapping is in progress.      |
21*4882a593Smuzhiyun  +---------------------------------------------------------------------------*/
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /*---------------------------------------------------------------------------+
24*4882a593Smuzhiyun  | math_emulate(), restore_i387_soft() and save_i387_soft() are the only     |
25*4882a593Smuzhiyun  | entry points for wm-FPU-emu.                                              |
26*4882a593Smuzhiyun  +---------------------------------------------------------------------------*/
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun #include <linux/signal.h>
29*4882a593Smuzhiyun #include <linux/regset.h>
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #include <linux/uaccess.h>
32*4882a593Smuzhiyun #include <asm/traps.h>
33*4882a593Smuzhiyun #include <asm/user.h>
34*4882a593Smuzhiyun #include <asm/fpu/internal.h>
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #include "fpu_system.h"
37*4882a593Smuzhiyun #include "fpu_emu.h"
38*4882a593Smuzhiyun #include "exception.h"
39*4882a593Smuzhiyun #include "control_w.h"
40*4882a593Smuzhiyun #include "status_w.h"
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #define __BAD__ FPU_illegal	/* Illegal on an 80486, causes SIGILL */
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun /* fcmovCC and f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun /* WARNING: "u" entries are not documented by Intel in their 80486 manual
47*4882a593Smuzhiyun    and may not work on FPU clones or later Intel FPUs.
48*4882a593Smuzhiyun    Changes to support them provided by Linus Torvalds. */
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun static FUNC const st_instr_table[64] = {
51*4882a593Smuzhiyun /* Opcode:	d8		d9		da		db */
52*4882a593Smuzhiyun /*		dc		dd		de		df */
53*4882a593Smuzhiyun /* c0..7 */	fadd__,		fld_i_,		fcmovb,		fcmovnb,
54*4882a593Smuzhiyun /* c0..7 */	fadd_i,		ffree_,		faddp_,		ffreep,/*u*/
55*4882a593Smuzhiyun /* c8..f */	fmul__,		fxch_i,		fcmove,		fcmovne,
56*4882a593Smuzhiyun /* c8..f */	fmul_i,		fxch_i,/*u*/	fmulp_,		fxch_i,/*u*/
57*4882a593Smuzhiyun /* d0..7 */	fcom_st,	fp_nop,		fcmovbe,	fcmovnbe,
58*4882a593Smuzhiyun /* d0..7 */	fcom_st,/*u*/	fst_i_,		fcompst,/*u*/	fstp_i,/*u*/
59*4882a593Smuzhiyun /* d8..f */	fcompst,	fstp_i,/*u*/	fcmovu,		fcmovnu,
60*4882a593Smuzhiyun /* d8..f */	fcompst,/*u*/	fstp_i,		fcompp,		fstp_i,/*u*/
61*4882a593Smuzhiyun /* e0..7 */	fsub__,		FPU_etc,	__BAD__,	finit_,
62*4882a593Smuzhiyun /* e0..7 */	fsubri,		fucom_,		fsubrp,		fstsw_,
63*4882a593Smuzhiyun /* e8..f */	fsubr_,		fconst,		fucompp,	fucomi_,
64*4882a593Smuzhiyun /* e8..f */	fsub_i,		fucomp,		fsubp_,		fucomip,
65*4882a593Smuzhiyun /* f0..7 */	fdiv__,		FPU_triga,	__BAD__,	fcomi_,
66*4882a593Smuzhiyun /* f0..7 */	fdivri,		__BAD__,	fdivrp,		fcomip,
67*4882a593Smuzhiyun /* f8..f */	fdivr_,		FPU_trigb,	__BAD__,	__BAD__,
68*4882a593Smuzhiyun /* f8..f */	fdiv_i,		__BAD__,	fdivp_,		__BAD__,
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun #define _NONE_ 0		/* Take no special action */
72*4882a593Smuzhiyun #define _REG0_ 1		/* Need to check for not empty st(0) */
73*4882a593Smuzhiyun #define _REGI_ 2		/* Need to check for not empty st(0) and st(rm) */
74*4882a593Smuzhiyun #define _REGi_ 0		/* Uses st(rm) */
75*4882a593Smuzhiyun #define _PUSH_ 3		/* Need to check for space to push onto stack */
76*4882a593Smuzhiyun #define _null_ 4		/* Function illegal or not implemented */
77*4882a593Smuzhiyun #define _REGIi 5		/* Uses st(0) and st(rm), result to st(rm) */
78*4882a593Smuzhiyun #define _REGIp 6		/* Uses st(0) and st(rm), result to st(rm) then pop */
79*4882a593Smuzhiyun #define _REGIc 0		/* Compare st(0) and st(rm) */
80*4882a593Smuzhiyun #define _REGIn 0		/* Uses st(0) and st(rm), but handle checks later */
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun static u_char const type_table[64] = {
83*4882a593Smuzhiyun /* Opcode:	d8	d9	da	db	dc	dd	de	df */
84*4882a593Smuzhiyun /* c0..7 */	_REGI_, _NONE_, _REGIn, _REGIn, _REGIi, _REGi_, _REGIp, _REGi_,
85*4882a593Smuzhiyun /* c8..f */	_REGI_, _REGIn, _REGIn, _REGIn, _REGIi, _REGI_, _REGIp, _REGI_,
86*4882a593Smuzhiyun /* d0..7 */	_REGIc, _NONE_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
87*4882a593Smuzhiyun /* d8..f */	_REGIc, _REG0_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_,
88*4882a593Smuzhiyun /* e0..7 */	_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
89*4882a593Smuzhiyun /* e8..f */	_REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
90*4882a593Smuzhiyun /* f0..7 */	_REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
91*4882a593Smuzhiyun /* f8..f */	_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
92*4882a593Smuzhiyun };
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun #ifdef RE_ENTRANT_CHECKING
95*4882a593Smuzhiyun u_char emulating = 0;
96*4882a593Smuzhiyun #endif /* RE_ENTRANT_CHECKING */
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun static int valid_prefix(u_char *Byte, u_char __user ** fpu_eip,
99*4882a593Smuzhiyun 			overrides * override);
100*4882a593Smuzhiyun 
math_emulate(struct math_emu_info * info)101*4882a593Smuzhiyun void math_emulate(struct math_emu_info *info)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun 	u_char FPU_modrm, byte1;
104*4882a593Smuzhiyun 	unsigned short code;
105*4882a593Smuzhiyun 	fpu_addr_modes addr_modes;
106*4882a593Smuzhiyun 	int unmasked;
107*4882a593Smuzhiyun 	FPU_REG loaded_data;
108*4882a593Smuzhiyun 	FPU_REG *st0_ptr;
109*4882a593Smuzhiyun 	u_char loaded_tag, st0_tag;
110*4882a593Smuzhiyun 	void __user *data_address;
111*4882a593Smuzhiyun 	struct address data_sel_off;
112*4882a593Smuzhiyun 	struct address entry_sel_off;
113*4882a593Smuzhiyun 	unsigned long code_base = 0;
114*4882a593Smuzhiyun 	unsigned long code_limit = 0;	/* Initialized to stop compiler warnings */
115*4882a593Smuzhiyun 	struct desc_struct code_descriptor;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun #ifdef RE_ENTRANT_CHECKING
118*4882a593Smuzhiyun 	if (emulating) {
119*4882a593Smuzhiyun 		printk("ERROR: wm-FPU-emu is not RE-ENTRANT!\n");
120*4882a593Smuzhiyun 	}
121*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
122*4882a593Smuzhiyun #endif /* RE_ENTRANT_CHECKING */
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	FPU_info = info;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	FPU_ORIG_EIP = FPU_EIP;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	if ((FPU_EFLAGS & 0x00020000) != 0) {
129*4882a593Smuzhiyun 		/* Virtual 8086 mode */
130*4882a593Smuzhiyun 		addr_modes.default_mode = VM86;
131*4882a593Smuzhiyun 		FPU_EIP += code_base = FPU_CS << 4;
132*4882a593Smuzhiyun 		code_limit = code_base + 0xffff;	/* Assumes code_base <= 0xffff0000 */
133*4882a593Smuzhiyun 	} else if (FPU_CS == __USER_CS && FPU_DS == __USER_DS) {
134*4882a593Smuzhiyun 		addr_modes.default_mode = 0;
135*4882a593Smuzhiyun 	} else if (FPU_CS == __KERNEL_CS) {
136*4882a593Smuzhiyun 		printk("math_emulate: %04x:%08lx\n", FPU_CS, FPU_EIP);
137*4882a593Smuzhiyun 		panic("Math emulation needed in kernel");
138*4882a593Smuzhiyun 	} else {
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 		if ((FPU_CS & 4) != 4) {	/* Must be in the LDT */
141*4882a593Smuzhiyun 			/* Can only handle segmented addressing via the LDT
142*4882a593Smuzhiyun 			   for now, and it must be 16 bit */
143*4882a593Smuzhiyun 			printk("FPU emulator: Unsupported addressing mode\n");
144*4882a593Smuzhiyun 			math_abort(FPU_info, SIGILL);
145*4882a593Smuzhiyun 		}
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 		code_descriptor = FPU_get_ldt_descriptor(FPU_CS);
148*4882a593Smuzhiyun 		if (code_descriptor.d) {
149*4882a593Smuzhiyun 			/* The above test may be wrong, the book is not clear */
150*4882a593Smuzhiyun 			/* Segmented 32 bit protected mode */
151*4882a593Smuzhiyun 			addr_modes.default_mode = SEG32;
152*4882a593Smuzhiyun 		} else {
153*4882a593Smuzhiyun 			/* 16 bit protected mode */
154*4882a593Smuzhiyun 			addr_modes.default_mode = PM16;
155*4882a593Smuzhiyun 		}
156*4882a593Smuzhiyun 		FPU_EIP += code_base = seg_get_base(&code_descriptor);
157*4882a593Smuzhiyun 		code_limit = seg_get_limit(&code_descriptor) + 1;
158*4882a593Smuzhiyun 		code_limit *= seg_get_granularity(&code_descriptor);
159*4882a593Smuzhiyun 		code_limit += code_base - 1;
160*4882a593Smuzhiyun 		if (code_limit < code_base)
161*4882a593Smuzhiyun 			code_limit = 0xffffffff;
162*4882a593Smuzhiyun 	}
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	FPU_lookahead = !(FPU_EFLAGS & X86_EFLAGS_TF);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	if (!valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
167*4882a593Smuzhiyun 			  &addr_modes.override)) {
168*4882a593Smuzhiyun 		RE_ENTRANT_CHECK_OFF;
169*4882a593Smuzhiyun 		printk
170*4882a593Smuzhiyun 		    ("FPU emulator: Unknown prefix byte 0x%02x, probably due to\n"
171*4882a593Smuzhiyun 		     "FPU emulator: self-modifying code! (emulation impossible)\n",
172*4882a593Smuzhiyun 		     byte1);
173*4882a593Smuzhiyun 		RE_ENTRANT_CHECK_ON;
174*4882a593Smuzhiyun 		EXCEPTION(EX_INTERNAL | 0x126);
175*4882a593Smuzhiyun 		math_abort(FPU_info, SIGILL);
176*4882a593Smuzhiyun 	}
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun       do_another_FPU_instruction:
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	no_ip_update = 0;
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	FPU_EIP++;		/* We have fetched the prefix and first code bytes. */
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	if (addr_modes.default_mode) {
185*4882a593Smuzhiyun 		/* This checks for the minimum instruction bytes.
186*4882a593Smuzhiyun 		   We also need to check any extra (address mode) code access. */
187*4882a593Smuzhiyun 		if (FPU_EIP > code_limit)
188*4882a593Smuzhiyun 			math_abort(FPU_info, SIGSEGV);
189*4882a593Smuzhiyun 	}
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	if ((byte1 & 0xf8) != 0xd8) {
192*4882a593Smuzhiyun 		if (byte1 == FWAIT_OPCODE) {
193*4882a593Smuzhiyun 			if (partial_status & SW_Summary)
194*4882a593Smuzhiyun 				goto do_the_FPU_interrupt;
195*4882a593Smuzhiyun 			else
196*4882a593Smuzhiyun 				goto FPU_fwait_done;
197*4882a593Smuzhiyun 		}
198*4882a593Smuzhiyun #ifdef PARANOID
199*4882a593Smuzhiyun 		EXCEPTION(EX_INTERNAL | 0x128);
200*4882a593Smuzhiyun 		math_abort(FPU_info, SIGILL);
201*4882a593Smuzhiyun #endif /* PARANOID */
202*4882a593Smuzhiyun 	}
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
205*4882a593Smuzhiyun 	FPU_code_access_ok(1);
206*4882a593Smuzhiyun 	FPU_get_user(FPU_modrm, (u_char __user *) FPU_EIP);
207*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
208*4882a593Smuzhiyun 	FPU_EIP++;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	if (partial_status & SW_Summary) {
211*4882a593Smuzhiyun 		/* Ignore the error for now if the current instruction is a no-wait
212*4882a593Smuzhiyun 		   control instruction */
213*4882a593Smuzhiyun 		/* The 80486 manual contradicts itself on this topic,
214*4882a593Smuzhiyun 		   but a real 80486 uses the following instructions:
215*4882a593Smuzhiyun 		   fninit, fnstenv, fnsave, fnstsw, fnstenv, fnclex.
216*4882a593Smuzhiyun 		 */
217*4882a593Smuzhiyun 		code = (FPU_modrm << 8) | byte1;
218*4882a593Smuzhiyun 		if (!((((code & 0xf803) == 0xe003) ||	/* fnclex, fninit, fnstsw */
219*4882a593Smuzhiyun 		       (((code & 0x3003) == 0x3001) &&	/* fnsave, fnstcw, fnstenv,
220*4882a593Smuzhiyun 							   fnstsw */
221*4882a593Smuzhiyun 			((code & 0xc000) != 0xc000))))) {
222*4882a593Smuzhiyun 			/*
223*4882a593Smuzhiyun 			 *  We need to simulate the action of the kernel to FPU
224*4882a593Smuzhiyun 			 *  interrupts here.
225*4882a593Smuzhiyun 			 */
226*4882a593Smuzhiyun 		      do_the_FPU_interrupt:
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 			FPU_EIP = FPU_ORIG_EIP;	/* Point to current FPU instruction. */
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 			RE_ENTRANT_CHECK_OFF;
231*4882a593Smuzhiyun 			current->thread.trap_nr = X86_TRAP_MF;
232*4882a593Smuzhiyun 			current->thread.error_code = 0;
233*4882a593Smuzhiyun 			send_sig(SIGFPE, current, 1);
234*4882a593Smuzhiyun 			return;
235*4882a593Smuzhiyun 		}
236*4882a593Smuzhiyun 	}
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	entry_sel_off.offset = FPU_ORIG_EIP;
239*4882a593Smuzhiyun 	entry_sel_off.selector = FPU_CS;
240*4882a593Smuzhiyun 	entry_sel_off.opcode = (byte1 << 8) | FPU_modrm;
241*4882a593Smuzhiyun 	entry_sel_off.empty = 0;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	FPU_rm = FPU_modrm & 7;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	if (FPU_modrm < 0300) {
246*4882a593Smuzhiyun 		/* All of these instructions use the mod/rm byte to get a data address */
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 		if ((addr_modes.default_mode & SIXTEEN)
249*4882a593Smuzhiyun 		    ^ (addr_modes.override.address_size == ADDR_SIZE_PREFIX))
250*4882a593Smuzhiyun 			data_address =
251*4882a593Smuzhiyun 			    FPU_get_address_16(FPU_modrm, &FPU_EIP,
252*4882a593Smuzhiyun 					       &data_sel_off, addr_modes);
253*4882a593Smuzhiyun 		else
254*4882a593Smuzhiyun 			data_address =
255*4882a593Smuzhiyun 			    FPU_get_address(FPU_modrm, &FPU_EIP, &data_sel_off,
256*4882a593Smuzhiyun 					    addr_modes);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 		if (addr_modes.default_mode) {
259*4882a593Smuzhiyun 			if (FPU_EIP - 1 > code_limit)
260*4882a593Smuzhiyun 				math_abort(FPU_info, SIGSEGV);
261*4882a593Smuzhiyun 		}
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 		if (!(byte1 & 1)) {
264*4882a593Smuzhiyun 			unsigned short status1 = partial_status;
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun 			st0_ptr = &st(0);
267*4882a593Smuzhiyun 			st0_tag = FPU_gettag0();
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 			/* Stack underflow has priority */
270*4882a593Smuzhiyun 			if (NOT_EMPTY_ST0) {
271*4882a593Smuzhiyun 				if (addr_modes.default_mode & PROTECTED) {
272*4882a593Smuzhiyun 					/* This table works for 16 and 32 bit protected mode */
273*4882a593Smuzhiyun 					if (access_limit <
274*4882a593Smuzhiyun 					    data_sizes_16[(byte1 >> 1) & 3])
275*4882a593Smuzhiyun 						math_abort(FPU_info, SIGSEGV);
276*4882a593Smuzhiyun 				}
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 				unmasked = 0;	/* Do this here to stop compiler warnings. */
279*4882a593Smuzhiyun 				switch ((byte1 >> 1) & 3) {
280*4882a593Smuzhiyun 				case 0:
281*4882a593Smuzhiyun 					unmasked =
282*4882a593Smuzhiyun 					    FPU_load_single((float __user *)
283*4882a593Smuzhiyun 							    data_address,
284*4882a593Smuzhiyun 							    &loaded_data);
285*4882a593Smuzhiyun 					loaded_tag = unmasked & 0xff;
286*4882a593Smuzhiyun 					unmasked &= ~0xff;
287*4882a593Smuzhiyun 					break;
288*4882a593Smuzhiyun 				case 1:
289*4882a593Smuzhiyun 					loaded_tag =
290*4882a593Smuzhiyun 					    FPU_load_int32((long __user *)
291*4882a593Smuzhiyun 							   data_address,
292*4882a593Smuzhiyun 							   &loaded_data);
293*4882a593Smuzhiyun 					break;
294*4882a593Smuzhiyun 				case 2:
295*4882a593Smuzhiyun 					unmasked =
296*4882a593Smuzhiyun 					    FPU_load_double((double __user *)
297*4882a593Smuzhiyun 							    data_address,
298*4882a593Smuzhiyun 							    &loaded_data);
299*4882a593Smuzhiyun 					loaded_tag = unmasked & 0xff;
300*4882a593Smuzhiyun 					unmasked &= ~0xff;
301*4882a593Smuzhiyun 					break;
302*4882a593Smuzhiyun 				case 3:
303*4882a593Smuzhiyun 				default:	/* Used here to suppress gcc warnings. */
304*4882a593Smuzhiyun 					loaded_tag =
305*4882a593Smuzhiyun 					    FPU_load_int16((short __user *)
306*4882a593Smuzhiyun 							   data_address,
307*4882a593Smuzhiyun 							   &loaded_data);
308*4882a593Smuzhiyun 					break;
309*4882a593Smuzhiyun 				}
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 				/* No more access to user memory, it is safe
312*4882a593Smuzhiyun 				   to use static data now */
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 				/* NaN operands have the next priority. */
315*4882a593Smuzhiyun 				/* We have to delay looking at st(0) until after
316*4882a593Smuzhiyun 				   loading the data, because that data might contain an SNaN */
317*4882a593Smuzhiyun 				if (((st0_tag == TAG_Special) && isNaN(st0_ptr))
318*4882a593Smuzhiyun 				    || ((loaded_tag == TAG_Special)
319*4882a593Smuzhiyun 					&& isNaN(&loaded_data))) {
320*4882a593Smuzhiyun 					/* Restore the status word; we might have loaded a
321*4882a593Smuzhiyun 					   denormal. */
322*4882a593Smuzhiyun 					partial_status = status1;
323*4882a593Smuzhiyun 					if ((FPU_modrm & 0x30) == 0x10) {
324*4882a593Smuzhiyun 						/* fcom or fcomp */
325*4882a593Smuzhiyun 						EXCEPTION(EX_Invalid);
326*4882a593Smuzhiyun 						setcc(SW_C3 | SW_C2 | SW_C0);
327*4882a593Smuzhiyun 						if ((FPU_modrm & 0x08)
328*4882a593Smuzhiyun 						    && (control_word &
329*4882a593Smuzhiyun 							CW_Invalid))
330*4882a593Smuzhiyun 							FPU_pop();	/* fcomp, masked, so we pop. */
331*4882a593Smuzhiyun 					} else {
332*4882a593Smuzhiyun 						if (loaded_tag == TAG_Special)
333*4882a593Smuzhiyun 							loaded_tag =
334*4882a593Smuzhiyun 							    FPU_Special
335*4882a593Smuzhiyun 							    (&loaded_data);
336*4882a593Smuzhiyun #ifdef PECULIAR_486
337*4882a593Smuzhiyun 						/* This is not really needed, but gives behaviour
338*4882a593Smuzhiyun 						   identical to an 80486 */
339*4882a593Smuzhiyun 						if ((FPU_modrm & 0x28) == 0x20)
340*4882a593Smuzhiyun 							/* fdiv or fsub */
341*4882a593Smuzhiyun 							real_2op_NaN
342*4882a593Smuzhiyun 							    (&loaded_data,
343*4882a593Smuzhiyun 							     loaded_tag, 0,
344*4882a593Smuzhiyun 							     &loaded_data);
345*4882a593Smuzhiyun 						else
346*4882a593Smuzhiyun #endif /* PECULIAR_486 */
347*4882a593Smuzhiyun 							/* fadd, fdivr, fmul, or fsubr */
348*4882a593Smuzhiyun 							real_2op_NaN
349*4882a593Smuzhiyun 							    (&loaded_data,
350*4882a593Smuzhiyun 							     loaded_tag, 0,
351*4882a593Smuzhiyun 							     st0_ptr);
352*4882a593Smuzhiyun 					}
353*4882a593Smuzhiyun 					goto reg_mem_instr_done;
354*4882a593Smuzhiyun 				}
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 				if (unmasked && !((FPU_modrm & 0x30) == 0x10)) {
357*4882a593Smuzhiyun 					/* Is not a comparison instruction. */
358*4882a593Smuzhiyun 					if ((FPU_modrm & 0x38) == 0x38) {
359*4882a593Smuzhiyun 						/* fdivr */
360*4882a593Smuzhiyun 						if ((st0_tag == TAG_Zero) &&
361*4882a593Smuzhiyun 						    ((loaded_tag == TAG_Valid)
362*4882a593Smuzhiyun 						     || (loaded_tag ==
363*4882a593Smuzhiyun 							 TAG_Special
364*4882a593Smuzhiyun 							 &&
365*4882a593Smuzhiyun 							 isdenormal
366*4882a593Smuzhiyun 							 (&loaded_data)))) {
367*4882a593Smuzhiyun 							if (FPU_divide_by_zero
368*4882a593Smuzhiyun 							    (0,
369*4882a593Smuzhiyun 							     getsign
370*4882a593Smuzhiyun 							     (&loaded_data))
371*4882a593Smuzhiyun 							    < 0) {
372*4882a593Smuzhiyun 								/* We use the fact here that the unmasked
373*4882a593Smuzhiyun 								   exception in the loaded data was for a
374*4882a593Smuzhiyun 								   denormal operand */
375*4882a593Smuzhiyun 								/* Restore the state of the denormal op bit */
376*4882a593Smuzhiyun 								partial_status
377*4882a593Smuzhiyun 								    &=
378*4882a593Smuzhiyun 								    ~SW_Denorm_Op;
379*4882a593Smuzhiyun 								partial_status
380*4882a593Smuzhiyun 								    |=
381*4882a593Smuzhiyun 								    status1 &
382*4882a593Smuzhiyun 								    SW_Denorm_Op;
383*4882a593Smuzhiyun 							} else
384*4882a593Smuzhiyun 								setsign(st0_ptr,
385*4882a593Smuzhiyun 									getsign
386*4882a593Smuzhiyun 									(&loaded_data));
387*4882a593Smuzhiyun 						}
388*4882a593Smuzhiyun 					}
389*4882a593Smuzhiyun 					goto reg_mem_instr_done;
390*4882a593Smuzhiyun 				}
391*4882a593Smuzhiyun 
392*4882a593Smuzhiyun 				switch ((FPU_modrm >> 3) & 7) {
393*4882a593Smuzhiyun 				case 0:	/* fadd */
394*4882a593Smuzhiyun 					clear_C1();
395*4882a593Smuzhiyun 					FPU_add(&loaded_data, loaded_tag, 0,
396*4882a593Smuzhiyun 						control_word);
397*4882a593Smuzhiyun 					break;
398*4882a593Smuzhiyun 				case 1:	/* fmul */
399*4882a593Smuzhiyun 					clear_C1();
400*4882a593Smuzhiyun 					FPU_mul(&loaded_data, loaded_tag, 0,
401*4882a593Smuzhiyun 						control_word);
402*4882a593Smuzhiyun 					break;
403*4882a593Smuzhiyun 				case 2:	/* fcom */
404*4882a593Smuzhiyun 					FPU_compare_st_data(&loaded_data,
405*4882a593Smuzhiyun 							    loaded_tag);
406*4882a593Smuzhiyun 					break;
407*4882a593Smuzhiyun 				case 3:	/* fcomp */
408*4882a593Smuzhiyun 					if (!FPU_compare_st_data
409*4882a593Smuzhiyun 					    (&loaded_data, loaded_tag)
410*4882a593Smuzhiyun 					    && !unmasked)
411*4882a593Smuzhiyun 						FPU_pop();
412*4882a593Smuzhiyun 					break;
413*4882a593Smuzhiyun 				case 4:	/* fsub */
414*4882a593Smuzhiyun 					clear_C1();
415*4882a593Smuzhiyun 					FPU_sub(LOADED | loaded_tag,
416*4882a593Smuzhiyun 						(int)&loaded_data,
417*4882a593Smuzhiyun 						control_word);
418*4882a593Smuzhiyun 					break;
419*4882a593Smuzhiyun 				case 5:	/* fsubr */
420*4882a593Smuzhiyun 					clear_C1();
421*4882a593Smuzhiyun 					FPU_sub(REV | LOADED | loaded_tag,
422*4882a593Smuzhiyun 						(int)&loaded_data,
423*4882a593Smuzhiyun 						control_word);
424*4882a593Smuzhiyun 					break;
425*4882a593Smuzhiyun 				case 6:	/* fdiv */
426*4882a593Smuzhiyun 					clear_C1();
427*4882a593Smuzhiyun 					FPU_div(LOADED | loaded_tag,
428*4882a593Smuzhiyun 						(int)&loaded_data,
429*4882a593Smuzhiyun 						control_word);
430*4882a593Smuzhiyun 					break;
431*4882a593Smuzhiyun 				case 7:	/* fdivr */
432*4882a593Smuzhiyun 					clear_C1();
433*4882a593Smuzhiyun 					if (st0_tag == TAG_Zero)
434*4882a593Smuzhiyun 						partial_status = status1;	/* Undo any denorm tag,
435*4882a593Smuzhiyun 										   zero-divide has priority. */
436*4882a593Smuzhiyun 					FPU_div(REV | LOADED | loaded_tag,
437*4882a593Smuzhiyun 						(int)&loaded_data,
438*4882a593Smuzhiyun 						control_word);
439*4882a593Smuzhiyun 					break;
440*4882a593Smuzhiyun 				}
441*4882a593Smuzhiyun 			} else {
442*4882a593Smuzhiyun 				if ((FPU_modrm & 0x30) == 0x10) {
443*4882a593Smuzhiyun 					/* The instruction is fcom or fcomp */
444*4882a593Smuzhiyun 					EXCEPTION(EX_StackUnder);
445*4882a593Smuzhiyun 					setcc(SW_C3 | SW_C2 | SW_C0);
446*4882a593Smuzhiyun 					if ((FPU_modrm & 0x08)
447*4882a593Smuzhiyun 					    && (control_word & CW_Invalid))
448*4882a593Smuzhiyun 						FPU_pop();	/* fcomp */
449*4882a593Smuzhiyun 				} else
450*4882a593Smuzhiyun 					FPU_stack_underflow();
451*4882a593Smuzhiyun 			}
452*4882a593Smuzhiyun 		      reg_mem_instr_done:
453*4882a593Smuzhiyun 			operand_address = data_sel_off;
454*4882a593Smuzhiyun 		} else {
455*4882a593Smuzhiyun 			if (!(no_ip_update =
456*4882a593Smuzhiyun 			      FPU_load_store(((FPU_modrm & 0x38) | (byte1 & 6))
457*4882a593Smuzhiyun 					     >> 1, addr_modes, data_address))) {
458*4882a593Smuzhiyun 				operand_address = data_sel_off;
459*4882a593Smuzhiyun 			}
460*4882a593Smuzhiyun 		}
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	} else {
463*4882a593Smuzhiyun 		/* None of these instructions access user memory */
464*4882a593Smuzhiyun 		u_char instr_index = (FPU_modrm & 0x38) | (byte1 & 7);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun #ifdef PECULIAR_486
467*4882a593Smuzhiyun 		/* This is supposed to be undefined, but a real 80486 seems
468*4882a593Smuzhiyun 		   to do this: */
469*4882a593Smuzhiyun 		operand_address.offset = 0;
470*4882a593Smuzhiyun 		operand_address.selector = FPU_DS;
471*4882a593Smuzhiyun #endif /* PECULIAR_486 */
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 		st0_ptr = &st(0);
474*4882a593Smuzhiyun 		st0_tag = FPU_gettag0();
475*4882a593Smuzhiyun 		switch (type_table[(int)instr_index]) {
476*4882a593Smuzhiyun 		case _NONE_:	/* also _REGIc: _REGIn */
477*4882a593Smuzhiyun 			break;
478*4882a593Smuzhiyun 		case _REG0_:
479*4882a593Smuzhiyun 			if (!NOT_EMPTY_ST0) {
480*4882a593Smuzhiyun 				FPU_stack_underflow();
481*4882a593Smuzhiyun 				goto FPU_instruction_done;
482*4882a593Smuzhiyun 			}
483*4882a593Smuzhiyun 			break;
484*4882a593Smuzhiyun 		case _REGIi:
485*4882a593Smuzhiyun 			if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
486*4882a593Smuzhiyun 				FPU_stack_underflow_i(FPU_rm);
487*4882a593Smuzhiyun 				goto FPU_instruction_done;
488*4882a593Smuzhiyun 			}
489*4882a593Smuzhiyun 			break;
490*4882a593Smuzhiyun 		case _REGIp:
491*4882a593Smuzhiyun 			if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
492*4882a593Smuzhiyun 				FPU_stack_underflow_pop(FPU_rm);
493*4882a593Smuzhiyun 				goto FPU_instruction_done;
494*4882a593Smuzhiyun 			}
495*4882a593Smuzhiyun 			break;
496*4882a593Smuzhiyun 		case _REGI_:
497*4882a593Smuzhiyun 			if (!NOT_EMPTY_ST0 || !NOT_EMPTY(FPU_rm)) {
498*4882a593Smuzhiyun 				FPU_stack_underflow();
499*4882a593Smuzhiyun 				goto FPU_instruction_done;
500*4882a593Smuzhiyun 			}
501*4882a593Smuzhiyun 			break;
502*4882a593Smuzhiyun 		case _PUSH_:	/* Only used by the fld st(i) instruction */
503*4882a593Smuzhiyun 			break;
504*4882a593Smuzhiyun 		case _null_:
505*4882a593Smuzhiyun 			FPU_illegal();
506*4882a593Smuzhiyun 			goto FPU_instruction_done;
507*4882a593Smuzhiyun 		default:
508*4882a593Smuzhiyun 			EXCEPTION(EX_INTERNAL | 0x111);
509*4882a593Smuzhiyun 			goto FPU_instruction_done;
510*4882a593Smuzhiyun 		}
511*4882a593Smuzhiyun 		(*st_instr_table[(int)instr_index]) ();
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	      FPU_instruction_done:
514*4882a593Smuzhiyun 		;
515*4882a593Smuzhiyun 	}
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	if (!no_ip_update)
518*4882a593Smuzhiyun 		instruction_address = entry_sel_off;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun       FPU_fwait_done:
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun #ifdef DEBUG
523*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
524*4882a593Smuzhiyun 	FPU_printall();
525*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
526*4882a593Smuzhiyun #endif /* DEBUG */
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	if (FPU_lookahead && !need_resched()) {
529*4882a593Smuzhiyun 		FPU_ORIG_EIP = FPU_EIP - code_base;
530*4882a593Smuzhiyun 		if (valid_prefix(&byte1, (u_char __user **) & FPU_EIP,
531*4882a593Smuzhiyun 				 &addr_modes.override))
532*4882a593Smuzhiyun 			goto do_another_FPU_instruction;
533*4882a593Smuzhiyun 	}
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun 	if (addr_modes.default_mode)
536*4882a593Smuzhiyun 		FPU_EIP -= code_base;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun /* Support for prefix bytes is not yet complete. To properly handle
542*4882a593Smuzhiyun    all prefix bytes, further changes are needed in the emulator code
543*4882a593Smuzhiyun    which accesses user address space. Access to separate segments is
544*4882a593Smuzhiyun    important for msdos emulation. */
valid_prefix(u_char * Byte,u_char __user ** fpu_eip,overrides * override)545*4882a593Smuzhiyun static int valid_prefix(u_char *Byte, u_char __user **fpu_eip,
546*4882a593Smuzhiyun 			overrides * override)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun 	u_char byte;
549*4882a593Smuzhiyun 	u_char __user *ip = *fpu_eip;
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	*override = (overrides) {
552*4882a593Smuzhiyun 	0, 0, PREFIX_DEFAULT};	/* defaults */
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
555*4882a593Smuzhiyun 	FPU_code_access_ok(1);
556*4882a593Smuzhiyun 	FPU_get_user(byte, ip);
557*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	while (1) {
560*4882a593Smuzhiyun 		switch (byte) {
561*4882a593Smuzhiyun 		case ADDR_SIZE_PREFIX:
562*4882a593Smuzhiyun 			override->address_size = ADDR_SIZE_PREFIX;
563*4882a593Smuzhiyun 			goto do_next_byte;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 		case OP_SIZE_PREFIX:
566*4882a593Smuzhiyun 			override->operand_size = OP_SIZE_PREFIX;
567*4882a593Smuzhiyun 			goto do_next_byte;
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 		case PREFIX_CS:
570*4882a593Smuzhiyun 			override->segment = PREFIX_CS_;
571*4882a593Smuzhiyun 			goto do_next_byte;
572*4882a593Smuzhiyun 		case PREFIX_ES:
573*4882a593Smuzhiyun 			override->segment = PREFIX_ES_;
574*4882a593Smuzhiyun 			goto do_next_byte;
575*4882a593Smuzhiyun 		case PREFIX_SS:
576*4882a593Smuzhiyun 			override->segment = PREFIX_SS_;
577*4882a593Smuzhiyun 			goto do_next_byte;
578*4882a593Smuzhiyun 		case PREFIX_FS:
579*4882a593Smuzhiyun 			override->segment = PREFIX_FS_;
580*4882a593Smuzhiyun 			goto do_next_byte;
581*4882a593Smuzhiyun 		case PREFIX_GS:
582*4882a593Smuzhiyun 			override->segment = PREFIX_GS_;
583*4882a593Smuzhiyun 			goto do_next_byte;
584*4882a593Smuzhiyun 		case PREFIX_DS:
585*4882a593Smuzhiyun 			override->segment = PREFIX_DS_;
586*4882a593Smuzhiyun 			goto do_next_byte;
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun /* lock is not a valid prefix for FPU instructions,
589*4882a593Smuzhiyun    let the cpu handle it to generate a SIGILL. */
590*4882a593Smuzhiyun /*	case PREFIX_LOCK: */
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun 			/* rep.. prefixes have no meaning for FPU instructions */
593*4882a593Smuzhiyun 		case PREFIX_REPE:
594*4882a593Smuzhiyun 		case PREFIX_REPNE:
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun 		      do_next_byte:
597*4882a593Smuzhiyun 			ip++;
598*4882a593Smuzhiyun 			RE_ENTRANT_CHECK_OFF;
599*4882a593Smuzhiyun 			FPU_code_access_ok(1);
600*4882a593Smuzhiyun 			FPU_get_user(byte, ip);
601*4882a593Smuzhiyun 			RE_ENTRANT_CHECK_ON;
602*4882a593Smuzhiyun 			break;
603*4882a593Smuzhiyun 		case FWAIT_OPCODE:
604*4882a593Smuzhiyun 			*Byte = byte;
605*4882a593Smuzhiyun 			return 1;
606*4882a593Smuzhiyun 		default:
607*4882a593Smuzhiyun 			if ((byte & 0xf8) == 0xd8) {
608*4882a593Smuzhiyun 				*Byte = byte;
609*4882a593Smuzhiyun 				*fpu_eip = ip;
610*4882a593Smuzhiyun 				return 1;
611*4882a593Smuzhiyun 			} else {
612*4882a593Smuzhiyun 				/* Not a valid sequence of prefix bytes followed by
613*4882a593Smuzhiyun 				   an FPU instruction. */
614*4882a593Smuzhiyun 				*Byte = byte;	/* Needed for error message. */
615*4882a593Smuzhiyun 				return 0;
616*4882a593Smuzhiyun 			}
617*4882a593Smuzhiyun 		}
618*4882a593Smuzhiyun 	}
619*4882a593Smuzhiyun }
620*4882a593Smuzhiyun 
math_abort(struct math_emu_info * info,unsigned int signal)621*4882a593Smuzhiyun void math_abort(struct math_emu_info *info, unsigned int signal)
622*4882a593Smuzhiyun {
623*4882a593Smuzhiyun 	FPU_EIP = FPU_ORIG_EIP;
624*4882a593Smuzhiyun 	current->thread.trap_nr = X86_TRAP_MF;
625*4882a593Smuzhiyun 	current->thread.error_code = 0;
626*4882a593Smuzhiyun 	send_sig(signal, current, 1);
627*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
628*4882a593Smuzhiyun       __asm__("movl %0,%%esp ; ret": :"g"(((long)info) - 4));
629*4882a593Smuzhiyun #ifdef PARANOID
630*4882a593Smuzhiyun 	printk("ERROR: wm-FPU-emu math_abort failed!\n");
631*4882a593Smuzhiyun #endif /* PARANOID */
632*4882a593Smuzhiyun }
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun #define S387 ((struct swregs_state *)s387)
635*4882a593Smuzhiyun #define sstatus_word() \
636*4882a593Smuzhiyun   ((S387->swd & ~SW_Top & 0xffff) | ((S387->ftop << SW_Top_Shift) & SW_Top))
637*4882a593Smuzhiyun 
fpregs_soft_set(struct task_struct * target,const struct user_regset * regset,unsigned int pos,unsigned int count,const void * kbuf,const void __user * ubuf)638*4882a593Smuzhiyun int fpregs_soft_set(struct task_struct *target,
639*4882a593Smuzhiyun 		    const struct user_regset *regset,
640*4882a593Smuzhiyun 		    unsigned int pos, unsigned int count,
641*4882a593Smuzhiyun 		    const void *kbuf, const void __user *ubuf)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun 	struct swregs_state *s387 = &target->thread.fpu.state.soft;
644*4882a593Smuzhiyun 	void *space = s387->st_space;
645*4882a593Smuzhiyun 	int ret;
646*4882a593Smuzhiyun 	int offset, other, i, tags, regnr, tag, newtop;
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
649*4882a593Smuzhiyun 	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, s387, 0,
650*4882a593Smuzhiyun 				 offsetof(struct swregs_state, st_space));
651*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
652*4882a593Smuzhiyun 
653*4882a593Smuzhiyun 	if (ret)
654*4882a593Smuzhiyun 		return ret;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	S387->ftop = (S387->swd >> SW_Top_Shift) & 7;
657*4882a593Smuzhiyun 	offset = (S387->ftop & 7) * 10;
658*4882a593Smuzhiyun 	other = 80 - offset;
659*4882a593Smuzhiyun 
660*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
661*4882a593Smuzhiyun 
662*4882a593Smuzhiyun 	/* Copy all registers in stack order. */
663*4882a593Smuzhiyun 	ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
664*4882a593Smuzhiyun 				 space + offset, 0, other);
665*4882a593Smuzhiyun 	if (!ret && offset)
666*4882a593Smuzhiyun 		ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
667*4882a593Smuzhiyun 					 space, 0, offset);
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 	/* The tags may need to be corrected now. */
672*4882a593Smuzhiyun 	tags = S387->twd;
673*4882a593Smuzhiyun 	newtop = S387->ftop;
674*4882a593Smuzhiyun 	for (i = 0; i < 8; i++) {
675*4882a593Smuzhiyun 		regnr = (i + newtop) & 7;
676*4882a593Smuzhiyun 		if (((tags >> ((regnr & 7) * 2)) & 3) != TAG_Empty) {
677*4882a593Smuzhiyun 			/* The loaded data over-rides all other cases. */
678*4882a593Smuzhiyun 			tag =
679*4882a593Smuzhiyun 			    FPU_tagof((FPU_REG *) ((u_char *) S387->st_space +
680*4882a593Smuzhiyun 						   10 * regnr));
681*4882a593Smuzhiyun 			tags &= ~(3 << (regnr * 2));
682*4882a593Smuzhiyun 			tags |= (tag & 3) << (regnr * 2);
683*4882a593Smuzhiyun 		}
684*4882a593Smuzhiyun 	}
685*4882a593Smuzhiyun 	S387->twd = tags;
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 	return ret;
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun 
fpregs_soft_get(struct task_struct * target,const struct user_regset * regset,struct membuf to)690*4882a593Smuzhiyun int fpregs_soft_get(struct task_struct *target,
691*4882a593Smuzhiyun 		    const struct user_regset *regset,
692*4882a593Smuzhiyun 		    struct membuf to)
693*4882a593Smuzhiyun {
694*4882a593Smuzhiyun 	struct swregs_state *s387 = &target->thread.fpu.state.soft;
695*4882a593Smuzhiyun 	const void *space = s387->st_space;
696*4882a593Smuzhiyun 	int offset = (S387->ftop & 7) * 10, other = 80 - offset;
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_OFF;
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun #ifdef PECULIAR_486
701*4882a593Smuzhiyun 	S387->cwd &= ~0xe080;
702*4882a593Smuzhiyun 	/* An 80486 sets nearly all of the reserved bits to 1. */
703*4882a593Smuzhiyun 	S387->cwd |= 0xffff0040;
704*4882a593Smuzhiyun 	S387->swd = sstatus_word() | 0xffff0000;
705*4882a593Smuzhiyun 	S387->twd |= 0xffff0000;
706*4882a593Smuzhiyun 	S387->fcs &= ~0xf8000000;
707*4882a593Smuzhiyun 	S387->fos |= 0xffff0000;
708*4882a593Smuzhiyun #endif /* PECULIAR_486 */
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	membuf_write(&to, s387, offsetof(struct swregs_state, st_space));
711*4882a593Smuzhiyun 	membuf_write(&to, space + offset, other);
712*4882a593Smuzhiyun 	membuf_write(&to, space, offset);
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	RE_ENTRANT_CHECK_ON;
715*4882a593Smuzhiyun 
716*4882a593Smuzhiyun 	return 0;
717*4882a593Smuzhiyun }
718