root / target-ppc / excp_helper.c @ 13ef70f6
History | View | Annotate | Download (33.1 kB)
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 "cpu.h" |
20 |
#include "helper.h" |
21 |
|
22 |
#include "helper_regs.h" |
23 |
|
24 |
//#define DEBUG_OP
|
25 |
//#define DEBUG_EXCEPTIONS
|
26 |
|
27 |
#ifdef DEBUG_EXCEPTIONS
|
28 |
# define LOG_EXCP(...) qemu_log(__VA_ARGS__)
|
29 |
#else
|
30 |
# define LOG_EXCP(...) do { } while (0) |
31 |
#endif
|
32 |
|
33 |
/*****************************************************************************/
|
34 |
/* PowerPC Hypercall emulation */
|
35 |
|
36 |
void (*cpu_ppc_hypercall)(CPUPPCState *);
|
37 |
|
38 |
/*****************************************************************************/
|
39 |
/* Exception processing */
|
40 |
#if defined(CONFIG_USER_ONLY)
|
41 |
void do_interrupt(CPUPPCState *env)
|
42 |
{ |
43 |
env->exception_index = POWERPC_EXCP_NONE; |
44 |
env->error_code = 0;
|
45 |
} |
46 |
|
47 |
void ppc_hw_interrupt(CPUPPCState *env)
|
48 |
{ |
49 |
env->exception_index = POWERPC_EXCP_NONE; |
50 |
env->error_code = 0;
|
51 |
} |
52 |
#else /* defined(CONFIG_USER_ONLY) */ |
53 |
static inline void dump_syscall(CPUPPCState *env) |
54 |
{ |
55 |
qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 " r3=%016" PRIx64 |
56 |
" r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64 |
57 |
" nip=" TARGET_FMT_lx "\n", |
58 |
ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3), |
59 |
ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5), |
60 |
ppc_dump_gpr(env, 6), env->nip);
|
61 |
} |
62 |
|
63 |
/* Note that this function should be greatly optimized
|
64 |
* when called with a constant excp, from ppc_hw_interrupt
|
65 |
*/
|
66 |
static inline void powerpc_excp(CPUPPCState *env, int excp_model, int excp) |
67 |
{ |
68 |
target_ulong msr, new_msr, vector; |
69 |
int srr0, srr1, asrr0, asrr1;
|
70 |
int lpes0, lpes1, lev;
|
71 |
|
72 |
if (0) { |
73 |
/* XXX: find a suitable condition to enable the hypervisor mode */
|
74 |
lpes0 = (env->spr[SPR_LPCR] >> 1) & 1; |
75 |
lpes1 = (env->spr[SPR_LPCR] >> 2) & 1; |
76 |
} else {
|
77 |
/* Those values ensure we won't enter the hypervisor mode */
|
78 |
lpes0 = 0;
|
79 |
lpes1 = 1;
|
80 |
} |
81 |
|
82 |
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
|
83 |
" => %08x (%02x)\n", env->nip, excp, env->error_code);
|
84 |
|
85 |
/* new srr1 value excluding must-be-zero bits */
|
86 |
msr = env->msr & ~0x783f0000ULL;
|
87 |
|
88 |
/* new interrupt handler msr */
|
89 |
new_msr = env->msr & ((target_ulong)1 << MSR_ME);
|
90 |
|
91 |
/* target registers */
|
92 |
srr0 = SPR_SRR0; |
93 |
srr1 = SPR_SRR1; |
94 |
asrr0 = -1;
|
95 |
asrr1 = -1;
|
96 |
|
97 |
switch (excp) {
|
98 |
case POWERPC_EXCP_NONE:
|
99 |
/* Should never happen */
|
100 |
return;
|
101 |
case POWERPC_EXCP_CRITICAL: /* Critical input */ |
102 |
switch (excp_model) {
|
103 |
case POWERPC_EXCP_40x:
|
104 |
srr0 = SPR_40x_SRR2; |
105 |
srr1 = SPR_40x_SRR3; |
106 |
break;
|
107 |
case POWERPC_EXCP_BOOKE:
|
108 |
srr0 = SPR_BOOKE_CSRR0; |
109 |
srr1 = SPR_BOOKE_CSRR1; |
110 |
break;
|
111 |
case POWERPC_EXCP_G2:
|
112 |
break;
|
113 |
default:
|
114 |
goto excp_invalid;
|
115 |
} |
116 |
goto store_next;
|
117 |
case POWERPC_EXCP_MCHECK: /* Machine check exception */ |
118 |
if (msr_me == 0) { |
119 |
/* Machine check exception is not enabled.
|
120 |
* Enter checkstop state.
|
121 |
*/
|
122 |
if (qemu_log_enabled()) {
|
123 |
qemu_log("Machine check while not allowed. "
|
124 |
"Entering checkstop state\n");
|
125 |
} else {
|
126 |
fprintf(stderr, "Machine check while not allowed. "
|
127 |
"Entering checkstop state\n");
|
128 |
} |
129 |
env->halted = 1;
|
130 |
env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
131 |
} |
132 |
if (0) { |
133 |
/* XXX: find a suitable condition to enable the hypervisor mode */
|
134 |
new_msr |= (target_ulong)MSR_HVB; |
135 |
} |
136 |
|
137 |
/* machine check exceptions don't have ME set */
|
138 |
new_msr &= ~((target_ulong)1 << MSR_ME);
|
139 |
|
140 |
/* XXX: should also have something loaded in DAR / DSISR */
|
141 |
switch (excp_model) {
|
142 |
case POWERPC_EXCP_40x:
|
143 |
srr0 = SPR_40x_SRR2; |
144 |
srr1 = SPR_40x_SRR3; |
145 |
break;
|
146 |
case POWERPC_EXCP_BOOKE:
|
147 |
srr0 = SPR_BOOKE_MCSRR0; |
148 |
srr1 = SPR_BOOKE_MCSRR1; |
149 |
asrr0 = SPR_BOOKE_CSRR0; |
150 |
asrr1 = SPR_BOOKE_CSRR1; |
151 |
break;
|
152 |
default:
|
153 |
break;
|
154 |
} |
155 |
goto store_next;
|
156 |
case POWERPC_EXCP_DSI: /* Data storage exception */ |
157 |
LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx |
158 |
"\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]);
|
159 |
if (lpes1 == 0) { |
160 |
new_msr |= (target_ulong)MSR_HVB; |
161 |
} |
162 |
goto store_next;
|
163 |
case POWERPC_EXCP_ISI: /* Instruction storage exception */ |
164 |
LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx |
165 |
"\n", msr, env->nip);
|
166 |
if (lpes1 == 0) { |
167 |
new_msr |= (target_ulong)MSR_HVB; |
168 |
} |
169 |
msr |= env->error_code; |
170 |
goto store_next;
|
171 |
case POWERPC_EXCP_EXTERNAL: /* External input */ |
172 |
if (lpes0 == 1) { |
173 |
new_msr |= (target_ulong)MSR_HVB; |
174 |
} |
175 |
goto store_next;
|
176 |
case POWERPC_EXCP_ALIGN: /* Alignment exception */ |
177 |
if (lpes1 == 0) { |
178 |
new_msr |= (target_ulong)MSR_HVB; |
179 |
} |
180 |
/* XXX: this is false */
|
181 |
/* Get rS/rD and rA from faulting opcode */
|
182 |
env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4))
|
183 |
& 0x03FF0000) >> 16; |
184 |
goto store_current;
|
185 |
case POWERPC_EXCP_PROGRAM: /* Program exception */ |
186 |
switch (env->error_code & ~0xF) { |
187 |
case POWERPC_EXCP_FP:
|
188 |
if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { |
189 |
LOG_EXCP("Ignore floating point exception\n");
|
190 |
env->exception_index = POWERPC_EXCP_NONE; |
191 |
env->error_code = 0;
|
192 |
return;
|
193 |
} |
194 |
if (lpes1 == 0) { |
195 |
new_msr |= (target_ulong)MSR_HVB; |
196 |
} |
197 |
msr |= 0x00100000;
|
198 |
if (msr_fe0 == msr_fe1) {
|
199 |
goto store_next;
|
200 |
} |
201 |
msr |= 0x00010000;
|
202 |
break;
|
203 |
case POWERPC_EXCP_INVAL:
|
204 |
LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); |
205 |
if (lpes1 == 0) { |
206 |
new_msr |= (target_ulong)MSR_HVB; |
207 |
} |
208 |
msr |= 0x00080000;
|
209 |
env->spr[SPR_BOOKE_ESR] = ESR_PIL; |
210 |
break;
|
211 |
case POWERPC_EXCP_PRIV:
|
212 |
if (lpes1 == 0) { |
213 |
new_msr |= (target_ulong)MSR_HVB; |
214 |
} |
215 |
msr |= 0x00040000;
|
216 |
env->spr[SPR_BOOKE_ESR] = ESR_PPR; |
217 |
break;
|
218 |
case POWERPC_EXCP_TRAP:
|
219 |
if (lpes1 == 0) { |
220 |
new_msr |= (target_ulong)MSR_HVB; |
221 |
} |
222 |
msr |= 0x00020000;
|
223 |
env->spr[SPR_BOOKE_ESR] = ESR_PTR; |
224 |
break;
|
225 |
default:
|
226 |
/* Should never occur */
|
227 |
cpu_abort(env, "Invalid program exception %d. Aborting\n",
|
228 |
env->error_code); |
229 |
break;
|
230 |
} |
231 |
goto store_current;
|
232 |
case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ |
233 |
if (lpes1 == 0) { |
234 |
new_msr |= (target_ulong)MSR_HVB; |
235 |
} |
236 |
goto store_current;
|
237 |
case POWERPC_EXCP_SYSCALL: /* System call exception */ |
238 |
dump_syscall(env); |
239 |
lev = env->error_code; |
240 |
if ((lev == 1) && cpu_ppc_hypercall) { |
241 |
cpu_ppc_hypercall(env); |
242 |
return;
|
243 |
} |
244 |
if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) { |
245 |
new_msr |= (target_ulong)MSR_HVB; |
246 |
} |
247 |
goto store_next;
|
248 |
case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ |
249 |
goto store_current;
|
250 |
case POWERPC_EXCP_DECR: /* Decrementer exception */ |
251 |
if (lpes1 == 0) { |
252 |
new_msr |= (target_ulong)MSR_HVB; |
253 |
} |
254 |
goto store_next;
|
255 |
case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ |
256 |
/* FIT on 4xx */
|
257 |
LOG_EXCP("FIT exception\n");
|
258 |
goto store_next;
|
259 |
case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ |
260 |
LOG_EXCP("WDT exception\n");
|
261 |
switch (excp_model) {
|
262 |
case POWERPC_EXCP_BOOKE:
|
263 |
srr0 = SPR_BOOKE_CSRR0; |
264 |
srr1 = SPR_BOOKE_CSRR1; |
265 |
break;
|
266 |
default:
|
267 |
break;
|
268 |
} |
269 |
goto store_next;
|
270 |
case POWERPC_EXCP_DTLB: /* Data TLB error */ |
271 |
goto store_next;
|
272 |
case POWERPC_EXCP_ITLB: /* Instruction TLB error */ |
273 |
goto store_next;
|
274 |
case POWERPC_EXCP_DEBUG: /* Debug interrupt */ |
275 |
switch (excp_model) {
|
276 |
case POWERPC_EXCP_BOOKE:
|
277 |
srr0 = SPR_BOOKE_DSRR0; |
278 |
srr1 = SPR_BOOKE_DSRR1; |
279 |
asrr0 = SPR_BOOKE_CSRR0; |
280 |
asrr1 = SPR_BOOKE_CSRR1; |
281 |
break;
|
282 |
default:
|
283 |
break;
|
284 |
} |
285 |
/* XXX: TODO */
|
286 |
cpu_abort(env, "Debug exception is not implemented yet !\n");
|
287 |
goto store_next;
|
288 |
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ |
289 |
env->spr[SPR_BOOKE_ESR] = ESR_SPV; |
290 |
goto store_current;
|
291 |
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ |
292 |
/* XXX: TODO */
|
293 |
cpu_abort(env, "Embedded floating point data exception "
|
294 |
"is not implemented yet !\n");
|
295 |
env->spr[SPR_BOOKE_ESR] = ESR_SPV; |
296 |
goto store_next;
|
297 |
case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ |
298 |
/* XXX: TODO */
|
299 |
cpu_abort(env, "Embedded floating point round exception "
|
300 |
"is not implemented yet !\n");
|
301 |
env->spr[SPR_BOOKE_ESR] = ESR_SPV; |
302 |
goto store_next;
|
303 |
case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ |
304 |
/* XXX: TODO */
|
305 |
cpu_abort(env, |
306 |
"Performance counter exception is not implemented yet !\n");
|
307 |
goto store_next;
|
308 |
case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ |
309 |
goto store_next;
|
310 |
case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ |
311 |
srr0 = SPR_BOOKE_CSRR0; |
312 |
srr1 = SPR_BOOKE_CSRR1; |
313 |
goto store_next;
|
314 |
case POWERPC_EXCP_RESET: /* System reset exception */ |
315 |
if (msr_pow) {
|
316 |
/* indicate that we resumed from power save mode */
|
317 |
msr |= 0x10000;
|
318 |
} else {
|
319 |
new_msr &= ~((target_ulong)1 << MSR_ME);
|
320 |
} |
321 |
|
322 |
if (0) { |
323 |
/* XXX: find a suitable condition to enable the hypervisor mode */
|
324 |
new_msr |= (target_ulong)MSR_HVB; |
325 |
} |
326 |
goto store_next;
|
327 |
case POWERPC_EXCP_DSEG: /* Data segment exception */ |
328 |
if (lpes1 == 0) { |
329 |
new_msr |= (target_ulong)MSR_HVB; |
330 |
} |
331 |
goto store_next;
|
332 |
case POWERPC_EXCP_ISEG: /* Instruction segment exception */ |
333 |
if (lpes1 == 0) { |
334 |
new_msr |= (target_ulong)MSR_HVB; |
335 |
} |
336 |
goto store_next;
|
337 |
case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ |
338 |
srr0 = SPR_HSRR0; |
339 |
srr1 = SPR_HSRR1; |
340 |
new_msr |= (target_ulong)MSR_HVB; |
341 |
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
342 |
goto store_next;
|
343 |
case POWERPC_EXCP_TRACE: /* Trace exception */ |
344 |
if (lpes1 == 0) { |
345 |
new_msr |= (target_ulong)MSR_HVB; |
346 |
} |
347 |
goto store_next;
|
348 |
case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ |
349 |
srr0 = SPR_HSRR0; |
350 |
srr1 = SPR_HSRR1; |
351 |
new_msr |= (target_ulong)MSR_HVB; |
352 |
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
353 |
goto store_next;
|
354 |
case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ |
355 |
srr0 = SPR_HSRR0; |
356 |
srr1 = SPR_HSRR1; |
357 |
new_msr |= (target_ulong)MSR_HVB; |
358 |
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
359 |
goto store_next;
|
360 |
case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ |
361 |
srr0 = SPR_HSRR0; |
362 |
srr1 = SPR_HSRR1; |
363 |
new_msr |= (target_ulong)MSR_HVB; |
364 |
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
365 |
goto store_next;
|
366 |
case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ |
367 |
srr0 = SPR_HSRR0; |
368 |
srr1 = SPR_HSRR1; |
369 |
new_msr |= (target_ulong)MSR_HVB; |
370 |
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
371 |
goto store_next;
|
372 |
case POWERPC_EXCP_VPU: /* Vector unavailable exception */ |
373 |
if (lpes1 == 0) { |
374 |
new_msr |= (target_ulong)MSR_HVB; |
375 |
} |
376 |
goto store_current;
|
377 |
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ |
378 |
LOG_EXCP("PIT exception\n");
|
379 |
goto store_next;
|
380 |
case POWERPC_EXCP_IO: /* IO error exception */ |
381 |
/* XXX: TODO */
|
382 |
cpu_abort(env, "601 IO error exception is not implemented yet !\n");
|
383 |
goto store_next;
|
384 |
case POWERPC_EXCP_RUNM: /* Run mode exception */ |
385 |
/* XXX: TODO */
|
386 |
cpu_abort(env, "601 run mode exception is not implemented yet !\n");
|
387 |
goto store_next;
|
388 |
case POWERPC_EXCP_EMUL: /* Emulation trap exception */ |
389 |
/* XXX: TODO */
|
390 |
cpu_abort(env, "602 emulation trap exception "
|
391 |
"is not implemented yet !\n");
|
392 |
goto store_next;
|
393 |
case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ |
394 |
if (lpes1 == 0) { /* XXX: check this */ |
395 |
new_msr |= (target_ulong)MSR_HVB; |
396 |
} |
397 |
switch (excp_model) {
|
398 |
case POWERPC_EXCP_602:
|
399 |
case POWERPC_EXCP_603:
|
400 |
case POWERPC_EXCP_603E:
|
401 |
case POWERPC_EXCP_G2:
|
402 |
goto tlb_miss_tgpr;
|
403 |
case POWERPC_EXCP_7x5:
|
404 |
goto tlb_miss;
|
405 |
case POWERPC_EXCP_74xx:
|
406 |
goto tlb_miss_74xx;
|
407 |
default:
|
408 |
cpu_abort(env, "Invalid instruction TLB miss exception\n");
|
409 |
break;
|
410 |
} |
411 |
break;
|
412 |
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ |
413 |
if (lpes1 == 0) { /* XXX: check this */ |
414 |
new_msr |= (target_ulong)MSR_HVB; |
415 |
} |
416 |
switch (excp_model) {
|
417 |
case POWERPC_EXCP_602:
|
418 |
case POWERPC_EXCP_603:
|
419 |
case POWERPC_EXCP_603E:
|
420 |
case POWERPC_EXCP_G2:
|
421 |
goto tlb_miss_tgpr;
|
422 |
case POWERPC_EXCP_7x5:
|
423 |
goto tlb_miss;
|
424 |
case POWERPC_EXCP_74xx:
|
425 |
goto tlb_miss_74xx;
|
426 |
default:
|
427 |
cpu_abort(env, "Invalid data load TLB miss exception\n");
|
428 |
break;
|
429 |
} |
430 |
break;
|
431 |
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ |
432 |
if (lpes1 == 0) { /* XXX: check this */ |
433 |
new_msr |= (target_ulong)MSR_HVB; |
434 |
} |
435 |
switch (excp_model) {
|
436 |
case POWERPC_EXCP_602:
|
437 |
case POWERPC_EXCP_603:
|
438 |
case POWERPC_EXCP_603E:
|
439 |
case POWERPC_EXCP_G2:
|
440 |
tlb_miss_tgpr:
|
441 |
/* Swap temporary saved registers with GPRs */
|
442 |
if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) { |
443 |
new_msr |= (target_ulong)1 << MSR_TGPR;
|
444 |
hreg_swap_gpr_tgpr(env); |
445 |
} |
446 |
goto tlb_miss;
|
447 |
case POWERPC_EXCP_7x5:
|
448 |
tlb_miss:
|
449 |
#if defined(DEBUG_SOFTWARE_TLB)
|
450 |
if (qemu_log_enabled()) {
|
451 |
const char *es; |
452 |
target_ulong *miss, *cmp; |
453 |
int en;
|
454 |
|
455 |
if (excp == POWERPC_EXCP_IFTLB) {
|
456 |
es = "I";
|
457 |
en = 'I';
|
458 |
miss = &env->spr[SPR_IMISS]; |
459 |
cmp = &env->spr[SPR_ICMP]; |
460 |
} else {
|
461 |
if (excp == POWERPC_EXCP_DLTLB) {
|
462 |
es = "DL";
|
463 |
} else {
|
464 |
es = "DS";
|
465 |
} |
466 |
en = 'D';
|
467 |
miss = &env->spr[SPR_DMISS]; |
468 |
cmp = &env->spr[SPR_DCMP]; |
469 |
} |
470 |
qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " |
471 |
TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 " |
472 |
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
|
473 |
env->spr[SPR_HASH1], env->spr[SPR_HASH2], |
474 |
env->error_code); |
475 |
} |
476 |
#endif
|
477 |
msr |= env->crf[0] << 28; |
478 |
msr |= env->error_code; /* key, D/I, S/L bits */
|
479 |
/* Set way using a LRU mechanism */
|
480 |
msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; |
481 |
break;
|
482 |
case POWERPC_EXCP_74xx:
|
483 |
tlb_miss_74xx:
|
484 |
#if defined(DEBUG_SOFTWARE_TLB)
|
485 |
if (qemu_log_enabled()) {
|
486 |
const char *es; |
487 |
target_ulong *miss, *cmp; |
488 |
int en;
|
489 |
|
490 |
if (excp == POWERPC_EXCP_IFTLB) {
|
491 |
es = "I";
|
492 |
en = 'I';
|
493 |
miss = &env->spr[SPR_TLBMISS]; |
494 |
cmp = &env->spr[SPR_PTEHI]; |
495 |
} else {
|
496 |
if (excp == POWERPC_EXCP_DLTLB) {
|
497 |
es = "DL";
|
498 |
} else {
|
499 |
es = "DS";
|
500 |
} |
501 |
en = 'D';
|
502 |
miss = &env->spr[SPR_TLBMISS]; |
503 |
cmp = &env->spr[SPR_PTEHI]; |
504 |
} |
505 |
qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC " |
506 |
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
|
507 |
env->error_code); |
508 |
} |
509 |
#endif
|
510 |
msr |= env->error_code; /* key bit */
|
511 |
break;
|
512 |
default:
|
513 |
cpu_abort(env, "Invalid data store TLB miss exception\n");
|
514 |
break;
|
515 |
} |
516 |
goto store_next;
|
517 |
case POWERPC_EXCP_FPA: /* Floating-point assist exception */ |
518 |
/* XXX: TODO */
|
519 |
cpu_abort(env, "Floating point assist exception "
|
520 |
"is not implemented yet !\n");
|
521 |
goto store_next;
|
522 |
case POWERPC_EXCP_DABR: /* Data address breakpoint */ |
523 |
/* XXX: TODO */
|
524 |
cpu_abort(env, "DABR exception is not implemented yet !\n");
|
525 |
goto store_next;
|
526 |
case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ |
527 |
/* XXX: TODO */
|
528 |
cpu_abort(env, "IABR exception is not implemented yet !\n");
|
529 |
goto store_next;
|
530 |
case POWERPC_EXCP_SMI: /* System management interrupt */ |
531 |
/* XXX: TODO */
|
532 |
cpu_abort(env, "SMI exception is not implemented yet !\n");
|
533 |
goto store_next;
|
534 |
case POWERPC_EXCP_THERM: /* Thermal interrupt */ |
535 |
/* XXX: TODO */
|
536 |
cpu_abort(env, "Thermal management exception "
|
537 |
"is not implemented yet !\n");
|
538 |
goto store_next;
|
539 |
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ |
540 |
if (lpes1 == 0) { |
541 |
new_msr |= (target_ulong)MSR_HVB; |
542 |
} |
543 |
/* XXX: TODO */
|
544 |
cpu_abort(env, |
545 |
"Performance counter exception is not implemented yet !\n");
|
546 |
goto store_next;
|
547 |
case POWERPC_EXCP_VPUA: /* Vector assist exception */ |
548 |
/* XXX: TODO */
|
549 |
cpu_abort(env, "VPU assist exception is not implemented yet !\n");
|
550 |
goto store_next;
|
551 |
case POWERPC_EXCP_SOFTP: /* Soft patch exception */ |
552 |
/* XXX: TODO */
|
553 |
cpu_abort(env, |
554 |
"970 soft-patch exception is not implemented yet !\n");
|
555 |
goto store_next;
|
556 |
case POWERPC_EXCP_MAINT: /* Maintenance exception */ |
557 |
/* XXX: TODO */
|
558 |
cpu_abort(env, |
559 |
"970 maintenance exception is not implemented yet !\n");
|
560 |
goto store_next;
|
561 |
case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ |
562 |
/* XXX: TODO */
|
563 |
cpu_abort(env, "Maskable external exception "
|
564 |
"is not implemented yet !\n");
|
565 |
goto store_next;
|
566 |
case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ |
567 |
/* XXX: TODO */
|
568 |
cpu_abort(env, "Non maskable external exception "
|
569 |
"is not implemented yet !\n");
|
570 |
goto store_next;
|
571 |
default:
|
572 |
excp_invalid:
|
573 |
cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp);
|
574 |
break;
|
575 |
store_current:
|
576 |
/* save current instruction location */
|
577 |
env->spr[srr0] = env->nip - 4;
|
578 |
break;
|
579 |
store_next:
|
580 |
/* save next instruction location */
|
581 |
env->spr[srr0] = env->nip; |
582 |
break;
|
583 |
} |
584 |
/* Save MSR */
|
585 |
env->spr[srr1] = msr; |
586 |
/* If any alternate SRR register are defined, duplicate saved values */
|
587 |
if (asrr0 != -1) { |
588 |
env->spr[asrr0] = env->spr[srr0]; |
589 |
} |
590 |
if (asrr1 != -1) { |
591 |
env->spr[asrr1] = env->spr[srr1]; |
592 |
} |
593 |
/* If we disactivated any translation, flush TLBs */
|
594 |
if (msr & ((1 << MSR_IR) | (1 << MSR_DR))) { |
595 |
tlb_flush(env, 1);
|
596 |
} |
597 |
|
598 |
if (msr_ile) {
|
599 |
new_msr |= (target_ulong)1 << MSR_LE;
|
600 |
} |
601 |
|
602 |
/* Jump to handler */
|
603 |
vector = env->excp_vectors[excp]; |
604 |
if (vector == (target_ulong)-1ULL) { |
605 |
cpu_abort(env, "Raised an exception without defined vector %d\n",
|
606 |
excp); |
607 |
} |
608 |
vector |= env->excp_prefix; |
609 |
#if defined(TARGET_PPC64)
|
610 |
if (excp_model == POWERPC_EXCP_BOOKE) {
|
611 |
if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
|
612 |
/* Cat.64-bit: EPCR.ICM is copied to MSR.CM */
|
613 |
new_msr |= (target_ulong)1 << MSR_CM;
|
614 |
} else {
|
615 |
vector = (uint32_t)vector; |
616 |
} |
617 |
} else {
|
618 |
if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) {
|
619 |
vector = (uint32_t)vector; |
620 |
} else {
|
621 |
new_msr |= (target_ulong)1 << MSR_SF;
|
622 |
} |
623 |
} |
624 |
#endif
|
625 |
/* XXX: we don't use hreg_store_msr here as already have treated
|
626 |
* any special case that could occur. Just store MSR and update hflags
|
627 |
*/
|
628 |
env->msr = new_msr & env->msr_mask; |
629 |
hreg_compute_hflags(env); |
630 |
env->nip = vector; |
631 |
/* Reset exception state */
|
632 |
env->exception_index = POWERPC_EXCP_NONE; |
633 |
env->error_code = 0;
|
634 |
|
635 |
if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
|
636 |
(env->mmu_model == POWERPC_MMU_BOOKE206)) { |
637 |
/* XXX: The BookE changes address space when switching modes,
|
638 |
we should probably implement that as different MMU indexes,
|
639 |
but for the moment we do it the slow way and flush all. */
|
640 |
tlb_flush(env, 1);
|
641 |
} |
642 |
} |
643 |
|
644 |
void do_interrupt(CPUPPCState *env)
|
645 |
{ |
646 |
powerpc_excp(env, env->excp_model, env->exception_index); |
647 |
} |
648 |
|
649 |
void ppc_hw_interrupt(CPUPPCState *env)
|
650 |
{ |
651 |
int hdice;
|
652 |
|
653 |
#if 0
|
654 |
qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n",
|
655 |
__func__, env, env->pending_interrupts,
|
656 |
env->interrupt_request, (int)msr_me, (int)msr_ee);
|
657 |
#endif
|
658 |
/* External reset */
|
659 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { |
660 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
|
661 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_RESET); |
662 |
return;
|
663 |
} |
664 |
/* Machine check exception */
|
665 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { |
666 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK);
|
667 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_MCHECK); |
668 |
return;
|
669 |
} |
670 |
#if 0 /* TODO */
|
671 |
/* External debug exception */
|
672 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) {
|
673 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG);
|
674 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DEBUG);
|
675 |
return;
|
676 |
}
|
677 |
#endif
|
678 |
if (0) { |
679 |
/* XXX: find a suitable condition to enable the hypervisor mode */
|
680 |
hdice = env->spr[SPR_LPCR] & 1;
|
681 |
} else {
|
682 |
hdice = 0;
|
683 |
} |
684 |
if ((msr_ee != 0 || msr_hv == 0 || msr_pr != 0) && hdice != 0) { |
685 |
/* Hypervisor decrementer exception */
|
686 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { |
687 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
688 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_HDECR); |
689 |
return;
|
690 |
} |
691 |
} |
692 |
if (msr_ce != 0) { |
693 |
/* External critical interrupt */
|
694 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { |
695 |
/* Taking a critical external interrupt does not clear the external
|
696 |
* critical interrupt status
|
697 |
*/
|
698 |
#if 0
|
699 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT);
|
700 |
#endif
|
701 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_CRITICAL); |
702 |
return;
|
703 |
} |
704 |
} |
705 |
if (msr_ee != 0) { |
706 |
/* Watchdog timer on embedded PowerPC */
|
707 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { |
708 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
|
709 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT); |
710 |
return;
|
711 |
} |
712 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { |
713 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL);
|
714 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI); |
715 |
return;
|
716 |
} |
717 |
/* Fixed interval timer on embedded PowerPC */
|
718 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { |
719 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT);
|
720 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_FIT); |
721 |
return;
|
722 |
} |
723 |
/* Programmable interval timer on embedded PowerPC */
|
724 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { |
725 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT);
|
726 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_PIT); |
727 |
return;
|
728 |
} |
729 |
/* Decrementer exception */
|
730 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { |
731 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR);
|
732 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR); |
733 |
return;
|
734 |
} |
735 |
/* External interrupt */
|
736 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { |
737 |
/* Taking an external interrupt does not clear the external
|
738 |
* interrupt status
|
739 |
*/
|
740 |
#if 0
|
741 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT);
|
742 |
#endif
|
743 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); |
744 |
return;
|
745 |
} |
746 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { |
747 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
748 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI); |
749 |
return;
|
750 |
} |
751 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { |
752 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM);
|
753 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM); |
754 |
return;
|
755 |
} |
756 |
/* Thermal interrupt */
|
757 |
if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { |
758 |
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM);
|
759 |
powerpc_excp(env, env->excp_model, POWERPC_EXCP_THERM); |
760 |
return;
|
761 |
} |
762 |
} |
763 |
} |
764 |
#endif /* !CONFIG_USER_ONLY */ |
765 |
|
766 |
#if defined(DEBUG_OP)
|
767 |
static void cpu_dump_rfi(target_ulong RA, target_ulong msr) |
768 |
{ |
769 |
qemu_log("Return from exception at " TARGET_FMT_lx " with flags " |
770 |
TARGET_FMT_lx "\n", RA, msr);
|
771 |
} |
772 |
#endif
|
773 |
|
774 |
/*****************************************************************************/
|
775 |
/* Exceptions processing helpers */
|
776 |
|
777 |
void helper_raise_exception_err(CPUPPCState *env, uint32_t exception,
|
778 |
uint32_t error_code) |
779 |
{ |
780 |
#if 0
|
781 |
printf("Raise exception %3x code : %d\n", exception, error_code);
|
782 |
#endif
|
783 |
env->exception_index = exception; |
784 |
env->error_code = error_code; |
785 |
cpu_loop_exit(env); |
786 |
} |
787 |
|
788 |
void helper_raise_exception(CPUPPCState *env, uint32_t exception)
|
789 |
{ |
790 |
helper_raise_exception_err(env, exception, 0);
|
791 |
} |
792 |
|
793 |
#if !defined(CONFIG_USER_ONLY)
|
794 |
void helper_store_msr(CPUPPCState *env, target_ulong val)
|
795 |
{ |
796 |
val = hreg_store_msr(env, val, 0);
|
797 |
if (val != 0) { |
798 |
env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
799 |
helper_raise_exception(env, val); |
800 |
} |
801 |
} |
802 |
|
803 |
static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, |
804 |
target_ulong msrm, int keep_msrh)
|
805 |
{ |
806 |
#if defined(TARGET_PPC64)
|
807 |
if (msr_is_64bit(env, msr)) {
|
808 |
nip = (uint64_t)nip; |
809 |
msr &= (uint64_t)msrm; |
810 |
} else {
|
811 |
nip = (uint32_t)nip; |
812 |
msr = (uint32_t)(msr & msrm); |
813 |
if (keep_msrh) {
|
814 |
msr |= env->msr & ~((uint64_t)0xFFFFFFFF);
|
815 |
} |
816 |
} |
817 |
#else
|
818 |
nip = (uint32_t)nip; |
819 |
msr &= (uint32_t)msrm; |
820 |
#endif
|
821 |
/* XXX: beware: this is false if VLE is supported */
|
822 |
env->nip = nip & ~((target_ulong)0x00000003);
|
823 |
hreg_store_msr(env, msr, 1);
|
824 |
#if defined(DEBUG_OP)
|
825 |
cpu_dump_rfi(env->nip, env->msr); |
826 |
#endif
|
827 |
/* No need to raise an exception here,
|
828 |
* as rfi is always the last insn of a TB
|
829 |
*/
|
830 |
env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
831 |
} |
832 |
|
833 |
void helper_rfi(CPUPPCState *env)
|
834 |
{ |
835 |
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], |
836 |
~((target_ulong)0x783F0000), 1); |
837 |
} |
838 |
|
839 |
#if defined(TARGET_PPC64)
|
840 |
void helper_rfid(CPUPPCState *env)
|
841 |
{ |
842 |
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], |
843 |
~((target_ulong)0x783F0000), 0); |
844 |
} |
845 |
|
846 |
void helper_hrfid(CPUPPCState *env)
|
847 |
{ |
848 |
do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1], |
849 |
~((target_ulong)0x783F0000), 0); |
850 |
} |
851 |
#endif
|
852 |
|
853 |
/*****************************************************************************/
|
854 |
/* Embedded PowerPC specific helpers */
|
855 |
void helper_40x_rfci(CPUPPCState *env)
|
856 |
{ |
857 |
do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3], |
858 |
~((target_ulong)0xFFFF0000), 0); |
859 |
} |
860 |
|
861 |
void helper_rfci(CPUPPCState *env)
|
862 |
{ |
863 |
do_rfi(env, env->spr[SPR_BOOKE_CSRR0], SPR_BOOKE_CSRR1, |
864 |
~((target_ulong)0x3FFF0000), 0); |
865 |
} |
866 |
|
867 |
void helper_rfdi(CPUPPCState *env)
|
868 |
{ |
869 |
do_rfi(env, env->spr[SPR_BOOKE_DSRR0], SPR_BOOKE_DSRR1, |
870 |
~((target_ulong)0x3FFF0000), 0); |
871 |
} |
872 |
|
873 |
void helper_rfmci(CPUPPCState *env)
|
874 |
{ |
875 |
do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], SPR_BOOKE_MCSRR1, |
876 |
~((target_ulong)0x3FFF0000), 0); |
877 |
} |
878 |
#endif
|
879 |
|
880 |
void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
|
881 |
uint32_t flags) |
882 |
{ |
883 |
if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || |
884 |
((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) ||
|
885 |
((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) ||
|
886 |
((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) ||
|
887 |
((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) {
|
888 |
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, |
889 |
POWERPC_EXCP_TRAP); |
890 |
} |
891 |
} |
892 |
|
893 |
#if defined(TARGET_PPC64)
|
894 |
void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
|
895 |
uint32_t flags) |
896 |
{ |
897 |
if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || |
898 |
((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) ||
|
899 |
((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) ||
|
900 |
((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) ||
|
901 |
((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) {
|
902 |
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, |
903 |
POWERPC_EXCP_TRAP); |
904 |
} |
905 |
} |
906 |
#endif
|
907 |
|
908 |
#if !defined(CONFIG_USER_ONLY)
|
909 |
/*****************************************************************************/
|
910 |
/* PowerPC 601 specific instructions (POWER bridge) */
|
911 |
|
912 |
void helper_rfsvc(CPUPPCState *env)
|
913 |
{ |
914 |
do_rfi(env, env->lr, env->ctr, 0x0000FFFF, 0); |
915 |
} |
916 |
|
917 |
/* Embedded.Processor Control */
|
918 |
static int dbell2irq(target_ulong rb) |
919 |
{ |
920 |
int msg = rb & DBELL_TYPE_MASK;
|
921 |
int irq = -1; |
922 |
|
923 |
switch (msg) {
|
924 |
case DBELL_TYPE_DBELL:
|
925 |
irq = PPC_INTERRUPT_DOORBELL; |
926 |
break;
|
927 |
case DBELL_TYPE_DBELL_CRIT:
|
928 |
irq = PPC_INTERRUPT_CDOORBELL; |
929 |
break;
|
930 |
case DBELL_TYPE_G_DBELL:
|
931 |
case DBELL_TYPE_G_DBELL_CRIT:
|
932 |
case DBELL_TYPE_G_DBELL_MC:
|
933 |
/* XXX implement */
|
934 |
default:
|
935 |
break;
|
936 |
} |
937 |
|
938 |
return irq;
|
939 |
} |
940 |
|
941 |
void helper_msgclr(CPUPPCState *env, target_ulong rb)
|
942 |
{ |
943 |
int irq = dbell2irq(rb);
|
944 |
|
945 |
if (irq < 0) { |
946 |
return;
|
947 |
} |
948 |
|
949 |
env->pending_interrupts &= ~(1 << irq);
|
950 |
} |
951 |
|
952 |
void helper_msgsnd(target_ulong rb)
|
953 |
{ |
954 |
int irq = dbell2irq(rb);
|
955 |
int pir = rb & DBELL_PIRTAG_MASK;
|
956 |
CPUPPCState *cenv; |
957 |
|
958 |
if (irq < 0) { |
959 |
return;
|
960 |
} |
961 |
|
962 |
for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { |
963 |
if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) {
|
964 |
cenv->pending_interrupts |= 1 << irq;
|
965 |
cpu_interrupt(cenv, CPU_INTERRUPT_HARD); |
966 |
} |
967 |
} |
968 |
} |
969 |
#endif
|