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