We'll have to instrument some RISC-V translations, as well as the start of a translation block, communicating with the associated trace encoder in case we're running a trace session. The trace encoder needs to report the first insn of the session so we'll start with that.
Unfortunately we've run out of bits in tb_flags, meaning we can't use riscv_tr_init_disas_context() and riscv_get_tb_cpu_state() to communicate whether we're running a trace session. One alternative would be to fold the "trace is running" logic in each trace helper. That would make all code paths, regardless of even having a trace encoder associated with the CPU, to check for trace encoder existence. Another alternative, which is implemented here, is to add an additional mirror flag 'trace_running' in CPURISCVState and turn it into a TCG global 'cpu_trace_running'. Each trace helper call is gated via this global, allowing us to skip calling trace helpers when we're not running a trace session. In case we end up increasing the size of tb_flags in the future we should revisit this code and use tb_flags instead. Signed-off-by: Daniel Henrique Barboza <[email protected]> --- hw/riscv/trace-encoder.c | 17 +++++++++++++++++ hw/riscv/trace-encoder.h | 4 ++++ hw/riscv/trace-events | 1 + target/riscv/cpu.h | 2 ++ target/riscv/helper.h | 4 ++++ target/riscv/meson.build | 3 ++- target/riscv/trace_helper.c | 36 ++++++++++++++++++++++++++++++++++++ target/riscv/translate.c | 10 ++++++++++ 8 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 target/riscv/trace_helper.c diff --git a/hw/riscv/trace-encoder.c b/hw/riscv/trace-encoder.c index d45e45d17e..9701ce43cf 100644 --- a/hw/riscv/trace-encoder.c +++ b/hw/riscv/trace-encoder.c @@ -150,11 +150,15 @@ static void trencoder_te_ctrl_postw(RegisterInfo *reg, uint64_t val) uint32_t trTeEnable = ARRAY_FIELD_EX32(te->regs, TR_TE_CONTROL, ENABLE); uint32_t trTeInstTracing = ARRAY_FIELD_EX32(te->regs, TR_TE_CONTROL, INST_TRACING); + RISCVCPU *cpu = te->cpu; + CPURISCVState *env = &cpu->env; if (!trTeActive) { te->enabled = false; te->trace_running = false; te->trace_next_insn = false; + + env->trace_running = false; return; } @@ -170,6 +174,7 @@ static void trencoder_te_ctrl_postw(RegisterInfo *reg, uint64_t val) } te->trace_running = trTeInstTracing ? true : false; + env->trace_running = te->trace_running; } static RegisterAccessInfo trencoder_regs_info[] = { @@ -231,6 +236,8 @@ static const MemoryRegionOps trencoder_ops = { static void trencoder_reset(DeviceState *dev) { TraceEncoder *te = TRACE_ENCODER(dev); + RISCVCPU *cpu = te->cpu; + CPURISCVState *env = &cpu->env; for (int i = 0; i < ARRAY_SIZE(te->regs_info); i++) { register_reset(&te->regs_info[i]); @@ -239,6 +246,7 @@ static void trencoder_reset(DeviceState *dev) te->enabled = false; te->trace_running = false; te->trace_next_insn = false; + env->trace_running = false; } static void trencoder_realize(DeviceState *dev, Error **errp) @@ -266,6 +274,14 @@ static void trencoder_realize(DeviceState *dev, Error **errp) } } +void trencoder_set_first_trace_insn(Object *trencoder_obj, uint64_t pc) +{ + TraceEncoder *trencoder = TRACE_ENCODER(trencoder_obj); + + trencoder->first_pc = pc; + trace_trencoder_first_trace_insn(pc); +} + static const Property trencoder_props[] = { /* * We need a link to the associated CPU to @@ -294,6 +310,7 @@ static const VMStateDescription vmstate_trencoder = { VMSTATE_UINT64(ramsink_ramstart, TraceEncoder), VMSTATE_UINT64(ramsink_ramlimit, TraceEncoder), VMSTATE_INT32(cpu_id, TraceEncoder), + VMSTATE_UINT64(first_pc, TraceEncoder), VMSTATE_END_OF_LIST(), } }; diff --git a/hw/riscv/trace-encoder.h b/hw/riscv/trace-encoder.h index 001d872514..cf3177aefe 100644 --- a/hw/riscv/trace-encoder.h +++ b/hw/riscv/trace-encoder.h @@ -27,6 +27,8 @@ struct TraceEncoder { MemoryRegion reg_mem; uint32_t reg_mem_size; + uint64_t first_pc; + hwaddr baseaddr; hwaddr dest_baseaddr; hwaddr ramsink_ramstart; @@ -43,4 +45,6 @@ struct TraceEncoder { OBJECT_DECLARE_SIMPLE_TYPE(TraceEncoder, TRACE_ENCODER) +void trencoder_set_first_trace_insn(Object *trencoder_obj, uint64_t pc); + #endif diff --git a/hw/riscv/trace-events b/hw/riscv/trace-events index 14e333fd9e..dc25377acf 100644 --- a/hw/riscv/trace-events +++ b/hw/riscv/trace-events @@ -28,6 +28,7 @@ riscv_iommu_hpm_evt_write(uint32_t ctr_idx, uint32_t ovf, uint64_t val) "ctr_idx # trace-encoder.c trencoder_read_error(uint64_t addr) "addr 0x%" PRIx64 trencoder_write_error(uint64_t addr, uint64_t value) "addr 0x%" PRIx64 " value 0x%" PRIx64 +trencoder_first_trace_insn(uint64_t pc) "pc 0x%" PRIx64 # trace-ram-sink.c tr_ramsink_read_error(uint64_t addr) "addr 0x%" PRIx64 diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 12251e4d94..16c6c280a8 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -514,6 +514,8 @@ struct CPUArchState { target_ulong rnmip; uint64_t rnmi_irqvec; uint64_t rnmi_excpvec; + + bool trace_running; }; /* diff --git a/target/riscv/helper.h b/target/riscv/helper.h index b785456ee0..e80320ad16 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -128,6 +128,10 @@ DEF_HELPER_4(csrrw, tl, env, int, tl, tl) DEF_HELPER_2(csrr_i128, tl, env, int) DEF_HELPER_4(csrw_i128, void, env, int, tl, tl) DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl) + +/* Trace helpers (should be put inside ifdef) */ +DEF_HELPER_2(trace_insn, void, env, i64) + #ifndef CONFIG_USER_ONLY DEF_HELPER_1(sret, tl, env) DEF_HELPER_1(mret, tl, env) diff --git a/target/riscv/meson.build b/target/riscv/meson.build index fdefe88ccd..564e2da5f2 100644 --- a/target/riscv/meson.build +++ b/target/riscv/meson.build @@ -26,7 +26,8 @@ riscv_ss.add(files( 'm128_helper.c', 'crypto_helper.c', 'zce_helper.c', - 'vcrypto_helper.c' + 'vcrypto_helper.c', + 'trace_helper.c' )) riscv_system_ss = ss.source_set() diff --git a/target/riscv/trace_helper.c b/target/riscv/trace_helper.c new file mode 100644 index 0000000000..ed84e6f79a --- /dev/null +++ b/target/riscv/trace_helper.c @@ -0,0 +1,36 @@ +/* + * RISC-V Trace Support TCG helpers + * + * Copyright (C) 2025 Ventana Micro Systems Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "cpu.h" +#include "trace.h" +#include "exec/helper-proto.h" + +#ifndef CONFIG_USER_ONLY +#include "hw/riscv/trace-encoder.h" +#endif + +#ifndef CONFIG_USER_ONLY +void helper_trace_insn(CPURISCVState *env, uint64_t pc) +{ + RISCVCPU *cpu = env_archcpu(env); + TraceEncoder *te = TRACE_ENCODER(cpu->trencoder); + + if (te->trace_next_insn) { + trencoder_set_first_trace_insn(cpu->trencoder, pc); + te->trace_next_insn = false; + } +} +#else /* #ifndef CONFIG_USER_ONLY */ +void helper_trace_insn(CPURISCVState *env, uint64_t pc) +{ + return; +} +#endif /* #ifndef CONFIG_USER_ONLY*/ diff --git a/target/riscv/translate.c b/target/riscv/translate.c index e1f4dc5ffd..ff288051e3 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -43,6 +43,9 @@ static TCGv_i64 cpu_fpr[32]; /* assume F and D extensions */ static TCGv load_res; static TCGv load_val; +/* TODO: this should be a tb_flag instead of a global */ +static TCGv cpu_trace_running; + /* * If an operation is being performed on less than TARGET_LONG_BITS, * it may require the inputs to be sign- or zero-extended; which will @@ -1340,6 +1343,11 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu) { + TCGLabel *skip = gen_new_label(); + + tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_trace_running, 0, skip); + gen_helper_trace_insn(tcg_env, tcg_constant_i64(db->pc_first)); + gen_set_label(skip); } static void riscv_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) @@ -1464,4 +1472,6 @@ void riscv_translate_init(void) "load_res"); load_val = tcg_global_mem_new(tcg_env, offsetof(CPURISCVState, load_val), "load_val"); + cpu_trace_running = tcg_global_mem_new(tcg_env, + offsetof(CPURISCVState, trace_running), "trace_running"); } -- 2.51.1
