18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------+
38c2ecf20Sopenharmony_ci |  get_address.c                                                            |
48c2ecf20Sopenharmony_ci |                                                                           |
58c2ecf20Sopenharmony_ci | Get the effective address from an FPU instruction.                        |
68c2ecf20Sopenharmony_ci |                                                                           |
78c2ecf20Sopenharmony_ci | Copyright (C) 1992,1993,1994,1997                                         |
88c2ecf20Sopenharmony_ci |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
98c2ecf20Sopenharmony_ci |                       Australia.  E-mail   billm@suburbia.net             |
108c2ecf20Sopenharmony_ci |                                                                           |
118c2ecf20Sopenharmony_ci |                                                                           |
128c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------+
158c2ecf20Sopenharmony_ci | Note:                                                                     |
168c2ecf20Sopenharmony_ci |    The file contains code which accesses user memory.                     |
178c2ecf20Sopenharmony_ci |    Emulator static data may change when user memory is accessed, due to   |
188c2ecf20Sopenharmony_ci |    other processes using the emulator while swapping is in progress.      |
198c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/stddef.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
248c2ecf20Sopenharmony_ci#include <asm/vm86.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "fpu_system.h"
278c2ecf20Sopenharmony_ci#include "exception.h"
288c2ecf20Sopenharmony_ci#include "fpu_emu.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define FPU_WRITE_BIT 0x10
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int reg_offset[] = {
338c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, ax),
348c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, cx),
358c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, dx),
368c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, bx),
378c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, sp),
388c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, bp),
398c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, si),
408c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, di)
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int reg_offset_vm86[] = {
468c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, cs),
478c2ecf20Sopenharmony_ci	offsetof(struct kernel_vm86_regs, ds),
488c2ecf20Sopenharmony_ci	offsetof(struct kernel_vm86_regs, es),
498c2ecf20Sopenharmony_ci	offsetof(struct kernel_vm86_regs, fs),
508c2ecf20Sopenharmony_ci	offsetof(struct kernel_vm86_regs, gs),
518c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, ss),
528c2ecf20Sopenharmony_ci	offsetof(struct kernel_vm86_regs, ds)
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define VM86_REG_(x) (*(unsigned short *) \
568c2ecf20Sopenharmony_ci		(reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int reg_offset_pm[] = {
598c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, cs),
608c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, ds),
618c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, es),
628c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, fs),
638c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, ds),	/* dummy, not saved on stack */
648c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, ss),
658c2ecf20Sopenharmony_ci	offsetof(struct pt_regs, ds)
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define PM_REG_(x) (*(unsigned short *) \
698c2ecf20Sopenharmony_ci		(reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* Decode the SIB byte. This function assumes mod != 0 */
728c2ecf20Sopenharmony_cistatic int sib(int mod, unsigned long *fpu_eip)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	u_char ss, index, base;
758c2ecf20Sopenharmony_ci	long offset;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	RE_ENTRANT_CHECK_OFF;
788c2ecf20Sopenharmony_ci	FPU_code_access_ok(1);
798c2ecf20Sopenharmony_ci	FPU_get_user(base, (u_char __user *) (*fpu_eip));	/* The SIB byte */
808c2ecf20Sopenharmony_ci	RE_ENTRANT_CHECK_ON;
818c2ecf20Sopenharmony_ci	(*fpu_eip)++;
828c2ecf20Sopenharmony_ci	ss = base >> 6;
838c2ecf20Sopenharmony_ci	index = (base >> 3) & 7;
848c2ecf20Sopenharmony_ci	base &= 7;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if ((mod == 0) && (base == 5))
878c2ecf20Sopenharmony_ci		offset = 0;	/* No base register */
888c2ecf20Sopenharmony_ci	else
898c2ecf20Sopenharmony_ci		offset = REG_(base);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (index == 4) {
928c2ecf20Sopenharmony_ci		/* No index register */
938c2ecf20Sopenharmony_ci		/* A non-zero ss is illegal */
948c2ecf20Sopenharmony_ci		if (ss)
958c2ecf20Sopenharmony_ci			EXCEPTION(EX_Invalid);
968c2ecf20Sopenharmony_ci	} else {
978c2ecf20Sopenharmony_ci		offset += (REG_(index)) << ss;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (mod == 1) {
1018c2ecf20Sopenharmony_ci		/* 8 bit signed displacement */
1028c2ecf20Sopenharmony_ci		long displacement;
1038c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_OFF;
1048c2ecf20Sopenharmony_ci		FPU_code_access_ok(1);
1058c2ecf20Sopenharmony_ci		FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
1068c2ecf20Sopenharmony_ci		offset += displacement;
1078c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_ON;
1088c2ecf20Sopenharmony_ci		(*fpu_eip)++;
1098c2ecf20Sopenharmony_ci	} else if (mod == 2 || base == 5) {	/* The second condition also has mod==0 */
1108c2ecf20Sopenharmony_ci		/* 32 bit displacement */
1118c2ecf20Sopenharmony_ci		long displacement;
1128c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_OFF;
1138c2ecf20Sopenharmony_ci		FPU_code_access_ok(4);
1148c2ecf20Sopenharmony_ci		FPU_get_user(displacement, (long __user *)(*fpu_eip));
1158c2ecf20Sopenharmony_ci		offset += displacement;
1168c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_ON;
1178c2ecf20Sopenharmony_ci		(*fpu_eip) += 4;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return offset;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic unsigned long vm86_segment(u_char segment, struct address *addr)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	segment--;
1268c2ecf20Sopenharmony_ci#ifdef PARANOID
1278c2ecf20Sopenharmony_ci	if (segment > PREFIX_SS_) {
1288c2ecf20Sopenharmony_ci		EXCEPTION(EX_INTERNAL | 0x130);
1298c2ecf20Sopenharmony_ci		math_abort(FPU_info, SIGSEGV);
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci#endif /* PARANOID */
1328c2ecf20Sopenharmony_ci	addr->selector = VM86_REG_(segment);
1338c2ecf20Sopenharmony_ci	return (unsigned long)VM86_REG_(segment) << 4;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/* This should work for 16 and 32 bit protected mode. */
1378c2ecf20Sopenharmony_cistatic long pm_address(u_char FPU_modrm, u_char segment,
1388c2ecf20Sopenharmony_ci		       struct address *addr, long offset)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct desc_struct descriptor;
1418c2ecf20Sopenharmony_ci	unsigned long base_address, limit, address, seg_top;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	segment--;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci#ifdef PARANOID
1468c2ecf20Sopenharmony_ci	/* segment is unsigned, so this also detects if segment was 0: */
1478c2ecf20Sopenharmony_ci	if (segment > PREFIX_SS_) {
1488c2ecf20Sopenharmony_ci		EXCEPTION(EX_INTERNAL | 0x132);
1498c2ecf20Sopenharmony_ci		math_abort(FPU_info, SIGSEGV);
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci#endif /* PARANOID */
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	switch (segment) {
1548c2ecf20Sopenharmony_ci	case PREFIX_GS_ - 1:
1558c2ecf20Sopenharmony_ci		/* user gs handling can be lazy, use special accessors */
1568c2ecf20Sopenharmony_ci		addr->selector = get_user_gs(FPU_info->regs);
1578c2ecf20Sopenharmony_ci		break;
1588c2ecf20Sopenharmony_ci	default:
1598c2ecf20Sopenharmony_ci		addr->selector = PM_REG_(segment);
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	descriptor = FPU_get_ldt_descriptor(addr->selector);
1638c2ecf20Sopenharmony_ci	base_address = seg_get_base(&descriptor);
1648c2ecf20Sopenharmony_ci	address = base_address + offset;
1658c2ecf20Sopenharmony_ci	limit = seg_get_limit(&descriptor) + 1;
1668c2ecf20Sopenharmony_ci	limit *= seg_get_granularity(&descriptor);
1678c2ecf20Sopenharmony_ci	limit += base_address - 1;
1688c2ecf20Sopenharmony_ci	if (limit < base_address)
1698c2ecf20Sopenharmony_ci		limit = 0xffffffff;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (seg_expands_down(&descriptor)) {
1728c2ecf20Sopenharmony_ci		if (descriptor.g) {
1738c2ecf20Sopenharmony_ci			seg_top = 0xffffffff;
1748c2ecf20Sopenharmony_ci		} else {
1758c2ecf20Sopenharmony_ci			seg_top = base_address + (1 << 20);
1768c2ecf20Sopenharmony_ci			if (seg_top < base_address)
1778c2ecf20Sopenharmony_ci				seg_top = 0xffffffff;
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci		access_limit =
1808c2ecf20Sopenharmony_ci		    (address <= limit) || (address >= seg_top) ? 0 :
1818c2ecf20Sopenharmony_ci		    ((seg_top - address) >= 255 ? 255 : seg_top - address);
1828c2ecf20Sopenharmony_ci	} else {
1838c2ecf20Sopenharmony_ci		access_limit =
1848c2ecf20Sopenharmony_ci		    (address > limit) || (address < base_address) ? 0 :
1858c2ecf20Sopenharmony_ci		    ((limit - address) >= 254 ? 255 : limit - address + 1);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci	if (seg_execute_only(&descriptor) ||
1888c2ecf20Sopenharmony_ci	    (!seg_writable(&descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
1898c2ecf20Sopenharmony_ci		access_limit = 0;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	return address;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/*
1958c2ecf20Sopenharmony_ci       MOD R/M byte:  MOD == 3 has a special use for the FPU
1968c2ecf20Sopenharmony_ci                      SIB byte used iff R/M = 100b
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci       7   6   5   4   3   2   1   0
1998c2ecf20Sopenharmony_ci       .....   .........   .........
2008c2ecf20Sopenharmony_ci        MOD    OPCODE(2)     R/M
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci       SIB byte
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci       7   6   5   4   3   2   1   0
2058c2ecf20Sopenharmony_ci       .....   .........   .........
2068c2ecf20Sopenharmony_ci        SS      INDEX        BASE
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci*/
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_civoid __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
2118c2ecf20Sopenharmony_ci			     struct address *addr, fpu_addr_modes addr_modes)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	u_char mod;
2148c2ecf20Sopenharmony_ci	unsigned rm = FPU_modrm & 7;
2158c2ecf20Sopenharmony_ci	long *cpu_reg_ptr;
2168c2ecf20Sopenharmony_ci	int address = 0;	/* Initialized just to stop compiler warnings. */
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Memory accessed via the cs selector is write protected
2198c2ecf20Sopenharmony_ci	   in `non-segmented' 32 bit protected mode. */
2208c2ecf20Sopenharmony_ci	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
2218c2ecf20Sopenharmony_ci	    && (addr_modes.override.segment == PREFIX_CS_)) {
2228c2ecf20Sopenharmony_ci		math_abort(FPU_info, SIGSEGV);
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	mod = (FPU_modrm >> 6) & 3;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (rm == 4 && mod != 3) {
2308c2ecf20Sopenharmony_ci		address = sib(mod, fpu_eip);
2318c2ecf20Sopenharmony_ci	} else {
2328c2ecf20Sopenharmony_ci		cpu_reg_ptr = &REG_(rm);
2338c2ecf20Sopenharmony_ci		switch (mod) {
2348c2ecf20Sopenharmony_ci		case 0:
2358c2ecf20Sopenharmony_ci			if (rm == 5) {
2368c2ecf20Sopenharmony_ci				/* Special case: disp32 */
2378c2ecf20Sopenharmony_ci				RE_ENTRANT_CHECK_OFF;
2388c2ecf20Sopenharmony_ci				FPU_code_access_ok(4);
2398c2ecf20Sopenharmony_ci				FPU_get_user(address,
2408c2ecf20Sopenharmony_ci					     (unsigned long __user
2418c2ecf20Sopenharmony_ci					      *)(*fpu_eip));
2428c2ecf20Sopenharmony_ci				(*fpu_eip) += 4;
2438c2ecf20Sopenharmony_ci				RE_ENTRANT_CHECK_ON;
2448c2ecf20Sopenharmony_ci				addr->offset = address;
2458c2ecf20Sopenharmony_ci				return (void __user *)address;
2468c2ecf20Sopenharmony_ci			} else {
2478c2ecf20Sopenharmony_ci				address = *cpu_reg_ptr;	/* Just return the contents
2488c2ecf20Sopenharmony_ci							   of the cpu register */
2498c2ecf20Sopenharmony_ci				addr->offset = address;
2508c2ecf20Sopenharmony_ci				return (void __user *)address;
2518c2ecf20Sopenharmony_ci			}
2528c2ecf20Sopenharmony_ci		case 1:
2538c2ecf20Sopenharmony_ci			/* 8 bit signed displacement */
2548c2ecf20Sopenharmony_ci			RE_ENTRANT_CHECK_OFF;
2558c2ecf20Sopenharmony_ci			FPU_code_access_ok(1);
2568c2ecf20Sopenharmony_ci			FPU_get_user(address, (signed char __user *)(*fpu_eip));
2578c2ecf20Sopenharmony_ci			RE_ENTRANT_CHECK_ON;
2588c2ecf20Sopenharmony_ci			(*fpu_eip)++;
2598c2ecf20Sopenharmony_ci			break;
2608c2ecf20Sopenharmony_ci		case 2:
2618c2ecf20Sopenharmony_ci			/* 32 bit displacement */
2628c2ecf20Sopenharmony_ci			RE_ENTRANT_CHECK_OFF;
2638c2ecf20Sopenharmony_ci			FPU_code_access_ok(4);
2648c2ecf20Sopenharmony_ci			FPU_get_user(address, (long __user *)(*fpu_eip));
2658c2ecf20Sopenharmony_ci			(*fpu_eip) += 4;
2668c2ecf20Sopenharmony_ci			RE_ENTRANT_CHECK_ON;
2678c2ecf20Sopenharmony_ci			break;
2688c2ecf20Sopenharmony_ci		case 3:
2698c2ecf20Sopenharmony_ci			/* Not legal for the FPU */
2708c2ecf20Sopenharmony_ci			EXCEPTION(EX_Invalid);
2718c2ecf20Sopenharmony_ci		}
2728c2ecf20Sopenharmony_ci		address += *cpu_reg_ptr;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	addr->offset = address;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	switch (addr_modes.default_mode) {
2788c2ecf20Sopenharmony_ci	case 0:
2798c2ecf20Sopenharmony_ci		break;
2808c2ecf20Sopenharmony_ci	case VM86:
2818c2ecf20Sopenharmony_ci		address += vm86_segment(addr_modes.override.segment, addr);
2828c2ecf20Sopenharmony_ci		break;
2838c2ecf20Sopenharmony_ci	case PM16:
2848c2ecf20Sopenharmony_ci	case SEG32:
2858c2ecf20Sopenharmony_ci		address = pm_address(FPU_modrm, addr_modes.override.segment,
2868c2ecf20Sopenharmony_ci				     addr, address);
2878c2ecf20Sopenharmony_ci		break;
2888c2ecf20Sopenharmony_ci	default:
2898c2ecf20Sopenharmony_ci		EXCEPTION(EX_INTERNAL | 0x133);
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return (void __user *)address;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_civoid __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
2968c2ecf20Sopenharmony_ci				struct address *addr, fpu_addr_modes addr_modes)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	u_char mod;
2998c2ecf20Sopenharmony_ci	unsigned rm = FPU_modrm & 7;
3008c2ecf20Sopenharmony_ci	int address = 0;	/* Default used for mod == 0 */
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Memory accessed via the cs selector is write protected
3038c2ecf20Sopenharmony_ci	   in `non-segmented' 32 bit protected mode. */
3048c2ecf20Sopenharmony_ci	if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
3058c2ecf20Sopenharmony_ci	    && (addr_modes.override.segment == PREFIX_CS_)) {
3068c2ecf20Sopenharmony_ci		math_abort(FPU_info, SIGSEGV);
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	addr->selector = FPU_DS;	/* Default, for 32 bit non-segmented mode. */
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	mod = (FPU_modrm >> 6) & 3;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	switch (mod) {
3148c2ecf20Sopenharmony_ci	case 0:
3158c2ecf20Sopenharmony_ci		if (rm == 6) {
3168c2ecf20Sopenharmony_ci			/* Special case: disp16 */
3178c2ecf20Sopenharmony_ci			RE_ENTRANT_CHECK_OFF;
3188c2ecf20Sopenharmony_ci			FPU_code_access_ok(2);
3198c2ecf20Sopenharmony_ci			FPU_get_user(address,
3208c2ecf20Sopenharmony_ci				     (unsigned short __user *)(*fpu_eip));
3218c2ecf20Sopenharmony_ci			(*fpu_eip) += 2;
3228c2ecf20Sopenharmony_ci			RE_ENTRANT_CHECK_ON;
3238c2ecf20Sopenharmony_ci			goto add_segment;
3248c2ecf20Sopenharmony_ci		}
3258c2ecf20Sopenharmony_ci		break;
3268c2ecf20Sopenharmony_ci	case 1:
3278c2ecf20Sopenharmony_ci		/* 8 bit signed displacement */
3288c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_OFF;
3298c2ecf20Sopenharmony_ci		FPU_code_access_ok(1);
3308c2ecf20Sopenharmony_ci		FPU_get_user(address, (signed char __user *)(*fpu_eip));
3318c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_ON;
3328c2ecf20Sopenharmony_ci		(*fpu_eip)++;
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	case 2:
3358c2ecf20Sopenharmony_ci		/* 16 bit displacement */
3368c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_OFF;
3378c2ecf20Sopenharmony_ci		FPU_code_access_ok(2);
3388c2ecf20Sopenharmony_ci		FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
3398c2ecf20Sopenharmony_ci		(*fpu_eip) += 2;
3408c2ecf20Sopenharmony_ci		RE_ENTRANT_CHECK_ON;
3418c2ecf20Sopenharmony_ci		break;
3428c2ecf20Sopenharmony_ci	case 3:
3438c2ecf20Sopenharmony_ci		/* Not legal for the FPU */
3448c2ecf20Sopenharmony_ci		EXCEPTION(EX_Invalid);
3458c2ecf20Sopenharmony_ci		break;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	switch (rm) {
3488c2ecf20Sopenharmony_ci	case 0:
3498c2ecf20Sopenharmony_ci		address += FPU_info->regs->bx + FPU_info->regs->si;
3508c2ecf20Sopenharmony_ci		break;
3518c2ecf20Sopenharmony_ci	case 1:
3528c2ecf20Sopenharmony_ci		address += FPU_info->regs->bx + FPU_info->regs->di;
3538c2ecf20Sopenharmony_ci		break;
3548c2ecf20Sopenharmony_ci	case 2:
3558c2ecf20Sopenharmony_ci		address += FPU_info->regs->bp + FPU_info->regs->si;
3568c2ecf20Sopenharmony_ci		if (addr_modes.override.segment == PREFIX_DEFAULT)
3578c2ecf20Sopenharmony_ci			addr_modes.override.segment = PREFIX_SS_;
3588c2ecf20Sopenharmony_ci		break;
3598c2ecf20Sopenharmony_ci	case 3:
3608c2ecf20Sopenharmony_ci		address += FPU_info->regs->bp + FPU_info->regs->di;
3618c2ecf20Sopenharmony_ci		if (addr_modes.override.segment == PREFIX_DEFAULT)
3628c2ecf20Sopenharmony_ci			addr_modes.override.segment = PREFIX_SS_;
3638c2ecf20Sopenharmony_ci		break;
3648c2ecf20Sopenharmony_ci	case 4:
3658c2ecf20Sopenharmony_ci		address += FPU_info->regs->si;
3668c2ecf20Sopenharmony_ci		break;
3678c2ecf20Sopenharmony_ci	case 5:
3688c2ecf20Sopenharmony_ci		address += FPU_info->regs->di;
3698c2ecf20Sopenharmony_ci		break;
3708c2ecf20Sopenharmony_ci	case 6:
3718c2ecf20Sopenharmony_ci		address += FPU_info->regs->bp;
3728c2ecf20Sopenharmony_ci		if (addr_modes.override.segment == PREFIX_DEFAULT)
3738c2ecf20Sopenharmony_ci			addr_modes.override.segment = PREFIX_SS_;
3748c2ecf20Sopenharmony_ci		break;
3758c2ecf20Sopenharmony_ci	case 7:
3768c2ecf20Sopenharmony_ci		address += FPU_info->regs->bx;
3778c2ecf20Sopenharmony_ci		break;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci      add_segment:
3818c2ecf20Sopenharmony_ci	address &= 0xffff;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	addr->offset = address;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	switch (addr_modes.default_mode) {
3868c2ecf20Sopenharmony_ci	case 0:
3878c2ecf20Sopenharmony_ci		break;
3888c2ecf20Sopenharmony_ci	case VM86:
3898c2ecf20Sopenharmony_ci		address += vm86_segment(addr_modes.override.segment, addr);
3908c2ecf20Sopenharmony_ci		break;
3918c2ecf20Sopenharmony_ci	case PM16:
3928c2ecf20Sopenharmony_ci	case SEG32:
3938c2ecf20Sopenharmony_ci		address = pm_address(FPU_modrm, addr_modes.override.segment,
3948c2ecf20Sopenharmony_ci				     addr, address);
3958c2ecf20Sopenharmony_ci		break;
3968c2ecf20Sopenharmony_ci	default:
3978c2ecf20Sopenharmony_ci		EXCEPTION(EX_INTERNAL | 0x131);
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return (void __user *)address;
4018c2ecf20Sopenharmony_ci}
402