We have the needed pieces to make the trace encoder write messages (or
packets, in e-trace lingo) in its associated RAM sink.
We'll start by writing a sync message that we're already able to fetch
from the TCG instruction train. The trace encoder must be able to
read/write TR_RAM_WP RAM sink regs to know where it is supposed to write
packets, thus a handful of helpers are added to manipulate both the ram
sink SMEM memory and relevant registers.
The bulk of the work is done in trencoder_send_message_smem() where the
trace encoder writes each trace packet. The 'wrap' mechanism is
implemented to keep writing the RAM sink memory even after a SMEM
workflow. If we can't fit a full packet in the end of the ring buffer
we're filling with NULLs and writing a new packet back to ramstart.
In an overflow event we'll wrap the TR_RAM_WP_LOW WRAP bit and userspace
is supposed to clear it after the wrapping was handled (i.e. userspace
read the whole ring buffer back to the start).
We're also allowing userspace to freely set ramsink and ramstart before
the trace session begins, allowing for more freedom w.r.t where the SMEM
will be written by the encoder. Note that this also means that userspace
is responsible for setting at least TR_RAM_WP_LOW and TR_RAM_WP_HIGH
before starting a trace session.
Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
---
hw/riscv/trace-encoder.c | 121 +++++++++++++++++++++++++++++++++++++++
1 file changed, 121 insertions(+)
diff --git a/hw/riscv/trace-encoder.c b/hw/riscv/trace-encoder.c
index 9701ce43cf..16c9475d46 100644
--- a/hw/riscv/trace-encoder.c
+++ b/hw/riscv/trace-encoder.c
@@ -20,6 +20,34 @@
#include "system/device_tree.h"
#include "hw/register.h"
#include "cpu.h"
+#include "hw/riscv/trace-ram-sink.h"
+#include "rv-trace-messages.h"
+
+/*
+ * Size of header + payload since we're not sending
+ * srcID and timestamp.
+ */
+#define TRACE_MSG_MAX_SIZE 32
+
+static TracePrivLevel trencoder_get_curr_priv_level(TraceEncoder *te)
+{
+ CPURISCVState *env = &te->cpu->env;
+
+ switch (env->priv) {
+ case PRV_U:
+ return env->virt_enabled ? VU : U;
+ case PRV_S:
+ return env->virt_enabled ? VS : S_HS;
+ case PRV_M:
+ return M;
+ }
+
+ /*
+ * Return a reserved value to signal an error.
+ * TODO: handle Debug (D).
+ */
+ return RESERVED;
+}
/*
* trTeControl register fields
@@ -82,6 +110,41 @@ REG32(TR_TE_IMPL, 0x4)
REG32(TR_TE_INST_FEATURES, 0x8)
FIELD(TR_TE_INST_FEATURES, NO_ADDR_DIFF, 0, 1)
+static uint32_t trencoder_read_reg(TraceEncoder *te, uint32_t reg_addr)
+{
+ hwaddr addr = te->dest_baseaddr + reg_addr;
+ uint32_t val;
+
+ cpu_physical_memory_read(addr, &val, sizeof(uint32_t));
+ return val;
+}
+
+static void trencoder_write_reg(TraceEncoder *te, uint32_t reg_addr,
+ uint32_t val)
+{
+ hwaddr addr = te->dest_baseaddr + reg_addr;
+
+ cpu_physical_memory_write(addr, &val, sizeof(uint32_t));
+}
+
+static hwaddr trencoder_read_ramsink_writep(TraceEncoder *te)
+{
+ hwaddr ret = trencoder_read_reg(te, A_TR_RAM_WP_HIGH);
+ ret <<= 32;
+ ret += trencoder_read_reg(te, A_TR_RAM_WP_LOW);
+
+ return ret;
+}
+
+static hwaddr trencoder_read_ramsink_ramlimit(TraceEncoder *te)
+{
+ hwaddr ret = trencoder_read_reg(te, A_TR_RAM_LIMIT_HIGH);
+ ret <<= 32;
+ ret += trencoder_read_reg(te, A_TR_RAM_LIMIT_LOW);
+
+ return ret;
+}
+
static uint64_t trencoder_te_ctrl_set_hardwire_vals(uint64_t input)
{
input = FIELD_DP32(input, TR_TE_CONTROL, INST_MODE, 0x6);
@@ -171,6 +234,9 @@ static void trencoder_te_ctrl_postw(RegisterInfo *reg, uint64_t val)
if (!te->trace_running && trTeInstTracing) {
/* Starting trace. Ask the CPU for the first trace insn */
te->trace_next_insn = true;
+
+ te->ramsink_ramstart = trencoder_read_ramsink_writep(te);
+ te->ramsink_ramlimit = trencoder_read_ramsink_ramlimit(te);
}
te->trace_running = trTeInstTracing ? true : false;
@@ -274,12 +340,67 @@ static void trencoder_realize(DeviceState *dev, Error **errp)
}
}
+static void trencoder_update_ramsink_writep(TraceEncoder *te,
+ hwaddr wp_val,
+ bool wrapped)
+{
+ uint32_t wp_low = trencoder_read_reg(te, A_TR_RAM_WP_LOW);
+
+ wp_low = FIELD_DP32(wp_low, TR_RAM_WP_LOW, ADDR,
+ extract64(wp_val, 2, 30));
+
+ if (wrapped) {
+ wp_low = FIELD_DP32(wp_low, TR_RAM_WP_LOW, WRAP, 1);
+ }
+
+ trencoder_write_reg(te, A_TR_RAM_WP_LOW, wp_low);
+ trencoder_write_reg(te, A_TR_RAM_WP_HIGH, extract64(wp_val, 32, 32));
+}
+
+static void trencoder_send_message_smem(TraceEncoder *trencoder,
+ uint8_t *msg, uint8_t msg_size)
+{
+ hwaddr dest = trencoder_read_ramsink_writep(trencoder);
+ bool wrapped = false;
+
+ msg_size = QEMU_ALIGN_UP(msg_size, 4);
+
+ /* clear trRamWrap before writing to SMEM */
+ dest = FIELD_DP64(dest, TR_RAM_WP_LOW, WRAP, 0);
+
+ /*
+ * Fill with null bytes if we can't fit the packet in
+ * ramlimit, set wrap and write the packet in ramstart.
+ */
+ if (dest + msg_size > trencoder->ramsink_ramlimit) {
+ g_autofree uint8_t *null_packet = NULL;
+ uint8_t null_size = trencoder->ramsink_ramlimit - dest;
+
+ null_packet = g_malloc0(null_size);
+ cpu_physical_memory_write(dest, null_packet, null_size);
+
+ dest = trencoder->ramsink_ramstart;
+ wrapped = true;
+ }
+
+ cpu_physical_memory_write(dest, msg, msg_size);
+ dest += msg_size;
+
+ trencoder_update_ramsink_writep(trencoder, dest, wrapped);
+}
+
void trencoder_set_first_trace_insn(Object *trencoder_obj, uint64_t pc)
{
TraceEncoder *trencoder = TRACE_ENCODER(trencoder_obj);
+ TracePrivLevel priv = trencoder_get_curr_priv_level(trencoder);
+ g_autofree uint8_t *msg = g_malloc0(TRACE_MSG_MAX_SIZE);
+ uint8_t msg_size;
trencoder->first_pc = pc;
trace_trencoder_first_trace_insn(pc);
+ msg_size = rv_etrace_gen_encoded_sync_msg(msg, pc, priv);
+
+ trencoder_send_message_smem(trencoder, msg, msg_size);
}
static const Property trencoder_props[] = {
--
2.51.1