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