xref: /kvm-unit-tests/riscv/sbi.c (revision 846737f068d95d5d4652a8bc17332cdfd1e8d74b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * SBI verification
4  *
5  * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
6  */
7 #include <libcflat.h>
8 #include <stdlib.h>
9 #include <limits.h>
10 #include <asm/barrier.h>
11 #include <asm/csr.h>
12 #include <asm/delay.h>
13 #include <asm/isa.h>
14 #include <asm/processor.h>
15 #include <asm/sbi.h>
16 #include <asm/smp.h>
17 #include <asm/timer.h>
18 
19 static void help(void)
20 {
21 	puts("Test SBI\n");
22 	puts("An environ must be provided where expected values are given.\n");
23 }
24 
25 static struct sbiret __base_sbi_ecall(int fid, unsigned long arg0)
26 {
27 	return sbi_ecall(SBI_EXT_BASE, fid, arg0, 0, 0, 0, 0, 0);
28 }
29 
30 static struct sbiret __time_sbi_ecall(unsigned long stime_value)
31 {
32 	return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
33 }
34 
35 static bool env_or_skip(const char *env)
36 {
37 	if (!getenv(env)) {
38 		report_skip("missing %s environment variable", env);
39 		return false;
40 	}
41 
42 	return true;
43 }
44 
45 static void gen_report(struct sbiret *ret,
46 		       long expected_error, long expected_value)
47 {
48 	bool check_error = ret->error == expected_error;
49 	bool check_value = ret->value == expected_value;
50 
51 	if (!check_error || !check_value)
52 		report_info("expected (error: %ld, value: %ld), received: (error: %ld, value %ld)",
53 			    expected_error, expected_value, ret->error, ret->value);
54 
55 	report(check_error, "expected sbi.error");
56 	report(check_value, "expected sbi.value");
57 }
58 
59 static void check_base(void)
60 {
61 	struct sbiret ret;
62 	long expected;
63 
64 	report_prefix_push("base");
65 
66 	ret = __base_sbi_ecall(SBI_EXT_BASE_GET_SPEC_VERSION, 0);
67 	if (ret.error || ret.value < 2) {
68 		report_skip("SBI spec version 0.2 or higher required");
69 		return;
70 	}
71 
72 	report_prefix_push("spec_version");
73 	if (env_or_skip("SPEC_VERSION")) {
74 		expected = strtol(getenv("SPEC_VERSION"), NULL, 0);
75 		gen_report(&ret, 0, expected);
76 	}
77 	report_prefix_pop();
78 
79 	report_prefix_push("impl_id");
80 	if (env_or_skip("IMPL_ID")) {
81 		expected = strtol(getenv("IMPL_ID"), NULL, 0);
82 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0);
83 		gen_report(&ret, 0, expected);
84 	}
85 	report_prefix_pop();
86 
87 	report_prefix_push("impl_version");
88 	if (env_or_skip("IMPL_VERSION")) {
89 		expected = strtol(getenv("IMPL_VERSION"), NULL, 0);
90 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0);
91 		gen_report(&ret, 0, expected);
92 	}
93 	report_prefix_pop();
94 
95 	report_prefix_push("probe_ext");
96 	expected = getenv("PROBE_EXT") ? strtol(getenv("PROBE_EXT"), NULL, 0) : 1;
97 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE);
98 	gen_report(&ret, 0, expected);
99 	report_prefix_push("unavailable");
100 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, 0xb000000);
101 	gen_report(&ret, 0, 0);
102 	report_prefix_pop();
103 	report_prefix_pop();
104 
105 	report_prefix_push("mvendorid");
106 	if (env_or_skip("MVENDORID")) {
107 		expected = strtol(getenv("MVENDORID"), NULL, 0);
108 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0);
109 		gen_report(&ret, 0, expected);
110 	}
111 	report_prefix_pop();
112 
113 	report_prefix_push("marchid");
114 	if (env_or_skip("MARCHID")) {
115 		expected = strtol(getenv("MARCHID"), NULL, 0);
116 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0);
117 		gen_report(&ret, 0, expected);
118 	}
119 	report_prefix_pop();
120 
121 	report_prefix_push("mimpid");
122 	if (env_or_skip("MIMPID")) {
123 		expected = strtol(getenv("MIMPID"), NULL, 0);
124 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0);
125 		gen_report(&ret, 0, expected);
126 	}
127 	report_prefix_pop();
128 
129 	report_prefix_pop();
130 }
131 
132 struct timer_info {
133 	bool timer_works;
134 	bool mask_timer_irq;
135 	bool timer_irq_set;
136 	bool timer_irq_cleared;
137 	unsigned long timer_irq_count;
138 };
139 
140 static struct timer_info timer_info;
141 
142 static bool timer_irq_pending(void)
143 {
144 	return csr_read(CSR_SIP) & IP_TIP;
145 }
146 
147 static void timer_irq_handler(struct pt_regs *regs)
148 {
149 	timer_info.timer_works = true;
150 
151 	if (timer_info.timer_irq_count < ULONG_MAX)
152 		++timer_info.timer_irq_count;
153 
154 	if (timer_irq_pending())
155 		timer_info.timer_irq_set = true;
156 
157 	if (timer_info.mask_timer_irq)
158 		timer_irq_disable();
159 	else
160 		__time_sbi_ecall(ULONG_MAX);
161 
162 	if (!timer_irq_pending())
163 		timer_info.timer_irq_cleared = true;
164 }
165 
166 static void timer_check_set_timer(bool mask_timer_irq)
167 {
168 	struct sbiret ret;
169 	unsigned long begin, end, duration;
170 	const char *mask_test_str = mask_timer_irq ? " for mask irq test" : "";
171 	unsigned long d = getenv("TIMER_DELAY") ? strtol(getenv("TIMER_DELAY"), NULL, 0) : 200000;
172 	unsigned long margin = getenv("TIMER_MARGIN") ? strtol(getenv("TIMER_MARGIN"), NULL, 0) : 200000;
173 
174 	d = usec_to_cycles(d);
175 	margin = usec_to_cycles(margin);
176 
177 	timer_info = (struct timer_info){ .mask_timer_irq = mask_timer_irq };
178 	begin = timer_get_cycles();
179 	ret = __time_sbi_ecall(begin + d);
180 
181 	report(!ret.error, "set timer%s", mask_test_str);
182 	if (ret.error)
183 		report_info("set timer%s failed with %ld\n", mask_test_str, ret.error);
184 
185 	while ((end = timer_get_cycles()) <= (begin + d + margin) && !timer_info.timer_works)
186 		cpu_relax();
187 
188 	report(timer_info.timer_works, "timer interrupt received%s", mask_test_str);
189 	report(timer_info.timer_irq_set, "pending timer interrupt bit set in irq handler%s", mask_test_str);
190 
191 	if (!mask_timer_irq) {
192 		report(timer_info.timer_irq_set && timer_info.timer_irq_cleared,
193 		       "pending timer interrupt bit cleared by setting timer to -1");
194 	}
195 
196 	if (timer_info.timer_works) {
197 		duration = end - begin;
198 		report(duration >= d && duration <= (d + margin), "timer delay honored%s", mask_test_str);
199 	}
200 
201 	report(timer_info.timer_irq_count == 1, "timer interrupt received exactly once%s", mask_test_str);
202 }
203 
204 static void check_time(void)
205 {
206 	bool pending;
207 
208 	report_prefix_push("time");
209 
210 	if (!sbi_probe(SBI_EXT_TIME)) {
211 		report_skip("time extension not available");
212 		report_prefix_pop();
213 		return;
214 	}
215 
216 	report_prefix_push("set_timer");
217 
218 	install_irq_handler(IRQ_S_TIMER, timer_irq_handler);
219 	local_irq_enable();
220 	if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
221 		csr_write(CSR_STIMECMP, ULONG_MAX);
222 		if (__riscv_xlen == 32)
223 			csr_write(CSR_STIMECMPH, ULONG_MAX);
224 	}
225 	timer_irq_enable();
226 
227 	timer_check_set_timer(false);
228 
229 	if (csr_read(CSR_SIE) & IE_TIE)
230 		timer_check_set_timer(true);
231 	else
232 		report_skip("timer irq enable bit is not writable, skipping mask irq test");
233 
234 	timer_irq_disable();
235 	__time_sbi_ecall(0);
236 	pending = timer_irq_pending();
237 	report(pending, "timer immediately pending by setting timer to 0");
238 	__time_sbi_ecall(ULONG_MAX);
239 	if (pending)
240 		report(!timer_irq_pending(), "pending timer cleared while masked");
241 	else
242 		report_skip("timer is not pending, skipping timer cleared while masked test");
243 
244 	local_irq_disable();
245 	install_irq_handler(IRQ_S_TIMER, NULL);
246 
247 	report_prefix_pop();
248 	report_prefix_pop();
249 }
250 
251 int main(int argc, char **argv)
252 {
253 
254 	if (argc > 1 && !strcmp(argv[1], "-h")) {
255 		help();
256 		exit(0);
257 	}
258 
259 	report_prefix_push("sbi");
260 	check_base();
261 	check_time();
262 
263 	return report_summary();
264 }
265