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) \ 33 target_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 } 47 HELPER_LD_ATOMIC(ll, ldl, 0x3, (target_long)(int32_t)) 48 #ifdef TARGET_MIPS64 49 HELPER_LD_ATOMIC(lld, ldq, 0x7, (target_ulong)) 50 #endif 51 #undef HELPER_LD_ATOMIC 52 53 #endif /* !CONFIG_USER_ONLY */ 54 55 static inline bool cpu_is_bigendian(CPUMIPSState *env) 56 { 57 return extract32(env->CP0_Config0, CP0C0_BE, 1); 58 } 59 60 static 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 74 void 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 98 void 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 128 void 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 172 void 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 217 static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 }; 218 219 void 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 241 void 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_stl_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_stl_mmuidx_ra(env, addr, env->active_tc.gpr[31], mem_idx, GETPC()); 259 } 260 } 261 262 #if defined(TARGET_MIPS64) 263 void 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 285 void 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