1*e844ca0cSClément Léger // SPDX-License-Identifier: GPL-2.0-only 2*e844ca0cSClément Léger /* 3*e844ca0cSClément Léger * SBI verification 4*e844ca0cSClément Léger * 5*e844ca0cSClément Léger * Copyright (C) 2025, Rivos Inc., Clément Léger <cleger@rivosinc.com> 6*e844ca0cSClément Léger */ 7*e844ca0cSClément Léger #include <alloc.h> 8*e844ca0cSClément Léger #include <alloc_page.h> 9*e844ca0cSClément Léger #include <libcflat.h> 10*e844ca0cSClément Léger #include <stdlib.h> 11*e844ca0cSClément Léger 12*e844ca0cSClément Léger #include <asm/csr.h> 13*e844ca0cSClément Léger #include <asm/page.h> 14*e844ca0cSClément Léger #include <asm/processor.h> 15*e844ca0cSClément Léger #include <asm/ptrace.h> 16*e844ca0cSClément Léger #include <asm/sbi.h> 17*e844ca0cSClément Léger 18*e844ca0cSClément Léger #include <sbi-tests.h> 19*e844ca0cSClément Léger 20*e844ca0cSClément Léger static bool double_trap; 21*e844ca0cSClément Léger static bool clear_sdt; 22*e844ca0cSClément Léger 23*e844ca0cSClément Léger #define GEN_TRAP() \ 24*e844ca0cSClément Léger do { \ 25*e844ca0cSClément Léger void *ptr = NULL; \ 26*e844ca0cSClément Léger unsigned long value = 0; \ 27*e844ca0cSClément Léger asm volatile( \ 28*e844ca0cSClément Léger " .option push\n" \ 29*e844ca0cSClément Léger " .option arch,-c\n" \ 30*e844ca0cSClément Léger " sw %0, 0(%1)\n" \ 31*e844ca0cSClément Léger " .option pop\n" \ 32*e844ca0cSClément Léger : : "r" (value), "r" (ptr) : "memory"); \ 33*e844ca0cSClément Léger } while (0) 34*e844ca0cSClément Léger 35*e844ca0cSClément Léger static void pagefault_trap_handler(struct pt_regs *regs) 36*e844ca0cSClément Léger { 37*e844ca0cSClément Léger if (READ_ONCE(clear_sdt)) 38*e844ca0cSClément Léger local_dlbtrp_disable(); 39*e844ca0cSClément Léger 40*e844ca0cSClément Léger if (READ_ONCE(double_trap)) { 41*e844ca0cSClément Léger WRITE_ONCE(double_trap, false); 42*e844ca0cSClément Léger GEN_TRAP(); 43*e844ca0cSClément Léger } 44*e844ca0cSClément Léger 45*e844ca0cSClément Léger /* Skip trapping instruction */ 46*e844ca0cSClément Léger regs->epc += 4; 47*e844ca0cSClément Léger 48*e844ca0cSClément Léger local_dlbtrp_enable(); 49*e844ca0cSClément Léger } 50*e844ca0cSClément Léger 51*e844ca0cSClément Léger static bool sse_dbltrp_called; 52*e844ca0cSClément Léger 53*e844ca0cSClément Léger static void sse_dbltrp_handler(void *data, struct pt_regs *regs, unsigned int hartid) 54*e844ca0cSClément Léger { 55*e844ca0cSClément Léger struct sbiret ret; 56*e844ca0cSClément Léger unsigned long flags; 57*e844ca0cSClément Léger unsigned long expected_flags = SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPP | 58*e844ca0cSClément Léger SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SDT; 59*e844ca0cSClément Léger 60*e844ca0cSClément Léger ret = sbi_sse_read_attrs(SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP, SBI_SSE_ATTR_INTERRUPTED_FLAGS, 1, 61*e844ca0cSClément Léger &flags); 62*e844ca0cSClément Léger sbiret_report_error(&ret, SBI_SUCCESS, "Get double trap event flags"); 63*e844ca0cSClément Léger report(flags == expected_flags, "SSE flags == 0x%lx", expected_flags); 64*e844ca0cSClément Léger 65*e844ca0cSClément Léger WRITE_ONCE(sse_dbltrp_called, true); 66*e844ca0cSClément Léger 67*e844ca0cSClément Léger /* Skip trapping instruction */ 68*e844ca0cSClément Léger regs->epc += 4; 69*e844ca0cSClément Léger } 70*e844ca0cSClément Léger 71*e844ca0cSClément Léger static int sse_double_trap(void) 72*e844ca0cSClément Léger { 73*e844ca0cSClément Léger struct sbiret ret; 74*e844ca0cSClément Léger int err = 0; 75*e844ca0cSClément Léger 76*e844ca0cSClément Léger struct sbi_sse_handler_arg handler_arg = { 77*e844ca0cSClément Léger .handler = sse_dbltrp_handler, 78*e844ca0cSClément Léger .stack = alloc_page() + PAGE_SIZE, 79*e844ca0cSClément Léger }; 80*e844ca0cSClément Léger 81*e844ca0cSClément Léger report_prefix_push("sse"); 82*e844ca0cSClément Léger 83*e844ca0cSClément Léger ret = sbi_sse_hart_unmask(); 84*e844ca0cSClément Léger if (!sbiret_report_error(&ret, SBI_SUCCESS, "SSE hart unmask ok")) { 85*e844ca0cSClément Léger report_skip("Failed to unmask SSE events, skipping test"); 86*e844ca0cSClément Léger goto out_free_page; 87*e844ca0cSClément Léger } 88*e844ca0cSClément Léger 89*e844ca0cSClément Léger ret = sbi_sse_register(SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP, &handler_arg); 90*e844ca0cSClément Léger if (ret.error == SBI_ERR_NOT_SUPPORTED) { 91*e844ca0cSClément Léger report_skip("SSE double trap event is not supported"); 92*e844ca0cSClément Léger goto out_mask_sse; 93*e844ca0cSClément Léger } 94*e844ca0cSClément Léger sbiret_report_error(&ret, SBI_SUCCESS, "SSE double trap register"); 95*e844ca0cSClément Léger 96*e844ca0cSClément Léger ret = sbi_sse_enable(SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP); 97*e844ca0cSClément Léger if (!sbiret_report_error(&ret, SBI_SUCCESS, "SSE double trap enable")) 98*e844ca0cSClément Léger goto out_unregister; 99*e844ca0cSClément Léger 100*e844ca0cSClément Léger /* 101*e844ca0cSClément Léger * Generate a double crash so that an SSE event should be generated. The SPEC (ISA nor SBI) 102*e844ca0cSClément Léger * does not explicitly tell that if supported it should generate an SSE event but that's 103*e844ca0cSClément Léger * a reasonable assumption to do so if both FWFT and SSE are supported. 104*e844ca0cSClément Léger */ 105*e844ca0cSClément Léger WRITE_ONCE(clear_sdt, false); 106*e844ca0cSClément Léger WRITE_ONCE(double_trap, true); 107*e844ca0cSClément Léger GEN_TRAP(); 108*e844ca0cSClément Léger 109*e844ca0cSClément Léger report(READ_ONCE(sse_dbltrp_called), "SSE double trap event generated"); 110*e844ca0cSClément Léger 111*e844ca0cSClément Léger ret = sbi_sse_disable(SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP); 112*e844ca0cSClément Léger sbiret_report_error(&ret, SBI_SUCCESS, "SSE double trap disable"); 113*e844ca0cSClément Léger 114*e844ca0cSClément Léger out_unregister: 115*e844ca0cSClément Léger ret = sbi_sse_unregister(SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP); 116*e844ca0cSClément Léger if (!sbiret_report_error(&ret, SBI_SUCCESS, "SSE double trap unregister")) 117*e844ca0cSClément Léger err = ret.error; 118*e844ca0cSClément Léger 119*e844ca0cSClément Léger out_mask_sse: 120*e844ca0cSClément Léger sbi_sse_hart_mask(); 121*e844ca0cSClément Léger 122*e844ca0cSClément Léger out_free_page: 123*e844ca0cSClément Léger free_page(handler_arg.stack - PAGE_SIZE); 124*e844ca0cSClément Léger report_prefix_pop(); 125*e844ca0cSClément Léger 126*e844ca0cSClément Léger return err; 127*e844ca0cSClément Léger } 128*e844ca0cSClément Léger 129*e844ca0cSClément Léger static void check_double_trap(void) 130*e844ca0cSClément Léger { 131*e844ca0cSClément Léger struct sbiret ret; 132*e844ca0cSClément Léger 133*e844ca0cSClément Léger /* Disable double trap */ 134*e844ca0cSClément Léger ret = sbi_fwft_set(SBI_FWFT_DOUBLE_TRAP, 0, 0); 135*e844ca0cSClément Léger sbiret_report_error(&ret, SBI_SUCCESS, "Set double trap enable feature value == 0"); 136*e844ca0cSClément Léger ret = sbi_fwft_get(SBI_FWFT_DOUBLE_TRAP); 137*e844ca0cSClément Léger sbiret_report(&ret, SBI_SUCCESS, 0, "Get double trap enable feature value == 0"); 138*e844ca0cSClément Léger 139*e844ca0cSClément Léger install_exception_handler(EXC_STORE_PAGE_FAULT, pagefault_trap_handler); 140*e844ca0cSClément Léger 141*e844ca0cSClément Léger WRITE_ONCE(clear_sdt, true); 142*e844ca0cSClément Léger WRITE_ONCE(double_trap, true); 143*e844ca0cSClément Léger GEN_TRAP(); 144*e844ca0cSClément Léger report_pass("Double trap disabled, trap first time ok"); 145*e844ca0cSClément Léger 146*e844ca0cSClément Léger /* Enable double trap */ 147*e844ca0cSClément Léger ret = sbi_fwft_set(SBI_FWFT_DOUBLE_TRAP, 1, 0); 148*e844ca0cSClément Léger sbiret_report_error(&ret, SBI_SUCCESS, "Set double trap enable feature value == 1"); 149*e844ca0cSClément Léger ret = sbi_fwft_get(SBI_FWFT_DOUBLE_TRAP); 150*e844ca0cSClément Léger if (!sbiret_report(&ret, SBI_SUCCESS, 1, "Get double trap enable feature value == 1")) 151*e844ca0cSClément Léger return; 152*e844ca0cSClément Léger 153*e844ca0cSClément Léger /* First time, clear the double trap flag (SDT) so that it doesn't generate a double trap */ 154*e844ca0cSClément Léger WRITE_ONCE(clear_sdt, true); 155*e844ca0cSClément Léger WRITE_ONCE(double_trap, true); 156*e844ca0cSClément Léger 157*e844ca0cSClément Léger GEN_TRAP(); 158*e844ca0cSClément Léger report_pass("Trapped twice allowed ok"); 159*e844ca0cSClément Léger 160*e844ca0cSClément Léger if (sbi_probe(SBI_EXT_SSE)) { 161*e844ca0cSClément Léger if (sse_double_trap()) { 162*e844ca0cSClément Léger report_skip("Could not correctly unregister SSE event, skipping last test"); 163*e844ca0cSClément Léger return; 164*e844ca0cSClément Léger } 165*e844ca0cSClément Léger } else { 166*e844ca0cSClément Léger report_skip("SSE double trap event will not be tested, extension is not available"); 167*e844ca0cSClément Léger } 168*e844ca0cSClément Léger 169*e844ca0cSClément Léger if (!env_or_skip("DOUBLE_TRAP_TEST_CRASH")) 170*e844ca0cSClément Léger return; 171*e844ca0cSClément Léger 172*e844ca0cSClément Léger /* 173*e844ca0cSClément Léger * Third time, keep the double trap flag (SDT) and generate another trap, this should 174*e844ca0cSClément Léger * generate a double trap. Since there is no SSE handler registered, it should crash to 175*e844ca0cSClément Léger * M-mode. 176*e844ca0cSClément Léger */ 177*e844ca0cSClément Léger WRITE_ONCE(clear_sdt, false); 178*e844ca0cSClément Léger WRITE_ONCE(double_trap, true); 179*e844ca0cSClément Léger report_info("Should generate a double trap and crash!"); 180*e844ca0cSClément Léger GEN_TRAP(); 181*e844ca0cSClément Léger report_fail("Should have crashed!"); 182*e844ca0cSClément Léger } 183*e844ca0cSClément Léger 184*e844ca0cSClément Léger int main(int argc, char **argv) 185*e844ca0cSClément Léger { 186*e844ca0cSClément Léger struct sbiret ret; 187*e844ca0cSClément Léger 188*e844ca0cSClément Léger report_prefix_push("dbltrp"); 189*e844ca0cSClément Léger 190*e844ca0cSClément Léger if (!sbi_probe(SBI_EXT_FWFT)) { 191*e844ca0cSClément Léger report_skip("FWFT extension is not available, can not enable double traps"); 192*e844ca0cSClément Léger goto out; 193*e844ca0cSClément Léger } 194*e844ca0cSClément Léger 195*e844ca0cSClément Léger ret = sbi_fwft_get(SBI_FWFT_DOUBLE_TRAP); 196*e844ca0cSClément Léger if (ret.error == SBI_ERR_NOT_SUPPORTED) { 197*e844ca0cSClément Léger report_skip("SBI_FWFT_DOUBLE_TRAP is not supported!"); 198*e844ca0cSClément Léger goto out; 199*e844ca0cSClément Léger } 200*e844ca0cSClément Léger 201*e844ca0cSClément Léger if (sbiret_report_error(&ret, SBI_SUCCESS, "SBI_FWFT_DOUBLE_TRAP get value")) 202*e844ca0cSClément Léger check_double_trap(); 203*e844ca0cSClément Léger 204*e844ca0cSClément Léger out: 205*e844ca0cSClément Léger report_prefix_pop(); 206*e844ca0cSClément Léger 207*e844ca0cSClément Léger return report_summary(); 208*e844ca0cSClément Léger } 209