linux/tools/testing/selftests/arm64/fp/vec-syscfg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2021 ARM Limited.
   4 * Original author: Mark Brown <broonie@kernel.org>
   5 */
   6#include <assert.h>
   7#include <errno.h>
   8#include <fcntl.h>
   9#include <stddef.h>
  10#include <stdio.h>
  11#include <stdlib.h>
  12#include <string.h>
  13#include <unistd.h>
  14#include <sys/auxv.h>
  15#include <sys/prctl.h>
  16#include <sys/types.h>
  17#include <sys/wait.h>
  18#include <asm/sigcontext.h>
  19#include <asm/hwcap.h>
  20
  21#include "../../kselftest.h"
  22#include "rdvl.h"
  23
  24#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
  25
  26#define ARCH_MIN_VL SVE_VL_MIN
  27
  28struct vec_data {
  29        const char *name;
  30        unsigned long hwcap_type;
  31        unsigned long hwcap;
  32        const char *rdvl_binary;
  33        int (*rdvl)(void);
  34
  35        int prctl_get;
  36        int prctl_set;
  37        const char *default_vl_file;
  38
  39        int default_vl;
  40        int min_vl;
  41        int max_vl;
  42};
  43
  44
  45static struct vec_data vec_data[] = {
  46        {
  47                .name = "SVE",
  48                .hwcap_type = AT_HWCAP,
  49                .hwcap = HWCAP_SVE,
  50                .rdvl = rdvl_sve,
  51                .rdvl_binary = "./rdvl-sve",
  52                .prctl_get = PR_SVE_GET_VL,
  53                .prctl_set = PR_SVE_SET_VL,
  54                .default_vl_file = "/proc/sys/abi/sve_default_vector_length",
  55        },
  56};
  57
  58static int stdio_read_integer(FILE *f, const char *what, int *val)
  59{
  60        int n = 0;
  61        int ret;
  62
  63        ret = fscanf(f, "%d%*1[\n]%n", val, &n);
  64        if (ret < 1 || n < 1) {
  65                ksft_print_msg("failed to parse integer from %s\n", what);
  66                return -1;
  67        }
  68
  69        return 0;
  70}
  71
  72/* Start a new process and return the vector length it sees */
  73static int get_child_rdvl(struct vec_data *data)
  74{
  75        FILE *out;
  76        int pipefd[2];
  77        pid_t pid, child;
  78        int read_vl, ret;
  79
  80        ret = pipe(pipefd);
  81        if (ret == -1) {
  82                ksft_print_msg("pipe() failed: %d (%s)\n",
  83                               errno, strerror(errno));
  84                return -1;
  85        }
  86
  87        fflush(stdout);
  88
  89        child = fork();
  90        if (child == -1) {
  91                ksft_print_msg("fork() failed: %d (%s)\n",
  92                               errno, strerror(errno));
  93                close(pipefd[0]);
  94                close(pipefd[1]);
  95                return -1;
  96        }
  97
  98        /* Child: put vector length on the pipe */
  99        if (child == 0) {
 100                /*
 101                 * Replace stdout with the pipe, errors to stderr from
 102                 * here as kselftest prints to stdout.
 103                 */
 104                ret = dup2(pipefd[1], 1);
 105                if (ret == -1) {
 106                        fprintf(stderr, "dup2() %d\n", errno);
 107                        exit(EXIT_FAILURE);
 108                }
 109
 110                /* exec() a new binary which puts the VL on stdout */
 111                ret = execl(data->rdvl_binary, data->rdvl_binary, NULL);
 112                fprintf(stderr, "execl(%s) failed: %d\n",
 113                        data->rdvl_binary, errno, strerror(errno));
 114
 115                exit(EXIT_FAILURE);
 116        }
 117
 118        close(pipefd[1]);
 119
 120        /* Parent; wait for the exit status from the child & verify it */
 121        do {
 122                pid = wait(&ret);
 123                if (pid == -1) {
 124                        ksft_print_msg("wait() failed: %d (%s)\n",
 125                                       errno, strerror(errno));
 126                        close(pipefd[0]);
 127                        return -1;
 128                }
 129        } while (pid != child);
 130
 131        assert(pid == child);
 132
 133        if (!WIFEXITED(ret)) {
 134                ksft_print_msg("child exited abnormally\n");
 135                close(pipefd[0]);
 136                return -1;
 137        }
 138
 139        if (WEXITSTATUS(ret) != 0) {
 140                ksft_print_msg("child returned error %d\n",
 141                               WEXITSTATUS(ret));
 142                close(pipefd[0]);
 143                return -1;
 144        }
 145
 146        out = fdopen(pipefd[0], "r");
 147        if (!out) {
 148                ksft_print_msg("failed to open child stdout\n");
 149                close(pipefd[0]);
 150                return -1;
 151        }
 152
 153        ret = stdio_read_integer(out, "child", &read_vl);
 154        fclose(out);
 155        if (ret != 0)
 156                return ret;
 157
 158        return read_vl;
 159}
 160
 161static int file_read_integer(const char *name, int *val)
 162{
 163        FILE *f;
 164        int ret;
 165
 166        f = fopen(name, "r");
 167        if (!f) {
 168                ksft_test_result_fail("Unable to open %s: %d (%s)\n",
 169                                      name, errno,
 170                                      strerror(errno));
 171                return -1;
 172        }
 173
 174        ret = stdio_read_integer(f, name, val);
 175        fclose(f);
 176
 177        return ret;
 178}
 179
 180static int file_write_integer(const char *name, int val)
 181{
 182        FILE *f;
 183        int ret;
 184
 185        f = fopen(name, "w");
 186        if (!f) {
 187                ksft_test_result_fail("Unable to open %s: %d (%s)\n",
 188                                      name, errno,
 189                                      strerror(errno));
 190                return -1;
 191        }
 192
 193        fprintf(f, "%d", val);
 194        fclose(f);
 195        if (ret < 0) {
 196                ksft_test_result_fail("Error writing %d to %s\n",
 197                                      val, name);
 198                return -1;
 199        }
 200
 201        return 0;
 202}
 203
 204/*
 205 * Verify that we can read the default VL via proc, checking that it
 206 * is set in a freshly spawned child.
 207 */
 208static void proc_read_default(struct vec_data *data)
 209{
 210        int default_vl, child_vl, ret;
 211
 212        ret = file_read_integer(data->default_vl_file, &default_vl);
 213        if (ret != 0)
 214                return;
 215
 216        /* Is this the actual default seen by new processes? */
 217        child_vl = get_child_rdvl(data);
 218        if (child_vl != default_vl) {
 219                ksft_test_result_fail("%s is %d but child VL is %d\n",
 220                                      data->default_vl_file,
 221                                      default_vl, child_vl);
 222                return;
 223        }
 224
 225        ksft_test_result_pass("%s default vector length %d\n", data->name,
 226                              default_vl);
 227        data->default_vl = default_vl;
 228}
 229
 230/* Verify that we can write a minimum value and have it take effect */
 231static void proc_write_min(struct vec_data *data)
 232{
 233        int ret, new_default, child_vl;
 234
 235        if (geteuid() != 0) {
 236                ksft_test_result_skip("Need to be root to write to /proc\n");
 237                return;
 238        }
 239
 240        ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL);
 241        if (ret != 0)
 242                return;
 243
 244        /* What was the new value? */
 245        ret = file_read_integer(data->default_vl_file, &new_default);
 246        if (ret != 0)
 247                return;
 248
 249        /* Did it take effect in a new process? */
 250        child_vl = get_child_rdvl(data);
 251        if (child_vl != new_default) {
 252                ksft_test_result_fail("%s is %d but child VL is %d\n",
 253                                      data->default_vl_file,
 254                                      new_default, child_vl);
 255                return;
 256        }
 257
 258        ksft_test_result_pass("%s minimum vector length %d\n", data->name,
 259                              new_default);
 260        data->min_vl = new_default;
 261
 262        file_write_integer(data->default_vl_file, data->default_vl);
 263}
 264
 265/* Verify that we can write a maximum value and have it take effect */
 266static void proc_write_max(struct vec_data *data)
 267{
 268        int ret, new_default, child_vl;
 269
 270        if (geteuid() != 0) {
 271                ksft_test_result_skip("Need to be root to write to /proc\n");
 272                return;
 273        }
 274
 275        /* -1 is accepted by the /proc interface as the maximum VL */
 276        ret = file_write_integer(data->default_vl_file, -1);
 277        if (ret != 0)
 278                return;
 279
 280        /* What was the new value? */
 281        ret = file_read_integer(data->default_vl_file, &new_default);
 282        if (ret != 0)
 283                return;
 284
 285        /* Did it take effect in a new process? */
 286        child_vl = get_child_rdvl(data);
 287        if (child_vl != new_default) {
 288                ksft_test_result_fail("%s is %d but child VL is %d\n",
 289                                      data->default_vl_file,
 290                                      new_default, child_vl);
 291                return;
 292        }
 293
 294        ksft_test_result_pass("%s maximum vector length %d\n", data->name,
 295                              new_default);
 296        data->max_vl = new_default;
 297
 298        file_write_integer(data->default_vl_file, data->default_vl);
 299}
 300
 301/* Can we read back a VL from prctl? */
 302static void prctl_get(struct vec_data *data)
 303{
 304        int ret;
 305
 306        ret = prctl(data->prctl_get);
 307        if (ret == -1) {
 308                ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
 309                                      data->name, errno, strerror(errno));
 310                return;
 311        }
 312
 313        /* Mask out any flags */
 314        ret &= PR_SVE_VL_LEN_MASK;
 315
 316        /* Is that what we can read back directly? */
 317        if (ret == data->rdvl())
 318                ksft_test_result_pass("%s current VL is %d\n",
 319                                      data->name, ret);
 320        else
 321                ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
 322                                      data->name, ret, data->rdvl());
 323}
 324
 325/* Does the prctl let us set the VL we already have? */
 326static void prctl_set_same(struct vec_data *data)
 327{
 328        int cur_vl = data->rdvl();
 329        int ret;
 330
 331        ret = prctl(data->prctl_set, cur_vl);
 332        if (ret < 0) {
 333                ksft_test_result_fail("%s prctl set failed: %d (%s)\n",
 334                                      data->name, errno, strerror(errno));
 335                return;
 336        }
 337
 338        if (cur_vl != data->rdvl())
 339                ksft_test_result_pass("%s current VL is %d\n",
 340                                      data->name, ret);
 341        else
 342                ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
 343                                      data->name, ret, data->rdvl());
 344}
 345
 346/* Can we set a new VL for this process? */
 347static void prctl_set(struct vec_data *data)
 348{
 349        int ret;
 350
 351        if (data->min_vl == data->max_vl) {
 352                ksft_test_result_skip("%s only one VL supported\n",
 353                                      data->name);
 354                return;
 355        }
 356
 357        /* Try to set the minimum VL */
 358        ret = prctl(data->prctl_set, data->min_vl);
 359        if (ret < 0) {
 360                ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 361                                      data->name, data->min_vl,
 362                                      errno, strerror(errno));
 363                return;
 364        }
 365
 366        if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) {
 367                ksft_test_result_fail("%s prctl set %d but return value is %d\n",
 368                                      data->name, data->min_vl, data->rdvl());
 369                return;
 370        }
 371
 372        if (data->rdvl() != data->min_vl) {
 373                ksft_test_result_fail("%s set %d but RDVL is %d\n",
 374                                      data->name, data->min_vl, data->rdvl());
 375                return;
 376        }
 377
 378        /* Try to set the maximum VL */
 379        ret = prctl(data->prctl_set, data->max_vl);
 380        if (ret < 0) {
 381                ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 382                                      data->name, data->max_vl,
 383                                      errno, strerror(errno));
 384                return;
 385        }
 386
 387        if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) {
 388                ksft_test_result_fail("%s prctl() set %d but return value is %d\n",
 389                                      data->name, data->max_vl, data->rdvl());
 390                return;
 391        }
 392
 393        /* The _INHERIT flag should not be present when we read the VL */
 394        ret = prctl(data->prctl_get);
 395        if (ret == -1) {
 396                ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
 397                                      data->name, errno, strerror(errno));
 398                return;
 399        }
 400
 401        if (ret & PR_SVE_VL_INHERIT) {
 402                ksft_test_result_fail("%s prctl() reports _INHERIT\n",
 403                                      data->name);
 404                return;
 405        }
 406
 407        ksft_test_result_pass("%s prctl() set min/max\n", data->name);
 408}
 409
 410/* If we didn't request it a new VL shouldn't affect the child */
 411static void prctl_set_no_child(struct vec_data *data)
 412{
 413        int ret, child_vl;
 414
 415        if (data->min_vl == data->max_vl) {
 416                ksft_test_result_skip("%s only one VL supported\n",
 417                                      data->name);
 418                return;
 419        }
 420
 421        ret = prctl(data->prctl_set, data->min_vl);
 422        if (ret < 0) {
 423                ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 424                                      data->name, data->min_vl,
 425                                      errno, strerror(errno));
 426                return;
 427        }
 428
 429        /* Ensure the default VL is different */
 430        ret = file_write_integer(data->default_vl_file, data->max_vl);
 431        if (ret != 0)
 432                return;
 433
 434        /* Check that the child has the default we just set */
 435        child_vl = get_child_rdvl(data);
 436        if (child_vl != data->max_vl) {
 437                ksft_test_result_fail("%s is %d but child VL is %d\n",
 438                                      data->default_vl_file,
 439                                      data->max_vl, child_vl);
 440                return;
 441        }
 442
 443        ksft_test_result_pass("%s vector length used default\n", data->name);
 444
 445        file_write_integer(data->default_vl_file, data->default_vl);
 446}
 447
 448/* If we didn't request it a new VL shouldn't affect the child */
 449static void prctl_set_for_child(struct vec_data *data)
 450{
 451        int ret, child_vl;
 452
 453        if (data->min_vl == data->max_vl) {
 454                ksft_test_result_skip("%s only one VL supported\n",
 455                                      data->name);
 456                return;
 457        }
 458
 459        ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT);
 460        if (ret < 0) {
 461                ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 462                                      data->name, data->min_vl,
 463                                      errno, strerror(errno));
 464                return;
 465        }
 466
 467        /* The _INHERIT flag should be present when we read the VL */
 468        ret = prctl(data->prctl_get);
 469        if (ret == -1) {
 470                ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
 471                                      data->name, errno, strerror(errno));
 472                return;
 473        }
 474        if (!(ret & PR_SVE_VL_INHERIT)) {
 475                ksft_test_result_fail("%s prctl() does not report _INHERIT\n",
 476                                      data->name);
 477                return;
 478        }
 479
 480        /* Ensure the default VL is different */
 481        ret = file_write_integer(data->default_vl_file, data->max_vl);
 482        if (ret != 0)
 483                return;
 484
 485        /* Check that the child inherited our VL */
 486        child_vl = get_child_rdvl(data);
 487        if (child_vl != data->min_vl) {
 488                ksft_test_result_fail("%s is %d but child VL is %d\n",
 489                                      data->default_vl_file,
 490                                      data->min_vl, child_vl);
 491                return;
 492        }
 493
 494        ksft_test_result_pass("%s vector length was inherited\n", data->name);
 495
 496        file_write_integer(data->default_vl_file, data->default_vl);
 497}
 498
 499/* _ONEXEC takes effect only in the child process */
 500static void prctl_set_onexec(struct vec_data *data)
 501{
 502        int ret, child_vl;
 503
 504        if (data->min_vl == data->max_vl) {
 505                ksft_test_result_skip("%s only one VL supported\n",
 506                                      data->name);
 507                return;
 508        }
 509
 510        /* Set a known value for the default and our current VL */
 511        ret = file_write_integer(data->default_vl_file, data->max_vl);
 512        if (ret != 0)
 513                return;
 514
 515        ret = prctl(data->prctl_set, data->max_vl);
 516        if (ret < 0) {
 517                ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 518                                      data->name, data->min_vl,
 519                                      errno, strerror(errno));
 520                return;
 521        }
 522
 523        /* Set a different value for the child to have on exec */
 524        ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC);
 525        if (ret < 0) {
 526                ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
 527                                      data->name, data->min_vl,
 528                                      errno, strerror(errno));
 529                return;
 530        }
 531
 532        /* Our current VL should stay the same */
 533        if (data->rdvl() != data->max_vl) {
 534                ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n",
 535                                      data->name);
 536                return;
 537        }
 538
 539        /* Check that the child inherited our VL */
 540        child_vl = get_child_rdvl(data);
 541        if (child_vl != data->min_vl) {
 542                ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n",
 543                                      data->min_vl, child_vl);
 544                return;
 545        }
 546
 547        ksft_test_result_pass("%s vector length set on exec\n", data->name);
 548
 549        file_write_integer(data->default_vl_file, data->default_vl);
 550}
 551
 552typedef void (*test_type)(struct vec_data *);
 553
 554static const test_type tests[] = {
 555        /*
 556         * The default/min/max tests must be first and in this order
 557         * to provide data for other tests.
 558         */
 559        proc_read_default,
 560        proc_write_min,
 561        proc_write_max,
 562
 563        prctl_get,
 564        prctl_set,
 565        prctl_set_no_child,
 566        prctl_set_for_child,
 567        prctl_set_onexec,
 568};
 569
 570int main(void)
 571{
 572        int i, j;
 573
 574        ksft_print_header();
 575        ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data));
 576
 577        for (i = 0; i < ARRAY_SIZE(vec_data); i++) {
 578                struct vec_data *data = &vec_data[i];
 579                unsigned long supported;
 580
 581                supported = getauxval(data->hwcap_type) & data->hwcap;
 582
 583                for (j = 0; j < ARRAY_SIZE(tests); j++) {
 584                        if (supported)
 585                                tests[j](data);
 586                        else
 587                                ksft_test_result_skip("%s not supported\n",
 588                                                      data->name);
 589                }
 590        }
 591
 592        ksft_exit_pass();
 593}
 594