]>
Commit | Line | Data |
---|---|---|
ad71ed68 BS |
1 | /* |
2 | * PowerPC exception 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 | |
6bd039cd | 9 | * version 2.1 of the License, or (at your option) any later version. |
ad71ed68 BS |
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 | */ | |
0d75590d | 19 | #include "qemu/osdep.h" |
f1c29ebc | 20 | #include "qemu/main-loop.h" |
ad71ed68 | 21 | #include "cpu.h" |
2ef6175a | 22 | #include "exec/helper-proto.h" |
63c91552 | 23 | #include "exec/exec-all.h" |
f08b6170 | 24 | #include "exec/cpu_ldst.h" |
0f3110fa | 25 | #include "internal.h" |
ad71ed68 BS |
26 | #include "helper_regs.h" |
27 | ||
47733729 DG |
28 | /* #define DEBUG_OP */ |
29 | /* #define DEBUG_SOFTWARE_TLB */ | |
30 | /* #define DEBUG_EXCEPTIONS */ | |
ad71ed68 | 31 | |
c79c73f6 BS |
32 | #ifdef DEBUG_EXCEPTIONS |
33 | # define LOG_EXCP(...) qemu_log(__VA_ARGS__) | |
34 | #else | |
35 | # define LOG_EXCP(...) do { } while (0) | |
36 | #endif | |
37 | ||
c79c73f6 BS |
38 | /*****************************************************************************/ |
39 | /* Exception processing */ | |
40 | #if defined(CONFIG_USER_ONLY) | |
97a8ea5a | 41 | void ppc_cpu_do_interrupt(CPUState *cs) |
c79c73f6 | 42 | { |
97a8ea5a AF |
43 | PowerPCCPU *cpu = POWERPC_CPU(cs); |
44 | CPUPPCState *env = &cpu->env; | |
45 | ||
27103424 | 46 | cs->exception_index = POWERPC_EXCP_NONE; |
c79c73f6 BS |
47 | env->error_code = 0; |
48 | } | |
49 | ||
458dd766 | 50 | static void ppc_hw_interrupt(CPUPPCState *env) |
c79c73f6 | 51 | { |
db70b311 | 52 | CPUState *cs = env_cpu(env); |
27103424 AF |
53 | |
54 | cs->exception_index = POWERPC_EXCP_NONE; | |
c79c73f6 BS |
55 | env->error_code = 0; |
56 | } | |
57 | #else /* defined(CONFIG_USER_ONLY) */ | |
58 | static inline void dump_syscall(CPUPPCState *env) | |
59 | { | |
6dc6b557 NP |
60 | qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 |
61 | " r3=%016" PRIx64 " r4=%016" PRIx64 " r5=%016" PRIx64 | |
62 | " r6=%016" PRIx64 " r7=%016" PRIx64 " r8=%016" PRIx64 | |
c79c73f6 BS |
63 | " nip=" TARGET_FMT_lx "\n", |
64 | ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3), | |
65 | ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5), | |
6dc6b557 NP |
66 | ppc_dump_gpr(env, 6), ppc_dump_gpr(env, 7), |
67 | ppc_dump_gpr(env, 8), env->nip); | |
68 | } | |
69 | ||
3c89b8d6 NP |
70 | static inline void dump_syscall_vectored(CPUPPCState *env) |
71 | { | |
72 | qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 | |
73 | " r3=%016" PRIx64 " r4=%016" PRIx64 " r5=%016" PRIx64 | |
74 | " r6=%016" PRIx64 " r7=%016" PRIx64 " r8=%016" PRIx64 | |
75 | " nip=" TARGET_FMT_lx "\n", | |
76 | ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3), | |
77 | ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5), | |
78 | ppc_dump_gpr(env, 6), ppc_dump_gpr(env, 7), | |
79 | ppc_dump_gpr(env, 8), env->nip); | |
80 | } | |
81 | ||
6dc6b557 NP |
82 | static inline void dump_hcall(CPUPPCState *env) |
83 | { | |
84 | qemu_log_mask(CPU_LOG_INT, "hypercall r3=%016" PRIx64 | |
ececb880 GK |
85 | " r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64 |
86 | " r7=%016" PRIx64 " r8=%016" PRIx64 " r9=%016" PRIx64 | |
87 | " r10=%016" PRIx64 " r11=%016" PRIx64 " r12=%016" PRIx64 | |
6dc6b557 NP |
88 | " nip=" TARGET_FMT_lx "\n", |
89 | ppc_dump_gpr(env, 3), ppc_dump_gpr(env, 4), | |
ececb880 GK |
90 | ppc_dump_gpr(env, 5), ppc_dump_gpr(env, 6), |
91 | ppc_dump_gpr(env, 7), ppc_dump_gpr(env, 8), | |
92 | ppc_dump_gpr(env, 9), ppc_dump_gpr(env, 10), | |
93 | ppc_dump_gpr(env, 11), ppc_dump_gpr(env, 12), | |
94 | env->nip); | |
c79c73f6 BS |
95 | } |
96 | ||
dead760b BH |
97 | static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp, |
98 | target_ulong *msr) | |
99 | { | |
100 | /* We no longer are in a PM state */ | |
1e7fd61d | 101 | env->resume_as_sreset = false; |
dead760b BH |
102 | |
103 | /* Pretend to be returning from doze always as we don't lose state */ | |
0911a60c | 104 | *msr |= SRR1_WS_NOLOSS; |
dead760b BH |
105 | |
106 | /* Machine checks are sent normally */ | |
107 | if (excp == POWERPC_EXCP_MCHECK) { | |
108 | return excp; | |
109 | } | |
110 | switch (excp) { | |
111 | case POWERPC_EXCP_RESET: | |
0911a60c | 112 | *msr |= SRR1_WAKERESET; |
dead760b BH |
113 | break; |
114 | case POWERPC_EXCP_EXTERNAL: | |
0911a60c | 115 | *msr |= SRR1_WAKEEE; |
dead760b BH |
116 | break; |
117 | case POWERPC_EXCP_DECR: | |
0911a60c | 118 | *msr |= SRR1_WAKEDEC; |
dead760b BH |
119 | break; |
120 | case POWERPC_EXCP_SDOOR: | |
0911a60c | 121 | *msr |= SRR1_WAKEDBELL; |
dead760b BH |
122 | break; |
123 | case POWERPC_EXCP_SDOOR_HV: | |
0911a60c | 124 | *msr |= SRR1_WAKEHDBELL; |
dead760b BH |
125 | break; |
126 | case POWERPC_EXCP_HV_MAINT: | |
0911a60c | 127 | *msr |= SRR1_WAKEHMI; |
dead760b | 128 | break; |
d8ce5fd6 | 129 | case POWERPC_EXCP_HVIRT: |
0911a60c | 130 | *msr |= SRR1_WAKEHVI; |
d8ce5fd6 | 131 | break; |
dead760b BH |
132 | default: |
133 | cpu_abort(cs, "Unsupported exception %d in Power Save mode\n", | |
134 | excp); | |
135 | } | |
136 | return POWERPC_EXCP_RESET; | |
137 | } | |
138 | ||
2586a4d7 FR |
139 | static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail) |
140 | { | |
141 | uint64_t offset = 0; | |
142 | ||
143 | switch (ail) { | |
bc5fdfc0 FR |
144 | case AIL_NONE: |
145 | break; | |
2586a4d7 FR |
146 | case AIL_0001_8000: |
147 | offset = 0x18000; | |
148 | break; | |
149 | case AIL_C000_0000_0000_4000: | |
150 | offset = 0xc000000000004000ull; | |
151 | break; | |
152 | default: | |
153 | cpu_abort(cs, "Invalid AIL combination %d\n", ail); | |
154 | break; | |
155 | } | |
156 | ||
157 | return offset; | |
158 | } | |
dead760b | 159 | |
ad77c6ca NP |
160 | static inline void powerpc_set_excp_state(PowerPCCPU *cpu, |
161 | target_ulong vector, target_ulong msr) | |
162 | { | |
163 | CPUState *cs = CPU(cpu); | |
164 | CPUPPCState *env = &cpu->env; | |
165 | ||
166 | /* | |
167 | * We don't use hreg_store_msr here as already have treated any | |
168 | * special case that could occur. Just store MSR and update hflags | |
169 | * | |
170 | * Note: We *MUST* not use hreg_store_msr() as-is anyway because it | |
171 | * will prevent setting of the HV bit which some exceptions might need | |
172 | * to do. | |
173 | */ | |
174 | env->msr = msr & env->msr_mask; | |
175 | hreg_compute_hflags(env); | |
176 | env->nip = vector; | |
177 | /* Reset exception state */ | |
178 | cs->exception_index = POWERPC_EXCP_NONE; | |
179 | env->error_code = 0; | |
180 | ||
181 | /* Reset the reservation */ | |
182 | env->reserve_addr = -1; | |
183 | ||
184 | /* | |
185 | * Any interrupt is context synchronizing, check if TCG TLB needs | |
186 | * a delayed flush on ppc64 | |
187 | */ | |
188 | check_tlb_flush(env, false); | |
189 | } | |
190 | ||
47733729 DG |
191 | /* |
192 | * Note that this function should be greatly optimized when called | |
193 | * with a constant excp, from ppc_hw_interrupt | |
c79c73f6 | 194 | */ |
5c26a5b3 | 195 | static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) |
c79c73f6 | 196 | { |
27103424 | 197 | CPUState *cs = CPU(cpu); |
5c26a5b3 | 198 | CPUPPCState *env = &cpu->env; |
c79c73f6 | 199 | target_ulong msr, new_msr, vector; |
3c89b8d6 | 200 | int srr0, srr1, asrr0, asrr1, lev = -1, ail; |
6d49d6d4 | 201 | bool lpes0; |
c79c73f6 BS |
202 | |
203 | qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx | |
204 | " => %08x (%02x)\n", env->nip, excp, env->error_code); | |
205 | ||
206 | /* new srr1 value excluding must-be-zero bits */ | |
a1bb7384 SW |
207 | if (excp_model == POWERPC_EXCP_BOOKE) { |
208 | msr = env->msr; | |
209 | } else { | |
210 | msr = env->msr & ~0x783f0000ULL; | |
211 | } | |
c79c73f6 | 212 | |
47733729 DG |
213 | /* |
214 | * new interrupt handler msr preserves existing HV and ME unless | |
6d49d6d4 BH |
215 | * explicitly overriden |
216 | */ | |
217 | new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB); | |
c79c73f6 BS |
218 | |
219 | /* target registers */ | |
220 | srr0 = SPR_SRR0; | |
221 | srr1 = SPR_SRR1; | |
222 | asrr0 = -1; | |
223 | asrr1 = -1; | |
224 | ||
21c0d66a BH |
225 | /* |
226 | * check for special resume at 0x100 from doze/nap/sleep/winkle on | |
227 | * P7/P8/P9 | |
228 | */ | |
1e7fd61d | 229 | if (env->resume_as_sreset) { |
dead760b | 230 | excp = powerpc_reset_wakeup(cs, env, excp, &msr); |
7778a575 BH |
231 | } |
232 | ||
47733729 | 233 | /* |
136fbf65 | 234 | * Exception targeting modifiers |
6d49d6d4 | 235 | * |
a790e82b | 236 | * LPES0 is supported on POWER7/8/9 |
6d49d6d4 BH |
237 | * LPES1 is not supported (old iSeries mode) |
238 | * | |
239 | * On anything else, we behave as if LPES0 is 1 | |
240 | * (externals don't alter MSR:HV) | |
5c94b2a5 CLG |
241 | * |
242 | * AIL is initialized here but can be cleared by | |
243 | * selected exceptions | |
244 | */ | |
245 | #if defined(TARGET_PPC64) | |
246 | if (excp_model == POWERPC_EXCP_POWER7 || | |
a790e82b BH |
247 | excp_model == POWERPC_EXCP_POWER8 || |
248 | excp_model == POWERPC_EXCP_POWER9) { | |
6d49d6d4 | 249 | lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); |
a790e82b | 250 | if (excp_model != POWERPC_EXCP_POWER7) { |
5c94b2a5 CLG |
251 | ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; |
252 | } else { | |
253 | ail = 0; | |
254 | } | |
255 | } else | |
256 | #endif /* defined(TARGET_PPC64) */ | |
257 | { | |
6d49d6d4 | 258 | lpes0 = true; |
5c94b2a5 CLG |
259 | ail = 0; |
260 | } | |
261 | ||
47733729 DG |
262 | /* |
263 | * Hypervisor emulation assistance interrupt only exists on server | |
9b2fadda BH |
264 | * arch 2.05 server or later. We also don't want to generate it if |
265 | * we don't have HVB in msr_mask (PAPR mode). | |
266 | */ | |
267 | if (excp == POWERPC_EXCP_HV_EMU | |
268 | #if defined(TARGET_PPC64) | |
d57d72a8 | 269 | && !(mmu_is_64bit(env->mmu_model) && (env->msr_mask & MSR_HVB)) |
9b2fadda BH |
270 | #endif /* defined(TARGET_PPC64) */ |
271 | ||
272 | ) { | |
273 | excp = POWERPC_EXCP_PROGRAM; | |
274 | } | |
275 | ||
c79c73f6 BS |
276 | switch (excp) { |
277 | case POWERPC_EXCP_NONE: | |
278 | /* Should never happen */ | |
279 | return; | |
280 | case POWERPC_EXCP_CRITICAL: /* Critical input */ | |
281 | switch (excp_model) { | |
282 | case POWERPC_EXCP_40x: | |
283 | srr0 = SPR_40x_SRR2; | |
284 | srr1 = SPR_40x_SRR3; | |
285 | break; | |
286 | case POWERPC_EXCP_BOOKE: | |
287 | srr0 = SPR_BOOKE_CSRR0; | |
288 | srr1 = SPR_BOOKE_CSRR1; | |
289 | break; | |
290 | case POWERPC_EXCP_G2: | |
291 | break; | |
292 | default: | |
293 | goto excp_invalid; | |
294 | } | |
bd6fefe7 | 295 | break; |
c79c73f6 BS |
296 | case POWERPC_EXCP_MCHECK: /* Machine check exception */ |
297 | if (msr_me == 0) { | |
47733729 DG |
298 | /* |
299 | * Machine check exception is not enabled. Enter | |
300 | * checkstop state. | |
c79c73f6 | 301 | */ |
013a2942 PB |
302 | fprintf(stderr, "Machine check while not allowed. " |
303 | "Entering checkstop state\n"); | |
304 | if (qemu_log_separate()) { | |
c79c73f6 BS |
305 | qemu_log("Machine check while not allowed. " |
306 | "Entering checkstop state\n"); | |
c79c73f6 | 307 | } |
259186a7 | 308 | cs->halted = 1; |
044897ef | 309 | cpu_interrupt_exittb(cs); |
c79c73f6 | 310 | } |
10c21b5c | 311 | if (env->msr_mask & MSR_HVB) { |
47733729 DG |
312 | /* |
313 | * ISA specifies HV, but can be delivered to guest with HV | |
314 | * clear (e.g., see FWNMI in PAPR). | |
10c21b5c NP |
315 | */ |
316 | new_msr |= (target_ulong)MSR_HVB; | |
317 | } | |
5c94b2a5 | 318 | ail = 0; |
c79c73f6 BS |
319 | |
320 | /* machine check exceptions don't have ME set */ | |
321 | new_msr &= ~((target_ulong)1 << MSR_ME); | |
322 | ||
323 | /* XXX: should also have something loaded in DAR / DSISR */ | |
324 | switch (excp_model) { | |
325 | case POWERPC_EXCP_40x: | |
326 | srr0 = SPR_40x_SRR2; | |
327 | srr1 = SPR_40x_SRR3; | |
328 | break; | |
329 | case POWERPC_EXCP_BOOKE: | |
a1bb7384 | 330 | /* FIXME: choose one or the other based on CPU type */ |
c79c73f6 BS |
331 | srr0 = SPR_BOOKE_MCSRR0; |
332 | srr1 = SPR_BOOKE_MCSRR1; | |
333 | asrr0 = SPR_BOOKE_CSRR0; | |
334 | asrr1 = SPR_BOOKE_CSRR1; | |
335 | break; | |
336 | default: | |
337 | break; | |
338 | } | |
bd6fefe7 | 339 | break; |
c79c73f6 BS |
340 | case POWERPC_EXCP_DSI: /* Data storage exception */ |
341 | LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx | |
342 | "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); | |
bd6fefe7 | 343 | break; |
c79c73f6 BS |
344 | case POWERPC_EXCP_ISI: /* Instruction storage exception */ |
345 | LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx | |
346 | "\n", msr, env->nip); | |
c79c73f6 | 347 | msr |= env->error_code; |
bd6fefe7 | 348 | break; |
c79c73f6 | 349 | case POWERPC_EXCP_EXTERNAL: /* External input */ |
fdfba1a2 EI |
350 | cs = CPU(cpu); |
351 | ||
6d49d6d4 | 352 | if (!lpes0) { |
c79c73f6 | 353 | new_msr |= (target_ulong)MSR_HVB; |
6d49d6d4 BH |
354 | new_msr |= env->msr & ((target_ulong)1 << MSR_RI); |
355 | srr0 = SPR_HSRR0; | |
356 | srr1 = SPR_HSRR1; | |
c79c73f6 | 357 | } |
68c2dd70 AG |
358 | if (env->mpic_proxy) { |
359 | /* IACK the IRQ on delivery */ | |
fdfba1a2 | 360 | env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); |
68c2dd70 | 361 | } |
bd6fefe7 | 362 | break; |
c79c73f6 | 363 | case POWERPC_EXCP_ALIGN: /* Alignment exception */ |
c79c73f6 | 364 | /* Get rS/rD and rA from faulting opcode */ |
47733729 DG |
365 | /* |
366 | * Note: the opcode fields will not be set properly for a | |
367 | * direct store load/store, but nobody cares as nobody | |
368 | * actually uses direct store segments. | |
3433b732 BH |
369 | */ |
370 | env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16; | |
bd6fefe7 | 371 | break; |
c79c73f6 BS |
372 | case POWERPC_EXCP_PROGRAM: /* Program exception */ |
373 | switch (env->error_code & ~0xF) { | |
374 | case POWERPC_EXCP_FP: | |
375 | if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { | |
376 | LOG_EXCP("Ignore floating point exception\n"); | |
27103424 | 377 | cs->exception_index = POWERPC_EXCP_NONE; |
c79c73f6 BS |
378 | env->error_code = 0; |
379 | return; | |
380 | } | |
1b7d17ca | 381 | |
47733729 DG |
382 | /* |
383 | * FP exceptions always have NIP pointing to the faulting | |
1b7d17ca BH |
384 | * instruction, so always use store_next and claim we are |
385 | * precise in the MSR. | |
386 | */ | |
c79c73f6 | 387 | msr |= 0x00100000; |
0ee604ab | 388 | env->spr[SPR_BOOKE_ESR] = ESR_FP; |
bd6fefe7 | 389 | break; |
c79c73f6 BS |
390 | case POWERPC_EXCP_INVAL: |
391 | LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); | |
c79c73f6 BS |
392 | msr |= 0x00080000; |
393 | env->spr[SPR_BOOKE_ESR] = ESR_PIL; | |
394 | break; | |
395 | case POWERPC_EXCP_PRIV: | |
c79c73f6 BS |
396 | msr |= 0x00040000; |
397 | env->spr[SPR_BOOKE_ESR] = ESR_PPR; | |
398 | break; | |
399 | case POWERPC_EXCP_TRAP: | |
c79c73f6 BS |
400 | msr |= 0x00020000; |
401 | env->spr[SPR_BOOKE_ESR] = ESR_PTR; | |
402 | break; | |
403 | default: | |
404 | /* Should never occur */ | |
a47dddd7 | 405 | cpu_abort(cs, "Invalid program exception %d. Aborting\n", |
c79c73f6 BS |
406 | env->error_code); |
407 | break; | |
408 | } | |
bd6fefe7 | 409 | break; |
c79c73f6 | 410 | case POWERPC_EXCP_SYSCALL: /* System call exception */ |
c79c73f6 | 411 | lev = env->error_code; |
6d49d6d4 | 412 | |
6dc6b557 NP |
413 | if ((lev == 1) && cpu->vhyp) { |
414 | dump_hcall(env); | |
415 | } else { | |
416 | dump_syscall(env); | |
417 | } | |
418 | ||
47733729 DG |
419 | /* |
420 | * We need to correct the NIP which in this case is supposed | |
bd6fefe7 BH |
421 | * to point to the next instruction |
422 | */ | |
423 | env->nip += 4; | |
424 | ||
6d49d6d4 | 425 | /* "PAPR mode" built-in hypercall emulation */ |
1d1be34d DG |
426 | if ((lev == 1) && cpu->vhyp) { |
427 | PPCVirtualHypervisorClass *vhc = | |
428 | PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); | |
429 | vhc->hypercall(cpu->vhyp, cpu); | |
c79c73f6 BS |
430 | return; |
431 | } | |
6d49d6d4 | 432 | if (lev == 1) { |
c79c73f6 BS |
433 | new_msr |= (target_ulong)MSR_HVB; |
434 | } | |
bd6fefe7 | 435 | break; |
3c89b8d6 NP |
436 | case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */ |
437 | lev = env->error_code; | |
438 | dump_syscall_vectored(env); | |
439 | env->nip += 4; | |
440 | new_msr |= env->msr & ((target_ulong)1 << MSR_EE); | |
441 | new_msr |= env->msr & ((target_ulong)1 << MSR_RI); | |
442 | break; | |
bd6fefe7 | 443 | case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ |
c79c73f6 | 444 | case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ |
c79c73f6 | 445 | case POWERPC_EXCP_DECR: /* Decrementer exception */ |
bd6fefe7 | 446 | break; |
c79c73f6 BS |
447 | case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ |
448 | /* FIT on 4xx */ | |
449 | LOG_EXCP("FIT exception\n"); | |
bd6fefe7 | 450 | break; |
c79c73f6 BS |
451 | case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ |
452 | LOG_EXCP("WDT exception\n"); | |
453 | switch (excp_model) { | |
454 | case POWERPC_EXCP_BOOKE: | |
455 | srr0 = SPR_BOOKE_CSRR0; | |
456 | srr1 = SPR_BOOKE_CSRR1; | |
457 | break; | |
458 | default: | |
459 | break; | |
460 | } | |
bd6fefe7 | 461 | break; |
c79c73f6 | 462 | case POWERPC_EXCP_DTLB: /* Data TLB error */ |
c79c73f6 | 463 | case POWERPC_EXCP_ITLB: /* Instruction TLB error */ |
bd6fefe7 | 464 | break; |
c79c73f6 | 465 | case POWERPC_EXCP_DEBUG: /* Debug interrupt */ |
0e3bf489 | 466 | if (env->flags & POWERPC_FLAG_DE) { |
a1bb7384 | 467 | /* FIXME: choose one or the other based on CPU type */ |
c79c73f6 BS |
468 | srr0 = SPR_BOOKE_DSRR0; |
469 | srr1 = SPR_BOOKE_DSRR1; | |
470 | asrr0 = SPR_BOOKE_CSRR0; | |
471 | asrr1 = SPR_BOOKE_CSRR1; | |
0e3bf489 RK |
472 | /* DBSR already modified by caller */ |
473 | } else { | |
474 | cpu_abort(cs, "Debug exception triggered on unsupported model\n"); | |
c79c73f6 | 475 | } |
bd6fefe7 | 476 | break; |
c79c73f6 BS |
477 | case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ |
478 | env->spr[SPR_BOOKE_ESR] = ESR_SPV; | |
bd6fefe7 | 479 | break; |
c79c73f6 BS |
480 | case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ |
481 | /* XXX: TODO */ | |
a47dddd7 | 482 | cpu_abort(cs, "Embedded floating point data exception " |
c79c73f6 BS |
483 | "is not implemented yet !\n"); |
484 | env->spr[SPR_BOOKE_ESR] = ESR_SPV; | |
bd6fefe7 | 485 | break; |
c79c73f6 BS |
486 | case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ |
487 | /* XXX: TODO */ | |
a47dddd7 | 488 | cpu_abort(cs, "Embedded floating point round exception " |
c79c73f6 BS |
489 | "is not implemented yet !\n"); |
490 | env->spr[SPR_BOOKE_ESR] = ESR_SPV; | |
bd6fefe7 | 491 | break; |
c79c73f6 BS |
492 | case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ |
493 | /* XXX: TODO */ | |
a47dddd7 | 494 | cpu_abort(cs, |
c79c73f6 | 495 | "Performance counter exception is not implemented yet !\n"); |
bd6fefe7 | 496 | break; |
c79c73f6 | 497 | case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ |
bd6fefe7 | 498 | break; |
c79c73f6 BS |
499 | case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ |
500 | srr0 = SPR_BOOKE_CSRR0; | |
501 | srr1 = SPR_BOOKE_CSRR1; | |
bd6fefe7 | 502 | break; |
c79c73f6 | 503 | case POWERPC_EXCP_RESET: /* System reset exception */ |
f85bcec3 | 504 | /* A power-saving exception sets ME, otherwise it is unchanged */ |
c79c73f6 BS |
505 | if (msr_pow) { |
506 | /* indicate that we resumed from power save mode */ | |
507 | msr |= 0x10000; | |
f85bcec3 | 508 | new_msr |= ((target_ulong)1 << MSR_ME); |
c79c73f6 | 509 | } |
10c21b5c | 510 | if (env->msr_mask & MSR_HVB) { |
47733729 DG |
511 | /* |
512 | * ISA specifies HV, but can be delivered to guest with HV | |
513 | * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU). | |
10c21b5c NP |
514 | */ |
515 | new_msr |= (target_ulong)MSR_HVB; | |
516 | } else { | |
517 | if (msr_pow) { | |
518 | cpu_abort(cs, "Trying to deliver power-saving system reset " | |
519 | "exception %d with no HV support\n", excp); | |
520 | } | |
521 | } | |
5c94b2a5 | 522 | ail = 0; |
bd6fefe7 | 523 | break; |
c79c73f6 | 524 | case POWERPC_EXCP_DSEG: /* Data segment exception */ |
c79c73f6 | 525 | case POWERPC_EXCP_ISEG: /* Instruction segment exception */ |
c79c73f6 | 526 | case POWERPC_EXCP_TRACE: /* Trace exception */ |
bd6fefe7 | 527 | break; |
d04ea940 CLG |
528 | case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ |
529 | msr |= env->error_code; | |
295397f5 | 530 | /* fall through */ |
bd6fefe7 | 531 | case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ |
c79c73f6 | 532 | case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ |
c79c73f6 | 533 | case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ |
c79c73f6 | 534 | case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ |
7af1e7b0 | 535 | case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ |
bd6fefe7 | 536 | case POWERPC_EXCP_HV_EMU: |
d8ce5fd6 | 537 | case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ |
c79c73f6 BS |
538 | srr0 = SPR_HSRR0; |
539 | srr1 = SPR_HSRR1; | |
540 | new_msr |= (target_ulong)MSR_HVB; | |
541 | new_msr |= env->msr & ((target_ulong)1 << MSR_RI); | |
bd6fefe7 | 542 | break; |
c79c73f6 | 543 | case POWERPC_EXCP_VPU: /* Vector unavailable exception */ |
1f29871c | 544 | case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ |
7019cb3d | 545 | case POWERPC_EXCP_FU: /* Facility unavailable exception */ |
5310799a BS |
546 | #ifdef TARGET_PPC64 |
547 | env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); | |
493028d8 CLG |
548 | #endif |
549 | break; | |
550 | case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ | |
551 | #ifdef TARGET_PPC64 | |
552 | env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); | |
553 | srr0 = SPR_HSRR0; | |
554 | srr1 = SPR_HSRR1; | |
555 | new_msr |= (target_ulong)MSR_HVB; | |
556 | new_msr |= env->msr & ((target_ulong)1 << MSR_RI); | |
5310799a | 557 | #endif |
bd6fefe7 | 558 | break; |
c79c73f6 BS |
559 | case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ |
560 | LOG_EXCP("PIT exception\n"); | |
bd6fefe7 | 561 | break; |
c79c73f6 BS |
562 | case POWERPC_EXCP_IO: /* IO error exception */ |
563 | /* XXX: TODO */ | |
a47dddd7 | 564 | cpu_abort(cs, "601 IO error exception is not implemented yet !\n"); |
bd6fefe7 | 565 | break; |
c79c73f6 BS |
566 | case POWERPC_EXCP_RUNM: /* Run mode exception */ |
567 | /* XXX: TODO */ | |
a47dddd7 | 568 | cpu_abort(cs, "601 run mode exception is not implemented yet !\n"); |
bd6fefe7 | 569 | break; |
c79c73f6 BS |
570 | case POWERPC_EXCP_EMUL: /* Emulation trap exception */ |
571 | /* XXX: TODO */ | |
a47dddd7 | 572 | cpu_abort(cs, "602 emulation trap exception " |
c79c73f6 | 573 | "is not implemented yet !\n"); |
bd6fefe7 | 574 | break; |
c79c73f6 | 575 | case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ |
c79c73f6 BS |
576 | switch (excp_model) { |
577 | case POWERPC_EXCP_602: | |
578 | case POWERPC_EXCP_603: | |
579 | case POWERPC_EXCP_603E: | |
580 | case POWERPC_EXCP_G2: | |
581 | goto tlb_miss_tgpr; | |
582 | case POWERPC_EXCP_7x5: | |
583 | goto tlb_miss; | |
584 | case POWERPC_EXCP_74xx: | |
585 | goto tlb_miss_74xx; | |
586 | default: | |
a47dddd7 | 587 | cpu_abort(cs, "Invalid instruction TLB miss exception\n"); |
c79c73f6 BS |
588 | break; |
589 | } | |
590 | break; | |
591 | case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ | |
c79c73f6 BS |
592 | switch (excp_model) { |
593 | case POWERPC_EXCP_602: | |
594 | case POWERPC_EXCP_603: | |
595 | case POWERPC_EXCP_603E: | |
596 | case POWERPC_EXCP_G2: | |
597 | goto tlb_miss_tgpr; | |
598 | case POWERPC_EXCP_7x5: | |
599 | goto tlb_miss; | |
600 | case POWERPC_EXCP_74xx: | |
601 | goto tlb_miss_74xx; | |
602 | default: | |
a47dddd7 | 603 | cpu_abort(cs, "Invalid data load TLB miss exception\n"); |
c79c73f6 BS |
604 | break; |
605 | } | |
606 | break; | |
607 | case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ | |
c79c73f6 BS |
608 | switch (excp_model) { |
609 | case POWERPC_EXCP_602: | |
610 | case POWERPC_EXCP_603: | |
611 | case POWERPC_EXCP_603E: | |
612 | case POWERPC_EXCP_G2: | |
613 | tlb_miss_tgpr: | |
614 | /* Swap temporary saved registers with GPRs */ | |
615 | if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { | |
616 | new_msr |= (target_ulong)1 << MSR_TGPR; | |
617 | hreg_swap_gpr_tgpr(env); | |
618 | } | |
619 | goto tlb_miss; | |
620 | case POWERPC_EXCP_7x5: | |
621 | tlb_miss: | |
622 | #if defined(DEBUG_SOFTWARE_TLB) | |
623 | if (qemu_log_enabled()) { | |
624 | const char *es; | |
625 | target_ulong *miss, *cmp; | |
626 | int en; | |
627 | ||
628 | if (excp == POWERPC_EXCP_IFTLB) { | |
629 | es = "I"; | |
630 | en = 'I'; | |
631 | miss = &env->spr[SPR_IMISS]; | |
632 | cmp = &env->spr[SPR_ICMP]; | |
633 | } else { | |
634 | if (excp == POWERPC_EXCP_DLTLB) { | |
635 | es = "DL"; | |
636 | } else { | |
637 | es = "DS"; | |
638 | } | |
639 | en = 'D'; | |
640 | miss = &env->spr[SPR_DMISS]; | |
641 | cmp = &env->spr[SPR_DCMP]; | |
642 | } | |
643 | qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " | |
644 | TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 " | |
645 | TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, | |
646 | env->spr[SPR_HASH1], env->spr[SPR_HASH2], | |
647 | env->error_code); | |
648 | } | |
649 | #endif | |
650 | msr |= env->crf[0] << 28; | |
651 | msr |= env->error_code; /* key, D/I, S/L bits */ | |
652 | /* Set way using a LRU mechanism */ | |
653 | msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; | |
654 | break; | |
655 | case POWERPC_EXCP_74xx: | |
656 | tlb_miss_74xx: | |
657 | #if defined(DEBUG_SOFTWARE_TLB) | |
658 | if (qemu_log_enabled()) { | |
659 | const char *es; | |
660 | target_ulong *miss, *cmp; | |
661 | int en; | |
662 | ||
663 | if (excp == POWERPC_EXCP_IFTLB) { | |
664 | es = "I"; | |
665 | en = 'I'; | |
666 | miss = &env->spr[SPR_TLBMISS]; | |
667 | cmp = &env->spr[SPR_PTEHI]; | |
668 | } else { | |
669 | if (excp == POWERPC_EXCP_DLTLB) { | |
670 | es = "DL"; | |
671 | } else { | |
672 | es = "DS"; | |
673 | } | |
674 | en = 'D'; | |
675 | miss = &env->spr[SPR_TLBMISS]; | |
676 | cmp = &env->spr[SPR_PTEHI]; | |
677 | } | |
678 | qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " | |
679 | TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, | |
680 | env->error_code); | |
681 | } | |
682 | #endif | |
683 | msr |= env->error_code; /* key bit */ | |
684 | break; | |
685 | default: | |
a47dddd7 | 686 | cpu_abort(cs, "Invalid data store TLB miss exception\n"); |
c79c73f6 BS |
687 | break; |
688 | } | |
bd6fefe7 | 689 | break; |
c79c73f6 BS |
690 | case POWERPC_EXCP_FPA: /* Floating-point assist exception */ |
691 | /* XXX: TODO */ | |
a47dddd7 | 692 | cpu_abort(cs, "Floating point assist exception " |
c79c73f6 | 693 | "is not implemented yet !\n"); |
bd6fefe7 | 694 | break; |
c79c73f6 BS |
695 | case POWERPC_EXCP_DABR: /* Data address breakpoint */ |
696 | /* XXX: TODO */ | |
a47dddd7 | 697 | cpu_abort(cs, "DABR exception is not implemented yet !\n"); |
bd6fefe7 | 698 | break; |
c79c73f6 BS |
699 | case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ |
700 | /* XXX: TODO */ | |
a47dddd7 | 701 | cpu_abort(cs, "IABR exception is not implemented yet !\n"); |
bd6fefe7 | 702 | break; |
c79c73f6 BS |
703 | case POWERPC_EXCP_SMI: /* System management interrupt */ |
704 | /* XXX: TODO */ | |
a47dddd7 | 705 | cpu_abort(cs, "SMI exception is not implemented yet !\n"); |
bd6fefe7 | 706 | break; |
c79c73f6 BS |
707 | case POWERPC_EXCP_THERM: /* Thermal interrupt */ |
708 | /* XXX: TODO */ | |
a47dddd7 | 709 | cpu_abort(cs, "Thermal management exception " |
c79c73f6 | 710 | "is not implemented yet !\n"); |
bd6fefe7 | 711 | break; |
c79c73f6 | 712 | case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ |
c79c73f6 | 713 | /* XXX: TODO */ |
a47dddd7 | 714 | cpu_abort(cs, |
c79c73f6 | 715 | "Performance counter exception is not implemented yet !\n"); |
bd6fefe7 | 716 | break; |
c79c73f6 BS |
717 | case POWERPC_EXCP_VPUA: /* Vector assist exception */ |
718 | /* XXX: TODO */ | |
a47dddd7 | 719 | cpu_abort(cs, "VPU assist exception is not implemented yet !\n"); |
bd6fefe7 | 720 | break; |
c79c73f6 BS |
721 | case POWERPC_EXCP_SOFTP: /* Soft patch exception */ |
722 | /* XXX: TODO */ | |
a47dddd7 | 723 | cpu_abort(cs, |
c79c73f6 | 724 | "970 soft-patch exception is not implemented yet !\n"); |
bd6fefe7 | 725 | break; |
c79c73f6 BS |
726 | case POWERPC_EXCP_MAINT: /* Maintenance exception */ |
727 | /* XXX: TODO */ | |
a47dddd7 | 728 | cpu_abort(cs, |
c79c73f6 | 729 | "970 maintenance exception is not implemented yet !\n"); |
bd6fefe7 | 730 | break; |
c79c73f6 BS |
731 | case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ |
732 | /* XXX: TODO */ | |
a47dddd7 | 733 | cpu_abort(cs, "Maskable external exception " |
c79c73f6 | 734 | "is not implemented yet !\n"); |
bd6fefe7 | 735 | break; |
c79c73f6 BS |
736 | case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ |
737 | /* XXX: TODO */ | |
a47dddd7 | 738 | cpu_abort(cs, "Non maskable external exception " |
c79c73f6 | 739 | "is not implemented yet !\n"); |
bd6fefe7 | 740 | break; |
c79c73f6 BS |
741 | default: |
742 | excp_invalid: | |
a47dddd7 | 743 | cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); |
c79c73f6 | 744 | break; |
c79c73f6 | 745 | } |
bd6fefe7 | 746 | |
6d49d6d4 | 747 | /* Sanity check */ |
10c21b5c NP |
748 | if (!(env->msr_mask & MSR_HVB)) { |
749 | if (new_msr & MSR_HVB) { | |
750 | cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " | |
751 | "no HV support\n", excp); | |
752 | } | |
753 | if (srr0 == SPR_HSRR0) { | |
754 | cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " | |
755 | "no HV support\n", excp); | |
756 | } | |
6d49d6d4 BH |
757 | } |
758 | ||
47733729 DG |
759 | /* |
760 | * Sort out endianness of interrupt, this differs depending on the | |
6d49d6d4 BH |
761 | * CPU, the HV mode, etc... |
762 | */ | |
1e0c7e55 | 763 | #ifdef TARGET_PPC64 |
6d49d6d4 BH |
764 | if (excp_model == POWERPC_EXCP_POWER7) { |
765 | if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) { | |
766 | new_msr |= (target_ulong)1 << MSR_LE; | |
767 | } | |
768 | } else if (excp_model == POWERPC_EXCP_POWER8) { | |
769 | if (new_msr & MSR_HVB) { | |
a790e82b BH |
770 | if (env->spr[SPR_HID0] & HID0_HILE) { |
771 | new_msr |= (target_ulong)1 << MSR_LE; | |
772 | } | |
773 | } else if (env->spr[SPR_LPCR] & LPCR_ILE) { | |
774 | new_msr |= (target_ulong)1 << MSR_LE; | |
775 | } | |
776 | } else if (excp_model == POWERPC_EXCP_POWER9) { | |
777 | if (new_msr & MSR_HVB) { | |
778 | if (env->spr[SPR_HID0] & HID0_POWER9_HILE) { | |
6d49d6d4 BH |
779 | new_msr |= (target_ulong)1 << MSR_LE; |
780 | } | |
781 | } else if (env->spr[SPR_LPCR] & LPCR_ILE) { | |
1e0c7e55 AB |
782 | new_msr |= (target_ulong)1 << MSR_LE; |
783 | } | |
784 | } else if (msr_ile) { | |
785 | new_msr |= (target_ulong)1 << MSR_LE; | |
786 | } | |
787 | #else | |
c79c73f6 BS |
788 | if (msr_ile) { |
789 | new_msr |= (target_ulong)1 << MSR_LE; | |
790 | } | |
1e0c7e55 | 791 | #endif |
c79c73f6 | 792 | |
47733729 DG |
793 | /* |
794 | * AIL only works if there is no HV transition and we are running | |
795 | * with translations enabled | |
5c94b2a5 | 796 | */ |
6d49d6d4 BH |
797 | if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) || |
798 | ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) { | |
5c94b2a5 CLG |
799 | ail = 0; |
800 | } | |
3c89b8d6 NP |
801 | |
802 | vector = env->excp_vectors[excp]; | |
803 | if (vector == (target_ulong)-1ULL) { | |
804 | cpu_abort(cs, "Raised an exception without defined vector %d\n", | |
805 | excp); | |
806 | } | |
807 | ||
808 | vector |= env->excp_prefix; | |
809 | ||
810 | /* If any alternate SRR register are defined, duplicate saved values */ | |
811 | if (asrr0 != -1) { | |
812 | env->spr[asrr0] = env->nip; | |
813 | } | |
814 | if (asrr1 != -1) { | |
815 | env->spr[asrr1] = msr; | |
5c94b2a5 CLG |
816 | } |
817 | ||
c79c73f6 BS |
818 | #if defined(TARGET_PPC64) |
819 | if (excp_model == POWERPC_EXCP_BOOKE) { | |
e42a61f1 AG |
820 | if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { |
821 | /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ | |
c79c73f6 | 822 | new_msr |= (target_ulong)1 << MSR_CM; |
e42a61f1 AG |
823 | } else { |
824 | vector = (uint32_t)vector; | |
c79c73f6 BS |
825 | } |
826 | } else { | |
d57d72a8 | 827 | if (!msr_isf && !mmu_is_64bit(env->mmu_model)) { |
c79c73f6 BS |
828 | vector = (uint32_t)vector; |
829 | } else { | |
830 | new_msr |= (target_ulong)1 << MSR_SF; | |
831 | } | |
832 | } | |
833 | #endif | |
cd0c6f47 | 834 | |
3c89b8d6 NP |
835 | if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { |
836 | /* Save PC */ | |
837 | env->spr[srr0] = env->nip; | |
838 | ||
839 | /* Save MSR */ | |
840 | env->spr[srr1] = msr; | |
841 | ||
842 | /* Handle AIL */ | |
843 | if (ail) { | |
844 | new_msr |= (1 << MSR_IR) | (1 << MSR_DR); | |
845 | vector |= ppc_excp_vector_offset(cs, ail); | |
846 | } | |
847 | ||
848 | #if defined(TARGET_PPC64) | |
849 | } else { | |
850 | /* scv AIL is a little different */ | |
851 | if (ail) { | |
852 | new_msr |= (1 << MSR_IR) | (1 << MSR_DR); | |
853 | } | |
854 | if (ail == AIL_C000_0000_0000_4000) { | |
855 | vector |= 0xc000000000003000ull; | |
856 | } else { | |
857 | vector |= 0x0000000000017000ull; | |
858 | } | |
859 | vector += lev * 0x20; | |
860 | ||
861 | env->lr = env->nip; | |
862 | env->ctr = msr; | |
863 | #endif | |
864 | } | |
865 | ||
ad77c6ca | 866 | powerpc_set_excp_state(cpu, vector, new_msr); |
c79c73f6 BS |
867 | } |
868 | ||
97a8ea5a | 869 | void ppc_cpu_do_interrupt(CPUState *cs) |
c79c73f6 | 870 | { |
97a8ea5a AF |
871 | PowerPCCPU *cpu = POWERPC_CPU(cs); |
872 | CPUPPCState *env = &cpu->env; | |
5c26a5b3 | 873 | |
27103424 | 874 | powerpc_excp(cpu, env->excp_model, cs->exception_index); |
c79c73f6 BS |
875 | } |
876 | ||
458dd766 | 877 | static void ppc_hw_interrupt(CPUPPCState *env) |
c79c73f6 | 878 | { |
db70b311 | 879 | PowerPCCPU *cpu = env_archcpu(env); |
3621e2c9 | 880 | bool async_deliver; |
259186a7 | 881 | |
c79c73f6 BS |
882 | /* External reset */ |
883 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { | |
884 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); | |
5c26a5b3 | 885 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); |
c79c73f6 BS |
886 | return; |
887 | } | |
888 | /* Machine check exception */ | |
889 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { | |
890 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); | |
5c26a5b3 | 891 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_MCHECK); |
c79c73f6 BS |
892 | return; |
893 | } | |
894 | #if 0 /* TODO */ | |
895 | /* External debug exception */ | |
896 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { | |
897 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); | |
5c26a5b3 | 898 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DEBUG); |
c79c73f6 BS |
899 | return; |
900 | } | |
901 | #endif | |
3621e2c9 BH |
902 | |
903 | /* | |
904 | * For interrupts that gate on MSR:EE, we need to do something a | |
905 | * bit more subtle, as we need to let them through even when EE is | |
906 | * clear when coming out of some power management states (in order | |
907 | * for them to become a 0x100). | |
908 | */ | |
1e7fd61d | 909 | async_deliver = (msr_ee != 0) || env->resume_as_sreset; |
3621e2c9 | 910 | |
4b236b62 BH |
911 | /* Hypervisor decrementer exception */ |
912 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { | |
913 | /* LPCR will be clear when not supported so this will work */ | |
914 | bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); | |
3621e2c9 | 915 | if ((async_deliver || msr_hv == 0) && hdice) { |
4b236b62 BH |
916 | /* HDEC clears on delivery */ |
917 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); | |
5c26a5b3 | 918 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR); |
c79c73f6 BS |
919 | return; |
920 | } | |
921 | } | |
d8ce5fd6 BH |
922 | |
923 | /* Hypervisor virtualization interrupt */ | |
924 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_HVIRT)) { | |
925 | /* LPCR will be clear when not supported so this will work */ | |
926 | bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE); | |
927 | if ((async_deliver || msr_hv == 0) && hvice) { | |
928 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HVIRT); | |
929 | return; | |
930 | } | |
931 | } | |
932 | ||
933 | /* External interrupt can ignore MSR:EE under some circumstances */ | |
d1dbe37c BH |
934 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { |
935 | bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); | |
6eebe6dc BH |
936 | bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); |
937 | /* HEIC blocks delivery to the hypervisor */ | |
938 | if ((async_deliver && !(heic && msr_hv && !msr_pr)) || | |
939 | (env->has_hv_mode && msr_hv == 0 && !lpes0)) { | |
d1dbe37c BH |
940 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL); |
941 | return; | |
942 | } | |
943 | } | |
c79c73f6 BS |
944 | if (msr_ce != 0) { |
945 | /* External critical interrupt */ | |
946 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { | |
5c26a5b3 | 947 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_CRITICAL); |
c79c73f6 BS |
948 | return; |
949 | } | |
950 | } | |
3621e2c9 | 951 | if (async_deliver != 0) { |
c79c73f6 BS |
952 | /* Watchdog timer on embedded PowerPC */ |
953 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { | |
954 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); | |
5c26a5b3 | 955 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_WDT); |
c79c73f6 BS |
956 | return; |
957 | } | |
958 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { | |
959 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); | |
5c26a5b3 | 960 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORCI); |
c79c73f6 BS |
961 | return; |
962 | } | |
963 | /* Fixed interval timer on embedded PowerPC */ | |
964 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { | |
965 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); | |
5c26a5b3 | 966 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_FIT); |
c79c73f6 BS |
967 | return; |
968 | } | |
969 | /* Programmable interval timer on embedded PowerPC */ | |
970 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { | |
971 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); | |
5c26a5b3 | 972 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PIT); |
c79c73f6 BS |
973 | return; |
974 | } | |
975 | /* Decrementer exception */ | |
976 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { | |
e81a982a AG |
977 | if (ppc_decr_clear_on_delivery(env)) { |
978 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); | |
979 | } | |
5c26a5b3 | 980 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DECR); |
c79c73f6 BS |
981 | return; |
982 | } | |
c79c73f6 BS |
983 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { |
984 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); | |
5ba7ba1d CLG |
985 | if (is_book3s_arch2x(env)) { |
986 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR); | |
987 | } else { | |
988 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI); | |
989 | } | |
c79c73f6 BS |
990 | return; |
991 | } | |
7af1e7b0 CLG |
992 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) { |
993 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL); | |
994 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR_HV); | |
995 | return; | |
996 | } | |
c79c73f6 BS |
997 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { |
998 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); | |
5c26a5b3 | 999 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PERFM); |
c79c73f6 BS |
1000 | return; |
1001 | } | |
1002 | /* Thermal interrupt */ | |
1003 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { | |
1004 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); | |
5c26a5b3 | 1005 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_THERM); |
c79c73f6 BS |
1006 | return; |
1007 | } | |
1008 | } | |
f8154fd2 BH |
1009 | |
1010 | if (env->resume_as_sreset) { | |
1011 | /* | |
1012 | * This is a bug ! It means that has_work took us out of halt without | |
1013 | * anything to deliver while in a PM state that requires getting | |
1014 | * out via a 0x100 | |
1015 | * | |
1016 | * This means we will incorrectly execute past the power management | |
1017 | * instruction instead of triggering a reset. | |
1018 | * | |
136fbf65 | 1019 | * It generally means a discrepancy between the wakeup conditions in the |
f8154fd2 BH |
1020 | * processor has_work implementation and the logic in this function. |
1021 | */ | |
db70b311 | 1022 | cpu_abort(env_cpu(env), |
f8154fd2 BH |
1023 | "Wakeup from PM state but interrupt Undelivered"); |
1024 | } | |
c79c73f6 | 1025 | } |
34316482 | 1026 | |
b5b7f391 | 1027 | void ppc_cpu_do_system_reset(CPUState *cs) |
34316482 AK |
1028 | { |
1029 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1030 | CPUPPCState *env = &cpu->env; | |
1031 | ||
1032 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); | |
1033 | } | |
ad77c6ca NP |
1034 | |
1035 | void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) | |
1036 | { | |
1037 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1038 | CPUPPCState *env = &cpu->env; | |
1039 | PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); | |
1040 | target_ulong msr = 0; | |
1041 | ||
1042 | /* | |
1043 | * Set MSR and NIP for the handler, SRR0/1, DAR and DSISR have already | |
1044 | * been set by KVM. | |
1045 | */ | |
1046 | msr = (1ULL << MSR_ME); | |
1047 | msr |= env->msr & (1ULL << MSR_SF); | |
1048 | if (!(*pcc->interrupts_big_endian)(cpu)) { | |
1049 | msr |= (1ULL << MSR_LE); | |
1050 | } | |
1051 | ||
1052 | powerpc_set_excp_state(cpu, vector, msr); | |
1053 | } | |
c79c73f6 BS |
1054 | #endif /* !CONFIG_USER_ONLY */ |
1055 | ||
458dd766 RH |
1056 | bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) |
1057 | { | |
1058 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1059 | CPUPPCState *env = &cpu->env; | |
1060 | ||
1061 | if (interrupt_request & CPU_INTERRUPT_HARD) { | |
1062 | ppc_hw_interrupt(env); | |
1063 | if (env->pending_interrupts == 0) { | |
1064 | cs->interrupt_request &= ~CPU_INTERRUPT_HARD; | |
1065 | } | |
1066 | return true; | |
1067 | } | |
1068 | return false; | |
1069 | } | |
1070 | ||
c79c73f6 BS |
1071 | #if defined(DEBUG_OP) |
1072 | static void cpu_dump_rfi(target_ulong RA, target_ulong msr) | |
1073 | { | |
1074 | qemu_log("Return from exception at " TARGET_FMT_lx " with flags " | |
1075 | TARGET_FMT_lx "\n", RA, msr); | |
1076 | } | |
1077 | #endif | |
1078 | ||
ad71ed68 BS |
1079 | /*****************************************************************************/ |
1080 | /* Exceptions processing helpers */ | |
1081 | ||
db789c6c BH |
1082 | void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, |
1083 | uint32_t error_code, uintptr_t raddr) | |
ad71ed68 | 1084 | { |
db70b311 | 1085 | CPUState *cs = env_cpu(env); |
27103424 | 1086 | |
27103424 | 1087 | cs->exception_index = exception; |
ad71ed68 | 1088 | env->error_code = error_code; |
db789c6c BH |
1089 | cpu_loop_exit_restore(cs, raddr); |
1090 | } | |
1091 | ||
1092 | void raise_exception_err(CPUPPCState *env, uint32_t exception, | |
1093 | uint32_t error_code) | |
1094 | { | |
1095 | raise_exception_err_ra(env, exception, error_code, 0); | |
1096 | } | |
1097 | ||
1098 | void raise_exception(CPUPPCState *env, uint32_t exception) | |
1099 | { | |
1100 | raise_exception_err_ra(env, exception, 0, 0); | |
1101 | } | |
1102 | ||
1103 | void raise_exception_ra(CPUPPCState *env, uint32_t exception, | |
1104 | uintptr_t raddr) | |
1105 | { | |
1106 | raise_exception_err_ra(env, exception, 0, raddr); | |
1107 | } | |
1108 | ||
1109 | void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, | |
1110 | uint32_t error_code) | |
1111 | { | |
1112 | raise_exception_err_ra(env, exception, error_code, 0); | |
ad71ed68 BS |
1113 | } |
1114 | ||
e5f17ac6 | 1115 | void helper_raise_exception(CPUPPCState *env, uint32_t exception) |
ad71ed68 | 1116 | { |
db789c6c | 1117 | raise_exception_err_ra(env, exception, 0, 0); |
ad71ed68 BS |
1118 | } |
1119 | ||
1120 | #if !defined(CONFIG_USER_ONLY) | |
e5f17ac6 | 1121 | void helper_store_msr(CPUPPCState *env, target_ulong val) |
ad71ed68 | 1122 | { |
db789c6c | 1123 | uint32_t excp = hreg_store_msr(env, val, 0); |
259186a7 | 1124 | |
db789c6c | 1125 | if (excp != 0) { |
db70b311 | 1126 | CPUState *cs = env_cpu(env); |
044897ef | 1127 | cpu_interrupt_exittb(cs); |
db789c6c | 1128 | raise_exception(env, excp); |
ad71ed68 BS |
1129 | } |
1130 | } | |
1131 | ||
7778a575 BH |
1132 | #if defined(TARGET_PPC64) |
1133 | void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) | |
1134 | { | |
1135 | CPUState *cs; | |
1136 | ||
db70b311 | 1137 | cs = env_cpu(env); |
7778a575 | 1138 | cs->halted = 1; |
7778a575 | 1139 | |
47733729 DG |
1140 | /* |
1141 | * The architecture specifies that HDEC interrupts are discarded | |
1142 | * in PM states | |
4b236b62 BH |
1143 | */ |
1144 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); | |
1145 | ||
3621e2c9 | 1146 | /* Condition for waking up at 0x100 */ |
1e7fd61d | 1147 | env->resume_as_sreset = (insn != PPC_PM_STOP) || |
21c0d66a | 1148 | (env->spr[SPR_PSSCR] & PSSCR_EC); |
7778a575 BH |
1149 | } |
1150 | #endif /* defined(TARGET_PPC64) */ | |
1151 | ||
a2e71b28 | 1152 | static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) |
ad71ed68 | 1153 | { |
db70b311 | 1154 | CPUState *cs = env_cpu(env); |
259186a7 | 1155 | |
a2e71b28 BH |
1156 | /* MSR:POW cannot be set by any form of rfi */ |
1157 | msr &= ~(1ULL << MSR_POW); | |
1158 | ||
ad71ed68 | 1159 | #if defined(TARGET_PPC64) |
a2e71b28 BH |
1160 | /* Switching to 32-bit ? Crop the nip */ |
1161 | if (!msr_is_64bit(env, msr)) { | |
ad71ed68 | 1162 | nip = (uint32_t)nip; |
ad71ed68 BS |
1163 | } |
1164 | #else | |
1165 | nip = (uint32_t)nip; | |
ad71ed68 BS |
1166 | #endif |
1167 | /* XXX: beware: this is false if VLE is supported */ | |
1168 | env->nip = nip & ~((target_ulong)0x00000003); | |
1169 | hreg_store_msr(env, msr, 1); | |
1170 | #if defined(DEBUG_OP) | |
1171 | cpu_dump_rfi(env->nip, env->msr); | |
1172 | #endif | |
47733729 DG |
1173 | /* |
1174 | * No need to raise an exception here, as rfi is always the last | |
1175 | * insn of a TB | |
ad71ed68 | 1176 | */ |
044897ef | 1177 | cpu_interrupt_exittb(cs); |
a8b73734 ND |
1178 | /* Reset the reservation */ |
1179 | env->reserve_addr = -1; | |
1180 | ||
cd0c6f47 | 1181 | /* Context synchronizing: check if TCG TLB needs flush */ |
e3cffe6f | 1182 | check_tlb_flush(env, false); |
ad71ed68 BS |
1183 | } |
1184 | ||
e5f17ac6 | 1185 | void helper_rfi(CPUPPCState *env) |
ad71ed68 | 1186 | { |
a2e71b28 | 1187 | do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); |
ad71ed68 BS |
1188 | } |
1189 | ||
a2e71b28 | 1190 | #define MSR_BOOK3S_MASK |
ad71ed68 | 1191 | #if defined(TARGET_PPC64) |
e5f17ac6 | 1192 | void helper_rfid(CPUPPCState *env) |
ad71ed68 | 1193 | { |
47733729 | 1194 | /* |
136fbf65 | 1195 | * The architecture defines a number of rules for which bits can |
47733729 | 1196 | * change but in practice, we handle this in hreg_store_msr() |
a2e71b28 BH |
1197 | * which will be called by do_rfi(), so there is no need to filter |
1198 | * here | |
1199 | */ | |
1200 | do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); | |
ad71ed68 BS |
1201 | } |
1202 | ||
3c89b8d6 NP |
1203 | void helper_rfscv(CPUPPCState *env) |
1204 | { | |
1205 | do_rfi(env, env->lr, env->ctr); | |
1206 | } | |
1207 | ||
e5f17ac6 | 1208 | void helper_hrfid(CPUPPCState *env) |
ad71ed68 | 1209 | { |
a2e71b28 | 1210 | do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); |
ad71ed68 BS |
1211 | } |
1212 | #endif | |
1213 | ||
1214 | /*****************************************************************************/ | |
1215 | /* Embedded PowerPC specific helpers */ | |
e5f17ac6 | 1216 | void helper_40x_rfci(CPUPPCState *env) |
ad71ed68 | 1217 | { |
a2e71b28 | 1218 | do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); |
ad71ed68 BS |
1219 | } |
1220 | ||
e5f17ac6 | 1221 | void helper_rfci(CPUPPCState *env) |
ad71ed68 | 1222 | { |
a2e71b28 | 1223 | do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); |
ad71ed68 BS |
1224 | } |
1225 | ||
e5f17ac6 | 1226 | void helper_rfdi(CPUPPCState *env) |
ad71ed68 | 1227 | { |
a1bb7384 | 1228 | /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ |
a2e71b28 | 1229 | do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); |
ad71ed68 BS |
1230 | } |
1231 | ||
e5f17ac6 | 1232 | void helper_rfmci(CPUPPCState *env) |
ad71ed68 | 1233 | { |
a1bb7384 | 1234 | /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ |
a2e71b28 | 1235 | do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); |
ad71ed68 BS |
1236 | } |
1237 | #endif | |
1238 | ||
e5f17ac6 BS |
1239 | void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, |
1240 | uint32_t flags) | |
ad71ed68 BS |
1241 | { |
1242 | if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || | |
1243 | ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || | |
1244 | ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || | |
1245 | ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || | |
1246 | ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { | |
72073dcc BH |
1247 | raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, |
1248 | POWERPC_EXCP_TRAP, GETPC()); | |
ad71ed68 BS |
1249 | } |
1250 | } | |
1251 | ||
1252 | #if defined(TARGET_PPC64) | |
e5f17ac6 BS |
1253 | void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, |
1254 | uint32_t flags) | |
ad71ed68 BS |
1255 | { |
1256 | if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || | |
1257 | ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || | |
1258 | ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || | |
1259 | ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || | |
1260 | ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { | |
72073dcc BH |
1261 | raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, |
1262 | POWERPC_EXCP_TRAP, GETPC()); | |
ad71ed68 BS |
1263 | } |
1264 | } | |
1265 | #endif | |
1266 | ||
1267 | #if !defined(CONFIG_USER_ONLY) | |
1268 | /*****************************************************************************/ | |
1269 | /* PowerPC 601 specific instructions (POWER bridge) */ | |
1270 | ||
e5f17ac6 | 1271 | void helper_rfsvc(CPUPPCState *env) |
ad71ed68 | 1272 | { |
a2e71b28 | 1273 | do_rfi(env, env->lr, env->ctr & 0x0000FFFF); |
ad71ed68 BS |
1274 | } |
1275 | ||
1276 | /* Embedded.Processor Control */ | |
1277 | static int dbell2irq(target_ulong rb) | |
1278 | { | |
1279 | int msg = rb & DBELL_TYPE_MASK; | |
1280 | int irq = -1; | |
1281 | ||
1282 | switch (msg) { | |
1283 | case DBELL_TYPE_DBELL: | |
1284 | irq = PPC_INTERRUPT_DOORBELL; | |
1285 | break; | |
1286 | case DBELL_TYPE_DBELL_CRIT: | |
1287 | irq = PPC_INTERRUPT_CDOORBELL; | |
1288 | break; | |
1289 | case DBELL_TYPE_G_DBELL: | |
1290 | case DBELL_TYPE_G_DBELL_CRIT: | |
1291 | case DBELL_TYPE_G_DBELL_MC: | |
1292 | /* XXX implement */ | |
1293 | default: | |
1294 | break; | |
1295 | } | |
1296 | ||
1297 | return irq; | |
1298 | } | |
1299 | ||
e5f17ac6 | 1300 | void helper_msgclr(CPUPPCState *env, target_ulong rb) |
ad71ed68 BS |
1301 | { |
1302 | int irq = dbell2irq(rb); | |
1303 | ||
1304 | if (irq < 0) { | |
1305 | return; | |
1306 | } | |
1307 | ||
1308 | env->pending_interrupts &= ~(1 << irq); | |
1309 | } | |
1310 | ||
1311 | void helper_msgsnd(target_ulong rb) | |
1312 | { | |
1313 | int irq = dbell2irq(rb); | |
1314 | int pir = rb & DBELL_PIRTAG_MASK; | |
182735ef | 1315 | CPUState *cs; |
ad71ed68 BS |
1316 | |
1317 | if (irq < 0) { | |
1318 | return; | |
1319 | } | |
1320 | ||
f1c29ebc | 1321 | qemu_mutex_lock_iothread(); |
bdc44640 | 1322 | CPU_FOREACH(cs) { |
182735ef AF |
1323 | PowerPCCPU *cpu = POWERPC_CPU(cs); |
1324 | CPUPPCState *cenv = &cpu->env; | |
1325 | ||
ad71ed68 BS |
1326 | if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { |
1327 | cenv->pending_interrupts |= 1 << irq; | |
182735ef | 1328 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); |
ad71ed68 BS |
1329 | } |
1330 | } | |
f1c29ebc | 1331 | qemu_mutex_unlock_iothread(); |
ad71ed68 | 1332 | } |
7af1e7b0 CLG |
1333 | |
1334 | /* Server Processor Control */ | |
7af1e7b0 | 1335 | |
5ba7ba1d CLG |
1336 | static bool dbell_type_server(target_ulong rb) |
1337 | { | |
47733729 DG |
1338 | /* |
1339 | * A Directed Hypervisor Doorbell message is sent only if the | |
7af1e7b0 | 1340 | * message type is 5. All other types are reserved and the |
47733729 DG |
1341 | * instruction is a no-op |
1342 | */ | |
5ba7ba1d | 1343 | return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; |
7af1e7b0 CLG |
1344 | } |
1345 | ||
1346 | void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) | |
1347 | { | |
5ba7ba1d | 1348 | if (!dbell_type_server(rb)) { |
7af1e7b0 CLG |
1349 | return; |
1350 | } | |
1351 | ||
5ba7ba1d | 1352 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL); |
7af1e7b0 CLG |
1353 | } |
1354 | ||
5ba7ba1d | 1355 | static void book3s_msgsnd_common(int pir, int irq) |
7af1e7b0 | 1356 | { |
7af1e7b0 CLG |
1357 | CPUState *cs; |
1358 | ||
7af1e7b0 CLG |
1359 | qemu_mutex_lock_iothread(); |
1360 | CPU_FOREACH(cs) { | |
1361 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1362 | CPUPPCState *cenv = &cpu->env; | |
1363 | ||
1364 | /* TODO: broadcast message to all threads of the same processor */ | |
1365 | if (cenv->spr_cb[SPR_PIR].default_value == pir) { | |
1366 | cenv->pending_interrupts |= 1 << irq; | |
1367 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); | |
1368 | } | |
1369 | } | |
1370 | qemu_mutex_unlock_iothread(); | |
1371 | } | |
5ba7ba1d CLG |
1372 | |
1373 | void helper_book3s_msgsnd(target_ulong rb) | |
1374 | { | |
1375 | int pir = rb & DBELL_PROCIDTAG_MASK; | |
1376 | ||
1377 | if (!dbell_type_server(rb)) { | |
1378 | return; | |
1379 | } | |
1380 | ||
1381 | book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); | |
1382 | } | |
1383 | ||
1384 | #if defined(TARGET_PPC64) | |
1385 | void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) | |
1386 | { | |
493028d8 CLG |
1387 | helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); |
1388 | ||
5ba7ba1d CLG |
1389 | if (!dbell_type_server(rb)) { |
1390 | return; | |
1391 | } | |
1392 | ||
1393 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); | |
1394 | } | |
1395 | ||
1396 | /* | |
1397 | * sends a message to other threads that are on the same | |
1398 | * multi-threaded processor | |
1399 | */ | |
1400 | void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) | |
1401 | { | |
1402 | int pir = env->spr_cb[SPR_PIR].default_value; | |
1403 | ||
493028d8 CLG |
1404 | helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); |
1405 | ||
5ba7ba1d CLG |
1406 | if (!dbell_type_server(rb)) { |
1407 | return; | |
1408 | } | |
1409 | ||
1410 | /* TODO: TCG supports only one thread */ | |
1411 | ||
1412 | book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL); | |
1413 | } | |
1414 | #endif | |
ad71ed68 | 1415 | #endif |
0f3110fa RH |
1416 | |
1417 | void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, | |
1418 | MMUAccessType access_type, | |
1419 | int mmu_idx, uintptr_t retaddr) | |
1420 | { | |
1421 | CPUPPCState *env = cs->env_ptr; | |
1422 | uint32_t insn; | |
1423 | ||
1424 | /* Restore state and reload the insn we executed, for filling in DSISR. */ | |
1425 | cpu_restore_state(cs, retaddr, true); | |
1426 | insn = cpu_ldl_code(env, env->nip); | |
1427 | ||
1428 | cs->exception_index = POWERPC_EXCP_ALIGN; | |
1429 | env->error_code = insn & 0x03FF0000; | |
1430 | cpu_loop_exit(cs); | |
1431 | } |