xref: /kvm-unit-tests/riscv/sbi.c (revision 9340e4b7e3aaf3a3b965d3e78c66c000ca4f82a1)
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 <alloc_page.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <limits.h>
12 #include <vmalloc.h>
13 #include <memregions.h>
14 #include <asm/barrier.h>
15 #include <asm/csr.h>
16 #include <asm/delay.h>
17 #include <asm/io.h>
18 #include <asm/mmu.h>
19 #include <asm/processor.h>
20 #include <asm/sbi.h>
21 #include <asm/smp.h>
22 #include <asm/timer.h>
23 
24 #define	HIGH_ADDR_BOUNDARY	((phys_addr_t)1 << 32)
25 
26 static void help(void)
27 {
28 	puts("Test SBI\n");
29 	puts("An environ must be provided where expected values are given.\n");
30 }
31 
32 static struct sbiret __base_sbi_ecall(int fid, unsigned long arg0)
33 {
34 	return sbi_ecall(SBI_EXT_BASE, fid, arg0, 0, 0, 0, 0, 0);
35 }
36 
37 static struct sbiret __dbcn_sbi_ecall(int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2)
38 {
39 	return sbi_ecall(SBI_EXT_DBCN, fid, arg0, arg1, arg2, 0, 0, 0);
40 }
41 
42 static void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo)
43 {
44 	*lo = (unsigned long)paddr;
45 	*hi = 0;
46 	if (__riscv_xlen == 32)
47 		*hi = (unsigned long)(paddr >> 32);
48 }
49 
50 static bool check_addr(phys_addr_t start, phys_addr_t size)
51 {
52 	struct mem_region *r = memregions_find(start);
53 	return r && r->end - start >= size && r->flags == MR_F_UNUSED;
54 }
55 
56 static phys_addr_t get_highest_addr(void)
57 {
58 	phys_addr_t highest_end = 0;
59 	struct mem_region *r;
60 
61 	for (r = mem_regions; r->end; ++r) {
62 		if (r->end > highest_end)
63 			highest_end = r->end;
64 	}
65 
66 	return highest_end - 1;
67 }
68 
69 static bool env_or_skip(const char *env)
70 {
71 	if (!getenv(env)) {
72 		report_skip("missing %s environment variable", env);
73 		return false;
74 	}
75 
76 	return true;
77 }
78 
79 static void gen_report(struct sbiret *ret,
80 		       long expected_error, long expected_value)
81 {
82 	bool check_error = ret->error == expected_error;
83 	bool check_value = ret->value == expected_value;
84 
85 	if (!check_error || !check_value)
86 		report_info("expected (error: %ld, value: %ld), received: (error: %ld, value %ld)",
87 			    expected_error, expected_value, ret->error, ret->value);
88 
89 	report(check_error, "expected sbi.error");
90 	report(check_value, "expected sbi.value");
91 }
92 
93 static void check_base(void)
94 {
95 	struct sbiret ret;
96 	long expected;
97 
98 	report_prefix_push("base");
99 
100 	ret = __base_sbi_ecall(SBI_EXT_BASE_GET_SPEC_VERSION, 0);
101 	if (ret.error || ret.value < 2) {
102 		report_skip("SBI spec version 0.2 or higher required");
103 		return;
104 	}
105 
106 	report_prefix_push("spec_version");
107 	if (env_or_skip("SBI_SPEC_VERSION")) {
108 		expected = (long)strtoul(getenv("SBI_SPEC_VERSION"), NULL, 0);
109 		gen_report(&ret, 0, expected);
110 	}
111 	report_prefix_pop();
112 
113 	report_prefix_push("impl_id");
114 	if (env_or_skip("SBI_IMPL_ID")) {
115 		expected = (long)strtoul(getenv("SBI_IMPL_ID"), NULL, 0);
116 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0);
117 		gen_report(&ret, 0, expected);
118 	}
119 	report_prefix_pop();
120 
121 	report_prefix_push("impl_version");
122 	if (env_or_skip("SBI_IMPL_VERSION")) {
123 		expected = (long)strtoul(getenv("SBI_IMPL_VERSION"), NULL, 0);
124 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0);
125 		gen_report(&ret, 0, expected);
126 	}
127 	report_prefix_pop();
128 
129 	report_prefix_push("probe_ext");
130 	expected = getenv("SBI_PROBE_EXT") ? (long)strtoul(getenv("SBI_PROBE_EXT"), NULL, 0) : 1;
131 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE);
132 	gen_report(&ret, 0, expected);
133 	report_prefix_push("unavailable");
134 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, 0xb000000);
135 	gen_report(&ret, 0, 0);
136 	report_prefix_pop();
137 	report_prefix_pop();
138 
139 	report_prefix_push("mvendorid");
140 	if (env_or_skip("MVENDORID")) {
141 		expected = (long)strtoul(getenv("MVENDORID"), NULL, 0);
142 		assert(__riscv_xlen == 32 || !(expected >> 32));
143 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0);
144 		gen_report(&ret, 0, expected);
145 	}
146 	report_prefix_pop();
147 
148 	report_prefix_push("marchid");
149 	if (env_or_skip("MARCHID")) {
150 		expected = (long)strtoul(getenv("MARCHID"), NULL, 0);
151 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0);
152 		gen_report(&ret, 0, expected);
153 	}
154 	report_prefix_pop();
155 
156 	report_prefix_push("mimpid");
157 	if (env_or_skip("MIMPID")) {
158 		expected = (long)strtoul(getenv("MIMPID"), NULL, 0);
159 		ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0);
160 		gen_report(&ret, 0, expected);
161 	}
162 	report_prefix_pop();
163 
164 	report_prefix_pop();
165 }
166 
167 struct timer_info {
168 	bool timer_works;
169 	bool mask_timer_irq;
170 	bool timer_irq_set;
171 	bool timer_irq_cleared;
172 	unsigned long timer_irq_count;
173 };
174 
175 static struct timer_info timer_info;
176 
177 static bool timer_irq_pending(void)
178 {
179 	return csr_read(CSR_SIP) & IP_TIP;
180 }
181 
182 static void timer_irq_handler(struct pt_regs *regs)
183 {
184 	timer_info.timer_works = true;
185 
186 	if (timer_info.timer_irq_count < ULONG_MAX)
187 		++timer_info.timer_irq_count;
188 
189 	if (timer_irq_pending())
190 		timer_info.timer_irq_set = true;
191 
192 	if (timer_info.mask_timer_irq)
193 		timer_irq_disable();
194 	else
195 		sbi_set_timer(ULONG_MAX);
196 
197 	if (!timer_irq_pending())
198 		timer_info.timer_irq_cleared = true;
199 }
200 
201 static void timer_check_set_timer(bool mask_timer_irq)
202 {
203 	struct sbiret ret;
204 	unsigned long begin, end, duration;
205 	const char *mask_test_str = mask_timer_irq ? " for mask irq test" : "";
206 	unsigned long d = getenv("SBI_TIMER_DELAY") ? strtol(getenv("SBI_TIMER_DELAY"), NULL, 0) : 200000;
207 	unsigned long margin = getenv("SBI_TIMER_MARGIN") ? strtol(getenv("SBI_TIMER_MARGIN"), NULL, 0) : 200000;
208 
209 	d = usec_to_cycles(d);
210 	margin = usec_to_cycles(margin);
211 
212 	timer_info = (struct timer_info){ .mask_timer_irq = mask_timer_irq };
213 	begin = timer_get_cycles();
214 	ret = sbi_set_timer(begin + d);
215 
216 	report(!ret.error, "set timer%s", mask_test_str);
217 	if (ret.error)
218 		report_info("set timer%s failed with %ld\n", mask_test_str, ret.error);
219 
220 	while ((end = timer_get_cycles()) <= (begin + d + margin) && !timer_info.timer_works)
221 		cpu_relax();
222 
223 	report(timer_info.timer_works, "timer interrupt received%s", mask_test_str);
224 	report(timer_info.timer_irq_set, "pending timer interrupt bit set in irq handler%s", mask_test_str);
225 
226 	if (!mask_timer_irq) {
227 		report(timer_info.timer_irq_set && timer_info.timer_irq_cleared,
228 		       "pending timer interrupt bit cleared by setting timer to -1");
229 	}
230 
231 	if (timer_info.timer_works) {
232 		duration = end - begin;
233 		report(duration >= d && duration <= (d + margin), "timer delay honored%s", mask_test_str);
234 	}
235 
236 	report(timer_info.timer_irq_count == 1, "timer interrupt received exactly once%s", mask_test_str);
237 }
238 
239 static void check_time(void)
240 {
241 	bool pending;
242 
243 	report_prefix_push("time");
244 
245 	if (!sbi_probe(SBI_EXT_TIME)) {
246 		report_skip("time extension not available");
247 		report_prefix_pop();
248 		return;
249 	}
250 
251 	report_prefix_push("set_timer");
252 
253 	install_irq_handler(IRQ_S_TIMER, timer_irq_handler);
254 	local_irq_enable();
255 	timer_irq_enable();
256 
257 	timer_check_set_timer(false);
258 
259 	if (csr_read(CSR_SIE) & IE_TIE)
260 		timer_check_set_timer(true);
261 	else
262 		report_skip("timer irq enable bit is not writable, skipping mask irq test");
263 
264 	timer_irq_disable();
265 	sbi_set_timer(0);
266 	pending = timer_irq_pending();
267 	report(pending, "timer immediately pending by setting timer to 0");
268 	sbi_set_timer(ULONG_MAX);
269 	if (pending)
270 		report(!timer_irq_pending(), "pending timer cleared while masked");
271 	else
272 		report_skip("timer is not pending, skipping timer cleared while masked test");
273 
274 	local_irq_disable();
275 	install_irq_handler(IRQ_S_TIMER, NULL);
276 
277 	report_prefix_pop();
278 	report_prefix_pop();
279 }
280 
281 #define DBCN_WRITE_TEST_STRING		"DBCN_WRITE_TEST_STRING\n"
282 #define DBCN_WRITE_BYTE_TEST_BYTE	((u8)'a')
283 
284 static void dbcn_write_test(const char *s, unsigned long num_bytes)
285 {
286 	unsigned long base_addr_lo, base_addr_hi;
287 	phys_addr_t paddr = virt_to_phys((void *)s);
288 	int num_calls = 0;
289 	struct sbiret ret;
290 
291 	split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
292 
293 	do {
294 		ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, num_bytes, base_addr_lo, base_addr_hi);
295 		num_bytes -= ret.value;
296 		paddr += ret.value;
297 		split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
298 		num_calls++;
299 	} while (num_bytes != 0 && ret.error == SBI_SUCCESS);
300 
301 	report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error);
302 	report_info("%d sbi calls made", num_calls);
303 }
304 
305 static void dbcn_high_write_test(const char *s, unsigned long num_bytes,
306 				 phys_addr_t page_addr, size_t page_offset)
307 {
308 	int nr_pages = page_offset ? 2 : 1;
309 	void *vaddr;
310 
311 	if (page_addr != PAGE_ALIGN(page_addr) || page_addr + PAGE_SIZE < HIGH_ADDR_BOUNDARY ||
312 	    !check_addr(page_addr, nr_pages * PAGE_SIZE)) {
313 		report_skip("Memory above 4G required");
314 		return;
315 	}
316 
317 	vaddr = alloc_vpages(nr_pages);
318 
319 	for (int i = 0; i < nr_pages; ++i)
320 		install_page(current_pgtable(), page_addr + i * PAGE_SIZE, vaddr + i * PAGE_SIZE);
321 	memcpy(vaddr + page_offset, DBCN_WRITE_TEST_STRING, num_bytes);
322 	dbcn_write_test(vaddr + page_offset, num_bytes);
323 }
324 
325 /*
326  * Only the write functionality is tested here. There's no easy way to
327  * non-interactively test the read functionality.
328  */
329 static void check_dbcn(void)
330 {
331 	unsigned long num_bytes = strlen(DBCN_WRITE_TEST_STRING);
332 	unsigned long base_addr_lo, base_addr_hi;
333 	bool do_invalid_addr = false;
334 	phys_addr_t paddr;
335 	struct sbiret ret;
336 	const char *tmp;
337 	char *buf;
338 
339 	report_prefix_push("dbcn");
340 
341 	ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_DBCN);
342 	if (!ret.value) {
343 		report_skip("DBCN extension unavailable");
344 		report_prefix_pop();
345 		return;
346 	}
347 
348 	report_prefix_push("write");
349 
350 	dbcn_write_test(DBCN_WRITE_TEST_STRING, num_bytes);
351 
352 	assert(num_bytes < PAGE_SIZE);
353 
354 	report_prefix_push("page boundary");
355 	buf = alloc_pages(1);
356 	memcpy(&buf[PAGE_SIZE - num_bytes / 2], DBCN_WRITE_TEST_STRING, num_bytes);
357 	dbcn_write_test(&buf[PAGE_SIZE - num_bytes / 2], num_bytes);
358 	report_prefix_pop();
359 
360 	report_prefix_push("high boundary");
361 	tmp = getenv("SBI_DBCN_SKIP_HIGH_BOUNDARY");
362 	if (!tmp || atol(tmp) == 0)
363 		dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes,
364 				     HIGH_ADDR_BOUNDARY - PAGE_SIZE, PAGE_SIZE - num_bytes / 2);
365 	else
366 		report_skip("user disabled");
367 	report_prefix_pop();
368 
369 	report_prefix_push("high page");
370 	tmp = getenv("SBI_DBCN_SKIP_HIGH_PAGE");
371 	if (!tmp || atol(tmp) == 0) {
372 		paddr = HIGH_ADDR_BOUNDARY;
373 		tmp = getenv("HIGH_PAGE");
374 		if (tmp)
375 			paddr = strtoull(tmp, NULL, 0);
376 		dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes, paddr, 0);
377 	} else {
378 		report_skip("user disabled");
379 	}
380 	report_prefix_pop();
381 
382 	/* Bytes are read from memory and written to the console */
383 	report_prefix_push("invalid parameter");
384 	tmp = getenv("INVALID_ADDR_AUTO");
385 	if (tmp && atol(tmp) == 1) {
386 		paddr = get_highest_addr() + 1;
387 		do_invalid_addr = true;
388 	} else if (env_or_skip("INVALID_ADDR")) {
389 		paddr = strtoull(getenv("INVALID_ADDR"), NULL, 0);
390 		do_invalid_addr = true;
391 	}
392 
393 	if (do_invalid_addr) {
394 		split_phys_addr(paddr, &base_addr_hi, &base_addr_lo);
395 		ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, 1, base_addr_lo, base_addr_hi);
396 		report(ret.error == SBI_ERR_INVALID_PARAM, "address (error=%ld)", ret.error);
397 	}
398 	report_prefix_pop();
399 
400 	report_prefix_pop();
401 	report_prefix_push("write_byte");
402 
403 	puts("DBCN_WRITE TEST CHAR: ");
404 	ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, (u8)DBCN_WRITE_BYTE_TEST_BYTE, 0, 0);
405 	puts("\n");
406 	report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error);
407 	report(ret.value == 0, "expected ret.value (%ld)", ret.value);
408 
409 	report_prefix_pop();
410 	report_prefix_pop();
411 }
412 
413 int main(int argc, char **argv)
414 {
415 	if (argc > 1 && !strcmp(argv[1], "-h")) {
416 		help();
417 		exit(0);
418 	}
419 
420 	report_prefix_push("sbi");
421 	check_base();
422 	check_time();
423 	check_dbcn();
424 
425 	return report_summary();
426 }
427