0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 #include <linux/stddef.h>
0022
0023 #include <linux/uaccess.h>
0024 #include <asm/vm86.h>
0025
0026 #include "fpu_system.h"
0027 #include "exception.h"
0028 #include "fpu_emu.h"
0029
0030 #define FPU_WRITE_BIT 0x10
0031
0032 static int reg_offset[] = {
0033 offsetof(struct pt_regs, ax),
0034 offsetof(struct pt_regs, cx),
0035 offsetof(struct pt_regs, dx),
0036 offsetof(struct pt_regs, bx),
0037 offsetof(struct pt_regs, sp),
0038 offsetof(struct pt_regs, bp),
0039 offsetof(struct pt_regs, si),
0040 offsetof(struct pt_regs, di)
0041 };
0042
0043 #define REG_(x) (*(long *)(reg_offset[(x)] + (u_char *)FPU_info->regs))
0044
0045 static int reg_offset_vm86[] = {
0046 offsetof(struct pt_regs, cs),
0047 offsetof(struct kernel_vm86_regs, ds),
0048 offsetof(struct kernel_vm86_regs, es),
0049 offsetof(struct kernel_vm86_regs, fs),
0050 offsetof(struct kernel_vm86_regs, gs),
0051 offsetof(struct pt_regs, ss),
0052 offsetof(struct kernel_vm86_regs, ds)
0053 };
0054
0055 #define VM86_REG_(x) (*(unsigned short *) \
0056 (reg_offset_vm86[((unsigned)x)] + (u_char *)FPU_info->regs))
0057
0058 static int reg_offset_pm[] = {
0059 offsetof(struct pt_regs, cs),
0060 offsetof(struct pt_regs, ds),
0061 offsetof(struct pt_regs, es),
0062 offsetof(struct pt_regs, fs),
0063 offsetof(struct pt_regs, ds),
0064 offsetof(struct pt_regs, ss),
0065 offsetof(struct pt_regs, ds)
0066 };
0067
0068 #define PM_REG_(x) (*(unsigned short *) \
0069 (reg_offset_pm[((unsigned)x)] + (u_char *)FPU_info->regs))
0070
0071
0072 static int sib(int mod, unsigned long *fpu_eip)
0073 {
0074 u_char ss, index, base;
0075 long offset;
0076
0077 RE_ENTRANT_CHECK_OFF;
0078 FPU_code_access_ok(1);
0079 FPU_get_user(base, (u_char __user *) (*fpu_eip));
0080 RE_ENTRANT_CHECK_ON;
0081 (*fpu_eip)++;
0082 ss = base >> 6;
0083 index = (base >> 3) & 7;
0084 base &= 7;
0085
0086 if ((mod == 0) && (base == 5))
0087 offset = 0;
0088 else
0089 offset = REG_(base);
0090
0091 if (index == 4) {
0092
0093
0094 if (ss)
0095 EXCEPTION(EX_Invalid);
0096 } else {
0097 offset += (REG_(index)) << ss;
0098 }
0099
0100 if (mod == 1) {
0101
0102 long displacement;
0103 RE_ENTRANT_CHECK_OFF;
0104 FPU_code_access_ok(1);
0105 FPU_get_user(displacement, (signed char __user *)(*fpu_eip));
0106 offset += displacement;
0107 RE_ENTRANT_CHECK_ON;
0108 (*fpu_eip)++;
0109 } else if (mod == 2 || base == 5) {
0110
0111 long displacement;
0112 RE_ENTRANT_CHECK_OFF;
0113 FPU_code_access_ok(4);
0114 FPU_get_user(displacement, (long __user *)(*fpu_eip));
0115 offset += displacement;
0116 RE_ENTRANT_CHECK_ON;
0117 (*fpu_eip) += 4;
0118 }
0119
0120 return offset;
0121 }
0122
0123 static unsigned long vm86_segment(u_char segment, struct address *addr)
0124 {
0125 segment--;
0126 #ifdef PARANOID
0127 if (segment > PREFIX_SS_) {
0128 EXCEPTION(EX_INTERNAL | 0x130);
0129 math_abort(FPU_info, SIGSEGV);
0130 }
0131 #endif
0132 addr->selector = VM86_REG_(segment);
0133 return (unsigned long)VM86_REG_(segment) << 4;
0134 }
0135
0136
0137 static long pm_address(u_char FPU_modrm, u_char segment,
0138 struct address *addr, long offset)
0139 {
0140 struct desc_struct descriptor;
0141 unsigned long base_address, limit, address, seg_top;
0142
0143 segment--;
0144
0145 #ifdef PARANOID
0146
0147 if (segment > PREFIX_SS_) {
0148 EXCEPTION(EX_INTERNAL | 0x132);
0149 math_abort(FPU_info, SIGSEGV);
0150 }
0151 #endif
0152
0153 switch (segment) {
0154 case PREFIX_GS_ - 1:
0155
0156 savesegment(gs, addr->selector);
0157 break;
0158 default:
0159 addr->selector = PM_REG_(segment);
0160 }
0161
0162 descriptor = FPU_get_ldt_descriptor(addr->selector);
0163 base_address = seg_get_base(&descriptor);
0164 address = base_address + offset;
0165 limit = seg_get_limit(&descriptor) + 1;
0166 limit *= seg_get_granularity(&descriptor);
0167 limit += base_address - 1;
0168 if (limit < base_address)
0169 limit = 0xffffffff;
0170
0171 if (seg_expands_down(&descriptor)) {
0172 if (descriptor.g) {
0173 seg_top = 0xffffffff;
0174 } else {
0175 seg_top = base_address + (1 << 20);
0176 if (seg_top < base_address)
0177 seg_top = 0xffffffff;
0178 }
0179 access_limit =
0180 (address <= limit) || (address >= seg_top) ? 0 :
0181 ((seg_top - address) >= 255 ? 255 : seg_top - address);
0182 } else {
0183 access_limit =
0184 (address > limit) || (address < base_address) ? 0 :
0185 ((limit - address) >= 254 ? 255 : limit - address + 1);
0186 }
0187 if (seg_execute_only(&descriptor) ||
0188 (!seg_writable(&descriptor) && (FPU_modrm & FPU_WRITE_BIT))) {
0189 access_limit = 0;
0190 }
0191 return address;
0192 }
0193
0194
0195
0196
0197
0198
0199
0200
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210 void __user *FPU_get_address(u_char FPU_modrm, unsigned long *fpu_eip,
0211 struct address *addr, fpu_addr_modes addr_modes)
0212 {
0213 u_char mod;
0214 unsigned rm = FPU_modrm & 7;
0215 long *cpu_reg_ptr;
0216 int address = 0;
0217
0218
0219
0220 if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
0221 && (addr_modes.override.segment == PREFIX_CS_)) {
0222 math_abort(FPU_info, SIGSEGV);
0223 }
0224
0225 addr->selector = FPU_DS;
0226
0227 mod = (FPU_modrm >> 6) & 3;
0228
0229 if (rm == 4 && mod != 3) {
0230 address = sib(mod, fpu_eip);
0231 } else {
0232 cpu_reg_ptr = ®_(rm);
0233 switch (mod) {
0234 case 0:
0235 if (rm == 5) {
0236
0237 RE_ENTRANT_CHECK_OFF;
0238 FPU_code_access_ok(4);
0239 FPU_get_user(address,
0240 (unsigned long __user
0241 *)(*fpu_eip));
0242 (*fpu_eip) += 4;
0243 RE_ENTRANT_CHECK_ON;
0244 addr->offset = address;
0245 return (void __user *)address;
0246 } else {
0247 address = *cpu_reg_ptr;
0248
0249 addr->offset = address;
0250 return (void __user *)address;
0251 }
0252 case 1:
0253
0254 RE_ENTRANT_CHECK_OFF;
0255 FPU_code_access_ok(1);
0256 FPU_get_user(address, (signed char __user *)(*fpu_eip));
0257 RE_ENTRANT_CHECK_ON;
0258 (*fpu_eip)++;
0259 break;
0260 case 2:
0261
0262 RE_ENTRANT_CHECK_OFF;
0263 FPU_code_access_ok(4);
0264 FPU_get_user(address, (long __user *)(*fpu_eip));
0265 (*fpu_eip) += 4;
0266 RE_ENTRANT_CHECK_ON;
0267 break;
0268 case 3:
0269
0270 EXCEPTION(EX_Invalid);
0271 }
0272 address += *cpu_reg_ptr;
0273 }
0274
0275 addr->offset = address;
0276
0277 switch (addr_modes.default_mode) {
0278 case 0:
0279 break;
0280 case VM86:
0281 address += vm86_segment(addr_modes.override.segment, addr);
0282 break;
0283 case PM16:
0284 case SEG32:
0285 address = pm_address(FPU_modrm, addr_modes.override.segment,
0286 addr, address);
0287 break;
0288 default:
0289 EXCEPTION(EX_INTERNAL | 0x133);
0290 }
0291
0292 return (void __user *)address;
0293 }
0294
0295 void __user *FPU_get_address_16(u_char FPU_modrm, unsigned long *fpu_eip,
0296 struct address *addr, fpu_addr_modes addr_modes)
0297 {
0298 u_char mod;
0299 unsigned rm = FPU_modrm & 7;
0300 int address = 0;
0301
0302
0303
0304 if (!addr_modes.default_mode && (FPU_modrm & FPU_WRITE_BIT)
0305 && (addr_modes.override.segment == PREFIX_CS_)) {
0306 math_abort(FPU_info, SIGSEGV);
0307 }
0308
0309 addr->selector = FPU_DS;
0310
0311 mod = (FPU_modrm >> 6) & 3;
0312
0313 switch (mod) {
0314 case 0:
0315 if (rm == 6) {
0316
0317 RE_ENTRANT_CHECK_OFF;
0318 FPU_code_access_ok(2);
0319 FPU_get_user(address,
0320 (unsigned short __user *)(*fpu_eip));
0321 (*fpu_eip) += 2;
0322 RE_ENTRANT_CHECK_ON;
0323 goto add_segment;
0324 }
0325 break;
0326 case 1:
0327
0328 RE_ENTRANT_CHECK_OFF;
0329 FPU_code_access_ok(1);
0330 FPU_get_user(address, (signed char __user *)(*fpu_eip));
0331 RE_ENTRANT_CHECK_ON;
0332 (*fpu_eip)++;
0333 break;
0334 case 2:
0335
0336 RE_ENTRANT_CHECK_OFF;
0337 FPU_code_access_ok(2);
0338 FPU_get_user(address, (unsigned short __user *)(*fpu_eip));
0339 (*fpu_eip) += 2;
0340 RE_ENTRANT_CHECK_ON;
0341 break;
0342 case 3:
0343
0344 EXCEPTION(EX_Invalid);
0345 break;
0346 }
0347 switch (rm) {
0348 case 0:
0349 address += FPU_info->regs->bx + FPU_info->regs->si;
0350 break;
0351 case 1:
0352 address += FPU_info->regs->bx + FPU_info->regs->di;
0353 break;
0354 case 2:
0355 address += FPU_info->regs->bp + FPU_info->regs->si;
0356 if (addr_modes.override.segment == PREFIX_DEFAULT)
0357 addr_modes.override.segment = PREFIX_SS_;
0358 break;
0359 case 3:
0360 address += FPU_info->regs->bp + FPU_info->regs->di;
0361 if (addr_modes.override.segment == PREFIX_DEFAULT)
0362 addr_modes.override.segment = PREFIX_SS_;
0363 break;
0364 case 4:
0365 address += FPU_info->regs->si;
0366 break;
0367 case 5:
0368 address += FPU_info->regs->di;
0369 break;
0370 case 6:
0371 address += FPU_info->regs->bp;
0372 if (addr_modes.override.segment == PREFIX_DEFAULT)
0373 addr_modes.override.segment = PREFIX_SS_;
0374 break;
0375 case 7:
0376 address += FPU_info->regs->bx;
0377 break;
0378 }
0379
0380 add_segment:
0381 address &= 0xffff;
0382
0383 addr->offset = address;
0384
0385 switch (addr_modes.default_mode) {
0386 case 0:
0387 break;
0388 case VM86:
0389 address += vm86_segment(addr_modes.override.segment, addr);
0390 break;
0391 case PM16:
0392 case SEG32:
0393 address = pm_address(FPU_modrm, addr_modes.override.segment,
0394 addr, address);
0395 break;
0396 default:
0397 EXCEPTION(EX_INTERNAL | 0x131);
0398 }
0399
0400 return (void __user *)address;
0401 }