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, ¤t_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 = ¶ms[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 = ¶ms[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