public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
* [kvm-unit-tests PATCH 1/2] riscv: sbi: dbtr: fix enable trigger return code check
@ 2026-03-13  5:08 Nicholas Piggin
  2026-03-13  5:08 ` [kvm-unit-tests PATCH 2/2] riscv: dbtr: Add initial API corner case / error handling tests Nicholas Piggin
  0 siblings, 1 reply; 2+ messages in thread
From: Nicholas Piggin @ 2026-03-13  5:08 UTC (permalink / raw)
  To: Andrew Jones; +Cc: Nicholas Piggin, kvm-riscv, kvm, opensbi

Enabling triggers that are not mapped to a HW debug trigger
should return SBI_ERR_INVALID_PARAM.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 riscv/sbi-dbtr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/riscv/sbi-dbtr.c b/riscv/sbi-dbtr.c
index 129f79b8..489242b0 100644
--- a/riscv/sbi-dbtr.c
+++ b/riscv/sbi-dbtr.c
@@ -744,7 +744,7 @@ static void dbtr_test_uninstall_enable(struct sbi_dbtr_shmem_entry *shmem, enum
 	dbtr_uninstall_trigger();
 
 	ret = sbi_debug_enable_triggers(0, 1);
-	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_enable_triggers");
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_enable_triggers");
 
 	install_exception_handler(EXC_BREAKPOINT, dbtr_exception_handler);
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [kvm-unit-tests PATCH 2/2] riscv: dbtr: Add initial API corner case / error handling tests
  2026-03-13  5:08 [kvm-unit-tests PATCH 1/2] riscv: sbi: dbtr: fix enable trigger return code check Nicholas Piggin
@ 2026-03-13  5:08 ` Nicholas Piggin
  0 siblings, 0 replies; 2+ messages in thread
From: Nicholas Piggin @ 2026-03-13  5:08 UTC (permalink / raw)
  To: Andrew Jones; +Cc: Nicholas Piggin, kvm-riscv, kvm, opensbi

The SBI DBTR specification has quite detailed API requirements
that are easy to get wrong. This implements the beginning of
testing some corner cases, not with trigger behaviour but with
API behaviour.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
These tests expose a bunch of major and minor errors in OpenSBI
handling, for which I will post patches to the opensbi list. As
well as possibly a bug in the SBI spec, reported here.

https://github.com/riscv-non-isa/riscv-sbi-doc/issues/257

Thanks,
Nick
---
---
 riscv/sbi-dbtr.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 314 insertions(+)

diff --git a/riscv/sbi-dbtr.c b/riscv/sbi-dbtr.c
index 489242b0..156b743b 100644
--- a/riscv/sbi-dbtr.c
+++ b/riscv/sbi-dbtr.c
@@ -423,6 +423,318 @@ static enum McontrolType dbtr_test_type(unsigned long *num_trig)
 	return SBI_DBTR_TDATA1_TYPE_NONE;
 }
 
+static struct sbiret dbtr_test_api(unsigned long trig_max)
+{
+	/* + 1 so overflow can be tested */
+	static struct sbi_dbtr_shmem_entry shmem[RV_MAX_TRIGGERS + 1] = {};
+	static unsigned long test[RV_MAX_TRIGGERS + 1];
+	unsigned long tdata1;
+	unsigned long trig_type;
+	struct sbiret ret;
+	int i;
+
+	/*
+	 * This set of tests is to test API response, not hardware/trigger
+	 * behaviour, which is left to later tests.
+	 *
+	 * Notably absent from these API tests at the moment are:
+	 * - Enable/disable tests.
+	 * - trig_state verification.
+	 * - Testing more valid and invalid tdata1/2/3 values.
+	 */
+
+	trig_type = dbtr_test_type(&trig_max);
+	tdata1 = gen_tdata1(trig_type, VALUE_LOAD | VALUE_STORE, MODE_S);
+
+	/* + 1, so overflow can be tested */
+	memset(shmem, 0, sizeof(shmem));
+	for (i = 0; i < RV_MAX_TRIGGERS + 1; i++) {
+		shmem[i].data.tdata1 = tdata1;
+		shmem[i].data.tdata2 = (unsigned long)&test[i];
+	}
+
+	report_prefix_push("api");
+
+	report_prefix_push("no_shmem");
+	ret = sbi_debug_read_triggers(0, 0);
+	sbiret_report_error(&ret, SBI_ERR_NO_SHMEM, "sbi_debug_read_triggers");
+	report(ret.value == 0, "read return value");
+	ret = sbi_debug_install_triggers(1);
+	sbiret_report_error(&ret, SBI_ERR_NO_SHMEM, "sbi_debug_install_triggers");
+	report(ret.value == 0, "install return value");
+	ret = sbi_debug_update_triggers(1);
+	sbiret_report_error(&ret, SBI_ERR_NO_SHMEM, "sbi_debug_update_triggers");
+	report(ret.value == 0, "update return value");
+	report_prefix_pop();
+
+	report_prefix_push("shmem");
+	ret = sbi_debug_set_shmem_raw(1, 0, 0);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_set_shmem unaligned");
+	ret = sbi_debug_set_shmem_raw(0, -1, 0);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_ADDRESS, "sbi_debug_set_shmem bad address");
+	ret = sbi_debug_set_shmem(shmem);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_set_shmem");
+	ret = sbi_debug_read_triggers(0, 1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers");
+	ret = sbi_debug_set_shmem_raw(-1, -1, 0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_set_shmem unset");
+	ret = sbi_debug_read_triggers(0, 1);
+	sbiret_report_error(&ret, SBI_ERR_NO_SHMEM, "sbi_debug_read_triggers");
+	report_prefix_pop();
+
+	ret = sbi_debug_set_shmem(shmem);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_set_shmem");
+
+	report_prefix_push("read triggers");
+	ret = sbi_debug_read_triggers(0, 0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers 0, 0");
+	ret = sbi_debug_read_triggers(0, 1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers 0, 1");
+	ret = sbi_debug_read_triggers(trig_max - 1, 0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers trig_max - 1, 0");
+	ret = sbi_debug_read_triggers(trig_max - 1, 1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers trig_max - 1, 1");
+	ret = sbi_debug_read_triggers(trig_max, 0);
+	sbiret_report_error(&ret, SBI_ERR_BAD_RANGE, "sbi_debug_read_triggers trig_max, 0");
+	ret = sbi_debug_read_triggers(trig_max, 1);
+	sbiret_report_error(&ret, SBI_ERR_BAD_RANGE, "sbi_debug_read_triggers trig_max, 1");
+	ret = sbi_debug_read_triggers(0, trig_max);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers 0, trig_max");
+	ret = sbi_debug_read_triggers(1, trig_max);
+	sbiret_report_error(&ret, SBI_ERR_BAD_RANGE, "sbi_debug_read_triggers 1, trig_max");
+	ret = sbi_debug_read_triggers(0, 1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers 1");
+	ret = sbi_debug_read_triggers(0, trig_max);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_read_triggers trig_max");
+	report_prefix_pop();
+
+	report_prefix_push("install triggers");
+	ret = sbi_debug_install_triggers(0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 0");
+	ret = sbi_debug_install_triggers(trig_max + 1);
+	sbiret_report_error(&ret, SBI_ERR_BAD_RANGE, "sbi_debug_install_triggers 0");
+
+	shmem[0].data.tdata1 = gen_tdata1(trig_type, VALUE_LOAD, MODE_M);
+	ret = sbi_debug_install_triggers(1);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_install_triggers 1 invalid");
+	report(ret.value == 0, "error index 0");
+	shmem[0].data.tdata1 = tdata1;
+
+	if (trig_max > 1) {
+		shmem[1].data.tdata1 = gen_tdata1(trig_type, VALUE_LOAD, MODE_M);
+		ret = sbi_debug_install_triggers(2);
+		sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_install_triggers 2 invalid");
+		report(ret.value == 1, "error index 1");
+		shmem[1].data.tdata1 = tdata1;
+	}
+
+	ret = sbi_debug_install_triggers(1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+	report(shmem[0].data.tstate == 0, "install index 0");
+	ret = sbi_debug_install_triggers(trig_max);
+	sbiret_report_error(&ret, SBI_ERR_FAILURE, "sbi_debug_install_triggers trig_max");
+	report(ret.value == trig_max - 1, "error index trig_max - 1");
+	if (trig_max > 1) {
+		ret = sbi_debug_install_triggers(1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+		report(shmem[0].data.tstate == 1, "install index 1");
+
+		ret = sbi_debug_uninstall_triggers(0, 1);
+		if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 1")) {
+			report_prefix_pop();
+			goto out;
+		}
+
+		ret = sbi_debug_install_triggers(1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+		report(shmem[0].data.tstate == 0, "install index 0");
+
+		ret = sbi_debug_uninstall_triggers(1, 1);
+		if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 1 1")) {
+			report_prefix_pop();
+			goto out;
+		}
+	}
+	ret = sbi_debug_uninstall_triggers(0, 1);
+	if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 1")) {
+		report_prefix_pop();
+		goto out;
+	}
+
+	ret = sbi_debug_install_triggers(trig_max);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers trig_max");
+	ret = sbi_debug_uninstall_triggers(0, (1UL << trig_max) - 1);
+	if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 (1ULL << trig_max) - 1")) {
+		report_prefix_pop();
+		goto out;
+	}
+
+	/* Try to detect overrun by looking at the return value */
+	report_prefix_push("overrun");
+	shmem[trig_max].data.tdata1 = gen_tdata1(trig_type, VALUE_LOAD | VALUE_STORE, MODE_M);
+	ret = sbi_debug_install_triggers(trig_max + 1);
+	sbiret_report_error(&ret, SBI_ERR_BAD_RANGE, "sbi_debug_install_triggers trig_max");
+	report(ret.value == 0, "value overrun check");
+	shmem[trig_max].data.tdata1 = tdata1; /* restore valid */
+	report_prefix_pop();
+
+	report_prefix_pop();
+
+	report_prefix_push("update triggers");
+	ret = sbi_debug_update_triggers(0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_update_triggers 0");
+	ret = sbi_debug_update_triggers(trig_max + 1);
+	sbiret_report_error(&ret, SBI_ERR_BAD_RANGE, "sbi_debug_update_triggers 0");
+
+	shmem[0].data.tstate = 0;
+	ret = sbi_debug_update_triggers(1);
+	sbiret_report_error(&ret, SBI_ERR_FAILURE, "sbi_debug_update_triggers 1 not mapped");
+	report(ret.value == 0, "error index 0");
+
+	shmem[0].data.tstate = trig_max;
+	ret = sbi_debug_update_triggers(1);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_update_triggers 1 idx == trig_max");
+	report(ret.value == 0, "error index 0");
+
+	ret = sbi_debug_install_triggers(1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+
+	shmem[0].data.tstate = 0;
+	ret = sbi_debug_update_triggers(1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_update_triggers 1");
+
+	/* Update 2 both with same index */
+	shmem[1].data.tstate = 0;
+	ret = sbi_debug_update_triggers(2);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_update_triggers 2");
+
+	shmem[0].data.tstate = 1;
+	ret = sbi_debug_update_triggers(1);
+	sbiret_report_error(&ret, SBI_ERR_FAILURE, "sbi_debug_update_triggers 1 not mapped");
+	report(ret.value == 0, "error index 0");
+
+	shmem[0].data.tstate = 0;
+	shmem[1].data.tstate = 1;
+	ret = sbi_debug_update_triggers(2);
+	sbiret_report_error(&ret, SBI_ERR_FAILURE, "sbi_debug_update_triggers 2 not mapped");
+	report(ret.value == 1, "error index 1");
+
+	shmem[0].data.tstate = 0;
+	shmem[0].data.tdata1 = gen_tdata1(trig_type, VALUE_LOAD, MODE_M);
+	ret = sbi_debug_update_triggers(1);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_update_triggers 1 invalid");
+	report(ret.value == 0, "error index 0");
+	shmem[0].data.tdata1 = tdata1;
+	if (trig_max > 1) {
+		ret = sbi_debug_install_triggers(1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+		report(shmem[0].data.tstate == 1, "install index 1");
+
+		shmem[0].data.tstate = 1;
+		ret = sbi_debug_update_triggers(1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_update_triggers 1");
+
+		shmem[0].data.tstate = 0;
+		shmem[1].data.tstate = 1;
+		ret = sbi_debug_update_triggers(2);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_update_triggers 2");
+
+		ret = sbi_debug_uninstall_triggers(1, 1);
+		if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 1 1")) {
+			report_prefix_pop();
+			goto out;
+		}
+	}
+	ret = sbi_debug_uninstall_triggers(0, 1);
+	if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 1")) {
+		report_prefix_pop();
+		goto out;
+	}
+
+	/* Try to detect overrun by looking at the return value */
+	report_prefix_push("overrun");
+	ret = sbi_debug_install_triggers(1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers trig_max");
+
+	for (i = 0; i <= trig_max; i++)
+		shmem[trig_max].data.tstate = 0;
+	shmem[trig_max].data.tdata1 = gen_tdata1(trig_type, VALUE_LOAD | VALUE_STORE, MODE_M);
+	ret = sbi_debug_update_triggers(trig_max + 1);
+	sbiret_report_error(&ret, SBI_ERR_BAD_RANGE, "sbi_debug_install_triggers trig_max");
+	report(ret.value == 0, "value overrun check");
+	shmem[trig_max].data.tdata1 = tdata1; /* restore valid */
+
+	ret = sbi_debug_uninstall_triggers(0, 1);
+	if (!sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 1")) {
+		report_prefix_pop();
+		goto out;
+	}
+	report_prefix_pop();
+
+	report_prefix_pop();
+
+	report_prefix_push("uninstall triggers");
+	ret = sbi_debug_uninstall_triggers(0, 0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 0");
+	ret = sbi_debug_uninstall_triggers(trig_max, 0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers trig_max 0");
+	ret = sbi_debug_uninstall_triggers(0, 1);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_uninstall_triggers 0 1");
+	ret = sbi_debug_uninstall_triggers(trig_max, 1);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_uninstall_triggers trig_max 1");
+	ret = sbi_debug_install_triggers(1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+	report(shmem[0].data.tstate == 0, "install index 0");
+	ret = sbi_debug_uninstall_triggers(0, 2);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_uninstall_triggers 0 2");
+	ret = sbi_debug_uninstall_triggers(0, 3);
+	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_uninstall_triggers 0 3");
+	ret = sbi_debug_uninstall_triggers(0, 1);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 1");
+	if (trig_max > 1) {
+		ret = sbi_debug_install_triggers(2);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 2");
+		report(shmem[0].data.tstate == 0, "install index 0");
+		report(shmem[1].data.tstate == 1, "install index 1");
+		ret = sbi_debug_uninstall_triggers(0, 1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 1");
+		ret = sbi_debug_install_triggers(1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+		report(shmem[0].data.tstate == 0, "install index 0");
+		ret = sbi_debug_uninstall_triggers(1, 1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 1 1");
+		ret = sbi_debug_install_triggers(1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+		report(shmem[1].data.tstate == 1, "install index 1");
+		ret = sbi_debug_uninstall_triggers(0, 2);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 2");
+		ret = sbi_debug_install_triggers(1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 1");
+		report(shmem[1].data.tstate == 1, "install index 1");
+		ret = sbi_debug_uninstall_triggers(0, 3);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 3");
+		ret = sbi_debug_install_triggers(2);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_install_triggers 2");
+		report(shmem[0].data.tstate == 0, "install index 0");
+		report(shmem[1].data.tstate == 1, "install index 1");
+		ret = sbi_debug_uninstall_triggers(1, 1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 1 1");
+		ret = sbi_debug_uninstall_triggers(0, 3);
+		sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "sbi_debug_uninstall_triggers 0 3");
+		ret = sbi_debug_uninstall_triggers(0, 1);
+		sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_uninstall_triggers 0 1");
+	}
+	report_prefix_pop();
+
+out:
+	ret = sbi_debug_set_shmem_raw(-1, -1, 0);
+	sbiret_report_error(&ret, SBI_SUCCESS, "sbi_debug_set_shmem unset");
+
+	report_prefix_pop();
+
+	return ret;
+}
+
 static struct sbiret dbtr_test_store_install_uninstall(struct sbi_dbtr_shmem_entry *shmem,
 						      enum McontrolType type)
 {
@@ -838,6 +1150,8 @@ void check_dbtr(void)
 	if (!num_trigs)
 		goto exit_test;
 
+	dbtr_test_api(num_trigs);
+
 	trig_type = dbtr_test_type(&num_trigs);
 	if (trig_type == SBI_DBTR_TDATA1_TYPE_NONE)
 		goto exit_test;
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-03-13  5:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-13  5:08 [kvm-unit-tests PATCH 1/2] riscv: sbi: dbtr: fix enable trigger return code check Nicholas Piggin
2026-03-13  5:08 ` [kvm-unit-tests PATCH 2/2] riscv: dbtr: Add initial API corner case / error handling tests Nicholas Piggin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox