1// SPDX-License-Identifier: GPL-2.0
2/*
3 * selftest for sparc64's privileged ADI driver
4 *
5 * Author: Tom Hromatka <tom.hromatka@oracle.com>
6 */
7#include <linux/kernel.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <stdarg.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <sys/syscall.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <unistd.h>
18
19#include "../../kselftest.h"
20
21#define DEBUG_LEVEL_1_BIT	(0x0001)
22#define DEBUG_LEVEL_2_BIT	(0x0002)
23#define DEBUG_LEVEL_3_BIT	(0x0004)
24#define DEBUG_LEVEL_4_BIT	(0x0008)
25#define DEBUG_TIMING_BIT	(0x1000)
26
27/* bit mask of enabled bits to print */
28#define DEBUG 0x0001
29
30#define DEBUG_PRINT_L1(...)	debug_print(DEBUG_LEVEL_1_BIT, __VA_ARGS__)
31#define DEBUG_PRINT_L2(...)	debug_print(DEBUG_LEVEL_2_BIT, __VA_ARGS__)
32#define DEBUG_PRINT_L3(...)	debug_print(DEBUG_LEVEL_3_BIT, __VA_ARGS__)
33#define DEBUG_PRINT_L4(...)	debug_print(DEBUG_LEVEL_4_BIT, __VA_ARGS__)
34#define DEBUG_PRINT_T(...)	debug_print(DEBUG_TIMING_BIT, __VA_ARGS__)
35
36static void debug_print(int level, const char *s, ...)
37{
38	va_list args;
39
40	va_start(args, s);
41
42	if (DEBUG & level)
43		vfprintf(stdout, s, args);
44	va_end(args);
45}
46
47#ifndef min
48#define min(x, y) ((x) < (y) ? x : y)
49#endif
50
51#define RETURN_FROM_TEST(_ret) \
52	do { \
53		DEBUG_PRINT_L1( \
54			"\tTest %s returned %d\n", __func__, _ret); \
55		return _ret; \
56	} while (0)
57
58#define ADI_BLKSZ	64
59#define ADI_MAX_VERSION	15
60
61#define TEST_STEP_FAILURE(_ret) \
62	do { \
63		fprintf(stderr, "\tTest step failure: %d at %s:%d\n", \
64			_ret, __func__, __LINE__); \
65		goto out; \
66	} while (0)
67
68#define RDTICK(_x) \
69	asm volatile(" rd %%tick, %0\n" : "=r" (_x))
70
71static int random_version(void)
72{
73	long tick;
74
75	RDTICK(tick);
76
77	return tick % (ADI_MAX_VERSION + 1);
78}
79
80#define MAX_RANGES_SUPPORTED	5
81static const char system_ram_str[] = "System RAM\n";
82static int range_count;
83static unsigned long long int start_addr[MAX_RANGES_SUPPORTED];
84static unsigned long long int   end_addr[MAX_RANGES_SUPPORTED];
85
86struct stats {
87	char		name[16];
88	unsigned long	total;
89	unsigned long	count;
90	unsigned long	bytes;
91};
92
93static struct stats read_stats = {
94	.name = "read", .total = 0, .count = 0, .bytes = 0};
95static struct stats pread_stats = {
96	.name = "pread", .total = 0, .count = 0, .bytes = 0};
97static struct stats write_stats = {
98	.name = "write", .total = 0, .count = 0, .bytes = 0};
99static struct stats pwrite_stats = {
100	.name = "pwrite", .total = 0, .count = 0, .bytes = 0};
101static struct stats seek_stats = {
102	.name = "seek", .total = 0, .count = 0, .bytes = 0};
103
104static void update_stats(struct stats * const ustats,
105			 unsigned long measurement, unsigned long bytes)
106{
107	ustats->total += measurement;
108	ustats->bytes += bytes;
109	ustats->count++;
110}
111
112static void print_ustats(const struct stats * const ustats)
113{
114	DEBUG_PRINT_L1("%s\t%7d\t%7.0f\t%7.0f\n",
115		       ustats->name, ustats->count,
116		       (float)ustats->total / (float)ustats->count,
117		       (float)ustats->bytes / (float)ustats->count);
118}
119
120static void print_stats(void)
121{
122	DEBUG_PRINT_L1("\nSyscall\tCall\tAvgTime\tAvgSize\n"
123		       "\tCount\t(ticks)\t(bytes)\n"
124		       "-------------------------------\n");
125
126	print_ustats(&read_stats);
127	print_ustats(&pread_stats);
128	print_ustats(&write_stats);
129	print_ustats(&pwrite_stats);
130	print_ustats(&seek_stats);
131}
132
133static int build_memory_map(void)
134{
135	char line[256];
136	FILE *fp;
137	int i;
138
139	range_count = 0;
140
141	fp = fopen("/proc/iomem", "r");
142	if (!fp) {
143		fprintf(stderr, "/proc/iomem: error %d: %s\n",
144			errno, strerror(errno));
145		return -errno;
146	}
147
148	while (fgets(line, sizeof(line), fp) != 0) {
149		if (strstr(line, system_ram_str)) {
150			char *dash, *end_ptr;
151
152			/* Given a line like this:
153			 * d0400000-10ffaffff : System RAM
154			 * replace the "-" with a space
155			 */
156			dash = strstr(line, "-");
157			dash[0] = 0x20;
158
159			start_addr[range_count] = strtoull(line, &end_ptr, 16);
160			end_addr[range_count] = strtoull(end_ptr, NULL, 16);
161			range_count++;
162		}
163	}
164
165	fclose(fp);
166
167	DEBUG_PRINT_L1("RAM Ranges\n");
168	for (i = 0; i < range_count; i++)
169		DEBUG_PRINT_L1("\trange %d: 0x%llx\t- 0x%llx\n",
170			       i, start_addr[i], end_addr[i]);
171
172	if (range_count == 0) {
173		fprintf(stderr, "No valid address ranges found.  Error.\n");
174		return -1;
175	}
176
177	return 0;
178}
179
180static int read_adi(int fd, unsigned char *buf, int buf_sz)
181{
182	int ret, bytes_read = 0;
183	long start, end, elapsed_time = 0;
184
185	do {
186		RDTICK(start);
187		ret = read(fd, buf + bytes_read, buf_sz - bytes_read);
188		RDTICK(end);
189		if (ret < 0)
190			return -errno;
191
192		elapsed_time += end - start;
193		update_stats(&read_stats, elapsed_time, buf_sz);
194		bytes_read += ret;
195
196	} while (bytes_read < buf_sz);
197
198	DEBUG_PRINT_T("\tread elapsed timed = %ld\n", elapsed_time);
199	DEBUG_PRINT_L3("\tRead  %d bytes\n", bytes_read);
200
201	return bytes_read;
202}
203
204static int pread_adi(int fd, unsigned char *buf,
205		     int buf_sz, unsigned long offset)
206{
207	int ret, i, bytes_read = 0;
208	unsigned long cur_offset;
209	long start, end, elapsed_time = 0;
210
211	cur_offset = offset;
212	do {
213		RDTICK(start);
214		ret = pread(fd, buf + bytes_read, buf_sz - bytes_read,
215			    cur_offset);
216		RDTICK(end);
217		if (ret < 0)
218			return -errno;
219
220		elapsed_time += end - start;
221		update_stats(&pread_stats, elapsed_time, buf_sz);
222		bytes_read += ret;
223		cur_offset += ret;
224
225	} while (bytes_read < buf_sz);
226
227	DEBUG_PRINT_T("\tpread elapsed timed = %ld\n", elapsed_time);
228	DEBUG_PRINT_L3("\tRead  %d bytes starting at offset 0x%lx\n",
229		       bytes_read, offset);
230	for (i = 0; i < bytes_read; i++)
231		DEBUG_PRINT_L4("\t\t0x%lx\t%d\n", offset + i, buf[i]);
232
233	return bytes_read;
234}
235
236static int write_adi(int fd, const unsigned char * const buf, int buf_sz)
237{
238	int ret, bytes_written = 0;
239	long start, end, elapsed_time = 0;
240
241	do {
242		RDTICK(start);
243		ret = write(fd, buf + bytes_written, buf_sz - bytes_written);
244		RDTICK(end);
245		if (ret < 0)
246			return -errno;
247
248		elapsed_time += (end - start);
249		update_stats(&write_stats, elapsed_time, buf_sz);
250		bytes_written += ret;
251	} while (bytes_written < buf_sz);
252
253	DEBUG_PRINT_T("\twrite elapsed timed = %ld\n", elapsed_time);
254	DEBUG_PRINT_L3("\tWrote %d of %d bytes\n", bytes_written, buf_sz);
255
256	return bytes_written;
257}
258
259static int pwrite_adi(int fd, const unsigned char * const buf,
260		      int buf_sz, unsigned long offset)
261{
262	int ret, bytes_written = 0;
263	unsigned long cur_offset;
264	long start, end, elapsed_time = 0;
265
266	cur_offset = offset;
267
268	do {
269		RDTICK(start);
270		ret = pwrite(fd, buf + bytes_written,
271			     buf_sz - bytes_written, cur_offset);
272		RDTICK(end);
273		if (ret < 0) {
274			fprintf(stderr, "pwrite(): error %d: %s\n",
275				errno, strerror(errno));
276			return -errno;
277		}
278
279		elapsed_time += (end - start);
280		update_stats(&pwrite_stats, elapsed_time, buf_sz);
281		bytes_written += ret;
282		cur_offset += ret;
283
284	} while (bytes_written < buf_sz);
285
286	DEBUG_PRINT_T("\tpwrite elapsed timed = %ld\n", elapsed_time);
287	DEBUG_PRINT_L3("\tWrote %d of %d bytes starting at address 0x%lx\n",
288		       bytes_written, buf_sz, offset);
289
290	return bytes_written;
291}
292
293static off_t seek_adi(int fd, off_t offset, int whence)
294{
295	long start, end;
296	off_t ret;
297
298	RDTICK(start);
299	ret = lseek(fd, offset, whence);
300	RDTICK(end);
301	DEBUG_PRINT_L2("\tlseek ret = 0x%llx\n", ret);
302	if (ret < 0)
303		goto out;
304
305	DEBUG_PRINT_T("\tlseek elapsed timed = %ld\n", end - start);
306	update_stats(&seek_stats, end - start, 0);
307
308out:
309	(void)lseek(fd, 0, SEEK_END);
310	return ret;
311}
312
313static int test0_prpw_aligned_1byte(int fd)
314{
315	/* somewhat arbitrarily chosen address */
316	unsigned long paddr =
317		(end_addr[range_count - 1] - 0x1000) & ~(ADI_BLKSZ - 1);
318	unsigned char version[1], expected_version;
319	loff_t offset;
320	int ret;
321
322	version[0] = random_version();
323	expected_version = version[0];
324
325	offset = paddr / ADI_BLKSZ;
326
327	ret = pwrite_adi(fd, version, sizeof(version), offset);
328	if (ret != sizeof(version))
329		TEST_STEP_FAILURE(ret);
330
331	ret = pread_adi(fd, version, sizeof(version), offset);
332	if (ret != sizeof(version))
333		TEST_STEP_FAILURE(ret);
334
335	if (expected_version != version[0]) {
336		DEBUG_PRINT_L2("\tExpected version %d but read version %d\n",
337			       expected_version, version[0]);
338		TEST_STEP_FAILURE(-expected_version);
339	}
340
341	ret = 0;
342out:
343	RETURN_FROM_TEST(ret);
344}
345
346#define TEST1_VERSION_SZ	4096
347static int test1_prpw_aligned_4096bytes(int fd)
348{
349	/* somewhat arbitrarily chosen address */
350	unsigned long paddr =
351		(end_addr[range_count - 1] - 0x6000) & ~(ADI_BLKSZ - 1);
352	unsigned char version[TEST1_VERSION_SZ],
353		expected_version[TEST1_VERSION_SZ];
354	loff_t offset;
355	int ret, i;
356
357	for (i = 0; i < TEST1_VERSION_SZ; i++) {
358		version[i] = random_version();
359		expected_version[i] = version[i];
360	}
361
362	offset = paddr / ADI_BLKSZ;
363
364	ret = pwrite_adi(fd, version, sizeof(version), offset);
365	if (ret != sizeof(version))
366		TEST_STEP_FAILURE(ret);
367
368	ret = pread_adi(fd, version, sizeof(version), offset);
369	if (ret != sizeof(version))
370		TEST_STEP_FAILURE(ret);
371
372	for (i = 0; i < TEST1_VERSION_SZ; i++) {
373		if (expected_version[i] != version[i]) {
374			DEBUG_PRINT_L2(
375				"\tExpected version %d but read version %d\n",
376				expected_version, version[0]);
377			TEST_STEP_FAILURE(-expected_version[i]);
378		}
379	}
380
381	ret = 0;
382out:
383	RETURN_FROM_TEST(ret);
384}
385
386#define TEST2_VERSION_SZ	10327
387static int test2_prpw_aligned_10327bytes(int fd)
388{
389	/* somewhat arbitrarily chosen address */
390	unsigned long paddr =
391		(start_addr[0] + 0x6000) & ~(ADI_BLKSZ - 1);
392	unsigned char version[TEST2_VERSION_SZ],
393		expected_version[TEST2_VERSION_SZ];
394	loff_t offset;
395	int ret, i;
396
397	for (i = 0; i < TEST2_VERSION_SZ; i++) {
398		version[i] = random_version();
399		expected_version[i] = version[i];
400	}
401
402	offset = paddr / ADI_BLKSZ;
403
404	ret = pwrite_adi(fd, version, sizeof(version), offset);
405	if (ret != sizeof(version))
406		TEST_STEP_FAILURE(ret);
407
408	ret = pread_adi(fd, version, sizeof(version), offset);
409	if (ret != sizeof(version))
410		TEST_STEP_FAILURE(ret);
411
412	for (i = 0; i < TEST2_VERSION_SZ; i++) {
413		if (expected_version[i] != version[i]) {
414			DEBUG_PRINT_L2(
415				"\tExpected version %d but read version %d\n",
416				expected_version, version[0]);
417			TEST_STEP_FAILURE(-expected_version[i]);
418		}
419	}
420
421	ret = 0;
422out:
423	RETURN_FROM_TEST(ret);
424}
425
426#define TEST3_VERSION_SZ	12541
427static int test3_prpw_unaligned_12541bytes(int fd)
428{
429	/* somewhat arbitrarily chosen address */
430	unsigned long paddr =
431		((start_addr[0] + 0xC000) & ~(ADI_BLKSZ - 1)) + 17;
432	unsigned char version[TEST3_VERSION_SZ],
433		expected_version[TEST3_VERSION_SZ];
434	loff_t offset;
435	int ret, i;
436
437	for (i = 0; i < TEST3_VERSION_SZ; i++) {
438		version[i] = random_version();
439		expected_version[i] = version[i];
440	}
441
442	offset = paddr / ADI_BLKSZ;
443
444	ret = pwrite_adi(fd, version, sizeof(version), offset);
445	if (ret != sizeof(version))
446		TEST_STEP_FAILURE(ret);
447
448	ret = pread_adi(fd, version, sizeof(version), offset);
449	if (ret != sizeof(version))
450		TEST_STEP_FAILURE(ret);
451
452	for (i = 0; i < TEST3_VERSION_SZ; i++) {
453		if (expected_version[i] != version[i]) {
454			DEBUG_PRINT_L2(
455				"\tExpected version %d but read version %d\n",
456				expected_version, version[0]);
457			TEST_STEP_FAILURE(-expected_version[i]);
458		}
459	}
460
461	ret = 0;
462out:
463	RETURN_FROM_TEST(ret);
464}
465
466static int test4_lseek(int fd)
467{
468#define	OFFSET_ADD	(0x100)
469#define OFFSET_SUBTRACT	(0xFFFFFFF000000000)
470
471	off_t offset_out, offset_in;
472	int ret;
473
474
475	offset_in = 0x123456789abcdef0;
476	offset_out = seek_adi(fd, offset_in, SEEK_SET);
477	if (offset_out != offset_in) {
478		ret = -1;
479		TEST_STEP_FAILURE(ret);
480	}
481
482	/* seek to the current offset.  this should return EINVAL */
483	offset_out = seek_adi(fd, offset_in, SEEK_SET);
484	if (offset_out < 0 && errno == EINVAL)
485		DEBUG_PRINT_L2(
486			"\tSEEK_SET failed as designed. Not an error\n");
487	else {
488		ret = -2;
489		TEST_STEP_FAILURE(ret);
490	}
491
492	offset_out = seek_adi(fd, 0, SEEK_CUR);
493	if (offset_out != offset_in) {
494		ret = -3;
495		TEST_STEP_FAILURE(ret);
496	}
497
498	offset_out = seek_adi(fd, OFFSET_ADD, SEEK_CUR);
499	if (offset_out != (offset_in + OFFSET_ADD)) {
500		ret = -4;
501		TEST_STEP_FAILURE(ret);
502	}
503
504	offset_out = seek_adi(fd, OFFSET_SUBTRACT, SEEK_CUR);
505	if (offset_out != (offset_in + OFFSET_ADD + OFFSET_SUBTRACT)) {
506		ret = -5;
507		TEST_STEP_FAILURE(ret);
508	}
509
510	ret = 0;
511out:
512	RETURN_FROM_TEST(ret);
513}
514
515static int test5_rw_aligned_1byte(int fd)
516{
517	/* somewhat arbitrarily chosen address */
518	unsigned long paddr =
519		(end_addr[range_count - 1] - 0xF000) & ~(ADI_BLKSZ - 1);
520	unsigned char version, expected_version;
521	loff_t offset;
522	off_t oret;
523	int ret;
524
525	offset = paddr / ADI_BLKSZ;
526	version = expected_version = random_version();
527
528	oret = seek_adi(fd, offset, SEEK_SET);
529	if (oret != offset) {
530		ret = -1;
531		TEST_STEP_FAILURE(ret);
532	}
533
534	ret = write_adi(fd, &version, sizeof(version));
535	if (ret != sizeof(version))
536		TEST_STEP_FAILURE(ret);
537
538	oret = seek_adi(fd, offset, SEEK_SET);
539	if (oret != offset) {
540		ret = -1;
541		TEST_STEP_FAILURE(ret);
542	}
543
544	ret = read_adi(fd, &version, sizeof(version));
545	if (ret != sizeof(version))
546		TEST_STEP_FAILURE(ret);
547
548	if (expected_version != version) {
549		DEBUG_PRINT_L2("\tExpected version %d but read version %d\n",
550			       expected_version, version);
551		TEST_STEP_FAILURE(-expected_version);
552	}
553
554	ret = 0;
555out:
556	RETURN_FROM_TEST(ret);
557}
558
559#define TEST6_VERSION_SZ        9434
560static int test6_rw_aligned_9434bytes(int fd)
561{
562	/* somewhat arbitrarily chosen address */
563	unsigned long paddr =
564		(end_addr[range_count - 1] - 0x5F000) & ~(ADI_BLKSZ - 1);
565	unsigned char version[TEST6_VERSION_SZ],
566		      expected_version[TEST6_VERSION_SZ];
567	loff_t offset;
568	off_t oret;
569	int ret, i;
570
571	offset = paddr / ADI_BLKSZ;
572	for (i = 0; i < TEST6_VERSION_SZ; i++)
573		version[i] = expected_version[i] = random_version();
574
575	oret = seek_adi(fd, offset, SEEK_SET);
576	if (oret != offset) {
577		ret = -1;
578		TEST_STEP_FAILURE(ret);
579	}
580
581	ret = write_adi(fd, version, sizeof(version));
582	if (ret != sizeof(version))
583		TEST_STEP_FAILURE(ret);
584
585	memset(version, 0, TEST6_VERSION_SZ);
586
587	oret = seek_adi(fd, offset, SEEK_SET);
588	if (oret != offset) {
589		ret = -1;
590		TEST_STEP_FAILURE(ret);
591	}
592
593	ret = read_adi(fd, version, sizeof(version));
594	if (ret != sizeof(version))
595		TEST_STEP_FAILURE(ret);
596
597	for (i = 0; i < TEST6_VERSION_SZ; i++) {
598		if (expected_version[i] != version[i]) {
599			DEBUG_PRINT_L2(
600				"\tExpected version %d but read version %d\n",
601				expected_version[i], version[i]);
602			TEST_STEP_FAILURE(-expected_version[i]);
603		}
604	}
605
606	ret = 0;
607out:
608	RETURN_FROM_TEST(ret);
609}
610
611#define TEST7_VERSION_SZ        14963
612static int test7_rw_aligned_14963bytes(int fd)
613{
614	/* somewhat arbitrarily chosen address */
615	unsigned long paddr =
616	  ((start_addr[range_count - 1] + 0xF000) & ~(ADI_BLKSZ - 1)) + 39;
617	unsigned char version[TEST7_VERSION_SZ],
618		      expected_version[TEST7_VERSION_SZ];
619	loff_t offset;
620	off_t oret;
621	int ret, i;
622
623	offset = paddr / ADI_BLKSZ;
624	for (i = 0; i < TEST7_VERSION_SZ; i++) {
625		version[i] = random_version();
626		expected_version[i] = version[i];
627	}
628
629	oret = seek_adi(fd, offset, SEEK_SET);
630	if (oret != offset) {
631		ret = -1;
632		TEST_STEP_FAILURE(ret);
633	}
634
635	ret = write_adi(fd, version, sizeof(version));
636	if (ret != sizeof(version))
637		TEST_STEP_FAILURE(ret);
638
639	memset(version, 0, TEST7_VERSION_SZ);
640
641	oret = seek_adi(fd, offset, SEEK_SET);
642	if (oret != offset) {
643		ret = -1;
644		TEST_STEP_FAILURE(ret);
645	}
646
647	ret = read_adi(fd, version, sizeof(version));
648	if (ret != sizeof(version))
649		TEST_STEP_FAILURE(ret);
650
651	for (i = 0; i < TEST7_VERSION_SZ; i++) {
652		if (expected_version[i] != version[i]) {
653			DEBUG_PRINT_L2(
654				"\tExpected version %d but read version %d\n",
655				expected_version[i], version[i]);
656			TEST_STEP_FAILURE(-expected_version[i]);
657		}
658
659		paddr += ADI_BLKSZ;
660	}
661
662	ret = 0;
663out:
664	RETURN_FROM_TEST(ret);
665}
666
667static int (*tests[])(int fd) = {
668	test0_prpw_aligned_1byte,
669	test1_prpw_aligned_4096bytes,
670	test2_prpw_aligned_10327bytes,
671	test3_prpw_unaligned_12541bytes,
672	test4_lseek,
673	test5_rw_aligned_1byte,
674	test6_rw_aligned_9434bytes,
675	test7_rw_aligned_14963bytes,
676};
677#define TEST_COUNT	ARRAY_SIZE(tests)
678
679int main(int argc, char *argv[])
680{
681	int fd, ret, test;
682
683	ret = build_memory_map();
684	if (ret < 0)
685		return ret;
686
687	fd = open("/dev/adi", O_RDWR);
688	if (fd < 0) {
689		fprintf(stderr, "open: error %d: %s\n",
690			errno, strerror(errno));
691		return -errno;
692	}
693
694	for (test = 0; test < TEST_COUNT; test++) {
695		DEBUG_PRINT_L1("Running test #%d\n", test);
696
697		ret = (*tests[test])(fd);
698		if (ret != 0)
699			ksft_test_result_fail("Test #%d failed: error %d\n",
700					      test, ret);
701		else
702			ksft_test_result_pass("Test #%d passed\n", test);
703	}
704
705	print_stats();
706	close(fd);
707
708	if (ksft_get_fail_cnt() > 0)
709		ksft_exit_fail();
710	else
711		ksft_exit_pass();
712
713	/* it's impossible to get here, but the compiler throws a warning
714	 * about control reaching the end of non-void function.  bah.
715	 */
716	return 0;
717}
718