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/cpu_ldst.h" 28 #include "exec/memop.h" 29 #include "internal.h" 30 31 #ifndef CONFIG_USER_ONLY 32 33 #define HELPER_LD_ATOMIC(name, insn, almask, do_cast) \ 34 target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \ 35 { \ 36 if (arg & almask) { \ 37 if (!(env->hflags & MIPS_HFLAG_DM)) { \ 38 env->CP0_BadVAddr = arg; \ 39 } \ 40 do_raise_exception(env, EXCP_AdEL, GETPC()); \ 41 } \ 42 env->CP0_LLAddr = cpu_mips_translate_address(env, arg, MMU_DATA_LOAD, \ 43 GETPC()); \ 44 env->lladdr = arg; \ 45 env->llval = do_cast cpu_##insn##_mmuidx_ra(env, arg, mem_idx, GETPC()); \ 46 return env->llval; \ 47 } 48 HELPER_LD_ATOMIC(ll, ldl, 0x3, (target_long)(int32_t)) 49 #ifdef TARGET_MIPS64 50 HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong)) 51 #endif 52 #undef HELPER_LD_ATOMIC 53 54 #endif /* !CONFIG_USER_ONLY */ 55 56 static inline target_ulong get_lmask(CPUMIPSState *env, 57 target_ulong value, unsigned bits) 58 { 59 unsigned mask = (bits / BITS_PER_BYTE) - 1; 60 61 value &= mask; 62 63 if (!mips_env_is_bigendian(env)) { 64 value ^= mask; 65 } 66 67 return value; 68 } 69 70 void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, 71 int mem_idx) 72 { 73 target_ulong lmask = get_lmask(env, arg2, 32); 74 int dir = mips_env_is_bigendian(env) ? 1 : -1; 75 76 cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 24), mem_idx, GETPC()); 77 78 if (lmask <= 2) { 79 cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 16), 80 mem_idx, GETPC()); 81 } 82 83 if (lmask <= 1) { 84 cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 8), 85 mem_idx, GETPC()); 86 } 87 88 if (lmask == 0) { 89 cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)arg1, 90 mem_idx, GETPC()); 91 } 92 } 93 94 void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, 95 int mem_idx) 96 { 97 target_ulong lmask = get_lmask(env, arg2, 32); 98 int dir = mips_env_is_bigendian(env) ? 1 : -1; 99 100 cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); 101 102 if (lmask >= 1) { 103 cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8), 104 mem_idx, GETPC()); 105 } 106 107 if (lmask >= 2) { 108 cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16), 109 mem_idx, GETPC()); 110 } 111 112 if (lmask == 3) { 113 cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24), 114 mem_idx, GETPC()); 115 } 116 } 117 118 #if defined(TARGET_MIPS64) 119 /* 120 * "half" load and stores. We must do the memory access inline, 121 * or fault handling won't work. 122 */ 123 124 void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, 125 int mem_idx) 126 { 127 target_ulong lmask = get_lmask(env, arg2, 64); 128 int dir = mips_env_is_bigendian(env) ? 1 : -1; 129 130 cpu_stb_mmuidx_ra(env, arg2, (uint8_t)(arg1 >> 56), mem_idx, GETPC()); 131 132 if (lmask <= 6) { 133 cpu_stb_mmuidx_ra(env, arg2 + 1 * dir, (uint8_t)(arg1 >> 48), 134 mem_idx, GETPC()); 135 } 136 137 if (lmask <= 5) { 138 cpu_stb_mmuidx_ra(env, arg2 + 2 * dir, (uint8_t)(arg1 >> 40), 139 mem_idx, GETPC()); 140 } 141 142 if (lmask <= 4) { 143 cpu_stb_mmuidx_ra(env, arg2 + 3 * dir, (uint8_t)(arg1 >> 32), 144 mem_idx, GETPC()); 145 } 146 147 if (lmask <= 3) { 148 cpu_stb_mmuidx_ra(env, arg2 + 4 * dir, (uint8_t)(arg1 >> 24), 149 mem_idx, GETPC()); 150 } 151 152 if (lmask <= 2) { 153 cpu_stb_mmuidx_ra(env, arg2 + 5 * dir, (uint8_t)(arg1 >> 16), 154 mem_idx, GETPC()); 155 } 156 157 if (lmask <= 1) { 158 cpu_stb_mmuidx_ra(env, arg2 + 6 * dir, (uint8_t)(arg1 >> 8), 159 mem_idx, GETPC()); 160 } 161 162 if (lmask <= 0) { 163 cpu_stb_mmuidx_ra(env, arg2 + 7 * dir, (uint8_t)arg1, 164 mem_idx, GETPC()); 165 } 166 } 167 168 void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, 169 int mem_idx) 170 { 171 target_ulong lmask = get_lmask(env, arg2, 64); 172 int dir = mips_env_is_bigendian(env) ? 1 : -1; 173 174 cpu_stb_mmuidx_ra(env, arg2, (uint8_t)arg1, mem_idx, GETPC()); 175 176 if (lmask >= 1) { 177 cpu_stb_mmuidx_ra(env, arg2 - 1 * dir, (uint8_t)(arg1 >> 8), 178 mem_idx, GETPC()); 179 } 180 181 if (lmask >= 2) { 182 cpu_stb_mmuidx_ra(env, arg2 - 2 * dir, (uint8_t)(arg1 >> 16), 183 mem_idx, GETPC()); 184 } 185 186 if (lmask >= 3) { 187 cpu_stb_mmuidx_ra(env, arg2 - 3 * dir, (uint8_t)(arg1 >> 24), 188 mem_idx, GETPC()); 189 } 190 191 if (lmask >= 4) { 192 cpu_stb_mmuidx_ra(env, arg2 - 4 * dir, (uint8_t)(arg1 >> 32), 193 mem_idx, GETPC()); 194 } 195 196 if (lmask >= 5) { 197 cpu_stb_mmuidx_ra(env, arg2 - 5 * dir, (uint8_t)(arg1 >> 40), 198 mem_idx, GETPC()); 199 } 200 201 if (lmask >= 6) { 202 cpu_stb_mmuidx_ra(env, arg2 - 6 * dir, (uint8_t)(arg1 >> 48), 203 mem_idx, GETPC()); 204 } 205 206 if (lmask == 7) { 207 cpu_stb_mmuidx_ra(env, arg2 - 7 * dir, (uint8_t)(arg1 >> 56), 208 mem_idx, GETPC()); 209 } 210 } 211 #endif /* TARGET_MIPS64 */ 212 213 static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 }; 214 215 void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, 216 uint32_t mem_idx) 217 { 218 target_ulong base_reglist = reglist & 0xf; 219 target_ulong do_r31 = reglist & 0x10; 220 221 if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) { 222 target_ulong i; 223 224 for (i = 0; i < base_reglist; i++) { 225 env->active_tc.gpr[multiple_regs[i]] = 226 (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC()); 227 addr += 4; 228 } 229 } 230 231 if (do_r31) { 232 env->active_tc.gpr[31] = 233 (target_long)cpu_ldl_mmuidx_ra(env, addr, mem_idx, GETPC()); 234 } 235 } 236 237 void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, 238 uint32_t mem_idx) 239 { 240 target_ulong base_reglist = reglist & 0xf; 241 target_ulong do_r31 = reglist & 0x10; 242 243 if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) { 244 target_ulong i; 245 246 for (i = 0; i < base_reglist; i++) { 247 cpu_stl_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]], 248 mem_idx, GETPC()); 249 addr += 4; 250 } 251 } 252 253 if (do_r31) { 254 cpu_stl_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); 255 } 256 } 257 258 #if defined(TARGET_MIPS64) 259 void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, 260 uint32_t mem_idx) 261 { 262 target_ulong base_reglist = reglist & 0xf; 263 target_ulong do_r31 = reglist & 0x10; 264 265 if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) { 266 target_ulong i; 267 268 for (i = 0; i < base_reglist; i++) { 269 env->active_tc.gpr[multiple_regs[i]] = 270 cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC()); 271 addr += 8; 272 } 273 } 274 275 if (do_r31) { 276 env->active_tc.gpr[31] = 277 cpu_ldq_mmuidx_ra(env, addr, mem_idx, GETPC()); 278 } 279 } 280 281 void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, 282 uint32_t mem_idx) 283 { 284 target_ulong base_reglist = reglist & 0xf; 285 target_ulong do_r31 = reglist & 0x10; 286 287 if (base_reglist > 0 && base_reglist <= ARRAY_SIZE(multiple_regs)) { 288 target_ulong i; 289 290 for (i = 0; i < base_reglist; i++) { 291 cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[multiple_regs[i]], 292 mem_idx, GETPC()); 293 addr += 8; 294 } 295 } 296 297 if (do_r31) { 298 cpu_stq_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); 299 } 300 } 301 302 #endif /* TARGET_MIPS64 */ 303