]> git.proxmox.com Git - qemu.git/blob - target-ppc/op_helper.c
cd1a53322c694987e7e05e2ed59b0a86f0536082
[qemu.git] / target-ppc / op_helper.c
1 /*
2 * PowerPC emulation helpers for QEMU.
3 *
4 * Copyright (c) 2003-2007 Jocelyn Mayer
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
9 * version 2 of the License, or (at your option) any later version.
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
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19 #include <string.h>
20 #include "cpu.h"
21 #include "dyngen-exec.h"
22 #include "host-utils.h"
23 #include "helper.h"
24
25 #include "helper_regs.h"
26
27 #if !defined(CONFIG_USER_ONLY)
28 #include "softmmu_exec.h"
29 #endif /* !defined(CONFIG_USER_ONLY) */
30
31 //#define DEBUG_OP
32
33 /*****************************************************************************/
34 /* Memory load and stores */
35
36 static inline target_ulong addr_add(target_ulong addr, target_long arg)
37 {
38 #if defined(TARGET_PPC64)
39 if (!msr_sf) {
40 return (uint32_t)(addr + arg);
41 } else
42 #endif
43 {
44 return addr + arg;
45 }
46 }
47
48 void helper_lmw(target_ulong addr, uint32_t reg)
49 {
50 for (; reg < 32; reg++) {
51 if (msr_le) {
52 env->gpr[reg] = bswap32(ldl(addr));
53 } else {
54 env->gpr[reg] = ldl(addr);
55 }
56 addr = addr_add(addr, 4);
57 }
58 }
59
60 void helper_stmw(target_ulong addr, uint32_t reg)
61 {
62 for (; reg < 32; reg++) {
63 if (msr_le) {
64 stl(addr, bswap32((uint32_t)env->gpr[reg]));
65 } else {
66 stl(addr, (uint32_t)env->gpr[reg]);
67 }
68 addr = addr_add(addr, 4);
69 }
70 }
71
72 void helper_lsw(target_ulong addr, uint32_t nb, uint32_t reg)
73 {
74 int sh;
75
76 for (; nb > 3; nb -= 4) {
77 env->gpr[reg] = ldl(addr);
78 reg = (reg + 1) % 32;
79 addr = addr_add(addr, 4);
80 }
81 if (unlikely(nb > 0)) {
82 env->gpr[reg] = 0;
83 for (sh = 24; nb > 0; nb--, sh -= 8) {
84 env->gpr[reg] |= ldub(addr) << sh;
85 addr = addr_add(addr, 1);
86 }
87 }
88 }
89 /* PPC32 specification says we must generate an exception if
90 * rA is in the range of registers to be loaded.
91 * In an other hand, IBM says this is valid, but rA won't be loaded.
92 * For now, I'll follow the spec...
93 */
94 void helper_lswx(target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb)
95 {
96 if (likely(xer_bc != 0)) {
97 if (unlikely((ra != 0 && reg < ra && (reg + xer_bc) > ra) ||
98 (reg < rb && (reg + xer_bc) > rb))) {
99 helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
100 POWERPC_EXCP_INVAL |
101 POWERPC_EXCP_INVAL_LSWX);
102 } else {
103 helper_lsw(addr, xer_bc, reg);
104 }
105 }
106 }
107
108 void helper_stsw(target_ulong addr, uint32_t nb, uint32_t reg)
109 {
110 int sh;
111
112 for (; nb > 3; nb -= 4) {
113 stl(addr, env->gpr[reg]);
114 reg = (reg + 1) % 32;
115 addr = addr_add(addr, 4);
116 }
117 if (unlikely(nb > 0)) {
118 for (sh = 24; nb > 0; nb--, sh -= 8) {
119 stb(addr, (env->gpr[reg] >> sh) & 0xFF);
120 addr = addr_add(addr, 1);
121 }
122 }
123 }
124
125 static void do_dcbz(target_ulong addr, int dcache_line_size)
126 {
127 int i;
128
129 addr &= ~(dcache_line_size - 1);
130 for (i = 0; i < dcache_line_size; i += 4) {
131 stl(addr + i, 0);
132 }
133 if (env->reserve_addr == addr) {
134 env->reserve_addr = (target_ulong)-1ULL;
135 }
136 }
137
138 void helper_dcbz(target_ulong addr)
139 {
140 do_dcbz(addr, env->dcache_line_size);
141 }
142
143 void helper_dcbz_970(target_ulong addr)
144 {
145 if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) {
146 do_dcbz(addr, 32);
147 } else {
148 do_dcbz(addr, env->dcache_line_size);
149 }
150 }
151
152 void helper_icbi(target_ulong addr)
153 {
154 addr &= ~(env->dcache_line_size - 1);
155 /* Invalidate one cache line :
156 * PowerPC specification says this is to be treated like a load
157 * (not a fetch) by the MMU. To be sure it will be so,
158 * do the load "by hand".
159 */
160 ldl(addr);
161 }
162
163 /* XXX: to be tested */
164 target_ulong helper_lscbx(target_ulong addr, uint32_t reg, uint32_t ra,
165 uint32_t rb)
166 {
167 int i, c, d;
168
169 d = 24;
170 for (i = 0; i < xer_bc; i++) {
171 c = ldub(addr);
172 addr = addr_add(addr, 1);
173 /* ra (if not 0) and rb are never modified */
174 if (likely(reg != rb && (ra == 0 || reg != ra))) {
175 env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d);
176 }
177 if (unlikely(c == xer_cmp)) {
178 break;
179 }
180 if (likely(d != 0)) {
181 d -= 8;
182 } else {
183 d = 24;
184 reg++;
185 reg = reg & 0x1F;
186 }
187 }
188 return i;
189 }
190
191 /*****************************************************************************/
192 /* Altivec extension helpers */
193 #if defined(HOST_WORDS_BIGENDIAN)
194 #define HI_IDX 0
195 #define LO_IDX 1
196 #else
197 #define HI_IDX 1
198 #define LO_IDX 0
199 #endif
200
201 #define LVE(name, access, swap, element) \
202 void helper_##name(ppc_avr_t *r, target_ulong addr) \
203 { \
204 size_t n_elems = ARRAY_SIZE(r->element); \
205 int adjust = HI_IDX*(n_elems - 1); \
206 int sh = sizeof(r->element[0]) >> 1; \
207 int index = (addr & 0xf) >> sh; \
208 \
209 if (msr_le) { \
210 r->element[LO_IDX ? index : (adjust - index)] = \
211 swap(access(addr)); \
212 } else { \
213 r->element[LO_IDX ? index : (adjust - index)] = \
214 access(addr); \
215 } \
216 }
217 #define I(x) (x)
218 LVE(lvebx, ldub, I, u8)
219 LVE(lvehx, lduw, bswap16, u16)
220 LVE(lvewx, ldl, bswap32, u32)
221 #undef I
222 #undef LVE
223
224 #define STVE(name, access, swap, element) \
225 void helper_##name(ppc_avr_t *r, target_ulong addr) \
226 { \
227 size_t n_elems = ARRAY_SIZE(r->element); \
228 int adjust = HI_IDX * (n_elems - 1); \
229 int sh = sizeof(r->element[0]) >> 1; \
230 int index = (addr & 0xf) >> sh; \
231 \
232 if (msr_le) { \
233 access(addr, swap(r->element[LO_IDX ? index : (adjust - index)])); \
234 } else { \
235 access(addr, r->element[LO_IDX ? index : (adjust - index)]); \
236 } \
237 }
238 #define I(x) (x)
239 STVE(stvebx, stb, I, u8)
240 STVE(stvehx, stw, bswap16, u16)
241 STVE(stvewx, stl, bswap32, u32)
242 #undef I
243 #undef LVE
244
245 #undef HI_IDX
246 #undef LO_IDX
247
248 /*****************************************************************************/
249 /* Softmmu support */
250 #if !defined(CONFIG_USER_ONLY)
251
252 #define MMUSUFFIX _mmu
253
254 #define SHIFT 0
255 #include "softmmu_template.h"
256
257 #define SHIFT 1
258 #include "softmmu_template.h"
259
260 #define SHIFT 2
261 #include "softmmu_template.h"
262
263 #define SHIFT 3
264 #include "softmmu_template.h"
265
266 /* try to fill the TLB and return an exception if error. If retaddr is
267 NULL, it means that the function was called in C code (i.e. not
268 from generated code or from helper.c) */
269 /* XXX: fix it to restore all registers */
270 void tlb_fill(CPUPPCState *env1, target_ulong addr, int is_write, int mmu_idx,
271 uintptr_t retaddr)
272 {
273 TranslationBlock *tb;
274 CPUPPCState *saved_env;
275 int ret;
276
277 saved_env = env;
278 env = env1;
279 ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx);
280 if (unlikely(ret != 0)) {
281 if (likely(retaddr)) {
282 /* now we have a real cpu fault */
283 tb = tb_find_pc(retaddr);
284 if (likely(tb)) {
285 /* the PC is inside the translated code. It means that we have
286 a virtual CPU fault */
287 cpu_restore_state(tb, env, retaddr);
288 }
289 }
290 helper_raise_exception_err(env, env->exception_index, env->error_code);
291 }
292 env = saved_env;
293 }
294 #endif /* !CONFIG_USER_ONLY */