brintos

brintos / linux-shallow public Read only

0
0
Text · 16.2 KiB · 84e5d9f Raw
718 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * selftest for sparc64's privileged ADI driver4 *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 0x000129 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 min48#define min(x, y) ((x) < (y) ? x : y)49#endif50 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	6459#define ADI_MAX_VERSION	1560 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	581static 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 RAM154			 * replace the "-" with a space155			 */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	4096347static 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	10327387static 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	12541427static 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        9434560static 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        14963612static 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		else702			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	else711		ksft_exit_pass();712 713	/* it's impossible to get here, but the compiler throws a warning714	 * about control reaching the end of non-void function.  bah.715	 */716	return 0;717}718