[PATCH 06/13] hw/riscv: add n-trace IndirectBranchHist rv-trace-message

Konstantin Semichastnov <[email protected]>
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-devel
Message-ID <[email protected]>
Add generation, encoding and sending logic for N-Trace
IndirectBranchHist message.

Signed-off-by: Konstantin Semichastnov <[email protected]>
Signed-off-by: Alexei Filippov <[email protected]>
---
 hw/riscv/rv-trace-messages.c | 42 +++++++++++++++++++++
 hw/riscv/rv-trace-messages.h |  4 ++
 hw/riscv/trace-encoder.c     | 87 ++++++++++++++++++++++++++++++++++++++++++++
 hw/riscv/trace-encoder.h     | 10 +++++
 target/riscv/trace_helper.c  |  3 +-
 5 files changed, 145 insertions(+), 1 deletion(-)

diff --git a/hw/riscv/rv-trace-messages.c b/hw/riscv/rv-trace-messages.c
index 0b90d6a437..2ee38c4354 100644
--- a/hw/riscv/rv-trace-messages.c
+++ b/hw/riscv/rv-trace-messages.c
@@ -354,12 +354,16 @@ typedef enum {
 typedef enum {
     WIDTH_TCODE  = 6,
     WIDTH_SYNC   = 4,
+    WIDTH_BTYPE  = 2,
     WIDTH_ICNT   = 22,
     WIDTH_FADDR  = 63,
+    WIDTH_UADDR  = 63,
+    WIDTH_HIST   = 32,
 } NTraceFieldWidth;
 
 typedef enum {
     TCODE_PROG_TRACE_SYNC        = 0x9,
+    TCODE_INDIRECT_BRANCH_HIST   = 0x1c,
 } NTraceTcodeFieldValues;
 
 typedef enum {
@@ -387,6 +391,13 @@ typedef struct NTraceFieldPayload {
         .value = (val),      \
     }
 
+#define PAYLOAD_BTYPE(val)    \
+    {                         \
+        .type = FIELD_FIXED,  \
+        .width = WIDTH_BTYPE, \
+        .value = (val),       \
+    }
+
 #define PAYLOAD_ICNT(val)    \
     {                        \
         .type = FIELD_VAR,   \
@@ -400,6 +411,20 @@ typedef struct NTraceFieldPayload {
         .width = WIDTH_FADDR, \
         .value = (val),       \
     }
+
+#define PAYLOAD_UADDR(val)    \
+    {                         \
+        .type = FIELD_VAR,    \
+        .width = WIDTH_UADDR, \
+        .value = (val),       \
+    }
+
+#define PAYLOAD_HIST(val)    \
+    {                        \
+        .type = FIELD_VAR,   \
+        .width = WIDTH_HIST, \
+        .value = (val),      \
+    }
 #define PAYLOAD_END()      \
     {                      \
         .type = FIELD_END, \
@@ -492,3 +517,20 @@ size_t rv_ntrace_gen_encoded_prog_trace_sync_msg(uint8_t *buf, uint64_t faddr)
     return rv_ntrace_gen_encoded_msg(buf, msg_fields);
 }
 
+size_t rv_ntrace_gen_indirect_branch_hist_msg(uint8_t *buf, uint8_t btype,
+                                              uint32_t icnt, uint64_t uaddr,
+                                              uint32_t hist)
+{
+    const NTraceFieldPayload msg_fields[] = {
+        PAYLOAD_TCODE(TCODE_INDIRECT_BRANCH_HIST),
+        /* SRC field is not supported for now */
+        PAYLOAD_BTYPE(btype),
+        PAYLOAD_ICNT(icnt),
+        PAYLOAD_UADDR(uaddr >> 1),
+        PAYLOAD_HIST(hist),
+        /* TSTAMP field is not supported for now */
+        PAYLOAD_END(),
+    };
+
+    return rv_ntrace_gen_encoded_msg(buf, msg_fields);
+}
diff --git a/hw/riscv/rv-trace-messages.h b/hw/riscv/rv-trace-messages.h
index 635e498393..d113129d65 100644
--- a/hw/riscv/rv-trace-messages.h
+++ b/hw/riscv/rv-trace-messages.h
@@ -38,4 +38,8 @@ size_t rv_etrace_gen_encoded_format1(uint8_t *buf,
                                      bool notify, bool updiscon);
 
 size_t rv_ntrace_gen_encoded_prog_trace_sync_msg(uint8_t *buf, uint64_t faddr);
+size_t rv_ntrace_gen_indirect_branch_hist_msg(uint8_t *buf, uint8_t btype,
+                                              uint32_t icnt, uint64_t uaddr,
+                                              uint32_t hist);
+
 #endif
diff --git a/hw/riscv/trace-encoder.c b/hw/riscv/trace-encoder.c
index 46501e4f03..fafafb7b9b 100644
--- a/hw/riscv/trace-encoder.c
+++ b/hw/riscv/trace-encoder.c
@@ -31,6 +31,11 @@
 
 #define TRACE_MAX_BRANCHES 31
 
+static uint64_t rv_ntrace_encode_uaddr(TraceEncoder *trencoder, uint64_t target_pc);
+static uint32_t rv_ntrace_encode_and_reset_branch_map(TraceEncoder *trencoder);
+static uint32_t rv_ntrace_get_and_reset_icnt(TraceEncoder *trencoder,
+                                             uint64_t pc, uint32_t inst_len);
+
 static TracePrivLevel trencoder_get_curr_priv_level(TraceEncoder *te)
 {
     CPURISCVState *env = &te->cpu->env;
@@ -478,6 +483,28 @@ static void trencoder_send_sync_msg(Object *trencoder_obj, uint64_t pc)
     trencoder_send_message_smem(trencoder, msg, msg_size);
 }
 
+void trencoder_send_indirect_branch_hist(TraceEncoder *trencoder,
+                                         uint64_t curr_pc,
+                                         uint32_t curr_inst_len,
+                                         uint64_t target_pc,
+                                         TraceBranchType btype)
+{
+    g_autofree uint8_t *msg = g_malloc0(TRACE_MSG_MAX_SIZE);
+    uint8_t msg_size;
+
+    uint32_t icnt = rv_ntrace_get_and_reset_icnt(trencoder, curr_pc, curr_inst_len);
+    uint64_t uaddr = rv_ntrace_encode_uaddr(trencoder, target_pc);
+    uint32_t hist = rv_ntrace_encode_and_reset_branch_map(trencoder);
+
+    g_assert(trencoder->ntrace);
+
+    msg_size = rv_ntrace_gen_indirect_branch_hist_msg(msg, btype, icnt, uaddr, hist);
+    trencoder->last_addr_reported = target_pc;
+    trencoder->last_icnt_reset = target_pc;
+
+    trencoder_send_message_smem(trencoder, msg, msg_size);
+}
+
 static void trencoder_send_updiscon(TraceEncoder *trencoder, uint64_t pc)
 {
     g_autofree uint8_t *format2_msg = g_malloc0(TRACE_MSG_MAX_SIZE);
@@ -551,6 +578,66 @@ void trencoder_send_prog_trace_sync(Object *trencoder_obj, uint64_t pc)
     trencoder_send_message_smem(trencoder, msg, msg_size);
 }
 
+static uint64_t rv_ntrace_encode_uaddr(TraceEncoder *trencoder, uint64_t target_pc)
+{
+    return trencoder->last_addr_reported ^ target_pc;
+}
+
+static uint32_t rv_ntrace_encode_and_reset_branch_map(TraceEncoder *trencoder)
+{
+    const uint32_t stop_bit = 1;
+
+    uint32_t hist = trencoder->branch_map;
+
+    if (trencoder->branches == 0) {
+        /*
+         * if branches == 0, we will overflow on bit shift, so just return
+         * empty history value here (i.e. - only stop bit)
+         */
+        return stop_bit;
+    }
+
+    /*
+     * trencoder->branch_map stores bit 1 for NOT taken branches,
+     * but N-Trace expects to stored bit 1 for TAKEN branches,
+     * so, we should invert bits
+     */
+    hist = ~hist;
+
+    /*
+     * Also, trencoder->branch_map is implemented in such way,
+     * that oldest branch is LSB bit, but N-Trace format require
+     * to HIST values be in shift-left format: the most recent branch
+     * should be in LSB bit. So we should reverse order
+     * of valid history bits
+     */
+    hist = revbit32(hist) >> (32 - trencoder->branches);
+
+    /* And finally N-Trace expects bit 1 as stop bit at the MSB position */
+    hist = deposit32(hist, trencoder->branches, 1, stop_bit);
+
+    trencoder->branch_map = 0;
+    trencoder->branches = 0;
+
+    return hist;
+}
+
+static uint32_t rv_ntrace_get_and_reset_icnt(TraceEncoder *trencoder,
+                                             uint64_t pc, uint32_t inst_len)
+{
+    uint64_t icnt = (pc + inst_len - trencoder->last_icnt_reset) / 2 +
+                    trencoder->icnt_accumulated;
+
+    trencoder->last_icnt_reset = pc;
+    trencoder->icnt_accumulated = 0;
+
+    if (icnt > MAKE_64BIT_MASK(0, 22)) {
+        /* I-CNT overflow reporting is not supported for now */
+        error_setg(&error_fatal, "N-Trace icnt overflowed");
+    }
+
+    return icnt;
+}
 
 void trencoder_trace_trap_insn(Object *trencoder_obj,
                                uint64_t pc, uint32_t ecause,
diff --git a/hw/riscv/trace-encoder.h b/hw/riscv/trace-encoder.h
index 6551ac77f9..f8a6a254dc 100644
--- a/hw/riscv/trace-encoder.h
+++ b/hw/riscv/trace-encoder.h
@@ -57,6 +57,11 @@ typedef enum {
     BRANCH_TAKEN_UNCONDITIONAL = 2,
 } TraceBranchRes;
 
+typedef enum {
+    BTYPE_CONTROL_FLOW = 0,
+    BTYPE_EXCEPTION    = 2,
+    BTYPE_INTERRUPT    = 3,
+} TraceBranchType;
 
 #define TYPE_TRACE_ENCODER "trace-encoder"
 
@@ -64,6 +69,11 @@ OBJECT_DECLARE_SIMPLE_TYPE(TraceEncoder, TRACE_ENCODER)
 
 void trencoder_set_first_trace_insn(Object *trencoder_obj, uint64_t pc);
 void trencoder_send_prog_trace_sync(Object *trencoder_obj, uint64_t pc);
+void trencoder_send_indirect_branch_hist(TraceEncoder *trencoder,
+                                         uint64_t curr_pc,
+                                         uint32_t curr_inst_len,
+                                         uint64_t target_pc,
+                                         TraceBranchType btype);
 void trencoder_trace_trap_insn(Object *trencoder_obj,
                                uint64_t pc, uint32_t ecause,
                                bool is_interrupt,
diff --git a/target/riscv/trace_helper.c b/target/riscv/trace_helper.c
index f6201e2fb9..d7763a998f 100644
--- a/target/riscv/trace_helper.c
+++ b/target/riscv/trace_helper.c
@@ -36,7 +36,8 @@ void helper_trace_updiscon(CPURISCVState *env, target_ulong curr_pc,
     TraceEncoder *te = TRACE_ENCODER(cpu->trencoder);
 
     if (te->ntrace) {
-        /* send IndirectBranchHist message */
+        trencoder_send_indirect_branch_hist(te, curr_pc, curr_inst_len, env->pc,
+                                            BTYPE_CONTROL_FLOW);
     } else {
         te->updiscon_pending = true;
         te->trace_next_insn = true;

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.