SSR.SS enables a single-step debug event after each packet executed in
user or guest mode.

Carry the state in a new SS_ACTIVE TB flag.  When it is set, translate
one packet per TB and raise HEX_EVENT_DEBUG w/PC at the next packet (which
becomes ELR).

Reviewed-by: Pierrick Bouvier <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
 target/hexagon/cpu.h                      |  1 +
 target/hexagon/translate.h                |  1 +
 target/hexagon/cpu.c                      |  4 ++
 target/hexagon/hexswi.c                   |  1 -
 target/hexagon/translate.c                | 50 +++++++++++++++++++++++
 tests/functional/hexagon/test_systests.py |  3 ++
 6 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index c1a944920e8..15ae5d1d009 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -227,6 +227,7 @@ FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1)
 FIELD(TB_FLAGS, MMU_INDEX, 1, 3)
 FIELD(TB_FLAGS, HVX_COPROC_ENABLED, 5, 1)
 FIELD(TB_FLAGS, CPU_MODE, 6, 2)
+FIELD(TB_FLAGS, SS_ACTIVE, 8, 1)
 
 G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env,
                                             uint32_t exception,
diff --git a/target/hexagon/translate.h b/target/hexagon/translate.h
index dc0c6b18bf1..98a5b39dd76 100644
--- a/target/hexagon/translate.h
+++ b/target/hexagon/translate.h
@@ -46,6 +46,7 @@ typedef struct DisasContext {
     bool implicit_usr_write;
 #ifndef CONFIG_USER_ONLY
     uint32_t cpu_mode;
+    bool ss_active;
     int greg_log[GREG_WRITES_MAX];
     int greg_log_idx;
     DECLARE_BITMAP(gregs_written, NUM_GREGS);
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index ebd77e0f7dd..f383ffd88e6 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -388,6 +388,10 @@ static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs)
                            GET_SSR_FIELD(SSR_XE, env->t_sreg[HEX_SREG_SSR]));
     hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, CPU_MODE,
                            get_cpu_mode(env));
+    /* Single-step applies only to user and guest mode packets */
+    hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, SS_ACTIVE,
+                           GET_SSR_FIELD(SSR_SS, env->t_sreg[HEX_SREG_SSR]) &&
+                           get_cpu_mode(env) != HEX_CPU_MODE_MONITOR);
 #else
     hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, MMU_USER_IDX);
 #endif
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
index 19439658392..92506c26817 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -846,7 +846,6 @@ void hexagon_cpu_do_interrupt(CPUState *cs)
     case HEX_EVENT_DEBUG:
         hexagon_ssr_set_cause(env, env->cause_code);
         set_addresses(env, 0, cs->exception_index);
-        qemu_log_mask(LOG_UNIMP, "single-step exception is not handled\n");
         break;
 
     case HEX_EVENT_PRECISE:
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index e2a5038a1a5..2f5e9f21098 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -175,10 +175,48 @@ static void gen_goto_tb(DisasContext *ctx, unsigned 
tb_slot_idx,
     }
 }
 
+#ifndef CONFIG_USER_ONLY
+/*
+ * Raise the single-step debug event once the packet has retired.  PC
+ * already holds the address of the next packet, which becomes ELR.
+ */
+static void gen_singlestep_exception(DisasContext *ctx)
+{
+    tcg_gen_movi_i32(hex_cause_code, HEX_CAUSE_DEBUG_SINGLESTEP);
+    gen_helper_raise_exception(tcg_env,
+                               tcg_constant_i32(HEX_EVENT_DEBUG),
+                               hex_gpr[HEX_REG_PC]);
+    ctx->base.is_jmp = DISAS_NORETURN;
+}
+
+static void gen_end_tb_singlestep(DisasContext *ctx)
+{
+    TCGv pc = hex_gpr[HEX_REG_PC];
+
+    if (ctx->branch_cond == TCG_COND_ALWAYS) {
+        tcg_gen_movi_tl(pc, ctx->branch_dest);
+    } else if (ctx->branch_cond != TCG_COND_NEVER) {
+        tcg_gen_movcond_tl(ctx->branch_cond, pc, ctx->branch_taken,
+                           tcg_constant_tl(1), tcg_constant_tl(ctx->next_PC),
+                           tcg_constant_tl(ctx->branch_dest));
+    } else if (!ctx->pkt.pkt_has_cof) {
+        tcg_gen_movi_tl(pc, ctx->next_PC);
+    }
+    gen_singlestep_exception(ctx);
+}
+#endif
+
 static void gen_end_tb(DisasContext *ctx)
 {
     gen_exec_counters(ctx);
 
+#ifndef CONFIG_USER_ONLY
+    if (ctx->ss_active) {
+        gen_end_tb_singlestep(ctx);
+        return;
+    }
+#endif
+
     if (ctx->branch_cond != TCG_COND_NEVER) {
         if (ctx->branch_cond != TCG_COND_ALWAYS) {
             TCGLabel *skip = gen_new_label();
@@ -1336,6 +1374,12 @@ static void 
hexagon_tr_init_disas_context(DisasContextBase *dcbase,
         FIELD_EX32(hex_flags, TB_FLAGS, HVX_COPROC_ENABLED);
     ctx->hvx_check_emitted = false;
     ctx->cpu_mode = FIELD_EX32(hex_flags, TB_FLAGS, CPU_MODE);
+    ctx->ss_active = FIELD_EX32(hex_flags, TB_FLAGS, SS_ACTIVE);
+    if (ctx->ss_active) {
+        /* Step one packet, and let endloop0 branch within the packet */
+        ctx->base.max_insns = 1;
+        ctx->is_tight_loop = false;
+    }
 #endif
 }
 
@@ -1403,6 +1447,12 @@ static void hexagon_tr_tb_stop(DisasContextBase *dcbase, 
CPUState *cpu)
     case DISAS_TOO_MANY:
         gen_exec_counters(ctx);
         tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->base.pc_next);
+#ifndef CONFIG_USER_ONLY
+        if (ctx->ss_active) {
+            gen_singlestep_exception(ctx);
+            break;
+        }
+#endif
         tcg_gen_exit_tb(NULL, 0);
         break;
     case DISAS_NORETURN:
diff --git a/tests/functional/hexagon/test_systests.py 
b/tests/functional/hexagon/test_systests.py
index 0489db0af91..1c1d5440bb6 100755
--- a/tests/functional/hexagon/test_systests.py
+++ b/tests/functional/hexagon/test_systests.py
@@ -111,5 +111,8 @@ def test_standalone_vec(self):
     def test_lock_verify(self):
         self.run_exit_zero("lock_verify")
 
+    def test_single_step(self):
+        self.run_exit_zero("single_step")
+
 if __name__ == "__main__":
     QemuSystemTest.main()
-- 
2.34.1

Reply via email to