xref: /kvm-unit-tests/riscv/sbi.c (revision ce58d3a45d96c36a4c94c22492cbe185c6695755)
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/io.h>
14 #include <asm/isa.h>
15 #include <asm/processor.h>
16 #include <asm/sbi.h>
17 #include <asm/smp.h>
18 #include <asm/timer.h>
19 
20 static void help(void)
21 {
22 	puts("Test SBI\n");
23 	puts("An environ must be provided where expected values are given.\n");
24 }
25 
26 static struct sbiret __base_sbi_ecall(int fid, unsigned long arg0)
27 {
28 	return sbi_ecall(SBI_EXT_BASE, fid, arg0, 0, 0, 0, 0, 0);
29 }
30 
31 static struct sbiret __time_sbi_ecall(unsigned long stime_value)
32 {
33 	return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0);
34 }
35 
36 static struct sbiret __dbcn_sbi_ecall(int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2)
37 {
38 	return sbi_ecall(SBI_EXT_DBCN, fid, arg0, arg1, arg2, 0, 0, 0);
39 }
40 
41 static void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo)
42 {
43 	*lo = (unsigned long)paddr;
44 	*hi = 0;
45 	if (__riscv_xlen == 32)
46 		*hi = (unsigned long)(paddr >> 32);
47 }
48 
49 static bool env_or_skip(const char *env)
50 {
51 	if (!getenv(env)) {
52 		report_skip("missing %s environment variable", env);
53 		return false;
54 	}
55 
56 	return true;
57 }
58 
59 static void gen_report(struct sbiret *ret,
60 		       long expected_error, long expected_value)
61 {
62 	bool check_error = ret->error == expected_error;
63 	bool check_value = ret->value == expected_value;
64 
65 	if (!check_error || !check_value)
66 		report_info("expected (error: %ld, value: %ld), received: (error: %ld, value %ld)",
67 			    expected_error, expected_value, ret->error, ret->value);
68 
69 	report(check_error, "expected sbi.error");
70 	report(check_value, "expected sbi.value");
71 }
72 
73 static void check_base(void)
74 {
75 	struct sbiret ret;
76 	long expected;
77 
78 	report_prefix_push("base");
79 
80 	ret = __base_sbi_ecall(SBI_EXT_BASE_GET_SPEC_VERSION, 0);
81 	if (ret.error || ret.value < 2) {
82 		report_skip("SBI spec version 0.2 or higher required");
83 		return;
84 	}
85 
86 	report_prefix_push("spec_version");
87 	if (env_or_skip("SPEC_VERSION")) {
88 		expected = strtol(getenv("SPEC_VERSION"), NULL, 0);
89 		gen_report(&ret, 0, expected);
90 	}
91 	report_prefix_pop();
92 
93 	report_prefix_push("impl_id");
94 	if (env_or_skip("IMPL_ID")) {
95 		expected = strtol(getenv("IMPL_ID"), NULL, 0);
96 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0);
97 		gen_report(&ret, 0, expected);
98 	}
99 	report_prefix_pop();
100 
101 	report_prefix_push("impl_version");
102 	if (env_or_skip("IMPL_VERSION")) {
103 		expected = strtol(getenv("IMPL_VERSION"), NULL, 0);
104 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0);
105 		gen_report(&ret, 0, expected);
106 	}
107 	report_prefix_pop();
108 
109 	report_prefix_push("probe_ext");
110 	expected = getenv("PROBE_EXT") ? strtol(getenv("PROBE_EXT"), NULL, 0) : 1;
111 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE);
112 	gen_report(&ret, 0, expected);
113 	report_prefix_push("unavailable");
114 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, 0xb000000);
115 	gen_report(&ret, 0, 0);
116 	report_prefix_pop();
117 	report_prefix_pop();
118 
119 	report_prefix_push("mvendorid");
120 	if (env_or_skip("MVENDORID")) {
121 		expected = strtol(getenv("MVENDORID"), NULL, 0);
122 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0);
123 		gen_report(&ret, 0, expected);
124 	}
125 	report_prefix_pop();
126 
127 	report_prefix_push("marchid");
128 	if (env_or_skip("MARCHID")) {
129 		expected = strtol(getenv("MARCHID"), NULL, 0);
130 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0);
131 		gen_report(&ret, 0, expected);
132 	}
133 	report_prefix_pop();
134 
135 	report_prefix_push("mimpid");
136 	if (env_or_skip("MIMPID")) {
137 		expected = strtol(getenv("MIMPID"), NULL, 0);
138 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0);
139 		gen_report(&ret, 0, expected);
140 	}
141 	report_prefix_pop();
142 
143 	report_prefix_pop();
144 }
145 
146 struct timer_info {
147 	bool timer_works;
148 	bool mask_timer_irq;
149 	bool timer_irq_set;
150 	bool timer_irq_cleared;
151 	unsigned long timer_irq_count;
152 };
153 
154 static struct timer_info timer_info;
155 
156 static bool timer_irq_pending(void)
157 {
158 	return csr_read(CSR_SIP) & IP_TIP;
159 }
160 
161 static void timer_irq_handler(struct pt_regs *regs)
162 {
163 	timer_info.timer_works = true;
164 
165 	if (timer_info.timer_irq_count < ULONG_MAX)
166 		++timer_info.timer_irq_count;
167 
168 	if (timer_irq_pending())
169 		timer_info.timer_irq_set = true;
170 
171 	if (timer_info.mask_timer_irq)
172 		timer_irq_disable();
173 	else
174 		__time_sbi_ecall(ULONG_MAX);
175 
176 	if (!timer_irq_pending())
177 		timer_info.timer_irq_cleared = true;
178 }
179 
180 static void timer_check_set_timer(bool mask_timer_irq)
181 {
182 	struct sbiret ret;
183 	unsigned long begin, end, duration;
184 	const char *mask_test_str = mask_timer_irq ? " for mask irq test" : "";
185 	unsigned long d = getenv("TIMER_DELAY") ? strtol(getenv("TIMER_DELAY"), NULL, 0) : 200000;
186 	unsigned long margin = getenv("TIMER_MARGIN") ? strtol(getenv("TIMER_MARGIN"), NULL, 0) : 200000;
187 
188 	d = usec_to_cycles(d);
189 	margin = usec_to_cycles(margin);
190 
191 	timer_info = (struct timer_info){ .mask_timer_irq = mask_timer_irq };
192 	begin = timer_get_cycles();
193 	ret = __time_sbi_ecall(begin + d);
194 
195 	report(!ret.error, "set timer%s", mask_test_str);
196 	if (ret.error)
197 		report_info("set timer%s failed with %ld\n", mask_test_str, ret.error);
198 
199 	while ((end = timer_get_cycles()) <= (begin + d + margin) && !timer_info.timer_works)
200 		cpu_relax();
201 
202 	report(timer_info.timer_works, "timer interrupt received%s", mask_test_str);
203 	report(timer_info.timer_irq_set, "pending timer interrupt bit set in irq handler%s", mask_test_str);
204 
205 	if (!mask_timer_irq) {
206 		report(timer_info.timer_irq_set && timer_info.timer_irq_cleared,
207 		       "pending timer interrupt bit cleared by setting timer to -1");
208 	}
209 
210 	if (timer_info.timer_works) {
211 		duration = end - begin;
212 		report(duration >= d && duration <= (d + margin), "timer delay honored%s", mask_test_str);
213 	}
214 
215 	report(timer_info.timer_irq_count == 1, "timer interrupt received exactly once%s", mask_test_str);
216 }
217 
218 static void check_time(void)
219 {
220 	bool pending;
221 
222 	report_prefix_push("time");
223 
224 	if (!sbi_probe(SBI_EXT_TIME)) {
225 		report_skip("time extension not available");
226 		report_prefix_pop();
227 		return;
228 	}
229 
230 	report_prefix_push("set_timer");
231 
232 	install_irq_handler(IRQ_S_TIMER, timer_irq_handler);
233 	local_irq_enable();
234 	if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
235 		csr_write(CSR_STIMECMP, ULONG_MAX);
236 		if (__riscv_xlen == 32)
237 			csr_write(CSR_STIMECMPH, ULONG_MAX);
238 	}
239 	timer_irq_enable();
240 
241 	timer_check_set_timer(false);
242 
243 	if (csr_read(CSR_SIE) & IE_TIE)
244 		timer_check_set_timer(true);
245 	else
246 		report_skip("timer irq enable bit is not writable, skipping mask irq test");
247 
248 	timer_irq_disable();
249 	__time_sbi_ecall(0);
250 	pending = timer_irq_pending();
251 	report(pending, "timer immediately pending by setting timer to 0");
252 	__time_sbi_ecall(ULONG_MAX);
253 	if (pending)
254 		report(!timer_irq_pending(), "pending timer cleared while masked");
255 	else
256 		report_skip("timer is not pending, skipping timer cleared while masked test");
257 
258 	local_irq_disable();
259 	install_irq_handler(IRQ_S_TIMER, NULL);
260 
261 	report_prefix_pop();
262 	report_prefix_pop();
263 }
264 
265 #define DBCN_WRITE_TEST_STRING		"DBCN_WRITE_TEST_STRING\n"
266 #define DBCN_WRITE_BYTE_TEST_BYTE	(u8)'a'
267 
268 /*
269  * Only the write functionality is tested here. There's no easy way to
270  * non-interactively test the read functionality.
271  */
272 static void check_dbcn(void)
273 {
274 	unsigned long num_bytes, base_addr_lo, base_addr_hi;
275 	phys_addr_t paddr;
276 	int num_calls = 0;
277 	struct sbiret ret;
278 
279 	report_prefix_push("dbcn");
280 
281 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_DBCN);
282 	if (!ret.value) {
283 		report_skip("DBCN extension unavailable");
284 		report_prefix_pop();
285 		return;
286 	}
287 
288 	num_bytes = strlen(DBCN_WRITE_TEST_STRING);
289 	paddr = virt_to_phys((void *)&DBCN_WRITE_TEST_STRING);
290 	split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
291 
292 	report_prefix_push("write");
293 
294 	do {
295 		ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, num_bytes, base_addr_lo, base_addr_hi);
296 		num_bytes -= ret.value;
297 		paddr += ret.value;
298 		split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
299 		num_calls++;
300 	} while (num_bytes != 0 && ret.error == SBI_SUCCESS);
301 
302 	report(ret.error == SBI_SUCCESS, "write success");
303 	report_info("%d sbi calls made", num_calls);
304 
305 	/* Bytes are read from memory and written to the console */
306 	if (env_or_skip("INVALID_ADDR")) {
307 		paddr = strtoull(getenv("INVALID_ADDR"), NULL, 0);
308 		split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
309 		ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, 1, base_addr_lo, base_addr_hi);
310 		report(ret.error == SBI_ERR_INVALID_PARAM, "invalid parameter: address");
311 	}
312 
313 	report_prefix_pop();
314 
315 	report_prefix_push("write_byte");
316 
317 	puts("DBCN_WRITE TEST CHAR: ");
318 	ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, (u8)DBCN_WRITE_BYTE_TEST_BYTE, 0, 0);
319 	puts("\n");
320 	report(ret.error == SBI_SUCCESS, "write success");
321 	report(ret.value == 0, "expected ret.value");
322 
323 	report_prefix_pop();
324 }
325 
326 int main(int argc, char **argv)
327 {
328 	if (argc > 1 && !strcmp(argv[1], "-h")) {
329 		help();
330 		exit(0);
331 	}
332 
333 	report_prefix_push("sbi");
334 	check_base();
335 	check_time();
336 	check_dbcn();
337 
338 	return report_summary();
339 }
340