cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

fpu_helper.c (14605B)


      1/*
      2 *  TriCore emulation for qemu: fpu helper.
      3 *
      4 *  Copyright (c) 2016 Bastian Koppelmann University of Paderborn
      5 *
      6 * This library is free software; you can redistribute it and/or
      7 * modify it under the terms of the GNU Lesser General Public
      8 * License as published by the Free Software Foundation; either
      9 * version 2.1 of the License, or (at your option) any later version.
     10 *
     11 * This library is distributed in the hope that it will be useful,
     12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14 * Lesser General Public License for more details.
     15 *
     16 * You should have received a copy of the GNU Lesser General Public
     17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
     18 */
     19
     20#include "qemu/osdep.h"
     21#include "cpu.h"
     22#include "exec/helper-proto.h"
     23#include "fpu/softfloat.h"
     24
     25#define QUIET_NAN 0x7fc00000
     26#define ADD_NAN   0x7fc00001
     27#define SQRT_NAN  0x7fc00004
     28#define DIV_NAN   0x7fc00008
     29#define MUL_NAN   0x7fc00002
     30#define FPU_FS PSW_USB_C
     31#define FPU_FI PSW_USB_V
     32#define FPU_FV PSW_USB_SV
     33#define FPU_FZ PSW_USB_AV
     34#define FPU_FU PSW_USB_SAV
     35
     36#define float32_sqrt_nan make_float32(SQRT_NAN)
     37#define float32_quiet_nan make_float32(QUIET_NAN)
     38
     39/* we don't care about input_denormal */
     40static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
     41{
     42    return get_float_exception_flags(&env->fp_status)
     43           & (float_flag_invalid
     44              | float_flag_overflow
     45              | float_flag_underflow
     46              | float_flag_output_denormal
     47              | float_flag_divbyzero
     48              | float_flag_inexact);
     49}
     50
     51static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2,
     52                                           float32 arg3, float32 result,
     53                                           uint32_t muladd_negate_c)
     54{
     55    uint32_t aSign, bSign, cSign;
     56    uint32_t aExp, bExp, cExp;
     57
     58    if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) ||
     59        float32_is_any_nan(arg3)) {
     60        return QUIET_NAN;
     61    } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) {
     62        return MUL_NAN;
     63    } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) {
     64        return MUL_NAN;
     65    } else {
     66        aSign = arg1 >> 31;
     67        bSign = arg2 >> 31;
     68        cSign = arg3 >> 31;
     69
     70        aExp = (arg1 >> 23) & 0xff;
     71        bExp = (arg2 >> 23) & 0xff;
     72        cExp = (arg3 >> 23) & 0xff;
     73
     74        if (muladd_negate_c) {
     75            cSign ^= 1;
     76        }
     77        if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) {
     78            if (aSign ^ bSign ^ cSign) {
     79                return ADD_NAN;
     80            }
     81        }
     82    }
     83
     84    return result;
     85}
     86
     87static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags)
     88{
     89    uint8_t some_excp = 0;
     90    set_float_exception_flags(0, &env->fp_status);
     91
     92    if (flags & float_flag_invalid) {
     93        env->FPU_FI = 1 << 31;
     94        some_excp = 1;
     95    }
     96
     97    if (flags & float_flag_overflow) {
     98        env->FPU_FV = 1 << 31;
     99        some_excp = 1;
    100    }
    101
    102    if (flags & float_flag_underflow || flags & float_flag_output_denormal) {
    103        env->FPU_FU = 1 << 31;
    104        some_excp = 1;
    105    }
    106
    107    if (flags & float_flag_divbyzero) {
    108        env->FPU_FZ = 1 << 31;
    109        some_excp = 1;
    110    }
    111
    112    if (flags & float_flag_inexact || flags & float_flag_output_denormal) {
    113        env->PSW |= 1 << 26;
    114        some_excp = 1;
    115    }
    116
    117    env->FPU_FS = some_excp;
    118}
    119
    120#define FADD_SUB(op)                                                           \
    121uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2)          \
    122{                                                                              \
    123    float32 arg1 = make_float32(r1);                                           \
    124    float32 arg2 = make_float32(r2);                                           \
    125    uint32_t flags;                                                            \
    126    float32 f_result;                                                          \
    127                                                                               \
    128    f_result = float32_##op(arg2, arg1, &env->fp_status);                      \
    129    flags = f_get_excp_flags(env);                                             \
    130    if (flags) {                                                               \
    131        /* If the output is a NaN, but the inputs aren't,                      \
    132           we return a unique value.  */                                       \
    133        if ((flags & float_flag_invalid)                                       \
    134            && !float32_is_any_nan(arg1)                                       \
    135            && !float32_is_any_nan(arg2)) {                                    \
    136            f_result = ADD_NAN;                                                \
    137        }                                                                      \
    138        f_update_psw_flags(env, flags);                                        \
    139    } else {                                                                   \
    140        env->FPU_FS = 0;                                                       \
    141    }                                                                          \
    142    return (uint32_t)f_result;                                                 \
    143}
    144FADD_SUB(add)
    145FADD_SUB(sub)
    146
    147uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
    148{
    149    uint32_t flags;
    150    float32 arg1 = make_float32(r1);
    151    float32 arg2 = make_float32(r2);
    152    float32 f_result;
    153
    154    f_result = float32_mul(arg1, arg2, &env->fp_status);
    155
    156    flags = f_get_excp_flags(env);
    157    if (flags) {
    158        /* If the output is a NaN, but the inputs aren't,
    159           we return a unique value.  */
    160        if ((flags & float_flag_invalid)
    161            && !float32_is_any_nan(arg1)
    162            && !float32_is_any_nan(arg2)) {
    163                f_result = MUL_NAN;
    164        }
    165        f_update_psw_flags(env, flags);
    166    } else {
    167        env->FPU_FS = 0;
    168    }
    169    return (uint32_t)f_result;
    170
    171}
    172
    173/*
    174 * Target TriCore QSEED.F significand Lookup Table
    175 *
    176 * The QSEED.F output significand depends on the least-significant
    177 * exponent bit and the 6 most-significant significand bits.
    178 *
    179 * IEEE 754 float datatype
    180 * partitioned into Sign (S), Exponent (E) and Significand (M):
    181 *
    182 * S   E E E E E E E E   M M M M M M ...
    183 *    |             |               |
    184 *    +------+------+-------+-------+
    185 *           |              |
    186 *          for        lookup table
    187 *      calculating     index for
    188 *        output E       output M
    189 *
    190 * This lookup table was extracted by analyzing QSEED output
    191 * from the real hardware
    192 */
    193static const uint8_t target_qseed_significand_table[128] = {
    194    253, 252, 245, 244, 239, 238, 231, 230, 225, 224, 217, 216,
    195    211, 210, 205, 204, 201, 200, 195, 194, 189, 188, 185, 184,
    196    179, 178, 175, 174, 169, 168, 165, 164, 161, 160, 157, 156,
    197    153, 152, 149, 148, 145, 144, 141, 140, 137, 136, 133, 132,
    198    131, 130, 127, 126, 123, 122, 121, 120, 117, 116, 115, 114,
    199    111, 110, 109, 108, 103, 102, 99, 98, 93, 92, 89, 88, 83,
    200    82, 79, 78, 75, 74, 71, 70, 67, 66, 63, 62, 59, 58, 55,
    201    54, 53, 52, 49, 48, 45, 44, 43, 42, 39, 38, 37, 36, 33,
    202    32, 31, 30, 27, 26, 25, 24, 23, 22, 19, 18, 17, 16, 15,
    203    14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2
    204};
    205
    206uint32_t helper_qseed(CPUTriCoreState *env, uint32_t r1)
    207{
    208    uint32_t arg1, S, E, M, E_minus_one, m_idx;
    209    uint32_t new_E, new_M, new_S, result;
    210
    211    arg1 = make_float32(r1);
    212
    213    /* fetch IEEE-754 fields S, E and the uppermost 6-bit of M */
    214    S = extract32(arg1, 31, 1);
    215    E = extract32(arg1, 23, 8);
    216    M = extract32(arg1, 17, 6);
    217
    218    if (float32_is_any_nan(arg1)) {
    219        result = float32_quiet_nan;
    220    } else if (float32_is_zero_or_denormal(arg1)) {
    221        if (float32_is_neg(arg1)) {
    222            result = float32_infinity | (1 << 31);
    223        } else {
    224            result = float32_infinity;
    225        }
    226    } else if (float32_is_neg(arg1)) {
    227        result = float32_sqrt_nan;
    228    } else if (float32_is_infinity(arg1)) {
    229        result = float32_zero;
    230    } else {
    231        E_minus_one = E - 1;
    232        m_idx = ((E_minus_one & 1) << 6) | M;
    233        new_S = S;
    234        new_E = 0xBD - E_minus_one / 2;
    235        new_M = target_qseed_significand_table[m_idx];
    236
    237        result = 0;
    238        result = deposit32(result, 31, 1, new_S);
    239        result = deposit32(result, 23, 8, new_E);
    240        result = deposit32(result, 15, 8, new_M);
    241    }
    242
    243    if (float32_is_signaling_nan(arg1, &env->fp_status)
    244        || result == float32_sqrt_nan) {
    245        env->FPU_FI = 1 << 31;
    246        env->FPU_FS = 1;
    247    } else {
    248        env->FPU_FS = 0;
    249    }
    250
    251    return (uint32_t) result;
    252}
    253
    254uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
    255{
    256    uint32_t flags;
    257    float32 arg1 = make_float32(r1);
    258    float32 arg2 = make_float32(r2);
    259    float32 f_result;
    260
    261    f_result = float32_div(arg1, arg2 , &env->fp_status);
    262
    263    flags = f_get_excp_flags(env);
    264    if (flags) {
    265        /* If the output is a NaN, but the inputs aren't,
    266           we return a unique value.  */
    267        if ((flags & float_flag_invalid)
    268            && !float32_is_any_nan(arg1)
    269            && !float32_is_any_nan(arg2)) {
    270                f_result = DIV_NAN;
    271        }
    272        f_update_psw_flags(env, flags);
    273    } else {
    274        env->FPU_FS = 0;
    275    }
    276
    277    return (uint32_t)f_result;
    278}
    279
    280uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1,
    281                      uint32_t r2, uint32_t r3)
    282{
    283    uint32_t flags;
    284    float32 arg1 = make_float32(r1);
    285    float32 arg2 = make_float32(r2);
    286    float32 arg3 = make_float32(r3);
    287    float32 f_result;
    288
    289    f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status);
    290
    291    flags = f_get_excp_flags(env);
    292    if (flags) {
    293        if (flags & float_flag_invalid) {
    294            arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
    295            arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
    296            arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
    297            f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0);
    298        }
    299        f_update_psw_flags(env, flags);
    300    } else {
    301        env->FPU_FS = 0;
    302    }
    303    return (uint32_t)f_result;
    304}
    305
    306uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1,
    307                      uint32_t r2, uint32_t r3)
    308{
    309    uint32_t flags;
    310    float32 arg1 = make_float32(r1);
    311    float32 arg2 = make_float32(r2);
    312    float32 arg3 = make_float32(r3);
    313    float32 f_result;
    314
    315    f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product,
    316                              &env->fp_status);
    317
    318    flags = f_get_excp_flags(env);
    319    if (flags) {
    320        if (flags & float_flag_invalid) {
    321            arg1 = float32_squash_input_denormal(arg1, &env->fp_status);
    322            arg2 = float32_squash_input_denormal(arg2, &env->fp_status);
    323            arg3 = float32_squash_input_denormal(arg3, &env->fp_status);
    324
    325            f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1);
    326        }
    327        f_update_psw_flags(env, flags);
    328    } else {
    329        env->FPU_FS = 0;
    330    }
    331    return (uint32_t)f_result;
    332}
    333
    334uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
    335{
    336    uint32_t result, flags;
    337    float32 arg1 = make_float32(r1);
    338    float32 arg2 = make_float32(r2);
    339
    340    set_flush_inputs_to_zero(0, &env->fp_status);
    341
    342    result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
    343    result |= float32_is_denormal(arg1) << 4;
    344    result |= float32_is_denormal(arg2) << 5;
    345
    346    flags = f_get_excp_flags(env);
    347    if (flags) {
    348        f_update_psw_flags(env, flags);
    349    } else {
    350        env->FPU_FS = 0;
    351    }
    352
    353    set_flush_inputs_to_zero(1, &env->fp_status);
    354    return result;
    355}
    356
    357uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
    358{
    359    float32 f_arg = make_float32(arg);
    360    int32_t result, flags;
    361
    362    result = float32_to_int32(f_arg, &env->fp_status);
    363
    364    flags = f_get_excp_flags(env);
    365    if (flags) {
    366        if (float32_is_any_nan(f_arg)) {
    367            result = 0;
    368        }
    369        f_update_psw_flags(env, flags);
    370    } else {
    371        env->FPU_FS = 0;
    372    }
    373    return (uint32_t)result;
    374}
    375
    376uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
    377{
    378    float32 f_result;
    379    uint32_t flags;
    380    f_result = int32_to_float32(arg, &env->fp_status);
    381
    382    flags = f_get_excp_flags(env);
    383    if (flags) {
    384        f_update_psw_flags(env, flags);
    385    } else {
    386        env->FPU_FS = 0;
    387    }
    388    return (uint32_t)f_result;
    389}
    390
    391uint32_t helper_utof(CPUTriCoreState *env, uint32_t arg)
    392{
    393    float32 f_result;
    394    uint32_t flags;
    395
    396    f_result = uint32_to_float32(arg, &env->fp_status);
    397
    398    flags = f_get_excp_flags(env);
    399    if (flags) {
    400        f_update_psw_flags(env, flags);
    401    } else {
    402        env->FPU_FS = 0;
    403    }
    404    return (uint32_t)f_result;
    405}
    406
    407uint32_t helper_ftoiz(CPUTriCoreState *env, uint32_t arg)
    408{
    409    float32 f_arg = make_float32(arg);
    410    uint32_t result;
    411    int32_t flags;
    412
    413    result = float32_to_int32_round_to_zero(f_arg, &env->fp_status);
    414
    415    flags = f_get_excp_flags(env);
    416    if (flags & float_flag_invalid) {
    417        flags &= ~float_flag_inexact;
    418        if (float32_is_any_nan(f_arg)) {
    419            result = 0;
    420        }
    421    }
    422
    423    if (flags) {
    424        f_update_psw_flags(env, flags);
    425    } else {
    426        env->FPU_FS = 0;
    427    }
    428
    429    return result;
    430}
    431
    432uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg)
    433{
    434    float32 f_arg = make_float32(arg);
    435    uint32_t result;
    436    int32_t flags;
    437
    438    result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status);
    439
    440    flags = f_get_excp_flags(env);
    441    if (flags & float_flag_invalid) {
    442        flags &= ~float_flag_inexact;
    443        if (float32_is_any_nan(f_arg)) {
    444            result = 0;
    445        }
    446    } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) {
    447        flags = float_flag_invalid;
    448        result = 0;
    449    }
    450
    451    if (flags) {
    452        f_update_psw_flags(env, flags);
    453    } else {
    454        env->FPU_FS = 0;
    455    }
    456    return result;
    457}
    458
    459void helper_updfl(CPUTriCoreState *env, uint32_t arg)
    460{
    461    env->FPU_FS =  extract32(arg, 7, 1) & extract32(arg, 15, 1);
    462    env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31;
    463    env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31;
    464    env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31;
    465    env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31;
    466    /* clear FX and RM */
    467    env->PSW &= ~(extract32(arg, 10, 1) << 26);
    468    env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26;
    469
    470    fpu_set_state(env);
    471}