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