Linux Audio

Check our new training course

Loading...
v6.2
  1// SPDX-License-Identifier: GPL-2.0
  2/*---------------------------------------------------------------------------+
  3 |  get_address.c                                                            |
  4 |                                                                           |
  5 | Get the effective address from an FPU instruction.                        |
  6 |                                                                           |
  7 | Copyright (C) 1992,1993,1994,1997                                         |
  8 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  9 |                       Australia.  E-mail   billm@suburbia.net             |
 10 |                                                                           |
 11 |                                                                           |
 12 +---------------------------------------------------------------------------*/
 13
 14/*---------------------------------------------------------------------------+
 15 | Note:                                                                     |
 16 |    The file contains code which accesses user memory.                     |
 17 |    Emulator static data may change when user memory is accessed, due to   |
 18 |    other processes using the emulator while swapping is in progress.      |
 19 +---------------------------------------------------------------------------*/
 20
 21#include <linux/stddef.h>
 22
 23#include <linux/uaccess.h>
 24#include <asm/vm86.h>
 25
 26#include "fpu_system.h"
 27#include "exception.h"
 28#include "fpu_emu.h"
 29
 30#define FPU_WRITE_BIT 0x10
 31
 32static int reg_offset[] = {
 33	offsetof(struct pt_regs, ax),
 34	offsetof(struct pt_regs, cx),
 35	offsetof(struct pt_regs, dx),
 36	offsetof(struct pt_regs, bx),
 37	offsetof(struct pt_regs, sp),
 38	offsetof(struct pt_regs, bp),
 39	offsetof(struct pt_regs, si),
 40	offsetof(struct pt_regs, di)
 41};
 42
 43#define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
 44
 45static int reg_offset_vm86[] = {
 46	offsetof(struct pt_regs, cs),
 47	offsetof(struct kernel_vm86_regs, ds),
 48	offsetof(struct kernel_vm86_regs, es),
 49	offsetof(struct kernel_vm86_regs, fs),
 50	offsetof(struct kernel_vm86_regs, gs),
 51	offsetof(struct pt_regs, ss),
 52	offsetof(struct kernel_vm86_regs, ds)
 53};
 54
 55#define VM86_REG_(x) (*(unsigned short *) \
 56		(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
 57
 58static int reg_offset_pm[] = {
 59	offsetof(struct pt_regs, cs),
 60	offsetof(struct pt_regs, ds),
 61	offsetof(struct pt_regs, es),
 62	offsetof(struct pt_regs, fs),
 63	offsetof(struct pt_regs, ds),	/* dummy, not saved on stack */
 64	offsetof(struct pt_regs, ss),
 65	offsetof(struct pt_regs, ds)
 66};
 67
 68#define PM_REG_(x) (*(unsigned short *) \
 69		(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
 70
 71/* Decode the SIB byte. This function assumes mod != 0 */
 72static int sib(int mod, unsigned long *fpu_eip)
 73{
 74	u_char ss, index, base;
 75	long offset;
 76
 77	RE_ENTRANT_CHECK_OFF;
 78	FPU_code_access_ok(1);
 79	FPU_get_user(base, (u_char __user *) (*fpu_eip));	/* The SIB byte */
 80	RE_ENTRANT_CHECK_ON;
 81	(*fpu_eip)++;
 82	ss = base >> 6;
 83	index = (base >> 3) & 7;
 84	base &= 7;
 85
 86	if ((mod == 0) && (base == 5))
 87		offset = 0;	/* No base register */
 88	else
 89		offset = REG_(base);
 90
 91	if (index == 4) {
 92		/* No index register */
 93		/* A non-zero ss is illegal */
 94		if (ss)
 95			EXCEPTION(EX_Invalid);
 96	} else {
 97		offset += (REG_(index)) << ss;
 98	}
 99
100	if (mod == 1) {
101		/* 8 bit signed displacement */
102		long displacement;
103		RE_ENTRANT_CHECK_OFF;
104		FPU_code_access_ok(1);
105		FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
106		offset += displacement;
107		RE_ENTRANT_CHECK_ON;
108		(*fpu_eip)++;
109	} else if (mod == 2 || base == 5) {	/* The second condition also has mod==0 */
110		/* 32 bit displacement */
111		long displacement;
112		RE_ENTRANT_CHECK_OFF;
113		FPU_code_access_ok(4);
114		FPU_get_user(displacement, (long __user *)(*fpu_eip));
115		offset += displacement;
116		RE_ENTRANT_CHECK_ON;
117		(*fpu_eip) += 4;
118	}
119
120	return offset;
121}
122
123static unsigned long vm86_segment(u_char segment, struct address *addr)
124{
125	segment--;
126#ifdef PARANOID
127	if (segment > PREFIX_SS_) {
128		EXCEPTION(EX_INTERNAL | 0x130);
129		math_abort(FPU_info, SIGSEGV);
130	}
131#endif /* PARANOID */
132	addr->selector = VM86_REG_(segment);
133	return (unsigned long)VM86_REG_(segment) << 4;
134}
135
136/* This should work for 16 and 32 bit protected mode. */
137static long pm_address(u_char FPU_modrm, u_char segment,
138		       struct address *addr, long offset)
139{
140	struct desc_struct descriptor;
141	unsigned long base_address, limit, address, seg_top;
142
143	segment--;
144
145#ifdef PARANOID
146	/* segment is unsigned, so this also detects if segment was 0: */
147	if (segment > PREFIX_SS_) {
148		EXCEPTION(EX_INTERNAL | 0x132);
149		math_abort(FPU_info, SIGSEGV);
150	}
151#endif /* PARANOID */
152
153	switch (segment) {
154	case PREFIX_GS_ - 1:
155		/* user gs handling can be lazy, use special accessors */
156		savesegment(gs, addr->selector);
157		break;
158	default:
159		addr->selector = PM_REG_(segment);
160	}
161
162	descriptor = FPU_get_ldt_descriptor(addr->selector);
163	base_address = seg_get_base(&descriptor);
164	address = base_address + offset;
165	limit = seg_get_limit(&descriptor) + 1;
166	limit *= seg_get_granularity(&descriptor);
167	limit += base_address - 1;
168	if (limit < base_address)
169		limit = 0xffffffff;
170
171	if (seg_expands_down(&descriptor)) {
172		if (descriptor.g) {
173			seg_top = 0xffffffff;
174		} else {
175			seg_top = base_address + (1 << 20);
176			if (seg_top < base_address)
177				seg_top = 0xffffffff;
178		}
179		access_limit =
180		    (address <= limit) || (address >= seg_top) ? 0 :
181		    ((seg_top - address) >= 255 ? 255 : seg_top - address);
182	} else {
183		access_limit =
184		    (address > limit) || (address < base_address) ? 0 :
185		    ((limit - address) >= 254 ? 255 : limit - address + 1);
186	}
187	if (seg_execute_only(&descriptor) ||
188	    (!seg_writable(&descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
189		access_limit = 0;
190	}
191	return address;
192}
193
194/*
195       MOD R/M byte:  MOD == 3 has a special use for the FPU
196                      SIB byte used iff R/M = 100b
197
198       7   6   5   4   3   2   1   0
199       .....   .........   .........
200        MOD    OPCODE(2)     R/M
201
202       SIB byte
203
204       7   6   5   4   3   2   1   0
205       .....   .........   .........
206        SS      INDEX        BASE
207
208*/
209
210void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
211			     struct address *addr, fpu_addr_modes addr_modes)
212{
213	u_char mod;
214	unsigned rm = FPU_modrm & 7;
215	long *cpu_reg_ptr;
216	int address = 0;	/* Initialized just to stop compiler warnings. */
217
218	/* Memory accessed via the cs selector is write protected
219	   in `non-segmented' 32 bit protected mode. */
220	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
221	    && (addr_modes.override.segment == PREFIX_CS_)) {
222		math_abort(FPU_info, SIGSEGV);
223	}
224
225	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
226
227	mod = (FPU_modrm >> 6) & 3;
228
229	if (rm == 4 && mod != 3) {
230		address = sib(mod, fpu_eip);
231	} else {
232		cpu_reg_ptr = &REG_(rm);
233		switch (mod) {
234		case 0:
235			if (rm == 5) {
236				/* Special case: disp32 */
237				RE_ENTRANT_CHECK_OFF;
238				FPU_code_access_ok(4);
239				FPU_get_user(address,
240					     (unsigned long __user
241					      *)(*fpu_eip));
242				(*fpu_eip) += 4;
243				RE_ENTRANT_CHECK_ON;
244				addr->offset = address;
245				return (void __user *)address;
246			} else {
247				address = *cpu_reg_ptr;	/* Just return the contents
248							   of the cpu register */
249				addr->offset = address;
250				return (void __user *)address;
251			}
252		case 1:
253			/* 8 bit signed displacement */
254			RE_ENTRANT_CHECK_OFF;
255			FPU_code_access_ok(1);
256			FPU_get_user(address, (signed char __user *)(*fpu_eip));
257			RE_ENTRANT_CHECK_ON;
258			(*fpu_eip)++;
259			break;
260		case 2:
261			/* 32 bit displacement */
262			RE_ENTRANT_CHECK_OFF;
263			FPU_code_access_ok(4);
264			FPU_get_user(address, (long __user *)(*fpu_eip));
265			(*fpu_eip) += 4;
266			RE_ENTRANT_CHECK_ON;
267			break;
268		case 3:
269			/* Not legal for the FPU */
270			EXCEPTION(EX_Invalid);
271		}
272		address += *cpu_reg_ptr;
273	}
274
275	addr->offset = address;
276
277	switch (addr_modes.default_mode) {
278	case 0:
279		break;
280	case VM86:
281		address += vm86_segment(addr_modes.override.segment, addr);
282		break;
283	case PM16:
284	case SEG32:
285		address = pm_address(FPU_modrm, addr_modes.override.segment,
286				     addr, address);
287		break;
288	default:
289		EXCEPTION(EX_INTERNAL | 0x133);
290	}
291
292	return (void __user *)address;
293}
294
295void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
296				struct address *addr, fpu_addr_modes addr_modes)
297{
298	u_char mod;
299	unsigned rm = FPU_modrm & 7;
300	int address = 0;	/* Default used for mod == 0 */
301
302	/* Memory accessed via the cs selector is write protected
303	   in `non-segmented' 32 bit protected mode. */
304	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
305	    && (addr_modes.override.segment == PREFIX_CS_)) {
306		math_abort(FPU_info, SIGSEGV);
307	}
308
309	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
310
311	mod = (FPU_modrm >> 6) & 3;
312
313	switch (mod) {
314	case 0:
315		if (rm == 6) {
316			/* Special case: disp16 */
317			RE_ENTRANT_CHECK_OFF;
318			FPU_code_access_ok(2);
319			FPU_get_user(address,
320				     (unsigned short __user *)(*fpu_eip));
321			(*fpu_eip) += 2;
322			RE_ENTRANT_CHECK_ON;
323			goto add_segment;
324		}
325		break;
326	case 1:
327		/* 8 bit signed displacement */
328		RE_ENTRANT_CHECK_OFF;
329		FPU_code_access_ok(1);
330		FPU_get_user(address, (signed char __user *)(*fpu_eip));
331		RE_ENTRANT_CHECK_ON;
332		(*fpu_eip)++;
333		break;
334	case 2:
335		/* 16 bit displacement */
336		RE_ENTRANT_CHECK_OFF;
337		FPU_code_access_ok(2);
338		FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
339		(*fpu_eip) += 2;
340		RE_ENTRANT_CHECK_ON;
341		break;
342	case 3:
343		/* Not legal for the FPU */
344		EXCEPTION(EX_Invalid);
345		break;
346	}
347	switch (rm) {
348	case 0:
349		address += FPU_info->regs->bx + FPU_info->regs->si;
350		break;
351	case 1:
352		address += FPU_info->regs->bx + FPU_info->regs->di;
353		break;
354	case 2:
355		address += FPU_info->regs->bp + FPU_info->regs->si;
356		if (addr_modes.override.segment == PREFIX_DEFAULT)
357			addr_modes.override.segment = PREFIX_SS_;
358		break;
359	case 3:
360		address += FPU_info->regs->bp + FPU_info->regs->di;
361		if (addr_modes.override.segment == PREFIX_DEFAULT)
362			addr_modes.override.segment = PREFIX_SS_;
363		break;
364	case 4:
365		address += FPU_info->regs->si;
366		break;
367	case 5:
368		address += FPU_info->regs->di;
369		break;
370	case 6:
371		address += FPU_info->regs->bp;
372		if (addr_modes.override.segment == PREFIX_DEFAULT)
373			addr_modes.override.segment = PREFIX_SS_;
374		break;
375	case 7:
376		address += FPU_info->regs->bx;
377		break;
378	}
379
380      add_segment:
381	address &= 0xffff;
382
383	addr->offset = address;
384
385	switch (addr_modes.default_mode) {
386	case 0:
387		break;
388	case VM86:
389		address += vm86_segment(addr_modes.override.segment, addr);
390		break;
391	case PM16:
392	case SEG32:
393		address = pm_address(FPU_modrm, addr_modes.override.segment,
394				     addr, address);
395		break;
396	default:
397		EXCEPTION(EX_INTERNAL | 0x131);
398	}
399
400	return (void __user *)address;
401}
v3.5.6
 
  1/*---------------------------------------------------------------------------+
  2 |  get_address.c                                                            |
  3 |                                                                           |
  4 | Get the effective address from an FPU instruction.                        |
  5 |                                                                           |
  6 | Copyright (C) 1992,1993,1994,1997                                         |
  7 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  8 |                       Australia.  E-mail   billm@suburbia.net             |
  9 |                                                                           |
 10 |                                                                           |
 11 +---------------------------------------------------------------------------*/
 12
 13/*---------------------------------------------------------------------------+
 14 | Note:                                                                     |
 15 |    The file contains code which accesses user memory.                     |
 16 |    Emulator static data may change when user memory is accessed, due to   |
 17 |    other processes using the emulator while swapping is in progress.      |
 18 +---------------------------------------------------------------------------*/
 19
 20#include <linux/stddef.h>
 21
 22#include <asm/uaccess.h>
 23#include <asm/desc.h>
 24
 25#include "fpu_system.h"
 26#include "exception.h"
 27#include "fpu_emu.h"
 28
 29#define FPU_WRITE_BIT 0x10
 30
 31static int reg_offset[] = {
 32	offsetof(struct pt_regs, ax),
 33	offsetof(struct pt_regs, cx),
 34	offsetof(struct pt_regs, dx),
 35	offsetof(struct pt_regs, bx),
 36	offsetof(struct pt_regs, sp),
 37	offsetof(struct pt_regs, bp),
 38	offsetof(struct pt_regs, si),
 39	offsetof(struct pt_regs, di)
 40};
 41
 42#define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
 43
 44static int reg_offset_vm86[] = {
 45	offsetof(struct pt_regs, cs),
 46	offsetof(struct kernel_vm86_regs, ds),
 47	offsetof(struct kernel_vm86_regs, es),
 48	offsetof(struct kernel_vm86_regs, fs),
 49	offsetof(struct kernel_vm86_regs, gs),
 50	offsetof(struct pt_regs, ss),
 51	offsetof(struct kernel_vm86_regs, ds)
 52};
 53
 54#define VM86_REG_(x) (*(unsigned short *) \
 55		(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
 56
 57static int reg_offset_pm[] = {
 58	offsetof(struct pt_regs, cs),
 59	offsetof(struct pt_regs, ds),
 60	offsetof(struct pt_regs, es),
 61	offsetof(struct pt_regs, fs),
 62	offsetof(struct pt_regs, ds),	/* dummy, not saved on stack */
 63	offsetof(struct pt_regs, ss),
 64	offsetof(struct pt_regs, ds)
 65};
 66
 67#define PM_REG_(x) (*(unsigned short *) \
 68		(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
 69
 70/* Decode the SIB byte. This function assumes mod != 0 */
 71static int sib(int mod, unsigned long *fpu_eip)
 72{
 73	u_char ss, index, base;
 74	long offset;
 75
 76	RE_ENTRANT_CHECK_OFF;
 77	FPU_code_access_ok(1);
 78	FPU_get_user(base, (u_char __user *) (*fpu_eip));	/* The SIB byte */
 79	RE_ENTRANT_CHECK_ON;
 80	(*fpu_eip)++;
 81	ss = base >> 6;
 82	index = (base >> 3) & 7;
 83	base &= 7;
 84
 85	if ((mod == 0) && (base == 5))
 86		offset = 0;	/* No base register */
 87	else
 88		offset = REG_(base);
 89
 90	if (index == 4) {
 91		/* No index register */
 92		/* A non-zero ss is illegal */
 93		if (ss)
 94			EXCEPTION(EX_Invalid);
 95	} else {
 96		offset += (REG_(index)) << ss;
 97	}
 98
 99	if (mod == 1) {
100		/* 8 bit signed displacement */
101		long displacement;
102		RE_ENTRANT_CHECK_OFF;
103		FPU_code_access_ok(1);
104		FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
105		offset += displacement;
106		RE_ENTRANT_CHECK_ON;
107		(*fpu_eip)++;
108	} else if (mod == 2 || base == 5) {	/* The second condition also has mod==0 */
109		/* 32 bit displacement */
110		long displacement;
111		RE_ENTRANT_CHECK_OFF;
112		FPU_code_access_ok(4);
113		FPU_get_user(displacement, (long __user *)(*fpu_eip));
114		offset += displacement;
115		RE_ENTRANT_CHECK_ON;
116		(*fpu_eip) += 4;
117	}
118
119	return offset;
120}
121
122static unsigned long vm86_segment(u_char segment, struct address *addr)
123{
124	segment--;
125#ifdef PARANOID
126	if (segment > PREFIX_SS_) {
127		EXCEPTION(EX_INTERNAL | 0x130);
128		math_abort(FPU_info, SIGSEGV);
129	}
130#endif /* PARANOID */
131	addr->selector = VM86_REG_(segment);
132	return (unsigned long)VM86_REG_(segment) << 4;
133}
134
135/* This should work for 16 and 32 bit protected mode. */
136static long pm_address(u_char FPU_modrm, u_char segment,
137		       struct address *addr, long offset)
138{
139	struct desc_struct descriptor;
140	unsigned long base_address, limit, address, seg_top;
141
142	segment--;
143
144#ifdef PARANOID
145	/* segment is unsigned, so this also detects if segment was 0: */
146	if (segment > PREFIX_SS_) {
147		EXCEPTION(EX_INTERNAL | 0x132);
148		math_abort(FPU_info, SIGSEGV);
149	}
150#endif /* PARANOID */
151
152	switch (segment) {
153	case PREFIX_GS_ - 1:
154		/* user gs handling can be lazy, use special accessors */
155		addr->selector = get_user_gs(FPU_info->regs);
156		break;
157	default:
158		addr->selector = PM_REG_(segment);
159	}
160
161	descriptor = LDT_DESCRIPTOR(PM_REG_(segment));
162	base_address = SEG_BASE_ADDR(descriptor);
163	address = base_address + offset;
164	limit = base_address
165	    + (SEG_LIMIT(descriptor) + 1) * SEG_GRANULARITY(descriptor) - 1;
 
166	if (limit < base_address)
167		limit = 0xffffffff;
168
169	if (SEG_EXPAND_DOWN(descriptor)) {
170		if (SEG_G_BIT(descriptor))
171			seg_top = 0xffffffff;
172		else {
173			seg_top = base_address + (1 << 20);
174			if (seg_top < base_address)
175				seg_top = 0xffffffff;
176		}
177		access_limit =
178		    (address <= limit) || (address >= seg_top) ? 0 :
179		    ((seg_top - address) >= 255 ? 255 : seg_top - address);
180	} else {
181		access_limit =
182		    (address > limit) || (address < base_address) ? 0 :
183		    ((limit - address) >= 254 ? 255 : limit - address + 1);
184	}
185	if (SEG_EXECUTE_ONLY(descriptor) ||
186	    (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
187		access_limit = 0;
188	}
189	return address;
190}
191
192/*
193       MOD R/M byte:  MOD == 3 has a special use for the FPU
194                      SIB byte used iff R/M = 100b
195
196       7   6   5   4   3   2   1   0
197       .....   .........   .........
198        MOD    OPCODE(2)     R/M
199
200       SIB byte
201
202       7   6   5   4   3   2   1   0
203       .....   .........   .........
204        SS      INDEX        BASE
205
206*/
207
208void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
209			     struct address *addr, fpu_addr_modes addr_modes)
210{
211	u_char mod;
212	unsigned rm = FPU_modrm & 7;
213	long *cpu_reg_ptr;
214	int address = 0;	/* Initialized just to stop compiler warnings. */
215
216	/* Memory accessed via the cs selector is write protected
217	   in `non-segmented' 32 bit protected mode. */
218	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
219	    && (addr_modes.override.segment == PREFIX_CS_)) {
220		math_abort(FPU_info, SIGSEGV);
221	}
222
223	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
224
225	mod = (FPU_modrm >> 6) & 3;
226
227	if (rm == 4 && mod != 3) {
228		address = sib(mod, fpu_eip);
229	} else {
230		cpu_reg_ptr = &REG_(rm);
231		switch (mod) {
232		case 0:
233			if (rm == 5) {
234				/* Special case: disp32 */
235				RE_ENTRANT_CHECK_OFF;
236				FPU_code_access_ok(4);
237				FPU_get_user(address,
238					     (unsigned long __user
239					      *)(*fpu_eip));
240				(*fpu_eip) += 4;
241				RE_ENTRANT_CHECK_ON;
242				addr->offset = address;
243				return (void __user *)address;
244			} else {
245				address = *cpu_reg_ptr;	/* Just return the contents
246							   of the cpu register */
247				addr->offset = address;
248				return (void __user *)address;
249			}
250		case 1:
251			/* 8 bit signed displacement */
252			RE_ENTRANT_CHECK_OFF;
253			FPU_code_access_ok(1);
254			FPU_get_user(address, (signed char __user *)(*fpu_eip));
255			RE_ENTRANT_CHECK_ON;
256			(*fpu_eip)++;
257			break;
258		case 2:
259			/* 32 bit displacement */
260			RE_ENTRANT_CHECK_OFF;
261			FPU_code_access_ok(4);
262			FPU_get_user(address, (long __user *)(*fpu_eip));
263			(*fpu_eip) += 4;
264			RE_ENTRANT_CHECK_ON;
265			break;
266		case 3:
267			/* Not legal for the FPU */
268			EXCEPTION(EX_Invalid);
269		}
270		address += *cpu_reg_ptr;
271	}
272
273	addr->offset = address;
274
275	switch (addr_modes.default_mode) {
276	case 0:
277		break;
278	case VM86:
279		address += vm86_segment(addr_modes.override.segment, addr);
280		break;
281	case PM16:
282	case SEG32:
283		address = pm_address(FPU_modrm, addr_modes.override.segment,
284				     addr, address);
285		break;
286	default:
287		EXCEPTION(EX_INTERNAL | 0x133);
288	}
289
290	return (void __user *)address;
291}
292
293void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
294				struct address *addr, fpu_addr_modes addr_modes)
295{
296	u_char mod;
297	unsigned rm = FPU_modrm & 7;
298	int address = 0;	/* Default used for mod == 0 */
299
300	/* Memory accessed via the cs selector is write protected
301	   in `non-segmented' 32 bit protected mode. */
302	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
303	    && (addr_modes.override.segment == PREFIX_CS_)) {
304		math_abort(FPU_info, SIGSEGV);
305	}
306
307	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
308
309	mod = (FPU_modrm >> 6) & 3;
310
311	switch (mod) {
312	case 0:
313		if (rm == 6) {
314			/* Special case: disp16 */
315			RE_ENTRANT_CHECK_OFF;
316			FPU_code_access_ok(2);
317			FPU_get_user(address,
318				     (unsigned short __user *)(*fpu_eip));
319			(*fpu_eip) += 2;
320			RE_ENTRANT_CHECK_ON;
321			goto add_segment;
322		}
323		break;
324	case 1:
325		/* 8 bit signed displacement */
326		RE_ENTRANT_CHECK_OFF;
327		FPU_code_access_ok(1);
328		FPU_get_user(address, (signed char __user *)(*fpu_eip));
329		RE_ENTRANT_CHECK_ON;
330		(*fpu_eip)++;
331		break;
332	case 2:
333		/* 16 bit displacement */
334		RE_ENTRANT_CHECK_OFF;
335		FPU_code_access_ok(2);
336		FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
337		(*fpu_eip) += 2;
338		RE_ENTRANT_CHECK_ON;
339		break;
340	case 3:
341		/* Not legal for the FPU */
342		EXCEPTION(EX_Invalid);
343		break;
344	}
345	switch (rm) {
346	case 0:
347		address += FPU_info->regs->bx + FPU_info->regs->si;
348		break;
349	case 1:
350		address += FPU_info->regs->bx + FPU_info->regs->di;
351		break;
352	case 2:
353		address += FPU_info->regs->bp + FPU_info->regs->si;
354		if (addr_modes.override.segment == PREFIX_DEFAULT)
355			addr_modes.override.segment = PREFIX_SS_;
356		break;
357	case 3:
358		address += FPU_info->regs->bp + FPU_info->regs->di;
359		if (addr_modes.override.segment == PREFIX_DEFAULT)
360			addr_modes.override.segment = PREFIX_SS_;
361		break;
362	case 4:
363		address += FPU_info->regs->si;
364		break;
365	case 5:
366		address += FPU_info->regs->di;
367		break;
368	case 6:
369		address += FPU_info->regs->bp;
370		if (addr_modes.override.segment == PREFIX_DEFAULT)
371			addr_modes.override.segment = PREFIX_SS_;
372		break;
373	case 7:
374		address += FPU_info->regs->bx;
375		break;
376	}
377
378      add_segment:
379	address &= 0xffff;
380
381	addr->offset = address;
382
383	switch (addr_modes.default_mode) {
384	case 0:
385		break;
386	case VM86:
387		address += vm86_segment(addr_modes.override.segment, addr);
388		break;
389	case PM16:
390	case SEG32:
391		address = pm_address(FPU_modrm, addr_modes.override.segment,
392				     addr, address);
393		break;
394	default:
395		EXCEPTION(EX_INTERNAL | 0x131);
396	}
397
398	return (void __user *)address;
399}