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