public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
* [kvm-unit-tests PATCH 0/3] riscv: sbi: Add SUSP tests
@ 2024-10-24 12:41 Andrew Jones
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 1/3] riscv: Implement setjmp/longjmp Andrew Jones
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Andrew Jones @ 2024-10-24 12:41 UTC (permalink / raw)
  To: kvm, kvm-riscv; +Cc: atishp, jamestiotio

Add tests for the SBI SUSP extension.

Based-on: https://gitlab.com/jones-drew/kvm-unit-tests/-/commits/riscv/sbi

Andrew Jones (3):
  riscv: Implement setjmp/longjmp
  riscv: sbi: Clean up env checking
  riscv: sbi: Add SUSP tests

 lib/riscv/asm/asm.h |   3 +
 lib/riscv/asm/sbi.h |   1 +
 lib/riscv/setjmp.S  |  50 +++++++
 lib/setjmp.h        |   4 +
 riscv/Makefile      |   1 +
 riscv/sbi-asm.S     |  55 ++++++++
 riscv/sbi-tests.h   |  19 +++
 riscv/sbi.c         | 308 ++++++++++++++++++++++++++++++++++++++++----
 8 files changed, 417 insertions(+), 24 deletions(-)
 create mode 100644 lib/riscv/setjmp.S

-- 
2.47.0


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

* [kvm-unit-tests PATCH 1/3] riscv: Implement setjmp/longjmp
  2024-10-24 12:41 [kvm-unit-tests PATCH 0/3] riscv: sbi: Add SUSP tests Andrew Jones
@ 2024-10-24 12:41 ` Andrew Jones
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 2/3] riscv: sbi: Clean up env checking Andrew Jones
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Andrew Jones @ 2024-10-24 12:41 UTC (permalink / raw)
  To: kvm, kvm-riscv; +Cc: atishp, jamestiotio

Being able to do setjmp and longjmp can be quite useful for tests.
Implement the functions for riscv.

Signed-off-by: Andrew Jones <andrew.jones@linux.dev>
---
 lib/riscv/setjmp.S | 50 ++++++++++++++++++++++++++++++++++++++++++++++
 lib/setjmp.h       |  4 ++++
 riscv/Makefile     |  1 +
 3 files changed, 55 insertions(+)
 create mode 100644 lib/riscv/setjmp.S

diff --git a/lib/riscv/setjmp.S b/lib/riscv/setjmp.S
new file mode 100644
index 000000000000..38b0f1cab576
--- /dev/null
+++ b/lib/riscv/setjmp.S
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#define __ASSEMBLY__
+#include <asm/asm.h>
+
+.section .text
+
+.balign 4
+.global setjmp
+setjmp:
+	REG_S	ra, (0 * SZREG)(a0)
+	REG_S	s0, (1 * SZREG)(a0)
+	REG_S	s1, (2 * SZREG)(a0)
+	REG_S	s2, (3 * SZREG)(a0)
+	REG_S	s3, (4 * SZREG)(a0)
+	REG_S	s4, (5 * SZREG)(a0)
+	REG_S	s5, (6 * SZREG)(a0)
+	REG_S	s6, (7 * SZREG)(a0)
+	REG_S	s7, (8 * SZREG)(a0)
+	REG_S	s8, (9 * SZREG)(a0)
+	REG_S	s9, (10 * SZREG)(a0)
+	REG_S	s10, (11 * SZREG)(a0)
+	REG_S	s11, (12 * SZREG)(a0)
+	REG_S	sp, (13 * SZREG)(a0)
+	REG_S	gp, (14 * SZREG)(a0)
+	REG_S	tp, (15 * SZREG)(a0)
+	li	a0, 0
+	ret
+
+.balign 4
+.global longjmp
+longjmp:
+	REG_L	ra, (0 * SZREG)(a0)
+	REG_L	s0, (1 * SZREG)(a0)
+	REG_L	s1, (2 * SZREG)(a0)
+	REG_L	s2, (3 * SZREG)(a0)
+	REG_L	s3, (4 * SZREG)(a0)
+	REG_L	s4, (5 * SZREG)(a0)
+	REG_L	s5, (6 * SZREG)(a0)
+	REG_L	s6, (7 * SZREG)(a0)
+	REG_L	s7, (8 * SZREG)(a0)
+	REG_L	s8, (9 * SZREG)(a0)
+	REG_L	s9, (10 * SZREG)(a0)
+	REG_L	s10, (11 * SZREG)(a0)
+	REG_L	s11, (12 * SZREG)(a0)
+	REG_L	sp, (13 * SZREG)(a0)
+	REG_L	gp, (14 * SZREG)(a0)
+	REG_L	tp, (15 * SZREG)(a0)
+	seqz	a0, a1
+	add	a0, a0, a1
+	ret
diff --git a/lib/setjmp.h b/lib/setjmp.h
index 6afdf665681a..f878ad81c645 100644
--- a/lib/setjmp.h
+++ b/lib/setjmp.h
@@ -8,7 +8,11 @@
 #define _LIBCFLAT_SETJMP_H_
 
 typedef struct jmp_buf_tag {
+#if defined(__i386__) || defined(__x86_64__)
 	long int regs[8];
+#elif defined(__riscv)
+	long int regs[16];
+#endif
 } jmp_buf[1];
 
 extern int setjmp (struct jmp_buf_tag env[1]);
diff --git a/riscv/Makefile b/riscv/Makefile
index 734441f94dad..28b04156bfd5 100644
--- a/riscv/Makefile
+++ b/riscv/Makefile
@@ -36,6 +36,7 @@ cflatobjs += lib/riscv/isa.o
 cflatobjs += lib/riscv/mmu.o
 cflatobjs += lib/riscv/processor.o
 cflatobjs += lib/riscv/sbi.o
+cflatobjs += lib/riscv/setjmp.o
 cflatobjs += lib/riscv/setup.o
 cflatobjs += lib/riscv/smp.o
 cflatobjs += lib/riscv/stack.o
-- 
2.47.0


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

* [kvm-unit-tests PATCH 2/3] riscv: sbi: Clean up env checking
  2024-10-24 12:41 [kvm-unit-tests PATCH 0/3] riscv: sbi: Add SUSP tests Andrew Jones
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 1/3] riscv: Implement setjmp/longjmp Andrew Jones
@ 2024-10-24 12:41 ` Andrew Jones
  2024-11-06 12:23   ` Andrew Jones
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 3/3] riscv: sbi: Add SUSP tests Andrew Jones
  2024-11-11 15:08 ` [kvm-unit-tests PATCH 0/3] " Andrew Jones
  3 siblings, 1 reply; 6+ messages in thread
From: Andrew Jones @ 2024-10-24 12:41 UTC (permalink / raw)
  To: kvm, kvm-riscv; +Cc: atishp, jamestiotio

Add a couple helpers to cleanup checking of test configuration
environment variables.

Signed-off-by: Andrew Jones <andrew.jones@linux.dev>
---
 riscv/sbi.c | 32 +++++++++++++++++++-------------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/riscv/sbi.c b/riscv/sbi.c
index d46befa1c6c1..1e7314ec8d98 100644
--- a/riscv/sbi.c
+++ b/riscv/sbi.c
@@ -74,6 +74,20 @@ static phys_addr_t get_highest_addr(void)
 	return highest_end - 1;
 }
 
+static bool env_enabled(const char *env)
+{
+	char *s = getenv(env);
+
+	return s && (*s == '1' || *s == 'y' || *s == 'Y');
+}
+
+static bool env_disabled(const char *env)
+{
+	char *s = getenv(env);
+
+	return !(s && (*s == '0' || *s == 'n' || *s == 'N'));
+}
+
 static bool env_or_skip(const char *env)
 {
 	if (!getenv(env)) {
@@ -348,7 +362,6 @@ static void check_dbcn(void)
 	bool highmem_supported = true;
 	phys_addr_t paddr;
 	struct sbiret ret;
-	const char *tmp;
 	char *buf;
 
 	report_prefix_push("dbcn");
@@ -371,13 +384,11 @@ static void check_dbcn(void)
 	dbcn_write_test(&buf[PAGE_SIZE - num_bytes / 2], num_bytes, false);
 	report_prefix_pop();
 
-	tmp = getenv("SBI_HIGHMEM_NOT_SUPPORTED");
-	if (tmp && atol(tmp) != 0)
+	if (env_enabled("SBI_HIGHMEM_NOT_SUPPORTED"))
 		highmem_supported = false;
 
 	report_prefix_push("high boundary");
-	tmp = getenv("SBI_DBCN_SKIP_HIGH_BOUNDARY");
-	if (!tmp || atol(tmp) == 0)
+	if (env_disabled("SBI_DBCN_SKIP_HIGH_BOUNDARY"))
 		dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes,
 				     HIGH_ADDR_BOUNDARY - PAGE_SIZE, PAGE_SIZE - num_bytes / 2,
 				     highmem_supported);
@@ -386,12 +397,8 @@ static void check_dbcn(void)
 	report_prefix_pop();
 
 	report_prefix_push("high page");
-	tmp = getenv("SBI_DBCN_SKIP_HIGH_PAGE");
-	if (!tmp || atol(tmp) == 0) {
-		paddr = HIGH_ADDR_BOUNDARY;
-		tmp = getenv("HIGH_PAGE");
-		if (tmp)
-			paddr = strtoull(tmp, NULL, 0);
+	if (env_disabled("SBI_DBCN_SKIP_HIGH_PAGE")) {
+		paddr = getenv("HIGH_PAGE") ? strtoull(getenv("HIGH_PAGE"), NULL, 0) : HIGH_ADDR_BOUNDARY;
 		dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes, paddr, 0, highmem_supported);
 	} else {
 		report_skip("user disabled");
@@ -400,8 +407,7 @@ static void check_dbcn(void)
 
 	/* Bytes are read from memory and written to the console */
 	report_prefix_push("invalid parameter");
-	tmp = getenv("INVALID_ADDR_AUTO");
-	if (tmp && atol(tmp) == 1) {
+	if (env_enabled("INVALID_ADDR_AUTO")) {
 		paddr = get_highest_addr() + 1;
 		do_invalid_addr = true;
 	} else if (env_or_skip("INVALID_ADDR")) {
-- 
2.47.0


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

* [kvm-unit-tests PATCH 3/3] riscv: sbi: Add SUSP tests
  2024-10-24 12:41 [kvm-unit-tests PATCH 0/3] riscv: sbi: Add SUSP tests Andrew Jones
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 1/3] riscv: Implement setjmp/longjmp Andrew Jones
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 2/3] riscv: sbi: Clean up env checking Andrew Jones
@ 2024-10-24 12:41 ` Andrew Jones
  2024-11-11 15:08 ` [kvm-unit-tests PATCH 0/3] " Andrew Jones
  3 siblings, 0 replies; 6+ messages in thread
From: Andrew Jones @ 2024-10-24 12:41 UTC (permalink / raw)
  To: kvm, kvm-riscv; +Cc: atishp, jamestiotio

Introduce tests for SBI system suspend. The basic test makes
sure it works and other tests make sure it fails as expected
with invalid entry criteria.

To test on QEMU or hardware the firmware needs to support system
suspend. For QEMU, OpenSBI can be told to enable its system
suspend test mode by creating a new DTB which has

    opensbi-config {
        compatible = "opensbi,config";
        system-suspend-test;
    };

added to the 'chosen' node. Then, run with '-dtb susp.dtb'.

Signed-off-by: Andrew Jones <andrew.jones@linux.dev>
---
 lib/riscv/asm/asm.h |   3 +
 lib/riscv/asm/sbi.h |   1 +
 riscv/sbi-asm.S     |  55 +++++++++
 riscv/sbi-tests.h   |  19 +++
 riscv/sbi.c         | 278 ++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 344 insertions(+), 12 deletions(-)

diff --git a/lib/riscv/asm/asm.h b/lib/riscv/asm/asm.h
index 763b28e6ad3c..107b5bb7e981 100644
--- a/lib/riscv/asm/asm.h
+++ b/lib/riscv/asm/asm.h
@@ -14,6 +14,9 @@
 #define REG_S	__REG_SEL(sd, sw)
 #define SZREG	__REG_SEL(8, 4)
 
+/* ASMARR() may be used with arrays of longs */
+#define ASMARR(reg, idx)	((idx) * SZREG)(reg)
+
 #define FP_SIZE 16
 
 #endif /* _ASMRISCV_ASM_H_ */
diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
index 1319439b7118..4e72f125fb43 100644
--- a/lib/riscv/asm/sbi.h
+++ b/lib/riscv/asm/sbi.h
@@ -22,6 +22,7 @@ enum sbi_ext_id {
 	SBI_EXT_HSM = 0x48534d,
 	SBI_EXT_SRST = 0x53525354,
 	SBI_EXT_DBCN = 0x4442434E,
+	SBI_EXT_SUSP = 0x53555350,
 };
 
 enum sbi_ext_base_fid {
diff --git a/riscv/sbi-asm.S b/riscv/sbi-asm.S
index 9606a13e5f3a..e871ea506f07 100644
--- a/riscv/sbi-asm.S
+++ b/riscv/sbi-asm.S
@@ -5,6 +5,7 @@
  * Copyright (C) 2024, James Raphael Tiovalen <jamestiotio@gmail.com>
  */
 #define __ASSEMBLY__
+#include <asm/asm.h>
 #include <asm/csr.h>
 
 #include "sbi-tests.h"
@@ -69,3 +70,57 @@ sbi_hsm_check_hart_start:
 sbi_hsm_check_non_retentive_suspend:
 	la	HSM_RESULTS_ARRAY, sbi_hsm_non_retentive_hart_suspend_checks
 	j	sbi_hsm_check
+
+.balign 4
+restore_csrs:
+	REG_L	a1, ASMARR(a0, SBI_CSR_SSTATUS_IDX)
+	csrw	CSR_SSTATUS, a1
+	REG_L	a1, ASMARR(a0, SBI_CSR_SIE_IDX)
+	csrw	CSR_SIE, a1
+	REG_L	a1, ASMARR(a0, SBI_CSR_STVEC_IDX)
+	csrw	CSR_STVEC, a1
+	REG_L	a1, ASMARR(a0, SBI_CSR_SSCRATCH_IDX)
+	csrw	CSR_SSCRATCH, a1
+	REG_L	a1, ASMARR(a0, SBI_CSR_SATP_IDX)
+	sfence.vma
+	csrw	CSR_SATP, a1
+	ret
+
+/*
+ * sbi_susp_resume
+ *
+ * State is as specified by "SUSP System Resume Register State" of the SBI spec
+ *   a0 is the hartid
+ *   a1 is the opaque parameter (here, it's the context array defined in check_susp())
+ * Doesn't return.
+ */
+#define SUSP_CTX		s1
+#define SUSP_RESULTS_MAP	s2
+.balign 4
+.global sbi_susp_resume
+sbi_susp_resume:
+	li	SUSP_RESULTS_MAP, 0
+	mv	SUSP_CTX, a1
+	REG_L	t0, ASMARR(SUSP_CTX, SBI_SUSP_MAGIC_IDX)
+	li	t1, SBI_SUSP_MAGIC
+	beq	t0, t1, 2f
+1:	pause
+	j	1b
+2:	csrr	t0, CSR_SATP
+	bnez	t0, 3f
+	ori	SUSP_RESULTS_MAP, SUSP_RESULTS_MAP, SBI_SUSP_TEST_SATP
+3:	csrr	t0, CSR_SSTATUS
+	andi	t0, t0, SR_SIE
+	bnez	t0, 4f
+	ori	SUSP_RESULTS_MAP, SUSP_RESULTS_MAP, SBI_SUSP_TEST_SIE
+4:	REG_L	t0, ASMARR(SUSP_CTX, SBI_SUSP_HARTID_IDX)
+	bne	t0, a0, 5f
+	ori	SUSP_RESULTS_MAP, SUSP_RESULTS_MAP, SBI_SUSP_TEST_HARTID
+5:	REG_S	SUSP_RESULTS_MAP, ASMARR(SUSP_CTX, SBI_SUSP_RESULTS_IDX)
+	REG_L	a0, ASMARR(SUSP_CTX, SBI_SUSP_CSRS_IDX)
+	call	restore_csrs
+	la	a0, sbi_susp_jmp
+	REG_L	a1, ASMARR(SUSP_CTX, SBI_SUSP_TESTNUM_IDX)
+	call	longjmp
+6:	pause	/* unreachable */
+	j	6b
diff --git a/riscv/sbi-tests.h b/riscv/sbi-tests.h
index f5cc8635d2aa..d0a7561a47b3 100644
--- a/riscv/sbi-tests.h
+++ b/riscv/sbi-tests.h
@@ -2,9 +2,28 @@
 #ifndef _RISCV_SBI_TESTS_H_
 #define _RISCV_SBI_TESTS_H_
 
+#define SBI_CSR_SSTATUS_IDX	0
+#define SBI_CSR_SIE_IDX		1
+#define SBI_CSR_STVEC_IDX	2
+#define SBI_CSR_SSCRATCH_IDX	3
+#define SBI_CSR_SATP_IDX	4
+
 #define SBI_HSM_TEST_DONE	(1 << 0)
 #define SBI_HSM_TEST_HARTID_A1	(1 << 1)
 #define SBI_HSM_TEST_SATP	(1 << 2)
 #define SBI_HSM_TEST_SIE	(1 << 3)
 
+#define SBI_SUSP_TEST_SATP	(1 << 0)
+#define SBI_SUSP_TEST_SIE	(1 << 1)
+#define SBI_SUSP_TEST_HARTID	(1 << 2)
+#define SBI_SUSP_TEST_MASK	7
+
+#define SBI_SUSP_MAGIC		0x505b
+
+#define SBI_SUSP_MAGIC_IDX	0
+#define SBI_SUSP_CSRS_IDX	1
+#define SBI_SUSP_HARTID_IDX	2
+#define SBI_SUSP_TESTNUM_IDX	3
+#define SBI_SUSP_RESULTS_IDX	4
+
 #endif /* _RISCV_SBI_TESTS_H_ */
diff --git a/riscv/sbi.c b/riscv/sbi.c
index 1e7314ec8d98..44c76692dad4 100644
--- a/riscv/sbi.c
+++ b/riscv/sbi.c
@@ -6,11 +6,14 @@
  */
 #include <libcflat.h>
 #include <alloc_page.h>
+#include <limits.h>
+#include <memregions.h>
+#include <on-cpus.h>
+#include <setjmp.h>
 #include <stdlib.h>
 #include <string.h>
-#include <limits.h>
 #include <vmalloc.h>
-#include <memregions.h>
+
 #include <asm/barrier.h>
 #include <asm/csr.h>
 #include <asm/delay.h>
@@ -22,6 +25,8 @@
 #include <asm/smp.h>
 #include <asm/timer.h>
 
+#include "sbi-tests.h"
+
 #define	HIGH_ADDR_BOUNDARY	((phys_addr_t)1 << 32)
 
 static void help(void)
@@ -47,6 +52,22 @@ static struct sbiret sbi_dbcn_write_byte(uint8_t byte)
 	return sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, byte, 0, 0, 0, 0, 0);
 }
 
+static struct sbiret sbi_system_suspend(uint32_t sleep_type, unsigned long resume_addr, unsigned long opaque)
+{
+	return sbi_ecall(SBI_EXT_SUSP, 0, sleep_type, resume_addr, opaque, 0, 0, 0);
+}
+
+static void start_cpu(void *data)
+{
+	/* nothing to do */
+}
+
+static void stop_cpu(void *data)
+{
+	struct sbiret ret = sbi_hart_stop();
+	assert_msg(0, "cpu%d failed to stop with sbiret.error %ld", smp_processor_id(), ret.error);
+}
+
 static void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo)
 {
 	*lo = (unsigned long)paddr;
@@ -98,6 +119,22 @@ static bool env_or_skip(const char *env)
 	return true;
 }
 
+static bool get_invalid_addr(phys_addr_t *paddr, bool allow_default)
+{
+	if (env_enabled("INVALID_ADDR_AUTO")) {
+		*paddr = get_highest_addr() + 1;
+		return true;
+	} else if (allow_default && !getenv("INVALID_ADDR")) {
+		*paddr = -1ul;
+		return true;
+	} else if (env_or_skip("INVALID_ADDR")) {
+		*paddr = strtoull(getenv("INVALID_ADDR"), NULL, 0);
+		return true;
+	}
+
+	return false;
+}
+
 static void gen_report(struct sbiret *ret,
 		       long expected_error, long expected_value)
 {
@@ -358,7 +395,6 @@ static void check_dbcn(void)
 {
 	unsigned long num_bytes = strlen(DBCN_WRITE_TEST_STRING);
 	unsigned long base_addr_lo, base_addr_hi;
-	bool do_invalid_addr = false;
 	bool highmem_supported = true;
 	phys_addr_t paddr;
 	struct sbiret ret;
@@ -407,15 +443,7 @@ static void check_dbcn(void)
 
 	/* Bytes are read from memory and written to the console */
 	report_prefix_push("invalid parameter");
-	if (env_enabled("INVALID_ADDR_AUTO")) {
-		paddr = get_highest_addr() + 1;
-		do_invalid_addr = true;
-	} else if (env_or_skip("INVALID_ADDR")) {
-		paddr = strtoull(getenv("INVALID_ADDR"), NULL, 0);
-		do_invalid_addr = true;
-	}
-
-	if (do_invalid_addr) {
+	if (get_invalid_addr(&paddr, false)) {
 		split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
 		ret = sbi_dbcn_write(1, base_addr_lo, base_addr_hi);
 		report(ret.error == SBI_ERR_INVALID_PARAM, "address (error=%ld)", ret.error);
@@ -438,6 +466,231 @@ static void check_dbcn(void)
 	report_prefix_popn(2);
 }
 
+void sbi_susp_resume(unsigned long hartid, unsigned long opaque);
+jmp_buf sbi_susp_jmp;
+
+struct susp_params {
+	unsigned long sleep_type;
+	unsigned long resume_addr;
+	unsigned long opaque;
+	bool returns;
+	struct sbiret ret;
+};
+
+static bool susp_basic_prep(unsigned long ctx[], struct susp_params *params)
+{
+	int cpu, me = smp_processor_id();
+	struct sbiret ret;
+	cpumask_t mask;
+
+	memset(params, 0, sizeof(*params));
+	params->sleep_type = 0; /* suspend-to-ram */
+	params->resume_addr = virt_to_phys(sbi_susp_resume);
+	params->opaque = virt_to_phys(ctx);
+	params->returns = false;
+
+	cpumask_copy(&mask, &cpu_present_mask);
+	cpumask_clear_cpu(me, &mask);
+	on_cpumask_async(&mask, stop_cpu, NULL);
+
+	/* Wait up to 1s for all harts to stop */
+	for (int i = 0; i < 100; i++) {
+		int count = 1;
+
+		udelay(10000);
+
+		for_each_present_cpu(cpu) {
+			if (cpu == me)
+				continue;
+			ret = sbi_hart_get_status(cpus[cpu].hartid);
+			if (!ret.error && ret.value == SBI_EXT_HSM_STOPPED)
+				++count;
+		}
+		if (count == cpumask_weight(&cpu_present_mask))
+			break;
+	}
+
+	for_each_present_cpu(cpu) {
+		ret = sbi_hart_get_status(cpus[cpu].hartid);
+		if (cpu == me) {
+			assert_msg(!ret.error && ret.value == SBI_EXT_HSM_STARTED,
+				   "cpu%d is not started", cpu);
+		} else {
+			assert_msg(!ret.error && ret.value == SBI_EXT_HSM_STOPPED,
+				   "cpu%d is not stopped", cpu);
+		}
+	}
+
+	return true;
+}
+
+static void susp_basic_check(unsigned long ctx[], struct susp_params *params)
+{
+	if (ctx[SBI_SUSP_RESULTS_IDX] == SBI_SUSP_TEST_MASK) {
+		report_pass("suspend and resume");
+	} else {
+		if (!(ctx[SBI_SUSP_RESULTS_IDX] & SBI_SUSP_TEST_SATP))
+			report_fail("SATP set to zero on resume");
+		if (!(ctx[SBI_SUSP_RESULTS_IDX] & SBI_SUSP_TEST_SIE))
+			report_fail("sstatus.SIE clear on resume");
+		if (!(ctx[SBI_SUSP_RESULTS_IDX] & SBI_SUSP_TEST_HARTID))
+			report_fail("a0 is hartid on resume");
+	}
+}
+
+static bool susp_type_prep(unsigned long ctx[], struct susp_params *params)
+{
+	bool r;
+
+	r = susp_basic_prep(ctx, params);
+	assert(r);
+	params->sleep_type = 1;
+	params->returns = true;
+	params->ret.error = SBI_ERR_INVALID_PARAM;
+
+	return true;
+}
+
+static bool susp_badaddr_prep(unsigned long ctx[], struct susp_params *params)
+{
+	phys_addr_t badaddr;
+	bool r;
+
+	if (!get_invalid_addr(&badaddr, false))
+		return false;
+
+	r = susp_basic_prep(ctx, params);
+	assert(r);
+	params->resume_addr = badaddr;
+	params->returns = true;
+	params->ret.error = SBI_ERR_INVALID_ADDRESS;
+
+	return true;
+}
+
+static bool susp_one_prep(unsigned long ctx[], struct susp_params *params)
+{
+	int started = 0, cpu, me = smp_processor_id();
+	struct sbiret ret;
+	bool r;
+
+	if (cpumask_weight(&cpu_present_mask) < 2) {
+		report_skip("At least 2 cpus required");
+		return false;
+	}
+
+	r = susp_basic_prep(ctx, params);
+	assert(r);
+	params->returns = true;
+	params->ret.error = SBI_ERR_DENIED;
+
+	for_each_present_cpu(cpu) {
+		if (cpu == me)
+			continue;
+		break;
+	}
+
+	on_cpu(cpu, start_cpu, NULL);
+
+	for_each_present_cpu(cpu) {
+		ret = sbi_hart_get_status(cpus[cpu].hartid);
+		assert_msg(!ret.error, "HSM get status failed for cpu%d", cpu);
+		if (ret.value == SBI_EXT_HSM_STARTED)
+			started++;
+	}
+
+	assert(started == 2);
+
+	return true;
+}
+
+static void check_susp(void)
+{
+	unsigned long csrs[] = {
+		[SBI_CSR_SSTATUS_IDX] = csr_read(CSR_SSTATUS),
+		[SBI_CSR_SIE_IDX] = csr_read(CSR_SIE),
+		[SBI_CSR_STVEC_IDX] = csr_read(CSR_STVEC),
+		[SBI_CSR_SSCRATCH_IDX] = csr_read(CSR_SSCRATCH),
+		[SBI_CSR_SATP_IDX] = csr_read(CSR_SATP),
+	};
+	unsigned long ctx[] = {
+		[SBI_SUSP_MAGIC_IDX] = SBI_SUSP_MAGIC,
+		[SBI_SUSP_CSRS_IDX] = (unsigned long)csrs,
+		[SBI_SUSP_HARTID_IDX] = current_thread_info()->hartid,
+		[SBI_SUSP_TESTNUM_IDX] = 0,
+		[SBI_SUSP_RESULTS_IDX] = 0,
+	};
+	enum {
+#define SUSP_FIRST_TESTNUM 1
+		SUSP_BASIC = SUSP_FIRST_TESTNUM,
+		SUSP_TYPE,
+		SUSP_BAD_ADDR,
+		SUSP_ONE_ONLINE,
+		NR_SUSP_TESTS,
+	};
+	struct susp_test {
+		const char *name;
+		bool (*prep)(unsigned long ctx[], struct susp_params *params);
+		void (*check)(unsigned long ctx[], struct susp_params *params);
+	} susp_tests[] = {
+		[SUSP_BASIC]		= { "basic",		susp_basic_prep,	susp_basic_check,	},
+		[SUSP_TYPE]		= { "sleep_type",	susp_type_prep,					},
+		[SUSP_BAD_ADDR]		= { "bad addr",		susp_badaddr_prep,				},
+		[SUSP_ONE_ONLINE]	= { "one cpu online",	susp_one_prep,					},
+	};
+	struct susp_params params;
+	struct sbiret ret;
+	int testnum, i;
+
+	local_irq_disable();
+	timer_stop();
+
+	report_prefix_push("susp");
+
+	ret = sbi_ecall(SBI_EXT_SUSP, 1, 0, 0, 0, 0, 0, 0);
+	report(ret.error == SBI_ERR_NOT_SUPPORTED, "funcid != 0 not supported");
+
+	for (i = SUSP_FIRST_TESTNUM; i < NR_SUSP_TESTS; i++) {
+		report_prefix_push(susp_tests[i].name);
+
+		ctx[SBI_SUSP_TESTNUM_IDX] = i;
+		ctx[SBI_SUSP_RESULTS_IDX] = 0;
+
+		assert(susp_tests[i].prep);
+		if (!susp_tests[i].prep(ctx, &params)) {
+			report_prefix_pop();
+			continue;
+		}
+
+		if ((testnum = setjmp(sbi_susp_jmp)) == 0) {
+			ret = sbi_system_suspend(params.sleep_type, params.resume_addr, params.opaque);
+
+			if (!params.returns && ret.error == SBI_ERR_NOT_SUPPORTED) {
+				report_skip("SUSP not supported?");
+				report_prefix_popn(2);
+				return;
+			} else if (!params.returns) {
+				report_fail("unexpected return with error: %ld, value: %ld", ret.error, ret.value);
+			} else {
+				report(ret.error == params.ret.error, "expected sbi.error");
+				if (ret.error != params.ret.error)
+					report_info("expected error %ld, received %ld", params.ret.error, ret.error);
+			}
+
+			report_prefix_pop();
+			continue;
+		}
+		assert(testnum == i);
+
+		if (susp_tests[i].check)
+			susp_tests[i].check(ctx, &params);
+
+		report_prefix_pop();
+	}
+
+	report_prefix_pop();
+}
+
 int main(int argc, char **argv)
 {
 	if (argc > 1 && !strcmp(argv[1], "-h")) {
@@ -449,6 +702,7 @@ int main(int argc, char **argv)
 	check_base();
 	check_time();
 	check_dbcn();
+	check_susp();
 
 	return report_summary();
 }
-- 
2.47.0


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

* Re: [kvm-unit-tests PATCH 2/3] riscv: sbi: Clean up env checking
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 2/3] riscv: sbi: Clean up env checking Andrew Jones
@ 2024-11-06 12:23   ` Andrew Jones
  0 siblings, 0 replies; 6+ messages in thread
From: Andrew Jones @ 2024-11-06 12:23 UTC (permalink / raw)
  To: kvm, kvm-riscv; +Cc: atishp, jamestiotio

On Thu, Oct 24, 2024 at 02:41:04PM +0200, Andrew Jones wrote:
> Add a couple helpers to cleanup checking of test configuration
> environment variables.
> 
> Signed-off-by: Andrew Jones <andrew.jones@linux.dev>
> ---
>  riscv/sbi.c | 32 +++++++++++++++++++-------------
>  1 file changed, 19 insertions(+), 13 deletions(-)
> 
> diff --git a/riscv/sbi.c b/riscv/sbi.c
> index d46befa1c6c1..1e7314ec8d98 100644
> --- a/riscv/sbi.c
> +++ b/riscv/sbi.c
> @@ -74,6 +74,20 @@ static phys_addr_t get_highest_addr(void)
>  	return highest_end - 1;
>  }
>  
> +static bool env_enabled(const char *env)
> +{
> +	char *s = getenv(env);
> +
> +	return s && (*s == '1' || *s == 'y' || *s == 'Y');
> +}
> +
> +static bool env_disabled(const char *env)
> +{
> +	char *s = getenv(env);
> +
> +	return !(s && (*s == '0' || *s == 'n' || *s == 'N'));
> +}

I've dropped env_disabled() since what I really want is !env_enabled()

> +
>  static bool env_or_skip(const char *env)
>  {
>  	if (!getenv(env)) {
> @@ -348,7 +362,6 @@ static void check_dbcn(void)
>  	bool highmem_supported = true;
>  	phys_addr_t paddr;
>  	struct sbiret ret;
> -	const char *tmp;
>  	char *buf;
>  
>  	report_prefix_push("dbcn");
> @@ -371,13 +384,11 @@ static void check_dbcn(void)
>  	dbcn_write_test(&buf[PAGE_SIZE - num_bytes / 2], num_bytes, false);
>  	report_prefix_pop();
>  
> -	tmp = getenv("SBI_HIGHMEM_NOT_SUPPORTED");
> -	if (tmp && atol(tmp) != 0)
> +	if (env_enabled("SBI_HIGHMEM_NOT_SUPPORTED"))
>  		highmem_supported = false;
>  
>  	report_prefix_push("high boundary");
> -	tmp = getenv("SBI_DBCN_SKIP_HIGH_BOUNDARY");
> -	if (!tmp || atol(tmp) == 0)
> +	if (env_disabled("SBI_DBCN_SKIP_HIGH_BOUNDARY"))
>  		dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes,
>  				     HIGH_ADDR_BOUNDARY - PAGE_SIZE, PAGE_SIZE - num_bytes / 2,
>  				     highmem_supported);
> @@ -386,12 +397,8 @@ static void check_dbcn(void)
>  	report_prefix_pop();
>  
>  	report_prefix_push("high page");
> -	tmp = getenv("SBI_DBCN_SKIP_HIGH_PAGE");
> -	if (!tmp || atol(tmp) == 0) {
> -		paddr = HIGH_ADDR_BOUNDARY;
> -		tmp = getenv("HIGH_PAGE");
> -		if (tmp)
> -			paddr = strtoull(tmp, NULL, 0);
> +	if (env_disabled("SBI_DBCN_SKIP_HIGH_PAGE")) {
> +		paddr = getenv("HIGH_PAGE") ? strtoull(getenv("HIGH_PAGE"), NULL, 0) : HIGH_ADDR_BOUNDARY;
>  		dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes, paddr, 0, highmem_supported);
>  	} else {
>  		report_skip("user disabled");
> @@ -400,8 +407,7 @@ static void check_dbcn(void)
>  
>  	/* Bytes are read from memory and written to the console */
>  	report_prefix_push("invalid parameter");
> -	tmp = getenv("INVALID_ADDR_AUTO");
> -	if (tmp && atol(tmp) == 1) {
> +	if (env_enabled("INVALID_ADDR_AUTO")) {
>  		paddr = get_highest_addr() + 1;
>  		do_invalid_addr = true;
>  	} else if (env_or_skip("INVALID_ADDR")) {
> -- 
> 2.47.0
> 
> 
> -- 
> kvm-riscv mailing list
> kvm-riscv@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kvm-riscv

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

* Re: [kvm-unit-tests PATCH 0/3] riscv: sbi: Add SUSP tests
  2024-10-24 12:41 [kvm-unit-tests PATCH 0/3] riscv: sbi: Add SUSP tests Andrew Jones
                   ` (2 preceding siblings ...)
  2024-10-24 12:41 ` [kvm-unit-tests PATCH 3/3] riscv: sbi: Add SUSP tests Andrew Jones
@ 2024-11-11 15:08 ` Andrew Jones
  3 siblings, 0 replies; 6+ messages in thread
From: Andrew Jones @ 2024-11-11 15:08 UTC (permalink / raw)
  To: kvm, kvm-riscv; +Cc: atishp, jamestiotio

On Thu, Oct 24, 2024 at 02:41:02PM +0200, Andrew Jones wrote:
> Add tests for the SBI SUSP extension.
> 
> Based-on: https://gitlab.com/jones-drew/kvm-unit-tests/-/commits/riscv/sbi
> 
> Andrew Jones (3):
>   riscv: Implement setjmp/longjmp
>   riscv: sbi: Clean up env checking
>   riscv: sbi: Add SUSP tests
> 
>  lib/riscv/asm/asm.h |   3 +
>  lib/riscv/asm/sbi.h |   1 +
>  lib/riscv/setjmp.S  |  50 +++++++
>  lib/setjmp.h        |   4 +
>  riscv/Makefile      |   1 +
>  riscv/sbi-asm.S     |  55 ++++++++
>  riscv/sbi-tests.h   |  19 +++
>  riscv/sbi.c         | 308 ++++++++++++++++++++++++++++++++++++++++----
>  8 files changed, 417 insertions(+), 24 deletions(-)
>  create mode 100644 lib/riscv/setjmp.S
> 
> -- 
> 2.47.0
>

Merged through riscv/sbi.

Thanks,
drew

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

end of thread, other threads:[~2024-11-11 15:08 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-24 12:41 [kvm-unit-tests PATCH 0/3] riscv: sbi: Add SUSP tests Andrew Jones
2024-10-24 12:41 ` [kvm-unit-tests PATCH 1/3] riscv: Implement setjmp/longjmp Andrew Jones
2024-10-24 12:41 ` [kvm-unit-tests PATCH 2/3] riscv: sbi: Clean up env checking Andrew Jones
2024-11-06 12:23   ` Andrew Jones
2024-10-24 12:41 ` [kvm-unit-tests PATCH 3/3] riscv: sbi: Add SUSP tests Andrew Jones
2024-11-11 15:08 ` [kvm-unit-tests PATCH 0/3] " Andrew Jones

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