diff options
Diffstat (limited to 'target/ppc')
-rw-r--r-- | target/ppc/cpu-qom.h | 2 | ||||
-rw-r--r-- | target/ppc/cpu.h | 21 | ||||
-rw-r--r-- | target/ppc/excp_helper.c | 11 | ||||
-rw-r--r-- | target/ppc/fpu_helper.c | 292 | ||||
-rw-r--r-- | target/ppc/helper.h | 14 | ||||
-rw-r--r-- | target/ppc/int_helper.c | 46 | ||||
-rw-r--r-- | target/ppc/kvm.c | 13 | ||||
-rw-r--r-- | target/ppc/kvm_ppc.h | 7 | ||||
-rw-r--r-- | target/ppc/mmu_helper.c | 6 | ||||
-rw-r--r-- | target/ppc/translate.c | 107 | ||||
-rw-r--r-- | target/ppc/translate/fp-impl.inc.c | 26 | ||||
-rw-r--r-- | target/ppc/translate_init.inc.c | 54 |
12 files changed, 337 insertions, 262 deletions
diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 433a71e484..4ea67692e2 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -24,8 +24,6 @@ #ifdef TARGET_PPC64 #define TYPE_POWERPC_CPU "powerpc64-cpu" -#elif defined(TARGET_PPCEMB) -#define TYPE_POWERPC_CPU "embedded-powerpc-cpu" #else #define TYPE_POWERPC_CPU "powerpc-cpu" #endif diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 4edcf62cf7..b5b8f6f440 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -52,23 +52,7 @@ #else /* defined (TARGET_PPC64) */ /* PowerPC 32 definitions */ #define TARGET_LONG_BITS 32 - -#if defined(TARGET_PPCEMB) -/* Specific definitions for PowerPC embedded */ -/* BookE have 36 bits physical address space */ -#if defined(CONFIG_USER_ONLY) -/* It looks like a lot of Linux programs assume page size - * is 4kB long. This is evil, but we have to deal with it... - */ #define TARGET_PAGE_BITS 12 -#else /* defined(CONFIG_USER_ONLY) */ -/* Pages can be 1 kB small */ -#define TARGET_PAGE_BITS 10 -#endif /* defined(CONFIG_USER_ONLY) */ -#else /* defined(TARGET_PPCEMB) */ -/* "standard" PowerPC 32 definitions */ -#define TARGET_PAGE_BITS 12 -#endif /* defined(TARGET_PPCEMB) */ #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 @@ -481,6 +465,11 @@ struct ppc_slb_t { #define msr_ts ((env->msr >> MSR_TS1) & 3) #define msr_tm ((env->msr >> MSR_TM) & 1) +#define DBCR0_ICMP (1 << 27) +#define DBCR0_BRT (1 << 26) +#define DBSR_ICMP (1 << 27) +#define DBSR_BRT (1 << 26) + /* Hypervisor bit is more specific */ #if defined(TARGET_PPC64) #define MSR_HVB (1ULL << MSR_SHV) diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index d6e97a90e0..0ec7ae1ad4 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -348,19 +348,16 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) case POWERPC_EXCP_ITLB: /* Instruction TLB error */ break; case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - switch (excp_model) { - case POWERPC_EXCP_BOOKE: + if (env->flags & POWERPC_FLAG_DE) { /* FIXME: choose one or the other based on CPU type */ srr0 = SPR_BOOKE_DSRR0; srr1 = SPR_BOOKE_DSRR1; asrr0 = SPR_BOOKE_CSRR0; asrr1 = SPR_BOOKE_CSRR1; - break; - default: - break; + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); } - /* XXX: TODO */ - cpu_abort(cs, "Debug exception is not implemented yet !\n"); break; case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ env->spr[SPR_BOOKE_ESR] = ESR_SPV; diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 8675d931b6..b9bb1b856e 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -36,26 +36,72 @@ static inline float128 float128_snan_to_qnan(float128 x) #define float32_snan_to_qnan(x) ((x) | 0x00400000) #define float16_snan_to_qnan(x) ((x) | 0x0200) +static inline bool fp_exceptions_enabled(CPUPPCState *env) +{ +#ifdef CONFIG_USER_ONLY + return true; +#else + return (env->msr & ((1U << MSR_FE0) | (1U << MSR_FE1))) != 0; +#endif +} + /*****************************************************************************/ /* Floating point operations helpers */ -uint64_t helper_float32_to_float64(CPUPPCState *env, uint32_t arg) + +/* + * This is the non-arithmatic conversion that happens e.g. on loads. + * In the Power ISA pseudocode, this is called DOUBLE. + */ +uint64_t helper_todouble(uint32_t arg) { - CPU_FloatU f; - CPU_DoubleU d; + uint32_t abs_arg = arg & 0x7fffffff; + uint64_t ret; - f.l = arg; - d.d = float32_to_float64(f.f, &env->fp_status); - return d.ll; + if (likely(abs_arg >= 0x00800000)) { + /* Normalized operand, or Inf, or NaN. */ + ret = (uint64_t)extract32(arg, 30, 2) << 62; + ret |= ((extract32(arg, 30, 1) ^ 1) * (uint64_t)7) << 59; + ret |= (uint64_t)extract32(arg, 0, 30) << 29; + } else { + /* Zero or Denormalized operand. */ + ret = (uint64_t)extract32(arg, 31, 1) << 63; + if (unlikely(abs_arg != 0)) { + /* Denormalized operand. */ + int shift = clz32(abs_arg) - 9; + int exp = -126 - shift + 1023; + ret |= (uint64_t)exp << 52; + ret |= abs_arg << (shift + 29); + } + } + return ret; } -uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg) +/* + * This is the non-arithmatic conversion that happens e.g. on stores. + * In the Power ISA pseudocode, this is called SINGLE. + */ +uint32_t helper_tosingle(uint64_t arg) { - CPU_FloatU f; - CPU_DoubleU d; + int exp = extract64(arg, 52, 11); + uint32_t ret; - d.ll = arg; - f.f = float64_to_float32(d.d, &env->fp_status); - return f.l; + if (likely(exp > 896)) { + /* No denormalization required (includes Inf, NaN). */ + ret = extract64(arg, 62, 2) << 30; + ret |= extract64(arg, 29, 30); + } else { + /* Zero or Denormal result. If the exponent is in bounds for + * a single-precision denormal result, extract the proper bits. + * If the input is not zero, and the exponent is out of bounds, + * then the result is undefined; this underflows to zero. + */ + ret = extract64(arg, 63, 1) << 31; + if (unlikely(exp >= 874)) { + /* Denormal result. */ + ret |= ((1ULL << 52) | extract64(arg, 0, 52)) >> (896 + 30 - exp); + } + } + return ret; } static inline int ppc_float32_get_unbiased_exp(float32 f) @@ -207,7 +253,7 @@ uint64_t float_invalid_op_excp(CPUPPCState *env, int op, int set_fpcc) if (ve != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; - if (msr_fe0 != 0 || msr_fe1 != 0) { + if (fp_exceptions_enabled(env)) { /* GETPC() works here because this is inline */ raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | op, GETPC()); @@ -225,7 +271,7 @@ static inline void float_zero_divide_excp(CPUPPCState *env, uintptr_t raddr) if (fpscr_ze != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; - if (msr_fe0 != 0 || msr_fe1 != 0) { + if (fp_exceptions_enabled(env)) { raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX, raddr); @@ -536,9 +582,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) int status = get_float_exception_flags(&env->fp_status); bool inexact_happened = false; - if (status & float_flag_divbyzero) { - float_zero_divide_excp(env, raddr); - } else if (status & float_flag_overflow) { + if (status & float_flag_overflow) { float_overflow_excp(env); } else if (status & float_flag_underflow) { float_underflow_excp(env); @@ -555,7 +599,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) if (cs->exception_index == POWERPC_EXCP_PROGRAM && (env->error_code & POWERPC_EXCP_FP)) { /* Differred floating-point exception after target FPR update */ - if (msr_fe0 != 0 || msr_fe1 != 0) { + if (fp_exceptions_enabled(env)) { raise_exception_err_ra(env, cs->exception_index, env->error_code, raddr); } @@ -580,102 +624,93 @@ void helper_reset_fpstatus(CPUPPCState *env) } /* fadd - fadd. */ -uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +float64 helper_fadd(CPUPPCState *env, float64 arg1, float64 arg2) { - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; + float64 ret = float64_add(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { + if (unlikely(status & float_flag_invalid)) { + if (float64_is_infinity(arg1) && float64_is_infinity(arg2)) { + /* Magnitude subtraction of infinities */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else if (float64_is_signaling_nan(arg1, &env->fp_status) || + float64_is_signaling_nan(arg2, &env->fp_status)) { /* sNaN addition */ float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } - farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); } - return farg1.ll; + return ret; } /* fsub - fsub. */ -uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +float64 helper_fsub(CPUPPCState *env, float64 arg1, float64 arg2) { - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; + float64 ret = float64_sub(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { - /* sNaN subtraction */ + if (unlikely(status & float_flag_invalid)) { + if (float64_is_infinity(arg1) && float64_is_infinity(arg2)) { + /* Magnitude subtraction of infinities */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); + } else if (float64_is_signaling_nan(arg1, &env->fp_status) || + float64_is_signaling_nan(arg2, &env->fp_status)) { + /* sNaN addition */ float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } - farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); } - return farg1.ll; + return ret; } /* fmul - fmul. */ -uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2) { - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; + float64 ret = float64_mul(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { - /* Multiplication of zero by infinity */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { + if (unlikely(status & float_flag_invalid)) { + if ((float64_is_infinity(arg1) && float64_is_zero(arg2)) || + (float64_is_zero(arg1) && float64_is_infinity(arg2))) { + /* Multiplication of zero by infinity */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); + } else if (float64_is_signaling_nan(arg1, &env->fp_status) || + float64_is_signaling_nan(arg2, &env->fp_status)) { /* sNaN multiplication */ float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); } - return farg1.ll; + return ret; } /* fdiv - fdiv. */ -uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +float64 helper_fdiv(CPUPPCState *env, float64 arg1, float64 arg2) { - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; + float64 ret = float64_div(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - if (unlikely(float64_is_infinity(farg1.d) && - float64_is_infinity(farg2.d))) { - /* Division of infinity by infinity */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); - } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { - /* Division of zero by zero */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { - /* sNaN division */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + if (unlikely(status)) { + if (status & float_flag_invalid) { + /* Determine what kind of invalid operation was seen. */ + if (float64_is_infinity(arg1) && float64_is_infinity(arg2)) { + /* Division of infinity by infinity */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); + } else if (float64_is_zero(arg1) && float64_is_zero(arg2)) { + /* Division of zero by zero */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); + } else if (float64_is_signaling_nan(arg1, &env->fp_status) || + float64_is_signaling_nan(arg2, &env->fp_status)) { + /* sNaN division */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + } + if (status & float_flag_divbyzero) { + float_zero_divide_excp(env, GETPC()); } - farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); } - return farg1.ll; + return ret; } @@ -860,40 +895,48 @@ uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) } /* fsqrt - fsqrt. */ -uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) +float64 helper_fsqrt(CPUPPCState *env, float64 arg) { - CPU_DoubleU farg; - - farg.ll = arg; + float64 ret = float64_sqrt(arg, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - if (unlikely(float64_is_any_nan(farg.d))) { - if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { - /* sNaN reciprocal square root */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - farg.ll = float64_snan_to_qnan(farg.ll); + if (unlikely(status & float_flag_invalid)) { + if (unlikely(float64_is_any_nan(arg))) { + if (unlikely(float64_is_signaling_nan(arg, &env->fp_status))) { + /* sNaN square root */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + } else { + /* Square root of a negative nonzero number */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); } - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Square root of a negative nonzero number */ - farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - } else { - farg.d = float64_sqrt(farg.d, &env->fp_status); } - return farg.ll; + + return ret; } /* fre - fre. */ -uint64_t helper_fre(CPUPPCState *env, uint64_t arg) +float64 helper_fre(CPUPPCState *env, float64 arg) { - CPU_DoubleU farg; - - farg.ll = arg; + /* "Estimate" the reciprocal with actual division. */ + float64 ret = float64_div(float64_one, arg, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { - /* sNaN reciprocal */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + if (unlikely(status)) { + if (status & float_flag_invalid) { + if (float64_is_signaling_nan(arg, &env->fp_status)) { + /* sNaN reciprocal */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } + } + if (status & float_flag_divbyzero) { + float_zero_divide_excp(env, GETPC()); + /* For FPSCR.ZE == 0, the result is 1/2. */ + ret = float64_set_sign(float64_half, float64_is_neg(arg)); + } } - farg.d = float64_div(float64_one, farg.d, &env->fp_status); - return farg.d; + + return ret; } /* fres - fres. */ @@ -916,27 +959,30 @@ uint64_t helper_fres(CPUPPCState *env, uint64_t arg) } /* frsqrte - frsqrte. */ -uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) +float64 helper_frsqrte(CPUPPCState *env, float64 arg) { - CPU_DoubleU farg; - - farg.ll = arg; + /* "Estimate" the reciprocal with actual division. */ + float64 rets = float64_sqrt(arg, &env->fp_status); + float64 retd = float64_div(float64_one, rets, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - if (unlikely(float64_is_any_nan(farg.d))) { - if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { - /* sNaN reciprocal square root */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - farg.ll = float64_snan_to_qnan(farg.ll); + if (unlikely(status)) { + if (status & float_flag_invalid) { + if (float64_is_signaling_nan(arg, &env->fp_status)) { + /* sNaN reciprocal */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + } else { + /* Square root of a negative nonzero number */ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); + } + } + if (status & float_flag_divbyzero) { + /* Reciprocal of (square root of) zero. */ + float_zero_divide_excp(env, GETPC()); } - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Reciprocal square root of a negative nonzero number */ - farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - } else { - farg.d = float64_sqrt(farg.d, &env->fp_status); - farg.d = float64_div(float64_one, farg.d, &env->fp_status); } - return farg.ll; + return retd; } /* fsel - fsel. */ @@ -1920,6 +1966,9 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ } \ } \ + if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) { \ + float_zero_divide_excp(env, GETPC()); \ + } \ \ if (r2sp) { \ xt.fld = helper_frsp(env, xt.fld); \ @@ -1969,6 +2018,9 @@ void helper_xsdivqp(CPUPPCState *env, uint32_t opcode) float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); } } + if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) { + float_zero_divide_excp(env, GETPC()); + } helper_compute_fprf_float128(env, xt.f128); putVSR(rD(opcode) + 32, &xt, env); diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 5706c2497f..ef64248bc4 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -61,8 +61,8 @@ DEF_HELPER_2(compute_fprf_float64, void, env, i64) DEF_HELPER_3(store_fpscr, void, env, i64, i32) DEF_HELPER_2(fpscr_clrbit, void, env, i32) DEF_HELPER_2(fpscr_setbit, void, env, i32) -DEF_HELPER_2(float64_to_float32, i32, env, i64) -DEF_HELPER_2(float32_to_float64, i64, env, i32) +DEF_HELPER_FLAGS_1(todouble, TCG_CALL_NO_RWG_SE, i64, i32) +DEF_HELPER_FLAGS_1(tosingle, TCG_CALL_NO_RWG_SE, i32, i64) DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) @@ -85,15 +85,15 @@ DEF_HELPER_2(friz, i64, env, i64) DEF_HELPER_2(frip, i64, env, i64) DEF_HELPER_2(frim, i64, env, i64) -DEF_HELPER_3(fadd, i64, env, i64, i64) -DEF_HELPER_3(fsub, i64, env, i64, i64) -DEF_HELPER_3(fmul, i64, env, i64, i64) -DEF_HELPER_3(fdiv, i64, env, i64, i64) +DEF_HELPER_3(fadd, f64, env, f64, f64) +DEF_HELPER_3(fsub, f64, env, f64, f64) +DEF_HELPER_3(fmul, f64, env, f64, f64) +DEF_HELPER_3(fdiv, f64, env, f64, f64) DEF_HELPER_4(fmadd, i64, env, i64, i64, i64) DEF_HELPER_4(fmsub, i64, env, i64, i64, i64) DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64) DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64) -DEF_HELPER_2(fsqrt, i64, env, i64) +DEF_HELPER_2(fsqrt, f64, env, f64) DEF_HELPER_2(fre, i64, env, i64) DEF_HELPER_2(fres, i64, env, i64) DEF_HELPER_2(frsqrte, i64, env, i64) diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index d52338ed71..fcac90a4a9 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -2671,16 +2671,14 @@ static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) return 0; } -static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, +static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, int *overflow) { int carry = 0; int i; - int is_zero = 1; for (i = 1; i <= 31; i++) { uint8_t digit = bcd_get_digit(a, i, invalid) + bcd_get_digit(b, i, invalid) + carry; - is_zero &= (digit == 0); if (digit > 9) { carry = 1; digit -= 10; @@ -2689,26 +2687,20 @@ static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, } bcd_put_digit(t, digit, i); - - if (unlikely(*invalid)) { - return -1; - } } *overflow = carry; - return is_zero; } -static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, +static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, int *overflow) { int carry = 0; int i; - int is_zero = 1; + for (i = 1; i <= 31; i++) { uint8_t digit = bcd_get_digit(a, i, invalid) - bcd_get_digit(b, i, invalid) + carry; - is_zero &= (digit == 0); if (digit & 0x80) { carry = -1; digit += 10; @@ -2717,14 +2709,9 @@ static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, } bcd_put_digit(t, digit, i); - - if (unlikely(*invalid)) { - return -1; - } } *overflow = carry; - return is_zero; } uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) @@ -2734,23 +2721,28 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) int sgnb = bcd_get_sgn(b); int invalid = (sgna == 0) || (sgnb == 0); int overflow = 0; - int zero = 0; uint32_t cr = 0; ppc_avr_t result = { .u64 = { 0, 0 } }; if (!invalid) { if (sgna == sgnb) { result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); - zero = bcd_add_mag(&result, a, b, &invalid, &overflow); - cr = (sgna > 0) ? CRF_GT : CRF_LT; - } else if (bcd_cmp_mag(a, b) > 0) { - result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); - zero = bcd_sub_mag(&result, a, b, &invalid, &overflow); - cr = (sgna > 0) ? CRF_GT : CRF_LT; + bcd_add_mag(&result, a, b, &invalid, &overflow); + cr = bcd_cmp_zero(&result); } else { - result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); - zero = bcd_sub_mag(&result, b, a, &invalid, &overflow); - cr = (sgnb > 0) ? CRF_GT : CRF_LT; + int magnitude = bcd_cmp_mag(a, b); + if (magnitude > 0) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + bcd_sub_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? CRF_GT : CRF_LT; + } else if (magnitude < 0) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); + bcd_sub_mag(&result, b, a, &invalid, &overflow); + cr = (sgnb > 0) ? CRF_GT : CRF_LT; + } else { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(0, ps); + cr = CRF_EQ; + } } } @@ -2759,8 +2751,6 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) cr = CRF_SO; } else if (overflow) { cr |= CRF_SO; - } else if (zero) { - cr = CRF_EQ; } *r = result; diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 9211ee2ee1..30aeafa7de 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -1315,9 +1315,7 @@ int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) return 0; } -#if defined(TARGET_PPCEMB) -#define PPC_INPUT_INT PPC40x_INPUT_INT -#elif defined(TARGET_PPC64) +#if defined(TARGET_PPC64) #define PPC_INPUT_INT PPC970_INPUT_INT #else #define PPC_INPUT_INT PPC6xx_INPUT_INT @@ -2785,3 +2783,12 @@ bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu) return !kvmppc_is_pr(cs->kvm_state); } + +void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) +{ + CPUState *cs = CPU(cpu); + + if (kvm_enabled()) { + kvm_set_one_reg(cs, KVM_REG_PPC_ONLINE, &online); + } +} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 657582bb32..f696c6e498 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -72,6 +72,7 @@ bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu); bool kvmppc_hpt_needs_host_contiguous_pages(void); void kvm_check_mmu(PowerPCCPU *cpu, Error **errp); +void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online); #else @@ -187,6 +188,12 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, return 0; } +static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, + unsigned int online) +{ + return; +} + #ifndef CONFIG_USER_ONLY static inline bool kvmppc_spapr_use_multitce(void) { diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index e6739e6c24..04f8317ea1 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -2363,12 +2363,12 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT) & PPC4XX_TLBHI_SIZE_MASK); /* We cannot handle TLB size < TARGET_PAGE_SIZE. - * If this ever occurs, one should use the ppcemb target instead - * of the ppc or ppc64 one + * If this ever occurs, we should implement TARGET_PAGE_BITS_VARY */ if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) { cpu_abort(cs, "TLB size " TARGET_FMT_lu " < %u " - "are not supported (%d)\n", + "are not supported (%d)\n" + "Please implement TARGET_PAGE_BITS_VARY\n", tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7)); } tlb->EPN = val & ~(tlb->size - 1); diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 9eaa10b421..881743571b 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -211,6 +211,7 @@ struct DisasContext { bool gtse; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; + uint32_t flags; uint64_t insns_flags; uint64_t insns_flags2; }; @@ -251,6 +252,17 @@ struct opc_handler_t { #endif }; +/* SPR load/store helpers */ +static inline void gen_load_spr(TCGv t, int reg) +{ + tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); +} + +static inline void gen_store_spr(int reg, TCGv t) +{ + tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); +} + static inline void gen_set_access_type(DisasContext *ctx, int access_type) { if (ctx->need_access_type && ctx->access_type != access_type) { @@ -313,6 +325,38 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp, ctx->exception = (excp); } +/* Translates the EXCP_TRACE/BRANCH exceptions used on most PowerPCs to + * EXCP_DEBUG, if we are running on cores using the debug enable bit (e.g. + * BookE). + */ +static uint32_t gen_prep_dbgex(DisasContext *ctx, uint32_t excp) +{ + if ((ctx->singlestep_enabled & CPU_SINGLE_STEP) + && (excp == POWERPC_EXCP_BRANCH)) { + /* Trace excpt. has priority */ + excp = POWERPC_EXCP_TRACE; + } + if (ctx->flags & POWERPC_FLAG_DE) { + target_ulong dbsr = 0; + switch (excp) { + case POWERPC_EXCP_TRACE: + dbsr = DBCR0_ICMP; + break; + case POWERPC_EXCP_BRANCH: + dbsr = DBCR0_BRT; + break; + } + TCGv t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_BOOKE_DBSR); + tcg_gen_ori_tl(t0, t0, dbsr); + gen_store_spr(SPR_BOOKE_DBSR, t0); + tcg_temp_free(t0); + return POWERPC_EXCP_DEBUG; + } else { + return excp; + } +} + static void gen_debug_exception(DisasContext *ctx) { TCGv_i32 t0; @@ -575,17 +619,6 @@ typedef struct opcode_t { } #endif -/* SPR load/store helpers */ -static inline void gen_load_spr(TCGv t, int reg) -{ - tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); -} - -static inline void gen_store_spr(int reg, TCGv t) -{ - tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); -} - /* Invalid instruction */ static void gen_invalid(DisasContext *ctx) { @@ -3602,6 +3635,24 @@ static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) #endif } +static void gen_lookup_and_goto_ptr(DisasContext *ctx) +{ + int sse = ctx->singlestep_enabled; + if (unlikely(sse)) { + if (sse & GDBSTUB_SINGLE_STEP) { + gen_debug_exception(ctx); + } else if (sse & (CPU_SINGLE_STEP | CPU_BRANCH_STEP)) { + uint32_t excp = gen_prep_dbgex(ctx, POWERPC_EXCP_BRANCH); + if (excp != POWERPC_EXCP_NONE) { + gen_exception(ctx, excp); + } + } + tcg_gen_exit_tb(NULL, 0); + } else { + tcg_gen_lookup_and_goto_ptr(); + } +} + /*** Branch ***/ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { @@ -3614,18 +3665,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_nip, dest & ~3); - if (unlikely(ctx->singlestep_enabled)) { - if ((ctx->singlestep_enabled & - (CPU_BRANCH_STEP | CPU_SINGLE_STEP)) && - (ctx->exception == POWERPC_EXCP_BRANCH || - ctx->exception == POWERPC_EXCP_TRACE)) { - gen_exception_nip(ctx, POWERPC_EXCP_TRACE, dest); - } - if (ctx->singlestep_enabled & GDBSTUB_SINGLE_STEP) { - gen_debug_exception(ctx); - } - } - tcg_gen_lookup_and_goto_ptr(); + gen_lookup_and_goto_ptr(ctx); } } @@ -3668,8 +3708,8 @@ static void gen_bcond(DisasContext *ctx, int type) uint32_t bo = BO(ctx->opcode); TCGLabel *l1; TCGv target; - ctx->exception = POWERPC_EXCP_BRANCH; + if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { target = tcg_temp_local_new(); if (type == BCOND_CTR) @@ -3733,10 +3773,11 @@ static void gen_bcond(DisasContext *ctx, int type) } else { tcg_gen_andi_tl(cpu_nip, target, ~3); } - tcg_gen_lookup_and_goto_ptr(); + gen_lookup_and_goto_ptr(ctx); tcg_temp_free(target); } if ((bo & 0x14) != 0x14) { + /* fallthrough case */ gen_set_label(l1); gen_goto_tb(ctx, 1, ctx->base.pc_next); } @@ -7419,6 +7460,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->need_access_type = !(env->mmu_model & POWERPC_MMU_64B); ctx->le_mode = !!(env->hflags & (1 << MSR_LE)); ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE; + ctx->flags = env->flags; #if defined(TARGET_PPC64) ctx->sf_mode = msr_is_64bit(env, env->msr); ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); @@ -7455,6 +7497,17 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->singlestep_enabled = 0; if ((env->flags & POWERPC_FLAG_BE) && msr_be) ctx->singlestep_enabled |= CPU_BRANCH_STEP; + if ((env->flags & POWERPC_FLAG_DE) && msr_de) { + ctx->singlestep_enabled = 0; + target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0]; + if (dbcr0 & DBCR0_ICMP) { + ctx->singlestep_enabled |= CPU_SINGLE_STEP; + } + if (dbcr0 & DBCR0_BRT) { + ctx->singlestep_enabled |= CPU_BRANCH_STEP; + } + + } if (unlikely(ctx->base.singlestep_enabled)) { ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP; } @@ -7565,7 +7618,9 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ctx->exception != POWERPC_SYSCALL && ctx->exception != POWERPC_EXCP_TRAP && ctx->exception != POWERPC_EXCP_BRANCH)) { - gen_exception_nip(ctx, POWERPC_EXCP_TRACE, ctx->base.pc_next); + uint32_t excp = gen_prep_dbgex(ctx, POWERPC_EXCP_TRACE); + if (excp != POWERPC_EXCP_NONE) + gen_exception_nip(ctx, excp, ctx->base.pc_next); } if (tcg_check_temp_count()) { diff --git a/target/ppc/translate/fp-impl.inc.c b/target/ppc/translate/fp-impl.inc.c index 2fbd4d4f38..a6f522b85c 100644 --- a/target/ppc/translate/fp-impl.inc.c +++ b/target/ppc/translate/fp-impl.inc.c @@ -660,15 +660,12 @@ GEN_LDUF(name, ldop, op | 0x21, type); \ GEN_LDUXF(name, ldop, op | 0x01, type); \ GEN_LDXF(name, ldop, 0x17, op | 0x00, type) -static inline void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) +static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr) { - TCGv t0 = tcg_temp_new(); - TCGv_i32 t1 = tcg_temp_new_i32(); - gen_qemu_ld32u(ctx, t0, arg2); - tcg_gen_trunc_tl_i32(t1, t0); - tcg_temp_free(t0); - gen_helper_float32_to_float64(arg1, cpu_env, t1); - tcg_temp_free_i32(t1); + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL)); + gen_helper_todouble(dest, tmp); + tcg_temp_free_i32(tmp); } /* lfd lfdu lfdux lfdx */ @@ -836,15 +833,12 @@ GEN_STUF(name, stop, op | 0x21, type); \ GEN_STUXF(name, stop, op | 0x01, type); \ GEN_STXF(name, stop, 0x17, op | 0x00, type) -static inline void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) +static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr) { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv t1 = tcg_temp_new(); - gen_helper_float64_to_float32(t0, cpu_env, arg1); - tcg_gen_extu_i32_tl(t1, t0); - tcg_temp_free_i32(t0); - gen_qemu_st32(ctx, t1, arg2); - tcg_temp_free(t1); + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_tosingle(tmp, src); + tcg_gen_qemu_st_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL)); + tcg_temp_free_i32(tmp); } /* stfd stfdu stfdux stfdx */ diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index 7813b1b004..263e63cb03 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -498,6 +498,7 @@ static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { + gen_store_spr(sprn, cpu_gpr[gprn]); gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ gen_stop_exception(ctx); @@ -1769,6 +1770,14 @@ static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); + spr_register(env, SPR_BOOKE_DSRR0, "DSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_DSRR1, "DSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); /* XXX : not implemented */ spr_register(env, SPR_BOOKE_DBSR, "DBSR", SPR_NOACCESS, SPR_NOACCESS, @@ -1841,6 +1850,14 @@ static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); + spr_register(env, SPR_BOOKE_SPRG8, "SPRG8", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_SPRG9, "SPRG9", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); } static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize, @@ -9630,17 +9647,6 @@ static int ppc_fixup_cpu(PowerPCCPU *cpu) return 0; } -static inline bool ppc_cpu_is_valid(PowerPCCPUClass *pcc) -{ -#ifdef TARGET_PPCEMB - return pcc->mmu_model == POWERPC_MMU_BOOKE || - pcc->mmu_model == POWERPC_MMU_SOFT_4xx || - pcc->mmu_model == POWERPC_MMU_SOFT_4xx_Z; -#else - return true; -#endif -} - static void ppc_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -9664,8 +9670,6 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) } } - assert(ppc_cpu_is_valid(pcc)); - create_ppc_opcodes(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -9916,10 +9920,6 @@ static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b) return -1; } - if (!ppc_cpu_is_valid(pcc)) { - return -1; - } - return pcc->pvr == pvr ? 0 : -1; } @@ -9950,10 +9950,6 @@ static gint ppc_cpu_compare_class_pvr_mask(gconstpointer a, gconstpointer b) return -1; } - if (!ppc_cpu_is_valid(pcc)) { - return -1; - } - if (pcc->pvr_match(pcc, pvr)) { return 0; } @@ -10019,11 +10015,7 @@ static ObjectClass *ppc_cpu_class_by_name(const char *name) g_free(typename); g_free(cpu_model); - if (oc && ppc_cpu_is_valid(POWERPC_CPU_CLASS(oc))) { - return oc; - } - - return NULL; + return oc; } static void ppc_cpu_parse_featurestr(const char *type, char *features, @@ -10129,9 +10121,6 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) char *name; int i; - if (!ppc_cpu_is_valid(pcc)) { - return; - } if (unlikely(strcmp(typename, TYPE_HOST_POWERPC_CPU) == 0)) { return; } @@ -10189,11 +10178,6 @@ static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) const char *typename; CpuDefinitionInfoList *entry; CpuDefinitionInfo *info; - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - if (!ppc_cpu_is_valid(pcc)) { - return; - } typename = object_class_get_name(oc); info = g_malloc0(sizeof(*info)); @@ -10278,6 +10262,8 @@ static void ppc_cpu_reset(CPUState *s) #endif #if defined(CONFIG_USER_ONLY) msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */ + msr |= (target_ulong)1 << MSR_FE0; /* Allow floating point exceptions */ + msr |= (target_ulong)1 << MSR_FE1; msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */ msr |= (target_ulong)1 << MSR_VSX; /* Allow VSX usage */ msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */ |