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 <dbarboza@ventanamicro.com>
---
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