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