linux/tools/testing/selftests/arm64/abi/tpidr2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   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 5
   9
  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 the
  93 * child should start with the same value and be able to set its own
  94 * 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 define
 161 * it here rather than adding it to nolibc, plus the raw API is a
 162 * 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 should
 174 * be unchanged and the child should start with zero and be able to
 175 * 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
 257int main(int argc, char **argv)
 258{
 259        int ret, i;
 260
 261        putstr("TAP version 13\n");
 262        putstr("1..");
 263        putnum(EXPECTED_TESTS);
 264        putstr("\n");
 265
 266        putstr("# PID: ");
 267        putnum(getpid());
 268        putstr("\n");
 269
 270        /*
 271         * This test is run with nolibc which doesn't support hwcap and
 272         * it's probably disproportionate to implement so instead check
 273         * for the default vector length configuration in /proc.
 274         */
 275        ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);
 276        if (ret >= 0) {
 277                run_test(default_value);
 278                run_test(write_read);
 279                run_test(write_sleep_read);
 280                run_test(write_fork_read);
 281                run_test(write_clone_read);
 282
 283        } else {
 284                putstr("# SME support not present\n");
 285
 286                for (i = 0; i < EXPECTED_TESTS; i++) {
 287                        putstr("ok ");
 288                        putnum(i);
 289                        putstr(" skipped, TPIDR2 not supported\n");
 290                }
 291
 292                tests_skipped += EXPECTED_TESTS;
 293        }
 294
 295        print_summary();
 296
 297        return 0;
 298}
 299