Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [kvm-unit-tests PATCH v2 0/3] riscv: add SBI SSE extension tests
@ 2024-11-22 14:04 Clément Léger
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 1/3] riscv: lib: Add SBI SSE extension definitions Clément Léger
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Clément Léger @ 2024-11-22 14:04 UTC (permalink / raw)
  To: kvm, kvm-riscv
  Cc: Clément Léger, Andrew Jones, Anup Patel, Atish Patra

This series adds an individual test for SBI SSE extension as well as
needed infrastructure for SSE support.

---

V2:
 - Rebased on origin/master and integrate it into sbi.c tests

Clément Léger (3):
  riscv: lib: Add SBI SSE extension definitions
  riscv: lib: Add SSE assembly entry handling
  riscv: sbi: Add SSE extension tests

 riscv/Makefile          |   2 +
 lib/riscv/asm/csr.h     |   2 +
 lib/riscv/asm/sbi.h     |  76 ++++
 lib/riscv/asm/sse.h     |  16 +
 lib/riscv/sse-entry.S   | 100 ++++
 lib/riscv/asm-offsets.c |   9 +
 riscv/sbi-tests.h       |   4 +
 riscv/sbi-sse.c         | 981 ++++++++++++++++++++++++++++++++++++++++
 riscv/sbi.c             |   1 +
 riscv/unittests.cfg     |   4 +
 10 files changed, 1195 insertions(+)
 create mode 100644 lib/riscv/asm/sse.h
 create mode 100644 lib/riscv/sse-entry.S
 create mode 100644 riscv/sbi-sse.c

-- 
2.45.2


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

* [kvm-unit-tests PATCH v2 1/3] riscv: lib: Add SBI SSE extension definitions
  2024-11-22 14:04 [kvm-unit-tests PATCH v2 0/3] riscv: add SBI SSE extension tests Clément Léger
@ 2024-11-22 14:04 ` Clément Léger
  2024-11-22 16:05   ` Andrew Jones
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling Clément Léger
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests Clément Léger
  2 siblings, 1 reply; 12+ messages in thread
From: Clément Léger @ 2024-11-22 14:04 UTC (permalink / raw)
  To: kvm, kvm-riscv
  Cc: Clément Léger, Andrew Jones, Anup Patel, Atish Patra

Add SBI SSE extension definitions in sbi.h

Signed-off-by: Clément Léger <cleger@rivosinc.com>
---
 lib/riscv/asm/sbi.h | 76 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)

diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
index 98a9b097..96100bc2 100644
--- a/lib/riscv/asm/sbi.h
+++ b/lib/riscv/asm/sbi.h
@@ -11,6 +11,9 @@
 #define SBI_ERR_ALREADY_AVAILABLE	-6
 #define SBI_ERR_ALREADY_STARTED		-7
 #define SBI_ERR_ALREADY_STOPPED		-8
+#define SBI_ERR_NO_SHMEM		-9
+#define SBI_ERR_INVALID_STATE		-10
+#define SBI_ERR_BAD_RANGE		-11
 
 #ifndef __ASSEMBLY__
 #include <cpumask.h>
@@ -23,6 +26,7 @@ enum sbi_ext_id {
 	SBI_EXT_SRST = 0x53525354,
 	SBI_EXT_DBCN = 0x4442434E,
 	SBI_EXT_SUSP = 0x53555350,
+	SBI_EXT_SSE = 0x535345,
 };
 
 enum sbi_ext_base_fid {
@@ -71,6 +75,78 @@ enum sbi_ext_dbcn_fid {
 	SBI_EXT_DBCN_CONSOLE_WRITE_BYTE,
 };
 
+/* SBI Function IDs for SSE extension */
+#define SBI_EXT_SSE_READ_ATTR		0x00000000
+#define SBI_EXT_SSE_WRITE_ATTR		0x00000001
+#define SBI_EXT_SSE_REGISTER		0x00000002
+#define SBI_EXT_SSE_UNREGISTER		0x00000003
+#define SBI_EXT_SSE_ENABLE		0x00000004
+#define SBI_EXT_SSE_DISABLE		0x00000005
+#define SBI_EXT_SSE_COMPLETE		0x00000006
+#define SBI_EXT_SSE_INJECT		0x00000007
+
+/* SBI SSE Event Attributes. */
+enum sbi_sse_attr_id {
+	SBI_SSE_ATTR_STATUS		= 0x00000000,
+	SBI_SSE_ATTR_PRIO		= 0x00000001,
+	SBI_SSE_ATTR_CONFIG		= 0x00000002,
+	SBI_SSE_ATTR_PREFERRED_HART	= 0x00000003,
+	SBI_SSE_ATTR_ENTRY_PC		= 0x00000004,
+	SBI_SSE_ATTR_ENTRY_ARG		= 0x00000005,
+	SBI_SSE_ATTR_INTERRUPTED_SEPC	= 0x00000006,
+	SBI_SSE_ATTR_INTERRUPTED_FLAGS	= 0x00000007,
+	SBI_SSE_ATTR_INTERRUPTED_A6	= 0x00000008,
+	SBI_SSE_ATTR_INTERRUPTED_A7	= 0x00000009,
+};
+
+#define SBI_SSE_ATTR_STATUS_STATE_OFFSET	0
+#define SBI_SSE_ATTR_STATUS_STATE_MASK		0x3
+#define SBI_SSE_ATTR_STATUS_PENDING_OFFSET	2
+#define SBI_SSE_ATTR_STATUS_INJECT_OFFSET	3
+
+#define SBI_SSE_ATTR_CONFIG_ONESHOT	(1 << 0)
+
+#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPP	BIT(0)
+#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPIE	BIT(1)
+#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV	BIT(2)
+#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP	BIT(3)
+
+enum sbi_sse_state {
+	SBI_SSE_STATE_UNUSED     = 0,
+	SBI_SSE_STATE_REGISTERED = 1,
+	SBI_SSE_STATE_ENABLED    = 2,
+	SBI_SSE_STATE_RUNNING    = 3,
+};
+
+/* SBI SSE Event IDs. */
+#define SBI_SSE_EVENT_LOCAL_RAS			0x00000000
+#define	SBI_SSE_EVENT_LOCAL_PLAT_0_START	0x00004000
+#define SBI_SSE_EVENT_LOCAL_PLAT_0_END		0x00007fff
+#define SBI_SSE_EVENT_GLOBAL_RAS		0x00008000
+#define	SBI_SSE_EVENT_GLOBAL_PLAT_0_START	0x00004000
+#define SBI_SSE_EVENT_GLOBAL_PLAT_0_END		0x00007fff
+
+#define SBI_SSE_EVENT_LOCAL_PMU			0x00010000
+#define	SBI_SSE_EVENT_LOCAL_PLAT_1_START	0x00014000
+#define SBI_SSE_EVENT_LOCAL_PLAT_1_END		0x00017fff
+#define	SBI_SSE_EVENT_GLOBAL_PLAT_1_START	0x0001c000
+#define SBI_SSE_EVENT_GLOBAL_PLAT_1_END		0x0001ffff
+
+#define	SBI_SSE_EVENT_LOCAL_PLAT_2_START	0x00024000
+#define SBI_SSE_EVENT_LOCAL_PLAT_2_END		0x00027fff
+#define	SBI_SSE_EVENT_GLOBAL_PLAT_2_START	0x0002c000
+#define SBI_SSE_EVENT_GLOBAL_PLAT_2_END		0x0002ffff
+
+#define SBI_SSE_EVENT_LOCAL_SOFTWARE		0xffff0000
+#define	SBI_SSE_EVENT_LOCAL_PLAT_3_START	0xffff4000
+#define SBI_SSE_EVENT_LOCAL_PLAT_3_END		0xffff7fff
+#define SBI_SSE_EVENT_GLOBAL_SOFTWARE		0xffff8000
+#define	SBI_SSE_EVENT_GLOBAL_PLAT_3_START	0xffffc000
+#define SBI_SSE_EVENT_GLOBAL_PLAT_3_END		0xffffffff
+
+#define SBI_SSE_EVENT_GLOBAL_BIT		(1 << 15)
+#define SBI_SSE_EVENT_PLATFORM_BIT		(1 << 14)
+
 struct sbiret {
 	long error;
 	long value;
-- 
2.45.2


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

* [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling
  2024-11-22 14:04 [kvm-unit-tests PATCH v2 0/3] riscv: add SBI SSE extension tests Clément Léger
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 1/3] riscv: lib: Add SBI SSE extension definitions Clément Léger
@ 2024-11-22 14:04 ` Clément Léger
  2024-11-22 16:20   ` Andrew Jones
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests Clément Léger
  2 siblings, 1 reply; 12+ messages in thread
From: Clément Léger @ 2024-11-22 14:04 UTC (permalink / raw)
  To: kvm, kvm-riscv
  Cc: Clément Léger, Andrew Jones, Anup Patel, Atish Patra

Add a SSE entry assembly code to handle SSE events. Events should be
registered with a struct sse_handler_arg containing a correct stack and
handler function.

Signed-off-by: Clément Léger <cleger@rivosinc.com>
---
 riscv/Makefile          |   1 +
 lib/riscv/asm/sse.h     |  16 +++++++
 lib/riscv/sse-entry.S   | 100 ++++++++++++++++++++++++++++++++++++++++
 lib/riscv/asm-offsets.c |   9 ++++
 4 files changed, 126 insertions(+)
 create mode 100644 lib/riscv/asm/sse.h
 create mode 100644 lib/riscv/sse-entry.S

diff --git a/riscv/Makefile b/riscv/Makefile
index 28b04156..e50621ad 100644
--- a/riscv/Makefile
+++ b/riscv/Makefile
@@ -39,6 +39,7 @@ cflatobjs += lib/riscv/sbi.o
 cflatobjs += lib/riscv/setjmp.o
 cflatobjs += lib/riscv/setup.o
 cflatobjs += lib/riscv/smp.o
+cflatobjs += lib/riscv/sse-entry.o
 cflatobjs += lib/riscv/stack.o
 cflatobjs += lib/riscv/timer.o
 ifeq ($(ARCH),riscv32)
diff --git a/lib/riscv/asm/sse.h b/lib/riscv/asm/sse.h
new file mode 100644
index 00000000..557f6680
--- /dev/null
+++ b/lib/riscv/asm/sse.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _ASMRISCV_SSE_H_
+#define _ASMRISCV_SSE_H_
+
+typedef void (*sse_handler_fn)(void *data, struct pt_regs *regs, unsigned int hartid);
+
+struct sse_handler_arg {
+	unsigned long reg_tmp;
+	sse_handler_fn handler;
+	void *handler_data;
+	void *stack;
+};
+
+extern void sse_entry(void);
+
+#endif /* _ASMRISCV_SSE_H_ */
diff --git a/lib/riscv/sse-entry.S b/lib/riscv/sse-entry.S
new file mode 100644
index 00000000..bedc47e9
--- /dev/null
+++ b/lib/riscv/sse-entry.S
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SBI SSE entry code
+ *
+ * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
+ */
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/csr.h>
+
+.global sse_entry
+sse_entry:
+	/* Save stack temporarily */
+	REG_S sp, SSE_REG_TMP(a6)
+	/* Set entry stack */
+	REG_L sp, SSE_HANDLER_STACK(a6)
+
+	addi sp, sp, -(PT_SIZE)
+	REG_S ra, PT_RA(sp)
+	REG_S s0, PT_S0(sp)
+	REG_S s1, PT_S1(sp)
+	REG_S s2, PT_S2(sp)
+	REG_S s3, PT_S3(sp)
+	REG_S s4, PT_S4(sp)
+	REG_S s5, PT_S5(sp)
+	REG_S s6, PT_S6(sp)
+	REG_S s7, PT_S7(sp)
+	REG_S s8, PT_S8(sp)
+	REG_S s9, PT_S9(sp)
+	REG_S s10, PT_S10(sp)
+	REG_S s11, PT_S11(sp)
+	REG_S tp, PT_TP(sp)
+	REG_S t0, PT_T0(sp)
+	REG_S t1, PT_T1(sp)
+	REG_S t2, PT_T2(sp)
+	REG_S t3, PT_T3(sp)
+	REG_S t4, PT_T4(sp)
+	REG_S t5, PT_T5(sp)
+	REG_S t6, PT_T6(sp)
+	REG_S gp, PT_GP(sp)
+	REG_S a0, PT_A0(sp)
+	REG_S a1, PT_A1(sp)
+	REG_S a2, PT_A2(sp)
+	REG_S a3, PT_A3(sp)
+	REG_S a4, PT_A4(sp)
+	REG_S a5, PT_A5(sp)
+	csrr a1, CSR_SEPC
+	REG_S a1, PT_EPC(sp)
+	csrr a2, CSR_SSTATUS
+	REG_S a2, PT_STATUS(sp)
+
+	REG_L a0, SSE_REG_TMP(a6)
+	REG_S a0, PT_SP(sp)
+
+	REG_L t0, SSE_HANDLER(a6)
+	REG_L a0, SSE_HANDLER_DATA(a6)
+	move a1, sp
+	move a2, a7
+	jalr t0
+
+
+	REG_L a1, PT_EPC(sp)
+	REG_L a2, PT_STATUS(sp)
+	csrw CSR_SEPC, a1
+	csrw CSR_SSTATUS, a2
+
+	REG_L ra, PT_RA(sp)
+	REG_L s0, PT_S0(sp)
+	REG_L s1, PT_S1(sp)
+	REG_L s2, PT_S2(sp)
+	REG_L s3, PT_S3(sp)
+	REG_L s4, PT_S4(sp)
+	REG_L s5, PT_S5(sp)
+	REG_L s6, PT_S6(sp)
+	REG_L s7, PT_S7(sp)
+	REG_L s8, PT_S8(sp)
+	REG_L s9, PT_S9(sp)
+	REG_L s10, PT_S10(sp)
+	REG_L s11, PT_S11(sp)
+	REG_L tp, PT_TP(sp)
+	REG_L t0, PT_T0(sp)
+	REG_L t1, PT_T1(sp)
+	REG_L t2, PT_T2(sp)
+	REG_L t3, PT_T3(sp)
+	REG_L t4, PT_T4(sp)
+	REG_L t5, PT_T5(sp)
+	REG_L t6, PT_T6(sp)
+	REG_L gp, PT_GP(sp)
+	REG_L a0, PT_A0(sp)
+	REG_L a1, PT_A1(sp)
+	REG_L a2, PT_A2(sp)
+	REG_L a3, PT_A3(sp)
+	REG_L a4, PT_A4(sp)
+	REG_L a5, PT_A5(sp)
+
+	REG_L sp, PT_SP(sp)
+
+	li a7, ASM_SBI_EXT_SSE
+	li a6, ASM_SBI_EXT_SSE_COMPLETE
+	ecall
diff --git a/lib/riscv/asm-offsets.c b/lib/riscv/asm-offsets.c
index 6c511c14..b3465eeb 100644
--- a/lib/riscv/asm-offsets.c
+++ b/lib/riscv/asm-offsets.c
@@ -3,7 +3,9 @@
 #include <elf.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
+#include <asm/sbi.h>
 #include <asm/smp.h>
+#include <asm/sse.h>
 
 int main(void)
 {
@@ -63,5 +65,12 @@ int main(void)
 	OFFSET(THREAD_INFO_HARTID, thread_info, hartid);
 	DEFINE(THREAD_INFO_SIZE, sizeof(struct thread_info));
 
+	OFFSET(SSE_REG_TMP, sse_handler_arg, reg_tmp);
+	OFFSET(SSE_HANDLER, sse_handler_arg, handler);
+	OFFSET(SSE_HANDLER_DATA, sse_handler_arg, handler_data);
+	OFFSET(SSE_HANDLER_STACK, sse_handler_arg, stack);
+	DEFINE(ASM_SBI_EXT_SSE, SBI_EXT_SSE);
+	DEFINE(ASM_SBI_EXT_SSE_COMPLETE, SBI_EXT_SSE_COMPLETE);
+
 	return 0;
 }
-- 
2.45.2


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

* [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests
  2024-11-22 14:04 [kvm-unit-tests PATCH v2 0/3] riscv: add SBI SSE extension tests Clément Léger
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 1/3] riscv: lib: Add SBI SSE extension definitions Clément Léger
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling Clément Léger
@ 2024-11-22 14:04 ` Clément Léger
  2024-11-22 16:34   ` Andrew Jones
  2 siblings, 1 reply; 12+ messages in thread
From: Clément Léger @ 2024-11-22 14:04 UTC (permalink / raw)
  To: kvm, kvm-riscv
  Cc: Clément Léger, Andrew Jones, Anup Patel, Atish Patra

Add SBI SSE extension tests for the following features:
- Test attributes errors (invalid values, RO, etc)
- Registration errors
- Simple events (register, enable, inject)
- Events with different priorities
- Global events dispatch on different harts
- Local events on all harts

Signed-off-by: Clément Léger <cleger@rivosinc.com>
---
 riscv/Makefile      |   1 +
 lib/riscv/asm/csr.h |   2 +
 riscv/sbi-tests.h   |   4 +
 riscv/sbi-sse.c     | 981 ++++++++++++++++++++++++++++++++++++++++++++
 riscv/sbi.c         |   1 +
 riscv/unittests.cfg |   4 +
 6 files changed, 993 insertions(+)
 create mode 100644 riscv/sbi-sse.c

diff --git a/riscv/Makefile b/riscv/Makefile
index e50621ad..768e1c25 100644
--- a/riscv/Makefile
+++ b/riscv/Makefile
@@ -46,6 +46,7 @@ ifeq ($(ARCH),riscv32)
 cflatobjs += lib/ldiv32.o
 endif
 cflatobjs += riscv/sbi-asm.o
+cflatobjs += riscv/sbi-sse.o
 
 ########################################
 
diff --git a/lib/riscv/asm/csr.h b/lib/riscv/asm/csr.h
index 16f5ddd7..06831380 100644
--- a/lib/riscv/asm/csr.h
+++ b/lib/riscv/asm/csr.h
@@ -21,6 +21,8 @@
 /* Exception cause high bit - is an interrupt if set */
 #define CAUSE_IRQ_FLAG		(_AC(1, UL) << (__riscv_xlen - 1))
 
+#define SSTATUS_SPP		_AC(0x00000100, UL) /* Previously Supervisor */
+
 /* Exception causes */
 #define EXC_INST_MISALIGNED	0
 #define EXC_INST_ACCESS		1
diff --git a/riscv/sbi-tests.h b/riscv/sbi-tests.h
index ce129968..2115acc6 100644
--- a/riscv/sbi-tests.h
+++ b/riscv/sbi-tests.h
@@ -33,4 +33,8 @@
 #define SBI_SUSP_TEST_HARTID	(1 << 2)
 #define SBI_SUSP_TEST_MASK	7
 
+#ifndef __ASSEMBLY__
+void check_sse(void);
+#endif /* !__ASSEMBLY__ */
+
 #endif /* _RISCV_SBI_TESTS_H_ */
diff --git a/riscv/sbi-sse.c b/riscv/sbi-sse.c
new file mode 100644
index 00000000..16eb0575
--- /dev/null
+++ b/riscv/sbi-sse.c
@@ -0,0 +1,981 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SBI SSE testsuite
+ *
+ * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
+ */
+#include <libcflat.h>
+#include <alloc_page.h>
+#include <bitops.h>
+#include <cpumask.h>
+#include <libcflat.h>
+#include <on-cpus.h>
+#include <alloc.h>
+
+#include <asm/barrier.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/sbi.h>
+#include <asm/setup.h>
+#include <asm/sse.h>
+
+#include "sbi-tests.h"
+
+#define SSE_STACK_SIZE	PAGE_SIZE
+
+struct sse_event_info {
+	unsigned long event_id;
+	const char *name;
+	bool can_inject;
+};
+
+static struct sse_event_info sse_event_infos[] = {
+	{
+		.event_id = SBI_SSE_EVENT_LOCAL_RAS,
+		.name = "local_ras",
+	},
+	{
+		.event_id = SBI_SSE_EVENT_GLOBAL_RAS,
+		.name = "global_ras",
+	},
+	{
+		.event_id = SBI_SSE_EVENT_LOCAL_PMU,
+		.name = "local_pmu",
+	},
+	{
+		.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,
+		.name = "local_software",
+	},
+	{
+		.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,
+		.name = "global_software",
+	},
+};
+
+static const char *const attr_names[] = {
+	[SBI_SSE_ATTR_STATUS] = "status",
+	[SBI_SSE_ATTR_PRIO] = "prio",
+	[SBI_SSE_ATTR_CONFIG] = "config",
+	[SBI_SSE_ATTR_PREFERRED_HART] = "preferred_hart",
+	[SBI_SSE_ATTR_ENTRY_PC] = "entry_pc",
+	[SBI_SSE_ATTR_ENTRY_ARG] = "entry_arg",
+	[SBI_SSE_ATTR_INTERRUPTED_SEPC] = "interrupted_pc",
+	[SBI_SSE_ATTR_INTERRUPTED_FLAGS] = "interrupted_flags",
+	[SBI_SSE_ATTR_INTERRUPTED_A6] = "interrupted_a6",
+	[SBI_SSE_ATTR_INTERRUPTED_A7] = "interrupted_a7",
+};
+
+static const unsigned long ro_attrs[] = {
+	SBI_SSE_ATTR_STATUS,
+	SBI_SSE_ATTR_ENTRY_PC,
+	SBI_SSE_ATTR_ENTRY_ARG,
+};
+
+static const unsigned long interrupted_attrs[] = {
+	SBI_SSE_ATTR_INTERRUPTED_FLAGS,
+	SBI_SSE_ATTR_INTERRUPTED_SEPC,
+	SBI_SSE_ATTR_INTERRUPTED_A6,
+	SBI_SSE_ATTR_INTERRUPTED_A7,
+};
+
+static const unsigned long interrupted_flags[] = {
+	SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPP,
+	SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPIE,
+	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV,
+	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP,
+};
+
+static struct sse_event_info *sse_evt_get_infos(unsigned long event_id)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
+		if (sse_event_infos[i].event_id == event_id)
+			return &sse_event_infos[i];
+	}
+
+	assert_msg(false, "Invalid event id: %ld", event_id);
+}
+
+static const char *sse_evt_name(unsigned long event_id)
+{
+	struct sse_event_info *infos = sse_evt_get_infos(event_id);
+
+	return infos->name;
+}
+
+static bool sse_evt_can_inject(unsigned long event_id)
+{
+	struct sse_event_info *infos = sse_evt_get_infos(event_id);
+
+	return infos->can_inject;
+}
+
+static bool sse_event_is_global(unsigned long event_id)
+{
+	return !!(event_id & SBI_SSE_EVENT_GLOBAL_BIT);
+}
+
+static struct sbiret sse_event_get_attr_raw(unsigned long event_id,
+					    unsigned long base_attr_id,
+					    unsigned long attr_count,
+					    unsigned long phys_lo,
+					    unsigned long phys_hi)
+{
+	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_READ_ATTR, event_id,
+			base_attr_id, attr_count, phys_lo, phys_hi, 0);
+}
+
+static unsigned long sse_event_get_attrs(unsigned long event_id, unsigned long attr_id,
+					 unsigned long *values, unsigned int attr_count)
+{
+	struct sbiret ret;
+
+	ret = sse_event_get_attr_raw(event_id, attr_id, attr_count, (unsigned long)values, 0);
+
+	return ret.error;
+}
+
+static unsigned long sse_event_get_attr(unsigned long event_id, unsigned long attr_id,
+					unsigned long *value)
+{
+	return sse_event_get_attrs(event_id, attr_id, value, 1);
+}
+
+static struct sbiret sse_event_set_attr_raw(unsigned long event_id, unsigned long base_attr_id,
+					    unsigned long attr_count, unsigned long phys_lo,
+					    unsigned long phys_hi)
+{
+	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_WRITE_ATTR, event_id, base_attr_id, attr_count,
+			 phys_lo, phys_hi, 0);
+}
+
+static unsigned long sse_event_set_attr(unsigned long event_id, unsigned long attr_id,
+					unsigned long value)
+{
+	struct sbiret ret;
+
+	ret = sse_event_set_attr_raw(event_id, attr_id, 1, (unsigned long)&value, 0);
+
+	return ret.error;
+}
+
+static unsigned long sse_event_register_raw(unsigned long event_id, void *entry_pc, void *entry_arg)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, (unsigned long)entry_pc,
+			(unsigned long)entry_arg, 0, 0, 0);
+
+	return ret.error;
+}
+
+static unsigned long sse_event_register(unsigned long event_id, struct sse_handler_arg *arg)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, (unsigned long)sse_entry,
+			(unsigned long)arg, 0, 0, 0);
+
+	return ret.error;
+}
+
+static unsigned long sse_event_unregister(unsigned long event_id)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_UNREGISTER, event_id, 0, 0, 0, 0, 0);
+
+	return ret.error;
+}
+
+static unsigned long sse_event_enable(unsigned long event_id)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_ENABLE, event_id, 0, 0, 0, 0, 0);
+
+	return ret.error;
+}
+
+static unsigned long sse_event_inject(unsigned long event_id, unsigned long hart_id)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_INJECT, event_id, hart_id, 0, 0, 0, 0);
+
+	return ret.error;
+}
+
+static unsigned long sse_event_disable(unsigned long event_id)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_DISABLE, event_id, 0, 0, 0, 0, 0);
+
+	return ret.error;
+}
+
+
+static int sse_get_state(unsigned long event_id, enum sbi_sse_state *state)
+{
+	int ret;
+	unsigned long status;
+
+	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
+	if (ret) {
+		report_fail("Failed to get SSE event status");
+		return -1;
+	}
+
+	*state = status & SBI_SSE_ATTR_STATUS_STATE_MASK;
+
+	return 0;
+}
+
+static void sse_global_event_set_current_hart(unsigned long event_id)
+{
+	int ret;
+
+	if (!sse_event_is_global(event_id))
+		return;
+
+	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
+				 current_thread_info()->hartid);
+	if (ret)
+		report_abort("set preferred hart failure");
+}
+
+static int sse_check_state(unsigned long event_id, unsigned long expected_state)
+{
+	int ret;
+	enum sbi_sse_state state;
+
+	ret = sse_get_state(event_id, &state);
+	if (ret)
+		return 1;
+	report(state == expected_state, "SSE event status == %ld", expected_state);
+
+	return state != expected_state;
+}
+
+static bool sse_event_pending(unsigned long event_id)
+{
+	int ret;
+	unsigned long status;
+
+	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
+	if (ret) {
+		report_fail("Failed to get SSE event status");
+		return false;
+	}
+
+	return !!(status & BIT(SBI_SSE_ATTR_STATUS_PENDING_OFFSET));
+}
+
+static void *sse_alloc_stack(void)
+{
+	return (alloc_page() + PAGE_SIZE);
+}
+
+static void sse_free_stack(void *stack)
+{
+	free_page(stack - PAGE_SIZE);
+}
+
+static void sse_test_attr(unsigned long event_id)
+{
+	unsigned long ret, value = 0;
+	unsigned long values[ARRAY_SIZE(ro_attrs)];
+	struct sbiret sret;
+	unsigned int i;
+
+	report_prefix_push("attrs");
+
+	for (i = 0; i < ARRAY_SIZE(ro_attrs); i++) {
+		ret = sse_event_set_attr(event_id, ro_attrs[i], value);
+		report(ret == SBI_ERR_BAD_RANGE, "RO attribute %s not writable",
+		       attr_names[ro_attrs[i]]);
+	}
+
+	for (i = SBI_SSE_ATTR_STATUS; i <= SBI_SSE_ATTR_INTERRUPTED_A7; i++) {
+		ret = sse_event_get_attr(event_id, i, &value);
+		report(ret == SBI_SUCCESS, "Read single attribute %s", attr_names[i]);
+		/* Preferred Hart reset value is defined by SBI vendor and status injectable bit
+		 * also depends on the SBI implementation
+		 */
+		if (i != SBI_SSE_ATTR_STATUS && i != SBI_SSE_ATTR_PREFERRED_HART)
+			report(value == 0, "Attribute %s reset value is 0", attr_names[i]);
+	}
+
+	ret = sse_event_get_attrs(event_id, SBI_SSE_ATTR_STATUS, values,
+				  SBI_SSE_ATTR_INTERRUPTED_A7 - SBI_SSE_ATTR_STATUS);
+	report(ret == SBI_SUCCESS, "Read multiple attributes");
+
+#if __riscv_xlen > 32
+	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, 0xFFFFFFFFUL + 1UL);
+	report(ret == SBI_ERR_INVALID_PARAM, "Write prio > 0xFFFFFFFF error");
+#endif
+
+	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, ~SBI_SSE_ATTR_CONFIG_ONESHOT);
+	report(ret == SBI_ERR_INVALID_PARAM, "Write invalid config error");
+
+	if (sse_event_is_global(event_id)) {
+		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART, 0xFFFFFFFFUL);
+		report(ret == SBI_ERR_INVALID_PARAM, "Set invalid hart id error");
+	} else {
+		/* Set Hart on local event -> RO */
+		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
+					 current_thread_info()->hartid);
+		report(ret == SBI_ERR_BAD_RANGE, "Set hart id on local event error");
+	}
+
+	/* Set/get flags, sepc, a6, a7 */
+	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
+		ret = sse_event_get_attr(event_id, interrupted_attrs[i], &value);
+		report(ret == 0, "Get interrupted %s no error", attr_names[interrupted_attrs[i]]);
+
+		/* 0x1 is a valid value for all the interrupted attributes */
+		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_FLAGS, 0x1);
+		report(ret == SBI_ERR_INVALID_STATE, "Set interrupted flags invalid state error");
+	}
+
+	/* Attr_count == 0 */
+	sret = sse_event_get_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 0, (unsigned long) &value, 0);
+	report(sret.error == SBI_ERR_INVALID_PARAM, "Read attribute attr_count == 0 error");
+
+	sret = sse_event_set_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 0, (unsigned long) &value, 0);
+	report(sret.error == SBI_ERR_INVALID_PARAM, "Write attribute attr_count == 0 error");
+
+	/* Invalid attribute id */
+	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, &value);
+	report(ret == SBI_ERR_BAD_RANGE, "Read invalid attribute error");
+	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, value);
+	report(ret == SBI_ERR_BAD_RANGE, "Write invalid attribute error");
+
+	/* Misaligned phys address */
+	sret = sse_event_get_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 1,
+				      ((unsigned long) &value | 0x1), 0);
+	report(sret.error == SBI_ERR_INVALID_ADDRESS, "Read attribute with invalid address error");
+	sret = sse_event_set_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 1,
+				      ((unsigned long) &value | 0x1), 0);
+	report(sret.error == SBI_ERR_INVALID_ADDRESS, "Write attribute with invalid address error");
+
+	report_prefix_pop();
+}
+
+static void sse_test_register_error(unsigned long event_id)
+{
+	unsigned long ret;
+
+	report_prefix_push("register");
+
+	ret = sse_event_unregister(event_id);
+	report(ret == SBI_ERR_INVALID_STATE, "SSE unregister non registered event");
+
+	ret = sse_event_register_raw(event_id, (void *) 0x1, NULL);
+	report(ret == SBI_ERR_INVALID_PARAM, "SSE register misaligned entry");
+
+	ret = sse_event_register_raw(event_id, (void *) sse_entry, NULL);
+	report(ret == SBI_SUCCESS, "SSE register ok");
+	if (ret)
+		goto done;
+
+	ret = sse_event_register_raw(event_id, (void *) sse_entry, NULL);
+	report(ret == SBI_ERR_INVALID_STATE, "SSE register twice failure");
+	if (!ret)
+		goto done;
+
+	ret = sse_event_unregister(event_id);
+	report(ret == SBI_SUCCESS, "SSE unregister ok");
+
+done:
+	report_prefix_pop();
+}
+
+struct sse_simple_test_arg {
+	bool done;
+	unsigned long event_id;
+};
+
+static void sse_simple_handler(void *data, struct pt_regs *regs, unsigned int hartid)
+{
+	volatile struct sse_simple_test_arg *arg = data;
+	int ret, i;
+	const char *attr_name;
+	unsigned long event_id = arg->event_id, value, prev_value, flags, attr;
+	const unsigned long regs_len = (SBI_SSE_ATTR_INTERRUPTED_A7 - SBI_SSE_ATTR_INTERRUPTED_A6) +
+				       1;
+	unsigned long interrupted_state[regs_len];
+
+	if ((regs->status & SSTATUS_SPP) == 0)
+		report_fail("Interrupted S-mode");
+
+	if (hartid != current_thread_info()->hartid)
+		report_fail("Hartid correctly passed");
+
+	sse_check_state(event_id, SBI_SSE_STATE_RUNNING);
+	if (sse_event_pending(event_id))
+		report_fail("Event is not pending");
+
+	/* Set a6, a7, sepc, flags while running */
+	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
+		attr = interrupted_attrs[i];
+		attr_name = attr_names[attr];
+
+		ret = sse_event_get_attr(event_id, attr, &prev_value);
+		report(ret == 0, "Get attr %s no error", attr_name);
+
+		/* We test SBI_SSE_ATTR_INTERRUPTED_FLAGS below with specific flag values */
+		if (attr == SBI_SSE_ATTR_INTERRUPTED_FLAGS)
+			continue;
+
+		ret = sse_event_set_attr(event_id, attr, 0xDEADBEEF + i);
+		report(ret == 0, "Set attr %s invalid state no error", attr_name);
+
+		ret = sse_event_get_attr(event_id, attr, &value);
+		report(ret == 0, "Get attr %s modified value no error", attr_name);
+		report(value == 0xDEADBEEF + i, "Get attr %s modified value ok", attr_name);
+
+		ret = sse_event_set_attr(event_id, attr, prev_value);
+		report(ret == 0, "Restore attr %s value no error", attr_name);
+	}
+
+	/* Test all flags allowed for SBI_SSE_ATTR_INTERRUPTED_FLAGS*/
+	attr = SBI_SSE_ATTR_INTERRUPTED_FLAGS;
+	attr_name = attr_names[attr];
+	ret = sse_event_get_attr(event_id, attr, &prev_value);
+	report(ret == 0, "Get attr %s no error", attr_name);
+
+	for (i = 0; i < ARRAY_SIZE(interrupted_flags); i++) {
+		flags = interrupted_flags[i];
+		ret = sse_event_set_attr(event_id, attr, flags);
+		report(ret == 0, "Set interrupted %s value no error", attr_name);
+		ret = sse_event_get_attr(event_id, attr, &value);
+		report(value == flags, "Get attr %s modified value ok", attr_name);
+	}
+
+	ret = sse_event_set_attr(event_id, attr, prev_value);
+		report(ret == 0, "Restore attr %s value no error", attr_name);
+
+	/* Try to change HARTID/Priority while running */
+	if (sse_event_is_global(event_id)) {
+		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
+					 current_thread_info()->hartid);
+		report(ret == SBI_ERR_INVALID_STATE, "Set hart id while running error");
+	}
+
+	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, 0);
+	report(ret == SBI_ERR_INVALID_STATE, "Set priority while running error");
+
+	ret = sse_event_get_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_A6, interrupted_state,
+				  regs_len);
+	report(ret == SBI_SUCCESS, "Read interrupted context from SSE handler ok");
+	if (interrupted_state[0] != SBI_EXT_SSE_INJECT)
+		report_fail("Interrupted state a6 check ok");
+	if (interrupted_state[1] != SBI_EXT_SSE)
+		report_fail("Interrupted state a7 check ok");
+
+	arg->done = true;
+}
+
+static void sse_test_inject_simple(unsigned long event_id)
+{
+	unsigned long ret;
+	struct sse_handler_arg args;
+	volatile struct sse_simple_test_arg test_arg = {.event_id = event_id};
+
+	args.handler = sse_simple_handler;
+	args.handler_data = (void *) &test_arg;
+	args.stack = sse_alloc_stack();
+
+	report_prefix_push("simple");
+
+	ret = sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
+	if (ret)
+		goto done;
+
+	ret = sse_event_register(event_id, &args);
+	report(ret == SBI_SUCCESS, "SSE register no error");
+	if (ret)
+		goto done;
+
+	ret = sse_check_state(event_id, SBI_SSE_STATE_REGISTERED);
+	if (ret)
+		goto done;
+
+	/* Be sure global events are targeting the current hart */
+	sse_global_event_set_current_hart(event_id);
+
+	ret = sse_event_enable(event_id);
+	report(ret == SBI_SUCCESS, "SSE enable no error");
+	if (ret)
+		goto done;
+
+	ret = sse_check_state(event_id, SBI_SSE_STATE_ENABLED);
+	if (ret)
+		goto done;
+
+	ret = sse_event_inject(event_id, current_thread_info()->hartid);
+	report(ret == SBI_SUCCESS, "SSE injection no error");
+	if (ret)
+		goto done;
+
+	barrier();
+	report(test_arg.done == 1, "SSE event handled ok");
+	test_arg.done = 0;
+
+	/* Set as oneshot and verify it is disabled */
+	ret = sse_event_disable(event_id);
+	report(ret == 0, "Disable event ok");
+	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, SBI_SSE_ATTR_CONFIG_ONESHOT);
+	report(ret == 0, "Set event attribute as ONESHOT");
+	ret = sse_event_enable(event_id);
+	report(ret == 0, "Enable event ok");
+
+	ret = sse_event_inject(event_id, current_thread_info()->hartid);
+	report(ret == SBI_SUCCESS, "SSE injection 2 no error");
+	if (ret)
+		goto done;
+
+	barrier();
+	report(test_arg.done == 1, "SSE event handled ok");
+	test_arg.done = 0;
+
+	ret = sse_check_state(event_id, SBI_SSE_STATE_REGISTERED);
+	if (ret)
+		goto done;
+
+	/* Clear ONESHOT FLAG */
+	sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, 0);
+
+	ret = sse_event_unregister(event_id);
+	report(ret == SBI_SUCCESS, "SSE unregister no error");
+	if (ret)
+		goto done;
+
+	sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
+
+done:
+	sse_free_stack(args.stack);
+	report_prefix_pop();
+}
+
+struct sse_foreign_cpu_test_arg {
+	bool done;
+	unsigned int expected_cpu;
+	unsigned long event_id;
+};
+
+static void sse_foreign_cpu_handler(void *data, struct pt_regs *regs, unsigned int hartid)
+{
+	volatile struct sse_foreign_cpu_test_arg *arg = data;
+
+	/* For arg content to be visible */
+	smp_rmb();
+	if (arg->expected_cpu != current_thread_info()->cpu)
+		report_fail("Received event on CPU (%d), expected CPU (%d)",
+			    current_thread_info()->cpu, arg->expected_cpu);
+
+	arg->done = true;
+	/* For arg update to be visible for other CPUs */
+	smp_wmb();
+}
+
+struct sse_local_per_cpu {
+	struct sse_handler_arg args;
+	unsigned long ret;
+};
+
+struct sse_local_data {
+	unsigned long event_id;
+	struct sse_local_per_cpu *cpu_args[NR_CPUS];
+};
+
+static void sse_register_enable_local(void *data)
+{
+	struct sse_local_data *local_data = data;
+	struct sse_local_per_cpu *cpu_arg = local_data->cpu_args[current_thread_info()->cpu];
+
+	cpu_arg->ret = sse_event_register(local_data->event_id, &cpu_arg->args);
+	if (cpu_arg->ret)
+		return;
+
+	cpu_arg->ret = sse_event_enable(local_data->event_id);
+}
+
+static void sse_disable_unregister_local(void *data)
+{
+	struct sse_local_data *local_data = data;
+	struct sse_local_per_cpu *cpu_arg = local_data->cpu_args[current_thread_info()->cpu];
+
+	cpu_arg->ret = sse_event_disable(local_data->event_id);
+	if (cpu_arg->ret)
+		return;
+
+	cpu_arg->ret = sse_event_unregister(local_data->event_id);
+}
+
+static void sse_test_inject_local(unsigned long event_id)
+{
+	int cpu;
+	unsigned long ret;
+	struct sse_local_data local_data;
+	struct sse_local_per_cpu *cpu_arg;
+	volatile struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
+
+	report_prefix_push("local_dispatch");
+	local_data.event_id = event_id;
+
+	for_each_online_cpu(cpu) {
+		cpu_arg = calloc(1, sizeof(struct sse_handler_arg));
+
+		cpu_arg->args.stack = sse_alloc_stack();
+		cpu_arg->args.handler = sse_foreign_cpu_handler;
+		cpu_arg->args.handler_data = (void *)&test_arg;
+		local_data.cpu_args[cpu] = cpu_arg;
+	}
+
+	on_cpus(sse_register_enable_local, &local_data);
+	for_each_online_cpu(cpu) {
+		if (local_data.cpu_args[cpu]->ret)
+			report_abort("CPU failed to register/enable SSE event");
+
+		test_arg.expected_cpu = cpu;
+		/* For test_arg content to be visible for other CPUs */
+		smp_wmb();
+		ret = sse_event_inject(event_id, cpus[cpu].hartid);
+		if (ret)
+			report_abort("CPU failed to register/enable SSE event");
+
+		while (!test_arg.done) {
+			/* For test_arg update to be visible */
+			smp_rmb();
+		}
+
+		test_arg.done = false;
+	}
+
+	on_cpus(sse_disable_unregister_local, &local_data);
+	for_each_online_cpu(cpu) {
+		if (local_data.cpu_args[cpu]->ret)
+			report_abort("CPU failed to disable/unregister SSE event");
+	}
+
+	for_each_online_cpu(cpu) {
+		cpu_arg = local_data.cpu_args[cpu];
+
+		sse_free_stack(cpu_arg->args.stack);
+	}
+
+	report_pass("local event dispatch on all CPUs");
+	report_prefix_pop();
+
+}
+
+static void sse_test_inject_global(unsigned long event_id)
+{
+	unsigned long ret;
+	unsigned int cpu;
+	struct sse_handler_arg args;
+	volatile struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
+	enum sbi_sse_state state;
+
+	args.handler = sse_foreign_cpu_handler;
+	args.handler_data = (void *)&test_arg;
+	args.stack = sse_alloc_stack();
+
+	report_prefix_push("global_dispatch");
+
+	ret = sse_event_register(event_id, &args);
+	if (ret)
+		goto done;
+
+	for_each_online_cpu(cpu) {
+		test_arg.expected_cpu = cpu;
+		/* For test_arg content to be visible for other CPUs */
+		smp_wmb();
+		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART, cpu);
+		if (ret) {
+			report_fail("Failed to set preferred hart");
+			goto done;
+		}
+
+		ret = sse_event_enable(event_id);
+		if (ret) {
+			report_fail("Failed to enable SSE event");
+			goto done;
+		}
+
+		ret = sse_event_inject(event_id, cpu);
+		if (ret) {
+			report_fail("Failed to inject event");
+			goto done;
+		}
+
+		while (!test_arg.done) {
+			/* For shared test_arg structure */
+			smp_rmb();
+		}
+
+		test_arg.done = false;
+
+		/* Wait for event to be in ENABLED state */
+		do {
+			ret = sse_get_state(event_id, &state);
+			if (ret) {
+				report_fail("Failed to get event state");
+				goto done;
+			}
+		} while (state != SBI_SSE_STATE_ENABLED);
+
+		ret = sse_event_disable(event_id);
+		if (ret) {
+			report_fail("Failed to disable SSE event");
+			goto done;
+		}
+
+		report_pass("Global event on CPU %d", cpu);
+	}
+
+done:
+	ret = sse_event_unregister(event_id);
+	if (ret)
+		report_fail("Failed to unregister event");
+
+	sse_free_stack(args.stack);
+	report_prefix_pop();
+}
+
+struct priority_test_arg {
+	unsigned long evt;
+	bool called;
+	u32 prio;
+	struct priority_test_arg *next_evt_arg;
+	void (*check_func)(struct priority_test_arg *arg);
+};
+
+static void sse_hi_priority_test_handler(void *arg, struct pt_regs *regs,
+					 unsigned int hartid)
+{
+	struct priority_test_arg *targ = arg;
+	struct priority_test_arg *next = targ->next_evt_arg;
+
+	targ->called = 1;
+	if (next) {
+		sse_event_inject(next->evt, current_thread_info()->hartid);
+		if (sse_event_pending(next->evt))
+			report_fail("Higher priority event is pending");
+		if (!next->called)
+			report_fail("Higher priority event was not handled");
+	}
+}
+
+static void sse_low_priority_test_handler(void *arg, struct pt_regs *regs,
+					  unsigned int hartid)
+{
+	struct priority_test_arg *targ = arg;
+	struct priority_test_arg *next = targ->next_evt_arg;
+
+	targ->called = 1;
+
+	if (next) {
+		sse_event_inject(next->evt, current_thread_info()->hartid);
+
+		if (!sse_event_pending(next->evt))
+			report_fail("Lower priority event is pending");
+
+		if (next->called)
+			report_fail("Lower priority event %s was handle before %s",
+			      sse_evt_name(next->evt), sse_evt_name(targ->evt));
+	}
+}
+
+static void sse_test_injection_priority_arg(struct priority_test_arg *in_args,
+					    unsigned int in_args_size,
+					    sse_handler_fn handler,
+					    const char *test_name)
+{
+	unsigned int i;
+	int ret;
+	unsigned long event_id;
+	struct priority_test_arg *arg;
+	unsigned int args_size = 0;
+	struct sse_handler_arg event_args[in_args_size];
+	struct priority_test_arg *args[in_args_size];
+	void *stack;
+	struct sse_handler_arg *event_arg;
+
+	report_prefix_push(test_name);
+
+	for (i = 0; i < in_args_size; i++) {
+		arg = &in_args[i];
+		event_id = arg->evt;
+		if (!sse_evt_can_inject(event_id))
+			continue;
+
+		args[args_size] = arg;
+		args_size++;
+	}
+
+	if (!args_size) {
+		report_skip("No event injectable");
+		report_prefix_pop();
+		goto skip;
+	}
+
+	for (i = 0; i < args_size; i++) {
+		arg = args[i];
+		event_id = arg->evt;
+		stack = sse_alloc_stack();
+
+		event_arg = &event_args[i];
+		event_arg->handler = handler;
+		event_arg->handler_data = (void *)arg;
+		event_arg->stack = stack;
+
+		if (i < (args_size - 1))
+			arg->next_evt_arg = args[i + 1];
+		else
+			arg->next_evt_arg = NULL;
+
+		/* Be sure global events are targeting the current hart */
+		sse_global_event_set_current_hart(event_id);
+
+		sse_event_register(event_id, event_arg);
+		sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, arg->prio);
+		sse_event_enable(event_id);
+	}
+
+	/* Inject first event */
+	ret = sse_event_inject(args[0]->evt, current_thread_info()->hartid);
+	report(ret == SBI_SUCCESS, "SSE injection no error");
+
+	for (i = 0; i < args_size; i++) {
+		arg = args[i];
+		event_id = arg->evt;
+
+		if (!arg->called)
+			report_fail("Event %s handler called", sse_evt_name(arg->evt));
+
+		sse_event_disable(event_id);
+		sse_event_unregister(event_id);
+
+		event_arg = &event_args[i];
+		sse_free_stack(event_arg->stack);
+	}
+
+skip:
+	report_prefix_pop();
+}
+
+static struct priority_test_arg hi_prio_args[] = {
+	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
+	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
+	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
+	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
+	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
+};
+
+static struct priority_test_arg low_prio_args[] = {
+	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
+	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
+	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
+	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
+	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
+};
+
+static struct priority_test_arg prio_args[] = {
+	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 5},
+	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
+	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 15},
+	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
+	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 25},
+};
+
+static struct priority_test_arg same_prio_args[] = {
+	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 0},
+	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 10},
+	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
+	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 10},
+	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
+};
+
+static void sse_test_injection_priority(void)
+{
+	report_prefix_push("prio");
+
+	sse_test_injection_priority_arg(hi_prio_args, ARRAY_SIZE(hi_prio_args),
+					sse_hi_priority_test_handler, "high");
+
+	sse_test_injection_priority_arg(low_prio_args, ARRAY_SIZE(low_prio_args),
+					sse_low_priority_test_handler, "low");
+
+	sse_test_injection_priority_arg(prio_args, ARRAY_SIZE(prio_args),
+					sse_low_priority_test_handler, "changed");
+
+	sse_test_injection_priority_arg(same_prio_args, ARRAY_SIZE(same_prio_args),
+					sse_low_priority_test_handler, "same_prio_args");
+
+	report_prefix_pop();
+}
+
+static bool sse_can_inject(unsigned long event_id)
+{
+	int ret;
+	unsigned long status;
+
+	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
+	report(ret == 0, "SSE get attr status no error");
+	if (ret)
+		return 0;
+
+	return !!(status & BIT(SBI_SSE_ATTR_STATUS_INJECT_OFFSET));
+}
+
+static void boot_secondary(void *data)
+{
+}
+
+void check_sse(void)
+{
+	unsigned long i, event;
+
+	/*
+	 * Dummy wakeup of all processors since some of them will be targeted
+	 * by global events without going through the wakeup call.
+	 */
+	on_cpus(boot_secondary, NULL);
+	report_prefix_push("sse");
+
+	if (!sbi_probe(SBI_EXT_SSE)) {
+		report_skip("SSE extension not available");
+		report_prefix_pop();
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
+		event = sse_event_infos[i].event_id;
+		report_prefix_push(sse_event_infos[i].name);
+		if (!sse_can_inject(event)) {
+			report_skip("Event does not support injection");
+			report_prefix_pop();
+			continue;
+		} else {
+			sse_event_infos[i].can_inject = true;
+		}
+		sse_test_attr(event);
+		sse_test_register_error(event);
+		sse_test_inject_simple(event);
+		if (sse_event_is_global(event))
+			sse_test_inject_global(event);
+		else
+			sse_test_inject_local(event);
+
+		report_prefix_pop();
+	}
+
+	sse_test_injection_priority();
+
+	report_prefix_pop();
+}
diff --git a/riscv/sbi.c b/riscv/sbi.c
index 6f4ddaf1..96dfb2ca 100644
--- a/riscv/sbi.c
+++ b/riscv/sbi.c
@@ -1451,6 +1451,7 @@ int main(int argc, char **argv)
 	check_hsm();
 	check_dbcn();
 	check_susp();
+	check_sse();
 
 	return report_summary();
 }
diff --git a/riscv/unittests.cfg b/riscv/unittests.cfg
index 2eb760ec..ddd05de7 100644
--- a/riscv/unittests.cfg
+++ b/riscv/unittests.cfg
@@ -18,3 +18,7 @@ groups = selftest
 file = sbi.flat
 smp = $MAX_SMP
 groups = sbi
+
+[sbi_sse]
+file = sbi_sse.flat
+groups = sbi
-- 
2.45.2


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

* Re: [kvm-unit-tests PATCH v2 1/3] riscv: lib: Add SBI SSE extension definitions
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 1/3] riscv: lib: Add SBI SSE extension definitions Clément Léger
@ 2024-11-22 16:05   ` Andrew Jones
  0 siblings, 0 replies; 12+ messages in thread
From: Andrew Jones @ 2024-11-22 16:05 UTC (permalink / raw)
  To: Clément Léger
  Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra

On Fri, Nov 22, 2024 at 03:04:55PM +0100, Clément Léger wrote:
> Add SBI SSE extension definitions in sbi.h
> 
> Signed-off-by: Clément Léger <cleger@rivosinc.com>
> ---
>  lib/riscv/asm/sbi.h | 76 +++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 76 insertions(+)
> 
> diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
> index 98a9b097..96100bc2 100644
> --- a/lib/riscv/asm/sbi.h
> +++ b/lib/riscv/asm/sbi.h
> @@ -11,6 +11,9 @@
>  #define SBI_ERR_ALREADY_AVAILABLE	-6
>  #define SBI_ERR_ALREADY_STARTED		-7
>  #define SBI_ERR_ALREADY_STOPPED		-8
> +#define SBI_ERR_NO_SHMEM		-9
> +#define SBI_ERR_INVALID_STATE		-10
> +#define SBI_ERR_BAD_RANGE		-11

Might as well add SBI_ERR_TIMEOUT and SBI_ERR_IO now too.

>  
>  #ifndef __ASSEMBLY__
>  #include <cpumask.h>
> @@ -23,6 +26,7 @@ enum sbi_ext_id {
>  	SBI_EXT_SRST = 0x53525354,
>  	SBI_EXT_DBCN = 0x4442434E,
>  	SBI_EXT_SUSP = 0x53555350,
> +	SBI_EXT_SSE = 0x535345,
>  };
>  
>  enum sbi_ext_base_fid {
> @@ -71,6 +75,78 @@ enum sbi_ext_dbcn_fid {
>  	SBI_EXT_DBCN_CONSOLE_WRITE_BYTE,
>  };
>  
> +/* SBI Function IDs for SSE extension */
> +#define SBI_EXT_SSE_READ_ATTR		0x00000000
> +#define SBI_EXT_SSE_WRITE_ATTR		0x00000001

These are named with an 'S' on the end in the spec, e.g.
sbi_sse_read_attrs. Let's add the S here to to be consistent.

> +#define SBI_EXT_SSE_REGISTER		0x00000002
> +#define SBI_EXT_SSE_UNREGISTER		0x00000003
> +#define SBI_EXT_SSE_ENABLE		0x00000004
> +#define SBI_EXT_SSE_DISABLE		0x00000005
> +#define SBI_EXT_SSE_COMPLETE		0x00000006
> +#define SBI_EXT_SSE_INJECT		0x00000007

What about sbi_sse_hart_unmask and sbi_sse_hart_mask?

The function IDs can be enums like the other extensions define them,
unless they need to be used in assembly. But, if they need to be used
in assembly then they should be outside the '#ifndef __ASSEMBLY__'

> +
> +/* SBI SSE Event Attributes. */
> +enum sbi_sse_attr_id {
> +	SBI_SSE_ATTR_STATUS		= 0x00000000,
> +	SBI_SSE_ATTR_PRIO		= 0x00000001,

SBI_SSE_ATTR_PRIORITY

> +	SBI_SSE_ATTR_CONFIG		= 0x00000002,
> +	SBI_SSE_ATTR_PREFERRED_HART	= 0x00000003,
> +	SBI_SSE_ATTR_ENTRY_PC		= 0x00000004,
> +	SBI_SSE_ATTR_ENTRY_ARG		= 0x00000005,
> +	SBI_SSE_ATTR_INTERRUPTED_SEPC	= 0x00000006,
> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS	= 0x00000007,
> +	SBI_SSE_ATTR_INTERRUPTED_A6	= 0x00000008,
> +	SBI_SSE_ATTR_INTERRUPTED_A7	= 0x00000009,
> +};
> +
> +#define SBI_SSE_ATTR_STATUS_STATE_OFFSET	0
> +#define SBI_SSE_ATTR_STATUS_STATE_MASK		0x3
> +#define SBI_SSE_ATTR_STATUS_PENDING_OFFSET	2
> +#define SBI_SSE_ATTR_STATUS_INJECT_OFFSET	3
> +
> +#define SBI_SSE_ATTR_CONFIG_ONESHOT	(1 << 0)
> +
> +#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPP	BIT(0)
> +#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPIE	BIT(1)

s/STATUS/SSTATUS/

> +#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV	BIT(2)
> +#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP	BIT(3)
> +
> +enum sbi_sse_state {
> +	SBI_SSE_STATE_UNUSED     = 0,
> +	SBI_SSE_STATE_REGISTERED = 1,
> +	SBI_SSE_STATE_ENABLED    = 2,
> +	SBI_SSE_STATE_RUNNING    = 3,

Searching for tabs I see an unexpected lack of them above between
the enums and the ='s and also...

> +};
> +
> +/* SBI SSE Event IDs. */
> +#define SBI_SSE_EVENT_LOCAL_RAS			0x00000000

Missing LOCAL_DOUBLE_TRAP

> +#define	SBI_SSE_EVENT_LOCAL_PLAT_0_START	0x00004000
> +#define SBI_SSE_EVENT_LOCAL_PLAT_0_END		0x00007fff
> +#define SBI_SSE_EVENT_GLOBAL_RAS		0x00008000
> +#define	SBI_SSE_EVENT_GLOBAL_PLAT_0_START	0x00004000
> +#define SBI_SSE_EVENT_GLOBAL_PLAT_0_END		0x00007fff

copy+paste error, global platform specific events are 0xc000 - 0xffff

> +
> +#define SBI_SSE_EVENT_LOCAL_PMU			0x00010000
> +#define	SBI_SSE_EVENT_LOCAL_PLAT_1_START	0x00014000
> +#define SBI_SSE_EVENT_LOCAL_PLAT_1_END		0x00017fff
> +#define	SBI_SSE_EVENT_GLOBAL_PLAT_1_START	0x0001c000
> +#define SBI_SSE_EVENT_GLOBAL_PLAT_1_END		0x0001ffff
> +
> +#define	SBI_SSE_EVENT_LOCAL_PLAT_2_START	0x00024000
> +#define SBI_SSE_EVENT_LOCAL_PLAT_2_END		0x00027fff
> +#define	SBI_SSE_EVENT_GLOBAL_PLAT_2_START	0x0002c000
> +#define SBI_SSE_EVENT_GLOBAL_PLAT_2_END		0x0002ffff
> +
> +#define SBI_SSE_EVENT_LOCAL_SOFTWARE		0xffff0000
> +#define	SBI_SSE_EVENT_LOCAL_PLAT_3_START	0xffff4000
> +#define SBI_SSE_EVENT_LOCAL_PLAT_3_END		0xffff7fff
> +#define SBI_SSE_EVENT_GLOBAL_SOFTWARE		0xffff8000
> +#define	SBI_SSE_EVENT_GLOBAL_PLAT_3_START	0xffffc000
> +#define SBI_SSE_EVENT_GLOBAL_PLAT_3_END		0xffffffff

...unexpected tabs between the #define and the symbol name in
several of the above definitions.

> +
> +#define SBI_SSE_EVENT_GLOBAL_BIT		(1 << 15)
> +#define SBI_SSE_EVENT_PLATFORM_BIT		(1 << 14)

Let's put these in numeric order like the spec has them, i.e.

  #define SBI_SSE_EVENT_PLATFORM_BIT		(1 << 14)
  #define SBI_SSE_EVENT_GLOBAL_BIT		(1 << 15)

> +
>  struct sbiret {
>  	long error;
>  	long value;
> -- 
> 2.45.2
>

Thanks,
drew

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

* Re: [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling Clément Léger
@ 2024-11-22 16:20   ` Andrew Jones
  2024-11-25  8:46     ` Clément Léger
  0 siblings, 1 reply; 12+ messages in thread
From: Andrew Jones @ 2024-11-22 16:20 UTC (permalink / raw)
  To: Clément Léger
  Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra

On Fri, Nov 22, 2024 at 03:04:56PM +0100, Clément Léger wrote:
> Add a SSE entry assembly code to handle SSE events. Events should be
> registered with a struct sse_handler_arg containing a correct stack and
> handler function.
> 
> Signed-off-by: Clément Léger <cleger@rivosinc.com>
> ---
>  riscv/Makefile          |   1 +
>  lib/riscv/asm/sse.h     |  16 +++++++
>  lib/riscv/sse-entry.S   | 100 ++++++++++++++++++++++++++++++++++++++++

Let's just add the entry function to riscv/sbi-asm.S and the
sse_handler_arg struct definition to riscv/sbi-tests.h

>  lib/riscv/asm-offsets.c |   9 ++++
>  4 files changed, 126 insertions(+)
>  create mode 100644 lib/riscv/asm/sse.h
>  create mode 100644 lib/riscv/sse-entry.S
> 
> diff --git a/riscv/Makefile b/riscv/Makefile
> index 28b04156..e50621ad 100644
> --- a/riscv/Makefile
> +++ b/riscv/Makefile
> @@ -39,6 +39,7 @@ cflatobjs += lib/riscv/sbi.o
>  cflatobjs += lib/riscv/setjmp.o
>  cflatobjs += lib/riscv/setup.o
>  cflatobjs += lib/riscv/smp.o
> +cflatobjs += lib/riscv/sse-entry.o
>  cflatobjs += lib/riscv/stack.o
>  cflatobjs += lib/riscv/timer.o
>  ifeq ($(ARCH),riscv32)
> diff --git a/lib/riscv/asm/sse.h b/lib/riscv/asm/sse.h
> new file mode 100644
> index 00000000..557f6680
> --- /dev/null
> +++ b/lib/riscv/asm/sse.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef _ASMRISCV_SSE_H_
> +#define _ASMRISCV_SSE_H_
> +
> +typedef void (*sse_handler_fn)(void *data, struct pt_regs *regs, unsigned int hartid);
> +
> +struct sse_handler_arg {
> +	unsigned long reg_tmp;
> +	sse_handler_fn handler;
> +	void *handler_data;
> +	void *stack;
> +};
> +
> +extern void sse_entry(void);
> +
> +#endif /* _ASMRISCV_SSE_H_ */
> diff --git a/lib/riscv/sse-entry.S b/lib/riscv/sse-entry.S
> new file mode 100644
> index 00000000..bedc47e9
> --- /dev/null
> +++ b/lib/riscv/sse-entry.S
> @@ -0,0 +1,100 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * SBI SSE entry code
> + *
> + * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
> + */
> +#include <asm/asm.h>
> +#include <asm/asm-offsets.h>
> +#include <asm/csr.h>
> +
> +.global sse_entry
> +sse_entry:
> +	/* Save stack temporarily */
> +	REG_S sp, SSE_REG_TMP(a6)
> +	/* Set entry stack */
> +	REG_L sp, SSE_HANDLER_STACK(a6)
> +
> +	addi sp, sp, -(PT_SIZE)
> +	REG_S ra, PT_RA(sp)
> +	REG_S s0, PT_S0(sp)
> +	REG_S s1, PT_S1(sp)
> +	REG_S s2, PT_S2(sp)
> +	REG_S s3, PT_S3(sp)
> +	REG_S s4, PT_S4(sp)
> +	REG_S s5, PT_S5(sp)
> +	REG_S s6, PT_S6(sp)
> +	REG_S s7, PT_S7(sp)
> +	REG_S s8, PT_S8(sp)
> +	REG_S s9, PT_S9(sp)
> +	REG_S s10, PT_S10(sp)
> +	REG_S s11, PT_S11(sp)
> +	REG_S tp, PT_TP(sp)
> +	REG_S t0, PT_T0(sp)
> +	REG_S t1, PT_T1(sp)
> +	REG_S t2, PT_T2(sp)
> +	REG_S t3, PT_T3(sp)
> +	REG_S t4, PT_T4(sp)
> +	REG_S t5, PT_T5(sp)
> +	REG_S t6, PT_T6(sp)
> +	REG_S gp, PT_GP(sp)
> +	REG_S a0, PT_A0(sp)
> +	REG_S a1, PT_A1(sp)
> +	REG_S a2, PT_A2(sp)
> +	REG_S a3, PT_A3(sp)
> +	REG_S a4, PT_A4(sp)
> +	REG_S a5, PT_A5(sp)
> +	csrr a1, CSR_SEPC
> +	REG_S a1, PT_EPC(sp)
> +	csrr a2, CSR_SSTATUS
> +	REG_S a2, PT_STATUS(sp)
> +
> +	REG_L a0, SSE_REG_TMP(a6)
> +	REG_S a0, PT_SP(sp)
> +
> +	REG_L t0, SSE_HANDLER(a6)
> +	REG_L a0, SSE_HANDLER_DATA(a6)
> +	move a1, sp
> +	move a2, a7

nit: prefer 'mv'

> +	jalr t0
> +
> +
> +	REG_L a1, PT_EPC(sp)
> +	REG_L a2, PT_STATUS(sp)
> +	csrw CSR_SEPC, a1
> +	csrw CSR_SSTATUS, a2
> +
> +	REG_L ra, PT_RA(sp)
> +	REG_L s0, PT_S0(sp)
> +	REG_L s1, PT_S1(sp)
> +	REG_L s2, PT_S2(sp)
> +	REG_L s3, PT_S3(sp)
> +	REG_L s4, PT_S4(sp)
> +	REG_L s5, PT_S5(sp)
> +	REG_L s6, PT_S6(sp)
> +	REG_L s7, PT_S7(sp)
> +	REG_L s8, PT_S8(sp)
> +	REG_L s9, PT_S9(sp)
> +	REG_L s10, PT_S10(sp)
> +	REG_L s11, PT_S11(sp)
> +	REG_L tp, PT_TP(sp)
> +	REG_L t0, PT_T0(sp)
> +	REG_L t1, PT_T1(sp)
> +	REG_L t2, PT_T2(sp)
> +	REG_L t3, PT_T3(sp)
> +	REG_L t4, PT_T4(sp)
> +	REG_L t5, PT_T5(sp)
> +	REG_L t6, PT_T6(sp)
> +	REG_L gp, PT_GP(sp)
> +	REG_L a0, PT_A0(sp)
> +	REG_L a1, PT_A1(sp)
> +	REG_L a2, PT_A2(sp)
> +	REG_L a3, PT_A3(sp)
> +	REG_L a4, PT_A4(sp)
> +	REG_L a5, PT_A5(sp)
> +
> +	REG_L sp, PT_SP(sp)
> +
> +	li a7, ASM_SBI_EXT_SSE
> +	li a6, ASM_SBI_EXT_SSE_COMPLETE
> +	ecall
> diff --git a/lib/riscv/asm-offsets.c b/lib/riscv/asm-offsets.c
> index 6c511c14..b3465eeb 100644
> --- a/lib/riscv/asm-offsets.c
> +++ b/lib/riscv/asm-offsets.c
> @@ -3,7 +3,9 @@
>  #include <elf.h>
>  #include <asm/processor.h>
>  #include <asm/ptrace.h>
> +#include <asm/sbi.h>
>  #include <asm/smp.h>
> +#include <asm/sse.h>
>  
>  int main(void)
>  {
> @@ -63,5 +65,12 @@ int main(void)
>  	OFFSET(THREAD_INFO_HARTID, thread_info, hartid);
>  	DEFINE(THREAD_INFO_SIZE, sizeof(struct thread_info));
>  
> +	OFFSET(SSE_REG_TMP, sse_handler_arg, reg_tmp);
> +	OFFSET(SSE_HANDLER, sse_handler_arg, handler);
> +	OFFSET(SSE_HANDLER_DATA, sse_handler_arg, handler_data);
> +	OFFSET(SSE_HANDLER_STACK, sse_handler_arg, stack);
> +	DEFINE(ASM_SBI_EXT_SSE, SBI_EXT_SSE);
> +	DEFINE(ASM_SBI_EXT_SSE_COMPLETE, SBI_EXT_SSE_COMPLETE);

sbi_hsm_check just uses the numbers and adds comments. We could instead
move the extension IDs from an enum to defines outside the
#ifndef __ASSEMBLY__ and function IDs can also be defines accessible
by assembly.

> +
>  	return 0;
>  }
> -- 
> 2.45.2
>

Thanks,
drew

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

* Re: [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests
  2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests Clément Léger
@ 2024-11-22 16:34   ` Andrew Jones
  2024-11-25  8:55     ` Clément Léger
  0 siblings, 1 reply; 12+ messages in thread
From: Andrew Jones @ 2024-11-22 16:34 UTC (permalink / raw)
  To: Clément Léger
  Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra

On Fri, Nov 22, 2024 at 03:04:57PM +0100, Clément Léger wrote:
> Add SBI SSE extension tests for the following features:
> - Test attributes errors (invalid values, RO, etc)
> - Registration errors
> - Simple events (register, enable, inject)
> - Events with different priorities
> - Global events dispatch on different harts
> - Local events on all harts
> 
> Signed-off-by: Clément Léger <cleger@rivosinc.com>
> ---
>  riscv/Makefile      |   1 +
>  lib/riscv/asm/csr.h |   2 +
>  riscv/sbi-tests.h   |   4 +
>  riscv/sbi-sse.c     | 981 ++++++++++++++++++++++++++++++++++++++++++++
>  riscv/sbi.c         |   1 +
>  riscv/unittests.cfg |   4 +
>  6 files changed, 993 insertions(+)
>  create mode 100644 riscv/sbi-sse.c
> 
> diff --git a/riscv/Makefile b/riscv/Makefile
> index e50621ad..768e1c25 100644
> --- a/riscv/Makefile
> +++ b/riscv/Makefile
> @@ -46,6 +46,7 @@ ifeq ($(ARCH),riscv32)
>  cflatobjs += lib/ldiv32.o
>  endif
>  cflatobjs += riscv/sbi-asm.o
> +cflatobjs += riscv/sbi-sse.o

We should figure out how to only link these files into
riscv/sbi.{flat,efi}

>  
>  ########################################
>  
> diff --git a/lib/riscv/asm/csr.h b/lib/riscv/asm/csr.h
> index 16f5ddd7..06831380 100644
> --- a/lib/riscv/asm/csr.h
> +++ b/lib/riscv/asm/csr.h
> @@ -21,6 +21,8 @@
>  /* Exception cause high bit - is an interrupt if set */
>  #define CAUSE_IRQ_FLAG		(_AC(1, UL) << (__riscv_xlen - 1))
>  
> +#define SSTATUS_SPP		_AC(0x00000100, UL) /* Previously Supervisor */
> +
>  /* Exception causes */
>  #define EXC_INST_MISALIGNED	0
>  #define EXC_INST_ACCESS		1
> diff --git a/riscv/sbi-tests.h b/riscv/sbi-tests.h
> index ce129968..2115acc6 100644
> --- a/riscv/sbi-tests.h
> +++ b/riscv/sbi-tests.h
> @@ -33,4 +33,8 @@
>  #define SBI_SUSP_TEST_HARTID	(1 << 2)
>  #define SBI_SUSP_TEST_MASK	7
>  
> +#ifndef __ASSEMBLY__
> +void check_sse(void);

We can just put this in riscv/sbi.c

> +#endif /* !__ASSEMBLY__ */
> +
>  #endif /* _RISCV_SBI_TESTS_H_ */
> diff --git a/riscv/sbi-sse.c b/riscv/sbi-sse.c
> new file mode 100644
> index 00000000..16eb0575
> --- /dev/null
> +++ b/riscv/sbi-sse.c
> @@ -0,0 +1,981 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * SBI SSE testsuite
> + *
> + * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
> + */
> +#include <libcflat.h>
> +#include <alloc_page.h>
> +#include <bitops.h>
> +#include <cpumask.h>
> +#include <libcflat.h>

libcflat.h is repeated and let's alphabetize all these

> +#include <on-cpus.h>
> +#include <alloc.h>
> +
> +#include <asm/barrier.h>
> +#include <asm/page.h>
> +#include <asm/processor.h>
> +#include <asm/sbi.h>
> +#include <asm/setup.h>
> +#include <asm/sse.h>
> +
> +#include "sbi-tests.h"
> +
> +#define SSE_STACK_SIZE	PAGE_SIZE
> +
> +struct sse_event_info {
> +	unsigned long event_id;
> +	const char *name;
> +	bool can_inject;
> +};
> +
> +static struct sse_event_info sse_event_infos[] = {
> +	{
> +		.event_id = SBI_SSE_EVENT_LOCAL_RAS,
> +		.name = "local_ras",
> +	},
> +	{
> +		.event_id = SBI_SSE_EVENT_GLOBAL_RAS,
> +		.name = "global_ras",
> +	},
> +	{
> +		.event_id = SBI_SSE_EVENT_LOCAL_PMU,
> +		.name = "local_pmu",
> +	},
> +	{
> +		.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,
> +		.name = "local_software",
> +	},
> +	{
> +		.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,
> +		.name = "global_software",
> +	},
> +};
> +
> +static const char *const attr_names[] = {
> +	[SBI_SSE_ATTR_STATUS] = "status",
> +	[SBI_SSE_ATTR_PRIO] = "prio",
> +	[SBI_SSE_ATTR_CONFIG] = "config",
> +	[SBI_SSE_ATTR_PREFERRED_HART] = "preferred_hart",
> +	[SBI_SSE_ATTR_ENTRY_PC] = "entry_pc",
> +	[SBI_SSE_ATTR_ENTRY_ARG] = "entry_arg",
> +	[SBI_SSE_ATTR_INTERRUPTED_SEPC] = "interrupted_pc",
> +	[SBI_SSE_ATTR_INTERRUPTED_FLAGS] = "interrupted_flags",
> +	[SBI_SSE_ATTR_INTERRUPTED_A6] = "interrupted_a6",
> +	[SBI_SSE_ATTR_INTERRUPTED_A7] = "interrupted_a7",
> +};
> +
> +static const unsigned long ro_attrs[] = {
> +	SBI_SSE_ATTR_STATUS,
> +	SBI_SSE_ATTR_ENTRY_PC,
> +	SBI_SSE_ATTR_ENTRY_ARG,
> +};
> +
> +static const unsigned long interrupted_attrs[] = {
> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS,
> +	SBI_SSE_ATTR_INTERRUPTED_SEPC,
> +	SBI_SSE_ATTR_INTERRUPTED_A6,
> +	SBI_SSE_ATTR_INTERRUPTED_A7,
> +};
> +
> +static const unsigned long interrupted_flags[] = {
> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPP,
> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPIE,
> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV,
> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP,
> +};
> +
> +static struct sse_event_info *sse_evt_get_infos(unsigned long event_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
> +		if (sse_event_infos[i].event_id == event_id)
> +			return &sse_event_infos[i];
> +	}
> +
> +	assert_msg(false, "Invalid event id: %ld", event_id);
> +}
> +
> +static const char *sse_evt_name(unsigned long event_id)
> +{
> +	struct sse_event_info *infos = sse_evt_get_infos(event_id);
> +
> +	return infos->name;
> +}
> +
> +static bool sse_evt_can_inject(unsigned long event_id)
> +{
> +	struct sse_event_info *infos = sse_evt_get_infos(event_id);
> +
> +	return infos->can_inject;
> +}
> +
> +static bool sse_event_is_global(unsigned long event_id)
> +{
> +	return !!(event_id & SBI_SSE_EVENT_GLOBAL_BIT);
> +}
> +
> +static struct sbiret sse_event_get_attr_raw(unsigned long event_id,
> +					    unsigned long base_attr_id,
> +					    unsigned long attr_count,
> +					    unsigned long phys_lo,
> +					    unsigned long phys_hi)
> +{
> +	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_READ_ATTR, event_id,
> +			base_attr_id, attr_count, phys_lo, phys_hi, 0);
> +}
> +
> +static unsigned long sse_event_get_attrs(unsigned long event_id, unsigned long attr_id,
> +					 unsigned long *values, unsigned int attr_count)
> +{
> +	struct sbiret ret;
> +
> +	ret = sse_event_get_attr_raw(event_id, attr_id, attr_count, (unsigned long)values, 0);
> +
> +	return ret.error;
> +}
> +
> +static unsigned long sse_event_get_attr(unsigned long event_id, unsigned long attr_id,
> +					unsigned long *value)
> +{
> +	return sse_event_get_attrs(event_id, attr_id, value, 1);
> +}
> +
> +static struct sbiret sse_event_set_attr_raw(unsigned long event_id, unsigned long base_attr_id,
> +					    unsigned long attr_count, unsigned long phys_lo,
> +					    unsigned long phys_hi)
> +{
> +	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_WRITE_ATTR, event_id, base_attr_id, attr_count,
> +			 phys_lo, phys_hi, 0);
> +}
> +
> +static unsigned long sse_event_set_attr(unsigned long event_id, unsigned long attr_id,
> +					unsigned long value)
> +{
> +	struct sbiret ret;
> +
> +	ret = sse_event_set_attr_raw(event_id, attr_id, 1, (unsigned long)&value, 0);
> +
> +	return ret.error;
> +}
> +
> +static unsigned long sse_event_register_raw(unsigned long event_id, void *entry_pc, void *entry_arg)
> +{
> +	struct sbiret ret;
> +
> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, (unsigned long)entry_pc,
> +			(unsigned long)entry_arg, 0, 0, 0);
> +
> +	return ret.error;
> +}
> +
> +static unsigned long sse_event_register(unsigned long event_id, struct sse_handler_arg *arg)
> +{
> +	struct sbiret ret;
> +
> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, (unsigned long)sse_entry,
> +			(unsigned long)arg, 0, 0, 0);
> +
> +	return ret.error;
> +}
> +
> +static unsigned long sse_event_unregister(unsigned long event_id)
> +{
> +	struct sbiret ret;
> +
> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_UNREGISTER, event_id, 0, 0, 0, 0, 0);
> +
> +	return ret.error;
> +}
> +
> +static unsigned long sse_event_enable(unsigned long event_id)
> +{
> +	struct sbiret ret;
> +
> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_ENABLE, event_id, 0, 0, 0, 0, 0);
> +
> +	return ret.error;
> +}
> +
> +static unsigned long sse_event_inject(unsigned long event_id, unsigned long hart_id)
> +{
> +	struct sbiret ret;
> +
> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_INJECT, event_id, hart_id, 0, 0, 0, 0);
> +
> +	return ret.error;
> +}
> +
> +static unsigned long sse_event_disable(unsigned long event_id)
> +{
> +	struct sbiret ret;
> +
> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_DISABLE, event_id, 0, 0, 0, 0, 0);
> +
> +	return ret.error;
> +}
> +
> +
> +static int sse_get_state(unsigned long event_id, enum sbi_sse_state *state)
> +{
> +	int ret;
> +	unsigned long status;
> +
> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
> +	if (ret) {
> +		report_fail("Failed to get SSE event status");
> +		return -1;
> +	}
> +
> +	*state = status & SBI_SSE_ATTR_STATUS_STATE_MASK;
> +
> +	return 0;
> +}
> +
> +static void sse_global_event_set_current_hart(unsigned long event_id)
> +{
> +	int ret;
> +
> +	if (!sse_event_is_global(event_id))
> +		return;
> +
> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
> +				 current_thread_info()->hartid);
> +	if (ret)
> +		report_abort("set preferred hart failure");
> +}
> +
> +static int sse_check_state(unsigned long event_id, unsigned long expected_state)
> +{
> +	int ret;
> +	enum sbi_sse_state state;
> +
> +	ret = sse_get_state(event_id, &state);
> +	if (ret)
> +		return 1;
> +	report(state == expected_state, "SSE event status == %ld", expected_state);
> +
> +	return state != expected_state;
> +}
> +
> +static bool sse_event_pending(unsigned long event_id)
> +{
> +	int ret;
> +	unsigned long status;
> +
> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
> +	if (ret) {
> +		report_fail("Failed to get SSE event status");
> +		return false;
> +	}
> +
> +	return !!(status & BIT(SBI_SSE_ATTR_STATUS_PENDING_OFFSET));
> +}
> +
> +static void *sse_alloc_stack(void)
> +{
> +	return (alloc_page() + PAGE_SIZE);
> +}
> +
> +static void sse_free_stack(void *stack)
> +{
> +	free_page(stack - PAGE_SIZE);
> +}

I guess this should be SSE_STACK_SIZE, otherwise that define can be
removed

> +
> +static void sse_test_attr(unsigned long event_id)
> +{
> +	unsigned long ret, value = 0;
> +	unsigned long values[ARRAY_SIZE(ro_attrs)];
> +	struct sbiret sret;
> +	unsigned int i;
> +
> +	report_prefix_push("attrs");
> +
> +	for (i = 0; i < ARRAY_SIZE(ro_attrs); i++) {
> +		ret = sse_event_set_attr(event_id, ro_attrs[i], value);
> +		report(ret == SBI_ERR_BAD_RANGE, "RO attribute %s not writable",
> +		       attr_names[ro_attrs[i]]);
> +	}
> +
> +	for (i = SBI_SSE_ATTR_STATUS; i <= SBI_SSE_ATTR_INTERRUPTED_A7; i++) {
> +		ret = sse_event_get_attr(event_id, i, &value);
> +		report(ret == SBI_SUCCESS, "Read single attribute %s", attr_names[i]);
> +		/* Preferred Hart reset value is defined by SBI vendor and status injectable bit
> +		 * also depends on the SBI implementation
> +		 */
> +		if (i != SBI_SSE_ATTR_STATUS && i != SBI_SSE_ATTR_PREFERRED_HART)
> +			report(value == 0, "Attribute %s reset value is 0", attr_names[i]);
> +	}
> +
> +	ret = sse_event_get_attrs(event_id, SBI_SSE_ATTR_STATUS, values,
> +				  SBI_SSE_ATTR_INTERRUPTED_A7 - SBI_SSE_ATTR_STATUS);
> +	report(ret == SBI_SUCCESS, "Read multiple attributes");
> +
> +#if __riscv_xlen > 32
> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, 0xFFFFFFFFUL + 1UL);
> +	report(ret == SBI_ERR_INVALID_PARAM, "Write prio > 0xFFFFFFFF error");
> +#endif
> +
> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, ~SBI_SSE_ATTR_CONFIG_ONESHOT);
> +	report(ret == SBI_ERR_INVALID_PARAM, "Write invalid config error");
> +
> +	if (sse_event_is_global(event_id)) {
> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART, 0xFFFFFFFFUL);
> +		report(ret == SBI_ERR_INVALID_PARAM, "Set invalid hart id error");
> +	} else {
> +		/* Set Hart on local event -> RO */
> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
> +					 current_thread_info()->hartid);
> +		report(ret == SBI_ERR_BAD_RANGE, "Set hart id on local event error");
> +	}
> +
> +	/* Set/get flags, sepc, a6, a7 */
> +	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
> +		ret = sse_event_get_attr(event_id, interrupted_attrs[i], &value);
> +		report(ret == 0, "Get interrupted %s no error", attr_names[interrupted_attrs[i]]);
> +
> +		/* 0x1 is a valid value for all the interrupted attributes */
> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_FLAGS, 0x1);
> +		report(ret == SBI_ERR_INVALID_STATE, "Set interrupted flags invalid state error");
> +	}
> +
> +	/* Attr_count == 0 */
> +	sret = sse_event_get_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 0, (unsigned long) &value, 0);
> +	report(sret.error == SBI_ERR_INVALID_PARAM, "Read attribute attr_count == 0 error");
> +
> +	sret = sse_event_set_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 0, (unsigned long) &value, 0);
> +	report(sret.error == SBI_ERR_INVALID_PARAM, "Write attribute attr_count == 0 error");
> +
> +	/* Invalid attribute id */
> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, &value);
> +	report(ret == SBI_ERR_BAD_RANGE, "Read invalid attribute error");
> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, value);
> +	report(ret == SBI_ERR_BAD_RANGE, "Write invalid attribute error");
> +
> +	/* Misaligned phys address */
> +	sret = sse_event_get_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 1,
> +				      ((unsigned long) &value | 0x1), 0);
> +	report(sret.error == SBI_ERR_INVALID_ADDRESS, "Read attribute with invalid address error");
> +	sret = sse_event_set_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 1,
> +				      ((unsigned long) &value | 0x1), 0);
> +	report(sret.error == SBI_ERR_INVALID_ADDRESS, "Write attribute with invalid address error");
> +
> +	report_prefix_pop();
> +}
> +
> +static void sse_test_register_error(unsigned long event_id)
> +{
> +	unsigned long ret;
> +
> +	report_prefix_push("register");
> +
> +	ret = sse_event_unregister(event_id);
> +	report(ret == SBI_ERR_INVALID_STATE, "SSE unregister non registered event");
> +
> +	ret = sse_event_register_raw(event_id, (void *) 0x1, NULL);
> +	report(ret == SBI_ERR_INVALID_PARAM, "SSE register misaligned entry");
> +
> +	ret = sse_event_register_raw(event_id, (void *) sse_entry, NULL);
> +	report(ret == SBI_SUCCESS, "SSE register ok");
> +	if (ret)
> +		goto done;
> +
> +	ret = sse_event_register_raw(event_id, (void *) sse_entry, NULL);
> +	report(ret == SBI_ERR_INVALID_STATE, "SSE register twice failure");
> +	if (!ret)
> +		goto done;
> +
> +	ret = sse_event_unregister(event_id);
> +	report(ret == SBI_SUCCESS, "SSE unregister ok");
> +
> +done:
> +	report_prefix_pop();
> +}
> +
> +struct sse_simple_test_arg {
> +	bool done;
> +	unsigned long event_id;
> +};
> +
> +static void sse_simple_handler(void *data, struct pt_regs *regs, unsigned int hartid)
> +{
> +	volatile struct sse_simple_test_arg *arg = data;
> +	int ret, i;
> +	const char *attr_name;
> +	unsigned long event_id = arg->event_id, value, prev_value, flags, attr;
> +	const unsigned long regs_len = (SBI_SSE_ATTR_INTERRUPTED_A7 - SBI_SSE_ATTR_INTERRUPTED_A6) +
> +				       1;
> +	unsigned long interrupted_state[regs_len];
> +
> +	if ((regs->status & SSTATUS_SPP) == 0)
> +		report_fail("Interrupted S-mode");
> +
> +	if (hartid != current_thread_info()->hartid)
> +		report_fail("Hartid correctly passed");
> +
> +	sse_check_state(event_id, SBI_SSE_STATE_RUNNING);
> +	if (sse_event_pending(event_id))
> +		report_fail("Event is not pending");
> +
> +	/* Set a6, a7, sepc, flags while running */
> +	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
> +		attr = interrupted_attrs[i];
> +		attr_name = attr_names[attr];
> +
> +		ret = sse_event_get_attr(event_id, attr, &prev_value);
> +		report(ret == 0, "Get attr %s no error", attr_name);
> +
> +		/* We test SBI_SSE_ATTR_INTERRUPTED_FLAGS below with specific flag values */
> +		if (attr == SBI_SSE_ATTR_INTERRUPTED_FLAGS)
> +			continue;
> +
> +		ret = sse_event_set_attr(event_id, attr, 0xDEADBEEF + i);
> +		report(ret == 0, "Set attr %s invalid state no error", attr_name);
> +
> +		ret = sse_event_get_attr(event_id, attr, &value);
> +		report(ret == 0, "Get attr %s modified value no error", attr_name);
> +		report(value == 0xDEADBEEF + i, "Get attr %s modified value ok", attr_name);
> +
> +		ret = sse_event_set_attr(event_id, attr, prev_value);
> +		report(ret == 0, "Restore attr %s value no error", attr_name);
> +	}
> +
> +	/* Test all flags allowed for SBI_SSE_ATTR_INTERRUPTED_FLAGS*/
> +	attr = SBI_SSE_ATTR_INTERRUPTED_FLAGS;
> +	attr_name = attr_names[attr];
> +	ret = sse_event_get_attr(event_id, attr, &prev_value);
> +	report(ret == 0, "Get attr %s no error", attr_name);
> +
> +	for (i = 0; i < ARRAY_SIZE(interrupted_flags); i++) {
> +		flags = interrupted_flags[i];
> +		ret = sse_event_set_attr(event_id, attr, flags);
> +		report(ret == 0, "Set interrupted %s value no error", attr_name);
> +		ret = sse_event_get_attr(event_id, attr, &value);
> +		report(value == flags, "Get attr %s modified value ok", attr_name);
> +	}
> +
> +	ret = sse_event_set_attr(event_id, attr, prev_value);
> +		report(ret == 0, "Restore attr %s value no error", attr_name);
> +
> +	/* Try to change HARTID/Priority while running */
> +	if (sse_event_is_global(event_id)) {
> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
> +					 current_thread_info()->hartid);
> +		report(ret == SBI_ERR_INVALID_STATE, "Set hart id while running error");
> +	}
> +
> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, 0);
> +	report(ret == SBI_ERR_INVALID_STATE, "Set priority while running error");
> +
> +	ret = sse_event_get_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_A6, interrupted_state,
> +				  regs_len);
> +	report(ret == SBI_SUCCESS, "Read interrupted context from SSE handler ok");
> +	if (interrupted_state[0] != SBI_EXT_SSE_INJECT)
> +		report_fail("Interrupted state a6 check ok");
> +	if (interrupted_state[1] != SBI_EXT_SSE)
> +		report_fail("Interrupted state a7 check ok");
> +
> +	arg->done = true;
> +}
> +
> +static void sse_test_inject_simple(unsigned long event_id)
> +{
> +	unsigned long ret;
> +	struct sse_handler_arg args;
> +	volatile struct sse_simple_test_arg test_arg = {.event_id = event_id};
> +
> +	args.handler = sse_simple_handler;
> +	args.handler_data = (void *) &test_arg;
> +	args.stack = sse_alloc_stack();
> +
> +	report_prefix_push("simple");
> +
> +	ret = sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
> +	if (ret)
> +		goto done;
> +
> +	ret = sse_event_register(event_id, &args);
> +	report(ret == SBI_SUCCESS, "SSE register no error");
> +	if (ret)
> +		goto done;
> +
> +	ret = sse_check_state(event_id, SBI_SSE_STATE_REGISTERED);
> +	if (ret)
> +		goto done;
> +
> +	/* Be sure global events are targeting the current hart */
> +	sse_global_event_set_current_hart(event_id);
> +
> +	ret = sse_event_enable(event_id);
> +	report(ret == SBI_SUCCESS, "SSE enable no error");
> +	if (ret)
> +		goto done;
> +
> +	ret = sse_check_state(event_id, SBI_SSE_STATE_ENABLED);
> +	if (ret)
> +		goto done;
> +
> +	ret = sse_event_inject(event_id, current_thread_info()->hartid);
> +	report(ret == SBI_SUCCESS, "SSE injection no error");
> +	if (ret)
> +		goto done;
> +
> +	barrier();
> +	report(test_arg.done == 1, "SSE event handled ok");
> +	test_arg.done = 0;
> +
> +	/* Set as oneshot and verify it is disabled */
> +	ret = sse_event_disable(event_id);
> +	report(ret == 0, "Disable event ok");
> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, SBI_SSE_ATTR_CONFIG_ONESHOT);
> +	report(ret == 0, "Set event attribute as ONESHOT");
> +	ret = sse_event_enable(event_id);
> +	report(ret == 0, "Enable event ok");
> +
> +	ret = sse_event_inject(event_id, current_thread_info()->hartid);
> +	report(ret == SBI_SUCCESS, "SSE injection 2 no error");
> +	if (ret)
> +		goto done;
> +
> +	barrier();
> +	report(test_arg.done == 1, "SSE event handled ok");
> +	test_arg.done = 0;
> +
> +	ret = sse_check_state(event_id, SBI_SSE_STATE_REGISTERED);
> +	if (ret)
> +		goto done;
> +
> +	/* Clear ONESHOT FLAG */
> +	sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, 0);
> +
> +	ret = sse_event_unregister(event_id);
> +	report(ret == SBI_SUCCESS, "SSE unregister no error");
> +	if (ret)
> +		goto done;
> +
> +	sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
> +
> +done:
> +	sse_free_stack(args.stack);
> +	report_prefix_pop();
> +}
> +
> +struct sse_foreign_cpu_test_arg {
> +	bool done;
> +	unsigned int expected_cpu;
> +	unsigned long event_id;
> +};
> +
> +static void sse_foreign_cpu_handler(void *data, struct pt_regs *regs, unsigned int hartid)
> +{
> +	volatile struct sse_foreign_cpu_test_arg *arg = data;
> +
> +	/* For arg content to be visible */
> +	smp_rmb();
> +	if (arg->expected_cpu != current_thread_info()->cpu)
> +		report_fail("Received event on CPU (%d), expected CPU (%d)",
> +			    current_thread_info()->cpu, arg->expected_cpu);
> +
> +	arg->done = true;
> +	/* For arg update to be visible for other CPUs */
> +	smp_wmb();
> +}
> +
> +struct sse_local_per_cpu {
> +	struct sse_handler_arg args;
> +	unsigned long ret;
> +};
> +
> +struct sse_local_data {
> +	unsigned long event_id;
> +	struct sse_local_per_cpu *cpu_args[NR_CPUS];
> +};
> +
> +static void sse_register_enable_local(void *data)
> +{
> +	struct sse_local_data *local_data = data;
> +	struct sse_local_per_cpu *cpu_arg = local_data->cpu_args[current_thread_info()->cpu];
> +
> +	cpu_arg->ret = sse_event_register(local_data->event_id, &cpu_arg->args);
> +	if (cpu_arg->ret)
> +		return;
> +
> +	cpu_arg->ret = sse_event_enable(local_data->event_id);
> +}
> +
> +static void sse_disable_unregister_local(void *data)
> +{
> +	struct sse_local_data *local_data = data;
> +	struct sse_local_per_cpu *cpu_arg = local_data->cpu_args[current_thread_info()->cpu];
> +
> +	cpu_arg->ret = sse_event_disable(local_data->event_id);
> +	if (cpu_arg->ret)
> +		return;
> +
> +	cpu_arg->ret = sse_event_unregister(local_data->event_id);
> +}
> +
> +static void sse_test_inject_local(unsigned long event_id)
> +{
> +	int cpu;
> +	unsigned long ret;
> +	struct sse_local_data local_data;
> +	struct sse_local_per_cpu *cpu_arg;
> +	volatile struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
> +
> +	report_prefix_push("local_dispatch");
> +	local_data.event_id = event_id;
> +
> +	for_each_online_cpu(cpu) {
> +		cpu_arg = calloc(1, sizeof(struct sse_handler_arg));
> +
> +		cpu_arg->args.stack = sse_alloc_stack();
> +		cpu_arg->args.handler = sse_foreign_cpu_handler;
> +		cpu_arg->args.handler_data = (void *)&test_arg;
> +		local_data.cpu_args[cpu] = cpu_arg;
> +	}
> +
> +	on_cpus(sse_register_enable_local, &local_data);
> +	for_each_online_cpu(cpu) {
> +		if (local_data.cpu_args[cpu]->ret)
> +			report_abort("CPU failed to register/enable SSE event");
> +
> +		test_arg.expected_cpu = cpu;
> +		/* For test_arg content to be visible for other CPUs */
> +		smp_wmb();
> +		ret = sse_event_inject(event_id, cpus[cpu].hartid);
> +		if (ret)
> +			report_abort("CPU failed to register/enable SSE event");
> +
> +		while (!test_arg.done) {
> +			/* For test_arg update to be visible */
> +			smp_rmb();
> +		}
> +
> +		test_arg.done = false;
> +	}
> +
> +	on_cpus(sse_disable_unregister_local, &local_data);
> +	for_each_online_cpu(cpu) {
> +		if (local_data.cpu_args[cpu]->ret)
> +			report_abort("CPU failed to disable/unregister SSE event");
> +	}
> +
> +	for_each_online_cpu(cpu) {
> +		cpu_arg = local_data.cpu_args[cpu];
> +
> +		sse_free_stack(cpu_arg->args.stack);
> +	}
> +
> +	report_pass("local event dispatch on all CPUs");
> +	report_prefix_pop();
> +
> +}
> +
> +static void sse_test_inject_global(unsigned long event_id)
> +{
> +	unsigned long ret;
> +	unsigned int cpu;
> +	struct sse_handler_arg args;
> +	volatile struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
> +	enum sbi_sse_state state;
> +
> +	args.handler = sse_foreign_cpu_handler;
> +	args.handler_data = (void *)&test_arg;
> +	args.stack = sse_alloc_stack();
> +
> +	report_prefix_push("global_dispatch");
> +
> +	ret = sse_event_register(event_id, &args);
> +	if (ret)
> +		goto done;
> +
> +	for_each_online_cpu(cpu) {
> +		test_arg.expected_cpu = cpu;
> +		/* For test_arg content to be visible for other CPUs */
> +		smp_wmb();
> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART, cpu);
> +		if (ret) {
> +			report_fail("Failed to set preferred hart");
> +			goto done;
> +		}
> +
> +		ret = sse_event_enable(event_id);
> +		if (ret) {
> +			report_fail("Failed to enable SSE event");
> +			goto done;
> +		}
> +
> +		ret = sse_event_inject(event_id, cpu);
> +		if (ret) {
> +			report_fail("Failed to inject event");
> +			goto done;
> +		}
> +
> +		while (!test_arg.done) {
> +			/* For shared test_arg structure */
> +			smp_rmb();
> +		}
> +
> +		test_arg.done = false;
> +
> +		/* Wait for event to be in ENABLED state */
> +		do {
> +			ret = sse_get_state(event_id, &state);
> +			if (ret) {
> +				report_fail("Failed to get event state");
> +				goto done;
> +			}
> +		} while (state != SBI_SSE_STATE_ENABLED);
> +
> +		ret = sse_event_disable(event_id);
> +		if (ret) {
> +			report_fail("Failed to disable SSE event");
> +			goto done;
> +		}
> +
> +		report_pass("Global event on CPU %d", cpu);
> +	}
> +
> +done:
> +	ret = sse_event_unregister(event_id);
> +	if (ret)
> +		report_fail("Failed to unregister event");
> +
> +	sse_free_stack(args.stack);
> +	report_prefix_pop();
> +}
> +
> +struct priority_test_arg {
> +	unsigned long evt;
> +	bool called;
> +	u32 prio;
> +	struct priority_test_arg *next_evt_arg;
> +	void (*check_func)(struct priority_test_arg *arg);
> +};
> +
> +static void sse_hi_priority_test_handler(void *arg, struct pt_regs *regs,
> +					 unsigned int hartid)
> +{
> +	struct priority_test_arg *targ = arg;
> +	struct priority_test_arg *next = targ->next_evt_arg;
> +
> +	targ->called = 1;
> +	if (next) {
> +		sse_event_inject(next->evt, current_thread_info()->hartid);
> +		if (sse_event_pending(next->evt))
> +			report_fail("Higher priority event is pending");
> +		if (!next->called)
> +			report_fail("Higher priority event was not handled");
> +	}
> +}
> +
> +static void sse_low_priority_test_handler(void *arg, struct pt_regs *regs,
> +					  unsigned int hartid)
> +{
> +	struct priority_test_arg *targ = arg;
> +	struct priority_test_arg *next = targ->next_evt_arg;
> +
> +	targ->called = 1;
> +
> +	if (next) {
> +		sse_event_inject(next->evt, current_thread_info()->hartid);
> +
> +		if (!sse_event_pending(next->evt))
> +			report_fail("Lower priority event is pending");
> +
> +		if (next->called)
> +			report_fail("Lower priority event %s was handle before %s",
> +			      sse_evt_name(next->evt), sse_evt_name(targ->evt));
> +	}
> +}
> +
> +static void sse_test_injection_priority_arg(struct priority_test_arg *in_args,
> +					    unsigned int in_args_size,
> +					    sse_handler_fn handler,
> +					    const char *test_name)
> +{
> +	unsigned int i;
> +	int ret;
> +	unsigned long event_id;
> +	struct priority_test_arg *arg;
> +	unsigned int args_size = 0;
> +	struct sse_handler_arg event_args[in_args_size];
> +	struct priority_test_arg *args[in_args_size];
> +	void *stack;
> +	struct sse_handler_arg *event_arg;
> +
> +	report_prefix_push(test_name);
> +
> +	for (i = 0; i < in_args_size; i++) {
> +		arg = &in_args[i];
> +		event_id = arg->evt;
> +		if (!sse_evt_can_inject(event_id))
> +			continue;
> +
> +		args[args_size] = arg;
> +		args_size++;
> +	}
> +
> +	if (!args_size) {
> +		report_skip("No event injectable");
> +		report_prefix_pop();
> +		goto skip;
> +	}
> +
> +	for (i = 0; i < args_size; i++) {
> +		arg = args[i];
> +		event_id = arg->evt;
> +		stack = sse_alloc_stack();
> +
> +		event_arg = &event_args[i];
> +		event_arg->handler = handler;
> +		event_arg->handler_data = (void *)arg;
> +		event_arg->stack = stack;
> +
> +		if (i < (args_size - 1))
> +			arg->next_evt_arg = args[i + 1];
> +		else
> +			arg->next_evt_arg = NULL;
> +
> +		/* Be sure global events are targeting the current hart */
> +		sse_global_event_set_current_hart(event_id);
> +
> +		sse_event_register(event_id, event_arg);
> +		sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, arg->prio);
> +		sse_event_enable(event_id);
> +	}
> +
> +	/* Inject first event */
> +	ret = sse_event_inject(args[0]->evt, current_thread_info()->hartid);
> +	report(ret == SBI_SUCCESS, "SSE injection no error");
> +
> +	for (i = 0; i < args_size; i++) {
> +		arg = args[i];
> +		event_id = arg->evt;
> +
> +		if (!arg->called)
> +			report_fail("Event %s handler called", sse_evt_name(arg->evt));
> +
> +		sse_event_disable(event_id);
> +		sse_event_unregister(event_id);
> +
> +		event_arg = &event_args[i];
> +		sse_free_stack(event_arg->stack);
> +	}
> +
> +skip:
> +	report_prefix_pop();
> +}
> +
> +static struct priority_test_arg hi_prio_args[] = {
> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
> +};
> +
> +static struct priority_test_arg low_prio_args[] = {
> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
> +};
> +
> +static struct priority_test_arg prio_args[] = {
> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 5},
> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 15},
> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 25},
> +};
> +
> +static struct priority_test_arg same_prio_args[] = {
> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 0},
> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 10},
> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 10},
> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
> +};
> +
> +static void sse_test_injection_priority(void)
> +{
> +	report_prefix_push("prio");
> +
> +	sse_test_injection_priority_arg(hi_prio_args, ARRAY_SIZE(hi_prio_args),
> +					sse_hi_priority_test_handler, "high");
> +
> +	sse_test_injection_priority_arg(low_prio_args, ARRAY_SIZE(low_prio_args),
> +					sse_low_priority_test_handler, "low");
> +
> +	sse_test_injection_priority_arg(prio_args, ARRAY_SIZE(prio_args),
> +					sse_low_priority_test_handler, "changed");
> +
> +	sse_test_injection_priority_arg(same_prio_args, ARRAY_SIZE(same_prio_args),
> +					sse_low_priority_test_handler, "same_prio_args");
> +
> +	report_prefix_pop();
> +}
> +
> +static bool sse_can_inject(unsigned long event_id)
> +{
> +	int ret;
> +	unsigned long status;
> +
> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
> +	report(ret == 0, "SSE get attr status no error");
> +	if (ret)
> +		return 0;
> +
> +	return !!(status & BIT(SBI_SSE_ATTR_STATUS_INJECT_OFFSET));
> +}
> +
> +static void boot_secondary(void *data)
> +{
> +}
> +
> +void check_sse(void)
> +{
> +	unsigned long i, event;
> +
> +	/*
> +	 * Dummy wakeup of all processors since some of them will be targeted
> +	 * by global events without going through the wakeup call.
> +	 */
> +	on_cpus(boot_secondary, NULL);
> +	report_prefix_push("sse");
> +
> +	if (!sbi_probe(SBI_EXT_SSE)) {
> +		report_skip("SSE extension not available");
> +		report_prefix_pop();
> +		return;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
> +		event = sse_event_infos[i].event_id;
> +		report_prefix_push(sse_event_infos[i].name);
> +		if (!sse_can_inject(event)) {
> +			report_skip("Event does not support injection");
> +			report_prefix_pop();
> +			continue;
> +		} else {
> +			sse_event_infos[i].can_inject = true;
> +		}
> +		sse_test_attr(event);
> +		sse_test_register_error(event);
> +		sse_test_inject_simple(event);
> +		if (sse_event_is_global(event))
> +			sse_test_inject_global(event);
> +		else
> +			sse_test_inject_local(event);
> +
> +		report_prefix_pop();
> +	}
> +
> +	sse_test_injection_priority();
> +
> +	report_prefix_pop();
> +}
> diff --git a/riscv/sbi.c b/riscv/sbi.c
> index 6f4ddaf1..96dfb2ca 100644
> --- a/riscv/sbi.c
> +++ b/riscv/sbi.c
> @@ -1451,6 +1451,7 @@ int main(int argc, char **argv)
>  	check_hsm();
>  	check_dbcn();
>  	check_susp();
> +	check_sse();
>  
>  	return report_summary();
>  }
> diff --git a/riscv/unittests.cfg b/riscv/unittests.cfg
> index 2eb760ec..ddd05de7 100644
> --- a/riscv/unittests.cfg
> +++ b/riscv/unittests.cfg
> @@ -18,3 +18,7 @@ groups = selftest
>  file = sbi.flat
>  smp = $MAX_SMP
>  groups = sbi
> +
> +[sbi_sse]
> +file = sbi_sse.flat
> +groups = sbi
> -- 
> 2.45.2
>

I only had time for quick skim, but it looks pretty good.

Thanks,
drew

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

* Re: [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling
  2024-11-22 16:20   ` Andrew Jones
@ 2024-11-25  8:46     ` Clément Léger
  2024-11-25  9:38       ` Andrew Jones
  0 siblings, 1 reply; 12+ messages in thread
From: Clément Léger @ 2024-11-25  8:46 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra



On 22/11/2024 17:20, Andrew Jones wrote:
> On Fri, Nov 22, 2024 at 03:04:56PM +0100, Clément Léger wrote:
>> Add a SSE entry assembly code to handle SSE events. Events should be
>> registered with a struct sse_handler_arg containing a correct stack and
>> handler function.
>>
>> Signed-off-by: Clément Léger <cleger@rivosinc.com>
>> ---
>>  riscv/Makefile          |   1 +
>>  lib/riscv/asm/sse.h     |  16 +++++++
>>  lib/riscv/sse-entry.S   | 100 ++++++++++++++++++++++++++++++++++++++++
> 
> Let's just add the entry function to riscv/sbi-asm.S and the
> sse_handler_arg struct definition to riscv/sbi-tests.h

Hi drew,

I need to have some offset generated using asm-offsets.c which is in
lib/riscv. If I move the sse_handler_arg in riscv/sbi-tests.h, that will
be really off to include that file in the lib/riscv/asm-offsets.c.
Except if you have some other solution.

> 
>>  lib/riscv/asm-offsets.c |   9 ++++
>>  4 files changed, 126 insertions(+)
>>  create mode 100644 lib/riscv/asm/sse.h
>>  create mode 100644 lib/riscv/sse-entry.S
>>
>> diff --git a/riscv/Makefile b/riscv/Makefile
>> index 28b04156..e50621ad 100644
>> --- a/riscv/Makefile
>> +++ b/riscv/Makefile
>> @@ -39,6 +39,7 @@ cflatobjs += lib/riscv/sbi.o
>>  cflatobjs += lib/riscv/setjmp.o
>>  cflatobjs += lib/riscv/setup.o
>>  cflatobjs += lib/riscv/smp.o
>> +cflatobjs += lib/riscv/sse-entry.o
>>  cflatobjs += lib/riscv/stack.o
>>  cflatobjs += lib/riscv/timer.o
>>  ifeq ($(ARCH),riscv32)
>> diff --git a/lib/riscv/asm/sse.h b/lib/riscv/asm/sse.h
>> new file mode 100644
>> index 00000000..557f6680
>> --- /dev/null
>> +++ b/lib/riscv/asm/sse.h
>> @@ -0,0 +1,16 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +#ifndef _ASMRISCV_SSE_H_
>> +#define _ASMRISCV_SSE_H_
>> +
>> +typedef void (*sse_handler_fn)(void *data, struct pt_regs *regs, unsigned int hartid);
>> +
>> +struct sse_handler_arg {
>> +	unsigned long reg_tmp;
>> +	sse_handler_fn handler;
>> +	void *handler_data;
>> +	void *stack;
>> +};
>> +
>> +extern void sse_entry(void);
>> +
>> +#endif /* _ASMRISCV_SSE_H_ */
>> diff --git a/lib/riscv/sse-entry.S b/lib/riscv/sse-entry.S
>> new file mode 100644
>> index 00000000..bedc47e9
>> --- /dev/null
>> +++ b/lib/riscv/sse-entry.S
>> @@ -0,0 +1,100 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * SBI SSE entry code
>> + *
>> + * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
>> + */
>> +#include <asm/asm.h>
>> +#include <asm/asm-offsets.h>
>> +#include <asm/csr.h>
>> +
>> +.global sse_entry
>> +sse_entry:
>> +	/* Save stack temporarily */
>> +	REG_S sp, SSE_REG_TMP(a6)
>> +	/* Set entry stack */
>> +	REG_L sp, SSE_HANDLER_STACK(a6)
>> +
>> +	addi sp, sp, -(PT_SIZE)
>> +	REG_S ra, PT_RA(sp)
>> +	REG_S s0, PT_S0(sp)
>> +	REG_S s1, PT_S1(sp)
>> +	REG_S s2, PT_S2(sp)
>> +	REG_S s3, PT_S3(sp)
>> +	REG_S s4, PT_S4(sp)
>> +	REG_S s5, PT_S5(sp)
>> +	REG_S s6, PT_S6(sp)
>> +	REG_S s7, PT_S7(sp)
>> +	REG_S s8, PT_S8(sp)
>> +	REG_S s9, PT_S9(sp)
>> +	REG_S s10, PT_S10(sp)
>> +	REG_S s11, PT_S11(sp)
>> +	REG_S tp, PT_TP(sp)
>> +	REG_S t0, PT_T0(sp)
>> +	REG_S t1, PT_T1(sp)
>> +	REG_S t2, PT_T2(sp)
>> +	REG_S t3, PT_T3(sp)
>> +	REG_S t4, PT_T4(sp)
>> +	REG_S t5, PT_T5(sp)
>> +	REG_S t6, PT_T6(sp)
>> +	REG_S gp, PT_GP(sp)
>> +	REG_S a0, PT_A0(sp)
>> +	REG_S a1, PT_A1(sp)
>> +	REG_S a2, PT_A2(sp)
>> +	REG_S a3, PT_A3(sp)
>> +	REG_S a4, PT_A4(sp)
>> +	REG_S a5, PT_A5(sp)
>> +	csrr a1, CSR_SEPC
>> +	REG_S a1, PT_EPC(sp)
>> +	csrr a2, CSR_SSTATUS
>> +	REG_S a2, PT_STATUS(sp)
>> +
>> +	REG_L a0, SSE_REG_TMP(a6)
>> +	REG_S a0, PT_SP(sp)
>> +
>> +	REG_L t0, SSE_HANDLER(a6)
>> +	REG_L a0, SSE_HANDLER_DATA(a6)
>> +	move a1, sp
>> +	move a2, a7
> 
> nit: prefer 'mv'
> 
>> +	jalr t0
>> +
>> +
>> +	REG_L a1, PT_EPC(sp)
>> +	REG_L a2, PT_STATUS(sp)
>> +	csrw CSR_SEPC, a1
>> +	csrw CSR_SSTATUS, a2
>> +
>> +	REG_L ra, PT_RA(sp)
>> +	REG_L s0, PT_S0(sp)
>> +	REG_L s1, PT_S1(sp)
>> +	REG_L s2, PT_S2(sp)
>> +	REG_L s3, PT_S3(sp)
>> +	REG_L s4, PT_S4(sp)
>> +	REG_L s5, PT_S5(sp)
>> +	REG_L s6, PT_S6(sp)
>> +	REG_L s7, PT_S7(sp)
>> +	REG_L s8, PT_S8(sp)
>> +	REG_L s9, PT_S9(sp)
>> +	REG_L s10, PT_S10(sp)
>> +	REG_L s11, PT_S11(sp)
>> +	REG_L tp, PT_TP(sp)
>> +	REG_L t0, PT_T0(sp)
>> +	REG_L t1, PT_T1(sp)
>> +	REG_L t2, PT_T2(sp)
>> +	REG_L t3, PT_T3(sp)
>> +	REG_L t4, PT_T4(sp)
>> +	REG_L t5, PT_T5(sp)
>> +	REG_L t6, PT_T6(sp)
>> +	REG_L gp, PT_GP(sp)
>> +	REG_L a0, PT_A0(sp)
>> +	REG_L a1, PT_A1(sp)
>> +	REG_L a2, PT_A2(sp)
>> +	REG_L a3, PT_A3(sp)
>> +	REG_L a4, PT_A4(sp)
>> +	REG_L a5, PT_A5(sp)
>> +
>> +	REG_L sp, PT_SP(sp)
>> +
>> +	li a7, ASM_SBI_EXT_SSE
>> +	li a6, ASM_SBI_EXT_SSE_COMPLETE
>> +	ecall
>> diff --git a/lib/riscv/asm-offsets.c b/lib/riscv/asm-offsets.c
>> index 6c511c14..b3465eeb 100644
>> --- a/lib/riscv/asm-offsets.c
>> +++ b/lib/riscv/asm-offsets.c
>> @@ -3,7 +3,9 @@
>>  #include <elf.h>
>>  #include <asm/processor.h>
>>  #include <asm/ptrace.h>
>> +#include <asm/sbi.h>
>>  #include <asm/smp.h>
>> +#include <asm/sse.h>
>>  
>>  int main(void)
>>  {
>> @@ -63,5 +65,12 @@ int main(void)
>>  	OFFSET(THREAD_INFO_HARTID, thread_info, hartid);
>>  	DEFINE(THREAD_INFO_SIZE, sizeof(struct thread_info));
>>  
>> +	OFFSET(SSE_REG_TMP, sse_handler_arg, reg_tmp);
>> +	OFFSET(SSE_HANDLER, sse_handler_arg, handler);
>> +	OFFSET(SSE_HANDLER_DATA, sse_handler_arg, handler_data);
>> +	OFFSET(SSE_HANDLER_STACK, sse_handler_arg, stack);
>> +	DEFINE(ASM_SBI_EXT_SSE, SBI_EXT_SSE);
>> +	DEFINE(ASM_SBI_EXT_SSE_COMPLETE, SBI_EXT_SSE_COMPLETE);
> 
> sbi_hsm_check just uses the numbers and adds comments. We could instead
> move the extension IDs from an enum to defines outside the
> #ifndef __ASSEMBLY__ and function IDs can also be defines accessible
> by assembly.

With respect to my previous comment, that applies for the IDs but I
still need the offsets to be generated. Any idea ?

Thanks, Clément

> 
>> +
>>  	return 0;
>>  }
>> -- 
>> 2.45.2
>>
> 
> Thanks,
> drew


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

* Re: [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests
  2024-11-22 16:34   ` Andrew Jones
@ 2024-11-25  8:55     ` Clément Léger
  2024-11-25  9:40       ` Andrew Jones
  0 siblings, 1 reply; 12+ messages in thread
From: Clément Léger @ 2024-11-25  8:55 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra



On 22/11/2024 17:34, Andrew Jones wrote:
> On Fri, Nov 22, 2024 at 03:04:57PM +0100, Clément Léger wrote:
>> Add SBI SSE extension tests for the following features:
>> - Test attributes errors (invalid values, RO, etc)
>> - Registration errors
>> - Simple events (register, enable, inject)
>> - Events with different priorities
>> - Global events dispatch on different harts
>> - Local events on all harts
>>
>> Signed-off-by: Clément Léger <cleger@rivosinc.com>
>> ---
>>  riscv/Makefile      |   1 +
>>  lib/riscv/asm/csr.h |   2 +
>>  riscv/sbi-tests.h   |   4 +
>>  riscv/sbi-sse.c     | 981 ++++++++++++++++++++++++++++++++++++++++++++
>>  riscv/sbi.c         |   1 +
>>  riscv/unittests.cfg |   4 +
>>  6 files changed, 993 insertions(+)
>>  create mode 100644 riscv/sbi-sse.c
>>
>> diff --git a/riscv/Makefile b/riscv/Makefile
>> index e50621ad..768e1c25 100644
>> --- a/riscv/Makefile
>> +++ b/riscv/Makefile
>> @@ -46,6 +46,7 @@ ifeq ($(ARCH),riscv32)
>>  cflatobjs += lib/ldiv32.o
>>  endif
>>  cflatobjs += riscv/sbi-asm.o
>> +cflatobjs += riscv/sbi-sse.o
> 
> We should figure out how to only link these files into
> riscv/sbi.{flat,efi}

Hey drew, thansk for the review.

I'll check if this is possible to do that yeah.

> 
>>  
>>  ########################################
>>  
>> diff --git a/lib/riscv/asm/csr.h b/lib/riscv/asm/csr.h
>> index 16f5ddd7..06831380 100644
>> --- a/lib/riscv/asm/csr.h
>> +++ b/lib/riscv/asm/csr.h
>> @@ -21,6 +21,8 @@
>>  /* Exception cause high bit - is an interrupt if set */
>>  #define CAUSE_IRQ_FLAG		(_AC(1, UL) << (__riscv_xlen - 1))
>>  
>> +#define SSTATUS_SPP		_AC(0x00000100, UL) /* Previously Supervisor */
>> +
>>  /* Exception causes */
>>  #define EXC_INST_MISALIGNED	0
>>  #define EXC_INST_ACCESS		1
>> diff --git a/riscv/sbi-tests.h b/riscv/sbi-tests.h
>> index ce129968..2115acc6 100644
>> --- a/riscv/sbi-tests.h
>> +++ b/riscv/sbi-tests.h
>> @@ -33,4 +33,8 @@
>>  #define SBI_SUSP_TEST_HARTID	(1 << 2)
>>  #define SBI_SUSP_TEST_MASK	7
>>  
>> +#ifndef __ASSEMBLY__
>> +void check_sse(void);
> 
> We can just put this in riscv/sbi.c

sbi.c is already almost 1500 lines long, adding SSE would make it a 2500
lines files. IMHO, it would be nice to keep it separated to keep it
clean. But if you really have a strong opinion to incorporate that in
sbi.c, I'll do that.

Thanks,

Clément

> 
>> +#endif /* !__ASSEMBLY__ */
>> +
>>  #endif /* _RISCV_SBI_TESTS_H_ */
>> diff --git a/riscv/sbi-sse.c b/riscv/sbi-sse.c
>> new file mode 100644
>> index 00000000..16eb0575
>> --- /dev/null
>> +++ b/riscv/sbi-sse.c
>> @@ -0,0 +1,981 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * SBI SSE testsuite
>> + *
>> + * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
>> + */
>> +#include <libcflat.h>
>> +#include <alloc_page.h>
>> +#include <bitops.h>
>> +#include <cpumask.h>
>> +#include <libcflat.h>
> 
> libcflat.h is repeated and let's alphabetize all these
> 
>> +#include <on-cpus.h>
>> +#include <alloc.h>
>> +
>> +#include <asm/barrier.h>
>> +#include <asm/page.h>
>> +#include <asm/processor.h>
>> +#include <asm/sbi.h>
>> +#include <asm/setup.h>
>> +#include <asm/sse.h>
>> +
>> +#include "sbi-tests.h"
>> +
>> +#define SSE_STACK_SIZE	PAGE_SIZE
>> +
>> +struct sse_event_info {
>> +	unsigned long event_id;
>> +	const char *name;
>> +	bool can_inject;
>> +};
>> +
>> +static struct sse_event_info sse_event_infos[] = {
>> +	{
>> +		.event_id = SBI_SSE_EVENT_LOCAL_RAS,
>> +		.name = "local_ras",
>> +	},
>> +	{
>> +		.event_id = SBI_SSE_EVENT_GLOBAL_RAS,
>> +		.name = "global_ras",
>> +	},
>> +	{
>> +		.event_id = SBI_SSE_EVENT_LOCAL_PMU,
>> +		.name = "local_pmu",
>> +	},
>> +	{
>> +		.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,
>> +		.name = "local_software",
>> +	},
>> +	{
>> +		.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,
>> +		.name = "global_software",
>> +	},
>> +};
>> +
>> +static const char *const attr_names[] = {
>> +	[SBI_SSE_ATTR_STATUS] = "status",
>> +	[SBI_SSE_ATTR_PRIO] = "prio",
>> +	[SBI_SSE_ATTR_CONFIG] = "config",
>> +	[SBI_SSE_ATTR_PREFERRED_HART] = "preferred_hart",
>> +	[SBI_SSE_ATTR_ENTRY_PC] = "entry_pc",
>> +	[SBI_SSE_ATTR_ENTRY_ARG] = "entry_arg",
>> +	[SBI_SSE_ATTR_INTERRUPTED_SEPC] = "interrupted_pc",
>> +	[SBI_SSE_ATTR_INTERRUPTED_FLAGS] = "interrupted_flags",
>> +	[SBI_SSE_ATTR_INTERRUPTED_A6] = "interrupted_a6",
>> +	[SBI_SSE_ATTR_INTERRUPTED_A7] = "interrupted_a7",
>> +};
>> +
>> +static const unsigned long ro_attrs[] = {
>> +	SBI_SSE_ATTR_STATUS,
>> +	SBI_SSE_ATTR_ENTRY_PC,
>> +	SBI_SSE_ATTR_ENTRY_ARG,
>> +};
>> +
>> +static const unsigned long interrupted_attrs[] = {
>> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS,
>> +	SBI_SSE_ATTR_INTERRUPTED_SEPC,
>> +	SBI_SSE_ATTR_INTERRUPTED_A6,
>> +	SBI_SSE_ATTR_INTERRUPTED_A7,
>> +};
>> +
>> +static const unsigned long interrupted_flags[] = {
>> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPP,
>> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_STATUS_SPIE,
>> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV,
>> +	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP,
>> +};
>> +
>> +static struct sse_event_info *sse_evt_get_infos(unsigned long event_id)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
>> +		if (sse_event_infos[i].event_id == event_id)
>> +			return &sse_event_infos[i];
>> +	}
>> +
>> +	assert_msg(false, "Invalid event id: %ld", event_id);
>> +}
>> +
>> +static const char *sse_evt_name(unsigned long event_id)
>> +{
>> +	struct sse_event_info *infos = sse_evt_get_infos(event_id);
>> +
>> +	return infos->name;
>> +}
>> +
>> +static bool sse_evt_can_inject(unsigned long event_id)
>> +{
>> +	struct sse_event_info *infos = sse_evt_get_infos(event_id);
>> +
>> +	return infos->can_inject;
>> +}
>> +
>> +static bool sse_event_is_global(unsigned long event_id)
>> +{
>> +	return !!(event_id & SBI_SSE_EVENT_GLOBAL_BIT);
>> +}
>> +
>> +static struct sbiret sse_event_get_attr_raw(unsigned long event_id,
>> +					    unsigned long base_attr_id,
>> +					    unsigned long attr_count,
>> +					    unsigned long phys_lo,
>> +					    unsigned long phys_hi)
>> +{
>> +	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_READ_ATTR, event_id,
>> +			base_attr_id, attr_count, phys_lo, phys_hi, 0);
>> +}
>> +
>> +static unsigned long sse_event_get_attrs(unsigned long event_id, unsigned long attr_id,
>> +					 unsigned long *values, unsigned int attr_count)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sse_event_get_attr_raw(event_id, attr_id, attr_count, (unsigned long)values, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +static unsigned long sse_event_get_attr(unsigned long event_id, unsigned long attr_id,
>> +					unsigned long *value)
>> +{
>> +	return sse_event_get_attrs(event_id, attr_id, value, 1);
>> +}
>> +
>> +static struct sbiret sse_event_set_attr_raw(unsigned long event_id, unsigned long base_attr_id,
>> +					    unsigned long attr_count, unsigned long phys_lo,
>> +					    unsigned long phys_hi)
>> +{
>> +	return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_WRITE_ATTR, event_id, base_attr_id, attr_count,
>> +			 phys_lo, phys_hi, 0);
>> +}
>> +
>> +static unsigned long sse_event_set_attr(unsigned long event_id, unsigned long attr_id,
>> +					unsigned long value)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sse_event_set_attr_raw(event_id, attr_id, 1, (unsigned long)&value, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +static unsigned long sse_event_register_raw(unsigned long event_id, void *entry_pc, void *entry_arg)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, (unsigned long)entry_pc,
>> +			(unsigned long)entry_arg, 0, 0, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +static unsigned long sse_event_register(unsigned long event_id, struct sse_handler_arg *arg)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, (unsigned long)sse_entry,
>> +			(unsigned long)arg, 0, 0, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +static unsigned long sse_event_unregister(unsigned long event_id)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_UNREGISTER, event_id, 0, 0, 0, 0, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +static unsigned long sse_event_enable(unsigned long event_id)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_ENABLE, event_id, 0, 0, 0, 0, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +static unsigned long sse_event_inject(unsigned long event_id, unsigned long hart_id)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_INJECT, event_id, hart_id, 0, 0, 0, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +static unsigned long sse_event_disable(unsigned long event_id)
>> +{
>> +	struct sbiret ret;
>> +
>> +	ret = sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_DISABLE, event_id, 0, 0, 0, 0, 0);
>> +
>> +	return ret.error;
>> +}
>> +
>> +
>> +static int sse_get_state(unsigned long event_id, enum sbi_sse_state *state)
>> +{
>> +	int ret;
>> +	unsigned long status;
>> +
>> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
>> +	if (ret) {
>> +		report_fail("Failed to get SSE event status");
>> +		return -1;
>> +	}
>> +
>> +	*state = status & SBI_SSE_ATTR_STATUS_STATE_MASK;
>> +
>> +	return 0;
>> +}
>> +
>> +static void sse_global_event_set_current_hart(unsigned long event_id)
>> +{
>> +	int ret;
>> +
>> +	if (!sse_event_is_global(event_id))
>> +		return;
>> +
>> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
>> +				 current_thread_info()->hartid);
>> +	if (ret)
>> +		report_abort("set preferred hart failure");
>> +}
>> +
>> +static int sse_check_state(unsigned long event_id, unsigned long expected_state)
>> +{
>> +	int ret;
>> +	enum sbi_sse_state state;
>> +
>> +	ret = sse_get_state(event_id, &state);
>> +	if (ret)
>> +		return 1;
>> +	report(state == expected_state, "SSE event status == %ld", expected_state);
>> +
>> +	return state != expected_state;
>> +}
>> +
>> +static bool sse_event_pending(unsigned long event_id)
>> +{
>> +	int ret;
>> +	unsigned long status;
>> +
>> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
>> +	if (ret) {
>> +		report_fail("Failed to get SSE event status");
>> +		return false;
>> +	}
>> +
>> +	return !!(status & BIT(SBI_SSE_ATTR_STATUS_PENDING_OFFSET));
>> +}
>> +
>> +static void *sse_alloc_stack(void)
>> +{
>> +	return (alloc_page() + PAGE_SIZE);
>> +}
>> +
>> +static void sse_free_stack(void *stack)
>> +{
>> +	free_page(stack - PAGE_SIZE);
>> +}
> 
> I guess this should be SSE_STACK_SIZE, otherwise that define can be
> removed
> 
>> +
>> +static void sse_test_attr(unsigned long event_id)
>> +{
>> +	unsigned long ret, value = 0;
>> +	unsigned long values[ARRAY_SIZE(ro_attrs)];
>> +	struct sbiret sret;
>> +	unsigned int i;
>> +
>> +	report_prefix_push("attrs");
>> +
>> +	for (i = 0; i < ARRAY_SIZE(ro_attrs); i++) {
>> +		ret = sse_event_set_attr(event_id, ro_attrs[i], value);
>> +		report(ret == SBI_ERR_BAD_RANGE, "RO attribute %s not writable",
>> +		       attr_names[ro_attrs[i]]);
>> +	}
>> +
>> +	for (i = SBI_SSE_ATTR_STATUS; i <= SBI_SSE_ATTR_INTERRUPTED_A7; i++) {
>> +		ret = sse_event_get_attr(event_id, i, &value);
>> +		report(ret == SBI_SUCCESS, "Read single attribute %s", attr_names[i]);
>> +		/* Preferred Hart reset value is defined by SBI vendor and status injectable bit
>> +		 * also depends on the SBI implementation
>> +		 */
>> +		if (i != SBI_SSE_ATTR_STATUS && i != SBI_SSE_ATTR_PREFERRED_HART)
>> +			report(value == 0, "Attribute %s reset value is 0", attr_names[i]);
>> +	}
>> +
>> +	ret = sse_event_get_attrs(event_id, SBI_SSE_ATTR_STATUS, values,
>> +				  SBI_SSE_ATTR_INTERRUPTED_A7 - SBI_SSE_ATTR_STATUS);
>> +	report(ret == SBI_SUCCESS, "Read multiple attributes");
>> +
>> +#if __riscv_xlen > 32
>> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, 0xFFFFFFFFUL + 1UL);
>> +	report(ret == SBI_ERR_INVALID_PARAM, "Write prio > 0xFFFFFFFF error");
>> +#endif
>> +
>> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, ~SBI_SSE_ATTR_CONFIG_ONESHOT);
>> +	report(ret == SBI_ERR_INVALID_PARAM, "Write invalid config error");
>> +
>> +	if (sse_event_is_global(event_id)) {
>> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART, 0xFFFFFFFFUL);
>> +		report(ret == SBI_ERR_INVALID_PARAM, "Set invalid hart id error");
>> +	} else {
>> +		/* Set Hart on local event -> RO */
>> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
>> +					 current_thread_info()->hartid);
>> +		report(ret == SBI_ERR_BAD_RANGE, "Set hart id on local event error");
>> +	}
>> +
>> +	/* Set/get flags, sepc, a6, a7 */
>> +	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
>> +		ret = sse_event_get_attr(event_id, interrupted_attrs[i], &value);
>> +		report(ret == 0, "Get interrupted %s no error", attr_names[interrupted_attrs[i]]);
>> +
>> +		/* 0x1 is a valid value for all the interrupted attributes */
>> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_FLAGS, 0x1);
>> +		report(ret == SBI_ERR_INVALID_STATE, "Set interrupted flags invalid state error");
>> +	}
>> +
>> +	/* Attr_count == 0 */
>> +	sret = sse_event_get_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 0, (unsigned long) &value, 0);
>> +	report(sret.error == SBI_ERR_INVALID_PARAM, "Read attribute attr_count == 0 error");
>> +
>> +	sret = sse_event_set_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 0, (unsigned long) &value, 0);
>> +	report(sret.error == SBI_ERR_INVALID_PARAM, "Write attribute attr_count == 0 error");
>> +
>> +	/* Invalid attribute id */
>> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, &value);
>> +	report(ret == SBI_ERR_BAD_RANGE, "Read invalid attribute error");
>> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, value);
>> +	report(ret == SBI_ERR_BAD_RANGE, "Write invalid attribute error");
>> +
>> +	/* Misaligned phys address */
>> +	sret = sse_event_get_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 1,
>> +				      ((unsigned long) &value | 0x1), 0);
>> +	report(sret.error == SBI_ERR_INVALID_ADDRESS, "Read attribute with invalid address error");
>> +	sret = sse_event_set_attr_raw(event_id, SBI_SSE_ATTR_STATUS, 1,
>> +				      ((unsigned long) &value | 0x1), 0);
>> +	report(sret.error == SBI_ERR_INVALID_ADDRESS, "Write attribute with invalid address error");
>> +
>> +	report_prefix_pop();
>> +}
>> +
>> +static void sse_test_register_error(unsigned long event_id)
>> +{
>> +	unsigned long ret;
>> +
>> +	report_prefix_push("register");
>> +
>> +	ret = sse_event_unregister(event_id);
>> +	report(ret == SBI_ERR_INVALID_STATE, "SSE unregister non registered event");
>> +
>> +	ret = sse_event_register_raw(event_id, (void *) 0x1, NULL);
>> +	report(ret == SBI_ERR_INVALID_PARAM, "SSE register misaligned entry");
>> +
>> +	ret = sse_event_register_raw(event_id, (void *) sse_entry, NULL);
>> +	report(ret == SBI_SUCCESS, "SSE register ok");
>> +	if (ret)
>> +		goto done;
>> +
>> +	ret = sse_event_register_raw(event_id, (void *) sse_entry, NULL);
>> +	report(ret == SBI_ERR_INVALID_STATE, "SSE register twice failure");
>> +	if (!ret)
>> +		goto done;
>> +
>> +	ret = sse_event_unregister(event_id);
>> +	report(ret == SBI_SUCCESS, "SSE unregister ok");
>> +
>> +done:
>> +	report_prefix_pop();
>> +}
>> +
>> +struct sse_simple_test_arg {
>> +	bool done;
>> +	unsigned long event_id;
>> +};
>> +
>> +static void sse_simple_handler(void *data, struct pt_regs *regs, unsigned int hartid)
>> +{
>> +	volatile struct sse_simple_test_arg *arg = data;
>> +	int ret, i;
>> +	const char *attr_name;
>> +	unsigned long event_id = arg->event_id, value, prev_value, flags, attr;
>> +	const unsigned long regs_len = (SBI_SSE_ATTR_INTERRUPTED_A7 - SBI_SSE_ATTR_INTERRUPTED_A6) +
>> +				       1;
>> +	unsigned long interrupted_state[regs_len];
>> +
>> +	if ((regs->status & SSTATUS_SPP) == 0)
>> +		report_fail("Interrupted S-mode");
>> +
>> +	if (hartid != current_thread_info()->hartid)
>> +		report_fail("Hartid correctly passed");
>> +
>> +	sse_check_state(event_id, SBI_SSE_STATE_RUNNING);
>> +	if (sse_event_pending(event_id))
>> +		report_fail("Event is not pending");
>> +
>> +	/* Set a6, a7, sepc, flags while running */
>> +	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
>> +		attr = interrupted_attrs[i];
>> +		attr_name = attr_names[attr];
>> +
>> +		ret = sse_event_get_attr(event_id, attr, &prev_value);
>> +		report(ret == 0, "Get attr %s no error", attr_name);
>> +
>> +		/* We test SBI_SSE_ATTR_INTERRUPTED_FLAGS below with specific flag values */
>> +		if (attr == SBI_SSE_ATTR_INTERRUPTED_FLAGS)
>> +			continue;
>> +
>> +		ret = sse_event_set_attr(event_id, attr, 0xDEADBEEF + i);
>> +		report(ret == 0, "Set attr %s invalid state no error", attr_name);
>> +
>> +		ret = sse_event_get_attr(event_id, attr, &value);
>> +		report(ret == 0, "Get attr %s modified value no error", attr_name);
>> +		report(value == 0xDEADBEEF + i, "Get attr %s modified value ok", attr_name);
>> +
>> +		ret = sse_event_set_attr(event_id, attr, prev_value);
>> +		report(ret == 0, "Restore attr %s value no error", attr_name);
>> +	}
>> +
>> +	/* Test all flags allowed for SBI_SSE_ATTR_INTERRUPTED_FLAGS*/
>> +	attr = SBI_SSE_ATTR_INTERRUPTED_FLAGS;
>> +	attr_name = attr_names[attr];
>> +	ret = sse_event_get_attr(event_id, attr, &prev_value);
>> +	report(ret == 0, "Get attr %s no error", attr_name);
>> +
>> +	for (i = 0; i < ARRAY_SIZE(interrupted_flags); i++) {
>> +		flags = interrupted_flags[i];
>> +		ret = sse_event_set_attr(event_id, attr, flags);
>> +		report(ret == 0, "Set interrupted %s value no error", attr_name);
>> +		ret = sse_event_get_attr(event_id, attr, &value);
>> +		report(value == flags, "Get attr %s modified value ok", attr_name);
>> +	}
>> +
>> +	ret = sse_event_set_attr(event_id, attr, prev_value);
>> +		report(ret == 0, "Restore attr %s value no error", attr_name);
>> +
>> +	/* Try to change HARTID/Priority while running */
>> +	if (sse_event_is_global(event_id)) {
>> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART,
>> +					 current_thread_info()->hartid);
>> +		report(ret == SBI_ERR_INVALID_STATE, "Set hart id while running error");
>> +	}
>> +
>> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, 0);
>> +	report(ret == SBI_ERR_INVALID_STATE, "Set priority while running error");
>> +
>> +	ret = sse_event_get_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_A6, interrupted_state,
>> +				  regs_len);
>> +	report(ret == SBI_SUCCESS, "Read interrupted context from SSE handler ok");
>> +	if (interrupted_state[0] != SBI_EXT_SSE_INJECT)
>> +		report_fail("Interrupted state a6 check ok");
>> +	if (interrupted_state[1] != SBI_EXT_SSE)
>> +		report_fail("Interrupted state a7 check ok");
>> +
>> +	arg->done = true;
>> +}
>> +
>> +static void sse_test_inject_simple(unsigned long event_id)
>> +{
>> +	unsigned long ret;
>> +	struct sse_handler_arg args;
>> +	volatile struct sse_simple_test_arg test_arg = {.event_id = event_id};
>> +
>> +	args.handler = sse_simple_handler;
>> +	args.handler_data = (void *) &test_arg;
>> +	args.stack = sse_alloc_stack();
>> +
>> +	report_prefix_push("simple");
>> +
>> +	ret = sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
>> +	if (ret)
>> +		goto done;
>> +
>> +	ret = sse_event_register(event_id, &args);
>> +	report(ret == SBI_SUCCESS, "SSE register no error");
>> +	if (ret)
>> +		goto done;
>> +
>> +	ret = sse_check_state(event_id, SBI_SSE_STATE_REGISTERED);
>> +	if (ret)
>> +		goto done;
>> +
>> +	/* Be sure global events are targeting the current hart */
>> +	sse_global_event_set_current_hart(event_id);
>> +
>> +	ret = sse_event_enable(event_id);
>> +	report(ret == SBI_SUCCESS, "SSE enable no error");
>> +	if (ret)
>> +		goto done;
>> +
>> +	ret = sse_check_state(event_id, SBI_SSE_STATE_ENABLED);
>> +	if (ret)
>> +		goto done;
>> +
>> +	ret = sse_event_inject(event_id, current_thread_info()->hartid);
>> +	report(ret == SBI_SUCCESS, "SSE injection no error");
>> +	if (ret)
>> +		goto done;
>> +
>> +	barrier();
>> +	report(test_arg.done == 1, "SSE event handled ok");
>> +	test_arg.done = 0;
>> +
>> +	/* Set as oneshot and verify it is disabled */
>> +	ret = sse_event_disable(event_id);
>> +	report(ret == 0, "Disable event ok");
>> +	ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, SBI_SSE_ATTR_CONFIG_ONESHOT);
>> +	report(ret == 0, "Set event attribute as ONESHOT");
>> +	ret = sse_event_enable(event_id);
>> +	report(ret == 0, "Enable event ok");
>> +
>> +	ret = sse_event_inject(event_id, current_thread_info()->hartid);
>> +	report(ret == SBI_SUCCESS, "SSE injection 2 no error");
>> +	if (ret)
>> +		goto done;
>> +
>> +	barrier();
>> +	report(test_arg.done == 1, "SSE event handled ok");
>> +	test_arg.done = 0;
>> +
>> +	ret = sse_check_state(event_id, SBI_SSE_STATE_REGISTERED);
>> +	if (ret)
>> +		goto done;
>> +
>> +	/* Clear ONESHOT FLAG */
>> +	sse_event_set_attr(event_id, SBI_SSE_ATTR_CONFIG, 0);
>> +
>> +	ret = sse_event_unregister(event_id);
>> +	report(ret == SBI_SUCCESS, "SSE unregister no error");
>> +	if (ret)
>> +		goto done;
>> +
>> +	sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
>> +
>> +done:
>> +	sse_free_stack(args.stack);
>> +	report_prefix_pop();
>> +}
>> +
>> +struct sse_foreign_cpu_test_arg {
>> +	bool done;
>> +	unsigned int expected_cpu;
>> +	unsigned long event_id;
>> +};
>> +
>> +static void sse_foreign_cpu_handler(void *data, struct pt_regs *regs, unsigned int hartid)
>> +{
>> +	volatile struct sse_foreign_cpu_test_arg *arg = data;
>> +
>> +	/* For arg content to be visible */
>> +	smp_rmb();
>> +	if (arg->expected_cpu != current_thread_info()->cpu)
>> +		report_fail("Received event on CPU (%d), expected CPU (%d)",
>> +			    current_thread_info()->cpu, arg->expected_cpu);
>> +
>> +	arg->done = true;
>> +	/* For arg update to be visible for other CPUs */
>> +	smp_wmb();
>> +}
>> +
>> +struct sse_local_per_cpu {
>> +	struct sse_handler_arg args;
>> +	unsigned long ret;
>> +};
>> +
>> +struct sse_local_data {
>> +	unsigned long event_id;
>> +	struct sse_local_per_cpu *cpu_args[NR_CPUS];
>> +};
>> +
>> +static void sse_register_enable_local(void *data)
>> +{
>> +	struct sse_local_data *local_data = data;
>> +	struct sse_local_per_cpu *cpu_arg = local_data->cpu_args[current_thread_info()->cpu];
>> +
>> +	cpu_arg->ret = sse_event_register(local_data->event_id, &cpu_arg->args);
>> +	if (cpu_arg->ret)
>> +		return;
>> +
>> +	cpu_arg->ret = sse_event_enable(local_data->event_id);
>> +}
>> +
>> +static void sse_disable_unregister_local(void *data)
>> +{
>> +	struct sse_local_data *local_data = data;
>> +	struct sse_local_per_cpu *cpu_arg = local_data->cpu_args[current_thread_info()->cpu];
>> +
>> +	cpu_arg->ret = sse_event_disable(local_data->event_id);
>> +	if (cpu_arg->ret)
>> +		return;
>> +
>> +	cpu_arg->ret = sse_event_unregister(local_data->event_id);
>> +}
>> +
>> +static void sse_test_inject_local(unsigned long event_id)
>> +{
>> +	int cpu;
>> +	unsigned long ret;
>> +	struct sse_local_data local_data;
>> +	struct sse_local_per_cpu *cpu_arg;
>> +	volatile struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
>> +
>> +	report_prefix_push("local_dispatch");
>> +	local_data.event_id = event_id;
>> +
>> +	for_each_online_cpu(cpu) {
>> +		cpu_arg = calloc(1, sizeof(struct sse_handler_arg));
>> +
>> +		cpu_arg->args.stack = sse_alloc_stack();
>> +		cpu_arg->args.handler = sse_foreign_cpu_handler;
>> +		cpu_arg->args.handler_data = (void *)&test_arg;
>> +		local_data.cpu_args[cpu] = cpu_arg;
>> +	}
>> +
>> +	on_cpus(sse_register_enable_local, &local_data);
>> +	for_each_online_cpu(cpu) {
>> +		if (local_data.cpu_args[cpu]->ret)
>> +			report_abort("CPU failed to register/enable SSE event");
>> +
>> +		test_arg.expected_cpu = cpu;
>> +		/* For test_arg content to be visible for other CPUs */
>> +		smp_wmb();
>> +		ret = sse_event_inject(event_id, cpus[cpu].hartid);
>> +		if (ret)
>> +			report_abort("CPU failed to register/enable SSE event");
>> +
>> +		while (!test_arg.done) {
>> +			/* For test_arg update to be visible */
>> +			smp_rmb();
>> +		}
>> +
>> +		test_arg.done = false;
>> +	}
>> +
>> +	on_cpus(sse_disable_unregister_local, &local_data);
>> +	for_each_online_cpu(cpu) {
>> +		if (local_data.cpu_args[cpu]->ret)
>> +			report_abort("CPU failed to disable/unregister SSE event");
>> +	}
>> +
>> +	for_each_online_cpu(cpu) {
>> +		cpu_arg = local_data.cpu_args[cpu];
>> +
>> +		sse_free_stack(cpu_arg->args.stack);
>> +	}
>> +
>> +	report_pass("local event dispatch on all CPUs");
>> +	report_prefix_pop();
>> +
>> +}
>> +
>> +static void sse_test_inject_global(unsigned long event_id)
>> +{
>> +	unsigned long ret;
>> +	unsigned int cpu;
>> +	struct sse_handler_arg args;
>> +	volatile struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
>> +	enum sbi_sse_state state;
>> +
>> +	args.handler = sse_foreign_cpu_handler;
>> +	args.handler_data = (void *)&test_arg;
>> +	args.stack = sse_alloc_stack();
>> +
>> +	report_prefix_push("global_dispatch");
>> +
>> +	ret = sse_event_register(event_id, &args);
>> +	if (ret)
>> +		goto done;
>> +
>> +	for_each_online_cpu(cpu) {
>> +		test_arg.expected_cpu = cpu;
>> +		/* For test_arg content to be visible for other CPUs */
>> +		smp_wmb();
>> +		ret = sse_event_set_attr(event_id, SBI_SSE_ATTR_PREFERRED_HART, cpu);
>> +		if (ret) {
>> +			report_fail("Failed to set preferred hart");
>> +			goto done;
>> +		}
>> +
>> +		ret = sse_event_enable(event_id);
>> +		if (ret) {
>> +			report_fail("Failed to enable SSE event");
>> +			goto done;
>> +		}
>> +
>> +		ret = sse_event_inject(event_id, cpu);
>> +		if (ret) {
>> +			report_fail("Failed to inject event");
>> +			goto done;
>> +		}
>> +
>> +		while (!test_arg.done) {
>> +			/* For shared test_arg structure */
>> +			smp_rmb();
>> +		}
>> +
>> +		test_arg.done = false;
>> +
>> +		/* Wait for event to be in ENABLED state */
>> +		do {
>> +			ret = sse_get_state(event_id, &state);
>> +			if (ret) {
>> +				report_fail("Failed to get event state");
>> +				goto done;
>> +			}
>> +		} while (state != SBI_SSE_STATE_ENABLED);
>> +
>> +		ret = sse_event_disable(event_id);
>> +		if (ret) {
>> +			report_fail("Failed to disable SSE event");
>> +			goto done;
>> +		}
>> +
>> +		report_pass("Global event on CPU %d", cpu);
>> +	}
>> +
>> +done:
>> +	ret = sse_event_unregister(event_id);
>> +	if (ret)
>> +		report_fail("Failed to unregister event");
>> +
>> +	sse_free_stack(args.stack);
>> +	report_prefix_pop();
>> +}
>> +
>> +struct priority_test_arg {
>> +	unsigned long evt;
>> +	bool called;
>> +	u32 prio;
>> +	struct priority_test_arg *next_evt_arg;
>> +	void (*check_func)(struct priority_test_arg *arg);
>> +};
>> +
>> +static void sse_hi_priority_test_handler(void *arg, struct pt_regs *regs,
>> +					 unsigned int hartid)
>> +{
>> +	struct priority_test_arg *targ = arg;
>> +	struct priority_test_arg *next = targ->next_evt_arg;
>> +
>> +	targ->called = 1;
>> +	if (next) {
>> +		sse_event_inject(next->evt, current_thread_info()->hartid);
>> +		if (sse_event_pending(next->evt))
>> +			report_fail("Higher priority event is pending");
>> +		if (!next->called)
>> +			report_fail("Higher priority event was not handled");
>> +	}
>> +}
>> +
>> +static void sse_low_priority_test_handler(void *arg, struct pt_regs *regs,
>> +					  unsigned int hartid)
>> +{
>> +	struct priority_test_arg *targ = arg;
>> +	struct priority_test_arg *next = targ->next_evt_arg;
>> +
>> +	targ->called = 1;
>> +
>> +	if (next) {
>> +		sse_event_inject(next->evt, current_thread_info()->hartid);
>> +
>> +		if (!sse_event_pending(next->evt))
>> +			report_fail("Lower priority event is pending");
>> +
>> +		if (next->called)
>> +			report_fail("Lower priority event %s was handle before %s",
>> +			      sse_evt_name(next->evt), sse_evt_name(targ->evt));
>> +	}
>> +}
>> +
>> +static void sse_test_injection_priority_arg(struct priority_test_arg *in_args,
>> +					    unsigned int in_args_size,
>> +					    sse_handler_fn handler,
>> +					    const char *test_name)
>> +{
>> +	unsigned int i;
>> +	int ret;
>> +	unsigned long event_id;
>> +	struct priority_test_arg *arg;
>> +	unsigned int args_size = 0;
>> +	struct sse_handler_arg event_args[in_args_size];
>> +	struct priority_test_arg *args[in_args_size];
>> +	void *stack;
>> +	struct sse_handler_arg *event_arg;
>> +
>> +	report_prefix_push(test_name);
>> +
>> +	for (i = 0; i < in_args_size; i++) {
>> +		arg = &in_args[i];
>> +		event_id = arg->evt;
>> +		if (!sse_evt_can_inject(event_id))
>> +			continue;
>> +
>> +		args[args_size] = arg;
>> +		args_size++;
>> +	}
>> +
>> +	if (!args_size) {
>> +		report_skip("No event injectable");
>> +		report_prefix_pop();
>> +		goto skip;
>> +	}
>> +
>> +	for (i = 0; i < args_size; i++) {
>> +		arg = args[i];
>> +		event_id = arg->evt;
>> +		stack = sse_alloc_stack();
>> +
>> +		event_arg = &event_args[i];
>> +		event_arg->handler = handler;
>> +		event_arg->handler_data = (void *)arg;
>> +		event_arg->stack = stack;
>> +
>> +		if (i < (args_size - 1))
>> +			arg->next_evt_arg = args[i + 1];
>> +		else
>> +			arg->next_evt_arg = NULL;
>> +
>> +		/* Be sure global events are targeting the current hart */
>> +		sse_global_event_set_current_hart(event_id);
>> +
>> +		sse_event_register(event_id, event_arg);
>> +		sse_event_set_attr(event_id, SBI_SSE_ATTR_PRIO, arg->prio);
>> +		sse_event_enable(event_id);
>> +	}
>> +
>> +	/* Inject first event */
>> +	ret = sse_event_inject(args[0]->evt, current_thread_info()->hartid);
>> +	report(ret == SBI_SUCCESS, "SSE injection no error");
>> +
>> +	for (i = 0; i < args_size; i++) {
>> +		arg = args[i];
>> +		event_id = arg->evt;
>> +
>> +		if (!arg->called)
>> +			report_fail("Event %s handler called", sse_evt_name(arg->evt));
>> +
>> +		sse_event_disable(event_id);
>> +		sse_event_unregister(event_id);
>> +
>> +		event_arg = &event_args[i];
>> +		sse_free_stack(event_arg->stack);
>> +	}
>> +
>> +skip:
>> +	report_prefix_pop();
>> +}
>> +
>> +static struct priority_test_arg hi_prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
>> +};
>> +
>> +static struct priority_test_arg low_prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
>> +};
>> +
>> +static struct priority_test_arg prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 5},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 15},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 25},
>> +};
>> +
>> +static struct priority_test_arg same_prio_args[] = {
>> +	{.evt = SBI_SSE_EVENT_LOCAL_PMU, .prio = 0},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_RAS, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_LOCAL_SOFTWARE, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_SOFTWARE, .prio = 10},
>> +	{.evt = SBI_SSE_EVENT_GLOBAL_RAS, .prio = 20},
>> +};
>> +
>> +static void sse_test_injection_priority(void)
>> +{
>> +	report_prefix_push("prio");
>> +
>> +	sse_test_injection_priority_arg(hi_prio_args, ARRAY_SIZE(hi_prio_args),
>> +					sse_hi_priority_test_handler, "high");
>> +
>> +	sse_test_injection_priority_arg(low_prio_args, ARRAY_SIZE(low_prio_args),
>> +					sse_low_priority_test_handler, "low");
>> +
>> +	sse_test_injection_priority_arg(prio_args, ARRAY_SIZE(prio_args),
>> +					sse_low_priority_test_handler, "changed");
>> +
>> +	sse_test_injection_priority_arg(same_prio_args, ARRAY_SIZE(same_prio_args),
>> +					sse_low_priority_test_handler, "same_prio_args");
>> +
>> +	report_prefix_pop();
>> +}
>> +
>> +static bool sse_can_inject(unsigned long event_id)
>> +{
>> +	int ret;
>> +	unsigned long status;
>> +
>> +	ret = sse_event_get_attr(event_id, SBI_SSE_ATTR_STATUS, &status);
>> +	report(ret == 0, "SSE get attr status no error");
>> +	if (ret)
>> +		return 0;
>> +
>> +	return !!(status & BIT(SBI_SSE_ATTR_STATUS_INJECT_OFFSET));
>> +}
>> +
>> +static void boot_secondary(void *data)
>> +{
>> +}
>> +
>> +void check_sse(void)
>> +{
>> +	unsigned long i, event;
>> +
>> +	/*
>> +	 * Dummy wakeup of all processors since some of them will be targeted
>> +	 * by global events without going through the wakeup call.
>> +	 */
>> +	on_cpus(boot_secondary, NULL);
>> +	report_prefix_push("sse");
>> +
>> +	if (!sbi_probe(SBI_EXT_SSE)) {
>> +		report_skip("SSE extension not available");
>> +		report_prefix_pop();
>> +		return;
>> +	}
>> +
>> +	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
>> +		event = sse_event_infos[i].event_id;
>> +		report_prefix_push(sse_event_infos[i].name);
>> +		if (!sse_can_inject(event)) {
>> +			report_skip("Event does not support injection");
>> +			report_prefix_pop();
>> +			continue;
>> +		} else {
>> +			sse_event_infos[i].can_inject = true;
>> +		}
>> +		sse_test_attr(event);
>> +		sse_test_register_error(event);
>> +		sse_test_inject_simple(event);
>> +		if (sse_event_is_global(event))
>> +			sse_test_inject_global(event);
>> +		else
>> +			sse_test_inject_local(event);
>> +
>> +		report_prefix_pop();
>> +	}
>> +
>> +	sse_test_injection_priority();
>> +
>> +	report_prefix_pop();
>> +}
>> diff --git a/riscv/sbi.c b/riscv/sbi.c
>> index 6f4ddaf1..96dfb2ca 100644
>> --- a/riscv/sbi.c
>> +++ b/riscv/sbi.c
>> @@ -1451,6 +1451,7 @@ int main(int argc, char **argv)
>>  	check_hsm();
>>  	check_dbcn();
>>  	check_susp();
>> +	check_sse();
>>  
>>  	return report_summary();
>>  }
>> diff --git a/riscv/unittests.cfg b/riscv/unittests.cfg
>> index 2eb760ec..ddd05de7 100644
>> --- a/riscv/unittests.cfg
>> +++ b/riscv/unittests.cfg
>> @@ -18,3 +18,7 @@ groups = selftest
>>  file = sbi.flat
>>  smp = $MAX_SMP
>>  groups = sbi
>> +
>> +[sbi_sse]
>> +file = sbi_sse.flat
>> +groups = sbi
>> -- 
>> 2.45.2
>>
> 
> I only had time for quick skim, but it looks pretty good.
> 
> Thanks,
> drew


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

* Re: [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling
  2024-11-25  8:46     ` Clément Léger
@ 2024-11-25  9:38       ` Andrew Jones
  2024-11-25 10:29         ` Clément Léger
  0 siblings, 1 reply; 12+ messages in thread
From: Andrew Jones @ 2024-11-25  9:38 UTC (permalink / raw)
  To: Clément Léger
  Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra

On Mon, Nov 25, 2024 at 09:46:48AM +0100, Clément Léger wrote:
> 
> 
> On 22/11/2024 17:20, Andrew Jones wrote:
> > On Fri, Nov 22, 2024 at 03:04:56PM +0100, Clément Léger wrote:
> >> Add a SSE entry assembly code to handle SSE events. Events should be
> >> registered with a struct sse_handler_arg containing a correct stack and
> >> handler function.
> >>
> >> Signed-off-by: Clément Léger <cleger@rivosinc.com>
> >> ---
> >>  riscv/Makefile          |   1 +
> >>  lib/riscv/asm/sse.h     |  16 +++++++
> >>  lib/riscv/sse-entry.S   | 100 ++++++++++++++++++++++++++++++++++++++++
> > 
> > Let's just add the entry function to riscv/sbi-asm.S and the
> > sse_handler_arg struct definition to riscv/sbi-tests.h
> 
> Hi drew,
> 
> I need to have some offset generated using asm-offsets.c which is in
> lib/riscv. If I move the sse_handler_arg in riscv/sbi-tests.h, that will
> be really off to include that file in the lib/riscv/asm-offsets.c.

That's true, but it's also not great to put a test-specific definition of
an arg structure in lib code. It seems like we'll eventually want a neater
solution to this, though, since using asm-offsets for test-specific
structures makes sense. However, we could put it off for now, since each
member of the structure that SSE tests need is the same size,
sizeof(long), so we can do the same thing that HSM and SUSP do, which is
to define some indices and access with ASMARR().

> Except if you have some other solution.

ASMARR(), even though I'm not a huge fan of that approach either...

> 
> > 
> >>  lib/riscv/asm-offsets.c |   9 ++++
> >>  4 files changed, 126 insertions(+)
> >>  create mode 100644 lib/riscv/asm/sse.h
> >>  create mode 100644 lib/riscv/sse-entry.S
> >>
> >> diff --git a/riscv/Makefile b/riscv/Makefile
> >> index 28b04156..e50621ad 100644
> >> --- a/riscv/Makefile
> >> +++ b/riscv/Makefile
> >> @@ -39,6 +39,7 @@ cflatobjs += lib/riscv/sbi.o
> >>  cflatobjs += lib/riscv/setjmp.o
> >>  cflatobjs += lib/riscv/setup.o
> >>  cflatobjs += lib/riscv/smp.o
> >> +cflatobjs += lib/riscv/sse-entry.o
> >>  cflatobjs += lib/riscv/stack.o
> >>  cflatobjs += lib/riscv/timer.o
> >>  ifeq ($(ARCH),riscv32)
> >> diff --git a/lib/riscv/asm/sse.h b/lib/riscv/asm/sse.h
> >> new file mode 100644
> >> index 00000000..557f6680
> >> --- /dev/null
> >> +++ b/lib/riscv/asm/sse.h
> >> @@ -0,0 +1,16 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-only */
> >> +#ifndef _ASMRISCV_SSE_H_
> >> +#define _ASMRISCV_SSE_H_
> >> +
> >> +typedef void (*sse_handler_fn)(void *data, struct pt_regs *regs, unsigned int hartid);
> >> +
> >> +struct sse_handler_arg {
> >> +	unsigned long reg_tmp;
> >> +	sse_handler_fn handler;
> >> +	void *handler_data;
> >> +	void *stack;
> >> +};
> >> +
> >> +extern void sse_entry(void);
> >> +
> >> +#endif /* _ASMRISCV_SSE_H_ */
> >> diff --git a/lib/riscv/sse-entry.S b/lib/riscv/sse-entry.S
> >> new file mode 100644
> >> index 00000000..bedc47e9
> >> --- /dev/null
> >> +++ b/lib/riscv/sse-entry.S
> >> @@ -0,0 +1,100 @@
> >> +/* SPDX-License-Identifier: GPL-2.0-only */
> >> +/*
> >> + * SBI SSE entry code
> >> + *
> >> + * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
> >> + */
> >> +#include <asm/asm.h>
> >> +#include <asm/asm-offsets.h>
> >> +#include <asm/csr.h>
> >> +
> >> +.global sse_entry
> >> +sse_entry:
> >> +	/* Save stack temporarily */
> >> +	REG_S sp, SSE_REG_TMP(a6)

While thinking about the asm-offsets issue, I took a closer look at this
and noticed that this should be a7, since ENTRY_ARG is specified to be
set to A7. It looks like we have A6 (hartid) and A7 (arg) swapped. The
opensbi implementation also has them swapped, allowing this test to pass.
Both need to be fixed.

Thanks,
drew

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

* Re: [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests
  2024-11-25  8:55     ` Clément Léger
@ 2024-11-25  9:40       ` Andrew Jones
  0 siblings, 0 replies; 12+ messages in thread
From: Andrew Jones @ 2024-11-25  9:40 UTC (permalink / raw)
  To: Clément Léger
  Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra

On Mon, Nov 25, 2024 at 09:55:47AM +0100, Clément Léger wrote:
> 
> 
> On 22/11/2024 17:34, Andrew Jones wrote:
> > On Fri, Nov 22, 2024 at 03:04:57PM +0100, Clément Léger wrote:
> >> Add SBI SSE extension tests for the following features:
> >> - Test attributes errors (invalid values, RO, etc)
> >> - Registration errors
> >> - Simple events (register, enable, inject)
> >> - Events with different priorities
> >> - Global events dispatch on different harts
> >> - Local events on all harts
> >>
> >> Signed-off-by: Clément Léger <cleger@rivosinc.com>
> >> ---
> >>  riscv/Makefile      |   1 +
> >>  lib/riscv/asm/csr.h |   2 +
> >>  riscv/sbi-tests.h   |   4 +
> >>  riscv/sbi-sse.c     | 981 ++++++++++++++++++++++++++++++++++++++++++++
> >>  riscv/sbi.c         |   1 +
> >>  riscv/unittests.cfg |   4 +
> >>  6 files changed, 993 insertions(+)
> >>  create mode 100644 riscv/sbi-sse.c
> >>
> >> diff --git a/riscv/Makefile b/riscv/Makefile
> >> index e50621ad..768e1c25 100644
> >> --- a/riscv/Makefile
> >> +++ b/riscv/Makefile
> >> @@ -46,6 +46,7 @@ ifeq ($(ARCH),riscv32)
> >>  cflatobjs += lib/ldiv32.o
> >>  endif
> >>  cflatobjs += riscv/sbi-asm.o
> >> +cflatobjs += riscv/sbi-sse.o
> > 
> > We should figure out how to only link these files into
> > riscv/sbi.{flat,efi}
> 
> Hey drew, thansk for the review.
> 
> I'll check if this is possible to do that yeah.
> 
> > 
> >>  
> >>  ########################################
> >>  
> >> diff --git a/lib/riscv/asm/csr.h b/lib/riscv/asm/csr.h
> >> index 16f5ddd7..06831380 100644
> >> --- a/lib/riscv/asm/csr.h
> >> +++ b/lib/riscv/asm/csr.h
> >> @@ -21,6 +21,8 @@
> >>  /* Exception cause high bit - is an interrupt if set */
> >>  #define CAUSE_IRQ_FLAG		(_AC(1, UL) << (__riscv_xlen - 1))
> >>  
> >> +#define SSTATUS_SPP		_AC(0x00000100, UL) /* Previously Supervisor */
> >> +
> >>  /* Exception causes */
> >>  #define EXC_INST_MISALIGNED	0
> >>  #define EXC_INST_ACCESS		1
> >> diff --git a/riscv/sbi-tests.h b/riscv/sbi-tests.h
> >> index ce129968..2115acc6 100644
> >> --- a/riscv/sbi-tests.h
> >> +++ b/riscv/sbi-tests.h
> >> @@ -33,4 +33,8 @@
> >>  #define SBI_SUSP_TEST_HARTID	(1 << 2)
> >>  #define SBI_SUSP_TEST_MASK	7
> >>  
> >> +#ifndef __ASSEMBLY__
> >> +void check_sse(void);
> > 
> > We can just put this in riscv/sbi.c
> 
> sbi.c is already almost 1500 lines long, adding SSE would make it a 2500
> lines files. IMHO, it would be nice to keep it separated to keep it
> clean. But if you really have a strong opinion to incorporate that in
> sbi.c, I'll do that.

I meant just this prototype, 'void check_sse(void);'

Thanks,
drew

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

* Re: [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling
  2024-11-25  9:38       ` Andrew Jones
@ 2024-11-25 10:29         ` Clément Léger
  0 siblings, 0 replies; 12+ messages in thread
From: Clément Léger @ 2024-11-25 10:29 UTC (permalink / raw)
  To: Andrew Jones; +Cc: kvm, kvm-riscv, Andrew Jones, Anup Patel, Atish Patra



On 25/11/2024 10:38, Andrew Jones wrote:
> On Mon, Nov 25, 2024 at 09:46:48AM +0100, Clément Léger wrote:
>>
>>
>> On 22/11/2024 17:20, Andrew Jones wrote:
>>> On Fri, Nov 22, 2024 at 03:04:56PM +0100, Clément Léger wrote:
>>>> Add a SSE entry assembly code to handle SSE events. Events should be
>>>> registered with a struct sse_handler_arg containing a correct stack and
>>>> handler function.
>>>>
>>>> Signed-off-by: Clément Léger <cleger@rivosinc.com>
>>>> ---
>>>>  riscv/Makefile          |   1 +
>>>>  lib/riscv/asm/sse.h     |  16 +++++++
>>>>  lib/riscv/sse-entry.S   | 100 ++++++++++++++++++++++++++++++++++++++++
>>>
>>> Let's just add the entry function to riscv/sbi-asm.S and the
>>> sse_handler_arg struct definition to riscv/sbi-tests.h
>>
>> Hi drew,
>>
>> I need to have some offset generated using asm-offsets.c which is in
>> lib/riscv. If I move the sse_handler_arg in riscv/sbi-tests.h, that will
>> be really off to include that file in the lib/riscv/asm-offsets.c.
> 
> That's true, but it's also not great to put a test-specific definition of
> an arg structure in lib code. It seems like we'll eventually want a neater
> solution to this, though, since using asm-offsets for test-specific
> structures makes sense. However, we could put it off for now, since each
> member of the structure that SSE tests need is the same size,
> sizeof(long), so we can do the same thing that HSM and SUSP do, which is
> to define some indices and access with ASMARR().

The struct sse_handler_arg isn't actually test-specific as well as the
assembly code. Test data are actually specified in the handler and
handler_data field of the sse_handler_arg. But maybe the part that uses
the sse_handler data should actually be hidden from the test layer. I
can add some function to wrap that.

> 
>> Except if you have some other solution.
> 
> ASMARR(), even though I'm not a huge fan of that approach either...
> 
>>
>>>
>>>>  lib/riscv/asm-offsets.c |   9 ++++
>>>>  4 files changed, 126 insertions(+)
>>>>  create mode 100644 lib/riscv/asm/sse.h
>>>>  create mode 100644 lib/riscv/sse-entry.S
>>>>
>>>> diff --git a/riscv/Makefile b/riscv/Makefile
>>>> index 28b04156..e50621ad 100644
>>>> --- a/riscv/Makefile
>>>> +++ b/riscv/Makefile
>>>> @@ -39,6 +39,7 @@ cflatobjs += lib/riscv/sbi.o
>>>>  cflatobjs += lib/riscv/setjmp.o
>>>>  cflatobjs += lib/riscv/setup.o
>>>>  cflatobjs += lib/riscv/smp.o
>>>> +cflatobjs += lib/riscv/sse-entry.o
>>>>  cflatobjs += lib/riscv/stack.o
>>>>  cflatobjs += lib/riscv/timer.o
>>>>  ifeq ($(ARCH),riscv32)
>>>> diff --git a/lib/riscv/asm/sse.h b/lib/riscv/asm/sse.h
>>>> new file mode 100644
>>>> index 00000000..557f6680
>>>> --- /dev/null
>>>> +++ b/lib/riscv/asm/sse.h
>>>> @@ -0,0 +1,16 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>>> +#ifndef _ASMRISCV_SSE_H_
>>>> +#define _ASMRISCV_SSE_H_
>>>> +
>>>> +typedef void (*sse_handler_fn)(void *data, struct pt_regs *regs, unsigned int hartid);
>>>> +
>>>> +struct sse_handler_arg {
>>>> +	unsigned long reg_tmp;
>>>> +	sse_handler_fn handler;
>>>> +	void *handler_data;
>>>> +	void *stack;
>>>> +};
>>>> +
>>>> +extern void sse_entry(void);
>>>> +
>>>> +#endif /* _ASMRISCV_SSE_H_ */
>>>> diff --git a/lib/riscv/sse-entry.S b/lib/riscv/sse-entry.S
>>>> new file mode 100644
>>>> index 00000000..bedc47e9
>>>> --- /dev/null
>>>> +++ b/lib/riscv/sse-entry.S
>>>> @@ -0,0 +1,100 @@
>>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>>> +/*
>>>> + * SBI SSE entry code
>>>> + *
>>>> + * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
>>>> + */
>>>> +#include <asm/asm.h>
>>>> +#include <asm/asm-offsets.h>
>>>> +#include <asm/csr.h>
>>>> +
>>>> +.global sse_entry
>>>> +sse_entry:
>>>> +	/* Save stack temporarily */
>>>> +	REG_S sp, SSE_REG_TMP(a6)
> 
> While thinking about the asm-offsets issue, I took a closer look at this
> and noticed that this should be a7, since ENTRY_ARG is specified to be
> set to A7. It looks like we have A6 (hartid) and A7 (arg) swapped. The
> opensbi implementation also has them swapped, allowing this test to pass.
> Both need to be fixed.

Ouch, nice catch, I'll fix that up.

> 
> Thanks,
> drew


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

end of thread, other threads:[~2024-11-25 10:29 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-22 14:04 [kvm-unit-tests PATCH v2 0/3] riscv: add SBI SSE extension tests Clément Léger
2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 1/3] riscv: lib: Add SBI SSE extension definitions Clément Léger
2024-11-22 16:05   ` Andrew Jones
2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 2/3] riscv: lib: Add SSE assembly entry handling Clément Léger
2024-11-22 16:20   ` Andrew Jones
2024-11-25  8:46     ` Clément Léger
2024-11-25  9:38       ` Andrew Jones
2024-11-25 10:29         ` Clément Léger
2024-11-22 14:04 ` [kvm-unit-tests PATCH v2 3/3] riscv: sbi: Add SSE extension tests Clément Léger
2024-11-22 16:34   ` Andrew Jones
2024-11-25  8:55     ` Clément Léger
2024-11-25  9:40       ` Andrew Jones

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