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