cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spidev_test.c (10529B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * SPI testing utility (using spidev driver)
      4 *
      5 * Copyright (c) 2007  MontaVista Software, Inc.
      6 * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
      7 *
      8 * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
      9 */
     10
     11#include <stdint.h>
     12#include <unistd.h>
     13#include <stdio.h>
     14#include <stdlib.h>
     15#include <string.h>
     16#include <errno.h>
     17#include <getopt.h>
     18#include <fcntl.h>
     19#include <time.h>
     20#include <sys/ioctl.h>
     21#include <linux/ioctl.h>
     22#include <sys/stat.h>
     23#include <linux/types.h>
     24#include <linux/spi/spidev.h>
     25
     26#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
     27
     28static void pabort(const char *s)
     29{
     30	if (errno != 0)
     31		perror(s);
     32	else
     33		printf("%s\n", s);
     34
     35	abort();
     36}
     37
     38static const char *device = "/dev/spidev1.1";
     39static uint32_t mode;
     40static uint8_t bits = 8;
     41static char *input_file;
     42static char *output_file;
     43static uint32_t speed = 500000;
     44static uint16_t delay;
     45static int verbose;
     46static int transfer_size;
     47static int iterations;
     48static int interval = 5; /* interval in seconds for showing transfer rate */
     49
     50static uint8_t default_tx[] = {
     51	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
     52	0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
     53	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
     54	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
     55	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
     56	0xF0, 0x0D,
     57};
     58
     59static uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
     60static char *input_tx;
     61
     62static void hex_dump(const void *src, size_t length, size_t line_size,
     63		     char *prefix)
     64{
     65	int i = 0;
     66	const unsigned char *address = src;
     67	const unsigned char *line = address;
     68	unsigned char c;
     69
     70	printf("%s | ", prefix);
     71	while (length-- > 0) {
     72		printf("%02X ", *address++);
     73		if (!(++i % line_size) || (length == 0 && i % line_size)) {
     74			if (length == 0) {
     75				while (i++ % line_size)
     76					printf("__ ");
     77			}
     78			printf(" |");
     79			while (line < address) {
     80				c = *line++;
     81				printf("%c", (c < 32 || c > 126) ? '.' : c);
     82			}
     83			printf("|\n");
     84			if (length > 0)
     85				printf("%s | ", prefix);
     86		}
     87	}
     88}
     89
     90/*
     91 *  Unescape - process hexadecimal escape character
     92 *      converts shell input "\x23" -> 0x23
     93 */
     94static int unescape(char *_dst, char *_src, size_t len)
     95{
     96	int ret = 0;
     97	int match;
     98	char *src = _src;
     99	char *dst = _dst;
    100	unsigned int ch;
    101
    102	while (*src) {
    103		if (*src == '\\' && *(src+1) == 'x') {
    104			match = sscanf(src + 2, "%2x", &ch);
    105			if (!match)
    106				pabort("malformed input string");
    107
    108			src += 4;
    109			*dst++ = (unsigned char)ch;
    110		} else {
    111			*dst++ = *src++;
    112		}
    113		ret++;
    114	}
    115	return ret;
    116}
    117
    118static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
    119{
    120	int ret;
    121	int out_fd;
    122	struct spi_ioc_transfer tr = {
    123		.tx_buf = (unsigned long)tx,
    124		.rx_buf = (unsigned long)rx,
    125		.len = len,
    126		.delay_usecs = delay,
    127		.speed_hz = speed,
    128		.bits_per_word = bits,
    129	};
    130
    131	if (mode & SPI_TX_OCTAL)
    132		tr.tx_nbits = 8;
    133	else if (mode & SPI_TX_QUAD)
    134		tr.tx_nbits = 4;
    135	else if (mode & SPI_TX_DUAL)
    136		tr.tx_nbits = 2;
    137	if (mode & SPI_RX_OCTAL)
    138		tr.rx_nbits = 8;
    139	else if (mode & SPI_RX_QUAD)
    140		tr.rx_nbits = 4;
    141	else if (mode & SPI_RX_DUAL)
    142		tr.rx_nbits = 2;
    143	if (!(mode & SPI_LOOP)) {
    144		if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
    145			tr.rx_buf = 0;
    146		else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
    147			tr.tx_buf = 0;
    148	}
    149
    150	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    151	if (ret < 1)
    152		pabort("can't send spi message");
    153
    154	if (verbose)
    155		hex_dump(tx, len, 32, "TX");
    156
    157	if (output_file) {
    158		out_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
    159		if (out_fd < 0)
    160			pabort("could not open output file");
    161
    162		ret = write(out_fd, rx, len);
    163		if (ret != len)
    164			pabort("not all bytes written to output file");
    165
    166		close(out_fd);
    167	}
    168
    169	if (verbose)
    170		hex_dump(rx, len, 32, "RX");
    171}
    172
    173static void print_usage(const char *prog)
    174{
    175	printf("Usage: %s [-DsbdlHOLC3vpNR24SI]\n", prog);
    176	puts("  -D --device   device to use (default /dev/spidev1.1)\n"
    177	     "  -s --speed    max speed (Hz)\n"
    178	     "  -d --delay    delay (usec)\n"
    179	     "  -b --bpw      bits per word\n"
    180	     "  -i --input    input data from a file (e.g. \"test.bin\")\n"
    181	     "  -o --output   output data to a file (e.g. \"results.bin\")\n"
    182	     "  -l --loop     loopback\n"
    183	     "  -H --cpha     clock phase\n"
    184	     "  -O --cpol     clock polarity\n"
    185	     "  -L --lsb      least significant bit first\n"
    186	     "  -C --cs-high  chip select active high\n"
    187	     "  -3 --3wire    SI/SO signals shared\n"
    188	     "  -v --verbose  Verbose (show tx buffer)\n"
    189	     "  -p            Send data (e.g. \"1234\\xde\\xad\")\n"
    190	     "  -N --no-cs    no chip select\n"
    191	     "  -R --ready    slave pulls low to pause\n"
    192	     "  -2 --dual     dual transfer\n"
    193	     "  -4 --quad     quad transfer\n"
    194	     "  -8 --octal    octal transfer\n"
    195	     "  -S --size     transfer size\n"
    196	     "  -I --iter     iterations\n");
    197	exit(1);
    198}
    199
    200static void parse_opts(int argc, char *argv[])
    201{
    202	while (1) {
    203		static const struct option lopts[] = {
    204			{ "device",  1, 0, 'D' },
    205			{ "speed",   1, 0, 's' },
    206			{ "delay",   1, 0, 'd' },
    207			{ "bpw",     1, 0, 'b' },
    208			{ "input",   1, 0, 'i' },
    209			{ "output",  1, 0, 'o' },
    210			{ "loop",    0, 0, 'l' },
    211			{ "cpha",    0, 0, 'H' },
    212			{ "cpol",    0, 0, 'O' },
    213			{ "lsb",     0, 0, 'L' },
    214			{ "cs-high", 0, 0, 'C' },
    215			{ "3wire",   0, 0, '3' },
    216			{ "no-cs",   0, 0, 'N' },
    217			{ "ready",   0, 0, 'R' },
    218			{ "dual",    0, 0, '2' },
    219			{ "verbose", 0, 0, 'v' },
    220			{ "quad",    0, 0, '4' },
    221			{ "octal",   0, 0, '8' },
    222			{ "size",    1, 0, 'S' },
    223			{ "iter",    1, 0, 'I' },
    224			{ NULL, 0, 0, 0 },
    225		};
    226		int c;
    227
    228		c = getopt_long(argc, argv, "D:s:d:b:i:o:lHOLC3NR248p:vS:I:",
    229				lopts, NULL);
    230
    231		if (c == -1)
    232			break;
    233
    234		switch (c) {
    235		case 'D':
    236			device = optarg;
    237			break;
    238		case 's':
    239			speed = atoi(optarg);
    240			break;
    241		case 'd':
    242			delay = atoi(optarg);
    243			break;
    244		case 'b':
    245			bits = atoi(optarg);
    246			break;
    247		case 'i':
    248			input_file = optarg;
    249			break;
    250		case 'o':
    251			output_file = optarg;
    252			break;
    253		case 'l':
    254			mode |= SPI_LOOP;
    255			break;
    256		case 'H':
    257			mode |= SPI_CPHA;
    258			break;
    259		case 'O':
    260			mode |= SPI_CPOL;
    261			break;
    262		case 'L':
    263			mode |= SPI_LSB_FIRST;
    264			break;
    265		case 'C':
    266			mode |= SPI_CS_HIGH;
    267			break;
    268		case '3':
    269			mode |= SPI_3WIRE;
    270			break;
    271		case 'N':
    272			mode |= SPI_NO_CS;
    273			break;
    274		case 'v':
    275			verbose = 1;
    276			break;
    277		case 'R':
    278			mode |= SPI_READY;
    279			break;
    280		case 'p':
    281			input_tx = optarg;
    282			break;
    283		case '2':
    284			mode |= SPI_TX_DUAL;
    285			break;
    286		case '4':
    287			mode |= SPI_TX_QUAD;
    288			break;
    289		case '8':
    290			mode |= SPI_TX_OCTAL;
    291			break;
    292		case 'S':
    293			transfer_size = atoi(optarg);
    294			break;
    295		case 'I':
    296			iterations = atoi(optarg);
    297			break;
    298		default:
    299			print_usage(argv[0]);
    300		}
    301	}
    302	if (mode & SPI_LOOP) {
    303		if (mode & SPI_TX_DUAL)
    304			mode |= SPI_RX_DUAL;
    305		if (mode & SPI_TX_QUAD)
    306			mode |= SPI_RX_QUAD;
    307		if (mode & SPI_TX_OCTAL)
    308			mode |= SPI_RX_OCTAL;
    309	}
    310}
    311
    312static void transfer_escaped_string(int fd, char *str)
    313{
    314	size_t size = strlen(str);
    315	uint8_t *tx;
    316	uint8_t *rx;
    317
    318	tx = malloc(size);
    319	if (!tx)
    320		pabort("can't allocate tx buffer");
    321
    322	rx = malloc(size);
    323	if (!rx)
    324		pabort("can't allocate rx buffer");
    325
    326	size = unescape((char *)tx, str, size);
    327	transfer(fd, tx, rx, size);
    328	free(rx);
    329	free(tx);
    330}
    331
    332static void transfer_file(int fd, char *filename)
    333{
    334	ssize_t bytes;
    335	struct stat sb;
    336	int tx_fd;
    337	uint8_t *tx;
    338	uint8_t *rx;
    339
    340	if (stat(filename, &sb) == -1)
    341		pabort("can't stat input file");
    342
    343	tx_fd = open(filename, O_RDONLY);
    344	if (tx_fd < 0)
    345		pabort("can't open input file");
    346
    347	tx = malloc(sb.st_size);
    348	if (!tx)
    349		pabort("can't allocate tx buffer");
    350
    351	rx = malloc(sb.st_size);
    352	if (!rx)
    353		pabort("can't allocate rx buffer");
    354
    355	bytes = read(tx_fd, tx, sb.st_size);
    356	if (bytes != sb.st_size)
    357		pabort("failed to read input file");
    358
    359	transfer(fd, tx, rx, sb.st_size);
    360	free(rx);
    361	free(tx);
    362	close(tx_fd);
    363}
    364
    365static uint64_t _read_count;
    366static uint64_t _write_count;
    367
    368static void show_transfer_rate(void)
    369{
    370	static uint64_t prev_read_count, prev_write_count;
    371	double rx_rate, tx_rate;
    372
    373	rx_rate = ((_read_count - prev_read_count) * 8) / (interval*1000.0);
    374	tx_rate = ((_write_count - prev_write_count) * 8) / (interval*1000.0);
    375
    376	printf("rate: tx %.1fkbps, rx %.1fkbps\n", rx_rate, tx_rate);
    377
    378	prev_read_count = _read_count;
    379	prev_write_count = _write_count;
    380}
    381
    382static void transfer_buf(int fd, int len)
    383{
    384	uint8_t *tx;
    385	uint8_t *rx;
    386	int i;
    387
    388	tx = malloc(len);
    389	if (!tx)
    390		pabort("can't allocate tx buffer");
    391	for (i = 0; i < len; i++)
    392		tx[i] = random();
    393
    394	rx = malloc(len);
    395	if (!rx)
    396		pabort("can't allocate rx buffer");
    397
    398	transfer(fd, tx, rx, len);
    399
    400	_write_count += len;
    401	_read_count += len;
    402
    403	if (mode & SPI_LOOP) {
    404		if (memcmp(tx, rx, len)) {
    405			fprintf(stderr, "transfer error !\n");
    406			hex_dump(tx, len, 32, "TX");
    407			hex_dump(rx, len, 32, "RX");
    408			exit(1);
    409		}
    410	}
    411
    412	free(rx);
    413	free(tx);
    414}
    415
    416int main(int argc, char *argv[])
    417{
    418	int ret = 0;
    419	int fd;
    420
    421	parse_opts(argc, argv);
    422
    423	if (input_tx && input_file)
    424		pabort("only one of -p and --input may be selected");
    425
    426	fd = open(device, O_RDWR);
    427	if (fd < 0)
    428		pabort("can't open device");
    429
    430	/*
    431	 * spi mode
    432	 */
    433	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
    434	if (ret == -1)
    435		pabort("can't set spi mode");
    436
    437	ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
    438	if (ret == -1)
    439		pabort("can't get spi mode");
    440
    441	/*
    442	 * bits per word
    443	 */
    444	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    445	if (ret == -1)
    446		pabort("can't set bits per word");
    447
    448	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    449	if (ret == -1)
    450		pabort("can't get bits per word");
    451
    452	/*
    453	 * max speed hz
    454	 */
    455	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    456	if (ret == -1)
    457		pabort("can't set max speed hz");
    458
    459	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    460	if (ret == -1)
    461		pabort("can't get max speed hz");
    462
    463	printf("spi mode: 0x%x\n", mode);
    464	printf("bits per word: %u\n", bits);
    465	printf("max speed: %u Hz (%u kHz)\n", speed, speed/1000);
    466
    467	if (input_tx)
    468		transfer_escaped_string(fd, input_tx);
    469	else if (input_file)
    470		transfer_file(fd, input_file);
    471	else if (transfer_size) {
    472		struct timespec last_stat;
    473
    474		clock_gettime(CLOCK_MONOTONIC, &last_stat);
    475
    476		while (iterations-- > 0) {
    477			struct timespec current;
    478
    479			transfer_buf(fd, transfer_size);
    480
    481			clock_gettime(CLOCK_MONOTONIC, &current);
    482			if (current.tv_sec - last_stat.tv_sec > interval) {
    483				show_transfer_rate();
    484				last_stat = current;
    485			}
    486		}
    487		printf("total: tx %.1fKB, rx %.1fKB\n",
    488		       _write_count/1024.0, _read_count/1024.0);
    489	} else
    490		transfer(fd, default_tx, default_rx, sizeof(default_tx));
    491
    492	close(fd);
    493
    494	return ret;
    495}