]>
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 | |
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 | */ | |
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 DG |
233 | /* |
234 | * Exception targetting 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) | |
269 | && !((env->mmu_model & POWERPC_MMU_64) && (env->msr_mask & MSR_HVB)) | |
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; | |
bd6fefe7 | 530 | case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ |
c79c73f6 | 531 | case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ |
c79c73f6 | 532 | case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ |
c79c73f6 | 533 | case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ |
7af1e7b0 | 534 | case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ |
bd6fefe7 | 535 | case POWERPC_EXCP_HV_EMU: |
d8ce5fd6 | 536 | case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ |
c79c73f6 BS |
537 | srr0 = SPR_HSRR0; |
538 | srr1 = SPR_HSRR1; | |
539 | new_msr |= (target_ulong)MSR_HVB; | |
540 | new_msr |= env->msr & ((target_ulong)1 << MSR_RI); | |
bd6fefe7 | 541 | break; |
c79c73f6 | 542 | case POWERPC_EXCP_VPU: /* Vector unavailable exception */ |
1f29871c | 543 | case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ |
7019cb3d | 544 | case POWERPC_EXCP_FU: /* Facility unavailable exception */ |
5310799a BS |
545 | #ifdef TARGET_PPC64 |
546 | env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56); | |
493028d8 CLG |
547 | #endif |
548 | break; | |
549 | case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */ | |
550 | #ifdef TARGET_PPC64 | |
551 | env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS); | |
552 | srr0 = SPR_HSRR0; | |
553 | srr1 = SPR_HSRR1; | |
554 | new_msr |= (target_ulong)MSR_HVB; | |
555 | new_msr |= env->msr & ((target_ulong)1 << MSR_RI); | |
5310799a | 556 | #endif |
bd6fefe7 | 557 | break; |
c79c73f6 BS |
558 | case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ |
559 | LOG_EXCP("PIT exception\n"); | |
bd6fefe7 | 560 | break; |
c79c73f6 BS |
561 | case POWERPC_EXCP_IO: /* IO error exception */ |
562 | /* XXX: TODO */ | |
a47dddd7 | 563 | cpu_abort(cs, "601 IO error exception is not implemented yet !\n"); |
bd6fefe7 | 564 | break; |
c79c73f6 BS |
565 | case POWERPC_EXCP_RUNM: /* Run mode exception */ |
566 | /* XXX: TODO */ | |
a47dddd7 | 567 | cpu_abort(cs, "601 run mode exception is not implemented yet !\n"); |
bd6fefe7 | 568 | break; |
c79c73f6 BS |
569 | case POWERPC_EXCP_EMUL: /* Emulation trap exception */ |
570 | /* XXX: TODO */ | |
a47dddd7 | 571 | cpu_abort(cs, "602 emulation trap exception " |
c79c73f6 | 572 | "is not implemented yet !\n"); |
bd6fefe7 | 573 | break; |
c79c73f6 | 574 | case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ |
c79c73f6 BS |
575 | switch (excp_model) { |
576 | case POWERPC_EXCP_602: | |
577 | case POWERPC_EXCP_603: | |
578 | case POWERPC_EXCP_603E: | |
579 | case POWERPC_EXCP_G2: | |
580 | goto tlb_miss_tgpr; | |
581 | case POWERPC_EXCP_7x5: | |
582 | goto tlb_miss; | |
583 | case POWERPC_EXCP_74xx: | |
584 | goto tlb_miss_74xx; | |
585 | default: | |
a47dddd7 | 586 | cpu_abort(cs, "Invalid instruction TLB miss exception\n"); |
c79c73f6 BS |
587 | break; |
588 | } | |
589 | break; | |
590 | case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ | |
c79c73f6 BS |
591 | switch (excp_model) { |
592 | case POWERPC_EXCP_602: | |
593 | case POWERPC_EXCP_603: | |
594 | case POWERPC_EXCP_603E: | |
595 | case POWERPC_EXCP_G2: | |
596 | goto tlb_miss_tgpr; | |
597 | case POWERPC_EXCP_7x5: | |
598 | goto tlb_miss; | |
599 | case POWERPC_EXCP_74xx: | |
600 | goto tlb_miss_74xx; | |
601 | default: | |
a47dddd7 | 602 | cpu_abort(cs, "Invalid data load TLB miss exception\n"); |
c79c73f6 BS |
603 | break; |
604 | } | |
605 | break; | |
606 | case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ | |
c79c73f6 BS |
607 | switch (excp_model) { |
608 | case POWERPC_EXCP_602: | |
609 | case POWERPC_EXCP_603: | |
610 | case POWERPC_EXCP_603E: | |
611 | case POWERPC_EXCP_G2: | |
612 | tlb_miss_tgpr: | |
613 | /* Swap temporary saved registers with GPRs */ | |
614 | if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { | |
615 | new_msr |= (target_ulong)1 << MSR_TGPR; | |
616 | hreg_swap_gpr_tgpr(env); | |
617 | } | |
618 | goto tlb_miss; | |
619 | case POWERPC_EXCP_7x5: | |
620 | tlb_miss: | |
621 | #if defined(DEBUG_SOFTWARE_TLB) | |
622 | if (qemu_log_enabled()) { | |
623 | const char *es; | |
624 | target_ulong *miss, *cmp; | |
625 | int en; | |
626 | ||
627 | if (excp == POWERPC_EXCP_IFTLB) { | |
628 | es = "I"; | |
629 | en = 'I'; | |
630 | miss = &env->spr[SPR_IMISS]; | |
631 | cmp = &env->spr[SPR_ICMP]; | |
632 | } else { | |
633 | if (excp == POWERPC_EXCP_DLTLB) { | |
634 | es = "DL"; | |
635 | } else { | |
636 | es = "DS"; | |
637 | } | |
638 | en = 'D'; | |
639 | miss = &env->spr[SPR_DMISS]; | |
640 | cmp = &env->spr[SPR_DCMP]; | |
641 | } | |
642 | qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " | |
643 | TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 " | |
644 | TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, | |
645 | env->spr[SPR_HASH1], env->spr[SPR_HASH2], | |
646 | env->error_code); | |
647 | } | |
648 | #endif | |
649 | msr |= env->crf[0] << 28; | |
650 | msr |= env->error_code; /* key, D/I, S/L bits */ | |
651 | /* Set way using a LRU mechanism */ | |
652 | msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; | |
653 | break; | |
654 | case POWERPC_EXCP_74xx: | |
655 | tlb_miss_74xx: | |
656 | #if defined(DEBUG_SOFTWARE_TLB) | |
657 | if (qemu_log_enabled()) { | |
658 | const char *es; | |
659 | target_ulong *miss, *cmp; | |
660 | int en; | |
661 | ||
662 | if (excp == POWERPC_EXCP_IFTLB) { | |
663 | es = "I"; | |
664 | en = 'I'; | |
665 | miss = &env->spr[SPR_TLBMISS]; | |
666 | cmp = &env->spr[SPR_PTEHI]; | |
667 | } else { | |
668 | if (excp == POWERPC_EXCP_DLTLB) { | |
669 | es = "DL"; | |
670 | } else { | |
671 | es = "DS"; | |
672 | } | |
673 | en = 'D'; | |
674 | miss = &env->spr[SPR_TLBMISS]; | |
675 | cmp = &env->spr[SPR_PTEHI]; | |
676 | } | |
677 | qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " | |
678 | TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp, | |
679 | env->error_code); | |
680 | } | |
681 | #endif | |
682 | msr |= env->error_code; /* key bit */ | |
683 | break; | |
684 | default: | |
a47dddd7 | 685 | cpu_abort(cs, "Invalid data store TLB miss exception\n"); |
c79c73f6 BS |
686 | break; |
687 | } | |
bd6fefe7 | 688 | break; |
c79c73f6 BS |
689 | case POWERPC_EXCP_FPA: /* Floating-point assist exception */ |
690 | /* XXX: TODO */ | |
a47dddd7 | 691 | cpu_abort(cs, "Floating point assist exception " |
c79c73f6 | 692 | "is not implemented yet !\n"); |
bd6fefe7 | 693 | break; |
c79c73f6 BS |
694 | case POWERPC_EXCP_DABR: /* Data address breakpoint */ |
695 | /* XXX: TODO */ | |
a47dddd7 | 696 | cpu_abort(cs, "DABR exception is not implemented yet !\n"); |
bd6fefe7 | 697 | break; |
c79c73f6 BS |
698 | case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ |
699 | /* XXX: TODO */ | |
a47dddd7 | 700 | cpu_abort(cs, "IABR exception is not implemented yet !\n"); |
bd6fefe7 | 701 | break; |
c79c73f6 BS |
702 | case POWERPC_EXCP_SMI: /* System management interrupt */ |
703 | /* XXX: TODO */ | |
a47dddd7 | 704 | cpu_abort(cs, "SMI exception is not implemented yet !\n"); |
bd6fefe7 | 705 | break; |
c79c73f6 BS |
706 | case POWERPC_EXCP_THERM: /* Thermal interrupt */ |
707 | /* XXX: TODO */ | |
a47dddd7 | 708 | cpu_abort(cs, "Thermal management exception " |
c79c73f6 | 709 | "is not implemented yet !\n"); |
bd6fefe7 | 710 | break; |
c79c73f6 | 711 | case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ |
c79c73f6 | 712 | /* XXX: TODO */ |
a47dddd7 | 713 | cpu_abort(cs, |
c79c73f6 | 714 | "Performance counter exception is not implemented yet !\n"); |
bd6fefe7 | 715 | break; |
c79c73f6 BS |
716 | case POWERPC_EXCP_VPUA: /* Vector assist exception */ |
717 | /* XXX: TODO */ | |
a47dddd7 | 718 | cpu_abort(cs, "VPU assist exception is not implemented yet !\n"); |
bd6fefe7 | 719 | break; |
c79c73f6 BS |
720 | case POWERPC_EXCP_SOFTP: /* Soft patch exception */ |
721 | /* XXX: TODO */ | |
a47dddd7 | 722 | cpu_abort(cs, |
c79c73f6 | 723 | "970 soft-patch exception is not implemented yet !\n"); |
bd6fefe7 | 724 | break; |
c79c73f6 BS |
725 | case POWERPC_EXCP_MAINT: /* Maintenance exception */ |
726 | /* XXX: TODO */ | |
a47dddd7 | 727 | cpu_abort(cs, |
c79c73f6 | 728 | "970 maintenance exception is not implemented yet !\n"); |
bd6fefe7 | 729 | break; |
c79c73f6 BS |
730 | case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ |
731 | /* XXX: TODO */ | |
a47dddd7 | 732 | cpu_abort(cs, "Maskable external exception " |
c79c73f6 | 733 | "is not implemented yet !\n"); |
bd6fefe7 | 734 | break; |
c79c73f6 BS |
735 | case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ |
736 | /* XXX: TODO */ | |
a47dddd7 | 737 | cpu_abort(cs, "Non maskable external exception " |
c79c73f6 | 738 | "is not implemented yet !\n"); |
bd6fefe7 | 739 | break; |
c79c73f6 BS |
740 | default: |
741 | excp_invalid: | |
a47dddd7 | 742 | cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); |
c79c73f6 | 743 | break; |
c79c73f6 | 744 | } |
bd6fefe7 | 745 | |
6d49d6d4 | 746 | /* Sanity check */ |
10c21b5c NP |
747 | if (!(env->msr_mask & MSR_HVB)) { |
748 | if (new_msr & MSR_HVB) { | |
749 | cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " | |
750 | "no HV support\n", excp); | |
751 | } | |
752 | if (srr0 == SPR_HSRR0) { | |
753 | cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " | |
754 | "no HV support\n", excp); | |
755 | } | |
6d49d6d4 BH |
756 | } |
757 | ||
47733729 DG |
758 | /* |
759 | * Sort out endianness of interrupt, this differs depending on the | |
6d49d6d4 BH |
760 | * CPU, the HV mode, etc... |
761 | */ | |
1e0c7e55 | 762 | #ifdef TARGET_PPC64 |
6d49d6d4 BH |
763 | if (excp_model == POWERPC_EXCP_POWER7) { |
764 | if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) { | |
765 | new_msr |= (target_ulong)1 << MSR_LE; | |
766 | } | |
767 | } else if (excp_model == POWERPC_EXCP_POWER8) { | |
768 | if (new_msr & MSR_HVB) { | |
a790e82b BH |
769 | if (env->spr[SPR_HID0] & HID0_HILE) { |
770 | new_msr |= (target_ulong)1 << MSR_LE; | |
771 | } | |
772 | } else if (env->spr[SPR_LPCR] & LPCR_ILE) { | |
773 | new_msr |= (target_ulong)1 << MSR_LE; | |
774 | } | |
775 | } else if (excp_model == POWERPC_EXCP_POWER9) { | |
776 | if (new_msr & MSR_HVB) { | |
777 | if (env->spr[SPR_HID0] & HID0_POWER9_HILE) { | |
6d49d6d4 BH |
778 | new_msr |= (target_ulong)1 << MSR_LE; |
779 | } | |
780 | } else if (env->spr[SPR_LPCR] & LPCR_ILE) { | |
1e0c7e55 AB |
781 | new_msr |= (target_ulong)1 << MSR_LE; |
782 | } | |
783 | } else if (msr_ile) { | |
784 | new_msr |= (target_ulong)1 << MSR_LE; | |
785 | } | |
786 | #else | |
c79c73f6 BS |
787 | if (msr_ile) { |
788 | new_msr |= (target_ulong)1 << MSR_LE; | |
789 | } | |
1e0c7e55 | 790 | #endif |
c79c73f6 | 791 | |
47733729 DG |
792 | /* |
793 | * AIL only works if there is no HV transition and we are running | |
794 | * with translations enabled | |
5c94b2a5 | 795 | */ |
6d49d6d4 BH |
796 | if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) || |
797 | ((new_msr & MSR_HVB) && !(msr & MSR_HVB))) { | |
5c94b2a5 CLG |
798 | ail = 0; |
799 | } | |
3c89b8d6 NP |
800 | |
801 | vector = env->excp_vectors[excp]; | |
802 | if (vector == (target_ulong)-1ULL) { | |
803 | cpu_abort(cs, "Raised an exception without defined vector %d\n", | |
804 | excp); | |
805 | } | |
806 | ||
807 | vector |= env->excp_prefix; | |
808 | ||
809 | /* If any alternate SRR register are defined, duplicate saved values */ | |
810 | if (asrr0 != -1) { | |
811 | env->spr[asrr0] = env->nip; | |
812 | } | |
813 | if (asrr1 != -1) { | |
814 | env->spr[asrr1] = msr; | |
5c94b2a5 CLG |
815 | } |
816 | ||
c79c73f6 BS |
817 | #if defined(TARGET_PPC64) |
818 | if (excp_model == POWERPC_EXCP_BOOKE) { | |
e42a61f1 AG |
819 | if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) { |
820 | /* Cat.64-bit: EPCR.ICM is copied to MSR.CM */ | |
c79c73f6 | 821 | new_msr |= (target_ulong)1 << MSR_CM; |
e42a61f1 AG |
822 | } else { |
823 | vector = (uint32_t)vector; | |
c79c73f6 BS |
824 | } |
825 | } else { | |
826 | if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) { | |
827 | vector = (uint32_t)vector; | |
828 | } else { | |
829 | new_msr |= (target_ulong)1 << MSR_SF; | |
830 | } | |
831 | } | |
832 | #endif | |
cd0c6f47 | 833 | |
3c89b8d6 NP |
834 | if (excp != POWERPC_EXCP_SYSCALL_VECTORED) { |
835 | /* Save PC */ | |
836 | env->spr[srr0] = env->nip; | |
837 | ||
838 | /* Save MSR */ | |
839 | env->spr[srr1] = msr; | |
840 | ||
841 | /* Handle AIL */ | |
842 | if (ail) { | |
843 | new_msr |= (1 << MSR_IR) | (1 << MSR_DR); | |
844 | vector |= ppc_excp_vector_offset(cs, ail); | |
845 | } | |
846 | ||
847 | #if defined(TARGET_PPC64) | |
848 | } else { | |
849 | /* scv AIL is a little different */ | |
850 | if (ail) { | |
851 | new_msr |= (1 << MSR_IR) | (1 << MSR_DR); | |
852 | } | |
853 | if (ail == AIL_C000_0000_0000_4000) { | |
854 | vector |= 0xc000000000003000ull; | |
855 | } else { | |
856 | vector |= 0x0000000000017000ull; | |
857 | } | |
858 | vector += lev * 0x20; | |
859 | ||
860 | env->lr = env->nip; | |
861 | env->ctr = msr; | |
862 | #endif | |
863 | } | |
864 | ||
ad77c6ca | 865 | powerpc_set_excp_state(cpu, vector, new_msr); |
c79c73f6 BS |
866 | } |
867 | ||
97a8ea5a | 868 | void ppc_cpu_do_interrupt(CPUState *cs) |
c79c73f6 | 869 | { |
97a8ea5a AF |
870 | PowerPCCPU *cpu = POWERPC_CPU(cs); |
871 | CPUPPCState *env = &cpu->env; | |
5c26a5b3 | 872 | |
27103424 | 873 | powerpc_excp(cpu, env->excp_model, cs->exception_index); |
c79c73f6 BS |
874 | } |
875 | ||
458dd766 | 876 | static void ppc_hw_interrupt(CPUPPCState *env) |
c79c73f6 | 877 | { |
db70b311 | 878 | PowerPCCPU *cpu = env_archcpu(env); |
3621e2c9 | 879 | bool async_deliver; |
259186a7 | 880 | |
c79c73f6 BS |
881 | /* External reset */ |
882 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { | |
883 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); | |
5c26a5b3 | 884 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); |
c79c73f6 BS |
885 | return; |
886 | } | |
887 | /* Machine check exception */ | |
888 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { | |
889 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); | |
5c26a5b3 | 890 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_MCHECK); |
c79c73f6 BS |
891 | return; |
892 | } | |
893 | #if 0 /* TODO */ | |
894 | /* External debug exception */ | |
895 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { | |
896 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); | |
5c26a5b3 | 897 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DEBUG); |
c79c73f6 BS |
898 | return; |
899 | } | |
900 | #endif | |
3621e2c9 BH |
901 | |
902 | /* | |
903 | * For interrupts that gate on MSR:EE, we need to do something a | |
904 | * bit more subtle, as we need to let them through even when EE is | |
905 | * clear when coming out of some power management states (in order | |
906 | * for them to become a 0x100). | |
907 | */ | |
1e7fd61d | 908 | async_deliver = (msr_ee != 0) || env->resume_as_sreset; |
3621e2c9 | 909 | |
4b236b62 BH |
910 | /* Hypervisor decrementer exception */ |
911 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { | |
912 | /* LPCR will be clear when not supported so this will work */ | |
913 | bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); | |
3621e2c9 | 914 | if ((async_deliver || msr_hv == 0) && hdice) { |
4b236b62 BH |
915 | /* HDEC clears on delivery */ |
916 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); | |
5c26a5b3 | 917 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR); |
c79c73f6 BS |
918 | return; |
919 | } | |
920 | } | |
d8ce5fd6 BH |
921 | |
922 | /* Hypervisor virtualization interrupt */ | |
923 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_HVIRT)) { | |
924 | /* LPCR will be clear when not supported so this will work */ | |
925 | bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE); | |
926 | if ((async_deliver || msr_hv == 0) && hvice) { | |
927 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HVIRT); | |
928 | return; | |
929 | } | |
930 | } | |
931 | ||
932 | /* External interrupt can ignore MSR:EE under some circumstances */ | |
d1dbe37c BH |
933 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { |
934 | bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); | |
6eebe6dc BH |
935 | bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); |
936 | /* HEIC blocks delivery to the hypervisor */ | |
937 | if ((async_deliver && !(heic && msr_hv && !msr_pr)) || | |
938 | (env->has_hv_mode && msr_hv == 0 && !lpes0)) { | |
d1dbe37c BH |
939 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL); |
940 | return; | |
941 | } | |
942 | } | |
c79c73f6 BS |
943 | if (msr_ce != 0) { |
944 | /* External critical interrupt */ | |
945 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { | |
5c26a5b3 | 946 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_CRITICAL); |
c79c73f6 BS |
947 | return; |
948 | } | |
949 | } | |
3621e2c9 | 950 | if (async_deliver != 0) { |
c79c73f6 BS |
951 | /* Watchdog timer on embedded PowerPC */ |
952 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { | |
953 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); | |
5c26a5b3 | 954 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_WDT); |
c79c73f6 BS |
955 | return; |
956 | } | |
957 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { | |
958 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); | |
5c26a5b3 | 959 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORCI); |
c79c73f6 BS |
960 | return; |
961 | } | |
962 | /* Fixed interval timer on embedded PowerPC */ | |
963 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { | |
964 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); | |
5c26a5b3 | 965 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_FIT); |
c79c73f6 BS |
966 | return; |
967 | } | |
968 | /* Programmable interval timer on embedded PowerPC */ | |
969 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { | |
970 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); | |
5c26a5b3 | 971 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PIT); |
c79c73f6 BS |
972 | return; |
973 | } | |
974 | /* Decrementer exception */ | |
975 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { | |
e81a982a AG |
976 | if (ppc_decr_clear_on_delivery(env)) { |
977 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); | |
978 | } | |
5c26a5b3 | 979 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DECR); |
c79c73f6 BS |
980 | return; |
981 | } | |
c79c73f6 BS |
982 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { |
983 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); | |
5ba7ba1d CLG |
984 | if (is_book3s_arch2x(env)) { |
985 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR); | |
986 | } else { | |
987 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI); | |
988 | } | |
c79c73f6 BS |
989 | return; |
990 | } | |
7af1e7b0 CLG |
991 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDOORBELL)) { |
992 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL); | |
993 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_SDOOR_HV); | |
994 | return; | |
995 | } | |
c79c73f6 BS |
996 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { |
997 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); | |
5c26a5b3 | 998 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PERFM); |
c79c73f6 BS |
999 | return; |
1000 | } | |
1001 | /* Thermal interrupt */ | |
1002 | if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { | |
1003 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); | |
5c26a5b3 | 1004 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_THERM); |
c79c73f6 BS |
1005 | return; |
1006 | } | |
1007 | } | |
f8154fd2 BH |
1008 | |
1009 | if (env->resume_as_sreset) { | |
1010 | /* | |
1011 | * This is a bug ! It means that has_work took us out of halt without | |
1012 | * anything to deliver while in a PM state that requires getting | |
1013 | * out via a 0x100 | |
1014 | * | |
1015 | * This means we will incorrectly execute past the power management | |
1016 | * instruction instead of triggering a reset. | |
1017 | * | |
1018 | * It generally means a discrepancy between the wakup conditions in the | |
1019 | * processor has_work implementation and the logic in this function. | |
1020 | */ | |
db70b311 | 1021 | cpu_abort(env_cpu(env), |
f8154fd2 BH |
1022 | "Wakeup from PM state but interrupt Undelivered"); |
1023 | } | |
c79c73f6 | 1024 | } |
34316482 | 1025 | |
b5b7f391 | 1026 | void ppc_cpu_do_system_reset(CPUState *cs) |
34316482 AK |
1027 | { |
1028 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1029 | CPUPPCState *env = &cpu->env; | |
1030 | ||
1031 | powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET); | |
1032 | } | |
ad77c6ca NP |
1033 | |
1034 | void ppc_cpu_do_fwnmi_machine_check(CPUState *cs, target_ulong vector) | |
1035 | { | |
1036 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1037 | CPUPPCState *env = &cpu->env; | |
1038 | PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); | |
1039 | target_ulong msr = 0; | |
1040 | ||
1041 | /* | |
1042 | * Set MSR and NIP for the handler, SRR0/1, DAR and DSISR have already | |
1043 | * been set by KVM. | |
1044 | */ | |
1045 | msr = (1ULL << MSR_ME); | |
1046 | msr |= env->msr & (1ULL << MSR_SF); | |
1047 | if (!(*pcc->interrupts_big_endian)(cpu)) { | |
1048 | msr |= (1ULL << MSR_LE); | |
1049 | } | |
1050 | ||
1051 | powerpc_set_excp_state(cpu, vector, msr); | |
1052 | } | |
c79c73f6 BS |
1053 | #endif /* !CONFIG_USER_ONLY */ |
1054 | ||
458dd766 RH |
1055 | bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) |
1056 | { | |
1057 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1058 | CPUPPCState *env = &cpu->env; | |
1059 | ||
1060 | if (interrupt_request & CPU_INTERRUPT_HARD) { | |
1061 | ppc_hw_interrupt(env); | |
1062 | if (env->pending_interrupts == 0) { | |
1063 | cs->interrupt_request &= ~CPU_INTERRUPT_HARD; | |
1064 | } | |
1065 | return true; | |
1066 | } | |
1067 | return false; | |
1068 | } | |
1069 | ||
c79c73f6 BS |
1070 | #if defined(DEBUG_OP) |
1071 | static void cpu_dump_rfi(target_ulong RA, target_ulong msr) | |
1072 | { | |
1073 | qemu_log("Return from exception at " TARGET_FMT_lx " with flags " | |
1074 | TARGET_FMT_lx "\n", RA, msr); | |
1075 | } | |
1076 | #endif | |
1077 | ||
ad71ed68 BS |
1078 | /*****************************************************************************/ |
1079 | /* Exceptions processing helpers */ | |
1080 | ||
db789c6c BH |
1081 | void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, |
1082 | uint32_t error_code, uintptr_t raddr) | |
ad71ed68 | 1083 | { |
db70b311 | 1084 | CPUState *cs = env_cpu(env); |
27103424 | 1085 | |
27103424 | 1086 | cs->exception_index = exception; |
ad71ed68 | 1087 | env->error_code = error_code; |
db789c6c BH |
1088 | cpu_loop_exit_restore(cs, raddr); |
1089 | } | |
1090 | ||
1091 | void raise_exception_err(CPUPPCState *env, uint32_t exception, | |
1092 | uint32_t error_code) | |
1093 | { | |
1094 | raise_exception_err_ra(env, exception, error_code, 0); | |
1095 | } | |
1096 | ||
1097 | void raise_exception(CPUPPCState *env, uint32_t exception) | |
1098 | { | |
1099 | raise_exception_err_ra(env, exception, 0, 0); | |
1100 | } | |
1101 | ||
1102 | void raise_exception_ra(CPUPPCState *env, uint32_t exception, | |
1103 | uintptr_t raddr) | |
1104 | { | |
1105 | raise_exception_err_ra(env, exception, 0, raddr); | |
1106 | } | |
1107 | ||
1108 | void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, | |
1109 | uint32_t error_code) | |
1110 | { | |
1111 | raise_exception_err_ra(env, exception, error_code, 0); | |
ad71ed68 BS |
1112 | } |
1113 | ||
e5f17ac6 | 1114 | void helper_raise_exception(CPUPPCState *env, uint32_t exception) |
ad71ed68 | 1115 | { |
db789c6c | 1116 | raise_exception_err_ra(env, exception, 0, 0); |
ad71ed68 BS |
1117 | } |
1118 | ||
1119 | #if !defined(CONFIG_USER_ONLY) | |
e5f17ac6 | 1120 | void helper_store_msr(CPUPPCState *env, target_ulong val) |
ad71ed68 | 1121 | { |
db789c6c | 1122 | uint32_t excp = hreg_store_msr(env, val, 0); |
259186a7 | 1123 | |
db789c6c | 1124 | if (excp != 0) { |
db70b311 | 1125 | CPUState *cs = env_cpu(env); |
044897ef | 1126 | cpu_interrupt_exittb(cs); |
db789c6c | 1127 | raise_exception(env, excp); |
ad71ed68 BS |
1128 | } |
1129 | } | |
1130 | ||
7778a575 BH |
1131 | #if defined(TARGET_PPC64) |
1132 | void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) | |
1133 | { | |
1134 | CPUState *cs; | |
1135 | ||
db70b311 | 1136 | cs = env_cpu(env); |
7778a575 | 1137 | cs->halted = 1; |
7778a575 | 1138 | |
47733729 DG |
1139 | /* |
1140 | * The architecture specifies that HDEC interrupts are discarded | |
1141 | * in PM states | |
4b236b62 BH |
1142 | */ |
1143 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); | |
1144 | ||
3621e2c9 | 1145 | /* Condition for waking up at 0x100 */ |
1e7fd61d | 1146 | env->resume_as_sreset = (insn != PPC_PM_STOP) || |
21c0d66a | 1147 | (env->spr[SPR_PSSCR] & PSSCR_EC); |
7778a575 BH |
1148 | } |
1149 | #endif /* defined(TARGET_PPC64) */ | |
1150 | ||
a2e71b28 | 1151 | static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr) |
ad71ed68 | 1152 | { |
db70b311 | 1153 | CPUState *cs = env_cpu(env); |
259186a7 | 1154 | |
a2e71b28 BH |
1155 | /* MSR:POW cannot be set by any form of rfi */ |
1156 | msr &= ~(1ULL << MSR_POW); | |
1157 | ||
ad71ed68 | 1158 | #if defined(TARGET_PPC64) |
a2e71b28 BH |
1159 | /* Switching to 32-bit ? Crop the nip */ |
1160 | if (!msr_is_64bit(env, msr)) { | |
ad71ed68 | 1161 | nip = (uint32_t)nip; |
ad71ed68 BS |
1162 | } |
1163 | #else | |
1164 | nip = (uint32_t)nip; | |
ad71ed68 BS |
1165 | #endif |
1166 | /* XXX: beware: this is false if VLE is supported */ | |
1167 | env->nip = nip & ~((target_ulong)0x00000003); | |
1168 | hreg_store_msr(env, msr, 1); | |
1169 | #if defined(DEBUG_OP) | |
1170 | cpu_dump_rfi(env->nip, env->msr); | |
1171 | #endif | |
47733729 DG |
1172 | /* |
1173 | * No need to raise an exception here, as rfi is always the last | |
1174 | * insn of a TB | |
ad71ed68 | 1175 | */ |
044897ef | 1176 | cpu_interrupt_exittb(cs); |
a8b73734 ND |
1177 | /* Reset the reservation */ |
1178 | env->reserve_addr = -1; | |
1179 | ||
cd0c6f47 | 1180 | /* Context synchronizing: check if TCG TLB needs flush */ |
e3cffe6f | 1181 | check_tlb_flush(env, false); |
ad71ed68 BS |
1182 | } |
1183 | ||
e5f17ac6 | 1184 | void helper_rfi(CPUPPCState *env) |
ad71ed68 | 1185 | { |
a2e71b28 | 1186 | do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful); |
ad71ed68 BS |
1187 | } |
1188 | ||
a2e71b28 | 1189 | #define MSR_BOOK3S_MASK |
ad71ed68 | 1190 | #if defined(TARGET_PPC64) |
e5f17ac6 | 1191 | void helper_rfid(CPUPPCState *env) |
ad71ed68 | 1192 | { |
47733729 DG |
1193 | /* |
1194 | * The architeture defines a number of rules for which bits can | |
1195 | * change but in practice, we handle this in hreg_store_msr() | |
a2e71b28 BH |
1196 | * which will be called by do_rfi(), so there is no need to filter |
1197 | * here | |
1198 | */ | |
1199 | do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]); | |
ad71ed68 BS |
1200 | } |
1201 | ||
3c89b8d6 NP |
1202 | void helper_rfscv(CPUPPCState *env) |
1203 | { | |
1204 | do_rfi(env, env->lr, env->ctr); | |
1205 | } | |
1206 | ||
e5f17ac6 | 1207 | void helper_hrfid(CPUPPCState *env) |
ad71ed68 | 1208 | { |
a2e71b28 | 1209 | do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); |
ad71ed68 BS |
1210 | } |
1211 | #endif | |
1212 | ||
1213 | /*****************************************************************************/ | |
1214 | /* Embedded PowerPC specific helpers */ | |
e5f17ac6 | 1215 | void helper_40x_rfci(CPUPPCState *env) |
ad71ed68 | 1216 | { |
a2e71b28 | 1217 | do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]); |
ad71ed68 BS |
1218 | } |
1219 | ||
e5f17ac6 | 1220 | void helper_rfci(CPUPPCState *env) |
ad71ed68 | 1221 | { |
a2e71b28 | 1222 | do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]); |
ad71ed68 BS |
1223 | } |
1224 | ||
e5f17ac6 | 1225 | void helper_rfdi(CPUPPCState *env) |
ad71ed68 | 1226 | { |
a1bb7384 | 1227 | /* FIXME: choose CSRR1 or DSRR1 based on cpu type */ |
a2e71b28 | 1228 | do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]); |
ad71ed68 BS |
1229 | } |
1230 | ||
e5f17ac6 | 1231 | void helper_rfmci(CPUPPCState *env) |
ad71ed68 | 1232 | { |
a1bb7384 | 1233 | /* FIXME: choose CSRR1 or MCSRR1 based on cpu type */ |
a2e71b28 | 1234 | do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]); |
ad71ed68 BS |
1235 | } |
1236 | #endif | |
1237 | ||
e5f17ac6 BS |
1238 | void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, |
1239 | uint32_t flags) | |
ad71ed68 BS |
1240 | { |
1241 | if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || | |
1242 | ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || | |
1243 | ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || | |
1244 | ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || | |
1245 | ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { | |
72073dcc BH |
1246 | raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, |
1247 | POWERPC_EXCP_TRAP, GETPC()); | |
ad71ed68 BS |
1248 | } |
1249 | } | |
1250 | ||
1251 | #if defined(TARGET_PPC64) | |
e5f17ac6 BS |
1252 | void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, |
1253 | uint32_t flags) | |
ad71ed68 BS |
1254 | { |
1255 | if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || | |
1256 | ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || | |
1257 | ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || | |
1258 | ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || | |
1259 | ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { | |
72073dcc BH |
1260 | raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, |
1261 | POWERPC_EXCP_TRAP, GETPC()); | |
ad71ed68 BS |
1262 | } |
1263 | } | |
1264 | #endif | |
1265 | ||
1266 | #if !defined(CONFIG_USER_ONLY) | |
1267 | /*****************************************************************************/ | |
1268 | /* PowerPC 601 specific instructions (POWER bridge) */ | |
1269 | ||
e5f17ac6 | 1270 | void helper_rfsvc(CPUPPCState *env) |
ad71ed68 | 1271 | { |
a2e71b28 | 1272 | do_rfi(env, env->lr, env->ctr & 0x0000FFFF); |
ad71ed68 BS |
1273 | } |
1274 | ||
1275 | /* Embedded.Processor Control */ | |
1276 | static int dbell2irq(target_ulong rb) | |
1277 | { | |
1278 | int msg = rb & DBELL_TYPE_MASK; | |
1279 | int irq = -1; | |
1280 | ||
1281 | switch (msg) { | |
1282 | case DBELL_TYPE_DBELL: | |
1283 | irq = PPC_INTERRUPT_DOORBELL; | |
1284 | break; | |
1285 | case DBELL_TYPE_DBELL_CRIT: | |
1286 | irq = PPC_INTERRUPT_CDOORBELL; | |
1287 | break; | |
1288 | case DBELL_TYPE_G_DBELL: | |
1289 | case DBELL_TYPE_G_DBELL_CRIT: | |
1290 | case DBELL_TYPE_G_DBELL_MC: | |
1291 | /* XXX implement */ | |
1292 | default: | |
1293 | break; | |
1294 | } | |
1295 | ||
1296 | return irq; | |
1297 | } | |
1298 | ||
e5f17ac6 | 1299 | void helper_msgclr(CPUPPCState *env, target_ulong rb) |
ad71ed68 BS |
1300 | { |
1301 | int irq = dbell2irq(rb); | |
1302 | ||
1303 | if (irq < 0) { | |
1304 | return; | |
1305 | } | |
1306 | ||
1307 | env->pending_interrupts &= ~(1 << irq); | |
1308 | } | |
1309 | ||
1310 | void helper_msgsnd(target_ulong rb) | |
1311 | { | |
1312 | int irq = dbell2irq(rb); | |
1313 | int pir = rb & DBELL_PIRTAG_MASK; | |
182735ef | 1314 | CPUState *cs; |
ad71ed68 BS |
1315 | |
1316 | if (irq < 0) { | |
1317 | return; | |
1318 | } | |
1319 | ||
f1c29ebc | 1320 | qemu_mutex_lock_iothread(); |
bdc44640 | 1321 | CPU_FOREACH(cs) { |
182735ef AF |
1322 | PowerPCCPU *cpu = POWERPC_CPU(cs); |
1323 | CPUPPCState *cenv = &cpu->env; | |
1324 | ||
ad71ed68 BS |
1325 | if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { |
1326 | cenv->pending_interrupts |= 1 << irq; | |
182735ef | 1327 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); |
ad71ed68 BS |
1328 | } |
1329 | } | |
f1c29ebc | 1330 | qemu_mutex_unlock_iothread(); |
ad71ed68 | 1331 | } |
7af1e7b0 CLG |
1332 | |
1333 | /* Server Processor Control */ | |
7af1e7b0 | 1334 | |
5ba7ba1d CLG |
1335 | static bool dbell_type_server(target_ulong rb) |
1336 | { | |
47733729 DG |
1337 | /* |
1338 | * A Directed Hypervisor Doorbell message is sent only if the | |
7af1e7b0 | 1339 | * message type is 5. All other types are reserved and the |
47733729 DG |
1340 | * instruction is a no-op |
1341 | */ | |
5ba7ba1d | 1342 | return (rb & DBELL_TYPE_MASK) == DBELL_TYPE_DBELL_SERVER; |
7af1e7b0 CLG |
1343 | } |
1344 | ||
1345 | void helper_book3s_msgclr(CPUPPCState *env, target_ulong rb) | |
1346 | { | |
5ba7ba1d | 1347 | if (!dbell_type_server(rb)) { |
7af1e7b0 CLG |
1348 | return; |
1349 | } | |
1350 | ||
5ba7ba1d | 1351 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDOORBELL); |
7af1e7b0 CLG |
1352 | } |
1353 | ||
5ba7ba1d | 1354 | static void book3s_msgsnd_common(int pir, int irq) |
7af1e7b0 | 1355 | { |
7af1e7b0 CLG |
1356 | CPUState *cs; |
1357 | ||
7af1e7b0 CLG |
1358 | qemu_mutex_lock_iothread(); |
1359 | CPU_FOREACH(cs) { | |
1360 | PowerPCCPU *cpu = POWERPC_CPU(cs); | |
1361 | CPUPPCState *cenv = &cpu->env; | |
1362 | ||
1363 | /* TODO: broadcast message to all threads of the same processor */ | |
1364 | if (cenv->spr_cb[SPR_PIR].default_value == pir) { | |
1365 | cenv->pending_interrupts |= 1 << irq; | |
1366 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); | |
1367 | } | |
1368 | } | |
1369 | qemu_mutex_unlock_iothread(); | |
1370 | } | |
5ba7ba1d CLG |
1371 | |
1372 | void helper_book3s_msgsnd(target_ulong rb) | |
1373 | { | |
1374 | int pir = rb & DBELL_PROCIDTAG_MASK; | |
1375 | ||
1376 | if (!dbell_type_server(rb)) { | |
1377 | return; | |
1378 | } | |
1379 | ||
1380 | book3s_msgsnd_common(pir, PPC_INTERRUPT_HDOORBELL); | |
1381 | } | |
1382 | ||
1383 | #if defined(TARGET_PPC64) | |
1384 | void helper_book3s_msgclrp(CPUPPCState *env, target_ulong rb) | |
1385 | { | |
493028d8 CLG |
1386 | helper_hfscr_facility_check(env, HFSCR_MSGP, "msgclrp", HFSCR_IC_MSGP); |
1387 | ||
5ba7ba1d CLG |
1388 | if (!dbell_type_server(rb)) { |
1389 | return; | |
1390 | } | |
1391 | ||
1392 | env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); | |
1393 | } | |
1394 | ||
1395 | /* | |
1396 | * sends a message to other threads that are on the same | |
1397 | * multi-threaded processor | |
1398 | */ | |
1399 | void helper_book3s_msgsndp(CPUPPCState *env, target_ulong rb) | |
1400 | { | |
1401 | int pir = env->spr_cb[SPR_PIR].default_value; | |
1402 | ||
493028d8 CLG |
1403 | helper_hfscr_facility_check(env, HFSCR_MSGP, "msgsndp", HFSCR_IC_MSGP); |
1404 | ||
5ba7ba1d CLG |
1405 | if (!dbell_type_server(rb)) { |
1406 | return; | |
1407 | } | |
1408 | ||
1409 | /* TODO: TCG supports only one thread */ | |
1410 | ||
1411 | book3s_msgsnd_common(pir, PPC_INTERRUPT_DOORBELL); | |
1412 | } | |
1413 | #endif | |
ad71ed68 | 1414 | #endif |
0f3110fa RH |
1415 | |
1416 | void ppc_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, | |
1417 | MMUAccessType access_type, | |
1418 | int mmu_idx, uintptr_t retaddr) | |
1419 | { | |
1420 | CPUPPCState *env = cs->env_ptr; | |
1421 | uint32_t insn; | |
1422 | ||
1423 | /* Restore state and reload the insn we executed, for filling in DSISR. */ | |
1424 | cpu_restore_state(cs, retaddr, true); | |
1425 | insn = cpu_ldl_code(env, env->nip); | |
1426 | ||
1427 | cs->exception_index = POWERPC_EXCP_ALIGN; | |
1428 | env->error_code = insn & 0x03FF0000; | |
1429 | cpu_loop_exit(cs); | |
1430 | } |