Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*---------------------------------------------------------------------------+
0003  |  get_address.c                                                            |
0004  |                                                                           |
0005  | Get the effective address from an FPU instruction.                        |
0006  |                                                                           |
0007  | Copyright (C) 1992,1993,1994,1997                                         |
0008  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
0009  |                       Australia.  E-mail   billm@suburbia.net             |
0010  |                                                                           |
0011  |                                                                           |
0012  +---------------------------------------------------------------------------*/
0013 
0014 /*---------------------------------------------------------------------------+
0015  | Note:                                                                     |
0016  |    The file contains code which accesses user memory.                     |
0017  |    Emulator static data may change when user memory is accessed, due to   |
0018  |    other processes using the emulator while swapping is in progress.      |
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),   /* dummy, not saved on stack */
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 /* Decode the SIB byte. This function assumes mod != 0 */
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));   /* The SIB byte */
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; /* No base register */
0088     else
0089         offset = REG_(base);
0090 
0091     if (index == 4) {
0092         /* No index register */
0093         /* A non-zero ss is illegal */
0094         if (ss)
0095             EXCEPTION(EX_Invalid);
0096     } else {
0097         offset += (REG_(index)) << ss;
0098     }
0099 
0100     if (mod == 1) {
0101         /* 8 bit signed displacement */
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) { /* The second condition also has mod==0 */
0110         /* 32 bit displacement */
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 /* PARANOID */
0132     addr->selector = VM86_REG_(segment);
0133     return (unsigned long)VM86_REG_(segment) << 4;
0134 }
0135 
0136 /* This should work for 16 and 32 bit protected mode. */
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     /* segment is unsigned, so this also detects if segment was 0: */
0147     if (segment > PREFIX_SS_) {
0148         EXCEPTION(EX_INTERNAL | 0x132);
0149         math_abort(FPU_info, SIGSEGV);
0150     }
0151 #endif /* PARANOID */
0152 
0153     switch (segment) {
0154     case PREFIX_GS_ - 1:
0155         /* user gs handling can be lazy, use special accessors */
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        MOD R/M byte:  MOD == 3 has a special use for the FPU
0196                       SIB byte used iff R/M = 100b
0197 
0198        7   6   5   4   3   2   1   0
0199        .....   .........   .........
0200         MOD    OPCODE(2)     R/M
0201 
0202        SIB byte
0203 
0204        7   6   5   4   3   2   1   0
0205        .....   .........   .........
0206         SS      INDEX        BASE
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;    /* Initialized just to stop compiler warnings. */
0217 
0218     /* Memory accessed via the cs selector is write protected
0219        in `non-segmented' 32 bit protected mode. */
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;    /* Default, for 32 bit non-segmented mode. */
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 = &REG_(rm);
0233         switch (mod) {
0234         case 0:
0235             if (rm == 5) {
0236                 /* Special case: disp32 */
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; /* Just return the contents
0248                                of the cpu register */
0249                 addr->offset = address;
0250                 return (void __user *)address;
0251             }
0252         case 1:
0253             /* 8 bit signed displacement */
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             /* 32 bit displacement */
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             /* Not legal for the FPU */
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;    /* Default used for mod == 0 */
0301 
0302     /* Memory accessed via the cs selector is write protected
0303        in `non-segmented' 32 bit protected mode. */
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;    /* Default, for 32 bit non-segmented mode. */
0310 
0311     mod = (FPU_modrm >> 6) & 3;
0312 
0313     switch (mod) {
0314     case 0:
0315         if (rm == 6) {
0316             /* Special case: disp16 */
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         /* 8 bit signed displacement */
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         /* 16 bit displacement */
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         /* Not legal for the FPU */
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 }