xref: /kvm-unit-tests/riscv/sbi-sse.c (revision 695740795adee59b48599e2f1a6bf19866a77779)
17536c9adSClément Léger // SPDX-License-Identifier: GPL-2.0-only
27536c9adSClément Léger /*
37536c9adSClément Léger  * SBI SSE testsuite
47536c9adSClément Léger  *
57536c9adSClément Léger  * Copyright (C) 2025, Rivos Inc., Clément Léger <cleger@rivosinc.com>
67536c9adSClément Léger  */
77536c9adSClément Léger #include <alloc.h>
87536c9adSClément Léger #include <alloc_page.h>
97536c9adSClément Léger #include <bitops.h>
107536c9adSClément Léger #include <cpumask.h>
117536c9adSClément Léger #include <libcflat.h>
127536c9adSClément Léger #include <on-cpus.h>
137536c9adSClément Léger #include <stdlib.h>
147536c9adSClément Léger 
157536c9adSClément Léger #include <asm/barrier.h>
167536c9adSClément Léger #include <asm/delay.h>
177536c9adSClément Léger #include <asm/io.h>
187536c9adSClément Léger #include <asm/page.h>
197536c9adSClément Léger #include <asm/processor.h>
207536c9adSClément Léger #include <asm/sbi.h>
217536c9adSClément Léger #include <asm/setup.h>
227536c9adSClément Léger #include <asm/timer.h>
237536c9adSClément Léger 
247536c9adSClément Léger #include "sbi-tests.h"
257536c9adSClément Léger 
267536c9adSClément Léger #define SSE_STACK_SIZE	PAGE_SIZE
277536c9adSClément Léger 
287536c9adSClément Léger struct sse_event_info {
297536c9adSClément Léger 	uint32_t event_id;
307536c9adSClément Léger 	const char *name;
317536c9adSClément Léger 	bool can_inject;
327536c9adSClément Léger };
337536c9adSClément Léger 
347536c9adSClément Léger static struct sse_event_info sse_event_infos[] = {
357536c9adSClément Léger 	{
367536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS,
377536c9adSClément Léger 		.name = "local_high_prio_ras",
387536c9adSClément Léger 	},
397536c9adSClément Léger 	{
407536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP,
417536c9adSClément Léger 		.name = "double_trap",
427536c9adSClément Léger 	},
437536c9adSClément Léger 	{
447536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS,
457536c9adSClément Léger 		.name = "global_high_prio_ras",
467536c9adSClément Léger 	},
477536c9adSClément Léger 	{
487536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW,
497536c9adSClément Léger 		.name = "local_pmu_overflow",
507536c9adSClément Léger 	},
517536c9adSClément Léger 	{
527536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS,
537536c9adSClément Léger 		.name = "local_low_prio_ras",
547536c9adSClément Léger 	},
557536c9adSClément Léger 	{
567536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS,
577536c9adSClément Léger 		.name = "global_low_prio_ras",
587536c9adSClément Léger 	},
597536c9adSClément Léger 	{
607536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,
617536c9adSClément Léger 		.name = "local_software",
627536c9adSClément Léger 	},
637536c9adSClément Léger 	{
647536c9adSClément Léger 		.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,
657536c9adSClément Léger 		.name = "global_software",
667536c9adSClément Léger 	},
677536c9adSClément Léger };
687536c9adSClément Léger 
697536c9adSClément Léger static const char *const attr_names[] = {
707536c9adSClément Léger 	[SBI_SSE_ATTR_STATUS] =			"status",
717536c9adSClément Léger 	[SBI_SSE_ATTR_PRIORITY] =		"priority",
727536c9adSClément Léger 	[SBI_SSE_ATTR_CONFIG] =			"config",
737536c9adSClément Léger 	[SBI_SSE_ATTR_PREFERRED_HART] =		"preferred_hart",
747536c9adSClément Léger 	[SBI_SSE_ATTR_ENTRY_PC] =		"entry_pc",
757536c9adSClément Léger 	[SBI_SSE_ATTR_ENTRY_ARG] =		"entry_arg",
767536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_SEPC] =	"interrupted_sepc",
777536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_FLAGS] =	"interrupted_flags",
787536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_A6] =		"interrupted_a6",
797536c9adSClément Léger 	[SBI_SSE_ATTR_INTERRUPTED_A7] =		"interrupted_a7",
807536c9adSClément Léger };
817536c9adSClément Léger 
827536c9adSClément Léger static const unsigned long ro_attrs[] = {
837536c9adSClément Léger 	SBI_SSE_ATTR_STATUS,
847536c9adSClément Léger 	SBI_SSE_ATTR_ENTRY_PC,
857536c9adSClément Léger 	SBI_SSE_ATTR_ENTRY_ARG,
867536c9adSClément Léger };
877536c9adSClément Léger 
887536c9adSClément Léger static const unsigned long interrupted_attrs[] = {
897536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_SEPC,
907536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS,
917536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_A6,
927536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_A7,
937536c9adSClément Léger };
947536c9adSClément Léger 
957536c9adSClément Léger static const unsigned long interrupted_flags[] = {
967536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPP,
977536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPIE,
987536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPELP,
997536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SDT,
1007536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV,
1017536c9adSClément Léger 	SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP,
1027536c9adSClément Léger };
1037536c9adSClément Léger 
sse_event_get_info(uint32_t event_id)1047536c9adSClément Léger static struct sse_event_info *sse_event_get_info(uint32_t event_id)
1057536c9adSClément Léger {
1067536c9adSClément Léger 	int i;
1077536c9adSClément Léger 
1087536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
1097536c9adSClément Léger 		if (sse_event_infos[i].event_id == event_id)
1107536c9adSClément Léger 			return &sse_event_infos[i];
1117536c9adSClément Léger 	}
1127536c9adSClément Léger 
1137536c9adSClément Léger 	assert_msg(false, "Invalid event id: %d", event_id);
1147536c9adSClément Léger }
1157536c9adSClément Léger 
sse_event_name(uint32_t event_id)1167536c9adSClément Léger static const char *sse_event_name(uint32_t event_id)
1177536c9adSClément Léger {
1187536c9adSClément Léger 	return sse_event_get_info(event_id)->name;
1197536c9adSClément Léger }
1207536c9adSClément Léger 
sse_event_can_inject(uint32_t event_id)1217536c9adSClément Léger static bool sse_event_can_inject(uint32_t event_id)
1227536c9adSClément Léger {
1237536c9adSClément Léger 	return sse_event_get_info(event_id)->can_inject;
1247536c9adSClément Léger }
1257536c9adSClément Léger 
sse_get_event_status_field(uint32_t event_id,unsigned long mask,unsigned long shift,unsigned long * value)1267536c9adSClément Léger static struct sbiret sse_get_event_status_field(uint32_t event_id, unsigned long mask,
1277536c9adSClément Léger 						unsigned long shift, unsigned long *value)
1287536c9adSClément Léger {
1297536c9adSClément Léger 	struct sbiret ret;
1307536c9adSClément Léger 	unsigned long status;
1317536c9adSClément Léger 
1327536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_STATUS, 1, &status);
1337536c9adSClément Léger 	if (ret.error) {
1347536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get event status");
1357536c9adSClément Léger 		return ret;
1367536c9adSClément Léger 	}
1377536c9adSClément Léger 
1387536c9adSClément Léger 	*value = (status & mask) >> shift;
1397536c9adSClément Léger 
1407536c9adSClément Léger 	return ret;
1417536c9adSClément Léger }
1427536c9adSClément Léger 
sse_event_get_state(uint32_t event_id,enum sbi_sse_state * state)1437536c9adSClément Léger static struct sbiret sse_event_get_state(uint32_t event_id, enum sbi_sse_state *state)
1447536c9adSClément Léger {
1457536c9adSClément Léger 	unsigned long status = 0;
1467536c9adSClément Léger 	struct sbiret ret;
1477536c9adSClément Léger 
1487536c9adSClément Léger 	ret = sse_get_event_status_field(event_id, SBI_SSE_ATTR_STATUS_STATE_MASK,
1497536c9adSClément Léger 					  SBI_SSE_ATTR_STATUS_STATE_OFFSET, &status);
1507536c9adSClément Léger 	*state = status;
1517536c9adSClément Léger 
1527536c9adSClément Léger 	return ret;
1537536c9adSClément Léger }
1547536c9adSClément Léger 
sse_global_event_set_current_hart(uint32_t event_id)1557536c9adSClément Léger static unsigned long sse_global_event_set_current_hart(uint32_t event_id)
1567536c9adSClément Léger {
1577536c9adSClément Léger 	struct sbiret ret;
1587536c9adSClément Léger 	unsigned long current_hart = current_thread_info()->hartid;
1597536c9adSClément Léger 
1607536c9adSClément Léger 	assert(sbi_sse_event_is_global(event_id));
1617536c9adSClément Léger 
1627536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &current_hart);
1637536c9adSClément Léger 	if (sbiret_report_error(&ret, SBI_SUCCESS, "Set preferred hart"))
1647536c9adSClément Léger 		return ret.error;
1657536c9adSClément Léger 
1667536c9adSClément Léger 	return 0;
1677536c9adSClément Léger }
1687536c9adSClément Léger 
sse_check_state(uint32_t event_id,unsigned long expected_state)1697536c9adSClément Léger static bool sse_check_state(uint32_t event_id, unsigned long expected_state)
1707536c9adSClément Léger {
1717536c9adSClément Léger 	struct sbiret ret;
1727536c9adSClément Léger 	enum sbi_sse_state state;
1737536c9adSClément Léger 
1747536c9adSClément Léger 	ret = sse_event_get_state(event_id, &state);
1757536c9adSClément Léger 	if (ret.error)
1767536c9adSClément Léger 		return false;
1777536c9adSClément Léger 
1787536c9adSClément Léger 	return report(state == expected_state, "event status == %ld", expected_state);
1797536c9adSClément Léger }
1807536c9adSClément Léger 
sse_event_pending(uint32_t event_id)1817536c9adSClément Léger static bool sse_event_pending(uint32_t event_id)
1827536c9adSClément Léger {
1837536c9adSClément Léger 	bool pending = 0;
1847536c9adSClément Léger 
1857536c9adSClément Léger 	sse_get_event_status_field(event_id, BIT(SBI_SSE_ATTR_STATUS_PENDING_OFFSET),
1867536c9adSClément Léger 		SBI_SSE_ATTR_STATUS_PENDING_OFFSET, (unsigned long *)&pending);
1877536c9adSClément Léger 
1887536c9adSClément Léger 	return pending;
1897536c9adSClément Léger }
1907536c9adSClément Léger 
sse_alloc_stack(void)1917536c9adSClément Léger static void *sse_alloc_stack(void)
1927536c9adSClément Léger {
1937536c9adSClément Léger 	/*
1947536c9adSClément Léger 	 * We assume that SSE_STACK_SIZE always fit in one page. This page will
1957536c9adSClément Léger 	 * always be decremented before storing anything on it in sse-entry.S.
1967536c9adSClément Léger 	 */
1977536c9adSClément Léger 	assert(SSE_STACK_SIZE <= PAGE_SIZE);
1987536c9adSClément Léger 
1997536c9adSClément Léger 	return (alloc_page() + SSE_STACK_SIZE);
2007536c9adSClément Léger }
2017536c9adSClément Léger 
sse_free_stack(void * stack)2027536c9adSClément Léger static void sse_free_stack(void *stack)
2037536c9adSClément Léger {
2047536c9adSClément Léger 	free_page(stack - SSE_STACK_SIZE);
2057536c9adSClément Léger }
2067536c9adSClément Léger 
sse_read_write_test(uint32_t event_id,unsigned long attr,unsigned long attr_count,unsigned long * value,long expected_error,const char * str)2077536c9adSClément Léger static void sse_read_write_test(uint32_t event_id, unsigned long attr, unsigned long attr_count,
2087536c9adSClément Léger 				unsigned long *value, long expected_error, const char *str)
2097536c9adSClément Léger {
2107536c9adSClément Léger 	struct sbiret ret;
2117536c9adSClément Léger 
2127536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, attr, attr_count, value);
2137536c9adSClément Léger 	sbiret_report_error(&ret, expected_error, "Read %s error", str);
2147536c9adSClément Léger 
2157536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, attr_count, value);
2167536c9adSClément Léger 	sbiret_report_error(&ret, expected_error, "Write %s error", str);
2177536c9adSClément Léger }
2187536c9adSClément Léger 
2197536c9adSClément Léger #define ALL_ATTRS_COUNT	(SBI_SSE_ATTR_INTERRUPTED_A7 + 1)
2207536c9adSClément Léger 
sse_test_attrs(uint32_t event_id)2217536c9adSClément Léger static void sse_test_attrs(uint32_t event_id)
2227536c9adSClément Léger {
2237536c9adSClément Léger 	unsigned long value = 0;
2247536c9adSClément Léger 	struct sbiret ret;
2257536c9adSClément Léger 	void *ptr;
2267536c9adSClément Léger 	unsigned long values[ALL_ATTRS_COUNT];
2277536c9adSClément Léger 	unsigned int i;
2287536c9adSClément Léger 	const char *invalid_hart_str;
2297536c9adSClément Léger 	const char *attr_name;
2307536c9adSClément Léger 
2317536c9adSClément Léger 	report_prefix_push("attrs");
2327536c9adSClément Léger 
2337536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(ro_attrs); i++) {
2347536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, ro_attrs[i], 1, &value);
2357536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_DENIED, "RO attribute %s not writable",
2367536c9adSClément Léger 				    attr_names[ro_attrs[i]]);
2377536c9adSClément Léger 	}
2387536c9adSClément Léger 
2397536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_STATUS, ALL_ATTRS_COUNT, values);
2407536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Read multiple attributes");
2417536c9adSClément Léger 
2427536c9adSClément Léger 	for (i = SBI_SSE_ATTR_STATUS; i <= SBI_SSE_ATTR_INTERRUPTED_A7; i++) {
2437536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, i, 1, &value);
2447536c9adSClément Léger 		attr_name = attr_names[i];
2457536c9adSClément Léger 
2467536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Read single attribute %s", attr_name);
2477536c9adSClément Léger 		if (values[i] != value)
2487536c9adSClément Léger 			report_fail("Attribute 0x%x single value read (0x%lx) differs from the one read with multiple attributes (0x%lx)",
2497536c9adSClément Léger 				    i, value, values[i]);
2507536c9adSClément Léger 		/*
2517536c9adSClément Léger 		 * Preferred hart reset value is defined by SBI vendor
2527536c9adSClément Léger 		 */
2537536c9adSClément Léger 		if (i != SBI_SSE_ATTR_PREFERRED_HART) {
2547536c9adSClément Léger 			/*
2557536c9adSClément Léger 			 * Specification states that injectable bit is implementation dependent
2567536c9adSClément Léger 			 * but other bits are zero-initialized.
2577536c9adSClément Léger 			 */
2587536c9adSClément Léger 			if (i == SBI_SSE_ATTR_STATUS)
2597536c9adSClément Léger 				value &= ~BIT(SBI_SSE_ATTR_STATUS_INJECT_OFFSET);
2607536c9adSClément Léger 			report(value == 0, "Attribute %s reset value is 0, found %lx", attr_name, value);
2617536c9adSClément Léger 		}
2627536c9adSClément Léger 	}
2637536c9adSClément Léger 
2647536c9adSClément Léger #if __riscv_xlen > 32
2657536c9adSClément Léger 	value = BIT(32);
2667536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
2677536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Write invalid prio > 0xFFFFFFFF error");
2687536c9adSClément Léger #endif
2697536c9adSClément Léger 
2707536c9adSClément Léger 	value = ~SBI_SSE_ATTR_CONFIG_ONESHOT;
2717536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_CONFIG, 1, &value);
2727536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Write invalid config value error");
2737536c9adSClément Léger 
2747536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id)) {
2757536c9adSClément Léger 		invalid_hart_str = getenv("INVALID_HART_ID");
2767536c9adSClément Léger 		if (!invalid_hart_str)
2777536c9adSClément Léger 			value = 0xFFFFFFFFUL;
2787536c9adSClément Léger 		else
2797536c9adSClément Léger 			value = strtoul(invalid_hart_str, NULL, 0);
2807536c9adSClément Léger 
2817536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
2827536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Set invalid hart id error");
2837536c9adSClément Léger 	} else {
2847536c9adSClément Léger 		/* Set Hart on local event -> RO */
2857536c9adSClément Léger 		value = current_thread_info()->hartid;
2867536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
2877536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_DENIED,
2887536c9adSClément Léger 				    "Set hart id on local event error");
2897536c9adSClément Léger 	}
2907536c9adSClément Léger 
2917536c9adSClément Léger 	/* Set/get flags, sepc, a6, a7 */
2927536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
2937536c9adSClément Léger 		attr_name = attr_names[interrupted_attrs[i]];
2947536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, interrupted_attrs[i], 1, &value);
2957536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get interrupted %s", attr_name);
2967536c9adSClément Léger 
2977536c9adSClément Léger 		value = ARRAY_SIZE(interrupted_attrs) - i;
2987536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, interrupted_attrs[i], 1, &value);
2997536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_INVALID_STATE,
3007536c9adSClément Léger 				    "Set attribute %s invalid state error", attr_name);
3017536c9adSClément Léger 	}
3027536c9adSClément Léger 
3037536c9adSClément Léger 	sse_read_write_test(event_id, SBI_SSE_ATTR_STATUS, 0, &value, SBI_ERR_INVALID_PARAM,
3047536c9adSClément Léger 			    "attribute attr_count == 0");
3057536c9adSClément Léger 	sse_read_write_test(event_id, SBI_SSE_ATTR_INTERRUPTED_A7 + 1, 1, &value, SBI_ERR_BAD_RANGE,
3067536c9adSClément Léger 			    "invalid attribute");
3077536c9adSClément Léger 
3087536c9adSClément Léger 	/* Misaligned pointer address */
3097536c9adSClément Léger 	ptr = (void *)&value;
3107536c9adSClément Léger 	ptr += 1;
3117536c9adSClément Léger 	sse_read_write_test(event_id, SBI_SSE_ATTR_STATUS, 1, ptr, SBI_ERR_INVALID_ADDRESS,
3127536c9adSClément Léger 		"attribute with invalid address");
3137536c9adSClément Léger 
3147536c9adSClément Léger 	report_prefix_pop();
3157536c9adSClément Léger }
3167536c9adSClément Léger 
sse_test_register_error(uint32_t event_id)3177536c9adSClément Léger static void sse_test_register_error(uint32_t event_id)
3187536c9adSClément Léger {
3197536c9adSClément Léger 	struct sbiret ret;
3207536c9adSClément Léger 
3217536c9adSClément Léger 	report_prefix_push("register");
3227536c9adSClément Léger 
3237536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
3247536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "unregister non-registered event");
3257536c9adSClément Léger 
3267536c9adSClément Léger 	ret = sbi_sse_register_raw(event_id, 0x1, 0);
3277536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "register misaligned entry");
3287536c9adSClément Léger 
3297536c9adSClément Léger 	ret = sbi_sse_register(event_id, NULL);
3307536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "register");
3317536c9adSClément Léger 	if (ret.error)
3327536c9adSClément Léger 		goto done;
3337536c9adSClément Léger 
3347536c9adSClément Léger 	ret = sbi_sse_register(event_id, NULL);
3357536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "register used event failure");
3367536c9adSClément Léger 
3377536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
3387536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "unregister");
3397536c9adSClément Léger 
3407536c9adSClément Léger done:
3417536c9adSClément Léger 	report_prefix_pop();
3427536c9adSClément Léger }
3437536c9adSClément Léger 
3447536c9adSClément Léger struct sse_simple_test_arg {
3457536c9adSClément Léger 	bool done;
3467536c9adSClément Léger 	unsigned long expected_a6;
3477536c9adSClément Léger 	uint32_t event_id;
3487536c9adSClément Léger };
3497536c9adSClément Léger 
3507536c9adSClément Léger #if __riscv_xlen > 32
3517536c9adSClément Léger 
3527536c9adSClément Léger struct alias_test_params {
3537536c9adSClément Léger 	unsigned long event_id;
3547536c9adSClément Léger 	unsigned long attr_id;
3557536c9adSClément Léger 	unsigned long attr_count;
3567536c9adSClément Léger 	const char *str;
3577536c9adSClément Léger };
3587536c9adSClément Léger 
test_alias(uint32_t event_id)3597536c9adSClément Léger static void test_alias(uint32_t event_id)
3607536c9adSClément Léger {
3617536c9adSClément Léger 	struct alias_test_params *write, *read;
3627536c9adSClément Léger 	unsigned long write_value, read_value;
3637536c9adSClément Léger 	struct sbiret ret;
3647536c9adSClément Léger 	bool err = false;
3657536c9adSClément Léger 	int r, w;
3667536c9adSClément Léger 	struct alias_test_params params[] = {
3677536c9adSClément Léger 		{event_id, SBI_SSE_ATTR_INTERRUPTED_A6, 1, "non aliased"},
3687536c9adSClément Léger 		{BIT(32) + event_id, SBI_SSE_ATTR_INTERRUPTED_A6, 1, "aliased event_id"},
3697536c9adSClément Léger 		{event_id, BIT(32) + SBI_SSE_ATTR_INTERRUPTED_A6, 1, "aliased attr_id"},
3707536c9adSClément Léger 		{event_id, SBI_SSE_ATTR_INTERRUPTED_A6, BIT(32) + 1, "aliased attr_count"},
3717536c9adSClément Léger 	};
3727536c9adSClément Léger 
3737536c9adSClément Léger 	report_prefix_push("alias");
3747536c9adSClément Léger 	for (w = 0; w < ARRAY_SIZE(params); w++) {
3757536c9adSClément Léger 		write = &params[w];
3767536c9adSClément Léger 
3777536c9adSClément Léger 		write_value = 0xDEADBEEF + w;
3787536c9adSClément Léger 		ret = sbi_sse_write_attrs(write->event_id, write->attr_id, write->attr_count, &write_value);
3797536c9adSClément Léger 		if (ret.error)
3807536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "Write %s, event 0x%lx attr 0x%lx, attr count 0x%lx",
3817536c9adSClément Léger 					    write->str, write->event_id, write->attr_id, write->attr_count);
3827536c9adSClément Léger 
3837536c9adSClément Léger 		for (r = 0; r < ARRAY_SIZE(params); r++) {
3847536c9adSClément Léger 			read = &params[r];
3857536c9adSClément Léger 			read_value = 0;
3867536c9adSClément Léger 			ret = sbi_sse_read_attrs(read->event_id, read->attr_id, read->attr_count, &read_value);
3877536c9adSClément Léger 			if (ret.error)
3887536c9adSClément Léger 				sbiret_report_error(&ret, SBI_SUCCESS,
3897536c9adSClément Léger 						    "Read %s, event 0x%lx attr 0x%lx, attr count 0x%lx",
3907536c9adSClément Léger 						    read->str, read->event_id, read->attr_id, read->attr_count);
3917536c9adSClément Léger 
3927536c9adSClément Léger 			/* Do not spam output with a lot of reports */
3937536c9adSClément Léger 			if (write_value != read_value) {
3947536c9adSClément Léger 				err = true;
3957536c9adSClément Léger 				report_fail("Write %s, event 0x%lx attr 0x%lx, attr count 0x%lx value %lx =="
3967536c9adSClément Léger 					    "Read %s, event 0x%lx attr 0x%lx, attr count 0x%lx value %lx",
3977536c9adSClément Léger 					    write->str, write->event_id, write->attr_id,
3987536c9adSClément Léger 					    write->attr_count, write_value, read->str,
3997536c9adSClément Léger 					    read->event_id, read->attr_id, read->attr_count,
4007536c9adSClément Léger 					    read_value);
4017536c9adSClément Léger 			}
4027536c9adSClément Léger 		}
4037536c9adSClément Léger 	}
4047536c9adSClément Léger 
4057536c9adSClément Léger 	report(!err, "BIT(32) aliasing tests");
4067536c9adSClément Léger 	report_prefix_pop();
4077536c9adSClément Léger }
4087536c9adSClément Léger #endif
4097536c9adSClément Léger 
sse_simple_handler(void * data,struct pt_regs * regs,unsigned int hartid)4107536c9adSClément Léger static void sse_simple_handler(void *data, struct pt_regs *regs, unsigned int hartid)
4117536c9adSClément Léger {
4127536c9adSClément Léger 	struct sse_simple_test_arg *arg = data;
4137536c9adSClément Léger 	int i;
4147536c9adSClément Léger 	struct sbiret ret;
4157536c9adSClément Léger 	const char *attr_name;
4167536c9adSClément Léger 	uint32_t event_id = READ_ONCE(arg->event_id), attr;
4177536c9adSClément Léger 	unsigned long value, prev_value, flags;
4187536c9adSClément Léger 	unsigned long interrupted_state[ARRAY_SIZE(interrupted_attrs)];
4197536c9adSClément Léger 	unsigned long modified_state[ARRAY_SIZE(interrupted_attrs)] = {4, 3, 2, 1};
4207536c9adSClément Léger 	unsigned long tmp_state[ARRAY_SIZE(interrupted_attrs)];
4217536c9adSClément Léger 
4227536c9adSClément Léger 	report((regs->status & SR_SPP) == SR_SPP, "Interrupted S-mode");
4237536c9adSClément Léger 	report(hartid == current_thread_info()->hartid, "Hartid correctly passed");
4247536c9adSClément Léger 	sse_check_state(event_id, SBI_SSE_STATE_RUNNING);
4257536c9adSClément Léger 	report(!sse_event_pending(event_id), "Event not pending");
4267536c9adSClément Léger 
4277536c9adSClément Léger 	/* Read full interrupted state */
4287536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
4297536c9adSClément Léger 				 ARRAY_SIZE(interrupted_attrs), interrupted_state);
4307536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Save full interrupted state from handler");
4317536c9adSClément Léger 
4327536c9adSClément Léger 	/* Write full modified state and read it */
4337536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
4347536c9adSClément Léger 				  ARRAY_SIZE(modified_state), modified_state);
4357536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS,
4367536c9adSClément Léger 			    "Write full interrupted state from handler");
4377536c9adSClément Léger 
4387536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
4397536c9adSClément Léger 				ARRAY_SIZE(tmp_state), tmp_state);
4407536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Read full modified state from handler");
4417536c9adSClément Léger 
4427536c9adSClément Léger 	report(memcmp(tmp_state, modified_state, sizeof(modified_state)) == 0,
4437536c9adSClément Léger 	       "Full interrupted state successfully written");
4447536c9adSClément Léger 
4457536c9adSClément Léger #if __riscv_xlen > 32
4467536c9adSClément Léger 	test_alias(event_id);
4477536c9adSClément Léger #endif
4487536c9adSClément Léger 
4497536c9adSClément Léger 	/* Restore full saved state */
4507536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_INTERRUPTED_SEPC,
4517536c9adSClément Léger 				  ARRAY_SIZE(interrupted_attrs), interrupted_state);
4527536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Full interrupted state restore from handler");
4537536c9adSClément Léger 
4547536c9adSClément Léger 	/* We test SBI_SSE_ATTR_INTERRUPTED_FLAGS below with specific flag values */
4557536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(interrupted_attrs); i++) {
4567536c9adSClément Léger 		attr = interrupted_attrs[i];
4577536c9adSClément Léger 		if (attr == SBI_SSE_ATTR_INTERRUPTED_FLAGS)
4587536c9adSClément Léger 			continue;
4597536c9adSClément Léger 
4607536c9adSClément Léger 		attr_name = attr_names[attr];
4617536c9adSClément Léger 
4627536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, attr, 1, &prev_value);
4637536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get attr %s", attr_name);
4647536c9adSClément Léger 
4657536c9adSClément Léger 		value = 0xDEADBEEF + i;
4667536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, attr, 1, &value);
4677536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Set attr %s", attr_name);
4687536c9adSClément Léger 
4697536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, attr, 1, &value);
4707536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get attr %s", attr_name);
4717536c9adSClément Léger 		report(value == 0xDEADBEEF + i, "Get attr %s, value: 0x%lx", attr_name, value);
4727536c9adSClément Léger 
4737536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, attr, 1, &prev_value);
4747536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Restore attr %s value", attr_name);
4757536c9adSClément Léger 	}
4767536c9adSClément Léger 
4777536c9adSClément Léger 	/* Test all flags allowed for SBI_SSE_ATTR_INTERRUPTED_FLAGS */
4787536c9adSClément Léger 	attr = SBI_SSE_ATTR_INTERRUPTED_FLAGS;
4797536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, attr, 1, &prev_value);
4807536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Save interrupted flags");
4817536c9adSClément Léger 
4827536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(interrupted_flags); i++) {
4837536c9adSClément Léger 		flags = interrupted_flags[i];
4847536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, attr, 1, &flags);
4857536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS,
4867536c9adSClément Léger 				    "Set interrupted flags bit 0x%lx value", flags);
4877536c9adSClément Léger 		ret = sbi_sse_read_attrs(event_id, attr, 1, &value);
4887536c9adSClément Léger 		sbiret_report_error(&ret, SBI_SUCCESS, "Get interrupted flags after set");
4897536c9adSClément Léger 		report(value == flags, "interrupted flags modified value: 0x%lx", value);
4907536c9adSClément Léger 	}
4917536c9adSClément Léger 
4927536c9adSClément Léger 	/* Write invalid bit in flag register */
4937536c9adSClément Léger 	flags = SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SDT << 1;
4947536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, 1, &flags);
4957536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Set invalid flags bit 0x%lx value error",
4967536c9adSClément Léger 			    flags);
4977536c9adSClément Léger 
4987536c9adSClément Léger #if __riscv_xlen > 32
4997536c9adSClément Léger 	flags = BIT(32);
5007536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, 1, &flags);
5017536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "Set invalid flags bit 0x%lx value error",
5027536c9adSClément Léger 			    flags);
5037536c9adSClément Léger #endif
5047536c9adSClément Léger 
5057536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, attr, 1, &prev_value);
5067536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Restore interrupted flags");
5077536c9adSClément Léger 
5087536c9adSClément Léger 	/* Try to change HARTID/Priority while running */
5097536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id)) {
5107536c9adSClément Léger 		value = current_thread_info()->hartid;
5117536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
5127536c9adSClément Léger 		sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "Set hart id while running error");
5137536c9adSClément Léger 	}
5147536c9adSClément Léger 
5157536c9adSClément Léger 	value = 0;
5167536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
5177536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_STATE, "Set priority while running error");
5187536c9adSClément Léger 
5197536c9adSClément Léger 	value = READ_ONCE(arg->expected_a6);
5207536c9adSClément Léger 	report(interrupted_state[2] == value, "Interrupted state a6, expected 0x%lx, got 0x%lx",
5217536c9adSClément Léger 	       value, interrupted_state[2]);
5227536c9adSClément Léger 
5237536c9adSClément Léger 	report(interrupted_state[3] == SBI_EXT_SSE,
5247536c9adSClément Léger 	       "Interrupted state a7, expected 0x%x, got 0x%lx", SBI_EXT_SSE,
5257536c9adSClément Léger 	       interrupted_state[3]);
5267536c9adSClément Léger 
5277536c9adSClément Léger 	WRITE_ONCE(arg->done, true);
5287536c9adSClément Léger }
5297536c9adSClément Léger 
sse_test_inject_simple(uint32_t event_id)5307536c9adSClément Léger static void sse_test_inject_simple(uint32_t event_id)
5317536c9adSClément Léger {
5327536c9adSClément Léger 	unsigned long value, error;
5337536c9adSClément Léger 	struct sbiret ret;
5347536c9adSClément Léger 	enum sbi_sse_state state = SBI_SSE_STATE_UNUSED;
5357536c9adSClément Léger 	struct sse_simple_test_arg test_arg = {.event_id = event_id};
5367536c9adSClément Léger 	struct sbi_sse_handler_arg args = {
5377536c9adSClément Léger 		.handler = sse_simple_handler,
5387536c9adSClément Léger 		.handler_data = (void *)&test_arg,
5397536c9adSClément Léger 		.stack = sse_alloc_stack(),
5407536c9adSClément Léger 	};
5417536c9adSClément Léger 
5427536c9adSClément Léger 	report_prefix_push("simple");
5437536c9adSClément Léger 
5447536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_UNUSED))
5457536c9adSClément Léger 		goto cleanup;
5467536c9adSClément Léger 
5477536c9adSClément Léger 	ret = sbi_sse_register(event_id, &args);
5487536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "register"))
5497536c9adSClément Léger 		goto cleanup;
5507536c9adSClément Léger 
5517536c9adSClément Léger 	state = SBI_SSE_STATE_REGISTERED;
5527536c9adSClément Léger 
5537536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_REGISTERED))
5547536c9adSClément Léger 		goto cleanup;
5557536c9adSClément Léger 
5567536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id)) {
5577536c9adSClément Léger 		/* Be sure global events are targeting the current hart */
5587536c9adSClément Léger 		error = sse_global_event_set_current_hart(event_id);
5597536c9adSClément Léger 		if (error)
5607536c9adSClément Léger 			goto cleanup;
5617536c9adSClément Léger 	}
5627536c9adSClément Léger 
5637536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
5647536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "enable"))
5657536c9adSClément Léger 		goto cleanup;
5667536c9adSClément Léger 
5677536c9adSClément Léger 	state = SBI_SSE_STATE_ENABLED;
5687536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_ENABLED))
5697536c9adSClément Léger 		goto cleanup;
5707536c9adSClément Léger 
5717536c9adSClément Léger 	ret = sbi_sse_hart_mask();
5727536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "hart mask"))
5737536c9adSClément Léger 		goto cleanup;
5747536c9adSClément Léger 
5757536c9adSClément Léger 	ret = sbi_sse_inject(event_id, current_thread_info()->hartid);
5767536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "injection masked")) {
5777536c9adSClément Léger 		sbi_sse_hart_unmask();
5787536c9adSClément Léger 		goto cleanup;
5797536c9adSClément Léger 	}
5807536c9adSClément Léger 
5817536c9adSClément Léger 	report(READ_ONCE(test_arg.done) == 0, "event masked not handled");
5827536c9adSClément Léger 
5837536c9adSClément Léger 	/*
5847536c9adSClément Léger 	 * When unmasking the SSE events, we expect it to be injected
5857536c9adSClément Léger 	 * immediately so a6 should be SBI_EXT_SBI_SSE_HART_UNMASK
5867536c9adSClément Léger 	 */
5877536c9adSClément Léger 	WRITE_ONCE(test_arg.expected_a6, SBI_EXT_SSE_HART_UNMASK);
5887536c9adSClément Léger 	ret = sbi_sse_hart_unmask();
5897536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "hart unmask"))
5907536c9adSClément Léger 		goto cleanup;
5917536c9adSClément Léger 
5927536c9adSClément Léger 	report(READ_ONCE(test_arg.done) == 1, "event unmasked handled");
5937536c9adSClément Léger 	WRITE_ONCE(test_arg.done, 0);
5947536c9adSClément Léger 	WRITE_ONCE(test_arg.expected_a6, SBI_EXT_SSE_INJECT);
5957536c9adSClément Léger 
5967536c9adSClément Léger 	/* Set as oneshot and verify it is disabled */
5977536c9adSClément Léger 	ret = sbi_sse_disable(event_id);
5987536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Disable event")) {
5997536c9adSClément Léger 		/* Nothing we can really do here, event can not be disabled */
6007536c9adSClément Léger 		goto cleanup;
6017536c9adSClément Léger 	}
6027536c9adSClément Léger 	state = SBI_SSE_STATE_REGISTERED;
6037536c9adSClément Léger 
6047536c9adSClément Léger 	value = SBI_SSE_ATTR_CONFIG_ONESHOT;
6057536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_CONFIG, 1, &value);
6067536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Set event attribute as ONESHOT"))
6077536c9adSClément Léger 		goto cleanup;
6087536c9adSClément Léger 
6097536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
6107536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Enable event"))
6117536c9adSClément Léger 		goto cleanup;
6127536c9adSClément Léger 	state = SBI_SSE_STATE_ENABLED;
6137536c9adSClément Léger 
6147536c9adSClément Léger 	ret = sbi_sse_inject(event_id, current_thread_info()->hartid);
6157536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "second injection"))
6167536c9adSClément Léger 		goto cleanup;
6177536c9adSClément Léger 
6187536c9adSClément Léger 	report(READ_ONCE(test_arg.done) == 1, "event handled");
6197536c9adSClément Léger 	WRITE_ONCE(test_arg.done, 0);
6207536c9adSClément Léger 
6217536c9adSClément Léger 	if (!sse_check_state(event_id, SBI_SSE_STATE_REGISTERED))
6227536c9adSClément Léger 		goto cleanup;
6237536c9adSClément Léger 	state = SBI_SSE_STATE_REGISTERED;
6247536c9adSClément Léger 
6257536c9adSClément Léger 	/* Clear ONESHOT FLAG */
6267536c9adSClément Léger 	value = 0;
6277536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_CONFIG, 1, &value);
6287536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Clear CONFIG.ONESHOT flag"))
6297536c9adSClément Léger 		goto cleanup;
6307536c9adSClément Léger 
6317536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
6327536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "unregister"))
6337536c9adSClément Léger 		goto cleanup;
6347536c9adSClément Léger 	state = SBI_SSE_STATE_UNUSED;
6357536c9adSClément Léger 
6367536c9adSClément Léger 	sse_check_state(event_id, SBI_SSE_STATE_UNUSED);
6377536c9adSClément Léger 
6387536c9adSClément Léger cleanup:
6397536c9adSClément Léger 	switch (state) {
6407536c9adSClément Léger 	case SBI_SSE_STATE_ENABLED:
6417536c9adSClément Léger 		ret = sbi_sse_disable(event_id);
6427536c9adSClément Léger 		if (ret.error) {
6437536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "disable event 0x%x", event_id);
6447536c9adSClément Léger 			break;
6457536c9adSClément Léger 		}
6467536c9adSClément Léger 	case SBI_SSE_STATE_REGISTERED:
6477536c9adSClément Léger 		sbi_sse_unregister(event_id);
6487536c9adSClément Léger 		if (ret.error)
6497536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "unregister event 0x%x", event_id);
6507536c9adSClément Léger 	default:
6517536c9adSClément Léger 		break;
6527536c9adSClément Léger 	}
6537536c9adSClément Léger 
6547536c9adSClément Léger 	sse_free_stack(args.stack);
6557536c9adSClément Léger 	report_prefix_pop();
6567536c9adSClément Léger }
6577536c9adSClément Léger 
6587536c9adSClément Léger struct sse_foreign_cpu_test_arg {
6597536c9adSClément Léger 	bool done;
6607536c9adSClément Léger 	unsigned int expected_cpu;
6617536c9adSClément Léger 	uint32_t event_id;
6627536c9adSClément Léger };
6637536c9adSClément Léger 
sse_foreign_cpu_handler(void * data,struct pt_regs * regs,unsigned int hartid)6647536c9adSClément Léger static void sse_foreign_cpu_handler(void *data, struct pt_regs *regs, unsigned int hartid)
6657536c9adSClément Léger {
6667536c9adSClément Léger 	struct sse_foreign_cpu_test_arg *arg = data;
6677536c9adSClément Léger 	unsigned int expected_cpu;
6687536c9adSClément Léger 
6697536c9adSClément Léger 	/* For arg content to be visible */
6707536c9adSClément Léger 	smp_rmb();
6717536c9adSClément Léger 	expected_cpu = READ_ONCE(arg->expected_cpu);
6727536c9adSClément Léger 	report(expected_cpu == current_thread_info()->cpu,
6737536c9adSClément Léger 	       "Received event on CPU (%d), expected CPU (%d)", current_thread_info()->cpu,
6747536c9adSClément Léger 	       expected_cpu);
6757536c9adSClément Léger 
6767536c9adSClément Léger 	WRITE_ONCE(arg->done, true);
6777536c9adSClément Léger 	/* For arg update to be visible for other CPUs */
6787536c9adSClément Léger 	smp_wmb();
6797536c9adSClément Léger }
6807536c9adSClément Léger 
6817536c9adSClément Léger struct sse_local_per_cpu {
6827536c9adSClément Léger 	struct sbi_sse_handler_arg args;
6837536c9adSClément Léger 	struct sbiret ret;
6847536c9adSClément Léger 	struct sse_foreign_cpu_test_arg handler_arg;
6857536c9adSClément Léger 	enum sbi_sse_state state;
6867536c9adSClément Léger };
6877536c9adSClément Léger 
sse_register_enable_local(void * data)6887536c9adSClément Léger static void sse_register_enable_local(void *data)
6897536c9adSClément Léger {
6907536c9adSClément Léger 	struct sbiret ret;
6917536c9adSClément Léger 	struct sse_local_per_cpu *cpu_args = data;
6927536c9adSClément Léger 	struct sse_local_per_cpu *cpu_arg = &cpu_args[current_thread_info()->cpu];
6937536c9adSClément Léger 	uint32_t event_id = cpu_arg->handler_arg.event_id;
6947536c9adSClément Léger 
6957536c9adSClément Léger 	ret = sbi_sse_register(event_id, &cpu_arg->args);
6967536c9adSClément Léger 	WRITE_ONCE(cpu_arg->ret, ret);
6977536c9adSClément Léger 	if (ret.error)
6987536c9adSClément Léger 		return;
6997536c9adSClément Léger 	cpu_arg->state = SBI_SSE_STATE_REGISTERED;
7007536c9adSClément Léger 
7017536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
7027536c9adSClément Léger 	WRITE_ONCE(cpu_arg->ret, ret);
7037536c9adSClément Léger 	if (ret.error)
7047536c9adSClément Léger 		return;
7057536c9adSClément Léger 	cpu_arg->state = SBI_SSE_STATE_ENABLED;
7067536c9adSClément Léger }
7077536c9adSClément Léger 
sbi_sse_disable_unregister_local(void * data)7087536c9adSClément Léger static void sbi_sse_disable_unregister_local(void *data)
7097536c9adSClément Léger {
7107536c9adSClément Léger 	struct sbiret ret;
7117536c9adSClément Léger 	struct sse_local_per_cpu *cpu_args = data;
7127536c9adSClément Léger 	struct sse_local_per_cpu *cpu_arg = &cpu_args[current_thread_info()->cpu];
7137536c9adSClément Léger 	uint32_t event_id = cpu_arg->handler_arg.event_id;
7147536c9adSClément Léger 
7157536c9adSClément Léger 	switch (cpu_arg->state) {
7167536c9adSClément Léger 	case SBI_SSE_STATE_ENABLED:
7177536c9adSClément Léger 		ret = sbi_sse_disable(event_id);
7187536c9adSClément Léger 		WRITE_ONCE(cpu_arg->ret, ret);
7197536c9adSClément Léger 		if (ret.error)
7207536c9adSClément Léger 			return;
7217536c9adSClément Léger 	case SBI_SSE_STATE_REGISTERED:
7227536c9adSClément Léger 		ret = sbi_sse_unregister(event_id);
7237536c9adSClément Léger 		WRITE_ONCE(cpu_arg->ret, ret);
7247536c9adSClément Léger 	default:
7257536c9adSClément Léger 		break;
7267536c9adSClément Léger 	}
7277536c9adSClément Léger }
7287536c9adSClément Léger 
sse_event_get_complete_timeout(void)7297536c9adSClément Léger static uint64_t sse_event_get_complete_timeout(void)
7307536c9adSClément Léger {
7317536c9adSClément Léger 	char *event_complete_timeout_str;
7327536c9adSClément Léger 	uint64_t timeout;
7337536c9adSClément Léger 
7347536c9adSClément Léger 	event_complete_timeout_str = getenv("SSE_EVENT_COMPLETE_TIMEOUT");
7357536c9adSClément Léger 	if (!event_complete_timeout_str)
7367536c9adSClément Léger 		timeout = 3000;
7377536c9adSClément Léger 	else
7387536c9adSClément Léger 		timeout = strtoul(event_complete_timeout_str, NULL, 0);
7397536c9adSClément Léger 
7407536c9adSClément Léger 	return timer_get_cycles() + usec_to_cycles(timeout);
7417536c9adSClément Léger }
7427536c9adSClément Léger 
sse_test_inject_local(uint32_t event_id)7437536c9adSClément Léger static void sse_test_inject_local(uint32_t event_id)
7447536c9adSClément Léger {
7457536c9adSClément Léger 	int cpu;
7467536c9adSClément Léger 	uint64_t timeout;
7477536c9adSClément Léger 	struct sbiret ret;
7487536c9adSClément Léger 	struct sse_local_per_cpu *cpu_args, *cpu_arg;
7497536c9adSClément Léger 	struct sse_foreign_cpu_test_arg *handler_arg;
7507536c9adSClément Léger 
7517536c9adSClément Léger 	cpu_args = calloc(NR_CPUS, sizeof(struct sbi_sse_handler_arg));
7527536c9adSClément Léger 
7537536c9adSClément Léger 	report_prefix_push("local_dispatch");
7547536c9adSClément Léger 	for_each_online_cpu(cpu) {
7557536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
7567536c9adSClément Léger 		cpu_arg->handler_arg.event_id = event_id;
7577536c9adSClément Léger 		cpu_arg->args.stack = sse_alloc_stack();
7587536c9adSClément Léger 		cpu_arg->args.handler = sse_foreign_cpu_handler;
7597536c9adSClément Léger 		cpu_arg->args.handler_data = (void *)&cpu_arg->handler_arg;
7607536c9adSClément Léger 		cpu_arg->state = SBI_SSE_STATE_UNUSED;
7617536c9adSClément Léger 	}
7627536c9adSClément Léger 
7637536c9adSClément Léger 	on_cpus(sse_register_enable_local, cpu_args);
7647536c9adSClément Léger 	for_each_online_cpu(cpu) {
7657536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
7667536c9adSClément Léger 		ret = cpu_arg->ret;
7677536c9adSClément Léger 		if (ret.error) {
7687536c9adSClément Léger 			report_fail("CPU failed to register/enable event: %ld", ret.error);
7697536c9adSClément Léger 			goto cleanup;
7707536c9adSClément Léger 		}
7717536c9adSClément Léger 
7727536c9adSClément Léger 		handler_arg = &cpu_arg->handler_arg;
7737536c9adSClément Léger 		WRITE_ONCE(handler_arg->expected_cpu, cpu);
7747536c9adSClément Léger 		/* For handler_arg content to be visible for other CPUs */
7757536c9adSClément Léger 		smp_wmb();
7767536c9adSClément Léger 		ret = sbi_sse_inject(event_id, cpus[cpu].hartid);
7777536c9adSClément Léger 		if (ret.error) {
7787536c9adSClément Léger 			report_fail("CPU failed to inject event: %ld", ret.error);
7797536c9adSClément Léger 			goto cleanup;
7807536c9adSClément Léger 		}
7817536c9adSClément Léger 	}
7827536c9adSClément Léger 
7837536c9adSClément Léger 	for_each_online_cpu(cpu) {
7847536c9adSClément Léger 		handler_arg = &cpu_args[cpu].handler_arg;
7857536c9adSClément Léger 		smp_rmb();
7867536c9adSClément Léger 
7877536c9adSClément Léger 		timeout = sse_event_get_complete_timeout();
7887536c9adSClément Léger 		while (!READ_ONCE(handler_arg->done) && timer_get_cycles() < timeout) {
7897536c9adSClément Léger 			/* For handler_arg update to be visible */
7907536c9adSClément Léger 			smp_rmb();
7917536c9adSClément Léger 			cpu_relax();
7927536c9adSClément Léger 		}
7937536c9adSClément Léger 		report(READ_ONCE(handler_arg->done), "Event handled");
7947536c9adSClément Léger 		WRITE_ONCE(handler_arg->done, false);
7957536c9adSClément Léger 	}
7967536c9adSClément Léger 
7977536c9adSClément Léger cleanup:
7987536c9adSClément Léger 	on_cpus(sbi_sse_disable_unregister_local, cpu_args);
7997536c9adSClément Léger 	for_each_online_cpu(cpu) {
8007536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
8017536c9adSClément Léger 		ret = READ_ONCE(cpu_arg->ret);
8027536c9adSClément Léger 		if (ret.error)
8037536c9adSClément Léger 			report_fail("CPU failed to disable/unregister event: %ld", ret.error);
8047536c9adSClément Léger 	}
8057536c9adSClément Léger 
8067536c9adSClément Léger 	for_each_online_cpu(cpu) {
8077536c9adSClément Léger 		cpu_arg = &cpu_args[cpu];
8087536c9adSClément Léger 		sse_free_stack(cpu_arg->args.stack);
8097536c9adSClément Léger 	}
8107536c9adSClément Léger 
8117536c9adSClément Léger 	report_prefix_pop();
8127536c9adSClément Léger }
8137536c9adSClément Léger 
sse_test_inject_global_cpu(uint32_t event_id,unsigned int cpu,struct sse_foreign_cpu_test_arg * test_arg)8147536c9adSClément Léger static void sse_test_inject_global_cpu(uint32_t event_id, unsigned int cpu,
8157536c9adSClément Léger 				       struct sse_foreign_cpu_test_arg *test_arg)
8167536c9adSClément Léger {
8177536c9adSClément Léger 	unsigned long value;
8187536c9adSClément Léger 	struct sbiret ret;
8197536c9adSClément Léger 	uint64_t timeout;
8207536c9adSClément Léger 	enum sbi_sse_state state;
8217536c9adSClément Léger 
8227536c9adSClément Léger 	WRITE_ONCE(test_arg->expected_cpu, cpu);
8237536c9adSClément Léger 	/* For test_arg content to be visible for other CPUs */
8247536c9adSClément Léger 	smp_wmb();
8257536c9adSClément Léger 	value = cpu;
8267536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PREFERRED_HART, 1, &value);
8277536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Set preferred hart"))
8287536c9adSClément Léger 		return;
8297536c9adSClément Léger 
8307536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
8317536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Enable event"))
8327536c9adSClément Léger 		return;
8337536c9adSClément Léger 
8347536c9adSClément Léger 	ret = sbi_sse_inject(event_id, cpu);
8357536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Inject event"))
8367536c9adSClément Léger 		goto disable;
8377536c9adSClément Léger 
8387536c9adSClément Léger 	smp_rmb();
8397536c9adSClément Léger 	timeout = sse_event_get_complete_timeout();
8407536c9adSClément Léger 	while (!READ_ONCE(test_arg->done) && timer_get_cycles() < timeout) {
8417536c9adSClément Léger 		/* For shared test_arg structure */
8427536c9adSClément Léger 		smp_rmb();
8437536c9adSClément Léger 		cpu_relax();
8447536c9adSClément Léger 	}
8457536c9adSClément Léger 
8467536c9adSClément Léger 	report(READ_ONCE(test_arg->done), "event handler called");
8477536c9adSClément Léger 	WRITE_ONCE(test_arg->done, false);
8487536c9adSClément Léger 
8497536c9adSClément Léger 	timeout = sse_event_get_complete_timeout();
8507536c9adSClément Léger 	/* Wait for event to be back in ENABLED state */
8517536c9adSClément Léger 	do {
8527536c9adSClément Léger 		ret = sse_event_get_state(event_id, &state);
8537536c9adSClément Léger 		if (ret.error)
8547536c9adSClément Léger 			goto disable;
8557536c9adSClément Léger 		cpu_relax();
8567536c9adSClément Léger 	} while (state != SBI_SSE_STATE_ENABLED && timer_get_cycles() < timeout);
8577536c9adSClément Léger 
8587536c9adSClément Léger 	report(state == SBI_SSE_STATE_ENABLED, "Event in enabled state");
8597536c9adSClément Léger 
8607536c9adSClément Léger disable:
8617536c9adSClément Léger 	ret = sbi_sse_disable(event_id);
8627536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Disable event");
8637536c9adSClément Léger }
8647536c9adSClément Léger 
sse_test_inject_global(uint32_t event_id)8657536c9adSClément Léger static void sse_test_inject_global(uint32_t event_id)
8667536c9adSClément Léger {
8677536c9adSClément Léger 	struct sbiret ret;
8687536c9adSClément Léger 	unsigned int cpu;
8697536c9adSClément Léger 	struct sse_foreign_cpu_test_arg test_arg = {.event_id = event_id};
8707536c9adSClément Léger 	struct sbi_sse_handler_arg args = {
8717536c9adSClément Léger 		.handler = sse_foreign_cpu_handler,
8727536c9adSClément Léger 		.handler_data = (void *)&test_arg,
8737536c9adSClément Léger 		.stack = sse_alloc_stack(),
8747536c9adSClément Léger 	};
8757536c9adSClément Léger 
8767536c9adSClément Léger 	report_prefix_push("global_dispatch");
8777536c9adSClément Léger 
8787536c9adSClément Léger 	ret = sbi_sse_register(event_id, &args);
8797536c9adSClément Léger 	if (!sbiret_report_error(&ret, SBI_SUCCESS, "Register event"))
8807536c9adSClément Léger 		goto err;
8817536c9adSClément Léger 
8827536c9adSClément Léger 	for_each_online_cpu(cpu)
8837536c9adSClément Léger 		sse_test_inject_global_cpu(event_id, cpu, &test_arg);
8847536c9adSClément Léger 
8857536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
8867536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "Unregister event");
8877536c9adSClément Léger 
8887536c9adSClément Léger err:
8897536c9adSClément Léger 	sse_free_stack(args.stack);
8907536c9adSClément Léger 	report_prefix_pop();
8917536c9adSClément Léger }
8927536c9adSClément Léger 
8937536c9adSClément Léger struct priority_test_arg {
8947536c9adSClément Léger 	uint32_t event_id;
8957536c9adSClément Léger 	bool called;
8967536c9adSClément Léger 	u32 prio;
8977536c9adSClément Léger 	enum sbi_sse_state state; /* Used for error handling */
8987536c9adSClément Léger 	struct priority_test_arg *next_event_arg;
8997536c9adSClément Léger 	void (*check_func)(struct priority_test_arg *arg);
9007536c9adSClément Léger };
9017536c9adSClément Léger 
sse_hi_priority_test_handler(void * arg,struct pt_regs * regs,unsigned int hartid)9027536c9adSClément Léger static void sse_hi_priority_test_handler(void *arg, struct pt_regs *regs,
9037536c9adSClément Léger 					 unsigned int hartid)
9047536c9adSClément Léger {
9057536c9adSClément Léger 	struct priority_test_arg *targ = arg;
9067536c9adSClément Léger 	struct priority_test_arg *next = targ->next_event_arg;
9077536c9adSClément Léger 
9087536c9adSClément Léger 	targ->called = true;
9097536c9adSClément Léger 	if (next) {
9107536c9adSClément Léger 		sbi_sse_inject(next->event_id, current_thread_info()->hartid);
9117536c9adSClément Léger 
9127536c9adSClément Léger 		report(!sse_event_pending(next->event_id), "Higher priority event is not pending");
9137536c9adSClément Léger 		report(next->called, "Higher priority event was handled");
9147536c9adSClément Léger 	}
9157536c9adSClément Léger }
9167536c9adSClément Léger 
sse_low_priority_test_handler(void * arg,struct pt_regs * regs,unsigned int hartid)9177536c9adSClément Léger static void sse_low_priority_test_handler(void *arg, struct pt_regs *regs,
9187536c9adSClément Léger 					  unsigned int hartid)
9197536c9adSClément Léger {
9207536c9adSClément Léger 	struct priority_test_arg *targ = arg;
9217536c9adSClément Léger 	struct priority_test_arg *next = targ->next_event_arg;
9227536c9adSClément Léger 
9237536c9adSClément Léger 	targ->called = true;
9247536c9adSClément Léger 
9257536c9adSClément Léger 	if (next) {
9267536c9adSClément Léger 		sbi_sse_inject(next->event_id, current_thread_info()->hartid);
9277536c9adSClément Léger 
9287536c9adSClément Léger 		report(sse_event_pending(next->event_id), "Lower priority event is pending");
9297536c9adSClément Léger 		report(!next->called, "Lower priority event %s was not handled before %s",
9307536c9adSClément Léger 		       sse_event_name(next->event_id), sse_event_name(targ->event_id));
9317536c9adSClément Léger 	}
9327536c9adSClément Léger }
9337536c9adSClément Léger 
sse_test_injection_priority_arg(struct priority_test_arg * in_args,unsigned int in_args_size,sbi_sse_handler_fn handler,const char * test_name)9347536c9adSClément Léger static void sse_test_injection_priority_arg(struct priority_test_arg *in_args,
9357536c9adSClément Léger 					    unsigned int in_args_size,
9367536c9adSClément Léger 					    sbi_sse_handler_fn handler,
9377536c9adSClément Léger 					    const char *test_name)
9387536c9adSClément Léger {
9397536c9adSClément Léger 	unsigned int i;
9407536c9adSClément Léger 	unsigned long value, uret;
9417536c9adSClément Léger 	struct sbiret ret;
9427536c9adSClément Léger 	uint32_t event_id;
9437536c9adSClément Léger 	struct priority_test_arg *arg;
9447536c9adSClément Léger 	unsigned int args_size = 0;
9457536c9adSClément Léger 	struct sbi_sse_handler_arg event_args[in_args_size];
9467536c9adSClément Léger 	struct priority_test_arg *args[in_args_size];
9477536c9adSClément Léger 	void *stack;
9487536c9adSClément Léger 	struct sbi_sse_handler_arg *event_arg;
9497536c9adSClément Léger 
9507536c9adSClément Léger 	report_prefix_push(test_name);
9517536c9adSClément Léger 
9527536c9adSClément Léger 	for (i = 0; i < in_args_size; i++) {
9537536c9adSClément Léger 		arg = &in_args[i];
9547536c9adSClément Léger 		arg->state = SBI_SSE_STATE_UNUSED;
9557536c9adSClément Léger 		event_id = arg->event_id;
9567536c9adSClément Léger 		if (!sse_event_can_inject(event_id))
9577536c9adSClément Léger 			continue;
9587536c9adSClément Léger 
9597536c9adSClément Léger 		args[args_size] = arg;
9607536c9adSClément Léger 		args_size++;
9617536c9adSClément Léger 		event_args->stack = 0;
9627536c9adSClément Léger 	}
9637536c9adSClément Léger 
9647536c9adSClément Léger 	if (!args_size) {
9657536c9adSClément Léger 		report_skip("No injectable events");
9667536c9adSClément Léger 		goto skip;
9677536c9adSClément Léger 	}
9687536c9adSClément Léger 
9697536c9adSClément Léger 	for (i = 0; i < args_size; i++) {
9707536c9adSClément Léger 		arg = args[i];
9717536c9adSClément Léger 		event_id = arg->event_id;
9727536c9adSClément Léger 		stack = sse_alloc_stack();
9737536c9adSClément Léger 
9747536c9adSClément Léger 		event_arg = &event_args[i];
9757536c9adSClément Léger 		event_arg->handler = handler;
9767536c9adSClément Léger 		event_arg->handler_data = (void *)arg;
9777536c9adSClément Léger 		event_arg->stack = stack;
9787536c9adSClément Léger 
9797536c9adSClément Léger 		if (i < (args_size - 1))
9807536c9adSClément Léger 			arg->next_event_arg = args[i + 1];
9817536c9adSClément Léger 		else
9827536c9adSClément Léger 			arg->next_event_arg = NULL;
9837536c9adSClément Léger 
9847536c9adSClément Léger 		/* Be sure global events are targeting the current hart */
9857536c9adSClément Léger 		if (sbi_sse_event_is_global(event_id)) {
9867536c9adSClément Léger 			uret = sse_global_event_set_current_hart(event_id);
9877536c9adSClément Léger 			if (uret)
9887536c9adSClément Léger 				goto err;
9897536c9adSClément Léger 		}
9907536c9adSClément Léger 
9917536c9adSClément Léger 		ret = sbi_sse_register(event_id, event_arg);
9927536c9adSClément Léger 		if (ret.error) {
9937536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "register event %s",
9947536c9adSClément Léger 					    sse_event_name(event_id));
9957536c9adSClément Léger 			goto err;
9967536c9adSClément Léger 		}
9977536c9adSClément Léger 		arg->state = SBI_SSE_STATE_REGISTERED;
9987536c9adSClément Léger 
9997536c9adSClément Léger 		value = arg->prio;
10007536c9adSClément Léger 		ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
10017536c9adSClément Léger 		if (ret.error) {
10027536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "set event %s priority",
10037536c9adSClément Léger 					    sse_event_name(event_id));
10047536c9adSClément Léger 			goto err;
10057536c9adSClément Léger 		}
10067536c9adSClément Léger 		ret = sbi_sse_enable(event_id);
10077536c9adSClément Léger 		if (ret.error) {
10087536c9adSClément Léger 			sbiret_report_error(&ret, SBI_SUCCESS, "enable event %s",
10097536c9adSClément Léger 					    sse_event_name(event_id));
10107536c9adSClément Léger 			goto err;
10117536c9adSClément Léger 		}
10127536c9adSClément Léger 		arg->state = SBI_SSE_STATE_ENABLED;
10137536c9adSClément Léger 	}
10147536c9adSClément Léger 
10157536c9adSClément Léger 	/* Inject first event */
10167536c9adSClément Léger 	ret = sbi_sse_inject(args[0]->event_id, current_thread_info()->hartid);
10177536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "injection");
10187536c9adSClément Léger 
10197536c9adSClément Léger 	/* Check that all handlers have been called */
10207536c9adSClément Léger 	for (i = 0; i < args_size; i++)
10217536c9adSClément Léger 		report(arg->called, "Event %s handler called", sse_event_name(args[i]->event_id));
10227536c9adSClément Léger 
10237536c9adSClément Léger err:
10247536c9adSClément Léger 	for (i = 0; i < args_size; i++) {
10257536c9adSClément Léger 		arg = args[i];
10267536c9adSClément Léger 		event_id = arg->event_id;
10277536c9adSClément Léger 
10287536c9adSClément Léger 		switch (arg->state) {
10297536c9adSClément Léger 		case SBI_SSE_STATE_ENABLED:
10307536c9adSClément Léger 			ret = sbi_sse_disable(event_id);
10317536c9adSClément Léger 			if (ret.error) {
10327536c9adSClément Léger 				sbiret_report_error(&ret, SBI_SUCCESS, "disable event 0x%x",
10337536c9adSClément Léger 						    event_id);
10347536c9adSClément Léger 				break;
10357536c9adSClément Léger 			}
10367536c9adSClément Léger 		case SBI_SSE_STATE_REGISTERED:
10377536c9adSClément Léger 			sbi_sse_unregister(event_id);
10387536c9adSClément Léger 			if (ret.error)
10397536c9adSClément Léger 				sbiret_report_error(&ret, SBI_SUCCESS, "unregister event 0x%x",
10407536c9adSClément Léger 						event_id);
10417536c9adSClément Léger 		default:
10427536c9adSClément Léger 			break;
10437536c9adSClément Léger 		}
10447536c9adSClément Léger 
10457536c9adSClément Léger 		event_arg = &event_args[i];
10467536c9adSClément Léger 		if (event_arg->stack)
10477536c9adSClément Léger 			sse_free_stack(event_arg->stack);
10487536c9adSClément Léger 	}
10497536c9adSClément Léger 
10507536c9adSClément Léger skip:
10517536c9adSClément Léger 	report_prefix_pop();
10527536c9adSClément Léger }
10537536c9adSClément Léger 
10547536c9adSClément Léger static struct priority_test_arg hi_prio_args[] = {
10557536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
10567536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE},
10577536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS},
10587536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS},
10597536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW},
10607536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS},
10617536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP},
10627536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS},
10637536c9adSClément Léger };
10647536c9adSClément Léger 
10657536c9adSClément Léger static struct priority_test_arg low_prio_args[] = {
10667536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS},
10677536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP},
10687536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS},
10697536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW},
10707536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS},
10717536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS},
10727536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE},
10737536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE},
10747536c9adSClément Léger };
10757536c9adSClément Léger 
10767536c9adSClément Léger static struct priority_test_arg prio_args[] = {
10777536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,		.prio = 5},
10787536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,		.prio = 10},
10797536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS,		.prio = 12},
10807536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW,		.prio = 15},
10817536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS,	.prio = 20},
10827536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS,		.prio = 22},
10837536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS,		.prio = 25},
10847536c9adSClément Léger };
10857536c9adSClément Léger 
10867536c9adSClément Léger static struct priority_test_arg same_prio_args[] = {
10877536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW,		.prio = 0},
10887536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS,		.prio = 0},
10897536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS,		.prio = 10},
10907536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_SOFTWARE,		.prio = 10},
10917536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_SOFTWARE,		.prio = 10},
10927536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS,	.prio = 20},
10937536c9adSClément Léger 	{.event_id = SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS,		.prio = 20},
10947536c9adSClément Léger };
10957536c9adSClément Léger 
sse_test_injection_priority(void)10967536c9adSClément Léger static void sse_test_injection_priority(void)
10977536c9adSClément Léger {
10987536c9adSClément Léger 	report_prefix_push("prio");
10997536c9adSClément Léger 
11007536c9adSClément Léger 	sse_test_injection_priority_arg(hi_prio_args, ARRAY_SIZE(hi_prio_args),
11017536c9adSClément Léger 					sse_hi_priority_test_handler, "high");
11027536c9adSClément Léger 
11037536c9adSClément Léger 	sse_test_injection_priority_arg(low_prio_args, ARRAY_SIZE(low_prio_args),
11047536c9adSClément Léger 					sse_low_priority_test_handler, "low");
11057536c9adSClément Léger 
11067536c9adSClément Léger 	sse_test_injection_priority_arg(prio_args, ARRAY_SIZE(prio_args),
11077536c9adSClément Léger 					sse_low_priority_test_handler, "changed");
11087536c9adSClément Léger 
11097536c9adSClément Léger 	sse_test_injection_priority_arg(same_prio_args, ARRAY_SIZE(same_prio_args),
11107536c9adSClément Léger 					sse_low_priority_test_handler, "same_prio_args");
11117536c9adSClément Léger 
11127536c9adSClément Léger 	report_prefix_pop();
11137536c9adSClément Léger }
11147536c9adSClément Léger 
test_invalid_event_id(unsigned long event_id)11157536c9adSClément Léger static void test_invalid_event_id(unsigned long event_id)
11167536c9adSClément Léger {
11177536c9adSClément Léger 	struct sbiret ret;
11187536c9adSClément Léger 	unsigned long value = 0;
11197536c9adSClément Léger 
11207536c9adSClément Léger 	ret = sbi_sse_register_raw(event_id, (unsigned long) sbi_sse_entry, 0);
11217536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
11227536c9adSClément Léger 			    "register event_id 0x%lx", event_id);
11237536c9adSClément Léger 
11247536c9adSClément Léger 	ret = sbi_sse_unregister(event_id);
11257536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
11267536c9adSClément Léger 			"unregister event_id 0x%lx", event_id);
11277536c9adSClément Léger 
11287536c9adSClément Léger 	ret = sbi_sse_enable(event_id);
11297536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
11307536c9adSClément Léger 			    "enable event_id 0x%lx", event_id);
11317536c9adSClément Léger 
11327536c9adSClément Léger 	ret = sbi_sse_disable(event_id);
11337536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
11347536c9adSClément Léger 			    "disable event_id 0x%lx", event_id);
11357536c9adSClément Léger 
11367536c9adSClément Léger 	ret = sbi_sse_inject(event_id, 0);
11377536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
11387536c9adSClément Léger 			    "inject event_id 0x%lx", event_id);
11397536c9adSClément Léger 
11407536c9adSClément Léger 	ret = sbi_sse_write_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
11417536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
11427536c9adSClément Léger 			    "write attr event_id 0x%lx", event_id);
11437536c9adSClément Léger 
11447536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_PRIORITY, 1, &value);
11457536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
11467536c9adSClément Léger 			    "read attr event_id 0x%lx", event_id);
11477536c9adSClément Léger }
11487536c9adSClément Léger 
sse_test_invalid_event_id(void)11497536c9adSClément Léger static void sse_test_invalid_event_id(void)
11507536c9adSClément Léger {
11517536c9adSClément Léger 
11527536c9adSClément Léger 	report_prefix_push("event_id");
11537536c9adSClément Léger 
11547536c9adSClément Léger 	test_invalid_event_id(SBI_SSE_EVENT_LOCAL_RESERVED_0_START);
11557536c9adSClément Léger 
11567536c9adSClément Léger 	report_prefix_pop();
11577536c9adSClément Léger }
11587536c9adSClément Léger 
sse_check_event_availability(uint32_t event_id,bool * can_inject,bool * supported)11597536c9adSClément Léger static void sse_check_event_availability(uint32_t event_id, bool *can_inject, bool *supported)
11607536c9adSClément Léger {
11617536c9adSClément Léger 	unsigned long status;
11627536c9adSClément Léger 	struct sbiret ret;
11637536c9adSClément Léger 
11647536c9adSClément Léger 	*can_inject = false;
11657536c9adSClément Léger 	*supported = false;
11667536c9adSClément Léger 
11677536c9adSClément Léger 	ret = sbi_sse_read_attrs(event_id, SBI_SSE_ATTR_STATUS, 1, &status);
11687536c9adSClément Léger 	if (ret.error != SBI_SUCCESS && ret.error != SBI_ERR_NOT_SUPPORTED) {
11697536c9adSClément Léger 		report_fail("Get event status != SBI_SUCCESS && != SBI_ERR_NOT_SUPPORTED: %ld",
11707536c9adSClément Léger 			    ret.error);
11717536c9adSClément Léger 		return;
11727536c9adSClément Léger 	}
11737536c9adSClément Léger 	if (ret.error == SBI_ERR_NOT_SUPPORTED)
11747536c9adSClément Léger 		return;
11757536c9adSClément Léger 
11767536c9adSClément Léger 	*supported = true;
11777536c9adSClément Léger 	*can_inject = (status >> SBI_SSE_ATTR_STATUS_INJECT_OFFSET) & 1;
11787536c9adSClément Léger }
11797536c9adSClément Léger 
sse_secondary_boot_and_unmask(void * data)11807536c9adSClément Léger static void sse_secondary_boot_and_unmask(void *data)
11817536c9adSClément Léger {
11827536c9adSClément Léger 	sbi_sse_hart_unmask();
11837536c9adSClément Léger }
11847536c9adSClément Léger 
sse_check_mask(void)11857536c9adSClément Léger static void sse_check_mask(void)
11867536c9adSClément Léger {
11877536c9adSClément Léger 	struct sbiret ret;
11887536c9adSClément Léger 
11897536c9adSClément Léger 	/* Upon boot, event are masked, check that */
11907536c9adSClément Léger 	ret = sbi_sse_hart_mask();
11917536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_ALREADY_STOPPED, "hart mask at boot time");
11927536c9adSClément Léger 
11937536c9adSClément Léger 	ret = sbi_sse_hart_unmask();
11947536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "hart unmask");
11957536c9adSClément Léger 	ret = sbi_sse_hart_unmask();
11967536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_ALREADY_STARTED, "hart unmask twice error");
11977536c9adSClément Léger 
11987536c9adSClément Léger 	ret = sbi_sse_hart_mask();
11997536c9adSClément Léger 	sbiret_report_error(&ret, SBI_SUCCESS, "hart mask");
12007536c9adSClément Léger 	ret = sbi_sse_hart_mask();
12017536c9adSClément Léger 	sbiret_report_error(&ret, SBI_ERR_ALREADY_STOPPED, "hart mask twice");
12027536c9adSClément Léger }
12037536c9adSClément Léger 
run_inject_test(struct sse_event_info * info)12047536c9adSClément Léger static void run_inject_test(struct sse_event_info *info)
12057536c9adSClément Léger {
12067536c9adSClément Léger 	unsigned long event_id = info->event_id;
12077536c9adSClément Léger 
12087536c9adSClément Léger 	if (!info->can_inject) {
12097536c9adSClément Léger 		report_skip("Event does not support injection, skipping injection tests");
12107536c9adSClément Léger 		return;
12117536c9adSClément Léger 	}
12127536c9adSClément Léger 
12137536c9adSClément Léger 	sse_test_inject_simple(event_id);
12147536c9adSClément Léger 
12157536c9adSClément Léger 	if (sbi_sse_event_is_global(event_id))
12167536c9adSClément Léger 		sse_test_inject_global(event_id);
12177536c9adSClément Léger 	else
12187536c9adSClément Léger 		sse_test_inject_local(event_id);
12197536c9adSClément Léger }
12207536c9adSClément Léger 
check_sse(void)12217536c9adSClément Léger void check_sse(void)
12227536c9adSClément Léger {
12237536c9adSClément Léger 	struct sse_event_info *info;
12247536c9adSClément Léger 	unsigned long i, event_id;
12257536c9adSClément Léger 	bool supported;
12267536c9adSClément Léger 
12277536c9adSClément Léger 	report_prefix_push("sse");
12287536c9adSClément Léger 
12297536c9adSClément Léger 	if (!sbi_probe(SBI_EXT_SSE)) {
12307536c9adSClément Léger 		report_skip("extension not available");
12317536c9adSClément Léger 		report_prefix_pop();
12327536c9adSClément Léger 		return;
12337536c9adSClément Léger 	}
12347536c9adSClément Léger 
1235*a3fc8778SAndrew Jones 	if (__sbi_get_imp_id() == SBI_IMPL_OPENSBI &&
1236*a3fc8778SAndrew Jones 	    __sbi_get_imp_version() < sbi_impl_opensbi_mk_version(1, 7)) {
12377536c9adSClément Léger 		report_skip("OpenSBI < v1.7 detected, skipping tests");
12387536c9adSClément Léger 		report_prefix_pop();
12397536c9adSClément Léger 		return;
12407536c9adSClément Léger 	}
12417536c9adSClément Léger 
12427536c9adSClément Léger 	sse_check_mask();
12437536c9adSClément Léger 
12447536c9adSClément Léger 	/*
12457536c9adSClément Léger 	 * Dummy wakeup of all processors since some of them will be targeted
12467536c9adSClément Léger 	 * by global events without going through the wakeup call as well as
12477536c9adSClément Léger 	 * unmasking SSE events on all harts
12487536c9adSClément Léger 	 */
12497536c9adSClément Léger 	on_cpus(sse_secondary_boot_and_unmask, NULL);
12507536c9adSClément Léger 
12517536c9adSClément Léger 	sse_test_invalid_event_id();
12527536c9adSClément Léger 
12537536c9adSClément Léger 	for (i = 0; i < ARRAY_SIZE(sse_event_infos); i++) {
12547536c9adSClément Léger 		info = &sse_event_infos[i];
12557536c9adSClément Léger 		event_id = info->event_id;
12567536c9adSClément Léger 		report_prefix_push(info->name);
12577536c9adSClément Léger 		sse_check_event_availability(event_id, &info->can_inject, &supported);
12587536c9adSClément Léger 		if (!supported) {
12597536c9adSClément Léger 			report_skip("Event is not supported, skipping tests");
12607536c9adSClément Léger 			report_prefix_pop();
12617536c9adSClément Léger 			continue;
12627536c9adSClément Léger 		}
12637536c9adSClément Léger 
12647536c9adSClément Léger 		sse_test_attrs(event_id);
12657536c9adSClément Léger 		sse_test_register_error(event_id);
12667536c9adSClément Léger 
12677536c9adSClément Léger 		run_inject_test(info);
12687536c9adSClément Léger 
12697536c9adSClément Léger 		report_prefix_pop();
12707536c9adSClément Léger 	}
12717536c9adSClément Léger 
12727536c9adSClément Léger 	sse_test_injection_priority();
12737536c9adSClément Léger 
12747536c9adSClément Léger 	report_prefix_pop();
12757536c9adSClément Léger }
1276