xref: /qemu/target/mips/tcg/ldst_helper.c (revision 84307cd6027c4602913177ff09aeefa4743b7234)
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