xref: /qemu/target/mips/tcg/ldst_helper.c (revision b49f4755)
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 
56 static inline bool cpu_is_bigendian(CPUMIPSState *env)
57 {
58     return extract32(env->CP0_Config0, CP0C0_BE, 1);
59 }
60 
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 
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 
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 
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 
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 
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 
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)
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 
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