qemu/target/mips/tcg/ldst_helper.c
<<
>>
Prefs
   1/*
   2 *  MIPS emulation load/store helpers for QEMU.
   3 *
   4 *  Copyright (c) 2004-2005 Jocelyn Mayer
   5 *
   6 * SPDX-License-Identifier: LGPL-2.1-or-later
   7 *
   8 * This library is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU Lesser General Public
  10 * License as published by the Free Software Foundation; either
  11 * version 2.1 of the License, or (at your option) any later version.
  12 *
  13 * This library is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * Lesser General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU Lesser General Public
  19 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  20 *
  21 */
  22
  23#include "qemu/osdep.h"
  24#include "cpu.h"
  25#include "exec/helper-proto.h"
  26#include "exec/exec-all.h"
  27#include "exec/memop.h"
  28#include "internal.h"
  29
  30#ifndef CONFIG_USER_ONLY
  31
  32#define HELPER_LD_ATOMIC(name, insn, almask, do_cast)                         \
  33target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx)  \
  34{                                                                             \
  35    if (arg & almask) {                                                       \
  36        if (!(env->hflags & MIPS_HFLAG_DM)) {                                 \
  37            env->CP0_BadVAddr = arg;                                          \
  38        }                                                                     \
  39        do_raise_exception(env, EXCP_AdEL, GETPC());                          \
  40    }                                                                         \
  41    env->CP0_LLAddr = cpu_mips_translate_address(env, arg, MMU_DATA_LOAD,     \
  42                                                 GETPC());                    \
  43    env->lladdr = arg;                                                        \
  44    env->llval = do_cast cpu_##insn##_mmuidx_ra(env, arg, mem_idx, GETPC());  \
  45    return env->llval;                                                        \
  46}
  47HELPER_LD_ATOMIC(ll, ldl, 0x3, (target_long)(int32_t))
  48#ifdef TARGET_MIPS64
  49HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong))
  50#endif
  51#undef HELPER_LD_ATOMIC
  52
  53#endif /* !CONFIG_USER_ONLY */
  54
  55static inline bool cpu_is_bigendian(CPUMIPSState *env)
  56{
  57    return extract32(env->CP0_Config0, CP0C0_BE, 1);
  58}
  59
  60static inline target_ulong get_lmask(CPUMIPSState *env,
  61                                     target_ulong value, unsigned bits)
  62{
  63    unsigned mask = (bits / BITS_PER_BYTE) - 1;
  64
  65    value &= mask;
  66
  67    if (!cpu_is_bigendian(env)) {
  68        value ^= mask;
  69    }
  70
  71    return value;
  72}
  73
  74void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
  75                int mem_idx)
  76{
  77    target_ulong lmask = get_lmask(env, arg2, 32);
  78    int dir = cpu_is_bigendian(env) ? 1 : -1;
  79
  80    cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC());
  81
  82    if (lmask <= 2) {
  83        cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 16),
  84                          mem_idx, GETPC());
  85    }
  86
  87    if (lmask <= 1) {
  88        cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 8),
  89                          mem_idx, GETPC());
  90    }
  91
  92    if (lmask == 0) {
  93        cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)arg1,
  94                          mem_idx, GETPC());
  95    }
  96}
  97
  98void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
  99                int mem_idx)
 100{
 101    target_ulong lmask = get_lmask(env, arg2, 32);
 102    int dir = cpu_is_bigendian(env) ? 1 : -1;
 103
 104    cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
 105
 106    if (lmask >= 1) {
 107        cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8),
 108                          mem_idx, GETPC());
 109    }
 110
 111    if (lmask >= 2) {
 112        cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16),
 113                          mem_idx, GETPC());
 114    }
 115
 116    if (lmask == 3) {
 117        cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24),
 118                          mem_idx, GETPC());
 119    }
 120}
 121
 122#if defined(TARGET_MIPS64)
 123/*
 124 * "half" load and stores.  We must do the memory access inline,
 125 * or fault handling won't work.
 126 */
 127
 128void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
 129                int mem_idx)
 130{
 131    target_ulong lmask = get_lmask(env, arg2, 64);
 132    int dir = cpu_is_bigendian(env) ? 1 : -1;
 133
 134    cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC());
 135
 136    if (lmask <= 6) {
 137        cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 48),
 138                          mem_idx, GETPC());
 139    }
 140
 141    if (lmask <= 5) {
 142        cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 40),
 143                          mem_idx, GETPC());
 144    }
 145
 146    if (lmask <= 4) {
 147        cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)(arg1 >> 32),
 148                          mem_idx, GETPC());
 149    }
 150
 151    if (lmask <= 3) {
 152        cpu_stb_mmuidx_ra(env, arg2 + 4 * dir, (uint8_t)(arg1 >> 24),
 153                          mem_idx, GETPC());
 154    }
 155
 156    if (lmask <= 2) {
 157        cpu_stb_mmuidx_ra(env, arg2 + 5 * dir, (uint8_t)(arg1 >> 16),
 158                          mem_idx, GETPC());
 159    }
 160
 161    if (lmask <= 1) {
 162        cpu_stb_mmuidx_ra(env, arg2 + 6 * dir, (uint8_t)(arg1 >> 8),
 163                          mem_idx, GETPC());
 164    }
 165
 166    if (lmask <= 0) {
 167        cpu_stb_mmuidx_ra(env, arg2 + 7 * dir, (uint8_t)arg1,
 168                          mem_idx, GETPC());
 169    }
 170}
 171
 172void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2,
 173                int mem_idx)
 174{
 175    target_ulong lmask = get_lmask(env, arg2, 64);
 176    int dir = cpu_is_bigendian(env) ? 1 : -1;
 177
 178    cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC());
 179
 180    if (lmask >= 1) {
 181        cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8),
 182                          mem_idx, GETPC());
 183    }
 184
 185    if (lmask >= 2) {
 186        cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16),
 187                          mem_idx, GETPC());
 188    }
 189
 190    if (lmask >= 3) {
 191        cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24),
 192                          mem_idx, GETPC());
 193    }
 194
 195    if (lmask >= 4) {
 196        cpu_stb_mmuidx_ra(env, arg2 - 4 * dir, (uint8_t)(arg1 >> 32),
 197                          mem_idx, GETPC());
 198    }
 199
 200    if (lmask >= 5) {
 201        cpu_stb_mmuidx_ra(env, arg2 - 5 * dir, (uint8_t)(arg1 >> 40),
 202                          mem_idx, GETPC());
 203    }
 204
 205    if (lmask >= 6) {
 206        cpu_stb_mmuidx_ra(env, arg2 - 6 * dir, (uint8_t)(arg1 >> 48),
 207                          mem_idx, GETPC());
 208    }
 209
 210    if (lmask == 7) {
 211        cpu_stb_mmuidx_ra(env, arg2 - 7 * dir, (uint8_t)(arg1 >> 56),
 212                          mem_idx, GETPC());
 213    }
 214}
 215#endif /* TARGET_MIPS64 */
 216
 217static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 };
 218
 219void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
 220                uint32_t mem_idx)
 221{
 222    target_ulong base_reglist = reglist & 0xf;
 223    target_ulong do_r31 = reglist & 0x10;
 224
 225    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
 226        target_ulong i;
 227
 228        for (i = 0; i < base_reglist; i++) {
 229            env->active_tc.gpr[multiple_regs[i]] =
 230                (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC());
 231            addr += 4;
 232        }
 233    }
 234
 235    if (do_r31) {
 236        env->active_tc.gpr[31] =
 237            (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC());
 238    }
 239}
 240
 241void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
 242                uint32_t mem_idx)
 243{
 244    target_ulong base_reglist = reglist & 0xf;
 245    target_ulong do_r31 = reglist & 0x10;
 246
 247    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
 248        target_ulong i;
 249
 250        for (i = 0; i < base_reglist; i++) {
 251            cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
 252                              mem_idx, GETPC());
 253            addr += 4;
 254        }
 255    }
 256
 257    if (do_r31) {
 258        cpu_stw_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
 259    }
 260}
 261
 262#if defined(TARGET_MIPS64)
 263void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
 264                uint32_t mem_idx)
 265{
 266    target_ulong base_reglist = reglist & 0xf;
 267    target_ulong do_r31 = reglist & 0x10;
 268
 269    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
 270        target_ulong i;
 271
 272        for (i = 0; i < base_reglist; i++) {
 273            env->active_tc.gpr[multiple_regs[i]] =
 274                cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC());
 275            addr += 8;
 276        }
 277    }
 278
 279    if (do_r31) {
 280        env->active_tc.gpr[31] =
 281            cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC());
 282    }
 283}
 284
 285void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist,
 286                uint32_t mem_idx)
 287{
 288    target_ulong base_reglist = reglist & 0xf;
 289    target_ulong do_r31 = reglist & 0x10;
 290
 291    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) {
 292        target_ulong i;
 293
 294        for (i = 0; i < base_reglist; i++) {
 295            cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]],
 296                              mem_idx, GETPC());
 297            addr += 8;
 298        }
 299    }
 300
 301    if (do_r31) {
 302        cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC());
 303    }
 304}
 305
 306#endif /* TARGET_MIPS64 */
 307