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 <linux/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 31 static 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 44 static 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 57 static 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 */ 71 static 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 122 static 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. */ 136 static 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_get_base(&descriptor); 163 address = base_address + offset; 164 limit = seg_get_limit(&descriptor) + 1; 165 limit *= seg_get_granularity(&descriptor); 166 limit += base_address - 1; 167 if (limit < base_address) 168 limit = 0xffffffff; 169 170 if (seg_expands_down(&descriptor)) { 171 if (descriptor.g) { 172 seg_top = 0xffffffff; 173 } else { 174 seg_top = base_address + (1 << 20); 175 if (seg_top < base_address) 176 seg_top = 0xffffffff; 177 } 178 access_limit = 179 (address <= limit) || (address >= seg_top) ? 0 : 180 ((seg_top - address) >= 255 ? 255 : seg_top - address); 181 } else { 182 access_limit = 183 (address > limit) || (address < base_address) ? 0 : 184 ((limit - address) >= 254 ? 255 : limit - address + 1); 185 } 186 if (seg_execute_only(&descriptor) || 187 (!seg_writable(&descriptor) && (FPU_modrm & FPU_WRITE_BIT))) { 188 access_limit = 0; 189 } 190 return address; 191 } 192 193 /* 194 MOD R/M byte: MOD == 3 has a special use for the FPU 195 SIB byte used iff R/M = 100b 196 197 7 6 5 4 3 2 1 0 198 ..... ......... ......... 199 MOD OPCODE(2) R/M 200 201 SIB byte 202 203 7 6 5 4 3 2 1 0 204 ..... ......... ......... 205 SS INDEX BASE 206 207 */ 208 209 void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip, 210 struct address *addr, fpu_addr_modes addr_modes) 211 { 212 u_char mod; 213 unsigned rm = FPU_modrm & 7; 214 long *cpu_reg_ptr; 215 int address = 0; /* Initialized just to stop compiler warnings. */ 216 217 /* Memory accessed via the cs selector is write protected 218 in `non-segmented' 32 bit protected mode. */ 219 if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) 220 && (addr_modes.override.segment == PREFIX_CS_)) { 221 math_abort(FPU_info, SIGSEGV); 222 } 223 224 addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ 225 226 mod = (FPU_modrm >> 6) & 3; 227 228 if (rm == 4 && mod != 3) { 229 address = sib(mod, fpu_eip); 230 } else { 231 cpu_reg_ptr = ®_(rm); 232 switch (mod) { 233 case 0: 234 if (rm == 5) { 235 /* Special case: disp32 */ 236 RE_ENTRANT_CHECK_OFF; 237 FPU_code_access_ok(4); 238 FPU_get_user(address, 239 (unsigned long __user 240 *)(*fpu_eip)); 241 (*fpu_eip) += 4; 242 RE_ENTRANT_CHECK_ON; 243 addr->offset = address; 244 return (void __user *)address; 245 } else { 246 address = *cpu_reg_ptr; /* Just return the contents 247 of the cpu register */ 248 addr->offset = address; 249 return (void __user *)address; 250 } 251 case 1: 252 /* 8 bit signed displacement */ 253 RE_ENTRANT_CHECK_OFF; 254 FPU_code_access_ok(1); 255 FPU_get_user(address, (signed char __user *)(*fpu_eip)); 256 RE_ENTRANT_CHECK_ON; 257 (*fpu_eip)++; 258 break; 259 case 2: 260 /* 32 bit displacement */ 261 RE_ENTRANT_CHECK_OFF; 262 FPU_code_access_ok(4); 263 FPU_get_user(address, (long __user *)(*fpu_eip)); 264 (*fpu_eip) += 4; 265 RE_ENTRANT_CHECK_ON; 266 break; 267 case 3: 268 /* Not legal for the FPU */ 269 EXCEPTION(EX_Invalid); 270 } 271 address += *cpu_reg_ptr; 272 } 273 274 addr->offset = address; 275 276 switch (addr_modes.default_mode) { 277 case 0: 278 break; 279 case VM86: 280 address += vm86_segment(addr_modes.override.segment, addr); 281 break; 282 case PM16: 283 case SEG32: 284 address = pm_address(FPU_modrm, addr_modes.override.segment, 285 addr, address); 286 break; 287 default: 288 EXCEPTION(EX_INTERNAL | 0x133); 289 } 290 291 return (void __user *)address; 292 } 293 294 void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip, 295 struct address *addr, fpu_addr_modes addr_modes) 296 { 297 u_char mod; 298 unsigned rm = FPU_modrm & 7; 299 int address = 0; /* Default used for mod == 0 */ 300 301 /* Memory accessed via the cs selector is write protected 302 in `non-segmented' 32 bit protected mode. */ 303 if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT) 304 && (addr_modes.override.segment == PREFIX_CS_)) { 305 math_abort(FPU_info, SIGSEGV); 306 } 307 308 addr->selector = FPU_DS; /* Default, for 32 bit non-segmented mode. */ 309 310 mod = (FPU_modrm >> 6) & 3; 311 312 switch (mod) { 313 case 0: 314 if (rm == 6) { 315 /* Special case: disp16 */ 316 RE_ENTRANT_CHECK_OFF; 317 FPU_code_access_ok(2); 318 FPU_get_user(address, 319 (unsigned short __user *)(*fpu_eip)); 320 (*fpu_eip) += 2; 321 RE_ENTRANT_CHECK_ON; 322 goto add_segment; 323 } 324 break; 325 case 1: 326 /* 8 bit signed displacement */ 327 RE_ENTRANT_CHECK_OFF; 328 FPU_code_access_ok(1); 329 FPU_get_user(address, (signed char __user *)(*fpu_eip)); 330 RE_ENTRANT_CHECK_ON; 331 (*fpu_eip)++; 332 break; 333 case 2: 334 /* 16 bit displacement */ 335 RE_ENTRANT_CHECK_OFF; 336 FPU_code_access_ok(2); 337 FPU_get_user(address, (unsigned short __user *)(*fpu_eip)); 338 (*fpu_eip) += 2; 339 RE_ENTRANT_CHECK_ON; 340 break; 341 case 3: 342 /* Not legal for the FPU */ 343 EXCEPTION(EX_Invalid); 344 break; 345 } 346 switch (rm) { 347 case 0: 348 address += FPU_info->regs->bx + FPU_info->regs->si; 349 break; 350 case 1: 351 address += FPU_info->regs->bx + FPU_info->regs->di; 352 break; 353 case 2: 354 address += FPU_info->regs->bp + FPU_info->regs->si; 355 if (addr_modes.override.segment == PREFIX_DEFAULT) 356 addr_modes.override.segment = PREFIX_SS_; 357 break; 358 case 3: 359 address += FPU_info->regs->bp + FPU_info->regs->di; 360 if (addr_modes.override.segment == PREFIX_DEFAULT) 361 addr_modes.override.segment = PREFIX_SS_; 362 break; 363 case 4: 364 address += FPU_info->regs->si; 365 break; 366 case 5: 367 address += FPU_info->regs->di; 368 break; 369 case 6: 370 address += FPU_info->regs->bp; 371 if (addr_modes.override.segment == PREFIX_DEFAULT) 372 addr_modes.override.segment = PREFIX_SS_; 373 break; 374 case 7: 375 address += FPU_info->regs->bx; 376 break; 377 } 378 379 add_segment: 380 address &= 0xffff; 381 382 addr->offset = address; 383 384 switch (addr_modes.default_mode) { 385 case 0: 386 break; 387 case VM86: 388 address += vm86_segment(addr_modes.override.segment, addr); 389 break; 390 case PM16: 391 case SEG32: 392 address = pm_address(FPU_modrm, addr_modes.override.segment, 393 addr, address); 394 break; 395 default: 396 EXCEPTION(EX_INTERNAL | 0x131); 397 } 398 399 return (void __user *)address; 400 } 401