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