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
cpu_is_bigendian(CPUMIPSState * env)56 static inline bool cpu_is_bigendian(CPUMIPSState *env)
57 {
58 return extract32(env->CP0_Config0, CP0C0_BE, 1);
59 }
60
get_lmask(CPUMIPSState * env,target_ulong value,unsigned bits)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
helper_swl(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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
helper_swr(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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
helper_sdl(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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
helper_sdr(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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
helper_lwm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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
helper_swm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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)
helper_ldm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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
helper_sdm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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