The trap message consists of a Format 3 Subformat 1 e-trace message.
According to the Efficient Trace for RISC-V spec, section "Format 3
thaddr, address and privilege fields", the 'thaddr' will be zero if:
- we can't infer the EPC after an uninferable PC discontinuity (like traps).
This doesn't happen in our current TCG backend - we'll always know the
trap EPC in riscv_cpu_do_interrupt();
- a second interrupt/exception happens while the handler of the first
trap hasn't exited. This also doesn't happen in TCG given that we do
not emulate a multi-insn pipeline model, i.e. we'll only retire one
insns at a time.
This means that we'll always send trap packets with 'thaddr' set to 1,
thus we're hardcoding it in the message helper.
Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
---
hw/riscv/rv-trace-messages.c | 94 ++++++++++++++++++++++++++++++++++++
hw/riscv/rv-trace-messages.h | 5 ++
2 files changed, 99 insertions(+)
diff --git a/hw/riscv/rv-trace-messages.c b/hw/riscv/rv-trace-messages.c
index 215135dd47..3e9466633d 100644
--- a/hw/riscv/rv-trace-messages.c
+++ b/hw/riscv/rv-trace-messages.c
@@ -35,6 +35,24 @@ typedef struct RVTraceSyncPayload {
} RVTraceSyncPayload;
#define SYNC_PAYLOAD_SIZE_64BITS 9
+/*
+ * Format 3 subformat 1 without 'time' and 'context' fields
+ */
+typedef struct RVTraceTrapPayload {
+ uint8_t format:2;
+ uint8_t subformat:2;
+ uint8_t branch:1;
+ uint8_t privilege:3;
+ uint8_t ecause:6;
+ uint8_t interrupt:1;
+ uint8_t thaddr:1;
+ uint32_t addressLow;
+ uint32_t addressHigh;
+ uint32_t tvalLow;
+ uint32_t tvalHigh;
+} RVTraceTrapPayload;
+#define TRAP_PAYLOAD_SIZE_64BITS 18
+
static void rv_etrace_write_bits(uint8_t *bytes, uint32_t bit_pos,
uint32_t num_bits, uint32_t val)
{
@@ -92,3 +110,79 @@ size_t rv_etrace_gen_encoded_sync_msg(uint8_t *buf, uint64_t pc,
return HEADER_SIZE + SYNC_PAYLOAD_SIZE_64BITS;
}
+
+/*
+ * Note: this function assumes thaddr = 1.
+ */
+size_t rv_etrace_gen_encoded_trap_msg(uint8_t *buf, uint64_t trap_addr,
+ TracePrivLevel priv_level,
+ uint8_t ecause,
+ bool is_interrupt,
+ uint64_t tval)
+{
+ RVTraceTrapPayload payload = {.format = 0b11,
+ .subformat = 0b01,
+ .branch = 1,
+ .privilege = priv_level,
+ .ecause = ecause};
+ RVTraceMessageHeader header = {.flow = 0, .extend = 0,
+ .length = TRAP_PAYLOAD_SIZE_64BITS};
+ uint8_t bit_pos;
+
+ payload.addressLow = extract64(trap_addr, 0, 32);
+ payload.addressHigh = extract64(trap_addr, 32, 32);
+
+ /*
+ * When interrupt = 1 'tval' is ommited. Take 8 bytes
+ * from the final size.
+ */
+ if (is_interrupt) {
+ header.length = TRAP_PAYLOAD_SIZE_64BITS - 8;
+ }
+
+ rv_etrace_write_header(buf, header);
+ bit_pos = 8;
+
+ rv_etrace_write_bits(buf, bit_pos, 2, payload.format);
+ bit_pos += 2;
+ rv_etrace_write_bits(buf, bit_pos, 2, payload.subformat);
+ bit_pos += 2;
+ rv_etrace_write_bits(buf, bit_pos, 1, payload.branch);
+ bit_pos += 1;
+ rv_etrace_write_bits(buf, bit_pos, 3, payload.privilege);
+ bit_pos += 3;
+
+ rv_etrace_write_bits(buf, bit_pos, 6, payload.ecause);
+ bit_pos += 6;
+
+ if (is_interrupt) {
+ rv_etrace_write_bits(buf, bit_pos, 1, 1);
+ } else {
+ rv_etrace_write_bits(buf, bit_pos, 1, 0);
+ }
+ bit_pos += 1;
+
+ /* thaddr is hardcoded to 1 for now */
+ rv_etrace_write_bits(buf, bit_pos, 1, 1);
+ bit_pos += 1;
+
+ rv_etrace_write_bits(buf, bit_pos, 32, payload.addressLow);
+ bit_pos += 32;
+ rv_etrace_write_bits(buf, bit_pos, 32, payload.addressHigh);
+ bit_pos += 32;
+
+ /* Skip trap_addr if is_interrupt */
+ if (is_interrupt) {
+ goto out;
+ }
+
+ payload.tvalLow = extract64(trap_addr, 0, 32);
+ payload.tvalHigh = extract64(trap_addr, 32, 32);
+
+ rv_etrace_write_bits(buf, bit_pos, 32, payload.tvalLow);
+ bit_pos += 32;
+ rv_etrace_write_bits(buf, bit_pos, 32, payload.tvalHigh);
+
+out:
+ return HEADER_SIZE + header.length;
+}
diff --git a/hw/riscv/rv-trace-messages.h b/hw/riscv/rv-trace-messages.h
index aeafea8849..f3e38b571f 100644
--- a/hw/riscv/rv-trace-messages.h
+++ b/hw/riscv/rv-trace-messages.h
@@ -21,5 +21,10 @@ typedef enum {
size_t rv_etrace_gen_encoded_sync_msg(uint8_t *buf, uint64_t pc,
TracePrivLevel priv_level);
+size_t rv_etrace_gen_encoded_trap_msg(uint8_t *buf, uint64_t trap_addr,
+ TracePrivLevel priv_level,
+ uint8_t ecause,
+ bool is_interrupt,
+ uint64_t tval);
#endif
--
2.51.1