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/vm86.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 = FPU_get_ldt_descriptor(addr->selector); 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 = ®_(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} 400