aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-08-21 13:27:11 +0100
committerPeter Maydell <peter.maydell@linaro.org>2018-08-21 13:27:11 +0100
commitee135aa0428fe5af2af7be04ff16d2b596a9330a (patch)
treee88095d1ea78607c88962a0fc89f3b63765979d4 /target
parent659b11e7a7239529cfdb4968418268ff9aa22d88 (diff)
parent0e3bf4890906fa7066a5deafd6ab033934b8d100 (diff)
downloadqemu-ee135aa0428fe5af2af7be04ff16d2b596a9330a.zip
qemu-ee135aa0428fe5af2af7be04ff16d2b596a9330a.tar.gz
qemu-ee135aa0428fe5af2af7be04ff16d2b596a9330a.tar.bz2
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-3.1-20180821' into staging
ppc patch queue 2018-08-21 Here's my first ppc & spapr pull request for qemu-3.1. This contains a bunch of things that have accumulated while 3.0 was in freeze. Highlights are: * SLOF firmware update * A number of floating point cleanups from Richard Henderson and Yasmin Beatriz * A new model for assigning irq numbers on spapr, this is an important preliminary step towards implementing the POWER9 "XIVE" interrupt controller # gpg: Signature made Tue 21 Aug 2018 05:32:44 BST # gpg: using RSA key 6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-3.1-20180821: (26 commits) ppc: add DBCR based debugging spapr_pci: factorize the use of SPAPR_MACHINE_GET_CLASS() mac_newworld: don't use legacy fw_cfg_init_mem() function mac_oldworld: don't use legacy fw_cfg_init_mem() function 40p: don't use legacy fw_cfg_init_mem() function qemu-doc: mark ppc/prep machine as deprecated hw/ppc: deprecate the machine type 'prep', replaced by '40p' spapr: introduce a IRQ controller backend to the machine hw/ppc/ppc405_uc: Convert away from old_mmio hw/ppc/ppc_boards: Don't use old_mmio for ref405ep_fpga hw/ppc/prep: Remove ifdeffed-out stub of XCSR code spapr: introduce a fixed IRQ number space spapr: Add a pseries-3.1 machine type target/ppc: simplify bcdadd/sub functions xics: don't include "target/ppc/cpu-qom.h" in "hw/ppc/xics.h" vfio/spapr: Allow backing bigger guest IOMMU pages with smaller physical pages target/ppc: bcdsub fix sign when result is zero target/ppc: Use non-arithmetic conversions for fp load/store target/ppc: Honor fpscr_ze semantics and tidy fre, fresqrt target/ppc: Tidy helper_fsqrt ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target')
-rw-r--r--target/ppc/cpu.h5
-rw-r--r--target/ppc/excp_helper.c11
-rw-r--r--target/ppc/fpu_helper.c292
-rw-r--r--target/ppc/helper.h14
-rw-r--r--target/ppc/int_helper.c46
-rw-r--r--target/ppc/translate.c107
-rw-r--r--target/ppc/translate/fp-impl.inc.c26
-rw-r--r--target/ppc/translate_init.inc.c19
8 files changed, 316 insertions, 204 deletions
diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index 4edcf62..ec14934 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -481,6 +481,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 d6e97a9..0ec7ae1 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 8675d93..b9bb1b8 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 5706c24..ef64248 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 d52338e..fcac90a 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/translate.c b/target/ppc/translate.c
index 9eaa10b..8817435 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 2fbd4d4..a6f522b 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 7813b1b..d920d3e 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,
@@ -10278,6 +10295,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 */