1 /*
2  * Test the powerpc alignment handler on POWER8/POWER9
3  *
4  * Copyright (C) 2017 IBM Corporation (Michael Neuling, Andrew Donnellan)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11 
12 /*
13  * This selftest exercises the powerpc alignment fault handler.
14  *
15  * We create two sets of source and destination buffers, one in regular memory,
16  * the other cache-inhibited (we use /dev/fb0 for this).
17  *
18  * We initialise the source buffers, then use whichever set of load/store
19  * instructions is under test to copy bytes from the source buffers to the
20  * destination buffers. For the regular buffers, these instructions will
21  * execute normally. For the cache-inhibited buffers, these instructions
22  * will trap and cause an alignment fault, and the alignment fault handler
23  * will emulate the particular instruction under test. We then compare the
24  * destination buffers to ensure that the native and emulated cases give the
25  * same result.
26  *
27  * TODO:
28  *   - Any FIXMEs below
29  *   - Test VSX regs < 32 and > 32
30  *   - Test all loads and stores
31  *   - Check update forms do update register
32  *   - Test alignment faults over page boundary
33  *
34  * Some old binutils may not support all the instructions.
35  */
36 
37 
38 #include <sys/mman.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <unistd.h>
43 #include <stdbool.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <assert.h>
48 #include <getopt.h>
49 #include <setjmp.h>
50 #include <signal.h>
51 
52 #include <asm/cputable.h>
53 
54 #include "utils.h"
55 
56 int bufsize;
57 int debug;
58 int testing;
59 volatile int gotsig;
60 
61 void sighandler(int sig, siginfo_t *info, void *ctx)
62 {
63 	ucontext_t *ucp = ctx;
64 
65 	if (!testing) {
66 		signal(sig, SIG_DFL);
67 		kill(0, sig);
68 	}
69 	gotsig = sig;
70 #ifdef __powerpc64__
71 	ucp->uc_mcontext.gp_regs[PT_NIP] += 4;
72 #else
73 	ucp->uc_mcontext.uc_regs->gregs[PT_NIP] += 4;
74 #endif
75 }
76 
77 #define XFORM(reg, n)  " " #reg " ,%"#n",%2 ;"
78 #define DFORM(reg, n)  " " #reg " ,0(%"#n") ;"
79 
80 #define TEST(name, ld_op, st_op, form, ld_reg, st_reg)		\
81 	void test_##name(char *s, char *d)			\
82 	{							\
83 		asm volatile(					\
84 			#ld_op form(ld_reg, 0)			\
85 			#st_op form(st_reg, 1)			\
86 			:: "r"(s), "r"(d), "r"(0)		\
87 			: "memory", "vs0", "vs32", "r31");	\
88 	}							\
89 	rc |= do_test(#name, test_##name)
90 
91 #define LOAD_VSX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 32, 32)
92 #define STORE_VSX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 32)
93 #define LOAD_VSX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 32, 32)
94 #define STORE_VSX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 32)
95 #define LOAD_VMX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 0, 32)
96 #define STORE_VMX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 0)
97 #define LOAD_VMX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 0, 32)
98 #define STORE_VMX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 0)
99 
100 #define LOAD_XFORM_TEST(op) TEST(op, op, stdx, XFORM, 31, 31)
101 #define STORE_XFORM_TEST(op) TEST(op, ldx, op, XFORM, 31, 31)
102 #define LOAD_DFORM_TEST(op) TEST(op, op, std, DFORM, 31, 31)
103 #define STORE_DFORM_TEST(op) TEST(op, ld, op, DFORM, 31, 31)
104 
105 #define LOAD_FLOAT_DFORM_TEST(op)  TEST(op, op, stfd, DFORM, 0, 0)
106 #define STORE_FLOAT_DFORM_TEST(op) TEST(op, lfd, op, DFORM, 0, 0)
107 #define LOAD_FLOAT_XFORM_TEST(op)  TEST(op, op, stfdx, XFORM, 0, 0)
108 #define STORE_FLOAT_XFORM_TEST(op) TEST(op, lfdx, op, XFORM, 0, 0)
109 
110 
111 /* FIXME: Unimplemented tests: */
112 // STORE_DFORM_TEST(stq)   /* FIXME: need two registers for quad */
113 // STORE_DFORM_TEST(stswi) /* FIXME: string instruction */
114 
115 // STORE_XFORM_TEST(stwat) /* AMO can't emulate or run on CI */
116 // STORE_XFORM_TEST(stdat) /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
117 
118 
119 /* preload byte by byte */
120 void preload_data(void *dst, int offset, int width)
121 {
122 	char *c = dst;
123 	int i;
124 
125 	c += offset;
126 
127 	for (i = 0 ; i < width ; i++)
128 		c[i] = i;
129 }
130 
131 int test_memcpy(void *dst, void *src, int size, int offset,
132 		void (*test_func)(char *, char *))
133 {
134 	char *s, *d;
135 
136 	s = src;
137 	s += offset;
138 	d = dst;
139 	d += offset;
140 
141 	assert(size == 16);
142 	gotsig = 0;
143 	testing = 1;
144 
145 	test_func(s, d); /* run the actual test */
146 
147 	testing = 0;
148 	if (gotsig) {
149 		if (debug)
150 			printf("  Got signal %i\n", gotsig);
151 		return 1;
152 	}
153 	return 0;
154 }
155 
156 void dumpdata(char *s1, char *s2, int n, char *test_name)
157 {
158 	int i;
159 
160 	printf("  %s: unexpected result:\n", test_name);
161 	printf("    mem:");
162 	for (i = 0; i < n; i++)
163 		printf(" %02x", s1[i]);
164 	printf("\n");
165 	printf("    ci: ");
166 	for (i = 0; i < n; i++)
167 		printf(" %02x", s2[i]);
168 	printf("\n");
169 }
170 
171 int test_memcmp(void *s1, void *s2, int n, int offset, char *test_name)
172 {
173 	char *s1c, *s2c;
174 
175 	s1c = s1;
176 	s1c += offset;
177 	s2c = s2;
178 	s2c += offset;
179 
180 	if (memcmp(s1c, s2c, n)) {
181 		if (debug) {
182 			printf("\n  Compare failed. Offset:%i length:%i\n",
183 			       offset, n);
184 			dumpdata(s1c, s2c, n, test_name);
185 		}
186 		return 1;
187 	}
188 	return 0;
189 }
190 
191 /*
192  * Do two memcpy tests using the same instructions. One cachable
193  * memory and the other doesn't.
194  */
195 int do_test(char *test_name, void (*test_func)(char *, char *))
196 {
197 	int offset, width, fd, rc, r;
198 	void *mem0, *mem1, *ci0, *ci1;
199 
200 	printf("\tDoing %s:\t", test_name);
201 
202 	fd = open("/dev/fb0", O_RDWR);
203 	if (fd < 0) {
204 		printf("\n");
205 		perror("Can't open /dev/fb0 now?");
206 		return 1;
207 	}
208 
209 	ci0 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED,
210 		   fd, 0x0);
211 	ci1 = mmap(NULL, bufsize, PROT_WRITE, MAP_SHARED,
212 		   fd, bufsize);
213 	if ((ci0 == MAP_FAILED) || (ci1 == MAP_FAILED)) {
214 		printf("\n");
215 		perror("mmap failed");
216 		SKIP_IF(1);
217 	}
218 
219 	rc = posix_memalign(&mem0, bufsize, bufsize);
220 	if (rc) {
221 		printf("\n");
222 		return rc;
223 	}
224 
225 	rc = posix_memalign(&mem1, bufsize, bufsize);
226 	if (rc) {
227 		printf("\n");
228 		free(mem0);
229 		return rc;
230 	}
231 
232 	rc = 0;
233 	/* offset = 0 no alignment fault, so skip */
234 	for (offset = 1; offset < 16; offset++) {
235 		width = 16; /* vsx == 16 bytes */
236 		r = 0;
237 
238 		/* load pattern into memory byte by byte */
239 		preload_data(ci0, offset, width);
240 		preload_data(mem0, offset, width); // FIXME: remove??
241 		memcpy(ci0, mem0, bufsize);
242 		memcpy(ci1, mem1, bufsize); /* initialise output to the same */
243 
244 		/* sanity check */
245 		test_memcmp(mem0, ci0, width, offset, test_name);
246 
247 		r |= test_memcpy(ci1,  ci0,  width, offset, test_func);
248 		r |= test_memcpy(mem1, mem0, width, offset, test_func);
249 		if (r && !debug) {
250 			printf("FAILED: Got signal");
251 			rc = 1;
252 			break;
253 		}
254 
255 		r |= test_memcmp(mem1, ci1, width, offset, test_name);
256 		if (r && !debug) {
257 			printf("FAILED: Wrong Data");
258 			rc = 1;
259 			break;
260 		}
261 	}
262 
263 	if (rc == 0)
264 		printf("PASSED");
265 
266 	printf("\n");
267 
268 	munmap(ci0, bufsize);
269 	munmap(ci1, bufsize);
270 	free(mem0);
271 	free(mem1);
272 	close(fd);
273 
274 	return rc;
275 }
276 
277 static bool can_open_fb0(void)
278 {
279 	int fd;
280 
281 	fd = open("/dev/fb0", O_RDWR);
282 	if (fd < 0)
283 		return false;
284 
285 	close(fd);
286 	return true;
287 }
288 
289 int test_alignment_handler_vsx_206(void)
290 {
291 	int rc = 0;
292 
293 	SKIP_IF(!can_open_fb0());
294 	SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
295 
296 	printf("VSX: 2.06B\n");
297 	LOAD_VSX_XFORM_TEST(lxvd2x);
298 	LOAD_VSX_XFORM_TEST(lxvw4x);
299 	LOAD_VSX_XFORM_TEST(lxsdx);
300 	LOAD_VSX_XFORM_TEST(lxvdsx);
301 	STORE_VSX_XFORM_TEST(stxvd2x);
302 	STORE_VSX_XFORM_TEST(stxvw4x);
303 	STORE_VSX_XFORM_TEST(stxsdx);
304 	return rc;
305 }
306 
307 int test_alignment_handler_vsx_207(void)
308 {
309 	int rc = 0;
310 
311 	SKIP_IF(!can_open_fb0());
312 	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
313 
314 	printf("VSX: 2.07B\n");
315 	LOAD_VSX_XFORM_TEST(lxsspx);
316 	LOAD_VSX_XFORM_TEST(lxsiwax);
317 	LOAD_VSX_XFORM_TEST(lxsiwzx);
318 	STORE_VSX_XFORM_TEST(stxsspx);
319 	STORE_VSX_XFORM_TEST(stxsiwx);
320 	return rc;
321 }
322 
323 int test_alignment_handler_vsx_300(void)
324 {
325 	int rc = 0;
326 
327 	SKIP_IF(!can_open_fb0());
328 
329 	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00));
330 	printf("VSX: 3.00B\n");
331 	LOAD_VMX_DFORM_TEST(lxsd);
332 	LOAD_VSX_XFORM_TEST(lxsibzx);
333 	LOAD_VSX_XFORM_TEST(lxsihzx);
334 	LOAD_VMX_DFORM_TEST(lxssp);
335 	LOAD_VSX_DFORM_TEST(lxv);
336 	LOAD_VSX_XFORM_TEST(lxvb16x);
337 	LOAD_VSX_XFORM_TEST(lxvh8x);
338 	LOAD_VSX_XFORM_TEST(lxvx);
339 	LOAD_VSX_XFORM_TEST(lxvwsx);
340 	LOAD_VSX_XFORM_TEST(lxvl);
341 	LOAD_VSX_XFORM_TEST(lxvll);
342 	STORE_VMX_DFORM_TEST(stxsd);
343 	STORE_VSX_XFORM_TEST(stxsibx);
344 	STORE_VSX_XFORM_TEST(stxsihx);
345 	STORE_VMX_DFORM_TEST(stxssp);
346 	STORE_VSX_DFORM_TEST(stxv);
347 	STORE_VSX_XFORM_TEST(stxvb16x);
348 	STORE_VSX_XFORM_TEST(stxvh8x);
349 	STORE_VSX_XFORM_TEST(stxvx);
350 	STORE_VSX_XFORM_TEST(stxvl);
351 	STORE_VSX_XFORM_TEST(stxvll);
352 	return rc;
353 }
354 
355 int test_alignment_handler_integer(void)
356 {
357 	int rc = 0;
358 
359 	SKIP_IF(!can_open_fb0());
360 
361 	printf("Integer\n");
362 	LOAD_DFORM_TEST(lbz);
363 	LOAD_DFORM_TEST(lbzu);
364 	LOAD_XFORM_TEST(lbzx);
365 	LOAD_XFORM_TEST(lbzux);
366 	LOAD_DFORM_TEST(lhz);
367 	LOAD_DFORM_TEST(lhzu);
368 	LOAD_XFORM_TEST(lhzx);
369 	LOAD_XFORM_TEST(lhzux);
370 	LOAD_DFORM_TEST(lha);
371 	LOAD_DFORM_TEST(lhau);
372 	LOAD_XFORM_TEST(lhax);
373 	LOAD_XFORM_TEST(lhaux);
374 	LOAD_XFORM_TEST(lhbrx);
375 	LOAD_DFORM_TEST(lwz);
376 	LOAD_DFORM_TEST(lwzu);
377 	LOAD_XFORM_TEST(lwzx);
378 	LOAD_XFORM_TEST(lwzux);
379 	LOAD_DFORM_TEST(lwa);
380 	LOAD_XFORM_TEST(lwax);
381 	LOAD_XFORM_TEST(lwaux);
382 	LOAD_XFORM_TEST(lwbrx);
383 	LOAD_DFORM_TEST(ld);
384 	LOAD_DFORM_TEST(ldu);
385 	LOAD_XFORM_TEST(ldx);
386 	LOAD_XFORM_TEST(ldux);
387 	LOAD_DFORM_TEST(lmw);
388 	STORE_DFORM_TEST(stb);
389 	STORE_XFORM_TEST(stbx);
390 	STORE_DFORM_TEST(stbu);
391 	STORE_XFORM_TEST(stbux);
392 	STORE_DFORM_TEST(sth);
393 	STORE_XFORM_TEST(sthx);
394 	STORE_DFORM_TEST(sthu);
395 	STORE_XFORM_TEST(sthux);
396 	STORE_XFORM_TEST(sthbrx);
397 	STORE_DFORM_TEST(stw);
398 	STORE_XFORM_TEST(stwx);
399 	STORE_DFORM_TEST(stwu);
400 	STORE_XFORM_TEST(stwux);
401 	STORE_XFORM_TEST(stwbrx);
402 	STORE_DFORM_TEST(std);
403 	STORE_XFORM_TEST(stdx);
404 	STORE_DFORM_TEST(stdu);
405 	STORE_XFORM_TEST(stdux);
406 	STORE_DFORM_TEST(stmw);
407 
408 	return rc;
409 }
410 
411 int test_alignment_handler_integer_206(void)
412 {
413 	int rc = 0;
414 
415 	SKIP_IF(!can_open_fb0());
416 	SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
417 
418 	printf("Integer: 2.06\n");
419 
420 	LOAD_XFORM_TEST(ldbrx);
421 	STORE_XFORM_TEST(stdbrx);
422 
423 	return rc;
424 }
425 
426 int test_alignment_handler_vmx(void)
427 {
428 	int rc = 0;
429 
430 	SKIP_IF(!can_open_fb0());
431 	SKIP_IF(!have_hwcap(PPC_FEATURE_HAS_ALTIVEC));
432 
433 	printf("VMX\n");
434 	LOAD_VMX_XFORM_TEST(lvx);
435 
436 	/*
437 	 * FIXME: These loads only load part of the register, so our
438 	 * testing method doesn't work. Also they don't take alignment
439 	 * faults, so it's kinda pointless anyway
440 	 *
441 	 LOAD_VMX_XFORM_TEST(lvebx)
442 	 LOAD_VMX_XFORM_TEST(lvehx)
443 	 LOAD_VMX_XFORM_TEST(lvewx)
444 	 LOAD_VMX_XFORM_TEST(lvxl)
445 	*/
446 	STORE_VMX_XFORM_TEST(stvx);
447 	STORE_VMX_XFORM_TEST(stvebx);
448 	STORE_VMX_XFORM_TEST(stvehx);
449 	STORE_VMX_XFORM_TEST(stvewx);
450 	STORE_VMX_XFORM_TEST(stvxl);
451 	return rc;
452 }
453 
454 int test_alignment_handler_fp(void)
455 {
456 	int rc = 0;
457 
458 	SKIP_IF(!can_open_fb0());
459 
460 	printf("Floating point\n");
461 	LOAD_FLOAT_DFORM_TEST(lfd);
462 	LOAD_FLOAT_XFORM_TEST(lfdx);
463 	LOAD_FLOAT_DFORM_TEST(lfdu);
464 	LOAD_FLOAT_XFORM_TEST(lfdux);
465 	LOAD_FLOAT_DFORM_TEST(lfs);
466 	LOAD_FLOAT_XFORM_TEST(lfsx);
467 	LOAD_FLOAT_DFORM_TEST(lfsu);
468 	LOAD_FLOAT_XFORM_TEST(lfsux);
469 	STORE_FLOAT_DFORM_TEST(stfd);
470 	STORE_FLOAT_XFORM_TEST(stfdx);
471 	STORE_FLOAT_DFORM_TEST(stfdu);
472 	STORE_FLOAT_XFORM_TEST(stfdux);
473 	STORE_FLOAT_DFORM_TEST(stfs);
474 	STORE_FLOAT_XFORM_TEST(stfsx);
475 	STORE_FLOAT_DFORM_TEST(stfsu);
476 	STORE_FLOAT_XFORM_TEST(stfsux);
477 	STORE_FLOAT_XFORM_TEST(stfiwx);
478 
479 	return rc;
480 }
481 
482 int test_alignment_handler_fp_205(void)
483 {
484 	int rc = 0;
485 
486 	SKIP_IF(!can_open_fb0());
487 	SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_05));
488 
489 	printf("Floating point: 2.05\n");
490 
491 	LOAD_FLOAT_DFORM_TEST(lfdp);
492 	LOAD_FLOAT_XFORM_TEST(lfdpx);
493 	LOAD_FLOAT_XFORM_TEST(lfiwax);
494 	STORE_FLOAT_DFORM_TEST(stfdp);
495 	STORE_FLOAT_XFORM_TEST(stfdpx);
496 
497 	return rc;
498 }
499 
500 int test_alignment_handler_fp_206(void)
501 {
502 	int rc = 0;
503 
504 	SKIP_IF(!can_open_fb0());
505 	SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06));
506 
507 	printf("Floating point: 2.06\n");
508 
509 	LOAD_FLOAT_XFORM_TEST(lfiwzx);
510 
511 	return rc;
512 }
513 
514 void usage(char *prog)
515 {
516 	printf("Usage: %s [options]\n", prog);
517 	printf("  -d	Enable debug error output\n");
518 	printf("\n");
519 	printf("This test requires a POWER8 or POWER9 CPU and a usable ");
520 	printf("framebuffer at /dev/fb0.\n");
521 }
522 
523 int main(int argc, char *argv[])
524 {
525 
526 	struct sigaction sa;
527 	int rc = 0;
528 	int option = 0;
529 
530 	while ((option = getopt(argc, argv, "d")) != -1) {
531 		switch (option) {
532 		case 'd':
533 			debug++;
534 			break;
535 		default:
536 			usage(argv[0]);
537 			exit(1);
538 		}
539 	}
540 
541 	bufsize = getpagesize();
542 
543 	sa.sa_sigaction = sighandler;
544 	sigemptyset(&sa.sa_mask);
545 	sa.sa_flags = SA_SIGINFO;
546 	if (sigaction(SIGSEGV, &sa, NULL) == -1
547 	    || sigaction(SIGBUS, &sa, NULL) == -1
548 	    || sigaction(SIGILL, &sa, NULL) == -1) {
549 		perror("sigaction");
550 		exit(1);
551 	}
552 
553 	rc |= test_harness(test_alignment_handler_vsx_206,
554 			   "test_alignment_handler_vsx_206");
555 	rc |= test_harness(test_alignment_handler_vsx_207,
556 			   "test_alignment_handler_vsx_207");
557 	rc |= test_harness(test_alignment_handler_vsx_300,
558 			   "test_alignment_handler_vsx_300");
559 	rc |= test_harness(test_alignment_handler_integer,
560 			   "test_alignment_handler_integer");
561 	rc |= test_harness(test_alignment_handler_integer_206,
562 			   "test_alignment_handler_integer_206");
563 	rc |= test_harness(test_alignment_handler_vmx,
564 			   "test_alignment_handler_vmx");
565 	rc |= test_harness(test_alignment_handler_fp,
566 			   "test_alignment_handler_fp");
567 	rc |= test_harness(test_alignment_handler_fp_205,
568 			   "test_alignment_handler_fp_205");
569 	rc |= test_harness(test_alignment_handler_fp_206,
570 			   "test_alignment_handler_fp_206");
571 	return rc;
572 }
573