aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-06-05 14:27:03 -0400
committerDavid S. Miller <davem@davemloft.net>2018-06-05 14:27:03 -0400
commit2b1f6a04f52b426158362f05eec24f6456d5fea7 (patch)
tree294394123558024171b2c509399d301a693ea0f1
parentfff75eb2a08c2ac96404a2d79685668f3cf5a7a3 (diff)
parent3c545084130c1fd5cf873a5fec3ee29070128e06 (diff)
Merge branch 'adi-driver'
Tom Hromatka says: ==================== sparc64: Add privileged ADI driver ADI is a feature supported on SPARC M7 and newer processors to allow hardware to catch rogue accesses to memory. ADI is supported for data fetches only and not instruction fetches. An app can enable ADI on its data pages, set version tags on them and use versioned addresses to access the data pages. Upper bits of the address contain the version tag. On M7 processors, upper four bits (bits 63-60) contain the version tag. If a rogue app attempts to access ADI enabled data pages, its access is blocked and processor generates an exception. Please see Documentation/sparc/adi.txt for further details. This patchset implements a char driver to read/write ADI versions from privileged user space processes. Intended consumers are makedumpfile and crash. v6: * Addressed a few action items from greg k-h * Added Reviewed-by Greg Kroah-Hartman and Shuah Khan v5: * Fixed MODULE_LICENSE() for adi.c v4: * Fixed messed up subject lines. v3: * Really fixed the copyright headers to use SPDX GPL v2. Really. v2: * Simplified copyright headers * Completely reworked sparc64 selftests Makefiles. Used the android selftests Makefiles as an example * Added run.sh and drivers_test.sh to the sparc64 selftest directory. Used bpf/test_kmod.sh and the android selftests as examples * Minor cleanups in the selftest adi-test.c * Added calls to ksft_test_*() in the adi-test.c ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/char/Kconfig12
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/adi.c239
-rw-r--r--tools/testing/selftests/Makefile1
-rw-r--r--tools/testing/selftests/sparc64/Makefile46
-rw-r--r--tools/testing/selftests/sparc64/drivers/.gitignore1
-rw-r--r--tools/testing/selftests/sparc64/drivers/Makefile15
-rw-r--r--tools/testing/selftests/sparc64/drivers/adi-test.c721
-rwxr-xr-xtools/testing/selftests/sparc64/drivers/drivers_test.sh30
-rwxr-xr-xtools/testing/selftests/sparc64/run.sh3
10 files changed, 1069 insertions, 0 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index e538061eadcb..410c30c42120 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -540,5 +540,17 @@ source "drivers/s390/char/Kconfig"
540 540
541source "drivers/char/xillybus/Kconfig" 541source "drivers/char/xillybus/Kconfig"
542 542
543config ADI
544 tristate "SPARC Privileged ADI driver"
545 depends on SPARC64
546 default m
547 help
548 SPARC M7 and newer processors utilize ADI (Application Data
549 Integrity) to version and protect memory. This driver provides
550 read/write access to the ADI versions for privileged processes.
551 This feature is also known as MCD (Memory Corruption Detection)
552 and SSM (Silicon Secured Memory). Intended consumers of this
553 driver include crash and makedumpfile.
554
543endmenu 555endmenu
544 556
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index c97c768cd1dd..b8d42b4e979b 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -57,3 +57,4 @@ js-rtc-y = rtc.o
57 57
58obj-$(CONFIG_XILLYBUS) += xillybus/ 58obj-$(CONFIG_XILLYBUS) += xillybus/
59obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o 59obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
60obj-$(CONFIG_ADI) += adi.o
diff --git a/drivers/char/adi.c b/drivers/char/adi.c
new file mode 100644
index 000000000000..751d7cc0da1b
--- /dev/null
+++ b/drivers/char/adi.c
@@ -0,0 +1,239 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Privileged ADI driver for sparc64
4 *
5 * Author: Tom Hromatka <tom.hromatka@oracle.com>
6 */
7#include <linux/kernel.h>
8#include <linux/miscdevice.h>
9#include <linux/module.h>
10#include <linux/proc_fs.h>
11#include <linux/slab.h>
12#include <linux/uaccess.h>
13#include <asm/asi.h>
14
15#define MAX_BUF_SZ PAGE_SIZE
16
17static int adi_open(struct inode *inode, struct file *file)
18{
19 file->f_mode |= FMODE_UNSIGNED_OFFSET;
20 return 0;
21}
22
23static int read_mcd_tag(unsigned long addr)
24{
25 long err;
26 int ver;
27
28 __asm__ __volatile__(
29 "1: ldxa [%[addr]] %[asi], %[ver]\n"
30 " mov 0, %[err]\n"
31 "2:\n"
32 " .section .fixup,#alloc,#execinstr\n"
33 " .align 4\n"
34 "3: sethi %%hi(2b), %%g1\n"
35 " jmpl %%g1 + %%lo(2b), %%g0\n"
36 " mov %[invalid], %[err]\n"
37 " .previous\n"
38 " .section __ex_table, \"a\"\n"
39 " .align 4\n"
40 " .word 1b, 3b\n"
41 " .previous\n"
42 : [ver] "=r" (ver), [err] "=r" (err)
43 : [addr] "r" (addr), [invalid] "i" (EFAULT),
44 [asi] "i" (ASI_MCD_REAL)
45 : "memory", "g1"
46 );
47
48 if (err)
49 return -EFAULT;
50 else
51 return ver;
52}
53
54static ssize_t adi_read(struct file *file, char __user *buf,
55 size_t count, loff_t *offp)
56{
57 size_t ver_buf_sz, bytes_read = 0;
58 int ver_buf_idx = 0;
59 loff_t offset;
60 u8 *ver_buf;
61 ssize_t ret;
62
63 ver_buf_sz = min_t(size_t, count, MAX_BUF_SZ);
64 ver_buf = kmalloc(ver_buf_sz, GFP_KERNEL);
65 if (!ver_buf)
66 return -ENOMEM;
67
68 offset = (*offp) * adi_blksize();
69
70 while (bytes_read < count) {
71 ret = read_mcd_tag(offset);
72 if (ret < 0)
73 goto out;
74
75 ver_buf[ver_buf_idx] = (u8)ret;
76 ver_buf_idx++;
77 offset += adi_blksize();
78
79 if (ver_buf_idx >= ver_buf_sz) {
80 if (copy_to_user(buf + bytes_read, ver_buf,
81 ver_buf_sz)) {
82 ret = -EFAULT;
83 goto out;
84 }
85
86 bytes_read += ver_buf_sz;
87 ver_buf_idx = 0;
88
89 ver_buf_sz = min(count - bytes_read,
90 (size_t)MAX_BUF_SZ);
91 }
92 }
93
94 (*offp) += bytes_read;
95 ret = bytes_read;
96out:
97 kfree(ver_buf);
98 return ret;
99}
100
101static int set_mcd_tag(unsigned long addr, u8 ver)
102{
103 long err;
104
105 __asm__ __volatile__(
106 "1: stxa %[ver], [%[addr]] %[asi]\n"
107 " mov 0, %[err]\n"
108 "2:\n"
109 " .section .fixup,#alloc,#execinstr\n"
110 " .align 4\n"
111 "3: sethi %%hi(2b), %%g1\n"
112 " jmpl %%g1 + %%lo(2b), %%g0\n"
113 " mov %[invalid], %[err]\n"
114 " .previous\n"
115 " .section __ex_table, \"a\"\n"
116 " .align 4\n"
117 " .word 1b, 3b\n"
118 " .previous\n"
119 : [err] "=r" (err)
120 : [ver] "r" (ver), [addr] "r" (addr),
121 [invalid] "i" (EFAULT), [asi] "i" (ASI_MCD_REAL)
122 : "memory", "g1"
123 );
124
125 if (err)
126 return -EFAULT;
127 else
128 return ver;
129}
130
131static ssize_t adi_write(struct file *file, const char __user *buf,
132 size_t count, loff_t *offp)
133{
134 size_t ver_buf_sz, bytes_written = 0;
135 loff_t offset;
136 u8 *ver_buf;
137 ssize_t ret;
138 int i;
139
140 if (count <= 0)
141 return -EINVAL;
142
143 ver_buf_sz = min_t(size_t, count, MAX_BUF_SZ);
144 ver_buf = kmalloc(ver_buf_sz, GFP_KERNEL);
145 if (!ver_buf)
146 return -ENOMEM;
147
148 offset = (*offp) * adi_blksize();
149
150 do {
151 if (copy_from_user(ver_buf, &buf[bytes_written],
152 ver_buf_sz)) {
153 ret = -EFAULT;
154 goto out;
155 }
156
157 for (i = 0; i < ver_buf_sz; i++) {
158 ret = set_mcd_tag(offset, ver_buf[i]);
159 if (ret < 0)
160 goto out;
161
162 offset += adi_blksize();
163 }
164
165 bytes_written += ver_buf_sz;
166 ver_buf_sz = min(count - bytes_written, (size_t)MAX_BUF_SZ);
167 } while (bytes_written < count);
168
169 (*offp) += bytes_written;
170 ret = bytes_written;
171out:
172 __asm__ __volatile__("membar #Sync");
173 kfree(ver_buf);
174 return ret;
175}
176
177static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
178{
179 loff_t ret = -EINVAL;
180
181 switch (whence) {
182 case SEEK_END:
183 case SEEK_DATA:
184 case SEEK_HOLE:
185 /* unsupported */
186 return -EINVAL;
187 case SEEK_CUR:
188 if (offset == 0)
189 return file->f_pos;
190
191 offset += file->f_pos;
192 break;
193 case SEEK_SET:
194 break;
195 }
196
197 if (offset != file->f_pos) {
198 file->f_pos = offset;
199 file->f_version = 0;
200 ret = offset;
201 }
202
203 return ret;
204}
205
206static const struct file_operations adi_fops = {
207 .owner = THIS_MODULE,
208 .llseek = adi_llseek,
209 .open = adi_open,
210 .read = adi_read,
211 .write = adi_write,
212};
213
214static struct miscdevice adi_miscdev = {
215 .minor = MISC_DYNAMIC_MINOR,
216 .name = KBUILD_MODNAME,
217 .fops = &adi_fops,
218};
219
220static int __init adi_init(void)
221{
222 if (!adi_capable())
223 return -EPERM;
224
225 return misc_register(&adi_miscdev);
226}
227
228static void __exit adi_exit(void)
229{
230 misc_deregister(&adi_miscdev);
231}
232
233module_init(adi_init);
234module_exit(adi_exit);
235
236MODULE_AUTHOR("Tom Hromatka <tom.hromatka@oracle.com>");
237MODULE_DESCRIPTION("Privileged interface to ADI");
238MODULE_VERSION("1.0");
239MODULE_LICENSE("GPL v2");
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 32aafa92074c..3e3984344ce5 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -31,6 +31,7 @@ TARGETS += ptrace
31TARGETS += seccomp 31TARGETS += seccomp
32TARGETS += sigaltstack 32TARGETS += sigaltstack
33TARGETS += size 33TARGETS += size
34TARGETS += sparc64
34TARGETS += splice 35TARGETS += splice
35TARGETS += static_keys 36TARGETS += static_keys
36TARGETS += sync 37TARGETS += sync
diff --git a/tools/testing/selftests/sparc64/Makefile b/tools/testing/selftests/sparc64/Makefile
new file mode 100644
index 000000000000..2082eeffd779
--- /dev/null
+++ b/tools/testing/selftests/sparc64/Makefile
@@ -0,0 +1,46 @@
1SUBDIRS := drivers
2
3TEST_PROGS := run.sh
4
5.PHONY: all clean
6
7include ../lib.mk
8
9all:
10 @for DIR in $(SUBDIRS); do \
11 BUILD_TARGET=$(OUTPUT)/$$DIR; \
12 mkdir $$BUILD_TARGET -p; \
13 make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\
14 #SUBDIR test prog name should be in the form: SUBDIR_test.sh \
15 TEST=$$DIR"_test.sh"; \
16 if [ -e $$DIR/$$TEST ]; then \
17 rsync -a $$DIR/$$TEST $$BUILD_TARGET/; \
18 fi \
19 done
20
21override define RUN_TESTS
22 @cd $(OUTPUT); ./run.sh
23endef
24
25override define INSTALL_RULE
26 mkdir -p $(INSTALL_PATH)
27 install -t $(INSTALL_PATH) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES)
28
29 @for SUBDIR in $(SUBDIRS); do \
30 BUILD_TARGET=$(OUTPUT)/$$SUBDIR; \
31 mkdir $$BUILD_TARGET -p; \
32 $(MAKE) OUTPUT=$$BUILD_TARGET -C $$SUBDIR INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR install; \
33 done;
34endef
35
36override define EMIT_TESTS
37 echo "./run.sh"
38endef
39
40override define CLEAN
41 @for DIR in $(SUBDIRS); do \
42 BUILD_TARGET=$(OUTPUT)/$$DIR; \
43 mkdir $$BUILD_TARGET -p; \
44 make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\
45 done
46endef
diff --git a/tools/testing/selftests/sparc64/drivers/.gitignore b/tools/testing/selftests/sparc64/drivers/.gitignore
new file mode 100644
index 000000000000..90e835ed74e6
--- /dev/null
+++ b/tools/testing/selftests/sparc64/drivers/.gitignore
@@ -0,0 +1 @@
adi-test
diff --git a/tools/testing/selftests/sparc64/drivers/Makefile b/tools/testing/selftests/sparc64/drivers/Makefile
new file mode 100644
index 000000000000..6264f40bbdbc
--- /dev/null
+++ b/tools/testing/selftests/sparc64/drivers/Makefile
@@ -0,0 +1,15 @@
1
2INCLUDEDIR := -I.
3CFLAGS := $(CFLAGS) $(INCLUDEDIR) -Wall -O2 -g
4
5TEST_GEN_FILES := adi-test
6
7all: $(TEST_GEN_FILES)
8
9$(TEST_GEN_FILES): adi-test.c
10
11TEST_PROGS := drivers_test.sh
12
13include ../../lib.mk
14
15$(OUTPUT)/adi-test: adi-test.c
diff --git a/tools/testing/selftests/sparc64/drivers/adi-test.c b/tools/testing/selftests/sparc64/drivers/adi-test.c
new file mode 100644
index 000000000000..95d93c6a88a5
--- /dev/null
+++ b/tools/testing/selftests/sparc64/drivers/adi-test.c
@@ -0,0 +1,721 @@
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#ifndef ARRAY_SIZE
28# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
29#endif
30
31/* bit mask of enabled bits to print */
32#define DEBUG 0x0001
33
34#define DEBUG_PRINT_L1(...) debug_print(DEBUG_LEVEL_1_BIT, __VA_ARGS__)
35#define DEBUG_PRINT_L2(...) debug_print(DEBUG_LEVEL_2_BIT, __VA_ARGS__)
36#define DEBUG_PRINT_L3(...) debug_print(DEBUG_LEVEL_3_BIT, __VA_ARGS__)
37#define DEBUG_PRINT_L4(...) debug_print(DEBUG_LEVEL_4_BIT, __VA_ARGS__)
38#define DEBUG_PRINT_T(...) debug_print(DEBUG_TIMING_BIT, __VA_ARGS__)
39
40static void debug_print(int level, const char *s, ...)
41{
42 va_list args;
43
44 va_start(args, s);
45
46 if (DEBUG & level)
47 vfprintf(stdout, s, args);
48 va_end(args);
49}
50
51#ifndef min
52#define min(x, y) ((x) < (y) ? x : y)
53#endif
54
55#define RETURN_FROM_TEST(_ret) \
56 do { \
57 DEBUG_PRINT_L1( \
58 "\tTest %s returned %d\n", __func__, _ret); \
59 return _ret; \
60 } while (0)
61
62#define ADI_BLKSZ 64
63#define ADI_MAX_VERSION 15
64
65#define TEST_STEP_FAILURE(_ret) \
66 do { \
67 fprintf(stderr, "\tTest step failure: %d at %s:%d\n", \
68 _ret, __func__, __LINE__); \
69 goto out; \
70 } while (0)
71
72#define RDTICK(_x) \
73 asm volatile(" rd %%tick, %0\n" : "=r" (_x))
74
75static int random_version(void)
76{
77 long tick;
78
79 RDTICK(tick);
80
81 return tick % (ADI_MAX_VERSION + 1);
82}
83
84#define MAX_RANGES_SUPPORTED 5
85static const char system_ram_str[] = "System RAM\n";
86static int range_count;
87static unsigned long long int start_addr[MAX_RANGES_SUPPORTED];
88static unsigned long long int end_addr[MAX_RANGES_SUPPORTED];
89
90struct stats {
91 char name[16];
92 unsigned long total;
93 unsigned long count;
94 unsigned long bytes;
95};
96
97static struct stats read_stats = {
98 .name = "read", .total = 0, .count = 0, .bytes = 0};
99static struct stats pread_stats = {
100 .name = "pread", .total = 0, .count = 0, .bytes = 0};
101static struct stats write_stats = {
102 .name = "write", .total = 0, .count = 0, .bytes = 0};
103static struct stats pwrite_stats = {
104 .name = "pwrite", .total = 0, .count = 0, .bytes = 0};
105static struct stats seek_stats = {
106 .name = "seek", .total = 0, .count = 0, .bytes = 0};
107
108static void update_stats(struct stats * const ustats,
109 unsigned long measurement, unsigned long bytes)
110{
111 ustats->total += measurement;
112 ustats->bytes += bytes;
113 ustats->count++;
114}
115
116static void print_ustats(const struct stats * const ustats)
117{
118 DEBUG_PRINT_L1("%s\t%7d\t%7.0f\t%7.0f\n",
119 ustats->name, ustats->count,
120 (float)ustats->total / (float)ustats->count,
121 (float)ustats->bytes / (float)ustats->count);
122}
123
124static void print_stats(void)
125{
126 DEBUG_PRINT_L1("\nSyscall\tCall\tAvgTime\tAvgSize\n"
127 "\tCount\t(ticks)\t(bytes)\n"
128 "-------------------------------\n");
129
130 print_ustats(&read_stats);
131 print_ustats(&pread_stats);
132 print_ustats(&write_stats);
133 print_ustats(&pwrite_stats);
134 print_ustats(&seek_stats);
135}
136
137static int build_memory_map(void)
138{
139 char line[256];
140 FILE *fp;
141 int i;
142
143 range_count = 0;
144
145 fp = fopen("/proc/iomem", "r");
146 if (!fp) {
147 fprintf(stderr, "/proc/iomem: error %d: %s\n",
148 errno, strerror(errno));
149 return -errno;
150 }
151
152 while (fgets(line, sizeof(line), fp) != 0) {
153 if (strstr(line, system_ram_str)) {
154 char *dash, *end_ptr;
155
156 /* Given a line like this:
157 * d0400000-10ffaffff : System RAM
158 * replace the "-" with a space
159 */
160 dash = strstr(line, "-");
161 dash[0] = 0x20;
162
163 start_addr[range_count] = strtoull(line, &end_ptr, 16);
164 end_addr[range_count] = strtoull(end_ptr, NULL, 16);
165 range_count++;
166 }
167 }
168
169 fclose(fp);
170
171 DEBUG_PRINT_L1("RAM Ranges\n");
172 for (i = 0; i < range_count; i++)
173 DEBUG_PRINT_L1("\trange %d: 0x%llx\t- 0x%llx\n",
174 i, start_addr[i], end_addr[i]);
175
176 if (range_count == 0) {
177 fprintf(stderr, "No valid address ranges found. Error.\n");
178 return -1;
179 }
180
181 return 0;
182}
183
184static int read_adi(int fd, unsigned char *buf, int buf_sz)
185{
186 int ret, bytes_read = 0;
187 long start, end, elapsed_time = 0;
188
189 do {
190 RDTICK(start);
191 ret = read(fd, buf + bytes_read, buf_sz - bytes_read);
192 RDTICK(end);
193 if (ret < 0)
194 return -errno;
195
196 elapsed_time += end - start;
197 update_stats(&read_stats, elapsed_time, buf_sz);
198 bytes_read += ret;
199
200 } while (bytes_read < buf_sz);
201
202 DEBUG_PRINT_T("\tread elapsed timed = %ld\n", elapsed_time);
203 DEBUG_PRINT_L3("\tRead %d bytes\n", bytes_read);
204
205 return bytes_read;
206}
207
208static int pread_adi(int fd, unsigned char *buf,
209 int buf_sz, unsigned long offset)
210{
211 int ret, i, bytes_read = 0;
212 unsigned long cur_offset;
213 long start, end, elapsed_time = 0;
214
215 cur_offset = offset;
216 do {
217 RDTICK(start);
218 ret = pread(fd, buf + bytes_read, buf_sz - bytes_read,
219 cur_offset);
220 RDTICK(end);
221 if (ret < 0)
222 return -errno;
223
224 elapsed_time += end - start;
225 update_stats(&pread_stats, elapsed_time, buf_sz);
226 bytes_read += ret;
227 cur_offset += ret;
228
229 } while (bytes_read < buf_sz);
230
231 DEBUG_PRINT_T("\tpread elapsed timed = %ld\n", elapsed_time);
232 DEBUG_PRINT_L3("\tRead %d bytes starting at offset 0x%lx\n",
233 bytes_read, offset);
234 for (i = 0; i < bytes_read; i++)
235 DEBUG_PRINT_L4("\t\t0x%lx\t%d\n", offset + i, buf[i]);
236
237 return bytes_read;
238}
239
240static int write_adi(int fd, const unsigned char * const buf, int buf_sz)
241{
242 int ret, bytes_written = 0;
243 long start, end, elapsed_time = 0;
244
245 do {
246 RDTICK(start);
247 ret = write(fd, buf + bytes_written, buf_sz - bytes_written);
248 RDTICK(end);
249 if (ret < 0)
250 return -errno;
251
252 elapsed_time += (end - start);
253 update_stats(&write_stats, elapsed_time, buf_sz);
254 bytes_written += ret;
255 } while (bytes_written < buf_sz);
256
257 DEBUG_PRINT_T("\twrite elapsed timed = %ld\n", elapsed_time);
258 DEBUG_PRINT_L3("\tWrote %d of %d bytes\n", bytes_written, buf_sz);
259
260 return bytes_written;
261}
262
263static int pwrite_adi(int fd, const unsigned char * const buf,
264 int buf_sz, unsigned long offset)
265{
266 int ret, bytes_written = 0;
267 unsigned long cur_offset;
268 long start, end, elapsed_time = 0;
269
270 cur_offset = offset;
271
272 do {
273 RDTICK(start);
274 ret = pwrite(fd, buf + bytes_written,
275 buf_sz - bytes_written, cur_offset);
276 RDTICK(end);
277 if (ret < 0) {
278 fprintf(stderr, "pwrite(): error %d: %s\n",
279 errno, strerror(errno));
280 return -errno;
281 }
282
283 elapsed_time += (end - start);
284 update_stats(&pwrite_stats, elapsed_time, buf_sz);
285 bytes_written += ret;
286 cur_offset += ret;
287
288 } while (bytes_written < buf_sz);
289
290 DEBUG_PRINT_T("\tpwrite elapsed timed = %ld\n", elapsed_time);
291 DEBUG_PRINT_L3("\tWrote %d of %d bytes starting at address 0x%lx\n",
292 bytes_written, buf_sz, offset);
293
294 return bytes_written;
295}
296
297static off_t seek_adi(int fd, off_t offset, int whence)
298{
299 long start, end;
300 off_t ret;
301
302 RDTICK(start);
303 ret = lseek(fd, offset, whence);
304 RDTICK(end);
305 DEBUG_PRINT_L2("\tlseek ret = 0x%llx\n", ret);
306 if (ret < 0)
307 goto out;
308
309 DEBUG_PRINT_T("\tlseek elapsed timed = %ld\n", end - start);
310 update_stats(&seek_stats, end - start, 0);
311
312out:
313 (void)lseek(fd, 0, SEEK_END);
314 return ret;
315}
316
317static int test0_prpw_aligned_1byte(int fd)
318{
319 /* somewhat arbitrarily chosen address */
320 unsigned long paddr =
321 (end_addr[range_count - 1] - 0x1000) & ~(ADI_BLKSZ - 1);
322 unsigned char version[1], expected_version;
323 loff_t offset;
324 int ret;
325
326 version[0] = random_version();
327 expected_version = version[0];
328
329 offset = paddr / ADI_BLKSZ;
330
331 ret = pwrite_adi(fd, version, sizeof(version), offset);
332 if (ret != sizeof(version))
333 TEST_STEP_FAILURE(ret);
334
335 ret = pread_adi(fd, version, sizeof(version), offset);
336 if (ret != sizeof(version))
337 TEST_STEP_FAILURE(ret);
338
339 if (expected_version != version[0]) {
340 DEBUG_PRINT_L2("\tExpected version %d but read version %d\n",
341 expected_version, version[0]);
342 TEST_STEP_FAILURE(-expected_version);
343 }
344
345 ret = 0;
346out:
347 RETURN_FROM_TEST(ret);
348}
349
350#define TEST1_VERSION_SZ 4096
351static int test1_prpw_aligned_4096bytes(int fd)
352{
353 /* somewhat arbitrarily chosen address */
354 unsigned long paddr =
355 (end_addr[range_count - 1] - 0x6000) & ~(ADI_BLKSZ - 1);
356 unsigned char version[TEST1_VERSION_SZ],
357 expected_version[TEST1_VERSION_SZ];
358 loff_t offset;
359 int ret, i;
360
361 for (i = 0; i < TEST1_VERSION_SZ; i++) {
362 version[i] = random_version();
363 expected_version[i] = version[i];
364 }
365
366 offset = paddr / ADI_BLKSZ;
367
368 ret = pwrite_adi(fd, version, sizeof(version), offset);
369 if (ret != sizeof(version))
370 TEST_STEP_FAILURE(ret);
371
372 ret = pread_adi(fd, version, sizeof(version), offset);
373 if (ret != sizeof(version))
374 TEST_STEP_FAILURE(ret);
375
376 for (i = 0; i < TEST1_VERSION_SZ; i++) {
377 if (expected_version[i] != version[i]) {
378 DEBUG_PRINT_L2(
379 "\tExpected version %d but read version %d\n",
380 expected_version, version[0]);
381 TEST_STEP_FAILURE(-expected_version[i]);
382 }
383 }
384
385 ret = 0;
386out:
387 RETURN_FROM_TEST(ret);
388}
389
390#define TEST2_VERSION_SZ 10327
391static int test2_prpw_aligned_10327bytes(int fd)
392{
393 /* somewhat arbitrarily chosen address */
394 unsigned long paddr =
395 (start_addr[0] + 0x6000) & ~(ADI_BLKSZ - 1);
396 unsigned char version[TEST2_VERSION_SZ],
397 expected_version[TEST2_VERSION_SZ];
398 loff_t offset;
399 int ret, i;
400
401 for (i = 0; i < TEST2_VERSION_SZ; i++) {
402 version[i] = random_version();
403 expected_version[i] = version[i];
404 }
405
406 offset = paddr / ADI_BLKSZ;
407
408 ret = pwrite_adi(fd, version, sizeof(version), offset);
409 if (ret != sizeof(version))
410 TEST_STEP_FAILURE(ret);
411
412 ret = pread_adi(fd, version, sizeof(version), offset);
413 if (ret != sizeof(version))
414 TEST_STEP_FAILURE(ret);
415
416 for (i = 0; i < TEST2_VERSION_SZ; i++) {
417 if (expected_version[i] != version[i]) {
418 DEBUG_PRINT_L2(
419 "\tExpected version %d but read version %d\n",
420 expected_version, version[0]);
421 TEST_STEP_FAILURE(-expected_version[i]);
422 }
423 }
424
425 ret = 0;
426out:
427 RETURN_FROM_TEST(ret);
428}
429
430#define TEST3_VERSION_SZ 12541
431static int test3_prpw_unaligned_12541bytes(int fd)
432{
433 /* somewhat arbitrarily chosen address */
434 unsigned long paddr =
435 ((start_addr[0] + 0xC000) & ~(ADI_BLKSZ - 1)) + 17;
436 unsigned char version[TEST3_VERSION_SZ],
437 expected_version[TEST3_VERSION_SZ];
438 loff_t offset;
439 int ret, i;
440
441 for (i = 0; i < TEST3_VERSION_SZ; i++) {
442 version[i] = random_version();
443 expected_version[i] = version[i];
444 }
445
446 offset = paddr / ADI_BLKSZ;
447
448 ret = pwrite_adi(fd, version, sizeof(version), offset);
449 if (ret != sizeof(version))
450 TEST_STEP_FAILURE(ret);
451
452 ret = pread_adi(fd, version, sizeof(version), offset);
453 if (ret != sizeof(version))
454 TEST_STEP_FAILURE(ret);
455
456 for (i = 0; i < TEST3_VERSION_SZ; i++) {
457 if (expected_version[i] != version[i]) {
458 DEBUG_PRINT_L2(
459 "\tExpected version %d but read version %d\n",
460 expected_version, version[0]);
461 TEST_STEP_FAILURE(-expected_version[i]);
462 }
463 }
464
465 ret = 0;
466out:
467 RETURN_FROM_TEST(ret);
468}
469
470static int test4_lseek(int fd)
471{
472#define OFFSET_ADD (0x100)
473#define OFFSET_SUBTRACT (0xFFFFFFF000000000)
474
475 off_t offset_out, offset_in;
476 int ret;
477
478
479 offset_in = 0x123456789abcdef0;
480 offset_out = seek_adi(fd, offset_in, SEEK_SET);
481 if (offset_out != offset_in) {
482 ret = -1;
483 TEST_STEP_FAILURE(ret);
484 }
485
486 /* seek to the current offset. this should return EINVAL */
487 offset_out = seek_adi(fd, offset_in, SEEK_SET);
488 if (offset_out < 0 && errno == EINVAL)
489 DEBUG_PRINT_L2(
490 "\tSEEK_SET failed as designed. Not an error\n");
491 else {
492 ret = -2;
493 TEST_STEP_FAILURE(ret);
494 }
495
496 offset_out = seek_adi(fd, 0, SEEK_CUR);
497 if (offset_out != offset_in) {
498 ret = -3;
499 TEST_STEP_FAILURE(ret);
500 }
501
502 offset_out = seek_adi(fd, OFFSET_ADD, SEEK_CUR);
503 if (offset_out != (offset_in + OFFSET_ADD)) {
504 ret = -4;
505 TEST_STEP_FAILURE(ret);
506 }
507
508 offset_out = seek_adi(fd, OFFSET_SUBTRACT, SEEK_CUR);
509 if (offset_out != (offset_in + OFFSET_ADD + OFFSET_SUBTRACT)) {
510 ret = -5;
511 TEST_STEP_FAILURE(ret);
512 }
513
514 ret = 0;
515out:
516 RETURN_FROM_TEST(ret);
517}
518
519static int test5_rw_aligned_1byte(int fd)
520{
521 /* somewhat arbitrarily chosen address */
522 unsigned long paddr =
523 (end_addr[range_count - 1] - 0xF000) & ~(ADI_BLKSZ - 1);
524 unsigned char version, expected_version;
525 loff_t offset;
526 off_t oret;
527 int ret;
528
529 offset = paddr / ADI_BLKSZ;
530 version = expected_version = random_version();
531
532 oret = seek_adi(fd, offset, SEEK_SET);
533 if (oret != offset) {
534 ret = -1;
535 TEST_STEP_FAILURE(ret);
536 }
537
538 ret = write_adi(fd, &version, sizeof(version));
539 if (ret != sizeof(version))
540 TEST_STEP_FAILURE(ret);
541
542 oret = seek_adi(fd, offset, SEEK_SET);
543 if (oret != offset) {
544 ret = -1;
545 TEST_STEP_FAILURE(ret);
546 }
547
548 ret = read_adi(fd, &version, sizeof(version));
549 if (ret != sizeof(version))
550 TEST_STEP_FAILURE(ret);
551
552 if (expected_version != version) {
553 DEBUG_PRINT_L2("\tExpected version %d but read version %d\n",
554 expected_version, version);
555 TEST_STEP_FAILURE(-expected_version);
556 }
557
558 ret = 0;
559out:
560 RETURN_FROM_TEST(ret);
561}
562
563#define TEST6_VERSION_SZ 9434
564static int test6_rw_aligned_9434bytes(int fd)
565{
566 /* somewhat arbitrarily chosen address */
567 unsigned long paddr =
568 (end_addr[range_count - 1] - 0x5F000) & ~(ADI_BLKSZ - 1);
569 unsigned char version[TEST6_VERSION_SZ],
570 expected_version[TEST6_VERSION_SZ];
571 loff_t offset;
572 off_t oret;
573 int ret, i;
574
575 offset = paddr / ADI_BLKSZ;
576 for (i = 0; i < TEST6_VERSION_SZ; i++)
577 version[i] = expected_version[i] = random_version();
578
579 oret = seek_adi(fd, offset, SEEK_SET);
580 if (oret != offset) {
581 ret = -1;
582 TEST_STEP_FAILURE(ret);
583 }
584
585 ret = write_adi(fd, version, sizeof(version));
586 if (ret != sizeof(version))
587 TEST_STEP_FAILURE(ret);
588
589 memset(version, 0, TEST6_VERSION_SZ);
590
591 oret = seek_adi(fd, offset, SEEK_SET);
592 if (oret != offset) {
593 ret = -1;
594 TEST_STEP_FAILURE(ret);
595 }
596
597 ret = read_adi(fd, version, sizeof(version));
598 if (ret != sizeof(version))
599 TEST_STEP_FAILURE(ret);
600
601 for (i = 0; i < TEST6_VERSION_SZ; i++) {
602 if (expected_version[i] != version[i]) {
603 DEBUG_PRINT_L2(
604 "\tExpected version %d but read version %d\n",
605 expected_version[i], version[i]);
606 TEST_STEP_FAILURE(-expected_version[i]);
607 }
608 }
609
610 ret = 0;
611out:
612 RETURN_FROM_TEST(ret);
613}
614
615#define TEST7_VERSION_SZ 14963
616static int test7_rw_aligned_14963bytes(int fd)
617{
618 /* somewhat arbitrarily chosen address */
619 unsigned long paddr =
620 ((start_addr[range_count - 1] + 0xF000) & ~(ADI_BLKSZ - 1)) + 39;
621 unsigned char version[TEST7_VERSION_SZ],
622 expected_version[TEST7_VERSION_SZ];
623 loff_t offset;
624 off_t oret;
625 int ret, i;
626
627 offset = paddr / ADI_BLKSZ;
628 for (i = 0; i < TEST7_VERSION_SZ; i++) {
629 version[i] = random_version();
630 expected_version[i] = version[i];
631 }
632
633 oret = seek_adi(fd, offset, SEEK_SET);
634 if (oret != offset) {
635 ret = -1;
636 TEST_STEP_FAILURE(ret);
637 }
638
639 ret = write_adi(fd, version, sizeof(version));
640 if (ret != sizeof(version))
641 TEST_STEP_FAILURE(ret);
642
643 memset(version, 0, TEST7_VERSION_SZ);
644
645 oret = seek_adi(fd, offset, SEEK_SET);
646 if (oret != offset) {
647 ret = -1;
648 TEST_STEP_FAILURE(ret);
649 }
650
651 ret = read_adi(fd, version, sizeof(version));
652 if (ret != sizeof(version))
653 TEST_STEP_FAILURE(ret);
654
655 for (i = 0; i < TEST7_VERSION_SZ; i++) {
656 if (expected_version[i] != version[i]) {
657 DEBUG_PRINT_L2(
658 "\tExpected version %d but read version %d\n",
659 expected_version[i], version[i]);
660 TEST_STEP_FAILURE(-expected_version[i]);
661 }
662
663 paddr += ADI_BLKSZ;
664 }
665
666 ret = 0;
667out:
668 RETURN_FROM_TEST(ret);
669}
670
671static int (*tests[])(int fd) = {
672 test0_prpw_aligned_1byte,
673 test1_prpw_aligned_4096bytes,
674 test2_prpw_aligned_10327bytes,
675 test3_prpw_unaligned_12541bytes,
676 test4_lseek,
677 test5_rw_aligned_1byte,
678 test6_rw_aligned_9434bytes,
679 test7_rw_aligned_14963bytes,
680};
681#define TEST_COUNT ARRAY_SIZE(tests)
682
683int main(int argc, char *argv[])
684{
685 int fd, ret, test;
686
687 ret = build_memory_map();
688 if (ret < 0)
689 return ret;
690
691 fd = open("/dev/adi", O_RDWR);
692 if (fd < 0) {
693 fprintf(stderr, "open: error %d: %s\n",
694 errno, strerror(errno));
695 return -errno;
696 }
697
698 for (test = 0; test < TEST_COUNT; test++) {
699 DEBUG_PRINT_L1("Running test #%d\n", test);
700
701 ret = (*tests[test])(fd);
702 if (ret != 0)
703 ksft_test_result_fail("Test #%d failed: error %d\n",
704 test, ret);
705 else
706 ksft_test_result_pass("Test #%d passed\n", test);
707 }
708
709 print_stats();
710 close(fd);
711
712 if (ksft_get_fail_cnt() > 0)
713 ksft_exit_fail();
714 else
715 ksft_exit_pass();
716
717 /* it's impossible to get here, but the compiler throws a warning
718 * about control reaching the end of non-void function. bah.
719 */
720 return 0;
721}
diff --git a/tools/testing/selftests/sparc64/drivers/drivers_test.sh b/tools/testing/selftests/sparc64/drivers/drivers_test.sh
new file mode 100755
index 000000000000..6d08273b7532
--- /dev/null
+++ b/tools/testing/selftests/sparc64/drivers/drivers_test.sh
@@ -0,0 +1,30 @@
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0
3
4SRC_TREE=../../../../
5
6test_run()
7{
8 if [ -f ${SRC_TREE}/drivers/char/adi.ko ]; then
9 insmod ${SRC_TREE}/drivers/char/adi.ko 2> /dev/null
10 if [ $? -ne 0 ]; then
11 rc=1
12 fi
13 else
14 # Use modprobe dry run to check for missing adi module
15 if ! /sbin/modprobe -q -n adi; then
16 echo "adi: [SKIP]"
17 elif /sbin/modprobe -q adi; then
18 echo "adi: ok"
19 else
20 echo "adi: [FAIL]"
21 rc=1
22 fi
23 fi
24 ./adi-test
25 rmmod adi 2> /dev/null
26}
27
28rc=0
29test_run
30exit $rc
diff --git a/tools/testing/selftests/sparc64/run.sh b/tools/testing/selftests/sparc64/run.sh
new file mode 100755
index 000000000000..38ad61f9328e
--- /dev/null
+++ b/tools/testing/selftests/sparc64/run.sh
@@ -0,0 +1,3 @@
1#!/bin/sh
2
3(cd drivers; ./drivers_test.sh)