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 = ®_(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