aboutsummaryrefslogtreecommitdiff
path: root/target-ppc/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target-ppc/translate.c')
-rw-r--r--target-ppc/translate.c271
1 files changed, 210 insertions, 61 deletions
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index 2e32e8d8b8..7c801f36e3 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -203,6 +203,7 @@ struct DisasContext {
int altivec_enabled;
int vsx_enabled;
int spe_enabled;
+ int tm_enabled;
ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
int singlestep_enabled;
uint64_t insns_flags;
@@ -250,26 +251,10 @@ static inline void gen_reset_fpstatus(void)
gen_helper_reset_fpstatus(cpu_env);
}
-static inline void gen_compute_fprf(TCGv_i64 arg, int set_fprf, int set_rc)
+static inline void gen_compute_fprf(TCGv_i64 arg)
{
- TCGv_i32 t0 = tcg_temp_new_i32();
-
- if (set_fprf != 0) {
- /* This case might be optimized later */
- tcg_gen_movi_i32(t0, 1);
- gen_helper_compute_fprf(t0, cpu_env, arg, t0);
- if (unlikely(set_rc)) {
- tcg_gen_mov_i32(cpu_crf[1], t0);
- }
- gen_helper_float_check_status(cpu_env);
- } else if (unlikely(set_rc)) {
- /* We always need to compute fpcc */
- tcg_gen_movi_i32(t0, 0);
- gen_helper_compute_fprf(t0, cpu_env, arg, t0);
- tcg_gen_mov_i32(cpu_crf[1], t0);
- }
-
- tcg_temp_free_i32(t0);
+ gen_helper_compute_fprf(cpu_env, arg);
+ gen_helper_float_check_status(cpu_env);
}
static inline void gen_set_access_type(DisasContext *ctx, int access_type)
@@ -346,11 +331,13 @@ static inline void gen_stop_exception(DisasContext *ctx)
ctx->exception = POWERPC_EXCP_STOP;
}
+#ifndef CONFIG_USER_ONLY
/* No need to update nip here, as execution flow will change */
static inline void gen_sync_exception(DisasContext *ctx)
{
ctx->exception = POWERPC_EXCP_SYNC;
}
+#endif
#define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \
GEN_OPCODE(name, opc1, opc2, opc3, inval, type, PPC_NONE)
@@ -452,7 +439,10 @@ EXTRACT_HELPER(ME, 1, 5);
EXTRACT_HELPER(TO, 21, 5);
EXTRACT_HELPER(CRM, 12, 8);
+
+#ifndef CONFIG_USER_ONLY
EXTRACT_HELPER(SR, 16, 4);
+#endif
/* mtfsf/mtfsfi */
EXTRACT_HELPER(FPBF, 23, 3);
@@ -2077,6 +2067,21 @@ static void gen_srd(DisasContext *ctx)
}
#endif
+#if defined(TARGET_PPC64)
+static void gen_set_cr1_from_fpscr(DisasContext *ctx)
+{
+ TCGv_i32 tmp = tcg_temp_new_i32();
+ tcg_gen_trunc_tl_i32(tmp, cpu_fpscr);
+ tcg_gen_shri_i32(cpu_crf[1], tmp, 28);
+ tcg_temp_free_i32(tmp);
+}
+#else
+static void gen_set_cr1_from_fpscr(DisasContext *ctx)
+{
+ tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28);
+}
+#endif
+
/*** Floating-Point arithmetic ***/
#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \
static void gen_f##name(DisasContext *ctx) \
@@ -2095,8 +2100,12 @@ static void gen_f##name(DisasContext *ctx) \
gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \
cpu_fpr[rD(ctx->opcode)]); \
} \
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], set_fprf, \
- Rc(ctx->opcode) != 0); \
+ if (set_fprf) { \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ if (unlikely(Rc(ctx->opcode) != 0)) { \
+ gen_set_cr1_from_fpscr(ctx); \
+ } \
}
#define GEN_FLOAT_ACB(name, op2, set_fprf, type) \
@@ -2120,8 +2129,12 @@ static void gen_f##name(DisasContext *ctx) \
gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \
cpu_fpr[rD(ctx->opcode)]); \
} \
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
- set_fprf, Rc(ctx->opcode) != 0); \
+ if (set_fprf) { \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ if (unlikely(Rc(ctx->opcode) != 0)) { \
+ gen_set_cr1_from_fpscr(ctx); \
+ } \
}
#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \
_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type); \
@@ -2144,8 +2157,12 @@ static void gen_f##name(DisasContext *ctx) \
gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \
cpu_fpr[rD(ctx->opcode)]); \
} \
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
- set_fprf, Rc(ctx->opcode) != 0); \
+ if (set_fprf) { \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ if (unlikely(Rc(ctx->opcode) != 0)) { \
+ gen_set_cr1_from_fpscr(ctx); \
+ } \
}
#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \
_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type); \
@@ -2163,8 +2180,12 @@ static void gen_f##name(DisasContext *ctx) \
gen_reset_fpstatus(); \
gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \
cpu_fpr[rB(ctx->opcode)]); \
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
- set_fprf, Rc(ctx->opcode) != 0); \
+ if (set_fprf) { \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ if (unlikely(Rc(ctx->opcode) != 0)) { \
+ gen_set_cr1_from_fpscr(ctx); \
+ } \
}
#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \
@@ -2179,8 +2200,12 @@ static void gen_f##name(DisasContext *ctx) \
gen_reset_fpstatus(); \
gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \
cpu_fpr[rB(ctx->opcode)]); \
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], \
- set_fprf, Rc(ctx->opcode) != 0); \
+ if (set_fprf) { \
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]); \
+ } \
+ if (unlikely(Rc(ctx->opcode) != 0)) { \
+ gen_set_cr1_from_fpscr(ctx); \
+ } \
}
/* fadd - fadds */
@@ -2213,7 +2238,10 @@ static void gen_frsqrtes(DisasContext *ctx)
cpu_fpr[rB(ctx->opcode)]);
gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env,
cpu_fpr[rD(ctx->opcode)]);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0)) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
/* fsel */
@@ -2234,7 +2262,10 @@ static void gen_fsqrt(DisasContext *ctx)
gen_reset_fpstatus();
gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env,
cpu_fpr[rB(ctx->opcode)]);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0)) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
static void gen_fsqrts(DisasContext *ctx)
@@ -2250,7 +2281,10 @@ static void gen_fsqrts(DisasContext *ctx)
cpu_fpr[rB(ctx->opcode)]);
gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env,
cpu_fpr[rD(ctx->opcode)]);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 1, Rc(ctx->opcode) != 0);
+ gen_compute_fprf(cpu_fpr[rD(ctx->opcode)]);
+ if (unlikely(Rc(ctx->opcode) != 0)) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
/*** Floating-Point multiply-and-add ***/
@@ -2370,7 +2404,9 @@ static void gen_fabs(DisasContext *ctx)
}
tcg_gen_andi_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)],
~(1ULL << 63));
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+ if (unlikely(Rc(ctx->opcode))) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
/* fmr - fmr. */
@@ -2382,7 +2418,9 @@ static void gen_fmr(DisasContext *ctx)
return;
}
tcg_gen_mov_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+ if (unlikely(Rc(ctx->opcode))) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
/* fnabs */
@@ -2395,7 +2433,9 @@ static void gen_fnabs(DisasContext *ctx)
}
tcg_gen_ori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)],
1ULL << 63);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+ if (unlikely(Rc(ctx->opcode))) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
/* fneg */
@@ -2408,7 +2448,9 @@ static void gen_fneg(DisasContext *ctx)
}
tcg_gen_xori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)],
1ULL << 63);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+ if (unlikely(Rc(ctx->opcode))) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
/* fcpsgn: PowerPC 2.05 specification */
@@ -2421,7 +2463,9 @@ static void gen_fcpsgn(DisasContext *ctx)
}
tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)],
cpu_fpr[rB(ctx->opcode)], 0, 63);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+ if (unlikely(Rc(ctx->opcode))) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
static void gen_fmrgew(DisasContext *ctx)
@@ -2479,7 +2523,9 @@ static void gen_mffs(DisasContext *ctx)
}
gen_reset_fpstatus();
tcg_gen_extu_tl_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpscr);
- gen_compute_fprf(cpu_fpr[rD(ctx->opcode)], 0, Rc(ctx->opcode) != 0);
+ if (unlikely(Rc(ctx->opcode))) {
+ gen_set_cr1_from_fpscr(ctx);
+ }
}
/* mtfsb0 */
@@ -6743,7 +6789,7 @@ static void gen_st##name(DisasContext *ctx) \
tcg_temp_free(EA); \
}
-#define GEN_VR_LVE(name, opc2, opc3) \
+#define GEN_VR_LVE(name, opc2, opc3, size) \
static void gen_lve##name(DisasContext *ctx) \
{ \
TCGv EA; \
@@ -6755,13 +6801,16 @@ static void gen_lve##name(DisasContext *ctx) \
gen_set_access_type(ctx, ACCESS_INT); \
EA = tcg_temp_new(); \
gen_addr_reg_index(ctx, EA); \
+ if (size > 1) { \
+ tcg_gen_andi_tl(EA, EA, ~(size - 1)); \
+ } \
rs = gen_avr_ptr(rS(ctx->opcode)); \
gen_helper_lve##name(cpu_env, rs, EA); \
tcg_temp_free(EA); \
tcg_temp_free_ptr(rs); \
}
-#define GEN_VR_STVE(name, opc2, opc3) \
+#define GEN_VR_STVE(name, opc2, opc3, size) \
static void gen_stve##name(DisasContext *ctx) \
{ \
TCGv EA; \
@@ -6773,6 +6822,9 @@ static void gen_stve##name(DisasContext *ctx) \
gen_set_access_type(ctx, ACCESS_INT); \
EA = tcg_temp_new(); \
gen_addr_reg_index(ctx, EA); \
+ if (size > 1) { \
+ tcg_gen_andi_tl(EA, EA, ~(size - 1)); \
+ } \
rs = gen_avr_ptr(rS(ctx->opcode)); \
gen_helper_stve##name(cpu_env, rs, EA); \
tcg_temp_free(EA); \
@@ -6783,17 +6835,17 @@ GEN_VR_LDX(lvx, 0x07, 0x03);
/* As we don't emulate the cache, lvxl is stricly equivalent to lvx */
GEN_VR_LDX(lvxl, 0x07, 0x0B);
-GEN_VR_LVE(bx, 0x07, 0x00);
-GEN_VR_LVE(hx, 0x07, 0x01);
-GEN_VR_LVE(wx, 0x07, 0x02);
+GEN_VR_LVE(bx, 0x07, 0x00, 1);
+GEN_VR_LVE(hx, 0x07, 0x01, 2);
+GEN_VR_LVE(wx, 0x07, 0x02, 4);
GEN_VR_STX(svx, 0x07, 0x07);
/* As we don't emulate the cache, stvxl is stricly equivalent to stvx */
GEN_VR_STX(svxl, 0x07, 0x0F);
-GEN_VR_STVE(bx, 0x07, 0x04);
-GEN_VR_STVE(hx, 0x07, 0x05);
-GEN_VR_STVE(wx, 0x07, 0x06);
+GEN_VR_STVE(bx, 0x07, 0x04, 1);
+GEN_VR_STVE(hx, 0x07, 0x05, 2);
+GEN_VR_STVE(wx, 0x07, 0x06, 4);
static void gen_lvsl(DisasContext *ctx)
{
@@ -8205,21 +8257,6 @@ static inline TCGv_ptr gen_fprp_ptr(int reg)
return r;
}
-#if defined(TARGET_PPC64)
-static void gen_set_cr1_from_fpscr(DisasContext *ctx)
-{
- TCGv_i32 tmp = tcg_temp_new_i32();
- tcg_gen_trunc_tl_i32(tmp, cpu_fpscr);
- tcg_gen_shri_i32(cpu_crf[1], tmp, 28);
- tcg_temp_free_i32(tmp);
-}
-#else
-static void gen_set_cr1_from_fpscr(DisasContext *ctx)
-{
- tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28);
-}
-#endif
-
#define GEN_DFP_T_A_B_Rc(name) \
static void gen_##name(DisasContext *ctx) \
{ \
@@ -9642,6 +9679,88 @@ GEN_SPE(efdctsiz, speundef, 0x1D, 0x0B, 0x00180000, 0xFFFFFFFF, PPC_SPE_DOUBLE
GEN_SPE(efdtstgt, efdtstlt, 0x1E, 0x0B, 0x00600000, 0x00600000, PPC_SPE_DOUBLE); //
GEN_SPE(efdtsteq, speundef, 0x1F, 0x0B, 0x00600000, 0xFFFFFFFF, PPC_SPE_DOUBLE); //
+static void gen_tbegin(DisasContext *ctx)
+{
+ if (unlikely(!ctx->tm_enabled)) {
+ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM);
+ return;
+ }
+ gen_helper_tbegin(cpu_env);
+}
+
+#define GEN_TM_NOOP(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(!ctx->tm_enabled)) { \
+ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \
+ return; \
+ } \
+ /* Because tbegin always fails in QEMU, these user \
+ * space instructions all have a simple implementation: \
+ * \
+ * CR[0] = 0b0 || MSR[TS] || 0b0 \
+ * = 0b0 || 0b00 || 0b0 \
+ */ \
+ tcg_gen_movi_i32(cpu_crf[0], 0); \
+}
+
+GEN_TM_NOOP(tend);
+GEN_TM_NOOP(tabort);
+GEN_TM_NOOP(tabortwc);
+GEN_TM_NOOP(tabortwci);
+GEN_TM_NOOP(tabortdc);
+GEN_TM_NOOP(tabortdci);
+GEN_TM_NOOP(tsr);
+
+static void gen_tcheck(DisasContext *ctx)
+{
+ if (unlikely(!ctx->tm_enabled)) {
+ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM);
+ return;
+ }
+ /* Because tbegin always fails, the tcheck implementation
+ * is simple:
+ *
+ * CR[CRF] = TDOOMED || MSR[TS] || 0b0
+ * = 0b1 || 0b00 || 0b0
+ */
+ tcg_gen_movi_i32(cpu_crf[crfD(ctx->opcode)], 0x8);
+}
+
+#if defined(CONFIG_USER_ONLY)
+#define GEN_TM_PRIV_NOOP(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \
+}
+
+#else
+
+#define GEN_TM_PRIV_NOOP(name) \
+static inline void gen_##name(DisasContext *ctx) \
+{ \
+ if (unlikely(ctx->pr)) { \
+ gen_inval_exception(ctx, POWERPC_EXCP_PRIV_OPC); \
+ return; \
+ } \
+ if (unlikely(!ctx->tm_enabled)) { \
+ gen_exception_err(ctx, POWERPC_EXCP_FU, FSCR_IC_TM); \
+ return; \
+ } \
+ /* Because tbegin always fails, the implementation is \
+ * simple: \
+ * \
+ * CR[0] = 0b0 || MSR[TS] || 0b0 \
+ * = 0b0 || 0b00 | 0b0 \
+ */ \
+ tcg_gen_movi_i32(cpu_crf[0], 0); \
+}
+
+#endif
+
+GEN_TM_PRIV_NOOP(treclaim);
+GEN_TM_PRIV_NOOP(trechkpt);
+
static opcode_t opcodes[] = {
GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE),
GEN_HANDLER(cmp, 0x1F, 0x00, 0x00, 0x00400000, PPC_INTEGER),
@@ -11054,6 +11173,29 @@ GEN_SPEOP_LDST(evstwhe, 0x18, 2),
GEN_SPEOP_LDST(evstwho, 0x1A, 2),
GEN_SPEOP_LDST(evstwwe, 0x1C, 2),
GEN_SPEOP_LDST(evstwwo, 0x1E, 2),
+
+GEN_HANDLER2_E(tbegin, "tbegin", 0x1F, 0x0E, 0x14, 0x01DFF800, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tend, "tend", 0x1F, 0x0E, 0x15, 0x01FFF800, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tabort, "tabort", 0x1F, 0x0E, 0x1C, 0x03E0F800, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tabortwc, "tabortwc", 0x1F, 0x0E, 0x18, 0x00000000, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tabortwci, "tabortwci", 0x1F, 0x0E, 0x1A, 0x00000000, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tabortdc, "tabortdc", 0x1F, 0x0E, 0x19, 0x00000000, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tabortdci, "tabortdci", 0x1F, 0x0E, 0x1B, 0x00000000, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tsr, "tsr", 0x1F, 0x0E, 0x17, 0x03DFF800, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(tcheck, "tcheck", 0x1F, 0x0E, 0x16, 0x007FF800, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(treclaim, "treclaim", 0x1F, 0x0E, 0x1D, 0x03E0F800, \
+ PPC_NONE, PPC2_TM),
+GEN_HANDLER2_E(trechkpt, "trechkpt", 0x1F, 0x0E, 0x1F, 0x03FFF800, \
+ PPC_NONE, PPC2_TM),
};
#include "helper_regs.h"
@@ -11311,6 +11453,13 @@ static inline void gen_intermediate_code_internal(PowerPCCPU *cpu,
} else {
ctx.vsx_enabled = 0;
}
+#if defined(TARGET_PPC64)
+ if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
+ ctx.tm_enabled = msr_tm;
+ } else {
+ ctx.tm_enabled = 0;
+ }
+#endif
if ((env->flags & POWERPC_FLAG_SE) && msr_se)
ctx.singlestep_enabled = CPU_SINGLE_STEP;
else