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