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 syscall(__NR_clone, clone_flags, newsp, parent_tidptr, tls, child_tidptr); 132 } 133 134 #define __STACK_SIZE (8 * 1024 * 1024) 135 136 /* 137 * If we clone with CLONE_VM then the value in the parent should 138 * be unchanged and the child should start with zero and be able to 139 * set its own value. 140 */ 141 static int write_clone_read(void) 142 { 143 int parent_tid, child_tid; 144 pid_t parent, waiting; 145 int ret, status; 146 void *stack; 147 148 parent = getpid(); 149 set_tpidr2(parent); 150 151 stack = malloc(__STACK_SIZE); 152 if (!stack) { 153 ksft_print_msg("malloc() failed\n"); 154 return 0; 155 } 156 157 ret = sys_clone(CLONE_VM, (unsigned long)stack + __STACK_SIZE, 158 &parent_tid, 0, &child_tid); 159 if (ret == -1) { 160 ksft_print_msg("clone() failed: %d\n", errno); 161 return 0; 162 } 163 164 if (ret == 0) { 165 /* In child */ 166 if (get_tpidr2() != 0) { 167 ksft_print_msg("TPIDR2 non-zero in child: %llx\n", 168 get_tpidr2()); 169 exit(0); 170 } 171 172 if (gettid() == 0) 173 ksft_print_msg("Child TID==0\n"); 174 set_tpidr2(gettid()); 175 if (get_tpidr2() == gettid()) { 176 exit(1); 177 } else { 178 ksft_print_msg("Failed to set TPIDR2 in child\n"); 179 exit(0); 180 } 181 } 182 183 for (;;) { 184 waiting = waitpid(ret, &status, __WCLONE); 185 186 if (waiting < 0) { 187 if (errno == EINTR) 188 continue; 189 ksft_print_msg("waitpid() failed: %d\n", errno); 190 return 0; 191 } 192 if (waiting != ret) { 193 ksft_print_msg("waitpid() returned wrong PID %d\n", 194 waiting); 195 return 0; 196 } 197 198 if (!WIFEXITED(status)) { 199 ksft_print_msg("child did not exit\n"); 200 return 0; 201 } 202 203 if (parent != get_tpidr2()) { 204 ksft_print_msg("TPIDR2 corrupted in parent\n"); 205 return 0; 206 } 207 208 return WEXITSTATUS(status); 209 } 210 } 211 212 int main(int argc, char **argv) 213 { 214 int ret; 215 216 ksft_print_header(); 217 ksft_set_plan(5); 218 219 ksft_print_msg("PID: %d\n", getpid()); 220 221 /* 222 * This test is run with nolibc which doesn't support hwcap and 223 * it's probably disproportionate to implement so instead check 224 * for the default vector length configuration in /proc. 225 */ 226 ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0); 227 if (ret >= 0) { 228 ksft_test_result(default_value(), "default_value\n"); 229 ksft_test_result(write_read(), "write_read\n"); 230 ksft_test_result(write_sleep_read(), "write_sleep_read\n"); 231 ksft_test_result(write_fork_read(), "write_fork_read\n"); 232 ksft_test_result(write_clone_read(), "write_clone_read\n"); 233 234 } else { 235 ksft_print_msg("SME support not present\n"); 236 237 ksft_test_result_skip("default_value\n"); 238 ksft_test_result_skip("write_read\n"); 239 ksft_test_result_skip("write_sleep_read\n"); 240 ksft_test_result_skip("write_fork_read\n"); 241 ksft_test_result_skip("write_clone_read\n"); 242 } 243 244 ksft_finished(); 245 } 246