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 21 #include <linux/stddef.h> 22 23 #include <asm/uaccess.h> 24 #include <asm/desc.h> 25 26 #include "fpu_system.h" 27 #include "exception.h" 28 #include "fpu_emu.h" 29 30 31 #define FPU_WRITE_BIT 0x10 32 33 static int reg_offset[] = { 34 offsetof(struct info,___eax), 35 offsetof(struct info,___ecx), 36 offsetof(struct info,___edx), 37 offsetof(struct info,___ebx), 38 offsetof(struct info,___esp), 39 offsetof(struct info,___ebp), 40 offsetof(struct info,___esi), 41 offsetof(struct info,___edi) 42 }; 43 44 #define REG_(x) (*(long *)(reg_offset[(x)]+(u_char *) FPU_info)) 45 46 static int reg_offset_vm86[] = { 47 offsetof(struct info,___cs), 48 offsetof(struct info,___vm86_ds), 49 offsetof(struct info,___vm86_es), 50 offsetof(struct info,___vm86_fs), 51 offsetof(struct info,___vm86_gs), 52 offsetof(struct info,___ss), 53 offsetof(struct info,___vm86_ds) 54 }; 55 56 #define VM86_REG_(x) (*(unsigned short *) \ 57 (reg_offset_vm86[((unsigned)x)]+(u_char *) FPU_info)) 58 59 /* This dummy, gs is not saved on the stack. */ 60 #define ___GS ___ds 61 62 static int reg_offset_pm[] = { 63 offsetof(struct info,___cs), 64 offsetof(struct info,___ds), 65 offsetof(struct info,___es), 66 offsetof(struct info,___fs), 67 offsetof(struct info,___GS), 68 offsetof(struct info,___ss), 69 offsetof(struct info,___ds) 70 }; 71 72 #define PM_REG_(x) (*(unsigned short *) \ 73 (reg_offset_pm[((unsigned)x)]+(u_char *) FPU_info)) 74 75 76 /* Decode the SIB byte. This function assumes mod != 0 */ 77 static int sib(int mod, unsigned long *fpu_eip) 78 { 79 u_char ss,index,base; 80 long offset; 81 82 RE_ENTRANT_CHECK_OFF; 83 FPU_code_access_ok(1); 84 FPU_get_user(base, (u_char __user *) (*fpu_eip)); /* The SIB byte */ 85 RE_ENTRANT_CHECK_ON; 86 (*fpu_eip)++; 87 ss = base >> 6; 88 index = (base >> 3) & 7; 89 base &= 7; 90 91 if ((mod == 0) && (base == 5)) 92 offset = 0; /* No base register */ 93 else 94 offset = REG_(base); 95 96 if (index == 4) 97 { 98 /* No index register */ 99 /* A non-zero ss is illegal */ 100 if ( ss ) 101 EXCEPTION(EX_Invalid); 102 } 103 else 104 { 105 offset += (REG_(index)) << ss; 106 } 107 108 if (mod == 1) 109 { 110 /* 8 bit signed displacement */ 111 long displacement; 112 RE_ENTRANT_CHECK_OFF; 113 FPU_code_access_ok(1); 114 FPU_get_user(displacement, (signed char __user *) (*fpu_eip)); 115 offset += displacement; 116 RE_ENTRANT_CHECK_ON; 117 (*fpu_eip)++; 118 } 119 else if (mod == 2 || base == 5) /* The second condition also has mod==0 */ 120 { 121 /* 32 bit displacement */ 122 long displacement; 123 RE_ENTRANT_CHECK_OFF; 124 FPU_code_access_ok(4); 125 FPU_get_user(displacement, (long __user *) (*fpu_eip)); 126 offset += displacement; 127 RE_ENTRANT_CHECK_ON; 128 (*fpu_eip) += 4; 129 } 130 131 return offset; 132 } 133 134 135 static unsigned long vm86_segment(u_char segment, 136 struct address *addr) 137 { 138 segment--; 139 #ifdef PARANOID 140 if ( segment > PREFIX_SS_ ) 141 { 142 EXCEPTION(EX_INTERNAL|0x130); 143 math_abort(FPU_info,SIGSEGV); 144 } 145 #endif /* PARANOID */ 146 addr->selector = VM86_REG_(segment); 147 return (unsigned long)VM86_REG_(segment) << 4; 148 } 149 150 151 /* This should work for 16 and 32 bit protected mode. */ 152 static long pm_address(u_char FPU_modrm, u_char segment, 153 struct address *addr, long offset) 154 { 155 struct desc_struct descriptor; 156 unsigned long base_address, limit, address, seg_top; 157 158 segment--; 159 160 #ifdef PARANOID 161 /* segment is unsigned, so this also detects if segment was 0: */ 162 if ( segment > PREFIX_SS_ ) 163 { 164 EXCEPTION(EX_INTERNAL|0x132); 165 math_abort(FPU_info,SIGSEGV); 166 } 167 #endif /* PARANOID */ 168 169 switch ( segment ) 170 { 171 /* gs isn't used by the kernel, so it still has its 172 user-space value. */ 173 case PREFIX_GS_-1: 174 /* N.B. - movl %seg, mem is a 2 byte write regardless of prefix */ 175 savesegment(gs, addr->selector); 176 break; 177 default: 178 addr->selector = PM_REG_(segment); 179 } 180 181 descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); 182 base_address = SEG_BASE_ADDR(descriptor); 183 address = base_address + offset; 184 limit = base_address 185 + (SEG_LIMIT(descriptor)+1) * SEG_GRANULARITY(descriptor) - 1; 186 if ( limit < base_address ) limit = 0xffffffff; 187 188 if ( SEG_EXPAND_DOWN(descriptor) ) 189 { 190 if ( SEG_G_BIT(descriptor) ) 191 seg_top = 0xffffffff; 192 else 193 { 194 seg_top = base_address + (1 << 20); 195 if ( seg_top < base_address ) seg_top = 0xffffffff; 196 } 197 access_limit = 198 (address <= limit) || (address >= seg_top) ? 0 : 199 ((seg_top-address) >= 255 ? 255 : seg_top-address); 200 } 201 else 202 { 203 access_limit = 204 (address > limit) || (address < base_address) ? 0 : 205 ((limit-address) >= 254 ? 255 : limit-address+1); 206 } 207 if ( SEG_EXECUTE_ONLY(descriptor) || 208 (!SEG_WRITE_PERM(descriptor) && (FPU_modrm & FPU_WRITE_BIT)) ) 209 { 210 access_limit = 0; 211 } 212 return address; 213 } 214 215 216 /* 217 MOD R/M byte: MOD == 3 has a special use for the FPU 218 SIB byte used iff R/M = 100b 219 220 7 6 5 4 3 2 1 0 221 ..... ......... ......... 222 MOD OPCODE(2) R/M 223 224 225 SIB byte 226 227 7 6 5 4 3 2 1 0 228 ..... ......... ......... 229 SS INDEX BASE 230 231 */ 232 233 void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip, 234 struct address *addr, 235 fpu_addr_modes addr_modes) 236 { 237 u_char mod; 238 unsigned rm = FPU_modrm & 7; 239 long *cpu_reg_ptr; 240 int address = 0; /* Initialized just to stop compiler warnings. */ 241 242 /* Memory accessed via the cs selector is write protected 243 in `non-segmented' 32 bit protected mode. */ 244 if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) 245 && (addr_modes.override.segment == PREFIX_CS_) ) 246 { 247 math_abort(FPU_info,SIGSEGV); 248 } 249 250 addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ 251 252 mod = (FPU_modrm >> 6) & 3; 253 254 if (rm == 4 && mod != 3) 255 { 256 address = sib(mod, fpu_eip); 257 } 258 else 259 { 260 cpu_reg_ptr = & REG_(rm); 261 switch (mod) 262 { 263 case 0: 264 if (rm == 5) 265 { 266 /* Special case: disp32 */ 267 RE_ENTRANT_CHECK_OFF; 268 FPU_code_access_ok(4); 269 FPU_get_user(address, (unsigned long __user *) (*fpu_eip)); 270 (*fpu_eip) += 4; 271 RE_ENTRANT_CHECK_ON; 272 addr->offset = address; 273 return (void __user *) address; 274 } 275 else 276 { 277 address = *cpu_reg_ptr; /* Just return the contents 278 of the cpu register */ 279 addr->offset = address; 280 return (void __user *) address; 281 } 282 case 1: 283 /* 8 bit signed displacement */ 284 RE_ENTRANT_CHECK_OFF; 285 FPU_code_access_ok(1); 286 FPU_get_user(address, (signed char __user *) (*fpu_eip)); 287 RE_ENTRANT_CHECK_ON; 288 (*fpu_eip)++; 289 break; 290 case 2: 291 /* 32 bit displacement */ 292 RE_ENTRANT_CHECK_OFF; 293 FPU_code_access_ok(4); 294 FPU_get_user(address, (long __user *) (*fpu_eip)); 295 (*fpu_eip) += 4; 296 RE_ENTRANT_CHECK_ON; 297 break; 298 case 3: 299 /* Not legal for the FPU */ 300 EXCEPTION(EX_Invalid); 301 } 302 address += *cpu_reg_ptr; 303 } 304 305 addr->offset = address; 306 307 switch ( addr_modes.default_mode ) 308 { 309 case 0: 310 break; 311 case VM86: 312 address += vm86_segment(addr_modes.override.segment, addr); 313 break; 314 case PM16: 315 case SEG32: 316 address = pm_address(FPU_modrm, addr_modes.override.segment, 317 addr, address); 318 break; 319 default: 320 EXCEPTION(EX_INTERNAL|0x133); 321 } 322 323 return (void __user *)address; 324 } 325 326 327 void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, 328 struct address *addr, 329 fpu_addr_modes addr_modes) 330 { 331 u_char mod; 332 unsigned rm = FPU_modrm & 7; 333 int address = 0; /* Default used for mod == 0 */ 334 335 /* Memory accessed via the cs selector is write protected 336 in `non-segmented' 32 bit protected mode. */ 337 if ( !addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) 338 && (addr_modes.override.segment == PREFIX_CS_) ) 339 { 340 math_abort(FPU_info,SIGSEGV); 341 } 342 343 addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ 344 345 mod = (FPU_modrm >> 6) & 3; 346 347 switch (mod) 348 { 349 case 0: 350 if (rm == 6) 351 { 352 /* Special case: disp16 */ 353 RE_ENTRANT_CHECK_OFF; 354 FPU_code_access_ok(2); 355 FPU_get_user(address, (unsigned short __user *) (*fpu_eip)); 356 (*fpu_eip) += 2; 357 RE_ENTRANT_CHECK_ON; 358 goto add_segment; 359 } 360 break; 361 case 1: 362 /* 8 bit signed displacement */ 363 RE_ENTRANT_CHECK_OFF; 364 FPU_code_access_ok(1); 365 FPU_get_user(address, (signed char __user *) (*fpu_eip)); 366 RE_ENTRANT_CHECK_ON; 367 (*fpu_eip)++; 368 break; 369 case 2: 370 /* 16 bit displacement */ 371 RE_ENTRANT_CHECK_OFF; 372 FPU_code_access_ok(2); 373 FPU_get_user(address, (unsigned short __user *) (*fpu_eip)); 374 (*fpu_eip) += 2; 375 RE_ENTRANT_CHECK_ON; 376 break; 377 case 3: 378 /* Not legal for the FPU */ 379 EXCEPTION(EX_Invalid); 380 break; 381 } 382 switch ( rm ) 383 { 384 case 0: 385 address += FPU_info->___ebx + FPU_info->___esi; 386 break; 387 case 1: 388 address += FPU_info->___ebx + FPU_info->___edi; 389 break; 390 case 2: 391 address += FPU_info->___ebp + FPU_info->___esi; 392 if ( addr_modes.override.segment == PREFIX_DEFAULT ) 393 addr_modes.override.segment = PREFIX_SS_; 394 break; 395 case 3: 396 address += FPU_info->___ebp + FPU_info->___edi; 397 if ( addr_modes.override.segment == PREFIX_DEFAULT ) 398 addr_modes.override.segment = PREFIX_SS_; 399 break; 400 case 4: 401 address += FPU_info->___esi; 402 break; 403 case 5: 404 address += FPU_info->___edi; 405 break; 406 case 6: 407 address += FPU_info->___ebp; 408 if ( addr_modes.override.segment == PREFIX_DEFAULT ) 409 addr_modes.override.segment = PREFIX_SS_; 410 break; 411 case 7: 412 address += FPU_info->___ebx; 413 break; 414 } 415 416 add_segment: 417 address &= 0xffff; 418 419 addr->offset = address; 420 421 switch ( addr_modes.default_mode ) 422 { 423 case 0: 424 break; 425 case VM86: 426 address += vm86_segment(addr_modes.override.segment, addr); 427 break; 428 case PM16: 429 case SEG32: 430 address = pm_address(FPU_modrm, addr_modes.override.segment, 431 addr, address); 432 break; 433 default: 434 EXCEPTION(EX_INTERNAL|0x131); 435 } 436 437 return (void __user *)address ; 438 } 439