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