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