680 lines · 16.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Test the powerpc alignment handler on POWER8/POWER9 * * Copyright (C) 2017 IBM Corporation (Michael Neuling, Andrew Donnellan) */ /* * This selftest exercises the powerpc alignment fault handler. * * We create two sets of source and destination buffers, one in regular memory, * the other cache-inhibited (by default we use /dev/fb0 for this, but an * alterative path for cache-inhibited memory may be provided, e.g. memtrace). * * We initialise the source buffers, then use whichever set of load/store * instructions is under test to copy bytes from the source buffers to the * destination buffers. For the regular buffers, these instructions will * execute normally. For the cache-inhibited buffers, these instructions * will trap and cause an alignment fault, and the alignment fault handler * will emulate the particular instruction under test. We then compare the * destination buffers to ensure that the native and emulated cases give the * same result. * * TODO: *   - Any FIXMEs below *   - Test VSX regs < 32 and > 32 *   - Test all loads and stores *   - Check update forms do update register *   - Test alignment faults over page boundary * * Some old binutils may not support all the instructions. */  #include <sys/mman.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdbool.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <getopt.h>#include <setjmp.h>#include <signal.h> #include "utils.h"#include "instructions.h" int bufsize;int debug;int testing;volatile int gotsig;bool prefixes_enabled;char *cipath = "/dev/fb0";long cioffset; void sighandler(int sig, siginfo_t *info, void *ctx){	ucontext_t *ucp = ctx; 	if (!testing) {		signal(sig, SIG_DFL);		kill(0, sig);	}	gotsig = sig;#ifdef __powerpc64__	if (prefixes_enabled) {		u32 inst = *(u32 *)ucp->uc_mcontext.gp_regs[PT_NIP];		ucp->uc_mcontext.gp_regs[PT_NIP] += ((inst >> 26 == 1) ? 8 : 4);	} else {		ucp->uc_mcontext.gp_regs[PT_NIP] += 4;	}#else	ucp->uc_mcontext.uc_regs->gregs[PT_NIP] += 4;#endif} #define XFORM(reg, n)  " " #reg " ,%"#n",%2 ;"#define DFORM(reg, n)  " " #reg " ,0(%"#n") ;" #define TEST(name, ld_op, st_op, form, ld_reg, st_reg)		\	void test_##name(char *s, char *d)			\	{							\		asm volatile(					\			#ld_op form(ld_reg, 0)			\			#st_op form(st_reg, 1)			\			:: "r"(s), "r"(d), "r"(0)		\			: "memory", "vs0", "vs32", "r31");	\	}							\	rc |= do_test(#name, test_##name) #define TESTP(name, ld_op, st_op, ld_reg, st_reg)		\	void test_##name(char *s, char *d)			\	{							\		asm volatile(					\			ld_op(ld_reg, %0, 0, 0)			\			st_op(st_reg, %1, 0, 0)			\			:: "r"(s), "r"(d), "r"(0)		\			: "memory", "vs0", "vs32", "r31");	\	}							\	rc |= do_test(#name, test_##name) #define LOAD_VSX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 32, 32)#define STORE_VSX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 32)#define LOAD_VSX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 32, 32)#define STORE_VSX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 32)#define LOAD_VMX_XFORM_TEST(op) TEST(op, op, stxvd2x, XFORM, 0, 32)#define STORE_VMX_XFORM_TEST(op) TEST(op, lxvd2x, op, XFORM, 32, 0)#define LOAD_VMX_DFORM_TEST(op) TEST(op, op, stxv, DFORM, 0, 32)#define STORE_VMX_DFORM_TEST(op) TEST(op, lxv, op, DFORM, 32, 0) #define LOAD_XFORM_TEST(op) TEST(op, op, stdx, XFORM, 31, 31)#define STORE_XFORM_TEST(op) TEST(op, ldx, op, XFORM, 31, 31)#define LOAD_DFORM_TEST(op) TEST(op, op, std, DFORM, 31, 31)#define STORE_DFORM_TEST(op) TEST(op, ld, op, DFORM, 31, 31) #define LOAD_FLOAT_DFORM_TEST(op)  TEST(op, op, stfd, DFORM, 0, 0)#define STORE_FLOAT_DFORM_TEST(op) TEST(op, lfd, op, DFORM, 0, 0)#define LOAD_FLOAT_XFORM_TEST(op)  TEST(op, op, stfdx, XFORM, 0, 0)#define STORE_FLOAT_XFORM_TEST(op) TEST(op, lfdx, op, XFORM, 0, 0) #define LOAD_MLS_PREFIX_TEST(op) TESTP(op, op, PSTD, 31, 31)#define STORE_MLS_PREFIX_TEST(op) TESTP(op, PLD, op, 31, 31) #define LOAD_8LS_PREFIX_TEST(op) TESTP(op, op, PSTD, 31, 31)#define STORE_8LS_PREFIX_TEST(op) TESTP(op, PLD, op, 31, 31) #define LOAD_FLOAT_MLS_PREFIX_TEST(op) TESTP(op, op, PSTFD, 0, 0)#define STORE_FLOAT_MLS_PREFIX_TEST(op) TESTP(op, PLFD, op, 0, 0) #define LOAD_VSX_8LS_PREFIX_TEST(op, tail) TESTP(op, op, PSTXV ## tail, 0, 32)#define STORE_VSX_8LS_PREFIX_TEST(op, tail) TESTP(op, PLXV ## tail, op, 32, 0) /* FIXME: Unimplemented tests: */// STORE_DFORM_TEST(stq)   /* FIXME: need two registers for quad */// STORE_DFORM_TEST(stswi) /* FIXME: string instruction */ // STORE_XFORM_TEST(stwat) /* AMO can't emulate or run on CI */// STORE_XFORM_TEST(stdat) /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */  /* preload byte by byte */void preload_data(void *dst, int offset, int width){	char *c = dst;	int i; 	c += offset; 	for (i = 0 ; i < width ; i++)		c[i] = i;} int test_memcpy(void *dst, void *src, int size, int offset,		void (*test_func)(char *, char *)){	char *s, *d; 	s = src;	s += offset;	d = dst;	d += offset; 	assert(size == 16);	gotsig = 0;	testing = 1; 	test_func(s, d); /* run the actual test */ 	testing = 0;	if (gotsig) {		if (debug)			printf("  Got signal %i\n", gotsig);		return 1;	}	return 0;} void dumpdata(char *s1, char *s2, int n, char *test_name){	int i; 	printf("  %s: unexpected result:\n", test_name);	printf("    mem:");	for (i = 0; i < n; i++)		printf(" %02x", s1[i]);	printf("\n");	printf("    ci: ");	for (i = 0; i < n; i++)		printf(" %02x", s2[i]);	printf("\n");} int test_memcmp(void *s1, void *s2, int n, int offset, char *test_name){	char *s1c, *s2c; 	s1c = s1;	s1c += offset;	s2c = s2;	s2c += offset; 	if (memcmp(s1c, s2c, n)) {		if (debug) {			printf("\n  Compare failed. Offset:%i length:%i\n",			       offset, n);			dumpdata(s1c, s2c, n, test_name);		}		return 1;	}	return 0;} /* * Do two memcpy tests using the same instructions. One cachable * memory and the other doesn't. */int do_test(char *test_name, void (*test_func)(char *, char *)){	int offset, width, fd, rc, r;	void *mem0, *mem1, *ci0, *ci1; 	printf("\tDoing %s:\t", test_name); 	fd = open(cipath, O_RDWR);	if (fd < 0) {		printf("\n");		perror("Can't open ci file now?");		return 1;	} 	ci0 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED,		   fd, cioffset);	ci1 = mmap(NULL, bufsize, PROT_WRITE | PROT_READ, MAP_SHARED,		   fd, cioffset + bufsize); 	if ((ci0 == MAP_FAILED) || (ci1 == MAP_FAILED)) {		printf("\n");		perror("mmap failed");		SKIP_IF(1);	} 	rc = posix_memalign(&mem0, bufsize, bufsize);	if (rc) {		printf("\n");		return rc;	} 	rc = posix_memalign(&mem1, bufsize, bufsize);	if (rc) {		printf("\n");		free(mem0);		return rc;	} 	rc = 0;	/*	 * offset = 0 is aligned but tests the workaround for the P9N	 * DD2.1 vector CI load issue (see 5080332c2c89 "powerpc/64s:	 * Add workaround for P9 vector CI load issue")	 */	for (offset = 0; offset < 16; offset++) {		width = 16; /* vsx == 16 bytes */		r = 0; 		/* load pattern into memory byte by byte */		preload_data(ci0, offset, width);		preload_data(mem0, offset, width); // FIXME: remove??		memcpy(ci0, mem0, bufsize);		memcpy(ci1, mem1, bufsize); /* initialise output to the same */ 		/* sanity check */		test_memcmp(mem0, ci0, width, offset, test_name); 		r |= test_memcpy(ci1,  ci0,  width, offset, test_func);		r |= test_memcpy(mem1, mem0, width, offset, test_func);		if (r && !debug) {			printf("FAILED: Got signal");			rc = 1;			break;		} 		r |= test_memcmp(mem1, ci1, width, offset, test_name);		if (r && !debug) {			printf("FAILED: Wrong Data");			rc = 1;			break;		}	} 	if (rc == 0)		printf("PASSED"); 	printf("\n"); 	munmap(ci0, bufsize);	munmap(ci1, bufsize);	free(mem0);	free(mem1);	close(fd); 	return rc;} static bool can_open_cifile(void){	int fd; 	fd = open(cipath, O_RDWR);	if (fd < 0)		return false; 	close(fd);	return true;} int test_alignment_handler_vsx_206(void){	int rc = 0; 	SKIP_IF(!can_open_cifile());	SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); 	printf("VSX: 2.06B\n");	LOAD_VSX_XFORM_TEST(lxvd2x);	LOAD_VSX_XFORM_TEST(lxvw4x);	LOAD_VSX_XFORM_TEST(lxsdx);	LOAD_VSX_XFORM_TEST(lxvdsx);	STORE_VSX_XFORM_TEST(stxvd2x);	STORE_VSX_XFORM_TEST(stxvw4x);	STORE_VSX_XFORM_TEST(stxsdx);	return rc;} int test_alignment_handler_vsx_207(void){	int rc = 0; 	SKIP_IF(!can_open_cifile());	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07)); 	printf("VSX: 2.07B\n");	LOAD_VSX_XFORM_TEST(lxsspx);	LOAD_VSX_XFORM_TEST(lxsiwax);	LOAD_VSX_XFORM_TEST(lxsiwzx);	STORE_VSX_XFORM_TEST(stxsspx);	STORE_VSX_XFORM_TEST(stxsiwx);	return rc;} int test_alignment_handler_vsx_300(void){	int rc = 0; 	SKIP_IF(!can_open_cifile()); 	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_00));	printf("VSX: 3.00B\n");	LOAD_VMX_DFORM_TEST(lxsd);	LOAD_VSX_XFORM_TEST(lxsibzx);	LOAD_VSX_XFORM_TEST(lxsihzx);	LOAD_VMX_DFORM_TEST(lxssp);	LOAD_VSX_DFORM_TEST(lxv);	LOAD_VSX_XFORM_TEST(lxvb16x);	LOAD_VSX_XFORM_TEST(lxvh8x);	LOAD_VSX_XFORM_TEST(lxvx);	LOAD_VSX_XFORM_TEST(lxvwsx);	LOAD_VSX_XFORM_TEST(lxvl);	LOAD_VSX_XFORM_TEST(lxvll);	STORE_VMX_DFORM_TEST(stxsd);	STORE_VSX_XFORM_TEST(stxsibx);	STORE_VSX_XFORM_TEST(stxsihx);	STORE_VMX_DFORM_TEST(stxssp);	STORE_VSX_DFORM_TEST(stxv);	STORE_VSX_XFORM_TEST(stxvb16x);	STORE_VSX_XFORM_TEST(stxvh8x);	STORE_VSX_XFORM_TEST(stxvx);	STORE_VSX_XFORM_TEST(stxvl);	STORE_VSX_XFORM_TEST(stxvll);	return rc;} int test_alignment_handler_vsx_prefix(void){	int rc = 0; 	SKIP_IF(!can_open_cifile());	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1)); 	printf("VSX: PREFIX\n");	LOAD_VSX_8LS_PREFIX_TEST(PLXSD, 0);	LOAD_VSX_8LS_PREFIX_TEST(PLXSSP, 0);	LOAD_VSX_8LS_PREFIX_TEST(PLXV0, 0);	LOAD_VSX_8LS_PREFIX_TEST(PLXV1, 1);	STORE_VSX_8LS_PREFIX_TEST(PSTXSD, 0);	STORE_VSX_8LS_PREFIX_TEST(PSTXSSP, 0);	STORE_VSX_8LS_PREFIX_TEST(PSTXV0, 0);	STORE_VSX_8LS_PREFIX_TEST(PSTXV1, 1);	return rc;} int test_alignment_handler_integer(void){	int rc = 0; 	SKIP_IF(!can_open_cifile()); 	printf("Integer\n");	LOAD_DFORM_TEST(lbz);	LOAD_DFORM_TEST(lbzu);	LOAD_XFORM_TEST(lbzx);	LOAD_XFORM_TEST(lbzux);	LOAD_DFORM_TEST(lhz);	LOAD_DFORM_TEST(lhzu);	LOAD_XFORM_TEST(lhzx);	LOAD_XFORM_TEST(lhzux);	LOAD_DFORM_TEST(lha);	LOAD_DFORM_TEST(lhau);	LOAD_XFORM_TEST(lhax);	LOAD_XFORM_TEST(lhaux);	LOAD_XFORM_TEST(lhbrx);	LOAD_DFORM_TEST(lwz);	LOAD_DFORM_TEST(lwzu);	LOAD_XFORM_TEST(lwzx);	LOAD_XFORM_TEST(lwzux);	LOAD_DFORM_TEST(lwa);	LOAD_XFORM_TEST(lwax);	LOAD_XFORM_TEST(lwaux);	LOAD_XFORM_TEST(lwbrx);	LOAD_DFORM_TEST(ld);	LOAD_DFORM_TEST(ldu);	LOAD_XFORM_TEST(ldx);	LOAD_XFORM_TEST(ldux);	STORE_DFORM_TEST(stb);	STORE_XFORM_TEST(stbx);	STORE_DFORM_TEST(stbu);	STORE_XFORM_TEST(stbux);	STORE_DFORM_TEST(sth);	STORE_XFORM_TEST(sthx);	STORE_DFORM_TEST(sthu);	STORE_XFORM_TEST(sthux);	STORE_XFORM_TEST(sthbrx);	STORE_DFORM_TEST(stw);	STORE_XFORM_TEST(stwx);	STORE_DFORM_TEST(stwu);	STORE_XFORM_TEST(stwux);	STORE_XFORM_TEST(stwbrx);	STORE_DFORM_TEST(std);	STORE_XFORM_TEST(stdx);	STORE_DFORM_TEST(stdu);	STORE_XFORM_TEST(stdux); #ifdef __BIG_ENDIAN__	LOAD_DFORM_TEST(lmw);	STORE_DFORM_TEST(stmw);#endif 	return rc;} int test_alignment_handler_integer_206(void){	int rc = 0; 	SKIP_IF(!can_open_cifile());	SKIP_IF(!have_hwcap(PPC_FEATURE_ARCH_2_06)); 	printf("Integer: 2.06\n"); 	LOAD_XFORM_TEST(ldbrx);	STORE_XFORM_TEST(stdbrx); 	return rc;} int test_alignment_handler_integer_prefix(void){	int rc = 0; 	SKIP_IF(!can_open_cifile());	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_3_1)); 	printf("Integer: PREFIX\n");	LOAD_MLS_PREFIX_TEST(PLBZ);	LOAD_MLS_PREFIX_TEST(PLHZ);	LOAD_MLS_PREFIX_TEST(PLHA);	LOAD_MLS_PREFIX_TEST(PLWZ);	LOAD_8LS_PREFIX_TEST(PLWA);	LOAD_8LS_PREFIX_TEST(PLD);	STORE_MLS_PREFIX_TEST(PSTB);	STORE_MLS_PREFIX_TEST(PSTH);	STORE_MLS_PREFIX_TEST(PSTW);	STORE_8LS_PREFIX_TEST(PSTD);	return rc;} int test_alignment_handler_vmx(void)

Lines 1–500 of 680.