Support for PowerPC BookE exception model.

No need to requeue timer exceptions.
Fix nip saving for 64 bits PowerPC.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2556 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
j_mayer 2007-03-31 12:57:57 +00:00
parent 636aaad7b5
commit c62db10577

View File

@ -1340,11 +1340,12 @@ void ppc_store_msr_32 (CPUPPCState *env, uint32_t value)
void do_compute_hflags (CPUPPCState *env)
{
/* Compute current hflags */
env->hflags = (msr_pr << MSR_PR) | (msr_le << MSR_LE) |
(msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_fe1 << MSR_FE1) |
(msr_vr << MSR_VR) | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) |
(msr_se << MSR_SE) | (msr_be << MSR_BE);
env->hflags = (msr_cm << MSR_CM) | (msr_vr << MSR_VR) |
(msr_ap << MSR_AP) | (msr_sa << MSR_SA) | (msr_pr << MSR_PR) |
(msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_se << MSR_SE) |
(msr_be << MSR_BE) | (msr_fe1 << MSR_FE1) | (msr_le << MSR_LE);
#if defined (TARGET_PPC64)
/* No care here: PowerPC 64 MSR_SF means the same as MSR_CM for BookE */
env->hflags |= (msr_sf << (MSR_SF - 32)) | (msr_hv << (MSR_HV - 32));
#endif
}
@ -1374,14 +1375,16 @@ static void dump_syscall(CPUState *env)
void do_interrupt (CPUState *env)
{
target_ulong msr, *srr_0, *srr_1;
int excp;
target_ulong msr, *srr_0, *srr_1, *asrr_0, *asrr_1;
int excp, idx;
excp = env->exception_index;
msr = do_load_msr(env);
/* The default is to use SRR0 & SRR1 to save the exception context */
srr_0 = &env->spr[SPR_SRR0];
srr_1 = &env->spr[SPR_SRR1];
asrr_0 = NULL;
asrr_1 = NULL;
#if defined (DEBUG_EXCEPTIONS)
if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) {
if (loglevel != 0) {
@ -1397,26 +1400,44 @@ void do_interrupt (CPUState *env)
env->nip, excp, env->error_code);
}
msr_pow = 0;
idx = -1;
/* Generate informations in save/restore registers */
switch (excp) {
/* Generic PowerPC exceptions */
case EXCP_RESET: /* 0x0100 */
if (PPC_EXCP(env) != PPC_FLAGS_EXCP_40x) {
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
srr_0 = &env->spr[SPR_40x_SRR2];
srr_1 = &env->spr[SPR_40x_SRR3];
break;
case PPC_FLAGS_EXCP_BOOKE:
idx = 0;
srr_0 = &env->spr[SPR_BOOKE_CSRR0];
srr_1 = &env->spr[SPR_BOOKE_CSRR1];
break;
default:
if (msr_ip)
excp += 0xFFC00;
excp |= 0xFFC00000;
} else {
srr_0 = &env->spr[SPR_40x_SRR2];
srr_1 = &env->spr[SPR_40x_SRR3];
break;
}
goto store_next;
case EXCP_MACHINE_CHECK: /* 0x0200 */
if (msr_me == 0) {
cpu_abort(env, "Machine check exception while not allowed\n");
}
if (unlikely(PPC_EXCP(env) == PPC_FLAGS_EXCP_40x)) {
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
srr_0 = &env->spr[SPR_40x_SRR2];
srr_1 = &env->spr[SPR_40x_SRR3];
break;
case PPC_FLAGS_EXCP_BOOKE:
idx = 1;
srr_0 = &env->spr[SPR_BOOKE_MCSRR0];
srr_1 = &env->spr[SPR_BOOKE_MCSRR1];
asrr_0 = &env->spr[SPR_BOOKE_CSRR0];
asrr_1 = &env->spr[SPR_BOOKE_CSRR1];
msr_ce = 0;
break;
default:
break;
}
msr_me = 0;
break;
@ -1425,6 +1446,7 @@ void do_interrupt (CPUState *env)
/* data location address has been stored
* when the fault has been detected
*/
idx = 2;
msr &= ~0xFFFF0000;
#if defined (DEBUG_EXCEPTIONS)
if (loglevel) {
@ -1438,6 +1460,7 @@ void do_interrupt (CPUState *env)
goto store_next;
case EXCP_ISI: /* 0x0400 */
/* Store exception cause */
idx = 3;
msr &= ~0xFFFF0000;
msr |= env->error_code;
#if defined (DEBUG_EXCEPTIONS)
@ -1448,20 +1471,12 @@ void do_interrupt (CPUState *env)
#endif
goto store_next;
case EXCP_EXTERNAL: /* 0x0500 */
if (msr_ee == 0) {
#if defined (DEBUG_EXCEPTIONS)
if (loglevel > 0) {
fprintf(logfile, "Skipping hardware interrupt\n");
}
#endif
/* Requeue it */
env->interrupt_request |= CPU_INTERRUPT_HARD;
return;
}
idx = 4;
goto store_next;
case EXCP_ALIGN: /* 0x0600 */
if (likely(PPC_EXCP(env) != PPC_FLAGS_EXCP_601)) {
/* Store exception cause */
idx = 5;
/* Get rS/rD and rA from faulting opcode */
env->spr[SPR_DSISR] |=
(ldl_code((env->nip - 4)) & 0x03FF0000) >> 16;
@ -1476,6 +1491,7 @@ void do_interrupt (CPUState *env)
}
goto store_current;
case EXCP_PROGRAM: /* 0x0700 */
idx = 6;
msr &= ~0xFFFF0000;
switch (env->error_code & ~0xF) {
case EXCP_FP:
@ -1501,6 +1517,7 @@ void do_interrupt (CPUState *env)
msr |= 0x00040000;
break;
case EXCP_TRAP:
idx = 15;
msr |= 0x00020000;
break;
default:
@ -1510,18 +1527,13 @@ void do_interrupt (CPUState *env)
msr |= 0x00010000;
goto store_current;
case EXCP_NO_FP: /* 0x0800 */
idx = 7;
msr &= ~0xFFFF0000;
goto store_current;
case EXCP_DECR:
if (msr_ee == 0) {
#if 1
/* Requeue it */
env->interrupt_request |= CPU_INTERRUPT_TIMER;
#endif
return;
}
goto store_next;
case EXCP_SYSCALL: /* 0x0C00 */
idx = 8;
/* NOTE: this is a temporary hack to support graphics OSI
calls from the MOL driver */
if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b &&
@ -1557,13 +1569,6 @@ void do_interrupt (CPUState *env)
"Instruction segment exception is not implemented yet !\n");
goto store_next;
case EXCP_HDECR: /* 0x0980 */
if (msr_ee == 0) {
#if 1
/* Requeue it */
env->interrupt_request |= CPU_INTERRUPT_TIMER;
#endif
return;
}
/* XXX: TODO */
cpu_abort(env, "Hypervisor decrementer exception is not implemented "
"yet !\n");
@ -1581,6 +1586,7 @@ void do_interrupt (CPUState *env)
}
return;
case 0x0F20:
idx = 9;
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
/* APU unavailable on 405 */
@ -1600,11 +1606,13 @@ void do_interrupt (CPUState *env)
}
return;
case 0x1000:
idx = 10;
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
/* PIT on 4xx */
/* XXX: TODO */
cpu_abort(env, "40x PIT exception is not implemented yet !\n");
msr &= ~0xFFFF0000;
if (loglevel != 0)
fprintf(logfile, "PIT exception\n");
goto store_next;
case PPC_FLAGS_EXCP_602:
case PPC_FLAGS_EXCP_603:
@ -1619,11 +1627,13 @@ void do_interrupt (CPUState *env)
}
return;
case 0x1010:
idx = 11;
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
/* FIT on 4xx */
/* XXX: TODO */
cpu_abort(env, "40x FIT exception is not implemented yet !\n");
msr &= ~0xFFFF0000;
if (loglevel != 0)
fprintf(logfile, "FIT exception\n");
goto store_next;
default:
cpu_abort(env, "Invalid exception 0x1010 !\n");
@ -1631,19 +1641,25 @@ void do_interrupt (CPUState *env)
}
return;
case 0x1020:
idx = 12;
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
/* Watchdog on 4xx */
/* XXX: TODO */
cpu_abort(env,
"40x watchdog exception is not implemented yet !\n");
msr &= ~0xFFFF0000;
if (loglevel != 0)
fprintf(logfile, "WDT exception\n");
goto store_next;
case PPC_FLAGS_EXCP_BOOKE:
srr_0 = &env->spr[SPR_BOOKE_CSRR0];
srr_1 = &env->spr[SPR_BOOKE_CSRR1];
break;
default:
cpu_abort(env, "Invalid exception 0x1020 !\n");
break;
}
return;
case 0x1100:
idx = 13;
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
/* DTLBMISS on 4xx */
@ -1662,6 +1678,7 @@ void do_interrupt (CPUState *env)
}
return;
case 0x1200:
idx = 14;
switch (PPC_EXCP(env)) {
case PPC_FLAGS_EXCP_40x:
/* ITLBMISS on 4xx */
@ -1838,6 +1855,10 @@ void do_interrupt (CPUState *env)
cpu_abort(env,
"601 run mode exception is not implemented yet !\n");
goto store_next;
case PPC_FLAGS_EXCP_BOOKE:
srr_0 = &env->spr[SPR_BOOKE_CSRR0];
srr_1 = &env->spr[SPR_BOOKE_CSRR1];
break;
default:
cpu_abort(env, "Invalid exception 0x1800 !\n");
break;
@ -1852,15 +1873,19 @@ void do_interrupt (CPUState *env)
return;
store_current:
/* save current instruction location */
*srr_0 = (env->nip - 4) & 0xFFFFFFFFULL;
*srr_0 = env->nip - 4;
break;
store_next:
/* save next instruction location */
*srr_0 = env->nip & 0xFFFFFFFFULL;
*srr_0 = env->nip;
break;
}
/* Save msr */
*srr_1 = msr;
if (asrr_0 != NULL)
*asrr_0 = *srr_0;
if (asrr_1 != NULL)
*asrr_1 = *srr_1;
/* If we disactivated any translation, flush TLBs */
if (msr_ir || msr_dr) {
tlb_flush(env, 1);
@ -1877,10 +1902,28 @@ void do_interrupt (CPUState *env)
msr_dr = 0;
msr_ri = 0;
msr_le = msr_ile;
msr_sf = msr_isf;
if (PPC_EXCP(env) == PPC_FLAGS_EXCP_BOOKE) {
msr_cm = msr_icm;
if (idx == -1 || (idx >= 16 && idx < 32)) {
cpu_abort(env, "Invalid exception index for excp %d %08x idx %d\n",
excp, excp, idx);
}
#if defined(TARGET_PPC64)
if (msr_cm)
env->nip = (uint64_t)env->spr[SPR_BOOKE_IVPR];
else
#endif
env->nip = (uint32_t)env->spr[SPR_BOOKE_IVPR];
if (idx < 16)
env->nip |= env->spr[SPR_BOOKE_IVOR0 + idx];
else if (idx < 38)
env->nip |= env->spr[SPR_BOOKE_IVOR32 + idx - 32];
} else {
msr_sf = msr_isf;
env->nip = excp;
}
do_compute_hflags(env);
/* Jump to handler */
env->nip = excp;
env->exception_index = EXCP_NONE;
}