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