xref: /kvm-unit-tests/riscv/isa-dbltrp.c (revision 1b7dc511d891db54f5389ab591bb46f19976464c)
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