]>
Commit | Line | Data |
---|---|---|
8a05fd9a RH |
1 | /* |
2 | * PowerPC emulation special registers manipulation 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.1 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 | ||
20 | #include "qemu/osdep.h" | |
2df4fe7a | 21 | #include "cpu.h" |
8a05fd9a RH |
22 | #include "qemu/main-loop.h" |
23 | #include "exec/exec-all.h" | |
24 | #include "sysemu/kvm.h" | |
25 | #include "helper_regs.h" | |
26 | ||
27 | /* Swap temporary saved registers with GPRs */ | |
28 | void hreg_swap_gpr_tgpr(CPUPPCState *env) | |
29 | { | |
30 | target_ulong tmp; | |
31 | ||
32 | tmp = env->gpr[0]; | |
33 | env->gpr[0] = env->tgpr[0]; | |
34 | env->tgpr[0] = tmp; | |
35 | tmp = env->gpr[1]; | |
36 | env->gpr[1] = env->tgpr[1]; | |
37 | env->tgpr[1] = tmp; | |
38 | tmp = env->gpr[2]; | |
39 | env->gpr[2] = env->tgpr[2]; | |
40 | env->tgpr[2] = tmp; | |
41 | tmp = env->gpr[3]; | |
42 | env->gpr[3] = env->tgpr[3]; | |
43 | env->tgpr[3] = tmp; | |
44 | } | |
45 | ||
2da8a6bc | 46 | static uint32_t hreg_compute_hflags_value(CPUPPCState *env) |
8a05fd9a | 47 | { |
2df4fe7a RH |
48 | target_ulong msr = env->msr; |
49 | uint32_t ppc_flags = env->flags; | |
50 | uint32_t hflags = 0; | |
51 | uint32_t msr_mask; | |
8a05fd9a | 52 | |
2df4fe7a RH |
53 | /* Some bits come straight across from MSR. */ |
54 | QEMU_BUILD_BUG_ON(MSR_LE != HFLAGS_LE); | |
55 | QEMU_BUILD_BUG_ON(MSR_PR != HFLAGS_PR); | |
56 | QEMU_BUILD_BUG_ON(MSR_DR != HFLAGS_DR); | |
2df4fe7a | 57 | QEMU_BUILD_BUG_ON(MSR_FP != HFLAGS_FP); |
2df4fe7a | 58 | msr_mask = ((1 << MSR_LE) | (1 << MSR_PR) | |
d764184d | 59 | (1 << MSR_DR) | (1 << MSR_FP)); |
18285046 | 60 | |
2df4fe7a | 61 | if (ppc_flags & POWERPC_FLAG_HID0_LE) { |
18285046 RH |
62 | /* |
63 | * Note that MSR_LE is not set in env->msr_mask for this cpu, | |
2df4fe7a | 64 | * and so will never be set in msr. |
18285046 RH |
65 | */ |
66 | uint32_t le = extract32(env->spr[SPR_HID0], 3, 1); | |
2df4fe7a RH |
67 | hflags |= le << MSR_LE; |
68 | } | |
69 | ||
7da31f26 RH |
70 | if (ppc_flags & POWERPC_FLAG_DE) { |
71 | target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0]; | |
72 | if (dbcr0 & DBCR0_ICMP) { | |
73 | hflags |= 1 << HFLAGS_SE; | |
74 | } | |
75 | if (dbcr0 & DBCR0_BRT) { | |
76 | hflags |= 1 << HFLAGS_BE; | |
77 | } | |
78 | } else { | |
79 | if (ppc_flags & POWERPC_FLAG_BE) { | |
80 | QEMU_BUILD_BUG_ON(MSR_BE != HFLAGS_BE); | |
81 | msr_mask |= 1 << MSR_BE; | |
82 | } | |
83 | if (ppc_flags & POWERPC_FLAG_SE) { | |
84 | QEMU_BUILD_BUG_ON(MSR_SE != HFLAGS_SE); | |
85 | msr_mask |= 1 << MSR_SE; | |
86 | } | |
2df4fe7a RH |
87 | } |
88 | ||
89 | if (msr_is_64bit(env, msr)) { | |
90 | hflags |= 1 << HFLAGS_64; | |
91 | } | |
92 | if ((ppc_flags & POWERPC_FLAG_SPE) && (msr & (1 << MSR_SPE))) { | |
93 | hflags |= 1 << HFLAGS_SPE; | |
94 | } | |
95 | if (ppc_flags & POWERPC_FLAG_VRE) { | |
96 | QEMU_BUILD_BUG_ON(MSR_VR != HFLAGS_VR); | |
97 | msr_mask |= 1 << MSR_VR; | |
18285046 | 98 | } |
0e6bac3e RH |
99 | if (ppc_flags & POWERPC_FLAG_VSX) { |
100 | QEMU_BUILD_BUG_ON(MSR_VSX != HFLAGS_VSX); | |
101 | msr_mask |= 1 << MSR_VSX; | |
2df4fe7a RH |
102 | } |
103 | if ((ppc_flags & POWERPC_FLAG_TM) && (msr & (1ull << MSR_TM))) { | |
104 | hflags |= 1 << HFLAGS_TM; | |
105 | } | |
f03de3b4 RH |
106 | if (env->spr[SPR_LPCR] & LPCR_GTSE) { |
107 | hflags |= 1 << HFLAGS_GTSE; | |
108 | } | |
1db3632a MF |
109 | if (env->spr[SPR_LPCR] & LPCR_HR) { |
110 | hflags |= 1 << HFLAGS_HR; | |
111 | } | |
f7460df2 DHB |
112 | if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC0) { |
113 | hflags |= 1 << HFLAGS_PMCC0; | |
114 | } | |
115 | if (env->spr[SPR_POWER_MMCR0] & MMCR0_PMCC1) { | |
116 | hflags |= 1 << HFLAGS_PMCC1; | |
117 | } | |
2df4fe7a RH |
118 | |
119 | #ifndef CONFIG_USER_ONLY | |
120 | if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) { | |
121 | hflags |= 1 << HFLAGS_HV; | |
122 | } | |
d764184d RH |
123 | |
124 | /* | |
125 | * This is our encoding for server processors. The architecture | |
126 | * specifies that there is no such thing as userspace with | |
127 | * translation off, however it appears that MacOS does it and some | |
128 | * 32-bit CPUs support it. Weird... | |
129 | * | |
130 | * 0 = Guest User space virtual mode | |
131 | * 1 = Guest Kernel space virtual mode | |
132 | * 2 = Guest User space real mode | |
133 | * 3 = Guest Kernel space real mode | |
134 | * 4 = HV User space virtual mode | |
135 | * 5 = HV Kernel space virtual mode | |
136 | * 6 = HV User space real mode | |
137 | * 7 = HV Kernel space real mode | |
138 | * | |
139 | * For BookE, we need 8 MMU modes as follow: | |
140 | * | |
141 | * 0 = AS 0 HV User space | |
142 | * 1 = AS 0 HV Kernel space | |
143 | * 2 = AS 1 HV User space | |
144 | * 3 = AS 1 HV Kernel space | |
145 | * 4 = AS 0 Guest User space | |
146 | * 5 = AS 0 Guest Kernel space | |
147 | * 6 = AS 1 Guest User space | |
148 | * 7 = AS 1 Guest Kernel space | |
149 | */ | |
150 | unsigned immu_idx, dmmu_idx; | |
151 | dmmu_idx = msr & (1 << MSR_PR) ? 0 : 1; | |
152 | if (env->mmu_model & POWERPC_MMU_BOOKE) { | |
153 | dmmu_idx |= msr & (1 << MSR_GS) ? 4 : 0; | |
154 | immu_idx = dmmu_idx; | |
155 | immu_idx |= msr & (1 << MSR_IS) ? 2 : 0; | |
156 | dmmu_idx |= msr & (1 << MSR_DS) ? 2 : 0; | |
157 | } else { | |
158 | dmmu_idx |= msr & (1ull << MSR_HV) ? 4 : 0; | |
159 | immu_idx = dmmu_idx; | |
160 | immu_idx |= msr & (1 << MSR_IR) ? 0 : 2; | |
161 | dmmu_idx |= msr & (1 << MSR_DR) ? 0 : 2; | |
162 | } | |
163 | hflags |= immu_idx << HFLAGS_IMMU_IDX; | |
164 | hflags |= dmmu_idx << HFLAGS_DMMU_IDX; | |
2df4fe7a RH |
165 | #endif |
166 | ||
2da8a6bc RH |
167 | return hflags | (msr & msr_mask); |
168 | } | |
169 | ||
170 | void hreg_compute_hflags(CPUPPCState *env) | |
171 | { | |
172 | env->hflags = hreg_compute_hflags_value(env); | |
173 | } | |
174 | ||
175 | #ifdef CONFIG_DEBUG_TCG | |
176 | void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, | |
177 | target_ulong *cs_base, uint32_t *flags) | |
178 | { | |
179 | uint32_t hflags_current = env->hflags; | |
180 | uint32_t hflags_rebuilt; | |
181 | ||
182 | *pc = env->nip; | |
183 | *cs_base = 0; | |
184 | *flags = hflags_current; | |
185 | ||
186 | hflags_rebuilt = hreg_compute_hflags_value(env); | |
187 | if (unlikely(hflags_current != hflags_rebuilt)) { | |
188 | cpu_abort(env_cpu(env), | |
189 | "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n", | |
190 | hflags_current, hflags_rebuilt); | |
191 | } | |
8a05fd9a | 192 | } |
2da8a6bc | 193 | #endif |
8a05fd9a RH |
194 | |
195 | void cpu_interrupt_exittb(CPUState *cs) | |
196 | { | |
197 | if (!kvm_enabled()) { | |
198 | return; | |
199 | } | |
200 | ||
201 | if (!qemu_mutex_iothread_locked()) { | |
202 | qemu_mutex_lock_iothread(); | |
203 | cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); | |
204 | qemu_mutex_unlock_iothread(); | |
205 | } else { | |
206 | cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); | |
207 | } | |
208 | } | |
209 | ||
210 | int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) | |
211 | { | |
212 | int excp; | |
213 | #if !defined(CONFIG_USER_ONLY) | |
214 | CPUState *cs = env_cpu(env); | |
215 | #endif | |
216 | ||
217 | excp = 0; | |
218 | value &= env->msr_mask; | |
219 | #if !defined(CONFIG_USER_ONLY) | |
220 | /* Neither mtmsr nor guest state can alter HV */ | |
221 | if (!alter_hv || !(env->msr & MSR_HVB)) { | |
222 | value &= ~MSR_HVB; | |
223 | value |= env->msr & MSR_HVB; | |
224 | } | |
225 | if (((value >> MSR_IR) & 1) != msr_ir || | |
226 | ((value >> MSR_DR) & 1) != msr_dr) { | |
227 | cpu_interrupt_exittb(cs); | |
228 | } | |
229 | if ((env->mmu_model & POWERPC_MMU_BOOKE) && | |
230 | ((value >> MSR_GS) & 1) != msr_gs) { | |
231 | cpu_interrupt_exittb(cs); | |
232 | } | |
233 | if (unlikely((env->flags & POWERPC_FLAG_TGPR) && | |
234 | ((value ^ env->msr) & (1 << MSR_TGPR)))) { | |
235 | /* Swap temporary saved registers with GPRs */ | |
236 | hreg_swap_gpr_tgpr(env); | |
237 | } | |
238 | if (unlikely((value >> MSR_EP) & 1) != msr_ep) { | |
239 | /* Change the exception prefix on PowerPC 601 */ | |
240 | env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000; | |
241 | } | |
242 | /* | |
243 | * If PR=1 then EE, IR and DR must be 1 | |
244 | * | |
245 | * Note: We only enforce this on 64-bit server processors. | |
246 | * It appears that: | |
247 | * - 32-bit implementations supports PR=1 and EE/DR/IR=0 and MacOS | |
248 | * exploits it. | |
249 | * - 64-bit embedded implementations do not need any operation to be | |
250 | * performed when PR is set. | |
251 | */ | |
252 | if (is_book3s_arch2x(env) && ((value >> MSR_PR) & 1)) { | |
253 | value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR); | |
254 | } | |
255 | #endif | |
256 | env->msr = value; | |
257 | hreg_compute_hflags(env); | |
258 | #if !defined(CONFIG_USER_ONLY) | |
259 | if (unlikely(msr_pow == 1)) { | |
260 | if (!env->pending_interrupts && (*env->check_pow)(env)) { | |
261 | cs->halted = 1; | |
262 | excp = EXCP_HALTED; | |
263 | } | |
264 | } | |
265 | #endif | |
266 | ||
267 | return excp; | |
268 | } | |
269 | ||
c06ba892 LMC |
270 | #ifdef CONFIG_SOFTMMU |
271 | void store_40x_sler(CPUPPCState *env, uint32_t val) | |
272 | { | |
273 | /* XXX: TO BE FIXED */ | |
274 | if (val != 0x00000000) { | |
275 | cpu_abort(env_cpu(env), | |
276 | "Little-endian regions are not supported by now\n"); | |
277 | } | |
278 | env->spr[SPR_405_SLER] = val; | |
279 | } | |
280 | #endif /* CONFIG_SOFTMMU */ | |
281 | ||
8a05fd9a RH |
282 | #ifndef CONFIG_USER_ONLY |
283 | void check_tlb_flush(CPUPPCState *env, bool global) | |
284 | { | |
285 | CPUState *cs = env_cpu(env); | |
286 | ||
287 | /* Handle global flushes first */ | |
288 | if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) { | |
289 | env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH; | |
290 | env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; | |
291 | tlb_flush_all_cpus_synced(cs); | |
292 | return; | |
293 | } | |
294 | ||
295 | /* Then handle local ones */ | |
296 | if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) { | |
297 | env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; | |
298 | tlb_flush(cs); | |
299 | } | |
300 | } | |
301 | #endif |