* [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h
@ 2025-06-05 16:18 Jesse Taube
2025-06-05 16:18 ` [kvm-unit-tests PATCH v3 2/2] riscv: sbi: Add SBI Debug Triggers Extension tests Jesse Taube
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Jesse Taube @ 2025-06-05 16:18 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-kselftest
Cc: Atish Patra, Anup Patel, Palmer Dabbelt, Clément Léger,
Himanshu Chauhan, Charlie Jenkins, Jesse Taube, Andrew Jones
When handeling traps and faults it is offten necessary to know the size
of the instruction at epc. Add RV_INSN_LEN to calculate the
instruction size.
Signed-off-by: Jesse Taube <jesse@rivosinc.com>
---
lib/riscv/asm/processor.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/riscv/asm/processor.h b/lib/riscv/asm/processor.h
index 40104272..631ce226 100644
--- a/lib/riscv/asm/processor.h
+++ b/lib/riscv/asm/processor.h
@@ -7,6 +7,8 @@
#define EXCEPTION_CAUSE_MAX 24
#define INTERRUPT_CAUSE_MAX 16
+#define RV_INSN_LEN(insn) ((((insn) & 0x3) < 0x3) ? 2 : 4)
+
typedef void (*exception_fn)(struct pt_regs *);
struct thread_info {
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [kvm-unit-tests PATCH v3 2/2] riscv: sbi: Add SBI Debug Triggers Extension tests
2025-06-05 16:18 [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h Jesse Taube
@ 2025-06-05 16:18 ` Jesse Taube
2025-06-06 8:48 ` Andrew Jones
2025-06-06 8:32 ` [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h Andrew Jones
2025-07-02 14:49 ` Andrew Jones
2 siblings, 1 reply; 5+ messages in thread
From: Jesse Taube @ 2025-06-05 16:18 UTC (permalink / raw)
To: kvm, kvm-riscv, linux-kselftest
Cc: Atish Patra, Anup Patel, Palmer Dabbelt, Clément Léger,
Himanshu Chauhan, Charlie Jenkins, Jesse Taube, Andrew Jones
Add tests for the DBTR SBI extension.
Signed-off-by: Jesse Taube <jesse@rivosinc.com>
---
V1 -> V2:
- Call report_prefix_pop before returning
- Disable compressed instructions in exec_call, update related comment
- Remove extra "| 1" in dbtr_test_load
- Remove extra newlines
- Remove extra tabs in check_exec
- Remove typedefs from enums
- Return when dbtr_install_trigger fails
- s/avalible/available/g
- s/unistall/uninstall/g
V2 -> V3:
- Change SBI_DBTR_SHMEM_INVALID_ADDR to -1UL
- Move all dbtr functions to sbi-dbtr.c
- Move INSN_LEN to processor.h
- Update include list
- Use C-style comments
---
lib/riscv/asm/sbi.h | 1 +
riscv/Makefile | 1 +
riscv/sbi-dbtr.c | 811 ++++++++++++++++++++++++++++++++++++++++++++
riscv/sbi-tests.h | 1 +
riscv/sbi.c | 1 +
5 files changed, 815 insertions(+)
create mode 100644 riscv/sbi-dbtr.c
diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
index a5738a5c..78fd6e2a 100644
--- a/lib/riscv/asm/sbi.h
+++ b/lib/riscv/asm/sbi.h
@@ -51,6 +51,7 @@ enum sbi_ext_id {
SBI_EXT_SUSP = 0x53555350,
SBI_EXT_FWFT = 0x46574654,
SBI_EXT_SSE = 0x535345,
+ SBI_EXT_DBTR = 0x44425452,
};
enum sbi_ext_base_fid {
diff --git a/riscv/Makefile b/riscv/Makefile
index 11e68eae..55c7ac93 100644
--- a/riscv/Makefile
+++ b/riscv/Makefile
@@ -20,6 +20,7 @@ all: $(tests)
$(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-asm.o
$(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-fwft.o
$(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-sse.o
+$(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-dbtr.o
all_deps += $($(TEST_DIR)/sbi-deps)
diff --git a/riscv/sbi-dbtr.c b/riscv/sbi-dbtr.c
new file mode 100644
index 00000000..a4bfa41e
--- /dev/null
+++ b/riscv/sbi-dbtr.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SBI DBTR testsuite
+ *
+ * Copyright (C) 2025, Rivos Inc., Jesse Taube <jesse@rivosinc.com>
+ */
+
+#include <asm/io.h>
+#include <bitops.h>
+#include <asm/processor.h>
+
+#include "sbi-tests.h"
+
+#define SBI_DBTR_SHMEM_INVALID_ADDR (-1UL)
+#define RV_MAX_TRIGGERS 32
+
+#define SBI_DBTR_TRIG_STATE_MAPPED BIT(0)
+#define SBI_DBTR_TRIG_STATE_U BIT(1)
+#define SBI_DBTR_TRIG_STATE_S BIT(2)
+#define SBI_DBTR_TRIG_STATE_VU BIT(3)
+#define SBI_DBTR_TRIG_STATE_VS BIT(4)
+#define SBI_DBTR_TRIG_STATE_HAVE_HW_TRIG BIT(5)
+
+#define SBI_DBTR_TRIG_STATE_HW_TRIG_IDX_SHIFT 8
+#define SBI_DBTR_TRIG_STATE_HW_TRIG_IDX(trig_state) (trig_state >> SBI_DBTR_TRIG_STATE_HW_TRIG_IDX_SHIFT)
+
+#define SBI_DBTR_TDATA1_TYPE_SHIFT (__riscv_xlen - 4)
+
+#define SBI_DBTR_TDATA1_MCONTROL6_LOAD_BIT BIT(0)
+#define SBI_DBTR_TDATA1_MCONTROL6_STORE_BIT BIT(1)
+#define SBI_DBTR_TDATA1_MCONTROL6_EXECUTE_BIT BIT(2)
+#define SBI_DBTR_TDATA1_MCONTROL6_U_BIT BIT(3)
+#define SBI_DBTR_TDATA1_MCONTROL6_S_BIT BIT(4)
+#define SBI_DBTR_TDATA1_MCONTROL6_SELECT_BIT BIT(21)
+#define SBI_DBTR_TDATA1_MCONTROL6_VS_BIT BIT(23)
+#define SBI_DBTR_TDATA1_MCONTROL6_VU_BIT BIT(24)
+
+#define SBI_DBTR_TDATA1_MCONTROL_LOAD_BIT BIT(0)
+#define SBI_DBTR_TDATA1_MCONTROL_STORE_BIT BIT(1)
+#define SBI_DBTR_TDATA1_MCONTROL_EXECUTE_BIT BIT(2)
+#define SBI_DBTR_TDATA1_MCONTROL_U_BIT BIT(3)
+#define SBI_DBTR_TDATA1_MCONTROL_S_BIT BIT(4)
+#define SBI_DBTR_TDATA1_MCONTROL_SELECT_BIT BIT(19)
+
+enum McontrolType {
+ SBI_DBTR_TDATA1_TYPE_NONE = (0UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_LEGACY = (1UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_MCONTROL = (2UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_ICOUNT = (3UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_ITRIGGER = (4UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_ETRIGGER = (5UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_MCONTROL6 = (6UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_TMEXTTRIGGER = (7UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_RESERVED0 = (8UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_RESERVED1 = (9UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_RESERVED2 = (10UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_RESERVED3 = (11UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_CUSTOM0 = (12UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_CUSTOM1 = (13UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_CUSTOM2 = (14UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+ SBI_DBTR_TDATA1_TYPE_DISABLED = (15UL << SBI_DBTR_TDATA1_TYPE_SHIFT),
+};
+
+enum Tdata1Value {
+ VALUE_NONE = 0,
+ VALUE_LOAD = BIT(0),
+ VALUE_STORE = BIT(1),
+ VALUE_EXECUTE = BIT(2),
+};
+
+enum Tdata1Mode {
+ MODE_NONE = 0,
+ MODE_M = BIT(0),
+ MODE_U = BIT(1),
+ MODE_S = BIT(2),
+ MODE_VU = BIT(3),
+ MODE_VS = BIT(4),
+};
+
+enum sbi_ext_dbtr_fid {
+ SBI_EXT_DBTR_NUM_TRIGGERS = 0,
+ SBI_EXT_DBTR_SETUP_SHMEM,
+ SBI_EXT_DBTR_TRIGGER_READ,
+ SBI_EXT_DBTR_TRIGGER_INSTALL,
+ SBI_EXT_DBTR_TRIGGER_UPDATE,
+ SBI_EXT_DBTR_TRIGGER_UNINSTALL,
+ SBI_EXT_DBTR_TRIGGER_ENABLE,
+ SBI_EXT_DBTR_TRIGGER_DISABLE,
+};
+
+struct sbi_dbtr_data_msg {
+ unsigned long tstate;
+ unsigned long tdata1;
+ unsigned long tdata2;
+ unsigned long tdata3;
+};
+
+struct sbi_dbtr_id_msg {
+ unsigned long idx;
+};
+
+/* SBI shared mem messages layout */
+struct sbi_dbtr_shmem_entry {
+ union {
+ struct sbi_dbtr_data_msg data;
+ struct sbi_dbtr_id_msg id;
+ };
+};
+
+static bool dbtr_handled;
+
+/* Expected to be leaf function as not to disrupt frame-pointer */
+static __attribute__((naked)) void exec_call(void)
+{
+ /* skip over nop when triggered instead of ret. */
+ asm volatile (".option push\n"
+ ".option arch, -c\n"
+ "nop\n"
+ "ret\n"
+ ".option pop\n");
+}
+
+static void dbtr_exception_handler(struct pt_regs *regs)
+{
+ dbtr_handled = true;
+
+ /* Reading *epc may cause a fault, skip over nop */
+ if ((void *)regs->epc == exec_call) {
+ regs->epc += 4;
+ return;
+ }
+
+ /* WARNING: Skips over the trapped intruction */
+ regs->epc += RV_INSN_LEN(readw((void *)regs->epc));
+}
+
+static bool do_save(void *tdata2)
+{
+ bool ret;
+
+ writel(0, tdata2);
+
+ ret = dbtr_handled;
+ dbtr_handled = false;
+
+ return ret;
+}
+
+static bool do_load(void *tdata2)
+{
+ bool ret;
+
+ readl(tdata2);
+
+ ret = dbtr_handled;
+ dbtr_handled = false;
+
+ return ret;
+}
+
+static bool do_exec(void)
+{
+ bool ret;
+
+ exec_call();
+
+ ret = dbtr_handled;
+ dbtr_handled = false;
+
+ return ret;
+}
+
+static unsigned long gen_tdata1_mcontrol(enum Tdata1Mode mode, enum Tdata1Value value)
+{
+ unsigned long tdata1 = SBI_DBTR_TDATA1_TYPE_MCONTROL;
+
+ if (value & VALUE_LOAD)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL_LOAD_BIT;
+
+ if (value & VALUE_STORE)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL_STORE_BIT;
+
+ if (value & VALUE_EXECUTE)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL_EXECUTE_BIT;
+
+ if (mode & MODE_M)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL_U_BIT;
+
+ if (mode & MODE_U)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL_U_BIT;
+
+ if (mode & MODE_S)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL_S_BIT;
+
+ return tdata1;
+}
+
+static unsigned long gen_tdata1_mcontrol6(enum Tdata1Mode mode, enum Tdata1Value value)
+{
+ unsigned long tdata1 = SBI_DBTR_TDATA1_TYPE_MCONTROL6;
+
+ if (value & VALUE_LOAD)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_LOAD_BIT;
+
+ if (value & VALUE_STORE)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_STORE_BIT;
+
+ if (value & VALUE_EXECUTE)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_EXECUTE_BIT;
+
+ if (mode & MODE_M)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_U_BIT;
+
+ if (mode & MODE_U)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_U_BIT;
+
+ if (mode & MODE_S)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_S_BIT;
+
+ if (mode & MODE_VU)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_VU_BIT;
+
+ if (mode & MODE_VS)
+ tdata1 |= SBI_DBTR_TDATA1_MCONTROL6_VS_BIT;
+
+ return tdata1;
+}
+
+static unsigned long gen_tdata1(enum McontrolType type, enum Tdata1Value value, enum Tdata1Mode mode)
+{
+ switch (type) {
+ case SBI_DBTR_TDATA1_TYPE_MCONTROL:
+ return gen_tdata1_mcontrol(mode, value);
+ case SBI_DBTR_TDATA1_TYPE_MCONTROL6:
+ return gen_tdata1_mcontrol6(mode, value);
+ default:
+ return 0;
+ }
+}
+
+static struct sbiret sbi_debug_num_triggers(unsigned long trig_tdata1)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_NUM_TRIGGERS, trig_tdata1, 0, 0, 0, 0, 0);
+}
+
+static struct sbiret sbi_debug_set_shmem_raw(unsigned long shmem_phys_lo,
+ unsigned long shmem_phys_hi,
+ unsigned long flags)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_SETUP_SHMEM, shmem_phys_lo,
+ shmem_phys_hi, flags, 0, 0, 0);
+}
+
+static struct sbiret sbi_debug_set_shmem(void *shmem)
+{
+ phys_addr_t p = virt_to_phys(shmem);
+
+ return sbi_debug_set_shmem_raw(lower_32_bits(p), upper_32_bits(p), 0);
+}
+
+static struct sbiret sbi_debug_read_triggers(unsigned long trig_idx_base,
+ unsigned long trig_count)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIGGER_READ, trig_idx_base,
+ trig_count, 0, 0, 0, 0);
+}
+
+static struct sbiret sbi_debug_install_triggers(unsigned long trig_count)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIGGER_INSTALL, trig_count, 0, 0, 0, 0, 0);
+}
+
+static struct sbiret sbi_debug_update_triggers(unsigned long trig_count)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIGGER_UPDATE, trig_count, 0, 0, 0, 0, 0);
+}
+
+static struct sbiret sbi_debug_uninstall_triggers(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIGGER_UNINSTALL, trig_idx_base,
+ trig_idx_mask, 0, 0, 0, 0);
+}
+
+static struct sbiret sbi_debug_enable_triggers(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIGGER_ENABLE, trig_idx_base,
+ trig_idx_mask, 0, 0, 0, 0);
+}
+
+static struct sbiret sbi_debug_disable_triggers(unsigned long trig_idx_base,
+ unsigned long trig_idx_mask)
+{
+ return sbi_ecall(SBI_EXT_DBTR, SBI_EXT_DBTR_TRIGGER_DISABLE, trig_idx_base,
+ trig_idx_mask, 0, 0, 0, 0);
+}
+
+static bool dbtr_install_trigger(struct sbi_dbtr_shmem_entry *shmem, void *tdata2,
+ unsigned long tdata1)
+{
+ struct sbiret sbi_ret;
+ bool ret;
+
+ shmem->data.tdata1 = tdata1;
+ shmem->data.tdata2 = (unsigned long)tdata2;
+
+ sbi_ret = sbi_debug_install_triggers(1);
+ ret = sbiret_report_error(&sbi_ret, SBI_SUCCESS, "sbi_debug_install_triggers");
+ if (ret)
+ install_exception_handler(EXC_BREAKPOINT, dbtr_exception_handler);
+
+ return ret;
+}
+
+static bool dbtr_uninstall_trigger(void)
+{
+ struct sbiret ret;
+
+ install_exception_handler(EXC_BREAKPOINT, NULL);
+
+ ret = sbi_debug_uninstall_triggers(0, 1);
+ return sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers");
+}
+
+static unsigned long dbtr_test_num_triggers(void)
+{
+ struct sbiret ret;
+ unsigned long tdata1 = 0;
+ /* sbi_debug_num_triggers will return trig_max in sbiret.value when trig_tdata1 == 0 */
+
+ /* should be at least one trigger. */
+ ret = sbi_debug_num_triggers(tdata1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_num_triggers");
+
+ if (ret.value == 0)
+ report_fail("sbi_debug_num_triggers: Returned 0 triggers available");
+ else
+ report_pass("sbi_debug_num_triggers: Returned %lu triggers available", ret.value);
+
+ return ret.value;
+}
+
+static enum McontrolType dbtr_test_type(unsigned long *num_trig)
+{
+ struct sbiret ret;
+ unsigned long tdata1 = SBI_DBTR_TDATA1_TYPE_MCONTROL6;
+
+ ret = sbi_debug_num_triggers(tdata1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_num_triggers");
+ if (ret.value > 0) {
+ report_pass("sbi_debug_num_triggers: Returned %lu mcontrol6 triggers available",
+ ret.value);
+ *num_trig = ret.value;
+ return tdata1;
+ }
+
+ tdata1 = SBI_DBTR_TDATA1_TYPE_MCONTROL;
+
+ ret = sbi_debug_num_triggers(tdata1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_num_triggers");
+ *num_trig = ret.value;
+ if (ret.value > 0) {
+ report_pass("sbi_debug_num_triggers: Returned %lu mcontrol triggers available",
+ ret.value);
+ return tdata1;
+ }
+
+ report_fail("sbi_debug_num_triggers: Returned 0 mcontrol(6) triggers available");
+
+ return SBI_DBTR_TDATA1_TYPE_NONE;
+}
+
+static struct sbiret dbtr_test_save_install_uninstall(struct sbi_dbtr_shmem_entry *shmem,
+ enum McontrolType type)
+{
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("save_trigger");
+
+ shmem->data.tdata1 = gen_tdata1(type, VALUE_STORE, MODE_S | MODE_S);
+ shmem->data.tdata2 = (unsigned long)&test;
+
+ ret = sbi_debug_install_triggers(1);
+ if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers")) {
+ report_prefix_pop();
+ return ret;
+ }
+
+ install_exception_handler(EXC_BREAKPOINT, dbtr_exception_handler);
+
+ report(do_save(&test), "triggered");
+
+ if (do_load(&test))
+ report_fail("triggered by load");
+
+ ret = sbi_debug_uninstall_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers");
+
+ if (do_save(&test))
+ report_fail("triggered after uninstall");
+
+ install_exception_handler(EXC_BREAKPOINT, NULL);
+ report_prefix_pop();
+
+ return ret;
+}
+
+static void dbtr_test_update(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("update_trigger");
+
+ if (!dbtr_install_trigger(shmem, NULL, gen_tdata1(type, VALUE_NONE, MODE_NONE))) {
+ report_prefix_pop();
+ return;
+ }
+
+ shmem->id.idx = 0;
+ shmem->data.tdata1 = gen_tdata1(type, VALUE_STORE, MODE_S);
+ shmem->data.tdata2 = (unsigned long)&test;
+
+ ret = sbi_debug_update_triggers(1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_update_triggers");
+
+ report(do_save(&test), "triggered");
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+static void dbtr_test_load(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ static unsigned long test;
+
+ report_prefix_push("load_trigger");
+ if (!dbtr_install_trigger(shmem, &test, gen_tdata1(type, VALUE_LOAD, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ report(do_load(&test), "triggered");
+
+ if (do_save(&test))
+ report_fail("triggered by save");
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+static void dbtr_test_disable_enable(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("sbi_debug_disable_triggers");
+ if (!dbtr_install_trigger(shmem, &test, gen_tdata1(type, VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ ret = sbi_debug_disable_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_disable_triggers");
+
+ if (do_save(&test)) {
+ report_fail("should not trigger");
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+ report_skip("sbi_debug_enable_triggers: no disable");
+
+ return;
+ }
+
+ report_pass("should not trigger");
+
+ report_prefix_pop();
+ report_prefix_push("sbi_debug_enable_triggers");
+
+ ret = sbi_debug_enable_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_enable_triggers");
+
+ report(do_save(&test), "triggered");
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+static void dbtr_test_exec(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ static unsigned long test;
+
+ report_prefix_push("exec_trigger");
+ /* check if loads and saves trigger exec */
+ if (!dbtr_install_trigger(shmem, &test, gen_tdata1(type, VALUE_EXECUTE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ if (do_load(&test))
+ report_fail("triggered by load");
+
+ if (do_save(&test))
+ report_fail("triggered by save");
+
+ dbtr_uninstall_trigger();
+
+ /* Check if exec works */
+ if (!dbtr_install_trigger(shmem, exec_call, gen_tdata1(type, VALUE_EXECUTE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+ report(do_exec(), "exec trigger");
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+static void dbtr_test_read(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ const unsigned long tstatus_expected = SBI_DBTR_TRIG_STATE_S | SBI_DBTR_TRIG_STATE_MAPPED;
+ const unsigned long tdata1 = gen_tdata1(type, VALUE_STORE, MODE_S);
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("sbi_debug_read_triggers");
+ if (!dbtr_install_trigger(shmem, &test, tdata1)) {
+ report_prefix_pop();
+ return;
+ }
+
+ ret = sbi_debug_read_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers");
+
+ report(shmem->data.tdata1 == tdata1, "tdata1 expected: 0x%016lx, found: 0x%016lx",
+ tdata1, shmem->data.tdata1);
+ report(shmem->data.tdata2 == ((unsigned long)&test),
+ "tdata2 expected: 0x%016lx, found: 0x%016lx", ((unsigned long)&test),
+ shmem->data.tdata2);
+ report(shmem->data.tstate == tstatus_expected, "tstate expected: 0x%016lx, found: 0x%016lx",
+ tstatus_expected, shmem->data.tstate);
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+static void check_exec(unsigned long base)
+{
+ struct sbiret ret;
+
+ report(do_exec(), "exec triggered");
+
+ ret = sbi_debug_uninstall_triggers(base, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers");
+}
+
+static void dbtr_test_multiple(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type,
+ unsigned long num_trigs)
+{
+ static unsigned long test[2];
+ struct sbiret ret;
+ bool have_three = num_trigs > 2;
+
+ if (num_trigs < 2)
+ return;
+
+ report_prefix_push("test_multiple");
+
+ if (!dbtr_install_trigger(shmem, &test[0], gen_tdata1(type, VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+ if (!dbtr_install_trigger(shmem, &test[1], gen_tdata1(type, VALUE_LOAD, MODE_S)))
+ goto error;
+ if (have_three &&
+ !dbtr_install_trigger(shmem, exec_call, gen_tdata1(type, VALUE_EXECUTE, MODE_S))) {
+ ret = sbi_debug_uninstall_triggers(1, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers");
+ goto error;
+ }
+
+ report(do_save(&test[0]), "save triggered");
+
+ if (do_load(&test[0]))
+ report_fail("save triggered by load");
+
+ report(do_load(&test[1]), "load triggered");
+
+ if (do_save(&test[1]))
+ report_fail("load triggered by save");
+
+ if (have_three)
+ check_exec(2);
+
+ ret = sbi_debug_uninstall_triggers(1, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers");
+
+ if (do_load(&test[1]))
+ report_fail("load triggered after uninstall");
+
+ report(do_save(&test[0]), "save triggered");
+
+ if (!have_three &&
+ dbtr_install_trigger(shmem, exec_call, gen_tdata1(type, VALUE_EXECUTE, MODE_S)))
+ check_exec(1);
+
+error:
+ ret = sbi_debug_uninstall_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers");
+
+ install_exception_handler(EXC_BREAKPOINT, NULL);
+ report_prefix_pop();
+}
+
+static void dbtr_test_multiple_types(struct sbi_dbtr_shmem_entry *shmem, unsigned long type)
+{
+ static unsigned long test;
+
+ report_prefix_push("dbtr_test_multiple_types");
+
+ /* check if loads and saves trigger exec */
+ if (!dbtr_install_trigger(shmem, &test,
+ gen_tdata1(type, VALUE_EXECUTE | VALUE_LOAD | VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ report(do_load(&test), "load trigger");
+
+ report(do_save(&test), "save trigger");
+
+ dbtr_uninstall_trigger();
+
+ /* Check if exec works */
+ if (!dbtr_install_trigger(shmem, exec_call,
+ gen_tdata1(type, VALUE_EXECUTE | VALUE_LOAD | VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ report(do_exec(), "exec trigger");
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+static void dbtr_test_disable_uninstall(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("disable uninstall");
+ if (!dbtr_install_trigger(shmem, &test, gen_tdata1(type, VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ ret = sbi_debug_disable_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_disable_triggers");
+
+ dbtr_uninstall_trigger();
+
+ if (!dbtr_install_trigger(shmem, &test, gen_tdata1(type, VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ report(do_save(&test), "triggered");
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+static void dbtr_test_uninstall_enable(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("uninstall enable");
+ if (!dbtr_install_trigger(shmem, &test, gen_tdata1(type, VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+ dbtr_uninstall_trigger();
+
+ ret = sbi_debug_enable_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_enable_triggers");
+
+ install_exception_handler(EXC_BREAKPOINT, dbtr_exception_handler);
+
+ report(!do_save(&test), "should not trigger");
+
+ install_exception_handler(EXC_BREAKPOINT, NULL);
+ report_prefix_pop();
+}
+
+static void dbtr_test_uninstall_update(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("uninstall update");
+ if (!dbtr_install_trigger(shmem, NULL, gen_tdata1(type, VALUE_NONE, MODE_NONE))) {
+ report_prefix_pop();
+ return;
+ }
+
+ dbtr_uninstall_trigger();
+
+ shmem->id.idx = 0;
+ shmem->data.tdata1 = gen_tdata1(type, VALUE_STORE, MODE_S);
+ shmem->data.tdata2 = (unsigned long)&test;
+
+ ret = sbi_debug_update_triggers(1);
+ sbiret_report_error(&ret, SBI_ERR_FAILURE, "sbi_debug_update_triggers");
+
+ install_exception_handler(EXC_BREAKPOINT, dbtr_exception_handler);
+
+ report(!do_save(&test), "should not trigger");
+
+ install_exception_handler(EXC_BREAKPOINT, NULL);
+ report_prefix_pop();
+}
+
+static void dbtr_test_disable_read(struct sbi_dbtr_shmem_entry *shmem, enum McontrolType type)
+{
+ const unsigned long tstatus_expected = SBI_DBTR_TRIG_STATE_S | SBI_DBTR_TRIG_STATE_MAPPED;
+ const unsigned long tdata1 = gen_tdata1(type, VALUE_STORE, MODE_NONE);
+ static unsigned long test;
+ struct sbiret ret;
+
+ report_prefix_push("disable_read");
+ if (!dbtr_install_trigger(shmem, &test, gen_tdata1(type, VALUE_STORE, MODE_S))) {
+ report_prefix_pop();
+ return;
+ }
+
+ ret = sbi_debug_disable_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_disable_triggers");
+
+ ret = sbi_debug_read_triggers(0, 1);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers");
+
+ report(shmem->data.tdata1 == tdata1, "tdata1 expected: 0x%016lx, found: 0x%016lx",
+ tdata1, shmem->data.tdata1);
+ report(shmem->data.tdata2 == ((unsigned long)&test),
+ "tdata2 expected: 0x%016lx, found: 0x%016lx",
+ ((unsigned long)&test), shmem->data.tdata2);
+ report(shmem->data.tstate == tstatus_expected, "tstate expected: 0x%016lx, found: 0x%016lx",
+ tstatus_expected, shmem->data.tstate);
+
+ dbtr_uninstall_trigger();
+ report_prefix_pop();
+}
+
+void check_dbtr(void)
+{
+ static struct sbi_dbtr_shmem_entry shmem[RV_MAX_TRIGGERS] = {};
+ unsigned long num_trigs;
+ enum McontrolType trig_type;
+ struct sbiret ret;
+
+ report_prefix_push("dbtr");
+
+ if (!sbi_probe(SBI_EXT_DBTR)) {
+ report_skip("extension not available");
+ report_prefix_pop();
+ return;
+ }
+
+ if (__sbi_get_imp_id() == SBI_IMPL_OPENSBI &&
+ __sbi_get_imp_version() < sbi_impl_opensbi_mk_version(1, 6)) {
+ report_skip("OpenSBI < v1.7 detected, skipping tests");
+ report_prefix_pop();
+ return;
+ }
+
+ num_trigs = dbtr_test_num_triggers();
+ if (!num_trigs)
+ goto error;
+
+ trig_type = dbtr_test_type(&num_trigs);
+ if (trig_type == SBI_DBTR_TDATA1_TYPE_NONE)
+ goto error;
+
+ ret = sbi_debug_set_shmem(shmem);
+ sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_set_shmem");
+
+ ret = dbtr_test_save_install_uninstall(&shmem[0], trig_type);
+ /* install or uninstall failed */
+ if (ret.error != SBI_SUCCESS)
+ goto error;
+
+ dbtr_test_load(&shmem[0], trig_type);
+ dbtr_test_exec(&shmem[0], trig_type);
+ dbtr_test_read(&shmem[0], trig_type);
+ dbtr_test_disable_enable(&shmem[0], trig_type);
+ dbtr_test_update(&shmem[0], trig_type);
+ dbtr_test_multiple_types(&shmem[0], trig_type);
+ dbtr_test_multiple(shmem, trig_type, num_trigs);
+ dbtr_test_disable_uninstall(&shmem[0], trig_type);
+ dbtr_test_uninstall_enable(&shmem[0], trig_type);
+ dbtr_test_uninstall_update(&shmem[0], trig_type);
+ dbtr_test_disable_read(&shmem[0], trig_type);
+
+error:
+ report_prefix_pop();
+}
diff --git a/riscv/sbi-tests.h b/riscv/sbi-tests.h
index d5c4ae70..6a227745 100644
--- a/riscv/sbi-tests.h
+++ b/riscv/sbi-tests.h
@@ -99,6 +99,7 @@ static inline bool env_enabled(const char *env)
void sbi_bad_fid(int ext);
void check_sse(void);
+void check_dbtr(void);
#endif /* __ASSEMBLER__ */
#endif /* _RISCV_SBI_TESTS_H_ */
diff --git a/riscv/sbi.c b/riscv/sbi.c
index edb1a6be..5bd496d0 100644
--- a/riscv/sbi.c
+++ b/riscv/sbi.c
@@ -1561,6 +1561,7 @@ int main(int argc, char **argv)
check_susp();
check_sse();
check_fwft();
+ check_dbtr();
return report_summary();
}
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h
2025-06-05 16:18 [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h Jesse Taube
2025-06-05 16:18 ` [kvm-unit-tests PATCH v3 2/2] riscv: sbi: Add SBI Debug Triggers Extension tests Jesse Taube
@ 2025-06-06 8:32 ` Andrew Jones
2025-07-02 14:49 ` Andrew Jones
2 siblings, 0 replies; 5+ messages in thread
From: Andrew Jones @ 2025-06-06 8:32 UTC (permalink / raw)
To: Jesse Taube
Cc: kvm, kvm-riscv, linux-kselftest, Atish Patra, Anup Patel,
Palmer Dabbelt, Clément Léger, Himanshu Chauhan,
Charlie Jenkins
On Thu, Jun 05, 2025 at 09:18:05AM -0700, Jesse Taube wrote:
> When handeling traps and faults it is offten necessary to know the size
handling
often
> of the instruction at epc. Add RV_INSN_LEN to calculate the
> instruction size.
>
> Signed-off-by: Jesse Taube <jesse@rivosinc.com>
> ---
> lib/riscv/asm/processor.h | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/lib/riscv/asm/processor.h b/lib/riscv/asm/processor.h
> index 40104272..631ce226 100644
> --- a/lib/riscv/asm/processor.h
> +++ b/lib/riscv/asm/processor.h
> @@ -7,6 +7,8 @@
> #define EXCEPTION_CAUSE_MAX 24
> #define INTERRUPT_CAUSE_MAX 16
>
> +#define RV_INSN_LEN(insn) ((((insn) & 0x3) < 0x3) ? 2 : 4)
> +
> typedef void (*exception_fn)(struct pt_regs *);
>
> struct thread_info {
> --
> 2.43.0
>
I've fixed the two typos while applying to riscv/sbi[1]
[1] https://gitlab.com/jones-drew/kvm-unit-tests/-/commits/riscv/sbi
Thanks,
drew
>
> --
> kvm-riscv mailing list
> kvm-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kvm-riscv
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [kvm-unit-tests PATCH v3 2/2] riscv: sbi: Add SBI Debug Triggers Extension tests
2025-06-05 16:18 ` [kvm-unit-tests PATCH v3 2/2] riscv: sbi: Add SBI Debug Triggers Extension tests Jesse Taube
@ 2025-06-06 8:48 ` Andrew Jones
0 siblings, 0 replies; 5+ messages in thread
From: Andrew Jones @ 2025-06-06 8:48 UTC (permalink / raw)
To: Jesse Taube
Cc: kvm, kvm-riscv, linux-kselftest, Atish Patra, Anup Patel,
Palmer Dabbelt, Clément Léger, Himanshu Chauhan,
Charlie Jenkins
On Thu, Jun 05, 2025 at 09:18:06AM -0700, Jesse Taube wrote:
> Add tests for the DBTR SBI extension.
>
> Signed-off-by: Jesse Taube <jesse@rivosinc.com>
> ---
> V1 -> V2:
> - Call report_prefix_pop before returning
> - Disable compressed instructions in exec_call, update related comment
> - Remove extra "| 1" in dbtr_test_load
> - Remove extra newlines
> - Remove extra tabs in check_exec
> - Remove typedefs from enums
> - Return when dbtr_install_trigger fails
> - s/avalible/available/g
> - s/unistall/uninstall/g
> V2 -> V3:
> - Change SBI_DBTR_SHMEM_INVALID_ADDR to -1UL
> - Move all dbtr functions to sbi-dbtr.c
> - Move INSN_LEN to processor.h
> - Update include list
> - Use C-style comments
> ---
> lib/riscv/asm/sbi.h | 1 +
> riscv/Makefile | 1 +
> riscv/sbi-dbtr.c | 811 ++++++++++++++++++++++++++++++++++++++++++++
> riscv/sbi-tests.h | 1 +
> riscv/sbi.c | 1 +
> 5 files changed, 815 insertions(+)
> create mode 100644 riscv/sbi-dbtr.c
>
> diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
> index a5738a5c..78fd6e2a 100644
> --- a/lib/riscv/asm/sbi.h
> +++ b/lib/riscv/asm/sbi.h
> @@ -51,6 +51,7 @@ enum sbi_ext_id {
> SBI_EXT_SUSP = 0x53555350,
> SBI_EXT_FWFT = 0x46574654,
> SBI_EXT_SSE = 0x535345,
> + SBI_EXT_DBTR = 0x44425452,
> };
>
> enum sbi_ext_base_fid {
> diff --git a/riscv/Makefile b/riscv/Makefile
> index 11e68eae..55c7ac93 100644
> --- a/riscv/Makefile
> +++ b/riscv/Makefile
> @@ -20,6 +20,7 @@ all: $(tests)
> $(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-asm.o
> $(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-fwft.o
> $(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-sse.o
> +$(TEST_DIR)/sbi-deps += $(TEST_DIR)/sbi-dbtr.o
>
> all_deps += $($(TEST_DIR)/sbi-deps)
>
> diff --git a/riscv/sbi-dbtr.c b/riscv/sbi-dbtr.c
> new file mode 100644
> index 00000000..a4bfa41e
> --- /dev/null
> +++ b/riscv/sbi-dbtr.c
> @@ -0,0 +1,811 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SBI DBTR testsuite
> + *
> + * Copyright (C) 2025, Rivos Inc., Jesse Taube <jesse@rivosinc.com>
> + */
> +
> +#include <asm/io.h>
> +#include <bitops.h>
> +#include <asm/processor.h>
> +
> +#include "sbi-tests.h"
Still missing at least libcflat.h (report_*). I'm not a big fan of
libcflat.h (a collection of random stuff...), but until somebody
gets around to dividing it up correctly, then pretty much every
unit test file will need it.
> +
> +#define SBI_DBTR_SHMEM_INVALID_ADDR (-1UL)
I was going to complain that my suggestion to use get_invalid_addr() was
ignored, but now I see that SBI_DBTR_SHMEM_INVALID_ADDR isn't used at all?
Thanks,
drew
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h
2025-06-05 16:18 [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h Jesse Taube
2025-06-05 16:18 ` [kvm-unit-tests PATCH v3 2/2] riscv: sbi: Add SBI Debug Triggers Extension tests Jesse Taube
2025-06-06 8:32 ` [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h Andrew Jones
@ 2025-07-02 14:49 ` Andrew Jones
2 siblings, 0 replies; 5+ messages in thread
From: Andrew Jones @ 2025-07-02 14:49 UTC (permalink / raw)
To: Jesse Taube
Cc: kvm, kvm-riscv, linux-kselftest, Atish Patra, Anup Patel,
Palmer Dabbelt, Clément Léger, Himanshu Chauhan,
Charlie Jenkins
On Thu, Jun 05, 2025 at 09:18:05AM -0700, Jesse Taube wrote:
> When handeling traps and faults it is offten necessary to know the size
> of the instruction at epc. Add RV_INSN_LEN to calculate the
> instruction size.
>
> Signed-off-by: Jesse Taube <jesse@rivosinc.com>
> ---
> lib/riscv/asm/processor.h | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/lib/riscv/asm/processor.h b/lib/riscv/asm/processor.h
> index 40104272..631ce226 100644
> --- a/lib/riscv/asm/processor.h
> +++ b/lib/riscv/asm/processor.h
> @@ -7,6 +7,8 @@
> #define EXCEPTION_CAUSE_MAX 24
> #define INTERRUPT_CAUSE_MAX 16
>
> +#define RV_INSN_LEN(insn) ((((insn) & 0x3) < 0x3) ? 2 : 4)
> +
> typedef void (*exception_fn)(struct pt_regs *);
>
> struct thread_info {
> --
> 2.43.0
Merged. Thanks
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-07-02 14:49 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-05 16:18 [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h Jesse Taube
2025-06-05 16:18 ` [kvm-unit-tests PATCH v3 2/2] riscv: sbi: Add SBI Debug Triggers Extension tests Jesse Taube
2025-06-06 8:48 ` Andrew Jones
2025-06-06 8:32 ` [kvm-unit-tests PATCH v3 1/2] riscv: Add RV_INSN_LEN to processor.h Andrew Jones
2025-07-02 14:49 ` Andrew Jones
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).