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