diff options
author | j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-09-29 13:06:16 +0000 |
---|---|---|
committer | j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-09-29 13:06:16 +0000 |
commit | e1833e1f96456fd8fc17463246fe0b2050e68efb (patch) | |
tree | 5d50859e3cb0a1c2628811d7255f112a9f87cdca /target-ppc/helper.c | |
parent | f93732914e0b06539170e84f046f01ebe99980f3 (diff) |
Rework PowerPC exceptions model to make it more versatile:
* don't use exception vectors as the exception number.
Use vectors numbers as defined in the PowerPC embedded specification instead
and extend this model to cover all emulated PowerPC variants exceptions.
* add some missing exceptions definitions, from PowerPC 2.04 specification
and actual PowerPC implementations.
* add code provision for hypervisor exceptions handling.
* define exception vectors and prefix in CPUPPCState to emulate BookE exception
vectors without any hacks.
* define per CPU model valid exception vectors.
* handle all known exceptions in user-mode only emulations.
* fix hardware interrupts priorities in most cases.
* change RET_EXCP macros name into GEN_EXCP as they don't return.
* do not stop translation on most instructions that are not defined as
context-synchronizing in PowerPC specification.
* fix PowerPC 64 jump targets and link register update when in 32 bits mode.
* Fix PowerPC 464 and 464F definitions.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3261 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-ppc/helper.c')
-rw-r--r-- | target-ppc/helper.c | 993 |
1 files changed, 528 insertions, 465 deletions
diff --git a/target-ppc/helper.c b/target-ppc/helper.c index f27a7f50a5..8b6bed1e51 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -44,10 +44,10 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, int exception, error_code; if (rw == 2) { - exception = EXCP_ISI; + exception = POWERPC_EXCP_ISI; error_code = 0; } else { - exception = EXCP_DSI; + exception = POWERPC_EXCP_DSI; error_code = 0; if (rw) error_code |= 0x02000000; @@ -1128,6 +1128,7 @@ static int check_physical (CPUState *env, mmu_ctx_t *ctx, ctx->prot |= PAGE_WRITE; } } + break; case POWERPC_MMU_BOOKE: ctx->prot |= PAGE_WRITE; break; @@ -1250,20 +1251,20 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, cpu_dump_state(env, logfile, fprintf, 0); #endif if (access_type == ACCESS_CODE) { - exception = EXCP_ISI; + exception = POWERPC_EXCP_ISI; switch (ret) { case -1: /* No matches in page tables or TLB */ switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: - exception = EXCP_I_TLBMISS; + exception = POWERPC_EXCP_IFTLB; env->spr[SPR_IMISS] = address; env->spr[SPR_ICMP] = 0x80000000 | ctx.ptem; error_code = 1 << 18; goto tlb_miss; case POWERPC_MMU_SOFT_4xx: case POWERPC_MMU_SOFT_4xx_Z: - exception = EXCP_40x_ITLBMISS; + exception = POWERPC_EXCP_ITLB; error_code = 0; env->spr[SPR_40x_DEAR] = address; env->spr[SPR_40x_ESR] = 0x00000000; @@ -1315,24 +1316,26 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, /* No code fetch is allowed in direct-store areas */ error_code = 0x10000000; break; +#if defined(TARGET_PPC64) case -5: /* No match in segment table */ - exception = EXCP_ISEG; + exception = POWERPC_EXCP_ISEG; error_code = 0; break; +#endif } } else { - exception = EXCP_DSI; + exception = POWERPC_EXCP_DSI; switch (ret) { case -1: /* No matches in page tables or TLB */ switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: if (rw == 1) { - exception = EXCP_DS_TLBMISS; + exception = POWERPC_EXCP_DSTLB; error_code = 1 << 16; } else { - exception = EXCP_DL_TLBMISS; + exception = POWERPC_EXCP_DLTLB; error_code = 0; } env->spr[SPR_DMISS] = address; @@ -1345,7 +1348,7 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, goto out; case POWERPC_MMU_SOFT_4xx: case POWERPC_MMU_SOFT_4xx_Z: - exception = EXCP_40x_DTLBMISS; + exception = POWERPC_EXCP_DTLB; error_code = 0; env->spr[SPR_40x_DEAR] = address; if (rw) @@ -1396,8 +1399,8 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, switch (access_type) { case ACCESS_FLOAT: /* Floating point load/store */ - exception = EXCP_ALIGN; - error_code = EXCP_ALIGN_FP; + exception = POWERPC_EXCP_ALIGN; + error_code = POWERPC_EXCP_ALIGN_FP; break; case ACCESS_RES: /* lwarx, ldarx or srwcx. */ @@ -1409,18 +1412,20 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, break; default: printf("DSI: invalid exception (%d)\n", ret); - exception = EXCP_PROGRAM; - error_code = EXCP_INVAL | EXCP_INVAL_INVAL; + exception = POWERPC_EXCP_PROGRAM; + error_code = POWERPC_EXCP_INVAL | POWERPC_EXCP_INVAL_INVAL; break; } break; +#if defined(TARGET_PPC64) case -5: /* No match in segment table */ - exception = EXCP_DSEG; + exception = POWERPC_EXCP_DSEG; error_code = 0; break; +#endif } - if (exception == EXCP_DSI && rw == 1) + if (exception == POWERPC_EXCP_DSI && rw == 1) error_code |= 0x02000000; /* Store fault address */ env->spr[SPR_DAR] = address; @@ -1830,12 +1835,14 @@ void do_compute_hflags (CPUPPCState *env) #if defined (CONFIG_USER_ONLY) void do_interrupt (CPUState *env) { - env->exception_index = -1; + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; } void ppc_hw_interrupt (CPUState *env) { - env->exception_index = -1; + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; } #else /* defined (CONFIG_USER_ONLY) */ static void dump_syscall (CPUState *env) @@ -1846,126 +1853,122 @@ static void dump_syscall (CPUState *env) env->gpr[5], env->gpr[6], env->nip); } -void do_interrupt (CPUState *env) +/* Note that this function should be greatly optimized + * when called with a constant excp, from ppc_hw_interrupt + */ +static always_inline void powerpc_excp (CPUState *env, + int excp_model, int excp) { - target_ulong msr, *srr_0, *srr_1, *asrr_0, *asrr_1; - int excp, idx; + target_ulong msr, vector; + int srr0, srr1, asrr0, asrr1; - 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) { - fprintf(logfile, - "Raise exception at 0x" ADDRX " => 0x%08x (%02x)\n", - env->nip, excp, env->error_code); - cpu_dump_state(env, logfile, fprintf, 0); - } - } -#endif if (loglevel & CPU_LOG_INT) { fprintf(logfile, "Raise exception at 0x" ADDRX " => 0x%08x (%02x)\n", env->nip, excp, env->error_code); } - msr_pow = 0; - idx = -1; - /* Generate informations in save/restore registers */ + msr = do_load_msr(env); + srr0 = SPR_SRR0; + srr1 = SPR_SRR1; + asrr0 = -1; + asrr1 = -1; + msr &= ~((target_ulong)0x783F0000); switch (excp) { - /* Generic PowerPC exceptions */ - case EXCP_RESET: /* 0x0100 */ - switch (env->excp_model) { + case POWERPC_EXCP_NONE: + /* Should never happen */ + return; + case POWERPC_EXCP_CRITICAL: /* Critical input */ + msr_ri = 0; /* XXX: check this */ + switch (excp_model) { case POWERPC_EXCP_40x: - srr_0 = &env->spr[SPR_40x_SRR2]; - srr_1 = &env->spr[SPR_40x_SRR3]; + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; break; case POWERPC_EXCP_BOOKE: - idx = 0; - srr_0 = &env->spr[SPR_BOOKE_CSRR0]; - srr_1 = &env->spr[SPR_BOOKE_CSRR1]; + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; break; - default: - if (msr_ip) - excp += 0xFFC00; - excp |= 0xFFC00000; + case POWERPC_EXCP_G2: break; + default: + goto excp_invalid; } goto store_next; - case EXCP_MACHINE_CHECK: /* 0x0200 */ - switch (env->excp_model) { + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + if (msr_me == 0) { + /* Machine check exception is not enabled */ + /* XXX: we may just stop the processor here, to allow debugging */ + excp = POWERPC_EXCP_RESET; + goto excp_reset; + } + msr_ri = 0; + msr_me = 0; +#if defined(TARGET_PPC64H) + msr_hv = 1; +#endif + /* XXX: should also have something loaded in DAR / DSISR */ + switch (excp_model) { case POWERPC_EXCP_40x: - srr_0 = &env->spr[SPR_40x_SRR2]; - srr_1 = &env->spr[SPR_40x_SRR3]; + srr0 = SPR_40x_SRR2; + srr1 = SPR_40x_SRR3; break; case POWERPC_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; + srr0 = SPR_BOOKE_MCSRR0; + srr1 = SPR_BOOKE_MCSRR1; + asrr0 = SPR_BOOKE_CSRR0; + asrr1 = SPR_BOOKE_CSRR1; break; default: break; } - msr_me = 0; - break; - case EXCP_DSI: /* 0x0300 */ - /* Store exception cause */ - /* data location address has been stored - * when the fault has been detected - */ - idx = 2; - msr &= ~0xFFFF0000; + goto store_next; + case POWERPC_EXCP_DSI: /* Data storage exception */ #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) { fprintf(logfile, "DSI exception: DSISR=0x" ADDRX" DAR=0x" ADDRX "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); } #endif + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif goto store_next; - case EXCP_ISI: /* 0x0400 */ - /* Store exception cause */ - idx = 3; - msr &= ~0xFFFF0000; - msr |= env->error_code; + case POWERPC_EXCP_ISI: /* Instruction storage exception */ #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) { fprintf(logfile, "ISI exception: msr=0x" ADDRX ", nip=0x" ADDRX "\n", msr, env->nip); } #endif + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + msr |= env->error_code; goto store_next; - case EXCP_EXTERNAL: /* 0x0500 */ - idx = 4; + case POWERPC_EXCP_EXTERNAL: /* External input */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes0 == 1) + msr_hv = 1; +#endif goto store_next; - case EXCP_ALIGN: /* 0x0600 */ - if (likely(env->excp_model != POWERPC_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; - /* data location address has been stored - * when the fault has been detected - */ - } else { - /* IO error exception on PowerPC 601 */ - /* XXX: TODO */ - cpu_abort(env, - "601 IO error exception is not implemented yet !\n"); - } + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + /* XXX: this is false */ + /* Get rS/rD and rA from faulting opcode */ + env->spr[SPR_DSISR] |= (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16; goto store_current; - case EXCP_PROGRAM: /* 0x0700 */ - idx = 6; - msr &= ~0xFFFF0000; + case POWERPC_EXCP_PROGRAM: /* Program exception */ switch (env->error_code & ~0xF) { - case EXCP_FP: - if (msr_fe0 == 0 && msr_fe1 == 0) { + case POWERPC_EXCP_FP: + if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) { #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) { fprintf(logfile, "Ignore floating point exception\n"); @@ -1973,6 +1976,11 @@ void do_interrupt (CPUState *env) #endif return; } + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif msr |= 0x00100000; /* Set FX */ env->fpscr[7] |= 0x8; @@ -1980,39 +1988,59 @@ void do_interrupt (CPUState *env) if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) env->fpscr[7] |= 0x4; + if (msr_fe0 != msr_fe1) { + msr |= 0x00010000; + goto store_current; + } break; - case EXCP_INVAL: + case POWERPC_EXCP_INVAL: #if defined (DEBUG_EXCEPTIONS) if (loglevel != 0) { fprintf(logfile, "Invalid instruction at 0x" ADDRX "\n", env->nip); } #endif + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif msr |= 0x00080000; break; - case EXCP_PRIV: + case POWERPC_EXCP_PRIV: + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif msr |= 0x00040000; break; - case EXCP_TRAP: - idx = 15; + case POWERPC_EXCP_TRAP: + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif msr |= 0x00020000; break; default: /* Should never occur */ + cpu_abort(env, "Invalid program exception %d. Aborting\n", + env->error_code); break; } - msr |= 0x00010000; - goto store_current; - case EXCP_NO_FP: /* 0x0800 */ - idx = 7; - msr &= ~0xFFFF0000; - goto store_current; - case EXCP_DECR: goto store_next; - case EXCP_SYSCALL: /* 0x0C00 */ - idx = 8; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + goto store_current; + case POWERPC_EXCP_SYSCALL: /* System call exception */ /* NOTE: this is a temporary hack to support graphics OSI calls from the MOL driver */ + /* XXX: To be removed */ if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b && env->osi_call) { if (env->osi_call(env) != 0) @@ -2021,166 +2049,254 @@ void do_interrupt (CPUState *env) if (loglevel & CPU_LOG_INT) { dump_syscall(env); } + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lev == 1 || (lpes0 == 0 && lpes1 == 0)) + msr_hv = 1; +#endif + goto store_next; + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + msr_ri = 0; + goto store_current; + case POWERPC_EXCP_DECR: /* Decrementer exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + goto store_next; + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + /* FIT on 4xx */ +#if defined (DEBUG_EXCEPTIONS) + if (loglevel != 0) + fprintf(logfile, "FIT exception\n"); +#endif + msr_ri = 0; /* XXX: check this */ goto store_next; - case EXCP_TRACE: /* 0x0D00 */ + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ +#if defined (DEBUG_EXCEPTIONS) + if (loglevel != 0) + fprintf(logfile, "WDT exception\n"); +#endif + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } + msr_ri = 0; /* XXX: check this */ goto store_next; - case EXCP_PERF: /* 0x0F00 */ + case POWERPC_EXCP_DTLB: /* Data TLB error */ + msr_ri = 0; /* XXX: check this */ + goto store_next; + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + msr_ri = 0; /* XXX: check this */ + goto store_next; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_DSRR0; + srr1 = SPR_BOOKE_DSRR1; + asrr0 = SPR_BOOKE_CSRR0; + asrr1 = SPR_BOOKE_CSRR1; + break; + default: + break; + } /* XXX: TODO */ - cpu_abort(env, - "Performance counter exception is not implemented yet !\n"); + cpu_abort(env, "Debug exception is not implemented yet !\n"); goto store_next; - /* 32 bits PowerPC specific exceptions */ - case EXCP_FP_ASSIST: /* 0x0E00 */ +#if defined(TARGET_PPCEMB) + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ + msr_ri = 0; /* XXX: check this */ + goto store_current; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ /* XXX: TODO */ - cpu_abort(env, "Floating point assist exception " + cpu_abort(env, "Embedded floating point data exception " "is not implemented yet !\n"); goto store_next; - /* 64 bits PowerPC exceptions */ - case EXCP_DSEG: /* 0x0380 */ + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ /* XXX: TODO */ - cpu_abort(env, "Data segment exception is not implemented yet !\n"); + cpu_abort(env, "Embedded floating point round exception " + "is not implemented yet !\n"); goto store_next; - case EXCP_ISEG: /* 0x0480 */ + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ + msr_ri = 0; /* XXX: TODO */ cpu_abort(env, - "Instruction segment exception is not implemented yet !\n"); + "Performance counter exception is not implemented yet !\n"); goto store_next; - case EXCP_HDECR: /* 0x0980 */ + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ /* XXX: TODO */ - cpu_abort(env, "Hypervisor decrementer exception is not implemented " - "yet !\n"); + cpu_abort(env, + "Embedded doorbell interrupt is not implemented yet !\n"); goto store_next; - /* Implementation specific exceptions */ - case 0x0A00: - switch (env->excp_model) { - case POWERPC_EXCP_G2: - /* Critical interrupt on G2 */ - /* XXX: TODO */ - cpu_abort(env, "G2 critical interrupt is not implemented yet !\n"); - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x0A00 !\n"); + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + switch (excp_model) { + case POWERPC_EXCP_BOOKE: + srr0 = SPR_BOOKE_CSRR0; + srr1 = SPR_BOOKE_CSRR1; break; - } - return; - case 0x0F20: - idx = 9; - switch (env->excp_model) { - case POWERPC_EXCP_40x: - /* APU unavailable on 405 */ - /* XXX: TODO */ - cpu_abort(env, - "APU unavailable exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_74xx: - /* Altivec unavailable */ - /* XXX: TODO */ - cpu_abort(env, "Altivec unavailable exception " - "is not implemented yet !\n"); - goto store_next; default: - cpu_abort(env, "Invalid exception 0x0F20 !\n"); break; } - return; - case 0x1000: - idx = 10; - switch (env->excp_model) { - case POWERPC_EXCP_40x: - /* PIT on 4xx */ - msr &= ~0xFFFF0000; + /* XXX: TODO */ + cpu_abort(env, "Embedded doorbell critical interrupt " + "is not implemented yet !\n"); + goto store_next; +#endif /* defined(TARGET_PPCEMB) */ + case POWERPC_EXCP_RESET: /* System reset exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + msr_hv = 1; +#endif + excp_reset: + goto store_next; +#if defined(TARGET_PPC64) + case POWERPC_EXCP_DSEG: /* Data segment exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + /* XXX: TODO */ + cpu_abort(env, "Data segment exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + /* XXX: TODO */ + cpu_abort(env, + "Instruction segment exception is not implemented yet !\n"); + goto store_next; +#endif /* defined(TARGET_PPC64) */ +#if defined(TARGET_PPC64H) + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSSR1; + msr_hv = 1; + goto store_next; +#endif + case POWERPC_EXCP_TRACE: /* Trace exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + goto store_next; +#if defined(TARGET_PPC64H) + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSSR1; + msr_hv = 1; + goto store_next; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSSR1; + msr_hv = 1; + /* XXX: TODO */ + cpu_abort(env, "Hypervisor instruction storage exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSSR1; + msr_hv = 1; + goto store_next; + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + srr0 = SPR_HSRR0; + srr1 = SPR_HSSR1; + msr_hv = 1; + goto store_next; +#endif /* defined(TARGET_PPC64H) */ + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + goto store_current; + case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ #if defined (DEBUG_EXCEPTIONS) - if (loglevel != 0) - fprintf(logfile, "PIT exception\n"); + if (loglevel != 0) + fprintf(logfile, "PIT exception\n"); +#endif + msr_ri = 0; /* XXX: check this */ + goto store_next; + case POWERPC_EXCP_IO: /* IO error exception */ + /* XXX: TODO */ + cpu_abort(env, "601 IO error exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_RUNM: /* Run mode exception */ + /* XXX: TODO */ + cpu_abort(env, "601 run mode exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + /* XXX: TODO */ + cpu_abort(env, "602 emulation trap exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + msr_ri = 0; /* XXX: check this */ +#if defined(TARGET_PPC64H) /* XXX: check this */ + if (lpes1 == 0) + msr_hv = 1; #endif - goto store_next; + switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: case POWERPC_EXCP_603E: case POWERPC_EXCP_G2: - /* ITLBMISS on 602/603 */ - goto store_gprs; + goto tlb_miss_tgpr; case POWERPC_EXCP_7x5: - /* ITLBMISS on 745/755 */ goto tlb_miss; default: - cpu_abort(env, "Invalid exception 0x1000 !\n"); - break; - } - return; - case 0x1010: - idx = 11; - switch (env->excp_model) { - case POWERPC_EXCP_40x: - /* FIT on 4xx */ - msr &= ~0xFFFF0000; -#if defined (DEBUG_EXCEPTIONS) - if (loglevel != 0) - fprintf(logfile, "FIT exception\n"); -#endif - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1010 !\n"); + cpu_abort(env, "Invalid instruction TLB miss exception\n"); break; } - return; - case 0x1020: - idx = 12; - switch (env->excp_model) { - case POWERPC_EXCP_40x: - /* Watchdog on 4xx */ - msr &= ~0xFFFF0000; -#if defined (DEBUG_EXCEPTIONS) - if (loglevel != 0) - fprintf(logfile, "WDT exception\n"); + break; + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + msr_ri = 0; /* XXX: check this */ +#if defined(TARGET_PPC64H) /* XXX: check this */ + if (lpes1 == 0) + msr_hv = 1; #endif - goto store_next; - case POWERPC_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 (env->excp_model) { - case POWERPC_EXCP_40x: - /* DTLBMISS on 4xx */ - msr &= ~0xFFFF0000; - goto store_next; + switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: case POWERPC_EXCP_603E: case POWERPC_EXCP_G2: - /* DLTLBMISS on 602/603 */ - goto store_gprs; + goto tlb_miss_tgpr; case POWERPC_EXCP_7x5: - /* DLTLBMISS on 745/755 */ goto tlb_miss; default: - cpu_abort(env, "Invalid exception 0x1100 !\n"); + cpu_abort(env, "Invalid data load TLB miss exception\n"); break; } - return; - case 0x1200: - idx = 14; - switch (env->excp_model) { - case POWERPC_EXCP_40x: - /* ITLBMISS on 4xx */ - msr &= ~0xFFFF0000; - goto store_next; + break; + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + msr_ri = 0; /* XXX: check this */ +#if defined(TARGET_PPC64H) /* XXX: check this */ + if (lpes1 == 0) + msr_hv = 1; +#endif + switch (excp_model) { case POWERPC_EXCP_602: case POWERPC_EXCP_603: case POWERPC_EXCP_603E: case POWERPC_EXCP_G2: - /* DSTLBMISS on 602/603 */ - store_gprs: + tlb_miss_tgpr: /* Swap temporary saved registers with GPRs */ swap_gpr_tgpr(env); msr_tgpr = 1; + goto tlb_miss; + case POWERPC_EXCP_7x5: + tlb_miss: #if defined (DEBUG_SOFTWARE_TLB) if (loglevel != 0) { const unsigned char *es; @@ -2207,183 +2323,81 @@ void do_interrupt (CPUState *env) env->error_code); } #endif - goto tlb_miss; - case POWERPC_EXCP_7x5: - /* DSTLBMISS on 745/755 */ - tlb_miss: - msr &= ~0xF83F0000; msr |= env->crf[0] << 28; msr |= env->error_code; /* key, D/I, S/L bits */ /* Set way using a LRU mechanism */ msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17; - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1200 !\n"); - break; - } - return; - case 0x1300: - switch (env->excp_model) { - case POWERPC_EXCP_601: - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_603E: - case POWERPC_EXCP_G2: - case POWERPC_EXCP_604: - case POWERPC_EXCP_7x0: - case POWERPC_EXCP_7x5: - /* IABR on 6xx/7xx */ - /* XXX: TODO */ - cpu_abort(env, "IABR exception is not implemented yet !\n"); - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1300 !\n"); - break; - } - return; - case 0x1400: - switch (env->excp_model) { - case POWERPC_EXCP_601: - case POWERPC_EXCP_602: - case POWERPC_EXCP_603: - case POWERPC_EXCP_603E: - case POWERPC_EXCP_G2: - case POWERPC_EXCP_604: - case POWERPC_EXCP_7x0: - case POWERPC_EXCP_7x5: - /* SMI on 6xx/7xx */ - /* XXX: TODO */ - cpu_abort(env, "SMI exception is not implemented yet !\n"); - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1400 !\n"); - break; - } - return; - case 0x1500: - switch (env->excp_model) { - case POWERPC_EXCP_602: - /* Watchdog on 602 */ - /* XXX: TODO */ - cpu_abort(env, - "602 watchdog exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_970: - /* Soft patch exception on 970 */ - /* XXX: TODO */ - cpu_abort(env, - "970 soft-patch exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_74xx: - /* VPU assist on 74xx */ - /* XXX: TODO */ - cpu_abort(env, "VPU assist exception is not implemented yet !\n"); - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1500 !\n"); - break; - } - return; - case 0x1600: - switch (env->excp_model) { - case POWERPC_EXCP_602: - /* Emulation trap on 602 */ - /* XXX: TODO */ - cpu_abort(env, "602 emulation trap exception " - "is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_970: - /* Maintenance exception on 970 */ - /* XXX: TODO */ - cpu_abort(env, - "970 maintenance exception is not implemented yet !\n"); - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1600 !\n"); - break; - } - return; - case 0x1700: - switch (env->excp_model) { - case POWERPC_EXCP_7x0: - case POWERPC_EXCP_7x5: - /* Thermal management interrupt on G3 */ - /* XXX: TODO */ - cpu_abort(env, "G3 thermal management exception " - "is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_970: - /* VPU assist on 970 */ - /* XXX: TODO */ - cpu_abort(env, - "970 VPU assist exception is not implemented yet !\n"); - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1700 !\n"); - break; - } - return; - case 0x1800: - switch (env->excp_model) { - case POWERPC_EXCP_970: - /* Thermal exception on 970 */ - /* XXX: TODO */ - cpu_abort(env, "970 thermal management exception " - "is not implemented yet !\n"); - goto store_next; - default: - cpu_abort(env, "Invalid exception 0x1800 !\n"); - break; - } - return; - case 0x2000: - switch (env->excp_model) { - case POWERPC_EXCP_40x: - /* DEBUG on 4xx */ - /* XXX: TODO */ - cpu_abort(env, "40x debug exception is not implemented yet !\n"); - goto store_next; - case POWERPC_EXCP_601: - /* Run mode exception on 601 */ - /* XXX: TODO */ - cpu_abort(env, - "601 run mode exception is not implemented yet !\n"); - goto store_next; - case POWERPC_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"); + cpu_abort(env, "Invalid data store TLB miss exception\n"); break; } - return; - /* Other exceptions */ - /* Qemu internal exceptions: - * we should never come here with those values: abort execution - */ + goto store_next; + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + /* XXX: TODO */ + cpu_abort(env, "Floating point assist exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + /* XXX: TODO */ + cpu_abort(env, "IABR exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_SMI: /* System management interrupt */ + /* XXX: TODO */ + cpu_abort(env, "SMI exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + /* XXX: TODO */ + cpu_abort(env, "Thermal management exception " + "is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ + msr_ri = 0; +#if defined(TARGET_PPC64H) + if (lpes1 == 0) + msr_hv = 1; +#endif + /* XXX: TODO */ + cpu_abort(env, + "Performance counter exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + /* XXX: TODO */ + cpu_abort(env, "VPU assist exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + /* XXX: TODO */ + cpu_abort(env, + "970 soft-patch exception is not implemented yet !\n"); + goto store_next; + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + /* XXX: TODO */ + cpu_abort(env, + "970 maintenance exception is not implemented yet !\n"); + goto store_next; default: - cpu_abort(env, "Invalid exception: code %d (%04x)\n", excp, excp); - return; + excp_invalid: + cpu_abort(env, "Invalid PowerPC exception %d. Aborting\n", excp); + break; store_current: /* save current instruction location */ - *srr_0 = env->nip - 4; + env->spr[srr0] = env->nip - 4; break; store_next: /* save next instruction location */ - *srr_0 = env->nip; + env->spr[srr0] = env->nip; break; } - /* Save msr */ - *srr_1 = msr; - if (asrr_0 != NULL) - *asrr_0 = *srr_0; - if (asrr_1 != NULL) - *asrr_1 = *srr_1; + /* Save MSR */ + env->spr[srr1] = msr; + /* If any alternate SRR register are defined, duplicate saved values */ + if (asrr0 != -1) + env->spr[asrr0] = env->spr[srr0]; + if (asrr1 != -1) + env->spr[asrr1] = env->spr[srr1]; /* If we disactivated any translation, flush TLBs */ - if (msr_ir || msr_dr) { + if (msr_ir || msr_dr) tlb_flush(env, 1); - } /* reload MSR with correct bits */ msr_ee = 0; msr_pr = 0; @@ -2394,37 +2408,42 @@ void do_interrupt (CPUState *env) msr_fe1 = 0; msr_ir = 0; msr_dr = 0; - msr_ri = 0; +#if 0 /* Fix this: not on all targets */ + msr_pmm = 0; +#endif msr_le = msr_ile; - if (env->excp_model == POWERPC_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); - } + do_compute_hflags(env); + /* Jump to handler */ + vector = env->excp_vectors[excp]; + if (vector == (target_ulong)-1) { + cpu_abort(env, "Raised an exception without defined vector %d\n", + excp); + } + vector |= env->excp_prefix; #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]; + if (excp_model == POWERPC_EXCP_BOOKE) { + msr_cm = msr_icm; + if (!msr_cm) + vector = (uint32_t)vector; } else { msr_sf = msr_isf; - env->nip = excp; + if (!msr_sf) + vector = (uint32_t)vector; } - do_compute_hflags(env); - /* Jump to handler */ - env->exception_index = EXCP_NONE; +#endif + env->nip = vector; + /* Reset exception state */ + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; } -void ppc_hw_interrupt (CPUPPCState *env) +void do_interrupt (CPUState *env) { - int raised = 0; + powerpc_excp(env, env->excp_model, env->exception_index); +} +void ppc_hw_interrupt (CPUPPCState *env) +{ #if 1 if (loglevel & CPU_LOG_INT) { fprintf(logfile, "%s: %p pending %08x req %08x me %d ee %d\n", @@ -2432,82 +2451,125 @@ void ppc_hw_interrupt (CPUPPCState *env) env->interrupt_request, msr_me, msr_ee); } #endif - /* Raise it */ + /* External reset */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { - /* External reset / critical input */ - /* XXX: critical input should be handled another way. - * This code is not correct ! - */ - env->exception_index = EXCP_RESET; env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); - raised = 1; - } - if (raised == 0 && msr_me != 0) { - /* Machine check exception */ - if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { - env->exception_index = EXCP_MACHINE_CHECK; - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); - raised = 1; - } + powerpc_excp(env, env->excp_model, POWERPC_EXCP_RESET); + return; + } + /* Machine check exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_MCHECK); + return; } - if (raised == 0 && msr_ee != 0) { -#if defined(TARGET_PPC64H) /* PowerPC 64 with hypervisor mode support */ +#if 0 /* TODO */ + /* External debug exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DEBUG); + return; + } +#endif +#if defined(TARGET_PPC64H) + if ((msr_ee != 0 || msr_hv == 0 || msr_pr == 1) & hdice != 0) { /* Hypervisor decrementer exception */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { - env->exception_index = EXCP_HDECR; env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); - raised = 1; - } else + powerpc_excp(env, env->excp_model, POWERPC_EXCP_HDECR); + return; + } + } +#endif + if (msr_ce != 0) { + /* External critical interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { + /* Taking a critical external interrupt does not clear the external + * critical interrupt status + */ +#if 0 + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT); #endif + powerpc_excp(env, env->excp_model, POWERPC_EXCP_CRITICAL); + return; + } + } + if (msr_ee != 0) { + /* Watchdog timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_WDT); + return; + } +#if defined(TARGET_PPCEMB) + if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORCI); + return; + } +#endif +#if defined(TARGET_PPCEMB) + /* External interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { + /* Taking an external interrupt does not clear the external + * interrupt status + */ +#if 0 + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); +#endif + powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); + return; + } +#endif + /* Fixed interval timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_FIT); + return; + } + /* Programmable interval timer on embedded PowerPC */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_PIT); + return; + } /* Decrementer exception */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { - env->exception_index = EXCP_DECR; env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); - raised = 1; - /* Programmable interval timer on embedded PowerPC */ - } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { - env->exception_index = EXCP_40x_PIT; - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); - raised = 1; - /* Fixed interval timer on embedded PowerPC */ - } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { - env->exception_index = EXCP_40x_FIT; - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); - raised = 1; - /* Watchdog timer on embedded PowerPC */ - } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { - env->exception_index = EXCP_40x_WATCHDOG; - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); - raised = 1; + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DECR); + return; + } +#if !defined(TARGET_PPCEMB) /* External interrupt */ - } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { - env->exception_index = EXCP_EXTERNAL; + if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { /* Taking an external interrupt does not clear the external * interrupt status */ #if 0 env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); #endif - raised = 1; -#if 0 // TODO - /* Thermal interrupt */ - } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { - env->exception_index = EXCP_970_THRM; - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); - raised = 1; + powerpc_excp(env, env->excp_model, POWERPC_EXCP_EXTERNAL); + return; + } #endif +#if defined(TARGET_PPCEMB) + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_DOORI); + return; } -#if 0 // TODO - /* External debug exception */ - } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { - env->exception_index = EXCP_xxx; - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); - raised = 1; #endif - } - if (raised != 0) { - env->error_code = 0; - do_interrupt(env); + if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_PERFM); + return; + } + /* Thermal interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) { + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM); + powerpc_excp(env, env->excp_model, POWERPC_EXCP_THERM); + return; + } } } #endif /* !CONFIG_USER_ONLY */ @@ -2572,7 +2634,8 @@ void cpu_ppc_reset (void *opaque) env->reserve = -1; /* Be sure no exception or interrupt is pending */ env->pending_interrupts = 0; - env->exception_index = EXCP_NONE; + env->exception_index = POWERPC_EXCP_NONE; + env->error_code = 0; /* Flush all TLBs */ tlb_flush(env, 1); } |