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