linux/arch/x86/math-emu/load_store.c
<<
>>
Prefs
   1/*---------------------------------------------------------------------------+
   2 |  load_store.c                                                             |
   3 |                                                                           |
   4 | This file contains most of the code to interpret the FPU instructions     |
   5 | which load and store from user memory.                                    |
   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 <asm/uaccess.h>
  22
  23#include "fpu_system.h"
  24#include "exception.h"
  25#include "fpu_emu.h"
  26#include "status_w.h"
  27#include "control_w.h"
  28
  29#define _NONE_ 0                /* st0_ptr etc not needed */
  30#define _REG0_ 1                /* Will be storing st(0) */
  31#define _PUSH_ 3                /* Need to check for space to push onto stack */
  32#define _null_ 4                /* Function illegal or not implemented */
  33
  34#define pop_0() { FPU_settag0(TAG_Empty); top++; }
  35
  36static u_char const type_table[32] = {
  37        _PUSH_, _PUSH_, _PUSH_, _PUSH_,
  38        _null_, _null_, _null_, _null_,
  39        _REG0_, _REG0_, _REG0_, _REG0_,
  40        _REG0_, _REG0_, _REG0_, _REG0_,
  41        _NONE_, _null_, _NONE_, _PUSH_,
  42        _NONE_, _PUSH_, _null_, _PUSH_,
  43        _NONE_, _null_, _NONE_, _REG0_,
  44        _NONE_, _REG0_, _NONE_, _REG0_
  45};
  46
  47u_char const data_sizes_16[32] = {
  48        4, 4, 8, 2, 0, 0, 0, 0,
  49        4, 4, 8, 2, 4, 4, 8, 2,
  50        14, 0, 94, 10, 2, 10, 0, 8,
  51        14, 0, 94, 10, 2, 10, 2, 8
  52};
  53
  54static u_char const data_sizes_32[32] = {
  55        4, 4, 8, 2, 0, 0, 0, 0,
  56        4, 4, 8, 2, 4, 4, 8, 2,
  57        28, 0, 108, 10, 2, 10, 0, 8,
  58        28, 0, 108, 10, 2, 10, 2, 8
  59};
  60
  61int FPU_load_store(u_char type, fpu_addr_modes addr_modes,
  62                   void __user * data_address)
  63{
  64        FPU_REG loaded_data;
  65        FPU_REG *st0_ptr;
  66        u_char st0_tag = TAG_Empty;     /* This is just to stop a gcc warning. */
  67        u_char loaded_tag;
  68
  69        st0_ptr = NULL;         /* Initialized just to stop compiler warnings. */
  70
  71        if (addr_modes.default_mode & PROTECTED) {
  72                if (addr_modes.default_mode == SEG32) {
  73                        if (access_limit < data_sizes_32[type])
  74                                math_abort(FPU_info, SIGSEGV);
  75                } else if (addr_modes.default_mode == PM16) {
  76                        if (access_limit < data_sizes_16[type])
  77                                math_abort(FPU_info, SIGSEGV);
  78                }
  79#ifdef PARANOID
  80                else
  81                        EXCEPTION(EX_INTERNAL | 0x140);
  82#endif /* PARANOID */
  83        }
  84
  85        switch (type_table[type]) {
  86        case _NONE_:
  87                break;
  88        case _REG0_:
  89                st0_ptr = &st(0);       /* Some of these instructions pop after
  90                                           storing */
  91                st0_tag = FPU_gettag0();
  92                break;
  93        case _PUSH_:
  94                {
  95                        if (FPU_gettagi(-1) != TAG_Empty) {
  96                                FPU_stack_overflow();
  97                                return 0;
  98                        }
  99                        top--;
 100                        st0_ptr = &st(0);
 101                }
 102                break;
 103        case _null_:
 104                FPU_illegal();
 105                return 0;
 106#ifdef PARANOID
 107        default:
 108                EXCEPTION(EX_INTERNAL | 0x141);
 109                return 0;
 110#endif /* PARANOID */
 111        }
 112
 113        switch (type) {
 114        case 000:               /* fld m32real */
 115                clear_C1();
 116                loaded_tag =
 117                    FPU_load_single((float __user *)data_address, &loaded_data);
 118                if ((loaded_tag == TAG_Special)
 119                    && isNaN(&loaded_data)
 120                    && (real_1op_NaN(&loaded_data) < 0)) {
 121                        top++;
 122                        break;
 123                }
 124                FPU_copy_to_reg0(&loaded_data, loaded_tag);
 125                break;
 126        case 001:               /* fild m32int */
 127                clear_C1();
 128                loaded_tag =
 129                    FPU_load_int32((long __user *)data_address, &loaded_data);
 130                FPU_copy_to_reg0(&loaded_data, loaded_tag);
 131                break;
 132        case 002:               /* fld m64real */
 133                clear_C1();
 134                loaded_tag =
 135                    FPU_load_double((double __user *)data_address,
 136                                    &loaded_data);
 137                if ((loaded_tag == TAG_Special)
 138                    && isNaN(&loaded_data)
 139                    && (real_1op_NaN(&loaded_data) < 0)) {
 140                        top++;
 141                        break;
 142                }
 143                FPU_copy_to_reg0(&loaded_data, loaded_tag);
 144                break;
 145        case 003:               /* fild m16int */
 146                clear_C1();
 147                loaded_tag =
 148                    FPU_load_int16((short __user *)data_address, &loaded_data);
 149                FPU_copy_to_reg0(&loaded_data, loaded_tag);
 150                break;
 151        case 010:               /* fst m32real */
 152                clear_C1();
 153                FPU_store_single(st0_ptr, st0_tag,
 154                                 (float __user *)data_address);
 155                break;
 156        case 011:               /* fist m32int */
 157                clear_C1();
 158                FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address);
 159                break;
 160        case 012:               /* fst m64real */
 161                clear_C1();
 162                FPU_store_double(st0_ptr, st0_tag,
 163                                 (double __user *)data_address);
 164                break;
 165        case 013:               /* fist m16int */
 166                clear_C1();
 167                FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address);
 168                break;
 169        case 014:               /* fstp m32real */
 170                clear_C1();
 171                if (FPU_store_single
 172                    (st0_ptr, st0_tag, (float __user *)data_address))
 173                        pop_0();        /* pop only if the number was actually stored
 174                                           (see the 80486 manual p16-28) */
 175                break;
 176        case 015:               /* fistp m32int */
 177                clear_C1();
 178                if (FPU_store_int32
 179                    (st0_ptr, st0_tag, (long __user *)data_address))
 180                        pop_0();        /* pop only if the number was actually stored
 181                                           (see the 80486 manual p16-28) */
 182                break;
 183        case 016:               /* fstp m64real */
 184                clear_C1();
 185                if (FPU_store_double
 186                    (st0_ptr, st0_tag, (double __user *)data_address))
 187                        pop_0();        /* pop only if the number was actually stored
 188                                           (see the 80486 manual p16-28) */
 189                break;
 190        case 017:               /* fistp m16int */
 191                clear_C1();
 192                if (FPU_store_int16
 193                    (st0_ptr, st0_tag, (short __user *)data_address))
 194                        pop_0();        /* pop only if the number was actually stored
 195                                           (see the 80486 manual p16-28) */
 196                break;
 197        case 020:               /* fldenv  m14/28byte */
 198                fldenv(addr_modes, (u_char __user *) data_address);
 199                /* Ensure that the values just loaded are not changed by
 200                   fix-up operations. */
 201                return 1;
 202        case 022:               /* frstor m94/108byte */
 203                frstor(addr_modes, (u_char __user *) data_address);
 204                /* Ensure that the values just loaded are not changed by
 205                   fix-up operations. */
 206                return 1;
 207        case 023:               /* fbld m80dec */
 208                clear_C1();
 209                loaded_tag = FPU_load_bcd((u_char __user *) data_address);
 210                FPU_settag0(loaded_tag);
 211                break;
 212        case 024:               /* fldcw */
 213                RE_ENTRANT_CHECK_OFF;
 214                FPU_access_ok(VERIFY_READ, data_address, 2);
 215                FPU_get_user(control_word,
 216                             (unsigned short __user *)data_address);
 217                RE_ENTRANT_CHECK_ON;
 218                if (partial_status & ~control_word & CW_Exceptions)
 219                        partial_status |= (SW_Summary | SW_Backward);
 220                else
 221                        partial_status &= ~(SW_Summary | SW_Backward);
 222#ifdef PECULIAR_486
 223                control_word |= 0x40;   /* An 80486 appears to always set this bit */
 224#endif /* PECULIAR_486 */
 225                return 1;
 226        case 025:               /* fld m80real */
 227                clear_C1();
 228                loaded_tag =
 229                    FPU_load_extended((long double __user *)data_address, 0);
 230                FPU_settag0(loaded_tag);
 231                break;
 232        case 027:               /* fild m64int */
 233                clear_C1();
 234                loaded_tag = FPU_load_int64((long long __user *)data_address);
 235                if (loaded_tag == TAG_Error)
 236                        return 0;
 237                FPU_settag0(loaded_tag);
 238                break;
 239        case 030:               /* fstenv  m14/28byte */
 240                fstenv(addr_modes, (u_char __user *) data_address);
 241                return 1;
 242        case 032:               /* fsave */
 243                fsave(addr_modes, (u_char __user *) data_address);
 244                return 1;
 245        case 033:               /* fbstp m80dec */
 246                clear_C1();
 247                if (FPU_store_bcd
 248                    (st0_ptr, st0_tag, (u_char __user *) data_address))
 249                        pop_0();        /* pop only if the number was actually stored
 250                                           (see the 80486 manual p16-28) */
 251                break;
 252        case 034:               /* fstcw m16int */
 253                RE_ENTRANT_CHECK_OFF;
 254                FPU_access_ok(VERIFY_WRITE, data_address, 2);
 255                FPU_put_user(control_word,
 256                             (unsigned short __user *)data_address);
 257                RE_ENTRANT_CHECK_ON;
 258                return 1;
 259        case 035:               /* fstp m80real */
 260                clear_C1();
 261                if (FPU_store_extended
 262                    (st0_ptr, st0_tag, (long double __user *)data_address))
 263                        pop_0();        /* pop only if the number was actually stored
 264                                           (see the 80486 manual p16-28) */
 265                break;
 266        case 036:               /* fstsw m2byte */
 267                RE_ENTRANT_CHECK_OFF;
 268                FPU_access_ok(VERIFY_WRITE, data_address, 2);
 269                FPU_put_user(status_word(),
 270                             (unsigned short __user *)data_address);
 271                RE_ENTRANT_CHECK_ON;
 272                return 1;
 273        case 037:               /* fistp m64int */
 274                clear_C1();
 275                if (FPU_store_int64
 276                    (st0_ptr, st0_tag, (long long __user *)data_address))
 277                        pop_0();        /* pop only if the number was actually stored
 278                                           (see the 80486 manual p16-28) */
 279                break;
 280        }
 281        return 0;
 282}
 283