aboutsummaryrefslogtreecommitdiff
path: root/target/microblaze/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/microblaze/translate.c')
-rw-r--r--target/microblaze/translate.c194
1 files changed, 130 insertions, 64 deletions
diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c
index a8a3249..abfcc7e 100644
--- a/target/microblaze/translate.c
+++ b/target/microblaze/translate.c
@@ -37,7 +37,12 @@
/* is_jmp field values */
#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */
-#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */
+#define DISAS_EXIT DISAS_TARGET_1 /* all cpu state modified dynamically */
+
+/* cpu state besides pc was modified dynamically; update pc to next */
+#define DISAS_EXIT_NEXT DISAS_TARGET_2
+/* cpu state besides pc was modified dynamically; update pc to btarget */
+#define DISAS_EXIT_JUMP DISAS_TARGET_3
static TCGv_i32 cpu_R[32];
static TCGv_i32 cpu_pc;
@@ -55,7 +60,7 @@ static TCGv_i32 cpu_res_val;
/* This is the state at translation time. */
typedef struct DisasContext {
DisasContextBase base;
- MicroBlazeCPU *cpu;
+ const MicroBlazeCPUConfig *cfg;
/* TCG op of the current insn_start. */
TCGOp *insn_start;
@@ -65,7 +70,6 @@ typedef struct DisasContext {
/* Decoder. */
uint32_t ext_imm;
- unsigned int cpustate_changed;
unsigned int tb_flags;
unsigned int tb_flags_to_set;
int mem_index;
@@ -143,7 +147,7 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
tcg_gen_exit_tb(dc->base.tb, n);
} else {
tcg_gen_movi_i32(cpu_pc, dest);
- tcg_gen_exit_tb(NULL, 0);
+ tcg_gen_lookup_and_goto_ptr();
}
dc->base.is_jmp = DISAS_NORETURN;
}
@@ -155,7 +159,7 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest)
static bool trap_illegal(DisasContext *dc, bool cond)
{
if (cond && (dc->tb_flags & MSR_EE)
- && dc->cpu->cfg.illegal_opcode_exception) {
+ && dc->cfg->illegal_opcode_exception) {
gen_raise_hw_excp(dc, ESR_EC_ILLEGAL_OP);
}
return cond;
@@ -175,6 +179,21 @@ static bool trap_userspace(DisasContext *dc, bool cond)
return cond_user;
}
+/*
+ * Return true, and log an error, if the current insn is
+ * within a delay slot.
+ */
+static bool invalid_delay_slot(DisasContext *dc, const char *insn_type)
+{
+ if (dc->tb_flags & D_FLAG) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Invalid insn in delay slot: %s at %08x\n",
+ insn_type, (uint32_t)dc->base.pc_next);
+ return true;
+ }
+ return false;
+}
+
static TCGv_i32 reg_for_read(DisasContext *dc, int reg)
{
if (likely(reg != 0)) {
@@ -272,7 +291,7 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects,
#define DO_TYPEA_CFG(NAME, CFG, SE, FN) \
static bool trans_##NAME(DisasContext *dc, arg_typea *a) \
- { return dc->cpu->cfg.CFG && do_typea(dc, a, SE, FN); }
+ { return dc->cfg->CFG && do_typea(dc, a, SE, FN); }
#define DO_TYPEA0(NAME, SE, FN) \
static bool trans_##NAME(DisasContext *dc, arg_typea0 *a) \
@@ -280,7 +299,7 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects,
#define DO_TYPEA0_CFG(NAME, CFG, SE, FN) \
static bool trans_##NAME(DisasContext *dc, arg_typea0 *a) \
- { return dc->cpu->cfg.CFG && do_typea0(dc, a, SE, FN); }
+ { return dc->cfg->CFG && do_typea0(dc, a, SE, FN); }
#define DO_TYPEBI(NAME, SE, FNI) \
static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \
@@ -288,7 +307,7 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects,
#define DO_TYPEBI_CFG(NAME, CFG, SE, FNI) \
static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \
- { return dc->cpu->cfg.CFG && do_typeb_imm(dc, a, SE, FNI); }
+ { return dc->cfg->CFG && do_typeb_imm(dc, a, SE, FNI); }
#define DO_TYPEBV(NAME, SE, FN) \
static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \
@@ -496,6 +515,9 @@ DO_TYPEA_CFG(idivu, use_div, true, gen_idivu)
static bool trans_imm(DisasContext *dc, arg_imm *arg)
{
+ if (invalid_delay_slot(dc, "imm")) {
+ return true;
+ }
dc->ext_imm = arg->imm << 16;
tcg_gen_movi_i32(cpu_imm, dc->ext_imm);
dc->tb_flags_to_set = IMM_FLAG;
@@ -661,7 +683,7 @@ static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb)
tcg_gen_movi_tl(ret, 0);
}
- if ((ra == 1 || rb == 1) && dc->cpu->cfg.stackprot) {
+ if ((ra == 1 || rb == 1) && dc->cfg->stackprot) {
gen_helper_stackprot(cpu_env, ret);
}
return ret;
@@ -681,7 +703,7 @@ static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm)
tcg_gen_movi_tl(ret, (uint32_t)imm);
}
- if (ra == 1 && dc->cpu->cfg.stackprot) {
+ if (ra == 1 && dc->cfg->stackprot) {
gen_helper_stackprot(cpu_env, ret);
}
return ret;
@@ -690,7 +712,7 @@ static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm)
#ifndef CONFIG_USER_ONLY
static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb)
{
- int addr_size = dc->cpu->cfg.addr_size;
+ int addr_size = dc->cfg->addr_size;
TCGv ret = tcg_temp_new();
if (addr_size == 32 || ra == 0) {
@@ -750,7 +772,7 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop,
if (size > MO_8 &&
(dc->tb_flags & MSR_EE) &&
- dc->cpu->cfg.unaligned_exceptions) {
+ dc->cfg->unaligned_exceptions) {
record_unaligned_ess(dc, rd, size, false);
mop |= MO_ALIGN;
}
@@ -896,7 +918,7 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop,
if (size > MO_8 &&
(dc->tb_flags & MSR_EE) &&
- dc->cpu->cfg.unaligned_exceptions) {
+ dc->cfg->unaligned_exceptions) {
record_unaligned_ess(dc, rd, size, true);
mop |= MO_ALIGN;
}
@@ -1063,6 +1085,9 @@ static bool do_branch(DisasContext *dc, int dest_rb, int dest_imm,
{
uint32_t add_pc;
+ if (invalid_delay_slot(dc, "branch")) {
+ return true;
+ }
if (delay) {
setup_dslot(dc, dest_rb < 0);
}
@@ -1102,6 +1127,9 @@ static bool do_bcc(DisasContext *dc, int dest_rb, int dest_imm,
{
TCGv_i32 zero, next;
+ if (invalid_delay_slot(dc, "bcc")) {
+ return true;
+ }
if (delay) {
setup_dslot(dc, dest_rb < 0);
}
@@ -1154,6 +1182,10 @@ static bool trans_brk(DisasContext *dc, arg_typea_br *arg)
if (trap_userspace(dc, true)) {
return true;
}
+ if (invalid_delay_slot(dc, "brk")) {
+ return true;
+ }
+
tcg_gen_mov_i32(cpu_pc, reg_for_read(dc, arg->rb));
if (arg->rd) {
tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next);
@@ -1161,7 +1193,7 @@ static bool trans_brk(DisasContext *dc, arg_typea_br *arg)
tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_BIP);
tcg_gen_movi_tl(cpu_res_addr, -1);
- dc->base.is_jmp = DISAS_UPDATE;
+ dc->base.is_jmp = DISAS_EXIT;
return true;
}
@@ -1172,6 +1204,10 @@ static bool trans_brki(DisasContext *dc, arg_typeb_br *arg)
if (trap_userspace(dc, imm != 0x8 && imm != 0x18)) {
return true;
}
+ if (invalid_delay_slot(dc, "brki")) {
+ return true;
+ }
+
tcg_gen_movi_i32(cpu_pc, imm);
if (arg->rd) {
tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next);
@@ -1202,7 +1238,7 @@ static bool trans_brki(DisasContext *dc, arg_typeb_br *arg)
~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM));
}
tcg_gen_ori_i32(cpu_msr, cpu_msr, msr_to_set);
- dc->base.is_jmp = DISAS_UPDATE;
+ dc->base.is_jmp = DISAS_EXIT;
#endif
return true;
@@ -1212,6 +1248,11 @@ static bool trans_mbar(DisasContext *dc, arg_mbar *arg)
{
int mbar_imm = arg->imm;
+ /* Note that mbar is a specialized branch instruction. */
+ if (invalid_delay_slot(dc, "mbar")) {
+ return true;
+ }
+
/* Data access memory barrier. */
if ((mbar_imm & 2) == 0) {
tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL);
@@ -1250,7 +1291,7 @@ static bool trans_mbar(DisasContext *dc, arg_mbar *arg)
*
* Therefore, choose to end the TB always.
*/
- dc->cpustate_changed = 1;
+ dc->base.is_jmp = DISAS_EXIT_NEXT;
return true;
}
@@ -1259,6 +1300,10 @@ static bool do_rts(DisasContext *dc, arg_typeb_bc *arg, int to_set)
if (trap_userspace(dc, to_set)) {
return true;
}
+ if (invalid_delay_slot(dc, "rts")) {
+ return true;
+ }
+
dc->tb_flags_to_set |= to_set;
setup_dslot(dc, true);
@@ -1280,7 +1325,7 @@ DO_RTS(rtsd, 0)
static bool trans_zero(DisasContext *dc, arg_zero *arg)
{
/* If opcode_0_illegal, trap. */
- if (dc->cpu->cfg.opcode_0_illegal) {
+ if (dc->cfg->opcode_0_illegal) {
trap_illegal(dc, true);
return true;
}
@@ -1302,19 +1347,6 @@ static void msr_read(DisasContext *dc, TCGv_i32 d)
tcg_temp_free_i32(t);
}
-#ifndef CONFIG_USER_ONLY
-static void msr_write(DisasContext *dc, TCGv_i32 v)
-{
- dc->cpustate_changed = 1;
-
- /* Install MSR_C. */
- tcg_gen_extract_i32(cpu_msr_c, v, 2, 1);
-
- /* Clear MSR_C and MSR_CC; MSR_PVR is not writable, and is always clear. */
- tcg_gen_andi_i32(cpu_msr, v, ~(MSR_C | MSR_CC | MSR_PVR));
-}
-#endif
-
static bool do_msrclrset(DisasContext *dc, arg_type_msr *arg, bool set)
{
uint32_t imm = arg->imm;
@@ -1347,7 +1379,7 @@ static bool do_msrclrset(DisasContext *dc, arg_type_msr *arg, bool set)
} else {
tcg_gen_andi_i32(cpu_msr, cpu_msr, ~imm);
}
- dc->cpustate_changed = 1;
+ dc->base.is_jmp = DISAS_EXIT_NEXT;
}
return true;
}
@@ -1380,7 +1412,13 @@ static bool trans_mts(DisasContext *dc, arg_mts *arg)
TCGv_i32 src = reg_for_read(dc, arg->ra);
switch (arg->rs) {
case SR_MSR:
- msr_write(dc, src);
+ /* Install MSR_C. */
+ tcg_gen_extract_i32(cpu_msr_c, src, 2, 1);
+ /*
+ * Clear MSR_C and MSR_CC;
+ * MSR_PVR is not writable, and is always clear.
+ */
+ tcg_gen_andi_i32(cpu_msr, src, ~(MSR_C | MSR_CC | MSR_PVR));
break;
case SR_FSR:
tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, fsr));
@@ -1412,7 +1450,7 @@ static bool trans_mts(DisasContext *dc, arg_mts *arg)
qemu_log_mask(LOG_GUEST_ERROR, "Invalid mts reg 0x%x\n", arg->rs);
return true;
}
- dc->cpustate_changed = 1;
+ dc->base.is_jmp = DISAS_EXIT_NEXT;
return true;
#endif
}
@@ -1501,7 +1539,8 @@ static bool trans_mfs(DisasContext *dc, arg_mfs *arg)
case 0x2000 ... 0x200c:
tcg_gen_ld_i32(dest, cpu_env,
- offsetof(CPUMBState, pvr.regs[arg->rs - 0x2000]));
+ offsetof(MicroBlazeCPU, cfg.pvr_regs[arg->rs - 0x2000])
+ - offsetof(MicroBlazeCPU, env));
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "Invalid mfs reg 0x%x\n", arg->rs);
@@ -1521,7 +1560,6 @@ static void do_rti(DisasContext *dc)
tcg_gen_or_i32(cpu_msr, cpu_msr, tmp);
tcg_temp_free_i32(tmp);
- dc->tb_flags &= ~DRTI_FLAG;
}
static void do_rtb(DisasContext *dc)
@@ -1534,7 +1572,6 @@ static void do_rtb(DisasContext *dc)
tcg_gen_or_i32(cpu_msr, cpu_msr, tmp);
tcg_temp_free_i32(tmp);
- dc->tb_flags &= ~DRTB_FLAG;
}
static void do_rte(DisasContext *dc)
@@ -1548,7 +1585,6 @@ static void do_rte(DisasContext *dc)
tcg_gen_or_i32(cpu_msr, cpu_msr, tmp);
tcg_temp_free_i32(tmp);
- dc->tb_flags &= ~DRTE_FLAG;
}
/* Insns connected to FSL or AXI stream attached devices. */
@@ -1622,9 +1658,8 @@ static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs)
MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs);
int bound;
- dc->cpu = cpu;
+ dc->cfg = &cpu->cfg;
dc->tb_flags = dc->base.tb->flags;
- dc->cpustate_changed = 0;
dc->ext_imm = dc->base.tb->cs_base;
dc->r0 = NULL;
dc->r0_set = false;
@@ -1700,20 +1735,47 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs)
dc->base.pc_next += 4;
if (dc->jmp_cond != TCG_COND_NEVER && !(dc->tb_flags & D_FLAG)) {
- if (dc->tb_flags & DRTI_FLAG) {
- do_rti(dc);
- } else if (dc->tb_flags & DRTB_FLAG) {
- do_rtb(dc);
- } else if (dc->tb_flags & DRTE_FLAG) {
- do_rte(dc);
+ /*
+ * Finish any return-from branch.
+ */
+ uint32_t rt_ibe = dc->tb_flags & (DRTI_FLAG | DRTB_FLAG | DRTE_FLAG);
+ if (unlikely(rt_ibe != 0)) {
+ dc->tb_flags &= ~(DRTI_FLAG | DRTB_FLAG | DRTE_FLAG);
+ if (rt_ibe & DRTI_FLAG) {
+ do_rti(dc);
+ } else if (rt_ibe & DRTB_FLAG) {
+ do_rtb(dc);
+ } else {
+ do_rte(dc);
+ }
}
- dc->base.is_jmp = DISAS_JUMP;
- }
- /* Force an exit if the per-tb cpu state has changed. */
- if (dc->base.is_jmp == DISAS_NEXT && dc->cpustate_changed) {
- dc->base.is_jmp = DISAS_UPDATE;
- tcg_gen_movi_i32(cpu_pc, dc->base.pc_next);
+ /* Complete the branch, ending the TB. */
+ switch (dc->base.is_jmp) {
+ case DISAS_NORETURN:
+ /*
+ * E.g. illegal insn in a delay slot. We've already exited
+ * and will handle D_FLAG in mb_cpu_do_interrupt.
+ */
+ break;
+ case DISAS_NEXT:
+ /*
+ * Normal insn a delay slot.
+ * However, the return-from-exception type insns should
+ * return to the main loop, as they have adjusted MSR.
+ */
+ dc->base.is_jmp = (rt_ibe ? DISAS_EXIT_JUMP : DISAS_JUMP);
+ break;
+ case DISAS_EXIT_NEXT:
+ /*
+ * E.g. mts insn in a delay slot. Continue with btarget,
+ * but still return to the main loop.
+ */
+ dc->base.is_jmp = DISAS_EXIT_JUMP;
+ break;
+ default:
+ g_assert_not_reached();
+ }
}
}
@@ -1733,13 +1795,15 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs)
gen_goto_tb(dc, 0, dc->base.pc_next);
return;
- case DISAS_UPDATE:
- if (unlikely(cs->singlestep_enabled)) {
- gen_raise_exception(dc, EXCP_DEBUG);
- } else {
- tcg_gen_exit_tb(NULL, 0);
- }
- return;
+ case DISAS_EXIT:
+ break;
+ case DISAS_EXIT_NEXT:
+ tcg_gen_movi_i32(cpu_pc, dc->base.pc_next);
+ break;
+ case DISAS_EXIT_JUMP:
+ tcg_gen_mov_i32(cpu_pc, cpu_btarget);
+ tcg_gen_discard_i32(cpu_btarget);
+ break;
case DISAS_JUMP:
if (dc->jmp_dest != -1 && !cs->singlestep_enabled) {
@@ -1774,13 +1838,20 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs)
if (unlikely(cs->singlestep_enabled)) {
gen_raise_exception(dc, EXCP_DEBUG);
} else {
- tcg_gen_exit_tb(NULL, 0);
+ tcg_gen_lookup_and_goto_ptr();
}
return;
default:
g_assert_not_reached();
}
+
+ /* Finish DISAS_EXIT_* */
+ if (unlikely(cs->singlestep_enabled)) {
+ gen_raise_exception(dc, EXCP_DEBUG);
+ } else {
+ tcg_gen_exit_tb(NULL, 0);
+ }
}
static void mb_tr_disas_log(const DisasContextBase *dcb, CPUState *cs)
@@ -1848,11 +1919,6 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, int flags)
env->esr, env->fsr, env->btr, env->edr,
env->ear, env->slr, env->shr);
- for (i = 0; i < 12; i++) {
- qemu_fprintf(f, "rpvr%-2d=%08x%c",
- i, env->pvr.regs[i], i % 4 == 3 ? '\n' : ' ');
- }
-
for (i = 0; i < 32; i++) {
qemu_fprintf(f, "r%2.2d=%08x%c",
i, env->regs[i], i % 4 == 3 ? '\n' : ' ');