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