[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