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