]>
Commit | Line | Data |
---|---|---|
9a64fbe4 | 1 | /* |
2f5a189c | 2 | * PowerPC memory access emulation helpers for QEMU. |
5fafdf24 | 3 | * |
76a66253 | 4 | * Copyright (c) 2003-2007 Jocelyn Mayer |
9a64fbe4 FB |
5 | * |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
6bd039cd | 9 | * version 2.1 of the License, or (at your option) any later version. |
9a64fbe4 FB |
10 | * |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
8167ee88 | 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
9a64fbe4 | 18 | */ |
db725815 | 19 | |
0d75590d | 20 | #include "qemu/osdep.h" |
3e457172 | 21 | #include "cpu.h" |
63c91552 | 22 | #include "exec/exec-all.h" |
1de7afc9 | 23 | #include "qemu/host-utils.h" |
2ef6175a | 24 | #include "exec/helper-proto.h" |
0411a972 | 25 | #include "helper_regs.h" |
f08b6170 | 26 | #include "exec/cpu_ldst.h" |
6914bc4f | 27 | #include "internal.h" |
f34ec0f6 | 28 | #include "qemu/atomic128.h" |
3e457172 | 29 | |
5a2c8b9e | 30 | /* #define DEBUG_OP */ |
d12d51d5 | 31 | |
e22c357b DK |
32 | static inline bool needs_byteswap(const CPUPPCState *env) |
33 | { | |
ee3eb3a7 | 34 | #if TARGET_BIG_ENDIAN |
1922322c | 35 | return FIELD_EX64(env->msr, MSR, LE); |
e22c357b | 36 | #else |
1922322c | 37 | return !FIELD_EX64(env->msr, MSR, LE); |
e22c357b DK |
38 | #endif |
39 | } | |
40 | ||
ff4a62cd AJ |
41 | /*****************************************************************************/ |
42 | /* Memory load and stores */ | |
43 | ||
2f5a189c BS |
44 | static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr, |
45 | target_long arg) | |
ff4a62cd AJ |
46 | { |
47 | #if defined(TARGET_PPC64) | |
e42a61f1 | 48 | if (!msr_is_64bit(env, env->msr)) { |
b327c654 BS |
49 | return (uint32_t)(addr + arg); |
50 | } else | |
ff4a62cd | 51 | #endif |
b327c654 BS |
52 | { |
53 | return addr + arg; | |
54 | } | |
ff4a62cd AJ |
55 | } |
56 | ||
bb99b391 RH |
57 | static void *probe_contiguous(CPUPPCState *env, target_ulong addr, uint32_t nb, |
58 | MMUAccessType access_type, int mmu_idx, | |
59 | uintptr_t raddr) | |
60 | { | |
61 | void *host1, *host2; | |
62 | uint32_t nb_pg1, nb_pg2; | |
63 | ||
64 | nb_pg1 = -(addr | TARGET_PAGE_MASK); | |
65 | if (likely(nb <= nb_pg1)) { | |
66 | /* The entire operation is on a single page. */ | |
67 | return probe_access(env, addr, nb, access_type, mmu_idx, raddr); | |
68 | } | |
69 | ||
70 | /* The operation spans two pages. */ | |
71 | nb_pg2 = nb - nb_pg1; | |
72 | host1 = probe_access(env, addr, nb_pg1, access_type, mmu_idx, raddr); | |
73 | addr = addr_add(env, addr, nb_pg1); | |
74 | host2 = probe_access(env, addr, nb_pg2, access_type, mmu_idx, raddr); | |
75 | ||
76 | /* If the two host pages are contiguous, optimize. */ | |
77 | if (host2 == host1 + nb_pg1) { | |
78 | return host1; | |
79 | } | |
80 | return NULL; | |
81 | } | |
82 | ||
2f5a189c | 83 | void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg) |
ff4a62cd | 84 | { |
2ca2ef49 RH |
85 | uintptr_t raddr = GETPC(); |
86 | int mmu_idx = cpu_mmu_index(env, false); | |
87 | void *host = probe_contiguous(env, addr, (32 - reg) * 4, | |
88 | MMU_DATA_LOAD, mmu_idx, raddr); | |
89 | ||
90 | if (likely(host)) { | |
91 | /* Fast path -- the entire operation is in RAM at host. */ | |
92 | for (; reg < 32; reg++) { | |
93 | env->gpr[reg] = (uint32_t)ldl_be_p(host); | |
94 | host += 4; | |
95 | } | |
96 | } else { | |
97 | /* Slow path -- at least some of the operation requires i/o. */ | |
98 | for (; reg < 32; reg++) { | |
99 | env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr); | |
100 | addr = addr_add(env, addr, 4); | |
b327c654 | 101 | } |
ff4a62cd AJ |
102 | } |
103 | } | |
104 | ||
2f5a189c | 105 | void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg) |
ff4a62cd | 106 | { |
2ca2ef49 RH |
107 | uintptr_t raddr = GETPC(); |
108 | int mmu_idx = cpu_mmu_index(env, false); | |
109 | void *host = probe_contiguous(env, addr, (32 - reg) * 4, | |
110 | MMU_DATA_STORE, mmu_idx, raddr); | |
111 | ||
112 | if (likely(host)) { | |
113 | /* Fast path -- the entire operation is in RAM at host. */ | |
114 | for (; reg < 32; reg++) { | |
115 | stl_be_p(host, env->gpr[reg]); | |
116 | host += 4; | |
117 | } | |
118 | } else { | |
119 | /* Slow path -- at least some of the operation requires i/o. */ | |
120 | for (; reg < 32; reg++) { | |
121 | cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr); | |
122 | addr = addr_add(env, addr, 4); | |
b327c654 | 123 | } |
ff4a62cd AJ |
124 | } |
125 | } | |
126 | ||
e41029b3 BH |
127 | static void do_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, |
128 | uint32_t reg, uintptr_t raddr) | |
dfbc799d | 129 | { |
bb99b391 RH |
130 | int mmu_idx; |
131 | void *host; | |
132 | uint32_t val; | |
b327c654 | 133 | |
bb99b391 RH |
134 | if (unlikely(nb == 0)) { |
135 | return; | |
dfbc799d | 136 | } |
bb99b391 RH |
137 | |
138 | mmu_idx = cpu_mmu_index(env, false); | |
139 | host = probe_contiguous(env, addr, nb, MMU_DATA_LOAD, mmu_idx, raddr); | |
140 | ||
141 | if (likely(host)) { | |
142 | /* Fast path -- the entire operation is in RAM at host. */ | |
143 | for (; nb > 3; nb -= 4) { | |
144 | env->gpr[reg] = (uint32_t)ldl_be_p(host); | |
145 | reg = (reg + 1) % 32; | |
146 | host += 4; | |
147 | } | |
148 | switch (nb) { | |
149 | default: | |
150 | return; | |
151 | case 1: | |
152 | val = ldub_p(host) << 24; | |
153 | break; | |
154 | case 2: | |
155 | val = lduw_be_p(host) << 16; | |
156 | break; | |
157 | case 3: | |
158 | val = (lduw_be_p(host) << 16) | (ldub_p(host + 2) << 8); | |
159 | break; | |
160 | } | |
161 | } else { | |
162 | /* Slow path -- at least some of the operation requires i/o. */ | |
163 | for (; nb > 3; nb -= 4) { | |
164 | env->gpr[reg] = cpu_ldl_mmuidx_ra(env, addr, mmu_idx, raddr); | |
165 | reg = (reg + 1) % 32; | |
166 | addr = addr_add(env, addr, 4); | |
167 | } | |
168 | switch (nb) { | |
169 | default: | |
170 | return; | |
171 | case 1: | |
172 | val = cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 24; | |
173 | break; | |
174 | case 2: | |
175 | val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16; | |
176 | break; | |
177 | case 3: | |
178 | val = cpu_lduw_mmuidx_ra(env, addr, mmu_idx, raddr) << 16; | |
179 | addr = addr_add(env, addr, 2); | |
180 | val |= cpu_ldub_mmuidx_ra(env, addr, mmu_idx, raddr) << 8; | |
181 | break; | |
dfbc799d AJ |
182 | } |
183 | } | |
bb99b391 | 184 | env->gpr[reg] = val; |
dfbc799d | 185 | } |
e41029b3 | 186 | |
bb99b391 RH |
187 | void helper_lsw(CPUPPCState *env, target_ulong addr, |
188 | uint32_t nb, uint32_t reg) | |
e41029b3 BH |
189 | { |
190 | do_lsw(env, addr, nb, reg, GETPC()); | |
191 | } | |
192 | ||
5a2c8b9e DG |
193 | /* |
194 | * PPC32 specification says we must generate an exception if rA is in | |
195 | * the range of registers to be loaded. In an other hand, IBM says | |
196 | * this is valid, but rA won't be loaded. For now, I'll follow the | |
197 | * spec... | |
dfbc799d | 198 | */ |
2f5a189c BS |
199 | void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg, |
200 | uint32_t ra, uint32_t rb) | |
dfbc799d AJ |
201 | { |
202 | if (likely(xer_bc != 0)) { | |
f0704d78 | 203 | int num_used_regs = DIV_ROUND_UP(xer_bc, 4); |
537d3e8e TH |
204 | if (unlikely((ra != 0 && lsw_reg_in_range(reg, num_used_regs, ra)) || |
205 | lsw_reg_in_range(reg, num_used_regs, rb))) { | |
e41029b3 BH |
206 | raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, |
207 | POWERPC_EXCP_INVAL | | |
208 | POWERPC_EXCP_INVAL_LSWX, GETPC()); | |
dfbc799d | 209 | } else { |
e41029b3 | 210 | do_lsw(env, addr, xer_bc, reg, GETPC()); |
dfbc799d AJ |
211 | } |
212 | } | |
213 | } | |
214 | ||
2f5a189c BS |
215 | void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, |
216 | uint32_t reg) | |
dfbc799d | 217 | { |
bb99b391 RH |
218 | uintptr_t raddr = GETPC(); |
219 | int mmu_idx; | |
220 | void *host; | |
221 | uint32_t val; | |
b327c654 | 222 | |
bb99b391 RH |
223 | if (unlikely(nb == 0)) { |
224 | return; | |
dfbc799d | 225 | } |
bb99b391 RH |
226 | |
227 | mmu_idx = cpu_mmu_index(env, false); | |
228 | host = probe_contiguous(env, addr, nb, MMU_DATA_STORE, mmu_idx, raddr); | |
229 | ||
230 | if (likely(host)) { | |
231 | /* Fast path -- the entire operation is in RAM at host. */ | |
232 | for (; nb > 3; nb -= 4) { | |
233 | stl_be_p(host, env->gpr[reg]); | |
234 | reg = (reg + 1) % 32; | |
235 | host += 4; | |
236 | } | |
237 | val = env->gpr[reg]; | |
238 | switch (nb) { | |
239 | case 1: | |
240 | stb_p(host, val >> 24); | |
241 | break; | |
242 | case 2: | |
243 | stw_be_p(host, val >> 16); | |
244 | break; | |
245 | case 3: | |
246 | stw_be_p(host, val >> 16); | |
247 | stb_p(host + 2, val >> 8); | |
248 | break; | |
249 | } | |
250 | } else { | |
251 | for (; nb > 3; nb -= 4) { | |
252 | cpu_stl_mmuidx_ra(env, addr, env->gpr[reg], mmu_idx, raddr); | |
253 | reg = (reg + 1) % 32; | |
254 | addr = addr_add(env, addr, 4); | |
255 | } | |
256 | val = env->gpr[reg]; | |
257 | switch (nb) { | |
258 | case 1: | |
259 | cpu_stb_mmuidx_ra(env, addr, val >> 24, mmu_idx, raddr); | |
260 | break; | |
261 | case 2: | |
262 | cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr); | |
263 | break; | |
264 | case 3: | |
265 | cpu_stw_mmuidx_ra(env, addr, val >> 16, mmu_idx, raddr); | |
266 | addr = addr_add(env, addr, 2); | |
267 | cpu_stb_mmuidx_ra(env, addr, val >> 8, mmu_idx, raddr); | |
268 | break; | |
a16b45e7 | 269 | } |
dfbc799d AJ |
270 | } |
271 | } | |
272 | ||
50728199 RK |
273 | static void dcbz_common(CPUPPCState *env, target_ulong addr, |
274 | uint32_t opcode, bool epid, uintptr_t retaddr) | |
799a8c8d | 275 | { |
c9f82d01 BH |
276 | target_ulong mask, dcbz_size = env->dcache_line_size; |
277 | uint32_t i; | |
278 | void *haddr; | |
d764184d | 279 | int mmu_idx = epid ? PPC_TLB_EPID_STORE : cpu_mmu_index(env, false); |
799a8c8d | 280 | |
414f5d14 | 281 | #if defined(TARGET_PPC64) |
c9f82d01 BH |
282 | /* Check for dcbz vs dcbzl on 970 */ |
283 | if (env->excp_model == POWERPC_EXCP_970 && | |
284 | !(opcode & 0x00200000) && ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { | |
8e33944f | 285 | dcbz_size = 32; |
b327c654 | 286 | } |
8e33944f AG |
287 | #endif |
288 | ||
c9f82d01 BH |
289 | /* Align address */ |
290 | mask = ~(dcbz_size - 1); | |
291 | addr &= mask; | |
292 | ||
293 | /* Check reservation */ | |
1cbddf6d | 294 | if ((env->reserve_addr & mask) == addr) { |
c9f82d01 BH |
295 | env->reserve_addr = (target_ulong)-1ULL; |
296 | } | |
8e33944f | 297 | |
c9f82d01 | 298 | /* Try fast path translate */ |
4dcf078f | 299 | haddr = probe_write(env, addr, dcbz_size, mmu_idx, retaddr); |
c9f82d01 BH |
300 | if (haddr) { |
301 | memset(haddr, 0, dcbz_size); | |
302 | } else { | |
303 | /* Slow path */ | |
304 | for (i = 0; i < dcbz_size; i += 8) { | |
5a376e4f | 305 | cpu_stq_mmuidx_ra(env, addr + i, 0, mmu_idx, retaddr); |
c9f82d01 BH |
306 | } |
307 | } | |
799a8c8d AJ |
308 | } |
309 | ||
50728199 RK |
310 | void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) |
311 | { | |
312 | dcbz_common(env, addr, opcode, false, GETPC()); | |
313 | } | |
314 | ||
315 | void helper_dcbzep(CPUPPCState *env, target_ulong addr, uint32_t opcode) | |
316 | { | |
317 | dcbz_common(env, addr, opcode, true, GETPC()); | |
318 | } | |
319 | ||
2f5a189c | 320 | void helper_icbi(CPUPPCState *env, target_ulong addr) |
37d269df | 321 | { |
76db3ba4 | 322 | addr &= ~(env->dcache_line_size - 1); |
5a2c8b9e DG |
323 | /* |
324 | * Invalidate one cache line : | |
37d269df AJ |
325 | * PowerPC specification says this is to be treated like a load |
326 | * (not a fetch) by the MMU. To be sure it will be so, | |
327 | * do the load "by hand". | |
328 | */ | |
af6d376e | 329 | cpu_ldl_data_ra(env, addr, GETPC()); |
37d269df AJ |
330 | } |
331 | ||
50728199 RK |
332 | void helper_icbiep(CPUPPCState *env, target_ulong addr) |
333 | { | |
334 | #if !defined(CONFIG_USER_ONLY) | |
335 | /* See comments above */ | |
336 | addr &= ~(env->dcache_line_size - 1); | |
5a376e4f | 337 | cpu_ldl_mmuidx_ra(env, addr, PPC_TLB_EPID_LOAD, GETPC()); |
50728199 RK |
338 | #endif |
339 | } | |
340 | ||
b327c654 | 341 | /* XXX: to be tested */ |
2f5a189c BS |
342 | target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, |
343 | uint32_t ra, uint32_t rb) | |
bdb4b689 AJ |
344 | { |
345 | int i, c, d; | |
b327c654 | 346 | |
bdb4b689 AJ |
347 | d = 24; |
348 | for (i = 0; i < xer_bc; i++) { | |
b00a3b36 | 349 | c = cpu_ldub_data_ra(env, addr, GETPC()); |
2f5a189c | 350 | addr = addr_add(env, addr, 1); |
bdb4b689 AJ |
351 | /* ra (if not 0) and rb are never modified */ |
352 | if (likely(reg != rb && (ra == 0 || reg != ra))) { | |
353 | env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); | |
354 | } | |
b327c654 | 355 | if (unlikely(c == xer_cmp)) { |
bdb4b689 | 356 | break; |
b327c654 | 357 | } |
bdb4b689 AJ |
358 | if (likely(d != 0)) { |
359 | d -= 8; | |
360 | } else { | |
361 | d = 24; | |
362 | reg++; | |
363 | reg = reg & 0x1F; | |
364 | } | |
365 | } | |
366 | return i; | |
367 | } | |
368 | ||
d6a46fe8 AJ |
369 | /*****************************************************************************/ |
370 | /* Altivec extension helpers */ | |
e03b5686 | 371 | #if HOST_BIG_ENDIAN |
d6a46fe8 AJ |
372 | #define HI_IDX 0 |
373 | #define LO_IDX 1 | |
374 | #else | |
375 | #define HI_IDX 1 | |
376 | #define LO_IDX 0 | |
377 | #endif | |
378 | ||
5a2c8b9e | 379 | /* |
1922322c VC |
380 | * We use MSR_LE to determine index ordering in a vector. However, |
381 | * byteswapping is not simply controlled by MSR_LE. We also need to | |
5a2c8b9e DG |
382 | * take into account endianness of the target. This is done for the |
383 | * little-endian PPC64 user-mode target. | |
384 | */ | |
e22c357b | 385 | |
cbfb6ae9 | 386 | #define LVE(name, access, swap, element) \ |
2f5a189c BS |
387 | void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ |
388 | target_ulong addr) \ | |
cbfb6ae9 AJ |
389 | { \ |
390 | size_t n_elems = ARRAY_SIZE(r->element); \ | |
5a2c8b9e | 391 | int adjust = HI_IDX * (n_elems - 1); \ |
cbfb6ae9 AJ |
392 | int sh = sizeof(r->element[0]) >> 1; \ |
393 | int index = (addr & 0xf) >> sh; \ | |
1922322c | 394 | if (FIELD_EX64(env->msr, MSR, LE)) { \ |
bbfb6f13 | 395 | index = n_elems - index - 1; \ |
e22c357b DK |
396 | } \ |
397 | \ | |
398 | if (needs_byteswap(env)) { \ | |
b327c654 | 399 | r->element[LO_IDX ? index : (adjust - index)] = \ |
bcd510b1 | 400 | swap(access(env, addr, GETPC())); \ |
b327c654 BS |
401 | } else { \ |
402 | r->element[LO_IDX ? index : (adjust - index)] = \ | |
bcd510b1 | 403 | access(env, addr, GETPC()); \ |
b327c654 | 404 | } \ |
cbfb6ae9 AJ |
405 | } |
406 | #define I(x) (x) | |
bcd510b1 BH |
407 | LVE(lvebx, cpu_ldub_data_ra, I, u8) |
408 | LVE(lvehx, cpu_lduw_data_ra, bswap16, u16) | |
409 | LVE(lvewx, cpu_ldl_data_ra, bswap32, u32) | |
cbfb6ae9 AJ |
410 | #undef I |
411 | #undef LVE | |
412 | ||
b327c654 | 413 | #define STVE(name, access, swap, element) \ |
2f5a189c BS |
414 | void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ |
415 | target_ulong addr) \ | |
b327c654 BS |
416 | { \ |
417 | size_t n_elems = ARRAY_SIZE(r->element); \ | |
418 | int adjust = HI_IDX * (n_elems - 1); \ | |
419 | int sh = sizeof(r->element[0]) >> 1; \ | |
420 | int index = (addr & 0xf) >> sh; \ | |
1922322c | 421 | if (FIELD_EX64(env->msr, MSR, LE)) { \ |
bbfb6f13 | 422 | index = n_elems - index - 1; \ |
e22c357b DK |
423 | } \ |
424 | \ | |
425 | if (needs_byteswap(env)) { \ | |
2f5a189c | 426 | access(env, addr, swap(r->element[LO_IDX ? index : \ |
bcd510b1 BH |
427 | (adjust - index)]), \ |
428 | GETPC()); \ | |
cbfb6ae9 | 429 | } else { \ |
2f5a189c | 430 | access(env, addr, r->element[LO_IDX ? index : \ |
bcd510b1 | 431 | (adjust - index)], GETPC()); \ |
cbfb6ae9 AJ |
432 | } \ |
433 | } | |
434 | #define I(x) (x) | |
bcd510b1 BH |
435 | STVE(stvebx, cpu_stb_data_ra, I, u8) |
436 | STVE(stvehx, cpu_stw_data_ra, bswap16, u16) | |
437 | STVE(stvewx, cpu_stl_data_ra, bswap32, u32) | |
cbfb6ae9 AJ |
438 | #undef I |
439 | #undef LVE | |
440 | ||
6914bc4f ND |
441 | #ifdef TARGET_PPC64 |
442 | #define GET_NB(rb) ((rb >> 56) & 0xFF) | |
443 | ||
444 | #define VSX_LXVL(name, lj) \ | |
445 | void helper_##name(CPUPPCState *env, target_ulong addr, \ | |
2aba168e | 446 | ppc_vsr_t *xt, target_ulong rb) \ |
6914bc4f | 447 | { \ |
2a175830 | 448 | ppc_vsr_t t; \ |
6914bc4f | 449 | uint64_t nb = GET_NB(rb); \ |
2a175830 | 450 | int i; \ |
6914bc4f | 451 | \ |
2a175830 | 452 | t.s128 = int128_zero(); \ |
6914bc4f ND |
453 | if (nb) { \ |
454 | nb = (nb >= 16) ? 16 : nb; \ | |
1922322c | 455 | if (FIELD_EX64(env->msr, MSR, LE) && !lj) { \ |
6914bc4f | 456 | for (i = 16; i > 16 - nb; i--) { \ |
2a175830 | 457 | t.VsrB(i - 1) = cpu_ldub_data_ra(env, addr, GETPC()); \ |
6914bc4f ND |
458 | addr = addr_add(env, addr, 1); \ |
459 | } \ | |
460 | } else { \ | |
461 | for (i = 0; i < nb; i++) { \ | |
2a175830 | 462 | t.VsrB(i) = cpu_ldub_data_ra(env, addr, GETPC()); \ |
6914bc4f ND |
463 | addr = addr_add(env, addr, 1); \ |
464 | } \ | |
465 | } \ | |
466 | } \ | |
2a175830 | 467 | *xt = t; \ |
6914bc4f ND |
468 | } |
469 | ||
470 | VSX_LXVL(lxvl, 0) | |
176e44e7 | 471 | VSX_LXVL(lxvll, 1) |
6914bc4f | 472 | #undef VSX_LXVL |
681c2478 ND |
473 | |
474 | #define VSX_STXVL(name, lj) \ | |
475 | void helper_##name(CPUPPCState *env, target_ulong addr, \ | |
2aba168e | 476 | ppc_vsr_t *xt, target_ulong rb) \ |
681c2478 | 477 | { \ |
681c2478 | 478 | target_ulong nb = GET_NB(rb); \ |
2a175830 | 479 | int i; \ |
681c2478 ND |
480 | \ |
481 | if (!nb) { \ | |
482 | return; \ | |
483 | } \ | |
2a175830 | 484 | \ |
681c2478 | 485 | nb = (nb >= 16) ? 16 : nb; \ |
1922322c | 486 | if (FIELD_EX64(env->msr, MSR, LE) && !lj) { \ |
681c2478 | 487 | for (i = 16; i > 16 - nb; i--) { \ |
2a175830 | 488 | cpu_stb_data_ra(env, addr, xt->VsrB(i - 1), GETPC()); \ |
681c2478 ND |
489 | addr = addr_add(env, addr, 1); \ |
490 | } \ | |
491 | } else { \ | |
492 | for (i = 0; i < nb; i++) { \ | |
2a175830 | 493 | cpu_stb_data_ra(env, addr, xt->VsrB(i), GETPC()); \ |
681c2478 ND |
494 | addr = addr_add(env, addr, 1); \ |
495 | } \ | |
496 | } \ | |
497 | } | |
498 | ||
499 | VSX_STXVL(stxvl, 0) | |
e122090d | 500 | VSX_STXVL(stxvll, 1) |
681c2478 | 501 | #undef VSX_STXVL |
6914bc4f ND |
502 | #undef GET_NB |
503 | #endif /* TARGET_PPC64 */ | |
504 | ||
d6a46fe8 AJ |
505 | #undef HI_IDX |
506 | #undef LO_IDX | |
0ff93d11 TM |
507 | |
508 | void helper_tbegin(CPUPPCState *env) | |
509 | { | |
5a2c8b9e DG |
510 | /* |
511 | * As a degenerate implementation, always fail tbegin. The reason | |
0ff93d11 TM |
512 | * given is "Nesting overflow". The "persistent" bit is set, |
513 | * providing a hint to the error handler to not retry. The TFIAR | |
514 | * captures the address of the failure, which is this tbegin | |
5a2c8b9e DG |
515 | * instruction. Instruction execution will continue with the next |
516 | * instruction in memory, which is precisely what we want. | |
0ff93d11 TM |
517 | */ |
518 | ||
519 | env->spr[SPR_TEXASR] = | |
520 | (1ULL << TEXASR_FAILURE_PERSISTENT) | | |
521 | (1ULL << TEXASR_NESTING_OVERFLOW) | | |
9de754d3 | 522 | (FIELD_EX64_HV(env->msr) << TEXASR_PRIVILEGE_HV) | |
d41ccf6e | 523 | (FIELD_EX64(env->msr, MSR, PR) << TEXASR_PRIVILEGE_PR) | |
0ff93d11 TM |
524 | (1ULL << TEXASR_FAILURE_SUMMARY) | |
525 | (1ULL << TEXASR_TFIAR_EXACT); | |
9de754d3 | 526 | env->spr[SPR_TFIAR] = env->nip | (FIELD_EX64_HV(env->msr) << 1) | |
d41ccf6e | 527 | FIELD_EX64(env->msr, MSR, PR); |
0ff93d11 TM |
528 | env->spr[SPR_TFHAR] = env->nip + 4; |
529 | env->crf[0] = 0xB; /* 0b1010 = transaction failure */ | |
530 | } |