From: Benjamin Herrenschmidt <b...@kernel.crashing.org> We make env->nip almost always point to the faulting instruction, thus avoiding a mess of "store_current" vs "store_next" in the exception handling. The syscall exception knows to move the PC by 4 and that's really about it.
This actually fixes a number of cases where the translator was setting env->nip to ctx->nip - 4 (ie. the *current* instruction) but the program check exception handler would branch to "store_current" which applies another -4 offset. Signed-off-by: Benjamin Herrenschmidt <b...@kernel.crashing.org> Signed-off-by: David Gibson <da...@gibson.dropbear.id.au> --- linux-user/main.c | 12 ++-- target-ppc/excp_helper.c | 146 ++++++++++++++++++----------------------------- target-ppc/translate.c | 60 +++++++++++-------- 3 files changed, 95 insertions(+), 123 deletions(-) diff --git a/linux-user/main.c b/linux-user/main.c index f2f4d2f..d112834 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1814,7 +1814,7 @@ void cpu_loop(CPUPPCState *env) env->error_code); break; } - info._sifields._sigfault._addr = env->nip - 4; + info._sifields._sigfault._addr = env->nip; queue_signal(env, info.si_signo, &info); break; case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ @@ -1822,7 +1822,7 @@ void cpu_loop(CPUPPCState *env) info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip - 4; + info._sifields._sigfault._addr = env->nip; queue_signal(env, info.si_signo, &info); break; case POWERPC_EXCP_SYSCALL: /* System call exception */ @@ -1834,7 +1834,7 @@ void cpu_loop(CPUPPCState *env) info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip - 4; + info._sifields._sigfault._addr = env->nip; queue_signal(env, info.si_signo, &info); break; case POWERPC_EXCP_DECR: /* Decrementer exception */ @@ -1862,7 +1862,7 @@ void cpu_loop(CPUPPCState *env) info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip - 4; + info._sifields._sigfault._addr = env->nip; queue_signal(env, info.si_signo, &info); break; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */ @@ -1926,7 +1926,7 @@ void cpu_loop(CPUPPCState *env) info.si_signo = TARGET_SIGILL; info.si_errno = 0; info.si_code = TARGET_ILL_COPROC; - info._sifields._sigfault._addr = env->nip - 4; + info._sifields._sigfault._addr = env->nip; queue_signal(env, info.si_signo, &info); break; case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */ @@ -2001,9 +2001,9 @@ void cpu_loop(CPUPPCState *env) env->gpr[5], env->gpr[6], env->gpr[7], env->gpr[8], 0, 0); if (ret == -TARGET_ERESTARTSYS) { - env->nip -= 4; break; } + env->nip += 4; if (ret == (target_ulong)(-TARGET_QEMU_ESIGRETURN)) { /* Returning from a successful sigreturn syscall. Avoid corrupting register state. */ diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index eb00473..d31eece 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -198,7 +198,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) default: goto excp_invalid; } - goto store_next; + break; case POWERPC_EXCP_MCHECK: /* Machine check exception */ if (msr_me == 0) { /* Machine check exception is not enabled. @@ -235,16 +235,16 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) default: break; } - goto store_next; + break; case POWERPC_EXCP_DSI: /* Data storage exception */ LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx "\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]); - goto store_next; + break; case POWERPC_EXCP_ISI: /* Instruction storage exception */ LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx "\n", msr, env->nip); msr |= env->error_code; - goto store_next; + break; case POWERPC_EXCP_EXTERNAL: /* External input */ cs = CPU(cpu); @@ -258,13 +258,14 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* IACK the IRQ on delivery */ env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack); } - goto store_next; + break; case POWERPC_EXCP_ALIGN: /* Alignment exception */ /* XXX: this is false */ /* Get rS/rD and rA from faulting opcode */ - env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4)) + /* Broken for LE mode */ + env->spr[SPR_DSISR] |= (cpu_ldl_code(env, env->nip) & 0x03FF0000) >> 16; - goto store_next; + break; case POWERPC_EXCP_PROGRAM: /* Program exception */ switch (env->error_code & ~0xF) { case POWERPC_EXCP_FP: @@ -280,15 +281,11 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) * precise in the MSR. */ msr |= 0x00100000; - goto store_next; + break; case POWERPC_EXCP_INVAL: LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip); msr |= 0x00080000; env->spr[SPR_BOOKE_ESR] = ESR_PIL; - /* Some invalids will have the PC in the right place already */ - if (env->error_code & POWERPC_EXCP_INVAL_LSWX) { - goto store_next; - } break; case POWERPC_EXCP_PRIV: msr |= 0x00040000; @@ -304,23 +301,16 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) env->error_code); break; } - goto store_current; - case POWERPC_EXCP_HV_EMU: - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - /* Some invalids will have the PC in the right place already */ - if (env->error_code == (POWERPC_EXCP_INVAL|POWERPC_EXCP_INVAL_LSWX)) { - goto store_next; - } - goto store_current; - case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ - goto store_current; + break; case POWERPC_EXCP_SYSCALL: /* System call exception */ dump_syscall(env); lev = env->error_code; + /* We need to correct the NIP which in this case is supposed + * to point to the next instruction + */ + env->nip += 4; + /* "PAPR mode" built-in hypercall emulation */ if ((lev == 1) && cpu_ppc_hypercall) { cpu_ppc_hypercall(cpu); @@ -329,15 +319,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) if (lev == 1) { new_msr |= (target_ulong)MSR_HVB; } - goto store_next; + break; + case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */ case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */ - goto store_current; case POWERPC_EXCP_DECR: /* Decrementer exception */ - goto store_next; + break; case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */ /* FIT on 4xx */ LOG_EXCP("FIT exception\n"); - goto store_next; + break; case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */ LOG_EXCP("WDT exception\n"); switch (excp_model) { @@ -348,11 +338,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) default: break; } - goto store_next; + break; case POWERPC_EXCP_DTLB: /* Data TLB error */ - goto store_next; case POWERPC_EXCP_ITLB: /* Instruction TLB error */ - goto store_next; + break; case POWERPC_EXCP_DEBUG: /* Debug interrupt */ switch (excp_model) { case POWERPC_EXCP_BOOKE: @@ -367,33 +356,33 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } /* XXX: TODO */ cpu_abort(cs, "Debug exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ env->spr[SPR_BOOKE_ESR] = ESR_SPV; - goto store_current; + break; case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */ /* XXX: TODO */ cpu_abort(cs, "Embedded floating point data exception " "is not implemented yet !\n"); env->spr[SPR_BOOKE_ESR] = ESR_SPV; - goto store_next; + break; case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */ /* XXX: TODO */ cpu_abort(cs, "Embedded floating point round exception " "is not implemented yet !\n"); env->spr[SPR_BOOKE_ESR] = ESR_SPV; - goto store_next; + break; case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */ /* XXX: TODO */ cpu_abort(cs, "Performance counter exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */ - goto store_next; + break; case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */ srr0 = SPR_BOOKE_CSRR0; srr1 = SPR_BOOKE_CSRR1; - goto store_next; + break; case POWERPC_EXCP_RESET: /* System reset exception */ if (msr_pow) { /* indicate that we resumed from power save mode */ @@ -404,65 +393,42 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) new_msr |= (target_ulong)MSR_HVB; ail = 0; - goto store_next; + break; case POWERPC_EXCP_DSEG: /* Data segment exception */ - goto store_next; case POWERPC_EXCP_ISEG: /* Instruction segment exception */ - goto store_next; - case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; case POWERPC_EXCP_TRACE: /* Trace exception */ - goto store_next; + break; + case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */ - srr0 = SPR_HSRR0; - srr1 = SPR_HSRR1; - new_msr |= (target_ulong)MSR_HVB; - new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ + case POWERPC_EXCP_HV_EMU: srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; new_msr |= env->msr & ((target_ulong)1 << MSR_RI); - goto store_next; + break; case POWERPC_EXCP_VPU: /* Vector unavailable exception */ - goto store_current; case POWERPC_EXCP_VSXU: /* VSX unavailable exception */ - goto store_current; case POWERPC_EXCP_FU: /* Facility unavailable exception */ - goto store_current; + break; case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */ LOG_EXCP("PIT exception\n"); - goto store_next; + break; case POWERPC_EXCP_IO: /* IO error exception */ /* XXX: TODO */ cpu_abort(cs, "601 IO error exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_RUNM: /* Run mode exception */ /* XXX: TODO */ cpu_abort(cs, "601 run mode exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_EMUL: /* Emulation trap exception */ /* XXX: TODO */ cpu_abort(cs, "602 emulation trap exception " "is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */ switch (excp_model) { case POWERPC_EXCP_602: @@ -577,71 +543,67 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) cpu_abort(cs, "Invalid data store TLB miss exception\n"); break; } - goto store_next; + break; case POWERPC_EXCP_FPA: /* Floating-point assist exception */ /* XXX: TODO */ cpu_abort(cs, "Floating point assist exception " "is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_DABR: /* Data address breakpoint */ /* XXX: TODO */ cpu_abort(cs, "DABR exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_IABR: /* Instruction address breakpoint */ /* XXX: TODO */ cpu_abort(cs, "IABR exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_SMI: /* System management interrupt */ /* XXX: TODO */ cpu_abort(cs, "SMI exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_THERM: /* Thermal interrupt */ /* XXX: TODO */ cpu_abort(cs, "Thermal management exception " "is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */ /* XXX: TODO */ cpu_abort(cs, "Performance counter exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_VPUA: /* Vector assist exception */ /* XXX: TODO */ cpu_abort(cs, "VPU assist exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_SOFTP: /* Soft patch exception */ /* XXX: TODO */ cpu_abort(cs, "970 soft-patch exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_MAINT: /* Maintenance exception */ /* XXX: TODO */ cpu_abort(cs, "970 maintenance exception is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */ /* XXX: TODO */ cpu_abort(cs, "Maskable external exception " "is not implemented yet !\n"); - goto store_next; + break; case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */ /* XXX: TODO */ cpu_abort(cs, "Non maskable external exception " "is not implemented yet !\n"); - goto store_next; + break; default: excp_invalid: cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp); break; - store_current: - /* save current instruction location */ - env->spr[srr0] = env->nip - 4; - break; - store_next: - /* save next instruction location */ - env->spr[srr0] = env->nip; - break; } + + /* Save PC */ + env->spr[srr0] = env->nip; + /* Save MSR */ env->spr[srr1] = msr; diff --git a/target-ppc/translate.c b/target-ppc/translate.c index dc5ebb1..afcdd3e 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -276,8 +276,12 @@ void gen_update_current_nip(void *opaque) static void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) { TCGv_i32 t0, t1; + + /* These are all synchronous exceptions, we set the PC back to + * the faulting instruction + */ if (ctx->exception == POWERPC_EXCP_NONE) { - gen_update_nip(ctx, ctx->nip); + gen_update_nip(ctx, ctx->nip - 4); } t0 = tcg_const_i32(excp); t1 = tcg_const_i32(error); @@ -290,8 +294,12 @@ static void gen_exception_err(DisasContext *ctx, uint32_t excp, uint32_t error) static void gen_exception(DisasContext *ctx, uint32_t excp) { TCGv_i32 t0; + + /* These are all synchronous exceptions, we set the PC back to + * the faulting instruction + */ if (ctx->exception == POWERPC_EXCP_NONE) { - gen_update_nip(ctx, ctx->nip); + gen_update_nip(ctx, ctx->nip - 4); } t0 = tcg_const_i32(excp); gen_helper_raise_exception(cpu_env, t0); @@ -299,13 +307,28 @@ static void gen_exception(DisasContext *ctx, uint32_t excp) ctx->exception = (excp); } +static void gen_exception_nip(DisasContext *ctx, uint32_t excp, + target_ulong nip) +{ + TCGv_i32 t0; + + gen_update_nip(ctx, nip); + t0 = tcg_const_i32(excp); + gen_helper_raise_exception(cpu_env, t0); + tcg_temp_free_i32(t0); + ctx->exception = (excp); +} + static void gen_debug_exception(DisasContext *ctx) { TCGv_i32 t0; + /* These are all synchronous exceptions, we set the PC back to + * the faulting instruction + */ if ((ctx->exception != POWERPC_EXCP_BRANCH) && (ctx->exception != POWERPC_EXCP_SYNC)) { - gen_update_nip(ctx, ctx->nip); + gen_update_nip(ctx, ctx->nip - 4); } t0 = tcg_const_i32(EXCP_DEBUG); gen_helper_raise_exception(cpu_env, t0); @@ -1643,7 +1666,7 @@ static void gen_pause(DisasContext *ctx) tcg_temp_free_i32(t0); /* Stop translation, this gives other CPUs a chance to run */ - gen_exception_err(ctx, EXCP_HLT, 1); + gen_exception_nip(ctx, EXCP_HLT, ctx->nip); } #endif /* defined(TARGET_PPC64) */ @@ -2912,12 +2935,6 @@ static void gen_lswi(DisasContext *ctx) nb = 32; nr = (nb + 3) / 4; if (unlikely(lsw_reg_in_range(start, nr, ra))) { - /* The handler expects the PC to point to *this* instruction, - * so setting ctx->exception here prevents it from being - * improperly updated again by gen_inval_exception - */ - gen_update_nip(ctx, ctx->nip - 4); - ctx->exception = POWERPC_EXCP_HV_EMU; gen_inval_exception(ctx, POWERPC_EXCP_INVAL_LSWX); return; } @@ -3055,16 +3072,12 @@ static void gen_conditional_store(DisasContext *ctx, TCGv EA, int reg, int size) { TCGv t0 = tcg_temp_new(); - uint32_t save_exception = ctx->exception; tcg_gen_st_tl(EA, cpu_env, offsetof(CPUPPCState, reserve_ea)); tcg_gen_movi_tl(t0, (size << 5) | reg); tcg_gen_st_tl(t0, cpu_env, offsetof(CPUPPCState, reserve_info)); tcg_temp_free(t0); - gen_update_nip(ctx, ctx->nip-4); - ctx->exception = POWERPC_EXCP_BRANCH; - gen_exception(ctx, POWERPC_EXCP_STCX); - ctx->exception = save_exception; + gen_exception_err(ctx, POWERPC_EXCP_STCX, 0); } #else static void gen_conditional_store(DisasContext *ctx, TCGv EA, @@ -3203,7 +3216,7 @@ static void gen_wait(DisasContext *ctx) -offsetof(PowerPCCPU, env) + offsetof(CPUState, halted)); tcg_temp_free_i32(t0); /* Stop translation, as the CPU is supposed to sleep from now */ - gen_exception_err(ctx, EXCP_HLT, 1); + gen_exception_nip(ctx, EXCP_HLT, ctx->nip); } #if defined(TARGET_PPC64) @@ -3306,10 +3319,7 @@ static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) (CPU_BRANCH_STEP | CPU_SINGLE_STEP)) && (ctx->exception == POWERPC_EXCP_BRANCH || ctx->exception == POWERPC_EXCP_TRACE)) { - target_ulong tmp = ctx->nip; - ctx->nip = dest; - gen_exception(ctx, POWERPC_EXCP_TRACE); - ctx->nip = tmp; + gen_exception_nip(ctx, POWERPC_EXCP_TRACE, dest); } if (ctx->singlestep_enabled & GDBSTUB_SINGLE_STEP) { gen_debug_exception(ctx); @@ -3578,7 +3588,7 @@ static void gen_tw(DisasContext *ctx) { TCGv_i32 t0 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ - gen_update_nip(ctx, ctx->nip); + gen_update_nip(ctx, ctx->nip - 4); gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); tcg_temp_free_i32(t0); @@ -3590,7 +3600,7 @@ static void gen_twi(DisasContext *ctx) TCGv t0 = tcg_const_tl(SIMM(ctx->opcode)); TCGv_i32 t1 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ - gen_update_nip(ctx, ctx->nip); + gen_update_nip(ctx, ctx->nip - 4); gen_helper_tw(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); tcg_temp_free(t0); tcg_temp_free_i32(t1); @@ -3602,7 +3612,7 @@ static void gen_td(DisasContext *ctx) { TCGv_i32 t0 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ - gen_update_nip(ctx, ctx->nip); + gen_update_nip(ctx, ctx->nip - 4); gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], t0); tcg_temp_free_i32(t0); @@ -3614,7 +3624,7 @@ static void gen_tdi(DisasContext *ctx) TCGv t0 = tcg_const_tl(SIMM(ctx->opcode)); TCGv_i32 t1 = tcg_const_i32(TO(ctx->opcode)); /* Update the nip since this might generate a trap exception */ - gen_update_nip(ctx, ctx->nip); + gen_update_nip(ctx, ctx->nip - 4); gen_helper_td(cpu_env, cpu_gpr[rA(ctx->opcode)], t0, t1); tcg_temp_free(t0); tcg_temp_free_i32(t1); @@ -7054,7 +7064,7 @@ void gen_intermediate_code(CPUPPCState *env, struct TranslationBlock *tb) ctx.exception != POWERPC_SYSCALL && ctx.exception != POWERPC_EXCP_TRAP && ctx.exception != POWERPC_EXCP_BRANCH)) { - gen_exception(ctxp, POWERPC_EXCP_TRACE); + gen_exception_nip(ctxp, POWERPC_EXCP_TRACE, ctx.nip); } else if (unlikely(((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) || (cs->singlestep_enabled) || singlestep || -- 2.7.4