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 /linux-user | |
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 'linux-user')
-rw-r--r-- | linux-user/main.c | 458 |
1 files changed, 279 insertions, 179 deletions
diff --git a/linux-user/main.c b/linux-user/main.c index b70c070c3f..fb424ad887 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -723,6 +723,16 @@ int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, target_ulong val) return -1; } +#define EXCP_DUMP(env, fmt, args...) \ +do { \ + fprintf(stderr, fmt , ##args); \ + cpu_dump_state(env, stderr, fprintf, 0); \ + if (loglevel != 0) { \ + fprintf(logfile, fmt , ##args); \ + cpu_dump_state(env, logfile, fprintf, 0); \ + } \ +} while (0) + void cpu_loop(CPUPPCState *env) { target_siginfo_t info; @@ -731,60 +741,22 @@ void cpu_loop(CPUPPCState *env) for(;;) { trapnr = cpu_ppc_exec(env); - if (trapnr != EXCP_SYSCALL_USER && trapnr != EXCP_BRANCH && - trapnr != EXCP_TRACE) { - if (loglevel > 0) { - cpu_dump_state(env, logfile, fprintf, 0); - } - } switch(trapnr) { - case EXCP_NONE: + case POWERPC_EXCP_NONE: + /* Just go on */ break; - case EXCP_SYSCALL_USER: - /* system call */ - /* WARNING: - * PPC ABI uses overflow flag in cr0 to signal an error - * in syscalls. - */ -#if 0 - printf("syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n", env->gpr[0], - env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6]); -#endif - env->crf[0] &= ~0x1; - ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], - env->gpr[5], env->gpr[6], env->gpr[7], - env->gpr[8]); - if (ret > (uint32_t)(-515)) { - env->crf[0] |= 0x1; - ret = -ret; - } - env->gpr[3] = ret; -#if 0 - printf("syscall returned 0x%08x (%d)\n", ret, ret); -#endif + case POWERPC_EXCP_CRITICAL: /* Critical input */ + cpu_abort(env, "Critical interrupt while in user mode. " + "Aborting\n"); break; - case EXCP_RESET: - /* Should not happen ! */ - fprintf(stderr, "RESET asked... Stop emulation\n"); - if (loglevel) - fprintf(logfile, "RESET asked... Stop emulation\n"); - abort(); - case EXCP_MACHINE_CHECK: - fprintf(stderr, "Machine check exeption... Stop emulation\n"); - if (loglevel) - fprintf(logfile, "Machine check exception. Stop emulation\n"); - info.si_signo = TARGET_SIGBUS; - info.si_errno = 0; - info.si_code = TARGET_BUS_OBJERR; - info._sifields._sigfault._addr = env->nip - 4; - queue_signal(info.si_signo, &info); - case EXCP_DSI: - fprintf(stderr, "Invalid data memory access: 0x" ADDRX "\n", - env->spr[SPR_DAR]); - if (loglevel) { - fprintf(logfile, "Invalid data memory access: 0x" ADDRX "\n", - env->spr[SPR_DAR]); - } + case POWERPC_EXCP_MCHECK: /* Machine check exception */ + cpu_abort(env, "Machine check exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DSI: /* Data storage exception */ + EXCP_DUMP(env, "Invalid data memory access: 0x" ADDRX "\n", + env->spr[SPR_DAR]); + /* XXX: check this. Seems bugged */ switch (env->error_code & 0xFF000000) { case 0x40000000: info.si_signo = TARGET_SIGSEGV; @@ -803,12 +775,8 @@ void cpu_loop(CPUPPCState *env) break; default: /* Let's send a regular segfault... */ - fprintf(stderr, "Invalid segfault errno (%02x)\n", - env->error_code); - if (loglevel) { - fprintf(logfile, "Invalid segfault errno (%02x)\n", - env->error_code); - } + EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", + env->error_code); info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; @@ -817,10 +785,10 @@ void cpu_loop(CPUPPCState *env) info._sifields._sigfault._addr = env->nip; queue_signal(info.si_signo, &info); break; - case EXCP_ISI: - fprintf(stderr, "Invalid instruction fetch\n"); - if (loglevel) - fprintf(logfile, "Invalid instruction fetch\n"); + case POWERPC_EXCP_ISI: /* Instruction storage exception */ + EXCP_DUMP(env, "Invalid instruction fetch: 0x\n" ADDRX "\n", + env->spr[SPR_DAR]); + /* XXX: check this */ switch (env->error_code & 0xFF000000) { case 0x40000000: info.si_signo = TARGET_SIGSEGV; @@ -835,12 +803,8 @@ void cpu_loop(CPUPPCState *env) break; default: /* Let's send a regular segfault... */ - fprintf(stderr, "Invalid segfault errno (%02x)\n", - env->error_code); - if (loglevel) { - fprintf(logfile, "Invalid segfault errno (%02x)\n", - env->error_code); - } + EXCP_DUMP(env, "Invalid segfault errno (%02x)\n", + env->error_code); info.si_signo = TARGET_SIGSEGV; info.si_errno = 0; info.si_code = TARGET_SEGV_MAPERR; @@ -849,28 +813,24 @@ void cpu_loop(CPUPPCState *env) info._sifields._sigfault._addr = env->nip - 4; queue_signal(info.si_signo, &info); break; - case EXCP_EXTERNAL: - /* Should not happen ! */ - fprintf(stderr, "External interruption... Stop emulation\n"); - if (loglevel) - fprintf(logfile, "External interruption... Stop emulation\n"); - abort(); - case EXCP_ALIGN: - fprintf(stderr, "Invalid unaligned memory access\n"); - if (loglevel) - fprintf(logfile, "Invalid unaligned memory access\n"); + case POWERPC_EXCP_EXTERNAL: /* External input */ + cpu_abort(env, "External interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ALIGN: /* Alignment exception */ + EXCP_DUMP(env, "Unaligned memory access\n"); + /* XXX: check this */ info.si_signo = TARGET_SIGBUS; info.si_errno = 0; info.si_code = TARGET_BUS_ADRALN; info._sifields._sigfault._addr = env->nip - 4; queue_signal(info.si_signo, &info); break; - case EXCP_PROGRAM: + case POWERPC_EXCP_PROGRAM: /* Program exception */ + /* XXX: check this */ switch (env->error_code & ~0xF) { - case EXCP_FP: - fprintf(stderr, "Program exception\n"); - if (loglevel) - fprintf(logfile, "Program exception\n"); + case POWERPC_EXCP_FP: + EXCP_DUMP(env, "Floating point program exception\n"); /* Set FX */ env->fpscr[7] |= 0x8; /* Finally, update FEX */ @@ -880,155 +840,138 @@ void cpu_loop(CPUPPCState *env) info.si_signo = TARGET_SIGFPE; info.si_errno = 0; switch (env->error_code & 0xF) { - case EXCP_FP_OX: + case POWERPC_EXCP_FP_OX: info.si_code = TARGET_FPE_FLTOVF; break; - case EXCP_FP_UX: + case POWERPC_EXCP_FP_UX: info.si_code = TARGET_FPE_FLTUND; break; - case EXCP_FP_ZX: - case EXCP_FP_VXZDZ: + case POWERPC_EXCP_FP_ZX: + case POWERPC_EXCP_FP_VXZDZ: info.si_code = TARGET_FPE_FLTDIV; break; - case EXCP_FP_XX: + case POWERPC_EXCP_FP_XX: info.si_code = TARGET_FPE_FLTRES; break; - case EXCP_FP_VXSOFT: + case POWERPC_EXCP_FP_VXSOFT: info.si_code = TARGET_FPE_FLTINV; break; - case EXCP_FP_VXNAN: - case EXCP_FP_VXISI: - case EXCP_FP_VXIDI: - case EXCP_FP_VXIMZ: - case EXCP_FP_VXVC: - case EXCP_FP_VXSQRT: - case EXCP_FP_VXCVI: + case POWERPC_EXCP_FP_VXNAN: + case POWERPC_EXCP_FP_VXISI: + case POWERPC_EXCP_FP_VXIDI: + case POWERPC_EXCP_FP_VXIMZ: + case POWERPC_EXCP_FP_VXVC: + case POWERPC_EXCP_FP_VXSQRT: + case POWERPC_EXCP_FP_VXCVI: info.si_code = TARGET_FPE_FLTSUB; break; default: - fprintf(stderr, "Unknown floating point exception " - "(%02x)\n", env->error_code); - if (loglevel) { - fprintf(logfile, "Unknown floating point exception " - "(%02x)\n", env->error_code & 0xF); - } + EXCP_DUMP(env, "Unknown floating point exception (%02x)\n", + env->error_code); + break; } - break; - case EXCP_INVAL: - fprintf(stderr, "Invalid instruction\n"); - if (loglevel) - fprintf(logfile, "Invalid instruction\n"); + break; + case POWERPC_EXCP_INVAL: + EXCP_DUMP(env, "Invalid instruction\n"); info.si_signo = TARGET_SIGILL; info.si_errno = 0; switch (env->error_code & 0xF) { - case EXCP_INVAL_INVAL: + case POWERPC_EXCP_INVAL_INVAL: info.si_code = TARGET_ILL_ILLOPC; break; - case EXCP_INVAL_LSWX: + case POWERPC_EXCP_INVAL_LSWX: info.si_code = TARGET_ILL_ILLOPN; break; - case EXCP_INVAL_SPR: + case POWERPC_EXCP_INVAL_SPR: info.si_code = TARGET_ILL_PRVREG; break; - case EXCP_INVAL_FP: + case POWERPC_EXCP_INVAL_FP: info.si_code = TARGET_ILL_COPROC; break; default: - fprintf(stderr, "Unknown invalid operation (%02x)\n", - env->error_code & 0xF); - if (loglevel) { - fprintf(logfile, "Unknown invalid operation (%02x)\n", - env->error_code & 0xF); - } + EXCP_DUMP(env, "Unknown invalid operation (%02x)\n", + env->error_code & 0xF); info.si_code = TARGET_ILL_ILLADR; break; } break; - case EXCP_PRIV: - fprintf(stderr, "Privilege violation\n"); - if (loglevel) - fprintf(logfile, "Privilege violation\n"); + case POWERPC_EXCP_PRIV: + EXCP_DUMP(env, "Privilege violation\n"); info.si_signo = TARGET_SIGILL; info.si_errno = 0; switch (env->error_code & 0xF) { - case EXCP_PRIV_OPC: + case POWERPC_EXCP_PRIV_OPC: info.si_code = TARGET_ILL_PRVOPC; break; - case EXCP_PRIV_REG: + case POWERPC_EXCP_PRIV_REG: info.si_code = TARGET_ILL_PRVREG; - break; + break; default: - fprintf(stderr, "Unknown privilege violation (%02x)\n", - env->error_code & 0xF); + EXCP_DUMP(env, "Unknown privilege violation (%02x)\n", + env->error_code & 0xF); info.si_code = TARGET_ILL_PRVOPC; break; } break; - case EXCP_TRAP: - fprintf(stderr, "Tried to call a TRAP\n"); - if (loglevel) - fprintf(logfile, "Tried to call a TRAP\n"); - abort(); + case POWERPC_EXCP_TRAP: + cpu_abort(env, "Tried to call a TRAP\n"); + break; default: /* Should not happen ! */ - fprintf(stderr, "Unknown program exception (%02x)\n", - env->error_code); - if (loglevel) { - fprintf(logfile, "Unknwon program exception (%02x)\n", - env->error_code); - } - abort(); + cpu_abort(env, "Unknown program exception (%02x)\n", + env->error_code); + break; } info._sifields._sigfault._addr = env->nip - 4; queue_signal(info.si_signo, &info); break; - case EXCP_NO_FP: - fprintf(stderr, "No floating point allowed\n"); - if (loglevel) - fprintf(logfile, "No floating point allowed\n"); + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ + EXCP_DUMP(env, "No floating point allowed\n"); info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; info._sifields._sigfault._addr = env->nip - 4; queue_signal(info.si_signo, &info); break; - case EXCP_DECR: - /* Should not happen ! */ - fprintf(stderr, "Decrementer exception\n"); - if (loglevel) - fprintf(logfile, "Decrementer exception\n"); - abort(); - case EXCP_TRACE: - /* Do nothing: we use this to trace execution */ - break; - case EXCP_FP_ASSIST: - /* Should not happen ! */ - fprintf(stderr, "Floating point assist exception\n"); - if (loglevel) - fprintf(logfile, "Floating point assist exception\n"); - abort(); - case EXCP_MTMSR: - /* We reloaded the msr, just go on */ - if (msr_pr == 0) { - fprintf(stderr, "Tried to go into supervisor mode !\n"); - if (loglevel) - fprintf(logfile, "Tried to go into supervisor mode !\n"); - abort(); - } + case POWERPC_EXCP_SYSCALL: /* System call exception */ + cpu_abort(env, "Syscall exception while in user mode. " + "Aborting\n"); break; - case EXCP_BRANCH: - /* We stopped because of a jump... */ + case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ + EXCP_DUMP(env, "No APU instruction allowed\n"); + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip - 4; + queue_signal(info.si_signo, &info); break; - case EXCP_INTERRUPT: - /* Don't know why this should ever happen... */ + case POWERPC_EXCP_DECR: /* Decrementer exception */ + cpu_abort(env, "Decrementer interrupt while in user mode. " + "Aborting\n"); break; - case EXCP_DEBUG: + case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ + cpu_abort(env, "Fix interval timer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ + cpu_abort(env, "Watchdog timer interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DTLB: /* Data TLB error */ + cpu_abort(env, "Data TLB exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ITLB: /* Instruction TLB error */ + cpu_abort(env, "Instruction TLB exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DEBUG: /* Debug interrupt */ + /* XXX: check this */ { int sig; - sig = gdb_handlesig (env, TARGET_SIGTRAP); - if (sig) - { + sig = gdb_handlesig(env, TARGET_SIGTRAP); + if (sig) { info.si_signo = sig; info.si_errno = 0; info.si_code = TARGET_TRAP_BRKPT; @@ -1036,14 +979,171 @@ void cpu_loop(CPUPPCState *env) } } break; - default: - fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", - trapnr); - if (loglevel) { - fprintf(logfile, "qemu: unhandled CPU exception 0x%02x - " - "0x%02x - aborting\n", trapnr, env->error_code); +#if defined(TARGET_PPCEMB) + case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */ + EXCP_DUMP(env, "No SPE/floating-point instruction allowed\n"); + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip - 4; + queue_signal(info.si_signo, &info); + break; + case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */ + cpu_abort(env, "Embedded floating-point data IRQ not handled\n"); + break; + case POWERPC_EXCP_EFPRI: /* Embedded floating-point round IRQ */ + cpu_abort(env, "Embedded floating-point round IRQ not handled\n"); + break; + case POWERPC_EXCP_EPERFM: /* Embedded performance monitor IRQ */ + cpu_abort(env, "Performance monitor exception not handled\n"); + break; + case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ + cpu_abort(env, "Doorbell interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ + cpu_abort(env, "Doorbell critical interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_RESET: /* System reset exception */ + cpu_abort(env, "Reset interrupt while in user mode. " + "Aborting\n"); + break; +#endif /* defined(TARGET_PPCEMB) */ +#if defined(TARGET_PPC64) /* PowerPC 64 */ + case POWERPC_EXCP_DSEG: /* Data segment exception */ + cpu_abort(env, "Data segment exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_ISEG: /* Instruction segment exception */ + cpu_abort(env, "Instruction segment exception " + "while in user mode. Aborting\n"); + break; +#endif /* defined(TARGET_PPC64) */ +#if defined(TARGET_PPC64H) /* PowerPC 64 with hypervisor mode support */ + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ + cpu_abort(env, "Hypervisor decrementer interrupt " + "while in user mode. Aborting\n"); + break; +#endif /* defined(TARGET_PPC64H) */ + case POWERPC_EXCP_TRACE: /* Trace exception */ + /* Nothing to do: + * we use this exception to emulate step-by-step execution mode. + */ + break; +#if defined(TARGET_PPC64H) /* PowerPC 64 with hypervisor mode support */ + case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ + cpu_abort(env, "Hypervisor data storage exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HISI: /* Hypervisor instruction storage excp */ + cpu_abort(env, "Hypervisor instruction storage exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ + cpu_abort(env, "Hypervisor data segment exception " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment excp */ + cpu_abort(env, "Hypervisor instruction segment exception " + "while in user mode. Aborting\n"); + break; +#endif /* defined(TARGET_PPC64H) */ + case POWERPC_EXCP_VPU: /* Vector unavailable exception */ + EXCP_DUMP(env, "No Altivec instructions allowed\n"); + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_COPROC; + info._sifields._sigfault._addr = env->nip - 4; + queue_signal(info.si_signo, &info); + break; + case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */ + cpu_abort(env, "Programable interval timer interrupt " + "while in user mode. Aborting\n"); + break; + case POWERPC_EXCP_IO: /* IO error exception */ + cpu_abort(env, "IO error exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_RUNM: /* Run mode exception */ + cpu_abort(env, "Run mode exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_EMUL: /* Emulation trap exception */ + cpu_abort(env, "Emulation trap exception not handled\n"); + break; + case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ + cpu_abort(env, "Instruction fetch TLB exception " + "while in user-mode. Aborting"); + break; + case POWERPC_EXCP_DLTLB: /* Data load TLB miss */ + cpu_abort(env, "Data load TLB exception while in user-mode. " + "Aborting"); + break; + case POWERPC_EXCP_DSTLB: /* Data store TLB miss */ + cpu_abort(env, "Data store TLB exception while in user-mode. " + "Aborting"); + break; + case POWERPC_EXCP_FPA: /* Floating-point assist exception */ + cpu_abort(env, "Floating-point assist exception not handled\n"); + break; + case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ + cpu_abort(env, "Instruction address breakpoint exception " + "not handled\n"); + break; + case POWERPC_EXCP_SMI: /* System management interrupt */ + cpu_abort(env, "System management interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_THERM: /* Thermal interrupt */ + cpu_abort(env, "Thermal interrupt interrupt while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_PERFM: /* Embedded performance monitor IRQ */ + cpu_abort(env, "Performance monitor exception not handled\n"); + break; + case POWERPC_EXCP_VPUA: /* Vector assist exception */ + cpu_abort(env, "Vector assist exception not handled\n"); + break; + case POWERPC_EXCP_SOFTP: /* Soft patch exception */ + cpu_abort(env, "Soft patch exception not handled\n"); + break; + case POWERPC_EXCP_MAINT: /* Maintenance exception */ + cpu_abort(env, "Maintenance exception while in user mode. " + "Aborting\n"); + break; + case POWERPC_EXCP_STOP: /* stop translation */ + /* We did invalidate the instruction cache. Go on */ + break; + case POWERPC_EXCP_BRANCH: /* branch instruction: */ + /* We just stopped because of a branch. Go on */ + break; + case POWERPC_EXCP_SYSCALL_USER: + /* system call in user-mode emulation */ + /* WARNING: + * PPC ABI uses overflow flag in cr0 to signal an error + * in syscalls. + */ +#if 0 + printf("syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n", env->gpr[0], + env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6]); +#endif + env->crf[0] &= ~0x1; + ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4], + env->gpr[5], env->gpr[6], env->gpr[7], + env->gpr[8]); + if (ret > (uint32_t)(-515)) { + env->crf[0] |= 0x1; + ret = -ret; } - abort(); + env->gpr[3] = ret; +#if 0 + printf("syscall returned 0x%08x (%d)\n", ret, ret); +#endif + break; + default: + cpu_abort(env, "Unknown exception 0x%d. Aborting\n", trapnr); + break; } process_pending_signals(env); } |