xref: /openbmc/qemu/target/mips/tcg/ldst_helper.c (revision 08ae519ab8eb6c9abbd97156cb3678f372521501)
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  
get_lmask(CPUMIPSState * env,target_ulong value,unsigned bits)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  
helper_swl(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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  
helper_swr(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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  
helper_sdl(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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  
helper_sdr(CPUMIPSState * env,target_ulong arg1,target_ulong arg2,int mem_idx)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  
helper_lwm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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  
helper_swm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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)
helper_ldm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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  
helper_sdm(CPUMIPSState * env,target_ulong addr,target_ulong reglist,uint32_t mem_idx)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