]>
Commit | Line | Data |
---|---|---|
9a64fbe4 | 1 | /* |
b327c654 | 2 | * PowerPC 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 | |
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 | |
8167ee88 | 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
9a64fbe4 | 18 | */ |
7b239bec | 19 | #include <string.h> |
3e457172 BS |
20 | #include "cpu.h" |
21 | #include "dyngen-exec.h" | |
603fccce | 22 | #include "host-utils.h" |
a7812ae4 | 23 | #include "helper.h" |
9a64fbe4 | 24 | |
0411a972 | 25 | #include "helper_regs.h" |
0487d6a8 | 26 | |
3e457172 BS |
27 | #if !defined(CONFIG_USER_ONLY) |
28 | #include "softmmu_exec.h" | |
29 | #endif /* !defined(CONFIG_USER_ONLY) */ | |
30 | ||
fdabc366 | 31 | //#define DEBUG_OP |
d12d51d5 | 32 | |
ff4a62cd AJ |
33 | /*****************************************************************************/ |
34 | /* Memory load and stores */ | |
35 | ||
636aa200 | 36 | static inline target_ulong addr_add(target_ulong addr, target_long arg) |
ff4a62cd AJ |
37 | { |
38 | #if defined(TARGET_PPC64) | |
b327c654 BS |
39 | if (!msr_sf) { |
40 | return (uint32_t)(addr + arg); | |
41 | } else | |
ff4a62cd | 42 | #endif |
b327c654 BS |
43 | { |
44 | return addr + arg; | |
45 | } | |
ff4a62cd AJ |
46 | } |
47 | ||
b327c654 | 48 | void helper_lmw(target_ulong addr, uint32_t reg) |
ff4a62cd | 49 | { |
76db3ba4 | 50 | for (; reg < 32; reg++) { |
b327c654 | 51 | if (msr_le) { |
76db3ba4 | 52 | env->gpr[reg] = bswap32(ldl(addr)); |
b327c654 | 53 | } else { |
76db3ba4 | 54 | env->gpr[reg] = ldl(addr); |
b327c654 BS |
55 | } |
56 | addr = addr_add(addr, 4); | |
ff4a62cd AJ |
57 | } |
58 | } | |
59 | ||
b327c654 | 60 | void helper_stmw(target_ulong addr, uint32_t reg) |
ff4a62cd | 61 | { |
76db3ba4 | 62 | for (; reg < 32; reg++) { |
b327c654 | 63 | if (msr_le) { |
76db3ba4 | 64 | stl(addr, bswap32((uint32_t)env->gpr[reg])); |
b327c654 | 65 | } else { |
76db3ba4 | 66 | stl(addr, (uint32_t)env->gpr[reg]); |
b327c654 BS |
67 | } |
68 | addr = addr_add(addr, 4); | |
ff4a62cd AJ |
69 | } |
70 | } | |
71 | ||
dfbc799d AJ |
72 | void helper_lsw(target_ulong addr, uint32_t nb, uint32_t reg) |
73 | { | |
74 | int sh; | |
b327c654 | 75 | |
76db3ba4 AJ |
76 | for (; nb > 3; nb -= 4) { |
77 | env->gpr[reg] = ldl(addr); | |
dfbc799d | 78 | reg = (reg + 1) % 32; |
b327c654 | 79 | addr = addr_add(addr, 4); |
dfbc799d AJ |
80 | } |
81 | if (unlikely(nb > 0)) { | |
82 | env->gpr[reg] = 0; | |
76db3ba4 AJ |
83 | for (sh = 24; nb > 0; nb--, sh -= 8) { |
84 | env->gpr[reg] |= ldub(addr) << sh; | |
b327c654 | 85 | addr = addr_add(addr, 1); |
dfbc799d AJ |
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))) { | |
e5f17ac6 | 99 | helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, |
e06fcd75 AJ |
100 | POWERPC_EXCP_INVAL | |
101 | POWERPC_EXCP_INVAL_LSWX); | |
dfbc799d AJ |
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; | |
b327c654 | 111 | |
76db3ba4 AJ |
112 | for (; nb > 3; nb -= 4) { |
113 | stl(addr, env->gpr[reg]); | |
dfbc799d | 114 | reg = (reg + 1) % 32; |
b327c654 | 115 | addr = addr_add(addr, 4); |
dfbc799d AJ |
116 | } |
117 | if (unlikely(nb > 0)) { | |
a16b45e7 | 118 | for (sh = 24; nb > 0; nb--, sh -= 8) { |
76db3ba4 | 119 | stb(addr, (env->gpr[reg] >> sh) & 0xFF); |
a16b45e7 AJ |
120 | addr = addr_add(addr, 1); |
121 | } | |
dfbc799d AJ |
122 | } |
123 | } | |
124 | ||
799a8c8d AJ |
125 | static void do_dcbz(target_ulong addr, int dcache_line_size) |
126 | { | |
799a8c8d | 127 | int i; |
b327c654 BS |
128 | |
129 | addr &= ~(dcache_line_size - 1); | |
130 | for (i = 0; i < dcache_line_size; i += 4) { | |
131 | stl(addr + i, 0); | |
799a8c8d | 132 | } |
b327c654 | 133 | if (env->reserve_addr == addr) { |
18b21a2f | 134 | env->reserve_addr = (target_ulong)-1ULL; |
b327c654 | 135 | } |
799a8c8d AJ |
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 | { | |
b327c654 | 145 | if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { |
799a8c8d | 146 | do_dcbz(addr, 32); |
b327c654 | 147 | } else { |
799a8c8d | 148 | do_dcbz(addr, env->dcache_line_size); |
b327c654 | 149 | } |
799a8c8d AJ |
150 | } |
151 | ||
37d269df AJ |
152 | void helper_icbi(target_ulong addr) |
153 | { | |
76db3ba4 | 154 | addr &= ~(env->dcache_line_size - 1); |
37d269df AJ |
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 | */ | |
577f25a5 | 160 | ldl(addr); |
37d269df AJ |
161 | } |
162 | ||
b327c654 BS |
163 | /* XXX: to be tested */ |
164 | target_ulong helper_lscbx(target_ulong addr, uint32_t reg, uint32_t ra, | |
165 | uint32_t rb) | |
bdb4b689 AJ |
166 | { |
167 | int i, c, d; | |
b327c654 | 168 | |
bdb4b689 AJ |
169 | d = 24; |
170 | for (i = 0; i < xer_bc; i++) { | |
76db3ba4 | 171 | c = ldub(addr); |
b327c654 | 172 | addr = addr_add(addr, 1); |
bdb4b689 AJ |
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 | } | |
b327c654 | 177 | if (unlikely(c == xer_cmp)) { |
bdb4b689 | 178 | break; |
b327c654 | 179 | } |
bdb4b689 AJ |
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 | ||
d6a46fe8 AJ |
191 | /*****************************************************************************/ |
192 | /* Altivec extension helpers */ | |
e2542fe2 | 193 | #if defined(HOST_WORDS_BIGENDIAN) |
d6a46fe8 AJ |
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 | ||
cbfb6ae9 | 201 | #define LVE(name, access, swap, element) \ |
b327c654 | 202 | void helper_##name(ppc_avr_t *r, target_ulong addr) \ |
cbfb6ae9 AJ |
203 | { \ |
204 | size_t n_elems = ARRAY_SIZE(r->element); \ | |
b327c654 | 205 | int adjust = HI_IDX*(n_elems - 1); \ |
cbfb6ae9 AJ |
206 | int sh = sizeof(r->element[0]) >> 1; \ |
207 | int index = (addr & 0xf) >> sh; \ | |
b327c654 BS |
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 | } \ | |
cbfb6ae9 AJ |
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 | ||
b327c654 BS |
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) { \ | |
cbfb6ae9 AJ |
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 | ||
d6a46fe8 AJ |
245 | #undef HI_IDX |
246 | #undef LO_IDX | |
247 | ||
fdabc366 FB |
248 | /*****************************************************************************/ |
249 | /* Softmmu support */ | |
b327c654 | 250 | #if !defined(CONFIG_USER_ONLY) |
fdabc366 FB |
251 | |
252 | #define MMUSUFFIX _mmu | |
fdabc366 FB |
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 */ | |
1328c2bf | 270 | void tlb_fill(CPUPPCState *env1, target_ulong addr, int is_write, int mmu_idx, |
20503968 | 271 | uintptr_t retaddr) |
fdabc366 FB |
272 | { |
273 | TranslationBlock *tb; | |
1328c2bf | 274 | CPUPPCState *saved_env; |
fdabc366 FB |
275 | int ret; |
276 | ||
fdabc366 | 277 | saved_env = env; |
bccd9ec5 | 278 | env = env1; |
97b348e7 | 279 | ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx); |
76a66253 | 280 | if (unlikely(ret != 0)) { |
fdabc366 FB |
281 | if (likely(retaddr)) { |
282 | /* now we have a real cpu fault */ | |
20503968 | 283 | tb = tb_find_pc(retaddr); |
fdabc366 FB |
284 | if (likely(tb)) { |
285 | /* the PC is inside the translated code. It means that we have | |
286 | a virtual CPU fault */ | |
20503968 | 287 | cpu_restore_state(tb, env, retaddr); |
76a66253 | 288 | } |
fdabc366 | 289 | } |
e5f17ac6 | 290 | helper_raise_exception_err(env, env->exception_index, env->error_code); |
fdabc366 FB |
291 | } |
292 | env = saved_env; | |
9a64fbe4 | 293 | } |
76a66253 | 294 | #endif /* !CONFIG_USER_ONLY */ |