]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * PowerPC memory access 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 "cpu.h" | |
20 | #include "qemu/host-utils.h" | |
21 | #include "helper.h" | |
22 | ||
23 | #include "helper_regs.h" | |
24 | ||
25 | #if !defined(CONFIG_USER_ONLY) | |
26 | #include "exec/softmmu_exec.h" | |
27 | #endif /* !defined(CONFIG_USER_ONLY) */ | |
28 | ||
29 | //#define DEBUG_OP | |
30 | ||
31 | /*****************************************************************************/ | |
32 | /* Memory load and stores */ | |
33 | ||
34 | static inline target_ulong addr_add(CPUPPCState *env, target_ulong addr, | |
35 | target_long arg) | |
36 | { | |
37 | #if defined(TARGET_PPC64) | |
38 | if (!msr_is_64bit(env, env->msr)) { | |
39 | return (uint32_t)(addr + arg); | |
40 | } else | |
41 | #endif | |
42 | { | |
43 | return addr + arg; | |
44 | } | |
45 | } | |
46 | ||
47 | void helper_lmw(CPUPPCState *env, target_ulong addr, uint32_t reg) | |
48 | { | |
49 | for (; reg < 32; reg++) { | |
50 | if (msr_le) { | |
51 | env->gpr[reg] = bswap32(cpu_ldl_data(env, addr)); | |
52 | } else { | |
53 | env->gpr[reg] = cpu_ldl_data(env, addr); | |
54 | } | |
55 | addr = addr_add(env, addr, 4); | |
56 | } | |
57 | } | |
58 | ||
59 | void helper_stmw(CPUPPCState *env, target_ulong addr, uint32_t reg) | |
60 | { | |
61 | for (; reg < 32; reg++) { | |
62 | if (msr_le) { | |
63 | cpu_stl_data(env, addr, bswap32((uint32_t)env->gpr[reg])); | |
64 | } else { | |
65 | cpu_stl_data(env, addr, (uint32_t)env->gpr[reg]); | |
66 | } | |
67 | addr = addr_add(env, addr, 4); | |
68 | } | |
69 | } | |
70 | ||
71 | void helper_lsw(CPUPPCState *env, target_ulong addr, uint32_t nb, uint32_t reg) | |
72 | { | |
73 | int sh; | |
74 | ||
75 | for (; nb > 3; nb -= 4) { | |
76 | env->gpr[reg] = cpu_ldl_data(env, addr); | |
77 | reg = (reg + 1) % 32; | |
78 | addr = addr_add(env, addr, 4); | |
79 | } | |
80 | if (unlikely(nb > 0)) { | |
81 | env->gpr[reg] = 0; | |
82 | for (sh = 24; nb > 0; nb--, sh -= 8) { | |
83 | env->gpr[reg] |= cpu_ldub_data(env, addr) << sh; | |
84 | addr = addr_add(env, addr, 1); | |
85 | } | |
86 | } | |
87 | } | |
88 | /* PPC32 specification says we must generate an exception if | |
89 | * rA is in the range of registers to be loaded. | |
90 | * In an other hand, IBM says this is valid, but rA won't be loaded. | |
91 | * For now, I'll follow the spec... | |
92 | */ | |
93 | void helper_lswx(CPUPPCState *env, target_ulong addr, uint32_t reg, | |
94 | 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(env, addr, xer_bc, reg); | |
104 | } | |
105 | } | |
106 | } | |
107 | ||
108 | void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, | |
109 | uint32_t reg) | |
110 | { | |
111 | int sh; | |
112 | ||
113 | for (; nb > 3; nb -= 4) { | |
114 | cpu_stl_data(env, addr, env->gpr[reg]); | |
115 | reg = (reg + 1) % 32; | |
116 | addr = addr_add(env, addr, 4); | |
117 | } | |
118 | if (unlikely(nb > 0)) { | |
119 | for (sh = 24; nb > 0; nb--, sh -= 8) { | |
120 | cpu_stb_data(env, addr, (env->gpr[reg] >> sh) & 0xFF); | |
121 | addr = addr_add(env, addr, 1); | |
122 | } | |
123 | } | |
124 | } | |
125 | ||
126 | static void do_dcbz(CPUPPCState *env, target_ulong addr, int dcache_line_size) | |
127 | { | |
128 | int i; | |
129 | ||
130 | addr &= ~(dcache_line_size - 1); | |
131 | for (i = 0; i < dcache_line_size; i += 4) { | |
132 | cpu_stl_data(env, addr + i, 0); | |
133 | } | |
134 | if (env->reserve_addr == addr) { | |
135 | env->reserve_addr = (target_ulong)-1ULL; | |
136 | } | |
137 | } | |
138 | ||
139 | void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t is_dcbzl) | |
140 | { | |
141 | int dcbz_size = env->dcache_line_size; | |
142 | ||
143 | #if !defined(CONFIG_USER_ONLY) && defined(TARGET_PPC64) | |
144 | if (!is_dcbzl && | |
145 | (env->excp_model == POWERPC_EXCP_970) && | |
146 | ((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) { | |
147 | dcbz_size = 32; | |
148 | } | |
149 | #endif | |
150 | ||
151 | /* XXX add e500mc support */ | |
152 | ||
153 | do_dcbz(env, addr, dcbz_size); | |
154 | } | |
155 | ||
156 | void helper_icbi(CPUPPCState *env, target_ulong addr) | |
157 | { | |
158 | addr &= ~(env->dcache_line_size - 1); | |
159 | /* Invalidate one cache line : | |
160 | * PowerPC specification says this is to be treated like a load | |
161 | * (not a fetch) by the MMU. To be sure it will be so, | |
162 | * do the load "by hand". | |
163 | */ | |
164 | cpu_ldl_data(env, addr); | |
165 | } | |
166 | ||
167 | /* XXX: to be tested */ | |
168 | target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, | |
169 | uint32_t ra, uint32_t rb) | |
170 | { | |
171 | int i, c, d; | |
172 | ||
173 | d = 24; | |
174 | for (i = 0; i < xer_bc; i++) { | |
175 | c = cpu_ldub_data(env, addr); | |
176 | addr = addr_add(env, addr, 1); | |
177 | /* ra (if not 0) and rb are never modified */ | |
178 | if (likely(reg != rb && (ra == 0 || reg != ra))) { | |
179 | env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d); | |
180 | } | |
181 | if (unlikely(c == xer_cmp)) { | |
182 | break; | |
183 | } | |
184 | if (likely(d != 0)) { | |
185 | d -= 8; | |
186 | } else { | |
187 | d = 24; | |
188 | reg++; | |
189 | reg = reg & 0x1F; | |
190 | } | |
191 | } | |
192 | return i; | |
193 | } | |
194 | ||
195 | /*****************************************************************************/ | |
196 | /* Altivec extension helpers */ | |
197 | #if defined(HOST_WORDS_BIGENDIAN) | |
198 | #define HI_IDX 0 | |
199 | #define LO_IDX 1 | |
200 | #else | |
201 | #define HI_IDX 1 | |
202 | #define LO_IDX 0 | |
203 | #endif | |
204 | ||
205 | #define LVE(name, access, swap, element) \ | |
206 | void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ | |
207 | target_ulong addr) \ | |
208 | { \ | |
209 | size_t n_elems = ARRAY_SIZE(r->element); \ | |
210 | int adjust = HI_IDX*(n_elems - 1); \ | |
211 | int sh = sizeof(r->element[0]) >> 1; \ | |
212 | int index = (addr & 0xf) >> sh; \ | |
213 | \ | |
214 | if (msr_le) { \ | |
215 | r->element[LO_IDX ? index : (adjust - index)] = \ | |
216 | swap(access(env, addr)); \ | |
217 | } else { \ | |
218 | r->element[LO_IDX ? index : (adjust - index)] = \ | |
219 | access(env, addr); \ | |
220 | } \ | |
221 | } | |
222 | #define I(x) (x) | |
223 | LVE(lvebx, cpu_ldub_data, I, u8) | |
224 | LVE(lvehx, cpu_lduw_data, bswap16, u16) | |
225 | LVE(lvewx, cpu_ldl_data, bswap32, u32) | |
226 | #undef I | |
227 | #undef LVE | |
228 | ||
229 | #define STVE(name, access, swap, element) \ | |
230 | void helper_##name(CPUPPCState *env, ppc_avr_t *r, \ | |
231 | target_ulong addr) \ | |
232 | { \ | |
233 | size_t n_elems = ARRAY_SIZE(r->element); \ | |
234 | int adjust = HI_IDX * (n_elems - 1); \ | |
235 | int sh = sizeof(r->element[0]) >> 1; \ | |
236 | int index = (addr & 0xf) >> sh; \ | |
237 | \ | |
238 | if (msr_le) { \ | |
239 | access(env, addr, swap(r->element[LO_IDX ? index : \ | |
240 | (adjust - index)])); \ | |
241 | } else { \ | |
242 | access(env, addr, r->element[LO_IDX ? index : \ | |
243 | (adjust - index)]); \ | |
244 | } \ | |
245 | } | |
246 | #define I(x) (x) | |
247 | STVE(stvebx, cpu_stb_data, I, u8) | |
248 | STVE(stvehx, cpu_stw_data, bswap16, u16) | |
249 | STVE(stvewx, cpu_stl_data, bswap32, u32) | |
250 | #undef I | |
251 | #undef LVE | |
252 | ||
253 | #undef HI_IDX | |
254 | #undef LO_IDX |