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 norvc\n" \
30 " sw %0, 0(%1)\n" \
31 " .option pop\n" \
32 : : "r" (value), "r" (ptr) : "memory"); \
33 } while (0)
34
pagefault_trap_handler(struct pt_regs * regs)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
sse_dbltrp_handler(void * data,struct pt_regs * regs,unsigned int hartid)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
sse_double_trap(void)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
check_double_trap(void)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
main(int argc,char ** argv)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