1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/sched.h> 4 #include <linux/wait.h> 5 6 #include "kselftest.h" 7 8 #define SYS_TPIDR2 "S3_3_C13_C0_5" 9 10 #define EXPECTED_TESTS 5 11 12 static void set_tpidr2(uint64_t val) 13 { 14 asm volatile ( 15 "msr " SYS_TPIDR2 ", %0\n" 16 : 17 : "r"(val) 18 : "cc"); 19 } 20 21 static uint64_t get_tpidr2(void) 22 { 23 uint64_t val; 24 25 asm volatile ( 26 "mrs %0, " SYS_TPIDR2 "\n" 27 : "=r"(val) 28 : 29 : "cc"); 30 31 return val; 32 } 33 34 /* Processes should start with TPIDR2 == 0 */ 35 static int default_value(void) 36 { 37 return get_tpidr2() == 0; 38 } 39 40 /* If we set TPIDR2 we should read that value */ 41 static int write_read(void) 42 { 43 set_tpidr2(getpid()); 44 45 return getpid() == get_tpidr2(); 46 } 47 48 /* If we set a value we should read the same value after scheduling out */ 49 static int write_sleep_read(void) 50 { 51 set_tpidr2(getpid()); 52 53 msleep(100); 54 55 return getpid() == get_tpidr2(); 56 } 57 58 /* 59 * If we fork the value in the parent should be unchanged and the 60 * child should start with the same value and be able to set its own 61 * value. 62 */ 63 static int write_fork_read(void) 64 { 65 pid_t newpid, waiting, oldpid; 66 int status; 67 68 set_tpidr2(getpid()); 69 70 oldpid = getpid(); 71 newpid = fork(); 72 if (newpid == 0) { 73 /* In child */ 74 if (get_tpidr2() != oldpid) { 75 ksft_print_msg("TPIDR2 changed in child: %llx\n", 76 get_tpidr2()); 77 exit(0); 78 } 79 80 set_tpidr2(getpid()); 81 if (get_tpidr2() == getpid()) { 82 exit(1); 83 } else { 84 ksft_print_msg("Failed to set TPIDR2 in child\n"); 85 exit(0); 86 } 87 } 88 if (newpid < 0) { 89 ksft_print_msg("fork() failed: %d\n", newpid); 90 return 0; 91 } 92 93 for (;;) { 94 waiting = waitpid(newpid, &status, 0); 95 96 if (waiting < 0) { 97 if (errno == EINTR) 98 continue; 99 ksft_print_msg("waitpid() failed: %d\n", errno); 100 return 0; 101 } 102 if (waiting != newpid) { 103 ksft_print_msg("waitpid() returned wrong PID: %d != %d\n", 104 waiting, newpid); 105 return 0; 106 } 107 108 if (!WIFEXITED(status)) { 109 ksft_print_msg("child did not exit\n"); 110 return 0; 111 } 112 113 if (getpid() != get_tpidr2()) { 114 ksft_print_msg("TPIDR2 corrupted in parent\n"); 115 return 0; 116 } 117 118 return WEXITSTATUS(status); 119 } 120 } 121 122 /* 123 * sys_clone() has a lot of per architecture variation so just define 124 * it here rather than adding it to nolibc, plus the raw API is a 125 * little more convenient for this test. 126 */ 127 static int sys_clone(unsigned long clone_flags, unsigned long newsp, 128 int *parent_tidptr, unsigned long tls, 129 int *child_tidptr) 130 { 131 return my_syscall5(__NR_clone, clone_flags, newsp, parent_tidptr, tls, 132 child_tidptr); 133 } 134 135 #define __STACK_SIZE (8 * 1024 * 1024) 136 137 /* 138 * If we clone with CLONE_VM then the value in the parent should 139 * be unchanged and the child should start with zero and be able to 140 * set its own value. 141 */ 142 static int write_clone_read(void) 143 { 144 int parent_tid, child_tid; 145 pid_t parent, waiting; 146 int ret, status; 147 void *stack; 148 149 parent = getpid(); 150 set_tpidr2(parent); 151 152 stack = malloc(__STACK_SIZE); 153 if (!stack) { 154 ksft_print_msg("malloc() failed\n"); 155 return 0; 156 } 157 158 ret = sys_clone(CLONE_VM, (unsigned long)stack + __STACK_SIZE, 159 &parent_tid, 0, &child_tid); 160 if (ret == -1) { 161 ksft_print_msg("clone() failed: %d\n", errno); 162 return 0; 163 } 164 165 if (ret == 0) { 166 /* In child */ 167 if (get_tpidr2() != 0) { 168 ksft_print_msg("TPIDR2 non-zero in child: %llx\n", 169 get_tpidr2()); 170 exit(0); 171 } 172 173 if (gettid() == 0) 174 ksft_print_msg("Child TID==0\n"); 175 set_tpidr2(gettid()); 176 if (get_tpidr2() == gettid()) { 177 exit(1); 178 } else { 179 ksft_print_msg("Failed to set TPIDR2 in child\n"); 180 exit(0); 181 } 182 } 183 184 for (;;) { 185 waiting = wait4(ret, &status, __WCLONE, NULL); 186 187 if (waiting < 0) { 188 if (errno == EINTR) 189 continue; 190 ksft_print_msg("wait4() failed: %d\n", errno); 191 return 0; 192 } 193 if (waiting != ret) { 194 ksft_print_msg("wait4() returned wrong PID %d\n", 195 waiting); 196 return 0; 197 } 198 199 if (!WIFEXITED(status)) { 200 ksft_print_msg("child did not exit\n"); 201 return 0; 202 } 203 204 if (parent != get_tpidr2()) { 205 ksft_print_msg("TPIDR2 corrupted in parent\n"); 206 return 0; 207 } 208 209 return WEXITSTATUS(status); 210 } 211 } 212 213 int main(int argc, char **argv) 214 { 215 int ret; 216 217 ksft_print_header(); 218 ksft_set_plan(5); 219 220 ksft_print_msg("PID: %d\n", getpid()); 221 222 /* 223 * This test is run with nolibc which doesn't support hwcap and 224 * it's probably disproportionate to implement so instead check 225 * for the default vector length configuration in /proc. 226 */ 227 ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0); 228 if (ret >= 0) { 229 ksft_test_result(default_value(), "default_value\n"); 230 ksft_test_result(write_read, "write_read\n"); 231 ksft_test_result(write_sleep_read, "write_sleep_read\n"); 232 ksft_test_result(write_fork_read, "write_fork_read\n"); 233 ksft_test_result(write_clone_read, "write_clone_read\n"); 234 235 } else { 236 ksft_print_msg("SME support not present\n"); 237 238 ksft_test_result_skip("default_value\n"); 239 ksft_test_result_skip("write_read\n"); 240 ksft_test_result_skip("write_sleep_read\n"); 241 ksft_test_result_skip("write_fork_read\n"); 242 ksft_test_result_skip("write_clone_read\n"); 243 } 244 245 ksft_finished(); 246 } 247