]> Git Repo - qemu.git/blobdiff - target/hppa/translate.c
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
[qemu.git] / target / hppa / translate.c
index 026ba5cb3e9b5dc519eff2ebfdd162028d2e3166..188fe688cbeca2179e81bd6ff8c0d84f76572355 100644 (file)
@@ -279,7 +279,7 @@ typedef struct DisasContext {
 } DisasContext;
 
 /* Note that ssm/rsm instructions number PSW_W and PSW_E differently.  */
-static int expand_sm_imm(int val)
+static int expand_sm_imm(DisasContext *ctx, int val)
 {
     if (val & PSW_SM_E) {
         val = (val & ~PSW_SM_E) | PSW_E;
@@ -291,43 +291,43 @@ static int expand_sm_imm(int val)
 }
 
 /* Inverted space register indicates 0 means sr0 not inferred from base.  */
-static int expand_sr3x(int val)
+static int expand_sr3x(DisasContext *ctx, int val)
 {
     return ~val;
 }
 
 /* Convert the M:A bits within a memory insn to the tri-state value
    we use for the final M.  */
-static int ma_to_m(int val)
+static int ma_to_m(DisasContext *ctx, int val)
 {
     return val & 2 ? (val & 1 ? -1 : 1) : 0;
 }
 
 /* Convert the sign of the displacement to a pre or post-modify.  */
-static int pos_to_m(int val)
+static int pos_to_m(DisasContext *ctx, int val)
 {
     return val ? 1 : -1;
 }
 
-static int neg_to_m(int val)
+static int neg_to_m(DisasContext *ctx, int val)
 {
     return val ? -1 : 1;
 }
 
 /* Used for branch targets and fp memory ops.  */
-static int expand_shl2(int val)
+static int expand_shl2(DisasContext *ctx, int val)
 {
     return val << 2;
 }
 
 /* Used for fp memory ops.  */
-static int expand_shl3(int val)
+static int expand_shl3(DisasContext *ctx, int val)
 {
     return val << 3;
 }
 
 /* Used for assemble_21.  */
-static int expand_shl11(int val)
+static int expand_shl11(DisasContext *ctx, int val)
 {
     return val << 11;
 }
@@ -347,6 +347,7 @@ static int expand_shl11(int val)
 /* Similarly, but we want to return to the main loop immediately
    to recognize unmasked interrupts.  */
 #define DISAS_IAQ_N_STALE_EXIT      DISAS_TARGET_2
+#define DISAS_EXIT                  DISAS_TARGET_3
 
 /* global register indexes */
 static TCGv_reg cpu_gr[32];
@@ -816,12 +817,10 @@ static bool gen_illegal(DisasContext *ctx)
 
 static bool use_goto_tb(DisasContext *ctx, target_ureg dest)
 {
-    /* Suppress goto_tb in the case of single-steping and IO.  */
-    if ((tb_cflags(ctx->base.tb) & CF_LAST_IO)
-        || ctx->base.singlestep_enabled) {
-        return false;
-    }
-    return true;
+    /* Suppress goto_tb for page crossing, IO, or single-steping.  */
+    return !(((ctx->base.pc_first ^ dest) & TARGET_PAGE_MASK)
+             || (tb_cflags(ctx->base.tb) & CF_LAST_IO)
+             || ctx->base.singlestep_enabled);
 }
 
 /* If the next insn is to be nullified, and it's on the same page,
@@ -2007,16 +2006,15 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset)
         /* Privilege 0 is maximum and is allowed to decrease.  */
         return offset;
     case 3:
-        /* Privilege 3 is minimum and is never allowed increase.  */
+        /* Privilege 3 is minimum and is never allowed to increase.  */
         dest = get_temp(ctx);
         tcg_gen_ori_reg(dest, offset, 3);
         break;
     default:
-        dest = tcg_temp_new();
+        dest = get_temp(ctx);
         tcg_gen_andi_reg(dest, offset, -4);
         tcg_gen_ori_reg(dest, dest, ctx->privilege);
         tcg_gen_movcond_reg(TCG_COND_GTU, dest, dest, offset, dest, offset);
-        tcg_temp_free(dest);
         break;
     }
     return dest;
@@ -2259,6 +2257,16 @@ static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a)
                        offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ]));
         break;
 
+    case CR_PID1:
+    case CR_PID2:
+    case CR_PID3:
+    case CR_PID4:
+        tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
+#ifndef CONFIG_USER_ONLY
+        gen_helper_change_prot_id(cpu_env);
+#endif
+        break;
+
     default:
         tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl]));
         break;
@@ -2475,9 +2483,8 @@ static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a)
         gen_helper_itlbp(cpu_env, addr, reg);
     }
 
-    /* Exit TB for ITLB change if mmu is enabled.  This *should* not be
-       the case, since the OS TLB fill handler runs with mmu disabled.  */
-    if (!a->data && (ctx->tb_flags & PSW_C)) {
+    /* Exit TB for TLB change if mmu is enabled.  */
+    if (ctx->tb_flags & PSW_C) {
         ctx->base.is_jmp = DISAS_IAQ_N_STALE;
     }
     return nullify_end(ctx);
@@ -2504,7 +2511,61 @@ static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a)
     }
 
     /* Exit TB for TLB change if mmu is enabled.  */
-    if (!a->data && (ctx->tb_flags & PSW_C)) {
+    if (ctx->tb_flags & PSW_C) {
+        ctx->base.is_jmp = DISAS_IAQ_N_STALE;
+    }
+    return nullify_end(ctx);
+#endif
+}
+
+/*
+ * Implement the pcxl and pcxl2 Fast TLB Insert instructions.
+ * See
+ *     https://parisc.wiki.kernel.org/images-parisc/a/a9/Pcxl2_ers.pdf
+ *     page 13-9 (195/206)
+ */
+static bool trans_ixtlbxf(DisasContext *ctx, arg_ixtlbxf *a)
+{
+    CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+#ifndef CONFIG_USER_ONLY
+    TCGv_tl addr, atl, stl;
+    TCGv_reg reg;
+
+    nullify_over(ctx);
+
+    /*
+     * FIXME:
+     *  if (not (pcxl or pcxl2))
+     *    return gen_illegal(ctx);
+     *
+     * Note for future: these are 32-bit systems; no hppa64.
+     */
+
+    atl = tcg_temp_new_tl();
+    stl = tcg_temp_new_tl();
+    addr = tcg_temp_new_tl();
+
+    tcg_gen_ld32u_i64(stl, cpu_env,
+                      a->data ? offsetof(CPUHPPAState, cr[CR_ISR])
+                      : offsetof(CPUHPPAState, cr[CR_IIASQ]));
+    tcg_gen_ld32u_i64(atl, cpu_env,
+                      a->data ? offsetof(CPUHPPAState, cr[CR_IOR])
+                      : offsetof(CPUHPPAState, cr[CR_IIAOQ]));
+    tcg_gen_shli_i64(stl, stl, 32);
+    tcg_gen_or_tl(addr, atl, stl);
+    tcg_temp_free_tl(atl);
+    tcg_temp_free_tl(stl);
+
+    reg = load_gpr(ctx, a->r);
+    if (a->addr) {
+        gen_helper_itlba(cpu_env, addr, reg);
+    } else {
+        gen_helper_itlbp(cpu_env, addr, reg);
+    }
+    tcg_temp_free_tl(addr);
+
+    /* Exit TB for TLB change if mmu is enabled.  */
+    if (ctx->tb_flags & PSW_C) {
         ctx->base.is_jmp = DISAS_IAQ_N_STALE;
     }
     return nullify_end(ctx);
@@ -2744,7 +2805,7 @@ static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i)
     }
     tcg_gen_andi_reg(tmp, tmp, 0x11111111);
     tcg_gen_muli_reg(tmp, tmp, 6);
-    do_unit(ctx, a->t, tmp, load_gpr(ctx, a->r), a->cf, false,
+    do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, false,
             is_i ? tcg_gen_add_reg : tcg_gen_sub_reg);
     return nullify_end(ctx);
 }
@@ -3034,7 +3095,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1,
     DisasCond cond;
 
     in2 = load_gpr(ctx, r);
-    dest = dest_gpr(ctx, r);
+    dest = tcg_temp_new();
     sv = NULL;
     cb_msb = NULL;
 
@@ -3050,6 +3111,8 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1,
     }
 
     cond = do_cond(c * 2 + f, dest, cb_msb, sv);
+    save_gpr(ctx, r, dest);
+    tcg_temp_free(dest);
     return do_cbranch(ctx, disp, n, &cond);
 }
 
@@ -3447,6 +3510,8 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
 {
     target_ureg dest = iaoq_dest(ctx, a->disp);
 
+    nullify_over(ctx);
+
     /* Make sure the caller hasn't done something weird with the queue.
      * ??? This is not quite the same as the PSW[B] bit, which would be
      * expensive to track.  Real hardware will trap for
@@ -3484,17 +3549,30 @@ static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a)
     }
 #endif
 
-    return do_dbranch(ctx, dest, a->l, a->n);
+    if (a->l) {
+        TCGv_reg tmp = dest_gpr(ctx, a->l);
+        if (ctx->privilege < 3) {
+            tcg_gen_andi_reg(tmp, tmp, -4);
+        }
+        tcg_gen_ori_reg(tmp, tmp, ctx->privilege);
+        save_gpr(ctx, a->l, tmp);
+    }
+
+    return do_dbranch(ctx, dest, 0, a->n);
 }
 
 static bool trans_blr(DisasContext *ctx, arg_blr *a)
 {
-    TCGv_reg tmp = get_temp(ctx);
-
-    tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3);
-    tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8);
-    /* The computation here never changes privilege level.  */
-    return do_ibranch(ctx, tmp, a->l, a->n);
+    if (a->x) {
+        TCGv_reg tmp = get_temp(ctx);
+        tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3);
+        tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8);
+        /* The computation here never changes privilege level.  */
+        return do_ibranch(ctx, tmp, a->l, a->n);
+    } else {
+        /* BLR R0,RX is a good way to load PC+8 into RX.  */
+        return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n);
+    }
 }
 
 static bool trans_bv(DisasContext *ctx, arg_bv *a)
@@ -4045,6 +4123,13 @@ static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a)
     return nullify_end(ctx);
 }
 
+static bool trans_diag(DisasContext *ctx, arg_diag *a)
+{
+    qemu_log_mask(LOG_UNIMP, "DIAG opcode ignored\n");
+    cond_free(&ctx->null_cond);
+    return true;
+}
+
 static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
 {
     DisasContext *ctx = container_of(dcbase, DisasContext, base);
@@ -4188,19 +4273,31 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
     ctx->iaoq_b = ctx->iaoq_n;
     ctx->base.pc_next += 4;
 
-    if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) {
-        return;
-    }
-    if (ctx->iaoq_f == -1) {
-        tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b);
-        copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
+    switch (ret) {
+    case DISAS_NORETURN:
+    case DISAS_IAQ_N_UPDATED:
+        break;
+
+    case DISAS_NEXT:
+    case DISAS_IAQ_N_STALE:
+    case DISAS_IAQ_N_STALE_EXIT:
+        if (ctx->iaoq_f == -1) {
+            tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b);
+            copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
 #ifndef CONFIG_USER_ONLY
-        tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
+            tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b);
 #endif
-        nullify_save(ctx);
-        ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
-    } else if (ctx->iaoq_b == -1) {
-        tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var);
+            nullify_save(ctx);
+            ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT
+                                ? DISAS_EXIT
+                                : DISAS_IAQ_N_UPDATED);
+        } else if (ctx->iaoq_b == -1) {
+            tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var);
+        }
+        break;
+
+    default:
+        g_assert_not_reached();
     }
 }
 
@@ -4222,11 +4319,12 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
     case DISAS_IAQ_N_UPDATED:
         if (ctx->base.singlestep_enabled) {
             gen_excp_1(EXCP_DEBUG);
-        } else if (is_jmp == DISAS_IAQ_N_STALE_EXIT) {
-            tcg_gen_exit_tb(NULL, 0);
-        } else {
+        } else if (is_jmp != DISAS_IAQ_N_STALE_EXIT) {
             tcg_gen_lookup_and_goto_ptr();
         }
+        /* FALLTHRU */
+    case DISAS_EXIT:
+        tcg_gen_exit_tb(NULL, 0);
         break;
     default:
         g_assert_not_reached();
@@ -4268,11 +4366,10 @@ static const TranslatorOps hppa_tr_ops = {
     .disas_log          = hppa_tr_disas_log,
 };
 
-void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
-
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
 {
     DisasContext ctx;
-    translator_loop(&hppa_tr_ops, &ctx.base, cs, tb);
+    translator_loop(&hppa_tr_ops, &ctx.base, cs, tb, max_insns);
 }
 
 void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb,
This page took 0.035694 seconds and 4 git commands to generate.