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