brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · 285c47d Raw
303 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3#include <linux/sched.h>4#include <linux/wait.h>5 6#define SYS_TPIDR2 "S3_3_C13_C0_5"7 8#define EXPECTED_TESTS 59 10static void putstr(const char *str)11{12	write(1, str, strlen(str));13}14 15static void putnum(unsigned int num)16{17	char c;18 19	if (num / 10)20		putnum(num / 10);21 22	c = '0' + (num % 10);23	write(1, &c, 1);24}25 26static int tests_run;27static int tests_passed;28static int tests_failed;29static int tests_skipped;30 31static void set_tpidr2(uint64_t val)32{33	asm volatile (34		"msr	" SYS_TPIDR2 ", %0\n"35		:36		: "r"(val)37		: "cc");38}39 40static uint64_t get_tpidr2(void)41{42	uint64_t val;43 44	asm volatile (45		"mrs	%0, " SYS_TPIDR2 "\n"46		: "=r"(val)47		:48		: "cc");49 50	return val;51}52 53static void print_summary(void)54{55	if (tests_passed + tests_failed + tests_skipped != EXPECTED_TESTS)56		putstr("# UNEXPECTED TEST COUNT: ");57 58	putstr("# Totals: pass:");59	putnum(tests_passed);60	putstr(" fail:");61	putnum(tests_failed);62	putstr(" xfail:0 xpass:0 skip:");63	putnum(tests_skipped);64	putstr(" error:0\n");65}66 67/* Processes should start with TPIDR2 == 0 */68static int default_value(void)69{70	return get_tpidr2() == 0;71}72 73/* If we set TPIDR2 we should read that value */74static int write_read(void)75{76	set_tpidr2(getpid());77 78	return getpid() == get_tpidr2();79}80 81/* If we set a value we should read the same value after scheduling out */82static int write_sleep_read(void)83{84	set_tpidr2(getpid());85 86	msleep(100);87 88	return getpid() == get_tpidr2();89}90 91/*92 * If we fork the value in the parent should be unchanged and the93 * child should start with the same value and be able to set its own94 * value.95 */96static int write_fork_read(void)97{98	pid_t newpid, waiting, oldpid;99	int status;100 101	set_tpidr2(getpid());102 103	oldpid = getpid();104	newpid = fork();105	if (newpid == 0) {106		/* In child */107		if (get_tpidr2() != oldpid) {108			putstr("# TPIDR2 changed in child: ");109			putnum(get_tpidr2());110			putstr("\n");111			exit(0);112		}113 114		set_tpidr2(getpid());115		if (get_tpidr2() == getpid()) {116			exit(1);117		} else {118			putstr("# Failed to set TPIDR2 in child\n");119			exit(0);120		}121	}122	if (newpid < 0) {123		putstr("# fork() failed: -");124		putnum(-newpid);125		putstr("\n");126		return 0;127	}128 129	for (;;) {130		waiting = waitpid(newpid, &status, 0);131 132		if (waiting < 0) {133			if (errno == EINTR)134				continue;135			putstr("# waitpid() failed: ");136			putnum(errno);137			putstr("\n");138			return 0;139		}140		if (waiting != newpid) {141			putstr("# waitpid() returned wrong PID\n");142			return 0;143		}144 145		if (!WIFEXITED(status)) {146			putstr("# child did not exit\n");147			return 0;148		}149 150		if (getpid() != get_tpidr2()) {151			putstr("# TPIDR2 corrupted in parent\n");152			return 0;153		}154 155		return WEXITSTATUS(status);156	}157}158 159/*160 * sys_clone() has a lot of per architecture variation so just define161 * it here rather than adding it to nolibc, plus the raw API is a162 * little more convenient for this test.163 */164static int sys_clone(unsigned long clone_flags, unsigned long newsp,165		     int *parent_tidptr, unsigned long tls,166		     int *child_tidptr)167{168	return my_syscall5(__NR_clone, clone_flags, newsp, parent_tidptr, tls,169			   child_tidptr);170}171 172/*173 * If we clone with CLONE_SETTLS then the value in the parent should174 * be unchanged and the child should start with zero and be able to175 * set its own value.176 */177static int write_clone_read(void)178{179	int parent_tid, child_tid;180	pid_t parent, waiting;181	int ret, status;182 183	parent = getpid();184	set_tpidr2(parent);185 186	ret = sys_clone(CLONE_SETTLS, 0, &parent_tid, 0, &child_tid);187	if (ret == -1) {188		putstr("# clone() failed\n");189		putnum(errno);190		putstr("\n");191		return 0;192	}193 194	if (ret == 0) {195		/* In child */196		if (get_tpidr2() != 0) {197			putstr("# TPIDR2 non-zero in child: ");198			putnum(get_tpidr2());199			putstr("\n");200			exit(0);201		}202 203		if (gettid() == 0)204			putstr("# Child TID==0\n");205		set_tpidr2(gettid());206		if (get_tpidr2() == gettid()) {207			exit(1);208		} else {209			putstr("# Failed to set TPIDR2 in child\n");210			exit(0);211		}212	}213 214	for (;;) {215		waiting = wait4(ret, &status, __WCLONE, NULL);216 217		if (waiting < 0) {218			if (errno == EINTR)219				continue;220			putstr("# wait4() failed: ");221			putnum(errno);222			putstr("\n");223			return 0;224		}225		if (waiting != ret) {226			putstr("# wait4() returned wrong PID ");227			putnum(waiting);228			putstr("\n");229			return 0;230		}231 232		if (!WIFEXITED(status)) {233			putstr("# child did not exit\n");234			return 0;235		}236 237		if (parent != get_tpidr2()) {238			putstr("# TPIDR2 corrupted in parent\n");239			return 0;240		}241 242		return WEXITSTATUS(status);243	}244}245 246#define run_test(name)			     \247	if (name()) {			     \248		tests_passed++;		     \249	} else {			     \250		tests_failed++;		     \251		putstr("not ");		     \252	}				     \253	putstr("ok ");			     \254	putnum(++tests_run);		     \255	putstr(" " #name "\n");256 257#define skip_test(name)			     \258	tests_skipped++;		     \259	putstr("ok ");			     \260	putnum(++tests_run);		     \261	putstr(" # SKIP " #name "\n");262 263int main(int argc, char **argv)264{265	int ret;266 267	putstr("TAP version 13\n");268	putstr("1..");269	putnum(EXPECTED_TESTS);270	putstr("\n");271 272	putstr("# PID: ");273	putnum(getpid());274	putstr("\n");275 276	/*277	 * This test is run with nolibc which doesn't support hwcap and278	 * it's probably disproportionate to implement so instead check279	 * for the default vector length configuration in /proc.280	 */281	ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);282	if (ret >= 0) {283		run_test(default_value);284		run_test(write_read);285		run_test(write_sleep_read);286		run_test(write_fork_read);287		run_test(write_clone_read);288 289	} else {290		putstr("# SME support not present\n");291 292		skip_test(default_value);293		skip_test(write_read);294		skip_test(write_sleep_read);295		skip_test(write_fork_read);296		skip_test(write_clone_read);297	}298 299	print_summary();300 301	return 0;302}303