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