xref: /openbmc/linux/arch/x86/math-emu/get_address.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2da957e11SThomas Gleixner /*---------------------------------------------------------------------------+
3da957e11SThomas Gleixner  |  get_address.c                                                            |
4da957e11SThomas Gleixner  |                                                                           |
5da957e11SThomas Gleixner  | Get the effective address from an FPU instruction.                        |
6da957e11SThomas Gleixner  |                                                                           |
7da957e11SThomas Gleixner  | Copyright (C) 1992,1993,1994,1997                                         |
8da957e11SThomas Gleixner  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
9da957e11SThomas Gleixner  |                       Australia.  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 <linux/stddef.h>
22da957e11SThomas Gleixner 
237c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
24ba3e127eSBrian Gerst #include <asm/vm86.h>
25da957e11SThomas Gleixner 
26da957e11SThomas Gleixner #include "fpu_system.h"
27da957e11SThomas Gleixner #include "exception.h"
28da957e11SThomas Gleixner #include "fpu_emu.h"
29da957e11SThomas Gleixner 
30da957e11SThomas Gleixner #define FPU_WRITE_BIT 0x10
31da957e11SThomas Gleixner 
32da957e11SThomas Gleixner static int reg_offset[] = {
33d315760fSTejun Heo 	offsetof(struct pt_regs, ax),
34d315760fSTejun Heo 	offsetof(struct pt_regs, cx),
35d315760fSTejun Heo 	offsetof(struct pt_regs, dx),
36d315760fSTejun Heo 	offsetof(struct pt_regs, bx),
37d315760fSTejun Heo 	offsetof(struct pt_regs, sp),
38d315760fSTejun Heo 	offsetof(struct pt_regs, bp),
39d315760fSTejun Heo 	offsetof(struct pt_regs, si),
40d315760fSTejun Heo 	offsetof(struct pt_regs, di)
41da957e11SThomas Gleixner };
42da957e11SThomas Gleixner 
43d315760fSTejun Heo #define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
44da957e11SThomas Gleixner 
45da957e11SThomas Gleixner static int reg_offset_vm86[] = {
46d315760fSTejun Heo 	offsetof(struct pt_regs, cs),
47d315760fSTejun Heo 	offsetof(struct kernel_vm86_regs, ds),
48d315760fSTejun Heo 	offsetof(struct kernel_vm86_regs, es),
49d315760fSTejun Heo 	offsetof(struct kernel_vm86_regs, fs),
50d315760fSTejun Heo 	offsetof(struct kernel_vm86_regs, gs),
51d315760fSTejun Heo 	offsetof(struct pt_regs, ss),
52d315760fSTejun Heo 	offsetof(struct kernel_vm86_regs, ds)
53da957e11SThomas Gleixner };
54da957e11SThomas Gleixner 
55da957e11SThomas Gleixner #define VM86_REG_(x) (*(unsigned short *) \
56d315760fSTejun Heo 		(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
57da957e11SThomas Gleixner 
58da957e11SThomas Gleixner static int reg_offset_pm[] = {
59d315760fSTejun Heo 	offsetof(struct pt_regs, cs),
60d315760fSTejun Heo 	offsetof(struct pt_regs, ds),
61d315760fSTejun Heo 	offsetof(struct pt_regs, es),
62d315760fSTejun Heo 	offsetof(struct pt_regs, fs),
63d315760fSTejun Heo 	offsetof(struct pt_regs, ds),	/* dummy, not saved on stack */
64d315760fSTejun Heo 	offsetof(struct pt_regs, ss),
65d315760fSTejun Heo 	offsetof(struct pt_regs, ds)
66da957e11SThomas Gleixner };
67da957e11SThomas Gleixner 
68da957e11SThomas Gleixner #define PM_REG_(x) (*(unsigned short *) \
69d315760fSTejun Heo 		(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
70da957e11SThomas Gleixner 
71da957e11SThomas Gleixner /* Decode the SIB byte. This function assumes mod != 0 */
sib(int mod,unsigned long * fpu_eip)72da957e11SThomas Gleixner static int sib(int mod, unsigned long *fpu_eip)
73da957e11SThomas Gleixner {
74da957e11SThomas Gleixner 	u_char ss, index, base;
75da957e11SThomas Gleixner 	long offset;
76da957e11SThomas Gleixner 
77da957e11SThomas Gleixner 	RE_ENTRANT_CHECK_OFF;
78da957e11SThomas Gleixner 	FPU_code_access_ok(1);
79da957e11SThomas Gleixner 	FPU_get_user(base, (u_char __user *) (*fpu_eip));	/* The SIB byte */
80da957e11SThomas Gleixner 	RE_ENTRANT_CHECK_ON;
81da957e11SThomas Gleixner 	(*fpu_eip)++;
82da957e11SThomas Gleixner 	ss = base >> 6;
83da957e11SThomas Gleixner 	index = (base >> 3) & 7;
84da957e11SThomas Gleixner 	base &= 7;
85da957e11SThomas Gleixner 
86da957e11SThomas Gleixner 	if ((mod == 0) && (base == 5))
87da957e11SThomas Gleixner 		offset = 0;	/* No base register */
88da957e11SThomas Gleixner 	else
89da957e11SThomas Gleixner 		offset = REG_(base);
90da957e11SThomas Gleixner 
913d0d14f9SIngo Molnar 	if (index == 4) {
92da957e11SThomas Gleixner 		/* No index register */
93da957e11SThomas Gleixner 		/* A non-zero ss is illegal */
94da957e11SThomas Gleixner 		if (ss)
95da957e11SThomas Gleixner 			EXCEPTION(EX_Invalid);
963d0d14f9SIngo Molnar 	} else {
97da957e11SThomas Gleixner 		offset += (REG_(index)) << ss;
98da957e11SThomas Gleixner 	}
99da957e11SThomas Gleixner 
1003d0d14f9SIngo Molnar 	if (mod == 1) {
101da957e11SThomas Gleixner 		/* 8 bit signed displacement */
102da957e11SThomas Gleixner 		long displacement;
103da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_OFF;
104da957e11SThomas Gleixner 		FPU_code_access_ok(1);
105da957e11SThomas Gleixner 		FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
106da957e11SThomas Gleixner 		offset += displacement;
107da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_ON;
108da957e11SThomas Gleixner 		(*fpu_eip)++;
1093d0d14f9SIngo Molnar 	} else if (mod == 2 || base == 5) {	/* The second condition also has mod==0 */
110da957e11SThomas Gleixner 		/* 32 bit displacement */
111da957e11SThomas Gleixner 		long displacement;
112da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_OFF;
113da957e11SThomas Gleixner 		FPU_code_access_ok(4);
114da957e11SThomas Gleixner 		FPU_get_user(displacement, (long __user *)(*fpu_eip));
115da957e11SThomas Gleixner 		offset += displacement;
116da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_ON;
117da957e11SThomas Gleixner 		(*fpu_eip) += 4;
118da957e11SThomas Gleixner 	}
119da957e11SThomas Gleixner 
120da957e11SThomas Gleixner 	return offset;
121da957e11SThomas Gleixner }
122da957e11SThomas Gleixner 
vm86_segment(u_char segment,struct address * addr)1233d0d14f9SIngo Molnar static unsigned long vm86_segment(u_char segment, struct address *addr)
124da957e11SThomas Gleixner {
125da957e11SThomas Gleixner 	segment--;
126da957e11SThomas Gleixner #ifdef PARANOID
1273d0d14f9SIngo Molnar 	if (segment > PREFIX_SS_) {
128da957e11SThomas Gleixner 		EXCEPTION(EX_INTERNAL | 0x130);
129da957e11SThomas Gleixner 		math_abort(FPU_info, SIGSEGV);
130da957e11SThomas Gleixner 	}
131da957e11SThomas Gleixner #endif /* PARANOID */
132da957e11SThomas Gleixner 	addr->selector = VM86_REG_(segment);
133da957e11SThomas Gleixner 	return (unsigned long)VM86_REG_(segment) << 4;
134da957e11SThomas Gleixner }
135da957e11SThomas Gleixner 
136da957e11SThomas Gleixner /* This should work for 16 and 32 bit protected mode. */
pm_address(u_char FPU_modrm,u_char segment,struct address * addr,long offset)137da957e11SThomas Gleixner static long pm_address(u_char FPU_modrm, u_char segment,
138da957e11SThomas Gleixner 		       struct address *addr, long offset)
139da957e11SThomas Gleixner {
140da957e11SThomas Gleixner 	struct desc_struct descriptor;
141da957e11SThomas Gleixner 	unsigned long base_address, limit, address, seg_top;
142da957e11SThomas Gleixner 
143da957e11SThomas Gleixner 	segment--;
144da957e11SThomas Gleixner 
145da957e11SThomas Gleixner #ifdef PARANOID
146da957e11SThomas Gleixner 	/* segment is unsigned, so this also detects if segment was 0: */
1473d0d14f9SIngo Molnar 	if (segment > PREFIX_SS_) {
148da957e11SThomas Gleixner 		EXCEPTION(EX_INTERNAL | 0x132);
149da957e11SThomas Gleixner 		math_abort(FPU_info, SIGSEGV);
150da957e11SThomas Gleixner 	}
151da957e11SThomas Gleixner #endif /* PARANOID */
152da957e11SThomas Gleixner 
1533d0d14f9SIngo Molnar 	switch (segment) {
154da957e11SThomas Gleixner 	case PREFIX_GS_ - 1:
155d9a89a26STejun Heo 		/* user gs handling can be lazy, use special accessors */
156*3a24a608SBrian Gerst 		savesegment(gs, addr->selector);
157da957e11SThomas Gleixner 		break;
158da957e11SThomas Gleixner 	default:
159da957e11SThomas Gleixner 		addr->selector = PM_REG_(segment);
160da957e11SThomas Gleixner 	}
161da957e11SThomas Gleixner 
16212e244f4SAndy Lutomirski 	descriptor = FPU_get_ldt_descriptor(addr->selector);
163718f5d00SThomas Gleixner 	base_address = seg_get_base(&descriptor);
164da957e11SThomas Gleixner 	address = base_address + offset;
165718f5d00SThomas Gleixner 	limit = seg_get_limit(&descriptor) + 1;
166718f5d00SThomas Gleixner 	limit *= seg_get_granularity(&descriptor);
167718f5d00SThomas Gleixner 	limit += base_address - 1;
1683d0d14f9SIngo Molnar 	if (limit < base_address)
1693d0d14f9SIngo Molnar 		limit = 0xffffffff;
170da957e11SThomas Gleixner 
171718f5d00SThomas Gleixner 	if (seg_expands_down(&descriptor)) {
172718f5d00SThomas Gleixner 		if (descriptor.g) {
173da957e11SThomas Gleixner 			seg_top = 0xffffffff;
174718f5d00SThomas Gleixner 		} else {
175da957e11SThomas Gleixner 			seg_top = base_address + (1 << 20);
1763d0d14f9SIngo Molnar 			if (seg_top < base_address)
1773d0d14f9SIngo Molnar 				seg_top = 0xffffffff;
178da957e11SThomas Gleixner 		}
179da957e11SThomas Gleixner 		access_limit =
180da957e11SThomas Gleixner 		    (address <= limit) || (address >= seg_top) ? 0 :
181da957e11SThomas Gleixner 		    ((seg_top - address) >= 255 ? 255 : seg_top - address);
1823d0d14f9SIngo Molnar 	} else {
183da957e11SThomas Gleixner 		access_limit =
184da957e11SThomas Gleixner 		    (address > limit) || (address < base_address) ? 0 :
185da957e11SThomas Gleixner 		    ((limit - address) >= 254 ? 255 : limit - address + 1);
186da957e11SThomas Gleixner 	}
187718f5d00SThomas Gleixner 	if (seg_execute_only(&descriptor) ||
188718f5d00SThomas Gleixner 	    (!seg_writable(&descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
189da957e11SThomas Gleixner 		access_limit = 0;
190da957e11SThomas Gleixner 	}
191da957e11SThomas Gleixner 	return address;
192da957e11SThomas Gleixner }
193da957e11SThomas Gleixner 
194da957e11SThomas Gleixner /*
195da957e11SThomas Gleixner        MOD R/M byte:  MOD == 3 has a special use for the FPU
196da957e11SThomas Gleixner                       SIB byte used iff R/M = 100b
197da957e11SThomas Gleixner 
198da957e11SThomas Gleixner        7   6   5   4   3   2   1   0
199da957e11SThomas Gleixner        .....   .........   .........
200da957e11SThomas Gleixner         MOD    OPCODE(2)     R/M
201da957e11SThomas Gleixner 
202da957e11SThomas Gleixner        SIB byte
203da957e11SThomas Gleixner 
204da957e11SThomas Gleixner        7   6   5   4   3   2   1   0
205da957e11SThomas Gleixner        .....   .........   .........
206da957e11SThomas Gleixner         SS      INDEX        BASE
207da957e11SThomas Gleixner 
208da957e11SThomas Gleixner */
209da957e11SThomas Gleixner 
FPU_get_address(u_char FPU_modrm,unsigned long * fpu_eip,struct address * addr,fpu_addr_modes addr_modes)210da957e11SThomas Gleixner void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
2113d0d14f9SIngo Molnar 			     struct address *addr, fpu_addr_modes addr_modes)
212da957e11SThomas Gleixner {
213da957e11SThomas Gleixner 	u_char mod;
214da957e11SThomas Gleixner 	unsigned rm = FPU_modrm & 7;
215da957e11SThomas Gleixner 	long *cpu_reg_ptr;
216da957e11SThomas Gleixner 	int address = 0;	/* Initialized just to stop compiler warnings. */
217da957e11SThomas Gleixner 
218da957e11SThomas Gleixner 	/* Memory accessed via the cs selector is write protected
219da957e11SThomas Gleixner 	   in `non-segmented' 32 bit protected mode. */
220da957e11SThomas Gleixner 	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
2213d0d14f9SIngo Molnar 	    && (addr_modes.override.segment == PREFIX_CS_)) {
222da957e11SThomas Gleixner 		math_abort(FPU_info, SIGSEGV);
223da957e11SThomas Gleixner 	}
224da957e11SThomas Gleixner 
225da957e11SThomas Gleixner 	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
226da957e11SThomas Gleixner 
227da957e11SThomas Gleixner 	mod = (FPU_modrm >> 6) & 3;
228da957e11SThomas Gleixner 
2293d0d14f9SIngo Molnar 	if (rm == 4 && mod != 3) {
230da957e11SThomas Gleixner 		address = sib(mod, fpu_eip);
2313d0d14f9SIngo Molnar 	} else {
232da957e11SThomas Gleixner 		cpu_reg_ptr = &REG_(rm);
2333d0d14f9SIngo Molnar 		switch (mod) {
234da957e11SThomas Gleixner 		case 0:
2353d0d14f9SIngo Molnar 			if (rm == 5) {
236da957e11SThomas Gleixner 				/* Special case: disp32 */
237da957e11SThomas Gleixner 				RE_ENTRANT_CHECK_OFF;
238da957e11SThomas Gleixner 				FPU_code_access_ok(4);
2393d0d14f9SIngo Molnar 				FPU_get_user(address,
2403d0d14f9SIngo Molnar 					     (unsigned long __user
2413d0d14f9SIngo Molnar 					      *)(*fpu_eip));
242da957e11SThomas Gleixner 				(*fpu_eip) += 4;
243da957e11SThomas Gleixner 				RE_ENTRANT_CHECK_ON;
244da957e11SThomas Gleixner 				addr->offset = address;
245da957e11SThomas Gleixner 				return (void __user *)address;
2463d0d14f9SIngo Molnar 			} else {
247da957e11SThomas Gleixner 				address = *cpu_reg_ptr;	/* Just return the contents
248da957e11SThomas Gleixner 							   of the cpu register */
249da957e11SThomas Gleixner 				addr->offset = address;
250da957e11SThomas Gleixner 				return (void __user *)address;
251da957e11SThomas Gleixner 			}
252da957e11SThomas Gleixner 		case 1:
253da957e11SThomas Gleixner 			/* 8 bit signed displacement */
254da957e11SThomas Gleixner 			RE_ENTRANT_CHECK_OFF;
255da957e11SThomas Gleixner 			FPU_code_access_ok(1);
256da957e11SThomas Gleixner 			FPU_get_user(address, (signed char __user *)(*fpu_eip));
257da957e11SThomas Gleixner 			RE_ENTRANT_CHECK_ON;
258da957e11SThomas Gleixner 			(*fpu_eip)++;
259da957e11SThomas Gleixner 			break;
260da957e11SThomas Gleixner 		case 2:
261da957e11SThomas Gleixner 			/* 32 bit displacement */
262da957e11SThomas Gleixner 			RE_ENTRANT_CHECK_OFF;
263da957e11SThomas Gleixner 			FPU_code_access_ok(4);
264da957e11SThomas Gleixner 			FPU_get_user(address, (long __user *)(*fpu_eip));
265da957e11SThomas Gleixner 			(*fpu_eip) += 4;
266da957e11SThomas Gleixner 			RE_ENTRANT_CHECK_ON;
267da957e11SThomas Gleixner 			break;
268da957e11SThomas Gleixner 		case 3:
269da957e11SThomas Gleixner 			/* Not legal for the FPU */
270da957e11SThomas Gleixner 			EXCEPTION(EX_Invalid);
271da957e11SThomas Gleixner 		}
272da957e11SThomas Gleixner 		address += *cpu_reg_ptr;
273da957e11SThomas Gleixner 	}
274da957e11SThomas Gleixner 
275da957e11SThomas Gleixner 	addr->offset = address;
276da957e11SThomas Gleixner 
2773d0d14f9SIngo Molnar 	switch (addr_modes.default_mode) {
278da957e11SThomas Gleixner 	case 0:
279da957e11SThomas Gleixner 		break;
280da957e11SThomas Gleixner 	case VM86:
281da957e11SThomas Gleixner 		address += vm86_segment(addr_modes.override.segment, addr);
282da957e11SThomas Gleixner 		break;
283da957e11SThomas Gleixner 	case PM16:
284da957e11SThomas Gleixner 	case SEG32:
285da957e11SThomas Gleixner 		address = pm_address(FPU_modrm, addr_modes.override.segment,
286da957e11SThomas Gleixner 				     addr, address);
287da957e11SThomas Gleixner 		break;
288da957e11SThomas Gleixner 	default:
289da957e11SThomas Gleixner 		EXCEPTION(EX_INTERNAL | 0x133);
290da957e11SThomas Gleixner 	}
291da957e11SThomas Gleixner 
292da957e11SThomas Gleixner 	return (void __user *)address;
293da957e11SThomas Gleixner }
294da957e11SThomas Gleixner 
FPU_get_address_16(u_char FPU_modrm,unsigned long * fpu_eip,struct address * addr,fpu_addr_modes addr_modes)295da957e11SThomas Gleixner void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
2963d0d14f9SIngo Molnar 				struct address *addr, fpu_addr_modes addr_modes)
297da957e11SThomas Gleixner {
298da957e11SThomas Gleixner 	u_char mod;
299da957e11SThomas Gleixner 	unsigned rm = FPU_modrm & 7;
300da957e11SThomas Gleixner 	int address = 0;	/* Default used for mod == 0 */
301da957e11SThomas Gleixner 
302da957e11SThomas Gleixner 	/* Memory accessed via the cs selector is write protected
303da957e11SThomas Gleixner 	   in `non-segmented' 32 bit protected mode. */
304da957e11SThomas Gleixner 	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
3053d0d14f9SIngo Molnar 	    && (addr_modes.override.segment == PREFIX_CS_)) {
306da957e11SThomas Gleixner 		math_abort(FPU_info, SIGSEGV);
307da957e11SThomas Gleixner 	}
308da957e11SThomas Gleixner 
309da957e11SThomas Gleixner 	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
310da957e11SThomas Gleixner 
311da957e11SThomas Gleixner 	mod = (FPU_modrm >> 6) & 3;
312da957e11SThomas Gleixner 
3133d0d14f9SIngo Molnar 	switch (mod) {
314da957e11SThomas Gleixner 	case 0:
3153d0d14f9SIngo Molnar 		if (rm == 6) {
316da957e11SThomas Gleixner 			/* Special case: disp16 */
317da957e11SThomas Gleixner 			RE_ENTRANT_CHECK_OFF;
318da957e11SThomas Gleixner 			FPU_code_access_ok(2);
3193d0d14f9SIngo Molnar 			FPU_get_user(address,
3203d0d14f9SIngo Molnar 				     (unsigned short __user *)(*fpu_eip));
321da957e11SThomas Gleixner 			(*fpu_eip) += 2;
322da957e11SThomas Gleixner 			RE_ENTRANT_CHECK_ON;
323da957e11SThomas Gleixner 			goto add_segment;
324da957e11SThomas Gleixner 		}
325da957e11SThomas Gleixner 		break;
326da957e11SThomas Gleixner 	case 1:
327da957e11SThomas Gleixner 		/* 8 bit signed displacement */
328da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_OFF;
329da957e11SThomas Gleixner 		FPU_code_access_ok(1);
330da957e11SThomas Gleixner 		FPU_get_user(address, (signed char __user *)(*fpu_eip));
331da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_ON;
332da957e11SThomas Gleixner 		(*fpu_eip)++;
333da957e11SThomas Gleixner 		break;
334da957e11SThomas Gleixner 	case 2:
335da957e11SThomas Gleixner 		/* 16 bit displacement */
336da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_OFF;
337da957e11SThomas Gleixner 		FPU_code_access_ok(2);
338da957e11SThomas Gleixner 		FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
339da957e11SThomas Gleixner 		(*fpu_eip) += 2;
340da957e11SThomas Gleixner 		RE_ENTRANT_CHECK_ON;
341da957e11SThomas Gleixner 		break;
342da957e11SThomas Gleixner 	case 3:
343da957e11SThomas Gleixner 		/* Not legal for the FPU */
344da957e11SThomas Gleixner 		EXCEPTION(EX_Invalid);
345da957e11SThomas Gleixner 		break;
346da957e11SThomas Gleixner 	}
3473d0d14f9SIngo Molnar 	switch (rm) {
348da957e11SThomas Gleixner 	case 0:
349d315760fSTejun Heo 		address += FPU_info->regs->bx + FPU_info->regs->si;
350da957e11SThomas Gleixner 		break;
351da957e11SThomas Gleixner 	case 1:
352d315760fSTejun Heo 		address += FPU_info->regs->bx + FPU_info->regs->di;
353da957e11SThomas Gleixner 		break;
354da957e11SThomas Gleixner 	case 2:
355d315760fSTejun Heo 		address += FPU_info->regs->bp + FPU_info->regs->si;
356da957e11SThomas Gleixner 		if (addr_modes.override.segment == PREFIX_DEFAULT)
357da957e11SThomas Gleixner 			addr_modes.override.segment = PREFIX_SS_;
358da957e11SThomas Gleixner 		break;
359da957e11SThomas Gleixner 	case 3:
360d315760fSTejun Heo 		address += FPU_info->regs->bp + FPU_info->regs->di;
361da957e11SThomas Gleixner 		if (addr_modes.override.segment == PREFIX_DEFAULT)
362da957e11SThomas Gleixner 			addr_modes.override.segment = PREFIX_SS_;
363da957e11SThomas Gleixner 		break;
364da957e11SThomas Gleixner 	case 4:
365d315760fSTejun Heo 		address += FPU_info->regs->si;
366da957e11SThomas Gleixner 		break;
367da957e11SThomas Gleixner 	case 5:
368d315760fSTejun Heo 		address += FPU_info->regs->di;
369da957e11SThomas Gleixner 		break;
370da957e11SThomas Gleixner 	case 6:
371d315760fSTejun Heo 		address += FPU_info->regs->bp;
372da957e11SThomas Gleixner 		if (addr_modes.override.segment == PREFIX_DEFAULT)
373da957e11SThomas Gleixner 			addr_modes.override.segment = PREFIX_SS_;
374da957e11SThomas Gleixner 		break;
375da957e11SThomas Gleixner 	case 7:
376d315760fSTejun Heo 		address += FPU_info->regs->bx;
377da957e11SThomas Gleixner 		break;
378da957e11SThomas Gleixner 	}
379da957e11SThomas Gleixner 
380da957e11SThomas Gleixner       add_segment:
381da957e11SThomas Gleixner 	address &= 0xffff;
382da957e11SThomas Gleixner 
383da957e11SThomas Gleixner 	addr->offset = address;
384da957e11SThomas Gleixner 
3853d0d14f9SIngo Molnar 	switch (addr_modes.default_mode) {
386da957e11SThomas Gleixner 	case 0:
387da957e11SThomas Gleixner 		break;
388da957e11SThomas Gleixner 	case VM86:
389da957e11SThomas Gleixner 		address += vm86_segment(addr_modes.override.segment, addr);
390da957e11SThomas Gleixner 		break;
391da957e11SThomas Gleixner 	case PM16:
392da957e11SThomas Gleixner 	case SEG32:
393da957e11SThomas Gleixner 		address = pm_address(FPU_modrm, addr_modes.override.segment,
394da957e11SThomas Gleixner 				     addr, address);
395da957e11SThomas Gleixner 		break;
396da957e11SThomas Gleixner 	default:
397da957e11SThomas Gleixner 		EXCEPTION(EX_INTERNAL | 0x131);
398da957e11SThomas Gleixner 	}
399da957e11SThomas Gleixner 
400da957e11SThomas Gleixner 	return (void __user *)address;
401da957e11SThomas Gleixner }
402