xref: /kvm-unit-tests/riscv/sbi-sse.c (revision 7536c9add92b9942c94b52e4dc973f247f5367a1)
1*7536c9adSClément Léger // SPDX-License-Identifier: GPL-2.0-only
2*7536c9adSClément Léger /*
3*7536c9adSClément Léger  * SBI SSE testsuite
4*7536c9adSClément Léger  *
5*7536c9adSClément Léger  * Copyright (C) 2025, Rivos Inc., Clément Léger <cleger@rivosinc.com>
6*7536c9adSClément Léger  */
7*7536c9adSClément Léger #include <alloc.h>
8*7536c9adSClément Léger #include <alloc_page.h>
9*7536c9adSClément Léger #include <bitops.h>
10*7536c9adSClément Léger #include <cpumask.h>
11*7536c9adSClément Léger #include <libcflat.h>
12*7536c9adSClément Léger #include <on-cpus.h>
13*7536c9adSClément Léger #include <stdlib.h>
14*7536c9adSClément Léger 
15*7536c9adSClément Léger #include <asm/barrier.h>
16*7536c9adSClément Léger #include <asm/delay.h>
17*7536c9adSClément Léger #include <asm/io.h>
18*7536c9adSClément Léger #include <asm/page.h>
19*7536c9adSClément Léger #include <asm/processor.h>
20*7536c9adSClément Léger #include <asm/sbi.h>
21*7536c9adSClément Léger #include <asm/setup.h>
22*7536c9adSClément Léger #include <asm/timer.h>
23*7536c9adSClément Léger 
24*7536c9adSClément Léger #include "sbi-tests.h"
25*7536c9adSClément Léger 
26*7536c9adSClément Léger #define SSE_STACK_SIZE	PAGE_SIZE
27*7536c9adSClément Léger 
28*7536c9adSClément Léger struct sse_event_info {
29*7536c9adSClément Léger 	uint32_t event_id;
30*7536c9adSClément Léger 	const char *name;
31*7536c9adSClément Léger 	bool can_inject;
32*7536c9adSClément Léger };
33*7536c9adSClément Léger 
34*7536c9adSClément Léger static struct sse_event_info sse_event_infos[] = {
35*7536c9adSClément Léger 	{
36*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS,
37*7536c9adSClément Léger 		.name = "local_high_prio_ras",
38*7536c9adSClément Léger 	},
39*7536c9adSClément Léger 	{
40*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP,
41*7536c9adSClément Léger 		.name = "double_trap",
42*7536c9adSClément Léger 	},
43*7536c9adSClément Léger 	{
44*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS,
45*7536c9adSClément Léger 		.name = "global_high_prio_ras",
46*7536c9adSClément Léger 	},
47*7536c9adSClément Léger 	{
48*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW,
49*7536c9adSClément Léger 		.name = "local_pmu_overflow",
50*7536c9adSClément Léger 	},
51*7536c9adSClément Léger 	{
52*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS,
53*7536c9adSClément Léger 		.name = "local_low_prio_ras",
54*7536c9adSClément Léger 	},
55*7536c9adSClément Léger 	{
56*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS,
57*7536c9adSClément Léger 		.name = "global_low_prio_ras",
58*7536c9adSClément Léger 	},
59*7536c9adSClément Léger 	{
60*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,
61*7536c9adSClément Léger 		.name = "local_software",
62*7536c9adSClément Léger 	},
63*7536c9adSClément Léger 	{
64*7536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,
65*7536c9adSClément Léger 		.name = "global_software",
66*7536c9adSClément Léger 	},
67*7536c9adSClément Léger };
68*7536c9adSClément Léger 
69*7536c9adSClément Léger static const char *const attr_names[] = {
70*7536c9adSClément Léger 	[SBI_SSE_ATTR_STATUS] =			"status",
71*7536c9adSClément Léger 	[SBI_SSE_ATTR_PRIORITY] =		"priority",
72*7536c9adSClément Léger 	[SBI_SSE_ATTR_CONFIG] =			"config",
73*7536c9adSClément Léger 	[SBI_SSE_ATTR_PREFERRED_HART] =		"preferred_hart",
74*7536c9adSClément Léger 	[SBI_SSE_ATTR_ENTRY_PC] =		"entry_pc",
75*7536c9adSClément Léger 	[SBI_SSE_ATTR_ENTRY_ARG] =		"entry_arg",
76*7536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_SEPC] =	"interrupted_sepc",
77*7536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_FLAGS] =	"interrupted_flags",
78*7536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_A6] =		"interrupted_a6",
79*7536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_A7] =		"interrupted_a7",
80*7536c9adSClément Léger };
81*7536c9adSClément Léger 
82*7536c9adSClément Léger static const unsigned long ro_attrs[] = {
83*7536c9adSClément Léger 	SBI_SSE_ATTR_STATUS,
84*7536c9adSClément Léger 	SBI_SSE_ATTR_ENTRY_PC,
85*7536c9adSClément Léger 	SBI_SSE_ATTR_ENTRY_ARG,
86*7536c9adSClément Léger };
87*7536c9adSClément Léger 
88*7536c9adSClément Léger static const unsigned long interrupted_attrs[] = {
89*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_SEPC,
90*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS,
91*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_A6,
92*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_A7,
93*7536c9adSClément Léger };
94*7536c9adSClément Léger 
95*7536c9adSClément Léger static const unsigned long interrupted_flags[] = {
96*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPP,
97*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPIE,
98*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPELP,
99*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SDT,
100*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV,
101*7536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP,
102*7536c9adSClément Léger };
103*7536c9adSClément Léger 
104*7536c9adSClément Léger static struct sse_event_info *sse_event_get_info(uint32_t event_id)
105*7536c9adSClément Léger {
106*7536c9adSClément Léger 	int i;
107*7536c9adSClément Léger 
108*7536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
109*7536c9adSClément Léger 		if (sse_event_infos[i].event_id == event_id)
110*7536c9adSClément Léger 			return &sse_event_infos[i];
111*7536c9adSClément Léger 	}
112*7536c9adSClément Léger 
113*7536c9adSClément Léger 	assert_msg(false, "Invalid event id: %d", event_id);
114*7536c9adSClément Léger }
115*7536c9adSClément Léger 
116*7536c9adSClément Léger static const char *sse_event_name(uint32_t event_id)
117*7536c9adSClément Léger {
118*7536c9adSClément Léger 	return sse_event_get_info(event_id)->name;
119*7536c9adSClément Léger }
120*7536c9adSClément Léger 
121*7536c9adSClément Léger static bool sse_event_can_inject(uint32_t event_id)
122*7536c9adSClément Léger {
123*7536c9adSClément Léger 	return sse_event_get_info(event_id)->can_inject;
124*7536c9adSClément Léger }
125*7536c9adSClément Léger 
126*7536c9adSClément Léger static struct sbiret sse_get_event_status_field(uint32_t event_id, unsigned long mask,
127*7536c9adSClément Léger 						unsigned long shift, unsigned long *value)
128*7536c9adSClément Léger {
129*7536c9adSClément Léger 	struct sbiret ret;
130*7536c9adSClément Léger 	unsigned long status;
131*7536c9adSClément Léger 
132*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_STATUS, 1, &status);
133*7536c9adSClément Léger 	if (ret.error) {
134*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get event status");
135*7536c9adSClément Léger 		return ret;
136*7536c9adSClément Léger 	}
137*7536c9adSClément Léger 
138*7536c9adSClément Léger 	*value = (status & mask) >> shift;
139*7536c9adSClément Léger 
140*7536c9adSClément Léger 	return ret;
141*7536c9adSClément Léger }
142*7536c9adSClément Léger 
143*7536c9adSClément Léger static struct sbiret sse_event_get_state(uint32_t event_id, enum sbi_sse_state *state)
144*7536c9adSClément Léger {
145*7536c9adSClément Léger 	unsigned long status = 0;
146*7536c9adSClément Léger 	struct sbiret ret;
147*7536c9adSClément Léger 
148*7536c9adSClément Léger 	ret = sse_get_event_status_field(event_id, SBI_SSE_ATTR_STATUS_STATE_MASK,
149*7536c9adSClément Léger 					  SBI_SSE_ATTR_STATUS_STATE_OFFSET, &status);
150*7536c9adSClément Léger 	*state = status;
151*7536c9adSClément Léger 
152*7536c9adSClément Léger 	return ret;
153*7536c9adSClément Léger }
154*7536c9adSClément Léger 
155*7536c9adSClément Léger static unsigned long sse_global_event_set_current_hart(uint32_t event_id)
156*7536c9adSClément Léger {
157*7536c9adSClément Léger 	struct sbiret ret;
158*7536c9adSClément Léger 	unsigned long current_hart = current_thread_info()->hartid;
159*7536c9adSClément Léger 
160*7536c9adSClément Léger 	assert(sbi_sse_event_is_global(event_id));
161*7536c9adSClément Léger 
162*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &current_hart);
163*7536c9adSClément Léger 	if (sbiret_report_error(&ret, SBI_SUCCESS, "Set preferred hart"))
164*7536c9adSClément Léger 		return ret.error;
165*7536c9adSClément Léger 
166*7536c9adSClément Léger 	return 0;
167*7536c9adSClément Léger }
168*7536c9adSClément Léger 
169*7536c9adSClément Léger static bool sse_check_state(uint32_t event_id, unsigned long expected_state)
170*7536c9adSClément Léger {
171*7536c9adSClément Léger 	struct sbiret ret;
172*7536c9adSClément Léger 	enum sbi_sse_state state;
173*7536c9adSClément Léger 
174*7536c9adSClément Léger 	ret = sse_event_get_state(event_id, &state);
175*7536c9adSClément Léger 	if (ret.error)
176*7536c9adSClément Léger 		return false;
177*7536c9adSClément Léger 
178*7536c9adSClément Léger 	return report(state == expected_state, "event status == %ld", expected_state);
179*7536c9adSClément Léger }
180*7536c9adSClément Léger 
181*7536c9adSClément Léger static bool sse_event_pending(uint32_t event_id)
182*7536c9adSClément Léger {
183*7536c9adSClément Léger 	bool pending = 0;
184*7536c9adSClément Léger 
185*7536c9adSClément Léger 	sse_get_event_status_field(event_id, BIT(SBI_SSE_ATTR_STATUS_PENDING_OFFSET),
186*7536c9adSClément Léger 		SBI_SSE_ATTR_STATUS_PENDING_OFFSET, (unsigned long *)&pending);
187*7536c9adSClément Léger 
188*7536c9adSClément Léger 	return pending;
189*7536c9adSClément Léger }
190*7536c9adSClément Léger 
191*7536c9adSClément Léger static void *sse_alloc_stack(void)
192*7536c9adSClément Léger {
193*7536c9adSClément Léger 	/*
194*7536c9adSClément Léger 	 * We assume that SSE_STACK_SIZE always fit in one page. This page will
195*7536c9adSClément Léger 	 * always be decremented before storing anything on it in sse-entry.S.
196*7536c9adSClément Léger 	 */
197*7536c9adSClément Léger 	assert(SSE_STACK_SIZE <= PAGE_SIZE);
198*7536c9adSClément Léger 
199*7536c9adSClément Léger 	return (alloc_page() + SSE_STACK_SIZE);
200*7536c9adSClément Léger }
201*7536c9adSClément Léger 
202*7536c9adSClément Léger static void sse_free_stack(void *stack)
203*7536c9adSClément Léger {
204*7536c9adSClément Léger 	free_page(stack - SSE_STACK_SIZE);
205*7536c9adSClément Léger }
206*7536c9adSClément Léger 
207*7536c9adSClément Léger static void sse_read_write_test(uint32_t event_id, unsigned long attr, unsigned long attr_count,
208*7536c9adSClément Léger 				unsigned long *value, long expected_error, const char *str)
209*7536c9adSClément Léger {
210*7536c9adSClément Léger 	struct sbiret ret;
211*7536c9adSClément Léger 
212*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, attr, attr_count, value);
213*7536c9adSClément Léger 	sbiret_report_error(&ret, expected_error, "Read %s error", str);
214*7536c9adSClément Léger 
215*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, attr_count, value);
216*7536c9adSClément Léger 	sbiret_report_error(&ret, expected_error, "Write %s error", str);
217*7536c9adSClément Léger }
218*7536c9adSClément Léger 
219*7536c9adSClément Léger #define ALL_ATTRS_COUNT	(SBI_SSE_ATTR_INTERRUPTED_A7 + 1)
220*7536c9adSClément Léger 
221*7536c9adSClément Léger static void sse_test_attrs(uint32_t event_id)
222*7536c9adSClément Léger {
223*7536c9adSClément Léger 	unsigned long value = 0;
224*7536c9adSClément Léger 	struct sbiret ret;
225*7536c9adSClément Léger 	void *ptr;
226*7536c9adSClément Léger 	unsigned long values[ALL_ATTRS_COUNT];
227*7536c9adSClément Léger 	unsigned int i;
228*7536c9adSClément Léger 	const char *invalid_hart_str;
229*7536c9adSClément Léger 	const char *attr_name;
230*7536c9adSClément Léger 
231*7536c9adSClément Léger 	report_prefix_push("attrs");
232*7536c9adSClément Léger 
233*7536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(ro_attrs); i++) {
234*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, ro_attrs[i], 1, &value);
235*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_DENIED, "RO attribute %s not writable",
236*7536c9adSClément Léger 				    attr_names[ro_attrs[i]]);
237*7536c9adSClément Léger 	}
238*7536c9adSClément Léger 
239*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_STATUS, ALL_ATTRS_COUNT, values);
240*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Read multiple attributes");
241*7536c9adSClément Léger 
242*7536c9adSClément Léger 	for (i = SBI_SSE_ATTR_STATUS; i <= SBI_SSE_ATTR_INTERRUPTED_A7; i++) {
243*7536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, i, 1, &value);
244*7536c9adSClément Léger 		attr_name = attr_names[i];
245*7536c9adSClément Léger 
246*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Read single attribute %s", attr_name);
247*7536c9adSClément Léger 		if (values[i] != value)
248*7536c9adSClément Léger 			report_fail("Attribute 0x%x single value read (0x%lx) differs from the one read with multiple attributes (0x%lx)",
249*7536c9adSClément Léger 				    i, value, values[i]);
250*7536c9adSClément Léger 		/*
251*7536c9adSClément Léger 		 * Preferred hart reset value is defined by SBI vendor
252*7536c9adSClément Léger 		 */
253*7536c9adSClément Léger 		if (i != SBI_SSE_ATTR_PREFERRED_HART) {
254*7536c9adSClément Léger 			/*
255*7536c9adSClément Léger 			 * Specification states that injectable bit is implementation dependent
256*7536c9adSClément Léger 			 * but other bits are zero-initialized.
257*7536c9adSClément Léger 			 */
258*7536c9adSClément Léger 			if (i == SBI_SSE_ATTR_STATUS)
259*7536c9adSClément Léger 				value &= ~BIT(SBI_SSE_ATTR_STATUS_INJECT_OFFSET);
260*7536c9adSClément Léger 			report(value == 0, "Attribute %s reset value is 0, found %lx", attr_name, value);
261*7536c9adSClément Léger 		}
262*7536c9adSClément Léger 	}
263*7536c9adSClément Léger 
264*7536c9adSClément Léger #if __riscv_xlen > 32
265*7536c9adSClément Léger 	value = BIT(32);
266*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
267*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Write invalid prio > 0xFFFFFFFF error");
268*7536c9adSClément Léger #endif
269*7536c9adSClément Léger 
270*7536c9adSClément Léger 	value = ~SBI_SSE_ATTR_CONFIG_ONESHOT;
271*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_CONFIG, 1, &value);
272*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Write invalid config value error");
273*7536c9adSClément Léger 
274*7536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id)) {
275*7536c9adSClément Léger 		invalid_hart_str = getenv("INVALID_HART_ID");
276*7536c9adSClément Léger 		if (!invalid_hart_str)
277*7536c9adSClément Léger 			value = 0xFFFFFFFFUL;
278*7536c9adSClément Léger 		else
279*7536c9adSClément Léger 			value = strtoul(invalid_hart_str, NULL, 0);
280*7536c9adSClément Léger 
281*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
282*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Set invalid hart id error");
283*7536c9adSClément Léger 	} else {
284*7536c9adSClément Léger 		/* Set Hart on local event -> RO */
285*7536c9adSClément Léger 		value = current_thread_info()->hartid;
286*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
287*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_DENIED,
288*7536c9adSClément Léger 				    "Set hart id on local event error");
289*7536c9adSClément Léger 	}
290*7536c9adSClément Léger 
291*7536c9adSClément Léger 	/* Set/get flags, sepc, a6, a7 */
292*7536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
293*7536c9adSClément Léger 		attr_name = attr_names[interrupted_attrs[i]];
294*7536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, interrupted_attrs[i], 1, &value);
295*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get interrupted %s", attr_name);
296*7536c9adSClément Léger 
297*7536c9adSClément Léger 		value = ARRAY_SIZE(interrupted_attrs) - i;
298*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, interrupted_attrs[i], 1, &value);
299*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_INVALID_STATE,
300*7536c9adSClément Léger 				    "Set attribute %s invalid state error", attr_name);
301*7536c9adSClément Léger 	}
302*7536c9adSClément Léger 
303*7536c9adSClément Léger 	sse_read_write_test(event_id, SBI_SSE_ATTR_STATUS, 0, &value, SBI_ERR_INVALID_PARAM,
304*7536c9adSClément Léger 			    "attribute attr_count == 0");
305*7536c9adSClément Léger 	sse_read_write_test(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, 1, &value, SBI_ERR_BAD_RANGE,
306*7536c9adSClément Léger 			    "invalid attribute");
307*7536c9adSClément Léger 
308*7536c9adSClément Léger 	/* Misaligned pointer address */
309*7536c9adSClément Léger 	ptr = (void *)&value;
310*7536c9adSClément Léger 	ptr += 1;
311*7536c9adSClément Léger 	sse_read_write_test(event_id, SBI_SSE_ATTR_STATUS, 1, ptr, SBI_ERR_INVALID_ADDRESS,
312*7536c9adSClément Léger 		"attribute with invalid address");
313*7536c9adSClément Léger 
314*7536c9adSClément Léger 	report_prefix_pop();
315*7536c9adSClément Léger }
316*7536c9adSClément Léger 
317*7536c9adSClément Léger static void sse_test_register_error(uint32_t event_id)
318*7536c9adSClément Léger {
319*7536c9adSClément Léger 	struct sbiret ret;
320*7536c9adSClément Léger 
321*7536c9adSClément Léger 	report_prefix_push("register");
322*7536c9adSClément Léger 
323*7536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
324*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "unregister non-registered event");
325*7536c9adSClément Léger 
326*7536c9adSClément Léger 	ret = sbi_sse_register_raw(event_id, 0x1, 0);
327*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "register misaligned entry");
328*7536c9adSClément Léger 
329*7536c9adSClément Léger 	ret = sbi_sse_register(event_id, NULL);
330*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "register");
331*7536c9adSClément Léger 	if (ret.error)
332*7536c9adSClément Léger 		goto done;
333*7536c9adSClément Léger 
334*7536c9adSClément Léger 	ret = sbi_sse_register(event_id, NULL);
335*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "register used event failure");
336*7536c9adSClément Léger 
337*7536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
338*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "unregister");
339*7536c9adSClément Léger 
340*7536c9adSClément Léger done:
341*7536c9adSClément Léger 	report_prefix_pop();
342*7536c9adSClément Léger }
343*7536c9adSClément Léger 
344*7536c9adSClément Léger struct sse_simple_test_arg {
345*7536c9adSClément Léger 	bool done;
346*7536c9adSClément Léger 	unsigned long expected_a6;
347*7536c9adSClément Léger 	uint32_t event_id;
348*7536c9adSClément Léger };
349*7536c9adSClément Léger 
350*7536c9adSClément Léger #if __riscv_xlen > 32
351*7536c9adSClément Léger 
352*7536c9adSClément Léger struct alias_test_params {
353*7536c9adSClément Léger 	unsigned long event_id;
354*7536c9adSClément Léger 	unsigned long attr_id;
355*7536c9adSClément Léger 	unsigned long attr_count;
356*7536c9adSClément Léger 	const char *str;
357*7536c9adSClément Léger };
358*7536c9adSClément Léger 
359*7536c9adSClément Léger static void test_alias(uint32_t event_id)
360*7536c9adSClément Léger {
361*7536c9adSClément Léger 	struct alias_test_params *write, *read;
362*7536c9adSClément Léger 	unsigned long write_value, read_value;
363*7536c9adSClément Léger 	struct sbiret ret;
364*7536c9adSClément Léger 	bool err = false;
365*7536c9adSClément Léger 	int r, w;
366*7536c9adSClément Léger 	struct alias_test_params params[] = {
367*7536c9adSClément Léger 		{event_id, SBI_SSE_ATTR_INTERRUPTED_A6, 1, "non aliased"},
368*7536c9adSClément Léger 		{BIT(32) + event_id, SBI_SSE_ATTR_INTERRUPTED_A6, 1, "aliased event_id"},
369*7536c9adSClément Léger 		{event_id, BIT(32) + SBI_SSE_ATTR_INTERRUPTED_A6, 1, "aliased attr_id"},
370*7536c9adSClément Léger 		{event_id, SBI_SSE_ATTR_INTERRUPTED_A6, BIT(32) + 1, "aliased attr_count"},
371*7536c9adSClément Léger 	};
372*7536c9adSClément Léger 
373*7536c9adSClément Léger 	report_prefix_push("alias");
374*7536c9adSClément Léger 	for (w = 0; w < ARRAY_SIZE(params); w++) {
375*7536c9adSClément Léger 		write = &params[w];
376*7536c9adSClément Léger 
377*7536c9adSClément Léger 		write_value = 0xDEADBEEF + w;
378*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(write->event_id, write->attr_id, write->attr_count, &write_value);
379*7536c9adSClément Léger 		if (ret.error)
380*7536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "Write %s, event 0x%lx attr 0x%lx, attr count 0x%lx",
381*7536c9adSClément Léger 					    write->str, write->event_id, write->attr_id, write->attr_count);
382*7536c9adSClément Léger 
383*7536c9adSClément Léger 		for (r = 0; r < ARRAY_SIZE(params); r++) {
384*7536c9adSClément Léger 			read = &params[r];
385*7536c9adSClément Léger 			read_value = 0;
386*7536c9adSClément Léger 			ret = sbi_sse_read_attrs(read->event_id, read->attr_id, read->attr_count, &read_value);
387*7536c9adSClément Léger 			if (ret.error)
388*7536c9adSClément Léger 				sbiret_report_error(&ret, SBI_SUCCESS,
389*7536c9adSClément Léger 						    "Read %s, event 0x%lx attr 0x%lx, attr count 0x%lx",
390*7536c9adSClément Léger 						    read->str, read->event_id, read->attr_id, read->attr_count);
391*7536c9adSClément Léger 
392*7536c9adSClément Léger 			/* Do not spam output with a lot of reports */
393*7536c9adSClément Léger 			if (write_value != read_value) {
394*7536c9adSClément Léger 				err = true;
395*7536c9adSClément Léger 				report_fail("Write %s, event 0x%lx attr 0x%lx, attr count 0x%lx value %lx =="
396*7536c9adSClément Léger 					    "Read %s, event 0x%lx attr 0x%lx, attr count 0x%lx value %lx",
397*7536c9adSClément Léger 					    write->str, write->event_id, write->attr_id,
398*7536c9adSClément Léger 					    write->attr_count, write_value, read->str,
399*7536c9adSClément Léger 					    read->event_id, read->attr_id, read->attr_count,
400*7536c9adSClément Léger 					    read_value);
401*7536c9adSClément Léger 			}
402*7536c9adSClément Léger 		}
403*7536c9adSClément Léger 	}
404*7536c9adSClément Léger 
405*7536c9adSClément Léger 	report(!err, "BIT(32) aliasing tests");
406*7536c9adSClément Léger 	report_prefix_pop();
407*7536c9adSClément Léger }
408*7536c9adSClément Léger #endif
409*7536c9adSClément Léger 
410*7536c9adSClément Léger static void sse_simple_handler(void *data, struct pt_regs *regs, unsigned int hartid)
411*7536c9adSClément Léger {
412*7536c9adSClément Léger 	struct sse_simple_test_arg *arg = data;
413*7536c9adSClément Léger 	int i;
414*7536c9adSClément Léger 	struct sbiret ret;
415*7536c9adSClément Léger 	const char *attr_name;
416*7536c9adSClément Léger 	uint32_t event_id = READ_ONCE(arg->event_id), attr;
417*7536c9adSClément Léger 	unsigned long value, prev_value, flags;
418*7536c9adSClément Léger 	unsigned long interrupted_state[ARRAY_SIZE(interrupted_attrs)];
419*7536c9adSClément Léger 	unsigned long modified_state[ARRAY_SIZE(interrupted_attrs)] = {4, 3, 2, 1};
420*7536c9adSClément Léger 	unsigned long tmp_state[ARRAY_SIZE(interrupted_attrs)];
421*7536c9adSClément Léger 
422*7536c9adSClément Léger 	report((regs->status & SR_SPP) == SR_SPP, "Interrupted S-mode");
423*7536c9adSClément Léger 	report(hartid == current_thread_info()->hartid, "Hartid correctly passed");
424*7536c9adSClément Léger 	sse_check_state(event_id, SBI_SSE_STATE_RUNNING);
425*7536c9adSClément Léger 	report(!sse_event_pending(event_id), "Event not pending");
426*7536c9adSClément Léger 
427*7536c9adSClément Léger 	/* Read full interrupted state */
428*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
429*7536c9adSClément Léger 				 ARRAY_SIZE(interrupted_attrs), interrupted_state);
430*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Save full interrupted state from handler");
431*7536c9adSClément Léger 
432*7536c9adSClément Léger 	/* Write full modified state and read it */
433*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
434*7536c9adSClément Léger 				  ARRAY_SIZE(modified_state), modified_state);
435*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS,
436*7536c9adSClément Léger 			    "Write full interrupted state from handler");
437*7536c9adSClément Léger 
438*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
439*7536c9adSClément Léger 				ARRAY_SIZE(tmp_state), tmp_state);
440*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Read full modified state from handler");
441*7536c9adSClément Léger 
442*7536c9adSClément Léger 	report(memcmp(tmp_state, modified_state, sizeof(modified_state)) == 0,
443*7536c9adSClément Léger 	       "Full interrupted state successfully written");
444*7536c9adSClément Léger 
445*7536c9adSClément Léger #if __riscv_xlen > 32
446*7536c9adSClément Léger 	test_alias(event_id);
447*7536c9adSClément Léger #endif
448*7536c9adSClément Léger 
449*7536c9adSClément Léger 	/* Restore full saved state */
450*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
451*7536c9adSClément Léger 				  ARRAY_SIZE(interrupted_attrs), interrupted_state);
452*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Full interrupted state restore from handler");
453*7536c9adSClément Léger 
454*7536c9adSClément Léger 	/* We test SBI_SSE_ATTR_INTERRUPTED_FLAGS below with specific flag values */
455*7536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
456*7536c9adSClément Léger 		attr = interrupted_attrs[i];
457*7536c9adSClément Léger 		if (attr == SBI_SSE_ATTR_INTERRUPTED_FLAGS)
458*7536c9adSClément Léger 			continue;
459*7536c9adSClément Léger 
460*7536c9adSClément Léger 		attr_name = attr_names[attr];
461*7536c9adSClément Léger 
462*7536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, attr, 1, &prev_value);
463*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get attr %s", attr_name);
464*7536c9adSClément Léger 
465*7536c9adSClément Léger 		value = 0xDEADBEEF + i;
466*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, attr, 1, &value);
467*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Set attr %s", attr_name);
468*7536c9adSClément Léger 
469*7536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, attr, 1, &value);
470*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get attr %s", attr_name);
471*7536c9adSClément Léger 		report(value == 0xDEADBEEF + i, "Get attr %s, value: 0x%lx", attr_name, value);
472*7536c9adSClément Léger 
473*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, attr, 1, &prev_value);
474*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Restore attr %s value", attr_name);
475*7536c9adSClément Léger 	}
476*7536c9adSClément Léger 
477*7536c9adSClément Léger 	/* Test all flags allowed for SBI_SSE_ATTR_INTERRUPTED_FLAGS */
478*7536c9adSClément Léger 	attr = SBI_SSE_ATTR_INTERRUPTED_FLAGS;
479*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, attr, 1, &prev_value);
480*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Save interrupted flags");
481*7536c9adSClément Léger 
482*7536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(interrupted_flags); i++) {
483*7536c9adSClément Léger 		flags = interrupted_flags[i];
484*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, attr, 1, &flags);
485*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS,
486*7536c9adSClément Léger 				    "Set interrupted flags bit 0x%lx value", flags);
487*7536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, attr, 1, &value);
488*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get interrupted flags after set");
489*7536c9adSClément Léger 		report(value == flags, "interrupted flags modified value: 0x%lx", value);
490*7536c9adSClément Léger 	}
491*7536c9adSClément Léger 
492*7536c9adSClément Léger 	/* Write invalid bit in flag register */
493*7536c9adSClément Léger 	flags = SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SDT << 1;
494*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, 1, &flags);
495*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Set invalid flags bit 0x%lx value error",
496*7536c9adSClément Léger 			    flags);
497*7536c9adSClément Léger 
498*7536c9adSClément Léger #if __riscv_xlen > 32
499*7536c9adSClément Léger 	flags = BIT(32);
500*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, 1, &flags);
501*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Set invalid flags bit 0x%lx value error",
502*7536c9adSClément Léger 			    flags);
503*7536c9adSClément Léger #endif
504*7536c9adSClément Léger 
505*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, 1, &prev_value);
506*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Restore interrupted flags");
507*7536c9adSClément Léger 
508*7536c9adSClément Léger 	/* Try to change HARTID/Priority while running */
509*7536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id)) {
510*7536c9adSClément Léger 		value = current_thread_info()->hartid;
511*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
512*7536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "Set hart id while running error");
513*7536c9adSClément Léger 	}
514*7536c9adSClément Léger 
515*7536c9adSClément Léger 	value = 0;
516*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
517*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "Set priority while running error");
518*7536c9adSClément Léger 
519*7536c9adSClément Léger 	value = READ_ONCE(arg->expected_a6);
520*7536c9adSClément Léger 	report(interrupted_state[2] == value, "Interrupted state a6, expected 0x%lx, got 0x%lx",
521*7536c9adSClément Léger 	       value, interrupted_state[2]);
522*7536c9adSClément Léger 
523*7536c9adSClément Léger 	report(interrupted_state[3] == SBI_EXT_SSE,
524*7536c9adSClément Léger 	       "Interrupted state a7, expected 0x%x, got 0x%lx", SBI_EXT_SSE,
525*7536c9adSClément Léger 	       interrupted_state[3]);
526*7536c9adSClément Léger 
527*7536c9adSClément Léger 	WRITE_ONCE(arg->done, true);
528*7536c9adSClément Léger }
529*7536c9adSClément Léger 
530*7536c9adSClément Léger static void sse_test_inject_simple(uint32_t event_id)
531*7536c9adSClément Léger {
532*7536c9adSClément Léger 	unsigned long value, error;
533*7536c9adSClément Léger 	struct sbiret ret;
534*7536c9adSClément Léger 	enum sbi_sse_state state = SBI_SSE_STATE_UNUSED;
535*7536c9adSClément Léger 	struct sse_simple_test_arg test_arg = {.event_id = event_id};
536*7536c9adSClément Léger 	struct sbi_sse_handler_arg args = {
537*7536c9adSClément Léger 		.handler = sse_simple_handler,
538*7536c9adSClément Léger 		.handler_data = (void *)&test_arg,
539*7536c9adSClément Léger 		.stack = sse_alloc_stack(),
540*7536c9adSClément Léger 	};
541*7536c9adSClément Léger 
542*7536c9adSClément Léger 	report_prefix_push("simple");
543*7536c9adSClément Léger 
544*7536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_UNUSED))
545*7536c9adSClément Léger 		goto cleanup;
546*7536c9adSClément Léger 
547*7536c9adSClément Léger 	ret = sbi_sse_register(event_id, &args);
548*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "register"))
549*7536c9adSClément Léger 		goto cleanup;
550*7536c9adSClément Léger 
551*7536c9adSClément Léger 	state = SBI_SSE_STATE_REGISTERED;
552*7536c9adSClément Léger 
553*7536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_REGISTERED))
554*7536c9adSClément Léger 		goto cleanup;
555*7536c9adSClément Léger 
556*7536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id)) {
557*7536c9adSClément Léger 		/* Be sure global events are targeting the current hart */
558*7536c9adSClément Léger 		error = sse_global_event_set_current_hart(event_id);
559*7536c9adSClément Léger 		if (error)
560*7536c9adSClément Léger 			goto cleanup;
561*7536c9adSClément Léger 	}
562*7536c9adSClément Léger 
563*7536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
564*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "enable"))
565*7536c9adSClément Léger 		goto cleanup;
566*7536c9adSClément Léger 
567*7536c9adSClément Léger 	state = SBI_SSE_STATE_ENABLED;
568*7536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_ENABLED))
569*7536c9adSClément Léger 		goto cleanup;
570*7536c9adSClément Léger 
571*7536c9adSClément Léger 	ret = sbi_sse_hart_mask();
572*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "hart mask"))
573*7536c9adSClément Léger 		goto cleanup;
574*7536c9adSClément Léger 
575*7536c9adSClément Léger 	ret = sbi_sse_inject(event_id, current_thread_info()->hartid);
576*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "injection masked")) {
577*7536c9adSClément Léger 		sbi_sse_hart_unmask();
578*7536c9adSClément Léger 		goto cleanup;
579*7536c9adSClément Léger 	}
580*7536c9adSClément Léger 
581*7536c9adSClément Léger 	report(READ_ONCE(test_arg.done) == 0, "event masked not handled");
582*7536c9adSClément Léger 
583*7536c9adSClément Léger 	/*
584*7536c9adSClément Léger 	 * When unmasking the SSE events, we expect it to be injected
585*7536c9adSClément Léger 	 * immediately so a6 should be SBI_EXT_SBI_SSE_HART_UNMASK
586*7536c9adSClément Léger 	 */
587*7536c9adSClément Léger 	WRITE_ONCE(test_arg.expected_a6, SBI_EXT_SSE_HART_UNMASK);
588*7536c9adSClément Léger 	ret = sbi_sse_hart_unmask();
589*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "hart unmask"))
590*7536c9adSClément Léger 		goto cleanup;
591*7536c9adSClément Léger 
592*7536c9adSClément Léger 	report(READ_ONCE(test_arg.done) == 1, "event unmasked handled");
593*7536c9adSClément Léger 	WRITE_ONCE(test_arg.done, 0);
594*7536c9adSClément Léger 	WRITE_ONCE(test_arg.expected_a6, SBI_EXT_SSE_INJECT);
595*7536c9adSClément Léger 
596*7536c9adSClément Léger 	/* Set as oneshot and verify it is disabled */
597*7536c9adSClément Léger 	ret = sbi_sse_disable(event_id);
598*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Disable event")) {
599*7536c9adSClément Léger 		/* Nothing we can really do here, event can not be disabled */
600*7536c9adSClément Léger 		goto cleanup;
601*7536c9adSClément Léger 	}
602*7536c9adSClément Léger 	state = SBI_SSE_STATE_REGISTERED;
603*7536c9adSClément Léger 
604*7536c9adSClément Léger 	value = SBI_SSE_ATTR_CONFIG_ONESHOT;
605*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_CONFIG, 1, &value);
606*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Set event attribute as ONESHOT"))
607*7536c9adSClément Léger 		goto cleanup;
608*7536c9adSClément Léger 
609*7536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
610*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Enable event"))
611*7536c9adSClément Léger 		goto cleanup;
612*7536c9adSClément Léger 	state = SBI_SSE_STATE_ENABLED;
613*7536c9adSClément Léger 
614*7536c9adSClément Léger 	ret = sbi_sse_inject(event_id, current_thread_info()->hartid);
615*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "second injection"))
616*7536c9adSClément Léger 		goto cleanup;
617*7536c9adSClément Léger 
618*7536c9adSClément Léger 	report(READ_ONCE(test_arg.done) == 1, "event handled");
619*7536c9adSClément Léger 	WRITE_ONCE(test_arg.done, 0);
620*7536c9adSClément Léger 
621*7536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_REGISTERED))
622*7536c9adSClément Léger 		goto cleanup;
623*7536c9adSClément Léger 	state = SBI_SSE_STATE_REGISTERED;
624*7536c9adSClément Léger 
625*7536c9adSClément Léger 	/* Clear ONESHOT FLAG */
626*7536c9adSClément Léger 	value = 0;
627*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_CONFIG, 1, &value);
628*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Clear CONFIG.ONESHOT flag"))
629*7536c9adSClément Léger 		goto cleanup;
630*7536c9adSClément Léger 
631*7536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
632*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "unregister"))
633*7536c9adSClément Léger 		goto cleanup;
634*7536c9adSClément Léger 	state = SBI_SSE_STATE_UNUSED;
635*7536c9adSClément Léger 
636*7536c9adSClément Léger 	sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
637*7536c9adSClément Léger 
638*7536c9adSClément Léger cleanup:
639*7536c9adSClément Léger 	switch (state) {
640*7536c9adSClément Léger 	case SBI_SSE_STATE_ENABLED:
641*7536c9adSClément Léger 		ret = sbi_sse_disable(event_id);
642*7536c9adSClément Léger 		if (ret.error) {
643*7536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "disable event 0x%x", event_id);
644*7536c9adSClément Léger 			break;
645*7536c9adSClément Léger 		}
646*7536c9adSClément Léger 	case SBI_SSE_STATE_REGISTERED:
647*7536c9adSClément Léger 		sbi_sse_unregister(event_id);
648*7536c9adSClément Léger 		if (ret.error)
649*7536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "unregister event 0x%x", event_id);
650*7536c9adSClément Léger 	default:
651*7536c9adSClément Léger 		break;
652*7536c9adSClément Léger 	}
653*7536c9adSClément Léger 
654*7536c9adSClément Léger 	sse_free_stack(args.stack);
655*7536c9adSClément Léger 	report_prefix_pop();
656*7536c9adSClément Léger }
657*7536c9adSClément Léger 
658*7536c9adSClément Léger struct sse_foreign_cpu_test_arg {
659*7536c9adSClément Léger 	bool done;
660*7536c9adSClément Léger 	unsigned int expected_cpu;
661*7536c9adSClément Léger 	uint32_t event_id;
662*7536c9adSClément Léger };
663*7536c9adSClément Léger 
664*7536c9adSClément Léger static void sse_foreign_cpu_handler(void *data, struct pt_regs *regs, unsigned int hartid)
665*7536c9adSClément Léger {
666*7536c9adSClément Léger 	struct sse_foreign_cpu_test_arg *arg = data;
667*7536c9adSClément Léger 	unsigned int expected_cpu;
668*7536c9adSClément Léger 
669*7536c9adSClément Léger 	/* For arg content to be visible */
670*7536c9adSClément Léger 	smp_rmb();
671*7536c9adSClément Léger 	expected_cpu = READ_ONCE(arg->expected_cpu);
672*7536c9adSClément Léger 	report(expected_cpu == current_thread_info()->cpu,
673*7536c9adSClément Léger 	       "Received event on CPU (%d), expected CPU (%d)", current_thread_info()->cpu,
674*7536c9adSClément Léger 	       expected_cpu);
675*7536c9adSClément Léger 
676*7536c9adSClément Léger 	WRITE_ONCE(arg->done, true);
677*7536c9adSClément Léger 	/* For arg update to be visible for other CPUs */
678*7536c9adSClément Léger 	smp_wmb();
679*7536c9adSClément Léger }
680*7536c9adSClément Léger 
681*7536c9adSClément Léger struct sse_local_per_cpu {
682*7536c9adSClément Léger 	struct sbi_sse_handler_arg args;
683*7536c9adSClément Léger 	struct sbiret ret;
684*7536c9adSClément Léger 	struct sse_foreign_cpu_test_arg handler_arg;
685*7536c9adSClément Léger 	enum sbi_sse_state state;
686*7536c9adSClément Léger };
687*7536c9adSClément Léger 
688*7536c9adSClément Léger static void sse_register_enable_local(void *data)
689*7536c9adSClément Léger {
690*7536c9adSClément Léger 	struct sbiret ret;
691*7536c9adSClément Léger 	struct sse_local_per_cpu *cpu_args = data;
692*7536c9adSClément Léger 	struct sse_local_per_cpu *cpu_arg = &cpu_args[current_thread_info()->cpu];
693*7536c9adSClément Léger 	uint32_t event_id = cpu_arg->handler_arg.event_id;
694*7536c9adSClément Léger 
695*7536c9adSClément Léger 	ret = sbi_sse_register(event_id, &cpu_arg->args);
696*7536c9adSClément Léger 	WRITE_ONCE(cpu_arg->ret, ret);
697*7536c9adSClément Léger 	if (ret.error)
698*7536c9adSClément Léger 		return;
699*7536c9adSClément Léger 	cpu_arg->state = SBI_SSE_STATE_REGISTERED;
700*7536c9adSClément Léger 
701*7536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
702*7536c9adSClément Léger 	WRITE_ONCE(cpu_arg->ret, ret);
703*7536c9adSClément Léger 	if (ret.error)
704*7536c9adSClément Léger 		return;
705*7536c9adSClément Léger 	cpu_arg->state = SBI_SSE_STATE_ENABLED;
706*7536c9adSClément Léger }
707*7536c9adSClément Léger 
708*7536c9adSClément Léger static void sbi_sse_disable_unregister_local(void *data)
709*7536c9adSClément Léger {
710*7536c9adSClément Léger 	struct sbiret ret;
711*7536c9adSClément Léger 	struct sse_local_per_cpu *cpu_args = data;
712*7536c9adSClément Léger 	struct sse_local_per_cpu *cpu_arg = &cpu_args[current_thread_info()->cpu];
713*7536c9adSClément Léger 	uint32_t event_id = cpu_arg->handler_arg.event_id;
714*7536c9adSClément Léger 
715*7536c9adSClément Léger 	switch (cpu_arg->state) {
716*7536c9adSClément Léger 	case SBI_SSE_STATE_ENABLED:
717*7536c9adSClément Léger 		ret = sbi_sse_disable(event_id);
718*7536c9adSClément Léger 		WRITE_ONCE(cpu_arg->ret, ret);
719*7536c9adSClément Léger 		if (ret.error)
720*7536c9adSClément Léger 			return;
721*7536c9adSClément Léger 	case SBI_SSE_STATE_REGISTERED:
722*7536c9adSClément Léger 		ret = sbi_sse_unregister(event_id);
723*7536c9adSClément Léger 		WRITE_ONCE(cpu_arg->ret, ret);
724*7536c9adSClément Léger 	default:
725*7536c9adSClément Léger 		break;
726*7536c9adSClément Léger 	}
727*7536c9adSClément Léger }
728*7536c9adSClément Léger 
729*7536c9adSClément Léger static uint64_t sse_event_get_complete_timeout(void)
730*7536c9adSClément Léger {
731*7536c9adSClément Léger 	char *event_complete_timeout_str;
732*7536c9adSClément Léger 	uint64_t timeout;
733*7536c9adSClément Léger 
734*7536c9adSClément Léger 	event_complete_timeout_str = getenv("SSE_EVENT_COMPLETE_TIMEOUT");
735*7536c9adSClément Léger 	if (!event_complete_timeout_str)
736*7536c9adSClément Léger 		timeout = 3000;
737*7536c9adSClément Léger 	else
738*7536c9adSClément Léger 		timeout = strtoul(event_complete_timeout_str, NULL, 0);
739*7536c9adSClément Léger 
740*7536c9adSClément Léger 	return timer_get_cycles() + usec_to_cycles(timeout);
741*7536c9adSClément Léger }
742*7536c9adSClément Léger 
743*7536c9adSClément Léger static void sse_test_inject_local(uint32_t event_id)
744*7536c9adSClément Léger {
745*7536c9adSClément Léger 	int cpu;
746*7536c9adSClément Léger 	uint64_t timeout;
747*7536c9adSClément Léger 	struct sbiret ret;
748*7536c9adSClément Léger 	struct sse_local_per_cpu *cpu_args, *cpu_arg;
749*7536c9adSClément Léger 	struct sse_foreign_cpu_test_arg *handler_arg;
750*7536c9adSClément Léger 
751*7536c9adSClément Léger 	cpu_args = calloc(NR_CPUS, sizeof(struct sbi_sse_handler_arg));
752*7536c9adSClément Léger 
753*7536c9adSClément Léger 	report_prefix_push("local_dispatch");
754*7536c9adSClément Léger 	for_each_online_cpu(cpu) {
755*7536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
756*7536c9adSClément Léger 		cpu_arg->handler_arg.event_id = event_id;
757*7536c9adSClément Léger 		cpu_arg->args.stack = sse_alloc_stack();
758*7536c9adSClément Léger 		cpu_arg->args.handler = sse_foreign_cpu_handler;
759*7536c9adSClément Léger 		cpu_arg->args.handler_data = (void *)&cpu_arg->handler_arg;
760*7536c9adSClément Léger 		cpu_arg->state = SBI_SSE_STATE_UNUSED;
761*7536c9adSClément Léger 	}
762*7536c9adSClément Léger 
763*7536c9adSClément Léger 	on_cpus(sse_register_enable_local, cpu_args);
764*7536c9adSClément Léger 	for_each_online_cpu(cpu) {
765*7536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
766*7536c9adSClément Léger 		ret = cpu_arg->ret;
767*7536c9adSClément Léger 		if (ret.error) {
768*7536c9adSClément Léger 			report_fail("CPU failed to register/enable event: %ld", ret.error);
769*7536c9adSClément Léger 			goto cleanup;
770*7536c9adSClément Léger 		}
771*7536c9adSClément Léger 
772*7536c9adSClément Léger 		handler_arg = &cpu_arg->handler_arg;
773*7536c9adSClément Léger 		WRITE_ONCE(handler_arg->expected_cpu, cpu);
774*7536c9adSClément Léger 		/* For handler_arg content to be visible for other CPUs */
775*7536c9adSClément Léger 		smp_wmb();
776*7536c9adSClément Léger 		ret = sbi_sse_inject(event_id, cpus[cpu].hartid);
777*7536c9adSClément Léger 		if (ret.error) {
778*7536c9adSClément Léger 			report_fail("CPU failed to inject event: %ld", ret.error);
779*7536c9adSClément Léger 			goto cleanup;
780*7536c9adSClément Léger 		}
781*7536c9adSClément Léger 	}
782*7536c9adSClément Léger 
783*7536c9adSClément Léger 	for_each_online_cpu(cpu) {
784*7536c9adSClément Léger 		handler_arg = &cpu_args[cpu].handler_arg;
785*7536c9adSClément Léger 		smp_rmb();
786*7536c9adSClément Léger 
787*7536c9adSClément Léger 		timeout = sse_event_get_complete_timeout();
788*7536c9adSClément Léger 		while (!READ_ONCE(handler_arg->done) && timer_get_cycles() < timeout) {
789*7536c9adSClément Léger 			/* For handler_arg update to be visible */
790*7536c9adSClément Léger 			smp_rmb();
791*7536c9adSClément Léger 			cpu_relax();
792*7536c9adSClément Léger 		}
793*7536c9adSClément Léger 		report(READ_ONCE(handler_arg->done), "Event handled");
794*7536c9adSClément Léger 		WRITE_ONCE(handler_arg->done, false);
795*7536c9adSClément Léger 	}
796*7536c9adSClément Léger 
797*7536c9adSClément Léger cleanup:
798*7536c9adSClément Léger 	on_cpus(sbi_sse_disable_unregister_local, cpu_args);
799*7536c9adSClément Léger 	for_each_online_cpu(cpu) {
800*7536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
801*7536c9adSClément Léger 		ret = READ_ONCE(cpu_arg->ret);
802*7536c9adSClément Léger 		if (ret.error)
803*7536c9adSClément Léger 			report_fail("CPU failed to disable/unregister event: %ld", ret.error);
804*7536c9adSClément Léger 	}
805*7536c9adSClément Léger 
806*7536c9adSClément Léger 	for_each_online_cpu(cpu) {
807*7536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
808*7536c9adSClément Léger 		sse_free_stack(cpu_arg->args.stack);
809*7536c9adSClément Léger 	}
810*7536c9adSClément Léger 
811*7536c9adSClément Léger 	report_prefix_pop();
812*7536c9adSClément Léger }
813*7536c9adSClément Léger 
814*7536c9adSClément Léger static void sse_test_inject_global_cpu(uint32_t event_id, unsigned int cpu,
815*7536c9adSClément Léger 				       struct sse_foreign_cpu_test_arg *test_arg)
816*7536c9adSClément Léger {
817*7536c9adSClément Léger 	unsigned long value;
818*7536c9adSClément Léger 	struct sbiret ret;
819*7536c9adSClément Léger 	uint64_t timeout;
820*7536c9adSClément Léger 	enum sbi_sse_state state;
821*7536c9adSClément Léger 
822*7536c9adSClément Léger 	WRITE_ONCE(test_arg->expected_cpu, cpu);
823*7536c9adSClément Léger 	/* For test_arg content to be visible for other CPUs */
824*7536c9adSClément Léger 	smp_wmb();
825*7536c9adSClément Léger 	value = cpu;
826*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
827*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Set preferred hart"))
828*7536c9adSClément Léger 		return;
829*7536c9adSClément Léger 
830*7536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
831*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Enable event"))
832*7536c9adSClément Léger 		return;
833*7536c9adSClément Léger 
834*7536c9adSClément Léger 	ret = sbi_sse_inject(event_id, cpu);
835*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Inject event"))
836*7536c9adSClément Léger 		goto disable;
837*7536c9adSClément Léger 
838*7536c9adSClément Léger 	smp_rmb();
839*7536c9adSClément Léger 	timeout = sse_event_get_complete_timeout();
840*7536c9adSClément Léger 	while (!READ_ONCE(test_arg->done) && timer_get_cycles() < timeout) {
841*7536c9adSClément Léger 		/* For shared test_arg structure */
842*7536c9adSClément Léger 		smp_rmb();
843*7536c9adSClément Léger 		cpu_relax();
844*7536c9adSClément Léger 	}
845*7536c9adSClément Léger 
846*7536c9adSClément Léger 	report(READ_ONCE(test_arg->done), "event handler called");
847*7536c9adSClément Léger 	WRITE_ONCE(test_arg->done, false);
848*7536c9adSClément Léger 
849*7536c9adSClément Léger 	timeout = sse_event_get_complete_timeout();
850*7536c9adSClément Léger 	/* Wait for event to be back in ENABLED state */
851*7536c9adSClément Léger 	do {
852*7536c9adSClément Léger 		ret = sse_event_get_state(event_id, &state);
853*7536c9adSClément Léger 		if (ret.error)
854*7536c9adSClément Léger 			goto disable;
855*7536c9adSClément Léger 		cpu_relax();
856*7536c9adSClément Léger 	} while (state != SBI_SSE_STATE_ENABLED && timer_get_cycles() < timeout);
857*7536c9adSClément Léger 
858*7536c9adSClément Léger 	report(state == SBI_SSE_STATE_ENABLED, "Event in enabled state");
859*7536c9adSClément Léger 
860*7536c9adSClément Léger disable:
861*7536c9adSClément Léger 	ret = sbi_sse_disable(event_id);
862*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Disable event");
863*7536c9adSClément Léger }
864*7536c9adSClément Léger 
865*7536c9adSClément Léger static void sse_test_inject_global(uint32_t event_id)
866*7536c9adSClément Léger {
867*7536c9adSClément Léger 	struct sbiret ret;
868*7536c9adSClément Léger 	unsigned int cpu;
869*7536c9adSClément Léger 	struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
870*7536c9adSClément Léger 	struct sbi_sse_handler_arg args = {
871*7536c9adSClément Léger 		.handler = sse_foreign_cpu_handler,
872*7536c9adSClément Léger 		.handler_data = (void *)&test_arg,
873*7536c9adSClément Léger 		.stack = sse_alloc_stack(),
874*7536c9adSClément Léger 	};
875*7536c9adSClément Léger 
876*7536c9adSClément Léger 	report_prefix_push("global_dispatch");
877*7536c9adSClément Léger 
878*7536c9adSClément Léger 	ret = sbi_sse_register(event_id, &args);
879*7536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Register event"))
880*7536c9adSClément Léger 		goto err;
881*7536c9adSClément Léger 
882*7536c9adSClément Léger 	for_each_online_cpu(cpu)
883*7536c9adSClément Léger 		sse_test_inject_global_cpu(event_id, cpu, &test_arg);
884*7536c9adSClément Léger 
885*7536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
886*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Unregister event");
887*7536c9adSClément Léger 
888*7536c9adSClément Léger err:
889*7536c9adSClément Léger 	sse_free_stack(args.stack);
890*7536c9adSClément Léger 	report_prefix_pop();
891*7536c9adSClément Léger }
892*7536c9adSClément Léger 
893*7536c9adSClément Léger struct priority_test_arg {
894*7536c9adSClément Léger 	uint32_t event_id;
895*7536c9adSClément Léger 	bool called;
896*7536c9adSClément Léger 	u32 prio;
897*7536c9adSClément Léger 	enum sbi_sse_state state; /* Used for error handling */
898*7536c9adSClément Léger 	struct priority_test_arg *next_event_arg;
899*7536c9adSClément Léger 	void (*check_func)(struct priority_test_arg *arg);
900*7536c9adSClément Léger };
901*7536c9adSClément Léger 
902*7536c9adSClément Léger static void sse_hi_priority_test_handler(void *arg, struct pt_regs *regs,
903*7536c9adSClément Léger 					 unsigned int hartid)
904*7536c9adSClément Léger {
905*7536c9adSClément Léger 	struct priority_test_arg *targ = arg;
906*7536c9adSClément Léger 	struct priority_test_arg *next = targ->next_event_arg;
907*7536c9adSClément Léger 
908*7536c9adSClément Léger 	targ->called = true;
909*7536c9adSClément Léger 	if (next) {
910*7536c9adSClément Léger 		sbi_sse_inject(next->event_id, current_thread_info()->hartid);
911*7536c9adSClément Léger 
912*7536c9adSClément Léger 		report(!sse_event_pending(next->event_id), "Higher priority event is not pending");
913*7536c9adSClément Léger 		report(next->called, "Higher priority event was handled");
914*7536c9adSClément Léger 	}
915*7536c9adSClément Léger }
916*7536c9adSClément Léger 
917*7536c9adSClément Léger static void sse_low_priority_test_handler(void *arg, struct pt_regs *regs,
918*7536c9adSClément Léger 					  unsigned int hartid)
919*7536c9adSClément Léger {
920*7536c9adSClément Léger 	struct priority_test_arg *targ = arg;
921*7536c9adSClément Léger 	struct priority_test_arg *next = targ->next_event_arg;
922*7536c9adSClément Léger 
923*7536c9adSClément Léger 	targ->called = true;
924*7536c9adSClément Léger 
925*7536c9adSClément Léger 	if (next) {
926*7536c9adSClément Léger 		sbi_sse_inject(next->event_id, current_thread_info()->hartid);
927*7536c9adSClément Léger 
928*7536c9adSClément Léger 		report(sse_event_pending(next->event_id), "Lower priority event is pending");
929*7536c9adSClément Léger 		report(!next->called, "Lower priority event %s was not handled before %s",
930*7536c9adSClément Léger 		       sse_event_name(next->event_id), sse_event_name(targ->event_id));
931*7536c9adSClément Léger 	}
932*7536c9adSClément Léger }
933*7536c9adSClément Léger 
934*7536c9adSClément Léger static void sse_test_injection_priority_arg(struct priority_test_arg *in_args,
935*7536c9adSClément Léger 					    unsigned int in_args_size,
936*7536c9adSClément Léger 					    sbi_sse_handler_fn handler,
937*7536c9adSClément Léger 					    const char *test_name)
938*7536c9adSClément Léger {
939*7536c9adSClément Léger 	unsigned int i;
940*7536c9adSClément Léger 	unsigned long value, uret;
941*7536c9adSClément Léger 	struct sbiret ret;
942*7536c9adSClément Léger 	uint32_t event_id;
943*7536c9adSClément Léger 	struct priority_test_arg *arg;
944*7536c9adSClément Léger 	unsigned int args_size = 0;
945*7536c9adSClément Léger 	struct sbi_sse_handler_arg event_args[in_args_size];
946*7536c9adSClément Léger 	struct priority_test_arg *args[in_args_size];
947*7536c9adSClément Léger 	void *stack;
948*7536c9adSClément Léger 	struct sbi_sse_handler_arg *event_arg;
949*7536c9adSClément Léger 
950*7536c9adSClément Léger 	report_prefix_push(test_name);
951*7536c9adSClément Léger 
952*7536c9adSClément Léger 	for (i = 0; i < in_args_size; i++) {
953*7536c9adSClément Léger 		arg = &in_args[i];
954*7536c9adSClément Léger 		arg->state = SBI_SSE_STATE_UNUSED;
955*7536c9adSClément Léger 		event_id = arg->event_id;
956*7536c9adSClément Léger 		if (!sse_event_can_inject(event_id))
957*7536c9adSClément Léger 			continue;
958*7536c9adSClément Léger 
959*7536c9adSClément Léger 		args[args_size] = arg;
960*7536c9adSClément Léger 		args_size++;
961*7536c9adSClément Léger 		event_args->stack = 0;
962*7536c9adSClément Léger 	}
963*7536c9adSClément Léger 
964*7536c9adSClément Léger 	if (!args_size) {
965*7536c9adSClément Léger 		report_skip("No injectable events");
966*7536c9adSClément Léger 		goto skip;
967*7536c9adSClément Léger 	}
968*7536c9adSClément Léger 
969*7536c9adSClément Léger 	for (i = 0; i < args_size; i++) {
970*7536c9adSClément Léger 		arg = args[i];
971*7536c9adSClément Léger 		event_id = arg->event_id;
972*7536c9adSClément Léger 		stack = sse_alloc_stack();
973*7536c9adSClément Léger 
974*7536c9adSClément Léger 		event_arg = &event_args[i];
975*7536c9adSClément Léger 		event_arg->handler = handler;
976*7536c9adSClément Léger 		event_arg->handler_data = (void *)arg;
977*7536c9adSClément Léger 		event_arg->stack = stack;
978*7536c9adSClément Léger 
979*7536c9adSClément Léger 		if (i < (args_size - 1))
980*7536c9adSClément Léger 			arg->next_event_arg = args[i + 1];
981*7536c9adSClément Léger 		else
982*7536c9adSClément Léger 			arg->next_event_arg = NULL;
983*7536c9adSClément Léger 
984*7536c9adSClément Léger 		/* Be sure global events are targeting the current hart */
985*7536c9adSClément Léger 		if (sbi_sse_event_is_global(event_id)) {
986*7536c9adSClément Léger 			uret = sse_global_event_set_current_hart(event_id);
987*7536c9adSClément Léger 			if (uret)
988*7536c9adSClément Léger 				goto err;
989*7536c9adSClément Léger 		}
990*7536c9adSClément Léger 
991*7536c9adSClément Léger 		ret = sbi_sse_register(event_id, event_arg);
992*7536c9adSClément Léger 		if (ret.error) {
993*7536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "register event %s",
994*7536c9adSClément Léger 					    sse_event_name(event_id));
995*7536c9adSClément Léger 			goto err;
996*7536c9adSClément Léger 		}
997*7536c9adSClément Léger 		arg->state = SBI_SSE_STATE_REGISTERED;
998*7536c9adSClément Léger 
999*7536c9adSClément Léger 		value = arg->prio;
1000*7536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
1001*7536c9adSClément Léger 		if (ret.error) {
1002*7536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "set event %s priority",
1003*7536c9adSClément Léger 					    sse_event_name(event_id));
1004*7536c9adSClément Léger 			goto err;
1005*7536c9adSClément Léger 		}
1006*7536c9adSClément Léger 		ret = sbi_sse_enable(event_id);
1007*7536c9adSClément Léger 		if (ret.error) {
1008*7536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "enable event %s",
1009*7536c9adSClément Léger 					    sse_event_name(event_id));
1010*7536c9adSClément Léger 			goto err;
1011*7536c9adSClément Léger 		}
1012*7536c9adSClément Léger 		arg->state = SBI_SSE_STATE_ENABLED;
1013*7536c9adSClément Léger 	}
1014*7536c9adSClément Léger 
1015*7536c9adSClément Léger 	/* Inject first event */
1016*7536c9adSClément Léger 	ret = sbi_sse_inject(args[0]->event_id, current_thread_info()->hartid);
1017*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "injection");
1018*7536c9adSClément Léger 
1019*7536c9adSClément Léger 	/* Check that all handlers have been called */
1020*7536c9adSClément Léger 	for (i = 0; i < args_size; i++)
1021*7536c9adSClément Léger 		report(arg->called, "Event %s handler called", sse_event_name(args[i]->event_id));
1022*7536c9adSClément Léger 
1023*7536c9adSClément Léger err:
1024*7536c9adSClément Léger 	for (i = 0; i < args_size; i++) {
1025*7536c9adSClément Léger 		arg = args[i];
1026*7536c9adSClément Léger 		event_id = arg->event_id;
1027*7536c9adSClément Léger 
1028*7536c9adSClément Léger 		switch (arg->state) {
1029*7536c9adSClément Léger 		case SBI_SSE_STATE_ENABLED:
1030*7536c9adSClément Léger 			ret = sbi_sse_disable(event_id);
1031*7536c9adSClément Léger 			if (ret.error) {
1032*7536c9adSClément Léger 				sbiret_report_error(&ret, SBI_SUCCESS, "disable event 0x%x",
1033*7536c9adSClément Léger 						    event_id);
1034*7536c9adSClément Léger 				break;
1035*7536c9adSClément Léger 			}
1036*7536c9adSClément Léger 		case SBI_SSE_STATE_REGISTERED:
1037*7536c9adSClément Léger 			sbi_sse_unregister(event_id);
1038*7536c9adSClément Léger 			if (ret.error)
1039*7536c9adSClément Léger 				sbiret_report_error(&ret, SBI_SUCCESS, "unregister event 0x%x",
1040*7536c9adSClément Léger 						event_id);
1041*7536c9adSClément Léger 		default:
1042*7536c9adSClément Léger 			break;
1043*7536c9adSClément Léger 		}
1044*7536c9adSClément Léger 
1045*7536c9adSClément Léger 		event_arg = &event_args[i];
1046*7536c9adSClément Léger 		if (event_arg->stack)
1047*7536c9adSClément Léger 			sse_free_stack(event_arg->stack);
1048*7536c9adSClément Léger 	}
1049*7536c9adSClément Léger 
1050*7536c9adSClément Léger skip:
1051*7536c9adSClément Léger 	report_prefix_pop();
1052*7536c9adSClément Léger }
1053*7536c9adSClément Léger 
1054*7536c9adSClément Léger static struct priority_test_arg hi_prio_args[] = {
1055*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
1056*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE},
1057*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS},
1058*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS},
1059*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW},
1060*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS},
1061*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP},
1062*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS},
1063*7536c9adSClément Léger };
1064*7536c9adSClément Léger 
1065*7536c9adSClément Léger static struct priority_test_arg low_prio_args[] = {
1066*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS},
1067*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP},
1068*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS},
1069*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW},
1070*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS},
1071*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS},
1072*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE},
1073*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
1074*7536c9adSClément Léger };
1075*7536c9adSClément Léger 
1076*7536c9adSClément Léger static struct priority_test_arg prio_args[] = {
1077*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,		.prio = 5},
1078*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,		.prio = 10},
1079*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS,		.prio = 12},
1080*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW,		.prio = 15},
1081*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS,	.prio = 20},
1082*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS,		.prio = 22},
1083*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS,		.prio = 25},
1084*7536c9adSClément Léger };
1085*7536c9adSClément Léger 
1086*7536c9adSClément Léger static struct priority_test_arg same_prio_args[] = {
1087*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW,		.prio = 0},
1088*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS,		.prio = 0},
1089*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS,		.prio = 10},
1090*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,		.prio = 10},
1091*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,		.prio = 10},
1092*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS,	.prio = 20},
1093*7536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS,		.prio = 20},
1094*7536c9adSClément Léger };
1095*7536c9adSClément Léger 
1096*7536c9adSClément Léger static void sse_test_injection_priority(void)
1097*7536c9adSClément Léger {
1098*7536c9adSClément Léger 	report_prefix_push("prio");
1099*7536c9adSClément Léger 
1100*7536c9adSClément Léger 	sse_test_injection_priority_arg(hi_prio_args, ARRAY_SIZE(hi_prio_args),
1101*7536c9adSClément Léger 					sse_hi_priority_test_handler, "high");
1102*7536c9adSClément Léger 
1103*7536c9adSClément Léger 	sse_test_injection_priority_arg(low_prio_args, ARRAY_SIZE(low_prio_args),
1104*7536c9adSClément Léger 					sse_low_priority_test_handler, "low");
1105*7536c9adSClément Léger 
1106*7536c9adSClément Léger 	sse_test_injection_priority_arg(prio_args, ARRAY_SIZE(prio_args),
1107*7536c9adSClément Léger 					sse_low_priority_test_handler, "changed");
1108*7536c9adSClément Léger 
1109*7536c9adSClément Léger 	sse_test_injection_priority_arg(same_prio_args, ARRAY_SIZE(same_prio_args),
1110*7536c9adSClément Léger 					sse_low_priority_test_handler, "same_prio_args");
1111*7536c9adSClément Léger 
1112*7536c9adSClément Léger 	report_prefix_pop();
1113*7536c9adSClément Léger }
1114*7536c9adSClément Léger 
1115*7536c9adSClément Léger static void test_invalid_event_id(unsigned long event_id)
1116*7536c9adSClément Léger {
1117*7536c9adSClément Léger 	struct sbiret ret;
1118*7536c9adSClément Léger 	unsigned long value = 0;
1119*7536c9adSClément Léger 
1120*7536c9adSClément Léger 	ret = sbi_sse_register_raw(event_id, (unsigned long) sbi_sse_entry, 0);
1121*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
1122*7536c9adSClément Léger 			    "register event_id 0x%lx", event_id);
1123*7536c9adSClément Léger 
1124*7536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
1125*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
1126*7536c9adSClément Léger 			"unregister event_id 0x%lx", event_id);
1127*7536c9adSClément Léger 
1128*7536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
1129*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
1130*7536c9adSClément Léger 			    "enable event_id 0x%lx", event_id);
1131*7536c9adSClément Léger 
1132*7536c9adSClément Léger 	ret = sbi_sse_disable(event_id);
1133*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
1134*7536c9adSClément Léger 			    "disable event_id 0x%lx", event_id);
1135*7536c9adSClément Léger 
1136*7536c9adSClément Léger 	ret = sbi_sse_inject(event_id, 0);
1137*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
1138*7536c9adSClément Léger 			    "inject event_id 0x%lx", event_id);
1139*7536c9adSClément Léger 
1140*7536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
1141*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
1142*7536c9adSClément Léger 			    "write attr event_id 0x%lx", event_id);
1143*7536c9adSClément Léger 
1144*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
1145*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
1146*7536c9adSClément Léger 			    "read attr event_id 0x%lx", event_id);
1147*7536c9adSClément Léger }
1148*7536c9adSClément Léger 
1149*7536c9adSClément Léger static void sse_test_invalid_event_id(void)
1150*7536c9adSClément Léger {
1151*7536c9adSClément Léger 
1152*7536c9adSClément Léger 	report_prefix_push("event_id");
1153*7536c9adSClément Léger 
1154*7536c9adSClément Léger 	test_invalid_event_id(SBI_SSE_EVENT_LOCAL_RESERVED_0_START);
1155*7536c9adSClément Léger 
1156*7536c9adSClément Léger 	report_prefix_pop();
1157*7536c9adSClément Léger }
1158*7536c9adSClément Léger 
1159*7536c9adSClément Léger static void sse_check_event_availability(uint32_t event_id, bool *can_inject, bool *supported)
1160*7536c9adSClément Léger {
1161*7536c9adSClément Léger 	unsigned long status;
1162*7536c9adSClément Léger 	struct sbiret ret;
1163*7536c9adSClément Léger 
1164*7536c9adSClément Léger 	*can_inject = false;
1165*7536c9adSClément Léger 	*supported = false;
1166*7536c9adSClément Léger 
1167*7536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_STATUS, 1, &status);
1168*7536c9adSClément Léger 	if (ret.error != SBI_SUCCESS && ret.error != SBI_ERR_NOT_SUPPORTED) {
1169*7536c9adSClément Léger 		report_fail("Get event status != SBI_SUCCESS && != SBI_ERR_NOT_SUPPORTED: %ld",
1170*7536c9adSClément Léger 			    ret.error);
1171*7536c9adSClément Léger 		return;
1172*7536c9adSClément Léger 	}
1173*7536c9adSClément Léger 	if (ret.error == SBI_ERR_NOT_SUPPORTED)
1174*7536c9adSClément Léger 		return;
1175*7536c9adSClément Léger 
1176*7536c9adSClément Léger 	*supported = true;
1177*7536c9adSClément Léger 	*can_inject = (status >> SBI_SSE_ATTR_STATUS_INJECT_OFFSET) & 1;
1178*7536c9adSClément Léger }
1179*7536c9adSClément Léger 
1180*7536c9adSClément Léger static void sse_secondary_boot_and_unmask(void *data)
1181*7536c9adSClément Léger {
1182*7536c9adSClément Léger 	sbi_sse_hart_unmask();
1183*7536c9adSClément Léger }
1184*7536c9adSClément Léger 
1185*7536c9adSClément Léger static void sse_check_mask(void)
1186*7536c9adSClément Léger {
1187*7536c9adSClément Léger 	struct sbiret ret;
1188*7536c9adSClément Léger 
1189*7536c9adSClément Léger 	/* Upon boot, event are masked, check that */
1190*7536c9adSClément Léger 	ret = sbi_sse_hart_mask();
1191*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_ALREADY_STOPPED, "hart mask at boot time");
1192*7536c9adSClément Léger 
1193*7536c9adSClément Léger 	ret = sbi_sse_hart_unmask();
1194*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "hart unmask");
1195*7536c9adSClément Léger 	ret = sbi_sse_hart_unmask();
1196*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_ALREADY_STARTED, "hart unmask twice error");
1197*7536c9adSClément Léger 
1198*7536c9adSClément Léger 	ret = sbi_sse_hart_mask();
1199*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "hart mask");
1200*7536c9adSClément Léger 	ret = sbi_sse_hart_mask();
1201*7536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_ALREADY_STOPPED, "hart mask twice");
1202*7536c9adSClément Léger }
1203*7536c9adSClément Léger 
1204*7536c9adSClément Léger static void run_inject_test(struct sse_event_info *info)
1205*7536c9adSClément Léger {
1206*7536c9adSClément Léger 	unsigned long event_id = info->event_id;
1207*7536c9adSClément Léger 
1208*7536c9adSClément Léger 	if (!info->can_inject) {
1209*7536c9adSClément Léger 		report_skip("Event does not support injection, skipping injection tests");
1210*7536c9adSClément Léger 		return;
1211*7536c9adSClément Léger 	}
1212*7536c9adSClément Léger 
1213*7536c9adSClément Léger 	sse_test_inject_simple(event_id);
1214*7536c9adSClément Léger 
1215*7536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id))
1216*7536c9adSClément Léger 		sse_test_inject_global(event_id);
1217*7536c9adSClément Léger 	else
1218*7536c9adSClément Léger 		sse_test_inject_local(event_id);
1219*7536c9adSClément Léger }
1220*7536c9adSClément Léger 
1221*7536c9adSClément Léger void check_sse(void)
1222*7536c9adSClément Léger {
1223*7536c9adSClément Léger 	struct sse_event_info *info;
1224*7536c9adSClément Léger 	unsigned long i, event_id;
1225*7536c9adSClément Léger 	bool supported;
1226*7536c9adSClément Léger 
1227*7536c9adSClément Léger 	report_prefix_push("sse");
1228*7536c9adSClément Léger 
1229*7536c9adSClément Léger 	if (!sbi_probe(SBI_EXT_SSE)) {
1230*7536c9adSClément Léger 		report_skip("extension not available");
1231*7536c9adSClément Léger 		report_prefix_pop();
1232*7536c9adSClément Léger 		return;
1233*7536c9adSClément Léger 	}
1234*7536c9adSClément Léger 
1235*7536c9adSClément Léger 	if (sbi_get_imp_id() == SBI_IMPL_OPENSBI &&
1236*7536c9adSClément Léger 	    sbi_get_imp_version() < sbi_impl_opensbi_mk_version(1, 7)) {
1237*7536c9adSClément Léger 		report_skip("OpenSBI < v1.7 detected, skipping tests");
1238*7536c9adSClément Léger 		report_prefix_pop();
1239*7536c9adSClément Léger 		return;
1240*7536c9adSClément Léger 	}
1241*7536c9adSClément Léger 
1242*7536c9adSClément Léger 	sse_check_mask();
1243*7536c9adSClément Léger 
1244*7536c9adSClément Léger 	/*
1245*7536c9adSClément Léger 	 * Dummy wakeup of all processors since some of them will be targeted
1246*7536c9adSClément Léger 	 * by global events without going through the wakeup call as well as
1247*7536c9adSClément Léger 	 * unmasking SSE events on all harts
1248*7536c9adSClément Léger 	 */
1249*7536c9adSClément Léger 	on_cpus(sse_secondary_boot_and_unmask, NULL);
1250*7536c9adSClément Léger 
1251*7536c9adSClément Léger 	sse_test_invalid_event_id();
1252*7536c9adSClément Léger 
1253*7536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
1254*7536c9adSClément Léger 		info = &sse_event_infos[i];
1255*7536c9adSClément Léger 		event_id = info->event_id;
1256*7536c9adSClément Léger 		report_prefix_push(info->name);
1257*7536c9adSClément Léger 		sse_check_event_availability(event_id, &info->can_inject, &supported);
1258*7536c9adSClément Léger 		if (!supported) {
1259*7536c9adSClément Léger 			report_skip("Event is not supported, skipping tests");
1260*7536c9adSClément Léger 			report_prefix_pop();
1261*7536c9adSClément Léger 			continue;
1262*7536c9adSClément Léger 		}
1263*7536c9adSClément Léger 
1264*7536c9adSClément Léger 		sse_test_attrs(event_id);
1265*7536c9adSClément Léger 		sse_test_register_error(event_id);
1266*7536c9adSClément Léger 
1267*7536c9adSClément Léger 		run_inject_test(info);
1268*7536c9adSClément Léger 
1269*7536c9adSClément Léger 		report_prefix_pop();
1270*7536c9adSClément Léger 	}
1271*7536c9adSClément Léger 
1272*7536c9adSClément Léger 	sse_test_injection_priority();
1273*7536c9adSClément Léger 
1274*7536c9adSClément Léger 	report_prefix_pop();
1275*7536c9adSClément Léger }
1276